[文档].艾米电子 – 逻辑门电路.[ModelSim][Quartus II][Verilog]
0赞对读者的假设
已经掌握:
内容
1 基本门电路
1.1 采用结构化描述方式
1. 使用Quartus II新建工程logic_gates,顶层模块名为logic_gates,如图1.1所示。
图1.1 新建工程logic_gates
2. 录入下面的代码。
01 |
` timescale 1ns/1ns |
02 |
module logic_gates( |
03 |
input iA, |
04 |
input iB, |
05 |
output oAnd, |
06 |
output oOr, |
07 |
output oNot |
08 |
); |
09 |
10 |
and and_inst(oAnd, iA,iB); |
11 |
or or_inst(oOr, iA,iB); |
12 |
not not_inst(oNot, iA); |
13 |
14 |
endmodule |
第10~12行,采用了门级原语,分别例化了与门、或门和非门。
1 |
and and_inst(oAnd, iA,iB); |
2 |
or or_inst(oOr, iA,iB); |
3 |
not not_inst(oNot, iA); |
3. 由于是基本的门电路,我们直接编译。编译成功后,选择Tools>Netlist Viewers>RTL Viewer,查看该工程综合后的RTL视图,如图1.2所示。
图1.2 RTL视图
4. 编写testbench。根据使用Verilog设计的Quartus II入门指南一文所述,在Quartus II软件中配置testbench,如图1.3所示。
01 |
` timescale 1ns/1ns |
02 |
03 |
module logic_gates_tb; |
04 |
05 |
reg i_a; |
06 |
reg i_b; |
07 |
wire o_and; |
08 |
wire o_or; |
09 |
wire o_not; |
10 |
11 |
initial |
12 |
begin |
13 |
i_a = 0; |
14 |
#40 i_a = 1; |
15 |
#40 i_a = 0; |
16 |
#40 i_a = 1; |
17 |
#40 i_a = 0; |
18 |
end |
19 |
20 |
initial |
21 |
begin |
22 |
i_b = 0; |
23 |
#40 i_b = 0; |
24 |
#40 i_b = 1; |
25 |
#40 i_b = 1; |
26 |
#40 i_b = 0; |
27 |
end |
28 |
29 |
logic_gates logic_gates_inst( |
30 |
.iA(i_a), |
31 |
.iB(i_b), |
32 |
.oAnd(o_and), |
33 |
.oOr(o_or), |
34 |
.oNot(o_not) |
35 |
); |
36 |
37 |
endmodule |
第11~18行和第20~17行的initial块,分别用作激励i_a和i_b。注意,initial块内的左手边信号,都必须声明称reg类型。而且initial块内的语句都是顺序执行的,#40表示在40时间单位时,信号发生变化。而begin后的第一句,则表示0时间单位时的初始值。注意,initial块不适合用在可综合的HDL代码中,仅适合用作仿真。
01 |
initial |
02 |
begin |
03 |
i_a = 0; |
04 |
#40 i_a = 1; |
05 |
#40 i_a = 0; |
06 |
#40 i_a = 1; |
07 |
#40 i_a = 0; |
08 |
end |
09 |
10 |
initial |
11 |
begin |
12 |
i_b = 0; |
13 |
#40 i_b = 0; |
14 |
#40 i_b = 1; |
15 |
#40 i_b = 1; |
16 |
#40 i_b = 0; |
17 |
end |
最后第29~35行,例化logic_gates模块。
1 |
logic_gates logic_gates_inst( |
2 |
.iA(i_a), |
3 |
.iB(i_b), |
4 |
.oAnd(o_and), |
5 |
.oOr(o_or), |
6 |
.oNot(o_not) |
7 |
); |
图1.3 配置testbench
5. 在Quartus II中,选择Tools>Run RDA Simulation Tools>EDA RTL Simulation,进行RTL仿真。在弹出的ModelSim_Altera窗口中,适当放大缩小波形,查看结果是否吻合,其波形如图1.4所示。
图1.4 仿真波形
6. 在Quartus II中,选择Tools>Run RDA Simulation Tools>Gate Level Simulation,进行门级仿真,如图1.5。
图1.5 门级仿真
仔细观察,将会发现,o_and、o_or和o_not,其初始值为未定态x,而期望的值则会延迟一段时间才会出现。具体的门级延迟视硬件不同而不同。而RTL仿真,则没有这个延迟;因此RTL仿真正确,不代表实际效果就正确,门级仿真的正确性也是一个重要的考察因素。
1.2 采用流描述方式
1. 替换先前的logic_gates.v为:
01 |
` timescale 1ns/1ns |
02 |
module logic_gates( |
03 |
input iA, |
04 |
input iB, |
05 |
output oAnd, |
06 |
output oOr, |
07 |
output oNot |
08 |
); |
09 |
10 |
assign oAnd = iA & iB; |
11 |
assign oOr = iA | iB; |
12 |
assign oNot = ~iA; |
13 |
14 |
endmodule |
第10~12行,使用了位运算符号,来处理与或非。 这是的描述组合逻辑电路的一种常用方式。
1 |
assign oAnd = iA & iB; |
2 |
assign oOr = iA | iB; |
3 |
assign oNot = ~iA; |
2. 重复1.1的第3步查看该工程综合后的RTL视图,如图1.6所示。
图1.6 RTL视图
3. 使用1.1的testbench,重复1.1的第5步查看功能仿真波形,如图1.7所示。
图1.7 功能仿真波形
1.3 采用行为描述方式
1. 替换先前的logic_gates.v为:
01 |
` timescale 1ns/1ns |
02 |
module logic_gates( |
03 |
input iA, |
04 |
input iB, |
05 |
output reg oAnd, |
06 |
output reg oOr, |
07 |
output reg oNot |
08 |
); |
09 |
10 |
always @ (*) |
11 |
begin |
12 |
oAnd = iA & iB; |
13 |
oOr = iA | iB; |
14 |
oNot = ~ iA; |
15 |
end |
16 |
17 |
endmodule |
第10~15行,在always块内,使用行为描述方式,来实现与或非门。注意:凡是出现在always块内,且在左手边的信号,都必须声明为reg类型,无论其最终是映射成寄存器抑或结点与否。这是用作描述组合逻辑电路的另一方式。注意,在描述组合逻辑电路时,always块内使用阻塞赋值方式(=)。
1 |
always @ (*) |
2 |
begin |
3 |
oAnd = iA & iB; |
4 |
oOr = iA | iB; |
5 |
oNot = ~ iA; |
6 |
end |
always @ (敏感信号),Verilog HDL 2001提供了一种更简便的方式:always @ (*) ,综合器自动加入敏感信号。第10行,便是这样处理的。
1 |
always @ (*) |
2. 重复1.1的第3步查看该工程综合后的RTL视图,如图1.8所示。
图1.8 RTL视图
3. 使用1.1的testbench,重复1.1的第5步查看功能仿真波形,如图1.9所示。
图1.9 功能仿真波形
1.4 小结
通过1.1~1.3的练习,我们发现3种描述方式所对应门电路其效果是一致的。
2 三态门电路
三态门,即其输出不仅与输入有关系,还受到使能信号控制;使能后,输出等于输入;未使能,则输出为高阻态。
1. 替换logic_gates.v为:
01 |
` timescale 1ns/1ns |
02 |
module logic_gates( |
03 |
input iA, |
04 |
input iEnable_n, |
05 |
output oTriState |
06 |
); |
07 |
08 |
assign oTriState = (~iEnable_n) ? iA : 1'bz ; |
09 |
10 |
endmodule |
第4行,iEnable_n为使能信号,低电平有效,常用后缀_n来暗示。
1 |
input iEnable_n, |
第8行,(条件) ? (条件为真时取值) : (条件为假时取值) ;用在此处描述三态门,若使能信号拉低,则输出等于输入;若使能信号拉高,则输出高阻。
1 |
assign oTriState = (~iEnable_n) ? iA : 1'bz ; |
2. 重新编译工程,查看其综合后的RTL视图,如图2.1 所示。
图2.1 三态门的RTL视图
3. 替换logic_gates_tb.v为:
01 |
` timescale 1ns/1ns |
02 |
03 |
module logic_gates_tb; |
04 |
05 |
reg i_a; |
06 |
reg i_enable_n; |
07 |
wire o_tri_state; |
08 |
09 |
initial |
10 |
begin |
11 |
i_a = 0; |
12 |
#40 i_a = 1; |
13 |
#40 i_a = 0; |
14 |
#40 i_a = 1; |
15 |
end |
16 |
17 |
initial |
18 |
begin |
19 |
i_enable_n = 1; |
20 |
#20 i_enable_n = 0; |
21 |
#40 i_enable_n = 1; |
22 |
#20 i_enable_n = 0; |
23 |
end |
24 |
25 |
logic_gates logic_gates_inst( |
26 |
.iA(i_a), |
27 |
.iEnable_n(i_enable_n), |
28 |
.oTriState(o_tri_state) |
29 |
); |
30 |
31 |
endmodule |
4. 查看其功能仿真与门级仿真是否正确,如图2.2和图2.3所示。
图2.2 功能仿真波形
图2.3 门级仿真波形
3 双向门电路
双向门电路,即可以输入又可以输出,常用于双向口。其实这个双向是时分复用,并不可以同时输入输出,因此它是单工的:也就是输入的时候,必须禁止输出使能;输出的时候,必须打开输出使能。注意:一般情况下,在子模块中,将双向口的拆分为输入和输出两个信号;最终只在顶层模块例化双向口。
1. 替换logic_gates.v为:
01 |
` timescale 1ns/1ns |
02 |
module logic_gates( |
03 |
input iA, |
04 |
input iW_R, |
05 |
output oB, |
06 |
inout ioC |
07 |
); |
08 |
09 |
assign ioC = iW_R ? iA : 1'bz ; |
10 |
assign oB = (~iW_R) ? ioC : 1'bz ; |
11 |
12 |
endmodule |
第9~10行,采用两个三态门,来实现双向门电路。
1 |
assign ioC = iW_R ? iA : 1'bz ; |
2 |
assign oB = (~iW_R) ? ioC : 1'bz ; |
下面的描述方式也常见于各种存储器电路中的双向数据总线描述。
1 |
assign ioC = iW_R ? iA : 1'bz ; |
2 |
assign oB = ioC; |
2. 重新编译工程,查看其综合后的RTL视图,如图3.1 所示。
图3.1 RTL视图
3. 这个testbench有点麻烦,此处用两个testbench先后测试双向口的读和写仿真。先做输出测试。替换logic_gates_tb.v为:
01 |
` timescale 1ns/1ns |
02 |
module logic_gates_tb; |
03 |
04 |
reg i_a; |
05 |
reg i_w_r; |
06 |
wire io_c; |
07 |
08 |
initial |
09 |
begin |
10 |
i_a = 0; |
11 |
#40 i_a = 1; |
12 |
#40 i_a = 0; |
13 |
#40 i_a = 1; |
14 |
end |
15 |
16 |
initial |
17 |
begin |
18 |
i_w_r = 1; |
19 |
#20 i_w_r = 0; |
20 |
#40 i_w_r = 1; |
21 |
#20 i_w_r = 0; |
22 |
end |
23 |
24 |
logic_gates logic_gates_inst( |
25 |
.iA(i_a), |
26 |
.iW_R(i_w_r), |
27 |
.oB(), |
28 |
.ioC(io_c) |
29 |
); |
30 |
31 |
endmodule |
4. 查看输出测试功能仿真与门级仿真是否正确,如图2.2和图2.3所示。
图2.2 输出测试功能仿真波形
图2.3 输出测试门级仿真波形
5. 再做输入测试,替换logic_gates_tb.v为:
01 |
` timescale 1ns/1ns |
02 |
module logic_gates_tb; |
03 |
04 |
reg i_w_r; |
05 |
wire o_b; |
06 |
wire io_c; |
07 |
08 |
reg io_c_reg; |
09 |
10 |
initial |
11 |
begin |
12 |
i_w_r = 1; |
13 |
#20 i_w_r = 0; |
14 |
#40 i_w_r = 1; |
15 |
#20 i_w_r = 0; |
16 |
end |
17 |
18 |
initial |
19 |
begin |
20 |
io_c_reg = 0; |
21 |
#40 io_c_reg = 1; |
22 |
#40 io_c_reg = 0; |
23 |
#40 io_c_reg = 1; |
24 |
end |
25 |
26 |
assign io_c = io_c_reg; |
27 |
28 |
logic_gates logic_gates_inst( |
29 |
.iA(), |
30 |
.iW_R(i_w_r), |
31 |
.oB(o_b), |
32 |
.ioC(io_c) |
33 |
); |
34 |
35 |
endmodule |
6. 适当增减信号,查看输出测试功能仿真与门级仿真是否正确,如图2.4和图2.5所示。
图2.4 输入测试的功能仿真波形
图2.5 输入测试的门级仿真波形
小结
这是我使用简单篇幅,描述了三类常见的门电路。希望初学者,能够学会和掌握,使用HDL描述电路,然后通过观察其综合的RTL视图来检测是否与所需 电路一致,通过仿真来测试其输入波形。诚然,学习FPGA不是一件简单的事情,除了扎实的学习态度以外,我们还需要多练多想多总结。不着急,慢慢来,也许 某一天,你就会发现,原来电路在我们心中。
辅助阅读
1. Wikipedia.Logic gate
2. Actel.Actel HDL Coding Style Guide - Actel HDL Coding
参考
1. 刘福奇, 刘波.Verilog HDL应用程序设计实例精讲.电子工业出版社