-
Notifications
You must be signed in to change notification settings - Fork 66
STM8 eForth Background Task
In STM8 eForth, the Background Task feature makes interactive development and and test of INPUT-PROCESS-OUTPUT
control or UI tasks easy. It is sufficient for most simple "Ladder Logic" or Arduino-style embedded control applications.
The Background Task feature provides a context switch of the Forth virtual machine, and it doesn't need any multitasking features, like semaphores, since no resources are shared with the foreground task (the console).
There is a trade-off: no interpreter and parser words that influence the console are allowed in a background task (check out the Idle Task if you need that).
Control tasks don't require a console as they use analog or digital I/O. Developing and testing the background task can be done interactively: With the Forth command line the task operation can be inspected, and parameters can be altered.
The background task feature has the following properties:
- fast preemptive context switch of eForth VM with dedicated data stack and PAD
- the variable
BG
holds the address of a background task word - the background task has it's own
BASE
(initialized to 10 before each cycle) -
?KEY
andEMIT
use configurable I/O words (e.g. W1209 uses 7S-LED display and push buttons) - number output with
.
or<# .. # .. #>
will just work (there is a small PAD,HLD
gets initialized before each cycle) - time can be measured by using the cyclic increment of the 16 bit "timer ticker"
TIM
- the "task word" is called in a 5ms cycle, which can be changed by changing the
T2
reload value.
The run time shouldn't exceed 1ms, or whatever the character rate of serial IO is (or else the interactive console will stall).
The following code demonstrates non-interactive use of the Background Task:
\ blinky using the background task timer to flash all board outputs
VARIABLE timemask ;
$40 timemask !
: blinky timemask TIM OVER AND = OUT! ;
' blinky BG ! \ set background word, start flashing
$80 timemask ! \ interactive: slower flashing
Due to dedicated character I/O support background tasks can also be used for implementing a simple user interface (like a parameter settings menu).
The following code is example for an interactive background task: a very simple RAM monitor for the W1209 board:
\ Simple W1209 7-Seg Display Memory Monitor
\ key mapping
\ set = 'A': display address
\ + = 'B': increment address
\ - = 'D': decrement address
VARIABLE monaddr
: inc ( -- ) \ increment monaddr
1 monaddr +! ;
: dec ( -- ) \ decrement monaddr
-1 monaddr +! ;
: cadr ( -- ) \ react on keys "+" and "-"
DUP 66 = IF inc THEN 68 = IF dec THEN ;
: disp ( -- ) \ show monaddr while key pressed
monaddr BKEY IF ? ELSE @ C@ . THEN ;
: ledmon ( -- ) \ react on key
HEX ?KEY IF cadr ELSE disp THEN ;
128 monaddr ! \ set address
' ledmon BG ! \ start in background
The task will then be executed by the 5ms ticker interrupt. It can be stopped by changing the execution word address to null, e.g. 0 BG !
.
The startup routine is vectored, and using 'BOOT
an initialization word can be started to set the background vector BG
.
Note: the following write-up from the PoC gives an overview of the solution
The Background task PoC does a context switch, where the essential data of the Forth virtual CPU is retained. There is a small dedicated data stack, which gets reset with every cycle tick. In theory, the data stack could be shared with the console, but in practice determining the SP in an interrupt routine is tricky (it might be in X
, Y
, YTEMP
or even on the return stack).
Using the background task is simple:
- write a word that doesn't require input from the stack
- write the address of the word to the address provided by
BG
- at the end of stack doesn't need to be balanced
Example:
VARIABLE timer
: tSet TIM 300 + timer ! ;
: tTest TIM timer @ - 0 < ;
: delay KEY@ IF tSet THEN tTest IF 1 ELSE 0 THEN OUT! ;
' delay BG !
Press a key on the W1209 (or run tSet
from the Forth command line) to activate the relay for about 1.5 s (note: there is catch: since TIM
, a 16 bit counter, rolls over the pulse will repeat every 328s).
The main problem that this lightweight approach needs to solve is that the state of the Forth system is stored in the USR
area (0x60 .. 0x7F). As we won't use the outer interpreter, nor the compiler in the background task, only some USR
data is relevant: formatted output uses some USR
variables and PAD memory.
With the option HAS_BACKGROUND
active, the RAM memory has the following layout:
-----------------+
0000h .. 004Fh : Free for user variables
-----------------+
0050h .. 005Fh : Module variables
-----------------+
0060h .. 007Fh : USR area (and some core storage, e.g. XTEMP)
-----------------+
0080h .. HERE-1 : user dictionary
-----------------+
HERE .. HERE+79 : gap for new word definitions
PAD .. SPP-DEPTH : scratchpad memory, e.g for text output
-----------------+
SPP-DEPTH..032Fh : SSP (data stack, growing down)
0330h .. 034Fh : BSSP (background data stack, growing DOWN)
-----------------+
0350h .. 039Fh : TIB (Terminal Input Buffer)
-----------------+
03A0h .. 03FFh : RPP (return stack)
-----------------+
When no background process is running, a memory dump of the area with SPP
, BSSP
, TIB
, and RPP
can look like this:
HEX 300 F0 DUMP
300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________
310 0 0 0 0 0 0 0 0 0 0 0 0 0 70 0 70 _____________p_p
320 0 0 0 20 0 30 3 27 3 29 0 10 3 20 3 FF ___!_!___)_+_0__ <- SPP
330 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________
340 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________ <- BSPP 16 words
350 33 30 30 20 46 30 20 44 55 4D 50 6A 6A 6A 6A 6A 300 F0 DUMPjjjjj - TIB start 80 bytes
360 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A jjjjjjjjjjjjjjjj
370 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A jjjjjjjjjjjjjjjj
380 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A jjjjjjjjjjjjjjjj
390 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A jjjjjjjjjjjjjjjj <- TIB end
3A0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________ -> RPP
3B0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________
3C0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________
3D0 0 0 0 0 0 0 B 0 A 0 3 81 B 0 3 81 _______________
3E0 0 21 C1 23 33 3 24 0 CF 0 82 6F 8C A7 0 0 A! #0_A%__(_:__o
3F0 95 30 0 C 95 6D 0 0 0 10 89 78 91 8B 91 CD _____|_____x___M <- RPP 96 bytes
Note that the TIB
was filled with j
for better visibility.
On the W1209, the following code brings the background task ticker counter TIM
on the 7S-LED display:
: show BASE @ HEX TIM 7S . BASE ! ;
' show BG !
The following code stops the background routine, and dumps the RAM contents:
0 BG !
HEX 300 F0 DUMP
300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________
310 0 0 0 0 0 0 0 0 0 0 0 0 0 70 0 70 _____________p_p
320 0 0 0 20 0 30 3 27 3 29 0 10 3 20 3 FF ___!_!___)_+_0__
330 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________
340 0 0 0 0 0 70 0 EE 97 6C 0 39 0 60 0 A _____p_n_l_9_`__ <- BSPP used in background
350 48 45 58 20 33 30 30 20 46 30 20 44 55 4D 50 45 HEX 300 F0 DUMPE
360 58 20 54 49 4D 20 37 53 20 2E 20 42 41 53 45 20 X TIM 7S . BASE
370 21 20 3B 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A ! ;jjjjjjjjjjjjj
380 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A jjjjjjjjjjjjjjjj
390 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A 6A jjjjjjjjjjjjjjjj
3A0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________
3B0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________
3C0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ________________
3D0 0 0 0 0 0 0 0 89 9 23 3 24 9 1 3 81 _________#_$___
3E0 0 81 38 23 33 3 24 0 F1 0 82 6F 8C A7 0 0 A! #0_A%__(_:__o
3F0 95 30 0 C 95 6D 0 0 0 10 89 78 91 8B 91 CD _____|_____x___M
Since the Background Task doesn't have a dedicated USR
area, the USR
variable HLD
, used by the output formatting routines, must be saved in the context switch (else if numbers were printed on a 7S-LED display in the background the output of DUMP
would be mangled).
Conclusions:
- for simple background code the
BSPP
area with 16 cells (32 bytes) is sufficient - working without a dedicated
USR
area works for simple tasks - at least the output routines must have a different set of
USR
variables