Skip to content

Latest commit

 

History

History
79 lines (67 loc) · 4.51 KB

FemtoRV32_II.md

File metadata and controls

79 lines (67 loc) · 4.51 KB

Episode II: the RV32I instruction set

Another important source of information is of course the RISC-V reference manual. There you learn that there are several flavors of the RISC-V standard. Let us start from the simplest one (RV32I, that is, 32 bits base integer instruction set). Then we will see how to add things, one thing at a time. This is a very nice feature of RISC-V, since the instruction set is modular, you can start with a very small self-contained kernel, and this kernel will be compliant with the norm. This means standard tools (compiler, assembler, linker) will be able to generate code for this kernel. Then I started reading Chapter 2 (page 13 to page 30). Seeing also the table page 130, there are in fact only 11 different instrutions ! (I say for instance that an AND, an OR, an ADD ... are the same instruction, the operation is just an additional parameter). Now we just try to have an idea of the overall picture, no need to dive into the details for now. Let's take a global look at these 11 instructions:

instruction description algo
branch conditional jump, 6 variants if(reg OP reg) PC<-PC+imm
ALU reg Three-registers ALU ops, 10 variants reg <- reg OP reg
ALU imm Two-registers ALU ops, 9 variants reg <- reg OP imm
load Memory-to-register, 5 variants reg <- mem[reg + imm]
store Register-to-memory, 3 variants mem[reg+imm] <- reg
LUI load upper immediate reg <- (im << 12)
AUIPC add upper immediate to PC reg <- PC+(im << 12)
JAL jump and link reg <- PC+4 ; PC <- PC+imm
JALR jump and link register reg <- PC+4 ; PC <- reg+imm
FENCE memory-ordering for multicores (not detailed here, skipped for now)
SYSTEM system calls, breakpoints (not detailed here, skipped for now)
  • The 6 branch variants are conditional jumps, that depend on a test on two registers.

  • ALU operations can be of the form register <- register OP register or register <- register OP immediate

  • Then we have load and store, that can operate on bytes, on 16 bit values (called half-words) or 32 bit values (called words). In addition byte and half-word loads can do sign expansion. The source/target address is obtained by adding an immediate offset to the content of a register.

  • The remaining instructions are more special (one may skip their description in a first read, you just need to know that they are used to implement unconditional jumps, function calls, memory ordering for multicores, system calls and breaks):

    • LUI (load upper immediate) is used to load the upper 20 bits of a constant. The lower bits can then be set using ADDI or ORI. At first sight it may seem weird that we need two instructions to load a 32 bit constant in a register, but in fact it is a smart choice, because all instructions are 32-bit long.

    • AUIPC (add upper immediate to PC) adds a constant to the current program counter and places the result in a register. It is meant to be used in combination with JALR to reach a 32-bit PC-relative address.

    • JAL (jump and link) adds an offset to the PC and stores the address of the instruction following the jump in a register. It can be used to implement function calls. JALR does the same thing, but adds the offset to a register.

    • FENCE and SYSTEMS are used to implement memory ordering in multicore systems, and system calls/breaks respectively.

To summarize, we got branches (conditional jumps), ALU operations, load and store, and a couple of special instructions used to implement unconditional jumps and function calls. There are also two functions for memory ordering and system calls (but we will ignore these two ones for now). OK, in fact only 9 instructions then, it seems doable... At this point, I had not understood everything, so I'll start from what I think to be the simplest parts (register file and ALU), then we will see the instruction decoder and how things are interconnected.

Next