diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ee68812f..dc26ef48 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,6 +12,7 @@ permissions: jobs: build_ci_image: + timeout-minutes: 30 runs-on: ubuntu-latest outputs: image-tag: ${{ steps.meta.outputs.tags }} @@ -60,6 +61,7 @@ jobs: type=registry,ref=ghcr.io/utoss/risc-v:buildcache,mode=max build_and_test: + timeout-minutes: 30 needs: build_ci_image runs-on: ubuntu-latest container: @@ -95,3 +97,37 @@ jobs: name: vvp-files path: out/*.vvp retention-days: 7 + + riscof: + timeout-minutes: 30 + needs: [build_ci_image, build_and_test] + runs-on: ubuntu-latest + container: + image: ${{ needs.build_ci_image.outputs.image-primary-tag }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Build DUT + run: make riscof_build_dut + + - name: Validate YAML + run: make riscof_validateyaml + + - name: Clone arch-test + run: make riscof_clone_archtest + + - name: Run RISCOF + run: make riscof_run + continue-on-error: true + + - name: Upload RISCOF report + if: always() + uses: actions/upload-artifact@v4 + with: + name: riscof-report + path: | + riscof/riscof_work/report.html + riscof/riscof_work/style.css + retention-days: 7 diff --git a/.gitignore b/.gitignore index bb21ac59..e74dc804 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,9 @@ *.vcd .devcontainer/ + +riscof/riscof_work/ +riscof/riscv-arch-test/ +riscof/utoss_riscv/__pycache__/ +riscof/sail_cSim/__pycache__ +riscof/config.ini diff --git a/Dockerfile.ci b/Dockerfile.ci index 2607edb5..1b36ea4b 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -1,3 +1,5 @@ +FROM ghcr.io/utoss/risc-v-toolchain:latest AS risc-v-toolchain + FROM public.ecr.aws/lts/ubuntu:22.04_stable ARG DEBIAN_FRONTEND=noninteractive @@ -32,5 +34,32 @@ RUN sudo apt update && sudo apt install -y autoconf gperf make gcc g++ bison fle cd iverilog-* && sh autoconf.sh && ./configure && make && make check && sudo make install && \ cd .. && rm ${ICARUS_SRC_TAR} && rm -rf ./iverilog-* +# install riscv toolchain +USER dev +COPY --from=risc-v-toolchain /opt/riscv /opt/riscv +ENV PATH="/opt/riscv/bin:${PATH}" + +# install SAIL simulator +USER dev +ARG SAIL_RISCV_VERSION=0.7 +ARG SAIL_RISCV_ZIP_NAME=sail_riscv-Linux-x86_64.tar.gz +ARG SAIL_RISCV_ZIP_URL=https://github.com/riscv/sail-riscv/releases/download/${SAIL_RISCV_VERSION}/${SAIL_RISCV_ZIP_NAME} +ARG SAIL_RISCV_ZIP_HASH="6b8c3abc3126ce14911a3dec46ff540a60841ef090898f72c4c6f9b0b825efab ${SAIL_RISCV_ZIP_NAME}" +RUN mkdir -p /tmp/sail-riscv && \ + cd /tmp/sail-riscv && \ + wget ${SAIL_RISCV_ZIP_URL} && \ + echo ${SAIL_RISCV_ZIP_HASH} | sha256sum -c && \ + tar -xzf ${SAIL_RISCV_ZIP_NAME} && \ + sudo mv sail_riscv-Linux-x86_64/bin/* /usr/local/bin/ && \ + cd .. && sudo rm -r /tmp/sail-riscv && \ + cd /usr/local/bin && \ + sudo mv riscv_sim_rv32d riscv_sim_RV32 && \ + sudo mv riscv_sim_rv64d riscv_sim_RV64 + +# install riscof +USER root +RUN apt update && apt install -y python3 python3-pip && \ + pip3 install riscof + # CI needs priviledged access USER root diff --git a/Dockerfile.riscv-toolchain b/Dockerfile.riscv-toolchain new file mode 100755 index 00000000..dc256a1b --- /dev/null +++ b/Dockerfile.riscv-toolchain @@ -0,0 +1,65 @@ +FROM public.ecr.aws/lts/ubuntu:22.04_stable AS builder + +ARG DEBIAN_FRONTEND=noninteractive +ARG KEYRING_PATH=/usr/share/keyrings +ARG APT_SOURCES_PATH=/etc/apt/sources.list.d + +# update and upgrade +RUN apt update && apt upgrade -y + +# install essentialls +RUN apt update && \ + apt install -y \ + man make build-essential git zsh vim curl wget procps gnupg gnupg2 ca-certificates zip \ + software-properties-common + +# unminimize the system +RUN bash -c "yes | unminimize" + +# create dev sudo user +RUN useradd --create-home dev && \ + usermod --append --groups sudo dev && \ + apt update && apt install -y sudo && \ + echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +# build riscv toolchain +RUN sudo apt-get install -y \ + autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk \ + build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev && \ + cd /tmp && \ + git clone --recursive https://github.com/riscv/riscv-gnu-toolchain.git && \ + git clone --recursive https://github.com/riscv/riscv-opcodes.git && \ + cd riscv-gnu-toolchain && \ + ./configure --prefix=/opt/riscv --with-arch=rv32i --with-abi=ilp32 && \ + sudo make + +# main image stage +FROM public.ecr.aws/lts/ubuntu:22.04_stable + +ARG DEBIAN_FRONTEND=noninteractive +ARG KEYRING_PATH=/usr/share/keyrings +ARG APT_SOURCES_PATH=/etc/apt/sources.list.d + +# update and upgrade +RUN apt update && apt upgrade -y + +# install essentialls +RUN apt update && \ + apt install -y \ + man make build-essential git zsh vim curl wget procps gnupg gnupg2 ca-certificates zip \ + software-properties-common + +# unminimize the system +RUN bash -c "yes | unminimize" + +# create dev sudo user +RUN useradd --create-home dev && \ + usermod --append --groups sudo dev && \ + apt update && apt install -y sudo && \ + echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +# get toolchain artifacts from builder stage +COPY --from=builder /opt/riscv /opt/riscv +ENV PATH="/opt/riscv/bin:${PATH}" + +USER dev diff --git a/Makefile b/Makefile index a11e79fa..6178b825 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,12 @@ TB_UTILS := test/utils.svh TB_VCD_BASE_PATH := test/vcd +RISCOF_DIR := riscof +RISCOF_DUT_SRC := $(RISCOF_DIR)/dut.sv +RISCOF_DUT_VVP := $(RISCOF_DIR)/dut.vvp +RISCOF_CONFIG_TEMPLATE := $(RISCOF_DIR)/config.ini.m4 +RISCOF_CONFIG := $(RISCOF_DIR)/config.ini + build_top: $(OUTPUT) run_top: $(OUTPUT) @@ -53,4 +59,32 @@ run_tb: build_tb echo "\033[32mAll testbenches passed!\033[0m"; \ fi +$(RISCOF_DUT_VVP): $(SRCS) $(RISCOF_DUT_SRC) + $(IVERILOG) -g2012 -o $(RISCOF_DUT_VVP) $(SRCS) $(RISCOF_DUT_SRC) + +$(RISCOF_CONFIG): $(RISCOF_CONFIG_TEMPLATE) + m4 -D M4__WORKSPACE_PATH="$(PWD)" $< > $@ + +riscof_build_dut: $(RISCOF_DUT_VVP) + +riscof_validateyaml: $(RISCOF_CONFIG) + cd $(RISCOF_DIR) && riscof validateyaml --config=config.ini + +riscof_clone_archtest: $(RISCOF_CONFIG) + cd $(RISCOF_DIR) && riscof arch-test --clone + +riscof_generate_testlist: $(RISCOF_CONFIG) + cd $(RISCOF_DIR) && \ + riscof testlist \ + --config=config.ini \ + --suite=riscv-arch-test/riscv-test-suite/ \ + --env=riscv-arch-test/riscv-test-suite/env + +riscof_run: $(RISCOF_CONFIG) riscof_build_dut + cd $(RISCOF_DIR) && \ + riscof run \ + --config=config.ini \ + --suite=riscv-arch-test/riscv-test-suite/ \ + --env=riscv-arch-test/riscv-test-suite/env + .PHONY: all run testbenches run-tests diff --git a/riscof/config.ini.m4 b/riscof/config.ini.m4 new file mode 100644 index 00000000..cd0bd424 --- /dev/null +++ b/riscof/config.ini.m4 @@ -0,0 +1,15 @@ +[RISCOF] +ReferencePlugin=sail_cSim +ReferencePluginPath=M4__WORKSPACE_PATH/riscof/sail_cSim +DUTPlugin=utoss_riscv +DUTPluginPath=M4__WORKSPACE_PATH/riscof/utoss_riscv + +[utoss_riscv] +PATH=M4__WORKSPACE_PATH/riscof +pluginpath=M4__WORKSPACE_PATH/riscof/utoss_riscv +ispec=M4__WORKSPACE_PATH/riscof/utoss_riscv/utoss_riscv_isa.yaml +pspec=M4__WORKSPACE_PATH/riscof/utoss_riscv/utoss_riscv_platform.yaml +target_run=1 + +[sail_cSim] +pluginpath=M4__WORKSPACE_PATH/riscof/sail_cSim diff --git a/riscof/dut.sv b/riscof/dut.sv new file mode 100644 index 00000000..6674893a --- /dev/null +++ b/riscof/dut.sv @@ -0,0 +1,97 @@ +`timescale 1ns/1ps + +`include "test/utils.svh" + +module dut; + + reg clk; + reg reset; + + top #( .MEM_SIZE ( 250000 /* 1MB */ ) ) + top + ( .clk ( clk ) + , .reset ( reset ) + ); + + initial begin + clk = 0; + forever #5 clk = ~clk; + end + + initial begin + reset <= `TRUE; + @(posedge clk); #1; + reset <= `FALSE; + + fork + watch_tohost(); + watch_timeout(); + join_any + + $finish; + end + + task watch_tohost(); + integer tohost; + reg [31:0] tohost_data; + + $display("%m: waiting for tohost..."); + if ($value$plusargs("tohost=%h", tohost)) begin + $display("%m: watching tohost at address <%0d>", tohost); + + while (tohost_data === 0 || tohost_data === 32'bx) begin + @(posedge clk); + tohost_data = top.memory.M[tohost[31:2]]; + end + + $display("%m: memory[tohost] written <%0d> at time %t", tohost_data, $time); + void'(extract_signature()); + end else begin + $display("%m: tohost not specified."); + end + endtask + + task watch_timeout(); + $display("%m: waiting for timeout..."); + repeat (100000) @(posedge clk); + $display("%m: timeout reached"); + void'(extract_signature()); + endtask + + function bit extract_signature(); + integer begin_signature, end_signature; + string sig_filename; + integer sig_file, i, i_fixed; + + if (!$value$plusargs("begin_signature=%h", begin_signature)) begin + $display("%m: begin_signature not specified."); + return 1; + end + if (!$value$plusargs("end_signature=%h", end_signature)) begin + $display("%m: end_signature not specified."); + return 1; + end + if (!$value$plusargs("signature=%s", sig_filename)) begin + $display("%m: signature file not specified."); + return 1; + end + + $display("%m: extracting signature from <%0x> to <%0x> to %s", begin_signature, end_signature, sig_file); + + sig_file = $fopen(sig_filename, "w"); + if (sig_file != 0) begin + for (i = begin_signature; i < end_signature; i = i + 4) begin + $fwrite(sig_file, "%08x\n", top.memory.M[i[31:2]]); + end + $fclose(sig_file); + $display("%m: signature written to %s", sig_filename); + return 0; + end else begin + $display("%m: failed to open signature file %s", sig_filename); + return 1; + end + endfunction + + `SETUP_VCD_DUMP(dut) + +endmodule diff --git a/riscof/sail_cSim/__init__.py b/riscof/sail_cSim/__init__.py new file mode 100644 index 00000000..0bfb5a62 --- /dev/null +++ b/riscof/sail_cSim/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) \ No newline at end of file diff --git a/riscof/sail_cSim/env/link.ld b/riscof/sail_cSim/env/link.ld new file mode 100644 index 00000000..8ad95e04 --- /dev/null +++ b/riscof/sail_cSim/env/link.ld @@ -0,0 +1,18 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(rvtest_entry_point) + +SECTIONS +{ + . = 0x80000000; + .text.init : { *(.text.init) } + . = ALIGN(0x1000); + .tohost : { *(.tohost) } + . = ALIGN(0x1000); + .text : { *(.text) } + . = ALIGN(0x1000); + .data : { *(.data) } + .data.string : { *(.data.string)} + .bss : { *(.bss) } + _end = .; +} + diff --git a/riscof/sail_cSim/env/model_test.h b/riscof/sail_cSim/env/model_test.h new file mode 100644 index 00000000..386ffdfa --- /dev/null +++ b/riscof/sail_cSim/env/model_test.h @@ -0,0 +1,55 @@ +#ifndef _COMPLIANCE_MODEL_H +#define _COMPLIANCE_MODEL_H + +#define RVMODEL_DATA_SECTION \ + .pushsection .tohost,"aw",@progbits; \ + .align 8; .global tohost; tohost: .dword 0; \ + .align 8; .global fromhost; fromhost: .dword 0; \ + .popsection; \ + .align 8; .global begin_regstate; begin_regstate: \ + .word 128; \ + .align 8; .global end_regstate; end_regstate: \ + .word 4; + +//RV_COMPLIANCE_HALT +#define RVMODEL_HALT \ + li x1, 1; \ + write_tohost: \ + sw x1, tohost, t5; \ + j write_tohost; + +#define RVMODEL_BOOT + +//RV_COMPLIANCE_DATA_BEGIN +#define RVMODEL_DATA_BEGIN \ + RVMODEL_DATA_SECTION \ + .align 4;\ + .global begin_signature; begin_signature: + +//RV_COMPLIANCE_DATA_END +#define RVMODEL_DATA_END \ + .align 4; .global end_signature; end_signature: + +//RVTEST_IO_INIT +#define RVMODEL_IO_INIT +//RVTEST_IO_WRITE_STR +#define RVMODEL_IO_WRITE_STR(_R, _STR) +//RVTEST_IO_CHECK +#define RVMODEL_IO_CHECK() +//RVTEST_IO_ASSERT_GPR_EQ +#define RVMODEL_IO_ASSERT_GPR_EQ(_S, _R, _I) +//RVTEST_IO_ASSERT_SFPR_EQ +#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I) +//RVTEST_IO_ASSERT_DFPR_EQ +#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I) + +#define RVMODEL_SET_MSW_INT + +#define RVMODEL_CLEAR_MSW_INT + +#define RVMODEL_CLEAR_MTIMER_INT + +#define RVMODEL_CLEAR_MEXT_INT + + +#endif // _COMPLIANCE_MODEL_H diff --git a/riscof/sail_cSim/riscof_sail_cSim.py b/riscof/sail_cSim/riscof_sail_cSim.py new file mode 100644 index 00000000..13c19a06 --- /dev/null +++ b/riscof/sail_cSim/riscof_sail_cSim.py @@ -0,0 +1,124 @@ +import os +import re +import shutil +import subprocess +import shlex +import logging +import random +import string +from string import Template + +import riscof.utils as utils +from riscof.pluginTemplate import pluginTemplate +import riscof.constants as constants +from riscv_isac.isac import isac + +logger = logging.getLogger() + +class sail_cSim(pluginTemplate): + __model__ = "sail_c_simulator" + __version__ = "0.5.0" + + def __init__(self, *args, **kwargs): + sclass = super().__init__(*args, **kwargs) + + config = kwargs.get('config') + if config is None: + logger.error("Config node for sail_cSim missing.") + raise SystemExit(1) + self.num_jobs = str(config['jobs'] if 'jobs' in config else 1) + self.pluginpath = os.path.abspath(config['pluginpath']) + self.sail_exe = { '32' : os.path.join(config['PATH'] if 'PATH' in config else "","riscv_sim_RV32"), + '64' : os.path.join(config['PATH'] if 'PATH' in config else "","riscv_sim_RV64")} + self.isa_spec = os.path.abspath(config['ispec']) if 'ispec' in config else '' + self.platform_spec = os.path.abspath(config['pspec']) if 'ispec' in config else '' + self.make = config['make'] if 'make' in config else 'make' + logger.debug("SAIL CSim plugin initialised using the following configuration.") + for entry in config: + logger.debug(entry+' : '+config[entry]) + return sclass + + def initialise(self, suite, work_dir, archtest_env): + self.suite = suite + self.work_dir = work_dir + self.objdump_cmd = 'riscv{1}-unknown-elf-objdump -D {0} > {2};' + self.compile_cmd = 'riscv{1}-unknown-elf-gcc -march={0} -mno-relax \ + -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles\ + -T '+self.pluginpath+'/env/link.ld\ + -I '+self.pluginpath+'/env/\ + -I ' + archtest_env + + def build(self, isa_yaml, platform_yaml): + ispec = utils.load_yaml(isa_yaml)['hart0'] + self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32') + self.isa = 'rv' + self.xlen + self.compile_cmd = self.compile_cmd+' -mabi='+('lp64 ' if 64 in ispec['supported_xlen'] else 'ilp32 ') + if "I" in ispec["ISA"]: + self.isa += 'i' + if "M" in ispec["ISA"]: + self.isa += 'm' + if "C" in ispec["ISA"]: + self.isa += 'c' + if "F" in ispec["ISA"]: + self.isa += 'f' + if "D" in ispec["ISA"]: + self.isa += 'd' + objdump = "riscv{0}-unknown-elf-objdump".format(self.xlen) + if shutil.which(objdump) is None: + logger.error(objdump+": executable not found. Please check environment setup.") + raise SystemExit(1) + compiler = "riscv{0}-unknown-elf-gcc".format(self.xlen) + if shutil.which(compiler) is None: + logger.error(compiler+": executable not found. Please check environment setup.") + raise SystemExit(1) + if shutil.which(self.sail_exe[self.xlen]) is None: + logger.error(self.sail_exe[self.xlen]+ ": executable not found. Please check environment setup.") + raise SystemExit(1) + if shutil.which(self.make) is None: + logger.error(self.make+": executable not found. Please check environment setup.") + raise SystemExit(1) + + + def runTests(self, testList, cgf_file=None): + if os.path.exists(self.work_dir+ "/Makefile." + self.name[:-1]): + os.remove(self.work_dir+ "/Makefile." + self.name[:-1]) + make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + self.name[:-1])) + make.makeCommand = self.make + ' -j' + self.num_jobs + for file in testList: + testentry = testList[file] + test = testentry['test_path'] + test_dir = testentry['work_dir'] + test_name = test.rsplit('/',1)[1][:-2] + + elf = 'ref.elf' + + execute = "@cd "+testentry['work_dir']+";" + + cmd = self.compile_cmd.format(testentry['isa'].lower(), self.xlen) + ' ' + test + ' -o ' + elf + compile_cmd = cmd + ' -D' + " -D".join(testentry['macros']) + execute+=compile_cmd+";" + + execute += self.objdump_cmd.format(elf, self.xlen, 'ref.disass') + sig_file = os.path.join(test_dir, self.name[:-1] + ".signature") + + execute += self.sail_exe[self.xlen] + ' --test-signature={0} {1} > {2}.log 2>&1;'.format(sig_file, elf, test_name) + + cov_str = ' ' + for label in testentry['coverage_labels']: + cov_str+=' -l '+label + + if cgf_file is not None: + coverage_cmd = 'riscv_isac --verbose info coverage -d \ + -t {0}.log --parser-name c_sail -o coverage.rpt \ + --sig-label begin_signature end_signature \ + --test-label rvtest_code_begin rvtest_code_end \ + -e ref.elf -c {1} -x{2} {3};'.format(\ + test_name, ' -c '.join(cgf_file), self.xlen, cov_str) + else: + coverage_cmd = '' + + + execute+=coverage_cmd + + make.add_target(execute) + make.execute_all(self.work_dir) diff --git a/riscof/utoss_riscv/env/link.ld b/riscof/utoss_riscv/env/link.ld new file mode 100644 index 00000000..5bd9fa33 --- /dev/null +++ b/riscof/utoss_riscv/env/link.ld @@ -0,0 +1,18 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(rvtest_entry_point) + +SECTIONS +{ + . = 0x00000000; + .text.init : { *(.text.init) } + . = ALIGN(0x1000); + .tohost : { *(.tohost) } + . = ALIGN(0x1000); + .text : { *(.text) } + . = ALIGN(0x1000); + .data : { *(.data) } + .data.string : { *(.data.string)} + .bss : { *(.bss) } + _end = .; +} + diff --git a/riscof/utoss_riscv/env/model_test.h b/riscof/utoss_riscv/env/model_test.h new file mode 100644 index 00000000..80101da6 --- /dev/null +++ b/riscof/utoss_riscv/env/model_test.h @@ -0,0 +1,60 @@ +#ifndef _COMPLIANCE_MODEL_H +#define _COMPLIANCE_MODEL_H +#define RVMODEL_DATA_SECTION \ + .pushsection .tohost,"aw",@progbits; \ + .align 8; .global tohost; tohost: .dword 0; \ + .align 8; .global fromhost; fromhost: .dword 0; \ + .popsection; \ + .align 8; .global begin_regstate; begin_regstate: \ + .word 128; \ + .align 8; .global end_regstate; end_regstate: \ + .word 4; + +//RV_COMPLIANCE_HALT +#define RVMODEL_HALT \ + li x1, 1; \ + write_tohost: \ + sw x1, tohost, t5; \ + j write_tohost; + +#define RVMODEL_BOOT + +//RV_COMPLIANCE_DATA_BEGIN +#define RVMODEL_DATA_BEGIN \ + RVMODEL_DATA_SECTION \ + .align 4;\ + .global begin_signature; begin_signature: + +//RV_COMPLIANCE_DATA_END +#define RVMODEL_DATA_END \ + .align 4;\ + .global end_signature; end_signature: + +//RVTEST_IO_INIT +#define RVMODEL_IO_INIT +//RVTEST_IO_WRITE_STR +#define RVMODEL_IO_WRITE_STR(_R, _STR) +//RVTEST_IO_CHECK +#define RVMODEL_IO_CHECK() +//RVTEST_IO_ASSERT_GPR_EQ +#define RVMODEL_IO_ASSERT_GPR_EQ(_S, _R, _I) +//RVTEST_IO_ASSERT_SFPR_EQ +#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I) +//RVTEST_IO_ASSERT_DFPR_EQ +#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I) + +#define RVMODEL_SET_MSW_INT \ + li t1, 1; \ + li t2, 0x2000000; \ + sw t1, 0(t2); + +#define RVMODEL_CLEAR_MSW_INT \ + li t2, 0x2000000; \ + sw x0, 0(t2); + +#define RVMODEL_CLEAR_MTIMER_INT + +#define RVMODEL_CLEAR_MEXT_INT + + +#endif // _COMPLIANCE_MODEL_H diff --git a/riscof/utoss_riscv/riscof_utoss_riscv.py b/riscof/utoss_riscv/riscof_utoss_riscv.py new file mode 100644 index 00000000..f184fddd --- /dev/null +++ b/riscof/utoss_riscv/riscof_utoss_riscv.py @@ -0,0 +1,275 @@ +import os +import re +import shutil +import subprocess +import shlex +import logging +import random +import string +from string import Template +import sys + +import riscof.utils as utils +import riscof.constants as constants +from riscof.pluginTemplate import pluginTemplate + +logger = logging.getLogger() + +class utoss_riscv(pluginTemplate): + __model__ = "utoss_riscv" + + #TODO: please update the below to indicate family, version, etc of your DUT. + __version__ = "XXX" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + config = kwargs.get('config') + + # If the config node for this DUT is missing or empty. Raise an error. At minimum we need + # the paths to the ispec and pspec files + if config is None: + print("Please enter input file paths in configuration.") + raise SystemExit(1) + + # In case of an RTL based DUT, this would be point to the final binary executable of your + # test-bench produced by a simulator (like verilator, vcs, incisive, etc). In case of an iss or + # emulator, this variable could point to where the iss binary is located. If 'PATH variable + # is missing in the config.ini we can hardcode the alternate here. + self.dut_exe = os.path.join(config['PATH'] if 'PATH' in config else "","dut.vvp") + + # Number of parallel jobs that can be spawned off by RISCOF + # for various actions performed in later functions, specifically to run the tests in + # parallel on the DUT executable. Can also be used in the build function if required. + self.num_jobs = str(config['jobs'] if 'jobs' in config else 1) + + # Path to the directory where this python file is located. Collect it from the config.ini + self.pluginpath=os.path.abspath(config['pluginpath']) + + # Collect the paths to the riscv-config absed ISA and platform yaml files. One can choose + # to hardcode these here itself instead of picking it from the config.ini file. + self.isa_spec = os.path.abspath(config['ispec']) + self.platform_spec = os.path.abspath(config['pspec']) + + #We capture if the user would like the run the tests on the target or + #not. If you are interested in just compiling the tests and not running + #them on the target, then following variable should be set to False + if 'target_run' in config and config['target_run']=='0': + self.target_run = False + else: + self.target_run = True + + def initialise(self, suite, work_dir, archtest_env): + + # capture the working directory. Any artifacts that the DUT creates should be placed in this + # directory. Other artifacts from the framework and the Reference plugin will also be placed + # here itself. + self.work_dir = work_dir + + # capture the architectural test-suite directory. + self.suite_dir = suite + + # Note the march is not hardwired here, because it will change for each + # test. Similarly the output elf name and compile macros will be assigned later in the + # runTests function + self.compile_cmd = 'riscv{1}-unknown-elf-gcc -march={0} -mno-relax \ + -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -g\ + -T '+self.pluginpath+'/env/link.ld\ + -I '+self.pluginpath+'/env/\ + -I ' + archtest_env + ' {2} -o {3} {4}' + + # add more utility snippets here + self.objcopy_cmd = 'riscv{0}-unknown-elf-objcopy \ + -O verilog --verilog-data-width=4 {1} {2}' + + self.symbol_address_cmd = \ + 'riscv{0}-unknown-elf-nm {1} ' \ + '| grep " D " ' \ + '| grep {2} ' \ + '| awk \'{{print $1}}\'' + + def build(self, isa_yaml, platform_yaml): + + # load the isa yaml as a dictionary in python. + ispec = utils.load_yaml(isa_yaml)['hart0'] + + # capture the XLEN value by picking the max value in 'supported_xlen' field of isa yaml. This + # will be useful in setting integer value in the compiler string (if not already hardcoded); + self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32') + + # for utoss_riscv start building the '--isa' argument. the self.isa is dutnmae specific and may not be + # useful for all DUTs + self.isa = 'rv' + self.xlen + if "I" in ispec["ISA"]: + self.isa += 'i' + if "M" in ispec["ISA"]: + self.isa += 'm' + if "F" in ispec["ISA"]: + self.isa += 'f' + if "D" in ispec["ISA"]: + self.isa += 'd' + if "C" in ispec["ISA"]: + self.isa += 'c' + + #TODO: The following assumes you are using the riscv-gcc toolchain. If + # not please change appropriately + self.compile_cmd = self.compile_cmd+' -mabi='+('lp64 ' if 64 in ispec['supported_xlen'] else 'ilp32 ') + + def runTests(self, testList): + + # Delete Makefile if it already exists. + if os.path.exists(self.work_dir+ "/Makefile." + self.name[:-1]): + os.remove(self.work_dir+ "/Makefile." + self.name[:-1]) + # create an instance the makeUtil class that we will use to create targets. + make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + self.name[:-1])) + + # set the make command that will be used. The num_jobs parameter was set in the __init__ + # function earlier + make.makeCommand = 'make -k -j' + self.num_jobs + + # we will iterate over each entry in the testList. Each entry node will be refered to by the + # variable testname. + for testname in testList: + + # for each testname we get all its fields (as described by the testList format) + testentry = testList[testname] + + # we capture the path to the assembly file of this test + test = testentry['test_path'] + + # capture the directory where the artifacts of this test will be dumped/created. RISCOF is + # going to look into this directory for the signature files + test_dir = testentry['work_dir'] + + # name of the elf file after compilation of the test + elf = 'my.elf' + mem = 'my.mem' + + log = 'dut.log' + + # name of the signature file as per requirement of RISCOF. RISCOF expects the signature to + # be named as DUT-.signature. The below variable creates an absolute path of + # signature file. + sig_file = os.path.join(test_dir, self.name[:-1] + ".signature") + + # for each test there are specific compile macros that need to be enabled. The macros in + # the testList node only contain the macros/values. For the gcc toolchain we need to + # prefix with "-D". The following does precisely that. + compile_macros= ' -D' + " -D".join(testentry['macros']) + + # substitute all variables in the compile command that we created in the initialize + # function + cmd = self.compile_cmd.format(testentry['isa'].lower(), self.xlen, test, elf, compile_macros) + + objcopy = self.objcopy_cmd.format(self.xlen, elf, mem) + + begin_sig_addr_cmd = self.symbol_address_cmd.format(self.xlen, elf, 'begin_signature') + end_sig_addr_cmd = self.symbol_address_cmd.format(self.xlen, elf, 'end_signature') + tohost_addr_cmd = self.symbol_address_cmd.format(self.xlen, elf, 'tohost') + + # if the user wants to disable running the tests and only compile the tests, then + # the "else" clause is executed below assigning the sim command to simple no action + # echo statement. + if self.target_run: + # set up the simulation command. Template is for spike. Please change. + simcmd = self.dut_exe + \ + ' --isa={0} +signature={1} +signature-granularity=4 +MEM={2} +VCD_PATH=. ' \ + '+begin_signature=`{3}` +end_signature=`{4}` +tohost=`{5}`' \ + '> {6} 2>&1' \ + .format( + self.isa, sig_file, mem, + begin_sig_addr_cmd, end_sig_addr_cmd, tohost_addr_cmd, + log + ) + else: + simcmd = 'echo "NO RUN"' + + # concatenate all commands that need to be executed within a make-target. + execute = '@cd {0}; {1}; {2}; {3};'.format(testentry['work_dir'], cmd, objcopy, simcmd) + + # create a target. The makeutil will create a target with the name "TARGET" where num + # starts from 0 and increments automatically for each new target that is added + make.add_target(execute) + + # if you would like to exit the framework once the makefile generation is complete uncomment the + # following line. Note this will prevent any signature checking or report generation. + #raise SystemExit + + # once the make-targets are done and the makefile has been created, run all the targets in + # parallel using the make command set above. + make.execute_all(self.work_dir) + + # if target runs are not required then we simply exit as this point after running all + # the makefile targets. + if not self.target_run: + raise SystemExit(0) + +#The following is an alternate template that can be used instead of the above. +#The following template only uses shell commands to compile and run the tests. + +# def runTests(self, testList): +# +# # we will iterate over each entry in the testList. Each entry node will be referred to by the +# # variable testname. +# for testname in testList: +# +# logger.debug('Running Test: {0} on DUT'.format(testname)) +# # for each testname we get all its fields (as described by the testList format) +# testentry = testList[testname] +# +# # we capture the path to the assembly file of this test +# test = testentry['test_path'] +# +# # capture the directory where the artifacts of this test will be dumped/created. +# test_dir = testentry['work_dir'] +# +# # name of the elf file after compilation of the test +# elf = 'my.elf' +# +# # name of the signature file as per requirement of RISCOF. RISCOF expects the signature to +# # be named as DUT-.signature. The below variable creates an absolute path of +# # signature file. +# sig_file = os.path.join(test_dir, self.name[:-1] + ".signature") +# +# # for each test there are specific compile macros that need to be enabled. The macros in +# # the testList node only contain the macros/values. For the gcc toolchain we need to +# # prefix with "-D". The following does precisely that. +# compile_macros= ' -D' + " -D".join(testentry['macros']) +# +# # collect the march string required for the compiler +# marchstr = testentry['isa'].lower() +# +# # substitute all variables in the compile command that we created in the initialize +# # function +# cmd = self.compile_cmd.format(marchstr, self.xlen, test, elf, compile_macros) +# +# # just a simple logger statement that shows up on the terminal +# logger.debug('Compiling test: ' + test) +# +# # the following command spawns a process to run the compile command. Note here, we are +# # changing the directory for this command to that pointed by test_dir. If you would like +# # the artifacts to be dumped else where change the test_dir variable to the path of your +# # choice. +# utils.shellCommand(cmd).run(cwd=test_dir) +# +# # for debug purposes if you would like stop the DUT plugin after compilation, you can +# # comment out the lines below and raise a SystemExit +# +# if self.target_run: +# # build the command for running the elf on the DUT. In this case we use spike and indicate +# # the isa arg that we parsed in the build stage, elf filename and signature filename. +# # Template is for spike. Please change for your DUT +# execute = self.dut_exe + ' --isa={0} +signature={1} +signature-granularity=4 {2}'.format(self.isa, sig_file, elf) +# logger.debug('Executing on Spike ' + execute) +# +# # launch the execute command. Change the test_dir if required. +# utils.shellCommand(execute).run(cwd=test_dir) +# +# # post-processing steps can be added here in the template below +# #postprocess = 'mv {0} temp.sig'.format(sig_file)' +# #utils.shellCommand(postprocess).run(cwd=test_dir) +# +# # if target runs are not required then we simply exit as this point after running all +# # the makefile targets. +# if not self.target_run: +# raise SystemExit diff --git a/riscof/utoss_riscv/utoss_riscv_isa.yaml b/riscof/utoss_riscv/utoss_riscv_isa.yaml new file mode 100644 index 00000000..7d8c2c2c --- /dev/null +++ b/riscof/utoss_riscv/utoss_riscv_isa.yaml @@ -0,0 +1,29 @@ +hart_ids: [0] +hart0: + ISA: RV32I + physical_addr_sz: 32 + User_Spec_Version: '2.3' + supported_xlen: [32] + misa: + reset-val: 0x40000100 + rv32: + accessible: true + mxl: + implemented: true + type: + warl: + dependency_fields: [] + legal: + - mxl[1:0] in [0x1] + wr_illegal: + - Unchanged + extensions: + implemented: true + type: + warl: + dependency_fields: [] + legal: + - extensions[25:0] bitmask [0x0000100, 0x0000000] + wr_illegal: + - Unchanged + diff --git a/riscof/utoss_riscv/utoss_riscv_platform.yaml b/riscof/utoss_riscv/utoss_riscv_platform.yaml new file mode 100644 index 00000000..8e1a3d8e --- /dev/null +++ b/riscof/utoss_riscv/utoss_riscv_platform.yaml @@ -0,0 +1,10 @@ +mtime: + implemented: true + address: 0xbff8 +mtimecmp: + implemented: true + address: 0x4000 +nmi: + label: nmi_vector +reset: + label: reset_vector diff --git a/src/top.v b/src/top.v index ed2f723d..871bb8f8 100644 --- a/src/top.v +++ b/src/top.v @@ -1,8 +1,9 @@ `include "src/types.svh" -module top ( input wire clk - , input wire reset - ); +module top #( parameter MEM_SIZE = 1024 ) + ( input wire clk + , input wire reset + ); wire cfsm__pc_update; wire cfsm__reg_write; @@ -83,15 +84,16 @@ module top ( input wire clk endcase end - MA memory // instructions and data - ( .A ( memory_address ) - , .WD ( dataB ) - , .WE ( __tmp_MemWrite ) - , .CLK ( clk ) + MA #( .SIZE ( MEM_SIZE ) ) + memory // instructions and data + ( .A ( memory_address ) + , .WD ( dataB ) + , .WE ( __tmp_MemWrite ) + , .CLK ( clk ) - // outputs - , .RD ( memory_data ) - ); + // outputs + , .RD ( memory_data ) + ); always @(posedge clk) begin if (cfsm__ir_write) begin @@ -156,10 +158,10 @@ module top ( input wire clk default: result = 32'hxxxxxxxx; endcase end - + always @(posedge clk) begin dataA <= rd1; dataB <= rd2; end -endmodule +endmodule diff --git a/src/utils/MA.v b/src/utils/MA.v index f29379ee..a47036cc 100644 --- a/src/utils/MA.v +++ b/src/utils/MA.v @@ -8,6 +8,16 @@ module MA #( parameter SIZE = 1024 ) reg [31:0] M[0:SIZE-1]; + initial begin + string mem_file; + + if ($value$plusargs("MEM=%s", mem_file)) begin + $display("loading memory from <%s>", mem_file); + $readmemh(mem_file, M); + $display("memory loaded"); + end + end + assign RD = M[A[31:2]]; // 2 LSBs used for byte addressing always @(posedge CLK) begin