Post Go back to editing

tddn off_raw parameter

Category: Software
Product Number: Pluto
Software Version: v0.39

Hello. In the pluto_tddn.py example in the pyadi-iio examples, the TDD engine is setup as following.

tddn.channel[0].on_raw = 0
tddn.channel[0].off_raw = 0
tddn.channel[0].polarity = 0
tddn.channel[0].enable = 1

# RX DMA SYNC
tddn.channel[1].on_raw = 0
tddn.channel[1].off_raw = 10
tddn.channel[1].polarity = 0
tddn.channel[1].enable = 1

# TX DMA SYNC
tddn.channel[2].on_raw = 0
tddn.channel[2].off_raw = 10
tddn.channel[2].polarity = 0
tddn.channel[2].enable = 1

The tddn's class documentation is unfortunately very minimal and barely explains how it works and how to modify this for different use cases. One has to look at the HDL documentation and diagram in Vivado to begin to have an idea of how to understand it and I don't have much experience with it. So some more in-depth explanation directly in the pyadi-iio would be appreciated.

From what I understand looking at the Vivado project, the uncommented 1st channel is connected to the GPIO 0 pin. So is it just for external sync or do the channel[0] properties matter if we are doing software sync?

Why the magic number 10 in the off_raw property of channel[1] and channel[2]? From what I understand this property "turns off" the DMA SYNC channels 10 clock cycles before the frame is finished. If I try to set off_raw to 0 in the RX DMA SYNC channel I get the following error

x = self._rx_buffered_data()
        ^^^^^^^^^^^^^^^^^^^^^^^^
  File "[...]/adi/compat.py", line 149, in _rx_buffered_data
    self._rxbuf.refill()
  File "[...]/iio.py", line 1003, in refill
    _buffer_refill(self._buffer)
  File "[...]/iio.py", line 62, in _check_negative
    raise OSError(-result, _strerror(-result))
TimeoutError: [Errno 110] Connection timed out

Which I can't recover from even after setting the value back to 10 unless I turn the Pluto off and on again.

If I set tddn.channel[2].off_raw = 0, then basically only 0's are sent to the transceiver for transmission.

Using off_raw = 2 seems to work. I would be grateful if someone could provide an explanation on how to pick this value and if it is possible to use off_raw = 0 so that samples aren't lost between frames.

