riscii

An emulator for the RISC II
Log | Files | Refs | LICENSE

commit d790c63fe3e871e96f2edafe440967000e1ea2dd
parent 47459597507cb3818399b8d1b12d4dc4c493ec35
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date:   Sun, 23 Oct 2022 14:14:27 -0700

More documentation

Diffstat:
Mriscii.org | 418+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 336 insertions(+), 82 deletions(-)

diff --git a/riscii.org b/riscii.org @@ -1,95 +1,349 @@ #+TITLE: RISC II documentation #+AUTHOR: Ryan Jeffrey #+EMAIL: ryan@ryanmj.xyz -#+OPTIONS: num:nil +#+STARTUP: align +#+STARTUP: shrink +#+OPTIONS: toc:2 +#+OPTIONS: tex:t -bibliography:refer.bib +* What? +The RISCII was among the first processors to be made with the design +concept of Reduced Instruction Set Computing, or RISC. Given that most +processors today are RISC-based it is a historically very important piece of +hardware which is why I was so confused when I found little in the way +of documentation on the project. [[https://web.archive.org/web/20070930184913/http://cs.swan.ac.uk/~csandy/cs-323/notes/0607.cs-323005.html][This page]][fn::I found an error on +this page (unless I misunderstand somehting): it mentions that the +RISCII had no virtual memory, but Katevenis' thesis mentions an MMU] +was one of few technical descriptions of the RISCII I could find and +it served as inspiration for this project. This document is a +description of the RISCII, and should provide enough information to +write an emulator. The RISCII was designed at UC Berkeley by students +Manolis Katevenis and Robert Sherburne under professors David +Patterson and Carlo Séquin and was completed in 1983 +[fn::https://people.eecs.berkeley.edu/~pattrsn/Arch/prototypes2.html]. +This document is essentially a heavily abridged version of chapters 3, +4, and the appendix of Katevenis' thesis[fn::ISBN-13:9780262111034]. + +* The Concept of RISC +The concept of RISC was created by David Patterson around the +year 1980. It was based on the premise of creating a CPU architecture +that had as few instructions as possible and as few instruction +formats as possible as a means to simplify CPU design. This idea was +inspired by observations made by Andrew S. Tanenbaum (among others) +that showed that most CPU instructions were not used in a typical computer +program. The benefits of a simpler architecture with a limited amount of instructions +would be more efficient use of CPU die space and the elimination of microcode. + +The Berkeley RISC project went about implementing these ideas with the following +methods: + +- All instructions are the same length (32 bits). This simplifies the fetch and decode steps of a typical CPU pipeline. +- Instructions are register-to-register. This means that an average + instruction will be of the form =register ← register <operation> + register=. This is opposed to traditional CPU design, which allows + instructions like =register ← M[x] + register=, or =M[x] ← M[x] + + register= among other formats. The exceptions to this are load and + store instructions, which move data to/from registers to/from + memory. +- Only two addressing modes: PC relative and absolute. There are no + pre/post increment/decrement instructions. +- All instructions execute in a single cycle (except for loads and stores). + +RISCs were also implemented with high level languages (namely C) in +mind, which is why RISCII lacks certain instructions, e.g. bit +rotation, since few HLLs support such features[fn::Modern RISCs like +ARM support bit rotation in particular due to its usefullness in +cryptography]. + * Instructions -There are three two basic instruction formats. -| Instruction | Operation | Format | Conditional | -|-------------+------------------------------------------------------------+--------+-------------| -| *calli* | CWP ← (CWP - 1) MOD 8, rd ← LSTPC, TODO | Short | | -| *getlpc* | rd ← LSTPC | Short | | -| *putpsw* | PSW ← rs1 + shortSource2 | Short | | -| *reti* | CWP ← (CWP + 1) MOD 8, I ← 1 | Short | [ X ] | -| getpsw | rd ← PSW | Short | | -| callx | CWP ← (CWP - 1) MOD 8, rd ← PC, NXTPC ← rs1 + shortSource2 | Short | | -| callr | CWP ← (CWP - 1) MOD 8, rd ← PC, NXTPC ← PC + imm19 | Long | | -| jmpx | NXTPC ← rs1 + shortShource2 | Short | [ X ] | -| jmpr | NXTPC ← PC + imm19 | Long | [ X ] | -| ret | CWP ← (CWP + 1) MOD 8, NXTPC ← rs1 + shortSource2 | Short | [ X ] | -| sll | rd ← rs1 << shortSource2 | Short | | -| srl | rd ← rs1 >> shortSource2 | Short | | -| sra | rd ← rs1 >> shortSource2 | Short | | -| ldhi | rd ← imm19 << 13 | Long | | -| and | rd ← rs1 & shortSource2 | Short | | -| or | rd ← rs1 ¦ shortSource2 | Short | | -| xor | rd ← rs1 ⊕ shortSource2 | Short | | -| add | rd ← rs1 + shortSource2 | Short | | -| addc | rd ← rs1 + shortSource2 + C | Short | | -| sub | rd ← rs1 - shortSource2 | Short | | -| subc | rd ← rs1 - shortSource2 + C | Short | | -| subi | rd ← shortSource2 - rs1 | Short | | -| subci | rd ← shortSource2 - rs1 + C | Short | | -| ldxw | rd ← M[rs1 + shortSource2] | Short | | -| ldrw | rd ← M[PC + imm19] | Long | | -| lxhu | rd ← M[rs1 + shortSource2] & 0xffff | Short | | -| lrhu | rd ← M[PC + imm19] & 0xffff | Long | | -| lxhs | rd ← sign_ext(M[rs1 + shortSource2] & 0xffff) | Short | | -| lrhs | rd ← sign_ext(M[PC + imm19] & 0xffff) | Long | | -| lxbu | rd ← M[rs1 + shortSource2] & 0xff | Short | | -| lrbu | rd ← M[PC + imm19] & 0xff | Long | | -| lxbs | rd ← sign_ext(M[rs1 + shortSource2] & 0xff) | Short | | -| lrbs | rd ← sign_ext(M[PC + imm19] & 0ff) | Long | | -| stxw | M[rs1 + shortSource1] ← rd | Short | | -| strw | M[PC + imm19] ← rd | Long | | -| stxh | M[rs1 + shortSource1] ← align(rd & 0xffff) | Short | | -| strh | M[PC + imm19] ← align(rd & 0xffff) | Long | | -| stxb | M[rs1 + shortSource1] ← align(rd & 0xff) | Short | | -| strb | M[PC + imm19] ← align(rd & 0xff) | Long | | -** sign_ext() +All RISCII instructions are 32 bits long. Each contains an opcode +(first seven bits), then a 1 bit [[sec:cc][SCC flag]], then a five bit destination +register name. There are two instruction formats for the last 19 bits +described below. For conditional instructions the destination register is +replaced with a four bit [[sec:conds][conditoinal code]]. + +If the SCC bit of an instruction is HIGH, +then it will set the SCC bit according its opcode. If the SCC bit is LOW it will +ignore the condition codes. + +** Short-Immediate format +This format adds a 5 bit source register name and a second data source +that can either be a 13 bit /signed/ immediate or a another five bit +register name. The 14th bit is the /immediate bit/ which is high if +shortSource2 is an immediate or low if a register. + +#+CAPTION: Format of a short-immediate instruction. +| 7 bits | 1 bit | 5 bits | 5 bits | 14 bits | +|--------+-------+--------+--------+--------------| +| opcode | SCC | DEST | rs1 | shortSource2 | + + +#+CAPTION: shortSource2 format for register +| 13th bit (IMM bit) | Bits 12-5 | Bits 4-0 | +|--------------------+-----------+----------| +| 0 | <garbage> | rs2 | + + +#+CAPTION: shortSource2 format for immediate +| 13th bit (IMM bit) | Bits 12-0 | +|--------------------+-------------------------| +| 1 | 13 bit signed immediate | +Where the MSB of the immediate is the sign bit. + +Where DEST is either a five-bit name of a [[sec:wins][destination register]] or a four bit [[sec:conds][conditional code]]. + +** Long-Immediate format +This format uses all the remaining 19 bits as an /unsigned/ immediate. +#+CAPTION: Format of a long-immediate instruction. +| 7 bits | 1 bit | 5 bits | 19 bits | +|--------+-------+--------+---------| +| opcode | SCC | DEST | imm19 | + +Where DEST is either a five-bit name of a [[sec:wins][destination register]] or a four bit [[sec:conds][conditional code]]. + +** Basic instruction information +| Instruction | Operation | Format | Conditional | +|-------------+--------------------------------------------------------------+--------+-------------| +| [[sec:calli][*calli*]] | =CWP ← (CWP - 1) MOD 8, rd ← LSTPC, TODO= | Short | | +| [[sec:getlpc][*getlpc*]] | =rd ← LSTPC= | Short | | +| [[sec:putpsw][*putpsw*]] | =PSW ← rs1 + shortSource2= | Short | | +| [[sec:reti][*reti*]] | =CWP ← (CWP + 1) MOD 8, I ← 1= | Short | ✅ | +| [[sec:getpsw][getpsw]] | =rd ← PSW= | Short | | +| [[sec:callx][callx]] | =CWP ← (CWP - 1) MOD 8, rd ← PC, NXTPC ← rs1 + shortSource2= | Short | | +| [[sec:callr][callr]] | =CWP ← (CWP - 1) MOD 8, rd ← PC, NXTPC ← PC + imm19= | Long | | +| [[sec:jmpx][jmpx]] | =NXTPC ← rs1 + shortShource2= | Short | ✅ | +| [[sec:jmpr][jmpr]] | =NXTPC ← PC + imm19= | Long | ✅ | +| [[sec:ret][ret]] | =CWP ← (CWP + 1) MOD 8, NXTPC ← rs1 + shortSource2= | Short | ✅ | +| [[sec:sll][sll]] | =rd ← rs1 << shortSource2= | Short | | +| [[sec:srl][srl]] | =rd ← rs1 >> shortSource2= | Short | | +| [[sec:sra][sra]] | =rd ← rs1 >> shortSource2= | Short | | +| [[sec:ldhi][ldhi]] | =rd ← imm19 << 13= | Long | | +| [[sec:and][and]] | =rd ← rs1 & shortSource2= | Short | | +| [[sec:or][or]] | =rd ← rs1 ¦ shortSource2= | Short | | +| [[sec:xor][xor]] | =rd ← rs1 ⊕ shortSource2= | Short | | +| [[sec:add][add]] | =rd ← rs1 + shortSource2= | Short | | +| [[sec:addc][addc]] | =rd ← rs1 + shortSource2 + C= | Short | | +| [[sec:sub][sub]] | =rd ← rs1 - shortSource2= | Short | | +| [[sec:subc][subc]] | =rd ← rs1 - shortSource2 + C= | Short | | +| [[sec:subi][subi]] | =rd ← shortSource2 - rs1= | Short | | +| [[sec:subci][subci]] | =rd ← shortSource2 - rs1 + C= | Short | | +| [[sec:ldxw][ldxw]] | =rd ← M[rs1 + shortSource2]= | Short | | +| [[sec:ldrw][ldrw]] | =rd ← M[PC + imm19]= | Long | | +| [[sec:lxhu][lxhu]] | =rd ← M[rs1 + shortSource2] & 0xffff= | Short | | +| [[sec:lrhu][lrhu]] | =rd ← M[PC + imm19] & 0xffff= | Long | | +| [[sec:lxhs][lxhs]] | =rd ← sign_ext(M[rs1 + shortSource2] & 0xffff)= | Short | | +| [[sec:lrhs][lrhs]] | =rd ← sign_ext(M[PC + imm19] & 0xffff)= | Long | | +| [[sec:lxbu][lxbu]] | =rd ← M[rs1 + shortSource2] & 0xff= | Short | | +| [[sec:lrbu][lrbu]] | =rd ← M[PC + imm19] & 0xff= | Long | | +| [[sec:lxbs][lxbs]] | =rd ← sign_ext(M[rs1 + shortSource2] & 0xff)= | Short | | +| [[sec:lrbs][lrbs]] | =rd ← sign_ext(M[PC + imm19] & 0ff)= | Long | | +| [[sec:stxw][stxw]] | =M[rs1 + shortSource1] ← rd= | Short | | +| [[sec:strw][strw]] | =M[PC + imm19] ← rd= | Long | | +| [[sec:stxh][stxh]] | =M[rs1 + shortSource1] ← align(rd & 0xffff)= | Short | | +| [[sec:strh][strh]] | =M[PC + imm19] ← align(rd & 0xffff)= | Long | | +| [[sec:stxb][stxb]] | =M[rs1 + shortSource1] ← align(rd & 0xff)= | Short | | +| [[sec:strb][strb]] | =M[PC + imm19] ← align(rd & 0xff)= | Long | | + +*Bold* name means priveleged instruction. + +*** =sign_ext()= Sign extend the value to 32 bits. -** align() -Align the value according to the memory address. - -| Value | 00 | 01 | 10 | 11 | -|-------+-------+-------+-------+-------| -| Word | [ X ] | | | | -| Short | [ X ] | | [ X ] | | -| Byte | [ X ] | [ X ] | [ X ] | [ X ] | - - - - -* Registers -The RISC II has 138 general purpose registers and an additional 5 -special registers used for internal state. -** Register windows -<<sec:wins>> The RISC II uses an overlapping window stack system for -its general purpose registers. There are 10 global registers available -to all windows at all times, 10 local registers available only to the -current window, 6 "in" register available to the current window and -the previous window (as the previous window's out registers), and 6 -"out" registers available to the current window and the next window -(as the next window's in registers) [[Parencites:&katevenis83][p. 54-56]]. There are 8 windows in -total [[parencite:&katevenis83][p. 179]]. When a function is called the special CWP -register[[sec:spec]] is incremented and the system moves up to the next -register window. If the system runs out of register windows on a -function call it must flush the oldest window(s) to memory and then -restore them when the current function returns. - -** Special registers: -<<sec:spec>> -- *PC*: The program counter. Holds the address of the current instruction being executed. Needed for PC-relative instructions [[parencite:&katevenis83][p. 88]]. +*** =align()= +Align (left shift) the value according to the memory address. +** Detailed instruction information +*** =calli= Call Interrupt +<<sec:calli>> +*** =getlpc= Get Last Program Counter +<<sec:getlpc>> +*** =putpsw= Put Processor Status Word +<<sec:putpsw>> +*** =reti= Return from interrupt +<<sec:reti>> +*** =getpsw= Get Processor Status Word +<<sec:getpsw>> +*** =callx= Call absolute +<<sec:callx>> +*** =callr= Call relative +<<sec:callr>> +*** =jmpx= Jump absolute +<<sec:jmpx>> +*** =jmpr= Jump relative +<<sec:jmpr>> +*** =ret= Return from subroutine +<<sec:ret>> +*** =sll= Shift left logical +<<sec:sll>> +*** =srl= Shift right logical +<<sec:srl>> +*** =sra= Shift right arithmetic +<<sec:sra>> +*** =ldhi= Load high bits +<<sec:ldhi>> +*** =and= Bitwise AND +<<sec:and>> +*** =or= Bitwise OR +<<sec:or>> +*** =xor= Bitwise XOR +<<sec:xor>> +*** =add= Add +<<sec:add>> +*** =addc= Add with carry +<<sec:addc>> +*** =sub= Subtract +<<sec:sub>> +*** =subc= Subtract with carry +<<sec:subc>> +*** =subi= Subtract inverse +<<sec:subi>> +*** =subci= Subtract inverse with carry +<<sec:subci>> +*** =ldxw= Load word into register absolute +<<sec:ldxw>> +*** =ldrw= Load word into register relative +<<sec:ldrw>> +*** =lxhu= Load unsigned half word into register absolute +<<sec:lxhu>> +*** =lrhu= Load unsigned half word into register relative +<<sec:lrhu>> +*** =lxhs= Load signed half word into register absolute +<<sec:lxhs>> +*** =lrhs= Load signed half word into register relative +<<sec:lrhs>> +*** =lxbu= Load unsigned byte into register absolute +<<sec:lxbu>> +*** =lrbu= Load unsigned byte into register relative +<<sec:lrbu>> +*** =lxbs= Load signed byte into register absolute +<<sec:lxbs>> +*** =lrbs= Load signed byte into register relative +<<sec:lrbs>> +*** =stxw= Store word absolute +<<sec:stxw>> +*** =strw= Store word relative +<<sec:strw>> +*** =stxh= Store half word absolute +<<sec:stxh>> +*** =strh= Store half word relative +<<sec:strh>> +*** =stxb= Store byte absolute +<<sec:stxb>> +*** =strb= Store byte relative +<<sec:strb>> + +* Conditionals +<<sec:conds>> +| Code | Name | Operation | +|------+---------------------------------+----------------------------------| +| 0001 | Signed greater than | $\overline{(N\oplus V) \vert Z}$ | +| 0010 | Signed less than or equal to | $(N\oplus V) \vert Z$ | +| 0011 | Signed greater than or equal to | $\overline{N\oplus Z}$ | +| 0100 | Signed less than | $N\oplus Z$ | +| 0101 | Unsigned greater than | $\overline{\overline{C} + Z}$ | +| 0110 | Unsigned less than or equal | $\overline{C} + Z$ | +| 0111 | Unsigned less than | $\overline{C}$ | +| 1000 | Unsigned greater than | $C$ | +| 1001 | Positive (or zero) | $\overline{N}$ | +| 1010 | Negative | $N$ | +| 1011 | Not equal | $\overline{Z}$ | +| 1100 | Equal | $Z$ | +| 1101 | No overflow | $\overline{V}$ | +| 1110 | Overflow | $V$ | +| 1111 | Always | 1 | + + +* The Clock +<<sec:clock>> + + +* Memory addressing + +When loading or storing, the RISC II requires that the memory address be aligned +according to the type that is being loaded/stored. Words addresses must be divisible by 4, +short addresses must be divisible by 2, and bytes can have any address. An invalid alignment +will result in a TRAP. + +Alignment requirements. + +Below is a table that shows what addresses are valid for each type. +The four rightmost columns represent the last two bits of the memory address being accessed. +Any blank value indicates an alignment error. + +| Value | 00 | 01 | 10 | 11 | +|-------+----+----+----+----| +| Word | ✅ | | | | +| Short | ✅ | | ✅ | | +| Byte | ✅ | ✅ | ✅ | ✅ | + + +* Data bus +** BAR (Byte Address Register) +<<sec:bar>> +Contains the two least significant bits of the memory address currently being accessed. + +* Control Unit +<<sec:control>> + +* User visible State +** Condition code bits +<<sec:cc>> + +The RISCII has four condition codes, each 1 bit. These codes are used for determining +branches. Each of the conditions below rely on the SCC bit of the mentioned instruction +to be HIGH. +- =C= Carry bit. HIGH if the last addition caused a carry to the 33rd + bit, or if the last subtraction did NOT have a borrow from the 33rd + bit. +- =V= Overflow bit. HIGH if the last addition or subtraction caused a + signed overflow (the sign bit was changed when the operation should + have resulted in a change in sign). +- =N= Negative bit. HIGH if the destination of the last instruction + has a value that is less than 0 (the sign bit is HIGH). +- =Z= Zero bit. HIGH if the destination of the last instruction has a + value of 0. + +** Register windows and General Purpose Registers +<<sec:wins>> The RISC II has 138 general purpose registers separated +by [[sec:wins][register windows]], each 32 bits. When a value is loaded into a +register it is sign extended to 32 bits. + +The RISC II uses an overlapping window stack system for its general +purpose registers. There are 10 global registers available to all +windows at all times, 10 local registers available only to the current +window, 6 "in" register available to the current window and the +previous window (as the previous window's out registers), and 6 "out" +registers available to the current window and the next window (as the +next window's in registers). There are 8 windows in total When a +function is called the special CWP register[[sec:spec]] is incremented and +the system moves up to the next register window. If the system runs +out of register windows on a function call it must flush the oldest +window(s) to memory and then restore them when the current function +returns. + +** Special registers +<<sec:spec>> The RISCII's pipeline also has five special registers +used for internal state. +- *PC*: The program counter. Holds the address of the current + instruction being executed. Needed for PC-relative instructions. - *NXTPC*: Next program counter. Holds the address of the next instruction to be executed. Useful because of RISC II's delayed - branching method [[parencite:&katevenis83][p. 88]]. + branching method. - *LSTPC*: Last program counter. What PC was during the execution of - the last instruction. Used for restoring from a trap/interrupt - [[parencite:&katevenis83][p. 88]]. + the last instruction. Used for restoring from a trap/interrupt. - *CWP*: Current window pointer[[sec:wins]]. The number of windows on the - window stack (does not exceed 8). + window stack (3 bits). - *SWP*: Saved window pointer[[sec:wins]]. Index of the youngest window - saved in memory + saved in memory (3 bits). + +* Unanswered Questions +This section contains questions relating to the RISCII design that have +not yet been answered. +- MMU and virtual memory. It is mentioned in Katevenis' paper but not + described. +- Destination latch for the overflow and carry bits. The bits are not + written into the PSW until the next cycle, so there has to be a + place for these values to exist before their writing. It is not + described in Katevenis' thesis. +- Endianess. Some parts of the thesis suggest Big Endianess, but it is + not explicitly stated.