一周掌握FPGAVerilogHDL语法day4

FPGA技术江湖 2025-03-23 08:30

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。


今天给大侠带来的是一周掌握FPGA Verilog HDL 语法,今天开启第四天。
一周掌握FPGA Verilog HDL语法 day 3 被平台综合了,如果想要看详细介绍的话,可以到公众号内部"行侠仗义"栏目下获取。
上一篇提到了阻塞与非阻塞、条件语句、块语句等,此篇我们继续来看case语句以及后续其他内容,结合实例理解理论语法,会让你理解运用的更加透彻。下面咱们废话就不多说了,一起来看看吧。




case语句




case语句是一种多分支选择语句,if语句只有两个分支可供选择,而实际问题中常常需要用到多分支选择,Verilog语言提供的case语句直接处理多分支选择。

case语句通常用于微处理器的指令译码,它的一般形式如下:

1) case(表达式) endcase 

2) casez(表达式) endcase 

3) casex(表达式) endcase case


分支项的一般格式如下: 

分支表达式: 语句 

缺省项(default项): 语句 


说明: 

a) case括弧内的表达式称为控制表达式,case分支项中的表达式称为分支表达式。控制表达式通常表示为控制信号的某些位,分支表达式则用这些控制信号的具体状态值来表示,因此分支表达式又可以称为常量表达式。

b) 当控制表达式的值与分支表达式的值相等时,就执行分支表达式后面的语句。如果所有的分支表达式的值都没有与控制表达式的值相匹配的,就执行default后面的语句。 

c) default项可有可无,一个case语句里只准有一个default项。下面是一个简单的使用case语句的例子。该例子中对寄存器rega译码以确定result的值。

  reg [15:0] rega;   reg [9:0result;    case(rega)       16 'd0: result = 10 'b0111111111;       16 'd1: result = 10 'b1011111111;       16 'd2: result = 10 'b1101111111;       16 'd3: result = 10 'b1110111111;       16 'd4: result = 10 'b1111011111;       16 'd5: result = 10 'b1111101111;       16 'd6: result = 10 'b1111110111;       16 'd7: result = 10 'b1111111011;       16 'd8: result = 10 'b1111111101;       16 'd9: result = 10 'b1111111110;       defaultresult = 'bx;   endcase

d) 每一个case分项的分支表达式的值必须互不相同,否则就会出现矛盾现象(对表达式的同一个值,有多种执行方案)。 

e) 执行完case分项后的语句,则跳出该case语句结构,终止case语句的执行。 

f) 在用case语句表达式进行比较的过程中,只有当信号的对应位的值能明确进行比较时,比较才能成功。因此要注意详细说明case分项的分支表达式的值。 

g) case语句的所有表达式的值的位宽必须相等,只有这样控制表达式和分支表达式才能进行对应位的比较。一个经常犯的错误是用'bx, 'bz 来替代 n'bx, n'bz,这样写是不对的,因为信号x, z的缺省宽度是机器的字节宽度,通常是32位(此处 n 是case控制表达式的位宽)。 

下面将给出 case, casez, casex 的真值表:


case语句与if_else_if语句的区别主要有两点:

1) 与case语句中的控制表达式和多分支表达式这种比较结构相比,if_else_if结构中的条件表达式更为直观一些。 
2) 对于那些分支表达式中存在不定值x和高阻值z位时,case语句提供了处理这种情况的手段。下面的两个例子介绍了处理x,z值位的case语句。
例1:
  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 = flagb? 'bx : 0;       default: result = 'bx;   endcase


例2:

case(sig)     1 'bz: $display("signal is floating");     1 'bx: $display("signal is unknown");     default$display("signal is %b", sig); endcase


Verilog HDL针对电路的特性提供了case语句的其它两种形式用来处理case语句比较过程中的不必考虑的情况( don't care condition )。其中casez语句用来处理不考虑高阻值z的比较过程,casex语句则将高阻值z和不定值都视为不必关心的情况。所谓不必关心的情况,即在表达式进行比较时,不将该位的状态考虑在内。这样在case语句表达式进行比较时,就可以灵活地设置以对信号的某些位进行比较。见下面的两个例子:

3

  reg[7:0] ir;     casez(ir)        8 'b1???????: instruction1(ir);        8 'b01??????: instruction2(ir);        8 'b00010???: instruction3(ir);        8 'b000001??: instruction4(ir);   endcase


例4:

  reg[7:0] r, mask;     mask = 8'bx0x0x0x0;     casex(r^mask)        8 'b001100xx: stat1;        8 'b1100xx00: stat2;        8 'b00xx0011: stat3;        8 'bxx001100: stat4;   endcase



由于使用条件语句不当在设计中生成了原本没想到有的锁存器

Verilog HDL设计中容易犯的一个通病是由于不正确使用语言,生成了并不想要的锁存器。下面我们给出了一个在“always"块中不正确使用if语句,造成这种错误的例子。

检查一下左边的"always"块,if语句保证了只有当al=1时,q才取d的值。这段程序没有写出 al = 0 时的结果, 那么当al=0时会怎么样呢? 

在"always"块内,如果在给定的条件下变量没有赋值,这个变量将保持原值,也就是说会生成一个锁存器。

如果设计人员希望当 al = 0 时q的值为0,else项就必不可少了,请注意看右边的"always"块,整个Verilog程序模块综合出来后,"always"块对应的部分不会生成锁存器。 

Verilog HDL程序另一种偶然生成锁存器是在使用case语句时缺少default项的情况下发生的。 

case语句的功能是:在某个信号(本例中的sel)取不同的值时,给另一个信号(本例中的q)赋不同的值。注意看下图左边的例子,如果sel=0,q取a值,而sel=11,q取b的值。这个例子中不清楚的是:如果sel取00和11以外的值时q将被赋予什么值?在下面左边的这个例子中,程序是用Verilog HDL写的,即默认为q保持原值,这就会自动生成锁存器。

右边的例子很明确,程序中的case语句有default项,指明了如果sel不取00或11时,编译器或仿真器应赋给q的值。程序所示情况下,q赋为0,因此不需要锁存器。 

以上就是怎样来避免偶然生成锁存器的错误。如果用到if语句,最好写上else项。如果用case语句,最好写上default项。遵循上面两条原则,就可以避免发生这种错误,使设计者更加明确设计目标,同时也增强了Verilog程序的可读性。






循环语句



在Verilog HDL中存在着四种类型的循环语句,用来控制执行语句的执行次数。 

1) forever 连续的执行语句。 

