C-Callable IP Libraries

This section describes how to create a C-Callable IP library for IP blocks written in a hardware description language like VHDL or Verilog. User applications can statically link with such libraries using the SDSoC™ system compiler, and the IP blocks are instantiated into the generated hardware system. Using the SDx™ IDE and its command line equivalents are shown.

Figure: Create and Use a C-Callable Library

SDSoC applications can use C-Callable IP libraries to access packaged IP blocks written in a hardware description language (HDL) such as VHDL or Verilog or with a high-level synthesis (HLS) tool like Vivado® HLS. At times, hardware specific optimizations or micro-architectures are easier to implement through an HDL and can be delivered encapsulated within a C-Callable library.

Using a C-Callable IP library provides both the design reuse flexibility of a software library and the performance gain of optimized hardware IP blocks. With a bottom-up approach individual IP blocks can be designed, implemented, and tested prior to being placed into a C-callable library for broader use. The library of hardware-accelerated functions allows a means to insulate hardware and software development teams from low-level implementation details while still ensuring that both teams are cognizant of the functional interfaces.

Creating C-Callable IP Libraries

The SDx™ installation contains examples of C-Callable IP in the <SDx_Install_Dir>/samples/rtl directory. These examples show single-function as well as multi-function accelerator libraries where function arguments are passed as registers, memory references, or AXI streams, which are highlighted (axilite_arraycopy, aximm_arraycopy, axis_arraycopy). The multi-function examples (mfa_fir, mfa_scalar128_none, mfa_scalar_axi) highlight single accelerators with multiple software entry points.

The SDx IDE's Library Project flow is used to create a C-Callable IP library with foundational support from the sdx_pack utility. A description of the sdx_pack command line arguments can be found in the SDx Command and Utility Reference Guide (UG1279). For inspecting interfaces within Vivado-packaged IP, sdx_pack provides the capability to query IP settings including their hardware interfaces. The SDx IDE uses this feature to populate menu selections with interfaces and pull-down choices relevant to the IP being transformed into a C-Callable IP library.

A procedure for creating a C-Callable IP library using the axis_arraycopy example for a zcu102 platform is described below. The axis_arraycopy example contains the Vivado-packaged IP and the files used to create the libarraycopy.a library. The arraycopy.hpp header file contains the software function declarations associated with the hardware functionality provided in the packaged IP. The component.xml contains the meta-data used by the SDx IDE and the underlying sdx_pack tool to build the library.

The following steps are necessary to create the C-Callable IP library with the SDx IDE:

  1. Create a C-Callable IP Library project.
    • Each library is created for a specific device_family, cpu_type, and OS_type tuple.
  2. Import source files from the <SDx_Install_Dir>/samples/rtl directory.
    • Import both the header file and the packaged IP.
  3. Identify the header file (.hpp) and the IP meta-data file (component.xml) to use as inputs to build the C-Callable IP library.
  4. Show how to customize the IP.
  5. Indicate how the arguments of each function in the C-Callable IP library maps to the hardware IP.

The following tables guide you on how to complete the SDx dialogs.

  • The top row of each table contains the SDx IDE menu selection to begin each task.
  • The Dialog column lists the names of the subsequent dialog boxes that open.
  • Selection and Action columns indicate how to fill out the dialog boxes to complete the task.

SDx Library Project

Begin by launching the SDx IDE and specifying a workspace (for example, sdx_workspace) to create a Library project (File > New > SDx Library Project).

The project name becomes the name of the library created by concatenating the prefix lib and the .a suffix (lib<project_name>.a).

Table 1. SDx Library Project
Dialog Selection or Field Name Action
Project Type Application Click Next
Create a New SDx Project Project name: arraycopy
Use default location Check-mark
  Click Next
Accelerated Library Type Type: C-Callable Library
Platform Name zcu102
  Click Next
System Configuration System configuration: A53_Linux
Runtime: C/C++
Domain: a53_linux
(pre-set) CPU: cortex-a53
(pre-set) OS: linux
  Linux Root File System: Leave unchecked
    Click Next
