Skip to content
Snippets Groups Projects
Select Git revision
  • master
  • imm-mullo
  • reallocate-cmpgt-opcodes
  • simd-shift
  • simd-prefix
  • wip-simd-add32
  • mul-13way
  • mul-9way
  • carry-unit
  • use-carry-only
  • verilog-lib
  • logic-analyzer
  • delete-OP_CAT-values
  • use-add_h64
  • use-nextpnr
15 results

instr16

  • 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