From 39717c24d269073ece43a8bfe518d728bcbbdca8 Mon Sep 17 00:00:00 2001 From: Ricardas Jonaitis Date: Sat, 24 Feb 2024 00:19:09 +0200 Subject: [PATCH] xtrx read firmware data, extract common lms7002m configuration functions --- src/CMakeLists.txt | 2 +- src/DSP/FFT.cpp | 203 ++++++++++++++++++ src/DSP/FFT.h | 52 +++++ src/DSP/RingBuffer.h | 72 +++++++ src/Si5351C/Si5351C.cpp | 3 +- src/Si5351C/Si5351C.h | 1 - src/boards/LMS7002M_SDRDevice.cpp | 156 ++++++++++++++ src/boards/LMS7002M_SDRDevice.h | 8 + src/boards/LimeSDR/LimeSDR.cpp | 195 +++++++++++------ src/boards/LimeSDR/LimeSDR.h | 6 + src/boards/LimeSDR_X3/LimeSDR_X3.cpp | 22 +- src/boards/LimeSDR_X3/LimeSDR_X3.h | 1 + src/boards/LimeSDR_XTRX/LimeSDR_XTRX.cpp | 170 ++++----------- src/boards/LimeSDR_XTRX/LimeSDR_XTRX.h | 4 +- src/boards/LimeSDR_XTRX/LimeSDR_XTRXEntry.cpp | 3 +- src/boards/MMX8/MM_X8.cpp | 3 +- src/boards/MMX8/MM_X8.h | 1 + src/boards/MMX8/MM_X8Entry.cpp | 3 +- src/comms/USB/CMakeLists.txt | 1 + src/comms/USB/LMS64C_ADF4002_Over_USB.cpp | 19 ++ src/comms/USB/LMS64C_ADF4002_Over_USB.h | 25 +++ src/comms/USB/LMS64C_FPGA_Over_USB.cpp | 10 + src/comms/USB/LMS64C_FPGA_Over_USB.h | 3 + src/fftviewer_wxgui/fftviewer_frFFTviewer.cpp | 6 +- src/include/limesuite/SDRDevice.cpp | 18 +- src/include/limesuite/SDRDevice.h | 38 ++-- src/include/limesuite/complex.h | 99 ++++++++- src/lms7002m/LMS7002M.cpp | 6 + src/lms7002m/LMS7002M_validation.cpp | 3 +- src/protocols/LMS64CProtocol.cpp | 4 +- 30 files changed, 885 insertions(+), 252 deletions(-) create mode 100644 src/DSP/FFT.cpp create mode 100644 src/DSP/FFT.h create mode 100644 src/DSP/RingBuffer.h create mode 100644 src/comms/USB/LMS64C_ADF4002_Over_USB.cpp create mode 100644 src/comms/USB/LMS64C_ADF4002_Over_USB.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c22083de4..8b73ba3c7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ set(LIME_SUITE_SOURCES EnumToString.cpp DSP/Equalizer.cpp + DSP/FFT.cpp memory/MemoryPool.cpp mcu_program/spi.cpp @@ -62,7 +63,6 @@ set(LIME_SUITE_SOURCES mcu_program/common_src/lms7002m_filters.c API/LMS_APIWrapper.cpp - include/limesuite/commonTypes.cpp include/limesuite/OpStatus.cpp include/limesuite/SDRDevice.cpp diff --git a/src/DSP/FFT.cpp b/src/DSP/FFT.cpp new file mode 100644 index 000000000..568d53718 --- /dev/null +++ b/src/DSP/FFT.cpp @@ -0,0 +1,203 @@ +#include "FFT.h" + +#include +#include + +namespace lime { + +FFT::FFT(uint32_t size) + : samplesFIFO(size * 128) +{ + m_fftCalcIn.resize(size); + m_fftCalcOut.resize(size); + m_fftCalcPlan = kiss_fft_alloc(size, 0, 0, 0); + + doWork.store(true, std::memory_order_relaxed); + mWorkerThread = std::thread(&FFT::ProcessLoop, this); + + GenerateWindowCoefficients(WindowFunctionType::BLACKMAN_HARRIS, m_fftCalcIn.size(), mWindowCoefs); +} + +FFT::~FFT() +{ + doWork.store(false, std::memory_order_relaxed); + inputAvailable.notify_one(); + if (mWorkerThread.joinable()) + mWorkerThread.join(); + kiss_fft_free(m_fftCalcPlan); +} + +int FFT::PushSamples(const complex32f_t* samples, uint32_t count) +{ + int produced = samplesFIFO.Produce(samples, count); + inputAvailable.notify_one(); + return produced; +} + +void FFT::SetResultsCallback(FFT::CallbackType fptr, void* userData) +{ + resultsCallback = fptr; + mUserData = userData; +} + +std::vector FFT::Calc(const std::vector& samples, WindowFunctionType window) +{ + const int fftSize = samples.size(); + std::vector coefs; + GenerateWindowCoefficients(window, fftSize, coefs); + + std::vector fftIn(fftSize); + std::vector fftOut(fftSize); + std::vector bins(fftSize); + kiss_fft_cfg plan = kiss_fft_alloc(fftSize, 0, 0, 0); + + for (int i = 0; i < fftSize; ++i) + { + fftIn[i].r = samples[i].i * coefs[i]; + fftIn[i].i = samples[i].q * coefs[i]; + } + kiss_fft(plan, fftIn.data(), fftOut.data()); + for (int i = 0; i < fftSize; ++i) + { + bins[i] = (fftOut[i].r * fftOut[i].r + fftOut[i].i * fftOut[i].i) / (fftSize * fftSize); + } + kiss_fft_free(plan); + return bins; +} + +void FFT::ConvertToDBFS(std::vector& bins) +{ + for (float& amplitude : bins) + amplitude = amplitude > 0 ? 10 * log10(amplitude) : -150; +} + +void FFT::Calculate(const complex16_t* src, uint32_t count, std::vector& outputBins) +{ + assert(count == m_fftCalcIn.size()); + for (uint32_t i = 0; i < count; ++i) + { + m_fftCalcIn[i].r = src[i].i / 32768.0 * mWindowCoefs[i]; + m_fftCalcIn[i].i = src[i].q / 32768.0 * mWindowCoefs[i]; + } + kiss_fft(m_fftCalcPlan, m_fftCalcIn.data(), m_fftCalcOut.data()); + outputBins.resize(count); + for (uint32_t i = 0; i < count; ++i) + { + float amplitude = (m_fftCalcOut[i].r * m_fftCalcOut[i].r + m_fftCalcOut[i].i * m_fftCalcOut[i].i) / (count * count); + outputBins[i] = amplitude > 0 ? 10 * log10(amplitude) : -150; + } +} + +void FFT::Calculate(const complex32f_t* src, uint32_t count, std::vector& outputBins) +{ + assert(count == m_fftCalcIn.size()); + for (uint32_t i = 0; i < count; ++i) + { + m_fftCalcIn[i].r = src[i].i * mWindowCoefs[i]; + m_fftCalcIn[i].i = src[i].q * mWindowCoefs[i]; + } + kiss_fft(m_fftCalcPlan, m_fftCalcIn.data(), m_fftCalcOut.data()); + outputBins.resize(count); + for (uint32_t i = 0; i < count; ++i) + { + float amplitude = (m_fftCalcOut[i].r * m_fftCalcOut[i].r + m_fftCalcOut[i].i * m_fftCalcOut[i].i) / (count * count); + outputBins[i] = amplitude > 0 ? 10 * log10(amplitude) : -150; + } +} + +void FFT::ProcessLoop() +{ + std::vector samples(m_fftCalcIn.size()); + std::vector fftBins(m_fftCalcOut.size()); + std::vector avgOutput(m_fftCalcOut.size()); + uint32_t samplesReady = 0; + + std::unique_lock lk(inputMutex); + + int resultsDone = 0; + while (doWork.load(std::memory_order_relaxed) == true) + { + if (inputAvailable.wait_for(lk, std::chrono::milliseconds(2000)) == std::cv_status::timeout) + { + printf("plot timeout\n"); + continue; + } + + int samplesNeeded = samples.size() - samplesReady; + int ret = samplesFIFO.Consume(samples.data() + samplesReady, samplesNeeded); + samplesReady += ret; + + if (samplesReady == samples.size()) + { + // auto t1 = std::chrono::high_resolution_clock::now(); + Calculate(samples.data(), samples.size(), fftBins); + ++resultsDone; + samplesReady = 0; + + for (uint32_t i = 0; i < fftBins.size(); ++i) + avgOutput[i] += fftBins[i] * fftBins[i]; + + if (resultsDone == avgCount) + { + if (resultsCallback) + { + // to log scale + for (uint i = 0; i < fftBins.size(); ++i) + avgOutput[i] = sqrt(avgOutput[i] / avgCount); + for (uint i = 0; i < fftBins.size(); ++i) + { + fftBins[i] = avgOutput[i] > 0 ? 10 * log10(avgOutput[i]) : -150; + } + resultsCallback(fftBins, mUserData); + } + resultsDone = 0; + memset(avgOutput.data(), 0, avgOutput.size() * sizeof(float)); + } + // auto t2 = std::chrono::high_resolution_clock::now(); + // auto timePeriod = std::chrono::duration_cast(t2 - t1).count(); + //printf("FFT update %lius\n", timePeriod); + } + } +} + +void FFT::GenerateWindowCoefficients(WindowFunctionType func, uint32_t N /*coef count*/, std::vector& windowFcoefs) +{ + float amplitudeCorrection = 1; + windowFcoefs.resize(N); + float a0 = 0.35875; + float a1 = 0.48829; + float a2 = 0.14128; + float a3 = 0.01168; + float PI = 3.14159265359; + switch (func) + { + case WindowFunctionType::BLACKMAN_HARRIS: + for (uint32_t i = 0; i < N; ++i) + windowFcoefs[i] = + a0 - a1 * cos((2 * PI * i) / (N - 1)) + a2 * cos((4 * PI * i) / (N - 1)) - a3 * cos((6 * PI * i) / (N - 1)); + break; + case WindowFunctionType::HAMMING: + amplitudeCorrection = 0; + a0 = 0.54; + for (uint32_t i = 0; i < N; ++i) + windowFcoefs[i] = a0 - (1 - a0) * cos((2 * PI * i) / (N)); + break; + case WindowFunctionType::HANNING: + amplitudeCorrection = 0; + for (uint32_t i = 0; i < N; ++i) + windowFcoefs[i] = 0.5 * (1 - cos((2 * PI * i) / (N))); + break; + case WindowFunctionType::NONE: + default: + for (uint32_t i = 0; i < N; ++i) + windowFcoefs[i] = 1; + return; + } + for (uint32_t i = 0; i < N; ++i) + amplitudeCorrection += windowFcoefs[i]; + amplitudeCorrection = 1.0 / (amplitudeCorrection / N); + for (uint32_t i = 0; i < N; ++i) + windowFcoefs[i] *= amplitudeCorrection; +} + +} // namespace lime diff --git a/src/DSP/FFT.h b/src/DSP/FFT.h new file mode 100644 index 000000000..c0b2138c8 --- /dev/null +++ b/src/DSP/FFT.h @@ -0,0 +1,52 @@ +#pragma once + +#include "limesuite/complex.h" +#include +#include +#include +#include +#include "RingBuffer.h" +#include "../external/kissFFT/kiss_fft.h" + +namespace lime { + +class FFT +{ + public: + typedef void (*CallbackType)(const std::vector& bins, void* userData); + FFT(uint32_t size); + ~FFT(); + + int PushSamples(const complex32f_t* samples, uint32_t count); + void SetResultsCallback(FFT::CallbackType fptr, void* userData); + + enum class WindowFunctionType { NONE = 0, BLACKMAN_HARRIS, HAMMING, HANNING }; + static void GenerateWindowCoefficients(WindowFunctionType type, uint32_t coefCount, std::vector& coefs); + static std::vector Calc(const std::vector& samples, WindowFunctionType window = WindowFunctionType::NONE); + static void ConvertToDBFS(std::vector& bins); + + private: + void Calculate(const complex16_t* src, uint32_t count, std::vector& outputBins); + void Calculate(const complex32f_t* src, uint32_t count, std::vector& outputBins); + void ProcessLoop(); + + RingBuffer samplesFIFO; + + std::thread mWorkerThread; + + kiss_fft_cfg m_fftCalcPlan; + std::vector mWindowCoefs; + std::vector m_fftCalcIn; + std::vector m_fftCalcOut; + + std::atomic doWork; + std::condition_variable inputAvailable; + std::mutex inputMutex; + + CallbackType resultsCallback; + void* mUserData; + + int avgCount = 100; +}; + +} // namespace lime \ No newline at end of file diff --git a/src/DSP/RingBuffer.h b/src/DSP/RingBuffer.h new file mode 100644 index 000000000..5c3e7010f --- /dev/null +++ b/src/DSP/RingBuffer.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +template class RingBuffer +{ + public: + RingBuffer(int32_t count) + : headIndex(0) + , tailIndex(0) + , size(0) + , capacity(count) + { + buffer.resize(count); + } + + int Consume(T* dest, int32_t count) + { + std::lock_guard lck(mMutex); + int consumed = 0; + count = std::min(count, size); + + if (headIndex + count >= capacity) + { + int toCopy = capacity - headIndex; + memcpy(dest, &buffer[headIndex], toCopy * sizeof(T)); + dest += toCopy; + headIndex = 0; + size -= toCopy; + count -= toCopy; + consumed += toCopy; + } + memcpy(dest, &buffer[headIndex], count * sizeof(T)); + headIndex += count; + size -= count; + consumed += count; + return consumed; + } + + int Produce(const T* src, int32_t count) + { + std::lock_guard lck(mMutex); + count = std::min(count, capacity - size); + int produced = 0; + + if (tailIndex + count >= capacity) + { + int toCopy = capacity - tailIndex; + memcpy(&buffer[tailIndex], src, toCopy * sizeof(T)); + src += toCopy; + count -= toCopy; + size += toCopy; + produced += toCopy; + tailIndex = 0; + } + memcpy(&buffer[tailIndex], src, count * sizeof(T)); + tailIndex += count; + size += count; + produced += count; + return produced; + } + + private: + std::mutex mMutex; + std::vector buffer; + int headIndex; + int tailIndex; + int size; + int capacity; +}; diff --git a/src/Si5351C/Si5351C.cpp b/src/Si5351C/Si5351C.cpp index 7fa913d77..4ffde26f1 100644 --- a/src/Si5351C/Si5351C.cpp +++ b/src/Si5351C/Si5351C.cpp @@ -20,8 +20,7 @@ using namespace std; using namespace lime; -// TODO: check if actually needed -// static uint8_t addrSi5351 = 0x20; +static const uint8_t addrSi5351 = 0xC0; /// Splits float into fraction integers A + B/C void realToFrac(const float real, int& A, int& B, int& C) diff --git a/src/Si5351C/Si5351C.h b/src/Si5351C/Si5351C.h index d1557e2e0..2ace29dc1 100644 --- a/src/Si5351C/Si5351C.h +++ b/src/Si5351C/Si5351C.h @@ -105,7 +105,6 @@ class LIME_API Si5351C private: void FindVCO(Si5351_Channel* clocks, Si5351_PLL* plls, const unsigned long Fmin, const unsigned long Fmax); lime::II2C& comms; - int addrSi5351; Si5351_PLL PLL[2]; Si5351_Channel CLK[8]; diff --git a/src/boards/LMS7002M_SDRDevice.cpp b/src/boards/LMS7002M_SDRDevice.cpp index 06847e82a..119911f3d 100644 --- a/src/boards/LMS7002M_SDRDevice.cpp +++ b/src/boards/LMS7002M_SDRDevice.cpp @@ -3,6 +3,9 @@ #include "DeviceExceptions.h" #include "FPGA_common.h" #include "limesuite/LMS7002M.h" +#include "mcu_program/common_src/lms7002m_calibrations.h" +#include "mcu_program/common_src/lms7002m_filters.h" +#include "lms7002m/MCU_BD.h" #include "LMSBoards.h" #include "Logger.h" #include "TRXLooper.h" @@ -964,6 +967,12 @@ void LMS7002M_SDRDevice::StreamStatus(uint8_t moduleIndex, SDRDevice::StreamStat } } +OpStatus LMS7002M_SDRDevice::UploadMemory( + eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback) +{ + return OpStatus::NOT_IMPLEMENTED; +} + OpStatus LMS7002M_SDRDevice::UpdateFPGAInterfaceFrequency(LMS7002M& soc, FPGA& fpga, uint8_t chipIndex) { double fpgaTxPLL = soc.GetReferenceClk_TSP(TRXDir::Tx); @@ -1066,4 +1075,151 @@ void LMS7002M_SDRDevice::SetGainInformationInDescriptor(RFSOCDescriptor& descrip #endif } +OpStatus LMS7002M_SDRDevice::LMS7002LOConfigure(LMS7002M* chip, const SDRDevice::SDRConfig& cfg) +{ + bool rxUsed = false; + bool txUsed = false; + for (int i = 0; i < 2; ++i) + { + const SDRDevice::ChannelConfig& ch = cfg.channel[i]; + rxUsed |= ch.rx.enabled; + txUsed |= ch.tx.enabled; + } + + OpStatus status = OpStatus::SUCCESS; + if (cfg.referenceClockFreq != 0) + { + status = chip->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, cfg.referenceClockFreq, 0); + if (status != OpStatus::SUCCESS) + return status; + } + + const bool tddMode = cfg.channel[0].rx.centerFrequency == cfg.channel[0].tx.centerFrequency; + chip->EnableSXTDD(tddMode); + // Rx PLL is not used in TDD mode + if (!tddMode && rxUsed && cfg.channel[0].rx.centerFrequency > 0) + { + status = chip->SetFrequencySX(TRXDir::Rx, cfg.channel[0].rx.centerFrequency); + if (status != OpStatus::SUCCESS) + return status; + } + if (txUsed && cfg.channel[0].tx.centerFrequency > 0) + { + status = chip->SetFrequencySX(TRXDir::Tx, cfg.channel[0].tx.centerFrequency); + if (status != OpStatus::SUCCESS) + return status; + } + return status; +} + +OpStatus LMS7002M_SDRDevice::LMS7002ChannelConfigure(LMS7002M* chip, const SDRDevice::ChannelConfig& config, uint8_t channelIndex) +{ + const SDRDevice::ChannelConfig& ch = config; + chip->SetActiveChannel((channelIndex & 1) ? LMS7002M::Channel::ChB : LMS7002M::Channel::ChA); + + chip->EnableChannel(TRXDir::Rx, channelIndex, ch.rx.enabled); + chip->SetPathRFE(static_cast(ch.rx.path)); + if (static_cast(ch.rx.path) == LMS7002M::PathRFE::LB1 || + static_cast(ch.rx.path) == LMS7002M::PathRFE::LB2) + { + // TODO: confirm which should be used for loopback + if (ch.rx.lpf > 0) + chip->Modify_SPI_Reg_bits(LMS7_INPUT_CTL_PGA_RBB, 3); // baseband loopback + else + chip->Modify_SPI_Reg_bits(LMS7_INPUT_CTL_PGA_RBB, 2); // LPF bypass + } + + chip->EnableChannel(TRXDir::Tx, channelIndex, ch.tx.enabled); + chip->SetBandTRF(ch.tx.path); + + for (const auto& gain : ch.rx.gain) + { + SetGain(0, TRXDir::Rx, channelIndex, gain.first, gain.second); + } + + for (const auto& gain : ch.tx.gain) + { + SetGain(0, TRXDir::Tx, channelIndex, gain.first, gain.second); + } + // TODO: set GFIR filters... + return OpStatus::SUCCESS; +} + +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); + 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) + 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) + return lime::ReportError(OpStatus::ERROR, "Tx ch%i GFIR config failed", i); + + if (ch.rx.calibrate && ch.rx.enabled) + { + SetupCalibrations(chip, ch.rx.sampleRate); + int status = CalibrateRx(false, false); + if (status != MCU_BD::MCU_NO_ERROR) + return lime::ReportError(OpStatus::ERROR, "Rx ch%i DC/IQ calibration failed: %s", i, MCU_BD::MCUStatusMessage(status)); + } + if (ch.tx.calibrate && ch.tx.enabled) + { + SetupCalibrations(chip, ch.tx.sampleRate); + int status = CalibrateTx(false); + if (status != MCU_BD::MCU_NO_ERROR) + return lime::ReportError(OpStatus::ERROR, "Rx ch%i DC/IQ calibration failed: %s", i, MCU_BD::MCUStatusMessage(status)); + } + if (ch.rx.lpf > 0 && ch.rx.enabled) + { + SetupCalibrations(chip, ch.rx.sampleRate); + int status = TuneRxFilter(ch.rx.lpf); + if (status != MCU_BD::MCU_NO_ERROR) + return lime::ReportError(OpStatus::ERROR, "Rx ch%i filter calibration failed: %s", i, MCU_BD::MCUStatusMessage(status)); + } + if (ch.tx.lpf > 0 && ch.tx.enabled) + { + SetupCalibrations(chip, ch.tx.sampleRate); + int status = TuneTxFilter(ch.tx.lpf); + if (status != MCU_BD::MCU_NO_ERROR) + return lime::ReportError(OpStatus::ERROR, "Tx ch%i filter calibration failed: %s", i, MCU_BD::MCUStatusMessage(status)); + } + return OpStatus::SUCCESS; +} + +OpStatus LMS7002M_SDRDevice::LMS7002TestSignalConfigure( + LMS7002M* chip, const SDRDevice::ChannelConfig& config, uint8_t channelIndex) +{ + const SDRDevice::ChannelConfig& ch = config; + chip->Modify_SPI_Reg_bits(LMS7_INSEL_RXTSP, ch.rx.testSignal.enabled ? 1 : 0); + if (ch.rx.testSignal.enabled) + { + const SDRDevice::ChannelConfig::Direction::TestSignal& signal = ch.rx.testSignal; + bool fullscale = signal.scale == SDRDevice::ChannelConfig::Direction::TestSignal::Scale::Full; + bool div4 = signal.divide == SDRDevice::ChannelConfig::Direction::TestSignal::Divide::Div4; + chip->Modify_SPI_Reg_bits(LMS7_TSGFC_RXTSP, fullscale ? 1 : 0); + chip->Modify_SPI_Reg_bits(LMS7_TSGFCW_RXTSP, div4 ? 1 : 0); + chip->Modify_SPI_Reg_bits(LMS7_TSGMODE_RXTSP, signal.dcMode ? 1 : 0); + chip->SPI_write(0x040C, 0x01FF); // DC.. bypasss + // LMS7_TSGMODE_RXTSP change resets DC values + return chip->LoadDC_REG_IQ(TRXDir::Rx, signal.dcValue.real(), signal.dcValue.imag()); + } + + chip->Modify_SPI_Reg_bits(LMS7_INSEL_TXTSP, ch.tx.testSignal.enabled ? 1 : 0); + if (ch.tx.testSignal.enabled) + { + const SDRDevice::ChannelConfig::Direction::TestSignal& signal = ch.tx.testSignal; + bool fullscale = signal.scale == SDRDevice::ChannelConfig::Direction::TestSignal::Scale::Full; + bool div4 = signal.divide == SDRDevice::ChannelConfig::Direction::TestSignal::Divide::Div4; + chip->Modify_SPI_Reg_bits(LMS7_TSGFC_TXTSP, fullscale ? 1 : 0); + chip->Modify_SPI_Reg_bits(LMS7_TSGFCW_TXTSP, div4 ? 1 : 0); + chip->Modify_SPI_Reg_bits(LMS7_TSGMODE_TXTSP, signal.dcMode ? 1 : 0); + chip->SPI_write(0x040C, 0x01FF); // DC.. bypasss + // LMS7_TSGMODE_TXTSP change resets DC values + return chip->LoadDC_REG_IQ(TRXDir::Tx, signal.dcValue.real(), signal.dcValue.imag()); + } + return OpStatus::SUCCESS; +} + } // namespace lime diff --git a/src/boards/LMS7002M_SDRDevice.h b/src/boards/LMS7002M_SDRDevice.h index 575761645..ade792b3a 100644 --- a/src/boards/LMS7002M_SDRDevice.h +++ b/src/boards/LMS7002M_SDRDevice.h @@ -110,6 +110,9 @@ class LIME_API LMS7002M_SDRDevice : public SDRDevice virtual void* GetInternalChip(uint32_t index) override; + virtual OpStatus UploadMemory( + eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback) override; + virtual int ReadFPGARegister(uint32_t address); virtual OpStatus WriteFPGARegister(uint32_t address, uint32_t value); @@ -117,6 +120,11 @@ class LIME_API LMS7002M_SDRDevice : public SDRDevice static OpStatus UpdateFPGAInterfaceFrequency(LMS7002M& soc, FPGA& fpga, uint8_t chipIndex); void SetGainInformationInDescriptor(RFSOCDescriptor& descriptor); + OpStatus LMS7002LOConfigure(LMS7002M* chip, const SDRDevice::SDRConfig& config); + OpStatus LMS7002ChannelConfigure(LMS7002M* chip, const SDRDevice::ChannelConfig& config, uint8_t channelIndex); + OpStatus LMS7002ChannelCalibration(LMS7002M* chip, const SDRDevice::ChannelConfig& config, uint8_t channelIndex); + OpStatus LMS7002TestSignalConfigure(LMS7002M* chip, const SDRDevice::ChannelConfig& config, uint8_t channelIndex); + DataCallbackType mCallback_logData; LogCallbackType mCallback_logMessage; std::vector mLMSChips; diff --git a/src/boards/LimeSDR/LimeSDR.cpp b/src/boards/LimeSDR/LimeSDR.cpp index 768c71a82..c3237cbb0 100644 --- a/src/boards/LimeSDR/LimeSDR.cpp +++ b/src/boards/LimeSDR/LimeSDR.cpp @@ -36,6 +36,7 @@ using namespace lime; static const uint8_t SPI_LMS7002M = 0; static const uint8_t SPI_FPGA = 1; +static const uint8_t SPI_ADF4002 = 2; static const SDRDevice::CustomParameter CP_VCTCXO_DAC = { "VCTCXO DAC (volatile)", 0, 0, 65535, false }; static const SDRDevice::CustomParameter CP_TEMPERATURE = { "Board Temperature", 1, 0, 65535, true }; @@ -103,6 +104,7 @@ LimeSDR::LimeSDR(std::shared_ptr spiLMS, , mSerialPort(commsPort) , mlms7002mPort(spiLMS) , mfpgaPort(spiFPGA) + , mConfigInProgress(false) { SDRDevice::Descriptor descriptor = GetDeviceInfo(); @@ -126,7 +128,7 @@ LimeSDR::LimeSDR(std::shared_ptr spiLMS, RFSOCDescriptor soc; soc.name = "LMS"; soc.channelCount = 2; - soc.pathNames[TRXDir::Rx] = { "None", "LNAH", "LNAL", "LNAW" }; + soc.pathNames[TRXDir::Rx] = { "None", "LNAH", "LNAL", "LNAW", "LB1", "LB2" }; soc.pathNames[TRXDir::Tx] = { "None", "Band1", "Band2" }; soc.samplingRateRange = { 100e3, 61.44e6, 0 }; soc.frequencyRange = { 100e3, 3.8e9, 0 }; @@ -137,6 +139,8 @@ LimeSDR::LimeSDR(std::shared_ptr spiLMS, soc.antennaRange[TRXDir::Rx]["LNAH"] = { 2e9, 2.6e9 }; soc.antennaRange[TRXDir::Rx]["LNAL"] = { 700e6, 900e6 }; soc.antennaRange[TRXDir::Rx]["LNAW"] = { 700e6, 2.6e9 }; + soc.antennaRange[TRXDir::Rx]["LB1"] = soc.antennaRange[TRXDir::Rx]["LNAL"]; + soc.antennaRange[TRXDir::Rx]["LB2"] = soc.antennaRange[TRXDir::Rx]["LNAW"]; soc.antennaRange[TRXDir::Tx]["Band1"] = { 30e6, 1.9e9 }; soc.antennaRange[TRXDir::Tx]["Band2"] = { 2e9, 2.6e9 }; @@ -149,6 +153,12 @@ LimeSDR::LimeSDR(std::shared_ptr spiLMS, descriptor.socTree = std::make_shared("SDR-USB", eDeviceNodeClass::SDRDevice, this); descriptor.socTree->children.push_back(fpgaNode); + const std::unordered_map eepromMap = { { eMemoryRegion::VCTCXO_DAC, { 16, 2 } } }; + descriptor.memoryDevices[MEMORY_DEVICES_TEXT.at(eMemoryDevice::FPGA_FLASH)] = + std::make_shared(this, eMemoryDevice::FPGA_FLASH); + descriptor.memoryDevices[MEMORY_DEVICES_TEXT.at(eMemoryDevice::EEPROM)] = + std::make_shared(this, eMemoryDevice::EEPROM, eepromMap); + mDeviceDescriptor = descriptor; //must configure synthesizer before using LimeSDR @@ -186,88 +196,98 @@ LimeSDR::~LimeSDR() OpStatus LimeSDR::Configure(const SDRConfig& cfg, uint8_t moduleIndex = 0) { - try - { - std::vector errors; - bool isValidConfig = LMS7002M_Validate(cfg, errors); + OpStatus status = OpStatus::SUCCESS; + std::vector errors; + bool isValidConfig = LMS7002M_Validate(cfg, errors); - if (!isValidConfig) - { - std::stringstream ss; + if (!isValidConfig) + { + std::stringstream ss; + for (const auto& err : errors) + ss << err << std::endl; + return lime::ReportError(OpStatus::ERROR, "LimeSDR: %s.", ss.str().c_str()); + } - for (const auto& err : errors) - { - ss << err << std::endl; - } + bool rxUsed = false; + bool txUsed = false; + for (int i = 0; i < 2; ++i) + { + const ChannelConfig& ch = cfg.channel[i]; + rxUsed |= ch.rx.enabled; + txUsed |= ch.tx.enabled; + } - throw std::logic_error(ss.str()); + try + { + mConfigInProgress = true; + LMS7002M* chip = mLMSChips.at(0); + if (!cfg.skipDefaults) + { + const bool skipTune = true; + // TODO: skip tune + status = Init(); + if (status != OpStatus::SUCCESS) + return status; } - bool rxUsed = false; - bool txUsed = false; + status = LMS7002LOConfigure(chip, cfg); + if (status != OpStatus::SUCCESS) + return lime::ReportError(OpStatus::ERROR, "LimeSDR: LO configuration failed."); for (int i = 0; i < 2; ++i) { - const ChannelConfig& ch = cfg.channel[i]; - rxUsed |= ch.rx.enabled; - txUsed |= ch.tx.enabled; + status = LMS7002ChannelConfigure(chip, cfg.channel[i], i); + if (status != OpStatus::SUCCESS) + return lime::ReportError(OpStatus::ERROR, "LimeSDR: channel%i configuration failed."); + LMS7002TestSignalConfigure(chip, cfg.channel[i], i); } - // config validation complete, now do the actual configuration - - if (cfg.referenceClockFreq != 0) - mLMSChips[0]->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, cfg.referenceClockFreq, 0); + // enabled ADC/DAC is required for FPGA to work + chip->Modify_SPI_Reg_bits(LMS7_PD_RX_AFE1, 0); + chip->Modify_SPI_Reg_bits(LMS7_PD_TX_AFE1, 0); + chip->SetActiveChannel(LMS7002M::Channel::ChA); - if (rxUsed && cfg.channel[0].rx.centerFrequency > 0) - mLMSChips[0]->SetFrequencySX(TRXDir::Rx, cfg.channel[0].rx.centerFrequency); - if (txUsed && cfg.channel[0].tx.centerFrequency > 0) - mLMSChips[0]->SetFrequencySX(TRXDir::Tx, cfg.channel[0].tx.centerFrequency); + double sampleRate; + if (rxUsed) + sampleRate = cfg.channel[0].rx.sampleRate; + else + sampleRate = cfg.channel[0].tx.sampleRate; + if (sampleRate > 0) + { + status = SetSampleRate(0, TRXDir::Rx, 0, sampleRate, cfg.channel[0].rx.oversample); + if (status != OpStatus::SUCCESS) + return lime::ReportError(OpStatus::ERROR, "LimeSDR: failed to set sampling rate."); + } for (int i = 0; i < 2; ++i) { - const ChannelConfig& ch = cfg.channel[i]; - mLMSChips[0]->SetActiveChannel((i & 1) ? LMS7002M::Channel::ChB : LMS7002M::Channel::ChA); - mLMSChips[0]->EnableChannel(TRXDir::Rx, i, ch.rx.enabled); - mLMSChips[0]->EnableChannel(TRXDir::Tx, i, ch.tx.enabled); - - mLMSChips[0]->SetPathRFE(static_cast(ch.rx.path)); - - if (ch.rx.path == 4) - { - mLMSChips[0]->Modify_SPI_Reg_bits(LMS7_INPUT_CTL_PGA_RBB, 3); // baseband loopback - } - mLMSChips[0]->SetBandTRF(ch.tx.path); - - for (const auto& gain : ch.rx.gain) - { - SetGain(0, TRXDir::Rx, i, gain.first, gain.second); - } - - for (const auto& gain : ch.tx.gain) - { - SetGain(0, TRXDir::Tx, i, gain.first, gain.second); - } - - // TODO: set filters... + const SDRDevice::ChannelConfig& ch = cfg.channel[i]; + LMS7002ChannelCalibration(chip, ch, i); + // TODO: should report calibration failure, but configuration can + // still work after failed calibration. } - mLMSChips[0]->SetActiveChannel(LMS7002M::Channel::ChA); + chip->SetActiveChannel(LMS7002M::Channel::ChA); - TRXDir direction = rxUsed ? TRXDir::Rx : TRXDir::Tx; - - // sampling rate - double sampleRate = cfg.channel[0].GetDirection(direction).sampleRate; + // Workaround: Toggle LimeLights transmit port to flush residual value from data interface + // uint16_t txMux = chip->Get_SPI_Reg_bits(LMS7param(TX_MUX)); + // chip->Modify_SPI_Reg_bits(LMS7param(TX_MUX), 2); + // chip->Modify_SPI_Reg_bits(LMS7param(TX_MUX), txMux); + mConfigInProgress = false; if (sampleRate > 0) { - SetSampleRate(0, direction, 0, sampleRate, cfg.channel[0].GetDirection(direction).oversample); + status = UpdateFPGAInterface(this); + if (status != OpStatus::SUCCESS) + return lime::ReportError(OpStatus::ERROR, "LimeSDR: failed to update FPGA interface frequency."); } } //try catch (std::logic_error& e) { - lime::error("LimeSDR config: %s", e.what()); - throw; + lime::error("LimeSDR_USB config: %s\n", e.what()); + return OpStatus::ERROR; } catch (std::runtime_error& e) { - throw; + lime::error("LimeSDR_USB config: %s\n", e.what()); + return OpStatus::ERROR; } return OpStatus::SUCCESS; } @@ -278,6 +298,9 @@ OpStatus LimeSDR::UpdateFPGAInterface(void* userData) constexpr int chipIndex = 0; assert(userData != nullptr); LimeSDR* pthis = static_cast(userData); + // don't care about cgen changes while doing Config(), to avoid unnecessary fpga updates + if (pthis->mConfigInProgress) + return OpStatus::SUCCESS; LMS7002M* soc = pthis->mLMSChips[chipIndex]; return UpdateFPGAInterfaceFrequency(*soc, *pthis->mFPGA, chipIndex); } @@ -350,14 +373,14 @@ OpStatus LimeSDR::Init() lms->Modify_SPI_Reg_bits(LMS7param(MAC), 1); // TODO: - // if(lms->CalibrateTxGain(0,nullptr) != 0) + // if(lms->CalibrateTxGain(0,nullptr) != OpStatus::SUCCESS) // return -1; EnableChannel(TRXDir::Rx, 0, false); EnableChannel(TRXDir::Tx, 0, false); lms->Modify_SPI_Reg_bits(LMS7param(MAC), 2); - // if(lms->CalibrateTxGain(0,nullptr) != 0) + // if(lms->CalibrateTxGain(0,nullptr) != OpStatus::SUCCESS) // return -1; EnableChannel(TRXDir::Rx, 1, false); @@ -365,9 +388,9 @@ OpStatus LimeSDR::Init() lms->Modify_SPI_Reg_bits(LMS7param(MAC), 1); - // if(lms->SetFrequency(SDRDevice::Dir::Tx, 0, lms->GetFrequency(SDRDevice::Dir::Tx, 0)) != 0) + // if(lms->SetFrequency(SDRDevice::Dir::Tx, 0, lms->GetFrequency(SDRDevice::Dir::Tx, 0)) != OpStatus::SUCCESS) // return -1; - // if(lms->SetFrequency(SDRDevice::Dir::Rx, 0, lms->GetFrequency(SDRDevice::Dir::Rx, 0)) != 0) + // if(lms->SetFrequency(SDRDevice::Dir::Rx, 0, lms->GetFrequency(SDRDevice::Dir::Rx, 0)) != OpStatus::SUCCESS) // return -1; // if (SetRate(10e6,2)!=0) @@ -474,10 +497,13 @@ OpStatus LimeSDR::SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, const bool willDoWrite = MOSI[srcIndex] & (1 << 31); for (int i = 0; i < maxBlocks && srcIndex < count; ++i) { - const bool isWrite = MOSI[srcIndex] & (1 << 31); + bool isWrite = MOSI[srcIndex] & (1 << 31); if (isWrite != willDoWrite) break; // change between write/read, flush packet + if (chipSelect == SPI_ADF4002) + isWrite = true; + if (isWrite) { switch (chipSelect) @@ -488,6 +514,9 @@ OpStatus LimeSDR::SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, case SPI_FPGA: pkt.cmd = LMS64CProtocol::CMD_BRDSPI_WR; break; + case SPI_ADF4002: + pkt.cmd = LMS64CProtocol::CMD_ADF4002_WR; + break; default: throw std::logic_error("LimeSDR SPI invalid SPI chip select"); } @@ -507,6 +536,8 @@ OpStatus LimeSDR::SPI(uint32_t chipSelect, const uint32_t* MOSI, uint32_t* MISO, case SPI_FPGA: pkt.cmd = LMS64CProtocol::CMD_BRDSPI_RD; break; + case SPI_ADF4002: + throw std::logic_error("LimeSDR ADF4002 registers reading not supported"); default: throw std::logic_error("LimeSDR SPI invalid SPI chip select"); } @@ -693,3 +724,39 @@ OpStatus LimeSDR::CustomParameterRead(std::vector& parameters { return mfpgaPort->CustomParameterRead(parameters); } + +OpStatus LimeSDR::UploadMemory( + eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback) +{ + int progMode; + LMS64CProtocol::ProgramWriteTarget target = LMS64CProtocol::ProgramWriteTarget::FPGA; + + // TODO: add FX3 firmware flashing + switch (device) + { + case eMemoryDevice::FPGA_RAM: + progMode = 0; + break; + case eMemoryDevice::FPGA_FLASH: + progMode = 1; + break; + default: + return OpStatus::INVALID_VALUE; + } + + return mfpgaPort->ProgramWrite(data, length, progMode, target, callback); +} + +OpStatus LimeSDR::MemoryWrite(std::shared_ptr storage, Region region, const void* data) +{ + if (storage == nullptr || storage->ownerDevice != this || storage->memoryDeviceType != eMemoryDevice::EEPROM) + return OpStatus::ERROR; + return mfpgaPort->MemoryWrite(region.address, data, region.size); +} + +OpStatus LimeSDR::MemoryRead(std::shared_ptr storage, Region region, void* data) +{ + if (storage == nullptr || storage->ownerDevice != this || storage->memoryDeviceType != eMemoryDevice::EEPROM) + return OpStatus::ERROR; + return mfpgaPort->MemoryRead(region.address, data, region.size); +} diff --git a/src/boards/LimeSDR/LimeSDR.h b/src/boards/LimeSDR/LimeSDR.h index d53f318ad..e54dc5825 100644 --- a/src/boards/LimeSDR/LimeSDR.h +++ b/src/boards/LimeSDR/LimeSDR.h @@ -51,6 +51,11 @@ class LimeSDR : public LMS7002M_SDRDevice virtual OpStatus CustomParameterWrite(const std::vector& parameters) override; virtual OpStatus CustomParameterRead(std::vector& parameters) override; + virtual OpStatus UploadMemory( + eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback) override; + virtual OpStatus MemoryWrite(std::shared_ptr storage, Region region, const void* data) override; + virtual OpStatus MemoryRead(std::shared_ptr storage, Region region, void* data) override; + protected: OpStatus EnableChannel(TRXDir dir, uint8_t channel, bool enabled); SDRDevice::Descriptor GetDeviceInfo(); @@ -62,6 +67,7 @@ class LimeSDR : public LMS7002M_SDRDevice std::shared_ptr mSerialPort; std::shared_ptr mlms7002mPort; std::shared_ptr mfpgaPort; + bool mConfigInProgress; }; } // namespace lime diff --git a/src/boards/LimeSDR_X3/LimeSDR_X3.cpp b/src/boards/LimeSDR_X3/LimeSDR_X3.cpp index 3963c910e..59f9c411b 100644 --- a/src/boards/LimeSDR_X3/LimeSDR_X3.cpp +++ b/src/boards/LimeSDR_X3/LimeSDR_X3.cpp @@ -571,16 +571,7 @@ OpStatus LimeSDR_X3::Configure(const SDRConfig& cfg, uint8_t socIndex) } } - if (cfg.referenceClockFreq != 0) - chip->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, cfg.referenceClockFreq, 0); - - const bool tddMode = cfg.channel[0].rx.centerFrequency == cfg.channel[0].tx.centerFrequency; - if (rxUsed && cfg.channel[0].rx.centerFrequency > 0) - chip->SetFrequencySX(TRXDir::Rx, cfg.channel[0].rx.centerFrequency); - if (txUsed && cfg.channel[0].tx.centerFrequency > 0) - chip->SetFrequencySX(TRXDir::Tx, cfg.channel[0].tx.centerFrequency); - if (tddMode) - chip->EnableSXTDD(true); + LMS7002LOConfigure(chip, cfg); if (socIndex == 0) chip->Modify_SPI_Reg_bits(LMS7_PD_TX_AFE1, 0); // enabled DAC is required for FPGA to work @@ -618,18 +609,9 @@ OpStatus LimeSDR_X3::Configure(const SDRConfig& cfg, uint8_t socIndex) for (int ch = 0; ch < 2; ++ch) { chip->SetActiveChannel((ch & 1) ? LMS7002M::Channel::ChB : LMS7002M::Channel::ChA); - - if (cfg.channel[ch].rx.testSignal.enabled) - { - chip->Modify_SPI_Reg_bits(LMS7_TSGFC_RXTSP, static_cast(cfg.channel[ch].rx.testSignal.scale)); - chip->Modify_SPI_Reg_bits(LMS7_TSGMODE_RXTSP, cfg.channel[ch].rx.testSignal.dcMode ? 1 : 0); - chip->SPI_write(0x040C, 0x01FF); // DC.. bypasss - // chip->LoadDC_REG_IQ(false, 0x1230, 0x4560); // gets reset by starting stream - } - chip->Modify_SPI_Reg_bits(LMS7_INSEL_TXTSP, cfg.channel[ch].tx.testSignal.enabled ? 1 : 0); - ConfigureDirection(TRXDir::Rx, chip, cfg, ch, socIndex); ConfigureDirection(TRXDir::Tx, chip, cfg, ch, socIndex); + LMS7002TestSignalConfigure(chip, cfg.channel[ch], ch); } if (socIndex == 0) diff --git a/src/boards/LimeSDR_X3/LimeSDR_X3.h b/src/boards/LimeSDR_X3/LimeSDR_X3.h index 2d8f4a1d1..57eefe5a7 100644 --- a/src/boards/LimeSDR_X3/LimeSDR_X3.h +++ b/src/boards/LimeSDR_X3/LimeSDR_X3.h @@ -14,6 +14,7 @@ namespace lime { class LitePCIe; class Equalizer; class SlaveSelectShim; +class ISerialPort; class LimeSDR_X3 : public LMS7002M_SDRDevice { diff --git a/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.cpp b/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.cpp index aba647f55..006d22fb7 100644 --- a/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.cpp +++ b/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.cpp @@ -105,41 +105,49 @@ OpStatus LimeSDR_XTRX::LMS1_UpdateFPGAInterface(void* userData) // Do not perform any unnecessary configuring to device in constructor, so you // could read back it's state for debugging purposes -LimeSDR_XTRX::LimeSDR_XTRX( - std::shared_ptr spiRFsoc, std::shared_ptr spiFPGA, std::shared_ptr sampleStream, double refClk) +LimeSDR_XTRX::LimeSDR_XTRX(std::shared_ptr spiRFsoc, + std::shared_ptr spiFPGA, + std::shared_ptr sampleStream, + std::shared_ptr control, + double refClk) : LMS7002M_SDRDevice() , lms7002mPort(spiRFsoc) , fpgaPort(spiFPGA) , mStreamPort(sampleStream) + , mSerialPort(control) , mConfigInProgress(false) { SDRDevice::Descriptor& desc = mDeviceDescriptor; desc.name = GetDeviceName(LMS_DEV_LIMESDR_XTRX); - // LMS64CProtocol::FirmwareInfo fw; - // LMS64CProtocol::GetFirmwareInfo(controlPipe, fw); - // LMS64CProtocol::FirmwareToDescriptor(fw, desc); + LMS64CProtocol::FirmwareInfo fw; + LMS64CProtocol::GetFirmwareInfo(*mSerialPort, fw); + LMS64CProtocol::FirmwareToDescriptor(fw, desc); desc.spiSlaveIds = { { "LMS7002M", SPI_LMS7002M }, { "FPGA", SPI_FPGA } }; - const std::unordered_map eepromMap = { { eMemoryRegion::VCTCXO_DAC, { 16, 2 } } }; - // desc.memoryDevices[MEMORY_DEVICES_TEXT.at(eMemoryDevice::FPGA_RAM)] = // std::make_shared(this, eMemoryDevice::FPGA_RAM); + const std::unordered_map flashMap = { { eMemoryRegion::VCTCXO_DAC, { 16, 2 } } }; desc.memoryDevices[MEMORY_DEVICES_TEXT.at(eMemoryDevice::FPGA_FLASH)] = std::make_shared(this, eMemoryDevice::FPGA_FLASH); - desc.memoryDevices[MEMORY_DEVICES_TEXT.at(eMemoryDevice::EEPROM)] = - std::make_shared(this, eMemoryDevice::EEPROM, eepromMap); desc.customParameters.push_back(cp_vctcxo_dac); mFPGA = new lime::FPGA_XTRX(spiFPGA, spiRFsoc); + FPGA::GatewareInfo gw = mFPGA->GetGatewareInfo(); + FPGA::GatewareToDescriptor(gw, desc); + + SDRDevice::Region serialNumberAddr = { 0x01FE0000, sizeof(uint64_t) }; + if (MemoryRead(desc.memoryDevices[MEMORY_DEVICES_TEXT.at(eMemoryDevice::FPGA_FLASH)], serialNumberAddr, &desc.serialNumber) != + OpStatus::SUCCESS) + desc.serialNumber = 0; RFSOCDescriptor soc; // LMS#1 soc.name = "LMS7002M"; soc.channelCount = 2; - soc.pathNames[TRXDir::Rx] = { "None", "LNAH", "LNAL", "LNAW" }; + soc.pathNames[TRXDir::Rx] = { "None", "LNAH", "LNAL", "LNAW", "LB1", "LB2" }; soc.pathNames[TRXDir::Tx] = { "None", "Band1", "Band2" }; soc.samplingRateRange = { 100e3, 61.44e6, 0 }; @@ -151,6 +159,8 @@ LimeSDR_XTRX::LimeSDR_XTRX( soc.antennaRange[TRXDir::Rx]["LNAH"] = { 2e9, 2.6e9 }; soc.antennaRange[TRXDir::Rx]["LNAL"] = { 700e6, 900e6 }; soc.antennaRange[TRXDir::Rx]["LNAW"] = { 700e6, 2.6e9 }; + soc.antennaRange[TRXDir::Rx]["LB1"] = soc.antennaRange[TRXDir::Rx]["LNAL"]; + soc.antennaRange[TRXDir::Rx]["LB2"] = soc.antennaRange[TRXDir::Rx]["LNAW"]; soc.antennaRange[TRXDir::Tx]["Band1"] = { 30e6, 1.9e9 }; soc.antennaRange[TRXDir::Tx]["Band2"] = { 2e9, 2.6e9 }; @@ -249,47 +259,13 @@ OpStatus LimeSDR_XTRX::Configure(const SDRConfig& cfg, uint8_t socIndex) InitLMS1(chip, skipTune); } - if (cfg.referenceClockFreq != 0) - chip->SetClockFreq(LMS7002M::ClockID::CLK_REFERENCE, cfg.referenceClockFreq, 0); - - const bool tddMode = cfg.channel[0].rx.centerFrequency == cfg.channel[0].tx.centerFrequency; - if (rxUsed && cfg.channel[0].rx.centerFrequency > 0) - chip->SetFrequencySX(TRXDir::Rx, cfg.channel[0].rx.centerFrequency); - if (txUsed && cfg.channel[0].tx.centerFrequency > 0) - chip->SetFrequencySX(TRXDir::Tx, cfg.channel[0].tx.centerFrequency); - if (tddMode) - chip->EnableSXTDD(true); - + LMS7002LOConfigure(chip, cfg); for (int i = 0; i < 2; ++i) { - const ChannelConfig& ch = cfg.channel[i]; - chip->SetActiveChannel((i & 1) ? LMS7002M::Channel::ChB : LMS7002M::Channel::ChA); - - chip->EnableChannel(TRXDir::Rx, i, ch.rx.enabled); - chip->EnableChannel(TRXDir::Tx, i, ch.tx.enabled); - - chip->Modify_SPI_Reg_bits(LMS7_INSEL_RXTSP, ch.rx.testSignal.enabled ? 1 : 0); - if (ch.rx.testSignal.enabled) - { - chip->Modify_SPI_Reg_bits(LMS7_TSGFC_RXTSP, static_cast(ch.rx.testSignal.scale)); - chip->Modify_SPI_Reg_bits(LMS7_TSGMODE_RXTSP, ch.rx.testSignal.dcMode ? 1 : 0); - chip->SPI_write(0x040C, 0x01FF); // DC.. bypasss - // chip->LoadDC_REG_IQ(false, 0x1230, 0x4560); // gets reset by starting stream - } - chip->Modify_SPI_Reg_bits(LMS7_INSEL_TXTSP, ch.tx.testSignal.enabled ? 1 : 0); - - for (const auto& gain : ch.rx.gain) - { - SetGain(0, TRXDir::Rx, i, gain.first, gain.second); - } - - for (const auto& gain : ch.tx.gain) - { - SetGain(0, TRXDir::Tx, i, gain.first, gain.second); - } - - // TODO: set filters... + LMS7002ChannelConfigure(chip, cfg.channel[i], i); + LMS7002TestSignalConfigure(chip, cfg.channel[i], i); } + // enabled ADC/DAC is required for FPGA to work chip->Modify_SPI_Reg_bits(LMS7_PD_RX_AFE1, 0); chip->Modify_SPI_Reg_bits(LMS7_PD_TX_AFE1, 0); @@ -302,52 +278,10 @@ OpStatus LimeSDR_XTRX::Configure(const SDRConfig& cfg, uint8_t socIndex) for (int i = 0; i < 2; ++i) { - chip->SetActiveChannel(i == 0 ? LMS7002M::Channel::ChA : LMS7002M::Channel::ChB); - const ChannelConfig& ch = cfg.channel[i]; - - if (socIndex == 0) - { - if (ch.rx.enabled && chip->SetGFIRFilter(TRXDir::Rx, i, ch.rx.gfir.enabled, ch.rx.gfir.bandwidth) != OpStatus::SUCCESS) - return 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) - return ReportError(OpStatus::ERROR, "Tx ch%i GFIR config failed", i); - } - - if (ch.rx.calibrate && ch.rx.enabled) - { - SetupCalibrations(chip, ch.rx.sampleRate); - int status = CalibrateRx(false, false); - if (status != MCU_BD::MCU_NO_ERROR) - return ReportError( - OpStatus::ERROR, "Rx ch%i DC/IQ calibration failed: %s.", i, MCU_BD::MCUStatusMessage(status)); - } - if (ch.tx.calibrate && ch.tx.enabled) - { - SetupCalibrations(chip, ch.tx.sampleRate); - int status = CalibrateTx(false); - if (status != MCU_BD::MCU_NO_ERROR) - return ReportError( - OpStatus::ERROR, "Tx ch%i DC/IQ calibration failed: %s.", i, MCU_BD::MCUStatusMessage(status)); - } - if (ch.rx.lpf > 0 && ch.rx.enabled) - { - SetupCalibrations(chip, ch.rx.sampleRate); - int status = TuneRxFilter(ch.rx.lpf); - if (status != MCU_BD::MCU_NO_ERROR) - return ReportError( - OpStatus::ERROR, "Rx ch%i filter calibration failed: %s.", i, MCU_BD::MCUStatusMessage(status)); - } - if (ch.tx.lpf > 0 && ch.tx.enabled) - { - SetupCalibrations(chip, ch.tx.sampleRate); - int status = TuneTxFilter(ch.tx.lpf); - if (status != MCU_BD::MCU_NO_ERROR) - return ReportError( - OpStatus::ERROR, "Tx ch%i filter calibration failed: %s.", i, MCU_BD::MCUStatusMessage(status)); - } - + const SDRDevice::ChannelConfig& ch = cfg.channel[i]; LMS1SetPath(false, i, ch.rx.path); LMS1SetPath(true, i, ch.tx.path); + LMS7002ChannelCalibration(chip, ch, i); } chip->SetActiveChannel(LMS7002M::Channel::ChA); @@ -511,10 +445,14 @@ OpStatus LimeSDR_XTRX::LMS1_SetSampleRate(double f_Hz, uint8_t rxDecimation, uin { const int decTbl[] = { 0, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; hbd_ovr = decTbl[oversample]; + rxDecimation = pow(2, hbd_ovr + 1); } cgenFreq *= 2 << hbd_ovr; if (txInterpolation >= rxDecimation) + { hbi_ovr = hbd_ovr + std::log2(txInterpolation / rxDecimation); + txInterpolation = pow(2, hbi_ovr + 1); + } else throw std::logic_error( strFormat("Rx decimation(2^%i) > Tx interpolation(2^%i) currently not supported", hbd_ovr, hbi_ovr)); @@ -582,33 +520,23 @@ void LimeSDR_XTRX::LMS1SetPath(bool tx, uint8_t chan, uint8_t pathId) } else { - uint8_t path; - switch (ePathLMS1_Rx(pathId)) - { - case ePathLMS1_Rx::NONE: - path = static_cast(LMS7002M::PathRFE::NONE); - break; - case ePathLMS1_Rx::LNAH: - path = static_cast(LMS7002M::PathRFE::LNAH); - break; - case ePathLMS1_Rx::LNAL: - path = static_cast(LMS7002M::PathRFE::LNAL); - break; - case ePathLMS1_Rx::LNAW: - path = static_cast(LMS7002M::PathRFE::LNAW); - break; - default: - throw std::logic_error("Invalid LMS1 Rx path"); - } + lime::LMS7002M::PathRFE path{ pathId }; + // first configure chip path or loopback + lms->SetPathRFE(lime::LMS7002M::PathRFE(path)); + + // configure rf switches ignoring loopback values + if (path == LMS7002M::PathRFE::LB1) + path = LMS7002M::PathRFE::LNAL; + else if (path == LMS7002M::PathRFE::LB2) + path = LMS7002M::PathRFE::LNAW; sw_val &= ~(0x3 << 2); - if (path == LMS_PATH_LNAW) + if (path == LMS7002M::PathRFE::LNAW) sw_val &= ~(0x3 << 2); - else if (path == LMS_PATH_LNAH) + else if (path == LMS7002M::PathRFE::LNAH) sw_val |= 2 << 2; - else if (path == LMS_PATH_LNAL) + else if (path == LMS7002M::PathRFE::LNAL) sw_val |= 1 << 2; - lms->SetPathRFE(lime::LMS7002M::PathRFE(path)); } // RF switch controls are toggled for both channels, use channel 0 as the deciding source. if (chan == 0) @@ -648,21 +576,15 @@ OpStatus LimeSDR_XTRX::UploadMemory( OpStatus LimeSDR_XTRX::MemoryWrite(std::shared_ptr storage, Region region, const void* data) { - if (storage == nullptr || storage->ownerDevice != this || storage->memoryDeviceType != eMemoryDevice::EEPROM) - { - return OpStatus::ERROR; - } - + if (storage == nullptr || storage->ownerDevice != this) + return OpStatus::INVALID_VALUE; return fpgaPort->MemoryWrite(region.address, data, region.size); } OpStatus LimeSDR_XTRX::MemoryRead(std::shared_ptr storage, Region region, void* data) { - if (storage == nullptr || storage->ownerDevice != this || storage->memoryDeviceType != eMemoryDevice::EEPROM) - { - return OpStatus::ERROR; - } - + if (storage == nullptr || storage->ownerDevice != this) + return OpStatus::INVALID_VALUE; return fpgaPort->MemoryRead(region.address, data, region.size); } diff --git a/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.h b/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.h index 93a75b977..57c714881 100644 --- a/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.h +++ b/src/boards/LimeSDR_XTRX/LimeSDR_XTRX.h @@ -12,6 +12,7 @@ namespace lime { class LitePCIe; +class ISerialPort; static const float XTRX_DEFAULT_REFERENCE_CLOCK = 26e6; @@ -23,6 +24,7 @@ class LimeSDR_XTRX : public LMS7002M_SDRDevice LimeSDR_XTRX(std::shared_ptr spiLMS7002M, std::shared_ptr spiFPGA, std::shared_ptr sampleStream, + std::shared_ptr control, double refClk = XTRX_DEFAULT_REFERENCE_CLOCK); virtual ~LimeSDR_XTRX(); @@ -54,13 +56,13 @@ class LimeSDR_XTRX : public LMS7002M_SDRDevice OpStatus LMS1_SetSampleRate(double f_Hz, uint8_t rxDecimation, uint8_t txInterpolation); static OpStatus LMS1_UpdateFPGAInterface(void* userData); - enum class ePathLMS1_Rx : uint8_t { NONE, LNAH, LNAL, LNAW }; enum class ePathLMS1_Tx : uint8_t { NONE, BAND1, BAND2 }; private: std::shared_ptr lms7002mPort; std::shared_ptr fpgaPort; std::shared_ptr mStreamPort; + std::shared_ptr mSerialPort; std::mutex mCommsMutex; bool mConfigInProgress; diff --git a/src/boards/LimeSDR_XTRX/LimeSDR_XTRXEntry.cpp b/src/boards/LimeSDR_XTRX/LimeSDR_XTRXEntry.cpp index 404658d4b..81bb4e9f8 100644 --- a/src/boards/LimeSDR_XTRX/LimeSDR_XTRXEntry.cpp +++ b/src/boards/LimeSDR_XTRX/LimeSDR_XTRXEntry.cpp @@ -101,7 +101,8 @@ SDRDevice* LimeSDR_XTRXEntry::make(const DeviceHandle& handle) std::string streamFile(handle.addr + "_trx0"); stream->SetPathName(streamFile); - return new LimeSDR_XTRX(route_lms7002m, route_fpga, stream); + auto controlPipe = std::make_shared(control); + return new LimeSDR_XTRX(route_lms7002m, route_fpga, stream, controlPipe); } catch (std::runtime_error& e) { const std::string reason = "Unable to connect to device using handle (" + handle.Serialize() + ")"; diff --git a/src/boards/MMX8/MM_X8.cpp b/src/boards/MMX8/MM_X8.cpp index a45ebe8e0..2f2955502 100644 --- a/src/boards/MMX8/MM_X8.cpp +++ b/src/boards/MMX8/MM_X8.cpp @@ -26,6 +26,7 @@ static double X8ReferenceClock = 30.72e6; LimeSDR_MMX8::LimeSDR_MMX8(std::vector>& spiLMS7002M, std::vector>& spiFPGA, std::vector> trxStreams, + std::shared_ptr control, ISPI* adfComms) : mTRXStreamPorts(trxStreams) { @@ -62,7 +63,7 @@ LimeSDR_MMX8::LimeSDR_MMX8(std::vector>& spiLMS7002M, desc.customParameters.push_back(cp_vctcxo_dac); for (size_t i = 0; i < mSubDevices.size(); ++i) { - mSubDevices[i] = new LimeSDR_XTRX(spiLMS7002M[i], spiFPGA[i], trxStreams[i], X8ReferenceClock); + mSubDevices[i] = new LimeSDR_XTRX(spiLMS7002M[i], spiFPGA[i], trxStreams[i], control, X8ReferenceClock); const SDRDevice::Descriptor& subdeviceDescriptor = mSubDevices[i]->GetDescriptor(); for (const auto& soc : subdeviceDescriptor.rfSOC) diff --git a/src/boards/MMX8/MM_X8.h b/src/boards/MMX8/MM_X8.h index 8696783cc..4fda5a5dc 100644 --- a/src/boards/MMX8/MM_X8.h +++ b/src/boards/MMX8/MM_X8.h @@ -26,6 +26,7 @@ class LimeSDR_MMX8 : public SDRDevice LimeSDR_MMX8(std::vector>& spiLMS7002M, std::vector>& spiFPGA, std::vector> trxStreams, + std::shared_ptr control, ISPI* adfComms); virtual ~LimeSDR_MMX8(); diff --git a/src/boards/MMX8/MM_X8Entry.cpp b/src/boards/MMX8/MM_X8Entry.cpp index 6f4d514a4..75683e529 100644 --- a/src/boards/MMX8/MM_X8Entry.cpp +++ b/src/boards/MMX8/MM_X8Entry.cpp @@ -203,7 +203,8 @@ SDRDevice* LimeSDR_MMX8Entry::make(const DeviceHandle& handle) trxStreams[i] = std::make_shared(); trxStreams[i]->SetPathName(portName); } - return new LimeSDR_MMX8(controls, fpga, std::move(trxStreams), adfComms); + auto controlPipe = std::make_shared(control); + return new LimeSDR_MMX8(controls, fpga, std::move(trxStreams), controlPipe, adfComms); } catch (std::runtime_error& e) { const std::string reason = "Unable to connect to device using handle (" + handle.Serialize() + "): " + e.what(); diff --git a/src/comms/USB/CMakeLists.txt b/src/comms/USB/CMakeLists.txt index cd12750a4..df49806cc 100644 --- a/src/comms/USB/CMakeLists.txt +++ b/src/comms/USB/CMakeLists.txt @@ -9,6 +9,7 @@ include(${THIS_SOURCE_DIR}/FX3/CMakeLists.txt) set(COMMS_USB_SOURCES ${THIS_SOURCE_DIR}/LMS64C_FPGA_Over_USB.cpp ${THIS_SOURCE_DIR}/LMS64C_LMS7002M_Over_USB.cpp + ${THIS_SOURCE_DIR}/LMS64C_ADF4002_Over_USB.cpp ${THIS_SOURCE_DIR}/TRXLooper_USB.cpp ${THIS_SOURCE_DIR}/USBEntry.cpp ${THIS_SOURCE_DIR}/USBGeneric.cpp diff --git a/src/comms/USB/LMS64C_ADF4002_Over_USB.cpp b/src/comms/USB/LMS64C_ADF4002_Over_USB.cpp new file mode 100644 index 000000000..954e35ad3 --- /dev/null +++ b/src/comms/USB/LMS64C_ADF4002_Over_USB.cpp @@ -0,0 +1,19 @@ +#include "LMS64C_ADF4002_Over_USB.h" +#include "LMS64CProtocol.h" + +using namespace lime; + +LMS64C_ADF4002_Over_USB::LMS64C_ADF4002_Over_USB(std::shared_ptr dataPort) + : pipe(dataPort) +{ +} + +OpStatus LMS64C_ADF4002_Over_USB::SPI(const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return LMS64CProtocol::ADF4002_SPI(*pipe, MOSI, count); +} + +OpStatus LMS64C_ADF4002_Over_USB::SPI(uint32_t spiBusAddress, const uint32_t* MOSI, uint32_t* MISO, uint32_t count) +{ + return LMS64CProtocol::ADF4002_SPI(*pipe, MOSI, count); +} diff --git a/src/comms/USB/LMS64C_ADF4002_Over_USB.h b/src/comms/USB/LMS64C_ADF4002_Over_USB.h new file mode 100644 index 000000000..b76fc4d57 --- /dev/null +++ b/src/comms/USB/LMS64C_ADF4002_Over_USB.h @@ -0,0 +1,25 @@ +#ifndef LIME_LMS64C_LMS7002M_OVER_USB_H +#define LIME_LMS64C_LMS7002M_OVER_USB_H + +#include "limesuite/IComms.h" +#include "USB_CSR_Pipe.h" +#include + +namespace lime { + +/** @brief A class for communicating with a device's ADF4002 chip over a USB interface. */ +class LMS64C_ADF4002_Over_USB : public IComms +{ + public: + LMS64C_ADF4002_Over_USB(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: + std::shared_ptr pipe; +}; + +} // namespace lime + +#endif // LIME_LMS64C_LMS7002M_OVER_USB_H diff --git a/src/comms/USB/LMS64C_FPGA_Over_USB.cpp b/src/comms/USB/LMS64C_FPGA_Over_USB.cpp index 95c421a66..e3ba95f6a 100644 --- a/src/comms/USB/LMS64C_FPGA_Over_USB.cpp +++ b/src/comms/USB/LMS64C_FPGA_Over_USB.cpp @@ -53,3 +53,13 @@ OpStatus LMS64C_FPGA_Over_USB::ProgramWrite(const char* data, size_t length, int return LMS64CProtocol::ProgramWrite( *pipe, data, length, prog_mode, static_cast(target), callback); } + +OpStatus LMS64C_FPGA_Over_USB::MemoryWrite(uint32_t address, const void* data, uint32_t dataLength) +{ + return LMS64CProtocol::MemoryWrite(*pipe, address, data, dataLength); +} + +OpStatus LMS64C_FPGA_Over_USB::MemoryRead(uint32_t address, void* data, uint32_t dataLength) +{ + return LMS64CProtocol::MemoryRead(*pipe, address, data, dataLength); +} diff --git a/src/comms/USB/LMS64C_FPGA_Over_USB.h b/src/comms/USB/LMS64C_FPGA_Over_USB.h index 822e040f1..931858cdc 100644 --- a/src/comms/USB/LMS64C_FPGA_Over_USB.h +++ b/src/comms/USB/LMS64C_FPGA_Over_USB.h @@ -27,6 +27,9 @@ class LMS64C_FPGA_Over_USB : public IComms 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 dataLengt) override; + private: std::shared_ptr pipe; }; diff --git a/src/fftviewer_wxgui/fftviewer_frFFTviewer.cpp b/src/fftviewer_wxgui/fftviewer_frFFTviewer.cpp index bd90afb36..e80b41473 100644 --- a/src/fftviewer_wxgui/fftviewer_frFFTviewer.cpp +++ b/src/fftviewer_wxgui/fftviewer_frFFTviewer.cpp @@ -503,7 +503,11 @@ void fftviewer_frFFTviewer::StreamingLoop( uint32_t samplesToCopy = min(samplesPopped, samplesToCapture); if (samplesToCopy <= 0) break; - memcpy((captureBuffer[ch].data() + samplesCaptured), buffers[ch], samplesToCopy * sizeof(complex32f_t)); + for (uint i = 0; i < samplesToCopy; ++i) + { + captureBuffer[ch][samplesCaptured + i].i = buffers[ch][i].i * 32767; + captureBuffer[ch][samplesCaptured + i].q = buffers[ch][i].q * 32767; + } samplesToCapture -= samplesToCopy; samplesCaptured += samplesToCopy; } diff --git a/src/include/limesuite/SDRDevice.cpp b/src/include/limesuite/SDRDevice.cpp index ceb4e3aa3..f6defeec4 100644 --- a/src/include/limesuite/SDRDevice.cpp +++ b/src/include/limesuite/SDRDevice.cpp @@ -75,4 +75,20 @@ OpStatus SDRDevice::CustomParameterWrite(const std::vector& p OpStatus SDRDevice::CustomParameterRead(std::vector& parameters) { return ReportError(OpStatus::NOT_IMPLEMENTED, "CustomParameterRead not implemented"); -} \ No newline at end of file +} + +OpStatus SDRDevice::UploadMemory( + eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback) +{ + return OpStatus::NOT_IMPLEMENTED; +} + +OpStatus SDRDevice::MemoryWrite(std::shared_ptr storage, Region region, const void* data) +{ + return OpStatus::NOT_IMPLEMENTED; +} + +OpStatus SDRDevice::MemoryRead(std::shared_ptr storage, Region region, void* data) +{ + return OpStatus::NOT_IMPLEMENTED; +} diff --git a/src/include/limesuite/SDRDevice.h b/src/include/limesuite/SDRDevice.h index 3d7c40712..3b6abdee0 100644 --- a/src/include/limesuite/SDRDevice.h +++ b/src/include/limesuite/SDRDevice.h @@ -21,6 +21,7 @@ namespace lime { struct DeviceNode; +class OEMTestReporter; /// SDRDevice can have multiple modules (RF chips), that can operate independently class LIME_API SDRDevice @@ -214,26 +215,21 @@ class LIME_API SDRDevice }; struct TestSignal { - enum class Divide : uint8_t { - Div8 = 1U, - Div4 = 2U, - }; + enum class Divide : uint8_t { Div8, Div4 }; + enum class Scale : uint8_t { Full, Half }; - enum class Scale : uint8_t { - Half = 0U, - Full = 1U, - }; - - bool enabled; - bool dcMode; + complex16_t dcValue; Divide divide; Scale scale; + bool enabled; + bool dcMode; TestSignal(bool enabled = false, bool dcMode = false, Divide divide = Divide::Div8, Scale scale = Scale::Half) - : enabled(enabled) - , dcMode(dcMode) + : dcValue(0, 0) , divide(divide) , scale(scale) + , enabled(enabled) + , dcMode(dcMode) { } }; @@ -432,20 +428,12 @@ class LIME_API SDRDevice virtual void* GetInternalChip(uint32_t index) = 0; typedef bool (*UploadMemoryCallback)(size_t bsent, size_t btotal, const char* statusMessage); + virtual OpStatus UploadMemory( - eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback) - { - return OpStatus::NOT_IMPLEMENTED; - }; + eMemoryDevice device, uint8_t moduleIndex, const char* data, size_t length, UploadMemoryCallback callback); - virtual OpStatus MemoryWrite(std::shared_ptr storage, Region region, const void* data) - { - return OpStatus::NOT_IMPLEMENTED; - }; - virtual OpStatus MemoryRead(std::shared_ptr storage, Region region, void* data) - { - return OpStatus::NOT_IMPLEMENTED; - }; + virtual OpStatus MemoryWrite(std::shared_ptr storage, Region region, const void* data); + virtual OpStatus MemoryRead(std::shared_ptr storage, Region region, void* data); }; } // namespace lime diff --git a/src/include/limesuite/complex.h b/src/include/limesuite/complex.h index 7902936eb..902811c42 100644 --- a/src/include/limesuite/complex.h +++ b/src/include/limesuite/complex.h @@ -5,16 +5,103 @@ namespace lime { +/** @brief Structure to hold a 12 bit integer complex number. */ +struct complex12compressed_t { + complex12compressed_t() + : complex12compressed_t(0, 0) + { + } + complex12compressed_t(int16_t i, int16_t q) { Set(i, q); } + + int16_t real() const + { + int16_t value = data[0]; + value |= (data[1] << 8); + // shifting to fill sign + value <<= 4; + value >>= 4; + return value; + } + void real(int16_t value) + { + data[0] = value; + data[1] = (data[1] & 0xF0) | ((value >> 8) & 0x0F); + } + + int16_t imag() const + { + int16_t value = data[1]; + value |= (data[2] << 8); + value <<= 4; + value >>= 4; + return value; + } + void imag(int16_t value) + { + data[1] = (value << 4) | (data[1] & 0x0F); + data[2] = value >> 4; + } + + void Set(int16_t i, int16_t q) + { + data[0] = i; + data[1] = (q << 4) | ((i >> 8) & 0x0F); + data[2] = q >> 4; + } + + private: + uint8_t data[3]; +}; + +// complex number structure for plain data types (int, float...) +template struct POD_complex_t { + POD_complex_t() + : POD_complex_t(0, 0) + { + } + POD_complex_t(T real, T imag) + : i(real) + , q(imag) + { + } + + T real() const { return i; } + void real(T value) { i = value; } + T imag() const { return q; } + void imag(T value) { q = value; } + + void Set(T ival, T qval) + { + i = ival; + q = qval; + } + + T i; + T q; +}; + /** @brief Structure to hold a 16 bit integer complex number. */ -struct complex16_t { - int16_t i; ///< The I component of the number. - int16_t q; ///< The Q component of the number. +struct complex16_t : public POD_complex_t { + complex16_t() + : complex16_t(0, 0) + { + } + complex16_t(int16_t re, int16_t im) + : POD_complex_t(re, im) + { + } }; /** @brief Structure to hold a 32 bit float complex number. */ -struct complex32f_t { - float i; ///< The I component of the number. - float q; ///< The Q component of the number. +struct complex32f_t : public POD_complex_t { + complex32f_t() + : complex32f_t(0, 0) + { + } + complex32f_t(float re, float im) + : POD_complex_t(re, im) + { + } }; /** @brief Structure to hold a 64 bit float complex number. */ diff --git a/src/lms7002m/LMS7002M.cpp b/src/lms7002m/LMS7002M.cpp index 12af82f4c..cbe1a5c81 100644 --- a/src/lms7002m/LMS7002M.cpp +++ b/src/lms7002m/LMS7002M.cpp @@ -851,6 +851,12 @@ OpStatus LMS7002M::SaveConfig(const std::string& filename) sprintf(addr, "0x%04X", addrToRead[i]); sprintf(value, "0x%04X", dataReceived[i]); fout << addr << "=" << value << std::endl; + // add parameter name/value as comments + for (const LMS7Parameter& parameter : LMS7parameterList) + { + if (parameter.address == addrToRead[i]) + fout << "//" << parameter.name << " : " << Get_SPI_Reg_bits(parameter) << std::endl; + } } fout << "[lms7002_registers_b]" << std::endl; diff --git a/src/lms7002m/LMS7002M_validation.cpp b/src/lms7002m/LMS7002M_validation.cpp index 6c1ea165c..6093fc699 100644 --- a/src/lms7002m/LMS7002M_validation.cpp +++ b/src/lms7002m/LMS7002M_validation.cpp @@ -66,7 +66,8 @@ bool LMS7002M_Validate(const SDRDevice::SDRConfig& cfg, std::vector if (ch.tx.enabled && !InRange(ch.tx.centerFrequency, minLO, maxLO)) errors.push_back(strFormat("Tx ch%i LO (%g) out of range [%g:%g]", i, ch.tx.centerFrequency, minLO, maxLO)); - if (ch.rx.enabled && !InRange(ch.rx.path, 0, 3)) + // TODO: make warning if rx loopback selection does not work with tx output + if (ch.rx.enabled && not InRange(ch.rx.path, 0, 5)) errors.push_back(strFormat("Rx ch%i invalid path(%i)", i, ch.rx.path)); if (ch.tx.enabled && !InRange(ch.tx.path, 0, 2)) diff --git a/src/protocols/LMS64CProtocol.cpp b/src/protocols/LMS64CProtocol.cpp index 57b28cb1b..d494c3a1c 100644 --- a/src/protocols/LMS64CProtocol.cpp +++ b/src/protocols/LMS64CProtocol.cpp @@ -695,7 +695,7 @@ OpStatus MemoryWrite(ISerialPort& port, uint32_t address, const void* data, size progView.SetChunkSize(std::min(dataLen - bytesSent, chunkSize)); progView.SetAddress(address + bytesSent); - progView.SetDevice(3); + progView.SetDevice(ProgramWriteTarget::FPGA); progView.SetData(src, chunkSize); src += chunkSize; @@ -728,7 +728,7 @@ OpStatus MemoryRead(ISerialPort& port, uint32_t address, void* data, size_t data LMS64CPacketMemoryWriteView writeView(&packet); writeView.SetMode(0); - writeView.SetDevice(3); + writeView.SetDevice(ProgramWriteTarget::FPGA); const size_t chunkSize = 32; static_assert(chunkSize <= writeView.GetMaxDataSize(), "chunk must fit into packet payload");