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仿真波形
通过波形仿真可以发现该设计满足设计需求,从快时域发送数据到慢时域,数据没有发生遗漏。
注:本设计尚未设计时序约束。
慢时域到快时域的设计在下一章总结,涉及到更多的时域就需要仲裁模块,这些也到后面再说。学识有限,一起努力,共同奋进。转载请注明出处。
PS:想在西安找一份FPGA工作,有哪位大神可以给个灯塔吗?鄙人毕业两年,一直从事功率器件封测工作,从2018月7月至今一直在学习FPGA,目前已经熟练掌握IIC/UART/SPI/VGA/USB2.0等接口协议,正在学习SDRAM、DDR2/3…唉,这点经历也基本可以说没有,继续努力吧,加油!这个ps到十一月十五就自动失效吧
------热爱生活 热爱时钟 热爱逻辑------