README.md (13340B)
1 Eris 2206 2 --------- 3 4 1. Introduction 5 2. Operating Eris 2206 6 3. Programming 7 3.1 Memory 8 3.2 Assembler 9 3.3 Example 10 3.4 I/O and UART 11 3. Hardware 12 3.1 SOC 13 3.2 CPU 14 3.4 LED Matrix Monitor 15 4. Case and Electronics 16 4.1 Case 17 4.2 Electronics 18 5. License 19 20 21 Introduction 22 ============ 23 24 [Watch the video!](https://frombelow.net/projects/eris2206/images/eris2206_demo.mp4) 25 26 Eris 2206 is a front-panel programmable 8 bit computer with lots of 27 Blinkenlights, implemented on an FPGA. It is not directly based on an 28 existing computer design (contemporary or historic), but it takes 29 inspiration from minicomputers and early microcomputers. The most 30 prominent feature of Eris 2206 is its 16x16 LED display, which shows 31 the internal state of the CPU and the first 24 memory addresses. The 32 main mode of programming Eris 2206 is by directly writing to its 128 33 bytes of RAM via front-panel switches. The 125 bytes ROM contain a 34 small bootloader to load programs and data via a serial connection. 35 36 37 Operating Eris 2206 38 =================== 39 40 Eris 2206 knows three operation modes: run mode, step mode, and memory 41 set mode. The mode is chosen by a three way switch. In run mode, 42 execution speed is set by the speed dial next to the mode switch, 43 while in step mode the step button triggers execution of the next 44 micro instruction. 45 46 Eris 2206 powers on when connected to a USB power supply. On power up 47 in run or step mode, it starts executing the program located at the 48 first ROM address, $80, which computes Fibonacci numbers. 49 50 The 16x16 LED matrix shows the internal state of the CPU and the first 51 RAM cells: 52 53 Row | Left Side | Right Side 54 ----:|:-----------------:|:------------------: 55 0 | Micro Instruction | Micro Instruction 56 1 | Address Bus | Data Bus 57 2 | Accumulator | ALU 58 3 | Program Counter | Data Pointer 59 4-15 | RAM | RAM 60 61 62 Programming 63 =========== 64 65 The main mode of programming Eris 2206 is by front panel switches. For 66 this, the mode switch has to be set to memory set mode. Pushing the 67 reset button sets the memory address to the first address, $00. Now 68 the data bus can be set via switches. Pushing the step button writes 69 the current value on the data bus to the current memory address and 70 advances the memory pointer. Pushing the skip button advances the 71 memory pointer without writing to memory. When program and data have 72 been written to memory, execution is started by setting the mode 73 button to step mode and pushing the reset button. Now the program is 74 executed by pushing the step button repeatedly or by switching to run 75 mode. 76 77 Alternatively, programs can be uploaded via the serial UART provided 78 by the USB interface. For this, a minimal first-stage boot loader has 79 to be programmed and executed via the front panel. This first-stage 80 boot loader jumps to the second stage serial boot loader at ROM 81 address $C0. The host program for serial upload is located in 82 directory `src/tools/send_serial.py`. It expects hex code input. 83 84 85 Memory 86 ------ 87 88 Since we have an 8 bit address bus, a total of 256 memory addresses 89 are available. The lower 128 bytes are RAM. The upper 125 bytes are 90 RAM. The uppermost three bytes provide memory mapped access 91 to I/O and the UART. 92 93 Address | Assignment 94 :-------|:-------------------- 95 $FF | UART - Clear/Status 96 $FE | UART - Send/Receive 97 $FD | I/O (0..5: LEDs) 98 $C0-$FC | ROM (Serial boot loader) 99 $80-$BF | ROM (Fibonacci numbers) 100 $00-$7F | RAM 101 102 103 Assembler 104 --------- 105 106 `src/tools/eras.pl` provides a macro assembler using CPP for macro 107 processing. The directory also contains a number of example programs. 108 109 The CPU knows four addressing modes: 110 111 - Implicit 112 113 The argument is given implicitly, e.g. INC, which adds one to the 114 accumulator. 115 116 - Immediate 117 118 The argument is the actual value. This mode is indicated by a 119 "#". Example: LDA #$5C loads the value $5C into the accumulator. 120 121 - Direct 122 123 The argument is the memory location of the actual value. Example: LDA 124 $5C loads the value stored at memory address $5C into the accumulator. 125 126 - Indirect 127 128 The argument is the memory address where the memory address of the 129 actual value is located. This mode is indicated by "()". Example: If 130 memory address $5C contains value $3B, and memory address $3B contains 131 value $67, LDA ($5C) loads $67 into the accumulator. 132 133 Op. | Imp. | Imm. | Dir. | Ind. | Description 134 ----|------|------|------|------|:------------------------------------ 135 NOP | $00 | | | | No operation 136 LDA | | $08 | $02 | $0B | Load accumulator 137 STA | | | $12 | $17 | Store accumulator 138 JMP | | | $1D | | Jump to address 139 JNZ | | | $21 | | Jump if accumulator != 0 140 JZE | | | $25 | | Jump if accumulator == 0 141 ADD | | $38 | $29 | $30 | Add value to accumulator 142 SUB | | $4B | $3C | $43 | Subtract value from accumulator 143 AND | | $5E | $4F | $56 | Logical AND of accumulator and value 144 ORA | | $71 | $62 | $69 | Logical OR of accumulator and value 145 XOR | | $84 | $75 | $7C | Logical XOR of accumulator and value 146 INC | $88 | | | | Increment accumulator 147 DEC | $8A | | | | Decrement accumulator 148 ROL | $8C | | | | Rotate accumulator 1 bit left 149 ROR | $8E | | | | Rotate accumulator 1 bit right 150 INV | $90 | | | | Invert accumulator 151 152 153 Example 154 ------- 155 156 We write a program that adds $05 and $06: 157 158 ``` 159 start: 160 LDA #$05 161 ADD #$06 162 STA :result 163 JMP :start 164 result: 165 ``` 166 167 The assembler generates the following machine code for this: 168 169 ``` 170 08 05 171 38 06 172 12 08 173 1D 00 174 ``` 175 176 In order to enter the machine code, we set the mode switch to "set", 177 and push the reset button. Now we enter each byte, followed by pushing 178 "step". Once we are done, we set the mode switch to step, push reset, 179 and set the mode switch to run. After a number of steps, the result is 180 shown memory address $08. 181 182 If we want to add different numbers, we set the mode to set again, 183 push reset, push skip once in order to advance to memory address $01 184 without overwriting the LDA instruction, set the first new number, push 185 step, push skip in order to advance to memory address $03, set the 186 second new number, and set the mode switch to run. 187 188 189 I/O and UART 190 ------------ 191 192 The I/O register simply maps the lowest 5 bits to the LEDs on the 193 iCEstick (which are not visible, since the iCEstick is in the 194 case). 195 196 The UART utilizes two addresses: One address for sending and receiving 197 data, and a second address for status information and to clear the 198 input buffer. 199 200 The byte written to address $FE is send. While the bit is transmitted, 201 bit 0 of $FF is 1 (tx_busy). In order to receive a byte, the receiver 202 buffer must be cleared by writing any value to $FF. Once a byte is 203 received, bit 1 of $FF is 1 (recv_buffer_full). Now the byte can be 204 read from $FE. In order to receive the next byte, the receive buffer 205 has to be cleared again. See `src/roms/rom_uart.asm` for an example. 206 207 208 Hardware 209 ======== 210 211 Eris2206 is implemented on an iCEstick FPGA Evaluation Kit. It is 212 synthesized using the free tools of project 213 [IceStorm](https://clifford.at/icestorm). 214 215 The GPIO pins of the FPGA connect to the 16x16 (WS2811) matrix display 216 and the buttons/switches. Beside some pull-down resistors, the only 217 additional hardware is an NE555 multivibrator circuit providing the 218 bus clock. The speed dial changes the frequency of the multivibrator. 219 220 221 SOC 222 --- 223 224 See `doc/schematic.dia` for an overview of the SOC. Besides the CPU core, 225 the main peripherals are memory (RAM and ROM), the UART, and a system 226 monitor providing output to a matrix of WS2811 LEDs. 227 228 229 CPU 230 --- 231 232 The following description of the internal works of the CPU should help 233 you to understand the implementation in `src/ecpu.v`. It is not 234 necessary to operate Eris 2206. 235 236 An instance of module control_logic orchestras the components of the 237 CPU. (These control lines are not shown in the schematic.) Microcode 238 implementations of the opcodes govern the operation of the control 239 logic. The microcode for the assembler opcodes is defined in and 240 generated by `src/tools/mc_compiler.py`. 241 242 The CPU has two counters and three registers: 243 244 The program counter (PC) points to the next instruction/argument to 245 be processed. The micro instruction pointer (MIP) points to the next 246 micro instruction to be executed. These counters can be set or 247 incremented. 248 249 The three registers are the data pointer (DP), the accumulator, and 250 the output of the arithmetic-logic unit (ALU). 251 252 The accumulator is the only register visible to the user. It reads 253 from and writes to external memory (and memory mapped I/O modules) via 254 the data bus. In addition to read and write operations, the 255 accumulator can be incremented, decremented, rotated (left and right), 256 and inverted. 257 258 The ALU operates on the current value of the accumulator and the 259 current value on the data base, and writes back to the data bus. 260 261 Like the PC, the DP does not write to the data bus, but to the address 262 bus. It is used to dereference addresses in direct and indirect 263 addressing mode. 264 265 A zero flag, i.e. a wire indicating whether the register value is 266 zero, is available for the accumulator. This flag is used by the 267 comparator in order to implement the conditional jump on (not) zero 268 instructions. 269 270 A little bit of glue logic multiplexes the data and address line: 271 Only the active component may write to the buses. 272 273 Micro instructions consist of 15 bits: 274 275 Bit | Operation 276 -----:|:--------------------------------------------------------------------- 277 0 | Write data bus to PC 278 1 | Write data bus to PC if zero flag 1 279 2 | Write data bus to PC if zero flag 1 280 3 | Increment PC 281 4 | Write data bus to DP 282 5 | Write DP to address bus (write PC to address bus if bit 5 = 0) 283 6 | Set memory address on address bus to value on data bus 284 7 | Write accumulator to data bus 285 8-10 | Set/increment/decrement/rotate/invert accumulator 286 11 | Set MIP to value on data bus (increment MIP if bit 11 = 0) 287 12-14 | Add/subtract/and/or/xor data bus to accumulator and write to data bus 288 289 The implementation of the opcodes as micro instructions is given in 290 `src/tools/mc_compiler.py`. `mc_compiler.py` compiles a ROM file of 291 the micro instruction implementations of all opcodes. The machine code 292 (= byte value) of an opcodes is the entry points for the microcode of 293 its implementation in this ROM file. Therefore in order to decode an 294 opcode, its byte code is read from the data bus and written into the 295 MIP. 296 297 On negative edges of the clock, the next micro instruction is set, 298 i.e. components are turned on/off according to the micro 299 instruction. The components operate on positive clock edges. 300 301 There is a separate clock port for the UART, because the UART must be 302 run at 12 MHz for timing reasons, while the rest of the SOC may run at 303 a lower speed. 304 305 306 LED Matrix Monitor 307 ------------------ 308 309 A 16x16 LED matrix provides a monitor view into the internals of the 310 SOC. The LEDs are driven by WS2811 controllers. The control logic is 311 implemented in `src/ws2811.v`. On each iteration of the internal (12 312 MHz) clock of the FPGA, one 8 bit element of the monitor is updated, 313 i.e. it takes 32 cycles to update the complete LED matrix. The WS2811 314 driver allows to set the three color channels of each LED on or 315 off. The intensity is fixed for all LEDs and color channels. 316 317 318 Case and Electronics 319 ==================== 320 321 Case 322 ---- 323 324 The case has been designed in OpenSCAD. The main design file is 325 `case/case.scad`. Most elements of the case can be cut from 6 mm ply 326 wood with a laser cutter. Running make in directory case generates 327 cutouts for a laser cutter in `case/dst/svg/`. Three elements are not 328 only cut, but also engraved. Engravings have been added manually using 329 Inkscape. These files for laser cutting are located in 330 `case/dst/labeled`. Note that for 331 `case/dst/labeled/top_plate/acryl_top_plate.svg` not 6 mm plywood, but 332 an acryl plate should be cutted. 333 334 The holders for the iCEstick and the circuit board are 3D printed. 335 After running make in directory case/, the 3D print files are located 336 in directory case/dst/stl. 337 338 The case is partly glued and partly held together by M4 screws. Most 339 M4 screws have nuts as counterparts, except the four screws in the 340 bottom. These are held by claw nuts. 341 342 Electronics 343 ----------- 344 345 The NE555 multivibrator is housed on a small circuit board, together 346 with a bunch of pull-down resistors for the switches. A Fritzing 347 design file is given in board/circuit.fzz. See src/ecpu.pcf for how to 348 connect the hardware components to the GPIO I/O pins. 349 350 License 351 ======= 352 353 See case/lasercut/LICENSE for author, copyright, and licensing 354 information of the lasercut library. 355 356 Everything else: 357 358 Copyright © 2022 Gerd Beuster <gerd@frombelow.net> 359 360 This project is free soft- and hardware: you can redistribute it 361 and/or modify it under the terms of the GNU General Public License as 362 published by the Free Software Foundation, either version 3 of the 363 License, or (at your option) any later version. 364 365 This project is distributed in the hope that it will be useful, but 366 WITHOUT ANY WARRANTY; without even the implied warranty of 367 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 368 General Public License for more details. 369 370 You should have received a copy of the GNU General Public License 371 along with this project. If not, see <http://www.gnu.org/licenses/>.