Assignment3: SoftCPU

tags: riscv

contributed by < shauming1020 >

Requirements

  • Ensure signature matched with the requirements described in RISC-V Compliance Tests.
  • Check the generated VCD file and use GTKwave to view the waveform.
  • Explain how your program is executed along with Reindeer Simulation.
  • Summarize how RISC-V Compliance Tests works and why the signature should be matched.
  • Explain how Reindeer works with Verilator.
  • What is 2 x 2 Pipeline? How can we benefit from such pipeline design?
  • What is “Hold and Load”? And, how the simulation does for bootstraping?
  • Can you show some signals/events inside Reindeer and describe?

Power function

Assignment1

Re-write the Assembly Code for riscv-compliance

// POWER.S #include "compliance_test.h" #include "compliance_io.h" #include "test_macros.h" # Test Virtual Machine (TVM) used by program. RV_COMPLIANCE_RV32M # Test code region. RV_COMPLIANCE_CODE_BEGIN RVTEST_IO_INIT RVTEST_IO_ASSERT_GPR_EQ(x31, x0, 0x00000000) RVTEST_IO_WRITE_STR(x31, "# Test Begin\n") # --------------------------------------------------------------------------------------------- RVTEST_IO_WRITE_STR(x31, "# Test part A1 - general test of value 10 register values\n"); # Addresses for test data and results la s0, test_A1_data # la s3, test_A1_res # Load testdata lw s0, 0(s0) # Set s0 equal to the parameter x # Register initialization li s1, 5 # Set s1 equal to the parameter y # Test main: li s2, 1 # s2 -> res, init = 1 loop: bge x0, s1, done # if (0 >= y) break andi t0, s1, 0x1 # t0 -> y & 0x1 li t1, 0x1 # t1 -> 0x1 bne t0, t1, even odd: mul s2, s2, s0 # res = res * x even: srli s1, s1, 1 # y_u >> 1, y /= 2 mul s0, s0, s0 # x = x * x j loop # goto loop done: sw s2, 0(s3) # --------------------------------------------------------------------------------------------- # HALT RV_COMPLIANCE_HALT RV_COMPLIANCE_CODE_END # Input data section. .data test_A1_data: .word 3 # x # Output data section. RV_COMPLIANCE_DATA_BEGIN .align 4 test_A1_res: .fill 1, 4, -1 RV_COMPLIANCE_DATA_END // --------------------------

First, we can check the result whether correctly with Ripes.

.data x: .word 3 y: .word 5 # unsigned .text main: lw s0, x # Set s0 equal to the parameter x lw s1, y # Set s1 equal to the parameter y li s2, 1 # s2 -> res, init = 1 loop: bge x0, s1, done # if (0 >= y) break andi t0, s1, 0x1 # t0 -> y & 0x1 li t1, 0x1 # t1 -> 0x1 bne t0, t1, even odd: mul s2, s2, s0 # res = res * x even: srli s1, s1, 1 # y_u >> 1, y /= 2 mul s0, s0, s0 # x = x * x j loop # goto loop done:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

x ^ y = 243, x = 3 and y = 5.


ELF file

We can use the riscv-compliance and riscv-ovpsim to generate the elf-file.

  1. Activate the source for riscv-none-embed-
