From 86ac3ccb6b83823db8bbea3152ec5cd63bfba499 Mon Sep 17 00:00:00 2001 From: darotsr Date: Wed, 25 Sep 2024 23:12:21 +0300 Subject: [PATCH 1/8] verilator simulation, work in progress --- .gitignore | 13 + Makefile | 50 +- Makefile.verilator | 57 ++ README.md | 49 +- bender_sim.mk | 8 +- bender_synth.mk | 6 +- cv32e40p/.gitignore | 6 + .../programs/custom/hello-world/hello-world.c | 155 ++++ eth.sh | 30 + golden-model/Makefile | 9 +- rtl/redmule_ctrl.sv | 4 +- sim/README.md | 21 + sim/core/.gitignore | 5 + sim/core/Makefile.verilator | 137 +++ sim/core/README.md | 152 ++++ tb/README.md | 11 + tb/core/.clang-format | 35 + tb/core/.gitignore | 16 + tb/core/cv32e40p_tb_wrapper.sv | 182 ++++ tb/core/dp_ram.sv | 87 ++ tb/core/mm_ram.sv | 841 ++++++++++++++++++ tb/core/tb_riscv/README.md | 56 ++ .../tb_riscv/include/perturbation_defines.sv | 29 + tb/core/tb_riscv/riscv_gnt_stall.sv | 142 +++ tb/core/tb_riscv/riscv_perturbation.sv | 322 +++++++ .../riscv_random_interrupt_generator.sv | 185 ++++ tb/core/tb_riscv/riscv_random_stall.sv | 260 ++++++ tb/core/tb_riscv/riscv_rvalid_stall.sv | 184 ++++ tb/core/tb_riscv/riscv_simchecker.sv | 645 ++++++++++++++ tb/core/tb_riscv/tb_riscv_core.sv | 386 ++++++++ tb/core/tb_top.sv | 165 ++++ tb/core/tb_top_verilator.cpp | 145 +++ tb/core/tb_top_verilator.sv | 106 +++ tb/redmule_tb.sv | 534 +++++------ 34 files changed, 4741 insertions(+), 292 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile.verilator create mode 100644 cv32e40p/.gitignore create mode 100644 cv32e40p/tests/programs/custom/hello-world/hello-world.c create mode 100644 eth.sh create mode 100644 sim/README.md create mode 100644 sim/core/.gitignore create mode 100644 sim/core/Makefile.verilator create mode 100644 sim/core/README.md create mode 100644 tb/README.md create mode 100644 tb/core/.clang-format create mode 100644 tb/core/.gitignore create mode 100644 tb/core/cv32e40p_tb_wrapper.sv create mode 100644 tb/core/dp_ram.sv create mode 100644 tb/core/mm_ram.sv create mode 100644 tb/core/tb_riscv/README.md create mode 100644 tb/core/tb_riscv/include/perturbation_defines.sv create mode 100644 tb/core/tb_riscv/riscv_gnt_stall.sv create mode 100644 tb/core/tb_riscv/riscv_perturbation.sv create mode 100644 tb/core/tb_riscv/riscv_random_interrupt_generator.sv create mode 100644 tb/core/tb_riscv/riscv_random_stall.sv create mode 100644 tb/core/tb_riscv/riscv_rvalid_stall.sv create mode 100644 tb/core/tb_riscv/riscv_simchecker.sv create mode 100644 tb/core/tb_riscv/tb_riscv_core.sv create mode 100644 tb/core/tb_top.sv create mode 100644 tb/core/tb_top_verilator.cpp create mode 100644 tb/core/tb_top_verilator.sv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92e1d41 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.bender/ +bin/ +install/ +manifest.flist +install +sw/build/ +golden-model/FP16/net_parameters.h +golden-model/FP16/scripts/__pycache__/ +golden-model/gemm/ +golden-model/venv/ +sw/inc/ +verilator_tb.vcd +vsim/ diff --git a/Makefile b/Makefile index 2fccf2e..01ffbf9 100644 --- a/Makefile +++ b/Makefile @@ -7,17 +7,18 @@ # Top-level Makefile # Paths to folders -mkfile_path := $(dir $(abspath $(firstword $(MAKEFILE_LIST)))) -SW ?= $(mkfile_path)sw +mkfile_path := $(shell git rev-parse --show-toplevel) +SW ?= $(mkfile_path)/sw BUILD_DIR ?= $(SW)/build -VSIM_DIR ?= $(mkfile_path)vsim +VSIM_DIR ?= $(mkfile_path)/vsim QUESTA ?= questa-2023.4 BENDER_DIR ?= . BENDER ?= bender ISA ?= riscv ARCH ?= rv XLEN ?= 32 -XTEN ?= imc +XTEN ?= imc_zicsr + ifeq ($(REDMULE_COMPLEX),1) TEST_SRCS := $(SW)/redmule_complex.c @@ -25,11 +26,11 @@ else TEST_SRCS := $(SW)/redmule.c endif -compile_script ?= $(mkfile_path)scripts/compile.tcl -compile_script_synth ?= $(mkfile_path)scripts/synth_compile.tcl +compile_script ?= $(mkfile_path)/scripts/compile.tcl +compile_script_synth ?= $(mkfile_path)/scripts/synth_compile.tcl compile_flag ?= +acc -permissive -suppress 2583 -suppress 13314 -INI_PATH = $(mkfile_path)modelsim.ini +INI_PATH = $(mkfile_path)/modelsim.ini WORK_PATH = $(VSIM_DIR)/work # Useful Parameters @@ -53,11 +54,13 @@ INC += -I$(SW)/utils BOOTSCRIPT := $(SW)/kernel/crt0.S LINKSCRIPT := $(SW)/kernel/link.ld -CC=$(ISA)$(XLEN)-unknown-elf-gcc -LD=$(CC) -OBJDUMP=$(ISA)$(XLEN)-unknown-elf-objdump -CC_OPTS=-march=$(ARCH)$(XLEN)$(XTEN) -mabi=ilp32 -D__$(ISA)__ -O2 -g -Wextra -Wall -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wundef -fdata-sections -ffunction-sections -MMD -MP -LD_OPTS=-march=$(ARCH)$(XLEN)$(XTEN) -mabi=ilp32 -D__$(ISA)__ -MMD -MP -nostartfiles -nostdlib -Wl,--gc-sections +# Setup toolchain (from SDK) and options +RV_CC=$(ISA)$(XLEN)-unknown-elf-gcc +RV_LD=$(ISA)$(XLEN)-unknown-elf-gcc +RV_OBJDUMP=$(ISA)$(XLEN)-unknown-elf-objdump +RV_CC_OPTS=-march=$(ARCH)$(XLEN)$(XTEN) -mabi=ilp32 -D__$(ISA)__ -O2 -g -Wextra -Wall -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wundef -fdata-sections -ffunction-sections -MMD -MP +RV_LD_OPTS=-march=$(ARCH)$(XLEN)$(XTEN) -mabi=ilp32 -D__$(ISA)__ -MMD -MP -nostartfiles -nostdlib -Wl,--gc-sections + # Setup build object dirs CRT=$(BUILD_DIR)/crt0.o @@ -70,17 +73,17 @@ STIM_DATA=$(VSIM_DIR)/stim_data.txt # Build implicit rules $(STIM_INSTR) $(STIM_DATA): $(BIN) objcopy --srec-len 1 --output-target=srec $(BIN) $(BIN).s19 - scripts/parse_s19.pl $(BIN).s19 > $(BIN).txt - python scripts/s19tomem.py $(BIN).txt $(STIM_INSTR) $(STIM_DATA) + scripts/parse_s19.pl $(BIN).s19 > $(BIN).txt 2>$(BUILD_DIR)/parse_s19.pl.log + python scripts/s19tomem.py $(BIN).txt $(STIM_INSTR) $(STIM_DATA) $(BIN): $(CRT) $(OBJ) - $(LD) $(LD_OPTS) -o $(BIN) $(CRT) $(OBJ) -T$(LINKSCRIPT) + $(RV_LD) $(RV_LD_OPTS) -o $(BIN) $(CRT) $(OBJ) -T$(LINKSCRIPT) $(CRT): $(BUILD_DIR) - $(CC) $(CC_OPTS) -c $(BOOTSCRIPT) -o $(CRT) + $(RV_CC) $(CC_OPTS) -c $(BOOTSCRIPT) -o $(CRT) $(OBJ): $(TEST_SRCS) - $(CC) $(CC_OPTS) -c $(TEST_SRCS) $(FLAGS) $(INC) -o $(OBJ) + $(RV_CC) $(CC_OPTS) -c $(TEST_SRCS) $(FLAGS) $(INC) -o $(OBJ) $(BUILD_DIR): mkdir -p $(BUILD_DIR) @@ -88,7 +91,7 @@ $(BUILD_DIR): SHELL := /bin/bash # Generate instructions and data stimuli -sw-build: $(STIM_INSTR) $(STIM_DATA) dis +sw-build: $(VSIM_DIR) $(STIM_INSTR) $(STIM_DATA) dis # Run the simulation run: $(CRT) @@ -118,8 +121,9 @@ bender: include bender_common.mk include bender_sim.mk include bender_synth.mk +include Makefile.verilator -WAVES := $(mkfile_path)scripts/wave.tcl +WAVES := $(mkfile_path)/scripts/wave.tcl ifeq ($(REDMULE_COMPLEX),1) tb := redmule_complex_tb @@ -149,12 +153,14 @@ synth-ips: sw-clean: rm -rf $(BUILD_DIR) + @rm -vf $(STIM_INSTR) + @rm -vf $(STIM_DATA) hw-clean: rm -rf $(VSIM_DIR) dis: - $(OBJDUMP) -d $(BIN) > $(DUMP) + $(RV_OBJDUMP) -d $(BIN) > $(DUMP) OP ?= gemm fp_fmt ?= FP16 @@ -169,7 +175,8 @@ golden-clean: $(MAKE) -C golden-model golden-clean clean-all: hw-clean sw-clean - rm -rf $(mkfile_path).bender + rm -rf $(mkfile_path)/.bender + rm -rf $(mkfile_path)/Bender.lock rm -rf $(compile_script) hw-build: $(VSIM_DIR) @@ -179,3 +186,4 @@ hw-build: $(VSIM_DIR) sw-all: sw-clean sw-build hw-all: hw-clean hw-build + diff --git a/Makefile.verilator b/Makefile.verilator new file mode 100644 index 0000000..23d409e --- /dev/null +++ b/Makefile.verilator @@ -0,0 +1,57 @@ +############# +# Verilator # +############# +VERI_LOG_DIR ?= $(mkfile_path)/vsim +SIM_TEST_RESULTS ?= $(VERI_LOG_DIR) +TEST ?= redmule +BIN_DIR = $(mkfile_path)/bin +VERI_FLAGS += + +VLT_TOP_MODULE ?= redmule_tb +#VLT_TOP_MODULE = tb_top_verilator + +.PHONY: veri-clean + +# Clean all build directories and temporary files for Questasim simulation +veri-clean: + rm -f manifest.flist + make -C sim/core -f Makefile.verilator CV_CORE_MANIFEST=${CURDIR}/manifest.flist SIM_RESULTS=$(BIN_DIR) $@ + +verilate: $(BIN_DIR)/verilator_executable + +manifest.flist: Bender.yml + $(BENDER) script verilator $(common_targs) $(VLT_BENDER) >$@ + touch $@ + +$(BIN_DIR)/verilator_executable: manifest.flist + mkdir -p $(dir $@) + make -C sim/core -f Makefile.verilator CV_CORE_MANIFEST=${CURDIR}/manifest.flist SIM_RESULTS=$(BIN_DIR) VLT_TOP_MODULE=$(VLT_TOP_MODULE) verilate + +sanity-veri-run: + make -C sim/core -f Makefile.verilator CV_CORE_MANIFEST=${CURDIR}/manifest.flist SIM_RESULTS=$(BIN_DIR) VLT_TOP_MODULE=$(VLT_TOP_MODULE) TEST=hello-world run-test + + +.PHONY: run-test +run-test: $(BIN_DIR)/verilator_executable + @echo "$(BANNER)" + @echo "* Running with Verilator: logfile in $(SIM_TEST_RESULTS)/$(TEST).log" + @echo "$(BANNER)" + mkdir -p $(VERI_LOG_DIR) + $(BIN_DIR)/verilator_executable \ + $(VERI_FLAGS) \ + "+firmware=$(SIM_TEST_RESULTS)/stim_instr.txt" \ + "+simdata=$(SIM_TEST_RESULTS)/stim_data.txt" \ + | tee $(VERI_LOG_DIR)/$(TEST).log + +.PHONY: help + +help: + @echo "verilator related available targets:" + @echo verilate -- build verilator simulation, available here: $(BIN_DIR)/verilator_executable + @echo veri-clean -- get a clean slate for simulation + @echo sanity-veri-run -- smoke test for $(BIN_DIR)/verilator_executable + + + + + diff --git a/README.md b/README.md index 47f47b3..bd9571a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,50 @@ +# ISOLDE +```sh +. ./eth.sh +``` +## build simulation +top module can be configured in cmd line,VLT_TOP_MODULE=, it defaults to **redmule_tb**, see [Makefile.verilator](Makefile.verilator) +```sh +make verilate +``` +### get a clean slate +```sh +make veri-clean +``` +## test simulation +only works if VLT_TOP_MODULE == tb_top_verilator +```sh +make verilate VLT_TOP_MODULE=tb_top_verilator +make sanity-veri-run +``` +Output similar to: +``` +HELLO WORLD!!! +This is the OpenHW Group CV32E40P CORE-V processor core. +CV32E40P is a RISC-V ISA compliant core with the following attributes: + mvendorid = 0x602 + marchid = 0x4 + mimpid = 0x0 + misa = 0x40001104 + XLEN is 32-bits + Supported Instructions Extensions: MIC + +TOP.tb_top_verilator @ 130110: EXIT SUCCESS +- /home/uic52463/hdd2/isolde-project/redmule/tb/core/tb_top_verilator.sv:83: Verilog $finish +``` +## build sw +get a clean slate +```sh +make sw-clean +``` +```sh +make golden +make sw-build +``` +the following files have been generated: +* vsim/stim_instr.txt +* vsim/stim_data.txt + # RedMulE RedMulE (**Red**uced-Precision Matrix **Mul**tiplication **E**ngine) is an open-source hardware accelerator based on the [HWPE](https://hwpe-doc.readthedocs.io/en/latest/index.html) template. It is designed to accelerate General Matrix-Matrix Operations (GEMM-Ops) on Floating-Point (FP) FP16 and FP8 input matrices. The keyword GEMM-Ops includes all the matrix operations of the kind **Z = (X op1 W) op2 Z**. The operators *op1* and *op2* can be any of those grouped in the following table: @@ -21,7 +68,7 @@ If you want to use RedMulE for academic purposes, please cite it as: ``` @article{TORTORELLA2023122, -title = {RedMule: A mixed-precision matrix–matrix operation engine for flexible and energy-efficient on-chip linear algebra and TinyML training acceleration}, +title = {RedMule: A mixed-precision matrix�matrix operation engine for flexible and energy-efficient on-chip linear algebra and TinyML training acceleration}, journal = {Future Generation Computer Systems}, volume = {149}, pages = {122-135}, diff --git a/bender_sim.mk b/bender_sim.mk index e9c74cf..3a4e0d0 100644 --- a/bender_sim.mk +++ b/bender_sim.mk @@ -8,8 +8,12 @@ sim_targs += -t rtl sim_targs += -t test +VLT_BENDER += -t rtl + ifeq ($(REDMULE_COMPLEX),1) - sim_targs += -t redmule_test_complex + sim_targs += -t redmule_test_complex + VLT_BENDER += -t redmule_test_complex else - sim_targs += -t redmule_test_hwpe + sim_targs += -t redmule_test_hwpe + VLT_BENDER += -t redmule_test_hwpe endif diff --git a/bender_synth.mk b/bender_synth.mk index 85f989a..c160b01 100644 --- a/bender_synth.mk +++ b/bender_synth.mk @@ -8,7 +8,9 @@ synth_targs += ifeq ($(REDMULE_COMPLEX),1) - synth_defs += -D REDMULE_COMPLEX_SYNTH + synth_defs += -D REDMULE_COMPLEX_SYNTH + VLT_FLAGS += -DREDMULE_COMPLEX_SYNTH else - synth_defs += -D REDMULE_HWPE_SYNTH + synth_defs += -D REDMULE_HWPE_SYNTH + VLT_FLAGS += -DREDMULE_HWPE_SYNTH endif diff --git a/cv32e40p/.gitignore b/cv32e40p/.gitignore new file mode 100644 index 0000000..7086cab --- /dev/null +++ b/cv32e40p/.gitignore @@ -0,0 +1,6 @@ +*.elf +*.hex +*.itb +*.objdump +*.yaml +*.readelf diff --git a/cv32e40p/tests/programs/custom/hello-world/hello-world.c b/cv32e40p/tests/programs/custom/hello-world/hello-world.c new file mode 100644 index 0000000..be4a257 --- /dev/null +++ b/cv32e40p/tests/programs/custom/hello-world/hello-world.c @@ -0,0 +1,155 @@ +/* +** +** Copyright 2020 OpenHW Group +** +** Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** https://solderpad.org/licenses/ +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +** +******************************************************************************* +** +** Sanity test for the CV32E40P core. Reads the MVENDORID, MISA, MARCHID and +** MIMPID CSRs and prints some useful (?) +** messages to stdout. Will fail if these +** CSRs do not match expected values. +** +******************************************************************************* +*/ + +#include +#include + +//FIXME: the core tb does not have the ability to select PULP/NO_PULP at +// compile-time, so we set a default MISA to NO_PULP value. This +// needs to be fixed, but for now does not affect UVM env. +#define EXP_MISA 0x40001104 + +#ifdef NO_PULP +#define EXP_MISA 0x40001104 +#endif + +#ifdef PULP +#define EXP_MISA 0x40801104 +#endif + + +int main(int argc, char *argv[]) +{ + unsigned int misa_rval, mvendorid_rval, marchid_rval, mimpid_rval, mxl; + int reserved, tentative, nonstd, user, super; + + mxl = 0; reserved = 0; tentative = 0; nonstd = 0; user = 0; super = 0; + + /* inline assembly: read mvendorid and misa */ + __asm__ volatile("csrr %0, 0xF11" : "=r"(mvendorid_rval)); + __asm__ volatile("csrr %0, 0x301" : "=r"(misa_rval)); + __asm__ volatile("csrr %0, 0xF12" : "=r"(marchid_rval)); + __asm__ volatile("csrr %0, 0xF13" : "=r"(mimpid_rval)); + + /* Check MVENDORID CSR: 0x602 is the value assigned by JEDEC to the OpenHW Group */ + if (mvendorid_rval != 0x00000602) { + printf("\tERROR: CSR MVENDORID reads as 0x%x - should be 0x00000602 for the OpenHW Group.\n\n", mvendorid_rval); + return EXIT_FAILURE; + } + + /* Check MISA CSR: if its zero, it might not be implemented at all */ + if (misa_rval != EXP_MISA) { + printf("\tERROR: CSR MISA reads as 0x%x - should be 0x%x for this release of CV32E40P!\n\n", misa_rval, EXP_MISA); + return EXIT_FAILURE; + } + + /* Check MARCHID CSR: 0x4 is the value assigned by the RISC-V Foundation to CV32E40P */ + if (marchid_rval != 0x00000004) { + printf("\tERROR: CSR MARCHID reads as 0x%x - should be 0x00000004 for CV32E40P.\n\n", marchid_rval); + return EXIT_FAILURE; + } + + /* Check MIMPID CSR: 0x0 is the value assigned by the OpenHW Group to the first release of CV32E40P */ + if (mimpid_rval != 0x00000000) { + printf("\tERROR: CSR MIMPID reads as 0x%x - should be 0x00000000 for this release of CV32E40P.\n\n", mimpid_rval); + return EXIT_FAILURE; + } + + /* Print a banner to stdout and interpret MISA CSR */ + printf("\nHELLO WORLD!!!\n"); + printf("This is the OpenHW Group CV32E40P CORE-V processor core.\n"); + printf("CV32E40P is a RISC-V ISA compliant core with the following attributes:\n"); + printf("\tmvendorid = 0x%x\n", mvendorid_rval); + printf("\tmarchid = 0x%x\n", marchid_rval); + printf("\tmimpid = 0x%x\n", mimpid_rval); + printf("\tmisa = 0x%x\n", misa_rval); + mxl = ((misa_rval & 0xC0000000) >> 30); // MXL == MISA[31:30] + switch (mxl) { + case 0: printf("\tERROR: MXL cannot be zero!\n"); + return EXIT_FAILURE; + break; + case 1: printf("\tXLEN is 32-bits\n"); + break; + case 2: printf("\tXLEN is 64-bits\n"); + break; + case 3: printf("\tXLEN is 128-bits\n"); + break; + default: printf("\tERROR: mxl (%0d) not in 0..3, your code is broken!\n", mxl); + return EXIT_FAILURE; + } + + printf("\tSupported Instructions Extensions: "); + if ((misa_rval >> 25) & 0x00000001) ++reserved; + if ((misa_rval >> 24) & 0x00000001) ++reserved; + if ((misa_rval >> 23) & 0x00000001) { + printf("X"); + ++nonstd; + } + if ((misa_rval >> 22) & 0x00000001) ++reserved; + if ((misa_rval >> 21) & 0x00000001) ++tentative; + if ((misa_rval >> 20) & 0x00000001) ++user; + if ((misa_rval >> 19) & 0x00000001) ++tentative; + if ((misa_rval >> 18) & 0x00000001) ++super; + if ((misa_rval >> 17) & 0x00000001) ++reserved; + if ((misa_rval >> 16) & 0x00000001) printf("Q"); + if ((misa_rval >> 15) & 0x00000001) ++tentative; + if ((misa_rval >> 14) & 0x00000001) ++reserved; + if ((misa_rval >> 13) & 0x00000001) printf("N"); + if ((misa_rval >> 12) & 0x00000001) printf("M"); + if ((misa_rval >> 11) & 0x00000001) ++tentative; + if ((misa_rval >> 10) & 0x00000001) ++reserved; + if ((misa_rval >> 9) & 0x00000001) printf("J"); + if ((misa_rval >> 8) & 0x00000001) printf("I"); + if ((misa_rval >> 7) & 0x00000001) printf("H"); + if ((misa_rval >> 6) & 0x00000001) printf("G"); + if ((misa_rval >> 5) & 0x00000001) printf("F"); + if ((misa_rval >> 4) & 0x00000001) printf("E"); + if ((misa_rval >> 3) & 0x00000001) printf("D"); + if ((misa_rval >> 2) & 0x00000001) printf("C"); + if ((misa_rval >> 1) & 0x00000001) printf("B"); + if ((misa_rval ) & 0x00000001) printf("A"); + printf("\n"); + if (super) { + printf("\tThis machine supports SUPERVISOR mode.\n"); + } + if (user) { + printf("\tThis machine supports USER mode.\n"); + } + if (nonstd) { + printf("\tThis machine supports non-standard instructions.\n"); + } + if (tentative) { + printf("\tWARNING: %0d tentative instruction extensions are defined!\n", tentative); + } + if (reserved) { + printf("\tERROR: %0d reserved instruction extensions are defined!\n\n", reserved); + return EXIT_FAILURE; + } + else { + printf("\n"); + return EXIT_SUCCESS; + } +} diff --git a/eth.sh b/eth.sh new file mode 100644 index 0000000..455ff3f --- /dev/null +++ b/eth.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Copyleft + +# Define environment variables +MINICONDA=~/miniconda3/etc/profile.d/conda.sh +MINICONDA_ENV=snitch +# To activate this environment, use +# +# $ conda activate snitch +# +# To deactivate an active environment, use +# +# $ conda deactivate + +# Get the root directory of the Git repository +export ROOT_DIR=$(git rev-parse --show-toplevel) + +export BENDER=~/eth/bin/bender +export PULP_RISCV_GCC_TOOLCHAIN=$ROOT_DIR/install/riscv +export GCC_TOOLCHAIN=$ROOT_DIR/install/riscv/bin +export CC=gcc-10 +export CXX=g++-10 + + +source $MINICONDA +conda activate $MINICONDA_ENV + +export PATH=~/eth/bin:~/verible/bin:$ROOT_DIR/install/verilator/bin:$GCC_TOOLCHAIN:$PATH +source ~/vivado.sh +echo `verilator --version` \ No newline at end of file diff --git a/golden-model/Makefile b/golden-model/Makefile index 03eee17..b7ebff1 100644 --- a/golden-model/Makefile +++ b/golden-model/Makefile @@ -27,11 +27,12 @@ check_sw: check_penv: check_sw ifndef PENV - $(error PENV is undefined. Make sure to export \ - PENV= before continue.) + $(error PENV is undefined. Make sure to export \ + PENV= before continuing.) +else python3 -m venv $(PENV) - source .venv/bin/activate - pip3 install numpy + . $(PENV)/bin/activate && \ + pip3 install numpy && \ pip3 install torch endif diff --git a/rtl/redmule_ctrl.sv b/rtl/redmule_ctrl.sv index 29d53dd..a5f1a03 100644 --- a/rtl/redmule_ctrl.sv +++ b/rtl/redmule_ctrl.sv @@ -19,8 +19,8 @@ module redmule_ctrl parameter int unsigned Width = 8 , parameter int unsigned NumPipeRegs = 3 , localparam int unsigned TILE = (NumPipeRegs +1)*Height, - localparam int unsigned W_ITERS = W_ITERS , - localparam int unsigned LEFT_PARAMS = LEFT_PARAMS + localparam int unsigned W_ITERS = redmule_pkg::W_ITERS , + localparam int unsigned LEFT_PARAMS = redmule_pkg::LEFT_PARAMS )( input logic clk_i , input logic rst_ni , diff --git a/sim/README.md b/sim/README.md new file mode 100644 index 0000000..783436b --- /dev/null +++ b/sim/README.md @@ -0,0 +1,21 @@ +## SIM directory +Convenient directories for launching simulations. +By default, all generated files will be written to a subdirectory of either the `core` or `uvmt` directories. +This can be controlled using environment variables as explained in `$CORE_V_VERIF/mk/uvmt/README.md`. + +### Cloning the RTL +The Makefiles will automatically clone the required RTL to `$CORE_V_VERIF/core-v-cores/cv32e40p`. +

