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.

Figure: Motion Detect Design Configuration



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

The following steps outline how to access the project in the SDSoC environment.
  1. Download and extract the platform to your system.
  2. Open SDx™ and create a new application project.
  3. From the Platform dialog box, select Add Custom Platform.
  4. From the Specify Custom Platform Location dialog box, select the location of the downloaded platform, and click OK.
  5. From the Platform dialog box, select the custom platform folder named zc702_trd or zc706_trd, and click Next.
  6. From the System Configuration dialog box, leave the default settings and click Next.
  7. 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.

Figure: AXI4 Data Mover Design



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.