Verilog: Task & Function

Task and Function are used to break up large procedures into smaller ones which helps to make life easier for developing and maintaining Verilog code. In this way, common procedures need to be written only once and can execute from different places. Both task and function are called from always or initial block and contain only behavioural statements.

Definition of task and function must be in a module. The highlighting difference between task and function is that, only task can handle event, delay or timing control statements and function executes in zero simulation time. There are some more differences, which we will be discussed in this sesssion.

Task

Task is declared using task & endtask keyword. Task must be used if delay, event or timing control constructs are required in the procedure. It may have zero or more than input,output or inout argument. As it executes in non-zero simulation time, it can enable other task and functions. It does not return value like function instead it can pass multiple value through output or inout arguments.

module MUX4X1_Using_TASK (Q, IN, SEL);
  input [3:0] IN;
  input [1:0] SEL;
  output Q;
  reg Q;

always @(IN or SEL)
   mux(IN, SEL,Q);
   
task mux;
 input [3:0] in;
 input [1:0] sel;
 output out;
 
 case (sel)
   2'b00: out = in[0];
   2'b01: out = in[1];
   2'b10: out = in[2];
   2'b11: out = in[3];
 endcase
endtask
endmodule

//For testing
module test;
reg [3:0] muxin;
reg [1:0] msel;
wire mout;

MUX4X1_Using_TASK mux_task(mout,muxin,msel);

initial
$monitor($time," -->muxin = %b, msel = %b, mout = %b",muxin,msel,mout);

initial begin
muxin = 4'b0111; 
msel = 2'b01;

#10;
muxin = 4'b0111;
msel = 2'b10;

#10;
muxin = 4'b0110;
msel = 2'b00;

#100 $finish;
end

endmodule
/*
Simulation result:
ncsim> run
             0 –>muxin = 0111, msel = 01, mout = 1
             10 –>muxin = 0111, msel = 10, mout = 1
             20 –>muxin = 0110, msel = 00, mout = 0
 Simulation complete via $finish(1) at time 120 NS + 0
./mux.v:74 #100 $finish;
*/

Automatic task

If a task is called concurrently from different places in the code these task calls will operate on the same task variables. This may leads to incorrect result. To avoid this problem a keyword automatic is added to make the task reentrant.

Function

Functions are declared using function & endfuncion keywords. Function can be used if there is no requirement for specifying delays, timing control constructs or events. There is at least one input argument is needed to use function. Function will return a single value and it usually uses for calculations. Also note that it cannot have output or inout arguments. As it cannot handle timing control statement, delays etc and executes in 0 simulation time, it can only enable another function and not task.

Function Example

module MUX4X1 (Q, IN, SEL);
  input [3:0] IN;
  input [1:0] SEL;
  output Q;
  reg tmpout;

always @(IN or SEL)
   tmpout <= mux(IN, SEL);
   
assign Q = tmpout;
function mux;
 input [3:0] in;
 input [1:0] sel;
 case (sel)
   2'b00: mux = in[0];
   2'b01: mux = in[1];
   2'b10: mux = in[2];
   2'b11: mux = in[3];
 endcase
endfunction
endmodule

//For testing
module test;
reg [3:0] muxin;
reg [1:0] msel;
wire mout;

MUX4X1_Using_FUNC mux_func(mout,muxin,msel);

initial
$monitor($time," -->muxin = %b, msel = %b, mout = %b",muxin,msel,mout);

initial begin
muxin = 4'b0111; 
msel = 2'b01;

#10;
muxin = 4'b0111;
msel = 2'b10;

#10;
muxin = 4'b0110;
msel = 2'b00;

#100 $finish;
end
endmodule
/*Simualation result:
ncsim> run
                   0 -->muxin = 0111, msel = 01, mout = 1
                  10 -->muxin = 0111, msel = 10, mout = 1
                  20 -->muxin = 0110, msel = 00, mout = 0
Simulation complete via $finish(1) at time 120 NS + 0
./MUX_Func.v:49 #100 $finish;
*/

Below example shows how to calculate parity using function. A simple code, but always used in the designs.

module parity_calc(Addr, Par);
  input [31:0] Addr;
  output Par;
  reg Par;

always @(Addr)
  Par = Parity_Calc_Func(Addr);
   

function Parity_Calc_Func;
 input [31:0] addr;
 Parity_Calc_Func = ^addr;
endfunction

endmodule


//For testing
module test;
reg [31:0] Addr;
wire Par;

parity_calc pcalc(Addr,Par);

initial
$monitor($time," -->Addr = %b, Par = %b, ",Addr,Par);

initial begin
Addr = 32'b1111_0000_0011_0111; 

#10;
Addr = 32'b1111_0000_0011_0101; 

#10;
Addr = 32'b1111_0001_1111_0101; 