Templates Empty Application Click Finish

Import Sources Dialog Options

The following table shows the menu selection for importing sources.

Table 2. Import Sources Dialog Options
Dialog Selection or Field Name Action
File system From directory: Browse to axis_arraycopy directory in samples/rtl
Click OK
Files: src/arraycopy.hpp Check-marked
Directory: ip Check-marked
Into folder: arraycopy/src
Click Finish

Add IP Customizations

In the IP Customizations window, click Add IP Customizations (file icon with stylized "h"). The following figure and table show how to add IP customization.

Table 3. Add IP Customizations
Dialog Selection or Field Name Action
Add IP Customizations Header File: Click Select
Files: Select arraycopy.hpp
Qualifier: Select src/src/arraycopy.hpp
Click OK
IP Path: Click Select
Files: Select component.xml
Qualifier: Select src/ip/component.xml
Click OK
Accelerator control: Protocol: ap_ctrl_hs
Port: s_axi_lite
Offset: 0
Primary Clock: ap_clk 10.0
Derived Clock: (no change)
IP Parameters (no change)
Click OK

Add Function Mapping

This axis_arraycopy example uses the contents of the provided samples/rtl/axis_arraycopy/src/Makefile to complete the dialog box option.

The component.xml and, if provided, the register_map.txt files associated with the IP block can also be queried for information on the how the function arguments map to the hardware.

In the IP Customizations window, click Add Function Mapping ("+" icon) shown in the figure and table.

Table 4. Add Function Mapping
Dialog Selection or Field Name Action
Add Function Mapping Function name: Click "+" icon on the right-side
Select arraycopy
Click OK
Arguments and Function Return mapped to AXILite Interface Click Add Function Argument Map ("+" icon) above the table for this interface type
Argument (Click within field to expose pull-down menu) M
AXILite Interface (Click within field to expose pull-down menu) s_axi_lite
Direction (Click within field to expose pull-down menu) IN
Register Info (Click within field to expose pull-down menu) M, at offset 16
Array Arguments mapped to AXIS Interface Click Add Function Argument Map ("+" icon) above the table for this interface type
Argument (Click within field to expose pull-down menu) A
AXIS Interface (Click within field to expose pull-down menu) A
Direction (Click within field to expose pull-down menu) IN
Array Arguments mapped to AXISinterface Click Add Function Argument Map ("+" icon) above the table for this interface type
Argument (Click within field to expose pull-down menu) B
AXIS Interface (Click within field to expose pull-down menu) B
Direction (Click within field to expose pull-down menu) OUT
Complete the Add Function Mapping dialog box Click OK

Building the C-Callable IP Project

To build the project, the C-Callable IP library is generated and placed in the build output directory of the application (for example, the Release directory) with the name lib<application>.a (libarraycopy.a for this example).

In addition to the SDx IDE method of creating a C-Callable IP library, a command line method that directly invokes the sdx_pack tool is available. The equivalent sdx_pack command to match the actions taken with the SDx IDE for the axis_arraycopy example is:

sdx_pack -header arraycopy.hpp -lib libarraycopy.a \
-func arraycopy -map A=A:in -map B=B:out -map M=s_axi_lite:in:16 -func-end \
-ip ../ip/component.xml -control ap_ctrl_hs=s_axi_lite:0 \
-primary-clk ap_clk=10.0 -target-family zynquplus \
-target-cpu cortex-a53 -target-os linux \
-verbose
Note: The C-Callable function and its argument map is listed between -func and -func-end options of the sdx_pack call.

Multi-Function Accelerator Libraries

The axis_arraycopy example is a library with a single accelerator function. Other examples, in particular the ones that begin with the mfa_ prefix are multi-function accelerator (MFA) libraries where more than one function is mapped onto one IP block. Below is the sdx_pack command for the mfa_scalar_128_none example that generates the libmfa.a.

