verilog任务和函数课件例程_第1页
verilog任务和函数课件例程_第2页
verilog任务和函数课件例程_第3页
verilog任务和函数课件例程_第4页
verilog任务和函数课件例程_第5页
已阅读5页,还剩61页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第七讲

主要内容:

Verilog任务

Verilog函数系统任务和函数7.1任务和函数在行为级设计中,经常需要在程序的多个不同地方实现同样的功能。这说明有必要把这些公共的局部提取出来,将其组成子程序,然后在需要的地方调用这些子程序,以防止重复编码。绝大多数程序设计语言都提供了过程或子程序来到达这个目的。同样,verilog语言提供的任务和函数可以将较大的行为级设方案分为较小的代码段,允许verilog设计者将在多个地方位用的相同代码提取出来,编写成任务和函数。Verilog的任务及函数区别和联系任务〔task)通常用于调试,或对硬件进行行为描述可以包含时序控制〔#延迟,@,wait〕可以有input,output,和inout参数可以调用其他任务或函数函数(function)通常用于计算,或描述组合逻辑不能包含任何延迟;函数仿真时间为0只含有input参数并由函数名返回一个结果可以调用其他函数,但不能调用任务区别任务和函数必须在module内调用在任务和函数中不能声明wire所有输入/输出都是局部存放器任务/函数执行完成后才返回结果。例如,假设任务/函数中有forever语句,那么永远不会返回结果共同点7.1Verilog任务“任务(task)”类似于其它编程语言中的“过程”。任务的使用包括任务定义和任务调用。任务既可表示组合逻辑又可表达时序逻辑,定义的形式如下task<任务名>;<端口及数据类型声明语句>begin<语句1><语句2>.....<语句n>endendtask任务可以没有或有一个或多个参数。值通过参数传入和传出任务。除输入参数外〔参数从任务中接收值〕,任务还能带有输出参数〔从任务中返回值〕和输入输出参数。任务的定义在模块说明局部中编写。其中:关键词task和endtask将它们之间的内容标志成是一个任务定义。其中关键词task标志着一个任务定义结构的开端,其后的“<任务名>”是为被定义任务所取的一个名称。在“<任务名>”后面不能出现输入输出端口列表。

“端口与类型说明”用于对任务各个端口的宽度和类型进行说明,其中端口类型由关键词input,output和inout (分别表示:输入、输出和双向端口)指定,该说明语句的语法与进行模块定义时的相应说明语句语法是一致的。

“局部变量说明”用来对任务内用到的局部变量进行宽度和类型说明,这个说明语句的语法与进行模块定义时的相应说明语句语法是一致的。由“begin”和“end”关键词界定的一组行为语句指明了任务被调用时需要进行的操作。在任务被调用时,这些行为语句将按串行方式得到执行。任务定义与“过程块”、“连续赋值语句”及“函数定义”这三种成份以并列方式存在于行为描述模块中,它们在层次级别上是相同的。任务定义结构不能出现在任何一个过程块的内部。

【例7-1】任务的定义。taskread_mem;

//任务定义结构的开头,指定任务名为“read_mem”input[15:0]address; //输入端口说明output[31:0]data; //输出端口说明reg[3:0]counter; //局部变量说明reg[7:0]temp[1:4]; //局部变量说明begin //语句块,指明任务被调用时需要进行的操作for(counter=1;counter<=4;counter=counter+1)temp[counter]=mem[address+counter-1];data={temp[1],temp[2],temp[3],temp[4]};endendtask//任务定义结构的结尾

