-
Notifications
You must be signed in to change notification settings - Fork 422
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add general transpose for vivado/vitis
- Loading branch information
Showing
6 changed files
with
158 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#ifndef NNET_PERMUTE_H_ | ||
#define NNET_PERMUTE_H_ | ||
|
||
namespace nnet { | ||
|
||
struct transpose_config { | ||
static const unsigned dims; | ||
static const unsigned N; | ||
// vivado/vitis hls can't index constexpr array for some reason | ||
// and vivado hls don't like template recursion either (vitis is fine) | ||
// thus this appears to be the only workaround (or overkill it with codegen) | ||
static const unsigned *const from_shape; | ||
static const unsigned *const to_shape; | ||
static const unsigned *const perm; | ||
static const unsigned *const perm_strides; | ||
}; | ||
|
||
template <typename CONFIG_T> unsigned transfer_idx(int index) { | ||
// Given output idx in c-order flat array, return input idx | ||
int idx = 0; | ||
for (int i = CONFIG_T::dims - 1; i >= 0; i--) { | ||
idx += (index % CONFIG_T::to_shape[i]) * CONFIG_T::perm_strides[i]; | ||
index /= CONFIG_T::to_shape[i]; | ||
} | ||
return idx; | ||
} | ||
|
||
template <typename data_T, typename res_T, typename CONFIG_T> | ||
void transpose(const data_T data[CONFIG_T::N], res_T res[CONFIG_T::N]) { | ||
for (int i = 0; i < CONFIG_T::N; i++) { | ||
#pragma HLS UNROLL | ||
int idx = transfer_idx<CONFIG_T>(i); | ||
res[i] = data[idx]; | ||
} | ||
} | ||
|
||
} // namespace nnet | ||
|
||
#endif |
67 changes: 67 additions & 0 deletions
67
hls4ml/templates/vivado/nnet_utils/nnet_transpose_stream.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
#ifndef NNET_TRANSPOSE_STREAM_H | ||
#define NNET_TRANSPOSE_STREAM_H | ||
|
||
#include "hls_stream.h" | ||
#include "nnet_transpose.h" | ||
#include <type_traits> | ||
|
||
namespace nnet { | ||
|
||
template <typename data_T, typename res_T, typename CONFIG_T> | ||
typename std::enable_if<CONFIG_T::dims == 2, void>::type transpose(hls::stream<data_T> &data, hls::stream<res_T> &res) { | ||
// #pragma HLS INLINE RECURSIVE | ||
typename data_T::value_type data_array[CONFIG_T::N]; | ||
#pragma HLS ARRAY_PARTITION variable=data_array complete | ||
|
||
for (int i = 0; i < CONFIG_T::N / data_T::size; i++) { | ||
#pragma HLS PIPELINE | ||
data_T in_data = data.read(); | ||
for (int j = 0; j < data_T::size; j++) { | ||
#pragma HLS UNROLL | ||
data_array[i * data_T::size + j] = typename data_T::value_type(in_data[j]); | ||
} | ||
} | ||
|
||
for (int i = 0; i < CONFIG_T::N / res_T::size; i++) { | ||
#pragma HLS PIPELINE | ||
res_T out_data; | ||
PRAGMA_DATA_PACK(out_data) | ||
for (int j = 0; j < res_T::size; j++) { | ||
#pragma HLS UNROLL | ||
out_data[j] = typename res_T::value_type(data_array[j * CONFIG_T::from_shape[1] + i]); | ||
} | ||
res.write(out_data); | ||
} | ||
} | ||
|
||
// This sfinae is for vivado_hls, which has some overhead using the transfer_idx in io_stream. | ||
// In vitis both performs exactly the same, thus this is not removed out of convenience. | ||
template <typename data_T, typename res_T, typename CONFIG_T> | ||
typename std::enable_if<CONFIG_T::dims != 2, void>::type transpose(hls::stream<data_T> &data, hls::stream<res_T> &res) { | ||
// #pragma HLS INLINE RECURSIVE | ||
typename data_T::value_type data_array[CONFIG_T::N]; | ||
#pragma HLS ARRAY_PARTITION variable=data_array complete | ||
|
||
for (int i = 0; i < CONFIG_T::N / data_T::size; i++) { | ||
#pragma HLS PIPELINE | ||
data_T in_data = data.read(); | ||
for (int j = 0; j < data_T::size; j++) { | ||
#pragma HLS UNROLL | ||
data_array[i * data_T::size + j] = typename data_T::value_type(in_data[j]); | ||
} | ||
} | ||
|
||
for (int i = 0; i < CONFIG_T::N / res_T::size; i++) { | ||
#pragma HLS PIPELINE | ||
res_T out_data; | ||
PRAGMA_DATA_PACK(out_data) | ||
for (int j = 0; j < res_T::size; j++) { | ||
#pragma HLS UNROLL | ||
out_data[j] = typename res_T::value_type(data_array[transfer_idx<CONFIG_T>(i * res_T::size + j)]); | ||
} | ||
res.write(out_data); | ||
} | ||
} | ||
|
||
} // namespace nnet | ||
#endif |