SV之task和function

本文主要介绍了SV(SystemVerilog)中的任务、函数、参数传递、数组返回及循环语句。任务和函数分静态与动态,参数传递有值、引用、名称和位置传递等方式,函数可返回数组,还介绍了forever、repeat等不同类型的循环语句及break和continue关键字。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

task

function

Argument Passing 参数传递

从函数返回一个数组 

Loops 


task

任务可以包含参数声明、输入参数、输出参数、输入参数、寄存器、事件和零或多个行为语句.任务可以是static(静态的)也可以是automatic(动态的) 。静态任务为所有任务调用共享相同的存储空间,动态任务为每个任务调用分配唯一的堆叠存储空间。

SV允许:

  • 在静态任务中声明动态变量,也可以在动态任务中声明静态变量;
  • 声明端口的更多功能;
  • 在任务结束前使用return返回;
  • 通过引用、值、名称和位置传递参数值;
  • 存在默认的参数值;
  • 默认的参数方向是input;
  • 默认的参数类型是logic;

看下面例子:

(1)在方法名的括号内部声明参数,并标明输入输出方向

module sv_task;
  int x;
  
  //task to add two integer numbers. 
  task sum(input int a,b,output int c);
    c = a+b;    
  endtask
  
  initial begin
    sum(10,5,x);
    $display("\tValue of x = %0d",x);
  end  
endmodule

 

(2)在方法体内部声明参数和方向

module sv_task;
  int x;
 
  //task to add two integer numbers.
  task sum;
    input int a,b;
    output int c;
    c = a+b;  
  endtask
 
  initial begin
    sum(10,5,x);
    $display("\tValue of x = %0d",x);
  end
endmodule

function

函数可以包含范围声明、返回类型声明、参数声明、输入参数声明、寄存器声明和事件声明.和任务一样,函数分为静态的和动态的。

  • 没有范围或返回类型声明的函数返回一个1bit的值;
  • 任何表达式都可以用作函数参数;
  • 函数不能包含任何时间控制的语句,也不能调用任务;
  • 函数只能返回一个值;

SV允许:

  • 在静态函数中声明动态变量,也可以在动态函数中声明静态变量;
  • 声明端口的更多功能;
  • 在函数结束前使用return返回;
  • 通过引用、值、名称和位置传递参数值;
  • 存在默认的参数值;
  • 默认的参数方向是input;
  • 默认的参数类型是logic;

看下面例子:

 (1)在函数名的括号内部声明参数

module sv_function;
  int x;
  //function to add two integer numbers.
  function int sum(input int a,b);
    sum = a+b;  
  endfunction
 
  initial begin
    x=sum(10,5);
    $display("\tValue of x = %0d",x);
  end
endmodule

(2)在函数体内部声明参数

module sv_function;
  int x;
 
  //function to add two integer numbers.
  function int sum;
    input int a,b;
    sum = a+b;  
  endfunction
  initial begin
    x=sum(10,5);
    $display("\tValue of x = %0d",x);
  end
endmodule

(3)带return关键字的函数

module sv_function;
  int x;
 
  //function to add two integer numbers.
  function int sum;
    input int a,b;
    return a+b;  
  endfunction
 
  initial begin
    x=sum(10,5);
    $display("\tValue of x = %0d",x);
  end
endmodule

(4) Void 函数,无返回值

module sv_function;
int x;
  
  //void function to display current simulation time 
  function void current_time;
    $display("\tCurrent simulation time is %0d",$time);    
  endfunction
  
  initial begin
    #10;
    current_time();
    #20;
    current_time();
  end
endmodule

(5)丢弃函数的返回值,用关键字void

module sv_function;
  int x;
   //function to add two integer numbers. 
  function int sum;
    input int a,b;
    return a+b;    
  endfunction
  
  initial begin
    $display("Calling function with void");
    void'(sum(10,5));//丢弃返回值
  end
 endmodule

(6)函数作为表达式的一部分

module sv_function;
  int x;
  //function to add two integer numbers. 
  function int sum;
    input int a,b;
  return a+b;    
  endfunction
  initial begin
    x = 10 + sum(10,5);
    $display("\tValue of x = %0d",x);
  end
endmodule

Argument Passing 参数传递

SV提供以下几种方式将参数传递给函数和任务:

  • 值传递;
  • 引用传递;
  • 参数名传递;
  • 位置传递;

(1)值传递

在按值传递参数时,参数传递机制通过将每个参数复制到子例程区域来工作。如果对子例程中的参数有任何更改,这些更改将在子例程之外不可见。看例子:

module argument_passing;
  int x,y,z;
  //function to add two integer numbers.
  function int sum(int x,y);
    x = x+y;
    return x+y;  
  endfunction
 
  initial begin
    x = 20;
    y = 30;
    z = sum(x,y);
    $display("-----------------------------------------------------------------");
    $display("\tValue of x = %0d",x);
    $display("\tValue of y = %0d",y);
    $display("\tValue of z = %0d",z);
    $display("-----------------------------------------------------------------");
  end
endmodule

变量x和y在函数sum中作为参数传递,sum函数中的参数x的更改在函数外部不可见。 结果如下:

