== frombelow.net ==

Eris 22206. Eris 2010. Led Pillar. Blinkenstrip.

Eris 2206 FPGA Computer

Eris 2206 is a front-panel programmable 8 bit computer with lots of Blinkenlights, implemented on an FPGA. It is not directly based on an existing computer design (contemporary or historic), but it takes inspiration from minicomputers and early microcomputers.


Watch the video!

The most prominent feature of Eris 2206 is its 16x16 LED display, which shows the internal state of the CPU and the first 24 memory addresses. The main mode of programming Eris 2206 is by directly writing to its 128 bytes of RAM via front-panel switches. The 125 bytes ROM contain a small bootloader to load programs and data via a serial connection.

Operating Eris 2206

Eris 2206 knows three operation modes: run mode, step mode, and memory set mode. The mode is chosen by a three way switch. In run mode, execution speed is set by the speed dial next to the mode switch, while in step mode the step button triggers execution of the next micro instruction.

Eris 2206 powers on when connected to a USB power supply. On power up in run or step mode, it starts executing the program located at the first ROM address, $80, which computes Fibonacci numbers.

The 16x16 LED matrix shows the internal state of the CPU and the first RAM cells:

Row Left Side Right Side
0 Micro Instruction Micro Instruction
1 Address Bus Data Bus
2 Accumulator ALU
3 Program Counter Data Pointer
4-15 RAM RAM


The main mode of programming Eris 2206 is by front panel switches. For this, the mode switch has to be set to memory set mode. Pushing the reset button sets the memory address to the first address, $00. Now the data bus can be set via switches. Pushing the step button writes the current value on the data bus to the current memory address and advances the memory pointer. Pushing the skip button advances the memory pointer without writing to memory. When program and data have been written to memory, execution is started by setting the mode button to step mode and pushing the reset button. Now the program is executed by pushing the step button repeatedly or by switching to run mode.

Alternatively, programs can be uploaded via the serial UART provided by the USB interface. For this, a minimal first-stage boot loader has to be programmed and executed via the front panel. This first-stage boot loader jumps to the second stage serial boot loader at ROM address $C0. The host program for serial upload is located in directory src/tools/send_serial.py. It expects hex code input.


Since we have an 8 bit address bus, a total of 256 memory addresses are available. The lower 128 bytes are RAM. The upper 125 bytes are RAM. The uppermost three bytes provide memory mapped access to I/O and the UART.

Address Assignment
$FF UART - Clear/Status
$FE UART - Send/Receive
$FD I/O (0..5: LEDs)
$C0-$FC ROM (Serial boot loader)
$80-$BF ROM (Fibonacci numbers)
$00-$7F RAM


src/tools/eras.pl provides a macro assembler using CPP for macro processing. The directory also contains a number of example programs.

The CPU knows four addressing modes:

  • Implicit

The argument is given implicitly, e.g. INC, which adds one to the accumulator.

  • Immediate

The argument is the actual value. This mode is indicated by a “#”. Example: LDA #$5C loads the value $5C into the accumulator.

  • Direct

The argument is the memory location of the actual value. Example: LDA $5C loads the value stored at memory address $5C into the accumulator.

  • Indirect

The argument is the memory address where the memory address of the actual value is located. This mode is indicated by “()”. Example: If memory address $5C contains value $3B, and memory address $3B contains value $67, LDA ($5C) loads $67 into the accumulator.

Op. Imp. Imm. Dir. Ind. Description
NOP $00 No operation
LDA $08 $02 $0B Load accumulator
STA $12 $17 Store accumulator
JMP $1D Jump to address
JNZ $21 Jump if accumulator != 0
JZE $25 Jump if accumulator == 0
ADD $38 $29 $30 Add value to accumulator
SUB $4B $3C $43 Subtract value from accumulator
AND $5E $4F $56 Logical AND of accumulator and value
ORA $71 $62 $69 Logical OR of accumulator and value
XOR $84 $75 $7C Logical XOR of accumulator and value
INC $88 Increment accumulator
DEC $8A Decrement accumulator
ROL $8C Rotate accumulator 1 bit left
ROR $8E Rotate accumulator 1 bit right
INV $90 Invert accumulator


We write a program that adds $05 and $06:

	LDA #$05
	ADD #$06
	STA :result
	JMP :start

The assembler generates the following machine code for this:

08 05
38 06
12 08
1D 00

In order to enter the machine code, we set the mode switch to “set”, and push the reset button. Now we enter each byte, followed by pushing “step”. Once we are done, we set the mode switch to step, push reset, and set the mode switch to run. After a number of steps, the result is shown memory address $08.

If we want to add different numbers, we set the mode to set again, push reset, push skip once in order to advance to memory address $01 without overwriting the LDA instruction, set the first new number, push step, push skip in order to advance to memory address $03, set the second new number, and set the mode switch to run.

I/O and UART

The I/O register simply maps the lowest 5 bits to the LEDs on the iCEstick (which are not visible, since the iCEstick is in the case).

The UART utilizes two addresses: One address for sending and receiving data, and a second address for status information and to clear the input buffer.

