RV32IMC core

王彥珽

GitHub

Environment Setup

I followed the senior's steps 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.

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

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

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 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.