Skip to content

Global Variables

JulianKemmerer edited this page May 30, 2022 · 52 revisions

static local variables maintain state between function calls/execution ... like you would expect in C.

Global variables used by a single function are identical to 'static local variables' discussed here. Prefer static locals to avoid polluting global namespace. Global variables belong to a single function and cannot be used to move data between functions. (yet?) See clock crossings for wires + message passing like interfaces for moving data around in global scope.

Functions that use such variables are referred to as "stateful functions" and those "stateful variables" are hardware registers.

The most straight forward example of a stateful function is something like blinking LEDs.

Below describes a slightly more advanced example. Feed-forward state and pipelined logic:

Here is PipelineC code that toggles between multiplying or dividing two floating point numbers depending on a stateful static select variable. First set of numbers is multiplied, the next divided, the next multiplied, etc. This stateful toggling variable is a single hardware register in addition to the pipeline registers for the main function.

// uint1_t select; // Prefer static locals instead
uint1_t toggle_select()
{
   static uint1_t select;
   if(select)
   {
      select = 0;
   }
   else
   {
      select = 1;
   }
   return select;
}

float main(float x, float y)
{
   float rv;  
   if(toggle_select())
   {
       rv = x * y;
   }
   else
   {
       rv = x / y;
   }
   return rv;
}

But whats the deal with putting the toggle in a separate function? Take a look at these rules for stateful functions:

  1. Stateful functions cannot be pipelined. That is, these functions fundamentally limit the operating frequency of your design and should be made as simple as possible.
  2. Each call to a stateful function or function containing stateful function calls creates separate copies of the stateful variables. Ex. Calling 'toggle_select()' again would not be toggling the same 'select' as the previous function call. Two instances of 'select' would exist.
  3. Stateful functions or functions containing stateful functions when called conditionally (ex. inside an IF) infer clock enable logic for all registers in the funciton. Autopipelined stateless functions do not currently support clock enable / conditinal use.

Wait, wait, wait, what hardware does this actually implement?

diagram

The above example implements two separate floating point modules - one for the divide, one for the multiply. The two operations are performed in parallel and their results muxed to get the return value.

Aw, wow, no resource sharing? Well ok, advanced user, the floating point operators are implemented as PipelineC functions that you can modify. Combine the code for the divide and multiply into one function, add in the muxing logic, and begin finding places where you can share variables/operators/etc to construct this shared multiplier/divider unit of yours - and probably shoot me an email.

What if I need functions with state but don't want to limit the operating frequency of my design? Well, you probably need to restructure your design, maybe lower your clock rate... or perhaps can try to use volatile stateful variables but they are pretty weird...

Clone this wiki locally