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.