SDSoC Platform Examples

Introduction

This appendix provides simple examples of SDSoC™ platforms created from a working hardware system built using the Vivado® Design Suite, with a software run-time environment, including operating system kernel, boot loaders, file system, and libraries that run on top of the hardware system. Each example demonstrates a commonly used platform feature, and is built upon the ZC702 board available from Xilinx.

  • zc702_axis_io - Accessing a data stream that could represent direct I/O from FPGA pins in an SDSoC platform
  • zc702_acp - Sharing a processing system AXI bus interface between the platform and the sdscc system compiler
Each example is structured with the following information:
  • Description of the platform and what it demonstrates.
  • Instructions to generate the SDSoC hardware platform meta-data file.
  • Instructions to create platform software libraries, if required.
  • Description of the SDSoC software platform meta-data file.
  • Basic platform testing.

In addition to these platform examples, it would be worthwhile to inspect the standard SDSoC platforms that are included in the SDx™ IDE in the <sdx_root>/platforms directory.

MicroBlaze Hardware Requirements

In addition to targeting Zynq and Zynq® UltraScale+™ MPSoC devices, you can build an SDSoC platform that targets any Xilinx® device by using the MicroBlaze processor as the target CPU (add reference to MicroBlaze™ documentation). A MicroBlaze platform in SDSoC must be a self-contained system containing an LMB memory, MicroBlaze Debug Module (MDM), UART, and AXI Timer built using the Vivado Design Suite and SDK. A sample MicroBlaze example is in <sdx_root>/sample/platforms/arty folder. The figure below shows a minimal system. Notice that the JTAG UART is enabled in the MDM and appears as an AXI-Lite slave. The system runs on a clock (sys) delivered from the board.

Figure: Minimal MicroBlaze System

The SDSoC runtime requires the platform hardware to include two IPs: a timer for the sds_clock_counter() API, and a UART to print runtime error messages. Since a MicroBlaze processor has a single AXI Master port (M_AXI_DP) to control AXI slaves, a MicroBlaze platform must include an AXI interconnect connected to this port as described in Example: Sharing a Platform IP AXI Port.

MicroBlaze systems that use DDR typically connect the processor to the DDR via MIG using the cache facilities built into the MicroBlaze. An example system is shown below.

Figure: MicroBlaze System with MIG

In the system above, the MicroBlaze is configured with an 8 KB cache. The instruction and data cache ports (M_AXI_DC and M_AXI_IC) are connected to the MIG. The UART is connected to an on-board USB-to-UART converter chip and does not use the JTAG UART from the previous design. The system runs on the user-interface (UI) clock produced by MIG. Other than these differences, the MicroBlaze systems are identical.

Ports on the MIG’s AXI Interconnect IP can be registered using PFM properties as described in Declaring AXI Ports for the type M_AXI_HP which is exactly the same as Zynq HP Ports. SDSoC runtime will invalidate or flush buffers in exactly the same way as it does for Zynq devices.

Example: Direct I/O in an SDSoC Platform

An SDSoC platform can include input and output subsystems; for example, analog-to-digital and digital-to-analog converters, or video I/O, by converting raw physical data streams into AXI4-Stream interfaces that are exported as part of the platform interface specification. For information on the zc702_axis_io sample platform, see “Using External I/O” in the SDSoC Environment User Guide. This example includes sample applications that demonstrate how an input data stream can be written directly into memory buffers without data loss, and how an application can "packetize" the data stream at the AXI transport level to communicate with other functions (including, but not limited to DMAs) that require packet framing.

Note: The source code for this platform can be found in <sdx_root>/samples/platforms/zc702_axis_io/src.

Run the following command from the command shell using zc702_axis_io_dsa.tcl, a Vivado Tcl script to build the hardware platform in a batch mode:

vivado –mode batch –source zc702_axis_io_dsa.tcl

Run the following command to build the platform in GUI mode to inspect the hardware system in the Vivado IP integrator:

vivado –mode gui –source zc702_axis_io_dsa.tcl

The command opens the Vivado IDE and builds the platform. The resulting hardware system will look similar to the following block diagram.

Figure: zc702_axis_io Block Diagram

