# Chisel boot camp
contributed by < [JoshuaLee0321](https://github.com/JoshuaLee0321/ca2023-lab3) >
# Checklist and implementations
I used this checklist to make myself clear for every step I make on this homework
- [x] Complete the exercises in the [lab 3](https://hackmd.io/@sysprog/r1mlr3I7p). (Fork from [lab 3 GitHub repository](https://github.com/sysprog21/ca2023-lab3).)
- [x] For the lab 3, provide a concise summary of the various test cases, outlining the aspects of the CPU they evaluate, the techniques employed for loading test program instructions, and the outcomes of these test cases. For signals involved in filling in the blanks, use the testing framework to output waveform diagrams and describe the changes in key signals of corresponding components when executing different instructions.
## Summaries on various test cases
### IF stage
In this stage, our primary objective is to ascertain the validity of the input instruction for execution. If the instruction is deemed invalid, the hardware must enter a halted state and refrain from further action.
Referring to the provided `//lab3(instructionFetch) ...`, the decision of whether to jump or not is crucial. This is precisely why we observe the usage of `Random.nextInt(2)` in `InstructionFetchTest.scala`. Consequently, the key task is to adjust the conditions for deciding whether to perform a jump or not in the `InstructionFetch.scala`. The relevant code for this decision-making process can be found in the corresponding sections of the test code.
```scala=
case 0 => // no jump
cur = pre + 4
c.io.jump_flag_id.poke(false.B)
c.clock.step()
c.io.instruction_address.expect(cur)
pre = pre + 4
case 1 => // jump
c.io.jump_flag_id.poke(true.B)
c.io.jump_address_id.poke(entry)
c.clock.step()
c.io.instruction_address.expect(entry)
pre = entry
```
`1 and 7` line is whether the instruction is jump or not
if `no jump` then we need to compare if the output `(next Instruction Addr)` is the previous value plus 4
On the other hand, we if we jump, we can also check if `IF` stage jumped to the correct position, that is, the previous one.
```bash
$ sbt "testOnly riscv.singlecycle.InstructionFetchTest"
[info] welcome to sbt 1.9.7 (Ubuntu Java 11.0.21)
[info] loading settings for project ca2023-lab3-build from plugins.sbt ...
[info] loading project definition from /home/joshua/ca2023-lab3/project
[info] loading settings for project root from build.sbt ...
[info] set current project to mycpu (in build file:/home/joshua/ca2023-lab3/)
[info] InstructionFetchTest:
[info] InstructionFetch of Single Cycle CPU
[info] - should fetch instruction
[info] Run completed in 4 seconds, 526 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: 6 s, completed Nov 30, 2023, 9:40:57 PM
```
### ID stage
Decode is a bit of tricky here, we need to output the corresponding signal for the other hardward to use.
But other than `memory read/write` signal are not implemented, all signals are implemented by previous author.
```bash
$ sbt "testOnly riscv.singlecycle.InstructionDecoderTest"
[info] welcome to sbt 1.9.7 (Ubuntu Java 11.0.21)
[info] loading settings for project ca2023-lab3-build from plugins.sbt ...
[info] loading project definition from /home/joshua/ca2023-lab3/project
[info] loading settings for project root from build.sbt ...
[info] set current project to mycpu (in build file:/home/joshua/ca2023-lab3/)
[info] compiling 1 Scala source to /home/joshua/ca2023-lab3/target/scala-2.13/classes ...
[warn] /home/joshua/ca2023-lab3/src/main/scala/riscv/core/InstructionDecode.scala:150:19: method apply in object MuxLookup is deprecated (since Chisel 3.6): Use MuxLookup(key, default)(mapping) instead
[warn] val immediate = MuxLookup(
[warn] ^
[warn] one warning found
[info] InstructionDecoderTest:
[info] InstructionDecoder of Single Cycle CPU
[info] - should produce correct control signal
[info] Run completed in 4 seconds, 578 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: 11 s, completed Nov 30, 2023, 9:56:44 PM
```
### EX stage
In the execution stage, our sole responsibility is to determine `ALUOPs`, as evidenced by the corresponding unit test in ExecuteTest:
```scala
// beq test
c.io.instruction.poke(0x00208163L.U) // pc + 2 if x1 === x2
c.io.instruction_address.poke(2.U)
c.io.immediate.poke(2.U)
c.io.aluop1_source.poke(1.U)
c.io.aluop2_source.poke(1.U)
c.clock.step()
```
After inputting the instruction, the test plan assesses the validity of `ALUOP1` and `ALUOP2` in the `BEQ` case.
### CPU wire
Finally, we just need to wire up the previous designed data path according to this graph

The only wire that hasn't been connected is in the CPU section, so we can simply wire it up and be ready to go.
```bash
$ sbt test
[info] welcome to sbt 1.9.7 (Ubuntu Java 11.0.21)
[info] loading settings for project ca2023-lab3-build from plugins.sbt ...
[info] loading project definition from /home/joshua/ca2023-lab3/project
...
[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: 19 s, completed Nov 30, 2023, 10:20:27 PM
```
- [x] Complete the exercises from Part 1 to Part 3.6 in the [Chisel tutorial](https://github.com/ucb-bar/chisel-tutorial).
> Already done with and finished all practices, you can find it in [JoshuaLee0321/chisel-bootcamp](https://github.com/JoshuaLee0321/chisel-bootcamp)
## Describe the operation of "Hello World in Chisel" and enhance it by incorporating logic circuit.
According to [Lab3: Construct a single-cycle CPU with Chisel](https://hackmd.io/CvrOEhLKSxOJKdblTjhEqQ?both)
The hello world program looks like the following
```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
}
```
Just as [Chisel-bootcamp](https://github.com/JoshuaLee0321/chisel-bootcamp/blob/master/2.1_first_module.ipynb) suggested, all chisel HDL code must inherit `Module` for convient bundle provided by chisel.
On the line `2` we can see the `Hello` module defines a hardward io that contains only one output wire.
```scala
val io = IO(new Bundle {
val led = Output(UInt(1.W))
})
```
It also defines two separate registers for recording the state of this circuit
```scala
val CNT_MAX = (50000000 / 2 - 1).U
val cntReg = RegInit(0.U(32.W))
val blkReg = RegInit(0.U(1.W))
```
Here comes the core part, if the counter reaches a defined value which is `val CNT_MAX = (50000000 / 2 - 1).U`, the output signal will turn on or off depending on the previous state.
In summary, this Chisel module implements a simple counter that increments on each clock cycle. When the counter reaches a specified maximum value `CNT_MAX`, it resets and toggles a one-bit register `blkReg`. The toggled value is then output to led. This kind of design is often used in digital circuits to generate periodic signals or control signals based on clock cycles.
---
## Modify the handwritten RISC-V assembly code in Homework 2 to ensure it functions correctly on "MyCPU." Keep the modified code in the `csrc` directory.
I modified the code on homework 2 and corresponding makefile in order to run on our Risc-v module
```c
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
uint16_t count_leading_zeros(uint64_t x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
x |= (x >> 32);
/* count ones (population count) */
x -= ((x >> 1) & 0x5555555555555555 /* Fill this! */);
x = ((x >> 2) & 0x3333333333333333) + (x & 0x3333333333333333 /* Fill this! */);
x = ((x >> 4) + x) & 0x0f0f0f0f0f0f0f0f;
x += (x >> 8);
x += (x >> 16);
x += (x >> 32);
return (64 - (x & 0x7f));
}
uint32_t cvrt_uint32(uint64_t x)
{
return (uint32_t)x;
}
uint16_t cvrt_uint16(uint64_t x)
{
return (uint16_t)x;
}
int main()
{
uint64_t test_data1 = 0x00003567;
*((volatile uint16_t *) (2)) = cvrt_uint16(test_data1);
return 0;
}
```
```bash
# Makefile
BINS = \
hw3.asmbin \
fibonacci.asmbin \
hello.asmbin \
mmio.asmbin \
quicksort.asmbin \
sb.asmbin
```
finally, we can use the following code
```scala
class hw2Test extends AnyFlatSpec with ChiselScalatestTester {
behavior.of("Single Cycle CPU")
it should "binarize the pixel" in {
test(new TestTopModule("hw3.asmbin")).withAnnotations(TestAnnotations.annos) { c =>
for (i <- 1 to 500) {
c.clock.step(100)
c.io.mem_debug_read_address.poke((i * 4).U) // Avoid timeout
}
c.io.mem_debug_read_address.poke(2.U)
c.clock.step()
c.io.mem_debug_read_data.expect(0x0.U)
c.io.mem_debug_read_address.poke(4.U)
c.clock.step()
c.io.mem_debug_read_data.expect(0x0.U)
c.io.mem_debug_read_address.poke(6.U)
c.clock.step()
c.io.mem_debug_read_data.expect(0x0.U)
c.io.mem_debug_read_address.poke(8.U)
c.clock.step()
c.io.mem_debug_read_data.expect(0xff.U)
c.io.mem_debug_read_address.poke(10.U)
c.clock.step()
c.io.mem_debug_read_data.expect(0xff.U)
}
}
}
```