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
6
Answers one question: can this bench actually capture with the AD2? That needs
7
three separate things to be true, and this script fails distinctly on each so
8
the failure names its own fix:
9
10
1. ``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.
13
2. The SDK enumerates at least one device. Distinguishes "instrument unplugged
14
or claimed by ``ftdi_sio``" from "software broken".
15
3. 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
20
A device that is already open in another process reports "busy", which counts
21
as a PASS: it proves the library, the device and the resources are all good,
22
and a bench running a live capture must not be flagged as broken.
23
24
Exit status is 0 on pass and 1 on any failure, so it is usable as a gate.
25
"""
26
27
from
__future__
import
annotations
28
29
import
ctypes
30
import
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".
34
DWF_FAILURE = 0
35
DWF_BAD_HANDLE = 0
36
# FDwfEnum device filter: 0 == enumfilterAll, every supported Digilent device.
37
DWF_ENUM_FILTER_ALL = 0
38
# FDwfGetLastErrorMsg writes into a caller-supplied buffer; the SDK documents
39
# 512 bytes as the required size.
40
DWF_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.
43
DWF_BUSY_MARKER =
"busy"
44
45
46
def
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
65
def
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
79
def
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
93
def
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
109
def
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
133
def
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
176
if
__name__ ==
"__main__"
:
177
sys.exit(
main
())
main
void main(void)
The application entry point Reset_Handler hands control to.
Definition
main.c:298
scripts
hil
ad2_smoke.py
Generated by
1.16.1