跨时钟域总结

       亚稳态介绍:

        亚稳态是指在设计的正常运行过程中,信号在一定时间内不能达到稳定的0或者1的现象。由于同步系统中,数据与时钟存在固定的关系,其输入信号满足触发器的时序要求,即建立时间和保持时间,所以不会发送亚稳态现象;而对于异步系统,数据与时钟关系不固定,当出现不满足建立时间和保持时间的现象时,就会输出亚稳态,输出介于0和1之间的不定态。

         举例:输入信号(src_data_out)在源时钟域(src_clk)的亚稳态窗内发生了变化,在另一个时钟域(dest_clk)对该输入信号采样时,就会导致输出出现亚稳态(dest_data_out)。

        所谓亚稳态窗,就是建立时间+保持时间

        另:在异步复位中,如果复位信号不满足removal time和recovery time也会出现亚稳态。 

        而对于容易出现亚稳态现象的电路则需要进行跨时钟域处理。

        跨时钟域介绍:

         跨时钟域主要目的是保证数据从A时钟到B时钟的正确传递,其发生于多时钟域下,包含以下关系:

        1. 时钟频率不同;

        2. 时钟频率相同,但相位不同;

         跨时钟域信号的传输分为两类:

        1. 控制信号的传输(单bit信号)

        2. 数据信号的传输(多bit信号)

        其中,控制信号的传输分为从快时钟域到慢时钟域的传输,慢时钟域到快时钟域的传输。

        慢时钟域到快时钟域的传输——延迟打拍:

        此类传输可直接使用两级触发器进行缓存即可,因为理论上,快时钟域总会采集到慢时钟域的信号;在快时钟域下,对慢时钟域的信号进行连续缓存两次,可有效降低因建立时间和保持时间的不满足而导致的亚稳态问题。

RTL code:
module pulse_syn_slow2f(
    input     clk_f,clk_s,rst_n,signal_slow,
    output    signal_fast
);
reg    signal_fast_r, signal_fast_rr;
always @(posedge clk_f or negedge rst_n)begin
       if(!rst_n)begin
            signal_fast_r  <= 1'b0;
            signal_fast_rr <= 1'b0;
        end 
       else begin
            signal_fast_r  <= signal_slow;
            signal_fast_rr <= signal_fast_r;   
        end               
end
//上升沿检测
assign signal_fast = !signal_fast_rr && signal_fast_r;
/*
下降沿检测
assign signal_fast = signal_fast_rr && !signal_fast_r;
*/
endmodule

         快时钟域到慢时钟域的传输——握手传输:

        此类情况,容易出现在慢时钟域中无法采集到快时钟域的信号,可采用握手传输的方式进行跨时钟域处理。

        原理:

        1. 在快时钟域下,对输入的脉冲信号进行检测,检测到输入的脉冲信号后,输出一个高电平信号,不急于将信号拉低,保持高电平状态;
        2. 在慢时钟域下,对快时钟域下输出的高电平信号进行延迟打拍采样;
        3. 在慢时钟域下,确认得到高电平信号后,输出反馈信号给快时钟域;
        4. 快时钟域对反馈信号进行延迟打拍采样,确认接收到反馈信号,拉低输出的高电平信号;

RTL code:
module pulse_syn_fast2s
#(parameter pulse_init = 1'b0)(
	input rst_n,clk_fast,pulse_fast,clk_slow,
	output pulse_slow);
wire 	clear_n;
reg 	pulse_fast_r;
//(1)快时钟域对脉冲信号检测,并不急于将信号拉低,保持输出信号为高电平状态
always @(posedge clk_fast or negedge rst_n)begin
	if(!rst_n)
		pulse_fast_r <= pulse_init;
	else if(!clear_n)
		pulse_fast_r <= 1'b0;
	else if(pulse_fast) 
		pulse_fast_r <= 1'b1;
