contributed by < KuanYuan0530
>
Reference: Lab3: Construct a single-cycle RISC-V CPU with Chisel
$ sudo apt install build-essential verilator gtkwave
# Install sdkman
$ curl -s "https://get.sdkman.io" | bash
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
# Install Eclipse Temurin JDK 11
$ sudo sdk install java 11.0.21-tem
$ sdk install sbt
Getting the Repository:
$ git clone https://github.com/ucb-bar/chisel-tutorial
$ cd chisel-tutorial
$ git checkout release
Before testing your system, ensure that you have sbt (the Scala build tool) installed.
$ sbt run
Reference output:
[info] Loading project definition from /home/chen/chisel-tutorial/project
[info] Loading settings for project chisel-tutorial from build.sbt ...
[info] Set current project to chisel-tutorial (in build file:/home/chen/chisel-tutorial/)
[info] running hello.Hello
[info] [0.001] Elaborating design...
[info] [0.140] Done elaborating.
Computed transform order in: 385.6 ms
Total FIRRTL Compile Time: 720.5 ms
End of dependency graph
Circuit state created
[info] [0.002] SEED 1701259036483
test Hello Success: 1 tests passed in 6 cycles taking 0.026425 seconds
[info] [0.007] RAN 1 CYCLES PASSED
# Add Docker's official GPG key:
$ sudo apt-get update
$ sudo apt-get install ca-certificates curl gnupg
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the repository to Apt sources:
$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
$ sudo docker run hello-world
Result:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
719385e32844: Pull complete
Digest: sha256:c79d06dfdfd3d3eb04cafd0dc2bacab0992ebc243e083cabe208bac4dd7759e0
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
$ sudo docker run -it --rm -p 8888:8888 sysprog21/chisel-bootcamp
To access the notebook, open this file in a browser:
file:///home/bootcamp/.local/share/jupyter/runtime/nbserver-7-open.html
Or copy and paste one of these URLs:
http://27ff70194fdb:8888/?token=54ecb0708eff8540d0bff19602df817e8742f54166c22307
or http://127.0.0.1:8888/?token=54ecb0708eff8540d0bff19602df817e8742f54166c22307
class Hello extends Module {
val io = IO(new Bundle {
val led = Output(UInt(1.W))
})
val CNT_MAX = (50000000 / 2 - 1).U;
val cntReg = RegInit(0.U(32.W))
val blkReg = RegInit(0.U(1.W))
cntReg := cntReg + 1.U
when(cntReg === CNT_MAX) {
cntReg := 0.U
blkReg := ~blkReg
}
io.led := blkReg
}
CNT_MAX is a constant value that represents the maximum count of the counter.cntReg is a temporary register for counting, initialized to 32-bit 0, and incremented by 1 at each clock cycle. blkReg is a flag indicating whether the maximum count is reached, initialized to 1 bit of 0. When the count reaches the maximum value, cntReg will be reset to 0, blkReg will be inverted, and blkReg will be output give to io.led.
class Hello extends Module {
val io = IO(new Bundle {
val led = Output(UInt(1.W))
})
val CNT_MAX = (50000000 / 2 - 1).U;
val cntReg = RegInit(0.U(32.W))
val blkReg = RegInit(0.U(1.W))
cntReg := Mux(cntReg === CNT_MAX, 0.U, cntReg + 1.U)
blkReg := Mux(cntReg === CNT_MAX, ~blkReg, blkReg)
io.led := blkReg
}
Enhance Hello World by incorporating logic circuit Mux() instead of when().
Get the repository:
$ git clone https://github.com/sysprog21/ca2023-lab3
$ cd ca2023-lab3
To simulate and run tests for this project, execute the following commands under the ca2023-lab3 directory.
$ sbt test
Result:
[info] *** 6 TESTS FAILED ***
[error] Failed tests:
[error] riscv.singlecycle.InstructionDecoderTest
[error] riscv.singlecycle.ByteAccessTest
[error] riscv.singlecycle.InstructionFetchTest
[error] riscv.singlecycle.ExecuteTest
[error] riscv.singlecycle.FibonacciTest
[error] riscv.singlecycle.QuicksortTest
[error] (Test / test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 67 s (01:07), completed Nov 29, 2023, 9:02:34 PM
Based on the above error message, we need to complete the following code.
src/main/scala/riscv/core/InstructionFetch.scala
src/main/scala/riscv/core/InstructionDecode.scala
src/main/scala/riscv/core/Execute.scala
src/main/scala/riscv/core/CPU.scala
We can run a single test, such as running only InstructionDecoderTest, execute the following command:
$ sbt "testOnly riscv.singlecycle.InstructionDecoderTest"
Execute following command to generate waveform:
$ WRITE_VCD=1 sbt test
Afterward, open .vcd files in various subdirectories under the test_run_dir directory with GTKWave.
$ sbt "testOnly riscv.singlecycle.InstructionFetchTest"
Test result:
[info] welcome to sbt 1.9.7 (Eclipse Adoptium Java 11.0.21)
[info] loading settings for project ca2023-lab3-build from plugins.sbt ...
[info] loading project definition from /home/chen/ca2023-lab3/project
[info] loading settings for project root from build.sbt ...
[info] set current project to mycpu (in build file:/home/chen/ca2023-lab3/)
[info] InstructionFetchTest:
[info] InstructionFetch of Single Cycle CPU
[info] - should fetch instruction
[info] Run completed in 10 seconds, 718 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 13 s, completed Dec 1, 2023, 8:01:49 AM
Result:
Based on the above waveform, when io.jump_flag_id is 0, it indicates that no jump has occurred. In this case, the instruction pc := pc + 4 will be executed, causing the program counter (pc) to point to the next instruction. When io.jump_flag_id is 1, it signifies that a jump has occurred. Consequently, the pc will be set equal to io.jump_address_id, directing it to the target instruction of the jump (where io.instruction_address corresponds to the signal of the program counter).
$ sbt "testOnly riscv.singlecycle.InstructionDecoderTest"
Test result:
[info] InstructionDecoderTest:
[info] InstructionDecoder of Single Cycle CPU
[info] - should produce correct control signal
[info] Run completed in 13 seconds, 214 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 31 s, completed Dec 1, 2023, 12:26:27 PM
opcode list:
object InstructionTypes {
val L = "b0000011".U
val I = "b0010011".U
val S = "b0100011".U
val RM = "b0110011".U
val B = "b1100011".U
}
Result:
Using a Mux based on the value of the opcode, it is determined whether a read or write operation is required. When the instruction belongs to the Load type, io.memory_read_enable is set to 1; otherwise, it is set to 0. When the instruction belongs to the Store type, io.memory_write_enable is set to 1; otherwise, it is set to 0.
Therefore, based on the waveform and opcode list provided, when opcode is b0000011, io.memory_read_enable is set to 1. When opcode is b0100011, io.memory_write_enable is set to 1. In all other cases, both io.memory_read_enable and io.memory_write_enable are set to 0.
$ sbt "testOnly riscv.singlecycle.ExecuteTest"
Test result:
[info] ExecuteTest:
[info] Execution of Single Cycle CPU
[info] - should execute correctly
[info] Run completed in 9 seconds, 828 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 21 s, completed Dec 1, 2023, 2:31:21 PM
object ALUOp1Source {
val Register = 0.U(1.W)
val InstructionAddress = 1.U(1.W)
}
object ALUOp2Source {
val Register = 0.U(1.W)
val Immediate = 1.U(1.W)
}
Result:
Based on the alu_ctrl, the operation to be performed by the ALU is determined, and the value is assigned to alu.io.func. Subsequently, based on the InstructionDecode, it is decided whether the first operand for the ALU is from the Register or the Instruction Address, and whether the second operand is from the Register or the Immediate.
Therefore, according to the given waveform, when io.aluop1_source.asBool is 1, it indicates that alu.io.op1 equal InstructionAddress. When io.aluop1_source.asBool is 0, then alu.io.op1 equal io.reg1_data. Similarly, when io.aluop2_source.asBool is 1, it signifies that alu.io.op2 equal io.immediate. When io.aluop2_source.asBool is 0, then alu.io.op2 equal io.reg2_data.
$ sbt "testOnly riscv.singlecycle.FibonacciTest"
[info] set current project to mycpu (in build file:/home/chen/ca2023-lab3/)
[info] compiling 1 Scala source to /home/chen/ca2023-lab3/target/scala-2.13/classes ...
[info] FibonacciTest:
[info] Single Cycle CPU
[info] - should recursively calculate Fibonacci(10)
[info] Run completed in 16 seconds, 161 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 29 s, completed Dec 1, 2023, 3:56:56 PM
$ sbt "testOnly riscv.singlecycle.QuicksortTest"
[info] set current project to mycpu (in build file:/home/chen/ca2023-lab3/)
[info] QuicksortTest:
[info] Single Cycle CPU
[info] - should perform a quicksort on 10 numbers
[info] Run completed in 16 seconds, 895 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 21 s, completed Dec 1, 2023, 3:59:46 PM
$ sbt test
Result:
[info] welcome to sbt 1.9.7 (Eclipse Adoptium Java 11.0.21)
[info] loading settings for project ca2023-lab3-build from plugins.sbt ...
[info] loading project definition from /home/chen/ca2023-lab3/project
[info] loading settings for project root from build.sbt ...
[info] set current project to mycpu (in build file:/home/chen/ca2023-lab3/)
[info] InstructionFetchTest:
[info] InstructionFetch of Single Cycle CPU
[info] - should fetch instruction
[info] FibonacciTest:
[info] Single Cycle CPU
[info] - should recursively calculate Fibonacci(10)
[info] QuicksortTest:
[info] Single Cycle CPU
[info] - should perform a quicksort on 10 numbers
[info] ExecuteTest:
[info] Execution of Single Cycle CPU
[info] - should execute correctly
[info] RegisterFileTest:
[info] Register File of Single Cycle CPU
[info] - should read the written content
[info] - should x0 always be zero
[info] - should read the writing content
[info] InstructionDecoderTest:
[info] InstructionDecoder of Single Cycle CPU
[info] - should produce correct control signal
[info] ByteAccessTest:
[info] Single Cycle CPU
[info] - should store and load a single byte
[info] Run completed in 52 seconds, 788 milliseconds.
[info] Total number of tests run: 9
[info] Suites: completed 7, aborted 0
[info] Tests: succeeded 9, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 55 s, completed Dec 1, 2023, 4:09:15 PM