Skip to content
tabemann edited this page Sep 20, 2024 · 16 revisions

About zeptoforth

zeptoforth is an inlining native-code Forth implementation for ARM Cortex-M microcontrollers which includes an RTOS and other batteries-added features. It is targeted at Cortex-M MCU's with larger flash sizes, due to the size of its included features and its heavy reliance upon inlining. It can compile to and execute from both flash and RAM; note that when compiling to flash flash is written immediately, no "save" operation is needed. It is highly modular, making use of a module system which enables easy namespace management so one does not have to worry about namespace pollution when developing code for it. Also, it is not difficult to put together custom builds that include just the subset of features one needs, e.g. to use less flash space.

zeptoforth is designed in particular as a priority-scheduled preemptive-multitasking and multiprocessing (on the Raspberry Pi Pico) Forth and provides a range of features to support this, including task notifications, semaphores, locks (with priority inversion resolution), queue channels, rendezvous channels, bidirectional channels (like rendezvous channels, but designed for sending a response to a given message), byte streams (like queue channels, but optimized for transferring arbitrary sequences of bytes), and interrupt service routine-safe channels (aka "simple channels" or "schannels" for short; like queue channels, but designed for being able to be used for sending messages to and from interrupt service routines). It also provides a purely round-robin "action scheduler" that enables lightweight asynchronous operation (without separate data and return stacks for each "action") with support for high-speed synchronous messaging between "actions" within a single task (as such it may only use a single core; also note that actions may only interact via messages with other actions within the same schedule). Using the action scheduler for multiple actions within a single task is highly recommended if maximum performance is needed, priority scheduling is not needed, blocking on anything other than message sending or receipt or fixed-length delays is not needed, multiprocessing is not needed, if separate data and return stacks are not needed, and/or if one has a large number of asynchronous elements (due to tasks being significantly heavier-weight than actions).

The philosophy of zeptoforth with regard to standardization is that ANS Forth and Forth 2012 code should be easy to port to it but it does not try hard too follow any particular standard. For instance, it supports and makes extensive use of quotations (via [: and ;]), it has a somewhat different exception model (where execution tokens that print out an error message if executed, e.g. if uncaught, are used as exceptions rather than fixed numeric values; this is emphasized by the use of try rather than catch and ?raise, along triggers and averts, rather than throw), it relies heavily upon a novel module system built on top of wordlists, it uses [immediate] and [compile-only] used within words rather than immediate and compile-only placed after words (immediate and compile-only do exist, but they are used for writing words for constructing other words), a heap allocator that takes a heap as an argument to allocate, free, and resize, rather than assuming a single global heap and which raises an exception on allocation failure, and it uses not rather than invert (as invert is an unfortunate ANS-ism).

API documentation is available on GitHub and in the docs directory on the build tarballs (which is the same as the html directory in git and in the source tarballs available on GitHub generated from that, which is built from the API documentation in Markdown format in the docs directory in git).

Included Features

zeptoforth includes the following features:

  • A priority-scheduled preemptive multitasker
  • Semaphores
  • Locks, with priority inversion handling
  • Message-oriented queue channels
  • Message-oriented rendezvous channels, aka "fchannels"
  • Message-oriented bidirectional synchronous reply channels, aka "rchannels"
  • Byte-oriented streams
  • Software alarms
  • Interrupt service handler-safe channels, aka "schannels"
  • Task notifications
  • Console redirection
  • Implicit compilation, i.e. automatically compiling temporary anonymous words from the REPL
  • Action scheduler support
  • Multicore support (on the RP2040 and the RP2350)
  • Double cell and S32.31 fixed-point numeric support
  • Lambda expressions
  • values and lexically scoped local variables
  • Closures
  • Object orientation
  • A disassembler
  • SysTick support
  • User-reconfigurable processor exception vector support
  • Interrupt-driven serial IO
  • Optional USB CDC console IO (on the RP2040 and the RP2350)
  • Rebooting via control-C on the console
  • GPIO support (including EXTI support on STM32 microcontrollers)
  • General UART support (in addition to serial console support)
  • One-shot ADC support
  • SPI (both master and slave) support
  • I2C (both master and slave) support (on the RP2040 and RP2350)
  • PWM support (on the RP2040 and RP2350)
  • Hardware timer support (on the RP2040 and RP2350)
  • RTC support (on the RP2040) and RTC emulation using the Always-On Timer (on the RP2350)
  • Always-On Timer support (on the RP2350)
  • Maps, including counted string and integer-keyed maps
  • Heap allocators
  • Memory pool allocators
  • Task pool allocators
  • Action pool allocators
  • Temporary storage allocators (used to provide immediate string constants)
  • A line editor
  • LED drivers
  • SDHC/SDXC card support using SPI
  • FAT32 support on SDHC/SDXC cards
  • Support for code loading from files in FAT32 filesystems
  • User-level FAT32 tools
  • Best-effort fault recovery
  • Quad SPI flash storage support (on the STM32F746 DISCOVERY board, the RP2040, and the RP2350)
  • A block editor (on the STM32F746 DISCOVERY board, the RP2040, and the RP2350)
  • Random number generator support (except on the STM32F411 "Black Pill" and STM32F411 Nucleo 64 boards)
  • Pseudorandom number generator support (using the TinyMT32 PRNG)
  • Programmable input/output support (on the RP2040 and the RP2350)
  • Optional swdcom support
  • Optional task monitor

