前言
工作中做了太久方案,还是要继续对代码手感的保持,这次完成握手型同步FIFO的RTL设计,设计规格如下:
- 写入与读出均采用握手型接口;
- 支持2的整数次与非整数次深度;
- 对外输出逻辑时序较优;
RTL设计
对外接口
握手型同步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
根据我的设计要求,对外输出的in_ready和out_valid必须为寄存器输出,以保证时序最优。out_data最好也要作为寄存器输出,但是out_data必然会有MUX逻辑,在深度较浅时时序会比较好,深度太深MUX逻辑带来的时序就不能忽略了。
逻辑设计
同步FIFO的设计思路在 【芯片前端】保持代码手感——同步FIFO 中已经盘过了,这次注重握手逻辑的规划,对于涉及过得设计思路不再赘述。
写入/读出ADDR逻辑
写入计数器主要调整了触发器en端的逻辑,把输入握手作为使能信号:
//================================================================== //写入计数器 //================================================================== 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) ? 0 : waddr[DP_WD-1:0] + 1; always @(posedge clk or negedge rst_n)begin if(~rst_n) waddr <= 0; else if(wenc) waddr <= {waddr_d_h, waddr_d_l}; end
读出计数器同理:
//================================================================== //读出计数器 //================================================================== 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) ? 0 : raddr[DP_WD-1:0] + 1; 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深度计数器
fifo深度的计算我做了一些调整,fifi_cnt也作为寄存器输出,主要是我需要用fifi_cnt_d这个变量,所以是在数据读写的当拍产生fifi_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
数据寄存
数据寄存,寄存器不复位降功耗,出口有一个多路的MUX,WIDTH*DEPTH越大,逻辑越深绕线越难:
//================================================================== //数据寄存 //================================================================== 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_verification进行仿真,得到波形图: