# 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** ![image](https://hackmd.io/_uploads/r1c-U55GZe.png) ## 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. ![image](https://hackmd.io/_uploads/S13M042Gbx.png) 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 ```