Mecrisp Forth's memory use May 2017
The Blue Pill is a widely available and extremely low-cost STM32F103-based µC board which has an amazing amount of features and plenty of memory onboard: 64K flash and 20K RAM.
In C/C++, the way development works is to run a cross-compiler, as made widely popular with the Arduino IDE. The central development flow is then as follows:
- edit source code on your Win/Mac/Lin laptop, desktop, RasPi, etc
- run the compiler, fix reported compilation errors
- upload the generated binary code to the µC and run it
- rinse and repeat…
Not so with a self-hosted development environment like Mecrisp Forth, where incremental and on-board development is used: instead of building an application from scratch, and uploading it, you grow your application in small steps and in a bottom-up fashion. Each snippet of code is based on words which have already been defined.
Forth reflects this gradual process in the way it allocates memory:
The above layout is for a USB-connected Blue Pill or other similar low-end board, and uses a build of Mecrisp Forth which assumes 64K flash and 20K RAM (more will simply be ignored).
The core Mecrisp Forth kernel with a serial port as console needs 20 KB of flash memory and some 1,100 bytes of RAM memory (mostly for data & return stacks and the input line buffer).
Adding the USB driver increases flash memory use by 5 KB and RAM use by some 2.5 KB.
The convention is to then load g6u/board.fs, which includes a range of utilities from flib:
- mecrisp/calltrace.fs - print a stack trace on unhandled exceptions
- mecrisp/cond.fs - compile-time conditionals (
- mecrisp/hexdump.fs -
hexdump, and a few more
- stm32f1/clock.fs - SysTick handler,
- stm32f1/io.fs - fast GPIO pin primitives,
- pkg/pins64.fs - pin definitions for up to TQFP-64 pinouts
- stm32f1/spi.fs - hardware SPI driver
- stm32f1/i2c.fs - hardware I2C driver
- stm32f1/timer.fs - hardware timer configuration
- stm32f1/pwm.fs - timer-based PWM utilities
- stm32f1/adc.fs - 12-bit ADC driver
- stm32f1/rtc.fs - hardware real time clock driver
As the last step,
board.fs, defines the following words:
hellowill print some identifying information and flash/ram free space use
initoverrides/extends the USB-related init to initialise a few more things
<<<board>>>is a “cornerstone” definition to seal the dictionary (see below)
Next, again by convention, the g6u/core.fs code is loaded - with yet more library definitions:
- i2c/ssd1306.fs - a driver for widely-used 128x64 and 128x32 OLEDs
- mecrisp/graphics.fs - graphics primitves and a 6x8 font
- any/digits.fs - a large 32x64 font, digits only
- mecrisp/multi.fs - the Mecrisp Forth multi-tasker
- any/timed.fs - configurable one-shot and periodic software timers
The above forms a sort of standard base set of words to provide definitions
which are expected to be of general use in many projects. The boundary between
core, and application definitions is quite fuzzy - you can easily set
up your own
core.fs file and use that instead.
At the time of this writing, the above definitions need 12 KB for
core.fs, and just over 1 KB of RAM (mostly for an OLED shadow buffer).
That leaves 17 KB flash and about 16 KB RAM free for application use, as you can
see in this
hello 64 KB <g6u> 32212433 ram/flash: 16792 17408 free ok.
Which brings us to “cornerstones”, the foundation which supports such a nested approach:
A cornerstone is a special word which can be defined at any point.
Calling this word at any time will then erase all definitions added after it.
Cornerstones make it very easy to manage flash memory. Note that running such a
cornerstone causes a software reset, since Mecrisp needs to reinitialise its RAM
to match the new situation. For completeness: there is also a
which erases only the definitions in RAM.
By convention, each major section ends with a cornerstone definition - as follows:
So to re-install an updated
core.fs, you can enter
<<<board>>> and then reload
The USB driver re-defines
eraseflash, so that it stays resident at all
times. Deleting the USB driver would kill the USB connection, i.e. Forth prompt,
which is usually not what you want!
Note that these are all just conventions used in the embello Forth codebase - you are absolutely free to use and name cornerstones in any way you like (or not at all).
Each cornerstone will round up flash memory to the next page size, which is 1 KB on F103 µCs with up to 128 KB flash, and 2 KB above that (the L052 has much smaller 128-byte flash pages).
You can define cornerstones in your app (just add a line “
<<<some-name>>>”), although in practice this is rarely needed. A simpler
approach is to place your word definitions in a separate file once they are
reasonably stable, and then include it in your copy of
By default, Mecrisp Forth compiles new definitions in RAM, growing up from the
bottom of free RAM and using up space which might be needed for application
data. While this is extremely convenient during development, with non-trivial
apps you will eventually run out of RAM. Moving earlier definitions to their own
file and including them in
core.fs solves this problem.
One final aspect of RAM use to keep in mind, is that the end of free RAM is used for variables and buffers compiled to flash memory: the initial value of each variable is of course stored in flash as part of the definition - it gets copied to high RAM during startup (and after a reset).