7. SpaceLib API#

7.1. Introduction#

This page documents the SpaceLib design library API that is provided by the SpaceStudio environment.

7.2. SpaceLib classes#

class abstract_module : public sc_core::sc_module#

The abstract_module class is the base class for a SpaceStudio module. A module is a user-defined C/C++ algorithm. It can be mapped as a hardware co-processor or a software thread.

uint32_t get_base_address(unsigned int id)#

This method returns the base address of the slave with the given ID.

Parameters:

id – The ID of a slave device.

Returns:

The base address of the slave with the given ID.

Example:
uint32_t base_address = get_base_address(BRAM0_ID);
uint32_t get_high_address(unsigned int id)#

This method returns the high address of the slave with the given ID.

Parameters:

id – The ID of a slave device.

Returns:

The high address of the slave with the given ID.

Example:
uint32_t high_address = get_high_address(BRAM0_ID);
void hw_compute_latency(int cycles)#

For a module mapped to hardware, this method adds a temporal annotation for a wait of the given number of cycles.

If a module is mapped to software, this method does nothing.

Parameters:

cycles – The number of cycles to wait.

Example:
hw_compute_latency(10); // Adds a temporal annotation for a wait of 10 cycles.
bool is_verbose()#

This method returns the verbose property for this module. Verbosity can be configured through the property view of the component instance in SpaceStudio.

Returns:

The verbose property for this module.

Example:
if (is_verbose()) {
    std::cout << "Verbose mode is enabled for this module." << std::endl;
}
void request_stack(std::size_t size_bytes)#

This method allocates asks for a specific stack capacity for the module. If this method is ommited, the default stack capacity is 16384 bytes (16 KiB). This method is called in the module’s constructor.

For hardware modules in simulation, this method will configure SystemC’s stack, similar to sc_core::sc_module::set_stack_size.

For software modules, this method will allocate a software stack for the thread running the module.

Parameters:

size_bytes – The requested stack capacity in bytes.

Example:
request_stack(16 * 1024); // Requests a stack capacity of 16 KiB.
eSpaceStatus ModuleWrite(unsigned int destination_id, uint32_t timeout, const T *data, uint32_t nb_elements = 1)#

This method sends data through a FIFO or DMA based communication channel. The type of communication (FIFO, direct streaming or DMA) can be configured in the SpaceStudio GUI using the “Manage Communications” button.

Parameters:
  • destination_id – The ID of the destination module or hardware component. Must be a compile-time constant.

  • timeout – Delay determining whether the communication is blocking or non-blocking. Must be either set to SPACE_BLOCKING or SPACE_NON_BLOCKING.

  • data – Pointer to the start of the buffer containing the data to send.

  • nb_elements – The number of elements to send (not bytes, but by number of elements of type T). This parameter is optional and defaults to 1.

Returns:

The status of the communication, which can be SPACE_OK, SPACE_EMPTY, SPACE_FULL or SPACE_ERROR.

Example:
SPACE_ALIGNED uint8_t data[4] = { 1, 2, 3, 4 };
eSpaceStatus status = ModuleWrite(CONSUMER0_ID, SPACE_NON_BLOCKING, data, 4);

Technical close-up

To ensure that communications work properly both in hardware and embedded software implementations, it is strongly recommended that the length of the data transferred in a ModuleWrite operation is a multiple of 4 bytes (i.e. 32 bits). This is the case in this example, where the data length is 4 bytes (4 chars of 1 byte each). It is also strongly recommended that the message buffer address be aligned on a 4-byte boundary when the module is mapped to software. The SPACE_ALIGNED modifier macro is provided to help with this alignment requirement, and it is used in the example above.

Note

The type T for this method is not a template type, and thus the method can be called as-is without specifying a type. Instead, the type will be determined by SpaceStudio’s static analysis engine, which will generate the appropriate code for the communication based on the type of the data.

eSpaceStatus ModuleWrite(unsigned int destination_id, uint32_t timeout)#

Convenience method for writing to a module without specifying data. This is useful for simple handshake operations.

See the general eSpaceStatus abstract_module::ModuleWrite(unsigned int, uint32_t, const T*, uint32_t) method for details. This is equivalent to calling the general method with an array of a single uint32_t :

