【深入浅出玩转FPGA学习7------基于FPGA的跨时钟域信号处理】

深入浅出玩转FPGA学习7------基于FPGA的跨时钟域信号处理

基于FPGA的跨时钟域信号处理

在逻辑设计领域,只涉及单个时钟域的设计并不多,尤其对于一些复杂的应用,FPGA往往需要和多个时钟域的信号进行通信 ,异步时钟域所涉及的两个时钟之间可能存在相位差也可能没有任何频率关系,即通常所说的不同频不同相 I。
如图所示是一个跨时钟域的异步通信示意图,发送域和接收域的时钟分别是clk_a和clk_b。这两个时钟频率不同,并且存在一定的相位差。 对于接收域而言,来自发送域的信号data._a2b有可能在任何时刻变化。
在这里插入图片描述
对于上述的异步时钟域通信 ,设计者需要做特殊的处理以确保数据可靠的传输。 由于两个异步时钟域的频率关系不确定,触发器之间要求的建立时间和保持时间也无法得到保证。 如果出现建立时间或者保持时间违规,接收域将会采样到处于亚稳态的数据,那么后果可想而知。
如何有效地进行跨时钟域的信号传输呢?最基本的思想是同步,在这个基础上设计者可以利用各种手段进行通信。该笔记将要介绍的单向控制信号检测 、 专用握手信号和借助于存储器都是比较常用的设计方法。

同步设计思想

列举一个异步时钟域中出现的很典型的问题,用一个反例来说明没有足够重视异步通信会给整个设计带来什么样的后果。先从这个模块要实现的功能说起。如图所示,设计功能其实很简单,就是一个频率计,只不过FPGA除了脉冲计数外,还要响应CPU的读取控制。
在这里插入图片描述
CPU的控制总线是指一个片选信号和一个读选通信号,当二者都有效时,FPGA需要对CPU的地址总线进行译码,然后把采样脉冲值送到CPU的数据总线上。CPU读时序如图所示:
在这里插入图片描述
对于这样“简单”的功能,不少设计者可能会给出类似下面的以组合逻辑为主的实现方式。

input clk;
input rst_n;
input pulse;
input cs_n;
input rd_n;
input[3:0] addr_bus;
output reg[15:0] data_bus;
reg[15:0] counter;
always @(posedge  pulse or negedge rst_n)
		if(!rst_n) counter <= 16'd0;
		else if(pulse) counter <= counter +1'b1;
wire dsp_cs =cs_n & rs_n;
always @ (dsp_cs or addr_bus)
		if(dsp_cs) data_bus <= 16'hzzzz;
		esle begin
				case(addr_bus)
						4'h0: data_bus <= counter;
						4'h1: ....;
						....
						default: ;
						endcase
		end

乍一看,可能认为这个代码也没什么问题,功能似乎都实现了,而且还认为这个代码挺简捷的,也不需要耗费多少逻辑资源就能实现。但是,对于这种时钟满天飞的设计,存在着诸多亚稳态危害爆发的可能。脉冲信号和由CPU控制总线产生的选通信号是来自两个异步时钟域的信号,它们作为内部时钟信号时,如果同一时刻出现一个时钟在写寄存器counter,另一个时钟在读寄存器counter,那么,很明显存在着发生冲突的可能。换句话说,如果寄存器正处于改变状态(被写)时有读取信号产生了,问题就会随之而来,如图所示:
在这里插入图片描述
脉冲信号pulse与CPU读选通信号cpu_cs是异步信号,pulse何时出现上升沿和cpu_cs何时出现下降沿是不可控的。如果很不幸地它们一起触发了,那么,结果可想而知。计数器counter[15:0]正在加1,这个自增地过程还在进行中,CPU数据总线data_bus[15:0]来读取counter[15:0],那么到底 读取的值是自增之前的值还是自增之后的值又或者是其他的值呢?
在这里插入图片描述

