Intel® High Level Synthesis Compiler Pro Edition: Reference Manual
A newer version of this document is available. Customers should click here to go to the newest version.
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.