Description
The main message, HLS 200-2199, provides the main information about
the cause of the II increase. The auxiliary message, HLS 200-2198, shows all the
operations competing for the same resource (memory port or FIFO port implementing a
streamed top array or a top hls::stream).
INFO: [HLS 200-2199] The II Violation in module 'pad_general_unsigned_short_16_Pipeline_VITIS_LOOP_23_2' (loop 'VITIS_LOOP_23_2'): Potential resource conflict between fifo read operation ('in_15_read', ./pad.h:48) on port 'in_15' (./pad.h:48) and fifo read operation ('in_15_read_15', ./pad.h:48) on port 'in_15' (./pad.h:48) on common resource 'in_15'.
INFO: [HLS 200-2198] Existing conflicting constraints include from fifo read operation ('in_15_read', ./pad.h:48) on port 'in_15' (./pad.h:48) to fifo read operation ('in_15_read_1', ./pad.h:48) on port 'in_15' (./pad.h:48) with distance of 1 (conflict 1 of 15)
...
INFO: [HLS 200-2198] Existing conflicting constraints include from fifo read operation ('in_15_read_14', ./pad.h:48) on port 'in_15' (./pad.h:48) to fifo read operation ('in_15_read_15', ./pad.h:48) on port 'in_15' (./pad.h:48) with distance of 1 (conflict 15 of 15)
INFO: [HLS 200-1470] Pipelining result : Target II = 1, Final II = 16, Depth = 72, loop 'VITIS_LOOP_23_2'
Explanation
The message is generated when an hls::stream or streamed array is accessed multiple times in a
pipelined loop. All FIFOs have exactly one read port and one write port.
Recommendation
To resolve the issue, check the conditions under which the conflicting operations are executed (both control and observability-dependent mutual exclusion) and ensure that they are correct and easy to analyze. In the following example, the code looks like this (the pipeline pragma fully unrolls all the nested loops):
template <typename T>
void pad_general(hls::stream<T> in[16], hls::stream<T> out[16], PadConfig config) {
...
for (int c = 0; c < iters; c++) {
#pragma HLS pipeline II=1
...
for (int m = 0; m < 16; m++) {
...
int ii = i-config.pad_start[0]; ...
int idx_read = ii*config.input_dim_size[3]*...;
tmp = in[idx_read%16].read();
out[m].write(tmp);
}
}
}
It is important to note the following:
- Top arrays of streams
inandoutare automatically fully partitioned into K streams. - In this case, the value of
idx_readdepends on the content of some input arrays, for exampleconfig.pad_start[0]andconfig.input_dim_size[3]. -
Vitis HLS is therefore unable to
infer that different input streams are accessed by each iteration of the
unrolled loop over
m. - All K accesses can potentially read the same stream.
in_15is reported in the preceding example, and any one of the other 15in_…streams could have been reported. A minimum II of 16 is required. - However, Vitis HLS
can infer that all accesses to
outare to one of the 16 streams.
In this case, a solution might be a single stream containing an array of 16 T, read into a local fully partitioned array and then used in the loop:
template <typename T>
void pad_general(hls::stream<hls::vector<T, 16>> &in, hls::stream<T> out[16], PadConfig config) {
...
for (int c = 0; c < iters; c++) {
#pragma HLS pipeline II=1
hls::vector<T,K> t = in.read();
#pragma HLS array_partition complete variable=t
...
for (int m = 0; m < 16; m++) {
...
int ii = i-config.pad_start[0]; ...
int idx_read = ii*config.input_dim_size[3]*...;
tmp = t[idx_read%16];
out[m].write(tmp);
}
}
}
For more information and for a definition of terms, refer to Pipeline II Violations.