1、1第6章 结构语句、系统任务、函数语句和显示系统任务26.1 结构说明语句Verilog语言中的任何过程模块都从属于以下四种结构的说明语句。(1)initial说明语句(2)always说明语句(3)task说明语句(4)function说明语句3initial和always说明语句在仿真的一开始即开始执行。initial语句只执行一次。相反,always语句则是不断地重复执行,直到仿真过程结束。always语句后面跟着的过程块是否运行,则要看它的触发条件是否满足,如满足则运行过程块一次,再次满足再运行一次,直至仿真过程结束。在一个模块中,使用initial和always语句的次数是不受限制的
2、。task和function语句可以在程序模块中的一处或多处调用。46.1.1 initial语句initial语句的格式如下:initialbegin 语句1;语句2;.语句n;end5例6.1 用initial 块对存储器变量赋初始值initialbegin areg=0;/初始化寄存器areg for(index=0;indexsize;index=index+1)memoryindex=0;/初始化一个memoryend6例6.2 用initial语句来生成激励波形initialbegin inputs=b000000;/初始时刻为0#10 inputs=b011001;#10 inp
3、uts=b011011;#10 inputs=b011000;#10 inputs=b001000;end7always语句由于其不断活动的特性,只有和一定的时序控制结合在一起才有用。如果一个always语句没有时序控制,则这个always语句将会使仿真器产生死锁。always areg=areg;但如果加上时序控制,则这个always语句将变为一条非常有用的描述语句。always#half_period areg=areg;8例6.5reg7:0 counter;reg tick;always(posedge areg)begin tick=tick;counter=counter+1;en
4、d9always 的时间控制可以是沿触发也可以是电平触发的,可以单个信号也可以多个信号,中间需要用关键字 or 连接。always(posedge clock or posedge reset)begin endalways(a or b or c)beginend10边沿触发的always块常常描述时序行为,如有限状态机。如果符合可综合风格要求,则可通过综合工具自动地将其转换为表示寄存器组和门级组合逻辑的结构,而该结构应具有时序所要求的行为 电平触发的always块常常用来描述组合逻辑的行为。如果符合可综合风格要求,可通过综合工具自动将其转换为表示组合逻辑的门级逻辑结构或带锁存器的组合逻辑结
5、构,而该结构应具有所要求的行为。一个模块中可以有多个always块,它们都是并行运行的。11always块的or事件控制有时,多个信号或者事件中任意一个发生的变化都能触发语句或语句块的执行。在Verilog语言中,可以使用“或”表达式来表示这种情况。由关键词“or”连接的多个事件名或信号名组成的列表称为敏感列表。关键词“or”被用来表示这种关系。Verilog 1364-2001版本的语法中,对于原来的规定作了补充:关键词or也可以使用,来代替。使用,来代替关键词or也适用于跳变沿敏感的触发器。12/有异步复位的电平敏感锁存器always (reset or clock or d)/等待复位信
6、号reset 或 时钟信号clock 或 输入信号d 的改变begin if(reset)/若 reset 信号为高,把q置零 q=1 b0;else if(clock)/若clock 信号为高,锁存输入信号d q=d;end13/有异步复位的电平敏感锁存器always (reset,clock,d)/等待复位信号reset 或 时钟信号clock 或 输入信号d的改变begin if(reset)/若 reset 信号为高,把q置零 q=1 b0;else if(clock)/若clock 信号为高,锁存输入信号d q=d;end14/用reset异步下降沿复位,clk正跳变沿触发的D寄存器
7、always (posedge clk,negedge reset)/注意:使用逗号来代替关键字or if(!reset)q=0;else q=d;15如果组合逻辑块语句的输入变量很多,那么编写敏感列表会很繁琐并且容易出错。针对这种情况,Verilog提供另外两个特殊的符号:*和(*),它们都表示对其后面语句块中所有输入变量的变化时敏感的。/用or 操作符的组合逻辑块/编写敏感列表很繁琐并且容易漏掉一个输入always(a or b or c or d or e or f or g or h or p or m)begin out1=a?b+c :d+e;out2=f?g+h :p+m;end
8、16/不用上述方法,用符号(*)来代替,/可以把所有输入变量都自动包括进敏感列表。always (*)begin out1=a?b+c :d+e;out2=f?g+h :p+m;end17电平敏感时序控制 waitalways wait(count_enable)#20 count=count+1;仿真器连续监视count_enable的值,若其值为0,则不执行后面的语句,仿真会停顿下来;如果其值为1,则在20个时间单位之后执行这条语句。如果count_enable始终为1,那么count将每过20个时间单位加1。186.2 task和function说明语句在设计中,设计者经常需要在程序的多
9、个不同地方实现同样的功能。这表明有必要把这些公共的代码提取出来,将其组成子程序,然后在需要的地方调用这些子程序,以避免重复编码。Verilog语言提供的任务和函数可以将较大的设计划分为较小的代码段,允许设计者将在多个地方使用的相同代码提取出来,编写成任务和函数,以使代码简洁、易懂。19task和function说明语句的不同点(1)函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位。(2)函数不能启动任务,而任务能启动其它任务和函数。(3)函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量。(4)函数返回一个值,而任务则不返回值。20task说明语句如果传给任
10、务的变量值和任务完成后接收结果的变量已定义,就可以用一条语句启动任务。任务完成以后控制就传回启动过程。如任务内部有定时控制,则启动的时间可以与控制返回的时间不同。任务可以启动其它的任务,其它任务又可以启动别的任务,可以启动的任务数是没有限制的。不管有多少任务启动,只有当所有的启动任务完成以后,控制才能返回。21任务的定义定义任务的语法如下:task;begin .endendtask22任务的调用及变量的传递启动任务并传递输入输出变量的声明语句的语法如下:(端口1,端口2,.,端口n);23module call_task;reg15:0 old_word;reg15:0 new_word;t
11、ask switch_bytes;input15:0 old_w;output15:0 new_w;reg15:0 temp;/此变量可以不要,此处是为了说明task内变量的定义 begin temp=old_w;new_w15:8=temp7:0;new_w7:0=temp15:8;end endtask24 initial begin old_word=16h3fa2;switch_bytes(old_word,new_word);#100;old_word=16habcd;switch_bytes(old_word,new_word);end endmodule 25编写Verilog程
12、序实现一个电路,该电路有2组输入端口a和b,1组输出端口c,位宽均为4,c输出a和b中较大的数。要求“对两数进行比较找出最大值”这一功能用任务来实现。26module task_max(a,b,c);input3:0 a,b;output3:0 c;task new_max;input3:0 a,b;output3:0 c;begin if(ab)c=a;else c=b;end endtask always(a,b)new_max(a,b,c);endmodule27function说明语句函数的目的是返回一个用于表达式的值。定义函数的语法:function (函数名);begin.ende
13、ndfunction28从函数返回的值函数的定义蕴含声明了与函数同名的、函数内部的寄存器。如在函数的声明语句中为缺省,则这个寄存器是一位的,否则是与函数定义中一致的寄存器。函数的定义把函数返回值所赋值寄存器的名称初始化为与函数同名的内部变量。函数的调用函数的调用是通过将函数作为表达式中的操作数来实现的。(,)29函数的使用规则与任务相比较函数的使用有较多的约束,下面给出的是函数的使用规则:函数的定义不能包含有任何的时间控制语句,即任何用、或wait来标识的语句。函数不能启动任务。定义函数时至少要有一个输入参量。在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具
14、有和函数名相同的名字。30module call_function;reg15:0 old_word;reg15:0 new_word;function15:0 switch_bytes;input15:0 old_w;reg15:0 temp;/此变量可以不要,此处是为了说明function内变量的定义 begin temp=old_w;switch_bytes15:8=temp7:0;switch_bytes7:0=temp15:8;end endfunction 31 initial begin old_word=16h3fa2;new_word=switch_bytes(old_wor
15、d);#100;old_word=16habcd;new_word=switch_bytes(old_word);end endmodule 326.2.4函数使用举例奇偶校验位的计算/定义一个模块,其中包含能计算偶校验位的函数(calc_parity)module parity;reg 31:0 addr;reg parity;initialbeginaddr=32h3456_789a;#10 addr=32hc4c6_78ff;#10 addr=32hff56_ff9a;#10 addr=32h3faa_aaaa;end33/每当地址值发生变化,计算新的偶校验位 always(addr)b
16、egin parity=calc_parity(addr);/第一次启动校验位计算函数 calc_parity$display(Parity calculated=%b,calc_parity(addr);/第二次启动校验位计算函数 calc_parityend/定义偶校验计算函数 function calc_parity;input 31:0 address;begin /适当地设置输出值,使用隐含的内部寄存器calc_parity calc_parity=address;/返回所有地址位的异或值 endendfunctionendmodule34/定义偶校验位计算函数,该函数采用ANSI
17、C 风格的变量声明function calc_parity(input 31:0 address);begin /适当地设置输出值,使用隐含的内部寄存器calc_parity calc_parity=address;/返回所有地址位的异或值 endendfunction使用C C风格风格进行变量声明的函数定义35/定义一个包含移位函数的模块 module shifter;/左/右 移位寄存器 define LEFT_SHIFT 1b0define RIGHT_SHIFT 1b1reg 31:0 addr,left_addr,right_addr;/reg control;/每当新地址出现时就计
18、算右移位和左移位的值 always(addr)begin/调用下面定义的具有左右移位功能的函数 left_addr=shift(addr,LEFT_SHIFT);right_addr=shift(addr,RIGHT_SHIFT);end36/定义移位函数,其输出是一个32位的值function 31:0 shift;input 31:0 address;input control;begin /根据控制信号适当地设置输出值 shift=(control=LEFT_SHIFT)?(address 1);endendfunctioninitialbegin addr=32h0001_0000;#
19、100;addr=32h0200_0000;end endmodule376.4 常用的系统任务6.4.1$display和$write任务格式$display(p1,p2,pn);$write(p1,p2,pn);这两个函数和系统任务的作用是用来输出信息,即将参数p2到pn按参数p1给定的格式输出。参数p1通常称为“格式控制”,参数p2至pn通常称为“输出表列”。这两个任务的作用基本相同。$display自动地在输出后进行换行,$write则不是这样。如果想在一行里输出多个信息,可以使用$write。因为$write在输出时不换行,要注意它的使用。可以在$write中加入换行符n,以确保明确
20、的输出显示格式。386.4.2 文件输出打开文件 使用系统任务$fopen用法:$fopen(“”);用法:=$fopen(“”);任务$fopen返回一个被称为多通道描述符的32位值。多通道描述符中只有一位被设置成1.标准输出有一个多通道描述符,其最低位(第0位)被设置成1.标准的输出也称通道为0。标准输出一直是开放的。以后对$fopen的每一次调用打开一个新通道,并且返回一个设置第1位、第2位等,直到32位描述的第30位。第31位是保留位。通道号与多通道描述符中被设置成1的位相对应。多通道的优点在于可以有选择的同时写多个文件。39写文件$fdisplay$fmonitor$fwrite$f
21、strobe都用于写文件这些任务在语法上与常规系统任务$display、$monitor等类似关闭文件 使用系统任务$fclose$fclose();40module ex6_20;/多通道描述符 integer handle1,handle2,handle3;/整型数为 32 位/标准输出是打开的;descriptor=32h0000_0001 (第0位置1)integer desc1,desc2,desc3;/三个文件的描述符initialbegin handle1=$fopen(file1.out);/handle1=32h0000_0002(bit 1 set 1)handle2=$f
22、open(file2.out);/handle2=32h0000_0004(bit 2 set 1)handle3=$fopen(file3.out);/handle3=32h0000_0008(bit 3 set 1)41 desc1=handle1|1;/按位或;desc1=32h0000_0003$fdisplay(desc1,Display 1);/写到文件file1.out和标准输出stdout desc2=handle2|handle1;/desc2=32 h0000_0006$fdisplay(desc2,Display 2);/写到文件file1.out和file2.out d
23、esc3=handle3;/desc3=32h0000_0008$fdisplay(desc3,Display 3 desc1=%d desc2=%d desc3=%d,desc1,desc2,desc3);/只写到文件file3.out$fclose(handle1|handle2|handle3);/本句不写也可以endendmodule426.4.3 显示层次通过任何显示任务,例如$display、$write、$monitor或者$strobe任务中的%m选项的方式可以显示任何级别的层次。43/显示层次信息 module M;initial$display(Displaying in%
24、m);endmodule/调用模块Mmodule top;M m1();M m2();M m3();endmodule44仿真输出如下所示:Displaying in top.m1Displaying in top.m2Displaying in top.m3这一特征可以显示全层次路径名,包括模块实例、任务、函数和命名块。456.4.4选通显示$strobe 和$display任务除了一点小的差别外,其它非常相似。如果许多其它语句和$display任务在同一个时间单位执行,那么这些语句与$display任务的执行顺序是不确定的。如果使用$strobe,该语句总是在同时刻的其他赋值语句执行完成后
25、才执行。46module ex6_22;reg3:0 a,b,c,d;reg clock;always (posedge clock)$strobe(%t Displaying a=%b,c=%b,$time,a,c);/显示正跳变沿时刻的值/选通显示always (posedge clock)begin a=b;c=d;end47initial clock=0;always#10 clock=clock;initial begin b=4h0;d=4h2;#25;b=4h9;d=4h5;end endmodule/在例6.22中,时钟上升沿的值在语句a=b和c=d执行完之后才显示。/如果使用$display,$display可能在语句a=b和c=d之前执行,结果显示不同的值。/run 45