跨时钟域
全同步设计:1个时钟
全异步设计:没有时钟(存在少)
全局异步,局部同步设计:不可避免,单一时钟不能满足设计需求
多时钟域设计不可避免问题:亚稳态,同步失败
同步化:同步器;保持寄存器和握手(效率不高);异步FIFO设计;
两级寄存器
一级寄存概率的平方,虽然两级并不能完全消除亚稳态危害,但是提高了可靠性,减少其发生概率
分类:电平同步器,边沿检测同步器,脉冲同步器
电平同步器(打两拍)
从Clock1到Clock2的跨时钟,触发器之间无组合逻辑,电平同步一般只适用于频率一样,相位上没有同步,但是两者频率不一样,就不能使用。如果是慢时钟到快时钟域,会出现数据重复采样。
边沿检测同步器(慢到快)
将慢时钟域的脉冲同步到快时钟域,快时钟采集慢数据,会出现数据重复,将数据变化转为一个脉冲,并进行门运算,去除重复数据
脉冲同步器(快到慢)
慢到快只需要考虑亚稳态问题,快到慢要考虑采样速率,保证数据不丢失,这个问题称为信号宽度问题。一般安全的宽度是快时钟域的信号宽度必须是慢时钟域周期的1.5倍以上,也就是持续三个时钟沿以上。整个设计包括脉冲电平检测、双触发器同步、边沿检测三部分。
多bit信号跨时钟域传输
b_load和b_en各自经过同步器延时之后,产生不同步的问题,解决方案将两路合成一路,再经过同步器,分成两路信号控制置位和使能端
总线信号的跨时钟域
正确方法:保持寄存器加握手信号;异步FIFO设计
异步FIFO:读时钟和写时钟为独立时钟,思想:隔离时钟域,匹配读写速度;
异步FIFO介绍
FIFO(First In First out)先进先出,它是一种存储器结构,能读写数据,所以有双口RAM作为存储数据的单元,在不同时钟域传输数据,使用异步FIFO来作为传输的缓冲区,既可以保证异步时钟数据传输的时序要求变得宽松,也提高传输效率。
FIFO写满和读空标志的产生
读写指针比较产生,同步FIFO直接把读写指针比较或者运算产生,而异步FIFO由于读写指针不同时钟域,比较前指针必须同步化,由于二进制的指针会出现多位同时跳变,直接由同步器进行同步化会有问题
判断存储器(RAM)写入写出的状态,需要有写满判断(wfull信号),和读空判断(empty信号),如何得到写满和读空信号?通过比较读写地址(告诉我们数据数据位置)、指针(四个信号 waddr,wptr,raddr,rptr)来判断此刻FIFO的写满和读空。
判断中涉及到w2r和r2w时钟域问题(2级寄存器同步)
对于异步FIFO,读写为不同时钟,如果直接采样,就会有亚稳态问题,比如写地址从00111改变01000的时候,读时钟恰好采样,除了最高位,其他4为有可能出现亚稳态,也有可能同步到错误的地址,这就是需要引入格雷码的原因。
设定地址指针位数时需要+1位,如16个数组需要4位,地址指针位数为5,
当最高位和次高位相同,其余位相同则认为是读空;
当最高位和次高位不同,其余位相同则认为是写满;
下面是用Verilog HDL设计深度为256(存储器可以写入256个数据),位宽为16(每个数据的宽度为16)的FIFO
module asyn_fifo
#(
parameter data_width = 16,
parameter data_depth = 8,
parameter ram_depth = 256
)
(
input rst_n,
input wr_clk,
input wr_en,
input [data_width-1:0] data_in,
output full,
input rd_clk,
input rd_en,
input reg [data_width-1:0] data_out,
output empty
);
reg [data_depth-1:0] wr_adr;
reg [data_depth-1:0] rd_adr;
reg [data_depth:0] wr_adr_ptr;
reg [data_depth:0] rd_adr_ptr;
wire [data_depth:0] wr_adr_gray;
reg [data_depth:0] wr_adr_gray1;
reg [data_depth:0] wr_adr_gray2;
wire [data_depth:0] rd_adr_gray;
reg [data_depth:0] rd_adr_gray1;
reg [data_depth:0] rd_adr_gray2;
//dual port ram -write and read
assign wr_adr = wr_adr_ptr[data_depth-1:0];
assign rd_adr = rd_adr_ptr[data_depth-1:0];
integer i;
reg [data_width-1:0] ram_fifo [ram_depth-1:0];
always@(posedge wr_clk or negedge rst_n)begin
if(!rst_n)begin
for(i=0;i<ram_depth;i++)
ram_fifo[i] <= 'd0;//全部清零
end
else if(wr_en && (~full))
ram_fifo[wr_adr] <= data_in;
else
ram_fifo[wr_adr] <= ram_fifo[wr_adr];
end
always@(posedge rd_clk or negedge rst_n)begin
if(!rst_n)
data_out <= 'd0;
end
else if(rd_en && (~empty))
data_out <= ram_fifo[rd_adr];
else
data_out <= 'd0;
end
//wr_adr_ptr ++ and rd_adr_ptr ++
always@(posedge wr_clk or negedge rst_n)begin
if(!rst_n)
wr_adr_ptr <= 'd0;
else if(wr_en && (~full))
wr_adr_ptr <= wr_adr_ptr + 1'b1;
else
wr_adr_ptr <= wr_adr_ptr;
end
always@(posedge rd_clk or negedge rst_n)begin
if(!rst_n)
rd_adr_ptr <= 'd0;
end
else if(rd_en && (~empty))
rd_adr_ptr <= rd_adr_ptr + 1'b1 ;
else
rd_adr_ptr <= rd_adr_ptr;
end
//binary to gray
assign wr_adr_gray = (wr_adr_ptr >> 1) ^ wr_adr_ptr;
assign rd_adr_gray = (rd_adr_ptr >> 1) ^ rd_adr_ptr;
//gray cdc compare
always@(posedge wr_clk or negedge rst_n)begin
if(!rst_n)begin
wr_adr_gray1 <= 'd0;
wr_adr_gray2 <= 'd0;
end
else begin
wr_adr_gray1 <= wr_adr_gray;
wr_adr_gray2 <= wr_adr_gray1;
end
end
always@(posedge rd_clk or negedge rst_n)begin
if(!rst_n)begin
rd_adr_gray1 <= 'd0;
rd_adr_gray2 <= 'd0;
end
else begin
rd_adr_gray1 <= rd_adr_gray;
rd_adr_gray2 <= rd_adr_gray1;
end
end
assign empty = (rd_adr_gray == wr_adr_gray2)?1'b1:1'b0;
assign full=(wr_adr_gray[data_depth:data_depth-1] !=rd_adr_gray2[data_depth:data_depth-1])&&(wr_adr_gray[data_depth:data_depth-2:0] ==rd_adr_gray2[data_depth-2:0]);
endmodule
测试代码如下
'timescale 1us/1us
module asyn_fifo_tb;
reg rst_n;
reg wr_clk;
reg wr_en;
reg [15:0] data_in;
wire full;
reg rd_clk;
reg rd_en;
wire [15:0] data_out;
wire empty;
asyn_fifo asyn_fifo_inst
(
.rst_n (rst_n),
.wr_clk (wr_clk),
.wr_en (wr_en),
.data_in (data_in),
.full (full),
.rd_clk (rd_clk),
.rd_en (rd_en),
data_out (data_out),
.empty (empty)
);
initial wr_clk = 0;
always#10 wr_clk=~wr_clk;
initial rd_clk = 0;
always#30 rd_clk=~rd_clk;
always@(posedge wr_clk or negedge rst_n)begin
if(!rst_n)
data_in <= '0d0;
else if(wr_en)
data_in <=data_in + 1'b1;
else
data_in <= data_in;
end
initial begin
rst_n = 0;
wr_en = 0;
rd_en = 0;
#200;
rst_n = 1;
wr_en = 1;
#20000;
wr_en = 0;
rd_en = 1;
#20000;
rd_en = 0;
$stop;
end
endmodule
仿真测试结果如下图所示