题意整理
本题要求实现异步FIFO,FIFO的位宽和深度可配置。
题解主体
异步FIFO结构如上图所示
1. 第1部分是双口RAM,用于数据的存储。
2. 第2部分是数据写入控制器
3. 第3部分是数据读取控制器
4. 读指针同步器
使用写时钟的两级触发器采集读指针,输出到数据写入控制器。
5. 写指针同步器
使用读时钟的两级触发器采集写指针,输出到数据读取控制器。
本题解采用的空满判断的方式是用格雷码的比较来产生空满信号。
如上图所示,使用4位格雷码作为深度为8的FIFO的读写指针。
将格雷码转换成四位二进制数,使用二进制数低三位作为访问RAM的地址。
与同步FIFO类似,当读写指针相等时,得出FIFO为空
当写指针比读指针多循环RAM一周时,此时读写指针的最高位和次高位都相反,其余位相同,FIFO为满。
Verilog代码描述如下:
module asyn_fifo#( parameter WIDTH = 8, parameter DEPTH = 16 )( input wclk , input rclk , input wrstn , input rrstn , input winc , input rinc , input [WIDTH-1:0] wdata , output wire wfull , output wire rempty , output wire [WIDTH-1:0] rdata ); parameter ADDR_WIDTH = $clog2(DEPTH); /**********************addr bin gen*************************/ reg [ADDR_WIDTH:0] waddr_bin; reg [ADDR_WIDTH:0] raddr_bin; always @(posedge wclk or negedge wrstn) begin if(~wrstn) begin waddr_bin <= 'd0; end else if(!wfull && winc)begin waddr_bin <= waddr_bin + 1'd1; end end always @(posedge rclk or negedge rrstn) begin if(~rrstn) begin raddr_bin <= 'd0; end else if(!rempty && rinc)begin raddr_bin <= raddr_bin + 1'd1; end end /**********************addr gray gen*************************/ wire [ADDR_WIDTH:0] waddr_gray; wire [ADDR_WIDTH:0] raddr_gray; reg [ADDR_WIDTH:0] wptr; reg [ADDR_WIDTH:0] rptr; assign waddr_gray = waddr_bin ^ (waddr_bin>>1); assign raddr_gray = raddr_bin ^ (raddr_bin>>1); always @(posedge wclk or negedge wrstn) begin if(~wrstn) begin wptr <= 'd0; end else begin wptr <= waddr_gray; end end always @(posedge rclk or negedge rrstn) begin if(~rrstn) begin rptr <= 'd0; end else begin rptr <= raddr_gray; end end /**********************syn addr gray*************************/ reg [ADDR_WIDTH:0] wptr_buff; reg [ADDR_WIDTH:0] wptr_syn; reg [ADDR_WIDTH:0] rptr_buff; reg [ADDR_WIDTH:0] rptr_syn; always @(posedge wclk or negedge wrstn) begin if(~wrstn) begin rptr_buff <= 'd0; rptr_syn <= 'd0; end else begin rptr_buff <= rptr; rptr_syn <= rptr_buff; end end always @(posedge rclk or negedge rrstn) begin if(~rrstn) begin wptr_buff <= 'd0; wptr_syn <= 'd0; end else begin wptr_buff <= wptr; wptr_syn <= wptr_buff; end end /**********************full empty gen*************************/ assign wfull = (wptr == {~rptr_syn[ADDR_WIDTH:ADDR_WIDTH-1],rptr_syn[ADDR_WIDTH-2:0]}); assign rempty = (rptr == wptr_syn); /**********************RAM*************************/ wire wen ; wire ren ; wire wren;//high write wire [ADDR_WIDTH-1:0] waddr; wire [ADDR_WIDTH-1:0] raddr; assign wen = winc & !wfull; assign ren = rinc & !rempty; assign waddr = waddr_bin[ADDR_WIDTH-1:0]; assign raddr = raddr_bin[ADDR_WIDTH-1:0]; dual_port_RAM #(.DEPTH(DEPTH), .WIDTH(WIDTH) )dual_port_RAM( .wclk (wclk), .wenc (wen), .waddr(waddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。 .wdata(wdata), //数据写入 .rclk (rclk), .renc (ren), .raddr(raddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。 .rdata(rdata) //数据输出 ); endmodule
参考答案
`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 /***************************************AFIFO*****************************************/ module asyn_fifo#( parameter WIDTH = 8, parameter DEPTH = 16 )( input wclk , input rclk , input wrstn , input rrstn , input winc , input rinc , input [WIDTH-1:0] wdata , output wire wfull , output wire rempty , output wire [WIDTH-1:0] rdata ); parameter ADDR_WIDTH = $clog2(DEPTH); /**********************addr bin gen*************************/ reg [ADDR_WIDTH:0] waddr_bin; reg [ADDR_WIDTH:0] raddr_bin; always @(posedge wclk or negedge wrstn) begin if(~wrstn) begin waddr_bin <= 'd0; end else if(!wfull && winc)begin waddr_bin <= waddr_bin + 1'd1; end end always @(posedge rclk or negedge rrstn) begin if(~rrstn) begin raddr_bin <= 'd0; end else if(!rempty && rinc)begin raddr_bin <= raddr_bin + 1'd1; end end /**********************addr gray gen*************************/ wire [ADDR_WIDTH:0] waddr_gray; wire [ADDR_WIDTH:0] raddr_gray; reg [ADDR_WIDTH:0] wptr; reg [ADDR_WIDTH:0] rptr; assign waddr_gray = waddr_bin ^ (waddr_bin>>1); assign raddr_gray = raddr_bin ^ (raddr_bin>>1); always @(posedge wclk or negedge wrstn) begin if(~wrstn) begin wptr <= 'd0; end else begin wptr <= waddr_gray; end end always @(posedge rclk or negedge rrstn) begin if(~rrstn) begin rptr <= 'd0; end else begin rptr <= raddr_gray; end end /**********************syn addr gray*************************/ reg [ADDR_WIDTH:0] wptr_buff; reg [ADDR_WIDTH:0] wptr_syn; reg [ADDR_WIDTH:0] rptr_buff; reg [ADDR_WIDTH:0] rptr_syn; always @(posedge wclk or negedge wrstn) begin if(~wrstn) begin rptr_buff <= 'd0; rptr_syn <= 'd0; end else begin rptr_buff <= rptr; rptr_syn <= rptr_buff; end end always @(posedge rclk or negedge rrstn) begin if(~rrstn) begin wptr_buff <= 'd0; wptr_syn <= 'd0; end else begin wptr_buff <= wptr; wptr_syn <= wptr_buff; end end /**********************full empty gen*************************/ assign wfull = (wptr == {~rptr_syn[ADDR_WIDTH:ADDR_WIDTH-1],rptr_syn[ADDR_WIDTH-2:0]}); assign rempty = (rptr == wptr_syn); /**********************RAM*************************/ wire wen ; wire ren ; wire wren;//high write wire [ADDR_WIDTH-1:0] waddr; wire [ADDR_WIDTH-1:0] raddr; assign wen = winc & !wfull; assign ren = rinc & !rempty; assign waddr = waddr_bin[ADDR_WIDTH-1:0]; assign raddr = raddr_bin[ADDR_WIDTH-1:0]; dual_port_RAM #(.DEPTH(DEPTH), .WIDTH(WIDTH) )dual_port_RAM( .wclk (wclk), .wenc (wen), .waddr(waddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。 .wdata(wdata), //数据写入 .rclk (rclk), .renc (ren), .raddr(raddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。 .rdata(rdata) //数据输出 ); endmodule