Using External I/O
Hardware accelerators generated in the SDSoC™ environment can communicate with system inputs and outputs either directly through hardware connections, or though memory buffers (for example, a frame buffer). Examples of system I/O include analog-to-digital and digital-to-analog converters, image, radar, LiDAR, and ultrasonic sensors, and HDMI™ multimedia streams.
A platform exports stream connections in hardware that can be accessed in software by calling platform library functions as described in Accessing External I/O Using Memory Buffers and Accessing External I/O Using Direct Hardware Connections. Direct hardware connections are implemented over AXI4-Stream channels and connections to memory buffers are realized through function calls implemented by the standard data movers supported in the SDSoC environment.
For information and examples that show how to create SDSoC platforms, see the SDSoC Environment Platform Development Guide.
Accessing External I/O Using Memory Buffers
This section uses the motion-detect ZC702 + HDMI IO FMC or the ZC706 + HDMI IO FMC platform found under the "Boards, Kits, & Modules" page of the SDSoC Development Environment. The preconfigured SDSoC platform is responsible for the HDMI data transfer to external memory. The application must call the platform interfaces to process the data from the frame buffer in DDR memory. The following figure shows an example of how the design is configured.
The SDSoC environment accesses the
external frame buffer through an accelerator interface to the platform. The zc702_hdmi
platform provides a software interface to access the
video frame buffer through the Video4Linux2
(V4L2) API. The
V4L2 framework provides an API accessing a collection of device drivers supporting real-time
video capture in Linux. This API is the application development platform I/O entry point.
The motion_demo_processing
function in the
following code snippet from m2m_sw_pipeline.c
demonstrates the function call interface.
extern void motion_demo_processing(unsigned short int *prev_buffer,
unsigned short int *in_buffer,
unsigned short int *out_buffer,
int fps_enable,
int height, int width, int stride);
.
.
.
unsigned short *out_ptr = v_pipe->drm.d_buff[buf_next->index].drm_buff;
unsigned short *in_ptr1 = buf_prev->v412_buff;
unsigned short *in_ptr2 = buf_next->v412_buff;
v_pipe->events[PROCESS_IN].counter_val++;
motion_demo_processing(in_ptr1, in_ptr2, out_ptr,
v_pipe->fps_enable,
(int)m2m_sw_stream_handle.video_in.format.height,
(int)m2m_sw_stream_handle.video_in.format.width,
(int)m2m_sw_stream_handle.video_in.format.bytesperline/2);
The application accesses this API in motion_detect.c, where motion_demo_procesing
is
defined and called by the img_process
function.
void motion_demo_processing(unsigned short int *prev_buffer,
unsigned short int *in_buffer,
unsigned short int *out_buffer,
int fps_enable,
int height, int width, int stride)
{
int param0=0, param1=1, param2=2;
img_process(prev_buffer, in_buffer, out_buffer, height, width, stride);
}
Finally, img_process
calls the various
filters and transforms to process the data.
void img_process(unsigned short int *frame_prev,
unsigned short int *frame_curr,
unsigned short int *frame_out,
int param0, int param1, int param2)
{
...
}
By using a platform API to access the frame buffers, the application developer does not program at the driver-level to process the video frames.
You can find the platform used for the code snippets on the SDSoC Downloads Page with the name ZC702 + HDMI IO FMC or ZC706 + HDMI IO FMC.
Accessing the SDSoC Environment
- Download and extract the platform to your system.
- Open SDx™ and create a new application project.
- From the Platform dialog box, select Add Custom Platform.
- From the Specify Custom Platform Location dialog box, select the location of the downloaded platform, and click OK.
- From the Platform dialog box, select the custom platform folder named zc702_trd or zc706_trd, and click Next.
- From the System Configuration dialog box, leave the default settings and click Next.
- From the Templates dialog box, select the
Dense Optical Flow (1PPC)
template, and click Finish.
Accessing External I/O Using Direct Hardware Connections
Whereas the example in Accessing External I/O Using Memory Buffers
demonstrated how applications can access system I/O through memory buffers, a platform can
also provide direct hardware connectivity to hardware accelerators within an application
generated in the SDSoC environment. The following figure
shows how a function s2mm_data_copy
communicates to the
platform using an AXI4-Stream channel and writes to DDR
memory using the zero_copy
data mover (implemented as an
AXI4 master interface). This design template is called
aximm
design example in the samples/platforms/zc702_axis_io platform.
In this example, the zc702_axis_io
platform
proxies actual I/O by providing a free-running binary counter (labeled Platform IP in the
diagram) running at 50 MHz, connected to an AXI4-Stream Data FIFO IP block
that exports an AXI4-Stream master interface to the
platform clocked at the data motion clock (which might differ from the 50 MHz input
clock).
The direct I/O interface is specified using the sys_port
pragma for a stream port. In the following code snippet, the direct
connection is specified for stream_fifo_M_AXIS
.
#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;
}
In the following main
application code, the
rbuf0
variable maps the FIFO input stream to the s2mm_data_copy
function, so the sds++
compiler creates a direct hardware connection over an AXI4-Stream channel. Because the s2mm_data_copy
function transfers buf
using the
zero_copy
data mover, the buffer must be allocated in
physically contiguous memory using sds_alloc
, and released
using sds_free
, as shown below.
int main()
{
unsigned *bufs[NUM_BUFFERS];
bool error = false;
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]);
}
error = check(bufs);
printf("TEST %s\n\r", (error ? "FAILED" : "PASSED"));
for(int i=0; i<NUM_BUFFERS; i++) {
sds_free(bufs[i]);
}
return 0;
}
Information on creating a platform using AXI4-Stream to write to memory directly can be found in the "SDSoC Platform Examples" appendix of SDSoC Environment Platform Development Guide.