[☰][2018-03-21]hacks:Bare metal rpi forth interpreter

Why?

I wanted to get a little bit more familiar with the raspberry pi platform and ARM in general. I've had this raspberry pi lying around for ~5 years and never really did anything interesting with it. Now that I had some time I decided to poke at it. This one is a classic exploration project - building a forth interpreter. Now I could've gone with compiling gforth on a ready made Arch distro, but that wouldn't have been much fun - so the condition was to make the interpreter without using an OS during runtime. Also no dynamic memory in the core implementation.

Getting started

I think the easiest way to get started with this is to get [pretty much any] image of a distro meant for the RPI like Raspbian or Arch. then you replace the kernel.img file in the boot partition with your program - that's it! This will make the bootloader load your program instead of the Linux kernel. You can find many examples of programs here. The uart02 example was particularly interesting, because it involved read/write communication with the UART headers on the RPI shown on pic below.


  • Headers 14,15. If you have serial to usb converter connect RPI Rx to Tx and RPI Tx to Rx. Don't forget to also connect ground(GND).

  • After you get your blinker examples running make sure to also try the uart02 example, we're building on top of this.

    Building the loop

    Here you can see the entire (not)main of the program. It contains initialization of the pins, UART registers and word buffers for the words we're going to consume via UART. Nothing really interesting here. Just read poll for characters from the Rx pin and put them in a buffer. A word is ended by the escape character - in this case a space - and the word is handled by the handle_word function.

    Note on GET32

    Obviously GET32 gets 32 bits at a time which makes 4 chars(bytes) per iteration. We're not going for top input performance here so only 1 char per call is actually processed. In more demanding cases this we can easily get 4x performance from here. Other than that there is rewriting this bit in assembly, using a mask and so on, but that's not the point of the article so we'll skip that.

    Linenoise under construction

    Tried using the excellent linenoise library to get more comfortable input experience. There is a trimmed version now https://github.com/marto1/raspforth/blob/master/linenoise.c. It has no hints, the history doesn't allocate memory at runtime and a lot of other small things have been trimmed off. It still uses dynamic memory at some places and there is the dependency on termios so it's not ready for prime time, yet. Overall it's at least 50%~ smaller now so that's nice.

    Building the forth

    Handling words splits them in two cases - numbers and symbols. Numbers are numerical constants like 123123 and are directly pushed on the stack. Scanning them and confirming they are numbers is kind of tricky and hard to do very fast. The first char is scanned for 0-9,+,- case and if it doesn't fit that it is definately not a number, otherwise the total length is checked - 10 is the limit for numbers here by default. And only then is the word scanned back-to-forth to confirm it's a number. This makes detecting symbols pretty fast but number scanning is a O(N) operation always.

    Types

    There is a very small type system builtin that keeps account of the type of each word on the stack. It's a unsigned long long number that will set a bit signifying the type of the word - 0 for symbols and 1 for numbers. This comes in handy for processing user defined functions and/or various optimizations.

    Operations

    Implemented operations are +, -, *, /, dup and swap; dup and swap are 'd' and 's' respectively. Also . will pop and print from the stack. It feels like it needs a few more operations to make it turing-complete. I'll have to dig more to find the bare minimum.

    Conclusion

    Turns out it's really easy to get started with programming the rasperry pi bare metal. Interpreters are fun to implement. There are tradeoffs to be made even in the very beginning of building one. You can do very simple stuff like:

    1 d + 2 *

    There are some other applications that would be interesting to do like dos+nintendo emulator, but will see if it's even worth it. Besides that there is quite a bit of periphery on the RPI so there might be projects with that also.

    share:[twitter][reddit][linkedin][g+]