Try   HackMD

Assignment3: single-cycle RISC-V CPU

contributed by <linyu0425>

Environment

  • OS : ubuntu 22.04
  • Install the dependent packages
$ sudo apt install build-essential verilator gtkwave
  • Installing sbt from SDKMAN
$ sdk install java $(sdk list java | grep -o "\b8\.[0-9]*\.[0-9]*\-tem" | head -1)
$ sdk install sbt
  • Ubuntu and other Debian-based distributions
sudo apt-get update
sudo apt-get install apt-transport-https curl gnupg -yqq
echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list
echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | sudo tee /etc/apt/sources.list.d/sbt_old.list
curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo -H gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/scalasbt-release.gpg --import
sudo chmod 644 /etc/apt/trusted.gpg.d/scalasbt-release.gpg
sudo apt-get update
sudo apt-get install sbt

MyCPU

InstuctionFetch.scala

Because the output section is still pending for the instruction_address part, and the space section involves io.instruction_valid, so the space part only needs to be determined based on io.jump_flag_id whether it is a branch or pc+4.

InstructionDecode.scala

Only memory_read and memory_write are left without values, so it is only necessary to use a MUX to determine the type of opcode and decide whether memory access is required.

Execute.scala

Just set up the inputs required for the ALU. Determine op1 based on aluop1_source, decide op2 based on aluop2_source, and determine func from the output of aluctrl.

CPU.scala

Connect the data from the ID stage to the EXE stage.

MyCPU testing

$ make test
sbt test
[info] welcome to sbt 1.9.7 (Temurin Java 1.8.0_392)
[info] loading settings for project ca2023-lab3-build from plugins.sbt ...
[info] loading project definition from /home/linyu/chisel-tutorial/ca2023-lab3/project
[info] loading settings for project root from build.sbt ...
[info] set current project to mycpu (in build file:/home/linyu/chisel-tutorial/ca2023-lab3/)
[info] compiling 1 Scala source to /home/linyu/chisel-tutorial/ca2023-lab3/target/scala-2.13/test-classes ...
[info] done compiling
[info] FibonacciTest:
[info] Single Cycle CPU
[info] - should recursively calculate Fibonacci(10)
[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] 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] ByteAccessTest:
[info] Single Cycle CPU
[info] - should store and load a single byte
[info] InstructionDecoderTest:
[info] InstructionDecoder of Single Cycle CPU
[info] - should produce correct control signal
[info] InstructionFetchTest:
[info] InstructionFetch of Single Cycle CPU
[info] - should fetch instruction
[info] Run completed in 17 seconds, 631 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: 23 s, completed Dec 1, 2023 11:09:17 PM

Modify Assembly code

  • As the storage in the assembly code of HW2 is already stored in two specific registers, it is only necessary to check whether the answers in these two registers are correct.
  • Assembly Code
li s2, 0 #s2: high 32 of number li s3, 0 #s3: low 32 of number . . . add s2, s2, s6 mv s3, s7

Modify Makefile

Add ' main.asmbin \ ' to BINS

BINS = \ main.asmbin \ fibonacci.asmbin \ hello.asmbin \ mmio.asmbin \ quicksort.asmbin \ sb.asmbin

Modify CPUTest.scala

In CPUTest.scala, add the following code:

