跨时钟域握手CDC

来自牛客网上的题,是比较简单的。

主机Master(driver)的行为:复位后,计数五个时钟周期后,准备第一个数据要发送,向从机发出request信号,然后等待从机接收,从机接收后会拉高ack,反馈应答信号到主机,主机接收后撤掉req信号,并且等待五个时钟周期后,发下一个数据...

从机slave(receiver)行为:复位后,ack为0,等待接收数据,当接收到主机发送请求信号时,直接接收数据并将ack拉高,当主机撤回req后再将data_ack置零。(确认主机知道已经接收数据了。如果只拉高ack一个时钟周期,下个时钟周期ack拉低,那么有可能主机因为时钟较慢的原因没有检测到响应信号,因此主机就不会撤掉req,而一直在发上一个数据,直到被接收才会发下一个。)

主机,驱动。几个关键点:

(1)从机的应答信号需要同步到主机的时钟域,并且进行上升沿检测,上升沿检测的目的是,更换数据,因为两次发送数据之间只能更换一次数据,不能在ack保持的每个时钟周期都更换数据,会造成中途丢失数据。因此只能在ack拉高的第一个周期检测出上升沿,并换掉数据data,变成下一个要发送的数据。

(2)计数器Cnt,计数器cnt必须在无应答的时候进行计数,无应答时计数到5个时钟周期,发送数据请求从机响应,等从机接收数据,主机接收到响应信号后再把请求撤出。

`timescale 1ns/1ps 

module data_driver(Clk_a,Rst_n,data_ack,data,data_req);

input Clk_a,Rst_n,data_ack;

output reg [3:0] data;
output reg data_req;


reg data_ack_s0,data_ack_s1,data_ack_s2;
reg [2:0] cnt ;

always@(posedge Clk_a,negedge Rst_n) begin
if(~Rst_n) begin
	data_ack_s0 <= 0;
	data_ack_s1 <= 0;
	data_ack_s2 <= 0;
	end
else begin
	data_ack_s0 <= data_ack;
	data_ack_s1 <= data_ack_s0;
	data_ack_s2 <= data_ack_s1;
	end
end

always@(posedge Clk_a,negedge Rst_n) begin
if(~Rst_n)
	cnt <= 0;
else if (~data_ack_s1) begin
	if(cnt == 3'd4)
		cnt <= 0;
	else
		cnt <= cnt + 'b1;
end
else
	cnt <= 0;
end

always@(posedge Clk_a,negedge Rst_n) begin
if(~Rst_n)
	data <= 0;
else if (~data_ack_s2&data_ack_s1 ) begin
	if(data==4'd7)
		data <= 0;
	else
		data <= data + 1;
end
else
	data <= data;
end

always@(posedge Clk_a,negedge Rst_n) begin
if(~Rst_n)
	data_req <= 0;
else if (cnt==3'd4)
	data_req <= 1;
else if (data_ack_s1)
	data_req <= 0;
end


endmodule

从机行为

(1)对主机发来的发送请求进行同步。当检测到发送请求且此时没有响应上一个信号,将数据接收。(这里为什么必须要检测没有响应信号ack,因为跨时钟,如果只是检测发送请求,此时有可能之前的响应信号还没有撤除,会再次接收数据,此时接收到的数据可能是主机更换了的数据,数据被提前接收了,增加了不必要的接收次数)

(2)响应信号的产生,当准备接收数据时就在下一个周期将ack信号拉高。并且没有撤销发送请求时一直拉高,直到撤销请求后,表示主机已经知道从机正确接收了数据。主机撤销请求,从机此时撤销响应。

`timescale 1ns/1ps


module data_receiver(Clk_b,Rst_n,ack,data_req,data,data_o);

input Clk_b,Rst_n;
input data_req;

output reg ack;
input [3:0] data;
output reg [3:0] data_o;

reg data_req_s0,data_req_s1;

always@(posedge Clk_b,negedge Rst_n ) begin 
if(~Rst_n)
	ack <= 0;
else if (data_req_s1)
	ack <= 1;
else 
	ack <= 0;
end

always@(posedge Clk_b,negedge Rst_n) begin
if(~Rst_n)
	data_o <= 0;
else if (data_req_s1&~ack)
	data_o <= data;
end

always@(posedge Clk_b,negedge Rst_n) begin
if(~Rst_n) 
	begin
	data_req_s0 <= 0;
	data_req_s1 <= 0;
	end
else
	begin
	data_req_s0 <= data_req;
	data_req_s1 <= data_req_s0;
end

end



endmodule

仿真

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值