IC验证-数字设计基础

想要进入IC行业,数字设计好像是没法规避的问题,慢慢积累吧,虽然可能没有机会去实际验证,但是总能有所收获

跨时钟域和亚稳态

亚稳态

锁存器出现亚稳态:

  • 在其中一个输入端输入的脉冲太短。
  • 两个端口输入同时有效,或两输入有效相差足够短。
  • 在使能输入的边缘处,输入信号不稳定。

跨时钟域如何导致亚稳态:

核心:输入信号无法保证建立、保持时间

  • 时钟脉冲太窄。
  • 异步信号对时钟有效沿是随机的,易产生亚稳态。异步信号包括:不被时钟控制的信号;或被不同时钟域的时钟同步的信号。

触发器进入亚稳态的时间可以用参数MTBF(Mean Time Between Failures)来描述,MTBF即触发器采样失败的时间间隔,表示为:

image-20210830153428846

其中fclock表示系统时钟频率,fdata代表异步输入信号的频率,tmet代表不会引起故障的最长亚稳态时间,C1和C2分别为与器件特性相关的常数。如果MTBF很大,就认为这个设计在实际工作中是能够正常运行的,不会因为亚稳态导致整个系统的失效。

亚稳态的处理方法:

  • 亚稳态不能避免。
  • 尽可能降低亚稳态的影响。
  • 高速数字电路依赖于同步器产生的从亚稳态事件中恢复的缓冲时间。

常见同步电路的解决方案

  • 两级DFF(传单信号)
  • FIFO(传有一定位宽的信号bus)
  • 握手信号(对于FIFO的深度要求比较大,需要引入握手信号)

img

跨时钟域

imgimage-20210830152255672

image-20210830155132541

跨时钟域的信号d可能在时钟上升沿时同步跳变,导致setup/hold time不够,此时会导致一种介于0和1之间的状态,可能震荡很长时间,也就是亚稳态

此时亚稳态的解决:根据MTBF公式来计算亚稳态的概率

对于单根信号- 双锁存(采两拍):

image-20210830161004761

同步复位和异步复位

1. fifo的空满检测

读指针和写指针

  • 读指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为0)。
  • 写指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0)

通过两个指针进行空满判断

当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时,或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,如下图所示:

image-20210915101227129

当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:

image-20210915102841760

为了区分到底是满状态还是空状态,可以采用以下方法:

在指针中添加一个额外的位(extra bit),当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针。

如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。
如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空;

2. 同步fifo代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
`timescale 1ns/1ps
module Sync_FIFO #(
//FIFO参数定义
parameter data_width = 32,// FIFO宽度
parameter data_depth = 256,// FIFO深度
parameter address_width = 8 // 地址宽度,对于深度为2^n的FIFO,需要的读/写指针位宽为(n+1)位,多的一位作为折返标志位
)(
//system signals
input clk,
input rst_n,

input write_en,// 写使能,高有效
input [data_width-1:0] data_in,// 写数据
output full,//写满标志,高有效

input read_en,// 读使能,高有效
output reg [data_width-1:0] data_out,// 读数据
output empty//读空标志,高有效
);

reg [data_width-1:0] FIFO_RAM [data_depth-1:0];// RAM定义,用于存放FIFO数据

reg [address_width:0] wr_addr_p;//写地址指针
reg [address_width:0] rd_addr_p;//读地址指针

wire [address_width-1:0] wr_addr;//写RAM 地址
wire [address_width-1:0] rd_addr;//读RAM 地址

assign wr_addr = wr_addr_p[address_width-1:0];// 读写RAM地址等于读写指针的低address_width位
assign rd_addr = rd_addr_p[address_width-1:0];// 读写RAM地址等于读写指针的低address_width位

always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr_addr_p <= 'h0;
rd_addr_p <= 'h0;
data_out <= 'h0;
end
else begin
case({write_en,read_en})
2'b00:begin// 不读不写
wr_addr_p <= wr_addr_p;
rd_addr_p <= rd_addr_p;
end
2'b10:if(~full) begin // 非满只写不读
FIFO_RAM[wr_addr] <= data_in;
wr_addr_p <= wr_addr_p + 1'b1;
end
2'b01:if(~empty) begin // 非空只读不写
data_out <= FIFO_RAM[rd_addr];
rd_addr_p <= rd_addr_p + 1'b1;
end
2'b11:if(~full && ~empty)begin // 非满非空又读又写
FIFO_RAM[wr_addr] <= data_in;
wr_addr_p <= wr_addr_p + 1'b1;

data_out <= FIFO_RAM[rd_addr];
rd_addr_p <= rd_addr_p + 1'b1;
end else if(~full) begin
FIFO_RAM[wr_addr] <= data_in;
wr_addr_p <= wr_addr_p + 1'b1;
end else begin
data_out <= FIFO_RAM[rd_addr];
rd_addr_p <= rd_addr_p + 1'b1;
end
endcase
end
end

// 判断空满
assign full = (wr_addr_p == {~rd_addr_p[address_width],rd_addr_p[address_width-1:0]});// 最高位不同,其余各位相同
assign empty = (wr_addr_p == rd_addr_p);// 所有位都相同
endmodule

流水线