/// 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