source riscv-none-embed-gcc/setenv 
  1. Change the current folder to riscv-compliance.

    ​​​​$ cd riscv-compliance
    
  2. Modify the Makefile

    ​​​​export RISCV_TARGET       ?= riscvOVPsim
    ​​​​export RISCV_DEVICE       ?= rv32im
    ​​​​export RISCV_PREFIX       ?= riscv-none-embed-
    ​​​​export RISCV_TARGET_FLAGS ?=
    ​​​​export RISCV_ASSERT       ?= 0
    ​​​​export TARGET_SIM ?= /home/dcmc/CA/ASS3/riscv-compliance/riscv-ovpsim/bin/Linux64/riscvOVPsim.exe
    

    RISCV_DEVICE : ISA you want to use. Note: rv32im contain mul/div instruction.
    TARGET_SIM : Path for riscvOVPsim.exe, and it is in the riscv-ovpsim folder.

  3. Make to test and check result

    ​​​​$ make
    

    Check the output

    ​​​​> ...
    ​​​​> Compare to reference files ... 
    
    ​​​​> Check             I-FENCE.I-01 ... OK
    ​​​​> --------------------------------
    ​​​​> OK: 1/1 RISCV_TARGET=riscvOVPsim RISCV_DEVICE=rv32Zifencei RISCV_ISA=rv32Zifencei
    
    ​​​​> make[1]: Leaving directory '/home/dcmc/CA/ASS3/riscv-compliance'
    
  4. Change current folder to riscv-test-suite/rv32im

    ​​​​$ cd riscv-test-suite/rv32im
    

    Put the POWER.S into src folder, then add the file name in Makefrag.

    ​​​​// Makefrag
    ​​​​...
    ​​​​rv32im_sc_tests = \
    ​​​​    MULHU \
    ​​​​    MULH \
    ​​​​    MULHSU \
    ​​​​    DIV \
    ​​​​    REM \
    ​​​​    REMU \
    ​​​​    MUL \
    ​​​​    DIVU \
    ​​​​    REMU \
    ​​​​    POWER \     # <-- Add
    ​​​​    ...
    
  5. Put the POWER.reference_output into references folder

    ​​​​000000f3
    ​​​​00000000
    ​​​​00000000
    ​​​​00000000
    

    000000f3 = 243

    The test program will compare the signature (program's output) with except result (referece_output).
    We can see the signature and elf-file at riscv-compliance/work/rv32im.

  6. Back to riscv-compliance folder and re-make.

    ​​​​$ make
    

    Finish the verification.

    ​​​​...
    ​​​​Compare to reference files ... 
    
    ​​​​Check                      DIV ... OK
    ​​​​Check                     DIVU ... OK
    ​​​​Check                     MULH ... OK
    ​​​​Check                   MULHSU ... OK
    ​​​​Check                    MULHU ... OK
    ​​​​Check                      MUL ... OK
    ​​​​Check                    POWER ... OK   <-- You can see the verification for POWER.S
    ​​​​Check                      REM ... OK
    ​​​​Check                     REMU ... OK
    ​​​​--------------------------------
    ​​​​OK: 9/9 RISCV_TARGET=riscvOVPsim RISCV_DEVICE=rv32im RISCV_ISA=rv32im
    ​​​​...
    

Check the generated VCD file and use GTKwave to view the waveform

  1. Copy the files generated by riscv-compliance into Reindeer/sim/compliance
    We have three files "POWER.elf", "POWER.signature.output" and "POWER.elf.objdump" in the work folder, and copy them into Reindeer/sim/compliance.

  2. Change current folder to Reindeer/sim/verilator

  3. Make the test for POWER

    ​​​​~/Reindeer/sim/verilator$ make test POWER
    

    Output

    ​​​​====> Testing ./obj_dir/VPulseRain_RV2T_MCU
    ​​​​TEST CASE: POWER
    ​​​​testing ../compliance/POWER.elf  -r ../compliance/references/POWER.reference_output
    
    
    
    ​​​​=============================================================
    ​​​​=== PulseRain Technology, RISC-V RV32IM Test Bench
    ​​​​=============================================================
    
    ​​​​     elf file 	: ../compliance/POWER.elf
    ​​​​     reference	: ../compliance/references/POWER.reference_output
    
    ​​​​     start address 		= 0x80000000
    ​​​​     begin signature address 	= 0x80002010
    ​​​​     end signature address 	= 0x80002020
    
    ​​​​=============> reset...
    ​​​​=============> init stack ...
    ​​​​=============> load elf file...
    
    ​​​​Loading section .text.init ... 	184 bytes, 	LMA = 0x80000000
    ​​​​    80000000 04c0006f
    ​​​​    80000004 34202f73
    ​​​​    80000008 00800f93
    ​​​​    8000000c 03ff0a63
    ​​​​    80000010 00900f93
    ​​​​    80000014 03ff0663
    ​​​​...
    ​​​​
    
    ​​​​=============================================================
    ​​​​Simulation exit ../compliance/POWER.elf
    ​​​​Wave trace POWER.vcd
    ​​​​=============================================================
    
    ​​​​====> Test PASSED, Total of 1 case(s)
    
  4. Check the wave with vcd file and gtkwave tool

    ​​​​~/Reindeer/sim/verilator$ gtkwave POWER.vcd
    

    Result in the register s2(x18) is exactly 0xF3 (243).


Explain how your program is executed along with Reindeer Simulation

Check the POWER.elf.objdump file.

POWER.elf.objdump
/home/dcmc/CA/ASS3/riscv-compliance/work/rv32im/POWER.elf:     file format elf32-littleriscv


Disassembly of section .text.init:

80000000 <_start>:
80000000:	04c0006f          	j	8000004c <reset_vector>

80000004 <trap_vector>:
80000004:	34202f73          	csrr	t5,mcause
80000008:	00800f93          	li	t6,8
8000000c:	03ff0a63          	beq	t5,t6,80000040 <write_tohost>
80000010:	00900f93          	li	t6,9
80000014:	03ff0663          	beq	t5,t6,80000040 <write_tohost>
80000018:	00b00f93          	li	t6,11
8000001c:	03ff0263          	beq	t5,t6,80000040 <write_tohost>
80000020:	80000f17          	auipc	t5,0x80000
80000024:	fe0f0f13          	addi	t5,t5,-32 # 0 <_start-0x80000000>
80000028:	000f0463          	beqz	t5,80000030 <trap_vector+0x2c>
8000002c:	000f0067          	jr	t5
80000030:	34202f73          	csrr	t5,mcause
80000034:	000f5463          	bgez	t5,8000003c <handle_exception>
80000038:	0040006f          	j	8000003c <handle_exception>

8000003c <handle_exception>:
8000003c:	5391e193          	ori	gp,gp,1337

80000040 <write_tohost>:
80000040:	00001f17          	auipc	t5,0x1
80000044:	fc3f2023          	sw	gp,-64(t5) # 80001000 <tohost>
80000048:	ff9ff06f          	j	80000040 <write_tohost>

8000004c <reset_vector>:
8000004c:	f1402573          	csrr	a0,mhartid
80000050:	00051063          	bnez	a0,80000050 <reset_vector+0x4>
80000054:	00000297          	auipc	t0,0x0
80000058:	01028293          	addi	t0,t0,16 # 80000064 <reset_vector+0x18>
8000005c:	30529073          	csrw	mtvec,t0
80000060:	18005073          	csrwi	satp,0
80000064:	00000297          	auipc	t0,0x0
80000068:	01c28293          	addi	t0,t0,28 # 80000080 <reset_vector+0x34>
8000006c:	30529073          	csrw	mtvec,t0
80000070:	fff00293          	li	t0,-1
80000074:	3b029073          	csrw	pmpaddr0,t0
80000078:	01f00293          	li	t0,31
8000007c:	3a029073          	csrw	pmpcfg0,t0
80000080:	00000297          	auipc	t0,0x0
80000084:	01828293          	addi	t0,t0,24 # 80000098 <reset_vector+0x4c>
80000088:	30529073          	csrw	mtvec,t0
8000008c:	30205073          	csrwi	medeleg,0
80000090:	30305073          	csrwi	mideleg,0
80000094:	30405073          	csrwi	mie,0
80000098:	00000193          	li	gp,0
8000009c:	00000297          	auipc	t0,0x0
800000a0:	f6828293          	addi	t0,t0,-152 # 80000004 <trap_vector>
800000a4:	30529073          	csrw	mtvec,t0
800000a8:	00100513          	li	a0,1
800000ac:	01f51513          	slli	a0,a0,0x1f
800000b0:	00054863          	bltz	a0,800000c0 <reset_vector+0x74>
800000b4:	0ff0000f          	fence
800000b8:	00100193          	li	gp,1
800000bc:	00000073          	ecall
800000c0:	80000297          	auipc	t0,0x80000
800000c4:	f4028293          	addi	t0,t0,-192 # 0 <_start-0x80000000>
800000c8:	00028e63          	beqz	t0,800000e4 <reset_vector+0x98>
800000cc:	10529073          	csrw	stvec,t0
800000d0:	0000b2b7          	lui	t0,0xb
800000d4:	10928293          	addi	t0,t0,265 # b109 <_start-0x7fff4ef7>
800000d8:	30229073          	csrw	medeleg,t0
800000dc:	30202373          	csrr	t1,medeleg
800000e0:	f4629ee3          	bne	t0,t1,8000003c <handle_exception>
800000e4:	30005073          	csrwi	mstatus,0
800000e8:	00002537          	lui	a0,0x2
800000ec:	80050513          	addi	a0,a0,-2048 # 1800 <_start-0x7fffe800>
800000f0:	30052073          	csrs	mstatus,a0
800000f4:	00000297          	auipc	t0,0x0
800000f8:	01428293          	addi	t0,t0,20 # 80000108 <begin_testcode>
800000fc:	34129073          	csrw	mepc,t0
80000100:	f1402573          	csrr	a0,mhartid
80000104:	30200073          	mret

80000108 <begin_testcode>:
80000108:	00002417          	auipc	s0,0x2
8000010c:	ef840413          	addi	s0,s0,-264 # 80002000 <test_A1_data>
80000110:	00002997          	auipc	s3,0x2
80000114:	f0098993          	addi	s3,s3,-256 # 80002010 <begin_signature>
80000118:	00042403          	lw	s0,0(s0)
8000011c:	00500493          	li	s1,5

80000120 <main>:
80000120:	00100913          	li	s2,1

80000124 <loop>:
80000124:	02905063          	blez	s1,80000144 <done>
80000128:	0014f293          	andi	t0,s1,1
8000012c:	00100313          	li	t1,1
80000130:	00629463          	bne	t0,t1,80000138 <even>

80000134 <odd>:
80000134:	02890933          	mul	s2,s2,s0

80000138 <even>:
80000138:	0014d493          	srli	s1,s1,0x1
8000013c:	02840433          	mul	s0,s0,s0
80000140:	fe5ff06f          	j	80000124 <loop>

80000144 <done>:
80000144:	0129a023          	sw	s2,0(s3)
80000148:	0ff0000f          	fence
8000014c:	00100193          	li	gp,1
80000150:	00000073          	ecall

80000154 <end_testcode>:
80000154:	00000073          	ecall
	...

Disassembly of section .tohost:

80001000 <tohost>:
	...

80001100 <fromhost>:
	...

Disassembly of section .data:

80002000 <test_A1_data>:
80002000:	00000003          	lb	zero,0(zero) # 0 <_start-0x80000000>
	...

80002010 <begin_signature>:
80002010:	ffff                	0xffff
80002012:	ffff                	0xffff
	...

80002020 <end_signature>:
	...

80002100 <begin_regstate>:
80002100:	0080                	addi	s0,sp,64
	...

80002200 <end_regstate>:
80002200:	0004                	0x4
	...
80000108 <begin_testcode>:
80000108:	00002417          	auipc	s0,0x2
8000010c:	ef840413          	addi	s0,s0,-264 # 80002000 <test_A1_data>
80000110:	00002997          	auipc	s3,0x2
80000114:	f0098993          	addi	s3,s3,-256 # 80002010 <begin_signature>
80000118:	00042403          	lw	s0,0(s0)  # <-- load signature (parameter x), s0 := 0x3
8000011c:	00500493          	li	s1,5         # <-- parameter y, s1 := 0x5

80000120 <main>:
80000120:	00100913          	li	s2,1         # <-- resutl, s2 := 0x1

80000124 <loop>:
80000124:	02905063          	blez	s1,80000144 <done>
80000128:	0014f293          	andi	t0,s1,1
8000012c:	00100313          	li	t1,1
80000130:	00629463          	bne	t0,t1,80000138 <even>

80000134 <odd>:
80000134:	02890933          	mul	s2,s2,s0

80000138 <even>:
80000138:	0014d493          	srli	s1,s1,0x1
8000013c:	02840433          	mul	s0,s0,s0
80000140:	fe5ff06f          	j	80000124 <loop>

80000144 <done>:
80000144:	0129a023          	sw	s2,0(s3)
80000148:	0ff0000f          	fence
8000014c:	00100193          	li	gp,1
80000150:	00000073          	ecall

80000154 <end_testcode>:
80000154:	00000073          	ecall
  • Use the instruction : [80000120: 00100913 li s2,1] as example

    • IF:
    • ID:
    • IE:
    • Forwarding to register x18 (s2) after calculating,
    • MEM:
  • [80000124: 02905063 blez s1,80000144 <done>]
    When the answer was be calculated, check the branch condition.

    • IF:
    • ID:
    • IE: We can see the branch signal was be activated, and the next PC is 80000144 (done).

Summarize how RISC-V Compliance Tests works and why the signature should be matched

The riscv-compliance/doc/README.adoc indicate that

2.1. The compliance test
At the heart of the testing infrastructure is the detailed compliance test. This is the RISC-V assembler code that is executed on the processor and that provides results in a defined memory area (the signature). The test should only use the minimum of instructions and only those absolutely necessary. It should only use instructions and registers from the ISA instruction set on which it is targeted.

2.5. The test signature
The test signature is defined as reference data written into memory during the execution of the test. It should record values and results of the operation of the Test. It is expected that an implementation, at the end of a test, dumps the signature in to a file such that only 4-bytes are written per line, starting with the most-significant byte on the left.


Explain how Reindeer works with Verilator

Verilator is a free and open-source software tool which converts Verilog (a hardware description language) to a cycle-accurate behavioral model in C++ or SystemC.

The Observation of Reindeer/sim/verilator/tb_PulseRain_RV2T.cpp

  • Fetch the signature from elf-file.
/* int main() { ... */ std::cout << " elf file \t: " << elf_file << "\n"; if (!ref_file.empty()) { std::cout << " reference\t: " << ref_file << "\n"; ref_file_process (ref_file); } elf_label_process(elf_file); ...
  • Create

What is 2 x 2 Pipeline? How can we benefit from such pipeline design?

2 x 2 Pipeline

Reindeer's pipeline is composed of 4 stages:

  1. Instruction Fetch (IF)
  2. Instruction Decode (ID)
  3. Instruction Execution (IE)
  4. Register Write Back and Memory Access (MEM)

In the 2 x 2 layout, each stage is active every other clock cycle. For the even cycle, only IF and IE stages are active, while for the odd cycle, only ID and MEM stages are active.

  • Benefit:
    1. The Instruction Fetch and Memory Access always happen on different clock cycles, thus to avoid the structural hazard caused by the single port memory.

What is “Hold and Load”? And, how the simulation does for bootstraping?

Hold and Load

Brings a hardware based OCD (on-chip debugger) into the fore.

The soft CPU and the OCD can share the same UART port. The RX signal goes to both the soft CPU and OCD, while the TX signal has to go through a mux. And that mux is controlled by the OCD.

After reset, the soft CPU will be put into a hold state, and it will have access to the UART TX port by default. But a valid debug frame sending from the host PC can let OCD to reconfigure the mux and switch the UART TX to OCD side, for which the memory can be accessed, and the control frames can be exchanged. A new software image can be loaded into the memory during the CPU hold state, which gives rise to the name "hold-and-load".

How the simulation does for bootstraping?