fifo具体理论可以参考https://blog.csdn.net/alangaixiaoxiao/article/details/81432144
主要点:
- 时钟之间通过打两拍进行同步,减小亚稳态的概率(不一定逻辑是正确的)
- 地址采用格雷码。因为二进制可能同时有多位进行变化,相对于每次只发生一位变化的格雷码易发生亚稳态。
异步FIFO结构图
知识延伸:
- 亚稳态:亚稳态是指触发器无法在 某个规定的时间段 内达到可以确认的状态。一旦触发器进入亚稳态,则既无法预测触发器的输出电平,也无法预测什么时候稳定在某个确认的电平上。此时的触发器输出端Q在较长时间内处于震荡状态且不等于输入端D,并且这种无用的输出电平可以沿信号通道上的各个触发器级联式传播下去。在同步系统中,输入信号总是与系统时钟同步,能够达到寄存器的时序要求,所以亚稳态不会发生。亚稳态通常发生在跨时钟域信号传输以及异步信号采集上。
- 格雷码和独热码: 二进制编码、格雷码编码使用最少的触发器,消耗较多的组合逻辑,而独热码编码反之。独热码编码的最大优势在于状态比较时仅仅需要比较一个位,从而一定程度上简化了译码逻辑。虽然在需要表示同样的状态数时,独热编码占用较多的位,也就是消耗较多的触发器,但这些额外触发器占用的面积可与译码电路省下来的面积相抵消。
FIFO代码如下:
RAM 模块
module Ram # (
parameter DATA_WIDTH = 4,
parameter ADDR_WIDTH = 8
)
(
input [ADDR_WIDTH-1:0] wr_addr,
input [ADDR_WIDTH-1:0] rd_addr,
input arst_n,
input wr_clk,
input rd_clk,
input [DATA_WIDTH-1:0] wr_data,
output reg [DATA_WIDTH-1:0] rd_data
);
localparam DEPTH = 1 << ADDR_WIDTH;
//建立memory
reg [DATA_WIDTH-1:0] Ram_mem [0:DEPTH-1];
integer i;
//初始化memory,并在写时钟读对应地址的数据
always@(posedge wr_clk or negedge arst_n) begin
if(!arst_n) begin
for(i = 0;i < DEPTH;i = i + 1)
Ram_mem[i] <= 8'b0;
end
else
Ram_mem[wr_addr] <= wr_data;
end
//在读时钟域读取数据(可以写成组合逻辑,看个人习惯)
always@(posedge rd_clk or negedge arst_n) begin
if(!arst_n) begin
rd_data <= 0;
end
else
rd_data <= Ram_mem[rd_addr];
end
endmodule
地址在异步时钟域同步模块
module syn_r2w #(
parameter ADDR_WIDTH = 8
)
(
input [ADDR_WIDTH:0] rptr,
input wclk,
input arst_n,
output reg [ADDR_WIDTH:0] ptr_r2w
);
reg [ADDR_WIDTH:0] pad_rptr;
//打两排实现地址在时钟域上的同步,降低亚稳态概率
always @(posedge wclk or negedge arst_n) begin
if (!arst_n) begin
{ptr_r2w,pad_rptr} <= 0;
end
else begin
{ptr_r2w,pad_rptr} <= {pad_rptr,rptr};
end
end
endmodule
module syn_w2r #(
parameter ADDR_WIDTH = 8
)
(
input [ADDR_WIDTH:0] wptr,
input rclk,
input arst_n,
output reg [ADDR_WIDTH:0] ptr_w2r
);
reg [ADDR_WIDTH:0] pad_wptr;
always @(posedge rclk or negedge arst_n) begin
if (!arst_n) begin
{ptr_w2r,pad_wptr} <= 0;
end
else begin
{ptr_w2r,pad_wptr} <= {pad_wptr,wptr};
end
end
endmodule
读时钟域判空模块
module rptr_empty #(
parameter ADDR_WIDTH = 8
)
(
output reg rempty,
output [ADDR_WIDTH-1:0] raddr,//写到RAM的二进制地址
output reg [ADDR_WIDTH:0] rptr,//输入到写时钟域的格雷码地址
input [ADDR_WIDTH:0] ptr_w2r,//同步到读时钟域的写格雷码地址
input rinc,//读使能
input rclk,
input arst_n
);
reg [ADDR_WIDTH:0] rbin;
wire [ADDR_WIDTH:0] rgaynext,rbinnext;
wire rempty_val;
//时序逻辑二进制和格雷码地址,负责初始化和时序逻辑
always @(posedge rclk or negedge arst_n) begin
if (!arst_n) begin
rbin <= 0;
rptr <= 0;
end
else begin
rbin <= rbinnext;
rptr <= rgaynext;
end
end
//根据使能信号判断是否对读地址加一
assign rbinnext = !arst_n ? 0:
!rinc? rbin :
rempty ? rbin :
(rbin+1);
//格雷码地址转换
assign rgaynext = !arst_n ? 0 :
(rbinnext>>1)^(rbinnext);
//给RAM的实际地址
assign raddr = rbin[ADDR_WIDTH-1:0];
//判空逻辑
assign rempty_val = !arst_n ? 1:
(rgaynext == ptr_w2r);
//时序逻辑完成同步
always @(posedge rclk or negedge arst_n) begin
if (!arst_n) begin
rempty <= 1'b1;
end
else begin
rempty <= rempty_val;
end
end
endmodule
写时钟判满模块
module wptr_full #(
parameter ADDR_WIDTH = 8
)
(
output reg wfull,
output [ADDR_WIDTH-1:0] waddr,
output reg [ADDR_WIDTH:0] wptr,
input [ADDR_WIDTH:0] ptr_r2w,
input winc,//写使胿
input wclk,
input arst_n
);
reg [ADDR_WIDTH:0] wbin;
wire [ADDR_WIDTH:0] wgraynext,wbinnext,r2w_graytmp;
wire wfull_eval;
always @(posedge wclk or negedge arst_n) begin
if (!arst_n) begin
wbin <= 0;
wptr <= 0;
end
else begin
wbin <= wbinnext;
wptr <= wgraynext;
end
end
assign wbinnext = !arst_n ? 0 :
!winc ? wbin :
( wfull ? wbin :
( wbin+1 ) );
assign wgraynext = !arst_n ? 0:
(wbinnext >> 1)^(wbinnext);
assign waddr = wbin[ADDR_WIDTH-1:0];
assign r2w_graytmp = !arst_n ? 0:
{~ptr_r2w[ADDR_WIDTH:ADDR_WIDTH-1],ptr_r2w[ADDR_WIDTH-2:0]};
assign wfull_eval = !arst_n ? 0:
( wgraynext == r2w_graytmp );
always @(posedge wclk or negedge arst_n) begin
if (!arst_n) begin
wfull <= 1'b1;
end
else begin
wfull <= wfull_eval;
end
end
endmodule
FIFO顶层模块
module asyn_fifo #(
parameter ADDR_WIDTH = 4,
parameter DATA_WIDTH = 2
)
(
input arst_n,
input [DATA_WIDTH-1:0] wdata,
input winc,
input wclk,
output [DATA_WIDTH-1:0] rdata,
input rinc,
input rclk,
output rempty,
output wfull,
//接出两个接口看信息
output [ADDR_WIDTH-1:0] waddr,
output [ADDR_WIDTH-1:0] raddr
);
wire [ADDR_WIDTH-1:0] waddr,raddr;
wire [ADDR_WIDTH:0] wptr,rptr,ptr_r2w,ptr_w2r;
syn_r2w #(
.ADDR_WIDTH(ADDR_WIDTH)
)
fifo_syn_r2w(
.rptr(rptr),
.wclk(wclk),
.arst_n(arst_n),
.ptr_r2w(ptr_r2w)
);
syn_w2r #(
.ADDR_WIDTH(ADDR_WIDTH)
)
fifo_syn_w2r(
.wptr(wptr),
.rclk(rclk),
.arst_n(arst_n),
.ptr_w2r(ptr_w2r)
);
Ram # (
.DATA_WIDTH(DATA_WIDTH),
.ADDR_WIDTH(ADDR_WIDTH)
)
fifo_Ram(
.wr_addr(waddr),
.rd_addr(raddr),
.arst_n(arst_n),
.wr_clk(wclk),
.rd_clk(rclk),
.wr_data(wdata),
.rd_data(rdata)
);
rptr_empty #(
.ADDR_WIDTH(ADDR_WIDTH)
)
fifo_rptr_empty(
.rempty(rempty),
.raddr(raddr),//写到RAM的二进制地址
.rptr(rptr),//写到读时钟域的格雷码地址
.ptr_w2r(ptr_w2r),//读时钟域的写格雷码地坿
.rinc(rinc),//写使胿
.rclk(rclk),
.arst_n(arst_n)
);
wptr_full #(
.ADDR_WIDTH(ADDR_WIDTH)
)
fifo_wptr_full(
.wfull(wfull),
.waddr(waddr),
.wptr(wptr),
.ptr_r2w(ptr_r2w),
.winc(winc),//写使胿
.wclk(wclk),
.arst_n(arst_n)
);
endmodule
testbench
`timescale 1ns / 1ps
module tb_asyn_fifo;
parameter ADDR_WIDTH = 4;
parameter DATA_WIDTH = 2;
localparam DEPTH = 1 << ADDR_WIDTH;
reg tb_arst_n,tb_wclk,tb_rclk,tb_winc,tb_rinc;
reg [DATA_WIDTH-1:0] tb_wdata;
wire tb_rempty,tb_wfull;
wire [DATA_WIDTH-1:0] tb_rdata;
wire [ADDR_WIDTH-1:0] tb_waddr,tb_raddr;
initial begin
tb_arst_n = 0;tb_wclk = 0;tb_rclk = 0;tb_wdata = 0;
tb_winc = 0;tb_rinc = 0;
#11
tb_arst_n = 1;
#500
tb_arst_n = 0;
end
// initial begin
// $vcdpluson;
// end
initial begin
#21
read_data();
end
initial begin
#41
write_data();
end
always #2 tb_wclk = ~tb_wclk;
always #5 tb_rclk = ~tb_rclk;
asyn_fifo #(
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH)
)
uut_asyn_fifo(
.arst_n(tb_arst_n),
.wdata(tb_wdata),
.winc(tb_winc),
.wclk(tb_wclk),
.rdata(tb_rdata),
.rinc(tb_rinc),
.rclk(tb_rclk),
.rempty(tb_rempty),
.wfull(tb_wfull),
.waddr(tb_waddr),
.raddr(tb_raddr)
);
task write_data;
begin
forever begin
@(posedge tb_wclk) begin
tb_winc <= 1;
tb_wdata <= ($random)%4;
end
end
end
endtask
task read_data;
begin
forever begin
@(posedge tb_rclk) begin
tb_rinc <= 1;
end
end
end
endtask
endmodule
仿真结果: