跨时钟域信号处理

感谢作者的详细讲解,尊重原创。原文转载自:https://blog.csdn.net/skyplain1984/article/details/54782968
https://www.cnblogs.com/hfyfpga/p/4281369.html

一、慢速多周期信号

Signal-in为clkA时钟域多周期信号。此时只需要用clkB打两拍即可。
module Signal_CrossDomain(
    input rst_p;
    input clkA,   // we actually don't need clkA in that example, but it is here for completeness as we'll need it in further examples
    input SignalIn_clkA,
    input clkB,
    output SignalOut_clkB
);

// We use a two-stages shift-register to synchronize SignalIn_clkA to the clkB clock domain
reg [1:0] SyncA_clkB;

always @(posedge clkB) begin
if(rst_p == 1’b1)
SyncA_clkB[0] <= 1’
b0;
else
SyncA_clkB[0] <= SignalIn_clkA; // notice that we use clkB
end

always @(posedge clkB) begin
if(rst_p == 1’b1)
SyncA_clkB[1] <= 1’
b0;
else
SyncA_clkB[1] <= SyncA_clkB[0]; // notice that we use clkB
end

assign SignalOut_clkB = SyncA_clkB[1]; // new signal synchronized to (=ready to be used in) clkB domain

endmodule


二、单周期信号


FlagIn(代码里表示为FlagIn_clkA)此时为clkA时钟域产生的单周期信号。
module Flag_CrossDomain(
    input rst_p;
    input clkA,
    input FlagIn_clkA,   // this is a one-clock pulse from the clkA domain
    input clkB,
    output FlagOut_clkB   // from which we generate a one-clock pulse in clkB domain
);

reg FlagToggle_clkA = 1’b0;
reg [2:0] SyncA_clkB = 3’
b000;

always @(posedge clkA) bein
if(rst_p == 1’b1)
FlagToggle_clkA <= 1’
b0;
else
FlagToggle_clkA <= FlagToggle_clkA ^ FlagIn_clkA; // when flag is asserted, this signal toggles (clkA domain)
end

always @(posedge clkB) begin
if(rst_p == 1’b1)
SyncA_clkB <= 3’
b000;
else
SyncA_clkB <= {SyncA_clkB[1:0], FlagToggle_clkA}; // now we cross the clock domains
end

assign FlagOut_clkB = (SyncA_clkB[2] ^ SyncA_clkB[1]); // and create the clkB flag


如果clkA端需要知道clkB端是否接收到了信号,可以添加一个busy信号

module Flag_CrossDomain(
	input rst_p,
    input clkA,
    input FlagIn_clkA,   // this is a one-clock pulse from the clkA domain
    output Busy_clkA,
	input clkB,
    output FlagOut_clkB   // from which we generate a one-clock pulse in clkB domain
);

reg FlagToggle_clkA;
reg [2:0] SyncA_clkB;
reg [1:0] SyncB_clkA;

always @(posedge clkA)begin
if(rst_p == 1’b1)
FlagToggle_clkA <= 1’
b0;
else
FlagToggle_clkA <= FlagToggle_clkA ^ (FlagIn_clkA & ~Busy_clkA);
end

always @(posedge clkB)begin
if(rst_p == 1’b1)
SyncA_clkB <= 3’
b000;
else
SyncA_clkB <= {SyncA_clkB[1:0], FlagToggle_clkA};
end

always @(posedge clkA)begin
if(rst_p == 1’b1)
SyncB_clkA <= 2’
b00;
else
SyncB_clkA <= {SyncB_clkA[0], SyncA_clkB[2]};
end

assign FlagOut_clkB = (SyncA_clkB[2] ^ SyncA_clkB[1]);
assign Busy_clkA = FlagToggle_clkA ^ SyncB_clkA[1];

endmodule

三、Task

如果clkA时钟域中有一个任务需要在clkB时钟域完成,可以遵循以下设计

module TaskAck_CrossDomain(
    input clkA,
    input TaskStart_clkA,
    output TaskBusy_clkA, TaskDone_clkA,
input clkB,
output TaskStart_clkB, TaskBusy_clkB,
input TaskDone_clkB

);

reg FlagToggle_clkA, FlagToggle_clkB, Busyhold_clkB;
reg [2:0] SyncA_clkB, SyncB_clkA;

always @(posedge clkA) FlagToggle_clkA <= FlagToggle_clkA ^ (TaskStart_clkA & ~TaskBusy_clkA);

always @(posedge clkB) SyncA_clkB <= {SyncA_clkB[1:0], FlagToggle_clkA};
assign TaskStart_clkB = (SyncA_clkB[2] ^ SyncA_clkB[1]);
assign TaskBusy_clkB = TaskStart_clkB | Busyhold_clkB;
always @(posedge clkB) Busyhold_clkB <= ~TaskDone_clkB & TaskBusy_clkB;
always @(posedge clkB) if(TaskBusy_clkB & TaskDone_clkB) FlagToggle_clkB <= FlagToggle_clkA;

always @(posedge clkA) SyncB_clkA <= {SyncB_clkA[1:0], FlagToggle_clkB};
assign TaskBusy_clkA = FlagToggle_clkA ^ SyncB_clkA[2];
assign TaskDone_clkA = SyncB_clkA[2] ^ SyncB_clkA[1];
endmodule


总结:
情况一: 快时钟域 -> 慢时钟域
<1> 电平同步(打两拍)

情况二: 慢时钟域 -> 快慢时钟域
<1>周期长 : 打两拍,确保慢时钟域可以踩到快时钟域。
<2>结绳法 :利用握手协议
结绳模块(Pluse2Toggle): 负责延长待采样信号
同步模块(Synchronization):负责双触发器锁存
解绳模块(Toggle2Pluse): 负责将长信号转换成脉冲信号

Tips小技巧:
1.全局时钟的跳变沿最可靠。
2.来自异步时钟域的输入需要寄存一次以同步化,再寄存一次以减少亚稳态带来的影响。
3.不需要用到跳变沿的来自同一时钟域的输入,没有必要对信号进行寄存。
4.需要用到跳变沿的来自同一时钟域的输入,寄存一次即可。
5.需要用到跳变沿的来自不同时钟域的输入,需要用到3个触发器,前两个用以同步,第3个触发器的输出和第2个的输出经过逻辑门来判断跳变沿。

给出一个verilog模板:
always @ (posedge Clk) //不对输入信号进行寄存
begin
if (inputs)
begin

end

end
always @ (posedge Clk) //对输入信号寄存一拍
begin
inputs_reg <= inputs;
if (inputs_reg == 1’b0 && inputs == 1’b1)
begin

end

end
always @ (posedge Clk) //对输入信号寄存三拍
begin
inputs_reg1 <= inputs;
inputs_reg2 <= inputs_reg1;
inputs_reg3 <= inputs_reg2;
if (inputs_reg2 == 1’b1 && inputs_reg3 == 1’b0)
begin

end

end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值