Intel® High Level Synthesis Compiler Pro Edition: Reference Manual
5.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.