Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve riscv-dv tests in CI #122

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/scripts/riscv_dv_matrix_include.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from yaml import load, Loader
from json import dumps
from itertools import product
import sys

RISCV_DV_TESTS = ['riscv_arithmetic_basic_test']


if __name__ == "__main__":
if sys.argv[1].strip() == 'run':
with open('.github/workflows/test-riscv-dv.yml', 'rb') as fd:
run_tests = load(fd, Loader=Loader)['jobs']['run-tests']
matrix = run_tests['strategy']['matrix']
isses = matrix['iss']
coverages = matrix['coverage']
result = [{
"test": test,
"iss": iss,
"coverage": coverage,
"version": "pyflow",
} for test, iss, coverage in product(RISCV_DV_TESTS, isses, coverages)]
print(dumps(result))
elif sys.argv[1].strip() == 'generate':
print(dumps(
[{
"test": test,
"version": "pyflow",
} for test in RISCV_DV_TESTS]
))
else:
exit(1)
40 changes: 40 additions & 0 deletions .github/scripts/riscv_dv_parse_testlist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import sys
import os
from json import dumps
from yaml import load, Loader
from typing import Generator

RISCV_DV_HOME = "third_party/riscv-dv/"


def parse_yaml(path: str) -> Generator[str, None, None]:
with open(path, 'rb') as fd:
tests = load(fd, Loader=Loader)
for test in tests:
if 'import' in test:
import_path = test['import'].split('/', 1)[1]
yield from parse_yaml(RISCV_DV_HOME + import_path)
elif 'test' in test:
yield test['test']


if __name__ == "__main__":
if len(sys.argv) == 2:
testlist = parse_yaml(
RISCV_DV_HOME + f'target/{sys.argv[1]}/testlist.yaml')
else:
testlist = parse_yaml(RISCV_DV_HOME + 'yaml/base_testlist.yaml')
testlist = list(testlist)

# remove, will cause incomplete sim, need customized RTL
testlist.remove("riscv_csr_test")

# remove excluded tests
excluded = os.environ.get("EXCLUDE_TESTS", None)
if excluded is not None:
excluded = [s.strip() for s in excluded.split(",")]
for test in excluded:
if test in testlist:
testlist.remove(test)