There is also support for loadable extras not included in any builds:

  • Single-cell S15.16 fixed-point numerics support
  • A profiler
  • An IPv4 stack for the Raspberry Pi Pico W (aka 'zeptoIP'); for more info consult BUILDING_AND_USING_ZEPTOIP.md.
  • A text editor for use with files in FAT32 filesystems (aka 'zeptoed'); for more information consult docs/extra/zeptoed.md.
  • An SNTP (Simple Network Time Protocol) implementation for use with zeptoIP
  • Bitmaps (in extra/common/bitmap.fs)
  • 16-bit pixmaps (in extra/common/pixmap16.fs)
  • SSD1306-based displays (in extra/common/ssd1306.fs)
  • ST7735S-based displays (in extra/common/st7735s.fs)
  • Monospace, bitmap fonts (in extra/common/font.fs)
  • A simple monospace, bitmap ASCII font (in extra/common/simple_font.fs)
  • Turtle graphics for ST7735S displays (in extra/rp2040/turtle.fs)
  • Neopixel support (in extra/rp2040/neopixel.fs, for the RP2040)

Supported Hardware

It currently supports the following boards:

  • Raspberry Pi Pico and other RP2040-based boards using 25Q-based Quad SPI flash
  • Raspberry Pi Pico 2, Pimoroni Pico Plus 2, and other RP2350-based boards using 25Q-based Quad SPI flash
  • STM32F407 DISCOVERY
  • STM32L476 DISCOVERY
  • STM32F746 DISCOVERY
  • STM32F411 "Black Pill"

Note that the led module likely will not work on RP2040-based boards other than the Raspberry Pi Pico and RP2350-based boards other than the Raspberry Pi Pico 2 and the Pimoroni Pico Plus 2 by default. By creating a constant platform-xiao on the SeeedStudio XIAO RP2040 or platform-wio on the SeeedStudio WIO RP2040 set to true, the led module can be used on these boards. For other RP2040 boards, using the pin module to manually control GPIO's for LED's may be necessary, but on the Raspberry Pi Pico W more elaborate means requiring the use of the CYW43439 firmware and driver is necessary to control the LED.

It is highly recommended to acquire a Raspberry Pi Pico if one wants to try zeptoforth out, as these are inexpensive and easy to acquire, and zeptoforth has extensive support for RP2040-based boards.

If one is willing to risk trying out bleeding-edge hardware, one may want to try out the Raspberry Pi Pico 2 or the Pimoroni Pico Plus 2, which are more capable than RP2040-based boards. However, be aware that there is a significant silicon bug in the bank 0 GPIO pads on the A2 spin of the RP2350, where input-enabled GPIO's get stuck high after receiving a high input if not pulled low again, e.g. by an external pull-down resistor or a fully-driven bus, and the internal pull-down resistors are inadequate to do this; note that toggling Input Enable clears this condition, but in many use cases is not feasible.

Feel free to recommend ports to other ARM Cortex-M-based hardware; mailing the developer an example of the hardware will help to bring about such a port. Writing your own port is also welcome, but a hardware example is needed for binaries to be included in official builds as building zeptoforth is reliant upon having the physical hardware for a given platform on hand.

Gotchas

Some important gotchas include that variables and values compiled to flash and users (which are always compiled to flash) must not be accessed (even though references to them can be compiled into code) without rebooting beforehand. This because the addresses in RAM which they reference either are uninitialized or are taken up by other things before rebooting, so they will contain either garbage data, in the very least, and writing to them will likely corrupt data already present.

One should be very careful about ensuring data alignment when writing code for zeptoforth. While misalignment normally only makes code slower on Cortex-M4, Cortex-M7, or Cortex-M33 MCU's such as the RP2350, STM32F407, STM32F411, STM32F746, or STM32L476, it will readily cause hardfaults on Cortex-M0+ MCU's such as the RP2040. Furthermore, misaligned data, e.g. a buffer of size 6, allocated in the main task's dictionary on bootup will cause Cortex-M0+ MCU's, i.e. the RP2040, to reliably crash before the REPL can come up, and this can only be recovered from by re-flashing one's board with a good image.