按照正常的思路,在前文完成前向時序優化和后向時序優化后,后面緊跟的應該是雙向時序優化策略了,不過不急,需要先實現一下握手型同步FIFO。本章所述的FIFO具備以下的特性:
1.寫入與讀出均采用握手型接口;
2.支持2的整數次與非整數次深度;
3.FIFO寫入數據至少一拍后才能讀出,不能bypass;
4.輸出邏輯為寄存器輸出;
接口
握手型同步FIFO,接口如下:
module sync_fifo #(
parameter DEPTH = 8,
parameter WIDTH = 32
)(
input clk,
input rst_n,
input in_valid,
input [WIDTH -1:0] in_data,
output in_ready,
output out_valid,
output [WIDTH -1:0] out_data,
input out_ready
);
localparam DP_WD = $clog2(DEPTH);
endmodule
鑒于并非所有的工具和版本都能很好的支持$clog2函數,所以也可以將該函數展開來寫。
根據通常的設計要求,對外輸出的in_ready和out_valid最優為寄存器輸出,以保證時序最優。out_data雖然理論上要作為寄存器輸出,但是out_data可以接受有mux邏輯。不過這樣在深度較淺時時序會比較好,深度太深mux邏輯帶來的時序則不能忽略。
讀寫計數器
fifo采用傳統的讀寫兩個計數器模式,以寫計數器為例進行說明,代碼如下:
//==================================================================
//寫入計數器
//==================================================================
reg [DP_WD :0]waddr;
wire wenc;
wire waddr_d_h;
wire [DP_WD -1:0]waddr_d_l;
assign wenc = in_valid && in_ready;
assign waddr_d_h = (waddr[DP_WD-1:0] == DEPTH-1) ? ~waddr[DP_WD] : waddr[DP_WD];
assign waddr_d_l = (waddr[DP_WD-1:0] == DEPTH-1) ? {DP_WD{1'b0}} : waddr[DP_WD-1:0] + 1'b1;
always @(posedge clk or negedge rst_n)begin
if(~rst_n) waddr <= 0;
else if(wenc) waddr <= {waddr_d_h, waddr_d_l};
end
地址指針waddr和raddr均比實際地址多一位,最高位用來指示套圈情況。當waddr和raddr的最高位相同時,fifo_cnt = waddr - raddr;當waddr和raddr的最高位相反時,fifo_cnt = DEPTH + waddr[ADDR_WIDTH-1:0] - raddr[ADDR_WIDTH-1:0]。
注意很多示意代碼會讓waddr一直累加翻轉,這樣的做法對于2^N深度的FIFO是沒有問題的,而如果配置深度為非2^N深度則會出現跳轉錯誤,因此在本代碼中將addr區分為H和L兩個區間,低位區間累加至DEPTH-1時高位區間翻轉。
讀出計數器的代碼同樣如此:
//==================================================================
//讀出計數器
//==================================================================
reg [DP_WD :0]raddr;
wire renc;
wire raddr_d_h;
wire [DP_WD -1:0]raddr_d_l;
assign renc = out_valid && out_ready;
assign raddr_d_h = (raddr[DP_WD-1:0] == DEPTH-1) ? ~raddr[DP_WD] : raddr[DP_WD];
assign raddr_d_l = (raddr[DP_WD-1:0] == DEPTH-1) ? {DP_WD{1'b0}} : raddr[DP_WD-1:0] + 1'b1;
always @(posedge clk or negedge rst_n)begin
if(~rst_n) raddr <= 0;
else if(renc) raddr <= {raddr_d_h, raddr_d_l};
end
深度計數器
通常FIFO的深度計數是通過waddr和raddr計算得到的,不過本文因為希望關鍵輸出為寄存器輸出需要用到fifo_cnt_d,因此做了一個深度計數器:
//==================================================================
//深度計數器
//==================================================================
reg [DP_WD :0]fifo_cnt_q;
wire [DP_WD :0]waddr_d = wenc ? {waddr_d_h, waddr_d_l} : waddr;
wire [DP_WD :0]raddr_d = renc ? {raddr_d_h, raddr_d_l} : raddr;
wire [DP_WD :0]fifo_cnt_d = (waddr_d[DP_WD] == raddr_d[DP_WD]) ? (waddr_d[DP_WD-1:0] - raddr_d[DP_WD-1:0]):
(waddr_d[DP_WD-1:0] + DEPTH - raddr_d[DP_WD-1:0]);
wire fifo_cnt_en = (wenc ^ renc);
always @(posedge clk or negedge rst_n)begin
if(~rst_n) fifo_cnt_q <= 0;
else if(fifo_cnt_en) fifo_cnt_q <= fifo_cnt_d;
end
數據寄存
數據寄存采用無復位寄存器以降低功耗,當然也可以使用sram實現,但是如果通過sram實現的話就需要考慮ram輸出的多一拍延遲了,如果深度不是很深的話,建議直接通過寄存器實現吧。
//==================================================================
//數據寄存
//==================================================================
reg [WIDTH -1:0]data[DEPTH];
always @(posedge clk or negedge rst_n)begin
if(wenc) data[waddr[DP_WD-1:0]] <= in_data;
end
assign out_data = data[raddr[DP_WD-1:0]];
對外邏輯
in_ready實際就是常用FIFO的full信號取反,out_valid就是empty的取反,因此單純做邏輯是不難的。但是為了實現寄存器輸出,最后選擇了這樣的邏輯:
//==================================================================
//對外邏輯
//==================================================================
//assign in_ready = (fifo_cnt_q < DEPTH);
//assign out_valid = (fifo_cnt_q > {DP_WD{1'b0}});
wire in_ready_en;
wire in_ready_d;
reg in_ready_q;
assign in_ready_en = (out_valid && out_ready) || in_ready;
assign in_ready_d = (fifo_cnt_d < DEPTH);
always @(posedge clk or negedge rst_n)begin
if(~rst_n) in_ready_q <= 1;
else if(in_ready_en)in_ready_q <= in_ready_d;
end
wire out_valid_en;
wire out_valid_d;
reg out_valid_q;
assign out_valid_en = (in_valid && in_ready) || out_valid;
assign out_valid_d = (fifo_cnt_d > {DP_WD{1'b0}});
always @(posedge clk or negedge rst_n)begin
if(~rst_n) out_valid_q <= 0;
else if(out_valid_en)out_valid_q <= out_valid_d;
end
assign in_ready = in_ready_q;
assign out_valid = out_valid_q;
好的所有代碼就是這些,auto_testbench的結果:
-
寄存器
+關注
關注
31文章
5336瀏覽量
120230 -
RAM
+關注
關注
8文章
1368瀏覽量
114641 -
計數器
+關注
關注
32文章
2256瀏覽量
94477 -
Mux
+關注
關注
0文章
38瀏覽量
23372 -
FIFO電路
+關注
關注
1文章
4瀏覽量
4898
發布評論請先 登錄
相關推薦
評論