Skip to content

Commit 0061c81

Browse files
committed
Update readme with threading models and optimizations.
1 parent d5baff2 commit 0061c81

File tree

2 files changed

+70
-8
lines changed

2 files changed

+70
-8
lines changed

README.md

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)
44

5-
ulp-forth is a Forth interpreter and cross compiler for the
5+
ulp-forth is a Forth interpreter and optimizing cross compiler for the
66
ESP32 ULP coprocessor. It has most of the Forth 2020 standard
77
implemented. Code is interpreted on the host machine, when
88
that is finished it compiles the `main` word for execution by the
@@ -30,20 +30,62 @@ Copyright 2024 Blake Felt [email protected]
3030

3131
# Contents
3232
* [Building ulp-forth](#building-ulp-forth)
33+
* [Using ulp-forth](#using-ulp-forth)
3334
* [Assembly words](#assembly-words)
35+
* [System words](#system-words)
3436
* [Clock words](#clock-words)
3537
* [GPIO words](#gpio-words)
3638
* [Serial words](#serial-words)
3739
* [I2C words](#i2c-words)
3840
* [Standard Core words](#standard-core-words)
3941
* [Standard Core Extension words](#standard-core-extension-words)
4042
* [Standard Double words](#standard-double-words)
43+
# [Optimizations](#optimizations)
4144

4245
# Building ulp-forth
4346

4447
The compiler can be built with `go build`. Unit tests are run on the
4548
host and on a ulp emulator, they can be run with `go test ./...`.
4649

50+
# Using ulp-forth
51+
52+
Help running the program can be found with `ulp-forth --help`.
53+
54+
## Running the interpreter
55+
56+
The interpreter can be run with `ulp-forth run`. This can be used for testing logic, but only runs on the host so cannot be used for testing hardware. Type `bye` to exit.
57+
58+
## Running the compiler
59+
60+
The cross compiler can be run with `ulp-forth build`. The user should pass in the list of files to be built, which will be interpreted in order before cross compiling the `MAIN` word. So `ulp-forth build first.f second.f` will first interpret first.f, then second.f, then cross compile the `MAIN` word.
61+
62+
## Compiler flags
63+
64+
* `--assembly` Output assembly that can be compiled by the main assemblers, set the --reserved flag before using.
65+
* `--custom_assembly` Output assembly only for use by ulp-asm, another project by this author.
66+
67+
* `--output` Name of the output file.
68+
* `--reserved` Number of reserved bytes for the ULP, for use with --assembly flag (default 8176). Note that the Espressif linker has a bug so has 12 less total bytes. Any space not used by code or data is used for the stacks.
69+
* `--subroutine` Use the subroutine threading model, see the [threading models](#threading-models) section.
70+
71+
# Threading models
72+
73+
There are two threading models for the output ULP code. This is forth definition of "threading" and is not the same as multithreading in other languages. It can be thought of as the execution environment.
74+
75+
Token threading is usually smaller and subroutine threading is usually bigger, but this can vary based on the program and optimizations.
76+
77+
## Token threading (default)
78+
79+
This is the default threading model. This uses a lightweight virtual machine to execute all forth words. This allows for some very compact code, but there is a speed penalty for the virtual machine.
80+
81+
Code using this is roughly 20% smaller than subroutine threaded code.
82+
83+
## Subroutine threading
84+
85+
This can be enabled with the `--subroutine` flag. It copmiles all forth words into assembly subroutines. This is very fast while executing, but there is a size penalty.
86+
87+
Code using this is roughly 20% faster than token threaded code.
88+
4789
# Assembly words
4890

4991
A few words are provided to make ULP assembly without extending
@@ -105,6 +147,12 @@ Skip leading spaces. Parse `name` delimited by a space. Create an
105147
assembly definition for `name` that writes to RTC address `addr0`
106148
followed by address `addr1`.
107149

150+
## System words
151+
152+
`HALT` halts execution of the ULP. Execution will resume at the instruction immediately following the HALT on both token and subroutine threaded models.
153+
154+
`MUTEX.TAKE` and `MUTEX.GIVE` takes and gives the software mutex respectively. The example project includes esp32 code to use this but a better way to use it needs to be written.
155+
108156
# Clock words
109157

110158
The ULP has access to the RTC_SLOW clock. ulp-forth also has
@@ -388,8 +436,6 @@ error if it cannot be cannot be cross compiled.
388436
* Can only run on host.
389437
* `COUNT`
390438
* `CR`
391-
* `CREATE`
392-
* Can only run on host.
393439
* `DECIMAL`
394440
* `DEPTH`
395441
* `DO`
@@ -491,6 +537,7 @@ all can be in the future.
491537
* `2LITERAL`
492538
* `2VARIABLE`
493539
* `D-`
540+
* `D+`
494541
* `D0<`
495542
* `D0=`
496543
* `D>S`
@@ -499,3 +546,18 @@ all can be in the future.
499546
* `DMIN`
500547
* `DNEGATE`
501548
* `DU<`
549+
550+
# Optimizations
551+
552+
The cross compiler includes some optimizations. More may be added later.
553+
554+
* Inline deferred words. If a word is defined by `DEFER` but the deferred word cannot be changed by the cross compiled output, this will inline the word. Normally a deferred word will look like: `address-containing-word @ EXECUTE EXIT`, this optimizes that to: `word EXIT`.
555+
* Tail calls. Words may be defined in assembly or forth. If the final word before an `EXIT` (or end of definition) is a forth word, this will instead jump to it. For example, a word that is compiled as `+ forth-word EXIT` will be optimized to `+ jump(forth-word)`. Smaller in token threaded model, faster, saves a stack slot.
556+
557+
To be added later:
558+
* Assembly inlining (subroutine threaded only)
559+
* Forth inlining
560+
* Forth common sequence compression
561+
* Flow control analysis
562+
* Constant folding
563+
* Peephole optimization

cmd/build.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,12 @@ ulp-forth build --assembly --reserved 1024 file1.f file2.f`,
116116
func init() {
117117
rootCmd.AddCommand(buildCmd)
118118

119-
buildCmd.Flags().String(CmdOutput, "", "Name of output file")
120-
buildCmd.Flags().IntP(CmdReserved, "r", 8176, "Number of reserved bytes for the ULP, for use with --assembly flag")
119+
buildCmd.Flags().String(CmdOutput, "", "Name of the output file.")
120+
buildCmd.Flags().IntP(CmdReserved, "r", 8176, "Number of reserved bytes for the ULP, for use with --assembly flag. Note that the espressif linker reserves an extra 12 bytes.")
121121

122-
buildCmd.Flags().Bool(CmdAssembly, false, "Output assembly that can be compiled by the main assemblers, set the --reserved flag before using")
123-
buildCmd.Flags().Bool(CmdCustomAssembly, false, "Output assembly only for use by ulp-asm")
122+
buildCmd.Flags().Bool(CmdAssembly, false, "Output assembly that can be compiled by the main assemblers, set the --reserved flag before using.")
123+
buildCmd.Flags().Bool(CmdCustomAssembly, false, "Output assembly only for use by ulp-asm, another project by this author.")
124124
buildCmd.MarkFlagsMutuallyExclusive(CmdCustomAssembly, CmdAssembly)
125125

126-
buildCmd.Flags().Bool(CmdSubroutineThreading, false, "Use subroutine threading model")
126+
buildCmd.Flags().Bool(CmdSubroutineThreading, false, "Use the subroutine threading model. Faster but larger.")
127127
}

0 commit comments

Comments
 (0)