This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

FPGA Development

TL;DR

git clone https://github.com/badgeteam/mch2022-tools/
git clone --recursive https://github.com/badgeteam/mch2022-firmware-ice40
python3 mch2022-tools/webusb_fpga.py mch2022-firmware-ice40/projects/Hello-World/hello_world.bin

If the TL;DR wasn’t wordy enough for you, try “FPGA Getting Started for Badgers with Tiny Brains” or read on!

If you look for a beginner friendly, graphical FPGA development suite: https://github.com/badgeteam/mch2022-icestudio

Welcome

The badge contains an ice40 FPGA that is connected to a PMOD connector, a serial QSPI RAM, and a RGB LED. It can also control the display over a parallel bus, and has an USB UART link via the RP2040 and an SPI link to the ESP32 which notifies the FPGA on the state of the buttons and offers read access to large data files.

You can start with having a look at the top-level diagram of the badge hardware of the complete badge, then proceed to the schematic and pin constraints file.

Quickstart

As with all the other methods to program the badge, step one is to download mch2022-tools. There are two main tools to use here, python3 webusb_fpga.py bitstream.bin which will upload a bitstream directly into the FPGA, and python3 webusb_fat_push.py bitsream.bin /sdcard/apps/ice40/myapp/bitstream.bin which will make the bitstream available in the launcher.

The easiest way to install the tools needed to synthesise bitstreams for the FPGA is oss-cad-suite.

You can also build Yosys, Icestorm, and NextPNR from source.

Do not try to install packaged Yosys/NextPNR/Icestorm tools that might come with your distro – the toolchain is advancing very, very quick, and if your distro packaged it three months ago, it is already heavily outdated. The ones in Debian Stable – Ouch!

The main repository with templates and examples is mch2022-firmware-ice40. Running make in any of the folders in the projects directory should produce a bitstream in separate build-tmp subfolder. Also take note of the cores folder, which contains many useful cores for basic functionality, such as providing the FPGA as a peripheral to the ESP via SPI and others.

The FPGA can kind of be used in two seperate modes: standalone and peripheral mode.

Standalone

When launching a bitstream from the launcher, the ESP32 hands over control of the display to the FPGA, and exposes an API for reading buttons and files.

A simple example to read the buttons is found in buttons.v

A more elaborate example of a full-fledged RISC-V SoC with a wishbone bus and video output can be found in riscv_doom. While the example is runing Doom, but it’s actually a full featured RISC-V processor so it’s possible to change the RISC-V code running on it, add or modify peripherals on the wishbone bus, etc.

The file read interface uses data files either temporarily uploaded along with the bitstream you are currently working on as webusb_fpga.py riscv-playground.bin 0xdabbad00:fw/tinyblinky/tinyblinky.bin or put into the filesystem as fpga_dabbad00.dat in the same folder as the bitstream itself. You can use multiple data files with different 32-bit hexadecimal file identifiers.

Hints

If you want to think of the badge solely as FPGA dev board, you can ignore most of its other functionality, just keep in mind these handy hints:

  • The two UART lines are routed to /dev/ttyACM1, your terminal program selects the baud rate.

  • The FPGA should control the RGB LED using the SB_RGBA_DRV hard macro with constant current capabilities instead of a simple Verilog outputs, as that would overdrive at least the red LED.

  • The FPGA shall wait for then lcd_mode pin that switches between SPI/parallel mode of the LCD to go high before starting to talk to the LCD, as it is driven by the ESP32.

  • Check twice before connecting external voltages to the PMOD :-)

Example projects for standalone mode

This is a list of Verilog examples available in https://github.com/badgeteam/mch2022-firmware-ice40/.

There is also a collection of examples written in Silice: https://github.com/sylefeb/mch2022-silice.

Blinkies

Three different blinkies are available for a bright first experience:

Buttons

A small example on how to get the state of the buttons. This is not trivial as the buttons are not connected to the FPGA.

Ledcomm

