Starting Forth on an STM32F1 Feb 2016
Here is what we’re after, as Forth Development Environment (would that be an “FDE”?):
- getting the Mecrisp core “flashed” onto the microcontroller
- setting up a convenient connection between your laptop and the µC board
- streamlining the iterative coding cycle as much as possible
Note that this is in some way quite similar to hooking up an Arduino or JeeNode, and developing software for it through the Arduino IDE. But there also some substantial differences:
- pick your own editor, “whatever works for you” is by far the best choice
- no compilers, no debuggers, no toolchain - just a simple way to talk to the µC
- no binary code, no runtime libraries, just you, your code, and your terminal
The Arduino approach puts all complexity in the “host” laptop setup. The Mecrisp approach builds words in the µC, on the fly, when they’re typed in (or uploaded, i.e. “simulated typing”).
Step 1) is not Mecrisp-specific. It’s the same stumbing block with every µC setup which needs specific firmware. You need to download the latest Mecrisp-Stellaris release from SourceForge, and “get it onto that darn chip… somehow” !
Here are some ways to do this, depending on what interface tools you have and your O/S:
- via a serial interface, an ST-Link, or a Black Magic Probe - see this weblog article
- using an Arduino Sketch - see “Poor-man’s boot loader upload” in that same article
The firmware in the Mecrisp distribution is available in two versions, a
.bin” and a “
It depends on the upload mechanism as to which one you need. With a Black Magic
Probe (BMP) and
arm-none-eabi-gdb, for example, the following commands should
do the trick:
% arm-none-eabi-gdb [...] (gdb) tar ext /dev/cu.usbmodemD5D1AAB1 (adjust as needed, of course) (gdb) mon swdp (gdb) at 1 (gdb) mon erase (essential for Mecrisp!) (gdb) load mecrisp-stellaris-stm32f103.hex 0x08000000 (gdb) q
Then, again if you are using a BMP and running on Mac OSX or Linux:
% screen /dev/cu.usbmodemD5D1AAB3 115200 Mecrisp-Stellaris 2.2.1a for STM32F103 by Matthias Koch ok. (quit with "ctrl-a ctrl-\" or "ctrl-a \" - depending on your setup)
The serial connection must be set up as 115200 Baud for Mecrisp - 8 bits, no parity, 1 stop bit.
If you’re using an ST-Link to upload the firmware, these two commands will do the trick:
st-flash erase # essential for Mecrisp! st-flash write mecrisp-stellaris-stm32f103.bin 0x08000000
It’s very simple and quick, but only · a f t e r · you’ve got all those Pesky Little Details just right. Getting firmware onto a bare STM32F103 µC can still be a hit-and-miss affair. There are simply too many variables involved to come up with a procedure here which will work for everyone.
The good news is that with a little care, you will not have to repeat this step again. Mecrisp is quite good at keeping itself intact (it refuses to re-flash itself, for example).
One of the things you’ll notice if you try out the above setup with
that it doesn’t quite get the line endings right (which are bare LFs in Mecrisp,
not CR+LF). It’s better to install a slightly more elaborate terminal emulator -
and PicoCom is in fact a very good
option for Mac OSX and Linux, as will become clear below. For Windows, there is
To install PicoCom on Mac OSX with Homebrew, enter this in a command shell:
brew install picocom
To install PicoCom on Debian/Raspbian/Ubuntu Linux, type:
sudo apt-get install picocom
The benefit of PicoCom is that it allows specifying a program to use for
uploads. We don’t want to manually enter text, we also need to send entire
source files to Mecrisp Forth over serial. The problem is that a bare Mecrisp
installation only supports polled serial I/O without handshake. This can only
handle text if it’s not coming in “too fast”. In
Mecrisp, each word on a line needs to be looked up and compiled, and it all
happens on a line-by-line basis. This means that you have to wait for its
ok.” prompt after each line, before sending more text.
One solution is to send all text · v e r y · s l o w l y · but that’ll make it extremely time-consuming.
A better solution is to send full speed and wait for that final prompt before sending the next line, to avoid input characters getting lost. This little utility has been created to do just that: msend.
If you have Go installed, getting msend (Mac OSX and Linux only, for now) is again a one-liner:
go get git.jeelabs.org/jcw/embello/tools/msend
Otherwise, you can get the latest binary release for a few platforms from GitHub.
With “msend” installed, PicoCom can now be started up as follows:
picocom -b 115200 --imap lfcrlf -s msend /dev/cu.usbmodemD5D1AAB3
Or even as “
mcom /dev/cu.usbmodemD5D1AAB3” - if you add an alias to your
.bashrc init file:
alias mcom='picocom -b 115200 --imap lfcrlf -s msend'
And now line endings not only work properly, you also get a very effective upload facility. This will be worth its own article, but you can see a transcript of an upload with includes over here.
Sending a file with PicoCom is triggered by typing “
To quit PicoCom, type “
ctrl-a ctrl-x” - see also the manual
page for further details.
Neither PicoCom nor msend are available for Windows, but there’s another solution:
- install TeraTerm, which is a terminal emulator for Windows
- look at this script file for TeraTerm, by Jean Jonethal
This combination should accomplish more or less the same as picocom + msend, i.e. terminal access, throttling text sends, and inserting “include” files.
Forth software development is about flow and insanely fast turnaround times between coming up with an idea and trying it out. There are no compilers or other tools to slow you down, and as a result you can type and try out an idea the moment it pops into your head. Total interactivity!
At the same time, the last thing we want, is to constantly re-enter code, let alone lose it for good if the µC crashes. The challenge is to find a proper balance between volatile commands (typed in directly at the Mecrisp prompt, on the µC) and re-sending lots of text from a laptop all the time.
Mecrisp has an elegant and simple approach to help with this:
- when you power it up, Mecrisp remembers only what it had stored in flash memory
- all new definitions (i.e. “
: myword ... ;”) are added and compiled into RAM
- stack underflows (a common mistake) clear the stack but won’t lose RAM
- a reset (whether in hardware or using the “
reset” word) will lose everything in RAM
- you can save your next definitions to flash memory by typing
- this will continue until you press reset or enter “
The thing is that in Mecrisp Forth, a hard crash is no big deal - you should expect to run into stuck code, awful crashes, weird things happening, non-responsive terminal I/O, etc. There’s a reset button on the µC which will get you back to a working state the (sub-) second you use it.
It could be a typo. There could be a hint in what’s on the screen. But even if not, if you make your coding cycles short and frequent, then chances are that you’ll quickly discover what went wrong.
Otherwise… the interactive Forth prompt is your friend: examine the values of variables, or the settings in hardware registers, and invent whatever words you need to help figure out this issue. Words can be one-liners, written only for use in the next few minutes of your investigation!
The more loosely coupled your words are, i.e. called in sequence, not nested
too deeply, the easier it will be to set up the stack and call any one of them,
in isolation, from the prompt. If something fails, you can take over and repeat
the rest of the words by hand, verifying that the stack is as expected (check
out the “
.s” word!), and peeking around to see what’s going on.
Looking at the diagram above, you’ll see that there are two kinds of permanence in this context: source code in files, and words defined in flash memory. The latter cannot easily be turned back into source, alas. That means they should be either one-offs or created by an earlier upload.
Although the best workflow has yet to be found, some comments on what is likely to work well:
- new code, especially when it’s about getting the hardware interface right, needs to run on the µC and can be quickly explored ad-hoc - at the Forth prompt, no definitions needed
- you can read / write to registers with “
io@” / “
io!” commands in a “peek and poke” style
- lengthy setup code can be written in your editor, and then uploaded and saved to flash
- hardware addresses are a lot easier to use as pre-defined Forth words (i.e.
- if you make uploaded code store itself in flash, you won’t have to re-upload it after a reset
- the “
cornerstone” word can partially unwind definitions from flash - great for uploads
- make sure your terminal window keeps a lot of history - it’s a very effective historical log
Maybe the rlwrap tool can be made to work with PicoCom - for command history and editing.
There is a lot more to say about this. The “msend” utility recognizes lines
starting with the word “
include” as special requests to fetch a file from
the system and send its contents (this can be nested). This allows keeping
various word sets in their own files, and then selectively include them
in each project. You can add “
to save the more stable words in flash.
For more ideas on how to organise the code, see the README in the Embello area on GitHub.
There is no need for large nested source file trees. Forth source code tends to be very compact - a single page of code is usually more than enough to implement a complete well-defined module. One directory with a few dozen files is plenty. Put them under source code control in GitHub or elsewhere, and you’ll have your entire project under control for the long-term. Each project can contain all the files it needs to be re-created (i.e. re-uploaded to a µC running Mecrisp Forth).
Enough for now, this’ll get you started. Now go Forth, and create lots of embedded µC projects!