Stumpy

A small (50 line) STUMP CPU emulator written in Python along with a quick-and-dirty example "test-bench" program for running STUMP binaries.

Take a look at the code on GitHub, if you're feeling a bit crazy.

The STUMP CPU

The STUMP is a simple 16-bit RISC microprocessor built as an example during the VLSI course at Manchester University. The specification for this can be found in the Lab Manual.

Demo

The following shows a demo execution of the emulator running "test program 1".

$ python test.py test1.bin 
     R0 0000 R1 0000 R2 0000 R3 0000 R4 0000 R5 0000 R6 0000 PC 0000 CC=0000
     > 
000: memory[0000]== 1100100000000000 : st r0, [r0, r0]
     memory[0000]== c800
     memory[0000] = 0000
     R0 0000 R1 0000 R2 0000 R3 0000 R4 0000 R5 0000 R6 0000 PC 0001 CC=0000
     > 
001: memory[0001]== 0001000100011111 : add r1, r0, #-1
     memory[0001]== 111f
     R0 0000 R1 ffff R2 0000 R3 0000 R4 0000 R5 0000 R6 0000 PC 0002 CC=0000
     > r2=0xDEAD
     r2           =dead
     > r3=0xBEEF
     r3           =beef
     > 
002: memory[0002]== 1101100100000001 : st r1, [r0, #1]
     memory[0002]== d901
     memory[0001] = ffff
     R0 0000 R1 ffff R2 dead R3 beef R4 0000 R5 0000 R6 0000 PC 0003 CC=0000
     > 
...

The Emulator

The emulator is designed to be consistent with the specification however as it is not a simulator, it is stepped by full fetch-execute-writeback cycles rather than clock cycles.

Size and Style

This roject was an enterprise of code golf and so the code is not specifically optimised for speed and simply just for source code size. While the emulator is correct and accurately emulates all external interfaces, it fits on 50 lines and just under 100 columns (assuming 2-character tabs). A commented version can be found in stumpy.py which is laid out in a semi-readable way (or at least in the way I developed it) with comments. stumpy_min.py is the same file but with all comments and empty lines removed.

I have avoided using "cheats" like using semi-colons to concatenate code onto one line and using list expansion to place multiple assignments on one line (though in the latter case I do this where it is appropriate or intuative). Some areas are spaced very inconsistently and not at all in order to fit within a reasonable line length.

Finally, yes, grep this for eval and you will be disapointed to discover one. This is used to help compact the representations of branch conditions and while not strictly legit, it can be considered "safe" as it never executes user code and most importantly, it does work!

Correctness

I have run it against the supplied test programs which while broad are not exhaustive and it has passed. While I believe the implementation is correct to the specification, don't rely on it! If you do notice a bug, please feel free to submit a fix (please explain the bug, how to reproduce and explain your fix -- this is code-golf code!) or report it (with a suitable example case).

Usage

The emulator is just a library and doesn't do anything by itself. To use it:

  1. Create a Memory object (passing the size in 16-bit words, usually 1<<16). This memory acts like a list indexable by memory address. Simply load your program into the memory.
  2. Create a Stump object passing in the memory you created.
  3. To step the CPU one instruction at a time, use the step() method.
  4. Registers 0-7 can be accessed as my_stump[r] where my_stump is an instance of Stump and r is the register number.
  5. The condition-codes can be accessed using my_stump[x] where my_stump is an instance of Stump and x is a constant n, z, v or c which are defined in stumpy.py. Note that in order to change these you may need to set cc_en in the stumpy instance to True.
  6. Memory is obviously accessible by reading the values in the Memory instance you created.

The Memory class may be modified to allow memory-mapped IO, for example, or to print debugging information on accesses.

The Test Interface/Example Program

An example usage of the module is provided in the form of test.py which is a (very, very carelessly hacked-together) program which takes an augment of a STUMP "binary" file (which is actually ASCII 0s and 1s separated in to word blocks on individual lines) which is loaded and then executed by the emulator.

When you start the program, the STUMP's registers are printed and a prompt is shown. The register values (R0 -PC) are all in hexadecimal. The condition code register, 'CC', is displayed in binary with the bits in-order- corresponding to n (sign), z (zero), v (overflow), c (carry). Initially these will always be zeroed out on start (unlike real hardware which may be undefined with the exception of the program counter and R0 which is tied to 0).

At the prompt, the following commands are available:

  • [nothing]: Just press enter to step the CPU by one instruction.
  • m addr: Print out the value in the memory address specified by addr. This may be any valid Python integer format, e.g. 123, 0xFF, 0b1101. The value printed will be in hexedecimal.
  • m addr = val: Set the value at the address given to the specified value, again these numbers may be any valid Python integer.
  • r num: Print out the value in the specified register. Registers may be given by number, i.e. 0-7. R7 may be accessed as pc. The 'CC' register's bits must be accessed individually by specifying the bit, i.e., n (sign), z (zero), v (overflow), c (carry).
  • r num = val: Set the value of the given register where num and val are formatted as above.
  • r: Dump all the registers on to the screen.

Tip: Whitespace is optional in the above arguments, e.g. r1=0xFFFF is equivalent to r 1 = 0xFFFF.

Note: And yes, numerical values are just passed straight into an eval with no sanity checking -- what-cha gonna' do about it, punk?

When the CPU is stepped, the time (in steps) is printed (in binary) followed by the instruction as loaded from the location indicated by the PC followed by a disassembly of the instruction.

Memory accesses are printed (where memory[addr]== val indicates a read and memory[addr] = val indicates a write. Addresses and values are printed in hexadecimal. Register accesses are currently not printed.

Once the instruction has finished executing the registers are dumped onto the screen and the prompt is shown again.

Assembler and Example Binaries

An assembler and example binaries for the STUMP can be found on department computers in $COMP22111/sasm and $COMP22111/Cadence/core/test*.s respectively. Unfortunately they are university property and so I cannot distribute these -- sorry!