Skip to content

STM8 eForth Background Task

Thomas edited this page Nov 1, 2017 · 9 revisions

Background Task

TG9541/STM8EF supports a background task, a very simple to use feature for simple INPUT-PROCESS-OUTPUT control tasks that can be developed, and tested, interactively. It is sufficient for most "Ladder Logic" or Arduino-style embedded control applications. A "task word" is called in a 5ms cycle (it shouldn't exceed 4ms runtime else the interactive console gets slowed down or stalls). It provides a complete context switch for the Forth virtual machine. As the design goal never was full multitasking the background task doesn't provide multitasking features like semaphores. Resource sharing with the foreground task (usually the console) must be managed on the application layer (usually through exclusive access). There is a trade-off: no interpreter and parser words that influence the console are allowed in a background task.

Background Task Properties

The background task feature has the following properties:

  • the variable BG holds the address of a background task word
  • fast preemptive context switch of eForth VM with dedicated data stack and pad
  • the background task's own BASE and HLD get initialized with every run
  • ?KEY and EMIT use configurable I/O words (e.g. W1209 uses 7S-LED display and pushbuttons)
  • cyclic increment of 16 bit timer ticker variable TIM

Control tasks are often non-interactive and 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.

Background Task Examples

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:

( W1209 memory monitor - key mapping set:'A', +:'B' , -:'D' )
VARIABLE monaddr  
: inc ( -- ) 1 monaddr +! ;                            \ increment monaddr
: dec ( -- ) -1 monaddr +! ;                           \ decrement monaddr
: cadr ( -- ) DUP 66 = IF inc THEN 68 = IF dec THEN ;  \ react on keys "+" and "-"
: disp ( -- ) monaddr BKEY IF ? ELSE  @ C@ . THEN ;    \ show monaddr while key pressed
: ledmon ( -- ) HEX ?KEY IF cadr ELSE disp THEN ;      \ react on key

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.

Background task PoC

Note: the following technical details about the background task from the PoC are outdated. However, some of the details of the final design are explained here

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 tick. In theory, the data stack could be shared, but in practice determining the SP in an interrupt routine is somewhat tricky (it may be in X, or in XTEMP).

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

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.

There is problem with this lightweight approach: the state of the Forth system is stored in the USR area (0x60 .. 0x7F). As we won't use the outer interpreter, or the compiler in the background task, not all data in USR is relevant. However, formatted output uses some USR variables, and memory in PAD!

Memory layout

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 !

Note that the current PoC doesn't have a dedicated USR area for the background task. As a consequence, when the output formatting routines are used in the background for the 7S-LED display, the output of DUMP will be mangled.

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

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
Clone this wiki locally