One main limitation of PIPO buffers is that they can flow only forward with
respect to the function call sequence in C++. In other words, the following connection is not
supported with PIPOs, while it can be supported with hls::stream_of_blocks
:
void top(...) {
int b[N];
for (int i = 0; i < M; i++) {
#pragma HLS dataflow
#pragma HLS stream off variable=b
consumer(b, ...); // reads b
producer(b, ...); // writes b
}
}
The following code example is contrived to demonstrate the concept:
#include "hls_streamofblocks.h"
typedef int buf[N];
void producer (hls::stream_of_blocks<buf> &out, ...) {
for (int i = 0; i < M; i++) {
hls::write_lock<buf> arr(out);
for (int j = 0; j < N; j++)
arr[f(j)] = ...;
}
}
void consumer(hls::stream_of_blocks<buf> &in, ...) {
if (in.empty()) // execute only when producer has already generated some meaningful data
return;
for (int i = 0; i < M; i++) {
hls::read_lock<buf> arr(in);
for (int j = 0; j < N; j++)
... = arr[g(j)];
...
}
}
void top(...) {
// Note the large non-default depth.
// The producer must complete execution before the consumer can start again, due to ap_ctrl_chain.
// A smaller depth would require ap_ctrl_none
hls::stream_of_blocks<buf, M+2> backward;
for (int i = 0; i < M; i++) {
#pragma HLS dataflow
consumer(backward, ...); // reads backward
producer(backward, ...); // writes backward
}