Zeptoforth Review

Zeptoforth is a LARGE Forth by Travis A Bemann with an emphasis on computing and a welcome addition to the Cortex-M Forth family.

Zeptoforth reuses some Mecrisp-Stellaris code, such as terminal routines (with permission of Matthias Koch) but it is mostly his own source according to Travis.

Supported Hardware

Zeptoforth currently supports the following MCU’s

  • STM32F407

  • STM32F746

  • STM32L476

  • RPi Pico (RP2040)

My review was performed on a STM32F407 Discovery Board using Zeptoforth 0.16.0.

Downloading

Zeptoforth is freely available on Github under the MIT license and may be cloned here.

git clone https://github.com/tabemann/zeptoforth.git

Documentation

zeptoforth/docs contains *.md files which I personally don’t find useful however html files including an index and search facility may be generated by running:

zeptoforth/html/gmake.

The doc looks like the index below, and while it could use a lot more general user info it is actually pretty impressive as regards Dictionary details.

_images/zeptoforth-doc.jpg

Dictionary

Zeptoforth has multitasking, blocks, a block editor, a scheduler, modules and a ton of other stuff. If a Forth is measured by the size and breadth of its Dictionary, Zeptoforth might just be the 100 pound Gorilla of Cortex-M Forths.

Warning

Zeptoforth is not designed to run on Cortex-M0.

Words

Zepoforth is just PACKED with heavy duty Words, and I believe there are more available via the module system.