uint32_t dummy_data = 0;
ModuleWrite(destination_id, timeout, &dummy_data, 1);
Example:
eSpaceStatus status = ModuleWrite(CONSUMER0_ID, SPACE_BLOCKING); // Unblock CONSUMER0_ID by sending a message with dummy data.
eSpaceStatus ModuleWrite(unsigned int destination_id, uint32_t timeout, const T &data)#

Convenience method for writing a single element to a module without specifying the number of elements.

See the general eSpaceStatus abstract_module::ModuleWrite(unsigned int, uint32_t, const T*, uint32_t) method for details.

eSpaceStatus ModuleRead(unsigned int destination_id, uint32_t timeout, T *data, uint32_t nb_elements = 1)#

This method reads a message from a FIFO or DMA based communication channel. The type of communication (FIFO, direct streaming or DMA) can be configured in the SpaceStudio GUI using the “Manage Communications” button.

Parameters:
  • destination_id – The ID of the source module or hardware component. Must be a compile-time constant.

  • timeout – Delay determining whether the communication is blocking or non-blocking. Must be either set to SPACE_BLOCKING or SPACE_NON_BLOCKING.

  • data – Pointer to the start of the buffer where the read data will be stored.

  • nb_elements – The number of elements to read (not bytes, but by number of elements of type T). This parameter is optional and defaults to 1.

Returns:

The status of the communication, which can be SPACE_OK, SPACE_EMPTY, SPACE_FULL or SPACE_ERROR.

Example:
SPACE_ALIGNED uint8_t data[4] = { 0 };
eSpaceStatus status = ModuleRead(PRODUCER0_ID, SPACE_BLOCKING, data, 4);

Technical close-up

To ensure that communications work properly both in hardware and embedded software implementations, it is strongly recommended that the length of the data transferred in a ModuleRead operation is a multiple of 4 bytes (i.e. 32 bits). This is the case in this example, where the data length is 4 bytes (4 chars of 1 byte each). It is also strongly recommended that the message buffer address be aligned on a 4-byte boundary when the module is mapped to software. The SPACE_ALIGNED modifier macro is provided to help with this alignment requirement, and it is used in the example above.

Note

The type T for this method is not a template type, and thus the method can be called as-is without specifying a type. Instead, the type will be determined by SpaceStudio’s static analysis engine, which will generate the appropriate code for the communication based on the type of the data.

eSpaceStatus ModuleRead(unsigned int destination_id, uint32_t timeout)#

Convenience method for reading from a module without specifying a data buffer. This is useful for simple handshake operations.

See the general eSpaceStatus abstract_module::ModuleRead(unsigned int, uint32_t, T*, uint32_t) method for details. This is equivalent to calling the general method with an array of a single uint32_t :

uint32_t dummy_data;
ModuleRead(destination_id, timeout, &dummy_data, 1);
Example:
eSpaceStatus status = ModuleRead(PRODUCER0_ID, SPACE_BLOCKING); // Waits for PRODUCER0_ID to send a message by reading with dummy data.
eSpaceStatus ModuleRead(unsigned int destination_id, uint32_t timeout, T &data)#

Convenience method for reading a single element from a module without specifying the number of elements.

See the general eSpaceStatus abstract_module::ModuleRead(unsigned int, uint32_t, T*, uint32_t) method for details.

eSpaceStatus DeviceWrite(unsigned int destination_id, uintptr_t offset, const T *data, uint32_t nb_elements = 1)#

This method writes a message to a device (i.e. a memory-mapped component, such as memories, timers, user-defined devices, etc…).

Parameters:
  • destination_id – The ID of the destination device. Must be a compile-time constant.

  • offset – The offset from the base address of the device where the data will be written.

  • data – Pointer to the start of the buffer containing the data to send.

  • nb_elements – The number of elements to send (not bytes, but by number of elements of type T). This parameter is optional and defaults to 1.

Returns:

The status of the communication, which can be SPACE_OK, SPACE_ERROR.

Example:
// Write to a timer's control register.
uint32_t control_value = 0x4; // Value to start the timer.
eSpaceStatus status = DeviceWrite(TIMER0_ID, 0x4, &control_value, 1);

Technical close-up

To ensure that communications work properly both in hardware and embedded software implementations, it is strongly recommended that the length of the data transferred in a DeviceWrite operation is a multiple of 4 bytes (i.e. 32 bits). It is also strongly recommended that the message buffer address be aligned on a 4-byte boundary when the module is mapped to software. The SPACE_ALIGNED modifier macro is provided to help with this alignment requirement, and it is used in the example above.