上例定义了一个名为“read_mem”的任务,该任务有一个16位的输入端口“address”、一个32位的输出端口“data”、一个4位的局部变量“counter”和一个8位的存储器“temp”。当上例所定义的任务被调用时,begin和end中间的语句得到执行,它们用来执行对存储器“mem”进行的四次读操作,将其结果合并后输出到端口“data”。在定义任务时必须注意如下几点:(1)在第一行“task”语句中不能列出端口名列表。(2)在任务定义结构中的“行为语句”局部可以有延时语句、敏感事件控制语句等时间控制语句出现。(3)一个任务可以没有输入、输出和双向端口,也可以有一个或多个输入、输出和双向端口。(4)一个任务可以没有返回值,也可以通过输出端口或双向端口返回一个或多个返回值。(5)在一个任务中可以调用其它的任务或函数,也可以调用该任务本身。(6)在任务定义结构内不允许出现过程块(initial或always过程块)。(7)在任务定义结构内可以出现“disable中止语句”,这条语句的执行将中断正在执行的任务。当任务被中断后,程序流程将返回到调用任务的地方继续向下执行。2.任务的调用任务的调用是通过“任务调用语句”来实现的。任务调用语句的语法如下:<任务名>(端口1,端口2,……,端口n);其中,“(端口1,端口2,……,端口n)”组成了一个端口名列表。在调用任务时必须注意:(1)任务调用语句只能出现在过程块内。(2)任务调用语句就像一条普通的行为语句那样得到处理。(3)当被调用的任务具有输入或输出端口时,任务调用语句必须包含端口名列表,这个列表内各个端口名出现的顺序和类型必须与任务定义结构中端口说明局部的端口顺序和类型相一致,注意只有存放器类的变量才能与任务的输出端口相对应。例如,例7-2所示模块对例7-1中定义的任务“read_mem”进行了调用。【例7-2】对任务进行调用。moduledemo_task_invo;reg[7:0]mem[128:0];reg[15:0]a;reg[31:0]b;initialbegina=0;read_mem(a,b);//第一次调用#10;a=64;read_mem(a,b);//第二次调用end<任务“read_mem”定义局部>endmodule

在上面的模块中,任务“read_mem”得到了两次调用,由于这个任务在定义时说明了输入端口和输出端口,所以任务调用语句内必须包含端口名列表“(a,b)”,其中变量a与任务的输入端口“address”相对应,变量b与任务的输出端口“data”相对应,并且这两个变量在宽度上也是与对应的端口相一致的。这样,在任务被调用执行时,变量a的值通过输入端口传给了address;在任务调用完成后,输出信号data又通过对应的端口传给了变量b。任务的调用具有以下特点:(1)在任务内定义的局部变量都具有局部和静态的特点。也就是说,如果同时对同一任务进行两次调用,那么两个调用进程使用的将是相同物理地址处的同一个局部变量。在这种情况下要注意防止内存使用冲突。(2)在一个任务中可以直接访问上一级调用模块中的任何存放器,例如上例中我们可以在任务“read_mem”中对存储器变量mem进行访问,这个mem变量是在上一级模块(调用模块)“demo_task_invo”中定义的。(3)由于任务内部可以包含时间控制语句,所以调用执行一个任务所需的时间可以是非零时间单位,也就是说任务启动时刻可以是不同于任务结束时刻的。(4)可以通过一个disable语句来中断任务的执行。任务被中断后,程序流程将返回到调用任务的地方继续往下执行。下面我们举几个例子说明任务的调用。【例7-3】用任务来实现一个8位加法器。moduletask_example(a,b,c);input[7:0]a,b;output[7:0]c;reg[7:0]c;taskadder; //任务定义局部input[7:0]a,b;output[7:0]adder;regc; //此处的变量c是任务内的局部变量integeri; //任务内局部变量说明beginc=0; // c存储着上一位数字相加时的进位for(i=0;i<=7;i=i+1)beginadder[i]=a[i]^b[i]^c;//求一位数字相加的结果c=(a[i]&b[i])|(a[i]&c)|(b[i]&c);endendendtask //任务定义局部结束always@(aorb)adder(a,b,c); //任务调用语句endmodule上例所示的模块由“任务定义局部”和“always过程块”两局部组成,其中定义的任务“adder”实现了一个8位加法器的运算功能,它有两个输入端口a和b,有一个输出端口adder。在任务被调用时,模块中的变量a,b,c分别和任务中的端口a,b,adder相互对应,这样在任务执行完毕后运算结果(即adder的值)可以通过输出端口传递给变量c。要注意在模块和任务内都定义了变量“c”,它们是两个不同的变量,在任务内定义的局部变量只能在任务内部使用。所以在这种情况下,出现在任务定义结构内的标识符“c”都代表局部变量,而在任务定义结构外出现的“c”那么代表着模块内定义的变量c。下面是一个具体的、实现交通灯控制的例子,在该模块中使用任务可以使程序简洁易懂。