words
restore-state       init                swd-module          init
rng-module          action-pool-module  task-pool-module    line-enabled?
disable-line        enable-line         init                line-internal-module
ansi-term-module    lock-module         chan-module         fchan-module
tqueue-module       init                free!               resize!
allocate!           x-memory-management-failure             resize
free                allocate            allocate-module     pool-module
see-for-gas         see                 see-xt-for-gas      see-xt
disassemble-for-gas disassemble         disassemble-internal-module
init                led-module          schedule-module     init
ms                  unused              task-unused         forget-ram
task-module         gpio-module         init                int-io-module
ms                  init                systick-module      init
atanh               acosh               asinh               tanh
cosh                sinh                f**                 x-domain-error
acos                asin                atan2               atan
tan                 cos                 sin                 ln
lnp1                exp                 expm1               factorial
sqrt                fi**                2r@                 d*
dmax                dmin                dabs                2rot
4dup                pi                  2find-get-value     find-get-value
2find-value         find-value          hfind-value         bfind-value
2find-get-index     find-get-index      2find-index         find-index
hfind-index         bfind-index         2iter-get           iter-get
2iter               iter                hiter               biter
count+loop          count-loop          while-loop          loop-until
choose              option              cornerstone         marker
interrupt-module    init                unimport            import
end-module          begin-import-module-once                begin-import-module
begin-module-once   begin-module        x-module-not-found
x-module-already-defined                x-order-overflow
x-module-stack-underflow                x-module-stack-overflow
init                flush-console       wake                wake-hook
wait                wait-hook           f.                  (f.)
format-fixed        ud.                 d.                  (ud.)
(d.)                format-double-unsigned                  format-double
#>                  sign                #s                  #
<#                  hold                [then]              [if]
[else]              .\(                 .\"                 s\"
c\"                 esc-string-module   defer@              defer-flash@
defer-ram@          defer!              defer-flash!        defer-ram!
defer               x"                  ;]                  [:
skip-until          dump                unloop              leave
j                   i                   +loop               loop
?do                 do                  wordlist            ram-wordlist
flash-wordlist      safe-type-unsigned  safe-type-integer   safe-type
safe-emit           end-critical        begin-critical      2variable
variable            hvariable           bvariable           aligned-buffer:
buffer:             user-aligned-buffer:                    user-buffer:
2user               user                huser               buser
endcasestr          ofstrcase           ofstr               endcase
endof               of                  case                equal-strings?
2field:             field:              hfield:             bfield:
+field              end-structure       begin-structure     does>
no-word-being-built <builds             create              fill
>name               word-info           words               lookup
execute-defined?    defined?            suppress            triggers
averts              .s                  h.16                h.8
h.4                 h.2                 h.1                 depth
?dup                align               abs                 -rot
max                 min                 inlined-flag        compiled-flag
immediate-flag      visible-flag        tos                 cell+
cells               cell                hex                 decimal
octal               binary              internal-module     forth-module
false               true                init                try
?raise              f/                  f*                  d/
ud/                 d/mod               uf/mod              ud/mod
m/mod               um/mod              u*/mod              u*/
*/mod               */                  udm*                ud*
m*                  um*                 um+                 d-
d+                  dnegate             2arshift            2rshift
2lshift             d0>=                d0<=                d0>
d0<                 d0<>                d0=                 d<=
d>=                 d>                  d<                  du<=
du>=                du>                 du<                 d<>
d=                  2tuck               2nip                2dup
2over               2swap               2drop               format-integer
format-unsigned     reverse             move                u.
.                   debugu.             (u.)                (.)
[char]              char                compile-cstring     c"
s"                  ."                  .(                  word-align,
undefer-lit         commit-flash        end-compress-flash  again
until               repeat              while               begin
then                not-following-if    else                if
compile-to-ram-only not-compiling       token-expected      2constant
constant            ;                   :noname             :
parse-digit         parse-base          parse-unsigned      parse-integer
failed-parse        bel                 nak                 ack
xoff                xon                 refill              rstack-underflow
rstack-overflow     stack-underflow     stack-overflow      display-normal
display-red         quit                abort               evaluate
>body               find-all            find-all-dict       find
3dup                equal-case-strings? to-upper-char       (
\                   token               token-end           token-start
newline?            ws?                 isb                 dsb
warm                reboot              set-order           get-order
set-current         get-current         sp!                 sp@
rp!                 rp@                 rdrop               r@
r>                  >r                  cstring,            ram-align,
flash-align,        align,              2reserve            reserve
hreserve            breserve            2flash-reserve      flash-reserve
hflash-reserve      bflash-reserve      2ram-reserve        ram-reserve
hram-reserve        bram-reserve        2,                  ,
h,                  b,                  2current!           current!
hcurrent!           bcurrent!           2flash,             flash,
hflash,             bflash,             2ram,               ram,
hram,               bram,               2@                  @
h@                  b@                  bit@                hbit@
bbit@               bic!                hbic!               bbic!
bis!                hbis!               bbis!               bit
+!                  h+!                 b+!                 2!
!                   h!                  b!                  unknown-word
recurse             literal             lit,                postpone
[']                 '                   token-word          compile,
compressing-flash   compress-flash      compiling-to-flash? compile-to-flash
compile-to-ram      ]                   [                   visible
inlined             compile-only        immediate           [inlined]
[compile-only]      [immediate]         init                welcome
exit                pause               ?execute            execute
sleep               disable-int         enable-int          key?
key                 count               serial-type         type
cr                  space               emit?               emit
allot               here                flash-latest!       ram-latest!
latest!             flash-latest        ram-latest          latest
flash-here!         flash-allot         flash-here          ram-here!
ram-allot           pad                 ram-here            u>=
u<=                 u>                  u<                  0>=
0<=                 0>                  0<                  0<>
0=                  >=                  <=                  >
<                   <>                  =                   4/
2/                  4*                  2*                  4-
2-                  1-                  4+                  2+
1+                  umod                mod                 u/
/                   *                   -                   +
negate              not                 bic                 xor
or                  and                 arshift             rshift
lshift              tuck                nip                 roll
pick                rot                 over                swap
dup                 drop                license             copyright
kernel-date         kernel-version      kernel-platform     time-4-handler-hook
time-3-handler-hook time-2-handler-hook adc-handler-hook    exti-4-handler-hook
exti-3-handler-hook exti-2-handler-hook exti-1-handler-hook exti-0-handler-hook
systick-handler-hook                    pendsv-handler-hook svcall-handler-hook
null-handler-hook   fault-handler-hook  validate-dict-hook  pause-hook
refill-hook         key?-hook           key-hook            emit?-hook
emit-hook           failed-parse-hook   handle-number-hook  prompt-hook
order               order-count         input-size          input
input#              >in                 sys-ram-dict-base   build-target
source              parse-buffer        parse#              >parse
handler             rstack-end          rstack-base         stack-end
stack-base          flash-end           flash-base          ram-end
ram-base            dict-base           pause-enabled       base
state               init-handlers       flash-block-size    flash-align,
2flash!             flash!              erase-all
flash!-already-written                  hflash!             bflash!

 ok

Installation

“Gmake: was all that was required and Zeptoforth built everything instantly with no errors or warnings. As it is developed on Linux, this is a good sign.

Terminal

Zeptoforth offers two types of Terminal connections

  1. SWDCOM, a high speed client designed by Jan Bramkamp which uses Stlink via the MCU SWD interface over USB. Works for Linux, FreeBSD etc.

  2. USART serial via 3.3v/USB dongle or USB on some boards. No Hardware handshaking provided.

Note

  1. the Serial option requires zeptocom.js or e4thcom due to built in #includes. The former requires Chrome Browser, the latter is Linux only.

Swdcom Terminal

_images/zeptoforth-terminal.jpg

Swdcom connected instantly for the picture above, and is devastatingly fast on Zeptoforth on a 168MHz STM32F407.

Binaries

Zeptoforth creates a zeptoforth.stm32l476.bin in the root directory and a number of binaries for the supported hardware, including previous versions in zeptoforth/bin

.
├── 0.13.0
├── 0.13.1
├── 0.13.2
├── 0.14.0
├── 0.14.1
├── 0.14.2
├── 0.14.3
├── 0.14.4
├── 0.15.0
├── 0.15.1
├── 0.15.2
└── 0.16.0

0.16.0

.
├── stm32f407
├── stm32f746
└── stm32l476

stm32f407

.
├── zeptoforth_big-0.16.0.bin
├── zeptoforth_big-0.16.0.ihex
├── zeptoforth_big_swdcom-0.16.0.bin
├── zeptoforth_big_swdcom-0.16.0.ihex
├── zeptoforth_full-0.16.0.bin
├── zeptoforth_full-0.16.0.ihex
├── zeptoforth_full_swdcom-0.16.0.bin
├── zeptoforth_full_swdcom-0.16.0.ihex
├── zeptoforth_kernel-0.16.0.bin
├── zeptoforth_kernel-0.16.0.elf
├── zeptoforth_kernel-0.16.0.ihex
├── zeptoforth_mini_no_corner-0.16.0.bin
├── zeptoforth_mini_no_corner-0.16.0.ihex
├── zeptoforth_mini_swdcom_no_corner-0.16.0.bin
└── zeptoforth_mini_swdcom_no_corner-0.16.0.ihex

Flashing the binary

I flashed the “zeptoforth_big_swdcom-0.16.0.bin” option above into a stm32f407 Discovery board like this

st-flash erase
st-flash write zeptoforth_big_swdcom-0.16.0.bin 0x08000000

This was fast and painless and gave me the SWDCOM terminal above.

Embedded Debugging

I can’t find much in the way of hardware debugging aids in Zeptoforth other than the usual Forth “@” etc.

STM32F407 hardware

91 peripherals
1540 registers
12347 bitfields

Debugging Example

Assume you want to find if the HSI clock is enabled and running, and if the PLL is on ? You know these bitfields are in the RCC_CR register and so you look up the Reference Manual and find the Bitfield format as below.

_images/rcc_cr-tech-manual.jpg

You want to find out those values RIGHT NOW. Forth is perfect for this, you can just use the interactive terminal to find out. We know that the memory map has already been loaded so the data in the RCC_CR register can easily be found.

RCC_CR @ hex. 00005183  ok

The Reference Manual format is not in Hex so that’s not useful. Lets turn it to binary ?

binary $5183 . 101000110000011  ok.

That’s better, but I don’t like counting bits on the screen because it’s too easy to make a mistake and waste hours/days debugging.

There is another way, but it’s not available on Zeptoforth.

Zeptoforth Quirks

  1. Uses a ‘module’ system which is just a simple way to abstract the Vocabulary use. While required modules are named in source files their source is not (as it doesn’t need to be). The Zeptoforth prebuilt image knows all about these files. However this makes the example sourcecode in the /test directory very hard/impossible to follow if you’re interested in the methods used for I2C for example.

  2. As mentioned, #includes are used in the sourcefiles which is not standard Forth. This requires specialised terminals or uploaders as mentioned above.

Conclusion

I review all the Forths I can find and compare them against my benchmark, Mecrisp-Stellaris, the focal point of this site, as I’m sure you know.

Sadly 99% of the Forths I test are broken, incomplete, abandoned or as fragile as glass. Or written in Java and take 3 days to boot up.

Zeptoforth is SOLID, under constant improvement, and I haven’t broken it in my (limited) testing. I think Zeptoforth, while a bit rough around the edges because of it’s V-0.16.0 age has a very bright future ahead,

Zeptforth Author

Travis Bemann is approachable and friendly, reads English and German and can crank out a new version with bugfixes and improvements in days.

Travis can be reached thru the Zeproforth menus at Github: https://github.com/tabemann/zeptoforth or on IRC: Freenode #mecrisp nick:tabemann EST (USA) time.

Written by Terry, 30 April 2021.