VHDL implementation of the RRISC CPU

A small CPU with a radically reduced instruction set. Hand-crafted. Implemented in VHDL, for use in an FPGA.

Download as .zip Download as .tar.gz View on GitHub

RRISC Assebmly - introduction

The RRISC CPU can address the following:

Basic commands

The RRISC CPU understands the following basic commands, that are all based on the read / write principle:

LD stands for load and is used for loading values into a register reg.

ST stands for store and is used for writing the value of a register to the RAM (or external ports).

Each of the above commands causes a transaction between elements of two groups: The first group consists of the 7 registers and the second group is is comprised of RAM, ports and instruction register 2 (‘operand’ of the instruction).

The following types of transactions are valid:

JMP stans for jump and is used to continue program execution at a defined address. Strictly speaking, a jump is nothing else but an LD of the program counter - but because of its side effect, it is considered a distinct command.

LD and ST can be applied to 7 registers. The registers are named A, B, C, D, E, F, G.


Conditions

In addition, all commands can be executed under four different conditions:

The ALU is the CPU’s arithmetic logical unit, and used to perform calculations and comparisons. The result of an ALU operation can be non-numerical, e.g. in a comparison operation. In that case, above-mentioned ALU flags are set instead.


Adressing modes

The RRISC CPU has the following addressing modes:

Immediate addressing mode

This is the simplest of them all. A register is loaded with an immediate value, a constant specified by the programmer.

Example: LDA # $01 - loads the value $01 into register A

Absolute addressing mode

In absolute addressing mode, the operand is a 16-bit address to the RAM.

Examples:

LDA $1300   ; load byte from RAM at address $1300
STA $1301   ; store byte into RAM at address $1301

External addressing mode

In external addressing mode, the operand is a 16-bit address of an external port. External ports are used to communicate with periphery. In our case, the ALU is external periphery.

Examples:

IN A,  $0000   ; read from port 0
OUT A, $0001   ; write to port 1

Indirect addressing mode

This mode is only applicable to JMP. It involves an indirection and is both interesting and also a bit more complicated. Indirect addressing can be used for both RAM and port access. It is best described by example:

Consider JMP $1300 - here the address is 1300 and can be split into a HIGH byte $13 and a LOW byte $00. The 16-bit address $1300 can be calculated from HIGH and LOW bytes as follows:

addr = HIGH * 256 + LOW

In indirect mode addressing, we write the instruction:

JMP $13[$00]  ; HIGH[LOW]

… and calculate the address as follows:

temp_addr := $1300
LOW := byte at address $1300
HIGH:= $13

addr = HIGH * 256 +LOW

So first we look up the LOW byte of the address at the given address and then use the HIGH byte and the new LOW byte as address.

We can write the address calculation without using a temporary, using square brackets as ‘look into address’ indirection operator:

addr = HIGH * 256 + [HIGH * 256 + LOW]

This mode can be used for jump tables.


Assembler commands

Based on:

the following assembler command set of the RRISC CPU is derived.

Note: the condition ‘unconditional’ is implicit. Unconditional commands do not have a condition clause.

Example:

lDA # $00         ; unconditional
LDA # $00 : EQ    ; only if EQ flag is set

LD(reg) Addr : condition

Loads register reg with the RAM contents at address addr if the given condition is true.

Example:

LDA $1300   ; loads A with value at $1300

ST(reg) Addr : condition

Stores value of register reg into RAM at address addr if the given condition is true.

Example:

STA $1300   ; stores a into RAM at $1300

LD(reg) # val : condition

Loads the value val into register reg if the given condition is true.

Example:

LDA # $00   ; loads A with value $00

IN reg, port : condition

Loads the value at external port port into register reg if the given condition is true.

Examole:

IN A, $0003 ; read from port 3

OUT reg, port : condition

Writes the value of register reg to external port port if the given condition is true.

Example:

OUT A, $05 ; write to port 5

JMP addr : condition

Jump to address addr if the given condition is true.

Example:

JMP $C000 : EQ ; if equal, jump to $c000

JMP HI[LO] : condition

Jump to RAM address HI * 256 + [HI * 256 + LO] if the given condition is true.

Example:

JMP $C0[00] : EQ ; if equal, jump to $c000 + [$c0000]

JMPP HI[LO] : condition

Jump to address HI * 256 + ports[HI * 256 + LO] if the given condition is true. The difference to JMP is that here the LO byte is read from the specified port.

Example:

JMPP $C0[$00] : EQ ; if equal, jump to $c000 + port[$c000]

^ toc

< It’s executing its first instruction

> RRISC Assembler - writing programs