异步FIFO相对于同步FIFO设计的要点是由于读写时钟为不同时钟,因此在判断空满标志时候需要对读写地址进行跨时钟域处理后才能进行比较
1、方法
(1)读写地址跨时钟域处理
(2)空标志信号
- 将写地址同步到读时钟域(因为空标志用于读,空了就不能读);
- 判断同步后的写地址是否和读地址相等,如果相等表示写地址追上了读地址,产生空信号;
- 对于同步产生地址延迟问题,即相当于和更小的读地址进行比较,提前产生了空标志,停止读操作,实际还有数据存在,在逻辑上没有问题,依然正常工作;
(3)满信号标志
- 将读地址同步到写时钟域(因为满信号用于写操作,满了不能再写);
- 对同步后的读地址和写地址进行比较,如果写地址比读地址刚好多循环了一圈FIFO深度,则产生满信号,这种情况在格雷码比较的时候表现为:地址的高两位不同,剩余位相同;
- 如果地址产生延迟问题,就会和更小的写地址比较从而提前产生满信号,从而停止写操作,而实际还有空间可以写,在逻辑上依然没有问题正常工作;
2、Verilog实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| module fifo_async #( parameter ADDR_WIDTH = 4, parameter DATA_WIDTH = 8 ) ( input clkr, input clkw, input rstn, input wen, input [DATA_WIDTH-1:0] din, input ren, output [DATA_WIDTH-1:0] dout, output empty, output full );
parameter DEPTH = 1<<ADDR_WIDTH; ... endmodule
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| reg [ADDR_WIDTH:0] wr_ptr_bin; reg [ADDR_WIDTH:0] wr_ptr_gray;
wire [ADDR_WIDTH:0] next_wr_ptr_bin; wire [ADDR_WIDTH:0] next_wr_ptr_gray;
assign next_wr_ptr_bin = (wen & (!full)) ? (wr_ptr_bin+1) : wr_ptr_bin; assign next_wr_ptr_gray = (next_wr_ptr_bin>>1) ^ next_wr_ptr_bin;
always @(posedge clkw or negedge rstn) begin if(!rstn) begin wr_ptr_bin <= 0; wr_ptr_gray <= 0; end else begin wr_ptr_bin <= next_wr_ptr_bin; wr_ptr_gray <= next_wr_ptr_gray; end end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| reg [ADDR_WIDTH:0] rd_ptr_bin; reg [ADDR_WIDTH:0] rd_ptr_gray;
wire [ADDR_WIDTH:0] next_rd_ptr_bin; wire [ADDR_WIDTH:0] next_rd_ptr_gray;
assign next_rd_ptr_bin = (ren & (!empty))? (rd_ptr_bin+1) : rd_ptr_bin; assign next_rd_ptr_gray = (next_rd_ptr_bin>>1) ^ next_rd_ptr_bin;
always @(posedge clkr or negedge rstn) begin if(!rstn) begin rd_ptr_bin <= 0; rd_ptr_gray <= 0; end else begin rd_ptr_bin <= next_rd_ptr_bin; rd_ptr_gray <= next_rd_ptr_gray; end end
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| reg [ADDR_WIDTH:0] wr_ptr_gray_ff1; reg [ADDR_WIDTH:0] wr_ptr_gray_ff2;
always @(posedge clkr or negedge rstn) begin if(!rstn) begin wr_ptr_gray_ff1 <= 0; wr_ptr_gray_ff2 <= 0; end else begin wr_ptr_gray_ff1 <= wr_ptr_gray; wr_ptr_gray_ff2 <= wr_ptr_gray_ff1; end end
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| reg [ADDR_WIDTH:0] rd_ptr_gray_ff1; reg [ADDR_WIDTH:0] rd_ptr_gray_ff2;
always @(posedge clkw or negedge rstn) begin if(!rstn) begin rd_ptr_gray_ff1 <= 0; rd_ptr_gray_ff2 <= 0; end else begin rd_ptr_gray_ff1 <= rd_ptr_gray; rd_ptr_gray_ff2 <= rd_ptr_gray_ff1; end end
|
1
| assign empty = (wr_ptr_gray_ff2 == rd_ptr_gray);
|
1
| assign full = (rd_ptr_gray_ff2 == {~wr_ptr_gray[ADDR_WIDTH:ADDR_WIDTH-1], wr_ptr_gray[ADDR_WIDTH-2:0]});
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| assign wr_addr = wr_ptr_bin[ADDR_WIDTH-1:0]; assign rd_addr = rd_ptr_bin[ADDR_WIDTH-1:0];
dual_ram # ( .ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(DATA_WIDTH) ) m_dual_ram( .clkr(clkr), .clkw(clkw), .we(wen&(!full)), .wr_addr(wr_addr), .din(din), .re(ren&(!empty)), .rd_addr(rd_addr), .dout(dout) );
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| module dual_ram #( parameter ADDR_WIDTH = 4, parameter DATA_WIDTH = 8 ) ( input clkr, input clkw, input we, input [ADDR_WIDTH-1:0] wr_addr, input [DATA_WIDTH-1:0] din, input re, input [ADDR_WIDTH-1:0] rd_addr, output reg [DATA_WIDTH-1:0] dout );
parameter DEPTH = 1<<ADDR_WIDTH; reg [DATA_WIDTH-1:0] mem[0:DEPTH-1];
always @(posedge clkw) begin if(we) begin mem[wr_addr] <= din; end end
always @(posedge clkr) begin if(re) begin dout <= mem[rd_addr]; end end
endmodule
|
3、仿真
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| `timescale 1ns/100ps
module tb_fifo_async();
parameter CLK_PERIOD = 5;
reg clk; reg rstn;
initial clk = 1'b0; always #CLK_PERIOD clk = ~clk;
reg clkr; reg clkw;
initial clkr = 1'b0; always #10 clkr = ~clkr;
initial clkw = 1'b0; always #40 clkw = ~clkw;
reg [7:0] din; reg wen; reg ren;
wire [7:0] dout; wire empty; wire full;
initial begin rstn = 1'b1; #200; rstn = 1'b0; repeat(4) @(posedge clk); rstn <= 1'b1; end
integer i = 0; integer cnt = 30; initial begin
din <= 8'h20; wen <= 0; ren <= 0; @(posedge rstn); wen <= 1'b1; for(i=0; i<cnt; i = i+1) begin @(posedge clkw); din <= din + 1;
if(i==17) ren <= 1'b1; end
#300; @(posedge clkr) ren <= 1'b1; for(i =0; i<cnt; i=i+1) begin @(posedge clkr); end
#400; $finish(); end
fifo_async m_fifo_async( .clkr(clkr), .clkw(clkw), .rstn(rstn), .wen(wen), .din(din), .ren(ren), .dout(dout), .empty(empty), .full(full) );
endmodule
|