在 Verilog 中描述组合电路
Steve Arar 博士于 2019 年 1 月 6 日
本文通过研究如何使用条件运算符来描述组合真值表,介绍了在 Verilog 中描述组合电路的技术。
本文通过研究如何使用条件运算符来描述组合真值表,介绍了在 Verilog 中描述组合电路的技术。它还展示了如何利用 Verilog 的“always”块来描述组合电路——“always”块可以为我们提供更简单的解决方案来描述数字电路。
在上一篇文章中,我们讨论了使用 Verilog “assign”关键字来执行连续赋值。此类分配始终处于活动状态,可用于获取数字电路的门级描述。例如,在以下描述与门的代码中,会不断评估右侧并将结果放在 out1 网络上:
assign out1 =a &b;
Verilog 有一个条件运算符 (?:),它允许我们在进行此类赋值之前检查条件。语法如下:
assign [signal_name] =[conditional_expression] ? [value_if_true] :[value_if_false];
评估“conditional_expression”。如果为真,则将“value_if_true”分配给“signal_name”。如果它不是真的,“signal_name”会得到“value_if_false”。例如,考虑以下代码:
assign out1 =(sel) ? (a &b) :(a|b);
如果“sel”为真,a&b 将被分配给“out1”。如果它不是真的,“out1”将得到 a|b。因此,上述代码实现了 2 对 1 多路复用器的功能。这段代码的概念实现可以如下图1所示。
条件赋值允许我们对某些电路进行更抽象的描述,因为它具有传统计算机编程语言中的“if”语句的功能。可以以嵌套形式使用条件运算符来实现更复杂的电路。示例 1 讨论了这些细节。
示例 1:嵌套条件运算符
使用条件运算符 (?:) 描述具有以下真值表的 4 比 2 优先级编码器:
该优先级编码器的 Verilog 代码如下:
模块 Prio_4_to_2(输入线 [3:0] x,输出线 [1:0] y,输出线 v);分配 y =x[3] ? 2'b11 :x[2] ? 2'b10 :x[1] ? 2'b01 :2'b00;分配 v =( x[3] | x[2] | x[1] | x[0] ) ? 1'b1 :1'b0;结束模块
除了第 7 行到第 10 行之外,该代码还包含我们上一篇文章中讨论的基本语言元素。那么让我们来看看这些行。
术语 2’b11、2’b10、2’b01 是指表示两位二进制数的 Verilog 符号。通常,第一个数字(在‘b 之前)指定位数。字母 b 指定数字是二进制的。 ‘b 后面的数字给出了数字的值。因此,2’b01 是 Verilog 符号,表示值为 01 的两位二进制数,3’b100 表示值为 100 的三位二进制数。
第 7 行在条件运算符中检查输入 x[3] 的 MSB。如果 x[3]=1,则条件评估为真,并将 2'b11 分配给 y(分配值取自真值表)。如果 x[3]=0,则条件评估为假,冒号 (:) 后的表达式将分配给 y。冒号后面的表达式是第8行的代码,它本身就是另一个条件运算符。
第 8 行中的第二个条件运算符检查输入的第二个最高有效位 x[2],以确定是否应将 2'b10 分配给 y 或冒号后的表达式(再次是另一个条件运算符(第 9 行))被评估。您可以验证分配给 y 的值是否与给定的真值表匹配。
如果输入的至少一位为逻辑高,则真值表的有效输出 (v) 将为逻辑高。第 11 行通过将按位 OR 运算符 (|) 应用于输入的位来显示此描述。上述代码的 Xilinx ISE 仿真如图 2 所示。
图 2. 来自上述代码的 Xilinx ISE 仿真。
需要注意的是,条件表达式会连续求值,直到找到真正的表达式。将执行与此真表达式对应的赋值。因此,与下一个表达式相比,较早评估的表达式具有更高的优先级。这意味着,从理论上讲,条件运算符更适合实现优先级网络(图 3),而不是多路复用器(图 4)等平衡结构。
图 3。 优先网络。
图 4。 一个 n 对 1 多路复用器,其中输入之间没有优先级。
之前的一篇文章揭示了关于 VHDL 并发分配的类似讨论。
Verilog 程序语句
我们可以将任何组合电路分解为几个基本逻辑门(AND、OR、NOT 等),并使用“assign”语句来描述这些门(门级描述)。我们还可以使用上一节中讨论的条件运算符来更抽象地描述一些组合电路(类似于计算机编程语言的“if”语句)。然而,还有一个更强大的解决方案:使用 Verilog“always”块。
在“always”块中,我们可以有按顺序执行的过程语句。此外,“always”块支持抽象语言结构,例如“if”和“case”语句。
由于人类推理具有顺序性质并依赖于抽象描述,因此顺序执行功能以及“始终”块中可用的抽象语言结构使我们能够更轻松地描述电路的功能。我们通常以算法的高级方式而不是低级逻辑门的方式思考。 “always”块可以为我们提供一个更简单的解决方案来描述数字电路。有关 HDL 为什么支持基于顺序语句的描述的更多详细信息,请参阅我的文章“顺序 VHDL 语句简介”。
示例 2:“始终”块语句
下面给出了“always”块的简化语法:
总是@(sensitive_list) 开始sequential_statements;结束
敏感列表指定何时应该执行“always”块内的顺序语句。例如,考虑使用“always”块来描述图 5 中的电路。
图 5。 电路_1
当 a 或 b 更改时,输出可能会更改,这意味着 a 和 b 都应该在“always”块的敏感列表中。一般来说,对于组合电路,所有的输入信号都应该包括在灵敏度列表中。
现在,我们可以使用按位与运算符来描述电路 (a&b) 的功能并将结果分配给输出。在“always”块中,有两种不同类型的赋值:阻塞赋值(=)和非阻塞赋值(<=)。使用阻塞赋值,我们得到如下代码:
总是@(a, b)begin out1 =a &b;end
阻塞赋值和非阻塞赋值有什么区别?
对于阻塞赋值,右侧会被评估并立即赋值给 out1。因此,当执行第 3 行时,out1 会在我们转到下一行代码之前立即更新。 “阻塞分配”这个名称强调了即将到来的行被阻塞,直到左侧更新。
使用非阻塞赋值,右边的表达式被评估,但它不会应用于左边的变量,直到我们到达“always”块的末尾。阻塞或非阻塞赋值的选择可能会让初学者感到困惑,并且它们的不当使用会导致不想要的功能。例如,使用阻塞赋值来推断触发器可能会引入竞争条件。
对于这篇介绍性文章,我们不会进一步详细介绍,我们只会遵循一个简单的准则来避免潜在的陷阱:在为组合电路编写代码时使用阻塞赋值。因此,清单 1 中的“always”块将用于描述 AND 门。
在上一篇文章中,我们熟悉了 Verilog“wire”数据类型。这种数据类型代表我们 FPGA 设计中的一条物理线路。在“always”块中,Verilog 标准不允许我们为“wire”赋值。相反,我们使用“reg”数据类型。 “reg”这个名字有点令人困惑,但请注意,“reg”可能会也可能不会导致您设计中的物理存储元素。以下代码是使用“always”块的图 5 的 Verilog 描述。请注意,输出数据类型应为“reg”,因为它从过程赋值中获取其值。
模块电路_1(输入线a,输入线b,输出reg out1);总是@(a, b) begin out1 =a &b;结束模块
在本文中,我们熟悉了 Verilog 条件运算符。我们使用此运算符的嵌套形式来描述优先级编码器。然后,我们接触了一个更强大的语言结构,“always”块,来描述组合电路。在以后的文章中,我们将研究使用“always”块来实现时序电路。
嵌入式