From e836f903ae5e6b6916627142d47227a142879c04 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sat, 26 Oct 2024 16:30:19 +0200 Subject: Use FFT Accelerator on DEXTER --- src/DabModulator.cpp | 11 ++- src/OfdmGenerator.cpp | 206 +++++++++++++++++++++++++++++++++++++++++++++++--- src/OfdmGenerator.h | 48 +++++++++++- src/output/Dexter.h | 12 +-- 4 files changed, 258 insertions(+), 19 deletions(-) diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 9e9d017..757b01f 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -190,7 +190,16 @@ int DabModulator::process(Buffer* dataOut) shared_ptr cifOfdm; - if (m_settings.fixedPoint) { + if (m_settings.useDexterOutput) { + cifOfdm = make_shared( + (1 + m_nbSymbols), + m_nbCarriers, + m_spacing, + m_settings.enableCfr, + m_settings.cfrClip, + m_settings.cfrErrorClip); + } + else if (m_settings.fixedPoint) { cifOfdm = make_shared( (1 + m_nbSymbols), m_nbCarriers, diff --git a/src/OfdmGenerator.cpp b/src/OfdmGenerator.cpp index e679694..198c2fc 100644 --- a/src/OfdmGenerator.cpp +++ b/src/OfdmGenerator.cpp @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2023 + Copyright (C) 2024 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -64,8 +64,7 @@ OfdmGeneratorCF32::OfdmGeneratorCF32(size_t nbSymbols, nbSymbols, nbCarriers, spacing, inverse ? "true" : "false", this); if (nbCarriers > spacing) { - throw std::runtime_error( - "OfdmGenerator::OfdmGenerator nbCarriers > spacing!"); + throw std::runtime_error("OfdmGenerator nbCarriers > spacing!"); } /* register the parameters that can be remote controlled */ @@ -162,8 +161,8 @@ int OfdmGeneratorCF32::process(Buffer* const dataIn, Buffer* dataOut) dataOut->setLength(myNbSymbols * mySpacing * sizeof(complexf)); - FFTW_TYPE* in = reinterpret_cast(dataIn->getData()); - FFTW_TYPE* out = reinterpret_cast(dataOut->getData()); + FFTW_TYPE *in = reinterpret_cast(dataIn->getData()); + FFTW_TYPE *out = reinterpret_cast(dataOut->getData()); size_t sizeIn = dataIn->getLength() / sizeof(complexf); size_t sizeOut = dataOut->getLength() / sizeof(complexf); @@ -205,7 +204,7 @@ int OfdmGeneratorCF32::process(Buffer* const dataIn, Buffer* dataOut) myPaprAfterCFR.clear(); } - for (size_t i = 0; i < myNbSymbols; ++i) { + for (size_t i = 0; i < myNbSymbols; i++) { myFftIn[0][0] = 0; myFftIn[0][1] = 0; @@ -485,8 +484,7 @@ OfdmGeneratorFixed::OfdmGeneratorFixed(size_t nbSymbols, etiLog.level(info) << "Using KISS FFT by Mark Borgerding for fixed-point transform"; if (nbCarriers > spacing) { - throw std::runtime_error( - "OfdmGenerator::OfdmGenerator nbCarriers > spacing!"); + throw std::runtime_error("OfdmGenerator nbCarriers > spacing!"); } if (inverse) { @@ -538,8 +536,8 @@ int OfdmGeneratorFixed::process(Buffer* const dataIn, Buffer* dataOut) { dataOut->setLength(myNbSymbols * mySpacing * sizeof(kiss_fft_cpx)); - kiss_fft_cpx* in = reinterpret_cast(dataIn->getData()); - kiss_fft_cpx* out = reinterpret_cast(dataOut->getData()); + kiss_fft_cpx *in = reinterpret_cast(dataIn->getData()); + kiss_fft_cpx *out = reinterpret_cast(dataOut->getData()); size_t sizeIn = dataIn->getLength() / sizeof(kiss_fft_cpx); size_t sizeOut = dataOut->getLength() / sizeof(kiss_fft_cpx); @@ -561,7 +559,7 @@ int OfdmGeneratorFixed::process(Buffer* const dataIn, Buffer* dataOut) "OfdmGenerator::process output size not valid!"); } - for (size_t i = 0; i < myNbSymbols; ++i) { + for (size_t i = 0; i < myNbSymbols; i++) { myFftIn[0].r = 0; myFftIn[0].i = 0; @@ -584,3 +582,189 @@ int OfdmGeneratorFixed::process(Buffer* const dataIn, Buffer* dataOut) return sizeOut; } + +#ifdef HAVE_DEXTER +OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(size_t nbSymbols, + size_t nbCarriers, + size_t spacing, + bool& enableCfr, + float& cfrClip, + float& cfrErrorClip, + bool inverse) : + ModCodec(), + myNbSymbols(nbSymbols), + myNbCarriers(nbCarriers), + mySpacing(spacing) +{ + PDEBUG("OfdmGeneratorDEXTER::OfdmGeneratorDEXTER(%zu, %zu, %zu, %s) @ %p\n", + nbSymbols, nbCarriers, spacing, inverse ? "true" : "false", this); + + etiLog.level(info) << "Using DEXTER FFT Aceelerator for fixed-point transform"; + + if (nbCarriers > spacing) { + throw std::runtime_error("OfdmGenerator nbCarriers > spacing!"); + } + + if (inverse) { + myPosDst = (nbCarriers & 1 ? 0 : 1); + myPosSrc = 0; + myPosSize = (nbCarriers + 1) / 2; + myNegDst = spacing - (nbCarriers / 2); + myNegSrc = (nbCarriers + 1) / 2; + myNegSize = nbCarriers / 2; + } + else { + myPosDst = (nbCarriers & 1 ? 0 : 1); + myPosSrc = nbCarriers / 2; + myPosSize = (nbCarriers + 1) / 2; + myNegDst = spacing - (nbCarriers / 2); + myNegSrc = 0; + myNegSize = nbCarriers / 2; + } + myZeroDst = myPosDst + myPosSize; + myZeroSize = myNegDst - myZeroDst; + + PDEBUG(" myPosDst: %u\n", myPosDst); + PDEBUG(" myPosSrc: %u\n", myPosSrc); + PDEBUG(" myPosSize: %u\n", myPosSize); + PDEBUG(" myNegDst: %u\n", myNegDst); + PDEBUG(" myNegSrc: %u\n", myNegSrc); + PDEBUG(" myNegSize: %u\n", myNegSize); + PDEBUG(" myZeroDst: %u\n", myZeroDst); + PDEBUG(" myZeroSize: %u\n", myZeroSize); + + const int N = mySpacing; // The size of the FFT + const size_t nbytes = N * sizeof(complexfix); + +#define IIO_ENSURE(expr, err) { \ + if (!(expr)) { \ + etiLog.log(error, "%s (%s:%d)\n", err, __FILE__, __LINE__); \ + throw std::runtime_error("Failed to set FFT for OfdmGeneratorDEXTER"); \ + } \ +} + IIO_ENSURE((m_ctx = iio_create_default_context()), "No context"); + IIO_ENSURE(m_dev_in = iio_context_find_device(m_ctx, "fft-accelerator-in"), "no dev"); + IIO_ENSURE(m_dev_out = iio_context_find_device(m_ctx, "fft-accelerator-out"), "no dev"); + IIO_ENSURE(m_channel_in = iio_device_find_channel(m_dev_in, "voltage0", true), "no channel"); + IIO_ENSURE(m_channel_out = iio_device_find_channel(m_dev_out, "voltage0", false), "no channel"); + + iio_channel_enable(m_channel_in); + iio_channel_enable(m_channel_out); + + m_buf_in = iio_device_create_buffer(m_dev_in, nbytes, false); + if (!m_buf_in) { + throw std::runtime_error("OfdmGeneratorDEXTER could not create in buffer"); + } + + m_buf_out = iio_device_create_buffer(m_dev_out, nbytes, false); + if (!m_buf_out) { + throw std::runtime_error("OfdmGeneratorDEXTER could not create out buffer"); + } +} + +OfdmGeneratorDEXTER::~OfdmGeneratorDEXTER() +{ + if (m_buf_in) { + iio_buffer_destroy(m_buf_in); + m_buf_in = nullptr; + } + + if (m_buf_out) { + iio_buffer_destroy(m_buf_out); + m_buf_out = nullptr; + } + + if (m_channel_in) { + iio_channel_disable(m_channel_in); + m_channel_in = nullptr; + } + + if (m_channel_out) { + iio_channel_disable(m_channel_out); + m_channel_out = nullptr; + } + + if (m_ctx) { + iio_context_destroy(m_ctx); + m_ctx = nullptr; + } +} + +int OfdmGeneratorDEXTER::process(Buffer* const dataIn, Buffer* dataOut) +{ + dataOut->setLength(myNbSymbols * mySpacing * sizeof(complexfix)); + + complexfix *in = reinterpret_cast(dataIn->getData()); + complexfix *out = reinterpret_cast(dataOut->getData()); + + size_t sizeIn = dataIn->getLength() / sizeof(complexfix); + size_t sizeOut = dataOut->getLength() / sizeof(complexfix); + + if (sizeIn != myNbSymbols * myNbCarriers) { + PDEBUG("Nb symbols: %zu\n", myNbSymbols); + PDEBUG("Nb carriers: %zu\n", myNbCarriers); + PDEBUG("Spacing: %zu\n", mySpacing); + PDEBUG("\n%zu != %zu\n", sizeIn, myNbSymbols * myNbCarriers); + throw std::runtime_error( + "OfdmGenerator::process input size not valid!"); + } + if (sizeOut != myNbSymbols * mySpacing) { + PDEBUG("Nb symbols: %zu\n", myNbSymbols); + PDEBUG("Nb carriers: %zu\n", myNbCarriers); + PDEBUG("Spacing: %zu\n", mySpacing); + PDEBUG("\n%zu != %zu\n", sizeIn, myNbSymbols * mySpacing); + throw std::runtime_error("OfdmGenerator::process output size not valid!"); + } + + ptrdiff_t iio_buf_size = (uint8_t*)iio_buffer_end(m_buf_in) - (uint8_t*)iio_buffer_start(m_buf_in); + if (iio_buf_size != (ssize_t)(mySpacing * sizeof(complexfix))) { + throw std::runtime_error("OfdmGenerator::process incorrect iio buffer size!"); + } + + ptrdiff_t p_inc = iio_buffer_step(m_buf_out); + if (p_inc != 1) { + throw std::runtime_error("OfdmGenerator::process Wrong p_inc"); + } + + const uint8_t *fft_out = (const uint8_t*)iio_buffer_first(m_buf_out, m_channel_out); + const uint8_t *fft_out_end = (const uint8_t*)iio_buffer_end(m_buf_out); + if (((fft_out_end - fft_out) != (ssize_t)(mySpacing * sizeof(complexfix))) != 0) { + throw std::runtime_error("OfdmGenerator::process fft_out length invalid!"); + } + + complexfix *fft_in = reinterpret_cast(iio_buffer_start(m_buf_in)); + + fft_in[0] = static_cast(0); + for (size_t i = 0; i < myZeroSize; i++) { + fft_in[myZeroDst + i] = static_cast(0); + } + + for (size_t i = 0; i < myNbSymbols; i++) { + /* For TM I this is: + * ZeroDst=769 ZeroSize=511 + * PosSrc=0 PosDst=1 PosSize=768 + * NegSrc=768 NegDst=1280 NegSize=768 + */ + memcpy(&fft_in[myPosDst], &in[myPosSrc], myPosSize * sizeof(complexfix)); + memcpy(&fft_in[myNegDst], &in[myNegSrc], myNegSize * sizeof(complexfix)); + + ssize_t nbytes_tx = iio_buffer_push(m_buf_in); + if (nbytes_tx < 0) { + throw std::runtime_error("OfdmGenerator::process error pushing IIO buffer!"); + } + + ssize_t nbytes_rx = iio_buffer_refill(m_buf_out); + if (nbytes_rx < 0) { + throw std::runtime_error("OfdmGenerator::process error refilling IIO buffer!"); + } + + memcpy(out, fft_out, mySpacing * sizeof(kiss_fft_cpx)); + + in += myNbCarriers; + out += mySpacing; + } + + return sizeOut; +} + +#endif // HAVE_DEXTER diff --git a/src/OfdmGenerator.h b/src/OfdmGenerator.h index 2e1aa63..b8ec702 100644 --- a/src/OfdmGenerator.h +++ b/src/OfdmGenerator.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2023 + Copyright (C) 2024 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -140,3 +140,49 @@ class OfdmGeneratorFixed : public ModCodec unsigned myZeroDst; unsigned myZeroSize; }; + +#ifdef HAVE_DEXTER +#include "iio.h" +// The PrecisionWave DEXTER device contains an FFT accelerator in FPGA +class OfdmGeneratorDEXTER : public ModCodec +{ + public: + OfdmGeneratorDEXTER(size_t nbSymbols, + size_t nbCarriers, + size_t spacing, + bool& enableCfr, + float& cfrClip, + float& cfrErrorClip, + bool inverse = true); + virtual ~OfdmGeneratorDEXTER(); + OfdmGeneratorDEXTER(const OfdmGeneratorDEXTER&) = delete; + OfdmGeneratorDEXTER& operator=(const OfdmGeneratorDEXTER&) = delete; + + int process(Buffer* const dataIn, Buffer* dataOut) override; + const char* name() override { return "OfdmGenerator"; } + + private: + struct iio_context *m_ctx = nullptr; + + // "in" and "out" are from the point of view of the FFT Accelerator block + struct iio_device *m_dev_in = nullptr; + struct iio_channel *m_channel_in = nullptr; + struct iio_buffer *m_buf_in = nullptr; + + struct iio_device *m_dev_out = nullptr; + struct iio_channel *m_channel_out = nullptr; + struct iio_buffer *m_buf_out = nullptr; + + const size_t myNbSymbols; + const size_t myNbCarriers; + const size_t mySpacing; + unsigned myPosSrc; + unsigned myPosDst; + unsigned myPosSize; + unsigned myNegSrc; + unsigned myNegDst; + unsigned myNegSize; + unsigned myZeroDst; + unsigned myZeroSize; +}; +#endif // HAVE_DEXTER diff --git a/src/output/Dexter.h b/src/output/Dexter.h index d4f425f..f8a17ba 100644 --- a/src/output/Dexter.h +++ b/src/output/Dexter.h @@ -98,16 +98,16 @@ class Dexter : public Output::SDRDevice SDRDeviceConfig& m_conf; - struct iio_context* m_ctx = nullptr; - struct iio_device* m_dexter_dsp_tx = nullptr; + struct iio_context *m_ctx = nullptr; + struct iio_device *m_dexter_dsp_tx = nullptr; - struct iio_device* m_ad9957 = nullptr; - struct iio_device* m_ad9957_tx0 = nullptr; - struct iio_channel* m_tx_channel = nullptr; + struct iio_device *m_ad9957 = nullptr; + struct iio_device *m_ad9957_tx0 = nullptr; + struct iio_channel *m_tx_channel = nullptr; struct iio_buffer *m_buffer = nullptr; /* Underflows are counted in a separate thread */ - struct iio_context* m_underflow_ctx = nullptr; + struct iio_context *m_underflow_ctx = nullptr; std::atomic m_running = ATOMIC_VAR_INIT(false); std::thread m_underflow_read_thread; void underflow_read_process(); -- cgit v1.2.3