跳转至

为什么FPGA的阻塞赋值会延时一个时钟周期,而在某些情况下不延时一个时钟周期呐?

参考资料

急问!为什么FPGA的<=语句在这种情况下不延迟一拍? - 知乎 (zhihu.com)

【必看】时序逻辑仿真成组合逻辑?你知道原因吗? - 知乎 (zhihu.com)

小梅哥爱漂流的个人空间-小梅哥爱漂流个人主页-哔哩哔哩视频 (bilibili.com)

第一种情况

​ 在学习FPGA的时候,经常会使用打拍的操作来用来消除亚稳态、获取信号的下降沿检测,来生成一些标志信号

插入两级寄存器进行数据同步,用来消除亚稳态。第三级寄存器和第二级寄存器共同构成下降沿检测,检测到下降沿时start_nedge产生一个时钟的高电平。

```verilog uart_rx.v //插入两级寄存器进行数据同步,用来消除亚稳态 //rx_reg1:第一级寄存器,寄存器空闲状态复位为1 always@(posedge clk or negedge rstn) if(rstn == 1'b0) rx_reg1 <= 1'b1; else rx_reg1 <= rx;

//rx_reg2:第二级寄存器,寄存器空闲状态复位为1 always@(posedge clk or negedge rstn) if(rstn == 1'b0) rx_reg2 <= 1'b1; else rx_reg2 <= rx_reg1;

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测 always@(posedge clk or negedge rstn) if(rstn == 1'b0) rx_reg3 <= 1'b1; else rx_reg3 <= rx_reg2;

//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平 always@(posedge clk or negedge rstn) if(rstn == 1'b0) start_nedge <= 1'b0; else if((~rx_reg2) && (rx_reg3)) start_nedge <= 1'b1; else start_nedge <= 1'b0;


信号延时一拍的原因,比如如上图所示,我们设置的是时钟上升沿检测信号变化。,当rx信号在时钟上升沿到来之前是高电平,来临之后变换为低电平,那么通过打一拍赋值给的新的寄存器的信号是在时钟到来之前的电平状态,也还就是高电平。因此信号会延时一个时钟周期的变换。主要原因是信号的赋值是根据时钟边沿变化的前一时刻来赋值的

当前程序的仿真情况如图所示:

![](https://jsd.cdn.zzko.cn/gh/zikwq/Blog_Pic/20240609131344.png)



## 第二种情况





```verilog test.v
module test(  
    input               clk,    //系统时钟;  
    input               rst,    //系统复位,高电平有效;  

    input       [1:0]   in,      
    output      [1:0]   out 
);

reg [1:0]   in_r,in_rr;     //分别打一拍、打两拍

assign out = in_rr;

always@(posedge clk or posedge rst)begin 
    if(rst)begin 
        in_r <= 2'd0;       //复位初始值
        in_rr <= 2'd0;      //复位初始值
    end
    else begin
        in_r <= in;         //输入打一拍
        in_rr <= in_r;      //输入打两拍
    end
end

endmodule

``verilog tb_test.vtimescale 1 ns/1 ns
module tb_test();

//输入输出端口 reg clk;
reg rst;
reg [1:0] in; wire [1:0] out;

//例化被测试模块
test u_test (
.clk (clk),
.rst (rst),
.in (in ),
.out (out)
);

//生成系统时钟,周期10ns;
initial begin clk = 1;
forever #5 clk = ~clk;
end

//生成复位信号 initial begin rst = 1; #25;
rst = 0;
end

//生成输入信号(测试激励) initial begin in = 0; #30; repeat(8)begin //循环8次; #10 in = in + 1; //输入递增1 end
$stop; //停止仿真 end

endmodule


![](https://jsd.cdn.zzko.cn/gh/zikwq/Blog_Pic/20240609132200.png)

当赋值的时候给#1延时用来模拟Tco(寄存器的时钟到输出延迟)的时候,仿真如下

![](https://jsd.cdn.zzko.cn/gh/zikwq/Blog_Pic/20240609133052.png)



虽然在上面的参考文献中,作者给出了出现第二种情况的答案,是因为D触发器的建立时间、保持时间、输出延时的原因。但是我还是有一点没有弄明白,为什么跟第一种情况不一样,如果作者的答案是正确的,那么就不会出现第一种情况了吧。留坑,碰见大佬解答

## 破案(又立案)

先贴出答案

![](https://jsd.cdn.zzko.cn/gh/zikwq/Blog_Pic/image-20240609134847484.png)

原因很简单,但是是一个小细节很难注意到。我们先来分析一下TestBench的代码片段。

```verilog
//生成输入信号(测试激励)
initial begin
    in = 0; 
    #30;
    repeat(8)begin          //循环8次;
        #10  in = in + 1;   //输入递增1
    end  
    $stop;                  //停止仿真
end

根据上上面的仿真图像可以看到,in的赋值与时钟上升沿是对齐的,并且in使用的是阻塞赋值,也就是说在时钟上升沿的那一刻数据发生了变化,

可以看到,·······

新问题

我们使用了阻塞赋值,在时钟上升沿时数据产生了变化,那么这里的tsu建立时间并不能满足要求。查阅资料得知当tus不满足时,数据将不被正确采样,而是等到下一个时钟周期的时候,数据才能被正确写入寄存器当中。那么问题来了,我们的数据是怎么写入寄存器里面的呐?

下面是testbench在阻塞赋值下的门级仿真

另一个新的问题(2024.6.10):单比特数据夸时钟域处理的问题

因为具体的问题是出现在亚稳态上的,并且跟这个问题相似,所以在这里一并记录下来。

在阅读王敏志编著的《FPGA设计实战演练高级技巧篇》P19的时候,作者讲到单比特数据夸时钟域处理方法可以使用2级或者3级同步器(打拍操作)来解决信号跨时钟域时出现亚稳态的情况,人体就出现这个亚稳态的部分,请看下面的图像:

问题1.时钟1输出数据1的数据为什么没有时钟沿对齐?

问题2.在时钟2第一次采样的时候确实是位于亚稳态区域,但是第二次采样应该可以采集到正确的数据了。为什么数据2还是位于亚稳态?

问题3. 既然是亚稳态,问什么数据2在时钟2的采样边沿可以采集到“高”?