eSpaceStatus DeviceWrite(unsigned int destination_id, uintptr_t offset, const T &data)#

Convenience method for writing a single element to a device without specifying the number of elements.

See the general eSpaceStatus abstract_module::DeviceWrite(unsigned int, uintptr_t, const T*, uint32_t) method for details.

eSpaceStatus DeviceRead(unsigned int destination_id, uintptr_t offset, T *data, uint32_t nb_elements = 1)#

This method reads a message from a device (i.e. a memory-mapped component, such as memories, timers, user-defined devices, etc…).

Parameters:
  • destination_id – The ID of the source device. Must be a compile-time constant.

  • offset – The offset from the base address of the device where the data will be read.

  • data – Pointer to the start of the buffer where the read data will be stored.

  • nb_elements – The number of elements to read (not bytes, but by number of elements of type T). This parameter is optional and defaults to 1.

Returns:

The status of the communication, which can be SPACE_OK, SPACE_ERROR.

Example:
// Read from a timer's counter register.
uint32_t counter_value;
eSpaceStatus status = DeviceRead(TIMER0_ID, 0x8, &counter_value, 1);

Technical close-up

To ensure that communications work properly both in hardware and embedded software implementations, it is strongly recommended that the length of the data transferred in a DeviceRead operation is a multiple of 4 bytes (i.e. 32 bits). It is also strongly recommended that the message buffer address be aligned on a 4-byte boundary when the module is mapped to software. The SPACE_ALIGNED modifier macro is provided to help with this alignment requirement, and it is used in the example above.

eSpaceStatus DeviceRead(unsigned int destination_id, uintptr_t offset, T &data)#

Convenience method for reading a single element from a device without specifying the number of elements.

See the general eSpaceStatus abstract_module::DeviceRead(unsigned int, uintptr_t, T*, uint32_t) method for details.

eSpaceStatus RegisterWrite(unsigned int register_file_id, uint32_t register_id, const T *data)#

This method writes a value to a register of a register file.

Parameters:
  • register_file_id – The ID of the register file containing the register to write. Must be a compile-time constant.

  • register_id – The ID of the register to write within the register file. Must be a compile-time constant.

  • data – Pointer to the start of the buffer containing the data to send. Should be a pointer to an unsigned long or same-size type.

Returns:

The status of the communication, which can be SPACE_OK, SPACE_ERROR.

Example:
uint32_t register_value = 0x1; // Value to write to the register.
eSpaceStatus status = RegisterWrite(REGISTER_FILE0_ID, CONTROL_REGISTER_ID, &register_value);
eSpaceStatus RegisterWrite(unsigned int register_file_id, uint32_t register_id, const T &data)#

Convenience method for writing a single element to a register without specifying a data buffer.

See the general eSpaceStatus abstract_module::RegisterWrite(unsigned int, uint32_t, const T*) method for details.

Example:
eSpaceStatus status = RegisterWrite(REGISTER_FILE0_ID, CONTROL_REGISTER_ID, 0x1);
eSpaceStatus RegisterRead(unsigned int register_file_id, uint32_t register_id, T *data)#

This method reads a value from a register of a register file.

Parameters:
  • register_file_id – The ID of the register file containing the register to read. Must be a compile-time constant.

  • register_id – The ID of the register to read within the register file. Must be a compile-time constant.

  • data – Pointer to the start of the buffer where the read data will be stored. Should be a pointer to an unsigned long or same-size type.

Returns:

The status of the communication, which can be SPACE_OK, SPACE_ERROR.

Example:
uint32_t register_value;
eSpaceStatus status = RegisterRead(REGISTER_FILE0_ID, CONTROL_REGISTER_ID, &register_value);
eSpaceStatus RegisterRead(unsigned int register_file_id, uint32_t register_id, T &data)#

Convenience method for reading a single element from a register without specifying a data buffer.

See the general eSpaceStatus abstract_module::RegisterRead(unsigned int, uint32_t, T*) method for details.

Example:
uint32_t register_value;
eSpaceStatus status = RegisterRead(REGISTER_FILE0_ID, CONTROL_REGISTER_ID, register_value);
eSpaceStatus Stream2Memory(unsigned int module_source_id, unsigned int memory_destination_id, uintptr_t memory_destination_offset, uint32_t timeout, uint32_t nb_bytes)#

