[LimeSDR-USB] Synchronization and Toggle TDD/FDD Mode

Hello everyone, My idea is to work with phase coherent applications. So I have a few questions.

How can I configure/Toggle TDD / FDD modes in LimeSDR? I didn’t find an option related to that in LimeSuiteGUI. I searched in previous threads [1],[2] and it seems it’s not easy to do such a task.

From this thread, @Zack says that one of the many operations the FPGA does is the synchronization between the Tx and Rx data, how this relates to TDD Mode?

Has anyone tested the behavior of the LimeSDR in TDD and made comparison to its behavior with an external clock reference?

Thanks in Advance!

1 Like

Actually I would also like to know if it’s possible to turn on TDD mode in a custom C++ application through SoapySDR API. Probably it will be matter of setting some bits via SPI?

Can anyone point out practical differenced between TDD and FDD in LimeSDR. How it changes behaviour of the LimeSDR. Is there a reason FDD mode cannot support TDD and vice-versa?

Quote from LMS7002M datasheet:

I am interested especially in checking fast TX/RX switching time.

TDD mode for LMS7002M is activated via configuration. The idea is to use only one PLL (SXT) for both Rx and Tx chains.

Here are steps on how to do it using LimeSuiteGUI:

  1. Starting setup is as a normal FDD mode (both RX and TX synthesizers are working).
  2. Set register 0x011C[6] = 0 while 0x0020[1:0] = 2 (or SXT tab is selected in LimeSuiteGUI - uncheck control check box called “LO buffer from SXT to SXR” under “Power down controls”).
  3. Set 0x011C[1] = 1 while 0x0020[1:0] = 1 (or SXR tab is selected in LimeSuiteGUI – check control check box called “VCO” under “Power down controls”).
  4. Both receive and transmit LO frequencies are changed via TX synthesizer control (SXT) in this configuration.

Here are the steps on how to achieve the same configuration described above using Lime Suite API:
LMS_WriteParam(device, LMS7_MAC, 2);
LMS_WriteParam(device, LMS7_PD_LOCH_T2RBUF, 0);
LMS_WriteParam(device, LMS7_MAC, 1);
LMS_WriteParam(device, LMS7_PD_VCO, 1);

Here is a custom FPGA gateware:
which activates FPGA_GPIO0 line when there are data to send. Pinout of J12 (FPGA_GPIO) is described in the Table 9 here:


Thank you @Zack for such quick response. It looks like quite easy thing to do, however to be honest I don’t know how to achieve the same effect with SoapySDR API. Even if I try to read the content of registers addressed by 0x0020 and 0x011C (which according to LMS7002M_parameters.h file hold the values of bits mentioned above) with readRegister() call, I always get zero.

@andrewback, any comments?

Afraid not. This sounds more like one for @IgnasJ or @joshblum.

Thank everyone for your responses! I will try this mode with the test signal in TxTSP and compare the “noise in the phase” between N-captured samples with one mode and another. When I have this results I’ll share them with you.


The transactSPI call can actually hit the spi bus for the LMS7002m. Read/write registers was actually tied to the FPGA register space in this case. But I thought that this wasnt very useful. So if you pull latest master, you can hit the LMS7002m register space through read/write register, just use the “RFIC0” interface name first. Example:

python -c "import SoapySDR; d = SoapySDR.Device(); print(hex(d.readRegister('RFIC0', 0x20)))"

The explicit way would be to hit these register via a writeSettings() call. Further, I wanted to make it automatic when both rx and tx LOs are turned to the same frequency. If you want the same frequency setting, then it should be OK to just share a single LO in this case. I hope that seems intuitive. I’m also doing this for skylark (iris - another lms7 based design).

Let me know how your investigation with the SPI settings goes.



I’m having issues that I believe might be caused by being in FDD mode rather than TDD mode (I’m using the same frequency for RX and TX); I go into detail about these in this thread..

In order to try and get TDD working, I inserted the following code at the end of the SoapyLMS7 constructor here:

//Beginning of added section SID
SoapySDR::logf(SOAPY_SDR_INFO, "SID almost end of Constructor"); //SID
SoapySDR::logf(SOAPY_SDR_INFO, "Enabling TDD");

SoapySDR::logf(SOAPY_SDR_INFO, "Register at 0x0020 before is %x", readRegister("RFIC0", 0x0020)); //MAC
SoapySDR::logf(SOAPY_SDR_INFO, "Register at 0x011C before is %x", readRegister("RFIC0", 0x011C)); //PD_LOCH_T2RBUF, PD_VCO
for (size_t channel = 0; channel < 1; channel++)
    auto rfic = getRFIC(channel);
    SoapySDR::logf(SOAPY_SDR_INFO, "Register at 0x0020 after (MAC,2) is %x", readRegister("RFIC0", 0x0020)); //MAC
    SoapySDR::logf(SOAPY_SDR_INFO, "Register at 0x011C after (PD_LOCH_T2RBUF,0) is %x", readRegister("RFIC0", 0x011C));
    SoapySDR::logf(SOAPY_SDR_INFO, "Register at 0x0020 after (MAC,1) is %x", readRegister("RFIC0", 0x0020)); //MAC
    SoapySDR::logf(SOAPY_SDR_INFO, "Register at 0x011C after (PD_VCO,1) is %x", readRegister("RFIC0", 0x011C));

