# Asssigment3: SoftCPU contributed by < `Tim096` > ###### tags: `Computer Architecture (Fall 2020)` ## Introduction ### Purpose of compliance tests The goal of compliance tests is to check whether the processor under development meets the open RISC-V standards or not. It is considered as non-functional testing meaning that it doesn’t substitute for design verification. This can be interpreted as testing to check all important aspects of the specification but without focusing on details, for example, on all possible values of instruction operands or all combinations of possible registers. The result that compliance tests provide to the user is an assurance that the specification has been interpreted correctly and the design under test (DUT) can be declared as RISC-V compliant. ### How Compliance Tests work ? RISCV compliance tests are a set of rules that ensures a claimed RISCV implementation fits the basic standard and plays along with other RISCV implementation in the ecosystem. From the [riscv-compliance/doc/README.adoc](https://github.com/riscv/riscv-compliance/blob/master/doc/README.adoc) we can have a better understanding of the compliance tests. > 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. #### What is signature ? ==**signature** is a defined memory area where the result of a test suite is stored== #### The test signature defined 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. #### How to run RISCV compliance tests ? According to the [README.md](https://github.com/riscv/riscv-compliance) in the [riscv-compliance](https://github.com/riscv/riscv-compliance) repo, these are the requirements to run the RISCV compliance tests - Specify the toolchain in the [Makefile](https://github.com/riscv/riscv-compliance/blob/master/Makefile) - `export RISCV_PREFIX ?= riscv64-unknown-elf-` - `export RISCV_TARGET_FLAGS ?=` - `export RISCV_ASSERT ?= 0` - Specify where is the target device in the [Makefile](https://github.com/riscv/riscv-compliance/blob/master/Makefile) - `export RISCV_TARGET ?= riscvOVPsim` - `export RISCV_DEVICE ?= rv32i` - Set the ISA of target device - Install `riscvOVPsim` simulator - `riscvOVPsim` is a simulator used here. It has to be installed parallelled to the `riscv-compliance` directory. - Install `riscvOVPsim` by `git clone https://github.com/riscv-ovpsim/imperas-riscv-tests.git` - Also, the environment variable `TARGET_SIM` should point to the executable `/riscv-ovpsim/bin/Linux64/riscvOVPsim.exe` Eventually, a modified version of Makefile can be found [here](https://github.com/WeiCheng14159/riscv-compliance/blob/rv32i-arch/Makefile) There're currently 48 test suites in the repository, run all the tests by running the command ```bash= export TARGET_SIM=/path-on-ur-computer/imperas-riscv-tests/riscv-ovpsim/bin/Linux64/riscvOVPsim.exe make stimulate verify ``` The result will be `OK: 48/48 RISCV_TARGET=riscvOVPsim RISCV_DEVICE=rv32i RISCV_ISA=` #### How to write my own RISCV compliance test ? - Learn from examples by looking into a list of test suites for `rv32i` architecture in the [riscv-compliance/riscv-test-suite/rv32i/src/](https://github.com/riscv/riscv-compliance/tree/master/riscv-test-suite/rv32i/src) directory. - A list of available testing marco can be found in [compliance_io.h](https://github.com/riscv/riscv-compliance/blob/master/riscv-target/riscvOVPsim/compliance_io.h) and [compliance_test.h](https://github.com/riscv/riscv-compliance/blob/master/riscv-target/riscvOVPsim/compliance_test.h) - Write your own test suite (`.S` file), and place it in `riscv-compliance/riscv-test-suite/rv32i/src/` directory - Modify the [Makefrag](https://github.com/riscv/riscv-compliance/blob/master/riscv-test-suite/rv32i/Makefrag) file to add your test suite - Write the reference output file for your test suite and place it in [riscv-compliance/riscv-test-suite/rv32i/references](https://github.com/riscv/riscv-compliance/tree/master/riscv-test-suite/rv32i/references) directory - Run `make stimulate verify` to check the result of your test suite. The output files can be found in `riscv-compliance/work` directory ## Choose an assembly program as new test suite The assembly code `Count Leading Zero` from [Assignment 1](https://hackmd.io/S7_Jr4AiRZelkXA-rH-ILQ?view) is chosen for our new test suite. ### Count Leading Zero assembly code (from hw1) :::spoiler Assembly Code ```c=1 clz: lw x5, mask li x6, 32 li x7, 0 _for: bne x6, zero, _count _return: mv x10, x7 jr ra _count: addi x6, x6, -1 and x28, x10, x5 bne x28, zero, _return addi x7, x7, 1 srli x5, x5, 1 j _for ``` ::: ### Count Leading Zero test suite #### Test case itself - This is the `I-CLZ-01.S` Count Leading Zero test suite. - I've created in total 9 test cases, for simplicity, only the first test case will be shown here. For a complete view of ALL the test cases, check [here](https://github.com/WeiCheng14159/riscv-compliance/blob/rv32i-arch/riscv-test-suite/rv32i/src/I-CLZ-01.S) :::spoiler Code ```c=1 #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 1\n"); # Addresses for test data and results la x1, test_1_data la x2, test_1_res # Load testdata lw x10, 0(x1) # Register initialization # Test sw x10, 0(x2) jal clz # Store results sw x10, 4(x2) // // Assert // RVTEST_IO_CHECK() RVTEST_IO_ASSERT_GPR_EQ(x2, x10, 0x00000000) RVTEST_IO_WRITE_STR(x31, "# Test part 1 - Complete\n"); ... RV_COMPLIANCE_HALT # --------------------------------------------------------------------------------------------- # HALT # Count Leading Zero program # x5 -> t0 # x6 -> t1 # x7 -> t2 # x28 -> t3 # x10 -> a0 clz: lw x5, mask li x6, 32 li x7, 0 _for: bne x6, zero, _count _return: mv x10, x7 jr ra _count: addi x6, x6, -1 and x28, x10, x5 bne x28, zero, _return addi x7, x7, 1 srli x5, x5, 1 j _for RV_COMPLIANCE_CODE_END # Input data section. .data mask: .word 0x80000000 test_1_data: .word 0xFFFFFFFF ... skip ... # Output data section. RV_COMPLIANCE_DATA_BEGIN test_1_res: .fill 2, 4, -1 ... skip ... RV_COMPLIANCE_DATA_END ``` ::: #### Signature - This is the `I-CLZ-01.reference_output` signature I've created [I-CLZ-01.reference_output](https://github.com/WeiCheng14159/riscv-compliance/blob/rv32i-arch/riscv-test-suite/rv32i/references/I-CLZ-01.reference_output) - There're in total 9 test cases. Each test case write 2 words to the memory. For simplicity, only the result for the first test is shown - 1st word: input argument - 2nd word: result - The reference output signature will be compared with the output of the simulator ```c=1 ffffffff // input 1 00000000 // result 1 00000001 // input 2 0000001f // result 2 ... skip... ``` ## How Reindeer works with Verilator ? Reindeer is a soft CPU of Von Neumann architecture. Verilator can convert verilog to a cycle-accurate behavioral model in C++ or SystemC. The generated models are cycle-accurate, 2-state. ## What's 2x2 pipeline and its benefits? ![](https://i.imgur.com/uC5c8uA.png) IF, IE are on the even cycles. ID, MEM are on the odd cycles. To avoid stuctural hazard, IF and MEM is on different clock cycles in this way. > Strctural hazard: a required source is busy ## What's "Hold and Load" ? Bring a hardware based OCD(on-chip debugger) into the fore - Overview of Hold and load - ![](https://i.imgur.com/nTWkrJr.png) ### OCD side OCD waits for signal to transfer the memory to Reindeer CPU. Initially, OCD is in `S_IDLE` state. It will switch to `S_SYNC_1` state when debug signal comes in. ```c=356 current_state[S_IDLE]: begin ctl_wr_ext_disable = 1'b1; if (enable_in_sr[0] && (new_data_in == `DEBUG_SYNC_2)) begin next_state [S_SYNC_1] = 1; end else begin ctl_crc_sync_reset = 1'b1; next_state [S_IDLE] = 1; end end ``` (Some trivial states are skipped for simplicity.) OCD will switch from `S_INPUT_WAIT` to `S_FRAME_TYPE` **(frame checking state)** when condition are satisfied. ```c=393 current_state [S_INPUT_WAIT] : begin if (input_counter == (`DEBUG_FRAME_LENGTH - `DEBUG_SYNC_LENGTH - 1)) begin next_state [S_FRAME_TYPE] = 1; end else begin next_state [S_INPUT_WAIT] = 1; end end ``` Then OCD switch from `S_FRAME_TYPE` state to `S_CRC` state where OCD do **CRC16 cyclic redundency checking**. ```c=401 current_state [S_FRAME_TYPE] : begin ctl_reset_input_counter = 1'b1; if (enable_in_sr[0]) begin next_state [S_CRC] = 1; end else begin next_state [S_FRAME_TYPE] = 1; end end ``` ## [How does simulation bootstrap ?](https://astrostatistics.psu.edu/su09/lecturenotes/simul.html) ## Signal and events inside Reindeer TBD ## Requirements 1. Following the instructions of [Lab3: Reindeer - RISCV RV32I[M] Soft CPU](https://hackmd.io/@sysprog/rJw2A5DqS), you shall modify the assembly programs used/done with [Assignment1](https://hackmd.io/@sysprog/2020-arch-homework1) or [Assignment2](https://hackmd.io/@sysprog/2020-arch-homework2) as new test case(s) for [Reindeer](https://github.com/PulseRain/Reindeer) Simulation with Verilator. * [ I-ADD-01](https://github.com/riscv/riscv-compliance/blob/master/riscv-test-suite/rv32i/src/I-ADD-01.S) is a good starting point for writing test cases. * You have to ensure signature matched with the requirements described in [RISC-V Compliance Tests](https://github.com/riscv/riscv-compliance/blob/master/doc/README.adoc). 2. Check the generated VCD file and use GTKwave to view the waveform. Then, explain how your program is executed along with [Reindeer](https://github.com/PulseRain/Reindeer) Simulation. 3. Write down your thoughts and progress in [HackMD notes](https://hackmd.io/s/features). * Summarize how [RISC-V Compliance Tests](https://github.com/riscv/riscv-compliance/blob/master/doc/README.adoc) works and why the signature should be matched. * Explain how [Reindeer](https://github.com/PulseRain/Reindeer) works with [Verilator](https://www.veripool.org/wiki/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](https://github.com/PulseRain/Reindeer) and describe?