2) repeat 连续执行一条语句 n 次。 

3) while 执行一条语句直到某个条件不满足。如果一开始条件即不满足(为假),  则语句一次也不能被执行。 

4) for通过以下三个步骤来决定语句的循环执行。 

a) 先给控制循环次数的变量赋初值。 

b) 判定控制循环的表达式的值,如为假则跳出循环语句,如为真则执行指定的语句后,转到第三步。 

c) 执行一条赋值语句来修正控制循环变量次数的变量的值,然后返回第二步。下面对各种循环语句详细的进行介绍。



forever语句

forever语句的格式如下: 

forever 语句; 或 forever begin 多条语句 end 

forever循环语句常用于产生周期性的波形,用来作为仿真测试信号。它与always语句不同处在于不能独立写在程序中,而必须写在initial块中。



repeat语句

repeat语句的格式如下: 

repeat(表达式) 语句;或 repeat(表达式) begin 多条语句 end


在repeat语句中,其表达式通常为常量表达式。下面的例子中使用repeat循环语句及加法和移位操作来实现一个乘法器。

  parameter size=8,longsize=16    reg [size:1] opa, opb;   reg [longsize:1result        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



while语句

while语句的格式如下: 

while(表达式) 语句 或 while(表达式) begin 多条语句 end 

下面举一个while语句的例子,该例子用while循环语句对rega这个八位二进制数中值为1的位进行计数。

    begin: count1s         reg[7:0] tempreg;         count=0        tempreg = rega;         while(tempreg)             begin                 if(tempreg[0]) count = count + 1;                tempreg = tempreg>>1            end     end



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;tempi1                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])                    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




结构说明语句




Verilog语言中的任何过程模块都从属于以下四种结构的说明语句。 

1) initial说明语句 

2) always说明语句 

3) task说明语句 

4) function说明语句 

initial和always说明语句在仿真的一开始即开始执行。initial语句只执行一次。相反,always语句则是不断地重复执行,直到仿真过程结束。在一个模块中,使用initial和always语句的次数是不受限制的。

task和function语句可以在程序模块中的一处或多处调用。其具体使用方法以后再详细地加以介绍。这里只对initial和always语句加以介绍。



initial语句

initial语句的格式如下: 

initial     begin         语句1        语句2        ......         语句n;     end

 [例1]

initial     begin         areg=0//初始化寄存器areg             for(index=0;indexindex=index+1                memory[index]=0//初始化一个memory     end

 在这个例子中用initial语句在仿真开始时对各变量进行初始化。 


[例2]:

  initial       begin           inputs = 'b000000; //初始时刻为0           #10 inputs = 'b011001;          #10 inputs = 'b011011;           #10 inputs = 'b011000;           #10 inputs = 'b001000;       end

从这个例子中,我们可以看到initial语句的另一用途,即用initial语句来生成激励波形作为电路的测试仿真信号。一个模块中可以有多个initial块,它们都是并行运行的。initial块常用于测试文件和虚拟模块的编写,用来产生仿真测试信号和设置信号记录等仿真环境。



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

always @( a or b or c ) //由多个电平触发的always块  begin  …… end

沿触发的always块常常描述时序逻辑,如果符合可综合风格要求可用综合工具自动转换为表示时序逻辑的寄存器组和门级逻辑,而电平触发的always块常常用来描述组合逻辑和带锁存器的组合逻辑,如果符合可综合风格要求可转换为表示组合逻辑的门级逻辑或带锁存器的组合逻辑。一个模块中可以有多个always块,它们都是并行运行的。



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说明语句

如果传给任务的变量值和任务完成后接收结果的变量已定义,就可以用一条语句启动任务。任务完成以后控制就传回启动过程。如任务内部有定时控制,则启动的时间可以与控制返回的时间不同。任务可以启动其它的任务,其它任务又可以启动别的任务,可以启动的任务数是没有限制的。不管有多少任务启动,只有当所有的启动任务完成以后,控制才能返回。 

1) 任务的定义 定义任务的语法如下:任务:

task <任务名><端口及数据类型声明语句> <语句1> <语句2> ..... <语句n> endtask

