|
| 1 | +Accessing IP Cores from Linux |
| 2 | +============================= |
| 3 | + |
| 4 | +PHANTOM FPGA IP cores are accessed by Linux userland software through the following API. |
| 5 | + |
| 6 | + |
| 7 | +Common Definitions |
| 8 | +------------------------ |
| 9 | + |
| 10 | +.. macro:: PHANTOM_OK |
| 11 | + |
| 12 | + Returned by functions to indicate no error. |
| 13 | + |
| 14 | +.. macro:: PHANTOM_FALSE |
| 15 | + |
| 16 | + Returned by functions to indicate false. |
| 17 | + |
| 18 | +.. macro:: PHANTOM_ERROR |
| 19 | + |
| 20 | + Returned by functions to indicate an otherwise unspecified error. |
| 21 | + |
| 22 | +.. macro:: PHANTOM_NOT_FOUND |
| 23 | + |
| 24 | + Returned by functions to indicate something was not found. |
| 25 | + |
| 26 | +.. type:: phantom_data_t |
| 27 | + |
| 28 | + A type guaranteed to be wide enough to store an entire word. On Zynq systems this is `uint32_t`, and on a Zynq UltraScale+ device it is `uint64_t`. |
| 29 | + |
| 30 | +.. type:: phantom_address_t |
| 31 | + |
| 32 | + A type guaranteed to be wide enough to store an entire system address. On Zynq systems this is `uint32_t`, and on a Zynq UltraScale+ device it is `uint64_t`. |
| 33 | + |
| 34 | +.. macro:: PHANTOM_DMA_TO_IP |
| 35 | + |
| 36 | + Specify a DMA transfer from main memory into an IP core. |
| 37 | + |
| 38 | +.. macro:: PHANTOM_DMA_FROM_IP |
| 39 | + |
| 40 | + Specify a DMA transfer from an IP core's address space into main memory. |
| 41 | + |
| 42 | + |
| 43 | + |
| 44 | +Configuring the FPGA |
| 45 | +--------------------- |
| 46 | + |
| 47 | +.. function:: int phantom_fpga_is_done() |
| 48 | + |
| 49 | + Check the FPGA's `done` pin, which is asserted when the FPGA is successfully programmed with a bitfile. |
| 50 | + |
| 51 | + :return: |
| 52 | + * :macro:`PHANTOM_OK` if the FPGA has been configured with a compatible bitfile |
| 53 | + * :macro:`PHANTOM_FALSE` if the DONE pin is not asserted. |
| 54 | + |
| 55 | + |
| 56 | +.. function:: int phantom_fpga_configure(FILE *bitfile) |
| 57 | + |
| 58 | + Configure the FPGA fabric with the specified bitfile. bitfile is a configuration file as generated by the Xilinx tools for the appropriate FPGA part. This function assumes that the FPGA is not currently programmed. Note that this function does not block until programming is complete. :func:`phantom_fpga_is_done()` should be called to determine that programming has completed successfully before the FPGA is used. |
| 59 | + |
| 60 | + :param FILE* bitfile: A FILE pointer to a compatible bitfile, opened in binary mode. |
| 61 | + |
| 62 | + :return: |
| 63 | + * :macro:`PHANTOM_OK` if configuration started successfully |
| 64 | + * :macro:`PHANTOM_ERROR` if the bitfile could not be sent to the FPGA. |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | +The `phantom_ip_t` structure |
| 69 | +---------------------------- |
| 70 | + |
| 71 | +The PHANTOM IP cores in the current design are represented by instances of the `phantom_ip_t` structure which are obtained by calling :func:`phantom_fpga_get_ips()`. |
| 72 | + |
| 73 | + |
| 74 | +.. type:: typedef struct {...} phantom_ip_t; |
| 75 | + |
| 76 | +The structure contains at least the following members: |
| 77 | + |
| 78 | +.. member:: char *idstring |
| 79 | + |
| 80 | + An identifying string. |
| 81 | + |
| 82 | +.. member:: uint32_t id |
| 83 | + |
| 84 | + A unique identifier for the IP. |
| 85 | + |
| 86 | +.. member:: uint8_t num_masters |
| 87 | + |
| 88 | + The number of AXI Masters in the IP. |
| 89 | + |
| 90 | +.. member:: phantom_address_t base_address |
| 91 | + |
| 92 | + The base memory address of the IP core's AXI Slave. |
| 93 | + |
| 94 | +.. member:: phantom_address_t address_size |
| 95 | + |
| 96 | + The size in bytes on the IP core's AXI Slave memory space. |
| 97 | + |
| 98 | +.. |
| 99 | +
|
| 100 | + |
| 101 | +Reading IP Core Information |
| 102 | +--------------------------- |
| 103 | + |
| 104 | +.. function:: int phantom_fpga_get_num_ips() |
| 105 | + |
| 106 | + If the FPGA is currently programmed, this gives the number of PHANTOM IP cores in the current design. |
| 107 | + |
| 108 | + :return: the number of IP cores that are currently configured onto the FPGA. -1 if a problem is encountered. |
| 109 | + |
| 110 | + |
| 111 | +.. function:: phantom_ip_t *phantom_fpga_get_ips() |
| 112 | + |
| 113 | + If the FPGA is currently programmed, this fetches an array of `phantom_ip_t` that describe the PHANTOM IP cores in the design, as in the following example:: |
| 114 | + |
| 115 | + phantom_ip_t *ips = phantom_fpga_get_ips(); |
| 116 | + |
| 117 | + for(int i = 0; i < phantom_fpga_get_num_ips(); i++) { |
| 118 | + printf("%d: %s\n", i, ips[i].idstring); |
| 119 | + } |
| 120 | + |
| 121 | + :return: An array of the PHANTOM IPs in the currently-programmed design, or `NULL` if none exist. |
| 122 | + |
| 123 | + |
| 124 | +.. function:: int phantom_fpga_ip_initialise(phantom_ip_t *ip, const char *idstring) |
| 125 | + |
| 126 | + If the FPGA is currently programmed, search for the PHANTOM IP core with the provided `idstring`. The `ip` struct is then initialised. |
| 127 | + |
| 128 | + :param phantom_ip_t* ip: A `phantom_ip_t` struct to be initialised. |
| 129 | + :param char* idstring: The identifier string of the IP. |
| 130 | + |
| 131 | + :return: |
| 132 | + * :macro:`PHANTOM_OK` if the IP is successfully initialised. |
| 133 | + * :macro:`PHANTOM_NOT_FOUND` if the FPGA is correctly programmed, but the requested core cannot be found. |
| 134 | + * :macro:`PHANTOM_ERROR` if another error occurs. |
| 135 | + |
| 136 | + |
| 137 | +.. function:: int phantom_fpga_ip_initialise_by_id(phantom_ip_t *ip, uint32_t id) |
| 138 | + |
| 139 | + As :func:`phantom_fpga_ip_initialise()`, but initialises a core based on its unique id. This is useful for when multiple copies of the same IP core are present. These can be queried through the use of :func:`phantom_fpga_get_ips()`. |
| 140 | + |
| 141 | + :param phantom_ip_t* ip: A `phantom_ip_t` struct to be initialised. |
| 142 | + :param uint32_t id: The unique identifier of the IP. |
| 143 | + |
| 144 | + :return: |
| 145 | + * :macro:`PHANTOM_OK` if the IP is successfully initialised. |
| 146 | + * :macro:`PHANTOM_NOT_FOUND` if the FPGA is correctly programmed, but the requested core cannot be found. |
| 147 | + * :macro:`PHANTOM_ERROR` if another error occurs. |
| 148 | + |
| 149 | + |
| 150 | + |
| 151 | + |
| 152 | + |
| 153 | + |
| 154 | + |
| 155 | +IP Control |
| 156 | +---------- |
| 157 | + |
| 158 | +.. function:: int phantom_fpga_ip_start(phantom_ip_t* ip) |
| 159 | + |
| 160 | + Starts the specified IP. Has no effect if the core is already started. |
| 161 | + |
| 162 | + :param phantom_ip_t* ip: The IP core to control. |
| 163 | + |
| 164 | + :return: :macro:`PHANTOM_OK` if the core started successfully, or :macro:`PHANTOM_ERROR` if not. |
| 165 | + |
| 166 | + |
| 167 | +.. function:: int phantom_fpga_ip_is_done(phantom_ip_t* ip) |
| 168 | + |
| 169 | + Checks if the specified IP has completed its execution. |
| 170 | + |
| 171 | + :param phantom_ip_t* ip: The IP core to query. |
| 172 | + |
| 173 | + :return: :macro:`PHANTOM_OK` if the core stopped successfully, or :macro:`PHANTOM_FALSE` if not. |
| 174 | + |
| 175 | + |
| 176 | +.. function:: int phantom_fpga_ip_is_idle(phantom_ip_t* ip) |
| 177 | + |
| 178 | + Checks if the specified IP is idle and ready for I/O or to be started. |
| 179 | + |
| 180 | + :param phantom_ip_t* ip: The IP core to query. |
| 181 | + |
| 182 | + :return: :macro:`PHANTOM_OK` if the core is idle, or :macro:`PHANTOM_FALSE` if not. |
| 183 | + |
| 184 | + |
| 185 | + |
| 186 | + |
| 187 | +IP Data I/O |
| 188 | +----------- |
| 189 | + |
| 190 | +PHANTOM IPs are presented to the Linux kernel as Userland IO (UIO) devices, and so the entire address space can be mapped and accessed using simple get and set functions. This is not the most efficient way to move large volumes of data however. The IP core itself can read and write from main system memory through an AXI master interface. Therefore the UIO interface should be used to pass parameters and configuration data, and the device itself should fetch input data as required using AXI burst transfers. |
| 191 | + |
| 192 | +It is also possible for the hardware design to include an AXI DMA controller to support automatic bulk data movement. This core can be controlled through the UIO interface through the simple helper functions provided here. More complex DMA transfers can be coded directly. |
| 193 | + |
| 194 | + |
| 195 | +.. function:: int phantom_fpga_ip_set(phantom_ip_t* ip, phantom_address_t addr, phantom_data_t val) |
| 196 | + |
| 197 | + Set a value inside the AXI Slave address space of the IP. `addr` is based from 0 and will be automatically offset to the appropriate base address (`phantom_ip_t.base_address`). |
| 198 | + |
| 199 | + :param phantom_ip_t* ip: The IP core to query. |
| 200 | + :param phantom_address_t addr: The address, based at 0, inside the address space of the IP core. |
| 201 | + :param phantom_data_t val: The value to set. |
| 202 | + |
| 203 | + :return: :macro:`PHANTOM_OK` if the value was set, or :macro:`PHANTOM_ERROR` if not. |
| 204 | + |
| 205 | + |
| 206 | +.. function:: phantom_data_t phantom_fpga_ip_get(phantom_ip_t* ip, phantom_address_t addr) |
| 207 | + |
| 208 | + Get a value from the AXI Slave address space of the IP. `addr` is based from 0 and will be automatically offset to the appropriate base address (`phantom_ip_t.base_address`). |
| 209 | + |
| 210 | + :param phantom_ip_t* ip: The IP core to query. |
| 211 | + :param phantom_address_t addr: The address, based at 0, inside the address space of the IP core. |
| 212 | + |
| 213 | + :return: The value of the argument specified by `addr`. |
| 214 | + |
| 215 | + |
| 216 | +.. function:: int phantom_fpga_dma_transfer(phantom_ip_t* ip, phantom_address_t dma_core, phantom_address_t buffaddr, phantom_address_t length, int direction) |
| 217 | + |
| 218 | + Cause a DMA core in the specified IP core to initiate a DMA transfer. This function assumes that an AXI DMA IP core is located at the appropriate address in the memory space of the target IP. This function returns immediately and the transfer will begin a time after this. For more details consult the Xilinx DMA Core driver. |
| 219 | + |
| 220 | + :param phantom_ip_t* ip: The IP core to query. |
| 221 | + :param phantom_address_t dma_core: The offset, starting at 0, of the DMA core inside the address space of the IP. |
| 222 | + :param phantom_address_t buffaddr: The address in main memory to start the transfer from. |
| 223 | + :param phantom_address_t length: The length in bytes of the transfer. |
| 224 | + :param int direction: The direction of the transfer. Valid values are `PHANTOM_DMA_TO_IP` or `PHANTOM_DMA_FROM_IP` |
| 225 | + |
| 226 | + :return: :macro:`PHANTOM_OK` if the transfer was started, or :macro:`PHANTOM_FALSE` if not. |
| 227 | + |
| 228 | + |
| 229 | +.. function:: int phantom_fpga_dma_is_idle(phantom_ip_t* ip, phantom_address_t dma_core, int direction) |
| 230 | + |
| 231 | + Check if the DMA core in the specified IP core is idle in the given direction. Because the Xilinx AXI DMA core is bidirectional, it is possible for a transfer to be proceeding in one direction whilst the other is idle. |
| 232 | + |
| 233 | + :param phantom_ip_t* ip: The IP core to query. |
| 234 | + :param phantom_address_t dma_core: The offset, starting at 0, of the DMA core inside the address space of the IP. |
| 235 | + :param int direction: The direction of the transfer. Valid values are `PHANTOM_DMA_TO_IP` or `PHANTOM_DMA_FROM_IP` |
| 236 | + |
| 237 | + :return: :macro:`PHANTOM_OK` if the core is idle, or :macro:`PHANTOM_FALSE` if not. |
0 commit comments