【例7-4】交通灯控制模块。moduletraffic_lights;regclock,red,amber,green;parameteron=1,off=0,red_tics=350,amber_tics=30,green_tics=200;//交通灯初始化

initial red=off;initial amber=off;initial green=off;//交通灯控制时序alwaysbeginred=on; //开红灯

light(red,red_tics); //调用等待任务

green=on; //开绿灯

light(green,green_tics); //等待

amber=on; //开黄灯

light(amber,amber_tics); //等待end//定义交通灯开启时间的任务tasklight;outputcolor;input[31:0]tics;beginrepeat(tics)@(posedgeclock);//等待tics个时钟的上升沿

color=off;//关灯

endendtask//产生时钟脉冲的always块alwaysbegin#100clock=0;#100clock=1;endendmodule任务举例1modulealutask(code,a,b,c);input[1:0]code;input[3:0]a,b;output[4:0]c;reg[4:0]c;taskmy_and;//任务定义,注意无端口列表input[3:0]a,b;//a,b,out名称的作用域范围为task任务内部output[4:0]out;integeri;beginfor(i=3;i>=0;i=i-1)out[i]=a[i]&b[i];//按位与endendtaskalways@(codeoraorb)begincase(code)2'b00:my_and(a,b,c);/*调用任务my_and,需注意端口列表的顺序应与任务定义中的一致,这里的a,b,c分别对应任务定义中的a,b,out*/2'b01:c=a|b;//或2'b10:c=a-b;//相减2‘b11:c=a+b;//相加default:c=2’bx;endcaseendendmodule任务举例2modulesort4(ra,rb,rc,rd,a,b,c,d);output[3:0]ra,rb,rc,rd;input[3:0]a,b,c,d;reg[3:0]ra,rb,rc,rd;reg[3:0]va,vb,vc,vd;always@(aorborcord)begin{va,vb,vc,vd}={a,b,c,d};sort2(va,vc);//va与vc互换。sort2(vb,vd);//vb与vd互换。sort2(va,vb);//va与vb互换。sort2(vc,vd);//vc与vd互换。sort2(vb,vc);//vb与vc互换。{ra,rb,rc,rd}={va,vb,vc,vd};endtasksort2;inout[3:0]x,y;reg[3:0]tmp;if(x>y)begintmp=x;//x与y变量的内容互换,要求顺序执行,所以采用阻塞赋值方式。

x=y;y=tmp;endendtaskendmodule任务举例3下面的任务中含有时序控制和一个输入,并引用了一个module变量,但没有输出、输入输出和内部变量,也不显示任何结果。moduletop;regclk,a,b;DUTu1(out,a,b,clk);always#5clk=!clk;

taskneg_clocks;input[31:0]number_of_edges;repeat(number_of_edges)@(negedgeclk);

endtaskinitialbeginclk=0;a=1;b=1;

neg_clocks(3);//任务调用

a=0;neg_clocks(5);b=0;endendmodule时序控制中使用的信号〔例如ck〕一定不能作为任务的输入,因为输入值只向该任务传送一次。任务举例4下面的任务中有输入,输出,时序控制和一个内部变量,并且引用了一个module变量。但没有双向端口,也没有显示。任务调用时的参数按任务定义的顺序列出。modulemult(clk,a,b,out,en_mult);

