1
1
# 1. A uasyncio monitor
2
2
3
3
This library provides a means of examining the behaviour of a running
4
- ` uasyncio ` system. The device under test is linked to a Raspberry Pi Pico. The
4
+ ` uasyncio ` system. The device under test is linked to a Raspberry Pico. The
5
5
latter displays the behaviour of the host by pin changes and/or optional print
6
6
statements. A logic analyser or scope provides an insight into the way an
7
7
asynchronous application is working; valuable informtion can also be gleaned at
@@ -25,11 +25,13 @@ trigger because another task is hogging the CPU. Lines 02 and 04 show the `foo`
25
25
and ` bar ` tasks. Line 03 shows the ` hog ` task and line 05 is a trigger issued
26
26
by ` hog() ` when it starts monopolising the CPU. The Pico issues the "hog
27
27
detect" trigger 100ms after hogging starts.
28
+
28
29
![ Image] ( ./monitor.jpg )
29
30
30
31
The following image shows brief (<4ms) hogging while ` quick_test.py ` ran. The
31
32
likely cause is garbage collection on the Pyboard D host. The monitor was able
32
33
to demostrate that this never exceeded 5ms.
34
+
33
35
![ Image] ( ./monitor_gc.jpg )
34
36
35
37
### Status
@@ -43,9 +45,15 @@ The device being monitored must run firmware V1.17 or later. The `uasyncio`
43
45
version should be V3 (included in the firmware). The file ` monitor.py ` should
44
46
be copied to the target, and ` monitor_pico ` to the Pico.
45
47
46
- ## 1.2 Usage
48
+ ## 1.2 Quick start guide
47
49
48
- A minimal example of a UART-monitored application looks like this:
50
+ For UART based monitoring, ensure that the host and Pico ` gnd ` pins are linked.
51
+ Connect the host's ` txd ` to the Pico pin 2 (UART(0) ` rxd ` ). On the Pico issue:
52
+ ``` python
53
+ from monitor_pico import run
54
+ run()
55
+ ```
56
+ Adapt the following to match the UART to be used on the host and run it.
49
57
``` python
50
58
import uasyncio as asyncio
51
59
from machine import UART # Using a UART for monitoring
67
75
finally :
68
76
asyncio.new_event_loop()
69
77
```
78
+ A square wave of period 200ms should be observed on Pico GPIO 4 (pin 6).
79
+
70
80
Example script ` quick_test.py ` provides a usage example. It may be adapted to
71
81
use a UART or SPI interface: see commented-out code.
72
82
@@ -129,7 +139,9 @@ only go low when all instances associated with that pin have terminated.
129
139
130
140
Consequently if ` max_instances=1 ` and multiple instances are launched, a
131
141
warning will appear on the host; the pin will go high when the first instance
132
- starts and will not go low until all have ended.
142
+ starts and will not go low until all have ended. The purpose of the warning is
143
+ because the existence of multiple instances may be unexpected behaviour in the
144
+ application under test.
133
145
134
146
## 1.3 Detecting CPU hogging
135
147
@@ -161,21 +173,22 @@ asyncio.create_task(monitor.hog_detect())
161
173
```
162
174
To aid in detecting the gaps in execution, the Pico code implements a timer.
163
175
This is retriggered by activity on ` ident=0 ` . If it times out, a brief high
164
- going pulse is produced on pin 28, along with the console message "Hog". The
176
+ going pulse is produced on GPIO 28, along with the console message "Hog". The
165
177
pulse can be used to trigger a scope or logic analyser. The duration of the
166
178
timer may be adjusted. Other modes of hog detection are also supported. See
167
179
[ section 4] ( ./README.md~4-the-pico-code ) .
168
180
169
181
## 1.4 Validation of idents
170
182
171
- Re-using idents would lead to confusing behaviour. A ` ValueError ` is thrown if
172
- an ident is out of range or is assigned to more than one coroutine.
183
+ Re-using idents would lead to confusing behaviour. If an ident is out of range
184
+ or is assigned to more than one coroutine an error message is printed and
185
+ execution terminates.
173
186
174
187
# 2. Monitoring synchronous code
175
188
176
189
In the context of an asynchronous application there may be a need to view the
177
- timing of synchronous code, or simply to create a trigger pulse at a known
178
- point in the code. The following are provided:
190
+ timing of synchronous code, or simply to create a trigger pulse at one or more
191
+ known points in the code. The following are provided:
179
192
* A ` sync ` decorator for synchronous functions or methods: like ` async ` it
180
193
monitors every call to the function.
181
194
* A ` trigger ` function which issues a brief pulse on the Pico.
@@ -192,15 +205,15 @@ monitor.reserve(4, 9, 10)
192
205
193
206
## 2.1 The sync decorator
194
207
195
- This works as per the ` @async ` decorator, but with no ` max_instances ` arg. This
196
- will activate GPIO 26 (associated with ident 20) for the duration of every call
197
- to ` sync_func() ` :
208
+ This works as per the ` @async ` decorator, but with no ` max_instances ` arg. The
209
+ following example will activate GPIO 26 (associated with ident 20) for the
210
+ duration of every call to ` sync_func() ` :
198
211
``` python
199
212
@monitor.sync (20 )
200
213
def sync_func ():
201
214
pass
202
215
```
203
- Note that the ident must not be reserved.
216
+ Note that idents used by decorators must not be reserved.
204
217
205
218
## 2.2 The mon_call context manager
206
219
@@ -217,7 +230,7 @@ with monitor.mon_call(22):
217
230
```
218
231
219
232
It is advisable not to use the context manager with a function having the
220
- ` mon_func ` decorator. The pin and report behaviour is confusing.
233
+ ` mon_func ` decorator. The behaviour of pins and reports are confusing.
221
234
222
235
## 2.3 The trigger timing marker
223
236
@@ -288,7 +301,7 @@ over 100ms. These behaviours can be modified by the following `run` args:
288
301
1 . ` period=100 ` Define the hog_detect timer period in ms.
289
302
2 . ` verbose=() ` A list or tuple of ` ident ` values which should produce console
290
303
output.
291
- 3 . ` device="uart" ` Set to "spi" for an SPI interface.
304
+ 3 . ` device="uart" ` Set to ` "spi" ` for an SPI interface.
292
305
4 . ` vb=True ` By default the Pico issues console messages reporting on initial
293
306
communication status, repeated each time the application under test restarts.
294
307
Set ` False ` to disable these messages.
@@ -356,16 +369,16 @@ only goes low when the last of these three instances is cancelled.
356
369
![ Image] ( ./tests/full_test.jpg )
357
370
358
371
` latency.py ` Measures latency between the start of a monitored task and the
359
- Pico pin going high. The sequence below is first the task pulses a pin (ident
360
- 6). Then the Pico pin monitoring the task goes high (ident 1 after ~ 20μs). Then
361
- the trigger on ident 2 occurs 112μs after the pin pulse.
372
+ Pico pin going high. In the image below the sequence starts when the host
373
+ pulses a pin (ident 6). The Pico pin monitoring the task then goes high (ident
374
+ 1 after ~ 20μs). Then the trigger on ident 2 occurs 112μs after the pin pulse.
362
375
363
376
![ Image] ( ./tests/latency.jpg )
364
377
365
378
` syn_test.py ` Demonstrates two instances of a bound method along with the ways
366
379
of monitoring synchronous code. The trigger on ident 5 marks the start of the
367
380
sequence. The ` foo1.pause ` method on ident 1 starts and runs ` foo1.wait1 ` on
368
- ident 3. 100ms after this ends, ` foo ` .wait2` on ident 4 is triggered. 100ms
381
+ ident 3. 100ms after this ends, ` foo.wait2 ` on ident 4 is triggered. 100ms
369
382
after this ends, ` foo1.pause ` on ident 1 ends. The second instance of ` .pause `
370
383
(` foo2.pause ` ) on ident 2 repeats this sequence shifted by 50ms. The 10ms gaps
371
384
in ` hog_detect ` show the periods of deliberate CPU hogging.
@@ -412,9 +425,10 @@ identically to other platforms and can be rebooted at will.
412
425
413
426
This is for anyone wanting to modify the code. Each ident is associated with
414
427
two bytes, ` 0x40 + ident ` and ` 0x60 + ident ` . These are upper and lower case
415
- printable ASCII characters (aside from ident 0 which is ` @ ` and the backtick
416
- character). When an ident becomes active (e.g. at the start of a coroutine),
417
- uppercase is transmitted, when it becomes inactive lowercase is sent.
428
+ printable ASCII characters (aside from ident 0 which is ` @ ` paired with the
429
+ backtick character). When an ident becomes active (e.g. at the start of a
430
+ coroutine), uppercase is transmitted, when it becomes inactive lowercase is
431
+ sent.
418
432
419
433
The Pico maintains a list ` pins ` indexed by ` ident ` . Each entry is a 3-list
420
434
comprising:
@@ -426,21 +440,23 @@ When a character arrives, the `ident` value is recovered. If it is uppercase
426
440
the pin goes high and the instance count is incremented. If it is lowercase the
427
441
instance count is decremented: if it becomes 0 the pin goes low.
428
442
429
- The ` init ` function on the host sends ` b"z" ` to the Pico. This clears down the
430
- instance counters (the program under test may have previously failed, leaving
431
- instance counters non-zero). The Pico also clears variables used to measure
432
- hogging. In the case of SPI communication, before sending the ` b"z" ` , a 0
433
- character is sent with ` cs/ ` high. The Pico implements a basic SPI slave using
434
- the PIO. This may have been left in an invalid state by a crashing host. It is
435
- designed to reset to a known state if it receives a character with ` cs/ ` high.
443
+ The ` init ` function on the host sends ` b"z" ` to the Pico. This sets each pin
444
+ int ` pins ` low and clears its instance counter (the program under test may have
445
+ previously failed, leaving instance counters non-zero). The Pico also clears
446
+ variables used to measure hogging. In the case of SPI communication, before
447
+ sending the ` b"z" ` , a 0 character is sent with ` cs/ ` high. The Pico implements
448
+ a basic SPI slave using the PIO. This may have been left in an invalid state by
449
+ a crashing host. The slave is designed to reset to a "ready" state if it
450
+ receives any character with ` cs/ ` high.
436
451
437
452
The ident ` @ ` (0x40) is assumed to be used by the ` hog_detect() ` function. When
438
453
the Pico receives it, processing occurs to aid in hog detection and creating a
439
454
trigger on GPIO28. Behaviour depends on the mode passed to the ` run() ` command.
440
455
In the following, ` thresh ` is the time passed to ` run() ` in ` period[0] ` .
441
456
* ` SOON ` This retriggers a timer with period ` thresh ` . Timeout causes a
442
457
trigger.
443
- * ` LATE ` Trigger occurs if the period since the last ` @ ` exceeds ` thresh ` .
458
+ * ` LATE ` Trigger occurs if the period since the last ` @ ` exceeds ` thresh ` . The
459
+ trigger happens when the next ` @ ` is received.
444
460
* ` MAX ` Trigger occurs if period exceeds ` thresh ` and also exceeds the prior
445
461
maximum.
446
462
0 commit comments