diff --git a/SoapyLMS7/Streaming.cpp b/SoapyLMS7/Streaming.cpp index 413cf4f97..0e02c1370 100644 --- a/SoapyLMS7/Streaming.cpp +++ b/SoapyLMS7/Streaming.cpp @@ -338,7 +338,7 @@ int SoapyLMS7::readStream( } SDRDevice::StreamMeta metadata; - const int64_t cmdTicks = + const uint64_t cmdTicks = ((icstream->flags & SOAPY_SDR_HAS_TIME) != 0) ? SoapySDR::timeNsToTicks(icstream->timeNs, sampleRate[SOAPY_SDR_RX]) : 0; int status = 0; @@ -364,7 +364,7 @@ int SoapyLMS7::readStream( return SOAPY_SDR_STREAM_ERROR; } - const int64_t expectedTime(cmdTicks + status); + const uint64_t expectedTime(cmdTicks + status); if (metadata.timestamp < expectedTime) { @@ -373,7 +373,7 @@ int SoapyLMS7::readStream( } // The command had a time, so we need to compare it to received time - if ((icstream->flags & SOAPY_SDR_HAS_TIME) != 0 and metadata.useTimestamp) + if ((icstream->flags & SOAPY_SDR_HAS_TIME) != 0 and metadata.waitForTimestamp) { // Our request time is now late, clear command and return error code if (cmdTicks < metadata.timestamp) @@ -408,17 +408,17 @@ int SoapyLMS7::readStream( if (icstream->numElems == 0) { icstream->hasCmd = false; - metadata.flush = true; + metadata.flushPartialPacket = true; } } // Output metadata - if (metadata.flush) + if (metadata.flushPartialPacket) { flags |= SOAPY_SDR_END_BURST; } - if (metadata.useTimestamp) + if (metadata.waitForTimestamp) { flags |= SOAPY_SDR_HAS_TIME; } @@ -447,8 +447,8 @@ int SoapyLMS7::writeStream(SoapySDR::Stream* stream, // Input metadata SDRDevice::StreamMeta metadata; metadata.timestamp = SoapySDR::timeNsToTicks(timeNs, sampleRate[SOAPY_SDR_RX]); - metadata.useTimestamp = (flags & SOAPY_SDR_HAS_TIME); - metadata.flush = (flags & SOAPY_SDR_END_BURST); + metadata.waitForTimestamp = (flags & SOAPY_SDR_HAS_TIME); + metadata.flushPartialPacket = (flags & SOAPY_SDR_END_BURST); int status = 0; switch (icstream->streamConfig.format) diff --git a/amarisoft-plugin/common.cpp b/amarisoft-plugin/common.cpp index f4f493e98..2464f3041 100644 --- a/amarisoft-plugin/common.cpp +++ b/amarisoft-plugin/common.cpp @@ -749,10 +749,10 @@ OpStatus ConfigureStreaming(LimePluginContext* context, const LimeRuntimeParamet stream.channels[TRXDir::Tx].resize(params->rf_ports[p].tx_channel_count); stream.linkFormat = SDRDevice::StreamConfig::DataFormat::I16; stream.format = context->samplesFormat; - stream.extraConfig.rxSamplesInPacket = 256; - stream.extraConfig.rxPacketsInBatch = 4; - stream.extraConfig.txMaxPacketsInBatch = 8; - stream.extraConfig.txSamplesInPacket = 256; + stream.extraConfig.rx.samplesInPacket = 256; + stream.extraConfig.rx.packetsInBatch = 4; + stream.extraConfig.tx.packetsInBatch = 8; + stream.extraConfig.tx.samplesInPacket = 256; // Initialize streams and map channels for (size_t ch = 0; ch < stream.channels[TRXDir::Rx].size(); ++ch) @@ -895,8 +895,8 @@ int LimePlugin_Write_complex16( template static int LimePlugin_Read(LimePluginContext* context, T** samples, int count, int port, SDRDevice::StreamMeta& meta) { - meta.useTimestamp = false; - meta.flush = false; + meta.waitForTimestamp = false; + meta.flushPartialPacket = false; int samplesGot = context->ports[port].composite->StreamRx(samples, count, &meta); if (samplesGot == 0) diff --git a/amarisoft-plugin/gainTable.h b/amarisoft-plugin/gainTable.h index 3d84e6bba..b88ffeea6 100644 --- a/amarisoft-plugin/gainTable.h +++ b/amarisoft-plugin/gainTable.h @@ -2,15 +2,12 @@ #include -// clang-format off - struct RxGainRow { int lna; int pga; }; - -static std::array rxGainTable{{ +static std::array rxGainTable{ { { 1, 12 }, { 1, 13 }, { 2, 12 }, @@ -61,15 +58,15 @@ static std::array rxGainTable{{ { 15, 28 }, { 15, 29 }, { 15, 30 }, - { 15, 31 } -}}; + { 15, 31 }, +} }; struct TxGainRow { int main; int lin; }; -static std::array txGainTable{{ +static std::array txGainTable{ { { 30, 30 }, { 30, 30 }, { 29, 29 }, @@ -120,7 +117,5 @@ static std::array txGainTable{{ { 3, 3 }, { 2, 2 }, { 1, 1 }, - { 0, 0 } -}}; - -// clang-format on + { 0, 0 }, +} }; diff --git a/amarisoft-plugin/trx_limesuite.cpp b/amarisoft-plugin/trx_limesuite.cpp index 7b4bbc278..3ddd134f4 100644 --- a/amarisoft-plugin/trx_limesuite.cpp +++ b/amarisoft-plugin/trx_limesuite.cpp @@ -2,7 +2,6 @@ * LimeMicroSystem transceiver driver * Copyright (C) 2015-2018 Amarisoft/LimeMicroSystems */ - #include "common.h" #include @@ -132,8 +131,8 @@ static void trx_lms7002m_write( SDRDevice::StreamMeta meta; meta.timestamp = timestamp; - meta.useTimestamp = true; - meta.flush = (md->flags & TRX_WRITE_MD_FLAG_END_OF_BURST); + meta.waitForTimestamp = true; + meta.flushPartialPacket = (md->flags & TRX_WRITE_MD_FLAG_END_OF_BURST); // samples format conversion is done internally LimePluginContext* lime = static_cast(s->opaque); @@ -147,8 +146,8 @@ static void trx_lms7002m_write( static int trx_lms7002m_read(TRXState* s, trx_timestamp_t* ptimestamp, void** samples, int count, int port, TRXReadMetadata* md) { SDRDevice::StreamMeta meta; - meta.useTimestamp = false; - meta.flush = false; + meta.waitForTimestamp = false; + meta.flushPartialPacket = false; md->flags = 0; LimePluginContext* lime = static_cast(s->opaque); @@ -259,9 +258,9 @@ static int trx_lms7002m_get_tx_samples_per_packet_func(TRXState* s1) { // LimePluginContext* lime = static_cast(s1->opaque); int txExpectedSamples = 256; //lime->samplesInPacket[0]; - // if (lime->streamExtras[0] && lime->streamExtras[0]->txSamplesInPacket > 0) + // if (lime->streamExtras[0] && lime->streamExtras[0]->tx.samplesInPacket > 0) // { - // txExpectedSamples = lime->streamExtras[0]->txSamplesInPacket; + // txExpectedSamples = lime->streamExtras[0]->tx.samplesInPacket; // } // Log(LogLevel::DEBUG, "Hardware expected samples count in Tx packet : %i", txExpectedSamples); return txExpectedSamples; diff --git a/docs/setup/documentation.rst b/docs/setup/documentation.rst index 9a779ab7f..4ce48899b 100644 --- a/docs/setup/documentation.rst +++ b/docs/setup/documentation.rst @@ -40,6 +40,7 @@ In the `docs` folder, located in the root folder of the repository, while in the cmake -S .. -B ../build # Generate the make file for the suite. make --no-print-directory -C ../build doc # Build Doxygen documentation breathe-apidoc --generate class --members --force --output-dir apidoc ../build/xml/ # Generate the class API pages + python add_undoc_members.py # Add a flag to add all undocumented members into the page make html # Generate the documentation itself .. important:: diff --git a/src/ADF4002/ADF4002.cpp b/src/ADF4002/ADF4002.cpp index a4f2fa3fe..8c3ed4602 100644 --- a/src/ADF4002/ADF4002.cpp +++ b/src/ADF4002/ADF4002.cpp @@ -8,8 +8,9 @@ #include #include -#include -#include +#include +#include +#include using namespace lime; @@ -22,7 +23,7 @@ ADF4002::~ADF4002() { } -void ADF4002::Initialize(ISPI* comms, double refClkHz) +void ADF4002::Initialize(std::shared_ptr comms, double refClkHz) { mComms = comms; txtFref = refClkHz / 1e6; @@ -324,7 +325,8 @@ void ADF4002::CalculateRN() lblFvco = dFvco; } -/** @brief Writes configuration to chip +/** @brief Gets the configuration from chip + * @param data The data to read from the chip. */ void ADF4002::GetConfig(unsigned char data[12]) { diff --git a/src/ADF4002/ADF4002.h b/src/ADF4002/ADF4002.h index b6eb71f12..7417d6817 100644 --- a/src/ADF4002/ADF4002.h +++ b/src/ADF4002/ADF4002.h @@ -8,17 +8,22 @@ #define ADF_MODULE_H #include "limesuite/config.h" - #include "limesuite/IComms.h" +#include + namespace lime { +/** @brief The class for controlling the ADF4002 frequency synthesizer. + * + * More information: https://www.analog.com/en/products/adf4002.html + */ class LIME_API ADF4002 { public: ADF4002(); ~ADF4002(); - void Initialize(ISPI* comms, double refClkHz); + void Initialize(std::shared_ptr comms, double refClkHz); int UploadConfig(); void SetFrefFvco(double Fref, double Fvco, int& rcount, int& ncount); @@ -67,7 +72,7 @@ class LIME_API ADF4002 double lblFvco; protected: - ISPI* mComms; + std::shared_ptr mComms; unsigned char m_registers[12]; }; diff --git a/src/API/LMS_APIWrapper.cpp b/src/API/LMS_APIWrapper.cpp index b2cc5bf6d..96641d255 100644 --- a/src/API/LMS_APIWrapper.cpp +++ b/src/API/LMS_APIWrapper.cpp @@ -628,7 +628,7 @@ API_EXPORT int CALL_CONV LMS_GetNormalizedGain(lms_device_t* device, bool dir_tx if (gain) *gain = (deviceGain - range.min) / (range.max - range.min); - return returnValue == OpStatus::SUCCESS ? 0 : -1; + return OpStatusToReturnCode(returnValue); } API_EXPORT int CALL_CONV LMS_GetGaindB(lms_device_t* device, bool dir_tx, size_t chan, unsigned* gain) @@ -648,7 +648,7 @@ API_EXPORT int CALL_CONV LMS_GetGaindB(lms_device_t* device, bool dir_tx, size_t if (gain) *gain = std::lround(deviceGain) + 12; - return returnValue == OpStatus::SUCCESS ? 0 : -1; + return OpStatusToReturnCode(returnValue); } API_EXPORT int CALL_CONV LMS_Calibrate(lms_device_t* device, bool dir_tx, size_t chan, double bw, unsigned flags) @@ -723,10 +723,10 @@ API_EXPORT int CALL_CONV LMS_SetTestSignal( } }; - try { + try + { apiDevice->device->SetTestSignal(apiDevice->moduleIndex, direction, chan, enumToTestStruct(sig), dc_i, dc_q); - } - catch (...) + } catch (...) { lime::error("Failed to set %s channel %i test signal.", ToString(direction).c_str(), chan); } @@ -1061,8 +1061,8 @@ int SendStream(lms_stream_t* stream, const void* samples, size_t sample_count, c if (meta != nullptr) { - metadata.flush = meta->flushPartialPacket; - metadata.useTimestamp = meta->waitForTimestamp; + metadata.flushPartialPacket = meta->flushPartialPacket; + metadata.waitForTimestamp = meta->waitForTimestamp; metadata.timestamp = meta->timestamp; } @@ -1789,10 +1789,10 @@ API_EXPORT int CALL_CONV LMS_WriteLMSReg(lms_device_t* device, uint32_t address, return -1; } - try { + try + { apiDevice->device->WriteRegister(apiDevice->moduleIndex, address, val); - } - catch(...) + } catch (...) { return lime::error("Failed to write register at %04X.", address); } @@ -1808,11 +1808,11 @@ API_EXPORT int CALL_CONV LMS_ReadLMSReg(lms_device_t* device, uint32_t address, return -1; } - try { + try + { if (val) *val = apiDevice->device->ReadRegister(apiDevice->moduleIndex, address); - } - catch (...) + } catch (...) { return lime::error("Failed to read register at %04X.", address); } @@ -1828,10 +1828,10 @@ API_EXPORT int CALL_CONV LMS_WriteFPGAReg(lms_device_t* device, uint32_t address return -1; } - try { + try + { apiDevice->device->WriteRegister(apiDevice->moduleIndex, address, val, true); - } - catch (...) + } catch (...) { return lime::error("Failed to write register at %04X.", address); } @@ -1847,11 +1847,11 @@ API_EXPORT int CALL_CONV LMS_ReadFPGAReg(lms_device_t* device, uint32_t address, return -1; } - try { + try + { if (val) *val = apiDevice->device->ReadRegister(apiDevice->moduleIndex, address, true); - } - catch (...) + } catch (...) { return lime::error("Failed to read register at %04X.", address); } diff --git a/src/CDCM6208/CDCM6208_Dev.h b/src/CDCM6208/CDCM6208_Dev.h index 7ebe68f84..bf5427f99 100644 --- a/src/CDCM6208/CDCM6208_Dev.h +++ b/src/CDCM6208/CDCM6208_Dev.h @@ -75,10 +75,19 @@ struct CDCM_Outputs { CDCM_Output Y7{ false, false, 0, 0, 0, 0, 30.72e6, 0 }; ///< The value of the Y7 output }; -/** @brief Class for controlling the CDCM6208 2:8 Clock Generator, Jitter Cleaner With Fractional Dividers */ +/** @brief Class for controlling the CDCM6208 2:8 Clock Generator, Jitter Cleaner With Fractional Dividers. + * + * More information: https://www.ti.com/product/CDCM6208 + */ class LIME_API CDCM_Dev { public: + /** + @brief Constructs a new CDCM_Dev object. + + @param comms The communications path to use. + @param SPI_BASE_ADDR The base address for the API interface. + */ CDCM_Dev(std::shared_ptr comms, uint16_t SPI_BASE_ADDR); int Init(double primaryFreq, double secondaryFreq); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a8922fe0..74a13bebf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,6 +45,7 @@ set(LIME_SUITE_SOURCES Si5351C/Si5351C.cpp ${PROJECT_SOURCE_DIR}/external/kissFFT/kiss_fft.c FPGA_common/FPGA_common.cpp + FPGA_common/WriteRegistersBatch.cpp windowFunction.cpp threadHelper/threadHelper.cpp CDCM6208/CDCM6208_Dev.cpp diff --git a/src/FPGA_common/FPGA_common.cpp b/src/FPGA_common/FPGA_common.cpp index 6d6e5c550..cf3669857 100644 --- a/src/FPGA_common/FPGA_common.cpp +++ b/src/FPGA_common/FPGA_common.cpp @@ -1,14 +1,16 @@ #include "FPGA_common.h" #include "limesuite/IComms.h" -#include -#include -#include +#include "LMSBoards.h" #include "Logger.h" +#include "WriteRegistersBatch.h" + #include -#include -#include -#include "LMSBoards.h" +#include +#include +#include +#include #include "samplesConversion.h" + using namespace std; #ifndef NDEBUG @@ -21,32 +23,6 @@ using namespace std; namespace lime { -/** @brief A class for writing a batch of registers into the FPGA. */ -class WriteRegistersBatch -{ - public: - WriteRegistersBatch(FPGA* fpga) - : owner(fpga){}; - ~WriteRegistersBatch() { ASSERT_WARNING(addrs.size() == 0, "FPGA WriteRegistersBatch not flushed"); } - OpStatus Flush() - { - OpStatus status = owner->WriteRegisters(addrs.data(), values.data(), addrs.size()); - addrs.clear(); - values.clear(); - return status; - } - void WriteRegister(uint16_t addr, uint16_t value) - { - addrs.push_back(addr); - values.push_back(value); - } - - private: - FPGA* owner; - std::vector addrs; - std::vector values; -}; - // 0x000A const int RX_EN = 1; //controls both receiver and transmitter const int TX_EN = 1 << 1; //used for wfm playback from fpga @@ -68,8 +44,8 @@ const uint16_t PHCFG_MODE = 1 << 14; const uint16_t busyAddr = 0x0021; static const std::chrono::milliseconds busyPollPeriod(10); // time between checking "done" bit -// does the fpga has "done" bit to indicate PLLCFG_START,PHCFG_START,PLLRST_START completion -static bool HasWaitForDone(uint8_t targetDevice) +// Does the FPGA have the "done" bit to indicate PLLCFG_START, PHCFG_START, PLLRST_START completion? +static constexpr bool HasWaitForDone(uint8_t targetDevice) { // TODO: list devices that don't have it, as it's most likely that future devices will support this switch (static_cast(targetDevice)) @@ -83,7 +59,7 @@ static bool HasWaitForDone(uint8_t targetDevice) } } -static bool HasFPGAClockPhaseSearch(uint8_t targetDevice, uint8_t version, uint8_t revision) +static constexpr bool HasFPGAClockPhaseSearch(uint8_t targetDevice, uint8_t version, uint8_t revision) { const uint16_t ver_rev = version << 8 | revision; switch (static_cast(targetDevice)) @@ -105,6 +81,9 @@ static bool HasFPGAClockPhaseSearch(uint8_t targetDevice, uint8_t version, uint8 } } +/// @brief Constructs the FPGA object. +/// @param fpgaSPI The FPGA communications interface. +/// @param lms7002mSPI The LMS7002M chip communications interface. FPGA::FPGA(std::shared_ptr fpgaSPI, std::shared_ptr lms7002mSPI) : fpgaPort(fpgaSPI) , lms7002mPort(lms7002mSPI) @@ -112,6 +91,8 @@ FPGA::FPGA(std::shared_ptr fpgaSPI, std::shared_ptr lms7002mSPI) { } +/// @brief Enables caching of registers on the hosts' end. +/// @param enabled Whether to enable or disable the caching. void FPGA::EnableValuesCache(bool enabled) { lime::debug("Enable FPGA registers cache: %s", enabled ? "true" : "false"); @@ -120,17 +101,29 @@ void FPGA::EnableValuesCache(bool enabled) regsCache.clear(); } -OpStatus FPGA::WriteRegister(uint32_t addr, uint32_t val) +/// @brief Writes the specified value into the specified address into the FPGA. +/// @param address The address to write to. +/// @param value The value to write. +/// @return The operation status. +OpStatus FPGA::WriteRegister(uint32_t address, uint32_t value) { - return WriteRegisters(&addr, &val, 1); + return WriteRegisters(&address, &value, 1); } -int FPGA::ReadRegister(uint32_t addr) +/// @brief Reads a value from the specified address in the FPGA. +/// @param address The address to read from. +/// @return The value of the register (or -1 on failure). +int FPGA::ReadRegister(uint32_t address) { uint32_t val; - return ReadRegisters(&addr, &val, 1) != OpStatus::SUCCESS ? -1 : val; + return ReadRegisters(&address, &val, 1) != OpStatus::SUCCESS ? -1 : val; } +/// @brief Writes the given registers into the FPGA's memory. +/// @param addrs The addresses to write to. +/// @param data The values to write into the memory. +/// @param cnt The amount of values to write. +/// @return The status of the operation. OpStatus FPGA::WriteRegisters(const uint32_t* addrs, const uint32_t* data, unsigned cnt) { std::vector spiBuffer; @@ -205,22 +198,34 @@ OpStatus FPGA::WriteRegisters(const uint32_t* addrs, const uint32_t* data, unsig return OpStatus::SUCCESS; } -int FPGA::WriteLMS7002MSPI(const uint32_t* data, uint32_t length) +/// @brief Writes the given data blocks into LMS7002M chip. +/// @param data The data to write. +/// @param length The length of the data to write. +/// @return The status of the operation. +OpStatus FPGA::WriteLMS7002MSPI(const uint32_t* data, uint32_t length) { #ifndef NDEBUG for (uint32_t i = 0; i < length; ++i) assert(data[i] & (1 << 31)); #endif - lms7002mPort->SPI(data, nullptr, length); - return 0; + return lms7002mPort->SPI(data, nullptr, length); } -int FPGA::ReadLMS7002MSPI(const uint32_t* writeData, uint32_t* readData, uint32_t length) +/// @brief Reads the given addresses from the LMS7002M's memory. +/// @param writeData The addresses to read from. +/// @param readData The storage to store the read data. +/// @param length The length of the data to read. +/// @return The status of the operation. +OpStatus FPGA::ReadLMS7002MSPI(const uint32_t* writeData, uint32_t* readData, uint32_t length) { - lms7002mPort->SPI(writeData, readData, length); - return 0; + return lms7002mPort->SPI(writeData, readData, length); } +/// @brief Reads the given registers from the FPGA's memory. +/// @param addrs The addresses to read. +/// @param data The data array to write the read values to. +/// @param cnt The amount of registers to read. +/// @return The operation status. OpStatus FPGA::ReadRegisters(const uint32_t* addrs, uint32_t* data, unsigned cnt) { std::vector spiBuffer; @@ -303,6 +308,8 @@ OpStatus FPGA::ReadRegisters(const uint32_t* addrs, uint32_t* data, unsigned cnt return status; } +/// @brief Tells the FPGA to start streaming sample data. +/// @return The operation status. OpStatus FPGA::StartStreaming() { lime::debug("%s", __func__); @@ -314,6 +321,8 @@ OpStatus FPGA::StartStreaming() return WriteRegister(0x000A, interface_ctrl_000A | RX_EN); } +/// @brief Tells the FPGA to stop streaming sample data. +/// @return The operation status. OpStatus FPGA::StopStreaming() { lime::debug("%s", __func__); @@ -324,6 +333,8 @@ OpStatus FPGA::StopStreaming() return WriteRegister(0x000A, interface_ctrl_000A & flags); } +/// @brief Resets the timestamp of the FPGA. +/// @return The operation status. OpStatus FPGA::ResetTimestamp() { lime::debug("%s", __func__); @@ -368,7 +379,7 @@ OpStatus FPGA::WaitTillDone(uint16_t pollAddr, uint16_t doneMask, uint16_t error if (!done) { - if ((chrono::high_resolution_clock::now() - t1) > timeout) + if ((std::chrono::high_resolution_clock::now() - t1) > timeout) { lime::warning("%s timeout", title.c_str()); return OpStatus::TIMEOUT; @@ -433,19 +444,18 @@ OpStatus FPGA::SetPllClock(uint8_t clockIndex, int nSteps, bool waitLock, bool d return OpStatus::SUCCESS; } -/** @brief Configures board FPGA clocks - @param pllIndex index of FPGA pll - @param inputFreq input frequency - @param clocks list of clocks to configure - @param clockCount number of clocks to configure - @return 0-success, other-failure +/** @brief Configures board FPGA clocks. + @param pllIndex Index of FPGA PLL. + @param inputFreq Input frequency. + @param clocks List of clocks to configure. + @return The operation status. */ OpStatus FPGA::SetPllFrequency(const uint8_t pllIndex, const double inputFreq, std::vector& clocks) { const uint8_t clockCount = clocks.size(); lime::debug("FPGA SetPllFrequency: PLL[%i] input:%.3f MHz clockCount:%i", pllIndex, inputFreq / 1e6, clockCount); WriteRegistersBatch batch(this); - const auto timeout = chrono::seconds(3); + const auto timeout = std::chrono::seconds(3); if (!fpgaPort) return ReportError(OpStatus::IO_FAILURE, "ConfigureFPGA_PLL: connection port is NULL"); @@ -458,7 +468,10 @@ OpStatus FPGA::SetPllFrequency(const uint8_t pllIndex, const double inputFreq, s //check if all clocks are above 5MHz const double PLLlowerLimit = 5e6; if (inputFreq < PLLlowerLimit) - return ReportError(OpStatus::OUT_OF_RANGE, "FPGA SetPllFrequency: PLL[%i] input frequency must be >=%g MHz", pllIndex, PLLlowerLimit / 1e6); + return ReportError(OpStatus::OUT_OF_RANGE, + "FPGA SetPllFrequency: PLL[%i] input frequency must be >=%g MHz", + pllIndex, + PLLlowerLimit / 1e6); for (int i = 0; i < clockCount; ++i) { lime::debug("CLK[%i] Fout:%.3f MHz bypass:%i phase:%g findPhase: %i", @@ -469,8 +482,11 @@ OpStatus FPGA::SetPllFrequency(const uint8_t pllIndex, const double inputFreq, s clocks[i].findPhase); willDoPhaseSearch |= clocks[i].findPhase; if (clocks[i].outFrequency < PLLlowerLimit && !clocks[i].bypass) - return ReportError( - OpStatus::OUT_OF_RANGE, "FPGA SetPllFrequency: PLL[%i], clock[%i] must be >=%g MHz", pllIndex, i, PLLlowerLimit / 1e6); + return ReportError(OpStatus::OUT_OF_RANGE, + "FPGA SetPllFrequency: PLL[%i], clock[%i] must be >=%g MHz", + pllIndex, + i, + PLLlowerLimit / 1e6); } uint16_t drct_clk_ctrl_0005 = ReadRegister(0x0005); @@ -507,7 +523,7 @@ OpStatus FPGA::SetPllFrequency(const uint8_t pllIndex, const double inputFreq, s const double vcoLimits_Hz[2] = { 600e6, 1280e6 }; // Collect all desired VCO frequencies - map desiredVCO; // + std::map desiredVCO; // for (int i = 0; i < clockCount; ++i) { if (clocks[i].outFrequency == 0 || clocks[i].bypass) @@ -521,7 +537,7 @@ OpStatus FPGA::SetPllFrequency(const uint8_t pllIndex, const double inputFreq, s if (it != desiredVCO.end()) it->second++; // increase demand else // add new frequency demand - desiredVCO.insert(pair(freq, 1)); + desiredVCO.insert(std::pair(freq, 1)); freq += clocks[i].outFrequency; } } @@ -530,7 +546,7 @@ OpStatus FPGA::SetPllFrequency(const uint8_t pllIndex, const double inputFreq, s // Find VCO that satisfies most outputs with integer dividers uint64_t bestFreqVCO = std::max_element( - desiredVCO.begin(), desiredVCO.end(), [](const pair& p1, const pair& p2) { + desiredVCO.begin(), desiredVCO.end(), [](const std::pair& p1, const std::pair& p2) { if (p1.second == p2.second) return p1.first < p2.first; // sort by VCO frequency return p1.second < p2.second; @@ -650,9 +666,15 @@ OpStatus FPGA::SetDirectClocking(int clockIndex) return OpStatus::SUCCESS; } -/** @brief Parses FPGA packet payload into samples -*/ -int FPGA::FPGAPacketPayload2Samples(const uint8_t* buffer, int bufLen, bool mimo, bool compressed, complex16_t** samples) +/** @brief Parses FPGA packet payload into samples. + @param buffer The buffer to parse. + @param bufLen The length of the buffer to parse. + @param mimo Whether the payload contains multiple (two) channels. + @param compressed Whether the samples are in 12-bit (true) or 16-bit (false) integer format. + @param samples The output buffer of the samples. + @return The amount of samples parsed. + */ +int FPGA::FPGAPacketPayload2Samples(const uint8_t* buffer, int bufLen, bool mimo, bool compressed, complex16_t* const* samples) { if (compressed) //compressed samples { @@ -691,9 +713,16 @@ int FPGA::FPGAPacketPayload2Samples(const uint8_t* buffer, int bufLen, bool mimo return bufLen / sizeof(complex16_t); } -/** @brief Parses FPGA packet payload into samples -*/ -int FPGA::FPGAPacketPayload2SamplesFloat(const uint8_t* buffer, int bufLen, bool mimo, bool compressed, complex32f_t** samples) +/** @brief Parses FPGA packet payload into samples. + @param buffer The buffer to parse. + @param bufLen The length of the buffer to parse. + @param mimo Whether the payload contains multiple (two) channels. + @param compressed Whether the samples are in 12-bit (true) or 16-bit (false) integer format. + @param samples The output buffer of the samples. + @return The amount of samples parsed. + */ +int FPGA::FPGAPacketPayload2SamplesFloat( + const uint8_t* buffer, int bufLen, bool mimo, bool compressed, complex32f_t* const* samples) { if (compressed) //compressed samples { @@ -804,106 +833,20 @@ int FPGA::Samples2FPGAPacketPayload( } return samplesCount * 2 * sizeof(complex16_t); } - memcpy(buffer, samples[0], samplesCount * sizeof(complex16_t)); + std::memcpy(buffer, samples[0], samplesCount * sizeof(complex16_t)); return samplesCount * sizeof(complex16_t); } -/* -int FPGA::UploadWFM(const void* const* samples, uint8_t chCount, size_t sample_count, StreamConfig::StreamDataFormat format, int epIndex) -{ - bool comp = (epIndex==2 && format!=StreamConfig::FMT_INT12) ? false : true; - - const int samplesInPkt = comp ? samples12InPkt : samples16InPkt; - SelectModule(epIndex); - WriteRegister(0x000C, chCount == 2 ? 0x3 : 0x1); //channels 0,1 - WriteRegister(0x000E, comp ? 0x2 : 0x0); //16bit samples - uint16_t regValue = ReadRegister(0x000D); - regValue |= 0x4; - WriteRegister(0x000D, regValue); - - lime::FPGA_DataPacket pkt; - size_t samplesUsed = 0; - int cnt = sample_count; - - const complex16_t* const* src = (const complex16_t* const*)samples; - const lime::complex16_t** batch = new const lime::complex16_t*[chCount]; - lime::complex16_t** samplesShort = new lime::complex16_t*[chCount]; - for(unsigned i=0; i> 4; - samplesShort[ch][i].q = src[ch][i].q >> 4; - } - src = samplesShort; - } - else if(format == StreamConfig::FMT_FLOAT32) - { - const float mult = comp ? 2047.0f : 32767.0f; - for(unsigned i=0; i 0) - { - pkt.counter = 0; - pkt.reserved[0] = 0; - int samplesToSend = cnt > samplesInPkt/chCount ? samplesInPkt/chCount : cnt; - - for(unsigned i=0; i> 8) & 0xFF; //WFM loading - pkt.reserved[1] = payloadSize & 0xFF; //WFM loading - pkt.reserved[0] = 0x1 << 5; //WFM loading - - long bToSend = 16+payloadSize; - if (connection->SendData((const char*)&pkt,bToSend,epIndex,500)!=bToSend) - break; - cnt -= samplesToSend; - } - delete[] batch; - for(unsigned i=0; iAbortSending(epIndex); - if(cnt == 0) - return 0; - else - return ReportError(-1, "Failed to upload waveform"); -} -*/ - -/** @brief Configures FPGA PLLs to LimeLight interface frequency -*/ +/// @brief Configures FPGA PLLs to LimeLight interface frequency. +/// @param txRate_Hz The transmit rate (in Hz). +/// @param rxRate_Hz The receive rate (in Hz). +/// @param txPhase The transmit phase offset (in degrees). +/// @param rxPhase The receive phase offset (in degrees). +/// @param chipIndex The chip to configure. +/// @return The operation status. OpStatus FPGA::SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, double txPhase, double rxPhase, int chipIndex) { - lime::debug("FPGA::SetInterfaceFreq tx:%.3f MHz rx:%.3f MHz txPhase:%g rxPhase:%g chip:%i", + lime::debug("FPGA::SetInterfaceFreq tx:%.3f MHz rx:%.3f MHz txPhase:%g rxPhase:%g ch:%i", txRate_Hz / 1e6, rxRate_Hz / 1e6, txPhase, @@ -913,8 +856,7 @@ OpStatus FPGA::SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, double txPha OpStatus status = OpStatus::SUCCESS; const uint32_t addr = 0x002A; - uint32_t val; - val = (1 << 31) | (0x0020u << 16) | 0xFFFD; //msbit 1=SPI write + uint32_t val = (1 << 31) | (0x0020u << 16) | 0xFFFD; // msbit 1=SPI write WriteLMS7002MSPI(&val, 1); ReadLMS7002MSPI(&addr, &val, 1); bool bypassTx = (val & 0xF0) == 0x00; @@ -953,11 +895,14 @@ OpStatus FPGA::SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, double txPha return status; } -/** @brief Configures FPGA PLLs to LimeLight interface frequency -*/ +/// @brief Configures FPGA PLLs to LimeLight interface frequency. +/// @param txRate_Hz The transmit rate (in Hz). +/// @param rxRate_Hz The receive rate (in Hz). +/// @param chipIndex The chip to configure. +/// @return The operation status. OpStatus FPGA::SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, int chipIndex) { - lime::debug("FPGA::SetInterfaceFreq tx:%.3f MHz rx:%.3f MHz chipIndex:%i", txRate_Hz / 1e6, rxRate_Hz / 1e6, chipIndex); + lime::debug("FPGA::SetInterfaceFreq tx:%.3f MHz rx:%.3f MHz channel:%i", txRate_Hz / 1e6, rxRate_Hz / 1e6, chipIndex); SelectModule(chipIndex); //PrintStackTrace(); const int pll_ind = (chipIndex == 1) ? 2 : 0; @@ -1224,12 +1169,15 @@ double FPGA::DetectRefClk(double fx3Clk) return clkTbl[i - 1]; } +/// @brief Gets the information about the gateware of the device from the FPGA. +/// @return The gateware information of the FPGA. FPGA::GatewareInfo FPGA::GetGatewareInfo() { GatewareInfo info; info.boardID = 0; info.version = 0; info.revision = 0; + info.hardwareVersion = 0; const uint32_t addrs[4] = { 0x0000, 0x0001, 0x0002, 0x0003 }; uint32_t data[4]; @@ -1237,12 +1185,15 @@ FPGA::GatewareInfo FPGA::GetGatewareInfo() return info; info.boardID = data[0]; - info.version = static_cast(data[1]); + info.version = data[1]; info.revision = data[2]; info.hardwareVersion = data[3] & 0x7F; return info; } +/// @brief Converts the Gateware information descriptor into an SDR Device descriptor. +/// @param gw The gateware information to convert. +/// @param[out] desc The descriptor to output the information to. void FPGA::GatewareToDescriptor(const FPGA::GatewareInfo& gw, SDRDevice::Descriptor& desc) { desc.gatewareTargetBoard = GetDeviceName(eLMS_DEV(gw.boardID)); diff --git a/src/FPGA_common/FPGA_common.h b/src/FPGA_common/FPGA_common.h index 18d136b31..212d0fcf9 100644 --- a/src/FPGA_common/FPGA_common.h +++ b/src/FPGA_common/FPGA_common.h @@ -4,9 +4,10 @@ @brief Common functions used to work with FPGA */ -#ifndef FPGA_COMMON_H -#define FPGA_COMMON_H -#include +#ifndef LIME_FPGA_H +#define LIME_FPGA_H + +#include #include #include #include @@ -53,9 +54,10 @@ class FPGA virtual OpStatus SetInterfaceFreq(double f_Tx_Hz, double f_Rx_Hz, int chipIndex = 0); double DetectRefClk(double fx3Clk = 100e6); - static int FPGAPacketPayload2Samples(const uint8_t* buffer, int bufLen, bool mimo, bool compressed, complex16_t** samples); + static int FPGAPacketPayload2Samples( + const uint8_t* buffer, int bufLen, bool mimo, bool compressed, complex16_t* const* samples); static int FPGAPacketPayload2SamplesFloat( - const uint8_t* buffer, int bufLen, bool mimo, bool compressed, complex32f_t** samples); + const uint8_t* buffer, int bufLen, bool mimo, bool compressed, complex32f_t* const* samples); static int Samples2FPGAPacketPayload( const complex16_t* const* samples, int samplesCount, bool mimo, bool compressed, uint8_t* buffer); static int Samples2FPGAPacketPayloadFloat( @@ -65,8 +67,8 @@ class FPGA virtual OpStatus ReadRegisters(const uint32_t* addrs, uint32_t* data, unsigned cnt); OpStatus WriteRegister(uint32_t addr, uint32_t val); int ReadRegister(uint32_t addr); - int WriteLMS7002MSPI(const uint32_t* addr, uint32_t length); - int ReadLMS7002MSPI(const uint32_t* addr, uint32_t* values, uint32_t length); + OpStatus WriteLMS7002MSPI(const uint32_t* addr, uint32_t length); + OpStatus ReadLMS7002MSPI(const uint32_t* addr, uint32_t* values, uint32_t length); /** @brief Structure containing the gateware information of the FPGA */ struct GatewareInfo { @@ -94,4 +96,4 @@ class FPGA }; } // namespace lime -#endif // FPGA_COMMON_H +#endif // LIME_FPGA_H diff --git a/src/FPGA_common/WriteRegistersBatch.cpp b/src/FPGA_common/WriteRegistersBatch.cpp new file mode 100644 index 000000000..fbe648e90 --- /dev/null +++ b/src/FPGA_common/WriteRegistersBatch.cpp @@ -0,0 +1,40 @@ +#include "WriteRegistersBatch.h" + +#ifndef NDEBUG + #define ASSERT_WARNING(cond, message) \ + if (!cond) \ + lime::warning("%s (%s)\n", message, #cond) +#else + #define ASSERT_WARNING(cond, message) +#endif + +using namespace lime; + +/// @brief Constructor for the batch. +/// @param fpga The FPGA this batch belongs to. +WriteRegistersBatch::WriteRegistersBatch(FPGA* fpga) + : owner(fpga){}; + +WriteRegistersBatch::~WriteRegistersBatch() +{ + ASSERT_WARNING(addrs.size() == 0, "FPGA WriteRegistersBatch not flushed"); +} + +/// @brief Writes the modified values into the FPGA. +/// @return The operation status. +OpStatus WriteRegistersBatch::Flush() +{ + OpStatus status = owner->WriteRegisters(addrs.data(), values.data(), addrs.size()); + addrs.clear(); + values.clear(); + return status; +} + +/// @brief Sets an address value pair to write into the FPGA on flushing. +/// @param addr The address to write to. +/// @param value The value to write. +void WriteRegistersBatch::WriteRegister(uint16_t addr, uint16_t value) +{ + addrs.push_back(addr); + values.push_back(value); +} diff --git a/src/FPGA_common/WriteRegistersBatch.h b/src/FPGA_common/WriteRegistersBatch.h new file mode 100644 index 000000000..bc22ff9ff --- /dev/null +++ b/src/FPGA_common/WriteRegistersBatch.h @@ -0,0 +1,29 @@ +#ifndef LIME_WRITEREGISTERSBATCH_H +#define LIME_WRITEREGISTERSBATCH_H + +#include +#include + +#include "FPGA_common.h" + +namespace lime { + +/** @brief A class for writing a batch of registers into the FPGA. */ +class WriteRegistersBatch +{ + public: + WriteRegistersBatch(FPGA* fpga); + ~WriteRegistersBatch(); + + OpStatus Flush(); + void WriteRegister(uint16_t addr, uint16_t value); + + private: + FPGA* owner; + std::vector addrs; + std::vector values; +}; + +} // namespace lime + +#endif // LIME_WRITEREGISTERSBATCH_H diff --git a/src/SDRDevice.cpp b/src/SDRDevice.cpp deleted file mode 100644 index 78351c6b2..000000000 --- a/src/SDRDevice.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "limesuite/SDRDevice.h" - -using namespace lime; - -SDRDevice::StreamConfig::Extras::Extras() -{ - memset(this, 0, sizeof(Extras)); - usePoll = true; -}; - -SDRDevice::StreamConfig::StreamConfig() -{ - memset(this, 0, sizeof(StreamConfig)); -} - -SDRDevice::StreamConfig::~StreamConfig() -{ - if (extraConfig) - delete extraConfig; -} - -SDRDevice::StreamConfig& SDRDevice::StreamConfig::operator=(const SDRDevice::StreamConfig& src) -{ - if (this == &src) - return *this; - - if (extraConfig) - { - delete extraConfig; - extraConfig = nullptr; - } - memcpy(this, &src, sizeof(SDRDevice::StreamConfig)); - if (src.extraConfig) - { - this->extraConfig = new Extras(); - *this->extraConfig = *src.extraConfig; - } - return *this; -} - -void SDRDevice::StreamStart(const std::vector moduleIndexes) -{ - for (uint8_t i : moduleIndexes) - StreamStart(i); -}; - -void SDRDevice::StreamStop(const std::vector moduleIndexes) -{ - for (uint8_t i : moduleIndexes) - StreamStop(i); -}; \ No newline at end of file diff --git a/src/Si5351C/Si5351C.cpp b/src/Si5351C/Si5351C.cpp index 4ffde26f1..581f60c8e 100644 --- a/src/Si5351C/Si5351C.cpp +++ b/src/Si5351C/Si5351C.cpp @@ -526,7 +526,9 @@ Si5351C::~Si5351C() { } -/** @brief Sends Configuration to Si5351C +/** + * @brief Sends Configuration to Si5351C + * @return The status code of the operation */ Si5351C::Status Si5351C::UploadConfiguration() { @@ -573,7 +575,7 @@ Si5351C::Status Si5351C::UploadConfiguration() @brief Loads register values for Si5356A from file @param FName input filename */ -bool Si5351C::LoadRegValuesFromFile(string FName) +void Si5351C::LoadRegValuesFromFile(string FName) { fstream fin; fin.open(FName, ios::in); @@ -587,16 +589,15 @@ bool Si5351C::LoadRegValuesFromFile(string FName) while (!fin.eof()) { fin.getline(line, len); - if (line[0] == '#') - continue; if (strcmp(line, "#END_PROFILE") == 0) break; + if (line[0] == '#') + continue; sscanf(line, "%i,%x", &addr, &value); m_newConfiguration[addr] = value; } fin.close(); - return false; } /** @brief Calculates multisynth dividers and VCO frequencies diff --git a/src/Si5351C/Si5351C.h b/src/Si5351C/Si5351C.h index 2ace29dc1..8325c199f 100644 --- a/src/Si5351C/Si5351C.h +++ b/src/Si5351C/Si5351C.h @@ -56,7 +56,10 @@ struct Si5351_PLL { class II2C; -/** @brief Class for controlling the Si5351C I2C-programmable any-frequency CMOS clock generator + VCXO */ +/** @brief Class for controlling the Si5351C I2C-programmable any-frequency CMOS clock generator + VCXO + * + * More information: https://www.skyworksinc.com/en/Products/Timing/CMOS-Clock-Generators/Si5351C-GM1 + */ class LIME_API Si5351C { public: @@ -93,7 +96,7 @@ class LIME_API Si5351C Si5351C(II2C& i2c_comms); ~Si5351C(); - bool LoadRegValuesFromFile(std::string FName); + void LoadRegValuesFromFile(std::string FName); void SetPLL(unsigned char id, unsigned long CLKIN_Hz, int CLK_SRC); void SetClock(unsigned char id, unsigned long fOut_Hz, bool enabled = true, bool inverted = false); diff --git a/src/Si5351C/Si5351C_wxgui.h b/src/Si5351C/Si5351C_wxgui.h index 2be7344dd..bb0642870 100644 --- a/src/Si5351C/Si5351C_wxgui.h +++ b/src/Si5351C/Si5351C_wxgui.h @@ -140,7 +140,6 @@ class Si5351C_wxgui : public wxFrame void OnButton1Click(wxCommandEvent& event); void OnbtnReadStatusClick(wxCommandEvent& event); void OnbtnClearStatusClick(wxCommandEvent& event); - bool LoadRegValuesFromFile(std::string FName); unsigned char m_newConfiguration[255]; lime::SDRDevice* device; //*) diff --git a/src/StreamComposite.cpp b/src/StreamComposite.cpp index 10f5ea683..a67341e37 100644 --- a/src/StreamComposite.cpp +++ b/src/StreamComposite.cpp @@ -75,42 +75,46 @@ void StreamComposite::StreamStop() g.first->StreamStop(g.second); } -template int StreamComposite::StreamRx(T** samples, uint32_t count, SDRDevice::StreamMeta* meta) +template uint32_t StreamComposite::StreamRx(T** samples, uint32_t count, SDRDevice::StreamMeta* meta) { T** dest = samples; for (auto& a : mActiveAggregates) { - int ret = a.device->StreamRx(a.streamIndex, dest, count, meta); - if (ret != static_cast(count)) + uint32_t ret = a.device->StreamRx(a.streamIndex, dest, count, meta); + if (ret != count) + { return ret; + } dest += a.channels.size(); } - return static_cast(count); + return count; } -template int StreamComposite::StreamTx(const T* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta) +template uint32_t StreamComposite::StreamTx(const T* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta) { const T* const* src = samples; for (auto& a : mActiveAggregates) { - int ret = a.device->StreamTx(a.streamIndex, src, count, meta); - if (ret != static_cast(count)) + uint32_t ret = a.device->StreamTx(a.streamIndex, src, count, meta); + if (ret != count) + { return ret; + } src += a.channels.size(); } - return static_cast(count); + return count; } // force instantiate functions with these types -template LIME_API int StreamComposite::StreamRx( +template LIME_API uint32_t StreamComposite::StreamRx( lime::complex16_t** samples, uint32_t count, SDRDevice::StreamMeta* meta); -template LIME_API int StreamComposite::StreamRx( +template LIME_API uint32_t StreamComposite::StreamRx( lime::complex32f_t** samples, uint32_t count, SDRDevice::StreamMeta* meta); -template LIME_API int StreamComposite::StreamTx( +template LIME_API uint32_t StreamComposite::StreamTx( const lime::complex16_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); -template LIME_API int StreamComposite::StreamTx( +template LIME_API uint32_t StreamComposite::StreamTx( const lime::complex32f_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); } // namespace lime diff --git a/src/boards/LMS7002M_SDRDevice.cpp b/src/boards/LMS7002M_SDRDevice.cpp index f7862e666..5ba9aaaa9 100644 --- a/src/boards/LMS7002M_SDRDevice.cpp +++ b/src/boards/LMS7002M_SDRDevice.cpp @@ -422,7 +422,9 @@ OpStatus LMS7002M_SDRDevice::ConfigureGFIR( uint8_t moduleIndex, TRXDir trx, uint8_t channel, ChannelConfig::Direction::GFIRFilter settings) { LMS7002M* lms = mLMSChips.at(moduleIndex); - return lms->SetGFIRFilter(trx, channel, settings.enabled, settings.bandwidth); + LMS7002M::Channel enumChannel = channel > 0 ? LMS7002M::Channel::ChB : LMS7002M::Channel::ChA; + + return lms->SetGFIRFilter(trx, enumChannel, settings.enabled, settings.bandwidth); } OpStatus LMS7002M_SDRDevice::SetGain(uint8_t moduleIndex, TRXDir direction, uint8_t channel, eGainTypes gain, double value) @@ -934,32 +936,35 @@ void LMS7002M_SDRDevice::StreamStop(uint8_t moduleIndex) mStreamers[moduleIndex] = nullptr; } -int LMS7002M_SDRDevice::StreamRx(uint8_t moduleIndex, complex32f_t* const* dest, uint32_t count, StreamMeta* meta) +uint32_t LMS7002M_SDRDevice::StreamRx(uint8_t moduleIndex, complex32f_t* const* dest, uint32_t count, StreamMeta* meta) { return mStreamers[moduleIndex]->StreamRx(dest, count, meta); } -int LMS7002M_SDRDevice::StreamRx(uint8_t moduleIndex, complex16_t* const* dest, uint32_t count, StreamMeta* meta) +uint32_t LMS7002M_SDRDevice::StreamRx(uint8_t moduleIndex, complex16_t* const* dest, uint32_t count, StreamMeta* meta) { return mStreamers[moduleIndex]->StreamRx(dest, count, meta); } -int LMS7002M_SDRDevice::StreamRx(uint8_t moduleIndex, complex12_t* const* dest, uint32_t count, StreamMeta* meta) +uint32_t LMS7002M_SDRDevice::StreamRx(uint8_t moduleIndex, complex12_t* const* dest, uint32_t count, StreamMeta* meta) { return mStreamers[moduleIndex]->StreamRx(dest, count, meta); } -int LMS7002M_SDRDevice::StreamTx(uint8_t moduleIndex, const complex32f_t* const* samples, uint32_t count, const StreamMeta* meta) +uint32_t LMS7002M_SDRDevice::StreamTx( + uint8_t moduleIndex, const complex32f_t* const* samples, uint32_t count, const StreamMeta* meta) { return mStreamers[moduleIndex]->StreamTx(samples, count, meta); } -int LMS7002M_SDRDevice::StreamTx(uint8_t moduleIndex, const complex16_t* const* samples, uint32_t count, const StreamMeta* meta) +uint32_t LMS7002M_SDRDevice::StreamTx( + uint8_t moduleIndex, const complex16_t* const* samples, uint32_t count, const StreamMeta* meta) { return mStreamers[moduleIndex]->StreamTx(samples, count, meta); } -int LMS7002M_SDRDevice::StreamTx(uint8_t moduleIndex, const complex12_t* const* samples, uint32_t count, const StreamMeta* meta) +uint32_t LMS7002M_SDRDevice::StreamTx( + uint8_t moduleIndex, const complex12_t* const* samples, uint32_t count, const StreamMeta* meta) { return mStreamers[moduleIndex]->StreamTx(samples, count, meta); } @@ -1004,7 +1009,7 @@ OpStatus LMS7002M_SDRDevice::UpdateFPGAInterfaceFrequency(LMS7002M& soc, FPGA& f OpStatus status = fpga.SetInterfaceFreq(fpgaTxPLL, fpgaRxPLL, chipIndex); if (status != OpStatus::SUCCESS) return status; - soc.ResetLogicregisters(); + soc.ResetLogicRegisters(); return OpStatus::SUCCESS; } @@ -1100,7 +1105,7 @@ OpStatus LMS7002M_SDRDevice::LMS7002LOConfigure(LMS7002M* chip, const SDRDevice: OpStatus status = OpStatus::SUCCESS; if (cfg.referenceClockFreq != 0) { - status = chip->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, cfg.referenceClockFreq, 0); + status = chip->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, cfg.referenceClockFreq); if (status != OpStatus::SUCCESS) return status; } @@ -1159,13 +1164,16 @@ OpStatus LMS7002M_SDRDevice::LMS7002ChannelConfigure(LMS7002M* chip, const SDRDe OpStatus LMS7002M_SDRDevice::LMS7002ChannelCalibration(LMS7002M* chip, const SDRDevice::ChannelConfig& config, uint8_t channelIndex) { int i = channelIndex; - chip->SetActiveChannel(i == 0 ? LMS7002M::Channel::ChA : LMS7002M::Channel::ChB); + auto enumChannel = i == 0 ? LMS7002M::Channel::ChA : LMS7002M::Channel::ChB; + chip->SetActiveChannel(enumChannel); const SDRDevice::ChannelConfig& ch = config; // TODO: Don't configure GFIR when external ADC/DAC is used - if (ch.rx.enabled && chip->SetGFIRFilter(TRXDir::Rx, i, ch.rx.gfir.enabled, ch.rx.gfir.bandwidth) != OpStatus::SUCCESS) + if (ch.rx.enabled && + chip->SetGFIRFilter(TRXDir::Rx, enumChannel, ch.rx.gfir.enabled, ch.rx.gfir.bandwidth) != OpStatus::SUCCESS) return lime::ReportError(OpStatus::ERROR, "Rx ch%i GFIR config failed", i); - if (ch.tx.enabled && chip->SetGFIRFilter(TRXDir::Tx, i, ch.tx.gfir.enabled, ch.tx.gfir.bandwidth) != OpStatus::SUCCESS) + if (ch.tx.enabled && + chip->SetGFIRFilter(TRXDir::Tx, enumChannel, ch.tx.gfir.enabled, ch.tx.gfir.bandwidth) != OpStatus::SUCCESS) return lime::ReportError(OpStatus::ERROR, "Tx ch%i GFIR config failed", i); if (ch.rx.calibrate && ch.rx.enabled) diff --git a/src/boards/LMS7002M_SDRDevice.h b/src/boards/LMS7002M_SDRDevice.h index 5da4172ec..77c5b03f6 100644 --- a/src/boards/LMS7002M_SDRDevice.h +++ b/src/boards/LMS7002M_SDRDevice.h @@ -99,12 +99,15 @@ class LIME_API LMS7002M_SDRDevice : public SDRDevice virtual void StreamStart(uint8_t moduleIndex) override; virtual void StreamStop(uint8_t moduleIndex) override; - virtual int StreamRx(uint8_t moduleIndex, complex32f_t* const* samples, uint32_t count, StreamMeta* meta) override; - virtual int StreamRx(uint8_t moduleIndex, complex16_t* const* samples, uint32_t count, StreamMeta* meta) override; - virtual int StreamRx(uint8_t moduleIndex, complex12_t* const* samples, uint32_t count, StreamMeta* meta) override; - virtual int StreamTx(uint8_t moduleIndex, const complex32f_t* const* samples, uint32_t count, const StreamMeta* meta) override; - virtual int StreamTx(uint8_t moduleIndex, const complex16_t* const* samples, uint32_t count, const StreamMeta* meta) override; - virtual int StreamTx(uint8_t moduleIndex, const complex12_t* const* samples, uint32_t count, const StreamMeta* meta) override; + virtual uint32_t StreamRx(uint8_t moduleIndex, complex32f_t* const* samples, uint32_t count, StreamMeta* meta) override; + virtual uint32_t StreamRx(uint8_t moduleIndex, complex16_t* const* samples, uint32_t count, StreamMeta* meta) override; + virtual uint32_t StreamRx(uint8_t moduleIndex, complex12_t* const* samples, uint32_t count, StreamMeta* meta) override; + virtual uint32_t StreamTx( + uint8_t moduleIndex, const complex32f_t* const* samples, uint32_t count, const StreamMeta* meta) override; + virtual uint32_t StreamTx( + uint8_t moduleIndex, const complex16_t* const* samples, uint32_t count, const StreamMeta* meta) override; + virtual uint32_t StreamTx( + uint8_t moduleIndex, const complex12_t* const* samples, uint32_t count, const StreamMeta* meta) override; virtual void StreamStatus(uint8_t moduleIndex, SDRDevice::StreamStats* rx, SDRDevice::StreamStats* tx) override; virtual void SetDataLogCallback(DataCallbackType callback) override; @@ -115,7 +118,9 @@ class LIME_API LMS7002M_SDRDevice : public SDRDevice virtual OpStatus UploadMemory( eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback) override; + /// @copydoc FPGA::ReadRegister() virtual int ReadFPGARegister(uint32_t address); + /// @copydoc FPGA::WriteRegister() virtual OpStatus WriteFPGARegister(uint32_t address, uint32_t value); protected: diff --git a/src/boards/LimeSDR/LimeSDR.cpp b/src/boards/LimeSDR/LimeSDR.cpp index ef209221e..b60b53545 100644 --- a/src/boards/LimeSDR/LimeSDR.cpp +++ b/src/boards/LimeSDR/LimeSDR.cpp @@ -96,6 +96,11 @@ static inline void ValidateChannel(uint8_t channel) throw std::logic_error("invalid channel index"); } +/// @brief Constructs a new LimeSDR object +/// @param spiLMS The communications port to the LMS7002M chip. +/// @param spiFPGA The communications port to the device's FPGA. +/// @param streamPort The communications port to send and receive sample data. +/// @param commsPort The communications port for direct communications with the device. LimeSDR::LimeSDR(std::shared_ptr spiLMS, std::shared_ptr spiFPGA, std::shared_ptr streamPort, @@ -462,19 +467,12 @@ uint8_t LimeSDR::GetPath(SDRDevice::Dir dir, uint8_t channel) const double LimeSDR::GetClockFreq(uint8_t clk_id, uint8_t channel) { - return mLMSChips[0]->GetClockFreq(static_cast(clk_id), channel); + return mLMSChips[0]->GetClockFreq(static_cast(clk_id)); } OpStatus LimeSDR::SetClockFreq(uint8_t clk_id, double freq, uint8_t channel) { - return mLMSChips[0]->SetClockFreq(static_cast(clk_id), freq, channel); -} - -void LimeSDR::EnableCache(bool enable) -{ - mLMSChips[0]->EnableValuesCache(enable); - if (mFPGA) - mFPGA->EnableValuesCache(enable); + return mLMSChips[0]->SetClockFreq(static_cast(clk_id), freq); } OpStatus LimeSDR::SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) diff --git a/src/boards/LimeSDR/LimeSDR.h b/src/boards/LimeSDR/LimeSDR.h index e54dc5825..37b1f8da8 100644 --- a/src/boards/LimeSDR/LimeSDR.h +++ b/src/boards/LimeSDR/LimeSDR.h @@ -32,8 +32,6 @@ class LimeSDR : public LMS7002M_SDRDevice virtual OpStatus SetSampleRate( uint8_t moduleIndex, TRXDir trx, uint8_t channel, double sampleRate, uint8_t oversample) override; - virtual void EnableCache(bool enable) override; - virtual OpStatus SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; virtual OpStatus StreamSetup(const StreamConfig& config, uint8_t moduleIndex) override; diff --git a/src/boards/LimeSDR/USB_CSR_Pipe_SDR.h b/src/boards/LimeSDR/USB_CSR_Pipe_SDR.h index 92a70a121..1645dae16 100644 --- a/src/boards/LimeSDR/USB_CSR_Pipe_SDR.h +++ b/src/boards/LimeSDR/USB_CSR_Pipe_SDR.h @@ -12,10 +12,14 @@ namespace lime { class USB_CSR_Pipe_SDR : public USB_CSR_Pipe { public: + /** + @brief Constructs a new USB_CSR_Pipe_SDR object + @param port The FX3 communications port to use. + */ explicit USB_CSR_Pipe_SDR(FX3& port); - virtual int Write(const uint8_t* data, size_t length, int timeout_ms) override; - virtual int Read(uint8_t* data, size_t length, int timeout_ms) override; + virtual int Write(const uint8_t* data, std::size_t length, int timeout_ms) override; + virtual int Read(uint8_t* data, std::size_t length, int timeout_ms) override; protected: FX3& port; diff --git a/src/boards/LimeSDR_Mini/FPGA_Mini.cpp b/src/boards/LimeSDR_Mini/FPGA_Mini.cpp index ebde6ca08..c60099ef4 100644 --- a/src/boards/LimeSDR_Mini/FPGA_Mini.cpp +++ b/src/boards/LimeSDR_Mini/FPGA_Mini.cpp @@ -15,7 +15,7 @@ FPGA_Mini::FPGA_Mini(std::shared_ptr fpgaSPI, std::shared_ptr lms700 { } -OpStatus FPGA_Mini::SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, double txPhase, double rxPhase, int channel) +OpStatus FPGA_Mini::SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, double txPhase, double rxPhase, int chipIndex) { OpStatus status = OpStatus::SUCCESS; @@ -44,17 +44,16 @@ OpStatus FPGA_Mini::SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, double clocks[3].phaseShift_deg = rxPhase; status = SetPllFrequency(0, rxRate_Hz, clocks); + return status; } - else - { - status = SetDirectClocking(0); - if (status == OpStatus::SUCCESS) - { - status = SetDirectClocking(1); - } + status = SetDirectClocking(0); + if (status != OpStatus::SUCCESS) + { + return status; } + status = SetDirectClocking(1); return status; } @@ -79,7 +78,7 @@ OpStatus FPGA_Mini::SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, int cha if (!phaseSearch) { - return SetInterfaceFreq(txRate_Hz, rxRate_Hz, txPhC1 + txPhC2 * txRate_Hz, rxPhC1 + rxPhC2 * rxRate_Hz, 0); + return SetInterfaceFreq(txRate_Hz, rxRate_Hz, txPhC1 + txPhC2 * txRate_Hz, rxPhC1 + rxPhC2 * rxRate_Hz); } std::vector dataRd; @@ -184,7 +183,7 @@ OpStatus FPGA_Mini::SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, int cha WriteRegister(0x000A, 0); if (!rxPhaseSearchSuccess || !txPhaseSearchSuccess) - return SetInterfaceFreq(txRate_Hz, rxRate_Hz, txPhC1 + txPhC2 * txRate_Hz, rxPhC1 + rxPhC2 * rxRate_Hz, 0); + return SetInterfaceFreq(txRate_Hz, rxRate_Hz, txPhC1 + txPhC2 * txRate_Hz, rxPhC1 + rxPhC2 * rxRate_Hz); return OpStatus::SUCCESS; } diff --git a/src/boards/LimeSDR_Mini/FPGA_Mini.h b/src/boards/LimeSDR_Mini/FPGA_Mini.h index 681583832..8e7c1cf73 100644 --- a/src/boards/LimeSDR_Mini/FPGA_Mini.h +++ b/src/boards/LimeSDR_Mini/FPGA_Mini.h @@ -13,8 +13,9 @@ class FPGA_Mini : public FPGA public: FPGA_Mini(std::shared_ptr fpgaSPI, std::shared_ptr lms7002mSPI); virtual ~FPGA_Mini(){}; - virtual OpStatus SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, double txPhase, double rxPhase, int channel) override; - virtual OpStatus SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, int channel) override; + virtual OpStatus SetInterfaceFreq( + double txRate_Hz, double rxRate_Hz, double txPhase, double rxPhase, int chipIndex = 0) override; + virtual OpStatus SetInterfaceFreq(double txRate_Hz, double rxRate_Hz, int chipIndex = 0) override; }; } // namespace lime diff --git a/src/boards/LimeSDR_Mini/LimeSDR_Mini.cpp b/src/boards/LimeSDR_Mini/LimeSDR_Mini.cpp index 5bf50c206..122c53d7f 100644 --- a/src/boards/LimeSDR_Mini/LimeSDR_Mini.cpp +++ b/src/boards/LimeSDR_Mini/LimeSDR_Mini.cpp @@ -135,6 +135,11 @@ static const std::vector> lms7002defaultsOverrides { 0x040C, 0x00FB } }; +/// @brief Constructs a new LimeSDR_Mini object +/// @param spiLMS The communications port to the LMS7002M chip. +/// @param spiFPGA The communications port to the device's FPGA. +/// @param streamPort The communications port to send and receive sample data. +/// @param commsPort The communications port for direct communications with the device. LimeSDR_Mini::LimeSDR_Mini(std::shared_ptr spiLMS, std::shared_ptr spiFPGA, std::shared_ptr streamPort, @@ -243,7 +248,7 @@ OpStatus LimeSDR_Mini::Configure(const SDRConfig& cfg, uint8_t moduleIndex = 0) if (cfg.referenceClockFreq != 0) { - mLMSChips[0]->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, cfg.referenceClockFreq, 0); + mLMSChips[0]->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, cfg.referenceClockFreq); } if (rxUsed) @@ -312,7 +317,7 @@ OpStatus LimeSDR_Mini::Init() lms->Modify_SPI_Reg_bits(LMS7param(MAC), 1); - if (lms->CalibrateTxGain(0, nullptr) != OpStatus::SUCCESS) + if (lms->CalibrateTxGain() != OpStatus::SUCCESS) return OpStatus::ERROR; lms->EnableChannel(TRXDir::Tx, 0, false); @@ -358,12 +363,12 @@ OpStatus LimeSDR_Mini::Reset() double LimeSDR_Mini::GetClockFreq(uint8_t clk_id, uint8_t channel) { - return mLMSChips[0]->GetClockFreq(static_cast(clk_id), channel); + return mLMSChips[0]->GetClockFreq(static_cast(clk_id)); } OpStatus LimeSDR_Mini::SetClockFreq(uint8_t clk_id, double freq, uint8_t channel) { - return mLMSChips[0]->SetClockFreq(static_cast(clk_id), freq, channel); + return mLMSChips[0]->SetClockFreq(static_cast(clk_id), freq); } OpStatus LimeSDR_Mini::Synchronize(bool toChip) @@ -381,16 +386,6 @@ OpStatus LimeSDR_Mini::Synchronize(bool toChip) return mLMSChips[0]->DownloadAll(); } -void LimeSDR_Mini::EnableCache(bool enable) -{ - mLMSChips[0]->EnableValuesCache(enable); - - if (mFPGA) - { - mFPGA->EnableValuesCache(enable); - } -} - OpStatus LimeSDR_Mini::SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) { assert(mStreamPort); @@ -658,11 +653,6 @@ void LimeSDR_Mini::StreamStop(uint8_t moduleIndex) mStreamers[0] = nullptr; } -void* LimeSDR_Mini::GetInternalChip(uint32_t index) -{ - return mLMSChips.at(index); -} - OpStatus LimeSDR_Mini::GPIODirRead(uint8_t* buffer, const size_t bufLength) { if (!buffer || bufLength == 0) diff --git a/src/boards/LimeSDR_Mini/LimeSDR_Mini.h b/src/boards/LimeSDR_Mini/LimeSDR_Mini.h index 19cceefd3..d0ce66f37 100644 --- a/src/boards/LimeSDR_Mini/LimeSDR_Mini.h +++ b/src/boards/LimeSDR_Mini/LimeSDR_Mini.h @@ -35,7 +35,6 @@ class LimeSDR_Mini : public LMS7002M_SDRDevice virtual double GetTemperature(uint8_t moduleIndex) override; virtual OpStatus Synchronize(bool toChip) override; - virtual void EnableCache(bool enable) override; virtual OpStatus SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; @@ -44,8 +43,6 @@ class LimeSDR_Mini : public LMS7002M_SDRDevice virtual void StreamStart(uint8_t moduleIndex) override; virtual void StreamStop(uint8_t moduleIndex) override; - virtual void* GetInternalChip(uint32_t index) override; - virtual OpStatus GPIODirRead(uint8_t* buffer, const size_t bufLength) override; virtual OpStatus GPIORead(uint8_t* buffer, const size_t bufLength) override; virtual OpStatus GPIODirWrite(const uint8_t* buffer, const size_t bufLength) override; diff --git a/src/boards/LimeSDR_Mini/USB_CSR_Pipe_Mini.h b/src/boards/LimeSDR_Mini/USB_CSR_Pipe_Mini.h index e46e567ca..5bb0e3ad3 100644 --- a/src/boards/LimeSDR_Mini/USB_CSR_Pipe_Mini.h +++ b/src/boards/LimeSDR_Mini/USB_CSR_Pipe_Mini.h @@ -10,10 +10,14 @@ namespace lime { class USB_CSR_Pipe_Mini : public USB_CSR_Pipe { public: + /** + @brief Constructs a new USB_CSR_Pipe_Mini object + @param port The FT601 communications port to use. + */ explicit USB_CSR_Pipe_Mini(FT601& port); - virtual int Write(const uint8_t* data, size_t length, int timeout_ms) override; - virtual int Read(uint8_t* data, size_t length, int timeout_ms) override; + virtual int Write(const uint8_t* data, std::size_t length, int timeout_ms) override; + virtual int Read(uint8_t* data, std::size_t length, int timeout_ms) override; protected: FT601& port; diff --git a/src/boards/LimeSDR_X3/LimeSDR_X3.cpp b/src/boards/LimeSDR_X3/LimeSDR_X3.cpp index 61d98ca0c..d5fc6d2b7 100644 --- a/src/boards/LimeSDR_X3/LimeSDR_X3.cpp +++ b/src/boards/LimeSDR_X3/LimeSDR_X3.cpp @@ -154,8 +154,12 @@ OpStatus LimeSDR_X3::LMS1_UpdateFPGAInterface(void* userData) return UpdateFPGAInterfaceFrequency(*soc, *pthis->mFPGA, chipIndex); } -// Do not perform any unnecessary configuring to device in constructor, so you -// could read back it's state for debugging purposes +/// @brief Constructs a new LimeSDR_X3 object +/// +/// @param spiLMS7002M The communications port to the LMS7002M chips. +/// @param spiFPGA The communications port to the device's FPGA. +/// @param trxStreams The communications ports to send and receive sample data. +/// @param control The serial port of the device for retrieving device firmware information. LimeSDR_X3::LimeSDR_X3(std::shared_ptr spiLMS7002M, std::shared_ptr spiFPGA, std::vector> trxStreams, @@ -163,13 +167,14 @@ LimeSDR_X3::LimeSDR_X3(std::shared_ptr spiLMS7002M, : LMS7002M_SDRDevice() , mTRXStreamPorts(trxStreams) , mfpgaPort(spiFPGA) - , mSerialPort(control) , mConfigInProgress(false) { + /// Do not perform any unnecessary configuring to device in constructor, so you + /// could read back it's state for debugging purposes SDRDevice::Descriptor& desc = mDeviceDescriptor; LMS64CProtocol::FirmwareInfo fw; - LMS64CProtocol::GetFirmwareInfo(*mSerialPort, fw); + LMS64CProtocol::GetFirmwareInfo(*control, fw); LMS64CProtocol::FirmwareToDescriptor(fw, desc); desc.spiSlaveIds = { @@ -658,7 +663,10 @@ void LimeSDR_X3::ConfigureDirection(TRXDir dir, LMS7002M* chip, const SDRConfig& if (socIndex == 0) { - if (trx.enabled && chip->SetGFIRFilter(dir, ch, trx.gfir.enabled, trx.gfir.bandwidth) != OpStatus::SUCCESS) + if (trx.enabled && chip->SetGFIRFilter(dir, + ch == 0 ? LMS7002M::Channel::ChA : LMS7002M::Channel::ChB, + trx.gfir.enabled, + trx.gfir.bandwidth) != OpStatus::SUCCESS) { throw std::logic_error(strFormat("%s ch%i GFIR config failed", dirName, ch)); } @@ -833,14 +841,14 @@ double LimeSDR_X3::GetClockFreq(uint8_t clk_id, uint8_t channel) { ValidateChannel(channel); LMS7002M* chip = mLMSChips[channel / 2]; - return chip->GetClockFreq(static_cast(clk_id), channel & 1); + return chip->GetClockFreq(static_cast(clk_id)); } OpStatus LimeSDR_X3::SetClockFreq(uint8_t clk_id, double freq, uint8_t channel) { ValidateChannel(channel); LMS7002M* chip = mLMSChips[channel / 2]; - return chip->SetClockFreq(static_cast(clk_id), freq, channel & 1); + return chip->SetClockFreq(static_cast(clk_id), freq); } OpStatus LimeSDR_X3::SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) diff --git a/src/boards/LimeSDR_X3/LimeSDR_X3.h b/src/boards/LimeSDR_X3/LimeSDR_X3.h index 57eefe5a7..58ad921ef 100644 --- a/src/boards/LimeSDR_X3/LimeSDR_X3.h +++ b/src/boards/LimeSDR_X3/LimeSDR_X3.h @@ -16,6 +16,7 @@ class Equalizer; class SlaveSelectShim; class ISerialPort; +/** @brief Class for managing the LimeSDR X3 device. */ class LimeSDR_X3 : public LMS7002M_SDRDevice { public: @@ -85,7 +86,6 @@ class LimeSDR_X3 : public LMS7002M_SDRDevice std::array, 3> mLMS7002Mcomms; std::shared_ptr mfpgaPort; - std::shared_ptr mSerialPort; std::mutex mCommsMutex; bool mConfigInProgress; }; diff --git a/src/boards/LimeSDR_X3/LimeSDR_X3Entry.cpp b/src/boards/LimeSDR_X3/LimeSDR_X3Entry.cpp index ac2de7ddf..96c05db76 100644 --- a/src/boards/LimeSDR_X3/LimeSDR_X3Entry.cpp +++ b/src/boards/LimeSDR_X3/LimeSDR_X3Entry.cpp @@ -5,7 +5,9 @@ #include "LimeSDR_X3Entry.h" #include "LitePCIe.h" #include "LimeSDR_X3.h" -#include "PCIeCommon.h" +#include "PCIE_CSR_Pipe.h" +#include "LMS64C_FPGA_Over_PCIe.h" +#include "LMS64C_LMS7002M_Over_PCIe.h" #include #include diff --git a/src/boards/LimeSDR_X3/SlaveSelectShim.h b/src/boards/LimeSDR_X3/SlaveSelectShim.h index ad4ab1a8f..48a4de4f0 100644 --- a/src/boards/LimeSDR_X3/SlaveSelectShim.h +++ b/src/boards/LimeSDR_X3/SlaveSelectShim.h @@ -11,9 +11,19 @@ namespace lime { class LIME_API SlaveSelectShim : public ISPI { public: + /** + @brief Construct a new Slave Select Shim object + @param comms The communications interface to use. + @param slaveId The ID of the slave for this shim. + */ SlaveSelectShim(std::shared_ptr comms, uint32_t slaveId); virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + + /** + @brief Send the reset device command to the device under this shim. + @return The status of the operation. + */ virtual OpStatus ResetDevice(); private: @@ -23,4 +33,4 @@ class LIME_API SlaveSelectShim : public ISPI } // namespace lime -#endif // LIME_SLAVESELECTSHIM_H \ No newline at end of file +#endif // LIME_SLAVESELECTSHIM_H diff --git a/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.cpp b/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.cpp index 7d8a04cbf..dc85aa73d 100644 --- a/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.cpp +++ b/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.cpp @@ -103,8 +103,13 @@ OpStatus LimeSDR_XTRX::LMS1_UpdateFPGAInterface(void* userData) return UpdateFPGAInterfaceFrequency(*soc, *pthis->mFPGA, chipIndex); } -// Do not perform any unnecessary configuring to device in constructor, so you -// could read back it's state for debugging purposes +/// @brief Constructs a new LimeSDR_XTRX object +/// +/// @param spiRFsoc The communications port to the LMS7002M chip. +/// @param spiFPGA The communications port to the device's FPGA. +/// @param sampleStream The communications port to send and receive sample data. +/// @param control The serial port communication of the device. +/// @param refClk The reference clock of the device. LimeSDR_XTRX::LimeSDR_XTRX(std::shared_ptr spiRFsoc, std::shared_ptr spiFPGA, std::shared_ptr sampleStream, @@ -117,6 +122,8 @@ LimeSDR_XTRX::LimeSDR_XTRX(std::shared_ptr spiRFsoc, , mSerialPort(control) , mConfigInProgress(false) { + /// Do not perform any unnecessary configuring to device in constructor, so you + /// could read back it's state for debugging purposes. SDRDevice::Descriptor& desc = mDeviceDescriptor; desc.name = GetDeviceName(LMS_DEV_LIMESDR_XTRX); @@ -175,7 +182,7 @@ LimeSDR_XTRX::LimeSDR_XTRX(std::shared_ptr spiRFsoc, for (auto iter : mLMSChips) { iter->SetReferenceClk_SX(TRXDir::Rx, refClk); - iter->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, refClk, 0); + iter->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, refClk); } const int chipCount = mLMSChips.size(); @@ -337,14 +344,14 @@ double LimeSDR_XTRX::GetClockFreq(uint8_t clk_id, uint8_t channel) { ValidateChannel(channel); LMS7002M* chip = mLMSChips[channel / 2]; - return chip->GetClockFreq(static_cast(clk_id), channel & 1); + return chip->GetClockFreq(static_cast(clk_id)); } OpStatus LimeSDR_XTRX::SetClockFreq(uint8_t clk_id, double freq, uint8_t channel) { ValidateChannel(channel); LMS7002M* chip = mLMSChips[channel / 2]; - return chip->SetClockFreq(static_cast(clk_id), freq, channel & 1); + return chip->SetClockFreq(static_cast(clk_id), freq); } OpStatus LimeSDR_XTRX::SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) diff --git a/src/boards/LimeSDR_XTRX/LimeSDR_XTRXEntry.cpp b/src/boards/LimeSDR_XTRX/LimeSDR_XTRXEntry.cpp index 81bb4e9f8..48779d0f1 100644 --- a/src/boards/LimeSDR_XTRX/LimeSDR_XTRXEntry.cpp +++ b/src/boards/LimeSDR_XTRX/LimeSDR_XTRXEntry.cpp @@ -6,7 +6,9 @@ #include "LitePCIe.h" #include "LimeSDR_XTRX.h" #include "protocols/LMS64CProtocol.h" -#include "PCIeCommon.h" +#include "PCIE_CSR_Pipe.h" +#include "LMS64C_FPGA_Over_PCIe.h" +#include "LMS64C_LMS7002M_Over_PCIe.h" #include #include diff --git a/src/boards/MMX8/CMakeLists.txt b/src/boards/MMX8/CMakeLists.txt index d9a6a68e6..bf4dbf5ec 100644 --- a/src/boards/MMX8/CMakeLists.txt +++ b/src/boards/MMX8/CMakeLists.txt @@ -5,8 +5,11 @@ set(THIS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/boards/MMX8) set(MM_X8_SOURCES - ${THIS_SOURCE_DIR}/MM_X8Entry.cpp + ${THIS_SOURCE_DIR}/LMS64C_ADF_Over_PCIe_MMX8.cpp + ${THIS_SOURCE_DIR}/LMS64C_FPGA_Over_PCIe_MMX8.cpp + ${THIS_SOURCE_DIR}/LMS64C_LMS7002M_Over_PCIe_MMX8.cpp ${THIS_SOURCE_DIR}/MM_X8.cpp + ${THIS_SOURCE_DIR}/MM_X8Entry.cpp ) ######################################################################## diff --git a/src/boards/MMX8/LMS64C_ADF_Over_PCIe_MMX8.cpp b/src/boards/MMX8/LMS64C_ADF_Over_PCIe_MMX8.cpp new file mode 100644 index 000000000..c2891bcf1 --- /dev/null +++ b/src/boards/MMX8/LMS64C_ADF_Over_PCIe_MMX8.cpp @@ -0,0 +1,19 @@ +#include "LMS64C_ADF_Over_PCIe_MMX8.h" + +using namespace lime; + +LMS64C_ADF_Over_PCIe_MMX8::LMS64C_ADF_Over_PCIe_MMX8(std::shared_ptr dataPort, uint32_t subdeviceIndex) + : pipe(dataPort) + , subdeviceIndex(subdeviceIndex) +{ +} + +OpStatus LMS64C_ADF_Over_PCIe_MMX8::SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return LMS64CProtocol::ADF4002_SPI(pipe, MOSI, count, subdeviceIndex); +} + +OpStatus LMS64C_ADF_Over_PCIe_MMX8::SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return LMS64CProtocol::ADF4002_SPI(pipe, MOSI, count, subdeviceIndex); +} diff --git a/src/boards/MMX8/LMS64C_ADF_Over_PCIe_MMX8.h b/src/boards/MMX8/LMS64C_ADF_Over_PCIe_MMX8.h new file mode 100644 index 000000000..3b615e28a --- /dev/null +++ b/src/boards/MMX8/LMS64C_ADF_Over_PCIe_MMX8.h @@ -0,0 +1,34 @@ +#ifndef LIME_LMS64C_ADF_OVER_PCIE_MMX8_H +#define LIME_LMS64C_ADF_OVER_PCIE_MMX8_H + +#include "limesuite/IComms.h" +#include "LitePCIe.h" +#include "PCIE_CSR_Pipe.h" + +#include +#include + +namespace lime { + +/** @brief A class for communicating with MMX8's subdevice's ADF4002 chips. */ +class LMS64C_ADF_Over_PCIe_MMX8 : public ISPI +{ + public: + /** + @brief Constructs a new LMS64C_ADF_Over_PCIe_MMX8 object + @param dataPort The PCIe data bus to use. + @param subdeviceIndex The subdevice index for which this class is created. + */ + LMS64C_ADF_Over_PCIe_MMX8(std::shared_ptr dataPort, uint32_t subdeviceIndex); + + virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + + private: + PCIE_CSR_Pipe pipe; + uint32_t subdeviceIndex; +}; + +} // namespace lime + +#endif // LIME_LMS64C_ADF_OVER_PCIE_MMX8_H diff --git a/src/boards/MMX8/LMS64C_FPGA_Over_PCIe_MMX8.cpp b/src/boards/MMX8/LMS64C_FPGA_Over_PCIe_MMX8.cpp new file mode 100644 index 000000000..f221c856d --- /dev/null +++ b/src/boards/MMX8/LMS64C_FPGA_Over_PCIe_MMX8.cpp @@ -0,0 +1,46 @@ +#include "LMS64C_FPGA_Over_PCIe_MMX8.h" + +using namespace lime; + +LMS64C_FPGA_Over_PCIe_MMX8::LMS64C_FPGA_Over_PCIe_MMX8(std::shared_ptr dataPort, uint32_t subdeviceIndex) + : pipe(dataPort) + , subdeviceIndex(subdeviceIndex) +{ +} + +OpStatus LMS64C_FPGA_Over_PCIe_MMX8::SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return LMS64CProtocol::FPGA_SPI(pipe, MOSI, MISO, count, subdeviceIndex); +} + +OpStatus LMS64C_FPGA_Over_PCIe_MMX8::SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return LMS64CProtocol::FPGA_SPI(pipe, MOSI, MISO, count, subdeviceIndex); +} + +OpStatus LMS64C_FPGA_Over_PCIe_MMX8::CustomParameterWrite(const std::vector& parameters) +{ + return LMS64CProtocol::CustomParameterWrite(pipe, parameters, subdeviceIndex); +} + +OpStatus LMS64C_FPGA_Over_PCIe_MMX8::CustomParameterRead(std::vector& parameters) +{ + return LMS64CProtocol::CustomParameterRead(pipe, parameters, subdeviceIndex); +} + +OpStatus LMS64C_FPGA_Over_PCIe_MMX8::ProgramWrite( + const char* data, size_t length, int prog_mode, int target, ProgressCallback callback) +{ + return LMS64CProtocol::ProgramWrite( + pipe, data, length, prog_mode, static_cast(target), callback, subdeviceIndex); +} + +OpStatus LMS64C_FPGA_Over_PCIe_MMX8::MemoryWrite(uint32_t address, const void* data, uint32_t dataLength) +{ + return LMS64CProtocol::MemoryWrite(pipe, address, data, dataLength, subdeviceIndex); +} + +OpStatus LMS64C_FPGA_Over_PCIe_MMX8::MemoryRead(uint32_t address, void* data, uint32_t dataLength) +{ + return LMS64CProtocol::MemoryRead(pipe, address, data, dataLength, subdeviceIndex); +} diff --git a/src/boards/MMX8/LMS64C_FPGA_Over_PCIe_MMX8.h b/src/boards/MMX8/LMS64C_FPGA_Over_PCIe_MMX8.h new file mode 100644 index 000000000..55eb4430f --- /dev/null +++ b/src/boards/MMX8/LMS64C_FPGA_Over_PCIe_MMX8.h @@ -0,0 +1,43 @@ +#ifndef LIME_LMS64C_FPGA_OVER_PCIE_MMX8_H +#define LIME_LMS64C_FPGA_OVER_PCIE_MMX8_H + +#include "limesuite/IComms.h" +#include "LitePCIe.h" +#include "PCIE_CSR_Pipe.h" + +#include +#include + +namespace lime { + +/** @brief A class for communicating with MMX8's subdevice's FPGA chips. */ +class LMS64C_FPGA_Over_PCIe_MMX8 : public IComms +{ + public: + /** + @brief Constructs a new LMS64C_FPGA_Over_PCIe_MMX8 object. + @param dataPort The PCIe data bus to use. + @param subdeviceIndex The subdevice index for which this class is created. + */ + LMS64C_FPGA_Over_PCIe_MMX8(std::shared_ptr dataPort, uint32_t subdeviceIndex); + + virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + + virtual OpStatus CustomParameterWrite(const std::vector& parameters) override; + virtual OpStatus CustomParameterRead(std::vector& parameters) override; + + virtual OpStatus ProgramWrite( + const char* data, size_t length, int prog_mode, int target, ProgressCallback callback = nullptr) override; + + virtual OpStatus MemoryWrite(uint32_t address, const void* data, uint32_t dataLength) override; + virtual OpStatus MemoryRead(uint32_t address, void* data, uint32_t dataLength) override; + + private: + PCIE_CSR_Pipe pipe; + uint32_t subdeviceIndex; +}; + +} // namespace lime + +#endif // LIME_LMS64C_FPGA_OVER_PCIE_MMX8_H diff --git a/src/boards/MMX8/LMS64C_LMS7002M_Over_PCIe_MMX8.cpp b/src/boards/MMX8/LMS64C_LMS7002M_Over_PCIe_MMX8.cpp new file mode 100644 index 000000000..ba3fd4fd7 --- /dev/null +++ b/src/boards/MMX8/LMS64C_LMS7002M_Over_PCIe_MMX8.cpp @@ -0,0 +1,24 @@ +#include "LMS64C_LMS7002M_Over_PCIe_MMX8.h" + +using namespace lime; + +LMS64C_LMS7002M_Over_PCIe_MMX8::LMS64C_LMS7002M_Over_PCIe_MMX8(std::shared_ptr dataPort, uint32_t subdeviceIndex) + : pipe(dataPort) + , subdeviceIndex(subdeviceIndex) +{ +} + +OpStatus LMS64C_LMS7002M_Over_PCIe_MMX8::SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return SPI(0, MOSI, MISO, count); +} + +OpStatus LMS64C_LMS7002M_Over_PCIe_MMX8::SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return LMS64CProtocol::LMS7002M_SPI(pipe, spiBusAddress, MOSI, MISO, count, subdeviceIndex); +} + +OpStatus LMS64C_LMS7002M_Over_PCIe_MMX8::ResetDevice(int chipSelect) +{ + return LMS64CProtocol::DeviceReset(pipe, chipSelect, subdeviceIndex); +} diff --git a/src/boards/MMX8/LMS64C_LMS7002M_Over_PCIe_MMX8.h b/src/boards/MMX8/LMS64C_LMS7002M_Over_PCIe_MMX8.h new file mode 100644 index 000000000..c1077c8b8 --- /dev/null +++ b/src/boards/MMX8/LMS64C_LMS7002M_Over_PCIe_MMX8.h @@ -0,0 +1,34 @@ +#ifndef LIME_LMS64C_LMS7002M_OVER_PCIE_MMX8_H +#define LIME_LMS64C_LMS7002M_OVER_PCIE_MMX8_H + +#include "limesuite/IComms.h" +#include "LitePCIe.h" +#include "PCIE_CSR_Pipe.h" + +#include +#include + +namespace lime { + +/** @brief A class for communicating with MMX8's subdevice's LMS7002M chips. */ +class LMS64C_LMS7002M_Over_PCIe_MMX8 : public IComms +{ + public: + /** + @brief Construct a new LMS64C_LMS7002M_Over_PCIe_MMX8 object + @param dataPort The PCIe data bus to use. + @param subdeviceIndex The subdevice index for which this class is created. + */ + LMS64C_LMS7002M_Over_PCIe_MMX8(std::shared_ptr dataPort, uint32_t subdeviceIndex); + virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + virtual OpStatus ResetDevice(int chipSelect) override; + + private: + PCIE_CSR_Pipe pipe; + uint32_t subdeviceIndex; +}; + +} // namespace lime + +#endif // LIME_LMS64C_LMS7002M_OVER_PCIE_MMX8_H diff --git a/src/boards/MMX8/MM_X8.cpp b/src/boards/MMX8/MM_X8.cpp index 3f056e686..655105208 100644 --- a/src/boards/MMX8/MM_X8.cpp +++ b/src/boards/MMX8/MM_X8.cpp @@ -21,15 +21,23 @@ namespace lime { static SDRDevice::CustomParameter cp_vctcxo_dac = { "VCTCXO DAC (volatile)", 0, 0, 65535, false }; static double X8ReferenceClock = 30.72e6; -// Do not perform any unnecessary configuring to device in constructor, so you -// could read back it's state for debugging purposes +/// @brief Constructs the LimeSDR_MMX8 object. +/// +/// @param spiLMS7002M The communications ports to the LMS7002M chips. +/// @param spiFPGA The communications ports to the device's FPGA chips. +/// @param trxStreams The communications ports to send and receive sample data. +/// @param control The serial port communication of the device. +/// @param adfComms The communications port to the device's ADF4002 chip. LimeSDR_MMX8::LimeSDR_MMX8(std::vector>& spiLMS7002M, std::vector>& spiFPGA, std::vector> trxStreams, std::shared_ptr control, - ISPI* adfComms) + std::shared_ptr adfComms) : mTRXStreamPorts(trxStreams) { + /// Do not perform any unnecessary configuring to device in constructor, so you + /// could read back it's state for debugging purposes + mMainFPGAcomms = spiFPGA[8]; SDRDevice::Descriptor& desc = mDeviceDescriptor; desc.name = GetDeviceName(LMS_DEV_LIMESDR_MMX8); @@ -632,32 +640,35 @@ void LimeSDR_MMX8::StreamStop(const std::vector moduleIndexes) mSubDevices[moduleIndex]->StreamStop(0); } -int LimeSDR_MMX8::StreamRx(uint8_t moduleIndex, lime::complex32f_t* const* dest, uint32_t count, StreamMeta* meta) +uint32_t LimeSDR_MMX8::StreamRx(uint8_t moduleIndex, lime::complex32f_t* const* dest, uint32_t count, StreamMeta* meta) { return mSubDevices[moduleIndex]->StreamRx(0, dest, count, meta); } -int LimeSDR_MMX8::StreamRx(uint8_t moduleIndex, lime::complex16_t* const* dest, uint32_t count, StreamMeta* meta) +uint32_t LimeSDR_MMX8::StreamRx(uint8_t moduleIndex, lime::complex16_t* const* dest, uint32_t count, StreamMeta* meta) { return mSubDevices[moduleIndex]->StreamRx(0, dest, count, meta); } -int LimeSDR_MMX8::StreamRx(uint8_t moduleIndex, lime::complex12_t* const* dest, uint32_t count, StreamMeta* meta) +uint32_t LimeSDR_MMX8::StreamRx(uint8_t moduleIndex, lime::complex12_t* const* dest, uint32_t count, StreamMeta* meta) { return mSubDevices[moduleIndex]->StreamRx(0, dest, count, meta); } -int LimeSDR_MMX8::StreamTx(uint8_t moduleIndex, const lime::complex32f_t* const* samples, uint32_t count, const StreamMeta* meta) +uint32_t LimeSDR_MMX8::StreamTx( + uint8_t moduleIndex, const lime::complex32f_t* const* samples, uint32_t count, const StreamMeta* meta) { return mSubDevices[moduleIndex]->StreamTx(0, samples, count, meta); } -int LimeSDR_MMX8::StreamTx(uint8_t moduleIndex, const lime::complex16_t* const* samples, uint32_t count, const StreamMeta* meta) +uint32_t LimeSDR_MMX8::StreamTx( + uint8_t moduleIndex, const lime::complex16_t* const* samples, uint32_t count, const StreamMeta* meta) { return mSubDevices[moduleIndex]->StreamTx(0, samples, count, meta); } -int LimeSDR_MMX8::StreamTx(uint8_t moduleIndex, const lime::complex12_t* const* samples, uint32_t count, const StreamMeta* meta) +uint32_t LimeSDR_MMX8::StreamTx( + uint8_t moduleIndex, const lime::complex12_t* const* samples, uint32_t count, const StreamMeta* meta) { return mSubDevices[moduleIndex]->StreamTx(0, samples, count, meta); } diff --git a/src/boards/MMX8/MM_X8.h b/src/boards/MMX8/MM_X8.h index 61c3ec450..da9119be2 100644 --- a/src/boards/MMX8/MM_X8.h +++ b/src/boards/MMX8/MM_X8.h @@ -27,7 +27,7 @@ class LimeSDR_MMX8 : public SDRDevice std::vector>& spiFPGA, std::vector> trxStreams, std::shared_ptr control, - ISPI* adfComms); + std::shared_ptr adfComms); virtual ~LimeSDR_MMX8(); virtual OpStatus Configure(const SDRConfig& config, uint8_t socIndex) override; @@ -120,14 +120,14 @@ class LimeSDR_MMX8 : public SDRDevice virtual void StreamStop(uint8_t moduleIndex) override; virtual void StreamStop(const std::vector moduleIndexes) override; - virtual int StreamRx(uint8_t moduleIndex, lime::complex32f_t* const* samples, uint32_t count, StreamMeta* meta) override; - virtual int StreamRx(uint8_t moduleIndex, lime::complex16_t* const* samples, uint32_t count, StreamMeta* meta) override; - virtual int StreamRx(uint8_t moduleIndex, lime::complex12_t* const* samples, uint32_t count, StreamMeta* meta) override; - virtual int StreamTx( + virtual uint32_t StreamRx(uint8_t moduleIndex, lime::complex32f_t* const* samples, uint32_t count, StreamMeta* meta) override; + virtual uint32_t StreamRx(uint8_t moduleIndex, lime::complex16_t* const* samples, uint32_t count, StreamMeta* meta) override; + virtual uint32_t StreamRx(uint8_t moduleIndex, lime::complex12_t* const* samples, uint32_t count, StreamMeta* meta) override; + virtual uint32_t StreamTx( uint8_t moduleIndex, const lime::complex32f_t* const* samples, uint32_t count, const StreamMeta* meta) override; - virtual int StreamTx( + virtual uint32_t StreamTx( uint8_t moduleIndex, const lime::complex16_t* const* samples, uint32_t count, const StreamMeta* meta) override; - virtual int StreamTx( + virtual uint32_t StreamTx( uint8_t moduleIndex, const lime::complex12_t* const* samples, uint32_t count, const StreamMeta* meta) override; virtual void StreamStatus(uint8_t moduleIndex, SDRDevice::StreamStats* rx, SDRDevice::StreamStats* tx) override; diff --git a/src/boards/MMX8/MM_X8Entry.cpp b/src/boards/MMX8/MM_X8Entry.cpp index 75683e529..5cc95769f 100644 --- a/src/boards/MMX8/MM_X8Entry.cpp +++ b/src/boards/MMX8/MM_X8Entry.cpp @@ -5,7 +5,10 @@ #include "MM_X8Entry.h" #include "LitePCIe.h" #include "MM_X8.h" -#include "PCIeCommon.h" +#include "PCIE_CSR_Pipe.h" +#include "LMS64C_ADF_Over_PCIe_MMX8.h" +#include "LMS64C_FPGA_Over_PCIe_MMX8.h" +#include "LMS64C_LMS7002M_Over_PCIe_MMX8.h" #include #include @@ -83,107 +86,13 @@ std::vector LimeSDR_MMX8Entry::enumerate(const DeviceHandle& hint) return handles; } -/** @brief A class for communicating with MMX8's subdevice's LMS7002M chips. */ -class LMS64C_LMS7002M_Over_PCIe_MMX8 : public lime::IComms -{ - public: - LMS64C_LMS7002M_Over_PCIe_MMX8(std::shared_ptr dataPort, uint32_t subdeviceIndex) - : pipe(dataPort) - , subdeviceIndex(subdeviceIndex) - { - } - virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override { return SPI(0, MOSI, MISO, count); } - virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override - { - return LMS64CProtocol::LMS7002M_SPI(pipe, spiBusAddress, MOSI, MISO, count, subdeviceIndex); - } - virtual OpStatus ResetDevice(int chipSelect) override { return LMS64CProtocol::DeviceReset(pipe, chipSelect, subdeviceIndex); }; - - private: - PCIE_CSR_Pipe pipe; - uint32_t subdeviceIndex; -}; - -/** @brief A class for communicating with MMX8's subdevice's FPGA chips. */ -class LMS64C_FPGA_Over_PCIe_MMX8 : public lime::IComms -{ - public: - LMS64C_FPGA_Over_PCIe_MMX8(std::shared_ptr dataPort, uint32_t subdeviceIndex) - : pipe(dataPort) - , subdeviceIndex(subdeviceIndex) - { - } - OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override - { - return LMS64CProtocol::FPGA_SPI(pipe, MOSI, MISO, count, subdeviceIndex); - } - - OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override - { - return LMS64CProtocol::FPGA_SPI(pipe, MOSI, MISO, count, subdeviceIndex); - } - - virtual OpStatus CustomParameterWrite(const std::vector& parameters) override - { - return LMS64CProtocol::CustomParameterWrite(pipe, parameters, subdeviceIndex); - }; - virtual OpStatus CustomParameterRead(std::vector& parameters) override - { - return LMS64CProtocol::CustomParameterRead(pipe, parameters, subdeviceIndex); - } - virtual OpStatus ProgramWrite( - const char* data, size_t length, int prog_mode, int target, ProgressCallback callback = nullptr) override - { - return LMS64CProtocol::ProgramWrite( - pipe, data, length, prog_mode, static_cast(target), callback, subdeviceIndex); - } - - OpStatus MemoryWrite(uint32_t address, const void* data, uint32_t dataLength) - { - return LMS64CProtocol::MemoryWrite(pipe, address, data, dataLength, subdeviceIndex); - } - - OpStatus MemoryRead(uint32_t address, void* data, uint32_t dataLength) - { - return LMS64CProtocol::MemoryRead(pipe, address, data, dataLength, subdeviceIndex); - } - - private: - PCIE_CSR_Pipe pipe; - uint32_t subdeviceIndex; -}; - -/** @brief A class for communicating with MMX8's subdevice's ADF4002 chips. */ -class LMS64C_ADF_Over_PCIe_MMX8 : public lime::ISPI -{ - public: - LMS64C_ADF_Over_PCIe_MMX8(std::shared_ptr dataPort, uint32_t subdeviceIndex) - : pipe(dataPort) - , subdeviceIndex(subdeviceIndex) - { - } - - OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override - { - return LMS64CProtocol::ADF4002_SPI(pipe, MOSI, count, subdeviceIndex); - } - OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override - { - return LMS64CProtocol::ADF4002_SPI(pipe, MOSI, count, subdeviceIndex); - } - - private: - PCIE_CSR_Pipe pipe; - uint32_t subdeviceIndex; -}; - SDRDevice* LimeSDR_MMX8Entry::make(const DeviceHandle& handle) { auto control = std::make_shared(); std::vector> trxStreams(8); std::vector> controls(8); std::vector> fpga(8); - ISPI* adfComms = new LMS64C_ADF_Over_PCIe_MMX8(control, 0); + auto adfComms = std::make_shared(control, 0); for (size_t i = 0; i < controls.size(); ++i) { controls[i] = std::make_shared(control, i + 1); diff --git a/src/cli/limeTRX.cpp b/src/cli/limeTRX.cpp index 355017dba..071e81ec4 100644 --- a/src/cli/limeTRX.cpp +++ b/src/cli/limeTRX.cpp @@ -506,10 +506,10 @@ int main(int argc, char** argv) if (syncPPS || rxSamplesInPacket || rxPacketsInBatch || txSamplesInPacket || txPacketsInBatch) { stream.extraConfig.waitPPS = syncPPS; - stream.extraConfig.rxSamplesInPacket = rxSamplesInPacket; - stream.extraConfig.txSamplesInPacket = txSamplesInPacket; - stream.extraConfig.rxPacketsInBatch = rxPacketsInBatch; - stream.extraConfig.txMaxPacketsInBatch = txPacketsInBatch; + stream.extraConfig.rx.samplesInPacket = rxSamplesInPacket; + stream.extraConfig.tx.samplesInPacket = txSamplesInPacket; + stream.extraConfig.rx.packetsInBatch = rxPacketsInBatch; + stream.extraConfig.tx.packetsInBatch = txPacketsInBatch; } useComposite = chipIndexes.size() > 1; @@ -600,7 +600,7 @@ int main(int argc, char** argv) SDRDevice::StreamMeta rxMeta; SDRDevice::StreamMeta txMeta; - txMeta.useTimestamp = true; + txMeta.waitForTimestamp = true; txMeta.timestamp = sampleRate / 100; // send tx samples 10ms after start #ifdef USE_GNU_PLOT @@ -638,8 +638,8 @@ int main(int argc, char** argv) const complex16_t* txSamples[16]; for (int i = 0; i < 16; ++i) txSamples[i] = &txData[txSent]; - int samplesSent = useComposite ? composite->StreamTx(txSamples, toSend, &txMeta) - : device->StreamTx(chipIndex, txSamples, toSend, &txMeta); + uint32_t samplesSent = useComposite ? composite->StreamTx(txSamples, toSend, &txMeta) + : device->StreamTx(chipIndex, txSamples, toSend, &txMeta); if (samplesSent > 0) { txSent += samplesSent; @@ -651,16 +651,16 @@ int main(int argc, char** argv) complex16_t* rxSamples[16]; for (int i = 0; i < 16; ++i) rxSamples[i] = rxData[i].data(); - int samplesRead = useComposite ? composite->StreamRx(rxSamples, fftSize, &rxMeta) - : device->StreamRx(chipIndex, rxSamples, fftSize, &rxMeta); - if (samplesRead <= 0) + uint32_t samplesRead = useComposite ? composite->StreamRx(rxSamples, fftSize, &rxMeta) + : device->StreamRx(chipIndex, rxSamples, fftSize, &rxMeta); + if (samplesRead == 0) continue; if (tx && repeater) { txMeta.timestamp = rxMeta.timestamp + samplesRead + repeaterDelay; - txMeta.useTimestamp = true; - txMeta.flush = true; + txMeta.waitForTimestamp = true; + txMeta.flushPartialPacket = true; if (useComposite) composite->StreamTx(rxSamples, samplesRead, &txMeta); else diff --git a/src/comms/PCIe/AvgRmsCounter.cpp b/src/comms/PCIe/AvgRmsCounter.cpp new file mode 100644 index 000000000..dd749f209 --- /dev/null +++ b/src/comms/PCIe/AvgRmsCounter.cpp @@ -0,0 +1,58 @@ +#include "AvgRmsCounter.h" + +#include + +using namespace lime; + +AvgRmsCounter::AvgRmsCounter() + : counter(0) + , avgAccumulator(0) + , rmsAccumulator(0) + , min(1e16) + , max(1e-16){}; + +void AvgRmsCounter::Add(double value) +{ + avgAccumulator += value; + rmsAccumulator += value * value; + ++counter; + + if (value < min) + { + min = value; + } + + if (value > max) + { + max = value; + } +} + +void AvgRmsCounter::GetResult(double& avg, double& rms) +{ + if (counter == 0) + { + return; + } + + avg = avgAccumulator / counter; + rms = std::sqrt(rmsAccumulator / counter); + + avgAccumulator = 0; + rmsAccumulator = 0; + counter = 0; +} + +double AvgRmsCounter::Min() +{ + auto temp = min; + min = 1e16; + return temp; +} + +double AvgRmsCounter::Max() +{ + auto temp = max; + max = 1e-16; + return temp; +} diff --git a/src/comms/PCIe/AvgRmsCounter.h b/src/comms/PCIe/AvgRmsCounter.h new file mode 100644 index 000000000..dbb127ef4 --- /dev/null +++ b/src/comms/PCIe/AvgRmsCounter.h @@ -0,0 +1,49 @@ +#ifndef LIME_AVGRMSCOUNTER_H +#define LIME_AVGRMSCOUNTER_H + +#include + +namespace lime { + +/** @brief A helper class for calculating the Average and the Root Mean Square */ +class AvgRmsCounter +{ + public: + AvgRmsCounter(); + + /** + @brief Adds the given value to the accumulators. + @param value The value to add. + */ + void Add(double value); + + /** + @brief Gets the results of the counters and resets them. + @param[out] avg The average of all the submitted numbers. + @param[out] rms The Root Mean Square Average of all the submitted numbers. + */ + void GetResult(double& avg, double& rms); + + /** + @brief Gets the current minimum value and resets it. + @return The minimum value recorded. + */ + double Min(); + + /** + @brief Gets the current maximum value and resets it. + @return The maximum value recorded. + */ + double Max(); + + private: + int32_t counter; + double avgAccumulator; + double rmsAccumulator; + double min; + double max; +}; + +} // namespace lime + +#endif // LIME_AVGRMSCOUNTER_H diff --git a/src/comms/PCIe/CMakeLists.txt b/src/comms/PCIe/CMakeLists.txt index 9f2cfd4ab..535a4cce2 100644 --- a/src/comms/PCIe/CMakeLists.txt +++ b/src/comms/PCIe/CMakeLists.txt @@ -4,9 +4,12 @@ set(THIS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/comms/PCIe) set(COMMS_LITE_PCIE_SOURCES + ${THIS_SOURCE_DIR}/AvgRmsCounter.cpp + ${THIS_SOURCE_DIR}/LMS64C_FPGA_Over_PCIe.cpp + ${THIS_SOURCE_DIR}/LMS64C_LMS7002M_Over_PCIe.cpp ${THIS_SOURCE_DIR}/LitePCIe.cpp + ${THIS_SOURCE_DIR}/PCIE_CSR_Pipe.cpp ${THIS_SOURCE_DIR}/TRXLooper_PCIE.cpp - ${THIS_SOURCE_DIR}/PCIeCommon.cpp ) ######################################################################## diff --git a/src/comms/PCIe/PCIeCommon.cpp b/src/comms/PCIe/LMS64C_FPGA_Over_PCIe.cpp similarity index 63% rename from src/comms/PCIe/PCIeCommon.cpp rename to src/comms/PCIe/LMS64C_FPGA_Over_PCIe.cpp index 1a9d67d2c..db9a62332 100644 --- a/src/comms/PCIe/PCIeCommon.cpp +++ b/src/comms/PCIe/LMS64C_FPGA_Over_PCIe.cpp @@ -1,35 +1,7 @@ -#include "PCIeCommon.h" -using namespace lime; - -PCIE_CSR_Pipe::PCIE_CSR_Pipe(std::shared_ptr port) - : port(port) -{ -} - -int PCIE_CSR_Pipe::Write(const uint8_t* data, size_t length, int timeout_ms) -{ - return port->WriteControl(data, length, timeout_ms); -} -int PCIE_CSR_Pipe::Read(uint8_t* data, size_t length, int timeout_ms) -{ - return port->ReadControl(data, length, timeout_ms); -} - -LMS64C_LMS7002M_Over_PCIe::LMS64C_LMS7002M_Over_PCIe(std::shared_ptr dataPort) - : pipe(dataPort) -{ -} - -OpStatus LMS64C_LMS7002M_Over_PCIe::SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) -{ - return SPI(0, MOSI, MISO, count); -} +#include "LMS64C_FPGA_Over_PCIe.h" -OpStatus LMS64C_LMS7002M_Over_PCIe::SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) -{ - return LMS64CProtocol::LMS7002M_SPI(pipe, spiBusAddress, MOSI, MISO, count); -} +using namespace lime; LMS64C_FPGA_Over_PCIe::LMS64C_FPGA_Over_PCIe(std::shared_ptr dataPort) : pipe(dataPort) diff --git a/src/comms/PCIe/LMS64C_FPGA_Over_PCIe.h b/src/comms/PCIe/LMS64C_FPGA_Over_PCIe.h new file mode 100644 index 000000000..89be7f883 --- /dev/null +++ b/src/comms/PCIe/LMS64C_FPGA_Over_PCIe.h @@ -0,0 +1,41 @@ +#ifndef LIME_LMS64C_FPGA_OVER_PCIE_H +#define LIME_LMS64C_FPGA_OVER_PCIE_H + +#include "limesuite/IComms.h" +#include "LitePCIe.h" +#include "PCIE_CSR_Pipe.h" + +#include +#include + +namespace lime { + +/** @brief A class for communicating with a device's FPGA over a PCIe interface. */ +class LMS64C_FPGA_Over_PCIe : public IComms +{ + public: + /** + @brief Constructs a new LMS64C_FPGA_Over_PCIe object. + @param dataPort The PCIe data bus to use. + */ + LMS64C_FPGA_Over_PCIe(std::shared_ptr dataPort); + + virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + + virtual OpStatus CustomParameterWrite(const std::vector& parameters) override; + virtual OpStatus CustomParameterRead(std::vector& parameters) override; + + virtual OpStatus ProgramWrite( + const char* data, size_t length, int prog_mode, int target, ProgressCallback callback = nullptr) override; + + virtual OpStatus MemoryWrite(uint32_t address, const void* data, uint32_t dataLength) override; + virtual OpStatus MemoryRead(uint32_t address, void* data, uint32_t dataLength) override; + + private: + PCIE_CSR_Pipe pipe; +}; + +} // namespace lime + +#endif // LIME_LMS64C_FPGA_OVER_PCIE_H diff --git a/src/comms/PCIe/LMS64C_LMS7002M_Over_PCIe.cpp b/src/comms/PCIe/LMS64C_LMS7002M_Over_PCIe.cpp new file mode 100644 index 000000000..f8700b2f4 --- /dev/null +++ b/src/comms/PCIe/LMS64C_LMS7002M_Over_PCIe.cpp @@ -0,0 +1,18 @@ +#include "LMS64C_LMS7002M_Over_PCIe.h" + +using namespace lime; + +LMS64C_LMS7002M_Over_PCIe::LMS64C_LMS7002M_Over_PCIe(std::shared_ptr dataPort) + : pipe(dataPort) +{ +} + +OpStatus LMS64C_LMS7002M_Over_PCIe::SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return SPI(0, MOSI, MISO, count); +} + +OpStatus LMS64C_LMS7002M_Over_PCIe::SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return LMS64CProtocol::LMS7002M_SPI(pipe, spiBusAddress, MOSI, MISO, count); +} diff --git a/src/comms/PCIe/LMS64C_LMS7002M_Over_PCIe.h b/src/comms/PCIe/LMS64C_LMS7002M_Over_PCIe.h new file mode 100644 index 000000000..a1179f5a1 --- /dev/null +++ b/src/comms/PCIe/LMS64C_LMS7002M_Over_PCIe.h @@ -0,0 +1,31 @@ +#ifndef LIME_LMS64C_LMS7002M_OVER_PCIE_H +#define LIME_LMS64C_LMS7002M_OVER_PCIE_H + +#include "limesuite/IComms.h" +#include "LitePCIe.h" +#include "PCIE_CSR_Pipe.h" + +#include +#include + +namespace lime { + +/** @brief A class for communicating with a device's LMS7002M chip over a PCIe interface. */ +class LMS64C_LMS7002M_Over_PCIe : public IComms +{ + public: + /** + @brief Constructs a new LMS64C_LMS7002M_Over_PCIe object + @param dataPort The PCIe data connection to use. + */ + LMS64C_LMS7002M_Over_PCIe(std::shared_ptr dataPort); + virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; + + private: + PCIE_CSR_Pipe pipe; +}; + +} // namespace lime + +#endif // LIME_LMS64C_LMS7002M_OVER_PCIE_H diff --git a/src/comms/PCIe/PCIE_CSR_Pipe.cpp b/src/comms/PCIe/PCIE_CSR_Pipe.cpp new file mode 100644 index 000000000..d033e67ce --- /dev/null +++ b/src/comms/PCIe/PCIE_CSR_Pipe.cpp @@ -0,0 +1,17 @@ +#include "PCIE_CSR_Pipe.h" + +using namespace lime; + +PCIE_CSR_Pipe::PCIE_CSR_Pipe(std::shared_ptr port) + : port(port) +{ +} + +int PCIE_CSR_Pipe::Write(const uint8_t* data, size_t length, int timeout_ms) +{ + return port->WriteControl(data, length, timeout_ms); +} +int PCIE_CSR_Pipe::Read(uint8_t* data, size_t length, int timeout_ms) +{ + return port->ReadControl(data, length, timeout_ms); +} diff --git a/src/comms/PCIe/PCIE_CSR_Pipe.h b/src/comms/PCIe/PCIE_CSR_Pipe.h new file mode 100644 index 000000000..639986635 --- /dev/null +++ b/src/comms/PCIe/PCIE_CSR_Pipe.h @@ -0,0 +1,29 @@ +#ifndef PCIE_CSR_PIPE_H +#define PCIE_CSR_PIPE_H + +#include "limesuite/IComms.h" +#include "LMS64CProtocol.h" +#include "LitePCIe.h" + +namespace lime { + +/** @brief An abstract class for interfacing with Control/Status registers (CSR) of a PCIe device. */ +class PCIE_CSR_Pipe : public ISerialPort +{ + public: + /** + @brief Constructs a new PCIE_CSR_Pipe object. + + @param port The LitePCIe port to use with this pipe. + */ + explicit PCIE_CSR_Pipe(std::shared_ptr port); + virtual int Write(const uint8_t* data, std::size_t length, int timeout_ms) override; + virtual int Read(uint8_t* data, std::size_t length, int timeout_ms) override; + + protected: + std::shared_ptr port; +}; + +} // namespace lime + +#endif // PCIE_CSR_PIPE_H diff --git a/src/comms/PCIe/PCIeCommon.h b/src/comms/PCIe/PCIeCommon.h deleted file mode 100644 index 619c8cc24..000000000 --- a/src/comms/PCIe/PCIeCommon.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef PCIECOMMON_H -#define PCIECOMMON_H - -#include "limesuite/IComms.h" -#include "LMS64CProtocol.h" -#include "LitePCIe.h" - -using namespace lime; - -/** @brief An abstract class for interfacing with Control/Status registers (CSR) of a PCIe device. */ -class PCIE_CSR_Pipe : public ISerialPort -{ - public: - explicit PCIE_CSR_Pipe(std::shared_ptr port); - virtual int Write(const uint8_t* data, size_t length, int timeout_ms) override; - virtual int Read(uint8_t* data, size_t length, int timeout_ms) override; - - protected: - std::shared_ptr port; -}; - -/** @brief A class for communicating with a device's LMS7002M chip over a PCIe interface. */ -class LMS64C_LMS7002M_Over_PCIe : public lime::IComms -{ - public: - LMS64C_LMS7002M_Over_PCIe(std::shared_ptr dataPort); - virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; - virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; - - private: - PCIE_CSR_Pipe pipe; -}; - -/** @brief A class for communicating with a device's FPGA over a PCIe interface. */ -class LMS64C_FPGA_Over_PCIe : public lime::IComms -{ - public: - LMS64C_FPGA_Over_PCIe(std::shared_ptr dataPort); - - virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; - virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; - - virtual OpStatus CustomParameterWrite(const std::vector& parameters) override; - virtual OpStatus CustomParameterRead(std::vector& parameters) override; - - virtual OpStatus ProgramWrite( - const char* data, size_t length, int prog_mode, int target, ProgressCallback callback = nullptr) override; - - virtual OpStatus MemoryWrite(uint32_t address, const void* data, uint32_t dataLength) override; - virtual OpStatus MemoryRead(uint32_t address, void* data, uint32_t dataLength) override; - - private: - PCIE_CSR_Pipe pipe; -}; - -#endif // PCIECOMMON_H \ No newline at end of file diff --git a/src/comms/PCIe/TRXLooper_PCIE.cpp b/src/comms/PCIe/TRXLooper_PCIE.cpp index 93d93559e..1e158fb12 100644 --- a/src/comms/PCIe/TRXLooper_PCIE.cpp +++ b/src/comms/PCIe/TRXLooper_PCIE.cpp @@ -1,23 +1,28 @@ -#include -#include "FPGA_common.h" -#include "limesuite/LMS7002M.h" -#include -#include "Logger.h" -#include -#include -#include +#include "TRXLooper_PCIE.h" + +#include +#include +#include +#include +#include +#include +#include #include -#include -#include -#include #include +#include -#include "TRXLooper_PCIE.h" -#include "limesuite/SDRDevice.h" -#include "LitePCIe.h" +#include "AvgRmsCounter.h" +#include "BufferInterleaving.h" #include "DataPacket.h" -#include "SamplesPacket.h" +#include "FPGA_common.h" +#include "LitePCIe.h" #include "limesuite/commonTypes.h" +#include "limesuite/complex.h" +#include "limesuite/LMS7002M.h" +#include "limesuite/SDRDevice.h" +#include "Logger.h" +#include "MemoryPool.h" +#include "TxBufferManager.h" static bool showStats = false; static const int statsPeriod_ms = 1000; // at 122.88 MHz MIMO, fpga tx pkt counter overflows every 272ms @@ -27,67 +32,20 @@ using namespace std::chrono; namespace lime { -/** @brief A helper class for calculating the Average and the Root Mean Square */ -class AvgRmsCounter +static constexpr int64_t ts_to_us(int64_t fs, int64_t ts) { - public: - AvgRmsCounter() - : counter(0) - , avgAccumulator(0) - , rmsAccumulator(0) - , min(1e16) - , max(1e-16){}; - - void Add(double value) - { - avgAccumulator += value; - rmsAccumulator += value * value; - ++counter; - if (value < min) - min = value; - if (value > max) - max = value; - } - void GetResult(double& avg, double& rms) - { - if (counter == 0) - return; - avg = avgAccumulator / static_cast(counter); - rms = sqrt(rmsAccumulator / static_cast(counter)); - avgAccumulator = 0; - rmsAccumulator = 0; - counter = 0; - } - - double Min() - { - auto temp = min; - min = 1e16; - return temp; - } - double Max() - { - auto temp = max; - max = 1e-16; - return temp; - } + int64_t n = (ts / fs); + int64_t r = (ts % fs); - private: - int32_t counter; - double avgAccumulator; - double rmsAccumulator; - double min; - double max; -}; - -static inline int64_t ts_to_us(int64_t fs, int64_t ts) -{ - int64_t n, r; - n = (ts / fs); - r = (ts % fs); - return n * 1000000 + (r * 1000000 / fs); + return n * 1000000 + ((r * 1000000) / fs); } +/// @brief Constructs a new TRXLooper_PCIE object. +/// @param rxPort The PCIe stream receive port to use. +/// @param txPort The PCIe stream transmit port to use. +/// @param f The FPGA to use in this stream. +/// @param chip The LMS7002M chip to use in this stream. +/// @param moduleIndex The ID of the chip to use. TRXLooper_PCIE::TRXLooper_PCIE( std::shared_ptr rxPort, std::shared_ptr txPort, FPGA* f, LMS7002M* chip, uint8_t moduleIndex) : TRXLooper(f, chip, moduleIndex) @@ -177,9 +135,9 @@ int TRXLooper_PCIE::TxSetup() int samplesInPkt = 256; //(mConfig.linkFormat == SDRDevice::StreamConfig::DataFormat::I16 ? 1020 : 1360) / chCount; const int packetSize = sizeof(StreamHeader) + samplesInPkt * sampleSize * chCount; - if (mConfig.extraConfig.txSamplesInPacket != 0) + if (mConfig.extraConfig.tx.samplesInPacket != 0) { - samplesInPkt = mConfig.extraConfig.txSamplesInPacket; + samplesInPkt = mConfig.extraConfig.tx.samplesInPacket; lime::debug("Tx samples overide %i", samplesInPkt); } @@ -187,9 +145,9 @@ int TRXLooper_PCIE::TxSetup() LitePCIe::DMAInfo dma = mTxArgs.port->GetDMAInfo(); - if (mConfig.extraConfig.txMaxPacketsInBatch != 0) + if (mConfig.extraConfig.tx.packetsInBatch != 0) { - mTx.packetsToBatch = mConfig.extraConfig.txMaxPacketsInBatch; + mTx.packetsToBatch = mConfig.extraConfig.tx.packetsInBatch; } mTx.packetsToBatch = std::clamp(mTx.packetsToBatch, 1, dma.bufferSize / packetSize); @@ -224,164 +182,6 @@ int TRXLooper_PCIE::TxSetup() return 0; } -/** - @brief A class for managing the transmission buffer for the PCIe transfer. - @tparam T The samples packet input type. - */ -template class TxBufferManager -{ - public: - TxBufferManager(bool mimo, - bool compressed, - uint32_t maxSamplesInPkt, - uint32_t maxPacketsInBatch, - SDRDevice::StreamConfig::DataFormat inputFormat) - : header(nullptr) - , payloadPtr(nullptr) - , mData(nullptr) - , bytesUsed(0) - , mCapacity(0) - , maxPacketsInBatch(maxPacketsInBatch) - , maxSamplesInPkt(maxSamplesInPkt) - , packetsCreated(0) - , payloadSize(0) - { - bytesForFrame = (compressed ? 3 : 4) * (mimo ? 2 : 1); - conversion.srcFormat = inputFormat; //SDRDevice::StreamConfig::DataFormat::F32; - conversion.destFormat = compressed ? SDRDevice::StreamConfig::DataFormat::I12 : SDRDevice::StreamConfig::DataFormat::I16; - conversion.channelCount = mimo ? 2 : 1; - maxPayloadSize = std::min(4080u, bytesForFrame * maxSamplesInPkt); - } - - void Reset(uint8_t* memPtr, uint32_t capacity) - { - packetsCreated = 0; - bytesUsed = 0; - mData = memPtr; - mCapacity = capacity; - memset(mData, 0, capacity); - header = reinterpret_cast(mData); - header->Clear(); - payloadSize = 0; - payloadPtr = reinterpret_cast(header) + sizeof(StreamHeader); - } - - inline bool hasSpace() const - { - const bool packetNotFull = payloadSize < maxPayloadSize; - const bool spaceAvailable = mCapacity - bytesUsed > sizeof(StreamHeader); - return packetNotFull && spaceAvailable; - } - - inline bool consume(T* src) - { - bool sendBuffer = false; - while (!src->empty()) - { - if (payloadSize >= maxPayloadSize || payloadSize == maxSamplesInPkt * bytesForFrame) - { - header = reinterpret_cast(mData + bytesUsed); - header->Clear(); - payloadPtr = reinterpret_cast(header) + sizeof(StreamHeader); - payloadSize = 0; - } - - header->ignoreTimestamp(!src->useTimestamp); - if (payloadSize == 0) - { - ++packetsCreated; - header->counter = src->timestamp; - bytesUsed += sizeof(StreamHeader); - } - const int freeSpace = std::min(maxPayloadSize - payloadSize, mCapacity - bytesUsed - 16); - uint32_t transferCount = std::min(freeSpace / bytesForFrame, src->size()); - transferCount = std::min(transferCount, maxSamplesInPkt); - if (transferCount > 0) - { - int samplesDataSize = Interleave(payloadPtr, src->front(), transferCount, conversion); - src->pop(transferCount); - payloadPtr = payloadPtr + samplesDataSize; - payloadSize += samplesDataSize; - bytesUsed += samplesDataSize; - header->SetPayloadSize(payloadSize); - assert(payloadSize > 0); - assert(payloadSize <= maxPayloadSize); - } - else - sendBuffer = true; - - if (packetsCreated >= maxPacketsInBatch && !hasSpace()) - sendBuffer = true; - - if (bytesUsed >= mCapacity - sizeof(StreamHeader)) - sendBuffer = true; // not enough space for more packets, need to flush - if (reinterpret_cast(payloadPtr) & 0xF) - sendBuffer = true; // next packets payload memory is not suitably aligned for vectorized filling - - if (sendBuffer) - { - const int busWidthBytes = 16; - int extraBytes = bytesUsed % busWidthBytes; - if (extraBytes != 0) - { - //lime::debug("Patch buffer, bytes %i, extra: %i, last payload: %i", bytesUsed, extraBytes, payloadSize); - // patch last packet so that whole buffer size would be multiple of bus width - int padding = busWidthBytes - extraBytes; - memset(payloadPtr, 0, padding); - payloadSize += padding; - bytesUsed += padding; - header->SetPayloadSize(payloadSize); - //lime::debug("Patch buffer, bytes %i, last payload: %i", bytesUsed, payloadSize); - } - break; - } - } - if (!hasSpace()) - return true; - return src->flush || sendBuffer; - } - - inline int size() const { return bytesUsed; }; - inline uint8_t* data() const { return mData; }; - inline int packetCount() const { return packetsCreated; }; - - private: - DataConversion conversion; - StreamHeader* header; - uint8_t* payloadPtr; - uint8_t* mData; - uint32_t bytesUsed; - uint32_t mCapacity; - uint32_t maxPacketsInBatch; - uint32_t maxSamplesInPkt; - uint32_t maxPayloadSize; - uint16_t packetsCreated; - uint16_t payloadSize; - uint8_t bytesForFrame; -}; - -void FPGATxState(FPGA* fpga) -{ - uint32_t words[4]; - for (int i = 0; i < 5; ++i) - { - uint64_t pendingTxTS = 0; - const uint32_t addrs[4] = { 0x61u + i * 4, 0x62u + i * 4, 0x63u + i * 4, 0x64u + i * 4 }; - fpga->ReadRegisters(addrs, words, 4); - pendingTxTS |= words[0]; - pendingTxTS |= words[1] << 16; - pendingTxTS |= static_cast(words[2]) << 32; - pendingTxTS |= static_cast(words[3]) << 48; - if (i < 4) - lime::debug("Buf%i: %08lX", i, pendingTxTS); - else - lime::debug("Rx: %08lX", pendingTxTS); - } - - uint16_t bufs = fpga->ReadRegister(0x0075); - lime::debug("currentIndex: %i, ready: %0X", bufs & 0xF, (bufs >> 4) & 0xF); -} - void TRXLooper_PCIE::TransmitPacketsLoop() { const bool mimo = std::max(mConfig.channels.at(lime::TRXDir::Tx).size(), mConfig.channels.at(lime::TRXDir::Rx).size()) > 1; @@ -399,7 +199,7 @@ void TRXLooper_PCIE::TransmitPacketsLoop() int64_t totalBytesSent = 0; //for data rate calculation int packetsSent = 0; int totalPacketSent = 0; - uint32_t maxFIFOlevel = 0; + std::size_t maxFIFOlevel = 0; int64_t lastTS = 0; struct PendingWrite { @@ -627,7 +427,7 @@ void TRXLooper_PCIE::TransmitPacketsLoop() snprintf(msg, sizeof(msg) - 1, "%s Tx: %3.3f MB/s | TS:%li pkt:%li o:%i shw:%u/%u(%+i) u:%i(%+i) l:%i(%+i) tsAdvance:%+.0f/%+.0f/%+.0f%s, " - "f:%i", + "f:%li", mRxArgs.port->GetPathName().c_str(), dataRate / 1000000.0, lastTS, @@ -699,9 +499,9 @@ int TRXLooper_PCIE::RxSetup() const int maxSamplesInPkt = 1024 / chCount; int requestSamplesInPkt = 256; //maxSamplesInPkt; - if (mConfig.extraConfig.rxSamplesInPacket != 0) + if (mConfig.extraConfig.rx.samplesInPacket != 0) { - requestSamplesInPkt = mConfig.extraConfig.rxSamplesInPacket; + requestSamplesInPkt = mConfig.extraConfig.rx.samplesInPacket; } int samplesInPkt = std::clamp(requestSamplesInPkt, 64, maxSamplesInPkt); @@ -723,9 +523,9 @@ int TRXLooper_PCIE::RxSetup() LitePCIe::DMAInfo dma = mRxArgs.port->GetDMAInfo(); - if (mConfig.extraConfig.rxPacketsInBatch != 0) + if (mConfig.extraConfig.rx.packetsInBatch != 0) { - mRx.packetsToBatch = mConfig.extraConfig.rxPacketsInBatch; + mRx.packetsToBatch = mConfig.extraConfig.rx.packetsInBatch; } mRx.packetsToBatch = std::clamp(mRx.packetsToBatch, 1, dma.bufferSize / packetSize); @@ -796,7 +596,7 @@ void TRXLooper_PCIE::ReceivePacketsLoop() SDRDevice::StreamStats& stats = mRx.stats; auto fifo = mRx.fifo; - const int outputSampleSize = + const uint8_t outputSampleSize = mConfig.format == SDRDevice::StreamConfig::DataFormat::F32 ? sizeof(complex32f_t) : sizeof(complex16_t); const int32_t outputPktSize = SamplesPacketType::headerSize + mRxArgs.packetsToBatch * samplesInPkt * outputSampleSize; @@ -855,7 +655,7 @@ void TRXLooper_PCIE::ReceivePacketsLoop() char msg[512]; snprintf(msg, sizeof(msg) - 1, - "%s Rx: %3.3f MB/s | TS:%li pkt:%li o:%i(%+i) l:%i(%+i) dma:%u/%u(%u) swFIFO:%i", + "%s Rx: %3.3f MB/s | TS:%li pkt:%li o:%i(%+i) l:%i(%+i) dma:%u/%u(%u) swFIFO:%li", mRxArgs.port->GetPathName().c_str(), stats.dataRate_Bps / 1e6, stats.timestamp, @@ -992,6 +792,9 @@ void TRXLooper_PCIE::RxTeardown() mRxArgs.port->RxDMAEnable(false, mRxArgs.bufferSize, 1); } +/// @copydoc SDRDevice::UploadTxWaveform() +/// @param fpga The FPGA device to use. +/// @param port The PCIe communications port to use. OpStatus TRXLooper_PCIE::UploadTxWaveform(FPGA* fpga, std::shared_ptr port, const lime::SDRDevice::StreamConfig& config, diff --git a/src/comms/PCIe/TRXLooper_PCIE.h b/src/comms/PCIe/TRXLooper_PCIE.h index e9c82f3a0..880079dc7 100644 --- a/src/comms/PCIe/TRXLooper_PCIE.h +++ b/src/comms/PCIe/TRXLooper_PCIE.h @@ -18,8 +18,8 @@ class TRXLooper_PCIE : public TRXLooper TRXLooper_PCIE( std::shared_ptr rxPort, std::shared_ptr txPort, FPGA* f, LMS7002M* chip, uint8_t moduleIndex); virtual ~TRXLooper_PCIE(); - virtual OpStatus Setup(const SDRDevice::StreamConfig& config); - virtual void Start(); + virtual OpStatus Setup(const SDRDevice::StreamConfig& config) override; + virtual void Start() override; static OpStatus UploadTxWaveform(FPGA* fpga, std::shared_ptr port, @@ -28,8 +28,6 @@ class TRXLooper_PCIE : public TRXLooper const void** samples, uint32_t count); - typedef SamplesPacket<2> SamplesPacketType; - /** @brief The transfer arguments for the PCIe transfer. */ struct TransferArgs { std::shared_ptr port; diff --git a/src/comms/PCIe/TxBufferManager.h b/src/comms/PCIe/TxBufferManager.h new file mode 100644 index 000000000..92d59a5b4 --- /dev/null +++ b/src/comms/PCIe/TxBufferManager.h @@ -0,0 +1,173 @@ +#ifndef LIME_TXBUFFERMANAGER_H +#define LIME_TXBUFFERMANAGER_H + +#include +#include + +#include "BufferInterleaving.h" +#include "limesuite/SDRDevice.h" +#include "protocols/DataPacket.h" + +namespace lime { + +/** + @brief A class for managing the transmission buffer for the PCIe transfer. + @tparam T The samples packet input type. + */ +template class TxBufferManager +{ + public: + /// @brief Constructs a new TxBufferManager object. + /// @param mimo Whether the stream is a Multiple In Multiple Out (MIMO) stream. + /// @param compressed Whether the stream is in 12-bit or in 16-bit format (true for 12-bit). + /// @param maxSamplesInPkt The maximum amount of samples allowed in a single packet. + /// @param maxPacketsInBatch The maximum amount of packets allowed in a single transfer batch. + /// @param inputFormat The input format of the samples for the device. + TxBufferManager(bool mimo, + bool compressed, + uint32_t maxSamplesInPkt, + uint32_t maxPacketsInBatch, + SDRDevice::StreamConfig::DataFormat inputFormat) + : header(nullptr) + , payloadPtr(nullptr) + , mData(nullptr) + , bytesUsed(0) + , mCapacity(0) + , maxPacketsInBatch(maxPacketsInBatch) + , maxSamplesInPkt(maxSamplesInPkt) + , packetsCreated(0) + , payloadSize(0) + { + bytesForFrame = (compressed ? 3 : 4) * (mimo ? 2 : 1); + conversion.srcFormat = inputFormat; //SDRDevice::StreamConfig::DataFormat::F32; + conversion.destFormat = compressed ? SDRDevice::StreamConfig::DataFormat::I12 : SDRDevice::StreamConfig::DataFormat::I16; + conversion.channelCount = mimo ? 2 : 1; + maxPayloadSize = std::min(4080u, bytesForFrame * maxSamplesInPkt); + } + + /// @brief Resets the buffer to point to an empty buffer. + /// @param memPtr The pointer of memory to set. + /// @param capacity The total capacity of the buffer. + void Reset(uint8_t* memPtr, uint32_t capacity) + { + packetsCreated = 0; + bytesUsed = 0; + mData = memPtr; + mCapacity = capacity; + std::memset(mData, 0, capacity); + header = reinterpret_cast(mData); + header->Clear(); + payloadSize = 0; + payloadPtr = reinterpret_cast(header) + sizeof(StreamHeader); + } + + /// @brief Checks if the buffer still has space in it. + /// @return Whether there still is space or not. + constexpr bool hasSpace() const + { + const bool packetNotFull = payloadSize < maxPayloadSize; + const bool spaceAvailable = mCapacity - bytesUsed > sizeof(StreamHeader); + return packetNotFull && spaceAvailable; + } + + /// @brief Adds samples from the given source packet into the transfer. + /// @param src The source packet to add to the transfer. + /// @return The sendability of the transfer (true to send it now). + bool consume(T* src) + { + bool sendBuffer = false; + while (!src->empty()) + { + if (payloadSize >= maxPayloadSize || payloadSize == maxSamplesInPkt * bytesForFrame) + { + header = reinterpret_cast(mData + bytesUsed); + header->Clear(); + payloadPtr = reinterpret_cast(header) + sizeof(StreamHeader); + payloadSize = 0; + } + + header->ignoreTimestamp(!src->useTimestamp); + if (payloadSize == 0) + { + ++packetsCreated; + header->counter = src->timestamp; + bytesUsed += sizeof(StreamHeader); + } + const uint32_t freeSpace = std::min(maxPayloadSize - payloadSize, mCapacity - bytesUsed - 16); + uint32_t transferCount = std::min(freeSpace / bytesForFrame, src->size()); + transferCount = std::min(transferCount, maxSamplesInPkt); + if (transferCount > 0) + { + int samplesDataSize = Interleave(payloadPtr, src->front(), transferCount, conversion); + src->pop(transferCount); + payloadPtr = payloadPtr + samplesDataSize; + payloadSize += samplesDataSize; + bytesUsed += samplesDataSize; + header->SetPayloadSize(payloadSize); + assert(payloadSize > 0); + assert(payloadSize <= maxPayloadSize); + } + else + sendBuffer = true; + + if (packetsCreated >= maxPacketsInBatch && !hasSpace()) + sendBuffer = true; + + if (bytesUsed >= mCapacity - sizeof(StreamHeader)) + sendBuffer = true; // not enough space for more packets, need to flush + if (reinterpret_cast(payloadPtr) & 0xF) + sendBuffer = true; // next packets payload memory is not suitably aligned for vectorized filling + + if (sendBuffer) + { + const int busWidthBytes = 16; + int extraBytes = bytesUsed % busWidthBytes; + if (extraBytes != 0) + { + //printf("Patch buffer, bytes %i, extra: %i, last payload: %i\n", bytesUsed, extraBytes, payloadSize); + // patch last packet so that whole buffer size would be multiple of bus width + int padding = busWidthBytes - extraBytes; + std::memset(payloadPtr, 0, padding); + payloadSize += padding; + bytesUsed += padding; + header->SetPayloadSize(payloadSize); + //printf("Patch buffer, bytes %i, last payload: %i\n", bytesUsed, payloadSize); + } + break; + } + } + if (!hasSpace()) + return true; + return src->flush || sendBuffer; + } + + /// @brief Gets the current size of the transfer. + /// @return The amount of bytes this transfer is currently using. + constexpr uint32_t size() const { return bytesUsed; }; + + /// @brief Gets the pointer to the buffer of the transfer. + /// @return The pointer to the buffer of the transfer. + constexpr uint8_t* data() const { return mData; }; + + /// @brief Gets the amount of packets in this transfer. + /// @return The amount of packets in this transfer. + constexpr uint16_t packetCount() const { return packetsCreated; }; + + private: + DataConversion conversion; + StreamHeader* header; + uint8_t* payloadPtr; + uint8_t* mData; + uint32_t bytesUsed; + uint32_t mCapacity; + uint32_t maxPacketsInBatch; + uint32_t maxSamplesInPkt; + uint32_t maxPayloadSize; + uint16_t packetsCreated; + uint32_t payloadSize; + uint8_t bytesForFrame; +}; + +} // namespace lime + +#endif // LIME_TXBUFFERMANAGER_H diff --git a/src/comms/USB/FT601/FT601.cpp b/src/comms/USB/FT601/FT601.cpp index 9a4c5fc9f..753272322 100644 --- a/src/comms/USB/FT601/FT601.cpp +++ b/src/comms/USB/FT601/FT601.cpp @@ -170,14 +170,14 @@ int FT601::BeginDataXfer(uint8_t* buffer, uint32_t length, uint8_t endPointAddr) if (ftStatus != FT_IO_PENDING) { lime::error("ERROR BEGIN DATA TRANSFER %d", ftStatus); - context->used = false; + context->isTransferUsed = false; return -1; } return index; } -bool FT601::WaitForXfer(int contextHandle, uint32_t timeout_ms) +bool FT601::WaitForXfer(int contextHandle, int32_t timeout_ms) { if (contextHandle < 0) { @@ -186,7 +186,7 @@ bool FT601::WaitForXfer(int contextHandle, uint32_t timeout_ms) USBTransferContext_FT601* context = &dynamic_cast(contexts)[contextHandle]; - if (!context->used) + if (!context->isTransferUsed) { return true; //there is nothing to wait for (signal wait finished) } @@ -209,7 +209,7 @@ int FT601::FinishDataXfer(uint8_t* buffer, uint32_t length, int contextHandle) } USBTransferContext_FT601* context = &dynamic_cast(contexts)[contextHandle]; - if (!context->used) + if (!context->isTransferUsed) { return 0; } @@ -229,7 +229,7 @@ int FT601::FinishDataXfer(uint8_t* buffer, uint32_t length, int contextHandle) } FT_ReleaseOverlapped(mFTHandle, context->inOvLap); - context->used = false; + context->isTransferUsed = false; return length; } @@ -241,10 +241,10 @@ void FT601::AbortEndpointXfers(uint8_t endPointAddr) { USBTransferContext_FT601* context = &dynamic_cast(contexts)[i]; - if (context->used && context->endPointAddr == endPointAddr) + if (context->isTransferUsed && context->endPointAddr == endPointAddr) { FT_ReleaseOverlapped(mFTHandle, context->inOvLap); - context->used = false; + context->isTransferUsed = false; } } @@ -289,7 +289,7 @@ int FT601::GetUSBContextIndex() // Find not used context for (i = 0; i < USB_MAX_CONTEXTS; i++) { - if (!FT601contexts[i].used) + if (!FT601contexts[i].isTransferUsed) { contextFound = true; break; @@ -302,7 +302,7 @@ int FT601::GetUSBContextIndex() return -1; } - FT601contexts[i].used = true; + FT601contexts[i].isTransferUsed = true; return i; } diff --git a/src/comms/USB/FT601/FT601.h b/src/comms/USB/FT601/FT601.h index 25187be21..32b50fc07 100644 --- a/src/comms/USB/FT601/FT601.h +++ b/src/comms/USB/FT601/FT601.h @@ -14,6 +14,10 @@ namespace lime { class FT601 : public USBGeneric { public: + /** + @brief Constructs the class for communicating with the FT601 controller. + @param usbContext The USB context to use for the communication. + */ FT601(void* usbContext = nullptr); virtual ~FT601(); @@ -35,12 +39,15 @@ class FT601 : public USBGeneric #ifndef __unix__ virtual int BeginDataXfer(uint8_t* buffer, uint32_t length, uint8_t endPointAddr) override; - virtual bool WaitForXfer(int contextHandle, uint32_t timeout_ms) override; + virtual bool WaitForXfer(int contextHandle, int32_t timeout_ms) override; virtual int FinishDataXfer(uint8_t* buffer, uint32_t length, int contextHandle) override; virtual void AbortEndpointXfers(uint8_t endPointAddr) override; #endif - virtual int GetUSBContextIndex() override; + /** + @brief Resets the stream buffers of the device. + @return Status of the operation (0 - success; -1 - failure). + */ int ResetStreamBuffers(); protected: @@ -53,6 +60,8 @@ class FT601 : public USBGeneric int FT_FlushPipe(unsigned char ep); uint32_t mUsbCounter; #endif + + virtual int GetUSBContextIndex() override; }; } // namespace lime diff --git a/src/comms/USB/FT601/USBTransferContext_FT601.cpp b/src/comms/USB/FT601/USBTransferContext_FT601.cpp index aecd9d6c2..0e5e4d373 100644 --- a/src/comms/USB/FT601/USBTransferContext_FT601.cpp +++ b/src/comms/USB/FT601/USBTransferContext_FT601.cpp @@ -2,6 +2,7 @@ using namespace lime; +/** @brief Constructs a new USBTransferContext_FT601 object */ USBTransferContext_FT601::USBTransferContext_FT601() : USBTransferContext() #ifndef __unix__ diff --git a/src/comms/USB/FX3/FX3.cpp b/src/comms/USB/FX3/FX3.cpp index 6bae88d80..b7b853e2b 100644 --- a/src/comms/USB/FX3/FX3.cpp +++ b/src/comms/USB/FX3/FX3.cpp @@ -282,7 +282,7 @@ int FX3::BeginDataXfer(uint8_t* buffer, uint32_t length, uint8_t endPointAddr) return index; } -bool FX3::WaitForXfer(int contextHandle, uint32_t timeout_ms) +bool FX3::WaitForXfer(int contextHandle, int32_t timeout_ms) { if (contextHandle < 0) { @@ -291,7 +291,7 @@ bool FX3::WaitForXfer(int contextHandle, uint32_t timeout_ms) USBTransferContext_FX3* FX3context = &static_cast(contexts)[contextHandle]; - if (!FX3context->used) + if (!FX3context->isTransferUsed) { return true; //there is nothing to wait for (signal wait finished) } @@ -309,7 +309,7 @@ int FX3::FinishDataXfer(uint8_t* buffer, uint32_t length, int contextHandle) USBTransferContext_FX3* FX3context = &static_cast(contexts)[contextHandle]; - if (!FX3context->used) + if (!FX3context->isTransferUsed) { return 0; } @@ -318,7 +318,7 @@ int FX3::FinishDataXfer(uint8_t* buffer, uint32_t length, int contextHandle) long len = length; bool status = FX3context->EndPt->FinishDataXfer(buffer, len, FX3context->inOvLap, FX3context->context); - FX3context->used = false; + FX3context->isTransferUsed = false; FX3context->Reset(); if (!status) @@ -336,7 +336,7 @@ void FX3::AbortEndpointXfers(uint8_t endPointAddr) std::scoped_lock lock{ FX3mutex }; USBTransferContext_FX3* FX3context = &static_cast(contexts)[i]; - if (FX3context->used && FX3context->EndPt->Address == endPointAddr) + if (FX3context->isTransferUsed && FX3context->EndPt->Address == endPointAddr) { FX3context->EndPt->Abort(); } @@ -351,7 +351,7 @@ void FX3::WaitForXfers(uint8_t endPointAddr) { USBTransferContext_FX3* FX3context = &static_cast(contexts)[i]; - if (FX3context->used && FX3context->EndPt->Address == endPointAddr) + if (FX3context->isTransferUsed && FX3context->EndPt->Address == endPointAddr) { WaitForXfer(i, 250); FinishDataXfer(nullptr, 0, i); @@ -376,7 +376,7 @@ int FX3::GetUSBContextIndex() // Find not used context for (i = 0; i < USB_MAX_CONTEXTS; i++) { - if (!FX3contexts[i].used) + if (!FX3contexts[i].isTransferUsed) { contextFound = true; break; @@ -389,7 +389,7 @@ int FX3::GetUSBContextIndex() return -1; } - FX3contexts[i].used = true; + FX3contexts[i].isTransferUsed = true; return i; } diff --git a/src/comms/USB/FX3/FX3.h b/src/comms/USB/FX3/FX3.h index a5ccfc12b..686248002 100644 --- a/src/comms/USB/FX3/FX3.h +++ b/src/comms/USB/FX3/FX3.h @@ -19,6 +19,10 @@ namespace lime { class FX3 : public USBGeneric { public: + /** + @brief Constructs the class for communicating with the FX3 controller. + @param usbContext The USB context to use for the communication. + */ FX3(void* usbContext = nullptr); virtual ~FX3(); @@ -29,7 +33,7 @@ class FX3 : public USBGeneric #ifndef __unix__ virtual int BeginDataXfer(uint8_t* buffer, uint32_t length, uint8_t endPointAddr) override; - virtual bool WaitForXfer(int contextHandle, uint32_t timeout_ms) override; + virtual bool WaitForXfer(int contextHandle, int32_t timeout_ms) override; virtual int FinishDataXfer(uint8_t* buffer, uint32_t length, int contextHandle) override; virtual void AbortEndpointXfers(uint8_t endPointAddr) override; @@ -40,19 +44,25 @@ class FX3 : public USBGeneric override; #endif - static constexpr int CTR_W_REQCODE = 0xC1; - static constexpr int CTR_W_VALUE = 0x0000; - static constexpr int CTR_W_INDEX = 0x0000; - - static constexpr int CTR_R_REQCODE = 0xC0; - static constexpr int CTR_R_VALUE = 0x0000; - static constexpr int CTR_R_INDEX = 0x0000; - - static constexpr uint8_t CONTROL_BULK_OUT_ADDRESS = 0x0F; - static constexpr uint8_t CONTROL_BULK_IN_ADDRESS = 0x8F; - - static constexpr uint8_t STREAM_BULK_OUT_ADDRESS = 0x01; - static constexpr uint8_t STREAM_BULK_IN_ADDRESS = 0x81; + static constexpr int CTR_W_REQCODE = + 0xC1; ///< The request field of the setup packet for the write operation for a control transfer. + static constexpr int CTR_W_VALUE = 0x0000; ///< The value of the setup packet for the write operation for a control transfer. + static constexpr int CTR_W_INDEX = 0x0000; ///< The index of the setup packet for the write operation for a control transfer. + + static constexpr int CTR_R_REQCODE = + 0xC0; ///< The request field of the setup packet for the read operation for a control transfer. + static constexpr int CTR_R_VALUE = + 0x0000; ///< The value field of the setup packet for the read operation for a control transfer. + static constexpr int CTR_R_INDEX = + 0x0000; ///< The index field of the setup packet for the read operation for a control transfer. + + static constexpr uint8_t CONTROL_BULK_OUT_ADDRESS = + 0x0F; ///< The memory address for writing information via the bulk transfer protocol. + static constexpr uint8_t CONTROL_BULK_IN_ADDRESS = + 0x8F; ///< THe memory address for reading information via the bulk transfer protocol. + + static constexpr uint8_t STREAM_BULK_OUT_ADDRESS = 0x01; ///< The memory address to which to write the samples to broadcast. + static constexpr uint8_t STREAM_BULK_IN_ADDRESS = 0x81; ///< The memory address from which to read the incoming samples. protected: virtual int GetUSBContextIndex() override; diff --git a/src/comms/USB/FX3/USBTransferContext_FX3.cpp b/src/comms/USB/FX3/USBTransferContext_FX3.cpp index e6660313d..40d189816 100644 --- a/src/comms/USB/FX3/USBTransferContext_FX3.cpp +++ b/src/comms/USB/FX3/USBTransferContext_FX3.cpp @@ -22,9 +22,14 @@ USBTransferContext_FX3::~USBTransferContext_FX3() } #endif +/** + On Windows systems, resets the FX3 USB controller device. + + @return Opposite of `isTransferUsed`. + */ bool USBTransferContext_FX3::Reset() { - if (used) + if (isTransferUsed) { return false; } diff --git a/src/comms/USB/LMS64C_FPGA_Over_USB.h b/src/comms/USB/LMS64C_FPGA_Over_USB.h index 931858cdc..7462186db 100644 --- a/src/comms/USB/LMS64C_FPGA_Over_USB.h +++ b/src/comms/USB/LMS64C_FPGA_Over_USB.h @@ -11,6 +11,10 @@ namespace lime { class LMS64C_FPGA_Over_USB : public IComms { public: + /** + @brief Constructs a new LMS64C_FPGA_Over_USB object. + @param dataPort The USB communications pipe to use. + */ LMS64C_FPGA_Over_USB(std::shared_ptr dataPort); virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; diff --git a/src/comms/USB/LMS64C_LMS7002M_Over_USB.h b/src/comms/USB/LMS64C_LMS7002M_Over_USB.h index 13b166b63..05c4913ba 100644 --- a/src/comms/USB/LMS64C_LMS7002M_Over_USB.h +++ b/src/comms/USB/LMS64C_LMS7002M_Over_USB.h @@ -11,6 +11,10 @@ namespace lime { class LMS64C_LMS7002M_Over_USB : public IComms { public: + /** + @brief Constructs a new LMS64C_LMS7002M_Over_USB object + @param dataPort The USB communications pipe to use. + */ LMS64C_LMS7002M_Over_USB(std::shared_ptr dataPort); virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) override; diff --git a/src/comms/USB/TRXLooper_USB.cpp b/src/comms/USB/TRXLooper_USB.cpp index 034792fda..0d4a563cf 100644 --- a/src/comms/USB/TRXLooper_USB.cpp +++ b/src/comms/USB/TRXLooper_USB.cpp @@ -12,6 +12,12 @@ using namespace std::literals::string_literals; namespace lime { +/// @brief Constructs a TRXLooper_USB object. +/// @param comms The USB communications interface to use. +/// @param f The FPGA to use. +/// @param chip The LMS7002M chip to use. +/// @param rxEndPt The endpoint for receiving the stream data from the USB communications. +/// @param txEndPt The endpoint for transmitting the stream data to the USB communications. TRXLooper_USB::TRXLooper_USB(std::shared_ptr comms, FPGA* f, LMS7002M* chip, uint8_t rxEndPt, uint8_t txEndPt) : TRXLooper(f, chip, 0) , comms(comms) @@ -297,7 +303,7 @@ void TRXLooper_USB::ReceivePacketsLoop() const int samplesInPkt = (mConfig.linkFormat == SDRDevice::StreamConfig::DataFormat::I16 ? 1020 : 1360) / conversion.channelCount; - const int outputSampleSize = + const uint8_t outputSampleSize = mConfig.format == SDRDevice::StreamConfig::DataFormat::F32 ? sizeof(complex32f_t) : sizeof(complex16_t); const int32_t outputPktSize = SamplesPacketType::headerSize + packetsToBatch * samplesInPkt * outputSampleSize; diff --git a/src/comms/USB/USBEntry.h b/src/comms/USB/USBEntry.h index 3356283db..550a3e401 100644 --- a/src/comms/USB/USBEntry.h +++ b/src/comms/USB/USBEntry.h @@ -38,6 +38,11 @@ struct VidPid { class USBEntry : public DeviceRegistryEntry { public: + /** + @brief Constructs a new USBEntry object. + @param name The name of the device. + @param deviceIds The device Vendor and Product ID pairs to match to. + */ USBEntry(const std::string& name, const std::set& deviceIds); virtual ~USBEntry(); diff --git a/src/comms/USB/USBGeneric.cpp b/src/comms/USB/USBGeneric.cpp index 912d25079..33fc61cc0 100644 --- a/src/comms/USB/USBGeneric.cpp +++ b/src/comms/USB/USBGeneric.cpp @@ -184,7 +184,7 @@ void USBGeneric::Disconnect() for (int i = 0; i < USB_MAX_CONTEXTS; ++i) { - if (contexts[i].used) + if (contexts[i].isTransferUsed) { AbortEndpointXfers(contexts[i].transfer->endpoint); } @@ -299,7 +299,7 @@ int USBGeneric::BeginDataXfer(uint8_t* buffer, uint32_t length, uint8_t endPoint if (status != 0) { lime::error("BEGIN DATA TRANSFER %s", libusb_error_name(status)); - contexts[i].used = false; + contexts[i].isTransferUsed = false; return -1; } @@ -308,10 +308,10 @@ int USBGeneric::BeginDataXfer(uint8_t* buffer, uint32_t length, uint8_t endPoint return 0; } -bool USBGeneric::WaitForXfer(int contextHandle, uint32_t timeout_ms) +bool USBGeneric::WaitForXfer(int contextHandle, int32_t timeout_ms) { #ifdef __unix__ - if (contextHandle >= 0 && contexts[contextHandle].used == true) + if (contextHandle >= 0 && contexts[contextHandle].isTransferUsed == true) { // Blocking not to waste CPU std::unique_lock lck(contexts[contextHandle].transferLock); @@ -325,10 +325,10 @@ bool USBGeneric::WaitForXfer(int contextHandle, uint32_t timeout_ms) int USBGeneric::FinishDataXfer(uint8_t* buffer, uint32_t length, int contextHandle) { #ifdef __unix__ - if (contextHandle >= 0 && contexts[contextHandle].used == true) + if (contextHandle >= 0 && contexts[contextHandle].isTransferUsed == true) { length = contexts[contextHandle].bytesXfered; - contexts[contextHandle].used = false; + contexts[contextHandle].isTransferUsed = false; contexts[contextHandle].Reset(); return length; } @@ -347,7 +347,7 @@ void USBGeneric::AbortEndpointXfers(uint8_t endPointAddr) #ifdef __unix__ for (int i = 0; i < USB_MAX_CONTEXTS; ++i) { - if (contexts[i].used && contexts[i].transfer->endpoint == endPointAddr) + if (contexts[i].isTransferUsed && contexts[i].transfer->endpoint == endPointAddr) { libusb_cancel_transfer(contexts[i].transfer); } @@ -370,7 +370,7 @@ int USBGeneric::GetUSBContextIndex() // Find not used context for (i = 0; i < USB_MAX_CONTEXTS; i++) { - if (!contexts[i].used) + if (!contexts[i].isTransferUsed) { contextFound = true; break; @@ -383,7 +383,7 @@ int USBGeneric::GetUSBContextIndex() return -1; } - contexts[i].used = true; + contexts[i].isTransferUsed = true; return i; } @@ -393,7 +393,7 @@ void USBGeneric::WaitForXfers(uint8_t endPointAddr) #ifdef __unix__ for (int i = 0; i < USB_MAX_CONTEXTS; ++i) { - if (contexts[i].used && contexts[i].transfer->endpoint == endPointAddr) + if (contexts[i].isTransferUsed && contexts[i].transfer->endpoint == endPointAddr) { WaitForXfer(i, 250); FinishDataXfer(nullptr, 0, i); diff --git a/src/comms/USB/USBGeneric.h b/src/comms/USB/USBGeneric.h index d779d8c16..35e5f1bcd 100644 --- a/src/comms/USB/USBGeneric.h +++ b/src/comms/USB/USBGeneric.h @@ -23,24 +23,90 @@ class USBGeneric { public: USBGeneric() = delete; + + /** + @brief Construct a new USBGeneric object. + + If this is the first object of this kind being constructed on a UNIX system + it also creates the thread to handle USB events. + @param usbContext The USB context to use. + */ USBGeneric(void* usbContext); virtual ~USBGeneric(); - static const int32_t defaultTimeout = 1000; - + static constexpr int32_t defaultTimeout = 1000; ///< The default timeout to use if none is specified. + + /** + @brief Connects to a given USB device. + @param vid The vendor ID of the device. + @param pid The prduct ID of the device. + @param serial The serial number of the device. + @return The status of the operation (true on success). + */ virtual bool Connect(uint16_t vid, uint16_t pid, const std::string& serial = ""); + + /** + @brief Returns whether this instance is connected to a device. + @return The state of the connection (true = connected). + */ virtual bool IsConnected(); + + /** @brief Disconnects from the USB device. */ virtual void Disconnect(); - // return actual number of bytes transferred + /** + @brief Transfers data using the bulk transfer USB protocol. + @param endPoint The address to use for the transfer. + @param data The pointer to the data buffer. + @param length The length of data being transferred. + @param timeout_ms The amount of time to wait (in ms) until the transfer is considered failed. + @return Actual number of bytes transferred. + */ virtual int32_t BulkTransfer(uint8_t endPoint, uint8_t* data, int length, int32_t timeout_ms = defaultTimeout); - // return actual number of bytes transferred + /** + @brief Transfers data using the control transfer USB protocol. + @param requestType The request type to use in the setup packet. + @param request The type of request being made. + @param value The value of the request being made. + @param index The index of the request being made. + @param data The pointer to the data buffer. + @param length The length of data being transferred. + @param timeout_ms The amount of time to wait (in ms) until the transfer is considered failed. + @return Actual number of bytes transferred. + */ virtual int32_t ControlTransfer( int requestType, int request, int value, int index, uint8_t* data, uint32_t length, int32_t timeout_ms = defaultTimeout); + /** + @brief Begins an asynchronous data transfer. + @param buffer The pointer to the data buffer. + @param length The length of the data being transferred. + @param endPointAddr The endpoint address to use for the transfer. + @return The handle of the transfer context to pass to WaitForXfer and FinishDataXfer functions. + */ virtual int BeginDataXfer(uint8_t* buffer, uint32_t length, uint8_t endPointAddr); - virtual bool WaitForXfer(int contextHandle, uint32_t timeout_ms); + + /** + @brief Waits until an asynchronous data transfer finishes. + @param contextHandle The context handle to wait for (received from BeginDataXfer). + @param timeout_ms The timeout (in ms) to wait for the transfer. + @return Indication whether the transfer is finished or not (true = finished). + */ + virtual bool WaitForXfer(int contextHandle, int32_t timeout_ms = defaultTimeout); + + /** + @brief Finishes an asynchronous data transfer. + @param buffer The pointer to the data buffer. + @param length The length of the data being transferred. + @param contextHandle The handle of the transfer (received from BeginDataXfer). + @return The amount of bytes transferred in the transfer. + */ virtual int FinishDataXfer(uint8_t* buffer, uint32_t length, int contextHandle); + + /** + @brief Aborts all current asynchronous transfers at the given endpoint. + @param endPointAddr The endpoint to abort all transfers from. + */ virtual void AbortEndpointXfers(uint8_t endPointAddr); protected: diff --git a/src/comms/USB/USBTransferContext.cpp b/src/comms/USB/USBTransferContext.cpp index 685416883..dd36b7b3d 100644 --- a/src/comms/USB/USBTransferContext.cpp +++ b/src/comms/USB/USBTransferContext.cpp @@ -3,7 +3,7 @@ using namespace lime; USBTransferContext::USBTransferContext() - : used(false) + : isTransferUsed(false) { #ifdef __unix__ transfer = libusb_alloc_transfer(0); @@ -22,7 +22,11 @@ USBTransferContext::~USBTransferContext() #endif } +/** + @brief Abstract method to override to reset the USB device. + @returns Opposite of `isTransferUsed`. + */ bool USBTransferContext::Reset() { - return !used; + return !isTransferUsed; } diff --git a/src/comms/USB/USBTransferContext.h b/src/comms/USB/USBTransferContext.h index 7d60d26e8..b030f3a0b 100644 --- a/src/comms/USB/USBTransferContext.h +++ b/src/comms/USB/USBTransferContext.h @@ -31,7 +31,7 @@ class USBTransferContext virtual ~USBTransferContext(); virtual bool Reset(); - bool used; + bool isTransferUsed; ///< A flag to mark if this transfer is currently being used. #ifdef __unix__ libusb_transfer* transfer; diff --git a/src/comms/USB/USB_CSR_Pipe.h b/src/comms/USB/USB_CSR_Pipe.h index d5852df75..34520a1e5 100644 --- a/src/comms/USB/USB_CSR_Pipe.h +++ b/src/comms/USB/USB_CSR_Pipe.h @@ -11,8 +11,8 @@ class USB_CSR_Pipe : public ISerialPort public: explicit USB_CSR_Pipe(){}; - virtual int Write(const uint8_t* data, size_t length, int timeout_ms) override = 0; - virtual int Read(uint8_t* data, size_t length, int timeout_ms) override = 0; + virtual int Write(const uint8_t* data, std::size_t length, int timeout_ms) override = 0; + virtual int Read(uint8_t* data, std::size_t length, int timeout_ms) override = 0; }; } // namespace lime diff --git a/src/examples/basicRX.cpp b/src/examples/basicRX.cpp index d25c56eec..1069f22ce 100644 --- a/src/examples/basicRX.cpp +++ b/src/examples/basicRX.cpp @@ -132,8 +132,8 @@ int main(int argc, char** argv) SDRDevice::StreamMeta rxMeta; while (std::chrono::high_resolution_clock::now() - startTime < std::chrono::seconds(10) && !stopProgram) { - int samplesRead = device->StreamRx(chipIndex, rxSamples, fftSize, &rxMeta); - if (samplesRead <= 0) + uint32_t samplesRead = device->StreamRx(chipIndex, rxSamples, fftSize, &rxMeta); + if (samplesRead == 0) continue; // process samples @@ -167,7 +167,7 @@ int main(int argc, char** argv) (frequencyLO + peakFrequency) / 1e6); #ifdef USE_GNU_PLOT gp.write("plot '-' with points\n"); - for (int j = 0; j < samplesRead; ++j) + for (uint32_t j = 0; j < samplesRead; ++j) gp.writef("%f %f\n", rxSamples[0][j].real(), rxSamples[0][j].imag()); gp.write("e\n"); gp.flush(); diff --git a/src/examples/basicTX.cpp b/src/examples/basicTX.cpp index fcc573fc9..85461f244 100644 --- a/src/examples/basicTX.cpp +++ b/src/examples/basicTX.cpp @@ -128,15 +128,15 @@ int main(int argc, char** argv) SDRDevice::StreamMeta txMeta; txMeta.timestamp = 0; - txMeta.useTimestamp = true; - txMeta.flush = true; + txMeta.waitForTimestamp = true; + txMeta.flushPartialPacket = true; - int totalSamplesSent = 0; + uint32_t totalSamplesSent = 0; while (std::chrono::high_resolution_clock::now() - startTime < std::chrono::seconds(10) && !stopProgram) //run for 10 seconds { - int samplesToSend = samplesInPkt * txPacketCount; - int samplesSent = device->StreamTx(chipIndex, src, samplesToSend, &txMeta); + uint32_t samplesToSend = samplesInPkt * txPacketCount; + uint32_t samplesSent = device->StreamTx(chipIndex, src, samplesToSend, &txMeta); if (samplesSent < 0) { printf("Failure to send\n"); diff --git a/src/examples/dualRXTX.cpp b/src/examples/dualRXTX.cpp index 89a2c8dfd..644b50dab 100644 --- a/src/examples/dualRXTX.cpp +++ b/src/examples/dualRXTX.cpp @@ -125,17 +125,17 @@ int main(int argc, char** argv) auto t2 = t1; int totalSamplesReceived = 0; - int totalSamplesSent = 0; + uint32_t totalSamplesSent = 0; float maxSignalAmplitude = 0; SDRDevice::StreamMeta rxMeta; while (std::chrono::high_resolution_clock::now() - startTime < std::chrono::seconds(10) && !stopProgram) { - int samplesRead = device->StreamRx(chipIndex, rxSamples, samplesInBuffer, &rxMeta); + uint32_t samplesRead = device->StreamRx(chipIndex, rxSamples, samplesInBuffer, &rxMeta); totalSamplesReceived += samplesRead; // process samples - for (int n = 0; n < samplesRead; ++n) + for (uint32_t n = 0; n < samplesRead; ++n) { float amplitude = pow(rxSamples[0][n].real(), 2) + pow(rxSamples[0][n].imag(), 2); if (amplitude > maxSignalAmplitude) @@ -144,9 +144,9 @@ int main(int argc, char** argv) SDRDevice::StreamMeta txMeta; txMeta.timestamp = rxMeta.timestamp + samplesInBuffer * 64; - txMeta.useTimestamp = true; - txMeta.flush = false; - int samplesSent = device->StreamTx(chipIndex, rxSamples, samplesInBuffer, &txMeta); + txMeta.waitForTimestamp = true; + txMeta.flushPartialPacket = false; + uint32_t samplesSent = device->StreamTx(chipIndex, rxSamples, samplesInBuffer, &txMeta); if (samplesSent < 0) { printf("Failure to send\n"); diff --git a/src/fftviewer_wxgui/fftviewer_frFFTviewer.cpp b/src/fftviewer_wxgui/fftviewer_frFFTviewer.cpp index 81859745b..54ab05f39 100644 --- a/src/fftviewer_wxgui/fftviewer_frFFTviewer.cpp +++ b/src/fftviewer_wxgui/fftviewer_frFFTviewer.cpp @@ -473,8 +473,8 @@ void fftviewer_frFFTviewer::StreamingLoop( pthis->mStreamRunning.store(true); SDRDevice::StreamMeta txMeta; - txMeta.useTimestamp = syncTx; - txMeta.flush = true; + txMeta.waitForTimestamp = syncTx; + txMeta.flushPartialPacket = true; int fftCounter = 0; SDRDevice::StreamMeta rxMeta; diff --git a/src/include/Register.h b/src/include/Register.h index f7b1244b3..ff079954d 100644 --- a/src/include/Register.h +++ b/src/include/Register.h @@ -1,34 +1,52 @@ #ifndef LIME_REGISTER_H #define LIME_REGISTER_H -#include +#include namespace lime { -inline constexpr uint32_t bitMask(uint8_t msb, uint8_t lsb) +/** + * @brief Gets the bit mask for the register + * @param msb The index of the most significant bit + * @param lsb The index of the least significant bit + * @return The mask of the bits of the register. + */ +constexpr uint32_t bitMask(uint8_t msb, uint8_t lsb) { return (~(~0u << (msb - lsb + 1))) << (lsb); } +/** + * @brief A structure for holding information about a register. + */ struct Register { - Register() + /// @brief Constructs the register with default values. + constexpr Register() : address(0) , defaultValue(0) , msb(15) , lsb(0) , twoComplement(false){}; + + /// @brief Constructs the register with the given values. + /// @param address The memory address of the register. + /// @param msb The index of the most significant bit of the register. + /// @param lsb The index of the least significant bit of the register. + /// @param defaultValue The default value of the register. + /// @param twocomplement Whether the register is represented in a Two's Complement way. constexpr Register(uint16_t address, uint8_t msb, uint8_t lsb, uint16_t defaultValue, bool twocomplement) : address(address) , defaultValue(defaultValue) , msb(msb) , lsb(lsb) , twoComplement(twocomplement){}; - uint16_t address; - uint16_t defaultValue; - uint8_t msb; - uint8_t lsb; - bool twoComplement; + + uint16_t address; ///< The memory address. + uint16_t defaultValue; ///< The default value of the register. + uint8_t msb; ///< The index of the most significant bit of the register. + uint8_t lsb; ///< The index of the least significant bit of the register. + bool twoComplement; ///< Indicates if the register is represented in a Two's Complement way. }; } // namespace lime -#endif // LIME_REGISTER_H \ No newline at end of file +#endif // LIME_REGISTER_H diff --git a/src/include/limesuite/DeviceHandle.h b/src/include/limesuite/DeviceHandle.h index 473d36f3a..49f2ad65d 100644 --- a/src/include/limesuite/DeviceHandle.h +++ b/src/include/limesuite/DeviceHandle.h @@ -45,12 +45,14 @@ class LIME_API DeviceHandle /*! * Serialize this connection handle into a string format. * This string format can be used to represent the handle. + * + * @return The handle in string format. */ std::string Serialize(void) const; /*! * Get a displayable string for this handle. - * @return a string that may be printed + * @return A string that may be printed. */ std::string ToString(void) const; diff --git a/src/include/limesuite/DeviceNode.h b/src/include/limesuite/DeviceNode.h index 9af6977ee..fef45bd3b 100644 --- a/src/include/limesuite/DeviceNode.h +++ b/src/include/limesuite/DeviceNode.h @@ -3,21 +3,30 @@ #include "DeviceNodeClass.h" #include +#include +#include namespace lime { +/// @brief Structure describing a device node in the device node tree. struct DeviceNode { + /// @brief Default constructor for the node. DeviceNode(){}; + + /// @brief The constructor for the device node. + /// @param name The name of the node. + /// @param nodeClass The device class of the node. + /// @param ptr The pointer to the device. DeviceNode(const std::string& name, eDeviceNodeClass nodeClass, void* ptr) : name(name) , deviceNodeClass(nodeClass) , ptr(ptr) { } - std::string name; - eDeviceNodeClass deviceNodeClass; - void* ptr; - std::vector> children; + std::string name; ///< The name of the node. + eDeviceNodeClass deviceNodeClass; ///< The device class of the node. + void* ptr; ///< The pointer to the device. + std::vector> children; ///< The children of this node in the device tree. }; } // namespace lime diff --git a/src/include/limesuite/DeviceRegistry.h b/src/include/limesuite/DeviceRegistry.h index 9a56d3743..22f5a3742 100644 --- a/src/include/limesuite/DeviceRegistry.h +++ b/src/include/limesuite/DeviceRegistry.h @@ -21,26 +21,30 @@ class LIME_API DeviceRegistry public: /*! * Discovery identifiers that can be used to create a connection. - * The hint may contain a connection type, serial number, ip address, etc. - * \param hint an optional connection handle with some fields filled-in - * \return a list of handles which can be used to make a connection + * The hint may contain a connection type, serial number, IP address, etc. + * \param hint An optional connection handle with some fields filled-in + * \return A list of handles which can be used to make a connection */ static std::vector enumerate(const DeviceHandle& hint = DeviceHandle()); /*! * Create a connection from an identifying handle. - * Return a null pointer when no factories are available. - * \param handle a connection handle with fields filled-in - * \return a pointer to a connection instance (or null) + * Returns a null pointer when no factories are available. + * \param handle A connection handle with fields filled-in + * \return A pointer to a connection instance (or null) */ static SDRDevice* makeDevice(const DeviceHandle& handle); /*! * Free an connection created by makeConnection(). + * \param conn The connection to free. */ static void freeDevice(SDRDevice* conn); - //! Get a list of available registry entry modules by name + /*! + * Get a list of available registry entry modules by name. + * \return The list of available registry modules. + */ static std::vector moduleNames(void); }; @@ -64,6 +68,8 @@ class LIME_API DeviceRegistryEntry * registers it into the connection registry. * The name describes the device board type, * that should be unique to the entry instance. + * + * \param name The name of the device entry. */ DeviceRegistryEntry(const std::string& name); @@ -74,15 +80,15 @@ class LIME_API DeviceRegistryEntry * A discovery function takes a connection handle hint * and returns a list of identifiers that can be used * to create connection with makeConnection(). - * \param hint an optional connection handle with some fields filled-in - * \return a list of handles which can be used to make a connection + * \param hint An optional connection handle with some fields filled-in. + * \return A list of handles which can be used to make a connection. */ virtual std::vector enumerate(const DeviceHandle& hint) = 0; /*! * A factory function creates a SDRDevice from a device handle. - * \param handle a device handle with fields filled-in - * \return a pointer to a SDRDevice instance + * \param handle A device handle with fields filled-in. + * \return A pointer to a SDRDevice instance. */ virtual SDRDevice* make(const DeviceHandle& handle) = 0; diff --git a/src/include/limesuite/IComms.h b/src/include/limesuite/IComms.h index 6709de6e9..ad3595f46 100644 --- a/src/include/limesuite/IComms.h +++ b/src/include/limesuite/IComms.h @@ -19,7 +19,7 @@ class LIME_API ISPI @param MOSI Main Out Sub In (data output from main). @param MISO Main In Sub Out (data output from sub). @param count Input/output data length. - @returns Whether the operation succeedded or not. + @returns The operation status. */ virtual OpStatus SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) = 0; @@ -29,7 +29,7 @@ class LIME_API ISPI @param MOSI Main Out Sub In (data output from main). @param MISO Main In Sub Out (data output from sub). @param count Input/output data length. - @returns Whether the operation succeedded or not. + @returns The operation status. */ virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) = 0; }; @@ -45,7 +45,7 @@ class LIME_API II2C @param address Inter-Integrated Circuit slave address. @param data Output buffer. @param length Output data length. - @return 0 on success. + @return The operation status. */ virtual OpStatus I2CWrite(int address, const uint8_t* data, uint32_t length) = 0; @@ -58,15 +58,16 @@ class LIME_API II2C @param address The address of the slave. @param [out] dest Buffer to store read data from the slave. @param length Number of bytes to read. - @return 0 on success. + @return The operation status. */ virtual OpStatus I2CRead(int address, uint8_t* dest, uint32_t length) = 0; }; +/// @brief The structure for writing and reading custom parameters struct CustomParameterIO { - int32_t id; - double value; - std::string units; + int32_t id; ///< The ID of the parameter + double value; ///< The value of the parameter. + std::string units; ///< The units of the parameter. }; /** @brief An interface for general device communications */ diff --git a/src/include/limesuite/LMS7002M.h b/src/include/limesuite/LMS7002M.h index 94f8f60fe..4dff2cd96 100644 --- a/src/include/limesuite/LMS7002M.h +++ b/src/include/limesuite/LMS7002M.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -26,27 +27,19 @@ class ISPI; class LMS7002M_RegistersMap; class MCU_BD; -struct RSSI_measurements { - void clear() - { - amplitudeFFT.clear(); - amplitudeGeortzelF.clear(); - amplitudeGeortzelFPGA.clear(); - } - - std::vector amplitudeFFT; - std::vector amplitudeGeortzelF; - std::vector amplitudeGeortzelFPGA; -}; - typedef double float_type; -/** @brief Class for communicating with the LMS7002M chip. */ +/*! @brief Class for communicating with the LMS7002M chip. + * + * More information: https://limemicro.com/technology/lms7002m/ +*/ class LIME_API LMS7002M { public: + /// @brief The maximum frequency of the onboard clock generator. static constexpr double CGEN_MAX_FREQ = 640e6; + /// @brief The IDs of the clocks on the chip. enum class ClockID : uint8_t { CLK_REFERENCE = 0, ///< Reference clock CLK_SXR = 1, ///< RX LO clock @@ -81,14 +74,22 @@ class LIME_API LMS7002M bool success; }; + /*! + * The constructor for the LMS7002M chip. + * @param port The connection interface + */ LMS7002M(std::shared_ptr port); /*! * Set the connection for the LMS7002M driver. - * @param port the connection interface + * @param port The connection interface */ void SetConnection(std::shared_ptr port); + /*! + * @brief Get the current connection to the device. + * @return The connection to the device. + */ std::shared_ptr GetConnection(void) const { return controlPort; } virtual ~LMS7002M(); @@ -108,15 +109,24 @@ class LIME_API LMS7002M /*! * Set the selected channel (MAC). * The API calls will reflect this channel. + * + * @param ch The channel to set the chip to. */ void SetActiveChannel(const Channel ch); /*! * Get the selected channel (MAC). * The API calls will reflect this channel. + * @param fromChip Whether to read directly from the chip or use the cache (true = device, false = cache) + * @return The currently active channel. */ Channel GetActiveChannel(bool fromChip = true); + /*! + * Gets the index of the current selected channel (MAC) + * @param fromChip Whether to read directly from the chip or use the cache (true = device, false = cache) + * @return The index of the currently active channel (0 - Channel A, 1 - Channel B) + */ size_t GetActiveChannelIndex(bool fromChip = true); /*! @@ -126,112 +136,301 @@ class LIME_API LMS7002M * @param dir Rx or Tx * @param channel true for the transmit size, false for receive * @param enable true to enable, false to disable + * @return The status of the operation */ OpStatus EnableChannel(TRXDir dir, const uint8_t channel, const bool enable); + /*! + * @brief Writes all registers from host to chip + * @return The status of the operation + */ OpStatus UploadAll(); + + /*! + * @brief Reads all registers from the chip to host + * @return The status of the operation + */ OpStatus DownloadAll(); + + /*! + * @brief Reads all chip configuration and checks if it matches with local registers copy. + * @return Whether the cached value is synced with the device or not + */ bool IsSynced(); + + /** + * @brief Copies all the channel specific registers from one channel to another one. + * @param src The channel to copy from. + * @param dest The channel to copy to. + * @param copySX Whether to copy the SX registers or not. + * @return The status of the operation + */ OpStatus CopyChannelRegisters(const Channel src, const Channel dest, bool copySX); + /*! + * @brief Sends reset signal to chip, after reset enables B channel controls + * @return The status of the operation + */ OpStatus ResetChip(); /*! * Perform soft-reset sequence over SPI + * @return The status of the operation */ OpStatus SoftReset(); - OpStatus ResetLogicregisters(); + /*! + * @brief Resets the logic registers to their default state. + * @return The status of the operation + */ + OpStatus ResetLogicRegisters(); + + /*! + * @brief Reads configuration file and uploads registers to chip + * @param filename Configuration source file + * @param tuneDynamicValues Whether to tune the dynamic values or not + * @return The status of the operation + */ OpStatus LoadConfig(const std::string& filename, bool tuneDynamicValues = true); + + /*! + * @brief Reads all registers from chip and saves to file + * @param filename destination filename + * @return The status of the operation + */ OpStatus SaveConfig(const std::string& filename); + /*! + * @brief Returns given parameter value from chip register + * @param param LMS7002M control parameter + * @param fromChip read directly from chip + * @return parameter value + */ uint16_t Get_SPI_Reg_bits(const LMS7Parameter& param, bool fromChip = false); + + /*! + * @brief Returns given parameter value from chip register + * @param address register address + * @param msb most significant bit index + * @param lsb least significant bit index + * @param fromChip read directly from chip + * @return register bits from selected interval, shifted to right by lsb bits + */ uint16_t Get_SPI_Reg_bits(uint16_t address, uint8_t msb, uint8_t lsb, bool fromChip = false); + + /*! + * @brief Change given parameter value + * @param param LMS7002M control parameter + * @param fromChip read initial value directly from chip + * @param value new parameter value + * @return The status of the operation + */ OpStatus Modify_SPI_Reg_bits(const LMS7Parameter& param, const uint16_t value, bool fromChip = false); + + /*! + * @brief Change given parameter value + * @param address register address + * @param msb Most significant bit index + * @param lsb Least significant bit index + * @param value new bits value, the value is shifted left by lsb bits + * @param fromChip read initial value directly from chip + * @return The status of the operation + */ OpStatus Modify_SPI_Reg_bits(uint16_t address, uint8_t msb, uint8_t lsb, uint16_t value, bool fromChip = false); + + /*! + * @brief Write given data value to whole register + * @param address SPI address + * @param data new register value + * @param toChip whether we're writing to the chip or not + * @return The status of the operation + */ OpStatus SPI_write(uint16_t address, uint16_t data, bool toChip = false); + + /*! + * @brief Reads whole register value from given address + * @param address SPI address + * @param fromChip read value directly from chip + * @param status The operation success status (optional). + * @return register value + */ uint16_t SPI_read(uint16_t address, bool fromChip = false, OpStatus* status = 0); + + /*! + * @brief Performs registers test by writing known data and confirming readback data + * @param fileName The name of the file to write the test output to. + * @return The operation status. + */ OpStatus RegistersTest(const std::string& fileName = "registersTest.txt"); + + /*! + * @brief Get parameter by name + * @param name The name of the parameter to get. + * @return A constant reference to the parameter + */ static const LMS7Parameter& GetParam(const std::string& name); - OpStatus CalibrateRx(float_type bandwidth, const bool useExtLoopback = false); - OpStatus CalibrateTx(float_type bandwidth, const bool useExtLoopback = false); + /*! + * @brief Calibrates Receiver. DC offset, IQ gains, IQ phase correction + * @param bandwidth_Hz The bandwidth to calibrate the device for (in Hz) + * @param useExtLoopback Whether to use external loopback or not. + * @return The status of the operation + */ + OpStatus CalibrateRx(float_type bandwidth_Hz, const bool useExtLoopback = false); - OpStatus TuneTxFilter(const float_type bandwidth); + /*! + * @brief Calibrates Transmitter. DC correction, IQ gains, IQ phase correction + * @param bandwidth_Hz The bandwidth to calibrate the device for (in Hz) + * @param useExtLoopback Whether to use external loopback or not. + * @return The status of the operation + */ + OpStatus CalibrateTx(float_type bandwidth_Hz, const bool useExtLoopback = false); + + /** + * @brief Tunes the Low Pass Filter for the TX direction. + * @param tx_lpf_freq_RF The frequency (in Hz) to tune the filter to. + * @return The status of the operation + */ + OpStatus TuneTxFilter(const float_type tx_lpf_freq_RF); + + /** + * @brief Tunes the Low Pass Filter for the RX direction. + * @param rx_lpf_freq_RF The frequency (in Hz) to tune the filter to. + * @return The status of the operation + */ OpStatus TuneRxFilter(const float_type rx_lpf_freq_RF); + /*! + * @brief Calibrates the internal Analog to Digital Converter on the chip. + * @param clkDiv The clock division ratio for measurement loop. + * @return The status of the operation + */ OpStatus CalibrateInternalADC(int clkDiv = 32); + + /*! + * @brief Calibrates the RP_BIAS of the chip. + * @return The status of the operation + */ OpStatus CalibrateRP_BIAS(); - OpStatus CalibrateTxGain(float maxGainOffset_dBFS, float* actualGain_dBFS); + + /*! + * @brief Calibrates the TX gain. + * @return The status of the operation + */ + OpStatus CalibrateTxGain(); + + /** + * @brief Calibrates the Analog RSSI + * @return The status of the operation + */ OpStatus CalibrateAnalogRSSI_DC_Offset(); /*! * Set the RX PGA gain in dB - * @param gain in dB range -12.0, 19.0 dB - * @return 0 for success, else error + * @param gain In dB range -12.0, 19.0 dB + * @param channel The channel to set it for + * @return The status of the operation */ OpStatus SetRBBPGA_dB(const float_type gain, const Channel channel); + /*! + * Gets the RX PGA gain in dB + * @param channel The channel to get it from + * @return The RX PGA gain (in dB) + */ float_type GetRBBPGA_dB(const Channel channel); /*! * Set the RX LNA gain in dB - * @param gain in dB range 0.0, 30.0 dB - * @return 0 for success, else error + * @param gain In dB range 0.0, 30.0 dB + * @param channel The channel to set it for + * @return The status of the operation */ OpStatus SetRFELNA_dB(const float_type gain, const Channel channel); + /*! + * Gets the RX LNA gain in dB + * @param channel The channel to get it from + * @return The RX LNA gain (in dB) + */ float_type GetRFELNA_dB(const Channel channel); /*! * Set the RX loopback LNA gain in dB - * @param gain in dB range 0.0, 40.0 dB - * @return 0 for success, else error + * @param gain In dB range 0.0, 40.0 dB + * @param channel The channel to set it for + * @return The status of the operation */ OpStatus SetRFELoopbackLNA_dB(const float_type gain, const Channel channel); - //! Get the actual RX loopback LNA gain in dB + /*! + * Get the actual RX loopback LNA gain in dB + * @param channel The channel to get it from + * @return The actual RX loopback LNA gain (in dB) + */ float_type GetRFELoopbackLNA_dB(const Channel channel); /*! * Set the RX TIA gain in dB - * @param gain in dB range 0.0, 12.0 dB - * @return 0 for success, else error + * @param gain In dB range 0.0, 12.0 dB + * @param channel The channel to set it for + * @return The status of the operation */ OpStatus SetRFETIA_dB(const float_type gain, const Channel channel); + /*! + * Get the RX TIA gain in dB + * @param channel The channel to get it from + * @return The RX TIA gain in dB + */ float_type GetRFETIA_dB(const Channel channel); /*! * Set the TX PAD gain in dB - * @param gain in dB range -52.0, 0.0 dB - * @return 0 for success, else error + * @param gain In dB range -52.0, 0.0 dB + * @param channel The channel to set it for + * @return The status of the operation */ OpStatus SetTRFPAD_dB(const float_type gain, const Channel channel); - //! Get the actual TX PAD gain in dB + /*! + * @brief Gets the actual TX PAD gain. + * @param channel The channel to get the gain from. + * @return The actual TX PAD gain (in dB). + */ float_type GetTRFPAD_dB(const Channel channel); /*! * Set the TBB frontend gain in dB - * @param gain in dB relative to optimal gain (0 - optimal gain, >0 may cause saturation) - * @return 0 for success, else error + * @param gain In dB relative to optimal gain (0 - optimal gain, >0 may cause saturation) + * @param channel The channel to set it for + * @return The status of the operation */ OpStatus SetTBBIAMP_dB(const float_type gain, const Channel channel); - //! Get the TBB frontend gain in dB + /*! + * @brief Gets the TBB frontend gain. + * @param channel The channel to get the gain from. + * @return The actual TBB frontend gain (in dB). + */ float_type GetTBBIAMP_dB(const Channel channel); /*! * Set the TX loopback PAD gain in dB - * @param gain in dB range -4.3, 0.0 dB - * @return 0 for success, else error + * @param gain In dB range -4.3, 0.0 dB + * @param channel The channel to set it for + * @return The status of the operation */ OpStatus SetTRFLoopbackPAD_dB(const float_type gain, const Channel channel); - //! Get the actual TX loopback PAD gain in dB + /*! + * @brief Gets the actual TX loopback PAD gain. + * @param channel The channel to get the gain from. + * @return The actual TX loopback PAD gain (in dB). + */ float_type GetTRFLoopbackPAD_dB(const Channel channel); + /// @brief The possible antennae on the chip. enum class PathRFE : uint8_t { NONE, LNAH, @@ -241,15 +440,23 @@ class LIME_API LMS7002M LB2, }; - //! Set the RFE input path. + /*! + * @brief Sets the RFE input path. + * @param path The enumeration value of the path to set it to + * @return The status of the operation + */ OpStatus SetPathRFE(PathRFE path); - //! Get the currently set RFE path + /*! + * @brief Gets the currently set RFE path + * @return The enumerator value of the currently selected RFE path. + */ PathRFE GetPathRFE(void); /*! * Set the TRF Band selection. * @param band 1 or 2 + * @return The status of the operation */ OpStatus SetBandTRF(const int band); @@ -259,47 +466,253 @@ class LIME_API LMS7002M */ int GetBandTRF(void); + /*! + * @brief Sets the antenna to use on the device + * @param direction The direction to set the antenna for. + * @param channel The channel to set the antenna for. + * @param path The index of the antenna to use. + * @return The status of the operation + */ OpStatus SetPath(TRXDir direction, uint8_t channel, uint8_t path); + /*! + * @brief Sets the reference clock of the SX + * @param dir Rx/Tx module selection + * @param freq_Hz The frequency to set the reference clock to (in Hz) + * @return The status of the operation + */ OpStatus SetReferenceClk_SX(TRXDir dir, float_type freq_Hz); + + /*! + * @brief Returns reference clock in Hz used for SXT or SXR. + * @param dir transmitter or receiver selection. + * @return The reference clock speed (in Hz). + */ float_type GetReferenceClk_SX(TRXDir dir); + + /*! + * Returns the curernt frequency of the clock generator. + * @return Current CLKGEN frequency in Hz. + * Returned frequency depends on reference clock used for Receiver. + */ float_type GetFrequencyCGEN(); + + /*! + * @brief Sets CLKGEN frequency, calculations use receiver'r reference clock + * @param freq_Hz desired frequency in Hz + * @param retainNCOfrequencies recalculate NCO coefficients to keep currently set frequencies + * @param output if not null outputs calculated CGEN parameters + * @return The status of the operation + */ OpStatus SetFrequencyCGEN(float_type freq_Hz, const bool retainNCOfrequencies = false, CGEN_details* output = nullptr); + + /*! + * @brief Gets whether the VCO comparators of the clock generator are locked or not. + * @return A value indicating whether the VCO comparators of the clock generator are locked or not. + */ bool GetCGENLocked(void); + + /*! + * @brief Returns currently set SXR/SXT frequency + * @param dir Rx/Tx module selection + * @return SX frequency Hz + */ float_type GetFrequencySX(TRXDir dir); + + /*! + * @brief Sets SX frequency + * @param dir Rx/Tx module selection + * @param freq_Hz desired frequency in Hz + * @param output if not null outputs intermediate calculation values + * @return The status of the operation + */ OpStatus SetFrequencySX(TRXDir dir, float_type freq_Hz, SX_details* output = nullptr); + + /*! + * @brief Sets SX frequency with Reference clock spur cancelation + * @param dir Rx/Tx module selection + * @param freq_Hz desired frequency in Hz + * @param BW The bandwidth (in Hz) + * @return The status of the operation + */ OpStatus SetFrequencySXWithSpurCancelation(TRXDir dir, float_type freq_Hz, float_type BW); + + /*! + * @brief Gets whether the VCO comparators of the LO synthesizer are locked or not. + * @param dir The direction to read from. + * @return A value indicating whether the VCO comparators of the clock generator are locked or not. + */ bool GetSXLocked(TRXDir dir); ///VCO modules available for tuning enum class VCO_Module : uint8_t { VCO_CGEN, VCO_SXR, VCO_SXT }; + + /*! + * @brief Performs VCO tuning operations for CLKGEN + * @return The status of the operation + */ OpStatus TuneCGENVCO(); + + /*! + * @brief Performs VCO tuning operations for CLKGEN, SXR, SXT modules + * @param module module selection for tuning 0-cgen, 1-SXR, 2-SXT + * @return The status of the operation + */ OpStatus TuneVCO(VCO_Module module); + /*! + * @brief Loads given DC_REG values into registers + * @param dir TxTSP or RxTSP selection + * @param I DC_REG I value + * @param Q DC_REG Q value + * @return The status of the operation + */ OpStatus LoadDC_REG_IQ(TRXDir dir, int16_t I, int16_t Q); + + /*! + * @brief Sets chosen NCO's frequency + * @param dir transmitter or receiver selection + * @param index NCO index from 0 to 15 + * @param freq_Hz desired NCO frequency + * @return The status of the operation + */ OpStatus SetNCOFrequency(TRXDir dir, uint8_t index, float_type freq_Hz); + + /*! + * @brief Returns chosen NCO's frequency in Hz + * @param dir transmitter or receiver selection + * @param index NCO index from 0 to 15 + * @param fromChip read frequency directly from chip or local registers + * @return NCO frequency in Hz + */ float_type GetNCOFrequency(TRXDir dir, uint8_t index, bool fromChip = true); - OpStatus SetNCOPhaseOffsetForMode0(TRXDir dir, float_type angle_Deg); - OpStatus SetNCOPhaseOffset(TRXDir dir, uint8_t index, float_type angle_Deg); + + /*! + * @brief Sets chosen NCO phase offset angle when memory table MODE is 0 + * @param dir transmitter or receiver selection + * @param angle_deg phase offset angle in degrees + * @return The status of the operation + */ + OpStatus SetNCOPhaseOffsetForMode0(TRXDir dir, float_type angle_deg); + + /*! + * @brief Sets chosen NCO's phase offset angle + * @param dir transmitter or receiver selection + * @param index PHO index from 0 to 15 + * @param angle_deg phase offset angle in degrees + * @return The status of the operation + */ + OpStatus SetNCOPhaseOffset(TRXDir dir, uint8_t index, float_type angle_deg); + + /*! + * @brief Returns chosen NCO's phase offset angle in radians + * @param dir transmitter or receiver selection + * @param index PHO index from 0 to 15 + * @return phase offset angle in degrees + */ float_type GetNCOPhaseOffset_Deg(TRXDir dir, uint8_t index); + + /*! + * @brief Returns TSP reference frequency + * @param dir TxTSP or RxTSP selection + * @return TSP reference frequency in Hz + */ float_type GetReferenceClk_TSP(TRXDir dir); + /*! + * @brief Returns currently loaded FIR coefficients. + * @param dir Transmitter or receiver selection. + * @param gfirIndex GFIR index from 0 to 2. + * @param coef Array of returned coefficients (normalized from -1 to 1) + * @param coefCount Number of coefficients to read. + * @return The status of the operation. + */ OpStatus GetGFIRCoefficients(TRXDir dir, uint8_t gfirIndex, float_type* coef, uint8_t coefCount); + + /*! + * @brief Uploads given FIR coefficients to chip + * @param dir Transmitter or receiver selection + * @param gfirIndex GFIR index from 0 to 2 + * @param coef array of coefficients (normalized from -1 to 1) + * @param coefCount number of coefficients + * @return The status of the operation + * + * This function does not change GFIR*_L or GFIR*_N parameters, they have to be set manually + */ OpStatus SetGFIRCoefficients(TRXDir dir, uint8_t gfirIndex, const float_type* coef, uint8_t coefCount); - OpStatus SetGFIRFilter(TRXDir dir, unsigned ch, bool enabled, double bandwidth); + /*! + * @brief Sets up the GFIR filter. + * @param dir The direction for which to set up the filter. + * @param ch The channel for which to set up the filter + * @param enabled Whether to enable or disable the GFIR filter + * @param bandwidth The bandwidth (in Hz) to set the filter for. + * @return The status of the operation + */ + OpStatus SetGFIRFilter(TRXDir dir, Channel ch, bool enabled, double bandwidth); + + /*! + * @brief Sets the frequencies of the NCO. + * @param dir The direction for which to set the frequencies. + * @param freq_Hz The frequency array to set. + * @param count The amount of frequencies to set (max 16) + * @param phaseOffset The phase offset of the NCO to set. + * @return The status of the operation + */ OpStatus SetNCOFrequencies(TRXDir dir, const float_type* freq_Hz, uint8_t count, float_type phaseOffset); + /*! + * @brief Gets the current frequencies of the NCO. + * @param dir The direction to receive the frequencies of. + * @param phaseOffset Returns the phase offset in here if it's not nullptr. + * @return The frequencies of the NCO (in Hz). + */ std::vector GetNCOFrequencies(TRXDir dir, float_type* phaseOffset = nullptr); + + /*! + * @brief Sets the phases of the NCO (up to 16 values) + * @param dir The direction to which to set the phases. + * @param angles_deg The angles of the NCO to set (in degrees). + * @param count The amount of angles to set (max 16) + * @param frequencyOffset The offset of the NCO (in Hz). + * @return The status of the operation + */ OpStatus SetNCOPhases(TRXDir dir, const float_type* angles_deg, uint8_t count, float_type frequencyOffset); + /*! + * @brief Gets the current phases of the NCO (currently returns an empty vector) + * @param dir The direction from which to get the phases. + * @param frequencyOffset The offset of the NCO (in Hz). + * @return The phases of the NCO (in degrees) + */ std::vector GetNCOPhases(TRXDir dir, float_type* frequencyOffset = nullptr); + /*! + * @brief Configures interfaces for desired frequency + * Sets interpolation and decimation, changes MCLK sources and TSP clock dividers accordingly to selected interpolation and decimation + * @param cgen_freq_Hz The clock frequency to set (in Hz) + * @param interpolation The HBI interpolation ratio (actual ratio is pow(2, interpolation + 1), 7 for bypass) + * @param decimation The HBD decimation ratio (actual ratio is pow(2, decimation + 1), 7 for bypass) + * @return The status of the operation + */ OpStatus SetInterfaceFrequency(float_type cgen_freq_Hz, const uint8_t interpolation, const uint8_t decimation); + /*! + * @brief Gets the current sample rate of the specified channel. + * @param dir The direction for which to get the sample rate. + * @param ch The channel from which to get the sample rate. + * @return The current sample rate (in Hz) + */ float_type GetSampleRate(TRXDir dir, Channel ch); + + /*! + * @brief Gets the current sample rate of the currently active channel. + * @param dir The direction for which to get the sample rate. + * @return The current sample rate (in Hz) + */ float_type GetSampleRate(TRXDir dir); + /// @brief The sample source for the LML. enum class LMLSampleSource : uint8_t { AI, AQ, @@ -308,26 +721,40 @@ class LIME_API LMS7002M }; /*! - * Set the LML sample positions in the RF to baseband direction. + * @brief Set the LML sample positions in the RF to baseband direction. + * @param s0 + * @param s1 + * @param s2 + * @param s3 */ void ConfigureLML_RF2BB(const LMLSampleSource s0, const LMLSampleSource s1, const LMLSampleSource s2, const LMLSampleSource s3); /*! - * Set the LML sample positions in the baseband to RF direction. + * @brief Set the LML sample positions in the baseband to RF direction. + * @param s0 + * @param s1 + * @param s2 + * @param s3 */ void ConfigureLML_BB2RF(const LMLSampleSource s0, const LMLSampleSource s1, const LMLSampleSource s2, const LMLSampleSource s3); + /*! + * @brief Enables or disables the DC corrector bypass + * @param enable Enables or disables the + * @return The status of the operation + */ OpStatus SetRxDCRemoval(const bool enable); /*! * Get the RX DC removal filter enabled. + * @return Whether the DC corrector bypass is enabled or not. */ bool GetRxDCRemoval(void); /*! * Enables/disables TDD mode - * @param enable true - use same pll for Tx and Rx, false - us seperate PLLs - * @return 0 for success for error condition + * @param enable true - use same PLL for Tx and Rx, false - us seperate PLLs + * @return The status of the operation */ OpStatus EnableSXTDD(bool enable); @@ -336,7 +763,7 @@ class LIME_API LMS7002M * @param dir true for tx, false for rx * @param I the real adjustment [+1.0, -1.0] * @param Q the imaginary adjustment [+1.0, -1.0] - * @return 0 for success for error condition + * @return The status of the operation */ OpStatus SetDCOffset(TRXDir dir, const float_type I, const float_type Q); @@ -354,6 +781,7 @@ class LIME_API LMS7002M * @param phase the phase adjustment [+pi, -pi] * @param gainI the real gain adjustment [+1.0, 0.0] * @param gainQ the imaginary gain adjustment [+1.0, 0.0] + * @return The status of the operation */ OpStatus SetIQBalance(const TRXDir dir, const float_type phase, const float_type gainI, const float_type gainQ); @@ -366,11 +794,91 @@ class LIME_API LMS7002M */ void GetIQBalance(const TRXDir dir, float_type& phase, float_type& gainI, float_type& gainQ); - double GetClockFreq(ClockID clk_id, uint8_t channel); - OpStatus SetClockFreq(ClockID clk_id, double freq, uint8_t channel); + /*! + * @brief Gets the frequency of the selected clock. + * @param clk_id The enumerator value of the clock to get. + * @return The current frequency of that clock (in Hz). + */ + double GetClockFreq(ClockID clk_id); + + /*! + * @brief Sets the frequency of the selected clock. + * @param clk_id The enumerator value of the clock to set. + * @param freq The frequency (in Hz) to set the clock to. + * @return The operation success status. + */ + OpStatus SetClockFreq(ClockID clk_id, double freq); + + /*! + * @brief Modifies the defaults of this instance's Register map. + * @param registerValues A vector of address value pairs to modify the defaults to. + */ + void ModifyRegistersDefaults(const std::vector>& registerValues); + + /*! + * @brief Sets whether a local registers cache is being used or not. + * @param enabled Whether to enable the cache or not. + */ + void EnableValuesCache(bool enabled = true); + + /*! + * @brief Returns whether register value caching on the host is enabled or not. + * @return True - cache is being used, false - device values only. + */ + bool IsValuesCacheEnabled() const; + + /*! + * @brief Gets the class to control the MCU on the chip. + * @return A pointer to the class responsible for controlling the MCU. + */ + MCU_BD* GetMCUControls() const; + + /*! + * @brief Gets the of the chip + * @return The current temperature of the chip (in degrees Celsius) + */ + float_type GetTemperature(); + + /// @brief The available log types on the LMS7002M + enum class LogType : uint8_t { LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DATA }; + /*! + * @brief Sets the function to call for any logs to be written to. + * @param callback The function to call on LMS7002M logs + */ + void SetLogCallback(std::function callback); + + /*! + * @brief Batches multiple register writes into least amount of transactions + * @param spiAddr spi register addresses to be written + * @param spiData registers data to be written + * @param cnt number of registers to write + * @param toChip force write to chip + * @return The status of the operation + */ + OpStatus SPI_write_batch(const uint16_t* spiAddr, const uint16_t* spiData, uint16_t cnt, bool toChip = false); + + /*! + * @brief Batches multiple register reads into least amount of transactions + * @param spiAddr SPI addresses to read + * @param spiData array for read data + * @param cnt number of registers to read + * @return The status of the operation + */ + OpStatus SPI_read_batch(const uint16_t* spiAddr, uint16_t* spiData, uint16_t cnt); + + /// @brief The clock generator change callback type + typedef OpStatus (*CGENChangeCallbackType)(void* userData); + + /*! + * @brief Sets the function that gets executed when the CGEN changes. + * @param callback The function to call on CGEN change. + * @param userData The data to pass to the function being called. + */ + void SetOnCGENChangeCallback(CGENChangeCallbackType callback, void* userData = nullptr); + private: ///enumeration to indicate module registers intervals - enum MemorySection { + enum class MemorySection : uint8_t { LimeLight = 0, EN_DIR, AFE, @@ -403,36 +911,20 @@ class LIME_API LMS7002M RSSI_DC_CALIBRATION, RSSI_PDET_TEMP_CONFIG, RSSI_DC_CONFIG, - MEMORY_SECTIONS_COUNT }; - virtual OpStatus SetDefaults(MemorySection module); - void ModifyRegistersDefaults(const std::vector>& registerValues); - - static float_type gVCO_frequency_table[3][2]; - static float_type gCGEN_VCO_frequencies[2]; - void EnableValuesCache(bool enabled = true); - bool IsValuesCacheEnabled(); - MCU_BD* GetMCUControls() const; - void EnableCalibrationByMCU(bool enabled); - float_type GetTemperature(); + /*! + * @brief Sets given module registers to default values + * @return The status of the operation + */ + virtual OpStatus SetDefaults(MemorySection module); - enum class LogType : uint8_t { LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DATA }; - void SetLogCallback(std::function callback); LMS7002M_RegistersMap* BackupRegisterMap(void); void RestoreRegisterMap(LMS7002M_RegistersMap* backup); - OpStatus SPI_write_batch(const uint16_t* spiAddr, const uint16_t* spiData, uint16_t cnt, bool toChip = false); - OpStatus SPI_read_batch(const uint16_t* spiAddr, uint16_t* spiData, uint16_t cnt); - - typedef OpStatus (*CGENChangeCallbackType)(void* userData); - void SetOnCGENChangeCallback(CGENChangeCallbackType callback, void* userData = nullptr); - - protected: CGENChangeCallbackType mCallback_onCGENChange; void* mCallback_onCGENChange_userData; - bool mCalibrationByMCU; MCU_BD* mcuControl; bool useCache; LMS7002M_RegistersMap* mRegistersMap; @@ -444,9 +936,11 @@ class LIME_API LMS7002M static const std::vector readOnlyRegisters; - std::array, MEMORY_SECTIONS_COUNT> MemorySectionAddresses; + static const std::map> MemorySectionAddresses; + static const std::array, 3> gVCO_frequency_table; + static const std::array gCGEN_VCO_frequencies; - uint32_t GetRSSI(RSSI_measurements* measurements = nullptr); + uint32_t GetRSSI(); void SetRxDCOFF(int8_t offsetI, int8_t offsetQ); OpStatus CalibrateTxGainSetup(); diff --git a/src/include/limesuite/LMS7002M_parameters.h b/src/include/limesuite/LMS7002M_parameters.h index 42dc75bfc..afd433bb4 100644 --- a/src/include/limesuite/LMS7002M_parameters.h +++ b/src/include/limesuite/LMS7002M_parameters.h @@ -19,12 +19,12 @@ extern "C" { /** @brief Structure defining a LMS7002M parameter. */ struct LMS7Parameter { - uint16_t address; - uint8_t msb; - uint8_t lsb; - uint16_t defaultValue; - const char* name; - const char* tooltip; + uint16_t address; ///< The address of the parameter + uint8_t msb; ///< The index of the most significant bit of the parameter. + uint8_t lsb; ///< The index of the least significant bit of the paramerer. + uint16_t defaultValue; ///< The default value of the parameter. + const char* name; ///< The name of the parameter. + const char* tooltip; ///< The tooltip of the parameter. }; static const struct LMS7Parameter LMS7_LRST_TX_B = { 0x0020, 15, 15, 1, "LRST_TX_B", "Resets all the logic registers to the default state for Tx MIMO channel B" }; diff --git a/src/include/limesuite/OpStatus.h b/src/include/limesuite/OpStatus.h index 78f107afa..902946d48 100644 --- a/src/include/limesuite/OpStatus.h +++ b/src/include/limesuite/OpStatus.h @@ -5,6 +5,7 @@ namespace lime { +/// @brief The possible status codes from operations. enum class OpStatus { SUCCESS = 0, ERROR = -1, @@ -19,6 +20,9 @@ enum class OpStatus { ABORTED = -10, }; +/// @brief Converts a given OpStatus value into a human readable C-string. +/// @param value The value to convert. +/// @return The C-string representing the status. const char* ToCString(OpStatus value); } // namespace lime diff --git a/src/include/limesuite/SDRDevice.cpp b/src/include/limesuite/SDRDevice.cpp index 46d26099e..dfede23b0 100644 --- a/src/include/limesuite/SDRDevice.cpp +++ b/src/include/limesuite/SDRDevice.cpp @@ -6,15 +6,17 @@ using namespace lime; SDRDevice::StreamConfig::Extras::Extras() : usePoll{ true } - , rxSamplesInPacket{ 0 } - , rxPacketsInBatch{ 0 } - , txMaxPacketsInBatch{ 0 } - , txSamplesInPacket{ 0 } , negateQ{ false } , waitPPS{ false } { } +SDRDevice::StreamConfig::Extras::PacketTransmission::PacketTransmission() + : samplesInPacket{ 0 } + , packetsInBatch{ 0 } +{ +} + SDRDevice::StreamConfig::StreamConfig() : format{ DataFormat::I16 } , linkFormat{ DataFormat::I16 } diff --git a/src/include/limesuite/SDRDevice.h b/src/include/limesuite/SDRDevice.h index d79204a71..6f7d01701 100644 --- a/src/include/limesuite/SDRDevice.h +++ b/src/include/limesuite/SDRDevice.h @@ -22,59 +22,66 @@ namespace lime { struct DeviceNode; class OEMTestReporter; -/// SDRDevice can have multiple modules (RF chips), that can operate independently +/// @brief Class for holding information about an SDR (Software Defined Radio) device. +/// SDRDevice can have multiple modules (RF chips), that can operate independently. class LIME_API SDRDevice { public: - static constexpr uint8_t MAX_CHANNEL_COUNT = 16; - static constexpr uint8_t MAX_RFSOC_COUNT = 16; + static constexpr uint8_t MAX_CHANNEL_COUNT = 16; ///< Maximum amount of channels an SDR Device can hold + static constexpr uint8_t MAX_RFSOC_COUNT = 16; ///< Maximum amount of Radio-Frequency System-on-Chips + /// @brief Enumerator to define the log level of a log message. enum class LogLevel : uint8_t { CRITICAL, ERROR, WARNING, INFO, VERBOSE, DEBUG }; - typedef void (*DataCallbackType)(bool, const uint8_t*, const uint32_t); - typedef void (*LogCallbackType)(LogLevel, const char*); - - typedef std::map SlaveNameIds_t; + /// @brief Information about possible gain values. struct GainValue { - uint16_t hardwareRegisterValue; - float actualGainValue; + uint16_t hardwareRegisterValue; ///< The value that is written to the hardware + float actualGainValue; ///< The actual meaning of the value (in dB) }; + /// @brief General information about the Radio-Frequency System-on-Chip (RFSoC). struct RFSOCDescriptor { - std::string name; - uint8_t channelCount; - std::unordered_map> pathNames; + std::string name; ///< The name of the system + uint8_t channelCount; ///< The available channel count of the system + std::unordered_map> pathNames; ///< The available antenna names - Range frequencyRange; - Range samplingRateRange; + Range frequencyRange; ///< Deliverable frequency capabilities of the device + Range samplingRateRange; ///< Sampling rate capabilities of the device - std::unordered_map> antennaRange; - std::unordered_map lowPassFilterRange; + std::unordered_map> antennaRange; ///< Antenna recommended bandwidths + std::unordered_map lowPassFilterRange; ///< The ranges of the low pass filter - std::unordered_map> gains; - std::unordered_map> gainRange; - std::unordered_map>> gainValues; + std::unordered_map> gains; ///< The types of gains available + std::unordered_map> gainRange; ///< The available ranges of each gain + std::unordered_map>> gainValues; ///< The possible gain values }; + /// @brief Structure for the information of a custom parameter. struct CustomParameter { - std::string name; - int32_t id; - int32_t minValue; - int32_t maxValue; - bool readOnly; + std::string name; ///< The name of the custom parameter + int32_t id; ///< The identifier of the custom parameter + int32_t minValue; ///< The minimum possible value of the custom parameter + int32_t maxValue; ///< The maximum possible value of the custom parameter + bool readOnly; ///< Denotes whether this value is read only or not }; + /// @brief Structure for storing the information of a memory region. struct Region { - int32_t address; - int32_t size; + int32_t address; ///< Starting address of the memory region + int32_t size; ///< The size of the memory region }; + /// @brief Describes a data storage of a certain type a device holds. struct DataStorage { - SDRDevice* ownerDevice; - eMemoryDevice memoryDeviceType; - std::unordered_map regions; - + SDRDevice* ownerDevice; ///< Pointer to the device that actually owns the data storage + eMemoryDevice memoryDeviceType; ///< The type of memory being described + std::unordered_map regions; ///< The documented memory regions of the data storage + + /// @brief Constructs a new Data Storage object + /// @param device The device this storage belongs to. + /// @param type The type of memory being described in this object. + /// @param regions The memory regions this memory contains. DataStorage(SDRDevice* device = nullptr, eMemoryDevice type = eMemoryDevice::COUNT, std::unordered_map regions = {}) @@ -85,108 +92,145 @@ class LIME_API SDRDevice } }; - /** @brief General information about device internals, static capabilities. */ + /// @brief General information about device internals, static capabilities. struct Descriptor { - std::string name; /// The displayable name for the device + std::string name; ///< The displayable name for the device /*! The displayable name for the expansion card - * Ex: if the RFIC is on a daughter-card + * Ex: if the RFIC is on a daughter-card. */ std::string expansionName; - std::string firmwareVersion; /// The firmware version as a string - std::string gatewareVersion; /// Gateware version as a string - std::string gatewareRevision; /// Gateware revision as a string - std::string gatewareTargetBoard; /// Which board should use this gateware - std::string hardwareVersion; /// The hardware version as a string - std::string protocolVersion; /// The protocol version as a string - uint64_t serialNumber{ 0 }; /// A unique board serial number - - SlaveNameIds_t spiSlaveIds; // names and SPI bus numbers of internal chips - std::vector rfSOC; - std::vector customParameters; + std::string firmwareVersion; ///< The firmware version as a string + std::string gatewareVersion; ///< Gateware version as a string + std::string gatewareRevision; ///< Gateware revision as a string + std::string gatewareTargetBoard; ///< Which board should use this gateware + std::string hardwareVersion; ///< The hardware version as a string + std::string protocolVersion; ///< The protocol version as a string + uint64_t serialNumber{ 0 }; ///< A unique board serial number + + std::map spiSlaveIds; ///< Names and SPI bus numbers of internal chips + std::vector rfSOC; ///< Descriptors of all RFSoC devices within this device + std::vector customParameters; ///< Descriptions of all custom parameters of this device + /** Descriptions of all memory storage devices on this device */ std::map> memoryDevices; - std::shared_ptr socTree; + std::shared_ptr socTree; ///< The device's subdevices tree view representation - static const char DEVICE_NUMBER_SEPARATOR_SYMBOL; - static const char PATH_SEPARATOR_SYMBOL; + static const char DEVICE_NUMBER_SEPARATOR_SYMBOL; ///< The symbol used to separate the device's name from its number + static const char PATH_SEPARATOR_SYMBOL; ///< The symbol that separates the device's name from its parent's name }; + /// @brief Structure for holding the statistics of a stream struct StreamStats { + /// @brief Structure for storing the first in first out queue statistics struct FIFOStats { - std::size_t totalCount; - std::size_t usedCount; + std::size_t totalCount; ///< The total amount of samples that can be in the FIFO queue. + std::size_t usedCount; ///< The amount of samples that is currently in the FIFO queue. - float ratio() const { return static_cast(usedCount) / totalCount; } + /// @brief Gets the ratio of the amount of FIFO filled up. + /// @return The amount of FIFO filled up (0 - completely empty, 1 - completely full). + constexpr float ratio() const { return static_cast(usedCount) / totalCount; } }; - StreamStats() { memset(this, 0, sizeof(StreamStats)); } - uint64_t timestamp; - int64_t bytesTransferred; - int64_t packets; - FIFOStats FIFO; - float dataRate_Bps; - uint32_t overrun; - uint32_t underrun; - uint32_t loss; - uint32_t late; + StreamStats() { std::memset(this, 0, sizeof(StreamStats)); } + uint64_t timestamp; ///< The current timestamp of the stream. + int64_t bytesTransferred; ///< The total amount of bytes transferred. + int64_t packets; ///< The total amount of packets transferred. + FIFOStats FIFO; ///< The status of the FIFO queue. + float dataRate_Bps; ///< The current data transmission rate. + uint32_t overrun; ///< The amount of packets overrun. + uint32_t underrun; ///< The amount of packets underrun. + uint32_t loss; ///< The amount of packets that are lost. + uint32_t late; ///< The amount of packets that arrived late for transmitting and were dropped. }; + /// @brief Describes the status of a global positioning system. struct GPS_Lock { + /// @brief Enumerator describing the possible status of a positioning system. enum class LockStatus : uint8_t { Undefined, NotAvailable, Has2D, Has3D }; - LockStatus galileo; - LockStatus beidou; - LockStatus glonass; - LockStatus gps; + LockStatus galileo; ///< Status for the Galileo system (European system). + LockStatus beidou; ///< Status for the BeiDou system (Chinese system). + LockStatus glonass; ///< Status for the GLONASS system (Russian system). + LockStatus gps; ///< Status for the GPS system (American system). }; - // channels order and data transmission formats setup + /// @brief Configuration settings for a stream. struct LIME_API StreamConfig { + /// @brief Extra configuration settings for a stream. struct Extras { + /// @brief The settings structure for a packet transmission. + struct PacketTransmission { + PacketTransmission(); + + uint16_t samplesInPacket; ///< The amount of samples to transfer in a single packet. + uint32_t packetsInBatch; ///< The amount of packets to send in a single transfer. + }; + Extras(); - bool usePoll; - uint16_t rxSamplesInPacket; - uint32_t rxPacketsInBatch; - uint32_t txMaxPacketsInBatch; - uint16_t txSamplesInPacket; - bool negateQ; - bool waitPPS; // start sampling from next following PPS + bool usePoll; ///< Whether to use a polling strategy for PCIe devices. + + PacketTransmission rx; ///< Configuration of the receive transfer direction. + PacketTransmission tx; ///< Configuration of the transmit transfer direction. + + bool negateQ; ///< Whether to negate the Q element before sending the data or not. + bool waitPPS; ///< Start sampling from next following PPS. }; + + /// @brief The definition of the function that gets called whenever a stream status changes. typedef bool (*StatusCallbackFunc)(bool isTx, const StreamStats* stats, void* userData); + + /// @brief Enumerator describing the data format. enum class DataFormat : uint8_t { - I16, - I12, - F32, + I16, ///< Data is in a form of two 16-bit integers. + I12, ///< Data is in a form of two 12-bit integers. + F32, ///< Data is in a form of two 32-bit floating-point values. }; StreamConfig(); - std::unordered_map> channels; + std::unordered_map> channels; ///< The channels to set up for the stream. - DataFormat format; // samples format used for Read/Write functions - DataFormat linkFormat; // samples format used in transport layer Host<->FPGA + DataFormat format; ///< Samples format used for Read/Write functions + DataFormat linkFormat; ///< Samples format used in transport layer Host<->FPGA - /// memory size to allocate for each channel buffering - /// Default: 0 - allow to decide internally + /// @brief Memory size to allocate for each channel buffering. + /// Default: 0 - allow to decide internally. uint32_t bufferSize; - /// optional: expected sampling rate for data transfer optimizations. - /// Default: 0 - deicide internally + /// Optional: expected sampling rate for data transfer optimizations (in Hz). + /// Default: 0 - decide internally. float hintSampleRate; - bool alignPhase; // attempt to do phases alignment between paired channels + bool alignPhase; ///< Attempt to do phases alignment between paired channels - StatusCallbackFunc statusCallback; - void* userData; // will be supplied to statusCallback + StatusCallbackFunc statusCallback; ///< Function to call on a status change. + void* userData; ///< Data that will be supplied to statusCallback // TODO: callback for drops and errors - Extras extraConfig; + Extras extraConfig; ///< Extra stream configuration settings. }; + /// @brief The metadata of a stream packet. struct StreamMeta { - int64_t timestamp = 0; - bool useTimestamp = false; - bool flush = false; // submit data to hardware without waiting for full buffer + /** + * Timestamp is a value of HW counter with a tick based on sample rate. + * In RX: time when the first sample in the returned buffer was received. + * In TX: time when the first sample in the submitted buffer should be send. + */ + uint64_t timestamp; + + /** + * In RX: not used/ignored. + * In TX: wait for the specified HW timestamp before broadcasting data over the air. + */ + bool waitForTimestamp; + + /** + * In RX: not used/ignored. + * In TX: send samples to HW even if packet is not completely filled (end TX burst). + */ + bool flushPartialPacket; }; + /// @brief Configuration of a single channel. struct ChannelConfig { ChannelConfig() : rx() @@ -194,6 +238,7 @@ class LIME_API SDRDevice { } + /// @brief Configuration for a direction in a channel. struct Direction { Direction() : centerFrequency(0) @@ -209,21 +254,31 @@ class LIME_API SDRDevice { } + /// @brief Configuration of a general finite impulse response (FIR) filter. struct GFIRFilter { - bool enabled; - double bandwidth; + bool enabled; ///< Whether the filter is enabled or not. + double bandwidth; ///< The bandwidth of the filter (in Hz). }; + /// @brief The structure holding the status of the test signal the device can produce. struct TestSignal { + /// @brief The enumeration describing the divide mode of the test signal. enum class Divide : uint8_t { Div8, Div4 }; - enum class Scale : uint8_t { Full, Half }; - complex16_t dcValue; - Divide divide; - Scale scale; - bool enabled; - bool dcMode; + /// @brief The enumeration describing the scale of the test signal. + enum class Scale : uint8_t { Full, Half }; + complex16_t dcValue; ///< The value to use when in DC mode. + Divide divide; ///< The current divide of the test signal. + Scale scale; ///< The current scale of the test signal. + bool enabled; ///< Denotes whether test mode is enabled or not. + bool dcMode; ///< The DC mode of the test mode. + + /// @brief The constructor for the Test Signal storage class. + /// @param enabled Whether the test signal is enabled or not. + /// @param dcMode Whether the DC mode is enabled or not. + /// @param divide The divide mode of the test signal. + /// @param scale The scale of the dest signal. TestSignal(bool enabled = false, bool dcMode = false, Divide divide = Divide::Div8, Scale scale = Scale::Half) : dcValue(0, 0) , divide(divide) @@ -234,19 +289,22 @@ class LIME_API SDRDevice } }; - double centerFrequency; - double NCOoffset; - double sampleRate; - std::unordered_map gain; - double lpf; - uint8_t path; - uint8_t oversample; - GFIRFilter gfir; - bool enabled; - bool calibrate; - TestSignal testSignal; + double centerFrequency; ///< The center frequency of the direction of this channel (in Hz). + double NCOoffset; ///< The offset from the channel's numerically controlled oscillator (NCO) (in Hz). + double sampleRate; ///< The sample rate of this direction of a channel (in Hz). + std::unordered_map gain; ///< The gains and their current values for this direction. + double lpf; ///< The bandwidth of the Low Pass Filter (LPF) (in Hz). + uint8_t path; ///< The antenna being used for this direction. + uint8_t oversample; ///< The oversample ratio of this direction. + GFIRFilter gfir; ///< The general finite impulse response (FIR) filter settings of this direction. + bool enabled; ///< Denotes whether this direction of a channel is enabled or not. + bool calibrate; ///< Denotes whether the device will be calibrated or not. + TestSignal testSignal; ///< Denotes whether the signal being sent is a test signal or not. }; + /// @brief Gets the reference to the direction settings. + /// @param direction The direction to get it for. + /// @return The reference to the direction. Direction& GetDirection(TRXDir direction) { switch (direction) @@ -258,6 +316,9 @@ class LIME_API SDRDevice } } + /// @brief Gets the const reference to the direction settings. + /// @param direction The direction to get it for. + /// @return The const reference to the direction. const Direction& GetDirection(TRXDir direction) const { switch (direction) @@ -269,58 +330,187 @@ class LIME_API SDRDevice } } - Direction rx; - Direction tx; + Direction rx; ///< Configuration settings for the Receive channel. + Direction tx; ///< Configuration settings for the Transmit channel. }; + /// @brief Configuration of an SDR device. struct SDRConfig { SDRConfig() : referenceClockFreq(0) , skipDefaults(false){}; - double referenceClockFreq; - ChannelConfig channel[MAX_CHANNEL_COUNT]; + double referenceClockFreq; ///< The reference clock frequency of the device. + ChannelConfig channel[MAX_CHANNEL_COUNT]; ///< The configuration settings for each of the channels. // Loopback setup? - bool skipDefaults; // skip default values initialization and write on top of current config + bool skipDefaults; ///< Skip default values initialization and write on top of current config. }; virtual ~SDRDevice(){}; + /// @brief Configures the device using the given configuration. + /// @param config The configuration to set up the device with. + /// @param moduleIndex The device index to configure. + /// @return The status of the operation. virtual OpStatus Configure(const SDRConfig& config, uint8_t moduleIndex) = 0; - /** @brief Returns SPI slave names and chip select IDs for use with SDRDevice::SPI() */ + /// @brief Gets the Descriptor of the SDR Device. + /// @return The Descriptor of the device. virtual const Descriptor& GetDescriptor() const = 0; + /// @brief Initializes the device with initial settings. + /// @return The success status of the initialization. virtual OpStatus Init() = 0; + + /// @brief Resets the device. + /// @return The status of the operation. virtual OpStatus Reset() = 0; + + /// @brief Gets the current status of the GPS locks. + /// @param status The pointer to which to output the GPS status. + /// @return The status of the operation. virtual OpStatus GetGPSLock(GPS_Lock* status) = 0; + /// @brief Enables or disables the specified channel. + /// @param moduleIndex The device index to configure. + /// @param trx The direction of the channel to configure. + /// @param channel The channel to configure. + /// @param enable Whether to enable the channel or not. + /// @return The status of the operation. virtual OpStatus EnableChannel(uint8_t moduleIndex, TRXDir trx, uint8_t channel, bool enable) = 0; + /// @brief Gets the frequency of a specified clock. + /// @param clk_id The clock ID to get the frequency of. + /// @param channel The channel to get the frequency of. + /// @return The frequency of the specified clock (in Hz). virtual double GetClockFreq(uint8_t clk_id, uint8_t channel) = 0; + + /// @brief Sets the frequency of a specified clock. + /// @param clk_id The clock ID to set the frequency of. + /// @param freq The new frequency of the specified clock (in Hz). + /// @param channel The channel to set the frequency of. + /// @return The status of the operation. virtual OpStatus SetClockFreq(uint8_t clk_id, double freq, uint8_t channel) = 0; + /// @brief Gets the current frequency of the given channel. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @param channel The channel to read from. + /// @return The current radio frequency of the channel (in Hz). virtual double GetFrequency(uint8_t moduleIndex, TRXDir trx, uint8_t channel) = 0; + + /// @brief Sets the radio frequency of the given channel. + /// @param moduleIndex The device index to configure. + /// @param trx The direction to configure. + /// @param channel The channel to configure. + /// @param frequency The frequency to set the channel to (in Hz). + /// @return The status of the operation. virtual OpStatus SetFrequency(uint8_t moduleIndex, TRXDir trx, uint8_t channel, double frequency) = 0; + /// @brief Gets the current frequency of the NCO. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @param channel The channel to read from. + /// @param index The index of the NCO to read from. + /// @return The current frequency of the NCO (in Hz) virtual double GetNCOFrequency(uint8_t moduleIndex, TRXDir trx, uint8_t channel, uint8_t index) = 0; + + /// @brief Sets the frequency and the phase angle of the NCO. + /// @param moduleIndex The device index to configure. + /// @param trx The direction to configure. + /// @param channel The channel to configure. + /// @param index The index of the NCO to use. + /// @param frequency The frequency of the NCO to set (in Hz). + /// @param phaseOffset Phase offset angle (in degrees) + /// @return The status of the operation. virtual OpStatus SetNCOFrequency( uint8_t moduleIndex, TRXDir trx, uint8_t channel, uint8_t index, double frequency, double phaseOffset = -1.0) = 0; + /// @brief Gets the current offset of the NCO compared to the main frequency. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @param channel The channel to read from. + /// @return The delta between the current device frequency and the current device NCO frequency (in Hz). virtual double GetNCOOffset(uint8_t moduleIndex, TRXDir trx, uint8_t channel) = 0; + /// @brief Gets the current sample rate of the device. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @param channel The channel to read from. + /// @return The currend device sample rate (in Hz) virtual double GetSampleRate(uint8_t moduleIndex, TRXDir trx, uint8_t channel) = 0; + + /// @brief Sets the sample rate of the device. + /// @param moduleIndex The device index to configure. + /// @param trx The direction to configure. + /// @param channel The channel to configure. + /// @param sampleRate The target sample rate (in Hz) + /// @param oversample The RF oversampling ratio. + /// @return The status of the operation. virtual OpStatus SetSampleRate(uint8_t moduleIndex, TRXDir trx, uint8_t channel, double sampleRate, uint8_t oversample) = 0; - virtual OpStatus SetGain(uint8_t moduleIndex, TRXDir direction, uint8_t channel, eGainTypes gain, double value) = 0; + /// @brief Gets the current value of the specified gain. + /// @param moduleIndex The device index to read from. + /// @param direction The direction to read from. + /// @param channel The channel to read from. + /// @param gain The type of gain to get the data of. + /// @param value The value of the gain (in dB). + /// @return The status code of the operation. virtual OpStatus GetGain(uint8_t moduleIndex, TRXDir direction, uint8_t channel, eGainTypes gain, double& value) = 0; + /// @brief Sets the gain level of a specified gain. + /// @param moduleIndex The device index to configure. + /// @param direction The direction to configure. + /// @param channel The channel to configure. + /// @param gain The type of gain to set. + /// @param value The amount of gain to set (in dB). + /// @return The status code of the operation. + virtual OpStatus SetGain(uint8_t moduleIndex, TRXDir direction, uint8_t channel, eGainTypes gain, double value) = 0; + + /// @brief Gets the current frequency of the Low Pass Filter. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @param channel The channel to read from. + /// @return The current frequency of the Low Pass Filter (in Hz). virtual double GetLowPassFilter(uint8_t moduleIndex, TRXDir trx, uint8_t channel) = 0; + + /// @brief Sets the Low Pass Filter to a specified frequency. + /// @param moduleIndex The device index to configure. + /// @param trx The direction to configure. + /// @param channel The channel to configure. + /// @param lpf The bandwidth of the Low Pass Filter to set it to (in Hz). + /// @return The status of the operation. virtual OpStatus SetLowPassFilter(uint8_t moduleIndex, TRXDir trx, uint8_t channel, double lpf) = 0; + /// @brief Gets the currently set antenna of the device. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @param channel The channel to read from. + /// @return The ID of the currently set antenna. virtual uint8_t GetAntenna(uint8_t moduleIndex, TRXDir trx, uint8_t channel) = 0; + + /// @brief Sets the current antenna of the device. + /// @param moduleIndex The device index to configure. + /// @param trx The direction to configure. + /// @param channel The channel to configure. + /// @param path The ID of the antenna to set the device to use. + /// @return The status of the operation. virtual OpStatus SetAntenna(uint8_t moduleIndex, TRXDir trx, uint8_t channel, uint8_t path) = 0; + /// @brief Gets the current status of the test signal mode. + /// @param moduleIndex The device index to read from. + /// @param direction The direction to read from. + /// @param channel The channel to read from. + /// @return The current status of the test signal mode. virtual ChannelConfig::Direction::TestSignal GetTestSignal(uint8_t moduleIndex, TRXDir direction, uint8_t channel) = 0; + + /// @brief Sets the test signal mode. + /// @param moduleIndex The device index to configure. + /// @param direction The direction to configure. + /// @param channel The channel to configure. + /// @param signalConfiguration The configuration of the test mode to set. + /// @param dc_i The I value of the test mode to send (0 for defaults) + /// @param dc_q The Q value of the test mode to send (0 for defaults) + /// @return The status of the operation. virtual OpStatus SetTestSignal(uint8_t moduleIndex, TRXDir direction, uint8_t channel, @@ -328,115 +518,339 @@ class LIME_API SDRDevice int16_t dc_i = 0, int16_t dc_q = 0) = 0; + /// @brief Gets if the DC corrector bypass is enabled or not. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @param channel The channel to read from. + /// @return Whether the DC corrector bypassis enabled or not (false = bypass the corrector, true = use the corrector) virtual bool GetDCOffsetMode(uint8_t moduleIndex, TRXDir trx, uint8_t channel) = 0; + + /// @brief Enables or disables the DC corrector bypass. + /// @param moduleIndex The device index to configure. + /// @param trx The direction to configure. + /// @param channel The channel to configure. + /// @param isAutomatic Whether to use the DC corrector bypass or not (false = bypass the corrector, true = use the corrector) + /// @return The status of the operation. virtual OpStatus SetDCOffsetMode(uint8_t moduleIndex, TRXDir trx, uint8_t channel, bool isAutomatic) = 0; + /// @brief Gets the DC I and Q corrector values. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @param channel The channel to read from. + /// @return The current DC I and Q corrector values. virtual complex64f_t GetDCOffset(uint8_t moduleIndex, TRXDir trx, uint8_t channel) = 0; + + /// @brief Sets the DC I and Q corrector values. + /// @param moduleIndex The device index to configure. + /// @param trx The direction to configure. + /// @param channel The channel to configure. + /// @param offset The offsets of the I and Q channels. + /// @return The status of the operation. virtual OpStatus SetDCOffset(uint8_t moduleIndex, TRXDir trx, uint8_t channel, const complex64f_t& offset) = 0; + /// @brief Gets the current I and Q gain corrector values. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @param channel The channel to read from. + /// @return The current I and Q gain corrector values. virtual complex64f_t GetIQBalance(uint8_t moduleIndex, TRXDir trx, uint8_t channel) = 0; + + /// @brief Sets the I and Q gain corrector values. + /// @param moduleIndex The device index to configure. + /// @param trx The direction to configure. + /// @param channel The channel to configure. + /// @param balance The I and Q corrector values to set. + /// @return The status of the operation. virtual OpStatus SetIQBalance(uint8_t moduleIndex, TRXDir trx, uint8_t channel, const complex64f_t& balance) = 0; + /// @brief Gets whether the VCO comparators of the clock generator are locked or not. + /// @param moduleIndex The device index to read from. + /// @return A value indicating whether the VCO comparators of the clock generator are locked or not. virtual bool GetCGENLocked(uint8_t moduleIndex) = 0; + + /// @brief Gets the temperature of the device. + /// @param moduleIndex The device index to get the temperature of. + /// @return The temperature of the device (in degrees Celsius) virtual double GetTemperature(uint8_t moduleIndex) = 0; + /// @brief Gets whether the VCO comparators of the LO synthesizer are locked or not. + /// @param moduleIndex The device index to read from. + /// @param trx The direction to read from. + /// @return A value indicating whether the VCO comparators of the clock generator are locked or not. virtual bool GetSXLocked(uint8_t moduleIndex, TRXDir trx) = 0; + /// @brief Reads the value of the given register. + /// @param moduleIndex The device index to read from. + /// @param address The memory address to read from. + /// @param useFPGA Whether to read memory from the FPGA or not. + /// @return The value read from the register. virtual unsigned int ReadRegister(uint8_t moduleIndex, unsigned int address, bool useFPGA = false) = 0; + + /// @brief Writes the given register value to the given address. + /// @param moduleIndex The device index to configure. + /// @param address The address of the memory to write to. + /// @param value The value to write to the device's memory. + /// @param useFPGA Whether to write to the FPGA or not (default false) + /// @return The status of the operation. virtual OpStatus WriteRegister(uint8_t moduleIndex, unsigned int address, unsigned int value, bool useFPGA = false) = 0; + /// @brief Loads the configuration of a device from a given file. + /// @param moduleIndex The device index to write the configuration into. + /// @param filename The file to read the data from. + /// @return The status of the operation. virtual OpStatus LoadConfig(uint8_t moduleIndex, const std::string& filename) = 0; + + /// @brief Saves the current configuration of the device into a given file. + /// @param moduleIndex The device index to save the data from. + /// @param filename The file to save the information to. + /// @return The status of the operation. virtual OpStatus SaveConfig(uint8_t moduleIndex, const std::string& filename) = 0; + /// @brief Gets the given parameter from the device. + /// @param moduleIndex The device index to configure. + /// @param channel The channel to configure. + /// @param parameterKey The key of the paremeter to read from. + /// @return The value read from the parameter. virtual uint16_t GetParameter(uint8_t moduleIndex, uint8_t channel, const std::string& parameterKey) = 0; + + /// @brief Sets the given parameter in the device. + /// @param moduleIndex The device index to configure. + /// @param channel The channel to configure. + /// @param parameterKey The key of the paremeter to write to. + /// @param value The value to write to the address. + /// @return The status of the operation. virtual OpStatus SetParameter(uint8_t moduleIndex, uint8_t channel, const std::string& parameterKey, uint16_t value) = 0; + /// @brief Gets the given parameter from the device. + /// @param moduleIndex The device index to get the data from. + /// @param channel The channel to get the data from. + /// @param address The memory address of the device to read. + /// @param msb The index of the most significant bit of the address to read. (16-bit register) + /// @param lsb The index of the least significant bit of the address to read. (16-bit register) + /// @return The value read from the parameter. virtual uint16_t GetParameter(uint8_t moduleIndex, uint8_t channel, uint16_t address, uint8_t msb, uint8_t lsb) = 0; + + /// @brief Sets the given parameter in the device. + /// @param moduleIndex The device index to configure. + /// @param channel The channel to configure. + /// @param address The memory address in the device to change. + /// @param msb The index of the most significant bit of the address to modify. (16-bit register) + /// @param lsb The index of the least significant bit of the address to modify. (16-bit register) + /// @param value The value to write to the address. + /// @return The status of the operation. virtual OpStatus SetParameter( uint8_t moduleIndex, uint8_t channel, uint16_t address, uint8_t msb, uint8_t lsb, uint16_t value) = 0; + /// @brief Calibrates the given channel for a given bandwidth. + /// @param moduleIndex The device index to configure. + /// @param trx The direction of the channel to configure. + /// @param channel The channel to configure. + /// @param bandwidth The bandwidth of the channel to calibrate for (in Hz). + /// @return The status of the operation. virtual OpStatus Calibrate(uint8_t moduleIndex, TRXDir trx, uint8_t channel, double bandwidth) = 0; + + /// @brief Configures the GFIR with the settings. + /// @param moduleIndex The device index to configure. + /// @param trx The direction of the channel to configure. + /// @param channel The channel to configure. + /// @param settings The settings of the GFIR to set. + /// @return The status of the operation. virtual OpStatus ConfigureGFIR( uint8_t moduleIndex, TRXDir trx, uint8_t channel, ChannelConfig::Direction::GFIRFilter settings) = 0; + /// @brief Gets the current coefficients of a GFIR. + /// @param moduleIndex The device index to get the coefficients from. + /// @param trx The direction of the channel to get the data from. + /// @param channel The channel to get the data from. + /// @param gfirID The ID of the GFIR to get the coefficients from. + /// @return The current coefficients (normalized in the range [-1; 1]) of the GFIR. virtual std::vector GetGFIRCoefficients(uint8_t moduleIndex, TRXDir trx, uint8_t channel, uint8_t gfirID) = 0; + + /// @brief Sets the coefficients of a given GFIR + /// @param moduleIndex The device index to configure. + /// @param trx The direction of the channel to configure. + /// @param channel The channel to set the filter of. + /// @param gfirID The ID of the GFIR to set. + /// @param coefficients The coefficients (normalized in the range [-1; 1]) to set the GFIR to. + /// @return The status of the operation. virtual OpStatus SetGFIRCoefficients( uint8_t moduleIndex, TRXDir trx, uint8_t channel, uint8_t gfirID, std::vector coefficients) = 0; + + /// @brief Sets the GFIR to use. + /// @param moduleIndex The device index to configure. + /// @param trx The direction of the channel to configure. + /// @param channel The channel to set the filter of. + /// @param gfirID The ID of the GFIR to set. + /// @param enabled Whether the specifed GFIR should be enabled or disabled. + /// @return The status of the operation. virtual OpStatus SetGFIR(uint8_t moduleIndex, TRXDir trx, uint8_t channel, uint8_t gfirID, bool enabled) = 0; + /// @brief Synchronizes the cached changed register values on the host with the real values on the device. + /// @param toChip The direction in which to synchronize (true = uploads to the device). + /// @return The status of the operation. virtual OpStatus Synchronize(bool toChip) = 0; + + /// @brief Enable or disable register value caching on the host side. + /// @param enable Whether to enable or disable the register value caching (true = enabled). virtual void EnableCache(bool enable) = 0; + /// @brief Gets the hardware timestamp with the applied offset. + /// @param moduleIndex The device index to configure. + /// @return The current timestamp of the hardware. virtual uint64_t GetHardwareTimestamp(uint8_t moduleIndex) = 0; + + /// @brief Sets the hardware timestamp to the provided one by applying a constant offset. + /// @param moduleIndex The device index to configure. + /// @param now What the definition of the current time should be. + /// @return The status of the operation. virtual OpStatus SetHardwareTimestamp(uint8_t moduleIndex, const uint64_t now) = 0; + /// @brief Sets up all the streams on a device. + /// @param config The configuration to use for setting the streams up. + /// @param moduleIndex The index of the device to set up. + /// @return The status code of the operation. virtual OpStatus StreamSetup(const StreamConfig& config, uint8_t moduleIndex) = 0; - virtual void StreamStart(const std::vector moduleIndexes); + + /// @brief Starts all the set up streams on the device. + /// @param moduleIndex The index of the device to start the streams on. virtual void StreamStart(uint8_t moduleIndex) = 0; + + /// @brief Starts all the set up streams on the devices. + /// @param moduleIndexes The indices of the devices to start the streams on. + virtual void StreamStart(const std::vector moduleIndexes); + + /// @brief Stops all the set up streams on the device. + /// @param moduleIndex The index of the device to stop the streams on. virtual void StreamStop(uint8_t moduleIndex) = 0; + + /// @brief Stops all the set up streams on the devices. + /// @param moduleIndexes The indices of the devices to stop the streams on. virtual void StreamStop(const std::vector moduleIndexes); - virtual int StreamRx(uint8_t moduleIndex, lime::complex32f_t* const* samples, uint32_t count, StreamMeta* meta) = 0; - virtual int StreamRx(uint8_t moduleIndex, lime::complex16_t* const* samples, uint32_t count, StreamMeta* meta) = 0; - virtual int StreamRx(uint8_t moduleIndex, lime::complex12_t* const* samples, uint32_t count, StreamMeta* meta) = 0; - virtual int StreamTx(uint8_t moduleIndex, const lime::complex32f_t* const* samples, uint32_t count, const StreamMeta* meta) = 0; - virtual int StreamTx(uint8_t moduleIndex, const lime::complex16_t* const* samples, uint32_t count, const StreamMeta* meta) = 0; - virtual int StreamTx(uint8_t moduleIndex, const lime::complex12_t* const* samples, uint32_t count, const StreamMeta* meta) = 0; + /// @brief Reveives samples from all the active streams in the device. + /// @param moduleIndex The index of the device to receive the samples from. + /// @param samples The buffer to put the received samples in. + /// @param count The amount of samples to reveive. + /// @param meta The metadata of the packets of the stream. + /// @return The amount of samples received. + virtual uint32_t StreamRx(uint8_t moduleIndex, lime::complex32f_t* const* samples, uint32_t count, StreamMeta* meta) = 0; + /// @copydoc SDRDevice::StreamRx() + virtual uint32_t StreamRx(uint8_t moduleIndex, lime::complex16_t* const* samples, uint32_t count, StreamMeta* meta) = 0; + /// @copydoc SDRDevice::StreamRx() + virtual uint32_t StreamRx(uint8_t moduleIndex, lime::complex12_t* const* samples, uint32_t count, StreamMeta* meta) = 0; + + /// @brief Transmits packets from all the active streams in the device. + /// @param moduleIndex The index of the device to transmit the samples with. + /// @param samples The buffer of the samples to transmit. + /// @param count The amount of samples to transmit. + /// @param meta The metadata of the packets of the stream. + /// @return The amount of samples transmitted. + virtual uint32_t StreamTx( + uint8_t moduleIndex, const lime::complex32f_t* const* samples, uint32_t count, const StreamMeta* meta) = 0; + /// @copydoc SDRDevice::StreamTx() + virtual uint32_t StreamTx( + uint8_t moduleIndex, const lime::complex16_t* const* samples, uint32_t count, const StreamMeta* meta) = 0; + /// @copydoc SDRDevice::StreamRx() + virtual uint32_t StreamTx( + uint8_t moduleIndex, const lime::complex12_t* const* samples, uint32_t count, const StreamMeta* meta) = 0; + + /// @brief Retrieves the current stream statistics. + /// @param moduleIndex The index of the device to retrieve the status from. + /// @param rx The pointer (or nullptr if not needed) to store the receive statistics to. + /// @param tx The pointer (or nullptr if not needed) to store the transmit statistics to. virtual void StreamStatus(uint8_t moduleIndex, SDRDevice::StreamStats* rx, SDRDevice::StreamStats* tx) = 0; + /// @brief Uploads waveform to on board memory for later use. + /// @param config The configuration of the stream. + /// @param moduleIndex The index of the device to upload the waveform to. + /// @param samples The samples to upload to the device. + /// @param count The amount of samples to upload to the device. + /// @return Operation status. virtual OpStatus UploadTxWaveform(const StreamConfig& config, uint8_t moduleIndex, const void** samples, uint32_t count) { return OpStatus::NOT_IMPLEMENTED; } + /// @copydoc ISPI::SPI() + /// @param spiBusAddress The SPI address of the device to use. virtual OpStatus SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count); + + /// @copydoc II2C::I2CWrite() virtual OpStatus I2CWrite(int address, const uint8_t* data, uint32_t length); - virtual OpStatus I2CRead(int addres, uint8_t* dest, uint32_t length); + + /// @copydoc II2C::I2CRead() + virtual OpStatus I2CRead(int address, uint8_t* dest, uint32_t length); /*********************************************************************** * GPIO API **********************************************************************/ - /** \copydoc IComms::GPIOWrite() */ + /// @copydoc IComms::GPIOWrite() virtual OpStatus GPIOWrite(const uint8_t* buffer, const size_t bufLength); - /** \copydoc IComms::GPIORead() */ + /// @copydoc IComms::GPIORead() virtual OpStatus GPIORead(uint8_t* buffer, const size_t bufLength); - /** \copydoc IComms::GPIODirWrite() */ + /// @copydoc IComms::GPIODirWrite() virtual OpStatus GPIODirWrite(const uint8_t* buffer, const size_t bufLength); - /** \copydoc IComms::GPIODirRead() */ + /// @copydoc IComms::GPIODirRead() virtual OpStatus GPIODirRead(uint8_t* buffer, const size_t bufLength); /*********************************************************************** * Aribtrary settings API **********************************************************************/ - /** @brief Sets custom on board control to given value units - @param parameters A vector of parameters describing the parameter to write - @return The operation success state - */ + /// @copydoc IComms::CustomParameterWrite() virtual OpStatus CustomParameterWrite(const std::vector& parameters); - /** @brief Returns value of custom on board control - @param parameters A vector of parameters describing the parameter to read - @return The operation success state - */ + /// @copydoc IComms::CustomParameterRead() virtual OpStatus CustomParameterRead(std::vector& parameters); - /** @brief Sets callback function which gets called each time data is sent or received */ + /// @brief The definition of a function to run when data is received. + typedef void (*DataCallbackType)(bool, const uint8_t*, const uint32_t); + + /// @brief Sets callback function which gets called each time data is sent or received + /// @param callback The callback to use from this point onwards. virtual void SetDataLogCallback(DataCallbackType callback){}; + + /// @brief The definition of a function to call when a log message is generated. + typedef void (*LogCallbackType)(LogLevel, const char*); + + /// @brief Sets callback function which gets called each a log message is received + /// @param callback The callback to use from this point onwards. virtual void SetMessageLogCallback(LogCallbackType callback){}; + /// @brief Gets the pointer to an internal chip of the device. + /// @param index The index of the device to retreive. + /// @return The pointer to the internal device. virtual void* GetInternalChip(uint32_t index) = 0; + /// @brief The definition of a function to call whenever memory is being uploaded. typedef bool (*UploadMemoryCallback)(size_t bsent, size_t btotal, const char* statusMessage); + /// @brief Uploads the given memory into the specified device. + /// @param device The memory device to upload the memory to. + /// @param moduleIndex The index of the main device to upload the memory to. + /// @param data The data to upload to the device. + /// @param length The length of the memory to upload. + /// @param callback The callback to call for status updates. + /// @return The success status of the operation. virtual OpStatus UploadMemory( eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback); + /// @brief Writes given data into a given memory address in EEPROM memory. + /// @param storage The storage device to write to. + /// @param region Information of the region in which to write the data to. + /// @param data The data to write into the specified memory. + /// @return The operation success state. virtual OpStatus MemoryWrite(std::shared_ptr storage, Region region, const void* data); + + /// @brief Reads data from a given memory address in EEPROM memory. + /// @param storage The storage device to read from. + /// @param region Information of the region from which to read the memory. + /// @param data The storage buffer for the data being read. + /// @return The operation success state. virtual OpStatus MemoryRead(std::shared_ptr storage, Region region, void* data); }; diff --git a/src/include/limesuite/StreamComposite.h b/src/include/limesuite/StreamComposite.h index 79089a4fa..0c5f8e7a8 100644 --- a/src/include/limesuite/StreamComposite.h +++ b/src/include/limesuite/StreamComposite.h @@ -8,10 +8,11 @@ namespace lime { +/** @brief Structure for holding information about the aggregate stream. */ struct LIME_API StreamAggregate { - SDRDevice* device; - std::vector channels; - int32_t streamIndex; + SDRDevice* device; ///< The device the stream is coming from. + std::vector channels; ///< The channels the device is streaming with. + int32_t streamIndex; ///< The index of the stream. }; /** @brief Class for managing streaming from multiple devices at the same time. */ @@ -19,14 +20,29 @@ class LIME_API StreamComposite { public: StreamComposite() = delete; + + /// @brief Constructs the StreamComposite object. + /// @param aggregate The list of streams to aggregate into one stream. StreamComposite(const std::vector& aggregate); + /// @brief Sets up the streams with the given configuration. + /// @param config The configuration to set up the streams with. + /// @return The status of the operation. OpStatus StreamSetup(const SDRDevice::StreamConfig& config); + + /// @brief Starts all of the aggregated streams. void StreamStart(); + + /// @brief Ends all of the aggregated streams. void StreamStop(); - template int StreamRx(T** samples, uint32_t count, SDRDevice::StreamMeta* meta); - template int StreamTx(const T* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); + /// @copydoc TRXLooper::StreamRx() + /// @tparam T The type of streams to send. + template uint32_t StreamRx(T** samples, uint32_t count, SDRDevice::StreamMeta* meta); + + /// @copydoc TRXLooper::StreamTx() + /// @tparam T The type of streams to receive. + template uint32_t StreamTx(const T* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); private: std::vector SplitAggregateStreamSetup(const SDRDevice::StreamConfig& cfg); @@ -34,4 +50,4 @@ class LIME_API StreamComposite std::vector mActiveAggregates; }; -} // namespace lime \ No newline at end of file +} // namespace lime diff --git a/src/include/limesuite/commonTypes.h b/src/include/limesuite/commonTypes.h index c35fe80d1..92a5d42a3 100644 --- a/src/include/limesuite/commonTypes.h +++ b/src/include/limesuite/commonTypes.h @@ -2,18 +2,24 @@ namespace lime { +/// @brief The direction of the transmission enum class TRXDir : bool { Rx, Tx }; const char* ToCString(TRXDir dir); +/// @brief Structure describing the range possible. struct Range { + /// @brief Constructs the range structure, + /// @param min The minimum value of the range (default 0.0) + /// @param max The maximum value of the range (default 0.0) + /// @param step The step of the range (default 0.0 - no step) constexpr Range(double min = 0.0, double max = 0.0, double step = 0.0) : min(min) , max(max) , step(step){}; - double min; - double max; - double step; + double min; ///< The minimum value of the range + double max; ///< The maximum value of the range + double step; ///< The step of the range (or 0.0 for any step) }; /** @@ -23,13 +29,39 @@ struct Range { template class DeltaVariable { public: + /** + @brief Construct a new DeltaVariable object. + @param init The initial value of the variable. + */ constexpr DeltaVariable(T init) : mValue(init) , mLastValue(0){}; + + /** + @brief Sets the value of the delta variable. + @param val The value to set the delta variable to. + */ constexpr void set(T val) { mValue = val; } + + /** + @brief Adds the given value to the delta variable + @param val The value to add to the delta variable. + */ constexpr void add(T val) { mValue += val; } - constexpr T delta() const { return mValue - mLastValue; } // value change since last reset + + /** + @brief Gets the difference between the value at last reset and current value. + @return The delta between the last time the delta variable was reset and the current value. + */ + constexpr T delta() const { return mValue - mLastValue; } + + /** + @brief Gets the current value of the delta variable. + @return The current stored value of the delta variable. + */ constexpr T value() const { return mValue; } + + /** @brief Resets the delta variable to start measuring from its current value. */ constexpr void checkpoint() { mLastValue = mValue; } private: diff --git a/src/include/limesuite/complex.h b/src/include/limesuite/complex.h index 16e39301e..0e5f08f6e 100644 --- a/src/include/limesuite/complex.h +++ b/src/include/limesuite/complex.h @@ -15,11 +15,16 @@ struct complex12packed_t { : complex12packed_t(0, 0) { } + + /// @brief Constructs the 12 bit compressed complex number. + /// @param i The I value of the number. + /// @param q The Q value of the number. constexpr complex12packed_t(int16_t i, int16_t q) : data{ uint8_t(i), uint8_t((q << 4) | ((i >> 8) & 0x0F)), uint8_t(q >> 4) } { } + /// @copydoc POD_complex_t::real() constexpr int16_t real() const { int16_t value = data[0]; @@ -30,12 +35,14 @@ struct complex12packed_t { return value; } + /// @copydoc POD_complex_t::real(T) constexpr void real(int16_t value) { data[0] = value; data[1] = (data[1] & 0xF0) | ((value >> 8) & 0x0F); } + /// @copydoc POD_complex_t::imag() constexpr int16_t imag() const { int16_t value = data[1]; @@ -44,17 +51,19 @@ struct complex12packed_t { return value; } + /// @copydoc POD_complex_t::imag(T) constexpr void imag(int16_t value) { data[1] = (value << 4) | (data[1] & 0x0F); data[2] = value >> 4; } - constexpr void Set(int16_t i, int16_t q) + /// @copydoc POD_complex_t::Set() + constexpr void Set(int16_t ival, int16_t qval) { - data[0] = i; - data[1] = (q << 4) | ((i >> 8) & 0x0F); - data[2] = q >> 4; + data[0] = ival; + data[1] = (qval << 4) | ((ival >> 8) & 0x0F); + data[2] = qval >> 4; } private: @@ -62,31 +71,50 @@ struct complex12packed_t { }; static_assert(std::is_trivially_copyable::value == true); -// complex number structure for plain data types (int, float...) +/// @brief Complex number structure for plain data types (int, float...) +/// @tparam T The type of the number to hold. template struct POD_complex_t { constexpr POD_complex_t() : POD_complex_t(0, 0) { } + + /// @brief Constructs the complex number with the specified values. + /// @param real The I value of the number. + /// @param imag The Q value of the number. constexpr POD_complex_t(T real, T imag) : i(real) , q(imag) { } + /// @brief Gets the I (real) value of the complex number. + /// @return The I value of the complex number. constexpr T real() const { return i; } + + /// @brief Sets the I (real) value of the complex number. + /// @param value The value to set the complex number's I component to. constexpr void real(T value) { i = value; } + + /// @brief Gets the Q (imaginary) value of the complex number. + /// @return The Q value of the complex number. constexpr T imag() const { return q; } + + /// @brief Sets the Q (imaginary) value of the complex number. + /// @param value The value to set the complex number's Q component to. constexpr void imag(T value) { q = value; } + /// @brief Sets both values of the complex number. + /// @param ival The I (real) component of the complex number. + /// @param qval The Q (imaginary) component of the complex number. constexpr void Set(T ival, T qval) { i = ival; q = qval; } - T i; - T q; + T i; ///< The I component of the number. + T q; ///< The Q component of the number. }; /** @brief Structure to hold a 16 bit integer complex number. */ @@ -94,14 +122,18 @@ using complex16_t = POD_complex_t; static_assert(std::is_trivially_copyable::value == true); /** @brief Structure to hold a 12 bit integer complex number. - Stored as 16bit, but the actual used values range should be of 12bits + Stored as 16 bit, but the actual used values range should be of 12bits */ -// inheriting complex16_t instead of using alias of POD_complex_t to +// Inheriting complex16_t instead of using alias of POD_complex_t to // differentiate types for templating class complex12_t : public complex16_t { constexpr complex12_t() : complex16_t(){}; + + /// @brief Constructs the 16 bit integer complex number. + /// @param re The I (real) component of the number. + /// @param im The Q (imaginary) component of the number. constexpr complex12_t(int16_t real, int16_t imag) : complex16_t(real, imag){}; }; diff --git a/src/lms7002_wxgui/lms7002_pnlRxTSP_view.cpp b/src/lms7002_wxgui/lms7002_pnlRxTSP_view.cpp index f4f317c59..767c40f11 100644 --- a/src/lms7002_wxgui/lms7002_pnlRxTSP_view.cpp +++ b/src/lms7002_wxgui/lms7002_pnlRxTSP_view.cpp @@ -1847,7 +1847,8 @@ void lms7002_pnlRXTSP_view::OnbtnSetLPFClick(wxCommandEvent& event) { double bw; txtLPFBW->GetValue().ToDouble(&bw); - if (lmsControl->SetGFIRFilter(TRXDir::Rx, mChannel, true, bw * 1e6) != OpStatus::SUCCESS) + if (lmsControl->SetGFIRFilter(TRXDir::Rx, mChannel == 0 ? LMS7002M::Channel::ChA : LMS7002M::Channel::ChB, true, bw * 1e6) != + OpStatus::SUCCESS) wxMessageBox(_("GFIR configuration failed"), _("Error")); UpdateGUI(); // API changes nco selection @@ -1890,7 +1891,7 @@ void lms7002_pnlRXTSP_view::UpdateGUI() if (lmsControl == nullptr) return; LMS7002_WXGUI::UpdateControlsByMap(this, lmsControl, wndId2Enum, mChannel); - double freq = lmsControl->GetClockFreq(LMS7002M::ClockID::CLK_RXTSP, mChannel); + double freq = lmsControl->GetClockFreq(LMS7002M::ClockID::CLK_RXTSP); lblRefClk->SetLabel(wxString::Format(_("%3.3f"), freq / 1e6)); int16_t iqcorr_value; diff --git a/src/lms7002_wxgui/lms7002_pnlTxTSP_view.cpp b/src/lms7002_wxgui/lms7002_pnlTxTSP_view.cpp index ef07df526..fbb7d800b 100644 --- a/src/lms7002_wxgui/lms7002_pnlTxTSP_view.cpp +++ b/src/lms7002_wxgui/lms7002_pnlTxTSP_view.cpp @@ -1649,7 +1649,8 @@ void lms7002_pnlTXTSP_view::OnbtnSetLPFClick(wxCommandEvent& event) double bw; txtLPFBW->GetValue().ToDouble(&bw); - if (lmsControl->SetGFIRFilter(TRXDir::Tx, mChannel, true, bw * 1e6) != OpStatus::SUCCESS) + if (lmsControl->SetGFIRFilter(TRXDir::Tx, mChannel == 0 ? LMS7002M::Channel::ChA : LMS7002M::Channel::ChB, true, bw * 1e6) != + OpStatus::SUCCESS) wxMessageBox(_("GFIR configuration failed"), _("Error")); UpdateGUI(); // API changes nco selection } diff --git a/src/lms7002m/LMS7002M.cpp b/src/lms7002m/LMS7002M.cpp index 4992fdb54..8d76a70da 100644 --- a/src/lms7002m/LMS7002M.cpp +++ b/src/lms7002m/LMS7002M.cpp @@ -38,8 +38,15 @@ using namespace lime; -float_type LMS7002M::gVCO_frequency_table[3][2] = { { 3800e6, 5222e6 }, { 4961e6, 6754e6 }, { 6306e6, 7714e6 } }; -float_type LMS7002M::gCGEN_VCO_frequencies[2] = { 1930e6, 2940e6 }; +constexpr std::array, 3> LMS7002M::gVCO_frequency_table{ + { { 3800e6, 5222e6 }, { 4961e6, 6754e6 }, { 6306e6, 7714e6 } } +}; +constexpr std::array LMS7002M::gCGEN_VCO_frequencies{ 1930e6, 2940e6 }; + +constexpr LMS7002M::Channel IntToChannel(int channel) +{ + return channel > 0 ? LMS7002M::Channel::ChB : LMS7002M::Channel::ChA; +} /// Define for parameter enumeration if prefix might be needed extern std::vector> LMS7parameterList; @@ -61,10 +68,57 @@ const std::vector LMS7002M::readOnlyRegisters{ { 0x040F, 0x0000 }, }; +const std::map> LMS7002M::MemorySectionAddresses{ + { LMS7002M::MemorySection::LimeLight, { 0x0020, 0x002F } }, + { LMS7002M::MemorySection::EN_DIR, { 0x0081, 0x0081 } }, + { LMS7002M::MemorySection::AFE, { 0x0082, 0x0082 } }, + { LMS7002M::MemorySection::BIAS, { 0x0084, 0x0084 } }, + { LMS7002M::MemorySection::XBUF, { 0x0085, 0x0085 } }, + { LMS7002M::MemorySection::CGEN, { 0x0086, 0x008C } }, + { LMS7002M::MemorySection::LDO, { 0x0092, 0x00A7 } }, + { LMS7002M::MemorySection::BIST, { 0x00A8, 0x00AC } }, + { LMS7002M::MemorySection::CDS, { 0x00AD, 0x00AE } }, + { LMS7002M::MemorySection::TRF, { 0x0100, 0x0104 } }, + { LMS7002M::MemorySection::TBB, { 0x0105, 0x010B } }, + { LMS7002M::MemorySection::RFE, { 0x010C, 0x0114 } }, + { LMS7002M::MemorySection::RBB, { 0x0115, 0x011A } }, + { LMS7002M::MemorySection::SX, { 0x011C, 0x0124 } }, + { LMS7002M::MemorySection::TRX_GAIN, { 0x0125, 0x0126 } }, + { LMS7002M::MemorySection::TxTSP, { 0x0200, 0x020C } }, + { LMS7002M::MemorySection::TxNCO, { 0x0240, 0x0261 } }, + { LMS7002M::MemorySection::TxGFIR1, { 0x0280, 0x02A7 } }, + { LMS7002M::MemorySection::TxGFIR2, { 0x02C0, 0x02E7 } }, + { LMS7002M::MemorySection::TxGFIR3a, { 0x0300, 0x0327 } }, + { LMS7002M::MemorySection::TxGFIR3b, { 0x0340, 0x0367 } }, + { LMS7002M::MemorySection::TxGFIR3c, { 0x0380, 0x03A7 } }, + { LMS7002M::MemorySection::RxTSP, { 0x0400, 0x040F } }, + { LMS7002M::MemorySection::RxNCO, { 0x0440, 0x0461 } }, + { LMS7002M::MemorySection::RxGFIR1, { 0x0480, 0x04A7 } }, + { LMS7002M::MemorySection::RxGFIR2, { 0x04C0, 0x04E7 } }, + { LMS7002M::MemorySection::RxGFIR3a, { 0x0500, 0x0527 } }, + { LMS7002M::MemorySection::RxGFIR3b, { 0x0540, 0x0567 } }, + { LMS7002M::MemorySection::RxGFIR3c, { 0x0580, 0x05A7 } }, + { LMS7002M::MemorySection::RSSI_DC_CALIBRATION, { 0x05C0, 0x05CC } }, + { LMS7002M::MemorySection::RSSI_PDET_TEMP_CONFIG, { 0x0600, 0x0606 } }, + { LMS7002M::MemorySection::RSSI_DC_CONFIG, { 0x0640, 0x0641 } }, +}; + /** @brief Switches LMS7002M SPI to requested channel and restores previous channel when going out of scope */ class ChannelScope { public: + /** + * @brief Saves the current channel and restores it at scope exit. + * @param chip The chip to use. + * @param useCache Whether to use caching or not. + */ + ChannelScope(LMS7002M* chip, bool useCache = false) + : mChip(chip) + , mStoredValue(chip->GetActiveChannel(!useCache)) + , mNeedsRestore(true) + { + } + /** @brief Convenient constructor when using explicit MAC value. @param chip The chip to use. @@ -73,13 +127,13 @@ class ChannelScope */ ChannelScope(LMS7002M* chip, LMS7002M::Channel mac, bool useCache = false) : mChip(chip) + , mStoredValue(chip->GetActiveChannel(!useCache)) , mNeedsRestore(false) { - mStoredValue = chip->GetActiveChannel(!useCache); if (mStoredValue == mac) return; - chip->SetActiveChannel(mac); + mChip->SetActiveChannel(mac); mNeedsRestore = true; } @@ -95,7 +149,7 @@ class ChannelScope { assert(index < 2); mStoredValue = chip->GetActiveChannel(!useCache); - auto expectedChannel = index > 0 ? LMS7002M::Channel::ChB : LMS7002M::Channel::ChA; + auto expectedChannel = IntToChannel(index); if (mStoredValue == expectedChannel) return; @@ -198,74 +252,8 @@ LMS7002M::LMS7002M(std::shared_ptr port) , controlPort(port) , _cachedRefClockRate(30.72e6) { - mCalibrationByMCU = true; opt_gain_tbb[0] = -1; opt_gain_tbb[1] = -1; - //memory intervals for registers tests and calibration algorithms - MemorySectionAddresses[LimeLight][0] = 0x0020; - MemorySectionAddresses[LimeLight][1] = 0x002F; - MemorySectionAddresses[EN_DIR][0] = 0x0081; - MemorySectionAddresses[EN_DIR][1] = 0x0081; - MemorySectionAddresses[AFE][0] = 0x0082; - MemorySectionAddresses[AFE][1] = 0x0082; - MemorySectionAddresses[BIAS][0] = 0x0084; - MemorySectionAddresses[BIAS][1] = 0x0084; - MemorySectionAddresses[XBUF][0] = 0x0085; - MemorySectionAddresses[XBUF][1] = 0x0085; - MemorySectionAddresses[CGEN][0] = 0x0086; - MemorySectionAddresses[CGEN][1] = 0x008C; - MemorySectionAddresses[LDO][0] = 0x0092; - MemorySectionAddresses[LDO][1] = 0x00A7; - MemorySectionAddresses[BIST][0] = 0x00A8; - MemorySectionAddresses[BIST][1] = 0x00AC; - MemorySectionAddresses[CDS][0] = 0x00AD; - MemorySectionAddresses[CDS][1] = 0x00AE; - MemorySectionAddresses[TRF][0] = 0x0100; - MemorySectionAddresses[TRF][1] = 0x0104; - MemorySectionAddresses[TBB][0] = 0x0105; - MemorySectionAddresses[TBB][1] = 0x010B; - MemorySectionAddresses[RFE][0] = 0x010C; - MemorySectionAddresses[RFE][1] = 0x0114; - MemorySectionAddresses[RBB][0] = 0x0115; - MemorySectionAddresses[RBB][1] = 0x011A; - MemorySectionAddresses[SX][0] = 0x011C; - MemorySectionAddresses[SX][1] = 0x0124; - MemorySectionAddresses[TRX_GAIN][0] = 0x0125; - MemorySectionAddresses[TRX_GAIN][1] = 0x0126; - MemorySectionAddresses[TxTSP][0] = 0x0200; - MemorySectionAddresses[TxTSP][1] = 0x020C; - MemorySectionAddresses[TxNCO][0] = 0x0240; - MemorySectionAddresses[TxNCO][1] = 0x0261; - MemorySectionAddresses[TxGFIR1][0] = 0x0280; - MemorySectionAddresses[TxGFIR1][1] = 0x02A7; - MemorySectionAddresses[TxGFIR2][0] = 0x02C0; - MemorySectionAddresses[TxGFIR2][1] = 0x02E7; - MemorySectionAddresses[TxGFIR3a][0] = 0x0300; - MemorySectionAddresses[TxGFIR3a][1] = 0x0327; - MemorySectionAddresses[TxGFIR3b][0] = 0x0340; - MemorySectionAddresses[TxGFIR3b][1] = 0x0367; - MemorySectionAddresses[TxGFIR3c][0] = 0x0380; - MemorySectionAddresses[TxGFIR3c][1] = 0x03A7; - MemorySectionAddresses[RxTSP][0] = 0x0400; - MemorySectionAddresses[RxTSP][1] = 0x040F; - MemorySectionAddresses[RxNCO][0] = 0x0440; - MemorySectionAddresses[RxNCO][1] = 0x0461; - MemorySectionAddresses[RxGFIR1][0] = 0x0480; - MemorySectionAddresses[RxGFIR1][1] = 0x04A7; - MemorySectionAddresses[RxGFIR2][0] = 0x04C0; - MemorySectionAddresses[RxGFIR2][1] = 0x04E7; - MemorySectionAddresses[RxGFIR3a][0] = 0x0500; - MemorySectionAddresses[RxGFIR3a][1] = 0x0527; - MemorySectionAddresses[RxGFIR3b][0] = 0x0540; - MemorySectionAddresses[RxGFIR3b][1] = 0x0567; - MemorySectionAddresses[RxGFIR3c][0] = 0x0580; - MemorySectionAddresses[RxGFIR3c][1] = 0x05A7; - MemorySectionAddresses[RSSI_DC_CALIBRATION][0] = 0x05C0; - MemorySectionAddresses[RSSI_DC_CALIBRATION][1] = 0x05CC; - MemorySectionAddresses[RSSI_PDET_TEMP_CONFIG][0] = 0x0600; - MemorySectionAddresses[RSSI_PDET_TEMP_CONFIG][1] = 0x0606; - MemorySectionAddresses[RSSI_DC_CONFIG][0] = 0x0640; - MemorySectionAddresses[RSSI_DC_CONFIG][1] = 0x0641; mRegistersMap->InitializeDefaultValues(LMS7parameterList); mcuControl = new MCU_BD(); @@ -298,10 +286,9 @@ size_t LMS7002M::GetActiveChannelIndex(bool fromChip) OpStatus LMS7002M::EnableChannel(TRXDir dir, const uint8_t channel, const bool enable) { - ChannelScope scope(this, channel); + ChannelScope scope(this, channel, false); - const Channel ch = this->GetActiveChannel(); - this->SetActiveChannel(channel > 0 ? Channel::ChB : Channel::ChA); + const Channel ch = IntToChannel(channel); const bool isTx = dir == TRXDir::Tx; //--- LML --- @@ -437,9 +424,6 @@ OpStatus LMS7002M::EnableChannel(TRXDir dir, const uint8_t channel, const bool e return OpStatus::SUCCESS; } -/** @brief Sends reset signal to chip, after reset enables B channel controls - @return 0-success, other-failure -*/ OpStatus LMS7002M::ResetChip() { OpStatus status; @@ -495,7 +479,6 @@ OpStatus LMS7002M::LoadConfigLegacyFile(const std::string& filename) uint16_t addr = 0; uint16_t value = 0; - Channel ch = this->GetActiveChannel(); //remember used channel OpStatus status; typedef INI ini_t; ini_t parser(filename, true); @@ -519,6 +502,8 @@ OpStatus LMS7002M::LoadConfigLegacyFile(const std::string& filename) std::vector dataToWrite; if (fileVersion == 1) { + ChannelScope scope(this); + if (parser.select("Reference clocks")) { this->SetReferenceClk_SX(TRXDir::Rx, parser.get("SXR reference frequency MHz", 30.72) * 1e6); @@ -658,17 +643,11 @@ OpStatus LMS7002M::LoadConfigLegacyFile(const std::string& filename) } } } - this->SetActiveChannel(ch); return OpStatus::SUCCESS; } return ReportError(OpStatus::INVALID_VALUE, "LoadConfigLegacyFile(%s) - invalid format", filename.c_str()); } -/** @brief Reads configuration file and uploads registers to chip - @param filename Configuration source file - @param tuneDynamicValues Whether to tune the dynamic values or not - @return 0 - success, other - failure -*/ OpStatus LMS7002M::LoadConfig(const std::string& filename, bool tuneDynamicValues) { std::ifstream f(filename); @@ -681,7 +660,6 @@ OpStatus LMS7002M::LoadConfig(const std::string& filename, bool tuneDynamicValue uint16_t addr = 0; uint16_t value = 0; - Channel ch = this->GetActiveChannel(); //remember used channel OpStatus status; typedef INI ini_t; @@ -710,6 +688,7 @@ OpStatus LMS7002M::LoadConfig(const std::string& filename, bool tuneDynamicValue if (fileVersion == 1) { + ChannelScope scope(this); if (parser.select("lms7002_registers_a") == true) { ini_t::sectionsit_t section = parser.sections.find("lms7002_registers_a"); @@ -768,14 +747,13 @@ OpStatus LMS7002M::LoadConfig(const std::string& filename, bool tuneDynamicValue if (status != OpStatus::SUCCESS && controlPort != nullptr) return status; } - this->SetActiveChannel(ch); parser.select("reference_clocks"); this->SetReferenceClk_SX(TRXDir::Rx, parser.get("sxr_ref_clk_mhz", 30.72) * 1e6); this->SetReferenceClk_SX(TRXDir::Tx, parser.get("sxt_ref_clk_mhz", 30.72) * 1e6); } - ResetLogicregisters(); + ResetLogicRegisters(); if (tuneDynamicValues) { @@ -796,7 +774,7 @@ OpStatus LMS7002M::LoadConfig(const std::string& filename, bool tuneDynamicValue return OpStatus::SUCCESS; } -OpStatus LMS7002M::ResetLogicregisters() +OpStatus LMS7002M::ResetLogicRegisters() { const uint16_t x0020_value = SPI_read(0x0020); //reset logic registers const uint16_t addr[] = { 0x0020, 0x0020 }; @@ -809,10 +787,6 @@ OpStatus LMS7002M::ResetLogicregisters() return SPI_write_batch(addr, values, 2); } -/** @brief Reads all registers from chip and saves to file - @param filename destination filename - @return 0-success, other failure -*/ OpStatus LMS7002M::SaveConfig(const std::string& filename) { std::ofstream fout; @@ -824,12 +798,13 @@ OpStatus LMS7002M::SaveConfig(const std::string& filename) char addr[80]; char value[80]; - Channel ch = this->GetActiveChannel(); + ChannelScope scope(this); std::vector addrToRead; - for (uint8_t i = 0; i < MEMORY_SECTIONS_COUNT; ++i) - for (uint16_t addr = MemorySectionAddresses[i][0]; addr <= MemorySectionAddresses[i][1]; ++addr) + for (const auto& memorySectionPair : MemorySectionAddresses) + for (uint16_t addr = memorySectionPair.second[0]; addr <= memorySectionPair.second[1]; ++addr) addrToRead.push_back(addr); + std::vector dataReceived; dataReceived.resize(addrToRead.size(), 0); @@ -861,9 +836,9 @@ OpStatus LMS7002M::SaveConfig(const std::string& filename) fout << "[lms7002_registers_b]" << std::endl; addrToRead.clear(); //add only B channel addresses - for (uint8_t i = 0; i < MEMORY_SECTIONS_COUNT; ++i) - if (i != RSSI_DC_CALIBRATION) - for (uint16_t addr = MemorySectionAddresses[i][0]; addr <= MemorySectionAddresses[i][1]; ++addr) + for (const auto& memorySectionPair : MemorySectionAddresses) + if (memorySectionPair.first != MemorySection::RSSI_DC_CALIBRATION) + for (uint16_t addr = memorySectionPair.second[0]; addr <= memorySectionPair.second[1]; ++addr) if (addr >= 0x0100) addrToRead.push_back(addr); @@ -876,8 +851,6 @@ OpStatus LMS7002M::SaveConfig(const std::string& filename) fout << addr << "=" << value << std::endl; } - this->SetActiveChannel(ch); //retore previously used channel - fout << "[reference_clocks]" << std::endl; fout << "sxt_ref_clk_mhz=" << this->GetReferenceClk_SX(TRXDir::Tx) / 1e6 << std::endl; fout << "sxr_ref_clk_mhz=" << this->GetReferenceClk_SX(TRXDir::Rx) / 1e6 << std::endl; @@ -1199,7 +1172,7 @@ OpStatus LMS7002M::SetTBBIAMP_dB(const float_type gain, const Channel channel) int ind = this->GetActiveChannelIndex() % 2; if (opt_gain_tbb[ind] <= 0) { - status = CalibrateTxGain(0, nullptr); + status = CalibrateTxGain(); if (status != OpStatus::SUCCESS) //set optimal BB gain return status; if (std::fabs(gain) < 0.2) // optimal gain = ~0dB @@ -1221,7 +1194,7 @@ float_type LMS7002M::GetTBBIAMP_dB(const Channel channel) if (opt_gain_tbb[ind] <= 0) { - if (CalibrateTxGain(0, nullptr) != OpStatus::SUCCESS) + if (CalibrateTxGain() != OpStatus::SUCCESS) return 0.0; Modify_SPI_Reg_bits(LMS7param(CG_IAMP_TBB), g_current, true); //restore } @@ -1307,7 +1280,7 @@ int LMS7002M::GetBandTRF(void) OpStatus LMS7002M::SetPath(TRXDir direction, uint8_t channel, uint8_t path) { - ChannelScope scope(this, channel); + ChannelScope scope(this, channel, false); if (direction == TRXDir::Tx) { @@ -1323,9 +1296,6 @@ OpStatus LMS7002M::SetReferenceClk_SX(TRXDir dir, float_type freq_Hz) return OpStatus::SUCCESS; } -/** @brief Returns reference clock in Hz used for SXT or SXR - @param dir transmitter or receiver selection -*/ float_type LMS7002M::GetReferenceClk_SX(TRXDir dir) { return _cachedRefClockRate; @@ -1333,7 +1303,7 @@ float_type LMS7002M::GetReferenceClk_SX(TRXDir dir) OpStatus LMS7002M::SetNCOFrequencies(TRXDir dir, const float_type* freq_Hz, uint8_t count, float_type phaseOffset) { - for (unsigned i = 0; i < 16 && i < count; i++) + for (uint8_t i = 0; i < 16 && i < count; i++) { OpStatus status = SetNCOFrequency(dir, i, freq_Hz[i]); if (status != OpStatus::SUCCESS) @@ -1355,9 +1325,6 @@ std::vector LMS7002M::GetNCOFrequencies(TRXDir dir, float_type* phas return ncos; } -/** @return Current CLKGEN frequency in Hz - Returned frequency depends on reference clock used for Receiver -*/ float_type LMS7002M::GetFrequencyCGEN() { float_type dMul = @@ -1367,10 +1334,6 @@ float_type LMS7002M::GetFrequencyCGEN() return dMul * (((gINT >> 4) + 1 + gFRAC / 1048576.0)); } -/** @brief Returns TSP reference frequency - @param dir TxTSP or RxTSP selection - @return TSP reference frequency in Hz -*/ float_type LMS7002M::GetReferenceClk_TSP(TRXDir dir) { float_type cgenFreq = GetFrequencyCGEN(); @@ -1381,12 +1344,6 @@ float_type LMS7002M::GetReferenceClk_TSP(TRXDir dir) return dir == TRXDir::Tx ? cgenFreq : clklfreq / 4.0; } -/** @brief Sets CLKGEN frequency, calculations use receiver'r reference clock - @param freq_Hz desired frequency in Hz - @param retainNCOfrequencies recalculate NCO coefficients to keep currently set frequencies - @param output if not null outputs calculated CGEN parameters - @return 0-succes, other-cannot deliver desired frequency -*/ OpStatus LMS7002M::SetFrequencyCGEN(const float_type freq_Hz, const bool retainNCOfrequencies, CGEN_details* output) { if (freq_Hz > CGEN_MAX_FREQ) @@ -1406,7 +1363,7 @@ OpStatus LMS7002M::SetFrequencyCGEN(const float_type freq_Hz, const bool retainN txModeNCO = Get_SPI_Reg_bits(LMS7param(MODE_TX), true); for (int ch = 0; ch < 2; ++ch) { - this->SetActiveChannel((ch == 0) ? Channel::ChA : Channel::ChB); + this->SetActiveChannel(IntToChannel(ch)); for (int i = 0; i < 16 && rxModeNCO == 0; ++i) rxNCO[ch].push_back(GetNCOFrequency(TRXDir::Rx, i, false)); for (int i = 0; i < 16 && txModeNCO == 0; ++i) @@ -1450,7 +1407,7 @@ OpStatus LMS7002M::SetFrequencyCGEN(const float_type freq_Hz, const bool retainN //recalculate NCO for (int ch = 0; ch < 2 && retainNCOfrequencies; ++ch) { - this->SetActiveChannel((ch == 0) ? Channel::ChA : Channel::ChB); + this->SetActiveChannel(IntToChannel(ch)); for (int i = 0; i < 16 && rxModeNCO == 0; ++i) SetNCOFrequency(TRXDir::Rx, i, rxNCO[ch][i]); for (int i = 0; i < 16 && txModeNCO == 0; ++i) @@ -1485,9 +1442,6 @@ bool LMS7002M::GetSXLocked(TRXDir dir) return (Get_SPI_Reg_bits(LMS7param(VCO_CMPHO).address, 13, 12, true) & 0x3) == 2; } -/** @brief Performs VCO tuning operations for CLKGEN - @return 0-success, other-failure -*/ OpStatus LMS7002M::TuneCGENVCO() { #ifndef NDEBUG @@ -1533,10 +1487,6 @@ OpStatus LMS7002M::TuneCGENVCO() return OpStatus::ERROR; } -/** @brief Performs VCO tuning operations for CLKGEN, SXR, SXT modules - @param module module selection for tuning 0-cgen, 1-SXR, 2-SXT - @return 0-success, other-failure -*/ OpStatus LMS7002M::TuneVCO(VCO_Module module) // 0-cgen, 1-SXR, 2-SXT { if (module == VCO_Module::VCO_CGEN) @@ -1555,7 +1505,7 @@ OpStatus LMS7002M::TuneVCO(VCO_Module module) // 0-cgen, 1-SXR, 2-SXT uint8_t lsb; //SWC lsb index uint8_t msb; //SWC msb index - Channel ch = this->GetActiveChannel(); //remember used channel + ChannelScope scope(this); if (module != VCO_Module::VCO_CGEN) //set addresses to SX module { @@ -1590,7 +1540,6 @@ OpStatus LMS7002M::TuneVCO(VCO_Module module) // 0-cgen, 1-SXR, 2-SXT cmphl = static_cast(Get_SPI_Reg_bits(addrCMP, 13, 12, true)); if (cmphl == 3) //VCO too high { - this->SetActiveChannel(ch); //restore previously used channel lime::debug("TuneVCO(%s) - attempted VCO too high", moduleName); return OpStatus::ERROR; } @@ -1599,7 +1548,6 @@ OpStatus LMS7002M::TuneVCO(VCO_Module module) // 0-cgen, 1-SXR, 2-SXT cmphl = static_cast(Get_SPI_Reg_bits(addrCMP, 13, 12, true)); if (cmphl == 0) //VCO too low { - this->SetActiveChannel(ch); //restore previously used channel lime::debug("TuneVCO(%s) - attempted VCO too low", moduleName); return OpStatus::ERROR; } @@ -1699,7 +1647,6 @@ OpStatus LMS7002M::TuneVCO(VCO_Module module) // 0-cgen, 1-SXR, 2-SXT } std::this_thread::sleep_for(settlingTime); cmphl = static_cast(Get_SPI_Reg_bits(addrCMP, 13, 12, true)); - this->SetActiveChannel(ch); //restore previously used channel if (cmphl == 2) { lime::debug("TuneVCO(%s) - confirmed lock with final csw=%i, cmphl=%i", moduleName, finalCSW, cmphl); @@ -1709,45 +1656,21 @@ OpStatus LMS7002M::TuneVCO(VCO_Module module) // 0-cgen, 1-SXR, 2-SXT return OpStatus::ERROR; } -/** @brief Returns given parameter value from chip register - @param param LMS7002M control parameter - @param fromChip read directly from chip - @return parameter value -*/ uint16_t LMS7002M::Get_SPI_Reg_bits(const LMS7Parameter& param, bool fromChip) { return Get_SPI_Reg_bits(param.address, param.msb, param.lsb, fromChip); } -/** @brief Returns given parameter value from chip register - @param address register address - @param msb most significant bit index - @param lsb least significant bit index - @param fromChip read directly from chip - @return register bits from selected interval, shifted to right by lsb bits -*/ uint16_t LMS7002M::Get_SPI_Reg_bits(uint16_t address, uint8_t msb, uint8_t lsb, bool fromChip) { return (SPI_read(address, fromChip) & (~(~0u << (msb + 1)))) >> lsb; //shift bits to LSB } -/** @brief Change given parameter value - @param param LMS7002M control parameter - @param fromChip read initial value directly from chip - @param value new parameter value -*/ OpStatus LMS7002M::Modify_SPI_Reg_bits(const LMS7Parameter& param, const uint16_t value, bool fromChip) { return Modify_SPI_Reg_bits(param.address, param.msb, param.lsb, value, fromChip); } -/** @brief Change given parameter value - @param address register address - @param msb Most significant byte - @param lsb Least significant byte - @param value new bits value, the value is shifted left by lsb bits - @param fromChip read initial value directly from chip -*/ OpStatus LMS7002M::Modify_SPI_Reg_bits( const uint16_t address, const uint8_t msb, const uint8_t lsb, const uint16_t value, bool fromChip) { @@ -1786,9 +1709,6 @@ OpStatus LMS7002M::Modify_SPI_Reg_mask( return status; } -/** @brief Get parameter by name - @param name parameter name -*/ const LMS7Parameter& LMS7002M::GetParam(const std::string& name) { for (const LMS7Parameter& parameter : LMS7parameterList) @@ -1802,12 +1722,6 @@ const LMS7Parameter& LMS7002M::GetParam(const std::string& name) throw std::logic_error("Parameter " + name + " not found"); } -/** @brief Sets SX frequency - @param dir Rx/Tx module selection - @param freq_Hz desired frequency in Hz - @param output if not null outputs intermediate calculation values - @return 0-success, other-cannot deliver requested frequency -*/ OpStatus LMS7002M::SetFrequencySX(TRXDir dir, float_type freq_Hz, SX_details* output) { static std::map tuning_cache_sel_vco; @@ -1850,7 +1764,7 @@ OpStatus LMS7002M::SetFrequencySX(TRXDir dir, float_type freq_Hz, SX_details* ou integerPart = static_cast(VCOfreq / divider - 4); fractionalPart = static_cast((VCOfreq / divider - static_cast(VCOfreq / divider)) * 1048576); - Channel ch = this->GetActiveChannel(); + ChannelScope scope(this); this->SetActiveChannel(dir == TRXDir::Tx ? Channel::ChSXT : Channel::ChSXR); Modify_SPI_Reg_bits(LMS7param(EN_INTONLY_SDM), 0); Modify_SPI_Reg_bits(LMS7param(INT_SDM), integerPart); //INT_SDM @@ -1896,7 +1810,6 @@ OpStatus LMS7002M::SetFrequencySX(TRXDir dir, float_type freq_Hz, SX_details* ou if (cmphl == 2) { lime::info("Fast Tune success; vco=%d value=%d", tuning_cache_sel_vco[freq_Hz], tuning_cache_csw_value[freq_Hz]); - this->SetActiveChannel(ch); //restore used channel if (output) { output->success = true; @@ -1973,20 +1886,12 @@ OpStatus LMS7002M::SetFrequencySX(TRXDir dir, float_type freq_Hz, SX_details* ou tuning_cache_csw_value[freq_Hz] = csw_value; } - this->SetActiveChannel(ch); //restore used channel - if (canDeliverFrequency == false) return ReportError( OpStatus::ERROR, "SetFrequencySX%s(%g MHz) - cannot deliver frequency", dir == TRXDir::Tx ? "T" : "R", freq_Hz / 1e6); return OpStatus::SUCCESS; } -/** @brief Sets SX frequency with Reference clock spur cancelation - @param dir Rx/Tx module selection - @param freq_Hz desired frequency in Hz - @param BW BW - @return 0-success, other-cannot deliver requested frequency -*/ OpStatus LMS7002M::SetFrequencySXWithSpurCancelation(TRXDir dir, float_type freq_Hz, float_type BW) { const float BWOffset = 2e6; @@ -2054,9 +1959,6 @@ OpStatus LMS7002M::SetFrequencySXWithSpurCancelation(TRXDir dir, float_type freq return OpStatus::SUCCESS; } -/** @brief Returns currently set SXR/SXT frequency - @return SX frequency Hz -*/ float_type LMS7002M::GetFrequencySX(TRXDir dir) { ChannelScope(this, dir == TRXDir::Tx ? Channel::ChSXT : Channel::ChSXR); @@ -2072,12 +1974,6 @@ float_type LMS7002M::GetFrequencySX(TRXDir dir) return dMul; } -/** @brief Sets chosen NCO's frequency - @param dir transmitter or receiver selection - @param index NCO index from 0 to 15 - @param freq_Hz desired NCO frequency - @return 0-success, other-failure -*/ OpStatus LMS7002M::SetNCOFrequency(TRXDir dir, uint8_t index, float_type freq_Hz) { if (index > 15) @@ -2096,12 +1992,6 @@ OpStatus LMS7002M::SetNCOFrequency(TRXDir dir, uint8_t index, float_type freq_Hz return OpStatus::SUCCESS; } -/** @brief Returns chosen NCO's frequency in Hz - @param dir transmitter or receiver selection - @param index NCO index from 0 to 15 - @param fromChip read frequency directly from chip or local registers - @return NCO frequency in Hz -*/ float_type LMS7002M::GetNCOFrequency(TRXDir dir, uint8_t index, bool fromChip) { if (index > 15) @@ -2114,11 +2004,6 @@ float_type LMS7002M::GetNCOFrequency(TRXDir dir, uint8_t index, bool fromChip) return refClk_Hz * (fcw / 4294967296.0); } -/** @brief Sets chosen NCO phase offset angle when memory table MODE is 0 -@param dir transmitter or receiver selection -@param angle_deg phase offset angle in degrees -@return 0-success, other-failure -*/ OpStatus LMS7002M::SetNCOPhaseOffsetForMode0(TRXDir dir, float_type angle_deg) { uint16_t addr = dir == TRXDir::Tx ? 0x0241 : 0x0441; @@ -2127,12 +2012,6 @@ OpStatus LMS7002M::SetNCOPhaseOffsetForMode0(TRXDir dir, float_type angle_deg) return OpStatus::SUCCESS; } -/** @brief Sets chosen NCO's phase offset angle - @param dir transmitter or receiver selection - @param index PHO index from 0 to 15 - @param angle_deg phase offset angle in degrees - @return 0-success, other-failure -*/ OpStatus LMS7002M::SetNCOPhaseOffset(TRXDir dir, uint8_t index, float_type angle_deg) { if (index > 15) @@ -2151,7 +2030,7 @@ OpStatus LMS7002M::SetNCOPhases(TRXDir dir, const float_type* angles_deg, uint8_ if (angles_deg != nullptr) { - for (unsigned i = 0; i < 16; i++) + for (uint8_t i = 0; i < 16 && i < count; i++) { status = SetNCOPhaseOffset(dir, i, angles_deg[i]); if (status != OpStatus::SUCCESS) @@ -2168,11 +2047,6 @@ std::vector LMS7002M::GetNCOPhases(TRXDir dir, float_type* frequency return angles_deg; } -/** @brief Returns chosen NCO's phase offset angle in radians - @param dir transmitter or receiver selection - @param index PHO index from 0 to 15 - @return phase offset angle in degrees -*/ float_type LMS7002M::GetNCOPhaseOffset_Deg(TRXDir dir, uint8_t index) { if (index > 15) @@ -2183,15 +2057,6 @@ float_type LMS7002M::GetNCOPhaseOffset_Deg(TRXDir dir, uint8_t index) return angle; } -/** @brief Uploads given FIR coefficients to chip - @param dir Transmitter or receiver selection - @param gfirIndex GIR index from 0 to 2 - @param coef array of coefficients (normalized from -1 to 1) - @param coefCount number of coefficients - @return 0-success, other-failure - - This function does not change GFIR*_L or GFIR*_N parameters, they have to be set manually -*/ OpStatus LMS7002M::SetGFIRCoefficients(TRXDir dir, uint8_t gfirIndex, const float_type* coef, uint8_t coefCount) { if (gfirIndex > 2) @@ -2249,13 +2114,6 @@ OpStatus LMS7002M::SetGFIRCoefficients(TRXDir dir, uint8_t gfirIndex, const floa return SPI_write_batch(addrs, reinterpret_cast(words), actualCoefCount, true); } -/** @brief Returns currently loaded FIR coefficients. - @param dir Transmitter or receiver selection. - @param GFIR_index GFIR index from 0 to 2. - @param coef Array of returned coefficients (normalized from -1 to 1) - @param coefCount Number of coefficients to read. - @return 0-success, other-failure. -*/ OpStatus LMS7002M::GetGFIRCoefficients(TRXDir dir, uint8_t gfirIndex, float_type* coef, uint8_t coefCount) { OpStatus status = OpStatus::ERROR; @@ -2305,12 +2163,6 @@ OpStatus LMS7002M::GetGFIRCoefficients(TRXDir dir, uint8_t gfirIndex, float_type return status; } -/** @brief Write given data value to whole register - @param address SPI address - @param data new register value - @param toChip whether we're writing to the chip or not - @return 0-succes, other-failure -*/ OpStatus LMS7002M::SPI_write(uint16_t address, uint16_t data, bool toChip) { if (address == 0x0640 || address == 0x0641) @@ -2329,23 +2181,18 @@ OpStatus LMS7002M::SPI_write(uint16_t address, uint16_t data, bool toChip) return this->SPI_write_batch(&address, &data, 1, toChip); } -/** @brief Reads whole register value from given address - @param address SPI address - @param status operation status(optional) - @param fromChip read value directly from chip - @return register value -*/ uint16_t LMS7002M::SPI_read(uint16_t address, bool fromChip, OpStatus* status) { fromChip |= !useCache; //registers containing read only registers, which values can change - static const std::unordered_set volatileRegs = { 0, - 1, - 2, - 3, - 4, - 5, - 6, + static const std::unordered_set volatileRegs = { + 0x0000, + 0x0001, + 0x0002, + 0x0003, + 0x0004, + 0x0005, + 0x0006, 0x002F, 0x008C, 0x00A8, @@ -2366,7 +2213,8 @@ uint16_t LMS7002M::SPI_read(uint16_t address, bool fromChip, OpStatus* status) 0x05C7, 0x05C8, 0x05C9, - 0x05CA }; + 0x05CA, + }; if (volatileRegs.find(address) != volatileRegs.end()) fromChip = true; @@ -2405,13 +2253,6 @@ uint16_t LMS7002M::SPI_read(uint16_t address, bool fromChip, OpStatus* status) return 0; } -/** @brief Batches multiple register writes into least amount of transactions - @param spiAddr spi register addresses to be written - @param spiData registers data to be written - @param cnt number of registers to write - @param toChip force write to chip - @return 0-success, other-failure -*/ OpStatus LMS7002M::SPI_write_batch(const uint16_t* spiAddr, const uint16_t* spiData, uint16_t cnt, bool toChip) { toChip |= !useCache; @@ -2457,12 +2298,6 @@ OpStatus LMS7002M::SPI_write_batch(const uint16_t* spiAddr, const uint16_t* spiD return OpStatus::SUCCESS; } -/** @brief Batches multiple register reads into least amount of transactions - @param spiAddr SPI addresses to read - @param spiData array for read data - @param cnt number of registers to read - @return 0-success, other-failure -*/ OpStatus LMS7002M::SPI_read_batch(const uint16_t* spiAddr, uint16_t* spiData, uint16_t cnt) { if (!controlPort) @@ -2498,9 +2333,6 @@ OpStatus LMS7002M::SPI_read_batch(const uint16_t* spiAddr, uint16_t* spiData, ui return OpStatus::SUCCESS; } -/** @brief Performs registers test by writing known data and confirming readback data - @return 0-registers test passed, other-failure -*/ OpStatus LMS7002M::RegistersTest(const std::string& fileName) { char chex[16]; @@ -2508,12 +2340,12 @@ OpStatus LMS7002M::RegistersTest(const std::string& fileName) return ReportError(OpStatus::IO_FAILURE, "No device connected"); OpStatus status; - Channel ch = this->GetActiveChannel(); + ChannelScope scope(this); //backup both channel data for restoration after test std::vector ch1Addresses; - for (uint8_t i = 0; i < MEMORY_SECTIONS_COUNT; ++i) - for (uint16_t addr = MemorySectionAddresses[i][0]; addr <= MemorySectionAddresses[i][1]; ++addr) + for (const auto& memorySectionPair : MemorySectionAddresses) + for (uint16_t addr = memorySectionPair.second[0]; addr <= memorySectionPair.second[1]; ++addr) ch1Addresses.push_back(addr); std::vector ch1Data; ch1Data.resize(ch1Addresses.size(), 0); @@ -2525,8 +2357,8 @@ OpStatus LMS7002M::RegistersTest(const std::string& fileName) return status; std::vector ch2Addresses; - for (uint8_t i = 0; i < MEMORY_SECTIONS_COUNT; ++i) - for (uint16_t addr = MemorySectionAddresses[i][0]; addr <= MemorySectionAddresses[i][1]; ++addr) + for (const auto& memorySectionPair : MemorySectionAddresses) + for (uint16_t addr = memorySectionPair.second[0]; addr <= memorySectionPair.second[1]; ++addr) if (addr >= 0x0100) ch2Addresses.push_back(addr); std::vector ch2Data; @@ -2547,34 +2379,37 @@ OpStatus LMS7002M::RegistersTest(const std::string& fileName) std::stringstream ss; //check single channel memory sections - std::vector modulesToCheck = { AFE, - BIAS, - XBUF, - CGEN, - BIST, - CDS, - TRF, - TBB, - RFE, - RBB, - SX, - TxTSP, - TxNCO, - TxGFIR1, - TxGFIR2, - TxGFIR3a, - TxGFIR3b, - TxGFIR3c, - RxTSP, - RxNCO, - RxGFIR1, - RxGFIR2, - RxGFIR3a, - RxGFIR3b, - RxGFIR3c, - LimeLight, - LDO }; - const std::string moduleNames[] = { "AFE", + std::vector modulesToCheck = { + MemorySection::AFE, + MemorySection::BIAS, + MemorySection::XBUF, + MemorySection::CGEN, + MemorySection::BIST, + MemorySection::CDS, + MemorySection::TRF, + MemorySection::TBB, + MemorySection::RFE, + MemorySection::RBB, + MemorySection::SX, + MemorySection::TxTSP, + MemorySection::TxNCO, + MemorySection::TxGFIR1, + MemorySection::TxGFIR2, + MemorySection::TxGFIR3a, + MemorySection::TxGFIR3b, + MemorySection::TxGFIR3c, + MemorySection::RxTSP, + MemorySection::RxNCO, + MemorySection::RxGFIR1, + MemorySection::RxGFIR2, + MemorySection::RxGFIR3a, + MemorySection::RxGFIR3b, + MemorySection::RxGFIR3c, + MemorySection::LimeLight, + MemorySection::LDO, + }; + const std::string moduleNames[] = { + "AFE", "BIAS", "XBUF", "CGEN", @@ -2600,7 +2435,8 @@ OpStatus LMS7002M::RegistersTest(const std::string& fileName) "RxGFIR3b", "RxGFIR3c", "LimeLight", - "LDO" }; + "LDO", + }; const uint16_t patterns[] = { 0xAAAA, 0x5555 }; const uint8_t patternsCount = 2; @@ -2610,8 +2446,8 @@ OpStatus LMS7002M::RegistersTest(const std::string& fileName) for (unsigned i = 0; i < modulesToCheck.size(); ++i) { bool moduleTestsSuccess = true; - uint16_t startAddr = MemorySectionAddresses[modulesToCheck[i]][0]; - uint16_t endAddr = MemorySectionAddresses[modulesToCheck[i]][1]; + uint16_t startAddr = MemorySectionAddresses.at(modulesToCheck[i]).at(0); + uint16_t endAddr = MemorySectionAddresses.at(modulesToCheck[i]).at(1); uint8_t channelCount = startAddr >= 0x0100 ? 2 : 1; for (int cc = 1; cc <= channelCount; ++cc) { @@ -2636,7 +2472,6 @@ OpStatus LMS7002M::RegistersTest(const std::string& fileName) SPI_write_batch(&ch1Addresses[0], &ch1Data[0], ch1Addresses.size(), true); this->SetActiveChannel(Channel::ChB); SPI_write_batch(&ch2Addresses[0], &ch2Data[0], ch2Addresses.size(), true); - this->SetActiveChannel(ch); if (!fileName.empty()) { @@ -2741,15 +2576,12 @@ void LMS7002M::SetRxDCOFF(int8_t offsetI, int8_t offsetQ) SPI_write(0x010E, valToSend); } -/** @brief Sets given module registers to default values - @return 0-success, other-failure -*/ OpStatus LMS7002M::SetDefaults(MemorySection module) { OpStatus status; std::vector addrs; std::vector values; - for (uint32_t address = MemorySectionAddresses[module][0]; address <= MemorySectionAddresses[module][1]; ++address) + for (uint16_t address = MemorySectionAddresses.at(module).at(0); address <= MemorySectionAddresses.at(module).at(1); ++address) { addrs.push_back(address); values.push_back(mRegistersMap->GetDefaultValue(address)); @@ -2764,15 +2596,12 @@ void LMS7002M::ModifyRegistersDefaults(const std::vectorSetDefaultValue(addrValuePair.first, addrValuePair.second); } -/** @brief Reads all chip configuration and checks if it matches with local registers copy -*/ bool LMS7002M::IsSynced() { if (!controlPort) return false; - bool isSynced = true; - Channel ch = this->GetActiveChannel(); + ChannelScope scope(this); std::vector addrToRead = mRegistersMap->GetUsedAddresses(0); std::vector dataReceived; @@ -2809,8 +2638,7 @@ bool LMS7002M::IsSynced() if (dataReceived[i] != regValue) { lime::debug("Addr: 0x%04X gui: 0x%04X chip: 0x%04X", addrToRead[i], regValue, dataReceived[i]); - isSynced = false; - goto isSyncedEnding; + return false; } } @@ -2846,24 +2674,19 @@ bool LMS7002M::IsSynced() if (dataReceived[i] != regValue) { lime::debug("Addr: 0x%04X gui: 0x%04X chip: 0x%04X", addrToRead[i], regValue, dataReceived[i]); - isSynced = false; - goto isSyncedEnding; + return false; } } -isSyncedEnding: - this->SetActiveChannel(ch); //restore previously used channel - return isSynced; -} -/** @brief Writes all registers from host to chip + return true; +} -*/ OpStatus LMS7002M::UploadAll() { if (!controlPort) return ReportError(OpStatus::IO_FAILURE, "No device connected"); - Channel ch = this->GetActiveChannel(); //remember used channel + ChannelScope scope(this); OpStatus status; @@ -2900,21 +2723,17 @@ OpStatus LMS7002M::UploadAll() status = SPI_write_batch(&addrToWrite[0], &dataToWrite[0], addrToWrite.size(), true); if (status != OpStatus::SUCCESS) return status; - this->SetActiveChannel(ch); //restore last used channel return OpStatus::SUCCESS; } -/** @brief Reads all registers from the chip to host - -*/ OpStatus LMS7002M::DownloadAll() { if (!controlPort) return ReportError(OpStatus::IO_FAILURE, "No device connected"); OpStatus status; - Channel ch = this->GetActiveChannel(false); + ChannelScope scope(this, true); std::vector addrToRead = mRegistersMap->GetUsedAddresses(0); std::vector dataReceived; @@ -2940,15 +2759,9 @@ OpStatus LMS7002M::DownloadAll() for (uint16_t i = 0; i < addrToRead.size(); ++i) mRegistersMap->SetValue(1, addrToRead[i], dataReceived[i]); - this->SetActiveChannel(ch); //retore previously used channel - return OpStatus::SUCCESS; } -/** @brief Configures interfaces for desired frequency - @return 0-success, other-failure - Sets interpolation and decimation, changes MCLK sources and TSP clock dividers accordingly to selected interpolation and decimation -*/ OpStatus LMS7002M::SetInterfaceFrequency(float_type cgen_freq_Hz, const uint8_t hbi, const uint8_t hbd) { OpStatus status; @@ -3025,46 +2838,48 @@ void LMS7002M::ConfigureLML_RF2BB( const LMLSampleSource s0, const LMLSampleSource s1, const LMLSampleSource s2, const LMLSampleSource s3) { //map a sample source to a position - std::map m; - m[LMLSampleSource::AI] = 1; - m[LMLSampleSource::AQ] = 0; - m[LMLSampleSource::BI] = 3; - m[LMLSampleSource::BQ] = 2; + const std::map m{ + { LMLSampleSource::AI, 1 }, + { LMLSampleSource::AQ, 0 }, + { LMLSampleSource::BI, 3 }, + { LMLSampleSource::BQ, 2 }, + }; //load the same config on both LMLs //only one will get used based on direction - this->Modify_SPI_Reg_bits(LMS7param(LML1_S3S), m[s3]); - this->Modify_SPI_Reg_bits(LMS7param(LML1_S2S), m[s2]); - this->Modify_SPI_Reg_bits(LMS7param(LML1_S1S), m[s1]); - this->Modify_SPI_Reg_bits(LMS7param(LML1_S0S), m[s0]); + this->Modify_SPI_Reg_bits(LMS7param(LML1_S3S), m.at(s3)); + this->Modify_SPI_Reg_bits(LMS7param(LML1_S2S), m.at(s2)); + this->Modify_SPI_Reg_bits(LMS7param(LML1_S1S), m.at(s1)); + this->Modify_SPI_Reg_bits(LMS7param(LML1_S0S), m.at(s0)); - this->Modify_SPI_Reg_bits(LMS7param(LML2_S3S), m[s3]); - this->Modify_SPI_Reg_bits(LMS7param(LML2_S2S), m[s2]); - this->Modify_SPI_Reg_bits(LMS7param(LML2_S1S), m[s1]); - this->Modify_SPI_Reg_bits(LMS7param(LML2_S0S), m[s0]); + this->Modify_SPI_Reg_bits(LMS7param(LML2_S3S), m.at(s3)); + this->Modify_SPI_Reg_bits(LMS7param(LML2_S2S), m.at(s2)); + this->Modify_SPI_Reg_bits(LMS7param(LML2_S1S), m.at(s1)); + this->Modify_SPI_Reg_bits(LMS7param(LML2_S0S), m.at(s0)); } void LMS7002M::ConfigureLML_BB2RF( const LMLSampleSource s0, const LMLSampleSource s1, const LMLSampleSource s2, const LMLSampleSource s3) { //map a sample source to a position - std::map m; - m[s3] = 2; - m[s2] = 3; - m[s0] = 1; - m[s1] = 0; + const std::map m{ + { s3, 2 }, + { s2, 3 }, + { s0, 1 }, + { s1, 0 }, + }; //load the same config on both LMLs //only one will get used based on direction - this->Modify_SPI_Reg_bits(LMS7param(LML1_BQP), m[LMLSampleSource::BQ]); - this->Modify_SPI_Reg_bits(LMS7param(LML1_BIP), m[LMLSampleSource::BI]); - this->Modify_SPI_Reg_bits(LMS7param(LML1_AQP), m[LMLSampleSource::AQ]); - this->Modify_SPI_Reg_bits(LMS7param(LML1_AIP), m[LMLSampleSource::AI]); + this->Modify_SPI_Reg_bits(LMS7param(LML1_BQP), m.at(LMLSampleSource::BQ)); + this->Modify_SPI_Reg_bits(LMS7param(LML1_BIP), m.at(LMLSampleSource::BI)); + this->Modify_SPI_Reg_bits(LMS7param(LML1_AQP), m.at(LMLSampleSource::AQ)); + this->Modify_SPI_Reg_bits(LMS7param(LML1_AIP), m.at(LMLSampleSource::AI)); - this->Modify_SPI_Reg_bits(LMS7param(LML2_BQP), m[LMLSampleSource::BQ]); - this->Modify_SPI_Reg_bits(LMS7param(LML2_BIP), m[LMLSampleSource::BI]); - this->Modify_SPI_Reg_bits(LMS7param(LML2_AQP), m[LMLSampleSource::AQ]); - this->Modify_SPI_Reg_bits(LMS7param(LML2_AIP), m[LMLSampleSource::AI]); + this->Modify_SPI_Reg_bits(LMS7param(LML2_BQP), m.at(LMLSampleSource::BQ)); + this->Modify_SPI_Reg_bits(LMS7param(LML2_BIP), m.at(LMLSampleSource::BI)); + this->Modify_SPI_Reg_bits(LMS7param(LML2_AQP), m.at(LMLSampleSource::AQ)); + this->Modify_SPI_Reg_bits(LMS7param(LML2_AIP), m.at(LMLSampleSource::AI)); } OpStatus LMS7002M::SetRxDCRemoval(const bool enable) @@ -3159,7 +2974,7 @@ void LMS7002M::EnableValuesCache(bool enabled) useCache = enabled; } -bool LMS7002M::IsValuesCacheEnabled() +bool LMS7002M::IsValuesCacheEnabled() const { return useCache; } @@ -3169,11 +2984,6 @@ MCU_BD* LMS7002M::GetMCUControls() const return mcuControl; } -void LMS7002M::EnableCalibrationByMCU(bool enabled) -{ - mCalibrationByMCU = enabled; -} - float_type LMS7002M::GetTemperature() { if (CalibrateInternalADC(32) != OpStatus::SUCCESS) @@ -3204,13 +3014,14 @@ void LMS7002M::SetLogCallback(std::function callback OpStatus LMS7002M::CopyChannelRegisters(const Channel src, const Channel dest, const bool copySX) { - Channel ch = this->GetActiveChannel(); //remember used channel + ChannelScope scope(this); std::vector addrToWrite; addrToWrite = mRegistersMap->GetUsedAddresses(1); if (!copySX) { - for (uint32_t address = MemorySectionAddresses[SX][0]; address <= MemorySectionAddresses[SX][1]; ++address) + const auto& SXMemoryAddresses = MemorySectionAddresses.at(MemorySection::SX); + for (uint32_t address = SXMemoryAddresses.at(0); address <= SXMemoryAddresses.at(1); ++address) addrToWrite.erase(std::find(addrToWrite.begin(), addrToWrite.end(), address)); } for (auto address : addrToWrite) @@ -3220,7 +3031,7 @@ OpStatus LMS7002M::CopyChannelRegisters(const Channel src, const Channel dest, c } if (controlPort) UploadAll(); - this->SetActiveChannel(ch); + return OpStatus::SUCCESS; } @@ -3277,7 +3088,7 @@ OpStatus LMS7002M::CalibrateAnalogRSSI_DC_Offset() return OpStatus::SUCCESS; } -double LMS7002M::GetClockFreq(ClockID clk_id, uint8_t channel) +double LMS7002M::GetClockFreq(ClockID clk_id) { switch (clk_id) { @@ -3299,7 +3110,7 @@ double LMS7002M::GetClockFreq(ClockID clk_id, uint8_t channel) } } -OpStatus LMS7002M::SetClockFreq(ClockID clk_id, double freq, uint8_t channel) +OpStatus LMS7002M::SetClockFreq(ClockID clk_id, double freq) { switch (clk_id) { @@ -3347,7 +3158,7 @@ float_type LMS7002M::GetSampleRate(TRXDir dir) return interface_Hz; } -OpStatus LMS7002M::SetGFIRFilter(TRXDir dir, unsigned ch, bool enabled, double bandwidth) +OpStatus LMS7002M::SetGFIRFilter(TRXDir dir, Channel ch, bool enabled, double bandwidth) { ChannelScope scope(this, ch); const bool bypassFIR = !enabled; @@ -3364,7 +3175,7 @@ OpStatus LMS7002M::SetGFIRFilter(TRXDir dir, unsigned ch, bool enabled, double b Modify_SPI_Reg_bits(LMS7param(GFIR3_BYP_RXTSP), bypassFIR); const bool sisoDDR = Get_SPI_Reg_bits(LMS7_LML1_SISODDR); const bool clockIsNotInverted = !(enabled | sisoDDR); - if (ch % 2) + if (ch == LMS7002M::Channel::ChB) { Modify_SPI_Reg_bits(LMS7param(CDSN_RXBLML), clockIsNotInverted); Modify_SPI_Reg_bits(LMS7param(CDS_RXBLML), enabled ? 3 : 0); @@ -3455,7 +3266,7 @@ OpStatus LMS7002M::SetGFIRFilter(TRXDir dir, unsigned ch, bool enabled, double b ss << std::endl; lime::info(ss.str()); - return ResetLogicregisters(); + return ResetLogicRegisters(); } void LMS7002M::SetOnCGENChangeCallback(CGENChangeCallbackType callback, void* userData) diff --git a/src/lms7002m/LMS7002M_RxTxCalibrations.cpp b/src/lms7002m/LMS7002M_RxTxCalibrations.cpp index f8709f6e6..6011d5606 100644 --- a/src/lms7002m/LMS7002M_RxTxCalibrations.cpp +++ b/src/lms7002m/LMS7002M_RxTxCalibrations.cpp @@ -159,7 +159,7 @@ static int SetExtLoopback(IConnection* port, uint8_t ch, bool enable, bool tx) */ /** @brief Flips the CAPTURE bit and returns digital RSSI value */ -uint32_t LMS7002M::GetRSSI(RSSI_measurements* measurements) +uint32_t LMS7002M::GetRSSI() { //delay to make sure RSSI gets enough samples to refresh before reading it this_thread::sleep_for(chrono::microseconds(50)); @@ -169,9 +169,6 @@ uint32_t LMS7002M::GetRSSI(RSSI_measurements* measurements) return rssi; } -/** @brief Calibrates Transmitter. DC correction, IQ gains, IQ phase correction -@return 0-success, other-failure -*/ OpStatus LMS7002M::CalibrateTx(float_type bandwidth_Hz, bool useExtLoopback) { if (TrxCalib_RF_LimitLow > bandwidth_Hz) @@ -267,9 +264,6 @@ OpStatus LMS7002M::CalibrateTx(float_type bandwidth_Hz, bool useExtLoopback) return OpStatus::SUCCESS; } -/** @brief Calibrates Receiver. DC offset, IQ gains, IQ phase correction - @return 0-success, other-failure -*/ OpStatus LMS7002M::CalibrateRx(float_type bandwidth_Hz, bool useExtLoopback) { if (TrxCalib_RF_LimitLow > bandwidth_Hz) @@ -390,11 +384,6 @@ OpStatus LMS7002M::CalibrateRx(float_type bandwidth_Hz, bool useExtLoopback) return OpStatus::SUCCESS; } -/** @brief Loads given DC_REG values into registers - @param dir TxTSP or RxTSP selection - @param I DC_REG I value - @param Q DC_REG Q value -*/ OpStatus LMS7002M::LoadDC_REG_IQ(TRXDir dir, int16_t I, int16_t Q) { if (dir == TRXDir::Tx) diff --git a/src/lms7002m/LMS7002M_gainCalibrations.cpp b/src/lms7002m/LMS7002M_gainCalibrations.cpp index c811758f9..34f046585 100644 --- a/src/lms7002m/LMS7002M_gainCalibrations.cpp +++ b/src/lms7002m/LMS7002M_gainCalibrations.cpp @@ -15,8 +15,8 @@ OpStatus LMS7002M::CalibrateTxGainSetup() SPI_write(0x0020, value); //RxTSP - SetDefaults(RxTSP); - SetDefaults(RxNCO); + SetDefaults(MemorySection::RxTSP); + SetDefaults(MemorySection::RxNCO); Modify_SPI_Reg_bits(LMS7param(AGC_MODE_RXTSP), 1); Modify_SPI_Reg_bits(LMS7param(AGC_AVG_RXTSP), 1); Modify_SPI_Reg_bits(LMS7param(HBD_OVR_RXTSP), 1); @@ -31,7 +31,7 @@ OpStatus LMS7002M::CalibrateTxGainSetup() Modify_SPI_Reg_bits(0x010D, 4, 1, 0xF); //RBB - SetDefaults(RBB); + SetDefaults(MemorySection::RBB); Modify_SPI_Reg_bits(LMS7param(PD_LPFL_RBB), 1); Modify_SPI_Reg_bits(LMS7param(INPUT_CTL_PGA_RBB), 3); Modify_SPI_Reg_bits(LMS7param(G_PGA_RBB), 12); @@ -42,7 +42,7 @@ OpStatus LMS7002M::CalibrateTxGainSetup() //AFE const int isel_dac_afe = Get_SPI_Reg_bits(LMS7param(ISEL_DAC_AFE)); - SetDefaults(AFE); + SetDefaults(MemorySection::AFE); Modify_SPI_Reg_bits(LMS7param(ISEL_DAC_AFE), isel_dac_afe); if (ch == 2) { @@ -52,7 +52,7 @@ OpStatus LMS7002M::CalibrateTxGainSetup() //BIAS const int rp_calib_bias = Get_SPI_Reg_bits(LMS7param(RP_CALIB_BIAS)); - SetDefaults(BIAS); + SetDefaults(MemorySection::BIAS); Modify_SPI_Reg_bits(LMS7param(RP_CALIB_BIAS), rp_calib_bias); //LDO @@ -62,7 +62,7 @@ OpStatus LMS7002M::CalibrateTxGainSetup() //use configured xbuf settings //CGEN - SetDefaults(CGEN); + SetDefaults(MemorySection::CGEN); status = SetFrequencyCGEN(61.44e6); if (status != OpStatus::SUCCESS) return status; @@ -77,8 +77,8 @@ OpStatus LMS7002M::CalibrateTxGainSetup() const int isinc = Get_SPI_Reg_bits(LMS7param(ISINC_BYP_TXTSP)); const int txcmixGainLSB = Get_SPI_Reg_bits(LMS7param(CMIX_GAIN_TXTSP)); const int txcmixGainMSB = Get_SPI_Reg_bits(LMS7param(CMIX_GAIN_TXTSP_R3)); - SetDefaults(TxTSP); - SetDefaults(TxNCO); + SetDefaults(MemorySection::TxTSP); + SetDefaults(MemorySection::TxNCO); Modify_SPI_Reg_bits(LMS7param(CMIX_GAIN_TXTSP), txcmixGainLSB); Modify_SPI_Reg_bits(LMS7param(CMIX_GAIN_TXTSP_R3), txcmixGainMSB); Modify_SPI_Reg_bits(LMS7param(ISINC_BYP_TXTSP), isinc); @@ -97,7 +97,7 @@ OpStatus LMS7002M::CalibrateTxGainSetup() return OpStatus::SUCCESS; } -OpStatus LMS7002M::CalibrateTxGain(float maxGainOffset_dBFS, float* actualGain_dBFS) +OpStatus LMS7002M::CalibrateTxGain() { if (!controlPort) { diff --git a/src/lms7002m/MCU_BD.cpp b/src/lms7002m/MCU_BD.cpp index 63321e9cd..1c5f00e15 100644 --- a/src/lms7002m/MCU_BD.cpp +++ b/src/lms7002m/MCU_BD.cpp @@ -471,7 +471,9 @@ void MCU_BD::Wait_CLK_Cycles(int delay) } /** @brief Upload program code from memory into MCU - @return 0:success, -1:failed + * @param m_iMode1 The high bit of the mode + * @param m_iMode0 The low bit of the mode + * @return 0:success, -1:failed */ int MCU_BD::Program_MCU(int m_iMode1, int m_iMode0) { @@ -923,7 +925,9 @@ int MCU_BD::RunInstr_MCU(unsigned short* pPCVAL) return retval; } -/** @brief Returns information about programming or reading data progress +/** + * @brief Returns information about programming or reading data progress + * @returns The structure containing information about the progress info. */ MCU_BD::ProgressInfo MCU_BD::GetProgressInfo() const { @@ -958,6 +962,7 @@ std::string MCU_BD::GetProgramFilename() const } /** @brief Starts algorithm in MCU + * @param id The ID of the procedure to execute */ void MCU_BD::RunProcedure(uint8_t id) { @@ -974,7 +979,8 @@ void MCU_BD::RunProcedure(uint8_t id) } /** @brief Waits for MCU to finish executing program -@return 0 success, 255 idle, 244 running, else algorithm status + * @param timeout_ms The timeout to wait for (in ms) + * @return 0 success, 255 idle, 244 running, else algorithm status */ int MCU_BD::WaitForMCU(uint32_t timeout_ms) { diff --git a/src/lms7002m/MCU_BD.h b/src/lms7002m/MCU_BD.h index 43249d264..7295e99af 100644 --- a/src/lms7002m/MCU_BD.h +++ b/src/lms7002m/MCU_BD.h @@ -106,11 +106,11 @@ class LIME_API MCU_BD OperationStatus writeIRAM(const uint8_t* addr, const uint8_t* values, const uint8_t count); void Wait_CLK_Cycles(int data); - // The IRAM content + /// The IRAM content unsigned char m_IRAM[256]; - // The SFR content + /// The SFR content unsigned char m_SFR[256]; - // The program memory code + /// The program memory code unsigned char byte_array[MCU_PROGRAM_SIZE]; void mSPI_write(unsigned short addr_reg, unsigned short data_reg); diff --git a/src/mcu_program/common_src/lms7002m_filters.c b/src/mcu_program/common_src/lms7002m_filters.c index 0510aa2da..83cf5c5de 100644 --- a/src/mcu_program/common_src/lms7002m_filters.c +++ b/src/mcu_program/common_src/lms7002m_filters.c @@ -867,5 +867,5 @@ TxFilterSearchEndStage : { } #ifdef __cplusplus -} +} // extern C #endif diff --git a/src/memory/MemoryPool.cpp b/src/memory/MemoryPool.cpp index 35a50a66a..0f85253bf 100644 --- a/src/memory/MemoryPool.cpp +++ b/src/memory/MemoryPool.cpp @@ -8,6 +8,12 @@ #include "Logger.h" namespace lime { + +/// @brief Constructs the Memory Pool and allocates the memory of the pool. +/// @param blockCount The amount of memory blocks to allocate. +/// @param blockSize The memory size of a single block. +/// @param alignment The alignment of the memory. +/// @param name The name of the memory pool. MemoryPool::MemoryPool(int blockCount, int blockSize, int alignment, const std::string& name) : name(name) , allocCnt(0) @@ -17,7 +23,7 @@ MemoryPool::MemoryPool(int blockCount, int blockSize, int alignment, const std:: for (int i = 0; i < blockCount; ++i) { #if __unix__ - void* ptr = aligned_alloc(alignment, blockSize); + void* ptr = std::aligned_alloc(alignment, blockSize); #else void* ptr = _aligned_malloc(blockSize, alignment); #endif @@ -31,6 +37,7 @@ MemoryPool::MemoryPool(int blockCount, int blockSize, int alignment, const std:: ownedAddresses.insert(ptr); } } + MemoryPool::~MemoryPool() { // if(mFreeBlocks.size() != ownedAddresses.size()) @@ -56,6 +63,9 @@ MemoryPool::~MemoryPool() } } +/// @brief Gives a block of memory of a given size. +/// @param size The size of the memory to give. Must not be more than the maximum size. +/// @return The pointer to the allocated memory. void* MemoryPool::Allocate(int size) { if (size > mBlockSize) @@ -77,6 +87,8 @@ void* MemoryPool::Allocate(int size) return ptr; } +/// @brief Frees the given memory location. +/// @param ptr The pointer of the memory to free. Must belong to this memory pool. void MemoryPool::Free(void* ptr) { std::lock_guard lock(mLock); diff --git a/src/memory/MemoryPool.h b/src/memory/MemoryPool.h index 34470f9d9..4869b06ab 100644 --- a/src/memory/MemoryPool.h +++ b/src/memory/MemoryPool.h @@ -20,7 +20,10 @@ class MemoryPool void* Allocate(int size); void Free(void* ptr); - int32_t MaxAllocSize() const { return mBlockSize; }; + + /// @brief Gets the maximum possible allocation size of this memory pool. + /// @return The maximum amount of memory (in bytes) this pool can allocate. + constexpr int32_t MaxAllocSize() const { return mBlockSize; }; private: std::string name; diff --git a/src/protocols/BufferInterleaving.cpp b/src/protocols/BufferInterleaving.cpp index cff892542..db2b5c1dc 100644 --- a/src/protocols/BufferInterleaving.cpp +++ b/src/protocols/BufferInterleaving.cpp @@ -6,7 +6,7 @@ namespace lime { template -static int DeinterleaveMIMO(DestT** dest, const uint8_t* buffer, uint32_t length, const DataConversion& fmt) +static int DeinterleaveMIMO(DestT* const* dest, const uint8_t* buffer, uint32_t length, const DataConversion& fmt) { int samplesProduced = length / sizeof(SrcT); const bool mimo = fmt.channelCount > 1; @@ -21,7 +21,7 @@ static int DeinterleaveMIMO(DestT** dest, const uint8_t* buffer, uint32_t length } template -static int DeinterleaveCompressionType(DestT** dest, const uint8_t* buffer, uint32_t length, const DataConversion& fmt) +static int DeinterleaveCompressionType(DestT* const* dest, const uint8_t* buffer, uint32_t length, const DataConversion& fmt) { const bool compressed = fmt.srcFormat == SDRDevice::StreamConfig::DataFormat::I12; if (!compressed) @@ -30,20 +30,23 @@ static int DeinterleaveCompressionType(DestT** dest, const uint8_t* buffer, uint return DeinterleaveMIMO(dest, buffer, length, fmt); } -int Deinterleave(void** dest, const uint8_t* buffer, uint32_t length, const DataConversion& fmt) +int Deinterleave(void* const* dest, const uint8_t* buffer, uint32_t length, const DataConversion& fmt) { int samplesProduced; switch (fmt.destFormat) { default: case SDRDevice::StreamConfig::DataFormat::I16: - samplesProduced = DeinterleaveCompressionType(reinterpret_cast(dest), buffer, length, fmt); + samplesProduced = + DeinterleaveCompressionType(reinterpret_cast(dest), buffer, length, fmt); break; case SDRDevice::StreamConfig::DataFormat::F32: - samplesProduced = DeinterleaveCompressionType(reinterpret_cast(dest), buffer, length, fmt); + samplesProduced = + DeinterleaveCompressionType(reinterpret_cast(dest), buffer, length, fmt); break; case SDRDevice::StreamConfig::DataFormat::I12: - samplesProduced = DeinterleaveCompressionType(reinterpret_cast(dest), buffer, length, fmt); + samplesProduced = + DeinterleaveCompressionType(reinterpret_cast(dest), buffer, length, fmt); break; } return samplesProduced; diff --git a/src/protocols/BufferInterleaving.h b/src/protocols/BufferInterleaving.h index 58a0966fe..f687023ab 100644 --- a/src/protocols/BufferInterleaving.h +++ b/src/protocols/BufferInterleaving.h @@ -11,7 +11,7 @@ struct DataConversion { uint8_t channelCount; }; -int Deinterleave(void** dest, const uint8_t* buffer, uint32_t length, const DataConversion& fmt); +int Deinterleave(void* const* dest, const uint8_t* buffer, uint32_t length, const DataConversion& fmt); int Interleave(uint8_t* dest, const void* const* src, uint32_t count, const DataConversion& fmt); } // namespace lime diff --git a/src/protocols/ISerialPort.h b/src/protocols/ISerialPort.h index 0b1ce4c96..6ea0cedbf 100644 --- a/src/protocols/ISerialPort.h +++ b/src/protocols/ISerialPort.h @@ -10,7 +10,24 @@ namespace lime { class ISerialPort { public: + /** + @brief Writes the specified data into whatever device is implementing this interface. + + @param data The data to write to the device. + @param length The length of the data. + @param timeout_ms The timeout (in ms) to wait until the transfer times out. + @return The amount of bytes written. + */ virtual int Write(const uint8_t* data, std::size_t length, int timeout_ms) = 0; + + /** + @brief Reads some data from the device. + + @param data The buffer in which to store the read data. + @param length The length of the data to store. + @param timeout_ms The timeout (in ms) to wait until the transfer times out. + @return The amount of bytes read. + */ virtual int Read(uint8_t* data, std::size_t length, int timeout_ms) = 0; }; diff --git a/src/protocols/PacketsFIFO.h b/src/protocols/PacketsFIFO.h index 526f2cbe7..1f9a7422d 100644 --- a/src/protocols/PacketsFIFO.h +++ b/src/protocols/PacketsFIFO.h @@ -38,35 +38,33 @@ namespace lime { template class PacketsFIFO { public: - std::condition_variable canRead; - std::condition_variable canWrite; - std::mutex mwr; - std::mutex mrd; - ///--------------------------------------------------------------------------- - /// @brief Constructor. Asserts when the underlying type is not lock free - PacketsFIFO(size_t fixedSize) + /// @brief Constructor. Asserts when the underlying type is not lock free. + /// @param fixedSize The maximum size of the queue. + PacketsFIFO(std::size_t fixedSize) : RingBufferSize(fixedSize + 1) { m_ringBuffer.resize(RingBufferSize); #ifndef NDEBUG - std::atomic test; + std::atomic test; assert(test.is_lock_free()); #endif } PacketsFIFO(const PacketsFIFO& src) = delete; - virtual ~PacketsFIFO() {} + virtual ~PacketsFIFO() + { + } ///--------------------------------------------------------------------------- - /// @brief Returns whether the queue is empty - /// @return True when empty + /// @brief Returns whether the queue is empty. + /// @return True when empty. bool empty() const noexcept { bool isEmpty = false; - const size_t readPosition = m_readPosition.load(); - const size_t writePosition = m_writePosition.load(); + const std::size_t readPosition = m_readPosition.load(); + const std::size_t writePosition = m_writePosition.load(); if (readPosition == writePosition) { @@ -77,17 +75,17 @@ template class PacketsFIFO } ///--------------------------------------------------------------------------- - /// @brief Pushes an element to the queue - /// @param element The element to add - /// @param wait Whether to wait or now - /// @param timeout The timeout (in ms) to wait for - /// @return True when the element was added, false when the queue is full + /// @brief Pushes an element to the queue. + /// @param element The element to add. + /// @param wait Whether to wait or now. + /// @param timeout The timeout (in ms) to wait for. + /// @return True when the element was added, false when the queue is full. bool push(const T element, bool wait = false, int timeout = 250) { std::unique_lock lk(mwr); - const size_t oldWritePosition = m_writePosition.load(); - const size_t newWritePosition = getPositionAfter(oldWritePosition); - const size_t readPosition = m_readPosition.load(); + const std::size_t oldWritePosition = m_writePosition.load(); + const std::size_t newWritePosition = getPositionAfter(oldWritePosition); + const std::size_t readPosition = m_readPosition.load(); if (newWritePosition == readPosition) { @@ -114,11 +112,11 @@ template class PacketsFIFO } ///--------------------------------------------------------------------------- - /// @brief Pops an element from the queue - /// @param element The returned element - /// @param wait Whether to wait or now - /// @param timeout The timeout (in ms) to wait for - /// @return True when succeeded, false when the queue is empty + /// @brief Pops an element from the queue. + /// @param element The returned element. + /// @param wait Whether to wait or now. + /// @param timeout The timeout (in ms) to wait for. + /// @return True when succeeded, false when the queue is empty. bool pop(T* element, bool wait = false, int timeout = 250) { std::unique_lock lk(mwr); @@ -141,7 +139,7 @@ template class PacketsFIFO } } - const size_t readPosition = m_readPosition.load(); + const std::size_t readPosition = m_readPosition.load(); *element = (m_ringBuffer[readPosition]); m_readPosition.store(getPositionAfter(readPosition)); canWrite.notify_one(); @@ -149,11 +147,11 @@ template class PacketsFIFO } ///--------------------------------------------------------------------------- - /// @brief Clears the content from the queue + /// @brief Clears the content from the queue. void clear() noexcept { - const size_t readPosition = m_readPosition.load(); - const size_t writePosition = m_writePosition.load(); + const std::size_t readPosition = m_readPosition.load(); + const std::size_t writePosition = m_writePosition.load(); if (readPosition != writePosition) { @@ -162,24 +160,27 @@ template class PacketsFIFO } ///--------------------------------------------------------------------------- - /// @brief Returns the maximum size of the queue - /// @return The maximum number of elements the queue can hold - constexpr uint32_t max_size() const noexcept { return RingBufferSize - 1; } + /// @brief Returns the maximum size of the queue. + /// @return The maximum number of elements the queue can hold. + constexpr std::size_t max_size() const noexcept + { + return RingBufferSize - 1; + } ///--------------------------------------------------------------------------- - /// @brief Returns the actual number of elements in the queue - /// @return The actual size or 0 when empty - uint32_t size() const noexcept + /// @brief Returns the actual number of elements in the queue. + /// @return The actual size or 0 when empty. + std::size_t size() const noexcept { - const size_t readPosition = m_readPosition.load(); - const size_t writePosition = m_writePosition.load(); + const std::size_t readPosition = m_readPosition.load(); + const std::size_t writePosition = m_writePosition.load(); if (readPosition == writePosition) { return 0; } - size_t size = 0; + std::size_t size = 0; if (writePosition < readPosition) { size = RingBufferSize - readPosition + writePosition; @@ -192,14 +193,22 @@ template class PacketsFIFO return size; } - size_t getPositionAfter(size_t pos) noexcept { return ((pos + 1 == RingBufferSize) ? 0 : pos + 1); } - private: // A lock-free queue is basically a ring buffer. - size_t RingBufferSize; + std::size_t RingBufferSize; std::vector m_ringBuffer; - std::atomic m_readPosition = { 0 }; - std::atomic m_writePosition = { 0 }; + std::atomic m_readPosition = { 0 }; + std::atomic m_writePosition = { 0 }; + + std::condition_variable canRead; + std::condition_variable canWrite; + std::mutex mwr; + std::mutex mrd; + + constexpr std::size_t getPositionAfter(std::size_t pos) const noexcept + { + return ((pos + 1 == RingBufferSize) ? 0 : pos + 1); + } }; } // namespace lime diff --git a/src/protocols/SamplesPacket.h b/src/protocols/SamplesPacket.h index da5b0c1bd..ad0e9225f 100644 --- a/src/protocols/SamplesPacket.h +++ b/src/protocols/SamplesPacket.h @@ -13,7 +13,16 @@ namespace lime { template class SamplesPacket { public: + /** The size of the structure that holds the sample packet information. */ static constexpr int headerSize = 3 * sizeof(uint8_t*) * chCount + 17; + + /** + @brief Constructs the sample packet class. + @param vptr Pointer to the sample data. + @param samplesCount The amount of samples to store. + @param frameSize The size of a single sample. + @return A pointer to the SamplePacket class. + */ static SamplesPacket* ConstructSamplesPacket(void* vptr, uint32_t samplesCount, uint8_t frameSize) { uint8_t* ptr = reinterpret_cast(vptr); @@ -32,22 +41,54 @@ template class SamplesPacket SamplesPacket() = delete; ~SamplesPacket() = delete; - inline int size() const + /** + @brief Gets the remaining amount of samples in the packet. + @return The amount of samples remaining. + */ + inline constexpr uint32_t size() const { assert(length >= offset); return length - offset; }; - inline bool empty() const { return size() == 0; }; - inline int capacity() const { return mCapacity; }; - inline bool isFull() const { return mCapacity - length == 0; }; + + /** + @brief Gets whether the packet is empty or not. + @return True if empty. + @return false if it has elements. + */ + inline constexpr bool empty() const { return size() == 0; }; + + /** + @brief Gets the maximum amount of samples this packet can hold. + @return The amount of samples the packet can hold. + */ + inline constexpr int capacity() const { return mCapacity; }; + + /** + @brief Gets whether the packet is full or not. + @return True if full. + @return False if there is still space.. + */ + inline constexpr bool isFull() const { return mCapacity - length == 0; }; + + /** + @brief Gets the channel count of this packet. + @return The amount of channels this packet holds the information for. + */ inline constexpr int channelCount() const { return chCount; }; - // copies samples data to buffer, returns actual copied samples count + /** + @brief Copies samples data to buffer. + @tparam T The type of samples to copy. + @param src The source array of the samples. + @param count The amount of samples to copy. + @return Actual copied samples count + */ template inline int push(const T* const* src, uint16_t count) { const uint16_t freeSamples = mCapacity - length; - const int samplesToCopy = std::min(freeSamples, count); - constexpr int alignment = sizeof(T); + const uint16_t samplesToCopy = std::min(freeSamples, count); + constexpr std::size_t alignment = sizeof(T); for (uint8_t i = 0; i < chCount; ++i) { if (src[i] == nullptr) @@ -58,18 +99,35 @@ template class SamplesPacket length += samplesToCopy; return samplesToCopy; } - // returns number of samples removed - inline int pop(int count) + + /** + @brief Removes a given amount of samples from the packet. + @param count The number of samples to remove. + @return Number of samples actually removed. + */ + inline uint32_t pop(uint32_t count) { - const uint16_t toPop = std::min(count, length - offset); + const uint32_t toPop = std::min(count, length - offset); for (uint8_t i = 0; i < chCount; ++i) head[i] += toPop * frameSize; offset += toPop; - timestamp += toPop; // also offset timestamp + timestamp += toPop; // Also offset timestamp return toPop; } - inline void* const* front() const { return reinterpret_cast(head); } - inline void** back() { return reinterpret_cast(tail); } + + /** + @brief Gets a pointer to the front element of the packet. + @return A pointer to the front of the packet elements. + */ + inline constexpr void* const* front() const { return reinterpret_cast(head); } + + /** + @brief Gets a pointer to the back element of the packet. + @return A pointer to the back of the packet elements. + */ + inline constexpr void* const* back() const { return reinterpret_cast(tail); } + + /** @brief Resets all the packet to be blank. */ inline void Reset() { offset = 0; @@ -78,15 +136,28 @@ template class SamplesPacket tail[i] = head[i] = channel[i]; } - inline void SetSize(uint32_t sz) + /** + @brief Sets the amount of samples of this packet. + @param sampleCount The amount of samples this packet has. + */ + inline void SetSize(uint32_t sampleCount) { - length = sz; + length = sampleCount; for (uint8_t i = 0; i < chCount; ++i) - tail[i] = channel[i] + sz * frameSize; + tail[i] = channel[i] + sampleCount * frameSize; } + /** + @brief Scales the packet's samples' I and Q values by the given multiplier. + @tparam T The type of samples the packet is holding. + @param iScale The multiplier with which to multiply all the I values. + @param qScale The multiplier with which to multiply all the Q values. + @param channelCount The amount of channels to multiply the values for. + */ template void Scale(float iScale, float qScale, int channelCount) { + assert(channelCount <= chCount); + int samplesCount = size(); for (int c = 0; c < channelCount; ++c) { @@ -99,19 +170,19 @@ template class SamplesPacket } } + uint64_t timestamp; ///< The timestamp of the packet. + bool useTimestamp; ///< Whether to use the timestamp or not. + bool flush; ///< Whether to flush the whole packet early or not. + private: uint8_t* head[chCount]; uint8_t* tail[chCount]; uint8_t* channel[chCount]; - public: - int64_t timestamp; - uint16_t offset; - uint16_t length; - uint16_t mCapacity; - uint16_t frameSize; - bool useTimestamp; - bool flush; + uint32_t offset; + uint32_t length; + uint32_t mCapacity; + uint8_t frameSize; }; } // namespace lime diff --git a/src/protocols/TRXLooper.cpp b/src/protocols/TRXLooper.cpp index fcfc4636a..59dee1a6d 100644 --- a/src/protocols/TRXLooper.cpp +++ b/src/protocols/TRXLooper.cpp @@ -18,6 +18,10 @@ using namespace std::chrono; static constexpr uint16_t defaultSamplesInPkt = 256; +/// @brief Constructs a new TRXLooper object. +/// @param f The FPGA device to use for streaming. +/// @param chip The LMS7002M device to use for streaming. +/// @param id The ID of the chip to use. TRXLooper::TRXLooper(FPGA* f, LMS7002M* chip, int id) : mCallback_logMessage(nullptr) , mStreamEnabled(false) @@ -42,400 +46,25 @@ TRXLooper::~TRXLooper() { } -uint64_t TRXLooper::GetHardwareTimestamp(void) +/// @brief Gets the current timestamp of the hardware. +/// @return The current timestamp of the hardware. +uint64_t TRXLooper::GetHardwareTimestamp() const { return mRx.lastTimestamp.load(std::memory_order_relaxed) + mTimestampOffset; } +/// @brief Sets the hardware timestamp. +/// @param now The current timestamp to set. +/// @return The status of the operation. OpStatus TRXLooper::SetHardwareTimestamp(const uint64_t now) { mTimestampOffset = now - mRx.lastTimestamp.load(std::memory_order_relaxed); return OpStatus::SUCCESS; } -/* -void TRXLooper::RstRxIQGen() -{ - uint32_t data[16]; - uint32_t reg20; - uint32_t reg11C; - uint32_t reg10C; - data[0] = (uint32_t(0x0020) << 16); - dataPort->ReadLMS7002MSPI(data, ®20, 1, chipId); - data[0] = (uint32_t(0x010C) << 16); - dataPort->ReadLMS7002MSPI(data, ®10C, 1, chipId); - data[0] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFD; - dataPort->WriteLMS7002MSPI(data, 1, chipId); - data[0] = (uint32_t(0x011C) << 16); - dataPort->ReadLMS7002MSPI(data, ®11C, 1, chipId); - data[0] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFD; //SXR - data[1] = (1 << 31) | (uint32_t(0x011C) << 16) | (reg11C | 0x10); //PD_FDIV - data[2] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFF; // mac 3 - both channels - data[3] = (1 << 31) | (uint32_t(0x0124) << 16) | 0x001F; //direct control of powerdowns - data[4] = (1 << 31) | (uint32_t(0x010C) << 16) | (reg10C | 0x8); // PD_QGEN_RFE - data[5] = (1 << 31) | (uint32_t(0x010C) << 16) | reg10C; //restore value - data[6] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFD; //SXR - data[7] = (1 << 31) | (uint32_t(0x011C) << 16) | reg11C; //restore value - data[8] = (1 << 31) | (uint32_t(0x0020) << 16) | reg20; //restore value - dataPort->WriteLMS7002MSPI(data, 9, chipId); -} - -void TRXLooper::AlignRxTSP() -{ - uint32_t reg20; - uint32_t regsA[2]; - uint32_t regsB[2]; - //backup values - { - const std::vector bakAddr = { (uint32_t(0x0400) << 16), (uint32_t(0x040C) << 16) }; - uint32_t data = (uint32_t(0x0020) << 16); - dataPort->ReadLMS7002MSPI(&data, ®20, 1, chipId); - data = (uint32_t(0x0020) << 16) | 0xFFFD; - dataPort->WriteLMS7002MSPI(&data, 1, chipId); - dataPort->ReadLMS7002MSPI(bakAddr.data(), regsA, bakAddr.size(), chipId); - data = (uint32_t(0x0020) << 16) | 0xFFFE; - dataPort->WriteLMS7002MSPI(&data, 1, chipId); - dataPort->ReadLMS7002MSPI(bakAddr.data(), regsB, bakAddr.size(), chipId); - } - - //alignment search - { - uint32_t dataWr[4]; - dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFF; - dataWr[1] = (1 << 31) | (uint32_t(0x0400) << 16) | 0x8085; - dataWr[2] = (1 << 31) | (uint32_t(0x040C) << 16) | 0x01FF; - dataPort->WriteLMS7002MSPI(dataWr, 3, chipId); - uint32_t* buf = new uint32_t[sizeof(FPGA_DataPacket) / sizeof(uint32_t)]; - - fpga->StopStreaming(); - fpga->WriteRegister(0xFFFF, 1 << chipId); - fpga->WriteRegister(0x0008, 0x0100); - fpga->WriteRegister(0x0007, 3); - - dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | 0x55FE; - dataWr[1] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFD; - - for (int i = 0; i < 100; i++) - { - dataPort->WriteLMS7002MSPI(&dataWr[0], 2, chipId); - dataPort->ResetStreamBuffers(); - fpga->StartStreaming(); - if (dataPort->ReceiveData((char*)buf, sizeof(FPGA_DataPacket), chipId, 50) != sizeof(FPGA_DataPacket)) - { - lime::warning("Channel alignment failed"); - break; - } - fpga->StopStreaming(); - dataPort->AbortReading(chipId); - if (buf[4] == buf[5]) - break; - } - delete[] buf; - } - - //restore values - { - uint32_t dataWr[7]; - dataWr[0] = (uint32_t(0x0020) << 16) | 0xFFFD; - dataWr[1] = (uint32_t(0x0400) << 16) | regsA[0]; - dataWr[2] = (uint32_t(0x040C) << 16) | regsA[1]; - dataWr[3] = (uint32_t(0x0020) << 16) | 0xFFFE; - dataWr[4] = (uint32_t(0x0400) << 16) | regsB[0]; - dataWr[5] = (uint32_t(0x040C) << 16) | regsB[1]; - dataWr[6] = (uint32_t(0x0020) << 16) | reg20; - dataPort->WriteLMS7002MSPI(dataWr, 7, chipId); - } -} - -double TRXLooper::GetPhaseOffset(int bin) -{ - int16_t* buf = new int16_t[sizeof(FPGA_DataPacket)/sizeof(int16_t)]; - - dataPort->ResetStreamBuffers(); - fpga->StartStreaming(); - if (dataPort->ReceiveData((char*)buf, sizeof(FPGA_DataPacket), chipId, 50)!=sizeof(FPGA_DataPacket)) - { - lime::warning("Channel alignment failed"); - delete [] buf; - return -1000; - } - fpga->StopStreaming(); - dataPort->AbortReading(chipId); - //calculate DFT bin of interest and check channel phase difference - const std::complex iunit(0, 1); - const double pi = std::acos(-1); - const int N = 512; - std::complex xA(0,0); - std::complex xB(0, 0); - for (int n = 0; n < N; n++) - { - const std::complex xAn(buf[8+4*n], buf[9+4*n]); - const std::complex xBn(buf[10+4*n],buf[11+4*n]); - const std::complex mult = std::exp(-2.0*iunit*pi* double(bin)* double(n)/double(N)); - xA += xAn * mult; - xB += xBn * mult; - } - double phaseA = std::arg(xA) * 180.0 / pi; - double phaseB = std::arg(xB) * 180.0 / pi; - double phasediff = phaseB - phaseA; - if (phasediff < -180.0) phasediff +=360.0; - if (phasediff > 180.0) phasediff -=360.0; - delete [] buf; - return phasediff; -} - -void TRXLooper::AlignRxRF(bool restoreValues) -{ - uint32_t reg20 = lms->SPI_read(0x20); - auto regBackup = lms->BackupRegisterMap(); - lms->SPI_write(0x20, 0xFFFF); - lms->SetDefaults(LMS7002M::RFE); - lms->SetDefaults(LMS7002M::RBB); - lms->SetDefaults(LMS7002M::TBB); - lms->SetDefaults(LMS7002M::TRF); - lms->SPI_write(0x10C, 0x88C5); - lms->SPI_write(0x10D, 0x0117); - lms->SPI_write(0x113, 0x024A); - lms->SPI_write(0x118, 0x418C); - lms->SPI_write(0x100, 0x4039); - lms->SPI_write(0x101, 0x7801); - lms->SPI_write(0x103, 0x0612); - lms->SPI_write(0x108, 0x318C); - lms->SPI_write(0x082, 0x8001); - lms->SPI_write(0x200, 0x008D); - lms->SPI_write(0x208, 0x01FB); - lms->SPI_write(0x400, 0x8081); - lms->SPI_write(0x40C, 0x01FF); - lms->SPI_write(0x404, 0x0006); - lms->LoadDC_REG_IQ(true, 0x3FFF, 0x3FFF); - double srate = lms->GetSampleRate(false, LMS7002M::ChA); - lms->SetFrequencySX(false,450e6); - int dec = lms->Get_SPI_Reg_bits(LMS7_HBD_OVR_RXTSP); - if (dec > 4) dec = 0; - - double offsets[] = {1.15/60.0, 1.1/40.0, 0.55/20.0, 0.2/10.0, 0.18/5.0}; - double tolerance[] = {0.9, 0.45, 0.25, 0.14, 0.06}; - double offset = offsets[dec]*srate/1e6; - std::vector dataWr; - dataWr.resize(16); - - fpga->WriteRegister(0xFFFF, 1 << chipId); - fpga->StopStreaming(); - fpga->WriteRegister(0x0008, 0x0100); - fpga->WriteRegister(0x0007, 3); - bool found = false; - for (int i = 0; i < 200; i++){ - lms->Modify_SPI_Reg_bits(LMS7_PD_FDIV_O_CGEN, 1); - lms->Modify_SPI_Reg_bits(LMS7_PD_FDIV_O_CGEN, 0); - AlignRxTSP(); - - lms->SetFrequencySX(true, 450e6+srate/16.0); - double offset1 = GetPhaseOffset(32); - if (offset1 < -360) - break; - lms->SetFrequencySX(true, 450e6+srate/8.0); - double offset2 = GetPhaseOffset(64); - if (offset2 < -360) - break; - double diff = offset1-offset2; - if (abs(diff-offset) < tolerance[dec]) - { - found = true; - break; - } - } - if (restoreValues) - lms->RestoreRegisterMap(regBackup); - if (found) - AlignQuadrature(restoreValues); - else - lime::warning("Channel alignment failed"); - lms->SPI_write(0x20, reg20); -} - -void TRXLooper::AlignQuadrature(bool restoreValues) -{ - auto regBackup = lms->BackupRegisterMap(); - - lms->SPI_write(0x20, 0xFFFF); - lms->SetDefaults(LMS7002M::RBB); - lms->SetDefaults(LMS7002M::TBB); - lms->SetDefaults(LMS7002M::TRF); - lms->SPI_write(0x113, 0x0046); - lms->SPI_write(0x118, 0x418C); - lms->SPI_write(0x100, 0x4039); - lms->SPI_write(0x101, 0x7801); - lms->SPI_write(0x108, 0x318C); - lms->SPI_write(0x082, 0x8001); - lms->SPI_write(0x200, 0x008D); - lms->SPI_write(0x208, 0x01FB); - lms->SPI_write(0x400, 0x8081); - lms->SPI_write(0x40C, 0x01FF); - lms->SPI_write(0x404, 0x0006); - lms->LoadDC_REG_IQ(true, 0x3FFF, 0x3FFF); - lms->SPI_write(0x20, 0xFFFE); - lms->SPI_write(0x105, 0x0006); - lms->SPI_write(0x100, 0x4038); - lms->SPI_write(0x113, 0x007F); - lms->SPI_write(0x119, 0x529B); - auto val = lms->Get_SPI_Reg_bits(LMS7_SEL_PATH_RFE, true); - lms->SPI_write(0x10D, val==3 ? 0x18F : val==2 ? 0x117 : 0x08F); - lms->SPI_write(0x10C, val==2 ? 0x88C5 : 0x88A5); - lms->SPI_write(0x20, 0xFFFD); - lms->SPI_write(0x103, val==2 ? 0x612 : 0xA12); - val = lms->Get_SPI_Reg_bits(LMS7_SEL_PATH_RFE, true); - lms->SPI_write(0x10D, val==3 ? 0x18F : val==2 ? 0x117 : 0x08F); - lms->SPI_write(0x10C, val==2 ? 0x88C5 : 0x88A5); - lms->SPI_write(0x119, 0x5293); - double srate = lms->GetSampleRate(false, LMS7002M::ChA); - double freq = lms->GetFrequencySX(false); - - fpga->WriteRegister(0xFFFF, 1 << chipId); - fpga->StopStreaming(); - fpga->WriteRegister(0x0008, 0x0100); - fpga->WriteRegister(0x0007, 3); - lms->SetFrequencySX(true, freq+srate/16.0); - bool found = false; - for (int i = 0; i < 100; i++){ - - double offset = GetPhaseOffset(32); - if (offset < -360) - break; - if (fabs(offset) <= 90.0) - { - found = true; - break; - } - RstRxIQGen(); - } - - if (restoreValues) - lms->RestoreRegisterMap(regBackup); - if (!found) - lime::warning("Channel alignment failed"); -} -*/ -/* -int TRXLooper::UpdateThreads(bool stopAll) -{ - bool needTx = false; - bool needRx = false; - - //check which threads are needed - if (!stopAll) - { - for(auto &i : mRxStreams) - if(i.used && i.IsActive()) - { - needRx = true; - break; - } - for(auto &i : mTxStreams) - if(i.used && i.IsActive()) - { - needTx = true; - break; - } - } - - //stop threads if not needed - if((!needTx) && mTx.thread.joinable()) - { - mTx.terminate.store(true, std::memory_order_relaxed); - mTx.thread.join(); - } - if((!needRx) && mRx.thread.joinable()) - { - mRx.terminate.store(true, std::memory_order_relaxed); - mRx.thread.join(); - } - - //configure FPGA on first start, or disable FPGA when not streaming - if((needTx || needRx) && (!mTx.thread.joinable()) && (!mRx.thread.joinable())) - { - ResizeChannelBuffers(); - fpga->WriteRegister(0xFFFF, 1 << chipId); - bool align = (mRxStreams[0].used && mRxStreams[1].used && (mRxStreams[0].config.align | mRxStreams[1].config.align)); - if (align) - AlignRxRF(true); - //enable FPGA streaming - fpga->StopStreaming(); - fpga->ResetTimestamp(); - mRx.lastTimestamp.store(0, std::memory_order_relaxed); - //Clear device stream buffers - dataPort->ResetStreamBuffers(); - - //enable MIMO mode, 12 bit compressed values - dataLinkFormat = StreamConfig::FMT_INT12; - //by default use 12 bit compressed, adjust link format for stream - - for(auto &i : mRxStreams) - if(i.used && i.config.linkFormat != StreamConfig::FMT_INT12) - { - dataLinkFormat = StreamConfig::FMT_INT16; - break; - } - - for(auto &i : mTxStreams) - if(i.used && i.config.linkFormat != StreamConfig::FMT_INT12) - { - dataLinkFormat = StreamConfig::FMT_INT16; - break; - } - - const uint16_t smpl_width = dataLinkFormat == StreamConfig::FMT_INT12 ? 2 : 0; - uint16_t mode = 0x0100; - - if (lms->Get_SPI_Reg_bits(LMS7param(LML1_SISODDR))) - mode = 0x0040; - else if (lms->Get_SPI_Reg_bits(LMS7param(LML1_TRXIQPULSE))) - mode = 0x0180; - - fpga->WriteRegister(0x0008, mode | smpl_width); - - const uint16_t channelEnables = (mRxStreams[0].used||mTxStreams[0].used) + 2 * (mRxStreams[1].used||mTxStreams[1].used); - fpga->WriteRegister(0x0007, channelEnables); - - uint32_t reg9 = fpga->ReadRegister(0x0009); - const uint32_t addr[] = {0x0009, 0x0009}; - const uint32_t data[] = {reg9 | (5 << 1), reg9 & ~(5 << 1)}; - fpga->StartStreaming(); - fpga->WriteRegisters(addr, data, 2); - if (!align) - lms->ResetLogicregisters(); - } - else if(not needTx and not needRx) - { - //disable FPGA streaming - fpga->WriteRegister(0xFFFF, 1 << chipId); - fpga->StopStreaming(); - } - - //FPGA should be configured and activated, start needed threads - if(needRx && (!mRx.thread.joinable())) - { - mRx.terminate.store(false, std::memory_order_relaxed); - auto RxLoopFunction = std::bind(&TRXLooper::ReceivePacketsLoop, this); - mRx.thread = std::thread(RxLoopFunction); - SetOSThreadPriority(ThreadPriority::NORMAL, ThreadPolicy::REALTIME, &mRx.thread); - } - if(needTx && (!mTx.thread.joinable())) - { - fpga->WriteRegister(0xFFFF, 1 << chipId); - fpga->WriteRegister(0xD, 0); //stop WFM - mTx.terminate.store(false, std::memory_order_relaxed); - auto TxLoopFunction = std::bind(&TRXLooper::TransmitPacketsLoop, this); - mTx.thread = std::thread(TxLoopFunction); - SetOSThreadPriority(ThreadPriority::NORMAL, ThreadPolicy::REALTIME, &mTx.thread); - } - return 0; -} -*/ - -// const lime::SDRDevice::StreamConfig& TRXLooper::GetConfig() const -// { -// return mConfig; -// } +/// @brief Sets up the stream of this looper. +/// @param cfg The configuration settings to set up the stream with. +/// @return The status of the operation. OpStatus TRXLooper::Setup(const SDRDevice::StreamConfig& cfg) { if (mRx.thread.joinable() || mTx.thread.joinable()) @@ -572,6 +201,7 @@ OpStatus TRXLooper::Setup(const SDRDevice::StreamConfig& cfg) return OpStatus::SUCCESS; } +/// @brief Starts the stream of this looper. void TRXLooper::Start() { mRx.fifo->clear(); @@ -589,9 +219,10 @@ void TRXLooper::Start() //int64_t startPoint = std::chrono::time_point_cast(pcStreamStart).time_since_epoch().count(); //printf("Stream%i start %lius\n", chipId, startPoint); // if (!mConfig.alignPhase) - // lms->ResetLogicregisters(); + // lms->ResetLogicRegisters(); } +/// @brief Stops the stream and cleans up all the memory. void TRXLooper::Stop() { if (!mStreamEnabled) @@ -623,20 +254,12 @@ void TRXLooper::Stop() mStreamEnabled = false; } -bool TRXLooper::IsStreamRunning() -{ - return mStreamEnabled; -} - -int TRXLooper::StreamRx(lime::complex32f_t* const* dest, uint32_t count, SDRDevice::StreamMeta* meta) +template uint32_t TRXLooper::StreamRxTemplate(T* const* dest, uint32_t count, SDRDevice::StreamMeta* meta) { bool timestampSet = false; uint32_t samplesProduced = 0; const bool useChannelB = mConfig.channels.at(TRXDir::Rx).size() > 1; - lime::complex32f_t* f32_dest[2] = { static_cast(dest[0]), - useChannelB ? static_cast(dest[1]) : nullptr }; - bool firstIteration = true; //auto start = high_resolution_clock::now(); @@ -644,62 +267,25 @@ int TRXLooper::StreamRx(lime::complex32f_t* const* dest, uint32_t count, SDRDevi { if (!mRx.stagingPacket && !mRx.fifo->pop(&mRx.stagingPacket, firstIteration, 2000)) return samplesProduced; + if (!timestampSet && meta) { meta->timestamp = mRx.stagingPacket->timestamp; timestampSet = true; } - int expectedCount = count - samplesProduced; - const int samplesToCopy = std::min(expectedCount, mRx.stagingPacket->size()); - - lime::complex32f_t* const* f32_src = reinterpret_cast(mRx.stagingPacket->front()); - - memcpy(&f32_dest[0][samplesProduced], f32_src[0], samplesToCopy * sizeof(complex32f_t)); - if (useChannelB) - memcpy(&f32_dest[1][samplesProduced], f32_src[1], samplesToCopy * sizeof(complex32f_t)); - mRx.stagingPacket->pop(samplesToCopy); - samplesProduced += samplesToCopy; - - if (mRx.stagingPacket->empty()) - { - mRx.memPool->Free(mRx.stagingPacket); - mRx.stagingPacket = nullptr; - } + uint32_t expectedCount = count - samplesProduced; + const uint32_t samplesToCopy = std::min(expectedCount, mRx.stagingPacket->size()); - // int duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count(); - // if(duration > 300) // TODO: timeout duration in meta - // return samplesProduced; - } - return samplesProduced; -} + T* const* src = reinterpret_cast(mRx.stagingPacket->front()); -int TRXLooper::StreamRx(lime::complex16_t* const* dest, uint32_t count, SDRDevice::StreamMeta* meta) -{ - bool timestampSet = false; - uint32_t samplesProduced = 0; - const bool useChannelB = mConfig.channels.at(lime::TRXDir::Rx).size() > 1; - bool firstIteration = true; + std::memcpy(&dest[0][samplesProduced], src[0], samplesToCopy * sizeof(T)); - //auto start = high_resolution_clock::now(); - while (samplesProduced < count) - { - if (!mRx.stagingPacket && !mRx.fifo->pop(&mRx.stagingPacket, firstIteration, 250)) - return samplesProduced; - if (!timestampSet && meta) + if (useChannelB) { - meta->timestamp = mRx.stagingPacket->timestamp; - timestampSet = true; + std::memcpy(&dest[1][samplesProduced], src[1], samplesToCopy * sizeof(T)); } - int expectedCount = count - samplesProduced; - const int samplesToCopy = std::min(expectedCount, mRx.stagingPacket->size()); - - lime::complex16_t* const* src = reinterpret_cast(mRx.stagingPacket->front()); - - memcpy(&dest[0][samplesProduced], src[0], samplesToCopy * sizeof(complex16_t)); - if (useChannelB) - memcpy(&dest[1][samplesProduced], src[1], samplesToCopy * sizeof(complex16_t)); mRx.stagingPacket->pop(samplesToCopy); samplesProduced += samplesToCopy; @@ -713,80 +299,64 @@ int TRXLooper::StreamRx(lime::complex16_t* const* dest, uint32_t count, SDRDevic // if(duration > 300) // TODO: timeout duration in meta // return samplesProduced; } + return samplesProduced; } -int TRXLooper::StreamRx(lime::complex12_t* const* dest, uint32_t count, SDRDevice::StreamMeta* meta) +/// @brief Reveives samples from this specific stream. +/// @param samples The buffer to put the received samples in. +/// @param count The amount of samples to reveive. +/// @param meta The metadata of the packets of the stream. +/// @return The amount of samples received. +uint32_t TRXLooper::StreamRx(complex32f_t* const* samples, uint32_t count, SDRDevice::StreamMeta* meta) { - bool timestampSet = false; - uint32_t samplesProduced = 0; - const bool useChannelB = mConfig.channels.at(lime::TRXDir::Rx).size() > 1; - bool firstIteration = true; - - //auto start = high_resolution_clock::now(); - while (samplesProduced < count) - { - if (!mRx.stagingPacket && !mRx.fifo->pop(&mRx.stagingPacket, firstIteration, 250)) - return samplesProduced; - if (!timestampSet && meta) - { - meta->timestamp = mRx.stagingPacket->timestamp; - timestampSet = true; - } - - int expectedCount = count - samplesProduced; - const int samplesToCopy = std::min(expectedCount, mRx.stagingPacket->size()); - - lime::complex12_t* const* src = reinterpret_cast(mRx.stagingPacket->front()); - - memcpy(&dest[0][samplesProduced], src[0], samplesToCopy * sizeof(complex12_t)); - if (useChannelB) - memcpy(&dest[1][samplesProduced], src[1], samplesToCopy * sizeof(complex12_t)); - mRx.stagingPacket->pop(samplesToCopy); - samplesProduced += samplesToCopy; + return StreamRxTemplate(samples, count, meta); +} - if (mRx.stagingPacket->empty()) - { - mRx.memPool->Free(mRx.stagingPacket); - mRx.stagingPacket = nullptr; - } +/// @copydoc TRXLooper::StreamRx() +uint32_t TRXLooper::StreamRx(complex16_t* const* samples, uint32_t count, SDRDevice::StreamMeta* meta) +{ + return StreamRxTemplate(samples, count, meta); +} - // int duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count(); - // if(duration > 300) // TODO: timeout duration in meta - // return samplesProduced; - } - return samplesProduced; +/// @copydoc TRXLooper::StreamRx() +uint32_t TRXLooper::StreamRx(lime::complex12_t* const* samples, uint32_t count, SDRDevice::StreamMeta* meta) +{ + return StreamRxTemplate(samples, count, meta); } -int TRXLooper::StreamTx(const lime::complex32f_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta) +template uint32_t TRXLooper::StreamTxTemplate(const T* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta) { const bool useChannelB = mConfig.channels.at(lime::TRXDir::Tx).size() > 1; - const bool useTimestamp = meta ? meta->useTimestamp : false; - const bool flush = meta && meta->flush; + const bool useTimestamp = meta ? meta->waitForTimestamp : false; + const bool flush = meta && meta->flushPartialPacket; int64_t ts = meta ? meta->timestamp : 0; - int samplesRemaining = count; + uint32_t samplesRemaining = count; const int samplesInPkt = mTx.samplesInPkt; const int packetsToBatch = mTx.packetsToBatch; - const int32_t outputPktSize = SamplesPacketType::headerSize + packetsToBatch * samplesInPkt * sizeof(complex32f_t); + const int32_t outputPktSize = SamplesPacketType::headerSize + packetsToBatch * samplesInPkt * sizeof(T); if (mTx.stagingPacket && mTx.stagingPacket->timestamp + mTx.stagingPacket->size() != meta->timestamp) { if (!mTx.fifo->push(mTx.stagingPacket)) return 0; + mTx.stagingPacket = nullptr; } - const lime::complex32f_t* src[2] = { samples[0], useChannelB ? samples[1] : nullptr }; - while (samplesRemaining) + const T* src[2] = { samples[0], useChannelB ? samples[1] : nullptr }; + while (samplesRemaining > 0) { if (!mTx.stagingPacket) { mTx.stagingPacket = SamplesPacketType::ConstructSamplesPacket( - mTx.memPool->Allocate(outputPktSize), samplesInPkt * packetsToBatch, sizeof(complex32f_t)); + mTx.memPool->Allocate(outputPktSize), samplesInPkt * packetsToBatch, sizeof(T)); + if (!mTx.stagingPacket) break; + mTx.stagingPacket->Reset(); mTx.stagingPacket->timestamp = ts; mTx.stagingPacket->useTimestamp = useTimestamp; @@ -807,121 +377,40 @@ int TRXLooper::StreamTx(const lime::complex32f_t* const* samples, uint32_t count if (!mTx.fifo->push(mTx.stagingPacket)) break; + mTx.stagingPacket = nullptr; } } + return count - samplesRemaining; } -int TRXLooper::StreamTx(const lime::complex16_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta) +/// @brief Transmits packets from from this specific stream. +/// @param samples The buffer of the samples to transmit. +/// @param count The amount of samples to transmit. +/// @param meta The metadata of the packets of the stream. +/// @return The amount of samples transmitted. +uint32_t TRXLooper::StreamTx(const lime::complex32f_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta) { - const bool useChannelB = mConfig.channels.at(lime::TRXDir::Tx).size() > 1; - const bool useTimestamp = meta ? meta->useTimestamp : false; - const bool flush = meta && meta->flush; - int64_t ts = meta ? meta->timestamp : 0; - - int samplesRemaining = count; - const int samplesInPkt = mTx.samplesInPkt; - const int packetsToBatch = mTx.packetsToBatch; - const int32_t outputPktSize = SamplesPacketType::headerSize + packetsToBatch * samplesInPkt * sizeof(complex16_t); - - if (mTx.stagingPacket && mTx.stagingPacket->timestamp + mTx.stagingPacket->size() != meta->timestamp) - { - if (!mTx.fifo->push(mTx.stagingPacket)) - return 0; - mTx.stagingPacket = nullptr; - } - - const lime::complex16_t* src[2] = { samples[0], useChannelB ? samples[1] : nullptr }; - while (samplesRemaining) - { - if (!mTx.stagingPacket) - { - mTx.stagingPacket = SamplesPacketType::ConstructSamplesPacket( - mTx.memPool->Allocate(outputPktSize), samplesInPkt * packetsToBatch, sizeof(complex16_t)); - if (!mTx.stagingPacket) - break; - mTx.stagingPacket->Reset(); - mTx.stagingPacket->timestamp = ts; - mTx.stagingPacket->useTimestamp = useTimestamp; - } - - int consumed = mTx.stagingPacket->push(src, samplesRemaining); - src[0] += consumed; - if (useChannelB) - src[1] += consumed; - - samplesRemaining -= consumed; - ts += consumed; - - if (mTx.stagingPacket->isFull() || flush) - { - if (samplesRemaining == 0) - mTx.stagingPacket->flush = flush; - - if (!mTx.fifo->push(mTx.stagingPacket)) - break; - mTx.stagingPacket = nullptr; - } - } - return count - samplesRemaining; + return StreamTxTemplate(samples, count, meta); } -int TRXLooper::StreamTx(const lime::complex12_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta) +/// @copydoc TRXLooper::StreamTx() +uint32_t TRXLooper::StreamTx(const lime::complex16_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta) { - const bool useChannelB = mConfig.channels.at(lime::TRXDir::Tx).size() > 1; - const bool useTimestamp = meta ? meta->useTimestamp : false; - const bool flush = meta && meta->flush; - int64_t ts = meta ? meta->timestamp : 0; - - int samplesRemaining = count; - const int samplesInPkt = mTx.samplesInPkt; - const int packetsToBatch = mTx.packetsToBatch; - const int32_t outputPktSize = SamplesPacketType::headerSize + packetsToBatch * samplesInPkt * sizeof(complex12_t); - - if (mTx.stagingPacket && mTx.stagingPacket->timestamp + mTx.stagingPacket->size() != meta->timestamp) - { - if (!mTx.fifo->push(mTx.stagingPacket)) - return 0; - mTx.stagingPacket = nullptr; - } - - const lime::complex12_t* src[2] = { samples[0], useChannelB ? samples[1] : nullptr }; - while (samplesRemaining) - { - if (!mTx.stagingPacket) - { - mTx.stagingPacket = SamplesPacketType::ConstructSamplesPacket( - mTx.memPool->Allocate(outputPktSize), samplesInPkt * packetsToBatch, sizeof(complex12_t)); - if (!mTx.stagingPacket) - break; - mTx.stagingPacket->Reset(); - mTx.stagingPacket->timestamp = ts; - mTx.stagingPacket->useTimestamp = useTimestamp; - } - - int consumed = mTx.stagingPacket->push(src, samplesRemaining); - src[0] += consumed; - if (useChannelB) - src[1] += consumed; - - samplesRemaining -= consumed; - ts += consumed; - - if (mTx.stagingPacket->isFull() || flush) - { - if (samplesRemaining == 0) - mTx.stagingPacket->flush = flush; + return StreamTxTemplate(samples, count, meta); +} - if (!mTx.fifo->push(mTx.stagingPacket)) - break; - mTx.stagingPacket = nullptr; - } - } - return count - samplesRemaining; +/// @copydoc TRXLooper::StreamTx() +uint32_t TRXLooper::StreamTx(const lime::complex12_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta) +{ + return StreamTxTemplate(samples, count, meta); } -SDRDevice::StreamStats TRXLooper::GetStats(TRXDir dir) +/// @brief Gets statistics from a specified transfer direction. +/// @param dir The direction of which to get the statistics. +/// @return The statistics of the transfers. +SDRDevice::StreamStats TRXLooper::GetStats(TRXDir dir) const { SDRDevice::StreamStats stats; diff --git a/src/protocols/TRXLooper.h b/src/protocols/TRXLooper.h index a344a7cd5..2a51e0f2d 100644 --- a/src/protocols/TRXLooper.h +++ b/src/protocols/TRXLooper.h @@ -21,27 +21,34 @@ class TRXLooper TRXLooper(FPGA* f, LMS7002M* chip, int id); virtual ~TRXLooper(); - uint64_t GetHardwareTimestamp(void); + uint64_t GetHardwareTimestamp() const; OpStatus SetHardwareTimestamp(const uint64_t now); - virtual OpStatus Setup(const lime::SDRDevice::StreamConfig& config); + virtual OpStatus Setup(const lime::SDRDevice::StreamConfig& cfg); virtual void Start(); virtual void Stop(); - virtual bool IsStreamRunning(); + /// @brief Gets whether the stream is currently running or not. + /// @return The current status of the stream (true if running). + constexpr bool IsStreamRunning() const { return mStreamEnabled; } - inline const lime::SDRDevice::StreamConfig& GetConfig() const { return mConfig; } + /// @brief Gets the current configuration of the stream. + /// @return The current configuration of the stream. + constexpr const lime::SDRDevice::StreamConfig& GetConfig() const { return mConfig; } - virtual int StreamRx(lime::complex32f_t* const* samples, uint32_t count, SDRDevice::StreamMeta* meta); - virtual int StreamRx(lime::complex16_t* const* samples, uint32_t count, SDRDevice::StreamMeta* meta); - virtual int StreamRx(lime::complex12_t* const* samples, uint32_t count, SDRDevice::StreamMeta* meta); - virtual int StreamTx(const lime::complex32f_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); - virtual int StreamTx(const lime::complex16_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); - virtual int StreamTx(const lime::complex12_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); + virtual uint32_t StreamRx(lime::complex32f_t* const* samples, uint32_t count, SDRDevice::StreamMeta* meta); + virtual uint32_t StreamRx(lime::complex16_t* const* samples, uint32_t count, SDRDevice::StreamMeta* meta); + virtual uint32_t StreamRx(lime::complex12_t* const* samples, uint32_t count, SDRDevice::StreamMeta* meta); + virtual uint32_t StreamTx(const lime::complex32f_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); + virtual uint32_t StreamTx(const lime::complex16_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); + virtual uint32_t StreamTx(const lime::complex12_t* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); + /// @brief Sets the callback to use for message logging. + /// @param callback The new callback to use. void SetMessageLogCallback(SDRDevice::LogCallbackType callback) { mCallback_logMessage = callback; } - SDRDevice::StreamStats GetStats(TRXDir tx); + SDRDevice::StreamStats GetStats(TRXDir tx) const; + /// @brief The type of a sample packet. typedef SamplesPacket<2> SamplesPacketType; protected: @@ -56,11 +63,6 @@ class TRXLooper uint64_t mTimestampOffset; lime::SDRDevice::StreamConfig mConfig; - // void AlignRxTSP(); - // void AlignRxRF(bool restoreValues); - // void AlignQuadrature(bool restoreValues); - // void RstRxIQGen(); - // double GetPhaseOffset(int bin); FPGA* fpga; LMS7002M* lms; int chipId; @@ -122,6 +124,10 @@ class TRXLooper Stream mRx; Stream mTx; + + private: + template uint32_t StreamRxTemplate(T* const* dest, uint32_t count, SDRDevice::StreamMeta* meta); + template uint32_t StreamTxTemplate(const T* const* samples, uint32_t count, const SDRDevice::StreamMeta* meta); }; } // namespace lime diff --git a/src/tests/comms/PCIe/PCIE_CSR_PipeTest.cpp b/src/tests/comms/PCIe/PCIE_CSR_PipeTest.cpp index e442fd12f..e3dd055c7 100644 --- a/src/tests/comms/PCIe/PCIE_CSR_PipeTest.cpp +++ b/src/tests/comms/PCIe/PCIE_CSR_PipeTest.cpp @@ -2,7 +2,7 @@ #include #include "LitePCIeMock.h" -#include "comms/PCIe/PCIeCommon.h" +#include "comms/PCIe/PCIE_CSR_Pipe.h" #include "LMS64CProtocol.h" using namespace lime; diff --git a/src/utilities/rfTest.cpp b/src/utilities/rfTest.cpp index ff7280efb..66c09ef83 100644 --- a/src/utilities/rfTest.cpp +++ b/src/utilities/rfTest.cpp @@ -178,7 +178,7 @@ bool FullStreamTxRx(SDRDevice& dev, bool MIMO) } // skip some packets at the start in case of leftover data garbage - int64_t ignoreSamplesAtStart = 0; + uint64_t ignoreSamplesAtStart = 0; //Initialize stream bool streamHadIssues = false; @@ -207,7 +207,7 @@ bool FullStreamTxRx(SDRDevice& dev, bool MIMO) bool show = false; int fired = 0; - int64_t lastRxTS = 0; + uint64_t lastRxTS = 0; int badSignal = 0; while (runForever.load()) @@ -218,7 +218,7 @@ bool FullStreamTxRx(SDRDevice& dev, bool MIMO) SDRDevice::StreamMeta rxMeta; rxMeta.timestamp = 0; auto tt1 = std::chrono::high_resolution_clock::now(); - int samplesRead = dev.StreamRx(testStreamIndex, dest, samplesInPkt * txPacketCount, &rxMeta); + uint32_t samplesRead = dev.StreamRx(testStreamIndex, dest, samplesInPkt * txPacketCount, &rxMeta); auto tt2 = std::chrono::high_resolution_clock::now(); int duration = std::chrono::duration_cast(tt2 - tt1).count(); if (show) @@ -251,13 +251,13 @@ bool FullStreamTxRx(SDRDevice& dev, bool MIMO) ++fired; int64_t rxNow = rxMeta.timestamp + samplesInPkt; txMeta.timestamp = rxNow + txDeltaTS; - txMeta.useTimestamp = true; - txMeta.flush = false; // not really matters because of continuous trasmitting + txMeta.waitForTimestamp = true; + txMeta.flushPartialPacket = false; // not really matters because of continuous trasmitting auto tt1 = std::chrono::high_resolution_clock::now(); - int samplesSent = dev.StreamTx(testStreamIndex, src, samplesInPkt * txPacketCount, &txMeta); + uint32_t samplesSent = dev.StreamTx(testStreamIndex, src, samplesInPkt * txPacketCount, &txMeta); bsent += txPacketCount; - //int samplesSent2 = dev.StreamTx(0, (const void **)src, samplesInPkt*txPacketCount/4, &txMeta); + //uint32_t samplesSent2 = dev.StreamTx(0, (const void **)src, samplesInPkt*txPacketCount/4, &txMeta); auto tt2 = std::chrono::high_resolution_clock::now(); int duration = std::chrono::duration_cast(tt2 - tt1).count(); if (show) @@ -366,7 +366,7 @@ bool TxTiming(SDRDevice& dev, bool MIMO, float tsDelay_ms) } // skip some packets at the start in case of leftover data garbage - int64_t ignoreSamplesAtStart = 0; //samplesInPkt*1024; + uint64_t ignoreSamplesAtStart = 0; //samplesInPkt*1024; //printf("Skipping %i rx samples at the beginning", ignoreSamplesAtStart); //Initialize stream @@ -392,7 +392,7 @@ bool TxTiming(SDRDevice& dev, bool MIMO, float tsDelay_ms) { //Receive samples SDRDevice::StreamMeta rxMeta; - int samplesRead = dev.StreamRx(chipIndex, dest, samplesInPkt * txPacketCount, &rxMeta); + uint32_t samplesRead = dev.StreamRx(chipIndex, dest, samplesInPkt * txPacketCount, &rxMeta); if (samplesRead < 0) { printf("Failed to StreamRx\n"); @@ -409,9 +409,9 @@ bool TxTiming(SDRDevice& dev, bool MIMO, float tsDelay_ms) if (!txPending) { txMeta.timestamp = rxNow + txDeltaTS; - txMeta.useTimestamp = true; - txMeta.flush = true; - int samplesSent = dev.StreamTx(chipIndex, src, samplesInPkt, &txMeta); + txMeta.waitForTimestamp = true; + txMeta.flushPartialPacket = true; + uint32_t samplesSent = dev.StreamTx(chipIndex, src, samplesInPkt, &txMeta); if (samplesSent <= 0) { if (samplesSent < 0) @@ -440,7 +440,7 @@ bool TxTiming(SDRDevice& dev, bool MIMO, float tsDelay_ms) } else // wait and check for tx packet reception { - for (int j = 0; j < samplesRead; ++j) + for (uint32_t j = 0; j < samplesRead; ++j) { float i = dest[0][j].real(); float q = dest[0][j].imag();