inputclk,en_mult;

input[3:0]a,b;

output[7:0]out;

reg[7:0]out;

always

@(posedgeclk)multme(a,b,out);//任务调用

taskmultme;//任务定义

input[3:0]xme,tome;

output[7:0]result;

wait(en_mult)result=xme*tome;

endtaskendmodule归纳1.任务的定义与引用都在一个module模块内部2.任务的定义与module的定义有些类似,同样需要进行端口说明与数据类型说明。另外,任务定义的内部没有过程块,但在块语句中可以包含定时控制局部。3.当任务被引用时,任务被激活。4.一个任务可以调用别的任务或函数。禁止命名块和任务moduledo_arith(out,a,b,c,d,e,clk,en_mult);

inputclk,en_mult;input[7:0]a,b,c,d,e;

output[15:0]out;

reg[15:0]out;

always@(posedgeclk)begin:arith_block//***命名块***

reg[3:0]tmp1,tmp2;//***局部变量***

{tmp1,tmp2}=f_or_and(a,b,c,d,e);//函数调用

if(en_mult)multme(tmp1,tmp2,out);//任务调用

endalways@(negedgeen_mult)begin//中止运算

disablemultme;//***禁止任务***

disable

arith_block;//***禁止命名块***

end//下面[定义任务和函数

……endmodule禁止命名块和任务disable语句终结一个命名块或任务的所有活动。也就是说,在一个命名块或任务中的所有语句执行完之前就返回。语法:disable<块名称>或disable<任务名称>当命名块或任务被禁止时,所有因他们调度的事件将从事件队列中去除disable是典型的不可综合语句。在前面的例子中,只禁止命名块也可以到达同样的目的:所有由命名块、任务及其中的函数调度的事件都被取消。任务的使用如果子程序满足下面任何一个条件,那么必须使用任务而不能使用函数。1.子程序中包含有延迟,时序或者事件控制结构2.没有输出或者输出变量超过一个3.没有输入变量7.2Verilog函数函数(function)类似于其它编程语言中的函数概念。与任务一样,VerilogHDL语言中的函数使用包括了函数的定义和函数的调用。

1.函数的定义函数定义的语法如下:function<返回值类型或返回值宽度><函数名>;<输入端口说明><局部变量说明>begin<行为语句1;><行为语句2;>……<行为语句n;>endendfunction其中:关键词function和endfunction表示这局部语句是一个函数定义结构,function语句标识着这个函数定义结构的开头;endfunction语句标识着函数定义结构的结尾。“<函数名>”是为被定义函数所取的一个名称,对被定义函数的调用是通过这个函数名来进行的。这个函数名在函数定义结构内部还代表着一个内部变量,函数调用后的返回值是通过这个函数名变量传递给调用语句(调用语句就是对函数进行了调用的语句)的。“<返回值类型或返回值宽度>”是可选的,它用来对函数调用返回数据的类型或宽度进行说明(这个数据是通过函数名返回的),它可以有如下三种形式:(1)“[msb:lsb]”:这种形式说明函数名所代表的返回数据变量是一个多位的存放器变量,它的位数由[msb:lsb]指定,比方如下函数定义语句:function[7:0]adder;就定义了一个函数“adder”,它的函数名“adder”还代表着一个8位宽的存放器变量,其最高位为第7位,最低位为第0位。(2)“integer”:这种形式说明函数名代表的返回变量是一个整数型变量。(3)“real”:这种形式说明函数名代表的返回变量是一个实数型变量。如果在函数的声明语句中<返回值类型或返回值宽度>这一项缺省,那么认为函数名代表的变量是一个一位的存放器。“<输入端口说明>”用来对函数各个输入端口的宽度和类型进行说明,在函数定义中必须至少有一个输入端口;该输入端口说明语句的语法与模块定义时的输入端口说明语句的语法是一致的。要注意在函数定义结构中不允许出现任何输出端口(output)和输入/输出端口(inout)。“<局部变量说明>”是对函数内部局部变量进行的宽度和类型说明。“行为语句”局部由关键词“begin”和“end”界定,这局部语句指明了函数被调用时要执行的操作,它们决定着函数实现的运算功能。在函数被调用时,这些行为语句将按串行方式得到执行。函数的主要特性函数定义中不能包含任何时序控制语句。函数至少有一个输入,不能包含任何输出或双向端口。函数只返回一个数据,其缺省为reg类型。传送到函数的参数顺序和函数输入参数的说明顺序相同。函数在模块〔module)内部定义。函数不能调用任务,但任务可以调用函数。虽然函数只返回单个值,但返回的值可以直接给信号连接赋值。这在需要有多个输出时非常有效。{o1,o2,o3,o4}=f_or_and(a,b,c,d,e);

