Contributed by kc71486
RISC-V
, jserv
os: ubuntu 22.04
sbt version: 1.9 (following this guide and use Ubuntu and other Debian-based distributions
method)
javac version: openjdk 17.0.8.1
Doesn't matter at all.
java version: openjdk 17.0.8.1
Default ubuntu 22.04 java version(openjdk 11.0.20.1) also works.
scala version: 2.11.12
It will work even if scala isn't installed, although adding it won't change anything.
verilator: 4.038
If verilator isn't installed, when running sbt test
, all testcase that requires file access (*.asmbin) would have java "not a relative path" error.
gtkwave: 3.3.104 (only used in evaluation)
The module represents a blinking LED, the counter will increament every tick (internal clock), and when the counter reaches certain threshold, the led signal will invert. This is an sequential circuit, and the whole block would be in clock triggered always block in verilog. cntReg
and blkReg
behaves like registers, while other variables would behave like wires.
It kinda looks like…
when
block should generally be avoided in standard HDL design. In this case, we can change it into mux.
invsig
represents the trigger point (wire-like). When invsig is on, cntReg becomes zero instead of (cntReg+1), and blkReg signal reverts.
There are not much things we need to add, only…
The heavy lifting memory and register access module part has been done for us, all we need to do is make connections between each modules.
For Instruction fetch, the address should either jump to the address or +4, which depends on jump_flag_id.
For Instruction decode, Load
will make memory_read_enable=1, Store
will make memory_write_enable=1
For CPU and execute, two control singnal determines which input, aluop1_source=1 makes ALU accept pc, while aluop2_source=1 makes ALU accept immediate.
For example, around 2171ps mark, 0000139C+FFFFFFB8=00001354
I removed all print function and csr function, and place called function into same file.
I removed all print function and csr function, and place called function into same file. Additionally, I removed some unused variables in data segments.
I added test HomeWorkTest to see if the result is as expected.
Try to use high clock step count, the result might not be evaluated if step is too low.
HomeWorkTest:
[info] Single Cycle CPU
[info] - should execute hamming code calculation *** FAILED ***
[info] io_mem_debug_read_data=27 (0x1b) did not equal expected=24 (0x18) (lines in CPUTest.scala: 133, 120) (CPUTest.scala:127)
To address the issue, my first thought is add more testcase to see if my datapath is wrong.
The testcase is here. I copied and modified it from other lecture's testbench.
[info] Testbench:
[info] Single Cycle CPU Testbench
[info] - should execute full testbench
Unfortunately, there aren't any error. It is actually really bad, because the issue is much trickier than I thought.
I tried manually input the arguments, but all of them seems correct. Even those with exactly same input.
Then I tried putting them in stack instead of global variable, and it also works.
Inspecting the data segment, I found something strange.
.data dump
There are only 11 variables, but I declare a total of 12 variables, so something must be wrong.
Inspecting the text segment, I think I found the reason.
202c is not listed in .data
segment, since .data
segment isn't zero initialized, it might just be a junk value.
[info] Run completed in 1 minute, 13 seconds.
[info] Total number of tests run: 12
[info] Suites: completed 10, aborted 0
[info] Tests: succeeded 12, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 91 s (01:31), completed Nov 21, 2023, 1:46:37 PM
I changed memory arguments to accept hex input and get more verbose halt output.
And I run verilator with the following arguments.
We define address 0x20000000 ~ 0x3FFFFFFF as VRAM (in hello.c). Any write action in this segment will be redirected into another dedicated memory module. VRAM can currently only be written to, reading through vram_bundle won't do anything.
We modify the address and write enable to redirect write signal correctly.
I also add some output port, so it can actually do something.
To actually print in console, I add VRAM class and implement flush method.
Whenever we write something in VRAM, the terminal will refresh the area according to the data in VRAM.
When ecall is called, cpu emits io_ecall_en signal,
which is caught by simulator.
It is then handled by InterruptHandler.