The byte written to address $FE is send. While the bit is transmitted, bit 0 of $FF is 1 (tx_busy). In order to receive a byte, the receiver buffer must be cleared by writing any value to $FF. Once a byte is received, bit 1 of $FF is 1 (recv_buffer_full). Now the byte can be read from $FE. In order to receive the next byte, the receive buffer has to be cleared again. See src/roms/rom_uart.asm for an example.


Eris2206 is implemented on an iCEstick FPGA Evaluation Kit. It is synthesized using the free tools of project IceStorm.

The GPIO pins of the FPGA connect to the 16x16 (WS2811) matrix display and the buttons/switches. Beside some pull-down resistors, the only additional hardware is an NE555 multivibrator circuit providing the bus clock. The speed dial changes the frequency of the multivibrator.


See doc/schematic.dia for an overview of the SOC. Besides the CPU core, the main peripherals are memory (RAM and ROM), the UART, and a system monitor providing output to a matrix of WS2811 LEDs.


The following description of the internal works of the CPU should help you to understand the implementation in src/ecpu.v. It is not necessary to operate Eris 2206.

An instance of module control_logic orchestras the components of the CPU. (These control lines are not shown in the schematic.) Microcode implementations of the opcodes govern the operation of the control logic. The microcode for the assembler opcodes is defined in and generated by src/tools/mc_compiler.py.

The CPU has two counters and three registers:

The program counter (PC) points to the next instruction/argument to be processed. The micro instruction pointer (MIP) points to the next micro instruction to be executed. These counters can be set or incremented.

The three registers are the data pointer (DP), the accumulator, and the output of the arithmetic-logic unit (ALU).

The accumulator is the only register visible to the user. It reads from and writes to external memory (and memory mapped I/O modules) via the data bus. In addition to read and write operations, the accumulator can be incremented, decremented, rotated (left and right), and inverted.

The ALU operates on the current value of the accumulator and the current value on the data base, and writes back to the data bus.

Like the PC, the DP does not write to the data bus, but to the address bus. It is used to dereference addresses in direct and indirect addressing mode.

A zero flag, i.e. a wire indicating whether the register value is zero, is available for the accumulator. This flag is used by the comparator in order to implement the conditional jump on (not) zero instructions.

A little bit of glue logic multiplexes the data and address line: Only the active component may write to the buses.

Micro instructions consist of 15 bits:

Bit Operation
0 Write data bus to PC
1 Write data bus to PC if zero flag 1
2 Write data bus to PC if zero flag 1
3 Increment PC
4 Write data bus to DP
5 Write DP to address bus (write PC to address bus if bit 5 = 0)
6 Set memory address on address bus to value on data bus
7 Write accumulator to data bus
8-10 Set/increment/decrement/rotate/invert accumulator
11 Set MIP to value on data bus (increment MIP if bit 11 = 0)
12-14 Add/subtract/and/or/xor data bus to accumulator and write to data bus

The implementation of the opcodes as micro instructions is given in src/tools/mc_compiler.py. mc_compiler.py compiles a ROM file of the micro instruction implementations of all opcodes. The machine code (= byte value) of an opcodes is the entry points for the microcode of its implementation in this ROM file. Therefore in order to decode an opcode, its byte code is read from the data bus and written into the MIP.

On negative edges of the clock, the next micro instruction is set, i.e. components are turned on/off according to the micro instruction. The components operate on positive clock edges.

There is a separate clock port for the UART, because the UART must be run at 12 MHz for timing reasons, while the rest of the SOC may run at a lower speed.

LED Matrix Monitor

A 16x16 LED matrix provides a monitor view into the internals of the SOC. The LEDs are driven by WS2811 controllers. The control logic is implemented in src/ws2811.v. On each iteration of the internal (12 MHz) clock of the FPGA, one 8 bit element of the monitor is updated, i.e. it takes 32 cycles to update the complete LED matrix. The WS2811 driver allows to set the three color channels of each LED on or off. The intensity is fixed for all LEDs and color channels.

Case and Electronics


The case has been designed in OpenSCAD. The main design file is case/case.scad. Most elements of the case can be cut from 6 mm ply wood with a laser cutter. Running make in directory case generates cutouts for a laser cutter in case/dst/svg/. Three elements are not only cut, but also engraved. Engravings have been added manually using Inkscape. These files for laser cutting are located in case/dst/labeled. Note that for case/dst/labeled/top_plate/acryl_top_plate.svg not 6 mm plywood, but an acryl plate should be cutted.

The holders for the iCEstick and the circuit board are 3D printed. After running make in directory case/, the 3D print files are located in directory case/dst/stl.

The case is partly glued and partly held together by M4 screws. Most M4 screws have nuts as counterparts, except the four screws in the bottom. These are held by claw nuts.


The NE555 multivibrator is housed on a small circuit board, together with a bunch of pull-down resistors for the switches. A Fritzing design file is given in board/circuit.fzz. See src/ecpu.pcf for how to connect the hardware components to the GPIO I/O pins.


See case/lasercut/LICENSE for author, copyright, and licensing information of the lasercut library.

Everything else:

Copyright © 2022 Gerd Beuster gerd@frombelow.net

This project is free soft- and hardware: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this project. If not, see http://www.gnu.org/licenses/.