目錄
Altera FPGA 基本要素
FPGA 開發流程和適用范圍
設計和實施規范
頂層設計的要點
Verilog HDL
語法規范
編寫規范
設計技巧
編輯整理 by?Staok,始于 2021.2 且無終稿。轉載請注明作者及出處。整理不易,請多支持。
本文件是“瞰百易”計劃的一部分,盡量遵循“二項玻”定則,致力于與網絡上碎片化嚴重的現象涇渭分明!
本文系廣泛擷取、借鑒和整理,適合剛入門的人閱讀和遵守,已經有較多經驗的人看一看圖個樂,如有錯誤恭謝指出!本文已經是長期積累和堆疊而形成一定規模,不必按照從前到后的順序去看,可以挑感興趣的章節去看。
本文為簡述風格,本意即記錄要點和便于快速拾起。
本文對應的?Github/Gitee?倉庫地址,本文最新的原文 和 一些源碼、備查手冊等等 均放在里面。
Altera FPGA 基本要素
p.s 過于基礎的概念不提,這不是入門帖。入門可以跳到 “O.0 值得跟著的學習網站” 章節進行攝入。
p.s 以下以 Cyclone IV E 系列 FPGA 為例。
-
FPGA基礎資源選擇:邏輯單元(LE)數量,內嵌存儲器(M9K)數量(總 RAM Bits 數),乘法器數量,PLL 數量,I/O 數量,全局時鐘網絡數量等。
-
板級電路組成:電源,時鐘,復位,JTAG,固化配置信息 FLASH,外設。具體連接形式參考一些開發板和開源板子的原理圖和 PCB。
- 電源:核心電源(標識 VCCINT,低壓版本 1.0V,非低壓 1.2V),IO BANK(標識 VCCIOx(x = 1 到 8),電壓 1.2V 到 3.3V),PLL(模擬 PLL 標識 VCCAx(x = 1、2 或 4),其地標識 GNDAx(x 同前),電壓 2.5V;數字 PLL 標識 VCCD_PLLx(x = 1、2 或 4),電壓 1.2V),外設供電。不同系列 FPGA 的供電措施不同,具體要看電器參數等手冊,盡量使用推薦值。
- 復位:上電后,FPGA 器件開始加載外部 FLASH 芯片的固化信息,加載完畢之后(最多 0.3s)再進行復位(低電平有效),阻容 RC 復位電路可選:R = 47kΩ,C = 10uF,3.3V 的 IO 標準下,充電到 1.7V 時間為 340ms。
-
全局時鐘網絡:專用時鐘網絡走線,同一時鐘到達不同寄存器的時間差可以被控制到很小的范圍內。外部輸入時鐘信號要連接到 “全局時鐘專用引腳” 上。FPGA 的綜合工具會自動識別和分配。
-
I/O:輸入和輸出時鐘信號盡量分配到專用引腳上。差分信號對兒必須分配到支持差分的專用引腳上。高速信號分配到支持高速傳輸的專用引腳上(如 DDR 的專用 IO 接口)。一些硬核使用的引腳可能是固定的要注意。總線信號盡量分配到同一個 BANK。一些產生噪聲干擾的信號(如時鐘信號)盡量遠離器件的配置喜歡和其它敏感的信號。
-
調試和固化:
更多詳細參考:
- FPGA配置方式。
- FPGA的各種功能管腳。
- Altera特殊管腳的使用。
- 官方手冊里是最全的、最準的,多看!
具體看官網手冊 “Cyclone IV Device Handbook Volume 1” 的 “Configuration Process” 章節和 “Configuring Altera FPGAs.pdf” 手冊。
-
調試為通過 JTAG 接口用 Blaster 下載器把編譯生成的 .sof 文件下載到 FPGA 內掉電易失的 SRAM 中。
-
固化是通過 JTAG 接口用 Blaster 下載器把編譯并轉化生成的 .jic 文件下載到 FPGA 對于的外部 FLASH 器件中。FPGA 上電從 FLASH 中獲取配置信息,分為幾種不同的配置模式,根據 [3:0]MSEL 四個引腳上電時的電平狀態確定,而具體的 [3:0]MSEL 與 啟動方式的關系 看對應 FPGA 芯片系列型號的手冊。配置模式分為以下幾種:
AS(主動串行),適用于小容量。由 FPGA 器件引導配置過程,EPCS 系列 FLASH 專供 AS 模式。一般用此模式。
AP(主動并行),速度快,占 I/O 更多,適用于大容量 FPGA 器件。EPC 系列 FLASH 用于此。
PS(被動串行),需要外部 MCU 或 CPLD(如 MAX II 系列)控制 FLASH 的數據打入 FPGA,此方式最靈活,對于多個 FPGA 或者要自動更換固件用此模式。
等其他。
FPGA 開發流程和適用范圍
-
開發流程:需求分析,模塊劃分,實現,前仿真,分配 IO,時鐘信號約束 + 其他信號時序分析和約束,后仿真,下載驗證和調試,固化代碼(注意是有順序的)。1.4 Verilog 設計方法 | 菜鳥教程 (runoob.com)。
-
FPGA 固有靈活性和并行性。FPGA 應用領域列舉:邏輯粘合,實時控制,高速信號采集和處理,協議實現,仿真驗證系統,片上系統 SoC。
-
處理器和 FPGA 分工:MCU、MPU 適合做管理、協調,FPGA 的數字邏輯適合做專用的、復雜的、結構和功能固定下來的算法實現。
-
推薦多去讀讀 FPGA 原廠(Altera 或 Xilinx)的官方文檔,在它們的一些文檔手冊中有各種常見的電路的參考實現實例和代碼風格。
-
板級 PCB 走線遵循 “PCB走線規范”。
-
…
設計和實施規范
這里的規范僅為初級,另有 “HuaWei Verilog 規范” 等規范可供參考。
頂層設計的要點
- 單個模塊盡量使用一個時鐘源;對于多個模塊要認真、清楚的劃分時鐘域;跨時鐘域的信號一定做同步處理(D觸發器同步);片內的 PLL / DLL 資源盡量利用起來;至少要對所有時鐘信號加上簡單的時序約束,不能沒有。
- 數據傳遞的兩邊速率不一致要在中間加 緩存機制,常見的如 FIFO 和 乒乓緩存,后者詳見 “設計技巧” 小節里的 “乒乓操作” 部分。
- 復雜邏輯/時序邏輯要使用 FSM (有限狀態機)方式來寫,在下面的 “模塊收集” 里面有狀態機的例子。
- 條件邏輯/狀態圖等一定要遍歷所有狀態,一定,防止不可預料的錯誤綜合結果,對于 if 要有 else,對于 case 要有 default。
- 對于仿真:先對每一個單個模塊仿真,要求代碼覆蓋率、條件分支覆蓋率、表達式覆蓋率必須達到 100%,這三個可以通過 Modelsim 查看;子系統仿真,將多個模塊組合在一起進行仿真,覆蓋率盡量高;系統級仿真,軟硬件整板聯調。仔細設計仿真激勵文件。
- 通常采用自頂向下的設計方式。先確定系統有哪些輸入和輸出,把系統劃分成多個子功能模塊(框圖模塊),每個功能模塊再劃分下一層的子模塊(HDL 意義上的模塊),最后的每個模塊的設計對應一個 module ,可以一個 module 設計成一個 verilog HDL 文件。
- 在 FPGA 邏輯全編譯之前,盡量將全部 頂層 IO 分配給 實體芯片的引腳 而 不要空置,沒用到的輸入信號也要 assign 到 確定的 0 或 1,這樣不但保證 確定的邏輯行為,而且如果不做那么全編譯時 Timing 時序 無法保證 從而 亮紅。
- 工程文件夾劃分規范:prj 為工程文件存放目錄; rtl 為 verilog 可綜合代碼存放目錄; testbench 為測試文件存放目錄; img 為設計相關圖片存放目錄; doc 為設計相關文檔存放目錄; prj 文件夾下還建立了子文件夾 ip,用于存放 Quartus Prime 中生成的 IP 核文件。
Verilog HDL
語法規范
-
No.1,層次化設計,IP 化設計。自寫小 IP 盡量參數化、可重用,方便日后搭建數字積木。
-
頂層文件名與頂層模塊名一致。
-
模塊的定義名加尾綴"_ module",輸入輸出的信號名各加后綴"_ in"和"_ out",低電平有效的信號加尾綴"_ n"或“#”,時鐘信號使用"clk _“或"Clk _“前綴,復位信號使用"rst _“前綴,使能信號使用"en"或者"Enable"標識等。
-
定義模塊的時候,定義輸入輸出信號時就帶好 “input”/“in” 、 “output”/“out” 和 “reg” 等的標識修飾。
-
一個 tab 四個空格。
-
用 tab 劃分清晰的語句層次,用 tab 對齊多行同層次語句等。
-
begin 和 end 語句塊修飾詞在豎方向對齊。
-
操作符等前后用一個空格做間隔。
-
注釋齊全,對自己和別人負責。
-
以下用一例子包含 verilog 常用語法。
/* 這里是注釋 */
// 還是注釋/*Verilog 保留字always and assign begin buf bufif0 bufif1 case casex casez cmosdeassign default defparam disable edge else end endcase endmoduleendfunction endprimitive endspecify endtable endtask eventfor force forever fork function highz0 highz1 if ifnoneinitial inout input integer join large macrmodule medium modulenand negedge nmos nor not notif0 notif1 or outputparameter pmos posedge primitive pull0 pull1 pullup pulldownrcmos real realtime reg release repeat rnmos rpmos rtran rtranif0rtranif1 scalared small specify specparam strong0 strong1 supply0 supply1table task time trantranif0 tranif1 tri tri0 tri1 triand triortrireg vectored wait wand weak0 weak1 while wire wor xnor xor
*//* 引用自 https://blog.csdn.net/luxinwylive/article/details/99827766(1)所有綜合工具都支持的結構:always,assign,begin,end,case,wire,tri,aupply0,supply1,reg,integer,default,for,function,and,nand,or,nor,xor,xnor,buf,not,bufif0,bufif1,notif0,notif1,if,inout,input,instantitation,module,negedge,posedge,operators,output,parameter。(2)所有綜合工具都不支持的結構:time,defparam,$finish,fork,join,initial,delays,UDP,wait。(3)有些工具支持有些工具不支持的結構:casex,casez,wand,triand,wor,trior,real,disable,forever,arrays,memories,repeat,task,while。
*//* wire 類型變量定義物理連線,不保存東西,reg 類型變量定義寄存器,用于保存東西 *//*引自 https://zhuanlan.zhihu.com/p/72012739wire 用法總結1.wire可以在Verilog中表示任意寬度的單線/總線2.wire可以用于模塊的輸入和輸出端口以及一些其他元素并在實際模塊聲明中3.wire不能存儲值(無狀態),并且不能在always @ 塊內賦值(=或<=)左側使用。4.wire是assign語句左側唯一的合法類型(assign 后面跟著的必須是一個 wire 類型)5.wire只能用于組合邏輯reg 用法總結1. 聲明寄存器,可以存儲信息(有內存,有狀態)允許連接到模塊的輸入端口,但不能連接到一個模塊的實例化的輸出2. 在模塊聲明中,reg可以用作輸出,但不能用作輸入3. 在always@(......)語句塊內,= 或者 <= 賦值語句的左邊必須是是reg變量在initial語句塊內,= 賦值語句的左邊必須是是reg變量4. Reg不能用于assign賦值語句的左側5. 當與@(posedge clock)塊一起使用時,reg可用于創建寄存器6. reg可用于組合邏輯和時序邏輯
*//* 連續賦值語句(assign)用于對線型變量(wire)的賦值,不能夠出現在任何一個過程塊(begin ... end)中;連續賦值語句(assign)定義組合邏輯,聲明物理邏輯的關系;線型變量一旦被連續賦值語句賦值后,賦值語句右端表達式中的信號有任何變化,都將實時地反映到左端的線型變量中 *//* 過程賦值語句(= 和 <=)完成對寄存器變量(reg)的賦值,只能在過程塊語句中被賦值;過程賦值語句只有在語句被執行到時,賦值過程才能夠進行一次,而且賦值過程的具體執行時間還受到各種因素的影響 *//*數據類型:5'o37 5 位八進制數,二進制為 1111110'o37 右對齊,高位補 010'bx0x1 左邊補 x,完整即 x x x x x x x 0 x 1,x 表示未知狀態4'b1x_01 4 位二進制數,為 1 x 0 1,下劃線方便閱讀4'hz 4 位z(擴展的z) , 即 zzzz,z 表高阻狀態parameter SEC_TIME = 48_000_000; 十進制數位長不能夠為變量表達式,可以為預編譯、parameter 的表達式verilog 中 整形、浮點型等變量的 定義字 相當于 define 或者 parameter 的作用,這里只用 后二者即可了字符串reg [8*14 : 1]Message = "INTERNAL ERROR"; I 為第 1 位,N 為 第 2 位,依此類推數組reg [wordsize : 0]my_memory[arraysize : 0];引用數組某個數的某個位my_memory_1 = my_memory[1];my_memory_1_bit0 = my_memory_1[0];verilog 不支持 數組作為 模塊的輸入或輸出,systemVerilog 支持運算;算術運算符(+,-,x,/,%)賦值運算符(=,<=)關系運算符(>,<,>=,<=)邏輯運算符(&&,||,!)條件運算符(?;)位運算符 (~,|,^,&,^~) 對于 & 運算用法之一:assign max_avl_address = &avl_address; 則 avl_address 最大(全1)的時候 max_avl_address 為 1,否則為 0移位運算符(<<,>>)拼接運算符({})
*//*預編譯:宏定義:`define WIDTH 8引用:reg [`WIDTH-1:0] s1; 原樣替換`ifdef 宏名 (標識符)程序段1`else程序段2`endif
*//*for 語句,盡量不要用,要使用 計數器 + case 語句 來替代https://blog.csdn.net/messi_cyc/article/details/79098444
*//*使用語句實現 邊沿檢測https://blog.csdn.net/bleauchat/article/details/85322247
*//* 模塊注釋規范 */
/********************************************************************************************************** File Name : xxx.v* Author : xxx* Version : V1.0.0* Date : 20xx-xx-xx* Brief : xxxxx******************************************************************************************************** History* 1.Author: xxx* Date: 20xx-xx-xx* Mod: xxxxx** 2.Author: xxx* Date: 20xx-xx-xx* Mod: xxxxx*********************************************************************************************************/// *********************************************************************************
// Project Name :
// Author : xxx
// Email :
// Blogs :
// File Name : xxx.v
// Module Name :
// Called By :
// Abstract :
//
// CopyRight(c) 2018-2021, xxx Studio..
// All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// xxxx/xx/xx xx 1.0 Original
//
// *********************************************************************************
module example_module
(/*輸入信號*/input clk_in, /*時鐘輸入*/input rst_n_in, /*復位(低有效)信號輸入*//*輸出信號*/output reg [7:0]q_out, /*q 左移位輸出,要用語句塊賦值,所以定義為 寄存器類型 */output reg [7:0]p_out /*p 右移位輸出*//* 寄存器組定義 reg [7:0]Mem[0:1] 即 2 個 8 位的 Mem*/output output_1 = 0,output_2 /* 缺省為 wire 線網類型,可以定義初始值 *//* tri 主要用于定義三態的線網 */);/* 定義常量參數 */parameter bit_7 = 7,bit_8 = 8;/* 三目運算例子 wire [2:0] Student = Marks > 18 ? Grade_A : Grade_C;assign LR = (LR_select[1] == 1'b1) ? 1'bz : LR_select[0];*//* 時序邏輯定義,對 q 左移位輸出*/always @(posedge clk_in or negedge rst_n_in)begin/* 順序執行 */if(!rst_n_in)beginq_out <= 8'bzzZz_0001; /* 總線賦值 */endelsebeginq_out <= { q_out[6:0] , q_out[bit_7] }; /* 使用位拼接,左移位 */endend/*對 p 右移位輸出*/always @(posedge clk_in or negedge rst_n_in)beginif(rst_n_in == 1'b0)beginp_out <= 8'b1000_0000;endelsebeginp_out <= { p_out[0] , p_out[7:1] }; /* 使用位拼接,右移位 */endend/* 在模塊里面調用模塊,即 FA_struct 模塊例化,并建立連接 */FA_struct FA1(.A (q_out[1]),.B (p_out[1]),.C (rst_n_in),.output_1(), /* 該引腳懸空,如果是 example_module 模塊的輸入則變為高阻,如果是輸出則棄用 */ .output_2(output_2));endmodule/* case 語句例子
case(case_expr)case item_expr : procedural_statement;. . .. . .[default:procedural_statement]
endcase
*//* 門級描述組合邏輯電路 */
module FA_struct
(input A;input B;input C;output output_1;output output_2;
);/* 模塊內連線 */wire S1, T1, T2, T3;xor x1 (S1, A, B);xor x2 (output_1, S1, C);and A1 (T3, A, B );and A2 (T2, B, C);and A3 (T1, A, C);or O1 (output_2, T1, T2, T3 );endmodule/*一個參數化模塊設計例子定義:
module Sdram_Write
#( parameter DATA_WIDTH = 16, 注,#() 這個部分用于模塊參數化配置,對于 verilog 不可綜合parameter ADDR_WIDTH = 12,parameter ROW_DEPTH = 2,parameter COL_DEPTH = 256,parameter BURST_LENGTH = 4, //burst lengthparameter ACT_DEPTH = 1,parameter BREAK_PRE = 1
)
(input clk,input rst_n,input wr_trig,input wr_en,input ref_rq,output reg [3:0] wr_cmd,output reg [ADDR_WIDTH - 1:0] wr_addr,output wr_rq,output reg wr_end_flag,output [1:0] wr_bank_addr,output [DATA_WIDTH - 1:0] wr_data,//wfifooutput wfifo_rd_en,input [7:0] wfifo_rd_data
);例化:
Sdram_Write
#( .DATA_WIDTH ('d16),.ADDR_WIDTH ('d12),.ROW_DEPTH ('d1),.COL_DEPTH ('d4),.BURST_LENGTH ('d4), //burst length.ACT_DEPTH ('d1),.BREAK_PRE ('d1)
)
Sdram_Write_inst(.clk (clk),.rst_n (rst_n),.wr_trig (wr_trig),.wr_en (wr_en),.ref_rq (ref_rq),.wr_cmd (wr_cmd),.wr_addr (wr_addr),.wr_rq (wr_rq),.wr_end_flag (wr_end_flag),.wr_bank_addr (wr_bank_addr),.wr_data (wr_data),.wfifo_rd_en (wfifo_rd_en),.wfifo_rd_data (wfifo_rd_data)
);*/
編寫規范
-
以時鐘信號同步的時序邏輯編寫時盡量只用非阻塞賦值”<="(同步執行),用阻塞賦值”="(順序執行)可能會產生bug,后者一般用于組合邏輯設計。盡量避免使用異步信號(比如異步總線等),即慎用或少用 assign 語句連接邏輯,而盡量把所有邏輯在 always @(*) begin … end 中實現;如果傳入一個異步信號,盡量加寄存器(D觸發器)用時鐘進行鎖存。
-
盡量大部分功能使用時序邏輯電路設計,使用行為語句 + 時序邏輯電路描述(“always@” + “<=”) 完成建模(對于 reg 類型變量)。對于組合邏輯電路描述,簡單邏輯可以使用連續賦值語句(“assign” + “=”)(對于 wire 類型變量),對于復雜組合邏輯使用 “always@( 所有敏感信號 )” + “=” 的語句。
-
Always 塊的一般形式為:
/* 這里加注釋對該模塊進行功能描述 */ always @(negedge clk_in or negedge i2s_module_rst) /* 在時鐘的邊沿觸發,再加一個復位觸發條件 */beginif(!i2s_module_rst) /* 先判斷是否復位 */begin /* 在復位塊中,因該對 else 情況里面的所有 被幅值的 reg 變量進行 復位,都設置為復位值,必要! */WS <= 1'b1; /* 添加語句描述 */endelsebegin /* 保持格式 */if(one_flame_counter < half_flame_count)beginWS <= 1'b0;endelsebeginWS <= 1'b1;endendend
-
case 語句必須帶 default 分支,照顧到 case 的所有情況;if 語句必須帶 else 分支;即分支語句要 寫到/考慮 所有情況。
-
所有的內部寄存器都應該能夠被復位,盡量每個模塊都要有時鐘同步復位信號(不要用異步復位)。
-
設計邏輯盡量避免不定態 x 或者高阻態 z 進入參與關鍵邏輯區域,仿真時就注意。
-
移位操作直接用位拼接。
-
同一個信號在很多地方使用,比如參數和時鐘等等,應該在每一個用到的地方加一個寄存器(D觸發器)用于中繼緩沖,避免一個信號扇出信號數量過多。
-
常用的,時鐘上升沿鎖存數據,時鐘下降沿改變數據。
-
從可綜合性角度考慮,應慎用各種循環語句(for,while 等,因為編譯器僅將其展開成重復語句,過多占用邏輯),大部分情況下,用于設計的循環語句可以用其他方式所替代,比如用 case 語句替代循環語句。并行塊(fork … join)不可綜合且容易出現競爭問題,在仿真設計中不建議使用。
-
邏輯表達式不要寫的太長,可以簡化邏輯(卡諾圖法或者公式法,或者 multisim 里面的邏輯分析儀簡化邏輯表達式)或者分多行去寫,即不要讓 RTL 圖中某一段邏輯鏈過于長;長邏輯表達式用括號劃分清關系減少歧義。
-
競爭與冒險的概念:邏輯電路中,由于門的輸入信號經過不同的延時,到達門的時間不一致,這種情況叫競爭;由于競爭而導致輸出產生毛刺(瞬時錯誤),這一現象叫冒險。為避免組合邏輯的輸出出現“毛刺”,即冒險或競爭的發生,可以在輸出加一個寄存器(D觸發器),即讓輸出與時鐘同步,當所有信號都到達寄存器(D 觸發器)的輸入后,時鐘再“打一拍” 進行鎖存 才能輸出,這樣避免最后的輸出有“毛刺”;避免鎖存器,使用觸發器。
-
對于有 選擇 和 加法、比較 等邏輯塊,編寫時應讓信號先經過 選擇器,再送入 乘法器、加法器 或 比較器 等,即“先選后比,先選后加,先選后乘”。邏輯電路面積大小對比:乘法器 > 加法器 > 比較器 > 選擇器。
-
盡量不要用減法和除法(一個考慮多,一個面積大);乘以常數直接用 “*”,編譯器會優化;兩變量乘法用硬件乘法器IP。
-
使用 function 函數語句對復雜數值運算打包(它不能包含任何時間控制語句);函數(function)可以調用其他函數(function)但不能調用任務(task),(function)函數由 任務(task)或其它 module 中調用。使用 task 語句寫可重用的、固定下來的組合邏輯(不能有時序邏輯 always,不能有 wire 類型數據,這就是和 module 的區別;任務(task)可以調用其他任務(task)和函數(function),任務(task)只能在 module 中的語句塊中被調用)。
-
可以用 generate for 寫 同結構不同參數 的 always@(*) 等代碼,用 generate if/case 寫一些隨著需求可變的代碼或 IP 核。 generate 語句屬于預編譯語句。
-
FGPA 的功耗與被使用的觸發器或門電路的數量及其翻轉次數成正比,盡量減少高速翻轉的觸發器數量是降低 FPGA 功耗的根本方法之一。
設計技巧
p.s 以下內容引自 信息理論與技術教研中心 別志松 的 PPT 《復雜數字系統設計的常用技巧》,本文作者又做了一些補充。
解決速度與面積矛盾的基本原則:1、向關鍵路徑要速度。對于關鍵路徑,可以采用犧牲面積換取速度的方式。2、向非關鍵路徑要面積。對于非關鍵路徑,通過各種方式換取面積。
基本途徑:1、EDA工具的約束和優化方式的設置。2、優化代碼。以下就是代碼優化的一些方法。
速度的三重定義
- Throughput:吞吐量
- 單位時間內能處理的數據量。
- 常用單位有bit/s,等。
- Latency:時延
- 指的是從數據輸入到達至相應數據輸出之間的時間。
- 單位是微秒等。
- Timing:時序
- 指的是時序單元之間的路徑所對應的時延。
- 通常說時序關系不滿足一般指觸發器間關鍵路徑的最大時延超過目標時鐘周期。
- 標準度量是時鐘周期或頻率。
速度的三個方面的折中
- Latency 換 Throughput。
- Timing 換 Throughput。
- Timing 換 Latency 等。
提高吞吐量的方法
-
流水線技術
流水線技術就是把本來只能在一個較長的時鐘周期內執行的操作(組合邏輯)分成幾步較小的操作,并在多個較高速的時鐘內完成。這些步驟的劃分是通過多級寄存器來實現的。前級寄存器處理新輸入數據的同時,末級寄存器產生老輸入數據所對應的輸出。
-
多路并行處理
將耗時較長的電路復制若干份,每份處理部分數據。這種處理方法需要對輸入數據進行分解,對輸出數據進行合并。主要用于減小 Latency。
減小延時的主要思想
- 盡快將數據從輸入傳遞到輸出,減小中間過程處理時延,即減少數據處理鏈的長度。
- 流水線技術不符合低 Latency 的要求。經常采用的方法:
- 并行處理。
- 去掉流水線。
- 減小 Latency 的代價:有可能減少吞吐量;會造成關鍵路徑時延增大。
- 既要保證 Throughput,又要保證較小的 Latency,只能采取前述并行處理方法。
改善時序性能的方法
即提高最高運行速度。
-
添加中間寄存器層。流水線化。防止中間純組合邏輯鏈太長導致延時太長,即防止一個語句特別長運算特別多,所以中間添加寄存器層。
將多數據的數學運算分為多個運算的組合,這些組合是并行處理的。
-
關鍵路徑改造為并行結構。并行處理。減少串入串出,增加輸入和輸出的數量,寫為并入并出。
-
展平邏輯結構。也是增加并行。
-
減小扇出。
面積優化方法
- 精簡代碼。引用上面“編寫規范”一節:
- 對于有選擇和加法、比較等邏輯塊,編寫時應讓信號先經過選擇器,再送入乘法器、加法器或比較器等,即“先選后比,先選后加,先選后乘”。面積:乘法器 > 加法器 > 比較器 > 選擇器。
- 不使用除法和減法。等等等等。
- 資源共享。
- 模塊化。聲明會被高利用率的寄存器。
- 等。
- 合理使用復位信號。
乒乓操作
“乒乓操作” 是一個常常應用于數據流控制的處理技巧。數據緩沖模塊可以為 單\雙口 RAM、FIFO 等。向緩沖區 1 存數據的時候,緩沖區 2 向外出數據,向緩沖區 2 存數據的時候,緩沖區 1 向外出數據,以此循環。
- 乒乓操作的最大特點是通過 “輸入數據選擇單元 ”和 “輸出數據選擇單元” 按節拍、相互配合的切換,將經過緩沖的數據流沒有停頓地送到 “數據流運算處理模塊” 進行運算與處理。
- 把乒乓操作模塊當做一個整體,站在這個模塊的兩端看數據,輸入數據流和輸出數據流都是連續不斷的,沒有任何停頓,因此非常適合對數據流進行流水線式處理。
- 所以乒乓操作常常與流水線結合使用,完成數據的無縫緩沖與處理。
乒乓緩存的實施
- 兩個單口 RAM 方式。設計一個 MUX 模塊,輸入為一個類似 SRAM 接口,其后面控制兩個單口 RAM(外部 SRAM 芯片 或 FPGA 內建 RAM),輸出一個 類似 SRAM 接口芯片和 通知可讀的信號(通知后面,一個 RAM 塊已經寫滿可以快速讀出)。
- 一個雙口 RAM 方式。設計一個 MUX 模塊,輸入接口同上,其 后面控制 一個 雙口 RAM(外部芯片或 FPGA 內建),MUX 模塊的輸入數據就不斷循環 從地址 0 到最大地址 存進該 RAM,當存到一半的時候 輸出一個 通知可讀信號,即當 前半部分 存滿后 可以讀前半部分(此時 MUX模塊正在后半部分存),當后半部分存滿后可以讀后半部分(此時 MUX模塊又回到前半部分地址開始存),這樣 一個 雙口 RAM 的前后兩半作為兩塊 RAM 進行乒乓操作。