Developer Reference

Migrating OpenCL™ FPGA Designs to SYCL*

ID 767849
Date 5/08/2024
Public

Device Code Modification

This topic describes some techniques you should consider when converting your OpenCL device code to SYCL*.

Channels/Pipes

You can use OpenCL channels and their SYCL equivalent pipes to send data from one kernel to another in a FIFO manner without going through device memory. This topic describes how to transform OpenCL channels into SYCL pipes.

In OpenCL, channels are an Intel® extension and you declare them as file scope variables using the syntax provided in the table below. This makes the channel unique within a given OpenCL kernel program and allows the compiler to make static connectivity. In SYCL, pipes use the C++ type system to guarantee static connectivity. Therefore, as you can see in the SYCL code below, my_pipe is a C++ type rather than a file scope variable as in OpenCL.

The first template argument of a pipe declaration can be of any type, but it is typically a user-defined C++ class or struct. The class or struct must be forward declared and need not be defined. This naming method is like the kernel naming method described in the Basic Device Code Modification section. The second template argument is the data type that the pipe contains. The third (optional) argument is the minimum number of elements that the pipe must store. The default value of 0 allows the compiler to choose a depth.

The following table summarizes the declaration and usage of OpenCL channels and SYCL pipes:

Declaration of OpenCL Channels and SYCL Pipes
OpenCL SYCL
channel int my_pipe __attribute__((depth(8)));
using my_pipe = pipe<class my_pipe_id, int, 8>; 
// blocking 
write_channel_intel(my_pipe, write_data); 

// non-blocking 
bool success_code = write_channel_nb_intel(my_pipe, write_data);
// blocking 
my_pipe::write(write_data); 

// non-blocking 
my_pipe::write(write_data, &success_code);
// blocking 
read_data = read_channel_intel(my_pipe); 

// non-blocking 
read_data = read_channel_nb_intel(my_pipe, &success_code);
// blocking 
read_data = my_pipe::read(); 

// non-blocking 
read_data = my_pipe::read(&success_code); 

In your OpenCL design, you may have used an array of channels. In SYCL, this is replaced with C++ meta-programming. To convert an array of channels in OpenCL to SYCL, refer to the pipe_array SYCL FPGA tutorial on GitHub.

I/O Pipes

I/O pipes are a special type of FIFO used to interface with input or output features of an FPGA board. These features may include network cards, PCIe busses, cameras, or other processing devices or protocols. Assume that the board_spec.xml file in your BSP has the following section describing the interface to a UDP offload engine that is implemented inside the BSP:

<channels>
  <interface name="udp_0" port="udp0_out"  type="streamsource" width="256"
   chan_id="io_pipe_in"/>
  <interface name="udp_0" port="udp0_in"  type="streamsink" width="256"
   chan_id="io_pipe_out"/>
</channels>

Then, the following table shows how you would construct the I/O pipe in both OpenCL and SYCL:

I/O Pipe Construction
OpenCL SYCL
pipe int read_io_pipe __attribute__((depth(8), io(“io_pipe_in”)));

pipe int write_io_pipe __attribute__((depth(8), io(“io_pipe_out”)));
struct read_io_pipe_id {
  // id=index to “io_pipe_in” in board_spec.xml
  static constexpr unsigned id = 0;
};

struct write_io_pipe_id {
  // id=index to “io_pipe_out” in board_spec.xml
  static constexpr unsigned id = 1;
};

using read_io_pipe = intel::kernel_readable_io_pipe<read_io_pipe_id, int, 8>;

using write_io_pipe = intel::kernel_writeable_io_pipe<write_io_pipe, int, 8>;

In OpenCL, the io attribute on the pipe must match the chan_id in the board_spec.xml file. In SYCL, the static id member of the pipe type identifier is used as an index to get the child interface of the channel's node in the board_spec.xml file. The methods for reading and writing to the I/O pipe are the same as those discussed in the Channels/Pipes section.