我們知道觸發器是邊沿敏感,鎖存器是電感敏感的存儲單元。那麼它們在FPGA內部究竟有什麼區別呢?本文通過幾個實際的案例來說明。
在xilinx 7系列的FPGA中,CLB(Configurable Logic Block)是邏輯實現的主要資源,在ug474中詳細介紹了CLB。每個CLB包含兩個slices,每個slices由4個(A,B,C,D)6輸入LUT和8個寄存器,1個CARRY4,3個MUX(多路選擇器)組成。同一CLB中的兩片slices沒有直接的線路連接,分屬於兩個不同的列,每列擁有獨立的快速進位鏈資源。slice分爲兩種類型 SLICEL, SLICEM . SLICEL可用於產生邏輯,算術,ROM。 SLICEM除以上作用外還可配置成分佈式RAM或32位的移位寄存器。每個CLB可包含兩個SLICEL或者一個SLICEL與一個SLICEM.(https://www.eefocus.com/b3574027/blog/15-05/312609_2e5ad.html)
打開vivado的device就能看到每個藍色框內就是1個CLB,左起第三個slice就是slicem。
在ug953中介紹各種底層硬件資源,其中CLB中的寄存器可以配置爲D Flip-Flop (D觸發器)和Transparent Data Latch(鎖存器),在245頁上有描述,具體又可以分爲以下幾種:
觸發器:
異步復位 | FDCE | 復位後Q輸出0 |
FDPE | 復位後Q輸出1 | |
同步復位 | FDRE | 復位後Q輸出0 |
FDSE | 復位後Q輸出1 |
鎖存器:
異步復位 | LDCE | 復位後Q輸出0 |
LDPE | 復位後Q輸出1 |
觸發器的基本結構如下圖所示:
當復位端CLR拉高時Q輸出0,當CE等於0時,Q保持不變,當CLR = 0;CE = 1; 在C端邊沿觸發時將D的數據同步到Q端。
鎖存器的基本結構如下圖所示:
當復位端CLR拉高時Q輸出0,當GE G有一個拉低時,Q端保持不變,都爲高時,Q輸出D的值。可以看到鎖存器是沒有同步復位的。無論altera還是xilinx的ff都可以配置成fd或者ld。
1.異步復位時序電路
以4分頻器爲例。
module test(
output c,
input clk,
input rst
);
reg clk2 = 1'd0;
reg [1:0]cnt = 1'd0;
always@(posedge clk or posedge rst)
if(rst)
begin
clk2 <= 1'd0;
cnt <= 1'd0;
end
else if(cnt == 1'd1)
begin
clk2 <= ~clk2;
cnt <= 1'd0;
end
else
cnt <= cnt + 1'd1;
assign c = clk2;
endmodule
綜合後的RTL圖爲:
可以看到佔用了2個FF和2個LUT,而且FF都是FDCE,是異步復位,初始值爲0的觸發器,這和代碼是一致的。LUT1的作用就是一個反相器,當cnt = 1時,cnt = 0;cnt = 0時,cnt = 1;同理,可以分析LUT2的真值表,當I0 = 1時,O輸出的是I1的取反,這和代碼也是一致的。總之,我們可以看出異步復位電路,RST異步接入,高電平有效時,FF配置爲FDCE。
當代碼修改爲if(!rst)時,RTL如下圖所示:
編譯器會用一個lut對rst做反相,這將佔用更多的資源,所以一般來說用高電平復位,同時,復位時應將用到的寄存器都初始化。
2.同步復位時序電路
同步復位就是在always 敏感列表裏不添加rst,僅在clk邊沿變化時,D發生變化。代碼修改爲:
always@(posedge clk)
if(rst)
begin
clk2 <= 1'd0;
cnt <= 1'd0;
end
else if(cnt == 1'd1)
begin
clk2 <= ~clk2;
cnt <= 1'd0;
end
else
cnt <= cnt + 1'd1;
綜合後的RTL圖爲:
可以看到rst信號接入到了lut,FF配置爲了FDRE。對於xilinx的LUT來說,觸發器支持高電平的異步和同步復位。
3.完整if else組合邏輯電路
一般認爲組合邏輯是指輸出只與當前的輸入狀態有關電路,而時序邏輯是指輸出不僅與當前輸入有關,還跟歷史輸入有關,例如前面的電路。
例如,a = (b&c&d) + e; 這明顯是組合邏輯。組合邏輯裏沒有時鐘,輸入信號至LUT裏後,直接輸出Q,不經過觸發器,除非電路中生成了鎖存器,Q輸出後會接入一個由FF配置成的LD。鎖存器會在什麼地方產生呢?我們一般回答在組合邏輯裏分支沒有完全描述的情況下產生,例如if後沒有else,case沒有default。
下面是完整的if else例子。
module test(
input a,b,
output [1:0]c,
input clk,
input rst
);
reg [1:0]reg_c = 1'd0;
always @(rst or a or b)
if(rst)
reg_c = 2'd0;
else if(a)
reg_c = 2'd1;
else if(b)
reg_c = 2'd2;
else
reg_c = 1'd0;
assign c = reg_c;
endmodule
生成的RTL圖如下圖所示:
由於它是組合邏輯,敏感列表裏寫不寫都是一樣的(我猜的)。
4不完整if else if組合邏輯電路
如果少了else會是什麼情況呢?
module test(
input a,b,
output [1:0]c,
input clk,
input rst
);
reg [1:0]reg_c = 1'd0;
always @(*)
if(rst)
reg_c = 2'd0;
else if(a)
reg_c = a - b;
else if(b)
reg_c = a + b;
assign c = reg_c;
endmodule
注意代碼,沒有了else,同時else if裏的內容也有修改。RTL圖爲:
可以看到,在LUT輸出後,加入了一個LDCE,這便是鎖存器。因爲在兩個else if以外的情況下,編譯器會將邏輯定爲保持當前輸出不變,因此,觀察LDCE的端口,rst接到了異步復位的CLR,鎖存器只支持異步復位,當a b條件不滿足else if時,即在LUT2中描述情況下a b同時爲0時,數據將鎖存,因此LUT會輸出0到LDCE的G端,此時Q將保持當前值。可以看到當條件羅列完全時,僅需要LUT就可以完成組合邏輯的描述,未羅列完全時,還需要LUT用於描述LDCE的G端,因此會多佔用FF資源。同時,鎖存器也不利於靜態時序分析,所謂靜態時序分析,我們通常是以clk的週期爲參考,去分析DATA和CLK的路徑,計算是否滿足下一級FF的setup和hold時間,而latch的復位是異步的,G端的變化是異步的,latch和時鐘毫無關係,分析起來自然十分麻煩。因此我們應儘量避免產生latch。
時序邏輯中if 或case不完整會不會產生latch呢?
答案是不一定產生,因爲時序邏輯是在clk的變化沿同步數據,它會用一個lut來描述沒有窮舉的情況,並輸出到FF的CE端,使FF在輸出時保持前一個值。如下圖所示。
反正最好是把else和default寫完整。也可以在組合邏輯always@(*)的首行,把信號先用阻塞方式(=)把信號先賦值,效果等同於else或default。
那麼如何檢查代碼中有latch呢?
最簡單的方法是在綜合後的message窗口,ctrl + f,搜索latch或者設置條件爲latch,點擊確定,即可看到有多少個latch。從資源佔用中看到只用了1個LUT 和1個FF就實現了,可是明明是2個LUT和1個FF?這時候要打開版圖來看。
如果在FLOP_LATCH下沒有LATCH選項,說明該設計中就沒有latch產生,同理,用Find命令可以查看其它資源。也可以在輸入腳本命令 all_latches。輸入all_ 會有命令提示,可以查看許多資源,請自行嘗試。
至此,本文介紹了latch和ff的區別,和latch產生的原因。實際上數字電路基礎中花了很多篇幅去講晶體三極管和MOS管組成TTL和CMOS電路,有或與非等基本電路,分析它們的電流和電壓特性,但FPGA裏負責大量邏輯運算是基於SRAM的LUT,是查找表,應該有一個區別的認識,不是FPGA裏有很多或與非門,是用LUT組成的或與非邏輯。
FPGA設計時常說的設計流程就是先綜合生成原理圖,再佈局佈線映射到FPGA中CLB中去。比如有16個並行總線端口輸入後經clk採樣,在原理圖看都是一樣的路徑,但由於映射到BGA引腳上,信號從PAD到16個寄存器有無數種佈線方法,它們之間的延時也是不固定的,當採樣速度很高,佈線的延時差異不能忽略時,就容易採樣錯誤,因此就要用時序約束,set input delay來描述這些信號相對於clk到來的時刻,佈局佈線時就根據這些約束保證輸入的路徑是差不多的。輸出也是同理。爲了得到更好的採樣結果,通常會將輸出輸入打1個節拍,用IOB的FF去輸出或採樣,可以保證每次佈局佈線時,從FF到PAD的時間都是固定的,而且最短。
5.阻塞和非阻塞
簡單說來,阻塞就是 = ,非阻塞就是 <=。爲什麼稱它爲阻塞呢?因爲 = 在同一個always 它是有先後執行順序的,後面的=被前面的 = 阻塞了。非阻塞就是並行的,一起運行。
懶得寫了,直接引用吧。
https://blog.csdn.net/August_cwj/article/details/77989071
限於水平,還有很多問題沒有描述,權當一個敲門磚,我們FPGAer還是應該要理解你的代碼實際上對應的是什麼電路,畢竟這是自古流傳的傳統。