Skip to content

Commit cf11598

Browse files
committed
Add DMA test ADSY1100
This a simplified ADC smoke test which loops acquisitions and checks if the buffers get loaded. Signed-off-by: Ciprian Hegbeli <ciprian.hegbeli@analog.com>
1 parent 18dfe1e commit cf11598

File tree

1 file changed

+356
-0
lines changed

1 file changed

+356
-0
lines changed

test/adsy1100/test_dma_buffer.py

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
import paramiko
2+
import time
3+
import argparse
4+
import logging as log
5+
6+
from importlib.resources import files, as_file
7+
from pathlib import Path
8+
9+
import adi
10+
11+
unit_ip='10.48.65.211'
12+
dtbo_location = "vu11p/vu11p-ad9084-vpx-uc47-reva.dtbo"
13+
bin_location = "vu11p/vu11p_uc47.bin"
14+
15+
16+
pdu3_ip = '10.48.65.238'
17+
18+
def _set_logging_level(verbose: bool = False, debug: bool = False):
19+
"""Configure logging level based on flags.
20+
21+
:param verbose: Enable INFO level logging
22+
:param debug: Enable DEBUG level logging (overrides verbose)
23+
"""
24+
if debug:
25+
log_level = log.DEBUG
26+
elif verbose:
27+
log_level = log.INFO
28+
else:
29+
log_level = log.WARNING
30+
31+
log.basicConfig(level=log_level)
32+
33+
def _module_login():
34+
#Helper function to login to the module once power is active
35+
36+
#Creates a client and returns it upon connection success.
37+
38+
#Need to add a counter and error if fails to connect after X times
39+
40+
# ################ Attempt to log in to root@192.168.2.1 ################
41+
login_successful = False
42+
43+
# Initialize client
44+
client = paramiko.SSHClient()
45+
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
46+
47+
while (not login_successful):
48+
# Establish an SSH connection to the card
49+
try:
50+
client.connect(hostname=unit_ip, port=22, username='root', password='root')
51+
login_successful = True # Login Succeeded
52+
except:
53+
continue # Try the while loop again
54+
55+
log.info("SSH connection established.\n")
56+
57+
return client
58+
59+
def _SSH_execute_command(session: paramiko.SSHClient, cmd: str):
60+
"""Takes in a client session and command, returns the resulting output and/or error (if applicable)
61+
62+
:param session: SSH session with Washington connection
63+
:type session: paramiko.SSHClient
64+
:param command: command to execute over SSH
65+
:type command: string
66+
67+
:return: resulting output from SSH command
68+
:rtype: str
69+
"""
70+
# Execute command over SSH
71+
_, stdout, stderr = session.exec_command(cmd)
72+
stdout.channel.recv_exit_status() # Verifies command execution is completed before proceeding
73+
74+
# Check for error; if there is an error, print the result to the terminal
75+
error_result = stderr.read().decode('utf-8')
76+
if error_result:
77+
log.warning(f"'{cmd}' command has the following error - {error_result}")
78+
79+
# Reads the resulting output and returns
80+
return stdout.read().decode('utf-8')
81+
82+
def _interpret_dmesg_readback(dmesg_result: str):
83+
"""Interprets the current dmesg results and returns 2 items
84+
- # of enabled JESD lanes
85+
- names of failed JESD lanes
86+
87+
:param dmesg_result: dmesg output for interpretation
88+
:type dmesg_result: string
89+
90+
:return: [count of the enabled JESD lanes, names of failed JESD lanes]
91+
:rtype: tuple(int, list(str))
92+
"""
93+
# Split the string by each line
94+
split_by_line = dmesg_result.split('\n')
95+
96+
important_lines = []
97+
failing_jesd_lanes = []
98+
active_jesd_lane_cnt = 0
99+
100+
for line in split_by_line:
101+
try:
102+
# Verify a given line has JESD & Rx or Tx information
103+
if ("rx" in line.split(' ')[4] or "tx" in line.split(' ')[4] and "jesd" in line.split(' ')[4]):
104+
important_lines.append(line)
105+
except:
106+
# If there is an error, continue since this line cannot be one that has JESD information that is needed
107+
continue
108+
109+
# Loop over each important line
110+
for line_2 in important_lines:
111+
# if case to verify that a given jesd channel is active
112+
if ('width 8/8' in line_2):
113+
active_jesd_lane_cnt += 1 # increment the counter for the number of active JESD lanes
114+
# if case to check if a given jesd channel has a failure
115+
elif ('status failed (WAIT_BS)' in line_2):
116+
failing_jesd_lanes.append(line_2.split(' ')[4][:-1]) # append the name of the JESD lane(s) with at least one failure
117+
118+
return active_jesd_lane_cnt, failing_jesd_lanes
119+
120+
def _module_boot_check(client):
121+
# Initialize tracker for the number of JESD lanes that are active
122+
active_JESD_cnt = 0
123+
failed_boot = False
124+
loop_cnt = 0
125+
126+
# Check that all 4 JESD lanes are displayed in dmesg before continuing
127+
# while (active_JESD_cnt != 1):
128+
while (active_JESD_cnt != 4 and not failed_boot):
129+
loop_cnt += 1
130+
log.debug(f"Checking dmesg for JESD lane initialization... Attempt #{loop_cnt}")
131+
# Add short delay between each dmesg read
132+
time.sleep(1)
133+
134+
# Run dmesg command
135+
full_dmesg = _SSH_execute_command(client, "dmesg")
136+
active_JESD_cnt, bad_JESD_lanes = _interpret_dmesg_readback(full_dmesg)
137+
log.debug(f"Number of active JESD lanes: {active_JESD_cnt}")
138+
log.debug(f"Failed JESD lanes: {bad_JESD_lanes}")
139+
140+
if "CF_AXI_DDS_DDS MASTER" in full_dmesg.split('\n')[-1] and loop_cnt >= 30:
141+
failed_boot = True
142+
143+
# If a failing JESD lane exist
144+
if (bool(len(bad_JESD_lanes))):
145+
log.warning(f"Not all JESD lanes initialized correctly. JESD lanes with issues - {'; '.join(bad_JESD_lanes)}\n")
146+
147+
return failed_boot
148+
149+
def __jesd_state_check(client):
150+
failed_boot = True
151+
state = ""
152+
max_retries = 18 #3 minutes of retries with 10 second delay between retries
153+
154+
for attempt in range(max_retries):
155+
try:
156+
_SSH_execute_command(client, "systemctl restart iiod")
157+
158+
dev = adi.ad9084(uri=f"ip:{unit_ip}")
159+
adc_fsm = dev.ctx.find_device("axi-ad9084-rx-hpc")
160+
state = adc_fsm.attrs["jesd204_fsm_state"].value
161+
162+
if state == "opt_post_running_stage":
163+
failed_boot = False
164+
break
165+
166+
log.info(f"Attempt {attempt + 1}/{max_retries}: state is '{state}', waiting...")
167+
time.sleep(10)
168+
except Exception as e:
169+
log.info(f"Attempt {attempt + 1}/{max_retries} failed: {e}")
170+
if attempt < max_retries - 1:
171+
time.sleep(10)
172+
else:
173+
log.error(f"All {max_retries} attempts failed")
174+
raise
175+
176+
return failed_boot
177+
178+
def _get_buffer(dev, side, chan):
179+
max_retries = 3
180+
for attempt in range(max_retries):
181+
try:
182+
if side == 'a':
183+
dev.rx_enabled_channels = [chan]
184+
iq_data = dev.rx()
185+
dev.rx_destroy_buffer()
186+
else:
187+
dev.rx2_enabled_channels = [chan]
188+
iq_data = dev.rx2()
189+
dev.rx2_destroy_buffer()
190+
return iq_data
191+
except Exception as e:
192+
log.warning(f"get_buffer() attempt {attempt + 1}/{max_retries} failed: {e}")
193+
log.warning(f"Retrying get_buffer() {side} {chan}...")
194+
if attempt < max_retries - 1:
195+
time.sleep(1)
196+
else:
197+
log.error(f"get_buffer() failed after {max_retries} attempts")
198+
raise
199+
200+
def ADSY1100_load_firmware():
201+
client = _module_login()
202+
203+
################ Run Startup Commands ################
204+
log.info("Running startup commands...\n")
205+
_SSH_execute_command(client, f"cd /boot/;./selmap_dtbo.sh -d {dtbo_location} -b {bin_location}")
206+
207+
# Status print
208+
log.info(".sh file successfully executed. Validating startup worked correctly...\n")
209+
210+
# failed_boot = _module_boot_check(client)
211+
failed_boot = __jesd_state_check(client)
212+
213+
# Status print
214+
log.info("Module started successfully.\n")
215+
216+
################ Restart IIO Daemon ################
217+
log.info("Restarting iio daemon...\n")
218+
time.sleep(5)
219+
_SSH_execute_command(client, "systemctl restart iiod")
220+
221+
################ Clean Up Clock Spurs ################
222+
log.info("Cleaning up Clock Spurs...\n")
223+
_SSH_execute_command(client, "iio_reg ltc6952 0x03 0x3F")
224+
225+
if failed_boot:
226+
log.warning("Firmware sequence failed. Trying again...")
227+
else:
228+
print("ADSY1100 firmware sequence completed successfully.")
229+
230+
client.close()
231+
232+
def ADSY1100_check_buffer():
233+
buffer_pass = True
234+
235+
max_retries = 2
236+
for attempt in range(max_retries):
237+
try:
238+
if not unit_ip:
239+
log.info("No unit IP provided, connecting locally.")
240+
dev = adi.ad9084(uri="local:")
241+
else:
242+
log.info(f"Connected to device at IP: {unit_ip}")
243+
dev = adi.ad9084(uri=f"ip:{unit_ip}")
244+
break
245+
except Exception as e:
246+
log.warning(f"adi.ad9084(uri=f\"ip:{unit_ip}\") attempt {attempt + 1}/{max_retries} failed: {e}")
247+
log.warning("Retrying connection to device...")
248+
if attempt < max_retries - 1:
249+
time.sleep(1)
250+
else:
251+
log.error(f"adi.ad9084(uri=f\"ip:{unit_ip}\") failed after {max_retries} attempts")
252+
return False
253+
254+
dev.rx_buffer_size = 2**12
255+
dev.rx2_buffer_size = 2**12
256+
257+
for side in ['a','b']:
258+
for chan in range(2):
259+
log.info(f"Testing {side} {chan}")
260+
261+
iq_data = _get_buffer(dev, side, chan)
262+
log.debug(iq_data)
263+
264+
if isinstance(iq_data, list):
265+
iq_data = iq_data[chan]
266+
267+
if len(iq_data) == 0:
268+
log.error(f"No data received on {side} {chan}\n")
269+
buffer_pass = False
270+
continue
271+
272+
return buffer_pass
273+
274+
def ADSY1100_reboot():
275+
client = _module_login()
276+
################ Run Reboot Command ################
277+
print("Running Reboot Command...\n")
278+
_SSH_execute_command(client, "reboot")
279+
280+
client.close()
281+
282+
def ADSY1100_power_cycle():
283+
# Helper function to power cycle the module using the PDU
284+
# Note: This function is not currently being used in the test, but can be used for future iterations if needed
285+
286+
# Create SSH client and connect to PDU
287+
print("Running Power Cycle Command...\n")
288+
max_retries = 3
289+
for attempt in range(max_retries):
290+
try:
291+
nuc5 = paramiko.SSHClient()
292+
nuc5.set_missing_host_key_policy(paramiko.AutoAddPolicy())
293+
nuc5.connect(hostname='10.48.65.151', port=22, username='nuc5', password='Analog123!')
294+
295+
# Power off the module
296+
_SSH_execute_command(nuc5, f"python3 /home/nuc5/nebula/nebula/cyberpower.py {pdu3_ip} 8 delayedReboot;")
297+
nuc5.close()
298+
break
299+
except Exception as e:
300+
log.warning(f"Power cycle attempt {attempt + 1}/{max_retries} failed: {e}")
301+
if attempt < max_retries - 1:
302+
time.sleep(1)
303+
else:
304+
log.error(f"Power cycle failed after {max_retries} attempts")
305+
raise
306+
307+
308+
# def save_dmesg(ADSY1100_SSH_Client: paramiko.SSHClient, data_path):
309+
# """
310+
# TODO
311+
# """
312+
# dmesg = _SSH_execute_command(ADSY1100_SSH_Client, "dmesg")
313+
# with open(f"{data_path}\\demsg_log_{time.strftime("%m%d%Y_%H%M%S", time.localtime())}.txt", "w") as file:
314+
# file.write(dmesg)
315+
316+
# def save_kernel(ADSY1100_SSH_Client: paramiko.SSHClient, data_path):
317+
# """
318+
# TODO
319+
# """
320+
# kernel = _SSH_execute_command(ADSY1100_SSH_Client, "journalctl -b --no-pager -k -p 7 -o short-precise --all")
321+
# with open(f"{data_path}\\kernel_log_{time.strftime("%m%d%Y_%H%M%S", time.localtime())}.txt", "w") as file:
322+
# file.write(kernel)
323+
324+
if __name__ == "__main__":
325+
326+
parser = argparse.ArgumentParser()
327+
parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output (INFO level)')
328+
parser.add_argument('--debug', action='store_true', help='Enable debug output (DEBUG level)')
329+
args = parser.parse_args()
330+
331+
_set_logging_level(verbose=args.verbose, debug=args.debug)
332+
print("Starting ADSY1100 DMA Buffer Test...\n")
333+
334+
buffer_check = ADSY1100_check_buffer()
335+
if buffer_check:
336+
print(f"Test {i+1} passed.")
337+
else:
338+
log.error(f"Test {i+1} failed.")
339+
340+
# iteration_count = 20
341+
# for i in range(iteration_count):
342+
# print(f"Starting iteration {i+1}/{iteration_count}...")
343+
344+
# ADSY1100_load_firmware()
345+
346+
# buffer_check = ADSY1100_check_buffer()
347+
# if buffer_check:
348+
# print(f"Test {i+1} passed.")
349+
# else:
350+
# log.error(f"Test {i+1} failed.")
351+
352+
# # ADSY1100_reboot()
353+
# ADSY1100_power_cycle()
354+
# time.sleep(10)
355+
356+
print("Action Complete")

0 commit comments

Comments
 (0)