题解 | #同步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