其格式与C语言中的if–else语句类似,使用方法有以下3种:
//形式1:只有if的形式
if(表达式) 语句1;
if(表达式)
begin
表达式1;
end
//形式2:if--else形式
if(表达式)
语句或语句块1;
else
语句或语句块2;
//形式3:if--else嵌套形式
if ( 表达式1) 语句1;
else if ( 表达式2 ) 语句2;
else if ( 表达式3 ) 语句3;
........
else if ( 表达式m ) 语句m;
else 语句n;
//例如:
if ( a > b ) out = int1;
else if ( a == b) out1= int2;
else out1 = int3;
表达式:一般为逻辑表达式或关系表达式,也可能是一位的变量。
系统对表达式的值进行判断,若为0,x,z,按“假”处理;若为1,按“真”处理,执行指定语句。
语句可是单句,也可是多句,多句时用“begin - end”语句括起来。对于if语句的嵌套,若不清楚if和else的匹配,最好用begin-end语句括起来。
条件语句必须在过程块中使用:
always@(a,b,int1,int2)
begin
if(a>b)
begin
out1=int1;
out2=int2;
end
else
begin
out1=int2;
out2=int1;
end
end
允许一定形式的表达式简写方式:
if(expression) 等同于 if(expression == 1)
if(!expression) 等同于 if(expression!= 1)
if语句的嵌套,即在if语句中又包含一个或多个if语句称为if语句的嵌套。应当注意if与else的配对关系,else总是与它上面的最近的if配对。
if-else 嵌套形式隐含优先级关系:
always@(sela or selb or a or b or c)
begin
if(sela) q=a;
else if(selb) q=b;
else q=c;
end
Verilog语言提供的case语句直接处理多分支选择,通常用于描述译码器、数据选择器、状态机及微处理器的指令译码等,它的一般形式如下:
case(表达式)
分支表达式1:语句1;
分支表达式2:语句2;
···
分支表达式n:语句n;
default: 语句n+1; //如果前面列出了表达式所有可能取值,default语句可以省略
endcase
case括弧内的表达式称为控制表达式,case分支项中的表达式称为分支表达式。分支表达式则用这些控制信号的具体状态值来表示,因此分支表达式又可以称为常量表达式。
当控制表达式的值与分支表达式的值相等时,就执行分支表达式后面的语句;如果所有的分支表达式的值都没有与控制表达式的值相匹配,就执行default后面的语句。
分支表达式后面的语句也可以是由begin-end括起来的语句块。
default项可有可无,一个case语句里只准有一个default项。同样,case也只能在块语句中使用。
//case语句实现3-8译码器的部分代码如下:
wire[2:0] sel;
reg[7:0] res;
always @ (sel or res)
begin
//case语句;
case (sel)
3’b000 : res=8’b00000001;
3’b001 : res=8’b00000010;
3’b010 : res=8’b00000100;
3’b011 : res=8’b00001000;
3’b100 : res=8’b00010000;
3’b101: res=8’b00100000;
3’b110 : res=8’b01000000;
default: res=8’b10000000;
endcase
end
case语句的所有表达式值的位宽必须相等,只有这样,控制表达式和分支表达式才能进行对应位的比较。一个经常犯的错误是用’bx,'bz来替代n’bx,n’bz,这样写是不对的,因为信号x,z的默认宽度是机器的字节宽度,通常是32位。
执行完case分项后的语句,则跳出该case语句结构,终止case语句的执行。
在case语句中,表达式与分支表达式1到分支表达式n之间的比较是一种全等比较(===),必须保证两者的对应位全等。如果表达式的值和分支表达式的值同时为不定值或者同时为高阻态,则认为是相等的
case | 0 | 1 | x | z |
---|---|---|---|---|
0 | 1 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 |
x | 0 | 0 | 1 | 0 |
z | 0 | 0 | 0 | 1 |
case ( a )
2’b1x:out = 1; // 只有a = 1x,才有out = 1
2’b1z:out = 0; // 只有a = 1z,才有out = 0
...
endcase
case(select[1,2])
2'b00: result = 0;
2'b01: result = flaga;
2'b0x,
2'b0z: result = flaga ? 'bx:0;
2'b10: result = flagb;
2'bx0,
2'bz0: result = 0;
default: result = flagb ? 'bz:0;
endcase
//当多个分项可以共用一个语句或语句块。其分支表达式之间用“,”隔开。
case语句还有两种变种,即casez语句和casex语句。
casez:
忽略比较过程中值为z的位,即如果比较的双方(表达式的值与分支表达式的值)有一方的某一位的值是z,那么对这些位的比较就不予考虑,只需关注其他位的比较结果。
casex:
在casex语句中,则把这种处理方式进一步扩展到对x的处理,即将z和x均视为无关值。
casez | 0 | 1 | x | z |
---|---|---|---|---|
0 | 1 | 0 | 0 | 1 |
1 | 0 | 1 | 0 | 1 |
x | 0 | 0 | 1 | 1 |
z | 1 | 1 | 1 | 1 |
//在分支表达式中,z常用?代替。
casez(a)
3'b1?? : out1 = 1;//如果a=100、101、110、111或1xx,1zz等,都有out1 = 1。
3'b0?1 : out2 = 1; //如果a=001、011、0x1、0z1,都有out2 = 1
.......
endcase
casex | 0 | 1 | x | z |
---|---|---|---|---|
0 | 1 | 0 | 1 | 1 |
1 | 0 | 1 | 1 | 1 |
x | 1 | 1 | 1 | 1 |
z | 1 | 1 | 1 | 1 |
例如:
casex(a)
2'b1x:out=1;
..................
endcase
//如果a=10、11、1x、1z,都有out=1。
if语句条件不完备情况
如果if语句和case语句的条件描述不完备,会造成不必要的锁存器 。
一般不可能列出所有分支,因为每一变量至少有4种取值0,1,z,x。为包含所有分支,可在if语句最后加上else;在case语句的最后加上default语句。
回顾一下锁存器和寄存器的区别:
always@(a or b)
begin
if(a == 1’b1)
y = b;
end
always@(posedge clk)
begin
if(a == 1’b1)
y <= b;
end
//毛刺通过D触发器被滤除
对FPGA来说,它的基本逻辑单元由多输入查找表、 D触发器构成,并不存在锁存器结构,因此如果在FPGA设计中使用锁存器,需要更多的资源来搭建锁存器,反而会更消耗资源。
所以在FPGA设计中,应该避免锁存器。在时序逻辑电路中,可以将锁存器改为带使能端的D触发器;在组合电路中,可以通过更改代码以覆盖所有条件分支等方式避免产生锁存器。
always @(al or d)
begin
if(al)
q<=d;
end
//有锁存器
always @(al or d)
begin
if(al) q<=d;
else q<=0
end
//无锁存器
检查一下上边的"always"块,if语句保证了只有当al=1时,q才取d的值。这段程序没有写出 al = 0 时的结果, 那么当al=0时会怎么样呢?
在"always"块内,如果在给定的条件下变量没有赋值,这个变量将保持原值,也就是说会生成一个锁存器!
避免偶然生成锁存器的错误。如果用到if语句,最好写上else项。如果用case语句,最好写上default项。遵循上面两条原则,就可以避免发生这种错误,使设计者更加明确设计目标,同时也增强了Verilog程序的可读性。
forever语句的格式如下:
forever 语句;
或者:
forever
begin
语句1;
语句2;
……
end
forever表示永久循环,无条件地无限次执行其后的语句,相当于while(1),直到遇到系统任务$finish
或$stop
,如果需要从循环中退出,可以使用disable
。
循环语句多用于生成时钟等周期性波形,它与always语句不同之处在于不能独立写在程序中,而必须写在initial块中。
initial
begin
clk = 0;
forever #25 clk = ~clk;
end
forever应该是过程块中最后一条语句。其后的语句将永远不会执行。
forever语句不可综合,通常用于testbench描述。
...
reg clk;
initial
begin
clk = 0;
forever //这种行为描述方式可以非常灵活的描述时钟,可以控制时钟的开始时间及周期占空比。仿真效率也高。
begin
#10 clk = 1;
#10 clk = 0;
end
end
...
repeat语句是最简单的循环语句,用于循环次数已知的情况。
repeat语句的表达形式为:
repeat(循环次数)
begin
操作1;
操作2;
………
end
下例实现连续8次循环左移的操作:
if (rotate == 1)
repeat (8)
begin
temp = data[15];
data = {data << 1,temp}; // data循环左移8次
end
while语句通过控制某个变量的取值来控制循环次数。一般表达形式:
while(条件)
begin
操作1;
操作2;
………
end
在使用while语句时,一般在循环体内更新条件的取值,以保证在适当的时候退出循环。
下例实现连续4次循环的操作
i = 0;
while(i < 4)
begin
a = a + 1;
//更新条件取值,使循环4次退出循环
i = i + 1;
end
可见在while结构中只要表达式为真(不为0),则重复执行一条语句(或语句块)
//其功能为:统计tempreg中 1 的个数
. . .
reg [7: 0] tempreg;
reg [3: 0] count;
. . .
count = 0;
while (tempreg)
begin
if (tempreg[0])
count = count + 1;
tempreg = tempreg >> 1; // Shift right
end
end
. . .
/*
Tempreg:
1011
0101
0010
0001
0000
*/
for语句可以实现所有的循环结构。其表达形式如下:
for(循环变量赋初值;条件表达式;更新循环变量)
begin
操作1:
操作2;
………
end
它的执行过程如下:
(1)先对循环变量赋初值。
(2)计算条件表达式,若其值为真(非0),则执行for语句中指定的内嵌语句,然后执行下面的第(3)步。若为假(0),则结束循环,转到第5步。
(3) 若条件表达式为真,在执行指定的语句后,执行更新循环变量。
(4) 转回上面的第(2)步骤继续执行。
(5) 执行for语句下面的语句。
for(i = 0; i <4; i =i+1)
begin
a = a+1;
end
例:用for语句来实现8位数据中低4位左移到高4位;
integer i;
reg [7:0] datain;
always @ (posedge clk)
begin
for(i=4;i<=7;i=i+1)
begin
datain[i] <= datain [i-4];
end
end
例:编写 在一个时钟周期内用for语句计算出13路脉冲信号为高电平的个数。
input clk,rst;
input [12:0] datain;
output [3:0] numout;
reg [3:0] i;
reg [3:0] num;
always @ (posedge clk)
begin
if ( !rst ) //重置信号
num <= 0;
else
begin
for ( i = 0; i < 13; i = i + 1) //用for循环进行计算
if ( datain [i ] ) num <= num +1;
end
end
命名块的特点是:
命名块中可以声明局部变量;
命名块是设计层次的一部分,命名块中声明的变量可以通过层次名引用进行访问
命名块可以被禁用,例如停止其执行。
//命名块
module top;
initial
begin : block1
integer i;
……….
end
initial
fork : block2
reg i;
……….
……….
join
Verilog通过关键字disable提供了一种中止命名块执行的方法。
disable可以用来从循环中退出、处理错误条件以及根据控制信号来控制某些代码段是否被执行。
对块语句的禁用导致本块语句终止执行,紧接在块后面的那条语句被执行。
例:(在C语言中break和continue的区别)
begin :continue
a = 0; b =0;
for(i=0;i<4;i = i+1)
begin
a = a+1;
if(i==2) disable continue;
b = b+1;
end
end
……………….;//a做3次加1操作后强制退出循环;而b只做2次加1操作。
a=0;
b=0;
for( i=0; i<4; i=i+1)
begin: continue
a = a+1;
if( i ==2) disable continue;
b= b+1;
end
……………………….;
//中止一次循环,继续下一次循环; a做4次加1操作, b只做3次加1操作.