A light emitting diode can shine, but it can also detect light. This contains an UART <-> Ledcomm bridge that allows one to transfer data between two badges just using a pair of LEDs. Still confused? Read the original paper https://merl.com/publications/docs/TR2003-35.pdf.

Forth Pmod Lab

Soldered something special for the Pmod connector? The Forth Pmod Lab helps you to quickly examine your hardware using the Forth language. Due to extensive documentation also suitable if you want to try Forth for the first time.

Snake

A free interpretation of the classic “snake” game with ASCII art and a Ledcomm based two-player mode. Enjoy!

RISCV-Playground

A complete beginner friendly RISC-V ‘fantasy microcontroller’ that deserves its own documentation.

Doom

Does it run Doom? Of course!

Peripheral mode

Both the C++ and the Python API contain a convenience function to load a bitstream into the FPGA from your ESP32 program. This allows the FPGA to be used as a peripheral for the ESP32 processor, think AI coprocessor, bitcoin mining, HDMI output…

A great way to get started with this is to use the spi_skeleton example, which exposes a wishbone bus to the ESP32 over SPI.

This mode could be used to add an UART port on the PMOD by adding the following code, adjusting the top level ports and incrementing WN.

	// UART [2]
	// ----

	uart_wb #(
		.DIV_WIDTH(12),
		.DW(32)
	) uart_I (
		.uart_tx  (uart_tx),
		.uart_rx  (uart_rx),
		.wb_addr  (wb_addr[1:0]),
		.wb_rdata (wb_rdata[2]),
		.wb_we    (wb_we),
		.wb_wdata (wb_wdata),
		.wb_cyc   (wb_cyc[2]),
		.wb_ack   (wb_ack[2]),
		.clk      (clk),
		.rst      (rst)
	);

On the ESP32 you could then write the following Python script that loads a bitstream and writes to the newly added UART port.

import mch22
from fpga_wishbone import FPGAWB

# load bitstream from SD card onto the FPGA
with open("/sd/apps/ice40/myapp/bitstream.bin", "rb") as f:
    mch22.fpga_load(f.read())

# create a wishbone command buffer
c = FPGAWB()
# setup UART
# (30e6/9600)-2
c.queue_write(2, 4, 3123)
# queue writing a byte
c.queue_write(2, 0, 0xaa)
# queue reading a byte
c.queue_read(2, 0)
# execute the command queue
c.exec()

Example projects for peripheral mode

Selftest

Badge hardware ok next to the FPGA? The selftest checks for that and reports back to the ESP32.

SPI-to-RGB

The SPI to RGB bridge gives the ESP32 control over the RGB LED, which is directly connected to the FPGA.

Guide for complete newbies to FPGAs

Let’s try for short:

For a bunch of TTL logic chip to do something useful, you need to wire them up - and the way you wire these determines the function of the completed circuit.

A “Field Programmambe Gate Array” contains a grid of “universal gates” called lookup-tables with -in our case- 4 binary inputs and 1 output, and every of these is accompanied by 1 flipflop bit. Nothing special so far. The special sauce of an FPGA is their connection - that there is a dense mesh of wires in different lengths that crisscross the entire chip, with switchbox points that allow to choose how to connect the individual logic elements to the mesh of wires. By selecting which switchboxes to activate, one builds an actual digital circuit on the FPGA.

For your curiosity, here is a DIY FPGA: http://blog.notdot.net/2012/10/Build-your-own-FPGA

You should have an idea by now! You are going to build logic circuits. And you’ll probably fall into a rabbit hole :-)

Check out our “FPGA Getting Started for Badgers with Tiny Brains” guide for step by step information getting from 0 to a hardly working FPGA setup, in case you never heard of FPGAs before.

We would love to give you a more complete intro, but for time-is-not-infinite reasons, recommend you intros from others instead.

For the ones that prefer reading and want to know everything to design their own RISC-V CPU at the end of the course:

https://github.com/BrunoLevy/learn-fpga/tree/master/FemtoRV/TUTORIALS/FROM_BLINKER_TO_RISCV

For the ones that prefer videos and a calm pace, Shawn Hymel has done a series in 12 parts that really starts at the beginning and explains the scenery you encounter:

https://github.com/ShawnHymel/introduction-to-fpga https://www.digikey.de/en/maker/projects/introduction-to-fpga-part-1-what-is-an-fpga/3ee5f6c8fa594161a655a9f960060893

1 - FPGA Getting Started explained by a Badger with a Very Small Brain

Ok let’s get real.

FPGA development is different from regular computer programming. It’s not necessarily more difficult, but the concepts involved are very different.

The number one difference is: in programming everything happens one things after another. With FPGAs, everything happens at once. This probably does not make sense yet, but it will.

Badger

What even is an FPGA?

FPGA stands for Field Programmable Gate Array. A “normal” chip like the ESP32 can also be considered a Gate Array. It’s an array of logic gates gates (NANDs ORs NOTs, etc.) that are wired together to form an ESP32 CPU. An FPGA also contains a bunch of logic gates. But they aren’t wired together. You write a (kinda) program to explain the way the gates are supposed to be wired together. This probably does not make sense without an example. So let’s get started.

Verilog

The (kinda) programming language almost all the examples use will be Verilog. It looks like this:

	// this is what comments look like
	/* or like this */

	// verilog is structured into `modules`
	module AND (input  a,   // modules have `wires` coming into them
	            input  b,
	            output c);  // or going out. Direction matters.
		                // there is also `inout`

		    // everything else is _just_ like Javascript.
		    assign c = a & b; // semicolons are mandatory

	endmodule // unless they're no. It depends.

The code above builds a logical AND abstraction. It take a and b coming into the module, and’s them together and assigns the resulting value to c. When the code gets run through the toolchain (the analog of “compiling” in FPGA-lang is synthesis) the toolchain search in its database for unused structures within the target FPGA that can be used to create such an AND.

These structures are called look-up tables (LUTs), because they can be configured to take a bunch of inputs and look up what the output should be in a table. For our AND the configured LUT will look like this:

a/b01
000
101

This is still really abstract

Install the tools

Ok, let’s get started for real. First clone out repo :

$ git clone git@github.com:badgeteam/mch2022-firmware-ice40.git --recursive
	Cloning into 'mch2022-firmware-ice40'...
	remote: Enumerating objects: 1333, done.
	remote: Counting objects: 100% (345/345), done.
	remote: Compressing objects: 100% (229/229), done.
	remote: Total 1333 (delta 193), reused 251 (delta 115), pack-reused 988
	Receiving objects: 100% (1333/1333), 1.97 MiB | 4.15 MiB/s, done.
	Resolving deltas: 100% (690/690), done.
..... 8< ....  snip snip snip boring .....

$ cd mch2022-firmware-ice40/
$ cat README.md
..... 8< ....  snip snip snip boring .....

Get the latest package for your computers architecture: 
https://github.com/YosysHQ/oss-cad-suite-build/releases

..... 8< ....  snip snip snip boring .....

… and then download all the necessary tools from https://github.com/YosysHQ/oss-cad-suite-build/releases


$ mkdir toolchain && cd toolchain
$ wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/$MY_TOOL_CHAIN_IT_DEPENDS!

oss-cad-suite-linux-arm64 47%[================>                     ] 185,29M  6,93B/s    eta 31h
..... 8< ....  snip snip snip boring .....

$ tar -xzf $WHATEVER_YOU_JUST_DOWNLOADED
$ cd ..
$ source toolchain/$WHATEVER/environment

Awesome you’re ready. Let’s get started for real. All the examples are in the projects subdirectory in the mch2022-firmware-ice40 folder that you cloned from Git.

  • _common : stuff needed everywhere
  • Buttons : ~fairly simple example that wires together all the buttons to change the RGB LED colors
  • Fading-RGB : even simpler example that just fades the LEDs
  • Fading-White : …
  • Forth : a stack CPU that’s designed to run Forth
  • Hello-World : looks like a good starting place !
  • Ledcomm : … have a look around
  • riscv_doom : game. running on a cpu synthesized onto the FPGA
  • RISCV-Playground : … it’s 47 degrees C
  • selftest : … you need to do some looking around yourself.
  • Snake : better game
  • spi_skeleton : … it build character
  • spi-to-rgb : … and I’m lazy

