Skip to content

STM8 eForth Compile to Flash

Thomas edited this page Mar 3, 2018 · 26 revisions

Compile to Flash Memory

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.

Forth VARIABLE in NVM mode

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:

  1. start a session by flashing the µC, or by typing COLD or RESET
  2. optionally write some test code in RAM mode, define and use temporary RAM mode helper words, or define startup words
  3. optionally run COLD (or WIPE) to readjust the RAM pool
  4. run NVM to define words or variables in Flash memory
  5. run RAM (or WIPE) to make pointers to the newly defined words, and also VARIABLE RAM allocation, persistent
  6. return to 2. (test your code, preferably automated), to 3. (COLD or WIPE) 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).

Creating Autostart Forth Applications

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.

Technical background of Compile to Flash

Background: eForth and Memory

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 to head (which assumes that the dictionary is in RAM)
  • the eForth interpreter EVAL uses PACK$ through TOKEN just like the compiler. Hereby words from the input stream are copied to the head 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).

Memory in STM8 eForth

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 active CP 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 of VARIABLE, and CREATE, 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 into NVM words.
  • when switching from mode NVM to mode RAM, 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.

Limitations of Compile to Flash

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.

Clone this wiki locally