# RV32IMC core
> 王彥珽
[GitHub](https://github.com/p76131416/ca-final-project)
## Environment Setup
I followed the [senior's steps](https://hackmd.io/p7eSxpLiR36j2-3g1GEmCw?both) to set up the environment, but I encountered an issue where I couldn't generate the `.asmbin` file. The problem was caused by the absence of the `riscv-none-elf` toolchain. After some investigation, I discovered that the installation instructions were actually provided after [Lab2](https://hackmd.io/@sysprog/SJAR5XMmi).
## Experiment Reproduction and Verification
The first issue encountered when reproducing last year's experiment:
```
[info] SimpleTrapTest:
[info] Single Cycle CPU with CSR and CLINT
[info] - should jump to trap handler and then return *** FAILED ***
[info] java.lang.NullPointerException:
[info] at ... ()
[info] at peripheral.InstructionROM.readAsmBinary(InstructionROM.scala:42)
[info] at peripheral.InstructionROM.<init>(InstructionROM.scala:25)
[info] at riscv.singlecycle.TestTopModule.$anonfun$instruction_rom$2(CPUTest.scala:30)
[info] at chisel3.Module$.do_apply(Module.scala:53)
[info] at riscv.singlecycle.TestTopModule.$anonfun$instruction_rom$1(CPUTest.scala:30)
[info] at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
[info] at riscv.singlecycle.TestTopModule.<init>(CPUTest.scala:30)
[info] at riscv.singlecycle.SimpleTrapTest.$anonfun$new$42(CPUTest.scala:125)
[info] at ... ()
[info] ...
```
The reason for this error is the absence of the **SimpleTrapTest** test file. Since the goal is to test the behavior of interrupts and CSR, the `hello.asmbin` file is used as a test file to serve as the side data for **SimpleTrapTest**.
(hello.asmbin maynot be the test file to serve as the side data.)(to be solved)
## RV32C Supports Extension
### Introduction to the RV32C Instruction Set
RV32C is a compressed instruction set in the RISC-V architecture. It is designed to **reduce the size of instructions**, thereby decreasing memory usage and increasing instruction density, which improves processor performance.
### Implementation Methods for RV32C

After studying the introduction to RV32C instructions, I initially planned to decode the 16-bit instructions in the InstructionDecode stage, similar to the approach used for RV32IM. However, as I progressed, the process became increasingly complex. Therefore, I decided to handle the mapping of 16-bit RV32C instructions during the **InstructionFetch** stage instead, expanding them into RV32I instructions.
### Mapping Between RV32C and RV32I Instructions
- CR-type format:
|instruction|op|funct|corresponding rv32i instr.|
|-----|-----|-----|-----|
|c.jr | 10 | 1000 | jalr x0, rs1, 0|
|c.jalr | 10 | 1001 | jalr ra, rs1, 0|
|c.mv | 10 | 1000 | add rd, x0, rs2 |
|c.add | 10 | 1001 | add rd, rd, rs2 |
|c.ebreak | 10 | 1001 | ebreak |
- CI-type format:
|instruction|op|funct|corresponding rv32i instr.|
|-----|-----|-----|-----|
|c.lwsp | 10 | 010 | lw rd, (4 * imm)(sp) |
|c.li | 01 | 010 | addi rd, x0, imm |
|c.lui | 01 | 011 | lui rd, imm |
|c.addi | 01 | 000 | addi rd, rd, imm |
|c. addi16sp | 01 | 011 | addi sp, sp, 16 * imm|
|c.slli | 10 | 000 | slli rd, rd, imm |
|c.nop | 01 | 000 | addi x0, x0, 0 |
- CSS-type format:
|instruction|op|funct|corresponding rv32i instr.|
|-----|-----|-----|-----|
|c.swsp | 10 | 110 | sw rs2, (4 * imm)(sp) |
- CIW-type format:
|instruction|op|funct|corresponding rv32i instr.|
|-----|-----|-----|-----|
|c.addi4spn | 00 | 000 | addi rd', sp, 4 * imm |
- CL-type format:
|instruction|op|funct|corresponding rv32i instr.|
|-----|-----|-----|-----|
|c.lw | 00 | 010 | lw rd', (4 * imm)(rs1') |
- CS-type format:
|instruction|op|funct|corresponding rv32i instr.|
|-----|-----|-----|-----|
|c.sw | 00 | 110 | sw rs1', (4 * imm)(rs2') |
|c.and | 01 | 10001111 | and rd', rd', rs2' |
|c.or | 01 | 10001110 | or rd', rd', rs2' |
|c.xor | 01 | 10001101 | xor rd', rd' rs2' |
|c.sub | 01 | 10001100 | sub rd', rd', rs2' |
- CB-type format:
|instruction|op|funct|corresponding rv32i instr.|
|-----|-----|-----|-----|
|c.beqz | 01 | 110 | beq rs', x0, 2 * imm |
|c.bnez | 01 | 111 | bne rs', x0, 2 * imm |
|c.srli | 01 | 100x00 | srli rd', rd', imm |
|c.srai | 01 | 100x01 | srai rd', rd', imm |
|c.andi | 01 | 100x10 | andi rd', rd', imm |
- CJ-type format:
|instruction|op|funct|corresponding rv32i instr.|
|-----|-----|-----|-----|
|c.j | 01 | 101 | jal x0, 2 * offset |
|c.jal | 01 | 001 | jal ra, 2 * offset |
## Other Instructions
I noticed that the senior did not include multiplication, division, and remainder operations in the ALU, so I added them.
In ALUControl.scala :
```
InstructionsTypeM.mul -> ALUFunctions.mul,
InstructionsTypeM.mulh -> ALUFunctions.mulh,
InstructionsTypeM.mulhsu -> ALUFunctions.mulhsu,
InstructionsTypeM.mulhum -> ALUFunctions.mulhum,
InstructionsTypeM.div -> ALUFunctions.div,
InstructionsTypeM.divu -> ALUFunctions.divu,
InstructionsTypeM.rem -> ALUFunctions.rem,
InstructionsTypeM.remu -> ALUFunctions.remu
```
In ALU.scala :
```
is(ALUFunctions.mul) {
io.result := (io.op1 * io.op2)(31, 0)
}
is(ALUFunctions.mulh) {
io.result := (io.op1 * io.op2)(63, 32)
}
is(ALUFunctions.mulhsu) {
io.result := (io.op1 * unsignop2)(63, 32)
}
is(ALUFunctions.mulhum) {
io.result := (unsignop1 * unsignop2)(63, 32)
}
is(ALUFunctions.div) {
io.result := io.op1 / io.op2
}
is(ALUFunctions.divu) {
io.result := unsignop1 / unsignop2
}
is(ALUFunctions.rem) {
io.result := io.op1 % io.op2
}
is(ALUFunctions.remu) {
io.result := unsignop1 % unsignop2
}
```
After implementing RV32MC, it successfully passed all the original test cases.
## RISC-V Architecture Test
### Introduction to riscv-arch-test
The riscv-arch-test framework is an official RISC-V International project designed to verify compliance with the RISC-V ISA specifications. It provides a standardized set of architectural tests to ensure that RISC-V implementations conform to the expected behavior as defined in the ISA manual.
using `git clone` to get `riscv-arch-test`:
```
git clone https://github.com/riscv-non-isa/riscv-arch-test.git
```
### Testing with RISCOF
Follow this [tutorial](https://riscof.readthedocs.io/en/latest/installation.html) to install.
After completing the installation, we use following instruction to generate the configuration file.
```
riscof setup --dutname=mycpu --refname=spike
```
Here, `dutname` refers to testing model, while `refname` refers to the reference model.
```
riscof validateyaml --config=config.ini
riscof testlist --config=config.ini --suite=riscv-arch-test/riscv-test-suite/ --env=riscv-arch-test/riscv-test-suite/env
```
Subsequently, the files are validated, and the required test database is generated.
However, certain modifications are required in `riscof_mycpu.py`. First, use Verilator to generate the `VTop` file, and then update the path of `dut.exe` to point to the location of `VTop`.
```
# self.dut_exe = os.path.join(config['PATH'] if 'PATH' in config else "","mycpu")
self.dut_exe = "/home/test/ca-final-project/verilog/verilator/obj_dir/VTop"
```
Perform the test.
```
riscof run --config=config.ini --suite=riscv-arch-test/riscv-test-suite/ --env=riscv-arch-test/riscv-test-suite/env
```
After executing the test, the following issues arose:
```
ERROR | Signature file : /home/test/ca-final-project/riscof_work/rv32i_m/C/src/cadd-01.S/dut/DUT-mycpu.signature does not exist
```
It is speculated that this issue arises because the compile command in the .py file still needs modification, and sbt must also support generating the required files.