+Variables in `./ExternalRepos.mk` control the URL, branch and hash of the cloned code - see +the comment header for examples. The defaults for these variables will clone the +most up-to-date and stable version of the RTL. Note that this is not always the +head of the master branch. + +### Directories + +#### core +The Makefile will run the SystemVerilog testbench found in `../tb/core` and +its associated tests from `../tests/core`. This testbench and tests were +inherited from the PULP-Platform team and have been modified only slightly. + + diff --git a/sim/core/.gitignore b/sim/core/.gitignore new file mode 100644 index 0000000..1224fc7 --- /dev/null +++ b/sim/core/.gitignore @@ -0,0 +1,5 @@ +cobj_dir +memory_dump.bin +testbench_verilator +*_results +*.vcd diff --git a/sim/core/Makefile.verilator b/sim/core/Makefile.verilator new file mode 100644 index 0000000..bff80c1 --- /dev/null +++ b/sim/core/Makefile.verilator @@ -0,0 +1,137 @@ + +CV_CORE ?= CV32E40P + +num_cores := $(shell nproc) +num_cores_half := $(shell echo "$$(($(num_cores) / 2))") + +PRJ_HOME := $(shell git rev-parse --show-toplevel) +CORE_V_VERIF := $(PRJ_HOME) +TBSRC_HOME := $(PRJ_HOME)/tb +TBSRC_CORE := $(TBSRC_HOME)/core +TBSRC_VERI := $(TBSRC_CORE)/tb_top_verilator.sv \ + $(TBSRC_CORE)/cv32e40p_tb_wrapper.sv \ + $(TBSRC_CORE)/tb_riscv/riscv_rvalid_stall.sv \ + $(TBSRC_CORE)/tb_riscv/riscv_gnt_stall.sv \ + $(TBSRC_CORE)/mm_ram.sv \ + $(TBSRC_CORE)/dp_ram.sv + + +CV_CORE_LC = $(shell echo $(CV_CORE) | tr A-Z a-z) +CV_CORE_UC = $(shell echo $(CV_CORE) | tr a-z A-Z) + +CV_CORE_PKG := $(shell $(BENDER) path $(CV_CORE_LC)) +CV_CORE_MANIFEST ?= $(CV_CORE_PKG)/cv32e40p_manifest.flist +export DESIGN_RTL_DIR = $(CV_CORE_PKG)/rtl + +TEST ?= +# Test-Program directores. +# Relative path is used for Verilator which cannot seem to handle loooong pathnames. +TEST_PROGRAM_PATH = $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs/custom +TEST_PROGRAM_RELPATH = ../../$(CV_CORE_LC)/tests/programs/custom +#TEST_PROGRAM_RELPATH = $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs/custom + +# Common output directories +RUN_INDEX ?= 0 +SIM_RESULTS ?= simulation_results +SIM_TEST_RESULTS = $(SIM_RESULTS)/$(TEST) +SIM_RUN_RESULTS = $(SIM_TEST_RESULTS)/$(RUN_INDEX) +SIM_TEST_PROGRAM_RESULTS = $(SIM_RUN_RESULTS)/test_program +SIM_BSP_RESULTS = $(SIM_TEST_PROGRAM_RESULTS)/bsp + +# Compile compile flags for all simulators +SV_CMP_FLAGS = + +#top module +VLT_TOP_MODULE ?= tb_top_verilator + +# verilator configuration +VERILATOR = verilator +VERILATOR_ROOT ?= $(dir $(shell which $(VLT)))/../share/verilator +VLT_ROOT ?= ${VERILATOR_ROOT} + +VERI_FLAGS += +VERI_COMPILE_FLAGS += -Wno-BLKANDNBLK $(SV_CMP_FLAGS) # hope this doesn't hurt us in the long run +VERI_TRACE ?= +VERI_OBJ_DIR ?= cobj_dir +#VERI_LOG_DIR ?= cobj_dir/logs +VERI_LOG_DIR ?= $(SIM_TEST_PROGRAM_RESULTS) +VERI_CFLAGS += -O2 + +############################################################################### +# Verilator + +# We first test if the user wants to to vcd dumping. This hacky part is required +# because we need to conditionally compile the testbench (-DVCD_TRACE) and pass +# the --trace flags to the verilator call +#ifeq ($(findstring +vcd,$(VERI_FLAGS)),+vcd) + +VLT_FLAGS += -Wno-fatal +VLT_CFLAGS += -DTOPLEVEL_NAME=$(VLT_TOP_MODULE) +VLT_CFLAGS += -std=c++17 + +ifneq (${WAVES}, 0) +VERI_TRACE="--trace" +VLT_CFLAGS += -DVCD_TRACE +endif + +VLT_FLAGS += --timing +VLT_CFLAGS += -g -I $(VLT_ROOT)/include -I $(VLT_ROOT)/include/vltstd + +verilate: testbench_verilator + +sanity-veri-run: + make veri-test TEST=hello-world + +testbench_verilator: $(TBSRC_VERI) + @echo "$(BANNER)" + @echo "* Compiling CORE TB and CV32E40P with Verilator" + @echo "$(BANNER)" + $(VERILATOR) --cc --sv --exe \ + $(VERI_TRACE) \ + $(VLT_FLAGS) \ + --Wno-lint --Wno-UNOPTFLAT \ + --Wno-MODDUP --top-module \ + $(VLT_TOP_MODULE) $(TBSRC_VERI) \ + -f $(CV_CORE_MANIFEST) \ + $(CV_CORE_PKG)/bhv/$(CV_CORE_LC)_core_log.sv \ + $(TBSRC_CORE)/tb_top_verilator.cpp --Mdir $(VERI_OBJ_DIR) \ + -CFLAGS "$(VLT_CFLAGS)" \ + $(VERI_COMPILE_FLAGS) + $(MAKE) -C $(VERI_OBJ_DIR) -j$(num_cores_half) -f V$(VLT_TOP_MODULE).mk + mkdir -p $(SIM_RESULTS) + mkdir -p $(SIM_TEST_RESULTS) + mv $(VERI_OBJ_DIR)/V$(VLT_TOP_MODULE) $(SIM_TEST_RESULTS)/verilator_executable + +veri-test: verilate $(TEST_PROGRAM_PATH)/$(TEST)/$(TEST).hex + @echo "$(BANNER)" + @echo "* Running with Verilator: logfile in $(SIM_TEST_RESULTS)/$(TEST).log" + @echo "$(BANNER)" + mkdir -p $(VERI_LOG_DIR) + $(SIM_TEST_RESULTS)/verilator_executable \ + $(VERI_FLAGS) \ + "+firmware=$(TEST_PROGRAM_RELPATH)/$(TEST)/$(TEST).hex" \ + | tee $(VERI_LOG_DIR)/$(TEST).log + +# verilator specific cleanup +veri-clean: verilate-clean + +verilate-clean: + if [ -d $(SIM_RESULTS) ]; then rm -r $(SIM_RESULTS); fi + if [ -d $(VERI_OBJ_DIR) ]; then rm -r $(VERI_OBJ_DIR); fi + rm -rf testbench_verilator + if [ -e memory_dump.bin ]; then rm memory_dump.bin; fi + if [ -e verilator_tb.vcd ]; then rm verilator_tb.vcd; fi + +.PHONY: run-test +run-test: verilate + @echo "$(BANNER)" + @echo "* Running with Verilator: logfile in $(SIM_TEST_RESULTS)/$(TEST).log" + @echo "$(BANNER)" + mkdir -p $(VERI_LOG_DIR) + $(SIM_TEST_RESULTS)/verilator_executable \ + $(VERI_FLAGS) \ + "+firmware=$(TEST_PROGRAM_RELPATH)/$(TEST)/$(TEST).hex" \ + | tee $(VERI_LOG_DIR)/$(TEST).log +############################################################################### + + diff --git a/sim/core/README.md b/sim/core/README.md new file mode 100644 index 0000000..c57447f --- /dev/null +++ b/sim/core/README.md @@ -0,0 +1,152 @@ +# ISOLDE +## Build simulator +```sh +make -f Makefile.verilator verilate +``` +## Run test +```sh +make -f Makefile.verilator TEST=hello-world run-test +``` +# clean slate +```sh +make -f Makefile.verilator veri-clean +``` +Simulation Directory for CV32E40P Core Testbench +================================================ +This is the directory in which you should run all tests of the Core Testbench. +The testbench itself is located at `../../tb/core` and the test-programs are at +`../../tests`. See the README in those directories for more information. + +To run the core testbench you will need a SystemVerilog simulator and RISC-V GCC compiler. + +Supported SystemVerilog Simulators +---------------------------------- +The core testbench and associated test-programs can be run using **_Verilator_**, the Metrics +**_dsim_**, Mentor's **_Questa_**, Cadence **_Xcelium_**, Synopsys **_vcs_** and Aldec **_Riviera-PRO_** +simulators. Note that **_Icarus_** verilog cannot compile the RTL and there are no plans +to support Icarus in the future. + +RISC-V GCC Compiler "Toolchain" +------------------------------- +Pointers to the recommended toolchain for CV32E40P are in `../TOOLCHAIN`. + +Running your own C programs +--------------------- +A hello world program is available and you can run it in the CV32E40P Core testbench. +Invoke the `dsim-hello_world` or `hello-world-veri-run` makefile rules to run it with +`dsim` or `verilator` respectively. + +The hello world program is located in the `custom` folder. The relevant sections +in the Makefile on how to compile and link this program can be found under `Running +custom programs`. Make sure you have a working C compiler (see above) and keep in +mind that you are running on a very basic machine. + +Running the testbench with [verilator](https://www.veripool.org/wiki/verilator) +---------------------- +Point your environment variable `RISCV` to your RISC-V toolchain. Call `make` +to run the default test (hello_world). + +Running your own Assembler programs +----------------------------- +If you have a C or assembly program in `../../tests/core/custom` +then the following will work with Verilator:
+``` +make veri-test TEST=dhrystone +make veri-test TEST=misalign +make veri-test TEST=fibonacci +make veri-test TEST=illegal +make veri-test TEST=riscv_ebreak_test_0 +``` + +Running the testbench with Metrics [dsim](https://metrics.ca) +---------------------- +Point your environment variable `RISCV` to your RISC-V toolchain. Call +`make dsim-sanity` to build and run the testbench with the hello_world +test in the custom directory. Other test targets of interest:
+``` +make dsim-test TEST=dhrystone +make dsim-test TEST=misalign +make dsim-test TEST=fibonacci +make dsim-test TEST=illegal +make dsim-test TEST=riscv_ebreak_test_0 +``` + + +Running the testbench with Cadence Xcelium [xrun](https://www.cadence.com/en_US/home/tools/system-design-and-verification/simulation-and-testbench-verification/xcelium-parallel-simulator.html) +---------------------- +**Note:** This testbench is known to require Xcelium 19.09 or later. See [Issue 11](https://github.com/openhwgroup/core-v-verif/issues/11) for more info. +Point your environment variable `RISCV` to your RISC-V toolchain. Call +`make xrun-test` to build and run the testbench with the hello_world +test in the custom directory, or you can provide the TEST variable on the +command line as shown for the dsim targets (e.g. make xrun-test TEST=misalign). + + +Running the testbench with Questa (vsim) +--------------------------------------------------------- + + +If you have a C or assembly program in `../../tests/programs/custom` +then the following _should_ work with Questa (note that this +has not been tested):
+``` +make questa-test TEST=hello-world +make questa-test TEST=dhrystone +make questa-test TEST=coremark +make questa-test TEST=fibonacci +``` + +Running the testbench with VCS (vcs) +---------------------- +Point your environment variable `RISCV` to your RISC-V toolchain. +Call `make firmware-vcs-run` to build the testbench and the firmware, and run it. +Use `SIMV_FLAGS` or `VCS_FLAGS` to configure the simulator and build respectively e.g. +`make firmware-vcs-run VCS_FLAGS+="-cm line+cond+fsm+tgl+branch" SIMV_FLAGS+="-cm line+cond+fsm+tgl+branch"` + +Running the testbench with Riviera-PRO (riviera) +---------------------- +Point you environment variable `RISCV` to your RISC-V toolchain. Call `make +riviera-hello-world` to build the testbench and the firmware, and run it. Use +`ASIM_FLAGS` to configure the simulator e.g. `make custom-asim-run +ASIM_FLAGS="-gui"`. + +Options +------- +A few plusarg options are supported: +* `+verbose` to show all memory read and writes and other miscellaneous information. + +* `+vcd` to produce a vcd file called `riscy_tb.vcd`. Verilator always produces + a vcd file called `verilator_tb.vcd`. + +Examples +-------- +Run all riscv_tests to completion with **dsim**: +`make dsim-cv32_riscv_tests` + diff --git a/tb/README.md b/tb/README.md new file mode 100644 index 0000000..66b6c3c --- /dev/null +++ b/tb/README.md @@ -0,0 +1,11 @@ +## CV32/TB: testbenches for the CV32E40P CORE-V family of RISC-V cores. +Derived from the +[tb](https://github.com/pulp-platform/riscv/tree/master/tb) +directory of the PULP-Platform RI5CY project. There are two distinct +testbenches: + +### core +Modified to remove a few RTL files (placed these in the rtl directory). This +testbench supports Verilator and we will do what we can to maintain Verilator +support here. Note that `tb_riscv` is now a sub-directory of `core`. + diff --git a/tb/core/.clang-format b/tb/core/.clang-format new file mode 100644 index 0000000..ab4772e --- /dev/null +++ b/tb/core/.clang-format @@ -0,0 +1,35 @@ +--- +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +BreakBeforeBraces: Linux +AlwaysBreakBeforeMultilineStrings: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +IndentCaseLabels: false +AlignEscapedNewlinesLeft: false +AlignTrailingComments: true +AlignOperands: true +AllowAllParametersOfDeclarationOnNextLine: false +AlignAfterOpenBracket: true +SpaceAfterCStyleCast: false +MaxEmptyLinesToKeep: 2 +BreakBeforeBinaryOperators: NonAssignment +BreakStringLiterals: false +SortIncludes: false +ContinuationIndentWidth: 4 +ColumnLimit: 80 +IndentPPDirectives: AfterHash +BinPackArguments: true +BinPackParameters: true +ForEachMacros: + - 'TAILQ_FOREACH' + - 'TAILQ_FOREACH_REVERSE' +BreakBeforeBinaryOperators: None +MaxEmptyLinesToKeep: 1 +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlignConsecutiveAssignments: true +... diff --git a/tb/core/.gitignore b/tb/core/.gitignore new file mode 100644 index 0000000..e9e88a5 --- /dev/null +++ b/tb/core/.gitignore @@ -0,0 +1,16 @@ +csmith/platform.info +csmith/test.c +csmith/test.elf +csmith/test_ref +csmith/output_ref.txt +csmith/output_sim.txt +platform.info +memory_dump.bin +riscv-fesvr +riscv-isa-sim +modelsim.ini +DVEfiles +csrc +inter.vpd +ucli.key +vc_hdrs.h diff --git a/tb/core/cv32e40p_tb_wrapper.sv b/tb/core/cv32e40p_tb_wrapper.sv new file mode 100644 index 0000000..634d992 --- /dev/null +++ b/tb/core/cv32e40p_tb_wrapper.sv @@ -0,0 +1,182 @@ +// Copyright 2018 Robert Balas +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Wrapper for a CV32E40P testbench, containing CV32E40P, Memory and stdout peripheral +// Contributor: Robert Balas +// Module renamed from riscv_wrapper to cv32e40p_tb_wrapper because (1) the +// name of the core changed, and (2) the design has a cv32e40p_wrapper module. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-0.51 + +module cv32e40p_tb_wrapper + #(parameter // Parameters used by TB + INSTR_RDATA_WIDTH = 32, + RAM_ADDR_WIDTH = 20, + BOOT_ADDR = 'h80, + DM_HALTADDRESS = 32'h1A11_0800, + HART_ID = 32'h0000_0000, + // Parameters used by DUT + PULP_XPULP = 0, + PULP_CLUSTER = 0, + FPU = 0, + PULP_ZFINX = 0, + NUM_MHPMCOUNTERS = 1 + ) + (input logic clk_i, + input logic rst_ni, + + input logic fetch_enable_i, + output logic tests_passed_o, + output logic tests_failed_o, + output logic [31:0] exit_value_o, + output logic exit_valid_o); + + // signals connecting core to memory + logic instr_req; + logic instr_gnt; + logic instr_rvalid; + logic [31:0] instr_addr; + logic [INSTR_RDATA_WIDTH-1:0] instr_rdata; + + logic data_req; + logic data_gnt; + logic data_rvalid; + logic [31:0] data_addr; + logic data_we; + logic [3:0] data_be; + logic [31:0] data_rdata; + logic [31:0] data_wdata; + + // signals to debug unit + logic debug_req; + + // irq signals (not used) + logic [0:31] irq; + logic [0:4] irq_id_in; + logic irq_ack; + logic [0:4] irq_id_out; + logic irq_sec; + + + // interrupts (only timer for now) + assign irq_sec = '0; + +// // core log reports parameter usage and illegal instructions to the logfile +// // MIKET: commenting out as the cv32e40p RTL wrapper does this as well. +// cv32e40p_core_log +// #( +// .PULP_XPULP ( PULP_XPULP ), +// .PULP_CLUSTER ( PULP_CLUSTER ), +// .FPU ( FPU ), +// .PULP_ZFINX ( PULP_ZFINX ), +// .NUM_MHPMCOUNTERS ( NUM_MHPMCOUNTERS )) +// core_log_i( +// .clk_i ( cv32e40p_core_i.id_stage_i.clk ), +// .is_decoding_i ( cv32e40p_core_i.id_stage_i.is_decoding_o ), +// .illegal_insn_dec_i ( cv32e40p_core_i.id_stage_i.illegal_insn_dec ), +// .hart_id_i ( cv32e40p_core_i.hart_id_i ), +// .pc_id_i ( cv32e40p_core_i.pc_id ) +// ); + + // instantiate the core + cv32e40p_core #( + .PULP_XPULP (PULP_XPULP), + .PULP_CLUSTER (PULP_CLUSTER), + .FPU (FPU), + .PULP_ZFINX (PULP_ZFINX), + .NUM_MHPMCOUNTERS (NUM_MHPMCOUNTERS) + ) + cv32e40p_core_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .pulp_clock_en_i ( '1 ), + .scan_cg_en_i ( '0 ), + + .boot_addr_i ( BOOT_ADDR ), + .dm_halt_addr_i ( DM_HALTADDRESS ), + .hart_id_i ( HART_ID ), + + .instr_req_o ( instr_req ), + .instr_gnt_i ( instr_gnt ), + .instr_rvalid_i ( instr_rvalid ), + .instr_addr_o ( instr_addr ), + .instr_rdata_i ( instr_rdata ), + + .data_req_o ( data_req ), + .data_gnt_i ( data_gnt ), + .data_rvalid_i ( data_rvalid ), + .data_we_o ( data_we ), + .data_be_o ( data_be ), + .data_addr_o ( data_addr ), + .data_wdata_o ( data_wdata ), + .data_rdata_i ( data_rdata ), + + .apu_req_o ( ), + .apu_gnt_i ( 1'b0 ), + .apu_operands_o ( ), + .apu_op_o ( ), + .apu_flags_o ( ), + .apu_rvalid_i ( 1'b0 ), + .apu_result_i ( {32{1'b0}} ), + .apu_flags_i ( {5{1'b0}} ), // APU_NUSFLAGS_CPU + + // Interrupts verified in UVM environment + .irq_i ( {32{1'b0}} ), + .irq_ack_o ( irq_ack ), + .irq_id_o ( irq_id_out ), + + .debug_req_i ( debug_req ), + + .fetch_enable_i ( fetch_enable_i ), + .core_sleep_o ( core_sleep_o ) + ); + + // this handles read to RAM and memory mapped pseudo peripherals + mm_ram + #(.RAM_ADDR_WIDTH (RAM_ADDR_WIDTH), + .INSTR_RDATA_WIDTH (INSTR_RDATA_WIDTH)) + ram_i + (.clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .dm_halt_addr_i ( DM_HALTADDRESS ), + + .instr_req_i ( instr_req ), + .instr_addr_i ( { {10{1'b0}}, + instr_addr[RAM_ADDR_WIDTH-1:0] + } ), + .instr_rdata_o ( instr_rdata ), + .instr_rvalid_o ( instr_rvalid ), + .instr_gnt_o ( instr_gnt ), + + .data_req_i ( data_req ), + .data_addr_i ( data_addr ), + .data_we_i ( data_we ), + .data_be_i ( data_be ), + .data_wdata_i ( data_wdata ), + .data_rdata_o ( data_rdata ), + .data_rvalid_o ( data_rvalid ), + .data_gnt_o ( data_gnt ), + + .irq_id_i ( irq_id_out ), + .irq_ack_i ( irq_ack ), + .irq_o ( irq ), + + .debug_req_o ( debug_req ), + + .pc_core_id_i ( cv32e40p_core_i.pc_id ), + + .tests_passed_o ( tests_passed_o ), + .tests_failed_o ( tests_failed_o ), + .exit_valid_o ( exit_valid_o ), + .exit_value_o ( exit_value_o )); + +endmodule // cv32e40p_tb_wrapper diff --git a/tb/core/dp_ram.sv b/tb/core/dp_ram.sv new file mode 100644 index 0000000..30974d4 --- /dev/null +++ b/tb/core/dp_ram.sv @@ -0,0 +1,87 @@ +// Copyright 2015 ETH Zurich and University of Bologna. +// Copyright 2017 Embecosm Limited +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// DVT LINTER waivers are fine because this is not a UVM component. +//@DVT_LINTER_WAIVER_START "MT20210811_0" disable SVTB.29.1.3.1, SVTB.29.1.7 + +module dp_ram + #(parameter ADDR_WIDTH = 8, + parameter INSTR_RDATA_WIDTH = 128) + (input logic clk_i, + + input logic en_a_i, + input logic [ADDR_WIDTH-1:0] addr_a_i, + input logic [31:0] wdata_a_i, + output logic [INSTR_RDATA_WIDTH-1:0] rdata_a_o, + input logic we_a_i, + input logic [3:0] be_a_i, + + input logic en_b_i, + input logic [ADDR_WIDTH-1:0] addr_b_i, + input logic [31:0] wdata_b_i, + output logic [31:0] rdata_b_o, + input logic we_b_i, + input logic [3:0] be_b_i); + + localparam bytes = 2**ADDR_WIDTH; + + logic [7:0] mem[bytes]; + logic [ADDR_WIDTH-1:0] addr_a_int; + logic [ADDR_WIDTH-1:0] addr_b_int; + + always_comb addr_a_int = {addr_a_i[ADDR_WIDTH-1:2], 2'b0}; + always_comb addr_b_int = {addr_b_i[ADDR_WIDTH-1:2], 2'b0}; + + always @(posedge clk_i) begin + for (int i = 0; i < INSTR_RDATA_WIDTH/8; i++) begin + rdata_a_o[(i*8)+: 8] <= mem[addr_a_int + i]; + end + + /* addr_b_i is the actual memory address referenced */ + if (en_b_i) begin + /* handle writes */ + if (we_b_i) begin + if (be_b_i[0]) mem[addr_b_int ] <= wdata_b_i[ 0+:8]; + if (be_b_i[1]) mem[addr_b_int + 1] <= wdata_b_i[ 8+:8]; + if (be_b_i[2]) mem[addr_b_int + 2] <= wdata_b_i[16+:8]; + if (be_b_i[3]) mem[addr_b_int + 3] <= wdata_b_i[24+:8]; + end + /* handle reads */ + else begin + if ($test$plusargs("verbose")) + $display("read addr=0x%08x: data=0x%08x", addr_b_int, + {mem[addr_b_int + 3], mem[addr_b_int + 2], + mem[addr_b_int + 1], mem[addr_b_int + 0]}); + + rdata_b_o[ 7: 0] <= mem[addr_b_int ]; + rdata_b_o[15: 8] <= mem[addr_b_int + 1]; + rdata_b_o[23:16] <= mem[addr_b_int + 2]; + rdata_b_o[31:24] <= mem[addr_b_int + 3]; + end + end + end + + export "DPI-C" function read_byte; + export "DPI-C" task write_byte; + + function int read_byte(input logic [ADDR_WIDTH-1:0] byte_addr); + read_byte = mem[byte_addr]; + endfunction + + task write_byte(input logic [ADDR_WIDTH-1:0] byte_addr, logic [7:0] val, output logic [7:0] other); + mem[byte_addr] = val; + other = mem[byte_addr]; + + endtask + +endmodule // dp_ram + +//@DVT_LINTER_WAIVER_END "MT20210811_0" diff --git a/tb/core/mm_ram.sv b/tb/core/mm_ram.sv new file mode 100644 index 0000000..259671f --- /dev/null +++ b/tb/core/mm_ram.sv @@ -0,0 +1,841 @@ +// Copyright 2017 Embecosm Limited +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// RAM and MM wrapper for RI5CY +// Contributor: Jeremy Bennett +// Robert Balas +// +// This maps the dp_ram module to the instruction and data ports of the RI5CY +// processor core and some pseudo peripherals + +// DVT LINTER waivers are fine because this is not a UVM component. +//@DVT_LINTER_WAIVER_START "MT20210811_1" disable SVTB.29.1.3.1, SVTB.29.1.7 + +module mm_ram +`ifndef VERILATOR + import uvm_pkg::*; + `include "uvm_macros.svh" +`endif + #( + parameter RAM_ADDR_WIDTH = 16, + INSTR_RDATA_WIDTH = 128, // width of read_data on instruction bus + DATA_RDATA_WIDTH = 32, // width of read_data on data bus + DBG_ADDR_WIDTH = 14, // POT ammount of memory allocated for debugger + // physically located at end of memory + IRQ_WIDTH = 32 // IRQ vector width + ) + ( + input logic clk_i, + input logic rst_ni, + input logic [31:0] dm_halt_addr_i, + + input logic instr_req_i, + input logic [31:0] instr_addr_i, + output logic [INSTR_RDATA_WIDTH-1:0] instr_rdata_o, + output logic instr_rvalid_o, + output logic instr_gnt_o, + + input logic data_req_i, + input logic [31:0] data_addr_i, + input logic data_we_i, + input logic [3:0] data_be_i, + input logic [31:0] data_wdata_i, + output logic [31:0] data_rdata_o, + output logic data_rvalid_o, + output logic data_gnt_o, + + input logic [4:0] irq_id_i, + input logic irq_ack_i, + output logic [IRQ_WIDTH-1:0] irq_o, + + input logic [31:0] pc_core_id_i, + + output logic debug_req_o, + + output logic tests_passed_o, + output logic tests_failed_o, + output logic exit_valid_o, + output logic [31:0] exit_value_o); + + localparam int RND_STALL_REGS = 16; + localparam int RND_STALL_INSTR_EN = 0; + localparam int RND_STALL_INSTR_MODE = 2; + localparam int RND_STALL_INSTR_MAX = 4; + localparam int RND_STALL_INSTR_GNT = 6; + localparam int RND_STALL_INSTR_VALID = 8; + localparam int RND_STALL_DATA_EN = 1; + localparam int RND_STALL_DATA_MODE = 3; + localparam int RND_STALL_DATA_MAX = 5; + localparam int RND_STALL_DATA_GNT = 7; + localparam int RND_STALL_DATA_VALID = 9; + + localparam int RND_IRQ_ID = 31; + + localparam int MMADDR_PRINT = 32'h1000_0000; + localparam int MMADDR_TESTSTATUS = 32'h2000_0000; + localparam int MMADDR_EXIT = 32'h2000_0004; + localparam int MMADDR_SIGBEGIN = 32'h2000_0008; + localparam int MMADDR_SIGEND = 32'h2000_000C; + localparam int MMADDR_SIGDUMP = 32'h2000_0010; + localparam int MMADDR_TIMERREG = 32'h1500_0000; + localparam int MMADDR_TIMERVAL = 32'h1500_0004; + localparam int MMADDR_DBG = 32'h1500_0008; + localparam int MMADDR_RNDSTALL = 16'h1600; + localparam int MMADDR_RNDNUM = 32'h1500_1000; + localparam int MMADDR_TICKS = 32'h1500_1004; + + // UVM info tags + localparam string MM_RAM_TAG = "MM_RAM"; + localparam string RNDSTALL_TAG = "RNDSTALL"; + + // mux for read and writes + enum logic [2:0]{RAM, MM, RND_STALL, ERR, RND_NUM, TICKS} select_rdata_d, select_rdata_q; + + enum logic {T_RAM, T_PER} transaction; + + + int i; + + logic [31:0] data_addr_aligned; + + // signals for handshake + logic data_rvalid_q; + logic instr_rvalid_q; + logic [INSTR_RDATA_WIDTH-1:0] core_instr_rdata; + logic [31:0] core_data_rdata; + + // signals to ram + logic ram_data_req; + logic [RAM_ADDR_WIDTH-1:0] ram_data_addr; + logic [31:0] ram_data_wdata; + logic [31:0] ram_data_rdata; + logic ram_data_we; + logic [3:0] ram_data_be; + logic ram_data_gnt; + logic ram_data_valid; + + logic data_req_dec; + logic [31:0] data_wdata_dec; + logic [RAM_ADDR_WIDTH-1:0] data_addr_dec; + logic data_we_dec; + logic [3:0] data_be_dec; + + logic [INSTR_RDATA_WIDTH-1:0] ram_instr_rdata; + logic ram_instr_req; + logic [RAM_ADDR_WIDTH-1:0] ram_instr_addr; + logic ram_instr_gnt; + logic ram_instr_valid; + logic [RAM_ADDR_WIDTH-1:0] instr_addr_remap; + + // signals to print peripheral + logic [31:0] print_wdata; + logic print_valid; + + // signature data + logic [31:0] sig_end_d, sig_end_q; + logic [31:0] sig_begin_d, sig_begin_q; + + // signals to timer + logic [IRQ_WIDTH-1:0] timer_irq_mask_q; + logic [31:0] timer_cnt_q; + logic [IRQ_WIDTH-1:0] irq_q; + logic timer_reg_valid; + logic timer_val_valid; + logic [31:0] timer_wdata; + + // cycle counting + logic [31:0] cycle_count_q; + logic cycle_count_overflow_q; + logic cycle_count_clear; + + // debugger control signals + logic [31:0] debugger_wdata; + logic debugger_valid; + + // signals to rnd_stall + logic [31:0] rnd_stall_regs [0:RND_STALL_REGS-1]; + + logic rnd_stall_req; + logic [31:0] rnd_stall_addr; + logic [31:0] rnd_stall_wdata; + logic rnd_stall_we; + logic [31:0] rnd_stall_rdata; + + //signal delayed by random stall + logic rnd_stall_instr_req; + logic rnd_stall_instr_gnt; + + logic rnd_stall_data_req; + logic rnd_stall_data_gnt; + + // random number generation + logic rnd_num_req; + logic [31:0] rnd_num; + + //random or monitor interrupt request + logic rnd_irq; + + // used by dump_signature methods + string sig_file; + string sig_string; + bit use_sig_file; + int sig_fd; + int errno; + string error_str; + + // Common code used by both reads and writes + function void setup_transaction(); + + data_req_dec = data_req_i; + if ( (data_addr_i >= dm_halt_addr_i) && + (data_addr_i < (dm_halt_addr_i + (2 ** DBG_ADDR_WIDTH)) ) + ) begin + // remap debug code to end of memory + data_addr_dec = (data_addr_i[RAM_ADDR_WIDTH-1:0] - dm_halt_addr_i[RAM_ADDR_WIDTH-1:0]) + + 2**RAM_ADDR_WIDTH - 2**DBG_ADDR_WIDTH; + end + else begin + data_addr_dec = data_addr_i[RAM_ADDR_WIDTH-1:0]; + end + + data_wdata_dec = data_wdata_i; + data_we_dec = data_we_i; + data_be_dec = data_be_i; + transaction = T_RAM; + + endfunction: setup_transaction + + // uhh, align? + always_comb data_addr_aligned = {data_addr_i[31:2], 2'b0}; + + always @(negedge rst_ni) begin : configure_stalls + for (i = 0; i < RND_STALL_REGS; i=i+1) begin + rnd_stall_regs[i] = 0; + end +`ifndef VERILATOR + if (!$test$plusargs("rand_stall_obi_disable")) begin + if ($test$plusargs("max_data_zero_instr_stall")) begin + `uvm_info(RNDSTALL_TAG, "Max data stall, zero instruction stall configuration", UVM_LOW) + // This "knob" creates maximum stalls on data loads/stores, and + // no stalls on instruction fetches. Used for fence.i testing. + rnd_stall_regs[RND_STALL_DATA_EN] = 1; + rnd_stall_regs[RND_STALL_DATA_MODE] = 2; + rnd_stall_regs[RND_STALL_DATA_GNT] = 2; + rnd_stall_regs[RND_STALL_DATA_VALID] = 2; + rnd_stall_regs[RND_STALL_DATA_MAX] = 8; + end + else begin + randcase + 2: begin + // No delays + end + 1: begin + // Create RAM stall delays + rnd_stall_regs[RND_STALL_INSTR_EN] = 1; + rnd_stall_regs[RND_STALL_INSTR_MODE] = $urandom_range(2,1); + rnd_stall_regs[RND_STALL_INSTR_GNT] = $urandom_range(3,0); + rnd_stall_regs[RND_STALL_INSTR_VALID] = $urandom_range(3,0); + rnd_stall_regs[RND_STALL_INSTR_MAX] = $urandom_range(3,0); + end + endcase + + randcase + 2: begin + // No delays + end + 1: begin + // Create RAM stall delays + rnd_stall_regs[RND_STALL_DATA_EN] = 1; + rnd_stall_regs[RND_STALL_DATA_MODE] = $urandom_range(2,1); + rnd_stall_regs[RND_STALL_DATA_GNT] = $urandom_range(2,0); + rnd_stall_regs[RND_STALL_DATA_VALID] = $urandom_range(2,0); + rnd_stall_regs[RND_STALL_DATA_MAX] = $urandom_range(3,0); + end + endcase + end + end + + `uvm_info(RNDSTALL_TAG, $sformatf("INSTR OBI stall enable: %0d", rnd_stall_regs[RND_STALL_INSTR_EN]), UVM_LOW) + `uvm_info(RNDSTALL_TAG, $sformatf("INSTR OBI stall mode: %0d", rnd_stall_regs[RND_STALL_INSTR_MODE]), UVM_LOW) + `uvm_info(RNDSTALL_TAG, $sformatf("INSTR OBI stall gnt: %0d", rnd_stall_regs[RND_STALL_INSTR_GNT]), UVM_LOW) + `uvm_info(RNDSTALL_TAG, $sformatf("INSTR OBI stall valid: %0d", rnd_stall_regs[RND_STALL_INSTR_VALID]), UVM_LOW) + `uvm_info(RNDSTALL_TAG, $sformatf("INSTR OBI stall max: %0d", rnd_stall_regs[RND_STALL_INSTR_MAX]), UVM_LOW) + `uvm_info(RNDSTALL_TAG, $sformatf("DATA OBI stall enable: %0d", rnd_stall_regs[RND_STALL_DATA_EN]), UVM_LOW) + `uvm_info(RNDSTALL_TAG, $sformatf("DATA OBI stall mode: %0d", rnd_stall_regs[RND_STALL_DATA_MODE]), UVM_LOW) + `uvm_info(RNDSTALL_TAG, $sformatf("DATA OBI stall gnt: %0d", rnd_stall_regs[RND_STALL_DATA_GNT]), UVM_LOW) + `uvm_info(RNDSTALL_TAG, $sformatf("DATA OBI stall valid: %0d", rnd_stall_regs[RND_STALL_DATA_VALID]), UVM_LOW) + `uvm_info(RNDSTALL_TAG, $sformatf("DATA OBI stall max: %0d", rnd_stall_regs[RND_STALL_DATA_MAX]), UVM_LOW) +`endif + end : configure_stalls + +`ifndef VERILATOR + function bit is_stall_sim(); + return rnd_stall_regs[RND_STALL_DATA_EN] || rnd_stall_regs[RND_STALL_INSTR_EN]; + endfunction : is_stall_sim +`endif + + // handle the mapping of read and writes to either memory or pseudo + // peripherals (currently just a redirection of writes to stdout) + always_comb begin + tests_passed_o = '0; + tests_failed_o = '0; + exit_value_o = 0; + exit_valid_o = '0; + data_req_dec = '0; + data_addr_dec = '0; + data_wdata_dec = '0; + data_we_dec = '0; + data_be_dec = '0; + print_wdata = '0; + print_valid = '0; + timer_wdata = '0; + timer_reg_valid = '0; + timer_val_valid = '0; + debugger_wdata = '0; + debugger_valid = '0; + sig_end_d = sig_end_q; + sig_begin_d = sig_begin_q; + rnd_stall_req = '0; + rnd_stall_addr = '0; + rnd_stall_wdata = '0; + rnd_stall_we = '0; + rnd_num_req = '0; + cycle_count_clear = '0; + select_rdata_d = RAM; + transaction = T_PER; + + if (data_req_i & data_gnt_o) begin + if (data_we_i) begin // handle writes + if (data_addr_i < 2 ** RAM_ADDR_WIDTH || + ( (data_addr_i >= dm_halt_addr_i) && + (data_addr_i < (dm_halt_addr_i + (2 ** DBG_ADDR_WIDTH)) )) + ) + begin + setup_transaction(); + end else if (data_addr_i == MMADDR_PRINT) begin + print_wdata = data_wdata_i; + print_valid = '1; + + end else if (data_addr_i == MMADDR_TESTSTATUS) begin + if (data_wdata_i == 123456789) + tests_passed_o = '1; + else if (data_wdata_i == 1) + tests_failed_o = '1; + + end else if (data_addr_i == MMADDR_EXIT) begin + exit_valid_o = '1; + exit_value_o = data_wdata_i; + + end else if (data_addr_i == MMADDR_SIGBEGIN) begin + // sets signature begin + sig_begin_d = data_wdata_i; + + end else if (data_addr_i == MMADDR_SIGEND) begin + // sets signature end + sig_end_d = data_wdata_i; + + end else if (data_addr_i == MMADDR_SIGDUMP) begin + // dump signature and halt +`ifndef VERILATOR + if ($value$plusargs("signature=%s", sig_file)) begin + sig_fd = $fopen(sig_file, "w"); + if (sig_fd == 0) begin + errno = $ferror(sig_fd, error_str); + `uvm_error(MM_RAM_TAG, $sformatf("Cannot open signature file %s for writing (error_str: %s).", sig_file, error_str)) + use_sig_file = 1'b0; + end else begin + use_sig_file = 1'b1; + end + end + + sig_string = ""; + for (logic [31:0] addr = sig_begin_q; addr < sig_end_q; addr +=4) begin + sig_string = {sig_string, $sformatf("%x%x%x%x\n", dp_ram_i.mem[addr+3], dp_ram_i.mem[addr+2], + dp_ram_i.mem[addr+1], dp_ram_i.mem[addr+0])}; + if (use_sig_file) begin + $fdisplay(sig_fd, "%x%x%x%x", dp_ram_i.mem[addr+3], dp_ram_i.mem[addr+2], + dp_ram_i.mem[addr+1], dp_ram_i.mem[addr+0]); + end + end + `uvm_info(MM_RAM_TAG, $sformatf("Dumping signature:\n%s", sig_string), UVM_LOW) +`else + if ($value$plusargs("signature=%s", sig_file)) begin + sig_fd = $fopen(sig_file, "w"); + if (sig_fd == 0) begin + $error("can't open file"); + use_sig_file = 1'b0; + end else begin + use_sig_file = 1'b1; + end + end + + $display("%m @ %0t: Dumping signature", $time); + for (logic [31:0] addr = sig_begin_q; addr < sig_end_q; addr +=4) begin + $display("%x%x%x%x", dp_ram_i.mem[addr+3], dp_ram_i.mem[addr+2], + dp_ram_i.mem[addr+1], dp_ram_i.mem[addr+0]); + if (use_sig_file) begin + $fdisplay(sig_fd, "%x%x%x%x", dp_ram_i.mem[addr+3], dp_ram_i.mem[addr+2], + dp_ram_i.mem[addr+1], dp_ram_i.mem[addr+0]); + end + end +`endif // ifndef VERILATOR + exit_valid_o = '1; // signal halt to testbench + exit_value_o = '0; + + end else if (data_addr_i == MMADDR_TIMERREG) begin + timer_wdata = data_wdata_i; + timer_reg_valid = '1; + + end else if (data_addr_i == MMADDR_TIMERVAL) begin + timer_wdata = data_wdata_i; + timer_val_valid = '1; + + end else if (data_addr_i == MMADDR_DBG) begin + debugger_wdata = data_wdata_i; + debugger_valid = '1; + + end else if (data_addr_i[31:16] == MMADDR_RNDSTALL) begin + rnd_stall_req = data_req_i; + rnd_stall_wdata = data_wdata_i; + rnd_stall_addr = data_addr_i; + rnd_stall_we = data_we_i; + end else if (data_addr_i == MMADDR_TICKS) begin + cycle_count_clear = 1; + end else begin + // out of bounds write + end + + end else begin // handle reads + if (data_addr_i < 2 ** RAM_ADDR_WIDTH || + ( (data_addr_i >= dm_halt_addr_i) && + (data_addr_i < (dm_halt_addr_i + (2 ** DBG_ADDR_WIDTH)) )) + ) + begin + select_rdata_d = RAM; + + setup_transaction(); + end else if (data_addr_i[31:16] == MMADDR_RNDSTALL) begin + select_rdata_d = RND_STALL; + + rnd_stall_req = data_req_i; + rnd_stall_wdata = data_wdata_i; + rnd_stall_addr = data_addr_i; + rnd_stall_we = data_we_i; + end else if (data_addr_i[31:0] == MMADDR_RNDNUM) begin + rnd_num_req = 1'b1; + select_rdata_d = RND_NUM; + end else if (data_addr_i == MMADDR_TICKS) begin + select_rdata_d = TICKS; + end else + select_rdata_d = ERR; + + end + end + end + +`ifndef VERILATOR + // signal out of bound writes + out_of_bounds_write: assert property + (@(posedge clk_i) disable iff (~rst_ni) + (data_req_i && data_we_i |-> data_addr_i < 2 ** RAM_ADDR_WIDTH + || ( (data_addr_i >= dm_halt_addr_i) && + (data_addr_i < (dm_halt_addr_i + (2 ** DBG_ADDR_WIDTH)) ) + ) + || data_addr_i == MMADDR_PRINT + || data_addr_i == MMADDR_TIMERREG + || data_addr_i == MMADDR_TIMERVAL + || data_addr_i == MMADDR_DBG + || data_addr_i == MMADDR_TESTSTATUS + || data_addr_i == MMADDR_EXIT + || data_addr_i == MMADDR_SIGBEGIN + || data_addr_i == MMADDR_SIGEND + || data_addr_i == MMADDR_SIGDUMP + || data_addr_i == MMADDR_TICKS + || data_addr_i[31:16] == MMADDR_RNDSTALL)) + else `uvm_fatal(MM_RAM_TAG, $sformatf("out of bounds write to %08x with %08x", data_addr_i, data_wdata_i)) +`endif + + logic[31:0] data_rdata_mux; + + // make sure we select the proper read data + always_comb begin: read_mux + data_rdata_mux = '0; + + if(select_rdata_q == RAM) begin + data_rdata_mux = core_data_rdata; + end else if(select_rdata_q == RND_STALL) begin + data_rdata_mux = rnd_stall_rdata; +`ifndef VERILATOR + `uvm_fatal(MM_RAM_TAG, $sformatf("out of bounds read from %08x\nRandom stall generator is not supported with Verilator", data_addr_i)); +`endif + end else if (select_rdata_q == RND_NUM) begin + data_rdata_mux = rnd_num; + end else if (select_rdata_q == TICKS) begin + data_rdata_mux = cycle_count_q; +`ifndef VERILATOR + if (cycle_count_overflow_q) begin + `uvm_fatal(MM_RAM_TAG, "cycle counter read after overflow"); + end + end else if (select_rdata_q == ERR) begin + `uvm_error(MM_RAM_TAG, $sformatf("out of bounds read from %08x (RAM_ADDR_WIDTH=%0d; dm_halt_addri=%08x, DBG_ADDR_WIDTH=%0d)", + data_addr_i, RAM_ADDR_WIDTH, dm_halt_addr_i, DBG_ADDR_WIDTH)) +`endif + end + end + + // print to stdout pseudo peripheral + always_ff @(posedge clk_i, negedge rst_ni) begin: print_peripheral + if(print_valid) begin + if ($test$plusargs("verbose")) begin + if (32 <= print_wdata && print_wdata < 128) + $display("OUT: '%c'", print_wdata[7:0]); + else + $display("OUT: %3d", print_wdata); + + end else begin + $write("%c", print_wdata[7:0]); +`ifndef VERILATOR + $fflush(); +`endif + end + end + end + + assign irq_o = irq_q | rnd_irq << RND_IRQ_ID; + + // Set irq vector to timer_irq_mask_q when timer counts down + // irq bit cleared when acknowledged + always_ff @(posedge clk_i, negedge rst_ni) begin: tb_irq_timer + if(~rst_ni) begin + timer_irq_mask_q <= '0; + timer_cnt_q <= '0; + irq_q <= '0; + end else begin + // set timer irq mask + if(timer_reg_valid) + timer_irq_mask_q <= timer_wdata; + + // write timer value + if(timer_val_valid) + timer_cnt_q <= timer_wdata; + else if(timer_cnt_q > 0) + timer_cnt_q <= timer_cnt_q - 1; + + // set/clear irq + if(timer_cnt_q == 1) + irq_q <= timer_irq_mask_q ; + else if(irq_ack_i) + irq_q[irq_id_i] <= 1'b0; + + end // else: !if(~rst_ni) + end // block: tb_irq_timer + + // Count cycles + always_ff @(posedge clk_i, negedge rst_ni) begin: tb_cycle_counter + if (~rst_ni) begin + cycle_count_q <= '0; + cycle_count_overflow_q <= 0; + end else begin + if (cycle_count_clear) begin + cycle_count_q <= '0; + end else begin + cycle_count_q <= cycle_count_q + 1; + end + + if (cycle_count_q + 1 == 0) begin + cycle_count_overflow_q <= 1; + end + end + end + + // Update random stall control + always @(posedge clk_i, negedge rst_ni) begin: tb_stall + if(~rst_ni) begin + rnd_stall_rdata <= '0; + end else begin + if(rnd_stall_req) begin + if(rnd_stall_we) + rnd_stall_regs[rnd_stall_addr[5:2]] <= rnd_stall_wdata; + else + rnd_stall_rdata <= rnd_stall_regs[rnd_stall_addr[5:2]]; + end + end + end // block: tb_stall + + // ------------------------------------------------------------- + // Generate a random number using the SystemVerilog random number function + always_ff @(posedge clk_i, negedge rst_ni) begin : rnd_num_gen + if (!rst_ni) + rnd_num <= 32'h0; + else if (rnd_num_req) +`ifndef VERILATOR + rnd_num <= $urandom(); +`else + rnd_num <= 32'h0; +`endif + end + + // ------------------------------------------------------------- + // Control debug_req. Writing to this alias will change or create + // a debug_req pulse. The debug_req can be a pulse or level change, + // can have a delay when to assert, and also have pulse duration + // determined by the values in the wdata field: + // + // wdata[31] = debug_req signal value + // wdata[30] = debug request mode, 0= level, 1= pulse + // wdata[29] = debug pulse duration random + // wdata[28:16] = debug pulse duration or pulse random max range + // wdata[15] = random start + // wdata[14:0] = start delay or start random max range + + logic [14:0] debugger_start_cnt_q; + logic debug_req_value_q; + logic [12:0] debug_req_duration_q; + always_ff @(posedge clk_i, negedge rst_ni) begin: tb_debugger + if(~rst_ni) begin + debugger_start_cnt_q <= '0; + debug_req_value_q <= '0; + debug_req_duration_q <= '0; + debug_req_o <= '0; + end else begin + + if(debugger_valid && (debugger_start_cnt_q==0) && (debug_req_duration_q==0)) begin + if(debugger_wdata[15]) //If random start + // then set max random delay range to wdata[14:0] + // note, if wdata[14:0] == 0, then assign max random range to 128 +`ifndef VERILATOR + debugger_start_cnt_q <= $urandom_range(1,~|debugger_wdata[14:0] ? 128 : debugger_wdata[14:0]); +`else + debugger_start_cnt_q <= 1; +`endif + else + // else, the delay is determined by wdata[14:0] + // note, if wdata[14:0] == 0, then assign value to 1 + debugger_start_cnt_q <= ~|debugger_wdata[14:0] ? 1 : debugger_wdata[14:0]; + + debug_req_value_q <= debugger_wdata[31]; // value to be applied to debug_req + + if(!debugger_wdata[30]) // If mode is level then set duration to 0 + debug_req_duration_q <= 'b0; + else // Else mode is pulse + if(debugger_wdata[29]) // If random pulse width + // then set max random pulse width to wdata[28:16] + // note, if wdata[28:16] ==0, then assign max to 128 +`ifndef VERILATOR + debug_req_duration_q <= $urandom_range(1,~|debugger_wdata[28:16] ? 128 : debugger_wdata[28:16]); +`else + debugger_start_cnt_q <= 1; +`endif + else + // else, the pulse is determined by wdata[28:16] + // note, if wdata[28:16]==0, then set pulse width to 1 + debug_req_duration_q <= ~|debugger_wdata[28:16] ? 1 : debugger_wdata[28:16]; + + end else begin + // Count down the delay to start + if(debugger_start_cnt_q > 0)begin + debugger_start_cnt_q <= debugger_start_cnt_q - 1; + // At count == 1, then assert the debug_req + if(debugger_start_cnt_q == 1) + debug_req_o <= debug_req_value_q; + end + // Count down debug_req pulse duration + else if(debug_req_duration_q > 0)begin + debug_req_duration_q <= debug_req_duration_q - 1; + // At count == 1, then de-assert debug_req + if(debug_req_duration_q == 1) + debug_req_o <= !debug_req_value_q; + end + end + end + end + + // ------------------------------------------------------------- + // show writes if requested + always_ff @(posedge clk_i, negedge rst_ni) begin: verbose_writes + if ($test$plusargs("verbose") && data_req_i && data_we_i) + $display("write addr=0x%08x: data=0x%08x", + data_addr_i, data_wdata_i); + end + + // instantiate the ram + dp_ram + #(.ADDR_WIDTH (RAM_ADDR_WIDTH), + .INSTR_RDATA_WIDTH(INSTR_RDATA_WIDTH)) + dp_ram_i + ( + .clk_i ( clk_i ), + + .en_a_i ( ram_instr_req ), + .addr_a_i ( ram_instr_addr ), + .wdata_a_i ( '0 ), // Not writing so ignored + .rdata_a_o ( ram_instr_rdata ), + .we_a_i ( '0 ), + .be_a_i ( 4'b1111 ), // Always want 32-bits + + .en_b_i ( ram_data_req ), + .addr_b_i ( ram_data_addr ), + .wdata_b_i ( ram_data_wdata ), + .rdata_b_o ( ram_data_rdata ), + .we_b_i ( ram_data_we ), + .be_b_i ( ram_data_be )); + + riscv_rvalid_stall instr_rvalid_stall_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .req_i ( instr_req_i ), + .gnt_i ( instr_gnt_o ), + .we_i ( 1'b0 ), + .rdata_i ( ram_instr_rdata ), + .rdata_o ( instr_rdata_o ), + .rvalid_o ( instr_rvalid_o ), + .en_stall_i ( rnd_stall_regs[RND_STALL_INSTR_EN][0]), + .stall_mode_i ( rnd_stall_regs[RND_STALL_INSTR_MODE] ), + .max_stall_i ( rnd_stall_regs[RND_STALL_INSTR_MAX] ), + .valid_stall_i( rnd_stall_regs[RND_STALL_INSTR_VALID])); + + riscv_rvalid_stall data_rvalid_stall_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .req_i ( data_req_i ), + .gnt_i ( data_gnt_o ), + .we_i ( data_we_i ), + .rdata_i ( data_rdata_mux ), + .rdata_o ( data_rdata_o ), + .rvalid_o ( data_rvalid_o ), + .en_stall_i ( rnd_stall_regs[RND_STALL_DATA_EN][0]), + .stall_mode_i ( rnd_stall_regs[RND_STALL_DATA_MODE] ), + .max_stall_i ( rnd_stall_regs[RND_STALL_DATA_MAX] ), + .valid_stall_i( rnd_stall_regs[RND_STALL_DATA_VALID])); + + // signature range + always @(posedge clk_i, negedge rst_ni) begin + if (~rst_ni) begin + sig_end_q <= '0; + sig_begin_q <= '0; + end else begin + sig_end_q <= sig_end_d; + sig_begin_q <= sig_begin_d; + end + end + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (~rst_ni) begin + select_rdata_q <= RAM; + end else begin + select_rdata_q <= select_rdata_d; + end + end + + assign instr_gnt_o = ram_instr_gnt; + assign data_gnt_o = ram_data_gnt; + + // remap debug code to end of memory + assign instr_addr_remap = ( (instr_addr_i >= dm_halt_addr_i) && + (instr_addr_i < (dm_halt_addr_i + (2 ** DBG_ADDR_WIDTH)) ) ) ? + (instr_addr_i - dm_halt_addr_i) + 2**RAM_ADDR_WIDTH - 2**DBG_ADDR_WIDTH : + instr_addr_i ; + + always_comb + begin + ram_instr_req = instr_req_i; + ram_instr_addr = instr_addr_remap; + ram_instr_gnt = instr_req_i ? 1'b1 : $urandom; + core_instr_rdata = ram_instr_rdata; + + ram_data_req = data_req_dec; + ram_data_addr = data_addr_dec; + ram_data_gnt = data_req_i ? 1'b1 : $urandom; + core_data_rdata = ram_data_rdata; + ram_data_wdata = data_wdata_dec; + ram_data_we = data_we_dec; + ram_data_be = data_be_dec; + + if(rnd_stall_regs[RND_STALL_INSTR_EN]) begin + ram_instr_req = rnd_stall_instr_req; + ram_instr_gnt = rnd_stall_instr_gnt; + end + if(rnd_stall_regs[RND_STALL_DATA_EN]) begin + ram_data_req = rnd_stall_data_req; + ram_data_gnt = rnd_stall_data_gnt; + end + end + + riscv_gnt_stall + #( + .DATA_WIDTH (INSTR_RDATA_WIDTH), + .RAM_ADDR_WIDTH (RAM_ADDR_WIDTH ) + ) + instr_gnt_stall_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .grant_mem_i ( rnd_stall_instr_req ), + .grant_core_o ( rnd_stall_instr_gnt ), + + .req_core_i ( instr_req_i ), + .req_mem_o ( rnd_stall_instr_req ), + + .en_stall_i ( rnd_stall_regs[RND_STALL_INSTR_EN][0]), + .stall_mode_i ( rnd_stall_regs[RND_STALL_INSTR_MODE] ), + .max_stall_i ( rnd_stall_regs[RND_STALL_INSTR_MAX] ), + .gnt_stall_i ( rnd_stall_regs[RND_STALL_INSTR_GNT] ) + ); + + riscv_gnt_stall + #( + .DATA_WIDTH (DATA_RDATA_WIDTH), + .RAM_ADDR_WIDTH (RAM_ADDR_WIDTH ) + ) + data_gnt_stall_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .grant_mem_i ( rnd_stall_data_req ), + .grant_core_o ( rnd_stall_data_gnt ), + + .req_core_i ( data_req_i ), + .req_mem_o ( rnd_stall_data_req ), + + .en_stall_i ( rnd_stall_regs[RND_STALL_DATA_EN][0]), + .stall_mode_i ( rnd_stall_regs[RND_STALL_DATA_MODE] ), + .max_stall_i ( rnd_stall_regs[RND_STALL_DATA_MAX] ), + .gnt_stall_i ( rnd_stall_regs[RND_STALL_DATA_GNT] ) + ); + +`ifndef VERILATOR + riscv_random_interrupt_generator + random_interrupt_generator_i + ( + .rst_ni ( rst_ni ), + .clk_i ( clk_i ), + .irq_i ( 1'b0 ), + .irq_id_i ( '0 ), + .irq_ack_i ( irq_ack_i == 1'b1 && irq_id_i == RND_IRQ_ID ), + .irq_ack_o ( ), + .irq_o ( rnd_irq ), + .irq_id_o ( /*disconnected, always generate RND_IRQ_ID*/ ), + .irq_mode_i ( rnd_stall_regs[10] ), + .irq_min_cycles_i ( rnd_stall_regs[11] ), + .irq_max_cycles_i ( rnd_stall_regs[12] ), + .irq_min_id_i ( RND_IRQ_ID ), + .irq_max_id_i ( RND_IRQ_ID ), + .irq_act_id_o ( ), + .irq_id_we_o ( ), + .irq_pc_id_i ( pc_core_id_i ), + .irq_pc_trig_i ( rnd_stall_regs[13] ) + ); +`endif + +endmodule // ram + +//@DVT_LINTER_WAIVER_END "MT20210811_1" diff --git a/tb/core/tb_riscv/README.md b/tb/core/tb_riscv/README.md new file mode 100644 index 0000000..24ce9a4 --- /dev/null +++ b/tb/core/tb_riscv/README.md @@ -0,0 +1,56 @@ +# TB_RISCV: VERIFICATION ENVIRONMENT FOR THE PULP CORES + +Files and directories foundn here are adopted from the testbench for the PULP +cores. They are now known as the `core` testbench of the CV32E40P testbench. +Note that some information in this file may be out of date. + +## Description + +The top file of this repository is called `tb_riscv_core.sv`. The input and output signals of this unit are the same as RI5CY. +It includes the following components: + - The core instance + - The instance of the perturbation module, that is described in the file `riscv_perturbation.sv`. + This programmable unit is able to introduce stalls on the memory interfaces and to generate interrupts requests. This is accomplished with the units `riscv_random_stall.sv` and `riscv_random_interrupt_generator.sv`, for stalls and interrupts respectively. + - The instance of the simulation checker is used to check the functional correctness of the core, you should set to 1 the parameter SIMCHECKER in the parameters list of the `tb_riscv_core.sv`. + +## How to set up the perturbation module + +The perturbation module is programmable. It contains a set of memory-mapped registers used to program the module according to the user needs. These registers are mapped in the debug unit address space, and the same buses of the debug unit are used to read and write those registers. + +#### How to set up the stalls generators + +Since both the instruction and data stalls generators are instances of the same unit, they are configured with the same steps. + +1) The default mode does not introduce any stalls on the interface. The input of the units are directly forwarded to the output ports. + +2) The mode STANDARD will introduce a fixed number of stalls on the specified signals of the desired interface. + +3) The mode RANDOM will randomly injects stalls on the desired interface. + +#### How to set up the interrupt generator + +As for the stalls generators, the interrupt generator can be easily programmed with load and store instructions. Again, three working modes are supported: + +1) The default mode does not generate any interrupt request. The input of the module, coming from the event unit and the core, are simply forwarded to the output ports. This allows the external peripherals to eventually raise interrupt requests. + +2) The mode RANDOM will randomize on the generation of interrupt requests. In particular, the module randomizes on both the number of stalls that separate two consecutive interrupt requests, as well as on the interrupt causes. + +3) The mode PC_TRIG is about raising an interrupt request when the value of the program counter, taken from the ID stage of the core, is equal to the value stored in a proper perturbation register. For this interrupt cause, the identifier 18 is reserved. + +Following an example to directly use the Assembler to set up the interrupt generator working with the PC-triggered mode: + +``` +la %[pc_trig], pc_label; +sw %[pc_trig], 0(%[perturbation_reg_addr]); +sw %[irq_mode], 0(%[perturbation_mode_addr]); +.... +pc_label: nop; +.... +``` + +In the previous example, `pc_label` represents the instruction address at which the interrupt request will be raised (i.e. when the `nop` instruction is in the ID stage of the core). +The address is stored in the perturbation register and The PC_TRIG mode is set via the last store instruction. + +## TODO: + +The simulation checker should be used yet. It is under development. diff --git a/tb/core/tb_riscv/include/perturbation_defines.sv b/tb/core/tb_riscv/include/perturbation_defines.sv new file mode 100644 index 0000000..3bd380d --- /dev/null +++ b/tb/core/tb_riscv/include/perturbation_defines.sv @@ -0,0 +1,29 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// Author: Francesco Minervini - minervif@student.ethz.ch // +// // +// Additional contributions by: // +// Design Name: Interrupt generator // +// Project Name: RI5CY, Zeroriscy // +// Language: SystemVerilog // +// // +// Description: Defines for the perturbation module // +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +package perturbation_defines; + + parameter STANDARD = 32'h1; + parameter RANDOM = 32'h2; + parameter PC_TRIG = 32'h3; + +endpackage \ No newline at end of file diff --git a/tb/core/tb_riscv/riscv_gnt_stall.sv b/tb/core/tb_riscv/riscv_gnt_stall.sv new file mode 100644 index 0000000..7942b0f --- /dev/null +++ b/tb/core/tb_riscv/riscv_gnt_stall.sv @@ -0,0 +1,142 @@ +// +// Copyright 2020 OpenHW Group +// Copyright 2020 Silicon Labs, Inc. +// +// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://solderpad.org/licenses/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// riscv_gnt_stall.sv +// +// OBI Grant stalling module for CV32E40P +// +// Author: Steve Richmond +// email: steve.richmond@silabs.com +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +module riscv_gnt_stall +`ifndef VERILATOR + import perturbation_defines::*; +`endif + #( + parameter MAX_STALL_N = 1, + RAM_ADDR_WIDTH = 32, + DATA_WIDTH = 32 + ) + +( + input logic clk_i, + input logic rst_ni, + + input logic req_core_i, + output logic req_mem_o, + + // grant to memory + output logic grant_core_o, + input logic grant_mem_i, + + input logic en_stall_i, + input logic [31:0] stall_mode_i, + input logic [31:0] max_stall_i, + input logic [31:0] gnt_stall_i +); + +// ----------------------------------------------------------------------------------------------- +// Local variables +// ----------------------------------------------------------------------------------------------- + +logic req_core_i_q; +logic grant_core_o_q; + +int grant_delay_cnt; +int delay_value; + +// ----------------------------------------------------------------------------------------------- +// Tasks and functions +// ----------------------------------------------------------------------------------------------- +task set_delay_value(); +`ifndef VERILATOR + if (!en_stall_i) + delay_value = 0; + else if (stall_mode_i == perturbation_defines::STANDARD) + delay_value = gnt_stall_i; + else if (stall_mode_i == perturbation_defines::RANDOM) + delay_value = $urandom_range(max_stall_i, 0); + else + delay_value = 0; +`else + delay_value = 0; +`endif +endtask : set_delay_value + +// ----------------------------------------------------------------------------------------------- +// Begin module code +// ----------------------------------------------------------------------------------------------- + +assign req_mem_o = req_core_i; + +always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + req_core_i_q <= 1'b0; + grant_core_o_q <= 1'b0; + end + else begin + req_core_i_q <= req_core_i; + grant_core_o_q <= grant_core_o; + end +end + +always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + grant_core_o <= 1'b0; + grant_delay_cnt <= 0; + end + else begin +`ifdef VERILATOR + //#1; +`else + #(100ps); +`endif + + // When request is removed, randomize gnt + if (!req_core_i) begin + grant_core_o <= $urandom; //@DVT_LINTER_WAIVER "MT20211214_2" disable SVTB.29.1.3.1 + end + + // New request coming in + else if (grant_core_o_q || !req_core_i_q) begin + // Initialize stall here + set_delay_value(); + if (delay_value == 0) begin + grant_delay_cnt <= 0; + grant_core_o <= 1'b1; + end + else begin + grant_delay_cnt <= delay_value; + grant_core_o <= 1'b0; + end + end + else if (grant_delay_cnt == 1) begin + grant_delay_cnt <= 0; + grant_core_o <= 1'b1; + end + else begin + grant_delay_cnt <= grant_delay_cnt - 1; + end + end +end + +endmodule : riscv_gnt_stall diff --git a/tb/core/tb_riscv/riscv_perturbation.sv b/tb/core/tb_riscv/riscv_perturbation.sv new file mode 100644 index 0000000..1f915fa --- /dev/null +++ b/tb/core/tb_riscv/riscv_perturbation.sv @@ -0,0 +1,322 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +////////////////////////////////////////////////////////////////////////////////////////////// +// // +// Author: Francesco Minervini - minervif@student.ethz.ch // +// // +// Additional contributions by: // +// Design Name: Perturbation Unit // +// Project Name: RI5CY, Zeroriscy // +// Language: SystemVerilog // +// // +// Description: This module instantiates both data and instructions stalls // +// generators as well as the random interrupt generator // +// // +////////////////////////////////////////////////////////////////////////////////////////////// + +import riscv_defines::*; +`include "riscv_config.sv" + +module riscv_perturbation +#( + parameter PERT_REGS = 15, + parameter INSTR_RDATA_WIDTH = 32 +) + +( + input logic rst_ni, + input logic clk_i, + + //Instruction interface + input logic pert_instr_req_i, + output logic pert_instr_req_o, + input logic pert_instr_grant_i, + output logic pert_instr_grant_o, + input logic pert_instr_rvalid_i, + output logic pert_instr_rvalid_o, + input logic [31:0] pert_instr_addr_i, + output logic [31:0] pert_instr_addr_o, + input logic [INSTR_RDATA_WIDTH-1:0] pert_instr_rdata_i, + output logic [INSTR_RDATA_WIDTH-1:0] pert_instr_rdata_o, + + //Data interface + input logic pert_data_req_i, + output logic pert_data_req_o, + input logic pert_data_grant_i, + output logic pert_data_grant_o, + input logic pert_data_rvalid_i, + output logic pert_data_rvalid_o, + input logic pert_data_we_i, + output logic pert_data_we_o, + input logic [3:0] pert_data_be_i, + output logic [3:0] pert_data_be_o, + input logic [31:0] pert_data_addr_i, + output logic [31:0] pert_data_addr_o, + input logic [31:0] pert_data_wdata_i, + output logic [31:0] pert_data_wdata_o, + input logic [INSTR_RDATA_WIDTH-1:0] pert_data_rdata_i, + output logic [INSTR_RDATA_WIDTH-1:0] pert_data_rdata_o, + + //Debug-perturbation interface + input logic pert_debug_req_i, + output logic pert_debug_req_o, + input logic pert_debug_gnt_i, + output logic pert_debug_gnt_o, + input logic pert_debug_rvalid_i, + output logic pert_debug_rvalid_o, + input logic pert_debug_we_i, + output logic pert_debug_we_o, + input logic [14:0] pert_debug_addr_i, + output logic [14:0] pert_debug_addr_o, + input logic [31:0] pert_debug_wdata_i, + output logic [31:0] pert_debug_wdata_o, + input logic [31:0] pert_debug_rdata_i, + output logic [31:0] pert_debug_rdata_o, + + //Interrupt interface + input logic pert_irq_i, + output logic pert_irq_o, + input logic [4:0] pert_irq_id_i, + output logic [4:0] pert_irq_id_o, + input logic pert_irq_ack_i, + output logic pert_irq_ack_o, + input logic [4:0] pert_irq_core_resp_id_i, + output logic [4:0] pert_irq_core_resp_id_o, + input logic [31:0] pert_pc_id_i + ); + + logic [31:0] pert_regs [0:PERT_REGS-1]; + logic [3:0] pert_addr_int; + logic [31:0] irq_act_id_int; + logic irq_id_resp_we_i; + //Signals for instruction stalls generator + logic [31:0] pert_instr_mode, pert_instr_max_stall, pert_instr_grant_stall, pert_instr_valid_stall; + //Signals for data stalls generator + logic [31:0] pert_data_mode, pert_data_max_stall, pert_data_grant_stall, pert_data_valid_stall; + //Signals for interrupt generator + logic [31:0] pert_irq_mode, pert_irq_min_cycles, pert_irq_max_cycles, pert_irq_min_id, pert_irq_max_id, pert_irq_pc_trig; + + logic is_perturbation; + + riscv_random_stall instr_random_stalls + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .grant_per_i ( pert_instr_grant_i ), + .grant_per_o ( pert_instr_grant_o ), + + .rvalid_per_i ( pert_instr_rvalid_i ), + .rvalid_per_o ( pert_instr_rvalid_o ), + + .rdata_per_i ( pert_instr_rdata_i ), + .rdata_per_o ( pert_instr_rdata_o ), + + .req_per_i ( pert_instr_req_i ), + .req_mem_o ( pert_instr_req_o ), + + .addr_per_i ( pert_instr_addr_i ), + .addr_mem_o ( pert_instr_addr_o ), + + .wdata_per_i ( ), + .wdata_mem_o ( ), + + .we_per_i ( ), + .we_mem_o ( ), + + .be_per_i ( ), + .be_mem_o ( ), + + .dbg_req_i ( pert_debug_req_i ), + .dbg_we_i ( pert_debug_we_i ), + + .dbg_mode_i ( pert_instr_mode ), + .dbg_max_stall_i ( pert_instr_max_stall ), + + .dbg_gnt_stall_i ( pert_instr_grant_stall ), + .dbg_valid_stall_i ( pert_instr_valid_stall ) + ); + + riscv_random_stall data_random_stalls + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .grant_per_i ( pert_data_grant_i ), + .grant_per_o ( pert_data_grant_o ), + + .rvalid_per_i ( pert_data_rvalid_i ), + .rvalid_per_o ( pert_data_rvalid_o ), + + .rdata_per_i ( pert_data_rdata_i ), + .rdata_per_o ( pert_data_rdata_o ), + + .req_per_i ( pert_data_req_i ), + .req_mem_o ( pert_data_req_o ), + + .addr_per_i ( pert_data_addr_i ), + .addr_mem_o ( pert_data_addr_o ), + + .wdata_per_i ( pert_data_wdata_i ), + .wdata_mem_o ( pert_data_wdata_o ), + + .we_per_i ( pert_data_we_i ), + .we_mem_o ( pert_data_we_o ), + + .be_per_i ( pert_data_be_i ), + .be_mem_o ( pert_data_be_o ), + + .dbg_req_i ( pert_debug_req_i ), + .dbg_we_i ( pert_debug_we_i ), + + .dbg_mode_i ( pert_data_mode ), + .dbg_max_stall_i ( pert_data_max_stall ), + + .dbg_gnt_stall_i ( pert_data_grant_stall ), + .dbg_valid_stall_i ( pert_data_valid_stall ) + ); + + + riscv_random_interrupt_generator riscv_random_interrupt_generator_i + ( + .rst_ni ( rst_ni ), + .clk_i ( clk_i ), + .irq_i ( pert_irq_i ), + .irq_id_i ( pert_irq_id_i ), + .irq_ack_i ( pert_irq_ack_i ), + .irq_ack_o ( pert_irq_ack_o ), + .irq_o ( pert_irq_o ), + .irq_id_o ( pert_irq_id_o ), + .irq_mode_i ( pert_irq_mode ), + .irq_min_cycles_i ( pert_irq_min_cycles ), + .irq_max_cycles_i ( pert_irq_max_cycles ), + .irq_min_id_i ( pert_irq_min_id ), + .irq_max_id_i ( pert_irq_max_id ), + .irq_act_id_o ( irq_act_id_int ), + .irq_id_we_o ( irq_id_resp_we_i ), + .irq_pc_id_i ( pert_pc_id_i ), + .irq_pc_trig_i ( pert_irq_pc_trig ) + ); + +assign pert_addr_int = pert_debug_addr_i[5:2]; + +assign is_perturbation = pert_debug_addr_i[13:8] == 6'b000110; + +always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + for(int i=0; i= 0; + n<= max_val; + }; + stalls = wait_cycles.n; + + end else begin + stalls = 0; + end + + + while(stalls != 0) begin + @(negedge clk_i); + stalls--; + end + + @(negedge clk_i); + if(req_per_q == 1'b1) begin + grant_core_o = 1'b1; + mem_acc.addr = addr_core_i; + mem_acc.be = be_core_i; + mem_acc.we = we_core_i; + mem_acc.wdata = wdata_core_i; + core_reqs.put(mem_acc); + core_resps_granted.put(1'b1); + end + + end + end + + initial + begin : data_process + stall_mem_t mem_acc; + automatic rand_data_cycles wait_cycles = new (); + logic granted; + int temp, stalls, max_val; + + while(1) begin + @(posedge clk_i); + #1; + rvalid_core_o = 1'b0; + rdata_core_o = 'x; + + core_resps_granted.get(granted); + + core_resps.get(mem_acc); + + if(stall_mode_i == STANDARD) begin //FIXED NUMBER OF STALLS MODE + stalls = valid_stall_i; + end else if(stall_mode_i == RANDOM) begin + max_val = max_stall_i; + temp = wait_cycles.randomize() with { + n>= 0; + n<= max_val; + }; + stalls = wait_cycles.n; + + end else begin + + stalls = 0; + end + + + while(stalls != 0) begin + @(negedge clk_i); + stalls--; + end + + rdata_core_o = mem_acc.rdata; + rvalid_core_o = 1'b1; + end + end + + initial + begin : wait_for_grant + stall_mem_t mem_acc; + we_mem_o = 1'b0; + req_mem_o = 1'b0; + addr_mem_o = '0; + be_mem_o = 4'b0; + wdata_mem_o = 'x; + + while(1) begin + @(posedge clk_i); + #1; + req_mem_o = 1'b0; + addr_mem_o = '0; + wdata_mem_o = 'x; + core_reqs.get(mem_acc); + req_mem_o = 1'b1; + addr_mem_o = mem_acc.addr; + we_mem_o = mem_acc.we; + be_mem_o = mem_acc.be; + wdata_mem_o = mem_acc.wdata; + + wait(grant_per_q); + memory_transfers.put(mem_acc); + + end + end + + initial + begin : wait_for_valid + stall_mem_t mem_acc; + while(1) begin + memory_transfers.get(mem_acc); + + wait(rvalid_per_q == 1'b1); + @(negedge clk_i); + mem_acc.rdata = rdata_mem_i; + + core_resps.put(mem_acc); + + end + end + `endif + endmodule diff --git a/tb/core/tb_riscv/riscv_rvalid_stall.sv b/tb/core/tb_riscv/riscv_rvalid_stall.sv new file mode 100644 index 0000000..1ec9f0c --- /dev/null +++ b/tb/core/tb_riscv/riscv_rvalid_stall.sv @@ -0,0 +1,184 @@ +// +// Copyright 2020 OpenHW Group +// Copyright 2020 Silicon Labs, Inc. +// +// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://solderpad.org/licenses/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// riscv_rvalid_stall.sv +// +// Simple FIFO to store data responses for the OBI +// This file should be completely compatible with Verilator and all commerical SystemVerilog simulators +// +// Author: Steve Richmond +// email: steve.richmond@silabs.com +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +module riscv_rvalid_stall( + // Clock, reset + input clk_i, + input rst_ni, + + // Request/gnt interface, to push entries into the FIFO + input req_i, + input gnt_i, + input we_i, + + // Read data valid, signals that read data from RAM is valid on this cycle + input [31:0] rdata_i, + + // Response bus, connect directly to OBI response port + output logic [31:0] rdata_o, + output logic rvalid_o, + + // Stall knobs + input logic en_stall_i, + input logic [31:0] stall_mode_i, + input logic [31:0] max_stall_i, + input logic [31:0] valid_stall_i +); + +// ----------------------------------------------------------------------------------------------- +// Local parameters +// ----------------------------------------------------------------------------------------------- +localparam FIFO_DATA_WL = 32; +localparam FIFO_DELAY_WL = 4; // Up to 15 cycles of delay +localparam FIFO_WE_WL = 1; + +localparam FIFO_DATA_LSB = 0; +localparam FIFO_DELAY_LSB = FIFO_DATA_LSB + FIFO_DATA_WL; +localparam FIFO_WE_LSB = FIFO_DELAY_LSB + FIFO_DELAY_WL; + +localparam FIFO_WL = FIFO_DATA_WL + FIFO_DELAY_WL + FIFO_WE_WL; + +localparam FIFO_DEPTH = 8; +localparam FIFO_PTR_WL = $clog2(FIFO_DEPTH) + 1; + +// ----------------------------------------------------------------------------------------------- +// Local variables +// ----------------------------------------------------------------------------------------------- +wire fifo_empty; +wire fifo_full; +wire fifo_push; + +logic [FIFO_PTR_WL-1:0] wptr; +logic [FIFO_PTR_WL-1:0] rptr; +logic [FIFO_PTR_WL-2:0] wptr_rdata; + +reg [FIFO_WL-1:0] fifo[FIFO_DEPTH]; +reg rvalid_i_q; + +wire [FIFO_DELAY_WL-1:0] current_delay; + +int i; + +// ----------------------------------------------------------------------------------------------- +// Tasks and functions +// ----------------------------------------------------------------------------------------------- +`ifndef VERILATOR +function logic [FIFO_DELAY_WL-1:0] get_random_delay(); + if (!en_stall_i) + get_random_delay = 0; + else if (stall_mode_i == perturbation_defines::STANDARD) + get_random_delay = valid_stall_i; + else if (stall_mode_i == perturbation_defines::RANDOM) + get_random_delay = $urandom_range(max_stall_i, 0); //@DVT_LINTER_WAIVER "MT20211214_3" disable SVTB.29.1.3.1 + else + get_random_delay = 0; +endfunction : get_random_delay +`endif + +// ----------------------------------------------------------------------------------------------- +// Begin module code +// ----------------------------------------------------------------------------------------------- + +assign fifo_push = req_i && gnt_i; + +always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + wptr <= '0; + rptr <= '0; + end + else begin + wptr <= (req_i && gnt_i) ? wptr + 1 : wptr; + rptr <= (rvalid_o) ? rptr + 1 : rptr; + end +end + +assign fifo_empty = (wptr == rptr) ? 1 : 0; +assign fifo_full = (wptr == {~rptr[FIFO_PTR_WL-1], rptr[FIFO_PTR_WL-2:0]}) ? 1 : 0; + +always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + for (i = 0; i < FIFO_DEPTH; i++) begin + fifo[i] = {FIFO_WL{1'b0}}; + end + end + else begin + if (fifo_push) begin + fifo[wptr[FIFO_PTR_WL-2:0]] = { + we_i, +`ifdef VERILATOR + 4'h0, +`else + get_random_delay(), +`endif + 32'h0}; + + wptr_rdata <= wptr[FIFO_PTR_WL-2:0]; + + rvalid_i_q <= (!we_i) ? 1 : 0; + end + else begin + rvalid_i_q <= 1'b0; + end + + if (rvalid_i_q) begin + fifo[wptr_rdata][31:0] <= fifo[wptr_rdata][FIFO_WE_LSB] ? 32'h0 : rdata_i; + end + end +end + +// Read interface +assign current_delay = fifo[rptr[FIFO_PTR_WL-2:0]][FIFO_DELAY_LSB +: FIFO_DELAY_WL]; + +always @(*) begin + rdata_o = '0; + rvalid_o = '0; + if (!fifo_empty && current_delay == 0) begin + rvalid_o = 1'b1; + if (rptr[FIFO_PTR_WL-2:0] == wptr_rdata && rvalid_i_q) + if (fifo[rptr[FIFO_PTR_WL-2:0]][FIFO_WE_LSB]) + rdata_o = 32'h0; + else + rdata_o = rdata_i; + else + rdata_o = fifo[rptr[FIFO_PTR_WL-2:0]][FIFO_DATA_LSB +: FIFO_DATA_WL]; + end +end + +// Manage current delay counter +always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + end + else begin + if (!fifo_empty && current_delay > 0) begin + fifo[rptr[FIFO_PTR_WL-2:0]][FIFO_DELAY_LSB +: FIFO_DELAY_WL] <= current_delay - 1; + end + end +end + +endmodule : riscv_rvalid_stall diff --git a/tb/core/tb_riscv/riscv_simchecker.sv b/tb/core/tb_riscv/riscv_simchecker.sv new file mode 100644 index 0000000..5442475 --- /dev/null +++ b/tb/core/tb_riscv/riscv_simchecker.sv @@ -0,0 +1,645 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +////////////////////////////////////////////////////////////////////////////////////////////// +// Engineer: Andreas Traber - atraber@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// Francesco Minervini - minervif@student.ethz.ch // +// // +// Design Name: RISC-V Simchecker // +// Project Name: RI5CY // +// Language: SystemVerilog // +// // +// Description: Compares the executed instructions with a golden model // +// // +////////////////////////////////////////////////////////////////////////////////////////////// +`ifndef VERILATOR +`include "riscv_config.sv" + +import riscv_defines::*; + +// do not import anything if the simchecker is not used +// this gets rid of warnings during simulation +import "DPI-C" function chandle riscv_checker_init(input int boot_addr, input int core_id, input int cluster_id, input string name); +import "DPI-C" function int riscv_checker_step(input chandle cpu, input longint simtime, input int cycle, input logic [31:0] pc, input logic [31:0] instr); +import "DPI-C" function void riscv_checker_irq(input chandle cpu, input int irq, input int irq_no); +import "DPI-C" function void riscv_checker_mem_access(input chandle cpu, input int we, input logic [31:0] addr, input logic [31:0] data); +import "DPI-C" function void riscv_checker_reg_access(input chandle cpu, input logic [31:0] addr, input logic [31:0] data); + +module riscv_simchecker +( + // Clock and Reset + input logic clk, + input logic rst_n, + + input logic fetch_enable, + input logic [31:0] boot_addr, + input logic [3:0] core_id, + input logic [5:0] cluster_id, + + input logic [15:0] instr_compressed, + input logic if_valid, + input logic pc_set, + + input logic [31:0] pc, + input logic [31:0] instr, + input logic is_compressed, + input logic id_valid, + input logic is_decoding, + input logic is_illegal, + input logic is_interrupt, + input logic [4:0] irq_no, + input logic pipe_flush, + input logic irq_i, + input logic is_mret, + + input logic int_enable, + + //Signals added for FPU ops + input logic lsu_ready_wb, + input logic apu_ready_wb, + input logic wb_contention, + + input logic apu_en_id, + input logic apu_req, + input logic apu_gnt, + input logic apu_valid, + input logic apu_singlecycle, + input logic apu_multicycle, + input logic [1:0] apu_latency, + input logic apu_active, + input logic apu_en_ex, +//////////////////////////////////////////// + input logic ex_valid, + input logic [ 5:0] ex_reg_addr, + + input logic ex_reg_we, + input logic [31:0] ex_reg_wdata, + + input logic ex_data_req, + input logic ex_data_gnt, + input logic ex_data_we, + input logic [31:0] ex_data_addr, + input logic [31:0] ex_data_wdata, + + input logic lsu_misaligned, + input logic wb_bypass, + + input logic wb_valid, + input logic [ 5:0] wb_reg_addr, + + input logic wb_reg_we, + input logic [31:0] wb_reg_wdata, + + input logic wb_data_rvalid, + input logic [31:0] wb_data_rdata + + +); + + // DPI stuff + chandle dpi_simdata; + + // SV-only stuff + typedef struct { + logic [ 5:0] addr; + logic [31:0] value; + } reg_t; + + typedef struct { + logic [31:0] addr; + logic we; + logic [ 3:0] be; + logic [31:0] wdata; + logic [31:0] rdata; + } mem_acc_t; + + class instr_trace_t; + time simtime; + int cycles; + logic [31:0] pc; + logic [31:0] instr; + logic irq; + logic [ 4:0] irq_no; + logic wb_bypass; + logic fpu_first; + int next_wait; + reg_t regs_write[$]; + mem_acc_t mem_access[$]; + + function new (); + irq = 1'b0; + wb_bypass = 1'b1; + fpu_first = 1'b0; + next_wait = 0; + regs_write = {}; + mem_access = {}; + endfunction + endclass + + + mailbox rdata_stack = new (4); + integer rdata_writes = 0; + + integer cycles; + integer mismatch=0; + logic pipe_wait; + logic fpu_in_ex; + int fp_completed; + int fp_ended; + int instr_is_valid; + + logic [15:0] instr_compressed_id; + logic is_irq_if, is_irq_id; + logic [ 4:0] irq_no_id, irq_no_if; + logic apu_req_accepted; + logic apu_res_on_alu_port; + logic apu_res_on_lsu_port; + logic enable = 0; + logic [ 1:0] apu_lat; + + instr_trace_t instr_queue[$]; + + localparam SIMCHECKER_VERBOSE = 0; + + mailbox instr_ex = new (4); + mailbox instr_wb = new (4); + mailbox fpu_active = new (4); + mailbox fpu_ex = new (4); + mailbox fpu_wb = new (4); + mailbox fpu_done = new (4); + mailbox fpu_end = new (4); + mailbox instr_wait = new (4); + + mailbox fpu_wait = new (4); + + // simchecker initialization + initial + begin + + enable = 1'b1; + pipe_wait = 1'b0; + fpu_in_ex = 1'b0; + fp_completed = 0; + fp_ended = 0; + instr_is_valid = 0; + instr_queue = {}; + wait(rst_n == 1'b1); + wait(fetch_enable == 1'b1); + + dpi_simdata = riscv_checker_init(boot_addr, core_id, cluster_id, "riscyv2"); + + end + + // virtual ID/EX pipeline + initial + begin + + instr_trace_t trace; + mem_acc_t mem_acc; + reg_t reg_write; + + while(1) begin + instr_ex.get(trace); + + + // wait until we are going to the next stage + do begin + @(negedge clk); + + reg_write.addr = ex_reg_addr; + reg_write.value = ex_reg_wdata; + + if(trace.fpu_first == 1'b1) begin //If the fp operation has been completed, the instruction doesn't have to wait anymore + if(apu_valid) + trace.fpu_first = 1'b0; + end + + + if (ex_reg_we) begin + trace.regs_write.push_back(reg_write); + end + // look for data accesses and log them + if (ex_data_req && ex_data_gnt) begin + mem_acc.addr = ex_data_addr; + mem_acc.we = ex_data_we; + + + if (mem_acc.we) + mem_acc.wdata = ex_data_wdata; + else + mem_acc.wdata = 'x; + + trace.mem_access.push_back(mem_acc); + end + end while (((!ex_valid || lsu_misaligned) && (!wb_bypass)) || (wb_contention)); //wb_contention is normally low, so it will not stop the process. As soon as there is a contention, this + //condition is true and restart storing values without unlocking the following check process. + trace.wb_bypass = wb_bypass; + + instr_wb.put(trace); + end + + end + + // virtual EX/WB pipeline + initial + begin + + instr_trace_t trace, fpu_trace; + reg_t reg_write; + logic [31:0] tmp_discard; + logic [31:0] rdata_tmp; + + while(1) begin + instr_wb.get(trace); + + if (!trace.wb_bypass) begin + + // wait until we are going to the next stage + do begin + @(negedge clk); + #1; + + + // pop rdata from stack when there were pending writes + while(rdata_stack.num() > 0 && rdata_writes > 0) begin + rdata_writes--; + rdata_stack.get(tmp_discard); + end + + if(lsu_ready_wb && !apu_ready_wb) begin + reg_write.addr = wb_reg_addr; + reg_write.value = wb_reg_wdata; + if(wb_reg_we) begin + trace.regs_write.push_back(reg_write); + end + end + + end while ((!wb_valid) && (!lsu_ready_wb)); + + + reg_write.addr = wb_reg_addr; + reg_write.value = wb_reg_wdata; + + + + if (wb_reg_we && !apu_valid) begin + trace.regs_write.push_back(reg_write); + end + + // take care of rdata + foreach(trace.mem_access[i]) begin + if (trace.mem_access[i].we) begin + // for writes we don't need to wait for the rdata, so if it has + // not appeared yet, we count it and remove it later from out + // stack + if (rdata_stack.num() > 0) + rdata_stack.get(tmp_discard); + else + rdata_writes++; + + end else begin + if (rdata_stack.num() == 0) + $warning("rdata stack is empty, but we are waiting for a read"); + + if (rdata_writes > 0) + $warning("rdata_writes is > 0, but we are waiting for a read"); + + // indirect addressing workaround for ncsim + rdata_tmp = trace.mem_access[i].rdata; + rdata_stack.get(rdata_tmp); + end + end + end + + + if(trace.fpu_first == 1'b1) begin //Check whether one instruction has to wait for the fp operation to be completed, then accumulate all the incoming instructions + pipe_wait = 1'b1; + #1; + end + + if(pipe_wait) begin + instr_wait.put(trace); + + end else begin + + foreach(trace.mem_access[i]) begin + if (trace.mem_access[i].we) begin + //TEMPORARY SOLUTION WITH BYPASS FOR UNINITIALIZED REGISTERS + if(trace.mem_access[i].wdata === 'X) + $display("Uninitialized register was met, skip this check"); + else begin + riscv_checker_mem_access(dpi_simdata, trace.mem_access[i].we, trace.mem_access[i].addr, trace.mem_access[i].wdata); + end + end else begin + riscv_checker_mem_access(dpi_simdata, trace.mem_access[i].we, trace.mem_access[i].addr, trace.mem_access[i].rdata); + end + end + + foreach(trace.regs_write[i]) begin + riscv_checker_reg_access(dpi_simdata, trace.regs_write[i].addr, trace.regs_write[i].value); + end + + riscv_checker_irq(dpi_simdata, trace.irq, trace.irq_no); + + if (riscv_checker_step(dpi_simdata, trace.simtime, trace.cycles, trace.pc, trace.instr)) begin + $display("SIMCHECKER: CORE pipeline: Cycle %d at %g ps 0x%x: Cluster %d, Core %d: Mismatch between simulator and RTL detected", trace.cycles, trace.simtime, trace.pc, cluster_id, core_id); + mismatch ++; + if(mismatch > 10) + $stop(); + end + end + end + end + + + + + + assign apu_req_accepted = apu_req & apu_gnt; + assign apu_res_on_alu_port = apu_singlecycle | apu_multicycle; + assign apu_res_on_lsu_port = (!apu_singlecycle & !apu_multicycle); + + initial + begin + + instr_trace_t trace; + while(1) begin + fpu_active.get(trace); + + if(apu_en_ex) begin //This is 1 if we already have a fp instruction in execution + if(~apu_active) begin //APU is active if there is an unreturned request. If it's low but apu_en_ex is high, we have to wait for the + apu_lat = apu_latency; //previous instruction to be done. This depends on the latency of that instruction + while(apu_lat > 1) begin + @(negedge clk); + apu_lat --; + end + + end else begin + do begin + @(negedge clk); + end while(apu_active); //APU is active when there is an unreturned request. FPU is still not available for a new operation. + end + end + + fpu_ex.put(trace); + end + + end + + + initial //FPU pipeline: "ID/EX stage" + begin + + + instr_trace_t trace; + reg_t reg_write;///////////////////////////////////////////////// + while(1) begin + fpu_ex.get(trace); + + if(!apu_req_accepted) begin + do begin + @(negedge clk); + end while(!apu_req_accepted); + end + + if((id_valid && is_decoding) || (pipe_flush && is_decoding) || (is_decoding && is_illegal) || (id_valid && is_mret)) begin + if(apu_en_id) + trace.next_wait = 0; + else begin + + fpu_in_ex = 1'b1; + trace.next_wait = 1; + end + end else trace.next_wait = 0; + + fpu_wb.put(trace); + end + + end + + initial //FPU pipeline: "EX/WB stage" + begin + + + instr_trace_t trace; + reg_t reg_write; + + while(1) begin + fpu_wb.get(trace); + + while(!apu_valid) begin + @(negedge clk); + end + + if(apu_res_on_lsu_port) begin + reg_write.addr = wb_reg_addr; + reg_write.value = wb_reg_wdata; + if(wb_reg_we) begin + + trace.regs_write.push_back(reg_write); + end + end else begin + if(apu_res_on_alu_port) begin + reg_write.addr = ex_reg_addr; + reg_write.value = ex_reg_wdata; + if(ex_reg_we) + trace.regs_write.push_back(reg_write); + end + end + + fpu_done.put(trace); + end + + end + + initial //FPU pipeline: "WB/CHECK STAGE". Call the ISS to check the results + begin + + + instr_trace_t trace; + while(1) begin + fpu_done.get(trace); + + + foreach(trace.regs_write[i]) begin + + riscv_checker_reg_access(dpi_simdata, trace.regs_write[i].addr, trace.regs_write[i].value); + end + if(riscv_checker_step(dpi_simdata, trace.simtime, trace.cycles, trace.pc, trace.instr)) begin + $display("SIMCHECKER: FPU pipeline: Cycle %d at %g ps 0x%x: Cluster %d, Core %d: Mismatch between simulator and RTL detected", trace.cycles, trace.simtime, trace.pc, cluster_id, core_id); + mismatch ++; + if(mismatch > 10) + $stop(); + end + #1; + + + if(trace.next_wait == 1) begin + fpu_end.put(1); + end + fpu_in_ex = 1'b0; + end + + end + + + initial + begin + + instr_trace_t trace; + + while(1) begin + @(negedge clk); + instr_is_valid = instr_wait.try_get(trace); + if(instr_is_valid == 1) begin + + instr_queue.push_back(trace); + end + + fp_completed = fpu_end.try_get(fp_ended); + if(fp_completed == 1) begin + foreach(instr_queue[i]) begin + for (int j = 0; j < instr_queue[i].mem_access.size(); j++) begin + /* code */ + if(instr_queue[i].mem_access[j].we) begin + if(instr_queue[i].mem_access[j].wdata === 'X) + $display("Uninitialized register was met, skip this check"); + else begin + riscv_checker_mem_access(dpi_simdata, instr_queue[i].mem_access[j].we, instr_queue[i].mem_access[j].addr, instr_queue[i].mem_access[j].wdata); + end + end else begin + riscv_checker_mem_access(dpi_simdata, instr_queue[i].mem_access[j].we, instr_queue[i].mem_access[j].addr, instr_queue[i].mem_access[j].rdata); + end + end + + for (int j = 0; j < instr_queue[i].regs_write.size(); j++) begin + /* code */ + riscv_checker_reg_access(dpi_simdata, instr_queue[i].regs_write[j].addr, instr_queue[i].regs_write[j].value); + end + + riscv_checker_irq(dpi_simdata, instr_queue[i].irq, instr_queue[i].irq_no); + + if(riscv_checker_step(dpi_simdata, instr_queue[i].simtime, instr_queue[i].cycles, instr_queue[i].pc, instr_queue[i].instr)) begin + $display("SIMCHECKER: In INSTR QUEUE: CORE pipeline: Cycle %d at %g ps 0x%x: Cluster %d, Core %d: Mismatch between simulator and RTL detected", cycles, instr_queue[i].simtime, instr_queue[i].pc, cluster_id, core_id); + mismatch ++; + if(mismatch > 10) + $stop(); + end + end + + instr_queue = {}; //Empty the queue, so that is available to accumulate new instructions + pipe_wait = 1'b0; //Pipe doesn't have to wait anymore + fp_completed = 0; + end + end + end + + + // cycle counter + always_ff @(posedge clk, negedge rst_n) + begin + if (rst_n == 1'b0) + cycles = 0; + else + cycles = cycles + 1; + end + + // create rdata stack + initial + begin + + while(1) begin + @(negedge clk); + + if (wb_data_rvalid) begin + rdata_stack.put(wb_data_rdata); + end + end + + end + + always_ff @(enable, posedge clk) + begin + if (pc_set) begin + is_irq_if <= is_interrupt; + irq_no_if <= irq_no; + end else if (if_valid) begin + is_irq_if <= 1'b0; + end + end + + always_ff @(enable, posedge clk) + begin + if (if_valid) begin + instr_compressed_id <= instr_compressed; + is_irq_id <= is_irq_if; + irq_no_id <= irq_no_if; + end else begin + is_irq_id <= is_irq_if; + irq_no_id <= irq_no_if; + end + end + + + + // log execution + initial + begin + + instr_trace_t trace; + + while(1) begin + @(negedge clk); + + // - special case for WFI because we don't wait for unstalling there + // - special case for illegal instructions, since they will not go through + // the pipe + + if ((id_valid && is_decoding) || (pipe_flush && is_decoding) || (is_decoding && is_illegal) || (id_valid && is_mret)) + begin + trace = new (); + + trace.simtime = $time; + trace.cycles = cycles; + trace.pc = pc; + + if (is_compressed) + trace.instr = {instr_compressed_id, instr_compressed_id}; + else + trace.instr = instr; + + + if(is_irq_id) begin + trace.irq = 1'b1; + trace.irq_no = irq_no_id; + end else begin + if(!int_enable) begin + trace.irq = irq_i; + trace.irq_no = irq_no; + end + end + if(apu_en_id) begin + fpu_active.put(trace); + end + else begin + #1; + if(apu_req_accepted && apu_en_ex && ~apu_valid && fpu_in_ex) begin + trace.fpu_first = 1'b1; //This means the instruction will go out of the pipeline + end + instr_ex.put(trace); + end + end + end + end + +endmodule +`endif diff --git a/tb/core/tb_riscv/tb_riscv_core.sv b/tb/core/tb_riscv/tb_riscv_core.sv new file mode 100644 index 0000000..620800a --- /dev/null +++ b/tb/core/tb_riscv/tb_riscv_core.sv @@ -0,0 +1,386 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +///////////////////////////////////////////////////////////////////////////////////////////////// +// // +// Author: Francesco Minervini - minervif@student.ethz.ch // +// // +// Additional contributions by: // +// // +// Design Name: Top level module including riscv core and perturbation // +// module // +// Project Name: TB RISC-V // +// Language: SystemVerilog // +// // +// Description: This module is instantiated in the core_region.sv code // +// when the parameter TB_RISCV is set to 1. // +// This module instantiates one between zero_riscy or riscy // +// core depending on the value of USE_ZERO_RISCY, then // +// it also instantiates and connects the core to the // +// perturbation module. // +// // +///////////////////////////////////////////////////////////////////////////////////////////////// + +module tb_riscv_core +#( + parameter N_EXT_PERF_COUNTERS = 0, + parameter INSTR_RDATA_WIDTH = 32, + parameter PULP_SECURE = 0, + parameter N_PMP_ENTRIES = 16, + parameter PULP_CLUSTER = 1, + parameter FPU = 0, + parameter SHARED_FP = 0, + parameter SHARED_DSP_MULT = 0, + parameter SHARED_INT_DIV = 0, + parameter SHARED_FP_DIVSQRT = 0, + parameter WAPUTYPE = 0, + parameter APU_NARGS_CPU = 3, + parameter APU_WOP_CPU = 6, + parameter APU_NDSFLAGS_CPU = 15, + parameter APU_NUSFLAGS_CPU = 5, + parameter SIMCHECKER = 0 +) +( + // Clock and Reset + input logic clk_i, + input logic rst_ni, + + input logic clock_en_i, // enable clock, otherwise it is gated + input logic test_en_i, // enable all clock gates for testing + + input logic fregfile_disable_i, // disable the fp regfile, using int regfile instead + + // Core ID, Cluster ID and boot address are considered more or less static + input logic [31:0] boot_addr_i, + input logic [ 3:0] core_id_i, + input logic [ 5:0] cluster_id_i, + + // Instruction memory interface + output logic instr_req_o, + input logic instr_gnt_i, + input logic instr_rvalid_i, + output logic [31:0] instr_addr_o, + input logic [INSTR_RDATA_WIDTH-1:0] instr_rdata_i, + + // Data memory interface + output logic data_req_o, + input logic data_gnt_i, + input logic data_rvalid_i, + output logic data_we_o, + output logic [3:0] data_be_o, + output logic [31:0] data_addr_o, + output logic [31:0] data_wdata_o, + input logic [31:0] data_rdata_i, + // apu-interconnect + // handshake signals + output logic apu_master_req_o, + output logic apu_master_ready_o, + input logic apu_master_gnt_i, + // request channel + output logic [31:0] apu_master_operands_o [APU_NARGS_CPU-1:0], + output logic [APU_WOP_CPU-1:0] apu_master_op_o, + output logic [WAPUTYPE-1:0] apu_master_type_o, + output logic [APU_NDSFLAGS_CPU-1:0] apu_master_flags_o, + // response channel + input logic apu_master_valid_i, + input logic [31:0] apu_master_result_i, + input logic [APU_NUSFLAGS_CPU-1:0] apu_master_flags_i, + + // Interrupt inputs + input logic irq_i, // level sensitive IR lines + input logic [4:0] irq_id_i, + output logic irq_ack_o, + output logic [4:0] irq_id_o, + input logic irq_sec_i, + + output logic sec_lvl_o, + + // Debug Interface + input logic debug_req_i, + output logic debug_gnt_o, + output logic debug_rvalid_o, + input logic [14:0] debug_addr_i, + input logic debug_we_i, + input logic [31:0] debug_wdata_i, + output logic [31:0] debug_rdata_o, + output logic debug_halted_o, + input logic debug_halt_i, + input logic debug_resume_i, + + // CPU Control Signals + input logic fetch_enable_i, + output logic core_busy_o, + + input logic [N_EXT_PERF_COUNTERS-1:0] ext_perf_counters_i +); + +localparam PERT_REGS = 15; + +////////////////////////////////////////////////////////////// +//Internal signals to connect core and perturbation module +///////////////////////////////////////////////////////////// + +// Additional instruction signals + +logic instr_req_int; +logic instr_grant_int; +logic instr_rvalid_int; +logic [31:0] instr_addr_int; +logic [INSTR_RDATA_WIDTH-1:0] instr_rdata_int; + +// Additional data signals +logic data_req_int; +logic data_grant_int; +logic data_rvalid_int; +logic data_we_int; +logic [3:0] data_be_int; +logic [31:0] data_addr_int; +logic [31:0] data_wdata_int; +logic [31:0] data_rdata_int; + +// Additional signals for pertubation/debug registers +logic debug_req_int; +logic debug_grant_int; +logic debug_rvalid_int; +logic debug_we_int; +logic [14:0] debug_addr_int; +logic [31:0] debug_wdata_int; +logic [31:0] debug_rdata_int; + +// Additional interrupt signals +logic irq_int; +logic irq_ack_int; +logic [4:0] irq_id_int; +logic [4:0] irq_core_resp_id_int; + + + + riscv_core + #( + .N_EXT_PERF_COUNTERS ( N_EXT_PERF_COUNTERS ), + .INSTR_RDATA_WIDTH ( INSTR_RDATA_WIDTH ), + .PULP_SECURE ( PULP_SECURE ), + .N_PMP_ENTRIES ( N_PMP_ENTRIES ), + .FPU ( FPU ), + .SHARED_FP ( SHARED_FP ), + .SHARED_DSP_MULT ( SHARED_DSP_MULT ), + .SHARED_INT_DIV ( SHARED_INT_DIV ), + .SHARED_FP_DIVSQRT ( SHARED_FP_DIVSQRT ), + .WAPUTYPE ( WAPUTYPE ), + .APU_NARGS_CPU ( APU_NARGS_CPU ), + .APU_WOP_CPU ( APU_WOP_CPU ), + .APU_NDSFLAGS_CPU ( APU_NDSFLAGS_CPU ), + .APU_NUSFLAGS_CPU ( APU_NUSFLAGS_CPU ) + ) + RISCV_CORE + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .clock_en_i ( clock_en_i ), + .test_en_i ( test_en_i ), + + .fregfile_disable_i ( fregfile_disable_i ), + + .boot_addr_i ( boot_addr_i ), + .core_id_i ( core_id_i ), + .cluster_id_i ( cluster_id_i ), + + .instr_req_o ( instr_req_int ), + .instr_gnt_i ( instr_grant_int ), + .instr_rvalid_i ( instr_rvalid_int ), + .instr_addr_o ( instr_addr_int ), + .instr_rdata_i ( instr_rdata_int ), + + .data_req_o ( data_req_int ), + .data_gnt_i ( data_grant_int ), + .data_rvalid_i ( data_rvalid_int ), + .data_we_o ( data_we_int ), + .data_be_o ( data_be_int ), + .data_addr_o ( data_addr_int ), + .data_wdata_o ( data_wdata_int ), + .data_rdata_i ( data_rdata_int ), + + .apu_master_req_o ( apu_master_req_o ), + .apu_master_ready_o ( apu_master_ready_o ), + .apu_master_gnt_i ( apu_master_gnt_i ), + .apu_master_operands_o ( apu_master_operands_o ), + .apu_master_op_o ( apu_master_op_o ), + .apu_master_type_o ( apu_master_type_o ), + .apu_master_flags_o ( apu_master_flags_o ), + + .apu_master_valid_i ( apu_master_valid_i ), + .apu_master_result_i ( apu_master_result_i ), + .apu_master_flags_i ( apu_master_flags_i ), + + .irq_i ( irq_int ), + .irq_id_i ( irq_id_int ), + .irq_ack_o ( irq_ack_int ), + .irq_id_o ( irq_core_resp_id_int ), + .irq_sec_i ( irq_sec_i ), + + .sec_lvl_o ( sec_lvl_o ), + + .debug_req_i ( debug_req_int ), + .debug_gnt_o ( debug_grant_int ), + .debug_rvalid_o ( debug_rvalid_int ), + .debug_addr_i ( debug_addr_int ), + .debug_we_i ( debug_we_int ), + .debug_wdata_i ( debug_wdata_int ), + .debug_rdata_o ( debug_rdata_int ), + .debug_halted_o ( debug_halted_o ), + .debug_halt_i ( debug_halt_i ), + .debug_resume_i ( debug_resume_i ), + + .fetch_enable_i ( fetch_enable_i ), + .core_busy_o ( core_busy_o ), + + .ext_perf_counters_i ( ext_perf_counters_i ) + ); + + // Perturbation module instance + riscv_perturbation + #( + .PERT_REGS ( PERT_REGS ), + .INSTR_RDATA_WIDTH ( INSTR_RDATA_WIDTH ) + ) + riscv_perturbation_i + ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .pert_instr_req_i ( instr_req_int ), + .pert_instr_req_o ( instr_req_o ), + .pert_instr_grant_i ( instr_gnt_i ), + .pert_instr_grant_o ( instr_grant_int ), + .pert_instr_rvalid_i ( instr_rvalid_i ), + .pert_instr_rvalid_o ( instr_rvalid_int ), + .pert_instr_addr_i ( instr_addr_int ), + .pert_instr_addr_o ( instr_addr_o ), + .pert_instr_rdata_i ( instr_rdata_i ), + .pert_instr_rdata_o ( instr_rdata_int ), + + .pert_data_req_i ( data_req_int ), + .pert_data_req_o ( data_req_o ), + .pert_data_grant_i ( data_gnt_i ), + .pert_data_grant_o ( data_grant_int ), + .pert_data_rvalid_i ( data_rvalid_i ), + .pert_data_rvalid_o ( data_rvalid_int ), + .pert_data_we_i ( data_we_int ), + .pert_data_we_o ( data_we_o ), + .pert_data_be_i ( data_be_int ), + .pert_data_be_o ( data_be_o ), + .pert_data_addr_i ( data_addr_int ), + .pert_data_addr_o ( data_addr_o ), + .pert_data_wdata_i ( data_wdata_int ), + .pert_data_wdata_o ( data_wdata_o ), + .pert_data_rdata_i ( data_rdata_i ), + .pert_data_rdata_o ( data_rdata_int ), + + .pert_debug_req_i ( debug_req_i ), + .pert_debug_req_o ( debug_req_int ), + .pert_debug_gnt_i ( debug_grant_int ), + .pert_debug_gnt_o ( debug_gnt_o ), + .pert_debug_rvalid_i ( debug_rvalid_int ), + .pert_debug_rvalid_o ( debug_rvalid_o ), + .pert_debug_we_i ( debug_we_i ), + .pert_debug_we_o ( debug_we_int ), + .pert_debug_addr_i ( debug_addr_i ), + .pert_debug_addr_o ( debug_addr_int ), + .pert_debug_wdata_i ( debug_wdata_i ), + .pert_debug_wdata_o ( debug_wdata_int ), + .pert_debug_rdata_i ( debug_rdata_int ), + .pert_debug_rdata_o ( debug_rdata_o ), + + .pert_irq_i ( irq_i ), + .pert_irq_o ( irq_int ), + .pert_irq_id_i ( irq_id_i ), + .pert_irq_id_o ( irq_id_int ), + .pert_irq_ack_i ( irq_ack_int ), + .pert_irq_ack_o ( irq_ack_o ), + .pert_irq_core_resp_id_i ( irq_core_resp_id_int ), + .pert_irq_core_resp_id_o ( irq_id_o ), + .pert_pc_id_i ( RISCV_CORE.pc_id ) + ); + + + `ifndef VERILATOR + generate + if(SIMCHECKER) begin: ri5cy_simchecker + riscv_simchecker riscv_simchecker_i + ( + .clk ( RISCV_CORE.clk_i ), + .rst_n ( RISCV_CORE.rst_ni ), + + .fetch_enable ( RISCV_CORE.fetch_enable_i ), + .boot_addr ( RISCV_CORE.boot_addr_i ), + .core_id ( RISCV_CORE.core_id_i ), + .cluster_id ( RISCV_CORE.cluster_id_i ), + + .instr_compressed ( RISCV_CORE.if_stage_i.fetch_rdata[15:0] ), + .if_valid ( RISCV_CORE.if_stage_i.if_valid ), + .pc_set ( RISCV_CORE.pc_set ), + + + .pc ( RISCV_CORE.id_stage_i.pc_id_i ), + .instr ( RISCV_CORE.id_stage_i.instr ), + .is_compressed ( RISCV_CORE.is_compressed_id ), + .id_valid ( RISCV_CORE.id_stage_i.id_valid_o ), + .is_decoding ( RISCV_CORE.id_stage_i.is_decoding_o ), + .is_illegal ( RISCV_CORE.id_stage_i.illegal_insn_dec ), + .is_interrupt ( RISCV_CORE.is_interrupt ), + .irq_no ( RISCV_CORE.irq_id_i ), + .pipe_flush ( RISCV_CORE.id_stage_i.controller_i.pipe_flush_i ), + .irq_i ( RISCV_CORE.irq_i ), + .is_mret ( RISCV_CORE.id_stage_i.controller_i.mret_insn_i ), + + .int_enable ( RISCV_CORE.id_stage_i.m_irq_enable_i ), + + .lsu_ready_wb ( RISCV_CORE.lsu_ready_wb ), + .apu_ready_wb ( RISCV_CORE.apu_ready_wb ), + .wb_contention ( RISCV_CORE.ex_stage_i.wb_contention ), + + .apu_en_id ( RISCV_CORE.id_stage_i.apu_en ), + .apu_req ( RISCV_CORE.ex_stage_i.apu_req ), + .apu_gnt ( RISCV_CORE.ex_stage_i.apu_gnt ), + .apu_valid ( RISCV_CORE.ex_stage_i.apu_valid ), + .apu_singlecycle ( RISCV_CORE.ex_stage_i.apu_singlecycle ), + .apu_multicycle ( RISCV_CORE.ex_stage_i.apu_multicycle ), + .apu_latency ( RISCV_CORE.ex_stage_i.apu_lat_i ), + .apu_active ( RISCV_CORE.ex_stage_i.apu_active ), + .apu_en_ex ( RISCV_CORE.ex_stage_i.apu_en_i ), + + .ex_valid ( RISCV_CORE.ex_valid ), + .ex_reg_addr ( RISCV_CORE.id_stage_i.register_file_i.waddr_b_i ), + + .ex_reg_we ( RISCV_CORE.id_stage_i.register_file_i.we_b_i ), + .ex_reg_wdata ( RISCV_CORE.id_stage_i.register_file_i.wdata_b_i ), + + .ex_data_req ( RISCV_CORE.data_req_o ), + .ex_data_gnt ( RISCV_CORE.data_gnt_i ), + .ex_data_we ( RISCV_CORE.data_we_o ), + .ex_data_addr ( RISCV_CORE.data_addr_o ), + .ex_data_wdata ( RISCV_CORE.data_wdata_o ), + + .lsu_misaligned ( RISCV_CORE.data_misaligned ), + .wb_bypass ( RISCV_CORE.ex_stage_i.branch_in_ex_i ), + + .wb_valid ( RISCV_CORE.wb_valid ), + .wb_reg_addr ( RISCV_CORE.id_stage_i.register_file_i.waddr_a_i ), + .wb_reg_we ( RISCV_CORE.id_stage_i.register_file_i.we_a_i ), + .wb_reg_wdata ( RISCV_CORE.id_stage_i.register_file_i.wdata_a_i ), + .wb_data_rvalid ( RISCV_CORE.data_rvalid_i ), + .wb_data_rdata ( RISCV_CORE.data_rdata_i ) + ); + end + endgenerate + `endif + +endmodule // tb_riscv_core diff --git a/tb/core/tb_top.sv b/tb/core/tb_top.sv new file mode 100644 index 0000000..752e7bc --- /dev/null +++ b/tb/core/tb_top.sv @@ -0,0 +1,165 @@ +// Copyright 2017 Embecosm Limited +// Copyright 2018 Robert Balas +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Top level wrapper for a RI5CY testbench +// Contributor: Robert Balas +// Jeremy Bennett + +`define TB_CORE + +module tb_top + #(parameter INSTR_RDATA_WIDTH = 32, + parameter RAM_ADDR_WIDTH = 22, + parameter BOOT_ADDR = 'h80); + + // comment to record execution trace + //`define TRACE_EXECUTION + + const time CLK_PHASE_HI = 5ns; + const time CLK_PHASE_LO = 5ns; + const time CLK_PERIOD = CLK_PHASE_HI + CLK_PHASE_LO; + const time STIM_APPLICATION_DEL = CLK_PERIOD * 0.1; + const time RESP_ACQUISITION_DEL = CLK_PERIOD * 0.9; + const time RESET_DEL = STIM_APPLICATION_DEL; + const int RESET_WAIT_CYCLES = 4; + + + // clock and reset for tb + logic core_clk; + logic iss_clk; + logic core_rst_n; + + // cycle counter + int unsigned cycle_cnt_q; + + // testbench result + logic tests_passed; + logic tests_failed; + logic exit_valid; + logic [31:0] exit_value; + + // signals for ri5cy + logic fetch_enable; + + // make the core start fetching instruction immediately + assign fetch_enable = '1; + + // allow vcd dump + initial begin + if ($test$plusargs("vcd")) begin + $dumpfile("riscy_tb.vcd"); + $dumpvars(0, tb_top); + end + end + + // we either load the provided firmware or execute a small test program that + // doesn't do more than an infinite loop with some I/O + initial begin: load_prog + automatic string firmware; + automatic int prog_size = 6; + + if($value$plusargs("firmware=%s", firmware)) begin + if($test$plusargs("verbose")) + $display("[TESTBENCH] @ t=%0t: loading firmware %0s", + $time, firmware); + $readmemh(firmware, cv32e40p_tb_wrapper_i.ram_i.dp_ram_i.mem); + end else begin + $display("No firmware specified"); + $finish; + end + end + + initial begin: clock_gen + forever begin + #CLK_PHASE_HI core_clk = 1'b0; + #CLK_PHASE_LO core_clk = 1'b1; + end + end: clock_gen + + + // timing format, reset generation and parameter check + initial begin + $timeformat(-9, 0, "ns", 9); + core_rst_n = 1'b0; + + // wait a few cycles + repeat (RESET_WAIT_CYCLES) begin + @(posedge core_clk); //TODO: was posedge, see below + end + + // start running + #RESET_DEL core_rst_n = 1'b1; + + repeat (3) @(negedge core_clk); + core_rst_n = 1'b1; + + if($test$plusargs("verbose")) begin + $display("reset deasserted", $time); + end + + if ( !( (INSTR_RDATA_WIDTH == 128) || (INSTR_RDATA_WIDTH == 32) ) ) begin + $fatal(2, "invalid INSTR_RDATA_WIDTH, choose 32 or 128"); + end + end + + // abort after n cycles, if we want to + always_ff @(posedge core_clk, negedge core_rst_n) begin + automatic int maxcycles; + if($value$plusargs("maxcycles=%d", maxcycles)) begin + if (~core_rst_n) begin + cycle_cnt_q <= 0; + end else begin + cycle_cnt_q <= cycle_cnt_q + 1; + if (cycle_cnt_q >= maxcycles) begin + $fatal(2, "Simulation aborted due to maximum cycle limit"); + end + end + end + end + + // check if we succeded + always_ff @(posedge core_clk, negedge core_rst_n) begin + if (tests_passed) begin + $display("ALL TESTS PASSED"); + $finish; + end + if (tests_failed) begin + $display("TEST(S) FAILED!"); + $finish; + end + if (exit_valid) begin + if (exit_value == 0) + $display("%m @ %0t: EXIT SUCCESS", $time); + else + $display("%m @ %0t: EXIT FAILURE: %d", exit_value, $time); + $finish; + end + end + + // wrapper for CV32E40P, the memory system and stdout peripheral + cv32e40p_tb_wrapper + #( + .INSTR_RDATA_WIDTH (INSTR_RDATA_WIDTH), + .RAM_ADDR_WIDTH (RAM_ADDR_WIDTH), + .BOOT_ADDR (BOOT_ADDR) + ) + cv32e40p_tb_wrapper_i + ( + .clk_i ( core_clk ), + .rst_ni ( core_rst_n ), + .fetch_enable_i ( fetch_enable ), + .tests_passed_o ( tests_passed ), + .tests_failed_o ( tests_failed ), + .exit_valid_o ( exit_valid ), + .exit_value_o ( exit_value ) + ); + +endmodule // tb_top diff --git a/tb/core/tb_top_verilator.cpp b/tb/core/tb_top_verilator.cpp new file mode 100644 index 0000000..776a13b --- /dev/null +++ b/tb/core/tb_top_verilator.cpp @@ -0,0 +1,145 @@ + + +#include "svdpi.h" +/** it should have a dedicade header file */ + + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +// Concatenate macros +#define CONCATENATE(a, b) a##b +#define CONCATENATE2(a, b) CONCATENATE(a, b) + +#ifndef TOPLEVEL_NAME +#error "TOPLEVEL_NAME must be set to the name of the toplevel." +#else +#pragma message ("TOPLEVEL_NAME is set to: " TOSTRING(TOPLEVEL_NAME)) +#endif + +// Construct the include _Dpi.h file name +#define TOP_LEVEL_DPI_HEADER_NAME CONCATENATE2(V, TOPLEVEL_NAME)__Dpi.h + +// Construct the include top module file name +#define TOP_LEVEL_HEADER_NAME CONCATENATE2(V, TOPLEVEL_NAME).h + +#define TOP_LEVEL_DUT CONCATENATE2(V, TOPLEVEL_NAME) + +#include TOSTRING(TOP_LEVEL_DPI_HEADER_NAME) +#include TOSTRING(TOP_LEVEL_HEADER_NAME) + + +/**header file ends here */ + +#include "verilated_vcd_c.h" +#include "verilated.h" + +#include +#include +#include +#include +#include +#include +#include + +void dump_memory(); +double sc_time_stamp(); + +static vluint64_t t = 0; +TOP_LEVEL_DUT *top; + +int main(int argc, char **argv, char **env) +{ + +#ifdef MCY + int mutidx = 0; + for (int i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "--mutidx") && i+1 < argc) + { + i++; + std::string s(argv[i]); + mutidx = std::stoi(s); + } + } +#endif + + Verilated::commandArgs(argc, argv); + Verilated::traceEverOn(true); + top = new TOP_LEVEL_DUT(); + + svSetScope(svGetScopeFromName( + "TOP.tb_top_verilator.cv32e40p_tb_wrapper_i.ram_i.dp_ram_i")); + Verilated::scopesDump(); + +#ifdef VCD_TRACE + VerilatedVcdC *tfp = new VerilatedVcdC; + top->trace(tfp, 99); + tfp->open("verilator_tb.vcd"); +#endif + top->fetch_enable_i = 1; + top->clk_i = 0; + top->rst_ni = 0; + + top->eval(); +#ifdef DUMP_MEMORY + dump_memory(); +#endif + +#ifdef MCY + svSetScope(svGetScopeFromName( + "TOP.tb_top_verilator.cv32e40p_tb_wrapper_i.riscv_core_i.ex_stage_i.alu_i.int_div.div_i")); + svLogicVecVal idx = {0}; + idx.aval = mutidx; + set_mutidx(&idx); + std::cout << "[tb_top_verilator] mutsel = " << idx.aval << "\n"; +#endif + + while (!Verilated::gotFinish()) { + if (t > 40) + top->rst_ni = 1; + top->clk_i = !top->clk_i; + top->eval(); +#ifdef VCD_TRACE + tfp->dump(t); +#endif + t += 5; + } +#ifdef VCD_TRACE + tfp->close(); +#endif + delete top; + exit(0); +} + +double sc_time_stamp() +{ + return t; +} + +#ifdef DUMP_MEMORY +void dump_memory() +{ + errno = 0; + std::ofstream mem_file; + svLogicVecVal addr = {0}; + + mem_file.exceptions(std::ofstream::failbit | std::ofstream::badbit); + try { + mem_file.open("memory_dump.bin"); + for (size_t i = 0; i < 1048576; i++) { + addr.aval = i; + uint32_t val = read_byte(&addr); + //uint32_t val = read_byte(&addr.aval); // mike@openhwgroup.org: if the above line fails to compile on your system, try this line + mem_file << std::setfill('0') << std::setw(2) << std::hex << val + << std::endl; + } + mem_file.close(); + + std::cout << "[tb_top_verilator] finished dumping memory" << std::endl; + + } catch (std::ofstream::failure e) { + std::cerr << "[tb_top_verilator] exception opening/reading/closing file memory_dump.bin\n"; + } +} +#endif \ No newline at end of file diff --git a/tb/core/tb_top_verilator.sv b/tb/core/tb_top_verilator.sv new file mode 100644 index 0000000..195ec6a --- /dev/null +++ b/tb/core/tb_top_verilator.sv @@ -0,0 +1,106 @@ +// Copyright 2018 Robert Balas +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Top level wrapper for a verilator RI5CY testbench +// Contributor: Robert Balas + +module tb_top_verilator + #(parameter INSTR_RDATA_WIDTH = 128, + parameter RAM_ADDR_WIDTH = 22, + parameter BOOT_ADDR = 'h80) + (input logic clk_i, + input logic rst_ni, + input logic fetch_enable_i, + output logic tests_passed_o, + output logic tests_failed_o); + + // cycle counter + int unsigned cycle_cnt_q; + + // testbench result + logic exit_valid; + logic [31:0] exit_value; + + + // we either load the provided firmware or execute a small test program that + // doesn't do more than an infinite loop with some I/O + initial begin: load_prog + automatic logic [1023:0] firmware; + automatic int prog_size = 6; + + if($value$plusargs("firmware=%s", firmware)) begin + if($test$plusargs("verbose")) + $display("[TESTBENCH] %t: loading firmware %0s ...", + $time, firmware); + $readmemh(firmware, cv32e40p_tb_wrapper_i.ram_i.dp_ram_i.mem); + + end else begin + $display("No firmware specified"); + $finish; + end + end + + // abort after n cycles, if we want to + always_ff @(posedge clk_i, negedge rst_ni) begin + automatic int maxcycles; + if($value$plusargs("maxcycles=%d", maxcycles)) begin + if (~rst_ni) begin + cycle_cnt_q <= 0; + end else begin + cycle_cnt_q <= cycle_cnt_q + 1; + if (cycle_cnt_q >= maxcycles) begin + // we $finish instead of $fatal because riscv-compliance + // interprets the return error code as total failure, which + // we don't want + $finish("Simulation aborted due to maximum cycle limit"); + end + end + end + end + + // check if we succeded + always_ff @(posedge clk_i, negedge rst_ni) begin + if (tests_passed_o) begin + $display("%m @ %0t: ALL TESTS PASSED", $time); + $finish; + end + if (tests_failed_o) begin + $display("%m @ %0t: TEST(S) FAILED!", $time); + $finish; + end + if (exit_valid) begin + if (exit_value == 0) + $display("%m @ %0t: EXIT SUCCESS", $time); + else + $display("%m @ %0t: EXIT FAILURE: %d", exit_value, $time); + $finish; + end + end + + // wrapper for cv32e40p, the memory system and stdout peripheral + cv32e40p_tb_wrapper + #(.INSTR_RDATA_WIDTH (INSTR_RDATA_WIDTH), + .RAM_ADDR_WIDTH (RAM_ADDR_WIDTH), + .BOOT_ADDR (BOOT_ADDR), + .PULP_CLUSTER (0), + .FPU (0), + .PULP_ZFINX (0), + .DM_HALTADDRESS (32'h1A110800) + ) + cv32e40p_tb_wrapper_i + (.clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .fetch_enable_i ( fetch_enable_i ), + .tests_passed_o ( tests_passed_o ), + .tests_failed_o ( tests_failed_o ), + .exit_valid_o ( exit_valid ), + .exit_value_o ( exit_value )); + +endmodule // tb_top_verilator diff --git a/tb/redmule_tb.sv b/tb/redmule_tb.sv index 6e078cc..9475e80 100644 --- a/tb/redmule_tb.sv +++ b/tb/redmule_tb.sv @@ -5,111 +5,114 @@ // Yvan Tortorella // -timeunit 1ps; -timeprecision 1ps; +timeunit 1ps; timeprecision 1ps; -module redmule_tb; -import redmule_pkg::*; +module redmule_tb ( + input logic clk_i, + input logic rst_ni, + input logic fetch_enable_i + +); + import redmule_pkg::*; // parameters parameter int unsigned PROB_STALL = 0; parameter int unsigned NC = 1; parameter int unsigned ID = 10; parameter int unsigned DW = redmule_pkg::DATA_W; - parameter int unsigned MP = DW/32; - parameter int unsigned MEMORY_SIZE = 192*1024; - parameter int unsigned STACK_MEMORY_SIZE = 192*1024; + parameter int unsigned MP = DW / 32; + parameter int unsigned MEMORY_SIZE = 192 * 1024; + parameter int unsigned STACK_MEMORY_SIZE = 192 * 1024; parameter int unsigned PULP_XPULP = 1; parameter int unsigned FPU = 0; parameter int unsigned PULP_ZFINX = 0; parameter logic [31:0] BASE_ADDR = 32'h1c000000; parameter logic [31:0] HWPE_ADDR_BASE_BIT = 20; parameter string STIM_INSTR = "./stim_instr.txt"; - parameter string STIM_DATA = "./stim_data.txt"; + parameter string STIM_DATA = "./stim_data.txt"; // global signals - logic clk; - logic rst_n; + //logic clk_i; + //logic rst_ni; logic test_mode; - logic fetch_enable; + //logic fetch_enable_i; logic [31:0] core_boot_addr; logic redmule_busy; - hwpe_stream_intf_tcdm instr[0:0] (.clk(clk)); - hwpe_stream_intf_tcdm stack[0:0] (.clk(clk)); - hwpe_stream_intf_tcdm tcdm [MP:0] (.clk(clk)); + hwpe_stream_intf_tcdm instr[0:0] (.clk(clk_i)); + hwpe_stream_intf_tcdm stack[0:0] (.clk(clk_i)); + hwpe_stream_intf_tcdm tcdm[MP:0] (.clk(clk_i)); - logic [NC-1:0][1:0] evt; + logic [NC-1:0][ 1:0] evt; logic [MP-1:0] tcdm_req; logic [MP-1:0] tcdm_gnt; logic [MP-1:0][31:0] tcdm_add; logic [MP-1:0] tcdm_wen; - logic [MP-1:0][3:0] tcdm_be; + logic [MP-1:0][ 3:0] tcdm_be; logic [MP-1:0][31:0] tcdm_data; logic [MP-1:0][31:0] tcdm_r_data; logic [MP-1:0] tcdm_r_valid; logic tcdm_r_opc; logic tcdm_r_user; - - logic periph_req; - logic periph_gnt; - logic [31:0] periph_add; - logic periph_wen; - logic [3:0] periph_be; - logic [31:0] periph_data; - logic [ID-1:0] periph_id; - logic [31:0] periph_r_data; - logic periph_r_valid; - logic [ID-1:0] periph_r_id; - - logic instr_req; - logic instr_gnt; - logic instr_rvalid; - logic [31:0] instr_addr; - logic [31:0] instr_rdata; - - logic data_req; - logic data_gnt; - logic data_rvalid; - logic data_we; - logic [3:0] data_be; - logic [31:0] data_addr; - logic [31:0] data_wdata; - logic [31:0] data_rdata; - logic data_err; - logic core_sleep; + + logic periph_req; + logic periph_gnt; + logic [ 31:0] periph_add; + logic periph_wen; + logic [ 3:0] periph_be; + logic [ 31:0] periph_data; + logic [ID-1:0] periph_id; + logic [ 31:0] periph_r_data; + logic periph_r_valid; + logic [ID-1:0] periph_r_id; + + logic instr_req; + logic instr_gnt; + logic instr_rvalid; + logic [ 31:0] instr_addr; + logic [ 31:0] instr_rdata; + + logic data_req; + logic data_gnt; + logic data_rvalid; + logic data_we; + logic [ 3:0] data_be; + logic [ 31:0] data_addr; + logic [ 31:0] data_wdata; + logic [ 31:0] data_rdata; + logic data_err; + logic core_sleep; // ATI timing parameters. - localparam TCP = 1.0ns; // clock period, 1 GHz clock - localparam TA = 0.2ns; // application time - localparam TT = 0.8ns; // test time - - // Performs one entire clock cycle. - task cycle; - clk <= #(TCP/2) 0; - clk <= #TCP 1; - #TCP; - endtask - - // The following task schedules the clock edges for the next cycle and - // advances the simulation time to that cycles test time (localparam TT) - // according to ATI timings. - task cycle_start; - clk <= #(TCP/2) 0; - clk <= #TCP 1; - #TT; - endtask - - // The following task finishes a clock cycle previously started with - // cycle_start by advancing the simulation time to the end of the cycle. - task cycle_end; - #(TCP-TT); - endtask + localparam TCP = 1.0ns; // clock period, 1 GHz clock + localparam TA = 0.2ns; // application time + localparam TT = 0.8ns; // test time + + // // Performs one entire clock cycle. + // task cycle; + // clk_i <= #(TCP / 2) 0; + // clk_i <= #TCP 1; + // #TCP; + // endtask + + // // The following task schedules the clock edges for the next cycle and + // // advances the simulation time to that cycles test time (localparam TT) + // // according to ATI timings. + // task cycle_start; + // clk_i <= #(TCP / 2) 0; + // clk_i <= #TCP 1; + // #TT; + // endtask + + // // The following task finishes a clock cycle previously started with + // // cycle_start by advancing the simulation time to the end of the cycle. + // task cycle_end; + // #(TCP - TT); + // endtask // bindings - always_comb - begin : bind_periph + always_comb begin : bind_periph periph_req = data_req & data_addr[HWPE_ADDR_BASE_BIT]; periph_add = data_addr; periph_wen = ~data_we; @@ -118,8 +121,7 @@ import redmule_pkg::*; periph_id = '0; end - always_comb - begin : bind_instrs + always_comb begin : bind_instrs instr[0].req = instr_req; instr[0].add = instr_addr; instr[0].wen = 1'b1; @@ -130,8 +132,7 @@ import redmule_pkg::*; instr_rvalid = instr[0].r_valid; end - always_comb - begin : bind_stack + always_comb begin : bind_stack stack[0].req = data_req & (data_addr[31:24] == '0) & ~data_addr[HWPE_ADDR_BASE_BIT]; stack[0].add = data_addr; stack[0].wen = ~data_we; @@ -140,30 +141,28 @@ import redmule_pkg::*; end logic other_r_valid; - always_ff @(posedge clk or negedge rst_n) begin - if (~rst_n) - other_r_valid <= '0; - else - other_r_valid <= data_req & (data_addr[31:24] == 8'h80); + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) other_r_valid <= '0; + else other_r_valid <= data_req & (data_addr[31:24] == 8'h80); end - for(genvar ii=0; ii Date: Thu, 26 Sep 2024 10:20:40 +0300 Subject: [PATCH 2/8] built-in time-out for simulation, see tb/core/tb_top_verilator.cpp --- .gitignore | 1 + Makefile.verilator | 35 +++++++++++++++++++++-------------- tb/core/tb_top_verilator.cpp | 4 ++-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 92e1d41..2295e74 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ golden-model/venv/ sw/inc/ verilator_tb.vcd vsim/ +log/ diff --git a/Makefile.verilator b/Makefile.verilator index 23d409e..41471cc 100644 --- a/Makefile.verilator +++ b/Makefile.verilator @@ -1,15 +1,18 @@ ############# # Verilator # ############# -VERI_LOG_DIR ?= $(mkfile_path)/vsim -SIM_TEST_RESULTS ?= $(VERI_LOG_DIR) -TEST ?= redmule -BIN_DIR = $(mkfile_path)/bin -VERI_FLAGS += VLT_TOP_MODULE ?= redmule_tb #VLT_TOP_MODULE = tb_top_verilator +VERI_LOG_DIR ?= $(mkfile_path)/log/$(VLT_TOP_MODULE) +SIM_TEST_INPUTS ?= $(mkfile_path)/vsim +TEST ?= redmule +BIN_DIR = $(mkfile_path)/bin/$(VLT_TOP_MODULE) +VERI_FLAGS += + + + .PHONY: veri-clean # Clean all build directories and temporary files for Questasim simulation @@ -27,30 +30,34 @@ $(BIN_DIR)/verilator_executable: manifest.flist mkdir -p $(dir $@) make -C sim/core -f Makefile.verilator CV_CORE_MANIFEST=${CURDIR}/manifest.flist SIM_RESULTS=$(BIN_DIR) VLT_TOP_MODULE=$(VLT_TOP_MODULE) verilate +sanity-veri-run: VLT_TOP_MODULE := tb_top_verilator sanity-veri-run: + mkdir -p $(VERI_LOG_DIR) + rm -f $(VERI_LOG_DIR)/verilator_tb.vcd make -C sim/core -f Makefile.verilator CV_CORE_MANIFEST=${CURDIR}/manifest.flist SIM_RESULTS=$(BIN_DIR) VLT_TOP_MODULE=$(VLT_TOP_MODULE) TEST=hello-world run-test - + mv sim/core/verilator_tb.vcd $(VERI_LOG_DIR)/ .PHONY: run-test run-test: $(BIN_DIR)/verilator_executable @echo "$(BANNER)" - @echo "* Running with Verilator: logfile in $(SIM_TEST_RESULTS)/$(TEST).log" + @echo "* Running with Verilator: logfile in $(VERI_LOG_DIR)/$(TEST).log" @echo "$(BANNER)" mkdir -p $(VERI_LOG_DIR) + rm -f $(VERI_LOG_DIR)/verilator_tb.vcd $(BIN_DIR)/verilator_executable \ $(VERI_FLAGS) \ - "+firmware=$(SIM_TEST_RESULTS)/stim_instr.txt" \ - "+simdata=$(SIM_TEST_RESULTS)/stim_data.txt" \ + "+firmware=$(SIM_TEST_INPUTS)/stim_instr.txt" \ + "+simdata=$(SIM_TEST_INPUTS)/stim_data.txt" \ | tee $(VERI_LOG_DIR)/$(TEST).log - + mv verilator_tb.vcd $(VERI_LOG_DIR)/ .PHONY: help help: @echo "verilator related available targets:" - @echo verilate -- build verilator simulation, available here: $(BIN_DIR)/verilator_executable - @echo veri-clean -- get a clean slate for simulation - @echo sanity-veri-run -- smoke test for $(BIN_DIR)/verilator_executable - + @echo verilate -- build verilator simulation, available here: $(BIN_DIR)/verilator_executable + @echo veri-clean -- get a clean slate for simulation + @echo verilate VLT_TOP_MODULE=tb_top_verilator + @echo sanity-veri-run -- smoke test for $(BIN_DIR)/verilator_executable, only with VLT_TOP_MODULE=tb_top_verilator diff --git a/tb/core/tb_top_verilator.cpp b/tb/core/tb_top_verilator.cpp index 776a13b..2e62c4b 100644 --- a/tb/core/tb_top_verilator.cpp +++ b/tb/core/tb_top_verilator.cpp @@ -50,7 +50,7 @@ TOP_LEVEL_DUT *top; int main(int argc, char **argv, char **env) { - +uint32_t timeOut{207374}; #ifdef MCY int mutidx = 0; for (int i = 1; i < argc; i++) @@ -95,7 +95,7 @@ int main(int argc, char **argv, char **env) std::cout << "[tb_top_verilator] mutsel = " << idx.aval << "\n"; #endif - while (!Verilated::gotFinish()) { + while (!Verilated::gotFinish() && t < timeOut) { if (t > 40) top->rst_ni = 1; top->clk_i = !top->clk_i; From b89277e8bf4f4ee519309b1858989cbabc6bd823 Mon Sep 17 00:00:00 2001 From: darotsr Date: Thu, 26 Sep 2024 20:37:26 +0300 Subject: [PATCH 3/8] verilator & toolchain can be installed locally --- .gitignore | 2 + Makefile.tools | 77 +++++++++++++++++++++++++++++++++++++ Makefile.verilator | 9 +++-- README.md | 9 ++++- eth.sh | 4 +- setup.sh | 2 + vendor/riscv32-elf-gcc.url | 1 + vendor/riscv32-elf-llvm.url | 1 + 8 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 Makefile.tools create mode 100644 setup.sh create mode 100644 vendor/riscv32-elf-gcc.url create mode 100644 vendor/riscv32-elf-llvm.url diff --git a/.gitignore b/.gitignore index 2295e74..4dbe20d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ sw/inc/ verilator_tb.vcd vsim/ log/ +*.tar.gz +verilator/ \ No newline at end of file diff --git a/Makefile.tools b/Makefile.tools new file mode 100644 index 0000000..1c947b5 --- /dev/null +++ b/Makefile.tools @@ -0,0 +1,77 @@ + +prj_path := $(shell git rev-parse --show-toplevel) +num_cores := $(shell nproc) +num_cores_half := $(shell echo "$$(($(num_cores) / 2))") +CXX := g++-10 + + +INSTALL_PREFIX ?= install +INSTALL_DIR ?= $(prj_path)/${INSTALL_PREFIX} +GCC_INSTALL_DIR ?= ${INSTALL_DIR}/riscv-gcc +LLVM_INSTALL_DIR ?= ${INSTALL_DIR}/riscv-llvm +ISA_SIM_INSTALL_DIR ?= ${INSTALL_DIR}/riscv-isa-sim +ISA_SIM_MOD_INSTALL_DIR ?= ${INSTALL_DIR}/riscv-isa-sim-mod +VERIL_SRC_ROOT ?= $(prj_path)/vendor +VERIL_INSTALL_DIR ?= ${INSTALL_DIR}/verilator +VERIL_VERSION ?= v5.028 + +all: verilator riscv32-gcc +# Verilator +verilator: ${VERIL_INSTALL_DIR}/bin/verilator + +${VERIL_INSTALL_DIR}/bin/verilator: + rm -fr $(VERIL_SRC_ROOT)/verilator + cd $(VERIL_SRC_ROOT) && git clone https://github.com/verilator/verilator.git + # Checkout the right version + cd $(VERIL_SRC_ROOT)/verilator && git reset --hard && git fetch && git checkout ${VERIL_VERSION} + # Compile verilator + cd $(VERIL_SRC_ROOT)/verilator && git clean -xfdf && autoconf && \ + ./configure --prefix=$(VERIL_INSTALL_DIR) CXX=g++-10 && make -j$(num_cores_half) && make install + touch ${VERIL_INSTALL_DIR}/bin/verilator + + + +.PHONY: cores + +cores: + @num_cores=$$(nproc); \ + num_cores=$$((num_cores / 2)); \ + echo "Number of cores available on this machine (divided by 2): $$num_cores" + +riscv32-gcc: $(GCC_INSTALL_DIR) + +$(GCC_INSTALL_DIR): vendor/riscv32-elf-gcc.url + rm -fr $(GCC_INSTALL_DIR) + mkdir -p $(INSTALL_DIR) + cd vendor && \ + wget `cat $(CURDIR)/$<` -O riscv.tar.gz && \ + tar -xzvf riscv.tar.gz -C $(INSTALL_DIR)/ riscv + mv $(INSTALL_DIR)/riscv $(GCC_INSTALL_DIR) + touch $(GCC_INSTALL_DIR) + + +riscv32-llvm: $(LLVM_INSTALL_DIR) + +vendor/riscv32-elf-llvm.tar.gz: vendor/riscv32-elf-llvm.url + cd vendor && \ + wget `cat $(CURDIR)/$<` -O riscv32-elf-llvm.tar.gz && \ + touch riscv32-elf-llvm.tar.gz + +$(LLVM_INSTALL_DIR): vendor/riscv32-elf-llvm.tar.gz + rm -fr $(LLVM_INSTALL_DIR) + mkdir -p $(INSTALL_DIR) + cd vendor && \ + tar -xzvf riscv32-elf-llvm.tar.gz -C $(INSTALL_DIR)/ riscv + mv $(INSTALL_DIR)/riscv $(LLVM_INSTALL_DIR) + touch $(LLVM_INSTALL_DIR) + +riscv32-llvm-patch: + @cd $(prj_path)/util/isolde && \ + tar -xzvf tools.tar.gz && \ + mv $(prj_path)/util/isolde/riscv32-unknown-elf-objcopy $(LLVM_INSTALL_DIR)/bin && \ + mv $(prj_path)/util/isolde/riscv32-unknown-elf-objdump $(LLVM_INSTALL_DIR)/bin && \ + echo "REPLACED riscv32-unknown-elf- objcopy/objdump" + +dev-dep: + sudo apt-get install libelf-dev + sudo apt-get install srecord diff --git a/Makefile.verilator b/Makefile.verilator index 41471cc..bed8fc2 100644 --- a/Makefile.verilator +++ b/Makefile.verilator @@ -40,7 +40,9 @@ sanity-veri-run: .PHONY: run-test run-test: $(BIN_DIR)/verilator_executable @echo "$(BANNER)" - @echo "* Running with Verilator: logfile in $(VERI_LOG_DIR)/$(TEST).log" + @echo "* Running with Verilator: " + @echo "* logfile in $(VERI_LOG_DIR)/$(TEST).log" + @echo "* *.vcd in $(VERI_LOG_DIR)" @echo "$(BANNER)" mkdir -p $(VERI_LOG_DIR) rm -f $(VERI_LOG_DIR)/verilator_tb.vcd @@ -54,8 +56,9 @@ run-test: $(BIN_DIR)/verilator_executable help: @echo "verilator related available targets:" - @echo verilate -- build verilator simulation, available here: $(BIN_DIR)/verilator_executable - @echo veri-clean -- get a clean slate for simulation + @echo verilate -- builds verilator simulation, available here: $(BIN_DIR)/verilator_executable + @echo run-test -- runs the test + @echo veri-clean -- gets a clean slate for simulation @echo verilate VLT_TOP_MODULE=tb_top_verilator @echo sanity-veri-run -- smoke test for $(BIN_DIR)/verilator_executable, only with VLT_TOP_MODULE=tb_top_verilator diff --git a/README.md b/README.md index bd9571a..8510e3c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ # ISOLDE +First time, install toolchain + +```sh +make -f Makefile.tools +``` +otherwise: ```sh -. ./eth.sh +. ./eth.sh ``` + ## build simulation top module can be configured in cmd line,VLT_TOP_MODULE=, it defaults to **redmule_tb**, see [Makefile.verilator](Makefile.verilator) ```sh diff --git a/eth.sh b/eth.sh index 455ff3f..cced27d 100644 --- a/eth.sh +++ b/eth.sh @@ -3,7 +3,7 @@ # Define environment variables MINICONDA=~/miniconda3/etc/profile.d/conda.sh -MINICONDA_ENV=snitch +MINICONDA_ENV=ibex # To activate this environment, use # # $ conda activate snitch @@ -17,7 +17,7 @@ export ROOT_DIR=$(git rev-parse --show-toplevel) export BENDER=~/eth/bin/bender export PULP_RISCV_GCC_TOOLCHAIN=$ROOT_DIR/install/riscv -export GCC_TOOLCHAIN=$ROOT_DIR/install/riscv/bin +export GCC_TOOLCHAIN=$ROOT_DIR/install/riscv-gcc/bin export CC=gcc-10 export CXX=g++-10 diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..ff3f5b9 --- /dev/null +++ b/setup.sh @@ -0,0 +1,2 @@ +make -f Makefile.tools +. ./eth.sh \ No newline at end of file diff --git a/vendor/riscv32-elf-gcc.url b/vendor/riscv32-elf-gcc.url new file mode 100644 index 0000000..893c171 --- /dev/null +++ b/vendor/riscv32-elf-gcc.url @@ -0,0 +1 @@ +https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2024.08.28/riscv32-elf-ubuntu-20.04-gcc-nightly-2024.08.28-nightly.tar.gz \ No newline at end of file diff --git a/vendor/riscv32-elf-llvm.url b/vendor/riscv32-elf-llvm.url new file mode 100644 index 0000000..26ccfa4 --- /dev/null +++ b/vendor/riscv32-elf-llvm.url @@ -0,0 +1 @@ +https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2024.09.03/riscv32-elf-ubuntu-20.04-llvm-nightly-2024.09.03-nightly.tar.gz \ No newline at end of file From 8347c1affcc011e315c8b5cb7c3bb1e810cbc8db Mon Sep 17 00:00:00 2001 From: darotsr Date: Sat, 28 Sep 2024 21:05:05 +0300 Subject: [PATCH 4/8] wip: build script for cv32e40p/tests/programs/custom/hello-world --- .gitignore | 3 +- cv32e40p/bsp/.gitignore | 1 + cv32e40p/bsp/Makefile | 34 +++ cv32e40p/bsp/README.md | 166 +++++++++++++++ cv32e40p/bsp/crt0.S | 72 +++++++ cv32e40p/bsp/handlers.S | 223 ++++++++++++++++++++ cv32e40p/bsp/link.ld | 311 ++++++++++++++++++++++++++++ cv32e40p/bsp/link_corev-dv.ld | 1 + cv32e40p/bsp/syscalls.c | 376 ++++++++++++++++++++++++++++++++++ cv32e40p/bsp/vectors.S | 54 +++++ eth.sh | 8 +- mk/Common.mk | 180 ++++++++++++++++ sim/core/Makefile.verilator | 23 ++- 13 files changed, 1446 insertions(+), 6 deletions(-) create mode 100644 cv32e40p/bsp/.gitignore create mode 100644 cv32e40p/bsp/Makefile create mode 100644 cv32e40p/bsp/README.md create mode 100644 cv32e40p/bsp/crt0.S create mode 100644 cv32e40p/bsp/handlers.S create mode 100644 cv32e40p/bsp/link.ld create mode 120000 cv32e40p/bsp/link_corev-dv.ld create mode 100644 cv32e40p/bsp/syscalls.c create mode 100644 cv32e40p/bsp/vectors.S create mode 100644 mk/Common.mk diff --git a/.gitignore b/.gitignore index 4dbe20d..e149e51 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ verilator_tb.vcd vsim/ log/ *.tar.gz -verilator/ \ No newline at end of file +verilator/ +simulation_results/ diff --git a/cv32e40p/bsp/.gitignore b/cv32e40p/bsp/.gitignore new file mode 100644 index 0000000..c0a1f34 --- /dev/null +++ b/cv32e40p/bsp/.gitignore @@ -0,0 +1 @@ +libcv-verif.a diff --git a/cv32e40p/bsp/Makefile b/cv32e40p/bsp/Makefile new file mode 100644 index 0000000..ee05577 --- /dev/null +++ b/cv32e40p/bsp/Makefile @@ -0,0 +1,34 @@ +CV_SW_TOOLCHAIN ?= /opt/riscv +RISCV ?= $(CV_SW_TOOLCHAIN) +RISCV_EXE_PREFIX ?= $(RISCV)/bin/riscv32-unknown-elf- +RISCV_GCC = $(RISCV_EXE_PREFIX)gcc +RISCV_AR = $(RISCV_EXE_PREFIX)ar +SRC = crt0.S handlers.S syscalls.c vectors.S +OBJ = crt0.o handlers.o syscalls.o vectors.o +LIBCV-VERIF = libcv-verif.a +CFLAGS ?= -Os -g -static -mabi=ilp32 -march=$(CV_SW_MARCH) -Wall -pedantic + +all: $(LIBCV-VERIF) + +$(LIBCV-VERIF): $(OBJ) + $(RISCV_AR) rcs $@ $(OBJ) + +%.o : %.c + $(RISCV_GCC) $(CFLAGS) -c $< -o $@ + +%.o : %.S + $(RISCV_GCC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJ) $(LIBCV-VERIF) + + +vars: + @echo "make bsp variables:" + @echo " CV_SW_TOOLCHAIN = $(CV_SW_TOOLCHAIN)" + @echo " CV_SW_MARCH = $(CV_SW_MARCH)" + @echo " RISCV = $(RISCV)" + @echo " RISCV_EXE_PREFIX = $(RISCV_EXE_PREFIX)" + @echo " RISCV_GCC = $(RISCV_GCC)" + @echo " RISCV_MARCH = $(RISCV_MARCH)" + diff --git a/cv32e40p/bsp/README.md b/cv32e40p/bsp/README.md new file mode 100644 index 0000000..b0b7d2b --- /dev/null +++ b/cv32e40p/bsp/README.md @@ -0,0 +1,166 @@ +Board Support Package (BSP) for CV32E40P Verification +===================================================== + +This BSP provides the code to support running programs on the CV32E40P verification +target. It performs initialization tasks (`crt0.S`), handles +interrupts/exceptions (`vectors.S`, `handlers.S`), provides syscall +implementations (`syscalls.c`) and includes a linker script (`link.ld`) to +control the placement of sections in the binary. + +Each file is described in more detail below followed by instructions for +building and using the BSP. + +C Runtime Initialization +------------------------ + +The C Runtime file `crt0.S` provides the `_start` function which is the entry +point of the program and performs the following tasks: + * Initialize global and stack pointer. + * Store the address of `vector_table` in `mtvec`, setting the lower two bits + to `0x2` to select vectored interrupt mode. + * Zero the BSS section. + * Invoke initialization of C constructors and set destructors to be called on + exit. + * Zero `argc` and `argv` (the stack is not initialized, so these are zeroed + to prevent uninitialized values causing a mismatch against the reference + result). + * Call `main`. + * If `main` returns, call `exit` with its return code. + +Interrupt and Exception Handling +-------------------------------- + +When a RISC-V core traps on an interrupt/exception, the `pc` is stored in `mepc` +and the reason for the trap is stored in `mcause`. The `MSB` of `mcause` +is set to `0` for an exception and `1` for an interrupt; the remaining bits +`mcause[MXLEN-2:0]` contain the exception code. The table of `mcause` values is +defined in Table 3.6 of the [RISC-V Instruction Set Manual Volume II: Privileged +Architecture Version 20190608-Priv-MSU-Ratified](https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMFDQC-and-Priv-v1.11/riscv-privileged-20190608.pdf). + +The core jumps to a location in the vector table according to the `BASE` address +of the vector table stored in `mtvec` and the value of the exception code in +`mcause`. In vectored mode, all exceptions jump to `BASE` and interrupts jump to +`BASE+4*mcause[XLEN-2:0]`. Note that because user software interrupts have +exception code `0`, they jump to the same location as exceptions, therefore the +user software interrupt handler must also handle exceptions. + +The vector table is defined in `vectors.S` and may jump to one of the +following interrupt request handlers in `handlers.S`: + * `u_sw_irq_handler` - handles user software interrupts and all exceptions. + Saves all caller saved registers then checks `mcause` and jumps to the + appropriate handler as follows: + - Breakpoint: jump to `handle_ebreak`. + - Illegal instruction: jump to `handle_illegal`. + - Environment call from M-mode: jump to `handle_ecall`. + - Any other exception or user software interrupt: jump to `handle_unknown`. + * `m_software_irq_handler` - handles machine-mode software interrupts + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_timer_irq_handler` - handles machine-mode timer interrupts + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_external_irq_handler` - handles machine-mode external interrupts + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast0_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast1_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast2_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast3_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast4_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast5_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast6_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast7_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast8_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast9_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast10_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast11_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast12_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast13_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast14_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `m_fast15_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) + - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. + * `__no_irq_handler` - loops printing "no exception handler installed". + +The following exception handlers may be called from `u_sw_irq_handler`: + * `handle_ecall` - calls `handle_syscall` which checks the syscall number and + calls the corresponding syscall function. + * `handle_ebreak` - currently just prints "ebreak exception handler entered" + * `handle_illegal_insn` - prints "illegal instruction exception handler + entered" + * `unknown_handler` - called when there is no handler for the interrupt/ + exception. This is the only case where `mepc` is not incremented, because we + do not know the appropiate action to take. + +Returning from the `u_sw_irq_handler`. All handlers called by `u_sw_irq_handler` +increment `mepc` before calling `mret`, except for `unknown_handler`. Handlers +that require `mepc` to be incremented jump to `end_handler_incr_mepc` otherwise +they jump to `end_handler_ret`. All caller saved registers are restored before +finally calling `mret`. + +Some test cases require the ability to override the default handlers. In future, +these handlers will be made overridable by defining their labels as `.weak` +symbols. Test cases can then provide their own handlers where necessary. + +System Calls +------------ + +On a bare-metal system there is no OS to handle system calls, therefore, we +define our own system calls in `syscalls.c`. For example, the implementation of +`_write` outputs a byte at a time to the virtual printer peripheral. Many of the +functions provide minimal implementations that simply fail gracefully due to +lack of necessary OS support e.g. no file system. + +The [RISC-V Instruction Set Manual Volume I: Unprivileged ISA Version 20191213]( +https://content.riscv.org/wp-content/uploads/2019/06/riscv-spec.pdf) states that +for an `ecall` the "ABI for the system will define how parameters for the +environment request are passed". This BSP follows the convention used for RISC-V +in `newlib`. Parameters are passed in registers `a0` to `a5` and system call ID +in `a7` (`t0` on RV32E). When handling an `ecall`, `handle_ecall` calls +`handle_syscall` which then calls the appropriate function that implements the +system call, passing parameters as necessary. + +Linker Script +------------- + +The linker script defines the memory layout and controls the mapping of input +sections from object files to output sections in the output binary. + +The `link.ld` script is based on the standard upstream RV32 linker script, with +some changes required for CV32E40P: + * Memory layout is defined as follows: + * `ram` start=0x0, length=4MB + * `dbg` start=0x1A110800, length=2KB + * Changes to output section placement are as follows: + - `.vectors` start=ORIGIN(`ram`) + - `.init` start=0x80 + - `.heap` starts at end of data and grows upwards + - `.stack` starts at the end of `ram` and grows downwards + - `.debugger` start=ORIGIN(`dbg`) + - `.debugger_exception` start=0x1A110C00 + - `.debugger_stack` follows `.debugger_exception` + +Building and using the BSP Library +---------------------------------- + +The BSP can be built in this directory as follows: +``` +make +``` +This produces libcv-verif.a which can then be linked with a test program as +follows: + +``` +gcc test-program.c -nostartfiles -T/path/to/bsp/link.ld -L/path/to/bsp/ -lcv-verif +``` diff --git a/cv32e40p/bsp/crt0.S b/cv32e40p/bsp/crt0.S new file mode 100644 index 0000000..302da07 --- /dev/null +++ b/cv32e40p/bsp/crt0.S @@ -0,0 +1,72 @@ +/* Copyright (c) 2017 SiFive Inc. All rights reserved. + * Copyright (c) 2019 ETH Zürich and University of Bologna + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the FreeBSD License. This program is distributed in the hope that + * it will be useful, but WITHOUT ANY WARRANTY expressed or implied, + * including the implied warranties of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. A copy of this license is available at + * http://www.opensource.org/licenses. + */ +/* Make sure the vector table gets linked into the binary. */ +.global vector_table + +/* Entry point for bare metal programs */ +.section .text.start +.global _start +.type _start, @function + +_start: +/* initialize global pointer */ +.option push +.option norelax +1: auipc gp, %pcrel_hi(__global_pointer$) + addi gp, gp, %pcrel_lo(1b) +.option pop + +/* initialize stack pointer */ + la sp, __stack_end + +/* set vector table address */ + la a0, __vector_start + ori a0, a0, 1 /*vector mode = vectored */ + csrw mtvec, a0 + +/* clear the bss segment */ + la a0, _edata + la a2, _end + sub a2, a2, a0 + li a1, 0 + call memset + +/* new-style constructors and destructors */ + la a0, __libc_fini_array + call atexit + call __libc_init_array + +/* call main */ +// lw a0, 0(sp) /* a0 = argc */ +// addi a1, sp, __SIZEOF_POINTER__ /* a1 = argv */ +// li a2, 0 /* a2 = envp = NULL */ +// Initialize these variables to 0. Cannot use argc or argv +// since the stack is not initialized + li a0, 0 + li a1, 0 + li a2, 0 + + call main + tail exit + +.size _start, .-_start + +.global _init +.type _init, @function +.global _fini +.type _fini, @function +_init: +_fini: + /* These don't have to do anything since we use init_array/fini_array. Prevent + missing symbol error */ + ret +.size _init, .-_init +.size _fini, .-_fini diff --git a/cv32e40p/bsp/handlers.S b/cv32e40p/bsp/handlers.S new file mode 100644 index 0000000..6068105 --- /dev/null +++ b/cv32e40p/bsp/handlers.S @@ -0,0 +1,223 @@ +/* +* Copyright 2019 ETH Zürich and University of Bologna +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* Exception codes */ +#define EXCEPTION_ILLEGAL_INSN 2 +#define EXCEPTION_BREAKPOINT 3 +#define EXCEPTION_ECALL_M 11 + +.section .text.handlers +.global __no_irq_handler +.global u_sw_irq_handler +.global m_software_irq_handler +.global m_timer_irq_handler +.global m_external_irq_handler +.global m_fast0_irq_handler +.global m_fast1_irq_handler +.global m_fast2_irq_handler +.global m_fast3_irq_handler +.global m_fast4_irq_handler +.global m_fast5_irq_handler +.global m_fast6_irq_handler +.global m_fast7_irq_handler +.global m_fast8_irq_handler +.global m_fast9_irq_handler +.global m_fast10_irq_handler +.global m_fast11_irq_handler +.global m_fast12_irq_handler +.global m_fast13_irq_handler +.global m_fast14_irq_handler +.global m_fast15_irq_handler + +.weak u_sw_irq_handler +.weak m_software_irq_handler +.weak m_timer_irq_handler +.weak m_external_irq_handler +.weak m_fast0_irq_handler +.weak m_fast1_irq_handler +.weak m_fast2_irq_handler +.weak m_fast3_irq_handler +.weak m_fast4_irq_handler +.weak m_fast5_irq_handler +.weak m_fast6_irq_handler +.weak m_fast7_irq_handler +.weak m_fast8_irq_handler +.weak m_fast9_irq_handler +.weak m_fast10_irq_handler +.weak m_fast11_irq_handler +.weak m_fast12_irq_handler +.weak m_fast13_irq_handler +.weak m_fast14_irq_handler +.weak m_fast15_irq_handler + + +/* exception handling */ +__no_irq_handler: + la a0, no_exception_handler_msg + jal ra, puts + j __no_irq_handler + +m_software_irq_handler: + j __no_irq_handler + +m_timer_irq_handler: + j __no_irq_handler + +m_external_irq_handler: + j __no_irq_handler + +m_fast0_irq_handler: + j __no_irq_handler + +m_fast1_irq_handler: + j __no_irq_handler + +m_fast2_irq_handler: + j __no_irq_handler + +m_fast3_irq_handler: + j __no_irq_handler + +m_fast4_irq_handler: + j __no_irq_handler + +m_fast5_irq_handler: + j __no_irq_handler + +m_fast6_irq_handler: + j __no_irq_handler + +m_fast7_irq_handler: + j __no_irq_handler + +m_fast8_irq_handler: + j __no_irq_handler + +m_fast9_irq_handler: + j __no_irq_handler + +m_fast10_irq_handler: + j __no_irq_handler + +m_fast11_irq_handler: + j __no_irq_handler + +m_fast12_irq_handler: + j __no_irq_handler + +m_fast13_irq_handler: + j __no_irq_handler + +m_fast14_irq_handler: + j __no_irq_handler + +m_fast15_irq_handler: + j __no_irq_handler + +u_sw_irq_handler: + /* While we are still using puts in handlers, save all caller saved + regs. Eventually, some of these saves could be deferred. */ + addi sp,sp,-64 + sw ra, 0(sp) + sw a0, 4(sp) + sw a1, 8(sp) + sw a2, 12(sp) + sw a3, 16(sp) + sw a4, 20(sp) + sw a5, 24(sp) + sw a6, 28(sp) + sw a7, 32(sp) + sw t0, 36(sp) + sw t1, 40(sp) + sw t2, 44(sp) + sw t3, 48(sp) + sw t4, 52(sp) + sw t5, 56(sp) + sw t6, 60(sp) + csrr t0, mcause + li t1, EXCEPTION_ILLEGAL_INSN + beq t0, t1, handle_illegal_insn + li t1, EXCEPTION_ECALL_M + beq t0, t1, handle_ecall + li t1, EXCEPTION_BREAKPOINT + beq t0, t1, handle_ebreak + j handle_unknown + +handle_ecall: + jal ra, handle_syscall + j end_handler_incr_mepc + +handle_ebreak: + /* TODO support debug handling requirements. */ + la a0, ebreak_msg + jal ra, puts + j end_handler_incr_mepc + +handle_illegal_insn: + la a0, illegal_insn_msg + jal ra, puts + j end_handler_incr_mepc + +handle_unknown: + la a0, unknown_msg + jal ra, puts + /* We don't know what interrupt/exception is being handled, so don't + increment mepc. */ + j end_handler_ret + +end_handler_incr_mepc: + csrr t0, mepc + lb t1, 0(t0) + li a0, 0x3 + and t1, t1, a0 + /* Increment mepc by 2 or 4 depending on whether the instruction at mepc + is compressed or not. */ + bne t1, a0, end_handler_incr_mepc2 + addi t0, t0, 2 +end_handler_incr_mepc2: + addi t0, t0, 2 + csrw mepc, t0 +end_handler_ret: + lw ra, 0(sp) + lw a0, 4(sp) + lw a1, 8(sp) + lw a2, 12(sp) + lw a3, 16(sp) + lw a4, 20(sp) + lw a5, 24(sp) + lw a6, 28(sp) + lw a7, 32(sp) + lw t0, 36(sp) + lw t1, 40(sp) + lw t2, 44(sp) + lw t3, 48(sp) + lw t4, 52(sp) + lw t5, 56(sp) + lw t6, 60(sp) + addi sp,sp,64 + mret + +.section .rodata +illegal_insn_msg: + .string "CV32E40P BSP: illegal instruction exception handler entered\n" +ecall_msg: + .string "CV32E40P BSP: ecall exception handler entered\n" +ebreak_msg: + .string "CV32E40P BSP: ebreak exception handler entered\n" +unknown_msg: + .string "CV32E40P BSP: unknown exception handler entered\n" +no_exception_handler_msg: + .string "CV32E40P BSP: no exception handler installed\n" diff --git a/cv32e40p/bsp/link.ld b/cv32e40p/bsp/link.ld new file mode 100644 index 0000000..9ea197d --- /dev/null +++ b/cv32e40p/bsp/link.ld @@ -0,0 +1,311 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2020 Free Software Foundation, Inc. + Copyright (C) 2019 ETH Zürich and University of Bologna + Copyright (C) 2020 OpenHW Group + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ + +/* This linker script is adapted from the default linker script for upstream + RISC-V GCC. It has been modified for use in verification of CORE-V cores. +*/ + +OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", + "elf32-littleriscv") +OUTPUT_ARCH(riscv) +ENTRY(_start) + +/* CORE-V */ +MEMORY +{ + /* Our testbench is a bit weird in that we initialize the RAM (thus + allowing initialized sections to be placed there). Infact we dump all + sections to ram. */ + + ram (rwxai) : ORIGIN = 0x00000000, LENGTH = 0x400000 + dbg (rwxai) : ORIGIN = 0x1A110800, LENGTH = 0x1000 +} + +SECTIONS +{ + /* CORE-V Debugger Code: This section address must be the same as the + DM_HaltAddress parameter in the RTL */ + .debugger (ORIGIN(dbg)): + { + KEEP(*(.debugger)); + } >dbg + .debugger_exception (0x1A111000): + { + KEEP(*(.debugger_exception)); + } >dbg + /* Debugger Stack*/ + .debugger_stack : ALIGN(16) + { + PROVIDE(__debugger_stack_start = .); + . = 0x80; + } >dbg + + /* CORE-V: we want a fixed entry point */ + PROVIDE(__boot_address = 0x80); + + /* CORE-V: interrupt vectors */ + .vectors (ORIGIN(ram)): + { + PROVIDE(__vector_start = .); + KEEP(*(.vectors)); + } >ram + + /* CORE-V: crt0 init code */ + .init (__boot_address): + { + KEEP (*(SORT_NONE(.init))) + KEEP (*(.text.start)) + } >ram + + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x10000)); . = SEGMENT_START("text-segment", 0x10000) + SIZEOF_HEADERS; + .interp : { *(.interp) } >ram + .note.gnu.build-id : { *(.note.gnu.build-id) } >ram + .hash : { *(.hash) } >ram + .gnu.hash : { *(.gnu.hash) } >ram + .dynsym : { *(.dynsym) } >ram + .dynstr : { *(.dynstr) } >ram + .gnu.version : { *(.gnu.version) } >ram + .gnu.version_d : { *(.gnu.version_d) } >ram + .gnu.version_r : { *(.gnu.version_r) } >ram + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) + *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) + *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) + *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } >ram + .rela.plt : + { + *(.rela.plt) + } >ram + + .plt : { *(.plt) } + .iplt : { *(.iplt) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(SORT(.text.sorted.*)) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } >ram + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } >ram + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } >ram + .rodata1 : { *(.rodata1) } >ram + .sdata2 : + { + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + } >ram + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } >ram + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } >ram + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ram + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >ram + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >ram + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ram + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } >ram + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >ram + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } >ram + /* Thread Local Storage sections */ + .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } >ram + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } >ram + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >ram + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } >ram + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >ram + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >ram + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >ram + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + . = DATA_SEGMENT_RELRO_END (0, .); + .data : + { + __DATA_BEGIN__ = .; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >ram + .data1 : { *(.data1) } >ram + .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : + { + __SDATA_BEGIN__ = .; + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + } >ram + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .sbss : + { + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + } >ram + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 32 / 8 : 1); + } >ram + . = ALIGN(32 / 8); + . = SEGMENT_START("ldata-segment", .); + . = ALIGN(32 / 8); + __bss_end = .; + __global_pointer$ = MIN(__SDATA_BEGIN__ + 0x800, + MAX(__DATA_BEGIN__ + 0x800, __bss_end - 0x800)); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + + /* Heap grows upward towards end of ram */ + .heap : ALIGN(16) + { + PROVIDE(__heap_start = .); + /* If end of ram is not 16-byte aligned, align to previous 16-byte + boundary */ + PROVIDE(__heap_end = ALIGN(ORIGIN(ram) + LENGTH(ram) - __heap_start - 15, 16)); + . = __heap_end; + } >ram + + /* Stack grows downward from end of ram */ + .stack (__heap_start) : ALIGN(16) /* this is a requirement of the ABI(?) */ + { + PROVIDE(__stack_start = __heap_start); + . = __heap_end; + PROVIDE(__stack_end = .); + } >ram + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} + diff --git a/cv32e40p/bsp/link_corev-dv.ld b/cv32e40p/bsp/link_corev-dv.ld new file mode 120000 index 0000000..6d62cc1 --- /dev/null +++ b/cv32e40p/bsp/link_corev-dv.ld @@ -0,0 +1 @@ +link.ld \ No newline at end of file diff --git a/cv32e40p/bsp/syscalls.c b/cv32e40p/bsp/syscalls.c new file mode 100644 index 0000000..36ffac3 --- /dev/null +++ b/cv32e40p/bsp/syscalls.c @@ -0,0 +1,376 @@ +/* An extremely minimalist syscalls.c for newlib + * Based on riscv newlib libgloss/riscv/sys_*.c + * + * Copyright 2019 Claire Wolf + * Copyright 2019 ETH Zürich and University of Bologna + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef errno +extern int errno; + +/* write to this reg for outputting strings */ +#define STDOUT_REG 0x10000000 +/* write test result of program to this reg */ +#define RESULT_REG 0x20000000 +/* write exit value of program to this reg */ +#define EXIT_REG 0x20000004 + +#define STDOUT_FILENO 1 + +/* It turns out that older newlib versions use different symbol names which goes + * against newlib recommendations. Anyway this is fixed in later version. + */ +#if __NEWLIB__ <= 2 && __NEWLIB_MINOR__ <= 5 +#define _sbrk sbrk +#define _write write +#define _close close +#define _lseek lseek +#define _read read +#define _fstat fstat +#define _isatty isatty +#endif +/* Upstream newlib now defines this in libgloss/riscv/internal_syscall.h. */ +long +__syscall_error(long a0) +{ + errno = -a0; + return -1; +} + +void unimplemented_syscall() +{ + const char *p = "BSP: Unimplemented system call called!\n"; + while (*p) + *(volatile int *)STDOUT_REG = *(p++); +} + +int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) +{ + errno = ENOSYS; + return -1; +} + +int _access(const char *file, int mode) +{ + errno = ENOSYS; + return -1; +} + +int _chdir(const char *path) +{ + errno = ENOSYS; + return -1; +} + +int _chmod(const char *path, mode_t mode) +{ + errno = ENOSYS; + return -1; +} + +int _chown(const char *path, uid_t owner, gid_t group) +{ + errno = ENOSYS; + return -1; +} + +int _close(int file) +{ + return -1; +} + +int _execve(const char *name, char *const argv[], char *const env[]) +{ + errno = ENOMEM; + return -1; +} + +void _exit(int exit_status) +{ + *(volatile int *)EXIT_REG = exit_status; + asm volatile("wfi"); + /* _exit should not return */ + while (1) {}; +} + +int _faccessat(int dirfd, const char *file, int mode, int flags) +{ + errno = ENOSYS; + return -1; +} + +int _fork(void) +{ + errno = EAGAIN; + return -1; +} + +int _fstat(int file, struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; + // errno = -ENOSYS; + // return -1; +} + +int _fstatat(int dirfd, const char *file, struct stat *st, int flags) +{ + errno = ENOSYS; + return -1; +} + +int _ftime(struct timeb *tp) +{ + errno = ENOSYS; + return -1; +} + +char *_getcwd(char *buf, size_t size) +{ + errno = -ENOSYS; + return NULL; +} + +int _getpid() +{ + return 1; +} + +int _gettimeofday(struct timeval *tp, void *tzp) +{ + errno = -ENOSYS; + return -1; +} + +int _isatty(int file) +{ + return (file == STDOUT_FILENO); +} + +int _kill(int pid, int sig) +{ + errno = EINVAL; + return -1; +} + +int _link(const char *old_name, const char *new_name) +{ + errno = EMLINK; + return -1; +} + +off_t _lseek(int file, off_t ptr, int dir) +{ + return 0; +} + +int _lstat(const char *file, struct stat *st) +{ + errno = ENOSYS; + return -1; +} + +int _open(const char *name, int flags, int mode) +{ + return -1; +} + +int _openat(int dirfd, const char *name, int flags, int mode) +{ + errno = ENOSYS; + return -1; +} + +ssize_t _read(int file, void *ptr, size_t len) +{ + return 0; +} + +int _stat(const char *file, struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; + // errno = ENOSYS; + // return -1; +} + +long _sysconf(int name) +{ + + return -1; +} + +clock_t _times(struct tms *buf) +{ + return -1; +} + +int _unlink(const char *name) +{ + errno = ENOENT; + return -1; +} + +int _utime(const char *path, const struct utimbuf *times) +{ + errno = ENOSYS; + return -1; +} + +int _wait(int *status) +{ + errno = ECHILD; + return -1; +} + +ssize_t _write(int file, const void *ptr, size_t len) +{ + const char *cptr = (char *)ptr; + if (file != STDOUT_FILENO) + { + errno = ENOSYS; + return -1; + } + + const void *eptr = cptr + len; + while (cptr != eptr) + *(volatile int *)STDOUT_REG = *cptr++; + return len; +} + +extern char __heap_start[]; +extern char __heap_end[]; +static char *brk = __heap_start; + +int _brk(void *addr) +{ + brk = addr; + return 0; +} + +void *_sbrk(ptrdiff_t incr) +{ + char *old_brk = brk; + register long sp asm("sp"); + + char *new_brk = brk += incr; + if (new_brk < (char *) sp && new_brk < __heap_end) + { + brk = new_brk; + + return old_brk; + } + else + { + errno = ENOMEM; + return (void *) -1; + } +} + +void handle_syscall (long a0, + long a1, + long a2, + long a3, + __attribute__((unused)) long a4, + __attribute__((unused)) long a5, + __attribute__((unused)) long a6, + long a7) { + #ifdef __riscv_32e + register long syscall_id asm("t0"); + #else + long syscall_id = a7; + #endif + + switch (syscall_id) { + case SYS_exit: + _exit (a0); + break; + case SYS_read: + _read (a0, (void *) a1, a2); + break; + case SYS_write: + _write (a0, (const void *) a1, a2); + break; + case SYS_getpid: + _getpid (); + break; + case SYS_kill: + _kill (a0, a1); + break; + case SYS_open: + _open ((const char *) a0, a1, a2); + break; + case SYS_openat: + _openat (a0, (const char *) a1, a2, a3); + break; + case SYS_close: + _close (a0); + break; + case SYS_lseek: + _lseek (a0, a1, a2); + break; + case SYS_brk: + _brk ((void *) a0); + break; + case SYS_link: + _link ((const char *) a0, (const char *) a1); + break; + case SYS_unlink: + _unlink ((const char *) a0); + break; + case SYS_chdir: + _chdir ((const char *) a0); + break; + case SYS_getcwd: + _getcwd ((char *) a0, a1); + break; + case SYS_stat: + _stat ((const char *) a0, (struct stat *) a1); + break; + case SYS_fstat: + _fstat (a0, (struct stat *) a1); + break; + case SYS_lstat: + _lstat ((const char *) a0, (struct stat *) a1); + break; + case SYS_fstatat: + _fstatat (a0, (const char *) a1, (struct stat *) a2, a3); + break; + case SYS_access: + _access ((const char *) a0, a1); + break; + case SYS_faccessat: + _faccessat (a0, (const char *) a1, a2, a3); + break; + case SYS_gettimeofday: + _gettimeofday ((struct timeval *) a0, (void *) a1); + break; + case SYS_times: + _times ((struct tms *) a0); + break; + default: + unimplemented_syscall (); + break; + } +} diff --git a/cv32e40p/bsp/vectors.S b/cv32e40p/bsp/vectors.S new file mode 100644 index 0000000..08af412 --- /dev/null +++ b/cv32e40p/bsp/vectors.S @@ -0,0 +1,54 @@ +/* +* Copyright 2019 ETH Zürich and University of Bologna +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +.section .vectors, "ax" +.option norvc +.global vector_table + +vector_table: + j u_sw_irq_handler + j __no_irq_handler + j __no_irq_handler + j m_software_irq_handler + j __no_irq_handler + j __no_irq_handler + j __no_irq_handler + j m_timer_irq_handler + j __no_irq_handler + j __no_irq_handler + j __no_irq_handler + j m_external_irq_handler + j __no_irq_handler + j __no_irq_handler + j __no_irq_handler + j __no_irq_handler + j m_fast0_irq_handler + j m_fast1_irq_handler + j m_fast2_irq_handler + j m_fast3_irq_handler + j m_fast4_irq_handler + j m_fast5_irq_handler + j m_fast6_irq_handler + j m_fast7_irq_handler + j m_fast8_irq_handler + j m_fast9_irq_handler + j m_fast10_irq_handler + j m_fast11_irq_handler + j m_fast12_irq_handler + j m_fast13_irq_handler + j m_fast14_irq_handler + j m_fast15_irq_handler + diff --git a/eth.sh b/eth.sh index cced27d..ba02596 100644 --- a/eth.sh +++ b/eth.sh @@ -27,4 +27,10 @@ conda activate $MINICONDA_ENV export PATH=~/eth/bin:~/verible/bin:$ROOT_DIR/install/verilator/bin:$GCC_TOOLCHAIN:$PATH source ~/vivado.sh -echo `verilator --version` \ No newline at end of file +echo `verilator --version` +# +export CV_SIMULATOR=verilator +export CV_SW_TOOLCHAIN=$ROOT_DIR/install/riscv-gcc +export CV_SW_PREFIX=riscv32-unknown-elf- +export CV_SW_MARCH=rv32im_zicsr +export CV_SW_CC=gcc \ No newline at end of file diff --git a/mk/Common.mk b/mk/Common.mk new file mode 100644 index 0000000..98f11bc --- /dev/null +++ b/mk/Common.mk @@ -0,0 +1,180 @@ +############################################################################### +# +# Copyleft 2024 +# Copyright 2020 OpenHW Group +# +# Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://solderpad.org/licenses/ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +# +############################################################################### +# +# Common code for simulation Makefiles. +# +############################################################################### +# +# Copyright 2019 Claire Wolf +# Copyright 2019 Robert Balas +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# +# Original Author: Robert Balas (balasr@iis.ee.ethz.ch) +# +############################################################################### + +RISCV = $(CV_SW_TOOLCHAIN) +RISCV_PREFIX = $(CV_SW_PREFIX) +RISCV_EXE_PREFIX = $(RISCV)/bin/$(RISCV_PREFIX) + +RISCV_MARCH = $(CV_SW_MARCH) +RISCV_CC = $(CV_SW_CC) +RISCV_CFLAGS += + +CFLAGS ?= -Os -g -static -mabi=ilp32 -march=$(RISCV_MARCH) -Wall -pedantic $(RISCV_CFLAGS) + +TEST_FILES = $(filter %.c %.S,$(wildcard $(TEST_TEST_DIR)/*)) +# Optionally use linker script provided in test directory +# this must be evaluated at access time, so ifeq/ifneq does +# not get parsed correctly +TEST_RESULTS_LD = $(addprefix $(SIM_TEST_PROGRAM_RESULTS)/, link.ld) +TEST_LD = $(addprefix $(TEST_TEST_DIR)/, link.ld) + +LD_LIBRARY = $(if $(wildcard $(TEST_RESULTS_LD)),-L $(SIM_TEST_PROGRAM_RESULTS),$(if $(wildcard $(TEST_LD)),-L $(TEST_TEST_DIR),)) +LD_FILE = $(if $(wildcard $(TEST_RESULTS_LD)),$(TEST_RESULTS_LD),$(if $(wildcard $(TEST_LD)),$(TEST_LD),$(BSP)/link.ld)) + +$(warning TEST_TEST_DIR set to $(TEST_TEST_DIR)) +$(warning RISCV set to $(RISCV)) +$(warning RISCV_PREFIX set to $(RISCV_PREFIX)) +$(warning RISCV_EXE_PREFIX set to $(RISCV_EXE_PREFIX)) +$(warning RISCV_MARCH set to $(RISCV_MARCH)) +$(warning RISCV_CC set to $(RISCV_CC)) +$(warning RISCV_CFLAGS set to $(RISCV_CFLAGS)) + +BSP = $(CORE_V_VERIF)/$(CV_CORE_LC)/bsp + + +%.hex: %.elf + @echo "$(BANNER)" + @echo "* Generating hexfile, readelf and objdump files" + @echo "$(BANNER)" + $(RISCV_EXE_PREFIX)objcopy -O verilog \ + $< \ + $@ + $(RISCV_EXE_PREFIX)readelf -a $< > $*.readelf + $(RISCV_EXE_PREFIX)objdump \ + -d \ + -M no-aliases \ + -M numeric \ + -S \ + $*.elf > $*.objdump + $(RISCV_EXE_PREFIX)objdump \ + -d \ + -S \ + -M no-aliases \ + -M numeric \ + -l \ + $*.elf | ${CORE_V_VERIF}/bin/objdump2itb - > $*.itb + +# Patterned targets to generate ELF. Used only if explicit targets do not match. +# +.PRECIOUS : %.elf + +# Single rule for compiling test source into an ELF file +# For directed tests, TEST_FILES gathers all of the .S and .c files in a test directory +# For corev_ tests, TEST_FILES will only point to the specific .S for the RUN_INDEX and TEST_NAME provided to make +ifeq ($(shell echo $(TEST) | head -c 6),corev_) +TEST_FILES = $(filter %.c %.S,$(wildcard $(SIM_TEST_PROGRAM_RESULTS)/$(TEST_NAME)$(OPT_RUN_INDEX_SUFFIX).S)) +else +TEST_FILES = $(filter %.c %.S,$(wildcard $(TEST_TEST_DIR)/*)) +endif + +# If a test defines "default_cflags" in its yaml, then it is responsible to define ALL flags +# Otherwise add the default cflags in the variable CFLAGS defined above +ifneq ($(TEST_DEFAULT_CFLAGS),) +TEST_CFLAGS += $(TEST_DEFAULT_CFLAGS) +else +TEST_CFLAGS += $(CFLAGS) +endif + +# Optionally use linker script provided in test directory +# this must be evaluated at access time, so ifeq/ifneq does +# not get parsed correctly +TEST_RESULTS_LD = $(addprefix $(SIM_TEST_PROGRAM_RESULTS)/, link.ld) +TEST_LD = $(addprefix $(TEST_TEST_DIR)/, link.ld) + +LD_LIBRARY = $(if $(wildcard $(TEST_RESULTS_LD)),-L $(SIM_TEST_PROGRAM_RESULTS),$(if $(wildcard $(TEST_LD)),-L $(TEST_TEST_DIR),)) +LD_FILE = $(if $(wildcard $(TEST_RESULTS_LD)),$(TEST_RESULTS_LD),$(if $(wildcard $(TEST_LD)),$(TEST_LD),$(BSP)/link.ld)) +LD_LIBRARY += -L $(SIM_BSP_RESULTS) + +ifeq ($(TEST_FIXED_ELF),1) +%.elf: + @echo "$(BANNER)" + @echo "* Copying fixed ELF test program to $(@)" + @echo "$(BANNER)" + mkdir -p $(SIM_TEST_PROGRAM_RESULTS) + cp $(TEST_TEST_DIR)/$(TEST).elf $@ +else +%.elf: $(TEST_FILES) bsp + mkdir -p $(SIM_TEST_PROGRAM_RESULTS) + @echo "$(BANNER)" + @echo "* Compiling test-program $@" + @echo "$(BANNER)" + $(RISCV_EXE_PREFIX)$(RISCV_CC) \ + $(CFG_CFLAGS) \ + $(TEST_CFLAGS) \ + $(RISCV_CFLAGS) \ + -I $(BSP) \ + -o $@ \ + -nostartfiles \ + -nostdlib \ + $(TEST_FILES) \ + -T $(LD_FILE) \ + $(LD_LIBRARY) \ + -lcv-verif +endif + +.PHONY: hex + +# Shorthand target to only build the firmware using the hex and elf suffix rules above +hex: $(SIM_TEST_PROGRAM_RESULTS)/$(TEST_PROGRAM)$(OPT_RUN_INDEX_SUFFIX).hex + +bsp: + @echo "$(BANNER)" + @echo "* Compiling the BSP" + @echo "$(BANNER)" + mkdir -p $(SIM_BSP_RESULTS) + cp $(BSP)/Makefile $(SIM_BSP_RESULTS) + make -C $(SIM_BSP_RESULTS) \ + VPATH=$(BSP) \ + RISCV=$(RISCV) \ + RISCV_PREFIX=$(RISCV_PREFIX) \ + RISCV_EXE_PREFIX=$(RISCV_EXE_PREFIX) \ + RISCV_MARCH=$(RISCV_MARCH) \ + RISCV_CC=$(RISCV_CC) \ + RISCV_CFLAGS="$(RISCV_CFLAGS)" \ + all + + +clean_bsp: + make -C $(BSP) clean + rm -rf $(SIM_BSP_RESULTS) \ No newline at end of file diff --git a/sim/core/Makefile.verilator b/sim/core/Makefile.verilator index bff80c1..2d4c839 100644 --- a/sim/core/Makefile.verilator +++ b/sim/core/Makefile.verilator @@ -23,12 +23,18 @@ CV_CORE_PKG := $(shell $(BENDER) path $(CV_CORE_LC)) CV_CORE_MANIFEST ?= $(CV_CORE_PKG)/cv32e40p_manifest.flist export DESIGN_RTL_DIR = $(CV_CORE_PKG)/rtl -TEST ?= + +TEST ?= hello-world # Test-Program directores. # Relative path is used for Verilator which cannot seem to handle loooong pathnames. TEST_PROGRAM_PATH = $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs/custom TEST_PROGRAM_RELPATH = ../../$(CV_CORE_LC)/tests/programs/custom #TEST_PROGRAM_RELPATH = $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs/custom +TEST_TEST_DIR := $(TEST_PROGRAM_PATH)/$(TEST) + +# -Core Firmware and the RISCV GCC Toolchain (SDK) +include $(CORE_V_VERIF)/mk/Common.mk + # Common output directories RUN_INDEX ?= 0 @@ -36,7 +42,7 @@ SIM_RESULTS ?= simulation_results SIM_TEST_RESULTS = $(SIM_RESULTS)/$(TEST) SIM_RUN_RESULTS = $(SIM_TEST_RESULTS)/$(RUN_INDEX) SIM_TEST_PROGRAM_RESULTS = $(SIM_RUN_RESULTS)/test_program -SIM_BSP_RESULTS = $(SIM_TEST_PROGRAM_RESULTS)/bsp +SIM_BSP_RESULTS = $(CORE_V_VERIF)/$(SIM_TEST_PROGRAM_RESULTS)/bsp # Compile compile flags for all simulators SV_CMP_FLAGS = @@ -122,8 +128,8 @@ verilate-clean: if [ -e memory_dump.bin ]; then rm memory_dump.bin; fi if [ -e verilator_tb.vcd ]; then rm verilator_tb.vcd; fi -.PHONY: run-test -run-test: verilate + +run-test: verilate $(TEST_PROGRAM_PATH)/$(TEST)/$(TEST).hex @echo "$(BANNER)" @echo "* Running with Verilator: logfile in $(SIM_TEST_RESULTS)/$(TEST).log" @echo "$(BANNER)" @@ -135,3 +141,12 @@ run-test: verilate ############################################################################### +clean-test-programs: + find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.o" -delete + find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.hex" -delete + find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.elf" -delete + find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.map" -delete + find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.readelf" -delete + find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.objdump" -delete + find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "corev_*.S" -delete + find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.itb" -delete From 5e41021394097f5cb3bf0aa446ad577b4a4a94f3 Mon Sep 17 00:00:00 2001 From: darotsr Date: Sun, 29 Sep 2024 22:35:49 +0300 Subject: [PATCH 5/8] script to build cv32e40p/tests/programs/custom/hello-world --- cv32e40p/.gitignore | 2 + cv32e40p/bsp/.gitignore | 1 - cv32e40p/bsp/Makefile | 69 ++- cv32e40p/bsp/README.md | 166 ------ cv32e40p/bsp/common-llvm.mk | 103 ++++ cv32e40p/bsp/common.mk | 84 +++ cv32e40p/bsp/crt0.S | 173 +++--- cv32e40p/bsp/handlers.S | 223 -------- cv32e40p/bsp/link.ld | 367 +++--------- cv32e40p/bsp/link_corev-dv.ld | 1 - cv32e40p/bsp/simple_system_common.c | 191 +++++++ cv32e40p/bsp/simple_system_common.h | 116 ++++ cv32e40p/bsp/simple_system_regs.h | 24 + cv32e40p/bsp/startup.c | 28 + cv32e40p/bsp/syscalls.c | 376 ------------- cv32e40p/bsp/tinyprintf.c | 521 ++++++++++++++++++ cv32e40p/bsp/tinyprintf.h | 186 +++++++ cv32e40p/bsp/vectors.S | 54 -- .../programs/custom/hello-world/hello-world.c | 3 +- mk/Common.mk | 100 ++-- sim/core/Makefile.verilator | 16 +- sim/core/README.md | 7 + tb/core/tb_top_verilator.sv | 2 +- 23 files changed, 1565 insertions(+), 1248 deletions(-) delete mode 100644 cv32e40p/bsp/.gitignore delete mode 100644 cv32e40p/bsp/README.md create mode 100644 cv32e40p/bsp/common-llvm.mk create mode 100644 cv32e40p/bsp/common.mk delete mode 100644 cv32e40p/bsp/handlers.S delete mode 120000 cv32e40p/bsp/link_corev-dv.ld create mode 100644 cv32e40p/bsp/simple_system_common.c create mode 100644 cv32e40p/bsp/simple_system_common.h create mode 100644 cv32e40p/bsp/simple_system_regs.h create mode 100644 cv32e40p/bsp/startup.c delete mode 100644 cv32e40p/bsp/syscalls.c create mode 100644 cv32e40p/bsp/tinyprintf.c create mode 100644 cv32e40p/bsp/tinyprintf.h delete mode 100644 cv32e40p/bsp/vectors.S diff --git a/cv32e40p/.gitignore b/cv32e40p/.gitignore index 7086cab..d7745fb 100644 --- a/cv32e40p/.gitignore +++ b/cv32e40p/.gitignore @@ -4,3 +4,5 @@ *.objdump *.yaml *.readelf +bsp.old/ +bsp.cv/ diff --git a/cv32e40p/bsp/.gitignore b/cv32e40p/bsp/.gitignore deleted file mode 100644 index c0a1f34..0000000 --- a/cv32e40p/bsp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libcv-verif.a diff --git a/cv32e40p/bsp/Makefile b/cv32e40p/bsp/Makefile index ee05577..b98af05 100644 --- a/cv32e40p/bsp/Makefile +++ b/cv32e40p/bsp/Makefile @@ -1,28 +1,57 @@ CV_SW_TOOLCHAIN ?= /opt/riscv RISCV ?= $(CV_SW_TOOLCHAIN) RISCV_EXE_PREFIX ?= $(RISCV)/bin/riscv32-unknown-elf- -RISCV_GCC = $(RISCV_EXE_PREFIX)gcc -RISCV_AR = $(RISCV_EXE_PREFIX)ar -SRC = crt0.S handlers.S syscalls.c vectors.S -OBJ = crt0.o handlers.o syscalls.o vectors.o -LIBCV-VERIF = libcv-verif.a -CFLAGS ?= -Os -g -static -mabi=ilp32 -march=$(CV_SW_MARCH) -Wall -pedantic +RISCV_GCC = $(RISCV_EXE_PREFIX)gcc +RISCV_AR = $(RISCV_EXE_PREFIX)ar +OBJDUMP = $(RISCV_EXE_PREFIX)objdump +LD_FILE ?= +# Source files +APP_FILES ?= +SRCS += crt0.S simple_system_common.c startup.c tinyprintf.c +SRCS += $(APP_FILES) + +SRC_C = $(filter %.c, $(SRCS)) +SRC_S = $(filter %.S, $(SRCS)) + +# Generate object files automatically from source files +OBJS += $(SRC_S:.S=.o) +OBJS += $(SRC_C:.c=.o) + +DEPS = $(OBJS:.o=.d) + +# Library output +LIBCV-VERIF = libcv-verif.a + +# Flags for tinyprintf +TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_PRINTF +TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_SPRINTF + +# Compiler flags +CFLAGS ?= -march=$(CV_SW_MARCH) -mabi=ilp32 -static -mcmodel=medany -Wall -g -O3 \ + -fvisibility=hidden -nostdlib -nostartfiles -ffreestanding $(TINY_PRINTF_FLAGS) $(RISCV_CFLAGS) + +# Build the library all: $(LIBCV-VERIF) -$(LIBCV-VERIF): $(OBJ) - $(RISCV_AR) rcs $@ $(OBJ) +$(LIBCV-VERIF): $(OBJS) + @echo '*** packing ' $(OBJS) into $(CURDIR)/$@ + $(RISCV_AR) rcs $@ $(OBJS) +# Compilation rules %.o : %.c $(RISCV_GCC) $(CFLAGS) -c $< -o $@ - + %.o : %.S $(RISCV_GCC) $(CFLAGS) -c $< -o $@ +# Clean up the generated files clean: - rm -f $(OBJ) $(LIBCV-VERIF) - + rm -f $(OBJS) + rm -f $(DEPS) + rm -f $(LIBCV-VERIF) +# Print variables for debugging vars: @echo "make bsp variables:" @echo " CV_SW_TOOLCHAIN = $(CV_SW_TOOLCHAIN)" @@ -32,3 +61,21 @@ vars: @echo " RISCV_GCC = $(RISCV_GCC)" @echo " RISCV_MARCH = $(RISCV_MARCH)" + +compile: $(OBJS) + @echo '*** compiled: ' $(SRCS) + + +%.elf: $(OBJS) +# mkdir -p $(SIM_TEST_PROGRAM_RESULTS) + @echo "*** TEST_FILES=$(TEST_FILES)" + @echo "*** linking $@" + @echo "$(BANNER)" + $(RISCV_EXE_PREFIX)$(RISCV_CC) \ + $(CFLAGS) \ + -o $@ \ + $(OBJS) \ + -T $(LD_FILE) \ +# $(CC) $(CFLAGS) -T $(LINKER_SCRIPT) $(OBJS) -o $@ $(LIBS) + $(OBJDUMP) -dh $@ >$@.headers + @echo $(CURDIR)/$@.headers diff --git a/cv32e40p/bsp/README.md b/cv32e40p/bsp/README.md deleted file mode 100644 index b0b7d2b..0000000 --- a/cv32e40p/bsp/README.md +++ /dev/null @@ -1,166 +0,0 @@ -Board Support Package (BSP) for CV32E40P Verification -===================================================== - -This BSP provides the code to support running programs on the CV32E40P verification -target. It performs initialization tasks (`crt0.S`), handles -interrupts/exceptions (`vectors.S`, `handlers.S`), provides syscall -implementations (`syscalls.c`) and includes a linker script (`link.ld`) to -control the placement of sections in the binary. - -Each file is described in more detail below followed by instructions for -building and using the BSP. - -C Runtime Initialization ------------------------- - -The C Runtime file `crt0.S` provides the `_start` function which is the entry -point of the program and performs the following tasks: - * Initialize global and stack pointer. - * Store the address of `vector_table` in `mtvec`, setting the lower two bits - to `0x2` to select vectored interrupt mode. - * Zero the BSS section. - * Invoke initialization of C constructors and set destructors to be called on - exit. - * Zero `argc` and `argv` (the stack is not initialized, so these are zeroed - to prevent uninitialized values causing a mismatch against the reference - result). - * Call `main`. - * If `main` returns, call `exit` with its return code. - -Interrupt and Exception Handling --------------------------------- - -When a RISC-V core traps on an interrupt/exception, the `pc` is stored in `mepc` -and the reason for the trap is stored in `mcause`. The `MSB` of `mcause` -is set to `0` for an exception and `1` for an interrupt; the remaining bits -`mcause[MXLEN-2:0]` contain the exception code. The table of `mcause` values is -defined in Table 3.6 of the [RISC-V Instruction Set Manual Volume II: Privileged -Architecture Version 20190608-Priv-MSU-Ratified](https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMFDQC-and-Priv-v1.11/riscv-privileged-20190608.pdf). - -The core jumps to a location in the vector table according to the `BASE` address -of the vector table stored in `mtvec` and the value of the exception code in -`mcause`. In vectored mode, all exceptions jump to `BASE` and interrupts jump to -`BASE+4*mcause[XLEN-2:0]`. Note that because user software interrupts have -exception code `0`, they jump to the same location as exceptions, therefore the -user software interrupt handler must also handle exceptions. - -The vector table is defined in `vectors.S` and may jump to one of the -following interrupt request handlers in `handlers.S`: - * `u_sw_irq_handler` - handles user software interrupts and all exceptions. - Saves all caller saved registers then checks `mcause` and jumps to the - appropriate handler as follows: - - Breakpoint: jump to `handle_ebreak`. - - Illegal instruction: jump to `handle_illegal`. - - Environment call from M-mode: jump to `handle_ecall`. - - Any other exception or user software interrupt: jump to `handle_unknown`. - * `m_software_irq_handler` - handles machine-mode software interrupts - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_timer_irq_handler` - handles machine-mode timer interrupts - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_external_irq_handler` - handles machine-mode external interrupts - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast0_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast1_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast2_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast3_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast4_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast5_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast6_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast7_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast8_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast9_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast10_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast11_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast12_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast13_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast14_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `m_fast15_irq_handler` - handles machine-mode fast external interrupts (platform extension for CV32) - - Currently jumps to `__no_irq_handler`. Behavior to be defined in future commit. - * `__no_irq_handler` - loops printing "no exception handler installed". - -The following exception handlers may be called from `u_sw_irq_handler`: - * `handle_ecall` - calls `handle_syscall` which checks the syscall number and - calls the corresponding syscall function. - * `handle_ebreak` - currently just prints "ebreak exception handler entered" - * `handle_illegal_insn` - prints "illegal instruction exception handler - entered" - * `unknown_handler` - called when there is no handler for the interrupt/ - exception. This is the only case where `mepc` is not incremented, because we - do not know the appropiate action to take. - -Returning from the `u_sw_irq_handler`. All handlers called by `u_sw_irq_handler` -increment `mepc` before calling `mret`, except for `unknown_handler`. Handlers -that require `mepc` to be incremented jump to `end_handler_incr_mepc` otherwise -they jump to `end_handler_ret`. All caller saved registers are restored before -finally calling `mret`. - -Some test cases require the ability to override the default handlers. In future, -these handlers will be made overridable by defining their labels as `.weak` -symbols. Test cases can then provide their own handlers where necessary. - -System Calls ------------- - -On a bare-metal system there is no OS to handle system calls, therefore, we -define our own system calls in `syscalls.c`. For example, the implementation of -`_write` outputs a byte at a time to the virtual printer peripheral. Many of the -functions provide minimal implementations that simply fail gracefully due to -lack of necessary OS support e.g. no file system. - -The [RISC-V Instruction Set Manual Volume I: Unprivileged ISA Version 20191213]( -https://content.riscv.org/wp-content/uploads/2019/06/riscv-spec.pdf) states that -for an `ecall` the "ABI for the system will define how parameters for the -environment request are passed". This BSP follows the convention used for RISC-V -in `newlib`. Parameters are passed in registers `a0` to `a5` and system call ID -in `a7` (`t0` on RV32E). When handling an `ecall`, `handle_ecall` calls -`handle_syscall` which then calls the appropriate function that implements the -system call, passing parameters as necessary. - -Linker Script -------------- - -The linker script defines the memory layout and controls the mapping of input -sections from object files to output sections in the output binary. - -The `link.ld` script is based on the standard upstream RV32 linker script, with -some changes required for CV32E40P: - * Memory layout is defined as follows: - * `ram` start=0x0, length=4MB - * `dbg` start=0x1A110800, length=2KB - * Changes to output section placement are as follows: - - `.vectors` start=ORIGIN(`ram`) - - `.init` start=0x80 - - `.heap` starts at end of data and grows upwards - - `.stack` starts at the end of `ram` and grows downwards - - `.debugger` start=ORIGIN(`dbg`) - - `.debugger_exception` start=0x1A110C00 - - `.debugger_stack` follows `.debugger_exception` - -Building and using the BSP Library ----------------------------------- - -The BSP can be built in this directory as follows: -``` -make -``` -This produces libcv-verif.a which can then be linked with a test program as -follows: - -``` -gcc test-program.c -nostartfiles -T/path/to/bsp/link.ld -L/path/to/bsp/ -lcv-verif -``` diff --git a/cv32e40p/bsp/common-llvm.mk b/cv32e40p/bsp/common-llvm.mk new file mode 100644 index 0000000..6636541 --- /dev/null +++ b/cv32e40p/bsp/common-llvm.mk @@ -0,0 +1,103 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +COMMON_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) + +COMMON_SRCS = $(wildcard $(COMMON_DIR)/*.c) +INCS := -I$(COMMON_DIR) + + +TARGET := riscv32 +ARCH := rv32g +RISCV_ABI := ilp32 + +RISCV_WARNINGS += -Wunused-variable -Wall -Wextra -Wno-unused-command-line-argument # -Werror + +LLVM_FLAGS ?= --target=$(TARGET) -march=$(ARCH) -menable-experimental-extensions -mabi=$(RISCV_ABI) -mno-relax +RISCV_FLAGS ?= $(LLVM_FLAGS) -mcmodel=medany -O3 -ffast-math -g $(RISCV_WARNINGS) +RISCV_CCFLAGS ?= $(RISCV_FLAGS) -ffunction-sections -fdata-sections -std=gnu99 -nostdlib -nostartfiles +RISCV_CXXFLAGS ?= $(RISCV_FLAGS) -ffunction-sections -fdata-sections +RISCV_LDFLAGS ?= -static -nostdlib + +LINKER_SCRIPT ?= $(COMMON_DIR)/link.ld + +CFLAGS ?= -march=$(ARCH) -mabi=ilp32 -static -mcmodel=medany -Wall -g -O3\ + -fvisibility=hidden -nostdlib -nostartfiles -ffreestanding $(PROGRAM_CFLAGS) + +ifdef PROGRAM +PROGRAM_C := $(PROGRAM).c +endif + +SRCS = $(COMMON_SRCS) $(PROGRAM_C) $(EXTRA_SRCS) + +C_SRCS = $(filter %.c, $(SRCS)) +ASM_SRCS = $(filter %.S, $(SRCS)) + +CC := $(LLVM_TOOLCHAIN)/clang +LD := $(LLVM_TOOLCHAIN)/riscv32-unknown-elf-ld +#LD := $(LLVM_TOOLCHAIN)/ld.lld +#LD := $(GCC_TOOLCHAIN)/riscv32-unknown-elf-gcc + + +#OBJCOPY := $(LLVM_TOOLCHAIN)/llvm-objcopy +OBJDUMP := $(GCC_TOOLCHAIN)/riscv32-unknown-elf-objdump +#OBJDUMP := $(LLVM_TOOLCHAIN)/llvm-objdump + + +CRT ?= $(COMMON_DIR)/crt0.S + + + + +OBJS := ${C_SRCS:.c=.o} ${ASM_SRCS:.S=.o} ${CRT:.S=.o} +DEPS = $(OBJS:%.o=%.d) + +ifdef PROGRAM +OUTFILES := $(PROGRAM).elf +else +OUTFILES := $(OBJS) +endif + +all: $(OUTFILES) + +ifdef PROGRAM +$(PROGRAM).elf: $(OBJS) $(LINKER_SCRIPT) +# $(LD) $(CFLAGS) -T $(LINKER_SCRIPT) $(OBJS) -o $@ $(LIBS) + $(LD) $(RISCV_LDFLAGS) -Map $@.map -T $(LINKER_SCRIPT) $(OBJS) -o $@ $(LIBS) + $(OBJDUMP) -dh $@ >$@.headers + + +.PHONY: disassemble +disassemble: $(PROGRAM).dis +endif + +%.dis: %.elf + $(OBJDUMP) -fhSD $^ > $@ + +# Note: this target requires the srecord package to be installed. +# XXX: This could be replaced by objcopy once +# https://sourceware.org/bugzilla/show_bug.cgi?id=19921 +# is widely available. +%.vmem: %.bin + srec_cat $^ -binary -offset 0x0000 -byte-swap 4 -o $@ -vmem + +%.bin: %.elf + $(OBJCOPY) -O binary $^ $@ + +%.o: %.c + $(CC) -c $(RISCV_CCFLAGS) $(INCS) -o $@ $< +# Rule to compile C to assembly +%.s: %.c + $(CC) -S $(RISCV_CCFLAGS) $(INCS) -o $@ $< + + +%.o: %.S + $(CC) -c $(RISCV_CCFLAGS) $(INCS) -o $@ $< + +clean: + $(RM) -f $(OBJS) $(DEPS) + rm -f *.bin *.vmem *.elf *.headers + +distclean: clean + $(RM) -f $(OUTFILES) diff --git a/cv32e40p/bsp/common.mk b/cv32e40p/bsp/common.mk new file mode 100644 index 0000000..ae0a876 --- /dev/null +++ b/cv32e40p/bsp/common.mk @@ -0,0 +1,84 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +COMMON_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) + +COMMON_SRCS = $(wildcard $(COMMON_DIR)/*.c) +INCS := -I$(COMMON_DIR) + +# ARCH = rv32im # to disable compressed instructions +ARCH ?= rv32im_zicsr + +ifdef PROGRAM +PROGRAM_C := $(PROGRAM).c +endif + +SRCS = $(COMMON_SRCS) $(PROGRAM_C) $(EXTRA_SRCS) + +C_SRCS = $(filter %.c, $(SRCS)) +ASM_SRCS = $(filter %.S, $(SRCS)) + +CC = riscv32-unknown-elf-gcc + +CROSS_COMPILE = $(patsubst %-gcc,%-,$(CC)) +OBJCOPY ?= $(CROSS_COMPILE)objcopy +OBJDUMP ?= $(CROSS_COMPILE)objdump + +LINKER_SCRIPT ?= $(COMMON_DIR)/link.ld +CRT ?= $(COMMON_DIR)/crt0.S + +TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_PRINTF +TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_SPRINTF +CFLAGS ?= -march=$(ARCH) -mabi=ilp32 -static -mcmodel=medany -Wall -g -O3\ + -fvisibility=hidden -nostdlib -nostartfiles -ffreestanding $(TINY_PRINTF_FLAGS) $(PROGRAM_CFLAGS) + +OBJS := ${C_SRCS:.c=.o} ${ASM_SRCS:.S=.o} ${CRT:.S=.o} +DEPS = $(OBJS:%.o=%.d) + +ifdef PROGRAM +OUTFILES := $(PROGRAM).elf +else +OUTFILES := $(OBJS) +endif + +all: $(OUTFILES) + +ifdef PROGRAM +$(PROGRAM).elf: $(OBJS) $(LINKER_SCRIPT) + $(CC) $(CFLAGS) -T $(LINKER_SCRIPT) $(OBJS) -o $@ $(LIBS) + $(OBJDUMP) -dh $@ >$@.headers + + +.PHONY: disassemble +disassemble: $(PROGRAM).dis +endif + +%.dis: %.elf + $(OBJDUMP) -fhSD $^ > $@ + +# Note: this target requires the srecord package to be installed. +# XXX: This could be replaced by objcopy once +# https://sourceware.org/bugzilla/show_bug.cgi?id=19921 +# is widely available. +%.vmem: %.bin + srec_cat $^ -binary -offset 0x0000 -byte-swap 4 -o $@ -vmem + +%.bin: %.elf + $(OBJCOPY) -O binary $^ $@ + +%.o: %.c + $(CC) $(CFLAGS) -MMD -c $(INCS) -o $@ $< +# Rule to compile C to assembly +%.s: %.c + $(CC) $(CFLAGS) -S $(INCS) $< -o $@ + +%.o: %.S + $(CC) $(CFLAGS) -MMD -c $(INCS) -o $@ $< + +clean: + $(RM) -f $(OBJS) $(DEPS) + rm -f *.bin *.vmem *.elf *.headers + +distclean: clean + $(RM) -f $(OUTFILES) diff --git a/cv32e40p/bsp/crt0.S b/cv32e40p/bsp/crt0.S index 302da07..be2a106 100644 --- a/cv32e40p/bsp/crt0.S +++ b/cv32e40p/bsp/crt0.S @@ -1,72 +1,105 @@ -/* Copyright (c) 2017 SiFive Inc. All rights reserved. - * Copyright (c) 2019 ETH Zürich and University of Bologna - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the FreeBSD License. This program is distributed in the hope that - * it will be useful, but WITHOUT ANY WARRANTY expressed or implied, - * including the implied warranties of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. A copy of this license is available at - * http://www.opensource.org/licenses. - */ -/* Make sure the vector table gets linked into the binary. */ -.global vector_table - -/* Entry point for bare metal programs */ -.section .text.start -.global _start -.type _start, @function +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +#include "simple_system_regs.h" + +.section .text + +default_exc_handler: + jal x0, simple_exc_handler + +timer_handler: + jal x0, simple_timer_handler + +reset_handler: + /* set all registers to zero */ + mv x1, x0 + mv x2, x1 + mv x3, x1 + mv x4, x1 + mv x5, x1 + mv x6, x1 + mv x7, x1 + mv x8, x1 + mv x9, x1 + mv x10, x1 + mv x11, x1 + mv x12, x1 + mv x13, x1 + mv x14, x1 + mv x15, x1 + mv x16, x1 + mv x17, x1 + mv x18, x1 + mv x19, x1 + mv x20, x1 + mv x21, x1 + mv x22, x1 + mv x23, x1 + mv x24, x1 + mv x25, x1 + mv x26, x1 + mv x27, x1 + mv x28, x1 + mv x29, x1 + mv x30, x1 + mv x31, x1 + + /* stack initilization */ + la x2, _stack_start _start: -/* initialize global pointer */ -.option push -.option norelax -1: auipc gp, %pcrel_hi(__global_pointer$) - addi gp, gp, %pcrel_lo(1b) -.option pop - -/* initialize stack pointer */ - la sp, __stack_end - -/* set vector table address */ - la a0, __vector_start - ori a0, a0, 1 /*vector mode = vectored */ - csrw mtvec, a0 - -/* clear the bss segment */ - la a0, _edata - la a2, _end - sub a2, a2, a0 - li a1, 0 - call memset - -/* new-style constructors and destructors */ - la a0, __libc_fini_array - call atexit - call __libc_init_array - -/* call main */ -// lw a0, 0(sp) /* a0 = argc */ -// addi a1, sp, __SIZEOF_POINTER__ /* a1 = argv */ -// li a2, 0 /* a2 = envp = NULL */ -// Initialize these variables to 0. Cannot use argc or argv -// since the stack is not initialized - li a0, 0 - li a1, 0 - li a2, 0 - - call main - tail exit - -.size _start, .-_start - -.global _init -.type _init, @function -.global _fini -.type _fini, @function -_init: -_fini: - /* These don't have to do anything since we use init_array/fini_array. Prevent - missing symbol error */ - ret -.size _init, .-_init -.size _fini, .-_fini + .global _start + + /* clear BSS */ + la x26, _bss_start + la x27, _bss_end + + bge x26, x27, zero_loop_end + +zero_loop: + sw x0, 0(x26) + addi x26, x26, 4 + ble x26, x27, zero_loop +zero_loop_end: + + +main_entry: + /* jump to startup program entry */ +// addi x10, x0, 0 +// addi x11, x0, 0 + jal x1, startup + /* Halt simulation */ + li a1, 0 + li a2, 0 + li a3, 0 + li a4, 0 + li a5, 0 + li a7, 93 + ecall + + /* If execution ends up here just put the core to sleep */ +sleep_loop: + wfi + j sleep_loop + +/* =================================================== [ exceptions ] === */ +/* This section has to be down here, since we have to disable rvc for it */ + + .section .vectors, "ax" + .option norvc; + + // All unimplemented interrupts/exceptions go to the default_exc_handler. + .org 0x00 + .rept 7 + jal x0, default_exc_handler + .endr + jal x0, timer_handler + .rept 23 + jal x0, default_exc_handler + .endr + + // reset vector + .org 0x80 + jal x0, reset_handler diff --git a/cv32e40p/bsp/handlers.S b/cv32e40p/bsp/handlers.S deleted file mode 100644 index 6068105..0000000 --- a/cv32e40p/bsp/handlers.S +++ /dev/null @@ -1,223 +0,0 @@ -/* -* Copyright 2019 ETH Zürich and University of Bologna -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -/* Exception codes */ -#define EXCEPTION_ILLEGAL_INSN 2 -#define EXCEPTION_BREAKPOINT 3 -#define EXCEPTION_ECALL_M 11 - -.section .text.handlers -.global __no_irq_handler -.global u_sw_irq_handler -.global m_software_irq_handler -.global m_timer_irq_handler -.global m_external_irq_handler -.global m_fast0_irq_handler -.global m_fast1_irq_handler -.global m_fast2_irq_handler -.global m_fast3_irq_handler -.global m_fast4_irq_handler -.global m_fast5_irq_handler -.global m_fast6_irq_handler -.global m_fast7_irq_handler -.global m_fast8_irq_handler -.global m_fast9_irq_handler -.global m_fast10_irq_handler -.global m_fast11_irq_handler -.global m_fast12_irq_handler -.global m_fast13_irq_handler -.global m_fast14_irq_handler -.global m_fast15_irq_handler - -.weak u_sw_irq_handler -.weak m_software_irq_handler -.weak m_timer_irq_handler -.weak m_external_irq_handler -.weak m_fast0_irq_handler -.weak m_fast1_irq_handler -.weak m_fast2_irq_handler -.weak m_fast3_irq_handler -.weak m_fast4_irq_handler -.weak m_fast5_irq_handler -.weak m_fast6_irq_handler -.weak m_fast7_irq_handler -.weak m_fast8_irq_handler -.weak m_fast9_irq_handler -.weak m_fast10_irq_handler -.weak m_fast11_irq_handler -.weak m_fast12_irq_handler -.weak m_fast13_irq_handler -.weak m_fast14_irq_handler -.weak m_fast15_irq_handler - - -/* exception handling */ -__no_irq_handler: - la a0, no_exception_handler_msg - jal ra, puts - j __no_irq_handler - -m_software_irq_handler: - j __no_irq_handler - -m_timer_irq_handler: - j __no_irq_handler - -m_external_irq_handler: - j __no_irq_handler - -m_fast0_irq_handler: - j __no_irq_handler - -m_fast1_irq_handler: - j __no_irq_handler - -m_fast2_irq_handler: - j __no_irq_handler - -m_fast3_irq_handler: - j __no_irq_handler - -m_fast4_irq_handler: - j __no_irq_handler - -m_fast5_irq_handler: - j __no_irq_handler - -m_fast6_irq_handler: - j __no_irq_handler - -m_fast7_irq_handler: - j __no_irq_handler - -m_fast8_irq_handler: - j __no_irq_handler - -m_fast9_irq_handler: - j __no_irq_handler - -m_fast10_irq_handler: - j __no_irq_handler - -m_fast11_irq_handler: - j __no_irq_handler - -m_fast12_irq_handler: - j __no_irq_handler - -m_fast13_irq_handler: - j __no_irq_handler - -m_fast14_irq_handler: - j __no_irq_handler - -m_fast15_irq_handler: - j __no_irq_handler - -u_sw_irq_handler: - /* While we are still using puts in handlers, save all caller saved - regs. Eventually, some of these saves could be deferred. */ - addi sp,sp,-64 - sw ra, 0(sp) - sw a0, 4(sp) - sw a1, 8(sp) - sw a2, 12(sp) - sw a3, 16(sp) - sw a4, 20(sp) - sw a5, 24(sp) - sw a6, 28(sp) - sw a7, 32(sp) - sw t0, 36(sp) - sw t1, 40(sp) - sw t2, 44(sp) - sw t3, 48(sp) - sw t4, 52(sp) - sw t5, 56(sp) - sw t6, 60(sp) - csrr t0, mcause - li t1, EXCEPTION_ILLEGAL_INSN - beq t0, t1, handle_illegal_insn - li t1, EXCEPTION_ECALL_M - beq t0, t1, handle_ecall - li t1, EXCEPTION_BREAKPOINT - beq t0, t1, handle_ebreak - j handle_unknown - -handle_ecall: - jal ra, handle_syscall - j end_handler_incr_mepc - -handle_ebreak: - /* TODO support debug handling requirements. */ - la a0, ebreak_msg - jal ra, puts - j end_handler_incr_mepc - -handle_illegal_insn: - la a0, illegal_insn_msg - jal ra, puts - j end_handler_incr_mepc - -handle_unknown: - la a0, unknown_msg - jal ra, puts - /* We don't know what interrupt/exception is being handled, so don't - increment mepc. */ - j end_handler_ret - -end_handler_incr_mepc: - csrr t0, mepc - lb t1, 0(t0) - li a0, 0x3 - and t1, t1, a0 - /* Increment mepc by 2 or 4 depending on whether the instruction at mepc - is compressed or not. */ - bne t1, a0, end_handler_incr_mepc2 - addi t0, t0, 2 -end_handler_incr_mepc2: - addi t0, t0, 2 - csrw mepc, t0 -end_handler_ret: - lw ra, 0(sp) - lw a0, 4(sp) - lw a1, 8(sp) - lw a2, 12(sp) - lw a3, 16(sp) - lw a4, 20(sp) - lw a5, 24(sp) - lw a6, 28(sp) - lw a7, 32(sp) - lw t0, 36(sp) - lw t1, 40(sp) - lw t2, 44(sp) - lw t3, 48(sp) - lw t4, 52(sp) - lw t5, 56(sp) - lw t6, 60(sp) - addi sp,sp,64 - mret - -.section .rodata -illegal_insn_msg: - .string "CV32E40P BSP: illegal instruction exception handler entered\n" -ecall_msg: - .string "CV32E40P BSP: ecall exception handler entered\n" -ebreak_msg: - .string "CV32E40P BSP: ebreak exception handler entered\n" -unknown_msg: - .string "CV32E40P BSP: unknown exception handler entered\n" -no_exception_handler_msg: - .string "CV32E40P BSP: no exception handler installed\n" diff --git a/cv32e40p/bsp/link.ld b/cv32e40p/bsp/link.ld index 9ea197d..297ffc1 100644 --- a/cv32e40p/bsp/link.ld +++ b/cv32e40p/bsp/link.ld @@ -1,311 +1,90 @@ -/* Script for -z combreloc */ -/* Copyright (C) 2014-2020 Free Software Foundation, Inc. - Copyright (C) 2019 ETH Zürich and University of Bologna - Copyright (C) 2020 OpenHW Group - Copying and distribution of this script, with or without modification, - are permitted in any medium without royalty provided the copyright - notice and this notice are preserved. */ +/* Copyright lowRISC contributors. + Licensed under the Apache License, Version 2.0, see LICENSE for details. + SPDX-License-Identifier: Apache-2.0 */ -/* This linker script is adapted from the default linker script for upstream - RISC-V GCC. It has been modified for use in verification of CORE-V cores. -*/ - -OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", - "elf32-littleriscv") OUTPUT_ARCH(riscv) -ENTRY(_start) -/* CORE-V */ MEMORY { - /* Our testbench is a bit weird in that we initialize the RAM (thus - allowing initialized sections to be placed there). Infact we dump all - sections to ram. */ - - ram (rwxai) : ORIGIN = 0x00000000, LENGTH = 0x400000 - dbg (rwxai) : ORIGIN = 0x1A110800, LENGTH = 0x1000 +/* Change this if you'd like different sizes. Arty A7-100(35) has a maximum of 607.5KB(225KB) + BRAM space. Configuration below is for maximum BRAM capacity with Artya A7-35 while letting + CoreMark run (.vmem of 152.8KB). +*/ + ram : ORIGIN = 0x00100000, LENGTH = 0x30000 /* 192 kB */ + stack : ORIGIN = 0x00130000, LENGTH = 0x8000 /* 32 kB */ } -SECTIONS -{ - /* CORE-V Debugger Code: This section address must be the same as the - DM_HaltAddress parameter in the RTL */ - .debugger (ORIGIN(dbg)): - { - KEEP(*(.debugger)); - } >dbg - .debugger_exception (0x1A111000): - { - KEEP(*(.debugger_exception)); - } >dbg - /* Debugger Stack*/ - .debugger_stack : ALIGN(16) - { - PROVIDE(__debugger_stack_start = .); - . = 0x80; - } >dbg +/* Stack information variables */ +_min_stack = 0x2000; /* 8K - minimum stack space to reserve */ +_stack_len = LENGTH(stack); +_stack_start = ORIGIN(stack) + LENGTH(stack); - /* CORE-V: we want a fixed entry point */ - PROVIDE(__boot_address = 0x80); +_entry_point = _vectors_start + 0x80; +ENTRY(_entry_point) - /* CORE-V: interrupt vectors */ - .vectors (ORIGIN(ram)): - { - PROVIDE(__vector_start = .); - KEEP(*(.vectors)); - } >ram +/* The tohost address is used by Spike for a magic "stop me now" message. This + is set to equal SIM_CTRL_CTRL (see simple_system_regs.h), which has that + effect in simple_system simulations. Note that it must be 8-byte aligned. - /* CORE-V: crt0 init code */ - .init (__boot_address): - { - KEEP (*(SORT_NONE(.init))) - KEEP (*(.text.start)) - } >ram + We don't read data back from Spike, so fromhost is set to some dummy value: + we place it just above the top of the stack. + */ +tohost = 0x20008; +fromhost = _stack_start + 0x10; - /* Read-only sections, merged into text segment: */ - PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x10000)); . = SEGMENT_START("text-segment", 0x10000) + SIZEOF_HEADERS; - .interp : { *(.interp) } >ram - .note.gnu.build-id : { *(.note.gnu.build-id) } >ram - .hash : { *(.hash) } >ram - .gnu.hash : { *(.gnu.hash) } >ram - .dynsym : { *(.dynsym) } >ram - .dynstr : { *(.dynstr) } >ram - .gnu.version : { *(.gnu.version) } >ram - .gnu.version_d : { *(.gnu.version_d) } >ram - .gnu.version_r : { *(.gnu.version_r) } >ram - .rela.dyn : - { - *(.rela.init) - *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) - *(.rela.fini) - *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) - *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) - *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) - *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) - *(.rela.ctors) - *(.rela.dtors) - *(.rela.got) - *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) - *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) - *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) - *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) - *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) - PROVIDE_HIDDEN (__rela_iplt_start = .); - *(.rela.iplt) - PROVIDE_HIDDEN (__rela_iplt_end = .); - } >ram - .rela.plt : +SECTIONS +{ + .vectors : { - *(.rela.plt) - } >ram + . = ALIGN(4); + _vectors_start = .; + KEEP(*(.vectors)) + _vectors_end = .; + } > ram - .plt : { *(.plt) } - .iplt : { *(.iplt) } - .text : - { - *(.text.unlikely .text.*_unlikely .text.unlikely.*) - *(.text.exit .text.exit.*) - *(.text.startup .text.startup.*) - *(.text.hot .text.hot.*) - *(SORT(.text.sorted.*)) - *(.text .stub .text.* .gnu.linkonce.t.*) - /* .gnu.warning sections are handled specially by elf.em. */ - *(.gnu.warning) - } >ram - .fini : - { - KEEP (*(SORT_NONE(.fini))) - } >ram - PROVIDE (__etext = .); - PROVIDE (_etext = .); - PROVIDE (etext = .); - .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } >ram - .rodata1 : { *(.rodata1) } >ram - .sdata2 : - { - *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) - } >ram - .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } >ram - .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } >ram - .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ram - .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >ram - .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >ram - /* These sections are generated by the Sun/Oracle C++ compiler. */ - .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } - /* Adjust the address for the data segment. We want to adjust up to - the same address within the page on the next page up. */ - . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); - /* Exception handling */ - .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ram - .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } >ram - .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >ram - .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } >ram - /* Thread Local Storage sections */ - .tdata : - { - PROVIDE_HIDDEN (__tdata_start = .); - *(.tdata .tdata.* .gnu.linkonce.td.*) - } >ram - .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } >ram - .preinit_array : - { - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP (*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); - } >ram - .init_array : - { - PROVIDE_HIDDEN (__init_array_start = .); - KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) - KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) - PROVIDE_HIDDEN (__init_array_end = .); - } >ram - .fini_array : - { - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) - KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) - PROVIDE_HIDDEN (__fini_array_end = .); - } >ram - .ctors : - { - /* gcc uses crtbegin.o to find the start of - the constructors, so we make sure it is - first. Because this is a wildcard, it - doesn't matter if the user does not - actually link against crtbegin.o; the - linker won't look for a file to match a - wildcard. The wildcard also means that it - doesn't matter which directory crtbegin.o - is in. */ - KEEP (*crtbegin.o(.ctors)) - KEEP (*crtbegin?.o(.ctors)) - /* We don't want to include the .ctor section from - the crtend.o file until after the sorted ctors. - The .ctor section from the crtend file contains the - end of ctors marker and it must be last */ - KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) - } >ram - .dtors : - { - KEEP (*crtbegin.o(.dtors)) - KEEP (*crtbegin?.o(.dtors)) - KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) - } >ram - .jcr : { KEEP (*(.jcr)) } - .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } - .dynamic : { *(.dynamic) } - . = DATA_SEGMENT_RELRO_END (0, .); - .data : - { - __DATA_BEGIN__ = .; - *(.data .data.* .gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - } >ram - .data1 : { *(.data1) } >ram - .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } - /* We want the small data sections together, so single-instruction offsets - can access them all, and initialized data all before uninitialized, so - we can shorten the on-disk segment size. */ - .sdata : - { - __SDATA_BEGIN__ = .; - *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) - *(.sdata .sdata.* .gnu.linkonce.s.*) - } >ram - _edata = .; PROVIDE (edata = .); - . = .; - __bss_start = .; - .sbss : - { - *(.dynsbss) - *(.sbss .sbss.* .gnu.linkonce.sb.*) - *(.scommon) - } >ram - .bss : - { - *(.dynbss) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - /* Align here to ensure that the .bss section occupies space up to - _end. Align after .bss to ensure correct alignment even if the - .bss section disappears because there are no input sections. - FIXME: Why do we need it? When there is no .bss section, we do not - pad the .data section. */ - . = ALIGN(. != 0 ? 32 / 8 : 1); - } >ram - . = ALIGN(32 / 8); - . = SEGMENT_START("ldata-segment", .); - . = ALIGN(32 / 8); - __bss_end = .; - __global_pointer$ = MIN(__SDATA_BEGIN__ + 0x800, - MAX(__DATA_BEGIN__ + 0x800, __bss_end - 0x800)); - _end = .; PROVIDE (end = .); - . = DATA_SEGMENT_END (.); + .text : { + . = ALIGN(4); + *(.text) + *(.text.*) + } > ram - /* Heap grows upward towards end of ram */ - .heap : ALIGN(16) - { - PROVIDE(__heap_start = .); - /* If end of ram is not 16-byte aligned, align to previous 16-byte - boundary */ - PROVIDE(__heap_end = ALIGN(ORIGIN(ram) + LENGTH(ram) - __heap_start - 15, 16)); - . = __heap_end; - } >ram + .rodata : { + . = ALIGN(4); + /* Small RO data before large RO data */ + *(.srodata) + *(.srodata.*) + *(.rodata); + *(.rodata.*) + } > ram - /* Stack grows downward from end of ram */ - .stack (__heap_start) : ALIGN(16) /* this is a requirement of the ABI(?) */ - { - PROVIDE(__stack_start = __heap_start); - . = __heap_end; - PROVIDE(__stack_end = .); - } >ram + .data : { + . = ALIGN(4); + /* Small data before large data */ + *(.sdata) + *(.sdata.*) + *(.data); + *(.data.*) + } > ram - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } - .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } - /* DWARF 3 */ - .debug_pubtypes 0 : { *(.debug_pubtypes) } - .debug_ranges 0 : { *(.debug_ranges) } - /* DWARF Extension. */ - .debug_macro 0 : { *(.debug_macro) } - .debug_addr 0 : { *(.debug_addr) } - .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } - /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } -} + .bss : + { + . = ALIGN(4); + _bss_start = .; + /* Small BSS before large BSS */ + *(.sbss) + *(.sbss.*) + *(.bss) + *(.bss.*) + *(COMMON) + _bss_end = .; + } > ram + /* ensure there is enough room for stack */ + .stack (NOLOAD): { + . = ALIGN(4); + . = . + _min_stack ; + . = ALIGN(4); + stack = . ; + _stack = . ; + } > stack +} diff --git a/cv32e40p/bsp/link_corev-dv.ld b/cv32e40p/bsp/link_corev-dv.ld deleted file mode 120000 index 6d62cc1..0000000 --- a/cv32e40p/bsp/link_corev-dv.ld +++ /dev/null @@ -1 +0,0 @@ -link.ld \ No newline at end of file diff --git a/cv32e40p/bsp/simple_system_common.c b/cv32e40p/bsp/simple_system_common.c new file mode 100644 index 0000000..e0d48d1 --- /dev/null +++ b/cv32e40p/bsp/simple_system_common.c @@ -0,0 +1,191 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "simple_system_common.h" +#include "tinyprintf.h" + + +void puthex(uint32_t h) { + int cur_digit; + // Iterate through h taking top 4 bits each time and outputting ASCII of hex + // digit for those 4 bits + for (int i = 0; i < 8; i++) { + cur_digit = h >> 28; + + if (cur_digit < 10) + putchar('0' + cur_digit); + else + putchar('A' - 10 + cur_digit); + + h <<= 4; + } +} + + +void sim_halt() { DEV_WRITE(MMADDR_EXIT, 1); } + +void pcount_reset() { + asm volatile( + "csrw minstret, x0\n" + "csrw mcycle, x0\n" + "csrw mhpmcounter3, x0\n" + "csrw mhpmcounter4, x0\n" + "csrw mhpmcounter5, x0\n" + "csrw mhpmcounter6, x0\n" + "csrw mhpmcounter7, x0\n" + "csrw mhpmcounter8, x0\n" + "csrw mhpmcounter9, x0\n" + "csrw mhpmcounter10, x0\n" + "csrw mhpmcounter11, x0\n" + "csrw mhpmcounter12, x0\n" + "csrw mhpmcounter13, x0\n" + "csrw mhpmcounter14, x0\n" + "csrw mhpmcounter15, x0\n" + "csrw mhpmcounter16, x0\n" + "csrw mhpmcounter17, x0\n" + "csrw mhpmcounter18, x0\n" + "csrw mhpmcounter19, x0\n" + "csrw mhpmcounter20, x0\n" + "csrw mhpmcounter21, x0\n" + "csrw mhpmcounter22, x0\n" + "csrw mhpmcounter23, x0\n" + "csrw mhpmcounter24, x0\n" + "csrw mhpmcounter25, x0\n" + "csrw mhpmcounter26, x0\n" + "csrw mhpmcounter27, x0\n" + "csrw mhpmcounter28, x0\n" + "csrw mhpmcounter29, x0\n" + "csrw mhpmcounter30, x0\n" + "csrw mhpmcounter31, x0\n" + "csrw minstreth, x0\n" + "csrw mcycleh, x0\n" + "csrw mhpmcounter3h, x0\n" + "csrw mhpmcounter4h, x0\n" + "csrw mhpmcounter5h, x0\n" + "csrw mhpmcounter6h, x0\n" + "csrw mhpmcounter7h, x0\n" + "csrw mhpmcounter8h, x0\n" + "csrw mhpmcounter9h, x0\n" + "csrw mhpmcounter10h, x0\n" + "csrw mhpmcounter11h, x0\n" + "csrw mhpmcounter12h, x0\n" + "csrw mhpmcounter13h, x0\n" + "csrw mhpmcounter14h, x0\n" + "csrw mhpmcounter15h, x0\n" + "csrw mhpmcounter16h, x0\n" + "csrw mhpmcounter17h, x0\n" + "csrw mhpmcounter18h, x0\n" + "csrw mhpmcounter19h, x0\n" + "csrw mhpmcounter20h, x0\n" + "csrw mhpmcounter21h, x0\n" + "csrw mhpmcounter22h, x0\n" + "csrw mhpmcounter23h, x0\n" + "csrw mhpmcounter24h, x0\n" + "csrw mhpmcounter25h, x0\n" + "csrw mhpmcounter26h, x0\n" + "csrw mhpmcounter27h, x0\n" + "csrw mhpmcounter28h, x0\n" + "csrw mhpmcounter29h, x0\n" + "csrw mhpmcounter30h, x0\n" + "csrw mhpmcounter31h, x0\n"); +} + +unsigned int get_mepc() { + uint32_t result; + __asm__ volatile("csrr %0, mepc;" : "=r"(result)); + return result; +} + +unsigned int get_mcause() { + uint32_t result; + __asm__ volatile("csrr %0, mcause;" : "=r"(result)); + return result; +} + +unsigned int get_mtval() { + uint32_t result; + __asm__ volatile("csrr %0, mtval;" : "=r"(result)); + return result; +} + +void simple_exc_handler(void) { + #if 0 + volatile register int a7 asm("a7"); + + // Check if A7 equals 93 + //https://jborza.com/post/2021-05-11-riscv-linux-syscalls/ + if (a7 == 93) { +#else + int result; + asm volatile ("mv %0, a7" : "=r"(result)); + // Check if A7 equals 93 + //https://jborza.com/post/2021-05-11-riscv-linux-syscalls/ + if (result == 93) { +#endif + printf("exit()\n"); + printf("======\n"); + }else{ + printf("EXCEPTION!!!\n"); + printf("============\n"); + printf("MEPC: 0x"); + puthex(get_mepc()); + printf("\nMCAUSE: 0x"); + puthex(get_mcause()); + printf("\nMTVAL: 0x"); + puthex(get_mtval()); + putchar('\n'); + } + sim_halt(); + + while(1); +} + +volatile uint64_t time_elapsed; +uint64_t time_increment; + +inline static void increment_timecmp(uint64_t time_base) { + uint64_t current_time = timer_read(); + current_time += time_base; + timecmp_update(current_time); +} + +void timer_enable(uint64_t time_base) { + time_elapsed = 0; + time_increment = time_base; + // Set timer values + increment_timecmp(time_base); + // enable timer interrupt + asm volatile("csrs mie, %0\n" : : "r"(0x80)); + // enable global interrupt + asm volatile("csrs mstatus, %0\n" : : "r"(0x8)); +} + +void timer_disable(void) { asm volatile("csrc mie, %0\n" : : "r"(0x80)); } + +uint64_t timer_read(void) { + uint32_t current_timeh; + uint32_t current_time; + // check if time overflowed while reading and try again + do { + current_timeh = DEV_READ(TIMER_BASE + TIMER_MTIMEH, 0); + current_time = DEV_READ(TIMER_BASE + TIMER_MTIME, 0); + } while (current_timeh != DEV_READ(TIMER_BASE + TIMER_MTIMEH, 0)); + uint64_t final_time = ((uint64_t)current_timeh << 32) | current_time; + return final_time; +} + +void timecmp_update(uint64_t new_time) { + DEV_WRITE(TIMER_BASE + TIMER_MTIMECMP, -1); + DEV_WRITE(TIMER_BASE + TIMER_MTIMECMPH, new_time >> 32); + DEV_WRITE(TIMER_BASE + TIMER_MTIMECMP, new_time); +} + +uint64_t get_elapsed_time(void) { return time_elapsed; } + +void simple_timer_handler(void) __attribute__((interrupt)); + +void simple_timer_handler(void) { + increment_timecmp(time_increment); + time_elapsed++; +} diff --git a/cv32e40p/bsp/simple_system_common.h b/cv32e40p/bsp/simple_system_common.h new file mode 100644 index 0000000..650ea6b --- /dev/null +++ b/cv32e40p/bsp/simple_system_common.h @@ -0,0 +1,116 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SIMPLE_SYSTEM_COMMON_H__ + +#include + +#include "simple_system_regs.h" + +#define DEV_WRITE(addr, val) (*((volatile uint32_t *)(addr)) = val) +#define DEV_READ(addr, val) (*((volatile uint32_t *)(addr))) +#define PCOUNT_READ(name, dst) asm volatile("csrr %0, " #name ";" : "=r"(dst)) + +extern void _putcf (void * unused, char c); +/** + * Writes character to simulator out log. Signature matches c stdlib function + * of the same name. + * + * @param c Character to output + * @returns Character output (never fails so no EOF ever returned) + */ +int putchar(char c); + +/** + * Writes string to simulator out log. Signature matches c stdlib function of + * the same name. + * + * @param str String to output + * @returns 0 always (never fails so no error) + */ +int puts(const char *str); + +/** + * Writes ASCII hex representation of number to simulator out log. + * + * @param h Number to output in hex + */ +void puthex(uint32_t h); + +/** + * Immediately halts the simulation + */ +void sim_halt(); + +/** + * Enables/disables performance counters. This effects mcycle and minstret as + * well as the mhpmcounterN counters. + * + * @param enable if non-zero enables, otherwise disables + */ +static inline void pcount_enable(int enable) { + // Note cycle is disabled with everything else + unsigned int inhibit_val = enable ? 0x0 : 0xFFFFFFFF; + // CSR 0x320 was called `mucounteren` in the privileged spec v1.9.1, it was + // then dropped in v1.10, and then re-added in v1.11 with the name + // `mcountinhibit`. Unfortunately, the version of binutils we use only allows + // the old name, and LLVM only supports the new name (though this is changed + // on trunk to support both), so we use the numeric value here for maximum + // compatibility. + asm volatile("csrw 0x320, %0\n" : : "r"(inhibit_val)); +} + +/** + * Resets all performance counters. This effects mcycle and minstret as well + * as the mhpmcounterN counters. + */ +void pcount_reset(); + +/** + * Enables timer interrupt + * + * @param time_base Number of time ticks to count before interrupt + */ +void timer_enable(uint64_t time_base); + +/** + * Returns current mtime value + */ +uint64_t timer_read(void); + +/** + * Set a new timer value + * + * @param new_time New value for time + */ +void timecmp_update(uint64_t new_time); + +/** + * Disables timer interrupt + */ +void timer_disable(void); + +/** + * Returns current global time value + */ +uint64_t get_elapsed_time(void); + +/** + * Enables/disables the instruction cache. This has no effect on Ibex + * configurations that do not have an instruction cache and in particular is + * safe to execute on those configurations. + * + * @param enable if non-zero enables, otherwise disables + */ +static inline void icache_enable(int enable) { + if (enable) { + // Set icache enable bit in CPUCTRLSTS + asm volatile("csrs 0x7c0, 1"); + } else { + // Clear icache enable bit in CPUCTRLSTS + asm volatile("csrc 0x7c0, 1"); + } +} + +#endif diff --git a/cv32e40p/bsp/simple_system_regs.h b/cv32e40p/bsp/simple_system_regs.h new file mode 100644 index 0000000..47b3929 --- /dev/null +++ b/cv32e40p/bsp/simple_system_regs.h @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SIMPLE_SYSTEM_REGS_H__ +#define SIMPLE_SYSTEM_REGS_H__ + +#define MMADDR_PRINT 0x10000000 + //localparam int MMADDR_TESTSTATUS = 32'h2000_0000; +#define MMADDR_EXIT 20000004 + +//#define SIM_CTRL_BASE 0x20000 +//#define SIM_CTRL_OUT 0x0 +//#define SIM_CTRL_CTRL 0x8 + +#define TIMER_BASE 0x30000 +#define TIMER_MTIME 0x0 +#define TIMER_MTIMEH 0x4 +#define TIMER_MTIMECMP 0x8 +#define TIMER_MTIMECMPH 0xC + + + +#endif // SIMPLE_SYSTEM_REGS_H__ diff --git a/cv32e40p/bsp/startup.c b/cv32e40p/bsp/startup.c new file mode 100644 index 0000000..52f1fa9 --- /dev/null +++ b/cv32e40p/bsp/startup.c @@ -0,0 +1,28 @@ + +#define TINYPRINTF_DEFINE_TFP_PRINTF 1 +#define TINYPRINTF_DEFINE_TFP_SPRINTF 1 +#include "tinyprintf.h" + +#include "simple_system_common.h" + + +extern int main(int argc, char *argv[]); + + + + void _putcf (void * unused, char c) { + (int)unused; + DEV_WRITE(MMADDR_PRINT, (unsigned char)c); +} + +int putchar(char c){ + _putcf (0, c); + return 1; +} + + + +void startup(){ + init_printf(0, _putcf); + main(0, 0); +} \ No newline at end of file diff --git a/cv32e40p/bsp/syscalls.c b/cv32e40p/bsp/syscalls.c deleted file mode 100644 index 36ffac3..0000000 --- a/cv32e40p/bsp/syscalls.c +++ /dev/null @@ -1,376 +0,0 @@ -/* An extremely minimalist syscalls.c for newlib - * Based on riscv newlib libgloss/riscv/sys_*.c - * - * Copyright 2019 Claire Wolf - * Copyright 2019 ETH Zürich and University of Bologna - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#undef errno -extern int errno; - -/* write to this reg for outputting strings */ -#define STDOUT_REG 0x10000000 -/* write test result of program to this reg */ -#define RESULT_REG 0x20000000 -/* write exit value of program to this reg */ -#define EXIT_REG 0x20000004 - -#define STDOUT_FILENO 1 - -/* It turns out that older newlib versions use different symbol names which goes - * against newlib recommendations. Anyway this is fixed in later version. - */ -#if __NEWLIB__ <= 2 && __NEWLIB_MINOR__ <= 5 -#define _sbrk sbrk -#define _write write -#define _close close -#define _lseek lseek -#define _read read -#define _fstat fstat -#define _isatty isatty -#endif -/* Upstream newlib now defines this in libgloss/riscv/internal_syscall.h. */ -long -__syscall_error(long a0) -{ - errno = -a0; - return -1; -} - -void unimplemented_syscall() -{ - const char *p = "BSP: Unimplemented system call called!\n"; - while (*p) - *(volatile int *)STDOUT_REG = *(p++); -} - -int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) -{ - errno = ENOSYS; - return -1; -} - -int _access(const char *file, int mode) -{ - errno = ENOSYS; - return -1; -} - -int _chdir(const char *path) -{ - errno = ENOSYS; - return -1; -} - -int _chmod(const char *path, mode_t mode) -{ - errno = ENOSYS; - return -1; -} - -int _chown(const char *path, uid_t owner, gid_t group) -{ - errno = ENOSYS; - return -1; -} - -int _close(int file) -{ - return -1; -} - -int _execve(const char *name, char *const argv[], char *const env[]) -{ - errno = ENOMEM; - return -1; -} - -void _exit(int exit_status) -{ - *(volatile int *)EXIT_REG = exit_status; - asm volatile("wfi"); - /* _exit should not return */ - while (1) {}; -} - -int _faccessat(int dirfd, const char *file, int mode, int flags) -{ - errno = ENOSYS; - return -1; -} - -int _fork(void) -{ - errno = EAGAIN; - return -1; -} - -int _fstat(int file, struct stat *st) -{ - st->st_mode = S_IFCHR; - return 0; - // errno = -ENOSYS; - // return -1; -} - -int _fstatat(int dirfd, const char *file, struct stat *st, int flags) -{ - errno = ENOSYS; - return -1; -} - -int _ftime(struct timeb *tp) -{ - errno = ENOSYS; - return -1; -} - -char *_getcwd(char *buf, size_t size) -{ - errno = -ENOSYS; - return NULL; -} - -int _getpid() -{ - return 1; -} - -int _gettimeofday(struct timeval *tp, void *tzp) -{ - errno = -ENOSYS; - return -1; -} - -int _isatty(int file) -{ - return (file == STDOUT_FILENO); -} - -int _kill(int pid, int sig) -{ - errno = EINVAL; - return -1; -} - -int _link(const char *old_name, const char *new_name) -{ - errno = EMLINK; - return -1; -} - -off_t _lseek(int file, off_t ptr, int dir) -{ - return 0; -} - -int _lstat(const char *file, struct stat *st) -{ - errno = ENOSYS; - return -1; -} - -int _open(const char *name, int flags, int mode) -{ - return -1; -} - -int _openat(int dirfd, const char *name, int flags, int mode) -{ - errno = ENOSYS; - return -1; -} - -ssize_t _read(int file, void *ptr, size_t len) -{ - return 0; -} - -int _stat(const char *file, struct stat *st) -{ - st->st_mode = S_IFCHR; - return 0; - // errno = ENOSYS; - // return -1; -} - -long _sysconf(int name) -{ - - return -1; -} - -clock_t _times(struct tms *buf) -{ - return -1; -} - -int _unlink(const char *name) -{ - errno = ENOENT; - return -1; -} - -int _utime(const char *path, const struct utimbuf *times) -{ - errno = ENOSYS; - return -1; -} - -int _wait(int *status) -{ - errno = ECHILD; - return -1; -} - -ssize_t _write(int file, const void *ptr, size_t len) -{ - const char *cptr = (char *)ptr; - if (file != STDOUT_FILENO) - { - errno = ENOSYS; - return -1; - } - - const void *eptr = cptr + len; - while (cptr != eptr) - *(volatile int *)STDOUT_REG = *cptr++; - return len; -} - -extern char __heap_start[]; -extern char __heap_end[]; -static char *brk = __heap_start; - -int _brk(void *addr) -{ - brk = addr; - return 0; -} - -void *_sbrk(ptrdiff_t incr) -{ - char *old_brk = brk; - register long sp asm("sp"); - - char *new_brk = brk += incr; - if (new_brk < (char *) sp && new_brk < __heap_end) - { - brk = new_brk; - - return old_brk; - } - else - { - errno = ENOMEM; - return (void *) -1; - } -} - -void handle_syscall (long a0, - long a1, - long a2, - long a3, - __attribute__((unused)) long a4, - __attribute__((unused)) long a5, - __attribute__((unused)) long a6, - long a7) { - #ifdef __riscv_32e - register long syscall_id asm("t0"); - #else - long syscall_id = a7; - #endif - - switch (syscall_id) { - case SYS_exit: - _exit (a0); - break; - case SYS_read: - _read (a0, (void *) a1, a2); - break; - case SYS_write: - _write (a0, (const void *) a1, a2); - break; - case SYS_getpid: - _getpid (); - break; - case SYS_kill: - _kill (a0, a1); - break; - case SYS_open: - _open ((const char *) a0, a1, a2); - break; - case SYS_openat: - _openat (a0, (const char *) a1, a2, a3); - break; - case SYS_close: - _close (a0); - break; - case SYS_lseek: - _lseek (a0, a1, a2); - break; - case SYS_brk: - _brk ((void *) a0); - break; - case SYS_link: - _link ((const char *) a0, (const char *) a1); - break; - case SYS_unlink: - _unlink ((const char *) a0); - break; - case SYS_chdir: - _chdir ((const char *) a0); - break; - case SYS_getcwd: - _getcwd ((char *) a0, a1); - break; - case SYS_stat: - _stat ((const char *) a0, (struct stat *) a1); - break; - case SYS_fstat: - _fstat (a0, (struct stat *) a1); - break; - case SYS_lstat: - _lstat ((const char *) a0, (struct stat *) a1); - break; - case SYS_fstatat: - _fstatat (a0, (const char *) a1, (struct stat *) a2, a3); - break; - case SYS_access: - _access ((const char *) a0, a1); - break; - case SYS_faccessat: - _faccessat (a0, (const char *) a1, a2, a3); - break; - case SYS_gettimeofday: - _gettimeofday ((struct timeval *) a0, (void *) a1); - break; - case SYS_times: - _times ((struct tms *) a0); - break; - default: - unimplemented_syscall (); - break; - } -} diff --git a/cv32e40p/bsp/tinyprintf.c b/cv32e40p/bsp/tinyprintf.c new file mode 100644 index 0000000..00abf80 --- /dev/null +++ b/cv32e40p/bsp/tinyprintf.c @@ -0,0 +1,521 @@ +/* +File: tinyprintf.c + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "tinyprintf.h" + + +/* + * Configuration + */ + +/* Enable long int support */ +#define PRINTF_LONG_SUPPORT + +/* Enable long long int support (implies long int support) */ +//#define PRINTF_LONG_LONG_SUPPORT + +/* Enable %z (size_t) support */ +#define PRINTF_SIZE_T_SUPPORT + +/* + * Configuration adjustments + */ +#ifdef PRINTF_SIZE_T_SUPPORT +#include +#endif + +#ifdef PRINTF_LONG_LONG_SUPPORT +# define PRINTF_LONG_SUPPORT +#endif + +/* __SIZEOF___ defined at least by gcc */ +#ifdef __SIZEOF_POINTER__ +# define SIZEOF_POINTER __SIZEOF_POINTER__ +#endif +#ifdef __SIZEOF_LONG_LONG__ +# define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__ +#endif +#ifdef __SIZEOF_LONG__ +# define SIZEOF_LONG __SIZEOF_LONG__ +#endif +#ifdef __SIZEOF_INT__ +# define SIZEOF_INT __SIZEOF_INT__ +#endif + +#ifdef __GNUC__ +# define _TFP_GCC_NO_INLINE_ __attribute__ ((noinline)) +#else +# define _TFP_GCC_NO_INLINE_ +#endif + +/* + * Implementation + */ +struct param { + char lz:1; /**< Leading zeros */ + char alt:1; /**< alternate form */ + char uc:1; /**< Upper case (for base16 only) */ + char align_left:1; /**< 0 == align right (default), 1 == align left */ + unsigned int width; /**< field width */ + char sign; /**< The sign to display (if any) */ + unsigned int base; /**< number base (e.g.: 8, 10, 16) */ + char *bf; /**< Buffer to output */ +}; + + +#ifdef PRINTF_LONG_LONG_SUPPORT +static void _TFP_GCC_NO_INLINE_ ulli2a( + unsigned long long int num, struct param *p) +{ + int n = 0; + unsigned long long int d = 1; + char *bf = p->bf; + while (num / d >= p->base) + d *= p->base; + while (d != 0) { + int dgt = num / d; + num %= d; + d /= p->base; + if (n || dgt > 0 || d == 0) { + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void lli2a(long long int num, struct param *p) +{ + if (num < 0) { + num = -num; + p->sign = '-'; + } + ulli2a(num, p); +} +#endif + +#ifdef PRINTF_LONG_SUPPORT +static void uli2a(unsigned long int num, struct param *p) +{ + int n = 0; + unsigned long int d = 1; + char *bf = p->bf; + while (num / d >= p->base) + d *= p->base; + while (d != 0) { + int dgt = num / d; + num %= d; + d /= p->base; + if (n || dgt > 0 || d == 0) { + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void li2a(long num, struct param *p) +{ + if (num < 0) { + num = -num; + p->sign = '-'; + } + uli2a(num, p); +} +#endif + +static void ui2a(unsigned int num, struct param *p) +{ + int n = 0; + unsigned int d = 1; + char *bf = p->bf; + while (num / d >= p->base) + d *= p->base; + while (d != 0) { + int dgt = num / d; + num %= d; + d /= p->base; + if (n || dgt > 0 || d == 0) { + *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; +} + +static void i2a(int num, struct param *p) +{ + if (num < 0) { + num = -num; + p->sign = '-'; + } + ui2a(num, p); +} + +static int a2d(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else + return -1; +} + +static char a2u(char ch, const char **src, int base, unsigned int *nump) +{ + const char *p = *src; + unsigned int num = 0; + int digit; + while ((digit = a2d(ch)) >= 0) { + if (digit > base) + break; + num = num * base + digit; + ch = *p++; + } + *src = p; + *nump = num; + return ch; +} + +static void putchw(void *putp, putcf putf, struct param *p) +{ + char ch; + int n = p->width; + char *bf = p->bf; + + /* Number of filling characters */ + while (*bf++ && n > 0) + n--; + if (p->sign) + n--; + if (p->alt && p->base == 16) + n -= 2; + else if (p->alt && p->base == 8) + n--; + + /* Fill with space to align to the right, before alternate or sign */ + if (!p->lz && !p->align_left) { + while (n-- > 0) + putf(putp, ' '); + } + + /* print sign */ + if (p->sign) + putf(putp, p->sign); + + /* Alternate */ + if (p->alt && p->base == 16) { + putf(putp, '0'); + putf(putp, (p->uc ? 'X' : 'x')); + } else if (p->alt && p->base == 8) { + putf(putp, '0'); + } + + /* Fill with zeros, after alternate or sign */ + if (p->lz) { + while (n-- > 0) + putf(putp, '0'); + } + + /* Put actual buffer */ + bf = p->bf; + while ((ch = *bf++)) + putf(putp, ch); + + /* Fill with space to align to the left, after string */ + if (!p->lz && p->align_left) { + while (n-- > 0) + putf(putp, ' '); + } +} + +void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) +{ + struct param p; +#ifdef PRINTF_LONG_SUPPORT + char bf[23]; /* long = 64b on some architectures */ +#else + char bf[12]; /* int = 32b on some architectures */ +#endif + char ch; + p.bf = bf; + + while ((ch = *(fmt++))) { + if (ch != '%') { + putf(putp, ch); + } else { +#ifdef PRINTF_LONG_SUPPORT + char lng = 0; /* 1 for long, 2 for long long */ +#endif + /* Init parameter struct */ + p.lz = 0; + p.alt = 0; + p.width = 0; + p.align_left = 0; + p.sign = 0; + + /* Flags */ + while ((ch = *(fmt++))) { + switch (ch) { + case '-': + p.align_left = 1; + continue; + case '0': + p.lz = 1; + continue; + case '#': + p.alt = 1; + continue; + default: + break; + } + break; + } + + /* Width */ + if (ch >= '0' && ch <= '9') { + ch = a2u(ch, &fmt, 10, &(p.width)); + } + + /* We accept 'x.y' format but don't support it completely: + * we ignore the 'y' digit => this ignores 0-fill + * size and makes it == width (ie. 'x') */ + if (ch == '.') { + p.lz = 1; /* zero-padding */ + /* ignore actual 0-fill size: */ + do { + ch = *(fmt++); + } while ((ch >= '0') && (ch <= '9')); + } + +#ifdef PRINTF_SIZE_T_SUPPORT +# ifdef PRINTF_LONG_SUPPORT + if (ch == 'z') { + ch = *(fmt++); + if (sizeof(size_t) == sizeof(unsigned long int)) + lng = 1; +# ifdef PRINTF_LONG_LONG_SUPPORT + else if (sizeof(size_t) == sizeof(unsigned long long int)) + lng = 2; +# endif + } else +# endif +#endif + +#ifdef PRINTF_LONG_SUPPORT + if (ch == 'l') { + ch = *(fmt++); + lng = 1; +#ifdef PRINTF_LONG_LONG_SUPPORT + if (ch == 'l') { + ch = *(fmt++); + lng = 2; + } +#endif + } +#endif + switch (ch) { + case 0: + goto abort; + case 'u': + p.base = 10; +#ifdef PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_LONG_SUPPORT + if (2 == lng) + ulli2a(va_arg(va, unsigned long long int), &p); + else +#endif + if (1 == lng) + uli2a(va_arg(va, unsigned long int), &p); + else +#endif + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'd': + case 'i': + p.base = 10; +#ifdef PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_LONG_SUPPORT + if (2 == lng) + lli2a(va_arg(va, long long int), &p); + else +#endif + if (1 == lng) + li2a(va_arg(va, long int), &p); + else +#endif + i2a(va_arg(va, int), &p); + putchw(putp, putf, &p); + break; +#ifdef SIZEOF_POINTER + case 'p': + p.alt = 1; +# if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT + lng = 0; +# elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG + lng = 1; +# elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG + lng = 2; +# endif +#endif + case 'x': + case 'X': + p.base = 16; + p.uc = (ch == 'X')?1:0; +#ifdef PRINTF_LONG_SUPPORT +#ifdef PRINTF_LONG_LONG_SUPPORT + if (2 == lng) + ulli2a(va_arg(va, unsigned long long int), &p); + else +#endif + if (1 == lng) + uli2a(va_arg(va, unsigned long int), &p); + else +#endif + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'o': + p.base = 8; + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'c': + putf(putp, (char)(va_arg(va, int))); + break; + case 's': + p.bf = va_arg(va, char *); + putchw(putp, putf, &p); + p.bf = bf; + break; + case '%': + putf(putp, ch); + default: + break; + } + } + } + abort:; +} + +#if TINYPRINTF_DEFINE_TFP_PRINTF +static putcf stdout_putf; +static void *stdout_putp; + +void init_printf(void *putp, putcf putf) +{ + stdout_putf = putf; + stdout_putp = putp; +} + +void tfp_printf(char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + tfp_format(stdout_putp, stdout_putf, fmt, va); + va_end(va); +} +#endif + +#if TINYPRINTF_DEFINE_TFP_SPRINTF +struct _vsnprintf_putcf_data +{ + size_t dest_capacity; + char *dest; + size_t num_chars; +}; + +static void _vsnprintf_putcf(void *p, char c) +{ + struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p; + if (data->num_chars < data->dest_capacity) + data->dest[data->num_chars] = c; + data->num_chars ++; +} + +int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + struct _vsnprintf_putcf_data data; + + if (size < 1) + return 0; + + data.dest = str; + data.dest_capacity = size-1; + data.num_chars = 0; + tfp_format(&data, _vsnprintf_putcf, format, ap); + + if (data.num_chars < data.dest_capacity) + data.dest[data.num_chars] = '\0'; + else + data.dest[data.dest_capacity] = '\0'; + + return data.num_chars; +} + +int tfp_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + retval = tfp_vsnprintf(str, size, format, ap); + va_end(ap); + return retval; +} + +struct _vsprintf_putcf_data +{ + char *dest; + size_t num_chars; +}; + +static void _vsprintf_putcf(void *p, char c) +{ + struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p; + data->dest[data->num_chars++] = c; +} + +int tfp_vsprintf(char *str, const char *format, va_list ap) +{ + struct _vsprintf_putcf_data data; + data.dest = str; + data.num_chars = 0; + tfp_format(&data, _vsprintf_putcf, format, ap); + data.dest[data.num_chars] = '\0'; + return data.num_chars; +} + +int tfp_sprintf(char *str, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + retval = tfp_vsprintf(str, format, ap); + va_end(ap); + return retval; +} +#endif \ No newline at end of file diff --git a/cv32e40p/bsp/tinyprintf.h b/cv32e40p/bsp/tinyprintf.h new file mode 100644 index 0000000..a7dc2bf --- /dev/null +++ b/cv32e40p/bsp/tinyprintf.h @@ -0,0 +1,186 @@ +/* +File: tinyprintf.h + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This library is really just two files: 'tinyprintf.h' and 'tinyprintf.c'. + +They provide a simple and small (+400 loc) printf functionality to +be used in embedded systems. + +I've found them so useful in debugging that I do not bother with a +debugger at all. + +They are distributed in source form, so to use them, just compile them +into your project. + +Two printf variants are provided: printf and the 'sprintf' family of +functions ('snprintf', 'sprintf', 'vsnprintf', 'vsprintf'). + +The formats supported by this implementation are: +'c' 'd' 'i' 'o' 'p' 'u' 's' 'x' 'X'. + +Zero padding and field width are also supported. + +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined, then +the long specifier is also supported. Note that this will pull in some +long math routines (pun intended!) and thus make your executable +noticeably longer. Likewise with 'PRINTF_LONG_LONG_SUPPORT' for the +long long specifier, and with 'PRINTF_SIZE_T_SUPPORT' for the size_t +specifier. + +The memory footprint of course depends on the target CPU, compiler and +compiler options, but a rough guesstimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your mileage may vary. By hacking the source code you can +get rid of some hundred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. + +To use the printf, you need to supply your own character output function, +something like : + +void putc ( void* p, char c) +{ + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; +} + +Before you can call printf, you need to initialize it to use your +character output function with something like: + +init_printf(NULL,putc); + +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything really) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). + +The code is re-entrant, except for the 'init_printf' function, so it is safe +to call it from interrupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! + +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf' when 'TINYPRINTF_OVERRIDE_LIBC' is set +(default). Setting it to 0 makes it possible to use them along with +'stdio.h' printf's in a single source file. When +'TINYPRINTF_OVERRIDE_LIBC' is set, please note that printf/sprintf are +not function-like macros, so if you have variables or struct members +with these names, things will explode in your face. Without variadic +macros this is the best we can do to wrap these function. If it is a +problem, just give up the macros and use the functions directly, or +rename them. + +It is also possible to avoid defining tfp_printf and/or tfp_sprintf by +clearing 'TINYPRINTF_DEFINE_TFP_PRINTF' and/or +'TINYPRINTF_DEFINE_TFP_SPRINTF' to 0. This allows for example to +export only tfp_format, which is at the core of all the other +functions. + +For further details see source code. + +regs Kusti, 23.10.2004 +*/ + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ + +#include + +/* Global configuration */ + +/* Set this to 0 if you do not want to provide tfp_printf */ +#ifndef TINYPRINTF_DEFINE_TFP_PRINTF +# define TINYPRINTF_DEFINE_TFP_PRINTF 1 +#endif + +/* Set this to 0 if you do not want to provide + tfp_sprintf/snprintf/vsprintf/vsnprintf */ +#ifndef TINYPRINTF_DEFINE_TFP_SPRINTF +# define TINYPRINTF_DEFINE_TFP_SPRINTF 1 +#endif + +/* Set this to 0 if you do not want tfp_printf and + tfp_{vsn,sn,vs,s}printf to be also available as + printf/{vsn,sn,vs,s}printf */ +#ifndef TINYPRINTF_OVERRIDE_LIBC +# define TINYPRINTF_OVERRIDE_LIBC 1 +#endif + +/* Optional external types dependencies */ + +#if TINYPRINTF_DEFINE_TFP_SPRINTF +# include /* size_t */ +#endif + +/* Declarations */ + +#ifdef __GNUC__ +# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx) \ + __attribute__((format (printf, fmt_idx, arg1_idx))) +#else +# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*putcf) (void *, char); + +/* + 'tfp_format' really is the central function for all tinyprintf. For + each output character after formatting, the 'putf' callback is + called with 2 args: + - an arbitrary void* 'putp' param defined by the user and + passed unmodified from 'tfp_format', + - the character. + The 'tfp_printf' and 'tfp_sprintf' functions simply define their own + callback and pass to it the right 'putp' it is expecting. +*/ +void tfp_format(void *putp, putcf putf, const char *fmt, va_list va); + +#if TINYPRINTF_DEFINE_TFP_SPRINTF +int tfp_vsnprintf(char *str, size_t size, const char *fmt, va_list ap); +int tfp_snprintf(char *str, size_t size, const char *fmt, ...) \ + _TFP_SPECIFY_PRINTF_FMT(3, 4); +int tfp_vsprintf(char *str, const char *fmt, va_list ap); +int tfp_sprintf(char *str, const char *fmt, ...) \ + _TFP_SPECIFY_PRINTF_FMT(2, 3); +# if TINYPRINTF_OVERRIDE_LIBC +# define vsnprintf tfp_vsnprintf +# define snprintf tfp_snprintf +# define vsprintf tfp_vsprintf +# define sprintf tfp_sprintf +# endif +#endif + +#if TINYPRINTF_DEFINE_TFP_PRINTF +void init_printf(void *putp, putcf putf); +void tfp_printf(char *fmt, ...) _TFP_SPECIFY_PRINTF_FMT(1, 2); +# if TINYPRINTF_OVERRIDE_LIBC +# define printf tfp_printf +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/cv32e40p/bsp/vectors.S b/cv32e40p/bsp/vectors.S deleted file mode 100644 index 08af412..0000000 --- a/cv32e40p/bsp/vectors.S +++ /dev/null @@ -1,54 +0,0 @@ -/* -* Copyright 2019 ETH Zürich and University of Bologna -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -.section .vectors, "ax" -.option norvc -.global vector_table - -vector_table: - j u_sw_irq_handler - j __no_irq_handler - j __no_irq_handler - j m_software_irq_handler - j __no_irq_handler - j __no_irq_handler - j __no_irq_handler - j m_timer_irq_handler - j __no_irq_handler - j __no_irq_handler - j __no_irq_handler - j m_external_irq_handler - j __no_irq_handler - j __no_irq_handler - j __no_irq_handler - j __no_irq_handler - j m_fast0_irq_handler - j m_fast1_irq_handler - j m_fast2_irq_handler - j m_fast3_irq_handler - j m_fast4_irq_handler - j m_fast5_irq_handler - j m_fast6_irq_handler - j m_fast7_irq_handler - j m_fast8_irq_handler - j m_fast9_irq_handler - j m_fast10_irq_handler - j m_fast11_irq_handler - j m_fast12_irq_handler - j m_fast13_irq_handler - j m_fast14_irq_handler - j m_fast15_irq_handler - diff --git a/cv32e40p/tests/programs/custom/hello-world/hello-world.c b/cv32e40p/tests/programs/custom/hello-world/hello-world.c index be4a257..4dab926 100644 --- a/cv32e40p/tests/programs/custom/hello-world/hello-world.c +++ b/cv32e40p/tests/programs/custom/hello-world/hello-world.c @@ -24,8 +24,9 @@ ******************************************************************************* */ -#include +//#include #include +#include "tinyprintf.h" //FIXME: the core tb does not have the ability to select PULP/NO_PULP at // compile-time, so we set a default MISA to NO_PULP value. This diff --git a/mk/Common.mk b/mk/Common.mk index 98f11bc..5ddf188 100644 --- a/mk/Common.mk +++ b/mk/Common.mk @@ -72,6 +72,7 @@ $(warning RISCV_CFLAGS set to $(RISCV_CFLAGS)) BSP = $(CORE_V_VERIF)/$(CV_CORE_LC)/bsp +RISCV_CFLAGS += -I $(BSP) %.hex: %.elf @echo "$(BANNER)" @@ -82,31 +83,25 @@ BSP = $(CORE_V_VERIF)/$(CV_CORE_LC)/bsp $@ $(RISCV_EXE_PREFIX)readelf -a $< > $*.readelf $(RISCV_EXE_PREFIX)objdump \ - -d \ + -fhSD \ -M no-aliases \ -M numeric \ -S \ $*.elf > $*.objdump - $(RISCV_EXE_PREFIX)objdump \ - -d \ - -S \ - -M no-aliases \ - -M numeric \ - -l \ - $*.elf | ${CORE_V_VERIF}/bin/objdump2itb - > $*.itb -# Patterned targets to generate ELF. Used only if explicit targets do not match. -# -.PRECIOUS : %.elf -# Single rule for compiling test source into an ELF file -# For directed tests, TEST_FILES gathers all of the .S and .c files in a test directory -# For corev_ tests, TEST_FILES will only point to the specific .S for the RUN_INDEX and TEST_NAME provided to make -ifeq ($(shell echo $(TEST) | head -c 6),corev_) -TEST_FILES = $(filter %.c %.S,$(wildcard $(SIM_TEST_PROGRAM_RESULTS)/$(TEST_NAME)$(OPT_RUN_INDEX_SUFFIX).S)) -else -TEST_FILES = $(filter %.c %.S,$(wildcard $(TEST_TEST_DIR)/*)) -endif + +TEST_FILES_FULL = $(filter %.c %.S,$(wildcard $(TEST_TEST_DIR)/*)) +TEST_FILES = $(notdir $(TEST_FILES_FULL)) + + +# Separate object file lists for .c and .S files +C_OBJS := $(patsubst %.c,%.o,$(filter %.c,$(TEST_FILES))) +S_OBJS := $(patsubst %.S,%.o,$(filter %.S,$(TEST_FILES))) + +# Combine them to get TEST_OBJS +TEST_OBJS := $(C_OBJS) $(S_OBJS) + # If a test defines "default_cflags" in its yaml, then it is responsible to define ALL flags # Otherwise add the default cflags in the variable CFLAGS defined above @@ -126,37 +121,11 @@ LD_LIBRARY = $(if $(wildcard $(TEST_RESULTS_LD)),-L $(SIM_TEST_PROGRAM_RESULTS) LD_FILE = $(if $(wildcard $(TEST_RESULTS_LD)),$(TEST_RESULTS_LD),$(if $(wildcard $(TEST_LD)),$(TEST_LD),$(BSP)/link.ld)) LD_LIBRARY += -L $(SIM_BSP_RESULTS) -ifeq ($(TEST_FIXED_ELF),1) -%.elf: - @echo "$(BANNER)" - @echo "* Copying fixed ELF test program to $(@)" - @echo "$(BANNER)" - mkdir -p $(SIM_TEST_PROGRAM_RESULTS) - cp $(TEST_TEST_DIR)/$(TEST).elf $@ -else -%.elf: $(TEST_FILES) bsp - mkdir -p $(SIM_TEST_PROGRAM_RESULTS) - @echo "$(BANNER)" - @echo "* Compiling test-program $@" - @echo "$(BANNER)" - $(RISCV_EXE_PREFIX)$(RISCV_CC) \ - $(CFG_CFLAGS) \ - $(TEST_CFLAGS) \ - $(RISCV_CFLAGS) \ - -I $(BSP) \ - -o $@ \ - -nostartfiles \ - -nostdlib \ - $(TEST_FILES) \ - -T $(LD_FILE) \ - $(LD_LIBRARY) \ - -lcv-verif -endif -.PHONY: hex +#.PHONY: hex # Shorthand target to only build the firmware using the hex and elf suffix rules above -hex: $(SIM_TEST_PROGRAM_RESULTS)/$(TEST_PROGRAM)$(OPT_RUN_INDEX_SUFFIX).hex +#hex: $(SIM_TEST_PROGRAM_RESULTS)/$(TEST_PROGRAM)$(OPT_RUN_INDEX_SUFFIX).hex bsp: @echo "$(BANNER)" @@ -174,7 +143,42 @@ bsp: RISCV_CFLAGS="$(RISCV_CFLAGS)" \ all +compile: + @echo "$(BANNER)" + @echo "* Compiling the test" + @echo "$(BANNER)" + mkdir -p $(SIM_BSP_RESULTS) + cp $(BSP)/Makefile $(SIM_BSP_RESULTS) + make -C $(SIM_BSP_RESULTS) \ + SRCS=$(TEST_FILES) \ + VPATH=$(TEST_TEST_DIR) \ + RISCV=$(RISCV) \ + RISCV_PREFIX=$(RISCV_PREFIX) \ + RISCV_EXE_PREFIX=$(RISCV_EXE_PREFIX) \ + RISCV_MARCH=$(RISCV_MARCH) \ + RISCV_CC=$(RISCV_CC) \ + RISCV_CFLAGS="$(RISCV_CFLAGS)" \ + compile + +%.elf: + @echo "$(BANNER)" + @echo "* Compiling the test" + @echo "$(BANNER)" + mkdir -p $(SIM_BSP_RESULTS) + cp $(BSP)/Makefile $(SIM_BSP_RESULTS) + make -C $(SIM_BSP_RESULTS) \ + APP_FILES=$(TEST_FILES) \ + VPATH=$(TEST_TEST_DIR):$(BSP) \ + RISCV=$(RISCV) \ + RISCV_PREFIX=$(RISCV_PREFIX) \ + RISCV_EXE_PREFIX=$(RISCV_EXE_PREFIX) \ + RISCV_MARCH=$(RISCV_MARCH) \ + RISCV_CC=$(RISCV_CC) \ + RISCV_CFLAGS="$(RISCV_CFLAGS)" \ + LD_FILE=$(BSP)/link.ld \ + $@ + clean_bsp: - make -C $(BSP) clean +# make -C $(BSP) clean rm -rf $(SIM_BSP_RESULTS) \ No newline at end of file diff --git a/sim/core/Makefile.verilator b/sim/core/Makefile.verilator index 2d4c839..102bcc3 100644 --- a/sim/core/Makefile.verilator +++ b/sim/core/Makefile.verilator @@ -42,7 +42,7 @@ SIM_RESULTS ?= simulation_results SIM_TEST_RESULTS = $(SIM_RESULTS)/$(TEST) SIM_RUN_RESULTS = $(SIM_TEST_RESULTS)/$(RUN_INDEX) SIM_TEST_PROGRAM_RESULTS = $(SIM_RUN_RESULTS)/test_program -SIM_BSP_RESULTS = $(CORE_V_VERIF)/$(SIM_TEST_PROGRAM_RESULTS)/bsp +SIM_BSP_RESULTS = $(CORE_V_VERIF)/sw/build/bsp # Compile compile flags for all simulators SV_CMP_FLAGS = @@ -141,12 +141,24 @@ run-test: verilate $(TEST_PROGRAM_PATH)/$(TEST)/$(TEST).hex ############################################################################### -clean-test-programs: +clean-test-programs: clean_bsp find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.o" -delete find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.hex" -delete find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.elf" -delete find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.map" -delete find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.readelf" -delete find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.objdump" -delete + find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.headers" -delete find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "corev_*.S" -delete find $(CORE_V_VERIF)/$(CV_CORE_LC)/tests/programs -name "*.itb" -delete + + + +elf: $(TEST_PROGRAM_PATH)/$(TEST)/$(TEST).elf + @echo Built $(TEST_PROGRAM_PATH)/$(TEST)/$(TEST).elf + +clean-elf: + rm -f $(TEST_PROGRAM_PATH)/$(TEST)/$(TEST).elf + +hex: $(TEST_PROGRAM_PATH)/$(TEST)/$(TEST).hex + @echo Built $(TEST_PROGRAM_PATH)/$(TEST)/$(TEST).objdump \ No newline at end of file diff --git a/sim/core/README.md b/sim/core/README.md index c57447f..07475f2 100644 --- a/sim/core/README.md +++ b/sim/core/README.md @@ -4,6 +4,13 @@ make -f Makefile.verilator verilate ``` ## Run test +### smoke test + +```sh +cd sim/core +make -f Makefile.verilator clean-test-programs +make -f Makefile.verilator run-test +``` ```sh make -f Makefile.verilator TEST=hello-world run-test ``` diff --git a/tb/core/tb_top_verilator.sv b/tb/core/tb_top_verilator.sv index 195ec6a..ef13eb0 100644 --- a/tb/core/tb_top_verilator.sv +++ b/tb/core/tb_top_verilator.sv @@ -14,7 +14,7 @@ module tb_top_verilator #(parameter INSTR_RDATA_WIDTH = 128, parameter RAM_ADDR_WIDTH = 22, - parameter BOOT_ADDR = 'h80) + parameter BOOT_ADDR = 32'h0010_0080) (input logic clk_i, input logic rst_ni, input logic fetch_enable_i, From 214df513003ef0980415318750581a4477ff59a8 Mon Sep 17 00:00:00 2001 From: darotsr Date: Mon, 30 Sep 2024 10:49:33 +0300 Subject: [PATCH 6/8] refactored cv32e40p/bsp/ --- cv32e40p/.gitignore | 1 + cv32e40p/bsp/Makefile | 7 +++---- cv32e40p/bsp/crt0.S | 14 ++++++------- cv32e40p/bsp/simple_system_common.c | 19 +++++++++++++++++ cv32e40p/bsp/simple_system_common.h | 12 ++++++++++- cv32e40p/bsp/simple_system_regs.h | 12 +++++++---- cv32e40p/bsp/startup.c | 32 ++++++++++++++--------------- cv32e40p/bsp/tinyprintf.c | 4 ++++ cv32e40p/bsp/tinyprintf.h | 5 +++++ 9 files changed, 74 insertions(+), 32 deletions(-) diff --git a/cv32e40p/.gitignore b/cv32e40p/.gitignore index d7745fb..653446a 100644 --- a/cv32e40p/.gitignore +++ b/cv32e40p/.gitignore @@ -4,5 +4,6 @@ *.objdump *.yaml *.readelf +*.headers bsp.old/ bsp.cv/ diff --git a/cv32e40p/bsp/Makefile b/cv32e40p/bsp/Makefile index b98af05..5a0af4e 100644 --- a/cv32e40p/bsp/Makefile +++ b/cv32e40p/bsp/Makefile @@ -24,8 +24,8 @@ DEPS = $(OBJS:.o=.d) LIBCV-VERIF = libcv-verif.a # Flags for tinyprintf -TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_PRINTF -TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_SPRINTF +TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_PRINTF=1 +TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_SPRINTF=0 # Compiler flags CFLAGS ?= -march=$(CV_SW_MARCH) -mabi=ilp32 -static -mcmodel=medany -Wall -g -O3 \ @@ -68,8 +68,7 @@ compile: $(OBJS) %.elf: $(OBJS) # mkdir -p $(SIM_TEST_PROGRAM_RESULTS) - @echo "*** TEST_FILES=$(TEST_FILES)" - @echo "*** linking $@" + @echo "*** linking $(OBJS) into $@" @echo "$(BANNER)" $(RISCV_EXE_PREFIX)$(RISCV_CC) \ $(CFLAGS) \ diff --git a/cv32e40p/bsp/crt0.S b/cv32e40p/bsp/crt0.S index be2a106..8c173df 100644 --- a/cv32e40p/bsp/crt0.S +++ b/cv32e40p/bsp/crt0.S @@ -71,13 +71,13 @@ main_entry: // addi x11, x0, 0 jal x1, startup /* Halt simulation */ - li a1, 0 - li a2, 0 - li a3, 0 - li a4, 0 - li a5, 0 - li a7, 93 - ecall +// li a1, 0 +// li a2, 0 +// li a3, 0 +// li a4, 0 +// li a5, 0 +// li a7, 93 +// ecall /* If execution ends up here just put the core to sleep */ sleep_loop: diff --git a/cv32e40p/bsp/simple_system_common.c b/cv32e40p/bsp/simple_system_common.c index e0d48d1..92c6bf9 100644 --- a/cv32e40p/bsp/simple_system_common.c +++ b/cv32e40p/bsp/simple_system_common.c @@ -6,6 +6,25 @@ #include "tinyprintf.h" + +// see tb/core/mm_ram.sv +void _Exit(int exit_code){ + + DEV_WRITE(MMADDR_EXIT, (uint32_t)exit_code); + while (1) { + asm volatile ("wfi"); + } +} + + void _putcf (void *, char c) { + DEV_WRITE(MMADDR_PRINT, (uint32_t)c); +} + + +int putchar(char c){ + _putcf (0, c); + return 1; +} void puthex(uint32_t h) { int cur_digit; // Iterate through h taking top 4 bits each time and outputting ASCII of hex diff --git a/cv32e40p/bsp/simple_system_common.h b/cv32e40p/bsp/simple_system_common.h index 650ea6b..1c8403b 100644 --- a/cv32e40p/bsp/simple_system_common.h +++ b/cv32e40p/bsp/simple_system_common.h @@ -12,7 +12,17 @@ #define DEV_READ(addr, val) (*((volatile uint32_t *)(addr))) #define PCOUNT_READ(name, dst) asm volatile("csrr %0, " #name ";" : "=r"(dst)) -extern void _putcf (void * unused, char c); +/** +* tinyprintf callback function, see tinyprintf.h for details +*/ +void _putcf (void * unused, char c); + +/** +* called upon main return +*/ +void _Exit(int exit_code) __attribute__ ((noreturn,noinline)); + + /** * Writes character to simulator out log. Signature matches c stdlib function * of the same name. diff --git a/cv32e40p/bsp/simple_system_regs.h b/cv32e40p/bsp/simple_system_regs.h index 47b3929..ba99a04 100644 --- a/cv32e40p/bsp/simple_system_regs.h +++ b/cv32e40p/bsp/simple_system_regs.h @@ -4,10 +4,14 @@ #ifndef SIMPLE_SYSTEM_REGS_H__ #define SIMPLE_SYSTEM_REGS_H__ - -#define MMADDR_PRINT 0x10000000 - //localparam int MMADDR_TESTSTATUS = 32'h2000_0000; -#define MMADDR_EXIT 20000004 +/** +see tb/core/mm_ram.sv + localparam int MMADDR_TESTSTATUS = 32'h2000_0000; + localparam int MMADDR_EXIT = 32'h2000_0004; +*/ +#define MMADDR_PRINT 0x10000000 +#define MMADDR_TESTSTATUS 0x20000000 +#define MMADDR_EXIT 0x20000004 //#define SIM_CTRL_BASE 0x20000 //#define SIM_CTRL_OUT 0x0 diff --git a/cv32e40p/bsp/startup.c b/cv32e40p/bsp/startup.c index 52f1fa9..b6d1ec4 100644 --- a/cv32e40p/bsp/startup.c +++ b/cv32e40p/bsp/startup.c @@ -1,28 +1,28 @@ -#define TINYPRINTF_DEFINE_TFP_PRINTF 1 -#define TINYPRINTF_DEFINE_TFP_SPRINTF 1 -#include "tinyprintf.h" - -#include "simple_system_common.h" +/* +inspired from +REPO = https://github.com/five-embeddev/riscv-scratchpad.git +BRANCH = master +HASH = de0c663 +PATH = baremetal-startup-c/src/startup.c + Simple C++ startup routine to setup CRT + SPDX-License-Identifier: Unlicense -extern int main(int argc, char *argv[]); - + (https://five-embeddev.com/ | http://www.shincbm.com/) - void _putcf (void * unused, char c) { - (int)unused; - DEV_WRITE(MMADDR_PRINT, (unsigned char)c); -} +*/ -int putchar(char c){ - _putcf (0, c); - return 1; -} +#include "tinyprintf.h" +#include "simple_system_common.h" +extern int main(int argc, char *argv[]); void startup(){ init_printf(0, _putcf); - main(0, 0); + int rc= main(0, 0); + _Exit(rc); + } \ No newline at end of file diff --git a/cv32e40p/bsp/tinyprintf.c b/cv32e40p/bsp/tinyprintf.c index 00abf80..7daad77 100644 --- a/cv32e40p/bsp/tinyprintf.c +++ b/cv32e40p/bsp/tinyprintf.c @@ -1,4 +1,8 @@ /* +REPO = https://github.com/openhwgroup/cv32e40p +BRANCH = master +HASH = 2ee3012 + File: tinyprintf.c Copyright (C) 2004 Kustaa Nyholm diff --git a/cv32e40p/bsp/tinyprintf.h b/cv32e40p/bsp/tinyprintf.h index a7dc2bf..1481b0d 100644 --- a/cv32e40p/bsp/tinyprintf.h +++ b/cv32e40p/bsp/tinyprintf.h @@ -1,4 +1,9 @@ /* + +REPO = https://github.com/openhwgroup/cv32e40p +BRANCH = master +HASH = 2ee3012 + File: tinyprintf.h Copyright (C) 2004 Kustaa Nyholm From 3a6917aff5478983b61a4943cd094ab8f7f199a0 Mon Sep 17 00:00:00 2001 From: daro Date: Tue, 1 Oct 2024 23:26:08 +0300 Subject: [PATCH 7/8] bugfix in tb/tb_dummy_memory.sv --- sim/core/Makefile.verilator | 5 ++--- tb/tb_dummy_memory.sv | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sim/core/Makefile.verilator b/sim/core/Makefile.verilator index 102bcc3..63f95a4 100644 --- a/sim/core/Makefile.verilator +++ b/sim/core/Makefile.verilator @@ -105,15 +105,14 @@ testbench_verilator: $(TBSRC_VERI) $(VERI_COMPILE_FLAGS) $(MAKE) -C $(VERI_OBJ_DIR) -j$(num_cores_half) -f V$(VLT_TOP_MODULE).mk mkdir -p $(SIM_RESULTS) - mkdir -p $(SIM_TEST_RESULTS) - mv $(VERI_OBJ_DIR)/V$(VLT_TOP_MODULE) $(SIM_TEST_RESULTS)/verilator_executable + mv $(VERI_OBJ_DIR)/V$(VLT_TOP_MODULE) $(SIM_RESULTS)/verilator_executable veri-test: verilate $(TEST_PROGRAM_PATH)/$(TEST)/$(TEST).hex @echo "$(BANNER)" @echo "* Running with Verilator: logfile in $(SIM_TEST_RESULTS)/$(TEST).log" @echo "$(BANNER)" mkdir -p $(VERI_LOG_DIR) - $(SIM_TEST_RESULTS)/verilator_executable \ + $(SIM_RESULTS)/verilator_executable \ $(VERI_FLAGS) \ "+firmware=$(TEST_PROGRAM_RELPATH)/$(TEST)/$(TEST).hex" \ | tee $(VERI_LOG_DIR)/$(TEST).log diff --git a/tb/tb_dummy_memory.sv b/tb/tb_dummy_memory.sv index d888ae0..dd2471c 100644 --- a/tb/tb_dummy_memory.sv +++ b/tb/tb_dummy_memory.sv @@ -9,7 +9,7 @@ timeunit 1ps; timeprecision 1ps; `ifdef VERILATOR - `define clk_verilated clk_delayed_i + `define clk_verilated clk_i `else `define clk_verilated clk_delayed `endif From eb9daf1ba7faa43e0955523f38773406d395891b Mon Sep 17 00:00:00 2001 From: daro Date: Wed, 2 Oct 2024 12:47:02 +0300 Subject: [PATCH 8/8] Verilator working :) --- Makefile.verilator | 19 +++++++++++++++++++ tb/redmule_tb.sv | 3 ++- tb/tb_dummy_memory.sv | 16 ++++++++-------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Makefile.verilator b/Makefile.verilator index bed8fc2..2128d94 100644 --- a/Makefile.verilator +++ b/Makefile.verilator @@ -52,6 +52,25 @@ run-test: $(BIN_DIR)/verilator_executable "+simdata=$(SIM_TEST_INPUTS)/stim_data.txt" \ | tee $(VERI_LOG_DIR)/$(TEST).log mv verilator_tb.vcd $(VERI_LOG_DIR)/ + + +.PHONY: veri-test +veri-test: $(BIN_DIR)/verilator_executable + @echo "$(BANNER)" + @echo "* Running with Verilator: " + @echo "* logfile in $(VERI_LOG_DIR)/$(TEST).log" + @echo "* *.vcd in $(VERI_LOG_DIR)" + @echo "$(BANNER)" + mkdir -p $(VERI_LOG_DIR) + rm -f $(VERI_LOG_DIR)/verilator_tb.vcd + $(BIN_DIR)/verilator_executable \ + $(VERI_FLAGS) \ + "+firmware=$(SIM_TEST_INPUTS)/stim_instr.txt" \ + "+simdata=$(SIM_TEST_INPUTS)/stim_data.txt" \ + | tee $(VERI_LOG_DIR)/$(TEST).log + mv verilator_tb.vcd $(VERI_LOG_DIR)/ + + .PHONY: help help: diff --git a/tb/redmule_tb.sv b/tb/redmule_tb.sv index 9475e80..97b7111 100644 --- a/tb/redmule_tb.sv +++ b/tb/redmule_tb.sv @@ -391,7 +391,8 @@ module redmule_tb ( // #(100 * TCP); // end WFI + returned != -1 signals end-of-computation - while (~core_sleep || errors == -1) #(TCP); + //while (~core_sleep || errors == -1) #(TCP); + do @(posedge clk_i); while (~core_sleep || errors==-1); cnt_rd = redmule_tb.i_dummy_dmemory.cnt_rd[0] + redmule_tb.i_dummy_dmemory.cnt_rd[1] + redmule_tb.i_dummy_dmemory.cnt_rd[2] + redmule_tb.i_dummy_dmemory.cnt_rd[3] + redmule_tb.i_dummy_dmemory.cnt_rd[4] + redmule_tb.i_dummy_dmemory.cnt_rd[5] + redmule_tb.i_dummy_dmemory.cnt_rd[6] + redmule_tb.i_dummy_dmemory.cnt_rd[7] + redmule_tb.i_dummy_dmemory.cnt_rd[8]; cnt_wr = redmule_tb.i_dummy_dmemory.cnt_wr[0] + redmule_tb.i_dummy_dmemory.cnt_wr[1] + redmule_tb.i_dummy_dmemory.cnt_wr[2] + redmule_tb.i_dummy_dmemory.cnt_wr[3] + redmule_tb.i_dummy_dmemory.cnt_wr[4] + redmule_tb.i_dummy_dmemory.cnt_wr[5] + redmule_tb.i_dummy_dmemory.cnt_wr[6] + redmule_tb.i_dummy_dmemory.cnt_wr[7] + redmule_tb.i_dummy_dmemory.cnt_wr[8]; $display("[TB] - cnt_rd=%-8d", cnt_rd); diff --git a/tb/tb_dummy_memory.sv b/tb/tb_dummy_memory.sv index dd2471c..d431e2b 100644 --- a/tb/tb_dummy_memory.sv +++ b/tb/tb_dummy_memory.sv @@ -155,16 +155,16 @@ module tb_dummy_memory end end -`ifdef VERILATOR - always_ff @(posedge `clk_verilated) - begin - tcdm_r_data <= tcdm_r_data_int; - tcdm_r_valid <= tcdm_r_valid_int; - end -`else +//`ifdef VERILATOR +// always_ff @(posedge `clk_verilated) +// begin +// tcdm_r_data <= tcdm_r_data_int; +// tcdm_r_valid <= tcdm_r_valid_int; +// end +// `else assign tcdm_r_data = tcdm_r_data_int; assign tcdm_r_valid = tcdm_r_valid_int; -`endif +// `endif generate