VerilogHDL基本语法.ppt
Verilog HDL 基本语法,语汇代码的编写标准 常量、变量及数据类型 关键字和标示符 运算符 语句 综合代码的编写标准,规定了文本布局、命名和注释的约定,以提高源代码的可读性和可维护性。每个Verilog HDL源文件中只准编写一个顶层模块,也不能把一个顶层模块分成几部分写在几个源文件中。源文件名字应与文件内容有关,最好与顶层模块同名!源文件名字的第一个字符必须是字母或下划线,不能是数字或$符号!每行只写一个声明语句或说明。源代码用层层缩进的格式来写。,语汇代码的编写标准,2,定义变量名的大小写应自始至终保持一致(如变量名第一个字母均大写)。变量名应该有意义,而且含有一定的有关信息。局部变量名(如循环变量)应简单扼要。通过注释对源代码做必要的说明,尤其对接口(如模块参数、端口、任务、函数变量)做必要的注释很重要。,语汇代码的编写标准,3,常量尽可能多地使用参数定义和宏定义,而不要在语句中直接使用字母、数字和字符串。参数定义(用一个标识符来代表一个常量)的格式:parameter 参数名1=表达式,参数名2=表达式,;宏定义(用一个简单的宏名来代替一个复杂的表达式)的格式:define 标志符(即宏名)字符串(即宏内容),语汇代码的编写标准,4,常量,整型数:,5,注:这里位宽指对应二进制数的宽度。,常量,x表示不定值,z表示高阻值;“?”是z的另一种表示符号,建议在case语句中使用?表示高阻态z为提高可读性,在较长的数字之间可用下划线_隔开!但不可以用在和之间。当常量未指明位宽时,默认为32位。,6,17/位宽,基数符号不写会采用default值(32bit十进制)8d32/8-bit十进制值为32 8h1A 8o37 8b0001_1100 32bx/”x”表unknown 4b0?/”?”表示高阻,常量,7,常量,实数十进制计数法,科学计数法7.2 1.8e-4/1.8*10-4 9.5E6字符串与字符变量字符串为两个双引号“”之间的字符,不许跨行“This is a string!”;/共17个字符,8,常量,参数(符号常量)参数是一个常量,经常用于定义时延和变量的宽度。parameter byte_size=8;/定义一个常数参数parameter,byte_msb=byte_size-1;/用数表达式赋值parameter average_delay=(r+f)/2;/用常数表达式赋值,9,变量,变量即在程序运行过程中其值可以改变的量Verilog HDL中共有19种数据类型;其中3个最基本的数据类型为:网络型(nets type)寄存器型(register type)数组型(memory type),10,变量,线网类型:nets type表示Verilog结构化元件间的物理连线。它的值由驱动元件的值决定;如果没有驱动元件连接到线网,线网的缺省值为z。寄存器类型:register type表示一个抽象的数据存储单元,它只能在always语句和initial语句中被赋值,并且它的值从一个赋值到另一个赋值被保存下来。寄存器类型的变量具有x的缺省值。,11,wire型变量它是最常用的nets型变量,常用来表示以assign语句赋值的组合逻辑信号。模块中的输入/输出信号类型缺省为wire型。可用做任何方程式的输入,或“assign”语句和实例元件的输出。,变量,12,wire 数据名1,数据名2,数据名n;,wiren-1:0 数据名1,数据名2,数据名m;或 wiren:1 数据名1,数据名2,数据名m;,每条总线位宽为n,共有m条总线,wire型向量(总线),格式,变量,13,变量,14,register类型:在程序块中作变量用,对信号赋值需要用该数据类型,赋值时用关键字initial或always开始。常用register变量reg:是最常见的数据类型,常代表触发器.integer:32位带符号整数型变量,可以作为普通寄存器 使用,典型应用为高层次行为建模。time类型:无符号时间变量。real和realtime类型:实数寄存器(或实数时间寄存器),变量,register型变量与nets型变量的根本区别是:register型变量需要被明确地赋值,并且在被重新赋值前一直保持原值。register型变量必须通过过程赋值语句赋值!不能通过assign语句赋值!在过程块内被赋值的每个信号必须定义成register型!,15,reg型变量定义在过程块中被赋值的信号,往往代表触发器,但不一定就是触发器(也可以是组合逻辑信号)!,reg 数据名1,数据名2,数据名n;,格式,变量,16,regn-1:0 数据名1,数据名2,数据名m;或 regn:1 数据名1,数据名2,数据名m;,每个向量位宽为n,共有m个reg型向量,例 reg4:1 regc,regd;/regc,regd为4位宽的reg型向量,reg型向量(总线),变量,17,用reg型变量生成组合逻辑举例:module rw1(a,b,out1,out2);input a,b;output out1,out2;reg out1;wire out2;assign out2=a;always(b)out1=b;endmodule,过程赋值语句,连续赋值语句,电平触发,Verilog中reg与wire的区别,reg型变量既可生成触发器,也可生成组合逻辑;wire型变量只能生成组合逻辑。,变量,18,用reg型变量生成触发器举例:module rw2(clk,d,out1,out2);input clk,d;output out1,out2;reg out1;wire out2;assign out2=d endendmodule,过程赋值语句,连续赋值语句,上沿触发,变量,19,变量,Memory类型 Verilog HDL通过对reg型变量建立数组来对存储器建模,可以描述RAM型存储器,ROM存储器和reg文件。数组中的每一个单元通过一个数组索引进行寻址。在Verilog语言中没有多维数组存在。memory型数据是通过扩展reg型数据的地址范围来生成的。,20,变量,格式:reg n-1:0 存储器名m-1:0;regn-1:0定义了存储器中每一个存储单元的大小,即该存储单元是一个n位的寄存器。存储器名后的m-1:0或m:1则定义了该存储器中有多少个这样的寄存器。最后用分号结束定义语句。,21,变量,例:reg 7:0 mema255:0;这个例子定义了一个名为mema的存储器,该存储器有256个8位的存储器。该存储器的地址范围是0到255。注意:对存储器进行地址索引的表达式必须是常数表达式。,22,变量,Verilog的变量的四种逻辑状态:0:逻辑零、逻辑非、低电平1:逻辑1、逻辑真、高电平x或X:不定态z或Z:高阻态,23,关键字,关键字事先定义好的确认符,用来组织语言结构;或者用于定义Verilog HDL提供的门元件(如and,not,or,buf)。用小写字母定义!如always,assign,begin,case,casex,else,end,for,function,if,input,output,repeat,table,time,while,wire,24,用户程序中的变量、节点等名称不能与关键字同名!,关键词,25,edgeelseendendcaseendfunctionendprimitiveendmoduleendspecifyendtableendtaskeventforforceforeverfork,function highz0highz1 ififnoneinitialinoutinputintegerjoinlargemacromodulemediummodulenand,negedgenor notnotif0notif1nmosoroutputparameterpmosposedgeprimitivepulldownpulluppull0pull1,andalwaysassignbeginbufbufif0bufif1casecasexcasezcmosdeassigndefaultdefparamdisable,关键词,26,tri0tri1 vectoredwaitwandweak0weak1whilewireworxnorxor,rcmosrealrealtimeregreleaserepeatrnmosrpmosrtranrtranif0rtranif1scalaredsmallspecifyspecparam,strengthstrong0 strong1supply0supply1tabletasktrantranif0tranif1timetritriandtriortrireg,特殊字符,$:以$开头的标识符代表系统命令$time:返回目前的仿真时间$display:显示出信号的值$stop:停止仿真#时延控制not#3 not1(sel_,sel);/instance delay#5 a=0;b=0;cin=1;/procedural statement delay,27,标示符,28,任何用Verilog HDL语言描述的“东西”都通过其名字来识别,这个名字被称为标识符。如源文件名、模块名、端口名、变量名、常量名、实例名等。标识符可由字母、数字、下划线和$符号构成;但第一个字符必须是字母或下划线,不能是数字或$符号!在Verilog HDL中变量名是区分大小写的!,标识符不能与关键字同名!,标示符,标识符必须是由a-z,A-Z,0-9,_,$这些字符組成,最長只能到1024个字符开头必须由a-z,A-Z或下划线_开头可以在标识符所取的非法名称前加上反斜杠“”,并在名称结尾加上空白键,这样就可以用任何可印出的ASCII字符来当作标识符的名称了;而反斜杠和空白键不会被视为标识符的一部分,29,标示符,30,合法的名字:A_99_ZReset_54MHz_Clock$Module,不合法的名字:123a$datamodule7seg.v,运算符,31,进行整数除法运算时,结果值略去小数部分,只取整数部分!%称为求模(或求余)运算符,要求%两侧均为整型数据;求模运算结果值的符号位取第一个操作数的符号位!例-11%3 结果为-2进行算术运算时,若某操作数为不定值x,则整个结果也为x。,运算符,32,运算符,位运算符:按位进行运算,结果的位数不变两个不同长度的操作数进行位运算时,将自动按右端对齐,位数少的操作数会在高位用0补齐 例 若A=5b11001,B=3b101,则A&B=(5b11001)&(5b00101)=5b00001,33,运算符,关系运算结果为1位的逻辑值1或0或x。关系运算时,若关系为真,则返回值为1;若声明的关系为假,则返回值为0;若某操作数为不定值x,则返回值为x。等式运算符运算结果为1位的逻辑值1或0或x。等于运算符(=)和全等运算符(=)的区别:使用等于运算符时,两个操作数必须逐位相等,结果才为1;若某些位为x或z,则结果为x。使用全等运算符时,若两个操作数的相应位完全一致(如同是1,或同是0,或同是x,或同是z),则结果为1;否则为0。,34,“=”的真值表,“=”的真值表,等于运算的结果可能为1或0或x,全等于运算的结果只有1或0,运算符,35,左移会扩充位数!,将操作数右移或左移n位,相当于将操作数除以或乘以2n。,右移位数不变,但右移的数据会丢失!,运算符,移位运算符:将操作数右移或左移n位,同时用n个0填补移出的空位。例 4b10013=4b0001;4b10014=4b0000 4b10011=5b10010;4b10012=6b100100;16=32b1000000,36,运算符,条件运算符?:EX:tri_data=en?data_out:32bz;/三态,37,运算符的优先级,为提高程序的可读性,建议使用括号来控制运算的优先级!例(ab)&(bc)(a=b)|(x=y)(!a)|(ab),运算符,38,语句,39,语句,40,赋值语句分为两类:连续赋值语句assign语句,用于对wire型变量赋值,是描述组合逻辑最常用的方法之一 例 assign c=a/a、b、c均为wire型变量过程赋值语句用于对reg型变量赋值,有两种方式:非阻塞(non-blocking)赋值方式:赋值符号为=,如 b=a;阻塞(blocking)赋值方式:赋值符号为=,如 b=a;,非阻塞赋值方式 always(posedge clk)begin b=a;c=b;end,非阻塞赋值在块结束时才完成赋值操作!,注:c的值比b的值落后一个时钟周期!,语句,41,阻塞赋值方式 always(posedge clk)begin b=a;c=b;end,阻塞赋值在该语句结束时就完成赋值操作!,注:在一个块语句中,如果有多条阻塞赋值语句,在前面的赋值语句没有完成之前,后面的语句就不能被执行,就像被阻塞了一样,因此称为阻塞赋值方式。这里c的值与b的值一样!,语句,42,非阻塞(non-blocking)赋值方式(b=a):b的值被赋成新值a的操作,并不是立刻完成的,而是在块结束时才完成;块内的多条赋值语句在块结束时同时赋值;硬件有对应的电路。阻塞(blocking)赋值方式(b=a):b的值立刻被赋成新值a;完成该赋值语句后才能执行下一句的操作;硬件没有对应的电路,因而综合结果未知。,建议在初学时只使用一种方式,不要混用!建议在可综合风格的模块中使用非阻塞赋值!,语句,43,块语句用来将两条或多条语句组合在一起,使其在格式上更像一条语句,以增加程序的可读性。块语句有两种:begin_end语句标识顺序执行的语句fork_join语句标识并行执行的语句,语句,44,顺序块块内的语句是顺序执行的;每条语句的延迟时间是相对于前一条语句的仿真时间而言的;直到最后一条语句执行完,程序流程控制才跳出该顺序块。,语句,45,begin 语句1;语句2;语句n;end,begin:块名 块内声明语句;语句1;语句2;语句n;end,注:块内声明语句可以是参数声明、reg型变量声明、integer型变量声明、real型变量声明语句。,或,顺序块的格式,语句,46,例 begin b=a;c=b;/c的值为a的值 end 例 begin b=a;#10 c=b;/在两条赋值语句间延迟10个时间单位 end,注:这里标识符“#”表示延迟;在模块调用中“#”表示参数的传递,语句,47,例用顺序块和延迟控制组合产生一个时序波形。parameter d=50;reg7:0 r;begin/由一系列延迟产生的波形#d r=h35;#d r=hE2;#d r=h00;#d r=hF7;#d end_wave;/触发事件end_wave end,注:每条语句的延迟时间d是相对于前一条语句的仿真时间而言的!,语句,48,用fork_join标识的块,语句,并行块块内的语句是同时执行的;块内每条语句的延迟时间是相对于程序流程控制进入到块内时的仿真时间而言的;延迟时间用于给赋值语句提供时序;当按时间排序在最后的语句执行完或一个disable语句执行时,程序流程控制跳出该并行块。,49,fork 语句1;语句2;语句n;join,fork:块名 块内声明语句;语句1;语句2;语句n;join,或,注:块内声明语句可以是参数声明、reg型变量声明、integer型变量声明、real型变量声明语句、time型变量声明语句和事件(event)说明语句。,并行块的格式,语句,50,例4用并行块和延迟控制组合产生一个时序波形。reg7:0 r;fork/由一系列延迟产生的波形#50 r=h35;#100 r=hE2;#150 r=h00;#200 r=hF7;#250 end_wave;/触发事件end_wave join,波形同例3,注:在fork_join块内,各条语句不必按顺序给出!但为增加可读性,最好按被执行的顺序书写!,语句,51,语句,if-else 条件语句case 语句for循环语句,52,if(条件表达式)块语句1else if(条件表达式2)块语句2else if(条件表达式n)块语句nelse 块语句n+1,case(敏感表达式)值1:块语句1 值2:块语句2 值n:块语句n default:块语句n+1endcase,for(表达式1;表达式2;表达式3)块语句,若if与else的数目不一样,注意用“begin_end”语句来确定if与else的配对关系!,if(表达式1)if(表达式2)语句1;else 语句2;else if(表达式3)语句3;else 语句4;,if(表达式1)begin if(表达式2)语句1;endelse 语句2;,当if与else的数目不一样时,最好用“begin_end”语句将单独的if语句括起来:,if语句的嵌套:,语句,53,语句,case语句与if-else语句区别if-else语句适于对不同的条件,执行不同的语句;对于每个判定只有两个分支。case语句适于对同一个控制信号取不同的值时,输出取不同的值!它是多分支语句。当控制信号只有一个时,最好采用case语句,比较简洁!,54,是case语句的两种变体,语句,case casez casex的区别在case语句中,分支表达式每一位的值都是确定的(或者为0,或者为1);在casez语句中,若分支表达式某些位的值为高阻值z,则不考虑对这些位的比较;在casex语句中,若分支表达式某些位的值为z或不定值x,则不考虑对这些位的比较。在分支表达式中,可用“?”来标识x或z,55,module mux_z(out,a,b,c,d,select);output out;input a,b,c,d;input3:0 select;reg out;/必须声明 always(select3:0 or a or b or c or d)begin casez(select)4b?1:out=a;4b?1?:out=b;4b?1?:out=c;4b 1?:out=d;endcase endendmodule,例 用casez描述的数据选择器,这里“?”表示高阻态,语句,56,使用条件语句注意事项应注意列出所有条件分支,否则当条件不满足时,编译器会生成一个锁存器保持原值!这一点可用于设计时序电路,如计数器:条件满足时加1,否则保持原值不变。而在组合电路设计中,应避免生成隐含锁存器!有效的方法是在if语句最后写上else项;在case语句最后写上default项。,语句,57,如何正确使用if语句?,生成了不想要的锁存器:,不会生成锁存器:,当al为0时,q保持原值!,当al为0时,q等于0!,语句,58,always(sel1:0 or a or b)case(sel1:0)2b00:q=a;2b11:q=b;endcase,生成了不想要的锁存器:,例 设计一个数据选择器always(sel1:0 or a or b)case(sel1:0)2b00:q=a;2b11:q=b;default:q=b0;endcase,不会生成锁存器:,如何正确使用case语句?,当sel为00或11以外的值时,q保持原值!,避免生成锁存器的原则:如果用到if语句,最好写上else项;如果用到case语句,最好写上default项。,语句,59,循环语句for语句通过3个步骤来决定语句的循环执行:(1)给控制循环次数的变量赋初值。(2)判定循环执行条件,若为假则跳出循环;若为真,则执行指定的语句后,转到第(3)步。(3)修改循环变量的值,返回第(2)步。repeat语句连续执行一条语句n次while语句执行一条语句,直到循环执行条件不满足;若一开始条件即不满足,则该语句一次也不能被执行!forever语句无限连续地执行语句,可用disable语句中断!,语句,60,for语句,for(表达式1;表达式2;表达式3)语句,for(循环变量赋初值;循环执行条件;循环变量增值)执行语句,相当于采用while语句建立的循环结构:begin 循环变量赋初值;while(循环执行条件)begin 循环变量增值;endend,for语句比while语句简洁!,语句,61,repeat语句连续执行一条或多条语句n次。,repeat(循环次数表达式)语句,repeat(循环次数表达式)begin end,执行语句为多条语句,或,格,语句,62,例 两个8位二进制数乘法,注:不如采用for语句简单!,语句,63,1.while语句有条件地执行一条或多条语句。首先判断循环执行条件表达式是否为真。若为真,则执行后面的语句或语句块;然后再回头判断循环执行条件表达式是否为真,若为真,再执行一次后面的语句;如此不断,直到条件表达式不为真。,while(循环执行条件表达式)语句,while(循环执行条件表达式)begin end,或,语句 while,64,module count1s_while(count,rega,clk);output3:0 count;input 7:0 rega;input clk;reg3:0 count;always(posedge clk)begin:count1 reg7:0 tempreg;/用作循环执行条件表达式 count=0;/count初值为0 tempreg=rega;/tempreg 初值为rega while(tempreg)/若tempreg非0,则执行以下语句 begin if(tempreg0)count=count+1;/只要tempreg最低位为1,则 count加1 tempreg=tempreg 1;/右移1位 end endendmodule,例 对一个8位二进制数中值为1的位进行计数。,改变循环执行条件表达式的值,如何用for语句改写此程序呢?,语句,65,无条件连续执行forever后面的语句或语句块。,forever 语句,forever begin end,或,常用在测试模块中产生周期性的波形,作为仿真激励信号。常用disable语句跳出循环!,注:不同于always语句,不能独立写在程序中,一般用在initial语句块中!,initial begin:Clocking clk=0;#10 forever#10 clk=!clk;endinitial begin:Stimulus disable Clocking;/停止时钟 end,语句 forever语句,66,initial说明语句 只执行一次always说明语句不断重复执行,直到仿真 结束task说明语句 可在程序模块中的一处或 多处调用function说明语句可在程序模块中的一处 或多处调用,语句 结构说明语句,67,always块语句包含一个或一个以上的声明语句(如:过程赋值语句、任务调用、条件语句和循环语句等),在仿真运行的全过程中,在定时控制下被反复执行。,语句 结构说明语句,在always块中被赋值的只能是register型变量(如reg,integer,real,time)。每个always块在仿真一开始便开始执行,当执行完块中最后一个语句,继续从always块的开头执行。,68,always,注1:如果always块中包含一个以上的语句,则这些语句必须放在begin_end或fork_join块中!,always(posedge clk or negedge clear)begin if(!clear)qout=0;/异步清零 else qout=1;end,语句,69,70,例生成一个0延迟的无限循环跳变过程形成仿真死锁 always areg=areg;例在测试文件中,用于生成一个无限延续的信号波形时钟信号,注2:always语句必须与一定的时序控制结合在一起才有用!如果没有时序控制,则易形成仿真死锁!,define half_period 50 module half_clk_top;reg reset,clk;/输入信号 wire clk_out;/输出信号 always#half_period clk=clk;endmodule,语句,70,always()begin/过程赋值语句/if语句/case语句/while,repeat,for循环/task,function调用 end,一般为输入,敏感信号表达式又称事件表达式或敏感表,当其值改变时,则执行一遍块内语句;在敏感信号表达式中应列出影响块内取值的所有信号!敏感信号可以为单个信号,也可为多个信号,中间需用关键字or连接!敏感信号不要为x或z,否则会阻挡进程!,always块语句,一个变量不能在多个always块中被赋值!,语句,71,always的时间控制可以为沿触发,也可为电平触发。关键字posedge表示上升沿;negedge表示下降沿。,always(posedge clock or posedge reset)begin end,always(a or b or c)begin end,由两个沿触发的always 块,由多个电平触发的always 块,语句,72,73,always块语句是用于综合过程的最有用的语句之一,但又常常是不可综合的。为得到最好的综合结果,always块程序应严格按以下模板来编写:,模板1,always(Inputs)/所有输入信号必须列出,用or隔开 begin/组合逻辑关系 end,模板2,always(Inputs)/所有输入信号必须列出,用or隔开 if(Enable)begin/锁存动作 end,语句,73,模板3,always(posedge Clock)/Clock only begin/同步动作 end,模板4,always(posedge Clock or negedge Reset)/Clock and Reset only begin if(!Reset)/测试异步复位电平是否有效/异步动作 else/同步动作 end/可产生触发器和组合逻辑,语句,74,当always块有多个敏感信号时,一定要采用if-else if语句,而不能采用并列的if语句!否则易造成一个寄存器有多个时钟驱动,将出现编译错误。,always posedge min_clk or negedge reset)begin if(reset)min=0;else if(min=8h59)/当reset无效且min=8h59时 begin min=0;h_clk=1;end end,通常采用异步清零!只有在时钟周期很小或清零信号为电平信号时采用同步清零。,千万别写成if哦!,语句,75,initial begin 语句1;语句2;语句n;end,格式,例 利用initial语句生成激励波形。,initial begin inputs=b000000;#10 inputs=b011001;#10 inputs=b011011;#10 inputs=b011000;#10 inputs=b001000;end,在仿真的初始状态对各变量进行初始化;在测试文件中生成激励波形作为电路的仿真信号。,语句 initial语句,76,parameter size=16;reg3:0 addr;reg reg1;reg7:0 memory0:15;initial begin reg1=0;for(addr=0;addrsize;addr=addr+1);memoryaddr=0;end,例 对各变量进行初始化。,语句,77,除module以外,Verilog还提供了任务(task)和函数(function)可重复调用定义和调用都包含在module内部,语句,78,task和function语句分别用来由用户定义任务和函数。任务和函数往往是大的程序模块中在不同地点多次用到的相同的程序段。利用任务和函数可将一个很大的程序模块分解为许多较小的任务和函数,便于理解和调试。输入、输出和总线信号的值可以传入、传出任务和函数。,语句 task和function语句,79,task 任务名;/无需定义端口名列表端口与类型说明;/调用时按顺序和类型列出局部变量说明;块语句endtask,当希望能够对一些信号进行一些运算并输出多个结果(即有多个输出变量)时,宜采用任务结构。常常利用任务来帮助实现结构化的模块设计,将批量的操作以任务的形式独立出来,使设计简单明了。,语句,(端口1,端口2,);,80,.always(posedge sys_clk)begin if(read_request=1)begin read_mem(IR,PC);/Event and function calls end endtask read_mem;output 15:0 data_in;input 15:0 addr;always(posedge read_grant)beginADDRESS=addr;#15 data_in=data;endendtask.,语句,注1:任务的定义与调用必须在一个module模块内!注2:任务被调用时,需列出端口名列表,且必须与任务定义中的I/O变量一一对应!注3:一个任务可以调用其他任务和函数。,81,例 通过任务调用完成4个4位二进制输入数据的冒泡排序。,任务的定义,任务的调用,82,function函数的目的是通过返回一个用于某表达式的值,来响应输入信号。适于对不同变量采取同一运算的操作。函数在模块内部定义,通常在本模块中调用,也能根据按模块层次分级命名的函数名从其他模块调用。而任务只能在同一模块内定义与调用!,语句,83,语句,Functionfunction 函数名;输入端口与类型说明;局部变量说明;块语句endfiction注意:1.函数不能调用任务,任务可调用任何其他函数和任务 2.函数只有输入变量且至少一个 3.函数定义的块语句不许出现定时控制 4.函数通过函数名返回一个值,缺省则返回1位reg型数据,(),与函数定义中的输入变量对应!,84,always(posedge sys_clk)begin.IR=swap_bits(IR);.endfunction 15:0 swap_bits;input 15:0 in_vec;reg 15:0 temp_reg;integer i;begin for(I=15;I=0;I=I-1)temp_reg15-i=in_veci;swap_bits=temp_reg;endendfunctioin,语句,85,语句,注1:函数的调用是通过将函数作为调用函数的表达式中的操作数来实现的!注2:函数在综合时被理解成具有独立运算功能的电路,每调用一次函数,相当于改变此电路的输入,以得到相应的计算结果。,86,函数的使用规则函数的定义不能包含任何时间控制语句用延迟#、事件控制或等待wait标识的语句。函数不能启动(即调用)任务!定义函数时至少要有一个输入参量!且不能有任何输出或输入/输出双向变量。在函数的定义中必须有一条赋值语句,给函数中的一个内部寄存器赋以函数的结果值,该内部寄存器与函数同名。,语句,87,例 利用函数对一个8位二进制数中为0的位进行计数。,语句,88,任务与函数的区别,语句,89,语句,“编译预处理”是Verilog HDL编译系统的一个组成部分。编译预处理语句以西文符号“”开头注意,不是单引号“”!在编译时,编译系统先对编译预处理语句进行预处理,然后将处理结果和源程序一起进行编译。,90,define语句宏定义语句用一个指定的标志符(即宏名)来代表一个字符串(即宏内容)。宏定义的作用:以一个简单的名字代替一个长的字符串或复杂表达式;以一个有含义的名字代替没有含义的数字和符号。,define 标志符(即宏名)字符串(即宏内容),例 define IN ina+inb+inc+ind宏展开在编译预处理时将宏名替换为字符串的过程。,语句,91,关于宏定义的说明宏名可以用大写字母,也可用小写字母表示;但建议用大写字母,以与变量名相区别。define语句可以写在模块定义的外面或里面。宏名的有效范围为定义命令之后到源文件结束。在引用已定义的宏名时,必须在其前面加上符号“”!使用宏名代替一个字符串,可简化书写,便于记忆,易于修改。,语句,92,关于宏定义的说明预处理时只是将程序中的宏名替换为字符串,不管含义是否正确。只有在编译宏展开后的源程序时才报错。宏名和宏内容必须在同一行中进行声明!宏定义不是Verilog HDL语句,不必在行末加分号!如果加了分号,会连分号一起置换!,语句,93,例 module test;reg a,b,c,d,e,out;define expression a+b+c+d;assign out=expression+e;,经过宏展开后,assign语句为:assign out=a+b+c+d;+e;/出现语法错误!,错误!,语句,94,例 module test;reg a,b,c;wire out;define aa a+b define cc c+aa/引用已定义的宏名aa 来定义宏cc assign out=cc;,经过宏展开后,assign语句为:assign out=c+a+b;,语句,在进行宏定义时,可引用已定义的宏名,实现层层置换。,95,文件包含语句一个源文件可将另一个源文件的全部内容包含进来。,include“文件名”,将file2.v中全部内容复制插入到include“file2.v”命令出现的地方,格式,语句 include语句,96,使用include语句的好处避免程序设计人员的重复劳动!不必将源代码复制到自己的另一源文件中,使源文件显得简洁。(1)可以将一些常用的宏定义命令或任务(task)组成一个文件,然后用include语句将该文件包含到自己的另一源文件中,相当于将工业上的标准元件拿来使用。(2)当某几个源文件经常需要被其他源文件调用时,则在其他源文件中用include语句将所需源文件包含进来。,语句,97,例 用include语句设计16位加法器,adder模块,位拼接,改变被引用模块adder中的参数size为my_size,98,一个include语句只能指定一个被包含的文件;若要包含n个文件,需用n个include语句。,include语句可出现在源程序的任何地方。被包含的文件若与包含文件不在同一子目录下,必须指明其路径!,include“aaa.v”“bbb.v”/非法!,include“parts/count.v”/合法!,include“aaa.v”include“bbb.v”/合法!,语句,99,可将多个include语句写在一行;在该行中,只可出现空格和注释行。,文件包含允许嵌套。,include“aaa.v”include“bbb.v”/合法!,语句,100,时间尺度语句用于定义跟在该命令后模块的时间单位和时间精度。,timescale/,时间单位用于定义模块中仿真时间和延迟时间的基准单位;时间精度用来声明该模块的仿真时间和延迟时间的精确程度。在同一程序设计里,可以包含采用不同时间单位的模块。此时用最小的时间精度值决定仿真的时间单位。,格式,语句 timescale语句,101,timescale 1ps/1ns/非法!timescale 1ns/1ps/合法!,时间精度至少要和时间单位一样精确,时间精度值不能大于时间单位值!,在timescale语句中,用来说明时间单位和时间精度参量值的数字必须是整数。其有效数字为1、10、100;单位为秒(s)、毫秒(ms)、微秒(us)、纳秒(ns)、皮秒(ps)、毫皮秒(fs)。,语句,102,例 timescale语句应用举例。timescale 10ns/1ns/时间单位为10ns,时间精度为1nsreg sel;initial begin#10 sel=0;/在10ns10时刻,sel变量被赋值为0#10 sel=1;/在10