always块(组合)
由于数字电路是由用导线连接的逻辑门组成的,因此任何电路都可以表示为模块和赋值语句的某种组合。然而,有时这并不是描述电路的最方便的方式。过程(其中总是块是一个示例)提供了一种用于描述电路的替代语法。
对于综合硬件,有两种类型的 always 块是相关的:
组合:always @( * )
时序逻辑:always @(posedge clk)
组合always块等效于assign语句,因此总有一种方法可以双向表达组合电路。选择使用哪个,主要是哪个语法更方便的问题。程序块内部代码的语法与外部代码不同。程序块具有更丰富的语句集(例如,if-then、case),不能包含连续赋值*,但也引入了许多新的非直观的错误方式。 (*过程连续赋值确实存在,但与连续赋值有些不同,并且不可合成。)
例如,assign 和combinational always 块描述了相同的电路。两者都创建了相同的组合逻辑块。每当任何输入(右侧)更改值时,两者都会重新计算输出。
assign out1 = a & b | c ^ d;
always @() out2 = a & b | c ^ d;
对于组合 always 块,始终使用()的敏感度列表。明确列出信号容易出错(如果您错过了),并且在硬件综合时会被忽略。如果您明确指定敏感度列表并遗漏了一个信号,合成的硬件仍然会像指定了(*)一样运行,但模拟不会也不匹配硬件的行为。(在 SystemVerilog 中,使用always_comb。)
关于 wire 与 reg 的说明:assign 语句的左侧必须是net类型(例如,wire),而过程赋值(在 always 块中)的左侧必须是变量类型(例如,reg)。这些类型(wire 与 reg)与合成的硬件无关,只是 Verilog 用作硬件模拟语言时遗留下来的语法。
练习
使用assign 语句和组合always 块构建AND 门。
模块声明
// synthesis verilog_input_version verilog_2001 module top_module( input a, input b, output wire out_assign, output reg out_alwaysblock );
答案
// synthesis verilog_input_version verilog_2001 module top_module( input a, input b, output wire out_assign, output reg out_alwaysblock ); assign out_assign = a & b; always @(*)begin out_alwaysblock = a & b; end endmodule
always块(时序)
对于硬件综合,有两种相关的always块:
组合:always @( * )
时序逻辑:always @(posedge clk)
时钟always块创建组合逻辑块,就像组合总是块一样,但也在组合逻辑块的输出处创建一组触发器(或“寄存器”)。逻辑块的输出不是立即可见,而是仅在下一个 (posedge clk) 之后立即可见。
阻塞与非阻塞分配
Verilog 中有三种类型的赋值:
连续赋值:(assign x = y;)。只能在不在过程内部时使用(“始终阻塞”)。
程序阻塞赋值:( x = y; )。只能在程序内部使用。
程序非阻塞赋值:( x <= y; )。只能在程序内部使用。
在一个组合always块,使用阻塞分配。在时序always块中,使用非阻塞分配。充分理解为什么对硬件设计不是特别有用,需要很好地理解 Verilog 模拟器如何跟踪事件。不遵循此规则会导致极难发现仿真和综合硬件之间的不确定性和不同的错误。
练习
以三种方式构建 XOR 门,使用赋值语句、组合的 always 块和时序的 always 块。请注意,时钟始终块产生与其他两个不同的电路:有一个触发器,因此输出被延迟。
模块声明
// synthesis verilog_input_version verilog_2001 module top_module( input clk, input a, input b, output wire out_assign, output reg out_always_comb, output reg out_always_ff );
答案
// synthesis verilog_input_version verilog_2001 module top_module( input clk, input a, input b, output wire out_assign, output reg out_always_comb, output reg out_always_ff ); assign out_assign = a^b; always @(*)begin out_always_comb = a ^ b; end always @(posedge clk)begin out_always_ff <= a ^ b; end endmodule
if语句
个如果语句通常会产生一个2至1多路复用器,选择如果该条件为真一个输入端,而另一个输入,如果条件为假。
always @(*) begin if (condition) begin out = x; end else begin out = y; end end
这等效于使用带有条件运算符的连续赋值:
assign out = (condition) ? x : y;
然而,程序化的if语句提供了一种新的出错方式。只有当out总是被分配一个值时,电路才是组合的。
练习
构建一个在a和b之间进行选择的 2 对 1 多路复用器。选择b如果两个 sel_b1和sel_b2是真实的。否则,选择一个。做同样的事情两次,一次使用assign语句,一次使用过程if 语句。
Module Declaration
// synthesis verilog_input_version verilog_2001 module top_module( input a, input b, input sel_b1, input sel_b2, output wire out_assign, output reg out_always );
答案
// synthesis verilog_input_version verilog_2001 module top_module( input a, input b, input sel_b1, input sel_b2, output wire out_assign, output reg out_always ); assign out_assign = (sel_b1 == 1 && sel_b2 == 1)? b : a ; always @(*)begin if(sel_b1==1 && sel_b2 == 1) out_always = b; else out_always = a; end endmodule