你要的FPGA&数字前端笔面试题来了

你要的FPGA&数字前端笔面试题来了

FPGA&ASIC基本开发流程

题目:简述ASIC设计流程,并列举出各部分用到的工具。

绘1

ASIC开发基本流程

芯片架构,考虑芯片定义、工艺、封装

RTL设计,使用Verilog、System Verilog、VHDL进行描述

功能仿真,理想情况下的仿真

验证,UVM验证方法学、FPGA原型验证

综合,逻辑综合,将描述的RTL代码映射到基本逻辑单元门、触发器上

DFT技术,插入扫描链

等价性检查,使用形式验证技术

STA,静态时序分析

布局规划,保证没有太多的内部交互,避免布线上的拥堵和困扰

时钟树综合,均匀地分配时钟,减少设计中不同部分间的时钟偏移

DRC,设计规则检查

LVS,布线图和原理图进行比较

生成GDSII

这整个流程称为RTL2GDSII,利用GDSII来生产芯片的过程称作流片(Tapeout),以上是一个Fabless公司的简易设计流程,最后将GDSII送至Foundry生产芯片。

题目:简述FPGA的开发流程。

绘图1

FPGA开发基本流程

系统规划,系统功能,功能模块划分

RTL设计,使用Verilog、System Verilog、VHDL进行描述

功能仿真,理想情况下的仿真

综合、编译、布局布线,FPGA厂商自带工具完成

时序仿真,时序分析约束

板级验证

题目:名词解释:

ROM:Read Only Memory,只读存储器,手机、计算机等设备的存储器,但现在的所说的ROM不只是Read Only了,也是可以写入的。

RAM:Random Access Memory,随机存取存储器,手机、计算机的运行内存。

SRAM:Static Random-Access Memory,静态随机存取存储器,只要供电数据就会保持,但断电数据就会消失,也被称为Volatile Memory

DRAM:Dynamic Random Access Memory,动态随机存储器,主要原理是利用电容存储电荷的多少来代表一个bit是0还是1,由于晶体管的漏电电流现象,电容会放电,所以要周期性的给电容充电,叫刷新。SRAM不需要刷新也会保持数据丢失,但是两者断电后数据都会消失,称为Volatile Memory

SDRAM:Synchronous Dynamic Random Access Memory,同步动态随机存储器,同步写入和读出数据的DRAM。

EEPROM:Electrically Erasable Programmable Read Only Memory,电可擦除可编程只读存储器,

DDR:Double Data Synchronous Dynamic Random Access Memory,双倍速率同步动态随机存储器,双倍速率传输的SDRAM,在时钟的上升沿和下降沿都可以进行数据传输。我们电脑的内存条都是DDR芯片。

FLASH: Flash Memory,闪存,非易失性固态存储,如制成内存卡或U盘。

数字电路基础

题目:bit, byte, word, dword, qword的区别

1byte = 8bit

1word = 2byte = 16bit

1dword = 2word = 4byte = 32bit

1qword = 2dword = 4word = 8byte = 64bit

题目:什么是原码,反码,补码,符号-数值码。以8bit为例,给出各自表示的数值范围

原码:符号位+真值,最高位表示符号位,以8bit为例。

[+3]原 = 0000_0011

[-3]原 = 1000_0011

表示范围:-127到+127

原码中0000和1000都表示0。

反码:正数的反码是它本身,负数的反码将原码除符号位外逐位取反。以8bit为例。

[+3]原 = [0000_0011]原 = [0000_0011]反

[-3]原 = [1000_0011]原 = [1111_1100]反

表示范围:-127到+127

反码中0000_0000和1111_1111都表示0。

补码:正数的补码是它本身,负数的补码将原码除符号位外逐位取反再加1。以8bit为例。

[+3]原 = [0000_0011]原 = [0000_0011]反 = [0000_0011]补

[-3]原 = [1000_0011]原 = [1111_1100]反 = [1111_1101]补

表示范围:-128到+127

补码中0的表示只有一种形式,即0000_0000,1000_0000表示-128。

以上是有符号数,对于无符号数来说都是来表示整数,其原码、反码、补码都是其本身。

更详细解释可参考维基百科。

https://zh.wikipedia.org/wiki/%E6%9C%89%E7%AC%A6%E8%99%9F%E6%95%B8%E8%99%95%E7%90%86

题目:数制转换

R进制数转换为十进制数:按权展开,相加

十进制数转化为R进制数:整数部分,除R取余法,除到商为0为止。小数部分,乘R取整法,乘到积为0为止。

二进制数转化八进制数:三位一组,整数部分左边补0,小数部分右边补0。反之亦然。

二进制数转化十六进制数:四位一组,整数部分左边补0,小数部分右边补0。反之亦然。

127 -127 127.375 -127.375 十进制数转化为R进制数:整数部分,除R取余法,除到商为0为止。小数部分,乘R取整法,乘到积为1为止。

127 = 0111_1111

-127 = 1111_1111

127.375 = 0111_1111.011

-127.375 = 1111_1111.011

题目:逻辑函数及其化简

公式法

卡诺图法

题目:什么是冒险和竞争,如何消除?

下面这个电路,使用了两个逻辑门,一个非门和一个与门,本来在理想情况下F的输出应该是一直稳定的0输出,但是实际上每个门电路从输入到输出是一定会有时间延迟的,这个时间通常叫做电路的开关延迟。而且制作工艺、门的种类甚至制造时微小的工艺偏差,都会引起这个开关延迟时间的变化。

冒险

竞争

