FIFO
一、同步FIFO设计
二、异步FIFO设计
异步FIFO主要用于跨时钟域的多bit数据传输,常常与RAM结合使用,本文的例子中,结合双端口RAM实现了深度为16的8bit数据传输,难点在于各个信号之间的逻辑与模块划分。FIFO产生读写有效使能信号,重点在于空满信号的判断与地址的有效增减。
写FIFO | 读FIFO | 双口RAM | ||||||
信号 | 定义 | 信号 | 定义 | 信号 | 定义 | |||
Input | wr_clk | 写时钟 | Input | rd_clk | 读时钟 | Input | wr_clk | 写时钟 |
wr_rstn | 写复位 | rd_rstn | 读复位 | rd_clk | 读时钟 | |||
wr_en | 写使能 | rd_en | 读使能 | ram_wen | 写使能 | |||
rd_wr_gray | 读地址格雷码 | wr_rd_gray | 写地址格雷码 | ram_ren | 读使能 | |||
Output | wr_addr | 写地址 | Output | rd_addr | 读地址 | wr_addr | 写地址 | |
wr_full | 写满信号 | rd_empty | 读空信号 | rd_addr | 读地址 | |||
ram_wen | RAM写使能 | ram_ren | RAM读使能 | wr_data_in [DATA_WIDTH-1 : 0] | 输入数据 | |||
wr_rd_gray | 写地址格雷码 | rd_wr_gray | 读地址格雷码 | Output | rd_data_out [DATA_WIDTH-1 : 0] | 输出数据 |
异步FIFO的空满判断有很多方法,但其中不变的一个点是,利用格雷码减少亚稳态发生的概率,在这个基础上可以得到,将二进制地址转换成格雷码后,满信号基于写时钟域来判断,此时同步过来的读地址信号有一定的延迟,即可能已经读出来三个数据了,但是指针显示只读了一个数据,而写地址指针无延时,造成实际上还没满,但是满信号有效的现象,假满只是导致部分资源的浪费,不会造成逻辑错误,是可以接收的方式;同时,空的判断也是如此,基于读时钟域进行判断,将同步过来的写地址格雷码进行比较,同样会出现假满,但不影响逻辑功能。
判断空和满时直接根据转成格雷码后的地址指针进行比较,可以发现格雷码与二进制码相比优点是相邻位变换bit为1,且格雷码是镜像对称的,判断为空时即两个格雷码值相等,判断为满时,最高位的格雷码值相反,后位相等。还有的方法有讲格雷码再转成二进制码后再进行比较来得到空满信号,另外,空满信号的判断方法还可以是将格雷码转换为二进制后,设置full_limit和empty_limit,我用的方法中limit的值相当于0。-->(verilog)一步步带你手写异步FIFO - 知乎
二进制码 | 格雷码 |
000 | 000 |
001 | 001 |
010 | 011 |
011 | 010 |
100 | 110 |
101 | 111 |
110 | 101 |
111 | 100 |
Debug总结
-
- 信号理解不到位,对各个模块之间相同信号的输入输出关系理解有误,如address信号相较于FIFO读写模块来讲是输出,对RAM来讲是输入信号;
- FIFO只是产生地址有效增减和空满判断,RAM只要根据地址指针读取对应数据;
- FIFO深度的计算、FIFO与RAM的区别,异步FIFO对于跨时钟域的贡献;
写FIFO代码
module fifo_wr #(
parameter DATA_WIDTH = 'd8, // FIFO DATA WIDTH
parameter DATA_DEEPTH = 'd16 // FIFO DATA DEEPTH
)(
input wr_rstn,
input wr_clk,
input wr_en,
input [$clog2(DATA_DEEPTH):0] rd_gray,
output[$clog2(DATA_DEEPTH):0] wr_rd_gray,
output wr_full,
output ram_wen,
output[$clog2(DATA_DEEPTH)-1:0] wr_addr
);
reg [$clog2(DATA_DEEPTH):0] wr_ptr;
reg [$clog2(DATA_DEEPTH):0] wr_gray;
reg [$clog2(DATA_DEEPTH):0] rd_gray1;
reg [$clog2(DATA_DEEPTH):0] rd_gray2;
// read's gray signal CDC
always @(posedge wr_clk or negedge wr_rstn) begin
if(~wr_rstn) begin
rd_gray1 <= 0;
rd_gray2 <= 0;
end else begin
rd_gray1 <= rd_gray;
rd_gray2 <= rd_gray1;
end
end
//binary to gray
always @(posedge wr_clk or negedge wr_rstn) begin
if(~wr_rstn) begin
wr_gray <= 0;
end else begin
wr_gray <= wr_ptr ^ (wr_ptr >> 1);
end
end
// generate wr_rd_gray signal
assign wr_rd_gray = wr_gray;
// generate full signal
assign wr_full = {~rd_gray2[$clog2(DATA_DEEPTH)],~rd_gray2[$clog2(DATA_DEEPTH)-1],rd_gray2[$clog2(DATA_DEEPTH)-2:0]} == wr_gray ? 1 : 0 ;
// generate ram_in signal
assign ram_wen = wr_en && (~wr_full);
// refresh write address
always @(posedge wr_clk or negedge wr_rstn) begin
if(~wr_rstn) begin
wr_ptr <= 0;
end else if(ram_wen)begin
wr_ptr <= wr_ptr + 'd1;
end
end
assign wr_addr = wr_ptr[$clog2(DATA_DEEPTH)-1:0];
endmodule : fifo_wr
读FIFO
module fifo_rd #(
parameter DATA_WIDTH = 'd8, // FIFO DATA WIDTH
parameter DATA_DEEPTH = 'd16 // FIFO DATA DEEPTH
)(
input rd_rstn,
input rd_clk,
input rd_en,
input [$clog2(DATA_DEEPTH):0] wr_gray,
output[$clog2(DATA_DEEPTH):0] rd_wr_gray,
output rd_empty,
output ram_ren,
output[$clog2(DATA_DEEPTH)-1:0] rd_addr
);
reg [$clog2(DATA_DEEPTH):0] rd_ptr;
reg [$clog2(DATA_DEEPTH):0] rd_gray;
reg [$clog2(DATA_DEEPTH):0] wr_gray1;
reg [$clog2(DATA_DEEPTH):0] wr_gray2;
// reg [DATA_WIDTH-1:0] ram_buffer_reg[$clog2(DATA_DEEPTH)-1:0];
// read's gray signal CDC
always @(posedge rd_clk or negedge rd_rstn) begin
if(~rd_rstn) begin
wr_gray1 <= 0;
wr_gray2 <= 0;
end else begin
wr_gray1 <= wr_gray;
wr_gray2 <= wr_gray1;
end
end
//binary to gray
always @(posedge rd_clk or negedge rd_rstn) begin
if(~rd_rstn) begin
rd_gray <= 0;
end else begin
rd_gray <= rd_ptr ^ (rd_ptr >> 1);
end
end
assign rd_wr_gray = rd_gray;
// generate wr_rd_gray signal
// assign wr_rd_gray = wr_gray;
// generate empty signal
assign rd_empty = (wr_gray2 == rd_gray) ? 1 : 0;
// generate ram_ren signal
assign ram_ren = rd_en && (~rd_empty);
// refresh read address
always @(posedge rd_clk or negedge rd_rstn) begin
if(~rd_rstn) begin
rd_ptr <= 0;
end
else if(ram_ren)begin
rd_ptr <= rd_ptr + 'd1;
end
end
assign rd_addr = rd_ptr[$clog2(DATA_DEEPTH)-1:0];
endmodule : fifo_rd
顶层模块
module afifo_top#(
parameter DATA_WIDTH = 'd8, // FIFO DATA WIDTH
parameter DATA_DEEPTH = 'd16 // FIFO DATA DEEPTH
)(
input wr_rstn, //
input wr_clk, //
input wr_en, //
input [DATA_WIDTH-1:0] wr_data_in, //1 //
output[$clog2(DATA_DEEPTH):0] wr_rd_gray,
output wr_full,
output ram_wen,
output[$clog2(DATA_DEEPTH)-1:0] wr_addr,
input rd_rstn, //
input rd_clk, //
input rd_en, //
output[$clog2(DATA_DEEPTH):0] rd_wr_gray,
output rd_empty,
output ram_ren,
output[DATA_WIDTH-1:0] rd_data_out, //2
output[$clog2(DATA_DEEPTH)-1:0] rd_addr
);
reg [DATA_WIDTH-1:0] ram_buffer_reg[DATA_DEEPTH-1:0];
// reg [DATA_WIDTH-1:0] ram_buffer_reg2[$clog2(DATA_DEEPTH)-1:0];
integer i;
reg [DATA_WIDTH-1:0] rd_data_out_reg;
always @(posedge wr_clk or negedge wr_rstn) begin
if(~wr_rstn) begin
for(i = 0 ; i < DATA_DEEPTH ; i = i + 1) begin
ram_buffer_reg[i] <= 0;
end
end else if(ram_wen)begin
ram_buffer_reg[wr_addr] <= wr_data_in;
end
end
always @(posedge rd_clk or negedge rd_rstn) begin
if(~rd_rstn) begin
rd_data_out_reg <= 0;
end else if(ram_ren) begin
rd_data_out_reg <= ram_buffer_reg[rd_addr];
end
end
assign rd_data_out = rd_data_out_reg;
//example the write module
fifo_wr #(
.DATA_WIDTH (DATA_WIDTH ), // FIFO DATA WIDTH
.DATA_DEEPTH(DATA_DEEPTH) // FIFO DATA DEEPTH
)
my_fifo_wr(
.wr_rstn (wr_rstn ),
.wr_clk (wr_clk ),
.wr_en (wr_en ),
.wr_addr (wr_addr ),
.rd_gray (rd_wr_gray ),
.wr_rd_gray (wr_rd_gray ),
.wr_full (wr_full ),
.ram_wen (ram_wen )
);
//example the read module
fifo_rd#(
.DATA_WIDTH (DATA_WIDTH ), // FIFO DATA WIDTH
.DATA_DEEPTH(DATA_DEEPTH) // FIFO DATA DEEPTH
)
my_fifo_rd(
.rd_rstn (rd_rstn ),
.rd_clk (rd_clk ),
.rd_en (rd_en ),
.rd_addr (rd_addr ),
.wr_gray (wr_rd_gray ),
.rd_wr_gray (rd_wr_gray ),
.rd_empty (rd_empty ),
.ram_ren (ram_ren )
);
//example the RAM module
/* ram #(
.DATA_WIDTH (DATA_WIDTH ), // FIFO DATA WIDTH
.DATA_DEEPTH(DATA_DEEPTH) // FIFO DATA DEEPTH
)
my_ram(
.ram_wclk(wr_clk),
.ram_rclk(rd_clk),
.ram_wen(ram_wen),
.ram_ren(ram_ren),
.wr_data(wr_data_in),
.wr_addr(wr_addr),
.rd_addr(rd_addr),
.rd_data(rd_data_out)
);
*/
endmodule : afifo_top
仿真代码
`timescale 1ns/1ns
module afifo_top_tb #(
parameter DATA_WIDTH = 'd8, // FIFO DATA WIDTH
parameter DATA_DEEPTH = 'd16 // FIFO DATA DEEPTH
)();
reg wr_rstn; //
reg wr_clk; //
reg wr_en; //
reg [DATA_WIDTH-1:0] wr_data_in; //1
wire[$clog2(DATA_DEEPTH)-1:0] wr_addr; //
wire[$clog2(DATA_DEEPTH):0] wr_rd_gray;
wire wr_full;
wire ram_wen;
reg rd_rstn; //
reg rd_clk; //
reg rd_en; //
wire[$clog2(DATA_DEEPTH)-1:0] rd_addr;
wire[$clog2(DATA_DEEPTH):0] rd_wr_gray;
wire rd_empty;
wire ram_ren;
wire[DATA_WIDTH-1:0] rd_data_out; //2
always #5 wr_clk = ~wr_clk;
always #15 rd_clk = ~rd_clk;
initial begin
#10 rd_rstn = 0;
wr_rstn = 0;
wr_clk = 0;
rd_clk = 0;
wr_en = 0;
rd_en = 0;
#20 rd_rstn = 1;
wr_rstn = 1;
#5 wr_data_in = 8'b1000_0000;
wr_en = 1;
#10 wr_data_in = 8'b1000_0001;
#10 wr_data_in = 8'b1000_0010;
#10 wr_data_in = 8'b1000_0011;
#10 wr_data_in = 8'b1000_0100;
#10 wr_data_in = 8'b1000_0101;
#10 wr_data_in = 8'b1000_0110;
#10 wr_data_in = 8'b1000_0111;
#10 wr_data_in = 8'b1000_1000;
#10 wr_data_in = 8'b1000_1001;
#10 wr_data_in = 8'b1000_1010;
#10 wr_data_in = 8'b1000_1011;
#10 wr_data_in = 8'b1000_1100;
#10 wr_data_in = 8'b1000_1101;
#10 wr_data_in = 8'b1000_1110;
#10 wr_data_in = 8'b1000_1111;
#10 wr_en = 0;
#10 wr_data_in = 8'b1111_1111;
#10 rd_en = 1;
#480 rd_en = 0;
end
// example the top module
afifo_top#(
.DATA_WIDTH (DATA_WIDTH ), // FIFO DATA WIDTH
.DATA_DEEPTH(DATA_DEEPTH) // FIFO DATA DEEPTH
)
my_afifo_top(
.wr_rstn (wr_rstn ),
.wr_clk (wr_clk ),
.wr_en (wr_en ),
.wr_addr (wr_addr ),
.wr_data_in (wr_data_in ),
.wr_rd_gray (wr_rd_gray ),
.wr_full (wr_full ),
.ram_wen (ram_wen ),
.rd_rstn (rd_rstn ),
.rd_clk (rd_clk ),
.rd_en (rd_en ),
.rd_addr (rd_addr ),
.rd_wr_gray (rd_wr_gray ),
.rd_empty (rd_empty ),
.ram_ren (ram_ren ),
.rd_data_out(rd_data_out)
);
endmodule : afifo_top_tb
仿真波形图