FPGA学习Verilog第三天

第五章 条件语句、循环语句、块语句与生成语句

Verilog 中部分语句与C相似,但也有些与C不同,如块语句、生成语句、csesx和casez等,即使将新概念与硬件结构联系起来、

#5.1条件语句(if_else 语句)

Verilog HDL语言提供了3种形式的if语句。
(1)

if  (a > b)
     out1 = int1;

(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;

条件语句必须在过程块语句中使用。所谓过程块语句是指由initial 和 always 引导的执行语句集合。除了在这两个块语句中引导的 begin end 块中可以编写条件语句外,其他地方都不能编写。
if语句6点说明:
(1).三种形式的if语句中在if后面都有“表达式”,一般为逻辑表达式或关系表达式。系统对表达式的值进行判断,若为0,x,z,按“假”处理,若为1,按“真”处理,执行指定的语句。
(2) .第二、第三种形式的if语句中,在每个else前面有一分号,整个语句结束处有一分号。如上图程序所示。
但应注意,不要误认为if 语句1; else 语句 2 ;是两个语句。它们都属于同一个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内是一个完整的复合语句,不需再附加分号。

(4).允许一定形式的表达式简写方式。如
if(expression) 等同与 if( expression == 1 )
if(!expression) 等同与 if( expression != 1 )
(5).if语句的嵌套
在if语句中又包含一个或多个if语句称为if语句的嵌套。
应当注意if与else的配对关系,else总是与它上面的最近的if配对。如果if与else的数目不
一样,为了实现程序设计者的企图,可以用begin_end块语句来确定配对关系。例如:

if( ) 
begin 
if( ) 语句1 (内嵌if) 
end 
else 
语句2

这时begin_end块语句限定了内嵌if语句的范围,因此else与第一个if配对。注意begin_end块语句在if_else语句中的使用。因为有时begin_end块语句的不慎使用会改变逻辑行为。

5.2case语句

if语句只有两个分支可供选择,而实际问题中常常需要用到多分支选择,case语句是一种多分支选择语句,Verilog语言提供的case语句直接处理多分支选择。
它的一般形式如下:

1) case(表达式)  endcase 
2) casez(表达式)  endcase 
3) casex(表达式)  endcase

case分支项的一般格式如下:
分支表达式: 语句
缺省项(default项): 语句

说明
(a)case括弧内的表达式称为控制表达式,case分支项中的表达式称为分支表达式。控制表达式通常表示为控制信号的某些位,分支表达式则用这些控制信号的具体状态值来表示,因此分支表达式又可以称为常量表达式。
(b)当控制表达式的值与分支表达式的值相等时,就执行分支表达式后面的语句。如果所有的分支表达式的值都没有与控制表达式的值相匹配的,就执行default后面的语句。
(c) default项可有可无,一个case语句里只准有一个default项。下面是一个简单的使用case语句的例子。该例子中对寄存器rega译码以确定result的值。

reg [15:0] rega; 
reg [9:0] result;
case(rega) 
16 'd0: result = 10 'b0111111111; 
16 'd1: result = 10 'b1011111111; 
16 'd2: result = 10 'b1101111111; 
16 'd3: result = 10 'b1110111111; 
16 'd4: result = 10 'b1111011111; 
16 'd5: result = 10 'b1111101111; 
16 'd6: result = 10 'b1111110111; 
16 'd7: result = 10 'b1111111011; 
16 'd8: result = 10 'b1111111101; 
16 'd9: result = 10 'b1111111110; 
default: result = 'bx; 
endcase