这些声明语句的语法与模块定义中的对应声明语句的语法是一致的。

2) 任务的调用及变量的传递 

启动任务并传递输入输出变量的声明语句的语法如下: 

任务的调用: 

<任务名>(端口1,端口2,...,端口n); 


下面的例子说明怎样定义任务和调用任务:任务定义:


    task my_task;         input a, b;         inout c;         output d, e;         …        <语句> //执行任务工作相应的语句         …        c = foo1; //赋初始值         d = foo2; //对任务的输出变量赋值t         e = foo3;     endtask

任务调用:my_task(v,w,x,y,z); 

任务调用变量(v,w,x,y,z)和任务定义的I/O变量(a,b,c,d,e)之间是一一对应的。当任务启动时,由v,w,和x.传入的变量赋给了a,b,和c,而当任务完成后的输出又通过c,d和e赋给了x,y和z。下面是一个具体的例子用来说明怎样在模块的设计中使用任务,使程序容易读懂:

module traffic_lights; 
    reg clock, red, amber, green;         parameter on=1, off=0, red_tics=350    amber_tics=30,green_tics=200         //交通灯初始化      initial red=off;      initial amber=off;      initial green=off;          //交通灯控制时序     always         begin             red=on//开红灯             light(red,red_tics); //调用等待任务             green=on//开绿灯             light(green,green_tics); //等待             amber=on//开黄灯             light(amber,amber_tics); //等待        end         //定义交通灯开启时间的任务     task light(color,tics);         output color;         input[31:0] tics;                 begin             repeat(tics) @(posedge clock);//等待tics个时钟的上升沿               color=off;//关灯         end     endtask         //产生时钟脉冲的always块     always         begin             #100 clock=0;             #100 clock=1;         end         endmodule

这个例子描述了一个简单的交通灯的时序控制,并且该交通灯有它自己的时钟产生器。


三. function说明语句 

函数的目的是返回一个用于表达式的值。 

1、定义函数的语法:

function <返回值的类型或范围> (函数名);     <端口说明语句>     <变量类型说明语句>         begin             <语句>             ........         end endfunction

请注意<返回值的类型或范围>这一项是可选项,如缺省则返回值为一位寄存器类型数据。下面用例子说明:

function [7:0] getbyte;     input [15:0] address;         begin         <说明语句> //从地址字中提取低字节的程序         getbyte = result_expression; //把结果赋予函数的返回字节     end endfunction

2、从函数返回的值 

函数的定义蕴含声明了与函数同名的、函数内部的寄存器。如在函数的声明语句中<返回值的类型或范围>为缺省,则这个寄存器是一位的,否则是与函数定义中<返回值的类型或范围>一致的寄存器。函数的定义把函数返回值所赋值寄存器的名称初始化为与函数同名的内部变量。下面的例子说明了这个概念:getbyte被赋予的值就是函数的返回值。y 函数的调用 

3、函数的调用

是通过将函数作为表达式中的操作数来实现的。

其调用格式如下:<函数名> (<表达式><,<表达式>>*) 其中函数名作为确认符。下面的例子中通过对两次调用函数getbyte的结果值进行位拼接运算来生成一个字。word = control? {getbyte(msbyte),getbyte(lsbyte)} : 0; 

4、函数的使用规则 与任务相比较函数的使用有较多的约束,下面给出的是函数的使用规则: 

1) 函数的定义不能包含有任何的时间控制语句,即任何用#、@、或wait来标识的语句。 

2) 函数不能启动任务。

3) 定义函数时至少要有一个输入参量。 

4) 在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具有和函数名相同的名字。 

5、举例说明 下面的例子中定义了一个可进行阶乘运算的名为factorial的函数,该函数返回一个32位的寄存器类型的值,该函数可后向调用自身,并且打印出部分结果值。

module tryfact;      //函数的定义    function[31:0]factorial;         input[3:0]operand;         reg[3:0]index;         begin         factorial = operand? 1 : 0        for(index=2;index<=operand;index=index+1        factorial = index * factorial;     end     endfunction         //函数的测试    reg[31:0]result    reg[3:0]n;     initial     begin         result=1        for(n=2;n<=9;n=n+1            begin                 $display("Partial result n= %d result= %d", n, result);                 result = n * factorial(n)/((n*2)+1);             end         $display("Finalresult=%d",result);     end endmodule//模块结束

前面我们已经介绍了足够的语句类型可以编写一些完整的模块。后续更新,将举许多实际的例子进行介绍。这些例子都给出了完整的模块描述,因此可以对它们进行仿真测试和结果检验。通过学习和练习我们就能逐步掌握利用Verilog HDL设计数字系统的方法和技术。





系统函数和任务




Verilog HDL语言中共有以下一些系统函数和任务:$bitstoreal, $rtoi, $display, $setup, $finish, $skew, $hold, $setuphold, $itor, $strobe, $period, $time, $printtimescale, $timefoemat, $realtime, $width, $real tobits, $write, $recovery。

在Verilog HDL语言中每个系统函数和任务前面都用一个标识符$来加以确认。这些系统函数和任务提供了非常强大的功能。有兴趣的同学可以自行查阅资料。

下面对一些常用的系统函数和任务逐一加以介绍。



$display和$write任务 

格式: 

$display(p1,p2,....pn); 

