Bare-metal Forth implementation for RISC-V RV32I core.
Forth was initially designed and created by Charles Moore. Many folks have adapted its ideas and principles to solve their own problems. Moving Forth by Brad Rodriguez is an amazing source of Forth implementation details and tradeoffs. If you are looking for some introductory content surrounding the Forth language in general, I recommend the book Starting Forth by Leo Brodie.
This implementation's general structure is based on Sectorforth by Cesar Blum. He took inspiration from a 1996 Usenet thread wherein folks discussed requirements for a minimal yet fully functional Forth implementation.
The hardware requirements for running DerzForth are minimal and straightforward:
RAM_BASE_ADDR
and RAM_SIZE
)ROM_BASE_ADDR
and ROM_SIZE
)serial_init
, serial_getc
, and serial_putc
)DerzForth has been tested on the following RISC-V development boards:
If you are unfamiliar with virtual environments, I suggest taking a brief moment to learn about them and set one up. The Python docs provide a great tutorial for getting started with virtual environments and packages.
DerzForth is an assembly program based on the Bronzebeard project. Consult Bronzebeard's project page for how to get it all setup (it's pretty easy and works on all major platforms).
Bronzebeard (and a few other tools) can be installed via pip:
pip install -r requirements.txt
Some boards require a USB to UART cable in order to program and/or interact. I recommend the CP2012.
For this board, the only setup necessary is a USB to UART cable.
For this board, the only setup necessary is a USB to UART cable.
For this board, the only setup necessary is a USB to UART cable.
Programming this board requires Segger's J-Link software. These tools work on all major platforms but depend on Java.
As far as cables go, just a single USB to Micro-USB cable is necessary.
With Bronzebeard installed:
bronzebeard -c -i boards/<target_board>/ --include-definitions derzforth.asm
Some boards share a common method of programming and interacting.
Enable boot mode on your given device:
To get a list of available serial ports, run the following command:
python3 -m serial.tools.list_ports
Then, program the device over serial UART:
stm32loader -p <device_port> -ewv bb.out
Here are some examples:
# Windows
stm32loader -p COM3 -ewv bb.out
# macOS
stm32loader -p /dev/cu.usbserial-0001 -ewv bb.out
# Linux
stm32loader -p /dev/ttyUSB0 -ewv bb.out
After converting the output binary to Intel HEX format, Segger J-Link handles the rest:
bin2hex.py --offset 0x20010000 bb.out bb.hex
JLinkExe -device FE310 -if JTAG -speed 4000 -jtagconf -1,-1 -autoconnect 1 scripts/hifive1_rev_b.jlink
After programming, put the device back into normal mode:
The J-Link command from the previous step will automatically reset the chip after programming!
To interact with the device, the same port as above can used with pySerial's builtin terminal:
python3 -m serial.tools.miniterm <device_port> 115200
Here are some examples:
# Windows
python3 -m serial.tools.miniterm COM3 115200
# macOS
python3 -m serial.tools.miniterm /dev/cu.usbserial-0001 115200
# macOS (J-Link Serial over USB)
python3 -m serial.tools.miniterm /dev/cu.usbmodem0009790147671 115200
# Linux
python3 -m serial.tools.miniterm /dev/ttyUSB0 115200
This minimal selection of primitive words is used to bootstrap the Forth system.
Word | Stack Effects | Description |
---|---|---|
: |
( -- ) | Start the definition of a new secondary word |
; |
( -- ) | Finish the definition of a new secondary word |
@ |
( addr -- x ) | Fetch memory contents at addr |
! |
( x addr -- ) | Store x at addr |
sp@ |
( -- sp ) | Get pointer to top of data stack |
rp@ |
( -- rp ) | Get pointer to top of return stack |
0= |
( x -- flag ) | -1 if top of stack is 0, 0 otherwise |
+ |
( x y -- z ) | Sum the two numbers at the top of the stack |
nand |
( x y -- z ) | NAND the two numbers at the top of the stack |
key |
( -- x ) | Read ASCII character from serial input |
emit |
( x -- ) | Write ASCII character to serial output |