天天看點

異步fifo 簡單實作-Verilog

異步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
           
異步fifo 簡單實作-Verilog

标題

First In First Out

data in

異步fifo 簡單實作-Verilog

data out 

異步fifo 簡單實作-Verilog

繼續閱讀