$write(p1,p2,....pn);


这两个函数和系统任务的作用是用来输出信息,即将参数p2到pn按参数p1给定的格式输出。参数p1通常称为“格式控制”,参数p2至pn通常称为“输出表列”。这两个任务的作用基本相同。$display自动地在输出后进行换行,$write则不是这样。如果想在一行里输出多个信息,可以使用$write。在$display和$write中,其输出格式控制是用双引号括起来的字符串,它包括两种信息: 

  • 格式说明,由"%"和格式字符组成。它的作用是将输出的数据转换成指定的格式输出。格式说明总是由“%”字符开始的。对于不同类型的数据用不同的格式输出。表一中给出了常用的几种输出格式。如下表一:


  • 普通字符,即需要原样输出的字符。其中一些特殊的字符可以通过表二中的转换序列来输出。下面表中的字符形式用于格式字符串参数中,用来显示特殊的字符。如下表二:



在$display和$write的参数列表中,其“输出表列”是需要输出的一些数据,可以是表达式。下面举几个例子说明一下。
[例1]:
module disp;     initial         begin             $display("\\\t%%\n\"\123");         end endmodule
输出结果为:
 \% 
"S 
从上面的这个例子中可以看到一些特殊字符的输出形式(八进制数123就是字符S)。
[例2]:
module disp;     reg[31:0] rval;     pulldown(pd);         initial         begin             rval=101            $display("rval=%h hex %d decimal", rval, rval);             $display("rval=%o otal %b binary", rval, rval);             $display("rval has %c ascii character value",rval);             $display("pd strength value is %v",pd);             $display("current scope is %m");             $display("%s is ascii value for 101",101);             $display("simulation time is %t",$time);         end         endmodule
其输出结果为: 
输出数据的显示宽度 在$display中,输出列表中数据的显示宽度是自动按照输出格式进行调整的。这样在显示输出数据时,在经过格式转换以后,总是用表达式的最大可能值所占的位数来显示表达式的当前值。在用十进制数格式输出时,输出结果前面的0值用空格来代替。对于其它进制,输出结果前面的0仍然显示出来。例如对于一个值的位宽为12位的表达式,如按照十六进制数输出,则输出结果占3个字符的位置,如按照十进制数输出,则输出结果占4个字符的位置。这是因为这个表达式的最大可能值为FFF(十六进制)、4095(十进制)。可以通过在%和表示进制的字符中间插入一个0自动调整显示输出数据宽度的方式。下例: 
$display("d=%0h a=%0h",data,addr); 
这样在显示输出数据时,在经过格式转换以后,总是用最少的位数来显示表达式的当前值。下面举例说明: 
[例3]:
module printval;     reg[11:0]r1;         initial          begin          r1=10         $display("Printing with maximum size=%d=%h",r1,r1);          $display("Printing with minimum size=%0d=%0h",r1,r1);          end enmodule
输出结果为: 
Printing with maximum size=10=00a: 
printing with minimum size=10=a; 

如果输出列表中表达式的值包含有不确定的值或高阻值,其结果输出遵循以下规则: 
(1).在输出格式为十进制的情况下: 
  • 如果表达式值的所有位均为不定值,则输出结果为小写的x。

  • 如果表达式值的所有位均为高阻值,则输出结果为小写的z。

  • 如果表达式值的部分位为不定值,则输出结果为大写的X。

  • 如果表达式值的部分位为高阻值,则输出结果为大写的Z。 





(2).在输出格式为十六进制和八进制的情况下:
  • 每4位二进制数为一组代表一位十六进制数,每3位二进制数为一组代表一位八进制数。

  • 如果表达式值相对应的某进制数的所有位均为不定值,则该位进制数的输出的结果为小写的x。

  • 如果表达式值相对应的某进制数的所有位均为高阻值,则该位进制数的输出结果为小写的z。

  • 如果表达式值相对应的某进制数的部分位为不定值,则该位进制数输出结果为大写的X。

  • 如果表达式值相对应的某进制数的部分位为高阻值,则该位进制数输出结果为大写的Z。 






