diff --git a/.github/workflows/scala.yml b/.github/workflows/scala.yml index fe69b741..86c6eb33 100644 --- a/.github/workflows/scala.yml +++ b/.github/workflows/scala.yml @@ -55,18 +55,20 @@ jobs: - name: Setup env run: echo "$HOME/tools/bin" >> $GITHUB_PATH - - name: Install cached tools - if: steps.tools.outputs.cache-hit != 'true' - run: source .github/workflows/tools.sh && install_cached - - - name: Install uncached tools - run: source .github/workflows/tools.sh && install_uncached - - - name: Install SpinalHDL - run: (cd .. && git clone https://github.com/SpinalHDL/SpinalHDL.git -b dev) + - name: Install all tools + run: source .github/workflows/tools.sh && install_all - name: Compile run: sbt clean compile - - name: Test - run: NAXRISCV_REGRESSION_THREAD_COUNT=1 SBT_OPTS="-Xmx2G -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=2G -Xss2M -Duser.timezone=GMT" sbt test + - name: Regression Test SIMULATOR + run: | + export NAXRISCV_REGRESSION_THREAD_COUNT=1 + export SBT_OPTS="-Xmx2G -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=2G -Xss2M -Duser.timezone=GMT" + sbt "testOnly *NaxRiscvRegression" + + - name: Regression Test RVLS + run: | + export NAXRISCV_REGRESSION_THREAD_COUNT=1 + export SBT_OPTS="-Xmx2G -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=2G -Xss2M -Duser.timezone=GMT" + sbt "testOnly *NaxRiscvRvls" diff --git a/.github/workflows/tools.sh b/.github/workflows/tools.sh index 8831b6a5..c87de524 100755 --- a/.github/workflows/tools.sh +++ b/.github/workflows/tools.sh @@ -54,6 +54,7 @@ install_elfio(){ install_packages(){ sudo apt-get update sudo apt install -y zlib1g-dev libboost-all-dev libboost-dev libasio-dev device-tree-compiler libsdl2-2.0-0 libsdl2-dev + sudo apt install -y git make autoconf build-essential flex libfl-dev bison help2man # First time prerequisites } install_uncached(){ @@ -78,3 +79,8 @@ install_cached(){ (install_spike) (install_verilator) } + +install_all(){ + export NAXRISCV=${PWD} + make install +} diff --git a/.gitignore b/.gitignore index 405ffd6c..856faa99 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,11 @@ obj_dir explor nax.h +toolchain/ simWorkspace/ tmp/ /archive.tar.gz *.out32 +.metals/ +src/test/python/naxriscv/*.mk +*.diff diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..d314845a --- /dev/null +++ b/Makefile @@ -0,0 +1,169 @@ +# Constants +PRJ_NAX=NaxRiscv +PROJECT?=$(PRJ_NAX) +# Dirs +MKFILE_PATH=$(abspath $(firstword $(MAKEFILE_LIST))) +CORE_DIR=$(dir $(MKFILE_PATH)) +TOOLCHAIN_DIR=$(CORE_DIR)/toolchain +# Tools +SPIKE_DIR=$(CORE_DIR)/ext/riscv-isa-sim +RVLS_DIR=$(CORE_DIR)/ext/rvls +RISCV_TEST_DIR=$(CORE_DIR)/ext/NaxSoftware/riscv-tests +# Riscv gcc +RISCV_HOST=riscv64-unknown-elf +RISCV=$(TOOLCHAIN_DIR)/riscv-gnu-toolchain +RISCV_VERSION=409b951ba6621f2f115aebddfb15ce2dd78ec24f +RISCV_GCC=$(RISCV)/bin/$(RISCV_HOST)-gcc +RISCV_OBJCOPY=$(RISCV)/bin/$(RISCV_HOST)-objcopy +RISCV_OBJDUMP=$(RISCV)/bin/$(RISCV_HOST)-objdump +# Verilator +VERILATOR_VERSION_NAX=v4.216 +VERILATOR_ROOT_NAX=$(TOOLCHAIN_DIR)/verilator-$(VERILATOR_VERSION_NAX) +# ELFIO +ELFIO_VERSION=d251da09a07dff40af0b63b8f6c8ae71d2d1938d # Avoid C++17 +# SDL +LIBSDL_VERSION=60d1944e463da73f753661190d783961a9c5b764 +# SBT +SBT_VERSION=1.7.1 +# OpenJDK +OPENJDK_VERSION=11.0.15+10 +OPENJDK_HOME=$(TOOLCHAIN_DIR)/openjdk +# Git links +GIT_URL_NAXRISCV=https://github.com/SpinalHDL/NaxRiscv +GIT_URL_SPINALHDL=https://github.com/SpinalHDL/SpinalHDL.git + +# install toolchain #################################### + +# install core +install-core: + ./ci/clone-submodules.sh $(CORE_DIR) + +# install initial +install-toolchain-initial: install-core + mkdir -p $(TOOLCHAIN_DIR) + ./ci/install-sbt.sh $(SBT_VERSION) $(TOOLCHAIN_DIR) + ./ci/install-openjdk.sh $(OPENJDK_VERSION) $(TOOLCHAIN_DIR) + +# install toolchain +install-toolchain: install-toolchain-initial + ./ci/install-verilator.sh $(VERILATOR_VERSION_NAX) $(TOOLCHAIN_DIR) + ./ci/install-libsdl-elfio-spikespinalhdl.sh $(SPIKE_DIR) $(ELFIO_VERSION) $(LIBSDL_VERSION) + ./ci/install-rvls.sh $(RVLS_DIR) + +# build spike & rvls after modification +build-spike-rvls: + ./ci/build_spike_rvls.sh $(CORE_DIR) + +# build regress simulator after modification +build-simulator: + ./ci/build_regress_simulator.sh $(CORE_DIR) + +# All NaxRiscv targets implements machine, supervisor and user mode +TARGETS_NAX=("rv32imasu" "rv32imacsu" "rv32imafcsu" "rv32imafdcsu" "rv64imasu" "rv64imacsu" "rv64imafcsu" "rv64imafdcsu") + +# Set default target if not specified +TARGET_NAX ?= rv64imafdcsu + +# Define parameters for each target +# RV32 +rv32imasu_PARAMS := +rv32imacsu_PARAMS := --withRvc +rv32imafcsu_PARAMS := --withRvc --withFloat +rv32imafdcsu_PARAMS := --withRvc --withFloat --withDouble +# RV64 +rv64imasu_PARAMS := +rv64imacsu_PARAMS := --withRvc +rv64imafcsu_PARAMS := --withRvc --withFloat +rv64imafdcsu_PARAMS := --withRvc --withFloat --withDouble + +# Get the parameters for the selected target +PARAMS := $($(TARGET_NAX)_PARAMS) + +# Determine the command based on the target architecture (32-bit or 64-bit) +ifneq (,$(findstring 32,$(TARGET_NAX))) + GEN_CMD := $(TOOLCHAIN_DIR)/sbt/bin/sbt "runMain naxriscv.Gen $(PARAMS)" + XLEN := 32 +else + GEN_CMD := $(TOOLCHAIN_DIR)/sbt/bin/sbt "runMain naxriscv.Gen64 $(PARAMS)" + XLEN := 64 +endif + +# RTL of NaxRiscv +.PHONY: $(PRJ_NAX).v +$(PRJ_NAX).v: + echo " ";\ + echo "Selected target: $(TARGET_NAX)";\ + echo "Parameters: $(PARAMS)";\ + echo "Generation command: $(GEN_CMD)";\ + echo " ";\ + export JAVA_HOME=$(OPENJDK_HOME); \ + export NAXRISCV=$(CORE_DIR); \ + export PATH=$(OPENJDK_HOME)/bin:$(PATH); \ + $(MAKE) clean-gen; \ + cd $(CORE_DIR); \ + $(GEN_CMD); \ + +# Verilator model of NaxRiscv +.PHONY: src/test/cpp/naxriscv/obj_dir/VNaxRiscv +src/test/cpp/naxriscv/obj_dir/VNaxRiscv:$(PRJ_NAX).v + make -C $(CORE_DIR)/src/test/cpp/naxriscv compile + VERILATOR_ROOT=$(VERILATOR_ROOT_NAX) + SPIKE=$(SPIKE_DIR) + PATH=$(VERILATOR_ROOT_NAX)/bin:$(PATH) + +verilate-$(PRJ_NAX): src/test/cpp/naxriscv/obj_dir/VNaxRiscv + +# install +install:clean-all install-toolchain + @echo " " + @echo "[SUCCESS] The entire toolchain is built with Success." + @echo " " + +# test execute ######################################### +test-regression : + @echo "Testing with NaxRiscvRegression...." + sbt "testOnly *.NaxRiscvRegression" + +test-rvls : + @echo "Testing with NaxRiscvRvls...." + sbt "testOnly *.NaxRiscvRvls" +# clean ################################################ + +clean-submodules: + rm -rf $(CORE_DIR)/ext/* + +clean-install: + rm -rf $(CORE_DIR)/tmp + +clean-sim: + rm -rf $(CORE_DIR)/.venv + +clean-workspace: + rm -rf $(CORE_DIR)/simWorkspace + +clean-exec: + rm -rf *.tmp + rm -rf *.xml + rm -rf *.tar.gz + +clean-toolchain: + rm -rf $(TOOLCHAIN_DIR) + +clean-gen: + rm -rf $(CORE_DIR)/src/test/cpp/naxriscv/obj_dir + rm -f $(PRJ_NAX).v + rm -f nax.h + rm -f NaxRiscvSynt.v + +clean-all: clean-install clean-sim clean-workspace clean-exec clean-gen + rm -rf $(TOOLCHAIN_DIR)/openjdk + rm -rf $(TOOLCHAIN_DIR)/sbt + rm -rf $(TOOLCHAIN_DIR)/verilator-$(VERILATOR_VERSION_NAX) + rm -rf $(SPIKE_DIR) + rm -rf $(RVLS_DIR) + rm -rf $(CORE_DIR)/ext/SpinalHDL + rm -rf $(CORE_DIR)/ext/NaxSoftware + +# include ################################################ +-include testsRvls.mk +-include src/test/python/naxriscv/all_testsRvls.mk \ No newline at end of file diff --git a/Patch_DualSimTracer_toSupportSeveralELF_addRunningLinuxFlag.patch b/Patch_DualSimTracer_toSupportSeveralELF_addRunningLinuxFlag.patch new file mode 100644 index 00000000..62ca32ce --- /dev/null +++ b/Patch_DualSimTracer_toSupportSeveralELF_addRunningLinuxFlag.patch @@ -0,0 +1,80 @@ +diff --git a/lib/src/main/scala/spinal/lib/misc/test/DualSimTracer.scala b/lib/src/main/scala/spinal/lib/misc/test/DualSimTracer.scala +index 5160ace69..d12a2a8ef 100644 +--- a/lib/src/main/scala/spinal/lib/misc/test/DualSimTracer.scala ++++ b/lib/src/main/scala/spinal/lib/misc/test/DualSimTracer.scala +@@ -12,14 +12,68 @@ import scala.concurrent.ExecutionContext + */ + object DualSimTracer { + def apply[T <: Component](compiled: SimCompiled[T], window: Long, seed: Int)(testbench: T => Unit): Unit = withCb(compiled, window, seed) { (dut, _) => testbench(dut) } +- def withCb[T <: Component](compiled: SimCompiled[T], window: Long, seed: Int, dualSimEnable : Boolean)(testbench: (T, (=> Unit) => Unit) => Unit): Unit = { +- dualSimEnable match { +- case true => DualSimTracer.withCb(compiled, window, seed)(testbench) +- case false => { +- val traceCallbacks = ArrayBuffer[() => Unit]() +- compiled.doSimUntilVoid(seed = seed) { dut => testbench(dut, f => traceCallbacks += (() => f)); traceCallbacks.foreach(_())} ++ ++ def apply[T <: Component](compiled: SimCompiled[T], window: Long, seed: Int, elfFile : String, runBuildroot: Boolean = false)(testbench: T => Unit): Unit = withCb(compiled, window, seed, elfFile, runBuildroot) { (dut: T, _, _, _) => testbench(dut) } ++ ++ def withCb[T <: Component](compiled: SimCompiled[T], window: Long, seed: Int, elfFile : String, runBuildroot: Boolean = false)(testbench: (T, (=> Unit) => Unit, String, Boolean) => Unit): Unit = { ++ var mTime = 0l ++ var mEnded = false ++ var explorerFailed = false ++ ++ implicit val ec = ExecutionContext.global ++ ++ val testName = elfFile.substring(elfFile.lastIndexOf("/") + 1) ++ ++ val explorer = new AsyncJob(toStdout = true, logsPath = new File(compiled.compiledPath, "explorer/" + testName)) ({ ++ try { ++ compiled.doSimUntilVoid(name = testName + s"_explorer", seed = seed) { dut => ++ disableSimWave() ++ periodicaly(window) { ++ mTime = simTime() ++ } ++ onSimEnd { ++ mTime = simTime() ++ mEnded = true ++ } ++ testbench(dut, cb => {}, elfFile, runBuildroot) ++ } ++// println("Explorer success") ++ } catch { ++ case e: Throwable => explorerFailed = true; throw e + } +- } ++ }) ++ ++ val tracer = new AsyncJob(toStdout = false, logsPath = new File(compiled.compiledPath, "tracer/" + testName))({ ++ val traceCallbacks = ArrayBuffer[() => Unit]() ++ compiled.doSimUntilVoid(name = testName + s"_tracer", seed = seed) { dut => ++ disableSimWave() ++ fork { ++ sleep(0) ++ while (true) { ++ while (simTime + window * 2 >= mTime && !mEnded) { ++ Thread.sleep(100, 0) ++ } ++ if (mEnded && explorerFailed) { ++ sleep((mTime - simTime - window) max 0) ++ enableSimWave() ++ traceCallbacks.foreach(_()) ++ sleep(window + 1000) ++ simFailure("slave thread didn't ended ????") ++ } ++ sleep(window) ++ } ++ } ++// println("Tracer success") ++ testbench(dut, callback => traceCallbacks += (() => callback), elfFile, runBuildroot) ++ } ++ }) ++ ++ explorer.join() ++ tracer.join() ++ ++ assert(explorer.failed == tracer.failed) ++ ++ if (tracer.failed) throw new Exception(s"Dual sim reached end with failure, see ${tracer.logsPath.getAbsolutePath}") + } + + def withCb[T <: Component](compiled: SimCompiled[T], window: Long, seed: Int)(testbench: (T, (=> Unit) => Unit) => Unit): Unit = { diff --git a/adding_wavePath_simConfig.patch b/adding_wavePath_simConfig.patch new file mode 100644 index 00000000..b42dab18 --- /dev/null +++ b/adding_wavePath_simConfig.patch @@ -0,0 +1,80 @@ +diff --git a/core/src/main/scala/spinal/core/sim/SimBootstraps.scala b/core/src/main/scala/spinal/core/sim/SimBootstraps.scala +index cb825e689..7f5cf3b2a 100644 +--- a/core/src/main/scala/spinal/core/sim/SimBootstraps.scala ++++ b/core/src/main/scala/spinal/core/sim/SimBootstraps.scala +@@ -658,6 +658,7 @@ object SpinalSimBackendSel{ + case class SpinalSimConfig( + var _workspacePath : String = System.getenv().getOrDefault("SPINALSIM_WORKSPACE","./simWorkspace"), + var _workspaceName : String = null, ++ var _wavePath : String = "./", + var _waveDepth : Int = 0, //0 => all + var _spinalConfig : SpinalConfig = SpinalConfig(), + var _optimisationLevel : Int = 0, +@@ -813,6 +814,12 @@ case class SpinalSimConfig( + this + } + ++ def wavePath(path: String): this.type = { ++ _wavePath = path ++ println("SET WAVE PATH: " + _wavePath) ++ this ++ } ++ + def withConfig(config: SpinalConfig): this.type = { + _spinalConfig = config + this +@@ -972,9 +979,33 @@ case class SpinalSimConfig( + _workspaceName = SimWorkspace.allocateWorkspace(_workspacePath, _workspaceName) + + println(f"[Progress] Simulation workspace in ${new File(s"${_workspacePath}/${_workspaceName}").getAbsolutePath}") +- new File(s"${_workspacePath}").mkdirs() +- FileUtils.deleteQuietly(new File(s"${_workspacePath}/${_workspaceName}")) +- new File(s"${_workspacePath}/${_workspaceName}").mkdirs() ++ val workspacePathDir = new File(s"${_workspacePath}") ++ // Create folder only if it does not exist ++ if (!workspacePathDir.exists()) workspacePathDir.mkdirs() ++ ++ val workspaceDir = new File(s"${_workspacePath}/${_workspaceName}") ++ if (workspaceDir.exists() && workspaceDir.isDirectory()) { ++ workspaceDir.listFiles().foreach { file => ++ try { ++ val extension = file.getName().split('.').lastOption.getOrElse("") ++ if (!Set("py", "mk").contains(extension) && file.getName() != "Makefile" && file.getName() != "logs" && file.getName() != "waves" && !file.isDirectory()) { ++ FileUtils.deleteQuietly(file) ++ } ++ } catch { ++ case e: Exception => ++ println(s"Failed to delete file: ${file.getName()}") ++ e.printStackTrace() ++ } ++ } ++ } else { ++ println(s"Directory ${workspaceDir.getAbsolutePath()} does not exist or is not a directory.") ++ } ++ ++ if (!workspaceDir.exists()) workspaceDir.mkdirs() ++ ++ val wavePathDir = new File(s"${_workspacePath}/${_workspaceName}/${_wavePath}") ++ if (!wavePathDir.exists()) wavePathDir.mkdirs() ++ + new File(s"${_workspacePath}/${_workspaceName}/rtl").mkdirs() + + val compiledPath = new File(s"${_workspacePath}/${_workspaceName}") +@@ -1022,7 +1053,7 @@ case class SpinalSimConfig( + maxCacheEntries = _maxCacheEntries, + cachePath = if (!_disableCache) (if (_cachePath != null) _cachePath else s"${_workspacePath}/.cache") else null, + workspacePath = s"${_workspacePath}/${_workspaceName}", +- vcdPath = wavePath, ++ vcdPath = s"${_workspacePath}/${_workspaceName}/${_wavePath}", + vcdPrefix = null, + workspaceName = "verilator", + waveDepth = _waveDepth, +@@ -1174,6 +1205,8 @@ case class SimConfigLegacy[T <: Component]( + def workspacePath(path: String): this.type = { _simConfig.workspacePath(path); this } + def workspaceName(name: String): this.type = { _simConfig.workspaceName(name); this } + ++ def wavePath(path: String): this.type = { _simConfig.wavePath(path); this } ++ + def withConfig(config: SpinalConfig): this.type = { _simConfig.withConfig(config); this } + + def noOptimisation: this.type = { _simConfig.noOptimisation ; this } diff --git a/ci/build_regress_simulator.sh b/ci/build_regress_simulator.sh new file mode 100755 index 00000000..0e437fc9 --- /dev/null +++ b/ci/build_regress_simulator.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Script to compile the NaxRiscv simulator + +# Exit immediately if a command exits with a non-zero status +set -e + +# Check if the correct number of arguments is provided (should be exactly 1) +if [ "$#" -ne 1 ]; then + echo "Error: You must provide exactly one argument, which is the base root directory path." + echo "Tip: Set the base directory by running the following command:" + echo " export NAXRISCV=\$(pwd)" + echo "Then, run the script from the NaxRiscv directory with:" + echo " $0 \$NAXRISCV" + exit 1 +fi + +# Navigate to the correct directory +cd $1/src/test/cpp/naxriscv || { + echo "Error: Directory $1/src/test/cpp/naxriscv does not exist." + exit 1 +} + +# Check if the obj_dir directory exists +if [ -d "$1/src/test/cpp/naxriscv/obj_dir" ]; then + echo "Cleaning previous build..." + make clean +fi + +# Compile the simulator +echo "Compiling the simulator..." +make compile + +# Check if compilation was successful +if [ $? -eq 0 ]; then + echo "Compilation completed successfully." +else + echo "Error: Compilation failed." + exit 1 +fi diff --git a/ci/build_spike_rvls.sh b/ci/build_spike_rvls.sh new file mode 100755 index 00000000..98781da3 --- /dev/null +++ b/ci/build_spike_rvls.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status +set -e + +# Check if the correct number of arguments is provided (should be exactly 1) +if [ "$#" -ne 1 ]; then + echo "Error: You must provide exactly one argument, which is the base directory path." + echo "Tip: Set the base directory by running the following command:" + echo " export NAXRISCV=\$(pwd)" + echo "Then, run the script from the NaxRiscv directory with:" + echo " $0 \$NAXRISCV" + exit 1 +fi + +# Base directory +BASE_DIR=$1 + +# Check if RISCV environment variable is set +if [ -z "$RISCV" ]; then + echo "Error: RISCV environment variable is not set." + exit 1 +fi + +# Install ELFIO if not already installed +if [ ! -e "$BASE_DIR/ext/riscv-isa-sim/include/elfio/elfio.hpp" ]; then + echo "Installing ELFIO..." + mkdir -p $BASE_DIR/tmp + cd $BASE_DIR/tmp + git clone https://github.com/serge1/ELFIO.git + cd ELFIO + git checkout master # or specify a specific version/tag + mkdir -p $BASE_DIR/ext/riscv-isa-sim/include + cp -R elfio $BASE_DIR/ext/riscv-isa-sim/include + cd $BASE_DIR + rm -rf $BASE_DIR/tmp +else + echo "ELFIO is already installed." +fi + +# Install SDL if not already installed +if [ ! -e "$BASE_DIR/ext/riscv-isa-sim/include/SDL2/SDL.h" ]; then + echo "Installing SDL..." + mkdir -p $BASE_DIR/tmp + cd $BASE_DIR/tmp + git clone https://github.com/libsdl-org/SDL + cd SDL + git checkout release-2.28.5 # or specify a specific version/tag + mkdir -p build + cd build + ../configure --prefix="$BASE_DIR/ext/riscv-isa-sim/" + make -j$(nproc) + make install + cd $BASE_DIR + rm -rf $BASE_DIR/tmp +else + echo "SDL is already installed." +fi + +# Check if the 'build' directory exists, create it if it doesn't +if [ ! -d "$BASE_DIR/ext/riscv-isa-sim/build/" ]; then + mkdir -p $BASE_DIR/ext/riscv-isa-sim/build/ +fi + +# Run the configuration script without Boost libraries +cd $BASE_DIR/ext/riscv-isa-sim/build/ +if [ -d "$BASE_DIR/ext/riscv-isa-sim/build/" ]; then + make clean +fi +../configure --prefix=${BASE_DIR}/ext/riscv-isa-sim/ --without-boost --without-boost-asio --without-boost-regex + +# Compile the library using all available cores +make -j$(nproc) +# Create a shared library +g++ --shared -L. -Wl,--export-dynamic -L/usr/lib/x86_64-linux-gnu \ + -Wl,-rpath,/lib -o package.so spike.o libspike_main.a libriscv.a \ + libdisasm.a libsoftfloat.a libfesvr.a libfdt.a -lpthread -ldl \ + -lboost_regex -lboost_system -lpthread -lboost_system -lboost_regex +# Clean previous build artifacts for 'rvls' +if [ ! -e "$BASE_DIR/ext/rvls/build/apps/rvls" ]; then + echo "Installing RVLS..." + cd $BASE_DIR/ext/rvls/ +else + cd $BASE_DIR/ext/rvls/ + make clean +fi + +# Compile the project using all available cores +make -j$(nproc) + +echo "Installation and build of ELFIO, SDL, Spike, and RVLS completed." \ No newline at end of file diff --git a/ci/clone-submodules.sh b/ci/clone-submodules.sh new file mode 100755 index 00000000..3a5b6696 --- /dev/null +++ b/ci/clone-submodules.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +# Checking the number of arguments +if [ "$#" -ne 1 ]; then + echo "Error: You must provide exactly one argument, which is the base directory path." + echo "Tip: Set the base directory by running the following command:" + echo " export NAXRISCV=\$(pwd)" + echo "Then, run the script from the NaxRiscv directory with:" + echo " $0 \$NAXRISCV" + exit 1 +fi + +# Arguments +ROOT_DIR=$1 +RVLS_DIR="$ROOT_DIR/ext/rvls" +#RVLS_VERSION="b5c15e129f8168c2317b2c129aef91adf935bfeb" + +# Function to apply a patch +apply_patch() { + local patch_dir=$1 + local patch_file=$2 + local patch_name=$3 + + echo "Applying patch for $patch_name..." + + # Apply the patch and verify the application succeeded + cd "$patch_dir" || { echo "Error: Directory $patch_dir does not exist."; exit 1; } + git apply --reject "$patch_file" + + if [ $? -ne 0 ]; then + echo "Error: Patch $patch_name failed to apply. Exiting." + exit 1 + fi + + echo "$patch_name successfully applied...OK" + echo " " +} + +# Function to check and clone submodules +clone_submodule() { + echo "Checking and cloning submodules if necessary..." + echo " " + + # Verifying submodules + if git submodule status | grep -q '^-' ; then + echo "Initializing submodules..." + echo " " + git submodule update --init --recursive + + # Checking for the existence of the init.sh file + if [ -f "$ROOT_DIR/ext/NaxSoftware/init.sh" ]; then + cd "$ROOT_DIR/ext/NaxSoftware" || exit 1 + ./init.sh + echo "All clones are successfully completed." + else + echo "Error: '$ROOT_DIR/ext/NaxSoftware/init.sh' not found!" + echo "Please check if the submodule is correctly initialized." + exit 1 + fi + else + echo "Submodules already initialized..." + echo " " + echo "If you want to force reinstallation:" + echo " 1- export NAXRISCV=\${PWD}" + echo " 2- run 'make clean-core' from \$NAXRISCV folder" + echo " 3- restart the script with '$0 \$NAXRISCV'" + fi + + ## Ensure RVLS is at the correct version + #echo "Ensuring RVLS is at the correct version: $RVLS_VERSION" + #cd "$RVLS_DIR" || { echo "Error: Directory $RVLS_DIR does not exist."; exit 1; } + #git fetch + #git checkout "$RVLS_VERSION" || { echo "Error: Failed to checkout RVLS version $RVLS_VERSION."; exit 1; } + #echo "RVLS successfully checked out to $RVLS_VERSION." + + # Apply patches + apply_patch "$RVLS_DIR" "$ROOT_DIR/rvls.diff" "rvls-include-elfio" + apply_patch "$RVLS_DIR" "$ROOT_DIR/rvls.patch" "rvls-all-modif" + apply_patch "$ROOT_DIR/ext/SpinalHDL/lib/src/main/scala/spinal/lib/misc/test" "$ROOT_DIR/Patch_DualSimTracer_toSupportSeveralELF_addRunningLinuxFlag.patch" "DualSim" + apply_patch "$ROOT_DIR/ext/SpinalHDL/core/src/main/scala/spinal/core/sim" "$ROOT_DIR/adding_wavePath_simConfig.patch" "simBootstraps" +} + +# Main script execution +clone_submodule diff --git a/ci/install-libsdl-elfio-spikespinalhdl.sh b/ci/install-libsdl-elfio-spikespinalhdl.sh new file mode 100755 index 00000000..22f58a49 --- /dev/null +++ b/ci/install-libsdl-elfio-spikespinalhdl.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# Global installation script for ELFIO, SDL, and Spike +# Usage: ./install-libsdl-elfio-spikespinalhdl.sh + +# Check if the correct number of arguments is provided +if [ $# -ne 3 ]; then + echo "Usage: $0 " + exit 1 +fi + +SPIKE_NAX_HOME=$1 +ELFIO_VERSION=$2 +LIBSDL_VERSION=$3 + + +# Create a temporary directory for downloads and builds +TMP_DIR=$(mktemp -d) +trap "rm -rf $TMP_DIR" EXIT + +# Install ELFIO +if [ ! -e "$SPIKE_NAX_HOME/include/elfio/elfio.hpp" ]; then + echo "Installing ELFIO..." + cd $TMP_DIR + git clone https://github.com/serge1/ELFIO.git + cd ELFIO + git checkout $ELFIO_VERSION + mkdir -p $SPIKE_NAX_HOME/include + cp -R elfio $SPIKE_NAX_HOME/include +else + echo "Using ELFIO from cached directory." +fi + +# Install SDL +if [ ! -e "$SPIKE_NAX_HOME/include/SDL2/SDL.h" ]; then + echo "Installing SDL..." + cd $TMP_DIR + git clone https://github.com/libsdl-org/SDL + cd SDL + git checkout $LIBSDL_VERSION + mkdir -p build + cd build + ../configure --prefix="$SPIKE_NAX_HOME/" + make -j $(nproc) + make install +else + echo "Using SDL from cached directory." +fi + +# Install Spike +if [ ! -e "$SPIKE_NAX_HOME/build/spike" ]; then + echo "Installing SpinalHDL/spike..." + cd $SPIKE_NAX_HOME + mkdir -p build + cd build + ../configure --prefix=$SPIKE_NAX_HOME --without-boost --without-boost-asio --without-boost-regex + make -j$(nproc) + g++ --shared -L. -Wl,--export-dynamic -L/usr/lib/x86_64-linux-gnu \ + -Wl,-rpath,/lib -o package.so spike.o libspike_main.a libriscv.a \ + libdisasm.a libsoftfloat.a libfesvr.a libfdt.a -lpthread -ldl \ + -lboost_regex -lboost_system -lpthread -lboost_system -lboost_regex +else + echo "Spike is already built and exists in $SPIKE_NAX_HOME/build." +fi + +echo "Installation of ELFIO, SDL, and Spike completed." \ No newline at end of file diff --git a/ci/install-openjdk.sh b/ci/install-openjdk.sh new file mode 100755 index 00000000..939bf35a --- /dev/null +++ b/ci/install-openjdk.sh @@ -0,0 +1,15 @@ +# usage: ./install-openjdk.sh + +#!/bin/bash + +if [ ! -e "$2/openjdk/bin/java" ]; then + echo "Installing OpenJDK..." + mkdir -p tmp ; rm -rf tmp/* ; cd tmp + mkdir -p $2/openjdk + wget https://builds.openlogic.com/downloadJDK/openlogic-openjdk/$1/openlogic-openjdk-$1-linux-x64.tar.gz + tar -xvzf openlogic-openjdk-$1-linux-x64.tar.gz -C . + mv openlogic-openjdk-$1-linux-x64/* $2/openjdk +else + echo "Using OpenJDK from cached directory." +fi + diff --git a/ci/install-rvls.sh b/ci/install-rvls.sh new file mode 100755 index 00000000..9084dfcf --- /dev/null +++ b/ci/install-rvls.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Checks if no argument is given +if [ $# -eq 0 ]; then + echo "Usage: ./install-rvls.sh " + exit 1 +fi + +if [ ! -e "$1/build/apps/rvls" ]; then + echo "Installing RVLS..." + cd $1 + make -j$(nproc) +else + echo "RVLS is already built and exists in $1/build/apps/." +fi diff --git a/ci/install-sbt.sh b/ci/install-sbt.sh new file mode 100755 index 00000000..f48264bd --- /dev/null +++ b/ci/install-sbt.sh @@ -0,0 +1,12 @@ +# usage: ./install-sbt.sh + +#!/bin/bash + +if [ ! -e "$2/sbt/bin/sbt" ]; then + echo "Installing SBT..." + mkdir -p tmp ; rm -rf tmp/* ; cd tmp + wget https://github.com/sbt/sbt/releases/download/v$1/sbt-$1.tgz + tar -xvzf sbt-$1.tgz -C $2/ +else + echo "Using SBT from cached directory." +fi diff --git a/ci/install-verilator.sh b/ci/install-verilator.sh new file mode 100755 index 00000000..af0b1a20 --- /dev/null +++ b/ci/install-verilator.sh @@ -0,0 +1,31 @@ +# usage: ./install-verilator.sh + +#!/bin/bash +set -e # Stop script on error + +VERILATOR_VERSION=$1 +INSTALL_DIR=$2 + +# full path of the installation directory +VERILATOR_DIR="$INSTALL_DIR/verilator-$VERILATOR_VERSION" + +if [ ! -e "$VERILATOR_DIR/bin/verilator" ]; then + echo "Installing Verilator $VERILATOR_VERSION..." + mkdir -p $INSTALL_DIR + cd $INSTALL_DIR + + # Clone and build in the verilator toolchain directory + git clone https://github.com/verilator/verilator.git $VERILATOR_DIR + unset VERILATOR_ROOT + cd $VERILATOR_DIR + git checkout $VERILATOR_VERSION + + # Generate the configuration script + autoconf + ./configure --prefix="$VERILATOR_DIR" + make -j$(nproc) + + echo "Verilator successfully installed in $VERILATOR_DIR" +else + echo "Verilator $VERILATOR_VERSION is already installed in $VERILATOR_DIR" +fi diff --git a/ext/rvls b/ext/rvls index 3081a28e..b5c15e12 160000 --- a/ext/rvls +++ b/ext/rvls @@ -1 +1 @@ -Subproject commit 3081a28e562756c12fc7d7aee1c554ea8bae36f5 +Subproject commit b5c15e129f8168c2317b2c129aef91adf935bfeb diff --git a/rvls.diff b/rvls.diff new file mode 100644 index 00000000..e8741906 --- /dev/null +++ b/rvls.diff @@ -0,0 +1,19 @@ +diff --git a/Makefile b/Makefile +index 0219d66..bbeb63a 100644 +--- a/Makefile ++++ b/Makefile +@@ -10,12 +10,13 @@ SRC := $(wildcard src/*.cpp) + + SPIKE?=../riscv-isa-sim + SPIKE_BUILD=$(realpath ${SPIKE}/build) ++ELFIO_BUILD=$(realpath ${SPIKE}/include) + SPIKE_OBJS:= libspike_main.a libriscv.a libdisasm.a libsoftfloat.a libfesvr.a libfdt.a + SPIKE_OBJS:=$(addprefix ${SPIKE_BUILD}/,${SPIKE_OBJS}) + LDFLAGS+=${SPIKE_OBJS} + LDFLAGS += -L/usr/lib/x86_64-linux-gnu + LIBRARIES += -lpthread -ldl -lboost_regex -lboost_system -lpthread -lboost_system -lboost_regex +- ++INCLUDE += -I$(realpath ${ELFIO_BUILD}) + INCLUDE += -I$(realpath ${SPIKE}/riscv) + INCLUDE += -I$(realpath ${SPIKE}/fesvr) + INCLUDE += -I$(realpath ${SPIKE}/softfloat) diff --git a/rvls.patch b/rvls.patch new file mode 100644 index 00000000..1deddc9d --- /dev/null +++ b/rvls.patch @@ -0,0 +1,592 @@ +Subject: [PATCH] RVLS +logging FCSR write register +initialize floating registers and update FCSR assertion +Update rvls tracer +Merge remote-tracking branch 'my-github-rvls/updated-spike' into patch_local_rvls +Added to support FPU tests with SocSim, and extended the method to include for logging the trap address. This change allows more detailed information to be captured during trap handling. +- Removed the shift operation because it caused a mismatch in 32-bit mode. - For example, with pc=0xffffffff8000017c and state->pc=0xffffffff8000017c, - a 32-bit shift was applied, resulting in a mismatch. This was problematic in SocSim. +Update RVLS for compatibility with the updated Spike version +--- +Index: bindings/jni/rvls/jni/Frontend.java +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/bindings/jni/rvls/jni/Frontend.java b/bindings/jni/rvls/jni/Frontend.java +index 5f34cf7..f33c35b 100644 +--- a/bindings/jni/rvls/jni/Frontend.java ++++ b/bindings/jni/rvls/jni/Frontend.java +@@ -3,11 +3,11 @@ package rvls.jni; + import java.io.File; + + public class Frontend { +- public static native long newDisassemble(int xlen); ++ public static native long newDisassemble(long handle, int hartId); + public static native void deleteDisassemble(long handle); + public static native String disassemble(long handle, long instruction); + +- public static native long newContext(String workspace); ++ public static native long newContext(String workspace, boolean spikeLogFileOut); + public static native void deleteContext(long handle); + + public static native void spikeDebug(long handle, boolean enable); +@@ -16,12 +16,13 @@ public class Frontend { + public static native void newCpuMemoryView(long handle, int viewId, long readIds, long writeIds); + public static native void newCpu(long handle, int hartId, String isa, String priv, int physWidth, int memoryViewId); + public static native void loadElf(long handle, long offset, String path); ++ public static native void loadU32(long handle, long address, int data); + public static native void loadBin(long handle, long offset, String path); + public static native void setPc(long handle, int hartId, long pc); + public static native void writeRf(long handle, int hardId, int rfKind, int address, long data); + public static native void readRf(long handle, int hardId, int rfKind, int address, long data); + public static native boolean commit(long handle, int hartId, long pc); +- public static native boolean trap(long handle, int hartId, boolean interrupt, int code); ++ public static native boolean trap(long handle, int hartId, boolean interrupt, int code, long fault_addr); + public static native String getLastErrorMessage(long handle); + public static native void ioAccess(long handle, int hartId, boolean write, long address, long data, int mask, int size, boolean error); + public static native void setInterrupt(long handle, int hartId, int intId, boolean value); +Index: src/hart.cpp +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/src/hart.cpp b/src/hart.cpp +--- a/src/hart.cpp (revision b5c15e129f8168c2317b2c129aef91adf935bfeb) ++++ b/src/hart.cpp (revision d2fbaa45be212de7b1df2360ad9f3d4dcf78f965) +@@ -9,8 +9,9 @@ + + + +-SpikeIf::SpikeIf(CpuMemoryView *memory){ +- this->memory = memory; ++SpikeIf::SpikeIf(CpuMemoryView *memory, const cfg_t *cfg) ++ : memory(memory), cfg(cfg) { ++ + } + + Region* SpikeIf::getRegion(u64 address){ +@@ -104,6 +105,14 @@ + + return false; + } ++// Correct definition of get_cfg without virtual and override ++const cfg_t& SpikeIf::get_cfg() const { ++ return *cfg; ++} ++// Correct definition of get_harts without virtual and override ++const std::map& SpikeIf::get_harts() const { ++ return harts; ++} + // Callback for processors to let the simulation know they were reset. + void SpikeIf::proc_reset(unsigned id) { + // printf("proc_reset %d\n", id); +@@ -116,13 +125,19 @@ + + + +-Hart::Hart(u32 hartId, string isa, string priv, u32 physWidth, CpuMemoryView *memory, FILE *logs){ ++Hart::Hart(u32 hartId, string isa, string priv, u32 physWidth, CpuMemoryView *memory, FILE *logs) ++: isa_hart(isa), priv_hart(priv), cfg() // Initialiser le membre cfg ++{ + this->memory = memory; + this->physWidth = physWidth; +- sif = new SpikeIf(memory); + std::ofstream outfile ("/dev/null",std::ofstream::binary); +- proc = new processor_t(isa.c_str(), priv.c_str(), "", sif, hartId, false, logs, outfile); +- auto xlen = proc->get_xlen(); ++ this->cfg.isa = isa_hart.c_str(); ++ this->cfg.priv = priv_hart.c_str(); ++ this->cfg.misaligned = false; ++ this->cfg.pmpregions = 0; ++ this->cfg.hartids.push_back(hartId); ++ sif = new SpikeIf(memory, &this->cfg); ++ proc = new processor_t(isa_hart.c_str(), priv_hart.c_str(), &this->cfg, sif, hartId, false, logs, outfile); auto xlen = proc->get_xlen(); + proc->set_impl(IMPL_MMU_SV32, xlen == 32); + proc->set_impl(IMPL_MMU_SV39, xlen == 64); + proc->set_impl(IMPL_MMU_SV48, false); +@@ -131,6 +146,12 @@ + state = proc->get_state(); + state->csrmap[CSR_MCYCLE] = std::make_shared(proc, CSR_MCYCLE, 0); + state->csrmap[CSR_MCYCLEH] = std::make_shared(proc, CSR_MCYCLEH, 0); ++ for(int i = 0;i < 32;i++){ ++ float128_t tmp; ++ tmp.v[0] = -1; ++ tmp.v[1] = -1; ++ state->FPR.write(i, tmp); ++ } + } + + void Hart::close() { +@@ -153,6 +174,11 @@ + floatWriteData = data; + break; + case 4: ++ if((csrAddress == CSR_FCSR || csrAddress == CSR_FRM || csrAddress == CSR_FFLAGS) && address == CSR_MSTATUS){ ++ fsDirty = true; ++ fsCsrAddress = address; ++ break; ++ } + if((csrWrite || csrRead) && csrAddress != address){ + failure("duplicated CSR access \n"); + } +@@ -186,16 +212,41 @@ + } + + void Hart::physExtends(u64 &v){ +- v = (u64)(((s64)v<<(64-physWidth)) >> (64-physWidth)); ++ //v = (u64)(((s64)v<<(64-physWidth)) >> (64-physWidth)); ++ auto xlen = proc->get_xlen(); ++ if(xlen == 32){ ++ v = (u64)(((s64)v<<(64-physWidth)) >> (64-physWidth)); ++ } ++ else{ ++ v = (s64)v; ++ } + } + +-void Hart::trap(bool interrupt, u32 code){ ++void Hart::trap(bool interrupt, u32 code, u64 address){ + int mask = 1 << code; + auto fromPc = state->pc; ++ bool pageFault = !interrupt && (code == 12 || code == 13 || code == 15); ++// printf("DUT did trap at tval: 0x%lx pc: %lx code %d\n", address, fromPc, code); ++ if(pageFault){ ++ auto mmu = proc->get_mmu(); ++ mmu->flush_tlb(); ++ mmu->fault_fetch = code == 12; ++ mmu->fault_load = code == 13; ++ mmu->fault_store = code == 15; ++ mmu->fault_address = address; ++ } ++ + if(interrupt) state->mip->write_with_mask(mask, mask); + proc->step(1); + if(interrupt) state->mip->write_with_mask(mask, 0); ++ if(pageFault){ ++ auto mmu = proc->get_mmu(); ++ mmu->fault_fetch = false; ++ mmu->fault_load = false; ++ mmu->fault_store = false; ++ } + if(!state->trap_happened){ ++// printf("DUT did trap on %lx Code %d\n", fromPc, code); + failure("DUT did trap on %lx\n", fromPc); + } + +@@ -206,8 +257,9 @@ + } + + void Hart::commit(u64 pc){ +- auto shift = 64-proc->get_xlen(); +- if(pc != (state->pc << shift >> shift)){ ++// auto shift = 64-proc->get_xlen(); ++// if(pc != (state->pc << shift >> shift)){ ++ if(pc != state->pc ){ + failure("PC MISSMATCH dut=%lx ref=%lx\n", pc, state->pc); + } + +@@ -244,6 +296,10 @@ + } + } + ++// Check ++// printf("**************************************************\n"); ++// printf("PC = %016lx, ref=%lx, INST = %08lx\n", pc, state->pc, state->last_inst.bits()); ++ + //Run the spike model + proc->step(1); + memory->step(); +@@ -251,6 +307,7 @@ + //Sync back some CSR + state->mip->unlogged_write_with_mask(-1, 0); + if(csrRead){ ++// printf("Debug: CSR read - address: %x, data: %lx\n", csrAddress, csrReadData); + switch(csrAddress){ + case MIP: + case SIP: +@@ -262,53 +319,76 @@ + + //Checks + // printf("%016lx %08lx\n", pc, state->last_inst.bits()); +- assertTrue("DUT missed a trap", !state->trap_happened); +- for (auto item : state->log_reg_write) { +- if (item.first == 0) +- continue; ++ assertTrue("DUT missed a trap", !((u32)state->trap_happened)); ++ int i = 0; ++ for (auto item : state->log_reg_write) { ++// printf("LOG N°%u :Debug: LOOP rd = %u, data = %lx, dut_floatData: %lx, dut_floatValid: %u\n", i, (u32)item.first >> 4, item.second.v[0], floatWriteData, floatWriteValid); ++ if (item.first == 0) ++ continue; + +- u32 rd = item.first >> 4; +- switch (item.first & 0xf) { +- case 0: { //integer +- assertTrue("INTEGER WRITE MISSING", integerWriteValid); +- assertEq("INTEGER WRITE MISSMATCH", integerWriteData, item.second.v[0]); +- integerWriteValid = false; +- } break; +- case 1: { //float +- assertTrue("FLOAT WRITE MISSING", floatWriteValid); +- assertEq("FLOAT WRITE MISSMATCH", floatWriteData, item.second.v[0]); +- floatWriteValid = false; +- } break; +- case 4:{ //CSR +- u64 inst = state->last_inst.bits(); +- switch(inst){ +- case 0x30200073: //MRET +- case 0x10200073: //SRET +- case 0x00200073: //URET +- physExtends(state->pc); +- break; +- default:{ +- if((inst & 0x7F) == 0x73 && (inst & 0x3000) != 0){ +- assertTrue("CSR WRITE MISSING", csrWrite); +- assertEq("CSR WRITE ADDRESS", (u32)(csrAddress & 0xCFF), (u32)(rd & 0xCFF)); ++ i += 1; ++ ++ u32 rd = item.first >> 4; ++ ++// printf("LOG N°%u :Debug: LOOP rd = %u, data = %lx, dut_floatData: %lx, dut_floatValid: %u\n", i, rd, item.second.v[0], floatWriteData, floatWriteValid); ++ ++ switch (item.first & 0xf) { ++ case 0: { //integer ++// printf("Debug: Integer write detected, rd = %u, data = %lx, dut_integerData: %lx\n", rd, item.second.v[0], integerWriteData); ++ assertTrue("INTEGER WRITE MISSING", integerWriteValid); ++ assertEq("INTEGER WRITE MISSMATCH", integerWriteData, item.second.v[0]); ++ integerWriteValid = false; ++ } break; ++ ++ case 1: { //float ++// printf("item.second.v[0] = %lx, item.second.v[1] = %lx\n",item.second.v[0], item.second.v[1]); ++// printf("Debug: Float write detected, rd = %u, data = %lx, dut_floatData: %lx, dut_floatValid: %u\n", rd, item.second.v[0], floatWriteData, floatWriteValid); ++ assertTrue("FLOAT WRITE MISSING", floatWriteValid); ++ assertEq("FLOAT WRITE MISSMATCH", floatWriteData, item.second.v[0]); ++ floatWriteValid = false; ++ } break; ++ ++ case 4: { //CSR ++ u64 inst = state->last_inst.bits(); ++// printf("Debug: CSR write detected, inst = %lx, rd = %u\n", inst, rd); ++ ++ switch (inst) { ++ case 0x30200073: // MRET ++ case 0x10200073: // SRET ++ case 0x00200073: // URET ++// printf("Debug: Handling MRET/SRET/URET instruction\n"); ++ physExtends(state->pc); ++ break; ++ default: { ++ if ((inst & 0x7F) == 0x73 && (inst & 0x3000) != 0) { ++// printf("Debug: CSR instruction detected, inst = %lx, CSR address = %x\n", inst, csrAddress); ++ ++ if ((inst >> 20) == CSR_FCSR || (inst >> 20) == CSR_FRM || (inst >> 20) == CSR_FFLAGS) { ++ if (rd != CSR_MSTATUS) { ++ assertTrue("CSR WRITE MISSING", csrWrite); ++ assertEq("CSR WRITE ADDRESS", (u32)(csrAddress & 0xCFF), (u32)(rd & 0xCFF)); + // assertEq("CSR WRITE DATA", whitebox->robCtx[robId].csrWriteData, item.second.v[0]); +- } +- break; +- } +- ++ } ++ break; ++ } ++ assertTrue("CSR WRITE MISSING", csrWrite); ++ assertEq("CSR WRITE ADDRESS", (u32)(csrAddress & 0xCFF), (u32)(rd & 0xCFF)); + } +- csrWrite = false; +- } break; +- default: { +- failure("??? unknown spike trace %lx\n", item.first & 0xf); +- } break; +- } +- } ++ break; ++ } ++ } ++ csrWrite = false; ++ } break; ++ default: { ++ failure("??? unknown spike trace %lx\n", item.first & 0xf); ++ } break; ++ } ++} + +- csrRead = false; +- assertTrue("CSR WRITE SPAWNED", !csrWrite); +- assertTrue("INTEGER WRITE SPAWNED", !integerWriteValid); +- assertTrue("FLOAT WRITE SPAWNED", !floatWriteValid); ++csrRead = false; ++assertTrue("CSR WRITE SPAWNED", !csrWrite); ++assertTrue("INTEGER WRITE SPAWNED", !integerWriteValid); ++assertTrue("FLOAT WRITE SPAWNED", !floatWriteValid); + } + + void Hart::ioAccess(TraceIo io){ +@@ -325,5 +405,6 @@ + } + + void Hart::addRegion(Region r){ ++// printf("Type: %d Base %lx Size %lx\n", r.type, r.base, r.size); + sif->regions.push_back(r); + } +Index: src/hart.hpp +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/src/hart.hpp b/src/hart.hpp +--- a/src/hart.hpp (revision b5c15e129f8168c2317b2c129aef91adf935bfeb) ++++ b/src/hart.hpp (revision 313afc005d70c6f777981dad5fb3de88dcaeaf55) +@@ -51,8 +51,10 @@ + CpuMemoryView *memory; + queue ioQueue; + vector regions; ++ const cfg_t * const cfg; ++ const std::map harts; + +- SpikeIf(CpuMemoryView *memory); ++ SpikeIf(CpuMemoryView *memory, const cfg_t *cfg); + + virtual char* addr_to_mem(reg_t addr); + virtual bool mmio_fetch(reg_t addr, size_t len, u8* bytes); +@@ -61,6 +63,8 @@ + virtual bool mmio_store(reg_t addr, size_t len, const u8* bytes); + virtual void proc_reset(unsigned id); + virtual const char* get_symbol(uint64_t addr); ++ virtual const cfg_t& get_cfg() const override; ++ virtual const std::map& get_harts() const override; + bool isMem(u64 address); + bool isIo(u64 address); + bool isFetchable(u64 address); +@@ -69,6 +73,9 @@ + + class Hart{ + public: ++ std::string isa_hart; ++ std::string priv_hart; ++ cfg_t cfg; + SpikeIf *sif; + processor_t *proc; + state_t *state; +@@ -89,6 +96,9 @@ + u64 csrWriteData = 0; + u64 csrReadData = 0; + ++ u32 fsCsrAddress = 0; ++ bool fsDirty = false; ++ + bool scValid = false; + bool scFailure = false; + +@@ -99,7 +109,7 @@ + void writeRf(u32 rfKind, u32 address, u64 data); + void readRf(u32 rfKind, u32 address, u64 data); + void physExtends(u64 &v); +- void trap(bool interrupt, u32 code); ++ void trap(bool interrupt, u32 code, u64 address); + void commit(u64 pc); + void ioAccess(TraceIo io); + void setInt(u32 id, bool value); +Index: src/jni_frontend.cpp +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/src/jni_frontend.cpp b/src/jni_frontend.cpp +index 652c913..589334f 100644 +--- a/src/jni_frontend.cpp ++++ b/src/jni_frontend.cpp +@@ -12,8 +12,8 @@ + #include "disasm.h" + + +-static disassembler_t disasm32 = disassembler_t(32); +-static disassembler_t disasm64 = disassembler_t(64); ++//static disassembler_t disasm32 = disassembler_t(32); ++//static disassembler_t disasm64 = disassembler_t(64); + + #ifdef __cplusplus + extern "C" { +@@ -37,8 +37,8 @@ string toString(JNIEnv *env, jstring jstr){ + return str; + } + +-JNIEXPORT jlong JNICALL Java_rvls_jni_Frontend_newDisassemble(JNIEnv * env, jobject obj, int xlen){ +- return (jlong) new disassembler_t(xlen); ++JNIEXPORT jlong JNICALL Java_rvls_jni_Frontend_newDisassemble(JNIEnv * env, jobject obj, long handle, int hartId){ ++ return (jlong) new disassembler_t(&rv->proc->get_isa()); + } + + JNIEXPORT jstring JNICALL Java_rvls_jni_Frontend_disassemble(JNIEnv * env, jobject obj, long handle, long instruction){ +@@ -52,10 +52,10 @@ JNIEXPORT void JNICALL Java_rvls_jni_Frontend_deleteDisassemble(JNIEnv * env, jo + } + + +-JNIEXPORT jlong JNICALL Java_rvls_jni_Frontend_newContext(JNIEnv * env, jobject obj, jstring jworkspace){ ++JNIEXPORT jlong JNICALL Java_rvls_jni_Frontend_newContext(JNIEnv * env, jobject obj, jstring jworkspace, jboolean spikeLogFileOut){ + string workspace = toString(env, jworkspace); + auto *ctx = new Context(); +- ctx->spikeLogs = fopen((workspace + "/spike.log").c_str(), "w"); ++ ctx->spikeLogs = fopen((spikeLogFileOut ? (workspace + "/spike.log") : "/dev/null").c_str(), "w"); + return (jlong)ctx; + } + +@@ -98,6 +98,10 @@ rvlsJni(loadBin), long offset, jstring path){ + c->loadBin(toString(env, path), offset); + } + ++rvlsJni(loadU32), long address, int data){ ++ c->loadU32(address, data); ++} ++ + rvlsJni(setPc), int hartId, long pc){ + rv->setPc(pc); + } +@@ -117,9 +121,10 @@ rvlsJniBool(commit), int hartId, long pc) { + } + return true; + } +-rvlsJniBool(trap), int hartId, jboolean interrupt, int code) { ++ ++rvlsJniBool(trap), int hartId, jboolean interrupt, int code, long address) { + try{ +- rv->trap(interrupt, code); ++ rv->trap(interrupt, code, address); + } catch (const std::exception &e) { + c->lastErrorMessage = e.what(); + return false; +Index: src/ascii_frontend.cpp +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/src/ascii_frontend.cpp b/src/ascii_frontend.cpp +--- a/src/ascii_frontend.cpp (revision d006d160d570ce64d454a1dc979d2f0016963b66) ++++ b/src/ascii_frontend.cpp (revision 2650638f8c4aac96b7e068d01373b4836713b079) +@@ -82,9 +82,10 @@ + rv->ioAccess(io); + } else if (str == "trap") { + u32 hartId, code; ++ u64 address; + bool interrupt; +- f >> hartId >> interrupt >> code; +- rv->trap(interrupt, code); ++ f >> hartId >> interrupt >> code >> address; ++ rv->trap(interrupt, code, address); + } else if (str == "int") { + f >> str; + if(str == "set") { +Index: src/context.cpp +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/src/context.cpp b/src/context.cpp +--- a/src/context.cpp (revision d006d160d570ce64d454a1dc979d2f0016963b66) ++++ b/src/context.cpp (revision 2650638f8c4aac96b7e068d01373b4836713b079) +@@ -1,5 +1,10 @@ + #include "context.hpp" + ++void Context::loadU32(u64 address, u32 data){ ++ memory.write(address, 4, (uint8_t*)&data); ++} ++ ++ + void Context::loadElf(std::string path, u64 offset){ + auto elf = new Elf(path.c_str()); + elf->visitBytes([&](u8 data, u64 address) { +Index: src/context.hpp +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/src/context.hpp b/src/context.hpp +--- a/src/context.hpp (revision d006d160d570ce64d454a1dc979d2f0016963b66) ++++ b/src/context.hpp (revision 2650638f8c4aac96b7e068d01373b4836713b079) +@@ -18,7 +18,7 @@ + FILE *spikeLogs; + u64 time = 0xFFFFFFFFFFFFFFFF; + std::string lastErrorMessage; +- ++ void loadU32(u64 address, u32 data); + void loadElf(std::string path, u64 offset); + void loadBin(std::string path, u64 offset); + void cpuMemoryViewNew(u32 id, u64 readIds, u64 writeIds); +Index: bindings/spinal/rvls/spinal/Tracer.scala +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/bindings/spinal/rvls/spinal/Tracer.scala b/bindings/spinal/rvls/spinal/Tracer.scala +index 674065d..091a4cf 100644 +--- a/bindings/spinal/rvls/spinal/Tracer.scala ++++ b/bindings/spinal/rvls/spinal/Tracer.scala +@@ -72,12 +72,13 @@ class DummyBackend() extends TraceBackend{ + override def newCpuMemoryView(viewId: Int, readIds: Long, writeIds: Long) = {} + override def newCpu(hartId: Int, isa: String, priv: String, physWidth: Int, memoryViewId: Int) = {} + override def loadElf(offset: Long, path: File) = {} ++ override def loadU32(address: Long, data: Int) = {} + override def loadBin(offset: Long, path: File) = {} + override def setPc(hartId: Int, pc: Long) = {} + override def writeRf(hardId: Int, rfKind: Int, address: Int, data: Long) = {} + override def readRf(hardId: Int, rfKind: Int, address: Int, data: Long) = {} + override def commit(hartId: Int, pc: Long) = {} +- override def trap(hartId: Int, interrupt: Boolean, code: Int) = {} ++ override def trap(hartId: Int, interrupt: Boolean, code: Int, fault_addr: Long) = {} + override def ioAccess(hartId: Int, access: TraceIo) = {} + override def setInterrupt(hartId: Int, intId: Int, value: Boolean) = {} + override def addRegion(hartId: Int, kind : Int, base: Long, size: Long) = {} +@@ -105,8 +106,8 @@ class FileBackend(f : File) extends TraceBackend{ + log(f"rv commit $hartId $pc%016x\n") + } + +- override def trap(hartId: Int, interrupt : Boolean, code : Int): Unit ={ +- log(f"rv trap $hartId ${interrupt.toInt} $code\n") ++ override def trap(hartId: Int, interrupt : Boolean, code : Int, fault_addr: Long): Unit ={ ++ log(f"rv trap $hartId ${interrupt.toInt} $code $fault_addr%016x\n") + } + + override def writeRf(hartId: Int, rfKind: Int, address: Int, data: Long) = { +@@ -129,6 +130,10 @@ class FileBackend(f : File) extends TraceBackend{ + log(f"rv region add $hartId $kind $base%016x $size%016x\n") + } + ++ override def loadU32(address: Long, data: Int): Unit = { ++ log(f"U32 load $address%016x $data") ++ } ++ + override def loadElf(offset: Long, path: File) = { + log(f"elf load $offset%016x ${path.getAbsolutePath}\n") + } +@@ -174,10 +179,10 @@ class FileBackend(f : File) extends TraceBackend{ + override def close() = bf.close() + } + +-class RvlsBackend(workspace : File = new File(".")) extends TraceBackend{ ++class RvlsBackend(workspace : File = new File("."), spikeLogFileOut: Boolean) extends TraceBackend{ + import rvls.jni.Frontend + FileUtils.forceMkdir(workspace) +- val handle = Frontend.newContext(workspace.getAbsolutePath) ++ val handle = Frontend.newContext(workspace.getAbsolutePath,spikeLogFileOut) + + override def flush(): Unit = {} + override def close(): Unit = { +@@ -194,6 +199,7 @@ class RvlsBackend(workspace : File = new File(".")) extends TraceBackend{ + override def newCpuMemoryView(viewId: Int, readIds: Long, writeIds: Long): Unit = Frontend.newCpuMemoryView(handle, viewId, readIds, writeIds) + override def newCpu(hartId: Int, isa: String, priv: String, physWidth: Int, memoryViewId: Int): Unit = Frontend.newCpu(handle, hartId, isa, priv, physWidth, memoryViewId) + override def loadElf(offset: Long, path: File): Unit = Frontend.loadElf(handle, offset, path.getAbsolutePath) ++ override def loadU32(address: Long, data: Int): Unit = Frontend.loadU32(handle, address, data) + override def loadBin(offset: Long, path: File): Unit = Frontend.loadBin(handle, offset, path.getAbsolutePath) + override def setPc(hartId: Int, pc: Long): Unit = Frontend.setPc(handle, hartId, pc) + override def writeRf(hardId: Int, rfKind: Int, address: Int, data: Long): Unit = Frontend.writeRf(handle, hardId, rfKind, address, data) +@@ -201,7 +207,7 @@ class RvlsBackend(workspace : File = new File(".")) extends TraceBackend{ + override def commit(hartId: Int, pc: Long): Unit = if(!Frontend.commit(handle, hartId, pc)) { + throw new Exception(Frontend.getLastErrorMessage(handle)) + } +- override def trap(hartId: Int, interrupt: Boolean, code: Int): Unit = if(!Frontend.trap(handle, hartId, interrupt, code)) { ++ override def trap(hartId: Int, interrupt: Boolean, code: Int, fault_addr: Long): Unit = if(!Frontend.trap(handle, hartId, interrupt, code, fault_addr)){ + throw new Exception(Frontend.getLastErrorMessage(handle)) + } + override def ioAccess(hartId: Int, access: TraceIo): Unit = Frontend.ioAccess(handle, hartId, access.write, access.address, access.data, access.mask, access.size, access.error) diff --git a/src/main/scala/naxriscv/Gen.scala b/src/main/scala/naxriscv/Gen.scala index 2b69d38e..a66a2950 100644 --- a/src/main/scala/naxriscv/Gen.scala +++ b/src/main/scala/naxriscv/Gen.scala @@ -33,6 +33,7 @@ import spinal.lib.misc.plic.WishbonePlic import scala.collection.mutable.ArrayBuffer import scala.sys.exit +import scopt.OParser class NaxRiscv(val plugins : Seq[Plugin]) extends Component{ val database = new DataBase @@ -43,6 +44,7 @@ object Config{ def plugins(resetVector : BigInt = 0x80000000l, withRdTime : Boolean = true, ioRange : UInt => Bool = _(31 downto 28) === 0x1, + memRange : UInt => Bool = _(31), fetchRange : UInt => Bool = _(31 downto 28) =/= 0x1, aluCount : Int = 2, decodeCount : Int = 2, @@ -50,6 +52,7 @@ object Config{ withMmu : Boolean = true, withPerfCounters : Boolean = true, withSupervisor : Boolean = true, + withUser : Boolean = true, withDistributedRam : Boolean = true, xlen : Int = 32, withLoadStore : Boolean = true, @@ -91,6 +94,7 @@ object Config{ case true => new MmuPlugin( spec = if(xlen == 32) MmuSpec.sv32 else MmuSpec.sv39, ioRange = ioRange, + memRange = memRange, fetchRange = fetchRange, physicalWidth = 32 ) @@ -312,6 +316,7 @@ object Config{ plugins += new PrivilegedPlugin(PrivilegedConfig.full.copy( withRdTime = withRdTime, withSupervisor = withSupervisor, + withUser = withUser, withDebug = withDebug, debugTriggers = debugTriggers, hartId = hartId @@ -469,7 +474,31 @@ object Config{ } // ramstyle = "MLAB, no_rw_check" object Gen extends App{ +// configuration variables + var withRvc = false + var withFloat = false + var withDouble = false + var withSupervisor = true + var withUser = true + // Argument parser + val builder = OParser.builder[Unit] + val parser = { + import builder._ + OParser.sequence( + programName("Gen"), + head("Gen64", "1.0"), + opt[Unit]("withRvc").action((_, _) => withRvc = true).text("Enable RVC"), + opt[Unit]("withFloat").action((_, _) => withFloat = true).text("Enable float"), + opt[Unit]("withDouble").action((_, _) => withDouble = true).text("Enable double"), + opt[Unit]("noSupervisor").action((_, _) => withSupervisor = false).text("Disable supervisor mode"), + opt[Unit]("noUser").action((_, _) => withUser = false).text("Disable user mode") + ) + } + // Parse command line arguments + OParser.parse(parser, args, ()) + // Configuring LUT inputs LutInputs.set(6) + // Plugin configuration function def plugins = { val l = Config.plugins( withRdTime = false, @@ -477,14 +506,16 @@ object Gen extends App{ decodeCount = 2, debugTriggers = 0, withDedicatedLoadAgu = false, - withRvc = false, + withRvc = withRvc, withLoadStore = true, withMmu = true, withDebug = false, withEmbeddedJtagTap = false, jtagTunneled = false, - withFloat = false, - withDouble = false, + withFloat = withFloat, + withDouble = withDouble, + withSupervisor = withSupervisor, + withUser = withUser, withLsu2 = true, lqSize = 16, sqSize = 16, @@ -537,7 +568,31 @@ no mmu, no load/store => 7873 // ramstyle = "MLAB, no_rw_check" object Gen64 extends App{ + // configuration variables + var withRvc = false + var withFloat = false + var withDouble = false + var withSupervisor = true + var withUser = true + // Argument parser + val builder = OParser.builder[Unit] + val parser = { + import builder._ + OParser.sequence( + programName("Gen"), + head("Gen64", "1.0"), + opt[Unit]("withRvc").action((_, _) => withRvc = true).text("Enable RVC"), + opt[Unit]("withFloat").action((_, _) => withFloat = true).text("Enable float"), + opt[Unit]("withDouble").action((_, _) => withDouble = true).text("Enable double"), + opt[Unit]("noSupervisor").action((_, _) => withSupervisor = false).text("Disable supervisor mode"), + opt[Unit]("noUser").action((_, _) => withUser = false).text("Disable user mode") + ) + } + // Parse command line arguments + OParser.parse(parser, args, ()) + // Configuring LUT inputs LutInputs.set(6) + // Plugin configuration function def plugins = { val l = Config.plugins( sideChannels = false, //WARNING, FOR TEST PURPOSES ONLY, turn to false for real stuff <3 @@ -545,12 +600,14 @@ object Gen64 extends App{ withRdTime = false, aluCount = 2, decodeCount = 2, - withRvc = false, + withRvc = withRvc, withDebug = false, withEmbeddedJtagTap = false, debugTriggers = 4, - withFloat = false, - withDouble = false, + withFloat = withFloat, + withDouble = withDouble, + withSupervisor = withSupervisor, + withUser = withUser, lqSize = 16, sqSize = 16, asic = false diff --git a/src/main/scala/naxriscv/execute/CsrAccessPlugin.scala b/src/main/scala/naxriscv/execute/CsrAccessPlugin.scala index 520c73f6..1acd95c2 100644 --- a/src/main/scala/naxriscv/execute/CsrAccessPlugin.scala +++ b/src/main/scala/naxriscv/execute/CsrAccessPlugin.scala @@ -390,6 +390,7 @@ class CsrAccessPlugin(val euId: String)(var writebackAt: Int) extends ExecutionU val read = Bits(XLEN bits) val writeDone = Bool() val readDone = Bool() + val fsDirty = Bool() //Log that FS bits are set to dirty in MSTATUS when write in FCSR or FRM or FFLAGS })) csrAccess.valid := isFireing && SEL && !fsm.regs.trap csrAccess.robId := ROB.ID @@ -398,6 +399,7 @@ class CsrAccessPlugin(val euId: String)(var writebackAt: Int) extends ExecutionU csrAccess.read := fsm.regs.csrValue csrAccess.writeDone := fsm.regs.write csrAccess.readDone := fsm.regs.read + if(RVF.get){ csrAccess.fsDirty := List(CSR.FRM, CSR.FCSR, CSR.FFLAGS).map(fsm.regs.sels(_)).orR && fsm.regs.write } else { csrAccess.fsDirty := False} } } } diff --git a/src/main/scala/naxriscv/misc/MmuPlugin.scala b/src/main/scala/naxriscv/misc/MmuPlugin.scala index ee6006fc..cb37185f 100644 --- a/src/main/scala/naxriscv/misc/MmuPlugin.scala +++ b/src/main/scala/naxriscv/misc/MmuPlugin.scala @@ -82,7 +82,8 @@ object MmuSpec{ class MmuPlugin(var spec : MmuSpec, var physicalWidth : Int, var ioRange : UInt => Bool, - var fetchRange : UInt => Bool) extends Plugin with AddressTranslationService{ + var fetchRange : UInt => Bool, + var memRange : UInt => Bool) extends Plugin with AddressTranslationService{ override def withTranslation = true override def invalidatePort = setup.invalidatePort @@ -324,7 +325,7 @@ class MmuPlugin(var spec : MmuSpec, ALLOW_READ := True ALLOW_WRITE := True PAGE_FAULT := False - ACCESS_FAULT := ps.preAddress.drop(physicalWidth) =/= 0 + ACCESS_FAULT := ps.preAddress.drop(physicalWidth) =/= 0 || !(memRange(TRANSLATED) || IO) } ALLOW_EXECUTE clearWhen(!fetchRange(TRANSLATED)) diff --git a/src/main/scala/naxriscv/misc/PrivilegedPlugin.scala b/src/main/scala/naxriscv/misc/PrivilegedPlugin.scala index 89662784..8cbc34b1 100644 --- a/src/main/scala/naxriscv/misc/PrivilegedPlugin.scala +++ b/src/main/scala/naxriscv/misc/PrivilegedPlugin.scala @@ -436,8 +436,7 @@ class PrivilegedPlugin(var p : PrivilegedConfig) extends Plugin with PrivilegedS csr.read (CSR.MSTATUS, XLEN-1 -> mstatus.sd) csr.read (CSR.MIP, 11 -> mip.meip, 7 -> mip.mtip, 3 -> mip.msip) csr.readWrite(CSR.MIE, 11 -> mie.meie, 7 -> mie.mtie, 3 -> mie.msie) - if (p.withSupervisor) csr.readWrite(CSR.MSTATUS, 22 -> mstatus.tsr, 20 -> mstatus.tvm) - if (p.withUser) csr.readWrite(CSR.MSTATUS, 21 -> mstatus.tw) + if(p.withSupervisor) csr.readWrite(CSR.MSTATUS, 22 -> mstatus.tsr, 21 -> mstatus.tw, 20 -> mstatus.tvm) if(withFs) csr.readWrite(CSR.MSTATUS, 13 -> mstatus.fs) @@ -983,6 +982,7 @@ class PrivilegedPlugin(var p : PrivilegedConfig) extends Plugin with PrivilegedS val code = Verilator.public(CombInit(fsm.trap.code )) val interrupt = Verilator.public(CombInit(fsm.trap.interrupt)) val tval = Verilator.public(CombInit(reschedule.tval)) + val tval_width = TVAL_WIDTH.get } } diff --git a/src/main/scala/naxriscv/platform/NaxriscvProbe.scala b/src/main/scala/naxriscv/platform/NaxriscvProbe.scala index 440af0a6..38418735 100644 --- a/src/main/scala/naxriscv/platform/NaxriscvProbe.scala +++ b/src/main/scala/naxriscv/platform/NaxriscvProbe.scala @@ -6,6 +6,7 @@ import naxriscv.fetch.AlignerPlugin import naxriscv.frontend.DecoderPlugin import naxriscv.lsu2.Lsu2Plugin import naxriscv.misc.{CommitPlugin, PrivilegedPlugin, RegFilePlugin, RobPlugin} +import naxriscv.riscv.CSR import naxriscv.{NaxRiscv, riscv} import spinal.core.{Bool, assert} import spinal.core.sim._ @@ -15,13 +16,14 @@ import spinal.lib.system.tag.MemoryConnection import scala.collection.mutable.ArrayBuffer class NaxriscvTilelinkProbe(naxTl : TilelinkNaxRiscvFiber, hartId : Int) extends NaxriscvProbe(naxTl.thread.core, hartId){ - override def add(tracer: TraceBackend) = { - super.add(tracer) + override def add(tracer: TraceBackend, isa: String) = { + super.add(tracer, isa) val dSpec = MemoryConnection.getMemoryTransfers(naxTl.dBus) for(e <- dSpec){ e.transfers match { case t : M2sTransfers => if (t.nonEmpty){ tracer.addRegion(hartId, 0, e.mapping) +// println("NaxMappingD "+e.mapping) } } } @@ -31,6 +33,7 @@ class NaxriscvTilelinkProbe(naxTl : TilelinkNaxRiscvFiber, hartId : Int) extends e.transfers match { case t : M2sTransfers => if (t.nonEmpty){ tracer.addRegion(hartId, 1, e.mapping) +// println("NaxMappingP "+e.mapping) } } } @@ -55,6 +58,9 @@ class NaxriscvProbe(nax : NaxRiscv, hartId : Int){ var csrWriteData = -1l var csrReadData = -1l + //Set FS bits to dirty in MSTATUS when write in FCSR + var fsDirty = false + var lsuAddress = -1l var lsuLen = 0 var storeValid = false @@ -77,6 +83,7 @@ class NaxriscvProbe(nax : NaxRiscv, hartId : Int){ loadValid = false storeValid = false isSc = false + fsDirty = false } clear() }; @@ -88,7 +95,7 @@ class NaxriscvProbe(nax : NaxRiscv, hartId : Int){ val privPlugin = nax.framework.getService[PrivilegedPlugin] val robPlugin = nax.framework.getService[RobPlugin] val lsuPlugin = nax.framework.getService[Lsu2Plugin] - val intRf = nax.framework.getServiceWhere[RegFilePlugin](_.spec == riscv.IntRegFile) + val RF = nax.framework.getServicesOf[RegFilePlugin] val commitWb = commitPlugin.logic.whitebox val commitMask = commitWb.commit.mask.simProxy() @@ -98,9 +105,6 @@ class NaxriscvProbe(nax : NaxRiscv, hartId : Int){ val commitRobToPcRobId = commitWb.robToPc.robId.simProxy() val commitRobToPcValue = commitWb.robToPc.pc.map(_.simProxy()).toArray - val intRfEvents = intRf.logic.writeEvents.toArray - val intRfEventsValid = intRfEvents.map(_.valid.simProxy()) - val ioBus = lsuPlugin.peripheralBus val ioBusCmdValid = ioBus.cmd.valid.simProxy() val ioBusCmdReady = ioBus.cmd.ready.simProxy() @@ -150,11 +154,11 @@ class NaxriscvProbe(nax : NaxRiscv, hartId : Int){ val robArray = Array.fill(robPlugin.robSize)(new RobCtx) - - def add(tracer : TraceBackend) : this.type = { + def add(tracer : TraceBackend, isa : String) : this.type = { backends += tracer tracer.newCpuMemoryView(hartId, lsuPlugin.lqSize+1, lsuPlugin.sqSize) //+1 because AMO - tracer.newCpu(hartId, s"RV${xlen}IMA", "MSU", 32, hartId) + //tracer.newCpu(hartId, s"RV${xlen}IMAFDC", "MSU", 32, hartId) + tracer.newCpu(hartId, isa, "MSU", 32, hartId) var pc = 0x80000000l if(xlen == 32) pc = (pc << 32) >> 32 tracer.setPc(hartId, pc) @@ -173,15 +177,39 @@ class NaxriscvProbe(nax : NaxRiscv, hartId : Int){ } } - for(i <- 0 until intRfEvents.size){ - if(intRfEventsValid(i).toBoolean){ - val port = intRfEvents(i) - val robId = port.robId.toInt - val ctx = robArray(robId) - ctx.integerWriteValid = true - var data = port.data.toLong - if(xlen == 32) data = (data << 32) >> 32 - ctx.integerWriteData = data + for(rf <- RF){ + rf.spec match{ + case riscv.IntRegFile => { + val intRfEvents = rf.logic.writeEvents.toArray + val intRfEventsValid = intRfEvents.map(_.valid.simProxy()) + for (i <- 0 until intRfEvents.size) { + if (intRfEventsValid(i).toBoolean) { + val port = intRfEvents(i) + val robId = port.robId.toInt + val ctx = robArray(robId) + ctx.integerWriteValid = true + var data = port.data.toLong + if (xlen == 32) data = (data << 32) >> 32 + ctx.integerWriteData = data + // println(s"INTEGER: writeData: 0x${data.toHexString}\n") + } + } + } + case riscv.FloatRegFile => { + val floatRfEvents = rf.logic.writeEvents.toArray + val floatRfEventsValid = floatRfEvents.map(_.valid.simProxy()) + for (i <- 0 until floatRfEvents.size) { + if (floatRfEventsValid(i).toBoolean) { + val port = floatRfEvents(i) + val robId = port.robId.toInt + val ctx = robArray(robId) + ctx.floatWriteValid = true + var data = port.data.toLong + ctx.floatWriteData = data + // println(s"\nFLOAT${i.toInt}: writeData: 0x${data.toHexString}\n") + } + } + } } } @@ -194,6 +222,7 @@ class NaxriscvProbe(nax : NaxRiscv, hartId : Int){ ctx.csrReadDone = csrAccess.readDone.toBoolean ctx.csrWriteData = csrAccess.write.toLong ctx.csrReadData = csrAccess.read.toLong + ctx.fsDirty = csrAccess.fsDirty.toBoolean } } @@ -201,7 +230,11 @@ class NaxriscvProbe(nax : NaxRiscv, hartId : Int){ if(trapFire.toBoolean){ val code = trapWb.trap.code.toInt val interrupt = trapWb.trap.interrupt.toBoolean - backends.foreach(_.trap(hartId, interrupt, code)) + val tval : Long = trapWb.trap.tval.toLong + val tval_width : Int = trapWb.trap.tval_width.toInt + val fault_address : Long = ((tval << (64 - tval_width)) >> (64 - tval_width)) +// println(s"Address: 0x${fault_address.toHexString}\t Code de trap: $code\t tval: 0x${(trapWb.trap.tval.toLong).toHexString}\t tval_width: $tval_width") + backends.foreach(_.trap(hartId, interrupt, code, fault_address)) } } @@ -228,9 +261,14 @@ class NaxriscvProbe(nax : NaxRiscv, hartId : Int){ if(robCtx.integerWriteValid){ backends.foreach(_.writeRf(hartId, 0, 32, robCtx.integerWriteData)) } + if(robCtx.floatWriteValid){ +// println(s"floatWriteValid: ${robCtx.floatWriteValid}, hartId: $hartId, writeData: ${robCtx.floatWriteData.toHexString}\n") + backends.foreach(_.writeRf(hartId, 1, 32, robCtx.floatWriteData)) + } if(robCtx.csrValid){ if(robCtx.csrReadDone) backends.foreach(_.readRf(hartId, 4, robCtx.csrAddress, robCtx.csrReadData)) if(robCtx.csrWriteDone) backends.foreach(_.writeRf(hartId, 4, robCtx.csrAddress, robCtx.csrWriteData)) + if(robCtx.fsDirty) backends.foreach(_.writeRf(hartId, 4, CSR.MSTATUS, 0x8000)) } backends.foreach(_.commit(hartId, robCtx.pc)) commitsCallbacks.foreach(_(hartId, robCtx.pc)) diff --git a/src/main/scala/naxriscv/platform/PeripheralEmulator.scala b/src/main/scala/naxriscv/platform/PeripheralEmulator.scala index 0c002348..f5dfdd7a 100644 --- a/src/main/scala/naxriscv/platform/PeripheralEmulator.scala +++ b/src/main/scala/naxriscv/platform/PeripheralEmulator.scala @@ -1,12 +1,25 @@ package naxriscv.platform +import scala.collection.mutable.Queue + +import java.util.Arrays +import java.io.{PrintWriter, FileWriter, File} +import java.io.IOException + import spinal.core._ +import spinal.lib.misc.test + import spinal.lib.bus.tilelink import spinal.lib.bus.tilelink._ import spinal.lib.bus.tilelink.sim.{Monitor, MonitorSubscriber, SlaveDriver, TransactionA, TransactionD} import spinal.core.sim._ import scala.util.Random +import scala.collection.mutable.ArrayBuffer + +trait TestSchedule { + def activate(): Unit +} class PeripheralEmulator(bus : tilelink.Bus, mei : Bool, sei : Bool, cd : ClockDomain) extends MonitorSubscriber{ val monitor = new Monitor(bus, cd).add(this) @@ -26,16 +39,75 @@ class PeripheralEmulator(bus : tilelink.Bus, mei : Bool, sei : Bool, cd : ClockD val IO_FAULT_ADDRESS = 0x0FFFFFF0 val RANDOM = 0xA8 + val testScheduleQueue: Queue[TestSchedule] = Queue() + val customCin: Queue[Char] = Queue() + var putcHistory: String = "" + var putcTarget: String = null.asInstanceOf[String] + mei #= false sei #= false +def testScheduleQueueNext(): Unit = { + if (testScheduleQueue.isEmpty) return + val e = testScheduleQueue.dequeue() + e.activate() +} + +class WaitPutc(val putc: String) extends TestSchedule { + //println("TestSchedule: WaitPutc") + def activate(): Unit = { + putcTarget = putc + //println("WaitPutc: "+putcTarget) + } +} + +class DoSuccess(filePassPath: String, passTests: ArrayBuffer[String]) extends TestSchedule { + //println("TestSchedule: DoSuccess") + def activate(): Unit = { + // Create the “PASS” file + val passFile = new File(filePassPath, "PASS") + println("PASS FILE " + passFile) + passFile.createNewFile() + passTests += "buildroot_" + passFile.getParentFile.getName + simSuccess() + } +} + +class DoGetc(val getc: String) extends TestSchedule { + //println("TestSchedule: DoGetc") + def activate(): Unit = { + getc.foreach(e => customCin.enqueue(e)) + customCin.enqueue('\n') + testScheduleQueueNext() + } +} + +def endsWith(value: String, ending: String): Boolean = { + if (ending.length > value.length) { + false + } else { + value.takeRight(ending.length).reverseIterator.zip(ending.reverseIterator).forall { + case (v, e) => v == e + } + } +} override def onA(a: TransactionA) = { val d = TransactionD(a) a.opcode match { case Opcode.A.PUT_FULL_DATA => { d.opcode = Opcode.D.ACCESS_ACK a.address.toInt match { - case PUTC => print(a.data(0).toChar) + case PUTC => { + putcHistory += a.data(0).toChar + print(a.data(0).toChar) + if (putcTarget != null) { + if (endsWith(putcHistory, putcTarget)) { + putcTarget = null + testScheduleQueueNext() + putcHistory = "" + } + } + } case PUT_HEX => print(a.data.reverse.map(v => f"$v%02x").mkString("")) case MACHINE_EXTERNAL_INTERRUPT_CTRL => mei #= a.data(0).toBoolean case SUPERVISOR_EXTERNAL_INTERRUPT_CTRL => sei #= a.data(0).toBoolean @@ -59,7 +131,14 @@ class PeripheralEmulator(bus : tilelink.Bus, mei : Bool, sei : Bool, cd : ClockD case GETC => { if(System.in.available() != 0) { d.data(0) = System.in.read().toByte - } else { + } + else if (!customCin.isEmpty) { + val customCinFront = customCin.dequeue() + for(i <- 0 until d.bytes) d.data(i) = 0x0.toByte + //java.util.Arrays.fill(d.data, 0, d.bytes) + d.data(0) = customCinFront.toByte + } + else { for(i <- 0 until d.bytes) d.data(i) = 0xFF.toByte } } diff --git a/src/main/scala/naxriscv/platform/TilelinkNaxRiscvFiber.scala b/src/main/scala/naxriscv/platform/TilelinkNaxRiscvFiber.scala index 9c85cf90..fa650dc9 100644 --- a/src/main/scala/naxriscv/platform/TilelinkNaxRiscvFiber.scala +++ b/src/main/scala/naxriscv/platform/TilelinkNaxRiscvFiber.scala @@ -32,7 +32,12 @@ import java.nio.file.Files import scala.collection.mutable.ArrayBuffer object TilelinkNaxRiscvFiber{ - def getCoherentConfig(hartId: Int, asic: Boolean = false, xlen: Int = 32) = { + def getCoherentConfig(hartId: Int, + asic: Boolean = false, + xlen: Int = 32, + withRvc: Boolean = false, + withFloat: Boolean = false, + withDouble: Boolean = false) = { Config.plugins( withCoherency = true, withRdTime = false, @@ -41,7 +46,10 @@ object TilelinkNaxRiscvFiber{ ioRange = a => a(31 downto 28) === 0x1, hartId = hartId, asic = asic, - xlen = xlen + xlen = xlen, + withRvc = withRvc, + withFloat = withFloat, + withDouble = withDouble ) } } diff --git a/src/main/scala/naxriscv/platform/Tracer.scala b/src/main/scala/naxriscv/platform/Tracer.scala index f4b24c0a..095d1ba8 100644 --- a/src/main/scala/naxriscv/platform/Tracer.scala +++ b/src/main/scala/naxriscv/platform/Tracer.scala @@ -28,12 +28,13 @@ trait TraceBackend{ def newCpuMemoryView(viewId : Int, readIds : Long, writeIds : Long) def newCpu(hartId : Int, isa : String, priv : String, physWidth : Int, memoryViewId : Int) : Unit def loadElf(offset : Long, path : File) : Unit + def loadU32(address: Long, data: Int): Unit def loadBin(offset : Long, path : File) : Unit def setPc(hartId : Int, pc : Long): Unit def writeRf(hardId : Int, rfKind : Int, address : Int, data : Long) //address out of range mean unknown def readRf(hardId : Int, rfKind : Int, address : Int, data : Long) //address out of range mean unknown def commit(hartId : Int, pc : Long): Unit - def trap(hartId: Int, interrupt : Boolean, code : Int) + def trap(hartId: Int, interrupt: Boolean, code:Int, fault_addr: Long) def ioAccess(hartId: Int, access : TraceIo) : Unit def setInterrupt(hartId : Int, intId : Int, value : Boolean) : Unit def addRegion(hartId : Int, kind : Int, base : Long, size : Long) : Unit @@ -61,12 +62,13 @@ class DummyBackend() extends TraceBackend{ override def newCpuMemoryView(viewId: Int, readIds: Long, writeIds: Long) = {} override def newCpu(hartId: Int, isa: String, priv: String, physWidth: Int, memoryViewId: Int) = {} override def loadElf(offset: Long, path: File) = {} + override def loadU32(address: Long, data: Int): Unit = {} override def loadBin(offset: Long, path: File) = {} override def setPc(hartId: Int, pc: Long) = {} override def writeRf(hardId: Int, rfKind: Int, address: Int, data: Long) = {} override def readRf(hardId: Int, rfKind: Int, address: Int, data: Long) = {} override def commit(hartId: Int, pc: Long) = {} - override def trap(hartId: Int, interrupt: Boolean, code: Int) = {} + override def trap(hartId: Int, interrupt: Boolean, code: Int, fault_addr: Long) = {} override def ioAccess(hartId: Int, access: TraceIo) = {} override def setInterrupt(hartId: Int, intId: Int, value: Boolean) = {} override def addRegion(hartId: Int, kind : Int, base: Long, size: Long) = {} @@ -94,8 +96,8 @@ class FileBackend(f : File) extends TraceBackend{ log(f"rv commit $hartId $pc%016x\n") } - override def trap(hartId: Int, interrupt : Boolean, code : Int): Unit ={ - log(f"rv trap $hartId ${interrupt.toInt} $code\n") + override def trap(hartId: Int, interrupt : Boolean, code : Int, fault_addr: Long): Unit ={ + log(f"rv trap $hartId ${interrupt.toInt} $code $fault_addr%016x\n") } override def writeRf(hartId: Int, rfKind: Int, address: Int, data: Long) = { @@ -122,6 +124,10 @@ class FileBackend(f : File) extends TraceBackend{ log(f"elf load $offset%016x ${path.getAbsolutePath}\n") } + override def loadU32(address: Long, data: Int): Unit = { + log(f"U32 load $address%016x $data") + } + override def loadBin(offset: Long, path: File) = { log(f"bin load $offset%016x ${path.getAbsolutePath} \n") } @@ -163,10 +169,10 @@ class FileBackend(f : File) extends TraceBackend{ override def close() = bf.close() } -class RvlsBackend(workspace : File = new File(".")) extends TraceBackend{ +class RvlsBackend(workspace : File = new File("."), spikeLogFileOut: Boolean) extends TraceBackend{ import rvls.jni.Frontend FileUtils.forceMkdir(workspace) - val handle = Frontend.newContext(workspace.getAbsolutePath) + val handle = Frontend.newContext(workspace.getAbsolutePath,spikeLogFileOut) override def flush(): Unit = {} override def close(): Unit = { @@ -184,11 +190,12 @@ class RvlsBackend(workspace : File = new File(".")) extends TraceBackend{ override def newCpu(hartId: Int, isa: String, priv: String, physWidth: Int, memoryViewId: Int): Unit = Frontend.newCpu(handle, hartId, isa, priv, physWidth, memoryViewId) override def loadElf(offset: Long, path: File): Unit = Frontend.loadElf(handle, offset, path.getAbsolutePath) override def loadBin(offset: Long, path: File): Unit = Frontend.loadBin(handle, offset, path.getAbsolutePath) + override def loadU32(address: Long, data: Int): Unit = Frontend.loadU32(handle, address, data) override def setPc(hartId: Int, pc: Long): Unit = Frontend.setPc(handle, hartId, pc) override def writeRf(hardId: Int, rfKind: Int, address: Int, data: Long): Unit = Frontend.writeRf(handle, hardId, rfKind, address, data) override def readRf(hardId: Int, rfKind: Int, address: Int, data: Long): Unit = Frontend.readRf(handle, hardId, rfKind, address, data) override def commit(hartId: Int, pc: Long): Unit = if(!Frontend.commit(handle, hartId, pc)) throw new Exception() - override def trap(hartId: Int, interrupt: Boolean, code: Int): Unit = if(!Frontend.trap(handle, hartId, interrupt, code)) throw new Exception() + override def trap(hartId: Int, interrupt: Boolean, code: Int, fault_addr: Long): Unit = if(!Frontend.trap(handle, hartId, interrupt, code, fault_addr)) throw new Exception() override def ioAccess(hartId: Int, access: TraceIo): Unit = Frontend.ioAccess(handle, hartId, access.write, access.address, access.data, access.mask, access.size, access.error) override def setInterrupt(hartId: Int, intId: Int, value: Boolean): Unit = Frontend.setInterrupt(handle, hartId, intId, value) override def addRegion(hartId: Int, kind: Int, base: Long, size: Long): Unit = Frontend.addRegion(handle, hartId, kind, base, size) diff --git a/src/main/scala/naxriscv/platform/tilelinkdemo/SocDemo.scala b/src/main/scala/naxriscv/platform/tilelinkdemo/SocDemo.scala index 821305a0..c0792721 100644 --- a/src/main/scala/naxriscv/platform/tilelinkdemo/SocDemo.scala +++ b/src/main/scala/naxriscv/platform/tilelinkdemo/SocDemo.scala @@ -16,9 +16,26 @@ import spinal.lib.misc.plic.TilelinkPlicFiber import spinal.lib.system.tag.PMA // SocDemo is a little SoC made only for simulation purposes. -class SocDemo(cpuCount : Int, withL2 : Boolean = true, asic : Boolean = false, xlen : Int = 32) extends Component { +class SocDemo(cpuCount : Int, + withL2 : Boolean = true, + asic : Boolean = false, + xlen : Int = 32, + withRvc: Boolean = true, + withFloat: Boolean = true, + withDouble: Boolean = false + ) extends Component { // Create a few NaxRiscv cpu - val naxes = for(hartId <- 0 until cpuCount) yield new TilelinkNaxRiscvFiber(TilelinkNaxRiscvFiber.getCoherentConfig(hartId, asic = asic, xlen = xlen)) +val naxes = for(hartId <- 0 until cpuCount) yield + new TilelinkNaxRiscvFiber( + TilelinkNaxRiscvFiber.getCoherentConfig( + hartId, + asic = asic, + xlen = xlen, + withRvc = withRvc, + withFloat = withFloat, + withDouble = withDouble + ) + ) // As NaxRiscv may emit memory request to some unmapped memory space, we need to catch those with TransactionFilter val memFilter, ioFilter = new fabric.TransferFilter() diff --git a/src/main/scala/naxriscv/platform/tilelinkdemo/SocSim.scala b/src/main/scala/naxriscv/platform/tilelinkdemo/SocSim.scala index 66bcd7ad..c6a888b6 100644 --- a/src/main/scala/naxriscv/platform/tilelinkdemo/SocSim.scala +++ b/src/main/scala/naxriscv/platform/tilelinkdemo/SocSim.scala @@ -1,6 +1,6 @@ package naxriscv.platform.tilelinkdemo -import naxriscv.fetch.FetchCachePlugin +import naxriscv.fetch.{FetchCachePlugin, PcPlugin} import naxriscv.lsu.DataCachePlugin import naxriscv.platform.{FileBackend, NaxriscvProbe, NaxriscvTilelinkProbe, PeripheralEmulator, RvlsBackend} import spinal.core._ @@ -11,12 +11,15 @@ import spinal.lib.bus.tilelink.sim.{Checker, MemoryAgent} import spinal.lib.misc.Elf import spinal.lib.misc.test.{DualSimTracer, MultithreadedFunSuite, MultithreadedTester} +import java.nio.file.Paths import java.io.File import java.lang import scala.collection.mutable.ArrayBuffer - - - +import scala.collection.mutable.Queue +import scala.io.StdIn.{readLine} +import scala.util.matching.Regex +import java.io.{OutputStream, PrintStream, ByteArrayOutputStream} +import sys.process._ /* Multi core SoC simulation. @@ -58,12 +61,33 @@ object SocSim extends App { var iverilog = false var naxCount = 1 var xlen = 32 + var withRvc = false + var withFloat = false + var withDouble = false val bins = ArrayBuffer[(Long, String)]() val elfs = ArrayBuffer[String]() + val u32 = ArrayBuffer[(Long, Int)]() + val passTests = ArrayBuffer[String]() + val failTests = ArrayBuffer[String]() + var startSymbol : String = "_start" + var passSymbol : String = "pass" + var failSymbol : String = "fail" + var startAdd = 0 + var workspacePath : String = "simWorkspace" + var workspaceName: String = "SocDemo" + var workspaceOutputDir: String = "logs" + var workspaceOutputSubDir: String = "." + // Linux commands + var getc = ArrayBuffer[String]() + var putc = ArrayBuffer[String]() + var doSuccess = false assert(new scopt.OptionParser[Unit]("NaxRiscv") { help("help").text("prints this usage text") opt[Int]("xlen") action { (v, c) => xlen = v } + opt[Unit]("withRvc") action { (v, c) => withRvc = true } + opt[Unit]("withFloat") action { (v, c) => withFloat = true } + opt[Unit]("withDouble") action { (v, c) => withDouble = true } opt[Unit]("dual-sim") action { (v, c) => dualSim = true } opt[Unit]("trace") action { (v, c) => traceIt = true } opt[Unit]("no-rvls") action { (v, c) => withRvls = false } @@ -71,13 +95,44 @@ object SocSim extends App { opt[Unit]("asic") action { (v, c) => asic = true } opt[Unit]("iverilog") action { (v, c) => iverilog = true } opt[Int]("nax-count") action { (v, c) => naxCount = v } - opt[Seq[String]]("load-bin") unbounded() action { (v, c) => bins += (lang.Long.parseLong(v(0), 16) -> v(1)) } + opt[Seq[String]]("load-bin") unbounded() action { (v, c) => bins += (lang.Long.parseLong(v(1), 16) -> v(0)) } opt[String]("load-elf") unbounded() action { (v, c) => elfs += v } + opt[Seq[String]]("load-u32") unbounded() action { (v, c) => u32 += ( lang.Long.parseLong(v(1), 16) -> Integer.parseInt(v(0), 10))} + opt[String]("start-symbol") action { (v, c) => startSymbol = v } + opt[String]("pass-symbol") action { (v, c) => passSymbol = v } + opt[String]("fail-symbol") action { (v, c) => failSymbol = v } + opt[Int]("start-add") action { (v, c) => startAdd = v } + opt[String]("getc") unbounded() action { (v, c) => getc += v } + opt[String]("putc") unbounded() action { (v, c) => putc += v } + opt[Unit]("success") action { (v, c) => doSuccess = true } + opt[String]("workspace-path") unbounded() action { (v, c) => workspacePath = v } + opt[String]("workspace-name") unbounded() action { (v, c) => workspaceName = v } + opt[String]("workspace-output-dir") unbounded() action { (v, c) => workspaceOutputDir = v } + opt[String]("workspace-output-sub-dir") unbounded() action { (v, c) => workspaceOutputSubDir = v } }.parse(args, Unit).nonEmpty) - - + // Replace commas (",") with spaces (" ") + if(getc.nonEmpty){ + getc = getc.map(_.replaceAll(",", " ")) + } + if(putc.nonEmpty){ + putc = putc.map(_.replaceAll(",", " ")) + } + if (workspaceOutputSubDir == null){ + workspaceOutputSubDir = Paths.get(elfs.apply(0)).getParent().getFileName().toString() + }//else{ + // workspaceOutputSubDir = Paths.get(elfs.apply(0)).getParent().getFileName().toString() + "/" + workspaceOutputSubDir + //} + //println("workspaceName: "+ workspaceName) val sc = SimConfig sc.allOptimisation + //set workspace path and name + sc.workspacePath(workspacePath) + + sc.workspaceName(workspaceName) + + sc.wavePath(s"waves/${workspaceOutputSubDir}") + +// sc.normalOptimisation if(iverilog) { sc.withIVerilog withRvls = false //unsuported because of probe @@ -90,7 +145,7 @@ object SocSim extends App { // sc.addSimulatorFlag("--prof-exec") // Tweek the toplevel a bit - class SocDemoSim(cpuCount : Int) extends SocDemo(cpuCount, withL2 = withL2, asic = asic, xlen = xlen){ + class SocDemoSim(cpuCount : Int) extends SocDemo(cpuCount, withL2 = withL2, asic = asic, xlen = xlen, withRvc = withRvc, withFloat = withFloat, withDouble = withDouble) { setDefinitionName("SocDemo") // You can for instance override cache parameters of the CPU caches like that : naxes.flatMap(_.plugins).foreach{ @@ -139,14 +194,33 @@ object SocSim extends App { } val compiled = sc.compile(new SocDemoSim(cpuCount = naxCount)) - // How we want to run the test - dualSim match { - case true => DualSimTracer.withCb(compiled, window = 50000*10, seed = 2)(testIt) - case false => compiled.doSimUntilVoid(name = s"test", seed = 2){dut => disableSimWave(); testIt(dut, f => if(traceIt) f)} + for (file <- elfs) { + //println("ELF PATH: "+file) + // How we want to run the test + dualSim match { + case true => DualSimTracer.withCb(compiled, window = 50000 * 10, seed = 2, file, runBuildroot = false)(testIt) + case false => compiled.doSimUntilVoid(name = file.substring(file.lastIndexOf("/") + 1), seed = 2) { dut => disableSimWave(); testIt(dut, f => if (traceIt) f, file, runBuildroot = false) } + } + } + + if(getc.nonEmpty || putc.nonEmpty){ + val testName = workspaceOutputSubDir.replace("/", "_") + val file = "/"+testName + // How we want to run the test + dualSim match { + case true => DualSimTracer.withCb(compiled, window = 50000 * 10, seed = 2, file, runBuildroot = true)(testIt) + case false => compiled.doSimUntilVoid(name = testName, seed = 2) { dut => disableSimWave(); testIt(dut, f => if (traceIt) f, file, runBuildroot = true) } + } + } + + println(passTests.size + " TESTS PASSED" + " & " + failTests.size + " TESTS FAILED") + if (failTests.size > 0){ + println("LIST OF TESTS FAILED") + for(test <- failTests) println(test) } // Testbench code - def testIt(dut : SocDemoSim, onTrace : (=> Unit) => Unit = cb => {}): Unit = { + def testIt(dut : SocDemoSim, onTrace : (=> Unit) => Unit = cb => {}, elfFile : String, runBuildroot: Boolean = false): Unit = { val cd = dut.clockDomain cd.forkStimulus(10) //cd.forkSimSpeedPrinter(1.0) @@ -159,27 +233,69 @@ object SocSim extends App { } val pa = new PeripheralEmulator(dut.peripheral.emulated.node.bus, dut.peripheral.custom.mei, dut.peripheral.custom.sei, cd) + println("\nPeripheralEmulator IS created\n") + + //set workspace path and name + //val parentDir = Paths.get(elfFile).getParent().getFileName().toString() + /*var wavePath = s"${workspaceOutputDir}/${parentDir}" + println("WAVE PATH: " + wavePath) + sc.wavePath(wavePath)*/ + + //set output directory + //val parentDir = Paths.get(elfFile).getParent().getFileName().toString() + val lastDotIndex : Int = currentTestName.lastIndexOf('.') + val _currentTestName: String = if (lastDotIndex != -1) { + currentTestName.substring(0, lastDotIndex) + } else { + currentTestName + } + + val logsOutputPathFile = new File(compiled.compiledPath+"/"+ workspaceOutputDir+"/"+workspaceOutputSubDir, _currentTestName) + if(!logsOutputPathFile.exists()) { + logsOutputPathFile.mkdirs + } + println("logs_output: "+logsOutputPathFile); + // Rvls will check that the CPUs are doing things right - val rvls = withRvls generate new RvlsBackend(new File(compiled.compiledPath, currentTestName)) + val rvls = withRvls generate new RvlsBackend(logsOutputPathFile, traceIt) if(withRvls) { rvls.spinalSimFlusher(10 * 10000) rvls.spinalSimTime(10000) } + // isa extensions for spike + var isa = "" + // I : integer + if (xlen == 32) isa += "RV32I" + else isa += "RV64I" + // M : integer multiplication and division + // A : atomic + isa += "MA" + // F : adds floating-point registers, single-precision + if(withFloat) isa += "F" + // D : expends the floating-point registers, and adds double-precision + if(withDouble) isa += "D" + // C : compressed instruction extension + if(withRvc) isa += "C" // Collect traces from the CPUs behaviour val naxes = (!iverilog) generate dut.naxes.map(nax => new NaxriscvTilelinkProbe(nax, nax.getHartId())) - if(withRvls) naxes.foreach(_.add(rvls)) - + println() + if(withRvls) { + naxes.foreach(_.add(rvls, isa)) + rvls.debug() + } // Things to enable when we want to collect traces onTrace{ - enableSimWave() - if(withRvls) rvls.debug() - - val tracerFile = new FileBackend(new File(new File(compiled.compiledPath, currentTestName), "tracer.log")) + enableSimWave() + //val parentDir = Paths.get(elfFile).getParent().getFileName().toString() + val tracerFile = new FileBackend(new File(logsOutputPathFile, "tracer.log")) + println("tracerFile Path: "+compiled.compiledPath+"/"+ workspaceOutputDir+"/"+workspaceOutputSubDir+"/"+ _currentTestName); + //println("Name: " + parentDir); + //println("currentTestName: " + currentTestName); tracerFile.spinalSimFlusher(10 * 10000) tracerFile.spinalSimTime(10000) if(naxes != null) naxes.foreach { hart => - hart.add(tracerFile) + hart.add(tracerFile, isa) val r = hart.backends.reverse hart.backends.clear() hart.backends ++= r @@ -189,21 +305,72 @@ object SocSim extends App { // Load the binaries for((offset, file) <- bins){ ma.mem.loadBin(offset - 0x80000000l, file) - if(withRvls) rvls.loadBin(offset, new File(file)) + if(withRvls){ + rvls.loadBin(offset, new File(file)) + } } - // load elfs - for (file <- elfs) { - val elf = new Elf(new File(file), xlen) + // Load U32 + //if(u32.nonEmpty){ + for ((address, data) <- u32) { + ma.mem.write(address - 0x80000000l, data) + println("TEST: " + ma.mem.readInt(address).toHexString) + if (withRvls) { + rvls.loadU32(address, data) + } + } + //} + // Setting the function to run the Buildroot process + if (runBuildroot) { + // Iterate the buffers of the getc and putc arrays alternately. + while (getc.nonEmpty || putc.nonEmpty) { + if(getc.nonEmpty){ + // Add items from getc to the queue + getc.headOption.foreach { element => + pa.testScheduleQueue.enqueue(new pa.WaitPutc(element)) + println("GETC: " + element) + } + getc = getc.tail + } + // Add items from putc to the queue + if(putc.nonEmpty){ + putc.headOption.foreach { element => + pa.testScheduleQueue.enqueue(new pa.DoGetc(element)) + println("PUTC: " + element) + } + putc = putc.tail + } + } + // Add a success to the end of the queue + if(doSuccess == true){ + pa.testScheduleQueue.enqueue(new pa.DoSuccess(compiled.compiledPath+"/"+ workspaceOutputDir+"/"+workspaceOutputSubDir, passTests)) + println("DoSuccess: success") + } + // Press the first command in Linux + pa.testScheduleQueueNext() + } + else{ + // load elfs + val elf = new Elf(new File(elfFile), xlen) elf.load(ma.mem, 0x80000000l) - if(withRvls) rvls.loadElf(0, elf.f) + if (withRvls) rvls.loadElf(0, elf.f) + + if (startSymbol != null) { + dut.naxes.foreach { + nax => cd.onNextSampling { + nax.thread.core.framework.getService[PcPlugin].logic.fetchPc.pcReg #= BigInt(elf.getSymbolAddress(startSymbol).toInt.toHexString, 16) + startAdd + } + } + if (withRvls) dut.naxes.map(nax => rvls.setPc(nax.getHartId(), elf.getSymbolAddress(startSymbol) + startAdd)) + } - if(elf.getELFSymbol("pass") != null && elf.getELFSymbol("fail") != null) { - val passSymbol = elf.getSymbolAddress("pass") - val failSymbol = elf.getSymbolAddress("fail") - if(naxes != null) naxes.foreach { nax => + //if (elf.getELFSymbol(passSymbol) != null && elf.getELFSymbol("fail") != null) { + if (elf.getELFSymbol(passSymbol) != null) { + val getPassSymbol = elf.getSymbolAddress(passSymbol) + //val getFailSymbol = elf.getSymbolAddress("fail") + if (naxes != null) naxes.foreach { nax => nax.commitsCallbacks += { (hartId, pc) => - if (pc == passSymbol) delayed(1) { + if (pc == getPassSymbol) delayed(1) { dut.naxes.foreach { nax => println(s"Hart $hartId") nax.plugins.foreach { @@ -212,15 +379,48 @@ object SocSim extends App { case _ => } } - + println("TEST PASS") + passTests += elfFile.substring(elfFile.lastIndexOf("/") + 1) + // Delete “FAIL” file or directory + val failFile = new File(compiled.compiledPath+"/"+ workspaceOutputDir+"/"+workspaceOutputSubDir+"/"+_currentTestName, "FAIL") + if (failFile.exists()) { + failFile.delete() + } + // Create the “PASS” file + val passFile = new File(compiled.compiledPath+"/"+ workspaceOutputDir+"/"+workspaceOutputSubDir+"/"+_currentTestName, "PASS") + println("PASS FILE "+passFile) + passFile.createNewFile() + // The test is carried out successfully + simSuccess() + } + } + } + } + if(elf.getELFSymbol(failSymbol) != null){ + val getFailSymbol = elf.getSymbolAddress(failSymbol) + if (naxes != null) naxes.foreach { nax => + nax.commitsCallbacks += { (hartId, pc) => + if (pc == getFailSymbol) delayed(1) { + println("TEST FAIL") + failTests += elfFile.substring(elfFile.lastIndexOf("/") + 1) + // Delete “PASS” file or directory + val passFile = new File(compiled.compiledPath+"/"+ workspaceOutputDir+"/"+workspaceOutputSubDir+"/"+_currentTestName, "PASS") + if(passFile.exists()){ + passFile.delete() + } + // Create the “FAIL” file + val failFile = new File(compiled.compiledPath+"/"+ workspaceOutputDir+"/"+workspaceOutputSubDir+"/"+_currentTestName, "FAIL") + failFile.createNewFile() + //The test failed simSuccess() + //simFailure("Software reach the fail symbole :(") } - if (pc == failSymbol) delayed(1)(simFailure("Software reach the fail symbole :(")) } } } } println("Sim starting <3") + } } diff --git a/src/test/README.md b/src/test/README.md new file mode 100644 index 00000000..a4683e61 --- /dev/null +++ b/src/test/README.md @@ -0,0 +1,256 @@ +# NaxRiscv RISC-V Core + +NaxRiscv is a high-performance RISC-V implementation with configurable extensions and verification infrastructure. + +## Table of Contents + + - Quick Start + + - Directory Structure + + - Key Components + + - Installation Guide + + - Build Targets + + - Testing + + - Advanced Configuration + + - Maintenance + +## Quick Start +### Prerequisites + + - Git + + - GNU Make + + - Basic build tools (gcc, g++, etc.) + +```bash +# Clone repository +git clone https://github.com/SpinalHDL/NaxRiscv.git +cd NaxRiscv +export NAXRISCV=$(pwd) # Set environment variable +git checkout rvls-update + +# Full installation (toolchain + core + simulators) +make install +``` + +Successful installation will display: +`[SUCCESS] The entire toolchain is built successfully.` +`[SUCCESS] Verilator model for $(TARGET_NAX) generated.` + +--- + +### Directory Structure +```bash +NaxRiscv/ +├── ci/ # CI/CD and installation scripts +├── ext/ # Submodules +│ ├── NaxSoftware/ # Test suites (baremetal, buildroot, etc.) +│ ├── riscv-isa-sim/ # Spike simulator +│ ├── rvls/ # RVLS trace checker +│ └── SpinalHDL/ # Hardware description framework +├── simWorkspace/ # Simulation outputs +│ ├── regression/ # Regression test results +│ └── rvls/ # RVLS verification results +├── src/ +│ ├── main/ # Core implementation +│ │ ├── scala/ # SpinalHDL sources +│ │ └── verilog/ # Generated RTL +│ └── test/ # Verification infrastructure +├── toolchain/ # Installed tools (Verilator, SBT, JDK) +└── tmp/ # Temporary build files +``` + + +### Key Components +- **Core Files** + + - `NaxRiscv.v`: Main Verilog implementation + + - `NaxRiscvSynt.v`: Synthesizable version + + - `nax.h`: Core configuration parameters + +- **Configuration Patches** + + - **RVLS**: `rvls.patch`, `rvls.diff` + + - **SpinalHDL**: `adding_wavePath_simConfig.patch`, `Patch_DualSimTracer...patch` + + + +## Installation Guide +### Step-by-Step Setup + +```bash +# Initial toolchain (SBT + OpenJDK) +make install-toolchain-initial + +# Full toolchain (Verilator, Spike, RVLS) +make install-toolchain + +# Generate RTL for default target (rv64imafdcsu) +make NaxRiscv.v +``` +Please refer to the `README.md` file located in `NaxRiscv/src/test/cpp/naxriscv/` for a detailed, step-by-step guide on setting things up independently from the main Makefile. + +### Environment Variables +```bash +export JAVA_HOME=$NAXRISCV/toolchain/openjdk +export PATH=$JAVA_HOME/bin:$PATH +``` + + +### Build Targets +|ASCII |Description | +|-------------------------------|-----------------------------------| +|`make build-spike-rvls` |Rebuild Spike and RVLS simulators | +|`make build-simulator` |Rebuild regression simulator | +|`make verilate-NaxRiscv` |Generate Verilator model | + + +## Testing + +### Method 1: RVLS Core Regression Testing (SocSim + RVLS) + +### 1. Single Configuration Test +```bash +# Generate Makefile for rv64imafdcsu +cd $NAXRISCV/src/test/python/naxriscv/ +./testsGenRvls.py --xlen 64 --withFloat --withDouble --withRvc --naxCount 1 + +# Execute tests and generate report +cd ../../../.. +make test-rv64imafdcsu-all -j$(nproc) +make test-rv64imafdcsu-report +``` +#### Generated Artifacts: +```bash +simWorkspace/SocDemo/ +├── logs/ # Test execution logs +├── waves/ # Waveform captures +├── rtl/ # SoC Verilog source +└── verilator/ # Verilated model +``` + +### 2. Full Configuration Suite +```bash +# Run all predefined ISA configurations +cd $NAXRISCV +make clean-testsRvls-all +make test-rvls +``` +#### Supported Configurations: +- **RV32**: `imasu`, `imacsu`, `imafcsu`, `imafdcsu` +- **RV64**: `imasu`, `imacsu`, `imafcsu`, `imafdcsu` + +#### Test Automation Workflow +`NaxRiscvRvls.scala` handles: + + - Workspace setup in `simWorkspace/rvls/config_` + + - Makefile generation via `testsGenRvls.py` + + - Parallel test execution + + - Result collection in `logs/` and `waves/` + +--- +### Method 2: Core Regression Testing + +#### Run Full Test Suite: +```bash +cd $NAXRISCV +make test-regression # Run all regression tests +``` +#### NaxRiscvRegression.scala manages: + + - Workspace creation in `simWorkspace/regression/config_` + + - Test setup with `testsGen.py` + + - Batch execution of all `ISA` variants + + - Result storage in `output/` + +--- + +## Advanced Configuration + +### Target Architectures +```bash +# Supported targets (32/64-bit) +TARGETS_NAX=( + rv32imasu rv32imacsu rv32imafcsu rv32imafdcsu + rv64imasu rv64imacsu rv64imafcsu rv64imafdcsu +) + +# Example: Build RV32IMAFDCSU core +make TARGET_NAX=rv32imafdcsu NaxRiscv.v +``` + +### Feature Flags +|Extension |Parameters |Test Coverage | +|---------------------------------|-------------------------------------|------------------------ +|`RV32IMASU` or `RV64IMASU` | Base ISA no parameter needed |Base ISA | +|`RV32IMACSU` or `RV64IMACSU` |`--withRvc` |Base ISA + Compression | +|`RV32IMAFCSU` or `RV64IMAFCSU` |`--withRvc --withFloat` |+ Single-precision FP | +|`RV32IMAFDCSU` or `RV64IMAFDCSU` |`--withRvc --withFloat --withDouble` |+ Double-precision FP | + + +### Test Generation Parameters +#### Script Options: +```bash +cd $NAXRISCV/src/test/python/naxriscv +./testsGenRvls.py [OPTIONS] + +# ISA extension parameters +--xlen=INT : Specify the value of xlen 32 or 64 +--naxCount=INT : Number of NaxRiscv cores +--withRvc : Activate Compressed Instructions Extension +--withFloat : Activate Single-precision floating-point extension +--withDouble : Activate Double-precision floating-point extension +--noRva : Disable the Atomic extension, which is enabled by default +--no-l2 : Disable the l2 cache, which is enabled by default + +# Simulation parameters +--noRvls : Disable RVLS, NaxRiscv behaviour will not be checked +--dualSim : Enable dual lock step simulation to only trace the 50000 cycles before failure +--trace : Enable wave capture + +# Workspace definition +--workspacePath=DIR : Set the path in the simWorkspace folder - default="./simWorkspace/SocDemo" +--workspaceName=DIR : Set workspace name in workspaceDir folder - default='.' +--workspaceOutputDir=DIR : Set Name of output directory in socSim workspace - default='logs' +``` +--- + +### Maintenance + +#### Cleaning Commands +```bash +make clean-core # Remove generated RTL +make clean-toolchain # Remove toolchain installations +make clean-all # Full cleanup (install + sim + gen) +``` + +#### Submodule Management +```bash +# Update submodules +git submodule update --init --recursive +``` + +--- + +### Contribution + +Please ensure all scripts in `ci/` are executable: +```bash +chmod +x ci/*.sh +``` \ No newline at end of file diff --git a/src/test/cpp/naxriscv/makefile b/src/test/cpp/naxriscv/makefile index 495700dd..6775badc 100644 --- a/src/test/cpp/naxriscv/makefile +++ b/src/test/cpp/naxriscv/makefile @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: MIT +MAKEFILE_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) + NAXRISCV_REPO?=$(realpath ../../../..) NAXRISCV_GEN_FOLDER?=${NAXRISCV_REPO} NAXRISCV_VERILOG?=${NAXRISCV_GEN_FOLDER}/NaxRiscv.v @@ -9,7 +11,8 @@ NAXRISCV_HEADER?=${NAXRISCV_GEN_FOLDER}/nax.h SPIKE?=${NAXRISCV_REPO}/ext/riscv-isa-sim SPIKE_BUILD=$(realpath ${SPIKE}/build) - +SPIKE_INCLUDE=$(realpath ${SPIKE}/include) +SPIKE_LIB=$(realpath ${SPIKE}/lib) TRACE?=yes #LOCKSTEP?=yes @@ -19,9 +22,9 @@ ADDCFLAGS += -CFLAGS -I$(realpath ${SPIKE}/riscv) ADDCFLAGS += -CFLAGS -I$(realpath ${SPIKE}/fesvr) ADDCFLAGS += -CFLAGS -I$(realpath ${SPIKE}/softfloat) ADDCFLAGS += -CFLAGS -I$(realpath ${SPIKE_BUILD}) +ADDCFLAGS += -CFLAGS -I$(realpath ${SPIKE_INCLUDE}) # to include SDL2 & elfio - -ADDCFLAGS += -CFLAGS -lSDL2 +ADDCFLAGS += -LDFLAGS -L$(realpath ${SPIKE_LIB}) ADDCFLAGS += -LDFLAGS -lSDL2 ADDCFLAGS += -CFLAGS -g @@ -47,7 +50,7 @@ SPIKE_OBJS:=$(addprefix ${SPIKE_BUILD}/,${SPIKE_OBJS}) SRCS=src/main.cpp SRCS+=${SPIKE_OBJS} # g++ -shared -L. -Wl,--export-dynamic -L/usr/lib/x86_64-linux-gnu -Wl,-rpath,/opt/riscv/lib -o package.so spike.o libspike_main.a libriscv.a libdisasm.a libsoftfloat.a libfesvr.a libfdt.a -lpthread -ldl -lboost_regex -lboost_system -lpthread -lboost_system -lboost_regex - +SRCS+=${SPIKE_BUILD}/package.so #SRCS+=${SPIKE_BUILD}/libspike_main.a #SRCS+=${SPIKE_BUILD}/libriscv.a @@ -85,7 +88,7 @@ run: compile ./obj_dir/VNaxRiscv verilate: ${NAXRISCV_VERILOG} - verilator -cc ${NAXRISCV_VERILOG} -CFLAGS -std=c++14 -LDFLAGS -pthread ${ADDCFLAGS} ${FLAGS} --gdbbt ${VERILATOR_ARGS} -Wno-UNOPTFLAT -Wno-WIDTH --x-assign unique --exe ${SRCS} + verilator -cc ${NAXRISCV_VERILOG} -CFLAGS -std=c++14 -LDFLAGS -pthread ${ADDCFLAGS} ${FLAGS} --gdbbt ${VERILATOR_ARGS} -Wno-UNOPTFLAT -Wno-WIDTH --x-assign unique --exe ${SRCS} src/nax.h: ${NAXRISCV_HEADER} cp ${NAXRISCV_HEADER} src/nax.h diff --git a/src/test/cpp/naxriscv/src/main.cpp b/src/test/cpp/naxriscv/src/main.cpp index 305295f5..01f21a00 100644 --- a/src/test/cpp/naxriscv/src/main.cpp +++ b/src/test/cpp/naxriscv/src/main.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -434,7 +434,9 @@ class Soc : public SimElement{ nax->PrivilegedPlugin_io_int_machine_external = 0; nax->PrivilegedPlugin_io_int_machine_timer = 0; nax->PrivilegedPlugin_io_int_machine_software = 0; + #if SUPERVISOR == 1 nax->PrivilegedPlugin_io_int_supervisor_external = 0; + #endif for(auto e : socElements) e->onReset(); } @@ -819,7 +821,7 @@ class sim_wrap : public simif_t{ Memory memory; queue mmioDut; - + // Configuration and Harts const cfg_t * const cfg; const std::map harts; @@ -901,7 +903,7 @@ class RobCtx{ int csrAddress; RvData csrWriteData; RvData csrReadData; - +// bool fsDirty; IData branchHistory; int opId; @@ -911,6 +913,7 @@ class RobCtx{ csrValid = false; csrWriteDone = false; csrReadDone = false; +// fsDirty = false; floatFlags = 0; } }; @@ -1250,6 +1253,7 @@ class NaxWhitebox : public SimElement{ robCtx[robId].csrReadDone = nax->csrAccess_payload_readDone; robCtx[robId].csrWriteData = nax->csrAccess_payload_write; robCtx[robId].csrReadData = nax->csrAccess_payload_read; +// robCtx[robId].fsDirty = nax->csrAccess_payload_fsDirty; } // if(nax->FrontendPlugin_allocated_isFireing){ @@ -1773,7 +1777,7 @@ cfg_t cfg; void spikeInit(){ std::string isa; std::string priv; - + fptr = trace_ref ? fopen((outputDir + "/spike.log").c_str(),"w") : NULL; std::ofstream outfile ("/dev/null",std::ofstream::binary); @@ -1791,15 +1795,17 @@ void spikeInit(){ #endif if(RVC) isa += "C"; priv = "MSU"; + // Initialization of the config class cfg.isa = isa.c_str(); cfg.priv = priv.c_str(); cfg.misaligned = false; cfg.pmpregions = 0; cfg.hartids.push_back(0); + // Instantiation wrap = new sim_wrap(&cfg); - proc = new processor_t(isa.c_str(), priv.c_str(), &cfg, wrap, 0, false, fptr, outfile); + proc = new processor_t(isa.c_str(), "MSU", &cfg, wrap, 0, false, fptr, outfile); proc->set_impl(IMPL_MMU_SV32, XLEN == 32); proc->set_impl(IMPL_MMU_SV39, XLEN == 64); proc->set_impl(IMPL_MMU_SV48, false); @@ -2187,19 +2193,23 @@ void simLoop(){ assertEq("MISSMATCH PC", pc, spike_pc); for (auto item : state->log_reg_write) { if (item.first == 0) - continue; + continue; int rd = item.first >> 4; +// printf("**************************************************\n"); +// printf("PC: %lx\t inst: %lx\t type: %d\t REF@: %d\t DUT@: %d\t REF_data: %lx\t DUT_data: %lx\n", last_commit_pc, state->last_inst.bits(), (u32)(item.first & 0xf), rd, whitebox->robCtx[robId].csrAddress, item.second.v[0], whitebox->robCtx[robId].csrWriteData); switch (item.first & 0xf) { + case 0: { //integer assertTrue("INTEGER WRITE MISSING", whitebox->robCtx[robId].integerWriteValid); assertEq("INTEGER WRITE DATA", whitebox->robCtx[robId].integerWriteData, item.second.v[0]); } break; case 1: { //float +// printf("\n*** FLOAT WRITE DATA DUT=%lx REF=%lx ***\n\n", (u64)whitebox->robCtx[robId].floatWriteData, (u64) item.second.v[0]); //TODO FPU track float writes assertTrue("FLOAT WRITE MISSING", whitebox->robCtx[robId].floatWriteValid); if(whitebox->robCtx[robId].floatWriteData != (RvFloat)item.second.v[0]){ - printf("\n*** FLOAT WRITE DATA DUT=%lx REF=%lx ***\n\n", (u64)whitebox->robCtx[robId].floatWriteData, (u64) item.second.v[0]);\ +// printf("\n*** FLOAT WRITE DATA DUT=%lx REF=%lx ***\n\n", (u64)whitebox->robCtx[robId].floatWriteData, (u64) item.second.v[0]); failure(); } } break; @@ -2211,12 +2221,21 @@ void simLoop(){ case 0x00200073: //URET break; default: - if(inst & 0x7F == 0x73 && inst & 0x3000 != 0){ +// printf("PC: %lx\t inst: %lx\t type: %d\t REF@: %ld\t DUT@: %ld\t REF_data: %x\t DUT_data: %x\n", last_commit_pc, inst, item.first & 0xf, rd, whitebox->robCtx[robId].csrAddress, item.second.v[0], whitebox->robCtx[robId].csrWriteData); + if((inst & 0x7F) == 0x73 && (inst & 0x3000) != 0){ + assertTrue("CSR WRITE MISSING", whitebox->robCtx[robId].csrWriteDone); + + if((inst >> 20)==CSR_FCSR || (inst >> 20)==CSR_FRM || (inst >> 20)==CSR_FFLAGS){ + if(rd!=CSR_MSTATUS){ + assertEq("CSR WRITE ADDRESS", whitebox->robCtx[robId].csrAddress & 0xCFF, rd & 0xCFF); + } + break; + } assertEq("CSR WRITE ADDRESS", whitebox->robCtx[robId].csrAddress & 0xCFF, rd & 0xCFF); -// assertEq("CSR WRITE DATA", whitebox->robCtx[robId].csrWriteData, item.second.v[0]); + break; } - break; + } } break; default: { diff --git a/src/test/python/naxriscv/testsGenRvls.py b/src/test/python/naxriscv/testsGenRvls.py new file mode 100755 index 00000000..d7e5dabd --- /dev/null +++ b/src/test/python/naxriscv/testsGenRvls.py @@ -0,0 +1,1484 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: 2023 "Everybody" +# +# SPDX-License-Identifier: MIT + +import subprocess +import random +import os +import argparse +import sys + +if os.getenv("NAXRISCV_SEED"): + random.seed(os.getenv("NAXRISCV_SEED")) + +NAXRISCV_TEST_FPU_FACTOR = 0.1 +if os.getenv("NAXRISCV_TEST_FPU_FACTOR"): + NAXRISCV_TEST_FPU_FACTOR = float(os.getenv("NAXRISCV_TEST_FPU_FACTOR")) + + +riscv32_tests = [ + "rv32ui-p-lui", + "rv32ui-p-auipc", + "rv32ui-p-jal", + "rv32ui-p-jalr", + "rv32ui-p-beq", + "rv32ui-p-bge", + "rv32ui-p-bgeu", + "rv32ui-p-blt", + "rv32ui-p-bltu", + "rv32ui-p-bne", + "rv32ui-p-add", + "rv32ui-p-addi", + "rv32ui-p-and", + "rv32ui-p-andi", + "rv32ui-p-or", + "rv32ui-p-ori", + "rv32ui-p-sll", + "rv32ui-p-slli", + "rv32ui-p-slt", + "rv32ui-p-slti", + "rv32ui-p-sra", + "rv32ui-p-srai", + "rv32ui-p-srl", + "rv32ui-p-srli", + "rv32ui-p-sub", + "rv32ui-p-xor", + "rv32ui-p-xori" +] + +riscv64_tests = [ + "rv64ui-p-add", + "rv64ui-p-addi", + "rv64ui-p-addiw", + "rv64ui-p-addw", + "rv64ui-p-and", + "rv64ui-p-andi", + "rv64ui-p-auipc", + "rv64ui-p-beq", + "rv64ui-p-bge", + "rv64ui-p-bgeu", + "rv64ui-p-blt", + "rv64ui-p-bltu", + "rv64ui-p-bne", + "rv64ui-p-jal", + "rv64ui-p-jalr", + "rv64ui-p-or", + "rv64ui-p-ori", + "rv64ui-p-sll", + "rv64ui-p-slli", + "rv64ui-p-slliw", + "rv64ui-p-sllw", + "rv64ui-p-slt", + "rv64ui-p-slti", + "rv64ui-p-sltiu", + "rv64ui-p-sltu", + "rv64ui-p-sra", + "rv64ui-p-srai", + "rv64ui-p-sraiw", + "rv64ui-p-sraw", + "rv64ui-p-srl", + "rv64ui-p-srli", + "rv64ui-p-srliw", + "rv64ui-p-srlw", + "rv64ui-p-sub", + "rv64ui-p-subw", + "rv64ui-p-xor", + "rv64ui-p-xori", +] + +riscvTestMemory = [ + "rv32ui-p-lb", + "rv32ui-p-lbu", + "rv32ui-p-lh", + "rv32ui-p-lhu", + "rv32ui-p-lw", + "rv32ui-p-sb", + "rv32ui-p-sh", + "rv32ui-p-sw" +] + +riscv64TestMemory = [ + "rv64ui-p-lb", + "rv64ui-p-lbu", + "rv64ui-p-lh", + "rv64ui-p-lhu", + "rv64ui-p-lui", + "rv64ui-p-lw", + "rv64ui-p-lwu", + "rv64ui-p-ld", + "rv64ui-p-sb", + "rv64ui-p-sh", + "rv64ui-p-sw", + "rv64ui-p-sd", +] + + +riscvTestAmo = [ + "rv32ua-p-amoswap_w", + "rv32ua-p-amoor_w", + "rv32ua-p-amoand_w", + "rv32ua-p-amoxor_w", + "rv32ua-p-amoadd_w", + "rv32ua-p-amomaxu_w", + "rv32ua-p-amomax_w", + "rv32ua-p-amominu_w", + "rv32ua-p-amomin_w", +] + +riscv64TestAmo = [ + "rv64ua-p-amoadd_d", + "rv64ua-p-amoand_d", + "rv64ua-p-amomax_d", + "rv64ua-p-amomaxu_d", + "rv64ua-p-amomin_d", + "rv64ua-p-amominu_d", + "rv64ua-p-amoor_d", + "rv64ua-p-amoswap_d", + "rv64ua-p-amoxor_d", + "rv64ua-p-amoadd_w", + "rv64ua-p-amoand_w", + "rv64ua-p-amomaxu_w", + "rv64ua-p-amomax_w", + "rv64ua-p-amomin_w", + "rv64ua-p-amominu_w", + "rv64ua-p-amoor_w", + "rv64ua-p-amoswap_w", + "rv64ua-p-amoxor_w", +] + + +riscv32TestFloat = [ + "rv32uf-p-fmadd", + "rv32uf-p-fadd", + "rv32uf-p-fcmp", + "rv32uf-p-fcvt_w", + "rv32uf-p-ldst", + "rv32uf-p-recoding", + "rv32uf-p-fclass", + "rv32uf-p-fcvt", + "rv32uf-p-fdiv", + "rv32uf-p-fmin", + "rv32uf-p-move" +] + + +riscv32TestDouble = [ + "rv32ud-p-fmadd", + "rv32ud-p-fadd", + "rv32ud-p-fcvt", + "rv32ud-p-recoding", + "rv32ud-p-fclass", + "rv32ud-p-fcvt_w", + "rv32ud-p-fmin", + "rv32ud-p-fcmp", + "rv32ud-p-fdiv", + "rv32ud-p-ldst" +] + +riscv64TestFloat = [ + "rv64uf-p-fcvt", + "rv64uf-p-recoding", + "rv64uf-p-fclass", + "rv64uf-p-fadd", + "rv64uf-p-fmin", + "rv64uf-p-fmadd", + "rv64uf-p-ldst", + "rv64uf-p-fcmp", + "rv64uf-p-fcvt_w", + "rv64uf-p-fdiv", + "rv64uf-p-move" +] + + +riscv64TestDouble = [ + "rv64ud-p-ldst", + "rv64ud-p-move", + "rv64ud-p-structural", + "rv64ud-p-fcvt", + "rv64ud-p-fdiv", + "rv64ud-p-fclass", + "rv64ud-p-recoding", + "rv64ud-p-fmin", + "rv64ud-p-fmadd", + "rv64ud-p-fcvt_w", + "rv64ud-p-fadd", + "rv64ud-p-fcmp" +] + +riscvTestMul = [ + "rv32um-p-mul", + "rv32um-p-mulh", + "rv32um-p-mulhsu", + "rv32um-p-mulhu" +] + +riscvTestDiv = [ + "rv32um-p-div", + "rv32um-p-divu", + "rv32um-p-rem", + "rv32um-p-remu" +] + + +riscv64TestMul = [ + "rv64um-p-mul", + "rv64um-p-mulh", + "rv64um-p-mulhsu", + "rv64um-p-mulhu", + "rv64um-p-mulw", +] + +riscv64TestDiv = [ + "rv64um-p-div", + "rv64um-p-divu", + "rv64um-p-rem", + "rv64um-p-remu", + "rv64um-p-divuw", + "rv64um-p-divw", + "rv64um-p-remuw", + "rv64um-p-remw", +] + + + +riscv32TestRvc = [ + "rv32uc-p-rvc", +] + +riscv64TestRvc = [ + "rv64uc-p-rvc", +] + +def listPrefix(prefix, l): + return list(map(lambda x : prefix + x, l)) + +riscvArch32i = listPrefix("rv32i_m/I/", [ + "add-01", + "addi-01", + "and-01", + "andi-01", + "auipc-01", + "beq-01", + "bge-01", + "bgeu-01", + "blt-01", + "bltu-01", + "bne-01", + "fence-01", + "jal-01", + "jalr-01", + "lb-align-01", + "lbu-align-01", + "lh-align-01", + "lhu-align-01", + "lui-01", + "lw-align-01", + "or-01", + "ori-01", + "sb-align-01", + "sh-align-01", + "sll-01", + "slli-01", + "slt-01", + "slti-01", + "sltiu-01", + "sltu-01", + "sra-01", + "srai-01", + "srl-01", + "srli-01", + "sub-01", + "sw-align-01", + "xor-01", + "xori-01", +]) + +riscvArch32M = listPrefix("rv32i_m/M/", [ + "div-01", + "divu-01", + "mul-01", + "mulh-01", + "mulhsu-01", + "mulhu-01", + "rem-01", + "remu-01", +]) + +riscvArch32Zifencei = listPrefix("rv32i_m/Zifencei/", [ + "Fencei", +]) + +riscvArch32C = listPrefix("rv32i_m/C/", [ + "cadd-01", + "caddi-01", + "caddi16sp-01", + "caddi4spn-01", + "cand-01", + "candi-01", + "cbeqz-01", + "cbnez-01", + "cebreak-01", + "cj-01", + "cjal-01", + "cjalr-01", + "cjr-01", + "cli-01", + "clui-01", + "clw-01", + "clwsp-01", + "cmv-01", + "cnop-01", + "cor-01", + "cslli-01", + "csrai-01", + "csrli-01", + "csub-01", + "csw-01", + "cswsp-01", + "cxor-01", +]) + +riscvArch32Priv = listPrefix("rv32i_m/privilege/", [ + "ebreak", + "ecall", + "misalign1-jalr-01", + "misalign2-jalr-01", + "misalign-beq-01", + "misalign-bge-01", + "misalign-bgeu-01", + "misalign-blt-01", + "misalign-bltu-01", + "misalign-bne-01", + "misalign-jal-01", + "misalign-lh-01", + "misalign-lhu-01", + "misalign-lw-01", + "misalign-sh-01", + "misalign-sw-01", +]) + + + + + + +riscvArch64i = listPrefix("rv64i_m/I/", [ + "add-01", + "addi-01", + "addiw-01", + "addw-01", + "and-01", + "andi-01", + "auipc-01", + "beq-01", + "bge-01", + "bgeu-01", + "blt-01", + "bltu-01", + "bne-01", + "fence-01", + "jal-01", + "jalr-01", + "lb-align-01", + "lbu-align-01", + "ld-align-01", + "lh-align-01", + "lhu-align-01", + "lui-01", + "lw-align-01", + "lwu-align-01", + "or-01", + "ori-01", + "sb-align-01", + "sd-align-01", + "sh-align-01", + "sll-01", + "slli-01", + "slliw-01", + "sllw-01", + "slt-01", + "slti-01", + "sltiu-01", + "sltu-01", + "sra-01", + "srai-01", + "sraiw-01", + "sraw-01", + "srl-01", + "srli-01", + "srliw-01", + "srlw-01", + "sub-01", + "subw-01", + "sw-align-01", + "xor-01", + "xori-01", +]) + +riscvArch64M = listPrefix("rv64i_m/M/", [ + "div-01", + "divu-01", + "divuw-01", + "divw-01", + "mul-01", + "mulh-01", + "mulhsu-01", + "mulhu-01", + "mulw-01", + "rem-01", + "remu-01", + "remuw-01", + "remw-01", +]) + +riscvArch64Zifencei = listPrefix("rv64i_m/Zifencei/", [ + "Fencei", +]) + +riscvArch64C = listPrefix("rv64i_m/C/", [ + "cadd-01", + "caddi-01", + "caddi16sp-01", + "caddi4spn-01", + "caddiw-01", + "caddw-01", + "cand-01", + "candi-01", + "cbeqz-01", + "cbnez-01", + "cebreak-01", + "cj-01", + "cjalr-01", + "cjr-01", + "cld-01", + "cldsp-01", + "cli-01", + "clui-01", + "clw-01", + "clwsp-01", + "cmv-01", + "cnop-01", + "cor-01", + "csd-01", + "csdsp-01", + "cslli-01", + "csrai-01", + "csrli-01", + "csub-01", + "csubw-01", + "csw-01", + "cswsp-01", + "cxor-01", +]) + +riscvArch64Priv = listPrefix("rv64i_m/privilege/", [ + "ebreak", + "ecall", + "misalign1-jalr-01", + "misalign2-jalr-01", + "misalign-beq-01", + "misalign-bge-01", + "misalign-bgeu-01", + "misalign-blt-01", + "misalign-bltu-01", + "misalign-bne-01", + "misalign-jal-01", + "misalign-ld-01", + "misalign-lh-01", + "misalign-lhu-01", + "misalign-lw-01", + "misalign-lwu-01", + "misalign-sd-01", + "misalign-sh-01", + "misalign-sw-01", +]) + + +riscvArch32C = listPrefix("rv32i_m/C/", [ + "caddi16sp-01", + "cxor-01", + "cnop-01", + "cslli-01", + "cmv-01", + "clwsp-01", + "csrai-01", + "cj-01", + "cand-01", + "cebreak-01", + "cli-01", + "csub-01", + "caddi4spn-01", + "cbnez-01", + "clw-01", + "csw-01", + "candi-01", + "cswsp-01", + "cjalr-01", + "caddi-01", + "clui-01", + "cadd-01", + "cbeqz-01", + "cjr-01", + "csrli-01", + "cor-01", + "cjal-01", +]) + +riscvArch64C = listPrefix("rv64i_m/C/", [ + "caddi16sp-01", + "csubw-01", + "csdsp-01", + "cxor-01", + "cnop-01", + "cslli-01", + "cmv-01", + "clwsp-01", + "csrai-01", + "cj-01", + "cand-01", + "cebreak-01", + "cldsp-01", + "cli-01", + "csub-01", + "caddi4spn-01", + "cbnez-01", + "caddw-01", + "csd-01", + "clw-01", + "caddiw-01", + "csw-01", + "candi-01", + "cswsp-01", + "cjalr-01", + "caddi-01", + "cld-01", + "clui-01", + "cadd-01", + "cbeqz-01", + "cjr-01", + "csrli-01", + "cor-01", +]) + +fpuTestRvf32 = [ + [0, "fmv.x.w" , "f32"], + [31, "fmv.s.x" , "f32"], + [101, "fadd.s" , "f32"], + [102, "fsub.s" , "f32"], + [103, "fmul.s" , "f32"], + [104, "fdiv.s" , "f32"], + [105, "fsqrt.s" , "f32"], + [106, "fmadd.s" , "f32"], + [107, "fmsub.s" , "f32"], + [108, "fnmadd.s" , "f32"], + [109, "fnmsub.s" , "f32"], + [110, "fsgnj.s" , "f32"], + [111, "fsgnjn.s" , "f32"], + [112, "fsgnjx.s" , "f32"], + [113, "fmin.s" , "f32"], + [114, "fmax.s" , "f32"], + [115, "fle.s" , "f32"], + [116, "feq.s" , "f32"], + [117, "flt.s" , "f32"], + [118, "fclass.s" , "f32"], + [119, "fcvt.s.wu" , "ui32"], + [120, "fcvt.s.w" , "i32"], + [121, "fcvt.wu.s" , "f32"], + [122, "fcvt.w.s" , "f32"], +] + +fpuTestRvf64 = [ + [127, "fcvt.s.lu" , "ui64"], + [128, "fcvt.s.l" , "i64"], + [129, "fcvt.lu.s" , "f32"], + [130, "fcvt.l.s" , "f32"], + [31, "fmv.s.x_64", "f64"], + [202, "fcvt.s.wu_64" , "ui64"], + [203, "fcvt.s.w_64" , "i64"], +] + +fpuTestRvd32 = [ + [1, "fadd.d" , "f64"], + [2, "fsub.d" , "f64"], + [3, "fmul.d" , "f64"], + [4, "fdiv.d" , "f64"], + [5, "fsqrt.d" , "f64"], + [6, "fmadd.d" , "f64"], + [7, "fmsub.d" , "f64"], + [8, "fnmadd.d" , "f64"], + [9, "fnmsub.d" , "f64"], + [10, "fsgnj.d" , "f64"], + [11, "fsgnjn.d" , "f64"], + [12, "fsgnjx.d" , "f64"], + [13, "fmin.d" , "f64"], + [14, "fmax.d" , "f64"], + [15, "fle.d" , "f64"], + [16, "feq.d" , "f64"], + [17, "flt.d" , "f64"], + [18, "fclass.d" , "f64"], + [19, "fcvt.d.wu", "ui32"], + [20, "fcvt.d.w" , "i32"], + [21, "fcvt.wu.d", "f64"], + [22, "fcvt.w.d" , "f64"], + [23, "fcvt.d.s" , "f64"], + [24, "fcvt.s.d" , "f64"], +] + +fpuTestRvd64 = [ + [25, "fmv.x.d" , "f64"], + [26, "fmv.d.x" , "ui64"], + [27, "fcvt.d.lu", "ui64"], + [28, "fcvt.d.l" , "i64"], + [29, "fcvt.lu.d", "f64"], + [30, "fcvt.l.d" , "f64"], + + + [200, "fcvt.d.wu_64", "ui64"], + [201, "fcvt.d.w_64" , "i64"], +] + + +#naxriscv_gen_folder = os.getenv('NAXRISCV_GEN_FOLDER') +#naxriscv_header = os.getenv('NAXRISCV_HEADER') +#naxriscv_header_path = naxriscv_header if naxriscv_header else naxriscv_gen_folder+"/nax.h" if naxriscv_gen_folder else 'nax.h' + +# socSim workspace path +workspace_path = None +if os.getenv('WORKSPACE_PATH'): + workspace_path = os.getenv('WORKSPACE_PATH') #workspace_path = s"simWorkspace/rvls" +#print(f"\nSocDemo workspace path: {workspace_path}\n") + +# socSim workspace name +workspace_name = None +if os.getenv('WORKSPACE_NAME'): + workspace_name = os.getenv('WORKSPACE_NAME') #workspace_name = s"$name" (config_rv[XLEN][ISA]) + +# Name of output directory in socSim workspace +workspace_output_dir = None +if os.getenv('WORKSPACE_OUTPUT_DIR'): + workspace_output_dir = os.getenv('WORKSPACE_OUTPUT_DIR') #workspace_output_dir = s"logs" + +# Reference path to calculate relative path +#base_path = "simWorkspace" +# Relative path extraction +#socdemo_gen_folder_rel = os.path.relpath(workspace_path, base_path) +#print(f"SocDemo gen folder relatif: {socdemo_gen_folder_rel}\n") + +naxriscv_software = os.getenv('NAXRISCV_SOFTWARE') +if naxriscv_software is None: + naxriscv_software = 'ext/NaxSoftware' + +freertos_count = 4 +linux_count = 1 #verifier si l'image linux est disponible pour la config +nax_count = None +if os.getenv('FREERTOS_COUNT'): + freertos_count = int(os.getenv('FREERTOS_COUNT')) +if os.getenv('LINUX_COUNT'): + linux_count = int(os.getenv('LINUX_COUNT')) +if os.getenv('NAXRISCV_COUNT'): + nax_count = int(os.getenv('NAXRISCV_COUNT')) + +# Create an ArgumentParser object +parser = argparse.ArgumentParser(description="Parameters and simulation options for NaxRiscv") + +# Ajouter des arguments positionnels pour la liste +# Architecture parameters +parser.add_argument('--xlen', type=int, default=32, help='[XLEN] specify the value of xlen 32 or 64') +parser.add_argument('--naxCount', type=int, default=1, help='[NAXCOUNT] specify the number of Naxriscv cores') +parser.add_argument('--withRvc', action='store_true', help='Add [--withRvc] option to activate Compressed Instructions Extension') +parser.add_argument('--withFloat', action='store_true', help='Add [--withFloat] option to activate Single-precision floating-point extension') +parser.add_argument('--withDouble', action='store_true', help='Add [--withDouble] option to activate Double-precision floating-point extension') +parser.add_argument('--noRva', action='store_true', help='Add [--noRva] option to disable the Atomic extension, which is enabled by default') +parser.add_argument('--noL2', action='store_true', help='Add [--no-l2] option to disable the l2 cache, which is enabled by default') +# Simulation parameters +parser.add_argument('--noRvls', action='store_true', help='Add [--noRvls] option to Disable rvls, so you dont need to compile it, but the NaxRiscv behaviour will not be checked.') +parser.add_argument('--dualSim', action='store_true', help='Add [--dualSim] option to activate Double simulation, one preceding the other, which will trigger the second simulation to capture the wave if the first fails.') +parser.add_argument('--trace', action='store_true', help='Add [--trace] option to enable wave capture') +# Workspace definition +parser.add_argument('--workspacePath', type=str, default="./simWorkspace/SocDemo", help='Set the path in the simWorkspace folder') +parser.add_argument('--workspaceName', type=str, default='.', help='set workspace name in workspaceDir folder') +parser.add_argument('--workspaceOutputDir', type=str, default='logs', help='set Name of output directory in socSim workspace') + +# Parsing command line arguments +args = parser.parse_args() + +# Use argument values for constant initialisation +# Naxriscv core +xlen = args.xlen +rvc = args.withRvc +rvf = args.withFloat +rvd = args.withDouble +rva = not args.noRva +# Naxriscv SoC +naxCount = args.naxCount if nax_count is None else nax_count + +withL2 = not args.noL2 +# Naxriscv simulation +withRvls = not args.noRvls +dualSim = args.dualSim +traceIt = args.trace +# Set socSim workspace path +workspacePath = args.workspacePath if workspace_path is None else workspace_path +workspaceName = args.workspaceName if workspace_name is None else workspace_name +workspaceOutputDir = args.workspaceOutputDir if workspace_output_dir is None else workspace_output_dir +''' +file = open(naxriscv_header_path,mode='r') +naxHeader = file.read() +file.close() +import re + +def getInt(key): + return int(re.findall(key + " (\d+)", naxHeader)[0]) + +def getBoolean(key): + return len(re.findall(key + " true", naxHeader)) != 0 + + +xlen = getInt("XLEN") +rvc = getBoolean("RVC") +rvf = getBoolean("RVF") +rvd = getBoolean("RVD") +rva = getBoolean("RVA") +''' + + +if xlen == 64: + arch="rv64im" + archLinux="rv64im" +else: + arch="rv32im" + archLinux="rv32im" + +if rva: + arch += "a" + archLinux += "a" + + +if rvf: + arch += "f" + archLinux += "f" +if rvd: + arch += "d" + archLinux += "d" + +if rvc: + arch += "c" + archLinux += "c" + +# Config name +config_name = archLinux + "su" +print(f"ISA arch: {config_name}\n") + +naxSoftware = [ + ["lsu", "baremetal/lsu/build/rv32im/lsu.elf"], +] + +nax64Software = [] + +naxSoftwareRegular = [ + "machine", "supervisor", "mmu_sv32", "dhrystone", "coremark" +] +nax64SoftwareRegular = [ + "machine", "supervisor", "mmu_sv39", "dhrystone", "coremark", +] + +freertos = ["blocktim", "countsem", "EventGroupsDemo", "flop", "integer", "QPeek", + "QueueSet", "recmutex", "semtest", "TaskNotify", "dynamic", + "GenQTest", "PollQ", "QueueOverwrite", "QueueSetPolling", "sp_flop", "test1"] + +if(rvf or rvd): + freertos.remove("sp_flop") + freertos.remove("flop") + +for e in naxSoftwareRegular: + naxSoftware.append([e, f"baremetal/{e}/build/{arch}/{e}.elf"]) + + +for e in random.sample(freertos, freertos_count): + naxSoftware.append(["freertos/" + e, f"baremetal/freertosDemo/build/{e}/{arch}/freertosDemo.elf"]) + +for e in nax64SoftwareRegular: + nax64Software.append([e, f"baremetal/{e}/build/{arch}/{e}.elf"]) + +# naxSoftware.append(["coremark", f"baremetal/coremark/coremark_{arch}.elf"]) + +# Tests list +rules = [] +testsPass = [] +testsFail = [] +testsFast = [] +testsFpu = [] +testsBuildroot = [] +ouputs = [] +ouputsFast = [] +ouputsFpu = [] +ouputsBuildroot = [] + +def find_project_directory(): + # Get the directory of the current script + current_directory = os.path.dirname(__file__) + project_directory = os.path.dirname(__file__) + first_part_path = None + #print(f"directory of the current script: {current_directory}\n") + # Traverse up until finding the project directory + for _ in range(current_directory.count(os.path.sep)): # Iterate over the number of path separators + if "NaxRiscv" in os.listdir(current_directory) or "workspace" in os.listdir(current_directory): + naxriscv_path = os.path.join(current_directory, "NaxRiscv") + workspace_path = os.path.join(current_directory, "workspace") + #print(f"naxriscv_path: {naxriscv_path}\n") + #print(f"workspace_path: {workspace_path}\n") + # Check if one directory is a subdirectory of the other + if naxriscv_path.startswith(workspace_path) or workspace_path.startswith(naxriscv_path): + raise ValueError("Both 'NaxRiscv' and 'workspace' cannot be subdirectories of each other.") + first_part_path = naxriscv_path if os.path.exists(naxriscv_path) else workspace_path + break + # Move up one directory + current_directory = os.path.dirname(current_directory) + if first_part_path is None: + raise ValueError("Could not find both 'NaxRiscv' or 'workspace' like parent directories.") + #print(f"Returned directory : {project_directory}\n") + return project_directory, first_part_path + +def get_absolute_path(relative_path): + _, first_part_path = find_project_directory() + return os.path.join(first_part_path, relative_path) + + +# Get the testsGenRvls script path +#script_path = os.path.abspath(__file__) +#print(f"\nScript path : {script_path}\n") + +# Split the script_path and take the absolute path and first part of script_path +#script_path_parts = script_path.split(os.path.sep) +#print(f"Script path parts : {script_path_parts}\n") + +# Get the absolute path of the testsGenRvls script +#script_path_abs = os.path.sep + os.path.join(*script_path_parts[0:-1]) +#print(f"\nScript path abs: {script_path_abs}\n") + +# Find the index of 'NaxRiscv' in the path +#naxriscv_index = script_path_parts.index('NaxRiscv') +#naxriscv_index = script_path_parts.index('NaxRiscv') +#print(f"naxriscv_index : {naxriscv_index}\n") + +# Get the relative path of the testsGenRvls script +#script_path_rel = os.path.sep.join(script_path_parts[naxriscv_index:-1]) +#print(f"\nScript path relative: {script_path_rel}\n") + +# Extract the relevant part of the path until 'NaxRiscv' +#first_script_path = os.path.sep.join(script_path_parts[:naxriscv_index]) +#print(f"First script path : {first_script_path}\n") + +# Path to the tests directory +#tests_directory_relative = "NaxRiscv/src/test/python/naxriscv" +# Create the absolute path to the tests directory +#tests_directory_abs = os.path.join(first_script_path, tests_directory_relative) +#print(f"Absolute tests directory: {tests_directory_abs}\n") + +def makefile_include_testsrvls(): + try: + project_directory, _ = find_project_directory() + #print(f"\nMakefile must be created at {project_directory} ") + # Set path to Makefile + makefile_path = os.path.join(project_directory, "Makefile") + #print(f"Makefile path { makefile_path} ") + # Create the Makefile if it does not exist, otherwise replace it + with open(makefile_path, "w+") as f: + f.write("\n-include testsRvls.mk\n") + #print(f"\nMakefile created at {makefile_path} and the path to testsRvls.mk has been added.\n") + except Exception as e: + print(f"An error occurred: {e}") + + +def all_testsRvls(): + try: + tests_directory_abs = get_absolute_path("src/test/python/naxriscv") + all_testsrvls_mk_path = os.path.join(tests_directory_abs, "all_testsRvls.mk") + #print(f"All MK in one : {all_testsrvls_mk_path}\n") + project_directory, _ = find_project_directory() + include_line = f"\n-include {os.path.join(project_directory, 'testsRvls.mk')}\n" + + if not os.path.exists(all_testsrvls_mk_path): + # If the file does not exist, it is created + with open(all_testsrvls_mk_path, "w") as f: + f.write(include_line) + #print(f"File {all_testsrvls_mk_path} created.\n") + else: + # If the file exists, read its contents + with open(all_testsrvls_mk_path, "r") as f: + file_content = f.read() + + # Filter lines that appear to contain paths + path_lines = [line for line in file_content.splitlines() if '-include' in line] + + # If more than 8 paths are present or just one path, replace the file + if len(path_lines) > 8 : + with open(all_testsrvls_mk_path, "w") as f: + f.write(include_line) + #print(f"File {all_testsrvls_mk_path} replaced with only one include.\n") + else: + if include_line not in file_content: + with open(all_testsrvls_mk_path, "a") as f: + f.write(include_line) + #print(f"Adding sub-makefile path to {all_testsrvls_mk_path}.\n") + #else: + #print(f"Sub-makefile already present in {all_testsrvls_mk_path}.\n") + except Exception as e: + print(f"An error occurred: {e}") + +makefile_include_testsrvls() +all_testsRvls() + +#makefile_include_testsrvls() + +#def all_testsRvls(): +# # Vérifie si le fichier all_testsRvls.mk existe dans le répertoire de tests +# all_testsrvls_mk_path = os.path.join(tests_directory_abs, "all_testsRvls.mk") +# print(f"All MK in one : {all_testsrvls_mk_path}\n") +# +# if not os.path.exists(all_testsrvls_mk_path): +# # If the file does not exist, create it +# with open(all_testsrvls_mk_path, "w") as f: +# f.write(f"\n-include {script_path_abs}/testsRvls.mk\n") +# print(f"File {all_testsrvls_mk_path} created.\n") +# else: +# # If the file exists, check if the line already exists +# with open(all_testsrvls_mk_path, "r") as f: +# file_content = f.read() +# if f"\n-include {script_path_abs}/testsRvls.mk\n" not in file_content: +# # If the line doesn't exist, add it at the end +# with open(all_testsrvls_mk_path, "a") as f: +# f.write(f"-include {script_path_abs}/testsRvls.mk\n") +# print(f"Adding sub-makefile path to {all_testsrvls_mk_path}.\n") +# else: +# print(f"Sub-makefile already present in {all_testsrvls_mk_path}.\n") + +#all_testsRvls() + +def get_simulation_command(): + # Build the execution command line + command_list = [ + "sbt", + "\"runMain naxriscv.platform.tilelinkdemo.SocSim", + "--xlen", str(xlen), + "--nax-count", str(naxCount) + ] + if rvc: + command_list.append("--withRvc") + if rvf: + command_list.append("--withFloat") + if rvd: + command_list.append("--withDouble") + #if dualSim: + # command_list.append("--dual-sim") + #if traceIt: + # command_list.append("--trace") + if not withRvls: + command_list.append("--no-rvls") + if not withL2: + command_list.append("--no-l2") + command_list.extend([ + "--workspace-path", str(workspacePath), + "--workspace-name", str(workspaceName), + "--workspace-output-dir", str(workspaceOutputDir) + ]) + # Use "${ARGS}" to add additional arguments when running the script + command_list.append("${ARGS}") + + return command_list + +with open('testsRvls.mk', 'w') as f: + + def rvTest(name, elf_list=None, elf=None, passs="pass", start="test_2", startAdd = 0): + # Sim Workspace Directory + #simWorkspaceDir = riscv_tests + # Get the basic command to start the SoC simulation + command_list = get_simulation_command() + if dualSim: + command_list.append("--dual-sim") + if traceIt: + command_list.append("--trace") + # Build the execution command line + command_list += [ + "--workspace-output-sub-dir", "riscv-tests/"+name, + "--start-symbol", start, + "--pass-symbol", str(passs), + "--start-add", str(startAdd) + ] + config_rule = name+"_"+config_name + f.write(f"{config_rule}:\n") + rules.append(config_rule) + testsFast.append(config_rule) + # Define output directory + outputDir = f"{workspacePath}/{workspaceName}/{workspaceOutputDir}/riscv-tests/{name}/" + # Load ELF files and write test output rules + if not elf_list: + outputDirectory = outputDir + elf + ouputs.append(outputDirectory) + ouputsFast.append(outputDirectory) + #f.write(f"{outputDirectory}/PASS:\n") + testsPass.append(outputDirectory +"/PASS") + testsFail.append(outputDirectory +"/FAIL") + # Add ELF list to command line + command_list.append("--load-elf") + command_list.append(f" {naxriscv_software}/riscv-tests/{elf}") + else: + for index, elf in enumerate(elf_list): + outputDirectory = outputDir + elf + ouputs.append(outputDirectory) + ouputsFast.append(outputDirectory) + #f.write(f"{outputDirectory}/PASS:\n") + testsPass.append(outputDirectory +"/PASS") + testsFail.append(outputDirectory +"/FAIL") + # Add ELF list to command line + command_list.append("--load-elf") + command_list.append(f" {naxriscv_software}/riscv-tests/{elf}") + # Join the list to create the chain + command_string = "\t" + " ".join(command_list) + # Writing string to file + f.write(command_string) + f.write(f"\"\n\n") + + def rvArch(name, elf_list=None, elf=None, xlen=xlen, naxCount = 1, passs="pass", dualSim=False, traceIt=False, withRvls=True, withL2=True): + # Get the basic command to start the SoC simulation + command_list = get_simulation_command() + if dualSim: + command_list.append("--dual-sim") + if traceIt: + command_list.append("--trace") + # Build the execution command line + command_list += [ + "--workspace-output-sub-dir", "riscv-arch-test/"+name, + "--pass-symbol", str(passs) + ] + config_rule = name+"_"+config_name + f.write(f"{config_rule}:\n") + rules.append(config_rule) + testsFast.append(config_rule) + # Define output directory + #outputDir = "output/riscv_arch/" + name + "/" + outputDir = f"{workspacePath}/{workspaceName}/{workspaceOutputDir}/riscv-arch-test/{name}/" + + # Load elf files and write test output rules + if not elf_list: + outputDirectory = outputDir + os.path.basename(elf) # Use os.path.basename to get just the file name + ouputs.append(outputDirectory) + ouputsFast.append(outputDirectory) + #f.write(f"{outputDirectory}/PASS:\n") + testsPass.append(outputDirectory +"/PASS") + testsFail.append(outputDirectory +"/FAIL") + # Add ELF list to command line + command_list.append("--load-elf") + command_list.append(f" {naxriscv_software}/riscv-arch-test/{elf}.elf") + else: + for index, elf in enumerate(elf_list): + outputDirectory = outputDir + os.path.basename(elf) # Use os.path.basename to get just the file name + ouputs.append(outputDirectory) + ouputsFast.append(outputDirectory) + #f.write(f"{outputDirectory}/PASS:\n") + testsPass.append(outputDirectory +"/PASS") + testsFail.append(outputDirectory +"/FAIL") + # Add ELF list to command line + command_list.append("--load-elf") + command_list.append(f" {naxriscv_software}/riscv-arch-test/{elf}.elf") + # Join the list to create the chain + command_string = "\t" + " ".join(command_list) + # Writing string to file + f.write(command_string) + f.write(f"\"\n\n") + + def fpuTest(testListName, name, vector, testId): + # Get the basic command to start the SoC simulation + command_list = get_simulation_command() + if dualSim: + command_list.append("--dual-sim") + if traceIt: + command_list.append("--trace") + + command_list += [ + "--workspace-output-sub-dir", "nax/fpu-test/"+testListName+"/"+name + ] + config_rule = name+"_"+config_name + f.write(f"{config_rule}:\n") + rules.append(config_rule) + testsFpu.append(config_rule) + # Define output directory + #outputDir = "output/nax/fpu_test/" + name + outputDir = f"{workspacePath}/{workspaceName}/{workspaceOutputDir}/nax/fpu-test/{testListName}/{name}/fpu_test" + testsPass.append(outputDir +"/PASS") + testsFail.append(outputDir +"/FAIL") + # Write test output rules + ouputs.append(outputDir) + ouputsFpu.append(outputDir) + testCount = int(0x50000*NAXRISCV_TEST_FPU_FACTOR) + if "sqrt" in name: + testCount //= 16 + if "div" in name: + testCount //= 8 + # Build the execution command line + command_list += [ + "--load-bin", f"{naxriscv_software}/baremetal/fpu_test/vector/{vector}.bin,90000000", + "--load-u32", f"{testCount},A0000000", + "--load-u32", f"{testId},A0000004", + "--load-elf", f"{naxriscv_software}/baremetal/fpu_test/build/{archLinux}/fpu_test.elf", + "--pass-symbol", "pass" + ] + # Join the list to create the chain + command_string = "\t" + " ".join(command_list) + # Writing string to file + f.write(command_string) + f.write(f"\"\n\n") + + def fpuTest3(): + # Get the basic command to start the SoC simulation + command_list = get_simulation_command() + if dualSim: + command_list.append("--dual-sim") + if traceIt: + command_list.append("--trace") + # Define FPU test name + name = "nax" + command_list += [ + "--workspace-output-sub-dir", name + ] + config_rule = "fpu_test3"+"_"+config_name + f.write(f"{config_rule}:\n") + rules.append(config_rule) + testsFpu.append(config_rule) + + # Define output directory + outputDir = f"{workspacePath}/{workspaceName}/{workspaceOutputDir}/{name}/fpu_test3" + # Define test output rules + testsPass.append(outputDir +"/PASS") + testsFail.append(outputDir +"/FAIL") + ouputs.append(outputDir) + ouputsFpu.append(outputDir) + # Build the execution command line + command_list += [ + "--load-bin", f"{naxriscv_software}/baremetal/fpu_test/vector/f32.bin,90000000", + "--load-elf", f"{naxriscv_software}/baremetal/fpu_test3/build/{archLinux}/fpu_test3.elf", + "--pass-symbol", "pass" + #"--fail-symbol", "fail" + ] + # Join the list to create the chain + command_string = "\t" + " ".join(command_list) + # Writing string to file + #f.write(f"{outputDir}/PASS:\n") + f.write(command_string) + f.write(f"\"\n\n") + + def regularSoftware(name, path): + # Get the basic command to start the SoC simulation + command_list = get_simulation_command() + if dualSim: + command_list.append("--dual-sim") + if traceIt: + command_list.append("--trace") + # Set workspace directory + _name = "nax/" + command_list += [ + "--workspace-output-sub-dir", _name + ] + config_rule = name+"_"+config_name + f.write(f"{config_rule}:\n") + rules.append(config_rule) + if name != "fpu_test2": + testsFast.append(config_rule) + else: + testsFpu.append(config_rule) + + # Define output directory + outputDir = f"{workspacePath}/{workspaceName}/{workspaceOutputDir}/{_name}/{name}" + testsPass.append(outputDir +"/PASS") + testsFail.append(outputDir +"/FAIL") + # Define test output rules + ouputs.append(outputDir) + if name != "fpu_test2": + ouputsFast.append(outputDir) + else: + ouputsFpu.append(outputDir) + + # Build the execution command line + command_list += [ + "--load-elf", f" {naxriscv_software}/{path}", + "--start-symbol", "_start", + "--pass-symbol", "pass", + #"--fail-symbol", "fail" + #"--no-putc-flush", + ] + # Join the list to create the chain + command_string = "\t" + " ".join(command_list) + # Writing string to file + #f.write(f"{outputDir}/PASS:\n") + f.write(command_string) + f.write(f"\"\n\n") + + + def regularBaremetal(name): + regularSoftware(name, f"baremetal/{name}/build/{arch}/{name}.elf") + + if xlen == 64: + + rvTest("riscv64_tests", elf_list=riscv64_tests) + + rvTest("riscv64TestMemory", elf_list=riscv64TestMemory) + rvTest("riscv64TestMul", elf_list=riscv64TestMul) + rvTest("riscv64TestDiv", elf_list=riscv64TestDiv) + rvTest("riscv64TestAmo", elf_list=riscv64TestAmo) + + if rvc: + rvTest("riscv64TestRvc", elf_list=riscv64TestRvc, startAdd=-8) + + if rvf: + rvTest("riscv64TestFloat", elf_list=riscv64TestFloat ,start = "_start") + + + if rvd: + rvTest("riscv64TestDouble", elf_list=riscv64TestDouble ,start = "_start") + + rvArch("riscvArch64i", elf_list=riscvArch64i, traceIt=traceIt) + rvArch("riscvArch64M", elf_list=riscvArch64M, traceIt=traceIt) + rvArch("riscvArch64Zifencei", elf_list=riscvArch64Zifencei, traceIt=traceIt) + + if rvc: + rvArch("riscvArch64C", elf_list=riscvArch64C, traceIt=traceIt) + + if rvf: + regularBaremetal("fpu_test2") + fpuTest3() + for e in fpuTestRvf32: + fpuTest("fpuTestRvf32", e[1], e[2], e[0]) + if xlen == 64: + for e in fpuTestRvf64: + fpuTest("fpuTestRvf64", e[1], e[2], e[0]) + + if rvd: + for e in fpuTestRvd32: + fpuTest("fpuTestRvd32", e[1], e[2], e[0]) + if xlen == 64: + for e in fpuTestRvd64: + fpuTest("fpuTestRvd64", e[1], e[2], e[0]) + + + for spec in nax64Software: + regularSoftware(spec[0], spec[1]) + + if xlen == 32: + + rvTest("riscv32_tests", elf_list=riscv32_tests) + + rvTest("riscvTestMemory", elf_list=riscvTestMemory) + + rvTest("riscvTestMul", elf_list=riscvTestMul) + rvTest("riscvTestDiv", elf_list=riscvTestDiv) + rvTest("riscvTestAmo", elf_list=riscvTestAmo) + + if rvc: + rvTest("riscv32TestRvc", elf_list=riscv32TestRvc, startAdd=-8) + + if rvf: + rvTest("riscv32TestFloat", elf_list=riscv32TestFloat, start = "_start") + + if rvd: + rvTest("riscv32TestDouble", elf_list=riscv32TestDouble, start = "_start") + + rvTest("rv32ua-p-lrsc_1234", elf="rv32ua-p-lrsc", passs="test_5") + rvTest("rv32ua-p-lrsc_6", elf="rv32ua-p-lrsc", start="test_6") + + if rvf: + regularBaremetal("fpu_test2") + fpuTest3() + for e in fpuTestRvf32: + fpuTest("fpuTestRvf32", e[1], e[2], e[0]) + + if rvd: + for e in fpuTestRvd32: + fpuTest("fpuTestRvd32", e[1], e[2], e[0]) + + rvArch("riscvArch32i", elf_list=riscvArch32i, traceIt=traceIt) + rvArch("riscvArch32M", elf_list=riscvArch32M, traceIt=traceIt) + rvArch("riscvArch32Zifencei", elf_list=riscvArch32Zifencei, traceIt=traceIt) + + if rvc: + rvArch("riscvArch32C", elf_list=riscvArch32C, traceIt=traceIt) + + for spec in naxSoftware: + regularSoftware(spec[0], spec[1]) + + # Run buildroot + for i in range(linux_count): + # Get the basic command to start the SoC simulation + command_list = get_simulation_command() + #if dualSim: + # command_list.append("--dual-sim") + #if traceIt: + # command_list.append("--trace") + # Set workspace directory + name = "nax/buildroot/run" + str(i) + command_list += [ + "--workspace-output-sub-dir", name + ] + config_rule = "buildroot_run"+ str(i)+"_"+config_name + f.write(f"{config_rule}:\n") + rules.append(config_rule) + testsBuildroot.append(config_rule) + # Define output directory + outputDir = f"{workspacePath}/{workspaceName}/{workspaceOutputDir}/{name}" + # outputDir = "output/nax/buildroot/run" + str(i) + testsPass.append(outputDir +"/PASS") + testsFail.append(outputDir +"/FAIL") + # Define test output rules + ouputs.append(outputDir) + ouputsBuildroot.append(outputDir) + + imagePath = f"{naxriscv_software}/buildroot/images/" + archLinux + + # Build the execution command line + command_list += [ + #"--seed {i}" , + #"--name buildroot_run{i}" , + "--load-bin", f"{imagePath}/fw_jump.bin,80000000", + "--load-bin", f"{imagePath}/linux.dtb,80F80000" , + "--load-bin", f"{imagePath}/Image,80400000" , + "--load-bin", f"{imagePath}/rootfs.cpio,81000000", + #"--no-stdin" , + #"--no-putc-flush" , + #"--seed={str(random.randint(0, 100000000))}" , + "--getc", "buildroot,login" , + "--putc", "root", + "--getc", "#" , + "--putc", "cat,/proc/cpuinfo", + "--getc", "#" , + "--putc", "echo,1+2+3*4,|,bc" , + "--getc", "#", + "--putc", "micropython" , + "--getc", ">>>," , + "--putc", "import,math" , + "--getc", ">>>," , + "--putc", "math.sin(math.pi/4)" , + "--getc", ">>>," , + "--putc", "from,sys,import,exit" , + "--getc", ">>>," , + "--putc", "exit()" , + "--getc", "#" , + "--putc", "ls,/" , + "--getc", "#", + "--putc", "echo" , + "--success" + ] + # Join the list to create the chain + command_string = "\t" + " ".join(command_list) + # Writing string to file + #f.write(f"{outputDir}/PASS:\n") + f.write(command_string) + f.write(f"\"\n\n") + + #f.write("OUTPUT_DIRS := \\\n") + #for outputDir in ouputs: + # f.write(f"\t{outputDir} \\\n") + + # Start writing the test report rule + f.write(f"""test-{config_name}-report:\n""") + # Add the command to echo that testing has started + f.write(f"""\t@echo "Checking tests..."\n""") + + # Write the TESTS_COUNT variable based on the number of tests in the 'tests' list + f.write(f"""\t@TESTS_COUNT={len(ouputs)} && \\\n""") + + # Find the PASS files and store them in PASS_FILES, then count them + f.write(f"""\tPASSED_FILES=$$(find {workspacePath}/{workspaceName}/{workspaceOutputDir}/ -name PASS 2>/dev/null) && \\\n""") + + # Find the FAIL files and store them in FAIL_FILES, then count them + f.write(f"""\tFAILED_FILES=$$(find {workspacePath}/{workspaceName}/{workspaceOutputDir}/ -name FAIL 2>/dev/null) && \\\n""") + + # Count the number of PASS files found + f.write(f"""\tPASSED=$$(echo "$$PASSED_FILES" | grep -v '^$$' | wc -l) && \\\n""") + + # Count the number of FAIL files found + f.write(f"""\tFAILED=$$(echo "$$FAILED_FILES" | grep -v '^$$' | wc -l) && \\\n""") + + # Calculation of unexecuted tests + f.write(f"""\tNOT_EXECUTED=$$((TESTS_COUNT - PASSED - FAILED)) && \\\n""") + + # Display the number of passed tests out of the total count + f.write(f"""\techo "$$PASSED/$$TESTS_COUNT tests passed" && \\\n""") + + # Only display the "tests failed" and "tests not executed" if FAILED > 0 or NOT_EXECUTED > 0 + f.write(f"""\tif [ $$FAILED -gt 0 ] || [ $$NOT_EXECUTED -gt 0 ]; then \\\n""") + #f.write(f"""\t\tif [ $$FAILED -gt 0 ]; then \\\n""") + f.write(f"""\t\techo "$$FAILED/$$TESTS_COUNT tests failed"; \\\n""") + #f.write(f"""\t\tfi; \\\n""") + #f.write(f"""\t\tif [ $$NOT_EXECUTED -gt 0 ]; then \\\n""") + f.write(f"""\t\techo "$$NOT_EXECUTED/$$TESTS_COUNT tests not executed"; \\\n""") + #f.write(f"""\t\tfi; \\\n""") + f.write(f"""\tfi\n""") + + # Add a couple of line breaks for clarity in the Makefile + f.write(f"\n\n") + + + f.write(f"""test-{config_name}-all:\n""") + for rule in rules: + f.write(f"\t$(MAKE) {rule}\n") + f.write(f"\n\n") + + f.write(f"""test-{config_name}-fast:\n""") + for rule in testsFast: + f.write(f"\t$(MAKE) {rule}\n") + f.write(f"\n\n") + + if testsFpu: + f.write(f"""test-{config_name}-fpu:\n""") + for rule in testsFpu: + f.write(f"\t$(MAKE) {rule}\n") + f.write(f"\n\n") + + if testsBuildroot: + f.write(f"""test-{config_name}-buildroot:\n""") + for rule in testsBuildroot: + f.write(f"\t$(MAKE) {rule}\n") + f.write(f"\n\n") + + + cleanPath = os.path.join(workspacePath, workspaceName) + keep_files = ["Makefile", "testsRvls.mk", "testsGenRvls.py"] + # Generate the clean rule + f.write(f"""test-{config_name}-clean-all:\n""") + f.write(f"\trm -rf {os.path.join(cleanPath, 'logs')}\n") + f.write(f"\trm -rf {os.path.join(cleanPath, 'rtl')}\n") + f.write(f"\trm -rf {os.path.join(cleanPath, 'verilator')}\n") + f.write(f"\trm -rf {os.path.join(cleanPath, 'waves')}\n") + f.write(f"\trm -f {os.path.join(cleanPath, 'verilatorScript.sh')}\n") + + f.write(f"\n\n") + + f.write(f"""test-{config_name}-clean:\n""") + f.write(f"""\trm -rf {" ".join(ouputs)}\n""") + + f.write(f"\n\n") + + f.write(f"""test-{config_name}-fast-clean:\n""") + f.write(f"""\trm -rf {" ".join(ouputsFast)}\n""") + + f.write(f"\n\n") + + if testsFpu: + f.write(f"""test-{config_name}-fpu-clean:\n""") + f.write(f"""\trm -rf {" ".join(ouputsFpu)}\n""") + + f.write(f"\n\n") + + if testsBuildroot: + f.write(f"""test-{config_name}-buildroot-clean:\n""") + f.write(f"""\trm -rf {" ".join(ouputsBuildroot)}\n""") + + f.write(f"\n\n") + + +# print(tests) + + +# for test in tests: +# subprocess.run(test) \ No newline at end of file diff --git a/src/test/scala/naxriscv/NaxRiscvRegression.scala b/src/test/scala/naxriscv/NaxRiscvRegression.scala index 5d0aef6f..5b7d09e7 100644 --- a/src/test/scala/naxriscv/NaxRiscvRegression.scala +++ b/src/test/scala/naxriscv/NaxRiscvRegression.scala @@ -88,9 +88,45 @@ class NaxRiscvRegression extends MultithreadedFunSuite(sys.env.getOrElse("NAXRIS val makeThreadCount = sys.env.getOrElse("NAXRISCV_REGRESSION_MAKE_THREAD_COUNT", "3").toInt println("Env :\n" + env.map(e => e._1 + "=" + e._2).mkString(" ")) doCmd("python3 ./testsGen.py", env :_*) + // Correction des accolades et chemins + val updatedEnv = HeavyLock.synchronized { + // Verilator configuration + val verilatorRoot = { + val base = new File("toolchain/verilator-v4.216") + val canonical = base.getCanonicalFile + if (!canonical.exists()) throw new Exception(s"Verilator missing at ${canonical.getAbsolutePath}") + canonical.getAbsolutePath + } + + // Verify Verilator binary exists + val verilatorBin = new File(s"$verilatorRoot/bin/verilator") + if (!verilatorBin.exists()) throw new Exception(s"Verilator binary missing: ${verilatorBin.getAbsolutePath}") + + // Spike configuration + val spikeRoot = { + val base = new File("ext/riscv-isa-sim") + val canonical = base.getCanonicalFile + if (!canonical.exists()) throw new Exception(s"Spike installation missing at ${canonical.getAbsolutePath}") + canonical.getAbsolutePath + } + + // Update global environment variables + env ++ List( + "VERILATOR_ROOT" -> verilatorRoot, + "SPIKE" -> spikeRoot, + "PATH" -> List( + s"$verilatorRoot/bin", + System.getenv("PATH") + ).mkString(File.pathSeparator) + ) + } + + // Compilation (needs synchronization) HeavyLock.synchronized { - doCmd("make compile", env: _*) + doCmd("make compile", updatedEnv: _*) } + // Run tests with updated environment + doCmd(s"make test-all -j${makeThreadCount}", updatedEnv: _*) doCmd(s"make test-all -j${makeThreadCount}", env :_*) val passed = doCmd(s"find output -name PASS", env :_*).lines.filter(line => line.contains("PASS")).toArray.size val failed = doCmd(s"find output -name FAIL", env :_*).lines.filter(line => line.contains("FAIL")).toArray.size diff --git a/src/test/scala/naxriscv/NaxRiscvRvls.scala b/src/test/scala/naxriscv/NaxRiscvRvls.scala new file mode 100644 index 00000000..e936f522 --- /dev/null +++ b/src/test/scala/naxriscv/NaxRiscvRvls.scala @@ -0,0 +1,203 @@ +// SPDX-FileCopyrightText: 2023 "Everybody" +// +// SPDX-License-Identifier: MIT + +package naxriscv + +import naxriscv.Gen.plugins +import naxriscv.compatibility.{MemReadDuringWriteHazardPhase, MultiPortWritesSymplifier} +//import naxriscv.utilities.{DocPlugin, Plugin} +import org.apache.commons.io.FileUtils +import org.scalatest.Tag +import org.scalatest.funsuite.AnyFunSuite +import spinal.core.SpinalConfig +import spinal.lib.misc.test.MultithreadedFunSuite + +import java.nio.file.{Files, Paths, Path, StandardOpenOption} +import java.io.{File, OutputStream} +import java.util.concurrent.ForkJoinPool +import scala.collection.mutable +import scala.concurrent.{Await, ExecutionContext, Future} +import scala.concurrent.duration.Duration +import scala.sys.process.{Process, ProcessLogger} +import scala.util.Random + +//object SyncBlock +//object NaxRiscvRvlsSim extends App { + class NaxRiscvRvls extends MultithreadedFunSuite(sys.env.getOrElse("NAXRISCV_REGRESSION_THREAD_COUNT", "1").toInt) { + + var seed = sys.env.getOrElse("NAXRISCV_SEED", Random.nextInt(100000000).toString).toInt + println("SEED="+seed) + + + + def doTest(name : String, + naxCount : Int, + params: => Seq[(String, String)], + linuxCount : Int = sys.env.getOrElse("LINUX_COUNT", "0").toInt, + freertosCount : Int = sys.env.getOrElse("FREERTOS_COUNT", "0").toInt, + seed : Int = Random.nextInt()): Unit ={ + testMp(name){ + var workspacePath = s"simWorkspace/rvls/$name" + val testbenchPath = s"src/test/python/naxriscv" + + println("Use seed : " + seed) + + FileUtils.deleteQuietly(new File(workspacePath)) + + def cpyTb(file : String) = FileUtils.copyFile(new File(testbenchPath + "/" + file), new File(workspacePath + "/" + file)) + + cpyTb("testsGenRvls.py") + + def doCmd(cmd: String, extraEnv: (String, String)*)(args: String*): String = { + val stdOut = new StringBuilder() + class Logger extends ProcessLogger { + override def err(s: => String): Unit = { + if (!s.startsWith("ar: creating ")) println(s) + } + override def out(s: => String): Unit = { + val v : String = s.replace("\r", "") + if (!v.contains("PASS") && !v.contains("make")) { + println(v) + } + stdOut ++= v + "\n" + } + override def buffer[T](f: => T) = f + } + // Concatenate command and arguments + val fullCommand = if (args.isEmpty) cmd else cmd + " " + args.mkString(" ") + + // Get the current working directory + val currentDirectory = new File(workspacePath).getAbsolutePath + + //Run the command with the specified working directory + try{ + val ret = Process(fullCommand, new File(workspacePath), extraEnv :_*).!(new Logger) + assert(ret == 0) + } catch { + case ex: Exception => println(s"Error executing command: ${ex.getMessage}") + } + stdOut.toString() + } + + // ===================================================================== + // Environment Configuration + // ===================================================================== + + // Verify and configure Verilator installation + val verilatorRoot = { + val base = new File("toolchain/verilator-v4.216") + val canonical = base.getCanonicalFile + if (!canonical.exists()) throw new Exception(s"Verilator missing at ${canonical.getAbsolutePath}") + canonical.getAbsolutePath + } + + // Ensure Verilator binary exists + val verilatorBin = new File(s"$verilatorRoot/bin/verilator") + if (!verilatorBin.exists()) throw new Exception(s"Verilator binary missing: ${verilatorBin.getAbsolutePath}") + + // Verify and configure Spike RISC-V simulator + val spikeRoot = { + val base = new File("ext/riscv-isa-sim") + val canonical = base.getCanonicalFile + if (!canonical.exists()) throw new Exception(s"Spike installation missing at ${canonical.getAbsolutePath}") + canonical.getAbsolutePath + } + + // Build environment variables for subprocesses + val env = List( + "WORKSPACE_PATH" -> s"./simWorkspace/rvls", + "WORKSPACE_NAME" -> s"$name", + "WORKSPACE_OUTPUT_DIR" -> s"logs", + "NAXRISCV_SOFTWARE" -> "ext/NaxSoftware", + "SPIKE" -> spikeRoot, // Absolute path to Spike + "VERILATOR_ROOT" -> verilatorRoot, // Verilator installation root + "PATH" -> List( // Prepend Verilator to PATH + s"$verilatorRoot/bin", + System.getenv("PATH") + ).mkString(File.pathSeparator), + "FREERTOS_COUNT" -> freertosCount.toString, + "LINUX_COUNT" -> linuxCount.toString, + "NAXRISCV_SEED" -> seed.toString, + "NAXRISCV_COUNT" -> naxCount.toString, + "NAXRISCV_TEST_FPU_FACTOR" -> 0.10.toString + ) + val envMap = env.toMap + + def parseConfig(isa: String): Option[List[String]] = { + val configMap: Map[String, List[String]] = Map( + "config_rv32imasu" -> List("--xlen 32"), + "config_rv64imasu" -> List("--xlen 64"), + "config_rv32imacsu" -> List("--xlen 32", "--withRvc"), + "config_rv64imacsu" -> List("--xlen 64", "--withRvc"), + "config_rv32imafcsu" -> List("--xlen 32", "--withFloat", "--withRvc"), + "config_rv64imafcsu" -> List("--xlen 64", "--withFloat", "--withRvc"), + "config_rv32imafdcsu" -> List("--xlen 32", "--withFloat", "--withDouble", "--withRvc"), + "config_rv64imafdcsu" -> List("--xlen 64", "--withFloat", "--withDouble", "--withRvc") + ) + + configMap.get(isa) + } + + // Function to extract configuration name from given string + def extractConfigName(nameString: String): String = { + if (nameString.startsWith("config_")) { + val pattern = "config_(\\w+)".r + val matches = pattern.findAllMatchIn(nameString) + if (matches.hasNext) { + matches.next().group(1) + } else { + throw new IllegalArgumentException("Configuration name could not be retrieved.") + } + } else { + nameString + } + } + + // Filter params to get arguments with values equal to 1 + val filteredParams = params.collect { + case (param, "1") => s"--$param" + }.toList + + val makeThreadCount = sys.env.getOrElse("NAXRISCV_REGRESSION_MAKE_THREAD_COUNT", "3").toInt + + // Get the configuration arguments that are used in test generation + val configurationArgs = parseConfig(name).getOrElse(Nil) ++ filteredParams + + configurationArgs match { + case args: List[String] => + // Call doCmd with command, environment variables, and configuration arguments + doCmd("python3 ./testsGenRvls.py", env: _*)(args: _*) + } + + // Extract config name + val configName = extractConfigName(name) + + var command = s"make test-rvls-all".replace("rvls", configName) + workspacePath = "." + doCmd(command, env :_*)() + command = s"make test-rvls-report".replace("rvls", configName) + doCmd(command, env :_*)() + + workspacePath = s"simWorkspace/rvls/$name" + val workspaceOutputDir = envMap.getOrElse("WORKSPACE_OUTPUT_DIR", "logs") + val passed = doCmd(s"find $workspaceOutputDir -name PASS", env :_*)().lines.filter(line => line.contains("PASS")).toArray.size + val failed = doCmd(s"find $workspaceOutputDir -name FAIL", env :_*)().lines.filter(line => line.contains("FAIL")).toArray.size + println(s"PASS = $passed") + println(s"FAIL = $failed") + if(failed != 0 || passed == 0) throw new Exception("Failure") + } + } + + //doTest("config_rv32imasu", naxCount = 1, params = Seq("noL2" -> "0", "noRvls" -> "0", "dualSim" -> "0", "trace" -> "0"), linuxCount = 1, freertosCount = 1) + //doTest("config_rv64imasu", naxCount = 1, params = Seq("noL2" -> "0", "noRvls" -> "0", "dualSim" -> "0", "trace" -> "0"), linuxCount = 1, freertosCount = 1) + //doTest("config_rv32imacsu",naxCount = 1, params = Seq("noL2" -> "0", "noRvls" -> "0", "dualSim" -> "0", "trace" -> "0"), linuxCount = 1, freertosCount = 1) + //doTest("config_rv32imafcsu", naxCount = 1, params = Seq("noL2" -> "0", "noRvls" -> "0", "dualSim" -> "0", "trace" -> "0"), linuxCount = 0, freertosCount = 1) + //doTest("config_rv32imafdcsu", naxCount = 1, params = Seq("noL2" -> "0", "noRvls" -> "0", "dualSim" -> "0", "trace" -> "0"), linuxCount = 0, freertosCount = 1) + //doTest("config_rv64imacsu", naxCount = 1, params = Seq("noL2" -> "0", "noRvls" -> "0", "dualSim" -> "0", "trace" -> "0"), linuxCount = 1, freertosCount = 1) + //doTest("config_rv64imafcsu", naxCount = 1, params = Seq("noL2" -> "0", "noRvls" -> "0", "dualSim" -> "0", "trace" -> "0"), linuxCount = 0, freertosCount = 1) + doTest("config_rv64imafdcsu", naxCount = 1, params = Seq("noL2" -> "0", "noRvls" -> "0", "dualSim" -> "0", "trace" -> "0"), linuxCount = 1, freertosCount = 1) + } + + +