# 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 ![rv32c](https://hackmd.io/_uploads/B11lA0sD1l.png) 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.