print(dumps(testlist))
110 changes: 103 additions & 7 deletions .github/workflows/test-riscv-dv.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,26 +104,112 @@ jobs:
#--------------#
# Tests
#--------------#
tests:
generate-config:
name: Generate configs
runs-on: ubuntu-latest
env:
EXCLUDE_TESTS: "riscv_illegal_instr_test,riscv_unaligned_load_store_test"
outputs:
test-types: ${{ steps.test-types.outputs.tests }}
test-include-generate: ${{ steps.test-types.outputs.include-generate }}
test-include-run: ${{ steps.test-types.outputs.include-run }}
hash: ${{ steps.hash.outputs.files-hash }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- id: test-types
run: |
python3 -m pip install pyyaml
echo "tests=$(python3 .github/scripts/riscv_dv_parse_testlist.py rv32imc)" | tee -a $GITHUB_OUTPUT
echo "include-generate=$(python3 .github/scripts/riscv_dv_matrix_include.py generate)" | tee -a $GITHUB_OUTPUT
echo "include-run=$(python3 .github/scripts/riscv_dv_matrix_include.py run)" | tee -a $GITHUB_OUTPUT
- id: hash
run: |
echo "files-hash=$(sha256sum **/*.sv **/*.py **/*.yml **/*.yaml tools/riscv-dv/*.py | cut -d\ -f1 | sha256sum | cut -d\ -f1)" | tee -a $GITHUB_OUTPUT

generate-code:
name: Generate code for tests
runs-on: [ self-hosted, Linux, X64, gcp-custom-runners ]
container: centos:8
needs: generate-config
strategy:
fail-fast: false
matrix:
test: ${{ fromJSON(needs.generate-config.outputs.test-types) }}
version: [ uvm ]
include: ${{ fromJSON(needs.generate-config.outputs.test-include-generate) }}
env:
GHA_EXTERNAL_DISK: additional-tools
CACHE_HASH: ${{ needs.generate-config.outputs.hash }}
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Setup Cache Metadata
id: cache_metadata
run: |
cache_code=cache_${{ matrix.test }}_${{ matrix.version }}
echo "cache_code=${cache_code}_${{ env.CACHE_HASH }}" | tee -a "$GITHUB_ENV"

- name: Cache Code
uses: actions/cache@v3
id: cache-code
timeout-minutes: 60
with:
path: tools/riscv-dv/work/test_${{ matrix.test }}/asm_test
key: ${{ env.cache_code }}

- name: Prepare Environment
if: steps.cache-code.outputs.cache-hit != 'true'
run: _secret_prepare_env

- name: Generate Tests
if: steps.cache-code.outputs.cache-hit != 'true' && matrix.version == 'uvm'
run: _secret_generate_code
env:
RISCV_DV_TEST: ${{ matrix.test }}

- name: Generate code
if: steps.cache-code.outputs.cache-hit != 'true' && matrix.version == 'pyflow'
run: |
export RV_ROOT=`realpath .`
pushd tools/riscv-dv
make -j`nproc` \
RISCV_DV_TEST=${{ matrix.test }} \
RISCV_DV_ITER=3 \
RISCV_DV_SEED=999 \
generate
popd

- name: Pack artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: riscv-dv_generated_code_${{ matrix.version }}
path: tools/riscv-dv/work/**/asm_test/*.S

run-tests:
name: Run RISC-V DV tests
runs-on: ubuntu-latest
needs: [veer-iss, renode]
needs: [ veer-iss, renode, generate-config, generate-code ]
strategy:
fail-fast: false
matrix:
test:
- riscv_arithmetic_basic_test
test: ${{ fromJSON(needs.generate-config.outputs.test-types) }}
iss:
- spike
- whisper
- renode
coverage: ["branch", "toggle"] #TODO: add functional coverage

version: [ uvm ]
include: ${{ fromJSON(needs.generate-config.outputs.test-include-run) }}
env:
DEBIAN_FRONTEND: "noninteractive"
CCACHE_DIR: "/opt/riscv-dv/.cache/"
VERILATOR_VERSION: v5.010
SPIKE_VERSION: d70ea67d
CACHE_HASH: ${{ needs.generate-config.outputs.hash }}

steps:
- name: Install utils
Expand All @@ -144,6 +230,7 @@ jobs:
cache_spike_key=${cache_spike_restore_key}${{ env.SPIKE_VERSION }}
cache_test_restore_key=${{ matrix.test }}_${{ matrix.coverage }}_
cache_test_key=${cache_test_restore_key}${time}
cache_code=cache_${{ matrix.test }}_${{ matrix.version }}

echo "date=$date" | tee -a "$GITHUB_ENV"
echo "time=$time" | tee -a "$GITHUB_ENV"
Expand All @@ -153,6 +240,7 @@ jobs:
echo "cache_spike_key=$cache_spike_key" | tee -a "$GITHUB_ENV"
echo "cache_test_restore_key=$cache_test_restore_key" | tee -a "$GITHUB_ENV"
echo "cache_test_key=$cache_test_key" | tee -a "$GITHUB_ENV"
echo "cache_code=${cache_code}_${{ env.CACHE_HASH }}" | tee -a "$GITHUB_ENV"

- name: Restore verilator cache
id: cache-verilator-restore
Expand Down Expand Up @@ -217,6 +305,14 @@ jobs:
key: cache_tests_${{ steps.cache_timestamp.outputs.time }}
restore-keys: cache_tests_

- name: Cache Code Restore
uses: actions/cache/restore@v3
id: cache-code-restore
timeout-minutes: 60
with:
path: tools/riscv-dv/work/test_${{ matrix.test }}/asm_test
key: ${{ env.cache_code }}

- name: Run test
run: |
export PATH=/opt/verilator/bin:$PATH
Expand Down Expand Up @@ -263,5 +359,5 @@ jobs:
if: always()
uses: actions/upload-artifact@v3
with:
name: artifacts-${{ matrix.test }}-${{ matrix.iss }}
name: artifacts-${{ matrix.test }}-${{ matrix.iss }}-${{ matrix.version }}
path: tools/riscv-dv/work/test_*
58 changes: 36 additions & 22 deletions tools/riscv-dv/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
RISCV_DV_PATH = $(RV_ROOT)/third_party/riscv-dv
RISCV_DV_SIM ?= pyflow
RISCV_DV_ISS ?= spike
RISCV_DV_TEST ?= riscv_arithmetic_basic_test
RISCV_DV_SEED ?= 999
Expand All @@ -11,13 +12,7 @@ SIM_DIR = $(TEST_DIR)/hdl_sim

VEER_TARGET = default
VEER_CONF = -set build_axi4 \
-set reset_vec=0x80000000# \
-set inst_access_enable0=1 \
-set inst_access_addr0=0x00000000 \
-set inst_access_mask0=0x001fffff \
-set data_access_enable0=1 \
-set data_access_addr0=0x00000000 \
-set data_access_mask0=0x001fffff
-set reset_vec=0x80000000

# Coverage reporting
ifeq ("$(COVERAGE)", "all")
Expand All @@ -43,6 +38,18 @@ HDL_FILES = $(WORK_DIR)/common_defines.vh \
$(RV_ROOT)/testbench/ahb_sif.sv \
$(RV_ROOT)/design/include/el2_def.sv

# riscv-dv args
RISCV_DV_ARGS = \
--simulator $(RISCV_DV_SIM) \
--test $(RISCV_DV_TEST) \
--iss $(RISCV_DV_ISS) \
--iss_timeout 60 \
--start_seed $(RISCV_DV_SEED) \
--iterations $(RISCV_DV_ITER) \
--batch_size $(RISCV_DV_BATCH) \
--isa rv32imc --mabi ilp32 \
-v -o $(TEST_DIR)

MAKEFILE = $(abspath $(MAKEFILE_LIST))

all:
Expand Down Expand Up @@ -71,22 +78,25 @@ $(WORK_DIR)/verilator/Vtb_top.mk: $(WORK_DIR)/defines.h
$(WORK_DIR)/verilator/Vtb_top: $(WORK_DIR)/verilator/Vtb_top.mk
$(MAKE) -C $(WORK_DIR)/verilator -f Vtb_top.mk OPT_FAST="-O3"

# Code generation, compilation and ISS simulation via RISC-V DV flow
# Code generation
$(TEST_DIR)/generate.log: | $(TEST_DIR)
# Generate
PYTHONPATH=$(RISCV_DV_PATH)/pygen python3 $(RISCV_DV_PATH)/run.py --simulator pyflow \
--test $(RISCV_DV_TEST) --iss $(RISCV_DV_ISS) \
--start_seed $(RISCV_DV_SEED) --iterations $(RISCV_DV_ITER) --batch_size $(RISCV_DV_BATCH) \
--isa rv32imc --mabi ilp32 --steps gen -v -o $(TEST_DIR) 2>&1 | tee $(TEST_DIR)/generate.log
PYTHONPATH=$(RISCV_DV_PATH)/pygen python3 $(RISCV_DV_PATH)/run.py $(RISCV_DV_ARGS) \
--steps gen
@touch $@

# Code patching & compilation
$(TEST_DIR)/compile.log: $(TEST_DIR)
# Patch the code
find $(TEST_DIR)/asm_test -name "*.S" -exec python3 code_fixup.py -i {} -o {} \;
# Compile, simulate
PYTHONPATH=$(RISCV_DV_PATH)/pygen python3 $(RISCV_DV_PATH)/run.py $(RISCV_DV_ARGS) \
--steps gcc_compile 2>&1 | tee $@

# ISS simulation
$(TEST_DIR)/iss_sim.log: $(TEST_DIR)/compile.log | $(TEST_DIR)
# Compile, simulate
PYTHONPATH=$(RISCV_DV_PATH)/pygen python3 $(RISCV_DV_PATH)/run.py --simulator pyflow \
--test $(RISCV_DV_TEST) --iss $(RISCV_DV_ISS) --iss_timeout 60 \
--start_seed $(RISCV_DV_SEED) --iterations $(RISCV_DV_ITER) --batch_size $(RISCV_DV_BATCH) \
--isa rv32imc --mabi ilp32 --steps gcc_compile,iss_sim -v -o $(TEST_DIR) 2>&1 | tee -a $(TEST_DIR)/generate.log
PYTHONPATH=$(RISCV_DV_PATH)/pygen python3 $(RISCV_DV_PATH)/run.py $(RISCV_DV_ARGS) \
--steps iss_sim 2>&1 | tee $@

$(TEST_DIR)/asm_test/%.hex: $(TEST_DIR)/asm_test/%.o
$(RISCV_NM) -B -n $< > $(basename $@).sym
Expand Down Expand Up @@ -120,7 +130,7 @@ $(TEST_DIR)/comp_%.log: $(TEST_DIR)/$(RISCV_DV_ISS)_sim/%.csv $(SIM_DIR)/%.csv
python3 $(RISCV_DV_PATH)/scripts/instr_trace_compare.py \
--csv_file_1 $(word 1, $^) --csv_name_1 ISS --csv_file_2 $(word 2, $^) --csv_name_2 HDL \
--in_order_mode 1 --log $@ --verbose 10 --mismatch_print_limit 20
tail -n 2 $@
cat $@

# Coverage data aggregation
$(WORK_DIR)/coverage.dat:
Expand All @@ -131,11 +141,15 @@ $(WORK_DIR)/coverage.dat:
touch $@ ; \
fi

run:
# Run RISC-V DV
generate:
# Run RISC-V DV code generation
$(MAKE) -f $(MAKEFILE) $(TEST_DIR)/generate.log

run:
# Run RISC-V DV compilation and simulation
$(MAKE) -f $(MAKEFILE) $(TEST_DIR)/iss_sim.log
# Run HDL simulation(s) and trace comparison
find $(TEST_DIR)/ -name "sim_*.log" | sed 's/sim_/comp_/g' | xargs $(MAKE) -f $(MAKEFILE)
find $(TEST_DIR)/$(RISCV_DV_ISS)_sim -name "*.log" | sed 's|sim/|sim/../comp_|g' | xargs realpath --relative-to=$(PWD) | xargs $(MAKE) -f $(MAKEFILE)
# Check for errors
for F in $(TEST_DIR)/comp_*.log; do grep "\[PASSED\]" $$F; if [ $$? -ne 0 ]; then exit 255; fi; done
# Aggregate coverage data
Expand All @@ -147,5 +161,5 @@ clean:
fullclean:
rm -rf $(WORK_DIR)

.PHONY: all run generate clean fullclean compare
.PHONY: all generate run clean fullclean
.SECONDARY:
13 changes: 9 additions & 4 deletions tools/riscv-dv/code_fixup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ def __init__(self, text):
return

self.mnemonic = m.group("mnemonic").lower()
self.operands = [op.strip() for op in m.group("operands").split()]
self.operands = [op.strip() for op in m.group("operands").split(",")]

def __str__(self):
return self.text

# =============================================================================

MNEMONICS = {"div", "divu", "rem", "remu",
"lb", "lbu", "lh", "lhu", "lw", "c.lw", "c.lwsp"}

def main():
parser = argparse.ArgumentParser()
Expand Down Expand Up @@ -75,7 +77,7 @@ def main():
continue

# Check if it is a delayed write. If not then bypass
is_delayed = line.mnemonic in ["div", "divu", "rem", "remu", "lw"]
is_delayed = line.mnemonic in MNEMONICS
if not is_delayed:
continue

Expand All @@ -92,8 +94,11 @@ def main():
for j, l in enumerate(following):
if l.operands and l.operands[0] == dst:
nops = max(0, max_nops - j)
for _ in range(nops):
out_lines.append(" " * 18 + "nop # FIXME: A fixup not to make VeeR cancel a delayed write\n")
pad = " " * 18
out_lines.append(pad + "# FIXME: Inserting {} nops not to make VeeR cancel a delayed write #\n".format(nops))
for k in range(nops):
out_lines.append(pad + "nop\n")
out_lines.append(pad + "# end of nop insertion #\n")
break

# Write
Expand Down
Loading