end
//(2)慢时钟域下对信号进行延迟打拍采样
reg [1:0] pulse_fast2s_r;
always @(posedge clk_slow or negedge rst_n)begin
	if(!rst_n)
		pulse_fast2s_r <= 2'b0;
	else 
		pulse_fast2s_r <= {pulse_fast2s_r[0],pulse_fast_r};
end
assign pulse_slow = pulse_fast2s_r[1];
//(3)快时钟域下对慢时钟域的反馈信号延迟打拍采样
reg [1:0] pulse_slow2f_r;
always @(posedge clk_fast or negedge rst_n)begin
	if(!rst_n)
		pulse_slow2f_r <= 2'b0;
	else 
		pulse_slow2f_r <= {pulse_slow2f_r[0],pulse_slow};
end
//(4)拉低快时钟域脉冲信号
assign clear_n = ~(!pulse_fast && pulse_slow2f_r[1]);

endmodule

        数据信号的传输(多bit信号)——异步FIFO:

        多位宽数据的异步传输问题,无论是从快时钟到慢时钟,还是从慢时钟到快时钟,
都可以用 FIFO 处理。

        异步FIFO系统框图:

        FIFO设计的几个关键点:

        1. FIFO空满的判断:

        增加1位标志位,因为FIFO是一种回卷式的读写,将标志位和地址位结合,可进行FIFO空满的判断;假设:读地址和写地址都指向同样的位置,即最高位相同,rdaddr==wraddr,可判断出FIFO此时的状态为读空;若读写地址的最高位不同,其余位相同,即rdaddr[N]==wraddr[N];  rdaddr[N-1:0]==wraddr[N-1:0]; 此时可判断出FIFO状态为写满;写地址比读地址大出一个FIFO深度。在读写吞吐量相同情况下,FIFO深度=写入数据个数-读出数据个数。

        FIFO满条件:                                                     

           FIFO空条件:

        2. 格雷码和二进制码的相互转换:

        因为格雷码本身的性质,即格雷码相邻两个状态之间只有单比特信号发生跳变,其余信号不变。因此,在FIFO中,地址总线可以先转换成格雷码,再进行两级触发器对格雷码地址进行同步,其误码概率与单比特信号的跨时钟域转换是一致的。

        格雷码转二进制:

bin[3]=gray[3]
bin[2]=gray[3]^gray[2]
bin[1]=gray[3]^gray[2]^gray[1]
bin[0]=gray[3]^gray[2]^gray[1]^gray[0]

RTL code:
integer i
always @(*)
	for(i=0;i<=size;i=i+1)
		bin[i] = ^(gray>>i);

        二进制转格雷码:

gray[3]=bin[3]
gray[2]=bin[2]^bin[3]
gray[1]=bin[1]^bin[2]
gray[0]=bin[0]^bin[1]
RTL code:
assign gray = (bin>>1)^bin;

        注意:先将a时钟的地址格雷码编码,再用b时钟采样格雷码地址进行两级触发器缓存,最后用b时钟对暂存的格雷码地址进行译码。

RTL code:
module async_fifo_16x16(
	//fifo_write
	wr_clk,wr_en,almost_full,full,wr_data,wr_reset,
	//fifo_read
	rd_clk,rd_en,almost_empty,empty,rd_data,rd_reset
);
/*************** parameter ************************/
parameter   ADDR_WIDTH = 4;
parameter   DATA_WIDTH = 16;
parameter 	ALMOST_FULL_GAP = 3;//离满还有 ALMOST_FULL_GAP时,almost_full有效
parameter	ALMOST_EMPTY_GAP = 3;//离空还有ALMOST_EMPTY_GAP时,almost_empty有效
parameter   FIFO_DEEP = 16;

input   wr_reset;//wr_clk 下的复位
input	wr_clk;
input   wr_en;
input   [DATA_WIDTH-1:0] wr_data;
output  almost_full;
output  full;
input 	rd_reset;
input   rd_clk;
input   rd_en;
output  almost_empty;
output  empty;
output	[DATA_WIDTH-1:0] rd_data;

