DMA Acquisition breaks on 2nd attempt, every time.

Applications, development tools, FPGA, C, WEB
Post Reply
chris@ner.com
Posts: 7
Joined: Thu Apr 18, 2024 4:34 pm

DMA Acquisition breaks on 2nd attempt, every time.

Post by chris@ner.com » Wed Apr 24, 2024 7:28 pm

125-14 (2-channel)
# Version: 2.00-aff683518
# Build: 35
# Commit: aff683518f5e62e9eb01c944949f9c3b7da1ec0f
# U-Boot: "redpitaya-v2022.1"
# Linux Kernel: "branch-redpitaya-v2024.1"
# Pro Applications: ""

Hi All,

Following the example: "acquire_trigger_posedge_axi_bigbuffer.py" to acquire RFIn1, and using an external-PE trigger.

Original code from:
GIT: \RedPitaya-master\RedPitaya-master\Examples\python\acquire_trigger_posedge_axi_bigbuffer.py

I intend to compare transfer times using different block sizes. This works for the first try, but then after trying another acquisition (with the same block size - as a test), the code breaks. - Pointing to a basic misunderstanding (on my part) of how this is supposed to work.

Error:
"ner_acquire_trigger_posedge_axi_bigbuffer.py"
Acquire # 0
ACQ:AXI:DATA:UNITS?: RAW
ACQ:AXI:DEC?: 1
Start address 16777216 size of available memory 2097152
Number of samples to capture per channel 1048576
All data captured
All data transferred. Fail_ct: 40. Samples recovered: 1050000.
Buffer last 100 samples [ -92. -89. -91. -91. -90. <snip>]
Releasing Red Pitaya resources.
Traceback (most recent call last):
File "ner_acquire_trigger_posedge_axi_bigbuffer.py", line 61, in <module>
print('ACQ:AXI:DATA:UNITS?: ',rp_s.txrx_txt('ACQ:AXI:DATA:UNITS?'))
File "redpitaya_scpi.py", line 99, in txrx_txt
return self.rx_txt()
File "redpitaya_scpi.py", line 46, in rx_txt
chunk = self._socket.recv(chunksize).decode('utf-8') # Receive chunk size of 2^n preferably
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 10: invalid start byte
Acquire # 1

Process finished with exit code 1
I am looping the bottom section of the code below (range(2)):

Code: Select all

#!/usr/bin/python3

import redpitaya_scpi as scpi
import matplotlib.pyplot as plot
import struct
import time
import numpy as np 

# ---- CUSTOM CODE START ----


# The Red Pitaya has an External Trigger Input on DIO0_P
# Setup and reserve that pin for EXT TRIG use.
# Use by default DIO1_P as an output line to fire the HV Pulser.
# Connect externally DIO0_P and DIO1_P together via a series 300-ohm resistor,
# to feed the threaded trigger signal back into the Red Pitaya.
def pulser_fire_dio_setup(my_rp_s, this_dio='DIO1_P'):
    # Set Red Pitaya EXT TRIG pin to input
    # There's no indication in the docs or examples that we need to do this.
    # But it sure seems like a good idea to me.
    my_rp_s.tx_txt('DIG:PIN:DIR IN, DIO0_P')

    # Set pulser trigger-in pin to output
    my_rp_s.tx_txt(f'DIG:PIN:DIR OUT, {this_dio}')
    # set pulser trigger pin LOW
    my_rp_s.tx_txt(f'DIG:PIN {this_dio},0')
    return


# The Red Pitaya has an External Trigger Input on DIO0_P
# Setup and reserve that pin for EXT TRIG use.
# Use, by default, DIO1_P as an output line to fire the HV Pulser.
def pulser_fire_once(my_rp_s, this_dio='DIO1_P'):
    # Set DIO High, delay tiny bit, set pulser Low.
    my_rp_s.tx_txt(f'DIG:PIN {this_dio},1')
    time.sleep(0.05)

    my_rp_s.tx_txt(f'DIG:PIN {this_dio},0')
    time.sleep(0.05)
    return

# ^^^^ CUSTOM CODE END ^^^^


rp_s = scpi.scpi('192.168.0.91')

# ---- CUSTOM CODE START ----

# Setup Red Pitaya Output #2 to control the pulser
pulser_fire_dio_setup(rp_s)

