/// Notes and Demos for LSU EE 4702-1 Spring 2001
// Functions and Tasks
// Sources: Ci: Ciletti, Modeling, synthesis, and rapid prototyping
// with the Verilog HDL
// LRM: IEEE, Verilog Language Reference Manual
/// Contents
// Functions
// Tasks
/// Functions
// LRM 10.3
// Procedural code that returns a value.
// Can be used in non-procedural code.
// Restrictions on Functions
// Has exactly one output variable.
// Within a module.
// No timing controls allowed.
// Cannot use nonblocking assignments.
// Cannot enable tasks.
// Must have at least one input variable.
// Cannot declare nets.
/// Function Declaration
// function RANGEORTYPE NAME;
// ITEM_DECLARATIONS
// STATEMENT
// endfunction
//
// RANGEORTYPE: A range (e.g., [5:0]) or a type (e.g., integer, real, etc.)
// ITEM_DECLARATIONS: input, variable, and parameter declarations.
// (Cannot declare outputs, NAME is the function
// name and the name of the single output.)
//
// Declare a function named NAME that returns a value of
// type RANGEORTYPE. Inputs and variables for use by the function
// are specified by ITEM_DECLARATIONS. The function returns
// the value last assigned to NAME by STATEMENT.
// Example of appropriate use of functions.
module demo_func();
// Multiplexor function with a 16-bit output.
function real realfunc;
input a;
real a;
realfunc = a + 1.5;
endfunction
function [15:0] mux;
input [1:0] control;
input [15:0] in0,in1,in2,in3;
begin
case( control )
0: mux = in0;
1: mux = in1;
2: mux = in2;
3: mux = in3;
default: mux = 16'bx;
endcase // case( control )
end
endfunction // mux
// Population count function.
function [4:0] pop;
input [31:0] a; // Works like a register.
begin
pop = 0;
while( a )
begin
pop = pop + ( a & 1 );
// Call by value, so it's okay to modify a.
a = a >> 1;
end
end
endfunction // pop
// Note: Functions can be used in continuous assignments.
// Execution starts whenever inputs change.
// Execution completes in same time step.
reg [31:0] x;
wire [4:0] p2 = pop(x);
reg [15:0] d1, d2, d3, d4;
integer i;
wire [15:0] v = mux(i[1:0],d1,d2,d3,d4);
initial
begin:INIT
integer foo;
integer p;
foo = 32'b10101;
// Functions can also be used in procedural code.
p = pop(foo);
$display("The population of %b is: %d",foo,p);
x = foo;
#0;
$display("The population of %b is: %d",foo,p2);
d1 = 100; d2 = 200; d3 = 300; d4 = 400;
for(i=0; i<4; i=i+1)
begin:B
reg [15:0] v2;
#0;
$display("Continuous assign, for input %d output is %d",i,v);
v2 = mux(i[1:0],d1,d2,d3,d4);
$display("Procedural use, for input %d output is %d",i,v2);
end
end // block: INIT
endmodule // demo_func
`ifdef ERRORS
module demo_func_errors();
function integer wont_work;
input i;
output j; // Outputs aren't declared. Use function name.
output k; // Can't have two outputs;
wire w; // Can't declare nets.
begin
#3; // No timing control in functions.
k <= 4; // No nonblocking assignment allowed.
end
endfunction // wont_work
endmodule // demo_func_errors
`endif // ifdef ERRORS
/// Tasks
// LRM 10.2
// Something like C procedures, but there are major differences.
// Unlike functions, cannot be used in an expression.
// Unlike functions, can have outputs.
// Unlike function, can contain timing controls.
// Like functions, must be declared within a module.
// Tasks cannot decalre net data types.
// Arguments passed by value.
// Tasks enabled (called) in procedural code (except procedural code
// in functions).
// In a statement enabling (calling) a task:
// any expression can be used for an input arguments,
// only an lvalue can be used for an output argument.
// (An lvalue is an an expression that's valid on the left-hand side
// of an assignment. That includes:
// reg, integer, real, time, and realtime,
// bit and part selects of above,
// memory referenes (covered later),
// concatenations of all above.
// Example showing appropriate use of tasks, though functions for
// pop and mux would be better.
module demo_tasks();
task mux;
output [15:0] mux_out;
input [1:0] control;
input [15:0] in0,in1, in2,in3;
begin
case( control )
0: mux_out = in0;
1: mux_out = in1;
2: mux_out = in2;
3: mux_out = in3;
// exemplar translate_off
default:
begin
$display("Mux with unexpected input, %b",control);
$stop;
end
// exemplar translate_on
endcase // case( control )
end
endtask // mux
task pop;
output [4:0] p; // Work like registers.
input [31:0] a; // Work like registers.
integer p;
begin
p = 0;
while( a )
begin
p = p + ( a & 1 );
// Note: call by value so it's okay to modify input.
a = a >> 1;
end
end
endtask // pop
wire [4:0] w;
// Use of pop and mux below roughly equivalent to continuous assignment
// in task example.
reg [31:0] x;
reg [4:0] p2;
always @( x ) pop(p2,x);
reg [15:0] d1, d2, d3, d4;
integer i;
reg [15:0] v;
always @( i or d1 or d2 or d3 or d4 ) mux(v,i[1:0],d1,d2,d3,d4);
initial
begin:INIT
integer foo;
integer p;
foo = 32'b10101;
// Output arguments (first here), must be something that
// could be assigned to. For example, since
// p = foo; is okay, pop(p,foo) is okay.
pop(p,foo);
// Since w is a wire, w = foo; here would be illegal and so
// would:
// pop(w,foo); ERROR: func.v(206): Illegal task output argument
$display("The population of %b is: %d",foo,p);
x = foo;
$display("The population of %b is: %d",foo,p);
d1 = 100; d2 = 200; d3 = 300; d4 = 400;
for(i=0; i<4; i=i+1)
begin:B
reg [15:0] v2;
#1;
$display("Continuous assign, for input %d output is %d",i,v);
mux(v2,i[1:0],d1,d2,d3,d4);
$display("Procedural use, for input %d output is %d",i,v2);
end
end
endmodule // demo