Finally, what does polarity do?

  • I was confused and thought that off_raw was counting from the end of the frame. Of course setting it to 0 causes problems reading from the buffer since there's nothing to read. So if I want the channel to stay on for the whole frame I need to set off_raw to be equal to the frame length.

    I am still confused as to why the pluto_tddn.py example uses off_raw = 10 when the frame_length is 4 ms, and the DMA channels are being turned off after 0.25 microseconds (40 Mhz is the clock rate, correct)?

  • Hello,

    The on_raw and off_raw attributes control the time from the beggining of the initial pulse trigger until the start and end respectively of the first pulse on the channel. This way, based on the configuration of frame lentgh on_raw and off_raw, one can control the duty_cycle of the pulse on the tdd channel. The attributes on_raw and off_raw specify raw values, the conversion in ms is available via the off_ms and on_ms attributes, available in the tddn class: https://github.com/analogdevicesinc/pyadi-iio/blob/main/adi/tddn.py#L171. Therefore, off_raw = 10 would translate to about 0.000040 from the initial trigger to the end of your channel's pulse, and then it stays low until the end of the frame length. If for a specific use case you need your pulse to stay in the reerse state for a certain period of time, you would need to set off_raw accordingly or simply set "off_ms" for that period.

    Channel 0 is most likely the enable channel, that channel is probably not tied to anything else but it is necessary to be enabled in order to enable the rest of the tdd channels, that's why no particular configuration is necessary for it.

    Finally, polarity refers to the polarity of the tdd channel, if it's regular polarity (from 0 to 1)  or inverse polarity (from 1 to 0).

    I hope this clarifies the attributes and their purpose a bit. Please let me know if you have any additional questions. I attach below a diagram that shows the timing.

  • Yes, the documentation on Pluto TDD is pretty thin right now.  But we'll be adding a page to this soon.  In the meantime, you can check out this presentation for a little bit more explanation:  https://youtu.be/ZnCi-LHJ0GQ?si=pNMoINfIuOYfcGl0&t=991

  • Thank you for the explanation and the diagram. That makes things much clearer, but I still have some questions.

    • off_raw = 10 would translate to about 0.000040

      How is every clock cycle 4 microseconds? Isn't the Pluto oscillator 40 Mhz which is what is used by the Zynq?

    • polarity refers to the polarity of the tdd channel

      I don't understand what "polarity of the channel" means in general. Can you provide an explanation or link to some relevant educational resource?

    Finally, can you help me clarify why the DMA channels are high for only 10 clock cycles? I don't have experience with digital systems outside a single university course. I had a look at the HDL project in Vivado, and the RX DMA TDD channel connects to the "fifo_wr_sync" input of the ADC's DMA controller. The triangle on the input means it only reads a rising edge, correct? So the TDD DMA channels only need a short pulse to synchronize?

    This "fifo_wr_sync" is not present in the AXI DMAC core documentation, some more explanation on the synchronization mechanism would be nice.

    The TX's channel I don't understand how it works, or why it is HIGH for 10 clock cycles. It connects to an OR gate together with a "rst" signal from the ad9361 core, whose result goes to the "reset" input of a "upack" core. The reset seems to be logical and not edge based, so it sound like the length of time the TX channel is held at HIGH seems important. Can you provide some explanation on how the TX synchronization works?

  • Hi !
    Regarding the HDL, could you please provide more details about your design? Are you using the Pluto project from a release or did you build it from the latest HDL main branch? Did you modify the design, have you implemented custom logic?

    Regards,
    Cristian

  • Hello. I'm using Pluto firmware v0.39 with no modification. The image I showed was built from the Pluto project in the Analog Devices HDL github repo, which I assume is the same, and used it just for analysis.

  • Hi  ,

    Regarding the "off_raw = 10 would translate to about 0.000040" I was just giving an example based on a different setup that I've worked on that also uses tdd. My point is that you can write the value of "off_raw" and the "off_ms" attribute would update accordingly so you can read it and see how much it translates to, and vice-versa.

    Regarding channel polarity, it simply means if the signal is behaving to regular or inverse logic. To put it differently, a channel with "regular polarity" (0) would be 0 in the default state and go to logic 1 when it is triggered, and a channel with inverse polarity would be in a default state of 1 and go to 0 at the pulse trigger. Therefore, in your example, where you have polarity set to 1, it means your signal stays high for most of the frame length and goes down only at the very end (which I assume is the necessary time to push the data in to buffers) and then it goes up again to logic 1.

    Best Regards,

    Ramona

  • Hello, thanks again for your help.

    Regarding the part where you say:

    "Therefore, in your example, where you have polarity set to 1, it means your signal stays high for most of the frame length and goes down only at the very end"

    That was also my first impression, that the channels should be on for most of the frame.

    But the polarity in the example is set to 0, and if you check the pluto_tddn.py example, (which is the one I copy pasted) the DMA channels are only active for 10 clock cycles and the frame length is 1 millisecond. So unless the zynq on Pluto is running at 10 kHz, then the channels are actually OFF for most of the frame.

     

  • Sorry, I misread from the screenshot and thought polarity is being set to 1. In this case yes, the signal is only up a short time. However, this doesn't mean the DMA channels are high for that amount of time, because the pulse is going toward fifo_wr_sync.

    Here is a related thread that offers a little bit more explanation on fifo_wr_sync: ez.analog.com/.../axi_dmac-core-what-is-function-of-the-fifo_wr_sync-pin

  • Thank you. I've understood the Rx DMA channel goes into fifo_wr_sync which is edge triggered. But what about the Tx channel? It seems to be going into an OR gate with a reset signal from the ad9361 block, and then that resets a "upack" block?

    My main struggle right now is that I'd like to send only 1 synchronized frame, and naively setting

    capture_range = 1

    tddn.burst_count = 1

    In the pluto_tddn.py example cause the following error:

    Traceback (most recent call last):
      File "[...]/pluto_tddn.py", line 114, in <module>
        receive_array[r] = my_sdr.rx()
                           ^^^^^^^^^^^
      File "[...]/adi/rx_tx.py", line 275, in rx
        data = self.__rx_complex()
               ^^^^^^^^^^^^^^^^^^^
      File "[...]/adi/rx_tx.py", line 240, in __rx_complex
        x = self._rx_buffered_data()
            ^^^^^^^^^^^^^^^^^^^^^^^^
      File "[...]/adi/compat.py", line 149, in _rx_buffered_data
        self._rxbuf.refill()
      File "[...]/iio.py", line 1003, in refill
        _buffer_refill(self._buffer)
      File "[...]/iio.py", line 62, in _check_negative
        raise OSError(-result, _strerror(-result))
    TimeoutError: [Errno 110] Connection timed out

    UPDATE: Managed to send a single synchronized burst. A small startup delay is needed, such as tddn.startup_delay_ms = 1. I assume this time is required for memory allocation somewhere?