README.md (3722 bytes)
1 # forthlisp-stm32 2 3 A Forth kernel for the STM32F446 (Cortex-M4F, 128 KiB RAM, 512 KiB flash), written from 4 scratch: a Thumb-2 assembler in SBCL builds it, and the shared Scheme from 5 [forthlisp](https://github.com/equwal/forthlisp) runs on it. No third-party Forth. 6 7 ``` 8 4 LISP the Scheme (forthlisp core: lisp.fs, prims.lisp, prelude.scm), loaded through 9 the kernel's console 10 3 FORTH kernel.lisp + kernel.fs: indirect-threaded Forth. Primitives are Thumb-2 11 templates; the outer interpreter is Forth, metacompiled by the host 12 2 SBCL asm.lisp: Thumb-2 + FPv4-SP encoders on SBCL's sb-assem segments and labels, 13 with its own fixup pass; kernel.lisp lays out headers and writes the image 14 1 ASM Thumb-2 / ARMv7-M, FPv4-SP single-precision floats 15 ``` 16 17 ## Quickstart 18 19 ```sh 20 git clone https://github.com/equwal/forthlisp # the core, next to this repo 21 git clone https://github.com/equwal/forthlisp-stm32 22 cd forthlisp-stm32 23 ./build-firmware.sh # SBCL writes kernel-f446.bin (about 10 KiB) 24 ./stm32-forth # the kernel's own prompt: 1 2 + . -> 3 25 ./stm32-lisp # Scheme: (+ 1 2) -> 3 26 ./test.sh # all layers; SUITE=1 also runs the R7RS suite (about 10 minutes) 27 ``` 28 29 Needs SBCL, `qemu-system-arm` (machine `netduinoplus2`), `arm-none-eabi-as` and 30 `arm-none-eabi-objcopy`, and `nc`. Override the tools with `SBCL`, `QEMU`, `ARM_AS`, 31 `ARM_OBJCOPY`, `NC`, `STTY`. `PORT` picks the TCP port of the emulated console. 32 33 A real board takes the image with `st-flash write kernel-f446.bin 0x8000000`. 34 35 ## Board and emulation 36 37 - QEMU has no F446 board. `netduinoplus2` (STM32F405) is the closest. The kernel uses only 38 what the F446 has: RAM ends at `0x20020000`, and the image fits in 512 KiB of flash. 39 - The console is USART2 at 115200 baud from the 16 MHz HSI. It is QEMU's second `-serial`. 40 - QEMU breaks on writes to the last 32 bytes of RAM, so the stacks stop short of them. 41 - QEMU does not model the F4 flash controller, so words compile to RAM. 42 43 ## Memory map and registers 44 45 - Flash `0x08000000`: vector table, primitives, kernel headers and threads. 46 - RAM `0x20000000`: system variables, the 512-byte input buffer, then the dictionary. 47 - Data stack: 4 KiB below `0x2001EFE0`. Return stack (`sp`): 4 KiB below `0x2001FFE0`. 48 - r4 = IP, r5 = data stack pointer, r6 = top of stack, r7 = W. 49 NEXT is `ldr r7,[r4],#4; ldr r0,[r7]; bx r0`. 50 51 ## Tests, in layer order 52 53 | Layer | Test | Result file | At publication | 54 |---|---|---|---| 55 | 1-2 | `asm-test.lisp`: every encoder form byte-identical to GNU as | `asm-tests.txt` | 105/105 | 56 | 3 | `forth-tests.txt`: kernel words over the serial console | `kernel-tests.txt` | 71/71 | 57 | 4 | the core's `r7rs-tests.scm` | core `conformance.txt` | 390/390 | 58 | 4 | chibi-scheme's R7RS suite | core `tests/r7rs/results.txt` | 603 pass / 140 fail / 389 skip | 59 60 ## Limits 61 62 - After the Scheme loads, the cons heap is 60 KiB, the blob heap 12 KiB and symbol names 63 10 KiB, with 5.9 KiB of dictionary free. 64 - Floats are single precision. 65 - A runtime `flash!` is not written yet, because QEMU cannot test it. 66 67 ## Influences 68 69 - Paul Khuong, "SBCL: the ultimate assembly code breadboard" 70 (https://pvk.ca/Blog/2014/03/15/sbcl-the-ultimate-assembly-code-breadboard/) 71 - Ron Garret, gll-mag-patch (https://github.com/rongarret/gll-mag-patch) 72 - Ron Garret, "Lisping at JPL" (https://flownet.com/gat/jpl-lisp.html) 73 74 ## Licence 75 76 MIT (see `LICENSE`). 77 78 ## CI 79 80 `ci-local.sh` (in the forthlisp core repo) runs every test layer of all three repos in a 81 clean `ubuntu:24.04` container. It runs after each publish, without a hosted CI service. 82 The last result is on https://dickt.store/forthlisp/.