# 5-stage pipeline RISC-V core
> 柳嘉祐
## study [5-stage-rv32i]( https://github.com/kinzafatim/5-Stage-RV32I )

#### 5-stage
IF:Fetch the instruction and update the program counter.
ID:Decode the instruction and read the registers.
EX:Execute the operation or compute the address.
MEM:Access memory (such as loading or storing data).
WB:Write the result back to the register.
#### register
IF_ID reg:Store the data from the IF stage and pass it to the ID stage.
ID_EX reg:Store the data from the ID stage and pass it to the EX stage.
EX_MEM reg:Store the data from the EX stage and pass it to the MEM stage.
MEM_WB reg:Store the data from the MEM stage and pass it to the WB stage.
#### control unit
```
// R type instructions (e.g., add, sub)
is(51.U) {
io.mem_write := 0.B
io.branch := 0.B
io.mem_read := 0.B
io.reg_write := 1.B
io.men_to_reg := 0.B
io.alu_operation := 0.U
io.operand_A := 0.U
io.operand_B := 0.B
io.extend := 0.U
io.next_pc_sel := 0.U
}
// I type instructions (e.g., immediate operations)
is(19.U) {
io.mem_write := 0.B
io.branch := 0.B
io.mem_read := 0.B
io.reg_write := 1.B
io.men_to_reg := 0.B
io.alu_operation := 1.U
io.operand_A := 0.U
io.operand_B := 1.B
io.extend := 0.U
io.next_pc_sel := 0.U
}
```
Classify instructions based on opcode and funct.
#### forwarding
```
add x1, x2, x3 # instruction 1
sub x4, x1, x5 # instruction 2 (depend on x1)
```
```
add x1, x2, x3 # instruction 1
add x4, x5, x6 # instruction 2
sub x7, x1, x9 # instruction 3 (depend on x1)
```
If the add instruction has not yet completed writing back to x1, and the sub instruction already requires the value of x1, a data hazard will occur.
Forwarding allows the sub instruction to directly obtain the result from the ALU output of the add instruction during the EX stage or from MEM stage, instead of waiting for the add instruction to complete the WB stage and then reading from the register.
```
when(io.EXMEM_regWr === "b1".U && io.EXMEM_rd =/= "b00000".U &&
(io.EXMEM_rd === io.IDEX_rs1.asUInt) && (io.EXMEM_rd === io.IDEX_rs2)) {
io.forward_a := "b10".U
io.forward_b := "b10".U
}.elsewhen(io.EXMEM_regWr === "b1".U && io.EXMEM_rd =/= "b00000".U &&
(io.EXMEM_rd === io.IDEX_rs2)) {
io.forward_b := "b10".U
}.elsewhen(io.EXMEM_regWr === "b1".U && io.EXMEM_rd =/= "b00000".U &&
(io.EXMEM_rd === io.IDEX_rs1)) {
io.forward_a := "b10".U
}
```
If it is certain that the value will be written back to the register, and the rd register is not zero and the MEM rd equals EX rs (1 or 2), the forwarding bit is set to 2'b10.
```
when((io.MEMWB_regWr === "b1".U) && (io.MEMWB_rd =/= "b00000".U) && (io.MEMWB_rd === io.IDEX_rs1) && (io.MEMWB_rd === io.IDEX_rs2) &&
~(io.EXMEM_regWr === "b1".U && io.EXMEM_rd =/= "b00000".U && (io.EXMEM_rd === io.IDEX_rs1) && (io.EXMEM_rd === io.IDEX_rs2))) {
io.forward_a := "b01".U
io.forward_b := "b01".U
}.elsewhen((io.MEMWB_regWr === "b1".U) && (io.MEMWB_rd =/= "b00000".U) && (io.MEMWB_rd === io.IDEX_rs2) &&
~(io.EXMEM_regWr === "b1".U && io.EXMEM_rd =/= "b00000".U && (io.EXMEM_rd === io.IDEX_rs2))){
io.forward_b := "b01".U
}.elsewhen((io.MEMWB_regWr === "b1".U) && (io.MEMWB_rd =/= "b00000".U) && (io.MEMWB_rd === io.IDEX_rs1) &&
~(io.EXMEM_regWr === "b1".U && io.EXMEM_rd =/= "b00000".U && (io.EXMEM_rd === io.IDEX_rs1))){
io.forward_a := "b01".U
}
```
If it is certain that the value will be written back to the register, the rd register is not zero, the WB rd equals EX rs (1 or 2), and it is confirmed that there is no forwarding from MEM, the forwarding bit is set to 2'b01.
| forwarding bit | forwarding from | forwarding to |
| -------- | -------- | -------- |
| 2'b00 | non | non |
| 2'b10 | mem | ex |
| 2'b01 | wb | ex |
#### alu
```
switch(io.alu_Op) {
is(ALU_ADD, ALU_ADDI, ALU_SW, ALU_LW, ALU_LUI, ALU_AUIPC) {
result := io.in_A + io.in_B
}
is(ALU_SLL, ALU_SLLI) {
result := (io.in_A.asUInt << io.in_B(4, 0)).asSInt
}
is(ALU_SLT, ALU_SLTI) {
result := Mux(io.in_A < io.in_B, 1.S, 0.S)
}
is(ALU_SLTU, ALU_SLTUI) {
result := Mux(io.in_A.asUInt < io.in_B.asUInt, 1.S, 0.S)
}
is(ALU_XOR, ALU_XORI) {
result := io.in_A ^ io.in_B
}
is(ALU_SRL, ALU_SRLI) {
result := (io.in_A.asUInt >> io.in_B(4, 0)).asSInt
}
is(ALU_OR, ALU_ORI) {
result := io.in_A | io.in_B
}
is(ALU_AND, ALU_ANDI) {
result := io.in_A & io.in_B
}
is(ALU_SUB) {
result := io.in_A - io.in_B
}
is(ALU_SRA, ALU_SRAI) {
result := (io.in_A >> io.in_B(4, 0)).asSInt
}
is(ALU_JAL, ALU_JALR) {
result := io.in_A
}
}
```
Perform calculations for each category using the ALU based on the classification.
#### hazard
```
val Rs1 = io.IF_ID_inst(19, 15)
val Rs2 = io.IF_ID_inst(24, 20)
when(io.ID_EX_memRead === 1.B && ((io.ID_EX_rd === Rs1) || (io.ID_EX_rd === Rs2))) {
io.inst_forward := true.B
io.pc_forward := true.B
io.ctrl_forward := true.B
}.otherwise {
io.inst_forward := false.B
io.pc_forward := false.B
io.ctrl_forward := false.B
}
io.inst_out := io.IF_ID_inst
io.pc_out := io.pc_in
io.current_pc_out := io.current_pc
```
This code implements Load-Use Hazard detection. When a memory load instruction has not completed its write-back and the next instruction depends on its result, it activates Forwarding or Stalling control signals to ensure data consistency and prevent instruction execution errors.
## perfcounter
> [rv32emu](https://github.com/sysprog21/rv32emu/tree/master/tests/perfcounter)
#### Build
```
git clone https://github.com/sysprog21/rv32emu.git
sudo apt install libsdl2-dev libsdl2-mixer-dev
```
#### Experimental JIT compilation
```
sudo apt-get install llvm-18
make ENABLE_JIT=1
```
result
```
gcc -march=rv32i_zicsr_zifencei -mabi=ilp32 -O2 -Wall -c -o getcycles.o getcycles.S
gcc: error: unrecognized argument in option ‘-mabi=ilp32’
gcc: note: valid arguments to ‘-mabi=’ are: ms sysv
make: *** [Makefile:15: getcycles.o] Error 1
```
1.Unsupported Compiler
You might be using the standard GCC for x86 or another platform to compile RISC-V code, but this GCC does not recognize the -march and -mabi options because these options are specific to the RISC-V target.
2.Compiler Prefix Error
The compile command might be using gcc instead of the cross-compiler, such as riscv32-unknown-elf-gcc.
#### Ask GPT and other solutions on the internet
environment
```
sudo apt update
sudo apt install gawk
sudo apt install bison flex texinfo
```
set
```
cd 5-Stage-RV32I/
git clone https://github.com/riscv/riscv-gnu-toolchain
./configure --prefix=/media/disk2/uuuwei0504/rv32emu --with-arch=rv32i_zicsr_zifencei --with-abi=ilp32cat
make
```
But encounter space-related issues
```
../.././gcc/gcc/config/riscv/thead-peephole.md:77:1: fatal error: error writing to /tmp/ccD30nlN.s: No space left on device
compilation terminated.
make[2]: *** [Makefile:1198: insn-recog.o] Error 1
make[2]: Leaving directory '/home/liu/5-stage-RV32I/riscv-gnu-toolchain/build-gcc-newlib-stage1/gcc'
make[1]: *** [Makefile:4702: all-gcc] Error 2
make[1]: Leaving directory '/home/liu/5-stage-RV32I/riscv-gnu-toolchain/build-gcc-newlib-stage1'
make: *** [Makefile:651: stamps/build-gcc-newlib-stage1] Error 2
```
## [3] [riscv-b](https://github.com/riscv/riscv-b)
```
git clone --recurse-submodules https://github.com/riscv/riscv-b.git
```
build
```
cd ./riscv-b && make build
```