题解 | #同步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 /**********************************SFIFO************************************/ 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 ); // 1.二进制地址 reg [$clog2(DEPTH)-1:0] waddr_bin; reg [$clog2(DEPTH)-1:0] raddr_bin; always@(posedge clk,negedge rst_n)begin if(!rst_n) waddr_bin <= 'd0; else if(winc && !wfull) waddr_bin <= waddr_bin + 'd1; end always@(posedge clk,negedge rst_n)begin if(!rst_n) raddr_bin <= 'd0; else if(rinc && !rempty) raddr_bin <= raddr_bin + 'd1; end // 2.计数器,控制空满flag reg [$clog2(DEPTH):0] cnt; always@(posedge clk,negedge rst_n)begin if(!rst_n) cnt <= 'd0; else if(winc && !wfull && rinc && !rempty) cnt <= cnt; else if(winc && !wfull) cnt <= cnt + 'd1; else if(rinc && !rempty) cnt <= cnt - 'd1; end //3. 空满flag always@(posedge clk,negedge rst_n)begin if(!rst_n) wfull <= 'd0; else if(cnt == DEPTH) wfull <= 'd1; else wfull <= 'd0; end always@(posedge clk,negedge rst_n)begin if(!rst_n) rempty <= 'd0; else if(cnt == 'd0) rempty <= 'd1; else rempty <= 'd0; end //4. RAM dual_port_RAM #(.DEPTH(DEPTH), .WIDTH(WIDTH)) u_dual_port_RAM( .wclk(clk), .wenc(winc && !wfull), .waddr(waddr_bin), //深度对2取对数,得到地址的位宽。 .wdata(wdata), //数据写入 .rclk(clk), .renc(rinc && !rempty), .raddr(raddr_bin), //深度对2取对数,得到地址的位宽。 .rdata(rdata) //数据输出 ); /* //读写地址模块 reg [$clog2(DEPTH)-1:0] waddr_bin; reg [$clog2(DEPTH)-1:0] raddr_bin; always@(posedge clk,negedge rst_n)begin if(!rst_n) waddr_bin <= 'd0; else if(~wfull && winc) waddr_bin <= waddr_bin + 'd1; end always@(posedge clk,negedge rst_n)begin if(!rst_n) raddr_bin <= 'd0; else if(~rempty && rinc) raddr_bin <= raddr_bin + 'd1; end //计数模块,控制空满 reg [$clog2(DEPTH)-1:0] cnt; always@(posedge clk, negedge rst_n)begin if(!rst_n) cnt <= 'd0; else if(winc & ~wfull & rinc & ~rempty) cnt <= cnt; else if(winc & ~wfull) cnt <= cnt + 'd1; else if(rinc & ~rempty) cnt <= cnt - 'd1; else cnt <= cnt; end //空满 必须和cnt临界值一起拉高 只需要判断cnt即可不需要判断winc了(类比异步FIFO只用判断读写指针) always@(*)begin if(!rst_n)begin wfull <= 'd0; rempty <= 'd0; end else begin wfull <= ( cnt == (DEPTH))? 1'd1: 1'd0; //计数到DEPTH-1拉高,会少写一个数... 因为这是组合逻辑 rempty <= ( cnt == 'd0 )? 1'd1: 1'd0; end end //RAM dual_port_RAM #( .DEPTH(DEPTH), .WIDTH(WIDTH) ) u_dual_port_RAM( .wclk(clk), .wenc(~wfull && winc), .waddr(waddr_bin), //深度对2取对数,得到地址的位宽。 .wdata(wdata), //数据写入 .rclk(clk), .renc(rinc & ~rempty), .raddr(raddr_bin), //深度对2取对数,得到地址的位宽。 .rdata(rdata) //数据输出 ); */ endmodule