Skip to content

Commit

Permalink
Add initial documentation in 'docs/'
Browse files Browse the repository at this point in the history
  • Loading branch information
aw committed Jan 13, 2023
1 parent 4c0bb52 commit 4717efa
Show file tree
Hide file tree
Showing 10 changed files with 631 additions and 123 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ jobs:
steps:
- uses: actions/checkout@v3

- name: Update APT repos
run: sudo apt-get update

- name: Install RISC-V build dependencies
run: sudo apt-get install build-essential binutils-riscv64-unknown-elf gcc-riscv64-unknown-elf

Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ debug:
flash:
stm32loader -p $(DEVICE) -ewv $(FIRMWARE)

djb2:
gcc -o djb2 djb2.c

longan-nano:
$(MAKE) build BOARD=longan-nano

Expand Down
134 changes: 16 additions & 118 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# FiveForths: 32-bit RISC-V Forth for microcontrollers

[![GitHub release](https://img.shields.io/github/release/aw/fiveforths.svg)](https://github.com/aw/fiveforths)
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/aw/fiveforths/main.yml) [![GitHub release](https://img.shields.io/github/release/aw/fiveforths.svg)](https://github.com/aw/fiveforths) [![justforfunnoreally.dev badge](https://img.shields.io/badge/justforfunnoreally-dev-9ff)](https://justforfunnoreally.dev)

[FiveForths](https://github.com/aw/fiveforths) is a tiny [Forth](https://www.forth.com/starting-forth/) written in hand-coded RISC-V assembly, initially designed to run on the 32-bit [Longan Nano](https://longan.sipeed.com/en/) (GD32VF103) microcontroller.

Expand All @@ -10,123 +10,34 @@ Development progress has been logged regularly in the [devlogs](https://aw.githu

---

1. [Requirements](#requirements)
2. [Getting started](#getting-started)
3. [Flashing the firmware](#flashing-the-firmware)
4. [About FiveForths](#about-fiveforths)
5. [Todo](#todo)
8. [Contributing](#contributing)
9. [Changelog](#changelog)
10. [Other Forths](#other-forths)
11. [License](#license)
1. [Quick start](#quick-start)
2. [Documentation](#documentation)
3. [Todo](#todo)
4. [Contributing](#contributing)
5. [Changelog](#changelog)
6. [License](#license)

# Requirements
# Quick start

* Linux (tested on Debian bullseye) with _RISC-V_ cross-compilation binaries installed
* 32-bit GD32VF103 microcontroller
* USB cable for flashing firmware (using `dfu-util`), or Serial/USB UART (`PA9`, `PA10`) pins
* Serial/USB UART connected to JTAG (`PA13`, `PA14`, `PA15`, `PB3`) pins for debugging
* Manually built `openocd` and `gdb` installed in `/opt/riscv/` for debugging only

# Getting started

It is possible to download a firmware binary or build the firmware manually.

## Download it

Download one of the firmware binaries from the [releases page](https://github.com/aw/fiveforths/releases).
The quickest way to get started is to download and flash one of the firmware binaries listed below:.

* [fiveforths-longan-nano-lite.bin](https://github.com/aw/fiveforths/releases/download/v0.2/fiveforths-longan-nano-lite.bin) (64K Flash, 20K RAM)
* [fiveforths-longan-nano.bin](https://github.com/aw/fiveforths/releases/download/v0.2/fiveforths-longan-nano.bin) (128K Flash, 32K RAM)

## Build it

The first step is to prepare the environment for building the firmware:

```
sudo apt-get install build-essential binutils-riscv64-unknown-elf gcc-riscv64-unknown-elf
```

This should install the _RISC-V_ binaries in `/usr/bin/` prefixed with: `riscv64-unknown-elf-`

Next, clone this repository:

```
git clone https://github.com/aw/fiveforths.git
cd fiveforths
```

Finally, build the firmware and debug files with `make`. The output should look like this:

```
$ make
/usr/bin/riscv64-unknown-elf-as -g -march=rv32imac -I src -o fiveforths.o fiveforths.s
/usr/bin/riscv64-unknown-elf-ld -m elf32lriscv -T fiveforths.ld -o fiveforths.elf fiveforths.o
/usr/bin/riscv64-unknown-elf-objcopy -O binary fiveforths.elf fiveforths.bin
/usr/bin/riscv64-unknown-elf-objcopy -O ihex fiveforths.elf fiveforths.hex
/usr/bin/riscv64-unknown-elf-objdump -D -S fiveforths.elf > fiveforths.dump
```

The firmware file is called `fiveforths.bin` and is **under 2 KBytes** as of _release v0.1_ since _January 08, 2023_.

# Flashing the firmware

There are many ways to flash the firmware to the _Longan Nano_. There are a few good resources [here](https://github.com/riscv-rust/longan-nano/), [here](https://github.com/theandrew168/derzforth#program), [here](https://www.susa.net/wordpress/2019/10/longan-nano-gd32vf103/), [here](https://www.appelsiini.net/2020/programming-gd32v-longan-nano/), [here](https://sigmdel.ca/michel/ha/gd32v/longan_nano_01_en.html), and elsewhere. I personally use a Serial/USB UART and flash it using the python `stm32loader` tool, unless I'm debugging with `GDB` then I'll use `load` to flash the new firmware via the JTAG pins.

## stm32loader

Install it with:
See the [TUTORIALS](docs/TUTORIALS.md) for detailed download and flashing information.

```
pip3 install stm32loader
```
# Documentation

Set the _Longan Nano_ into `boot mode`:

```
press BOOT, press RESET, release RESET, release BOOT
```

Flash the firmware with:

```
stm32loader -p /dev/ttyUSB0 -ewv fiveforths.bin
```

The output should look like this:

```
Activating bootloader (select UART)
Bootloader version: 0x30
Chip id: 0x410 (STM32F10x Medium-density)
Supply -f [family] to see flash size and device UID, e.g: -f F1
Extended erase (0x44), this can take ten seconds or more
Write 8 chunks at address 0x8000000...
Writing ████████████████████████████████ 8/8
Read 8 chunks at address 0x8000000...
Reading ████████████████████████████████ 8/8
Verification OK
```

The device may be different from `/dev/ttyUSB0`, but I'm sure you can figure it out.

# About FiveForths

The source files are:

* [fiveforths.s](fiveforths.s): lists the register assignment and loads the actual source files from `src/`.
* [src/01-variables-constants.s](src/01-variables-constants.s): defines some constants which are stored in Flash memory, but which may point to memory addresses to be used as variables.
* [src/02-macros.s](src/02-macros.s): defines macros to avoid repeating code throughout the source files.

**WIP**
* [TUTORIALS](docs/TUTORIALS.md): a quick guide to **get started**
* [EXPLAIN](docs/EXPLAIN.md): learn the story behind _FiveForths_
* [HOWTO](docs/HOWTO.md): build, usage, and code examples in Forth and RISC-V Assembly
* [REFERENCE](docs/REFERENCE.md): learn the technical details, what's under the hood

# TODO

- [ ] Finish writing this README
- [x] Fix remaining bugs (carriage return issue)
- [ ] Implement bounds checks for stacks and dictionary
- [ ] Code cleanup and optimizations
- [ ] Add example Forth code to turn it into a "real" Forth (ex: `[`, `]`, `branch`, etc)
- [ ] Code cleanup and optimizations

# Contributing

Expand All @@ -144,19 +55,6 @@ Please create a pull-request or [open an issue](https://github.com/aw/picolisp-k

## 0.1 2023-01-09 - First release

# Other Forths

This document would be incomplete without listing other Forths which inspired me and are worth checking out:

* [colorForth, by Chuck Moore (inventor)](https://colorforth.github.io/cf.htm)
* [Mecrisp, batteries-included with FPGA support](https://mecrisp.sourceforge.net/)
* [sectorforth, super tiny 16-bit implementation](https://github.com/cesarblum/sectorforth)
* [jonesforth, 32-bit heavily documented](https://rwmj.wordpress.com/2010/08/07/jonesforth-git-repository/)
* [derzforth, 32-bit risc-v inspiration](https://github.com/theandrew168/derzforth)
* [nasmjf, the devlog idea and well documented](http://ratfactor.com/nasmjf/)
* [CamelForth, by Brad Rodriguez (Moving Forth)](http://www.camelforth.com)
* [muforth, the sum of all Forth knowledge](https://muforth.nimblemachines.com/)

# License

[MIT License](LICENSE)
Expand Down
35 changes: 35 additions & 0 deletions djb2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
FiveForths - https://github.com/aw/FiveForths
RISC-V Forth implementation
The MIT License (MIT)
Copyright (c) 2021~ Alexander Williams, https://a1w.ca
*/

#include <stdio.h>
#include <string.h>

// source: http://www.cse.yorku.ca/~oz/hash.html
unsigned long djb2(unsigned char *str)
{
unsigned long hash = 5381;
int c;

while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

return hash;
}

int main(int argc, char *argv[]) {
unsigned char *str = argv[1];
unsigned long result = djb2(str);

int length = strlen(str) << 24; // move the length to the last 8 bits (MSG) by shifting length by 24
result = result & ~0xff000000; // zero out the 8-bit flags+length by inverting the mask
result = result | length; // add the length of the string to the hash by bitwise OR'ing

printf("djb2_hash: 0x%08x\n", result );

return 0;
}
62 changes: 62 additions & 0 deletions docs/EXPLAIN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# FiveForths: Explain

[FiveForths](https://github.com/aw/fiveforths) is a tiny [Forth](https://www.forth.com/starting-forth/) written in hand-coded RISC-V assembly, initially designed to run on the 32-bit [Longan Nano](https://longan.sipeed.com/en/) (GD32VF103) microcontroller.

---

This document provides an explanation of _FiveForths_ and the story behind it.

## Menu

1. [Why another Forth?](#why-another-forth)
2. [Why not Lisp?](#why-not-lisp)
3. [Why indirect threading?](#why-indirect-threading)
4. [Why word hashing?](#why-word-hashing)
5. [Why so few primitives?](#why-so-few-primitives)

### Why another Forth

Most likely, every **Forth** creator in this millenium has faced this question. I've already blogged about my [decision here](https://a1w.ca/p/2021-03-15-the-future-of-computing-with-riscv-fpgas-and-forth/) and [here](https://a1w.ca/p/2023-01-03-year-of-the-microcontroller/). To resume, I think _Forth_ is a nice way to start fresh in the world of microcontrollers and FPGAs. I think if we want to go back to owning our tools and creating things that run the way they should, then we need to start from a good minimal base.

My initial goal was to start from [derzforth](https://github.com/theandrew168/derzforth/pulls?q=is%3Apr+is%3Aclosed) by contributing to the project and help bring it to a fully useable implementation,my existing but I eventually decided to create my own which was more aligned with my personal goals.

I want to use _FiveForths_ as an alternative to my current C++/Lua-based microcontroller projects. I also want to use it as a building block for deploying to larger RISC-V instruction sets (ex: 64-bit) on much more powerful devices. Eventually, like most dreamers, I'd like to create an operating system, or something like that, so I guess I had to start somewhere.

### Why not Lisp

Those who know me are aware that I've been programming in [PicoLisp](https://picolisp.com) for almost a decade. It's been my go-to language for almost everything at the high level, but I've been unable to get it to lower-level microcontroller projects. In the end, I'm even sure that Lisp (of any kind) is suitable for a microcontroller.

I still think it's a wonderful high-level language and will continue using it for those projects. Eventually maybe I'll implement PicoLisp in Forth. Who knows? For now, it's _Forth_ at the low-level, and _PicoLisp_ at the high-level.

### Why indirect threading

At the start of 2023, I wrote about [what kind of threading](https://aw.github.io/fiveforths/devlog-29-what-kind-of-threads) I planned on using (direct-threading), but the implementation was ugly and buggy, so I switched to indirect-threading shortly after. The implementation is very similar to existing implementations such as [jonesforth](https://github.com/nornagon/jonesforth), so it was very easy to get it working perfectly, and it's also easy to reason about.

I'm aware that it may have some disadvantages on a modern RISC-V CPU architecture, but it's something that can eventually be changed if I start to notice some performance issues, and if I have time to work on it.

### Why word hashing

A typical _Forth_ will have a variable word length for dictionary entries. The header that's built in memory would then store the word length and the characters of the word, and add padding to words for it to align on a boundary (ex: 4 byte boundary). A 32-character word header would end up requiring 11 CELLs or memory compared to a hash which would only require 3 CELLs for the header.

The disadvantage of hashing is the number of cycles required to compute the hash. We're looking at an order of magnitude more time, but on a _Longan Nano_ running at 8 million cycles per second (8 MHz) it's still blazingly fast.

Considering the relatively small memory size of the _Longan Nano Lite_ (20 KBytes), I feel like it would be a massive waste to use nearly 3x more memory by storing words the traditional way. Since it can be also be clocked up to 108 MHz, it seems much more sensible to focus on optimizing memory usage rather than optimizing the code execution path.


### Why so few primitives

_FiveForths_ only has 19 built-in primitives, which is at least 150 less than a typical _Forth_. This means to get a "real" _Forth_ would require writing (or pasting) hundreds of lines of code into the terminal. Keeping with the idea of having an extremely minimal implementation, this approach seems fine for me. Not all primitives are needed for every use-case, and this allows the dictionary to be built specifically to suit ones needs.

---

Now that you've read the answer to various questions, you're ready to read the other documents below:

* [TUTORIALS](TUTORIALS.md): a quick guide to **get started**
* [HOWTO](HOWTO.md): build, usage, and code examples in Forth and RISC-V Assembly
* [REFERENCE](REFERENCE.md): learn the technical details, what's under the hood

# License

[MIT License](LICENSE)

FiveForths documentation and source code copyright © 2021~ [Alexander Williams](https://a1w.ca) and licensed under the permissive open source [MIT](https://opensource.org/licenses/MIT) license.
Loading

0 comments on commit 4717efa

Please sign in to comment.