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 platformzc702_acp
- Sharing a processing system AXI bus interface between the platform and thesdscc
system compiler
- 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.
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.
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.
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.
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.
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.
- 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]
- 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. - 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:
M_AXI_GP
– a general purpose masterS_AXI_ACP
– a cache coherent slaveS_AXI_HP
– a high performance, non-cache coherent slaveS_AXI_HPC
– a high performance slave (Zynq UltraScale+ MPSoC only)MIG
– a slave on an external DDR (MIG) memory controller IPAn 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
- 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]
- 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 theprocessing_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]
- 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]
- Use the following command to generate the
DSA:
write_dsa -force ./zc702_axis_io.dsa
Making the SDSoC Platform from the Command Line
- 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
- 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
- Use the following command to create a new system configuration for Linux
applications:
system -name linux -display-name "Linux" \ -boot ./boot -readme ./generic.readme
- 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
- Use the following command to register boot files for the Linux system
configuration:
boot -bif ./linux/linux.bif
- 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
- 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
- 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
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.
- Launch Xilinx SDx and provide a path to your workspace such as <path_to_tutorial>/myplatforms/.
- Create a new project by selecting .
- Specify the type of project as an Application Project, and click Next.
- Specify a project name in the Create New SDx Project page such as my_zc702_axis_io, and click Next.
- In the Choose Hardware Platform page click Add Custom Platform.
- Navigate to the folder containing the platform <sdx_root>/samples/platforms/zc702_axis_io.
- The platform will show up in the Choose Hardware Platform Page.
Select zc702_axis_io (custom) and click
Next.
- On the System Configuration page, keep the default Linux for System Configuration and click Next.
- 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 withins2mm_data_copy_wrapper
creates a direct signal path from the platform input to a hardware function calleds2mm_data_copy
that then pushes the data to memory as azero_copy
datamover. That is, thes2mm_data_copy
function acts as a custom DMA. The main program allocates four buffers, invokess2mm_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. - 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; }
- Buffers are allocated using
- 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.
- 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
<sdx_root>/samples/platforms/zc702_acp/src
file.- 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.
- 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]
- 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. - 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 masterS_AXI_ACP
– a cache coherent slaveS_AXI_HP
– a high performance, non-cache coherent slaveS_AXI_HPC
– a high performance slave (Zynq UltraScale+ MPSoC only)MIG
– a slave on an external DDR (MIG) memory controller IPAn 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
- The platform uses both the
S_AXI_ACP
andM_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.
- 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]
- 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]
- Finally, use the following command to generate the
DSA:
write_dsa -force ./zc702_acp.dsa