異步FIFO實際應用場景很多,代碼風格也各不相同,但核心應該都是圍繞着兩個:
1. 跨時鐘域同步
2. 空滿檢測
最經典的文獻還是 cliff cummings 的兩篇文章
本文對FIFO進行簡單的實作,進行了簡單的讀寫和空滿觀測
fifo參數可配
memory模型可替換
// top
//fifo
module async_fifo #(
parameter DEPTH =16,
parameter WIDTH = 12,
parameter ADDR = 4
) (
input wire wclk,
input wire rclk,
input wire rrst_n,
input wire wrst_n,
input wire wen,
input wire ren,
input wire [WIDTH-1:0] din,
//output
output wire [WIDTH-1:0] qout
);
reg [ADDR:0] wptr;
reg [ADDR:0] rptr;
wire full;
wire empty;
wire [ADDR-1:0] waddr = wptr[ADDR-1:0];
wire [ADDR-1:0] raddr = rptr[ADDR-1:0];
[email protected](posedge wclk or negedge wrst_n) begin
if(~wrst_n)
wptr <= 'h0;
else if(~wen && (~full))
wptr <= wptr + 1;
else wptr <= wptr;
end
//=========================================================
[email protected](posedge rclk or negedge rrst_n) begin
if(~rrst_n)
rptr <= 'h0;
else if(~ren && (~empty))
rptr <= rptr + 1;
else
rptr <= rptr;
end
// bin2gray
wire [ADDR:0] g_wptr = (wptr >>1) ^ wptr;
wire [ADDR:0] g_rptr = (rptr >>1) ^ rptr;
//
wire [ADDR:0] g_rptr_sync;
wire [ADDR:0] g_wptr_sync;
sync3 #(.ADDR(4)) sync_r2w(//input
.upstream_in(g_rptr),
.dst_clk(wclk),
.dst_rst(wrst_n),
.sync_out(g_rptr_sync));
sync3 #(.ADDR(4))sync_w2r(//input
.upstream_in(g_wptr),
.dst_clk(rclk),
.dst_rst(rrst_n),
.sync_out(g_wptr_sync));
fifo_ctlr #(.ADDR(4)) u_fifo_ctrl (//input
.g_rptr_sync(g_rptr_sync),//gray ptr
.g_wptr_sync(g_wptr_sync),//gray ptr
.g_rptr(g_rptr),//gray ptr
.g_wptr(g_wptr),//gray ptr
.wclk(wclk),
.rclk(rclk),
.rrst_n(rrst_n),
.wrst_n(wrst_n),
.empty(empty),
.full(full));
register_fifo #(
.DEPTH(16),
.WIDTH(12),
.ADDR( 4)
) u_fifo_mem (//input
.wclk(wclk),
.rclk(rclk),
.wrst_n(wrst_n),
.rrst_n(rrst_n),
.wen(wen),
.ren(ren),
.din(din),
.raddr(raddr),
.waddr(waddr),
// output
.qout(qout));
endmodule
// synchronizer
module sync3 #(parameter ADDR = 4
)(//input
input wire [ADDR:0] upstream_in,
input wire dst_clk,
input wire dst_rst,
output wire [ADDR:0] sync_out
);
reg [ADDR:0] sync0_reg;
reg [ADDR:0] sync1_reg;
reg [ADDR:0] sync2_reg;
always @(posedge dst_clk or negedge dst_rst) begin
if(~dst_rst) begin
sync0_reg <= {ADDR{1'b0}};
sync1_reg <= {ADDR{1'b0}};
sync2_reg <= {ADDR{1'b0}};
end else begin
sync0_reg <= upstream_in;
sync1_reg <= sync0_reg;
sync2_reg <= sync1_reg;
end
end
assign sync_out = sync2_reg;
endmodule
// fifo ctlr
module fifo_ctlr #(parameter ADDR = 4
)(//input
input wire [ADDR:0] g_rptr_sync,//gray ptr
input wire [ADDR:0] g_wptr_sync,//gray ptr
input wire [ADDR:0] g_rptr,//gray ptr
input wire [ADDR:0] g_wptr,//gray ptr
input wire wclk,
input wire rclk,
input wire rrst_n,
input wire wrst_n,
output wire empty,
output wire full
);
assign empty = g_wptr_sync==g_rptr;
assign full = {~g_rptr_sync[ADDR],~g_rptr_sync[ADDR-1],g_rptr_sync[ADDR-2:0]}==g_wptr;
endmodule
// memory model
module register_fifo #(
parameter DEPTH = 16,
parameter WIDTH = 12,
parameter ADDR = 4
) (//input
input wire wclk,
input wire rclk,
input wire wrst_n,
input wire rrst_n,
input wire wen,
input wire ren,
input wire [WIDTH-1:0] din,
input wire [ADDR-1 :0] waddr,
input wire [ADDR-1 :0] raddr,
// output
output reg [WIDTH-1:0] qout);
reg [WIDTH-1:0] mem [0:DEPTH-1];
integer i;
always @(posedge wclk or negedge wrst_n) begin
if (~wrst_n) begin
//generate
for (i=0;i<DEPTH;i=i+1) begin:RF
mem[i] <= {WIDTH{1'b0}};
end
//endgenerate
end else if(~wen)
mem[waddr] <= din;
end
always @(posedge rclk or negedge rrst_n) begin
if (~rrst_n) begin
qout <= {WIDTH{1'b0}};
end else if(~ren)
qout<= mem[raddr];
end
endmodule
//tb
`timescale 1ns/1ps
module tb ();
parameter WIDTH = 12;
reg clk;
reg rst_n;
reg rst_n_clkn;
reg [7:0] div_cfg;
reg [WIDTH-1:0] din;
wire [WIDTH-1:0] qout;
reg wen;
reg ren;
reg wrst_n;
reg rrst_n;
wire clk_o;
wire clk_o_new;
wire clk_o_oe;
initial begin
clk = 0;
rst_n = 1'b0;
wrst_n = 1'b0;
rrst_n = 1'b0;
wen = 1'b1;
ren = 1'b1;
rst_n_clkn = 1'b0;
div_cfg = 8'h02;
#2
@(posedge clk)
rst_n = 1'b1;
@(negedge clk)
rst_n_clkn = 1'b1;
wrst_n = 1'b1;
rrst_n = 1'b1;
wen =1'b0;
ren = 1'b0;
repeat (20) @(posedge clk) din=$random/60;
#50
$finish;
end
//initial begin
//end
always #0.5 clk = ~clk;
async_fifo #(
.DEPTH(16),
.WIDTH(12),
.ADDR(4)
) inst_afifo (
.wclk(clk),
.rclk(clk_o_oe),
.wen(wen),
.ren(ren),
//.waddr(waddr),
//.raddr(raddr),
.rrst_n(rrst_n),
.wrst_n(wrst_n),
.din(din),
//output
.qout(qout));
odd_even_div inst_odd_even_div (
//input
.clk(clk),
.rst_n(rst_n),
.rst_n_clkn(rst_n_clkn),
.div_cfg(div_cfg),
// output
.done(),
.clk_o(clk_o_oe)
);
endmodule
// clk div
module odd_even_div (
//input
input wire clk,
input wire rst_n,
input wire rst_n_clkn,
input wire [7:0] div_cfg,
// output
output wire done,
output wire clk_o
);
/// counter
// reg [7:0] div_cfg;
reg div_clk_r;
reg div_clk_f;
reg [7:0] cnt;
wire [7:0] signal_chg_1st;
wire [7:0] signal_chg_2nd;
wire [7:0] signal_shift_r;
wire clko_pre;
wire even_or_odd;
// control div_cfg changes
reg [7:0] div_cfg_r;
reg [2:0] div_cfg_chg;
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
div_cfg_r <= 8'h02;
div_cfg_chg<=3'b000;
end else if (div_cfg!=div_cfg_r && |div_cfg && ~div_clk_r&& ~div_clk_f) begin
div_cfg_chg<=3'b111;
div_cfg_r <= div_cfg;
end else
div_cfg_chg<={div_cfg_chg[1:0],1'b0};
end
//
assign done = ~div_cfg_chg[2];
assign even_or_odd = ^div_cfg_r;
//wire clko;
assign signal_shift_r = div_cfg_r >>1;
assign signal_chg_1st = even_or_odd ? div_cfg_r>> 1: (div_cfg_r-{{7{1'b0}},1'b1})>>1;
assign signal_chg_2nd = div_cfg_r;
always @(posedge clk or negedge rst_n) begin
if (~rst_n)
cnt <= 8'h00;
else if (div_cfg_chg[2]) //synchronous reset counter and stay 3 cycles after div_cfg changes
cnt <= 8'h00;
else if (cnt==div_cfg_r)
cnt <= 8'h01;
else
cnt <= cnt +1;
end
always @(posedge clk or negedge rst_n) begin
if (~rst_n)
div_clk_r <= 1'b0;
else if (div_cfg_chg[2])
div_clk_r <= 1'b0;
else if (cnt==8'h00)
div_clk_r <= 1'b1;
else if (cnt== signal_chg_1st || cnt== signal_chg_2nd)
div_clk_r <= ~div_clk_r;
end
always @(negedge clk or negedge rst_n_clkn) begin
if (~rst_n_clkn)
div_clk_f <= 1'b0;
else if (div_cfg_chg[2])
div_clk_f <= 1'b0;
else if (cnt==8'h01)
div_clk_f <= 1'b1;
else if (cnt== signal_chg_1st +'b1 )
div_clk_f <= ~div_clk_f;
end
assign clko_pre = even_or_odd? div_clk_r : div_clk_f || div_clk_r;
assign clk_o = clko_pre &(~div_cfg_chg[2]);
endmodule
标題
First In First Out
data in
data out