对于二进制输出格式,表达式值的每一位的输出结果为0、1、x、z。下面举例说明: 
语句输出结果: 
$display("%d", 1'bx); 输出结果为:x 
$display("%h", 14'bx0_1010); 输出结果为:xxXa 
$display("%h %o",12'b001x_xx10_1x01,12'b001_xxx_101_x01); 输出结果为:XXX 1x5X 

注意:因为$write在输出时不换行,要注意它的使用。可以在$write中加入换行符\n,以确保明确的输出显示格式。



系统任务$monitor

格式: 

$monitor(p1,p2,.....,pn); 

$monitor; 

$monitoron; 

$monitoroff;


任务$monitor提供了监控和输出参数列表中的表达式或变量值的功能。其参数列表中输出控制格式字符串和输出表列的规则和$display中的一样。
当启动一个带有一个或多个参数的$monitor任务时,仿真器则建立一个处理机制,使得每当参数列表中变量或表达式的值发生变化时,整个参数列表中变量或表达式的值都将输出显示。如果同一时刻,两个或多个参数的值发生变化,则在该时刻只输出显示一次。但在$monitor中,参数可以是$time系统函数。这样参数列表中变量或表达式的值同时发生变化的时刻可以通过标明同一时刻的多行输出来显示。如:
 $monitor($time,,"rxd=%b txd=%b",rxd,txd); 
在$display中也可以这样使用。注意在上面的语句中,“,,"代表一个空参数。空参数在输出时显示为空格。 $monitoron和$monitoroff任务的作用是通过打开和关闭监控标志来控制监控任务$monitor的启动和停止,这样使得程序员可以很容易的控制$monitor何时发生。其中$monitoroff任务用于关闭监控标志,停止监控任务$monitor,$monitoron则用于打开监控标志,启动监控任务$monitor。通常在通过调用$monitoron启动$monitor时,不管$monitor参数列表中的值是否发生变化,总是立刻输出显示当前时刻参数列表中的值,这用于在监控的初始时刻设定初始比较值。在缺省情况下,控制标志在仿真的起始时刻就已经打开了。
在多模块调试的情况下,许多模块中都调用了$monitor,因为任何时刻只能有一个$monitor起作用,因此需配合$monitoron与$monitoroff使用,把需要监视的模块用$monitoron打开,在监视完毕后及时用$monitoroff关闭,以便把$monitor 让给其他模块使用。$monitor与$display的不同处还在于$monitor往往在initial块中调用,只要不调用$monitoroff,$monitor便不间断地对所设定的信号进行监视。


时间度量系统函数$time
在Verilog HDL中有两种类型的时间系统函数:$time和$realtime。用这两个时间系统函数可以得到当前的仿真时刻。
 1、系统函数$time 
$time可以返回一个64比特的整数来表示的当前仿真时刻值。该时刻是以模块的仿真时间尺度为基准的。下面举例说明。 
[例1]:
`timescale 10ns/1ns 
module test;     reg set        parameter p=1.6        initial         begin         $monitor($time,,"set=",set);         #p set=0        #p set=1        end         endmodule
输出结果为: 
0 set=x 
2 set=0 
3 set=1
在这个例子中,模块test想在时刻为16ns时设置寄存器set为0,在时刻为32ns时设置寄存器set为1。但是由$time记录的set变化时刻却和预想的不一样。这是由下面两个原因引起的:
1) $time显示时刻受时间尺度比例的影响。在上面的例子中,时间尺度是10ns,因为$time输出的时刻总是时间尺度的倍数,这样将16ns和32ns输出为1.6和3.2。 
2) 因为$time总是输出整数,所以在将经过尺度比例变换的数字输出时,要先进行取整。在上面的例子中,1.6和3.2经取整后为2和3输出。注意:时间的精确度并不影响数字的取整。 

2、$realtime系统函数 
$realtime和$time的作用是一样的,只是$realtime返回的时间数字是一个实型数,该数字也是以时间尺度为基准的。下面举例说明:
 [例2]: 
`timescale10ns/1ns 
module test;     reg set        parameter p=1.55        initial         begin             $monitor($realtime,,"set=",set);             #p set=0            #p set=1        end endmodule
输出结果为: 
0 set=x 
1.6 set=0 
3.2 set=1
从上面的例子可以看出,$realtime将仿真时刻经过尺度变换以后即输出,不需进行取整操作。所以$realtime返回的时刻是实型数。


系统任务$finish 
格式: 
        $finish; 
        $finish(n); 
系统任务$finish的作用是退出仿真器,返回主操作系统,也就是结束仿真过程。任务$finish可以带参数,根据参数的值输出不同的特征信息。
如果不带参数,默认$finish的参数值为1。下面给出了对于不同的参数值,系统输出的特征信息:
 0 不输出任何信息 
1 输出当前仿真时刻和位置 
2 输出当前仿真时刻,位置和在仿真过程中 所用memory及CPU时间的统计


系统任务$stop
格式: 
        $stop; 
        $stop(n);
$stop任务的作用是把EDA工具(例如仿真器)置成暂停模式,在仿真环境下给出一个交互式的命令提示符,将控制权交给用户。这个任务可以带有参数表达式。根据参数值(0,1或2)的不同,输出不同的信息。参数值越大,输出的信息越多。


系统任务$readmemb和$readmemh 
在Verilog HDL程序中有两个系统任务$readmemb和$readmemh用来从文件中读取数据到存贮器中。这两个系统任务可以在仿真的任何时刻被执行使用,其使用格式共有以下六种: 
1) $readmemb("<数据文件名>",<存贮器名>); 
2) $readmemb("<数据文件名>",<存贮器名>,<起始地址>); 
3) $readmemb("<数据文件名>",<存贮器名>,<起始地址>,<结束地址>); 
4) $readmemh("<数据文件名>",<存贮器名>); 
5) $readmemh("<数据文件名>",<存贮器名>,<起始地址>); 
6) $readmemh("<数据文件名>",<存贮器名>,<起始地址>,<结束地址>); 