【例7-5】函数的定义。function[7:0]getbyte;

//函数定义结构的开头,注意此行中不能出现端口名列表input[63:0]word; //说明第一个输入端口(输入端口1)input[3:0]bytenum;//说明第二个输入端口(输入端口2)integerbit; //局部变量说明reg[7:0]temp; //局部变量说明beginfor(bit=0;bit<=7;bit=bit+1)temp[bit]=word[((bytenum-1)*8)+bit];//第一条行为语句getbyte=temp;

//第二条行为语句:将结果赋值给函数名变量getbyteendendfunction //函数定义结束上例定义了一个名为“getbyte”的函数,该函数有两个输入端口:一个64位的输入端口“word”和一个4位的输入端口“bytenum”。同时该函数还定义了两个局部变量bit(整数型变量)和temp(8位存放器变量)。上例所定义的函数被调用时,begin和end之间的行为语句局部得到执行,其中第一条for循环语句实现了将64位存放器word的一局部位的值赋给变量temp,需要赋值的位由输入bytenum指定。第二条赋值语句将得到的temp值赋给函数名变量“getbyte”。这样就可以通过这个函数名变量将结果值返回给调用语句。在进行函数定义时必须注意:(1)与任务一样,函数定义结构只能出现在模块中,而不能出现在过程块内。(2)函数至少必须有一个输入端口。(3)函数不能有任何类型的输出端口(output端口)和双向端口(inout端口)。(4)在函数定义结构中的行为语句局部内不能出现任何类型的时间控制描述,也不允许使用disable终止语句。(5)与任务定义一样,函数定义结构内部不能出现过程块。(6)在一个函数内可以对其它函数进行调用,但是函数不能调用其它任务。(7)在第一行“function”语句中不能出现端口名列表。function[7:0]get0;input[7:0]x;reg[7:0]count;integeri;begincount=0;for(i=0;i<=7;i=i+1)if(x[i]=1'b0)count=count+1;get0=count;endendfunction

2.函数的调用函数调用的格式如下:

