-
Notifications
You must be signed in to change notification settings - Fork 66
STM8 eForth Compile to Flash
STM8 eForth allows writing applications to Flash memory in a convenient way. This page provides an introduction to the problem, and explains the solution approach.
The following words control memory mode aspects:
Word | Description | Availability |
---|---|---|
NVM |
Switch to Non Volatile Memory mode | Core dictionary |
RAM |
Switch to RAM mode, make the dictionary in Flash permanent |
Core dictionary |
RESET |
Restore original dictionary initialization pointers from shadow pointers(i.e. remove dictionary entries added in NVM mode) |
Core dictionary |
PERSIST |
Copy current dictionary initialization pointers to shadow pointers, including the 'BOOT pointer |
Library (lib/ ) |
WIPE |
Remove temporary dictionary in RAM, switch to mode RAM
|
Alias (target/ ) |
The following listing shows an example session:
RESET ok
: a 2 . ; ok
NVM ok
: b 1 . ; ok
RAM ok
WORDS
a b IRET SAVEC RESET RAM NVM WORDS .S DUMP IMMEDIATE ALLOT VARIABLE CONSTANT CREATE ] : ; OVERT ." AFT REPEAT WHILE ELSE THEN IF AGAIN UNTIL BEGIN NEXT FOR LITERAL C, , ' CR [ \ ( .( ? . U. TYPE SPACE KEY DECIMAL HEX FILL CMOVE HERE +! PICK 0= ABS NEGATE NOT 1+ 1- 2+ 2- 2* 2/ */ */MOD M* * UM* / MOD /MOD M/MOD UM/MOD WITHIN MIN MAX < U< = DNEGATE 2DUP ROT ?DUP BL BASE - 0< OR AND XOR + UM+ I OVER SWAP DUP 2DROP DROP NIP >R R@ R> C! C@ ! @ 2@ 2! EXIT EXECUTE EMIT ?KEY 'BOOT COLD ok
Words in RAM
mode are very useful as a temporary dictionary, e.g. for defining temporary immediate
words, for alias words, or for unit testing.
Note: to retain words compiled in NVM
mode, switching back to RAM
mode is required. Otherwise the the added words won't be linked after COLD
or after a power-cycle, and will be overwritten by new definitions!
Note:: STM8L101 devices (e.g. STM8L101F3, STM8L001J3), unlike other STM8L families, have limited IAP features, and it's not yet clear if they can be supported by STM8 eForth.
In eForth, variables consist of a word in the dictionary, which use the word DOVAR
in the "code field", and at least one RAM cell in the "parameter field". The role of DOVAR is to put the address of the "parameter field" (the first RAM cell after DOVAR
) on the data stack, and then return to the calling word. In NVM
mode the parameter field of a defined word is also in the Flash memory (which makes it a constant).
From STM8EF release v2.2.8 on, there is an extra level of indirection for a VARIABLE in a ROM (in NVM
mode DOVAR
is replaced by DOVAR@
). A managed RAM pool (assigned by WIPE
, e.g. in COLD
) makes the feature transparent to the user (it "just works").
Arrays in RAM are also supported, e.g.:
: CELLS ( n -- 2*n ) 2* ; \ number of Forth Cells to number of bytes
NVM
VARIABLE abc 9 CELLS ALLOT
RAM
abc 10 cells $55 FILL
The word cells
is a helper word for using the CELLS
idiom for address calculations.NVM
changes to Flash mode, VARIABLE
and ALLOT
define the variable word abc
and transparently allocate (1+9)
cells (20 bytes) of RAM storage. RAM
switches back to RAM mode. The array (in RAM) represented by the word abc
is filled bytewise with the value 0xFF. The variable abc
"survives" a reset, but the memory it points to is volatile (RAM).
The following programming workflow is assumed:
- start a session by flashing the µC, or by typing
COLD
orRESET
- optionally write some test code in
RAM
mode, define and use temporaryRAM
mode helper words, or define startup words - optionally run
COLD
(orWIPE
) to readjust the RAM pool - run
NVM
to define words or variables in Flash memory - run
RAM
(orWIPE
) to make pointers to the newly defined words, and alsoVARIABLE
RAM allocation, persistent - return to 2. (test your code, preferably automated), to 3. (
COLD
orWIPE
) to renew the RAM pool, or to 4. (write more persistent code)
If you need to reserve more than 32 bytes of memory, it's advisable to cycle through the steps 3 to 6 before you write to any variable that uses RAM outside the 32 byte pool area.
Requirements, assumptions and technical details of the feature are described in issue #16.
The STM8S also offers EEPROM memory (from 128 bytes up to 2 KiB) which can be used for storing data. There is no EEPROM memory allocation (for parameters in EEPROM automatic allocation is problematic, since in most cases EEPROM addresses need to stay the same when extending a program).
Due to the STM8 IAP feature, in NVM
it's possible to write to random Flash memory addresses. This means that constants (e.g. the startup code vector) can be modified, too!
As an example, the following code snippet demonstrates how to define a new greeting word, and set it as the start word:
NVM
: mystart ( -- )
CR 3 FOR
R@ . NEXT
CR ." Hi!" CR ;
' mystart 'BOOT !
RAM
NVM
unlocks Flash memory IAP write access, and makes here
point to the top of the dictionary in the non-volatile memory. mystart
implements a new start-up word (it counts down from 3 to 0, and prints some text). '
(tick) retrieves the address of the code of mystart
, 'BOOT
gets the address of the startup word pointer, and !
stores the address of the startup word. RAM
than switches back to volatile memory mode, and it thereby stores the dictionary pointers permanently, and locks the Flash memory.
When you run COLD
(or the board is powered-cycled) the new start-up code prints the following:
COLD
3 2 1 0
Hi!
With a startup word it's also possible to initialize an application, e.g. for starting the background task. Here is a very simple example, that shows a running counter on the 3 digit 7S-LED display of a W1209:
NVM
: show ( -- )
TIM U. ;
: startup ( -- )
[ ' show ] LITERAL BG ! CR ;
' startup 'BOOT !
RAM
The example first sets NVM
mode, then defines show
to print the value of the background ticker TIM
(by default, character output like .
in background tasks go to the board UI, e.g. LED display and pushbuttons). The definition of the word startup
uses [
and ]
to change to the interpreter, put the address of show
on the data stack, and insert it into the compiled code with LITERAL
. BG
gets the address of the background task pointer to which the literal value from the stack is stored with !
. RAM
stores pointers to Flash memory, and then changes the operating mode back to RAM. After the next reset, the background tasks starts running before control is given to the user.
The word RESET
provides a reset to default feature for the vocabulary in NVM and RAM. It also resets the 'BOOT
vector and other initialization values.
The original STM8 eForth, like e.g. Forth79, keeps the dictionary (the linked list of compiled Forth words) in RAM. As a STC (Subroutine Threaded Code) Forth, it assumes a von Neumann Architecture (unified data/code memory).
It used to take advantage of keeping the dictionary in RAM in the following the way:
- a dictionary header gets modified after writing the name string of a word, or even after linked for linking (
COMPILE-ONLY
,IMMEDIATE
) - the branch target of "structure words" (e.g.
IF
) is written by the "closing structure word" (e.g.THEN
) to a memory location stored on the stack - eForth
VARIABLE
provides in-line RAM memory in the dictionary (no particular memory management is required) - the
PAD
RAM area for formatted character is defined relative tohead
(which assumes that the dictionary is in RAM) - the eForth interpreter
EVAL
usesPACK$
throughTOKEN
just like the compiler. Hereby words from the input stream are copied to thehead
of the dictionary for address look-up. This is code efficient but it assumes that the dictionary is in RAM
For embedded applications, small µCs have to store the Forth dictionary in ROM memory, and many low-end µCs are apply Harvard architecture, and can't run machine code from RAM (e.g. PIC16, MCS51, or AVR).
The STM8 family applies a modified Harvard architecture: an internal bus bridge simulates a unified address space which makes it behave like a "von Neumann" system, with a minor speed penalty when running code from RAM. Running code from EEPROM isn't possible. Dr. C.H. Ting's original STM8 eForth uses RAM to store user defined words. New Forth words therefore don't "survive" the next a power-cycle.
The STM8 bus bridge makes it possible to write directly to Flash memory, and the IAP (In Application Programming) feature automatically "stalls" the CPU core during write operations (erase is implicit).
The improved STM8 eForth implements a mode for compiling Forth words to Flash memory using the STM8 IAP feature, the NVM
mode. It's possible to switch between the modes RAM
(temporary words) and NVM
(words stored permanently).
As eForth was designed as an easily portable minimal Forth, code for maintaining multiple dictionaries (along with multi-user features) was removed for Dr. C.H. Ting's Firmware Engineering Workshop. The solution here is to add new words in RAM to the head of the list, while words in Flash get inserted just in front of the first word in RAM.
Most of the functionality relies on the words VARIABLE
, ALLOT
, HERE
, CONTEXT
, and OVERT
, which observe the modes NVM
and RAM
, as well as the semantics of compiler and interpreter:
- STM8 eForth has two instances of
CP
(context pointer) for RAM and Flash: the activeCP
is swapped by switching modes - in mode
NVM
,HERE
points to wither RAM or Flash depending on the semantics (compile or interpret) -
CREATE
temporarily switches semantics from interprete to compile, restricting the use ofVARIABLE
, andCREATE
, to interpreter mode (this shouldn't be a problem in small embedded systems) - the dictionary is maintained in a single list, by always linking the dictionary part in Flash back to the part in RAM (RAM words always appear first in a dictionary search)
- the compiler word
CALL,
makes sure that words in RAM can't be compiled intoNVM
words. - when switching from mode
NVM
to modeRAM
, the NVM context pointers are persisted to Flash memory
With the word NVM
the Flash memory is unlocked, and the CP (head of list) is set to NVM in compiler mode (after :
or ]
), and to RAM in interpreter mode after ;
or [
).
Note: when the Flash memory us unlocked, write operations to the Core are possible! While this may be used for patching the Forth core, it can also render it unusable!
The word RAM
stores the pointers to the last word (LAST), and to the head of list to the initialization table in Flash memory. It then changes the head of list pointer to RAM.
One of the problems with this approach is that the number of write cycles to Flash memory is limited. However, the 100 write cycles mentioned in the data sheet only concern the guaranteed 20 years data retention at 55°C ambient temperature. At room temperature, writing 1000 or 10000 times is likely to work for a development device (which can be replaced if it fails). In the future the concept can be extended to using EEPROM for memory cells with frequent write operations.
However, the main "stress factor" has been mitigated: temporary storage for the interpreter is always in RAM, even in NVM
mode. Please note that there are some edge cases where this trade-off may cause problems (e.g. when using ,
in a colon definition). It's recommended to file an issue to discuss workarounds or improvements.