# ^^^^ CUSTOM CODE END ^^^^
for i in range(2):              # 1st loop seems to work.  Next one does not.
    print(f"Acquire # {i}")
    rp_s.tx_txt('ACQ:RST')
    rp_s.tx_txt('ACQ:DATA:FORMAT BIN')

    rp_s.tx_txt('ACQ:AXI:DATA:UNITS RAW')
    print('ACQ:AXI:DATA:UNITS?: ',rp_s.txrx_txt('ACQ:AXI:DATA:UNITS?'))
    rp_s.check_error()

    rp_s.tx_txt('ACQ:AXI:DEC 1')
    print('ACQ:AXI:DEC?: ',rp_s.txrx_txt('ACQ:AXI:DEC?'))
    rp_s.check_error()

    start = int(rp_s.txrx_txt('ACQ:AXI:START?'))
    size = int(rp_s.txrx_txt('ACQ:AXI:SIZE?'))
    samples = (size // 2) # 1 sample = 16 Bit
    rp_s.check_error()

    print("Start address ",start," size of available memory ",size)
    print("Number of samples to capture per channel " + str(samples))

    # Specify the buffer sizes in bytes for the first and second channels
    add_str_ch1 = 'ACQ:AXI:SOUR1:SET:Buffer ' + str(start) + ',' + str(size)

    rp_s.tx_txt(add_str_ch1)
    rp_s.check_error()

    # You need to specify the number of samples after the trigger
    rp_s.tx_txt('ACQ:AXI:SOUR1:Trig:Dly '+ str(samples))
    rp_s.check_error()

    rp_s.tx_txt('ACQ:AXI:SOUR1:ENable ON')
    rp_s.check_error()

    rp_s.tx_txt('ACQ:START')
    # rp_s.tx_txt('ACQ:TRIG CH1_PE')    # Replaced below.
    # rp_s.check_error()

    # ---- CUSTOM CODE START ----
    rp_s.tx_txt('ACQ:TRig:LEV 0.9')  # 0.9V seems to work reliably so far.
    rp_s.tx_txt('ACQ:TRIG EXT_PE')   # Wait for Trigger on the External Input, Positive Edge
    # ^^^^ CUSTOM CODE END ^^^^

    while 1:
        rp_s.tx_txt('ACQ:AXI:SOUR1:TRIG:FILL?')
        if rp_s.rx_txt() == '1':
            break
        pulser_fire_once(rp_s)

    print("All data captured")
    rp_s.tx_txt('ACQ:STOP')

    trig = int(rp_s.txrx_txt('ACQ:AXI:SOUR1:Trig:Pos?'))

    # It is quite difficult for the server to transfer a large amount of data at once, and there may not be
    # enough memory with a very large capture buffer. Therefore, we request data from the server in parts.
    received_size = 0
    block_size = 50000
    buff_all = []
    fail_ct = 0
    while received_size < samples:
        if (received_size + block_size) > samples:
            block_size = samples - received_size

        rp_s.tx_txt('ACQ:AXI:SOUR1:DATA:Start:N? ' + str(trig)+',' + str(block_size))
        buff_byte = rp_s.rx_arb()
        if buff_byte:
            buff = [struct.unpack('!h',bytearray(buff_byte[i:i+2]))[0] for i in range(0, len(buff_byte), 2)]
            buff_all = np.append(buff_all, buff)
            trig += block_size
            trig = trig % samples
            received_size += block_size
        else:
            fail_ct += 1
            if fail_ct > 100:
                print(f"scope_get_dma_data(): Timeout receiving data.  Giving up.")

    print(f"All data transferred. Fail_ct: {fail_ct}.  Samples recovered: {len(buff_all)}.")
    print("Buffer last 100 samples",buff_all[-100:])
    print('Releasing Red Pitaya resources.')
    rp_s.tx_txt('ACQ:AXI:SOUR1:ENable OFF')
    time.sleep(1)               # Paranoia.  May/should not be necessary.

# plot.plot(buff_all)
# plot.ylabel('Voltage')
# plot.show()

# Close connection with Red Pitaya
print('Releasing Red Pitaya resources.')
rp_s.close()
print("Exiting.")
As shown, the first iteration seems to work, but when I try one more acquisition loop (with the same settings), things break. Harshly. It feels like I'm walking over some memory, or something low level like that, as the board sometimes hangs (and loses all network awareness, requiring a reboot).

Any ideas?

-Chris

User avatar
redpitaya
Site Admin
Posts: 926
Joined: Wed Mar 26, 2014 7:04 pm

Re: DMA Acquisition breaks on 2nd attempt, every time.

Post by redpitaya » Fri Apr 26, 2024 2:04 pm

Hello Chris,

Please try these examples here:
https://redpitaya.readthedocs.io/en/lat ... cquisition

They should be up to date. We need to test and updated the examples on GitHub.

chris@ner.com
Posts: 7
Joined: Thu Apr 18, 2024 4:34 pm

Re: DMA Acquisition breaks on 2nd attempt, every time.

Post by chris@ner.com » Mon Apr 29, 2024 10:46 pm

Excellent! Thank you for the help! I was definitely referring to the wrong examples!

A quick follow up question: In the example code we see this:

Code: Select all

while 1:
            # Give me a ping, Vasili. One ping only.
            pulser_fire_once(self.com)  # takes up 0.1 seconds to complete.
            # Wait for trigger
            self.com.tx_txt("ACQ:TRig:STAT?")
            if self.com.rx_txt() == 'TD':
                print("Triggered")
                time.sleep(1)           #      ****** hhhm.... why? ******
                break
            # wait for fill adc buffer
            self.com.tx_txt('ACQ:AXI:SOUR1:TRIG:FILL?')
            if self.com.rx_txt() == '1':
                break
        self.com.tx_txt('ACQ:STOP')
  • Could you provide a quick explanation of why the time.sleep(1) line exists?
  • Is it really required, and can it be safely optimized out?
That bit takes up 50% of the acquisition time...

Thanks!
Chris

User avatar
redpitaya
Site Admin
Posts: 926
Joined: Wed Mar 26, 2014 7:04 pm

Re: DMA Acquisition breaks on 2nd attempt, every time.

Post by redpitaya » Tue Apr 30, 2024 8:54 am

To be perfectly frank, that specific line of code is sort of redundant and can be safely removed.

We introduced "ACQ:SOUR1:TRIG:FILL?" command in 2.00 OS. Its purpose is to check whether the buffer is full (if all acquisition data is acquired). Back in 1.04 this command did not exist, so it was possible that you retrieved data from Red Pitaya too soon (after the triggering moment, but before the buffer was completely full), which is why we added a delay there to make sure that does not happen.

Thank you for pointing it out, I will updated the examples to exclude the redundant pause.

Post Reply
jadalnie klasyczne ekskluzywne meble wypoczynkowe do salonu ekskluzywne meble tapicerowane ekskluzywne meble do sypialni ekskluzywne meble włoskie

Who is online

Users browsing this forum: No registered users and 17 guests