The C-Callable IP library contains eight functions and is shown in the following code example. This accelerator library uses control protocol none, indicating that the user explicitly controls the IP. This MFA example also demonstrates 128-bit scalar function arguments that map to AXI4-Lite interfaces as well as 128-bit array arguments that map to master AXI4-Stream interfaces.

sdx_pack -header mfa.hpp -I inc -lib libmfa.a \
-func mfa_reset -map inst=s_axi_AXILiteS:in:0x40 -func-end \
-func mfa_init -map inA=inA:in -map inst=s_axi_AXILiteS:in:0x40 -func-end \
-func mfa_copy -map outB=outB:out -map inst=s_axi_AXILiteS:in:0x40 -func-end \
-func mfa_sum -map result=axi_AXILiteS:out:0x2c -map inst=s_axi_AXILiteS:in:0x40 -func-end \
-func mfa_status -map return=axi_AXILiteS:out:0x10 -map inst=s_axi_AXILiteS:in:0x40 -func-end \
-func mfa_status2 -map status=s_axi_AXILiteS:out:0x10 -map inst=s_axi_AXILiteS:in:0x40 -func-end \
-func mfa_result -map result=s_axi_AXILiteS:out:0x2c -func-end \
-func mfa_stop -map inst=s_axi_AXILiteS:in:0x40 -func-end \
-ip ../ip/component.xml -control none \
-add-ip-repo ../dummy_ip  \
-add-ip-repo ../dummy_ip_repo \
-primary-clk ap_clk=10.0 \
-target-family zynquplus -target-cpu cortex-a53 -target-os linux -verbose

Another example of an MFA type of C-Callable library is the mfa_scalar_axi accelerator. This accelerator library uses an ap_ctrl_hs control protocol and shows the use of scalar function arguments that map to AXI4-Lite interfaces as well as array arguments that map to master AXI4 interfaces.

sdx_pack -header mfa.hpp -I inc -lib libmfa.a \
	-func mfa_reset -map status=s_axi_AXILiteS:out:0x20 -map inst=s_axi_AXILiteS:in:0x34 -func-end \
	-func mfa_init -map inA=s_axi_AXILiteS:in:0x10,m_axi_inA:in -map status=s_axi_AXILiteS:out:0x20 \
          -map inst=s_axi_AXILiteS:in:0x34 -func-end \
	-func mfa_copy -map outB=s_axi_AXILiteS:in:0x18,m_axi_outB:out -map status=s_axi_AXILiteS:out:0x20 \
          -map inst=s_axi_AXILiteS:in:0x34 -func-end \
	-func mfa_sum -map result=s_axi_AXILiteS:out:0x28 -map status=s_axi_AXILiteS:out:0x20 \
          -map inst=s_axi_AXILiteS:in:0x34 -func-end \
	-func mfa_status -map status=s_axi_AXILiteS:out:0x20 -func-end \
	-func mfa_stop -map status=s_axi_AXILiteS:out:0x20 -map inst=s_axi_AXILiteS:in:0x34 -func-end \
	-ip ../ip/component.xml -control AXI=s_axi_AXILiteS:0x0 \
    -primary-clk ap_clk=10.0 \
	-target-family zynquplus -target-cpu cortex-a53 -target-os linux -verbose

A register map showing the bit-level definition of the AXI4-Lite control protocol signals used in the mfa_scalar_axi example is provided in the mfa_scalar_axi/ip/register_map.txt file and excerpted below. In general, IP register mapping information is provided by the IP developer.

// ==============================================================
// File generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2018.3
// Copyright (C) 1986-2018 Xilinx, Inc. All Rights Reserved.
// 
// ==============================================================

// AXILiteS
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of inA_offset
//        bit 31~0 - inA_offset[31:0] (Read/Write)
// 0x14 : reserved
// 0x18 : Data signal of outB_offset
//        bit 31~0 - outB_offset[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of status
//        bit 31~0 - status[31:0] (Read)
// 0x24 : Control signal of status
//        bit 0  - status_ap_vld (Read/COR)
//        others - reserved
// 0x28 : Data signal of result
//        bit 31~0 - result[31:0] (Read)
// 0x2c : Data signal of result
//        bit 31~0 - result[63:32] (Read)
// 0x30 : Control signal of result
//        bit 0  - result_ap_vld (Read/COR)
//        others - reserved
// 0x34 : Data signal of inst
//        bit 31~0 - inst[31:0] (Read/Write)
// 0x38 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)

