國內(nèi)最好的crm軟件南昌seo技術外包

前言
FIFO功能模塊分兩篇文章,本篇為同步FIFO,另一篇為異步FIFO,傳送門:
Verilog功能模塊——異步FIFO-CSDN博客
同步FIFO實現(xiàn)起來是異步FIFO的簡化版,所以,本博文不再介紹FIFO實現(xiàn)原理,感興趣的同學可以去看我異步FIFO的文章,基本看懂了異步FIFO,同步FIFO自然就懂了。
二. 模塊功能框圖與信號說明

信號說明:
分類 | 信號名稱 | 輸入/輸出 | 說明 |
---|---|---|---|
參數(shù) | DATA_WIDTH | – | 數(shù)據(jù)位寬 |
ADDR_WIDTH | – | 地址位寬,FIFO深度=2**ADDR_WIDTH | |
FWFT_EN | – | First word fall-through輸出模式使能,高電平有效 | |
FIFO寫端口 | din | input | FIFO數(shù)據(jù)輸入 |
wr_en | input | FIFO寫使能 | |
full | output | FIFO滿信號 | |
almost_full | output | FIFO快滿信號,FIFO剩余容量<=1時置高 | |
FIFO讀端口 | dout | output | FIFO數(shù)據(jù)輸出 |
rd_en | input | FIFO讀使能 | |
empty | output | FIFO空信號 | |
almost_empty | output | FIFO快空信號,FIFO內(nèi)數(shù)據(jù)量<=1時置高 | |
時鐘與復位 | clk | input | FIFO讀時鐘 |
rst | input | FIFO讀復位 |
注意:
- 信號的命名與Vivado中的FIFO IP核完全一致
- 復位均為高電平復位,與Vivado中的FIFO IP核保持一致
- 復位為異步復位
- FIFO深度通過ADDR_WIDTH來設置,所以FIFO的深度必然是2的指數(shù),如2、4、8、16等
三. 部分代碼展示
//++ 生成讀寫指針 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
reg [ADDR_WIDTH:0] rptr;
always @(posedge clk or posedge rst) beginif (rst)rptr <= 0;else if (rd_en & ~empty)rptr <= rptr + 1'b1;
endreg [ADDR_WIDTH:0] wptr;
always @(posedge clk or posedge rst) beginif (rst)wptr <= 0;else if (wr_en & ~full)wptr <= wptr + 1'b1;
endwire [ADDR_WIDTH-1:0] raddr = rptr[ADDR_WIDTH-1:0];
wire [ADDR_WIDTH-1:0] waddr = wptr[ADDR_WIDTH-1:0];wire [ADDR_WIDTH:0] rptr_p1 = rptr + 1'b1;
wire [ADDR_WIDTH:0] wptr_p1 = wptr + 1'b1;
//-- 生成讀寫指針 ------------------------------------------------------------//++ 生成empty與almost_empty信號 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
always @(*) beginif (rst)empty <= 1'b1;else if (rptr == wptr)empty <= 1'b1;elseempty <= 1'b0;
endalways @(*) beginif (rst)almost_empty <= 1'b1;else if (rptr_p1 == wptr || empty)almost_empty <= 1'b1;elsealmost_empty <= 1'b0;
end
//-- 生成empty與almost_empty信號 ------------------------------------------------------------//++ 生成full與almost_full信號 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
always @(*) beginif (rst)full <= 1'b1;else if ((wptr[ADDR_WIDTH] != rptr[ADDR_WIDTH])&& (wptr[ADDR_WIDTH-1:0] == rptr[ADDR_WIDTH-1:0]))full <= 1'b1;elsefull <= 1'b0;
endalways @(*) beginif (rst)almost_full <= 1'b1;else if (((wptr_p1[ADDR_WIDTH] != rptr[ADDR_WIDTH])&& (wptr_p1[ADDR_WIDTH-1:0] == rptr[ADDR_WIDTH-1:0]))|| full)almost_full <= 1'b1;elsealmost_full <= 1'b0;
end
//-- 生成full與almost_full信號 ------------------------------------------------------------
三. 功能仿真
比較以下情形中的fifo行為是否與FIFO IP核一致,
情形一:單次寫單次讀
情形二:寫滿后再讀空
情形三:在讀的過程中寫,在寫的過程中讀
判斷模塊功能正常的依據(jù):
- 寫入數(shù)據(jù)是否按順序正常讀出
- 空信號和滿信號是否正常輸出。
為方便比較,編寫了頂層文件,實例化了FIFO IP核與自編模塊,部分代碼如下:
vivado_sync_fifo vivado_sync_fifo_u0 (.clk (clk ), // input wire clk.rst (rst ), // input wire rst.din (din ), // input wire [7 : 0] din.wr_en (wr_en ), // input wire wr_en.rd_en (rd_en ), // input wire rd_en.dout (vivado_fifo_dout ), // output wire [7: 0] dout.full (vivado_fifo_full ), // output wire full.almost_full (vivado_fifo_almost_full ), // output wire almost_full.empty (vivado_fifo_empty ), // output wire empty.almost_empty (vivado_fifo_almost_empty)// output wire almost_empty
);syncFIFO # (.DATA_WIDTH (DATA_WIDTH),.ADDR_WIDTH (ADDR_WIDTH),.FWFT_EN (FWFT_EN )
) syncFIFO_inst (.din (din ),.wr_en (wr_en ),.full (full ),.almost_full (almost_full ),.dout (dout ),.rd_en (rd_en ),.empty (empty ),.almost_empty (almost_empty),.clk (clk ),.rst (rst )
);
testbench部分代碼如下:
// 生成時鐘
localparam CLKT = 2;
initial beginclk = 0;forever #(CLKT / 2) clk = ~clk;
end// 讀寫使能控制
initial beginrst = 1;#(CLKT * 2)rst = 0;wr_en = 0;rd_en = 0;#(CLKT * 2)wait(~full && ~vivado_fifo_full); // 兩個FIFO都從復位態(tài)恢復時開始寫// 寫入一個數(shù)據(jù)wr_en = 1;#(CLKT * 1)wr_en = 0;// 讀出一個數(shù)據(jù)wait(~empty && ~vivado_fifo_empty);// 兩個FIFO都非空時開始讀,比較讀數(shù)據(jù)和empty信號是否有差異rd_en = 1;#(CLKT * 1)rd_en = 0;// 寫滿wr_en = 1;wait(full && vivado_fifo_full); // 兩個FIFO都滿時停止寫,如果兩者不同時滿,則先滿的一方會有寫滿的情況發(fā)生,但對功能無影響// vivado FIFO IP在FWFT模式時, 設定深度16時實際深度為17, 但仿真顯示full會在寫入15個數(shù)據(jù)后置高, 過幾個時鐘后后拉低,// 再寫入一個數(shù)據(jù), full又置高; 然后過幾個時鐘又拉低, 再寫入一個數(shù)據(jù)置高, 如此才能寫入17個數(shù)據(jù)// 所以這里多等待12個wclk周期, 就是為了能真正寫滿vivado FWFT FIFO#(CLKT * 12)wr_en = 0;// 讀空wait(~empty && ~vivado_fifo_empty);rd_en = 1;wait(empty && vivado_fifo_empty); // 兩個FIFO都空時停止讀,如果兩者不同時空,則先空的一方會有讀空的情況發(fā)生,但對功能無影響rd_en = 0;#(CLKT * 10)$stop;
end// 使用以下代碼時,先注釋掉上面的讀寫使能控制initial
// 同時讀寫
// initial begin
// #(CLKT * 30)
// $stop;
// end// assign wr_en = ~full || ~vivado_fifo_full; // 未滿就一直寫
// assign rd_en = ~empty || ~vivado_fifo_empty; // 未空就一直讀always @(posedge clk) beginif (rst)din <= 0;else if (wr_en && ~full && ~vivado_fifo_full)din <= din + 1;
endendmodule
8bit,16深度,FWFT FIFO仿真,波形如下:

可以看到模塊輸出的自編fifo與vivado fwft fifo的寫端口和讀端口行為是一致的,只是可能會超前或滯后一定的clk周期。

可以看到empty拉低時,數(shù)據(jù)已經(jīng)有效了,所以自編模塊實現(xiàn)了FWFT功能,Vivado FIFO的實際深度為17,所以它多讀出了一個數(shù)據(jù),空信號更晚拉高。
因篇幅問題,其它條件下的仿真不再展示,感興趣的同學可通過更改testbench自行驗證。
- FWFT_EN改為0,注意同步修改Vivado FIFO的配置
四. 工程分享
Verilog功能模塊——同步FIFO,Vivado 2021.2工程。
歡迎大家關注我的公眾號:徐曉康的博客,回復以下四位數(shù)字獲取。
8302
建議復制過去不會碼錯字!
或者在我的碼云倉庫獲取,傳送門:
徐曉康/Verilog功能模塊 - 碼云 - 開源中國 (gitee.com)

徐曉康的博客持續(xù)分享高質量硬件、FPGA與嵌入式知識,軟件,工具等內(nèi)容,歡迎大家關注。