Shi – embeddable ARMv7-M Forth

Shi is a fast and tiny embeddable Forth implementation written for the Thumb-2 ISA (ARMv7-M and newer). It currently fits into less than 7kB of flash and 320B of ram.

Whats special about Shi is that it’s not intended to be used as standalone Forth but as library. I was looking for a Lua like experience but with a smaller footprint and more performance.

The project arose from very special needs of my current employer for whom I’ve been developing model train decoders. Those decoders run on low-end Cortex M4 processor and have to handle a lot of parallel tasks like decoding different track communication protocols, motor control, audio output and so forth ( 😉 ). So there ain’t a lot of space and time left for running scripts and I was in demand for the absolutely fastest possible solution.

Searching for alternatives to Lua and MicroPython I found Mecrisp-Stellaris, an amazing Forth implementation for ARMv6/7-M. Sadly Mecrisp-Stellaris is a whole firmware using a serial port as REPL and not a library. I tried to figure out what to remove to make it fit my needs but in the end decided that I had to write my own.

It took me almost two years to totally grasp how Forth works and a couple of iterations and refactoring once I understood how certain aspects play together and interact with each other.

I’m specially happy about how the C++ interface turned out which makes use of very modern features like variadic templates and user-defined literals. Here is a sneak peak of what’s possible with Shi. Further examples and documentation can be found in the repository.

// Running code
shi::evaluate("13 29 +");

// Same but using literal _s
"1 -"_s;

// Creating a new definition called "mean" and then call it
": mean + 2 / ;"_s;
shi::Word mean{"mean"};
int32_t retval{mean(100, 200)};

// Same but using literal _w
retval = "mean"_w(100, 50);

// Getting multiple return values by using std::tuple
": range 0 do i loop ;"_s;
std::tuple<int32_t, int32_t, int32_t, int32_t> t{"range"_w(4)};

// Creating a definition which refers to a C variable
int32_t moore_birthday{1938};
shi::variable(&moore_birthday, "moore_bday");
"2019 moore_bday @ -"_s;

// Same but using literal _v
uint8_t moore_age;
"moore_age"_v(&moore_age);
"moore_age c!"_s;