SoapySDR::logf(SOAPY_SDR_INFO, "Register at 0x0020 after all changes is %x", readRegister("RFIC0", 0x0020)); //MAC
SoapySDR::logf(SOAPY_SDR_INFO, "Register at 0x011C after call changes is %x", readRegister("RFIC0", 0x011C)); //PD_LOCH_T2RBUF, PD_VCO
SoapySDR::logf(SOAPY_SDR_INFO, "SID end of Constructor"); //SID
//End of added section SID

I get the following output:

linux; GNU C++ version 5.4.0 20160609; Boost_105800; UHD_003.010.002.000-3-g122bfae1

gr-osmosdr v0.1.4-98-gc653754d (0.1.5git) gnuradio
built-in source types: file fcd rtl rtl_tcp uhd hackrf bladerf rfspace airspy soapy redpitaya 
[INFO] Make connection: 'LimeSDR-USB [USB 3.0] 9062000C41E13'
[INFO] Reference clock 30.720 MHz
[INFO] Device name: LimeSDR-USB
[INFO] Reference: 30.72 MHz
[INFO] Init LMS7002M(0)
[INFO] Ver=7, Rev=1, Mask=1
[INFO] LMS7002M calibration values caching Disable
[INFO] SID almost end of Constructor
[INFO] Enabling TDD
[INFO] Register at 0x0020 before is fffe
[INFO] Register at 0x011C before is ad43
[INFO] Register at 0x0020 after (MAC,2) is fffe
[INFO] Register at 0x011C after (PD_LOCH_T2RBUF,0) is ad03
[INFO] Register at 0x0020 after (MAC,1) is fffd
[INFO] Register at 0x011C after (PD_VCO,1) is ad43
[INFO] Register at 0x0020 after all changes is fffd
[INFO] Register at 0x011C after call changes is ad43
[INFO] SID end of Constructor
gr-osmosdr v0.1.4-98-gc653754d (0.1.5git) gnuradio
built-in sink types: uhd hackrf bladerf soapy redpitaya file 
'Q' to quit 

From the above, it looks to me like the call to set PD_LOCH_T2RBUF to 0 is working as intended (bit 6 is being set to 0, which seems correct to me according to LMS7002M_parameters.h). The register changes after the two MAC register changes also make sense. However, after setting PD_VCO to 1, the register 0x011C is now back to 0xad43, which doesn’t make sense to me, as it should be changing different bits. What am I doing wrong here? Making these changes didn’t seem to fix my issues, but that may be because TDD mode isn’t being enabled successfully due to the above (is there an easy way to check?)

Thanks and regards,


EDIT: Changed the code above so that it was actually what I used to generate the output given above (had accidentally copied in an older version of the code previously)

0x011C is affected by MAC so you read different value after changing MAC to 1. When MAC is ‘1’ you read/write SXR registers, and when MAC is ‘2’ you read/write SXT registers.

Thanks for your reply. I just realised that the code I posted in my post wasn’t actually the code used to generate the output in my post, which is not helpful, apologies. I don’t have access to the code right now (will update with the correct code tomorrow), but I essentially added more print statements in between each register write.

Ok, that makes more sense. I’ll try putting more reads after changing MAC to make sure that the right bits are getting set.

According to you should the code I have above be setting up TDD correctly? Is there a way I can check independently? Is it possible that something else is nullifying the above (I put the above in the constructor as I knew that would always have to be called; I guess it’s possible that another function called later could be undoing it?). i haven’t modified any of the other functions in SoapyLMS7.

Really appreciate the help!


These setting should enable usage of Tx PLL in both Rx and Tx. So after these changes you should be receiving at Tx frequency (regardless of what get frequency functions may report as they read Rx PLL settings) and trying to set Rx frequency should fail as SXR VCO is powered down.

When setting frequency VCO is being powered on.

Thanks for all your responses, really appreciate it.

Turns out my issues weren’t actually being caused by whether the board was in FDD/TDD mode (but in trying to figure out where to put code in order to make sure it was being activated, I managed to figure out how to fix some of my other issues).

For those looking here in the future, I’m still not 100% certain (I never independently verified) whether I successfully enabled TDD mode, but in case it’s useful, the way I did it was I added the following code here.

for (size_t channel = 0; channel < 1; channel++)
    auto rfic = getRFIC(channel);

The pointless ‘for loop’ is just to avoid rfic entering the scope of the rest of the function. I’m sure there are neater ways of doing this.

For my application at least, the last thing that was being called in the SoapyLMS7 driver was ‘activateStream()’, which does do an RX and TX calibration, so the key was to add things after this calibration had occurred.

You can limit the scope by using braces only, so ‘for’ line is not necessary.