FPGA设计中的跨时钟域问题(转)


跨时钟域信号同步技术研究(笔记) - 辉 - 接着飘

在一个FPGA设计中可能会用到多个时钟,每个时钟在FPGA内部形成一个时钟域,如果在一个时钟域中产生的信号需要在另一个时钟域中使用,那么需要特别小心!

到另一个时钟域的信号

假设一个在时钟域CLKA产生的信号需要在时钟域CLKB中使用,那么它需要首先与时钟域CLKB“同步”,也就是说需要一个“同步”设计,它接受来自时钟域CLKA的信号,并产生一个新的信号输出到CLKB。

FPGA设计中的跨时钟域问题(转) - huihui - 辉辉的博客
在第一个设计中,我们假设信号的改变相对于时钟域CLKA和CLKB的时钟都是很慢的,典型的做法是使用两个触发器来将信号同步到时钟域CLKB。(具体原因参考随后我翻译的blog)
module signal_crossdomain(
        clka,
        signalin,
        clkb,
        signalout
        );
input clka;
input signalin;
input clkb;
output signalout;
//信号的跨时钟域传递,使用两个移位寄存器来进行“同步”
reg [1:0] synca_clkb;
always @(posedge clkb) synca_clkb[0] <= signalin;   //注意使用的是clkb
always @(posedge clkb) synca_clkb[1] <= synca_clkb[0];  //注意我们使用的是clkb
assign signalout = synca_clkb[1];
endmodule
由于这两个触发器的使用,会使得信号有一定的延迟,例如下图就是使用两个触发器同步时代波形图:
FPGA设计中的跨时钟域问题(转) - huihui - 辉辉的博客
到另一个时钟域的标志(FLAG)
好了,假设需要跨时钟域传递的信号是一个宽度仅为1个时钟周期的脉冲(称为标志信号FLAG),那么前面的设计可能就不会正常工作了,因为这个FLAG很可能被CLKB错过或者延长(变成多个FLAG),具体是由CLKB的速度决定的。
我们仍然使用一个同步器,但是它是为FLAG信号设计的
FPGA设计中的跨时钟域问题(转) - huihui - 辉辉的博客
 设计的技巧是先把FLAG信号转换为电平信号,然后再使用两个触发器来同步。
module Flag_CrossDomain(
        rst,
        clkA, 
        FlagIn_clkA, 
        clkB, 
        FlagOut_clkB
        );
// 时钟域 clkA 的信号:clkA, FlagIn_clkA;  
// 时钟域 clkB 的信号input clkB;     
input rst;
input clkA;
input FlagIn_clkA;
input clkB;
output FlagOut_clkB;
reg FlagToggle_clkA;
reg [2:0] SyncA_clkB;
always @(posedge clkA or negedge rst)
begin
        if(!rst) begin
                FlagToggle_clkA <= 1'b0;
        end
        else if(FlagIn_clkA) begin
                FlagToggle_clkA <= ~FlagToggle_clkA;  // 当FLAG来到时,改变电平状态
        end
end
doalways @(posedge clkB or negedge rst)
begin
        if(!rst) begin
                SyncA_clkB <= 3'd0;
        end
        else begin
                SyncA_clkB <= {SyncA_clkB[1:0], FlagToggle_clkA};   // 与clk B同步
        end
end
assign FlagOut_clkB = (SyncA_clkB[2] ^ SyncA_clkB[1]);  // 重生FLAG信号
endmodule
得到的仿真结果如下:

FPGA设计中的跨时钟域问题(转) - huihui - 辉辉的博客

这种方法来实现不同时钟域之间信号传递会存在一些问题,如当输出信号的两个脉冲之间的clkb需要满足有两个上升沿,负责会出现问题,如下图所示:

FPGA设计中的跨时钟域问题(转) - huihui - 辉辉的博客

这样我们可以在clkA域中设置一个busy信号来指示clkB域正在处理信号,这时候clkA域中再输入信号是无效的:

FPGA设计中的跨时钟域问题(转) - huihui - 辉辉的博客

 

