在数字电路中可以根据电路功能的不同分为组合逻辑电路与时序逻。
组合逻辑电路在逻辑功能上的特点是任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。
而时序逻辑电路在逻辑功能上的特点是任意时刻的输出不仅取决于当时的输入信号,而且还取决于电路原来的状态,或者说,还与以前的输入有关。
在次中将通过一个组合逻辑电路的设计再次熟悉 Quartus Prime 工程的建立以及完整的 FPGA 开发流程,并以138译码器为例学习简单的组合逻辑电路设计。
译码器是一种具有“翻译”功能的逻辑电路,这种电路能将输入二进制代码的各种状态,按照其原意翻译成对应的输出信号。以常见的138译码器为例,这是其真值表:
我们忽略掉其它信号,仅看输入(A)和输出(Y’),我们可以看到,每一个二进制输入对应着一个低电平输出。
从上图中,我们可以得到模块的接口图:
根据这个模块,我们可以写出接口列表:module decoder_138(
a0,
a1,
a2,
out_n
);
input a0;
input a1;
input a2;
output reg [7:0] out_n;//输出为8位,低电平有效
在根据138的真值表,我们可以写出译码部分的代码:
always @ (a0 or a1 or a2)
begin
case({a0,a1,a2})
3'b000:out_n = 8'b1111_1110;
3'b001:out_n = 8'b1111_1101;
3'b010:out_n = 8'b1111_1011;
3'b011:out_n = 8'b1111_0111;
3'b100:out_n = 8'b1110_1111;
3'b101:out_n = 8'b1101_1111;
3'b110:out_n = 8'b1011_1111;
3'b111:out_n = 8'b0111_1111;
endcase
end
代码写完以后,我们怎么验证代码是否正确呢?难道我们要像单片机一样,将"代码"烧到板子上验证,这样未免太麻烦了。我们可以使用Modelsim进行仿真验证。
首先是仿真时间单位以及仿真精度。
`timescale 1ns/1ns //仿真时间单位为1ns,仿真精度为1ns。
然后是被测试模块的端口和例化,需要注意的是在仿真脚本中,输入只能是reg型,输出只能是wire型。同时注意位宽。
module decoder_138_tb;
reg a0;
reg a1;
reg a2;
wire [7:0] out_n;
decoder_138 decoder0(
.a0(a0),
.a1(a1),
.a2(a2),
.out_n(out_n)
);
例化模块的时候注意,前面是元件名,后面是模块名,这两个之间的关系就类似于这样:
C语言定义变量 | int | a |
---|---|---|
模块例化 | decoder_138 | decoder0 |
激励信号是什么呢,说得简单点就是模块的输入。
integer i; //int型变量
initial begin
i = 0;
a0 = 0;
a1 = 0;
a2 = 0;
#200 //延时200ns
for(i=0;i<8;i=i+1)
begin
{a0,a1,a2} = i;//改变输入值,只会取i的低三位
#200;
end
$stop;
end
在这里稍微偷了下懒,用了一个位拼接和for循环来赋值,其实应该这么写:
initial begin
a0 = 0; a1 = 0; a2 = 0; //初始三个输入均为 0
#200; //200ns 的延时
a0 = 0; a1 = 0; a2 = 1;
#200;
a0 = 0; a1 = 1; a2 = 0;
............
这样虽然很麻烦,但是很直观。
确认没什么问题以后,设置好仿真脚本,就可以开始仿真了,这是译码器的仿真结果:
从图中可以看出,输入输出关系和138译码器的真值表一模一样。 也许这样不太好分析输入与输出的关系,我们可以将输入信号组合起来。 这样就很输入与输出的关系就很直接了。 从以上的仿真可以看出,本次设计的138译码器仿真结果与真值表完全一样,并无任何异常。除了译码器以外,还可以设计编码器,数据选择器,全加器等等一些其它的组合逻辑电路。