This method is used to perform DMA-based communication from a hardware user module to a memory location.

Parameters:
  • module_source_id – The ID of the source module. Must be a compile-time constant.

  • memory_destination_id – The ID of the destination memory.

  • memory_destination_offset – The offset from the base address of the destination memory where the data will be written.

  • timeout – Delay determining whether the communication is blocking or non-blocking. Must be either set to SPACE_BLOCKING or SPACE_NON_BLOCKING.

  • nb_bytes – The number of bytes to transfer.

Returns:

The status of the communication, which can be SPACE_OK, SPACE_ERROR.

Example:
// Transfer 1024 bytes from a hardware module to a memory location using DMA.
eSpaceStatus status = Stream2Memory(PRODUCER0_ID, BRAM0_ID, 0x0, SPACE_BLOCKING, 1024);

Technical close-up

To ensure that communications work properly both in hardware and embedded software implementations, it is strongly recommended that the length of the data transferred in a Stream2Memory operation is a multiple of 4 bytes (i.e. 32 bits).

eSpaceStatus Memory2Stream(unsigned int memory_source_id, uintptr_t memory_source_offset, unsigned int module_destination_id, uint32_t timeout, uint32_t nb_bytes)#

This method is used to perform DMA-based communication from a memory location to a hardware user module.

Parameters:
  • memory_source_id – The ID of the source memory.

  • memory_source_offset – The offset from the base address of the source memory where the data will be read.

  • module_destination_id – The ID of the destination module. Must be a compile-time constant.

  • timeout – Delay determining whether the communication is blocking or non-blocking. Must be either set to SPACE_BLOCKING or SPACE_NON_BLOCKING.

  • nb_bytes – The number of bytes to transfer.

Returns:

The status of the communication, which can be SPACE_OK, SPACE_ERROR.

Example:
// Transfer 1024 bytes from a memory location to a hardware module using DMA.
eSpaceStatus status = Memory2Stream(BRAM0_ID, 0x0, CONSUMER0_ID, SPACE_BLOCKING, 1024);

Technical close-up

To ensure that communications work properly both in hardware and embedded software implementations, it is strongly recommended that the length of the data transferred in a Memory2Stream operation is a multiple of 4 bytes (i.e. 32 bits).

eSpaceStatus Memory2Memory(unsigned int memory_source_id, uintptr_t memory_source_offset, unsigned int memory_destination_id, uintptr_t memory_destination_offset, uint32_t timeout, uint32_t nb_bytes)#

This method is used to perform DMA-based communication from a memory location to another memory location.

Parameters:
  • memory_source_id – The ID of the source memory.

  • memory_source_offset – The offset from the base address of the source memory where the data will be read.

  • memory_destination_id – The ID of the destination memory.

  • memory_destination_offset – The offset from the base address of the destination memory where the data will be written.

  • timeout – Delay determining whether the communication is blocking or non-blocking. Must be either set to SPACE_BLOCKING or SPACE_NON_BLOCKING.

  • nb_bytes – The number of bytes to transfer.

Returns:

The status of the communication, which can be SPACE_OK, SPACE_ERROR.

Example:
// Transfer 1024 bytes from a BRAM to another using DMA.
eSpaceStatus status = Memory2Memory(BRAM0_ID, 0x0, BRAM1_ID, 0x0, SPACE_BLOCKING, 1024);

Technical close-up

To ensure that communications work properly both in hardware and embedded software implementations, it is strongly recommended that the length of the data transferred in a Memory2Memory operation is a multiple of 4 bytes (i.e. 32 bits).

class master_device#

The master_device class is the base class for a master device. A master device is a user-defined C/C++ device capable of issuing master transactions on an interconnect. A master device is always connected as both a slave and a master to the interconnect.

uint32_t get_base_address(unsigned int id)#

This method returns the base address of a slave device with a given ID. The device corresponding to the given ID must be connected to the same interconnect as the master device.

Parameters:

id – The ID of a slave device

Returns:

The base address of the slave device with the given ID.

Example:
uint32_t base_address = get_base_address(BRAM0_ID);
uint32_t get_high_address(unsigned int id)#

This method returns the high address of a slave device with a given ID. The device corresponding to the given ID must be connected to the same interconnect as the master device.

Parameters:

id – The ID of a slave device

Returns:

The high address of the slave device with the given ID.

