FPGA跨时域之握手信号

FPG跨时域之握手信号-简化篇

------热爱生活 热爱时钟 热爱逻辑------

在面试的时候面试官谈到如何解决跨时域的问题,作为一个初学者只知道用异步FIFO来实现同步。然后在博客里看了一些文章,有一些自己的理解记在这里,肯定有不妥之处,仅仅写一写自己的想法。

一、快时域到慢时域-Freq=100m/Freq=50m

复杂的握手协议后期再工作中继续学习会给出自己的理解,目前水平有限,就只有写出简单的握手协议。下图是自己对握手协议的理解:
在这里插入图片描述
图1.1 握手协议模型图
如上图所示,两个时域之间通过请求和应答信号线进行握手,时域A发送请求发送数据信号req,同时是准备好数据;时域B接收到时域A发送的请求信号后,回应一个应答信号ack,同时将数据接收进行寄存;时域A接收到应答信号后重新发送请求信号req,进行第二个数据传输,依次直到完成时域A数据发送完成。
但是要注意程序设计,不正确的程序设计将会发生数据漏取(快时域到慢时域)或者插入数据(慢时域到快时域)。这种握手信号设计是以牺牲时钟为代价而进行的设计,因为跨时域涉及到亚稳态,亚稳态的重要性以及解决方法在其他博文里有详细的解释,请大家自行搜索,本文在接收数据时采用了两级寄存,以保证数据传输的准确性。下面将详细叙述我的程序设计方案(Verilog HDL)。

二、快时域发送数据,慢时域接收数据设计方案

1、设计方案

在本实际案列中使用100mHz频率作为快时域A,A时域控制ROM地址变化,读取ROM的数据,发送到慢时域B,B时域使用50mHz接收时域A的数据。
在这里插入图片描述
图2.1 架构设计

2、程序设计

A时域检测应答信号 ack为低电平时拉高发送请求信号req(高有效),发送数据,A时域检测到应答信号ack上升沿到来时则拉低请求信号req。
B时域检测到 A时域 req请求信号上升沿到来时拉高应答信号ack,接收数据。
设计顶层模块进行仿真测试。

3、程序清单

  • 1).顶层设计
//----跨时域顶层  仿真使用----
module time_top(
						input	clk,		//输入系统时钟50mHz
						input	rst_n,	
						output	[7:0]data_o,
						);
						wire clk_100m;
						wire	rst_en;
						wire	ack1;
						wire	[7:0]data_r;
						wire	req1;
						wire	[7:0]addr;
						wire [7:0]data;
						
						
//----10mHz频率产生----
my_pll inst_my_pll(
								.areset(!rst_n),
								.inclk0(clk),
								.c0(clk_100m),
								.locked(rst_en)
								);
//----ROM 模块 ----								
my_rom inst_my_rom(
								.address(addr),
								.clock(clk_100m),
								.q(data)
								);
//----快时域A----								
transmit_100m transimt(
								.clk(clk_100m),
								.rst_n(rst_en),
								.ack(ack1),
								.data_in(data),
								.data_out(data_r),
								.req(req1),
								.addr(addr)
										);
//----慢时域B----										
receive_50m	recive(
							.clk(clk),
							.rst_n(rst_n),
							.req(req1),
							.data_in(data_r),
							.data_out(data_o),
							.ack(ack1)
							);	
endmodule
  • 2).A时域程序设计-发送数据,数据源来自ROM.
//---快时域 100mHz  发送ROM中的数数据----
module transmit_100m(
							input	clk,			//100mHz
							input	rst_n,			//低电平复位
							input	ack,			//应答信号
							input	[7:0]data_in,	//数据收入
							output	[7:0]data_out,	//输出数据
							output	reg  req,			//发送请求信号
							output	reg	 [7:0]addr			//ROM地址
							);
//-----接收应答信号并寄存----
reg	ack_r1;
reg	ack_r2;
reg	ack_r3;
always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)begin
		ack_r1<=0;
		ack_r2<=0;
		ack_r3<=0;
	end
	else begin
		ack_r1<=ack;
		ack_r2<=ack_r1;
		ack_r3<=ack_r2;
	end