如图所示,这是一个计数器的近似模型。当计数器自增1的时候,如果最低为0,那么自增的结果只会使最低位翻转;但是当最低为1,自增1的后果除了使最低为翻转,还有可能使其他任何位翻转,比如4‘b1111自增1的后果会使4个位都翻转。由于每个位之间从发生翻转到翻转完成都需要经过一段逻辑延时和走线延时,对于一个16位的计数器,要想使这16位寄存器的翻转时间一致,那是不可能做到的,所以,对于之前的设计中出现了如图所示的冲突时,被读取的脉冲值很可能是完全错误的。
上面的代码是最典型的组合逻辑实现方式,是很不可行的。 也许很多朋友会提出异议,也许还会提出很多类似的组合逻辑方案,但是.如果没有同步设计的思想,不把这两个异步时钟域的信号同步到一个时钟域里进行处理,冲突的问题是无法得到根本解决的。
那么 ,这个设计该如何同步呢?它的同步处理设计思想如图2.44所示 。 先是使用脉冲检测法把脉冲信号与系统时钟信号clk同步,然后依然使用脉冲检测法得到一个系统时钟宽度的使能脉冲作为数据锁存信号,也就将CPU的控制信号和系统时钟信号 clk同步了 。 如此处理后,两个异步时钟域的信号就不存在任何读/写冲突的情况了。
在这里插入图片描述
在 FPGA 开发过程中 ,同步思想是贯穿于整个设计始终的。 同步设计不仅可以稳定 、 可
靠地实现既定功能,也可以大大提高设计的实现、测试 、调试及维护效率。 同步设计的最大好处可以归纳为以下5点:
1.有效避免异步信号通信产生的冲突。
2.便于时序约束 、时序分析及时序仿其.
3.便于板级时序问题定位。
4.有利于同器件间的代码移植,减少重复设计。
5.最小化器件升级对同一工程带来的影响 。

单项控制信号检测

下面介绍一下一个基于单片机的应用的项目,11.0592Mhz的51单片机读/写时序图如图所示:
在这里插入图片描述
实际波形与图 2.45 相差无几,地址总线这里没有给出 ,不过地址总线一般会早于片选CS稳定下来 ,并且晚于片选信号CS撤销(这个说法不是绝对的,但是至少对本文讨论的应用是这样的)。
FPGA 是作为 MCU 的从机, 即模拟 MCU 的扩展 RAM。 MCU 若发出写选通 , FPGA 就得在数据稳定于数据总线时将其锁存起来;MCU 若发出读选通, FPGA 就要在 MCU 锁存数 据的建立时间之前把数据放到数据总线上 , 并且直到 MCU 锁存数据的保持时间结束后才能将数据撤销。
其实这个 MCU 的读/写时序时间相对还是很充裕的,因为该设计中的 FPGA 使用了50 MHz的晶振,所以一个很基本的思想就是要把 MCU 端的信号同步到 FPGA 的时钟域上 ,达到异步信号的同步处理。

input clk;    //50Mhz
input rst_n;  //复位信号,低有效
input mcu_cs_n;   //MUC片选信号,低有效
input mcu_wr_n;   //MCU写信号,低有效
input[3:0] mcu_addr;  //MCU地址总线
input[7:0] mcu_db;   //MCU数据总线
reg[3:0] mcu_addr_r;  //mcu_addr 锁存寄存器
reg[7:0] mcu_db_r;     //mcu_db锁存寄存器
//mcu_cs_n和mcu_wr_n同时拉低时wr_state拉低,表示片选并写选通
wire wr_state = mcu_cs_n || mcu_wr_n;  //写状态标志位,写选通时拉低
always @ (posedge clk or negedge rst_n)
		if(!rst_n) begin
				mcu_addr_r <= 4'h0;
				mcu_db_r <= 8'h00;
				end
		else if(!wr_state) begin
				mcu_addr_r <= mcu_addr;   //mcu_addr 锁存寄存器
				mcu_db_r <= mcu_db;     //mcu_db 锁存寄存器
				end
wire pos_wr;      //MCU写状态上升沿标志位
reg wr1,wr2;    //MCU写状态寄存器
always @ (posedge clk or negedge rst_n)
		if(!rst_n) begin
				wr1 <= 1'b1;
				wr2 <= 1'b1;
				end
		else begin
				wr1 <= wr_state;
				wr2 <= wr1;
				end
				assign pos_wr = ~ wr2 && wr1;   //写选通信号上升沿,pos_wr拉高一个时钟周期

