本节导读 在数字电路中可以根据电路功能的不同分为,组合逻辑电路与时序逻辑电路。组合逻辑电路在逻辑功能上的特点是任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关。而时序逻辑从电路特征上看来,其特点为任意时刻的输出不仅取决于该时刻的输入,而且还和电路原来的状态有关。组合逻辑电路在电路结构上,不涉及对信号跳变沿的处理,无存储电路,也没有反馈电路,通常可以通过真值表的形式表达出来。时序逻辑电路在电路结构上,不管输入如何变化,仅当时钟的沿(上升沿或下降沿)到达时,才有可能使输出发生变化。 在本节中将通过一个组合逻辑电路的设计再次熟悉QuartusPrime工程的建立以及完整的FPGA开发流程,并以译码器为例学习简单的组合逻辑电路设计。 1.1.1 译码器工作原理译码器(Decoder)是一种多输入多输出的组合逻辑电路,负责将二进制代码翻译为特定的对象(如逻辑电平等),功能与编码器相反。译码器一般分为通用译码器和数字显示译码器两大类。本节设计的是通用译码器,数字显示译码器会在数码管驱动设计章节中详细介绍。 以三八译码器为例,即将3种输入状态翻译成8种输出状态,其真值表如表3.1所示,其中ABC为数据输入,OUT为数据输出。在MCU应用中,如果需要保证一定的速度情况下实现此功能,一般选取外挂一片74HC38或者74LS38等独立芯片,但FPGA提供了一个完整的想象以及实现空间,仅靠其自身即可实现设计要求。 xxx译码器真值表 1.1.2 译码器Verilog实现依据前面相关章节所述,建立工程子文件夹后,新建一个以名为decoder3_8的工程保存在prj下,并在本工程目录的rtl文件夹下新建verilog file文件并以decoder3_8.v保存。 module decoder3_8( a, b, c, out ); //代码部分见下 endmodule | 根据表7.1的真值表可以看出,本模块有a、b、c三个输入端以及一个8bit的输出端out。这样就可以得出如图 3.1‑1所示的模块接口图以及表3.2的接口功能描述
图 3.1 1译码器模块接口图
由表3.2可以得出如下的接口列表。对接口列表进行定义。 input a;//输入端口A input b;//输入端口B input c;//输入端口C
output [7:0]out;//输出端口 |
现在使用数据选择器来实现表3.1的功能。由于此处已经将case中的所有情况穷举,因此可以不用书写default。{}为位拼接操作符,always@()括号内为敏感信号列表。由于输出数据out在always块中赋值,再将其修改为reg类型。 reg [7:0]out; always@(a,b,c)begin case({a,b,c}) 3'b000:out = 8'b0000_0001; 3'b001:out = 8'b0000_0010; 3'b010:out = 8'b0000_0100; 3'b011:out = 8'b0000_1000; 3'b100:out = 8'b0001_0000; 3'b101:out = 8'b0010_0000; 3'b110:out = 8'b0100_0000; 3'b111:out = 8'b1000_0000; endcase end | 1.1.1 激励创建及仿真测试将上面的设计内容进行分析和综合直至没有错误以及警告。 为测试仿真编写测试激励文件,新建decoder3_8_tb.v文件保存到testbench文件夹下。 module decoder3_8_tb; //具体设计见下 endmodule |
首先设置仿真时间单位以及仿真精度,格式为:timescale 单位/精度。例如1ns/100ps,这样在设置延时#100,就代表着延时100*1ns。并且可以延时100.1ns。如果想延时100.0001ns则可以将仿真精度修改为1fs。仿真精度越高电脑所需要的仿真时间就越长,因此需要根据实际情况来进行定义。下面的这种常用的书写方式就代表着仿真时间单位为1ns,仿真精度为1ns。 要对decoder3_8.v进行仿真,就需要对其输入端口施加激励来观测输出端口的输出波形,与理论的输出波形进行对比,进而判断逻辑代码编写是否正确。端口声明如下,在待仿真文件中是输入类型的信号需要在仿真文件设置为reg类型;在待仿真文件中输出类型的信号需要在仿真文件中设置为wire类型。这样就有了下面的端口声明: reg a; reg b; reg c; wire [7:0] out; |
再将待测试文件例化到仿真文件中。有两种例化方式,第一种方式如下,调用decoder3_8模块为u1元件名。这种例化方式代码显得简洁,但是要求在例化模块中的信号排列顺序要与被调用模块中的信号排列顺序严格一致。 decoder3_8 u1(a,b,c,out); |
在实际工程中多采用的是第二种例化方式如下,调用decoder3_8模块为decoder3_8元件名。这种例化方式略显复杂,但是信号的顺序可以随便变更,在较多接口的模块调用时不容易出错。此处可随意调整a、b、c或者out的顺序进行编译。 decoder3_8 decoder3_8( .a(a), .b(b), .c(c), .out(out) ); |
相关定义后,编写的任意组合状态的输入信号产生激励信号,如下 initial begin a = 0;b = 0;c = 0; //在0时刻三个输入均为0 #200; //经过200ns的延时 a = 0;b = 0;c = 1; //在200ns时输入信号的值 #200; //又经过200ns的延时 a = 0;b = 1;c = 0; //在400ns时输入信号的值 #200; //又经过200ns的延时 a = 0;b = 1;c = 1; //在600ns时输入信号的值 #200; a = 1;b = 0;c = 0; #200; a = 1;b = 0;c = 1; #200; a = 1;b = 1;c = 0; #200; a = 1;b = 1;c = 1; #200; $stop; //停止仿真 end |
再次进行分析和综合直至没有错误以及警告。 设置好仿真脚本后进行功能仿真,可以看到如图 3.1‑2所示的波形。首先可以看出输入信号a、b、c每一时刻的状态与仿真文件中的赋值情况一致。再观察输出信号out,发现当输入为000时输出为0000_0001;当输入为001时输出为0000_0010。可自行分析其他状态,最终得出逻辑代码符合既定的设计要求,至此功能仿真结束。 图 3.1‑2 功能仿真波形 点击RTL viewer,可以看到图 3.1‑3的电路结构,可以看出综合出来为三输入八输出的译码器模块符合预期目的。进行全编译来实现布局布线,再进行后仿真也就是时序仿真。 图 3.1‑3 设计模块RTL视图 进行时序仿真,在图 3.1‑4中可以观察发现整体数据均存在一定的延迟现象,但功能依然符合设计要求。 图 3.1‑4 时序仿真波形 序仿真局部波形如图 3.1‑5,放大0ns处可得图 3.1‑6以及图 3.1‑7所示部分波形,可看出,刚开始上电时由于输入数据需要等待一段时间才会到达输出端口,因此存在一定时间的未知态,并在0000_0001到0000_0010转换中出现了0000_0011(也可能是0000_0000)这一数据。这是由于门电路在上面两个状态转换中,其实际转换过程为0000_0001→0000_0011→0000_0010或者0000_0001→0000_0000→0000_0010。对于转换过程中出现的错误数据只能在后期进行数据筛选而没有办法避免。 图 3.1‑5 局部放大波形 图 3.1‑6 数据转换部分放大波形 图 3.1‑7 数据转换部分放大波形 从以上的波形分析,可以发现本章设计的3-8译码器功能仿真与时序仿真均工作正常,因此这样就完成了一个基本的组合逻辑设计──3-8译码器的设计。
本节中以3-8译码器为例学习了简单的组合逻辑设计,并且以此复习了FPGA的开发软件及开发流程。本章结束后可以自行设计选择器、译码器以及加法器等其他组合逻辑电路,并进行仿真,进一步加深对组合逻辑的理解以及激励文件的编写能力。
|