class main extends AnyFlatSpec with ChiselScalatestTester { behavior.of("Single Cycle CPU") it should "answer is s2 = 0x1234540a , s3 = 0x8f5c3d98." in { test(new TestTopModule("main.asmbin")).withAnnotations(TestAnnotations.annos) { c => for (i <- 1 to 50) { c.clock.step(100) c.io.mem_debug_read_address.poke((i * 4).U) // Avoid timeout } c.io.regs_debug_read_address.poke(18.U) c.io.regs_debug_read_data.expect(0x1234540a.U) c.io.regs_debug_read_address.poke(19.U) c.io.regs_debug_read_data.expect(0x8f5c3d98.U) } } }

After make test:

[info] main: [info] Single Cycle CPU [info] - should answer is s2 = 0x1234540a , s3 = 0x8f5c3d98. *** FAILED *** [info] java.lang.IllegalArgumentException: requirement failed: UInt literal -1889780328 is negative [info] at scala.Predef$.require(Predef.scala:337) [info] at chisel3.internal.firrtl.ULit.<init>(IR.scala:150) [info] at chisel3.UIntFactory.Lit(UIntFactory.scala:21) [info] at chisel3.UIntFactory.Lit$(UIntFactory.scala:20) [info] at chisel3.package$UInt$.Lit(package.scala:182) [info] at chisel3.package$fromBigIntToLiteral.U(package.scala:51) [info] at riscv.singlecycle.main.$anonfun$new$40(CPUTest.scala:127) [info] at riscv.singlecycle.main.$anonfun$new$40$adapted(CPUTest.scala:119) [info] at chiseltest.internal.GenericBackend.$anonfun$run$1(GenericBackend.scala:170) [info] at chiseltest.internal.ThreadedBackend$TesterThread$$anon$1.$anonfun$run$1(ThreadedBackend.scala:495) [info] ...
  • [info] java.lang.IllegalArgumentException: requirement failed: UInt literal -1889780328 is negative.
  • I think that in the process of converting to a positive integer, a negative value occurred, which may be due to overflow. Therefore, simply changing it to 0x1234540aL.U should prevent overflow.

Modify CPUTest.scala :

class main extends AnyFlatSpec with ChiselScalatestTester { behavior.of("Single Cycle CPU") it should "answer is s2 = 0x1234540a , s3 = 0x8f5c3d98." in { test(new TestTopModule("main.asmbin")).withAnnotations(TestAnnotations.annos) { c => for (i <- 1 to 50) { c.clock.step(100) c.io.mem_debug_read_address.poke((i * 4).U) // Avoid timeout } c.io.regs_debug_read_address.poke(18.U) c.io.regs_debug_read_data.expect(0x1234540aL.U) c.io.regs_debug_read_address.poke(19.U) c.io.regs_debug_read_data.expect(0x8f5c3d98L.U) } } }

After make test:

sbt test [info] welcome to sbt 1.9.7 (Temurin Java 1.8.0_392) [info] loading settings for project ca2023-lab3-build from plugins.sbt ... [info] loading project definition from /home/linyu/chisel-tutorial/ca2023-lab3/project [info] loading settings for project root from build.sbt ... [info] set current project to mycpu (in build file:/home/linyu/chisel-tutorial/ca2023-lab3/) [info] compiling 1 Scala source to /home/linyu/chisel-tutorial/ca2023-lab3/target/scala-2.13/test-classes ... [info] done compiling [info] InstructionFetchTest: [info] InstructionFetch of Single Cycle CPU [info] - should fetch instruction [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] InstructionDecoderTest: [info] InstructionDecoder of Single Cycle CPU [info] - should produce correct control signal [info] main: [info] Single Cycle CPU [info] - should answer is s2 = 0x1234540a , s3 = 0x8f5c3d98. [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] ByteAccessTest: [info] Single Cycle CPU [info] - should store and load a single byte [info] FibonacciTest: [info] Single Cycle CPU [info] - should recursively calculate Fibonacci(10) [info] Run completed in 19 seconds, 659 milliseconds. [info] Total number of tests run: 10 [info] Suites: completed 8, aborted 0 [info] Tests: succeeded 10, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [success] Total time: 24 s, completed Dec 2, 2023 12:01:22 AM

Waveform Verification

Answer

  • write_data = 0x12345409
  • reg_addr = 18 //s2
  • write_enable = 1
    image
  • write_data = 0x8f5c3d98
  • reg_addr = 19 //s3
  • write_enable = 1
    image