<函数名>(<输入表达式1>,<输入表达式2>,……,<输入表达式m>);其中,m个“<输入表达式>”与函数定义结构中说明的各个输入端口一一对应,它们代表着各个输入端口的输入数据。这些输入表达式的排列顺序及类型必须与各个输入端口在函数定义结构中的排列顺序及类型保持严格一致。在调用函数时必须注意如下两点:(1)函数的调用不能单独作为一条语句出现,它只能作为一个操作数出现在调用语句内。例如,下面这条语句对前面所定义的函数“getbyte”进行了调用:out=getbyte(input1,number);在这条调用语句中,函数调用局部“getbyte(input1,number)”被看作是一个操作数,这个操作数的取值就是函数调用的返回值。在整个调用语句中,函数调用局部是作为“赋值表达式”出现在整条过程赋值语句中的,函数调用局部不能单独地作为一条语句出现,这就是说语句“getbyte(input1,number);”是非法的。(2)函数调用既能出现在过程块中,也能出现在assign连续赋值语句中。比方语句:wire[7:0]net1;reg[63:0]input1;assignnet1=getbyte(input1,3);中的函数调用就出现在一条连续赋值语句内,这条语句指定由函数调用返回值对8位连线型变量net1进行连续驱动。进行函数调用后,上述函数调用格式中的函数名将代表返回值参与调用语句(对函数进行调用的语句)的执行。举例来说,对上面定义的函数“getbyte”进行调用的模块可以是例7-20所示的模块:【例7-6】函数的调用。moduledemo_function_call;reg[7:0]call_output;reg[63:0]input1;reg[3:0]input2;initialbegininput1=64'h123456789abcdef0;input2=3;call_output=getbyte(input1,input2); //第一次调用$display("afterthefirstcall,thereturnedvalueis:%b",call_output);#100;$display(“secondcall,returnvalue:%b”,getbyte(input1,6)); //第二次调用end<函数getbyte定义局部>endmodule上例模块中的initial过程块对函数“getbyte”进行了两次调用:函数getbyte的第一次调用是作为过程赋值语句“call_output=getbyte(input1,input2);”右端的赋值表达式出现的,调用时的输入表达式分别为两个存放器变量input1和input2,它们将与函数定义结构中的第一个和第二个输入端口相对应,因此这两个存放器变量的取值将分别被传递给函数输入端口“word”和“bytenum”。函数调用完成后,过程赋值语句中的“getbyte”将具有函数调用的返回值,这个返回值将作为“赋值表达式”参与对变量call_output进行的过程赋值操作。函数getbyte的第二次调用是作为系统任务$display语句内的“输出变量表项”出现的,调用时的输入表达式分别是一个存放器变量“input1”和一个常数“6”,它们的值将被分别被传递给函数定义中的两个输入端口“word”和“bytenum”。函数调用完成后,$display语句中的“getbyte”将具有函数调用的返回值,这个返回值将作为“输出变量表项”参与$display语句的执行。由以上所给出的函数定义和函数调用的例子可以看出,在函数定义中必须有一条赋值语句来对函数名变量进行赋值,这样才能通过这个函数名变量来将函数调用的结果(返回值)传递给调用语句。在例7-5给出的函数定义中,语句“getbyte=temp;”就是这么一条赋值语句,它将函数调用的运算结果(变量temp的取值)赋值给函数名变量getbyte。函数的每一次调用只能通过函数名变量返回一个值。在有些时候我们要求一次函数调用能返回多个值,可以通过在函数定义和函数调用中使用合并操作符“{”、“}”来解决这一问题。

【例7-7】由一个函数返回多个值的方法。moduledemo_multiout_function;reg[7:0]a,b,c,d;initialbegina=8'h54;b=8'h32;{c,d}=multiout_fun(a,b);

//语句S1,进行了函数调用的过程赋值语句$display("thevalueofcis:%hdis:%h",c,d);endfunction[15:0]multiout_fun;input[7:0]in1,in2;reg[7:0]out1;reg[7:0]out2;beginout1=in1&in2;out2=in1|in2;multiout_fun={out1,out2};

//语句S2,对函数名变量进行赋值endendfunctionendmodule

上例中定义了一个函数multi_fun,我们想从这个函数中同时得到两个返回值out1和out2,由于函数调用时只能由函数名返回一个值,因此out1和out2的取值不可能同时独立地被返回,但是可以借助于合并操作符“{,}”来实现多个输出值的同时输出:在函数定义内,语句S2通过合并操作符把两个输出值out1和out2合并成一个值并将它赋值给函数名变量“multiout_fun”。这样,虽然函数在形式上还是只有multiout_fun变量这一个返回值,但是在这个返回值内实际上包含了两个输出数据。在对函数进行了调用的过程赋值语句S1中,函数调用返回值“multiout_fun”被赋值给由两个变量c和d组合而成的一个合并变量。这样,在执行了赋值语句S1后,函数调用返回值“multiout_fun”中所包含的两局部输出值将分别被赋值给变量c和d,这样也就是实现了将函数输出out1和out2的值分别传递给变量c和d。在使用函数时必须注意:由于在函数定义结构中不能出现任何类型的时间控制语句,所以函数调用执行所需的时间只能是零仿真时间,也就是说,函数调用启动时刻和函数调用返回时刻是相同的。

