@@ -18,13 +18,9 @@ when the blocking period exceeds a threshold. The threshold can be a fixed time
18
18
or the current maximum blocking period. A logic analyser enables the state at
19
19
the time of the transient event to be examined.
20
20
21
- The following image shows the ` quick_test.py ` code being monitored at the point
22
- when a task hogs the CPU. The top line 00 shows the "hog detect" trigger. Line
23
- 01 shows the fast running ` hog_detect ` task which cannot run at the time of the
24
- trigger because another task is hogging the CPU. Lines 02 and 04 show the ` foo `
25
- and ` bar ` tasks. Line 03 shows the ` hog ` task and line 05 is a trigger issued
26
- by ` hog() ` when it starts monopolising the CPU. The Pico issues the "hog
27
- detect" trigger 100ms after hogging starts.
21
+ This image shows the detection of CPU hogging. A trigger pulse is generated
22
+ 100ms after hogging caused the scheduler to be unable to schedule tasks. It is
23
+ discussed in more detail in [ section 6] ( ./README.md#6-test-and-demo-scripts ) .
28
24
29
25
![ Image] ( ./monitor.jpg )
30
26
@@ -37,8 +33,8 @@ to demonstrate that this never exceeded 5ms.
37
33
## 1.1 Concepts
38
34
39
35
Communication with the Pico may be by UART or SPI, and is uni-directional from
40
- DUT to Pico. If a UART is used only one GPIO pin is needed. SPI requires three
41
- - ` mosi ` , ` sck ` and ` cs/ ` .
36
+ DUT to Pico. If a UART is used only one GPIO pin is needed. SPI requires three,
37
+ namely ` mosi ` , ` sck ` and ` cs/ ` .
42
38
43
39
The Pico runs the following:
44
40
``` python
@@ -189,8 +185,7 @@ crashed, was interrupted or failed.
189
185
190
186
Re-using idents would lead to confusing behaviour. If an ident is out of range
191
187
or is assigned to more than one coroutine an error message is printed and
192
- execution terminates. See [ section 7.3] ( ./README.md#73-validation ) for a
193
- special case where validation must be defeated.
188
+ execution terminates.
194
189
195
190
# 3. Monitoring uasyncio code
196
191
@@ -210,6 +205,9 @@ The decorator positional args are as follows:
210
205
task to be independently monitored (default 1).
211
206
3 . ` verbose=True ` If ` False ` suppress the warning which is printed on the DUT
212
207
if the instance count exceeds ` max_instances ` .
208
+ 4 . ` looping=False ` Set ` True ` if the decorator is called repeatedly e.g.
209
+ decorating a nested function or method. The ` True ` value ensures validation of
210
+ the ident occurs once only when the decorator first runs.
213
211
214
212
Whenever the coroutine runs, a pin on the Pico will go high, and when the code
215
213
terminates it will go low. This enables the behaviour of the system to be
@@ -261,13 +259,13 @@ import monitor
261
259
asyncio.create_task(monitor.hog_detect())
262
260
# code omitted
263
261
```
264
- To aid in detecting the gaps in execution, the Pico code implements a timer.
265
- This is retriggered by activity on ` ident=0 ` . If it times out, a brief high
266
- going pulse is produced on GPIO 28, along with the console message "Hog". The
267
- pulse can be used to trigger a scope or logic analyser. The duration of the
268
- timer may be adjusted. Other modes of hog detection are also supported, notably
269
- producing a trigger pulse only when the prior maximum was exceeded. See
270
- [ section 4 ] ( ./README.md~4-the-pico-code ) .
262
+ To aid in detecting the gaps in execution, in its default mode the Pico code
263
+ implements a timer. This is retriggered by activity on ` ident=0 ` . If it times
264
+ out, a brief high going pulse is produced on GPIO 28, along with the console
265
+ message "Hog". The pulse can be used to trigger a scope or logic analyser. The
266
+ duration of the timer may be adjusted. Other modes of hog detection are also
267
+ supported, notably producing a trigger pulse only when the prior maximum was
268
+ exceeded. See [ section 5 ] ( ./README.md#5-Pico ) .
271
269
272
270
# 4. Monitoring arbitrary code
273
271
@@ -292,13 +290,17 @@ duration of every call to `sync_func()`:
292
290
def sync_func ():
293
291
pass
294
292
```
293
+ Decorator args:
294
+ 1 . ` ident `
295
+ 2 . ` looping=False ` Set ` True ` if the decorator is called repeatedly e.g. in a
296
+ nested function or method. The ` True ` value ensures validation of the ident
297
+ occurs once only when the decorator first runs.
295
298
296
299
## 4.2 The mon_call context manager
297
300
298
301
This may be used to monitor a function only when called from specific points in
299
- the code. Validation of idents is looser here because a context manager is
300
- often used in a looping construct: it seems impractical to distinguish this
301
- case from that where two context managers are instantiated with the same ID.
302
+ the code. Since context managers may be used in a looping construct the ident
303
+ is only checked for conflicts when the CM is first instantiated.
302
304
303
305
Usage:
304
306
``` python
@@ -319,8 +321,7 @@ is created by passing the ident. If the instance is run with no args a brief
319
321
(~ 80μs) pulse will occur on the Pico pin. If ` True ` is passed, the pin will go
320
322
high until ` False ` is passed.
321
323
322
- The closure should be instantiated once only. If instantiated in a loop the
323
- ident will fail the check on re-use.
324
+ The closure should be instantiated once only in the outermost scope.
324
325
``` python
325
326
trig = monitor.trigger(10 ) # Associate trig with ident 10.
326
327
@@ -335,10 +336,10 @@ def bar():
335
336
## 4.4 Timing of code segments
336
337
337
338
It can be useful to time the execution of a specific block of code especially
338
- if the time varies. It is possible to cause a message to be printed and a
339
- trigger pulse to be generated whenever the execution time exceeds the prior
340
- maximum. The scope or logic analyser may be triggered by this pulse allowing
341
- the state of other parts of the system to be checked.
339
+ if the duration varies in real time . It is possible to cause a message to be
340
+ printed and a trigger pulse to be generated whenever the execution time exceeds
341
+ the prior maximum. A scope or logic analyser may be triggered by this pulse
342
+ allowing the state of other components of the system to be checked.
342
343
343
344
This is done by re-purposing ident 0 as follows:
344
345
``` python
@@ -467,13 +468,21 @@ from monitor_pico import run, WIDTH
467
468
run((20 , WIDTH )) # Ignore widths < 20ms.
468
469
```
469
470
Assuming that ident 0 is used as described in
470
- [ section 4.4 ] ( ./README.md#44 -timing-of-code-segments ) a trigger pulse on GPIO28
471
+ [ section 5.5 ] ( ./README.md#55 -timing-of-code-segments ) a trigger pulse on GPIO28
471
472
will occur each time the time taken exceeds both 20ms and its prior maximum. A
472
473
message with the actual width is also printed whenever this occurs.
473
474
474
475
# 6. Test and demo scripts
475
476
476
- ` quick_test.py ` Primarily tests deliberate CPU hogging. Discussed in section 1.
477
+ The following image shows the ` quick_test.py ` code being monitored at the point
478
+ when a task hogs the CPU. The top line 00 shows the "hog detect" trigger. Line
479
+ 01 shows the fast running ` hog_detect ` task which cannot run at the time of the
480
+ trigger because another task is hogging the CPU. Lines 02 and 04 show the ` foo `
481
+ and ` bar ` tasks. Line 03 shows the ` hog ` task and line 05 is a trigger issued
482
+ by ` hog() ` when it starts monopolising the CPU. The Pico issues the "hog
483
+ detect" trigger 100ms after hogging starts.
484
+
485
+ ![ Image] ( ./monitor.jpg )
477
486
478
487
` full_test.py ` Tests task timeout and cancellation, also the handling of
479
488
multiple task instances. If the Pico is run with ` run((1, MAX)) ` it reveals
@@ -508,6 +517,8 @@ in `hog_detect` show the periods of deliberate CPU hogging.
508
517
` syn_time.py ` Demonstrates timing of a specific code segment with a trigger
509
518
pulse being generated every time the period exceeds its prior maximum.
510
519
520
+ ![ Image] ( ./tests/syn_time.jpg )
521
+
511
522
# 7. Internals
512
523
513
524
## 7.1 Performance and design notes
@@ -573,21 +584,7 @@ In the following, `thresh` is the time passed to `run()` in `period[0]`.
573
584
* ` MAX ` Trigger occurs if period exceeds ` thresh ` and also exceeds the prior
574
585
maximum.
575
586
576
- This project was inspired by
577
- [ this GitHub thread] ( https://github.com/micropython/micropython/issues/7456 ) .
578
-
579
- ## 7.3 Validation
580
-
581
- The ` monitor ` module attempts to protect against inadvertent multiple use of an
582
- ` ident ` . There are use patterns which are incompatible with this, notably where
583
- a decorated function or coroutine is instantiated in a looping construct. To
584
- cater for such cases validation can be defeated. This is done by issuing:
585
- ``` python
586
- import monitor
587
- monitor.validation(False )
588
- ```
589
-
590
- ## 7.4 ESP8266 note
587
+ ## 7.3 ESP8266 note
591
588
592
589
ESP8266 applications can be monitored using the transmit-only UART 1.
593
590
@@ -610,3 +607,6 @@ device under test is on the right, linked to the Pico board by means of a UART.
610
607
![ Image] ( ./monitor_hw.JPG )
611
608
612
609
I can supply a schematic and PCB details if anyone is interested.
610
+
611
+ This project was inspired by
612
+ [ this GitHub thread] ( https://github.com/micropython/micropython/issues/7456 ) .
0 commit comments