第三章 Verilog HDL 基本语法
--------------------------------------------------------------------------------------------------------------------------------------------------
forever语句的格式如下:
forever 语句; 或
forever begin 多条语句 end
forever循环语句常用于产生周期性的波形,用来作为仿真测试信号。它与always语句不同处在于不能独立写在程序中,而必须写在initial块中。其具体使用方法将在\事件控制\这一小节里详细地加以说明。
3.6.2.repeat语句
repeat语句的格式如下:
repeat(表达式) 语句; 或
repeat(表达式) begin 多条语句 end
在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; end end
3.6.3.while语句
while语句的格式如下:
while(表达式) 语句
或用如下格式:
while(表达式) begin 多条语句 end
下面举一个while语句的例子,该例子用while循环语句对rega这个八位二进制数中值为1的位进行计数。
begin: count1s reg[7:0] tempreg; count=0;
tempreg = rega;
42
第三章 Verilog HDL 基本语法
--------------------------------------------------------------------------------------------------------------------------------------------------
while(tempreg) begin
if(tempreg[0]) count = count + 1; tempreg = tempreg>>1; end end
3.6.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循环语句实际上相当于采用while循环语句建立以下的循环结构:
begin
循环变量赋初值; while(循环结束条件)
begin
执行语句
循环变量增值; end
end
这样对于需要8条语句才能完成的一个循环控制,for循环语句只需两条即可。
下面分别举两个使用for循环语句的例子。例1用for语句来初始化memory。例2则用for循环语句来实现前面用repeat语句实现的乘法器。
[例1]:begin: init_mem
reg[7:0] tempi;
for(tempi=0;tempi memory[tempi]=0; end [例2]: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]) 43 第三章 Verilog HDL 基本语法 -------------------------------------------------------------------------------------------------------------------------------------------------- result = result + (opa<<(bindex-1)); end 在for语句中,循环变量增值表达式可以不必是一般的常规加法或减法表达式。下面是对rega这个八位二进制数中值为1的位进行计数的另一种方法。见下例: begin: count1s reg[7:0] tempreg; count=0; for( tempreg=rega; tempreg; tempreg=tempreg>>1 ) if(tempreg[0]) count=count+1; end 3.7.结构说明语句 Verilog语言中的任何过程模块都从属于以下四种结构的说明语句。 1) initial说明语句 2) always说明语句 3) task说明语句 4) function说明语句 initial和always说明语句在仿真的一开始即开始执行。initial语句只执行一次。相反,always语句则是不断地重复执行,直到仿真过程结束。在一个模块中,使用initial和always语句的次数是不受限制的。task和function语句可以在程序模块中的一处或多处调用。其具体使用方法以后再详细地加以介绍。这里只对initial和always语句加以介绍。 3.7.1.initial语句 initial语句的格式如下: initial begin 语句1; 语句2; ...... 语句n; end 举例说明: [例1]: initial begin areg=0; //初始化寄存器areg for(index=0;index memory[index]=0; //初始化一个memory end 在这个例子中用initial语句在仿真开始时对各变量进行初始化。 [例2]: initial 44 第三章 Verilog HDL 基本语法 -------------------------------------------------------------------------------------------------------------------------------------------------- begin inputs = 'b000000; //初始时刻为0 #10 inputs = 'b011001; #10 inputs = 'b011011; #10 inputs = 'b011000; #10 inputs = 'b001000; end 从这个例子中,我们可以看到initial语句的另一用途,即用initial语句来生成激励波形作为电路的测试仿真信号。一个模块中可以有多个initial块,它们都是并行运行的。initial块常用于测试文件和虚拟模块的编写,用来产生仿真测试信号和设置信号记录等仿真环境。 3.7.2.always语句 always语句在仿真过程中是不断重复执行的。 其声明格式如下: always <时序控制> <语句> always语句由于其不断重复执行的特性,只有和一定的时序控制结合在一起才有用。如果一个always语句没有时序控制,则这个always语句将会发成一个仿真死锁。见下例: [例1]:always areg = ~areg; 这个always语句将会生成一个0延迟的无限循环跳变过程,这时会发生仿真死锁。如果加上时序控制,则这个always语句将变为一条非常有用的描述语句。见下例: [例2]:always #half_period areg = ~areg; 这个例子生成了一个周期为:period(=2*half_period) 的无限延续的信号波形,常用这种方法来描述时钟信号,作为激励信号来测试所设计的电路。 [例3]:reg[7:0] counter; reg tick; always @(posedge areg) begin tick = ~tick; counter = counter + 1; end 这个例子中,每当areg信号的上升沿出现时把tick信号反相,并且把counter增加1。这种时间控制是always语句最常用的。 always 的时间控制可以是沿触发也可以是电平触发的,可以单个信号也可以多个信号,中间需要用关键字 or 连接,如: always @(posedge clock or posedge reset) //由两个沿触发的always块 begin …… end 45 第三章 Verilog HDL 基本语法 -------------------------------------------------------------------------------------------------------------------------------------------------- always @( a or b or c ) //由多个电平触发的always块 begin …… end 沿触发的always块常常描述时序逻辑,如果符合可综合风格要求可用综合工具自动转换为表示时序逻辑的寄存器组和门级逻辑,而电平触发的always块常常用来描述组合逻辑和带锁存器的组合逻辑,如果符合可综合风格要求可转换为表示组合逻辑的门级逻辑或带锁存器的组合逻辑。一个模块中可以有多个always块,它们都是并行运行的。 3.7.3.task和function说明语句 task和function说明语句分别用来定义任务和函数。利用任务和函数可以把一个很大的程序模块分解成许多较小的任务和函数便于理解和调试。输入、输出和总线信号的值可以传入、传出任务和函数。任务和函数往往还是大的程序模块中在不同地点多次用到的相同的程序段。学会使用task和function语句可以简化程序的结构,使程序明白易懂,是编写较大型模块的基本功。 一. task和function说明语句的不同点 任务和函数有些不同,主要的不同有以下四点: 1) 函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单 位。 2) 函数不能启动任务,而任务能启动其它任务和函数。 3) 函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量。 4) 函数返回一个值,而任务则不返回值。 函数的目的是通过返回一个值来响应输入信号的值。任务却能支持多种目的,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。Verilog HDL模块使用函数时是把它当作表达式中的操作符,这个操作的结果值就是这个函数的返回值。下面让我们用例子来说明: 例如,定义一任务或函数对一个16位的字进行操作让高字节与低字节互换,把它变为另一个字(假定这个任务或函数名为: switch_bytes)。 任务返回的新字是通过输出端口的变量,因此16位字字节互换任务的调用源码是这样的: switch_bytes(old_word,new_word); 任务switch_bytes把输入old_word的字的高、低字节互换放入new_word端口输出。 而函数返回的新字是通过函数本身的返回值,因此16位字字节互换函数的调用源码是这样的: new_word = switch_bytes(old_word); 下面分两节分别介绍任务和函数语句的要点。 二. task说明语句 如果传给任务的变量值和任务完成后接收结果的变量已定义,就可以用一条语句启动任务。任务完成以后控制就传回启动过程。如任务内部有定时控制,则启动的时间可以与控制返回的时间不同。 46