Visible to Intel only — GUID: mwt1583362141410
Ixiasoft
Visible to Intel only — GUID: mwt1583362141410
Ixiasoft
4.3.1. The pipe Class and Its Use
The pipe class exposes static methods for writing a data word to a pipe and reading a data word from a pipe. The reads and writes can be blocking or nonblocking, with the form chosen based on the overload resolution.
template <class name, class dataT, size_t min_capacity = 0> class pipe { public: // Blocking static dataT read(); static void write(dataT data); // Non-blocking static dataT read(bool &success); static void write(dataT data, bool &success); }
Parameter | Description |
---|---|
name | The type that is used to create a unique identifier for the pipe. It is typically a user-defined class, in a user namespace. Forward declaration of the type is enough, and the type need not be defined. |
dataT | The data type of the packet contained within a pipe. This is the data type that is read during a successful pipe read() operation, or written during a successful pipe write() operation. The type must have a standard layout and be trivially copyable. |
min_capacity | The minimum number of words (in units of dataT) that the pipe must be able to store without any being read out. The compiler might create a pipe with a larger capacity due to performance considerations. |
Example Using Pipes
#include "HLS/hls.h" template<unsigned ID, class T, unsigned pipe_capacity> class TaskSystem { private: template<unsigned SystemID> class InputPipeID {}; template<unsigned SystemID> class TaskPipeID {}; template<unsigned SystemID> class OutputPipeID {}; public: using input_pipe = ihc::pipe<class InputPipeID<ID>, T, pipe_capacity>; using output_pipe = ihc::pipe<class OutputPipeID<ID>, T, pipe_capacity>; using task_pipe = ihc::pipe<class TaskPipeID<ID>, T, pipe_capacity>; static void first_task(unsigned N) { T data; for(unsigned i=0; i<N; ++i) { data = input_pipe::read(); task_pipe::write(data); } } static void second_task(unsigned N) { T data; for(unsigned i=0; i<N; ++i) { data = task_pipe::read(); output_pipe::write(data); } } };
With this header file, first_task and second_task can be called from separate task functions to achieve concurrency.
#include "HLS/hls.h" #include <iostream> #include “task_system.h” unsigned constexpr ID = 42; // can be any unique value unsigned constexpr CAPACITY = 100; using MySystem = TaskSystem<ID, int, CAPACITY>; int main() { ihc::launch<MySystem::first_task>(CAPACITY); ihc::launch<MySystem::second_task>(CAPACITY); for(int i = 0; i < CAPACITY; ++i) { std::cout << "input: " << i << "\n"; MySystem::input_pipe::write(i); } for(int i = 0; i < CAPACITY; ++i) { int data = MySystem::output_pipe::read(); std::cout << "output: " << data << "\n"; } return 0; }
Example of an Array of Pipes
The following code example implements an array of pipes using templates, and includes functions to write to such an array.
#include "HLS/hls.h" // PipeArray template <class ArrayID, typename T, unsigned pipeCapacity, unsigned arraySize> class PipeArray { private: template <unsigned idx> struct StructIndex; template <unsigned idx> struct VerifyIndex { static_assert(idx < arraySize, "Index out of bounds"); using VerifiedPipe = ihc::pipe<StructIndex<idx>, T, pipeCapacity>; }; public: template <unsigned idx> using pipe_at = typename VerifyIndex<idx>::VerifiedPipe; static void write_to_pipes(T *values); }; // Write Unroller template <class ArrayID, typename T, unsigned pipeCapacity, unsigned arraySize, unsigned idx> struct WriteUnroller { using my_array = PipeArray<ArrayID, T, pipeCapacity, arraySize>; static void write_to_pipes_impl(T *values) { my_array::template pipe_at<idx>::write(values[idx]); WriteUnroller<ArrayID, T, pipeCapacity, arraySize, idx + 1>::write_to_pipes_impl(values); } }; template <class ArrayID, typename T, unsigned pipeCapacity, unsigned arraySize> struct WriteUnroller<ArrayID, T, pipeCapacity, arraySize, arraySize> { static void write_to_pipes_impl(T *values) {} }; // Write function template <class ArrayID, typename T, unsigned pipeCapacity, unsigned arraySize> void PipeArray<ArrayID, T, pipeCapacity, arraySize>::write_to_pipes(T *values) { WriteUnroller<ArrayID, T, pipeCapacity, arraySize, 0>::write_to_pipes_impl( values); }
The function PipeArray::write_to_pipes takes an array of values to be written, and calls WriteUnroller::write_to_pipes_impl, which uses recursive templating to write to each pipe in the array. Reading from the array of pipes would have a similar implementation.