FPGA從Xilinx的7系列學起(6)
用戶必須要認識到,學習一下技巧可以讓更多的邏輯放在更少的Slice中,使工具能夠達到既實現設計時序要求又滿足用戶對功耗的要求。而現在很多用戶缺乏代碼編寫的想法,編寫出一個有時序問題的設計。為了滿足要求,就會不停修改,再綜合再布局布線來滿足自己的時序目標。其實,他們需要的是重新評估他們的HDL代碼技術以及他們的控制信號。

例如,如何使用這些D觸發器呢?首先說明幾個概念:
所有觸發器為D類型,所有的觸發器具有一個時鐘輸入(CLK),所有觸發器都有一個使能信號(CE),所有觸發器有一個高有效的復位信號(SR),SR可以是同步或異步控制來設置觸發器值,所有觸發器的值在配置過程中將會被確定下來。
所有D觸發器在7系列FPGA有一個芯片使能(CE)引腳,并通過時鐘進行相關操作。當使能信號有效的時候,D觸發器的輸出等于前一個輸入,當使能信號無效時,D觸發器保持當前值。下面的代碼就是一個很好的例子來說明FPGA的D觸發器會被工具推斷出來是寄存器。
Verilog:
always @ (posedge CLK )
begin
if (CE)
Q <= D;
end
VHDL:
FF: process (CLK)
begin
if (rising_edge CLK) then
if (CE = ‘1’) then
Q <= D;
end if;
end if;
end
對于XILINX的器件來說,大部分都是高有效。如果用戶想使用低有效,需要占用LUT作為一個反轉邏輯來使用,這也無疑增加了資源的使用。
D觸發器有一個高有效的SR端口信號,SR端口可以被配置成同步置位/復位、異步置位或者復位、清零端口。它是基于用戶的編碼風格被配置為同步或異步的。置位時,觸發器輸出將被強制為觸發器的置位/復位值,綜合工具會自動從用戶的HDL代碼中推斷出初始值進行設置的。
介紹一下異步復位。首先必須指出,雖然XILINX的器件支持異步復位,但是XILINX不建議使用異步復位。當用戶必須使用異步復位的時候,想使工具能夠推斷出異步復位邏輯,那么必須把異步復位信號放在敏感列表里面,當該復位信號有效的時候,輸出的信號必須立即等于復位的值。下面的例子可以很好的闡述該問題。
Verilog
always @ (posedge CLK or posedge RST )
begin
if (RST)
Q <= 1’b0;
else
Q <= D;
End
VHDL
FF: process (CLK, RST)
begin
if (RST = ‘1’) then
Q <= ‘0’;
elsif (rising_edge CLK) then
Q <= D;
end if;
end
上圖這個例子很好的說明了如何同步用戶的異步復位設計。這個電路設置能夠有效的避免亞穩態。在用戶必須使用異步復位的時候,應該使用該例子推薦的方法來進行設計,也就是異步復位必須同步化。如果沒有把異步復位同步化,那么將可能引起亞穩態或者增加亞穩態的風險。
Verilog
always @ (posedge CLK)
begin
if (RST)
Q <= 1’b0;
else
Q <= D;
end
VHDL
FF: process (CLK)
begin
if (rising_edge CLK) then
if (RST = ‘1’) then
Q <= ‘0’;
else
Q <= D;
end if;
end
再說說同步復位。這個例子說明了如何HDL代碼中正確的使用同步復位。有幾個關鍵點很重要,首先就是復位信號不能在敏感列表里面,其次,只有當時鐘有效的翻轉的時候,輸出信號才能有效賦值。當復位信號有效的時候,只是得到了一個同步復位的行為,只有當有復位效后的第一個有效時鐘翻轉才能傳遞復位信息。這個Reset信號就是一個時序路徑上的一個節點,會被工具自動進行相對應的時序約束。這意味這工具會根據你的時序目標進行優化的布線。這也是XILINX推薦的復位的方法。
下面這個例子說明在用戶的Flip-Flops設置初始值。大多數設計師這樣寫代碼為的是仿真能夠更加簡便。否則當用戶開始運行仿真的時候,用戶設計中使用的寄存器等都處于一個不確定態。可以,按照例子來寫的代碼,可以讓寄存器等有一個確定的初始值,處于一個確定態。
reg Q =1’b1;
always @ (posedge CLK or posedge RST )
begin
if (RST)
Q <= 1’b0;
else
Q <= D;
end
signal Q: std_logic:=‘1’;
FF: process (CLK, RST)
begin
if (RST = ‘1’) then
Q <= ‘0’;
elsif (rising_edge CLK) then
Q <= D;
end if;
end
觸發器中的Flip-flops和flip-flop/latches有著相同的控制信號,CLK,SR, CE等等。那么對用戶來說最重要的事情就是嘗試著減少用戶設計中的控制信號的數量。用戶需要加強對控制信號的行為的理解:所有Flip-flops和flip-flop/latches共享所有這些控制信號,這些控制信號主要是時鐘,置位和復位。如果一個組中的一個flip-flop用了時鐘使能信號,那么所有其他的Flip-Flop必須使用相同的時鐘使能或者不用時鐘使能。如果一個組中的一個flip-flop用了置位或者復位信號,那么所有其他的Flip-Flop必須使用相同的置位或者復位信號。
在同一個Slice中的Flip-Flops會在同一時間進入復位或者置位的狀態。但是有一點需要強調一下:所有Flip-Flops都有自己單獨的SRVAL屬性,也就是說信號在統一復位的時候,可以有自己的值。
對于整個slice來說,使用它的設計者要切記注意控制信號,工具的推斷功能非常非常重要的。特別重要的,因為觸發器中每個切片將共享所有相同的控制信號,因為被占用后,其他和它使用不一致控制信號的觸發器將無法使用剩下來的資源。那么對用戶來說,設計要盡量使用少的控制信號:關鍵是要評估是否有未使用的寄存器的一些情況,如果有,就嘗試使用相同控制信號。
另外,用戶要記住控制信號的置位、復位、時鐘使能信號是能連接到了LUT的輸入端,而不一定是連接到相應的flip-flop的相對應的端口。不像老的FPGA的架構有獨立的置位和復位信號端口,7系列的置位和復位端口沒有獨立的端口,但是可以只被被編程使用兩個、一個或者一個也不使用。因此如果你想兩個都是用的話,它必須用LUT去生成置位或者復位功能。以上說的都是同步的。置位或者復位信號可以被設置為異步信號。但是異步信號的置位或者復位信號端口不能使用LUT來生成,因此綜合工具不可能使用LUT來實現、映射異步控制信號到LUT的輸入信號中,它直接就會映射到寄存器的端口。
下圖展示了工具如何通過使用同步置位和復位信號獲得好處。工具轉移置位和復位端口連接到LUT輸入上,并將復位推斷為同步,那么這些觸發器可以被放置在相同的Slice。
這一功能可以通過使用綜合過程的“Reduce ControlSets”屬性來實現。
在這個例子中,三個觸發器中的原始設計必須放在單獨的Slice中,因為它們每一個使用不同的控制集合。所有三個觸發器共享相同的時鐘,但是第一個觸發器沒有置位/復位,第二個觸發器使用“置位(set)”同步地將其輸出設置為1,而另一個使用“復位(reset)”將輸出重置為0。如果同步置位和復位功能被使用的LUT邏輯實現,那么所有三個觸發器可以被置于同一個Slice中。整個實現過程,圖例中顯示的非常明顯。那么用戶可以看到增加了組合邏輯將一些控制信號放在未使用的LUT中去,卻減少了Slice的總體的使用量。
上圖的例子中,兩個觸發器在原始設計必須放在單獨的Slice里面,因為它們每一個使用不同的控制集合。但是一個時鐘使能可以被工具推斷為觸發器輸入或可能被工具推斷LUT的輸入。當他使用LUT的輸入的時候,這兩個寄存器可以被放置在同一個Slice里面。在VIVADO和ISE下面都有選項來幫助大家自動實現該功能。大家在做設計的時候可以選用。
做設計的時候有些原則還應該注意:
在控制信號的規劃的時候,XILINX的原語和IP的例化可能增加額外的控制信號到用戶的設計中,那么肯定有些設計要和原語、IP共享這些共享信號。建議用戶自己建立和仿真自己IP——特別是用戶的設計非常大——并以此結果來評估使用這些控制信號。
不建議使用低電平有效的控制信號,這是因為觸發器(flip-flops)本身沒有翻轉邏輯,而需要使用LUT來完成該功能。雖然可能很多人在上大學求學期間學習的原則都是低電平有效。但是這個真的是一個非常落伍的設計理念。當然除非要進行ASIC的流片工作或者使用非常古老的芯片,否則用戶必須修改使用低電平有效的陳舊的思想。
低電平有效,必須增加使用更多的LUT,他們需要LUT才能把低電平有效翻轉為高電平有效然后再連接到觸發器的控制端口,這樣就需要白白浪費一個LUT資源。
如果聽說過層次設計(hierarchicaldesign)的方法學(意味著用戶在使用 partitions功能,使用保持hierarchy功能, 使用IP核, 使用多網表,或者使用自下而上的編程),那么這個問題會變得更加嚴重:會有一個很差的資源使用情況,一個非常長的運行時間,一個糟糕的時序問題,一個差勁的功耗問題。
舉一個例子,如圖所示的例子準確的展示使用低有效作為控制信號,并且使用了層次結構等等的設計方法學。大家可以清晰的看到,因為要保持一個結構就無法跨越這些邊界。那么如何修正這些代碼呢?在這個案例中,XILINX的建議是直接修改代碼或者重新整合IP核變為高有效,然后重新綜合。大家一定請記住,綜合工具和實現工具不能合并這種邏輯到同一個Slice的,因為用戶是不允許跨層次邊界優化。
再給大家提問一個問題,大家先不要看答案:
1) 以下這些案例能不能把這兩個寄存器放在同一個slice里面?
注:所有控制信號都是直接驅動觸發器的控制信號。
– 案例 1
? FF1: Clock, CE, Set
? FF2: CLK, CE, Set
– 案例 2
? FF1: CLK, CE, Reset
? FF2: CLK, Reset
– 案例3
? FF1: CLK, CE, Reset
? FF2: CLK, CE, not Reset
– 案例 4
? FF1: CLK, CE, Reset
? FF2: CLK, CE, Reset
案例1:不能。觸發器有不同的時鐘,所以它們有不同的控制組的。
案例2:不能。FF2具有時鐘使能,所以它們有不同的控制組的。
案例3:不能。FF2無法提供翻轉邏輯,所以它們有不同的控制組的。
案例4:能。兩個觸發器具有完全相同的控制信號,所以它們是相同的控制組的成員。可以被放在同一個Slice里面。
減少使用控制信號可以提高用戶的資源使用率。復位邏輯的正確使用,可以提高設計的效率。如果不是設計必須要求使用異步復位,所有的設計都使用同步復位。對更大更復雜的設計,只有必須的關鍵的邏輯使用同步復位,其他邏輯直接不使用復位,而是依靠芯片自由GSR來進行復位就夠了(這個想法夠膽大,不放心的還是按照老辦法來設計吧)。異步復位也只是在萬不得已的情況才使用。
Reset分為全局Reset和局部Reset。全局Reset可以讓所有存儲類的元素處于一個已知的狀態。全局的Reset一般是通過外部的引腳或者一個標準進程的完成或者等待PLL/MMCM鎖定信號的有效來實現的。局部復位一般是有內部信號產生,他是讓一些存儲類的元素處于一個已知的狀態,如利用內部的計數器。一般來說局部復位都是同步信號。
Reset又分為同步復位和異步復位,一個同步的局部復位是一個標準設計里面的一部分,一般都是使用狀態機或者計數器讓一些信號進入一個固定的狀態,例如經常會用的0狀態。綜合工具一般是建議使用同步設計,不建議使用異步設計的。當使用異步復位設計的時候,可能會產生一個亞穩態,讓電路處于一個不確定的狀態。因此局部的異步復位,最好不要使用。對于全局的異步復位,需要一些方法來處理它,以前提過異步的同步化。
全局的同步置位/復位信號可以讓存儲類的元素進入一個已知的狀態, 那么他帶來的好處是什么?全局的同步置位/復位信號實現非常簡單,可以在工程師需要的時候控制設計中的控制信號或者觸發控制信號,確保設計萬無一失,更可以使用讓綜合工具來進行減少控制信號功能。那么它的缺點是什么?當時鐘不能保證正常工作的時候,同步復位信號將不能夠生。另外,同步復位會占用一些同步資源,可能會影響用戶的時序設計的目標。
全局的異步使用,以前我們就講過,必須異步的同步化。這樣才能保障用戶的設計不進入到亞穩態。但是有時候,用戶會必須使用異步復位,它的優點就是任何時候生效的時候都能工作,例如,當用戶使用一個熱插拔影響時候,這可能需要一個不依靠時鐘產生復位來保障整個工作的啟動。缺點也很明顯,它不能使用減少控制信號的功能,也會大量占用布線資源。目前很多綜合工具都是支持把異步復位轉換為同步復位。
既然異步復位不應該使用,那么對于用戶已經設計的代碼我們應該怎么處理呢?有三種選擇,第一,留下設計中的異步復位,清楚的了解他的缺陷。第二,你使用工具轉換(如下圖所示)。這個是非常危險的,因為異步復位畢竟和同步復位不一樣,轉換后,代碼的綜合結果可能和行為仿真出來的結果完全不同。第三,修改你的代碼或者使用腳本來修改代碼讓異步變成同步,這個是我們推薦的方法。要記住只是頂層修改一下復位代碼并不一定能夠有一個正確的結果,還是應該把所有的代碼檢查一遍,然后把從HDL級上把所有的異步復位清除掉。
如何選擇使用哪種方式來處理已有設計呢?如果已有設計的代碼完全沒有問題,能用滿足時序要求,能夠使用當前選用的器件,那么工程師就沒有必須去做什么改動。綜合工具不可能像修改代碼一樣的有效果,那么一定要仔細考慮使用該選項,雖然該功能可能會有一些問題,但是畢竟是簡單易使用。
對大型的特別復雜的設計且時序等等都不滿足要求,就只能修改代碼了。再強調一下不要只是簡單的修改頂層,因為綜合工具都是從底層開始綜合的,所以用戶必須從Flip-Flops這個級別進行修改。
下圖是一個用戶的例子,他除了有專門的GSR的復位外,額外使用了同步全局復位,可以看到他大量的占用了布線資源,最后造成了用戶設計的時序目標無法完成。要記住復位信號是需要和設計中的其他信號來競爭占用相同的設計資源,包括關鍵路徑的時序,如果這些設計資源給其他信號使用能夠更好滿足設計的時序要求。有心的用戶可以檢查一下一個典型的復位,就會知道有多少扇出數量,這個扇出的數量會讓用戶覺得可怕。這也是為什么要在設計中刪除復位的最大原因了。
在芯片中有一個GSR資源,有專門的慢速布線資源,他能夠在幾個毫秒的級別里面激活。如果用戶需要時刻駕馭自己設計,同時需要在系統運行過程中去復位,那么GSR對該用戶是沒有意義的。這因為GSR還是太慢,畢竟它不是基于時間來復位的,這個條件下我們也不推薦用戶使用GSR。然而,GSR將自動作為配置過程的一部分。配置完成后,所有的觸發器都會處于一個已知狀態。如果用戶想嘗試和使用,或以某種方式控制它,那么需要例化一個UPE2的原語進入到用戶的設計中,并連接到GSR的輸入上。但是這個實在是沒有意義,因為它太慢了,因此不推薦它作為一個復位系統的方式。
DSP 資源的靈活性遠遠超過用戶認知,加減乘除、累加、計數器、比較器,移位寄存器、復用器、模式匹配等等全部可以使用DSP實現。DSP如何使用復位呢?每一個DSP的Slice有超過250多個寄存器,但是沒有一個使用了異步復位。用戶使用同步復位可以令綜合工具更加容易的推斷出使用DSP資源。
這個事情的重要性,需要進一步強調一下。同步復位可以令工具更容易的使用專用的硬件資源。特別對DSP資源來說,用戶特別喜歡DSP的輸出的寄存器,這樣可以增加pipeline,提高系統的性能。只要使用了異步的復位,那么綜合工具將無法有效的推斷出使用DSP資源。在實際的設計中,我們經常可以看到只要是使用了同步復位,DSP資源將被使用的更有效率。那么只要用戶想建立一個算術的系統并且想有效使用DSP資源,那么一定要確保自己使用的同步復位。
Block RAMs可以通過使用輸出寄存器,可以獲得最小的輸出的時間,提高性能。同樣輸出的寄存器也僅僅有同步服務的接口。沒有被使用BlockRAM的資源,那么可以用來作很多功能,例如:ROM、大的LUT、復雜的邏輯、大狀態機、深度移位寄存器。使用BlockRAM作為其他的功能,能夠解放出大量的寄存器。同樣,同步復位可以令工具更容易的推斷出Block RAM硬件資源。只要使用了異步的復位,那么綜合工具將無法有效的推斷出使用Block RAM資源。
另外一個重要的事情,也要說明一下,就是移位寄存器。用戶一定要記住要想直接使用LUT作為以為寄存器一定不能有復位端口。只要是在相關的HDL代碼中使用了復位的信號,那么這個設計就不可能使用LUT作為SRL寄存器了。綜合工具或者把用戶的設計放在相關的CLB的寄存器里面了。這個會非常明顯的增加使用的寄存器的數量。工具或者建立一個輔助的電路來模擬復位的功能。但是同樣的,復位電路也需要大量的消耗資源。設計肯定也會更慢,這個肯定和設計的目的背道而馳。
很多用戶可能會有一個疑問,那么這個移位寄存器到底應該怎么在代碼中體現呢?給一個例子,當做設計的時候能夠仿照這個來實現自己的功能。
VHDL版本的:
library IEEE;
useIEEE.STD_LOGIC_1164.ALL;
entity shifter is
generic (
C_DWIDTH : integer := 8;
C_NUM_CYCLES : integer := 32
);
Port ( Clk : in STD_LOGIC;
En : in STD_LOGIC;
Din : in STD_LOGIC_VECTOR(C_DWIDTH-1 downto 0);
Dout : out STD_LOGIC_VECTOR(C_DWIDTH-1 downto 0));
end shifter;
architecture Behavioral ofshifter is
signal din_d1 :std_logic_vector(C_DWIDTH-1 downto 0); -- Shift register Input
type array_slv is array(C_DWIDTH-1 downto 0) of std_logic_vector(C_NUM_CYCLES-1 downto 0);
signal din_shreg :array_slv;
begin
process (Clk)
begin
if Clk'event and Clk='1' then
if En = '1' then
for i in 0 to C_DWIDTH-1 loop
din_shreg(i) <=din_shreg(i)(C_NUM_CYCLES-2 downto 0) & Din(i);
end loop;
end if;
end if;
end process;
process (din_shreg)
begin
for i in 0 to C_DWIDTH-1 loop
Dout(i) <=din_shreg(i)(C_NUM_CYCLES-1);
end loop;
end process;
end Behavioral;
Verilog版本的:
`timescale 1 ps / 1 ps
module shifter
#(
parameter C_DWIDTH = 8,
parameter C_NUM_CYCLES = 32
)
(
Clk,
En,
Din,
Dout
);
input Clk;
input En;
input [C_DWIDTH-1:0] Din;
output [C_DWIDTH-1:0] Dout;
reg [C_NUM_CYCLES-1:0]din_shreg [C_DWIDTH-1:0];
integer srl_index;
initial
for (srl_index = 0; srl_index? din_shreg[srl_index] ={C_NUM_CYCLES{1'b0}}; genvar i; generate for (i=0; i < C_DWIDTH; i=i+1) begin always @(posedge Clk) if (En) din_shreg[i] <={din_shreg[i][C_NUM_CYCLES-2:0], Din[i]}; assign Dout[i] =din_shreg[i][C_NUM_CYCLES-1]; end endgenerate endmodule FPGA 單片機 嵌入式
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。