天天看点

数字IC笔试常考

信号的跨时钟域同步

首先要了解为什么要进行跨时钟域同步。对于存在不同时钟域的电路,信号跨时钟域传输往往会导致亚稳态的问题。亚稳态指的是触发器无法在某个规定时间段内达到一个可确认的状态。

数字IC笔试常考

如上图所示,第一个时钟域的输出A传入第二个时钟域,由于时钟域不同,A信号的跳变可能与时钟信号的跳变在同一时刻,导致输出B的结果未知,生成亚稳态。

亚稳态的产生是由于触发器出现了时序违例(Timing violation)。

可以使用平均故障间隔时间(MTBF)来衡量亚稳态的出现情况,越小说明亚稳态越容易出现。

对于同步电路,可以有以下几个措施避免亚稳态:降低时钟频率,优化组合逻辑,优化时钟树,提高时钟驱动能力,改善时钟质量,选用更快的DFF。

对于异步电路要考虑单比特和多比特的情形,快时钟域到慢时钟域的情形。解决办法主要有插入同步单元,借助编码,添加握手信号,使用FIFO。

对于单比特信号,可以插入两级DFF。

快时钟域转到慢时钟域,应该将快时钟域信号拉长,至少要让慢时钟域采两拍;慢时钟域转到快时钟域,如果慢时钟比快时钟两倍还慢,可以直接采样,否则也要进行信号拉长,直到稳定采样。

对于多比特信号,可以用异步FIFO。或者采用格雷码配合双锁存。

异步FIFO

FIFO就是先进先出的缓存器。显然,由于先进先出,所以外部无法指定地址,只能按照顺序进行读写。对于FIFO设计,主要的问题在于读写指针是否同步,如何判断空满状态。

如果读写指针在同一个时钟域下,那么该FIFO即可称为同步FIFO。否则称为异步FIFO。因为无法对一个空的FIFO进一步读,无法对一个满的FIFO进一步写,所以如何确定空满状态是FIFO设计的重中之重。

对于同步FIFO,由于读写在同一个时钟下,所以空满状态直接使用一个计数器即可,在这个时钟域下,读则计数器减1,写则计数器加1。计数器为0则空,为FIFO深度则满。

对于异步FIFO,由于读写不在同一个时钟下,所以用一个计数器无法实现空满状态的判断。可以考虑利用两个计数器(地址指针)进行判断。在读操作之前,判断是否为空;在写操作之前,判断是否为满。

那么何时为空?如果读计数器=写计数器,说明写进去的数据,都被读了出来,那么当前FIFO就为空。

那么何时为满?已知读计数器=写计数器为空,如果写计数器多写一轮,那么FIFO就满了。因此判断满的条件是,读计数器+FIFO深度=写计数器。这里FIFO深度默认为2的整数次幂。

现在还有一个问题,由于要对读写计数器进行比较,而两个计数器分属不同时钟域,如何将计数器跨时钟同步呢?这就用到了上文的内容,一般结合格雷码,并插入两级DFF传输读写计数器。

插入同步单元后,显然由于同步延迟,达到读时钟域的写计数器与真实的写计数器相比慢了两个时钟周期。如此一来,得到的空信号也不是真实的。不过可以发现,这时候的空信号是更加保守的,因为真实的写计数器可能继续增加,而空信号已经产生。这种虚空状态不会影响到FIFO的功能。同样,当读计数器同步到写时钟域时,产生的也是虚满的状态,同样是更加保守的,FIFO可能这时候还没有满。

建立时间与保持时间

建立时间:触发器时钟上升沿之前数据需要保持稳定的最小时间;

保持时间:触发器时钟上升沿之后数据需要保持稳定的最小时间。

建立时间与保持时间需要满足如下关系:

数字IC笔试常考
数字IC笔试常考

可见,要满足建立时间,应该使得数据更快到达,时钟更慢到达;而保持时间则相反。

修复建立时间的手段有:减小时钟频率,增大时钟周期,调整时钟树(提前发射时钟,推后捕获时钟),采用更快的触发器,优化组合逻辑,打断组合逻辑。

修复保持时间的手段有:插入缓冲器。由于保持时间与时钟频率无关,所以更难修复。

异步复位同步释放电路

首先要知道异步复位同步释放是什么意思。这指的是复位信号rst,在复位时是异步的,在复位取消(即释放复位信号)时又是同步的。

数字IC笔试常考

先说为什么要同步释放。根据亚稳态的定义可知,该复位信号独立与时钟信号,复位释放后,很有可能与时钟上升沿靠得很近,引起亚稳态。所以,要将释放过程打两拍,即经过两级DFF,使得释放信号与时钟信号同步,避免亚稳态的产生。

那么为什么要异步复位?异步复位不会产生亚稳态吗?同步复位不好吗?同步复位与时钟信号同步,所以复位的生效依靠于时钟边沿的出现,无法起到立即生效的作用。而异步复位是独立于时钟的,复位立即生效。

下面给出实现代码。

module Sys_Rst(
    input       clk,
    input       rst,
    output      sys_rst
);

reg     rst_r0;
reg     rst_r1;

always @(posedge clk or posedge rst)begin
    if(rst)begin
        rst_r0 <= 1'b1;
        rst_r1 <= 1'b1;
    end
    else begin
        rst_r0 <= 1'b0;
        rst_r1 <= rst_r0;
    end
end

assign  sys_rst = rst_r1;

endmodule
           

继续阅读