d) 每一个case分项的分支表达式的值必须互不相同,否则就会出现矛盾现象(对表达式的同一个值,有多种执行方案)。
e) 执行完case分项后的语句,则跳出该case语句结构,终止case语句的执行。
f) 在用case语句表达式进行比较的过程中,只有当信号的对应位的值能明确进行比较时,比较才能成功。因此要注意详细说明case分项的分支表达式的值。
g) case语句的所有表达式的值的位宽必须相等,只有这样控制表达式和分支表达式才能进行对应位的比较。一个经常犯的错误是用’bx, 'bz 来替代 n’bx, n’bz,这样写是不对的,因为信号x, z的缺省宽度是机器的字节宽度,通常是32位(此处 n 是case控制表达式的位宽)。
FPGA学习Verilog第三天_第1张图片
Verilog HDL针对电路的特性提供了case语句的其它两种形式用来处理case语句比较过程中的不必考虑的情况( don’t care condition )。其中casez语句用来处理不考虑高阻值z的比较过程,casex语句则将高阻值z和不定值都视为不必关心的情况。所谓不必关心的情况,即在表达式进行比较时,不将该位的状态考虑在内。cazex即无论是输入或输出出现x或z时,输出就是1。

Verilog HDL设计中容易犯的一个通病是由于不正确使用语言,生成了并不想要的锁存器。下面我们
给出了一个在“always"块中不正确使用if语句,造成这种错误的例子。
FPGA学习Verilog第三天_第2张图片
在"always"块内,如果在给定的条件下变量没有赋值,这个变量将保持原值,也就是说会生成一个锁存器!

Verilog HDL程序另一种偶然生成锁存器是在使用case语句时缺少default项的情况下发生的。
case语句的功能是:在某个信号(本例中的sel)取不同的值时,给另一个信号(本例中的q)赋不同的值。注意看下图左边的例子,如果sel=0,q取a值,而sel=11,q取b的值。这个例子中不清楚的是:如果sel取00和11以外的值时q将被赋予什么值?在下面左边的这个例子中,程序是用Verilog HDL写的,即默认为q保持原值,这就会自动生成锁存器。
FPGA学习Verilog第三天_第3张图片
以上就是怎样来避免偶然生成锁存器的错误。如果用到if语句,最好写上else项。如果用case语句,最好写上defalut项。遵循上面两条原则,就可以避免发生这种错误,使设计者更加明确设计目标,同时也增强了Verilog程序的可读性。不写else或default变量就会保留原值,形成锁存器。
if else 语句的三种类型(没有else、有一条else、嵌套if_else_if 语句)可以生成条件语句。
case 语句采用关键词case、endcase和default来表示。case语句中的defalut语句是可选的,但是一条case语句中不允许出现多条defalut。而且case语句可以嵌套。
case语句可以生成多路分支语句

5.3循环语句

在Verilog HDL中存在着四种类型的循环语句,用来控制执行语句的执行次数。

  1. forever 连续的执行语句。
  2. repeat 连续执行一条语句 n 次。
  3. while 执行一条语句直到某个条件不满足(为假、值为0)。如果一开始条件即不满足(为假、值为0), 则语句一次也不能被执行。
  4. for通过以下三个步骤来决定语句的循环执行。
    a) 先给控制循环次数的变量赋初值。
    b) 判定控制循环的表达式的值,如为假则跳出循环语句,如为真则执行指定的语句后,转到第三步。
    c) 执行一条赋值语句来修正控制循环变量次数的变量的值,然后返回第二步。

5.3.1 forever 语句

forever 语句;

forever begin 多条语句 end

forever循环语句常用于产生周期性的波形,用来作为仿真测试信号。它与always语句不同处在于不能独立写在程序中,而必须写在initial块中。

5.3.2 repeat 语句

repeat语句的格式如下:
repeat(表达式) 语句;

repeat(表达式) begin 多条语句 end
在repeat语句中,其表达式通常为常量表达式。repeat后边常量是几,就是就是循环执行几次。
下面的例子中使用repeat循环语句及加法和移位操作来实现一个乘法器。

parameter size=8,longsize=16; 
reg [size:1] opa, opb; 
reg [longsize:1] result; 
begin: mult 
    reg [longsize:1] shift_opa, shift_opb; 
    shift_opa = opa; 
    shift_opb = opb; 
    result = 0; 
    repeat(size) 
       begin 
         if(shift_opb[1]) 
              result = result + shift_opa; 
              shift_opa = shift_opa <<1;    //乘数左移
              shift_opb = shift_opb >>1;    //被乘数右移 判断是否为1
       end 