To make this design portable, this platform includes a free-running binary counter that generates a continuous stream of data samples at 50 MHz, which acts as a proxy for data streaming directly from FPGA pins. To convert this input data stream into an AXI4-Stream for SDSoC applications, the platform connects the counter output to the s_axis_tdata slave port of an AXI4-Stream data FIFO, with a constant block providing the required s_axis_tvalid signal, always one. The data FIFO IP is configured to store up to 1024 samples with an output clock of 100 MHz to provide system elasticity so that the consumer of the stream can process the stream "bubble-free" (i.e., without dropping data samples). In a real platform, the means for converting to an AXI4-Stream, relative clocking and amount of hardware buffering will vary according to system requirements.

Similar to input streaming off of an analog-to-digital converter, this data stream is not packetized, which means the AXI4-Stream has no TLAST signal. Consequently, any SDSoC application that consumes the data stream must be capable of handling unpacketized streams. Within the SDSoC environment, every data mover IP core (e.g., the Vivado AXI4 Direct Memory Access IP (AXI DMA)) requires packetized AXI4-Streams that include the TLAST signal. To consume the streaming input from this platform, an application must employ direct hardware connections to the AXI4-Stream port.

TIP: A platform can also export an AXI4-Stream port that includes the TLAST signal, in which case SDSoC applications do not require direct connections to the port.

Declaring the SDSoC Hardware Platform Interface

As described in Creating the Platform Hardware Component, the hardware platform port interface is defined by setting PFM properties on cells and ports within a Vivado IP integrator block diagram.

