Verilog 提供两种主要的建模方法:行为建模和结构建模。结构建模聚焦于电路的具体物理结构描述,而行为建模则侧重于电路功能的描述,类似于高级编程语言的风格,如C语言,不涉及底层实现细节。
3.1 数据流行为建模
数据流建模是一种简洁的行为建模方式,核心在于使用assign
关键字进行连续赋值,适用于描述组合逻辑电路。
连续赋值语句
- 语法:
assign LHS_target = RHS_expression;
LHS_target
: 目标线网RHS_expression
: 赋值操作表达式
示例:
Verilog
wire[3:0] z, preset, clear; assign z = preset & clear;
- 赋值目标: 包括标量线网、向量线网、向量的位选择、向量的部分选择及这些类型的连接结果。
- 执行机制: 右侧操作数变化时,表达式重新计算并将新结果赋给左侧线网。
- 时延: 支持上升时延、下降时延和关闭时延,语法为
assign #(rise, fall, turn-off) LHS_target = RHS_expression;
。
线网声明赋值
线网可在声明时直接赋值,例如:
Verilog
wire[3:0] sum = 4'b0;
时延的概念
- 时延: 定义程序暂停的单位时间长度,默认时延为0。
- 线网时延: 时延可内置于线网声明中,用于控制赋值的时机。
数据流建模实例:一位全加器
使用数据流建模方式实现一位全加器,可以直观展示连续赋值语句的使用。
Verilog
module FullAdder(input a, b, cin, output sum, cout); wire carry; assign sum = a ^ b ^ cin; assign carry = (a & b) | (a & cin) | (b & cin); assign cout = carry | (sum & cin); endmodule
在这个例子中,a
、b
和cin
是输入,sum
和cout
是输出。sum
是半加器的输出,而cout
是进位输出,通过连续赋值语句assign
直接计算得出。
3.2 语句块
3.2.1 顺序行为建模:Initial与Always语句
在Verilog的顺序行为描述中,initial
和always
语句是构建动态逻辑行为的核心。它们不仅限于单一语句,而是引导了一系列的过程或结构,允许更复杂的控制流和时序逻辑的描述。
Initial语句
- 执行时机:仅执行一次,在仿真启动的瞬间(时刻0)。
- 封装结构:通常采用
begin-end
块封装多条语句,形成顺序执行的语句块。 - 局部变量:若语句块中使用局部变量,如
integer Index;
,则需命名该语句块。
示例:
Verilog
parameter SIZE = 1024; reg[7:0] RAM [0:SIZE-1]; reg RibReg; initial begin: SEQ_BLK_A integer Index; RibReg = 0; for (Index = 0; Index < SIZE; Index = Index + 1) RAM[Index] = 0; end
Always语句
- 执行时机:从时刻0开始,依据时序控制条件重复执行。
- 时序控制:通过
@
符号定义事件触发或使用wait
关键字定义电平敏感事件。 - 执行模式:与
initial
不同,always
可根据条件多次触发执行。
语法格式:
Verilog
1always [timing_control] procedural_statement
其中,
timing_control
:时序控制,可为时延控制或事件控制。procedural_statement
:过程语句,描述具体的逻辑行为。
示例:
Verilog
always @(posedge clk or negedge rst) if (!rst) Q <= 1'b0; else Q <= D;
小结
在Verilog设计中,initial
和always
语句是实现顺序行为建模的关键工具。initial
语句适用于一次性初始化,而always
语句则用于响应特定事件或条件的重复执行,两者结合提供了灵活的时序逻辑描述能力。通过适当的封装和时序控制,可以精确控制电路的动态行为,满足复杂逻辑设计的需求。
3.2.2 时序控制
时序控制是Verilog中用于精确控制电路行为的重要机制,主要包括时延控制和事件控制两大类。
时延控制
时延控制允许在指定的时间间隔后执行操作,主要形式包括:
- 语句前时延
- 单独时延
- 语句内时延
示例:
Verilog
Done = #5 (A & B);
等价于:
Verilog
begin tmp = (A & B); #5 Done = tmp; end
值得注意的是,显示零时延(#0
)将导致当前仿真时间点的执行挂起,直到所有同时发生的事件处理完毕。
事件控制
事件控制通过监测特定事件的发生来触发操作,分为边沿触发事件和电平敏感事件。
边沿触发事件
使用@
符号定义,可监测一个或多个事件,多个事件间使用or
关键字分隔。
示例:
Verilog
always @(posedge clk or negedge rst) if (!rst) Q <= 1'b0; else Q <= D;
电平敏感事件
使用wait
关键字定义,等待条件满足后执行后续过程。
示例:
Verilog
wait (condition); // process statements
其中,condition
定义了电平敏感事件的触发条件。
小结
通过时延控制和事件控制,Verilog提供了丰富的机制来精确控制电路的时序行为。无论是简单的时延操作,还是复杂的边沿触发和电平敏感事件,都能有效地模拟和控制电路的动态特性。理解并熟练运用这些时序控制机制,是Verilog设计中不可或缺的一部分。
3.2.3 Verilog中的语句块与赋值机制
在Verilog中,语句块分为两类:顺序语句块和并行语句块,分别用于控制语句的执行顺序和并行度。
- 顺序语句块:使用
begin-end
结构,确保语句按顺序执行。 - 并行语句块:使用
fork-join
结构,使语句块中的语句并行执行,不依赖于书写顺序。
过程性赋值
过程性赋值是Verilog中最常用的赋值形式,特点如下:
- 出现在
initial
和always
语句中。 - 专用于寄存器变量赋值。
- 右侧表达式可以是任意类型。
过程性赋值分为阻塞和非阻塞两种:
- 阻塞过程赋值:使用
=
操作符,执行赋值时会阻塞后续语句。 - 非阻塞过程赋值:使用
<=
操作符,赋值与后续语句并行执行。
混合使用阻塞与非阻塞赋值
在Verilog设计中,阻塞与非阻塞赋值的混合使用是常见的,需要根据设计需求和性能考虑来决定。
过程性连续赋值
不同于标准的连续赋值,过程性连续赋值结合了过程赋值的灵活性和连续赋值的响应性,支持对寄存器和线网赋值。
- assign-deassign:用于寄存器赋值,
assign
赋值,deassign
取消赋值。 - force-release:用于线网和寄存器的强制赋值和释放,
force
覆盖当前值,release
恢复原状。
示例代码
以下示例代码,展示assign-deassign
和force-release
的使用:
Verilog
module DEF(D, Clr, Clk, Q); input D, Clr, Clk; output Q; reg Q; always @(negedge Clk) Q = D; always @(Clr) if (!Clr) begin assign Q = 0; end else begin deassign Q; end endmodule // Force-release example module ForceRelease(Colt); reg[2:0] Colt; wire prt; or #1 (prt, Std, Dzx); // Or gate definition initial begin Colt = 2; force Colt = 1; release Colt; assign Colt = 5; force Colt = 3; release Colt; end initial begin force prt = Dzx & Srd; #5; release prt; end endmodule
以上代码展示了assign-deassign
和force-release
的使用,以及如何通过fork-join
和begin-end
块来控制语句的执行顺序和并行性。通过这些机制,Verilog提供了强大的手段来描述和控制电路行为。
3.2.5 If语句
If语句用于条件分支控制,如果一个分支包含多条过程语句,应使用begin-end
块将其包裹,类似于C语言中的大括号{}
。
3.2.6 Case语句
Case语句用于多路分支选择,特别指出的是,当条件表达式或分支表达式中的某些位出现未知值x
或高阻态z
时,这些值在比较中仍具有意义,只要在相同位置出现x
或z
,就被视为相等。
- casex和casezcasex和casez语句提供了特殊处理
x
和z
的方式:
casez
:在比较中,值z
被视为无关值,即在该位出现z
时,该位在比较中被忽略。casex
:值x
和z
都被视为无关位,只要其余位匹配,即认为两个值相等。
3.2.7 循环语句
循环语句允许重复执行一组过程语句,Verilog提供了多种循环控制结构。
- forever循环
forever循环持续不断地执行过程语句,无需声明循环变量。其语法格式为: - Verilog
forever procedural_statement
- 注意:forever循环中必须包含时序控制,否则将在0时延后无限循环。forever循环常用于生成周期性波形,作为仿真测试信号。
- repeat循环
repeat循环执行固定次数的过程语句,通过loop_count
控制循环次数。语法格式如下: - Verilog
repeat(loop_count) procedural_statement
- 若
loop_count
为不确定值(如x
或z
),则循环次数被视为0。
示例: - Verilog
repeat(Count) @ (posedge Clk) Sum <= Sum + 1;
- 此示例中,首先计算
Sum + 1
的值,然后等待Clk
的上升沿,最后执行赋值操作。 - while循环
while循环在条件为真时重复执行过程语句,直至条件变为假。 - for循环
for循环提供了一种结构化的循环控制,包括初始化、条件检查和循环变量更新三个部分。
示例代码
以下示例代码,展示了Verilog中的控制结构使用:
Verilog
module ControlStructures( input wire clk, input wire reset, output reg [3:0] counter ); always @(posedge clk or posedge reset) begin if (reset) begin counter <= 4'b0; end else begin if (counter == 4'b1111) begin counter <= 4'b0; end else begin counter <= counter + 1; end end end always @(counter) begin case(counter) 4'b0000: output <= "Zero"; 4'b0001: output <= "One"; 4'b0010: output <= "Two"; default: output <= "Other"; endcase end initial begin forever #10 counter <= counter + 1; end always @(counter) begin repeat(4) @(negedge clk); end endmodule
以上代码展示了if语句、case语句、forever循环以及repeat循环的使用,体现了Verilog中控制结构的多样性和灵活性。