Chapter1. 同步异步FIFO专题
章节导图

同步FIFO
移位寄存器方法 单指针
module sync_fifo #(
parameter DATA_WIDTH = 8
parameter FIFO_DEPTH = 16,
)
(
input clk,
input rst_n,
input [DATA_WIDTH-1:0] din,
input push,
input pop,
output [DATA_WIDTH-1:0] dout,
output reg full,
output reg empty
);
parameter A_FULL = FIFO_DEPTH - 2;
reg [DATA_WIDTH-1:0] mem [FIFO_DEPTH-1:0];
reg [$clog2(FIFO_DEPTH)-1:0] read_ptr;
integer i;
// shift to write data
always @(posedge clk or negedge rst_n)begin
if(push & !full)begin
mem[0] <= din;
for(i = 0; i < FIFO_DEPTH; i = i + 1)begin
mem[i+1] <= mem[i];
end
end
end
// read poniter
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
read_ptr <= FIFO_DEPTH - 1'b1;
else if (push & !pop & !full)begin
if(read_ptr == FIFO_DEPTH - 1'b1)
read_ptr <= 1'b0;
else
read_ptr <= read_ptr + 1'b1;
end
else if (pop & !push & !empty)begin
if(read_ptr == 1'b0)
read_ptr <= FIFO_DEPTH - 1'b1;
else
read_ptr <= read_ptr - 1'b1;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
empty <= 1'b1;
else if (~push & pop & (read_ptr == 0))
empty <= 1'b1;
else if (!pop & push)
empty <= 1'b0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
full <= 1'b0;
else if (~pop & push & (read_ptr == A_FULL))
full <= 1'b1;
else if (!push & pop)
full <= 1'b0;
end
assign dout = (FIFO_DEPTH == 1) ? mem[0] : mem[read_ptr];
endmodule
双口RAM同步FIFO
module dual_port_RAM #(
parameter WIDTH = 8,
parameter DEPTH = 16
)
(
input wclk,
input wenc,
input [$clog2(DEPTH)-1:0] waddr,
input [DEPTH-1:0] wdata,
input rclk,
input renc,
input [$clog2(DEPTH)-1:0] raddr,
output reg [DEPTH-1:0] rdata,
);
reg [WIDTH-1:0] mem [DEPTH-1:0];
always @(posedge wclk)begin
if(wenc)
mem[waddr] <= wdata;
end
always @(posedge rclk) begin
if(renc)
rdata <= mem[raddr];
end
endmodule
module sync_fifo #(
parameter WIDTH = 8,
parameter DEPTH = 16
)
(
input clk,
input rst_n,
input winc,
input rinc,
input [WIDTH-1:0] datain,
output reg empty,
output reg full,
output [WIDTH-1:0] dataout
);
reg [$clog2(DEPTH):0] wr_ptr, rd_ptr;
wire wenc, renc;
assign wenc = !full & winc;
assign renc = !empty & rinc;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
full <= 1'b0;
empty <= 1'b0;
end
else begin
full = (wr_ptr - rd_ptr) == DEPTH;
empty = (wr_ptr == rd_ptr);
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
wr_ptr <= 1'b0;
else if (wenc)
wr_ptr <= wr_ptr + 1'b1;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
rd_ptr <= 1'b0;
else if (renc)
rd_ptr <= rd_ptr + 1'b1;
end
dual_port_RAM inst (clk, wenc, wr_ptr[$clog2(DEPTH)-1:0], wdata, clk, renc. rd_ptr[$clog2(DEPTH)-1:0], rdata);
endmodule
单口RAM同步FIFO
// 计算指针部分的模块是相同的,需要改变的仅仅是RAM部分
module single_port_RAM #(
parameter WIDTH = 8,
parameter DEPTH = 16
)
(
input clk,
input wenc,
input renc,
input [$clog2(DEPTH)-1:0] waddr,
input [$clog2(DEPTH)-1:0] raddr,
input [WIDTH-1:0] wdata,
output reg [WIDTH-1:0] rdata
);
reg [WIDTH-1:0] mem [DEPTH-1:0];
// 写优先, 当读和写同时为高的情况下,只执行写操作,读操作不理会
always @(posedge clk)begin
if(wenc)
mem[waddr] <= wdata;
else if (renc)
rdata <= mem[raddr];
end
endmodule
异步FIFO
格雷码
如果异步FIFO是快到慢的cdc情况,那么可能出现格雷码变化不是连续的(漏采样)情况,会不会出现亚稳态,如果不会,为什么?
- 答案:不会出现亚稳态,即便出现漏采样的情况,两次采样到的格雷码不是连续变化的,但也不会出现亚稳态,因为相邻格雷码只有一位改变,而多bit cdc出现亚稳态的原因是在同一个周期有多个bit发生变化,发生变化的时间是有差异的,漏采的情况中,虽然多bit发生了变化,但是并不是在同一个周期内发生的,因此直接打两拍不会出现亚稳态。
空满信号悲观是什么意思?
- 答案:空信号是在读时钟域判断的,满信号是在写时钟域判断的;由于跨时钟域同步,因此可能会出现假空或者假满的情况,这是一种悲观的判断。
异步FIFO代码
module dual_port_RAM #(
parameter WIDTH = 8,
parameter DEPTH = 16
)
(
input wclk,
input wenc,
input [$clog2(DEPTH)-1:0] waddr,
input [DEPTH-1:0] wdata,
input rclk,
input renc,
input [$clog2(DEPTH)-1:0] raddr,
output reg [DEPTH-1:0] rdata
);
reg [WIDTH-1:0] mem [DEPTH-1:0];
always @(posedge wclk)begin
if(wenc)
mem[waddr] <= wdata;
end
always @(posedge rclk)begin
if(renc)
rdata = mem[raddr];
end
endmodule
module async_fifo #(
parameter WIDTH = 8
parameter DEPTH = 16,
)
(
input wclk,
input rclk,
input wrst_n,
input rrst_n,
input winc,
input rinc,
input [WIDTH-1:0] wdata,
output wfull,
output rempty,
output [WIDTH-1:0] rdata
);
parameter ADDR_WIDTH = $clog2(DEPTH);
reg [ADDR_WIDTH:0] wr_ptr, rd_ptr, wr_grey0, wr_grey1, wr_grey2;
reg [ADDR_WIDTH:0] rd_grey0, rd_grey1, rd_grey2;
wire [ADDR_WIDTH:0] wr_grey, rd_grey;
wire wenc, renc;
assign wenc = !wfull & winc;
assign renc = !rempty & rinc;
assign wr_grey = wr_ptr ^ (wr_ptr >> 1);
assign rd_grey = rd_ptr ^ (rd_ptr >> 1);
assign wfull = wr_grey0 == {~rd_grey2[ADDR_WIDTH:ADDR_WIDTH-1], rd_grey2[ADDR_WIDTH-2:0]};
assign rempty = wr_grey2 == rd_grey0;
always @(posedge wclk or negedge wrst_n)begin
if(!wrst_n)
wr_ptr <= 1'b0;
else if (wenc)
wr_ptr <= wr_ptr + 1'b1;
end
always @(posedge rclk or negedge rrst_n)begin
if(!rrst_n)
rd_ptr <= 1'b0;
else if (renc)
rd_ptr <= rd_ptr + 1'b1;
end
always @(posedge wclk or negedge wrst_n)begin
if(!wrst_n)
wr_grey0 <= 1'b0;
else
wr_grey0 <= wr_grey;
end
always @(posedge rclk or negedge rrst_n)begin
if(!rrst_n)
rd_grey0 <= 1'b0;
else
rd_grey0 <= rd_grey;
end
always @(posedge wclk or negedge wrst_n)begin
if(!wrst_n)begin
rd_grey1 <= 1'b0;
rd_grey2 <= 1'b0;
end
else begin
rd_grey1 <= rd_grey0;
rd_grey2 <= rd_grey1;
end
end
always @(posedge rclk or negedge rrst_n)begin
if(!rrst_n)begin
wr_grey1 <= 1'b0;
wr_grey2 <= 1'b0;
end
else begin
wr_grey1 <= wr_grey0;
wr_grey2 <= wr_grey1;
end
end
dual_port_RAM inst
(
wclk, wenc, wr_ptr[ADDR_WIDTH-1:0], wdata, rclk, rd_ptr[ADDR_WIDTH-1:0],
rdata
);
endmodule