Installing more drivers in flash Feb 2017

An application written in Mecrisp Forth consists of a number of different parts:

As delivered, the JeeNode Zero rev4 comes with ≈ 23 KB of compiled Forth code pre-installed in flash memory, leaving ≈ 21 KB of flash for additional code, as you can see in the greeting:

Mecrisp-Stellaris RA 2.3.3 with M0 core for STM32L053C8 by Matthias Koch
64 KB <jz4> 3B5E0728 ram/flash: 4960 21248 free ok.

Forth compiles code to a “dictionary”, which is essentially a growing stack of word definitions. There is a dictionary in flash, and there’s a second one in RAM. Conceptually, words defined later override earlier definitions, and appear later in the dictionary. Words defined in RAM override words defined in flash, i.e. lookup starts in RAM, and continues in flash if not found.

It’s all very clean, but there is a small gotcha:

compiletoflash  ok.
: a ." flash!" ;  ok.
compiletoram  ok.
: a ." ram!" ; Redefine a.  ok.
compiletoflash  ok.
a flash! ok.
compiletoram  ok.
a ram! ok.
forgetram  ok.
a flash! ok.

So while compiling to flash, the words in RAM are not visible! The reason for this is that words in flash can’t refer to words in RAM, as these would be gone on the next power cycle or reset. Note that you can refer to code in RAM via variables, and run them from flash using execute.

Development is very different from development in C or C++, because in Forth everything happens on the µC itself. This leads to a different way of structuring code - it helps to make a clear distinction between: on the one hand drivers and other relatively stable code, and on the other hand code which is currently being written and debugged.

A really effective way to develop code in Mecrisp Forth is to load all stable code in flash, and to keep all work-in-progress code in RAM. That way, a simple reset always restores the µC to a clearly defined state - no matter how bad the bugs are and no matter how many hardware settings the new code might have messed up.

Which is exactly why the JeeNode comes with a fair amount of pre-installed code. You won’t have to install anything to try out the ADC, PWM, I2C, SPI, or the RF69 wireless radio driver - they are all available out of the box. Even basic OLED support and graphical and text display primitives plus a small font are pre-installed.

This burnt in code is split into three sections:

On a JNZ rev4, each section is defined by a source file, which in turn includes everything else:

The logic of the core.fs file is as follows:

Since it’s the last word defined in the above sections, you can type “<<<core>>>” whenever you want to reset flash memory to that “standard” state. (note that cornerstones always end with a software reset, so this also wipes out anything in RAM).

Cornerstones provide a nice mechanism to manage flash memory - they act as markers to erase all newer definitions after their own position in the dictionary. Note that cornerstones can only be used in flash memory. For clearing all RAM definitions, there is forgetram.

To install a customised version of core.fs on a JeeNode Zero:

  1. Get a copy of the embello repository on GitHub, either as download from the home page or (preferably) as a git clone, which makes it much easier to track changes.

  2. Go to the directory with the relevant files, i.e. “cd explore/1608-forth/jz4/” and make changes to core.fs - you may have to re-order includes in case of dependencies.

  3. Launch Folie (add “-r” option when not using a SerPlus) and enter “!s core.fs” - you should see a number of messages, as flash gets erased and all the included files are sent.

Here is a transcript of the send process, omitting most of the “Erase” lines for brevity:

!s core.fs
1> core.fs 3: <<<board>>>

Erase block at  00008700  from Flash
Erase block at  00008780  from Flash
Erase block at  00008800  from Flash
Finished. Reset 
Mecrisp-Stellaris RA 2.3.3 with M0 core for STM32L053C8 by Matthias Koch
64 KB <jz4> 3B5E0728 ram/flash: 6804 30976 free ok.
1> core.fs 4: cr compiletoflash
1> core.fs 5: ( core start: ) 00008700  ok.
1> core.fs 13: ( core end, size: ) 0000ACA8 9640  ok.

The first effect is that the flash dictionary will be reset, releasing all the memory used by the previous version of core.fs and whatever might have been added to flash later on. The second effect is that a fresh core.fs configuration will be compiled and saved in its place.

By installing new drivers and dropping the ones you don’t need, each JeeNode Zero can be configured exactly as required, but keep in mind that even with a standard core.fs setup, modified drivers can also be loaded in RAM or added to flash, superseding prior definitions.

Redefining a word to supersede the previous version is common practice in Forth (you’ll get a “Redefine” message whenever this happens), but beware that older definitions will continue to refer to the original code (“early binding”). Here is an example to illustrate that behaviour:

: a 123 . ;  ok.
a 123  ok.
: b a a ;  ok.
b 123 123  ok.
: a 456 . ; Redefine a.  ok.
b 123 123  ok.
: b a a ; Redefine b.  ok.
b 456 456  ok.

If you keep this behaviour in mind, there’s usually less need to erase and reload code in flash. Instead, you can simply load that code again in RAM, or append it to flash. Once you run out of free memory, that’s of course a good reason to do a full erase/re-flash as described earlier.

These same approaches can be used for board.fs, but don’t forget to reload core.fs as well.

Weblog © Jean-Claude Wippler. Generated by Hugo.