So, if you looked around, all the examples are structured similarly:

  • they contain a Makefile we use this to turn the designs into bitstream. Those are basically a bunch of bits that are used to configure or Program the Array of Gates. And you are sitting in a Field.
  • Most already contains a *.bit file. This is the bitstream for the example. You could just load it to the badge.
  • There is an rtl directory containing *.v files. *.v is the extension for Verilog. RTL stands for “Register Transfer Logic (or Language” and describes the aspect of Verilog that looks more like Javascript but is able to be converted into logic gates.
  • The CPU projects also contain software to run on the CPU and possibly a toolchain to compile the software
  • misc other stuff

ENOUGH ALREADY you’re boring me to pieces …

Build the Project

Ok, we’ll start with ‘Hello World’. If you followed the instructions, you just need to type make and everything works:

$ make
cd /mch2022-firmware-ice40/projects/Hello-World/build-tmp && \
	yosys -s /mch2022-firmware-ice40/projects/Hello-World/build-tmp/hello-world.ys \
		 -l /mch2022-firmware-ice40/projects/Hello-World/build-tmp/hello-world.synth.rpt
/mch2022-firmware-ice40/toolchain/oss-cad-suite/bin/yosys: line 6: /mch2022-firmware-ice40/toolchain/oss-cad-suite/lib/ld-linux-aarch64.so.1: cannot execute binary file: Exec format error
/mch2022-firmware-ice40/toolchain/oss-cad-suite/bin/yosys: line 6: /mch2022-firmware-ice40/toolchain/oss-cad-suite/lib/ld-linux-aarch64.so.1: Success
make: *** [../../build/project-rules.mk:88: /mch2022-firmware-ice40/projects/Hello-World/build-tmp/hello-world.json] Error 126

Urgh. I screwed this up, but because one or two of you will screw this up as well, I thought I’d leave it in. If you look carefully at the error message, you’ll see something about aarch64. Which is ARM stuff. I’m using Badger-Basic on an x86, so I downloaded the wrong tools. Drat. (I actually managed to download the wrong tools twice :m)

… Several minutes later …

$ make
cd /mch2022-firmware-ice40/projects/Hello-World/build-tmp && \
	yosys -s /mch2022-firmware-ice40/projects/Hello-World/build-tmp/hello-world.ys \
		 -l /mch2022-firmware-ice40/projects/Hello-World/build-tmp/hello-world.synth.rpt

.... 8< ....  snip snip snip totally not boring but lots of it ..... 

Info: Program finished normally.
icepack -s /mch2022-firmware-ice40/projects/Hello-World/build-tmp/hello-world.asc /mch2022-firmware-ice40/projects/Hello-World/build-tmp/hello-world.bin

Ok. Are we done yet?

Install on the Badge

Almost. Now we only need to push the newly generated bitstream onto the badge. And then we get to the exciting part: explaining how it all works! Use the webusb tools to push the bitstream:

 $ cd ../../tools/
 $ python webusb_fpga.py ../projects/Hello-World/hello_world.bin 
Waiting for ESP32 to boot into FPGA download mode...
Sending bitstream : ...................................................

If this didn’t work, and there are error messages concerning USB, you need to install pyusb. Try something like:

$ pip install pyusb
... or
$ apt install python-usb

Awesome

What you can’t tell from the picture is that the LED is actually blinking. In different colors. Super cool.

So … you said you’ll explain how this all works…

I also said I’m lazy and it’s 47 degrees Celcius. This section may be expanded upon or left abandoned with good intentions of finishing it up before MCH2022. *cough*

If it’s not done, either read the more about advanced examples or head over to the fpga repo, there is a lot of information there. Also, come by the workshops at the camp to chat.

Further Resources

No matter how far we get, this will not turn into a “Learning Verilog” Tutorial. Here is a list of resources we like to learn more about FPGA development: