# My RISC-V CPU
> CA Assignment3 <[Assignment3: Your Own RISC-V CPU](https://hackmd.io/@sysprog/2025-arch-homework3)>
> CA Lab3 <[Lab3: Construct a RISC-V CPU with Chisel](https://hackmd.io/@sysprog/B1Qxu2UkZx)>
## Basic Setting
### Install sbt on Linux
```bash=
curl -s "https://get.sdkman.io" | bash
source "/home/ianrwan/.sdkman/bin/sdkman-init.sh"
sdk install java 11.0.29-tem
sdk install sbt
```
In `line 2`, here is a `.` in the folder of `.sdkman`.
It means "Hidden Folder", so we won't see the existence of this folder if we type `ls`。
:::info
1. If we turn on a new terminal, we have to retype
`source "/home/ianrwan/.sdkman/bin/sdkman-init.sh"` again.
Otherwise the commands `sbt` won't work.
2. If we want to use `sbt` to test our process like `sbt test`. We have to make sure `build.sbt` is in our folder. Otherwise it won't work.
**Error Message**
```
[error] Neither build.sbt nor a 'project' directory in the current directory: /home/ianrwan/ca2025-mycpu/1-single-cycle
[error] run 'sbt new', touch build.sbt, or run 'sbt --allow-empty'.
[error]
[error] To opt out of this check, create /home/ianrwan/.config/sbt/sbtopts with:
[error] --allow-empty
```
:::
### Install Chisel
```bash=
git clone https://github.com/ucb-bar/chisel-tutorial
cd chisel-tutorial
git checkout release
sbt run
```
We have to make sure JAVA version is `11.0.29`, or it won't work correctly. We can use the commands `java -version` to check the current JAVA version.
### Install and Boot Docker
The offical site of Docker installation -> [dockerdocs](https://docs.docker.com/engine/install/ubuntu/)
```bash=
sudo docker run -it --rm -p 8888:8888 sysprog21/chisel-bootcamp
```
If we don't type any IP before `8888:8888`, it will mean `0.0.0.0:8888:8888`, and `0.0.0.0` means all the internet interface.
If we want to assign a specific internet interface, we can directly type the specific IP like `192.168.142.129:8888:8888`. Afterwards we turn on a remote connection terminal (or browser) to connect into this IP.
**Connection Works Correctly**

## Enhance Hello World in Chisel
### The Original Scala Code
```scala=
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
}
```
## Download ca2025-mycpu
```bash=
git clone https://github.com/sysprog21/ca2025-mycpu
cd ca2025-mycpu
```
The folders and files under `ca2025-mycpu`.
```
~/ca2025-mycpu
├── 0-minimal
├── 1-single-cycle
├── 2-mmio-trap
├── 3-pipeline
├── build.sbt
├── common
├── LICENSE
├── Makefile
├── project
├── README.md
├── scripts
├── target
├── test_run_dir
└── tests
```
`build.sbt` is in this folder, so we can use `sbt test` to test our process.
If we look into `build.sbt` we can see it will test all of `0-minimal`, `1-single-cycle`, `2-mmio-trap`, `3-pipeline` in one time.
According to the assinment requirement, we need to complete three projects, `1-single-cycle`, `2-mmio-trap`, `3-pipeline` core parts.
Their cores are in `~/ca2025-mycpu/(one of the projects folder name)/src/main/scala/riscv/core/`.
## Use Verilator to Check Waveform
### Get The .vcd File from Verilator
We will take example from `1-single-cycle`.
In `1-single-cycle`, we can see `Makefile` inside it, and `Makefile` contains `verilator` for `make` command.
```bash=
make verilator
./run-verilator.sh -instruction src/main/resources/fibonacci.asmbin -time 2000 -vcd dump.vcd
```
This will generate a `dump.vcd` file in the current folder.
### Use Hexdump to Look into Binary File
We can switch to the `src/main/resources` folder and check what inside `fibonacci.asmbin` is.
```bash=
cd src/main/resources
hexdump fibonacci.asmbin | head -1
```
In here, `hexdump` can help us read the binary file, and we can get the output.
> `head -1` means only the first line will be displayed on the screen. The same as `head -1` if we want to see the second line on the screen we can type `head -2`, also third line is `head -3`, and so on.
The first line output in `fibonacci.asmbin`:
```
0000000 1197 0000 8193 9081 1137 0000 0297 0000
```
`.asmbin` file only contains instructions, so the representation is instruction format in binary of instustions. We know that RISC-V instructions are 32 bits, so `1197 0000` can be represented as an instruction, `8193 9081` is also an instruction, and so on.
> `0000000` is file offset, it's not in the `.asmbin` file.
If we wanna see the waveform we can go back to the folder where `dump.vcd` is, and use `gtkwave` to see the waveform.
```bash=
gtkwave dump.vcd
```
In here, `io_instruction[31:0]` represent to the wire of instruction, and we can see something different when we look into the waveform.

Look at `io_instruction[31:0]`, we can see `00001197` is fetched into the RISC-V single cycle machine, but what we see in `fibonacci.asmbin` is `11970000`, it might be confused. The following context will show what the happen is.
### Discover Little-endian between Waveform and Binary File
We go back to the folder `src/main/resources` to use `xxd` to look into `fibonacci.asmbin`.
```bash=
xxd fibonacci.asmbin
```
The output will be
```
00000000: 9711 0000 9381 8190 3711 0000 9702 0000 ........7.......
00000010: 9382 c20f 1703 0000 1303 430f 63f6 6200 ..........C.c.b.
00000020: 23a0 0200 9102 ddbf 9702 0000 9382 020e #...............
00000030: 1703 0000 1303 830d 63f6 6200 23a0 0200 ........c.b.#...
00000040: 9102 ddbf 4120 01a0 1301 01fe 232e 1100 ....A ......#...
00000050: 232c 8100 232a 9100 1304 0102 2326 a4fe #,..#*......#&..
00000060: 0327 c4fe 9307 1000 6308 f700 0327 c4fe .'......c....'..
00000070: 9307 2000 6316 f700 9307 1000 6f00 0003 .. .c.......o...
00000080: 8327 c4fe 9387 f7ff 1385 0700 eff0 dffb .'..............
00000090: 9304 0500 8327 c4fe 9387 e7ff 1385 0700 .....'..........
000000a0: eff0 9ffa 9307 0500 b387 f400 1385 0700 ................
000000b0: 8320 c101 0324 8101 8324 4101 1301 0102 . ...$...$A.....
000000c0: 6780 0000 1301 01ff 2326 1100 2324 8100 g.......#&..#$..
000000d0: 2322 9100 1304 0101 9304 4000 1305 a000 #"........@.....
000000e0: eff0 9ff6 9307 0500 23a0 f400 9307 0000 ........#.......
000000f0: 1385 0700 8320 c100 0324 8100 8324 4100 ..... ...$...$A.
00000100: 1301 0101 6780 0000 ....g...
```
> `00000000`, `00000001`... is file offset.
According to the instruction format `00001197` generated by the waveform. The **most significant byte** is `00`, and the **least significat byte** is `97`, but if we look into the output from `xxd`, it will show `97110000` in the first line. It means that the machine is **little-endian**.
And if we use `hexdump` to see the binary file, it will automatically convert each 2 bypes to **big-endian**. That is why `hexdump` shows `11970000`.
## Work Nyancat on MMIO Trap RISC-V CPU
If we look into the folder in `2-mmio-trap`, we can see `Makefile` is in here. We can see `demo` which is in `Makefile` can turn on verilator and start the VGA to demo the `Nyancat` animation.
```bash=
make demo
```
**The output result after `make demo`**:
{%youtube NUgWSzoMfHY %}
## View Pipeline
If we look into the folder in `3-pipeline`, we can see `Makefile` is in here. Here is `test` inside `Makefile`, so we can type the commands following:
```bash=
make test
```
### Pipeline Test
The test result is all passed.
```
[info] PipelineRegisterTest:
[info] Pipeline Register
[info] - should be able to stall and flush
[info] Run completed in 2 minutes, 17 seconds.
[info] Total number of tests run: 29
[info] Suites: completed 3, aborted 0
[info] Tests: succeeded 29, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 138 s (02:18), completed Dec 21, 2025, 3:19:11 PM
```