第三章 Verilog HDL 基本语法
--------------------------------------------------------------------------------------------------------------------------------------------------
(2).阻塞(Blocking)赋值方式( 如 b = a; )
1) 赋值语句执行完后,块才结束。
2) b的值在赋值语句执行完后立刻就改变的。 3) 可能会产生意想不到的结果。
非阻塞赋值方式和阻塞赋值方式的区别常给设计人员带来问题。问题主要是给\块内的reg型信号的赋值方式不易把握。到目前为止,前面所举的例子中的\模块内的reg型信号都是采用下面的这种赋值方式:
b <= a;
这种方式的赋值并不是马上执行的,也就是说\块内的下一条语句执行后,b并不等于a,而是保持原来的值。\块结束后,才进行赋值。而另一种赋值方式阻塞赋值方式,如下所示:
b = a;
这种赋值方式是马上执行的。也就是说执行下一条语句时,b已等于a。尽管这种方式看起来很直观,但是可能引起麻烦。下面举例说明:
[例1]:always @( posedge clk )
begin b<=a; c<=b; end
[例1] 中的\块中用了非阻塞赋值方式,定义了两个reg型信号b和c,clk信号的上
升沿到来时,b就等于a,c就等于b,这里应该用到了两个触发器。请注意:赋值是在\块结束后执行的,c应为原来b的值。这个\块实际描述的电路功能如下图所示:
aclkDQbDQcCLKCLK
[例2]: always @(posedge clk)
begin
b=a; c=b; end
[例2]中的 \块用了阻塞赋值方式。clk信号的上升沿到来时,将发生如下的变化:b马上取a的值,c马上取b的值(即等于a),生成的电路图如下所示只用了一个触发器来寄存器a的值,又输出给b和c。这大概不是设计者的初衷,如果采用[例1]所示的非阻塞赋值方式就可以避免这种错误。
32
第三章 Verilog HDL 基本语法
--------------------------------------------------------------------------------------------------------------------------------------------------
a clk D Q b c CLK 关于赋值语句更详细的说明请参阅第七章中深入理解阻塞和非阻塞赋值小节。
3.4.2 块语句
块语句通常用来将两条或多条语句组合在一起,使其在格式上看更象一条语句。块语句有
两种,一种是begin_end语句,通常用来标识顺序执行的语句,用它来标识的块称为顺序块。一种是fork_join语句,通常用来标识并行执行的语句,用它来标识的块称为并行块。下面进行详细的介绍。
一.顺序块
顺序块有以下特点:
1) 块内的语句是按顺序执行的,即只有上面一条语句执行完后下面的语句才能执行。 2) 每条语句的延迟时间是相对于前一条语句的仿真时间而言的。 3) 直到最后一条语句执行完,程序流程控制才跳出该语句块。
顺序块的格式如下:
begin
语句1; 语句2; ...... 语句n; end
或
begin:块名
块内声明语句 语句1; 语句2; ...... 语句n; end
其中:
? 块名即该块的名字,一个标识名。其作用后面再详细介绍。
? 块内声明语句可以是参数声明语句、reg型变量声明语句、integer型变量声明语句、
real型变量声明语句。
下面举例说明: [例1]:begin
areg = breg;
creg = areg; //creg的值为breg的值。 end
33
第三章 Verilog HDL 基本语法
--------------------------------------------------------------------------------------------------------------------------------------------------
从该例可以看出,第一条赋值语句先执行,areg的值更新为breg的值,然后程序流程控制转到第二条赋值语句,creg的值更新为areg的值。因为这两条赋值语句之间没有任何延迟时间,creg的值实为breg的值。当然可以在顺序块里延迟控制时间来分开两个赋值语句的执行时间,见[例2]:
[例2]: begin
areg = breg;
#10 creg = areg;
//在两条赋值语句间延迟10个时间单位。 end
[例3]:parameter d=50; //声明d是一个参数
reg [7:0] r; //声明r是一个8位的寄存器变量 begin //由一系列延迟产生的波形
#d r = 'h35; #d r = 'hE2; #d r = 'h00; #d r = 'hF7;
#d -> end_wave; //触发事件end_wave end
这个例子中用顺序块和延迟控制组合来产生一个时序波形。
二. 并行块
并行块有以下四个特点:
1) 块内语句是同时执行的,即程序流程控制一进入到该并行块,块内语句则开始同时并
行地执行。
2) 块内每条语句的延迟时间是相对于程序流程控制进入到块内时的仿真时间的。 3) 延迟时间是用来给赋值语句提供执行时序的。
4) 当按时间时序排序在最后的语句执行完后或一个disable语句执行时,程序流程控制跳
出该程序块。
并行块的格式如下:
fork
语句1; 语句2; ....... 语句n; join 或
fork:块名 块内声明语句
语句1; 语句2; ...... 语句n; join
其中:
? 块名即标识该块的一个名字,相当于一个标识符。 ? 块内说明语句可以是参数说明语句、reg型变量声明语句、integer型变量声明语句、real
型变量声明语句、time型变量声明语句、事件(event)说明语句。
34
第三章 Verilog HDL 基本语法
--------------------------------------------------------------------------------------------------------------------------------------------------
下面举例说明:
[例4]:fork
#50 r = 'h35; #100 r = 'hE2; #150 r = 'h00; #200 r = 'hF7;
#250 -> end_wave; //触发事件end_wave. join
在这个例子中用并行块来替代了前面例子中的顺序块来产生波形,用这两种方法生成的波形是一样的。
三. 块名
在VerilgHDL语言中,可以给每个块取一个名字,只需将名字加在关键词begin或fork后面即
可。这样做的原因有以下几点。
1) 这样可以在块内定义局部变量,即只在块内使用的变量。 2) 这样可以允许块被其它语句调用,如被disable语句。 3) 在Verilog语言里,所有的变量都是静态的,即所有的变量都只有一个唯一的存储地址,
因此进入或跳出块并不影响存储在变量内的值。
基于以上原因,块名就提供了一个在任何仿真时刻确认变量值的方法。
四. 起始时间和结束时间
在并行块和顺序块中都有一个起始时间和结束时间的概念。对于顺序块,起始时间就是第一条语句开始被执行的时间,结束时间就是最后一条语句执行完的时间。而对于并行块来说,起始时间对于块内所有的语句是相同的,即程序流程控制进入该块的时间,其结束时间是按时间排序在最后的语句执行完的时间。
当一个块嵌入另一个块时,块的起始时间和结束时间是很重要的。至于跟在块后面的语句只有在该块的结束时间到了才能开始执行,也就是说,只有该块完全执行完后,后面的语句才可以执行。
在fork_join块内,各条语句不必按顺序给出,因此在并行块里,各条语句在前还是在后是无关紧要的。见下例:
[例5]:fork
#250 -> end_wave; #200 r = 'hF7; #150 r = 'h00; #100 r = 'hE2; #50 r = 'h35; join
在这个例子中,各条语句并不是按被执行的先后顺序给出的,但同样可以生成前面例子中的波形。
3.5.条件语句
3.5.1. if_else语句
if语句是用来判定所给定的条件是否满足,根据判定的结果(真或假)决定执行给出的两种操作之一。Verilog HDL语言提供了三种形式的if语句。
(1).if(表达式)语句
35
第三章 Verilog HDL 基本语法
--------------------------------------------------------------------------------------------------------------------------------------------------
例如: if ( a > b ) out1 <= int1;
(2).if(表达式) 语句1
else 语句2
例如: if(a>b) out1<=int1;
else out1<=int2;
(3).if(表达式1) 语句1;
else if(表达式2) 语句2; else if(表达式3) 语句3; ........
else if(表达式m) 语句m; else 语句n;
例如:
if(a>b) out1<=int1;
else if(a==b) out1<=int2; else out1<=int3;
六点说明:
(1).三种形式的if语句中在if后面都有“表达式”,一般为逻辑表达式或关系表达式。系统
对表达式的值进行判断,若为0,x,z,按“假”处理,若为1,按“真”处理,执行指定的语句。
(2) .第二、第三种形式的if语句中,在每个else前面有一分号,整个语句结束处有一分号。 例如:
If (a>b) out1 <=int1;else out1 <=int2; 各 有 一 个 分 号
这是由于分号是Verilog HDL语句中不可缺少的部分,这个分号是if语句中的内嵌套语句所要求的。如果无此分号,则出现语法错误。但应注意,不要误认为上面是两个语句(if语句和else语句)。它们都属于同一个if语句。else子句不能作为语句单独使用,它必须是if语句的一部分,与if配对使用。
(3).在if和else后面可以包含一个内嵌的操作语句(如上例),也可以有多个操作语句,此时
用begin和end这两个关键词将几个语句包含起来成为一个复合块语句。如:
if(a>b)
begin
out1<=int1; out2<=int2; end else
begin
out1<=int2; out2<=int1; end
注意在end后不需要再加分号。因为begin_end内是一个完整的复合语句,不需再附加分号。
36