/*********************** wire && reg ********************/
wire    [DATA_WIDTH-1:0] wr_addr;
wire    [DATA_WIDTH-1:0] rd_addr;
wire 	[DATA_WIDTH-1:0] data_out_temp;//FIFO读出数据
reg 	[ADDR_WIDTH:0] 	 wr_gap;//写指针与读指针之间的间隔
reg  	[ADDR_WIDTH:0]   rd_gap;//读指针与写指针之间的间隔

wire    [DATA_WIDTH-1:0] rd_data;//fifo data output
reg 					 almost_full;//fifo almost full
reg 					 full;
reg 					 almost_empty;
reg 					 empty;
reg     [ADDR_WIDTH:0]   waddr;//写地址,最高位为指针循环指示
reg 	[ADDR_WIDTH:0]   waddr_gray;//写地址格雷码
reg		[ADDR_WIDTH:0]	 waddr_gray_sync_dl;//写地址格雷码,同步到读时钟域
reg 	[ADDR_WIDTH:0]	 waddr_gray_sync;
reg 	[ADDR_WIDTH:0] 	 raddr;//读地址,最高位为指针循环指示位
reg 	[ADDR_WIDTH:0]	 raddr_gray;//读地址格雷码
reg 	[ADDR_WIDTH:0]	 raddr_gray_sync_dl;//同步到写时钟域的读地址格雷码
reg		[ADDR_WIDTH:0]	 raddr_gray_sync;
reg 	[ADDR_WIDTH:0] 	 raddr_gray2bin;//读地址的二进制码
reg		[ADDR_WIDTH:0]	 waddr_gray2bin;//写地址的二进制码
reg 					 wen;//RAM写使能
reg 					 ren;//RAM读使能