在这两个系统任务中,被读取的数据文件的内容只能包含:空白位置(空格,换行,制表格(tab)和form-feeds),注释行(//形式的和/*...*/形式的都允许),二进制或十六进制的数字。数字中不能包含位宽说明和格式说明,对于$readmemb系统任务,每个数字必须是二进制数字,对于$readmemh系统任务,每个数字必须是十六进制数字。数字中不定值x或X,高阻值z或Z,和下划线(_)的使用方法及代表的意义与一般Verilog HDL程序中的用法及意义是一样的。另外数字必须用空白位置或注释行来分隔开。 
在下面的讨论中,地址一词指对存贮器(memory)建模的数组的寻址指针。当数据文件被读取时,每一个被读取的数字都被存放到地址连续的存贮器单元中去。存贮器单元的存放地址范围由系统任务声明语句中的起始地址和结束地址来说明,每个数据的存放地址在数据文件中进行说明。当地址出现在数据文件中,其格式为字符“@”后跟上十六进制数。如: @hh...h 
对于这个十六进制的地址数中,允许大写和小写的数字。在字符“@”和数字之间不允许存在空白位置。可以在数据文件里出现多个地址。当系统任务遇到一个地址说明时,系统任务将该地址后的数据存放到存贮器中相应的地址单元中去。 
对于上面六种系统任务格式,需补充说明以下五点: 
1) 如果系统任务声明语句中和数据文件里都没有进行地址说明,则缺省的存放起始地址为该存贮器定义语句中的起始地址。数据文件里的数据被连续存放到该存贮器中,直到该存贮器单元存满为止或数据文件里的数据存完。 
2) 如果系统任务中说明了存放的起始地址,没有说明存放的结束地址,则数据从起始地址开始存放,存放到该存贮器定义语句中的结束地址为止。 
3) 如果在系统任务声明语句中,起始地址和结束地址都进行了说明,则数据文件里的数据按该起始地址开始存放到存贮器单元中,直到该结束地址,而不考虑该存贮器的定义语句中的起始地址和结束地址。 
4) 如果地址信息在系统任务和数据文件里都进行了说明,那么数据文件里的地址必须在系统任务中地址参数声明的范围之内。否则将提示错误信息,并且装载数据到存贮器中的操作被中断。 
5) 如果数据文件里的数据个数和系统任务中起始地址及结束地址暗示的数据个数不同的话,也要提示错误信息。 
下面举例说明:
先定义一个有256个地址的字节存贮器 
mem: reg[7:0] mem[1:256]; 
下面给出的系统任务以各自不同的方式装载数据到存贮器mem中。
initial $readmemh("mem.data",mem); initial $readmemh("mem.data",mem,16); initial $readmemh("mem.data",mem,128,1);
第一条语句在仿真时刻为0时,将装载数据到以地址是1的存贮器单元为起始存放单元的存贮器中去。第二条语句将装载数据到以单元地址是16的存贮器单元为起始存放单元的存贮器中去,一直到地址是256的单元为止。第三条语句将从地址是128的单元开始装载数据,一直到地址为1的单元。在第三种情况中,当装载完毕,系统要检查在数据文件里是否有128个数据,如果没有,系统将提示错误信息。


