Digital IC design study notes_cross-clock domain synchronization problem 2_multi-bit signal cross-clock domain problem_asynchronous FIFO

Digital IC design study notes

Cross-clock domain synchronization problem

2 多比特信号跨时钟域问题_异步FIFO

2 Asynchronous FIFO

  • Schematic diagram

Insert picture description here
The design of asynchronous FIFO mainly consists of 5 parts:

  1. FIFO Memory : Dual-port RAM to store data
  2. sync_r2w : synchronizer, synchronize the read data pointer to the write clock domain
  3. sync_w2r : synchronizer, synchronize write data pointer to read clock domain
  4. wptr_full : Logic that handles the write pointer and full signal
  5. 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
    Insert picture description here
    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

Insert picture description here
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

Insert picture description here
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
    Insert picture description here
    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
    Insert picture description here

[Note]: Personal study notes, if there are any mistakes, please feel free to enlighten me. This is polite~~~


Guess you like

Origin blog.csdn.net/weixin_50722839/article/details/110149044