跨时钟域方法总结
https://www.docin.com/p-1715858862.html
异步FIFO
https://blog.youkuaiyun.com/darknessdarkness/article/details/104726798
一、结绳法(一)
之前参考文章有错误,修改之后电路结果才和仿真对应。
单信号的跨时钟域处理通常有,
- 两级寄存器串联。
- 脉冲同步器。
- 结绳法。
- 采用握手。
1.数据驱动结绳
利用数据的边沿做时钟,可将脉冲延长,直到采集到数据,然后复位。
这里需要注意的是,clkB域需要等待三个clkB才会在最后一个寄存器输出并完成输入端的复位。所以如果Din_clkA变化较快,即持续时间小于三个clkB(Din_clkA的频率大于clkB的1/3),那么Din_clkA的变化将无法采样到。
该结绳法结构如图
代码:
module data_clk(
input clk2,
input rst2,
input data,
output pluse_out
);
//-----------------clk1---
reg pluse_reg1;
reg clk2_r1,clk2_r2,clk2_r3;
wire rst1;
assign rst1 = clk2_r3 & ~data;
always @ (posedge data or posedge rst1)
if(rst1)
pluse_reg1 <= 1'b0;
else
pluse_reg1 <= 1'b1;
always@(posedge clk2 or negedge rst2 )
if(rst2)
{clk2_r1,clk2_r2,clk2_r3}<= 3'b000;
else
{clk2_r1,clk2_r2,clk2_r3} <= {pluse_reg1,clk2_r1,clk2_r2};
assign pluse_out = clk2_r2&&~clk2_r3;
endmodule
测试文件:
`timescale 1ns / 1ps
module txt();
reg clk2;
reg rst2;
reg data;
wire pluse_out;
initial
begin
clk2=0;
rst2 = 1;
data =0;
#100;
rst2= 0;
// data =0;
#100
data =0;
#200
data =1;
#10
data =0;
#200
data =1;
#10
data =0;
#80
data =1;
#10
data =0;
end
always #10 clk2 = ~clk2;
data_clk pluse_data_inst(
.clk2(clk2),
.rst2(rst2),
.data(data),
.pluse_out(pluse_out)
);
endmodule
仿真电路图:
2.脉冲同步器
由于脉冲在快时钟域传递到慢时钟域时,慢时钟有时无法采样的信号,根据奈奎是特采样定理。
因此需要对信号进行处理,可以让慢信号采样到。
`timescale 1ns / 1ps
module pluse_sync(
input src_clk,
input src_rst_n,
input s_pluse,
input des_clk,
input des_rst_n,
output des_pluse
);
/*//**********************synchronization req form src***********
reg req;
reg d_req1,d_req2;
always @(posedge des_clk ,negedge des_rst_n)
if(!des_rst_n)
{d_req1,d_req2} <=2'b0;
else
{d_req1,d_req2} <={req,d_req1};
//**************************************************************
//------------------------------synchronization ack form destination------------
reg ack;
reg d2s_ack1,d2sack2;
always @(posedge src_clk ,negedge src_rst_n)
if(!src_rst_n)
{d2s_ack1,d2sack2} <=2'b0;
else
{d2s_ack1,d2sack2} <={ack,d2s_ack1};
//*******************************************/
reg src_pluse;
always @ (posedge src_clk or negedge src_rst_n)
if(!src_rst_n)
src_pluse <= 1'b0;
else if(s_pluse)
src_pluse <= ~src_pluse;
reg d_reg1,d_reg2;
always @ (posedge des_clk or negedge des_rst_n)
if(!des_rst_n)
{d_reg1,d_reg2} <= 2'b00;
else
{d_reg1,d_reg2} <= {src_pluse,d_reg1};
assign des_pluse = (d_reg1 != d_reg2) ? 1'b1:1'b0;
endmodule
`timescale 1ns / 1ps
module tset();
reg src_clk;
reg src_rst_n;
reg s_pluse;
reg des_clk;
reg des_rst_n;
wire des_pluse;
//wire syn_fail;
initial
begin
src_clk =0;
des_clk =0;
src_rst_n=0;
des_rst_n =0;
s_pluse =0;
#40
src_rst_n=1;
des_rst_n =1;
#60
s_pluse =0;
#20
s_pluse =1;
#20
s_pluse =0;
#140
s_pluse =1;
#20
s_pluse=0;
#100
//#20
s_pluse =1;
#20
s_pluse =0;
#20
s_pluse =1;
#20
s_pluse =0;
end
always #10 src_clk =~src_clk;
always #5 des_clk =~des_clk;
pluse_sync pluse_sync_inst(
.src_clk(src_clk),
.src_rst_n(src_rst_n),
.s_pluse(s_pluse),
.des_clk(des_clk),
.des_rst_n(des_rst_n),
.des_pluse(des_pluse)
// .syn_fail
);
endmodule
二、结绳法(二)
结绳法1这种结绳法的原理是,数据作为时钟,即当数据有上升沿(0->1)时,寄存器1的输出将会稳定在高电平,此时等待clkB采样,当clkB完成采样后,寄存器4会输出高电平,若此时数据为高电平,那么即可完成复位,开始下一次采样等待。这种设计数据来临(即上升沿)时,clkB域需要等待3个clkB才会在寄存器4输出并完成输入端的复位,所以数据如果变化较快即持续时间短于3个clkB,也就是clkA频率大于clkB的1/3,那么这时数据的变化将无法被采样到,因为clkB域需要3个clkB才能完成采样,并且此时数据必须是低电平才能复位,并且复位后的0也要延迟3个clkB才能到达输出端。即这种结绳法慢时钟采样快时钟信号,数据变化至少需要间隔3个慢时钟域的时钟。
输入脉冲需要是单周期脉冲,否则无法进行结绳。
结绳法2又可分为两种电路,在快时钟域->慢时钟域时如果:
fd>=fs∗(fp+1.25)
其中f_p为寄存器级数即源时钟域到目的输出需要的寄存器数量。如果目的时钟满足上面的要求即可以在一个时钟周期内采样到信号。因此可以不需要握手信号,这样可以减少电路设计的延时以及不必要的电路逻辑节省资源。
1.不需要握手信号的结绳法电路结构
代码:
module jieshen_reqnoack(
input clk1,
input clk2,
input rst_1,rst_2,
input pulse_1,
output pulse_2
);
reg reg_clk1;
always@(posedge clk1 or negedge rst_1)begin
if(!rst_1)
reg_clk1<=0;
else if(pulse_1)
reg_clk1<=~reg_clk1;
end
reg reg1_clk2,reg2_clk2,reg3_clk2;
always@(posedge clk2 or negedge rst_2)begin
if(!rst_2)begin
{reg3_clk2,reg2_clk2,reg1_clk2}<=3'b000;
end
else
{reg3_clk2,reg2_clk2,reg1_clk2}<={reg2_clk2,reg1_clk2,reg_clk1};
end
assign pulse_2=reg2_clk2 ^ reg3_clk2;
endmodule
测试文件:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/07/23 17:03:46
// Design Name:
// Module Name: tb_jieshen_reqnoack
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module tb_jieshen_reqnoack;
reg clk1,clk2;
reg rst_1,rst_2;
reg pulse_1;
wire pulse_2;
initial
begin
clk1=0;
clk2=0;
pulse_1=0;
rst_1=0;
rst_2=0;
#100 rst_1=1;
rst_2=1;
#400 pulse_1=1;
#100 pulse_1=0;
#600 pulse_1=1;
#100 pulse_1=0;
#600 pulse_1=1;
#100 pulse_1=0;
#600 pulse_1=1;
#100 pulse_1=0;
end
always #50 clk1=~clk1;
always #100 clk2=~clk2;
jieshen_reqnoack u_module(
.clk1(clk1),
.clk2(clk2),
.rst_1(rst_1),
.rst_2(rst_2),
.pulse_1(pulse_1),
.pulse_2(pulse_2)
);
endmodule
仿真波形:
2.握手协议结绳法
结绳法可以解决快时钟向慢时钟过渡的问题,适用范围广,但是实现较为复杂,且效率不高
`timescale 1ns / 1ps
module JS_ReqAck(
input clk1,clk2,
input rst1,rst2,//active state H
input req_clk1,
output ACK_clk1,
output pluse
);
//*************req*****************************
//---------------clk1 domain-------------
//-------------_clk1--------
reg reg_clk1;
always @ (posedge clk1 or posedge rst1)
if(rst1)
reg_clk1 <= 1'b0;
else if(req_clk1)
reg_clk1 <= ~reg_clk1;
//---------------clk2 domain-----------------
reg reg1_clk2,reg2_clk2,reg3_clk2;
always @(posedge clk2 or posedge rst2)
if(rst2)
{reg1_clk2,reg2_clk2,reg3_clk2} <= 3'b000;
else
{reg1_clk2,reg2_clk2,reg3_clk2} <= {reg_clk1,reg1_clk2,reg2_clk2};
//***********************req end*******************************
//************************ACK*****************************
//---------------clk2 domain-------------
//-------------_clk2--------
reg reg4_clk2;
always @ (posedge clk2 or posedge rst2)
if(rst2)
reg4_clk2 <= 1'b0;
else if(pluse)
reg4_clk2 <= ~reg4_clk2;
//---------------clk1 domain-----------------
reg reg1_clk1,reg2_clk1,reg3_clk1;
always @(posedge clk1 or posedge rst1)
if(rst1)
{reg1_clk1,reg2_clk1,reg3_clk1} <= 3'b000;
else
{reg1_clk1,reg2_clk1,reg3_clk1} <= {reg4_clk2,reg1_clk1,reg2_clk1};
//------output-------------
assign pluse = reg2_clk2 ^ reg3_clk2;
assign ACK_clk1 = reg2_clk1 ^ reg3_clk1;
endmodule
测试文件:
`timescale 1ns / 1ps
module text();
reg clk1, clk2;
reg rst1,rst2;
reg req_clk1;
wire pluse;
wire ACK_clk1;
initial
begin
clk2=0;
clk1=0;
rst2 = 1;
rst1 = 1;
req_clk1=0;
#20;
rst1= 0;
#20;
rst2= 0;
#130
req_clk1=0;
#20
req_clk1=1;
#20
req_clk1=0;
#200
req_clk1=1;
#20
req_clk1=0;
#60
req_clk1=1;
#20
req_clk1=0;
end
always #10 clk1 = ~clk1;
always #25 clk2 = ~clk2;
JS_ReqAck JS_ReqnoAck(
.clk1(clk1),
.rst1(rst1),
.clk2(clk2),
.rst2(rst2),
.req_clk1(req_clk1),
.pluse(pluse),
.ACK_clk1(ACK_clk1)
);
endmodule
仿真图片
三、脉冲展宽法