题解 | #同步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 ); reg [$clog2(DEPTH) :0] fifo_cnt; reg [$clog2(DEPTH)-1:0] waddr_fifo; reg [$clog2(DEPTH)-1:0] raddr_fifo; always@(posedge clk or negedge rst_n)begin if(!rst_n) waddr_fifo <= 0; else if(!wfull && winc) waddr_fifo <= waddr_fifo + 1'd1; else waddr_fifo <= waddr_fifo; end always@(posedge clk or negedge rst_n)begin if(!rst_n) raddr_fifo <= 0; else if(!rempty && rinc) raddr_fifo <= raddr_fifo + 1'd1; else raddr_fifo <= raddr_fifo; end always@(posedge clk or negedge rst_n)begin if(!rst_n) fifo_cnt <= 0; else begin case({winc,rinc}) 2'b00:fifo_cnt <= fifo_cnt; //不读不写 2'b01: //仅仅读 if(fifo_cnt != 0) //fifo没有被读空 fifo_cnt <= fifo_cnt - 1'b1; //fifo个数-1 2'b10: //仅仅写 if(fifo_cnt != DEPTH) //fifo没有被写满 fifo_cnt <= fifo_cnt + 1'b1; //fifo个数+1 2'b11:fifo_cnt <= fifo_cnt; //读写同时 default:; endcase end end always@(posedge clk or negedge rst_n)begin if(!rst_n)begin wfull <= 1'b0; rempty <= 1'b0; end else begin wfull = (fifo_cnt == DEPTH) ? 1'b1 : 1'b0; rempty = (fifo_cnt == 0)? 1'b1 : 1'b0; end end dual_port_RAM #( .DEPTH (16), .WIDTH (8) )uut( .wclk(clk), .wenc(winc&~wfull), .waddr(waddr_fifo), .wdata(wdata), .rclk(clk), .renc(rinc&~rempty), .raddr(raddr_fifo), .rdata(rdata) ); endmodule
需要注意:
1、在端口连接时需统一时钟信号clk;
2、使用计数器fifo_cnt来控制满空状态,在计数器写满时为满,反之;
3、例化信号wenc应该与主模块winc&!wfull 即写使能与非满,同理renc;
4、地址控制时,读写地址分别根据使能信号与空满状态进行递增与清零