The mfa_fir C-Callable IP library example highlights instantiating IP parameters, using AXI4-Stream interfaces, a 24-bit data type, and a control protocol selection of none.

 ibfir.a: fir.hpp
	sdx_pack -header fir.hpp -lib libfir.a \
	-func fir -map X=S_AXIS_DATA:in -map Y=M_AXIS_DATA:out -func-end \
	-func fir_reload -map H=S_AXIS_RELOAD:in -func-end \
	-func fir_config -map H=S_AXIS_CONFIG:in -func-end \
	-ip ../ip/fir_compiler_v7_2/component.xml -control none \
	-param DATA_Has_TLAST="Packet_Framing" \
	-param M_DATA_Has_TREADY="true" \
	-param Coefficient_Width="8" \
	-param Data_Width="8" \
	-param Quantization="Integer_Coefficients" \
	-param Output_Rounding_Mode="Full_Precision" \
	-param Coefficient_Reload="true" \
    -param Coefficient_Structure=Non_Symmetric \
    -primary-clk aclk_intf=10.0 \
	-target-family zynquplus -target-cpu cortex-a53 -target-os linux -verbose

Considerations for C-Callable IP Libraries

  1. Function arguments of TYPE *a or TYPE &a are interpreted as an OUTPUT scalar.
  2. Arrays must be declared as TYPE a[N] or TYPE a[].
  3. Function return type can only be a scalar in the format of TYPE: TYPE* or TYPE& are not allowed.
  4. C-Callable IP library header files cannot have SDS pragmas that use MACRO as parameters: #pragma sds data copy (A[0:SIZE]) is not allowed when SIZE is a macro (for example, #define SIZE 16).
  5. Overlapped function calls of multi-function accelerators (MFAs) are not allowed, as there is only one IP instance in the hardware; therefore, async pragmas around MFA functions are very risky and not recommended unless there is no chance of overlapping during runtime.
  6. Argument sizes must match between the software declared argument and the hardware port pair: <project_name>/Release/reports/sdx_pack.html report file can be used to double-check if the library implemented the expected argument sizes, offsets, and bus interfaces.
  7. This supports up to one AXI4-Lite interface per top-level IP.
  8. For C-Callable functions, all pragmas must be applied when building the library using sdx_pack. Any pragmas added into the header file after the library is already built are ignored by the tool.

sdx_pack Command

The following are examples of the sdx_pack command:

sdx_pack -header <header.h/pp>-ip <component.xml>
[-param <name>="value"] [configuration options]

The following table provides further details on the sdx_pack tool.

Table 5. sdx_pack Command Options
Option Description
-header header.h/.hpp

(required)

Header file (.h, .hpp) with function prototypes. sdx_pack generates C++ style library for a .hpp file and a C-style library for a .h file.

Only one top-level header file is allowed per library.

The top-level header can include other header files, using the -I option.
-ip component.xml

(required)

Path to Vivado packaged IP. Only one top-level IP per library. The top-level IP can invoke other IP blocks, using the -add-ip-repo option.
-control protocol [=port [:offset]] (required) IP control protocol options:
  • ap_ctrl_hs

    Automatic control protocol based on an AXI4-Lite control register, typically at offset 0x0 (for example: -control ap_ctrl_hs=s_axi_AXILiteS:0)

  • none

    User application explicitly controls the IP (for example -control none)

-func function_name -map swName= hwNAME:direction[:offset[aximm_name:direction]] -func-end (required)

Specify a list of C-Callable IP functions. Each function is listed between a -func and -func-end option pair. Function arguments are mapped to each IP port with the -map option.