函数中不能有时序控制,但调用它的过程可以有时序控制。函数名f_or_and在函数中作为register使用moduleorand(a,b,c,d,e,out);input[7:0]a,b,c,d,e;output[7:0]out;reg[7:0]out;always

@(aorborcordore)out=f_or_and(a,b,c,d,e);//函数调用

function[7:0]f_or_and;

input[7:0]a,b,c,d,e;

if(e==1)f_or_and=(a|b)&(c|d);

elsef_or_and=0;

endfunctionendmodule函数返回值可以声明为其它register类型:integer,real,或time。在任何表达式中都可调用函数modulechecksub(neg,a,b);

outputneg;

regneg;

inputa,b;

function

integer

subtr;

input

[7:0]in_a,in_b;

subtr

=in_a-in_b;//结果可能为负

endfunction

always

@(aorb)

if(subtr(a,b)<0)neg=1;

elseneg=0;endmodule从函数返回的值函数的定义蕴含声明了与函数同名的、函数内部的存放器。如在函数的声明语句中<返回值的类型或范围>为缺省,那么这个存放器是一位的,否那么是与函数定义中<返回值的类型或范围>一致的存放器。函数的定义把函数返回值所赋值存放器的名称初始化为与函数同名的内部变量。通过对两次调用函数getbyte的结果值进行位拼接运算来生成一个字。word=control?{getbyte(msbyte),getbyte(lsbyte)}:0;

【例7-8】乘加器。`timescale1ns/10psmodulemult_acc(out,ina,inb,clk,clr);input[7:0]ina,inb;inputclk,clr;output[15:0]out;wire[15:0]mult_out,adder_out;reg[15:0]out;parameterset=10;parameterhld=20;assignadder_out=mult_out+out;//描述了加法器assignmult_out=mult(ina,inb);

//进行了函数调用,实现了乘法器always@(posedgeclkorposedgeclr) //描述了时序逻辑,实现清零功能及输出锁存beginif(clr)out=16'h0000;elseout=adder_out;endfunction[15:0]mult;//函数定义局部input[7:0]a,b;reg[15:0]r;integeri;beginif(a[0]==1)r=b;elser=0;for(i=1;

i<=7;

i=i+1)beginif(a[i]==1)r=r+b<<i;endmult=r;endendfunctionendmodule图7.2乘加器结构上例所示模块由两条连续赋值语句、always过程块、函数定义和延时说明块等几局部组成。其中:第一条连续赋值语句描述了一个加法器,其输入端为multi_out和out,输出端为adder_out。第二条连续赋值语句通过对函数mult的调用实现了一个乘法器,其输入端为ina和inb,输出端为mult_out,其中的函数调用实现了乘法运算功能。always过程块对乘法器的时序逻辑进行了描述,实现了异步清零和输出数据锁存功能,每当清零信号clr变为高电平就进入清零操作状态,对输出锁存器进行清零操作;在正常工作状态下输出锁存器将在时钟clk的每个上升沿得到刷新。函数定义局部对函数mult进行了定义,该函数实现了一个8位乘法运算功能,它有两个输入变量a和b,返回一个16位的运算结果。用函数和case语句描述的编码器〔不含优先顺序〕modulecode_83(din,dout);input[7:0]din;output[2:0]dout;function[2:0]code;//函数定义input[7:0]din;//函数只有输入,输出为函数名本身casex(din)8'b1xxx_xxxx:code=3'h7;8'b01xx_xxxx:code=3'h6;8'b001x_xxxx:code=3'h5;8'b0001_xxxx:code=3'h4;8'b0000_1xxx:code=3'h

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论