module FlagAck_CrossDomain(      clkA, FlagIn_clkA, Busy_clkA,       clkB, FlagOut_clkB);  // clkA domain signals  input clkA, FlagIn_clkA;  output Busy_clkA;  // clkB domain signals  input clkB;  output FlagOut_clkB;  reg FlagToggle_clkA;  reg [2:0] SyncA_clkB;  reg [1:0] SyncB_clkA;  always @(posedge clkA) if(FlagIn_clkA & ~Busy_clkA) FlagToggle_clkA <= ~FlagToggle_clkA;  always @(posedge clkB) SyncA_clkB <= {SyncA_clkB[1:0], FlagToggle_clkA};  always @(posedge clkA) SyncB_clkA <= {SyncB_clkA[0], SyncA_clkB[1]};  assign FlagOut_clkB = (SyncA_clkB[2] ^ SyncA_clkB[1]);  assign Busy_clkA = FlagToggle_clkA ^ SyncB_clkA[1];  endmodule  

也可以在clkB中也添加一个完成信号

FPGA设计中的跨时钟域问题(转) - huihui - 辉辉的博客

module TaskAck_CrossDomain(
    clkA, TaskStart_clkA, TaskBusy_clkA, TaskDone_clkA, 
    clkB, TaskStart_clkB, TaskBusy_clkB, TaskDone_clkB);
// clkA domain signals
input clkA;
input TaskStart_clkA;
output TaskBusy_clkA, TaskDone_clkA;
// clkB domain signals
input clkB;
output TaskBusy_clkB, TaskStart_clkB;
input TaskDone_clkB;
reg FlagToggle_clkA, Busyhold_clkB; reg [2:0] SyncA_clkB, SyncB_clkA;
always @(posedge clkA) if(TaskStart_clkA & ~TaskBusy_clkA) FlagToggle_clkA <= ~FlagToggle_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 clkA) SyncB_clkA <= {SyncB_clkA[1:0], Busyhold_clkB ^ SyncA_clkB[2]};
assign TaskBusy_clkA = FlagToggle_clkA ^ SyncB_clkA[2];
assign TaskDone_clkA = SyncB_clkA[2] ^ SyncB_clkA[1];
endmodule
input clkB;
output TaskBusy_clkB, TaskStart_clkB;
input TaskDone_clkB;
reg FlagToggle_clkA, Busyhold_clkB; reg [2:0] SyncA_clkB, SyncB_clkA;
always @(posedge clkA) if(TaskStart_clkA & ~TaskBusy_clkA) FlagToggle_clkA <= ~FlagToggle_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 clkA) SyncB_clkA <= {SyncB_clkA[1:0], Busyhold_clkB ^ SyncA_clkB[2]};
assign TaskBusy_clkA = FlagToggle_clkA ^ SyncB_clkA[2];
assign TaskDone_clkA = SyncB_clkA[2] ^ SyncB_clkA[1];
endmodule
input clkB;
output TaskBusy_clkB, TaskStart_clkB;
input TaskDone_clkB;
reg FlagToggle_clkA, Busyhold_clkB; reg [2:0] SyncA_clkB, SyncB_clkA;
always @(posedge clkA) if(TaskStart_clkA & ~TaskBusy_clkA) FlagToggle_clkA <= ~FlagToggle_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 clkA) SyncB_clkA <= {SyncB_clkA[1:0], Busyhold_clkB ^ SyncA_clkB[2]};
assign TaskBusy_clkA = FlagToggle_clkA ^ SyncB_clkA[2];
assign TaskDone_clkA = SyncB_clkA[2] ^ SyncB_clkA[1];
endmodule
Links
To learn what is metastablity and why two flip-flops are used to cross clock domains, follow these links. 
What Is Metastability? and Interfacing Two Clock Domains from World of ASIC. 
Crossing the abyss from the EDN magazine. 
Digital Logic Metastability from interfacebus.com 
Metastability in electronics from Wikipedia. 
Synchronization in Digital Logic Circuits from Ryan Donohue. 

That's all folks!
Have fun crossing clock domains

 源地址:http://blog.xdnice.com/blog6i75177.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值