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.
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
SWDCOM, a high speed client designed by Jan Bramkamp which uses Stlink via the MCU SWD interface over USB. Works for Linux, FreeBSD etc.
USART serial via 3.3v/USB dongle or USB on some boards. No Hardware handshaking provided.
Note
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¶
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.
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¶
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.
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,