Skip to content

Global Variables

Julian Kemmerer edited this page Jun 26, 2022 · 52 revisions

Global variables that are not shared (used by a single function instance) are identical to static local variables.

Otherwise global variables are the indicator of shared data between functions.

If the functions sharing the global variable exist in multiple clock domains, then the global variable is not used directly in code and instead requires use of READ and WRITE wrapper clock domain crossing mechanisms.

Shared Global Variables (Same Clock Domain)

Only the function instance that writes to the shared global variable is considered a stateful function and that function behaves as expected regarding reads+writes ordering/statefullness of the global variable, and lack of autopipelining.

This means that the other function instances that use the global variable can only read from it. The value that these functions read from the global is from 'at the end' of the function execution where the variable is written.

Consider the below example with a instance of a write_func writing to a shared global variable the_global. Two read function instances read_main/read_func and another_read_main/read_func read from the shared global. Both read functions see the value of the_global as it is at the end of the write function's execution.

// Global variable value at the end of the write function's execution/return
// is what read functions see at/triggers the start of their execution
int32_t the_global = 0;
// All reads and writes must be same clock domain
//  or marked as as ASYNC_WIRE
//#pragma ASYNC_WIRE the_global

void write_func(uint1_t sel)
{
  // Save global
  int32_t temp = the_global;
  // Do stuff ~maybe
  if(sel)
  {
    // 'Erase it'
    the_global = -1;
    // Do some stuff
    int32_t result = temp*temp;
    // Reset global
    the_global = temp;
    // Accum for demo
    the_global += result;
  }
}
#pragma MAIN_MHZ write_main 100.0
void write_main(uint1_t sel)
{
  write_func(sel);
}
/*// Not allowed, get error about ~multiple drivers
#pragma MAIN_MHZ another_write_main 100.0
void another_write_main(uint1_t sel)
{
  write_func(sel);
}*/

uint32_t read_func()
{
  static uint32_t local_counter;
  if(the_global==-1)
  {
    // Never reaches here
    // "inter-clock cycle" variable state from write_main never seen
    // Only value of the_global at the end of write_func is seen
    local_counter = 0;
  }
  // Count as demo
  local_counter += the_global;
  return local_counter;
}
#pragma MAIN_MHZ read_main 100.0
uint32_t read_main()
{
  return read_func();
}
#pragma MAIN_MHZ another_read_main 100.0
uint32_t another_read_main()
{
  return read_func();
}
/*// Get error about multiple clocks unless marked ASYNC_WIRE
#pragma MAIN_MHZ not_same_clk_read_main 50.0
uint32_t not_same_clk_read_main()
{
  return read_func();
}*/

Shared Global Wires (Same Clock Domain)

Shared global wires are declared identically to shared global registers as above. A shared global register that stores no value from cycle to cycle instead infers a shared global wire:

In order to have the register part of your global variable optimize away: never use the stored state of the global variable. Instead always be writing a write a value to the global variable that is a combination of other signals (and not state), then no storage register is needed.

Clone this wiki locally