题解 | #同步FIFO#
同步FIFO
https://www.nowcoder.com/practice/3ece2bed6f044ceebd172a7bf5cfb416
//感觉自己还是有很多细节的地方没搞懂 `timescale 1ns/1ns /**********************************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 module sfifo#( parameter WIDTH = 8, parameter DEPTH = 16 )( input clk , //读写时钟 input rst_n , //异步复位 input winc , //写使能 input rinc , //写使能 input [WIDTH-1:0] wdata , //写数据 output reg wfull , //写满信号 output reg rempty , //读空信号 output wire [WIDTH-1:0] rdata //读数据 ); localparam WIDTH_ADDR = $clog2(DEPTH); //定义地址位宽 reg [WIDTH_ADDR-1:0] waddr,raddr; //读写地址 reg [WIDTH_ADDR:0] cnt;//这个用来计ram储存器有多少个数据未读,切记不能[WIDTH-1:0],个人理解因为是16个数据,16个状态,还要加上初始状态 wire wenc,renc; //读写使能 //地址计数器(计算当前读写地址之间的差值) always@(posedge clk or negedge rst_n) begin if(!rst_n) cnt <= 'd0; else if(winc && !rinc && !wfull && (cnt < DEPTH)) //如果写使能拉高,读使能拉低并且未写满情况下,cnt加1,也就多了一个未读的数据 cnt <= cnt + 1'd1; else if(!winc && rinc && !rempty && (cnt > 0))//如果读使能拉高,写使能拉低并且非空情况下,cnt减1,也就少了一个未读的数据 cnt <= cnt - 1'd1; else cnt <= cnt;//其他情况下不变,包括边读边写的时候他也不变 end //写满读空标志 always @(posedge clk or negedge rst_n)begin if(~rst_n)begin wfull <= 1'b0; rempty <= 1'b0;//复位情况下不应该是1吗这里?感觉这里应该是1,但可能会影响后面的逻辑,所以出错 end else if(cnt == DEPTH)//如果ram已经有了DEPTH(16)个未读数据,则拉高写满标志 wfull <= 1'b1; else if(cnt == 0)//如果ram只有0个数据,则拉高读空标志 rempty <= 1'b1; else begin //以后这样的还是分开写吧,忘了写这个else,找了大半天的问题 wfull <= 1'b0; rempty <= 1'b0; end end //写地址控制 always @(posedge clk or negedge rst_n)begin if(~rst_n) waddr <= 'd0; else if(!wfull && winc) //如果未满且写使能拉高,如果waddr已经到最大了DEPTH-1则置0,否则加1 waddr <= (waddr==(DEPTH-1))?('d0):(waddr+1'b1); else waddr <= waddr; end //读地址控制 always @(posedge clk or negedge rst_n)begin if(~rst_n) raddr <= 'd0; else if(!rempty && rinc) raddr <= (raddr==(DEPTH-1))?'d0:(raddr+1'b1); else raddr <= raddr; end dual_port_RAM inst( .wclk(clk), .wenc(wenc), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(renc), .raddr(raddr), .rdata(rdata) ); assign wenc = winc && !wfull;//只有ram非满且写使能,fifo的写使能才拉高 assign renc = rinc && !rempty; endmodule