Quantcast
Channel: JeeLabs
Viewing all articles
Browse latest Browse all 265

Installing more drivers in flash

$
0
0

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

  • The Mecrisp kernel itself: this is 20 KB of Matthias Koch’s hand-craftedassembly code, turning a µC into a Forth compiler / engine, with over 300 pre-defined “words”.
  • Compiled code, stored in flash memory - these will always be present on power-up.
  • Compiled variables, buffers, and code, stored in RAM - variables will be reset to their initial values on power-up and after each reset, but any code in RAM will be gone.

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:

  • Always - this code really should hardly ever need to be replaced, and may in fact require special tricks to update (on the F103, this is the case for the USB console driver)

  • Board - this code implements drivers for the most important hardware peripherals, such as GPIO, ADC, PWM, I2C, and SPI - it also defines the LED constant (pin PA8 on a JNZ rev4)

  • Core - this part can easily be replaced, depending on what you need for a specific project - as pre-installed, it contains among others, drivers for the RFM69 radio module and the OLED

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

  • jz4/always.fs currently only defines cornerstone, which is used to mark off each section

  • jz4/board.fs defines the pins assigned to LED + RFM69 and adds essential drivers from flib

  • jz4/core.fs is the most interesting part, and can easily be customised. Here is the main code:

    <<<board>>>
    compiletoflash
    include ../flib/spi/rf69.fs
    include ../flib/any/varint.fs
    include ../flib/i2c/ssd1306.fs
    include ../flib/mecrisp/graphics.fs
    include ../flib/mecrisp/multi.fs
    cornerstone <<<core>>>
    

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

  • it’s meant to be uploaded via Folie’s “!s core.fs” command and since it refers to other files by a relative path, this must be sent from a specific directory (more on that below)
  • <<<board>>> is a “cornerstone” defined as a last step in the boards.fs file and calling it erases all definitions from flash which have been defined afterboard.fs was installed
  • since normally core.fs is loaded right after board.fs, the result is that re-sending the core.fs file will erase itself and everything newer, and then save updated definitions
  • as a last step, a cornerstone called <<<core>>> is defined - by calling this later on, you can erase all definitions added afterwards, so this restores flash to a known state

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:

  ok.
!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
 ok.
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.


Viewing all articles
Browse latest Browse all 265

Trending Articles