/************** 异步FIFO读/写地址的格雷码编码 ****************/
//写控制逻辑	
//RAM写使能与地址
assign wen = wr_en && (!full); 
//fifo write address generated
always @(posedge wr_clk or posedge wr_reset)
	if(wr_reset)
		waddr <= {(ADDR_WIDTH+1){1'b0}};
	else if(wen)
		waddr <= waddr + 1'b1;
assign wr_addr = waddr[ADDR_WIDTH-1:0];//写地址连接到RAM存储器
//fifo write address : bin2gray
always @(posedge wr_clk or posedge wr_reset)
	if(wr_reset)
		waddr_gray <= {(ADDR_WIDTH+1){1'b0}};
	else 
		waddr_gray <= waddr ^ {1'b0,waddr[ADDR_WIDTH:1]};//原二进制码左移一位,高位补零,再与原二进制码进行异或

//fifo read address gray sync to wr_clk
always @(posedge wr_clk or posedge wr_reset)
	if(wr_reset)begin
		raddr_gray_sync <= {(ADDR_WIDTH+1){1'b0}};
		raddr_gray_sync_dl <= {(ADDR_WIDTH+1){1'b0}};
	end
	else begin
		raddr_gray_sync <= raddr_gray;
		raddr_gray_sync_dl <= raddr_gray_sync;
	end
		
//读地址格雷码转变为二进制码
always @(*)begin
	raddr_gray2bin = {
		raddr_gray_sync_dl[4],
		raddr_gray_sync_dl[4]^raddr_gray_sync_dl[3],
		raddr_gray_sync_dl[4]^raddr_gray_sync_dl[3]^raddr_gray_sync_dl[2],
		raddr_gray_sync_dl[4]^raddr_gray_sync_dl[3]^raddr_gray_sync_dl[2]^raddr_gray_sync_dl[1],
		raddr_gray_sync_dl[4]^raddr_gray_sync_dl[3]^raddr_gray_sync_dl[2]^raddr_gray_sync_dl[1]^raddr_gray_sync_dl[0]
	};
end
//写指针与读指针间隔计算
/*wraddr和rdaddr最高位不等时,rdaddr[3:0]>wraddr[3:0],低4位直接相减即可*/
/*wraddr和rdaddr最高位相等时,rdaddr[3:0]<wraddr[3:0],wraddr减去rdaddr得到
已经写入FIFO的数值,要得到还剩多少个就要满则必须由FIFO深度减去该值*/
always @(*)begin
	if(raddr_gray2bin[4]^waddr[4])
		wr_gap = raddr_gray2bin[3:0]-waddr[3:0];
	else 
		wr_gap = FIFO_DEEP + raddr_gray2bin - waddr;
end

//almost_full 信号产生
always @(posedge wr_clk or posedge wr_reset)begin
	if(wr_reset)
		almost_full <= 1'b0;
	else begin
		if(wr_gap < ALMOST_FULL_GAP)begin
			almost_full <= 1'b1;
		end
		else 
			almost_full <= 1'b0;
	end		
end

//full 信号产生
always @(posedge wr_clk or posedge wr_reset)
	if(wr_reset)
		full <= 1'b0;
	else 
		full <= (!(|wr_gap)) || ((wr_gap==1)&wr_en);


//异步FIFO读控制逻辑
//RAM读使能
assign ren = rd_en && (!empty);
//FIFO读地址产生
always @(posedge rd_clk or posedge rd_reset)
	if(rd_reset)
		raddr <= {(ADDR_WIDTH+1){1'b0}};
	else if(ren)
		raddr <= raddr + 1'b1;
assign rd_addr = raddr[ADDR_WIDTH-1:0];//连接到FIFO内RAM存储器的读地址
//写地址:bin2gray
always @(posedge rd_clk or posedge rd_reset)
	if(rd_reset)
		raddr_gray <= {(ADDR_WIDTH+1){1'b0}};
	else 
		raddr_gray <= raddr ^ {1'b0,raddr[ADDR_WIDTH:1]};
//写地址同步到读时钟
always @(posedge rd_clk or posedge rd_reset)begin
	if(rd_reset)begin
		waddr_gray_sync <= {(ADDR_WIDTH+1){1'b0}};
		waddr_gray_sync_dl <= {(ADDR_WIDTH+1){1'b0}};
	end
	else begin
		waddr_gray_sync <= waddr_gray;
		waddr_gray_sync_dl <= waddr_gray_sync;
	end
end
//写地址格雷码变成二进制
always @(*)begin
	waddr_gray2bin = {
	waddr_gray_sync_dl[4],
	waddr_gray_sync_dl[4]^waddr_gray_sync_dl[3],
	waddr_gray_sync_dl[4]^waddr_gray_sync_dl[3]^waddr_gray_sync_dl[2],
	waddr_gray_sync_dl[4]^waddr_gray_sync_dl[3]^waddr_gray_sync_dl[2]^addr_gray_sync_dl[1],
	waddr_gray_sync_dl[4]^waddr_gray_sync_dl[3]^waddr_gray_sync_dl[2]^addr_gray_sync_dl[1]^waddr_gray_sync_dl[0]
	};
end

//读指针与写指针的间隔
always @(*)
	rd_gap = waddr_gray2bin - raddr;

//almost_empty信号产生
always @(posedge rd_clk or posedge rd_reset)
	if(rd_reset)
		almost_empty <= 1'b1; //重置时,FIFO应为空
	else begin
		if(rd_gap < ALMOST_EMPTY_GAP)
			almost_empty <= 1'b1;
		else
			almost_empty <= 1'b0;
	end
//产生empty信号
always @(posedge rd_clk or posedge rd_reset)begin
	if(rd_reset)
		empty <= 1'b1;
	else 
		empty <= (!(|rd_gap))||((rd_gap==1)&rd_en);
end

//例化FIFO内部RAM寄存器
ram_16x16 ul(
//port a
.clka (wr_clk),
.addra (wr_addr),
.dina (wr_data),
.wra(wen),
//port b
.clkb(rd_clk),
.addrb(rd_addr),
.doutb(rd_data),
.rdb(ren)
);
		
endmodule

参考资料:

《FPGA深度解析》

《硬件架构的艺术》

《菜鸟教程——跨时钟域处理》

  • 7
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值