Contents
Assignment
Implement all RV32I and RV64I instructions.
RV64I is the basic set of integer, logic, and memory instructions in RISC-V. These instructions must be implemented for anything useful to happen with your project.
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-1024x185.png)
The instruction and encodings for RV64I includes RV32I. So, to implement RV64I, first implement RV32I, and then extend it to implement the RV64I instructions. Some of the RV32I instructions will change names when RV64I is implemented. For example, addi in RV32I will change to addiw (addi word), since the RV64I addi will function with 64-bit operands, whereas addiw will function with 32-bit operands (word size).
You will need to implement the following instruction types. It is best to abstract these and then create a mapping that detects the instruction type given an instruction opcode.
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-1-1024x219.png)
There are a lot of instructions here, but you will notice that most of them are simply one operation, such as add, subtract, multiply, and divide. Many of these instructions have the same opcode. To distinguish the instruction, a sub-opcode is used, or sometimes even a sub-sub-opcodes. In this case, take a look at funct3 for the sub-opcode. The sub-sub-opcode is funct7.
So, start with the opcode. This will determine which instruction type it is–again make an array that maps the opcodes to the instruction types. Then, determine if there is a sub-opcode (funct3). Essentially, you are trying to drill down until there is only one instruction instead of yet another list of instructions.
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-2-1024x63.png)
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-3-1024x64.png)
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-4-1024x183.png)
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-5-1024x95.png)
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-6-1024x157.png)
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-7-1024x277.png)
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-8-e1622202518859-1024x304.png)
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-9-1024x34.png)
The ECALL instruction is a special instruction that is used to make a system call, and it is decoded as an I-type. System calls are the way that you will be able to get input and print output to the screen. Without ecall, you will be unable to run a program. In fact, ecall is used to exit a program as well!
Notice that SLLI, SRAI, and SRLI use a portion of the immediate as the shamt, which stands for shift amount. This shift amount will be 6 bits for RV64I. It is 5 bits for the 32-bit versions of these instructions, SLLIW, SRAIW, and SRLIW.
When you are finished with the RV32I instructions, you will need to modify some instructions and add others to implement the rest of RV64I.
![](https://marz.utk.edu/wp-content/uploads/2021/05/image-10-1024x452.png)
You will notice that SRLI, SRAI, and SLLI increase the shift amount (shamt) to six bits (\(2^6=64\)) instead of the original five (\(2^5=32\)). The original five bit versions become SLLIW (shift-left-logical-immediate-word), SRLIW, and SRAIW.
The instructions above that end in W stands for “word”. For example, SUBW means “subtract word”. Since all registers (source and destination) are 64 bits, the XXXW instructions will only look at the lower 32 bits of the source operands and store 32 bits into the destination register. The W instruction allow for overflow at 32 bits rather than at 64 bits.
Since the destination register is 32 bits, all W instructions will widen by replicating the sign bit at index 31 through the upper bits 63 through 32. In other words, the W instructions always sign-extend to widen from 32 bits to 64 bits.
System Calls
The first instruction you should implement is ECALL. This is simply a way for the CPU to get into the operating system to perform services for your program, such as input, output, and exiting the program.
You will notice that ECALL’s opcode is \(1110011_2\). The rest of the instruction MUST BE 0! There are other instructions that I have NOT listed that use the same opcode, but have different values for the rest of the instruction. So, only determine an ECALL function based on the opcode, and 0s for the rest of the 25 bits (32 bit instruction – 7 bit opcode).
System calls for this system will have the service number in the a7 (register number 17) register. Then, the arguments for the service will be in a0, a1, a2, …, a6 registers in order. The following table shows the system calls that your machine must support.
System Call Number (in a7 register) | Function | Arguments |
---|---|---|
0 | exit() | a0: exit return code |
2 | putchar() | a0: char |
3 | getchar() | None |
The exit system call will quit the program and return the error code specified by a0. Recall that exiting with the error code 0 means that the program quit successfully (no error).
The putchar system call takes a char in a0. The ourlib library will use this system call to implement printf. The job of this system call is to output the character as given. DO NOT PRINT A TRAILING NEWLINE CHARACTER!
The getchar system call takes no arguments. This function will wait for one character from the user and return the character in a0 (register number 10).
You may implement other system calls, but for any invalid system call number, your machine should just handle it gracefully–in other words don’t crash the running program for an invalid system call.