The INSTR16 project started with the question: Is it possible to design a good instruction set with 16 general registers, and 16-bit opcodes? I now think the answer is yes (but "good" is a bit subjective, of course). The project has grown a bit beyond an afternoon hack, to the point where I have a simulator, a primitive assembler, as well as a Verilog implementation of most of the CPU, synthesizable to a FPGA (ice40) development board. The instruction set features 16 64-bit registers, 8 of which can also be used for floating point operations. There are very few three-register instructions, most notably indexed load and store and long shift. Most instructions have a source and destination register. All memory accesses are full 64 bits, with some tricks to help accessing individual fields of 8, 16 or 32 bits, assuming they are naturally aligned. Immediate constants, and addressing offsets, are of limited size. When a constant or offset doesn't fit in the immediate field in an instruction, additional prefix instructions can be used to build larger constants. (Sure, in some sense this makes opcodes variable size rather than fixed to 16 bits. But the prefix instructions are decoded independently. The closest alternative is handle all but very small constants by loading a 64-bit value into a register, using pc-relative addressing, like the ARM ldc instruction. But using a prefix instruction is much more compact for the common case of smallish constants). The top directory contains instr16.tex, which is the main specification, and some other text files with partly out-of-date working notes. The assembler/ directory contains a primitive assembler. There's no linker and no real object file format. The .o files generated by the assembler are raw machine code to be loaded at address 0 (the initial pc). To organize assembly code in multiple files, enable m4 preprocessing with -m and use m4 include. The assembler syntax is a bit inspired by ARM. The simulator/ directory contains a simulator which is intended to implement all instructions in the spec, plus halt (0xffff) and bkpt (0xfffe). The simulator takes an object file, generated by the assembler, as the first argument, and optionally initial values for the registers. It also simulates memory mapped i/o registers for terminal input and output. The hw/ directory contains a Verilog implementation of the processor (except the floating point arithmetic), and some arithmetic modules I have been playing with. The primary modules are instr_decode.v and cpu.v, in the hw/cpu/lib sub directory. The top-level file for simulation is main.v, and top-level for synthesis is top-ice40.v. Both variants connect the processor to some RAM and a memory-mapped serial port, and the ice40 file also adds a memory mapped register for controlling the LEDs on the board. I use Icarus Verilog for simulation. The cpu is also synthesizable using Yosys, targeting iCE40-HX8K FPGA. See https://github.com/YosysHQ/icestorm for information about this FPGA family and the free software tools to program it. Tests are collected in the testsuite/ directory. To get started, try make make check ./simulator/instr16 examples/hello.o => Hello world. ./hw/main +quiet=1 +img=examples/hello.hex => Hello world. For a more elaborate example, there's an RPN calculator, ./simulator/instr16 examples/calc.o 17 50 + => 67 7 % => 4 53 * => 212 To try out the Verilog implementation, first install Icarus Verilog, for simulation. To synthesize for ice40, these tools are needed (in suggested install order): Icestorm Yosys next-pnr To load a simple program (examples/blink-slow.s) into the processor and on the FPGA, try (cd hw && make ice40-blink-slow.bin && iceproc ice40-blink-slow.bin) Then the LEDs should start blinking in a rather boring binary pattern. To load the RPN-calculator example (examples/calc.s) into the processor on the FPGA, procedure is similar. Try (cd hw && make ice40-calc.bin && iceproc ice40-calc.bin) Then connect to the serial port, typically /dev/ttyUSB1, using either ./hw/recv-serial -i, or your favorite serial program. The port is configured as 19200 baud, 8 bits, no parity. Happy hacking, Niels Möller, <nisse@lysator.liu.se> LocalWords: opcodes Verilog VHDL pc preprocessing testsuite Niels ldc LocalWords: putd tex bkpt Möller FPGA hw cpu yosys synthesizable RPN LocalWords: Icestorm LEDs
Select Git revision
instr16
-
-
- Open with
- Visual Studio Code
- IntelliJ IDEA
- Download source code
Niels Möller
authored
Name | Last commit | Last update |
---|---|---|
assembler | ||
examples | ||
hw | ||
simulator | ||
testsuite | ||
.gitignore | ||
COPYING | ||
Makefile | ||
README | ||
compare.txt | ||
floating-point.txt | ||
instr16.tex | ||
load-and-store.txt | ||
notes.txt | ||
reciprocal.tex | ||
run-tests | ||
srt-division.tex |