The -map option is then used as follows:

  • Scalars map onto an AXI4-Lite interface
    • Map input scalar (for example, int a) with -map a=s_axi_AXILiteS:in:offset
    • Map output scalar (for example: int *a, or int &a) with -map a=s_axi_AXILiteS:out:offset
    • Map function return scalar (return type can only be a scalar) with -map return=s_axi_AXILiteS:out:offset
  • Arrays map onto AXI4, AXI4-Stream, or AXI4-Lite interfaces. The arrays must be one-dimensional (for example, int a[N], or int a[]).
    • Map to AXI4, with -map a=s_axiAXILiteS:in:offset,a_hwName:direction. Not allowed when control=none.

      The first part is mapping to the address and the second part is mapping to the data port.

    • Map to AXI4-Stream with -map a=a_hwName:direction.
    • Map to AXI4-Lite with -map a=s_axi_AXILiteS:in:offset. Array must be one-dimensional and of constant size.
-param name="value" IP parameter name-value pairs to instantiate IP parameters. Use one -param option per pair.
-lib libname Use specified libname for naming the generated library. By default lib header.a is used.
-I path If the file named with the -header option includes other files, this option specifies the path to the additionally included files. Multiple -I options can be used. For easier library distribution, place all include files into a single directory.
-add-ip-repo path Add all IP found in the listed repository into the library. Although multiple -add-ip-repo options can be used to specify multiple paths.

Xilinx recommends to place all required IP into a single directory and use a single -add-ip-repo option.

-primary-clk clk_interface=min_clk_period Specify the primary clock interface and its minimum clock period in nanoseconds.
-derived-clk clk_interface=multiplier:divisor Specify a phase-aligned derived clock interface and its multiplier and divisor in units of integers. Only two phase-aligned clocks are supported.
-target-family device_family The target device family supported by the IP (for example, zynq (default), zynquplus).
-target-cpu cpu_type Specify target CPU:
  • cortex-a9 (default)
  • cortex-a53
  • cortex-r5
  • microblaze
-target-os name Specify target OS:
  • linux (default)
  • standalone (bare-metal)
-query-target type Query one of: supported device families, cpu types, or OS type for the IP [family, cpu, os]
-query-interface type Query interfaces and parameters of the IP. Multiple query types supported [all, aximm, axilite, axis, clock, control, param, misc]
Note: This requires that the IP has packaged all necessary information needed by the query.
-o output.json User-specified JSON file to save query results.
-verbose Print verbose output to STDOUT.
-version Print the sdx_pack version information to STDOUT.
-h, -help, --help Display sdx_pack option usage and descriptions.

Here is an example of the code:

sdx_pack -header arraycopy.hpp -lib libarraycopy.a \
-func arraycopy -map A=A:in -map B=B:out \
-map M=s_axi_lite:in:16 -func-end \
-ip ../ip/component.xml -control AXI=s_axi_lite:0 \
-target-family zynquplus -target-cpu cortex-a53 -target-os standalone \
-verbose

Where:

  • arraycopy.hpp specifies the header file defining the function prototype for the arraycopy function.
  • component.xml of the IP generates the packaged Vivado IP for SDx.
  • -control specifies the IP control protocol.
  • –map specifies the mapping of an argument from the software function to a port on the Vivado IP. Notice the option is used three times in the example above to map function arguments A, B, and M to IP ports.
  • The –target-os option specifies the target operating system.

The sdx_pack utility generates a C-Callable IP library to match the name in the -lib option, libarraycopy.a in this case.

Using C-Callable IP Libraries

