第6章

---恢复内容开始---

initial 与 always

  • 一个程序模块可以有多个initial和always;每个initial和always说明语句在仿真的一开始同时立即开始执行。其中,initial语句只执行一次,而always语句则是不断的重复活动,直到仿真过程结束。
  • 一个模块中的多个initial块是并行执行的。
  • always语句声明格式: always <时序控制> <语句>
  • 如果一个always语句没有时序控制1,则这个always语句将会使仿真器发生死锁。  eg: always areg=~areg;会生成一个0延时的无限循环跳变过程。这时就会发生死锁。
  • always的时间控制可以是电平触发也可以是边沿触发。
  • 边沿触发常用来描述时序行为,如有限状态机。通过综合工具自动将其转换为表示寄存器组和门级组合逻辑的结构。
  • 电平触发常用来描述组合逻辑的行为,通过综合工具自动将其转换为表示组合逻辑的门级逻辑结构或带锁存器的组合逻辑结构。
  • 当always中有多个触发事件时,可用 "or" 关键字连接。在Verilog 1364-2001版本中,也可以用逗号  “,”  进行代替。

当always的时序控制表达式中的输入变量很多时,可以用 @ *  和 @(*) 来表示对其后面的语句块中所有输入变量的变化是敏感的。下面是书上的一个例子:可以用

1 always @(*)
2 begin
3     out1=a?(b+c):(d+e);
4     out2=f?(g+h):(p+m);
5 end

来代替:

always @function(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

 

  • always 除了可以使用符号@和后面的敏感电平列表来表示当敏感电平列表中的值发生变化时会执行对接下来的语句以外;Verilog还允许使用另外一种形式表示的电平敏感时序控制(即后面的语句和语句块需要等待某个条件为真时才能执行)。Verilog语言关键字wait来表示等待电平敏感的条件为真。eg: 

1 always

2 wait(count_enable) #20 count=count+1; 

  像上面的例子就不像之前的情况,需要等count_enable的电平值发生改变时才执行下一步。而是会直接检测count_enable的值。

task和function

  •  输入、输出和总线信号的值可以传入、传出任务和函数。
  • 任务和函数的不同点主要有以下4点:
  1. 函数的仿真时间单位必须要与主模块一致;任务可以有自己的仿真时间单位;
  2. 函数不能启动任务;任务可以启动其他函数和任务;
  3. 函数至少要有一个输入变量,而任务可以没有或者有多个类型的变量;
  4. 函数会有一个返回值,而任务则没有返回值。
  • 函数的目的是通过返回一个值来响应输入信号的值。任务却能支持多种目的,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口输出。

task说明语句

  • 任务的定义:

    task <任务名>;

     <端口及数据类型声明语句>

     语句

    endcase

  • 任务调用: <任务名>(端口1,端口2,...,端口n);
  • 下面是一个例子:
    // task定义
    
    task my_task;
    input a,b;
    input c;
    output d,e;
    
    ...
    
    <语句>
    
    ...
    
    c=foo1;
    d=foo2;
    e=foo3;
    
    endtask
    
    // task调用
    
    my_task(v,w,x,y,z);

    几点注意事项:

  1. 任务的调用不想module那样,不需要在调用的时候再另外生成一个任务名。直接用被调用的任务名加参数就行了;
  2. v -> a,w -> b,x -> c , y -> d , z -> e;是按照input,output定义是时的顺序一一对应。

function说明语句

  • 定义函数:function <返回值的类型或范围> (函数名);

        <端口说明语句>

       <变量类型说明语句>

        begin

        <语句>

        ......

        end

       endfunction

  在头部定义中,“返回值的类型或范围”是可选项。默认为一位寄存器类型数据。

  函数至少要有一个输入。

  eg:

function [7:0] getbyte;
input [15:0] address;
begin
    <说明语句>
    getbyte=result_expression;
end
endfunction
  • 从函数返回的值:函数的声明蕴含声明了与函数同名的、函数内部的寄存器。如上例中,在写下function [7:0] getbyte  语句的同时,也就在函数中声明了一个8位的名为getbyte的寄存器。并且函数最终会将该寄存器中的值当做函数的返回值。
  • 函数的调用:调用格式:<函数名> (<表达式>,...<表达式>)     例如:一个对上面的函数的调用例子: word=control?{getbyte(msbyte),getbyte(lsbyte)}:0;
  • 函数的使用规则:
  1. 函数的定义不能包含有任何的时间控制语句,即任何用 #、@或wait来标识的语句。
  2. 函数不能启动任务;
  3. 定义函数时至少要有一个输入参量;
  4. 在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具有和函数名相同的名字。

书上6.2.4及以后的几个例子的一些注意点

  • 当用  `define  LEFT_SHIFT  1'b0      对模块进行宏定义后,当要使用该宏定义时,要使用   `LEFT_SHIFT 而不是   LEFT_SHIFT 。注意要带上  “ ` ”。
  • Verilog中的函数是不能够进行递归调用的。不过若在函数声明时使用了关键字 automatic ,那么该函数将成为自动的或可递归的。此时仿真器为每一个函数调用动态地分配新的地址空间。于是,自动函数中声明的局部变量不能通过层次名进行访问,而自动函数本身可以通过层次名进行访问。下面是书上的例子:
 1 module top;
 2 
 3 function automatic integer factorial;
 4 input [31:0] oper;
 5 integer i;
 6 begin 
 7     if(oper>=2)
 8     factorial=factorial(oper-1)*oper;
 9     else
10     factorial=1;
11 end
12 endfunction
13 
14 integer result;
15 initial
16 begin    
17     result=factorial(4);
18     $display("Factorial of 4 is %0d",result);
19 end
20 endmodule

几点注意点:

  1.   integer与reg类型的不同:integer也是一种寄存器数据类型,integer类型的变量为有符号数而reg类型的变量则为无符号数除非特别声明为有符号数,还有就是integer的位宽为宿主机的字的位数,但最小为32位用integer的变量都可以用reg定义,只是对于用于计数更方便而已。reg,integer,real,time都是寄存器数据类型,定义在Verilog中用来保存数值的变量,和实际的硬件电路中的寄存器有区别。  所以在函数定义时声明integer就已经声明返回的寄存器是32位寄存器。

posted on 2018-03-07 13:18  我是人间惆怅客1  阅读(195)  评论(0)    收藏  举报

导航