上面的代码就是将基于MCU发出的异步时序做一种同步处理。 当然了,这种处理是基 于特定的应用。 MCU写选通撤销时,pos_wr信号(使用了脉冲边沿检测方法处理)会拉高一 个时钟周期,则可以利用此信号作为后续处理的状态机中的一个指示信号,然后对已经锁存在FPGA内部相应寄存器里的地址总线和数据总线进行处理。
另外,mcu_addr_r和mcu_db_r的锁存为什么要在wr_state为低电平时进行?wr_state拉低期间即MCU片选和写选通同时有效期间,数据/地址总线一定是稳定的,而为了有更充足的数据建立时间,比较常见的做法是用mcu_wr_n的上升 沿锁存数据,而如果用诸如posedge mcu_ wr_n来做触发锁存数据/地址,那就很容易出现异 步冲突的问题(这个问题的危害上一节已经提出来了),达不到同步的效果,所以采用一个电平 倌号作为使能信号则更加稳妥。换个角度看,无非是wr_state上升沿0-20ns时间内都有可能是最后锁存下来的数据,这对于充足的MCU写时序来说是绰绰有余了。理论上来说, wr_stata是一个总线使能信号,应该要做至少一级同步再使用更稳妥一些,但是这里MCU的 时序较充裕,即便是wr_stata没有进行同步处理,出现了在wr_state的一个亚稳态内锁存数据的情况,那么此时的数据/地址总线的数据也不会受到影响,该是什么值仍是什么值。不同 的应用中往往有允许非常规处理的时候,就像时序分析中的时序例外一样。

专用握手信号

图2.46是一个基本的握手通信原理。 所谓握手,即通信双方使用了专用控制信号进行状态指示 。 这个控制信号既有发送域给接收域的,也有接收域给发送域的,有别于前面的单向控制信号检测方式。
在这里插入图片描述
使用握手协议方式处理跨时钟域数据传输时,只需要对双方的握手信号(req和ack)分别使用脉冲检测方法进行同步。在具体实现中.假设req 、ack 、data 总线在初始化时都处于无效状态,发送域先把数据放人总线,随后发送有效的req信号给接收域;接收域在检测到有效的req信号后锁存数据总线,然后回送一个有效的ack信号表示读取完成应答;发送域在检测到有效ack信号后撤销当前的req信号,接收域在检测到req撤销后也相应撤销ack信号,此时完成一次正常握手通信。此后,发送域可以继续开始下一次握手通信.如此循环。该方式能够使接收到的数据稳定可靠,有效地避免了亚稳态的出现。但控制信号握手检测会消耗通信双方较多的时间。以上所述的通信流程如图2.47所示。
在这里插入图片描述
下面通过一个简单的工程代码及其仿真测试进一步加深大家对基本握手协议的认识。

module handshack(
						clk,rst_n,req,datain,ack,dataout
						);
						input clk;   //50MHz系统时钟频率
						input rst_n;     //低电平复位信号
						input req;    //请求信号,高电平有效
						input[7:0] datain;    //输入数据
						output ack;    //应答信号,高电平有效
						output[7:0] dataout;   //输出数据,主要用于观察是否和输入一致
						//req上升沿检测
						reg reqr1,reqr2,reqr3;
						always @ (posedge clk or negedge rst_n)
								if(!rst_n) begin
										reqr1 <= 1'b1;
										reqr2 <= 1'b1;
										reqr3 <= 1'b1;
									end
								else begin
										reqr1 <= req;
										reqr2 <= reqr1;
										reqr3 <= reqr2;
									end
							//pos_req2比pos_req1延后一个时钟周期,确保数据被稳定锁存
							wire pos_req1 = reqr1 &~ reqr2;   //req上升沿标志位,高有效一个时钟周期
							wire pos_req2 = reqr2 &~ reqr3;  //req上升沿标志位,高有效一个时钟周期
							//数据锁存
							reg[7:0] dataoutr;
							always @ (posedge clk or negedge rst_n)
										if(!rst_n) ackr <= 1'b0;
										else if(pos_req2) ackr <= 1'b1;
										else if(!req) ackr <= 1'b0;
							assign ack = ackr;
							endmodule

该实例的 Verilog 代码模拟了握手通信的接收域,其仿真波形如图 2.48 所示 。 在发送域请求信号(req)有效的若干个时钟周期后 , 先是数据 (datain)被有效锁存在输出 (dataout) 上 .然后接收域的应答信号(ack)也处于有效状态,此后发送域撤销请求信号,接收域也跟若撤销 了应答信号,由此完成一次通信。
在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周猿猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值