答案解析
最终提交的代码如下:
/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
parameter WIDTH = 8)(
input wclk
,input wenc
,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。
,input [WIDTH-1:0] wdata //数据写入
,input rclk
,input renc
,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。
,output reg [WIDTH-1:0] rdata //数据输出
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end
always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];
end
endmodule
/**********************************SFIFO************************************/
module sfifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input clk ,
input rst_n ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output wfull ,
output rempty ,
output wire [WIDTH-1:0] rdata
);
localparam DP_WD = $clog2(DEPTH);
reg [DP_WD :0]waddr;
wire wenc;
wire waddr_d_h;
wire [DP_WD -1:0]waddr_d_l;
assign wenc = winc & (!wfull);
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 = rinc & (!rempty);
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
wire [DP_WD :0]fifo_cnt = (waddr[DP_WD] == raddr[DP_WD]) ? waddr[DP_WD-1:0] - raddr[DP_WD-1:0]:
(waddr[DP_WD-1:0] + DEPTH - raddr[DP_WD-1:0]);
assign rempty = (fifo_cnt == 0);
assign wfull = (fifo_cnt == DEPTH);
dual_port_RAM #(.DEPTH(DEPTH), .WIDTH(WIDTH))
u_ram (
.wclk (clk),
.wenc (wenc),
.waddr (waddr[$clog2(DEPTH)-1:0]),
.wdata (wdata),
.rclk (clk),
.renc (renc),
.raddr (raddr[$clog2(DEPTH)-1:0]),
.rdata (rdata)
);
endmodule
关于读写地址以及fifo_cnt的产生,在FIFO里经典的做法读写地址均做循环累加:地址指针waddr和raddr均比实际地址多一位,最高位用来指示套圈情况。当waddr和raddr的最高位相同时,fifo_cnt = waddr-raddr;当waddr和raddr的最高位相反时,fifo_cnt = DEPTH + waddr[ADDR_WIDTH-1:0] - raddr[ADDR_WIDTH-1:0]。这种用最高位表示套圈的思路是没问题的,但是参考答案里的做法是有问题的,他的做法是直接定义waddr[ADDR_WIDTH:0]然后从0开始加,加到高位自动翻转:
always @(posedge clk or negedge rst_n) begin if(~rst_n) begin waddr <= 'd0; end else if(!wfull && winc)begin waddr <= waddr + 1'd1; end end
这样的做法只适用于深度为2^N的fifo,一旦深度非2^N那么addr就乱了。如果还要使用最高位标志套圈的思路,那么需要做出修改如下:
reg [DP_WD :0]waddr;
wire wenc;
wire waddr_d_h;
wire [DP_WD -1:0]waddr_d_l;
assign wenc = winc & (!wfull);
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
最高位彻底的作为标志位,当低位计数到DEPTH-1时,高位翻转。如此一来仍旧可以用经典的方式计算fifo_cnt:
wire [DP_WD :0]fifo_cnt = (waddr[DP_WD] == raddr[DP_WD]) ? waddr[DP_WD-1:0] - raddr[DP_WD-1:0]: (waddr[DP_WD-1:0] + DEPTH - raddr[DP_WD-1:0]);
当高位值一致时就waddr-raddr,当高位值不一样时就waddr+DEPTH-raddr,得到了目前的fifo内数据量,注意,此时计算的fifo_cnt在时序上处于winc/rinc的下一拍,也就是数据真正写入/读出的那一拍。 下一步空满信号,空满信号的产生原理也很简单,fifo_cnt==0时为空,fifo_cnt==DEPTH时为满。但是网站上给出的参考***括对照波形,wfull/rempty信号的输出都是在fifo_cnt的下一拍,即winc/rinc的延后两拍,这是有问题的,下面这段代码是按照对比波形写出来的,也就是对比pass的行为:
wire rempty_d = (fifo_cnt == 0); always @(posedge clk or negedge rst_n)begin if(~rst_n) rempty <= 0; else rempty <= rempty_d; end wire wfull_d = (fifo_cnt == DEPTH); always @(posedge clk or negedge rst_n)begin if(~rst_n) wfull <= 0; else wfull <= wfull_d; end
而后ram的wenc/renc的产生与wfull/rempty相关:
assign wenc = winc & (!wfull); assign renc = rinc & (!rempty);
wenc/renc的产生思路没有什么,当满时不再写入ram空时不再读取ram这个行为是合理的,当然fifo内部不看wfull/rempty也没有什么问题,毕竟fifo把wfull/rempty给到外面就是为了让控制器做处理的:wfull置起后,winc不能为高,否则写行为不可控可能数据覆盖;rempty置起后,rinc不能为高,否则读行为不可控读数据不准。
那么话题回到wfull/rempty,根据上面的分析,wfull/rempty这两个信号的反馈必须在winc/rinc的下一拍得到,否则控制器无法及时的调整winc/rinc逻辑。举个例子,当前深度16的FIFO内已有15个数,cyc0 winc起请求写下一个数,cyc1 数据写入,cys2 wfull信号起。那么在cyc1 winc仍然可以置起写数(因为没看到wfull),哪怕这个时候在fifo内做了ram保护 wenc = winc & (!wfull) 也没有用,因此cyc1在内部也没有看到wfull信号。
用上面那个通过了网站测评的代码做以下测试,向深度16的fifo里写了18个数,然后读数,可以看到第一个数据已经被覆盖:
同时后续的wfull/rempty信号也乱了,因为fifo_cnt连带着都跳乱了。因此wfull/rempty的产生必须在winc/rinc的下一拍:
wire rempty = (fifo_cnt == 0); wire wfull = (fifo_cnt == DEPTH);
当然了,这样也会导致fifo的winc/rinc时序变差(要看wfull/rempty),因此可以考虑winc/rinc当拍产生wfull_d/rempty_d,然后打拍得到wfull/rempty,代价是wfull_d/rempty_d的产生逻辑比较深,而且做起来稍微复杂了一点点(其实不复杂,就是要做一些选择信号啥的,我懒得做了)看取舍吧。我就用上面那种了:
这才是一个合理的fifo波形。
最后附一下testbench,也欢迎大家使用auto_testbench脚本(https://blog.csdn.net/moon9999/article/details/126912972):
`define DELAY(N, clk) begin \
repeat(N) @(posedge clk);\
#1ps;\
end
module testbench();
//-------------------------------------{{{common cfg
timeunit 1ns;
timeprecision 1ps;
initial $timeformat(-9,3,"ns",6);
string tc_name;
int tc_seed;
initial begin
if(!$value$plusargs("tc_name=%s", tc_name)) $error("no tc_name!");
else $display("tc name = %0s", tc_name);
if(!$value$plusargs("ntb_random_seed=%0d", tc_seed)) $error("no tc_seed");
else $display("tc seed = %0d", tc_seed);
end
//-------------------------------------}}}
//-------------------------------------{{{parameter declare
parameter WIDTH = 8;
parameter DEPTH = 16;
//-------------------------------------}}}
//-------------------------------------{{{signal declare
logic clk;
logic rst_n;
logic winc;
logic rinc;
logic [WIDTH-1:0] wdata;
logic wfull;
logic rempty;
logic [WIDTH-1:0] rdata;
//-------------------------------------}}}
//-------------------------------------{{{clk/rst cfg
initial forever #5ns clk = ~clk;
initial begin
rst_n = 1'b0;
`DELAY(30, clk);
rst_n = 1'b1;
end
initial begin
#100000ns $finish;
end
//-------------------------------------}}}
//-------------------------------------{{{valid sig assign
//-------------------------------------}}}
//-------------------------------------{{{ready sig assign
//-------------------------------------}}}
//-------------------------------------{{{data sig assign
always @(posedge clk or negedge rst_n)begin
if(!rst_n) wdata <= 0;
else wdata <= wdata + 1;
end
//-------------------------------------}}}
//-------------------------------------{{{other sig assign
initial begin
winc = 0;
rinc = 0;
`DELAY(50, clk);
winc = 1;
`DELAY(12, clk);
rinc = 1;
`DELAY(6, clk);
winc = 0;
`DELAY(3, clk);
rinc = 0;
`DELAY(2, clk);
winc = 1;
`DELAY(8, clk);
winc = 0;
`DELAY(8, clk);
rinc = 1;
end
//-------------------------------------}}}
//-------------------------------------{{{rtl inst
sfifo #(
.WIDTH(WIDTH),
.DEPTH(DEPTH))
u_sfifo(
.clk(clk),
.rst_n(rst_n),
.winc(winc),
.rinc(rinc),
.wdata(wdata),
.wfull(wfull),
.rempty(rempty),
.rdata(rdata)
);
//-------------------------------------}}}
endmodule



京公网安备 11010502036488号