ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ad2_smoke.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# SPDX-License-Identifier: MIT
3# Copyright (c) 2026 Brighton Sikarskie
4"""Smoke-test the Digilent Analog Discovery 2 through the WaveForms SDK.
5
6Answers one question: can this bench actually capture with the AD2? That needs
7three separate things to be true, and this script fails distinctly on each so
8the failure names its own fix:
9
101. ``libdwf`` loads. The WaveForms deb is installed by extracting it, not by
11 apt (see the ``ad2_tools`` Ansible role for why), so a botched extract shows
12 up here as a load error rather than as a missing package.
132. The SDK enumerates at least one device. Distinguishes "instrument unplugged
14 or claimed by ``ftdi_sio``" from "software broken".
153. A device opens. This is the step that needs
16 ``/usr/share/digilent/waveforms`` -- without those firmware/configuration
17 resources ``FDwfDeviceOpen`` fails with "Device not supported. No compatible
18 configuration found", which enumeration alone would never reveal.
19
20A device that is already open in another process reports "busy", which counts
21as a PASS: it proves the library, the device and the resources are all good,
22and a bench running a live capture must not be flagged as broken.
23
24Exit status is 0 on pass and 1 on any failure, so it is usable as a gate.
25"""
26
27from __future__ import annotations
28
29import ctypes
30import sys
31
32# libdwf reports failure as 0 and success as non-zero (its own convention, not
33# an errno), and hands back a device handle of 0 for "not opened".
34DWF_FAILURE = 0
35DWF_BAD_HANDLE = 0
36# FDwfEnum device filter: 0 == enumfilterAll, every supported Digilent device.
37DWF_ENUM_FILTER_ALL = 0
38# FDwfGetLastErrorMsg writes into a caller-supplied buffer; the SDK documents
39# 512 bytes as the required size.
40DWF_ERROR_BUFFER_BYTES = 512
41# Substring libdwf puts in the error message when the device is healthy but
42# already claimed by another process. Matched case-insensitively.
43DWF_BUSY_MARKER = "busy"
44
45
46def load_dwf() -> ctypes.CDLL | None:
47 """Load the WaveForms shared library.
48
49 Returns:
50 The loaded ``libdwf`` handle, or None when it cannot be loaded. The
51 loader's own message and the remedy are printed to stderr in that case.
52 """
53 try:
54 return ctypes.CDLL("libdwf.so")
55 except OSError as exc:
56 print(
57 f"FAIL: cannot load libdwf.so: {exc}\n"
58 " The WaveForms SDK is not installed, or /usr/local/lib is not\n"
59 " in the loader cache. Re-run the ad2_tools Ansible role.",
60 file=sys.stderr,
61 )
62 return None
63
64
65def last_error(dwf: ctypes.CDLL) -> str:
66 """Read the most recent libdwf error message.
67
68 Args:
69 dwf: The loaded ``libdwf`` handle.
70
71 Returns:
72 The SDK's error text, or a placeholder when it reported none.
73 """
74 buf = ctypes.create_string_buffer(DWF_ERROR_BUFFER_BYTES)
75 dwf.FDwfGetLastErrorMsg(buf)
76 return buf.value.decode(errors="replace").strip() or "(no message)"
77
78
79def sdk_version(dwf: ctypes.CDLL) -> str:
80 """Query the WaveForms SDK version string.
81
82 Args:
83 dwf: The loaded ``libdwf`` handle.
84
85 Returns:
86 The version reported by ``FDwfGetVersion``.
87 """
88 buf = ctypes.create_string_buffer(DWF_ERROR_BUFFER_BYTES)
89 dwf.FDwfGetVersion(buf)
90 return buf.value.decode(errors="replace").strip()
91
92
93def enum_devices(dwf: ctypes.CDLL) -> int | None:
94 """Count the Digilent devices the SDK can see.
95
96 Args:
97 dwf: The loaded ``libdwf`` handle.
98
99 Returns:
100 The number of enumerated devices, or None when the enumeration call
101 itself failed (as opposed to succeeding with a count of zero).
102 """
103 count = ctypes.c_int()
104 if dwf.FDwfEnum(ctypes.c_int(DWF_ENUM_FILTER_ALL), ctypes.byref(count)) == DWF_FAILURE:
105 return None
106 return count.value
107
108
109def try_open(dwf: ctypes.CDLL) -> tuple[bool, str]:
110 """Attempt to open the first available device and close it again.
111
112 Args:
113 dwf: The loaded ``libdwf`` handle.
114
115 Returns:
116 A ``(ok, detail)`` pair. ``ok`` is True when the device opened, or when
117 it refused because another process holds it -- both prove the SDK, the
118 device and the configuration resources are sound. ``detail`` is a
119 human-readable outcome.
120 """
121 handle = ctypes.c_int()
122 opened = dwf.FDwfDeviceOpen(ctypes.c_int(-1), ctypes.byref(handle))
123 if opened != DWF_FAILURE and handle.value != DWF_BAD_HANDLE:
124 dwf.FDwfDeviceClose(handle)
125 return True, "opened and closed cleanly"
126
127 detail = last_error(dwf)
128 if DWF_BUSY_MARKER in detail.lower():
129 return True, f"busy -- another process holds it ({detail})"
130 return False, detail
131
132
133def main() -> int:
134 """Run the three-stage smoke test.
135
136 Returns:
137 0 when the bench can capture, 1 otherwise.
138 """
139 dwf = load_dwf()
140 if dwf is None:
141 return 1
142 print(f"libdwf loaded, SDK version {sdk_version(dwf)}")
143
144 count = enum_devices(dwf)
145 if count is None:
146 print(f"FAIL: FDwfEnum failed: {last_error(dwf)}", file=sys.stderr)
147 return 1
148 if count < 1:
149 print(
150 "FAIL: libdwf enumerates no devices.\n"
151 " Check the AD2 is plugged in and powered (lsusb -d 0403:6014),\n"
152 " and that the Adept runtime's udev rules ran dftdrvdtch to\n"
153 " detach ftdi_sio from the interface.",
154 file=sys.stderr,
155 )
156 return 1
157 print(f"libdwf enumerates {count} device(s)")
158
159 ok, detail = try_open(dwf)
160 if not ok:
161 print(
162 f"FAIL: FDwfDeviceOpen failed: {detail}\n"
163 " 'No compatible configuration found' means the device\n"
164 " firmware/configuration resources are missing -- they must be\n"
165 " installed to /usr/share/digilent/waveforms. Re-run the\n"
166 " ad2_tools Ansible role.",
167 file=sys.stderr,
168 )
169 return 1
170
171 print(f"device open: {detail}")
172 print("PASS: the AD2 is present and capture-capable")
173 return 0
174
175
176if __name__ == "__main__":
177 sys.exit(main())
void main(void)
The application entry point Reset_Handler hands control to.
Definition main.c:298