Digital IC design study notes
Cross-clock domain synchronization problem
2 多比特信号跨时钟域问题_异步FIFO
2 Asynchronous FIFO
- Schematic diagram
The design of asynchronous FIFO mainly consists of 5 parts:
- FIFO Memory : Dual-port RAM to store data
- sync_r2w : synchronizer, synchronize the read data pointer to the write clock domain
- sync_w2r : synchronizer, synchronize write data pointer to read clock domain
- wptr_full : Logic that handles the write pointer and full signal
- rptr_empt : Logic that handles the read pointer and empty signal
- Verilog code
//----TOP module-------------------------------
module asyncfifo_r1#(
parameter ADDRSIZE = 4,
parameter DATASIZE = 8
)
(
//----write signal ----------------------------
input wclk,
input wrst,
input [DATASIZE-1:0] wdata,
input winc,
output wfull,
//----read signal ----------------------------
input rclk,
input rrst,
input rinc,
output empty,
output [DATASIZE-1:0] rdata
);
wire [ADDRSIZE-1:0] waddr;
wire [ADDRSIZE-1:0] raddr;
wire [ADDRSIZE:0] wptr, rptr, wq2_rptr, rq2_wptr;
FIFOMEM_R0 U1 (
.wclk (wclk),
.wdata(wdata),
.rdata(rdata),
.waddr (waddr),
.raddr(raddr),
.wclken(winc),
.wfull (wfull)
);
syn_r2w U2 (
.wclk(wclk),
.rst_n(wrst),
.wq2_rptr(wq2_rptr),
.rptr(rptr)
);
sync_w2r_r1 U3 (
.rclk(rclk),
.rst_n(rrst),
.rq2_wptr(rq2_wptr),
.wptr(wptr)
);
wptr_full_r1 U4(
.wclk(wclk),
.wrst_n(wrst),
.winc(winc),
.wq2_rptr(wq2_rptr),
.wfull(wfull),
.waddr(waddr),
.wptr(wptr)
);
rptr_empty U5(
.rclk(rclk),
.rst_n(rrst),
.rinc(rinc),
.rq2_wptr(rq2_wptr),
.rempty(empty),
.raddr(raddr),
.rptr(rptr)
);
endmodule
1. FIFO Memory
- In FPGA, we can use block ram or distributed ram.
- Verilog code
module FIFOMEM_R0#(
parameter DATASIZE = 8, // memory data word width
parameter ADDRSIZE = 4 // memory address bits
)
(
input wclk,
input [DATASIZE-1:0] wdata,
input [ADDRSIZE-1:0] waddr,
input [ADDRSIZE-1:0] raddr,
input wclken,
input wfull,
output [DATASIZE-1:0] rdata
);
localparam DEPTH = 1<< ADDRSIZE;
reg [DATASIZE-1:0] mem [0:DEPTH-1];
//read----------------------------------
assign rdata = mem[raddr];
//write---------------------------------
always@(posedge wclk)begin
if(wclken && !wfull) //写有效且未写满
mem[waddr] <= wdata;
end
endmodule
- Modelsim simulation
Note: The memory address is a binary address; the address for determining whether it is full or empty is a gray code address.
2. sync_r2w
- Verilog code
module syn_r2w#(
parameter ADDRSIZE = 4
)
(
input wclk,
input rst_n,
input [ADDRSIZE:0] rptr,
output reg [ADDRSIZE:0] wq2_rptr
);
reg [ADDRSIZE:0] wq1_rptr;
always@(posedge wclk or negedge rst_n)begin
if(!rst_n)begin
wq1_rptr <= 0;
wq2_rptr <= 0;
end else begin
wq1_rptr <= rptr;
wq2_rptr <= wq1_rptr;
end
end
endmodule
- Modelsim simulation
3. sync_w2r
- Verilog code
module sync_w2r_r1#(
parameter ADDRSIZE = 4
)
(
input rclk,
input rst_n,
input [ADDRSIZE:0] wptr,
output reg [ADDRSIZE:0] rq2_wptr
);
reg [ADDRSIZE:0] rq1_wptr;
always@(posedge rclk or negedge rst_n)begin
if(!rst_n)begin
rq1_wptr <= 0;
rq2_wptr <= 0;
end else begin
rq1_wptr <= wptr;
rq2_wptr <= rq1_wptr;
end
end
endmodule
- Modelsim simulation
4. wptr_full
-
The full signal generation logic is placed in the write clock domain. Since the lower N-1 bits of the N-bit Gray code are symmetrical, when comparing read and write pointers, it is not enough for us to judge that the Nth bit is not equal and the lower N-1 bits are equal, like a binary code, and a false full signal will be generated. In order to generate a correct full signal, we need to judge the following three conditions at the same time:
The N-1th bit is not equal
The N-2th bit is not equal The
lower N-3 bits are all equal
and the empty signal is similar. For the register output of the full signal, for For writing pointers, we can use wgraynext instead of wptr (wptr is one beat later than wgraynext). -
Verilog code
module wptr_full_r1#(
parameter ADDRSIZE = 4
)
(
input wclk,
input wrst_n,
input winc,
input [ADDRSIZE:0] wq2_rptr,
output reg wfull,
output [ADDRSIZE-1:0] waddr,
output reg [ADDRSIZE:0] wptr
);
wire wfull_val;
wire [ADDRSIZE:0] wbinnext;
wire [ADDRSIZE:0] wgraynext;
reg [ADDRSIZE:0] wbin;
//----write address---------------//
assign waddr = wbin[ADDRSIZE-1:0];
//----bin2gray--------------------//
assign wbinnext = wbin + (winc & ~wfull);
assign wgraynext = (wbinnext>>1)^ wbinnext;
//----gray pointer----------------//
always@(posedge wclk or negedge wrst_n)begin
if(!wrst_n)begin
wptr <= 0;
wbin <= 0;
end else
begin
wptr <= wgraynext;//gray
wbin <= wbinnext;
end
end
//----write full------------------//
assign wfull_val = ((wgraynext[ADDRSIZE] != wq2_rptr[ADDRSIZE]) &&
(wgraynext[ADDRSIZE-1] != wq2_rptr[ADDRSIZE-1]) &&
(wgraynext[ADDRSIZE-2:0] == wq2_rptr[ADDRSIZE-2:0]));
always@(posedge wclk or negedge wrst_n)begin
if(!wrst_n)
wfull <= 0;
else
wfull <= wfull_val;
end
endmodule
- Modelsim simulation
5. rptr_empty - The generation logic of the null signal is placed in the read clock domain. It is enough to directly compare whether the read and write pointers are equal (the read pointer catches up with the write pointer). Note that the write pointer here is the write pointer synchronized to the read clock domain. For the register output of the empty signal, we can use
rgraynext instead of rptr to read the pointer when comparing (rptr is one beat later than rgraynext). - Verilog code
module rptr_empty#(
parameter ADDRSIZE = 4
)
(
input rclk,
input rst_n,
input rinc,
input [ADDRSIZE:0] rq2_wptr,
output reg rempty,
output [ADDRSIZE-1:0] raddr,
output reg [ADDRSIZE:0] rptr //gray
);
reg [ADDRSIZE:0] rbin;
wire [ADDRSIZE:0] rgraynext;
wire [ADDRSIZE:0] rbinnext;
wire rempty_val;
//----gray pointer---------------------------------------//
always@(posedge rclk or negedge rst_n)begin
if(!rst_n)begin
rbin <= 0;
rptr <= 0;
end else
begin
rbin <= rbinnext;
rptr <= rgraynext;
end
end
//----memory address-------------------------------------//
assign raddr = rbin[ADDRSIZE-1:0];
//----bin2gray-------------------------------------------//
assign rbinnext = rbin + (rinc & ~rempty);
assign rgraynext = (rbinnext>>1)^rbinnext;
//----FIFO empty when rptr == ayncronized wptr-----------//
assign rempty_val = (rgraynext == rq2_wptr);
always@(posedge rclk or negedge rst_n)begin
if(!rst_n)begin
rempty <= 0;
end else
rempty <= rempty_val;
end
endmodule
- Modelsim simulation
[Note]: Personal study notes, if there are any mistakes, please feel free to enlighten me. This is polite~~~