(2)引用传递

在“引用传递中,原始参数的引用被传递给子例程。由于子例程的参数是指向原始参数的,在子例程内部对参数的任何改变在函数外部都可见。要表明通过引用传递的参数,参数声明的前面加关键字ref。引用传递中参数值的任何修改都可以通过在ref之前使用const关键字来避免,此时任何在子例程中更改参数值的操作都会导致编译错误。看下面例子:

module argument_passing;
  int x,y,z;
 
  //function to add two integer numbers.
  function int sum(ref int x,y);//参数默认为input,且采用ref关键字后不能再加input
    x = x+y;
    return x+y;  
  endfunction
 
  initial begin
    x = 20;
    y = 30;
    z = sum(x,y);
    $display("-----------------------------------------------------------------");
    $display("\tValue of x = %0d",x);
    $display("\tValue of y = %0d",y);
    $display("\tValue of z = %0d",z);
    $display("-----------------------------------------------------------------");
  end
endmodule

函数内部对x的改变,在函数外部都是可见的,所以允许结果如下:

(3)引用传递带关键字const

引用传递中参数值的任何修改都可以通过在ref之前使用const关键字来禁止,任何在子例程中更改参数值的尝试都会导致编译错误。看下面例子:

module argument_passing;
  int x,y,z;
  //function to add two integer numbers.
  function int sum(const ref int x,y);
    x = x+y;
    return x+y;  
  endfunction
 
  initial begin
    x = 20;
    y = 30;
    z = sum(x,y);
    $display("-----------------------------------------------------------------");
    $display("\tValue of x = %0d",x);
    $display("\tValue of y = %0d",y);
    $display("\tValue of z = %0d",z);
    $display("-----------------------------------------------------------------");
  end
endmodule

运行结果报错:

(4)默认参数值

可以为子例程的参数指定默认值。在子例程调用中,可以在参数列表中省略具有默认值的参数。如果将任何值传递给具有默认值的参数,则将考虑新值。例子如下:

module argument_passing;
  int q;
 
  //function to add three integer numbers.
  function int sum(int x=5,y=10,z=20);
    return x+y+z;  
  endfunction
 
  initial begin
    q = sum( , ,10);
    $display("-----------------------------------------------------------------");
    $display("\tValue of z = %0d",q);
    $display("-----------------------------------------------------------------");
  end
endmodule

上例中x、y、z在函数定义时都有默认值,但是也可以在进行函数调用时修改。对于使用默认值的参数,直接用空格表示;对于要修改默认值的参数,可以在对应的参数位置加入修改值。运行结果如下:

(5)通过名称传递参数

通过.参数名(函数变量)的方式传递参数,这与verilog中调用子模块类似,参数的顺序可以随意。看下面例子:

module argument_passing;
  int x,y,z;
 
  function void display(int x,string y);
    $display("\tValue of x = %0d, y = %0s",x,y);  
  endfunction
 
  initial begin
    display(.y("Hello World"),.x(2016));
  end
endmodule

运行结果:

注意,对于简单类型的变量,如:int , bit等通常没必要使用引用传递,只有当传递数组等复杂参数时才需要用到ref ,且当函数的参数同时有 简单参数和复杂参数时 ,要注意二者的顺序。看下面的例子:

task stick (ref int array [50], int a, b);
....
endtask
//a和b的方向是什么?是否采用引用传递?
//实际上这样写的话,a和b默认采用ref传递,且参数方向默认为输入
//为了避免a,b采用引用传递,可以将上面修改为:

task stick (ref int array [50],input int a, b);
....
endtask

 

从函数返回一个数组 

verilog 的子程序只能返回一个简单值,如比特 ,整数或者是向量;但是在SV 中,函数可以通过多种方式返回一个数组。第一种就是定义一个数组类型,然后在函数的声明中使用该类型。

typedef int fixed_array [5];

fixed_array f5;

function fixed_array init(int start);
    foreach (init[i])
        init[i] = i + start;
endfunction

initial begin
            f5 = init(5);
            foreach(f5[i])
                $display("f5[%0d] = %0d", i, f5[i]);
        end

 上述代码存在的一个问题是,函数init创建了一个数组,该数组值被拷贝到f5中,如果数组很大,可能会引起一个性能上的问题。

另一种方法就是通过引用传递关键字ref来进行数组参数的传递。

function void init(ref int f [5], input int start);
    foreach (f[i])
        f[i] = i + start;
endfunction

int fa [5];

initial begin
            init(fa, 5);
            foreach (fa[i])
                $display("fa[%0d] = %0d", i, fa[i]);
        end

Loops 

SV提供不同类型的循环语句:

foreverRuns the given set of statements forever
repeatRepeats the given set of statements for a given number of times
whileRepeats the given set of statments as long as given condition is true
forSimilar to while loop, but more condense and popular form
do whileRepeats the given set of statements atleast once, and then loops as long as condition is true
foreachUsed mainly to iterate through all elements in an array

forever

类似于while(1),如果不添加终止循环的语句,它会一直执行。

 

