数字IC设计学习笔记
跨时钟域同步问题
2 多比特信号跨时钟域问题_异步FIFO
2 异步FIFO
- 原理图
异步FIFO的设计主要有5部分组成:
- FIFO Memory:双口RAM存储数据
- sync_r2w:同步器,同步读数据指针到写时钟域
- sync_w2r:同步器,同步写数据指针到读时钟域
- wptr_full:处理写指针和满信号的逻辑
- rptr_empt:处理读指针和空信号的逻辑
- Verilog代码
//----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
- 在FPGA中,我们可以用block ram也可以用distributed ram。
- Verilog代码
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仿真
注意:对memory的地址为二进制的地址;对空满判定的地址为格雷码的地址。
2. sync_r2w
- Verilog代码
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仿真
3. sync_w2r
- Verilog代码
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仿真
4. wptr_full
-
满信号的产生逻辑放在写时钟域。由于N比特格雷码的低N-1比特具有对称性,比较读写指针时我们像二进制码那样仅仅判断第N比特不等而低N-1比特相等是不够的,会产生错误的满信号。为了产生正确的满信号,我们需要同时判断以下三个条件:
第N-1比特不相等
第N-2比特不相等
低N-3比特全相等
与空信号类似,为了full信号的寄存器输出,对于写指针我们可以用 wgraynext 而不是 wptr(wptr要比wgraynext晚一拍)。 -
Verilog代码
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仿真
5. rptr_empty - 空信号的产生逻辑放在读时钟域。直接对比读写指针是否相等即可(读指针追上写指针),注意这里的写指针是同步到读时钟域之后的写指针。为了empty信号的寄存器输出,比较时读指针我们可以用
rgraynext 而不是 rptr(rptr要比rgraynext晚一拍)。 - Verilog代码
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仿真
【注】:个人学习笔记,如有错误,望不吝赐教,这厢有礼了~~~