In the zc702_axis_io_dsa.tcl script, this occurs in lines 45-67. Use the following steps to set the properties on cells and ports within a Vivado IP integrator block diagram.

  1. Declare the platform name as an IP-XACT VLNV (vendor:library:name:version) string using the following commands:
    set_property PFM_NAME \
    "xilinx.com:zc702_axis_io:zc702_axis_io:1.0" \
    [get_files ./zc702_axis_io_vivado/zc702_axis_io.srcs/\ 
    sources_1/bd/zc702_axis_io/zc702_axis_io.bd]
    
  2. Declare a platform clock with id 1 using the following commands:
    set_property PFM.CLOCK { \
    clk_out2 {id "1" is_default "true" proc_sys_reset "psr_1" } \
    	} [get_bd_cells /clk_wiz_0]
    

    Note that every clock must have an associated proc_sys_reset that provides synchronized reset signals for blocks using this clock.

  3. At least, one general purpose AXI master and one AXI slave port must be declared. Use the following command to declare the platform AXI interfaces, each with an associative list containing several attributes.
    set_property PFM.AXI_PORT { \
    M_AXI_GP0 {memport "M_AXI_GP"} \
    M_AXI_GP1 {memport "M_AXI_GP"} \
    S_AXI_ACP {memport "S_AXI_ACP" sptag "ACP" 
    memory "ps7 ACP_DDR_LOWOCM"} \
    S_AXI_HP0 {memport "S_AXI_HP" sptag "HP0" 
    memory ps7 HP0_DDR_LOWOCM"} \
    S_AXI_HP1 {memport "S_AXI_HP" sptag "HP1" 
    memory ps7 HP1_DDR_LOWOCM"} \
    S_AXI_HP2 {memport "S_AXI_HP" sptag "HP2" 
    memory ps7 HP2_DDR_LOWOCM"} \
    S_AXI_HP3 {memport "S_AXI_HP" sptag "HP3" 
    memory ps7 HP3_DDR_LOWOCM"} \
    } [get_bd_cells /ps7]

    Each AXI port requires a memport memory type declaration, which must be one of the following:

    1. M_AXI_GP – a general purpose master
    2. S_AXI_ACP – a cache coherent slave
    3. S_AXI_HP – a high performance, non-cache coherent slave
    4. S_AXI_HPC – a high performance slave (Zynq UltraScale+ MPSoC only)
    5. MIG – a slave on an external DDR (MIG) memory controller IP

      An AXI slave port requires an sptag that provides a symbolic tag to represent the port, and two additional memory attributes.

    6. Memory instance: The cell name of the block in the IP integrator address editor
    7. Address segment: The ‘Base Name’ associated with the port as seen in the Vivado IP integrator address editor
  4. Use the following command to declare the stream_fifo master AXI4-Stream port.
    set_property PFM.AXIS_PORT { \
    	M_AXIS {type "M_AXIS"} \
    	} [get_bd_cells /stream_fifo]
    
  5. Use the following command to declare the interrupt inputs by constructing a list of port names on a Concat block that is connected to the interrupt port on the processing_system7 block:
    set intVar []
    for {set i 0} {$i < 16} {incr i} {
    	lappend intVar In$i {}
    }
    set_property PFM.IRQ $intVar [get_bd_cells /xlconcat_0]
    
  6. After declaring the port interface, use the following command to generate the output products required to create the DSA from the block diagram:
    generate_target all \
    [get_files ./zc702_axis_io_vivado/zc702_axis_io.srcs/\
    sources_1/bd/zc702_axis_io/zc702_axis_io.bd]
    
  7. Use the following command to generate the DSA:
    write_dsa -force ./zc702_axis_io.dsa

Making the SDSoC Platform from the Command Line

As described in Software Platform Data Creation, the following platform components provide the application run time context:
  • Bootloaders
  • Operating system
  • File system

The <sdx_root>/samples/platforms/zc702_axis_io/src/zc702_axis_io_pfm.tcl file is a a tcl script that builds the SDSoC platform by incorporating the DSA hardware and software components that were built using PetaLinux and SDx (SDK style first-stage boot loader project).

Run the following command from an SDSoC command shell to build the platform in batch mode using zc702_axis_io_pfm.tcl SDx tcl script which is executed by the xsct utility provided as part of SDx.:

xsct –sdx  ./zc702_axis_io_dsa.tcl
  1. Use the following command to create a platform object in the xsct command line interpreter:
    platform -name zc702_axis_io \
    -desc "Zynq ZC702 Board with direct I/O" \
    -hw ./zc702_axis_io.dsa -out ./output \
    -prebuilt  -samples samples
    
  2. Use the following command to create a new system configuration for Linux applications:
    system -name linux -display-name "Linux"  \
    -boot ./boot  -readme ./generic.readme
    
  3. Use the following command to define a processor group or domain for this system configuration:
    domain -name linux -proc ps7_cortexa9_0 \
    -os linux -image ./linux/image
    
  4. Use the following command to register boot files for the Linux system configuration:
    boot -bif ./linux/linux.bif
  5. Use the following command to register QEMU arguments and a directory containing boot files to support SDSoC emulation:
    domain -qemu-args ./qemu/lnx/qemu_args.txt
    domain -qemu-data ./boot
    
  6. Use the following command to create and populate a standalone ('bare metal') system configuration:
    system -name standalone  -display-name "Standalone" -boot ./boot  -readme ./generic.readme
    domain -name standalone -proc ps7_cortexa9_0 -os standalone
    app -lscript ./standalone/lscript.ld
    boot -bif ./standalone/standalone.bif
    domain -qemu-args ./qemu/std/qemu_args.txt
    domain -qemu-data ./boot
    
  7. Use the following command to create the SDSoC platform:
    platform –generate

    This script creates an SDK Platform project called zc702_axis_io in the following directory:

    output/zc702_axis_io/export/

Platform Sample Designs

An SDSoC platform can include sample applications that demonstrate its use, as described in Sample Applications. The SDx IDE looks for a file called samples/<example>/description.json (template.xml) for information on the sample application within a platform. The template.xml file for the zc702_axis_io platform lists several test applications, each of which is of specific interest.
<template location="aximm" name="Unpacketized AXI4-Stream to DDR" 
              description="Shows how to copy unpacketized AXI4-Stream data directly to DDR.">
        <supports>
            <and>
                <or>
                    <os name="Linux"/>
                    <os name="Standalone"/>
                </or>
            </and>
        </supports>
        <accelerator name="s2mm_data_copy" location="main.cpp"/>
    </template>
	<template location="stream" name="Packetize an AXI4-Stream" 
                  description="Shows how to packetize an unpacketized AXI4-Stream.">
        <supports>
            <and>
                <or>
                    <os name="Linux"/>
		    <os name="Standalone"/>
                </or>
            </and>
        </supports>
        <accelerator name="packetize" location="packetize.cpp"/>
        <accelerator name="minmax" location="minmax.cpp"/>
    </template>
    <template location="pull_packet" name="Lossless data capture from AXI4-Stream to DDR" 
              description="Illustrates a technique to enable lossless data capture from a free-running input source.">
        <supports>
            <and>
                <or>
                    <os name="Linux"/>
                    <os name="Standalone"/>
                </or>
            </and>
        </supports>
        <accelerator name="PullPacket" location="main.cpp"/>
    </template>

To use a platform in the SDx IDE, you must add it to the platform repository for the Eclipse workspace as described in the following steps.

  1. Launch Xilinx SDx and provide a path to your workspace such as <path_to_tutorial>/myplatforms/.
  2. Create a new project by selecting File > New > Xilinx SDx Project.
  3. Specify the type of project as an Application Project, and click Next.
  4. Specify a project name in the Create New SDx Project page such as my_zc702_axis_io, and click Next.
  5. In the Choose Hardware Platform page click Add Custom Platform.

    Figure: Add Custom Platform

  6. Navigate to the folder containing the platform <sdx_root>/samples/platforms/zc702_axis_io.
  7. The platform will show up in the Choose Hardware Platform Page. Select zc702_axis_io (custom) and click Next.

    Figure: Choose Hardware Platform

  8. On the System Configuration page, keep the default Linux for System Configuration and click Next.
  9. On the Templates page, select Unpacketized AXI4-Stream to DDR to test the platform with one of the sample applications, and click Finish.

    The s2mm_data_copy function is pre-selected for hardware acceleration. The program data flow within s2mm_data_copy_wrapper creates a direct signal path from the platform input to a hardware function called s2mm_data_copy that then pushes the data to memory as a zero_copy datamover. That is, the s2mm_data_copy function acts as a custom DMA. The main program allocates four buffers, invokes s2mm_data_copy_wrapper, and then checks the written buffers to ensure that data values are sequential, i.e., the data is written bubble-free. For simplicity, this program does not reset the counter, so the initial value depends upon how much time elapses between board power-up and invoking the program.

  10. Open up main.cpp. Key points to observe are:
    • Buffers are allocated using sds_alloc to guarantee physically contiguous allocation required for the zero_copy datamover.
      unsigned *bufs[NUM_BUFFERS];
      unsigned* rbuf0;
      for(int i=0; i<NUM_BUFFERS; i++) {
          bufs[i] = (unsigned*) sds_alloc(BUF_SIZE *
       sizeof(unsigned));
      }
      // Flush the platform FIFO of start-up garbage
      s2mm_data_copy(rbuf0, bufs[0]);
      s2mm_data_copy(rbuf0, bufs[0]);
      s2mm_data_copy(rbuf0, bufs[0]);
      for(int i=0; i<NUM_BUFFERS; i++) {
        s2mm_data_copy(rbuf0, bufs[i]);
      }
      
    • Specify the connectivity between hardware function and the platform using the sys_port pragma.
      // s2mm "DMA" accelerator
      #pragma SDS data sys_port (fifo:stream_fifo_M_AXIS)
      #pragma SDS data zero_copy(buf)
      int s2mm_data_copy(unsigned *fifo, unsigned buf[BUF_SIZE]) 
      {
      #pragma HLS interface axis port=fifo
           for(int i=0; i<BUF_SIZE; i++) {
      #pragma HLS pipeline
                buf[i] = *fifo;
           }
           return 0;
      }
  11. Build the application by clicking on the Build icon in the toolbar. When the build completes, the Debug folder contains an sd_card folder with the boot image and application ELF.
  12. After the build finishes, copy the contents of the sd_card directory onto an SD card, boot, and run my_zc702_axis_io.elf.
sh-4.3# cd /mnt
sh-4.3# ./my_zc702_axis_io.elf
TEST PASSED!
sh-4.3#

Example: Sharing a Platform IP AXI Port

To share an AXI master (slave) interface between a platform IP and the accelerator and data motion IPs generated by the SDSoC compilers, employ the SDSoC Tcl API to declare the first unused AXI master (slave) port (in index order) on the AXI interconnect IP block connected to the shared interface. Your platform must use each of the lower indexed masters (slaves) on this AXI interconnect.

SDSoC Platform Hardware Interface

Use the following steps to build the SDSoC hardware platform interface within a Vivado IDE:
Note: The source code for this platform is available in <sdx_root>/samples/platforms/zc702_acp/src file.
  1. Run the following command from the command shell using zc702_acp_dsa.tcl, a Vivado tcl script to build the hardware platform in a batch mode:
    vivado –mode batch –source zc702_acp_dsa.tcl

    You can also build the platform in GUI mode to inspect the hardware system in Vivado IP integrator. Run the following command to build the platform in GUI mode:

    vivado –mode gui –source zc702_acp_dsa.tcl

    This command will open the Vivado IDE and build the platform. The resulting hardware system will look similar to the following block diagram.

    Figure: zc702_acp Block Design

  2. Use the following commands to declare the platform name as an IP-XACT VLNV (vendor:library:name:version) string:
    set_property PFM_NAME \
    "xilinx.com:zc702_acp:zc702_acp:1.0" \
    [get_files ./zc702_acp_vivado/zc702_acp.srcs/\ 
    sources_1/bd/zc702_acp/zc702_acp.bd]
    
  3. Use the following command to declare a platform clock with id 1:
    set_property PFM.CLOCK { \
      clk_out1 {id "2" is_default "true" proc_sys_reset "psr_0" } \
      clk_out2 {id "1" is_default "false" proc_sys_reset "psr_1" } \
      clk_out3 {id "0" is_default "false" proc_sys_reset "psr_2" } \
      clk_out4 {id "3" is_default "false" proc_sys_reset "psr_3" } \
    } [get_bd_cells /clk_wiz_0]
    

    Note that every clock must have an associated proc_sys_reset that provides synchronized reset signals for blocks using this clock.

  4. At least one general purpose AXI master and one AXI slave port must be declared. Use the following command to declare the platform AXI interfaces from the processing system IP, each with an associative list containing several attributes:
    set_property PFM.AXI_PORT { \
    M_AXI_GP1 {memport "M_AXI_GP"} \
    S_AXI_HP0 {memport "S_AXI_HP" sptag "HP0" 
    memory ps7 HP0_DDR_LOWOCM"} \
    S_AXI_HP1 {memport "S_AXI_HP" sptag "HP1" 
    memory ps7 HP1_DDR_LOWOCM"} \
    S_AXI_HP2 {memport "S_AXI_HP" sptag "HP2" 
    memory ps7 HP2_DDR_LOWOCM"} \
    S_AXI_HP3 {memport "S_AXI_HP" sptag "HP3" 
    memory ps7 HP3_DDR_LOWOCM"} \
    } [get_bd_cells /ps7]
    

    Each AXI port requires a memport memory type declaration, which must be one of the following:

    • M_AXI_GP – a general purpose master
    • S_AXI_ACP – a cache coherent slave
    • S_AXI_HP – a high performance, non-cache coherent slave
    • S_AXI_HPC – a high performance slave (Zynq UltraScale+ MPSoC only)
    • MIG – a slave on an external DDR (MIG) memory controller IP

      An AXI slave port requires an sptag that provides a symbolic tag to represent the port, and two additional memory attributes.

    • Memory instance – the cell name of the block in the IP integrator address editor
    • Address segment – the ‘Base Name’ associated with the port as seen in the Vivado IP integrator address editor
  5. The platform uses both the S_AXI_ACP and M_AXI_GP0 ports on the processing system. Use the following Tcl code to declare additional ports on the axi_interconnect IPs within the platform:
    set gpMasters []
    for {set i 1} {$i < 64} {incr i} {
      lappend gpMasters M[format %02d $i]_AXI {memport "M_AXI_GP"}
    }
    set_property PFM.AXI_PORT $gpMasters \
    [get_bd_cells /axi_ic_ps7_M_AXI_GP0]
    
    set acpSlaves []
    for {set i 1} {$i < 8} {incr i} {
      lappend acpSlaves S[format %02d $i]_AXI {memport "S_AXI_ACP" \ 		sptag "ACP" memory "ps_ACP_DDR_LOWOCM"}
    }
    set_property PFM.AXI_PORT $acpSlaves \
    [get_bd_cells /axi_ic_ps7_S_AXI_ACP]
    

    Note that the memport attribute is inherited from the processing system port connected to the axi_interconnect.

  6. Use the following command declares the interrupt inputs by constructing a list of port names on a Concat block that is connected to the interrupt port on the processing_system7 block:
    set intVar []
    for {set i 0} {$i < 16} {incr i} {
    	lappend intVar In$i {}
    }
    set_property PFM.IRQ $intVar [get_bd_cells /xlconcat_0]
    
  7. After declaring the port interface, use the following command to generate the output products required to create the DSA from the block diagram:
    generate_target all \
    [get_files ./zc702_acp_vivado/zc702_acp.srcs/\
    sources_1/bd/zc702_acp/zc702_acp.bd]
    
  8. Finally, use the following command to generate the DSA:
    write_dsa -force ./zc702_acp.dsa