Example:
uint32_t high_address = get_high_address(BRAM0_ID);
bool is_offset_valid(unsigned int id, uint32_t offset)#

Determines whether a provided offset is valid for the slave device with the given ID. The device corresponding to the given ID must be connected to the same interconnect as the master device.

Parameters:
  • id – The ID of a slave device

  • offset – The offset to check for validity.

Returns:

true if the provided offset is valid for the slave device with the given ID, false otherwise.

Example:
// Checks whether offset 0x100 is valid for BRAM0_ID.
bool valid = is_offset_valid(BRAM0_ID, 0x100);
uint32_t get_size(unsigned int id)#

Returns the size of the slave device with the given ID’s address space. The device corresponding to the given ID must be connected to the same interconnect as the master device.

Parameters:

id – The ID of a slave device

Returns:

The size of the slave device with the given ID’s address space in bytes

Example:
uint32_t size = get_size(BRAM0_ID);
void DeviceRead(uintptr_t address, const void *data, uint32_t nb_bytes)#

Reads a message from a device (i.e. a memory-mapped component, such as memories, timers, user-defined devices, etc…) through an interconnect.

Parameters:
  • address – The address where the data will be read. Must be mapped to a slave device.

  • data – Pointer to the start of the buffer where the read data will be stored.

  • nb_bytes – The number of bytes to read.

Example:
uint32_t base = get_base_address(BRAM0_ID);
uint32_t data[4] = { 0 };
DeviceRead(base, data, sizeof(uint32_t) * 4);
void DeviceWrite(uintptr_t address, const void *data, uint32_t nb_bytes)#

Writes a message to a device (i.e. a memory-mapped component, such as memories, timers, user-defined devices, etc…) through an interconnect.

Parameters:
  • address – The address where the data will be written. Must be mapped to a slave device.

  • data – Pointer to the start of the buffer containing the data to send.

  • nb_bytes – The number of bytes to write.

Example:
uint32_t base = get_base_address(BRAM0_ID);
uint32_t data[4] = { 1, 2, 3, 4 };
DeviceWrite(base, data, sizeof(uint32_t) * 4);
class slave_device : public sc_core::sc_module#

The slave_device class is the base class for a slave device. A slave device is a user-defined C/C++ device capable of responding to transactions on an interconnect.

unsigned int get_id()#

Returns the ID of the device. This ID can be configured in the SpaceStudio GUI, thrugh the property view of the component instance in an architecture.

Returns:

The ID of the device.

Example:
unsigned int id = get_id();
uint32_t get_base_address()#

This method returns the base address of the device’s address space.

Returns:

The base address of the device’s address space.

Example:
uint32_t base_address = get_base_address();
uint32_t get_offset(tlm::tlm_generic_payload &trans)#

Returns the offset from a TLM transaction.

Parameters:

trans – The TLM transaction received from the access method.

Returns:

The offset from the TLM transaction’s address.

uint32_t get_size()#

Returns the size of the device’s address space.

Returns:

The size of the device’s address space in bytes.

bool is_verbose()#

This method returns the verbose property for this device. Verbosity can be configured through the property view of the component instance in SpaceStudio.

Returns:

The verbose property for this device.

Example:
if (is_verbose()) {
    std::cout << "Verbose mode is enabled for this device." << std::endl;
}
void hw_compute_latency(int cycles)#

For a device mapped to hardware, this method adds a temporal annotation for a wait of the given number of cycles.

If a device is mapped to software, this method does nothing.

Parameters:

cycles – The number of cycles to wait.

Example:
hw_compute_latency(10); // Adds a temporal annotation for a wait of 10 cycles.
sc_core::sc_time &get_clock_period()#

Returns the clock period of the device.

7.3. Global Functions#

7.3.1. spacelib_global.h#

The spacelib_global.h header provides functions that are mostly useful to abstract_module, master_device, and slave_device classes.

std::string get_project_path()#

This function returns the path of the SpaceStudio project as a string.

Returns:

The path of the SpaceStudio project.

Example:
// Open a data file relative to the project path.
std::ifstream file(spacelib_global::get_project_path() + "/import/signal.bin", std::ios::in | std::ios::binary);

7.3.2. SpaceDisplay.h#

Defines function that can be used to print messages to SpaceStudio’s console.

void SpacePrint(const char *message_format, ...)#

This function prints a formatted message to SpaceStudio’s console. It supports the same formatting syntax as printf.

Parameters:

message_format – The format string for the message to print, followed by any additional arguments required by the format string.

Example:
int data = 0xFF;
SpacePrint("this is a message with data=%d", data);
void SpaceMessageVerbose(bool verbose, const char *name, const char *message_format, ...)#

Prints a formatted message to SpaceStudio’s console if the verbose parameter is true, prefixed by the provided name. It supports the same formatting syntax as printf.

Parameters:
  • verbose – Whether to print the message or not.

  • name – The name to prefix the message with (usually the object or module name).

  • message_format – The format string for the message, followed by any additional arguments required by the format string.

Example:
SpaceMessageVerbose(is_verbose(), name(), "this is a verbose messaged");
void SpaceMessageError(const char *name, const char *message_format, ...)#

Prints a formatted error message to SpaceStudio’s console, prefixed by the provided name. It supports the same formatting syntax as printf.

The message will be highlighted in red in the console to indicate that it is an error message.

Parameters:
  • name – The name to prefix the error message with (usually the object or module name).

  • message_format – The format string for the error message, followed by any additional arguments required by the format string.

Example:
int error_code = 0xFF;
SpaceMessageError(name(), "Error %", error_code);
void SpaceMessageWarning(const char *name, const char *message_format, ...)#

Prints a formatted warning message to SpaceStudio’s console, prefixed by the provided name. It supports the same formatting syntax as printf.

The message will be highlighted in yellow in the console to indicate that it is a warning message.

Parameters:
  • name – The name to prefix the warning message with (usually the object or module name).

  • message_format – The format string for the warning message, followed by any additional arguments required by the format string.

Example:
int warning_code = 0xFF;
SpaceMessageWarning(name(), "This is a warning with code=%d");

7.4. Globally defined macros#

Some macros are defined by the SpaceStudio engine to enable user code to adapt itself to the architecture mapping.

MODULENAME0_MAPPING#

The name of this macro will depend on the module instance name. For instance, the macro for the first instance of a module named consumer will be CONSUMER0_MAPPING, then CONSUMER1_MAPPING for the second instance, and so on.

This macro will be defined to either HW_MAPPING or SW_MAPPING (two other macro-defined values) depending on the mapping of its corresponding module instance. It allows a module to query the mapping of other instances.

Example:
#if CONSUMER0_MAPPING == HW_MAPPING
    // Code specific to the case where CONSUMER0 is mapped to hardware.
#else
    // Code specific to the case where CONSUMER0 is mapped to software.
#endif
MAPPING#

This macro is defined within the context of a module’s code to either HW_MAPPING or SW_MAPPING (two other macro-defined values) depending on the mapping of the module.

It allows the module to query its own mapping.

Example:
#if MAPPING == HW_MAPPING
    // Code specific to the case where this module is mapped to hardware.
#else
    // Code specific to the case where this module is mapped to software.
#endif
MODULENAME_GROUP_SIZE#

The name of this macro will depend on a module’s name. For instance, for a module named producer, the macro will be PRODUCER_GROUP_SIZE.

This macro gives the number of instances of a module in the architecture. This is useful to write code that adapts to the number of instances of a module, for instance by using it as the size of an array.

Example:
// Define an array with an element for each instance of the producer module.
int data[PRODUCER_GROUP_SIZE];
INDEX#

This macro gives the instance number of the instance of a module. For instance, if there are 4 instances of a module named producer, the INDEX macro will be defined to 0 in the context of the first instance, 1 in the context of the second instance, and so on.

Example:
// One-to-one communication between producer and consumer modules using the INDEX macro to match instances.
ModuleWrite(CONSUMER_GROUP[INDEX], SPACE_BLOCKING, 0xdeadbeef);
SPACE_SIMULATION#

This macro is defined when the code is being compiled for simulation. It can be used to conditionally compile code that should only be included in the simulation version of the design, and not in the implementation version.

Example:
#ifdef SPACE_SIMULATION
    // Code specific to the simulation.
#else
    // Code specific to implementation.
#endif
SPACE_ARCHITECTURE_IMPLEMENTATION#

This macro is defined when the code is being compiled for the implementation of the architecture. It can be used to conditionally compile code that should only be included in the implementation version of the design, and not in the simulation version.

Example:
#ifdef SPACE_ARCHITECTURE_IMPLEMENTATION
    // Code specific to the implementation.
#else
    // Code specific to simulation.
#endif