#100 $finish;
end

endmodule
/*
//Simulation result
ncsim> run
                   0 -->Addr = 00000000000000001111000000110111, Par = 1, 
                  10 -->Addr = 00000000000000001111000000110101, Par = 0, 
                  20 -->Addr = 00000000000000001111000111110101, Par = 1, 
Simulation complete via $finish(1) at time 120 NS + 0
./Parity_Calculation.v:36 #100 $finish;
*/

Automatic (Recursive) Functions

If normal functions are called recursively, the results are non-deterministic because both calls operate on the same variable space. But by using automatic keyword, all function declarations are allocated dynamically for each call and operate on independent variable space. Following is a well known example for automatic function.

module Factorial_calc(Num, Factorial);
  input [31:0] Num;
  output integer Factorial;

//Automatic (recursive function)
function automatic integer Factorial_calc_Func;
 input [31:0] n;
 begin
 if(n >=2) 
   Factorial_calc_Func = Factorial_calc_Func(n-1) * n;
 else
   Factorial_calc_Func = 1;
  end 
endfunction

always @(Num) begin
Factorial = Factorial_calc_Func(Num);
end

endmodule

//For testing
module test;
reg [31:0] num;
wire [31:0] fact;

Factorial_calc fcalc(num,fact);

initial
$monitor($time," --> Factorial of Num %d = %d, ",num,fact);

initial begin
num = 32'd6; 

#10;
num = 32'd2; 

#10;
num = 32'd5; 
#100 $finish;
end
endmodule
/* Simulation Result
ncsim> run
                   0 --> Factorial of Num          6 =        720, 
                  10 --> Factorial of Num          2 =          2, 
                  20 --> Factorial of Num          5 =        120, 
Simulation complete via $finish(1) at time 120 NS + 0
./Factorial.v:45 #100 $finish;
*/

Constant Function

Constant function can be used to instead of constants. In the below example it calculates AWIDTH by using constant function.

module RAM (ADDR, WR, CS, DATA);
parameter DWIDTH = 16;
parameter DEPTH = 512;
localparam AWIDTH = Clogb2(DEPTH);

input WR, CS;
input [AWIDTH - 1:0] ADDR;
inout [DWIDTH - 1:0] DATA;

function integer Clogb2(input integer depth);
begin
     for (Clogb2 = 0; depth > 0; Clogb2 = Clogb2 + 1)
        depth = depth >> 1;
end
endfunction

initial
$monitor($time," --> Parameter Values DWIDTH=%d; DEPTH=%d; AWIDTH=%d",DWIDTH,DEPTH,AWIDTH);
//Write required functionality. Above code just calculate AWIDTH by using const-function.
//..
endmodule

/*Simulation Result
ncsim> run
                   0 --> Parameter Values DWIDTH=         16; DEPTH=        512; AWIDTH=         10
ncsim: *W,RNQUIE: Simulation is complete.
*/

Signed Function

If signed operation needs to performed on return value, then that function must be defined using signed keyword.

function signed [63:0] signed_calc(input [63:0] num);
..
endfunction

Task & Function Differences at a glance

Task Function
May execute on non-zero simulation time Executes on zero simulation time
May have delay, event or timing control constructs Not possible as it executes on zero simulation time
Cannot return a value Always return a single value
Pass values (can be multiple) through output or inout arguments Cannot have output or inout arguments
Can enable other functions and tasks Can enable other functions and not task
Can have input, output or inout Must have atleast one input. Cannot have output or inout

