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

TaskFunction
May execute on non-zero simulation timeExecutes on zero simulation time
May have delay, event or timing control constructsNot possible as it executes on zero simulation time
Cannot return a valueAlways return a single value
Pass values (can be multiple) through output or inout argumentsCannot have output or inout arguments
Can enable other functions and tasksCan enable other functions and not task
Can have input, output or inoutMust have atleast one input. Cannot have output or inout