FWFT-FIFO的同步和异步verilog代码
//-----------------------------------------------------------------------------
// File Name : fifo_sync.v
//-----------------------------------------------------------------------------
//参数化同步FIFO。DEPTH可以是任何大于0的整数值。实际使用的内存深度是depth,如果depth不是2的幂,
//则四舍五入到下一个2的幂。然而,满/空标志将表现得好像实际深度是depth。
//例如,DEPTH = 1000将使用ram大小WIDTH x 1024,但当level = 1000并且不执行后续写入时,
//将断言full flag。
//
// XST will synthesize to a block RAM if the size is large enough.
//
// parameter ranges:
//
// DEPTH powers of 2, > 1
// WIDTH > 0
//
// Actual write depth = DEPTH
// Read latency = 1
//
//=============================================================================
module fifo_sync_fwft
#(
parameter RAM_STYLE = "block", //block, distributed, or ultra
parameter WIDTH = 8,
parameter DEPTH = 1024
)
(
// use 'old style' ports since 'level' needs a calculated width
input sreset,
input clk,
input wr_en,
input [WIDTH-1:0] wr_data,
output reg full = 1'b0,
output reg alm_full = 1'b0,
output reg overflow = 1'b0,
input rd_en,
output [WIDTH-1:0] rd_data,
output reg empty = 1'b1,
output reg underflow = 1'b0,
output reg [$clog2(DEPTH+1)-1:0] level = 0
);
// `include "sv_clog2.vh"
localparam LVL_WID = $clog2(DEPTH+1);
localparam AWID = $clog2(DEPTH);
// variable length constants
localparam [LVL_WID-1:0] LEVEL_ZERO = {LVL_WID {1'b0}};
localparam [LVL_WID-1:0] LEVEL_ONE = {{LVL_WID-1 {1'b0}}, 1'b1};
localparam [AWID-1:0] PTR_ZERO = {AWID {1'b0}};
localparam [AWID-1:0] PTR_ONE = {{AWID-1 {1'b0}}, 1'b1};
// pointers
reg [AWID-1:0] wr_ptr = PTR_ZERO;
reg [AWID-1:0] rd_ptr = PTR_ZERO;
reg [AWID-1:0] rd_ptr_plus1 = PTR_ONE;
// RAM
reg wren_q1;
reg [AWID-1:0] wr_ptr_q1;
reg [WIDTH-1:0] wr_data_q1;
reg take_new;
reg [WIDTH-1:0] rd_data_mem = {WIDTH {1'b0}};
reg [WIDTH-1:0] rd_data_new;
// round up to the next power of 2 for depths which are not a power of 2
(* ram_style = RAM_STYLE *)
reg [WIDTH-1:0] mem [(1 << AWID)-1:0];
// write pointer
always @(posedge clk)
if(sreset == 1'b1)
wr_ptr <= PTR_ZERO;
else if(wr_en == 1'b1 && full == 1'b0)
wr_ptr <= wr_ptr + 1;
// read pointer
always @(posedge clk)
if(sreset == 1'b1) begin
rd_ptr <= PTR_ZERO;
rd_ptr_plus1 <= PTR_ONE;
end
else if(rd_en == 1'b1 && empty == 1'b0) begin
rd_ptr <= rd_ptr_plus1;
rd_ptr_plus1 <= rd_ptr_plus1 + PTR_ONE;
end
// full
always @(posedge clk) begin
if(sreset == 1'b1)
full <= 1'b0;
// assert when level is 1 less than
// the depth and a write occurs and
// no read occurs
else if(wr_en == 1'b1 && rd_en == 1'b0 && level == (DEPTH-1))
full <= 1'b1;
// de-assert when a read occurs since a write
// is not honored when full == 1
else if(rd_en == 1'b1)
full <= 1'b0;
end
// alm_full, asserted when level greater than 3/4 Depth
always @(posedge clk) begin
if(sreset == 1'b1)
alm_full <= 1'b0;
else if(level >= (3*DEPTH>>2))
alm_full <= 1'b1;
else
alm_full <= 1'b0;
end
// empty
always @(posedge clk)
if(sreset == 1'b1)
empty <= 1'b1;
// assert when the level is 1 and a read occurs, but no write occurs
else if(rd_en == 1'b1 && wr_en == 1'b0 && level == LEVEL_ONE)
empty <= 1'b1;
// deassert when a write occurs since a read is not honored when empty = 1
else if(wr_en == 1'b1)
empty <= 1'b0;
// level
always @(posedge clk)
if(sreset == 1'b1)
level <= LEVEL_ZERO;
else begin
case({(wr_en & ~full),(rd_en & ~empty)})
2'b00: level <= level;
2'b01: level <= level - LEVEL_ONE;
2'b10: level <= level + LEVEL_ONE;
2'b11: level <= level;
endcase
end
// overflow
// the write fails when the FIFO is in reset
always @(posedge clk)
if(sreset == 1'b1)
overflow <= wr_en;
else
overflow <= wr_en & full;
// underflow
// the read fails when the FIFO is in reset
always @(posedge clk)
if(sreset == 1'b1)
underflow <= rd_en;
else
underflow <= rd_en & empty;
//---------------------------------- RAM -----------------------------------
// for XST to synthesize to a BRAM, the write address must be latched
always @(posedge clk)
begin
wren_q1 <= wr_en & ~full;
wr_data_q1 <= wr_data;
wr_ptr_q1 <= wr_ptr;
end
always @(posedge clk)
if(wren_q1 == 1'b1)
mem[wr_ptr_q1] <= wr_data_q1;
// Since the write is delayed by 1 clock,reading a memory cell that
// was written on the previous cycle will not return the new data.
// To prevent this, return the data that was just written instead of
// the memory cell contents.
// check for read after write on same memory address
always@(posedge clk) begin
// if the address of the desired memory cell (rd_ptr_plus1) is a pending write, forward the data that is still in the pipe to the output
if(rd_en == 1'b1 && empty == 1'b0) begin
if(rd_ptr_plus1 == wr_ptr || rd_ptr_plus1 == wr_ptr_q1) begin
take_new <= 1'b1;
end
else
take_new <= 1'b0;
if(rd_ptr_plus1 == wr_ptr)
rd_data_new <= wr_data;
else if(rd_ptr_plus1 == wr_ptr_q1)
rd_data_new <= wr_data_q1;
end
// if the address of the desired memory cell (rd_ptr) is a pending write, forward the data that is still in the pipe to the output
else begin
// rd_ptr == wr_ptr
if(empty == 1'b1 || (rd_ptr == wr_ptr_q1 && wren_q1 == 1'b1))
take_new <= 1'b1;
else
take_new <= 1'b0;
if(rd_ptr == wr_ptr)
rd_data_new <= wr_data;
else if(rd_ptr == wr_ptr_q1)
rd_data_new <= wr_data_q1;
end
end
always @(posedge clk)
if(rd_en == 1'b1 && empty == 1'b0)
rd_data_mem <= mem[rd_ptr_plus1];
else
rd_data_mem <= mem[rd_ptr];
assign rd_data = take_new ? rd_data_new : rd_data_mem;
endmodule
上面是同步FWFT-FIFO的verilog手写代码,这种FIFO的一般用于低延时场景,优点是读使能与读数据是同步输出的,没有时延。
module fifo_async_fwft
#(
parameter WIDTH = 8,
parameter DEPTH = 1024
)
(
// use 'old style' ports since
// wr_level and rd_level need a calculated width
reset,
wr_clk,
wr_en,
wr_data,
wr_full,
wr_overflow,
wr_level,
rd_clk,
rd_en,
rd_data,
rd_empty,
rd_underflow,
rd_level
);
// `include "sv_clog2.vh"
localparam AWID = $clog2(DEPTH);
// variable length constants
localparam [AWID-1:0] ZERO = {AWID {1'b0}};
localparam [AWID-1:0] ONE = {{AWID-1 {1'b0}}, 1'b1};
function [AWID-1:0] bin2gray;
input [AWID-1:0] bin;
begin
bin2gray = (bin >> 1) ^ bin;
end
endfunction
function [AWID-1:0] gray2bin;
input [AWID-1:0] gray;
integer i;
begin
gray2bin = gray;
for(i = 1; i < AWID; i = 2 * i)
gray2bin = gray2bin ^ (gray2bin >> i);
end
endfunction
// PORTS
// asynchronous reset
input reset;
// wr_clk domain ports
input wr_clk;
input wr_en;
input [WIDTH-1:0] wr_data;
output reg wr_full = 1'b0;
output reg wr_overflow = 1'b0;
output reg [AWID-1:0] wr_level = ZERO;
// rd_clk domain ports
input rd_clk;
input rd_en;
output reg [WIDTH-1:0] rd_data;
output reg rd_empty = 1'b1;
output reg rd_underflow = 1'b0;
output reg [AWID-1:0] rd_level = ZERO;
genvar k;
reg rd_reset_ms;
reg rd_reset;
reg wr_reset_ms;
reg wr_reset;
// pointers
reg [AWID-1:0] wr_ptr_bin = ZERO;
reg [AWID-1:0] wr_ptr_bin_q1 = ZERO;
reg [AWID-1:0] wr_ptr_gray = ZERO;
reg [AWID-1:0] wr_ptr_gray_ms = ZERO;
reg [AWID-1:0] wr_ptr_gray_rd = ZERO;
reg [AWID-1:0] wr_ptr_bin_rd = ZERO;
reg [AWID-1:0] rd_ptr_bin = ZERO;
reg [AWID-1:0] rd_ptr_gray = ZERO;
reg [AWID-1:0] rd_ptr_gray_ms = ZERO;
reg [AWID-1:0] rd_ptr_gray_wr = ZERO;
reg [AWID-1:0] rd_ptr_bin_wr = ZERO;
// RAM
reg wren_q1;
reg [AWID-1:0] wr_ptr_gray_q1 = ZERO;
reg [AWID-1:0] wr_ptr_gray_q2 = ZERO;
reg [WIDTH-1:0] wr_data_q1;
// round up to the next power of 2 for depths which are not a power of 2
reg [WIDTH-1:0] mem [(1 << AWID)-1:0];
// Initialize memory array for simulation
integer i;
initial begin
for (i=0; i<(1 << AWID); i=i+1)
mem[i] = {WIDTH{1'b1}};
end
//--------------------------- synchronize resets ---------------------------
// sync reset to wr_clk domain
(* HBLKNM="wr_reset_sync_ffs" *)
always @(posedge wr_clk or posedge reset)
if(reset == 1'b1) begin
wr_reset_ms <= 1'b1;
wr_reset <= 1'b1;
end
else begin
wr_reset_ms <= 1'b0;
wr_reset <= wr_reset_ms;
end
// sync reset to rd_clk domain
(* HBLKNM="rd_reset_sync_ffs" *)
always @(posedge rd_clk or posedge reset)
if(reset == 1'b1) begin
rd_reset_ms <= 1'b1;
rd_reset <= 1'b1;
end
else begin
rd_reset_ms <= 1'b0;
rd_reset <= rd_reset_ms;
end
//----------------------------- write signals ------------------------------
// write pointers - binary & gray
always @(posedge wr_clk)
if(wr_reset == 1'b1) begin
wr_ptr_bin <= ZERO;
wr_ptr_gray <= ZERO;
end
else if(wr_en == 1'b1 && wr_full == 1'b0) begin
wr_ptr_bin <= wr_ptr_bin + ONE;
wr_ptr_gray <= bin2gray(wr_ptr_bin + ONE);
end
// synchronize rd_ptr_gray to wr_clk domain
generate
for(k = 0; k < AWID; k = k + 1) begin: rd_ptr_sync
// place each sync FF pair in the same component
// for max settling time
(* HBLKNM="rd_ptr_sync_ffs" *)
always @(posedge wr_clk) begin
rd_ptr_gray_ms[k] <= rd_ptr_gray[k];
rd_ptr_gray_wr[k] <= rd_ptr_gray_ms[k];
end
end
endgenerate
always @(posedge wr_clk)
rd_ptr_bin_wr <= gray2bin(rd_ptr_gray_wr);
// wr_level
always @(posedge wr_clk)
if(wr_reset == 1'b1)
wr_level <= ZERO;
else
wr_level <= wr_ptr_bin - rd_ptr_bin_wr + (wr_en & ~wr_full);
// wr_full
always @(posedge wr_clk)
if(wr_reset == 1'b1)
wr_full <= 1'b1;
else
wr_full <= &(wr_ptr_bin - rd_ptr_bin_wr + (wr_en & ~wr_full));
// wr_overflow
// the write fails when the FIFO is in reset
always @(posedge wr_clk)
if(wr_reset == 1'b1)
wr_overflow <= wr_en;
else
wr_overflow <= wr_en & wr_full;
//------------------------------ read signals ------------------------------
// read pointer - binary & gray
always @(posedge rd_clk)
if(rd_reset == 1'b1) begin
rd_ptr_bin <= ZERO;
rd_ptr_gray <= ZERO;
end
else if(rd_en == 1'b1 && rd_empty == 1'b0) begin
rd_ptr_bin <= rd_ptr_bin + ONE;
rd_ptr_gray <= bin2gray(rd_ptr_bin + ONE);
end
// synchronize wr_ptr_gray_q2 to rd_clk domain
generate
for(k = 0; k < AWID; k = k + 1) begin: wr_ptr_sync
// place each sync FF pair in the same component for max settling time
(* HBLKNM="wr_ptr_sync_ffs" *)
always @(posedge rd_clk) begin
wr_ptr_gray_ms[k] <= wr_ptr_gray_q2[k];
wr_ptr_gray_rd[k] <= wr_ptr_gray_ms[k];
end
end
endgenerate
always @(posedge rd_clk)
wr_ptr_bin_rd <= gray2bin(wr_ptr_gray_rd);
// rd_level
always @(posedge rd_clk)
if(rd_reset == 1'b1)
rd_level <= ZERO;
else
rd_level <= wr_ptr_bin_rd - rd_ptr_bin - (rd_en & ~rd_empty);
// rd_empty
always @(posedge rd_clk)
if(rd_reset == 1'b1)
rd_empty <= 1'b1;
else
rd_empty <= (wr_ptr_bin_rd - rd_ptr_bin - (rd_en & ~rd_empty)) == ZERO;
// rd_underflow the read fails when the FIFO is in reset
always @(posedge rd_clk)
if(rd_reset == 1'b1)
rd_underflow <= rd_en;
else
rd_underflow <= rd_en & rd_empty;
//---------------------------------- RAM -----------------------------------
// for XST to synthesize to a BRAM, the write address must be latched
always @(posedge wr_clk) begin
wren_q1 <= wr_en & ~wr_full;
wr_data_q1 <= wr_data;
wr_ptr_bin_q1 <= wr_ptr_bin;
wr_ptr_gray_q1 <= wr_ptr_gray;
wr_ptr_gray_q2 <= wr_ptr_gray_q1;
end
always @(posedge wr_clk)
if(wren_q1 == 1'b1)
mem[wr_ptr_bin_q1] <= wr_data_q1;
always @(posedge rd_clk)
if(rd_en == 1'b1 && rd_empty == 1'b0)
rd_data <= mem[rd_ptr_bin + ONE];
else
rd_data <= mem[rd_ptr_bin];
endmodule
上面是异步FWFT-FIFO的verilog代码,这种FIFO的一般用于低延时场景,优点是读使能与读数据是同步输出的,没有时延。
以上的两份代码均非原创,原创大佬另有其人。代码片不保证无BUG,只是在这里分享给各位借鉴与使用。