end
//----两级寄存,原理请参考其他博文-----
wire 	 pos_ack1;
wire	 pos_ack2;
assign pos_ack1=ack_r1	&	(~ack_r2);	//检测ack信号上升沿
assign pos_ack2=ack_r2	&	(~ack_r3);	//ack信号上升沿延时一个周期

//----产生发送数据请求信号-----
always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)begin
		req<=0;
	end
	else begin
		if(ack==0)begin		//应答信号为低电平时表示数据总线空闲,可以接收数据
			req<=1;		//如果外部控制 req,可引入使能信号,使能信号有效时req拉高
		end
		else if(pos_ack2)begin	//检测到ack应答信号上升沿,请求信号拉低,停止发送数据
			req<=0;
		end
	end
end
//----请求信号下降沿检测,当请求信号下降沿到来时,ROM地址加一,为下一次发送数据做好准备----
reg	req1;
reg	req2;
wire	neg_req;
always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)begin
		req1<=0;
		req2<=0;
	end
	else begin
		req1<=req;
		req2<=req1;
	end
end
assign	neg_req=req2&(~req1);	//检测请求信号req下降沿

//----发送数据状态设计----
reg	[1:0]state;
reg	[7:0]data_r;	//数据寄存器,对输入数据进行寄存
parameter idle=2'd0;	
parameter transimt=2'd1;
always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)begin
		state<=idle;
		addr<=8'd0;
	end
	else begin
				case(state)
				idle	:	begin
				if(req)begin	//发送数据请求信号到来,则接收输入数据
				state<=transimt;
			  data_r<=data_in;
				end
			 else begin
				state<=idle;
			end
		end
		transimt		:	begin
				if(neg_req)begin	//请求信号下降沿到来,地址加1,为下一次发送数据做好准备
				addr<=addr+1;
				state<=idle;
				end
				else begin
				state<=transimt;
				end
			end			
		default :	state<=idle;		
		endcase
	end
end
assign data_out=data_r;	//将寄存器的数据输出
endmodule

  • 3).B时域接收数据-50mHz接收数据
//----慢时域接收数据----
module receive_50m(
							input   clk,//50m
							input	rst_n,
							input	req,
							input	[7:0]data_in,
							
							output	 [7:0]data_out,
							output	reg ack
							);
//----寄存数据请求信号----
reg req_r1;
reg req_r2;
reg req_r3;

always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)begin
		req_r1<=0;
		req_r2<=0;
		req_r3<=0;
	end
	else begin
		req_r1<=req;
		req_r2<=req_r1;
		req_r3<=req_r2;
	end
end
wire pos_req1;
wire	pos_req2;
assign pos_req1=req_r1	&	(~req_r2);//检测req信号上升沿
assign pos_req2=req_r2	&	(~req_r3);//两级寄存
//----进行接收数据----
reg	[7:0]data_r;//数据寄存
always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)begin
		data_r<=0;
	end
	else if(pos_req1)
			data_r<=data_in;
end
//----应答信号产生----
always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)begin
		ack<=0;
	end
	else begin
		if(pos_req2)		//检测到 req信号的上升沿,则进行数据接收
			ack<=1;
		else if(!req)		//当检测到 req信号拉底后,应答信号拉低
			ack<=0;
	end
end
assign data_out=data_r;
endmodule

2、仿真测试

  • 1).顶层波形
    在这里插入图片描述
  • 2).快时域A仿真波形
    在这里插入图片描述
  • 3).慢时域B仿真波形
    在这里插入图片描述
    通过波形仿真可以发现该设计满足设计需求,从快时域发送数据到慢时域,数据没有发生遗漏。
    注:本设计尚未设计时序约束。
    慢时域到快时域的设计在下一章总结,涉及到更多的时域就需要仲裁模块,这些也到后面再说。学识有限,一起努力,共同奋进。转载请注明出处。

------热爱生活 热爱时钟 热爱逻辑------

  • 27
    点赞
  • 215
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值