end

5.3.3while语句

while语句的格式如下:
while(表达式) 语句
或:
while(表达式) begin 多条语句 end
表达式为假结束循环。

begin: count1s 
reg[7:0] tempreg; 
count=0; 
tempreg = rega; 
while(tempreg) 
    begin 
    if(tempreg[0]) count = count + 1;
    tempreg = tempreg>>1; 
end 
end

while(寄存器)???、 是否合法?如何判断表达式是否为真?

5.3.4 for 语句

for语句的一般形式为:
for(表达式1;表达式2;表达式3) 语句
它的执行过程如下:
(1) 先求解表达式1;
(2) 求解表达式2,若其值为真(非0),则执行for语句中指定的内嵌语句,然后执行下面的
第3步。若为假(0),则结束循环,转到第5步。
(3) 若表达式为真,在执行指定的语句后,求解表达式3。
(4) 转回上面的第2步骤继续执行。
(5) 执行for语句下面的语句。
for语句最简单的应用形式是很易理解的。
其形式如下:
for(循环变量赋初值;循环结束条件;循环变量增值)
执行语句
下面则是用for循环语句来实现前面用repeat语句实现的乘法器。

parameter size = 8, longsize = 16; 
reg[size:1] opa, opb; 
reg[longsize:1] result; 
begin:mult 
     integer bindex; 
     result=0; 
     for( bindex=1; bindex<=size; bindex=bindex+1 ) 
            if(opb[bindex]) 
            result = result + (opa<<(bindex-1)); 
end

在for语句中,循环变量增值表达式可以不必是一般的常规加法或减法表达式。也可以是移位等运算算法。
例:for( tempreg=rega; tempreg; tempreg=tempreg>>1 )

5.3.5顺序块与并行块补充

在使用并行块时需注意,如果两条语句在同一时间对同一变量产生影响,那么将会引起隐含的竞争,这种情况需要避免。并行块中所有语句都是在仿真0时刻开始执行,但是实际的执行顺序是未知的。从仿真的角度来讲,并行块中的所有语句是一起执行的,但是实际上运行仿真程序的CPU在任一时刻只能执行一条语句,而且不同的仿真器按照不同的顺序执行。
块语句的特点
块语句具有三个特点:嵌套块、命名块、和命名块的禁用。
嵌套块

initial
begin 
     x=1'b0;
     fork
          #5    y = 1'b1;
          #10  z = {x,y};
          join 
          #20  w= {y,x};
end
  
endmodule

命名块
块可以有自己的名字,这称为命名块。命名块的特点是:
(1) 命名块中可以声明局部变量;
(2)命名块是设计层次的一部分,命名块中声明的变量可以通过层次名引用进行访问;
(3)命名块可以被禁用,例如停止其执行。
例如;

   module  top ;
   initial 
      begin  block1;          //名字是block1的顺序命名块。
        integer i;                //可以用层次名 top.block1.i 被其他模块访问      

命名块的禁用
Verilog采用关键字disable提供一种中止命名块执行的方法。disable可以用来从循环中退出,处理错误条件以及根据控制信号来控制某些代码段是否被执行。
例如:disable block1 ; //禁用块block1。

顶层模块调用其他模块时,正确填入I/O口的对应信号。
例如已有全加器模块FullAdder。在顶层模块中调用连接线分别为W4,W5,W3,W2和W1。

FullAdder  FA(.Sum(W1),
               .Cout(W2),
               .Cin(W3),
               .A(W4),
               .B(W5));

定义端口线,正确填入I/O口对应的信号。
input [0:2];最高位MSB:IP[0]; 最低位IP[2];
wire[16:23]A 最高位MSB:A[16];最低位LSB:A[23];
与0按位异或是本身,与1按位异或为取反。
与0按位同或是取反,与1同位异或为本身。
一个<=,一个触发器。

你可能感兴趣的:(学生,FPGA学习)