- VLSI设计教程
- VLSI设计 - 首页
- VLSI设计 - 数字系统
- VLSI设计 - FPGA技术
- VLSI设计 - MOS晶体管
- VLSI设计 - MOS反相器
- 组合MOS逻辑电路
- 时序MOS逻辑电路
- VHDL编程
- VHDL - 简介
- VHDL - 组合电路
- VHDL - 时序电路
- Verilog
- Verilog - 简介
- 行为建模与时序
- VLSI设计有用资源
- VLSI设计 - 快速指南
- VLSI设计 - 有用资源
- VLSI设计 - 讨论
Verilog中的行为建模与时序
Verilog中的行为模型包含过程语句,这些语句控制仿真并操作数据类型的变量。所有这些语句都包含在过程中。每个过程都与其关联的活动流。
在行为模型的仿真过程中,由“always”和“initial”语句定义的所有流程在仿真时间“零”时同时开始。initial语句执行一次,always语句重复执行。在此模型中,寄存器变量a和b分别在仿真时间“零”时初始化为二进制1和0。然后initial语句完成,并且在该仿真运行期间不再执行。此initial语句包含一个begin-end块(也称为顺序块)的语句。在此begin-end类型块中,a先初始化,然后是b。
行为建模示例
module behave; reg [1:0]a,b; initial begin a = ’b1; b = ’b0; end always begin #50 a = ~a; end always begin #100 b = ~b; end End module
过程赋值
过程赋值用于更新reg、integer、time和memory变量。过程赋值和连续赋值之间存在显着差异,如下所述:
连续赋值驱动net变量,并在任何输入操作数的值发生变化时进行评估和更新。
过程赋值在围绕它们的程序流程构造的控制下更新寄存器变量的值。
过程赋值的右侧可以是任何计算为值的表达式。但是,右侧的部分选择必须具有常量索引。左侧指示从右侧接收赋值的变量。过程赋值的左侧可以采用以下形式之一:
寄存器、整数、实数或时间变量 - 对这些数据类型之一的名称引用进行赋值。
寄存器、整数、实数或时间变量的位选择 - 对单个位进行赋值,同时保持其他位不变。
寄存器、整数、实数或时间变量的部分选择 - 两个或多个连续位的部分选择,同时保持其余位不变。对于部分选择形式,仅允许常量表达式。
存储器元素 - 存储器的一个字。请注意,存储器元素引用不允许位选择和部分选择。
上述任何一种的连接 - 可以指定上述四种形式的任何一种的连接,这有效地将右侧表达式的结果进行分区,并按顺序将分区部分分配给连接的各个部分。
赋值中的延迟(不适用于综合)
在延迟赋值中,在执行语句并进行左侧赋值之前,会经过Δt个时间单位。使用赋值内延迟,右侧会立即计算,但结果会在Δt之后放置到左侧赋值。如果另一个过程在Δt期间更改右侧信号,则不会影响输出。综合工具不支持延迟。
语法
过程赋值变量 = 表达式
延迟赋值#Δt 变量 = 表达式;
赋值内延迟变量 = #Δt 表达式;
示例
reg [6:0] sum; reg h, ziltch; sum[7] = b[7] ^ c[7]; // execute now. ziltch = #15 ckz&h; /* ckz&a evaluated now; ziltch changed after 15 time units. */ #10 hat = b&c; /* 10 units after ziltch changes, b&c is evaluated and hat changes. */
阻塞赋值
阻塞过程赋值语句必须在顺序块中跟随它的语句执行之前执行。阻塞过程赋值语句不会阻止顺序块中跟随它的语句的执行。
语法
阻塞过程赋值的语法如下:
<lvalue> = <timing_control> <expression>
其中,lvalue是过程赋值语句有效的某种数据类型,=是赋值运算符,timing control是可选的赋值内延迟。timing control延迟可以是延迟控制(例如,#6)或事件控制(例如,@(posedge clk))。表达式是模拟器分配给左侧的右侧值。阻塞过程赋值使用的=赋值运算符也用于过程连续赋值和连续赋值。
示例
rega = 0;
rega[3] = 1; // a bit-select
rega[3:5] = 7; // a part-select
mema[address] = 8’hff; // assignment to a memory element
{carry, acc} = rega + regb; // a concatenation
非阻塞(RTL)赋值
非阻塞过程赋值允许您计划赋值,而不会阻塞过程流。当您希望在同一时间步长内进行多个寄存器赋值而无需考虑顺序或相互依赖关系时,可以使用非阻塞过程语句。
语法
非阻塞过程赋值的语法如下:
<lvalue> <= <timing_control> <expression>
其中lvalue是过程赋值语句有效的某种数据类型,<=是非阻塞赋值运算符,timing control是可选的赋值内timing control。timing control延迟可以是延迟控制或事件控制(例如,@(posedge clk))。表达式是模拟器分配给左侧的右侧值。非阻塞赋值运算符与模拟器用于小于或等于关系运算符的运算符相同。当您在表达式中使用<=运算符时,模拟器将其解释为关系运算符,当您在非阻塞过程赋值构造中使用<=运算符时,模拟器将其解释为赋值运算符。
模拟器如何评估非阻塞过程赋值 当模拟器遇到非阻塞过程赋值时,它会分两步评估和执行非阻塞过程赋值,如下所示:
模拟器评估右侧并计划在由过程timing control指定的时间进行新值的赋值。模拟器评估右侧并计划在由过程timing control指定的时间进行新值的赋值。
在给定延迟到期或发生适当事件的时间步长的末尾,模拟器通过将值分配给左侧来执行赋值。
示例
module evaluates2(out); output out; reg a, b, c; initial begin a = 0; b = 1; c = 0; end always c = #5 ~c; always @(posedge c) begin a <= b; b <= a; end endmodule
条件
条件语句(或if-else语句)用于决定是否执行语句。
形式上,语法如下:
<statement> ::= if ( <expression> ) <statement_or_null> ||= if ( <expression> ) <statement_or_null> else <statement_or_null> <statement_or_null> ::= <statement> ||= ;
<expression>被评估;如果为真(即,具有非零的已知值),则执行第一个语句。如果为假(具有零值或值为x或z),则第一个语句不执行。如果存在else语句并且<expression>为假,则执行else语句。由于测试if表达式的数值是否为零,因此可以进行某些捷径。
例如,以下两个语句表达相同的逻辑:
if (expression) if (expression != 0)
由于if-else的else部分是可选的,因此当从嵌套的if序列中省略else时可能会产生混淆。这是通过始终将else与缺少else的最接近的前一个if关联来解决的。
示例
if (index > 0) if (rega > regb) result = rega; else // else applies to preceding if result = regb; If that association is not what you want, use a begin-end block statement to force the proper association if (index > 0) begin if (rega > regb) result = rega; end else result = regb;
if-else-if的构造
以下构造经常出现,值得单独讨论。
示例
if (<expression>) <statement> else if (<expression>) <statement> else if (<expression>) <statement> else <statement>
此if序列(称为if-else-if构造)是编写多路决策的最通用方法。表达式按顺序评估;如果任何表达式为真,则执行与其关联的语句,这将终止整个链。每个语句要么是单个语句,要么是语句块。
if-else-if构造的最后一个else部分处理“以上皆非”或默认情况,即没有满足其他条件。有时没有默认的显式操作;在这种情况下,可以省略尾随的else,也可以将其用于错误检查以捕获不可能的条件。
Case语句
case语句是一种特殊的多分支决策语句,它测试表达式是否与多个其他表达式之一匹配,并相应地分支。例如,case语句对于描述微处理器指令的解码很有用。case语句具有以下语法:
示例
<statement> ::= case ( <expression> ) <case_item>+ endcase ||= casez ( <expression> ) <case_item>+ endcase ||= casex ( <expression> ) <case_item>+ endcase <case_item> ::= <expression> <,<expression>>* : <statement_or_null> ||= default : <statement_or_null> ||= default <statement_or_null>
case表达式按给定的确切顺序进行评估和比较。在线性搜索期间,如果其中一个case项表达式与括号中的表达式匹配,则执行与该case项关联的语句。如果所有比较都失败,并且给出了default项,则执行default项语句。如果没有给出default语句,并且所有比较都失败,则不执行任何case项语句。
除了语法之外,case语句在两个重要方面与多分支if-else-if构造不同:
if-else-if构造中的条件表达式比将一个表达式与多个其他表达式进行比较(如case语句中)更通用。
当表达式中存在x和z值时,case语句提供了确定的结果。
循环语句
有四种类型的循环语句。它们提供了一种控制语句执行零次、一次或多次的方法。
forever连续执行语句。
repeat执行语句固定次数。
while执行语句,直到表达式变为假。如果表达式一开始为假,则语句根本不执行。
for通过以下三步过程控制其关联语句的执行:
执行通常用于初始化控制执行循环次数的变量的赋值
评估表达式——如果结果为零,则for循环退出,如果结果不为零,则for循环执行其关联语句,然后执行步骤3
执行通常用于修改循环控制变量值的赋值,然后重复步骤2
以下是循环语句的语法规则:
示例
<statement> ::= forever <statement> ||=forever begin <statement>+ end <Statement> ::= repeat ( <expression> ) <statement> ||=repeat ( <expression> ) begin <statement>+ end <statement> ::= while ( <expression> ) <statement> ||=while ( <expression> ) begin <statement>+ end <statement> ::= for ( <assignment> ; <expression> ; <assignment> ) <statement> ||=for ( <assignment> ; <expression> ; <assignment> ) begin <statement>+ end
延迟控制
延迟控制
可以通过使用以下语法来延迟控制过程语句的执行:
<statement> ::= <delay_control> <statement_or_null> <delay_control> ::= # <NUMBER> ||= # <identifier> ||= # ( <mintypmax_expression> )
以下示例将赋值的执行延迟10个时间单位:
#10 rega = regb;
接下来的三个示例在数字符号(#)之后提供一个表达式。赋值的执行延迟由表达式的值指定的时间量决定。
事件控制
可以通过使用以下事件控制语法,将过程语句的执行与net或寄存器上的值变化或已声明事件的发生同步:
示例
<statement> ::= <event_control> <statement_or_null> <event_control> ::= @ <identifier> ||= @ ( <event_expression> ) <event_expression> ::= <expression> ||= posedge <SCALAR_EVENT_EXPRESSION> ||= negedge <SCALAR_EVENT_EXPRESSION> ||= <event_expression> <or <event_expression>>
*<SCALAR_EVENT_EXPRESSION> 是一个表达式,其结果为一位的值。
网络和寄存器上的值变化可以用作事件来触发语句的执行。这被称为检测隐式事件。Verilog 语法还允许您根据变化的方向检测变化——即,朝向值 1(posedge)或朝向值 0(negedge)。posedge 和 negedge 对未知表达式值的处理如下:
- 当从 1 转换到未知,以及从未知转换到 0 时,会检测到 negedge。
- 当从 0 转换到未知,以及从未知转换到 1 时,会检测到 posedge。
过程:Always 和 Initial 块
Verilog 中的所有过程都指定在以下四个块之一中:1) Initial 块 2) Always 块 3) 任务 4) 函数
initial 和 always 语句在仿真开始时启用。initial 块只执行一次,并且当语句执行完后其活动结束。相反,always 块重复执行。其活动只有在仿真终止时才会结束。在一个模块中可以定义任意数量的 initial 和 always 块。任务和函数是从其他过程中的一个或多个位置启用的过程。
Initial 块
initial 语句的语法如下:
<initial_statement> ::= initial <statement>
以下示例说明了在仿真开始时使用 initial 语句初始化变量。
Initial Begin Areg = 0; // initialize a register For (index = 0; index < size; index = index + 1) Memory [index] = 0; //initialize a memory Word End
initial 块的另一个典型用法是指定波形描述,这些描述执行一次以向被仿真的电路的主要部分提供激励。
Initial Begin Inputs = ’b000000; // initialize at time zero #10 inputs = ’b011001; // first pattern #10 inputs = ’b011011; // second pattern #10 inputs = ’b011000; // third pattern #10 inputs = ’b001000; // last pattern End
Always 块
‘always’ 语句在整个仿真运行过程中持续重复。always 语句的语法如下所示
<always_statement> ::= always <statement>
由于 ‘always’ 语句的循环特性,只有在与某种形式的时序控制结合使用时才有用。如果 ‘always’ 语句不提供时间推进的方法,则 ‘always’ 语句会产生仿真死锁情况。例如,以下代码创建了一个无限的零延迟循环:
Always areg = ~areg;
为上述代码提供时序控制可以创建一个可能有效的描述——如以下示例所示:
Always #half_period areg = ~areg;