module tb;

  // This initial block has a forever loop which will "run forever"
  // Hence this block will never finish in simulation
  initial begin
    forever begin
      #5 $display ("Hello World !");
    end
  end

  // Because the other initial block will run forever, our simulation will hang!
  // To avoid that, we will explicity terminate simulation after 50ns using $finish
  initial
    #50 $finish;//终止循环
endmodule

仿真结果:

Simulation Log

ncsim> run
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
Simulation complete via $finish(1) at time 50 NS + 0

 repeat

module tb;

  	// This initial block will execute a repeat statement that will run 5 times and exit
	initial begin

        // Repeat everything within begin end 5 times and exit "repeat" block
		repeat(5) begin
			$display ("Hello World !");
		end
	end
endmodule

仿真结果:

ncsim> run
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
ncsim: *W,RNQUIE: Simulation is complete.

while

module tb;
 bit clk;

  always #10 clk = ~clk;
  initial begin
  	bit [3:0] counter;

    $display ("Counter = %0d", counter);      // Counter = 0
  	while (counter < 10) begin
    	@(posedge clk);//在while语句块中不需要加always关键字,在其他块语句中也不需要加,语句末需要加分号,这是与always语句的不同
    	counter++;
        $display ("Counter = %0d", counter);      // Counter increments
  	end
  	$display ("Counter = %0d", counter);      // Counter = 10
    $finish;//需要加上终止循环的语句,否则clk会一直运行
end
endmodule

仿真结果:

ncsim> run
Counter = 0
Counter = 1
Counter = 2
Counter = 3
Counter = 4
Counter = 5
Counter = 6
Counter = 7
Counter = 8
Counter = 9
Counter = 10
Counter = 10
Simulation complete via $finish(1) at time 190 NS + 0

 for

module tb;
 bit clk;

  always #10 clk = ~clk;
  initial begin
  	bit [3:0] counter;

    $display ("Counter = %0d", counter);      // Counter = 0
  	for (counter = 2; counter < 14; counter = counter + 2) begin
    	@(posedge clk);
    	$display ("Counter = %0d", counter);      // Counter increments
  	end
    $display ("Counter = %0d", counter);      // Counter = 14
    $finish;
  end
endmodule

仿真结果:

ncsim> run
Counter = 0
Counter = 2
Counter = 4
Counter = 6
Counter = 8
Counter = 10
Counter = 12
Counter = 14
Simulation complete via $finish(1) at time 110 NS + 0

do while

 与while的不同之处在于do while是先执行后检查,所以执行次数总是比while多一次。

module tb;
 bit clk;

  always #10 clk = ~clk;
  initial begin
  	bit [3:0] counter;

    $display ("Counter = %0d", counter);      // Counter = 0
		do begin
			@ (posedge clk);
			counter ++;
          $display ("Counter = %0d", counter);      // Counter increments
        end while (counter < 5);
    $display ("Counter = %0d", counter);      // Counter = 14
    $finish;
  end
endmodule

仿真结果:

ncsim> run
Counter = 0
Counter = 1
Counter = 2
Counter = 3
Counter = 4
Counter = 5
Counter = 5
Simulation complete via $finish(1) at time 90 NS + 0

 foreach

foreach是for语句的精简版,不需要定义自增变量,以便用于遍历数组。

module tb_top;
   bit [7:0] array [8];   // Create a fixed size array

   initial begin

      // Assign a value to each location in the array
      foreach (array [index]) begin
         array[index] = index;
      end

      // Iterate through each location and print the value of current location
      foreach (array [index]) begin
         $display ("array[%0d] = 0x%0d", index, array[index]);
      end
   end
endmodule

仿真结果:

ncsim> run
array[0] = 0x0
array[1] = 0x1
array[2] = 0x2
array[3] = 0x3
array[4] = 0x4
array[5] = 0x5
array[6] = 0x6
array[7] = 0x7
ncsim: *W,RNQUIE: Simulation is complete.

break和continue

 两个关键字的作用和C语言相同,前者用于终止循环,后者用于终止本次循环进入下一次循环。

module tb;
  	initial begin

      // This for loop increments i from 0 to 9 and exit
      for (int i = 0 ; i < 10; i++) begin
        $display ("Iteration [%0d]", i);

        // Let's create a condition such that the
        // for loop exits when i becomes 7
        if (i == 7)
          break;
      end
    end
endmodule

仿真结果:

ncsim> run
Iteration [0]
Iteration [1]
Iteration [2]
Iteration [3]
Iteration [4]
Iteration [5]
Iteration [6]
Iteration [7]
ncsim: *W,RNQUIE: Simulation is complete.
module tb;
  	initial begin

      // This for loop increments i from 0 to 9 and exit
      for (int i = 0 ; i < 10; i++) begin

        // Let's create a condition such that the
        // for loop
        if (i == 7)
          continue;

        $display ("Iteration [%0d]", i);
      end
    end
endmodule
ncsim> run
Iteration [0]
Iteration [1]
Iteration [2]
Iteration [3]
Iteration [4]
Iteration [5]
Iteration [6]
Iteration [8]
Iteration [9]
ncsim: *W,RNQUIE: Simulation is complete.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值