18 comments on “Verilog: Task & Function

  1. Bnote

    I’m not sure but I think the result is following for the task example.
    0 –>muxin = 0111, msel = 01, mout = 1
    10 –>muxin = 0111, msel = 10, mout = 1
    20 –>muxin = 0110, msel = 00, mout = 0
    Instead the result is written in your task box
    Simulation result:
    ncsim> run
    0 –>muxin = 0111, msel = 01, mout = 1
    10 –>muxin = 0111, msel = 10, mout = 1
    20 –>muxin = 0111, msel = 00, mout = 1
    Simulation complete via $finish(1) at time 120 NS + 0
    ./mux.v:74 #100 $finish;

    Would you tell me the right answer?

    Reply
    1. Sini Balakrishnan Post author

      Yes. This is the right answer. Thanks for pointing out.
      ncsim> run
      0 –>muxin = 0111, msel = 01, mout = 1
      10 –>muxin = 0111, msel = 10, mout = 1
      20 –>muxin = 0110, msel = 00, mout = 0
      Simulation complete via $finish(1) at time 120 NS + 0

      Reply
      1. Ameera

        hi,
        please can you put an example of task calling a task?
        and I have a question if you please, let us say that task#1 calls task#2, does task#2 executes in the same clock cycle of task#1 or it will be the next cycle ??
        actually I want to implement two tasks say X and Y, task X should start in the first clock cycle then task Y will be repeated n times, each time in a different clock cycle … how this could be implemented ?
        Thanks in advance. 🙂

        Reply
        1. Sini Balakrishnan Post author

          Please find below example for task calling another task.

          This depends on how you invoke the tasks in your code. You can model it as per your requirement. You can model to call it at the same time or in the different clock cycles or after some specific time period etc.

          Here your requirement is to invoke the task1 in the first clock cycle and task2 in the upcoming cycles. This can be implemented in the following method.

          //////////////////////////////////
          module task_check(clk);

          input wire clk;
          integer n;

          initial begin
          task1;
          end

          task task1;
          begin
          $display($time,” —-> Am in task 1\n”);
          for (n=0; n < 15; n=n+1) begin @(posedge clk) task2; end end endtask task task2; $display($time," =====> Am in TASK2 \n”);
          endtask
          endmodule

          ////////////////////////////////////
          module test(clk);
          output reg clk = 1’b0;
          task_check tt(clk);
          always #5 clk = ~clk;

          initial begin
          #200 $finish;
          end
          endmodule
          ////////////////////////////////////
          ncsim> run
          0 —-> Am in task 1

          5 =====> Am in TASK2

          15 =====> Am in TASK2

          25 =====> Am in TASK2

          35 =====> Am in TASK2

          45 =====> Am in TASK2

          55 =====> Am in TASK2

          65 =====> Am in TASK2

          75 =====> Am in TASK2

          85 =====> Am in TASK2

          95 =====> Am in TASK2

          105 =====> Am in TASK2

          115 =====> Am in TASK2

          125 =====> Am in TASK2

          135 =====> Am in TASK2

          145 =====> Am in TASK2

          /////////////////////////////////////////////

          Here task1 will get called in the first posdge of clk (simulation time 5) and task2 will get called 15 times in the simulation time 15,25,35 etc.

          Hope this helps.

          Reply
          1. Ameera

            Thanks very much Sini Balakrishnan, it was very helpful.
            but I don’t want task X to run at CLK #1 and stops, actually what I want is something like a thread;
            Suppose each thread starts with task X in its first CLK then task y in the upcoming cycles.

            I want to run multiple threads with a one CLK delay between one thread and the next.

            Thanks for helping, I appreciate it.

  2. Dana

    I try to run the automatic recursive function and it gives me an error saying “recursive function call in function declaration is not supported”

    how can this be fixed?

    Reply
  3. Garima

    Hello,
    I am writing a memory in verilog using read and write task.
    I am getting this error : Hierarchical name component lookup failed at ‘WRITE’.
    I am not understanding why it is coming.Can you please tell me..


    module memory(data_out,data_in,address,we,clk);
    input clk,we;
    input [1:0]address;
    input [3:0]data_in;
    output [3:0]data_out;
    reg [3:0]data_out;
    reg [1:0]mem[3:0];
    always @(posedge clk or we)
    begin
    $display("I am in main module");
    if(we)
    mem[address]<= data_in ;
    else
    data_out <= mem[address];
    end

    task WRITE;
    input [3:0]data_in;
    input [1:0]address;
    output [3:0]data_out;
    reg [3:0]data_out;
    input clk,we;
    begin
    $display("I AM in WRITE TASK");
    @(posedge clk);
    we=1'b1;
    end
    endtask

    task automatic READ;
    output [3:0]data_out;
    reg [3:0]data_out;
    reg [1:0]mem[3:0];
    input clk,we;
    input [1:0]address;
    begin
    $display("I AM IN READ TASK");
    @(posedge clk);
    we=0;
    end
    endtask
    endmodule

    module test;
    reg clk,we;
    reg [1:0]address;
    reg [3:0]data_in;
    wire [3:0]data_out;

    memory mem1(data_in,data_out,clk,we,address);

    //writing into memory
    initial
    begin
    clk=1'b0;
    we=1'b0;
    address = 2'b0;
    data_in = 4'b0;
    end
    always #10 clk =~clk;

    initial
    begin
    $dumpfile ("mem.dump");
    $dumpvars(0,test);
    #200 $finish;
    end

    initial
    begin
    WRITE(0,1);
    WRITE(1,2);
    READ(0,1);
    end
    initial
    //Reading data from location "2"
    $monitor("Time",$time,"\t" ,"data_in=%b data_out=%b address=%b ",data_in,data_out,address);
    endmodule

    Reply
  4. chan

    Hi Sini,
    Your work is quite impressive. If possible pls clear my doubt. My o/p value always coming out as “X”. could you pls identify the source of error?
    A few points add to validate my code. A single task called by two diff calls with different values passed by.
    module vga_model(hsync,vsync);

    output hsync,vsync;
    reg vsync,hsync;

    parameter hfp=5,hbp=8,hpw=6,hdt=6;
    parameter vfp=10,vbp=7,vpw=4,vdt=5;
    reg clk;
    always #5 clk=!clk;

    initial
    begin
    clk = 1’b0;

    $monitor($time,”MODEL:: hsync=%b vsync=%b”,hsync,vsync);
    fork
    vga_sig_gen(vdt,vfp,vbp,vpw,vsync);
    vga_sig_gen(hdt,hfp,hbp,hpw,hsync);
    join

    end

    task automatic vga_sig_gen;
    input [31:0] dt;
    input [31:0] fp;
    input [31:0] bp;
    input [31:0] pw;
    output sync;
    reg sync;

    begin
    $display($time,”%h %h %h %h %b”,dt,fp,bp,pw,out);
    // display time cycles
    repeat(dt)
    begin @(posedge clk);sync=1’b1;$display($time,”Am in display time cycles =%b”,sync); end
    repeat(fp)
    begin @(posedge clk);sync=1’b1;$display($time,”Am in Front porch cycles =%b”,sync); end
    repeat(pw)
    begin @(posedge clk);sync=1’b0;$display($time,”Am in back porch cycles =%b”,sync); end
    repeat(bp)
    begin @(posedge clk);sync = 1’b1;$display($time,”Am in pulse width cycles =%b”,sync); end
    end//task begin
    endtask
    endmodule

    module tb_vga;
    wire hsyn,vsyn;
    initial
    begin
    $monitor(“TB : hsync=%b vsync=%b”,hsyn,vsyn);
    end
    vga_model ref_inst(.hsync(hsyn),.vsync(vsyn));
    endmodule

    Reply
    1. Sini Balakrishnan Post author

      Hi,
      I am not sure about the logic which you are trying to implement. But with slight changes in the code, I could see TB gives values for hsync and vsync.
      ////////////////////////////////////////////////////
      module vga_model(hsync,vsync);

      output hsync,vsync;
      reg vsync,hsync;

      parameter hfp=5,hbp=8,hpw=6,hdt=6;
      parameter vfp=10,vbp=7,vpw=4,vdt=5;
      reg clk;
      always #5 clk=!clk;

      initial
      begin
      clk = 1’b0;
      $monitor($time,”MODEL:: hsync=%b vsync=%b”,hsync,vsync);
      fork
      vga_sig_gen(vdt,vfp,vbp,vpw,vsync);
      vga_sig_gen(hdt,hfp,hbp,hpw,hsync);
      join
      end

      task automatic vga_sig_gen;
      input [31:0] dt;
      input [31:0] fp;
      input [31:0] bp;
      input [31:0] pw;
      output sync;
      reg sync;

      begin
      $display(“called task —————- “);
      $display($time,”%h %h %h %h”,dt,fp,bp,pw);
      // display time cycles
      repeat(dt)
      begin @(posedge clk);sync=1’b1;$display($time,” Am in display time cycles =%b”,sync); end
      repeat(fp)
      begin @(posedge clk);sync=1’b1;$display($time,” Am in Front porch cycles =%b”,sync); end
      repeat(pw)
      begin @(posedge clk);sync=1’b0;$display($time,” Am in back porch cycles =%b”,sync); end
      repeat(bp)
      begin @(posedge clk);sync = 1’b1;$display($time,” Am in pulse width cycles =%b”,sync); end
      end//task begin
      endtask
      endmodule

      ////////////////////////////////////////////////////

      module tb_vga;
      wire hsyn,vsyn;

      vga_model ref_inst(.hsync(hsyn),.vsync(vsyn));

      initial
      begin
      $monitor(“TB : hsync=%b vsync=%b”,hsyn,vsyn);
      #500 $finish;
      end

      endmodule
      ////////////////////////////////////////////////////
      245 Am in pulse width cycles =1
      TB : hsync=1 vsync=x
      255 Am in pulse width cycles =1
      TB : hsync=1 vsync=1

      Reply
  5. Avi

    Will you please provide RTL(in SV/Verilog) for factorial calculation using recursive module instead of recursive function?

    Reply
  6. anonymous

    give an example of task calling function?
    what happens if fuction calls a task?
    example of function calling a function?

    Reply
  7. 9121

    I want to know more about signed functions like the impact on lifetime of function and anything else if available.

    Reply
  8. Nitin

    Constant funtion example is incorrect, you don’t need 10bit address for a depth of 512. Even for a depth of 2 you don’t need 2 bits. Subtract 1 from depth before entering into the loop.

    Reply

Leave a Reply to Ameera Cancel reply

Your email address will not be published. Required fields are marked *