Legacy AXI4 Slave Interface

The legacy AXI4 slave interface is similar to the AXI4 target interface in hardware. The legacy AXI4 slave interface can only be applied to a global variable with a struct data type, where each struct element becomes a memory-mapped register or RAM that can be accessed via the AXI4 slave interface. The pragma below specifies an AXI4 slave interface,

// For shared global variables:
// Add before the variable definition
#pragma HLS interface variable(<GLOBAL_VARIABLE_NAME>) type(axi_slave) concurrent_access(true|false)

When the concurrent_access option is set to true (default to false), the external logic can read/write the AXI4 slave interface while the SmartHLS module is running. The concurrent access will however reduce the SmartHLS module's throughput to access the memory.

After compilation, SmartHLS will generate a report file (hls_output/reports/axi_slave_memory_map.hls.rpt) to specify the address map for each struct element. Here is an example struct and its corresponding memory map.

#ifndef __SLAVE_LAYOUT_H__
#define __SLAVE_LAYOUT_H__

#include <stdint.h>

// Define the AXI slave memory layout as a struct in a header file.
struct SlaveLayout {
    uint16_t array[8];
    uint32_t a, b;
    uint64_t sum_result;
    uint32_t xor_result, or_result;
};

#endif
// Declare a 'SlaveLayout' type global variable in C++ source file (.cpp).
// Use HLS interface pragma with axi_slave type to specify an AXI4 slave interface.
#pragma HLS interface variable(global_var) type(axi_slave) concurrent_access(true)
SlaveLayout global_var;

The corresponding address map report (hls_output/reports/axi_slave_memory_map.hls.rpt) is shown below.

Address Map for AXI Slave Interface: global_var

+--------------+-----------+-------------------+----------+
| Word Address | Bit Range | Variables         | Removed? |
+--------------+-----------+-------------------+----------+
| 0            |  15 :  0  | memory.array[0]   |          |
|              |  31 : 16  | memory.array[1]   |          |
|              |  47 : 32  | memory.array[2]   |          |
|              |  63 : 48  | memory.array[3]   |          |
| 1            |  15 :  0  | memory.array[4]   |          |
|              |  31 : 16  | memory.array[5]   |          |
|              |  47 : 32  | memory.array[6]   |          |
|              |  63 : 48  | memory.array[7]   |          |
| 2            |  31 :  0  | memory.a          |          |
|              |  63 : 32  | memory.b          |          |
| 3            |  63 :  0  | memory.sum_result |          |
| 4            |  31 :  0  | memory.xor_result |          |
|              |  63 : 32  | memory.or_result  |          |
| 5            |   0 :  0  | slave_memory_ctrl |          |
+--------------+-----------+-------------------+----------+
  • Note that the first column in the report shows the word-address -- multiply by 8 to get the byte-address.
  • The last column will indicate the struct elements that are optimized away from compilation because the SmartHLS module does not access them.
  • Notice that the last element in the table, slave_memory_ctrl, is not part of the struct definition. This is a special status control register for the SmartHLS module. Writing to the address of slave_memory_ctrl will start the SmartHLS module (if the module was not running), and reading the register can poll the status, a value of 1 indicates the SmartHLS module has finished running, and 0 otherwise. This memory-mapped control interface can be useful for an AXI4 master to control the SmartHLS-generated module's execution (e.g., a processor controlling the SmartHLS-generated module).
Important: The legacy AXI4 slave has some restrictions,
  • A SmartHLS module can have at most one AXI4 slave interface, and the AXI4 slave interface type can only be specified for a global variable with a struct data type.
    • If multiple data need to be placed behind the AXI4 slave interface, you can define a new struct type to include all the data, then instantiate a global variable with the struct type, and specify the above pragma for the global variable.
  • AXI4 slave interface is incompatible with any top-level that has AXI4 Target for Module Control. If your design uses any AXI4 slave interface, you can't use AXI4 Target for Module Control for any top-level.
  • The AXI4 slave interface always uses 32-bit address and 64-bit data width.
  • The AXI4 slave interface only supports the AXI4-lite protocol with additional support for incremental bursting.
  • The AxBURST and AxSIZE input signals are ignored by the slave logic.
    • Regardless of the actual input values of the AxBURST and AxSIZE signals, the AXI4 slave always uses the incremental burst type (AxBURST == 1) with a size of 8 bytes per transfer (AxSIZE == 3).
  • Byte-enable write (via WSTRB port) must be aligned to the elements in the struct.
    • For the WSTRB bits corresponding to (different bytes of) a common struct element, these WSTRB bits must be all 1s or all 0s.
    • For example, if we are writing to a word address that maps to two 'int' type integers (4 bytes each, packed together as one 64-bit AXI word), the upper 4 bits of the WSTRB port must be all 1s or 0s, and the same applies to the lower 4 bits of the WSTRB port. That is, we cannot update partial bytes of either 'int' type integers, but updating all bytes of one of the two 'int' type integers is allowed.
  • The SW/HW Co-Simulation is only supported if the top level function is not pipelined.
  • When AXI4 slave interface is used, the top-level function must use void return type.