Skip to content

Modules, Hierarchy, Composability

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

Top Level IO

Any function marked as a pragma 'MAIN', ex. #pragma MAIN my_main_inst, will have its inputs and output arguments/value exposed as a port on the top level generated VHDL module. ex.

#pragma MAIN_MHZ my_main 100.0
uint8_t my_main(uint8_t input_port_name)
{
  return ...
}
entity top is
port(
  clk_100p0 : in std_logic;
  -- IO for each main func
  my_main_input_port_name : in unsigned(7 downto 0);
  my_main_return_output : out unsigned(7 downto 0)
);
end top;

Global Wires/Ports

It has also proved useful to use shared global variables clock crossing mechanisms to create globally visible versions of ports so multiple function instances can use the wires. For example:

#pragma MAIN_MHZ my_main 100.0
uint8_t my_global_input;
uint8_t my_global_output;
uint8_t my_main(uint8_t input_port_name)
{
  // Connect top level port to global port
  my_global_input = input_port_name;
  // Connect top level port to global port
  uint8_t output_wire = my_global_output;
  return output_wire;
}
// Later, anywhere in code can connect to the other side of those wires
some_input_val = my_global_input;
my_global_output = some_output_val;

stylediagram


Feed forward nested functions

// sub.c
output_t sub(input_t input)
{
  // Do work on input to get output 
  return work(input);
}
// top.c
#include "sub.c"
output_t top(input_t input)
{
  return sub(input);
}

Point to point wires between modules

// top.c
// Globally visible ports/wires
input_t top_to_sub; // Output from top, input to sub
output_t sub_to_top; // Input into top, output from sub
output_t top(input_t input)
{
  top_to_sub = input;
  return sub_to_top;
}
// sub.c
#include "top.c"
void sub()
{
  // Do work on input to get output
  input_t input = top_to_sub;
  output_t output = work(input);
  sub_to_top = output;
}

Using both - debug/status LEDs example

bothstyles

As opposed to rewiring the LEDs manually through the ports of top,sub, and work using a globally visible point to point wire makes reading and writing arbitrary signals in the design possible.

// leds.c
uint4_t leds; // Globally visible port/wire name
uint4_t leds_module()
{
  // Drive the output port with the global wire
  return leds;
}
#include "leds.c"
output_t work(input_t input)
{
  // Doing lots of complicated stuff
  // ...
  // Lets drive status leds
  leds = 0xF;
  // ...
} 
output_t sub(input_t input)
{
  return work(input);
} 
output_t top(input_t input)
{
  return sub(input);
}