Exporting a Library for GCC

This chapter demonstrates how to use the sds++ compiler to build a library of software functions with entry points into hardware functions implemented in programmable logic. This library can be linked into applications using the standard GCC linker for Zynq®-7000 SoC and Zynq® UltraScale+™ MPSoC devices.

In addition to the library, sds++ generates a complete boot image for the hardware functions and data motion network, including an FPGA bitstream. Using this library, you can develop software applications that call the hardware functions (and fixed hardware) using standard GCC toolchains. You are still targeting the same hardware system and using the sds++-generated boot environment, but you are free to develop your software application using the GNU toolchain in the software development environment of your choice.

IMPORTANT: In the current SDSoC™ release, libraries are not thread-safe, so they must be called into from a single thread within an application, which could consist of many threads and processes.

Building a Shared Library

TIP: Shared libraries can be only created for application projects targeting Linux OS.

To build a shared library, sds++ requires at least one accelerator. The following example provides three entry points into two hardware accelerators: a matrix multiplier and a matrix adder. You can find these files in the samples/libmatrix/build directory.

  • mmult_accel.cpp: Accelerator code for the matrix multiplier
  • mmult_accel.h: Header file for the matrix multiplier
  • madd_accel.cpp: Accelerator code for the matrix adder
  • madd_accel.h: Header file for the matrix adder
  • matrix.cpp: Code that calls the accelerators and determines the data motion network
  • matrix.h: Header file for the library

The matrix.cpp file contains functions that define the accelerator interfaces as well as how the hardware functions communicate with the platform (that is, the data motion networks between platform and accelerators). The function madd calls a single matrix adder accelerator, and the function mmult calls a single matrix multiplier accelerator. Another function mmultadd is implemented using two hardware functions, with the output of the matrix multiplier connected directly to the input of the matrix adder.

/* matrix.cpp */
#include "madd_accel.h"
#include "mmult_accel.h"
    
void madd(float in_A[MSIZE*MSIZE], float in_B[MSIZE*MSIZE], float out_C[MSIZE*MSIZE])                                             
{
  madd_accel(in_A, in_B, out_C);
}
    
void mmult(float in_A[MSIZE*MSIZE], float in_B[MSIZE*MSIZE], float out_C[MSIZE*MSIZE])                                            
{
  mmult_accel(in_A, in_B, out_C);
}
    
void mmultadd(float in_A[MSIZE*MSIZE], float in_B[MSIZE*MSIZE], float in_C[MSIZE*MSIZE], 
float out_D[MSIZE*MSIZE])
{
  float tmp[MSIZE * MSIZE];
    
  mmult_accel(in_A, in_B, tmp);
  madd_accel(tmp, in_C, out_D);
}

The matrix.h file defines the function interfaces to the shared library and is included in the application source code.

/* matrix.h */
#ifndef MATRIX_H_
#define MATRIX_H_
   
#define MSIZE 16
    
void madd(float in_A[MSIZE*MSIZE], float in_B[MSIZE*MSIZE], float out_C[MSIZE*MSIZE]);
  
void mmult(float in_A[MSIZE*MSIZE], float in_B[MSIZE*MSIZE], float out_C[MSIZE*MSIZE]);
    
void mmultadd(float in_A[MSIZE*MSIZE], float in_B[MSIZE*MSIZE], float in_C[MSIZE*MSIZE], 
float out_D[MSIZE*MSIZE]);
    
#endif /* MATRIX_H_ */

In the samples/libmatrix/build/shared folder, the Makefile shows how the project is built by specifying that the functions mmult_accel, madd, and mmult_add must be implemented in programmable logic.

SDSFLAGS = \
    -sds-pf ${PLATFORM} \
    -sds-hw mmult_accel mmult_accel.cpp -sds-end \
    -sds-hw madd_accel madd_accel.cpp -sds-end

Object files are generated with position independent code (PIC), like standard shared libraries, using the GCC standard -fpic option:

sds++ ${SDSFLAGS} -c -fpic –o mmult_accel.o mmult_accel.cpp
    sds++ ${SDSFLAGS} -c -fpic –o madd_accel.o madd_accel.cpp
    sds++ ${SDSFLAGS} -c -fpic –o matrix.o matrix.cpp

Link the objects files by using the using the GCC standard –shared switch:

sds++ ${SDSFLAGS} -shared -o libmatrix.so mmult_accel.o madd_accel.o matrix.o

After building the project, the following files are generated:

  • libmatrix.so: Shared library suitable for linking using GCC and for runtime use
  • sd_card: Directory containing an SD card image for booting the board

Delivering a Library

The following structure allows compiling and linking into applications using GCC in standard ways.

<path_to_library>/include/matrix.h
<path_to_library>/lib/libmatrix.so
<path_to_library>/sd_card
IMPORTANT: The sd_card folder is to be copied into an SD card and used to boot the board. This image includes a copy of the libmatrix.so file that is used at runtime.

Compiling and Linking Against a Library

The following is an example of using the library with a GCC compiler. The library is used by including the header file matrix.h and then calling the necessary library functions.

/* main.cpp (pseudocode) */
#include "matrix.h"
        
int main(int argc, char* argv[])
{
  float *A, *B, *C, *D;
  float *J, *K, *L;
  float *X, *Y, *Z;
  ...
  mmultadd(A, B, C, D);
  ...
  mmult(J, K, L);
  ...
  madd(X, Y, Z);
  ...
}

To compile against a library, the compiler needs the header file. The path to the header file is specified using the -I switch. You can find example files in the samples/libmatrix/use directory.

TIP: For explanation purposes, the code above is only pseudocode and not the same as the main.cpp file in the directory. The file has more code that allows full compilation and execution.
gcc –I <path_to_library>/include –o main.o main.c

To link against the library, the linker needs the library. The path to the library is specified using the -L switch. Also, ask the linker to link against the library using the -l switch.

gcc –I <path_to_library>/lib –o main.elf main.o -lmatrix

For detailed information on using the GCC compiler and linker switches see the GCC documentation.

Use a Library at Runtime

At runtime, the loader looks for the shared library when loading the executable. After booting the board into a Linux prompt and before executing the ELF file, add the path to the library to the LD_LIBRARY_PATH environment variable. The sd_card created when building the library already has the library, so the path to the mount point for the sd_card must be specified.

For example, if the sd_card is mounted at /mnt, use this command:

export LD_LIBRARY_PATH=/mnt

Exporting a Shared Library

The following steps demonstrate how to export an SDSoC environment shared library with the corresponding SD card boot image using the SDSoC environment GUI.

  1. Select File > New > SDx Library Project to bring up the New SDx Library Project dialog box.
  2. Create a new SDx Library project.
    1. Type libmatrix in the Project name field.
    2. Specify the Location.
    3. Click Next.
    4. Check the Shared Library checkbox in the Accelerated Library Type window.
    5. Click Next.
    6. Select Platform to be zc102.
    7. Click Next.
  3. Provide the system configuration and software details of your project.
    1. Accept the default values in the System Configuration dialog box.
      Note: Ensure that Sysroot path is unchecked.
    2. Click Next.
  4. In the Templates dialog box, select Matrix Shared Library from the Available Templates, and click Finish to create the project.
  5. In the Project Explorer, right-click libmatrix and select Import Sources.
  6. In the Import Source dialog box, select Browse and samples/libmatrix and select build and click OK.
  7. In the Import Sources dialog box, check build and click Finish.

    A new SDSoC shared library application project called libmatrix is created in the Project Explorer view. The project includes two hardware functions mmult_accel and madd_accel that are visible in the SDSoC Project Overview.

  8. Build the library.
    1. In the Project Explorer view, select the libmatrix project.
    2. Select Project > Build Project.

      After the build completes, there is a boot SD card image under the Debug (or current configuration) folder.