Section 10.3 always_comb and function sensitivity


Subject: Section 10.3 always_comb and function sensitivity
From: Adam Krolnik (krolnik@lsil.com)
Date: Thu Mar 28 2002 - 08:43:17 PST


Good morning;

The current spec compares always_comb and @* and notes this
difference:

"always_comb is sensitive to changes within the contents of a
function whereas @* is only sensitive to the changes to the
arguments of the function."

For example, the result of a block using 'always_comb' vs 'always @*'
will potentially be different when the code calls this simple function:

function [3:0] bad_code;
  input [3:0] offset;

  bad_code = offset + base; // Add the user offset to the global base.

endfunction

The argument for having this proposal is that it is a good usage
model to have functions that use information from within the
current module but are not identified in the signature of the
function. For example, a module has three computational units
within that utilize common information to all but also has
separate information for each.

module three_barrel(
  input global_inputs;
  input barrel1, barrel2, barrel3;
  output global_outputs;
  output barrel1_out, barrel2_out, barrel3_out;

reg ...;
always_comb
  begin
  <compute some global elements to all 3 barrels.>
  ...
  end

reg barrel1_...;
reg barrel2_...;
reg barrel3_...;

always_comb
  begin
  <computation for barrel1 using barrel1
   variables and global variables>;
 
  ...
  // This is not a great idea - I copied the above large
  // code and changed the names from barrel1 to barrel2.
  // I wish there would be a better way?!
  <same computation for barrel2 using barrel2
   variable and global variable.>

  ...

  // Arrrgh - same thing for barrel3... What to do?>
  ...
  end

endmodule

Okay, how about taking the above code and making a function.

 function barrel1_outs barrel_computation;
   input barrel1_<signals>;
   begin
   <move code from above down here - don't change a thing :)>
   end

Now rewrite the above block as follows.

 
always_comb
  begin
  barrel1_out = barrel_computation(barrel1_<signal set>);

  ...
  barrel2_out = barrel_computation(barrel2_<signal set>);

  ...
  barrel3_out = barrel_computation(barrel3_<signal set>);

  ...
  end

Hey, that made my work much easier. Except that there are more
signals that the function depends on that I've noted in the
signature.

I would argue that this is a poor way for these reasons:

  1. You will confuse a reviewer of the code to see that there
     are variables not mentioned in the function interface.

  2. Someone else using the code may not know that a variable
     is used by the function and accidently make unintended changes.

There are other potential problems with this proposed feature:
  1. What do you do with static variables in a function. Does
     a change in these cause all blocks that use the function
     to recompute their values?

  2. What about functions that call functions, etc. This could
     have a far reaching effect.

  3. You prevent potential use of a function or require a copy
     and rewrite if another use is required but with a few different
     global variables.

I would propose an alternative approach to the use of the function.
This approach would use the nested modules. This would be a better
approach for these reasons:

  1. Nested modules have access to the parent module's internal
     signals - this provides the globals without having to
     specifically define them in the interface.

  2. Modules allow for multiple outputs whereas functions only
     return 1 value. The above code would either have to create
     structures to make it work or return a concatenation of
     outputs. Both of these is more work and the second is error
     prone.

  3. Modules are the correct way to proceed when you have a
     reuse of code due to replicated structures in a block.

  4. Modules provide better debugging support than functions.
     The information is maintained for each instance and you are
     able to view the intermediate values. With functions, you are
     not able to view all the intermediate values for each call.

I propose striking the above sentence from section 10.3.

"always_comb is sensitive to changes within the contents of a
function whereas @* is only sensitive to the changes to the
arguments of the function."
 
We would be left with always_comb still being different from @*.

   Adam Krolnik
   Verification Mgr.
   LSI Logic Corp.
   Plano TX. 75074



This archive was generated by hypermail 2b28 : Thu Mar 28 2002 - 08:44:58 PST