diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index 8a7733c2e..b87f4f116 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -67,6 +67,12 @@ jobs: os: ubuntu-latest image: null riscv_path: /home/riscv + # Custom location user level installation + - name: custom-user-install + os: ubuntu-latest + image: null + user: true + riscv_path: $HOME/riscv-toolchain # run on selected version of ubuntu or on ubuntu-latest with docker image runs-on: ${{ matrix.os }} @@ -108,6 +114,7 @@ jobs: fi # Set environment variables for the rest of the job - name: Set Environment Variables + if: always() run: | if [ ! -z ${{ matrix.riscv_path }} ]; then sed -i 's,exit 1,export RISCV=${{ matrix.riscv_path }},g' setup.sh diff --git a/README.md b/README.md index 62a59d5a2..2c2bc0d0a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Installation CI](https://github.com/jordancarlin/cvw/actions/workflows/install.yml/badge.svg?branch=main) +![Installation CI](https://github.com/openhwgroup/cvw/actions/workflows/install.yml/badge.svg?branch=main) # core-v-wally diff --git a/bin/nightly_build.py b/bin/nightly_build.py index 629da074e..349bdb378 100755 --- a/bin/nightly_build.py +++ b/bin/nightly_build.py @@ -84,7 +84,7 @@ from pathlib import Path class FolderManager: """A class for managing folders and repository cloning.""" - def __init__(self): + def __init__(self, basedir): """ Initialize the FolderManager instance. @@ -92,8 +92,12 @@ class FolderManager: base_dir (str): The base directory where folders will be managed and repository will be cloned. """ env_extract_var = 'WALLY' - self.base_dir = os.environ.get(env_extract_var) - self.base_parent_dir = os.path.dirname(self.base_dir) + if os.environ.get(env_extract_var): + self.base_dir = os.environ.get(env_extract_var) + self.base_parent_dir = os.path.dirname(self.base_dir) + else: + self.base_dir = basedir + self.base_parent_dir = self.base_dir # logger.info(f"Base directory: {self.base_dir}") # logger.info(f"Parent Base directory: {self.base_parent_dir}") @@ -313,7 +317,7 @@ class TestRunner: self.logger.error(f"Error making the tests. Target: {target}") return False - def run_tests(self, test_type=None, test_name=None, test_extension=None): + def run_tests(self, test_type=None, test_name=None, test_extensions=None): """ Run a script through the terminal and save the output to a file. @@ -329,12 +333,12 @@ class TestRunner: output_file = self.log_dir.joinpath(f"{test_name}-output.log") os.chdir(self.sim_dir) - if test_extension: - command = [test_type, test_name, test_extension] - self.logger.info(f"Command used to run tests: {test_type} {test_name} {test_extension}") + if test_extensions: + command = [test_type, test_name] + test_extensions + self.logger.info(f"Command used to run tests in directory {self.sim_dir}: {test_type} {test_name} {' '.join(test_extensions)}") else: command = [test_type, test_name] - self.logger.info(f"Command used to run tests: {test_type} {test_name}") + self.logger.info(f"Command used to run tests in directory {self.sim_dir}: {test_type} {test_name}") # Execute the command using subprocess and save the output into a file @@ -348,10 +352,10 @@ class TestRunner: self.logger.error("There was an error in running the tests in the run_tests function: {e}") # Check if the command executed successfuly if result.returncode or result.returncode == 0: - self.logger.info(f"Test ran successfuly. Test type: {test_type}, test name: {test_name}, test extention: {test_extension}") + self.logger.info(f"Test ran successfuly. Test type: {test_type}, test name: {test_name}, test extension: {' '.join(test_extensions)}") return True, output_file else: - self.logger.error(f"Error making test. Test type: {test_type}, test name: {test_name}, test extention: {test_extension}") + self.logger.error(f"Error making test. Test type: {test_type}, test name: {test_name}, test extension: {' '.join(test_extensions)}") return False, output_file @@ -406,22 +410,30 @@ class TestRunner: # Remove ANSI escape codes line = re.sub(r'\x1b\[[0-9;]*[mGK]', '', lines[index]) - if "Success" in line: + if "Success" in line: # test succeeds passed_configs.append(line.split(':')[0].strip()) elif "passed lint" in line: - passed_configs.append(line.split(' ')[0].strip()) + passed_configs.append(f"Lint: {line.split(' ')[0].strip()}") #passed_configs.append(line) # potentially use a space elif "failed lint" in line: - failed_configs.append(line.split(' ')[0].strip(), "no log file") + failed_configs.append([f"Lint: {line.split(' ')[0].strip()}", "No Log File"]) #failed_configs.append(line) - elif "Failures detected in output" in line: + elif "Failures detected in output" in line: # Text explicitly fails try: config_name = line.split(':')[0].strip() - log_file = os.path.abspath("logs/"+config_name+".log") + log_file = os.path.abspath(os.path.join("logs", config_name, ".log")) failed_configs.append((config_name, log_file)) except: failed_configs.append((config_name, "Log file not found")) + + elif "Timeout" in line: # Test times out + try: + config_name = line.split(':')[0].strip() + log_file = os.path.abspath("logs/"+config_name+".log") + failed_configs.append((f"Timeout: {config_name}", log_file)) + except: + failed_configs.append((f"Timeout: {config_name}", "No Log File")) index += 1 @@ -535,7 +547,7 @@ class TestRunner: md_file.write(f"\n**Total failed tests: {total_number_failures}**") for (test_item, item) in zip(test_list, failed_tests): md_file.write(f"\n\n### {test_item[1]} test") - md_file.write(f"\n**Command used:** {test_item[0]} {test_item[1]} {test_item[2]}\n\n") + md_file.write(f"\n**Command used:** {test_item[0]} {test_item[1]} {' '.join(test_item[2])}\n\n") md_file.write(f"**Failed Tests:**\n") @@ -558,7 +570,7 @@ class TestRunner: md_file.write(f"\n**Total successful tests: {total_number_success}**") for (test_item, item) in zip(test_list, passed_tests): md_file.write(f"\n\n### {test_item[1]} test") - md_file.write(f"\n**Command used:** {test_item[0]} {test_item[1]} {test_item[2]}\n\n") + md_file.write(f"\n**Command used:** {test_item[0]} {test_item[1]} {' '.join(test_item[2])}\n\n") md_file.write(f"\n**Successful Tests:**\n") @@ -619,7 +631,7 @@ class TestRunner: # check if there are any emails if not receiver_emails: - self.logger.ERROR("No receiver emails provided.") + self.logger.error("No receiver emails provided.") return # grab the html file @@ -660,7 +672,7 @@ def main(): parser.add_argument('--path',default = "nightly", help='specify the path for where the nightly repositories will be cloned ex: "nightly-runs') parser.add_argument('--repository',default = "https://github.com/openhwgroup/cvw", help='specify which github repository you want to clone') - parser.add_argument('--target', default = "all", help='types of tests you can make are: all, wally-riscv-arch-test, no') + parser.add_argument('--target', default = "--jobs", help='types of tests you can make are: all, wally-riscv-arch-test, no') parser.add_argument('--tests', default = "nightly", help='types of tests you can run are: nightly, test, test_lint') parser.add_argument('--send_email',default = "", nargs="+", help='What emails to send test results to. Example: "[email1],[email2],..."') @@ -682,7 +694,7 @@ def main(): log_file_path = log_path.joinpath("nightly_build.log") previous_cvw_path = Path.home().joinpath(args.path,f"{yesterday}/cvw") # creates the object - folder_manager = FolderManager() + folder_manager = FolderManager(basedir=args.path) # setting the path on where to clone new repositories of cvw folder_manager.create_folders([cvw_path, results_path, log_path]) @@ -691,14 +703,18 @@ def main(): folder_manager.clone_repository(cvw_path, args.repository) # Define tests that we can run - if (args.tests == "nightly"): - test_list = [["python", "regression-wally", "--nightly --buildroot"]] - elif (args.tests == "test"): - test_list = [["python", "regression-wally", ""]] - elif (args.tests == "test_lint"): - test_list = [["bash", "lint-wally", "-nightly"]] + # + # flags are a list + if (args.tests == "all"): + test_list = [["python", "./regression-wally", ["--nightly", "--buildroot"]]] + elif (args.tests == "nightly"): + test_list = [["python", "./regression-wally", ["--nightly"]]] + elif (args.tests == "regression"): + test_list = [["python", "./regression-wally", []]] + elif (args.tests == "lint"): + test_list = [["bash", "./lint-wally", ["--nightly"]]] else: - print(f"Error: Invalid test '"+args.test+"' specified") + print(f"Error: Invalid test {args.tests} specified") raise SystemExit ############################################# @@ -747,12 +763,12 @@ def main(): if args.target != "no": test_runner.execute_makefile(target = args.target, makefile_path=test_runner.cvw) - if args.target == "all": - # Compile Linux for local testing - test_runner.set_env_var("RISCV",str(test_runner.cvw)) - linux_path = test_runner.cvw / "linux" - test_runner.execute_makefile(target = "all_nosudo", makefile_path=linux_path) - test_runner.execute_makefile(target = "dumptvs_nosudo", makefile_path=linux_path) + # TODO: remove vestigial code if no longer wanted + # if args.target == "all": + # # Compile Linux for local testing + # test_runner.set_env_var("RISCV",str(test_runner.cvw)) + # linux_path = test_runner.cvw / "linux" + # test_runner.execute_makefile(target = "all", makefile_path=linux_path) ############################################# # RUN TESTS # @@ -766,9 +782,9 @@ def main(): total_failures = [] total_success = [] - for test_type, test_name, test_extension in test_list: + for test_type, test_name, test_extensions in test_list: - check, output_location = test_runner.run_tests(test_type=test_type, test_name=test_name, test_extension=test_extension) + check, output_location = test_runner.run_tests(test_type=test_type, test_name=test_name, test_extensions=test_extensions) try: if check: # this checks if the test actually ran successfuly output_log_list.append(output_location) @@ -778,7 +794,7 @@ def main(): passed, failed = test_runner.clean_format_output(input_file = output_location) logger.info(f"{test_name} has been formatted to markdown") except: - logger.ERROR(f"Error occured with formatting {test_name}") + logger.error(f"Error occured with formatting {test_name}") logger.info(f"The # of failures are for {test_name}: {len(failed)}") total_number_failures+= len(failed) @@ -788,15 +804,19 @@ def main(): total_number_success += len(passed) total_success.append(passed) test_runner.rewrite_to_markdown(test_name, passed, failed) + + newlinechar = "\n" + logger.info(f"Failed tests: \n{newlinechar.join([x[0] for x in failed])}") except Exception as e: - logger.error("There was an error in running the tests: {e}") + logger.error(f"There was an error in running the tests: {e}") logger.info(f"The total sucesses for all tests ran are: {total_number_success}") logger.info(f"The total failures for all tests ran are: {total_number_failures}") - # Copy actual test logs from sim/questa, sim/verilator - test_runner.copy_sim_logs([test_runner.cvw / "sim/questa/logs", test_runner.cvw / "sim/verilator/logs"]) + # Copy actual test logs from sim/questa, sim/verilator, sim/vcs + if not args.tests == "test_lint": + test_runner.copy_sim_logs([test_runner.cvw / "sim/questa/logs", test_runner.cvw / "sim/verilator/logs", test_runner.cvw / "sim/vcs/logs"]) ############################################# # FORMAT TESTS # diff --git a/bin/regression-wally b/bin/regression-wally index 2d741ded8..643787adc 100755 --- a/bin/regression-wally +++ b/bin/regression-wally @@ -507,7 +507,7 @@ def main(): TIMEOUT_DUR = 20*60 # seconds os.system('rm -f questa/cov/*.ucdb') elif args.fcov: - TIMEOUT_DUR = 2*60 + TIMEOUT_DUR = 8*60 os.system('rm -f questa/fcov_ucdb/* questa/fcov_logs/* questa/fcov/*') elif args.buildroot: TIMEOUT_DUR = 60*1440 # 1 day @@ -534,6 +534,7 @@ def main(): try: num_fail+=result.get(timeout=TIMEOUT_DUR) except TimeoutError: + pool.terminate() num_fail+=1 print(f"{bcolors.FAIL}%s: Timeout - runtime exceeded %d seconds{bcolors.ENDC}" % (config.cmd, TIMEOUT_DUR)) diff --git a/bin/wally-tool-chain-install.sh b/bin/wally-tool-chain-install.sh index c722abf81..f20da4e22 100755 --- a/bin/wally-tool-chain-install.sh +++ b/bin/wally-tool-chain-install.sh @@ -48,28 +48,64 @@ ENDC='\033[0m' # Reset to default color error() { echo -e "${FAIL_COLOR}Error: $STATUS installation failed" echo -e "Error on line ${BASH_LINENO[0]} with command $BASH_COMMAND${ENDC}" - echo -e "Please check the log in $RISCV/logs/$STATUS.log for more information." + if [ -e "$RISCV/logs/$STATUS.log" ]; then + echo -e "Please check the log in $RISCV/logs/$STATUS.log for more information." + fi exit 1 } # Check if a git repository exists, is up to date, and has been installed -# Clones the repository if it doesn't exist +# clones the repository if it doesn't exist +# $1: repo name +# $2: repo url to clone from +# $3: file to check if already installed +# $4: upstream branch, optional, default is master git_check() { local repo=$1 local url=$2 local check=$3 local branch="${4:-master}" - if [[ ((! -e $repo) && ($(git clone "$url") || true)) || ($(cd "$repo"; git fetch; git rev-parse HEAD) != $(cd "$repo"; git rev-parse origin/"$branch")) || (! -e $check) ]]; then - return 0 + + # Clone repo if it doesn't exist + if [[ ! -e $repo ]]; then + for ((i=1; i<=5; i++)); do + git clone "$url" && break + echo -e "${WARNING_COLOR}Failed to clone $repo. Retrying.${ENDC}" + rm -rf "$repo" + sleep $i + done + if [[ ! -e $repo ]]; then + echo -e "${ERROR_COLOR}Failed to clone $repo after 5 attempts. Exiting.${ENDC}" + exit 1 + fi + fi + + # Get the current HEAD commit hash and the remote branch commit hash + cd "$repo" + git fetch + local local_head=$(git rev-parse HEAD) + local remote_head=$(git rev-parse origin/"$branch") + + # Check if the git repository is not up to date or the specified file does not exist + if [[ "$local_head" != "$remote_head" ]]; then + echo "$repo is not up to date. Updating now." + true + elif [[ ! -e $check ]]; then + true else - return 1 + false fi } # Log output to a file and only print lines with keywords logger() { - local log="$RISCV/logs/$1.log" - cat < /dev/stdin | tee -a "$log" | (grep -iE --color=never "(\bwarning|\berror|\bfail|\bsuccess|\bstamp|\bdoesn't work)" || true) | (grep -viE --color=never "(_warning|warning_|_error|error_|-warning|warning-|-error|error-|Werror|error\.o|warning flags)" || true) + local log_file="$RISCV/logs/$1.log" + local keyword_pattern="(\bwarning|\berror|\bfail|\bsuccess|\bstamp|\bdoesn't work)" + local exclude_pattern="(_warning|warning_|_error|error_|-warning|warning-|-error|error-|Werror|error\.o|warning flags)" + + cat < /dev/stdin | tee -a "$log_file" | \ + (grep -iE --color=never "$keyword_pattern" || true) | \ + (grep -viE --color=never "$exclude_pattern" || true) } set -e # break on error @@ -111,6 +147,10 @@ fi export PATH=$PATH:$RISCV/bin:/usr/bin export PKG_CONFIG_PATH=$RISCV/lib64/pkgconfig:$RISCV/lib/pkgconfig:$RISCV/share/pkgconfig:$RISCV/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH +if (( RHEL_VERSION != 8 )); then + retry_on_host_error="--retry-on-host-error" +fi + # Check for incompatible PATH environment variable before proceeding with installation if [[ ":$PATH:" == *::* || ":$PATH:" == *:.:* ]]; then echo -e "${FAIL_COLOR}Error: You seem to have the current working directory in your \$PATH environment variable." @@ -191,11 +231,13 @@ if (( RHEL_VERSION == 8 )) || (( UBUNTU_VERSION == 20 )); then section_header "Installing glib" pip install -U meson # Meson is needed to build glib cd "$RISCV" - curl --location https://download.gnome.org/sources/glib/2.70/glib-2.70.5.tar.xz | tar xJ + wget -nv --retry-connrefused $retry_on_host_error https://download.gnome.org/sources/glib/2.70/glib-2.70.5.tar.xz + tar -xJf glib-2.70.5.tar.xz + rm -f glib-2.70.5.tar.xz cd glib-2.70.5 meson setup _build --prefix="$RISCV" - meson compile -C _build - meson install -C _build + meson compile -C _build -j "${NUM_THREADS}" 2>&1 | logger $STATUS; [ "${PIPESTATUS[0]}" == 0 ] + meson install -C _build 2>&1 | logger $STATUS; [ "${PIPESTATUS[0]}" == 0 ] cd "$RISCV" rm -rf glib-2.70.5 echo -e "${SUCCESS_COLOR}glib successfully installed!${ENDC}" @@ -208,11 +250,13 @@ if (( RHEL_VERSION == 8 )); then if [ ! -e "$RISCV"/include/gmp.h ]; then section_header "Installing gmp" cd "$RISCV" - curl --location https://ftp.gnu.org/gnu/gmp/gmp-6.3.0.tar.xz | tar xJ + wget -nv --retry-connrefused $retry_on_host_error https://ftp.gnu.org/gnu/gmp/gmp-6.3.0.tar.xz + tar -xJf gmp-6.3.0.tar.xz + rm -f gmp-6.3.0.tar.xz cd gmp-6.3.0 ./configure --prefix="$RISCV" - make -j "${NUM_THREADS}" - make install + make -j "${NUM_THREADS}" 2>&1 | logger $STATUS; [ "${PIPESTATUS[0]}" == 0 ] + make install 2>&1 | logger $STATUS; [ "${PIPESTATUS[0]}" == 0 ] cd "$RISCV" rm -rf gmp-6.3.0 echo -e "${SUCCESS_COLOR}gmp successfully installed!${ENDC}" @@ -231,7 +275,7 @@ STATUS="riscv-gnu-toolchain" cd "$RISCV" # Temporarily pin riscv-gnu-toolchain to use GCC 13.2.0. GCC 14 does not work with the Q extension. if git_check "riscv-gnu-toolchain" "https://github.com/riscv/riscv-gnu-toolchain" "$RISCV/riscv-gnu-toolchain/stamps/build-gcc-newlib-stage2"; then - cd riscv-gnu-toolchain + cd "$RISCV"/riscv-gnu-toolchain git reset --hard && git clean -f && git checkout master && git pull ./configure --prefix="${RISCV}" --with-multilib-generator="rv32e-ilp32e--;rv32i-ilp32--;rv32im-ilp32--;rv32iac-ilp32--;rv32imac-ilp32--;rv32imafc-ilp32f--;rv32imafdc-ilp32d--;rv64i-lp64--;rv64ic-lp64--;rv64iac-lp64--;rv64imac-lp64--;rv64imafdc-lp64d--;rv64im-lp64--;" make -j "${NUM_THREADS}" 2>&1 | logger $STATUS; [ "${PIPESTATUS[0]}" == 0 ] @@ -257,7 +301,7 @@ STATUS="elf2hex" cd "$RISCV" export PATH=$RISCV/bin:$PATH if git_check "elf2hex" "https://github.com/sifive/elf2hex.git" "$RISCV/bin/riscv64-unknown-elf-elf2bin"; then - cd elf2hex + cd "$RISCV"/elf2hex git reset --hard && git clean -f && git checkout master && git pull autoreconf -i ./configure --target=riscv64-unknown-elf --prefix="$RISCV" @@ -279,7 +323,7 @@ section_header "Installing/Updating QEMU" STATUS="qemu" cd "$RISCV" if git_check "qemu" "https://github.com/qemu/qemu" "$RISCV/include/qemu-plugin.h"; then - cd qemu + cd "$RISCV"/qemu git reset --hard && git clean -f && git checkout master && git pull --recurse-submodules -j "${NUM_THREADS}" git submodule update --init --recursive ./configure --target-list=riscv64-softmmu --prefix="$RISCV" @@ -301,7 +345,7 @@ section_header "Installing/Updating SPIKE" STATUS="spike" cd "$RISCV" if git_check "riscv-isa-sim" "https://github.com/riscv-software-src/riscv-isa-sim" "$RISCV/lib/pkgconfig/riscv-riscv.pc"; then - cd riscv-isa-sim + cd "$RISCV"/riscv-isa-sim git reset --hard && git clean -f && git checkout master && git pull mkdir -p build cd build @@ -327,7 +371,7 @@ STATUS="verilator" cd "$RISCV" if git_check "verilator" "https://github.com/verilator/verilator" "$RISCV/share/pkgconfig/verilator.pc"; then unset VERILATOR_ROOT - cd verilator + cd "$RISCV"/verilator git reset --hard && git clean -f && git checkout master && git pull autoconf ./configure --prefix="$RISCV" @@ -352,7 +396,9 @@ section_header "Installing/Updating Sail Compiler" STATUS="Sail Compiler" if [ ! -e "$RISCV"/bin/sail ]; then cd "$RISCV" - curl --location https://github.com/rems-project/sail/releases/latest/download/sail.tar.gz | tar xvz --directory="$RISCV" --strip-components=1 + wget -nv --retry-connrefused $retry_on_host_error --output-document=sail.tar.gz https://github.com/rems-project/sail/releases/latest/download/sail.tar.gz + tar xz --directory="$RISCV" --strip-components=1 -f sail.tar.gz + rm -f sail.tar.gz echo -e "${SUCCESS_COLOR}Sail Compiler successfully installed/updated!${ENDC}" else echo -e "${SUCCESS_COLOR}Sail Compiler already installed.${ENDC}" @@ -363,7 +409,7 @@ fi section_header "Installing/Updating RISC-V Sail Model" STATUS="riscv-sail-model" if git_check "sail-riscv" "https://github.com/riscv/sail-riscv.git" "$RISCV/bin/riscv_sim_RV32"; then - cd sail-riscv + cd "$RISCV"/sail-riscv git reset --hard && git clean -f && git checkout master && git pull ARCH=RV64 make -j "${NUM_THREADS}" c_emulator/riscv_sim_RV64 2>&1 | logger $STATUS; [ "${PIPESTATUS[0]}" == 0 ] ARCH=RV32 make -j "${NUM_THREADS}" c_emulator/riscv_sim_RV32 2>&1 | logger $STATUS; [ "${PIPESTATUS[0]}" == 0 ] @@ -386,7 +432,7 @@ STATUS="OSU Skywater 130 cell library" mkdir -p "$RISCV"/cad/lib cd "$RISCV"/cad/lib if git_check "sky130_osu_sc_t12" "https://foss-eda-tools.googlesource.com/skywater-pdk/libs/sky130_osu_sc_t12" "$RISCV/cad/lib/sky130_osu_sc_t12" "main"; then - cd sky130_osu_sc_t12 + cd "$RISCV"/sky130_osu_sc_t12 git reset --hard && git clean -f && git checkout main && git pull echo -e "${SUCCESS_COLOR}OSU Skywater library successfully installed!${ENDC}" else @@ -428,8 +474,8 @@ section_header "Downloading Site Setup Script" STATUS="site-setup scripts" cd "$RISCV" if [ ! -e "${RISCV}"/site-setup.sh ]; then - wget https://raw.githubusercontent.com/openhwgroup/cvw/main/site-setup.sh - wget https://raw.githubusercontent.com/openhwgroup/cvw/main/site-setup.csh + wget -nv --retry-connrefused $retry_on_host_error https://raw.githubusercontent.com/openhwgroup/cvw/main/site-setup.sh + wget -nv --retry-connrefused $retry_on_host_error https://raw.githubusercontent.com/openhwgroup/cvw/main/site-setup.csh echo -e "${SUCCESS_COLOR}Site setup script successfully downloaded!${ENDC}" echo -e "${WARNING_COLOR}Make sure to edit the environment variables in $RISCV/site-setup.sh (or .csh) to point to your installation of EDA tools and licensce files.${ENDC}" else diff --git a/bin/wsim b/bin/wsim index 3f6cbaaae..ca44e22a6 100755 --- a/bin/wsim +++ b/bin/wsim @@ -59,12 +59,12 @@ if(args.testsuite.endswith('.elf') and args.elf == ""): # No --elf argument; che args.testsuite = fields[1] + "_" + fields[3] else: args.testsuite = fields[2] + "_" + fields[3] - elif ('/' in args.testsuite): + elif ('/' in args.testsuite): args.testsuite=args.testsuite.rsplit('/', 1)[1] # strip off path if present else: print("ELF file not found: " + args.testsuite) exit(1) - + if(args.lockstep and not args.testsuite.endswith('.elf')): print(f"Invalid Options. Cannot run a testsuite, {args.testsuite} with lockstep. Must run a single elf.") exit(1) @@ -89,13 +89,14 @@ if(int(args.locksteplog) >= 1): EnableLog = 1 else: EnableLog = 0 prefix = "" if (args.lockstep or args.lockstepverbose or args.fcov or args.fcovimp): - if (args.sim == "questa" or args.sim == "vcs"): + if (args.sim == "questa" or args.sim == "vcs"): prefix = "IMPERAS_TOOLS=" + WALLY + "/config/"+args.config+"/imperas.ic" - if (args.sim == "questa"): - prefix = "MTI_VCO_MODE=64 " + prefix +# Force Questa to use 64-bit mode, sometimes it defaults to 32-bit even on 64-bit machines +if (args.sim == "questa"): + prefix = "MTI_VCO_MODE=64 " + prefix if (args.lockstep or args.lockstepverbose): - if(args.locksteplog != 0): ImperasPlusArgs = " +IDV_TRACE2LOG=" + str(EnableLog) + " +IDV_TRACE2LOG_AFTER=" + str(args.locksteplog) + if(args.locksteplog != 0): ImperasPlusArgs = " +IDV_TRACE2LOG=" + str(EnableLog) + " +IDV_TRACE2LOG_AFTER=" + str(args.locksteplog) else: ImperasPlusArgs = "" if(args.fcovimp): CovEnableStr = "1" if int(args.covlog) > 0 else "0" diff --git a/config/rv32gc/coverage.svh b/config/rv32gc/coverage.svh index 3a04643ad..217e8788a 100644 --- a/config/rv32gc/coverage.svh +++ b/config/rv32gc/coverage.svh @@ -5,12 +5,20 @@ // This file is needed in the config subdirectory for each config supporting coverage. // It defines which extensions are enabled for that config. +// Unprivileged extensions `include "RV32I_coverage.svh" `include "RV32M_coverage.svh" `include "RV32F_coverage.svh" +`include "RV32D_coverage.svh" +`include "RV32ZfhD_coverage.svh" `include "RV32Zfh_coverage.svh" `include "RV32Zicond_coverage.svh" `include "RV32Zca_coverage.svh" `include "RV32Zcb_coverage.svh" `include "RV32ZcbM_coverage.svh" -`include "RV32ZcbZbb_coverage.svh" \ No newline at end of file +`include "RV32ZcbZbb_coverage.svh" +`include "RV32Zcf_coverage.svh" +`include "RV32Zcd_coverage.svh" + +// Privileged extensions +`include "ZicsrM_coverage.svh" diff --git a/config/rv32gc/imperas.ic b/config/rv32gc/imperas.ic index fe4a3dbed..a4b5fe64c 100644 --- a/config/rv32gc/imperas.ic +++ b/config/rv32gc/imperas.ic @@ -59,6 +59,28 @@ #--override cpu/instret_undefined=T #--override cpu/hpmcounter_undefined=T +## context registers not implemented +#--override cpu/scontext_undefined=True +#--override cpu/mcontext_undefined=True + +# Disable all features that might want mseccfg or CSRs 7a0-7af +--override cpu/Smepmp_version=none +--override cpu/Smmpm=none +#--override cpu/Zicfilp=F +--override cpu/trigger_num=0 # disable CSRs 7a0-7a8 + +--override no_pseudo_inst=T # For code coverage, don't produce pseudoinstructions + + +# mcause and scause only have 4 lsbs of code and 1 msb of interrupt flag +#--override cpu/ecode_mask=0x8000000F # for RV32 +--override cpu/ecode_mask=0x800000000000000F # for RV64 + +# Debug mode not yet supported +--override cpu/debug_mode=none + + + --override cpu/reset_address=0x80000000 --override cpu/unaligned=F # Zicclsm (should be true) diff --git a/config/rv64gc/coverage.svh b/config/rv64gc/coverage.svh index 5b11b5a22..0491b4ab3 100644 --- a/config/rv64gc/coverage.svh +++ b/config/rv64gc/coverage.svh @@ -5,18 +5,25 @@ // This file is needed in the config subdirectory for each config supporting coverage. // It defines which extensions are enabled for that config. +// Unprivileged extensions `include "RV64I_coverage.svh" `include "RV64M_coverage.svh" `include "RV64F_coverage.svh" +`include "RV64D_coverage.svh" +`include "RV64ZfhD_coverage.svh" `include "RV64Zfh_coverage.svh" -`include "RV64VM_coverage.svh" -`include "RV64VM_PMP_coverage.svh" -`include "RV64CBO_VM_coverage.svh" -`include "RV64CBO_PMP_coverage.svh" -`include "RV64Zicbom_coverage.svh" `include "RV64Zicond_coverage.svh" `include "RV64Zca_coverage.svh" `include "RV64Zcb_coverage.svh" `include "RV64ZcbM_coverage.svh" `include "RV64ZcbZbb_coverage.svh" -`include "RV64ZcbZba_coverage.svh" \ No newline at end of file +`include "RV64ZcbZba_coverage.svh" +`include "RV64Zcd_coverage.svh" + +// Privileged extensions +`include "RV64VM_coverage.svh" +`include "ZicsrM_coverage.svh" +// `include "RV64VM_PMP_coverage.svh" +// `include "RV64CBO_VM_coverage.svh" +// `include "RV64CBO_PMP_coverage.svh" +// `include "RV64Zicbom_coverage.svh" diff --git a/config/rv64gc/imperas.ic b/config/rv64gc/imperas.ic index 1b038c920..0ff19fa68 100644 --- a/config/rv64gc/imperas.ic +++ b/config/rv64gc/imperas.ic @@ -57,15 +57,32 @@ #--override cpu/instret_undefined=T #--override cpu/hpmcounter_undefined=T ---override cpu/scontext_undefined=T ---override cpu/mcontext_undefined=T +# context registers not implemented +#--override cpu/scontext_undefined=True +#--override cpu/mcontext_undefined=True + +# Disable all features that might want mseccfg or CSRs 7a0-7af +--override cpu/Smepmp_version=none +--override cpu/Smmpm=none +#--override cpu/Zicfilp=F +--override cpu/trigger_num=0 # disable CSRs 7a0-7a8 + +# For code coverage, don't produce pseudoinstructions +--override no_pseudo_inst=T + +# nonratified mnosie register not implemented --override cpu/mnoise_undefined=T -# *** how to override other undefined registers: seed, mphmevent, mseccfg, debugger registers -#--override cpu/seed_undefined=T -#--override mhpmevent3_undefined=T -#--override cpu/mseccfg_undefined=T -#--override cpu/tselect_undefined=T -#--override cpu/tdata1_undefined=T + +# mcause and scause only have 4 lsbs of code and 1 msb of interrupt flag +#--override cpu/ecode_mask=0x8000000F # for RV32 +--override cpu/ecode_mask=0x800000000000000F # for RV64 + +# Debug mode not yet supported +--override cpu/debug_mode=none + +# Zkr entropy source and seed register not supported. +--override cpu/Zkr=F + --override cpu/reset_address=0x80000000 diff --git a/linux/Makefile b/linux/Makefile index aadf24bdc..b098b0a8b 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -27,9 +27,16 @@ BINARIES := fw_jump.elf vmlinux busybox OBJDUMPS := $(foreach name, $(BINARIES), $(basename $(name) .elf)) OBJDUMPS := $(foreach name, $(OBJDUMPS), $(DIS)/$(name).objdump) -.PHONY: all generate disassemble install clean cleanDTB cleanDriver check_write_permissions +.PHONY: all generate disassemble install clean cleanDTB check_write_permissions check_environment -all: check_write_permissions clean download Image disassemble install dumptvs +all: check_environment check_write_permissions clean download Image disassemble install dumptvs + +check_environment: $(RISCV) +ifeq ($(findstring :$(RISCV)/lib:,:$(LD_LIBRARY_PATH):),) + @(echo "ERROR: Your environment variables are not set correctly." >&2 \ + && echo "Make sure to source setup.sh or install buildroot using the wally-tool-chain-install.sh script." >&2 \ + && exit 1) +endif check_write_permissions: ifeq ($(SUDO), sudo) @@ -41,17 +48,17 @@ endif && exit 1) @$(SUDO) rm -r $(RISCV)/.test -Image: +Image: check_environment bash -c "unset LD_LIBRARY_PATH; $(MAKE) -C $(BUILDROOT)" $(MAKE) generate @echo "Buildroot Image successfully generated." -install: check_write_permissions +install: check_write_permissions check_environment $(SUDO) rm -rf $(RISCV)/$(BUILDROOT) $(SUDO) mv $(BUILDROOT) $(RISCV)/$(BUILDROOT) @echo "Buildroot successfully installed." -dumptvs: check_write_permissions +dumptvs: check_write_permissions check_environment $(SUDO) mkdir -p $(RISCV)/linux-testvectors cd testvector-generation; ./genInitMem.sh @echo "Testvectors successfully generated." @@ -70,7 +77,7 @@ $(RISCV): @ echo "and sourced setup.sh" # Disassembly rules --------------------------------------------------- -disassemble: +disassemble: check_environment rm -rf $(BUILDROOT)/output/images/disassembly find $(BUILDROOT)/output/build/linux-* -maxdepth 1 -name "vmlinux" | xargs cp -t $(BUILDROOT)/output/images/ mkdir -p $(DIS) @@ -114,9 +121,6 @@ $(BUILDROOT): # --------------------------------------------------------------------- -cleanDriver: - rm -f $(DRIVER) - cleanDTB: rm -f $(IMAGES)/*.dtb diff --git a/sim/questa/wally.do b/sim/questa/wally.do index d712d60ad..a53a0e3c7 100644 --- a/sim/questa/wally.do +++ b/sim/questa/wally.do @@ -181,7 +181,7 @@ if {$DEBUG > 0} { # suppress spurious warnngs about # "Extra checking for conflicts with always_comb done at vopt time" # because vsim will run vopt -set INC_DIRS "+incdir+${CONFIG}/${CFG} +incdir+${CONFIG}/deriv/${CFG} +incdir+${CONFIG}/shared +incdir+${FCRVVI} +incdir+${FCRVVI}/rv32 +incdir+${FCRVVI}/rv64 +incdir+${FCRVVI}/rv64_priv +incdir+${FCRVVI}/common" +set INC_DIRS "+incdir+${CONFIG}/${CFG} +incdir+${CONFIG}/deriv/${CFG} +incdir+${CONFIG}/shared +incdir+${FCRVVI} +incdir+${FCRVVI}/rv32 +incdir+${FCRVVI}/rv64 +incdir+${FCRVVI}/rv64_priv +incdir+${FCRVVI}/priv +incdir+${FCRVVI}/common +incdir+${FCRVVI}" set SOURCES "${SRC}/cvw.sv ${TB}/${TESTBENCH}.sv ${TB}/common/*.sv ${SRC}/*/*.sv ${SRC}/*/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*.sv ${WALLY}/addins/verilog-ethernet/*/*/*/*.sv" vlog -permissive -lint -work ${WKDIR} {*}${INC_DIRS} {*}${FCvlog} {*}${FCdefineCOVER_EXTS} {*}${lockstepvlog} {*}${SOURCES} -suppress 2282,2583,7053,7063,2596,13286 diff --git a/src/privileged/csrc.sv b/src/privileged/csrc.sv index 848cb7e01..8b292513b 100644 --- a/src/privileged/csrc.sv +++ b/src/privileged/csrc.sv @@ -8,7 +8,6 @@ // See RISC-V Privileged Mode Specification 20190608 3.1.10-11 // // Documentation: RISC-V System on Chip Design -// MHPMEVENT is not supported // // A component of the CORE-V-WALLY configurable RISC-V project. // https://github.com/openhwgroup/cvw @@ -66,7 +65,8 @@ module csrc import cvw::*; #(parameter cvw_t P) ( localparam MTIME = 12'hB01; // this is a memory-mapped register; no such CSR exists, and access should faul; localparam MHPMCOUNTERHBASE = 12'hB80; localparam MTIMEH = 12'hB81; // this is a memory-mapped register; no such CSR exists, and access should fault - localparam MHPMEVENTBASE = 12'h320; + localparam MHPMEVENTBASE = 12'h323; + localparam MHPMEVENTLAST = 12'h33F; localparam HPMCOUNTERBASE = 12'hC00; localparam HPMCOUNTERHBASE = 12'hC80; localparam TIME = 12'hC01; @@ -156,37 +156,41 @@ module csrc import cvw::*; #(parameter cvw_t P) ( if (PrivilegeModeW == P.M_MODE | MCOUNTEREN_REGW[CounterNumM] & (!P.S_SUPPORTED | PrivilegeModeW == P.S_MODE | SCOUNTEREN_REGW[CounterNumM])) begin IllegalCSRCAccessM = 1'b0; - if (P.XLEN==64) begin // 64-bit counter reads - // Veri lator doesn't realize this only occurs for XLEN=64 - /* verilator lint_off WIDTH */ - if (CSRAdrM == TIME & ~CSRWriteM) CSRCReadValM = MTIME_CLINT; // TIME register is a shadow of the memory-mapped MTIME from the CLINT - /* verilator lint_on WIDTH */ - else if (CSRAdrM >= MHPMCOUNTERBASE & CSRAdrM < MHPMCOUNTERBASE+P.COUNTERS & CSRAdrM != MTIME) - CSRCReadValM = HPMCOUNTER_REGW[CounterNumM]; - else if (CSRAdrM >= HPMCOUNTERBASE & CSRAdrM < HPMCOUNTERBASE+P.COUNTERS & ~CSRWriteM) // read-only - CSRCReadValM = HPMCOUNTER_REGW[CounterNumM]; - else begin + if (CSRAdrM >= MHPMEVENTBASE & CSRAdrM <= MHPMEVENTLAST) begin + CSRCReadValM = '0; // mphmevent[3:31] tied to read-only zero + end else begin + if (P.XLEN==64) begin // 64-bit counter reads + // Veri lator doesn't realize this only occurs for XLEN=64 + /* verilator lint_off WIDTH */ + if (CSRAdrM == TIME & ~CSRWriteM) CSRCReadValM = MTIME_CLINT; // TIME register is a shadow of the memory-mapped MTIME from the CLINT + /* verilator lint_on WIDTH */ + else if (CSRAdrM >= MHPMCOUNTERBASE & CSRAdrM < MHPMCOUNTERBASE+P.COUNTERS & CSRAdrM != MTIME) + CSRCReadValM = HPMCOUNTER_REGW[CounterNumM]; + else if (CSRAdrM >= HPMCOUNTERBASE & CSRAdrM < HPMCOUNTERBASE+P.COUNTERS & ~CSRWriteM) // read-only + CSRCReadValM = HPMCOUNTER_REGW[CounterNumM]; + else begin + CSRCReadValM = '0; + IllegalCSRCAccessM = 1'b1; // requested CSR doesn't exist + end + end else begin // 32-bit counter reads + // Veril ator doesn't realize this only occurs for XLEN=32 + /* verilator lint_off WIDTH */ + if (CSRAdrM == TIME & ~CSRWriteM) CSRCReadValM = MTIME_CLINT[31:0];// TIME register is a shadow of the memory-mapped MTIME from the CLINT + else if (CSRAdrM == TIMEH & ~CSRWriteM) CSRCReadValM = MTIME_CLINT[63:32]; + /* verilator lint_on WIDTH */ + else if (CSRAdrM >= MHPMCOUNTERBASE & CSRAdrM < MHPMCOUNTERBASE+P.COUNTERS & CSRAdrM != MTIME) + CSRCReadValM = HPMCOUNTER_REGW[CounterNumM]; + else if (CSRAdrM >= HPMCOUNTERBASE & CSRAdrM < HPMCOUNTERBASE+P.COUNTERS & ~CSRWriteM) // read-only + CSRCReadValM = HPMCOUNTER_REGW[CounterNumM]; + else if (CSRAdrM >= MHPMCOUNTERHBASE & CSRAdrM < MHPMCOUNTERHBASE+P.COUNTERS & CSRAdrM != MTIMEH) + CSRCReadValM = HPMCOUNTERH_REGW[CounterNumM]; + else if (CSRAdrM >= HPMCOUNTERHBASE & CSRAdrM < HPMCOUNTERHBASE+P.COUNTERS & ~CSRWriteM) // read-only + CSRCReadValM = HPMCOUNTERH_REGW[CounterNumM]; + else begin CSRCReadValM = '0; - IllegalCSRCAccessM = 1'b1; // requested CSR doesn't exist + IllegalCSRCAccessM = 1'b1; // requested CSR doesn't exist + end end - end else begin // 32-bit counter reads - // Veril ator doesn't realize this only occurs for XLEN=32 - /* verilator lint_off WIDTH */ - if (CSRAdrM == TIME & ~CSRWriteM) CSRCReadValM = MTIME_CLINT[31:0];// TIME register is a shadow of the memory-mapped MTIME from the CLINT - else if (CSRAdrM == TIMEH & ~CSRWriteM) CSRCReadValM = MTIME_CLINT[63:32]; - /* verilator lint_on WIDTH */ - else if (CSRAdrM >= MHPMCOUNTERBASE & CSRAdrM < MHPMCOUNTERBASE+P.COUNTERS & CSRAdrM != MTIME) - CSRCReadValM = HPMCOUNTER_REGW[CounterNumM]; - else if (CSRAdrM >= HPMCOUNTERBASE & CSRAdrM < HPMCOUNTERBASE+P.COUNTERS & ~CSRWriteM) // read-only - CSRCReadValM = HPMCOUNTER_REGW[CounterNumM]; - else if (CSRAdrM >= MHPMCOUNTERHBASE & CSRAdrM < MHPMCOUNTERHBASE+P.COUNTERS & CSRAdrM != MTIMEH) - CSRCReadValM = HPMCOUNTERH_REGW[CounterNumM]; - else if (CSRAdrM >= HPMCOUNTERHBASE & CSRAdrM < HPMCOUNTERHBASE+P.COUNTERS & ~CSRWriteM) // read-only - CSRCReadValM = HPMCOUNTERH_REGW[CounterNumM]; - else begin - CSRCReadValM = '0; - IllegalCSRCAccessM = 1'b1; // requested CSR doesn't exist - end end end else begin CSRCReadValM = '0; diff --git a/src/privileged/csrs.sv b/src/privileged/csrs.sv index 4797f800b..a2a21c2ee 100644 --- a/src/privileged/csrs.sv +++ b/src/privileged/csrs.sv @@ -128,13 +128,16 @@ module csrs import cvw::*; #(parameter cvw_t P) ( else assign STimerInt = 1'b0; + logic [1:0] LegalizedCBIE; + assign LegalizedCBIE = CSRWriteValM[5:4] == 2'b10 ? SENVCFG_REGW[5:4] : CSRWriteValM[5:4]; // Assume WARL for reserved CBIE = 10, keeps old value assign SENVCFG_WriteValM = { {(P.XLEN-8){1'b0}}, CSRWriteValM[7] & P.ZICBOZ_SUPPORTED, - CSRWriteValM[6:4] & {3{P.ZICBOM_SUPPORTED}}, + CSRWriteValM[6] & P.ZICBOM_SUPPORTED, + LegalizedCBIE & {2{P.ZICBOM_SUPPORTED}}, 3'b0, CSRWriteValM[0] & P.VIRTMEM_SUPPORTED - }; + }; flopenr #(P.XLEN) SENVCFGreg(clk, reset, WriteSENVCFGM, SENVCFG_WriteValM, SENVCFG_REGW); diff --git a/src/uncore/spi_apb.sv b/src/uncore/spi_apb.sv index 57260e769..54a072ac9 100644 --- a/src/uncore/spi_apb.sv +++ b/src/uncore/spi_apb.sv @@ -234,9 +234,9 @@ module spi_apb import cvw::*; #(parameter cvw_t P) ( // SPI enable generation, where SCLK = PCLK/(2*(SckDiv + 1)) // Asserts SCLKenable at the rising and falling edge of SCLK by counting from 0 to SckDiv // Active at 2x SCLK frequency to account for implicit half cycle delays and actions on both clock edges depending on phase - // When SckDiv is 0, count doesn't work and SCLKenable is simply PCLK + // When SckDiv is 0, count doesn't work and SCLKenable is simply PCLK *** dh 10/26/24: this logic is seriously broken. SCLK is not scaled to PCLK/(2*(SckDiv + 1)). SCLKenableEarly doesn't work right for SckDiv=0 assign ZeroDiv = ~|(SckDiv[10:0]); - assign SCLKenable = ZeroDiv ? PCLK : (DivCounter == SckDiv); + assign SCLKenable = ZeroDiv ? 1 : (DivCounter == SckDiv); assign SCLKenableEarly = ((DivCounter + 12'b1) == SckDiv); always_ff @(posedge PCLK) if (~PRESETn) DivCounter <= '0; diff --git a/src/uncore/spi_controller.sv b/src/uncore/spi_controller.sv new file mode 100644 index 000000000..c4305047e --- /dev/null +++ b/src/uncore/spi_controller.sv @@ -0,0 +1,348 @@ +/////////////////////////////////////////// +// spi_controller.sv +// +// Written: jacobpease@protonmail.com +// Created: October 28th, 2024 +// Modified: +// +// Purpose: Controller logic for SPI +// +// Documentation: RISC-V System on Chip Design +// +// A component of the CORE-V-WALLY configurable RISC-V project. +// https://github.com/openhwgroup/cvw +// +// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +// except in compliance with the License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work distributed under the +// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// either express or implied. See the License for the specific language governing permissions +// and limitations under the License. +//////////////////////////////////////////////////////////////////////////////////////////////// + + +module spi_controller ( + input logic PCLK, + input logic PRESETn, + input logic TransmitStart, + input logic [11:0] SckDiv, + input logic [1:0] SckMode, + input logic [1:0] CSMode, + input logic [15:0] Delay0, + input logic [15:0] Delay1, + input logic [7:0] txFIFORead, + input logic txFIFOReadEmpty, + output logic SPICLK, + output logic SPIOUT, + output logic CS +); + + // CSMode Stuff + localparam HOLDMODE = 2'b10; + localparam AUTOMODE = 2'b00; + localparam OFFMODE = 2'b11; + + typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype; + statetype CurrState, NextState; + + // SCLKenable stuff + logic [11:0] DivCounter; + logic SCLKenable; + logic SCLKenableEarly; + logic SCLKenableLate; + logic EdgeTiming; + logic ZeroDiv; + logic Clock0; + logic Clock1; + logic SCK; // SUPER IMPORTANT, THIS CAN'T BE THE SAME AS SPICLK! + + + // Shift and Sample Edges + logic PreShiftEdge; + logic PreSampleEdge; + logic ShiftEdge; + logic SampleEdge; + + // Frame stuff + logic [2:0] BitNum; + logic LastBit; + logic EndOfFrame; + logic EndOfFrameDelay; + logic PhaseOneOffset; + + // Transmit Stuff + logic ContinueTransmit; + + // SPIOUT Stuff + logic TransmitLoad; + logic [7:0] TransmitReg; + logic Transmitting; + logic EndTransmission; + + logic HoldMode; + + // Delay Stuff + logic [7:0] cssck; + logic [7:0] sckcs; + logic [7:0] intercs; + logic [7:0] interxfr; + + logic HasCSSCK; + logic HasSCKCS; + logic HasINTERCS; + logic HasINTERXFR; + + logic EndOfCSSCK; + logic EndOfSCKCS; + logic EndOfINTERCS; + logic EndOfINTERXFR; + + logic [7:0] CSSCKCounter; + logic [7:0] SCKCSCounter; + logic [7:0] INTERCSCounter; + logic [7:0] INTERXFRCounter; + + logic DelayIsNext; + + // Convenient Delay Reg Names + assign cssck = Delay0[7:0]; + assign sckcs = Delay0[15:8]; + assign intercs = Delay1[7:0]; + assign interxfr = Delay1[15:8]; + + // Do we have delay for anything? + assign HasCSSCK = cssck > 8'b0; + assign HasSCKCS = sckcs > 8'b0; + assign HasINTERCS = intercs > 8'b0; + assign HasINTERXFR = interxfr > 8'b0; + + // Have we hit full delay for any of the delays? + assign EndOfCSSCK = CSSCKCounter == cssck; + assign EndOfSCKCS = SCKCSCounter == sckcs; + assign EndOfINTERCS = INTERCSCounter == intercs; + assign EndOfINTERXFR = INTERXFRCounter == interxfr; + + // Clock Signal Stuff ----------------------------------------------- + // I'm going to handle all clock stuff here, including ShiftEdge and + // SampleEdge. This makes sure that SPICLK is an output of a register + // and it properly synchronizes signals. + + assign SCLKenableLate = DivCounter > SckDiv; + assign SCLKenable = DivCounter == SckDiv; + assign SCLKenableEarly = (DivCounter + 1'b1) == SckDiv; + assign LastBit = BitNum == 3'd7; + assign EdgeTiming = SckDiv > 12'b0 ? SCLKenableEarly : SCLKenable; + + //assign SPICLK = Clock0; + + assign ContinueTransmit = ~txFIFOReadEmpty & EndOfFrame; + assign EndTransmission = txFIFOReadEmpty & EndOfFrameDelay; + + always_ff @(posedge PCLK) begin + if (~PRESETn) begin + DivCounter <= 12'b0; + SPICLK <= SckMode[1]; + SCK <= 0; + BitNum <= 3'h0; + PreShiftEdge <= 0; + PreSampleEdge <= 0; + EndOfFrame <= 0; + end else begin + // TODO: Consolidate into one delay counter since none of the + // delays happen at the same time? + if (TransmitStart) begin + SCK <= 0; + end else if (SCLKenable) begin + SCK <= ~SCK; + end + + if ((CurrState == CSSCK) & SCK) begin + CSSCKCounter <= CSSCKCounter + 8'd1; + end else begin + CSSCKCounter <= 8'd0; + end + + if ((CurrState == SCKCS) & SCK) begin + SCKCSCounter <= SCKCSCounter + 8'd1; + end else begin + SCKCSCounter <= 8'd0; + end + + if ((CurrState == INTERCS) & SCK) begin + INTERCSCounter <= INTERCSCounter + 8'd1; + end else begin + INTERCSCounter <= 8'd0; + end + + if ((CurrState == INTERXFR) & SCK) begin + INTERXFRCounter <= INTERXFRCounter + 8'd1; + end else begin + INTERXFRCounter <= 8'd0; + end + + // SPICLK Logic + if (TransmitStart) begin + SPICLK <= SckMode[1]; + end else if (SCLKenable & Transmitting) begin + SPICLK <= (~EndTransmission & ~DelayIsNext) ? ~SPICLK : SckMode[1]; + end + + // Reset divider + if (SCLKenable | TransmitStart) begin + DivCounter <= 12'b0; + end else begin + DivCounter = DivCounter + 12'd1; + end + + // EndOfFrame controller + // if (SckDiv > 0 ? SCLKenableEarly & LastBit & SPICLK : LastBit & ~SPICLK) begin + // EndOfFrame <= 1'b1; + // end else begin + // EndOfFrame <= 1'b0; + // end + + if (~TransmitStart) begin + EndOfFrame <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting; + end + + // Increment BitNum + if (ShiftEdge & Transmitting) begin + BitNum <= BitNum + 3'd1; + end else if (EndOfFrameDelay) begin + BitNum <= 3'b0; + end + end + end + + // Delay ShiftEdge and SampleEdge by a half PCLK period + // Aligned EXACTLY ON THE MIDDLE of the leading and trailing edges. + // Sweeeeeeeeeet... + always_ff @(posedge ~PCLK) begin + if (~PRESETn | TransmitStart) begin + ShiftEdge <= 0; + PhaseOneOffset <= 0; + SampleEdge <= 0; + EndOfFrameDelay <= 0; + end else begin + ShiftEdge <= ((SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & ~LastBit & Transmitting) & PhaseOneOffset; + PhaseOneOffset <= PhaseOneOffset == 0 ? Transmitting & SCLKenable : PhaseOneOffset; + SampleEdge <= (SckMode[1] ^ SckMode[0] ^ ~SPICLK) & SCLKenable & Transmitting; + EndOfFrameDelay <= (SckMode[1] ^ SckMode[0] ^ SPICLK) & SCLKenable & LastBit & Transmitting; + end + end + + // typedef enum logic [2:0] {INACTIVE, CSSCK, TRANSMIT, SCKCS, HOLD, INTERCS, INTERXFR} statetype; + // statetype CurrState, NextState; + + assign HoldMode = CSMode == 2'b10; + assign TransmitLoad = TransmitStart | (EndOfFrameDelay & ~txFIFOReadEmpty); + + always_ff @(posedge PCLK) begin + if (~PRESETn) begin + CurrState <= INACTIVE; + end else if (SCLKenable) begin + CurrState <= NextState; + end + end + + always_comb begin + case (CurrState) + INACTIVE: begin // INACTIVE case -------------------------------- + if (TransmitStart) begin + if (~HasCSSCK) begin + NextState = TRANSMIT; + end else begin + NextState = CSSCK; + end + end else begin + NextState = INACTIVE; + end + end + CSSCK: begin // DELAY0 case ------------------------------------- + if (EndOfCSSCK) begin + NextState = TRANSMIT; + end + end + TRANSMIT: begin // TRANSMIT case -------------------------------- + case(CSMode) + AUTOMODE: begin + if (EndTransmission) begin + NextState = INACTIVE; + end else if (ContinueTransmit) begin + NextState = SCKCS; + end + end + HOLDMODE: begin + if (EndTransmission) begin + NextState = HOLD; + end else if (ContinueTransmit) begin + if (HasINTERXFR) NextState = INTERXFR; + end + end + OFFMODE: begin + + end + + endcase + end + SCKCS: begin // SCKCS case -------------------------------------- + if (EndOfSCKCS) begin + if (EndTransmission) begin + if (CSMode == AUTOMODE) NextState = INACTIVE; + else if (CSMode == HOLDMODE) NextState = HOLD; + end else if (ContinueTransmit) begin + if (HasINTERCS) NextState = INTERCS; + else NextState = TRANSMIT; + end + end + end + HOLD: begin // HOLD mode case ----------------------------------- + if (CSMode == AUTOMODE) begin + NextState = INACTIVE; + end else if (TransmitStart) begin // If FIFO is written to, start again. + NextState = TRANSMIT; + end + end + INTERCS: begin // INTERCS case ---------------------------------- + if (EndOfINTERCS) begin + if (HasCSSCK) NextState = CSSCK; + else NextState = TRANSMIT; + end + end + INTERXFR: begin // INTERXFR case -------------------------------- + if (EndOfINTERXFR) begin + NextState = TRANSMIT; + end + end + default: begin + NextState = INACTIVE; + end + endcase + end + + assign Transmitting = CurrState == TRANSMIT; + assign DelayIsNext = (NextState == CSSCK | NextState == SCKCS | NextState == INTERCS | NextState == INTERXFR); + + // + always_ff @(posedge PCLK) begin + if (~PRESETn) begin + TransmitReg <= 8'b0; + end else if (TransmitLoad) begin + TransmitReg <= txFIFORead; + end else if (ShiftEdge) begin + TransmitReg <= {TransmitReg[6:0], TransmitReg[0]}; + end + end + + assign SPIOUT = TransmitReg[7]; + assign CS = CurrState == INACTIVE | CurrState == INTERCS; + +endmodule diff --git a/testbench/testbench.sv b/testbench/testbench.sv index e8ad09b36..5b053c763 100644 --- a/testbench/testbench.sv +++ b/testbench/testbench.sv @@ -540,7 +540,6 @@ module testbench; always @(posedge clk) begin if (LoadMem) begin $readmemh(memfilename, dut.core.lsu.dtim.dtim.ram.ram.RAM); - $display("Read memfile %s", memfilename); end if (CopyRAM) begin LogXLEN = (1 + P.XLEN/32); // 2 for rv32 and 3 for rv64 diff --git a/tests/coverage/WALLY-init-lib.h b/tests/coverage/WALLY-init-lib.h index 0ce72fd6b..dd29bbab8 100644 --- a/tests/coverage/WALLY-init-lib.h +++ b/tests/coverage/WALLY-init-lib.h @@ -28,6 +28,12 @@ // The PMP tests are sensitive to the exact addresses in this code, so unfortunately // modifying anything breaks those tests. +// Provides simple firmware services through ecall. Place argument in a0 and issue ecall: +// 0: change to user mode +// 1: change to supervisor mode +// 3: change to machine mode +// 4: terminate program + .section .text.init .global rvtest_entry_point diff --git a/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/references/WALLY-spi-01.reference_output b/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/references/WALLY-spi-01.reference_output index f62d9b088..425866ac2 100644 --- a/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/references/WALLY-spi-01.reference_output +++ b/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/references/WALLY-spi-01.reference_output @@ -32,59 +32,59 @@ 00000003 -00000074 +00000074 # spi_burst_send -00000063 +00000063 # spi_burst_send -00000052 +00000052 # spi_burst_send -00000041 +00000041 # spi_burst_send -000000A1 +000000A1 # spi_burst_send 00000003 -000000B2 +000000B2 # spi_burst_send 00000001 -000000C3 +000000C3 # spi_burst_send -000000D4 +000000D4 # spi_burst_send 00000003 -000000A4 +000000A4 # tx_data write test 00000001 -000000B4 +000000B4 # tx_data write test -000000A5 +000000A5 # spi_burst_send -000000B5 +000000B5 # spi_burst_send -000000C5 +000000C5 # spi_burst_send -000000D5 +000000D5 # spi_burst_send -000000A7 +000000A7 # spi_burst_send -000000B7 +000000B7 # spi_burst_send -000000C7 +000000C7 # spi_burst_send 00000002 -000000D7 +000000D7 # spi_burst_send 00000000 00000011 #basic read write -000000FF +000000FF # first test sck_div -000000AE +000000AE # min sck_div first spi_burst_send 000000AD