Verilog 入门(七)(任务、函数)

任务

一个任务就像一个过程,它可以从描述的不同位置执行共同的代码段。共同的代码段用任务定义编写成任务,这样它就能够从设计描述的不同位置通过任务调用被调用。任务可以包含时序控制,即时延控制,并且任务也能调用其它任务和函数。

任务定义

任务定义的形式如下:

task task_id;
  [declarations]
  procedural_statement
endtask

任务可以没有或有一个或多个参数。值通过参数传入和传出任务。除输入参数外(参数从任务中接收值),任务还能带有输出参数(从任务中返回值)和输入输出参数。任务的定义在模块说明部分中编写。例如:

module Has_Task;
  parameter MAXBITS = 8;

  task Reverse_Bits;
    input[MAXBITS-1:0] Din;
    output[MAXBITS-1:0] Dout;
    integer K;

    begin
      for(K=0; K<MAXBITS; K=K+1)
        Dout[MAXBITS-K] = Din[K]
    end
  endtask
endmodule

任务的输入和输出在任务开始处声明。这些输入和输出的顺序决定了它们在任务调用中的顺序。

任务调用

一个任务由任务调用语句调用。任务调用语句给出传入任务的参数值和接收结果的变量值。任务调用语句是过程性语句,可以在 always 语句或 initial 语句中使用。形式如下:

task_id[(expr1, expr2, ..., exprN)];

任务调用语句中参数列表必须与任务定义中的输入、输出和输入输出参数说明的顺序匹配。此外,参数要按值传递,不能按地址传递。下面是调用任务 Reverse_Bits 的实例:

reg[MAXBITS-1:0] Reg_X, New_Reg;
Reverse_Bits(Reg_X, New_Reg)

注意因为任务能够包含定时控制,任务可在被调用后再经过一定时延才返回值。因为任务调用语句是过程性语句,所以任务调用中的输出和输入输出参数必须是寄存器类型的

通过采用任务 task 完成 4 个 4bits 输入数据的比较排列。

module sort4data(a,b,c,d,ra,rb,rc,rd);
  input[3:0] a,b,c,d;
  output[3:0] ra,rb,rc,rd;
  reg[3:0] ra,rb,rc,rd;
  reg[3:0] va,vb,vc,vd;
  always@(a or b or c or d) begin
    {
    
    va,vb,vc,vd} = {
    
    a,b,c,d};
    sort2(va,vc);
    sort2(vb,vd);
    sort2(va,vb);
    sort2(vc,vd);
    sort2(vb,vc);
    {
    
    ra,rb,rc,rd} = {
    
    va,vb,vc,vd};
  end

  task sort2;
    inout[3:0] x,y;
    reg[3:0] tmp;
    if(x>y) begin tmp=x; x=y; y=tmp; end
  endtask
endmodule

函数

函数,如同任务一样,也可以在模块不同位置执行共同代码。函数与任务的不同之处是函数只能返回一个值,它不能包含任何时延或时序控制(必须立即执行),并且它不能调用其它的任务。此外,函数必须带有至少一个输入,在函数中允许没有输出或输入输出说明。函数可以调用其它的函数。

函数说明部分

函数说明部分可以在模块说明中的任何位置出现,函数的输入是由输入说明指定,形式如下:

function[range] function_id;
  input_declaration
  other_declarations
  procedural_statament
endfunction

函数实例如下:

module Function_Example
  parameter MAXBITS = 8;

  function[MAXBITS-1:0] Reverse_Bits;
    input[MAXBITS-1:0] Din;
    integer K;
    begin
      for(K=0; K<MAXBITS; K=K+1)
        Reverse_Bits[MAXBITS-K] = Din[K];
    end
  endfunction
endmodule

函数定义在函数内部隐式地声明一个寄存器变量,该寄存器变量与函数同名并且取值范围相同。函数通过在函数定义中显式地对该寄存器赋值来返回函数值。对这一寄存器的赋值必须出现在函数定义中。

函数调用

函数调用是表达式的一部分。形式如下:

func_id(expr1, expr2,..., exprN)

以下是函数调用的例子:

reg[MAXBITS-1:0] New_Reg, Reg_X;
New_Reg = Reverse_Bits(Reg_X);

与任务相似,函数定义中声明的所有局部寄存器都是静态的,即函数中的局部寄存器在函数的多个调用之间保持它们的值。

设计一个采用调用函数的阶乘运算电路。

module factorial_fun(clk, n, result, reset);
  input[3:0] n;
  input clk, reset;
  output[31:0] result;
  reg[31:0] result;
  always@(posedge clk or negedge reset)
    if(!reset) result <= 0;
    else result <= factorial(n);
  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
endmodule

值变转储文件

值变转储(VCD)文件包含设计中指定变量取值变化的信息。它的主要目的是为其它后处理工具提供信息。

下面的系统任务用于创建和将信息导入 VCD 文件:

$dumpfile:本系统任务指定转储文件名,例如

$dumpfile("uart.dump");

dumpvars:本系统任务指定哪些变量值变化时转储进转储文件。

dumpoff:本系统任务促使转储任务被挂起。

dumplimit:本系统任务为 VCD 文件指定最大长度(字节)。转储在达到此界限时停止。

dumpflush:本系统任务刷新操作系统 VCD 文件缓冲区中的数据,将数据存到 VCD 文件中。执行此系统任务后,转储任务处于唤醒状态。

下面是在 5~12 之间计数的可逆计数器的例子:

module CountUpDown(Clk, Count, Up_Down);
  input Clk, Up_Down;
  output[0:3] Count;
  reg[0:3] Count;

  initial Count = 'd5;

  always@(posedge Clk) begin
    if(Up_Down)
      begin
        Count = Count + 1;
        if(Count > 12)
          Count = 12
      end
    else
      begin
        Count = Count - 1;
        if(Count < 5)
          Count = 5;
      end
  end
endmodule

module Test;
  reg Clock, UpDn;
  wire[0:3] Cnt_Out;
  parameter ON_DELAY = 1, OFF_DELAY = 2;

  CountUpDown C1(Clock, Cnt_Out, UpDn);

  always begin
    Clock = 1;
    #ON_DELAY
    Clock = 0;
    #OFF_DELAY;
  end

  initial begin
    UpDn = 0;
    #50 UpDn = 1;
    #100 $dumpflush;
    $stop;
  end

  initial begin
    $dumpfile("count.dump");
    $dumplimit(4096);
    $dumpvars(0, Test);
    $dumpvars(0, C1.Count, C1.Clk, C1.Up_Down);
  end
endmodule

猜你喜欢

转载自blog.csdn.net/myDarling_/article/details/134753661
今日推荐