实际上如果算上逻辑门的延迟的话,那么F最后就会产生毛刺。信号由于经由不同路径传输达到某一汇合点的时间有先有后的现象,就称之为竞争,由于竞争现象所引起的电路输出发生瞬间错误的现象,就称之为冒险,FPGA设计中最简单的避免方法是尽量使用时序逻辑同步输入输出。

  • 加滤波电容,消除毛刺的影响
  • 加选通信号,避开毛刺
  • 增加冗余项,消除逻辑冒险。

题目:设计一个2-4译码器。

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
module Decode_2_4(
input [1:0] indata,
input enable_n,
//output reg [3:0] outdata
output [3:0] outdata
);

/*
always @(*)begin
if(enable_n == 1'b1)
outdata = 4'b1111;
else begin
case(indata)
2'b00: outdata = 4'b1110;
2'b01: outdata = 4'b1101;
2'b10: outdata = 4'b1011;
2'b11: outdata = 4'b0111;
endcase
end
end
*/

assign outdata[3] = ~(indata[1] & indata[0] & ~enable_n);
assign outdata[2] = ~(indata[1] & ~indata[0] & ~enable_n);
assign outdata[1] = ~(~indata[1] & indata[0] & ~enable_n);
assign outdata[0] = ~(~indata[1] & ~indata[0] & ~enable_n);

endmodule

题目:设计BCD译码器,输入0~9。

BCD译码器也称为4-10线译码器

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
module Decode_4_10(
input [3:0] indata,
//output reg [9:0] outdata
output [9:0] outdata
);
/*
always @(*)begin
case(indata)
4'b0000: outdata = 10'b1111_1111_10;
4'b0001: outdata = 10'b1111_1111_01;
4'b0010: outdata = 10'b1111_1110_11;
4'b0011: outdata = 10'b1111_1101_11;
4'b0100: outdata = 10'b1111_1011_11;
4'b0101: outdata = 10'b1111_0111_11;
4'b0110: outdata = 10'b1110_1111_11;
4'b0111: outdata = 10'b1101_1111_11;
4'b1000: outdata = 10'b1011_1111_11;
4'b1001: outdata = 10'b0111_1111_11;
default: outdata = 10'b1111_1111_11;
endcase
end
*/
assign outdata[0] = ~(~indata[3] & ~indata[2] & ~indata[1] & ~indata[0]);
assign outdata[1] = ~(~indata[3] & ~indata[2] & ~indata[1] & indata[0]);
assign outdata[2] = ~(~indata[3] & ~indata[2] & indata[1] & ~indata[0]);
assign outdata[3] = ~(~indata[3] & ~indata[2] & indata[1] & indata[0]);
assign outdata[4] = ~(~indata[3] & indata[2] & ~indata[1] & ~indata[0]);
assign outdata[5] = ~(~indata[3] & indata[2] & ~indata[1] & indata[0]);
assign outdata[6] = ~(~indata[3] & indata[2] & indata[1] & ~indata[0]);
assign outdata[7] = ~(~indata[3] & indata[2] & indata[1] & indata[0]);
assign outdata[8] = ~(indata[3] & ~indata[2] & ~indata[1] & ~indata[0]);
assign outdata[9] = ~(indata[3] & ~indata[2] & ~indata[1] & indata[0]);

endmodule

题目:MOS逻辑门

与非门:上并下串(上为PMOS,下为NMOS)

与非门

或非门:上串下并

或非门

反相器

反相器

CMOS三态门

漏极开路输出

题目:用D触发器带同步高置数和异步高复位端的二分频的电路,画出逻辑电路,Verilog描述。

D触发器