系统任务 $random 
这个系统函数提供了一个产生随机数的手段。当函数被调用时返回一个32bit的随机数。它是一个带符号的整形数。 
$random一般的用法是:$ramdom % b ,其中 b>0.它给出了一个范围在(-b+1):(b-1)中的随机数。
下面给出一个产生随机数的例子: 
reg[23:0] rand; 
rand = $random % 60; 
上面的例子给出了一个范围在-59到59之间的随机数,下面的例子通过位并接操作产生一个值在0到59之间的数。 
reg[23:0] rand; 
rand = {$random} % 60; 
利用这个系统函数可以产生随机脉冲序列或宽度随机的脉冲序列,以用于电路的测试。下面例子中的Verilog HDL模块可以产生宽度随机的随机脉冲序列的测试信号源,在电路模块的设计仿真时非常有用。同学们可以根据测试的需要,模仿下例,灵活使用$random系统函数编制出与实际情况类似的随机脉冲序列。
[例]:
 `timescale 1ns/1ns  module random_pulse( dout );     output [9:0] dout;         reg dout;         integer delay1,delay2,k;         initial         begin         #10 dout=0;         for (k=0; k< 100; k=k+1            begin                 delay1 = 20 * ( {$random} % 6);                 // delay1 在0到100ns间变化                delay2 = 20 * ( 1 + {$random} % 3);                 // delay2 在20到60ns间变化                #delay1 dout = 1 << ({$random} %10);                 //dout的0--9位中随机出现1,并出现的时间在0-100ns间变化                #delay2 dout = 0;                 //脉冲的宽度在在20到60ns间变化            end         end         endmodule

Day 4 就到这里,Day 5 继续开始编译预处理 大侠保重,告辞。

图片

END

福利】:QQ交流群173560979,进群备注名字+学校/企业。
淘宝店铺:https://shop588964188.taobao.com
论坛网址:www.sxznfpga.com
叁芯智能FPGA课程

往期精选 

图片
图片

【免费】FPGA工程师人才招聘平台

FPGA人才招聘,企业HR,看过来!

系统设计精选 | 基于FPGA的实时图像边缘检测系统设计(附代码)

基于原语的千兆以太网RGMII接口设计

时序分析理论和timequest使用_中文电子版

求职面试 | FPGA或IC面试题最新汇总篇

FPGA时序分析及约束专题课,Quartus、ISE、Vivado全涉及

FPGA图像处理专题课,Quartus、ISE、Vivado全涉及

资料汇总|FPGA软件安装包、书籍、源码、技术文档…(2025.02.19更新)


图片
图片

FPGA技术江湖广发江湖帖

无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有,QQ微信双选,FPGA技术江湖打造最纯净最专业的技术交流学习平台。


FPGA技术江湖微信交流群

图片

加群主微信,备注姓名+学校/公司+专业/岗位进群


FPGA技术江湖QQ交流群

图片

备注姓名+学校/公司+专业/岗位进群

图片

FPGA技术江湖 任何技术的学习就好比一个江湖,对于每一位侠客都需要不断的历练,从初入江湖的小白到归隐山林的隐世高人,需要不断的自我感悟自己修炼,让我们一起仗剑闯FPGA乃至更大的江湖。
评论 (0)
  •       知识产权保护对工程师的双向影响      正向的激励,保护了工程师的创新成果与权益,给企业带来了知识产权方面的收益,企业的创新和发明大都是工程师的劳动成果,他们的职务发明应当受到奖励和保护,是企业发展的重要源泉。专利同时也成了工程师职称评定的指标之一,专利体现了工程师的创新能力,在求职、竞聘技术岗位或参与重大项目时,专利证书能显著增强个人竞争力。专利将工程师的创意转化为受法律保护的“无形资产”,避免技术成果被他人抄袭或无偿使
    广州铁金刚 2025-03-25 11:48 114浏览
  • 今年全国两会期间,“体重管理”和“育儿”整体配套政策引发了持久广泛关注。从“吃”到“养”,都围绕着国人最为关心的话题:健康。大家常说“病从口入”,在吃这件事上,过去大家可能更多是为了填饱肚子,如今,消费者从挑选食材到厨电都贯彻着健康的宗旨,吃得少了更要吃得好了。这也意味着在新消费趋势下,谁能抓住众人的心头好,就能带起众人的购买欲望,才能在新一轮竞争中脱颖而出。作为家电行业的风向标,在2025年中国家电及消费电子博览会(AWE)上,这两个话题也被媒体和公众频繁提及。深耕中国厨房三十余年的苏泊尔再次
    华尔街科技眼 2025-03-22 11:42 75浏览
  • 人形机器人产业节奏预估:2024年原型机元年,2025年小规模量产元年。当宇树科技H1人形机器人以灵动的手部动作在春晚舞台上演创意融合舞蹈《秧Bot》,舞出"中国智造"时,电视机前十几亿观众第一次深刻意识到:那个需要仰望波士顿动力的时代正在落幕。*图源:宇树科技短短数周后,宇树G1机器人又用一段丝滑的街舞在网络收割亿级播放量,钢铁之躯跳出赛博朋克的浪漫。2月11日,宇树科技在其京东官方旗舰店上架了两款人形机器人产品,型号分别为Unitree H1和G1。2月12日,9.9万元的G1人形机器人首批
    艾迈斯欧司朗 2025-03-22 21:05 129浏览
  • 文/Leon编辑/cc孙聪颖‍国内电商市场竞争已呈深度内卷之势,头部电商企业纷纷将目光投向跨境领域,以此增强自身的营收能力。近日,天猫国际便在海外市场再度发力,积极拓展业务版图 。日前,《华尔街科技眼》从韩国电商人士处独家获悉,天猫国际开启“全球代工厂品牌扶持项目”,首站落地韩国,主要面向美妆个护品类招商,计划在2025年4月至2026年3月招标150家代工厂品牌,为代工厂商家海外品牌入驻中国市场提供全链路孵化方案。4月11日,天猫国际将在韩国首尔乐天酒店召开线下招商会,面向当地代工厂及品牌方,
    华尔街科技眼 2025-03-25 17:42 22浏览
  • 核心板简介创龙科技 SOM-TL3562 是一款基于瑞芯微 RK3562J/RK3562 处理器设计的四核 ARM C ortex-A53 + 单核 ARM Cortex-M0 全国产工业核心板,主频高达 2.0GHz。核心板 CPU、R OM、RAM、电源、晶振等所有元器件均采用国产工业级方案,国产化率 100%。核心板通过 LCC 邮票孔 + LGA 封装连接方式引出 MAC、GMAC、PCIe 2.1、USB3.0、 CAN、UART、SPI、MIPI CSI、MIPI
    Tronlong 2025-03-24 09:59 164浏览
  • 文/Leon编辑/cc孙聪颖‍“无AI,不家电”的浪潮,正在席卷整个家电行业。中国家电及消费电子博览会(AWE2025)期间,几乎所有的企业,都展出了搭载最新AI大模型的产品,从电视、洗衣机、冰箱等黑白电,到扫地机器人、双足机器人,AI渗透率之高令人惊喜。此番景象,不仅让人思考:AI对于家电的真正意义是什么,具体体现在哪些方面?作为全球家电巨头,海信给出了颇有大智慧的答案:AI化繁为简,将复杂留给技术、把简单还给生活,是海信对于AI 家电的终极答案。在AWE上,海信发布了一系列世俱杯新品,发力家
    华尔街科技眼 2025-03-23 20:46 72浏览
  • 在人工智能与物联网技术蓬勃发展的今天,语音交互已成为智能设备的重要功能。广州唯创电子推出的WT3000T8语音合成芯片凭借其高性能、低功耗和灵活的控制方式,广泛应用于智能家居、工业设备、公共服务终端等领域。本文将从功能特点、调用方法及实际应用场景入手,深入解析这款芯片的核心技术。一、WT3000T8芯片的核心功能WT3000T8是一款基于UART通信的语音合成芯片,支持中文、英文及多语种混合文本的实时合成。其核心优势包括:高兼容性:支持GB2312/GBK/BIG5/UNICODE编码,适应不同
    广州唯创电子 2025-03-24 08:42 153浏览
  • 无论你是刚步入职场的新人,还是已经有几年经验的职场老手,培养领导力都是职业发展中一个至关重要的环节。拥有良好的领导能力不仅能让你从人群中脱颖而出,也能让你在团队中成为一个值得信赖、富有影响力的核心成员。什么是领导力?领导力并不仅仅意味着“当老板”或者“发号施令”。它更多地是一种能够影响他人、激发团队潜能,并带领大家实现目标的能力。一位优秀的领导者需要具备清晰的沟通能力、解决问题的能力,以及对人心的深刻理解。他们知道如何激励人心,如何在压力下保持冷静,并能在关键时刻做出正确的决策。如何培养领导力?
    优思学院 2025-03-23 12:24 85浏览
  •        当今社会已经步入了知识经济的时代,信息大爆炸,新鲜事物层出不穷,科技发展更是一日千里。知识经济时代以知识为核心生产要素,通过创新驱动和人力资本的高效运转推动社会经济发展。知识产权(IP)应运而生,成为了知识经济时代竞争的核心要素,知识产权(Intellectual Property,IP)是指法律赋予人们对‌智力创造成果和商业标识等无形财产‌所享有的专有权利。其核心目的是通过保护创新和创意,激励技术进步、文化繁荣和公平竞争,同时平衡公共利益与
    广州铁金刚 2025-03-24 10:46 48浏览
  • WT588F02B是广州唯创电子推出的一款高性能语音芯片,广泛应用于智能家电、安防设备、玩具等领域。然而,在实际开发中,用户可能会遇到烧录失败的问题,导致项目进度受阻。本文将从下载连线、文件容量、线路长度三大核心因素出发,深入分析烧录失败的原因并提供系统化的解决方案。一、检查下载器与芯片的物理连接问题表现烧录时提示"连接超时"或"设备未响应",或烧录进度条卡顿后报错。原因解析接口错位:WT588F02B采用SPI/UART双模通信,若下载器引脚定义与芯片引脚未严格对应(如TXD/RXD交叉错误)
    广州唯创电子 2025-03-26 09:05 43浏览
  • 在智慧城市领域中,当一个智慧路灯项目因信号盲区而被迫增设数百个网关时,当一个传感器网络因入网设备数量爆增而导致系统通信失效时,当一个智慧交通系统因基站故障而导致交通瘫痪时,星型网络拓扑与蜂窝网络拓扑在构建广覆盖与高节点数物联网网络时的局限性便愈发凸显,行业内亟需一种更高效、可靠与稳定的组网技术以满足构建智慧城市海量IoT网络节点的需求。星型网络的无线信号覆盖范围高度依赖网关的部署密度,同时单一网关的承载设备数量有限,难以支撑海量IoT网络节点的城市物联系统;而蜂窝网络的无线信号覆盖范围同样高度依
    华普微HOPERF 2025-03-24 17:00 152浏览
  • 在智能终端设备快速普及的当下,语音交互已成为提升用户体验的关键功能。广州唯创电子推出的WT3000T8语音合成芯片,凭借其卓越的语音处理能力、灵活的控制模式及超低功耗设计,成为工业控制、商业终端、公共服务等领域的理想选择。本文将从技术特性、场景适配及成本优势三方面,解析其如何助力行业智能化转型。一、核心技术优势:精准、稳定、易集成1. 高品质语音输出,适配复杂环境音频性能:支持8kbps~320kbps宽范围比特率,兼容MP3/WAV格式,音质清晰自然,无机械感。大容量存储:内置Flash最大支
    广州唯创电子 2025-03-24 09:08 178浏览
  • 在智能终端设备开发中,语音芯片与功放电路的配合直接影响音质表现。广州唯创电子的WTN6、WT588F等系列芯片虽功能强大,但若硬件设计不当,可能导致输出声音模糊、杂音明显。本文将以WTN6与WT588F系列为例,解析音质劣化的常见原因及解决方法,帮助开发者实现清晰纯净的语音输出。一、声音不清晰的典型表现与核心原因当语音芯片输出的音频信号存在以下问题时,需针对性排查:背景杂音:持续的“沙沙”声或高频啸叫,通常由信号干扰或滤波不足导致。语音失真:声音断断续续或含混不清,可能与信号幅度不匹配或功放参数
    广州唯创电子 2025-03-25 09:32 33浏览
  • 在嵌入式语音系统的开发过程中,广州唯创电子推出的WT588系列语音芯片凭借其优异的音质表现和灵活的编程特性,广泛应用于智能终端、工业控制、消费电子等领域。作为该系列芯片的关键状态指示信号,BUSY引脚的设计处理直接影响着系统交互的可靠性和功能拓展性。本文将从电路原理、应用场景、设计策略三个维度,深入解析BUSY引脚的技术特性及其工程实践要点。一、BUSY引脚工作原理与信号特性1.1 电气参数电平标准:输出3.3V TTL电平(与VDD同源)驱动能力:典型值±8mA(可直接驱动LED)响应延迟:语
    广州唯创电子 2025-03-26 09:26 46浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