第3章:处理多个时钟
3.1跨时钟域数据传输简介
主要难点:
1.满足建立时间和保持时间条件;
2.避免产生亚稳态;
建立时间:在时钟脉冲到来前,输入数据需要保持稳定的时间。
保持时间:在时钟脉冲到来后,输入数据仍需保持稳定的时间。
(即输入数据在时钟触发沿前后均需要保持稳定相应的时间)
3.4多时钟设计的处理技术
1.时钟命名法:
系统时钟:sysy_clk,发送时钟:tx_clk,接收时钟:rx_clk
同一时钟域的时钟也应当使用相同的前缀:如sys_rom_addr,sys_rom_data
方便脚本处理,提高接口可读性,提高团队工作效率
2.分块化设计
1)每个模块都应当在单个时钟下工作;
2)在信号跨时钟域传输时,使用同步器模块,将信号同步;
3)同步器模块规模应尽可能小。
3.同步器模块
双级同步器:
电路:
时序:
当出现的亚稳态回到稳态的时间超过一个采样时钟周期时,也会产生亚稳态传输,此时就应当使用三级同步器
电路:
时序:
当使用的级数越多,产生亚稳态的概率越低,但大多数设计中使用两级同步电路就可以满足需求,只有在时钟频率较高的设计中,才会用到三级同步器
3.5跨时钟域
数据跨时钟域传输的两种方法
1.使用握手信号的方式
2.使用异步FIFO
在异步时钟域传输中,多个时钟源于同一时钟,并且它们的相位和频率是已知的,则可以看成跨同步时钟域的时钟。
1)同频零相位时钟
实际不算跨时钟域传输,等效为同一时钟;
2)同频恒定相位时钟
当相位差固定时,只要满足建立/保持时间,就可以正确传输,但是要注意组合逻辑的时延设计;
3)非同频、可变相位差时钟
1.整数倍频时钟
一个时钟是另一个时钟的整数倍,并且有效边沿的相位差是可变的。最小相位差是中等于较快时钟的时钟周期。
快时钟域到慢时钟域传输时,为了避免数据丢失,数据应当至少保持一个目的周期的稳定转态,这可以通过源时钟计数来完成。
慢时钟域到快时钟域传输时,数据均能采集到
2.非整数倍频时钟
此种条件下比较复杂,大体分为三种情况:
1)在源时钟和目的时钟有效沿之间有足够大的相位差,所以不会产生亚稳态;
clk1为5ns,clk2为10ns,最小相位差为2.5ns,可以满足建立保持时间。
2)源时钟和目的时钟有效沿非常接近,导致产生亚稳态问题。
在慢时钟域到快时钟域传输时,如下图,可以发现会产生数据不连贯的现象(产生亚稳态,但是在下一边沿即正常采样),
但是不会丢失数据,这是由于数据信号的稳定时间超过一个目的周期;
当在快时钟域到慢时钟域传输时,只要保证数据信号稳定时间大于一个目的周期即可。
clk1为10ns,clk2为7ns,最小相位差差在49,50ns产生,为0.5ns,这时不满足建立保持时间
3)两个时钟沿在许多连续的有效沿都很接近,这与异步时钟很相似
clk1为10ns,clk2为9ns,会产生4个连续周期,时钟沿很近
此时会产生很多亚稳态,需要进行同步,为了不丢失数据,数据应保持2个目的时钟周期,适用于两个方向的传输。
3.6握手信号方法
时序如下:需要同步x_req和y_ack,一个数据从发送器传输到接收器,需要5个时钟周期
握手信号要求
数据在发送时钟域内稳定至少两个时钟上升沿(等待req的同步);
请求信号x_req宽度应该超过两个上升沿时钟,避免由快到慢时钟域数据丢失。
3.7同步FIFO
先入先出存储器,常用于跨时钟域数据传输。双端口RAM可以使得FIFO的读写操作可以独立进行。
同步FIFO架构
通过读写指针产生读写地址,写指针指向下一个要写入的地址,读指针指向下一个要读取的地址,有效的读写使能信号使读写指针递增。
当产生fifo_empty信号,表示fifo中没有数据,不可以读取;
当产生fifo_full信号,表示fifo中数据已满,不可以写入。
产生空、满信号的方法:
一、累加比较读写指针
fifo_full 逻辑代码:
always@(posedge clk or negedge reset_n)
begin: fifo_full_gen
if(~reset_n)
fifo_full <= 1'b0;
else if(wr_fifo && rd_fifo)
;//do nothing
else if(rd_fifo)
fifo_full <= 1'b0;
else if(wr_fifo && (rd_ptr = wr_ptr + 1'b1))
fifo_full <= 1'b1;
end
fifo_empty 逻辑代码:
always@(posedge clk or negedge reset_n)
begin: fifo_empty_gen
if(~reset_n)
fifo_empty <= 1'b0;
else if(wr_fifo && rd_fifo)
;//do nothing
else if(wr_fifo)
fifo_emptyl <= 1'b0;
else if(wr_fifo && (wr_ptr = rd_ptr + 1'b1))
fifo_empty <= 1'b1;
end
二、计数器表示当前fifo状态
3.8异步FIFO
用于对性能要求较高的设计中,尤其是时钟延迟比系统资源更为重要的环境中。
3.8.1使用格雷码
格雷码产生亚稳态的几率远小于二进制码计数。
当异步FIFO位于满或空的状态时,需要把读写指针进行同步。
FIFO满:
同步写指针需要两个周期的延迟,在此期间,FIFO一直保持满的状态,不能写入,这可以避免数据丢失。
FIFO空:
同理,空状态时,也需要把读指针同步到写时钟域中,也会延迟两个周期。
3.8.3使用格雷码实现FIFO指针
使用二进制加法器的格雷码计数器
格雷码与二进制码的转换
格雷码转二进制码:
binn-1 = grayn-1
bini = grayi ^ bini+1
即bin = grayi ^ gray i+1 ^ grayi+2 ^…..^grayn-1
verilog代码;
module gray_to_bin(bin,gray)
parameter SIZE = 4;
input [SIZE - 1: 10] bin;
output [SIZE-1:10] gray;
reg [SIZE-1 :0]bin;
integer I;
always@(*)
for(i = 0,i<=SIZE,i++)
bin[i]= ^(gray >>1);
endmodule
二进制码转格雷码:
grayn-1 = binn-1
grayi = bini ^ binn+1
verilog代码:
module bin_to_gray(bin,gray)
parameter SIZE = 4;
input [SIZE - 1: 0] bin;
output [SIZE-1 : 10] gray;
assign gray = (bin >> 1) ^ bin;
endmodule
3.8.3.3格雷码计数器
verilog代码:
module gray_counter(clk,gray,inr,reset_n)
parameter SIZE = 4;
input clk,inr,reset_n;
output [SIZE-1:0] gray;
reg [SIZE-1] gray_temp,gray,bin_temp,bin;
integer I;
always@(*)
begin:gray_bin_gray
for(i=0;i<SIZE;i++)
b[i] = ^(gray >> i);
bin_temp = bin +inr;
gray_temp = (bin_temp >> 1) ^ bin_temp;
end
always@(posedge clk or negedge reset_n)
begin:gray——registered
if(~reset_n)
gray <={SIZE{1'b0}};
else
gray <= gray_temp;
end
endmodule
3.8.4FIFO满和FIFO空的产生
当二进制码指针最高有效位不同,其余位相同时,FIFO为满;
当二进制码的所有为都相同时,FIFO为空。
当直接用格雷码实现计算FIFO空和FIFO满进,需要4个格雷码-二进制码转换器;
3.8.5双n位格雷码计数器用于FIFO空满产生逻辑
创建2个格雷码计数器,一个n位,一个n-1位
FIFO空产生的条件:
此时为格雷码计数器
verilog代码为:
always@(posedge clk or negedge reset_n)
begin:fifo_mpty_gen
if(~reset_n)
fifo_empty <= 1'b1;
else
fifo_empty<= (rd_getmp == wr_ptr_sync); //当前读指针被暂存,与被同步后的写指针比较
end
FIFO满条件的产生:
1.当FIFO为空,即rd_ptr == wr_ptr==0,
2.连续写,直到FIFO满,此时wr_ptr==7,rd_ptr=0;
3.连续读,直到FIFO空,rd_ptr==wr_ptr==1
但是在位于7,8时,会产生错误的位信号,所以我们将读写指针的MSB和第二MSB异或,所得到的的值进行对比,若一致,则为空
verilog代码如下:
wire rd_2nd_msb =rd_ptr_sync[SIZE ^ rd_ptr_sync[SIZE-1]];
wire wr_2nd_msb =wr_ptr_sync[SIZE ^ wr_ptr_sync[SIZE-1]];
always@(posedge clk or negedge reset_n)
begin:fifo_full_gen
if(~reset_n)
fifo_full <= 1'b0;
else
fifo_full <= ((wr_gtemp[SIZE] != rd_ptr_sync[SIZE]) && (rd_2nd_msb == wr_2nd_msb) && (wr_gtemp[SIZE-2:0] == rd_ptr_sync[SIZE-2:0]));
end