1
2
3
4
5
6
7
8
9
10
11
reg     Q;
always @(posedge clk or posedge rst)begin
if(rst == 1'b1)
Q <= 1'b0;

else if(set == 1'b1)
Q <= 1'b1;

else
Q <= ~Q;
end

题目:解释一下Vih,Vil,Vol,Voh,Vt,Iddq

Vih:输入电压由高到低变化时,输出电压开始上升且传输特性曲线斜率为-1的点,即图中B点对应的输入电压。(仍能维持输出为逻辑“1”的最大输出电压)

Vil:输入电压由低到高变化时,输出电压开始下降且传输特性曲线斜率为-1的点,即图中A点对应的输入电压。(仍能维持输出为逻辑“0”的最小输出电压)

Voh:定义为最小合格高电平。(维持输出为逻辑“1”的最大输出电压)

Vol:定义为最大合格低电平。(维持输出为逻辑“0”的最大输出电压)

CMOS集成电路内部规定Vol = 0v,Voh = Vdd。

Vt:MOS管的阈值电压

Iddq:IDDQ是指当CMOS集成电路中的所有管子都处于静止状态时的电源总电流。

如果输入电平是(Vil+Vih)/2,会有什么问题

输入电压(Vil+Vih)/2接近于0.5Vdd,CMOS反相器的阈值电压也接近于0.5Vdd,这样输出不确定会是高电平还是低电平,输出呈亚稳态!

题目:CMOS反相器的功耗主要包括哪几部分?分别与哪些因素相关?

$$
P_{Total} = P_{dynamic} + P_{short} + P_{leakage}
$$

  • P_dynamic 是电路翻转产生的动态功耗
  • P_short是P管和N管同时导通时产生的短路功耗
  • P_leakage 是由扩散区和衬底之间的反向偏置漏电流引起的静态功耗

静态功耗:CMOS反相器在静态时,P、N管只有一个导通。由于没有Vdd到GND的直流桐庐,所以CMOS的静态功耗应该等于零。但实际上,由于扩散区和衬底的PN结上存在反向漏电流,所以会产生静态功耗。

短路功耗:CMOS电路在“0”和“1”的转换过程中,P、N管会同时导通,产生一个由Vdd到VSS窄脉冲电流,由此引起功耗

动态功耗:C_L 这个CMOS反相器的输出负载电容,由NMOS和PMOS晶体管的漏扩散电容、连线电容和扇出门的输入电容组成。

题目:CMOS反相器的速度与哪些因素有关?什么是转换时间(transition time)和传播延迟(propagation delay)?

反相器的速度与哪些因素有关。

  1. 电容(负载电容、自载电容、连线电容)较小,漏端扩散区的面积应尽可能小。输入电容要考虑: (1)Cgs 随栅压而变化(2)密勒效应(3)自举效应

  2. 加大晶体管的尺寸(驱动能力),使晶体管的等效导通电阻(输出电阻)较小。但这同时加大自载电容和负载电容(下一级晶体管的输入电容)。

  3. 提高电源电压,提高电源电压可以降低延时,即用功耗换取性能但超过一定程度后改善有限。电压过高会引起可靠性问题(氧化层击穿、热电子等)。

    Transition Time(转换时间):上升时间:从10%Vdd上升到90%Vdd的时间,下降时间L从90%Vdd下降到10%dd的时间。上升时间和下降时间统称为Transition Time,也有定义为20%到80%。
    Propagation Delay(传播延时):在输入信号变化到50%Vdd到输出信号变化到50%Vdd之间的时间

题目:transition time, propagation delay等参数的定义

传输延迟

Transition Time(转换时间):输入和输出信号,上升时间:从10%Vdd上升到90%Vdd的时间,下降时间L从90%Vdd下降到10%Vdd的时间。上升时间和下降时间统称为Transition Time。

转换时间

Propagation Delay(传播延时):在输入信号变化到超过50%Vdd到输出信号变化到超过50%Vdd之间的时间。

Timing constraints include: setup time, hold time, recovery time, and minimum pulse width.

建立时间

在时钟沿来临前,输入信号的变化超过50%Vdd的时间到时钟变化超过50%Vdd的时间中,输入信号保持稳定的最小时间。

保持时间

在时钟沿来临后,输入信号的变化超过50%Vdd的时间到时钟变化超过50%Vdd的时间中,输入信号保持稳定的最小时间。

覆盖时间

复位或者置位信号变化超过50%Vdd的时间到时钟变化超过50%Vdd的时间中,时钟沿来临的前最小时间,保证复位或置位完成。

removal时间

复位或者置位信号变化超过50%Vdd的时间到时钟变化超过50%Vdd的时间中,时钟沿来临的后最小时间,保证置位或复位完成。

最小脉冲宽度

最小脉冲宽度就是信号上升沿变化超过50%Vdd到下降沿变化低于50%Vdd时,测量高电平的最小脉冲宽度,低电平最小宽度同理。

个人认为不能保证各个时间参数,可能会产生亚稳态。

题目:ASIC中低功耗的设计方法和思路(不适用于FPGA)

  • 合理规划芯片的工作模式,通过功耗管理模块控制芯片各模块的Clock,Reset起到控制功耗的目的。
  • 门控时钟(Clockgateing):有效降低动态功耗
  • 多电压供电:通过控制模块的电压来降低功耗
  • 多阈值电压

什么是latch-up(闩锁效应)?

闩锁效应是集成电路中出现的一种效应。通常拉CMOS结构中,形成PNP搭NPN寄生晶体管。当有一个微小电流触发,会形成正反馈,让芯片发热报废。

MOS工艺会包含许多内在的双极型管,它们在CMOS工艺中特别会引起麻烦,因为同时存在的阱和衬底会形成寄生的n-p-n-p结构,这些类似于闸流管的器件一旦激发会导致Vdd和Vss线短路,这通常会破坏芯片,或至少使系统无法工作职能断电后重新启动。——《数字集成电路》

Latchup

题目:输入一个8bit数,输出其中1的个数。如果只能使用1bit全加器,最少需要几个?

7个1bit全加器

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
module number_one(
input clk,
input rst_n,
input [7:0] din,
output [3:0] num_one
);

wire [1:0] sum0;
wire [1:0] sum1;
wire [2:0] sum2;

full_adder_one u0(
.dina (din[0]),
.dinb (din[1]),
.cin (din[2]),
.sum (sum0[0]),
.cout (sum0[1])
);

full_adder_one u1(
.dina (din[3]),
.dinb (din[4]),
.cin (din[5]),
.sum (sum1[0]),
.cout (sum1[1])
);

adder2 u3(
.dina (sum0),
.dinb (sum1),
.cin (din[6]),
.sum (sum2[1:0]),
.cout (sum2[2])
);

adder3 u2(
.dina (sum2),
.dinb (0),
.cin (din[7]),
.sum (num_one[2:0]),
.cout (num_one[3])
);

endmodule

module full_adder_one(
input dina,
input dinb,
input cin,
output sum,
output cout
);

assign {cout, sum} = dina + dinb + cin;

endmodule
module adder2(
input [1:0] dina,
input [1:0] dinb,
input cin,
output [1:0] sum,
output cout
);

wire co;

full_adder_one u0(
.dina (dina[0]),
.dinb (dinb[0]),
.cin (cin),
.sum (sum[0]),
.cout (co)
);

full_adder_one u1(
.dina (dina[1]),
.dinb (dinb[1]),
.cin (co),
.sum (sum[1]),
.cout (cout)
);

endmodule

module adder3(
input [2:0] dina,
input [2:0] dinb,
input cin,
output [2:0] sum,
output cout
);

wire co;

full_adder_one u0(
.dina (dina[0]),
.dinb (dinb[0]),
.cin (cin),
.sum (sum[0]),
.cout (co)
);

adder2 u1(
.dina (dina[2:1]),
.dinb (dinb[2:1]),
.cin (co),
.sum (sum[2:1]),
.cout (cout)
);

endmodule

时序逻辑电路基础

题目:简述建立时间和保持时间,作图说明

image001

建立时间Tsu(setup):触发器在时钟上升沿到来之前,其数据输入端的数据必须保持不变的最小时间。

保持时间Th(hold):触发器在时钟上升沿到来之后,其数据输入端的数据必须保持不变的最小时间。

题目:说明D触发器与Latch的区别。

锁存器对电平信号敏感,在输入脉冲的电平作用下改变状态。

D触发器对时钟边沿敏感,检测到上升沿或下降沿触发瞬间改变状态。

https://www.vlsifacts.com/difference-latch-flip-flop/

题目:什么是同步电路和异步电路。

同步逻辑是时钟之间有固定的因果关系。异步逻辑是各时钟之间没有固定的因果关系。

在电路中同一个时钟源的时钟分频出来的不同频率的时钟作用于两部分电路,这两部分电路也是同步的。反之,不同时钟源的电路就是异步电路。

题目:最小周期计算

image003

Tco:寄存器更新延迟。clock output delay,时钟触发到数据输出的最大延迟时间

最小时钟周期:Tmin = Tco + Tdata + Tsu - Tskew。最快频率Fmax = 1/Tmin

Tskew = Tclkd – Tclks。

题目:什么是Clock Jitter和Clock Skew,这两者有什么区别。

时钟抖动(Clock Jitter):指芯片的某一个给定点上时钟周期发生暂时性变化,使得时钟周期在不同的周期上可能加长或缩短。

时钟偏移(Clock Skew):是由于布线长度及负载不同引起的,导致同一个时钟信号到达相邻两个时序单元的时间不一致。

区别:Jitter是在时钟发生器内部产生的,和晶振或者PLL内部电路有关,布线对其没有影响。Skew是由不同布线长度导致的不同路径的时钟上升沿到来的延时不同。

题目:什么是亚稳态,产生的原因,如何消除?

亚稳态:是指触发器无法在某个规定时间段内达到一个确定的状态。

原因:由于触发器的Tsu和Th不满足,当触发器进入亚稳态,使得无法预测该单元的输出,这种不稳定是会沿信号通道的各个触发器级联传播。

消除:两级或多级寄存器同步。理论上亚稳态不能完全消除,只能降低,一般采用两级触发器同步就可以大大降低亚稳态发生的概率,再加多级触发器改善不大。

image005

1
2
3
4
5
6
7
8
9
10
11
12
reg data_d1;
reg data_d2;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_d1 <= 1'd0;
data_d2 <= 1'd0;
end
else begin
data_d1 <= data_in;
data_d2 <= data_d1;
end
end

题目:同步和异步

同步复位和异步复位的区别

同步复位是复位信号随时钟边沿触发有效。异步复位是复位信号有效和时钟无关。

同步逻辑和异步逻辑的区别

同步逻辑是时钟之间有固定的因果关系。异步逻辑是各时钟之间没有固定的因果关系

同步电路和异步电路区别

同步电路有统一的时钟源,经过PLL分频后的时钟驱动的模块,因为是一个统一的时钟源驱动,所以还是同步电路。异步电路没有统一的时钟源。

跨时钟域处理

题目:reg和wire的区别

reg是寄存器类型可以存储数据,wire是线网型

reg型在always块和initial块中赋值,wire型用assign赋值

reg型可用于时序逻辑和组合逻辑赋值,wire型只能用于组合逻辑赋值

wire表示直通,即只要输入有变化,输出马上出现结果,reg表示一定要有触发,输出才会反映输入

题目:阻塞赋值与非阻塞赋值的区别

1
2
3
4
5
6
7
8
9
always @(posedge clk) begin
b <= a;
c <= b;
end

always @(posedge clk) begin
b = a;
c = b;
end

第一种赋值方式是非阻塞赋值,最后结果是b = a,c = b。

第二种赋值方式是阻塞赋值,最后的结果是b = a,c = a。

非阻塞赋值在触发调节满足是,两条语句是同时进行的,阻塞赋值是顺序执行的。

题目:localparam、parameter和define的区别

声明:

localparam xx = yy;

parameter xx = yy;
`define XX YY

使用:
xx
`XX

localparam只能在当前Verilog文件中使用

parameter与define都可以用来定义常量

parameter写在模块中,可以被上一层模块调用时,进行参数传递。

parameter 作用于声明的那个文件;define 从编译器读到这条指令开始到编译结束都有效,或者遇到`undef命令使之失效。

题目:task与function的区别

https://blog.csdn.net/kobesdu/article/details/39080571

题目:谈谈对Retiming技术的理解

  Retiming就是重新调整时序,例如电路中遇到复杂的组合逻辑,延迟过大,电路时序不满足,这个时候采用流水线技术,在组合逻辑中插入寄存器加流水线,进行操作,面积换速度思想。

数字集成电路

题目:反相器的速度与哪些因素有关?什么是转换时间(transition time)和传播延迟(propagation delay)?

反相器的速度与哪些因素有关。

  • 电容(负载电容、自载电容、连线电容)较小,漏端扩散区的面积应尽可能小。输入电容要考虑: (1)Cgs 随栅压而变化(2)密勒效应(3)自举效应

  • 加大晶体管的尺寸(驱动能力),使晶体管的等效导通电阻(输出电阻)较小。但这同时加大自载电容和负载电容(下一级晶体管的输入电容)。

  • 提高电源电压,提高电源电压可以降低延时,即用功耗换取性能但超过一定程度后改善有限。电压过高会引起可靠性问题(氧化层击穿、热电子等)。

Transition Time(转换时间):上升时间:从10%Vdd上升到90%Vdd的时间,下降时间L从90%Vdd下降到10%dd的时间。上升时间和下降时间统称为Transition Time,也有定义为20%到80%。

Propagation Delay(传播延时):在输入信号变化到50%Vdd到输出信号变化到50%Vdd之间的时间。

题目:什么是高阻态

高阻态:电路的一种输出状态,既不是高电平也不是低电平,如果高阻态再输入下一级电路的话,对下级电路无任何影响,可以理解为断路,不被任何东西所驱动,也不驱动任何东西

题目:什么是open-drain output?

Open-Drain Output漏极开路输出,称为OD门,两个OD门并联可以实现线与功能,输出端外接的上拉电阻提高驱动能力

image

题目:相同面积的cmos与非门和或非门哪个更快?

电子迁移率是空穴的2.5倍(在硅基CMOS工艺中),运算就是用这些大大小小的MOS管驱动后一级的负载电容,翻转速度和负载大小一级前级驱动能力相关。为了上升延迟和下降延迟相同,PMOS需要做成NMOS两倍多大小。

载流子的迁移率,对PMOS而言,载流子是空穴;对NMOS而言,载流子是电子。

PMOS采用空穴导电,NMOS采用电子导电,由于PMOS的载流子的迁移率比NMOS的迁移率小,所以,同样尺寸条件下,PMOS的充电时间要大于NMOS的充电时间长,在互补CMOS电路中,与非门是PMOS管并联,NMOS管串联,而或非门正好相反,所以,同样尺寸条件下,与非门的速度快,所以,在互补CMOS电路中,优先选择与非门。

题目:画出clock gating cell的原理图。

image

http://vlsi.pro/integrated-clock-gating-cell/

题目:解释一下亚稳态。

亚稳态指触发器的输出无法再某个规定时间段内达到一个可以确定的状态,介于0和1之间,如图中的2号小球既可能回到1状态,也可能达到3状态,亚稳态也是可以传输的,导致逻辑误判系统不稳定。亚稳态有恢复时间。解决亚稳态的方法

  • 降低系统时钟

  • 用更快的FF

  • 引入同步机制,防止亚稳态传播

  • 改善时钟质量

    image

解释一下Latch的建立时间与保持时间。

在Latch的输入开关断开和环路开关闭合时,有效的D输入信号在跳变前(建立时间)和跳变后(保持时间)的短时间内不能发生变化。以保证数据被锁存。

image

image

RTL代码

题目:多时钟域设计中,如何处理跨时钟域

  • 单bit:两级触发器同步(适用于慢到快)

  • 多bit:采用异步FIFO,异步双口RAM

  • 加握手信号

  • 格雷码转换

题目:编写Verilog代码描述跨时钟域信号传输,慢时钟域到快时钟域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
reg     [1:0]   signal_r;
//-------------------------------------------------------
//
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_r <= 2'b00;
end

else begin
signal_r <= {signal_r[0], signal_in};
end

end

assign signal_out = signal_r[1];

image

题目:编写Verilog代码描述跨时钟域信号传输,快时钟域到慢时钟域

​ 跨时钟域处理从快时钟域到慢时钟域,如果是下面第一个图,cklb则可以采样到signal_a_in,但是如果只有单脉冲,如第二个图,则不能确保采样掉signal_a_in。这个时候用两级触发器同步是没有用的。

image

image

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
代码如下:
//Synchronous
module Sync_Pulse(
input clka,
input clkb,
input rst_n,
input pulse_ina,
output pulse_outb,
output signal_outb
);

//-------------------------------------------------------
reg signal_a;
reg signal_b;
reg [1:0] signal_b_r;
reg [1:0] signal_a_r;

//-------------------------------------------------------
//在clka下,生成展宽信号signal_a
always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_a <= 1'b0;
end
else if(pulse_ina == 1'b1)begin
signal_a <= 1'b1;
end
else if(signal_a_r[1] == 1'b1)
signal_a <= 1'b0;
else
signal_a <= signal_a;
end

//-------------------------------------------------------
//在clkb下同步signal_a
always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_b <= 1'b0;
end
else begin
signal_b <= signal_a;
end
end

//-------------------------------------------------------
//在clkb下生成脉冲信号和输出信号
always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_b_r <= 2'b00;
end
else begin
signal_b_r <= {signal_b_r[0], signal_b};
end
end

assign pulse_outb = ~signal_b_r[1] & signal_b_r[0];
assign signal_outb = signal_b_r[1];

//-------------------------------------------------------
//在clka下采集signal_b[1],生成signal_a_r[1]用于反馈拉低signal_a
always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
signal_a_r <= 2'b00;
end
else begin
signal_a_r <= {signal_a_r[0], signal_b_r[1]};
end

end

endmodule

这部分代码参考:

作者:肉娃娃  

出处:https://home.cnblogs.com/u/rouwawa/

慢到快,单脉冲

image

慢到快,长信号传递

image

快到慢,单脉冲

image

单脉冲,长信号传递

image

上述代码可以实现快到慢,慢到快时钟域任意转换,pulse_outb会输出单个脉冲,signal_outb输出信号时间长度最少为clkb的四个周期,当signal_a_in的信号长度大于clkb的四个周期,signal_outb输出与signal_a_in时间长度相同。

题目:用Verilog实现1bit信号边沿检测功能,输出一个周期宽度的脉冲信号。

  • 上升沿
  • 下降沿
  • 上升沿或下降沿
1
2
input clk, rst_n, data; 
output data_edge;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module Edge_Detect(
input clk,
input rst_n,
input data,
output pos_edge,
output neg_edge,
output data_edge
);

reg [1:0] data_r;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_r <= 2'b00;
end
else begin
data_r <= {data_r[0], data};
end
end

assign pos_edge = ~data_r[1] & data_r[0];
assign neg_edge = data_r[1] & ~data_r[0];
assign data_edge = pos_edge | neg_edge;

endmodule

​ 怎么记忆:上升沿之前是0,现在变成1,所以上个周期传输到的signal_r[1]是0所以取反。反之亦然。

题目:用Verilog实现串并转换

  • lsb优先
  • msb优先
1
2
input clk, rst_n, data_i; 
output [7:0] data_o;
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
module Deserialize(
input clk,
input rst_n,
input data_i,
output reg [7:0] data_o
);

//lsb first
/*
reg [2:0] cnt;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_o <= 8'b0;
cnt <= 3'd0;
end
else begin
data_o[cnt] <= data_i;
cnt <= cnt + 1'b1;
end
end
*/

//msb first
reg [2:0] cnt;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_o <= 8'b0;
cnt <= 3'd0;
end
else begin
data_o[7 - cnt] <= data_i;
cnt <= cnt + 1'b1;
end
end

endmodule

题目:用verilog实现串并变换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
input [3:0] data_in;
output [3:0] data_out;
input [1:0] mode;
input clk;
input rst_n;

mode 0 :串行输入data_in[0],并行输出data_out[3:0]
mode 1 :并行输入data_in[3:0],串行输出data_out[0]
mode 2 :并行输入data_in[3:0],并行输出data_out[3:0],延迟1个时钟周期
mode 3 :并行输入data_in[3:0],并行反序输出data_out[3:0],延迟1个时钟周期并且交换bit顺序
data_out[3]=data_in[0];
data_out[2]=data_in[1]
data_out[1]=data_in[2]
data_out[0]=data_in[3]

附加要求【选做】
将输入输出的位宽做成参数化

可实现任意位宽设置

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
module Deserialize
#(
parameter DATA_WIDTH = 4,
parameter CNT_WIDTH = log2(DATA_WIDTH)
//parameter CNT_WIDTH = clog2(DATA_WIDTH-1)
)
(
input clk,
input rst_n,
input [1:0] mode,
input [DATA_WIDTH-1:0] data_i,
output reg [DATA_WIDTH-1:0] data_o
);

//mode 0
reg [DATA_WIDTH-1:0] data_r0;
reg data_r1;
reg [DATA_WIDTH-1:0] data_r2;
reg [DATA_WIDTH-1:0] data_r3;

reg [CNT_WIDTH-1:0] cnt;
reg [1:0] mode_r;
//mode change once cnt restart count
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
mode_r <= 0;
else
mode_r <= mode;
end
assign change = (mode_r ^ mode)? 1'b1: 1'b0;

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
cnt <= 0;
else if(change == 1'b1)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
data_r0 <= 4'b0;
else
data_r0[cnt] <= data_i[0];
end

//mode 1
always @(posedge clk or negedge rst_n)begin
if(rst_n == 0)
data_r1 <= 0;
else
data_r1 <= data_i[cnt];
end

//mode 2
always @(posedge clk or negedge rst_n)begin
if(rst_n == 0)
data_r2 <= 0;
else
data_r2 <= data_i;
end

integer i;
reg [DATA_WIDTH -1:0] data_r;
always @(posedge clk)begin
for(i = 0; i <= DATA_WIDTH-1; i = i+1)begin
data_r[DATA_WIDTH-1-i] <= data_i[i];
end
end

//mode 3
always @(posedge clk or negedge rst_n)begin
if(rst_n == 0)
data_r3 <= 0;
else
data_r3 <= data_r;
end

//mux4
always @(*)begin
case(mode)
2'b00: data_o = data_r0;
2'b01: data_o = {data_o[3:1], data_r1};
2'b10: data_o = data_r2;
2'b11: data_o = data_r3;
endcase
end

//-------------------------------------------------------
//以下两个函数任用一个
//求2的对数函数
function integer log2;
input integer value;
begin
value = value-1;
for (log2=0; value>0; log2=log2+1)
value = value>>1;
end
endfunction

//求2的对数函数
function integer clogb2 (input integer bit_depth);
begin
for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
bit_depth = bit_depth>>1;
end
endfunction

endmodule

题目:用verilog实现一个4bit二进制计数器。

  • 异步复位

  • 同步复位

1
2
input clk, rst_n; 
output [3:0] o_cnt;
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
module count_four_bit(
input clk,
input rst_n,
output reg [3:0] o_cnt
);

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
o_cnt <= 0;
end
else begin
o_cnt <= o_cnt + 1'b1;
end
end
/*
always @(posedge clk)begin
if(rst_n == 1'b0)begin
o_cnt <= 0;
end
else begin
o_cnt <= o_cnt + 1'b1;
end
end*/

endmodule

题目:用verilog实现4bit约翰逊(Johnson)计数器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module Johnson_Counter(
input clk,
input rst_n,
output reg [3:0] johnson_cnt
);

//----------------------------------------------------
//johnson_cnt
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
johnson_cnt <= 4'b0000;
else
johnson_cnt <= {~johnson_cnt[0], johnson_cnt[3:1]};
end

endmodule

题目: 用verilog实现4bit环形计数器:复位有效时输出0001,复位释放后依次输出0010,0100,1000,0001,0010…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module cnt(
input clk,
input rst_n,
output reg [3:0] cnt
);

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
cnt <= 4'b0001;
end
else begin
cnt <= {cnt[2:0],cnt[3]};
end
end

endmodule

题目:用verilog实现PWM控制呼吸灯。呼吸周期2秒:1秒逐渐变亮,1秒逐渐变暗。系统时钟24MHz,pwm周期1ms,精度1us。

led_out高电平占空比多,led较亮,反之,led较暗,实现呼吸灯效果

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
module Breath_LED(
input clk, //24Mhz
input rst_n,
output led_out
);

parameter DELAY24 = 24;
//parameter DELAY1000 = 1000;
parameter DELAY1000 = 10;//just test


wire delay_1us;
wire delay_1ms;
wire delay_1s;
reg pwm;
reg [7:0] cnt1;
reg [10:0] cnt2;
reg [10:0] cnt3;
reg display_state;

//延时1us
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt1 <= 6'b0;
else if(cnt1 == DELAY24 - 1'b1)
cnt1 <= 6'b0;
else
cnt1 <= cnt1 + 1'b1;
end

assign delay_1us = (cnt1 == DELAY24 - 1'b1)? 1'b1:1'b0;

//延时1ms
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt2 <= 10'b0;
else if(delay_1us == 1'b1)begin
if(cnt2 == DELAY1000 - 1'b1)
cnt2 <= 10'b0;
else
cnt2 <= cnt2 + 1'b1;
end
else
cnt2 <= cnt2;
end
assign delay_1ms = ((delay_1us == 1'b1) && (cnt2 == DELAY1000 - 1'b1))? 1'b1:1'b0;

//延时1s
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt3 <= 10'b0;
else if(delay_1ms)
begin
if(cnt3 == DELAY1000 - 1'b1)
cnt3 <= 10'b0;
else
cnt3 <= cnt3 + 1'b1;
end
else
cnt3 <= cnt3;
end

assign delay_1s = ((delay_1ms == 1'b1) && (cnt3 == DELAY1000 - 1'b1))? 1'b1:1'b0;

//state change
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
display_state <= 1'b0;
else if(delay_1s)//每一秒切换一次led灯显示状态
display_state <= ~display_state;
else
display_state <= display_state;
end

//pwm信号的产生
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
pwm <= 1'b0;
else
case(display_state)
1'b0: pwm <= (cnt2 < cnt3)? 1'b1:1'b0;
1'b1: pwm <= (cnt2 < cnt3)? 1'b0:1'b1;
default: pwm <= pwm;
endcase
end

//位拼接使得输出八位led呼吸灯
assign led_out = pwm;

endmodule

题目:序列检测器:有“101”序列输入时输出为1,其他输入情况下,输出为0。画出状态转移图,并用Verilog描述。

1
2
input clk, rst_n, data; 
output flag_101;

image

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
module Detect_101(
input clk,
input rst_n,
input data,
output flag_101
);

parameter S0 = 0,
S1 = 1,
S2 = 2,
S3 = 3;

reg [1:0] state;

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
state <= S0;
end
else begin
case(state)
S0:
if(data == 1)
state <= S1;
else
state <= S0;
S1:
if(data == 0)
state <= S2;
else
state <= S1;
S2:
if(data == 1)
state <= S3;
else
state <= S0;
S3:
if(data == 1)
state <= S1;
else
state <= S2;
endcase
end
end

assign flag_101 = (state == S3)? 1'b1: 1'b0;

endmodule

题目:用Verilog实现一个异步双端口ram,深度16,位宽8bit。A口读出,B口写入。支持片选,读写请求,要求代码可综合。

1
2
3
4
5
6
7
8
9
10
11
12
module dpram_16x8 ( 
input clk_a,
input [3:0] addr_a,
output [7:0] dout_a,
...
input clk_b,
input [7:0] din_b,
input [3:0] addr_b,
...
);
...
endmodule
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
module Dual_Port_Sram
#(
parameter ADDR_WIDTH = 4,
parameter DATA_WIDTH = 8,
parameter DATA_DEPTH = 1 << ADDR_WIDTH
)
(
input clka,
input clkb,
input rst_n,
input csen_n,
//Port A Signal
input [ADDR_WIDTH-1:0] addra,
output reg [DATA_WIDTH-1:0] data_a,
input rdena_n,
//Port B Signal
input [ADDR_WIDTH-1:0] addrb,
input wrenb_n,
input [DATA_WIDTH-1:0] data_b
);

integer i;
reg [DATA_WIDTH-1:0] register[DATA_DEPTH-1:0];

always @(posedge clkb or negedge rst_n)begin
if(rst_n == 1'b0)begin
for(i = 0; i < DATA_DEPTH; i = i + 1)
register[i] <= 'b0000_1111;
end
else if(wrenb_n == 1'b0 && csen_n == 1'b0)
register[addrb] <= data_b;
end

always @(posedge clka or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_a <= 0;
end
else if(rdena_n == 1'b0 && csen_n == 1'b0)
data_a <= register[addra];
else
data_a <= data_a;
end

endmodule

题目:用Verilog实现三分频电路,要求输出50%占空比。

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
module Div_three(
input clk,
input rst_n,
output div_three
);

reg [1:0] cnt;
reg div_clk1;
reg div_clk2;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
cnt <= 0;
end
else if(cnt == 2)
cnt <= 0;
else begin
cnt <= cnt + 1;
end
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
div_clk1 <= 0;
end
else if(cnt == 0)begin
div_clk1 <= ~div_clk1;
end
else
div_clk1 <= div_clk1;
end

always @(negedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
div_clk2 <= 0;
end
else if(cnt == 2)begin
div_clk2 <= ~div_clk2;
end
else
div_clk2 <= div_clk2;
end

assign div_three = div_clk2 ^ div_clk1;

endmodule

题目:用Verilog实现glitch free时钟切换电路。输入sel,clka,clkb,sel为1输出clka,sel为0输出clkb。

part1是比较垃圾的写法

part2 是两个时钟源是倍数的关系

part3是两个时钟源为异步时钟的关系

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
73
74
75
76
77
78
79
80
81
82
83
module Change_Clk_Source(
input clk1,
input clk0,
input select,
input rst_n,
output outclk
);

//-------------------------------------------------------
//part 1
//assign outclk = (clk1 & select) | (~select & clk0);

//-------------------------------------------------------
//part 2
reg out1;
reg out0;
always @(negedge clk1 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out1 <= 0;
end
else begin
out1 <= ~out0 & select;
end
end


always @(negedge clk0 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out0 <= 0;
end
else begin
out0 <= ~select & ~out1;
end
end

assign outclk = (out1 & clk1) | (out0 & clk0);
/*
//-------------------------------------------------------
//part 3
reg out_r1;
reg out1;
reg out_r0;
reg out0;

always @(posedge clk1 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out_r1 <= 0;
end
else begin
out_r1 <= ~out0 & select;
end
end

always @(negedge clk1 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out1 <= 0;
end
else begin
out1 <= out_r1;
end
end

always @(posedge clk0 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out_r0 <= 0;
end
else begin
out_r0 <= ~select & ~out1;
end
end

always @(negedge clk0 or negedge rst_n)begin
if(rst_n == 1'b0)begin
out0 <= 0;
end
else begin
out0 <= out_r0;
end
end

assign outclk = (out1 & clk1) | (out0 & clk0);
*/
endmodule

两个时钟源频率呈倍数关系

image

两个时钟源是异步时钟。

image

题目:用Verilog实现异步复位同步释放电路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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

题目:用Verilog实现按键抖动消除电路,抖动小于15ms,输入时钟12MHz。

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
module debounce(
input clk,//12Mhz
input rst_n,
input key_in,
output key_flag
);

parameter JITTER = 240;//12Mhz / (1/20ms)

reg [1:0] key_r;
wire change;
reg [15:0] delay_cnt;

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
key_r <= 0;
end
else begin
key_r <= {key_r[0],key_in};
end
end

assign change = (~key_r[1] & key_r[0]) | (key_r[1] & ~key_r[0]);

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
delay_cnt <= 0;
end
else if(change == 1'b1)
delay_cnt <= 0;
else if(delay_cnt == JITTER)
delay_cnt <= delay_cnt;
else
delay_cnt <= delay_cnt + 1;
end

assign key_flag = ((delay_cnt == JITTER - 1) && (key_in == 1'b1))? 1'b1: 1'b0;

endmodule

题目:用Verilog实现一个同步FIFO,深度16,数据位宽8bit。

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
module Syn_fifo
#(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4,
parameter RAM_DEPTH = (1 << ADDR_WIDTH)
)
(
input clk,
input rst_n,
input [DATA_WIDTH-1:0] data_in,
input wr_en,
input rd_en,
output reg [DATA_WIDTH-1:0] data_out,
output empty, //fifo empty
output full //fifo full
);

reg [ADDR_WIDTH-1:0] wr_cnt;
reg [ADDR_WIDTH-1:0] rd_cnt;
reg [ADDR_WIDTH-1:0] status_cnt;
reg [DATA_WIDTH-1:0] data_ram;

//-------------------------------------------------------
assign full = (status_cnt == (RAM_DEPTH-1))? 1'b1: 1'b0;
assign empty = (status_cnt == 0)? 1'b1: 1'b0;


//Syn
reg rd_en_r;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
rd_en_r <= 0;
end
else begin
rd_en_r <= rd_en;
end
end


//-------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
wr_cnt <= 0;
end
else if(wr_cnt == RAM_DEPTH-1)
wr_cnt <= 0;
else if(wr_en)begin
wr_cnt <= wr_cnt + 1'b1;
end
else
wr_cnt <= wr_cnt;
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
rd_cnt <= 0;
end
else if(rd_cnt == RAM_DEPTH-1)
rd_cnt <= 0;
else if(rd_en)begin
rd_cnt <= rd_cnt + 1'b1;
end
else
rd_cnt <= rd_cnt;
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_out <= 0;
end
else if(rd_en_r)begin
data_out <= data_ram;
end
end


always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
status_cnt <= 0;
end
else if(rd_en && !wr_en && (status_cnt != 0))begin
status_cnt <= status_cnt - 1;
end
else if(wr_en && !rd_en && (status_cnt != RAM_DEPTH-1))
status_cnt <= status_cnt + 1;
else
status_cnt <= status_cnt;
end

//-------------------------------------------------------
//Syn_Dual_Port_RAM
integer i;
reg [DATA_WIDTH-1:0] register[RAM_DEPTH-1:0];

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
for(i = 0; i < RAM_DEPTH; i = i + 1)
register[i] <= 0;
end
else if(wr_en == 1'b1)
register[wr_cnt] <= data_in;
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_ram <= 0;
end
else if(rd_en == 1'b1)
data_ram <= register[rd_cnt];
else
data_ram <= data_ram;
end

endmodule

Reference

http://www.asic-world.com/examples/verilog/syn_fifo.html

题目:IIC协议的RTL设计

https://www.cnblogs.com/ninghechuan/p/9534893.html

题目:Verilog设计异步FIFO

Verilog设计异步FIFO

题目:FIFO深度计算

异步FIFO深度为17,如何设计地址格雷码

https://www.embedded.com/print/4015117

https://patents.google.com/patent/CN101930350A/zh

FIFO最小深度计算

你问我FIFO有多深

NingHeChuan wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!