Version: Vitis 2023.2
This tutorial explains how to add custom IPs into a Versal™ adaptive SoC platform. This tutorial is an ‘add-on’ to the basic Versal platform creation tutorial. Therefore, for each step, there is a pointer to the corresponding part of that tutorial, as required.
This tutorial targets the Versal board.
Overview
The tutorial is structured as follows:
Step 1: Create a Hardware Platform
In this tutorial, you use the prebuilt example design for Versal to build the hardware platform. This method saves time instead of manually creating the IP integrator block design.
Follow the instructions in Versal platform creation tutorial-Step1. You may skip the optional steps.
Step 2: Add Custom IP into the Block Design
In this step, you add a DDS compiler IP into the platform design that you created in the last step. The DDS compiler IP is provided in the AMD IP catalog in the Vivado Design Suite. For more details, see PG141. Because it is a publicly available IP, it supports simulation inherently. If you want to add your own IP cores, you must provide simulation models to support simulation.
Reopen the system block design that was created in last step if you have already closed it.
Click the Add IP (plus icon) button and search for dds. Double click the DDS compiler IP to add it to the canvas.
Configure the DDS IP as follows. Be aware that some of the settings are not a hard requirement and are just for the purpose of this design. The only hard requirement is that there is only a single AXI4-Stream interface for this IP and therefore the phase output port is disabled.
a. Configuration Tab Settings:
b. Implementation Tab Settings:
c. Detailed Implementation Tab Settings:
Keep the default settings for this page.
d. Output Frequency Tab Settings:
You can adjust the frequency value but keep the sample rate in mind. The clocking rate is 300 MHz. If there are too few samples within one cycle, the sine wave cannot be sampled correctly.
Exit the configuration page and connect the
aclk
port to theclk_out1
signal of clocking wizard.Now the block design should look like the following:
Open up platform setup window and enable the M_AXIS_DATA port. Set the SP Tag to
AXIS
- this will be used by the Vitis™ linker to connect to the accelerator.
Regenerate the block design output products.
Export the hardware platform. In the platform type selection page, choose Hardware and hardware emulation to run emulation with this platform.
Step 3: Package the Platform in the Vitis Software Platform
In this step, you pack the hardware XSA with software components in the Vitis IDE. Follow the instructions in Versal platform creation tutorial-Step2.
Step 4: Test the Platform
In this step, you build a simple vadd
application and run hardware emulation to test the platform. Follow the instructions in Versal platform creation tutorial-Step3 along with the following modifications.
You need to run Application1 on the above page. After creating the vadd
system project, make the following changes.
Open up the
krnl_vadd.cpp
file under thesrc
folder of thevadd_kernels
project. Add the following header files at the beginning of this file. These are used to support the AXIS data type and HLS stream data type.
#include "ap_int.h"
#include "ap_axi_sdata.h"
Define a type named
pkt
before the kernel function definition.
typedef ap_axis<15, 0, 0, 0> pkt;
In this design, the data from the DDS IP is not processed. It is only passed on to the DDR. To do that, add a function similar to the following:
static void dss_process( int *wave, hls::stream<pkt> &s_in) {
for (int i = 0; i < 1024; i++)
{
#pragma HLS PIPELINE II = 1
pkt value = s_in.read();
wave[i] = value.data;
}
}
Add two ports into the top level function. The
dds_in
port is an AXI4-Stream type and thewave_out
port is an AXI-MM type.
void krnl_vadd(uint32_t* in1, uint32_t* in2, uint32_t* out, int size, int *wave_out, hls::stream<pkt> &dds_in)
Declare the two ports added in step4 in the
krnl_vadd
function.
#pragma HLS INTERFACE m_axi port = wave_out bundle = gmem2
#pragma HLS INTERFACE axis port = dds_in
Add one line to call the
dss_process
function in thekrnl_vadd
function.
dss_process(wave_out,dds_in);
Now the kernel code modification is complete. Save your work and close the file.
Note: For the complete modification code reference on the
vadd
kernel, see kernel code modifications.
To direct the Vitis linker to correctly link the kernel’s AXI stream port with the corresponding interface of the platform, create a configuration file called
system.cfg
and add the following lines to it.
[connectivity]
stream_connect = AXIS:krnl_vadd_1.dds_in
This command indicates the connections between two AXI4-Stream ports. The name before the colon must be the master interface while the latter is the name of the slave interface. In this design, the AXIS sptag from platform represents the AXI master side therefore we put it in the front. Here you just need to specify the sptag
that was defined during platform creation and the Vitis linker detects it automatically.
The system_cfg
file is put under the vadd_system_hw_link
project. You can choose other locations but just make sure that you point to it correctly in the binary container settings.
Specify the config file in binary container settings:
Now modify the host code to read back the data from the DDR. Open up the
vadd.cpp
file under thesrc
folder of thevadd
project and modify the following items.a. Add header files.
#include "ap_int.h" #include <stdio.h>
b. Create a new
cl_mem buffer
for the DDS output data.cl::Buffer buffer_waveout(context, CL_MEM_WRITE_ONLY, 1024*sizeof(int));
c. Bond the buffer with the kernel.
krnl_vector_add.setArg(narg++,buffer_waveout);
d. Map the host buffer with the device buffer.
int *ptr_waveout = (int *) q.enqueueMapBuffer (buffer_waveout , CL_TRUE , CL_MAP_READ , 0, 1024*sizeof(int));
e. Read the data from the DDR.
q.enqueueMigrateMemObjects({buffer_waveout},CL_MIGRATE_MEM_OBJECT_HOST);
f. Save the data into a file. The data is left shifted by 17 bits because the DDS compiler IP output is 16-bit signed data and the host buffer 32-bit integer data. This is to remove the extra signed bits and keep the actual payload data.
FILE *fp_dout; fp_dout=fopen("wave_out.txt","w"); for (int i = 0; i < 1024; i++) { fprintf(fp_dout,"%d\n",ptr_waveout[i]<<17); } fclose(fp_dout);
g. Unmap the buffer.
q.enqueueUnmapMemObject(buffer_waveout , ptr_waveout);
Note: For the complete modification code reference on the
vadd
host application, see host code modifications.Now run emulation on the modified
vadd
application. Follow the instructions in Versal platform creation tutorial-Step3 Emulation.After the ‘Test Passed’ pop-up appears in the console window, copy the generated
wave_out.txt
file from the QEMU target. First, launch the XSCT console window fromAMD
menu if you have not already done so.
Connect to the tcf
target using the following command:
connect -host 127.0.0.1 -port 1440
Copy the wave_out.txt
file from the tcf
target to the host machine using the following command:
tfile copy -to-host /run/media/mmcblk0p1/wave_out.txt $(YOUR_DIR)/wave_out.txt
The wave_out.txt file will be copied over to $(YOUR_DIR). Choose a tool to plot the data. A possible option is the tool gnuplot
which is an open source tool and easy to use it to print out the sine wave.