After generating the C-Callable library, create a new SDx Application project to use the library. Continuing with the example of the axis_arraycopy C-Callable IP library built in the previous section, use the generated library (libarraycopy.a) from the library build's Release directory. The result of building the Application project is an executable file (ELF) that is linked with the C-Callable IP Library.

  1. Create an SDx application project to output an executable file. Click File > New > SDx Application Project.
    Note: The tuple consisting of device_family, cpu_type, and os_type must match that of the C-Callable IP library.
    Table 6. SDx Application Project
    Dialog Box Selection or Field Name Action
    Project Type Application Click Next
    Create a New SDx Project Project name: app_arraycopy
      Use default location Check-marked
        Click Next
    Platform Name zcu102
        Click Next
    System Configuration System configuration: A53_Linux
      Runtime: C/C++
      Domain: a53_linux
    (pre-set) CPU: cortex-a53
    (pre-set) OS: linux
      Linux Root File System: Unchecked
        Click Next
    Templates Empty Application Click Finish
  2. Import the function declarations header file (.hpp) common to both the library and the application. In the Project Explorer window, right-click app_arraycopy and select Import Sources.
    Table 7. Select Import Sources
    Dialog Selection or Field Name Action
    File system From directory: Browse to axis_arraycopy/src directory in <SDx_Install_Dir>/samples/rtl.
        Click OK
      Files: arraycopy.hpp Check-marked
      Into folder: app_arraycopy/src
        Click Finish
  3. Open the Importing Sources dialog box again to get the example main application code from the <SDx_Install_Dir>/samples/rtl directory. In the Project Explorer window, right-click app_arraycopy and select Import Sources.
    Table 8. Select Import Sources
    Dialog Selection or Field Name Action
    File system From directory: Browse to the axis_arraycopy/app directory in <SDx_Install_Dir>/samples/rtl
        Click OK
      Files: main.cpp Check-marked
      Into folder: app_arraycopy/src
        Click Finish
    Now that the source files for the app_arraycopy application have been imported, update the C/C++ Build Settings to have the sds++ linker use the arraycopy.a C-Callable IP library when building the application.
  4. Update the C/C++ Build Settings in the Project Explorer window, right-click app_arraycopy and select C/C++ Build Settings as shown in the table.
    Table 9. C/C++ Build Settings
    Dialog Selection or Field Name Action
    Settings > Tool Settings SDS++ Linker Select Libraries
        Click Add symbol (with "+" icon) in the Libraries (-l) window
      Libraries(-l) arraycopy
        Click OK
        Click Add symbol (with "+" icon) in the Library search path (-L) window
        Click Workspace
      Folder: Navigate to and select arraycopy/Release
        Click OK
    (pre-set) Directory: ${workspace_loc:/arraycopy/Release}
        Click OK
        Click Apply and Close

To create the application, you can use the Assistant window to build the app_arraycopy application.

  1. In the Assistant window under app_arraycopy[SDSoC], right-click Debug[Hardware] and select Build. The Console window shows the build progression including the sds++ system compiler invocation.
  2. After the application successfully builds the target executable file (app_arraycopy.elf), the Assistant window populates with a Data Motion Network Report, the Compilation Log, and an SD Card Image menu. Through the SD Card Image menu, the contents of the generated (sd_card) files directory is available to view using the Project Explorer, a file browser, or a command shell window.
  3. When the build completes, you can write the contents of the generated sd_card directory to the root of a FAT32-formatted SD card and boot and run the app_arraycopy.elf application on a ZCU102 board. The sd_card directory includes a README.txt for boot setup instructions, a bootable BOOT.BIN file, and the image.ub file used to boot Linux.

The SDx IDE builds the application with the sds++ system compiler using the C-callable library and the application code. The main application is compiled to produce an object file and then it is linked with the C-callable library (arraycopy).

The following examples show the issued commands.

Compilation of main.cpp:

sds++ -Wall -O0 -g -I../src -c -fmessage-length=0 -MTsrc/main.o -MMD -MP -MFsrc/main.d \
 -MTsrc/main.o -o src/main.o ../src/main.cpp \
 -sds-sys-config a53_linux -sds-proc a53_linux -sds-pf zcu102

Linking main.o with arraycopy library to produce executable application app_arraycopy.elf:

sds++ -L<path_to_arraycopy/Release> --remote_ip_cache ../ip_cache \
 -o app_arraycopy.elf ./src/main.o -larraycopy -dmclkid 1 \
 -sds-sys-config a53_linux -sds-proc a53_linux -sds-pf zcu102