From da25f96105985654fcc5977c21b43206d01557cf Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Tue, 2 Mar 2021 16:47:58 +1030 Subject: [PATCH 01/14] Added basic FPGA FAE client executable for testing --- re/src/workers/fft_accel/test/CMakeLists.txt | 4 +- .../fft_accel/test/fpga_client/CMakeLists.txt | 15 ++ .../fft_accel/test/fpga_client/fft_client.cpp | 205 ++++++++++++++++++ 3 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 re/src/workers/fft_accel/test/fpga_client/CMakeLists.txt create mode 100644 re/src/workers/fft_accel/test/fpga_client/fft_client.cpp diff --git a/re/src/workers/fft_accel/test/CMakeLists.txt b/re/src/workers/fft_accel/test/CMakeLists.txt index 391820ad3..06169b254 100644 --- a/re/src/workers/fft_accel/test/CMakeLists.txt +++ b/re/src/workers/fft_accel/test/CMakeLists.txt @@ -47,4 +47,6 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_$ ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/$ if(MSVC) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -endif(MSVC) \ No newline at end of file +endif(MSVC) + +add_subdirectory(fpga_client) \ No newline at end of file diff --git a/re/src/workers/fft_accel/test/fpga_client/CMakeLists.txt b/re/src/workers/fft_accel/test/fpga_client/CMakeLists.txt new file mode 100644 index 000000000..9c6926acd --- /dev/null +++ b/re/src/workers/fft_accel/test/fpga_client/CMakeLists.txt @@ -0,0 +1,15 @@ + +find_package(Boost 1.67.0 REQUIRED COMPONENTS program_options) + +add_executable(fpga_fft_client) + +target_sources(fpga_fft_client + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/fft_client.cpp + ) + +target_link_libraries(fpga_fft_client + PRIVATE + sem_fft_accel_fpga_worker + Boost::program_options +) \ No newline at end of file diff --git a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp new file mode 100644 index 000000000..7bfd2f0d4 --- /dev/null +++ b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp @@ -0,0 +1,205 @@ +// +// Created by Jackson Michael on 1/3/21. +// + +#include "boost/program_options.hpp" +#include "boost/endian.hpp" + +#include "component.h" +#include "print_logger.h" +#include "sem_fft_accel_worker.hpp" + +#include +#include +#include +#include + +namespace sem::fft_accel::test { + + struct config_data { + std::string fae_endpoint; + std::string input_data_path; + boost::optional expected_result_path; + }; + + std::optional parse_cmd_args(int argc, char **argv) { + namespace prog_opts = boost::program_options; + + config_data data; + + prog_opts::options_description generic("Generic options"); + generic.add_options() + ("help,h", "Print a description of the available commands and exit"); + + prog_opts::options_description config("Client configuration options"); + config.add_options() + ("fae-endpoint,e", prog_opts::value(&data.fae_endpoint)->required(), + "Set the endpoint of the FFT Acceleration Engine this client should connect to") + ("input_data,i", prog_opts::value(&data.input_data_path)->required(), + "Specify the base filepath for the input fft data ('_r.txt' and '_i.txt' will be appended)") + ("expected_result,x", prog_opts::value(&data.expected_result_path), + "Specify the base filepath for the expected fft result ('_r.txt' and '_i.txt' will be appended)"); + + prog_opts::options_description cmd_options; + cmd_options.add(generic).add(config); + + prog_opts::variables_map variables_map; + prog_opts::store(prog_opts::parse_command_line(argc, argv, cmd_options), variables_map); + + if (variables_map.count("help")) { + std::cout << cmd_options << std::endl; + return {}; + } + + // At this point, check if the variables marked as required have been provided, throwing as needed + prog_opts::notify(variables_map); + + return data; + } + + /// Reinterpret an integer value as a float without performing any cast conversions + float float_from_bits(uint32_t value) { + return *reinterpret_cast(&value); + } + + /// Convert a bitset to the equivalent IEEE754 float (not broadly tested across may platforms) + float float_from(std::bitset<32> bits) { + return float_from_bits(bits.to_ulong()); + } + + /// Performs a big-endian-to-native conversion before interpreting the bits as an IEEE754 float + float float_from_big_endian(std::bitset<32> bits) { + uint32_t big_endian_representation = bits.to_ulong(); + auto native_representation = boost::endian::big_to_native(big_endian_representation); + + // Use pointer conversion to nudge the compiler into viewing an integer as a float + return float_from_bits(native_representation); + } + + struct fft_data_fileset { + std::filesystem::path real_data; + std::filesystem::path imaginary_data; + }; + + fft_data_fileset get_fileset_from_base_path(const std::filesystem::path& path) { + fft_data_fileset fileset; + + auto base_filename = path.filename(); + + fileset.real_data = path; + fileset.real_data.replace_filename(base_filename.string().append("_r.txt")); + + fileset.imaginary_data = path; + fileset.imaginary_data.replace_filename(base_filename.string().append("_i.txt")); + + return fileset; + } + + std::vector load_ascii_binary_fft_data(const std::filesystem::path &path) { + + auto fileset = get_fileset_from_base_path(path); + + std::ifstream real_component_stream(fileset.real_data); + if (!real_component_stream) { + throw std::runtime_error("Failed to open file_stream for " + path.string()); + } + std::ifstream imaginary_component_stream(fileset.imaginary_data); + if (!imaginary_component_stream) { + throw std::runtime_error("Failed to open file_stream for " + path.string()); + } + + std::vector fft_data; + + while (real_component_stream.good() && imaginary_component_stream.good()) { + // Read the ascii bit field values from both files + std::bitset<32> real_component_bits; + real_component_stream >> real_component_bits; + std::bitset<32> imaginary_component_bits; + imaginary_component_stream >> imaginary_component_bits; + + // Handle the end-of-file case (one file case and both files case) + if (real_component_stream.eof() != imaginary_component_stream.eof()) { + throw std::runtime_error( + "Error when parsing FFT data files; reached end of one file while more data remains in the other"); + } + if (real_component_stream.eof() && imaginary_component_stream.eof()) { + break; + } + + // If we didn't reach the end of the file, add the data values to the vector + fft_data.push_back(float_from(real_component_bits)); + fft_data.push_back(float_from(imaginary_component_bits)); + } + + return fft_data; + } + + std::vector calculate_worker_result(const config_data& config, const std::vector& input_data) { + using fft_worker = sem::fft_accel::Worker; + + Component client_component("FPGA_FFT_testing_client"); + auto fft_client = client_component.AddTypedWorker("test_fft_client_worker"); + + Print::Logger print_logger; + fft_client->logger().AddLogger(print_logger); + + auto attr_ref = fft_client->GetAttribute(std::string(fft_worker::AttrNames::accelerator_endpoint)); + if (auto endpoint_attr = attr_ref.lock()) { + endpoint_attr->SetValue(config.fae_endpoint); + } else { + throw std::runtime_error("Failed to acquire lock on FAE endpoint attribute"); + } + + if (!client_component.Configure()) { + throw std::runtime_error("Client component failed to configure"); + } + + return fft_client->calculate_fft(input_data); + } + + bool compare_result(std::vector actual, std::vector expected) { + if (actual.size() != expected.size()) { + std::cerr << "Size of calculated FFT result doesn't match size of expected result" << std::endl; + return false; + } + + bool mismatch_encountered = false; + for (size_t index=0; index < actual.size(); index++) { + if (actual.at(index) != expected.at(index)) { + std::cerr << "Calculated result doesn't match with expected at index " << index << ": " + << actual.at(index) << " vs " << expected.at(index) << std::endl; + mismatch_encountered = true; + } + } + + return mismatch_encountered; + } + +} + +int main(int argc, char **argv) { + using namespace sem::fft_accel::test; + + auto config_result = parse_cmd_args(argc, argv); + if (!config_result.has_value()) { + return 1; + } + config_data config = config_result.value(); + + auto fft_data = load_ascii_binary_fft_data(config.input_data_path); + + try { + auto output_data = calculate_worker_result(config, fft_data); + + if (config.expected_result_path.has_value()) { + auto expected_result = load_ascii_binary_fft_data(config.expected_result_path.value()); + compare_result(output_data, expected_result); + } + } catch (const std::exception &ex) { + std::cerr << "An exception was thrown from a RE system:" << std::endl + << ex.what() << std::endl; + return 1; + } + + return 0; +} \ No newline at end of file From 0b9d99bc2c6d7e2f94b93dab7a22ef98e6704fb4 Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Tue, 2 Mar 2021 17:06:00 +1030 Subject: [PATCH 02/14] Remove boost::endian and add sub-namespaces --- .../fft_accel/test/fpga_client/fft_client.cpp | 170 +++++++++--------- 1 file changed, 83 insertions(+), 87 deletions(-) diff --git a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp index 7bfd2f0d4..20e05d4a9 100644 --- a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp +++ b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp @@ -3,7 +3,6 @@ // #include "boost/program_options.hpp" -#include "boost/endian.hpp" #include "component.h" #include "print_logger.h" @@ -22,39 +21,41 @@ namespace sem::fft_accel::test { boost::optional expected_result_path; }; - std::optional parse_cmd_args(int argc, char **argv) { - namespace prog_opts = boost::program_options; + namespace cmd { + std::optional parse_args(int argc, char **argv) { + namespace prog_opts = boost::program_options; - config_data data; + config_data data; - prog_opts::options_description generic("Generic options"); - generic.add_options() - ("help,h", "Print a description of the available commands and exit"); + prog_opts::options_description generic("Generic options"); + generic.add_options() + ("help,h", "Print a description of the available commands and exit"); - prog_opts::options_description config("Client configuration options"); - config.add_options() - ("fae-endpoint,e", prog_opts::value(&data.fae_endpoint)->required(), - "Set the endpoint of the FFT Acceleration Engine this client should connect to") - ("input_data,i", prog_opts::value(&data.input_data_path)->required(), - "Specify the base filepath for the input fft data ('_r.txt' and '_i.txt' will be appended)") - ("expected_result,x", prog_opts::value(&data.expected_result_path), - "Specify the base filepath for the expected fft result ('_r.txt' and '_i.txt' will be appended)"); + prog_opts::options_description config("Client configuration options"); + config.add_options() + ("fae-endpoint,e", prog_opts::value(&data.fae_endpoint)->required(), + "Set the endpoint of the FFT Acceleration Engine this client should connect to") + ("input_data,i", prog_opts::value(&data.input_data_path)->required(), + "Specify the base filepath for the input fft data ('_r.txt' and '_i.txt' will be appended)") + ("expected_result,x", prog_opts::value(&data.expected_result_path), + "Specify the base filepath for the expected fft result ('_r.txt' and '_i.txt' will be appended)"); - prog_opts::options_description cmd_options; - cmd_options.add(generic).add(config); + prog_opts::options_description cmd_options; + cmd_options.add(generic).add(config); - prog_opts::variables_map variables_map; - prog_opts::store(prog_opts::parse_command_line(argc, argv, cmd_options), variables_map); + prog_opts::variables_map variables_map; + prog_opts::store(prog_opts::parse_command_line(argc, argv, cmd_options), variables_map); - if (variables_map.count("help")) { - std::cout << cmd_options << std::endl; - return {}; - } + if (variables_map.count("help")) { + std::cout << cmd_options << std::endl; + return {}; + } - // At this point, check if the variables marked as required have been provided, throwing as needed - prog_opts::notify(variables_map); + // At this point, check if the variables marked as required have been provided, throwing as needed + prog_opts::notify(variables_map); - return data; + return data; + } } /// Reinterpret an integer value as a float without performing any cast conversions @@ -67,74 +68,67 @@ namespace sem::fft_accel::test { return float_from_bits(bits.to_ulong()); } - /// Performs a big-endian-to-native conversion before interpreting the bits as an IEEE754 float - float float_from_big_endian(std::bitset<32> bits) { - uint32_t big_endian_representation = bits.to_ulong(); - auto native_representation = boost::endian::big_to_native(big_endian_representation); + namespace file { + struct fft_dataset { + std::filesystem::path real_data; + std::filesystem::path imaginary_data; + }; - // Use pointer conversion to nudge the compiler into viewing an integer as a float - return float_from_bits(native_representation); - } + fft_dataset get_dataset_from_base_path(const std::filesystem::path &path) { + fft_dataset fileset; - struct fft_data_fileset { - std::filesystem::path real_data; - std::filesystem::path imaginary_data; - }; + auto base_filename = path.filename(); - fft_data_fileset get_fileset_from_base_path(const std::filesystem::path& path) { - fft_data_fileset fileset; + fileset.real_data = path; + fileset.real_data.replace_filename(base_filename.string().append("_r.txt")); - auto base_filename = path.filename(); - - fileset.real_data = path; - fileset.real_data.replace_filename(base_filename.string().append("_r.txt")); - - fileset.imaginary_data = path; - fileset.imaginary_data.replace_filename(base_filename.string().append("_i.txt")); - - return fileset; - } + fileset.imaginary_data = path; + fileset.imaginary_data.replace_filename(base_filename.string().append("_i.txt")); - std::vector load_ascii_binary_fft_data(const std::filesystem::path &path) { - - auto fileset = get_fileset_from_base_path(path); - - std::ifstream real_component_stream(fileset.real_data); - if (!real_component_stream) { - throw std::runtime_error("Failed to open file_stream for " + path.string()); - } - std::ifstream imaginary_component_stream(fileset.imaginary_data); - if (!imaginary_component_stream) { - throw std::runtime_error("Failed to open file_stream for " + path.string()); + return fileset; } - std::vector fft_data; + std::vector load_ascii_binary_fft_data(const std::filesystem::path &path) { - while (real_component_stream.good() && imaginary_component_stream.good()) { - // Read the ascii bit field values from both files - std::bitset<32> real_component_bits; - real_component_stream >> real_component_bits; - std::bitset<32> imaginary_component_bits; - imaginary_component_stream >> imaginary_component_bits; + auto fileset = get_dataset_from_base_path(path); - // Handle the end-of-file case (one file case and both files case) - if (real_component_stream.eof() != imaginary_component_stream.eof()) { - throw std::runtime_error( - "Error when parsing FFT data files; reached end of one file while more data remains in the other"); + std::ifstream real_component_stream(fileset.real_data); + if (!real_component_stream) { + throw std::runtime_error("Failed to open file_stream for " + path.string()); } - if (real_component_stream.eof() && imaginary_component_stream.eof()) { - break; + std::ifstream imaginary_component_stream(fileset.imaginary_data); + if (!imaginary_component_stream) { + throw std::runtime_error("Failed to open file_stream for " + path.string()); } - // If we didn't reach the end of the file, add the data values to the vector - fft_data.push_back(float_from(real_component_bits)); - fft_data.push_back(float_from(imaginary_component_bits)); - } + std::vector fft_data; + + while (real_component_stream.good() && imaginary_component_stream.good()) { + // Read the ascii bit field values from both files + std::bitset<32> real_component_bits; + real_component_stream >> real_component_bits; + std::bitset<32> imaginary_component_bits; + imaginary_component_stream >> imaginary_component_bits; + + // Handle the end-of-file case (one file case and both files case) + if (real_component_stream.eof() != imaginary_component_stream.eof()) { + throw std::runtime_error( + "Error when parsing FFT data files; reached end of one file while more data remains in the other"); + } + if (real_component_stream.eof() && imaginary_component_stream.eof()) { + break; + } + + // If we didn't reach the end of the file, add the data values to the vector + fft_data.push_back(float_from(real_component_bits)); + fft_data.push_back(float_from(imaginary_component_bits)); + } - return fft_data; + return fft_data; + } } - std::vector calculate_worker_result(const config_data& config, const std::vector& input_data) { + std::vector calculate_worker_result(const config_data &config, const std::vector &input_data) { using fft_worker = sem::fft_accel::Worker; Component client_component("FPGA_FFT_testing_client"); @@ -157,17 +151,17 @@ namespace sem::fft_accel::test { return fft_client->calculate_fft(input_data); } - bool compare_result(std::vector actual, std::vector expected) { + bool compare_and_print_mismatches(std::vector actual, std::vector expected) { if (actual.size() != expected.size()) { std::cerr << "Size of calculated FFT result doesn't match size of expected result" << std::endl; return false; } bool mismatch_encountered = false; - for (size_t index=0; index < actual.size(); index++) { + for (size_t index = 0; index < actual.size(); index++) { if (actual.at(index) != expected.at(index)) { std::cerr << "Calculated result doesn't match with expected at index " << index << ": " - << actual.at(index) << " vs " << expected.at(index) << std::endl; + << actual.at(index) << " vs " << expected.at(index) << std::endl; mismatch_encountered = true; } } @@ -180,21 +174,23 @@ namespace sem::fft_accel::test { int main(int argc, char **argv) { using namespace sem::fft_accel::test; - auto config_result = parse_cmd_args(argc, argv); + auto config_result = cmd::parse_args(argc, argv); if (!config_result.has_value()) { return 1; } config_data config = config_result.value(); - auto fft_data = load_ascii_binary_fft_data(config.input_data_path); + auto fft_input_data = file::load_ascii_binary_fft_data(config.input_data_path); try { - auto output_data = calculate_worker_result(config, fft_data); + auto calculated_result = calculate_worker_result(config, fft_input_data); if (config.expected_result_path.has_value()) { - auto expected_result = load_ascii_binary_fft_data(config.expected_result_path.value()); - compare_result(output_data, expected_result); + auto expected_result = file::load_ascii_binary_fft_data(config.expected_result_path.value()); + + compare_and_print_mismatches(calculated_result, expected_result); } + } catch (const std::exception &ex) { std::cerr << "An exception was thrown from a RE system:" << std::endl << ex.what() << std::endl; From 2dd5f07f0bcf65e033e4204e401049a38f1b0b75 Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Tue, 2 Mar 2021 18:03:32 +1030 Subject: [PATCH 03/14] Switch to boost filesystem for compiler backwards compatibility --- .../fft_accel/test/fpga_client/CMakeLists.txt | 8 +++++- .../fft_accel/test/fpga_client/fft_client.cpp | 25 +++++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/re/src/workers/fft_accel/test/fpga_client/CMakeLists.txt b/re/src/workers/fft_accel/test/fpga_client/CMakeLists.txt index 9c6926acd..3fc0ee49f 100644 --- a/re/src/workers/fft_accel/test/fpga_client/CMakeLists.txt +++ b/re/src/workers/fft_accel/test/fpga_client/CMakeLists.txt @@ -1,8 +1,13 @@ -find_package(Boost 1.67.0 REQUIRED COMPONENTS program_options) +find_package(Boost 1.67.0 REQUIRED COMPONENTS program_options filesystem) add_executable(fpga_fft_client) +target_compile_features(fpga_fft_client + PUBLIC + cxx_std_17 + ) + target_sources(fpga_fft_client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/fft_client.cpp @@ -12,4 +17,5 @@ target_link_libraries(fpga_fft_client PRIVATE sem_fft_accel_fpga_worker Boost::program_options + Boost::filesystem ) \ No newline at end of file diff --git a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp index 20e05d4a9..5cde2b54c 100644 --- a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp +++ b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp @@ -3,12 +3,12 @@ // #include "boost/program_options.hpp" +#include "boost/filesystem.hpp" #include "component.h" #include "print_logger.h" #include "sem_fft_accel_worker.hpp" -#include #include #include #include @@ -69,34 +69,39 @@ namespace sem::fft_accel::test { } namespace file { + namespace filesystem = boost::filesystem; + struct fft_dataset { - std::filesystem::path real_data; - std::filesystem::path imaginary_data; + filesystem::path real_data; + filesystem::path imaginary_data; }; - fft_dataset get_dataset_from_base_path(const std::filesystem::path &path) { + fft_dataset get_dataset_from_base_path(const filesystem::path &path) { fft_dataset fileset; - auto base_filename = path.filename(); + auto real_filename = path.filename(); + real_filename += "_r.txt"; + auto imag_filename = path.filename(); + imag_filename += "_i.txt"; fileset.real_data = path; - fileset.real_data.replace_filename(base_filename.string().append("_r.txt")); + fileset.real_data.remove_filename() += real_filename; fileset.imaginary_data = path; - fileset.imaginary_data.replace_filename(base_filename.string().append("_i.txt")); + fileset.imaginary_data.remove_filename() += imag_filename; return fileset; } - std::vector load_ascii_binary_fft_data(const std::filesystem::path &path) { + std::vector load_ascii_binary_fft_data(const filesystem::path &path) { auto fileset = get_dataset_from_base_path(path); - std::ifstream real_component_stream(fileset.real_data); + std::ifstream real_component_stream(fileset.real_data.string()); if (!real_component_stream) { throw std::runtime_error("Failed to open file_stream for " + path.string()); } - std::ifstream imaginary_component_stream(fileset.imaginary_data); + std::ifstream imaginary_component_stream(fileset.imaginary_data.string()); if (!imaginary_component_stream) { throw std::runtime_error("Failed to open file_stream for " + path.string()); } From e13beae234ff96155ffd0148586acf0df7223b96 Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Wed, 3 Mar 2021 11:43:34 +1030 Subject: [PATCH 04/14] remove boost::optional::has_value reference for older boost API compatibility --- re/src/workers/fft_accel/test/fpga_client/fft_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp index 5cde2b54c..bbdd7f303 100644 --- a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp +++ b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp @@ -190,7 +190,7 @@ int main(int argc, char **argv) { try { auto calculated_result = calculate_worker_result(config, fft_input_data); - if (config.expected_result_path.has_value()) { + if (config.expected_result_path) { auto expected_result = file::load_ascii_binary_fft_data(config.expected_result_path.value()); compare_and_print_mismatches(calculated_result, expected_result); From bfd3449e46c554df129096ffa0a4863d74e5b3d5 Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Fri, 19 Mar 2021 14:13:14 +1030 Subject: [PATCH 05/14] Added endian conversion for float arrays --- re/src/workers/fft_accel/data/fft_data.cpp | 6 ++++++ re/src/workers/fft_accel/data/fft_data.hpp | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/re/src/workers/fft_accel/data/fft_data.cpp b/re/src/workers/fft_accel/data/fft_data.cpp index cf1bf1586..11ec3e8ee 100644 --- a/re/src/workers/fft_accel/data/fft_data.cpp +++ b/re/src/workers/fft_accel/data/fft_data.cpp @@ -4,6 +4,8 @@ #include "fft_data.hpp" +#include + using namespace sem::fft_accel::data; template<> @@ -16,6 +18,10 @@ fft_data_packet::fft_data_packet(SerializedPacket &serialized_data) : auto data_span = bytes.subspan(SerializedPacket::header_byte_length); fft_data_.resize(data_span.size() / sizeof(float)); memcpy(&(*fft_data_.begin()), &(*data_span.begin()), data_span.size()); + + for (auto& sample_value : fft_data_) { + boost::endian::big_to_native_inplace(sample_value); + } } template<> diff --git a/re/src/workers/fft_accel/data/fft_data.hpp b/re/src/workers/fft_accel/data/fft_data.hpp index 6e8cbb4cd..6db24ed64 100644 --- a/re/src/workers/fft_accel/data/fft_data.hpp +++ b/re/src/workers/fft_accel/data/fft_data.hpp @@ -13,6 +13,7 @@ #include "packet_headers.hpp" #include +#include "boost/endian/conversion.hpp" namespace sem::fft_accel::data { @@ -123,10 +124,17 @@ namespace sem::fft_accel::data { data_.at(2) = static_cast(native_packet.request_id()); // 3rd byte currently unused, formerly used for CSR // TODO: Thoroughly investigate endianness of remaining fields to be serialized - data_.at(4) = static_cast(native_packet.payload_data().size()); + uint16_t size_as_big_endian = boost::endian::native_to_big(native_packet.payload_data().size()); + reinterpret_cast(data_.at(4)) = size_as_big_endian; constexpr auto payload_byte_length = NumElements * sizeof(SampleType); memcpy(&(*(data_.begin() + 6)), &(*native_packet.payload_data().begin()), payload_byte_length); + + // Note: the &(*(iterator)) syntax is needed for MSVC compatibility + auto float_view = reinterpret_cast*>(&(*(data_.begin() + 6))); + for (auto& sample_value : *float_view) { + boost::endian::native_to_big_inplace(sample_value); + } } template From e4a7567009a30489b93dc0b81cddbe051b300fab Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Fri, 19 Mar 2021 14:41:09 +1030 Subject: [PATCH 06/14] Reinterpreting floats as uint for endian conversions --- re/src/workers/fft_accel/data/fft_data.cpp | 2 +- re/src/workers/fft_accel/data/fft_data.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/re/src/workers/fft_accel/data/fft_data.cpp b/re/src/workers/fft_accel/data/fft_data.cpp index 11ec3e8ee..599a1d808 100644 --- a/re/src/workers/fft_accel/data/fft_data.cpp +++ b/re/src/workers/fft_accel/data/fft_data.cpp @@ -20,7 +20,7 @@ fft_data_packet::fft_data_packet(SerializedPacket &serialized_data) : memcpy(&(*fft_data_.begin()), &(*data_span.begin()), data_span.size()); for (auto& sample_value : fft_data_) { - boost::endian::big_to_native_inplace(sample_value); + boost::endian::big_to_native_inplace(reinterpret_cast(sample_value)); } } diff --git a/re/src/workers/fft_accel/data/fft_data.hpp b/re/src/workers/fft_accel/data/fft_data.hpp index 6db24ed64..eeb6f6c36 100644 --- a/re/src/workers/fft_accel/data/fft_data.hpp +++ b/re/src/workers/fft_accel/data/fft_data.hpp @@ -131,7 +131,7 @@ namespace sem::fft_accel::data { memcpy(&(*(data_.begin() + 6)), &(*native_packet.payload_data().begin()), payload_byte_length); // Note: the &(*(iterator)) syntax is needed for MSVC compatibility - auto float_view = reinterpret_cast*>(&(*(data_.begin() + 6))); + auto float_view = reinterpret_cast*>(&(*(data_.begin() + 6))); for (auto& sample_value : *float_view) { boost::endian::native_to_big_inplace(sample_value); } From 54fb4017a22d249628b363ccdd965257ab90aa40 Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Fri, 19 Mar 2021 15:34:56 +1030 Subject: [PATCH 07/14] Added missing 16 bit cast for FFT size packet header field --- re/src/workers/fft_accel/data/fft_data.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/re/src/workers/fft_accel/data/fft_data.hpp b/re/src/workers/fft_accel/data/fft_data.hpp index eeb6f6c36..f168946cb 100644 --- a/re/src/workers/fft_accel/data/fft_data.hpp +++ b/re/src/workers/fft_accel/data/fft_data.hpp @@ -124,7 +124,7 @@ namespace sem::fft_accel::data { data_.at(2) = static_cast(native_packet.request_id()); // 3rd byte currently unused, formerly used for CSR // TODO: Thoroughly investigate endianness of remaining fields to be serialized - uint16_t size_as_big_endian = boost::endian::native_to_big(native_packet.payload_data().size()); + uint16_t size_as_big_endian = boost::endian::native_to_big(static_cast(native_packet.payload_data().size())); reinterpret_cast(data_.at(4)) = size_as_big_endian; constexpr auto payload_byte_length = NumElements * sizeof(SampleType); From e2669d5e584deaf0e7302a3089b9b3801e3c2d6c Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Thu, 25 Mar 2021 15:37:06 +1030 Subject: [PATCH 08/14] FFT size driven from vector input rather than max packet size --- re/src/workers/fft_accel/data/fft_data.hpp | 4 ++-- .../fft_accel/data/test/fft_data_tests.cpp | 3 ++- .../fft_accel/test/fpga_client/fft_client.cpp | 20 +++++++++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/re/src/workers/fft_accel/data/fft_data.hpp b/re/src/workers/fft_accel/data/fft_data.hpp index f168946cb..ef6402790 100644 --- a/re/src/workers/fft_accel/data/fft_data.hpp +++ b/re/src/workers/fft_accel/data/fft_data.hpp @@ -69,9 +69,9 @@ namespace sem::fft_accel::data { using SerializedPacket = serialized_fft_data; fft_data_packet(const data::vector_range &data_range, uint8_t request_id, - uint8_t sequence_num) : + uint8_t sequence_num, uint16_t fft_request_size) : fft_data_(data_range.begin(), data_range.end()), - header_data_(request_id, sequence_num, max_elements){ + header_data_(request_id, sequence_num, fft_request_size){ if (fft_data_.size() > max_elements) { throw std::invalid_argument("Attempting to create fft_data_packet from vector that is too large"); } diff --git a/re/src/workers/fft_accel/data/test/fft_data_tests.cpp b/re/src/workers/fft_accel/data/test/fft_data_tests.cpp index 4528c217e..e427a9b63 100644 --- a/re/src/workers/fft_accel/data/test/fft_data_tests.cpp +++ b/re/src/workers/fft_accel/data/test/fft_data_tests.cpp @@ -20,7 +20,8 @@ TEST(fft_accel_worker_data_packet, constructor_nothrow) { data_packet = std::make_unique>( payload_range, get_random(), - get_random() + get_random(), + payload_data.size()/2 ); ); ASSERT_NE(data_packet, nullptr); diff --git a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp index bbdd7f303..b9199cbee 100644 --- a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp +++ b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp @@ -19,6 +19,7 @@ namespace sem::fft_accel::test { std::string fae_endpoint; std::string input_data_path; boost::optional expected_result_path; + uint num_repeats }; namespace cmd { @@ -38,7 +39,9 @@ namespace sem::fft_accel::test { ("input_data,i", prog_opts::value(&data.input_data_path)->required(), "Specify the base filepath for the input fft data ('_r.txt' and '_i.txt' will be appended)") ("expected_result,x", prog_opts::value(&data.expected_result_path), - "Specify the base filepath for the expected fft result ('_r.txt' and '_i.txt' will be appended)"); + "Specify the base filepath for the expected fft result ('_r.txt' and '_i.txt' will be appended)") + ("num_repetitions,n", prog_opts::value(&data.num_repeats)->default_value(1), + "Specify the number of FFT calculation requests that should be made of using the supplied data"); prog_opts::options_description cmd_options; cmd_options.add(generic).add(config); @@ -187,13 +190,18 @@ int main(int argc, char **argv) { auto fft_input_data = file::load_ascii_binary_fft_data(config.input_data_path); - try { - auto calculated_result = calculate_worker_result(config, fft_input_data); - if (config.expected_result_path) { - auto expected_result = file::load_ascii_binary_fft_data(config.expected_result_path.value()); + if (config.expected_result_path) { + auto expected_result = file::load_ascii_binary_fft_data(config.expected_result_path.value()); + } - compare_and_print_mismatches(calculated_result, expected_result); + try { + for (int repetition_count = 0; repetition_count < config.num_repeats; repetition_count++) {} + auto calculated_result = calculate_worker_result(config, fft_input_data); + + if (config.expected_result_path) { + compare_and_print_mismatches(calculated_result, expected_result); + } } } catch (const std::exception &ex) { From bc6fbc7ba45877cddc28a973ee10be55d433a42e Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Thu, 25 Mar 2021 15:37:35 +1030 Subject: [PATCH 09/14] FPGA test client now has convenient repetition through CLI --- re/src/workers/fft_accel/data/fft_packet_group.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/re/src/workers/fft_accel/data/fft_packet_group.hpp b/re/src/workers/fft_accel/data/fft_packet_group.hpp index 141b0aaa2..d395f6bf2 100644 --- a/re/src/workers/fft_accel/data/fft_packet_group.hpp +++ b/re/src/workers/fft_accel/data/fft_packet_group.hpp @@ -77,7 +77,7 @@ namespace sem::fft_accel::data { data::vector_range data_segment{fft_data, segment_start, segment_end}; - packets_.emplace_back(data_segment, request_id, cur_index); + packets_.emplace_back(data_segment, request_id, cur_index, fft_data.size()/2); } } From 2d19eb7342077e4d6a473d8ef2f634ffb698a8f3 Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Fri, 26 Mar 2021 16:39:21 +1030 Subject: [PATCH 10/14] FFT size now driven by total vec input size --- .../workers/fft_accel/data/test/data_test_util.hpp | 13 ++++++++----- .../runtime/test/runtime_adapter_tests.cpp | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/re/src/workers/fft_accel/data/test/data_test_util.hpp b/re/src/workers/fft_accel/data/test/data_test_util.hpp index 2148e0db2..15a041b6f 100644 --- a/re/src/workers/fft_accel/data/test/data_test_util.hpp +++ b/re/src/workers/fft_accel/data/test/data_test_util.hpp @@ -34,7 +34,7 @@ namespace sem::fft_accel::data::test { static std::uniform_int_distribution dis( std::numeric_limits::min(), std::numeric_limits::max() - ); + ); #endif return dis(e); } @@ -66,16 +66,19 @@ namespace sem::fft_accel::data::test { } template - inline fft_data_packet generate_random_fft_data_packet() { + inline fft_data_packet generate_random_fft_data_packet() { + auto &&vec_data = generate_random_single_packet_fft_vec_data(); + auto num_samples = static_cast(vec_data.size() / 2); return { - generate_random_single_packet_fft_vec_data(), + vec_data, + get_random(), get_random(), - get_random() + num_samples, }; } template - inline serialized_fft_data generate_random_serialized_data_packet() { + inline serialized_fft_data generate_random_serialized_data_packet() { return serialized_fft_data( generate_random_array::packet_byte_size>() ); diff --git a/re/src/workers/fft_accel/runtime/test/runtime_adapter_tests.cpp b/re/src/workers/fft_accel/runtime/test/runtime_adapter_tests.cpp index 1957a66ff..a52f64d0d 100644 --- a/re/src/workers/fft_accel/runtime/test/runtime_adapter_tests.cpp +++ b/re/src/workers/fft_accel/runtime/test/runtime_adapter_tests.cpp @@ -143,7 +143,8 @@ AssertionResult map_can_construct_then_receive(size_t num_packets) { response_packets.emplace_back( data::vector_range{data::test::generate_random_fft_data(packet_type::max_elements)}, constructed_request_id, - sequence_num); + sequence_num, + input_data.size()/2); } // Send responses for all packets apart from the last, expecting no error, but result should contain nullopt (request not yet fulfilled) From 426d4f56a10fb7f0700b116379aad7310b314d65 Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Fri, 26 Mar 2021 17:07:00 +1030 Subject: [PATCH 11/14] Added CLI option for number of repeated --- .../fft_accel/test/fpga_client/fft_client.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp index b9199cbee..6a1a3d1f9 100644 --- a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp +++ b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp @@ -19,7 +19,7 @@ namespace sem::fft_accel::test { std::string fae_endpoint; std::string input_data_path; boost::optional expected_result_path; - uint num_repeats + uint32_t num_repeats{1}; }; namespace cmd { @@ -156,7 +156,10 @@ namespace sem::fft_accel::test { throw std::runtime_error("Client component failed to configure"); } - return fft_client->calculate_fft(input_data); + auto&& result = fft_client->calculate_fft(input_data); + + fft_client->Terminate(); + return result; } bool compare_and_print_mismatches(std::vector actual, std::vector expected) { @@ -191,12 +194,13 @@ int main(int argc, char **argv) { auto fft_input_data = file::load_ascii_binary_fft_data(config.input_data_path); + std::vector expected_result; if (config.expected_result_path) { - auto expected_result = file::load_ascii_binary_fft_data(config.expected_result_path.value()); + expected_result = file::load_ascii_binary_fft_data(config.expected_result_path.value()); } try { - for (int repetition_count = 0; repetition_count < config.num_repeats; repetition_count++) {} + for (int repetition_count = 0; repetition_count < config.num_repeats; repetition_count++) { auto calculated_result = calculate_worker_result(config, fft_input_data); if (config.expected_result_path) { From ba9b3d577c682b4cf4cf3f19dc5c33dcf0e9087d Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Sat, 27 Mar 2021 11:28:18 +1030 Subject: [PATCH 12/14] Fixed mutex-related crashes --- .../fft_accel/runtime/single_thread_dispatcher.hpp | 13 ++++++++----- re/src/workers/fft_accel/sem_fft_accel_worker.cpp | 3 +++ re/src/workers/fft_accel/sem_fft_accel_worker.hpp | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/re/src/workers/fft_accel/runtime/single_thread_dispatcher.hpp b/re/src/workers/fft_accel/runtime/single_thread_dispatcher.hpp index 68f0d550a..8cbfc51cb 100644 --- a/re/src/workers/fft_accel/runtime/single_thread_dispatcher.hpp +++ b/re/src/workers/fft_accel/runtime/single_thread_dispatcher.hpp @@ -52,8 +52,9 @@ namespace sem::fft_accel::runtime { template single_thread_dispatcher::single_thread_dispatcher() : - should_continue_running_(true), - dispatch_thread_([this]() { run_dispatch_loop(); }) {} + should_continue_running_(true) { + dispatch_thread_ = std::thread([this]() { run_dispatch_loop(); }); + } template single_thread_dispatcher::~single_thread_dispatcher() { @@ -63,8 +64,10 @@ namespace sem::fft_accel::runtime { template void single_thread_dispatcher::stop() { - std::lock_guard queue_lock(queue_mutex_); - should_continue_running_ = false; + { + std::lock_guard queue_lock(queue_mutex_); + should_continue_running_ = false; + } // Ensure that the queue variable gets the signal to stop waiting for new events event_occurred_.notify_all(); } @@ -122,7 +125,7 @@ namespace sem::fft_accel::runtime { template void single_thread_dispatcher::run_dispatch_loop() { - while (should_continue_running_.load()) { + while (should_continue_running_) { std::unique_lock queue_lock(queue_mutex_); if (!event_queue_.empty()) { diff --git a/re/src/workers/fft_accel/sem_fft_accel_worker.cpp b/re/src/workers/fft_accel/sem_fft_accel_worker.cpp index 03dd465be..ae7ab0c07 100644 --- a/re/src/workers/fft_accel/sem_fft_accel_worker.cpp +++ b/re/src/workers/fft_accel/sem_fft_accel_worker.cpp @@ -30,6 +30,9 @@ sem::fft_accel::Worker::Worker(const BehaviourContainer &container, const std::s } } +sem::fft_accel::Worker::~Worker() { + Terminate(); +} void sem::fft_accel::Worker::HandleConfigure() { auto endpoint = GetAttribute(std::string(AttrNames::accelerator_endpoint)).lock(); diff --git a/re/src/workers/fft_accel/sem_fft_accel_worker.hpp b/re/src/workers/fft_accel/sem_fft_accel_worker.hpp index 06826b0b1..21a3cafa8 100644 --- a/re/src/workers/fft_accel/sem_fft_accel_worker.hpp +++ b/re/src/workers/fft_accel/sem_fft_accel_worker.hpp @@ -16,6 +16,7 @@ class Worker : public ::Worker { using callback_func_type = std::function; Worker(const BehaviourContainer &container, const std::string &inst_name); + ~Worker(); const std::string &get_version() const override; From 1d53fb43a52f735307e3929a4eacde19e2ac4bce Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Tue, 30 Mar 2021 12:04:15 +1030 Subject: [PATCH 13/14] Added async and fixed remaining FFT sizing issues --- re/src/workers/fft_accel/data/fft_data.cpp | 3 + re/src/workers/fft_accel/data/fft_data.hpp | 4 +- .../fft_accel/test/fpga_client/fft_client.cpp | 72 +++++++++++-------- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/re/src/workers/fft_accel/data/fft_data.cpp b/re/src/workers/fft_accel/data/fft_data.cpp index 599a1d808..4722c0784 100644 --- a/re/src/workers/fft_accel/data/fft_data.cpp +++ b/re/src/workers/fft_accel/data/fft_data.cpp @@ -30,5 +30,8 @@ uint8_t fft_data_packet::sequence_number() const { return header_data_.se template<> uint8_t fft_data_packet::request_id() const { return header_data_.request_id(); }; +template<> +uint16_t fft_data_packet::fft_size() const { return header_data_.fft_size(); }; + template<> const std::vector &fft_data_packet::payload_data() const { return fft_data_; }; diff --git a/re/src/workers/fft_accel/data/fft_data.hpp b/re/src/workers/fft_accel/data/fft_data.hpp index ef6402790..646ee88e3 100644 --- a/re/src/workers/fft_accel/data/fft_data.hpp +++ b/re/src/workers/fft_accel/data/fft_data.hpp @@ -83,6 +83,8 @@ namespace sem::fft_accel::data { [[nodiscard]] uint8_t request_id() const; + [[nodiscard]] uint16_t fft_size() const; + [[nodiscard]] const std::vector &payload_data() const; private: @@ -124,7 +126,7 @@ namespace sem::fft_accel::data { data_.at(2) = static_cast(native_packet.request_id()); // 3rd byte currently unused, formerly used for CSR // TODO: Thoroughly investigate endianness of remaining fields to be serialized - uint16_t size_as_big_endian = boost::endian::native_to_big(static_cast(native_packet.payload_data().size())); + uint16_t size_as_big_endian = boost::endian::native_to_big(static_cast(native_packet.fft_size())); reinterpret_cast(data_.at(4)) = size_as_big_endian; constexpr auto payload_byte_length = NumElements * sizeof(SampleType); diff --git a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp index 6a1a3d1f9..96f11549f 100644 --- a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp +++ b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace sem::fft_accel::test { @@ -20,6 +21,7 @@ namespace sem::fft_accel::test { std::string input_data_path; boost::optional expected_result_path; uint32_t num_repeats{1}; + bool send_async; }; namespace cmd { @@ -41,7 +43,9 @@ namespace sem::fft_accel::test { ("expected_result,x", prog_opts::value(&data.expected_result_path), "Specify the base filepath for the expected fft result ('_r.txt' and '_i.txt' will be appended)") ("num_repetitions,n", prog_opts::value(&data.num_repeats)->default_value(1), - "Specify the number of FFT calculation requests that should be made of using the supplied data"); + "Specify the number of FFT calculation requests that should be made of using the supplied data") + ("send_async,a", prog_opts::bool_switch(&data.send_async)->default_value(false), + "Set to true to send packets in parallel, by default will send in serial"); prog_opts::options_description cmd_options; cmd_options.add(generic).add(config); @@ -136,32 +140,6 @@ namespace sem::fft_accel::test { } } - std::vector calculate_worker_result(const config_data &config, const std::vector &input_data) { - using fft_worker = sem::fft_accel::Worker; - - Component client_component("FPGA_FFT_testing_client"); - auto fft_client = client_component.AddTypedWorker("test_fft_client_worker"); - - Print::Logger print_logger; - fft_client->logger().AddLogger(print_logger); - - auto attr_ref = fft_client->GetAttribute(std::string(fft_worker::AttrNames::accelerator_endpoint)); - if (auto endpoint_attr = attr_ref.lock()) { - endpoint_attr->SetValue(config.fae_endpoint); - } else { - throw std::runtime_error("Failed to acquire lock on FAE endpoint attribute"); - } - - if (!client_component.Configure()) { - throw std::runtime_error("Client component failed to configure"); - } - - auto&& result = fft_client->calculate_fft(input_data); - - fft_client->Terminate(); - return result; - } - bool compare_and_print_mismatches(std::vector actual, std::vector expected) { if (actual.size() != expected.size()) { std::cerr << "Size of calculated FFT result doesn't match size of expected result" << std::endl; @@ -184,6 +162,7 @@ namespace sem::fft_accel::test { int main(int argc, char **argv) { using namespace sem::fft_accel::test; + using fft_worker = sem::fft_accel::Worker; auto config_result = cmd::parse_args(argc, argv); if (!config_result.has_value()) { @@ -200,14 +179,45 @@ int main(int argc, char **argv) { } try { - for (int repetition_count = 0; repetition_count < config.num_repeats; repetition_count++) { - auto calculated_result = calculate_worker_result(config, fft_input_data); + Component client_component("FPGA_FFT_testing_client"); + auto fft_client = client_component.AddTypedWorker("test_fft_client_worker"); + + Print::Logger print_logger; + fft_client->logger().AddLogger(print_logger); + + auto attr_ref = fft_client->GetAttribute(std::string(fft_worker::AttrNames::accelerator_endpoint)); + if (auto endpoint_attr = attr_ref.lock()) { + endpoint_attr->SetValue(config.fae_endpoint); + } else { + throw std::runtime_error("Failed to acquire lock on FAE endpoint attribute"); + } + + if (!client_component.Configure()) { + throw std::runtime_error("Client component failed to configure"); + } - if (config.expected_result_path) { - compare_and_print_mismatches(calculated_result, expected_result); + if (config.send_async) { + fft_client->SetResponseCallback([&expected_result](uint8_t fft_id, std::vector data) { + std::cout << "Received packet with FFT_REQUEST_ID = " << fft_id << std::endl; + compare_and_print_mismatches(std::move(data), expected_result); + }); + } + + for (int repetition_count = 0; repetition_count < config.num_repeats; repetition_count++) { + if (config.send_async) { + int fft_id = fft_client->calculate_fft_async(fft_input_data); + std::cout << "Send packet with FFT_REQUEST_ID = " << fft_id << std::endl; + } else { + auto calculated_result = fft_client->calculate_fft(fft_input_data); + + if (config.expected_result_path) { + compare_and_print_mismatches(calculated_result, expected_result); + } } } + fft_client->Terminate(); + } catch (const std::exception &ex) { std::cerr << "An exception was thrown from a RE system:" << std::endl << ex.what() << std::endl; From 8a0ebbc699bb6b2ed8f7ae36d6d91d9c6760328b Mon Sep 17 00:00:00 2001 From: JacksonM8 Date: Tue, 30 Mar 2021 14:30:07 +1030 Subject: [PATCH 14/14] Fixed requests remaining tracked after fulfillment, async packets tracked in test client --- .../fft_accel/runtime/fft_request_map.hpp | 3 +- .../fft_accel/test/fpga_client/fft_client.cpp | 288 ++++++++++-------- 2 files changed, 155 insertions(+), 136 deletions(-) diff --git a/re/src/workers/fft_accel/runtime/fft_request_map.hpp b/re/src/workers/fft_accel/runtime/fft_request_map.hpp index 753519200..77621d658 100644 --- a/re/src/workers/fft_accel/runtime/fft_request_map.hpp +++ b/re/src/workers/fft_accel/runtime/fft_request_map.hpp @@ -162,7 +162,8 @@ namespace sem::fft_accel::runtime { } if (request.remaining_packets() == 0) { - auto &&fulfilled_request = staged_packet_groups_.at(request_id); + auto fulfilled_request = std::move(staged_packet_groups_.at(request_id)); + staged_packet_groups_.erase(request_id); unfulfilled_promises_.at(request_id).set_value(fulfilled_request); return {std::optional>({request_id, fulfilled_request.to_vector()})}; } else { diff --git a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp index 96f11549f..909170eef 100644 --- a/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp +++ b/re/src/workers/fft_accel/test/fpga_client/fft_client.cpp @@ -2,179 +2,189 @@ // Created by Jackson Michael on 1/3/21. // -#include "boost/program_options.hpp" #include "boost/filesystem.hpp" +#include "boost/program_options.hpp" #include "component.h" #include "print_logger.h" #include "sem_fft_accel_worker.hpp" -#include #include +#include #include #include namespace sem::fft_accel::test { - struct config_data { - std::string fae_endpoint; - std::string input_data_path; - boost::optional expected_result_path; - uint32_t num_repeats{1}; - bool send_async; - }; - - namespace cmd { - std::optional parse_args(int argc, char **argv) { - namespace prog_opts = boost::program_options; - - config_data data; - - prog_opts::options_description generic("Generic options"); - generic.add_options() - ("help,h", "Print a description of the available commands and exit"); - - prog_opts::options_description config("Client configuration options"); - config.add_options() - ("fae-endpoint,e", prog_opts::value(&data.fae_endpoint)->required(), - "Set the endpoint of the FFT Acceleration Engine this client should connect to") - ("input_data,i", prog_opts::value(&data.input_data_path)->required(), - "Specify the base filepath for the input fft data ('_r.txt' and '_i.txt' will be appended)") - ("expected_result,x", prog_opts::value(&data.expected_result_path), - "Specify the base filepath for the expected fft result ('_r.txt' and '_i.txt' will be appended)") - ("num_repetitions,n", prog_opts::value(&data.num_repeats)->default_value(1), - "Specify the number of FFT calculation requests that should be made of using the supplied data") - ("send_async,a", prog_opts::bool_switch(&data.send_async)->default_value(false), - "Set to true to send packets in parallel, by default will send in serial"); - - prog_opts::options_description cmd_options; - cmd_options.add(generic).add(config); - - prog_opts::variables_map variables_map; - prog_opts::store(prog_opts::parse_command_line(argc, argv, cmd_options), variables_map); - - if (variables_map.count("help")) { - std::cout << cmd_options << std::endl; - return {}; - } - - // At this point, check if the variables marked as required have been provided, throwing as needed - prog_opts::notify(variables_map); - - return data; - } +struct config_data { + std::string fae_endpoint; + std::string input_data_path; + boost::optional expected_result_path; + uint32_t num_repeats{1}; + bool send_async{false}; +}; + +namespace cmd { +std::optional parse_args(int argc, char** argv) +{ + namespace prog_opts = boost::program_options; + + config_data data; + + prog_opts::options_description generic("Generic options"); + generic.add_options()("help,h", "Print a description of the available commands and exit"); + + prog_opts::options_description config("Client configuration options"); + config.add_options()("fae-endpoint,e", prog_opts::value(&data.fae_endpoint)->required(), + "Set the endpoint of the FFT Acceleration Engine this client should " + "connect to")("input_data,i", + prog_opts::value(&data.input_data_path)->required(), + "Specify the base filepath for the input fft data ('_r.txt' " + "and '_i.txt' will be " + "appended)")("expected_result,x", + prog_opts::value(&data.expected_result_path), + "Specify the base filepath for the expected " + "fft result ('_r.txt' and '_i.txt' " + "will be appended)")( + "num_repetitions,n", prog_opts::value(&data.num_repeats)->default_value(1), + "Specify the number of FFT calculation requests that should be made of using the supplied " + "data")("send_async,a", prog_opts::bool_switch(&data.send_async)->default_value(false), + "Set to true to send packets in parallel, by default will send in serial"); + + prog_opts::options_description cmd_options; + cmd_options.add(generic).add(config); + + prog_opts::variables_map variables_map; + prog_opts::store(prog_opts::parse_command_line(argc, argv, cmd_options), variables_map); + + if(variables_map.count("help")) { + std::cout << cmd_options << std::endl; + return {}; } - /// Reinterpret an integer value as a float without performing any cast conversions - float float_from_bits(uint32_t value) { - return *reinterpret_cast(&value); - } + // At this point, check if the variables marked as required have been provided, throwing as + // needed + prog_opts::notify(variables_map); - /// Convert a bitset to the equivalent IEEE754 float (not broadly tested across may platforms) - float float_from(std::bitset<32> bits) { - return float_from_bits(bits.to_ulong()); - } - - namespace file { - namespace filesystem = boost::filesystem; + return data; +} +} // namespace cmd - struct fft_dataset { - filesystem::path real_data; - filesystem::path imaginary_data; - }; +/// Reinterpret an integer value as a float without performing any cast conversions +float float_from_bits(uint32_t value) +{ + return *reinterpret_cast(&value); +} - fft_dataset get_dataset_from_base_path(const filesystem::path &path) { - fft_dataset fileset; +/// Convert a bitset to the equivalent IEEE754 float (not broadly tested across may platforms) +float float_from(std::bitset<32> bits) +{ + return float_from_bits(bits.to_ulong()); +} - auto real_filename = path.filename(); - real_filename += "_r.txt"; - auto imag_filename = path.filename(); - imag_filename += "_i.txt"; +namespace file { +namespace filesystem = boost::filesystem; - fileset.real_data = path; - fileset.real_data.remove_filename() += real_filename; +struct fft_dataset { + filesystem::path real_data; + filesystem::path imaginary_data; +}; - fileset.imaginary_data = path; - fileset.imaginary_data.remove_filename() += imag_filename; +fft_dataset get_dataset_from_base_path(const filesystem::path& path) +{ + fft_dataset fileset; - return fileset; - } + auto real_filename = path.filename(); + real_filename += "_r.txt"; + auto imag_filename = path.filename(); + imag_filename += "_i.txt"; - std::vector load_ascii_binary_fft_data(const filesystem::path &path) { + fileset.real_data = path; + fileset.real_data.remove_filename() += real_filename; - auto fileset = get_dataset_from_base_path(path); + fileset.imaginary_data = path; + fileset.imaginary_data.remove_filename() += imag_filename; - std::ifstream real_component_stream(fileset.real_data.string()); - if (!real_component_stream) { - throw std::runtime_error("Failed to open file_stream for " + path.string()); - } - std::ifstream imaginary_component_stream(fileset.imaginary_data.string()); - if (!imaginary_component_stream) { - throw std::runtime_error("Failed to open file_stream for " + path.string()); - } + return fileset; +} - std::vector fft_data; +std::vector load_ascii_binary_fft_data(const filesystem::path& path) +{ + auto fileset = get_dataset_from_base_path(path); - while (real_component_stream.good() && imaginary_component_stream.good()) { - // Read the ascii bit field values from both files - std::bitset<32> real_component_bits; - real_component_stream >> real_component_bits; - std::bitset<32> imaginary_component_bits; - imaginary_component_stream >> imaginary_component_bits; + std::ifstream real_component_stream(fileset.real_data.string()); + if(!real_component_stream) { + throw std::runtime_error("Failed to open file_stream for " + path.string()); + } + std::ifstream imaginary_component_stream(fileset.imaginary_data.string()); + if(!imaginary_component_stream) { + throw std::runtime_error("Failed to open file_stream for " + path.string()); + } - // Handle the end-of-file case (one file case and both files case) - if (real_component_stream.eof() != imaginary_component_stream.eof()) { - throw std::runtime_error( - "Error when parsing FFT data files; reached end of one file while more data remains in the other"); - } - if (real_component_stream.eof() && imaginary_component_stream.eof()) { - break; - } + std::vector fft_data; - // If we didn't reach the end of the file, add the data values to the vector - fft_data.push_back(float_from(real_component_bits)); - fft_data.push_back(float_from(imaginary_component_bits)); - } + while(real_component_stream.good() && imaginary_component_stream.good()) { + // Read the ascii bit field values from both files + std::bitset<32> real_component_bits; + real_component_stream >> real_component_bits; + std::bitset<32> imaginary_component_bits; + imaginary_component_stream >> imaginary_component_bits; - return fft_data; + // Handle the end-of-file case (one file case and both files case) + if(real_component_stream.eof() != imaginary_component_stream.eof()) { + throw std::runtime_error("Error when parsing FFT data files; reached end of one file " + "while more data remains in the other"); } + if(real_component_stream.eof() && imaginary_component_stream.eof()) { + break; + } + + // If we didn't reach the end of the file, add the data values to the vector + fft_data.push_back(float_from(real_component_bits)); + fft_data.push_back(float_from(imaginary_component_bits)); } - bool compare_and_print_mismatches(std::vector actual, std::vector expected) { - if (actual.size() != expected.size()) { - std::cerr << "Size of calculated FFT result doesn't match size of expected result" << std::endl; - return false; - } + return fft_data; +} +} // namespace file + +bool compare_and_print_mismatches(std::vector actual, std::vector expected) +{ + if(actual.size() != expected.size()) { + std::cerr << "Size of calculated FFT result doesn't match size of expected result" + << std::endl; + return false; + } - bool mismatch_encountered = false; - for (size_t index = 0; index < actual.size(); index++) { - if (actual.at(index) != expected.at(index)) { - std::cerr << "Calculated result doesn't match with expected at index " << index << ": " - << actual.at(index) << " vs " << expected.at(index) << std::endl; - mismatch_encountered = true; - } + bool mismatch_encountered = false; + for(size_t index = 0; index < actual.size(); index++) { + if(actual.at(index) != expected.at(index)) { + std::cerr << "Calculated result doesn't match with expected at index " << index << ": " + << actual.at(index) << " vs " << expected.at(index) << std::endl; + mismatch_encountered = true; } - - return mismatch_encountered; } + return mismatch_encountered; } -int main(int argc, char **argv) { +} // namespace sem::fft_accel::test + +int main(int argc, char** argv) +{ using namespace sem::fft_accel::test; using fft_worker = sem::fft_accel::Worker; auto config_result = cmd::parse_args(argc, argv); - if (!config_result.has_value()) { + if(!config_result.has_value()) { return 1; } config_data config = config_result.value(); auto fft_input_data = file::load_ascii_binary_fft_data(config.input_data_path); - std::vector expected_result; - if (config.expected_result_path) { + if(config.expected_result_path) { expected_result = file::load_ascii_binary_fft_data(config.expected_result_path.value()); } @@ -185,32 +195,40 @@ int main(int argc, char **argv) { Print::Logger print_logger; fft_client->logger().AddLogger(print_logger); - auto attr_ref = fft_client->GetAttribute(std::string(fft_worker::AttrNames::accelerator_endpoint)); - if (auto endpoint_attr = attr_ref.lock()) { + auto attr_ref = fft_client->GetAttribute( + std::string(fft_worker::AttrNames::accelerator_endpoint)); + if(auto endpoint_attr = attr_ref.lock()) { endpoint_attr->SetValue(config.fae_endpoint); } else { throw std::runtime_error("Failed to acquire lock on FAE endpoint attribute"); } - if (!client_component.Configure()) { + if(!client_component.Configure()) { throw std::runtime_error("Client component failed to configure"); } - if (config.send_async) { - fft_client->SetResponseCallback([&expected_result](uint8_t fft_id, std::vector data) { - std::cout << "Received packet with FFT_REQUEST_ID = " << fft_id << std::endl; - compare_and_print_mismatches(std::move(data), expected_result); + std::set requested_ids; + if(config.send_async) { + fft_client->SetResponseCallback([&expected_result, &requested_ids]( + uint8_t fft_id, std::vector data) { + if(requested_ids.count(fft_id)) { + std::cout << "Received packet with FFT_REQUEST_ID = " << fft_id << std::endl; + compare_and_print_mismatches(std::move(data), expected_result); + requested_ids.erase(fft_id); + } else { + std::cout << "Received response ID not associated with a request" << std::endl; + } }); } - for (int repetition_count = 0; repetition_count < config.num_repeats; repetition_count++) { - if (config.send_async) { + for(int repetition_count = 0; repetition_count < config.num_repeats; repetition_count++) { + if(config.send_async) { int fft_id = fft_client->calculate_fft_async(fft_input_data); std::cout << "Send packet with FFT_REQUEST_ID = " << fft_id << std::endl; } else { auto calculated_result = fft_client->calculate_fft(fft_input_data); - if (config.expected_result_path) { + if(config.expected_result_path) { compare_and_print_mismatches(calculated_result, expected_result); } } @@ -218,7 +236,7 @@ int main(int argc, char **argv) { fft_client->Terminate(); - } catch (const std::exception &ex) { + } catch(const std::exception& ex) { std::cerr << "An exception was thrown from a RE system:" << std::endl << ex.what() << std::endl; return 1;