diff options
-rw-r--r-- | host/lib/convert/gen_convert_general.py | 73 | ||||
-rw-r--r-- | host/tests/convert_test.cpp | 133 |
2 files changed, 174 insertions, 32 deletions
diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index c4a8c9725..1b1106d4c 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -5,6 +5,9 @@ # # SPDX-License-Identifier: GPL-3.0-or-later # +""" +Auto-Generator for generic converters +""" TMPL_HEADER = """ <% @@ -16,6 +19,7 @@ TMPL_HEADER = """ #include "convert_common.hpp" #include <uhd/utils/byteswap.hpp> +#include <algorithm> using namespace uhd::convert; @@ -29,6 +33,48 @@ DECLARE_CONVERTER(item32, 1, item32, 1, PRIORITY_GENERAL) { } """ +TMPL_CONV_CHDR_SC16_TO_FP = """ +DECLARE_CONVERTER({fctype}, 1, sc16_chdr, 1, PRIORITY_GENERAL) {{ + // Note: We convert I and Q separately, because there's no optimized + // constructor to create a complex<{fptype}> from a complex<int16_t>. This + // means we need to multiply nsamps by 2 + const {fptype}* input = reinterpret_cast<const {fptype}*>(inputs[0]); + int16_t* output = reinterpret_cast<int16_t*>(outputs[0]); + + for (size_t i = 0; i < nsamps * 2; i += 2) {{ + output[i] = static_cast<int16_t>(input[i] * {fptype}(scale_factor)); + output[i+1] = static_cast<int16_t>(input[i+1] * {fptype}(scale_factor)); + }} +}} + +DECLARE_CONVERTER(sc16_chdr, 1, {fctype}, 1, PRIORITY_GENERAL) {{ + // Note: We convert I and Q separately, because there's no optimized + // constructor to create a complex<{fptype}> from a complex<int16_t>. This + // means we need to multiply nsamps by 2 + const int16_t* input = reinterpret_cast<const int16_t*>(inputs[0]); + {fptype}* output = reinterpret_cast<{fptype}*>(outputs[0]); + + for (size_t i = 0; i < nsamps * 2; i += 2) {{ + output[i] = static_cast<{fptype}>(input[i]) * {fptype}(scale_factor); + output[i+1] = static_cast<{fptype}>(input[i+1]) * {fptype}(scale_factor); + }} +}} +""" + +# For CHDR converters, all converters where the input and output type are the +# same can be done by a memcpy, because we can do the endianness adaptation in +# the FPGA. +TMPL_CONV_CHDR_MEMCPY = """ +DECLARE_CONVERTER({in_type}, 1, {out_type}, 1, PRIORITY_GENERAL) {{ + const {ptr_type} *input = reinterpret_cast<const {ptr_type} *>(inputs[0]); + {ptr_type}* output = reinterpret_cast<{ptr_type}*>(outputs[0]); + + // Benchmark shows that copy_n can be significantly slower in some cases + //std::copy_n(input, nsamps, output); + memcpy(output, input, sizeof({ptr_type}) * nsamps); +}} +""" + # Some 32-bit types converters are also defined in convert_item32.cpp to # take care of quirks such as I/Q ordering on the wire etc. TMPL_CONV_ITEM32 = """ @@ -177,10 +223,35 @@ def parse_tmpl(_tmpl_text, **kwargs): return Template(_tmpl_text).render(**kwargs) if __name__ == '__main__': - import sys, os + import sys + import os file = os.path.basename(__file__) output = parse_tmpl(TMPL_HEADER, file=file) + for fctype, fptype in ( + ('fc32', 'float'), + ('fc64', 'double'), + ): + output += TMPL_CONV_CHDR_SC16_TO_FP.format( + fctype=fctype, fptype=fptype) + + ## Generate CHDR converters + # These guys don't have to worry about endianness + for uhd_type, ptr_type in ( + ('u8', 'uint8_t'), + ('s8', 'int8_t'), + ('s16', 'int16_t'), + ('f32', 'float'), + ('sc8', 'std::complex<int8_t>'), + ('sc16', 'std::complex<int16_t>'), + ('fc32', 'std::complex<float>'), + ('fc64', 'std::complex<double>'), + ): + for in_type, out_type in ((uhd_type + '_chdr', uhd_type), (uhd_type, uhd_type + '_chdr')): + output += TMPL_CONV_CHDR_MEMCPY.format( + in_type=in_type, + out_type=out_type, + ptr_type=ptr_type) ## Generate all data types that are exactly ## item32 or multiples thereof: for end in ('be', 'le'): diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp index 67409e310..9bdfbef6c 100644 --- a/host/tests/convert_test.cpp +++ b/host/tests/convert_test.cpp @@ -110,6 +110,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_sc16) } } +BOOST_AUTO_TEST_CASE(test_convert_types_chdr_sc16) +{ + convert::id_type id; + id.input_format = "sc16"; + id.num_inputs = 1; + id.output_format = "sc16_chdr"; + id.num_outputs = 1; + + // try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++) { + test_convert_types_sc16(nsamps, id); + } +} + /*********************************************************************** * Test float conversion **********************************************************************/ @@ -175,6 +189,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_fc32) } } +BOOST_AUTO_TEST_CASE(test_convert_types_chdr_fc32) +{ + convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "sc16_chdr"; + id.num_outputs = 1; + + // try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++) { + test_convert_types_for_floats<fc32_t>(nsamps, id); + } +} + BOOST_AUTO_TEST_CASE(test_convert_types_be_fc64) { convert::id_type id; @@ -203,6 +231,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_fc64) } } +BOOST_AUTO_TEST_CASE(test_convert_types_chdr_fc64) +{ + convert::id_type id; + id.input_format = "fc64"; + id.num_inputs = 1; + id.output_format = "sc16_chdr"; + id.num_outputs = 1; + + // try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++) { + test_convert_types_for_floats<fc64_t>(nsamps, id); + } +} + /*********************************************************************** * Test float to/from sc12 conversion loopback **********************************************************************/ @@ -294,6 +336,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_be_fc32_with_fc32) } } +BOOST_AUTO_TEST_CASE(test_convert_types_fc32_with_fc32_chdr) +{ + convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "fc32_chdr"; + id.num_outputs = 1; + + // try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++) { + test_convert_types_for_floats<fc32_t>(nsamps, id); + } +} + /*********************************************************************** * Test float to short conversion loopback **********************************************************************/ @@ -488,6 +544,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_u8_and_u8) } } +BOOST_AUTO_TEST_CASE(test_convert_types_u8_and_u8_chdr) +{ + convert::id_type id; + id.input_format = "u8"; + id.output_format = "u8_chdr"; + id.num_inputs = 1; + id.num_outputs = 1; + + // try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++) { + test_convert_types_u8(nsamps, id); + } +} + /*********************************************************************** * Test s8 conversion **********************************************************************/ @@ -528,6 +598,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_s8_and_s8) } } +BOOST_AUTO_TEST_CASE(test_convert_types_s8_and_s8_chdr) +{ + convert::id_type id; + id.input_format = "s8"; + id.output_format = "s8_chdr"; + id.num_inputs = 1; + id.num_outputs = 1; + + // try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++) { + test_convert_types_s8(nsamps, id); + } +} + /*********************************************************************** * Test s16 conversion **********************************************************************/ @@ -568,6 +652,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_s16_and_s16) } } +BOOST_AUTO_TEST_CASE(test_convert_types_s16_and_s16_chdr) +{ + convert::id_type id; + id.input_format = "s16"; + id.output_format = "s16_chdr"; + id.num_inputs = 1; + id.num_outputs = 1; + + // try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++) { + test_convert_types_s16(nsamps, id); + } +} + /*********************************************************************** * Test fc32 -> fc32 conversion **********************************************************************/ @@ -611,43 +709,16 @@ BOOST_AUTO_TEST_CASE(test_convert_types_fc32_and_fc32) } } -/*********************************************************************** - * Test f32 -> f32 conversion - **********************************************************************/ -static void test_convert_types_f32(size_t nsamps, convert::id_type& id) -{ - // fill the input samples - std::vector<float> input(nsamps), output(nsamps); - for (float& in : input) - in = float((float(std::rand()) / float(RAND_MAX / 2)) - 1); - - // run the loopback and test - convert::id_type in_id = id; - convert::id_type out_id = id; - std::swap(out_id.input_format, out_id.output_format); - std::swap(out_id.num_inputs, out_id.num_outputs); - loopback(nsamps, in_id, out_id, input, output); - for (size_t i = 0; i < nsamps; i++) { - MY_CHECK_CLOSE(input[i], output[i], float(1. / (1 << 16))); - } -} - -BOOST_AUTO_TEST_CASE(test_convert_types_f32_and_f32) +BOOST_AUTO_TEST_CASE(test_convert_types_fc32_and_fc32_chdr) { convert::id_type id; - id.input_format = "f32"; + id.input_format = "fc32"; + id.output_format = "fc32_chdr"; id.num_inputs = 1; id.num_outputs = 1; // try various lengths to test edge cases - id.output_format = "f32_item32_le"; - for (size_t nsamps = 1; nsamps < 16; nsamps++) { - test_convert_types_f32(nsamps, id); - } - - // try various lengths to test edge cases - id.output_format = "f32_item32_be"; for (size_t nsamps = 1; nsamps < 16; nsamps++) { - test_convert_types_f32(nsamps, id); + test_convert_types_fc32(nsamps, id); } } |