1 概述
本小节主要讲解 Verilog 语法的 FIFO 设计,需要掌握 FIFO 的基本原理,掌握同步 FIFO 和异步 FIFO 的结构。
2 同步 FIFO
FIFO 表示先入先出,它是一种存储器结构。同步 FIFO 是使用单一时钟同时进行读取和写入操作的,数据流和相关的控制逻辑在同一个时钟域内处理。同步 FIFO 的接口设计如下:
同步 FIFO 具体的设计如下:
module synch_fifo#(
parameter FIFO_AFULL_SIZE = 1,
parameter FIFO_AEMPTY_SIZE = 1,
parameter FIFO_ADDR_WIDTH = 4,
parameter FIFO_WIDTH = 8,
parameter FIFO_DEPTH = 2**FIFO_ADDR_WIDTH
)
(
fifo_clk,
fifo_rst_n,
fifo_wren,
fifo_wrdata,
fifo_rden,
fifo_rddata,
fifo_full,
fifo_empty,
fifo_afull,
fifo_aempty,
fifo_room_avail,
fifo_data_avail
);
input wire fifo_clk;
input wire fifo_rst_n;
input wire fifo_wren;
input wire [FIFO_WIDTH-1:0]fifo_wrdata;
input wire fifo_rden;
output wire [FIFO_WIDTH-1:0fifo_rddata;
output reg fifo_full;
output reg fifo_empty;
output wire fifo_afull;
output wire fifo_aempty;
output reg [FIFO_ADDR_WIDTH:0fifo_room_avail;
output wire [FIFO_ADDR_WIDTH:0fifo_data_avail;
localparam FIFO_DEPTH_MINUS1 = FIFO_DEPTH - 1;
//****************REG**************************
reg [FIFO_ADDR_WIDTH-1:0] wr_ptr,wr_ptr_nxt;
reg [FIFO_ADDR_WIDTH-1:0] rd_ptr,rd_ptr_nxt;
reg [FIFO_ADDR_WIDTH:0] num_entries,num_entries_nxt;
wire fifo_full_nxt;
wire fifo_empty_nxt;
wire [FIFO_ADDR_WIDTH:0] fifo_room_avail_nxt;
//write pointer control logic
//*********************************************
always@(*)
begin
wr_ptr_nxt = wr_ptr;
if(fifo_wren)
begin
if(wr_ptr == FIFO_DEPTH_MINUS1)
wr_ptr_nxt = 'd0;
else
wr_ptr_nxt = wr_ptr + 1'b1;
end
end
//read pointer control logic
//*********************************************
always@(*)
begin
rd_ptr_nxt = rd_ptr;
if(fifo_rden)
begin
if(rd_ptr == FIFO_DEPTH_MINUS1)
rd_ptr_nxt = 'd0;
else
rd_ptr_nxt = rd_ptr + 1'b1;
end
end
//calculate number of occupied entries in the fifo
//*********************************************
always@(*)
begin
num_entries_nxt = num_entries;
if (fifo_wren && fifo_rden)
begin
num_entries_nxt = num_entries;
end
else if (fifo_wren)
begin
num_entries_nxt = num_entries + 1'b1;
end
else if (fifo_rden)
begin
num_entries_nxt = num_entries - 1'b1;
end
end
assign fifo_full_nxt = (num_entries_nxt == FIFO_DEPTH);
assign fifo_empty_nxt = (num_entries_nxt == 'd0);
assign fifo_data_avail = num_entries;
assign fifo_room_avail_nxt = FIFO_DEPTH - num_entries_nxt;
assign fifo_afull = (fifo_room_avail_nxt <= FIFO_AFULL_SIZE)? 1:0;
assign fifo_aempty = (fifo_data_avail <= FIFO_AEMPTY_SIZE ) ? 1:0;
//the fifo output
//*********************************************
always@(posedge fifo_clk or negedge fifo_rst_n)
begin
if (!fifo_rst_n)
begin
wr_ptr <= 'd0;
rd_ptr <= 'd0;
num_entries <= 'd0;
fifo_full <= 1'b0;
fifo_empty <= 1'b1;
fifo_room_avail <= FIFO_DEPTH;
end
else
begin
wr_ptr <= wr_ptr_nxt;
rd_ptr <= rd_ptr_nxt;
num_entries <= num_entries_nxt;
fifo_full <= fifo_full_nxt;
fifo_empty <= fifo_empty_nxt;
fifo_room_avail <= fifo_room_avail_nxt;
end
end
//the ram instantiation sdp
//*********************************************
sdp_ram_sync #(
.DATA_W(FIFO_WIDTH),
.ADDR_WIDTH(FIFO_ADDR_WIDTH),
.REG_OUT(0)
)
u_ram
(
.data_in(fifo_wrdata),
.wraddress(wr_ptr),
.wren(fifo_wren),
.clk(fifo_clk),
.data_out(fifo_rddata),
.rdaddress
(rd_ptr),
.rden(fifo_rden),
.rst_n(fifo_rst_n)
);
endmodule
module sdp_ram_sync#(
parameter DATA_W = 1,
parameter ADDR_WIDTH= 9,
parameter REG_OUT = 1,
parameter U_DLY = 1
)
(
input [1-1:0]clk,
input [1-1:0]rst_n,
input [1-1:0]wren,
input [ADDR_WIDTH-1:0]wraddress,
input [DATA_W-1:0]data_in,
input [1-1:0]rden,
input [ADDR_WIDTH-1:0]rdaddress,
output [DATA_W-1:0]data_out);
localparam ADDR_NUM = 2**ADDR_WIDTH;
reg [DATA_W-1:0] mem [ADDR_NUM-1:0];
reg [DATA_W-1:0] q_tmp;
reg [DATA_W-1:0] q_tmp_1d ;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
mem[wraddress] <= #U_DLY 'd0;
else if(wren==1'b1)
begin
mem[wraddress] <= #U_DLY data_in;
end
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
q_tmp <= #U_DLY 'd0;
else if(rden==1'b1)
begin
q_tmp <= #U_DLY mem[rdaddress];
end
end
always@(posedge clk )
begin
q_tmp_1d <= #U_DLY q_tmp;
end
assign data_out =(REG_OUT == 1) ? q_tmp_1d : q_tmp ;
endmodule
| 仿真结果如图所示:
3 异步 FIFO
在实际的应用中常常需要多个时钟域的数据传递,需要使用异步 FIFO 将数据从一个时钟域传递到另一个时钟域。
从原理上来讲,同步 FIFO 和异步 FIFO 类似,但是由于异步 FIFO 与两个时钟相关,电路复杂度变高。对于异步 FIFO 的写入操作和读出操作的方式与同步 FIFO 类似,写入和读出操作都有自己的信号集,其复杂度在于产生FIFO 的状态信号。
通过比较写入指针和读取时钟的值,可以产生空,满等状态信号,指针的跨时钟传递经过了格雷码和二进制码的转换以及打两拍进行跨时钟的处理。如下图所示:
写入指针首先被转换为格雷码,并被寄存到写入时钟域的触发器中。然后,再经过读取时钟域同步,打两拍,后面进行格雷码到二进制的转换电路,进行寄存。此时写入指针已被传送到读取时钟域,用于和读取时钟域的读指针进行比较,求得空状态信号。
当读取指针被传送到写入时钟域时,相对于读取时钟域中的读取指针将会有 3-4 个的时钟周期延迟。这意味着可用于写入数据的位置要比显示的可能要多三到四个。这是异步 FIFO 操作保守的一面,如此才不会有数据上溢出。当 FIFO 读取数据时,读指针数据在三到四个周期内将会跟上实际的读指针值。另外 FIFO 具有一定的深度,其中有足够多的数据被读取,因此不会影响数据传送的性能。例:
module asynch_fifo#(
parameter FIFO_AFULL_SIZE = 1,
parameter FIFO_AEMPTY_SIZE = 1,
parameter FIFO_PTR = 4,
parameter FIFO_WIDTH = 8
// parameter FIFO_DEPTH = 16
)
(
fifo_wrclk,
fifo_wr_rst_n ,
fifo_wren,
fifo_wrdata,
reset_wrptr,
fifo_rdclk,
fifo_rd_rst_n ,
fifo_rden,
fifo_rddata,
reset_rdptr,
fifo_full,
fifo_empty,
fifo_afull,
fifo_aempty,
fifo_room_avail,
fifo_data_avail
);
input wire fifo_wrclk;
input wire fifo_wr_rst_n ;
input wire fifo_wren;
input wire [FIFO_WIDTH-1:0fifo_wrdata;
input wire reset_wrptr;
input wire fifo_rdclk;
input wirefifo_rd_rst_n;
input wire fifo_rden;
output wire [FIFO_WIDTH-1:0fifo_rddata;
input wire reset_rdptr;
output reg fifo_full;
output reg fifo_empty;
output wire fifo_afull;
output wire fifo_aempty;
output reg [FIFO_PTR:0fifo_room_avail;
output reg [FIFO_PTR:0fifo_data_avail;
localparam FIFO_DEPTH = (1<<FIFO_PTR);
localparam FIFO_TWICEDEPTH_MINUS1 = 2*FIFO_DEPTH - 1;
//****************REG**************************
reg [FIFO_PTR:0]wr_ptr_wab,wr_ptr_wab_nxt;//extra wraparound bit
wire [FIFO_PTR:0]fifo_room_avail_nxt;
wire fifo_full_nxt;
wire [FIFO_PTR:0]wr_ptr;//write ptr without wraparound bit
reg [FIFO_PTR:0]rd_ptr_wab,rd_ptr_wab_nxt;//extra wraparound bit
wire[FIFO_PTR:0] fifo_data_avail_nxt;
wire fifo_empty_nxt;
wire [FIFO_PTR:0]rd_ptr;//read ptr without wraparound bit
reg [FIFO_PTR:0]wr_ptr_wab_gray;
wire [FIFO_PTR:0]wr_ptr_wab_gray_nxt;
reg [FIFO_PTR:0]wr_ptr_wab_gray_sync1;
reg [FIFO_PTR:0]wr_ptr_wab_gray_sync2;
reg [FIFO_PTR:0]wr_ptr_wab_rdclk;
wire [FIFO_PTR:0]wr_ptr_wab_rdclk_nxt;
reg [FIFO_PTR:0]rd_ptr_wab_gray;
wire [FIFO_PTR:0]rd_ptr_wab_gray_nxt;
reg [FIFO_PTR:0]rd_ptr_wab_gray_sync1;
reg [FIFO_PTR:0]rd_ptr_wab_gray_sync2;
reg [FIFO_PTR:0]rd_ptr_wab_wrclk;
wire [FIFO_PTR:0]rd_ptr_wab_wrclk_nxt;//write pointer control logic
//*********************************************
always@(*)
begin
wr_ptr_wab_nxt = wr_ptr_wab;
if(reset_wrptr)
begin
wr_ptr_wab_nxt = 'd0;
end
else if(fifo_wren&&(wr_ptr_wab == FIFO_TWICEDEPTH_MINUS1))
begin
wr_ptr_wab_nxt = 'd0;
end
else if(fifo_wren)
begin
wr_ptr_wab_nxt = wr_ptr_wab + 1'b1;
end
end
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
if(!fifo_wr_rst_n)
begin
wr_ptr_wab <= 'd0;
end
else
begin
wr_ptr_wab <= wr_ptr_wab_nxt;
end
end
//convert the binary wr_ptr to gray,flop it,and then pass it to read domain
//*********************************************
binary_to_gray#(.PTR(FIFO_PTR)) u_binary_to_gray_wr
(
.binary_value(wr_ptr_wab_nxt),
.gray_value(wr_ptr_wab_gray_nxt));
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
if(!fifo_wr_rst_n)
begin
wr_ptr_wab_gray <= 'd0;
end
else
begin
wr_ptr_wab_gray <= wr_ptr_wab_gray_nxt;
end
end
//synchronize wr_ptr_wab_gray into read clock domain
//*********************************************
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
if(!fifo_rd_rst_n)
begin
wr_ptr_wab_gray_sync1 <= 'd0;
wr_ptr_wab_gray_sync2 <= 'd0;
end
else
begin
wr_ptr_wab_gray_sync1 <= wr_ptr_wab_gray;
wr_ptr_wab_gray_sync2 <= wr_ptr_wab_gray_sync1;
end
end
//convert wr_ptr_wab_gray_sync2 back to binary form
//*********************************************
gray_to_binary#(.PTR(FIFO_PTR)) u_gray_to_binary_wr
(
.gray_value(wr_ptr_wab_gray_sync2 ),
.binary_value(wr_ptr_wab_rdclk_nxt)
);
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
if(!fifo_rd_rst_n)
begin
wr_ptr_wab_rdclk <= 'd0;
end
else
begin
wr_ptr_wab_rdclk <= wr_ptr_wab_rdclk_nxt;
end
end
//read pointer control logic
//*********************************************
always@(*)
begin
rd_ptr_wab_nxt = rd_ptr_wab;
if(reset_rdptr)
begin
rd_ptr_wab_nxt = 'd0;
end
else if(fifo_rden && (rd_ptr_wab == FIFO_TWICEDEPTH_MINUS1))
begin
rd_ptr_wab_nxt = 'd0;
end
else if(fifo_rden)
begin
rd_ptr_wab_nxt = rd_ptr_wab + 1'b1;
end
end
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
if(!fifo_rd_rst_n)
begin
rd_ptr_wab <= 'd0;
end
else
begin
rd_ptr_wab <= rd_ptr_wab_nxt;
end
end
//convert the binary rd_ptr to gray and then pass it to write clock domain
//*********************************************
binary_to_gray#(.PTR(FIFO_PTR)) u_binary_to_gray_rd
(
.binary_value(rd_ptr_wab_nxt),
.gray_value(rd_ptr_wab_gray_nxt)
);
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
if(!fifo_rd_rst_n)
begin
rd_ptr_wab_gray <= 'd0;
end
else
begin
rd_ptr_wab_gray <= rd_ptr_wab_gray_nxt;
end
end
//synchronize rd_ptr_wab_gray into write clock domain
//*********************************************
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
if(!fifo_wr_rst_n)
begin
rd_ptr_wab_gray_sync1 <= 'd0;
rd_ptr_wab_gray_sync2 <= 'd0;
end
else
begin
rd_ptr_wab_gray_sync1 <= rd_ptr_wab_gray;
rd_ptr_wab_gray_sync2 <= rd_ptr_wab_gray_sync1;
end
end
//convert rd_ptr_wab_gray_sync2 back to binary form
//*********************************************
gray_to_binary#(.PTR(FIFO_PTR)) u_gray_to_binary_rd
(
.gray_value(rd_ptr_wab_gray_sync2 ),
.binary_value(rd_ptr_wab_wrclk_nxt)
);
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
if(!fifo_rd_rst_n)
begin
rd_ptr_wab_wrclk <= 'd0;
end
else
begin
rd_ptr_wab_wrclk <= rd_ptr_wab_wrclk_nxt;
end
end
assign wr_ptr = wr_ptr_wab[FIFO_PTR-1:0];
assign rd_ptr = rd_ptr_wab[FIFO_PTR-1:0];
//the ram instantiation sdp
//*********************************************
sdp_ram #(
.DATA_W(FIFO_WIDTH),
.ADDR_WIDTH(FIFO_PTR),
.REG_OUT(0)
)
u_ram(
.data_in(fifo_wrdata),
.wraddress(wr_ptr),
.wren(fifo_wren),
.clk_w(fifo_wrclk),
.data_out(fifo_rddata),
.rdaddress(rd_ptr),
.rden(fifo_rden),
.clk_r(fifo_rdclk),
.rst_n_w(fifo_wr_rst_n ),
.rst_n_r(fifo_rd_rst_n )
);
//generate fifo_full:pointers equal,but the warp around bits are different
//*********************************************
assign fifo_full_nxt = (wr_ptr_wab_nxt[FIFO_PTR] != rd_ptr_wab_wrclk_nxt[FIFO_PTR]) && (wr_ptr_wab_nxt[FIFO_PTR-1:0] == rd_ptr_wab_wrclk_nxt[FIFO_PTR-1:0]);
assign fifo_room_avail_nxt = (wr_ptr_wab[FIFO_PTR] == rd_ptr_wab_wrclk[FIFO_PTR])? (FIFO_DEPTH - (wr_ptr_wab[FIFO_PTR-1:0] = rd_ptr_wab_wrclk[FIFO_PTR-1:0])): (rd_ptr_wab_wrclk[FIFO_PTR-1:0] -wr_ptr_wab[FIFO_PTR-1:0]);
//generate fifo_empty:pointers are equal including the warp around bits
//*********************************************
assign fifo_empty_nxt = (rd_ptr_wab_nxt[FIFO_PTR:0] == wr_ptr_wab_rdclk_nxt[FIFO_PTR:0]);
assign fifo_data_avail_nxt = (rd_ptr_wab[FIFO_PTR] == wr_ptr_wab_rdclk[FIFO_PTR])? (wr_ptr_wab_rdclk[FIFO_PTR-1:0] - rd_ptr_wab[FIFO_PTR-1:0]): (FIFO_DEPTH - (rd_ptr_wab[FIFO_PTR-1:0] - wr_ptr_wab_rdclk[FIFO_PTR-1:0]));
assign fifo_afull = (fifo_room_avail_nxt <= FIFO_AFULL_SIZE)? 1:0;
assign fifo_aempty = (fifo_data_avail_nxt <= FIFO_AEMPTY_SIZE ) ? 1:0;
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
if(!fifo_wr_rst_n)
begin
fifo_full <= 1'b0;
fifo_room_avail <= 'd0;
end
else
begin
fifo_full <= fifo_full_nxt;
fifo_room_avail <= fifo_room_avail_nxt;
end
end
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
if(!fifo_rd_rst_n)
begin
fifo_empty <= 1'b1;
fifo_data_avail <= 'd0;
end
else
begin
fifo_empty <= fifo_empty_nxt;
fifo_data_avail <= fifo_data_avail_nxt;
end
end
endmodule
module binary_to_gray#(
parameter PTR = 4//"1","0"
)
(
binary_value,
gray_value
);
input wire [PTR:0]binary_value ;
output wire [PTR:0gray_value;
generate
genvar i;
for( i = 0 ; i < PTR ; i = i + 1 )
begin
assign gray_value = binary_value ^ binary_value[ i + 1 ];
end
endgenerate
assign gray_value[PTR] = binary_value[PTR];
endmodule
module gray_to_binary#(
parameter PTR= 4//"1","0"
)(
binary_value,
gray_value
);
input wire [PTR:0]gray_value;
output wire [PTR:0binary_value ;
generate
genvar i;
for( i = 0 ; i < PTR ; i = i + 1 )
begin
assign binary_value = binary_value[i + 1] ^ gray_value;
end
endgenerate
assign binary_value[PTR] = gray_value[PTR];
endmodule
module sdp_ram#(
parameter DATA_W = 1,
parameter ADDR_WIDTH= 9,
parameter REG_OUT = 1,
parameter U_DLY = 1
)(
input [1-1:0]clk_w,
input[1-1:0]rst_n_w,
input [1-1:0]wren,
input [ADDR_WIDTH-1:0]wraddress,
input [DATA_W-1:0]data_in,
input [1-1:0]clk_r,
input [1-1:0]rst_n_r,
input [1-1:0]rden,
input [ADDR_WIDTH-1:0]rdaddress,
output [DATA_W-1:0]data_out
);
localparam ADDR_NUM = 2**ADDR_WIDTH;
reg [DATA_W-1:0] mem [ADDR_NUM-1:0];
reg [DATA_W-1:0] q_tmp;
reg [DATA_W-1:0] q_tmp_1d ;
always@(posedge clk_w or negedge rst_n_w)
begin
if(!rst_n_w)
mem[wraddress] <= #U_DLY 'd0;
else if(wren==1'b1)
begin
mem[wraddress] <= #U_DLY data_in;
end
end
always@(posedge clk_r or negedge rst_n_r)
begin
if(!rst_n_r)
q_tmp <= #U_DLY 'd0;
else if(rden==1'b1)
begin
q_tmp <= #U_DLY mem[rdaddress];
end
end
always@(posedge clk_r )
begin
q_tmp_1d <= #U_DLY q_tmp;
end
assign data_out =(REG_OUT == 1) ? q_tmp_1d : q_tmp ;
endmodule
| 仿真结果如图所示:
|