diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..c162cf9ba --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,19 @@ +name: Test +on: + push: + pull_request: + branches: [main] + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v3 # required + - name: Run Regression Tests + uses: Purdue-SoCET/SoCET-CI@main # core usage + with: + targets: | # List all FuseSoC Test Targets + make config + make verilate \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6bfc103d6..52703ebfe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,16 @@ # ignore the build and run files build/ sim_out/ +obj_dir/ +rvb_out/ # ignore memory files from test cases *.hex meminit.ver verification/asm-tests/RV32I/*.elf verification/asm-tests/RV32I/*.log verification/asm-tests/RV32I/*.hex +*.bin +*.elf # config files are auto-generated source_code/include/component_selection_defines.vh verification/c-firmware/custom_instruction_calls.h @@ -27,3 +31,62 @@ source_code/fpga/output_files *.lock-waf* # Ignore files for commands for moving stuff around *.cmd +mitll* +# Ignore temporary files created by gedit +*~ +# Ignore test cache files +run_tests_cache.json +# Ignore log files and Verilator outputs +*.log +*.fst +memsim.dump +meminit.bin + +# ignore files auto-generated by vscode. +_info +_vmake +*.qdb +*.qtl +*.qpg + +# ignore python bytecode files +*.pyc +__pycache__/ + +# ignore xrun files +INCA_libs/ +xcelium.d/ +xrun.history +xrun.key +.simvision +waves.shm/ + +open_files.sh + +# fusesoc +fusesoc_libraries +fusesoc.conf + +# synthesis scripts +desflow1/ +.vscode/ + +# uvm +work +mapped +fpga +transcript +*.wlf +README +*.swp +._* +*.log +*.deps +*.hex +*.ver +mti* +*.diff +system.summary +system_fpga.summary +*.vstf +*vsim* \ No newline at end of file diff --git a/.rules.verible_lint b/.rules.verible_lint new file mode 100644 index 000000000..98a2d74ea --- /dev/null +++ b/.rules.verible_lint @@ -0,0 +1,3 @@ +parameter-name-style=localparam_style:CamelCase|ALL_CAPS +-explicit-function-lifetime +-explicit-task-lifetime diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..436dfb322 --- /dev/null +++ b/Makefile @@ -0,0 +1,88 @@ +ROOT := $(shell pwd) + +RISCV := $(ROOT)/source_code +RISCV_CORE := $(RISCV)/standard_core +PIPELINE := $(RISCV)/pipelines +RISCV_PKGS := $(RISCV)/packages +RISC_MGMT := $(RISCV)/risc_mgmt +SPARCE := $(RISCV)/sparce +PRIVS := $(RISCV)/privs +BRANCH_PREDICT := $(RISCV)/branch_predictors +CACHES := $(RISCV)/caches +RISCV_BUS := $(RISCV)/bus_bridges +RV32C := $(RISCV)/rv32c +RV32M_FILES := $(RISC_MGMT)/extensions/rv32m/carry_save_adder.sv $(RISC_MGMT)/extensions/rv32m/flex_counter_mul.sv $(RISC_MGMT)/extensions/rv32m/full_adder.sv $(RISC_MGMT)/extensions/rv32m/pp_mul32.sv $(RISC_MGMT)/extensions/rv32m/radix4_divider.sv $(RISC_MGMT)/extensions/rv32m/rv32m_decode.sv $(RISC_MGMT)/extensions/rv32m/rv32m_execute.sv $(RISC_MGMT)/extensions/rv32m/rv32m_memory.sv +RV32C_FILES := $(RV32C)/decompressor.sv $(RV32C)/fetch_buffer.sv $(RV32C)/rv32c_disabled.sv $(RV32C)/rv32c_enabled.sv $(RV32C)/rv32c_wrapper.sv +RISC_MGMT_FILES := $(RISC_MGMT)/risc_mgmt_wrapper.sv $(RISC_MGMT)/tspp/tspp_risc_mgmt.sv $(RV32M_FILES) +RISC_EXT_FILES := $(RISC_MGMT)/extensions/template/template_decode.sv $(RISC_MGMT)/extensions/template/template_execute.sv $(RISC_MGMT)/extensions/template/template_memory.sv +CORE_PKG_FILES := $(RISCV_PKGS)/rv32i_types_pkg.sv $(RISCV_PKGS)/alu_types_pkg.sv $(RISCV_PKGS)/risc_mgmt/template_pkg.sv $(RISCV_PKGS)/risc_mgmt/crc32_pkg.sv $(RISCV_PKGS)/risc_mgmt/rv32m_pkg.sv $(RISCV_PKGS)/risc_mgmt/test_pkg.sv $(RISCV_PKGS)/machine_mode_types_pkg.sv $(RISCV_PKGS)/machine_mode_types_1_12_pkg.sv $(RISCV_PKGS)/pma_types_1_12_pkg.sv +CORE_FILES := $(RISCV_CORE)/alu.sv $(RISCV_CORE)/branch_res.sv $(RISCV_CORE)/control_unit.sv $(RISCV_CORE)/dmem_extender.sv $(RISCV_CORE)/endian_swapper.sv $(RISCV_CORE)/jump_calc.sv $(RISCV_CORE)/memory_controller.sv $(RISCV_CORE)/RISCVBusiness.sv $(RISCV_CORE)/rv32i_reg_file.sv $(RISCV_CORE)/top_core.sv +PIPELINE_FILES := $(PIPELINE)/tspp/tspp_execute_stage.sv $(PIPELINE)/tspp/tspp_fetch_stage.sv $(PIPELINE)/tspp/tspp_hazard_unit.sv #$(PIPELINE)/tspp/tspp.sv +PREDICTOR_FILES := $(BRANCH_PREDICT)/branch_predictor_wrapper.sv $(BRANCH_PREDICT)/nottaken_predictor/nottaken_predictor.sv +PRIV_FILES := $(PRIVS)/priv_wrapper.sv $(PRIVS)/priv_1_12/priv_1_12_block.sv $(PRIVS)/priv_1_12/priv_1_12_int_ex_handler.sv $(PRIVS)/priv_1_12/priv_1_12_csr.sv $(PRIVS)/priv_1_12/priv_1_12_pipe_control.sv $(PRIVS)/priv_1_12/priv_1_12_pma.sv +CACHE_FILES := $(CACHES)/caches_wrapper.sv $(CACHES)/pass_through/pass_through_cache.sv $(CACHES)/direct_mapped_tpf/direct_mapped_tpf_cache.sv $(CACHES)/separate_caches.sv +SPARCE_FILES := $(SPARCE)/sparce_wrapper.sv $(SPARCE)/sparce_disabled/sparce_disabled.sv $(SPARCE)/sparce_enabled/sparce_cfid.sv $(SPARCE)/sparce_enabled/sparce_enabled.sv $(SPARCE)/sparce_enabled/sparce_psru.sv $(SPARCE)/sparce_enabled/sparce_sasa_table.sv $(SPARCE)/sparce_enabled/sparce_sprf.sv $(SPARCE)/sparce_enabled/sparce_svc.sv +RISCV_BUS_FILES := $(RISCV_BUS)/generic_nonpipeline.sv $(RISCV_BUS)/ahb.sv +TRACKER_FILES := $(RISCV)/trackers/cpu_tracker.sv $(RISCV)/trackers/branch_tracker.sv + +COMPONENT_FILES_SV := $(CORE_PKG_FILES) $(RISC_MGMT_FILES) $(RISC_EXT_FILES) $(CORE_FILES) $(RV32C_FILES) $(PIPELINE_FILES) $(SPARCE_FILES) $(PREDICTOR_FILES) $(PRIV_FILES) $(CACHE_FILES) $(RISCV_BUS_FILES) $(TRACKER_FILES) + +TOP_ENTITY := RISCVBusiness + +HEADER_FILES := -I$(RISCV)/include + + +define USAGE +@echo "----------------------------------------------------------------------" +@echo " Build Targets:" +@echo " config: config core with example.yml" +@echo " verilate: Invoke 'FuseSoC run --build' to build Verilator target" +@echo " xcelium: Invoke 'FuseSoC run --build' to build Xcelium target" +@echo " lint: Invoke 'FuseSoC run --build' to run the Verilator lint target" +@echo " clean: Remove build directories" +@echo " veryclean: Remove fusesoc libraries & build directories" +@echo "----------------------------------------------------------------------" +endef + +.phony: default clean config verilate xcelium + + +default: + $(USAGE) + +config: + @echo "----------------------" + @echo " Running config_core" + @echo "----------------------" + @python3 scripts/config_core.py example.yml + +verilate: config + @fusesoc --cores-root . run --setup --build --build-root rvb_out --target sim --tool verilator socet:riscv:RISCVBusiness --make_options='-j' + @echo "------------------------------------------------------------------" + @echo "Build finished, you can run with 'fusesoc run', or by navigating" + @echo "to the build directory created by FuseSoC and using the Makefile there." + @echo "------------------------------------------------------------------" + +no_mem: config + @fusesoc --cores-root . run --setup --build --build-root rvb_out --target no_mc --tool verilator socet:riscv:RISCVBusiness --make_options='-j' + @echo "------------------------------------------------------------------" + @echo "Build finished, you can run with 'fusesoc run', or by navigating" + @echo "to the build directory created by FuseSoC and using the Makefile there." + @echo "------------------------------------------------------------------" + +xcelium: config + @fusesoc --cores-root . run --setup --build --build-root rvb_out --target sim --tool xcelium socet:riscv:RISCVBusiness + @echo "Build finished, you can run with 'fusesoc run', or by navigating" + @echo "to the build directory created by FuseSoC and using the Makefile there." + +lint: config + @fusesoc --cores-root . run --setup --build --build-root rvb_out --target lint --tool verilator socet:riscv:RISCVBusiness + @echo "Lint finished, no errors found" + +clean: + rm -rf build + rm -rf rvb_out + +veryclean: + rm -rf fusesoc_libraries + rm fusesoc.conf diff --git a/README.md b/README.md index e7e8187a9..24bf36388 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,43 @@ Design documents and project information for the RISC-V Business project can be [RISCV-Business Documentation](https://wiki.itap.purdue.edu/display/RISC/RISCV-Business) **Current User-Level ISA Spec :** v2.1 -**Current Privileged ISA Spec :** v1.11 +**Current Privileged ISA Spec :** v1.12 # Getting Started Getting started with RISCV Business consists of three steps: setting up the RISCV tool-chain, setting up the WAF build system, and finally interacting with the RISCV Business project itself. +## Fusesoc +This project uses the [Fusesoc](http://fusesoc.net/) build system. Use the following commands to get started... + +```bash +# install project dependencies & +# setup git pre-commit hook +./setup.sh + +# For ease of use, use the makefile to run FuseSoC tests +make # shows all build targets available + +# configure the RISC-V core +make config +# or python3 scripts/config_core.py .yml +# if you want to use a config other than example.yml + +make verilate # build with Verilator, or... +make xcelium # build with Xcelium +``` + +> Congrats! All dependencies are now set up. Now you can run simulations/tests: + + +```bash +# Run ISA tests +run_tests_verilator.py + +# Run specify binary on Verliator core simulation +./rvb_out/sim-verilator/Vtop_core meminit.bin +``` + ## Generating RISC-V tool-chain Refer to the following link for instructions on installing the RISC-V software tools: @@ -36,14 +67,6 @@ Then run after setting the environment variable "RISCV" to your install location ./build.sh ~~~ -## Installing the build environment - -RISCVBusiness uses SoCFoundationFlow, built off of the waf build system. The following repository contains the source for waf: - -[SoCFoundationFlow](https://github.com/mattaw/SoCFoundationFlow) - -Refer to SoCFoundationFlow for installation instructions. - ## Setup and Run RISCV Business First, clone the repository diff --git a/RISCVBusiness.core b/RISCVBusiness.core new file mode 100644 index 000000000..3be27fbfe --- /dev/null +++ b/RISCVBusiness.core @@ -0,0 +1,155 @@ +CAPI=2: +name: socet:riscv:RISCVBusiness:0.1.1 +description: RISC-V Core for AFTx series + +filesets: + rtl: + depend: + - "socet:bus-components:bus_protocol_if" + - "socet:bus-components:ahb_if" + - "socet:bus-components:apb_if" + - "socet:riscv:packages" + - "socet:riscv:stage3" + - "socet:riscv:priv" + - "socet:riscv:caches" + #- "socet:riscv:risc_mgmt" + - "socet:riscv:riscv_standard" + - "socet:riscv:riscv_include" + - "socet:riscv:rv32c" + - "socet:riscv:rv32m" + files: + - source_code/branch_predictors/branch_predictor_wrapper.sv + - source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv + - source_code/bus_bridges/generic_nonpipeline.sv + #- source_code/pipelines/tspp/tspp_hazard_unit.sv + #- source_code/pipelines/tspp/tspp_fetch_stage.sv + #- source_code/pipelines/tspp/tspp_execute_stage.sv + - source_code/sparce/sparce_disabled/sparce_disabled.sv + - source_code/RV32E/rv32e_reg_file.sv + - source_code/RV32E/rv32e_wrapper.sv + file_type: systemVerilogSource + + trackers: + files: + - source_code/trackers/branch_tracker.sv + - source_code/trackers/cpu_tracker.sv + file_type: systemVerilogSource + + buses: + files: + - "!tool_verilator? (source_code/bus_bridges/ahb.sv)" + - "!tool_verilator? (source_code/bus_bridges/apb.sv)" + file_type: systemVerilogSource + + fpga_wrapper: + files: + - source_code/fpga/RISCVBusiness_fpga.sv : {file_type: systemVerilogSource} + - source_code/ram/ram_sim_model.sv : {file_type: systemVerilogSource} + - source_code/ram/ram_wrapper.sv : {file_type: systemVerilogSource} + - rvb.sdc : {file_type: SDC} + + verilator_tb: + files: + - tb_core.cc : {fileType: cppSource} + file_type: systemVerilogSource + + no_memory_tb: + files: + - tb_core_no_memory.cc : {file_type: cppSource} + file_type: systemVerilogSource + + tb: + files: + - source_code/tb/tb_RISCVBusiness_self_test.sv + - source_code/ram/config_ram_wrapper.sv + - source_code/ram/ram_sim_model.sv + - source_code/ram/ram_wrapper.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + - buses + toplevel: RISCVBusiness + + sim: + <<: *default + description: Simulate w/TB + default_tool: verilator + filesets_append: + - trackers + - "tool_verilator? (verilator_tb)" + - "!tool_verilator? (tb)" + toplevel: + - "tool_verilator? (top_core)" + - "!tool_verilator? (tb_RISCVBusiness_self_test)" + tools: + xcelium: + xrun_options: + - +xmtimescale+1ns/100ps + modelsim: + vsim_options: + - -vopt + - -voptargs='+acc' + - -t ps + + verilator: + verilator_options: ["-Wno-SYMRSVDWORD", "-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] + #verilator_options: ["-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] + + no_mc: + <<: *default + description: Simulate w/TB, no memory controller + default_tool: verilator + filesets_append: + #- trackers + - "tool_verilator? (no_memory_tb)" + toplevel: + - "tool_verilator? (top_core)" + tools: + verilator: + verilator_options: ["-Wno-SYMRSVDWORD", "-Wno-lint", "--trace", "--trace-fst", "--trace-structs"] + + fpga: + <<: *default + filesets_append: + - buses + - fpga_wrapper + description: FPGA Synthesis + default_tool: quartus + parameters: + - SYNTHESIS=true + toplevel: top_core + tools: + quartus: + family: Cyclone IV E + device: EP4CE115F29C7 + + + lint: + filesets: + - rtl + - trackers + description: Linting + default_tool: veriblelint + toplevel: top_core + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search', '--waiver_files=riscv.waiver'] + + format: + filesets: + - rtl + description: Linting + default_tool: veribleformat + toplevel: top_core + tools: + veribleformat: + verible_format_args: ['--indentation_spaces=4', '--inplace'] + +parameters: + SYNTHESIS: + description: Set for tools performing synthesis + datatype: bool + paramtype: vlogdefine diff --git a/dev.yml b/dev.yml new file mode 100644 index 000000000..8d153ea28 --- /dev/null +++ b/dev.yml @@ -0,0 +1,35 @@ +# ISA Configurations +isa_params: + xlen : 32 + +# Microarchitectural Configurations +microarch_params: + # Branch/Jump Configurations + br_predictor_type : "not_taken" + + # Cache configurations + cache_config : "separate" + dcache_type : "pass_through" + icache_type : "pass_through" + + # Bus configurations + bus_endianness : "big" + bus_interface_type : "generic_bus_if" + + # Sparisty Optimizations + sparce_enabled : "disabled" + + # RV32C + rv32c_enabled : "disabled" + + # Halt + infinite_loop_halts : "true" + + # base ISA + base_isa: "RV32I" + +# RISC-MGMT Extension Configuration +risc_mgmt_params: + standard_extensions: + nonstandard_extensions: + diff --git a/example.yml b/example.yml index 6355d31f7..bd565a31e 100644 --- a/example.yml +++ b/example.yml @@ -9,13 +9,32 @@ microarch_params: # Cache configurations cache_config : "separate" - dcache_type : "pass_through" - icache_type : "pass_through" + dcache_type : "l1" + dcache_size : 1024 + dcache_block_size : 2 + dcache_assoc : 1 + icache_type : "l1" + icache_size : 1024 + icache_block_size : 2 + icache_assoc : 1 + noncache_start_addr : 32'hF000_0000 # Bus configurations bus_endianness : "big" bus_interface_type : "generic_bus_if" + # Sparisty Optimizations + sparce_enabled : "disabled" + + # RV32C + rv32c_enabled : "disabled" + + # Halt + infinite_loop_halts : "true" + + # base ISA + base_isa: "RV32I" + # RISC-MGMT Extension Configuration risc_mgmt_params: standard_extensions: diff --git a/isa_test.yml b/isa_test.yml new file mode 100644 index 000000000..e3a9ea0c7 --- /dev/null +++ b/isa_test.yml @@ -0,0 +1,34 @@ +# ISA Configurations +isa_params: + xlen : 32 + +# Microarchitectural Configurations +microarch_params: + # Branch/Jump Configurations + br_predictor_type : "not_taken" + + # Cache configurations + cache_config : "separate" + dcache_type : "pass_through" + icache_type : "pass_through" + + # Bus configurations + bus_endianness : "big" + bus_interface_type : "generic_bus_if" + + # Sparisty Optimizations + sparce_enabled : "disabled" + + # RV32C + rv32c_enabled : "disabled" + + # Halt + infinite_loop_halts : "true" + + # base ISA + base_isa: "RV32I" + +# RISC-MGMT Extension Configuration +risc_mgmt_params: + standard_extensions: + nonstandard_extensions: diff --git a/run_tests.py b/run_tests.py index 713cfac97..f4c4a7990 100755 --- a/run_tests.py +++ b/run_tests.py @@ -38,12 +38,13 @@ FILE_NAME = None ARCH = "RV32I" SUPPORTED_ARCHS = [] -SUPPORTED_TEST_TYPES = ['asm', 'c', 'selfasm', ""] +SUPPORTED_TEST_TYPES = ['asm', 'c', 'selfasm', "sparce", ""] +SPARCE_MODULES = ['sparce_svc', 'sparce_sprf', 'sparce_sasa_table', 'sparce_psru', 'sparce_cfid'] TEST_TYPE = "" # Change this variable to the filename (minus extension) # of the top level file for your project. This should # match the file name given in the top level wscript -TOP_LEVEL = "RISCVBusiness" +TOP_LEVEL = "RISCVBusiness" # NOTE: Adjust this module name to adjust what top level is needed for the Cadence simulation def parse_arguments(): global ARCH, FILE_NAME, SUPPORTED_ARCHS, TEST_TYPE @@ -74,16 +75,19 @@ def parse_arguments(): SUPPORTED_ARCHS = glob.glob('./verification/' + test_file_dir + '*') SUPPORTED_ARCHS = [a.split('/'+test_file_dir)[1] for a in SUPPORTED_ARCHS] if ARCH not in SUPPORTED_ARCHS: - print "ERROR: No " + test_type + " tests exist for " + ARCH - sys.exit(1) - else: - if TEST_TYPE == 'selfasm': - test_file_dir = 'self-tests/' + if test_type != 'sparce': + print "ERROR: No " + test_type + " tests exist for " + ARCH + sys.exit(1) else: + if TEST_TYPE == 'sparce': + pass + elif TEST_TYPE == 'selfasm': + test_file_dir = 'self-tests/' + else: test_file_dir = TEST_TYPE + '-tests/' - SUPPORTED_ARCHS = glob.glob('./verification/' + test_file_dir + '*') - SUPPORTED_ARCHS = [a.split('/'+test_file_dir)[1] for a in SUPPORTED_ARCHS] - if ARCH not in SUPPORTED_ARCHS: + SUPPORTED_ARCHS = glob.glob('./verification/' + test_file_dir + '*') + SUPPORTED_ARCHS = [a.split('/'+test_file_dir)[1] for a in SUPPORTED_ARCHS] + if ARCH not in SUPPORTED_ARCHS: print "ERROR: No " + TEST_TYPE + " tests exist for " + ARCH sys.exit(1) @@ -550,6 +554,43 @@ def run_asm(): return failures +def run_sparce(): + failures = 0 + print "starting sparce module tests..." + for module in SPARCE_MODULES: + + pass_msg = '{0:<40}{1:>20}'.format(module,START_GREEN + '[PASSED]' + END_COLOR) + fail_msg = '{0:<40}{1:>20}'.format(module,START_RED + '[FAILED]' + END_COLOR) + + output_dir = './sim_out/sparce/' + module + '/' + if not os.path.exists(output_dir): + try: + os.makedirs(output_dir) + except OSError as exc: # Guard against race condition + if exc.errno != errno.EEXIST: + raise + cmd_arr = ['waf', 'configure', '--top_level=' + module] + failure = subprocess.call(cmd_arr, stdout=FNULL) + if failure: + print "Error configuring test for " + module + failures += 1 + else: + cmd_arr = ['waf', 'verify_source'] + log = open(output_dir + 'waf_output.log', 'w') + log.write('Now running ' + module) + failure = subprocess.call(cmd_arr, stdout=log) + if failure: + log.close() + log = open(output_dir + 'waf_output.log', 'r') + for line in log: + print line + failures += 1 + print fail_msg + else: + print pass_msg + + return failures + def run_selfasm(): failures = 0 if FILE_NAME is None: @@ -560,7 +601,7 @@ def run_selfasm(): print "Starting self tests..." for f in files: # TODO: Fix timer error - if 'timer' in f: continue + #if 'timer2' in f: continue if 'asicfab' in os.environ['HOSTNAME']: # Do work remotely @@ -681,11 +722,15 @@ def run_c(): failures = run_c() # self tests elif TEST_TYPE == "selfasm": - failures = run_selfasm() + failures = run_selfasm() + # sparce tests + elif TEST_TYPE == "sparce": + failures = run_sparce() elif TEST_TYPE == "": - failures += run_asm() - failures += run_selfasm() - failures += run_c() + failures += run_asm() + failures += run_selfasm() + failures += run_c() + failures += run_sparce() else: print "To be implemented" sys.exit(failures) diff --git a/run_tests_config.json b/run_tests_config.json new file mode 100644 index 000000000..df4872c65 --- /dev/null +++ b/run_tests_config.json @@ -0,0 +1,15 @@ +{ + "arch": "RV32I", + "abi": "ilp32", + "xlen": "rv32i", + "test_type": "self-tests", + "top_level": "RISCVBusiness_self_test", + "test_filenames": ["*.S"], + + "cache_file": "run_tests_cache.json", + "build_dir": "./build", + "verif_dir": "./verification", + "asm_env": "./verification/asm-env/selfasm", + "sim_dir": "./sim_out", + "link_file": "link.ld" +} diff --git a/run_tests_verilator.py b/run_tests_verilator.py new file mode 100755 index 000000000..81d90cf83 --- /dev/null +++ b/run_tests_verilator.py @@ -0,0 +1,447 @@ +#!/usr/bin/python3 + +# +# Copyright 2022 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: updated_run_tests.py +# +# Created by: Nicholas Gildenhuys +# Email: ngildenh@purdue.edu +# Date Created: 03/29/2022 +# Description: A better script for running processor tests + + +######## Library Imports ######## +import argparse +from asyncio.log import logger +import sys +import os +import re +from typing import List, Type +import logging +import subprocess +import pathlib +import glob +import json +import itertools + + +######## Globals ######## +END_COLOR = "\033[0m" +GREEN = "\033[92m" +RED = "\033[31m" +#WAF_LOGFILE = "waf_output.log" +#BUILD_LOGFILE = "build_log.log" +DEFAULT_CONFIG_FILE = "run_tests_config.json" +MEMINIT_HEX_FILE = "meminit.hex" +ELF2HEX_COMAND = "/home/ecegrid/a/socpub/Public/riscv_dev/riscv_installs/RV_current/bin/elf2hex" +FAILED = "Failed" +PASSED = "Passed" + +######## Error Code Exception Class ######## +class Error(Exception): + """A custom exception class for raising exceptions from sub processes""" + def __init__(self, error_string: str): + self.error_string = error_string + def __str__(self) -> str: + return self.error_string + +class run_config(): + def __init__(self, config_json:dict): + # top level info + self.arch = str(config_json["arch"]) + self.abi = str(config_json["abi"]) + self.xlen = str(config_json["xlen"]) + self.test_type = str(config_json["test_type"]) + self.top_level = str(config_json["top_level"]) + # read directory definitions + self.build_dir = pathlib.Path(config_json["build_dir"]) + self.verif_dir = pathlib.Path(config_json["verif_dir"]) + self.asm_env = pathlib.Path(config_json["asm_env"]) + # directory of all where the test files are + self.test_dir = self.verif_dir/self.test_type/self.arch + + # output directories + self.sim_dir = pathlib.Path(config_json["sim_dir"]) + self.out_dir = self.sim_dir/self.arch + + # file definitions + self.link_file = self.verif_dir/pathlib.Path("asm-env")/config_json["link_file"] + # the cache file name, expects it in same as run directory + self.cache_file = pathlib.Path(config_json["cache_file"]) + # location of the test files + if(not self.test_dir.exists()): + raise Error(f"Test Directory: {self.test_dir} does not exists") + # init for apending later + self.test_filepaths = [] + self.test_filenames = config_json["test_filenames"] + return + +######## Bulk Work Functions ######## +def run_tests(config: Type[run_config]) -> List[str]: + """ + Should return the list of tests that were failed + """ + print(f"Running Strings: {config.test_filepaths}") + # setup the sim out directory + if(not config.out_dir.exists()): + os.makedirs(config.out_dir) + + # setup the loggers + # the log names don't matter as they will be replaced in the for loop + build_logger = setup_logger("build_logger", pathlib.Path("./build_log.log")) + waf_logger = setup_logger("waf_logger", pathlib.Path("./waf_log.log")) + + # setup caching dict mechanism + test_status = dict() + # read in the cached results if possible + if(pathlib.Path(config.cache_file).exists()): + with open(config.cache_file, "r") as cache_fp: + test_status = json.load(cache_fp) + + # run the tests + try: + for file in config.test_filepaths: + filepath = pathlib.Path(file) + # check if there is a cached result + test_cached_result = FAILED + if(filepath.stem in test_status): + test_cached_result = test_status[filepath.stem] + + # skip files that begin with _ + # or files that have already passed + if(filepath.name[0] == '_' or test_cached_result == PASSED): + print(f"Skipping Test: {filepath.name}") + continue + print(f"-----------------------------") + print(f"Running Test: {filepath.name}") + # setup the test output directory if it is not there + test_out_dir = config.out_dir/filepath.stem + # setup the sim out folder + if(not test_out_dir.exists()): + os.makedirs(test_out_dir) + # update the loggers + build_log_filepath = test_out_dir/str(filepath.stem+"_build.log") + build_logger = change_logger_file_handlers(build_logger, build_log_filepath) + waf_log_filepath = test_out_dir/str(filepath.stem+"_waf.log") + waf_logger = change_logger_file_handlers(waf_logger, waf_log_filepath) + + # compile the assembly file + print(f" - Compiling...") + outpath = test_out_dir/str(filepath.stem+".elf") + hex_filepath = compile_asm(filepath, outpath, config, build_logger) + # clean up the hex file - needs writting + #clean_init_hex(hex_filepath) + print(f" - Running Verilator...") + # run self sim - basic done + res = run_sim(config.top_level, waf_logger) + #waf_log_filepath = pathlib.Path(waf_logger.handlers[0].baseFilename) + print(f" - Checking Results...") + if 'PASSED' in res: + print(GREEN + '[PASSED]' + END_COLOR) + result = PASSED + else: + print(RED + '[FAILED]' + END_COLOR) + result = FAILED + # check results - + #result = check_self_results(waf_log_filepath, build_logger) + + # cache the result in the dict + test_status[filepath.stem] = result + # print(test_status) + # catch keyboard interrupts to flush the cache file + except KeyboardInterrupt: + pass # yes I know that this is not the pest practice + + with open(config.cache_file, "w") as cache_fp: + json.dump(test_status, cache_fp) + + return + + +# compile the assembly file - done +def compile_asm(filepath: Type[pathlib.Path], outpath: Type[pathlib.Path],\ + config: Type[run_config], logger: Type[logging.Logger])\ + -> Type[pathlib.Path]: + # main compile arguments + # notes: need to parameratize the .T, .I, abi, and xlen flags + # also probably pass the filepath too + compile_cmd_arr = ["riscv64-unknown-elf-gcc", + "-march=" + config.xlen, "-mabi=" + config.abi, + "-static", "-mcmodel=medany", "-fvisibility=hidden", + "-nostdlib", "-nostartfiles", + "-T"+str(config.link_file), + "-I"+str(config.asm_env), str(filepath), "-o", + str(outpath)] + + log_header("riscv64-unknown-elf-gcc", logger) + try: + compile_process: Type[subprocess.CompletedProcess] = subprocess.run(compile_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except subprocess.CalledProcessError as p_error: + compile_process = p_error + finally: + log_subprocess(compile_process, logger) + + + # create the mem init file + # NOTE: Hard coded values here that I am too lazy to config + #elf_2_hex_cmd_arr = [ELF2HEX_COMAND, "8", "65536", str(outpath), "2147483648"] + elf_2_bin = ['riscv64-unknown-elf-objcopy', '-O', 'binary', str(outpath), 'meminit.bin'] + # hex file return this for cleaning + hex_filepath = outpath.parent/MEMINIT_HEX_FILE + + log_header("elf2hex", logger) + elf_2_hex_process: Type[subprocess.CompletedProcess] = subprocess.run(elf_2_bin, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + log_subprocess(elf_2_hex_process, logger, level=logging.ERROR) + #with open(hex_filepath, "w") as hex_file: + # hex_file.write(str(elf_2_hex_process.stdout, encoding="utf-8")) + + logger.info(f"Finished {filepath.name} Compilation") + + return hex_filepath + +# run the simulation +def run_sim(top_level: str, logger: Type[logging.Logger]) -> None: + + # TODO: Assuming simulator already built + cmd_arr = ['./rvb_out/sim-verilator/Vtop_core', 'meminit.bin'] + res = subprocess.run(cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + + #config_cmd_arr = ["waf", "configure", "--top_level=" + top_level] + #log_header("waf configure", logger) + #config_process = subprocess.run(config_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + #log_subprocess(config_process, logger) + + #sim_cmd_arr = ["waf", "verify_source"] + #log_header("waf verify_source", logger) + #sim_process = subprocess.run(sim_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + #log_subprocess(sim_process, logger) + + return res.stdout + +def run_spike_asm(filepath: str) -> None: + return +# compare the results +def compare_results(filepath: str) -> bool: + return False +# compare spike results +def compare_spike_results(): + return +# just check selfasm results +def check_self_results(filepath: Type[pathlib.Path],\ + logger: Type[logging.Logger]) -> str: + # filepath should be path to the waf_output.log + short_name = filepath.parent.stem + pass_msg = '{0:<10}{1}'.format(short_name,GREEN + '[PASSED]' + END_COLOR) + fail_msg = '{0:<10}{1}'.format(short_name,RED + '[FAILED]' + END_COLOR) + with open(filepath, 'r') as waf_output: + waf_output_text = waf_output.read() + match = re.search(r'SUCCESS', waf_output_text) + if match: + print(pass_msg) + logger.info(pass_msg) + return PASSED + else: + match = re.search(r'(ERROR:)\s+(.*)', waf_output_text) + print(match.group(2)) + print(fail_msg) + logger.error(fail_msg) + return FAILED + +######## Side Helper Function ######## +def log_subprocess(complete_proc: Type[subprocess.CompletedProcess],\ + logger: Type[logging.Logger], level=logging.NOTSET) -> None: + """ Logs the std output to the logfile, then if there is an error + it raises an exception that prints out the output from stderr + """ + # this is mainly for the elf2hex so we dont get the hexdump in the log + # but we can still get errors + if(level <= logging.INFO): + logger.info(str(complete_proc.stdout, encoding="utf-8")) + # put in error if there are any and raise an exception + if(complete_proc.returncode): + error_string = str(complete_proc.stderr, encoding="utf-8") + logger.error(error_string) + raise Error(error_string=error_string) + return +def log_header(process: str, logger: Type[logging.Logger]) -> None: + logger.info(f"============ Starting {process} Process ============") + return + +# Create a temp file that consists of the Intel HEX format +# version of the meminit.hex file, delete the original log file +# and rename the temp file to the original's name +def clean_init_hex(filepath: Type[pathlib.Path]) -> None: + # filepath is the path object to the dirty meminit.hex file + # the one that was generated by the elf to hex + #short_name = file_name.split(ARCH+'/')[1][:-2] + #short_name = filepath.stem + #output_dir = './sim_out/' + ARCH + '/' + short_name + '/' + #output_dir = filepath.parent + #init_output = output_dir + 'meminit.hex' + init_output = filepath + build_dir = pathlib.Path("./build/") + + #cleaned_location = init_output[:len(file_name)-4] + "_clean.hex" + cleaned_location = filepath.parent/"meminit_clean.hex" + addr = 0x00 + with open(init_output, 'r') as init_file: + with open(cleaned_location, 'w')as cleaned_file: + for line in init_file: + stripped_line = line[:len(line)-1] + for i in range(len(stripped_line), 0, -8): + data_word = stripped_line[i-8:i] + new_data_word = data_word[6:8] + data_word[4:6] + new_data_word += data_word[2:4] + data_word[0:2] + checksum = calculate_checksum_str(int(new_data_word, 16), addr) + if len(checksum) < 2: + checksum = '0' + checksum + addr_str = hex(addr//4)[2:] + #left pad the string with 0s until 4 hex digits + while len(addr_str) < 4: + addr_str = '0' + addr_str + if new_data_word != "00000000": + out = ":04" + addr_str + "00" + new_data_word + checksum + '\n' + cleaned_file.write(out) + addr += 0x4 + # add the EOL record to the file + cleaned_file.write(":00000001FF") + + + subprocess.call(['mv', str(init_output), str(init_output.parent/"meminit_dirty.hex")]) + subprocess.call(['mv', str(cleaned_location), str(init_output)]) + if not os.path.exists(build_dir): + os.makedirs(build_dir) + subprocess.call(['cp', str(init_output), str(build_dir/"meminit.hex")]) + return + +# Returns the string representation of the +# checksum for the given data and address values +def calculate_checksum_str(data: int, addr: int) -> str: + addr = addr//4 + high_addr = (addr & 0xFF00) >> 8 + low_addr = addr & 0x00FF + data1 = data & 0x000000FF + data2 = (data & 0x0000FF00) >> 8 + data3 = (data & 0x00FF0000) >> 16 + data4 = (data & 0xFF000000) >> 24 + checksum = 4 + high_addr + low_addr + checksum += data1 + data2 + data3 + data4 + checksum = checksum & 0xFF + checksum = int(invert_bin_string(bin(checksum)[2:]),2) + checksum += 1 + checksum_lower_byte = hex(checksum)[2:] + if len(checksum_lower_byte) > 2: + checksum_lower_byte = checksum_lower_byte[-2:] + return checksum_lower_byte + +def invert_bin_string(bin_string: str) -> str: + inverted = '' + while len(bin_string) < 8: + bin_string = '0' + bin_string + for bit in bin_string: + if bit == '0': + inverted = inverted + '1' + else: + inverted = inverted + '0' + return inverted + +######## Logger Related Functions ######## +def setup_logger(name: str, log_filepath: Type[pathlib.Path],\ + level=logging.DEBUG) -> Type[logging.Logger]: + handler = logging.FileHandler(filename=log_filepath, mode="w") + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + logger = logging.getLogger(name) + logger.setLevel(level) + logger.addHandler(handler) + return logger + +def change_logger_file_handlers(logger: Type[logging.Logger],\ + new_filepath: Type[pathlib.Path]) -> Type[logging.Logger]: + """ Removes all old file handlers and replaces it with a new one """ + # remove all old handlers + for handler in logger.handlers: + logger.removeHandler(handler) + handler.close() + # add the new one + new_handler = logging.FileHandler(filename=new_filepath, mode="w") + formatter = logging.Formatter('[%(asctime)s] %(levelname)-4s: %(message)s') + new_handler.setFormatter(formatter) + + logger.addHandler(new_handler) + return logger + +######## Main Function ######## +def main(): + return + +def parse_args()-> Type[run_config]: + parser = argparse.ArgumentParser(description="Run various processor tests at the top level of RISCVBusisness") + parser.add_argument("--config_file", "-c", dest="config_file", + type=str, default=DEFAULT_CONFIG_FILE, + help="Specify the config file path") + parser.add_argument("--arch", "-a", dest="arch", + type=str, default=None, + help="Specify the architecture targeted. Option(s): RV32I Default: RV32I") + parser.add_argument("--test_type", "-t", dest="test_type", + type=str, default=None, + help="Specify what type of tests to run. Option(s): asm, selfasm,c Default: selfasm") + parser.add_argument("file_names", metavar="file_names", + type=str, nargs="*", + help="Run all tests that begin with this string. Optional") + parser.add_argument("--clean", action="store_true", dest="clean", + help="Clean the cache file before running") + args = parser.parse_args() + + conf_dict = dict() + with open(args.config_file, "r") as conf_fp: + conf_dict = json.load(conf_fp) + + config = run_config(conf_dict) + if(args.arch): + config.arch = args.arch + if(args.test_type): + config.test_type = args.test_type + if(args.file_names): + # find all the files that match the pattern + print(args.file_names) + config.test_filenames = args.file_names + # get the list of test files + test_files =[] + for filename in config.test_filenames: + test_files.append(glob.glob(str(config.test_dir/filename))) + config.test_filepaths = list(itertools.chain(*test_files)) + + # process the clean flag, remove the cache file if want a clean run + if(args.clean): + try: + os.remove(config.cache_file) + except FileNotFoundError: + pass # ignore if the file is not there + + + return config + +######## Main Function ######## +if __name__ == "__main__": + print("Running Main...") + # setup the logfile + #logging.basicConfig(filename=log_filepath, mode="w", level=logging.DEBUG) + config = parse_args() + run_tests(config) + # shutdown any remaining loggers + logging.shutdown() diff --git a/scripts/config_core.py b/scripts/config_core.py index 2a2e3edda..24ef976f7 100755 --- a/scripts/config_core.py +++ b/scripts/config_core.py @@ -40,11 +40,29 @@ 'br_predictor_type' : ['not_taken'], # Cache Configurations 'cache_config' : ['separate'], - 'dcache_type' : ['pass_through', 'direct_mapped_tpf'], - 'icache_type' : ['pass_through', 'direct_mapped_tpf'], + 'dcache_type' : ['pass_through', 'direct_mapped_tpf', 'l1'], + 'icache_type' : ['pass_through', 'direct_mapped_tpf', 'l1'], + # Cache Configurations (free_params) + 'noncache_start_addr' : [], + # Cache Configurations (int_params) + 'dcache_size' : [], + 'dcache_block_size' : [], + 'dcache_assoc' : [], + 'icache_size' : [], + 'icache_block_size' : [], + 'icache_assoc' : [], # Bus Configurations 'bus_endianness' : ['big', 'little'], - 'bus_interface_type' : ['ahb_if', 'generic_bus_if'] + 'bus_interface_type' : ['ahb_if', 'generic_bus_if', 'apb_if'], + # Sparisty Optimizations + 'sparce_enabled' : [ 'enabled', 'disabled' ], + # RV32C + 'rv32c_enabled' : [ 'enabled', 'disabled' ], + # base ISA Configurations + 'base_isa': ['RV32I', 'RV32E'], + + # Halt Enable -- Good for testing, not for tapeout + 'infinite_loop_halts' : ['true', 'false'] } RISC_MGMT_PARAMS = \ @@ -65,7 +83,7 @@ def load_configuration(file_name): with open(file_name, 'r') as f: try: - config = yaml.load(f) + config = yaml.full_load(f) except yaml.parser.ParserError: sys.exit('Parse of '+ file_name + ' failed. Please check yml syntax') return config @@ -77,7 +95,7 @@ def add_custom_instruction_header(name, encoding, length, opcode, fptr): fptr.write("GENERATE_CUSTOM_INSTRUCTION_R_TYPE(" + name + "," + opcode + "," + str(i) + "," + funct[0:7] + "," + funct[7:10] + ")\n") elif encoding in RISC_MGMT_PARAMS['nonstandard_extensions']['encoding']: - print "Warning: Generation of C Macros for the encoding " + encoding + " is not supported." + print("Warning: Generation of C Macros for the encoding " + encoding + " is not supported.") else: err = "Error: Invalid custom instruction encoding: " + encoding sys.exit(err) @@ -106,6 +124,8 @@ def create_include(config): # Handle localparam configurations isa_params = config['isa_params'] + free_params = ['noncache_start_addr'] + int_params = ['dcache_size', 'dcache_block_size', 'dcache_assoc', 'icache_size', 'icache_block_size', 'icache_assoc'] include_file.write('// ISA Params:\n') for isa_param in isa_params: try: @@ -127,13 +147,30 @@ def create_include(config): include_file.write('\n// Microarch Params:\n') uarch_params = config['microarch_params'] for uarch_param in uarch_params: - if uarch_params[uarch_param] not in UARCH_PARAMS[uarch_param]: + # errors + if uarch_param in int_params or uarch_param in free_params: + if uarch_param not in free_params and not isinstance(uarch_params[uarch_param], int): + err = 'Illegal configuration of incorrect type for ' + uarch_param + sys.exit(err) + if uarch_params['dcache_size'] % (uarch_params['dcache_block_size'] * uarch_params['dcache_assoc']) != 0: + err = 'Invalid dcache_size. Not divisible by block_size * assoc.' + sys.exit(err) + if uarch_params['icache_size'] % (uarch_params['icache_block_size'] * uarch_params['icache_assoc']) != 0: + err = 'Invalid icache_size. Not divisible by block_size * assoc.' + sys.exit(err) + elif uarch_params[uarch_param] not in UARCH_PARAMS[uarch_param]: err = 'Illegal configuration. ' + uarch_params[uarch_param] err += ' is not a valid configuration for ' + uarch_param sys.exit(err) + # write to parameter file + if uarch_param in free_params or uarch_param in int_params: + line = 'localparam ' + uarch_param.upper() + ' = ' + str(uarch_params[uarch_param]) + ';\n' else: line = 'localparam ' - line += uarch_param.upper() + ' = "' + uarch_params[uarch_param] + '"' + if isinstance(uarch_params[uarch_param], str): # deal with integer params + line += uarch_param.upper() + ' = "' + uarch_params[uarch_param] + '"' + else: + line += uarch_param.upper() + ' = ' + str(uarch_params[uarch_param]) line += ';\n' include_file.write(line) diff --git a/scripts/config_core_2.py b/scripts/config_core_2.py new file mode 100755 index 000000000..892f5fb03 --- /dev/null +++ b/scripts/config_core_2.py @@ -0,0 +1,230 @@ +#!/usr/local/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: config_core.py +# +# Created by: Jacob R. Stevens +# Email: steven69@purdue.edu +# Date Created: 02/14/2017 +# Description: Configures a RISCV core based on the given YAML config file +import yaml +import argparse +import sys + +VH_FILE = 'source_code/include/component_selection_defines.vh' +C_FILE = 'verification/c-firmware/custom_instruction_calls.h' + +ISA_PARAMS = \ + { + 'xlen' : [32] + } + +UARCH_PARAMS = \ + { + # Branch/Jump Configurations + 'br_predictor_type' : ['not_taken'], + # Cache Configurations + 'cache_config' : ['separate'], + 'dcache_type' : ['pass_through', 'direct_mapped_tpf'], + 'icache_type' : ['pass_through', 'direct_mapped_tpf'], + # Bus Configurations + 'bus_endianness' : ['big', 'little'], + 'bus_interface_type' : ['ahb_if', 'generic_bus_if'], + # Sparisty Optimizations + 'sparce_enabled' : [ 'enabled', 'disabled' ], + # RV32C + 'rv32c_enabled' : [ 'enabled', 'disabled' ], + # base ISA Configurations + 'base_isa': ['RV32I', 'RV32E'], + + # Halt Enable -- Good for testing, not for tapeout + 'infinite_loop_halts' : ['true', 'false'] + } + +RISC_MGMT_PARAMS = \ + { + # Valid standard extensions + 'standard_extensions' : {'name' : ['rv32m']}, + # Valid nonstandard extensions + 'nonstandard_extensions' : {'encoding' : ['R_TYPE', 'M_TYPE', 'J_TYPE', 'BR_TYPE', 'G_TYPE']} + } + +NONSTANDARD_OPCODES = [ '0001011', + '0101011', + '1011011', + '1111011'] + +# Returns an object containing the parsed configuration file. +# Currently uses PyYAML and YAML format +def load_configuration(file_name): + with open(file_name, 'r') as f: + try: + config = yaml.load(f) + except yaml.parser.ParserError: + sys.exit('Parse of '+ file_name + ' failed. Please check yml syntax') + return config + +def add_custom_instruction_header(name, encoding, length, opcode, fptr): + if encoding == 'R_TYPE': + for i in range(length): + funct = format(i, '010b') + fptr.write("GENERATE_CUSTOM_INSTRUCTION_R_TYPE(" + + name + "," + opcode + "," + str(i) + "," + funct[0:7] + "," + funct[7:10] + ")\n") + elif encoding in RISC_MGMT_PARAMS['nonstandard_extensions']['encoding']: + print "Warning: Generation of C Macros for the encoding " + encoding + " is not supported." + else: + err = "Error: Invalid custom instruction encoding: " + encoding + sys.exit(err) + +# Creates the include file from the config object +def create_include(config): + include_file = open(VH_FILE, 'w') + c_file = open(C_FILE, 'w') + # Write include header to file + header = '/*\nWARNING: THIS FILE WAS AUTOMATICALLY GENERATED BY\n' + header += 'CONFIG_CORE. DO NOT MAKE CHANGES TO THIS FILE. ANY CHANGES\n' + header += 'MAY BE OVERWRITTEN. DO NOT VERSION CONTROL THIS FILE.\n*/\n' + header += '`ifndef COMPONENT_SELECTION_DEFINES_VH\n' + header += '`define COMPONENT_SELECTION_DEFINES_VH\n\n\n' + include_file.write(header) + + # Write include to c file + c_header = '/*\nWARNING: THIS FILE WAS AUTOMATICALLY GENERATED BY\n' + c_header += 'CONFIG_CORE. DO NOT MAKE CHANGES TO THIS FILE. ANY CHANGES\n' + c_header += 'MAY BE OVERWRITTEN. DO NOT VERSION CONTROL THIS FILE.\n*/\n' + c_header += '#ifndef CUSTOM_INSTRUCTION_CALLS_H\n' + c_header += '#define CUSTOM_INSTRUCTION_CALLS_H\n\n\n' + c_header += '#include "custom_instruction_macros.h"\n\n' + c_header += '// Custom Instruction Usage:\n// CALL_CUSTOM_INSTRUCTION_R_TYPE(, , , , )\n\n' + c_file.write(c_header) + + # Handle localparam configurations + isa_params = config['isa_params'] + include_file.write('// ISA Params:\n') + for isa_param in isa_params: + try: + if isa_params[isa_param] not in ISA_PARAMS[isa_param]: + err = 'Illegal configuration. ' + isa_params[isa_param] + err += ' is not a valid configuration for ' + isa_param + sys.exit(err) + else: + line = 'localparam ' + # xlen will be an integer in include file, so no quotes needed + if 'xlen' == isa_param: + line += isa_param.upper() + ' = ' + str(isa_params[isa_param]) + else: + line += isa_param.upper() + ' = "' + isa_params[isa_param] + '"' + line += ';\n' + include_file.write(line) + except TypeError: + sys.exit('Type Error. Please check to make sure .yml file is correct.') + include_file.write('\n// Microarch Params:\n') + uarch_params = config['microarch_params'] + for uarch_param in uarch_params: + if uarch_params[uarch_param] not in UARCH_PARAMS[uarch_param]: + err = 'Illegal configuration. ' + uarch_params[uarch_param] + err += ' is not a valid configuration for ' + uarch_param + sys.exit(err) + else: + line = 'localparam ' + line += uarch_param.upper() + ' = "' + uarch_params[uarch_param] + '"' + line += ';\n' + include_file.write(line) + + # Handle bus interface define + bus_type = uarch_params['bus_interface_type'].split('_if')[0] + bus_define = '`define BUS_INTERFACE_' + bus_type.upper() + '\n' + include_file.write(bus_define) + + # Handling of RISC-MGMT Extensions + rmgmt_extensions = [] + try: + if 'risc_mgmt_params' in config: + rmgmt_params = config['risc_mgmt_params'] + if rmgmt_params != None: + for rmgmt_param in rmgmt_params: + extensions = rmgmt_params[rmgmt_param] + if extensions != None: + for extension in extensions: + if rmgmt_param == "standard_extensions" and extension['name'] not in RISC_MGMT_PARAMS[rmgmt_param]['name']: + err = 'Unsupported extension: ' + extension['name'] + sys.exit(err) + else: + rmgmt_extensions.append([extension, rmgmt_param]) + except: + err = "Error Parsing RISC-MGMT extension configuration." + sys.exit(err) + + # Need to at least have the nop extension + if(len(rmgmt_extensions) == 0): + rmgmt_extensions.append([{'name':'template', 'encoding' : 'R_TYPE', 'length':1}, 'nonstandard_extension']) + + include_file.write('\n// RISC-MGMT Extensions:\n') + include_file.write('`define NUM_EXTENSIONS ' + str(len(rmgmt_extensions)) + '\n') + include_file.write('`define RISC_MGMT_EXTENSIONS\t\\\n') + ext_num = 0 + nonstandard_num = 0 + for extension in rmgmt_extensions: + if(extension[1] == 'standard_extensions'): + include_file.write('\t`ADD_EXTENSION('+extension[0]['name']+','+str(ext_num)+")") + elif (nonstandard_num < len(NONSTANDARD_OPCODES)): + include_file.write('\t`ADD_EXTENSION_WITH_OPCODE('+extension[0]['name']+','+ + str(ext_num)+','+ "7'b" + NONSTANDARD_OPCODES[nonstandard_num]+")") + if 'encoding' in extension[0].keys() and 'length' in extension[0].keys(): + add_custom_instruction_header(extension[0]['name'], extension[0]['encoding'], + extension[0]['length'], NONSTANDARD_OPCODES[nonstandard_num], c_file) + else: + err = "Error: Missing the encoding or lenght field for nonstandard extension" + sys.exit(err) + nonstandard_num = nonstandard_num + 1 + ext_num = ext_num + 1 + if(ext_num != len(rmgmt_extensions)): + include_file.write('\t\\\n') + else: + include_file.write('\n') + + #set defines to indicate what ISA support is present + include_file.write('\n') + for extension in rmgmt_extensions: + if(extension[1] == 'standard_extensions'): + include_file.write('`define ' + extension[0]['name'].upper() + '_SUPPORTED\t1\n') + + # Write include footer to file + footer = '\n`endif // COMPONENT_SELECTION_DEFINES_VH\n' + include_file.write(footer) + include_file.close() + + footer = '\n#endif // CUSTOM_INSTRUCTION_CALLS_H\n' + c_file.write(footer) + c_file.close() + + + +if __name__ == '__main__': + description = 'Configure a processor. This script takes a .yml' + description += ' configuration file as input and creates the file ' + description += 'component_selection_defines.vh in source_code/include.' + description += 'Caution: if this file already exists, it is overwritten.' + parser = argparse.ArgumentParser(description=description) + parser.add_argument('file_name', metavar='file_name', type=str, + help='The config file name') + args = parser.parse_args() + + #TODO: Check for .yaml/.yml + config = load_configuration(args.file_name) + create_include(config) diff --git a/scripts/pre-commit b/scripts/pre-commit new file mode 100755 index 000000000..9252ca88e --- /dev/null +++ b/scripts/pre-commit @@ -0,0 +1,8 @@ +#! /bin/sh +echo "Pre-Commit: Running Lint" +fusesoc --cores-root . run --no-export --target lint socet:aft:gpio + +if [ $? -ne 0 ] +then + exit 1 +fi diff --git a/setup.sh b/setup.sh new file mode 100755 index 000000000..344e45078 --- /dev/null +++ b/setup.sh @@ -0,0 +1,41 @@ +#! /bin/sh + +RED='\033[0;31m' +BLUE='\033[0;34m' +GREEN='\033[0;32m' +NC='\033[0m' # No Color + +info_print() { + echo -e "$BLUE$1$NC" +} + +error_print() { + echo -e "$RED$1$NC" +} + +success_print() { + echo -e "$GREEN$1$NC" +} + +echo "Initializing Repository..." + +if [ -d "./fusesoc_libraries" ] ; then + rm -rf ./fusesoc_libraries # clean old fusesoc libs + rm ./fusesoc.conf +fi + +info_print "Installing Digital Libraries" +if ! fusesoc library add digital-lib git@github.com:Purdue-SoCET/digital-lib.git ; then + error_print "Failed to fetch digital-lib: ensure that ssh key is set up for repository permissions" + exit 1 +fi + +if ! fusesoc library add bus-components git@github.com:Purdue-SoCET/bus-components.git ; then + error_print "Failed to fetch bus-components: ensure that ssh key is set up for repository permissions" + exit 1 +fi + +#info_print "Installing pre-commit hook" +#cp ./scripts/pre-commit ./.git/hooks/ + +success_print "Initialization Complete!" diff --git a/source_code/Floating_Point_Unit/source/ADD_step1.sv b/source_code/Floating_Point_Unit/source/ADD_step1.sv index 57629ba02..f6b0a90d4 100644 --- a/source_code/Floating_Point_Unit/source/ADD_step1.sv +++ b/source_code/Floating_Point_Unit/source/ADD_step1.sv @@ -13,43 +13,42 @@ // sign_not_shifted - sign of the floating point that does not get shifted // frac_not_shifted - fraction of the floating point that does not get shifted // exp_max - max exponent of the two given floating points -module ADD_step1 - ( - input [31:0] floating_point1_in, - input [31:0] floating_point2_in, - output sign_shifted, - output [25:0] frac_shifted, - output sign_not_shifted, - output [25:0] frac_not_shifted, - output [7:0] exp_max - ); +module ADD_step1 ( + input [31:0] floating_point1_in, + input [31:0] floating_point2_in, + output sign_shifted, + output [25:0] frac_shifted, + output sign_not_shifted, + output [25:0] frac_not_shifted, + output [7:0] exp_max +); - reg [7:0] unsigned_exp_diff; - reg cmp_out; //exp1 >= exp2 -> cmp_out == 0 - //exp1 < exp2 -> cmp_out == 1 - wire [31:0] floating_point_shift; - wire [31:0] floating_point_not_shift; - reg [31:0] shifted_floating_point; - - int_compare cmp_exponents ( - .exp1(floating_point1_in[30:23]), - .exp2(floating_point2_in[30:23]), - .u_diff(unsigned_exp_diff), - .cmp_out(cmp_out) - ); - assign floating_point_shift = cmp_out ? floating_point1_in : floating_point2_in; - assign floating_point_not_shift = cmp_out ? floating_point2_in : floating_point1_in; - assign exp_max = cmp_out ? floating_point2_in[30:23] : floating_point1_in[30:23]; - - right_shift shift_frac_with_smaller_exp ( - .fraction({1'b1, floating_point_shift[22:0], 2'b0}), - .shift_amount(unsigned_exp_diff), - .result(frac_shifted) - ); + reg [7:0] unsigned_exp_diff; + reg cmp_out; //exp1 >= exp2 -> cmp_out == 0 + //exp1 < exp2 -> cmp_out == 1 + wire [31:0] floating_point_shift; + wire [31:0] floating_point_not_shift; + reg [31:0] shifted_floating_point; + + int_compare cmp_exponents ( + .exp1(floating_point1_in[30:23]), + .exp2(floating_point2_in[30:23]), + .u_diff(unsigned_exp_diff), + .cmp_out(cmp_out) + ); + assign floating_point_shift = cmp_out ? floating_point1_in : floating_point2_in; + assign floating_point_not_shift = cmp_out ? floating_point2_in : floating_point1_in; + assign exp_max = cmp_out ? floating_point2_in[30:23] : floating_point1_in[30:23]; + + right_shift shift_frac_with_smaller_exp ( + .fraction({1'b1, floating_point_shift[22:0], 2'b0}), + .shift_amount(unsigned_exp_diff), + .result(frac_shifted) + ); + + assign frac_not_shifted = {1'b1, floating_point_not_shift[22:0], 2'b0}; + assign sign_not_shifted = floating_point_not_shift[31]; + assign sign_shifted = floating_point_shift[31]; - assign frac_not_shifted = {1'b1, floating_point_not_shift[22:0], 2'b0}; - assign sign_not_shifted = floating_point_not_shift[31]; - assign sign_shifted = floating_point_shift[31]; - endmodule diff --git a/source_code/Floating_Point_Unit/source/ADD_step2.sv b/source_code/Floating_Point_Unit/source/ADD_step2.sv index d7ac08b5d..f5ae248b4 100644 --- a/source_code/Floating_Point_Unit/source/ADD_step2.sv +++ b/source_code/Floating_Point_Unit/source/ADD_step2.sv @@ -10,60 +10,59 @@ // sign1/2 - signs of fractions to be added // exp_max_in - max exponent from step1 (if the sum is zero the exponent is set to zero) //Outputs: -// sign_out - sign of the result +// sign_out - sign of the result // sum - magnitude of the result regardless of any carry out // carry_out - signal if there is an oveflow from the addition // exp_max_out - if sum is non-zero, this is equal to exp_max_in -module ADD_step2 - ( - input [25:0] frac1, - input sign1, - input [25:0] frac2, - input sign2, - input [7:0] exp_max_in, // - output sign_out, - output [25:0] sum, - output carry_out, - output reg [7:0] exp_max_out// - ); +module ADD_step2 ( + input [25:0] frac1, + input sign1, + input [25:0] frac2, + input sign2, + input [ 7:0] exp_max_in, // + output sign_out, + output [25:0] sum, + output carry_out, + output reg [ 7:0] exp_max_out // +); - reg [26:0] frac1_signed; - reg [26:0] frac2_signed; - reg [26:0] sum_signed; - - always_comb begin : exp_max_assignment - if(sum_signed == 0) exp_max_out = 8'b00000000; - else exp_max_out = exp_max_in; - end - + reg [26:0] frac1_signed; + reg [26:0] frac2_signed; + reg [26:0] sum_signed; + + always_comb begin : exp_max_assignment + if (sum_signed == 0) exp_max_out = 8'b00000000; + else exp_max_out = exp_max_in; + end + + + + u_to_s change_to_signed1 ( + .sign(sign1), + .frac_unsigned(frac1), + .frac_signed(frac1_signed) + ); + + u_to_s change_to_signed2 ( + .sign(sign2), + .frac_unsigned(frac2), + .frac_signed(frac2_signed) + ); + + adder_26b add_signed_fracs ( + .frac1(frac1_signed), + .frac2(frac2_signed), + .sum (sum_signed), + .ovf (carry_out) + ); + + s_to_u change_to_unsigned ( + .frac_signed(sum_signed), + .sign(sign_out), + .frac_unsigned(sum) + ); - - u_to_s change_to_signed1( - .sign(sign1), - .frac_unsigned(frac1), - .frac_signed(frac1_signed) - ); - - u_to_s change_to_signed2( - .sign(sign2), - .frac_unsigned(frac2), - .frac_signed(frac2_signed) - ); - - adder_26b add_signed_fracs( - .frac1(frac1_signed), - .frac2(frac2_signed), - .sum(sum_signed), - .ovf(carry_out) - ); - s_to_u change_to_unsigned( - .frac_signed(sum_signed), - .sign(sign_out), - .frac_unsigned(sum) - ); - - endmodule - + diff --git a/source_code/Floating_Point_Unit/source/ADD_step3.sv b/source_code/Floating_Point_Unit/source/ADD_step3.sv index 809046a5c..4be3c96ec 100644 --- a/source_code/Floating_Point_Unit/source/ADD_step3.sv +++ b/source_code/Floating_Point_Unit/source/ADD_step3.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/23/18 // -//Module Summary: +//Module Summary: // Third step of addition operation in three-step pipeline. // Rounds result based on rounding mode (frm) and left shifts fraction if needed // @@ -14,85 +14,83 @@ //Outputs: // floating_point_out - final floating point -module ADD_step3 - ( - input ovf_in, - input unf_in, - input dz, // divide by zero flag - input inv, - input [2:0] frm, - input [7:0] exponent_max_in, - input sign_in, - input [25:0] frac_in, - input carry_out, - output [31:0] floating_point_out, - output [4:0] flags - ); +module ADD_step3 ( + input ovf_in, + input unf_in, + input dz, // divide by zero flag + input inv, + input [2:0] frm, + input [7:0] exponent_max_in, + input sign_in, + input [25:0] frac_in, + input carry_out, + output [31:0] floating_point_out, + output [4:0] flags +); - wire inexact; - wire sign; - wire [7:0] exponent; - wire [22:0] frac; - - assign {sign, exponent, frac} = floating_point_out; - - reg [7:0] exp_minus_shift_amount; - reg [25:0] shifted_frac; - reg [7:0] shifted_amount; - reg [7:0] exp_out; - reg ovf; - reg unf; - + wire inexact; + wire sign; + wire [ 7:0] exponent; + wire [22:0] frac; - left_shift shift_left ( - .fraction(frac_in), - .result(shifted_frac), - .shifted_amount(shifted_amount) - ); - - subtract SUB ( - .exp1(exponent_max_in), - .shifted_amount(shifted_amount), - .result(exp_minus_shift_amount) - ); + assign {sign, exponent, frac} = floating_point_out; - - reg [24:0] round_this; - - always_comb begin - ovf = 0; - unf = 0; - if(carry_out == 1) begin - round_this = frac_in[25:1]; - exp_out = exponent_max_in + 1; - if((exponent_max_in == 8'b11111110) && (~unf_in)) ovf = 1; - end - else begin - round_this = shifted_frac[24:0]; - exp_out = exp_minus_shift_amount; - if(({1'b0, exponent_max_in} < shifted_amount) && (~ovf_in)) unf = 1; - end - end + reg [ 7:0] exp_minus_shift_amount; + reg [25:0] shifted_frac; + reg [ 7:0] shifted_amount; + reg [ 7:0] exp_out; + reg ovf; + reg unf; - reg [31:0] round_out; - - rounder ROUND ( - .frm(frm), - .sign(sign_in), - .exp_in(exp_out), - .fraction(round_this), - .round_out(round_out), - .rounded(round_flag) - ); - - assign inexact = ovf_in | ovf | unf_in | unf | round_flag; - assign flags = {inv, dz, (ovf | ovf_in), (unf | unf_in), inexact}; - assign floating_point_out[31] = round_out[31]; - assign floating_point_out[30:0] = inv ? 31'b1111111101111111111111111111111 : + + left_shift shift_left ( + .fraction(frac_in), + .result(shifted_frac), + .shifted_amount(shifted_amount) + ); + + subtract SUB ( + .exp1(exponent_max_in), + .shifted_amount(shifted_amount), + .result(exp_minus_shift_amount) + ); + + + reg [24:0] round_this; + + always_comb begin + ovf = 0; + unf = 0; + if (carry_out == 1) begin + round_this = frac_in[25:1]; + exp_out = exponent_max_in + 1; + if ((exponent_max_in == 8'b11111110) && (~unf_in)) ovf = 1; + end else begin + round_this = shifted_frac[24:0]; + exp_out = exp_minus_shift_amount; + if (({1'b0, exponent_max_in} < shifted_amount) && (~ovf_in)) unf = 1; + end + end + + reg [31:0] round_out; + + rounder ROUND ( + .frm(frm), + .sign(sign_in), + .exp_in(exp_out), + .fraction(round_this), + .round_out(round_out), + .rounded(round_flag) + ); + + assign inexact = ovf_in | ovf | unf_in | unf | round_flag; + assign flags = {inv, dz, (ovf | ovf_in), (unf | unf_in), inexact}; + assign floating_point_out[31] = round_out[31]; + assign floating_point_out[30:0] = inv ? 31'b1111111101111111111111111111111 : ovf_in ? 31'b1111111100000000000000000000000 : ovf ? 31'b1111111100000000000000000000000 : unf_in ? 31'b0000000000000000000000000000000 : unf ? 31'b0000000000000000000000000000000 : round_out[30:0]; - + endmodule diff --git a/source_code/Floating_Point_Unit/source/FPU_top_level.sv b/source_code/Floating_Point_Unit/source/FPU_top_level.sv index 8db454c7b..9a6694798 100644 --- a/source_code/Floating_Point_Unit/source/FPU_top_level.sv +++ b/source_code/Floating_Point_Unit/source/FPU_top_level.sv @@ -14,260 +14,258 @@ // frm - rounding mode // funct7 - 7 bit operation code //Outputs: -// floating_point_out - result of operation +// floating_point_out - result of operation // flags - 5 error flags (overflow, underflow, divide by zero, inexact result, invalid operation) -module FPU_top_level -( - input clk, - input nrst, - input [31:0] floating_point1, - input [31:0] floating_point2, - input [2:0] frm, - input [6:0] funct7, - output [31:0] floating_point_out, - output [4:0] flags - ); - - - - reg [2:0] frm2; - reg [2:0] frm3; - reg [6:0] funct7_2; - reg [6:0] funct7_3; - - //funct7 definitions - localparam ADD = 7'b0100000; - localparam MUL = 7'b0000010; - - // ADD step1 outputs -> step2 inputs - //reg nxt_sign_shifted; - reg sign_shifted; - //reg [25:0] nxt_frac_shifted; - reg [25:0] frac_shifted; - //reg nxt_sign_not_shifted; - reg sign_not_shifted; - //reg [25:0] nxt_frac_not_shifted; - reg [25:0] frac_not_shifted; - //reg [7:0] nxt_exp_max; - reg [7:0] exp_max; - - // MUL step1 outputs -> step2 inputs - reg mul_sign1; - reg mul_sign2; - reg [7:0] mul_exp1; - reg [7:0] mul_exp2; - reg [25:0] product; - reg mul_carry_out; - - reg [61:0] step1_to_step2; - reg [61:0] nxt_step1_to_step2; - - - // ADD step2 outputs -> step3 inputs - reg add_sign_out; - //reg nxt_sign_out; - reg [25:0] add_sum; +module FPU_top_level ( + input clk, + input nrst, + input [31:0] floating_point1, + input [31:0] floating_point2, + input [ 2:0] frm, + input [ 6:0] funct7, + output [31:0] floating_point_out, + output [ 4:0] flags +); + + + + reg [2:0] frm2; + reg [2:0] frm3; + reg [6:0] funct7_2; + reg [6:0] funct7_3; + + //funct7 definitions + localparam ADD = 7'b0100000; + localparam MUL = 7'b0000010; + + // ADD step1 outputs -> step2 inputs + //reg nxt_sign_shifted; + reg sign_shifted; + //reg [25:0] nxt_frac_shifted; + reg [25:0] frac_shifted; + //reg nxt_sign_not_shifted; + reg sign_not_shifted; + //reg [25:0] nxt_frac_not_shifted; + reg [25:0] frac_not_shifted; + //reg [7:0] nxt_exp_max; + reg [ 7:0] exp_max; + + // MUL step1 outputs -> step2 inputs + reg mul_sign1; + reg mul_sign2; + reg [ 7:0] mul_exp1; + reg [ 7:0] mul_exp2; + reg [25:0] product; + reg mul_carry_out; + + reg [61:0] step1_to_step2; + reg [61:0] nxt_step1_to_step2; + + + // ADD step2 outputs -> step3 inputs + reg add_sign_out; + //reg nxt_sign_out; + reg [25:0] add_sum; //reg [25:0] nxt_sum; - reg add_carry_out; - //reg nxt_carry_out; - reg [7:0] add_exp_max; - //reg [7:0] nxt_exp_max_out; - - // MUL step2 outputs -> step3 inputs - reg mul_sign_out; - reg [7:0] sum_exp; - reg mul_ovf; - reg mul_unf; - - // invalid operation flag - reg inv; - reg inv2; - reg inv3; - - - reg [37:0] step2_to_step3; - reg [37:0] nxt_step2_to_step3; - - // right shift smaller fraction by difference in exponents - - ADD_step1 addStep1( - .floating_point1_in(floating_point1), - .floating_point2_in(floating_point2), - .sign_shifted(sign_shifted), - .frac_shifted(frac_shifted), - .sign_not_shifted(sign_not_shifted), - .frac_not_shifted(frac_not_shifted), - .exp_max(exp_max) - ); - - // multiply fractions - - MUL_step1 mulStep1( - .fp1_in(floating_point1), - .fp2_in(floating_point2), - .sign1(mul_sign1), - .sign2(mul_sign2), - .exp1(mul_exp1), - .exp2(mul_exp2), - .product(product), - .carry_out(mul_carry_out) - ); - - always_comb begin : check_for_invalid_op - inv = 0; - if(funct7 == ADD) begin - if((floating_point1[30:0] == 31'h7F800000) && - (floating_point2[30:0] == 31'h7F800000) && + reg add_carry_out; + //reg nxt_carry_out; + reg [ 7:0] add_exp_max; + //reg [7:0] nxt_exp_max_out; + + // MUL step2 outputs -> step3 inputs + reg mul_sign_out; + reg [ 7:0] sum_exp; + reg mul_ovf; + reg mul_unf; + + // invalid operation flag + reg inv; + reg inv2; + reg inv3; + + + reg [37:0] step2_to_step3; + reg [37:0] nxt_step2_to_step3; + + // right shift smaller fraction by difference in exponents + + ADD_step1 addStep1 ( + .floating_point1_in(floating_point1), + .floating_point2_in(floating_point2), + .sign_shifted(sign_shifted), + .frac_shifted(frac_shifted), + .sign_not_shifted(sign_not_shifted), + .frac_not_shifted(frac_not_shifted), + .exp_max(exp_max) + ); + + // multiply fractions + + MUL_step1 mulStep1 ( + .fp1_in(floating_point1), + .fp2_in(floating_point2), + .sign1(mul_sign1), + .sign2(mul_sign2), + .exp1(mul_exp1), + .exp2(mul_exp2), + .product(product), + .carry_out(mul_carry_out) + ); + + always_comb begin : check_for_invalid_op + inv = 0; + if (funct7 == ADD) begin + if((floating_point1[30:0] == 31'h7F800000) && + (floating_point2[30:0] == 31'h7F800000) && (floating_point1[31] ^ floating_point2[31])) begin - inv = 1; - end - end - - if(funct7 == MUL) begin - if(((floating_point1[30:0] == 31'h00000000) && + inv = 1; + end + end + + if (funct7 == MUL) begin + if(((floating_point1[30:0] == 31'h00000000) && (floating_point2[30:0] == 31'h7F800000)) || ((floating_point1[30:0] == 31'h7F800000) && (floating_point2[30:0] == 31'h00000000))) begin - inv = 1; - end - end - end // block: check_for_invalid_op - - - always_comb begin : select_op_step1to2 - case(funct7) - ADD: begin - nxt_step1_to_step2[61] = sign_shifted; - nxt_step1_to_step2[60:35] = frac_shifted; - nxt_step1_to_step2[34] = sign_not_shifted; - nxt_step1_to_step2[33:8] = frac_not_shifted; - nxt_step1_to_step2[7:0] = exp_max; - end - MUL: begin - nxt_step1_to_step2[61] = mul_sign1; - nxt_step1_to_step2[60] = mul_sign2; - nxt_step1_to_step2[59:52] = mul_exp1; - nxt_step1_to_step2[51:44] = mul_exp2; - nxt_step1_to_step2[43:18] = product; - nxt_step1_to_step2[17] = mul_carry_out; - - end - - endcase // case (funct7) - end // block: select_op - - always_ff @ (posedge clk, negedge nrst) begin : STEP1_to_STEP2 - if(nrst == 0) begin - frm2 <= 0; - step1_to_step2 <= 0; - funct7_2 <= 0; - inv2 <= 0;/* + inv = 1; + end + end + end // block: check_for_invalid_op + + + always_comb begin : select_op_step1to2 + case (funct7) + ADD: begin + nxt_step1_to_step2[61] = sign_shifted; + nxt_step1_to_step2[60:35] = frac_shifted; + nxt_step1_to_step2[34] = sign_not_shifted; + nxt_step1_to_step2[33:8] = frac_not_shifted; + nxt_step1_to_step2[7:0] = exp_max; + end + MUL: begin + nxt_step1_to_step2[61] = mul_sign1; + nxt_step1_to_step2[60] = mul_sign2; + nxt_step1_to_step2[59:52] = mul_exp1; + nxt_step1_to_step2[51:44] = mul_exp2; + nxt_step1_to_step2[43:18] = product; + nxt_step1_to_step2[17] = mul_carry_out; + + end + + endcase // case (funct7) + end // block: select_op + + always_ff @(posedge clk, negedge nrst) begin : STEP1_to_STEP2 + if (nrst == 0) begin + frm2 <= 0; + step1_to_step2 <= 0; + funct7_2 <= 0; + inv2 <= 0; /* sign_shifted <= 0; frac_shifted <= 0; sign_not_shifted <= 0; frac_not_shifted <= 0; exp_max <= 0;*/ - end - else begin - frm2 <= frm; - step1_to_step2 <= nxt_step1_to_step2; - funct7_2 <= funct7; - inv2 <= inv;/* + end else begin + frm2 <= frm; + step1_to_step2 <= nxt_step1_to_step2; + funct7_2 <= funct7; + inv2 <= inv; /* sign_shifted <= nxt_sign_shifted; frac_shifted <= nxt_frac_shifted; sign_not_shifted <= nxt_sign_not_shifted; frac_not_shifted <= nxt_frac_not_shifted; exp_max <= nxt_exp_max;*/ - end - end - - // add signed fractions - - ADD_step2 add_step2 ( - .frac1(step1_to_step2[60:35]), // frac_shifted - .sign1(step1_to_step2[61]), // sign_shifted - .frac2(step1_to_step2[33:8]), // frac_not_shhifted - .sign2(step1_to_step2[34]), // sign_not_shifted - .exp_max_in(step1_to_step2[7:0]), // exp_max - .sign_out(add_sign_out), - .sum(add_sum), - .carry_out(add_carry_out), - .exp_max_out(add_exp_max) - ); - - // add exponents and xor sign bits - - MUL_step2 mul_step2 ( - .sign1(step1_to_step2[61]), // mul_sign1 - .sign2(step1_to_step2[60]), // mul_sign2 - .exp1(step1_to_step2[59:52]), // mul_exp1 - .exp2(step1_to_step2[51:44]), // mul_exp2 - .sign_out(mul_sign_out), - .sum_exp(sum_exp), - .ovf(mul_ovf), - .unf(mul_unf), - .carry(step1_to_step2[17]) - ); - - always_comb begin : select_op_step2to3 - case(funct7_2) - ADD: begin - nxt_step2_to_step3[37:36]= 2'b00; - nxt_step2_to_step3[35] = add_sign_out; - nxt_step2_to_step3[34:9] = add_sum; - nxt_step2_to_step3[8] = add_carry_out; - nxt_step2_to_step3[7:0] = add_exp_max; - end - MUL: begin - nxt_step2_to_step3[37] = mul_ovf; - nxt_step2_to_step3[36] = mul_unf; - nxt_step2_to_step3[35] = mul_sign_out; - nxt_step2_to_step3[34:9] = step1_to_step2[43:18]; // product from MUL_step1 - nxt_step2_to_step3[8] = step1_to_step2[17]; // mul_carry_out; - nxt_step2_to_step3[7:0] = sum_exp; - - end - endcase // case (funct7_2) - end // block: select_op_step2to3 - - - always_ff @ (posedge clk, negedge nrst) begin : STEP2_to_STEP3 - if(nrst == 0) begin - funct7_3 <= 0; - step2_to_step3 <= 0; - frm3 <= 0; - inv3 <= 0; - end - else begin - funct7_3 <= funct7_2; - step2_to_step3 <= nxt_step2_to_step3; - frm3 <= frm2; - inv3 <= inv2; - end - end - - //shift fraction until 1 is the first digit - //round based on rounding mode - reg o; - - always_comb begin - if((step2_to_step3[7:0] == 8'b11111111) && (step2_to_step3[36] == 1'b0) && (step2_to_step3[8] == 0)) o = 1; - else o = step2_to_step3[37]; - end - - ADD_step3 step3 ( - .ovf_in(o), - .unf_in(step2_to_step3[36]), - .dz(1'b0), - .inv(inv3), - .frm(frm3), - .exponent_max_in(step2_to_step3[7:0]), - .sign_in(step2_to_step3[35]), - .frac_in(step2_to_step3[34:9]), - .carry_out(step2_to_step3[8]), - .floating_point_out(floating_point_out), - .flags(flags) - ); - - endmodule + end + end + + // add signed fractions + + ADD_step2 add_step2 ( + .frac1 (step1_to_step2[60:35]), // frac_shifted + .sign1 (step1_to_step2[61]), // sign_shifted + .frac2 (step1_to_step2[33:8]), // frac_not_shhifted + .sign2 (step1_to_step2[34]), // sign_not_shifted + .exp_max_in (step1_to_step2[7:0]), // exp_max + .sign_out (add_sign_out), + .sum (add_sum), + .carry_out (add_carry_out), + .exp_max_out(add_exp_max) + ); + + // add exponents and xor sign bits + + MUL_step2 mul_step2 ( + .sign1(step1_to_step2[61]), // mul_sign1 + .sign2(step1_to_step2[60]), // mul_sign2 + .exp1(step1_to_step2[59:52]), // mul_exp1 + .exp2(step1_to_step2[51:44]), // mul_exp2 + .sign_out(mul_sign_out), + .sum_exp(sum_exp), + .ovf(mul_ovf), + .unf(mul_unf), + .carry(step1_to_step2[17]) + ); + + always_comb begin : select_op_step2to3 + case (funct7_2) + ADD: begin + nxt_step2_to_step3[37:36]= 2'b00; + nxt_step2_to_step3[35] = add_sign_out; + nxt_step2_to_step3[34:9] = add_sum; + nxt_step2_to_step3[8] = add_carry_out; + nxt_step2_to_step3[7:0] = add_exp_max; + end + MUL: begin + nxt_step2_to_step3[37] = mul_ovf; + nxt_step2_to_step3[36] = mul_unf; + nxt_step2_to_step3[35] = mul_sign_out; + nxt_step2_to_step3[34:9] = step1_to_step2[43:18]; // product from MUL_step1 + nxt_step2_to_step3[8] = step1_to_step2[17]; // mul_carry_out; + nxt_step2_to_step3[7:0] = sum_exp; + + end + endcase // case (funct7_2) + end // block: select_op_step2to3 + + + always_ff @(posedge clk, negedge nrst) begin : STEP2_to_STEP3 + if (nrst == 0) begin + funct7_3 <= 0; + step2_to_step3 <= 0; + frm3 <= 0; + inv3 <= 0; + end else begin + funct7_3 <= funct7_2; + step2_to_step3 <= nxt_step2_to_step3; + frm3 <= frm2; + inv3 <= inv2; + end + end + + //shift fraction until 1 is the first digit + //round based on rounding mode + reg o; + + always_comb begin + if((step2_to_step3[7:0] == 8'b11111111) && (step2_to_step3[36] == 1'b0) && (step2_to_step3[8] == 0)) + o = 1; + else o = step2_to_step3[37]; + end + + ADD_step3 step3 ( + .ovf_in(o), + .unf_in(step2_to_step3[36]), + .dz(1'b0), + .inv(inv3), + .frm(frm3), + .exponent_max_in(step2_to_step3[7:0]), + .sign_in(step2_to_step3[35]), + .frac_in(step2_to_step3[34:9]), + .carry_out(step2_to_step3[8]), + .floating_point_out(floating_point_out), + .flags(flags) + ); + +endmodule diff --git a/source_code/Floating_Point_Unit/source/MUL_step1.sv b/source_code/Floating_Point_Unit/source/MUL_step1.sv index 10c8dd47a..1d522a119 100644 --- a/source_code/Floating_Point_Unit/source/MUL_step1.sv +++ b/source_code/Floating_Point_Unit/source/MUL_step1.sv @@ -13,31 +13,30 @@ // product - result of fraction multiplication // carry_out - signal if there is a carry out of the multiplication -module MUL_step1 - ( - input [31:0] fp1_in, - input [31:0] fp2_in, - output sign1, - output sign2, - output [7:0] exp1, - output [7:0] exp2, - output [25:0] product, - output carry_out - ); +module MUL_step1 ( + input [31:0] fp1_in, + input [31:0] fp2_in, + output sign1, + output sign2, + output [ 7:0] exp1, + output [ 7:0] exp2, + output [25:0] product, + output carry_out +); - assign sign1 = fp1_in[31]; - assign sign2 = fp2_in[31]; - assign exp1 = fp1_in[30:23]; - assign exp2 = fp2_in[30:23]; + assign sign1 = fp1_in[31]; + assign sign2 = fp2_in[31]; + assign exp1 = fp1_in[30:23]; + assign exp2 = fp2_in[30:23]; - mul_26b MUL ( - .frac_in1({1'b1, fp1_in[22:0], 2'b00}), - .frac_in2({1'b1, fp2_in[22:0], 2'b00}), - .frac_out(product), - .overflow(carry_out) - ); + mul_26b MUL ( + .frac_in1({1'b1, fp1_in[22:0], 2'b00}), + .frac_in2({1'b1, fp2_in[22:0], 2'b00}), + .frac_out(product), + .overflow(carry_out) + ); + +endmodule // MUL_step1 -endmodule // MUL_step1 - diff --git a/source_code/Floating_Point_Unit/source/MUL_step2.sv b/source_code/Floating_Point_Unit/source/MUL_step2.sv index bf7167183..4d6eab8cf 100644 --- a/source_code/Floating_Point_Unit/source/MUL_step2.sv +++ b/source_code/Floating_Point_Unit/source/MUL_step2.sv @@ -11,32 +11,31 @@ //Outputs: // sign_out - result of xor operation // sum_exp - result of addition -// ovf - signal if an overflow has occurred +// ovf - signal if an overflow has occurred // unf - signal if an undeflow has occurred -module MUL_step2 - ( - input sign1, - input sign2, - input [7:0] exp1, - input [7:0] exp2, - output sign_out, - output [7:0] sum_exp, - output reg ovf, - output reg unf, - input carry - ); - +module MUL_step2 ( + input sign1, + input sign2, + input [7:0] exp1, + input [7:0] exp2, + output sign_out, + output [7:0] sum_exp, + output reg ovf, + output reg unf, + input carry +); - adder_8b add_EXPs( - .carry(carry), - .exp1(exp1), - .exp2(exp2), - .sum(sum_exp), - .ovf(ovf), - .unf(unf) - ); - - assign sign_out = sign1 ^ sign2; + + adder_8b add_EXPs ( + .carry(carry), + .exp1 (exp1), + .exp2 (exp2), + .sum (sum_exp), + .ovf (ovf), + .unf (unf) + ); + + assign sign_out = sign1 ^ sign2; endmodule diff --git a/source_code/Floating_Point_Unit/source/adder_26b.sv b/source_code/Floating_Point_Unit/source/adder_26b.sv index 57b00709c..22232f579 100644 --- a/source_code/Floating_Point_Unit/source/adder_26b.sv +++ b/source_code/Floating_Point_Unit/source/adder_26b.sv @@ -1,36 +1,36 @@ //By : Joe Nasti //Last Updated : 7.16.18 // -//Module Summary: +//Module Summary: // adds two signed 26 bit fraction values // //Inputs: // frac1/2 - signed 26 bit values with decimal point fixed after second bit //Outputs: // sum - output of sum operation regardless of overflow -// ovf - high if an overflow has occured - -module adder_26b( - input [26:0] frac1, - input [26:0] frac2, - output reg [26:0] sum, - output reg ovf +// ovf - high if an overflow has occured + +module adder_26b ( + input [26:0] frac1, + input [26:0] frac2, + output reg [26:0] sum, + output reg ovf ); -always_comb begin + always_comb begin + + sum = frac1 + frac2; + ovf = 0; + + if (frac1[26] == 1 && frac2[26] == 1 && sum[26] == 0) begin + ovf = 1; + sum[26] = 1; + end + + if (frac1[26] == 0 && frac2[26] == 0 && sum[26] == 1) begin + ovf = 1; + sum[26] = 0; + end - sum = frac1 + frac2; - ovf = 0; - - if(frac1[26] == 1 && frac2[26]== 1 && sum[26] == 0) begin - ovf = 1; - sum[26] = 1; - end - - if(frac1[26] == 0 && frac2[26]== 0 && sum[26] == 1) begin - ovf = 1; - sum[26] = 0; - end - -end + end endmodule diff --git a/source_code/Floating_Point_Unit/source/adder_8b.sv b/source_code/Floating_Point_Unit/source/adder_8b.sv index acfe38116..f6b0f9d4a 100644 --- a/source_code/Floating_Point_Unit/source/adder_8b.sv +++ b/source_code/Floating_Point_Unit/source/adder_8b.sv @@ -10,28 +10,28 @@ // ovf - signal overflow has occurred // unf - signal underflow has occurred -module adder_8b( - input carry, - input [7:0] exp1, - input [7:0] exp2, - output [7:0] sum, - output ovf, - output unf +module adder_8b ( + input carry, + input [7:0] exp1, + input [7:0] exp2, + output [7:0] sum, + output ovf, + output unf ); - reg [7:0] r_exp1; - reg [7:0] r_exp2; - reg [7:0] r_sum; - + reg [7:0] r_exp1; + reg [7:0] r_exp2; + reg [7:0] r_sum; - always_comb begin - r_exp1 = exp1 - 8'b10000000; - r_exp2 = exp2 - 8'b10000000; - r_sum = r_exp1 + r_exp2; - end - - assign sum = (exp1 + exp2) - 8'b10000000; // add with offset - assign ovf = r_sum[7] && ~r_exp1[7] && ~r_exp2[7]; - assign unf = ((carry != 1) || (sum != 8'b11111111)) && (~r_sum[7] && r_exp1[7] && r_exp2[7]); + + always_comb begin + r_exp1 = exp1 - 8'b10000000; + r_exp2 = exp2 - 8'b10000000; + r_sum = r_exp1 + r_exp2; + end + + assign sum = (exp1 + exp2) - 8'b10000000; // add with offset + assign ovf = r_sum[7] && ~r_exp1[7] && ~r_exp2[7]; + assign unf = ((carry != 1) || (sum != 8'b11111111)) && (~r_sum[7] && r_exp1[7] && r_exp2[7]); endmodule diff --git a/source_code/Floating_Point_Unit/source/int_compare.sv b/source_code/Floating_Point_Unit/source/int_compare.sv index 41e041c2d..2adbf1288 100644 --- a/source_code/Floating_Point_Unit/source/int_compare.sv +++ b/source_code/Floating_Point_Unit/source/int_compare.sv @@ -4,35 +4,35 @@ //Module Summary: // Compares magnitude of two unsigned 8 bit integers // -//Inputs: +//Inputs: // exp1/2 - 8 bit unsigned integers //Outputs: // u_diff - unsigned difference between exp1 and exp2 // cmp_out - exp1 < exp2 -> 1, exp1 >= exp2 -> 0 -module int_compare( - input [7:0] exp1, - input [7:0] exp2, - output [7:0] u_diff, - output reg cmp_out +module int_compare ( + input [7:0] exp1, + input [7:0] exp2, + output [7:0] u_diff, + output reg cmp_out ); -wire [8:0] u_exp1 = {1'b0, exp1}; -wire [8:0] u_exp2 = {1'b0, exp2}; -reg [8:0] diff; + wire [8:0] u_exp1 = {1'b0, exp1}; + wire [8:0] u_exp2 = {1'b0, exp2}; + reg [8:0] diff; -assign u_diff = diff[7:0]; + assign u_diff = diff[7:0]; -always_comb begin - diff = u_exp1 - u_exp2; - case(diff[8]) - 1'b0: cmp_out = 1'b0; - 1'b1: begin - cmp_out = 1'b1; - diff = -diff; - end - endcase -end + always_comb begin + diff = u_exp1 - u_exp2; + case (diff[8]) + 1'b0: cmp_out = 1'b0; + 1'b1: begin + cmp_out = 1'b1; + diff = -diff; + end + endcase + end endmodule diff --git a/source_code/Floating_Point_Unit/source/left_shift.sv b/source_code/Floating_Point_Unit/source/left_shift.sv index 8b6c0c2a0..e08cf47c2 100644 --- a/source_code/Floating_Point_Unit/source/left_shift.sv +++ b/source_code/Floating_Point_Unit/source/left_shift.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/16/2018 // -//Module Summary: +//Module Summary: // Left shifts an unsigned 26 bit value until the first '1' is the most significant bit and returns the amount shifted // //Inputs: @@ -9,41 +9,116 @@ //Outputs: // result - resulting 26 bit value with a '1' in most significance and zeros shifted in from the right -module left_shift( - input [25:0] fraction, - output reg [25:0] result, - output reg [7:0] shifted_amount +module left_shift ( + input [25:0] fraction, + output reg [25:0] result, + output reg [ 7:0] shifted_amount ); - always_comb begin - result = fraction; - shifted_amount = 0; - casez(fraction) - 26'b01????????????????????????: begin result = {fraction[24:0], 1'd0}; shifted_amount = 1; end - 26'b001???????????????????????: begin result = {fraction[23:0], 2'd0}; shifted_amount = 2; end - 26'b0001??????????????????????: begin result = {fraction[22:0], 3'd0}; shifted_amount = 3; end - 26'b00001?????????????????????: begin result = {fraction[21:0], 4'd0}; shifted_amount = 4; end - 26'b000001????????????????????: begin result = {fraction[20:0], 5'd0}; shifted_amount = 5; end - 26'b0000001???????????????????: begin result = {fraction[19:0], 6'd0}; shifted_amount = 6; end - 26'b00000001??????????????????: begin result = {fraction[18:0], 7'd0}; shifted_amount = 7; end - 26'b000000001?????????????????: begin result = {fraction[17:0], 8'd0}; shifted_amount = 8; end - 26'b0000000001????????????????: begin result = {fraction[16:0], 9'd0}; shifted_amount = 9; end - 26'b00000000001???????????????: begin result = {fraction[15:0],10'd0}; shifted_amount = 10; end - 26'b000000000001??????????????: begin result = {fraction[14:0],11'd0}; shifted_amount = 11; end - 26'b0000000000001?????????????: begin result = {fraction[13:0],12'd0}; shifted_amount = 12; end - 26'b00000000000001????????????: begin result = {fraction[12:0],13'd0}; shifted_amount = 13; end - 26'b000000000000001???????????: begin result = {fraction[11:0],14'd0}; shifted_amount = 14; end - 26'b0000000000000001??????????: begin result = {fraction[10:0],15'd0}; shifted_amount = 15; end - 26'b00000000000000001?????????: begin result = {fraction[9:0], 16'd0}; shifted_amount = 16; end - 26'b000000000000000001????????: begin result = {fraction[8:0], 17'd0}; shifted_amount = 17; end - 26'b0000000000000000001???????: begin result = {fraction[7:0], 18'd0}; shifted_amount = 18; end - 26'b00000000000000000001??????: begin result = {fraction[6:0], 19'd0}; shifted_amount = 19; end - 26'b000000000000000000001?????: begin result = {fraction[5:0], 20'd0}; shifted_amount = 20; end - 26'b0000000000000000000001????: begin result = {fraction[4:0], 21'd0}; shifted_amount = 21; end - 26'b00000000000000000000001???: begin result = {fraction[3:0], 22'd0}; shifted_amount = 22; end - 26'b000000000000000000000001??: begin result = {fraction[2:0], 23'd0}; shifted_amount = 23; end - 26'b0000000000000000000000001?: begin result = {fraction[1:0], 24'd0}; shifted_amount = 24; end - 26'b00000000000000000000000001: begin result = {fraction[0], 25'd0}; shifted_amount = 25; end - endcase - end + always_comb begin + result = fraction; + shifted_amount = 0; + casez (fraction) + 26'b01????????????????????????: begin + result = {fraction[24:0], 1'd0}; + shifted_amount = 1; + end + 26'b001???????????????????????: begin + result = {fraction[23:0], 2'd0}; + shifted_amount = 2; + end + 26'b0001??????????????????????: begin + result = {fraction[22:0], 3'd0}; + shifted_amount = 3; + end + 26'b00001?????????????????????: begin + result = {fraction[21:0], 4'd0}; + shifted_amount = 4; + end + 26'b000001????????????????????: begin + result = {fraction[20:0], 5'd0}; + shifted_amount = 5; + end + 26'b0000001???????????????????: begin + result = {fraction[19:0], 6'd0}; + shifted_amount = 6; + end + 26'b00000001??????????????????: begin + result = {fraction[18:0], 7'd0}; + shifted_amount = 7; + end + 26'b000000001?????????????????: begin + result = {fraction[17:0], 8'd0}; + shifted_amount = 8; + end + 26'b0000000001????????????????: begin + result = {fraction[16:0], 9'd0}; + shifted_amount = 9; + end + 26'b00000000001???????????????: begin + result = {fraction[15:0], 10'd0}; + shifted_amount = 10; + end + 26'b000000000001??????????????: begin + result = {fraction[14:0], 11'd0}; + shifted_amount = 11; + end + 26'b0000000000001?????????????: begin + result = {fraction[13:0], 12'd0}; + shifted_amount = 12; + end + 26'b00000000000001????????????: begin + result = {fraction[12:0], 13'd0}; + shifted_amount = 13; + end + 26'b000000000000001???????????: begin + result = {fraction[11:0], 14'd0}; + shifted_amount = 14; + end + 26'b0000000000000001??????????: begin + result = {fraction[10:0], 15'd0}; + shifted_amount = 15; + end + 26'b00000000000000001?????????: begin + result = {fraction[9:0], 16'd0}; + shifted_amount = 16; + end + 26'b000000000000000001????????: begin + result = {fraction[8:0], 17'd0}; + shifted_amount = 17; + end + 26'b0000000000000000001???????: begin + result = {fraction[7:0], 18'd0}; + shifted_amount = 18; + end + 26'b00000000000000000001??????: begin + result = {fraction[6:0], 19'd0}; + shifted_amount = 19; + end + 26'b000000000000000000001?????: begin + result = {fraction[5:0], 20'd0}; + shifted_amount = 20; + end + 26'b0000000000000000000001????: begin + result = {fraction[4:0], 21'd0}; + shifted_amount = 21; + end + 26'b00000000000000000000001???: begin + result = {fraction[3:0], 22'd0}; + shifted_amount = 22; + end + 26'b000000000000000000000001??: begin + result = {fraction[2:0], 23'd0}; + shifted_amount = 23; + end + 26'b0000000000000000000000001?: begin + result = {fraction[1:0], 24'd0}; + shifted_amount = 24; + end + 26'b00000000000000000000000001: begin + result = {fraction[0], 25'd0}; + shifted_amount = 25; + end + endcase + end endmodule diff --git a/source_code/Floating_Point_Unit/source/max_select.sv b/source_code/Floating_Point_Unit/source/max_select.sv index 804bf528c..e581d74e6 100644 --- a/source_code/Floating_Point_Unit/source/max_select.sv +++ b/source_code/Floating_Point_Unit/source/max_select.sv @@ -1,23 +1,21 @@ // not used -module max_select( - input [7:0] exp1, - input [7:0] exp2, - output reg [7:0] max +module max_select ( + input [7:0] exp1, + input [7:0] exp2, + output reg [7:0] max ); -reg [8:0] u_exp1; -reg [8:0] u_exp2; -reg [8:0] diff; + reg [8:0] u_exp1; + reg [8:0] u_exp2; + reg [8:0] diff; - assign u_exp1 = {1'b0, exp1}; - assign u_exp2 = {1'b0, exp2}; - assign diff = u_exp1 - u_exp2; + assign u_exp1 = {1'b0, exp1}; + assign u_exp2 = {1'b0, exp2}; + assign diff = u_exp1 - u_exp2; -always_comb begin - if(diff[8] == 0) - max = exp1; - else - max = exp2; -end + always_comb begin + if (diff[8] == 0) max = exp1; + else max = exp2; + end endmodule diff --git a/source_code/Floating_Point_Unit/source/mul_26b.sv b/source_code/Floating_Point_Unit/source/mul_26b.sv index fb3cb91ad..3af7128c3 100644 --- a/source_code/Floating_Point_Unit/source/mul_26b.sv +++ b/source_code/Floating_Point_Unit/source/mul_26b.sv @@ -6,25 +6,24 @@ // //Inputs: // frac_in1/2 - 26 bit fractions with decimal point after first bit -//Outputs: +//Outputs: // frac_out - 26 bit result of operation regardless of overflow occuring // overflow - flags if an overflow has occured -module mul_26b -( - input [25:0] frac_in1, - input [25:0] frac_in2, - output [25:0] frac_out, - output overflow - ); +module mul_26b ( + input [25:0] frac_in1, + input [25:0] frac_in2, + output [25:0] frac_out, + output overflow +); - reg [51:0] frac_out_52b; + reg [51:0] frac_out_52b; - assign overflow = frac_out_52b[51]; - assign frac_out = frac_out_52b[50:25]; + assign overflow = frac_out_52b[51]; + assign frac_out = frac_out_52b[50:25]; - always_comb begin : MULTIPLY - frac_out_52b = frac_in1 * frac_in2; - - end -endmodule + always_comb begin : MULTIPLY + frac_out_52b = frac_in1 * frac_in2; + + end +endmodule diff --git a/source_code/Floating_Point_Unit/source/right_shift.sv b/source_code/Floating_Point_Unit/source/right_shift.sv index 0ed6f3b3c..cf325d47f 100644 --- a/source_code/Floating_Point_Unit/source/right_shift.sv +++ b/source_code/Floating_Point_Unit/source/right_shift.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/18/18 // -//Module Summary: +//Module Summary: // right shifts a 26 bit value by a given amount // //Inputs: @@ -10,42 +10,42 @@ //Outputs: // result - 26 bit result of the shift -module right_shift( - input [25:0] fraction, - input [7:0] shift_amount, - output reg [25:0] result +module right_shift ( + input [25:0] fraction, + input [ 7:0] shift_amount, + output reg [25:0] result ); - always_comb begin - case(shift_amount) - 0 : result = fraction; - 1 : result = {1'd0, fraction[25:1]}; - 2 : result = {2'd0, fraction[25:2]}; - 3 : result = {3'd0, fraction[25:3]}; - 4 : result = {4'd0, fraction[25:4]}; - 5 : result = {5'd0, fraction[25:5]}; - 6 : result = {6'd0, fraction[25:6]}; - 7 : result = {7'd0, fraction[25:7]}; - 8 : result = {8'd0, fraction[25:8]}; - 9 : result = {9'd0, fraction[25:9]}; - 10: result = {10'd0, fraction[25:10]}; - 11: result = {11'd0, fraction[25:11]}; - 12: result = {12'd0, fraction[25:12]}; - 13: result = {13'd0, fraction[25:13]}; - 14: result = {14'd0, fraction[25:14]}; - 15: result = {15'd0, fraction[25:15]}; - 16: result = {16'd0, fraction[25:16]}; - 17: result = {17'd0, fraction[25:17]}; - 18: result = {18'd0, fraction[25:18]}; - 19: result = {19'd0, fraction[25:19]}; - 20: result = {20'd0, fraction[25:20]}; - 21: result = {21'd0, fraction[25:21]}; - 22: result = {22'd0, fraction[25:22]}; - 23: result = {23'd0, fraction[25:23]}; - 24: result = {24'd0, fraction[25:24]}; - 25: result = {25'd0, fraction[25]}; - default: result = 26'd0; - endcase - end + always_comb begin + case (shift_amount) + 0: result = fraction; + 1: result = {1'd0, fraction[25:1]}; + 2: result = {2'd0, fraction[25:2]}; + 3: result = {3'd0, fraction[25:3]}; + 4: result = {4'd0, fraction[25:4]}; + 5: result = {5'd0, fraction[25:5]}; + 6: result = {6'd0, fraction[25:6]}; + 7: result = {7'd0, fraction[25:7]}; + 8: result = {8'd0, fraction[25:8]}; + 9: result = {9'd0, fraction[25:9]}; + 10: result = {10'd0, fraction[25:10]}; + 11: result = {11'd0, fraction[25:11]}; + 12: result = {12'd0, fraction[25:12]}; + 13: result = {13'd0, fraction[25:13]}; + 14: result = {14'd0, fraction[25:14]}; + 15: result = {15'd0, fraction[25:15]}; + 16: result = {16'd0, fraction[25:16]}; + 17: result = {17'd0, fraction[25:17]}; + 18: result = {18'd0, fraction[25:18]}; + 19: result = {19'd0, fraction[25:19]}; + 20: result = {20'd0, fraction[25:20]}; + 21: result = {21'd0, fraction[25:21]}; + 22: result = {22'd0, fraction[25:22]}; + 23: result = {23'd0, fraction[25:23]}; + 24: result = {24'd0, fraction[25:24]}; + 25: result = {25'd0, fraction[25]}; + default: result = 26'd0; + endcase + end endmodule diff --git a/source_code/Floating_Point_Unit/source/rounder.sv b/source_code/Floating_Point_Unit/source/rounder.sv index f8fc6095c..82c1caf62 100644 --- a/source_code/Floating_Point_Unit/source/rounder.sv +++ b/source_code/Floating_Point_Unit/source/rounder.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/18/18 // -//Module Summary: +//Module Summary: // rounds floating point after the operation according to the frm (rounding mode) // //Inputs: @@ -12,50 +12,42 @@ //Ouputs: // rount_out - resulting floating point after rounding operation -module rounder( - input [2:0] frm, - input sign, - input [7:0] exp_in, - input [24:0] fraction, - output reg [31:0] round_out, - output rounded - ); - reg round_amount; +module rounder ( + input [ 2:0] frm, + input sign, + input [ 7:0] exp_in, + input [24:0] fraction, + output reg [31:0] round_out, + output rounded +); + reg round_amount; - localparam RNE = 3'b000; - localparam RZE = 3'b001; - localparam RDN = 3'b010; - localparam RUP = 3'b011; - localparam RMM = 3'b100; - - always_comb begin - round_amount = 0; - if(fraction[24:2] != '1) begin - if(frm == RNE) begin - if(fraction[1:0] == 2'b11) - round_amount = 1; - end - else if(frm == RZE) begin - round_amount = 0; - end - else if(frm == RDN) begin - if(sign == 1 && ((fraction[0] == 1) || (fraction[1] == 1))) - round_amount = 1; - end - else if(frm == RUP) begin - if(sign == 0 && ((fraction[0] == 1) || (fraction[1] == 1))) - round_amount = 1; - end - else if(frm == RMM) begin - if(fraction[1] == 1) - round_amount = 1; - end - end // if (fraction[24:2] != '1) - end // always_comb + localparam RNE = 3'b000; + localparam RZE = 3'b001; + localparam RDN = 3'b010; + localparam RUP = 3'b011; + localparam RMM = 3'b100; + + always_comb begin + round_amount = 0; + if (fraction[24:2] != '1) begin + if (frm == RNE) begin + if (fraction[1:0] == 2'b11) round_amount = 1; + end else if (frm == RZE) begin + round_amount = 0; + end else if (frm == RDN) begin + if (sign == 1 && ((fraction[0] == 1) || (fraction[1] == 1))) round_amount = 1; + end else if (frm == RUP) begin + if (sign == 0 && ((fraction[0] == 1) || (fraction[1] == 1))) round_amount = 1; + end else if (frm == RMM) begin + if (fraction[1] == 1) round_amount = 1; + end + end // if (fraction[24:2] != '1) + end // always_comb + + assign rounded = round_amount; + assign round_out = {sign, exp_in, fraction[24:2] + round_amount}; - assign rounded = round_amount; - assign round_out = {sign, exp_in, fraction[24:2] + round_amount}; - endmodule - - + + diff --git a/source_code/Floating_Point_Unit/source/s_to_u.sv b/source_code/Floating_Point_Unit/source/s_to_u.sv index 8725b9536..cbd64c1df 100644 --- a/source_code/Floating_Point_Unit/source/s_to_u.sv +++ b/source_code/Floating_Point_Unit/source/s_to_u.sv @@ -6,26 +6,26 @@ // //Inputs: // frac_signed - 27 bit signed value -//Outputs: +//Outputs: // sign - 1 bit sign 'frac_unsigned' // frac_unsigned - 26 bit magnitude of 'frac_signed' -module s_to_u( - input [26:0] frac_signed, - output reg sign, - output [25:0] frac_unsigned +module s_to_u ( + input [26:0] frac_signed, + output reg sign, + output [25:0] frac_unsigned ); -reg [26:0] rfrac_signed; + reg [26:0] rfrac_signed; -assign frac_unsigned = rfrac_signed[25:0]; - -always_comb begin + assign frac_unsigned = rfrac_signed[25:0]; + + always_comb begin sign = 0; - rfrac_signed = frac_signed; - if(frac_signed[26] == 1) begin - rfrac_signed = -frac_signed; - sign = 1; - end -end + rfrac_signed = frac_signed; + if (frac_signed[26] == 1) begin + rfrac_signed = -frac_signed; + sign = 1; + end + end endmodule diff --git a/source_code/Floating_Point_Unit/source/subtract.sv b/source_code/Floating_Point_Unit/source/subtract.sv index 27a783ffa..00da21ce7 100644 --- a/source_code/Floating_Point_Unit/source/subtract.sv +++ b/source_code/Floating_Point_Unit/source/subtract.sv @@ -1,21 +1,20 @@ // subtracts shifted amount from the exponent -module subtract -( - input [7:0] exp1, - input [7:0] shifted_amount, - output [7:0] result +module subtract ( + input [7:0] exp1, + input [7:0] shifted_amount, + output [7:0] result ); - reg [8:0] u_exp1 = {1'b0, exp1}; - reg [8:0] u_shifted_amount = {1'b0,shifted_amount}; - reg [8:0] u_result; - -always_comb begin - u_exp1 = {1'b0, exp1}; - u_shifted_amount = {1'b0,shifted_amount}; - u_result = u_exp1 - u_shifted_amount; -end + reg [8:0] u_exp1 = {1'b0, exp1}; + reg [8:0] u_shifted_amount = {1'b0, shifted_amount}; + reg [8:0] u_result; - assign result = u_result[7:0]; -endmodule // subtract + always_comb begin + u_exp1 = {1'b0, exp1}; + u_shifted_amount = {1'b0, shifted_amount}; + u_result = u_exp1 - u_shifted_amount; + end + + assign result = u_result[7:0]; +endmodule // subtract diff --git a/source_code/Floating_Point_Unit/source/u_to_s.sv b/source_code/Floating_Point_Unit/source/u_to_s.sv index b3a851920..e0a1c6abc 100644 --- a/source_code/Floating_Point_Unit/source/u_to_s.sv +++ b/source_code/Floating_Point_Unit/source/u_to_s.sv @@ -1,7 +1,7 @@ //By : Joe Nasti //Last Updated : 7/21/18 // -//Module Summary: +//Module Summary: // Converts a one bit sign and 26 bit magnitude to a 27 bit signed value // //Inputs: @@ -10,16 +10,16 @@ //Outputs: // frac_signed - 27 bit signed result of conversion -module u_to_s( - input sign, - input [25:0] frac_unsigned, - output reg [26:0] frac_signed +module u_to_s ( + input sign, + input [25:0] frac_unsigned, + output reg [26:0] frac_signed ); -always_comb begin - frac_signed = {1'b0, frac_unsigned}; - if(sign == 1) begin - frac_signed = -frac_signed; - end -end + always_comb begin + frac_signed = {1'b0, frac_unsigned}; + if (sign == 1) begin + frac_signed = -frac_signed; + end + end endmodule diff --git a/source_code/Half_Precision_FPU/Half_Precision_Specs.pdf b/source_code/Half_Precision_FPU/Half_Precision_Specs.pdf new file mode 100644 index 000000000..c73f0c95a Binary files /dev/null and b/source_code/Half_Precision_FPU/Half_Precision_Specs.pdf differ diff --git a/source_code/Half_Precision_FPU/README.md b/source_code/Half_Precision_FPU/README.md new file mode 100644 index 000000000..bae3740be --- /dev/null +++ b/source_code/Half_Precision_FPU/README.md @@ -0,0 +1,20 @@ +# SoCET_Half_Precision_FPU +SystemVerilog implementation of half precision floating point unit + +Multiplication: + - Functional for most numbers I've checked by hand (Including infinities, qNans/sNans, and sub-normals). + - Current testing is plugging numbers in by hand so have only tested ~10 of each type + - Khoi is writing testbench so that we can finalize module. + +Addition: N/A + +Subtraction: N/A + +Rounding: N/A + +TODO: + - RISCV Business repository + - Make a folder in there and put this code there + - Allow the user to choose the rounding mode + - Add inputs which accrues flags + - Things like divide by zero, or other exceptions diff --git a/source_code/Half_Precision_FPU/RISCV-User-Manual.pdf b/source_code/Half_Precision_FPU/RISCV-User-Manual.pdf new file mode 100644 index 000000000..e4a463483 Binary files /dev/null and b/source_code/Half_Precision_FPU/RISCV-User-Manual.pdf differ diff --git a/source_code/Half_Precision_FPU/addition/float_add_16bit.sv b/source_code/Half_Precision_FPU/addition/float_add_16bit.sv new file mode 100644 index 000000000..b5f712a8b --- /dev/null +++ b/source_code/Half_Precision_FPU/addition/float_add_16bit.sv @@ -0,0 +1,198 @@ +module top ( + // I/O ports + input logic hz100, reset, + input logic [20:0] pb, + output logic [7:0] left, right, + ss7, ss6, ss5, ss4, ss3, ss2, ss1, ss0, + output logic red, green, blue, + + // UART ports + output logic [7:0] txdata, + input logic [7:0] rxdata, + output logic txclk, rxclk, + input logic txready, rxready +); + reg[15:0] float1, float2, expected_sum, sum; + assign float2 = {1'b0, 5'b10101, 10'b0011001100}; + assign float1 = {1'b0, 5'b10101, 10'b0011100011}; + assign expected_sum = {1'b0, 5'b11011, 10'b0111011100}; + + half_FP_add add1(.clk(hz100), .rst(1'b0), .float1(float1), .float2(float2), .sum_out(sum)); + + assign blue = (sum == expected_sum) ? 1'b1 : 1'b0; + assign left = sum[15:8]; + assign right = sum[7:0]; + + +endmodule + +// Add more modules down here... + +module half_FP_add ( + input clk, rst, + input [15:0] float1, float2, + output[15:0] sum_out +); + logic sign1, sign2, sign_s, hidden_bit1, hidden_bit2; + logic [4:0] exp1, exp2, exp_s; + logic [9:0] mant1, mant2, mant_s; + logic [13:0] sum; + logic guard, round_bit, sticky; + + typedef enum logic [2:0] { + initializing, + special_case, + align, + add, + normalise_1, + normalise_2, + round, + assemble + } state_t; + + state_t state; + state_t next_state; + + always_comb begin + case(state) + initializing: begin + sign1 = float1[15]; + sign2 = float2[15]; + + exp1 = float1[14:10]; + exp2 = float2[14:10]; + + mant1 = float1[9:0]; + mant2 = float2[9:0]; + hidden_bit1 = 1'b1; + hidden_bit2 = 1'b1; + + next_state = special_case; + end + + special_case: begin + next_state = align; + end + + align: begin + if(exp1 > exp2) begin + exp2 = exp2 + 1; + mant2 = mant2 >> 1; + end + else if(exp2 > exp1) begin + exp1 = exp1 + 1; + mant1 = mant1 >> 1; + end + else begin + next_state = add; + end + end + + add: begin + exp_s = exp1; + if(sign1 == sign2) begin + sum = {4'b0000, mant1 + mant2}; + sign_s = sign1; + end + else begin + if(mant1 >= mant2) begin + sum = {4'b0000, mant1 - mant2}; + sign_s = sign1; + end + else begin + sum = {4'b0000, mant2 - mant1}; + sign_s = sign2; + end + end + + if(sum[9]) begin + mant_s = sum[13:4]; + guard = sum[3]; + round_bit = sum[2]; + sticky = sum[1] | sum[0]; + exp_s = exp_s + 1; + end + else begin + mant_s = sum[12:3]; + guard = sum[2]; + round_bit = sum[1]; + sticky = sum[0]; + end + + next_state = normalise_1; + end + + normalise_1: begin + if(mant_s[9] == 0 && exp_s > 0) begin + exp_s = exp_s - 1; + mant_s = mant_s << 1; + mant_s[0] = guard; + guard = round_bit; + round_bit = 0; + end + else begin + next_state = normalise_2; + end + end + + normalise_2: begin + if(exp_s == 0) begin + exp_s = exp_s + 1; + mant_s = mant_s >> 1; + guard = mant_s[0]; + round_bit = guard; + sticky = sticky | round_bit; + end + else begin + state = round; + end + end + + round: begin + if(guard && (round_bit | sticky | mant_s[0])) begin + if(mant_s == 10'b1111111111) begin + exp_s = exp_s + 1; + end + end + next_state = assemble; + end + + assemble: begin + sum_out[15] = sign_s; + sum_out[14:10] = exp_s; + sum_out[9:0] = mant_s; + end + + default: begin + sign_s = 0; + sign1 = 0; + sign2 = 0; + + exp_s = 0; + exp1 = 0; + exp2 = 0; + + hidden_bit1 = 0; + hidden_bit2 = 0; + + mant_s = 0; + mant1 = 0; + mant2 = 0; + + guard = 0; + round_bit = 0; + sticky = 0; + sum = 0; + + next_state = state; + end + endcase + end + + always_ff @(posedge clk, posedge rst) begin + if(rst == 1'b1) begin + state <= initializing; + end + else state <= next_state; + end +endmodule diff --git a/source_code/Half_Precision_FPU/multiplication/float_mult_16bit.sv b/source_code/Half_Precision_FPU/multiplication/float_mult_16bit.sv new file mode 100644 index 000000000..15658e478 --- /dev/null +++ b/source_code/Half_Precision_FPU/multiplication/float_mult_16bit.sv @@ -0,0 +1,206 @@ +module float_mult_16bit ( + input logic clk, n_rst, + input logic [15:0] float1, + input logic [15:0] float2, + output logic [15:0] product +); + + logic sign1, sign2, sign_p; + logic hidden_lead1, hidden_lead2; + + logic signed_exponent; + logic [4:0] exp1, exp2; + logic [4:0] exp_p; + logic signed [4:0] exp_p_signed; + + logic [5:0] exp_comb; + logic [9:0] mant1, mant2, mant_p; + + logic [21:0] mant_multiplied; + logic guard, round_bit, sticky, dummy; + + typedef enum logic [2:0] { + initializing, + special_case, + multiply, + normalize_exp, + normalize_mant, + assemble + } state_t; + + state_t state; + state_t next_state; + + always_comb begin + case(state) + // Currently goes + // initializing -> + // special_case -> + // multiplying -> + // normalize_exp -> + // normalize_mant -> + // assemble + initializing: begin + sign1 = float1[15]; + sign2 = float2[15]; + + exp1 = float1[14:10]; + exp2 = float2[14:10]; + + hidden_lead1 = 1'b1; + hidden_lead2 = 1'b1; + + mant1 = float1[9:0]; + mant2 = float2[9:0]; + product = 'd0; + + next_state = special_case; + end + + special_case: begin + // if either number is infinity, output is infinity + if((exp1 == 5'b11111 & mant1 == 0) | (exp2 == 5'b11111 & mant2 == 0)) begin + sign_p = sign1 ^ sign2; // Accounts for +/- infinity + exp_p = 5'b11111; + mant_p = 10'b0000000000; + next_state = assemble; + end + + // if either number is quiet NaN, return quiet Nan + else if((exp1 == 5'd31 & mant1[9] == 1'b0) | (exp2 == 5'd31 & mant2[9] == 1'b0)) begin + sign_p = 1'b1; + exp_p = 5'b11111; + mant_p = 10'b0111111111; + next_state = assemble; + end + + // if either number is signalling NaN, return signalling Nan + else if((exp1 == 5'd31 & mant1[9] == 1'b1) | (exp2 == 5'd31 & mant2[9] == 1'b1)) begin + sign_p = 1'b1; + exp_p = 5'b11111; + mant_p = 10'b1111111111; + next_state = assemble; + end + + // if either number is zero, return zero + else if((exp1 == 0 & mant1 == 0) | (exp2 == 0 & mant2 == 0)) begin + sign_p = sign1 ^ sign2; // IEE754 mandates signed zero + exp_p = 5'b00000; + mant_p = 10'b0000000000; + next_state = assemble; + end + + else begin + if(exp1 == 0) begin + hidden_lead1 = 1'b0; + end + if(exp2 == 0) begin + hidden_lead2 = 1'b0; + end + next_state = multiply; + end + end + + multiply: begin + sign_p = sign1 ^ sign2; + + if((exp1 + exp2) < 15) begin + exp_p = 0; + exp_p_signed = exp1 + exp2 - 5'd15; + signed_exponent = 1'b1; + end + else begin + exp_p = exp1 + exp2 - 5'd15; + exp_p_signed = 0; + signed_exponent = 1'b0; + end + + mant_multiplied = {hidden_lead1, mant1} * {hidden_lead2, mant2}; + + if(signed_exponent) begin + next_state = normalize_exp; + mant_multiplied = mant_multiplied >> 1; + end + else begin + next_state = normalize_mant; + end + end + + normalize_exp: begin + if(exp_p_signed < 0) begin + exp_p_signed = exp_p_signed + 1; + mant_multiplied = mant_multiplied >> 1; + end + else begin + next_state = normalize_mant; + end + end + + normalize_mant: begin + if(mant_multiplied[21] == 1'b1) begin + exp_p = exp_p + 1; + mant_multiplied = mant_multiplied >> 1; + end + + mant_p = mant_multiplied[19:10]; + + guard = mant_multiplied[9]; + round_bit = mant_multiplied[8]; + + if(guard) begin + round_bit = guard; + guard = mant_p[0]; + mant_p = mant_p + 1; + end + + next_state = assemble; + end + + assemble: begin + product[15] = sign_p; + product[14:10] = signed_exponent ? exp_p_signed : exp_p; + product[9:0] = mant_p; + + // if (float1 || float2) next_state = initializing; + end + + default: begin + sign_p = 0; + sign1 = 0; + sign2 = 0; + + exp_p = 0; + exp_p_signed = 0; + exp1 = 0; + exp2 = 0; + + hidden_lead1 = 0; + hidden_lead2 = 0; + + mant_p = 0; + mant1 = 0; + mant2 = 0; + + guard = 0; + round_bit = 0; + sticky = 0; + mant_multiplied = 0; + + next_state = state; + end + endcase + end + + // always_comb begin + // product[15] = sign_p; + // product[14:10] = signed_exponent ? exp_p_signed : exp_p; + // product[9:0] = mant_p; + // end + + always_ff @(posedge clk, negedge n_rst) begin + if(!n_rst) begin + state <= initializing; + end + else state <= next_state; + end +endmodule diff --git a/source_code/RV32E/rv32e_reg_file.sv b/source_code/RV32E/rv32e_reg_file.sv new file mode 100644 index 000000000..9302f11fc --- /dev/null +++ b/source_code/RV32E/rv32e_reg_file.sv @@ -0,0 +1,65 @@ +/* +* Copyright 2021 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32e_reg_file.sv +* +* Created by: Jiahao Xu +* Email: xu1392@purdue.edu +* Date Created: 2/26/2021 +* Description: Implenmentation of RV32E register file. +*/ +`include "rv32i_reg_file_if.vh" + +module rv32e_reg_file ( + input CLK, + nRST, + rv32i_reg_file_if rf_if +); + import rv32i_types_pkg::*; + localparam int NUM_REGS = 16; + + // input: + // rf_if.wen: write enable. + // rf_if.w_data [31:0]: the word to write. + // rf_if.rd [4:0]: the register that need to write. + // rf_if.rs1 [4:0]: the address of read reg 1. + // rf_if.rs2 [4:0]: the address of read reg 2. + // output + // rf_if.rs1_data [31:0]: the read data of rs1 + // rf_if.rs2_data [31:0]: the read data of rs2 + + + word_t [NUM_REGS-1 : 0] registers; + + always_ff @(posedge CLK, negedge nRST) begin : RV32E_REG_FILE_FF + if (~nRST) begin + registers <= '0; + end // check if the rd is valid. + else if (rf_if.wen && (|rf_if.rd) && ~rf_if.rd[4]) begin + // if (WEN && rd!=0 && rd<16) write. + registers[rf_if.rd] <= rf_if.w_data; + end else begin + // else keep the current register file unchanged. + registers <= registers; + end + end + // dispose the MSB of the select line since this register file + // has only 16 registers. + // the select line should be in [0,15] + // if invalid rs, return 0. + assign rf_if.rs1_data = rf_if.rs1[4] ? 0 : registers[rf_if.rs1]; + assign rf_if.rs2_data = rf_if.rs2[4] ? 0 : registers[rf_if.rs2]; +endmodule diff --git a/source_code/RV32E/rv32e_wrapper.sv b/source_code/RV32E/rv32e_wrapper.sv new file mode 100644 index 000000000..7247b839f --- /dev/null +++ b/source_code/RV32E/rv32e_wrapper.sv @@ -0,0 +1,16 @@ +`include "component_selection_defines.vh" +`include "rv32i_reg_file_if.vh" +module rv32e_wrapper ( + input logic CLK, + nRST, + rv32i_reg_file_if rf_if +); + generate + case (BASE_ISA) + "RV32I": rv32i_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); + "RV32E": rv32e_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); + default: + rv32i_reg_file rf (.CLK(CLK), .nRST(nRST), .rf_if(rf_if)); + endcase + endgenerate +endmodule diff --git a/source_code/branch_predictors/branch_predictor_wrapper.sv b/source_code/branch_predictors/branch_predictor_wrapper.sv index e65750d70..e3f0cac61 100644 --- a/source_code/branch_predictors/branch_predictor_wrapper.sv +++ b/source_code/branch_predictors/branch_predictor_wrapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,14 +27,15 @@ `include "component_selection_defines.vh" module branch_predictor_wrapper ( - input logic CLK, nRST, - predictor_pipeline_if.predictor predict_if + input logic CLK, + nRST, + predictor_pipeline_if.predictor predict_if ); - // Predictor used based on the BR_PREDICTOR_TYPE definition - generate - case (BR_PREDICTOR_TYPE) - "not_taken" : nottaken_predictor predictor(.*); - endcase - endgenerate + // Predictor used based on the BR_PREDICTOR_TYPE definition + generate + case (BR_PREDICTOR_TYPE) + "not_taken": nottaken_predictor predictor (.*); + endcase + endgenerate endmodule diff --git a/source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv b/source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv index 0bc5b4932..753628b90 100644 --- a/source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv +++ b/source_code/branch_predictors/nottaken_predictor/nottaken_predictor.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,12 +24,13 @@ `include "predictor_pipeline_if.vh" -module nottaken_predictor( - input logic CLK, nRST, - predictor_pipeline_if.predictor predict_if +module nottaken_predictor ( + input logic CLK, + nRST, + predictor_pipeline_if.predictor predict_if ); - assign predict_if.predict_taken = 0; - assign predict_if.target_addr = predict_if.current_pc + 4; + assign predict_if.predict_taken = 0; + assign predict_if.target_addr = predict_if.current_pc + 4; endmodule diff --git a/source_code/branch_predictors/wscript b/source_code/branch_predictors/wscript deleted file mode 100644 index 82c98b932..000000000 --- a/source_code/branch_predictors/wscript +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(nottaken_predictor) - -def sim_source(cnf): - cnf.recurse(nottaken_predictor) diff --git a/source_code/bus_bridges/ahb.sv b/source_code/bus_bridges/ahb.sv index 86009e4be..602fc7018 100644 --- a/source_code/bus_bridges/ahb.sv +++ b/source_code/bus_bridges/ahb.sv @@ -1,100 +1,95 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* -* Filename: ahb_master.sv -* +* +* +* Filename: ahb_master.sv +* * Created by: Chuan Yean Tan * Email: tan56@purdue.edu * Date Created: 08/31/2016 -* Description: Processes read & write request into AHB-Lite protocol -* TODO: 1. HRESP -> has to be added to the state transitions +* Description: Processes read & write request into AHB-Lite protocol +* TODO: 1. HRESP -> has to be added to the state transitions */ `include "generic_bus_if.vh" -`include "ahb_if.vh" module ahb ( - input CLK, nRST, - ahb_if.ahb_m ahb_m, - generic_bus_if.generic_bus out_gen_bus_if + input CLK, + nRST, + generic_bus_if.generic_bus out_gen_bus_if, + ahb_if.manager ahb_m ); - typedef enum logic { - IDLE, - DATA - } state_t; + typedef enum logic { + IDLE, + DATA + } state_t; - state_t state, n_state; + state_t state, n_state; - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) - state <= IDLE; - else - state <= n_state; - end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) state <= IDLE; + else state <= n_state; + end - assign n_state = (out_gen_bus_if.ren | out_gen_bus_if.wen) ? DATA : IDLE; + always_comb begin + if ((state == DATA) & !(ahb_m.HREADY)) n_state = state; + else n_state = out_gen_bus_if.ren | out_gen_bus_if.wen ? DATA : IDLE; + end - always_comb begin - if(out_gen_bus_if.byte_en == 4'b1111) - ahb_m.HSIZE = 3'b010; // word - else if(out_gen_bus_if.byte_en == 4'b1100 || out_gen_bus_if.byte_en == 4'b0011) - ahb_m.HSIZE = 3'b001; // half word - else - ahb_m.HSIZE = 3'b000; // byte - end + always_comb begin + if (out_gen_bus_if.byte_en == 4'b1111) ahb_m.HSIZE = 3'b010; // word + else if (out_gen_bus_if.byte_en == 4'b1100 || out_gen_bus_if.byte_en == 4'b0011) + ahb_m.HSIZE = 3'b001; // half word + else ahb_m.HSIZE = 3'b000; // byte + end - always_comb - begin - //-- Read Request --// - if ( out_gen_bus_if.ren ) - begin - ahb_m.HTRANS = 2'b10; - ahb_m.HWRITE = 1'b0; - ahb_m.HADDR = out_gen_bus_if.addr; - ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HPROT = 0; - ahb_m.HMASTLOCK = 0; - end - //-- Write Request --// - else if ( out_gen_bus_if.wen ) - begin - ahb_m.HTRANS = 2'b10; - ahb_m.HWRITE = 1'b1; - ahb_m.HADDR = out_gen_bus_if.addr; - ahb_m.HWDATA = out_gen_bus_if.wdata; - ahb_m.HBURST = 0; - ahb_m.HPROT = 0; - ahb_m.HMASTLOCK = 0; + always_comb begin + if (out_gen_bus_if.ren) begin + ahb_m.HTRANS = 2'b10; + ahb_m.HWRITE = 1'b0; + ahb_m.HADDR = {out_gen_bus_if.addr[31:2], 2'b00}; + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HWSTRB = out_gen_bus_if.byte_en; + end else if (out_gen_bus_if.wen) begin + ahb_m.HTRANS = 2'b10; + ahb_m.HWRITE = 1'b1; + ahb_m.HADDR = {out_gen_bus_if.addr[31:2], 2'b00}; + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HWSTRB = out_gen_bus_if.byte_en; + end else begin + ahb_m.HTRANS = 2'b0; + ahb_m.HWRITE = 1'b0; + ahb_m.HADDR = 0; + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HWSTRB = out_gen_bus_if.byte_en; + end + + if (state == DATA) begin + ahb_m.HWDATA = out_gen_bus_if.wdata; + ahb_m.HWSTRB = out_gen_bus_if.byte_en; + end end - //-- Default : Not reading / writing --// - else - begin - ahb_m.HTRANS = 2'b00; - ahb_m.HWRITE = 1'b0; - ahb_m.HADDR = 0; - ahb_m.HWDATA = 0; - ahb_m.HBURST = 0; - ahb_m.HPROT = 0; - ahb_m.HMASTLOCK = 0; - end - end - assign out_gen_bus_if.busy = ~(ahb_m.HREADY & (state == DATA)); - assign out_gen_bus_if.rdata = ahb_m.HRDATA; + + assign out_gen_bus_if.busy = state == IDLE || ~((ahb_m.HREADY && (state == DATA))); + assign out_gen_bus_if.rdata = ahb_m.HRDATA; + assign out_gen_bus_if.error = ahb_m.HRESP; + // Unused signals + assign ahb_m.HMASTLOCK = 1'b0; + assign ahb_m.HBURST = 3'b000; + assign ahb_m.HSEL = (ahb_m.HTRANS != 2'b00); endmodule diff --git a/source_code/bus_bridges/apb.sv b/source_code/bus_bridges/apb.sv new file mode 100644 index 000000000..63ee7f9f0 --- /dev/null +++ b/source_code/bus_bridges/apb.sv @@ -0,0 +1,103 @@ + +module apb ( + input CLK, nRST, + apb_if.requester apbif, + generic_bus_if.generic_bus out_gen_bus_if +); + + typedef enum logic [1:0] { + IDLE, + REQUEST, + DATA + } state_t; + + typedef struct packed { + logic [31:0] addr; + logic [31:0] wdata; + logic wen; // ren unneeded since only used in data phase, !wen -> ren + logic [3:0] strobe; + } request_t; + + state_t state, n_state; + request_t request, request_n; + + always_ff @(posedge CLK, negedge nRST) begin + if(!nRST) begin + state <= IDLE; + request <= '0; + end else begin + state <= n_state; + request <= request_n; + end + end + + always_comb begin + request_n = request; + if(state == IDLE || (state == DATA && apbif.PREADY)) begin + request_n.addr = {out_gen_bus_if.addr[31:2], 2'b00}; + request_n.wdata = out_gen_bus_if.wdata; + request_n.wen = out_gen_bus_if.wen; + request_n.strobe = out_gen_bus_if.byte_en; + end + end + + + // TODO: How does APB work with the memory controller? + always_comb begin + n_state = state; + if(state == IDLE && (out_gen_bus_if.ren || out_gen_bus_if.wen)) begin + n_state = REQUEST; + end else if(state == REQUEST) begin + n_state = DATA; + end else if(state == DATA && !apbif.PREADY) begin + n_state = DATA; + end else if(state == DATA && apbif.PREADY && (out_gen_bus_if.ren || out_gen_bus_if.wen)) begin + n_state = REQUEST; + end else if(state == DATA && apbif.PREADY && !(out_gen_bus_if.ren || out_gen_bus_if.wen)) begin + n_state = IDLE; + end + + /* + if(state == DATA && !apbif.PREADY) begin + n_state = state; + end else if(state == DATA && apbif.PREADY && (out_gen_bus_if.ren || out_gen_bus_if.wen)) begin + n_state = REQUEST; + end else if(state == REQUEST) begin + n_state = DATA; + end else begin + n_state = IDLE; + end + */ + end + + always_comb begin + if(state == IDLE) begin + apbif.PADDR = '0; + apbif.PSEL = '0; + apbif.PPROT = '0; + apbif.PENABLE = '0; + apbif.PWRITE = '0; + apbif.PSTRB = '0; + end else if(state == REQUEST) begin + apbif.PADDR = request.addr; + apbif.PSEL = 1'b1; + apbif.PPROT = '0; + apbif.PENABLE = 1'b0; + apbif.PWRITE = request.wen; + apbif.PSTRB = request.strobe; + end else begin + apbif.PADDR = request.addr; + apbif.PSEL = 1'b1; + apbif.PPROT = '0; + apbif.PENABLE = 1'b1; + apbif.PWRITE = request.wen; + apbif.PSTRB = request.strobe; + end + end + + // Response + assign out_gen_bus_if.rdata = apbif.PRDATA; + assign out_gen_bus_if.busy = (state == IDLE) || (state == REQUEST) || ~apbif.PREADY; + assign apbif.PWDATA = out_gen_bus_if.wdata; + +endmodule diff --git a/source_code/bus_bridges/generic_nonpipeline.sv b/source_code/bus_bridges/generic_nonpipeline.sv index b5e20fab1..49042a1b6 100644 --- a/source_code/bus_bridges/generic_nonpipeline.sv +++ b/source_code/bus_bridges/generic_nonpipeline.sv @@ -1,64 +1,61 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: generic_nonpipeline.sv -* +* * Created by: Chuan Yean Tan * Email: tan56@purdue.edu * Date Created: 09/14/2016 -* Description: Translates pipeline bus to a non-pipeline bus +* Description: Translates pipeline bus to a non-pipeline bus */ `include "generic_bus_if.vh" module generic_nonpipeline ( - input logic CLK, nRST, - generic_bus_if pipeline_trans_if, - generic_bus_if.cpu out_gen_bus_if + input logic CLK, + nRST, + generic_bus_if pipeline_trans_if, + generic_bus_if.cpu out_gen_bus_if ); - logic [31:0] next_wdata; - logic next_busy; + logic [31:0] next_wdata; + logic next_busy; - always_ff @ ( posedge CLK, negedge nRST ) - begin - if(!nRST) - begin - out_gen_bus_if.wen <= pipeline_trans_if.wen; - out_gen_bus_if.ren <= pipeline_trans_if.ren; - out_gen_bus_if.addr <= pipeline_trans_if.addr; - out_gen_bus_if.byte_en <= pipeline_trans_if.byte_en; - out_gen_bus_if.wdata <= next_wdata; - end - else - begin - if ((out_gen_bus_if.busy == 0) || (~out_gen_bus_if.ren && ~out_gen_bus_if.wen)) - begin - out_gen_bus_if.wen <= pipeline_trans_if.wen; - out_gen_bus_if.ren <= pipeline_trans_if.ren; - out_gen_bus_if.addr <= pipeline_trans_if.addr; - out_gen_bus_if.byte_en <= pipeline_trans_if.byte_en; - out_gen_bus_if.wdata <= next_wdata; - end - end - end + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) begin + out_gen_bus_if.wen <= pipeline_trans_if.wen; + out_gen_bus_if.ren <= pipeline_trans_if.ren; + out_gen_bus_if.addr <= pipeline_trans_if.addr; + out_gen_bus_if.byte_en <= pipeline_trans_if.byte_en; + out_gen_bus_if.wdata <= next_wdata; + end else begin + if ((out_gen_bus_if.busy == 0) || (~out_gen_bus_if.ren && ~out_gen_bus_if.wen)) begin + out_gen_bus_if.wen <= pipeline_trans_if.wen; + out_gen_bus_if.ren <= pipeline_trans_if.ren; + out_gen_bus_if.addr <= pipeline_trans_if.addr; + out_gen_bus_if.byte_en <= pipeline_trans_if.byte_en; + out_gen_bus_if.wdata <= next_wdata; + end + end + end - assign pipeline_trans_if.busy = (out_gen_bus_if.addr == 0) ? 1 : out_gen_bus_if.busy; - assign pipeline_trans_if.rdata = out_gen_bus_if.rdata; - assign next_wdata = pipeline_trans_if.wdata; + assign pipeline_trans_if.busy = (out_gen_bus_if.addr == 0) ? 1 : out_gen_bus_if.busy; + assign pipeline_trans_if.rdata = out_gen_bus_if.rdata; + assign next_wdata = pipeline_trans_if.wdata; + assign pipeline_trans_if.error = out_gen_bus_if.error; endmodule diff --git a/source_code/caches/cache_if.svh b/source_code/caches/cache_if.svh new file mode 100644 index 000000000..b0481da12 --- /dev/null +++ b/source_code/caches/cache_if.svh @@ -0,0 +1,49 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cache_if.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/03/2022 +* Description: Bundles cache signals into one interface for each of passing in UVM DB +*/ + +`ifndef CACHE_IF_SVH +`define CACHE_IF_SVH + +interface cache_if( + input logic CLK +); + logic nRST; + logic clear, flush; + logic clear_done, flush_done; + + modport driver + ( + output nRST, + output clear, flush, + input CLK, clear_done, flush_done + ); + + modport cache + ( + input clear, flush, CLK, nRST, + output clear_done, flush_done + ); +endinterface + +`endif diff --git a/source_code/caches/caches.core b/source_code/caches/caches.core new file mode 100644 index 000000000..03c318ab2 --- /dev/null +++ b/source_code/caches/caches.core @@ -0,0 +1,22 @@ +CAPI=2: +name: socet:riscv:caches:0.1.0 +description: caches + +filesets: + rtl: + depend: + - "socet:riscv:packages" + files: + - caches_wrapper.sv + - separate_caches.sv + - direct_mapped_tpf/direct_mapped_tpf_cache.sv + - pass_through/pass_through_cache.sv + - l1/l1_cache.sv + - sram/sram.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + toplevel: caches_wrapper \ No newline at end of file diff --git a/source_code/caches/caches_wrapper.sv b/source_code/caches/caches_wrapper.sv index 025019160..11ab9b126 100644 --- a/source_code/caches/caches_wrapper.sv +++ b/source_code/caches/caches_wrapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,26 +19,27 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 11/08/2016 -* Description: Wrapper file that instantiates the desired cache structure +* Description: Wrapper file that instantiates the desired cache structure */ `include "generic_bus_if.vh" `include "cache_control_if.vh" `include "component_selection_defines.vh" -module caches_wrapper( - input logic CLK, nRST, - generic_bus_if.cpu icache_mem_gen_bus_if, - generic_bus_if.cpu dcache_mem_gen_bus_if, - generic_bus_if.generic_bus icache_proc_gen_bus_if, - generic_bus_if.generic_bus dcache_proc_gen_bus_if, - cache_control_if cc_if +module caches_wrapper ( + input logic CLK, + nRST, + generic_bus_if.cpu icache_mem_gen_bus_if, + generic_bus_if.cpu dcache_mem_gen_bus_if, + generic_bus_if.generic_bus icache_proc_gen_bus_if, + generic_bus_if.generic_bus dcache_proc_gen_bus_if, + cache_control_if cc_if ); - generate - case (CACHE_CONFIG) - "separate" : separate_caches sep_caches(.*); - endcase - endgenerate + generate + case (CACHE_CONFIG) + "separate": separate_caches sep_caches (.*); + endcase + endgenerate endmodule - + diff --git a/source_code/caches/direct_mapped_tpf/direct_mapped_tpf_cache.sv b/source_code/caches/direct_mapped_tpf/direct_mapped_tpf_cache.sv index 9afb79dea..dc3222fea 100644 --- a/source_code/caches/direct_mapped_tpf/direct_mapped_tpf_cache.sv +++ b/source_code/caches/direct_mapped_tpf/direct_mapped_tpf_cache.sv @@ -1,528 +1,496 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: direct_mapped_tpf_cache.sv -* +* * Created by: John Skubic -* Email: jjs.skubic@gmail.com +* Email: jjs.skubic@gmail.com * Date Created: 05/28/2017 * Description: Direct Mapped Cache with tagged prefetch. The following are configurable: * - Cache Size * - Non-Cacheable start address * - Block Size -* - Prefetch Length +* - Prefetch Length */ `include "generic_bus_if.vh" module direct_mapped_tpf_cache ( - input logic CLK, nRST, - input logic clear, flush, - output logic clear_done, flush_done, - generic_bus_if.cpu mem_gen_bus_if, - generic_bus_if.generic_bus proc_gen_bus_if + input logic CLK, + nRST, + input logic clear, + flush, + output logic clear_done, + flush_done, + generic_bus_if.cpu mem_gen_bus_if, + generic_bus_if.generic_bus proc_gen_bus_if ); - import rv32i_types_pkg::*; - - /* --- Parameters --- */ - - // configurable parameters - parameter CACHE_SIZE = 1024; // In bytes, must be power of 2 - parameter BLOCK_SIZE = 2; // must be power of 2 - parameter PREFETCH_LENGTH = 1; // must be power of 2 - parameter NONCACHE_START_ADDR = 32'h8000_0000; - - // local parameters - localparam N_INDICES = CACHE_SIZE / (BLOCK_SIZE * WORD_SIZE / 8); - localparam BLK_OFF_BITS = $clog2(BLOCK_SIZE); - localparam IDX_BITS = $clog2(N_INDICES); - localparam TAG_BITS = RAM_ADDR_SIZE - 2 - BLK_OFF_BITS - IDX_BITS; - localparam N_BITS_IN_FRAME = (BLOCK_SIZE * WORD_SIZE) + TAG_BITS + 3; - localparam N_BYTES_IN_FRAME = N_BITS_IN_FRAME%8 ? N_BITS_IN_FRAME/8 + 1 : N_BITS_IN_FRAME/8; - localparam META_BYTE_L = (BLOCK_SIZE * WORD_SIZE)/8; - localparam META_BYTE_H = N_BYTES_IN_FRAME-1; - localparam PFETCH_CNT_BITS = $clog2(PREFETCH_LENGTH); - - /* --- Custom Data Types --- */ - - typedef struct packed { - logic [TAG_BITS-1:0] tag; - logic [IDX_BITS-1:0] idx; - logic [BLK_OFF_BITS-1:0] blk_off; - logic [1:0] byte_off; - } cache_addr_t; - - typedef struct packed { - logic d; - logic v; - logic p; - logic [TAG_BITS-1:0] tag; - word_t [BLOCK_SIZE-1:0] data; - } cache_frame_t; - - typedef enum logic [3:0] { - IDLE, - // flush and clear handling - CLEAR_PREP, - CLEAR_WB, - CLEAR_UPDATE, - // normal operation - EVAL, - FETCH, - PREFETCH, - PREFETCH_PREP, - PREFETCH_WB, - WB - } sm_t; - - /* --- Signal Instantiations --- */ - - cache_frame_t frame_buffer, frame_buffer_next; - cache_addr_t req_addr; - - logic init_flag; - logic init_complete; - logic flush_flag; - logic flush_reg; - logic flush_clear; - logic clear_flag, clear_clear, clear_reg; - logic request; - logic direct_mem_req; - logic hit, tag_match; - - logic [3:0] req_byte_en; - logic [31:0] req_byte_en_expand; - - cache_addr_t curr_addr, curr_addr_next; - - // signals for accessing the sram of cache - cache_frame_t cache_wdata; - logic [RAM_ADDR_SIZE-1:0] cache_addr; - logic [N_BYTES_IN_FRAME-1:0] cache_byte_en; - logic cache_wen, cache_ren; - logic [N_BITS_IN_FRAME-1:0] cache_rdata; - logic cache_busy, cache_busy_raw; - - // state machine - sm_t curr_state, next_state; - cache_addr_t sm_addr; - generic_bus_if sm_bus_if(); - - logic [IDX_BITS:0] flush_cnt, flush_cnt_next; - logic [BLK_OFF_BITS:0] access_cnt, access_cnt_next; - logic [PFETCH_CNT_BITS:0] prefetch_cnt, prefetch_cnt_next; - - /* --- Module Instantiations --- */ - - // sram for cache - config_ram_wrapper # ( - .N_BYTES(N_BYTES_IN_FRAME), - .DEPTH(N_INDICES) + import rv32i_types_pkg::*; + + /* --- Parameters --- */ + + // configurable parameters + parameter CACHE_SIZE = 1024; // In bytes, must be power of 2 + parameter BLOCK_SIZE = 2; // must be power of 2 + parameter PREFETCH_LENGTH = 1; // must be power of 2 + parameter NONCACHE_START_ADDR = 32'h8000_0000; + + // local parameters + localparam N_INDICES = CACHE_SIZE / (BLOCK_SIZE * WORD_SIZE / 8); + localparam BLK_OFF_BITS = $clog2(BLOCK_SIZE); + localparam IDX_BITS = $clog2(N_INDICES); + localparam TAG_BITS = RAM_ADDR_SIZE - 2 - BLK_OFF_BITS - IDX_BITS; + localparam N_BITS_IN_FRAME = (BLOCK_SIZE * WORD_SIZE) + TAG_BITS + 3; + localparam N_BYTES_IN_FRAME = N_BITS_IN_FRAME%8 ? N_BITS_IN_FRAME/8 + 1 : N_BITS_IN_FRAME/8; + localparam META_BYTE_L = (BLOCK_SIZE * WORD_SIZE) / 8; + localparam META_BYTE_H = N_BYTES_IN_FRAME - 1; + localparam PFETCH_CNT_BITS = $clog2(PREFETCH_LENGTH); + + /* --- Custom Data Types --- */ + + typedef struct packed { + logic [TAG_BITS-1:0] tag; + logic [IDX_BITS-1:0] idx; + logic [BLK_OFF_BITS-1:0] blk_off; + logic [1:0] byte_off; + } cache_addr_t; + + typedef struct packed { + logic d; + logic v; + logic p; + logic [TAG_BITS-1:0] tag; + word_t [BLOCK_SIZE-1:0] data; + } cache_frame_t; + + typedef enum logic [3:0] { + IDLE, + // flush and clear handling + CLEAR_PREP, + CLEAR_WB, + CLEAR_UPDATE, + // normal operation + EVAL, + FETCH, + PREFETCH, + PREFETCH_PREP, + PREFETCH_WB, + WB + } sm_t; + + /* --- Signal Instantiations --- */ + + cache_frame_t frame_buffer, frame_buffer_next; + cache_addr_t req_addr; + + logic init_flag; + logic init_complete; + logic flush_flag; + logic flush_reg; + logic flush_clear; + logic clear_flag, clear_clear, clear_reg; + logic request; + logic direct_mem_req; + logic hit, tag_match; + + logic [ 3:0] req_byte_en; + logic [31:0] req_byte_en_expand; + + cache_addr_t curr_addr, curr_addr_next; + + // signals for accessing the sram of cache + cache_frame_t cache_wdata; + logic [RAM_ADDR_SIZE-1:0] cache_addr; + logic [N_BYTES_IN_FRAME-1:0] cache_byte_en; + logic cache_wen, cache_ren; + logic [N_BITS_IN_FRAME-1:0] cache_rdata; + logic cache_busy, cache_busy_raw; + + // state machine + sm_t curr_state, next_state; + cache_addr_t sm_addr; + generic_bus_if sm_bus_if (); + + logic [IDX_BITS:0] flush_cnt, flush_cnt_next; + logic [BLK_OFF_BITS:0] access_cnt, access_cnt_next; + logic [PFETCH_CNT_BITS:0] prefetch_cnt, prefetch_cnt_next; + + /* --- Module Instantiations --- */ + + // sram for cache + config_ram_wrapper #( + .N_BYTES(N_BYTES_IN_FRAME), + .DEPTH (N_INDICES) ) cache_mem ( - .CLK(CLK), - .nRST(nRST), - .wdata(cache_wdata), - .addr(cache_addr), - .byte_en(cache_byte_en), - .wen(cache_wen), - .ren(cache_ren), - .rdata(cache_rdata), - .busy(cache_busy_raw) - ); - - assign cache_busy = cache_busy_raw | ~(cache_wen | cache_ren); - - /* --- Glue Logic and Output Logic --- */ - - assign req_addr = proc_gen_bus_if.addr; - assign req_byte_en = proc_gen_bus_if.byte_en; - assign req_byte_en_expand = {{8{req_byte_en[3]}}, {8{req_byte_en[2]}}, {8{req_byte_en[1]}}, {8{req_byte_en[0]}}}; - assign direct_mem_req = (proc_gen_bus_if.addr >= NONCACHE_START_ADDR); - assign flush_flag = flush | init_flag | flush_reg; - assign clear_flag = clear | clear_reg; - assign request = (proc_gen_bus_if.wen | proc_gen_bus_if.ren) && ~direct_mem_req; - - // clear and flush response - assign clear_done = clear_clear; - assign flush_done = flush_clear; - - //hits given out in EVAL and FETCH - assign tag_match = (req_addr.tag == frame_buffer.tag) && frame_buffer.v; - always_comb begin - hit = 1'b0; - if (curr_state == EVAL) begin - if (tag_match && (~proc_gen_bus_if.wen || ~cache_busy)) - hit = 1'b1; - end else if ((curr_state == FETCH) && (access_cnt == BLOCK_SIZE) && ~cache_busy) begin - hit = 1'b1; + .CLK(CLK), + .nRST(nRST), + .wdata(cache_wdata), + .addr(cache_addr), + .byte_en(cache_byte_en), + .wen(cache_wen), + .ren(cache_ren), + .rdata(cache_rdata), + .busy(cache_busy_raw) + ); + + assign cache_busy = cache_busy_raw | ~(cache_wen | cache_ren); + + /* --- Glue Logic and Output Logic --- */ + + assign req_addr = proc_gen_bus_if.addr; + assign req_byte_en = proc_gen_bus_if.byte_en; + assign req_byte_en_expand = { + {8{req_byte_en[3]}}, {8{req_byte_en[2]}}, {8{req_byte_en[1]}}, {8{req_byte_en[0]}} + }; + assign direct_mem_req = (proc_gen_bus_if.addr >= NONCACHE_START_ADDR); + assign flush_flag = flush | init_flag | flush_reg; + assign clear_flag = clear | clear_reg; + assign request = (proc_gen_bus_if.wen | proc_gen_bus_if.ren) && ~direct_mem_req; + + // clear and flush response + assign clear_done = clear_clear; + assign flush_done = flush_clear; + + //hits given out in EVAL and FETCH + assign tag_match = (req_addr.tag == frame_buffer.tag) && frame_buffer.v; + always_comb begin + hit = 1'b0; + if (curr_state == EVAL) begin + if (tag_match && (~proc_gen_bus_if.wen || ~cache_busy)) hit = 1'b1; + end else if ((curr_state == FETCH) && (access_cnt == BLOCK_SIZE) && ~cache_busy) begin + hit = 1'b1; + end end - end - - // Memory Arbitration between state machine and direct memory access - assign mem_gen_bus_if.addr = direct_mem_req ? proc_gen_bus_if.addr : sm_bus_if.addr; - assign mem_gen_bus_if.wdata = direct_mem_req ? proc_gen_bus_if.wdata : sm_bus_if.wdata; - assign mem_gen_bus_if.ren = direct_mem_req ? proc_gen_bus_if.ren : sm_bus_if.ren; - assign mem_gen_bus_if.wen = direct_mem_req ? proc_gen_bus_if.wen : sm_bus_if.wen; - assign mem_gen_bus_if.byte_en = direct_mem_req ? proc_gen_bus_if.byte_en : sm_bus_if.byte_en; - assign proc_gen_bus_if.rdata = direct_mem_req ? mem_gen_bus_if.rdata :frame_buffer.data[req_addr.blk_off] ; - assign sm_bus_if.rdata = mem_gen_bus_if.rdata; - assign proc_gen_bus_if.busy = direct_mem_req ? mem_gen_bus_if.busy : ~hit; - assign sm_bus_if.busy = direct_mem_req ? 1'b1 : mem_gen_bus_if.busy; - - assign sm_bus_if.addr = sm_addr; - - - // Flip-Flops with no reset - always_ff @ (posedge CLK) begin - frame_buffer <= frame_buffer_next; - curr_addr <= curr_addr_next; - end - - // Flip-Flops with reset - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) begin - flush_cnt <= '0; - access_cnt <= '0; - prefetch_cnt <= '0; - end else begin - flush_cnt <= flush_cnt_next; - access_cnt <= access_cnt_next; - prefetch_cnt <= prefetch_cnt_next; + + // Memory Arbitration between state machine and direct memory access + assign mem_gen_bus_if.addr = direct_mem_req ? proc_gen_bus_if.addr : sm_bus_if.addr; + assign mem_gen_bus_if.wdata = direct_mem_req ? proc_gen_bus_if.wdata : sm_bus_if.wdata; + assign mem_gen_bus_if.ren = direct_mem_req ? proc_gen_bus_if.ren : sm_bus_if.ren; + assign mem_gen_bus_if.wen = direct_mem_req ? proc_gen_bus_if.wen : sm_bus_if.wen; + assign mem_gen_bus_if.byte_en = direct_mem_req ? proc_gen_bus_if.byte_en : sm_bus_if.byte_en; + assign proc_gen_bus_if.rdata = direct_mem_req ? mem_gen_bus_if.rdata :frame_buffer.data[req_addr.blk_off] ; + assign sm_bus_if.rdata = mem_gen_bus_if.rdata; + assign proc_gen_bus_if.busy = direct_mem_req ? mem_gen_bus_if.busy : ~hit; + assign sm_bus_if.busy = direct_mem_req ? 1'b1 : mem_gen_bus_if.busy; + + assign sm_bus_if.addr = sm_addr; + + + // Flip-Flops with no reset + always_ff @(posedge CLK) begin + frame_buffer <= frame_buffer_next; + curr_addr <= curr_addr_next; end - end - - // Init on reset - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - init_flag <= 1'b1; - else if (init_complete) - init_flag <= 1'b0; - end - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - flush_reg <= 1'b0; - else if (flush_clear) - flush_reg <= 1'b0; - else if (flush) - flush_reg <= 1'b1; - end - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - clear_reg <= 1'b0; - else if (clear_clear) - clear_reg <= 1'b0; - else if (clear) - clear_reg <= 1'b1; - end - - // SM ouptut Logic - - always_comb begin - flush_cnt_next = flush_cnt; - cache_addr = '0; - cache_ren = 0; - cache_wen = 0; - cache_byte_en = '1; - cache_wdata = '0; - curr_addr_next = curr_addr; - frame_buffer_next = frame_buffer; - access_cnt_next = access_cnt; - prefetch_cnt_next = prefetch_cnt; - sm_addr = '0; - sm_bus_if.wen = 0; - sm_bus_if.ren = 0; - sm_bus_if.wdata = '0; - sm_bus_if.byte_en = '1; - init_complete = 1'b0; - flush_clear = 1'b0; - clear_clear = 1'b0; - - casez (curr_state) - IDLE : begin - if (clear_flag) begin - curr_addr_next = req_addr; - flush_cnt_next = N_INDICES-1; - end else if (flush_flag) begin - curr_addr_next = 0; - flush_cnt_next = 0; - end else if (request) begin - cache_addr = req_addr.idx; - cache_ren = 1'b1; - if(~cache_busy) begin - curr_addr_next = req_addr; - frame_buffer_next = cache_rdata; - end + + // Flip-Flops with reset + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + flush_cnt <= '0; + access_cnt <= '0; + prefetch_cnt <= '0; + end else begin + flush_cnt <= flush_cnt_next; + access_cnt <= access_cnt_next; + prefetch_cnt <= prefetch_cnt_next; end - end - // Normal Operation - EVAL : begin - if (~tag_match) - access_cnt_next = 0; - else begin // hit - if (hit && frame_buffer.p) begin - {curr_addr_next.tag, curr_addr_next.idx} = + end + + // Init on reset + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) init_flag <= 1'b1; + else if (init_complete) init_flag <= 1'b0; + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) flush_reg <= 1'b0; + else if (flush_clear) flush_reg <= 1'b0; + else if (flush) flush_reg <= 1'b1; + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) clear_reg <= 1'b0; + else if (clear_clear) clear_reg <= 1'b0; + else if (clear) clear_reg <= 1'b1; + end + + // SM ouptut Logic + + always_comb begin + flush_cnt_next = flush_cnt; + cache_addr = '0; + cache_ren = 0; + cache_wen = 0; + cache_byte_en = '1; + cache_wdata = '0; + curr_addr_next = curr_addr; + frame_buffer_next = frame_buffer; + access_cnt_next = access_cnt; + prefetch_cnt_next = prefetch_cnt; + sm_addr = '0; + sm_bus_if.wen = 0; + sm_bus_if.ren = 0; + sm_bus_if.wdata = '0; + sm_bus_if.byte_en = '1; + init_complete = 1'b0; + flush_clear = 1'b0; + clear_clear = 1'b0; + + casez (curr_state) + IDLE: begin + if (clear_flag) begin + curr_addr_next = req_addr; + flush_cnt_next = N_INDICES - 1; + end else if (flush_flag) begin + curr_addr_next = 0; + flush_cnt_next = 0; + end else if (request) begin + cache_addr = req_addr.idx; + cache_ren = 1'b1; + if (~cache_busy) begin + curr_addr_next = req_addr; + frame_buffer_next = cache_rdata; + end + end + end + // Normal Operation + EVAL: begin + if (~tag_match) access_cnt_next = 0; + else begin // hit + if (hit && frame_buffer.p) begin + {curr_addr_next.tag, curr_addr_next.idx} = {curr_addr.tag, curr_addr.idx} + 1; - prefetch_cnt_next = 0; - end - if (proc_gen_bus_if.wen) begin // cache write hit - cache_addr = req_addr.idx; - frame_buffer_next.data[req_addr.blk_off] = + prefetch_cnt_next = 0; + end + if (proc_gen_bus_if.wen) begin // cache write hit + cache_addr = req_addr.idx; + frame_buffer_next.data[req_addr.blk_off] = (frame_buffer.data[req_addr.blk_off] & ~req_byte_en_expand) | (req_byte_en_expand & proc_gen_bus_if.wdata); - frame_buffer_next.d = 1'b1; - cache_wdata = frame_buffer_next; - cache_wdata.p = 1'b0; - cache_wen = 1'b1; - end - end - end - FETCH : begin - sm_addr = curr_addr; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - - cache_wdata = frame_buffer; - cache_addr = curr_addr.idx; - if (access_cnt == BLOCK_SIZE) begin - sm_bus_if.ren = 1'b0; - cache_wen = 1'b1; - if (~cache_busy) begin - {curr_addr_next.tag, curr_addr_next.idx} + frame_buffer_next.d = 1'b1; + cache_wdata = frame_buffer_next; + cache_wdata.p = 1'b0; + cache_wen = 1'b1; + end + end + end + FETCH: begin + sm_addr = curr_addr; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + + cache_wdata = frame_buffer; + cache_addr = curr_addr.idx; + if (access_cnt == BLOCK_SIZE) begin + sm_bus_if.ren = 1'b0; + cache_wen = 1'b1; + if (~cache_busy) begin + {curr_addr_next.tag, curr_addr_next.idx} = {curr_addr.tag, curr_addr.idx} + 1; - prefetch_cnt_next = 0; - end - end else begin - sm_bus_if.ren = 1'b1; - cache_wen = 1'b0; - end - - frame_buffer_next.tag = req_addr.tag; - frame_buffer_next.p = 0; - frame_buffer_next.v = 1; - frame_buffer_next.d = proc_gen_bus_if.wen; - - if (~sm_bus_if.busy) begin - if (proc_gen_bus_if.wen && (sm_addr.blk_off == req_addr.blk_off)) begin - frame_buffer_next.data[sm_addr.blk_off] = + prefetch_cnt_next = 0; + end + end else begin + sm_bus_if.ren = 1'b1; + cache_wen = 1'b0; + end + + frame_buffer_next.tag = req_addr.tag; + frame_buffer_next.p = 0; + frame_buffer_next.v = 1; + frame_buffer_next.d = proc_gen_bus_if.wen; + + if (~sm_bus_if.busy) begin + if (proc_gen_bus_if.wen && (sm_addr.blk_off == req_addr.blk_off)) begin + frame_buffer_next.data[sm_addr.blk_off] = (sm_bus_if.rdata & ~req_byte_en_expand) | (req_byte_en_expand & proc_gen_bus_if.wdata); - end else begin - frame_buffer_next.data[sm_addr.blk_off] = sm_bus_if.rdata; - end - access_cnt_next = access_cnt + 1; - end - end - WB : begin - sm_addr.idx = curr_addr.idx; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - sm_addr.tag = frame_buffer.tag; - sm_bus_if.wdata = frame_buffer.data[access_cnt]; - sm_bus_if.wen = 1'b1; - if (~sm_bus_if.busy) - access_cnt_next = access_cnt + 1; - - if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE-1)) - access_cnt_next = '0; - end - PREFETCH_PREP : begin - cache_addr = curr_addr.idx; - cache_ren = 1'b1; - if (~cache_busy) begin - access_cnt_next = 0; - frame_buffer_next = cache_rdata; - end - end - PREFETCH_WB : begin - sm_addr.idx = curr_addr.idx; - sm_addr.tag = frame_buffer.tag; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - sm_bus_if.wdata = frame_buffer.data[access_cnt]; - sm_bus_if.wen = 1'b1; - sm_bus_if.byte_en = 4'hf; - if (~sm_bus_if.busy) begin - if (access_cnt == BLOCK_SIZE-1) - access_cnt_next = 0; - else - access_cnt_next = access_cnt + 1; - end - end - PREFETCH : begin - sm_addr = curr_addr; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - cache_addr = curr_addr.idx; - cache_wdata = frame_buffer; - - if (access_cnt == BLOCK_SIZE) begin - sm_bus_if.ren = 1'b0; - cache_wen = 1'b1; - end else begin - sm_bus_if.ren = 1'b1; - cache_wen = 1'b0; - end - - if (~sm_bus_if.busy) begin - access_cnt_next = access_cnt + 1; - frame_buffer_next.data[access_cnt] = sm_bus_if.rdata; - frame_buffer_next.p = 1'b1; - frame_buffer_next.v = 1'b1; - frame_buffer_next.d = 1'b0; - frame_buffer_next.tag = curr_addr.tag; - end + end else begin + frame_buffer_next.data[sm_addr.blk_off] = sm_bus_if.rdata; + end + access_cnt_next = access_cnt + 1; + end + end + WB: begin + sm_addr.idx = curr_addr.idx; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + sm_addr.tag = frame_buffer.tag; + sm_bus_if.wdata = frame_buffer.data[access_cnt]; + sm_bus_if.wen = 1'b1; + if (~sm_bus_if.busy) access_cnt_next = access_cnt + 1; + + if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE - 1)) access_cnt_next = '0; + end + PREFETCH_PREP: begin + cache_addr = curr_addr.idx; + cache_ren = 1'b1; + if (~cache_busy) begin + access_cnt_next = 0; + frame_buffer_next = cache_rdata; + end + end + PREFETCH_WB: begin + sm_addr.idx = curr_addr.idx; + sm_addr.tag = frame_buffer.tag; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + sm_bus_if.wdata = frame_buffer.data[access_cnt]; + sm_bus_if.wen = 1'b1; + sm_bus_if.byte_en = 4'hf; + if (~sm_bus_if.busy) begin + if (access_cnt == BLOCK_SIZE - 1) access_cnt_next = 0; + else access_cnt_next = access_cnt + 1; + end + end + PREFETCH: begin + sm_addr = curr_addr; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + cache_addr = curr_addr.idx; + cache_wdata = frame_buffer; + + if (access_cnt == BLOCK_SIZE) begin + sm_bus_if.ren = 1'b0; + cache_wen = 1'b1; + end else begin + sm_bus_if.ren = 1'b1; + cache_wen = 1'b0; + end + + if (~sm_bus_if.busy) begin + access_cnt_next = access_cnt + 1; + frame_buffer_next.data[access_cnt] = sm_bus_if.rdata; + frame_buffer_next.p = 1'b1; + frame_buffer_next.v = 1'b1; + frame_buffer_next.d = 1'b0; + frame_buffer_next.tag = curr_addr.tag; + end + + if (~cache_busy) begin + prefetch_cnt_next = prefetch_cnt + 1; + {curr_addr_next.tag, curr_addr_next.idx} = {curr_addr.tag, curr_addr.idx} + 1; + end + end + // Flush and Clear + CLEAR_PREP: begin + cache_addr = curr_addr.idx; + cache_ren = 1'b1; + if (~cache_busy) begin + frame_buffer_next = cache_rdata; + if (frame_buffer_next.d && frame_buffer_next.v) access_cnt_next = 0; + end + end + CLEAR_WB: begin //writeback all words in the current frame + sm_addr.idx = curr_addr.idx; + sm_addr.byte_off = 2'b0; + sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; + sm_addr.tag = frame_buffer.tag; + sm_bus_if.wdata = frame_buffer.data[access_cnt]; + sm_bus_if.wen = 1'b1; + if (~sm_bus_if.busy) access_cnt_next = access_cnt + 1; + end + CLEAR_UPDATE: begin // clear out the cache memory, increment flush count + cache_addr = curr_addr.idx; + cache_wen = 1'b1; + cache_wdata = '0; + cache_byte_en = '1; + if (~cache_busy) begin + flush_cnt_next = flush_cnt + 1; + curr_addr_next.idx = curr_addr.idx + 1; + if (flush_cnt == N_INDICES - 1) begin + init_complete = 1'b1; + if (flush_reg) flush_clear = 1'b1; + else if (clear_reg) clear_clear = 1'b1; + end + end + end + endcase + end - if (~cache_busy) begin - prefetch_cnt_next = prefetch_cnt + 1; - {curr_addr_next.tag, curr_addr_next.idx} - = {curr_addr.tag, curr_addr.idx} + 1; - end - end - // Flush and Clear - CLEAR_PREP : begin - cache_addr = curr_addr.idx; - cache_ren = 1'b1; - if (~cache_busy) begin - frame_buffer_next = cache_rdata; - if (frame_buffer_next.d && frame_buffer_next.v) - access_cnt_next = 0; - end - end - CLEAR_WB : begin //writeback all words in the current frame - sm_addr.idx = curr_addr.idx; - sm_addr.byte_off = 2'b0; - sm_addr.blk_off = access_cnt[BLK_OFF_BITS-1:0]; - sm_addr.tag = frame_buffer.tag; - sm_bus_if.wdata = frame_buffer.data[access_cnt]; - sm_bus_if.wen = 1'b1; - if (~sm_bus_if.busy) - access_cnt_next = access_cnt + 1; - end - CLEAR_UPDATE : begin // clear out the cache memory, increment flush count - cache_addr = curr_addr.idx; - cache_wen = 1'b1; - cache_wdata = '0; - cache_byte_en = '1; - if (~cache_busy) begin - flush_cnt_next = flush_cnt + 1; - curr_addr_next.idx = curr_addr.idx + 1; - if (flush_cnt == N_INDICES-1) begin - init_complete = 1'b1; - if (flush_reg) - flush_clear = 1'b1; - else if (clear_reg) - clear_clear = 1'b1; - end - end - end - endcase - end - - /* --- State Machine Logic --- */ - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - curr_state <= IDLE; - else - curr_state <= next_state; - end - - // Next State Logic - - always_comb begin - next_state = curr_state; - casez (curr_state) - IDLE : begin - if (clear_flag || flush_flag) - next_state = CLEAR_PREP; - else if (~cache_busy && request) begin - next_state = EVAL; - end - end - // Normal Operation - EVAL : begin - if (proc_gen_bus_if.ren && tag_match) begin - if (frame_buffer.p) - next_state = PREFETCH_PREP; - else - next_state = IDLE; - end else if (proc_gen_bus_if.wen && tag_match) begin - if (~cache_busy) begin - if (frame_buffer.p) - next_state = PREFETCH_PREP; - else - next_state = IDLE; - end - end else if (frame_buffer.v && frame_buffer.d) - next_state = WB; - else - next_state = FETCH; - end - FETCH : begin - if ((access_cnt == BLOCK_SIZE) && ~cache_busy) - next_state = PREFETCH_PREP; - end - PREFETCH_PREP : begin - if (~cache_busy) begin - if (frame_buffer_next.d && frame_buffer_next.v) - next_state = PREFETCH_WB; - else - next_state = PREFETCH; - end - end - PREFETCH_WB : begin - if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE-1)) - next_state = PREFETCH; - end - PREFETCH : begin - if ((access_cnt == BLOCK_SIZE) && ~cache_busy) begin - if (prefetch_cnt == PREFETCH_LENGTH-1) - next_state = IDLE; - else - next_state = PREFETCH_PREP; - end - end - WB : begin - if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE-1)) - next_state = FETCH; - end - // Flush and Clear - CLEAR_PREP : begin - if (~cache_busy) begin - if (~init_flag && frame_buffer_next.d && frame_buffer_next.v) - next_state = CLEAR_WB; - else - next_state = CLEAR_UPDATE; - end - end - CLEAR_WB : begin - if (~sm_bus_if.busy && (access_cnt == (BLOCK_SIZE-1))) - next_state = CLEAR_UPDATE; - end - CLEAR_UPDATE : begin - if (~cache_busy) begin - if (flush_cnt != N_INDICES-1) - next_state = CLEAR_PREP; - else - next_state = IDLE; - end - end - endcase - end + /* --- State Machine Logic --- */ + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) curr_state <= IDLE; + else curr_state <= next_state; + end + + // Next State Logic + + always_comb begin + next_state = curr_state; + casez (curr_state) + IDLE: begin + if (clear_flag || flush_flag) next_state = CLEAR_PREP; + else if (~cache_busy && request) begin + next_state = EVAL; + end + end + // Normal Operation + EVAL: begin + if (proc_gen_bus_if.ren && tag_match) begin + if (frame_buffer.p) next_state = PREFETCH_PREP; + else next_state = IDLE; + end else if (proc_gen_bus_if.wen && tag_match) begin + if (~cache_busy) begin + if (frame_buffer.p) next_state = PREFETCH_PREP; + else next_state = IDLE; + end + end else if (frame_buffer.v && frame_buffer.d) next_state = WB; + else next_state = FETCH; + end + FETCH: begin + if ((access_cnt == BLOCK_SIZE) && ~cache_busy) next_state = PREFETCH_PREP; + end + PREFETCH_PREP: begin + if (~cache_busy) begin + if (frame_buffer_next.d && frame_buffer_next.v) next_state = PREFETCH_WB; + else next_state = PREFETCH; + end + end + PREFETCH_WB: begin + if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE - 1)) next_state = PREFETCH; + end + PREFETCH: begin + if ((access_cnt == BLOCK_SIZE) && ~cache_busy) begin + if (prefetch_cnt == PREFETCH_LENGTH - 1) next_state = IDLE; + else next_state = PREFETCH_PREP; + end + end + WB: begin + if (~sm_bus_if.busy && (access_cnt == BLOCK_SIZE - 1)) next_state = FETCH; + end + // Flush and Clear + CLEAR_PREP: begin + if (~cache_busy) begin + if (~init_flag && frame_buffer_next.d && frame_buffer_next.v) + next_state = CLEAR_WB; + else next_state = CLEAR_UPDATE; + end + end + CLEAR_WB: begin + if (~sm_bus_if.busy && (access_cnt == (BLOCK_SIZE - 1))) next_state = CLEAR_UPDATE; + end + CLEAR_UPDATE: begin + if (~cache_busy) begin + if (flush_cnt != N_INDICES - 1) next_state = CLEAR_PREP; + else next_state = IDLE; + end + end + + default: next_state = curr_state; + endcase + end endmodule diff --git a/source_code/caches/l1/l1_cache.sv b/source_code/caches/l1/l1_cache.sv new file mode 100644 index 000000000..f0fac0838 --- /dev/null +++ b/source_code/caches/l1/l1_cache.sv @@ -0,0 +1,419 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: l1_cache.sv +* +* Created by: Rufat Imanov, Aedan Frazier, Dhruv Gupta +* Email: rimanov@purdue.edu +* Date Created: 06/20/2021 +* Description: L1 Cache. The following are configurable: +* - Cache Size +* - Non-Cacheable start address +* - Block Size | max 8 +* - ASSOC | either 1 or 2 +*/ + +`include "generic_bus_if.vh" + +module l1_cache #( + parameter CACHE_SIZE = 1024, // must be power of 2, in bytes, max 4k - 4 * 2^10 + parameter BLOCK_SIZE = 2, // must be power of 2, max 8 + parameter ASSOC = 1, // dont set this to 0 + parameter NONCACHE_START_ADDR = 32'hF000_0000 // sh/sb still have issues when uncached; not sure whats up with that still tbh +) +( + input logic CLK, nRST, + input logic clear, flush, + output logic clear_done, flush_done, + generic_bus_if.cpu mem_gen_bus_if, + generic_bus_if.generic_bus proc_gen_bus_if +); + import rv32i_types_pkg::*; + + // local parameters + localparam N_TOTAL_BYTES = CACHE_SIZE / 8; + localparam N_TOTAL_WORDS = N_TOTAL_BYTES / 4; + localparam N_TOTAL_FRAMES = N_TOTAL_WORDS / BLOCK_SIZE; + localparam N_SETS = N_TOTAL_FRAMES / ASSOC; + localparam N_FRAME_BITS = $clog2(ASSOC) + (ASSOC == 1); + localparam N_SET_BITS = $clog2(N_SETS) + (N_SETS == 1); + localparam N_BLOCK_BITS = $clog2(BLOCK_SIZE) + (BLOCK_SIZE == 1); + localparam N_TAG_BITS = WORD_SIZE - N_SET_BITS - N_BLOCK_BITS - 2; + localparam FRAME_SIZE = WORD_SIZE * BLOCK_SIZE + N_TAG_BITS + 2; // in bits + localparam SRAM_W = FRAME_SIZE * ASSOC; // sram parameters + + typedef struct packed { + logic valid; + logic dirty; + logic [N_TAG_BITS - 1:0] tag; + word_t [BLOCK_SIZE - 1:0] data; + } cache_frame_t; // cache frame + + typedef struct packed { + cache_frame_t [ASSOC - 1:0] frames; + } cache_set_t; // cache set + + typedef struct packed { + logic [N_TAG_BITS-1:0] tag_bits; + logic [N_SET_BITS-1:0] idx_bits; + logic [N_BLOCK_BITS-1:0] block_bits; + logic [1:0] byte_bits; + } decoded_cache_addr_t; // cache address type + + typedef struct packed { + logic finish; + logic [N_SET_BITS-1:0] set_num; + logic [N_FRAME_BITS-1:0] frame_num; // assoc + logic [N_BLOCK_BITS-1:0] word_num; + } flush_idx_t; // flush counter type + + typedef enum { + IDLE, HIT, FETCH, WB, FLUSH_CACHE + } cache_fsm_t; // cache state machine + + // counter signals + flush_idx_t flush_idx, next_flush_idx; + logic [N_BLOCK_BITS:0] word_num, next_word_num; + logic enable_word_count, clear_word_count, + clear_flush_count, enable_flush_count, enable_flush_count_nowb; + logic word_count_done; + // States + cache_fsm_t state, next_state; + // lru + logic [N_FRAME_BITS-1:0] ridx; + logic [N_SETS-1:0] last_used; + logic [N_SETS-1:0] next_last_used; + // address + word_t read_addr, next_read_addr; + decoded_cache_addr_t decoded_req_addr, next_decoded_req_addr; + decoded_cache_addr_t decoded_addr; + // Cache Hit + logic hit, pass_through; + word_t [BLOCK_SIZE-1:0] hit_data; + logic [N_FRAME_BITS-1:0] hit_idx; + // sram signals + cache_set_t sramWrite, sramRead, sramMask; + logic sramWEN; // no need for REN + logic [N_SET_BITS-1:0] sramSEL; + // flush reg + logic flush_req, nflush_req; + logic idle_done; + + // error handling + assign proc_gen_bus_if.error = mem_gen_bus_if.error; + + // sram instance + assign sramSEL = (state == FLUSH_CACHE || state == IDLE) ? flush_idx.set_num : decoded_addr.idx_bits; + sram #(.SRAM_WR_SIZE(SRAM_W), .SRAM_HEIGHT(N_SETS)) + SRAM(CLK, nRST, sramWrite, sramRead, 1'b1, sramWEN, sramSEL, sramMask); + + // flip flops + always_ff @ (posedge CLK, negedge nRST) begin + if(~nRST) begin + state <= IDLE; + flush_idx <= 0; + word_num <= 0; + last_used <= 0; + read_addr <= 0; + decoded_req_addr <= 0; + flush_req <= 0; + end + else begin + state <= next_state; // cache state machine + flush_idx <= next_flush_idx; // index for flushing the cache entries + word_num <= next_word_num; // word counter for fetching/writing back + last_used <= next_last_used; // MRU index + read_addr <= next_read_addr; // cache address to provide to memory + decoded_req_addr <= next_decoded_req_addr; // cache address requested by core + flush_req <= nflush_req; // flush requested by core + end + end + + // counters + always_comb begin + next_word_num = word_num; + next_flush_idx = flush_idx; + word_count_done = ~mem_gen_bus_if.busy && (BLOCK_SIZE - 1) == word_num; + // word counter logic + if (clear_word_count) + next_word_num = 0; + else if (enable_word_count) + next_word_num = word_num + 1; + + // flush counter logic + if (clear_flush_count) + next_flush_idx = 0; + else if (enable_flush_count_nowb) + next_flush_idx = flush_idx + BLOCK_SIZE; + else if (enable_flush_count) + next_flush_idx = flush_idx + 1; + + // correction for non-powers of 2 or 1 + if (next_flush_idx.set_num == N_SETS) + next_flush_idx = {1'b1, (N_SET_BITS + N_FRAME_BITS + N_BLOCK_BITS)'('0)}; + if (next_flush_idx.frame_num == ASSOC) + next_flush_idx = {({flush_idx.finish, flush_idx.set_num} + 1'b1), (N_FRAME_BITS + N_BLOCK_BITS)'('0)}; + if (next_flush_idx.word_num == BLOCK_SIZE) + next_flush_idx = {({flush_idx.finish, flush_idx.set_num, flush_idx.frame_num} + 1'b1), N_BLOCK_BITS'('0)}; + end + + // decoded address conversion + assign decoded_addr = decoded_cache_addr_t'(proc_gen_bus_if.addr); + + // hit logic with pass through + always_comb begin + hit = 0; + hit_idx = 0; + hit_data = 0; + pass_through = proc_gen_bus_if.addr >= NONCACHE_START_ADDR; + + if (!pass_through) begin + for(int i = 0; i < ASSOC; i++) begin + if(sramRead.frames[i].tag == decoded_addr.tag_bits && sramRead.frames[i].valid) begin + hit = 1'b1; + hit_data = sramRead.frames[i].data; + hit_idx = i; + end + end + end + end + + // cache output logic + // Outputs: counter control signals, cache, signals to memory, signals to processor + always_comb begin + sramWEN = 0; + sramWrite = 0; + sramMask = '1; + proc_gen_bus_if.busy = 1; + proc_gen_bus_if.rdata = 0; // TODO: Can this be optimized? + mem_gen_bus_if.ren = 0; + mem_gen_bus_if.wen = 0; + mem_gen_bus_if.addr = 0; + mem_gen_bus_if.wdata = 0; + mem_gen_bus_if.byte_en = '1; // set this to all 1s for evictions + enable_flush_count = 0; + enable_word_count = 0; + enable_flush_count_nowb = 0; + clear_flush_count = 0; + clear_word_count = 0; + flush_done = 0; + idle_done = 0; + clear_done = 0; + next_read_addr = read_addr; + next_decoded_req_addr = decoded_req_addr; + next_last_used = last_used; + + // associativity, using NRU + if (ASSOC == 1 || (last_used[decoded_addr.idx_bits] == (ASSOC - 1))) + ridx = 0; + else + ridx = last_used[decoded_addr.idx_bits] + 1; + + // state dependent output logic + casez(state) + IDLE: begin + // clear out caches with flush + sramWEN = 1; + sramWrite.frames[flush_idx.frame_num] = '0; + sramMask.frames[flush_idx.frame_num] = '0; + enable_flush_count_nowb = 1; + // flag the completion of flush + if (flush_idx.finish) begin + clear_flush_count = 1; + idle_done = 1; + end + end + HIT: begin + next_read_addr = decoded_addr; + clear_word_count = 1; + // cache hit on a processor read + if(proc_gen_bus_if.ren && hit && !flush) begin + proc_gen_bus_if.busy = 0; + proc_gen_bus_if.rdata = hit_data[decoded_addr.block_bits]; + next_last_used[decoded_addr.idx_bits] = hit_idx; + end + // cache hit on a processor write + else if(proc_gen_bus_if.wen && hit && !flush) begin + proc_gen_bus_if.busy = 0; + sramWEN = 1; + casez (proc_gen_bus_if.byte_en) + 4'b0001: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'hFFFFFF00; + 4'b0010: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'hFFFF00FF; + 4'b0100: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'hFF00FFFF; + 4'b1000: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'h00FFFFFF; + 4'b0011: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'hFFFF0000; + 4'b1100: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'h0000FFFF; + default: sramMask.frames[hit_idx].data[decoded_addr.block_bits] = 32'h0; + endcase + sramMask.frames[hit_idx].dirty = 0; + sramWrite.frames[hit_idx].data[decoded_addr.block_bits] = proc_gen_bus_if.wdata; + sramWrite.frames[hit_idx].dirty = 1; + next_last_used[decoded_addr.idx_bits] = hit_idx; + end + // passthrough + else if(pass_through && (proc_gen_bus_if.wen || proc_gen_bus_if.ren)) begin + mem_gen_bus_if.wen = proc_gen_bus_if.wen; + mem_gen_bus_if.ren = proc_gen_bus_if.ren; + mem_gen_bus_if.addr = proc_gen_bus_if.addr; + mem_gen_bus_if.byte_en = proc_gen_bus_if.byte_en; + proc_gen_bus_if.busy = mem_gen_bus_if.busy; + proc_gen_bus_if.rdata = mem_gen_bus_if.rdata; + if(proc_gen_bus_if.wen) begin + casez (proc_gen_bus_if.byte_en) + 4'b0001: mem_gen_bus_if.wdata = {24'd0, proc_gen_bus_if.wdata[7:0]}; + 4'b0010: mem_gen_bus_if.wdata = {16'd0,proc_gen_bus_if.wdata[15:8],8'd0}; + 4'b0100: mem_gen_bus_if.wdata = {8'd0, proc_gen_bus_if.wdata[23:16], 16'd0}; + 4'b1000: mem_gen_bus_if.wdata = {proc_gen_bus_if.wdata[31:24], 24'd0}; + 4'b0011: mem_gen_bus_if.wdata = {16'd0, proc_gen_bus_if.wdata[15:0]}; + 4'b1100: mem_gen_bus_if.wdata = {proc_gen_bus_if.wdata[31:16],16'd0}; + default: mem_gen_bus_if.wdata = proc_gen_bus_if.wdata; + endcase + end + end + // cache miss on a clean block + else if((proc_gen_bus_if.ren || proc_gen_bus_if.wen) && ~hit && ~sramRead.frames[ridx].dirty && ~pass_through) begin + next_decoded_req_addr = decoded_addr; + next_read_addr = {decoded_addr.tag_bits, decoded_addr.idx_bits, N_BLOCK_BITS'('0), 2'b00}; + end + // cache miss on a dirty block + else if((proc_gen_bus_if.ren || proc_gen_bus_if.wen) && ~hit && sramRead.frames[ridx].dirty && ~pass_through) begin + next_decoded_req_addr = decoded_addr; + next_read_addr = {sramRead.frames[ridx].tag, decoded_addr.idx_bits, N_BLOCK_BITS'('0), 2'b00}; + end + end + FETCH: begin + // set cache to be invalid before cache completes fetch + mem_gen_bus_if.ren = 1; + mem_gen_bus_if.addr = read_addr; + sramMask.frames[ridx].valid = 0; + sramWrite.frames[ridx].valid = 0; + // fill data + if(~mem_gen_bus_if.busy) begin + sramWEN = 1'b1; + enable_word_count = 1'b1; + next_read_addr = read_addr + 4; + sramWrite.frames[ridx].data[word_num] = mem_gen_bus_if.rdata; + sramMask.frames[ridx].data[word_num] = 1'b0; + end + // complete fetch transaction from memory + if(word_count_done) begin + sramWEN = 1; + clear_word_count = 1'b1; + sramWrite.frames[ridx].valid = 1'b1; + sramWrite.frames[ridx].tag = decoded_req_addr.tag_bits; + sramMask.frames[ridx].valid = 1'b0; + sramMask.frames[ridx].tag = 1'b0; + end + end + WB: begin + // set stim for eviction + mem_gen_bus_if.wen = 1'b1; + mem_gen_bus_if.addr = read_addr; + mem_gen_bus_if.wdata = sramRead.frames[ridx].data[word_num]; + // increment eviction word counter + if(~mem_gen_bus_if.busy) begin + enable_word_count = 1; + next_read_addr = read_addr + 4; + end + // invalidate when eviction is complete + if(word_count_done) begin + sramWEN = 1; + clear_word_count = 1; + sramWrite.frames[ridx].dirty = 0; + sramMask.frames[ridx].dirty = 0; + sramWrite.frames[ridx].valid = 0; + sramMask.frames[ridx].valid = 0; + next_read_addr = {decoded_addr.tag_bits, decoded_addr.idx_bits, N_BLOCK_BITS'('0), 2'b00}; + end + end + FLUSH_CACHE: begin + // flush to memory if valid & dirty + if (sramRead.frames[flush_idx.frame_num].valid && sramRead.frames[flush_idx.frame_num].dirty) begin + mem_gen_bus_if.wen = 1'b1; + mem_gen_bus_if.addr = {sramRead.frames[flush_idx.frame_num].tag, flush_idx.set_num, flush_idx.word_num, 2'b00}; + mem_gen_bus_if.wdata = sramRead.frames[flush_idx.frame_num].data[flush_idx.word_num]; + // increment to next word when flush of word is done + if (~mem_gen_bus_if.busy) begin + enable_flush_count = 1; + // clears entry when flushed + if (flush_idx.word_num == (BLOCK_SIZE - 1)) begin + sramWEN = 1; + sramWrite.frames[flush_idx.frame_num] = 0; + sramMask.frames[flush_idx.frame_num] = 0; + end + end + end + // else clears entry, moves to next frame + else begin + sramWEN = 1; + sramWrite.frames[flush_idx.frame_num] = 0; + sramMask.frames[flush_idx.frame_num] = 0; + enable_flush_count_nowb = 1; + end + // flag the completion of flush + if (flush_idx.finish) begin + clear_flush_count = 1; + flush_done = 1; + end + end + endcase + end + + // next state logic + always_comb begin + next_state = state; + casez(state) + IDLE: begin + if (idle_done) + next_state = HIT; + end + HIT: begin + if ((proc_gen_bus_if.ren || proc_gen_bus_if.wen) && ~hit && sramRead.frames[ridx].dirty && ~pass_through) + next_state = WB; + else if ((proc_gen_bus_if.ren || proc_gen_bus_if.wen) && ~hit && ~sramRead.frames[ridx].dirty && ~pass_through) + next_state = FETCH; + if (flush || flush_req) + next_state = FLUSH_CACHE; + end + FETCH: begin + if (mem_gen_bus_if.error || decoded_addr != decoded_req_addr || !(proc_gen_bus_if.ren || proc_gen_bus_if.wen)) + next_state = HIT; + else if (word_count_done) + next_state = HIT; + end + WB: begin + if (mem_gen_bus_if.error || decoded_addr != decoded_req_addr || !(proc_gen_bus_if.ren || proc_gen_bus_if.wen)) + next_state = HIT; + else if (word_count_done) + next_state = FETCH; + end + FLUSH_CACHE: begin + if (flush_done) + next_state = HIT; + end + endcase + end + + // flush saver + always_comb begin + nflush_req = flush_req; + if (flush) + nflush_req = 1; + if (state == FLUSH_CACHE) + nflush_req = 0; + end + +endmodule diff --git a/source_code/caches/l1/l1_wrapper.sv b/source_code/caches/l1/l1_wrapper.sv new file mode 100644 index 000000000..ea41ce7b3 --- /dev/null +++ b/source_code/caches/l1/l1_wrapper.sv @@ -0,0 +1,12 @@ + + +module l1_wrapper(); + + generic_bus_if mem_gen_bus_if(); + generic_bus_if proc_gen_bus_if(); + + logic CLK, nRST, clear, flush, clear_done, flush_done; + + l1_cache DUT(.*); + +endmodule diff --git a/source_code/caches/memory_arbiter.sv b/source_code/caches/memory_arbiter.sv new file mode 100644 index 000000000..e3cebb9ef --- /dev/null +++ b/source_code/caches/memory_arbiter.sv @@ -0,0 +1,100 @@ +`include "generic_bus_if.vh" + +module memory_arbiter ( + input CLK, nRST, + generic_bus_if.generic_bus icache_if, dcache_if, + generic_bus_if.cpu mem_arb_if +); + typedef enum logic[1:0] {IDLE, IREQUEST, DREQUEST} state_t; + state_t state, next_state; + + always_comb begin : OUTPUT_LOGIC + mem_arb_if.ren = '0; + mem_arb_if.wen = '0; + mem_arb_if.addr = '0; + mem_arb_if.wdata = '0; + mem_arb_if.byte_en = '0; + + icache_if.busy = '1; + icache_if.rdata = '0; + + dcache_if.busy = '1; + dcache_if.rdata = '0; + + case(state) + IDLE: begin + if(dcache_if.wen || dcache_if.ren) begin + mem_arb_if.ren = dcache_if.ren; + mem_arb_if.wen = dcache_if.wen; + mem_arb_if.addr = dcache_if.addr; + mem_arb_if.wdata = dcache_if.wdata; + mem_arb_if.byte_en = dcache_if.byte_en; + dcache_if.busy = mem_arb_if.busy; + dcache_if.rdata = mem_arb_if.rdata; + end + else if(icache_if.wen || icache_if.ren) begin + mem_arb_if.ren = icache_if.ren; + mem_arb_if.wen = icache_if.wen; + mem_arb_if.addr = icache_if.addr; + mem_arb_if.wdata = icache_if.wdata; + mem_arb_if.byte_en = icache_if.byte_en; + icache_if.busy = mem_arb_if.busy; + icache_if.rdata = mem_arb_if.rdata; + end + end + IREQUEST: begin + mem_arb_if.ren = icache_if.ren; + mem_arb_if.wen = icache_if.wen; + mem_arb_if.addr = icache_if.addr; + mem_arb_if.wdata = icache_if.wdata; + mem_arb_if.byte_en = icache_if.byte_en; + icache_if.busy = mem_arb_if.busy; + icache_if.rdata = mem_arb_if.rdata; + end + DREQUEST: begin + mem_arb_if.ren = dcache_if.ren; + mem_arb_if.wen = dcache_if.wen; + mem_arb_if.addr = dcache_if.addr; + mem_arb_if.wdata = dcache_if.wdata; + mem_arb_if.byte_en = dcache_if.byte_en; + dcache_if.busy = mem_arb_if.busy; + dcache_if.rdata = mem_arb_if.rdata; + end + endcase + end + + always_comb begin : NEXT_STATE_LOGIC + next_state = state; + + case(state) + IDLE: begin + if((dcache_if.wen || dcache_if.ren) && mem_arb_if.busy) begin + next_state = DREQUEST; + end + else if((icache_if.wen || icache_if.ren) && mem_arb_if.busy) begin + next_state = IREQUEST; + end + end + DREQUEST: begin + if(~mem_arb_if.busy) begin // hopefully, busy will always be high until fetch, so no problem + next_state = IDLE; + end + end + IREQUEST: begin + if(~mem_arb_if.busy) begin + next_state = IDLE; + end + end + endcase + end + + always_ff @ (posedge CLK, negedge nRST) begin + if(~nRST) begin + state <= IDLE; + end + else begin + state <= next_state; + end + end + +endmodule diff --git a/source_code/caches/pass_through/pass_through_cache.sv b/source_code/caches/pass_through/pass_through_cache.sv index ae910ed97..e8f14e3fc 100644 --- a/source_code/caches/pass_through/pass_through_cache.sv +++ b/source_code/caches/pass_through/pass_through_cache.sv @@ -1,43 +1,45 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: pass_through_cache.sv -* +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 -* Description: Pass Through Cache +* Description: Pass Through Cache */ `include "generic_bus_if.vh" module pass_through_cache ( - input logic CLK, nRST, - generic_bus_if.cpu mem_gen_bus_if, - generic_bus_if.generic_bus proc_gen_bus_if + input logic CLK, + nRST, + generic_bus_if.cpu mem_gen_bus_if, + generic_bus_if.generic_bus proc_gen_bus_if ); - //passthrough layer - assign mem_gen_bus_if.addr = proc_gen_bus_if.addr; - assign mem_gen_bus_if.ren = proc_gen_bus_if.ren; - assign mem_gen_bus_if.wen = proc_gen_bus_if.wen; - assign mem_gen_bus_if.wdata = proc_gen_bus_if.wdata; - assign mem_gen_bus_if.byte_en = proc_gen_bus_if.byte_en; + //passthrough layer + assign mem_gen_bus_if.addr = proc_gen_bus_if.addr; + assign mem_gen_bus_if.ren = proc_gen_bus_if.ren; + assign mem_gen_bus_if.wen = proc_gen_bus_if.wen; + assign mem_gen_bus_if.wdata = proc_gen_bus_if.wdata; + assign mem_gen_bus_if.byte_en = proc_gen_bus_if.byte_en; - assign proc_gen_bus_if.rdata = mem_gen_bus_if.rdata; - assign proc_gen_bus_if.busy = mem_gen_bus_if.busy; + assign proc_gen_bus_if.rdata = mem_gen_bus_if.rdata; + assign proc_gen_bus_if.busy = mem_gen_bus_if.busy; + assign proc_gen_bus_if.error = mem_gen_bus_if.error; endmodule diff --git a/source_code/caches/separate_caches.sv b/source_code/caches/separate_caches.sv index 3b47953e9..92ee0e420 100644 --- a/source_code/caches/separate_caches.sv +++ b/source_code/caches/separate_caches.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 11/08/2016 -* Description: Caches consisting of separate I$ and D$ +* Description: Caches consisting of separate I$ and D$ */ `include "generic_bus_if.vh" @@ -27,61 +27,102 @@ `include "component_selection_defines.vh" module separate_caches ( - input logic CLK, nRST, - generic_bus_if.cpu icache_mem_gen_bus_if, - generic_bus_if.cpu dcache_mem_gen_bus_if, - generic_bus_if.generic_bus icache_proc_gen_bus_if, - generic_bus_if.generic_bus dcache_proc_gen_bus_if, - cache_control_if cc_if + input logic CLK, + nRST, + generic_bus_if.cpu icache_mem_gen_bus_if, + generic_bus_if.cpu dcache_mem_gen_bus_if, + generic_bus_if.generic_bus icache_proc_gen_bus_if, + generic_bus_if.generic_bus dcache_proc_gen_bus_if, + cache_control_if.caches cc_if ); - generate - case (DCACHE_TYPE) - "pass_through" : begin - pass_through_cache dcache( - .CLK(CLK), - .nRST(nRST), - .mem_gen_bus_if(dcache_mem_gen_bus_if), - .proc_gen_bus_if(dcache_proc_gen_bus_if) - ); - assign cc_if.dclear_done = 1'b1; - assign cc_if.dflush_done = 1'b1; - end - "direct_mapped_tpf" : direct_mapped_tpf_cache dcache( - .CLK(CLK), - .nRST(nRST), - .mem_gen_bus_if(dcache_mem_gen_bus_if), - .proc_gen_bus_if(dcache_proc_gen_bus_if), - .flush(cc_if.dcache_flush), - .clear(cc_if.dcache_clear), - .flush_done(cc_if.dflush_done), - .clear_done(cc_if.dclear_done) - ); - endcase - endgenerate + generate + /* verilator lint_off width */ + case (DCACHE_TYPE) + /* verilator lint_on width */ + "pass_through": begin : g_dcache_passthrough + pass_through_cache dcache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(dcache_mem_gen_bus_if), + .proc_gen_bus_if(dcache_proc_gen_bus_if) + ); + assign cc_if.dclear_done = 1'b1; + assign cc_if.dflush_done = 1'b1; + end + "direct_mapped_tpf": + direct_mapped_tpf_cache dcache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(dcache_mem_gen_bus_if), + .proc_gen_bus_if(dcache_proc_gen_bus_if), + .flush(cc_if.dcache_flush), + .clear(cc_if.dcache_clear), + .flush_done(cc_if.dflush_done), + .clear_done(cc_if.dclear_done) + ); + "l1": + l1_cache #( + .CACHE_SIZE(DCACHE_SIZE), + .BLOCK_SIZE(DCACHE_BLOCK_SIZE), + .ASSOC(DCACHE_ASSOC), + .NONCACHE_START_ADDR(NONCACHE_START_ADDR) + ) + dcache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(dcache_mem_gen_bus_if), + .proc_gen_bus_if(dcache_proc_gen_bus_if), + .flush(cc_if.dcache_flush), + .clear(cc_if.dcache_clear), + .flush_done(cc_if.dflush_done), + .clear_done(cc_if.dclear_done) + ); + endcase + endgenerate - generate - case (ICACHE_TYPE) - "pass_through" : begin - pass_through_cache icache( - .CLK(CLK), - .nRST(nRST), - .mem_gen_bus_if(icache_mem_gen_bus_if), - .proc_gen_bus_if(icache_proc_gen_bus_if) - ); - assign cc_if.iclear_done = 1'b1; - assign cc_if.iflush_done = 1'b1; - end - "direct_mapped_tpf" : direct_mapped_tpf_cache icache( - .CLK(CLK), - .nRST(nRST), - .mem_gen_bus_if(icache_mem_gen_bus_if), - .proc_gen_bus_if(icache_proc_gen_bus_if), - .flush(cc_if.icache_flush), - .clear(cc_if.icache_clear), - .flush_done(cc_if.iflush_done), - .clear_done(cc_if.iclear_done) - ); - endcase - endgenerate + generate + /* verilator lint_off width */ + case (ICACHE_TYPE) + /* verilator lint_on width */ + "pass_through": begin : g_icache_passthrough + pass_through_cache icache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(icache_mem_gen_bus_if), + .proc_gen_bus_if(icache_proc_gen_bus_if) + ); + assign cc_if.iclear_done = 1'b1; + assign cc_if.iflush_done = 1'b1; + end + "direct_mapped_tpf": + direct_mapped_tpf_cache icache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(icache_mem_gen_bus_if), + .proc_gen_bus_if(icache_proc_gen_bus_if), + .flush(cc_if.icache_flush), + .clear(cc_if.icache_clear), + .flush_done(cc_if.iflush_done), + .clear_done(cc_if.iclear_done) + ); + "l1": + l1_cache #( + .CACHE_SIZE(ICACHE_SIZE), + .BLOCK_SIZE(ICACHE_BLOCK_SIZE), + .ASSOC(ICACHE_ASSOC), + .NONCACHE_START_ADDR(NONCACHE_START_ADDR) + ) + icache ( + .CLK(CLK), + .nRST(nRST), + .mem_gen_bus_if(icache_mem_gen_bus_if), + .proc_gen_bus_if(icache_proc_gen_bus_if), + .flush(cc_if.icache_flush), + .clear(cc_if.icache_clear), + .flush_done(cc_if.iflush_done), + .clear_done(cc_if.iclear_done) + ); + endcase + endgenerate endmodule diff --git a/source_code/caches/sram/run_sram_test.sh b/source_code/caches/sram/run_sram_test.sh new file mode 100755 index 000000000..33303f3d1 --- /dev/null +++ b/source_code/caches/sram/run_sram_test.sh @@ -0,0 +1,2 @@ +vlog -sv -work work *.sv +incdir+../../include +vsim -c -voptargs="+acc" work.sram_tb -do "do wave.do; run -all" diff --git a/source_code/caches/sram/sram.sv b/source_code/caches/sram/sram.sv new file mode 100644 index 000000000..feee4d519 --- /dev/null +++ b/source_code/caches/sram/sram.sv @@ -0,0 +1,54 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sram.sv +* +* Created by: Jimmy Mingze Jin +* Email: jin357@purdue.edu +* Date Created: 01/29/2023 +* Description: SRAM dummy module +* - w/r size in bits +*/ + +module sram #( + parameter SRAM_WR_SIZE = 128, + parameter SRAM_HEIGHT = 128 +) +( + input logic CLK, nRST, + input logic [SRAM_WR_SIZE-1:0] wVal, + output logic [SRAM_WR_SIZE-1:0] rVal, + input logic REN, WEN, + input logic [$clog2(SRAM_HEIGHT)-1:0] SEL, + input logic [SRAM_WR_SIZE-1:0] wMask +); + typedef logic [SRAM_WR_SIZE-1:0] sram_entry_size_t; + sram_entry_size_t [SRAM_HEIGHT-1:0] sramMemory; + sram_entry_size_t [SRAM_HEIGHT-1:0] n_sramMemory; + + always_ff @(posedge CLK) begin + sramMemory <= n_sramMemory; + end + + always_comb begin + n_sramMemory = sramMemory; + rVal = 32'hBAD0BAD0; + if (WEN) + n_sramMemory[SEL] = (wVal & ~wMask) | (wMask & sramMemory[SEL]); + if (REN) + rVal = sramMemory[SEL]; + end +endmodule \ No newline at end of file diff --git a/source_code/caches/sram/sram_tb.sv b/source_code/caches/sram/sram_tb.sv new file mode 100644 index 000000000..891b5a9ff --- /dev/null +++ b/source_code/caches/sram/sram_tb.sv @@ -0,0 +1,96 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sram_tb.sv +* +* Created by: Jimmy Mingze Jin +* Email: jin357@purdue.edu +* Date Created: 01/29/2023 +* Description: basic sram tb +*/ + +// setup +localparam CLK_PERIOD = 10; +localparam TB_SRAM_WR_SIZE = 128; +localparam TB_SRAM_HEIGHT = 128; +localparam TB_IS_BIDIRECTIONAL = 0; +typedef logic [TB_SRAM_WR_SIZE-1:0] sram2_entry_size_t; // is this legal + +`timescale 1ns/10ps +module sram_tb(); + // CLK/nRST + logic CLK = 0, nRST = 1; + always #(CLK_PERIOD/2) CLK++; + + + + // tb vars + sram2_entry_size_t wVal, rVal; + logic REN, WEN; + logic [$clog2(TB_SRAM_HEIGHT):0] SEL; + + // DUT instance. + sram #(.SRAM_WR_SIZE(TB_SRAM_WR_SIZE), + .SRAM_HEIGHT(TB_SRAM_HEIGHT), + .IS_BIDIRECTIONAL(TB_IS_BIDIRECTIONAL)) + DUT (CLK, nRST, wVal, rVal, REN, WEN, SEL); + + task reset_dut; + @(negedge CLK) nRST = 1'b0; + #(CLK_PERIOD * 2) nRST = 1'b1; + @(posedge CLK); + endtask + + initial begin + // set input signals. + REN = 0; + WEN = 0; + SEL = 0; + wVal = 0; + $timeformat(-9, 0, " ns", 20); + reset_dut(); + #(CLK_PERIOD); + + // try to fill data + for (SEL = 0; SEL < TB_SRAM_HEIGHT; SEL++) begin + wVal = 1 + SEL; + WEN = 1; + #(CLK_PERIOD * 2); + WEN = 0; + #(CLK_PERIOD); + end + + #(CLK_PERIOD); + + // try to read in order + for (SEL = 0; SEL < TB_SRAM_HEIGHT; SEL++) begin + REN = 1; + #(CLK_PERIOD * 1.5); + if (rVal != SEL + 1) + $display("expected %d but got %d at time %4t", SEL + 1, rVal, $time); + else + $display("correctly got %d at time %4t", rVal, $time); + + #(CLK_PERIOD * 0.5); + REN = 0; + #(CLK_PERIOD); + end + + #(CLK_PERIOD); + + $finish; + end +endmodule \ No newline at end of file diff --git a/source_code/caches/wscript b/source_code/caches/wscript deleted file mode 100644 index 76cc3a017..000000000 --- a/source_code/caches/wscript +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(pass_through) - cnf.recurse(direct_mapped_tpf) - -def sim_source(cnf): - cnf.recurse(pass_through) - cnf.recurse(direct_mapped_tpf) diff --git a/source_code/fpga/RISCVBusiness_fpga.sv b/source_code/fpga/RISCVBusiness_fpga.sv index b9c331fc9..a3c36353c 100644 --- a/source_code/fpga/RISCVBusiness_fpga.sv +++ b/source_code/fpga/RISCVBusiness_fpga.sv @@ -103,7 +103,7 @@ module RISCVBusiness_fpga assign HEX6 = ~display.hex6; assign HEX7 = ~display.hex7; - generate + generate genvar seg_select; for(seg_select=0; seg_select < 8; seg_select= seg_select + 1) begin: seven_seg_display_controller diff --git a/source_code/include/control_unit_if.vh b/source_code/include/control_unit_if.vh index cb2e32772..74a443da6 100644 --- a/source_code/include/control_unit_if.vh +++ b/source_code/include/control_unit_if.vh @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,20 +29,22 @@ interface control_unit_if; import alu_types_pkg::*; import rv32i_types_pkg::*; - import machine_mode_types_1_11_pkg::*; + import machine_mode_types_1_12_pkg::*; + import rv32m_pkg::*; - logic dwen, dren, j_sel, branch, jump, ex_pc_sel, imm_shamt_sel, halt, wen, ifence; + logic dwen, dren, j_sel, branch, jump, ex_pc_sel, imm_shamt_sel, halt, wen, ifence, wfi; aluop_t alu_op; logic [1:0] alu_a_sel, alu_b_sel; logic [2:0] w_sel; logic [4:0] shamt; + logic [4:0] rd; logic [11:0] imm_I, imm_S; logic [20:0] imm_UJ; logic [12:0] imm_SB; word_t instr, imm_U; load_t load_type; branch_t branch_type; - opcode_t opcode; + opcode_t opcode; // Privilege control signals logic fault_insn, illegal_insn, ret_insn, breakpoint, ecall_insn; @@ -50,16 +52,18 @@ interface control_unit_if; csr_addr_t csr_addr; logic [4:0] zimm; + // Extension control signals + rv32m_decode_t rv32m_control; + modport control_unit( - input instr, + input instr, output dwen, dren, j_sel, branch, jump, ex_pc_sel, alu_a_sel, alu_b_sel, w_sel, load_type, branch_type, shamt, - imm_I, imm_S, imm_SB, imm_UJ, imm_U, imm_shamt_sel, alu_op, - opcode, halt, wen, fault_insn, illegal_insn, ret_insn, breakpoint, + imm_I, imm_S, imm_SB, imm_UJ, imm_U, imm_shamt_sel, alu_op, + opcode, halt, wen, fault_insn, illegal_insn, ret_insn, breakpoint, ecall_insn, csr_swap, csr_set, csr_clr, csr_imm, csr_rw_valid, - csr_addr, zimm, ifence + csr_addr, zimm, ifence, wfi, rd, rv32m_control ); endinterface `endif - diff --git a/source_code/include/core_interrupt_if.vh b/source_code/include/core_interrupt_if.vh new file mode 100644 index 000000000..53558945c --- /dev/null +++ b/source_code/include/core_interrupt_if.vh @@ -0,0 +1,26 @@ +`ifndef CORE_INTERRUPT_IF +`define CORE_INTERRUPT_IF + +interface core_interrupt_if (); + logic ext_int, ext_int_clear; + logic soft_int, soft_int_clear; + logic timer_int, timer_int_clear; + + modport core( + input ext_int, soft_int, timer_int, + ext_int_clear, soft_int_clear, timer_int_clear + ); + + modport plic( + output ext_int, ext_int_clear + ); + + modport clint( + output soft_int_clear, timer_int_clear, + soft_int, timer_int + ); + +endinterface + + +`endif diff --git a/source_code/include/decompressor_if.vh b/source_code/include/decompressor_if.vh new file mode 100644 index 000000000..0646f161e --- /dev/null +++ b/source_code/include/decompressor_if.vh @@ -0,0 +1,21 @@ +`ifndef DECOMPRESSOR_IF_VH +`define DECOMPRESSOR_IF_VH + +interface decompressor_if(); + import rv32i_types_pkg::word_t; + word_t inst32; + logic [15:0] inst16; + logic edit_rs1, edit_rs2, edit_rd, c_ena; + + modport dcpr ( + input inst16, + output inst32, c_ena, edit_rs1, edit_rs2, edit_rd + ); + + modport cu ( + input edit_rs1, edit_rs2, edit_rd, c_ena + ); + +endinterface + +`endif //DECOMPRESSOR_IF_VH diff --git a/source_code/include/fetch_buffer_if.vh b/source_code/include/fetch_buffer_if.vh new file mode 100644 index 000000000..84c6f2514 --- /dev/null +++ b/source_code/include/fetch_buffer_if.vh @@ -0,0 +1,18 @@ +`ifndef FETCH_BUFFER_IF_VH +`define FETCH_BUFFER_IF_VH + +interface fetch_buffer_if(); + import rv32i_types_pkg::word_t; + + + word_t inst, reset_pc, nextpc, imem_pc, result, reset_pc_val; + logic reset_en, inst_arrived, pc_update, done, done_earlier, done_earlier_send, ex_busy; + + modport fb ( + input inst, reset_en, reset_pc, inst_arrived, pc_update, ex_busy, reset_pc_val, + output done, nextpc, imem_pc, result, done_earlier, done_earlier_send + ); + +endinterface + +`endif //FETCH_BUFFER_IF_VH diff --git a/source_code/include/generic_bus_if.vh b/source_code/include/generic_bus_if.vh index 9634f7e44..31d84e850 100644 --- a/source_code/include/generic_bus_if.vh +++ b/source_code/include/generic_bus_if.vh @@ -33,15 +33,16 @@ interface generic_bus_if (); word_t rdata; logic ren,wen; logic busy; + logic error; logic [3:0] byte_en; modport generic_bus ( input addr, ren, wen, wdata, byte_en, - output rdata, busy + output rdata, busy, error ); modport cpu ( - input rdata, busy, + input rdata, busy, error, output addr, ren, wen, wdata, byte_en ); diff --git a/source_code/include/include.core b/source_code/include/include.core new file mode 100644 index 000000000..2b9b40ad2 --- /dev/null +++ b/source_code/include/include.core @@ -0,0 +1,37 @@ +CAPI=2: +name: socet:riscv:riscv_include:0.1.0 +description: include files + +filesets: + rtl: + files: + - cache_control_if.vh : {is_include_file: true} + - component_selection_defines.vh : {is_include_file: true} + - risc_mgmt_if.vh : {is_include_file: true} + - generic_bus_if.vh : {is_include_file: true} + - rv32c_if.vh : {is_include_file: true} + - risc_mgmt_decode_if.vh : {is_include_file: true} + - risc_mgmt_execute_if.vh : {is_include_file: true} + - risc_mgmt_macros.vh : {is_include_file: true} + - risc_mgmt_memory_if.vh : {is_include_file: true} + - tspp_fetch_execute_if.vh : {is_include_file: true} + - tspp_hazard_unit_if.vh : {is_include_file: true} + - control_unit_if.vh : {is_include_file: true} + - jump_calc_if.vh : {is_include_file: true} + - priv_1_12_internal_if.vh : {is_include_file: true} + - priv_ext_if.vh : {is_include_file: true} + - prv_pipeline_if.vh : {is_include_file: true} + - rv32i_reg_file_if.vh : {is_include_file: true} + - predictor_pipeline_if.vh : {is_include_file: true} + - alu_if.vh : {is_include_file: true} + - branch_res_if.vh : {is_include_file: true} + - core_interrupt_if.vh : {is_include_file: true} + - decompressor_if.vh : {is_include_file: true} + - fetch_buffer_if.vh : {is_include_file: true} + - sparce_pipeline_if.vh : {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl diff --git a/source_code/include/priv_1_11_internal_if.vh b/source_code/include/priv_1_11_internal_if.vh index c20177331..8c3f01622 100644 --- a/source_code/include/priv_1_11_internal_if.vh +++ b/source_code/include/priv_1_11_internal_if.vh @@ -27,66 +27,87 @@ `include "component_selection_defines.vh" -interface priv_1_11_internal_if; +interface priv_1_11_internal_if; // also labeled as prv_intern_if in most modules import machine_mode_types_1_11_pkg::*; import rv32i_types_pkg::*; - logic mip_rup; - logic mtval_rup; - logic mcause_rup; - logic mepc_rup; - logic mstatus_rup; - logic clear_timer_int; - logic intr; - logic pipe_clear; - logic ret; - logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s; - logic breakpoint, env_m, timer_int, soft_int, ext_int; - logic insert_pc; - logic swap, clr, set; - logic valid_write, invalid_csr; - logic instr_retired; + // Machine registers are being ruptured (activated) to denote when to change the value of this register for hardware + logic mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup; + logic intr; // denote whether an exception or interrupt register + logic pipe_clear; // e_ex_stage is where you check what type of hazard unit instruction you are receiving. Simply, checking whether or not the pipeline is clear of any hazards + logic mret, sret, uret; //returns after handling a trap instruction + + + // sources for interrupts + logic timer_int_u, timer_int_s, timer_int_m; + logic soft_int_u, soft_int_s, soft_int_m; + logic ext_int_u, ext_int_s, ext_int_m; + logic reserved_0, reserved_1, reserved_2; + + // signals to clear the pending interrupt + logic clear_timer_int_u, clear_timer_int_s, clear_timer_int_m; + logic clear_soft_int_u, clear_soft_int_s, clear_soft_int_m; + logic clear_ext_int_u, clear_ext_int_s, clear_ext_int_m; + + + // sources for exceptions + logic mal_insn, fault_insn_access, illegal_insn, breakpoint, fault_l, mal_l, fault_s, mal_s; + logic env_u, env_s, env_m, fault_insn_page, fault_load_page, fault_store_page; + + logic insert_pc; // inform pipeline that the pc will need to be changed. either when an instruction is a ret instruction, or pipeline is clear and a proper instruction + logic swap, clr, set; // activated for CSR Assembly instructions + + + logic valid_write, invalid_csr; // valid write occurs with an r type instruction that does not have any pipeline stalls; invalid_csr + logic instr_retired; // instruction is done (retired) when there is a write back enable and there is a proper instruction // RISC-MGMT logic ex_rmgmt; logic [$clog2(`NUM_EXTENSIONS)-1:0] ex_rmgmt_cause; - word_t epc, mtval, priv_pc; - word_t [3:0] xtvec, xepc_r; + word_t epc; // pc of the instruction prior to the exception + word_t priv_pc; // pc that would need to be changed for pipeline word_t wdata, rdata; + word_t mtval; mip_t mip, mip_next; mtval_t mtval_next; mcause_t mcause, mcause_next; - mepc_t mepc, mepc_next; + mepc_t mepc, mepc_next; // holds pc of interrupted instruction mstatus_t mstatus, mstatus_next; mtvec_t mtvec; mie_t mie; - csr_addr_t addr; + csr_addr_t addr; // 12-bit address for CSR instructions modport csr ( input mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup, mip_next, mtval_next, mcause_next, mepc_next, mstatus_next, swap, clr, set, wdata, addr, valid_write, instr_retired, - output mtvec, mepc, mie, timer_int, mip, mcause, mstatus, clear_timer_int, - rdata, invalid_csr, xtvec, xepc_r + output mtvec, mepc, mie, mip, mcause, mstatus, + rdata, invalid_csr ); modport prv_control ( output mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup, mip_next, mcause_next, mepc_next, mstatus_next, mtval_next, intr, - input mepc, mie, mip, mcause, mstatus, clear_timer_int, pipe_clear, ret, - epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m, timer_int, soft_int, ext_int, mtval, ex_rmgmt, - ex_rmgmt_cause + input mepc, mie, mip, mcause, mstatus, clear_timer_int_m, clear_ext_int_m, clear_soft_int_m, + clear_timer_int_u, clear_ext_int_u, clear_soft_int_u, clear_timer_int_s, clear_ext_int_s, + clear_soft_int_s, pipe_clear, mret, epc, fault_insn_access, mal_insn, illegal_insn, fault_l, + mal_l, fault_s, mal_s, breakpoint, env_m, env_s, env_u, fault_insn_page, fault_load_page, + fault_store_page, timer_int_u, timer_int_s, timer_int_m, soft_int_u, soft_int_s, soft_int_m, + ext_int_u, ext_int_s, ext_int_m, mtval, ex_rmgmt, ex_rmgmt_cause ); modport pipe_ctrl ( - input intr, ret, pipe_clear, xtvec, xepc_r, + input intr, mret, pipe_clear, mtvec, mcause, mepc, output insert_pc, priv_pc ); + modport tb ( + output ext_int_m, clear_ext_int_m + ); + endinterface `endif // PRIV_1_11_INTERNAL_IF_VH diff --git a/source_code/include/priv_1_12_internal_if.vh b/source_code/include/priv_1_12_internal_if.vh new file mode 100644 index 000000000..c2de35802 --- /dev/null +++ b/source_code/include/priv_1_12_internal_if.vh @@ -0,0 +1,133 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_internal_if.vh +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/27/2022 +* Description: Interface for components within the privilege block v1.12 +*/ + +`ifndef PRIV_1_12_INTERNAL_IF_VH +`define PRIV_1_12_INTERNAL_IF_VH + +`include "component_selection_defines.vh" + +interface priv_1_12_internal_if; + import machine_mode_types_1_12_pkg::*; + import pma_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + // RISC-MGMT? + // not sure what these are for, part of priv 1.11 + logic ex_rmgmt; + logic [$clog2(`NUM_EXTENSIONS)-1:0] ex_rmgmt_cause; + + priv_level_t curr_privilege_level; // Current process privilege + + // CSR block values + csr_addr_t csr_addr; // CSR address to read + logic csr_write, csr_set, csr_clear, csr_read_only; // Is the CSR currently being modified? + logic invalid_csr; // Bad CSR address + logic inst_ret; // signal when an instruction is retired + word_t new_csr_val, old_csr_val; // new and old CSR values (atomically swapped) + logic valid_write; // valid write occurs with an r type instruction that does not have any pipeline stalls + + // Sources for interrupts + logic timer_int_u, timer_int_s, timer_int_m; + logic soft_int_u, soft_int_s, soft_int_m; + logic ext_int_u, ext_int_s, ext_int_m; + + // Sources to clear the pending interrupt + logic clear_timer_int_u, clear_timer_int_s, clear_timer_int_m; + logic clear_soft_int_u, clear_soft_int_s, clear_soft_int_m; + logic clear_ext_int_u, clear_ext_int_s, clear_ext_int_m; + + // Sources for exceptions + logic mal_insn, fault_insn_access, illegal_insn, breakpoint, fault_l, mal_l, fault_s, mal_s; + logic env_u, env_s, env_m, fault_insn_page, fault_load_page, fault_store_page; + + // Values involving CSR file for ex/int handling + mip_t curr_mip, next_mip; + mie_t curr_mie, next_mie; + mcause_t curr_mcause, next_mcause; + csr_reg_t curr_mepc, next_mepc; + mstatus_t curr_mstatus, next_mstatus; + mtvec_t curr_mtvec; + csr_reg_t curr_mtval, next_mtval; + logic inject_mip, inject_mcause, inject_mepc, inject_mstatus, inject_mtval; + + // Things from the pipe we care about + word_t epc; // pc of the instruction prior to the exception + word_t priv_pc; // pc to handle the interrupt/exception + logic pipe_clear; // is the pipeline clear of hazards + logic insert_pc; // inform pipeline that we are changing the PC + logic mret, sret; // returning from a trap instruction + logic intr; // Did something trigger an interrupt or exception? + + // Addresses and memory access info for memory protection + logic [RAM_ADDR_SIZE-1:0] daddr, iaddr; // Address to check + logic ren, wen, xen; // RWX access type + + // PMA comm variables + pma_accwidth_t d_acc_width, i_acc_width; // Width of memory access + logic pma_s_fault, pma_l_fault, pma_i_fault; // PMA store fault, load fault, instruction fault + + // PMP variables + logic pmp_s_fault, pmp_l_fault, pmp_i_fault; // PMP store fault, load fault, instruction fault + + modport csr ( + input csr_addr, curr_privilege_level, csr_write, csr_set, csr_clear, csr_read_only, new_csr_val, inst_ret, valid_write, + inject_mcause, inject_mepc, inject_mip, inject_mstatus, inject_mtval, + next_mcause, next_mepc, next_mie, next_mip, next_mstatus, next_mtval, + output old_csr_val, invalid_csr, + curr_mcause, curr_mepc, curr_mie, curr_mip, curr_mstatus, curr_mtvec + ); + + modport int_ex_handler ( + input timer_int_u, timer_int_s, timer_int_m, soft_int_u, soft_int_s, soft_int_m, ext_int_u, ext_int_s, ext_int_m, + clear_timer_int_u, clear_timer_int_s, clear_timer_int_m, clear_soft_int_u, clear_soft_int_s, clear_soft_int_m, + clear_ext_int_u, clear_ext_int_s, clear_ext_int_m, mal_insn, fault_insn_access, illegal_insn, breakpoint, fault_l, mal_l, fault_s, mal_s, + env_u, env_s, env_m, fault_insn_page, fault_load_page, fault_store_page, curr_mcause, curr_mepc, curr_mie, curr_mip, curr_mstatus, curr_mtval, + mret, sret, pipe_clear, ex_rmgmt, ex_rmgmt_cause, epc, curr_privilege_level, + output inject_mcause, inject_mepc, inject_mip, inject_mstatus, inject_mtval, + next_mcause, next_mepc, next_mie, next_mip, next_mstatus, next_mtval, intr + ); + + modport pipe_ctrl ( + input intr, pipe_clear, mret, sret, curr_mtvec, curr_mepc, next_mcause, + output insert_pc, priv_pc + ); + + modport pma ( + input iaddr, daddr, ren, wen, xen, d_acc_width, i_acc_width, + output pma_s_fault, pma_i_fault, pma_l_fault + ); + + modport pmp ( + input iaddr, daddr, ren, wen, xen, curr_privilege_level, curr_mstatus, + output pmp_s_fault, pmp_i_fault, pmp_l_fault + ); + + modport mode ( + input mret, curr_mstatus, intr, + output curr_privilege_level + ); + +endinterface + +`endif // PRIV_1_12_INTERNAL_IF_VH diff --git a/source_code/include/priv_ext_if.vh b/source_code/include/priv_ext_if.vh new file mode 100644 index 000000000..836b7e63a --- /dev/null +++ b/source_code/include/priv_ext_if.vh @@ -0,0 +1,58 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: prv_ext_if.vh +* +* Created by: Hadi AHmed +* Email: ahmed138@purdue.edu +* Date Created: 10/36/2022 +* Description: Interface connecting the main CSR file to other extension + CSR files so they can handle their state independently. +*/ + +`ifndef PRIV_EXT_IF_VH +`define PRIV_EXT_IF_VH + +interface priv_ext_if(); +import machine_mode_types_1_12_pkg::*; +import rv32i_types_pkg::*; + +// from Priv-CSR to Ext-CSR +csr_addr_t csr_addr; // CSR address +word_t value_in; // New CSR value +logic csr_active; // active CSR operation + +// from Ext-CSR to Priv-CSR +logic invalid_csr, ack; // invalid_csr: error signal when processing CSR, ack: csr_addr belongs to extension +word_t value_out; // Old CSR value + +// Extensions must ALWAYS make sure they drive 'ack' high if 'csr_addr' belongs to them and that +// 'value_out' is the value of the CSR at 'csr_addr'. Only perform a write on 'csr_active', but +// the privileged unit will need the other values to calculate 'value_in'. + +modport priv ( + input invalid_csr, ack, value_out, + output csr_addr, value_in, csr_active +); + +modport ext ( + output invalid_csr, ack, value_out, + input csr_addr, value_in, csr_active +); + +endinterface + +`endif // PRIV_EXT_IF_VH \ No newline at end of file diff --git a/source_code/include/prv_pipeline_if.vh b/source_code/include/prv_pipeline_if.vh index 40c0bfda5..a04de0dd9 100644 --- a/source_code/include/prv_pipeline_if.vh +++ b/source_code/include/prv_pipeline_if.vh @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,9 +20,6 @@ * Email: jskubic@purdue.edu * Date Created: 08/24/2016 * Description: Interface connecting the priv block to the pipeline. -* Contains connections between modules inside the priv block. -* TODO: These two functionalities should be split into two -* separate interfaces. */ `ifndef PRV_PIPELINE_IF_VH @@ -31,12 +28,13 @@ `include "component_selection_defines.vh" interface prv_pipeline_if(); - import machine_mode_types_1_11_pkg::*; + import machine_mode_types_1_12_pkg::*; import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; // exception signals logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m, ret; + breakpoint, env, ret, wfi; // interrupt signals logic timer_int, soft_int, ext_int; @@ -47,39 +45,52 @@ interface prv_pipeline_if(); word_t [3:0] xtvec, xepc_r; // csr rw - logic swap, clr, set; - logic invalid_csr, valid_write; - csr_addr_t addr; + logic swap, clr, set, read_only; + logic invalid_priv_isn, valid_write; + csr_addr_t csr_addr; word_t rdata, wdata; // performance signals logic wb_enable, instr; - // RISC-MGMT + // RISC-MGMT logic ex_rmgmt; logic [$clog2(`NUM_EXTENSIONS)-1:0] ex_rmgmt_cause; + // Memory protection signals + logic iren, dwen, dren; + logic [RAM_ADDR_SIZE-1:0] iaddr, daddr; + pma_accwidth_t d_acc_width, i_acc_width; + logic prot_fault_s, prot_fault_l, prot_fault_i; + modport hazard ( - input priv_pc, insert_pc, intr, - output pipe_clear, ret, epc, fault_insn, mal_insn, + input priv_pc, insert_pc, intr, prot_fault_s, prot_fault_l, prot_fault_i, + output pipe_clear, ret, epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m, badaddr, wb_enable, + breakpoint, env, wfi, badaddr, wb_enable, ex_rmgmt, ex_rmgmt_cause ); modport pipe ( - output swap, clr, set, wdata, addr, valid_write, instr, - input rdata, invalid_csr + output swap, clr, set, read_only, wdata, csr_addr, valid_write, instr, dren, dwen, daddr, d_acc_width, + input rdata, invalid_priv_isn ); + modport fetch ( + input prot_fault_i, + output iren, iaddr, i_acc_width + ); modport priv_block ( input pipe_clear, ret, epc, fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m, badaddr, swap, clr, set, - wdata, addr, valid_write, wb_enable, instr, + breakpoint, env, badaddr, swap, clr, set, read_only, wfi, + wdata, csr_addr, valid_write, wb_enable, instr, ex_rmgmt, ex_rmgmt_cause, - output priv_pc, insert_pc, intr, rdata, invalid_csr + daddr, iaddr, dren, dwen, iren, + d_acc_width, i_acc_width, + output priv_pc, insert_pc, intr, rdata, invalid_priv_isn, + prot_fault_s, prot_fault_l, prot_fault_i ); endinterface diff --git a/source_code/include/risc_mgmt_if.vh b/source_code/include/risc_mgmt_if.vh index 1722afeb2..c8ef203d8 100644 --- a/source_code/include/risc_mgmt_if.vh +++ b/source_code/include/risc_mgmt_if.vh @@ -69,12 +69,15 @@ interface risc_mgmt_if (); logic exception; logic [`NUM_EXTENSIONS-1:0] ex_cause; + // to stall rv32c + logic [`NUM_EXTENSIONS-1:0] risc_mgmt_start; + modport ts_rmgmt ( output req_reg_r, req_reg_w, rsel_s_0, rsel_s_1, rsel_d, reg_w, reg_wdata, req_br_j, branch_jump, br_j_addr, req_mem, mem_addr, mem_store, mem_ren, mem_wen, mem_byte_en, execute_stall, memory_stall, - active_insn, exception, ex_cause, ex_token, + active_insn, exception, ex_cause, ex_token, risc_mgmt_start, input rdata_s_0, rdata_s_1, mem_load, mem_busy, insn, pc, if_ex_enable ); @@ -92,7 +95,7 @@ interface risc_mgmt_if (); input req_reg_r, req_reg_w, rsel_s_0, rsel_s_1, rsel_d, reg_w, reg_wdata, req_br_j, branch_jump, br_j_addr, req_mem, mem_addr, mem_store, mem_ren, - mem_wen, mem_byte_en, ex_token + mem_wen, mem_byte_en, ex_token, risc_mgmt_start ); modport ts_hazard ( diff --git a/source_code/include/rv32c_if.vh b/source_code/include/rv32c_if.vh new file mode 100644 index 000000000..c2a3d1d5b --- /dev/null +++ b/source_code/include/rv32c_if.vh @@ -0,0 +1,30 @@ +`ifndef RV32C_IF_VH +`define RV32C_IF_VH + +interface rv32c_if(); + + import rv32i_types_pkg::word_t; + + word_t inst, reset_pc, nextpc, imem_pc, result, inst32, reset_pc_val; + logic [15:0] inst16; + logic reset_en, inst_arrived, pc_update, done, c_ena, rv32c_ena, done_earlier, done_earlier_send, halt, ex_busy; + + + modport rv32c ( + input inst, reset_en, reset_pc, inst_arrived, pc_update, inst16, halt, ex_busy, reset_pc_val, + output done, nextpc, imem_pc, result, inst32, c_ena, rv32c_ena, done_earlier, done_earlier_send + ); + + modport fetch ( + input done, nextpc, imem_pc, result, rv32c_ena, done_earlier, done_earlier_send, + output inst, reset_en, reset_pc, inst_arrived, pc_update, reset_pc_val + ); + + modport execute ( + input inst32, c_ena, done_earlier, + output inst16, halt, ex_busy + ); + +endinterface + +`endif //RV32C_IF_VH diff --git a/source_code/include/rv32i_reg_file_if.vh b/source_code/include/rv32i_reg_file_if.vh index 39fc6cb7e..602221a54 100644 --- a/source_code/include/rv32i_reg_file_if.vh +++ b/source_code/include/rv32i_reg_file_if.vh @@ -39,7 +39,7 @@ interface rv32i_reg_file_if(); ); modport cu ( - output rs1, rs2, rd + output rs1, rs2 ); endinterface diff --git a/source_code/include/sparce_internal_if.vh b/source_code/include/sparce_internal_if.vh new file mode 100644 index 000000000..be172ba41 --- /dev/null +++ b/source_code/include/sparce_internal_if.vh @@ -0,0 +1,87 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_internal_if.vh +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/30/2019 +* Description: Interface containing the modports for all the internal +* components of SparCE, including the SVC, SASA Table, +* PSRU, and SpRF +*/ + +`ifndef SPARCE_INTERNAL_IF_VH +`define SPARCE_INTERNAL_IF_VH + + +typedef enum logic { + SASA_COND_OR = 1'b0, + SASA_COND_AND = 1'b1 +} sasa_cond_t; + +interface sparce_internal_if; + + import rv32i_types_pkg::*; + + // SVC + word_t wb_data; + logic is_sparse; + + // SpRF + logic wb_en, rs1_sparsity, rs2_sparsity; + + // SASA Table + word_t pc, sasa_addr, sasa_data, preceding_pc; + logic sasa_wen, valid, sasa_enable; + logic [4:0] sasa_rs1, sasa_rs2, rd; + sasa_cond_t condition; + logic [4:0] insts_to_skip; + + // PSRU + word_t sparce_target; + logic skipping; + + // CFID + logic ctrl_flow_enable; + word_t rdata; + + modport svc ( + output is_sparse, + input wb_data + ); + + modport sprf ( + output rs1_sparsity, rs2_sparsity, + input wb_en, rd, is_sparse, sasa_rs1, sasa_rs2 + ); + + modport sasa_table ( + output sasa_rs1, sasa_rs2, insts_to_skip, preceding_pc, condition, valid, + input pc, sasa_addr, sasa_data, sasa_wen, sasa_enable + ); + + modport psru ( + output skipping, sparce_target, + input valid, insts_to_skip, preceding_pc, condition, rs1_sparsity, rs2_sparsity, ctrl_flow_enable + ); + + modport cfid ( + output ctrl_flow_enable, + input rdata + ); +endinterface +`endif //SPARCE_PIPELINE_IF diff --git a/source_code/include/sparce_pipeline_if.vh b/source_code/include/sparce_pipeline_if.vh new file mode 100644 index 000000000..5b2c83c58 --- /dev/null +++ b/source_code/include/sparce_pipeline_if.vh @@ -0,0 +1,69 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_pipeline_if.vh +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/17/2019 +* Description: Interface between the sparsity optimization unit and the +* rest of the pipeline. Does not include internal signals +* between blocks needed for sparsity optimizations. +*/ + +`ifndef SPARCE_PIPELINE_IF_VH +`define SPARCE_PIPELINE_IF_VH + +interface sparce_pipeline_if; + + import rv32i_types_pkg::*; + + word_t pc, wb_data, sasa_data, sasa_addr; + logic wb_en, sasa_wen; + logic [4:0] rd; + logic if_ex_enable; + + word_t sparce_target, rdata; + logic skipping; + + modport pipeline ( + input sparce_target, skipping, + output pc, wb_data, wb_en, sasa_data, sasa_addr, sasa_wen, rd, if_ex_enable, rdata + ); + + modport pipe_fetch ( + input sparce_target, skipping, + output pc, rdata + ); + + modport pipe_execute ( + input sparce_target, skipping, + output wb_data, wb_en, sasa_data, sasa_addr, sasa_wen, rd + ); + + modport hazard ( + input sparce_target, skipping, + output if_ex_enable + ); + + modport sparce ( + output sparce_target, skipping, + input pc, wb_data, wb_en, sasa_data, sasa_addr, sasa_wen, rd, if_ex_enable, rdata + ); + + +endinterface +`endif //SPARCE_PIPELINE_IF diff --git a/source_code/include/tspp_hazard_unit_if.vh b/source_code/include/tspp_hazard_unit_if.vh index 273bf0def..4e2ca77d5 100644 --- a/source_code/include/tspp_hazard_unit_if.vh +++ b/source_code/include/tspp_hazard_unit_if.vh @@ -34,7 +34,7 @@ interface tspp_hazard_unit_if(); //Pipeline Exceptions logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, - breakpoint, env_m; + breakpoint, env; word_t epc_f, epc_e, badaddr_f, badaddr_e; // TVEC Insertion @@ -44,24 +44,27 @@ interface tspp_hazard_unit_if(); // Pipeline Tokens logic token_ex; + // RV32C + logic rv32c_ready; + modport hazard_unit ( input i_mem_busy, d_mem_busy, dren, dwen, jump, branch, mispredict, halt, pc,fault_insn, mal_insn, illegal_insn, fault_l, - mal_l, fault_s, mal_s, breakpoint, env_m, ret, - epc_f, epc_e, badaddr_f, badaddr_e, token_ex, fence_stall, + mal_l, fault_s, mal_s, breakpoint, env, ret, + epc_f, epc_e, badaddr_f, badaddr_e, token_ex, fence_stall, rv32c_ready, output pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren ); modport fetch ( input pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren, - output i_mem_busy, fault_insn, mal_insn, epc_f, badaddr_f + output i_mem_busy, fault_insn, mal_insn, epc_f, badaddr_f, rv32c_ready ); modport execute ( input if_ex_stall, npc_sel, output d_mem_busy, dren, dwen, jump, branch, mispredict, halt, - illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env_m, ret, epc_e, + illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env, ret, epc_e, badaddr_e, token_ex, fence_stall ); diff --git a/source_code/packages/alu_types_pkg.sv b/source_code/packages/alu_types_pkg.sv index d0fd74a7b..122da19d6 100644 --- a/source_code/packages/alu_types_pkg.sv +++ b/source_code/packages/alu_types_pkg.sv @@ -1,22 +1,22 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: alu_types_pkg.sv -* -* Created by: Jacob R. Stevens +* +* Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 06/01/2016 * Description: Package containing types used in the alu @@ -26,18 +26,18 @@ package alu_types_pkg; - typedef enum logic [3:0] { - ALU_SLL = 4'b0000, - ALU_SRL = 4'b0001, - ALU_SRA = 4'b0010, - ALU_ADD = 4'b0011, - ALU_SUB = 4'b0100, - ALU_AND = 4'b0101, - ALU_OR = 4'b0110, - ALU_XOR = 4'b0111, - ALU_SLT = 4'b1000, - ALU_SLTU = 4'b1001 - } aluop_t; + typedef enum logic [3:0] { + ALU_SLL = 4'b0000, + ALU_SRL = 4'b0001, + ALU_SRA = 4'b0010, + ALU_ADD = 4'b0011, + ALU_SUB = 4'b0100, + ALU_AND = 4'b0101, + ALU_OR = 4'b0110, + ALU_XOR = 4'b0111, + ALU_SLT = 4'b1000, + ALU_SLTU = 4'b1001 + } aluop_t; endpackage `endif diff --git a/source_code/packages/machine_mode_types_1_11_pkg.sv b/source_code/packages/machine_mode_types_1_11_pkg.sv index e3c7b8074..e184e0040 100644 --- a/source_code/packages/machine_mode_types_1_11_pkg.sv +++ b/source_code/packages/machine_mode_types_1_11_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,230 +27,238 @@ package machine_mode_types_1_11_pkg; - typedef enum logic [11:0] { - /* Machine Mode Addresses */ - MVENDORID_ADDR = 12'hF11, - MARCHID_ADDR = 12'hF12, - MIMPID_ADDR = 12'hF13, - MHARTID_ADDR = 12'hF14, + typedef enum logic [11:0] { + /* Machine Mode Addresses */ + MVENDORID_ADDR = 12'hF11, + MARCHID_ADDR = 12'hF12, + MIMPID_ADDR = 12'hF13, + MHARTID_ADDR = 12'hF14, + + MSTATUS_ADDR = 12'h300, + MISA_ADDR = 12'h301, + MEDELEG_ADDR = 12'h302, + MIDELEG_ADDR = 12'h303, + MIE_ADDR = 12'h304, + MTVEC_ADDR = 12'h305, + MCOUNTEREN_ADDR = 12'h306, + + MSCRATCH_ADDR = 12'h340, + MEPC_ADDR = 12'h341, + MCAUSE_ADDR = 12'h342, + MTVAL_ADDR = 12'h343, + MIP_ADDR = 12'h344, + + MCYCLE_ADDR = 12'hB00, + MINSTRET_ADDR = 12'hB02, + MCYCLEH_ADDR = 12'hB80, + MINSTRETH_ADDR = 12'hB82 + } csr_addr_t; + + /* Machine Mode Register Types */ - MSTATUS_ADDR = 12'h300, - MISA_ADDR = 12'h301, - MEDELEG_ADDR = 12'h302, - MIDELEG_ADDR = 12'h303, - MIE_ADDR = 12'h304, - MTVEC_ADDR = 12'h305, - //TODO: MCOUNTEREN AND ASSOCIATED REGS/CONTGROL (e.g. cycle/time) - MCOUNTEREN_ADDR = 12'h306, - - MSCRATCH_ADDR = 12'h340, - MEPC_ADDR = 12'h341, - MCAUSE_ADDR = 12'h342, - MTVAL_ADDR = 12'h343, - MIP_ADDR = 12'h344, + /* misaid types */ - // TODO: MAY BE ABLE TO REMOVE BELOW - MBASE_ADDR = 12'h380, - MBOUND_ADDR = 12'h381, - MIBASE_ADDR = 12'h382, - MIBOUND_ADDR = 12'h383, - MDBASE_ADDR = 12'h384, - MDBOUND_ADDR = 12'h385, - // TODO: MAY BE ABLE TO REMOVE ABOVE - // TODO: BELOW MUST BE REMOVED - MTOHOST_ADDR = 12'h780, - MFROMHOST_ADDR = 12'h781, - HTIMEW_ADDR = 12'hB01, - HTIMEHW_ADDR = 12'hB81, - MTIMECMP_ADDR = 12'h321, - MTIME_ADDR = 12'h701, - MTIMEH_ADDR = 12'h741, - // TODO: ABOVE MUST BE REMOVED + typedef enum logic [1:0] { + BASE_RV32 = 2'h1, + BASE_RV64 = 2'h2, + BASE_RV128 = 2'h3 + } misaid_base_t; - MCYCLE_ADDR = 12'hB00, - MINSTRET_ADDR = 12'hB02, - MCYCLEH_ADDR = 12'hB80, - MINSTRETH_ADDR = 12'hB82 - } csr_addr_t; + typedef struct packed { + misaid_base_t base; + logic [3:0] zero; + logic [25:0] extensions; + } misaid_t; - /* Priv Levels */ - typedef enum logic [1:0] { - U_MODE = 2'b00, - S_MODE = 2'b01, - H_MODE = 2'b10, - M_MODE = 2'b11 - } prv_lvl_t; - - /* Machine Mode Register Types */ + parameter logic [25:0] MISAID_EXT_A = 26'h1 << 0; + parameter logic [25:0] MISAID_EXT_B = 26'h1 << 1; + parameter logic [25:0] MISAID_EXT_C = 26'h1 << 2; + parameter logic [25:0] MISAID_EXT_D = 26'h1 << 3; + parameter logic [25:0] MISAID_EXT_E = 26'h1 << 4; + parameter logic [25:0] MISAID_EXT_F = 26'h1 << 5; + parameter logic [25:0] MISAID_EXT_G = 26'h1 << 6; + parameter logic [25:0] MISAID_EXT_H = 26'h1 << 7; + parameter logic [25:0] MISAID_EXT_I = 26'h1 << 8; + parameter logic [25:0] MISAID_EXT_J = 26'h1 << 9; + parameter logic [25:0] MISAID_EXT_K = 26'h1 << 10; + parameter logic [25:0] MISAID_EXT_L = 26'h1 << 11; + parameter logic [25:0] MISAID_EXT_M = 26'h1 << 12; + parameter logic [25:0] MISAID_EXT_N = 26'h1 << 13; + parameter logic [25:0] MISAID_EXT_O = 26'h1 << 14; + parameter logic [25:0] MISAID_EXT_P = 26'h1 << 15; + parameter logic [25:0] MISAID_EXT_Q = 26'h1 << 16; + parameter logic [25:0] MISAID_EXT_R = 26'h1 << 17; + parameter logic [25:0] MISAID_EXT_S = 26'h1 << 18; + parameter logic [25:0] MISAID_EXT_T = 26'h1 << 19; + parameter logic [25:0] MISAID_EXT_U = 26'h1 << 20; + parameter logic [25:0] MISAID_EXT_V = 26'h1 << 21; + parameter logic [25:0] MISAID_EXT_W = 26'h1 << 22; + parameter logic [25:0] MISAID_EXT_X = 26'h1 << 23; + parameter logic [25:0] MISAID_EXT_Y = 26'h1 << 24; + parameter logic [25:0] MISAID_EXT_Z = 26'h1 << 25; - /* misaid types */ + /* mstatus types */ - typedef enum logic [1:0] { - BASE_RV32 = 2'h1, - BASE_RV64 = 2'h2, - BASE_RV128 = 2'h3 - } misaid_base_t; + typedef enum logic [1:0] { + FS_OFF = 2'h0, + FS_INITIAL = 2'h1, + FS_CLEAN = 2'h2, + FS_DIRTY = 2'h3 + } fs_t; - typedef struct packed { - misaid_base_t base; - logic [3:0] zero; - logic [25:0] extensions; - } misaid_t; + typedef enum logic [1:0] { + XS_ALL_OFF = 2'h0, + XS_NONE_DC = 2'h1, + XS_NONE_D = 2'h2, + XS_SOME_D = 2'h3 + } xs_t; - parameter MISAID_EXT_A = 26'h1 << 0; - parameter MISAID_EXT_B = 26'h1 << 1; - parameter MISAID_EXT_C = 26'h1 << 2; - parameter MISAID_EXT_D = 26'h1 << 3; - parameter MISAID_EXT_E = 26'h1 << 4; - parameter MISAID_EXT_F = 26'h1 << 5; - parameter MISAID_EXT_G = 26'h1 << 6; - parameter MISAID_EXT_H = 26'h1 << 7; - parameter MISAID_EXT_I = 26'h1 << 8; - parameter MISAID_EXT_J = 26'h1 << 9; - parameter MISAID_EXT_K = 26'h1 << 10; - parameter MISAID_EXT_L = 26'h1 << 11; - parameter MISAID_EXT_M = 26'h1 << 12; - parameter MISAID_EXT_N = 26'h1 << 13; - parameter MISAID_EXT_O = 26'h1 << 14; - parameter MISAID_EXT_P = 26'h1 << 15; - parameter MISAID_EXT_Q = 26'h1 << 16; - parameter MISAID_EXT_R = 26'h1 << 17; - parameter MISAID_EXT_S = 26'h1 << 18; - parameter MISAID_EXT_T = 26'h1 << 19; - parameter MISAID_EXT_U = 26'h1 << 20; - parameter MISAID_EXT_V = 26'h1 << 21; - parameter MISAID_EXT_W = 26'h1 << 22; - parameter MISAID_EXT_X = 26'h1 << 23; - parameter MISAID_EXT_Y = 26'h1 << 24; - parameter MISAID_EXT_Z = 26'h1 << 25; + typedef enum logic [1:0] { + U_LEVEL = 2'h0, + S_LEVEL = 2'h1, + RESERVED_LEVEL = 2'h2, + M_LEVEL = 2'h3 + } priv_level_t; - /* mstatus types */ + typedef enum logic [1:0] { + DIRECT = 2'h0, + VECTORED = 2'h1 + } vector_modes_t; - typedef enum logic [4:0] { - VM_MBARE = 5'h0, - VM_MBB = 5'h1, - VM_MBBID = 5'h2, - VM_SV32 = 5'h8, - VM_SV39 = 5'h9, - VM_SV48 = 5'ha, - VM_SV57 = 5'hb, - VM_SV64 = 5'hc - } vm_t; + typedef struct packed { + logic [29:0] base; + vector_modes_t mode; + } mtvec_t; - typedef enum logic [1:0] { - FS_OFF = 2'h0, - FS_INITIAL = 2'h1, - FS_CLEAN = 2'h2, - FS_DIRTY = 2'h3 - } fs_t; + typedef struct packed { + logic sd; + logic [7:0] reserved_3; + logic tsr; + logic tw; + logic tvm; + logic mxr; + logic sum; + logic mprv; + xs_t xs; + fs_t fs; + priv_level_t mpp; + logic [1:0] reserved_2; + logic spp; + logic mpie; + logic reserved_1; + logic spie; + logic upie; + logic mie; + logic reserved_0; + logic sie; + logic uie; + } mstatus_t; - typedef enum logic [1:0] { - XS_ALL_OFF = 2'h0, - XS_NONE_DC = 2'h1, - XS_NONE_D = 2'h2, - XS_SOME_D = 2'h3 - } xs_t; - - typedef struct packed { - logic sd; - logic [8:0] zero; - vm_t vm; - logic mprv; - xs_t xs; - fs_t fs; - prv_lvl_t prv3; - logic ie3; - prv_lvl_t prv2; - logic ie2; - prv_lvl_t prv1; - logic ie1; - prv_lvl_t prv; - logic ie; - } mstatus_t; + /* mip and mie types */ - /* mip and mie types */ + typedef struct packed { // total size for xlen (or mxlen) is 32 bits for our processor + logic [19:0] reserved_3; + logic meip; + logic reserved_2; + logic seip; + logic ueip; + logic mtip; + logic reserved_1; + logic stip; + logic utip; + logic msip; + logic reserved_0; + logic ssip; + logic usip; + } mip_t; - typedef struct packed { - logic [23:0] zero_2; - logic mtip; - logic htip; - logic stip; - logic zero_1; - logic msip; - logic hsip; - logic ssip; - logic zero_0; - } mip_t; + typedef struct packed { + logic [19:0] reserved_3; + logic meie; + logic reserved_2; + logic seie; + logic ueie; + logic mtie; + logic reserved_1; + logic stie; + logic utie; + logic msie; + logic reserved_0; + logic ssie; + logic usie; + } mie_t; - typedef struct packed { - logic [23:0] zero_2; - logic mtie; - logic htie; - logic stie; - logic zero_1; - logic msie; - logic hsie; - logic ssie; - logic zero_0; - } mie_t; + /* mcause register variables */ - /* mcause register variables */ + typedef struct packed { + logic interrupt; + logic [30:0] cause; + } mcause_t; - typedef struct packed { - logic interrupt; - logic [30:0] cause; - } mcause_t; + // ex_code_t should be cast from an + // instantiation of mcause_t + typedef enum logic [30:0] { + INSN_MAL = 31'd0, + INSN_ACCESS = 31'd1, + ILLEGAL_INSN = 31'd2, + BREAKPOINT = 31'd3, + L_ADDR_MAL = 31'd4, + L_FAULT = 31'd5, + S_ADDR_MAL = 31'd6, + S_FAULT = 31'd7, + ENV_CALL_U = 31'd8, + ENV_CALL_S = 31'd9, + RESERVED_0 = 31'd10, + ENV_CALL_M = 31'd11, + INSN_PAGE = 31'd12, + LOAD_PAGE = 31'd13, + RESERVED_1 = 31'd14, + STORE_PAGE = 31'd15, + RESERVED_2 = 31'd16 + } ex_code_t; - // ex_code_t should be cast from an - // instantiation of mcause_t - typedef enum logic [30:0] { - INSN_MAL = 31'h0, - INSN_FAULT = 31'h1, - ILLEGAL_INSN = 31'h2, - BREAKPOINT = 31'h3, - L_ADDR_MAL = 31'h4, - L_FAULT = 31'h5, - S_ADDR_MAL = 31'h6, - S_FAULT = 31'h7, - ENV_CALL_U = 31'h8, - ENV_CALL_S = 31'h9, - ENV_CALL_H = 31'ha, - ENV_CALL_M = 31'hb - } ex_code_t; + typedef enum logic [30:0] { + SOFT_INT_U = 31'd0, + SOFT_INT_S = 31'd1, + RESERVED_4 = 31'd2, + SOFT_INT_M = 31'd3, + TIMER_INT_U = 31'd4, + TIMER_INT_S = 31'd5, + RESERVED_5 = 31'd6, + TIMER_INT_M = 31'd7, + EXT_INT_U = 31'd8, + EXT_INT_S = 31'd9, + RESERVED_6 = 31'd10, + EXT_INT_M = 31'd11 + } int_code_t; - typedef enum logic [30:0] { - SOFT_INT = 31'h0, - TIMER_INT = 31'h1, - EXT_INT = 31'hb //NON-STANDARD in priv-1.7 - } int_code_t; + /* Simple registers */ - /* Simple registers */ + typedef logic [63:0] mcycle_t; + typedef logic [63:0] minstret_t; + typedef logic [31:0] mscratch_t; + typedef logic [31:0] mtval_t; + typedef logic [31:0] mvendorid_t; + typedef logic [31:0] marchid_t; + typedef logic [31:0] mimpid_t; + typedef logic [31:0] mhartid_t; + typedef logic [31:0] medeleg_t; + typedef logic [31:0] mideleg_t; + //typedef logic [31:0] mtvec_t; + typedef logic [31:0] mepc_t; + typedef logic [31:0] mtime_t; + typedef logic [31:0] mtimeh_t; + typedef logic [31:0] mtimecmp_t; - typedef logic [63:0] mcycle_t; - typedef logic [63:0] minstret_t; - typedef logic [31:0] mscratch_t; - typedef logic [31:0] mtval_t; - typedef logic [31:0] mvendorid_t; - typedef logic [31:0] marchid_t; - typedef logic [31:0] mimpid_t; - typedef logic [31:0] mhartid_t; - typedef logic [31:0] medeleg_t; - typedef logic [31:0] mideleg_t; - typedef logic [31:0] mtvec_t; - typedef logic [31:0] mepc_t; - typedef logic [31:0] mtime_t; - typedef logic [31:0] mtimeh_t; - typedef logic [31:0] mtimecmp_t; + /* User simple registers */ + typedef logic [31:0] cycle_t; + typedef logic [31:0] time_t; + typedef logic [31:0] instret_t; - /* User simple registers */ - typedef logic [31:0] cycle_t; - typedef logic [31:0] time_t; - typedef logic [31:0] instret_t; + //Non Standard Extentions + typedef logic [31:0] mtohost_t; + typedef logic [31:0] mfromhost_t; - //Non Standard Extentions - typedef logic [31:0] mtohost_t; - typedef logic [31:0] mfromhost_t; - endpackage -`endif //MACHINE_MODE_TYPES_1_11_PKG_SV +`endif //MACHINE_MODE_TYPES_1_11_PKG_SV diff --git a/source_code/packages/machine_mode_types_1_12_pkg.sv b/source_code/packages/machine_mode_types_1_12_pkg.sv new file mode 100644 index 000000000..aa2e12e7c --- /dev/null +++ b/source_code/packages/machine_mode_types_1_12_pkg.sv @@ -0,0 +1,507 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: machine_mode_types_1_12_pkg.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/08/2022 +* Description: Types needed to implement machine mode priv. isa 1.12 +*/ + +`ifndef MACHINE_MODE_TYPES_1_12_PKG_SV +`define MACHINE_MODE_TYPES_1_12_PKG_SV + +package machine_mode_types_1_12_pkg; + + /* Machine Mode Addresses */ + typedef enum logic [11:0] { + /* Machine Information Registers */ + MVENDORID_ADDR = 12'hF11, + MARCHID_ADDR = 12'hF12, + MIMPID_ADDR = 12'hF13, + MHARTID_ADDR = 12'hF14, + MCONFIGPTR_ADDR = 12'hF15, + + /* Machine Trap Setup */ + MSTATUS_ADDR = 12'h300, + MISA_ADDR = 12'h301, + MEDELEG_ADDR = 12'h302, + MIDELEG_ADDR = 12'h303, + MIE_ADDR = 12'h304, + MTVEC_ADDR = 12'h305, + MCOUNTEREN_ADDR = 12'h306, + MSTATUSH_ADDR = 12'h310, + + /* Machine Trap Handling */ + MSCRATCH_ADDR = 12'h340, + MEPC_ADDR = 12'h341, + MCAUSE_ADDR = 12'h342, + MTVAL_ADDR = 12'h343, + MIP_ADDR = 12'h344, + MTINST_ADDR = 12'h34A, + MTVAL2_ADDR = 12'h34B, + + /* Machine Configuration */ + MENVCFG_ADDR = 12'h30A, + MENVCFGH_ADDR = 12'h31A, + MSECCFG_ADDR = 12'h747, + MSECCFGH_ADDR = 12'h757, + + /* Machine Memory Protection */ + /* See pmp_types_1_12_pkg.sv */ + + /* Machine Counter/Timers */ + MCYCLE_ADDR = 12'hB00, + MINSTRET_ADDR = 12'hB02, + MHPMCOUNTER3_ADDR = 12'hB03, + MHPMCOUNTER4_ADDR = 12'hB04, + MHPMCOUNTER5_ADDR = 12'hB05, + MHPMCOUNTER6_ADDR = 12'hB06, + MHPMCOUNTER7_ADDR = 12'hB07, + MHPMCOUNTER8_ADDR = 12'hB08, + MHPMCOUNTER9_ADDR = 12'hB09, + MHPMCOUNTER10_ADDR = 12'hB0A, + MHPMCOUNTER11_ADDR = 12'hB0B, + MHPMCOUNTER12_ADDR = 12'hB0C, + MHPMCOUNTER13_ADDR = 12'hB0D, + MHPMCOUNTER14_ADDR = 12'hB0E, + MHPMCOUNTER15_ADDR = 12'hB0F, + MHPMCOUNTER16_ADDR = 12'hB10, + MHPMCOUNTER17_ADDR = 12'hB11, + MHPMCOUNTER18_ADDR = 12'hB12, + MHPMCOUNTER19_ADDR = 12'hB13, + MHPMCOUNTER20_ADDR = 12'hB14, + MHPMCOUNTER21_ADDR = 12'hB15, + MHPMCOUNTER22_ADDR = 12'hB16, + MHPMCOUNTER23_ADDR = 12'hB17, + MHPMCOUNTER24_ADDR = 12'hB18, + MHPMCOUNTER25_ADDR = 12'hB19, + MHPMCOUNTER26_ADDR = 12'hB1A, + MHPMCOUNTER27_ADDR = 12'hB1B, + MHPMCOUNTER28_ADDR = 12'hB1C, + MHPMCOUNTER29_ADDR = 12'hB1D, + MHPMCOUNTER30_ADDR = 12'hB1E, + MHPMCOUNTER31_ADDR = 12'hB1F, + MCYCLEH_ADDR = 12'hB80, + MINSTRETH_ADDR = 12'hB82, + MHPMCOUNTER3H_ADDR = 12'hB83, + MHPMCOUNTER4H_ADDR = 12'hB84, + MHPMCOUNTER5H_ADDR = 12'hB85, + MHPMCOUNTER6H_ADDR = 12'hB86, + MHPMCOUNTER7H_ADDR = 12'hB87, + MHPMCOUNTER8H_ADDR = 12'hB88, + MHPMCOUNTER9H_ADDR = 12'hB89, + MHPMCOUNTER10H_ADDR = 12'hB8A, + MHPMCOUNTER11H_ADDR = 12'hB8B, + MHPMCOUNTER12H_ADDR = 12'hB8C, + MHPMCOUNTER13H_ADDR = 12'hB8D, + MHPMCOUNTER14H_ADDR = 12'hB8E, + MHPMCOUNTER15H_ADDR = 12'hB8F, + MHPMCOUNTER16H_ADDR = 12'hB90, + MHPMCOUNTER17H_ADDR = 12'hB91, + MHPMCOUNTER18H_ADDR = 12'hB92, + MHPMCOUNTER19H_ADDR = 12'hB93, + MHPMCOUNTER20H_ADDR = 12'hB94, + MHPMCOUNTER21H_ADDR = 12'hB95, + MHPMCOUNTER22H_ADDR = 12'hB96, + MHPMCOUNTER23H_ADDR = 12'hB97, + MHPMCOUNTER24H_ADDR = 12'hB98, + MHPMCOUNTER25H_ADDR = 12'hB99, + MHPMCOUNTER26H_ADDR = 12'hB9A, + MHPMCOUNTER27H_ADDR = 12'hB9B, + MHPMCOUNTER28H_ADDR = 12'hB9C, + MHPMCOUNTER29H_ADDR = 12'hB9D, + MHPMCOUNTER30H_ADDR = 12'hB9E, + MHPMCOUNTER31H_ADDR = 12'hB9F, + + /* Machine Counter Setup */ + MCOUNTINHIBIT_ADDR = 12'h320, + MHPMEVENT3_ADDR = 12'h323, + MHPMEVENT4_ADDR = 12'h324, + MHPMEVENT5_ADDR = 12'h325, + MHPMEVENT6_ADDR = 12'h326, + MHPMEVENT7_ADDR = 12'h327, + MHPMEVENT8_ADDR = 12'h328, + MHPMEVENT9_ADDR = 12'h329, + MHPMEVENT10_ADDR = 12'h32A, + MHPMEVENT11_ADDR = 12'h32B, + MHPMEVENT12_ADDR = 12'h32C, + MHPMEVENT13_ADDR = 12'h32D, + MHPMEVENT14_ADDR = 12'h32E, + MHPMEVENT15_ADDR = 12'h32F, + MHPMEVENT16_ADDR = 12'h330, + MHPMEVENT17_ADDR = 12'h331, + MHPMEVENT18_ADDR = 12'h332, + MHPMEVENT19_ADDR = 12'h333, + MHPMEVENT20_ADDR = 12'h334, + MHPMEVENT21_ADDR = 12'h335, + MHPMEVENT22_ADDR = 12'h336, + MHPMEVENT23_ADDR = 12'h337, + MHPMEVENT24_ADDR = 12'h338, + MHPMEVENT25_ADDR = 12'h339, + MHPMEVENT26_ADDR = 12'h33A, + MHPMEVENT27_ADDR = 12'h33B, + MHPMEVENT28_ADDR = 12'h33C, + MHPMEVENT29_ADDR = 12'h33D, + MHPMEVENT30_ADDR = 12'h33E, + MHPMEVENT31_ADDR = 12'h33F + } maddr_t; + + /* User Mode Addresses */ + typedef enum logic [11:0] { + CYCLE_ADDR = 12'hC00, + TIME_ADDR = 12'hC01, + INSTRET_ADDR = 12'hC02, + HPMCOUNTER3_ADDR = 12'hC03, + HPMCOUNTER4_ADDR = 12'hC04, + HPMCOUNTER5_ADDR = 12'hC05, + HPMCOUNTER6_ADDR = 12'hC06, + HPMCOUNTER7_ADDR = 12'hC07, + HPMCOUNTER8_ADDR = 12'hC08, + HPMCOUNTER9_ADDR = 12'hC09, + HPMCOUNTER10_ADDR = 12'hC0A, + HPMCOUNTER11_ADDR = 12'hC0B, + HPMCOUNTER12_ADDR = 12'hC0C, + HPMCOUNTER13_ADDR = 12'hC0D, + HPMCOUNTER14_ADDR = 12'hC0E, + HPMCOUNTER15_ADDR = 12'hC0F, + HPMCOUNTER16_ADDR = 12'hC10, + HPMCOUNTER17_ADDR = 12'hC11, + HPMCOUNTER18_ADDR = 12'hC12, + HPMCOUNTER19_ADDR = 12'hC13, + HPMCOUNTER20_ADDR = 12'hC14, + HPMCOUNTER21_ADDR = 12'hC15, + HPMCOUNTER22_ADDR = 12'hC16, + HPMCOUNTER23_ADDR = 12'hC17, + HPMCOUNTER24_ADDR = 12'hC18, + HPMCOUNTER25_ADDR = 12'hC19, + HPMCOUNTER26_ADDR = 12'hC1A, + HPMCOUNTER27_ADDR = 12'hC1B, + HPMCOUNTER28_ADDR = 12'hC1C, + HPMCOUNTER29_ADDR = 12'hC1D, + HPMCOUNTER30_ADDR = 12'hC1E, + HPMCOUNTER31_ADDR = 12'hC1F, + CYCLEH_ADDR = 12'hC80, + INSTRETH_ADDR = 12'hC81, + TIMEH_ADDR = 12'hC82, + HPMCOUNTER3H_ADDR = 12'hC83, + HPMCOUNTER4H_ADDR = 12'hC84, + HPMCOUNTER5H_ADDR = 12'hC85, + HPMCOUNTER6H_ADDR = 12'hC86, + HPMCOUNTER7H_ADDR = 12'hC87, + HPMCOUNTER8H_ADDR = 12'hC88, + HPMCOUNTER9H_ADDR = 12'hC89, + HPMCOUNTER10H_ADDR = 12'hC8A, + HPMCOUNTER11H_ADDR = 12'hC8B, + HPMCOUNTER12H_ADDR = 12'hC8C, + HPMCOUNTER13H_ADDR = 12'hC8D, + HPMCOUNTER14H_ADDR = 12'hC8E, + HPMCOUNTER15H_ADDR = 12'hC8F, + HPMCOUNTER16H_ADDR = 12'hC90, + HPMCOUNTER17H_ADDR = 12'hC91, + HPMCOUNTER18H_ADDR = 12'hC92, + HPMCOUNTER19H_ADDR = 12'hC93, + HPMCOUNTER20H_ADDR = 12'hC94, + HPMCOUNTER21H_ADDR = 12'hC95, + HPMCOUNTER22H_ADDR = 12'hC96, + HPMCOUNTER23H_ADDR = 12'hC97, + HPMCOUNTER24H_ADDR = 12'hC98, + HPMCOUNTER25H_ADDR = 12'hC99, + HPMCOUNTER26H_ADDR = 12'hC9A, + HPMCOUNTER27H_ADDR = 12'hC9B, + HPMCOUNTER28H_ADDR = 12'hC9C, + HPMCOUNTER29H_ADDR = 12'hC9D, + HPMCOUNTER30H_ADDR = 12'hC9E, + HPMCOUNTER31H_ADDR = 12'hC9F + } uaddr_t; + + + /* Machine Mode Register Types */ + + /* misa types */ + + typedef enum logic [1:0] { + BASE_RV32 = 2'h1, + BASE_RV64 = 2'h2, + BASE_RV128 = 2'h3 + } misa_base_t; + + typedef struct packed { + misa_base_t base; + logic [3:0] zero; + logic [25:0] extensions; + } misa_t; + + localparam logic[25:0] MISA_EXT_A = 26'h1 << 0; + localparam logic[25:0] MISA_EXT_B = 26'h1 << 1; + localparam logic[25:0] MISA_EXT_C = 26'h1 << 2; + localparam logic[25:0] MISA_EXT_D = 26'h1 << 3; + localparam logic[25:0] MISA_EXT_E = 26'h1 << 4; + localparam logic[25:0] MISA_EXT_F = 26'h1 << 5; + localparam logic[25:0] MISA_EXT_G = 26'h1 << 6; + localparam logic[25:0] MISA_EXT_H = 26'h1 << 7; + localparam logic[25:0] MISA_EXT_I = 26'h1 << 8; + localparam logic[25:0] MISA_EXT_J = 26'h1 << 9; + localparam logic[25:0] MISA_EXT_K = 26'h1 << 10; + localparam logic[25:0] MISA_EXT_L = 26'h1 << 11; + localparam logic[25:0] MISA_EXT_M = 26'h1 << 12; + localparam logic[25:0] MISA_EXT_N = 26'h1 << 13; + localparam logic[25:0] MISA_EXT_O = 26'h1 << 14; + localparam logic[25:0] MISA_EXT_P = 26'h1 << 15; + localparam logic[25:0] MISA_EXT_Q = 26'h1 << 16; + localparam logic[25:0] MISA_EXT_R = 26'h1 << 17; + localparam logic[25:0] MISA_EXT_S = 26'h1 << 18; + localparam logic[25:0] MISA_EXT_T = 26'h1 << 19; + localparam logic[25:0] MISA_EXT_U = 26'h1 << 20; + localparam logic[25:0] MISA_EXT_V = 26'h1 << 21; + localparam logic[25:0] MISA_EXT_W = 26'h1 << 22; + localparam logic[25:0] MISA_EXT_X = 26'h1 << 23; + localparam logic[25:0] MISA_EXT_Y = 26'h1 << 24; + localparam logic[25:0] MISA_EXT_Z = 26'h1 << 25; + + /* mstatus types */ + + typedef enum logic [1:0] { + FS_OFF = 2'h0, + FS_INITIAL = 2'h1, + FS_CLEAN = 2'h2, + FS_DIRTY = 2'h3 + } fs_t; + + typedef enum logic [1:0] { + VS_OFF = 2'h0, + VS_INITIAL = 2'h1, + VS_CLEAN = 2'h2, + VS_DIRTY = 2'h3 + } vs_t; + + typedef enum logic [1:0] { + XS_ALL_OFF = 2'h0, + XS_NONE_DC = 2'h1, + XS_NONE_D = 2'h2, + XS_SOME_D = 2'h3 + } xs_t; + + typedef enum logic [1:0] { + U_MODE = 2'h0, + S_MODE = 2'h1, + RESERVED_MODE = 2'h2, + M_MODE = 2'h3 + } priv_level_t; + + typedef struct packed { + logic sd; // Extension dirty signal (R/O) + logic [7:0] reserved_3; + logic tsr; // Trap SRET (no effect without S-Mode) + logic tw; // Timeout wait + logic tvm; // Trap virtual memory (no effect without S-Mode) + logic mxr; // Make executable readable (no effect without S-Mode) + logic sum; // Supervisor user memory access (no effect without S-Mode) + logic mprv; // Modify privilege + xs_t xs; // User extensions state + fs_t fs; // FP extension state + priv_level_t mpp; // Previous privilege level + vs_t vs; // Vector extension state + logic spp; // Supervisor previous privilege level (no effect without S-Mode) + logic mpie; // M-Mode previous enable + logic ube; // U-Mode endianness control + logic spie; // S-Mode previous enable + logic reserved_2; + logic mie; // M-Mode interrupt enable + logic reserved_1; + logic sie; // S-Mode interrupt enable + logic reserved_0; + } mstatus_t; + + typedef struct packed { + logic [25:0] reserved_1; + logic mbe; // M-Mode endianness control + logic sbe; // S-Mode endianness control + logic [3:0] reserved_0; + } mstatush_t; + + /* mtvec types */ + typedef enum logic [1:0] { + DIRECT = 2'h0, + VECTORED = 2'h1, + RES_0 = 2'h2, + RES_1 = 2'h3 + } vector_modes_t; + + typedef struct packed { + logic [29:0] base; + vector_modes_t mode; + } mtvec_t; + + /* mip and mie types */ + + // Decreasing priority: MEI, MSI, MTI + + typedef struct packed { + logic [15:0] impl_defined; // Implementation defined + logic [3:0] zero_6; + logic meip; // M-Mode external interrupt + logic zero_5; + logic seip; // S-Mode external interrupt + logic zero_4; + logic mtip; // M-Mode timer interrupt + logic zero_3; + logic stip; // S-Mode timer interrupt + logic zero_2; + logic msip; // M-Mode software interrupt + logic zero_1; + logic ssip; // S-Mode software interrupt + logic zero_0; + } mip_t; + + typedef struct packed { + logic [15:0] impl_defined; + logic [3:0] zero_6; + logic meie; + logic zero_5; + logic seie; + logic zero_4; + logic mtie; + logic zero_3; + logic stie; + logic zero_2; + logic msie; + logic zero_1; + logic ssie; + logic zero_0; + } mie_t; + + /* mcounteren and mcountinhibit types */ + typedef struct packed { + logic hpm31; + logic hpm30; + logic hpm29; + logic hpm28; + logic hpm27; + logic hpm26; + logic hpm25; + logic hpm24; + logic hpm23; + logic hpm22; + logic hpm21; + logic hpm20; + logic hpm19; + logic hpm18; + logic hpm17; + logic hpm16; + logic hpm15; + logic hpm14; + logic hpm13; + logic hpm12; + logic hpm11; + logic hpm10; + logic hpm9; + logic hpm8; + logic hpm7; + logic hpm6; + logic hpm5; + logic hpm4; + logic hpm3; + logic ir; + logic tm; + logic cy; + } mcounteren_t; + + typedef struct packed { + logic hpm31; + logic hpm30; + logic hpm29; + logic hpm28; + logic hpm27; + logic hpm26; + logic hpm25; + logic hpm24; + logic hpm23; + logic hpm22; + logic hpm21; + logic hpm20; + logic hpm19; + logic hpm18; + logic hpm17; + logic hpm16; + logic hpm15; + logic hpm14; + logic hpm13; + logic hpm12; + logic hpm11; + logic hpm10; + logic hpm9; + logic hpm8; + logic hpm7; + logic hpm6; + logic hpm5; + logic hpm4; + logic hpm3; + logic ir; + logic reserved_0; + logic cy; + } mcountinhibit_t; + + /* mcause register variables */ + + typedef struct packed { + logic interrupt; + logic [30:0] cause; + } mcause_t; + + // ex_code_t should be cast from an + // instantiation of mcause_t + typedef enum logic [30:0] { + INSN_MAL = 31'd0, + INSN_ACCESS = 31'd1, + ILLEGAL_INSN = 31'd2, + BREAKPOINT = 31'd3, + L_ADDR_MAL = 31'd4, + L_FAULT = 31'd5, + S_ADDR_MAL = 31'd6, + S_FAULT = 31'd7, + ENV_CALL_U = 31'd8, + ENV_CALL_S = 31'd9, + ENV_CALL_M = 31'd11, + INSN_PAGE = 31'd12, + LOAD_PAGE = 31'd13, + STORE_PAGE = 31'd15 + } ex_code_t; + + typedef enum logic [30:0] { + SOFT_INT_S = 31'd1, + SOFT_INT_M = 31'd3, + TIMER_INT_S = 31'd5, + TIMER_INT_M = 31'd7, + EXT_INT_S = 31'd9, + EXT_INT_M = 31'd11 + } int_code_t; + + // General CSR definition + typedef logic [11:0] csr_addr_t; + typedef logic [31:0] csr_reg_t; + typedef logic [63:0] long_csr_t; + + //Non Standard Extentions + // Unsure what these are for, + // part of priv 1.11 + typedef logic [31:0] mtohost_t; + typedef logic [31:0] mfromhost_t; + +endpackage + +`endif //MACHINE_MODE_TYPES_1_12_PKG_SV diff --git a/source_code/packages/machine_mode_types_1_7_pkg.sv b/source_code/packages/machine_mode_types_1_7_pkg.sv index 52952a72e..2c47b7e6d 100644 --- a/source_code/packages/machine_mode_types_1_7_pkg.sv +++ b/source_code/packages/machine_mode_types_1_7_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,225 +27,225 @@ package machine_mode_types_1_7_pkg; - typedef enum logic [11:0] { - /* Machine Mode Addresses */ - MCPUID_ADDR = 12'hF00, - MIMPID_ADDR = 12'hF01, - MHARTID_ADDR = 12'hF10, - - MSTATUS_ADDR = 12'h300, - MTVEC_ADDR = 12'h301, - MTDELEG_ADDR = 12'h302, - MIE_ADDR = 12'h304, - - MSCRATCH_ADDR = 12'h340, - MEPC_ADDR = 12'h341, - MCAUSE_ADDR = 12'h342, - MBADADDR_ADDR = 12'h343, - MIP_ADDR = 12'h344, - - MBASE_ADDR = 12'h380, - MBOUND_ADDR = 12'h381, - MIBASE_ADDR = 12'h382, - MIBOUND_ADDR = 12'h383, - MDBASE_ADDR = 12'h384, - MDBOUND_ADDR = 12'h385, - - HTIMEW_ADDR = 12'hB01, - HTIMEHW_ADDR = 12'hB81, - - MTIMECMP_ADDR = 12'h321, - MTIME_ADDR = 12'h701, - MTIMEH_ADDR = 12'h741, - - MTOHOST_ADDR = 12'h780, - MFROMHOST_ADDR = 12'h781, - /* User Mode Addresses */ - CYCLE_ADDR = 12'hC00, - TIME_ADDR = 12'hC01, - INSTRET_ADDR = 12'hC02, - CYCLEH_ADDR = 12'hC80, - TIMEH_ADDR = 12'hC81, - INSTRETH_ADDR = 12'hC82 - } csr_addr_t; - - - /* Priv Levels */ - typedef enum logic [1:0] { - U_MODE = 2'b00, - S_MODE = 2'b01, - H_MODE = 2'b10, - M_MODE = 2'b11 - } prv_lvl_t; - - /* Machine Mode Register Types */ - - /* mcpuid types */ - - typedef enum logic [1:0] { - BASE_RV32 = 2'h1, - BASE_RV64 = 2'h2, - BASE_RV128 = 2'h3 - } mcpuid_base_t; - - typedef struct packed { - mcpuid_base_t base; - logic [3:0] zero; - logic [25:0] extensions; - } mcpuid_t; - - parameter MCPUID_EXT_A = 26'h1 << 0; - parameter MCPUID_EXT_B = 26'h1 << 1; - parameter MCPUID_EXT_C = 26'h1 << 2; - parameter MCPUID_EXT_D = 26'h1 << 3; - parameter MCPUID_EXT_E = 26'h1 << 4; - parameter MCPUID_EXT_F = 26'h1 << 5; - parameter MCPUID_EXT_G = 26'h1 << 6; - parameter MCPUID_EXT_H = 26'h1 << 7; - parameter MCPUID_EXT_I = 26'h1 << 8; - parameter MCPUID_EXT_J = 26'h1 << 9; - parameter MCPUID_EXT_K = 26'h1 << 10; - parameter MCPUID_EXT_L = 26'h1 << 11; - parameter MCPUID_EXT_M = 26'h1 << 12; - parameter MCPUID_EXT_N = 26'h1 << 13; - parameter MCPUID_EXT_O = 26'h1 << 14; - parameter MCPUID_EXT_P = 26'h1 << 15; - parameter MCPUID_EXT_Q = 26'h1 << 16; - parameter MCPUID_EXT_R = 26'h1 << 17; - parameter MCPUID_EXT_S = 26'h1 << 18; - parameter MCPUID_EXT_T = 26'h1 << 19; - parameter MCPUID_EXT_U = 26'h1 << 20; - parameter MCPUID_EXT_V = 26'h1 << 21; - parameter MCPUID_EXT_W = 26'h1 << 22; - parameter MCPUID_EXT_X = 26'h1 << 23; - parameter MCPUID_EXT_Y = 26'h1 << 24; - parameter MCPUID_EXT_Z = 26'h1 << 25; - parameter MTVEC_MEMORY_ADDR = 32'h1c0; - - /* mstatus types */ - - typedef enum logic [4:0] { - VM_MBARE = 5'h0, - VM_MBB = 5'h1, - VM_MBBID = 5'h2, - VM_SV32 = 5'h8, - VM_SV39 = 5'h9, - VM_SV48 = 5'ha, - VM_SV57 = 5'hb, - VM_SV64 = 5'hc - } vm_t; - - typedef enum logic [1:0] { - FS_OFF = 2'h0, - FS_INITIAL = 2'h1, - FS_CLEAN = 2'h2, - FS_DIRTY = 2'h3 - } fs_t; - - typedef enum logic [1:0] { - XS_ALL_OFF = 2'h0, - XS_NONE_DC = 2'h1, - XS_NONE_D = 2'h2, - XS_SOME_D = 2'h3 - } xs_t; - - typedef struct packed { - logic sd; - logic [8:0] zero; - vm_t vm; - logic mprv; - xs_t xs; - fs_t fs; - prv_lvl_t prv3; - logic ie3; - prv_lvl_t prv2; - logic ie2; - prv_lvl_t prv1; - logic ie1; - prv_lvl_t prv; - logic ie; - } mstatus_t; - - /* mip and mie types */ - - typedef struct packed { - logic [23:0] zero_2; - logic mtip; - logic htip; - logic stip; - logic zero_1; - logic msip; - logic hsip; - logic ssip; - logic zero_0; - } mip_t; - - typedef struct packed { - logic [23:0] zero_2; - logic mtie; - logic htie; - logic stie; - logic zero_1; - logic msie; - logic hsie; - logic ssie; - logic zero_0; - } mie_t; - - /* mcause register variables */ - - typedef struct packed { - logic interrupt; - logic [30:0] cause; - } mcause_t; - - // ex_code_t should be cast from an - // instantiation of mcause_t - typedef enum logic [30:0] { - INSN_MAL = 31'h0, - INSN_FAULT = 31'h1, - ILLEGAL_INSN = 31'h2, - BREAKPOINT = 31'h3, - L_ADDR_MAL = 31'h4, - L_FAULT = 31'h5, - S_ADDR_MAL = 31'h6, - S_FAULT = 31'h7, - ENV_CALL_U = 31'h8, - ENV_CALL_S = 31'h9, - ENV_CALL_H = 31'ha, - ENV_CALL_M = 31'hb - } ex_code_t; - - typedef enum logic [30:0] { - SOFT_INT = 31'h0, - TIMER_INT = 31'h1, - EXT_INT = 31'hb //NON-STANDARD in priv-1.7 - } int_code_t; - - /* Simple registers */ - - typedef logic [63:0] mcycle_t; - typedef logic [63:0] minstret_t; - typedef logic [31:0] mscratch_t; - typedef logic [31:0] mbadaddr_t; - typedef logic [31:0] mimpid_t; - typedef logic [31:0] mhartid_t; - typedef logic [31:0] mtdeleg_t; - typedef logic [31:0] mtvec_t; - typedef logic [31:0] mepc_t; - typedef logic [31:0] mtime_t; - typedef logic [31:0] mtimeh_t; - typedef logic [31:0] mtimecmp_t; - - /* User simple registers */ - typedef logic [31:0] cycle_t; - typedef logic [31:0] time_t; - typedef logic [31:0] instret_t; - - //Non Standard Extentions - typedef logic [31:0] mtohost_t; - typedef logic [31:0] mfromhost_t; - + typedef enum logic [11:0] { + /* Machine Mode Addresses */ + MCPUID_ADDR = 12'hF00, + MIMPID_ADDR = 12'hF01, + MHARTID_ADDR = 12'hF10, + + MSTATUS_ADDR = 12'h300, + MTVEC_ADDR = 12'h301, + MTDELEG_ADDR = 12'h302, + MIE_ADDR = 12'h304, + + MSCRATCH_ADDR = 12'h340, + MEPC_ADDR = 12'h341, + MCAUSE_ADDR = 12'h342, + MBADADDR_ADDR = 12'h343, + MIP_ADDR = 12'h344, + + MBASE_ADDR = 12'h380, + MBOUND_ADDR = 12'h381, + MIBASE_ADDR = 12'h382, + MIBOUND_ADDR = 12'h383, + MDBASE_ADDR = 12'h384, + MDBOUND_ADDR = 12'h385, + + HTIMEW_ADDR = 12'hB01, + HTIMEHW_ADDR = 12'hB81, + + MTIMECMP_ADDR = 12'h321, + MTIME_ADDR = 12'h701, + MTIMEH_ADDR = 12'h741, + + MTOHOST_ADDR = 12'h780, + MFROMHOST_ADDR = 12'h781, + /* User Mode Addresses */ + CYCLE_ADDR = 12'hC00, + TIME_ADDR = 12'hC01, + INSTRET_ADDR = 12'hC02, + CYCLEH_ADDR = 12'hC80, + TIMEH_ADDR = 12'hC81, + INSTRETH_ADDR = 12'hC82 + } csr_addr_t; + + + /* Priv Levels */ + typedef enum logic [1:0] { + U_MODE = 2'b00, + S_MODE = 2'b01, + H_MODE = 2'b10, + M_MODE = 2'b11 + } prv_lvl_t; + + /* Machine Mode Register Types */ + + /* mcpuid types */ + + typedef enum logic [1:0] { + BASE_RV32 = 2'h1, + BASE_RV64 = 2'h2, + BASE_RV128 = 2'h3 + } mcpuid_base_t; + + typedef struct packed { + mcpuid_base_t base; + logic [3:0] zero; + logic [25:0] extensions; + } mcpuid_t; + + parameter MCPUID_EXT_A = 26'h1 << 0; + parameter MCPUID_EXT_B = 26'h1 << 1; + parameter MCPUID_EXT_C = 26'h1 << 2; + parameter MCPUID_EXT_D = 26'h1 << 3; + parameter MCPUID_EXT_E = 26'h1 << 4; + parameter MCPUID_EXT_F = 26'h1 << 5; + parameter MCPUID_EXT_G = 26'h1 << 6; + parameter MCPUID_EXT_H = 26'h1 << 7; + parameter MCPUID_EXT_I = 26'h1 << 8; + parameter MCPUID_EXT_J = 26'h1 << 9; + parameter MCPUID_EXT_K = 26'h1 << 10; + parameter MCPUID_EXT_L = 26'h1 << 11; + parameter MCPUID_EXT_M = 26'h1 << 12; + parameter MCPUID_EXT_N = 26'h1 << 13; + parameter MCPUID_EXT_O = 26'h1 << 14; + parameter MCPUID_EXT_P = 26'h1 << 15; + parameter MCPUID_EXT_Q = 26'h1 << 16; + parameter MCPUID_EXT_R = 26'h1 << 17; + parameter MCPUID_EXT_S = 26'h1 << 18; + parameter MCPUID_EXT_T = 26'h1 << 19; + parameter MCPUID_EXT_U = 26'h1 << 20; + parameter MCPUID_EXT_V = 26'h1 << 21; + parameter MCPUID_EXT_W = 26'h1 << 22; + parameter MCPUID_EXT_X = 26'h1 << 23; + parameter MCPUID_EXT_Y = 26'h1 << 24; + parameter MCPUID_EXT_Z = 26'h1 << 25; + parameter MTVEC_MEMORY_ADDR = 32'h1c0; + + /* mstatus types */ + + typedef enum logic [4:0] { + VM_MBARE = 5'h0, + VM_MBB = 5'h1, + VM_MBBID = 5'h2, + VM_SV32 = 5'h8, + VM_SV39 = 5'h9, + VM_SV48 = 5'ha, + VM_SV57 = 5'hb, + VM_SV64 = 5'hc + } vm_t; + + typedef enum logic [1:0] { + FS_OFF = 2'h0, + FS_INITIAL = 2'h1, + FS_CLEAN = 2'h2, + FS_DIRTY = 2'h3 + } fs_t; + + typedef enum logic [1:0] { + XS_ALL_OFF = 2'h0, + XS_NONE_DC = 2'h1, + XS_NONE_D = 2'h2, + XS_SOME_D = 2'h3 + } xs_t; + + typedef struct packed { + logic sd; + logic [8:0] zero; + vm_t vm; + logic mprv; + xs_t xs; + fs_t fs; + prv_lvl_t prv3; + logic ie3; + prv_lvl_t prv2; + logic ie2; + prv_lvl_t prv1; + logic ie1; + prv_lvl_t prv; + logic ie; + } mstatus_t; + + /* mip and mie types */ + + typedef struct packed { + logic [23:0] zero_2; + logic mtip; + logic htip; + logic stip; + logic zero_1; + logic msip; + logic hsip; + logic ssip; + logic zero_0; + } mip_t; + + typedef struct packed { + logic [23:0] zero_2; + logic mtie; + logic htie; + logic stie; + logic zero_1; + logic msie; + logic hsie; + logic ssie; + logic zero_0; + } mie_t; + + /* mcause register variables */ + + typedef struct packed { + logic interrupt; + logic [30:0] cause; + } mcause_t; + + // ex_code_t should be cast from an + // instantiation of mcause_t + typedef enum logic [30:0] { + INSN_MAL = 31'h0, + INSN_FAULT = 31'h1, + ILLEGAL_INSN = 31'h2, + BREAKPOINT = 31'h3, + L_ADDR_MAL = 31'h4, + L_FAULT = 31'h5, + S_ADDR_MAL = 31'h6, + S_FAULT = 31'h7, + ENV_CALL_U = 31'h8, + ENV_CALL_S = 31'h9, + ENV_CALL_H = 31'ha, + ENV_CALL_M = 31'hb + } ex_code_t; + + typedef enum logic [30:0] { + SOFT_INT = 31'h0, + TIMER_INT = 31'h1, + EXT_INT = 31'hb //NON-STANDARD in priv-1.7 + } int_code_t; + + /* Simple registers */ + + typedef logic [63:0] mcycle_t; + typedef logic [63:0] minstret_t; + typedef logic [31:0] mscratch_t; + typedef logic [31:0] mbadaddr_t; + typedef logic [31:0] mimpid_t; + typedef logic [31:0] mhartid_t; + typedef logic [31:0] mtdeleg_t; + typedef logic [31:0] mtvec_t; + typedef logic [31:0] mepc_t; + typedef logic [31:0] mtime_t; + typedef logic [31:0] mtimeh_t; + typedef logic [31:0] mtimecmp_t; + + /* User simple registers */ + typedef logic [31:0] cycle_t; + typedef logic [31:0] time_t; + typedef logic [31:0] instret_t; + + //Non Standard Extentions + typedef logic [31:0] mtohost_t; + typedef logic [31:0] mfromhost_t; + endpackage -`endif //MACHINE_MODE_TYPES_1_7_PKG_SV +`endif //MACHINE_MODE_TYPES_1_7_PKG_SV diff --git a/source_code/packages/machine_mode_types_pkg.sv b/source_code/packages/machine_mode_types_pkg.sv index 729617c32..7ca1a78b9 100644 --- a/source_code/packages/machine_mode_types_pkg.sv +++ b/source_code/packages/machine_mode_types_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,230 +29,230 @@ package machine_mode_types_pkg; - typedef enum logic [11:0] { - /* Machine Mode Addresses */ - MVENDORID_ADDR = 12'hF11, - MARCHID_ADDR = 12'hF12, - MIMPID_ADDR = 12'hF13, - MHARTID_ADDR = 12'hF14, - - MSTATUS_ADDR = 12'h300, - MISA_ADDR = 12'h301, - MEDELEG_ADDR = 12'h302, - MIDELEG_ADDR = 12'h303, - MIE_ADDR = 12'h304, - MTVEC_ADDR = 12'h305, - //TODO: MCOUNTEREN AND ASSOCIATED REGS/CONTGROL (e.g. cycle/time) - MCOUNTEREN_ADDR = 12'h306, - - MSCRATCH_ADDR = 12'h340, - MEPC_ADDR = 12'h341, - MCAUSE_ADDR = 12'h342, - MTVAL_ADDR = 12'h343, - MIP_ADDR = 12'h344, - - // TODO: MAY BE ABLE TO REMOVE BELOW - MBASE_ADDR = 12'h380, - MBOUND_ADDR = 12'h381, - MIBASE_ADDR = 12'h382, - MIBOUND_ADDR = 12'h383, - MDBASE_ADDR = 12'h384, - MDBOUND_ADDR = 12'h385, - // TODO: MAY BE ABLE TO REMOVE ABOVE - // TODO: BELOW MUST BE REMOVED - MTOHOST_ADDR = 12'h780, - MFROMHOST_ADDR = 12'h781, - HTIMEW_ADDR = 12'hB01, - HTIMEHW_ADDR = 12'hB81, - MTIMECMP_ADDR = 12'h321, - MTIME_ADDR = 12'h701, - MTIMEH_ADDR = 12'h741, - // TODO: ABOVE MUST BE REMOVED - - MCYCLE_ADDR = 12'hB00, - MINSTRET_ADDR = 12'hB02, - MCYCLEH_ADDR = 12'hB80, - MINSTRETH_ADDR = 12'hB82 - } csr_addr_t; - - /* Priv Levels */ - typedef enum logic [1:0] { - U_MODE = 2'b00, - S_MODE = 2'b01, - H_MODE = 2'b10, - M_MODE = 2'b11 - } prv_lvl_t; - - /* Machine Mode Register Types */ - - /* misaid types */ - - typedef enum logic [1:0] { - BASE_RV32 = 2'h1, - BASE_RV64 = 2'h2, - BASE_RV128 = 2'h3 - } misaid_base_t; - - typedef struct packed { - misaid_base_t base; - logic [3:0] zero; - logic [25:0] extensions; - } misaid_t; - - parameter MISAID_EXT_A = 26'h1 << 0; - parameter MISAID_EXT_B = 26'h1 << 1; - parameter MISAID_EXT_C = 26'h1 << 2; - parameter MISAID_EXT_D = 26'h1 << 3; - parameter MISAID_EXT_E = 26'h1 << 4; - parameter MISAID_EXT_F = 26'h1 << 5; - parameter MISAID_EXT_G = 26'h1 << 6; - parameter MISAID_EXT_H = 26'h1 << 7; - parameter MISAID_EXT_I = 26'h1 << 8; - parameter MISAID_EXT_J = 26'h1 << 9; - parameter MISAID_EXT_K = 26'h1 << 10; - parameter MISAID_EXT_L = 26'h1 << 11; - parameter MISAID_EXT_M = 26'h1 << 12; - parameter MISAID_EXT_N = 26'h1 << 13; - parameter MISAID_EXT_O = 26'h1 << 14; - parameter MISAID_EXT_P = 26'h1 << 15; - parameter MISAID_EXT_Q = 26'h1 << 16; - parameter MISAID_EXT_R = 26'h1 << 17; - parameter MISAID_EXT_S = 26'h1 << 18; - parameter MISAID_EXT_T = 26'h1 << 19; - parameter MISAID_EXT_U = 26'h1 << 20; - parameter MISAID_EXT_V = 26'h1 << 21; - parameter MISAID_EXT_W = 26'h1 << 22; - parameter MISAID_EXT_X = 26'h1 << 23; - parameter MISAID_EXT_Y = 26'h1 << 24; - parameter MISAID_EXT_Z = 26'h1 << 25; - - /* mstatus types */ - - typedef enum logic [4:0] { - VM_MBARE = 5'h0, - VM_MBB = 5'h1, - VM_MBBID = 5'h2, - VM_SV32 = 5'h8, - VM_SV39 = 5'h9, - VM_SV48 = 5'ha, - VM_SV57 = 5'hb, - VM_SV64 = 5'hc - } vm_t; - - typedef enum logic [1:0] { - FS_OFF = 2'h0, - FS_INITIAL = 2'h1, - FS_CLEAN = 2'h2, - FS_DIRTY = 2'h3 - } fs_t; - - typedef enum logic [1:0] { - XS_ALL_OFF = 2'h0, - XS_NONE_DC = 2'h1, - XS_NONE_D = 2'h2, - XS_SOME_D = 2'h3 - } xs_t; - - typedef struct packed { - logic sd; - logic [8:0] zero; - vm_t vm; - logic mprv; - xs_t xs; - fs_t fs; - prv_lvl_t prv3; - logic ie3; - prv_lvl_t prv2; - logic ie2; - prv_lvl_t prv1; - logic ie1; - prv_lvl_t prv; - logic ie; - } mstatus_t; - - /* mip and mie types */ - - typedef struct packed { - logic [23:0] zero_2; - logic mtip; - logic htip; - logic stip; - logic zero_1; - logic msip; - logic hsip; - logic ssip; - logic zero_0; - } mip_t; - - typedef struct packed { - logic [23:0] zero_2; - logic mtie; - logic htie; - logic stie; - logic zero_1; - logic msie; - logic hsie; - logic ssie; - logic zero_0; - } mie_t; - - /* mcause register variables */ - - typedef struct packed { - logic interrupt; - logic [30:0] cause; - } mcause_t; - - // ex_code_t should be cast from an - // instantiation of mcause_t - typedef enum logic [30:0] { - INSN_MAL = 31'h0, - INSN_FAULT = 31'h1, - ILLEGAL_INSN = 31'h2, - BREAKPOINT = 31'h3, - L_ADDR_MAL = 31'h4, - L_FAULT = 31'h5, - S_ADDR_MAL = 31'h6, - S_FAULT = 31'h7, - ENV_CALL_U = 31'h8, - ENV_CALL_S = 31'h9, - ENV_CALL_H = 31'ha, - ENV_CALL_M = 31'hb - } ex_code_t; - - typedef enum logic [30:0] { - SOFT_INT = 31'h0, - TIMER_INT = 31'h1, - EXT_INT = 31'hb //NON-STANDARD in priv-1.7 - } int_code_t; - - /* Simple registers */ - - typedef logic [63:0] mcycle_t; - typedef logic [63:0] minstret_t; - typedef logic [31:0] mscratch_t; - typedef logic [31:0] mtval_t; - typedef logic [31:0] mvendorid_t; - typedef logic [31:0] marchid_t; - typedef logic [31:0] mimpid_t; - typedef logic [31:0] mhartid_t; - typedef logic [31:0] medeleg_t; - typedef logic [31:0] mideleg_t; - typedef logic [31:0] mtvec_t; - typedef logic [31:0] mepc_t; - typedef logic [31:0] mtime_t; - typedef logic [31:0] mtimeh_t; - typedef logic [31:0] mtimecmp_t; - - /* User simple registers */ - typedef logic [31:0] cycle_t; - typedef logic [31:0] time_t; - typedef logic [31:0] instret_t; - - //Non Standard Extentions - typedef logic [31:0] mtohost_t; - typedef logic [31:0] mfromhost_t; - + typedef enum logic [11:0] { + /* Machine Mode Addresses */ + MVENDORID_ADDR = 12'hF11, + MARCHID_ADDR = 12'hF12, + MIMPID_ADDR = 12'hF13, + MHARTID_ADDR = 12'hF14, + + MSTATUS_ADDR = 12'h300, + MISA_ADDR = 12'h301, + MEDELEG_ADDR = 12'h302, + MIDELEG_ADDR = 12'h303, + MIE_ADDR = 12'h304, + MTVEC_ADDR = 12'h305, + //TODO: MCOUNTEREN AND ASSOCIATED REGS/CONTGROL (e.g. cycle/time) + MCOUNTEREN_ADDR = 12'h306, + + MSCRATCH_ADDR = 12'h340, + MEPC_ADDR = 12'h341, + MCAUSE_ADDR = 12'h342, + MTVAL_ADDR = 12'h343, + MIP_ADDR = 12'h344, + + // TODO: MAY BE ABLE TO REMOVE BELOW + MBASE_ADDR = 12'h380, + MBOUND_ADDR = 12'h381, + MIBASE_ADDR = 12'h382, + MIBOUND_ADDR = 12'h383, + MDBASE_ADDR = 12'h384, + MDBOUND_ADDR = 12'h385, + // TODO: MAY BE ABLE TO REMOVE ABOVE + // TODO: BELOW MUST BE REMOVED + MTOHOST_ADDR = 12'h780, + MFROMHOST_ADDR = 12'h781, + HTIMEW_ADDR = 12'hB01, + HTIMEHW_ADDR = 12'hB81, + MTIMECMP_ADDR = 12'h321, + MTIME_ADDR = 12'h701, + MTIMEH_ADDR = 12'h741, + // TODO: ABOVE MUST BE REMOVED + + MCYCLE_ADDR = 12'hB00, + MINSTRET_ADDR = 12'hB02, + MCYCLEH_ADDR = 12'hB80, + MINSTRETH_ADDR = 12'hB82 + } csr_addr_t; + + /* Priv Levels */ + typedef enum logic [1:0] { + U_MODE = 2'b00, + S_MODE = 2'b01, + H_MODE = 2'b10, + M_MODE = 2'b11 + } prv_lvl_t; + + /* Machine Mode Register Types */ + + /* misaid types */ + + typedef enum logic [1:0] { + BASE_RV32 = 2'h1, + BASE_RV64 = 2'h2, + BASE_RV128 = 2'h3 + } misaid_base_t; + + typedef struct packed { + misaid_base_t base; + logic [3:0] zero; + logic [25:0] extensions; + } misaid_t; + + parameter MISAID_EXT_A = 26'h1 << 0; + parameter MISAID_EXT_B = 26'h1 << 1; + parameter MISAID_EXT_C = 26'h1 << 2; + parameter MISAID_EXT_D = 26'h1 << 3; + parameter MISAID_EXT_E = 26'h1 << 4; + parameter MISAID_EXT_F = 26'h1 << 5; + parameter MISAID_EXT_G = 26'h1 << 6; + parameter MISAID_EXT_H = 26'h1 << 7; + parameter MISAID_EXT_I = 26'h1 << 8; + parameter MISAID_EXT_J = 26'h1 << 9; + parameter MISAID_EXT_K = 26'h1 << 10; + parameter MISAID_EXT_L = 26'h1 << 11; + parameter MISAID_EXT_M = 26'h1 << 12; + parameter MISAID_EXT_N = 26'h1 << 13; + parameter MISAID_EXT_O = 26'h1 << 14; + parameter MISAID_EXT_P = 26'h1 << 15; + parameter MISAID_EXT_Q = 26'h1 << 16; + parameter MISAID_EXT_R = 26'h1 << 17; + parameter MISAID_EXT_S = 26'h1 << 18; + parameter MISAID_EXT_T = 26'h1 << 19; + parameter MISAID_EXT_U = 26'h1 << 20; + parameter MISAID_EXT_V = 26'h1 << 21; + parameter MISAID_EXT_W = 26'h1 << 22; + parameter MISAID_EXT_X = 26'h1 << 23; + parameter MISAID_EXT_Y = 26'h1 << 24; + parameter MISAID_EXT_Z = 26'h1 << 25; + + /* mstatus types */ + + typedef enum logic [4:0] { + VM_MBARE = 5'h0, + VM_MBB = 5'h1, + VM_MBBID = 5'h2, + VM_SV32 = 5'h8, + VM_SV39 = 5'h9, + VM_SV48 = 5'ha, + VM_SV57 = 5'hb, + VM_SV64 = 5'hc + } vm_t; + + typedef enum logic [1:0] { + FS_OFF = 2'h0, + FS_INITIAL = 2'h1, + FS_CLEAN = 2'h2, + FS_DIRTY = 2'h3 + } fs_t; + + typedef enum logic [1:0] { + XS_ALL_OFF = 2'h0, + XS_NONE_DC = 2'h1, + XS_NONE_D = 2'h2, + XS_SOME_D = 2'h3 + } xs_t; + + typedef struct packed { + logic sd; + logic [8:0] zero; + vm_t vm; + logic mprv; + xs_t xs; + fs_t fs; + prv_lvl_t prv3; + logic ie3; + prv_lvl_t prv2; + logic ie2; + prv_lvl_t prv1; + logic ie1; + prv_lvl_t prv; + logic ie; + } mstatus_t; + + /* mip and mie types */ + + typedef struct packed { + logic [23:0] zero_2; + logic mtip; + logic htip; + logic stip; + logic zero_1; + logic msip; + logic hsip; + logic ssip; + logic zero_0; + } mip_t; + + typedef struct packed { + logic [23:0] zero_2; + logic mtie; + logic htie; + logic stie; + logic zero_1; + logic msie; + logic hsie; + logic ssie; + logic zero_0; + } mie_t; + + /* mcause register variables */ + + typedef struct packed { + logic interrupt; + logic [30:0] cause; + } mcause_t; + + // ex_code_t should be cast from an + // instantiation of mcause_t + typedef enum logic [30:0] { + INSN_MAL = 31'h0, + INSN_FAULT = 31'h1, + ILLEGAL_INSN = 31'h2, + BREAKPOINT = 31'h3, + L_ADDR_MAL = 31'h4, + L_FAULT = 31'h5, + S_ADDR_MAL = 31'h6, + S_FAULT = 31'h7, + ENV_CALL_U = 31'h8, + ENV_CALL_S = 31'h9, + ENV_CALL_H = 31'ha, + ENV_CALL_M = 31'hb + } ex_code_t; + + typedef enum logic [30:0] { + SOFT_INT = 31'h0, + TIMER_INT = 31'h1, + EXT_INT = 31'hb //NON-STANDARD in priv-1.7 + } int_code_t; + + /* Simple registers */ + + typedef logic [63:0] mcycle_t; + typedef logic [63:0] minstret_t; + typedef logic [31:0] mscratch_t; + typedef logic [31:0] mtval_t; + typedef logic [31:0] mvendorid_t; + typedef logic [31:0] marchid_t; + typedef logic [31:0] mimpid_t; + typedef logic [31:0] mhartid_t; + typedef logic [31:0] medeleg_t; + typedef logic [31:0] mideleg_t; + typedef logic [31:0] mtvec_t; + typedef logic [31:0] mepc_t; + typedef logic [31:0] mtime_t; + typedef logic [31:0] mtimeh_t; + typedef logic [31:0] mtimecmp_t; + + /* User simple registers */ + typedef logic [31:0] cycle_t; + typedef logic [31:0] time_t; + typedef logic [31:0] instret_t; + + //Non Standard Extentions + typedef logic [31:0] mtohost_t; + typedef logic [31:0] mfromhost_t; + endpackage -`endif //MACHINE_MODE_TYPES_PKG_SV +`endif //MACHINE_MODE_TYPES_PKG_SV diff --git a/source_code/packages/pma_types_1_12_pkg.sv b/source_code/packages/pma_types_1_12_pkg.sv new file mode 100644 index 000000000..f50e6b982 --- /dev/null +++ b/source_code/packages/pma_types_1_12_pkg.sv @@ -0,0 +1,78 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: pma_types_1_12_pkg.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 04/04/2022 +* Description: Types needed to implement physical memory attributes +*/ + +`ifndef PMA_TYPES_1_12_PKG_SV +`define PMA_TYPES_1_12_PKG_SV + +package pma_types_1_12_pkg; + + /* pmacfg types */ + + typedef enum logic [1:0] { + AMONone = 2'b00, + AMOSwap = 2'b01, + AMOLogical = 2'b10, + AMOArithmetic = 2'b11 + } pma_amo_t; + + typedef enum logic [1:0] { + RsrvNone = 2'b00, + RsrvNonEventual = 2'b01, + RsrvEventual = 2'b10, + RsrvReserved = 2'b11 + } pma_rsrv_t; + + typedef enum logic [2:0] { + ByteAcc = 3'b000, + HWLower = 3'b001, + HWUpper = 3'b010, + WordAcc = 3'b011, + Burst2W = 3'b100, + Burst4W = 3'b101, + Burst8W = 3'b110, + AccWidthReserved = 3'b111 + } pma_accwidth_t; + + typedef struct packed { + logic [1:0] reserved; + logic W; // Writes supported + logic R; // Reads supported + logic X; // Execute supported + pma_accwidth_t AccWidth; // Max supported access width + logic Idm; // Idempotency + logic Cache; // Able to cache + logic Coh; // Coherency + pma_rsrv_t Rsrv; // Reservability + pma_amo_t AMO; // AMO operations + logic MIO; // Memory or I/O + } pma_cfg_t; + + typedef struct packed { + pma_cfg_t pma_cfg_1; + pma_cfg_t pma_cfg_0; + } pma_reg_t; + +endpackage + +`endif //PMA_TYPES_1_12_PKG_SV diff --git a/source_code/packages/pmp_types_1_12_pkg.sv b/source_code/packages/pmp_types_1_12_pkg.sv new file mode 100644 index 000000000..af061a0ac --- /dev/null +++ b/source_code/packages/pmp_types_1_12_pkg.sv @@ -0,0 +1,146 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: pmp_types_1_12_pkg.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/20/2022 +* Description: Types needed to implement physical memory protection +*/ + +`ifndef PMP_TYPES_1_12_PKG_SV +`define PMP_TYPES_1_12_PKG_SV + +package pmp_types_1_12_pkg; + + /* PMP CSR Addresses */ + typedef enum logic [11:0] { + /* Machine Memory Protection */ + PMPCFG0_ADDR = 12'h3A0, + PMPCFG1_ADDR = 12'h3A1, + PMPCFG2_ADDR = 12'h3A2, + PMPCFG3_ADDR = 12'h3A3, + // PMPCFG4_ADDR = 12'h3A4, + // PMPCFG5_ADDR = 12'h3A5, + // PMPCFG6_ADDR = 12'h3A6, + // PMPCFG7_ADDR = 12'h3A7, + // PMPCFG8_ADDR = 12'h3A8, + // PMPCFG9_ADDR = 12'h3A9, + // PMPCFG10_ADDR = 12'h3AA, + // PMPCFG11_ADDR = 12'h3AB, + // PMPCFG12_ADDR = 12'h3AC, + // PMPCFG13_ADDR = 12'h3AD, + // PMPCFG14_ADDR = 12'h3AE, + // PMPCFG15_ADDR = 12'h3AF, + PMPADDR0_ADDR = 12'h3B0, + PMPADDR1_ADDR = 12'h3B1, + PMPADDR2_ADDR = 12'h3B2, + PMPADDR3_ADDR = 12'h3B3, + PMPADDR4_ADDR = 12'h3B4, + PMPADDR5_ADDR = 12'h3B5, + PMPADDR6_ADDR = 12'h3B6, + PMPADDR7_ADDR = 12'h3B7, + PMPADDR8_ADDR = 12'h3B8, + PMPADDR9_ADDR = 12'h3B9, + PMPADDR10_ADDR = 12'h3BA, + PMPADDR11_ADDR = 12'h3BB, + PMPADDR12_ADDR = 12'h3BC, + PMPADDR13_ADDR = 12'h3BD, + PMPADDR14_ADDR = 12'h3BE, + PMPADDR15_ADDR = 12'h3BF + // PMPADDR16_ADDR = 12'h3C0, + // PMPADDR17_ADDR = 12'h3C1, + // PMPADDR18_ADDR = 12'h3C2, + // PMPADDR19_ADDR = 12'h3C3, + // PMPADDR20_ADDR = 12'h3C4, + // PMPADDR21_ADDR = 12'h3C5, + // PMPADDR22_ADDR = 12'h3C6, + // PMPADDR23_ADDR = 12'h3C7, + // PMPADDR24_ADDR = 12'h3C8, + // PMPADDR25_ADDR = 12'h3C9, + // PMPADDR26_ADDR = 12'h3CA, + // PMPADDR27_ADDR = 12'h3CB, + // PMPADDR28_ADDR = 12'h3CC, + // PMPADDR29_ADDR = 12'h3CD, + // PMPADDR30_ADDR = 12'h3CE, + // PMPADDR31_ADDR = 12'h3CF, + // PMPADDR32_ADDR = 12'h3D0, + // PMPADDR33_ADDR = 12'h3D1, + // PMPADDR34_ADDR = 12'h3D2, + // PMPADDR35_ADDR = 12'h3D3, + // PMPADDR36_ADDR = 12'h3D4, + // PMPADDR37_ADDR = 12'h3D5, + // PMPADDR38_ADDR = 12'h3D6, + // PMPADDR39_ADDR = 12'h3D7, + // PMPADDR40_ADDR = 12'h3D8, + // PMPADDR41_ADDR = 12'h3D9, + // PMPADDR42_ADDR = 12'h3DA, + // PMPADDR43_ADDR = 12'h3DB, + // PMPADDR44_ADDR = 12'h3DC, + // PMPADDR45_ADDR = 12'h3DD, + // PMPADDR46_ADDR = 12'h3DE, + // PMPADDR47_ADDR = 12'h3DF, + // PMPADDR48_ADDR = 12'h3E0, + // PMPADDR49_ADDR = 12'h3E1, + // PMPADDR50_ADDR = 12'h3E2, + // PMPADDR51_ADDR = 12'h3E3, + // PMPADDR52_ADDR = 12'h3E4, + // PMPADDR53_ADDR = 12'h3E5, + // PMPADDR54_ADDR = 12'h3E6, + // PMPADDR55_ADDR = 12'h3E7, + // PMPADDR56_ADDR = 12'h3E8, + // PMPADDR57_ADDR = 12'h3E9, + // PMPADDR58_ADDR = 12'h3EA, + // PMPADDR59_ADDR = 12'h3EB, + // PMPADDR60_ADDR = 12'h3EC, + // PMPADDR61_ADDR = 12'h3ED, + // PMPADDR62_ADDR = 12'h3EE, + // PMPADDR63_ADDR = 12'h3EF + } pmp_csr_addr_t; + + /* pmpcfg types */ + + typedef enum logic [1:0] { + OFF = 2'b00, + TOR = 2'b01, + NA4 = 2'b10, + NAPOT = 2'b11 + } pmp_mode_t; + + typedef struct packed { + logic L; + logic [1:0] reserved; + pmp_mode_t A; + logic X; + logic W; + logic R; + } pmpcfg_base_t; + +// typedef struct packed { +// pmpcfg_base_t cfg3; +// pmpcfg_base_t cfg2; +// pmpcfg_base_t cfg1; +// pmpcfg_base_t cfg0; +// } pmpcfg_t; + +typedef pmpcfg_base_t [3:0] pmpcfg_t; + +typedef logic [31:0] pmpaddr_t; + +endpackage + +`endif //PMP_TYPES_1_12_PKG_SV diff --git a/source_code/packages/risc_mgmt/crc32_pkg.sv b/source_code/packages/risc_mgmt/crc32_pkg.sv index b6d34c157..e18f3ecef 100644 --- a/source_code/packages/risc_mgmt/crc32_pkg.sv +++ b/source_code/packages/risc_mgmt/crc32_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 04/06/2017 -* Description: Package for crc32 extension +* Description: Package for crc32 extension */ `ifndef CRC32_PKG_SV @@ -27,20 +27,18 @@ package crc32_pkg; - // Interface between the decode and execute stage - // This must be named "decode_execute_t" - typedef struct packed { - logic reset; - logic new_byte; - } decode_execute_t; + // Interface between the decode and execute stage + // This must be named "decode_execute_t" + typedef struct packed { + logic reset; + logic new_byte; + } decode_execute_t; - // Interface between the execute and memory stage - // This must be named "execute_memory_t" - typedef struct packed { - logic signal; - } execute_memory_t; + // Interface between the execute and memory stage + // This must be named "execute_memory_t" + typedef struct packed {logic signal;} execute_memory_t; endpackage -`endif //CRC32_PKG_SV +`endif //CRC32_PKG_SV diff --git a/source_code/packages/risc_mgmt/rv32m_pkg.sv b/source_code/packages/risc_mgmt/rv32m_pkg.sv index 7b2b40eaa..d55a6c0a1 100644 --- a/source_code/packages/risc_mgmt/rv32m_pkg.sv +++ b/source_code/packages/risc_mgmt/rv32m_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: Types for the RV32M standard extension +* Description: Types for the RV32M standard extension */ `ifndef RV32M_PKG_SV @@ -27,39 +27,37 @@ package rv32m_pkg; - localparam RV32M_OPCODE = 7'b0110011; - localparam RV32M_OPCODE_MINOR = 7'b0000001; - - typedef struct packed { - logic [6:0] opcode_minor; - logic [4:0] rs2; - logic [4:0] rs1; - logic [2:0] funct; - logic [4:0] rd; - logic [6:0] opcode_major; - } rv32m_insn_t; - - // Interface between the decode and execute stage - // This must be named "decode_execute_t" - typedef struct packed { - logic mul; - logic div; - logic rem; - logic usign_usign; - logic sign_sign; - logic sign_usign; - logic lower_word; - logic start; - } decode_execute_t; + localparam logic [6:0] RV32M_OPCODE = 7'b0110011; + localparam logic [6:0] RV32M_OPCODE_MINOR = 7'b0000001; + + typedef struct packed { + logic [6:0] opcode_minor; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct; + logic [4:0] rd; + logic [6:0] opcode_major; + } rv32m_insn_t; + + // Interface between the decode and execute stage + // This must be named "decode_execute_t" + typedef struct packed { + logic mul; + logic div; + logic rem; + logic usign_usign; + logic sign_sign; + logic sign_usign; + logic lower_word; + logic start; + } decode_execute_t; + + // Interface between the execute and memory stage + // This must be named "execute_memory_t" + typedef struct packed {logic signal;} execute_memory_t; - // Interface between the execute and memory stage - // This must be named "execute_memory_t" - typedef struct packed { - logic signal; - } execute_memory_t; - endpackage -`endif //RV32M_PKG_SV +`endif //RV32M_PKG_SV diff --git a/source_code/packages/risc_mgmt/template_pkg.sv b/source_code/packages/risc_mgmt/template_pkg.sv index 14cc8842c..d47d70afb 100644 --- a/source_code/packages/risc_mgmt/template_pkg.sv +++ b/source_code/packages/risc_mgmt/template_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,7 +18,7 @@ * * Created by: * Email: -* Date Created: +* Date Created: * Description: Template for a package containg the needed types for * a RISC-MGMT extension */ @@ -28,19 +28,15 @@ package template_pkg; - // Interface between the decode and execute stage - // This must be named "decode_execute_t" - typedef struct packed { - logic signal; - } decode_execute_t; + // Interface between the decode and execute stage + // This must be named "decode_execute_t" + typedef struct packed {logic signal;} decode_execute_t; - // Interface between the execute and memory stage - // This must be named "execute_memory_t" - typedef struct packed { - logic signal; - } execute_memory_t; + // Interface between the execute and memory stage + // This must be named "execute_memory_t" + typedef struct packed {logic signal;} execute_memory_t; endpackage -`endif //TEMPLATE_PKG_SV +`endif //TEMPLATE_PKG_SV diff --git a/source_code/packages/risc_mgmt/test_pkg.sv b/source_code/packages/risc_mgmt/test_pkg.sv index 18309f853..863ae4bfe 100644 --- a/source_code/packages/risc_mgmt/test_pkg.sv +++ b/source_code/packages/risc_mgmt/test_pkg.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: ISA extension used for the RISC-MGMT testbench +* Description: ISA extension used for the RISC-MGMT testbench */ `ifndef TEST_PKG_SV @@ -27,49 +27,49 @@ package test_pkg; - // Interface between the decode and execute stage - // This must be named "decode_execute_t" - typedef struct packed { - logic rtype; - logic rtype_stall; - logic br_j; - logic mem_lw; - logic mem_sw; - logic exception; - logic nop; - logic [8:0] imm; - } decode_execute_t; + // Interface between the decode and execute stage + // This must be named "decode_execute_t" + typedef struct packed { + logic rtype; + logic rtype_stall; + logic br_j; + logic mem_lw; + logic mem_sw; + logic exception; + logic nop; + logic [8:0] imm; + } decode_execute_t; - // Interface between the execute and memory stage - // This must be named "execute_memory_t" - typedef struct packed { - logic mem_lw; - logic mem_sw; - logic nop; - logic exception; - logic [31:0] mem_addr; - logic [31:0] mem_store; - } execute_memory_t; + // Interface between the execute and memory stage + // This must be named "execute_memory_t" + typedef struct packed { + logic mem_lw; + logic mem_sw; + logic nop; + logic exception; + logic [31:0] mem_addr; + logic [31:0] mem_store; + } execute_memory_t; - typedef enum logic [3:0] { - RTYPE, - RTYPE_STALL_5, - BR_J, - MEM_LOAD, - MEM_STORE, - EXCEPTION, - NOP - } test_funct_t; + typedef enum logic [3:0] { + RTYPE, + RTYPE_STALL_5, + BR_J, + MEM_LOAD, + MEM_STORE, + EXCEPTION, + NOP + } test_funct_t; - typedef struct packed { - logic [5:0] imm; - test_funct_t funct; - logic [4:0] rs_d; - logic [4:0] rs_0; - logic [4:0] rs_1; - logic [6:0] opcode; - } test_insn_t; + typedef struct packed { + logic [5:0] imm; + test_funct_t funct; + logic [4:0] rs_d; + logic [4:0] rs_0; + logic [4:0] rs_1; + logic [6:0] opcode; + } test_insn_t; endpackage -`endif //TEST_PKG_SV +`endif //TEST_PKG_SV diff --git a/source_code/packages/riscv_packages.core b/source_code/packages/riscv_packages.core new file mode 100644 index 000000000..275e25eae --- /dev/null +++ b/source_code/packages/riscv_packages.core @@ -0,0 +1,32 @@ +CAPI=2: +name: socet:riscv:packages:0.1.0 +description: RISC-V Tracker modules + +filesets: + rtl: + files: + - alu_types_pkg.sv + - rv32i_types_pkg.sv + - machine_mode_types_1_12_pkg.sv + - pma_types_1_12_pkg.sv + - pmp_types_1_12_pkg.sv + - rv32m_pkg.sv + #- risc_mgmt/crc32_pkg.sv + #- risc_mgmt/template_pkg.sv + #- risc_mgmt/test_pkg.sv + #- risc_mgmt/rv32m_pkg.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + lint: + filesets: + - rtl + description: Linting + default_tool: veriblelint + toplevel: alu_types_pkg + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search'] diff --git a/source_code/packages/rv32i_types_pkg.sv b/source_code/packages/rv32i_types_pkg.sv index 43968cc7e..bd8103cc4 100644 --- a/source_code/packages/rv32i_types_pkg.sv +++ b/source_code/packages/rv32i_types_pkg.sv @@ -1,22 +1,22 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: rv32i_types_pkg.sv -* -* Created by: Jacob R. Stevens +* +* Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 06/01/2016 * Description: Package containing types used for a RV32I implementation @@ -25,182 +25,183 @@ `ifndef RV32I_TYPES_PKG_SV `define RV32I_TYPES_PKG_SV package rv32i_types_pkg; - parameter WORD_SIZE = 32; - parameter RAM_ADDR_SIZE = 32; - parameter OP_W = 7; - parameter BR_W = 3; - parameter LD_W = 3; - parameter SW_W = 3; - parameter IMM_W = 3; - parameter REG_W = 3; - - typedef logic [WORD_SIZE-1:0] word_t; - - typedef enum logic [OP_W-1:0] { - LUI = 7'b0110111, - AUIPC = 7'b0010111, - JAL = 7'b1101111, - JALR = 7'b1100111, - // All branching instructions share an opcode - BRANCH = 7'b1100011, - // All load instructions share an opcode - LOAD = 7'b0000011, - // All store instructions share an opcode - STORE = 7'b0100011, - // All immediate ALU instructions share an opcode - IMMED = 7'b0010011, - // All register-register instructions share an opcode - REGREG = 7'b0110011, - // All system instructions share an opcode - SYSTEM = 7'b1110011, - MISCMEM = 7'b0001111 - } opcode_t; - - typedef enum logic [BR_W-1:0] { - BEQ = 3'b000, - BNE = 3'b001, - BLT = 3'b100, - BGE = 3'b101, - BLTU = 3'b110, - BGEU = 3'b111 - } branch_t; - - typedef enum logic [LD_W-1:0] { - LB = 3'b000, - LH = 3'b001, - LW = 3'b010, - LBU = 3'b100, - LHU = 3'b101 - } load_t; - - typedef enum logic [SW_W-1:0] { - SB = 3'b000, - SH = 3'b001, - SW = 3'b010 - } store_t; - - typedef enum logic [IMM_W-1:0] { - ADDI = 3'b000, - SLTI = 3'b010, - SLTIU = 3'b011, - XORI = 3'b100, - ORI = 3'b110, - ANDI = 3'b111, - SLLI = 3'b001, - // Logical/Arithmetic based on bit 30 of instruction - // 0 / 1 - SRI = 3'b101 - } imm_t; - - typedef enum logic [REG_W-1:0] { - // Add/Sub based on bit 30 of instruction - // 0 / 1 - ADDSUB = 3'b000, - SLL = 3'b001, - SLT = 3'b010, - SLTU = 3'b011, - XOR = 3'b100, - // Logical/Arithmetic based on bit 30 of instruction - // 0 / 1 - SR = 3'b101, - OR = 3'b110, - AND = 3'b111 - } regreg_t; - - typedef enum logic [2:0] { - // Non CSR contains ECALL, EBREAK, and xRET instructions - // ECALL/EBREAK based on bit 20 of instruction - // 0 / 1 - // xRET based on bits 28 and 29 of instruction - PRIV = 3'b000, - CSRRW = 3'b001, - CSRRS = 3'b010, - CSRRC = 3'b011, - CSRRWI = 3'b101, - CSRRSI = 3'b110, - CSRRCI = 3'b111 - } rv32i_system_t; - - typedef enum logic [11:0] { - ECALL = 12'b0000000_00000, - EBREAK = 12'b0000000_00001, - MRET = 12'b0011000_00010, - SRET = 12'b0001000_00010, - URET = 12'b0000000_00010 - } priv_insn_t; - - typedef enum logic [2:0] { - FENCE = 3'b000, - FENCEI = 3'b001 - } rv32i_miscmem_t; - - typedef struct packed { - logic [6:0] funct7; - logic [4:0] rs2; - logic [4:0] rs1; - logic [2:0] funct3; - logic [4:0] rd; - opcode_t opcode; - } rtype_t; - - typedef struct packed { - logic [11:0] imm11_00; - logic [4:0] rs1; - logic [2:0] funct3; - logic [4:0] rd; - opcode_t opcode; - } itype_t; - - typedef struct packed { - logic [6:0] imm11_05; - logic [4:0] rs2; - logic [4:0] rs1; - logic [2:0] funct3; - logic [4:0] imm04_00; - opcode_t opcode; - } stype_t; - - typedef struct packed { - logic imm12; - logic [5:0] imm10_05; - logic [4:0] rs2; - logic [4:0] rs1; - logic [2:0] funct3; - logic [3:0] imm04_01; - logic imm11; - opcode_t opcode; - } sbtype_t; - - typedef struct packed { - logic [19:0] imm31_12; - logic [4:0] rd; - opcode_t opcode; - } utype_t; - - typedef struct packed { - logic imm20; - logic [9:0] imm10_01; - logic imm11; - logic [7:0] imm19_12; - logic [4:0] rd; - opcode_t opcode; - } ujtype_t; - - typedef struct packed { - logic [11:0] csr; - logic [4:0] rs1_zimm; - logic [2:0] funct3; - logic [4:0] rd; - opcode_t opcode; - } systype_t; - - typedef struct packed { - logic token; - word_t pc; - word_t pc4; - word_t instr; - word_t prediction; - } fetch_ex_pipeline_reg_t; + parameter int WORD_SIZE = 32; + parameter int RAM_ADDR_SIZE = 32; + parameter int OP_W = 7; + parameter int BR_W = 3; + parameter int LD_W = 3; + parameter int SW_W = 3; + parameter int IMM_W = 3; + parameter int REG_W = 3; + + typedef logic [WORD_SIZE-1:0] word_t; + + typedef enum logic [OP_W-1:0] { + LUI = 7'b0110111, + AUIPC = 7'b0010111, + JAL = 7'b1101111, + JALR = 7'b1100111, + // All branching instructions share an opcode + BRANCH = 7'b1100011, + // All load instructions share an opcode + LOAD = 7'b0000011, + // All store instructions share an opcode + STORE = 7'b0100011, + // All immediate ALU instructions share an opcode + IMMED = 7'b0010011, + // All register-register instructions share an opcode + REGREG = 7'b0110011, + // All system instructions share an opcode + SYSTEM = 7'b1110011, + MISCMEM = 7'b0001111 + } opcode_t; + + typedef enum logic [BR_W-1:0] { + BEQ = 3'b000, + BNE = 3'b001, + BLT = 3'b100, + BGE = 3'b101, + BLTU = 3'b110, + BGEU = 3'b111 + } branch_t; + + typedef enum logic [LD_W-1:0] { + LB = 3'b000, + LH = 3'b001, + LW = 3'b010, + LBU = 3'b100, + LHU = 3'b101 + } load_t; + + typedef enum logic [SW_W-1:0] { + SB = 3'b000, + SH = 3'b001, + SW = 3'b010 + } store_t; + + typedef enum logic [IMM_W-1:0] { + ADDI = 3'b000, + SLTI = 3'b010, + SLTIU = 3'b011, + XORI = 3'b100, + ORI = 3'b110, + ANDI = 3'b111, + SLLI = 3'b001, + // Logical/Arithmetic based on bit 30 of instruction + // 0 / 1 + SRI = 3'b101 + } imm_t; + + typedef enum logic [REG_W-1:0] { + // Add/Sub based on bit 30 of instruction + // 0 / 1 + ADDSUB = 3'b000, + SLL = 3'b001, + SLT = 3'b010, + SLTU = 3'b011, + XOR = 3'b100, + // Logical/Arithmetic based on bit 30 of instruction + // 0 / 1 + SR = 3'b101, + OR = 3'b110, + AND = 3'b111 + } regreg_t; + + typedef enum logic [2:0] { + // Non CSR contains ECALL, EBREAK, and xRET instructions + // ECALL/EBREAK based on bit 20 of instruction + // 0 / 1 + // xRET based on bits 28 and 29 of instruction + PRIV = 3'b000, + CSRRW = 3'b001, + CSRRS = 3'b010, + CSRRC = 3'b011, + CSRRWI = 3'b101, + CSRRSI = 3'b110, + CSRRCI = 3'b111 + } rv32i_system_t; + + typedef enum logic [11:0] { + ECALL = 12'b0000000_00000, + EBREAK = 12'b0000000_00001, + MRET = 12'b0011000_00010, + SRET = 12'b0001000_00010, + URET = 12'b0000000_00010, + WFI = 12'b0001000_00101 + } priv_insn_t; + + typedef enum logic [2:0] { + FENCE = 3'b000, + FENCEI = 3'b001 + } rv32i_miscmem_t; + + typedef struct packed { + logic [6:0] funct7; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } rtype_t; + + typedef struct packed { + logic [11:0] imm11_00; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } itype_t; + + typedef struct packed { + logic [6:0] imm11_05; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] imm04_00; + opcode_t opcode; + } stype_t; + + typedef struct packed { + logic imm12; + logic [5:0] imm10_05; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [3:0] imm04_01; + logic imm11; + opcode_t opcode; + } sbtype_t; + + typedef struct packed { + logic [19:0] imm31_12; + logic [4:0] rd; + opcode_t opcode; + } utype_t; + + typedef struct packed { + logic imm20; + logic [9:0] imm10_01; + logic imm11; + logic [7:0] imm19_12; + logic [4:0] rd; + opcode_t opcode; + } ujtype_t; + + typedef struct packed { + logic [11:0] csr; + logic [4:0] rs1_zimm; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } systype_t; + + typedef struct packed { + logic token; + word_t pc; + word_t pc4; + word_t instr; + word_t prediction; + } fetch_ex_pipeline_reg_t; endpackage `endif diff --git a/source_code/packages/rv32m_pkg.sv b/source_code/packages/rv32m_pkg.sv new file mode 100644 index 000000000..070cb6dbd --- /dev/null +++ b/source_code/packages/rv32m_pkg.sv @@ -0,0 +1,64 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32m_pkg.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/07/2017 +* Description: Types for the RV32M standard extension +*/ + +`ifndef RV32M_PKG_SV +`define RV32M_PKG_SV + +package rv32m_pkg; + + localparam logic [6:0] RV32M_OPCODE = 7'b0110011; + localparam logic [6:0] RV32M_OPCODE_MINOR = 7'b0000001; + + typedef struct packed { + logic [6:0] opcode_minor; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct; + logic [4:0] rd; + logic [6:0] opcode_major; + } rv32m_insn_t; + + // Equivalent to ALUOP for integer, decode operation locally to the FU + // This should be direct-cast of the funct3 field of insn + typedef enum logic [2:0] { + MUL = 3'b000, + MULH = 3'b001, + MULHSU = 3'b010, + MULHU = 3'b011, + DIV = 3'b100, + DIVU = 3'b101, + REM = 3'b110, + REMU = 3'b111 + } rv32m_op_t; + + // RV32M decoder: output + typedef struct packed { + logic select; + rv32m_op_t op; + } rv32m_decode_t; + + +endpackage + +`endif //RV32M_PKG_SV diff --git a/source_code/packages/wscript b/source_code/packages/wscript deleted file mode 100644 index 1c24a2b98..000000000 --- a/source_code/packages/wscript +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(risc_mgmt) - -def sim_source(cnf): - cnf.recurse(risc_mgmt) diff --git a/source_code/pipelines/pipeline_wrapper.sv b/source_code/pipelines/pipeline_wrapper.sv index 64e971fc5..58c046447 100644 --- a/source_code/pipelines/pipeline_wrapper.sv +++ b/source_code/pipelines/pipeline_wrapper.sv @@ -1,21 +1,21 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: pipeline_wrapper.sv -* +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 05/17/2017 @@ -28,18 +28,22 @@ `include "risc_mgmt_if.vh" `include "cache_control_if.vh" `include "component_selection_defines.vh" +`include "sparce_pipeline_if.vh" +`include "rv32c_if.vh" module pipeline_wrapper ( - input logic CLK, nRST, - output logic halt, - generic_bus_if.cpu igen_bus_if, - generic_bus_if.cpu dgen_bus_if, - prv_pipeline_if prv_pipe_if, - predictor_pipeline_if predict_if, - risc_mgmt_if rm_if, - cache_control_if cc_if + input logic CLK, + nRST, + output logic halt, + generic_bus_if.cpu igen_bus_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if prv_pipe_if, + predictor_pipeline_if predict_if, + risc_mgmt_if rm_if, + cache_control_if cc_if, + sparce_pipeline_if sparce_if ); - - tspp tspp_pipeline(.*); + + tspp tspp_pipeline (.*); endmodule diff --git a/source_code/pipelines/stage3/include/stage3_fetch_execute_if.vh b/source_code/pipelines/stage3/include/stage3_fetch_execute_if.vh new file mode 100644 index 000000000..879dd70f0 --- /dev/null +++ b/source_code/pipelines/stage3/include/stage3_fetch_execute_if.vh @@ -0,0 +1,44 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_fetch_execute_if.vh +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/01/2016 +* Description: Interface between the fetch and execute pipeline stages +*/ + +`ifndef STAGE3_FETCH_EXECUTE_IF_VH +`define STAGE3_FETCH_EXECUTE_IF_VH + +interface stage3_fetch_execute_if; + import rv32i_types_pkg::*; + import stage3_types_pkg::*; + + fetch_ex_t fetch_ex_reg; + word_t brj_addr; + + modport fetch( + output fetch_ex_reg + ); + + modport execute( + input fetch_ex_reg + ); + +endinterface +`endif diff --git a/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh b/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh new file mode 100644 index 000000000..2dcab72cc --- /dev/null +++ b/source_code/pipelines/stage3/include/stage3_forwarding_unit_if.vh @@ -0,0 +1,32 @@ +`ifndef __STAGE3_FORWARD_UNIT_VH__ +`define __STAGE3_FORWARD_UNIT_VH__ + +interface stage3_forwarding_unit_if(); + + logic [4:0] rd_m; + logic [4:0] rs1_e; + logic [4:0] rs2_e; + logic reg_write; + logic load; + logic fwd_rs1; + logic fwd_rs2; + rv32i_types_pkg::word_t rd_mem_data; + + modport execute( + input fwd_rs1, fwd_rs2, rd_mem_data, + output rs1_e, rs2_e + ); + + modport mem( + output rd_m, rd_mem_data, reg_write, load + ); + + modport fw_unit( + input rs1_e, rs2_e, rd_m, reg_write, load, + output fwd_rs1, fwd_rs2 + ); + +endinterface + + +`endif diff --git a/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh new file mode 100644 index 000000000..55ef37840 --- /dev/null +++ b/source_code/pipelines/stage3/include/stage3_hazard_unit_if.vh @@ -0,0 +1,103 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_hazard_unit_if.vh +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/15/2016 +* Description: Interface for the hazard unit of the two stage pipeline +*/ + +`ifndef STAGE3_HAZARD_UNIT_IF_VH +`define STAGE3_HAZARD_UNIT_IF_VH + +interface stage3_hazard_unit_if(); + + import rv32i_types_pkg::word_t; + + // Pipeline status signals (inputs) + logic [4:0] rs1_e, rs2_e, rd_m; + logic reg_write, csr_read; + logic i_mem_busy, d_mem_busy, dren, dwen, ret, suppress_data; + logic jump, branch, fence_stall; + logic mispredict, halt; + word_t pc_f, pc_e, pc_m; + logic valid_e, valid_m; // f always valid since it's the PC + logic ifence; + logic ex_busy; + + // Control (outputs) + logic pc_en, npc_sel; + logic if_ex_flush, ex_mem_flush; + logic if_ex_stall, ex_mem_stall; + logic iren, suppress_iren; + logic rollback; // signal for rolling back fetched instructions after instruction in mem stage, for certain CSR and ifence instructions + + // xTVEC Insertion + word_t priv_pc; + logic insert_priv_pc; + + //Pipeline Exceptions (inputs) + logic fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, + breakpoint, env, wfi; + word_t badaddr; + + // Pipeline Tokens + logic token_ex; + logic token_mem; + + // RV32C + logic rv32c_ready; + + modport hazard_unit ( + input rs1_e, rs2_e, rd_m, + reg_write, csr_read, + i_mem_busy, d_mem_busy, dren, dwen, ret, + jump, branch, fence_stall, mispredict, halt, pc_f, pc_e, pc_m, + fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env, wfi, + badaddr, ifence, + token_ex, token_mem, rv32c_ready, + valid_e, valid_m, ex_busy, + output pc_en, npc_sel, + if_ex_flush, ex_mem_flush, + if_ex_stall, ex_mem_stall, + priv_pc, insert_priv_pc, iren, suppress_iren, suppress_data, rollback + ); + + modport fetch ( + input pc_en, npc_sel, if_ex_stall, if_ex_flush, priv_pc, insert_priv_pc, iren, suppress_iren, rollback, + output i_mem_busy, rv32c_ready, pc_f + ); + + modport execute ( + input ex_mem_stall, ex_mem_flush, npc_sel, + output rs1_e, rs2_e, token_ex, pc_e, valid_e, ex_busy + ); + + modport mem ( + input ex_mem_stall, ex_mem_flush, suppress_data, + output rd_m, reg_write, csr_read, + d_mem_busy, dren, dwen, ret, + jump, branch, fence_stall, mispredict, halt, pc_m, valid_m, + fault_insn, mal_insn, illegal_insn, fault_l, mal_l, fault_s, mal_s, breakpoint, env, + badaddr, ifence, wfi, + token_mem + ); + + endinterface + +`endif diff --git a/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh b/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh new file mode 100644 index 000000000..2afc4f404 --- /dev/null +++ b/source_code/pipelines/stage3/include/stage3_mem_pipe_if.vh @@ -0,0 +1,34 @@ +`ifndef __STAGE3_MEM_PIPE_IF__ +`define __STAGE3_MEM_PIPE_IF__ + +interface stage3_mem_pipe_if(); + + import rv32i_types_pkg::*; + import stage3_types_pkg::*; + + logic reg_write; + logic [4:0] rd_m; + ex_mem_t ex_mem_reg; + word_t brj_addr; + word_t reg_wdata; + word_t pc4; // For flush in case of fence_i, CSR, etc. + + modport fetch( + input brj_addr, pc4 + ); + + + modport execute( + input reg_wdata, reg_write, rd_m, + output ex_mem_reg + ); + + modport mem( + input ex_mem_reg, + output brj_addr, reg_wdata, reg_write, rd_m, pc4 + ); + +endinterface + + +`endif diff --git a/source_code/pipelines/stage3/source/stage3.sv b/source_code/pipelines/stage3/source/stage3.sv new file mode 100644 index 000000000..3b57adc85 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3.sv @@ -0,0 +1,64 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Two Stage In-Order Pipeline +*/ + +`include "stage3_fetch_execute_if.vh" +`include "stage3_hazard_unit_if.vh" +`include "predictor_pipeline_if.vh" +`include "generic_bus_if.vh" +`include "prv_pipeline_if.vh" +//`include "risc_mgmt_if.vh" +`include "cache_control_if.vh" +`include "sparce_pipeline_if.vh" +`include "rv32c_if.vh" + +module stage3 #( + RESET_PC = 32'h80000000 +)( + input CLK, + input nRST, + output logic halt, + output logic wfi, + generic_bus_if.cpu igen_bus_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if prv_pipe_if, + predictor_pipeline_if predict_if, + //risc_mgmt_if rm_if, + cache_control_if cc_if, + sparce_pipeline_if sparce_if, + rv32c_if rv32cif +); + //interface instantiations + stage3_fetch_execute_if fetch_ex_if(); + stage3_mem_pipe_if mem_pipe_if(); + stage3_hazard_unit_if hazard_if(); + stage3_forwarding_unit_if fw_if(); + + //module instantiations + stage3_fetch_stage #(.RESET_PC(RESET_PC)) fetch_stage_i(.mem_fetch_if(mem_pipe_if), .*); + stage3_execute_stage execute_stage_i(.ex_mem_if(mem_pipe_if), .*); + stage3_mem_stage mem_stage_i(.ex_mem_if(mem_pipe_if), .*); + stage3_hazard_unit hazard_unit_i(.*); + stage3_forwarding_unit forward_unit_i(.*); + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_execute_stage.sv b/source_code/pipelines/stage3/source/stage3_execute_stage.sv new file mode 100644 index 000000000..af4573384 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_execute_stage.sv @@ -0,0 +1,321 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_execute_stage.sv +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/16/2016 +* Description: Execute Stage for the Two Stage Pipeline +*/ + +`include "stage3_fetch_execute_if.vh" +`include "stage3_hazard_unit_if.vh" +`include "stage3_forwarding_unit_if.vh" +`include "control_unit_if.vh" +`include "component_selection_defines.vh" +`include "rv32i_reg_file_if.vh" +`include "alu_if.vh" +//`include "prv_pipeline_if.vh" +//`include "risc_mgmt_if.vh" +`include "rv32c_if.vh" + +module stage3_execute_stage ( + input CLK, + input nRST, + stage3_fetch_execute_if.execute fetch_ex_if, + stage3_mem_pipe_if.execute ex_mem_if, + stage3_hazard_unit_if.execute hazard_if, + stage3_forwarding_unit_if.execute fw_if, + //risc_mgmt_if.ts_execute rm_if, + sparce_pipeline_if.pipe_execute sparce_if, + rv32c_if.execute rv32cif +); + + import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; + import stage3_types_pkg::*; + + // Interface declarations + control_unit_if cu_if (); + rv32i_reg_file_if rf_if (); + alu_if alu_if (); + jump_calc_if jump_if (); + branch_res_if branch_if (); + + /********************** + * Decode/Register Read + ***********************/ + + // RV32C inputs + assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; + assign rv32cif.halt = 1'b0; // TODO: Is this signal necessary? Can't get it right on decode of a halt instruction + assign rv32cif.ex_busy = hazard_if.ex_mem_stall; //cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; + assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + //assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + + // Control unit, inputs are post-decompression + control_unit cu ( + .cu_if(cu_if), + .rf_if(rf_if), + .rmgmt_rsel_s_0('0), + .rmgmt_rsel_s_1('0), + .rmgmt_rsel_d('0), + .rmgmt_req_reg_r('0), + .rmgmt_req_reg_w('0) + //.rmgmt_rsel_s_0(rm_if.rsel_s_0), + //.rmgmt_rsel_s_1(rm_if.rsel_s_1), + //.rmgmt_rsel_d(rm_if.rsel_d), + //.rmgmt_req_reg_r(rm_if.req_reg_r), + //.rmgmt_req_reg_w(rm_if.req_reg_w) + ); + + assign wfi = cu_if.wfi; //Added by rkannank + + generate + if (BASE_ISA == "RV32E") begin : g_rfile_select + rv32e_reg_file rf ( + .CLK, + .nRST, + .rf_if + ); + end else begin : g_rfile_select + rv32i_reg_file rf ( + .CLK, + .nRST, + .rf_if + ); + end + endgenerate + + + /****************** + * Functional Units + *******************/ + logic rv32m_busy; + word_t rv32m_out; + word_t ex_out; + word_t rs1_post_fwd, rs2_post_fwd; + + alu alu (.*); + jump_calc jump_calc (.*); + branch_res branch_res (.br_if(branch_if)); + + rv32m_wrapper RV32M_FU ( + .CLK, + .nRST, + .rv32m_start(cu_if.rv32m_control.select), + .operation(cu_if.rv32m_control.op), // TODO: Better way? + .rv32m_a(rs1_post_fwd), // All RV32M are reg-reg, so just feed post-fwd regs + .rv32m_b(rs2_post_fwd), + .rv32m_busy, + .rv32m_out + ); + + // Forwarding + // These rs*_post_fwd values should be used in place of rs1/rs2 anywhere they are used + assign rs1_post_fwd = fw_if.fwd_rs1 ? fw_if.rd_mem_data : rf_if.rs1_data; + assign rs2_post_fwd = fw_if.fwd_rs2 ? fw_if.rd_mem_data : rf_if.rs2_data; + + + /****************** + * Sign Extensions + ******************/ + word_t imm_I_ext, imm_S_ext, imm_UJ_ext; + assign imm_I_ext = {{20{cu_if.imm_I[11]}}, cu_if.imm_I}; + assign imm_UJ_ext = {{11{cu_if.imm_UJ[20]}}, cu_if.imm_UJ}; + assign imm_S_ext = {{20{cu_if.imm_S[11]}}, cu_if.imm_S}; + + /********************************************** + * Jump Target Calculator and Associated Logic + **********************************************/ + word_t jump_addr /* verilator isolate_assignments */; + always_comb begin + if (cu_if.j_sel) begin + jump_if.base = fetch_ex_if.fetch_ex_reg.pc; + jump_if.offset = imm_UJ_ext; + jump_addr = jump_if.jal_addr; + end else begin + jump_if.base = rs1_post_fwd; + jump_if.offset = imm_I_ext; + jump_addr = jump_if.jalr_addr; + end + end + + /***** + * ALU + *****/ + word_t imm_or_shamt; + assign imm_or_shamt = (cu_if.imm_shamt_sel == 1'b1) ? cu_if.shamt : imm_I_ext; + assign alu_if.aluop = cu_if.alu_op; + logic mal_addr; + + always_comb begin + case (cu_if.alu_a_sel) + 2'd0: alu_if.port_a = rs1_post_fwd; + 2'd1: alu_if.port_a = imm_S_ext; + 2'd2: alu_if.port_a = fetch_ex_if.fetch_ex_reg.pc; + 2'd3: alu_if.port_a = '0; //Not Used + endcase + end + + always_comb begin + case (cu_if.alu_b_sel) + 2'd0: alu_if.port_b = rs1_post_fwd; + 2'd1: alu_if.port_b = rs2_post_fwd; + 2'd2: alu_if.port_b = imm_or_shamt; + 2'd3: alu_if.port_b = cu_if.imm_U; + endcase + end + + + // FU output mux -- feeds into pipeline register + // Add to this when more FUs are added + // TODO: Make this nicer, with enum for FU selection + assign ex_out = (cu_if.rv32m_control.select) ? rv32m_out : alu_if.port_out; + + /************************* + * Register File Writeback + *************************/ + assign rf_if.w_data = ex_mem_if.reg_wdata; + assign rf_if.rd = ex_mem_if.rd_m; + assign rf_if.wen = ex_mem_if.reg_write && !hazard_if.ex_mem_stall; // TODO: The second signal only matters for some miniscule power reduction by not writing each cycle. This is correct with only the wen signal due to no loop from reg read to reg write + + /*********************************************** + * Branch Target Resolution and Associated Logic + ***********************************************/ + word_t resolved_addr; + logic branch_taken; + word_t branch_addr; + word_t brj_addr; + + assign branch_if.rs1_data = rs1_post_fwd;//rf_if.rs1_data; + assign branch_if.rs2_data = rs2_post_fwd; //rf_if.rs2_data; + assign branch_if.pc = fetch_ex_if.fetch_ex_reg.pc; + assign branch_if.imm_sb = cu_if.imm_SB; + assign branch_if.branch_type = cu_if.branch_type; + + // Mux resource based on if RISC-MGMT is trying to access it + assign branch_taken = branch_if.branch_taken;//rm_if.req_br_j ? rm_if.branch_jump : branch_if.branch_taken; + assign branch_addr = branch_if.branch_addr;//rm_if.req_br_j ? rm_if.br_j_addr : branch_if.branch_addr; + //assign rm_if.pc = fetch_ex_if.fetch_ex_reg.pc; + + assign resolved_addr = branch_if.branch_taken ? branch_addr : fetch_ex_if.fetch_ex_reg.pc4; + assign brj_addr = cu_if.ex_pc_sel ? jump_addr : resolved_addr; + //assign brj_addr = ((cu_if.ex_pc_sel == 1'b1) && ~rm_if.req_br_j) ? + // jump_addr : resolved_addr; + + //assign hazard_if.mispredict = fetch_ex_if.fetch_ex_reg.prediction ^ branch_taken; + + /******************************** + * Hazard/Forwarding Unit Signals + *********************************/ + assign hazard_if.rs1_e = rf_if.rs1; + assign hazard_if.rs2_e = rf_if.rs2; + + assign fw_if.rs1_e = rf_if.rs1; + assign fw_if.rs2_e = rf_if.rs2; + + assign hazard_if.pc_e = fetch_ex_if.fetch_ex_reg.pc; + assign hazard_if.ex_busy = (rv32m_busy && cu_if.rv32m_control.select); // Add & conditions here for other FUs that can stall + assign hazard_if.valid_e = fetch_ex_if.fetch_ex_reg.valid; + + + // TODO: NEW + always_ff @(posedge CLK, negedge nRST) begin + if(!nRST) begin + /*verilator lint_off ENUMVALUE*/ + ex_mem_if.ex_mem_reg <= '{default: '0}; + /*verilator lint_on ENUMVALUE*/ + end else begin + // TODO: This register is ~180b. Not awful, but can it be smaller? + // PS: Does it even matter? Synth. tools may be able to merge regs. + if(!hazard_if.ex_mem_flush && !hazard_if.ex_mem_stall) begin + // TODO: Handle case of exceptions earlier in the pipe being passed on to handle in the last stage + // Single bit control signals -- squash these if we have an exception + // Only need to check illegal since it's the only "new" exception we have + ex_mem_if.ex_mem_reg.valid <= fetch_ex_if.fetch_ex_reg.valid; + if(!cu_if.illegal_insn) begin + ex_mem_if.ex_mem_reg.branch <= cu_if.branch; + ex_mem_if.ex_mem_reg.prediction <= fetch_ex_if.fetch_ex_reg.prediction; + ex_mem_if.ex_mem_reg.branch_taken <= branch_if.branch_taken; + ex_mem_if.ex_mem_reg.dren <= cu_if.dren; + ex_mem_if.ex_mem_reg.dwen <= cu_if.dwen; + ex_mem_if.ex_mem_reg.reg_write <= cu_if.wen; + ex_mem_if.ex_mem_reg.ifence <= cu_if.ifence; + ex_mem_if.ex_mem_reg.jump <= cu_if.jump; + ex_mem_if.ex_mem_reg.halt <= cu_if.halt; + ex_mem_if.ex_mem_reg.csr_swap <= cu_if.csr_swap; + ex_mem_if.ex_mem_reg.csr_clr <= cu_if.csr_clr; + ex_mem_if.ex_mem_reg.csr_set <= cu_if.csr_set; + ex_mem_if.ex_mem_reg.csr_imm <= cu_if.csr_imm; + ex_mem_if.ex_mem_reg.csr_read_only <= (rf_if.rs1 == '0) || (cu_if.zimm == '0); + ex_mem_if.ex_mem_reg.breakpoint <= cu_if.breakpoint; + ex_mem_if.ex_mem_reg.ecall_insn <= cu_if.ecall_insn; + ex_mem_if.ex_mem_reg.ret_insn <= cu_if.ret_insn; + ex_mem_if.ex_mem_reg.wfi_insn <= cu_if.wfi; + ex_mem_if.ex_mem_reg.was_compressed <= 1'b0; // TODO: RV32C support + end + ex_mem_if.ex_mem_reg.illegal_insn <= cu_if.illegal_insn; + ex_mem_if.ex_mem_reg.badaddr <= fetch_ex_if.fetch_ex_reg.badaddr; + ex_mem_if.ex_mem_reg.mal_insn <= fetch_ex_if.fetch_ex_reg.mal_insn; + ex_mem_if.ex_mem_reg.fault_insn <= fetch_ex_if.fetch_ex_reg.fault_insn; + + // Bit vectors + ex_mem_if.ex_mem_reg.w_sel <= cu_if.w_sel; + ex_mem_if.ex_mem_reg.zimm <= cu_if.zimm; + ex_mem_if.ex_mem_reg.rd_m <= cu_if.rd; + ex_mem_if.ex_mem_reg.load_type <= cu_if.load_type; + ex_mem_if.ex_mem_reg.csr_addr <= cu_if.csr_addr; + + // Word sized members + ex_mem_if.ex_mem_reg.brj_addr <= brj_addr; + ex_mem_if.ex_mem_reg.port_out <= ex_out; + ex_mem_if.ex_mem_reg.rs1_data <= rs1_post_fwd; + ex_mem_if.ex_mem_reg.rs2_data <= rs2_post_fwd; + ex_mem_if.ex_mem_reg.instr <= cu_if.instr; + ex_mem_if.ex_mem_reg.pc <= fetch_ex_if.fetch_ex_reg.pc; + ex_mem_if.ex_mem_reg.pc4 <= fetch_ex_if.fetch_ex_reg.pc4; + ex_mem_if.ex_mem_reg.imm_U <= cu_if.imm_U; + + // CPU Tracker + ex_mem_if.ex_mem_reg.tracker_signals.opcode <= cu_if.opcode; + ex_mem_if.ex_mem_reg.tracker_signals.imm_SB <= cu_if.imm_SB; + ex_mem_if.ex_mem_reg.tracker_signals.imm_S <= cu_if.imm_S; + ex_mem_if.ex_mem_reg.tracker_signals.imm_I <= cu_if.imm_I; + ex_mem_if.ex_mem_reg.tracker_signals.imm_UJ <= cu_if.imm_UJ; + ex_mem_if.ex_mem_reg.tracker_signals.imm_U <= cu_if.imm_U; + + end else if(hazard_if.ex_mem_flush && !hazard_if.ex_mem_stall) begin + /*verilator lint_off ENUMVALUE*/ + ex_mem_if.ex_mem_reg <= '{default: '0}; + /*verilator lint_on ENUMVALUE*/ + end + // else: retain state + end + end + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + /*assign sparce_if.wb_data = rf_if.w_data; + assign sparce_if.wb_en = rf_if.wen; + assign sparce_if.sasa_data = rf_if.rs2_data; + assign sparce_if.sasa_addr = alu_if.port_out; + assign sparce_if.sasa_wen = cu_if.dwen; + assign sparce_if.rd = rf_if.rd;*/ + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_fetch_stage.sv b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv new file mode 100644 index 000000000..34fed7aa2 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_fetch_stage.sv @@ -0,0 +1,159 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_fetch_stage.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/19/2016 +* Description: Fetch stage for the two stage pipeline +*/ + +`include "stage3_fetch_execute_if.vh" +`include "stage3_hazard_unit_if.vh" +`include "predictor_pipeline_if.vh" +`include "generic_bus_if.vh" +`include "component_selection_defines.vh" +`include "cache_control_if.vh" +`include "rv32c_if.vh" +`include "prv_pipeline_if.vh" + +module stage3_fetch_stage ( + input logic CLK, + nRST, + stage3_fetch_execute_if.fetch fetch_ex_if, + stage3_mem_pipe_if.fetch mem_fetch_if, + stage3_hazard_unit_if.fetch hazard_if, + predictor_pipeline_if.access predict_if, + generic_bus_if.cpu igen_bus_if, + sparce_pipeline_if.pipe_fetch sparce_if, + rv32c_if.fetch rv32cif, + prv_pipeline_if.fetch prv_pipe_if +); + import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; + + parameter logic [31:0] RESET_PC = 32'h80000000; + + word_t pc, pc4or2, npc, instr; + + //Send exceptions through pipeline + logic mal_addr; + logic fault_insn; + logic mal_insn; + word_t badaddr; + + //PC logic + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + pc <= RESET_PC; + end else if (hazard_if.pc_en /*| rv32cif.done_earlier*/) begin + pc <= npc; + end + end + + //RV32C + assign rv32cif.inst = igen_bus_if.rdata; + assign rv32cif.inst_arrived = hazard_if.if_ex_flush == 0 & hazard_if.if_ex_stall == 0; + assign rv32cif.reset_en = hazard_if.insert_priv_pc | hazard_if.rollback | sparce_if.skipping + | hazard_if.npc_sel | predict_if.predict_taken; + assign rv32cif.pc_update = hazard_if.pc_en; + assign rv32cif.reset_pc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (hazard_if.rollback ? mem_fetch_if.pc4 + : (sparce_if.skipping ? sparce_if.sparce_target + : (hazard_if.npc_sel ? mem_fetch_if.brj_addr + : (predict_if.predict_taken ? predict_if.target_addr + : pc4or2)))); + assign rv32cif.reset_pc_val = RESET_PC; + + assign pc4or2 = (rv32cif.rv32c_ena & (rv32cif.result[1:0] != 2'b11)) ? (pc + 2) : (pc + 4); + assign predict_if.current_pc = pc; + assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (hazard_if.rollback ? mem_fetch_if.pc4 + : (sparce_if.skipping ? sparce_if.sparce_target + : (hazard_if.npc_sel ? mem_fetch_if.brj_addr + : (predict_if.predict_taken ? predict_if.target_addr + : rv32cif.rv32c_ena ? rv32cif.nextpc + : pc4or2)))); + + //Instruction Access logic + assign hazard_if.i_mem_busy = igen_bus_if.busy && !fault_insn; + assign igen_bus_if.addr = rv32cif.rv32c_ena ? rv32cif.imem_pc : pc; + assign igen_bus_if.ren = hazard_if.iren && !rv32cif.done_earlier && !hazard_if.suppress_iren; + assign igen_bus_if.wen = 1'b0; + assign igen_bus_if.byte_en = 4'b1111; + assign igen_bus_if.wdata = '0; + + + assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); + assign fault_insn = prv_pipe_if.prot_fault_i || (igen_bus_if.ren && igen_bus_if.error); // TODO: Set this up to fault on bus error + assign mal_insn = mal_addr; + assign badaddr = igen_bus_if.addr; + assign hazard_if.pc_f = pc; + assign hazard_if.rv32c_ready = rv32cif.done_earlier && rv32cif.rv32c_ena; // TODO: Is rv32cif.done needed? Seems like it coincides with busy = 0 + + + //Fetch Execute Pipeline Signals + word_t instr_to_ex; + assign instr_to_ex = rv32cif.rv32c_ena ? rv32cif.result : igen_bus_if.rdata; + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) fetch_ex_if.fetch_ex_reg <= '0; + else if (hazard_if.if_ex_flush && !hazard_if.if_ex_stall) fetch_ex_if.fetch_ex_reg <= '0; + else if (!hazard_if.if_ex_stall) begin + if(mal_insn || fault_insn) begin + // Squash to NOP if exception + // Still valid for exception handling + fetch_ex_if.fetch_ex_reg.instr <= '0; + end else begin + fetch_ex_if.fetch_ex_reg.instr <= instr_to_ex; + end + fetch_ex_if.fetch_ex_reg.valid <= 1'b1; + fetch_ex_if.fetch_ex_reg.token <= 1'b1; + fetch_ex_if.fetch_ex_reg.mal_insn <= mal_insn; + fetch_ex_if.fetch_ex_reg.fault_insn <= fault_insn; + fetch_ex_if.fetch_ex_reg.badaddr <= badaddr; + fetch_ex_if.fetch_ex_reg.pc <= pc; + fetch_ex_if.fetch_ex_reg.pc4 <= pc4or2; + fetch_ex_if.fetch_ex_reg.prediction <= predict_if.predict_taken; // TODO: This is just wrong... + end + end + + + // Send memory protection signals + assign prv_pipe_if.iren = hazard_if.iren; + assign prv_pipe_if.iaddr = igen_bus_if.addr; + assign prv_pipe_if.i_acc_width = WordAcc; + + // Choose the endianness of the data coming into the processor + generate + if (BUS_ENDIANNESS == "big") assign instr = igen_bus_if.rdata; + else if (BUS_ENDIANNESS == "little") + endian_swapper ltb_endian ( + .word_in(igen_bus_if.rdata), + .word_out(instr) + ); + endgenerate + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + + assign sparce_if.pc = pc; + assign sparce_if.rdata = igen_bus_if.rdata; +endmodule + + diff --git a/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv b/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv new file mode 100644 index 000000000..0f5bf141f --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_forwarding_unit.sv @@ -0,0 +1,14 @@ + +module stage3_forwarding_unit( + stage3_forwarding_unit_if.fw_unit fw_if +); + + logic rs1_match, rs2_match; + + assign rs1_match = (fw_if.rd_m != 0) && (fw_if.rs1_e == fw_if.rd_m); + assign rs2_match = (fw_if.rd_m != 0) && (fw_if.rs2_e == fw_if.rd_m); + + assign fw_if.fwd_rs1 = rs1_match && fw_if.reg_write && !fw_if.load; + assign fw_if.fwd_rs2 = rs2_match && fw_if.reg_write && !fw_if.load; + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_hazard_unit.sv b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv new file mode 100644 index 000000000..dc82f33a4 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_hazard_unit.sv @@ -0,0 +1,188 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: stage3_hazard_unit.sv +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/14/2016 +* Description: Hazard unit that controls the flushing and stalling of +* the stages of the Two Stage Pipeline +*/ + +`include "stage3_hazard_unit_if.vh" +`include "prv_pipeline_if.vh" +//`include "risc_mgmt_if.vh" + +module stage3_hazard_unit ( + stage3_hazard_unit_if.hazard_unit hazard_if, + prv_pipeline_if.hazard prv_pipe_if + //risc_mgmt_if.ts_hazard rm_if, + //sparce_pipeline_if.hazard sparce_if +); + import alu_types_pkg::*; + import rv32i_types_pkg::*; + + // Pipeline hazard signals + logic dmem_access; + logic branch_jump; + logic wait_for_imem; + logic wait_for_dmem; + logic rs1_match; + logic rs2_match; + logic mem_use_stall; + logic cannot_forward; + logic fetch_busy; + logic execute_busy; + logic mem_busy; + + // IRQ/Exception hazard signals + logic ex_flush_hazard; + logic exception; + logic intr; + word_t epc; + + // TODO: RISC-MGMT + //logic rmgmt_stall; + + //assign rm_if.if_ex_enable = ~hazard_if.if_ex_stall; + //assign rmgmt_stall = rm_if.memory_stall | rm_if.execute_stall; + + // Hazard detection + assign rs1_match = (hazard_if.rs1_e == hazard_if.rd_m) && (hazard_if.rd_m != 0); + assign rs2_match = (hazard_if.rs2_e == hazard_if.rd_m) && (hazard_if.rd_m != 0); + assign cannot_forward = (hazard_if.dren || hazard_if.csr_read); // cannot forward outputs generated in mem stage + + assign dmem_access = (hazard_if.dren || hazard_if.dwen); + assign branch_jump = hazard_if.jump || (hazard_if.branch && hazard_if.mispredict); + assign wait_for_imem = hazard_if.iren && hazard_if.i_mem_busy && !hazard_if.suppress_iren && !hazard_if.rv32c_ready; // don't wait for imem when rv32c is done early + assign wait_for_dmem = dmem_access && hazard_if.d_mem_busy && !hazard_if.suppress_data; + assign mem_use_stall = hazard_if.reg_write && cannot_forward && (rs1_match || rs2_match); + + assign hazard_if.npc_sel = branch_jump; + + + + /* Hazards due to Interrupts/Exceptions */ + assign prv_pipe_if.ret = hazard_if.ret; + assign exception = hazard_if.fault_insn | hazard_if.mal_insn // | prv_pipe_if.prot_fault_i + | hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l + | hazard_if.fault_s | hazard_if.mal_s | hazard_if.breakpoint + | hazard_if.env | prv_pipe_if.prot_fault_l | prv_pipe_if.prot_fault_s; + + assign intr = ~exception & prv_pipe_if.intr; + + assign prv_pipe_if.pipe_clear = 1'b1; // TODO: What is this for?//exception; //| ~(hazard_if.token_ex | rm_if.active_insn); + assign ex_flush_hazard = ((intr || exception) && !wait_for_dmem) || exception || prv_pipe_if.ret || (hazard_if.ifence && !hazard_if.fence_stall); // I-fence must flush to force re-fetch of in-flight instructions. Flush will happen after stallling for cache response. + + assign hazard_if.insert_priv_pc = prv_pipe_if.insert_pc; + assign hazard_if.priv_pc = prv_pipe_if.priv_pc; + + assign hazard_if.iren = 1'b1; + // TODO: Removed intr as cause of suppression -- is this OK? + //assign hazard_if.suppress_iren = branch_jump || exception || prv_pipe_if.ret || prv_pipe_if.insert_pc; // prevents a false instruction request from being sent when pipeline flush imminent + assign hazard_if.suppress_iren = branch_jump || ex_flush_hazard || prv_pipe_if.ret || prv_pipe_if.insert_pc; // prevents a false instruction request from being sent when pipeline flush imminent + assign hazard_if.suppress_data = exception; // suppress data transfer on interrupt/exception. Exception case: prevent read/write of faulting location. Interrupt: make symmetric with exceptions for ease + + assign hazard_if.rollback = (hazard_if.ifence && !hazard_if.fence_stall); // TODO: more cases for CSRs that affect I-fetch (PMA/PMP registers) + + // EPC priority logic + assign epc = hazard_if.valid_m && !intr ? hazard_if.pc_m : + (hazard_if.valid_e ? hazard_if.pc_e : hazard_if.pc_f); + + /* Send Exception notifications to Prv Block */ + // TODO: Correct execution of exceptions + assign prv_pipe_if.wb_enable = !hazard_if.if_ex_stall | + hazard_if.jump | + hazard_if.branch; //Because 2 stages + + assign prv_pipe_if.fault_insn = hazard_if.fault_insn;// | prv_pipe_if.prot_fault_i; + assign prv_pipe_if.mal_insn = hazard_if.mal_insn; + assign prv_pipe_if.illegal_insn = hazard_if.illegal_insn; + assign prv_pipe_if.fault_l = hazard_if.fault_l | prv_pipe_if.prot_fault_l; + assign prv_pipe_if.mal_l = hazard_if.mal_l; + assign prv_pipe_if.fault_s = hazard_if.fault_s | prv_pipe_if.prot_fault_s; + assign prv_pipe_if.mal_s = hazard_if.mal_s; + assign prv_pipe_if.breakpoint = hazard_if.breakpoint; + assign prv_pipe_if.env = hazard_if.env; + assign prv_pipe_if.wfi = hazard_if.wfi; + assign prv_pipe_if.ex_rmgmt = 1'b0;//rm_if.exception; + + assign prv_pipe_if.ex_rmgmt_cause = '0;//rm_if.ex_cause; + assign prv_pipe_if.epc = epc; + assign prv_pipe_if.badaddr = hazard_if.badaddr; + + + /* + * Pipeline control signals + * + * Control hazard (Exception, Jump, Mispredict): F/E -> Flush, E/M -> Flush + * - Special case: interrupt. Async, don't know where the oldest insn is. Interrupts must assume the memory op will go through, so flush only I/F + * Data hazard (unforwardable, load/CSR read): F/E -> Stall, E/M -> Flush + * Waiting (i.e. slow dmem access, fence, etc.): + * - If fetch stage slow, flush if_ex so in-flight instructions may finish (insert bubbles) + * - If ex stage slow, flush ex_mem so in-flight instruction may finish (insert bubbles). Stall if_ex + * - If mem stage slow, stall everyone + * - Halt - stall everyone + * Note: Stall of later stage implies stall of earlier stage. + * PC should not update if: + * - fetch_if is stalling (can't pass instruction on) + * PC should update if: + * - fetch is not stalling + * - there is a forced redirect + */ + + /*assign hazard_if.pc_en = (~wait_for_dmem & ~wait_for_imem & ~hazard_if.halt & ~ex_flush_hazard + & ~rmgmt_stall & ~hazard_if.fence_stall) + | branch_jump | prv_pipe_if.insert_pc | prv_pipe_if.ret | hazard_if.rollback;*/ + // Unforunately, pc_en is negative logic of stalling + assign hazard_if.pc_en = (!hazard_if.if_ex_stall && !wait_for_imem) // Normal case: next stage free, not waiting for instruction + || branch_jump + || ex_flush_hazard + || prv_pipe_if.insert_pc + || prv_pipe_if.ret;//) //&& !wait_for_imem; + + assign hazard_if.if_ex_flush = ex_flush_hazard // control hazard + || branch_jump // control hazard + || (wait_for_imem && !hazard_if.ex_mem_stall); // Flush if fetch stage lagging, but ex/mem are moving + + assign hazard_if.ex_mem_flush = ex_flush_hazard // Control hazard + || branch_jump // Control hazard + //|| (mem_use_stall && !hazard_if.d_mem_busy) // Data hazard -- flush once data memory is no longer busy (request complete) + || (hazard_if.if_ex_stall && !hazard_if.ex_mem_stall); // if_ex_stall covers mem_use stall condition + + + assign hazard_if.if_ex_stall = hazard_if.ex_mem_stall // Stall this stage if next stage is stalled + // || (wait_for_imem && !dmem_access) // ??? + //& (~ex_flush_hazard | e_ex_stage) // ??? + //|| rm_if.execute_stall // + || (hazard_if.ex_busy && !ex_flush_hazard && !branch_jump) // Ugly case -- need to flush for control hazards when X busy, but other cases require stalling to take priority to prevent data loss (e.g. slow instruction fetch, valid insn in X, load in M --> giving flush priority would destroy insn in X) + || mem_use_stall + || hazard_if.fence_stall; // Data hazard -- stall until dependency clears (from E/M flush after writeback) + // TODO: Exceptions + assign hazard_if.ex_mem_stall = wait_for_dmem // Second clause ensures we finish memory op on interrupt condition + || hazard_if.fence_stall + || hazard_if.halt; + //|| branch_jump && wait_for_imem; // This can be removed once there is I$. Solves problem where + // stale I-request returns after PC is redirected + // TODO: Enforce mutual exclusivity of these signals with assertion + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + //assign sparce_if.if_ex_enable = rm_if.if_ex_enable; + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_mem_stage.sv b/source_code/pipelines/stage3/source/stage3_mem_stage.sv new file mode 100644 index 000000000..e937e1d1c --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_mem_stage.sv @@ -0,0 +1,273 @@ + +`include "stage3_hazard_unit_if.vh" +`include "stage3_mem_pipe_if.vh" +`include "generic_bus_if.vh" +`include "predictor_pipeline_if.vh" +`include "cache_control_if.vh" +`include "prv_pipeline_if.vh" + +module stage3_mem_stage( + input CLK, + input nRST, + stage3_mem_pipe_if.mem ex_mem_if, + stage3_hazard_unit_if.mem hazard_if, + stage3_forwarding_unit_if.mem fw_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if.pipe prv_pipe_if, + cache_control_if.pipeline cc_if, + predictor_pipeline_if.update predict_if, + output logic halt, + output logic wfi +); + + import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; + + /*************** + * Branch Update + ****************/ + assign predict_if.update_predictor = ex_mem_if.ex_mem_reg.branch; + assign predict_if.prediction = ex_mem_if.ex_mem_reg.prediction; + assign predict_if.branch_result = ex_mem_if.ex_mem_reg.branch_taken; + assign predict_if.update_addr = ex_mem_if.ex_mem_reg.brj_addr; + + + + + /************* + * Data Access + **************/ + word_t store_swapped; + word_t dload_ext; + logic mal_addr; + logic [1:0] byte_offset; + logic [3:0] byte_en, byte_en_temp, byte_en_standard; + + + + // TODO: RISC-MGMT + assign dgen_bus_if.ren = ex_mem_if.ex_mem_reg.dren && !hazard_if.suppress_data; + assign dgen_bus_if.wen = ex_mem_if.ex_mem_reg.dwen && !hazard_if.suppress_data; + assign dgen_bus_if.byte_en = byte_en; + assign dgen_bus_if.addr = ex_mem_if.ex_mem_reg.port_out; + assign byte_offset = ex_mem_if.ex_mem_reg.port_out[1:0]; + + // TODO: RISC-MGMT + assign byte_en_temp = byte_en_standard; + + // Address alignment + always_comb begin + if (byte_en == 4'hf) mal_addr = (dgen_bus_if.addr[1:0] != 2'b00); + else if (byte_en == 4'h3 || byte_en == 4'hc) begin + mal_addr = (dgen_bus_if.addr[1:0] == 2'b01 || dgen_bus_if.addr[1:0] == 2'b11); + end else mal_addr = 1'b0; + end + + endian_swapper store_swap( + .word_in(ex_mem_if.ex_mem_reg.rs2_data), + .word_out(store_swapped) + ); + + dmem_extender dmem_ext( + .dmem_in(dgen_bus_if.rdata), + .load_type(ex_mem_if.ex_mem_reg.load_type), + .byte_en(byte_en), + .ext_out(dload_ext) + ); + + always_comb begin : LOAD_TYPE + case (ex_mem_if.ex_mem_reg.load_type) + LB, LBU: begin + case (byte_offset) + 2'b00: byte_en_standard = 4'b0001; + 2'b01: byte_en_standard = 4'b0010; + 2'b10: byte_en_standard = 4'b0100; + 2'b11: byte_en_standard = 4'b1000; + default: byte_en_standard = 4'b0000; + endcase + end + + LH, LHU: begin + case (byte_offset) + 2'b00: byte_en_standard = 4'b0011; + 2'b10: byte_en_standard = 4'b1100; + default: byte_en_standard = 4'b0000; + endcase + end + + LW: begin + byte_en_standard = 4'b1111; + end + + default: byte_en_standard = 4'b0000; + endcase + end : LOAD_TYPE + + // TODO: RISC-MGMT + always_comb begin : STORE_TYPE + case(ex_mem_if.ex_mem_reg.load_type) + LB: dgen_bus_if.wdata = {4{ex_mem_if.ex_mem_reg.rs2_data[7:0]}}; + LH: dgen_bus_if.wdata = {2{ex_mem_if.ex_mem_reg.rs2_data[15:0]}}; + LW: dgen_bus_if.wdata = ex_mem_if.ex_mem_reg.rs2_data; + default: dgen_bus_if.wdata = '0; + endcase + end : STORE_TYPE + + // Endianness + generate + if(BUS_ENDIANNESS == "big") begin : g_data_bus_be + assign byte_en = byte_en_temp; + end else if(BUS_ENDIANNESS == "little") begin : g_data_bus_le + assign byte_en = ex_mem_if.ex_mem_reg.dren ? byte_en_temp + : {byte_en_temp[0], byte_en_temp[1], + byte_en_temp[2], byte_en_temp[3]}; + end + endgenerate + + + /****************** + * Cache management + *******************/ + logic ifence_reg; + logic ifence_pulse; + logic iflushed, iflushed_next; + logic dflushed, dflushed_next; + logic iflush_done_reg, dflush_done_reg; + + always_ff @(posedge CLK, negedge nRST) begin + if(!nRST) begin + ifence_reg <= 1'b0; + iflushed <= 1'b1; + dflushed <= 1'b1; + end else begin + ifence_reg <= ex_mem_if.ex_mem_reg.ifence; + iflushed <= iflushed_next; + dflushed <= dflushed_next; + end + end + + assign ifence_pulse = ex_mem_if.ex_mem_reg.ifence & ~ifence_reg; + assign cc_if.icache_flush = ifence_pulse; + assign cc_if.dcache_flush = ifence_pulse; + // holds iflushed/dflushed high when done, resets to 0 on a pulse + always_comb begin + iflushed_next = iflushed; + dflushed_next = dflushed; + if (ifence_pulse) begin + iflushed_next = 0; + dflushed_next = 0; + end + if (cc_if.iflush_done) + iflushed_next = 1; + if (cc_if.dflush_done) + dflushed_next = 1; + end + + /************************ + * Hazard/Forwarding Unit + *************************/ + // Note: Some hazard unit signals are assigned below in the CSR section + assign hazard_if.d_mem_busy = dgen_bus_if.busy; + assign hazard_if.ifence = ex_mem_if.ex_mem_reg.ifence; + assign hazard_if.fence_stall = ifence_reg && !(iflushed && dflushed); + assign hazard_if.dren = ex_mem_if.ex_mem_reg.dren; + assign hazard_if.dwen = ex_mem_if.ex_mem_reg.dwen; + assign hazard_if.jump = ex_mem_if.ex_mem_reg.jump; + assign hazard_if.branch = ex_mem_if.ex_mem_reg.branch; + assign hazard_if.halt = ex_mem_if.ex_mem_reg.halt; + assign hazard_if.rd_m = ex_mem_if.ex_mem_reg.rd_m; + assign hazard_if.reg_write = ex_mem_if.ex_mem_reg.reg_write; + assign hazard_if.csr_read = prv_pipe_if.valid_write; + assign hazard_if.token_mem = 0; // TODO: RISC-MGMT + assign hazard_if.mispredict = ex_mem_if.ex_mem_reg.prediction ^ ex_mem_if.ex_mem_reg.branch_taken; + //assign hazard_if.pc = ex_mem_if.ex_mem_reg.pc; + + assign halt = ex_mem_if.ex_mem_reg.halt; + assign fw_if.rd_m = ex_mem_if.ex_mem_reg.rd_m; + assign fw_if.reg_write = ex_mem_if.reg_write; + assign fw_if.load = (ex_mem_if.ex_mem_reg.dren || ex_mem_if.ex_mem_reg.dwen); + + + /****** + * CSRs + *******/ + assign prv_pipe_if.swap = ex_mem_if.ex_mem_reg.csr_swap; + assign prv_pipe_if.clr = ex_mem_if.ex_mem_reg.csr_clr; + assign prv_pipe_if.set = ex_mem_if.ex_mem_reg.csr_set; + assign prv_pipe_if.read_only = ex_mem_if.ex_mem_reg.csr_read_only; + assign prv_pipe_if.wdata = ex_mem_if.ex_mem_reg.csr_imm ? {27'h0, ex_mem_if.ex_mem_reg.zimm} : ex_mem_if.ex_mem_reg.rs1_data; + assign prv_pipe_if.csr_addr = ex_mem_if.ex_mem_reg.csr_addr; + assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr + | prv_pipe_if.set) & ~hazard_if.ex_mem_stall; + assign prv_pipe_if.instr = (ex_mem_if.ex_mem_reg.instr != '0); + + assign hazard_if.fault_insn = ex_mem_if.ex_mem_reg.fault_insn; + assign hazard_if.mal_insn = ex_mem_if.ex_mem_reg.mal_insn; + assign hazard_if.illegal_insn = ex_mem_if.ex_mem_reg.illegal_insn || prv_pipe_if.invalid_priv_isn; + assign hazard_if.fault_l = ex_mem_if.ex_mem_reg.dren && dgen_bus_if.error; + assign hazard_if.mal_l = ex_mem_if.ex_mem_reg.dren & mal_addr; + assign hazard_if.fault_s = ex_mem_if.ex_mem_reg.dwen && dgen_bus_if.error; + assign hazard_if.mal_s = ex_mem_if.ex_mem_reg.dwen & mal_addr; + assign hazard_if.breakpoint = ex_mem_if.ex_mem_reg.breakpoint; + assign hazard_if.env = ex_mem_if.ex_mem_reg.ecall_insn; + assign hazard_if.ret = ex_mem_if.ex_mem_reg.ret_insn; + assign hazard_if.wfi = ex_mem_if.ex_mem_reg.wfi_insn; + assign hazard_if.badaddr = (hazard_if.fault_insn || hazard_if.mal_insn) ? ex_mem_if.ex_mem_reg.badaddr : dgen_bus_if.addr; + + // NEW + assign hazard_if.pc_m = ex_mem_if.ex_mem_reg.pc; + assign hazard_if.valid_m = ex_mem_if.ex_mem_reg.valid; + assign ex_mem_if.pc4 = ex_mem_if.ex_mem_reg.pc4; + + // Memory protection (doesn't consider RISC-MGMT) + assign prv_pipe_if.dren = ex_mem_if.ex_mem_reg.dren; + assign prv_pipe_if.dwen = ex_mem_if.ex_mem_reg.dwen; + assign prv_pipe_if.daddr = ex_mem_if.ex_mem_reg.port_out; + assign prv_pipe_if.d_acc_width = WordAcc; + + // TODO: Currently omitting SparCE + + /*********** + * Writeback + ************/ + assign ex_mem_if.brj_addr = ex_mem_if.ex_mem_reg.brj_addr; + assign ex_mem_if.reg_write = ex_mem_if.ex_mem_reg.reg_write; + assign ex_mem_if.rd_m = ex_mem_if.ex_mem_reg.rd_m; + + always_comb begin + // TODO: RISC-MGMT + case (ex_mem_if.ex_mem_reg.w_sel) + 3'd0: ex_mem_if.reg_wdata = dload_ext; + 3'd1: ex_mem_if.reg_wdata = ex_mem_if.ex_mem_reg.pc4; + 3'd2: ex_mem_if.reg_wdata = ex_mem_if.ex_mem_reg.imm_U; + 3'd3: ex_mem_if.reg_wdata = ex_mem_if.ex_mem_reg.port_out; + 3'd4: ex_mem_if.reg_wdata = prv_pipe_if.rdata; + default: ex_mem_if.reg_wdata = '0; + endcase + + // Forwarding unit + case (ex_mem_if.ex_mem_reg.w_sel) + 3'd1: fw_if.rd_mem_data = ex_mem_if.ex_mem_reg.pc4; + 3'd2: fw_if.rd_mem_data = ex_mem_if.ex_mem_reg.imm_U; + 3'd3: fw_if.rd_mem_data = ex_mem_if.ex_mem_reg.port_out; + 3'd4: fw_if.rd_mem_data = prv_pipe_if.rdata; + default: fw_if.rd_mem_data = '0; + endcase + end + + /************** + * CPU Tracking + ***************/ + logic wb_stall; + logic [2:0] funct3; + logic [11:0] funct12; + logic instr_30; + + // TODO: Fix up hazard unit + assign funct3 = ex_mem_if.ex_mem_reg.instr[14:12]; + assign funct12 = ex_mem_if.ex_mem_reg.instr[31:20]; + assign instr_30 = ex_mem_if.ex_mem_reg.instr[30]; + assign wb_stall = hazard_if.ex_mem_stall & ~hazard_if.jump & ~hazard_if.branch; // TODO: Is this right? + + +endmodule diff --git a/source_code/pipelines/stage3/source/stage3_types_pkg.sv b/source_code/pipelines/stage3/source/stage3_types_pkg.sv new file mode 100644 index 000000000..e7ddd0603 --- /dev/null +++ b/source_code/pipelines/stage3/source/stage3_types_pkg.sv @@ -0,0 +1,72 @@ + +package stage3_types_pkg; + + import rv32i_types_pkg::*; + import machine_mode_types_1_12_pkg::*; + + typedef struct packed { + logic valid; + logic token; + logic mal_insn; + logic fault_insn; + word_t pc; + word_t pc4; + word_t instr; + word_t prediction; + word_t badaddr; + } fetch_ex_t; + + typedef struct packed { + opcode_t opcode; + logic [12:0] imm_SB; + logic [11:0] imm_S; + logic [11:0] imm_I; + logic [20:0] imm_UJ; + logic [31:0] imm_U; + } tracker_ex_mem_t; + + // TODO: Instructions? + typedef struct packed { + logic valid; + logic branch; + logic prediction; + logic branch_taken; + logic dren; + logic dwen; + logic reg_write; + logic ifence; + logic jump; + logic halt; + logic csr_swap; + logic csr_clr; + logic csr_set; + logic csr_imm; + logic csr_read_only; + logic breakpoint; + logic ecall_insn; + logic ret_insn; + logic wfi_insn; + logic was_compressed; // Determine if PC should advance by 4 or 2, avoid passing PC and PC + (2/4) through pipeline + logic [2:0] w_sel; + logic [3:0] byte_en; // TODO: Where should this be generated? + logic [4:0] zimm; + logic [4:0] rd_m; + logic mal_insn; + logic fault_insn; + logic illegal_insn; + load_t load_type; + csr_addr_t csr_addr; + word_t brj_addr; + word_t port_out; + word_t rs1_data; + word_t rs2_data; + word_t instr; + word_t pc; + word_t pc4; + word_t imm_U; + word_t badaddr; + tracker_ex_mem_t tracker_signals; + // TODO: imm_U? Maybe needed + } ex_mem_t; + +endpackage diff --git a/source_code/pipelines/stage3/stage3.core b/source_code/pipelines/stage3/stage3.core new file mode 100644 index 000000000..7f34a96dd --- /dev/null +++ b/source_code/pipelines/stage3/stage3.core @@ -0,0 +1,36 @@ +CAPI=2: +name: socet:riscv:stage3:0.1.0 +description: Two-stage pipeline + +filesets: + rtl: + files: + - source/stage3_types_pkg.sv + - include/stage3_fetch_execute_if.vh : {is_include_file: true} + - include/stage3_mem_pipe_if.vh : {is_include_file: true} + - include/stage3_hazard_unit_if.vh : {is_include_file: true} + - include/stage3_forwarding_unit_if.vh : {is_include_file: true} + - source/stage3_fetch_stage.sv + - source/stage3_execute_stage.sv + - source/stage3_mem_stage.sv + - source/stage3_hazard_unit.sv + - source/stage3_forwarding_unit.sv + - source/stage3.sv + file_type: systemVerilogSource + + +targets: + default: &default + filesets: + - rtl + toplevel: stage3 + + lint: + <<: *default + description: Linting + default_tool: veriblelint + toplevel: stage3 + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search', '--waiver_files=stage3.waiver'] + diff --git a/source_code/pipelines/tspp/tspp.core b/source_code/pipelines/tspp/tspp.core new file mode 100644 index 000000000..e40b084b0 --- /dev/null +++ b/source_code/pipelines/tspp/tspp.core @@ -0,0 +1,29 @@ +CAPI=2: +name: socet:riscv:tspp:0.1.0 +description: Two-stage pipeline + +filesets: + rtl: + files: + - tspp.sv + - tspp_fetch_stage.sv + - tspp_execute_stage.sv + - tspp_hazard_unit.sv + file_type: systemVerilogSource + + +targets: + default: &default + filesets: + - rtl + toplevel: tspp + + lint: + <<: *default + description: Linting + default_tool: veriblelint + toplevel: tspp + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search', '--waiver_files=tspp.waiver'] + diff --git a/source_code/pipelines/tspp/tspp.sv b/source_code/pipelines/tspp/tspp.sv index 4af0ea493..68df83115 100644 --- a/source_code/pipelines/tspp/tspp.sv +++ b/source_code/pipelines/tspp/tspp.sv @@ -1,21 +1,21 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: tspp.sv -* +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 @@ -29,24 +29,28 @@ `include "prv_pipeline_if.vh" `include "risc_mgmt_if.vh" `include "cache_control_if.vh" +`include "sparce_pipeline_if.vh" +`include "rv32c_if.vh" module tspp ( - input logic CLK, nRST, - output logic halt, - generic_bus_if.cpu igen_bus_if, - generic_bus_if.cpu dgen_bus_if, - prv_pipeline_if prv_pipe_if, - predictor_pipeline_if predict_if, - risc_mgmt_if rm_if, - cache_control_if cc_if + input logic CLK, + nRST, + output logic halt, + generic_bus_if.cpu igen_bus_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if prv_pipe_if, + predictor_pipeline_if predict_if, + risc_mgmt_if rm_if, + cache_control_if cc_if, + sparce_pipeline_if sparce_if ); - //interface instantiations - tspp_fetch_execute_if fetch_ex_if(); - tspp_hazard_unit_if hazard_if(); + //interface instantiations + tspp_fetch_execute_if fetch_ex_if (); + tspp_hazard_unit_if hazard_if (); - //module instantiations - tspp_fetch_stage fetch_stage_i (.*); - tspp_execute_stage execute_stage_i (.*); - tspp_hazard_unit hazard_unit_i (.*); + //module instantiations + tspp_fetch_stage fetch_stage_i (.*); + tspp_execute_stage execute_stage_i (.*); + tspp_hazard_unit hazard_unit_i (.*); endmodule diff --git a/source_code/pipelines/tspp/tspp_execute_stage.sv b/source_code/pipelines/tspp/tspp_execute_stage.sv index b23357168..7965ba730 100644 --- a/source_code/pipelines/tspp/tspp_execute_stage.sv +++ b/source_code/pipelines/tspp/tspp_execute_stage.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 06/16/2016 -* Description: Execute Stage for the Two Stage Pipeline +* Description: Execute Stage for the Two Stage Pipeline */ `include "tspp_fetch_execute_if.vh" @@ -33,372 +33,402 @@ `include "prv_pipeline_if.vh" `include "risc_mgmt_if.vh" `include "cache_control_if.vh" - -module tspp_execute_stage( - input logic CLK, nRST, - tspp_fetch_execute_if.execute fetch_ex_if, - tspp_hazard_unit_if.execute hazard_if, - predictor_pipeline_if.update predict_if, - generic_bus_if.cpu dgen_bus_if, - prv_pipeline_if.pipe prv_pipe_if, - output logic halt, - risc_mgmt_if.ts_execute rm_if, - cache_control_if.pipeline cc_if +`include "rv32c_if.vh" + +module tspp_execute_stage ( + input logic CLK, + nRST, + tspp_fetch_execute_if.execute fetch_ex_if, + tspp_hazard_unit_if.execute hazard_if, + predictor_pipeline_if.update predict_if, + generic_bus_if.cpu dgen_bus_if, + prv_pipeline_if.pipe prv_pipe_if, + output logic halt, + risc_mgmt_if.ts_execute rm_if, + cache_control_if.pipeline cc_if, + sparce_pipeline_if.pipe_execute sparce_if, + rv32c_if.execute rv32cif, + output logic wfi ); - import rv32i_types_pkg::*; - - // Interface declarations - control_unit_if cu_if(); - rv32i_reg_file_if rf_if(); - alu_if alu_if(); - jump_calc_if jump_if(); - branch_res_if branch_if(); - - // Module instantiations - control_unit cu ( - .cu_if(cu_if), - .rf_if(rf_if), - .rmgmt_rsel_s_0(rm_if.rsel_s_0), - .rmgmt_rsel_s_1(rm_if.rsel_s_1), - .rmgmt_rsel_d(rm_if.rsel_d), - .rmgmt_req_reg_r(rm_if.req_reg_r), - .rmgmt_req_reg_w(rm_if.req_reg_w) - ); - rv32i_reg_file rf (.*); - alu alu (.*); - jump_calc jump_calc (.*); - - branch_res branch_res ( - .br_if(branch_if) - ); - - word_t store_swapped; - endian_swapper store_swap ( - .word_in(rf_if.rs2_data), - .word_out(store_swapped) - ); - - word_t dload_ext; - logic [3:0] byte_en, byte_en_temp, byte_en_standard; - dmem_extender dmem_ext ( - .dmem_in(dgen_bus_if.rdata), - .load_type(cu_if.load_type), - .byte_en(byte_en), - .ext_out(dload_ext) - ); - - - /******************************************************* + import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; + + // Interface declarations + control_unit_if cu_if (); + rv32i_reg_file_if rf_if (); + alu_if alu_if (); + jump_calc_if jump_if (); + branch_res_if branch_if (); + + // Module instantiations + control_unit cu ( + .cu_if(cu_if), + .rf_if(rf_if), + .rmgmt_rsel_s_0(rm_if.rsel_s_0), + .rmgmt_rsel_s_1(rm_if.rsel_s_1), + .rmgmt_rsel_d(rm_if.rsel_d), + .rmgmt_req_reg_r(rm_if.req_reg_r), + .rmgmt_req_reg_w(rm_if.req_reg_w) + ); + + assign wfi = cu_if.wfi; //Added by rkannank + assign prv_pipe_if.wfi = cu_if.wfi; + + generate + if (BASE_ISA == "RV32E") begin : g_rfile_select + rv32e_reg_file rf ( + .CLK, + .nRST, + .rf_if + ); + end else begin : g_rfile_select + rv32i_reg_file rf ( + .CLK, + .nRST, + .rf_if + ); + end + endgenerate + // rv32i_reg_file rf (.*); + alu alu (.*); + jump_calc jump_calc (.*); + + branch_res branch_res (.br_if(branch_if)); + + word_t store_swapped; + endian_swapper store_swap ( + .word_in (rf_if.rs2_data), + .word_out(store_swapped) + ); + + word_t dload_ext; + logic [3:0] byte_en, byte_en_temp, byte_en_standard; + dmem_extender dmem_ext ( + .dmem_in (dgen_bus_if.rdata), + .load_type(cu_if.load_type), + .byte_en (byte_en), + .ext_out (dload_ext) + ); + + + /******************************************************* * MISC RISC-MGMT Logic *******************************************************/ - assign rm_if.rdata_s_0 = rf_if.rs1_data; - assign rm_if.rdata_s_1 = rf_if.rs2_data; + assign rm_if.rdata_s_0 = rf_if.rs1_data; + assign rm_if.rdata_s_1 = rf_if.rs2_data; - /******************************************************* + /******************************************************** *** Choose the Endianness Coming into the processor *******************************************************/ - generate - if (BUS_ENDIANNESS == "big") - begin - assign byte_en = byte_en_temp; - end else if (BUS_ENDIANNESS == "little") - begin - assign byte_en = cu_if.dren ? byte_en_temp : + generate + if (BUS_ENDIANNESS == "big") begin : g_data_bus_be + assign byte_en = byte_en_temp; + end else if (BUS_ENDIANNESS == "little") begin : g_data_bus_le + assign byte_en = cu_if.dren ? byte_en_temp : {byte_en_temp[0], byte_en_temp[1], byte_en_temp[2], byte_en_temp[3]}; - end - endgenerate + end + endgenerate - assign cu_if.instr = fetch_ex_if.fetch_ex_reg.instr; - assign rm_if.insn = fetch_ex_if.fetch_ex_reg.instr; + //RV32C + assign rv32cif.inst16 = fetch_ex_if.fetch_ex_reg.instr[15:0]; + assign rv32cif.halt = cu_if.halt; + assign rv32cif.ex_busy = cu_if.dren | cu_if.dwen | rm_if.risc_mgmt_start; + assign cu_if.instr = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; + assign rm_if.insn = rv32cif.c_ena ? rv32cif.inst32 : fetch_ex_if.fetch_ex_reg.instr; - /******************************************************* - *** Sign Extensions - *******************************************************/ - word_t imm_I_ext, imm_S_ext, imm_UJ_ext; - assign imm_I_ext = {{20{cu_if.imm_I[11]}}, cu_if.imm_I}; - assign imm_UJ_ext = {{11{cu_if.imm_UJ[20]}}, cu_if.imm_UJ}; - assign imm_S_ext = {{20{cu_if.imm_S[11]}}, cu_if.imm_S}; - /******************************************************* - *** Jump Target Calculator and Associated Logic + /******************************************************* + *** Sign Extensions *******************************************************/ - word_t jump_addr; - always_comb begin - if (cu_if.j_sel) begin - jump_if.base = fetch_ex_if.fetch_ex_reg.pc; - jump_if.offset = imm_UJ_ext; - jump_addr = jump_if.jal_addr; - end else begin - jump_if.base = rf_if.rs1_data; - jump_if.offset = imm_I_ext; - jump_addr = jump_if.jalr_addr; + word_t imm_I_ext, imm_S_ext, imm_UJ_ext; + assign imm_I_ext = {{20{cu_if.imm_I[11]}}, cu_if.imm_I}; + assign imm_UJ_ext = {{11{cu_if.imm_UJ[20]}}, cu_if.imm_UJ}; + assign imm_S_ext = {{20{cu_if.imm_S[11]}}, cu_if.imm_S}; + + /******************************************************* + *** Jump Target Calculator and Associated Logic + *******************************************************/ + word_t jump_addr /* verilator isolate_assignments */; + always_comb begin + if (cu_if.j_sel) begin + jump_if.base = fetch_ex_if.fetch_ex_reg.pc; + jump_if.offset = imm_UJ_ext; + jump_addr = jump_if.jal_addr; + end else begin + jump_if.base = rf_if.rs1_data; + jump_if.offset = imm_I_ext; + jump_addr = jump_if.jalr_addr; + end end - end - /******************************************************* - *** ALU and Associated Logic + /******************************************************* + *** ALU and Associated Logic *******************************************************/ - word_t imm_or_shamt; - assign imm_or_shamt = (cu_if.imm_shamt_sel == 1'b1) ? cu_if.shamt : imm_I_ext; - assign alu_if.aluop = cu_if.alu_op; - logic mal_addr; - - always_comb begin - case (cu_if.alu_a_sel) - 2'd0: alu_if.port_a = rf_if.rs1_data; - 2'd1: alu_if.port_a = imm_S_ext; - 2'd2: alu_if.port_a = fetch_ex_if.fetch_ex_reg.pc; - 2'd3: alu_if.port_a = '0; //Not Used - endcase - end - - always_comb begin - case(cu_if.alu_b_sel) - 2'd0: alu_if.port_b = rf_if.rs1_data; - 2'd1: alu_if.port_b = rf_if.rs2_data; - 2'd2: alu_if.port_b = imm_or_shamt; - 2'd3: alu_if.port_b = cu_if.imm_U; - endcase - end - - always_comb begin - if(rm_if.req_reg_w) begin - rf_if.w_data = rm_if.reg_wdata; - end else begin - case(cu_if.w_sel) - 3'd0 : rf_if.w_data = dload_ext; - 3'd1 : rf_if.w_data = fetch_ex_if.fetch_ex_reg.pc4; - 3'd2 : rf_if.w_data = cu_if.imm_U; - 3'd3 : rf_if.w_data = alu_if.port_out; - 3'd4 : rf_if.w_data = prv_pipe_if.rdata; - default : rf_if.w_data = '0; - endcase + word_t imm_or_shamt; + assign imm_or_shamt = (cu_if.imm_shamt_sel == 1'b1) ? cu_if.shamt : imm_I_ext; + assign alu_if.aluop = cu_if.alu_op; + logic mal_addr; + + always_comb begin + case (cu_if.alu_a_sel) + 2'd0: alu_if.port_a = rf_if.rs1_data; + 2'd1: alu_if.port_a = imm_S_ext; + 2'd2: alu_if.port_a = fetch_ex_if.fetch_ex_reg.pc; + 2'd3: alu_if.port_a = '0; //Not Used + endcase + end + + always_comb begin + case (cu_if.alu_b_sel) + 2'd0: alu_if.port_b = rf_if.rs1_data; + 2'd1: alu_if.port_b = rf_if.rs2_data; + 2'd2: alu_if.port_b = imm_or_shamt; + 2'd3: alu_if.port_b = cu_if.imm_U; + endcase + end + + always_comb begin + if (rm_if.req_reg_w) begin + rf_if.w_data = rm_if.reg_wdata; + end else begin + case (cu_if.w_sel) + 3'd0: rf_if.w_data = dload_ext; + 3'd1: rf_if.w_data = fetch_ex_if.fetch_ex_reg.pc4; + 3'd2: rf_if.w_data = cu_if.imm_U; + 3'd3: rf_if.w_data = alu_if.port_out; + 3'd4: rf_if.w_data = prv_pipe_if.rdata; + default: rf_if.w_data = '0; + endcase + end end - end - assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) & (~hazard_if.if_ex_stall | hazard_if.npc_sel) & - ~(cu_if.dren & mal_addr); - /******************************************************* - *** Branch Target Resolution and Associated Logic + assign rf_if.rd = cu_if.rd; // Change to accommodate tspp and stage3 + assign rf_if.wen = (cu_if.wen | (rm_if.req_reg_w & rm_if.reg_w)) + & (~hazard_if.if_ex_stall | hazard_if.npc_sel | rv32cif.done_earlier) + & ~(cu_if.dren & mal_addr); + /******************************************************* + *** Branch Target Resolution and Associated Logic *******************************************************/ - word_t resolved_addr; - logic branch_taken; - word_t branch_addr; - - assign branch_if.rs1_data = rf_if.rs1_data; - assign branch_if.rs2_data = rf_if.rs2_data; - assign branch_if.pc = fetch_ex_if.fetch_ex_reg.pc; - assign branch_if.imm_sb = cu_if.imm_SB; - assign branch_if.branch_type = cu_if.branch_type; - - // Mux resource based on if RISC-MGMT is trying to access it - assign branch_taken = rm_if.req_br_j ? rm_if.branch_jump : branch_if.branch_taken; - assign branch_addr = rm_if.req_br_j ? rm_if.br_j_addr : branch_if.branch_addr; - assign rm_if.pc = fetch_ex_if.fetch_ex_reg.pc; - - assign resolved_addr = branch_if.branch_taken ? - branch_addr : fetch_ex_if.fetch_ex_reg.pc4; - - assign fetch_ex_if.brj_addr = ((cu_if.ex_pc_sel == 1'b1) && ~rm_if.req_br_j) ? + word_t resolved_addr; + logic branch_taken; + word_t branch_addr; + + assign branch_if.rs1_data = rf_if.rs1_data; + assign branch_if.rs2_data = rf_if.rs2_data; + assign branch_if.pc = fetch_ex_if.fetch_ex_reg.pc; + assign branch_if.imm_sb = cu_if.imm_SB; + assign branch_if.branch_type = cu_if.branch_type; + + // Mux resource based on if RISC-MGMT is trying to access it + assign branch_taken = rm_if.req_br_j ? rm_if.branch_jump : branch_if.branch_taken; + assign branch_addr = rm_if.req_br_j ? rm_if.br_j_addr : branch_if.branch_addr; + assign rm_if.pc = fetch_ex_if.fetch_ex_reg.pc; + + assign resolved_addr = branch_if.branch_taken ? branch_addr : fetch_ex_if.fetch_ex_reg.pc4; + + assign fetch_ex_if.brj_addr = ((cu_if.ex_pc_sel == 1'b1) && ~rm_if.req_br_j) ? jump_addr : resolved_addr; - - assign hazard_if.mispredict = fetch_ex_if.fetch_ex_reg.prediction ^ - branch_taken; - - /******************************************************* - *** Data Ram Interface Logic + assign hazard_if.mispredict = fetch_ex_if.fetch_ex_reg.prediction ^ branch_taken; + + + /******************************************************* + *** Data Ram Interface Logic *******************************************************/ - logic [1:0] byte_offset; - - // RISC-MGMT connection - assign rm_if.mem_load = dgen_bus_if.rdata; - - assign dgen_bus_if.ren = rm_if.req_mem ? rm_if.mem_ren : cu_if.dren & ~mal_addr; - assign dgen_bus_if.wen = rm_if.req_mem ? rm_if.mem_wen : cu_if.dwen & ~mal_addr; - assign byte_en_temp = rm_if.req_mem ? rm_if.mem_byte_en : byte_en_standard; - assign dgen_bus_if.byte_en = byte_en; - assign dgen_bus_if.addr = rm_if.req_mem ? rm_if.mem_addr : alu_if.port_out; - assign hazard_if.d_mem_busy = dgen_bus_if.busy; - assign byte_offset = alu_if.port_out[1:0]; - - always_comb begin - dgen_bus_if.wdata = '0; - if (rm_if.req_mem) - dgen_bus_if.wdata = rm_if.mem_store; - else begin - case(cu_if.load_type) // load_type can be used for store_type as well - LB: dgen_bus_if.wdata = {4{rf_if.rs2_data[7:0]}}; - LH: dgen_bus_if.wdata = {2{rf_if.rs2_data[15:0]}}; - LW: dgen_bus_if.wdata = rf_if.rs2_data; - endcase + logic [1:0] byte_offset; + + // RISC-MGMT connection + assign rm_if.mem_load = dgen_bus_if.rdata; + + assign dgen_bus_if.ren = rm_if.req_mem ? rm_if.mem_ren : cu_if.dren & ~mal_addr & ~prv_pipe_if.prot_fault_l; + assign dgen_bus_if.wen = rm_if.req_mem ? rm_if.mem_wen : cu_if.dwen & ~mal_addr & ~prv_pipe_if.prot_fault_s; + assign byte_en_temp = rm_if.req_mem ? rm_if.mem_byte_en : byte_en_standard; + assign dgen_bus_if.byte_en = byte_en; + assign dgen_bus_if.addr = rm_if.req_mem ? rm_if.mem_addr : alu_if.port_out; + assign hazard_if.d_mem_busy = dgen_bus_if.busy; + assign byte_offset = alu_if.port_out[1:0]; + + always_comb begin + dgen_bus_if.wdata = '0; + if (rm_if.req_mem) dgen_bus_if.wdata = rm_if.mem_store; + else begin + case (cu_if.load_type) // load_type can be used for store_type as well + LB: dgen_bus_if.wdata = {4{rf_if.rs2_data[7:0]}}; + LH: dgen_bus_if.wdata = {2{rf_if.rs2_data[15:0]}}; + LW: dgen_bus_if.wdata = rf_if.rs2_data; + endcase + end end - end - - - // Assign byte_en based on load type - // funct3 for loads and stores are the same bit positions - // byte_en is valid for both loads and stores - always_comb begin - unique case(cu_if.load_type) - LB : begin - unique case(byte_offset) - 2'b00 : byte_en_standard = 4'b0001; - 2'b01 : byte_en_standard = 4'b0010; - 2'b10 : byte_en_standard = 4'b0100; - 2'b11 : byte_en_standard = 4'b1000; - default : byte_en_standard = 4'b0000; - endcase - end - LBU : begin - unique case(byte_offset) - 2'b00 : byte_en_standard = 4'b0001; - 2'b01 : byte_en_standard = 4'b0010; - 2'b10 : byte_en_standard = 4'b0100; - 2'b11 : byte_en_standard = 4'b1000; - default : byte_en_standard = 4'b0000; - endcase - end - LH : begin - unique case(byte_offset) - 2'b00 : byte_en_standard = 4'b0011; - 2'b10 : byte_en_standard = 4'b1100; - default : byte_en_standard = 4'b0000; - endcase - end - LHU : begin - unique case(byte_offset) - 2'b00 : byte_en_standard = 4'b0011; - 2'b10 : byte_en_standard = 4'b1100; - default : byte_en_standard = 4'b0000; + + + // Assign byte_en based on load type + // funct3 for loads and stores are the same bit positions + // byte_en is valid for both loads and stores + always_comb begin + unique case (cu_if.load_type) + LB: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0001; + 2'b01: byte_en_standard = 4'b0010; + 2'b10: byte_en_standard = 4'b0100; + 2'b11: byte_en_standard = 4'b1000; + default: byte_en_standard = 4'b0000; + endcase + end + LBU: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0001; + 2'b01: byte_en_standard = 4'b0010; + 2'b10: byte_en_standard = 4'b0100; + 2'b11: byte_en_standard = 4'b1000; + default: byte_en_standard = 4'b0000; + endcase + end + LH: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0011; + 2'b10: byte_en_standard = 4'b1100; + default: byte_en_standard = 4'b0000; + endcase + end + LHU: begin + unique case (byte_offset) + 2'b00: byte_en_standard = 4'b0011; + 2'b10: byte_en_standard = 4'b1100; + default: byte_en_standard = 4'b0000; + endcase + end + LW: byte_en_standard = 4'b1111; + default: byte_en_standard = 4'b0000; endcase - end - LW: byte_en_standard = 4'b1111; - default : byte_en_standard = 4'b0000; - endcase - end - - // Fence instructions - - // posedge detector for ifence - // subsequent ifences will have same effect as a single fence - logic ifence_reg; - logic ifence_pulse; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - ifence_reg <= 1'b0; - else - ifence_reg <= cu_if.ifence; - end - - assign ifence_pulse = cu_if.ifence && ~ifence_reg; - assign cc_if.icache_flush = ifence_pulse; - assign cc_if.icache_clear = 1'b0; - assign cc_if.dcache_flush = ifence_pulse; - assign cc_if.dcache_clear = 1'b0; - - //regs to detect flush completion - logic dflushed, iflushed; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - iflushed <= 1'b1; - else if (ifence_pulse) - iflushed <= 1'b0; - else if (cc_if.iflush_done) - iflushed <= 1'b1; - end - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - dflushed <= 1'b1; - else if (ifence_pulse) - dflushed <= 1'b0; - else if (cc_if.dflush_done) - dflushed <= 1'b1; - end - - assign hazard_if.fence_stall = cu_if.ifence && (~dflushed || ~iflushed); - - /******************************************************* - *** Hazard Unit Interface Logic + end + + // Fence instructions + + // posedge detector for ifence + // subsequent ifences will have same effect as a single fence + logic ifence_reg; + logic ifence_pulse; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) ifence_reg <= 1'b0; + else ifence_reg <= cu_if.ifence; + end + + assign ifence_pulse = cu_if.ifence && ~ifence_reg; + assign cc_if.icache_flush = ifence_pulse; + assign cc_if.icache_clear = 1'b0; + assign cc_if.dcache_flush = ifence_pulse; + assign cc_if.dcache_clear = 1'b0; + + //regs to detect flush completion + logic dflushed, iflushed; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) iflushed <= 1'b1; + else if (ifence_pulse) iflushed <= 1'b0; + else if (cc_if.iflush_done) iflushed <= 1'b1; + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) dflushed <= 1'b1; + else if (ifence_pulse) dflushed <= 1'b0; + else if (cc_if.dflush_done) dflushed <= 1'b1; + end + + assign hazard_if.fence_stall = cu_if.ifence && (~dflushed || ~iflushed); + + /******************************************************* + *** Hazard Unit Interface Logic + *******************************************************/ + assign hazard_if.dren = cu_if.dren; + assign hazard_if.dwen = cu_if.dwen; + assign hazard_if.jump = cu_if.jump; + assign hazard_if.branch = cu_if.branch; + assign hazard_if.halt = halt; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) halt <= 1'b0; + else if (cu_if.halt) halt <= cu_if.halt; + end + + /******************************************************* + *** CSR / Priv Interface Logic *******************************************************/ - assign hazard_if.dren = cu_if.dren; - assign hazard_if.dwen = cu_if.dwen; - assign hazard_if.jump = cu_if.jump; - assign hazard_if.branch = cu_if.branch; - assign hazard_if.halt = halt; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - halt <= 1'b0; - else if (cu_if.halt) - halt <= cu_if.halt; - end - - /******************************************************* - *** CSR / Priv Interface Logic - *******************************************************/ - assign prv_pipe_if.swap = cu_if.csr_swap; - assign prv_pipe_if.clr = cu_if.csr_clr; - assign prv_pipe_if.set = cu_if.csr_set; - assign prv_pipe_if.wdata = cu_if.csr_imm ? {27'h0, cu_if.zimm} : rf_if.rs1_data; - assign prv_pipe_if.addr = cu_if.csr_addr; - assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr | + assign prv_pipe_if.swap = cu_if.csr_swap; + assign prv_pipe_if.clr = cu_if.csr_clr; + assign prv_pipe_if.set = cu_if.csr_set; + assign prv_pipe_if.read_only = (rf_if.rs1 == '0) || (cu_if.zimm == '0); // See Zicsr docs for more info + assign prv_pipe_if.wdata = cu_if.csr_imm ? {27'h0, cu_if.zimm} : rf_if.rs1_data; + assign prv_pipe_if.csr_addr = cu_if.csr_addr; + assign prv_pipe_if.valid_write = (prv_pipe_if.swap | prv_pipe_if.clr | prv_pipe_if.set) & ~hazard_if.if_ex_stall; - assign prv_pipe_if.instr = (cu_if.instr != '0); + assign prv_pipe_if.instr = (cu_if.instr != '0); - always_comb begin - if(byte_en == 4'hf) - mal_addr = (dgen_bus_if.addr[1:0] != 2'b00); - else if (byte_en == 4'h3 || byte_en == 4'hc) begin - mal_addr = (dgen_bus_if.addr[1:0] == 2'b01 || dgen_bus_if.addr[1:0] == 2'b11); + always_comb begin + if (byte_en == 4'hf) mal_addr = (dgen_bus_if.addr[1:0] != 2'b00); + else if (byte_en == 4'h3 || byte_en == 4'hc) begin + mal_addr = (dgen_bus_if.addr[1:0] == 2'b01 || dgen_bus_if.addr[1:0] == 2'b11); + end else mal_addr = 1'b0; end - else - mal_addr = 1'b0; - end - - //Send exceptions to Hazard Unit - assign hazard_if.illegal_insn = (cu_if.illegal_insn & ~rm_if.ex_token) | prv_pipe_if.invalid_csr; - assign hazard_if.fault_l = 1'b0; - assign hazard_if.mal_l = cu_if.dren & mal_addr; - assign hazard_if.fault_s = 1'b0; - assign hazard_if.mal_s = cu_if.dwen & mal_addr; - assign hazard_if.breakpoint = cu_if.breakpoint; - assign hazard_if.env_m = cu_if.ecall_insn; - assign hazard_if.ret = cu_if.ret_insn; - assign hazard_if.badaddr_e = dgen_bus_if.addr; - - assign hazard_if.epc_e = fetch_ex_if.fetch_ex_reg.pc; - assign hazard_if.token_ex = fetch_ex_if.fetch_ex_reg.token; - - /********************************************************* + + //Send exceptions to Hazard Unit + assign hazard_if.illegal_insn = (cu_if.illegal_insn & ~rm_if.ex_token) + | prv_pipe_if.invalid_priv_isn; + assign hazard_if.fault_l = prv_pipe_if.prot_fault_l; + assign hazard_if.mal_l = cu_if.dren & mal_addr; + assign hazard_if.fault_s = prv_pipe_if.prot_fault_s; + assign hazard_if.mal_s = cu_if.dwen & mal_addr; + assign hazard_if.breakpoint = cu_if.breakpoint; + assign hazard_if.env = cu_if.ecall_insn; + assign hazard_if.ret = cu_if.ret_insn; + assign hazard_if.badaddr_e = dgen_bus_if.addr; + + assign hazard_if.epc_e = fetch_ex_if.fetch_ex_reg.pc; + assign hazard_if.token_ex = fetch_ex_if.fetch_ex_reg.token; + + // setup signals for memory protection + // not considering risc-mgmt here TODO + assign prv_pipe_if.dren = cu_if.dren & ~mal_addr; + assign prv_pipe_if.dwen = cu_if.dwen & ~mal_addr; + assign prv_pipe_if.daddr = alu_if.port_out; + assign prv_pipe_if.d_acc_width = WordAcc; + + /********************************************************* *** Branch Predictor Logic *********************************************************/ - assign predict_if.update_predictor = cu_if.branch; - assign predict_if.prediction = fetch_ex_if.fetch_ex_reg.prediction; - assign predict_if.branch_result = branch_if.branch_taken; - //predict_if.update_addr = ; - - /********************************************************* + assign predict_if.update_predictor = cu_if.branch; + assign predict_if.prediction = fetch_ex_if.fetch_ex_reg.prediction; + assign predict_if.branch_result = branch_if.branch_taken; + //predict_if.update_addr = ; + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + assign sparce_if.wb_data = rf_if.w_data; + assign sparce_if.wb_en = rf_if.wen; + assign sparce_if.sasa_data = rf_if.rs2_data; + assign sparce_if.sasa_addr = alu_if.port_out; + assign sparce_if.sasa_wen = cu_if.dwen; + assign sparce_if.rd = rf_if.rd; + + /********************************************************* *** Signals for Bind Tracking - Read-Only, These don't affect execution *********************************************************/ - logic wb_stall; - logic [2:0] funct3; - logic [11:0] funct12; - logic instr_30; + logic wb_stall; + logic [2:0] funct3; + logic [11:0] funct12; + logic instr_30; - assign wb_stall = hazard_if.if_ex_stall & ~hazard_if.jump & ~hazard_if.branch; - assign funct3 = cu_if.instr[14:12]; - assign funct12 = cu_if.instr[31:20]; - assign instr_30 = cu_if.instr[30]; + assign wb_stall = hazard_if.if_ex_stall & ~hazard_if.jump & ~hazard_if.branch; + assign funct3 = cu_if.instr[14:12]; + assign funct12 = cu_if.instr[31:20]; + assign instr_30 = cu_if.instr[30]; endmodule - diff --git a/source_code/pipelines/tspp/tspp_fetch_stage.sv b/source_code/pipelines/tspp/tspp_fetch_stage.sv index 905aa0726..5421de016 100644 --- a/source_code/pipelines/tspp/tspp_fetch_stage.sv +++ b/source_code/pipelines/tspp/tspp_fetch_stage.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,75 +27,113 @@ `include "predictor_pipeline_if.vh" `include "generic_bus_if.vh" `include "component_selection_defines.vh" +`include "cache_control_if.vh" +`include "rv32c_if.vh" +`include "prv_pipeline_if.vh" module tspp_fetch_stage ( - input logic CLK, nRST, - tspp_fetch_execute_if.fetch fetch_ex_if, - tspp_hazard_unit_if.fetch hazard_if, - predictor_pipeline_if.access predict_if, - generic_bus_if.cpu igen_bus_if + input logic CLK, + nRST, + tspp_fetch_execute_if.fetch fetch_ex_if, + tspp_hazard_unit_if.fetch hazard_if, + predictor_pipeline_if.access predict_if, + generic_bus_if.cpu igen_bus_if, + sparce_pipeline_if.pipe_fetch sparce_if, + rv32c_if.fetch rv32cif, + prv_pipeline_if.fetch prv_pipe_if ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; + import pma_types_1_12_pkg::*; - //parameter RESET_PC = 32'h200; - parameter RESET_PC = 32'h80000000; + parameter logic [31:0] RESET_PC = 32'h80000000; - word_t pc, pc4, npc, instr; + word_t pc, pc4or2, npc, instr; - //PC logic + //PC logic - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) begin - pc <= RESET_PC; - end else if (hazard_if.pc_en) begin - pc <= npc; + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + pc <= RESET_PC; + end else if (hazard_if.pc_en | rv32cif.done_earlier) begin + pc <= npc; + end end - end - - assign pc4 = pc + 4; - assign predict_if.current_pc = pc; - assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc : (hazard_if.npc_sel ? fetch_ex_if.brj_addr : - (predict_if.predict_taken ? predict_if.target_addr : pc4)); - - //Instruction Access logic - assign hazard_if.i_mem_busy = igen_bus_if.busy; - assign igen_bus_if.addr = pc; - assign igen_bus_if.ren = hazard_if.iren; - assign igen_bus_if.wen = 1'b0; - assign igen_bus_if.byte_en = 4'b1111; - assign igen_bus_if.wdata = '0; - - //Fetch Execute Pipeline Signals - always_ff @ (posedge CLK, negedge nRST) begin - if (!nRST) - fetch_ex_if.fetch_ex_reg <= '0; - else if (hazard_if.if_ex_flush) - fetch_ex_if.fetch_ex_reg <= '0; - else if (!hazard_if.if_ex_stall) begin - fetch_ex_if.fetch_ex_reg.token <= 1'b1; - fetch_ex_if.fetch_ex_reg.pc <= pc; - fetch_ex_if.fetch_ex_reg.pc4 <= pc4; - fetch_ex_if.fetch_ex_reg.instr <= igen_bus_if.rdata; - fetch_ex_if.fetch_ex_reg.prediction <= predict_if.predict_taken; + + //RV32C + assign rv32cif.inst = igen_bus_if.rdata; + assign rv32cif.inst_arrived = hazard_if.if_ex_flush == 0 & hazard_if.if_ex_stall == 0; + assign rv32cif.reset_en = hazard_if.insert_priv_pc | sparce_if.skipping + | hazard_if.npc_sel | predict_if.predict_taken; + assign rv32cif.pc_update = hazard_if.pc_en; + assign rv32cif.reset_pc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (sparce_if.skipping ? sparce_if.sparce_target + : (hazard_if.npc_sel ? fetch_ex_if.brj_addr + : (predict_if.predict_taken ? predict_if.target_addr + : pc4or2))); + assign rv32cif.reset_pc_val = RESET_PC; + + assign pc4or2 = (rv32cif.rv32c_ena & (rv32cif.result[1:0] != 2'b11)) ? (pc + 2) : (pc + 4); + assign predict_if.current_pc = pc; + assign npc = hazard_if.insert_priv_pc ? hazard_if.priv_pc + : (sparce_if.skipping ? sparce_if.sparce_target + : (hazard_if.npc_sel ? fetch_ex_if.brj_addr + : (predict_if.predict_taken ? predict_if.target_addr + : rv32cif.rv32c_ena ? rv32cif.nextpc + : pc4or2))); + + //Instruction Access logic + assign hazard_if.i_mem_busy = igen_bus_if.busy; + assign igen_bus_if.addr = rv32cif.rv32c_ena ? rv32cif.imem_pc : pc; + assign igen_bus_if.ren = hazard_if.iren & !rv32cif.done_earlier & ~prv_pipe_if.prot_fault_i; + assign igen_bus_if.wen = 1'b0; + assign igen_bus_if.byte_en = 4'b1111; + assign igen_bus_if.wdata = '0; + + //Fetch Execute Pipeline Signals + word_t instr_to_ex; + assign instr_to_ex = rv32cif.rv32c_ena ? rv32cif.result : igen_bus_if.rdata; + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) fetch_ex_if.fetch_ex_reg <= '0; + else if (hazard_if.if_ex_flush) fetch_ex_if.fetch_ex_reg <= '0; + else if (((rv32cif.done | rv32cif.done_earlier) & rv32cif.rv32c_ena) + | (!hazard_if.if_ex_stall & !rv32cif.rv32c_ena)) begin + fetch_ex_if.fetch_ex_reg.token <= 1'b1; + fetch_ex_if.fetch_ex_reg.pc <= pc; + fetch_ex_if.fetch_ex_reg.pc4 <= pc4or2; + fetch_ex_if.fetch_ex_reg.instr <= instr_to_ex; + fetch_ex_if.fetch_ex_reg.prediction <= predict_if.predict_taken; + end end - end - - //Send exceptions to Hazard Unit - logic mal_addr; - assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); - assign hazard_if.fault_insn = 1'b0; - assign hazard_if.mal_insn = mal_addr; - assign hazard_if.badaddr_f = igen_bus_if.addr; - assign hazard_if.epc_f = pc; - - // Choose the endianness of the data coming into the processor - generate - if (BUS_ENDIANNESS == "big") - assign instr = igen_bus_if.rdata; - else if (BUS_ENDIANNESS == "little") - endian_swapper ltb_endian(igen_bus_if.rdata, instr); - endgenerate + //Send exceptions to Hazard Unit + logic mal_addr; + assign mal_addr = (igen_bus_if.addr[1:0] != 2'b00); + assign hazard_if.fault_insn = prv_pipe_if.prot_fault_i; + assign hazard_if.mal_insn = mal_addr; + assign hazard_if.badaddr_f = igen_bus_if.addr; + assign hazard_if.epc_f = pc; + + // Send memory protection signals + assign prv_pipe_if.iren = 1'b1; + assign prv_pipe_if.iaddr = igen_bus_if.addr; + assign prv_pipe_if.i_acc_width = WordAcc; + + // Choose the endianness of the data coming into the processor + generate + if (BUS_ENDIANNESS == "big") assign instr = igen_bus_if.rdata; + else if (BUS_ENDIANNESS == "little") + endian_swapper ltb_endian ( + .word_in(igen_bus_if.rdata), + .word_out(instr) + ); + endgenerate + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + + assign sparce_if.pc = pc; + assign sparce_if.rdata = igen_bus_if.rdata; endmodule diff --git a/source_code/pipelines/tspp/tspp_hazard_unit.sv b/source_code/pipelines/tspp/tspp_hazard_unit.sv index f84a04024..3022289f5 100644 --- a/source_code/pipelines/tspp/tspp_hazard_unit.sv +++ b/source_code/pipelines/tspp/tspp_hazard_unit.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,95 +20,100 @@ * Email: steven69@purdue.edu * Date Created: 06/14/2016 * Description: Hazard unit that controls the flushing and stalling of -* the stages of the Two Stage Pipeline +* the stages of the Two Stage Pipeline */ `include "tspp_hazard_unit_if.vh" `include "prv_pipeline_if.vh" `include "risc_mgmt_if.vh" -module tspp_hazard_unit -( - tspp_hazard_unit_if.hazard_unit hazard_if, - prv_pipeline_if.hazard prv_pipe_if, - risc_mgmt_if.ts_hazard rm_if +module tspp_hazard_unit ( + tspp_hazard_unit_if.hazard_unit hazard_if, + prv_pipeline_if.hazard prv_pipe_if, + risc_mgmt_if.ts_hazard rm_if, + sparce_pipeline_if.hazard sparce_if ); - import alu_types_pkg::*; - import rv32i_types_pkg::*; - - // Pipeline hazard signals - logic dmem_access; - logic branch_jump; - logic wait_for_imem; - logic wait_for_dmem; - - // IRQ/Exception hazard signals - logic ex_flush_hazard; - logic e_ex_stage; - logic e_f_stage; - logic intr; - - logic rmgmt_stall; - - assign rm_if.if_ex_enable = ~hazard_if.if_ex_stall; - assign rmgmt_stall = rm_if.memory_stall | rm_if.execute_stall; - - assign dmem_access = (hazard_if.dren || hazard_if.dwen); - assign branch_jump = hazard_if.jump || - (hazard_if.branch && hazard_if.mispredict); - assign wait_for_imem = hazard_if.iren & hazard_if.i_mem_busy; - assign wait_for_dmem = dmem_access & hazard_if.d_mem_busy; - - assign hazard_if.npc_sel = branch_jump; - - assign hazard_if.pc_en = (~wait_for_dmem&~wait_for_imem&~hazard_if.halt&~ex_flush_hazard&~rmgmt_stall&~hazard_if.fence_stall) | - branch_jump | prv_pipe_if.insert_pc | prv_pipe_if.ret; - - assign hazard_if.if_ex_flush = ex_flush_hazard | branch_jump | + import alu_types_pkg::*; + import rv32i_types_pkg::*; + + // Pipeline hazard signals + logic dmem_access; + logic branch_jump; + logic wait_for_imem; + logic wait_for_dmem; + + // IRQ/Exception hazard signals + logic ex_flush_hazard; + logic e_ex_stage; + logic e_f_stage; + logic intr; + + logic rmgmt_stall; + + assign rm_if.if_ex_enable = ~hazard_if.if_ex_stall; + assign rmgmt_stall = rm_if.memory_stall | rm_if.execute_stall; + + assign dmem_access = (hazard_if.dren || hazard_if.dwen); + assign branch_jump = hazard_if.jump || (hazard_if.branch && hazard_if.mispredict); + assign wait_for_imem = hazard_if.iren & hazard_if.i_mem_busy; + assign wait_for_dmem = dmem_access & hazard_if.d_mem_busy; + + assign hazard_if.npc_sel = branch_jump; + + assign hazard_if.pc_en = (~wait_for_dmem & ~wait_for_imem & ~hazard_if.halt & ~ex_flush_hazard + & ~rmgmt_stall & ~hazard_if.fence_stall) + | branch_jump | prv_pipe_if.insert_pc | prv_pipe_if.ret; + + assign hazard_if.if_ex_flush = ex_flush_hazard | branch_jump | (wait_for_imem & dmem_access & ~hazard_if.d_mem_busy); - assign hazard_if.if_ex_stall = (wait_for_dmem || + assign hazard_if.if_ex_stall = (wait_for_dmem || (wait_for_imem & ~dmem_access) || - hazard_if.halt) & (~ex_flush_hazard | e_ex_stage) || + hazard_if.halt) & (~ex_flush_hazard | e_ex_stage) || hazard_if.fence_stall || rm_if.execute_stall; - /* Hazards due to Interrupts/Exceptions */ - assign prv_pipe_if.ret = hazard_if.ret; - assign e_ex_stage = hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l | + /* Hazards due to Interrupts/Exceptions */ + assign prv_pipe_if.ret = hazard_if.ret; + assign e_ex_stage = hazard_if.illegal_insn | hazard_if.fault_l | hazard_if.mal_l | hazard_if.fault_s | hazard_if.mal_s | hazard_if.breakpoint | - hazard_if.env_m; - assign e_f_stage = hazard_if.fault_insn | hazard_if.mal_insn; - assign intr = ~e_ex_stage & ~e_f_stage & prv_pipe_if.intr; + hazard_if.env; + assign e_f_stage = hazard_if.fault_insn | hazard_if.mal_insn; + assign intr = ~e_ex_stage & ~e_f_stage & prv_pipe_if.intr; - assign prv_pipe_if.pipe_clear = e_ex_stage | ~(hazard_if.token_ex | rm_if.active_insn); - assign ex_flush_hazard = ((intr | e_f_stage) & ~wait_for_dmem) | e_ex_stage | prv_pipe_if.ret; + assign prv_pipe_if.pipe_clear = e_ex_stage | ~(hazard_if.token_ex | rm_if.active_insn); + assign ex_flush_hazard = ((intr | e_f_stage) & ~wait_for_dmem) | e_ex_stage | prv_pipe_if.ret; - assign hazard_if.insert_priv_pc = prv_pipe_if.insert_pc; - assign hazard_if.priv_pc = prv_pipe_if.priv_pc; - - assign hazard_if.iren = !intr; // prevents a false instruction request from being sent + assign hazard_if.insert_priv_pc = prv_pipe_if.insert_pc; + assign hazard_if.priv_pc = prv_pipe_if.priv_pc; - /* Send Exception notifications to Prv Block */ + assign hazard_if.iren = !intr; // prevents a false instruction request from being sent - assign prv_pipe_if.wb_enable = !hazard_if.if_ex_stall | + /* Send Exception notifications to Prv Block */ + + assign prv_pipe_if.wb_enable = !hazard_if.if_ex_stall | hazard_if.jump | hazard_if.branch; //Because 2 stages - assign prv_pipe_if.fault_insn = hazard_if.fault_insn; - assign prv_pipe_if.mal_insn = hazard_if.mal_insn; - assign prv_pipe_if.illegal_insn = hazard_if.illegal_insn; - assign prv_pipe_if.fault_l = hazard_if.fault_l; - assign prv_pipe_if.mal_l = hazard_if.mal_l; - assign prv_pipe_if.fault_s = hazard_if.fault_s; - assign prv_pipe_if.mal_s = hazard_if.mal_s; - assign prv_pipe_if.breakpoint = hazard_if.breakpoint; - assign prv_pipe_if.env_m = hazard_if.env_m; - assign prv_pipe_if.ex_rmgmt = rm_if.exception; - - assign prv_pipe_if.ex_rmgmt_cause = rm_if.ex_cause; - assign prv_pipe_if.epc = (e_ex_stage | rm_if.exception) ? hazard_if.epc_e : hazard_if.epc_f; - assign prv_pipe_if.badaddr = (hazard_if.mal_insn | hazard_if.fault_insn) ? hazard_if.badaddr_f : - (rm_if.exception ? rm_if.mem_addr : hazard_if.badaddr_e); + assign prv_pipe_if.fault_insn = hazard_if.fault_insn; + assign prv_pipe_if.mal_insn = hazard_if.mal_insn; + assign prv_pipe_if.illegal_insn = hazard_if.illegal_insn; + assign prv_pipe_if.fault_l = hazard_if.fault_l; + assign prv_pipe_if.mal_l = hazard_if.mal_l; + assign prv_pipe_if.fault_s = hazard_if.fault_s; + assign prv_pipe_if.mal_s = hazard_if.mal_s; + assign prv_pipe_if.breakpoint = hazard_if.breakpoint; + assign prv_pipe_if.env = hazard_if.env; + assign prv_pipe_if.ex_rmgmt = rm_if.exception; + + assign prv_pipe_if.ex_rmgmt_cause = rm_if.ex_cause; + assign prv_pipe_if.epc = (e_ex_stage | rm_if.exception) ? hazard_if.epc_e : hazard_if.epc_f; + assign prv_pipe_if.badaddr = (hazard_if.mal_insn | hazard_if.fault_insn) ? hazard_if.badaddr_f : + (rm_if.exception ? rm_if.mem_addr : hazard_if.badaddr_e); + + /********************************************************* + *** SparCE Module Logic + *********************************************************/ + assign sparce_if.if_ex_enable = rm_if.if_ex_enable; endmodule diff --git a/source_code/pipelines/wscript b/source_code/pipelines/wscript deleted file mode 100644 index 8eb0fdef4..000000000 --- a/source_code/pipelines/wscript +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(tspp) - -def sim_source(cnf): - cnf.recurse(tspp) diff --git a/source_code/privs/priv.core b/source_code/privs/priv.core new file mode 100644 index 000000000..34807d03a --- /dev/null +++ b/source_code/privs/priv.core @@ -0,0 +1,32 @@ +CAPI=2: +name: socet:riscv:priv:0.1.0 +description: Privileged Architecture for RISCVBusiness + +filesets: + rtl: + files: + - priv_wrapper.sv + - priv_1_12/priv_1_12_int_ex_handler.sv + - priv_1_12/priv_1_12_block.sv + - priv_1_12/priv_1_12_csr.sv + - priv_1_12/priv_1_12_pipe_control.sv + - priv_1_12/priv_1_12_pma.sv + - priv_1_12/priv_1_12_pmp.sv + - priv_1_12/priv_1_12_pmp_matcher.sv + - priv_1_12/priv_1_12_mode.sv + file_type: systemVerilogSource + + +targets: + default: &default + filesets: + - rtl + toplevel: priv_wrapper + + lint: + <<: *default + description: Linting + default_tool: veriblelint + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search'] diff --git a/source_code/privs/priv_1_11/priv_1_11_block.sv b/source_code/privs/priv_1_11/priv_1_11_block.sv index 2b6c9187c..ab513a1a1 100644 --- a/source_code/privs/priv_1_11/priv_1_11_block.sv +++ b/source_code/privs/priv_1_11/priv_1_11_block.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,57 +24,98 @@ `include "prv_pipeline_if.vh" `include "priv_1_11_internal_if.vh" +`include "core_interrupt_if.vh" module priv_1_11_block ( - input logic CLK, nRST, - prv_pipeline_if.priv_block prv_pipe_if + input logic CLK, + nRST, + prv_pipeline_if.priv_block prv_pipe_if, + core_interrupt_if.core interrupt_if + /*input logic plic_ext_int, + input logic clint_soft_int, + input logic clint_clear_soft_int, + input logic clint_timer_int, + input logic clint_clear_timer_int*/ ); - priv_1_11_internal_if prv_intern_if(); + import machine_mode_types_1_11_pkg::*; + + priv_1_11_internal_if prv_intern_if (); + + + priv_1_11_csr_rfile csr_rfile_i ( + .*, + .prv_intern_if(prv_intern_if) + ); + priv_1_11_control prv_control_i ( + .*, + .prv_intern_if(prv_intern_if) + ); + priv_1_11_pipeline_control pipeline_control_i ( + .*, + .prv_intern_if(prv_intern_if) + ); + + // Disable interrupts that will not be used + assign prv_intern_if.timer_int_u = 1'b0; + assign prv_intern_if.timer_int_s = 1'b0; + assign prv_intern_if.timer_int_m = interrupt_if.timer_int; + assign prv_intern_if.soft_int_u = 1'b0; + assign prv_intern_if.soft_int_s = 1'b0; + assign prv_intern_if.soft_int_m = interrupt_if.soft_int; + assign prv_intern_if.ext_int_u = 1'b0; + assign prv_intern_if.ext_int_s = 1'b0; + assign prv_intern_if.ext_int_m = interrupt_if.ext_int; + assign prv_intern_if.reserved_0 = 1'b0; + assign prv_intern_if.reserved_1 = 1'b0; + assign prv_intern_if.reserved_2 = 1'b0; - logic [1:0] prv_intr, prv_ret; - - priv_1_11_csr_rfile csr_rfile_i(.*, .prv_intern_if(prv_intern_if)); - priv_1_11_control prv_control_i(.*, .prv_intern_if(prv_intern_if)); - priv_1_11_pipeline_control pipeline_control_i(.*, .prv_intern_if(prv_intern_if)); + // Disable clear interrupts that will not be used + assign prv_intern_if.clear_timer_int_u = 1'b0; + assign prv_intern_if.clear_timer_int_s = 1'b0; + assign prv_intern_if.clear_timer_int_m = interrupt_if.timer_int_clear; + assign prv_intern_if.clear_soft_int_u = 1'b0; + assign prv_intern_if.clear_soft_int_s = 1'b0; + assign prv_intern_if.clear_soft_int_m = interrupt_if.soft_int_clear; + assign prv_intern_if.clear_ext_int_u = 1'b0; + assign prv_intern_if.clear_ext_int_s = 1'b0; + assign prv_intern_if.clear_ext_int_m = interrupt_if.ext_int_clear; - //Machine Mode Only - assign prv_intr = 2'b11; - assign prv_ret = 2'b11; - assign prv_intern_if.soft_int = 1'b0; - //TODO: PIC (Programmable Interrupt Controller) - assign prv_intern_if.ext_int = 1'b0; + // from pipeline to the priv unit + assign prv_intern_if.pipe_clear = prv_pipe_if.pipe_clear; + assign prv_intern_if.mret = prv_pipe_if.ret; + assign prv_intern_if.epc = prv_pipe_if.epc; + assign prv_intern_if.fault_insn_access = prv_pipe_if.fault_insn; + assign prv_intern_if.mal_insn = prv_pipe_if.mal_insn; + assign prv_intern_if.illegal_insn = prv_pipe_if.illegal_insn; + assign prv_intern_if.fault_l = prv_pipe_if.fault_l; + assign prv_intern_if.mal_l = prv_pipe_if.mal_l; + assign prv_intern_if.fault_s = prv_pipe_if.fault_s; + assign prv_intern_if.mal_s = prv_pipe_if.mal_s; + assign prv_intern_if.breakpoint = prv_pipe_if.breakpoint; + assign prv_intern_if.env_m = prv_pipe_if.env_m; + assign prv_intern_if.env_s = 1'b0; + assign prv_intern_if.env_u = 1'b0; + assign prv_intern_if.fault_insn_page = 1'b0; + assign prv_intern_if.fault_load_page = 1'b0; + assign prv_intern_if.fault_store_page = 1'b0; + assign prv_intern_if.mtval = prv_pipe_if.badaddr; + assign prv_intern_if.swap = prv_pipe_if.swap; + assign prv_intern_if.clr = prv_pipe_if.clr; + assign prv_intern_if.set = prv_pipe_if.set; + assign prv_intern_if.wdata = prv_pipe_if.wdata; + assign prv_intern_if.addr = prv_pipe_if.addr; + assign prv_intern_if.valid_write = prv_pipe_if.valid_write; + assign prv_intern_if.instr_retired = prv_pipe_if.wb_enable & prv_pipe_if.instr; - // Assign inputs to the prv_block to the corresponding internal signals - assign prv_intern_if.pipe_clear = prv_pipe_if.pipe_clear; - assign prv_intern_if.ret = prv_pipe_if.ret; - assign prv_intern_if.epc = prv_pipe_if.epc; - assign prv_intern_if.fault_insn = prv_pipe_if.fault_insn; - assign prv_intern_if.mal_insn = prv_pipe_if.mal_insn; - assign prv_intern_if.illegal_insn = prv_pipe_if.illegal_insn; - assign prv_intern_if.fault_l = prv_pipe_if.fault_l; - assign prv_intern_if.mal_l = prv_pipe_if.mal_l; - assign prv_intern_if.fault_s = prv_pipe_if.fault_s; - assign prv_intern_if.mal_s = prv_pipe_if.mal_s; - assign prv_intern_if.breakpoint = prv_pipe_if.breakpoint; - assign prv_intern_if.env_m = prv_pipe_if.env_m; - assign prv_intern_if.mtval = prv_pipe_if.badaddr; - assign prv_intern_if.swap = prv_pipe_if.swap; - assign prv_intern_if.clr = prv_pipe_if.clr; - assign prv_intern_if.set = prv_pipe_if.set; - assign prv_intern_if.wdata = prv_pipe_if.wdata; - assign prv_intern_if.addr = prv_pipe_if.addr; - assign prv_intern_if.valid_write = prv_pipe_if.valid_write; - assign prv_intern_if.instr_retired= prv_pipe_if.wb_enable & prv_pipe_if.instr; + assign prv_intern_if.ex_rmgmt = prv_pipe_if.ex_rmgmt; + assign prv_intern_if.ex_rmgmt_cause = prv_pipe_if.ex_rmgmt_cause; - assign prv_intern_if.ex_rmgmt = prv_pipe_if.ex_rmgmt; - assign prv_intern_if.ex_rmgmt_cause = prv_pipe_if.ex_rmgmt_cause; + // from priv unit to pipeline + assign prv_pipe_if.priv_pc = prv_intern_if.priv_pc; + assign prv_pipe_if.insert_pc = prv_intern_if.insert_pc; + assign prv_pipe_if.intr = prv_intern_if.intr; + assign prv_pipe_if.rdata = prv_intern_if.rdata; + assign prv_pipe_if.invalid_csr = prv_intern_if.invalid_csr; - // Assign outputs from internal signals to the outputs of the priv block - assign prv_pipe_if.priv_pc = prv_intern_if.priv_pc; - assign prv_pipe_if.insert_pc = prv_intern_if.insert_pc; - assign prv_pipe_if.intr = prv_intern_if.intr; - assign prv_pipe_if.rdata = prv_intern_if.rdata; - assign prv_pipe_if.invalid_csr = prv_intern_if.invalid_csr; - endmodule diff --git a/source_code/privs/priv_1_11/priv_1_11_control.sv b/source_code/privs/priv_1_11/priv_1_11_control.sv index 037d5c84a..be6f96db2 100644 --- a/source_code/privs/priv_1_11/priv_1_11_control.sv +++ b/source_code/privs/priv_1_11/priv_1_11_control.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,115 +25,162 @@ `include "priv_1_11_internal_if.vh" module priv_1_11_control ( - input CLK, nRST, - priv_1_11_internal_if.prv_control prv_intern_if + input CLK, + nRST, + priv_1_11_internal_if.prv_control prv_intern_if ); - import rv32i_types_pkg::*; - import machine_mode_types_1_11_pkg::*; - - ex_code_t ex_src; - logic exception; - - int_code_t intr_src; - logic interrupt; - logic interrupt_reg, interrupt_fired; - - always_comb begin - interrupt = 1'b1; - intr_src = SOFT_INT; - - if (prv_intern_if.timer_int) begin - intr_src = TIMER_INT; + import rv32i_types_pkg::*; + import machine_mode_types_1_11_pkg::*; + + ex_code_t ex_src; + logic exception; + + int_code_t intr_src; + logic interrupt, clear_interrupt; + logic interrupt_reg, interrupt_fired, update_mie; + + always_comb begin // determine the source of the interrupt to be stored in the mcause register + interrupt = 1'b1; + intr_src = SOFT_INT_M; + + if (prv_intern_if.ext_int_m) begin + intr_src = EXT_INT_M; + end else if (prv_intern_if.soft_int_m) begin + intr_src = SOFT_INT_M; + end else if (prv_intern_if.timer_int_m) begin + intr_src = TIMER_INT_M; + end else if (prv_intern_if.ext_int_s) begin + intr_src = EXT_INT_S; + end else if (prv_intern_if.soft_int_s) begin + intr_src = SOFT_INT_S; + end else if (prv_intern_if.timer_int_s) begin + intr_src = TIMER_INT_S; + end else if (prv_intern_if.ext_int_u) begin + intr_src = EXT_INT_U; + end else if (prv_intern_if.soft_int_u) begin + intr_src = SOFT_INT_U; + end else if (prv_intern_if.timer_int_u) begin + intr_src = TIMER_INT_U; + end else interrupt = 1'b0; + end + + assign clear_interrupt = (prv_intern_if.clear_timer_int_m || prv_intern_if.clear_soft_int_m || prv_intern_if.clear_ext_int_m || prv_intern_if.clear_timer_int_u || prv_intern_if.clear_soft_int_u || prv_intern_if.clear_ext_int_u || prv_intern_if.clear_timer_int_s || prv_intern_if.clear_soft_int_s || prv_intern_if.clear_ext_int_s); + + assign prv_intern_if.mip_rup = interrupt || clear_interrupt; + + always_comb begin // modify the pending status register + prv_intern_if.mip_next = prv_intern_if.mip; + + if (prv_intern_if.ext_int_m) prv_intern_if.mip_next.meip = 1'b1; + else if (prv_intern_if.clear_ext_int_m) prv_intern_if.mip_next.meip = 1'b0; + else if (prv_intern_if.soft_int_m) prv_intern_if.mip_next.msip = 1'b1; + else if (prv_intern_if.clear_soft_int_m) prv_intern_if.mip_next.msip = 1'b0; + else if (prv_intern_if.timer_int_m) prv_intern_if.mip_next.mtip = 1'b1; + else if (prv_intern_if.clear_timer_int_m) prv_intern_if.mip_next.mtip = 1'b0; + else if (prv_intern_if.ext_int_s) prv_intern_if.mip_next.seip = 1'b1; + else if (prv_intern_if.clear_ext_int_s) prv_intern_if.mip_next.seip = 1'b0; + else if (prv_intern_if.soft_int_s) prv_intern_if.mip_next.ssip = 1'b1; + else if (prv_intern_if.clear_soft_int_s) prv_intern_if.mip_next.ssip = 1'b0; + else if (prv_intern_if.timer_int_s) prv_intern_if.mip_next.stip = 1'b1; + else if (prv_intern_if.clear_timer_int_s) prv_intern_if.mip_next.stip = 1'b0; + else if (prv_intern_if.ext_int_u) prv_intern_if.mip_next.ueip = 1'b1; + else if (prv_intern_if.clear_ext_int_u) prv_intern_if.mip_next.ueip = 1'b0; + else if (prv_intern_if.soft_int_u) prv_intern_if.mip_next.usip = 1'b1; + else if (prv_intern_if.clear_soft_int_u) prv_intern_if.mip_next.usip = 1'b0; + else if (prv_intern_if.timer_int_u) prv_intern_if.mip_next.utip = 1'b1; + else if (prv_intern_if.clear_timer_int_u) prv_intern_if.mip_next.utip = 1'b0; + end - else if (prv_intern_if.soft_int) begin - intr_src = SOFT_INT; + + always_comb begin // determine whether or not an exception occured, as well as the source of the exception + exception = 1'b1; + ex_src = INSN_MAL; + + if (prv_intern_if.breakpoint) ex_src = BREAKPOINT; + else if (prv_intern_if.fault_insn_page) ex_src = INSN_PAGE; + else if (prv_intern_if.fault_insn_access) ex_src = INSN_ACCESS; + else if (prv_intern_if.illegal_insn) ex_src = ILLEGAL_INSN; + else if (prv_intern_if.mal_insn) ex_src = INSN_MAL; + else if (prv_intern_if.env_u) ex_src = ENV_CALL_U; + else if (prv_intern_if.env_s) ex_src = ENV_CALL_S; + else if (prv_intern_if.env_m) ex_src = ENV_CALL_M; + else if (prv_intern_if.mal_s) ex_src = S_ADDR_MAL; + else if (prv_intern_if.mal_l) ex_src = L_ADDR_MAL; + else if (prv_intern_if.fault_store_page) ex_src = STORE_PAGE; + else if (prv_intern_if.fault_load_page) ex_src = LOAD_PAGE; + else if (prv_intern_if.fault_s) ex_src = S_FAULT; + else if (prv_intern_if.fault_l) ex_src = L_FAULT; + else if (prv_intern_if.ex_rmgmt) ex_src = ex_code_t'(prv_intern_if.ex_rmgmt_cause); + else exception = 1'b0; end - else if (prv_intern_if.ext_int) begin - intr_src = EXT_INT; + + //output to pipeline control + assign prv_intern_if.intr = exception | interrupt_reg; + assign interrupt_fired = (prv_intern_if.mstatus.mie & ((prv_intern_if.mie.mtie & prv_intern_if.mip.mtip) | + (prv_intern_if.mie.msie & prv_intern_if.mip.msip) | (prv_intern_if.mie.meie & prv_intern_if.mip.meip))); + + // Register Updates on Interrupt/Exception + assign prv_intern_if.mcause_rup = exception | interrupt; // TODO: Change to interrupt + assign prv_intern_if.mcause_next.interrupt = ~exception; + assign prv_intern_if.mcause_next.cause = exception ? ex_src : intr_src; + + assign prv_intern_if.mstatus_rup = exception | prv_intern_if.intr | prv_intern_if.mret; // TODO: Change to prv_intern_if.intr + + always_comb begin + prv_intern_if.mstatus_next.mie = prv_intern_if.mstatus.mie; + prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mpie; + //changed from intr + if (update_mie) begin // interrupt has truly been registered and it is time to go to the vector table + prv_intern_if.mstatus_next.mpie = prv_intern_if.mstatus.mie; // when a trap is taken mpie is set to the current mie + prv_intern_if.mstatus_next.mie = 1'b0; // disable the interrupt once it enters the handler + + end else if (prv_intern_if.mret) begin // leaving the vector table + prv_intern_if.mstatus_next.mpie = 1'b1; + prv_intern_if.mstatus_next.mie = prv_intern_if.mstatus.mpie; + end end - else - interrupt = 1'b0; - end - - assign prv_intern_if.mip_rup = interrupt || prv_intern_if.clear_timer_int; - always_comb begin - prv_intern_if.mip_next = prv_intern_if.mip; - if (prv_intern_if.timer_int) prv_intern_if.mip_next.mtip = 1'b1; - if (prv_intern_if.clear_timer_int) prv_intern_if.mip_next.mtip = 1'b0; - if (prv_intern_if.soft_int) prv_intern_if.mip_next.msip = 1'b1; - if (prv_intern_if.ext_int) prv_intern_if.mip_next.msip = 1'b1; //external interrupts not specified in 1.7 - end - - always_comb begin - exception = 1'b1; - ex_src = INSN_MAL; - - if (prv_intern_if.fault_l) - ex_src = L_FAULT; - else if (prv_intern_if.mal_l) - ex_src = L_ADDR_MAL; - else if (prv_intern_if.fault_s) - ex_src = S_FAULT; - else if (prv_intern_if.mal_s) - ex_src = S_ADDR_MAL; - else if (prv_intern_if.breakpoint) - ex_src = BREAKPOINT; - else if (prv_intern_if.env_m) - ex_src = ENV_CALL_M; - else if (prv_intern_if.illegal_insn) - ex_src = ILLEGAL_INSN; - else if (prv_intern_if.fault_insn) - ex_src = INSN_FAULT; - else if (prv_intern_if.mal_insn) - ex_src = INSN_MAL; - else if (prv_intern_if.ex_rmgmt) - ex_src = ex_code_t'(prv_intern_if.ex_rmgmt_cause); - else - exception = 1'b0; - end - - //output to pipeline control - assign prv_intern_if.intr = exception | interrupt_reg; - assign interrupt_fired = (prv_intern_if.mstatus.ie & ((prv_intern_if.mie.mtie & prv_intern_if.mip.mtip) | - (prv_intern_if.mie.msie & prv_intern_if.mip.msip))); - - // Register Updates on Interrupt/Exception - assign prv_intern_if.mcause_rup = exception | interrupt_fired; - assign prv_intern_if.mcause_next.interrupt = ~exception; - assign prv_intern_if.mcause_next.cause = exception ? ex_src : intr_src; - - assign prv_intern_if.mstatus_rup = exception | interrupt_fired; - - always_comb begin - if (prv_intern_if.intr) begin - prv_intern_if.mstatus_next.ie = 1'b0; - end else if (prv_intern_if.ret) begin - prv_intern_if.mstatus_next.ie = 1'b1; + + + // Update EPC as soon as interrupt or exception is found + // Note: mepc cannot update immediately, as if the processor is in an interrupt already, + // the MEPC captured will be within the interrupt (and nested interrupts are not supported). + // Interrupt fired notes when an interrupt is seen by the processor, i.e. when mstatus.mie is high again. + // The signal is 2 cycles long, so the update_mie signal is used to clip it down to 1 to prevent MEPC + // double update which results in skipping an instruction. + assign prv_intern_if.mepc_rup = exception | (interrupt_fired & ~update_mie); // TODO: Change to interrupt + assign prv_intern_if.mepc_next = prv_intern_if.epc; + + assign prv_intern_if.mtval_rup = (prv_intern_if.mal_l | prv_intern_if.fault_l | prv_intern_if.mal_s | prv_intern_if.fault_s | + prv_intern_if.illegal_insn | prv_intern_if.fault_insn_access | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) + & prv_intern_if.pipe_clear; // TODO: May need to insert other exception signals + assign prv_intern_if.mtval_next = prv_intern_if.mtval; + + /* Interrupt needs to be latched until pipeline cleared */ + /* because mstatus.ie causes the irq to disappear after */ + /* one cycle. Cannot wait to clear mstatus.ie because */ + /* then another interrupt can fire during pipeline clear */ + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) interrupt_reg <= '0; + else if (interrupt_fired) interrupt_reg <= 1'b1; + else if (prv_intern_if.pipe_clear) interrupt_reg <= '0; end - else begin - prv_intern_if.mstatus_next.ie = prv_intern_if.mstatus.ie; + + /* + * Fix for MIE/MPIE issue. This used to be the same as 'interrupt_reg' above, + * but the above stays high for 2+ cycles (i.e. waiting for pipe_clear). + * This caused MPIE to update twice; the first update would set MPIE to 1, + * and the second would cause MPIE to return to 0. Then, after an MRET, + * MIE would not be restored since MPIE was lost. Additionally, shortening + * interrupt_reg was not an option since pipe_clear must be asserted for the + * PC to be inserted into the pipeline from the priv unit, so creating this + * extra register was the cleanest solution to ensuring MPIE updates exactly + * once. + */ + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) update_mie <= '0; + else if (interrupt_fired && ~update_mie) update_mie <= 1'b1; + else if (prv_intern_if.pipe_clear) update_mie <= '0; + else update_mie <= '0; end - end - - // Update EPC as soon as interrupt or exception is found - assign prv_intern_if.mepc_rup = exception | interrupt_fired; - assign prv_intern_if.mepc_next = prv_intern_if.epc; - - assign prv_intern_if.mtval_rup = (prv_intern_if.mal_l | prv_intern_if.fault_l | prv_intern_if.mal_s | prv_intern_if.fault_s | - prv_intern_if.illegal_insn | prv_intern_if.fault_insn | prv_intern_if.mal_insn | prv_intern_if.ex_rmgmt) - & prv_intern_if.pipe_clear; - assign prv_intern_if.mtval_next = prv_intern_if.mtval; - - /* Interrupt needs to be latched until pipeline cleared */ - /* because mstatus.ie causes the irq to disappear after */ - /* one cycle. Cannot wait to clear mstatus.ie because */ - /* then another interrupt can fire during pipeline clear */ - always_ff @ (posedge CLK, negedge nRST) begin - if (!nRST) - interrupt_reg <= '0; - else if (interrupt_fired) - interrupt_reg <= 1'b1; - else if (prv_intern_if.pipe_clear) - interrupt_reg <= '0; - end + endmodule diff --git a/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv b/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv index 17316b08f..35c9bd86c 100644 --- a/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv +++ b/source_code/privs/priv_1_11/priv_1_11_csr_rfile.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -32,31 +32,30 @@ module priv_1_11_csr_rfile ( ); import machine_mode_types_1_11_pkg::*; import rv32i_types_pkg::*; - + /* Machine Information Registers */ mvendorid_t mvendorid; marchid_t marchid; mimpid_t mimpid; mhartid_t mhartid; - misaid_t misaid; + misaid_t misaid, misaid_next, misaid_temp, misaid_default; - assign misaid.base = BASE_RV32; - assign misaid.zero = '0; - assign misaid.extensions = MISAID_EXT_I `ifdef RV32M_SUPPORTED | - MISAID_EXT_M `endif `ifdef RV32C_SUPPORTED | - MISAID_EXT_C `endif `ifdef RV32F_SUPPORTED | - MISAID_EXT_F `endif `ifdef CUSTOM_SUPPORTED | - MISAID_EXT_X `endif; + assign misaid_default.base = BASE_RV32; + assign misaid_default.zero = '0; + assign misaid_default.extensions = MISAID_EXT_I `ifdef RV32M_SUPPORTED | + MISAID_EXT_M `endif `ifdef RV32C_SUPPORTED | + MISAID_EXT_C `endif `ifdef RV32F_SUPPORTED | + MISAID_EXT_F `endif `ifdef CUSTOM_SUPPORTED | + MISAID_EXT_X `endif; //TODO: Version Numbering Convention assign mvendorid = '0; assign marchid = '0; assign mimpid = '0; + assign mhartid = '0; - assign mhartid = '0; - /* Machine Trap Setup Registers */ mstatus_t mstatus, mstatus_next; @@ -64,72 +63,71 @@ module priv_1_11_csr_rfile ( mideleg_t mideleg; mie_t mie, mie_next; mtvec_t mtvec, mtvec_next; - - assign mstatus.zero = '0; - - // mstatus bits set due to only Machine Mode Implemented - assign mstatus.prv = M_MODE; - assign mstatus.prv1 = M_MODE; - assign mstatus.ie1 = 1'b0; - assign mstatus.prv2 = M_MODE; - assign mstatus.ie2 = 1'b0; - assign mstatus.prv3 = M_MODE; - assign mstatus.ie3 = 1'b0; +/* + // Privilege and Global Interrupt-Enable Stack + assign mstatus_next.uie = 1'b0; + assign mstatus_next.sie = 1'b0; + assign mstatus_next.reserved_0 = 1'b0; + assign mstatus_next.upie = 1'b0; + assign mstatus_next.spie = 1'b0; + assign mstatus_next.reserved_1 = 1'b0; + assign mstatus_next.spp = 1'b0; + assign mstatus_next.reserved_2 = 2'b0; + assign mstatus_next.mpp = M_LEVEL; // No memory protection - assign mstatus.vm = VM_MBARE; - assign mstatus.mprv = 1'b0; + assign mstatus_next.mprv = 1'b0; + assign mstatus_next.sum = 1'b0; + assign mstatus_next.mxr = 1'b0; + + // No virtualization protection + assign mstatus_next.tvm = 1'b0; + assign mstatus_next.tw = 1'b0; + assign mstatus_next.tsr = 1'b0; // No FPU or Extensions - assign mstatus.xs = XS_ALL_OFF; - assign mstatus.fs = FS_OFF; - assign mstatus.sd = 1'b0; + assign mstatus_next.xs = XS_ALL_OFF; + assign mstatus_next.fs = FS_OFF; // Even though FPU will be integrated for AFTx06, there is no functionality for Supervisor Mode + assign mstatus_next.sd = (mstatus.fs == FS_DIRTY) | (mstatus.xs == XS_SOME_D); + assign mstatus_next.reserved_3 = '0; +*/ + // Deleg Register Zero in Machine Mode Only (Should be removed) assign medeleg = '0; assign mideleg = '0; - assign mie.zero_0 = '0; - assign mie.zero_1 = '0; - assign mie.zero_2 = '0; - assign mie.htie = 1'b0; - assign mie.stie = 1'b0; - assign mie.hsie = 1'b0; - assign mie.ssie = 1'b0; - +/* + assign mie_next.reserved_0 = '0; + assign mie_next.reserved_1 = '0; + assign mie_next.reserved_2 = '0; + assign mie_next.reserved_3 = '0; + assign mie_next.utie = 1'b0; + assign mie_next.stie = 1'b0; + assign mie_next.usie = 1'b0; + assign mie_next.ssie = 1'b0; + assign mie_next.ueie = 1'b0; + assign mie_next.seie = 1'b0; +*/ /* Machine Trap Handling */ - + mscratch_t mscratch, mscratch_next; mepc_t mepc, mepc_next; mcause_t mcause, mcause_next; mtval_t mtval, mtval_next; mip_t mip, mip_next; - - assign mip.zero_0 = '0; - assign mip.zero_1 = '0; - assign mip.zero_2 = '0; - assign mip.htip = 1'b0; - assign mip.stip = 1'b0; - assign mip.hsip = 1'b0; - assign mip.ssip = 1'b0; - - - /* Machine Protection and Translation */ - // Unimplemented, only MBARE supported - - - /* Machine Timers and Counters */ - mtimecmp_t mtimecmp, mtimecmp_next; - mtime_t mtime, mtime_next; - mtimeh_t mtimeh, mtimeh_next; - logic [63:0] mtimefull, mtimefull_next; - assign mtime = mtimefull[31:0]; - assign mtimeh = mtimefull[63:32]; - assign mtimefull_next = mtimefull + 1; - assign prv_intern_if.timer_int = (mtime == mtimecmp); - assign prv_intern_if.clear_timer_int = (prv_intern_if.addr == MTIMECMP_ADDR) & - prv_intern_if.valid_write; - +/* + assign mip_next.reserved_0 = '0; + assign mip_next.reserved_1 = '0; + assign mip_next.reserved_2 = '0; + assign mip_next.reserved_3 = '0; + assign mip_next.utip = 1'b0; + assign mip_next.stip = 1'b0; + assign mip_next.usip = 1'b0; + assign mip_next.ssip = 1'b0; + assign mip_next.ueip = 1'b0; + assign mip_next.seip = 1'b0; +*/ /* Machine Counter Delta Registers */ // Unimplemented, only Machine Mode Supported @@ -151,85 +149,44 @@ module priv_1_11_csr_rfile ( assign instretfull_next = (prv_intern_if.instr_retired == 1'b1) ? instretfull + 1 : instretfull; - //Non Standard Extensions, used for testing - mtohost_t mtohost, mtohost_next; - mfromhost_t mfromhost, mfromhost_next; - always_ff @ (posedge CLK, negedge nRST) begin if (~nRST) begin - mstatus.ie <= 1'b1; + mstatus <= '0; + //mstatus.mie <= 1'b0; + //mstatus.mpie <= 1'b0; mie.mtie <= 1'b0; mie.msie <= 1'b0; mip.msip <= 1'b0; mip.mtip <= 1'b0; + mie.meie <= 1'b0; + mip.meip <= 1'b0; + misaid <= misaid_default; mtvec <= '0; mcause <= '0; mepc <= '0; mtval <= '0; - mscratch <= '0; - mtohost <= '0; - mfromhost <= '0; - mtimecmp <= '0; - mtimefull <= '0; - /* Performance Counters */ timefull <= '0; cyclefull <= '0; instretfull <= '0; - end else if (prv_intern_if.addr == MTIMEH_ADDR)begin - mstatus.ie <= mstatus_next.ie; - mie.mtie <= mie_next.mtie; - mie.msie <= mie_next.msie; - mip.msip <= mip_next.msip; // interrupt - mip.mtip <= mip_next.mtip; // interrupt + end else begin + mstatus <= mstatus_next; + //mstatus.mie <= mstatus_next.mie; + //mstatus.mpie <= mstatus_next.mpie; + mie <= mie_next; + //mie.mtie <= mie_next.mtie; + //mie.msie <= mie_next.msie; + //mie.meie <= mie_next.meie; + mip <= mip_next; + //mip.msip <= mip_next.msip; + //mip.mtip <= mip_next.mtip; + //mip.meip <= mip_next.meip; + misaid <= misaid_next; mtvec <= mtvec_next; mcause <= mcause_next; mepc <= mepc_next; mtval <= mtval_next; mscratch <= mscratch_next; - mtohost <= mtohost_next; - mfromhost <= mfromhost_next; - mtimecmp <= mtimecmp_next; - mtimefull <= {mtimeh_next, mtimefull_next[31:0]}; - /* Performance Counters */ - timefull <= timefull_next; - cyclefull <= cyclefull_next; - instretfull <= instretfull_next; - end else if (prv_intern_if.addr == MTIME_ADDR) begin - mstatus.ie <= mstatus_next.ie; - mie.mtie <= mie_next.mtie; - mie.msie <= mie_next.msie; - mip.msip <= mip_next.msip; // interrupt - mip.mtip <= mip_next.mtip; // interrupt - mtvec <= mtvec_next; - mcause <= mcause_next; - mepc <= mepc_next; - mtval <= mtval_next; - mscratch <= mscratch_next; - mtohost <= mtohost_next; - mfromhost <= mfromhost_next; - mtimecmp <= mtimecmp_next; - mtimefull <= {mtimefull_next[63:32], mtime_next}; - /* Performance Counters */ - timefull <= timefull_next; - cyclefull <= cyclefull_next; - instretfull <= instretfull_next; - end else begin - mstatus.ie <= mstatus_next.ie; - mie.mtie <= mie_next.mtie; - mie.msie <= mie_next.msie; - mip.msip <= mip_next.msip; // interrupt - mip.mtip <= mip_next.mtip; // interrupt - mtvec <= mtvec_next; - mcause <= mcause_next; - mepc <= mepc_next; - mtval <= mtval_next; - mscratch <= mscratch_next; - mtohost <= mtohost_next; - mfromhost <= mfromhost_next; - mtimecmp <= mtimecmp_next; - mtimefull <= mtimefull_next; - /* Performance Counters */ timefull <= timefull_next; cyclefull <= cyclefull_next; instretfull <= instretfull_next; @@ -257,80 +214,63 @@ module priv_1_11_csr_rfile ( prv_intern_if.rdata ); - // Readonly by pipeline, rw by prv + // Readonly by pipeline, rw by prv, controlled by hardware assign mip_next = prv_intern_if.mip_rup ? prv_intern_if.mip_next : mip; assign mtval_next = prv_intern_if.mtval_rup ? prv_intern_if.mtval_next : mtval; assign mcause_next = prv_intern_if.mcause_rup ? prv_intern_if.mcause_next : mcause; // Read and write by pipeline and prv - //TODO: Waveforms for this look wrong, potential bug assign mstatus_next = (prv_intern_if.addr == MSTATUS_ADDR) ? mstatus_t'(rup_data) : ( prv_intern_if.mstatus_rup ? prv_intern_if.mstatus_next : mstatus ); - assign mepc_next = (prv_intern_if.addr == MEPC_ADDR) ? mepc_t'(rup_data) : ( - prv_intern_if.mepc_rup ? prv_intern_if.mepc_next : + prv_intern_if.mepc_rup ? prv_intern_if.mepc_next : mepc ); - // Read and write by pipeline + + // Readonly by priv, rw by pipeline, assigned based on csr instructions assign mie_next = (prv_intern_if.addr == MIE_ADDR) ? mie_t'(rup_data) : mie; assign mtvec_next = (prv_intern_if.addr == MTVEC_ADDR) ? mtvec_t'(rup_data) : mtvec; assign mscratch_next = (prv_intern_if.addr == MSCRATCH_ADDR) ? mscratch_t'(rup_data) : mscratch; - assign mtohost_next = (prv_intern_if.addr == MTOHOST_ADDR) ? mtohost_t'(rup_data) : mtohost; - assign mtime_next = (prv_intern_if.addr == MTIME_ADDR) ? mtime_t'(rup_data) : mtime; - assign mtimeh_next = (prv_intern_if.addr == MTIMEH_ADDR) ? mtimeh_t'(rup_data) : mtimeh; - assign mtimecmp_next = (prv_intern_if.addr == MTIMECMP_ADDR) ? mtimecmp_t'(rup_data) : mtimecmp; - + // Ensure legal MISA value - WARL always_comb begin + misaid_temp = misaid_t'(rup_data); + if(prv_intern_if.addr == MISA_ADDR && misaid_temp.base != 2'b00 + && (misaid_temp.extensions & MISAID_EXT_E) ^ (misaid_temp.extensions & MISAID_EXT_I) != 'b1 + && misaid_temp.zero == 4'b0) begin + misaid_next = misaid_temp; + end else begin + misaid_next = misaid; + end + end + + always_comb begin // register to send to pipeline based on the address valid_csr_addr = 1'b1; casez (prv_intern_if.addr) MVENDORID_ADDR : prv_intern_if.rdata = mvendorid; MARCHID_ADDR : prv_intern_if.rdata = marchid; MIMPID_ADDR : prv_intern_if.rdata = mimpid; MHARTID_ADDR : prv_intern_if.rdata = mhartid; - MISA_ADDR : prv_intern_if.rdata = misaid; + MISA_ADDR : prv_intern_if.rdata = misaid; MSTATUS_ADDR : prv_intern_if.rdata = mstatus; MTVEC_ADDR : prv_intern_if.rdata = mtvec; - MEDELEG_ADDR : prv_intern_if.rdata = medeleg; - MIDELEG_ADDR : prv_intern_if.rdata = mideleg; + MEDELEG_ADDR : prv_intern_if.rdata = medeleg; + MIDELEG_ADDR : prv_intern_if.rdata = mideleg; MIE_ADDR : prv_intern_if.rdata = mie; MSCRATCH_ADDR : prv_intern_if.rdata = mscratch; MEPC_ADDR : prv_intern_if.rdata = mepc; MCAUSE_ADDR : prv_intern_if.rdata = mcause; MTVAL_ADDR : prv_intern_if.rdata = mtval; - MIP_ADDR : prv_intern_if.rdata = mip; - - //machine protection and translation not present - MBASE_ADDR : prv_intern_if.rdata = '0; - MBOUND_ADDR : prv_intern_if.rdata = '0; - MIBASE_ADDR : prv_intern_if.rdata = '0; - MIBOUND_ADDR : prv_intern_if.rdata = '0; - MDBASE_ADDR : prv_intern_if.rdata = '0; - MDBOUND_ADDR : prv_intern_if.rdata = '0; - - //only machine mode - HTIMEW_ADDR : prv_intern_if.rdata = '0; - HTIMEHW_ADDR : prv_intern_if.rdata = '0; - - //Timers - MTIMECMP_ADDR : prv_intern_if.rdata = mtimecmp; - MTIME_ADDR : prv_intern_if.rdata = mtime; - MTIMEH_ADDR : prv_intern_if.rdata = mtimeh; - - // Non-Standard mtohost/mfromhost - MTOHOST_ADDR : prv_intern_if.rdata = mtohost; - MFROMHOST_ADDR : prv_intern_if.rdata = mfromhost; + MIP_ADDR : prv_intern_if.rdata = mip; // Performance counters MCYCLE_ADDR : prv_intern_if.rdata = cycle; - MTIME_ADDR : prv_intern_if.rdata = _time; MINSTRET_ADDR : prv_intern_if.rdata = instret; MCYCLEH_ADDR : prv_intern_if.rdata = cycleh; - MTIMEH_ADDR : prv_intern_if.rdata = timeh; MINSTRETH_ADDR : prv_intern_if.rdata = instreth; default : begin @@ -347,7 +287,5 @@ module priv_1_11_csr_rfile ( assign prv_intern_if.mcause = mcause; assign prv_intern_if.mip = mip; - assign prv_intern_if.xtvec[2'b11] = mtvec; - assign prv_intern_if.xepc_r[2'b11] = mepc; endmodule diff --git a/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv b/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv index cb727adb9..03938b373 100644 --- a/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv +++ b/source_code/privs/priv_1_11/priv_1_11_pipeline_control.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,38 +20,32 @@ * Email: steven69@purdue.edu * Date Created: 08/13/2019 * Description: Control signals for the pipeline from the exception/IRQ -* block +* block */ - +// Code will mainly be used as pipeline control `include "priv_1_11_internal_if.vh" -module priv_1_11_pipeline_control -( - input logic [1:0] prv_intr, prv_ret, - priv_1_11_internal_if.pipe_ctrl prv_intern_if +module priv_1_11_pipeline_control ( + priv_1_11_internal_if.pipe_ctrl prv_intern_if // interface for pipeline control ); - import rv32i_types_pkg::*; - logic interrupt_pending; - - assign prv_intern_if.insert_pc = prv_intern_if.ret | (prv_intern_if.pipe_clear & prv_intern_if.intr); - - always_comb begin - if(prv_intern_if.intr) - case(prv_intr) - 2'b00: prv_intern_if.priv_pc = prv_intern_if.xtvec[2'b00]; - 2'b01: prv_intern_if.priv_pc = prv_intern_if.xtvec[2'b01]; - 2'b10: prv_intern_if.priv_pc = prv_intern_if.xtvec[2'b10]; - 2'b11: prv_intern_if.priv_pc = prv_intern_if.xtvec[2'b11]; - endcase - else if (prv_intern_if.ret) - case(prv_ret) - 2'b00: prv_intern_if.priv_pc = prv_intern_if.xepc_r[2'b00]; - 2'b01: prv_intern_if.priv_pc = prv_intern_if.xepc_r[2'b01]; - 2'b10: prv_intern_if.priv_pc = prv_intern_if.xepc_r[2'b10]; - 2'b11: prv_intern_if.priv_pc = prv_intern_if.xepc_r[2'b11]; - endcase - else - prv_intern_if.priv_pc = 32'b0; - end + import machine_mode_types_1_11_pkg::*; + import rv32i_types_pkg::*; + + assign prv_intern_if.insert_pc = prv_intern_if.mret | (prv_intern_if.pipe_clear & prv_intern_if.intr); // insert the PC + + + always_comb begin + prv_intern_if.priv_pc = '0; + + if (prv_intern_if.intr) begin + if (prv_intern_if.mtvec.mode == VECTORED & prv_intern_if.mcause.interrupt) begin // vectored mode based on the interrupt source + prv_intern_if.priv_pc = {prv_intern_if.mtvec.base, 2'b00} + {prv_intern_if.mcause.cause, 2'b00}; + end else prv_intern_if.priv_pc = prv_intern_if.mtvec.base << 2; + + end else if (prv_intern_if.mret) + prv_intern_if.priv_pc = prv_intern_if.mepc; // when leaving the ISR, restore to the original PC + + end + endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_block.sv b/source_code/privs/priv_1_12/priv_1_12_block.sv new file mode 100644 index 000000000..f566c3bfd --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_block.sv @@ -0,0 +1,128 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_block.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/27/2022 +* Description: Top level block for the privileged unit, v1.12 +*/ + +`include "prv_pipeline_if.vh" +`include "priv_1_12_internal_if.vh" +`include "core_interrupt_if.vh" +`include "priv_ext_if.vh" + +module priv_1_12_block ( + input logic CLK, nRST, + input logic [63:0] mtime, + prv_pipeline_if.priv_block prv_pipe_if, + core_interrupt_if.core interrupt_if +); + + import machine_mode_types_1_12_pkg::*; + + priv_1_12_internal_if prv_intern_if(); + priv_ext_if priv_ext_pma_if(); + priv_ext_if priv_ext_pmp_if(); + + priv_1_12_csr csr (.CLK(CLK), .nRST(nRST), .mtime(mtime), .prv_intern_if(prv_intern_if), .priv_ext_pma_if(priv_ext_pma_if), .priv_ext_pmp_if(priv_ext_pmp_if)); + priv_1_12_int_ex_handler int_ex_handler (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if)); + priv_1_12_pipe_control pipe_ctrl (.prv_intern_if(prv_intern_if)); + priv_1_12_pma pma (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_if(priv_ext_pma_if)); + priv_1_12_pmp pmp (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if), .priv_ext_if(priv_ext_pmp_if)); + priv_1_12_mode mode (.CLK(CLK), .nRST(nRST), .prv_intern_if(prv_intern_if)); + + // Assign CSR values + assign prv_intern_if.inst_ret = prv_pipe_if.wb_enable & prv_pipe_if.instr; + assign prv_intern_if.csr_addr = prv_pipe_if.csr_addr; + assign prv_intern_if.csr_write = prv_pipe_if.swap; + assign prv_intern_if.csr_clear = prv_pipe_if.clr; + assign prv_intern_if.csr_set = prv_pipe_if.set; + assign prv_intern_if.csr_read_only = prv_pipe_if.read_only; + assign prv_intern_if.new_csr_val = prv_pipe_if.wdata; + assign prv_pipe_if.rdata = prv_intern_if.old_csr_val; + assign prv_pipe_if.invalid_priv_isn = prv_intern_if.invalid_csr | (prv_pipe_if.ret & (prv_intern_if.curr_privilege_level != M_MODE)) + | (prv_pipe_if.wfi & (prv_intern_if.curr_privilege_level == U_MODE) & (prv_intern_if.curr_mstatus.tw)); + + // Disable interrupts that will not be used + assign prv_intern_if.timer_int_u = 1'b0; + assign prv_intern_if.timer_int_s = 1'b0; + assign prv_intern_if.timer_int_m = interrupt_if.timer_int; + assign prv_intern_if.soft_int_u = 1'b0; + assign prv_intern_if.soft_int_s = 1'b0; + assign prv_intern_if.soft_int_m = interrupt_if.soft_int; + assign prv_intern_if.ext_int_u = 1'b0; + assign prv_intern_if.ext_int_s = 1'b0; + assign prv_intern_if.ext_int_m = interrupt_if.ext_int; + + // Disable clear interrupts that will not be used + assign prv_intern_if.clear_timer_int_u = 1'b0; + assign prv_intern_if.clear_timer_int_s = 1'b0; + assign prv_intern_if.clear_timer_int_m = interrupt_if.timer_int_clear; + assign prv_intern_if.clear_soft_int_u = 1'b0; + assign prv_intern_if.clear_soft_int_s = 1'b0; + assign prv_intern_if.clear_soft_int_m = interrupt_if.soft_int_clear; + assign prv_intern_if.clear_ext_int_u = 1'b0; + assign prv_intern_if.clear_ext_int_s = 1'b0; + assign prv_intern_if.clear_ext_int_m = interrupt_if.ext_int_clear; + + // from pipeline to the priv unit + assign prv_intern_if.pipe_clear = prv_pipe_if.pipe_clear; + assign prv_intern_if.epc = prv_pipe_if.epc; + assign prv_intern_if.fault_insn_access = prv_pipe_if.fault_insn; + assign prv_intern_if.mal_insn = prv_pipe_if.mal_insn; + assign prv_intern_if.illegal_insn = prv_pipe_if.illegal_insn; + assign prv_intern_if.fault_l = prv_pipe_if.fault_l; + assign prv_intern_if.mal_l = prv_pipe_if.mal_l; + assign prv_intern_if.fault_s = prv_pipe_if.fault_s; + assign prv_intern_if.mal_s = prv_pipe_if.mal_s; + assign prv_intern_if.breakpoint = prv_pipe_if.breakpoint; + assign prv_intern_if.env_m = prv_pipe_if.env && (prv_intern_if.curr_privilege_level == M_MODE); + assign prv_intern_if.env_s = 1'b0; + assign prv_intern_if.env_u = prv_pipe_if.env && (prv_intern_if.curr_privilege_level == U_MODE); + assign prv_intern_if.fault_insn_page = 1'b0; + assign prv_intern_if.fault_load_page = 1'b0; + assign prv_intern_if.fault_store_page = 1'b0; + assign prv_intern_if.curr_mtval = prv_pipe_if.badaddr; + assign prv_intern_if.valid_write = prv_pipe_if.valid_write; + assign prv_intern_if.mret = prv_pipe_if.ret & (prv_intern_if.curr_privilege_level == M_MODE); + assign prv_intern_if.sret = 1'b0; + + // RISC-MGMT? + // not sure what these are for, part of priv 1.11 + assign prv_intern_if.ex_rmgmt = prv_pipe_if.ex_rmgmt; + assign prv_intern_if.ex_rmgmt_cause = prv_pipe_if.ex_rmgmt_cause; + + // from priv unit to pipeline + assign prv_pipe_if.priv_pc = prv_intern_if.priv_pc; + assign prv_pipe_if.insert_pc = prv_intern_if.insert_pc; + assign prv_pipe_if.intr = prv_intern_if.intr; + + // Memory protection signals + assign prv_intern_if.daddr = prv_pipe_if.daddr; + assign prv_intern_if.iaddr = prv_pipe_if.iaddr; + assign prv_intern_if.d_acc_width = prv_pipe_if.d_acc_width; + assign prv_intern_if.i_acc_width = prv_pipe_if.i_acc_width; + assign prv_intern_if.ren = prv_pipe_if.dren; + assign prv_intern_if.wen = prv_pipe_if.dwen; + assign prv_intern_if.xen = prv_pipe_if.iren; + assign prv_pipe_if.prot_fault_i = prv_intern_if.pma_i_fault | prv_intern_if.pmp_i_fault; + assign prv_pipe_if.prot_fault_l = prv_intern_if.pma_l_fault | prv_intern_if.pmp_l_fault; + assign prv_pipe_if.prot_fault_s = prv_intern_if.pma_s_fault | prv_intern_if.pmp_s_fault; + +endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_csr.sv b/source_code/privs/priv_1_12/priv_1_12_csr.sv new file mode 100644 index 000000000..60a13cbd5 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_csr.sv @@ -0,0 +1,495 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_csr.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 03/28/2022 +* Description: CSR File for Priv Unit 1.12 +*/ + +`include "priv_1_12_internal_if.vh" +`include "priv_ext_if.vh" +`include "component_selection_defines.vh" + +module priv_1_12_csr #( + parameter int HARTID = 0 +)( + input CLK, + input nRST, + input logic [63:0] mtime, + priv_1_12_internal_if.csr prv_intern_if, + priv_ext_if.priv priv_ext_pma_if, + priv_ext_if.priv priv_ext_pmp_if + `ifdef RV32F_SUPPORTED + , priv_ext_if.priv priv_ext_f_if + `endif // RV32F_SUPPORTED + `ifdef RV32V_SUPPORTED + , priv_ext_if.priv priv_ext_v_if + `endif // RV32V_SUPPORTED +); + + + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + /* Machine Information */ + csr_reg_t mvendorid; + csr_reg_t marchid; + csr_reg_t mimpid; + csr_reg_t mhartid; + csr_reg_t mconfigptr; + /* Machine Trap Setup */ + mstatus_t mstatus, mstatus_next; + misa_t misa; + mie_t mie, mie_next; + mtvec_t mtvec, mtvec_next; + mstatush_t mstatush; + /* Machine Trap Handling */ + csr_reg_t mscratch, mscratch_next; + csr_reg_t mepc, mepc_next; + mcause_t mcause, mcause_next; + csr_reg_t mtval, mtval_next; + mip_t mip, mip_next; + /* Machine Counters/Timers */ + mcounteren_t mcounteren, mcounteren_next; + mcountinhibit_t mcounterinhibit, mcounterinhibit_next; + csr_reg_t mcycle; + csr_reg_t minstret; + csr_reg_t mcycleh; + csr_reg_t minstreth; + long_csr_t cycles_full, cf_next; + long_csr_t instret_full, if_next; + + csr_reg_t nxt_csr_val; + + // invalid_csr flags + logic invalid_csr_priv, invalid_csr_addr; + assign prv_intern_if.invalid_csr = invalid_csr_priv | invalid_csr_addr; + + // csr operation flag + logic csr_operation; + assign csr_operation = prv_intern_if.csr_write | prv_intern_if.csr_set | prv_intern_if.csr_clear; + + // Extension Broadcast Signals + // - PMA + assign priv_ext_pma_if.csr_addr = prv_intern_if.csr_addr; + assign priv_ext_pma_if.value_in = nxt_csr_val; + assign priv_ext_pma_if.csr_active = ~invalid_csr_priv & prv_intern_if.valid_write & (csr_operation); + // - PMP + assign priv_ext_pmp_if.csr_addr = prv_intern_if.csr_addr; + assign priv_ext_pmp_if.value_in = nxt_csr_val; + assign priv_ext_pmp_if.csr_active = ~invalid_csr_priv & prv_intern_if.valid_write & (csr_operation); + `ifdef RV32F_SUPPORTED + assign priv_ext_f_if.csr_addr = prv_intern_if.csr_addr; + assign priv_ext_f_if.value_in = nxt_csr_val; + assign priv_ext_f_if.csr_active = ~invalid_csr_priv & prv_intern_if.valid_write & (csr_operation); +`endif // RV32F_SUPPORTED +`ifdef RV32V_SUPPORTED + assign priv_ext_v_if.csr_addr = prv_intern_if.csr_addr; + assign priv_ext_v_if.value_in = nxt_csr_val; + assign priv_ext_v_if.csr_active = ~invalid_csr_priv & prv_intern_if.valid_write & (csr_operation); +`endif // RV32V_SUPPORTED + + /* Save some logic with this */ + assign mcycle = cycles_full[31:0]; + assign mcycleh = cycles_full[63:32]; + assign minstret = instret_full[31:0]; + assign minstreth = instret_full[63:32]; + + /* These info registers are always just tied to certain values */ + assign mvendorid = '0; + assign marchid = '0; + assign mimpid = '0; + assign mconfigptr = '0; + assign mhartid = HARTID; + + /* These registers are RO fields */ + assign misa.zero = '0; + assign misa.base = BASE_RV32; + // NOTE: Per the v1.12 spec, both I and E CANNOT be high - If supporting E, I must be disabled + assign misa.extensions = MISA_EXT_I + | MISA_EXT_U + `ifdef RV32C_SUPPORTED + | MISA_EXT_C + `endif `ifdef RV32E_SUPPORTED + | MISA_EXT_E + `endif `ifdef RV32F_SUPPORTED + | MISA_EXT_F + `endif `ifdef RV32M_SUPPORTED + | MISA_EXT_M + `endif `ifdef RV32V_SUPPORTED + | MISA_EXT_V + `endif `ifdef CUSTOM_SUPPORTED + | MISA_EXT_X + `endif; + + assign mstatush.reserved_0 = '0; + assign mstatush.sbe = 1'b0; + assign mstatush.mbe = 1'b0; + assign mstatush.reserved_1 = '0; + + + + // Control and Status Registers + always_ff @ (posedge CLK, negedge nRST) begin + if (~nRST) begin + /* mstatus reset */ + mstatus.mie <= 1'b0; + mstatus.mpie <= 1'b0; + mstatus.mpp <= U_MODE; + mstatus.mprv <= 1'b0; + mstatus.tw <= 1'b1; + mstatus.reserved_0 <= '0; + mstatus.reserved_1 <= '0; + mstatus.reserved_2 <= '0; + mstatus.reserved_3 <= '0; + mstatus.sie <= 1'b0; + mstatus.spie <= 1'b0; + mstatus.ube <= 1'b0; + mstatus.spp <= 1'b0; + mstatus.sum <= 1'b0; + mstatus.mxr <= 1'b0; + mstatus.tvm <= 1'b0; + mstatus.tsr <= 1'b0; + mstatus.sd <= 1'b0; + `ifdef RV32V_SUPPORTED + mstatus.vs <= VS_INITIAL; + `else + mstatus.vs <= VS_OFF; + `endif + `ifdef RV32F_SUPPORTED + mstatus.fs <= FS_INITIAL; + `else + mstatus.fs <= FS_OFF; + `endif + `ifdef CUSTOM_SUPPORTED + mstatus.xs <= XS_NONE_D; + `else + mstatus.xs <= XS_ALL_OFF; + `endif + + /* mtvec reset */ + mtvec.mode <= DIRECT; + mtvec.base <= '0; + + /* mie reset */ + mie <= '0; + + /* mip reset */ + mip <= '0; + + /* msratch reset */ + mscratch <= '0; + + /* mepc reset */ + mepc <= '0; + + /* mtval reset */ + mtval <= '0; + + /* mcounter reset */ + mcounteren <= '1; + mcounterinhibit <= '0; + + /* perf mon reset */ + cycles_full <= '0; + instret_full <= '0; + + /* mcause reset */ + mcause <= '0; + + end else begin + mstatus <= mstatus_next; + mtvec <= mtvec_next; + mie <= mie_next; + mip <= mip_next; + mscratch <= mscratch_next; + mepc <= mepc_next; + mtval <= mtval_next; + mcounteren <= mcounteren_next; + mcounterinhibit <= mcounterinhibit_next; + mcause <= mcause_next; + cycles_full <= cf_next; + instret_full <= if_next; + end + end + + // Privilege Check and Legal Value Check + logic inject_mcycle, inject_minstret, inject_mcycleh, inject_minstreth; + always_comb begin + mstatus_next = mstatus; + mtvec_next = mtvec; + mie_next = mie; + mip_next = mip; + mscratch_next = mscratch; + mepc_next = mepc; + mtval_next = mtval; + mcounteren_next = mcounteren; + mcounterinhibit_next = mcounterinhibit; + mcause_next = mcause; + + inject_mcycle = 1'b0; + inject_mcycleh = 1'b0; + inject_minstret = 1'b0; + inject_minstreth = 1'b0; + + nxt_csr_val = (prv_intern_if.csr_write) ? prv_intern_if.new_csr_val : + (prv_intern_if.csr_set) ? prv_intern_if.new_csr_val | prv_intern_if.old_csr_val : + (prv_intern_if.csr_clear) ? ~prv_intern_if.new_csr_val & prv_intern_if.old_csr_val : + prv_intern_if.new_csr_val; + invalid_csr_priv = 1'b0; + + if (prv_intern_if.csr_addr[11:10] == 2'b11 && !prv_intern_if.csr_read_only) begin + if (csr_operation) begin + invalid_csr_priv = 1'b1; // Attempting to modify a R/O CSR + end + end else if (prv_intern_if.csr_addr[9:8] > prv_intern_if.curr_privilege_level) begin + if (csr_operation) begin + invalid_csr_priv = 1'b1; // Not enough privilege + end + end else begin + if (prv_intern_if.valid_write) begin + casez(prv_intern_if.csr_addr) + MSTATUS_ADDR: begin + if (prv_intern_if.new_csr_val[12:11] == RESERVED_MODE || prv_intern_if.new_csr_val[12:11] == S_MODE) begin + mstatus_next.mpp = U_MODE; // If invalid privilege level, dump at 0 + end else begin + mstatus_next.mpp = priv_level_t'(nxt_csr_val[12:11]); + end + mstatus_next.mie = nxt_csr_val[3]; + mstatus_next.mpie = nxt_csr_val[7]; + mstatus_next.mprv = nxt_csr_val[17]; + mstatus_next.tw = nxt_csr_val[21]; + end + + MTVEC_ADDR: begin + if (prv_intern_if.new_csr_val[1:0] > 2'b01) begin + mtvec_next.mode = DIRECT; + end else begin + mtvec_next.mode = vector_modes_t'(nxt_csr_val[1:0]); + end + mtvec_next.base = nxt_csr_val[31:2]; + end + + MIE_ADDR: begin + mie_next.msie = nxt_csr_val[3]; + mie_next.mtie = nxt_csr_val[7]; + mie_next.meie = nxt_csr_val[11]; + end + + MIP_ADDR: begin + mip_next.msip = nxt_csr_val[3]; + mip_next.mtip = nxt_csr_val[7]; + mip_next.meip = nxt_csr_val[11]; + end + MSCRATCH_ADDR: begin + mscratch_next = nxt_csr_val; + end + MEPC_ADDR: begin + mepc_next = nxt_csr_val; + end + MTVAL_ADDR: begin + mtval_next = nxt_csr_val; + end + MCOUNTEREN_ADDR: begin + mcounteren_next = nxt_csr_val; + end + MCOUNTINHIBIT_ADDR: begin + mcounterinhibit_next = nxt_csr_val; + end + MCAUSE_ADDR: begin + mcause_next = nxt_csr_val; + end + MCYCLE_ADDR: begin + inject_mcycle = 1'b1; + end + MINSTRET_ADDR: begin + inject_minstret = 1'b1; + end + MCYCLEH_ADDR: begin + inject_mcycleh = 1'b1; + end + MINSTRETH_ADDR: begin + inject_minstreth = 1'b1; + end + endcase + end + end + + // inject values + if (prv_intern_if.inject_mstatus) begin + mstatus_next = prv_intern_if.next_mstatus; + end + if (prv_intern_if.inject_mtval) begin + mtval_next = prv_intern_if.next_mtval; + end + if (prv_intern_if.inject_mepc) begin + mepc_next = prv_intern_if.next_mepc; + end + if (prv_intern_if.inject_mcause) begin + mcause_next = prv_intern_if.next_mcause; + end + if (prv_intern_if.inject_mip) begin + mip_next = prv_intern_if.next_mip; + end + + mstatus_next.sd = &(mstatus_next.vs) | &(mstatus_next.fs) | &(mstatus_next.xs); + end + + // hw perf mon + always_comb begin + cf_next = cycles_full; + if_next = instret_full; + + if (~mcounterinhibit.cy) begin + cf_next = cycles_full + 1; + end + if (~mcounterinhibit.ir) begin + if_next = instret_full + prv_intern_if.inst_ret; + end + + if (inject_mcycle) begin + cf_next = {mcycleh, nxt_csr_val}; + end + if (inject_mcycleh) begin + cf_next = {nxt_csr_val, mcycle}; + end + + if (inject_minstret) begin + if_next = {minstreth, nxt_csr_val}; + end + if (inject_minstreth) begin + if_next = {nxt_csr_val, minstret}; + end + end + + // Return proper values to CPU + always_comb begin + /* CPU return */ + prv_intern_if.old_csr_val = '0; + invalid_csr_addr = 1'b0; + casez(prv_intern_if.csr_addr) + /* Machine Mode Addresses */ + MVENDORID_ADDR: prv_intern_if.old_csr_val = mvendorid; + MARCHID_ADDR: prv_intern_if.old_csr_val = marchid; + MIMPID_ADDR: prv_intern_if.old_csr_val = mimpid; + MHARTID_ADDR: prv_intern_if.old_csr_val = mhartid; + MCONFIGPTR_ADDR: prv_intern_if.old_csr_val = mconfigptr; + MSTATUS_ADDR: prv_intern_if.old_csr_val = mstatus; + MISA_ADDR: prv_intern_if.old_csr_val = misa; + MIE_ADDR: prv_intern_if.old_csr_val = mie; + MTVEC_ADDR: prv_intern_if.old_csr_val = mtvec; + MSTATUSH_ADDR: prv_intern_if.old_csr_val = mstatush; + MSCRATCH_ADDR: prv_intern_if.old_csr_val = mscratch; + MEPC_ADDR: prv_intern_if.old_csr_val = mepc; + MCAUSE_ADDR: prv_intern_if.old_csr_val = mcause; + MTVAL_ADDR: prv_intern_if.old_csr_val = mtval; + MIP_ADDR: prv_intern_if.old_csr_val = mip; + MCOUNTEREN_ADDR: prv_intern_if.old_csr_val = mcounteren; + MCOUNTINHIBIT_ADDR: prv_intern_if.old_csr_val = mcounterinhibit; + MCYCLE_ADDR: prv_intern_if.old_csr_val = mcycle; + MINSTRET_ADDR: prv_intern_if.old_csr_val = minstret; + MCYCLEH_ADDR: prv_intern_if.old_csr_val = mcycleh; + MINSTRETH_ADDR: prv_intern_if.old_csr_val = minstreth; + /* Unprivileged Addresses */ + CYCLE_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.cy) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = mcycle; + end + end + CYCLEH_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.cy) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = mcycleh; + end + end + INSTRET_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.ir) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = minstret; + end + end + INSTRETH_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.ir) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = minstreth; + end + end + TIME_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.tm) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = /* TODO get mtime */ mtime[31:0]; + end + end + TIMEH_ADDR: begin + if (prv_intern_if.curr_privilege_level == U_MODE & ~mcounteren.tm) begin + invalid_csr_addr = 1'b1; + end else begin + prv_intern_if.old_csr_val = /* TODO get mtimeh */ mtime[63:32]; + end + end + /* Extension Addresses */ + default: begin + if (csr_operation) begin + if (priv_ext_pma_if.ack) begin + prv_intern_if.old_csr_val = priv_ext_pma_if.value_out; + end else if (priv_ext_pmp_if.ack) begin + prv_intern_if.old_csr_val = priv_ext_pmp_if.value_out; + end + `ifdef RV32F_SUPPORTED + else if (priv_ext_f_if.ack) begin + prv_intern_if.old_csr_val = priv_ext_f_if.value_out; + end + `endif // RV32F_SUPPORTED + `ifdef RV32V_SUPPORTED + else if (priv_ext_v_if.ack) begin + prv_intern_if.old_csr_val = priv_ext_v_if.value_out; + end + `endif // RV32V_SUPPORTED + + // CSR address doesn't exist + invalid_csr_addr = 1'b1 + & (~priv_ext_pma_if.ack) & (~priv_ext_pma_if.invalid_csr) + & (~priv_ext_pmp_if.ack) & (~priv_ext_pmp_if.invalid_csr) + `ifdef RV32F_SUPPORTED + & (~priv_ext_f_if.ack) & (~priv_ext_f_if.invalid_csr) + `endif // RV32F_SUPPORTED + `ifdef RV32V_SUPPORTED + & (~priv_ext_v_if.ack) & (~priv_ext_v_if.invalid_csr) + `endif // RV32V_SUPPORTED + ; + end + end + endcase + end + + /* Priv control return */ + assign prv_intern_if.curr_mip = mip; + assign prv_intern_if.curr_mie = mie; + assign prv_intern_if.curr_mcause = mcause; + assign prv_intern_if.curr_mepc = mepc; + assign prv_intern_if.curr_mstatus = mstatus; + assign prv_intern_if.curr_mtvec = mtvec; + +endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv new file mode 100644 index 000000000..6c93c13bb --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_int_ex_handler.sv @@ -0,0 +1,188 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_int_ex_handler.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 09/27/2022 +* Description: Main interrupt and exception handler block +*/ + +`include "priv_1_12_internal_if.vh" + +module priv_1_12_int_ex_handler ( + input CLK, nRST, + priv_1_12_internal_if.int_ex_handler prv_intern_if +); + + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + ex_code_t ex_src; + logic exception; + + int_code_t int_src; + logic interrupt, clear_interrupt; + logic interrupt_fired; + + // Determine the source of the interrupt + always_comb begin + interrupt = 1'b1; + int_src = SOFT_INT_S; + + if (prv_intern_if.ext_int_m) begin + int_src = EXT_INT_M; + end + else if (prv_intern_if.soft_int_m) begin + int_src = SOFT_INT_M; + end + else if (prv_intern_if.timer_int_m) begin + int_src = TIMER_INT_M; + end + else if (prv_intern_if.ext_int_s) begin + int_src = EXT_INT_S; + end + else if (prv_intern_if.soft_int_s) begin + int_src = SOFT_INT_S; + end + else if (prv_intern_if.timer_int_s) begin + int_src = TIMER_INT_S; + end + else begin + interrupt = 1'b0; + end + end + + assign clear_interrupt = (prv_intern_if.clear_timer_int_m | prv_intern_if.clear_soft_int_m + | prv_intern_if.clear_ext_int_m | prv_intern_if.clear_timer_int_s + | prv_intern_if.clear_soft_int_s | prv_intern_if.clear_ext_int_s); + + // Determine whether an exception occured + always_comb begin + exception = 1'b1; + ex_src = INSN_MAL; + + if (prv_intern_if.breakpoint) + ex_src = BREAKPOINT; + else if (prv_intern_if.fault_insn_page) + ex_src = INSN_PAGE; + else if (prv_intern_if.fault_insn_access) + ex_src = INSN_ACCESS; + else if (prv_intern_if.illegal_insn) + ex_src = ILLEGAL_INSN; + else if (prv_intern_if.mal_insn) + ex_src = INSN_MAL; + else if (prv_intern_if.env_u) + ex_src = ENV_CALL_U; + else if (prv_intern_if.env_s) + ex_src = ENV_CALL_S; + else if (prv_intern_if.env_m) + ex_src = ENV_CALL_M; + else if (prv_intern_if.mal_s) + ex_src = S_ADDR_MAL; + else if (prv_intern_if.mal_l) + ex_src = L_ADDR_MAL; + else if (prv_intern_if.fault_store_page) + ex_src = STORE_PAGE; + else if (prv_intern_if.fault_load_page) + ex_src = LOAD_PAGE; + else if (prv_intern_if.fault_s) + ex_src = S_FAULT; + else if (prv_intern_if.fault_l) + ex_src = L_FAULT; + else if (prv_intern_if.ex_rmgmt) + ex_src = ex_code_t'(prv_intern_if.ex_rmgmt_cause); + else + exception = 1'b0; + end + + // Output info to pipe_ctrl + assign prv_intern_if.intr = exception | interrupt_fired; + + // Only output an interrupt if said interrupt is enabled + assign interrupt_fired = (prv_intern_if.curr_mstatus.mie & + ((prv_intern_if.curr_mie.mtie & prv_intern_if.curr_mip.mtip) + | (prv_intern_if.curr_mie.msie & prv_intern_if.curr_mip.msip) + | (prv_intern_if.curr_mie.meie & prv_intern_if.curr_mip.meip))); + + // Register updates on Interrupts/Exceptions + assign prv_intern_if.inject_mcause = exception | interrupt_fired; + assign prv_intern_if.next_mcause.interrupt = ~exception; + assign prv_intern_if.next_mcause.cause = exception ? ex_src : int_src; + + assign prv_intern_if.inject_mip = interrupt | clear_interrupt; + always_comb begin + prv_intern_if.next_mip = prv_intern_if.curr_mip; + + if (prv_intern_if.ext_int_m) prv_intern_if.next_mip.meip = 1'b1; + else if (prv_intern_if.clear_ext_int_m) prv_intern_if.next_mip.meip = 1'b0; + + if (prv_intern_if.soft_int_m) prv_intern_if.next_mip.msip = 1'b1; + else if (prv_intern_if.clear_soft_int_m) prv_intern_if.next_mip.msip = 1'b0; + + if (prv_intern_if.timer_int_m) prv_intern_if.next_mip.mtip = 1'b1; + else if (prv_intern_if.clear_timer_int_m) prv_intern_if.next_mip.mtip = 1'b0; + + if (prv_intern_if.ext_int_s) prv_intern_if.next_mip.seip = 1'b1; + else if (prv_intern_if.clear_ext_int_s) prv_intern_if.next_mip.seip = 1'b0; + + if (prv_intern_if.soft_int_s) prv_intern_if.next_mip.ssip = 1'b1; + else if (prv_intern_if.clear_soft_int_s) prv_intern_if.next_mip.ssip = 1'b0; + + if (prv_intern_if.timer_int_s) prv_intern_if.next_mip.stip = 1'b1; + else if (prv_intern_if.clear_timer_int_s) prv_intern_if.next_mip.stip = 1'b0; + end + + assign prv_intern_if.inject_mstatus = prv_intern_if.intr | prv_intern_if.mret; + + always_comb begin + prv_intern_if.next_mstatus = prv_intern_if.curr_mstatus; + // interrupt has truly been registered and it is time to go to the vector table + if (prv_intern_if.intr) begin + // when a trap is taken mpie is set to the current mie + prv_intern_if.next_mstatus.mpie = prv_intern_if.curr_mstatus.mie; + prv_intern_if.next_mstatus.mie = 1'b0; + end else if (prv_intern_if.mret) begin + prv_intern_if.next_mstatus.mpie = 1'b0; // leaving the vector table + prv_intern_if.next_mstatus.mie = prv_intern_if.curr_mstatus.mpie; + end + + // We need to change mstatus bits for mode changes + if (prv_intern_if.intr) begin // If we are receiving an exception or interrupt + prv_intern_if.next_mstatus.mpp = prv_intern_if.curr_privilege_level; + end else if (prv_intern_if.mret) begin // If we are going back from a trap + prv_intern_if.next_mstatus.mpp = U_MODE; // We must set mpp to the least privileged mode possible + if (prv_intern_if.curr_mstatus.mpp != M_MODE) begin + prv_intern_if.next_mstatus.mprv = 1'b0; + end + end + end + + assign prv_intern_if.inject_mepc = exception | interrupt_fired; + assign prv_intern_if.next_mepc = prv_intern_if.epc; + + assign prv_intern_if.inject_mtval = (prv_intern_if.mal_l | prv_intern_if.fault_l + | prv_intern_if.mal_s | prv_intern_if.fault_s + | prv_intern_if.illegal_insn + | prv_intern_if.fault_insn_access + | prv_intern_if.mal_insn + | prv_intern_if.breakpoint + | prv_intern_if.ex_rmgmt) + & prv_intern_if.pipe_clear; + assign prv_intern_if.next_mtval = prv_intern_if.curr_mtval; + +endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_mode.sv b/source_code/privs/priv_1_12/priv_1_12_mode.sv new file mode 100644 index 000000000..13f72505c --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_mode.sv @@ -0,0 +1,59 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_mode.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 11/16/2022 +* Description: Processor privilege mode switcher +*/ + +`include "prv_pipeline_if.vh" +`include "priv_1_12_internal_if.vh" +`include "core_interrupt_if.vh" +`include "priv_ext_if.vh" + +module priv_1_12_mode ( + input logic CLK, nRST, + priv_1_12_internal_if.mode prv_intern_if +); + + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + priv_level_t curr_priv_level, next_priv_level; + + always_ff @ (posedge CLK, negedge nRST) begin + if (~nRST) begin + curr_priv_level <= M_MODE; + end else begin + curr_priv_level <= next_priv_level; + end + end + + always_comb begin + next_priv_level = curr_priv_level; + if (prv_intern_if.intr) begin + next_priv_level = M_MODE; + end else if (prv_intern_if.mret) begin + next_priv_level = prv_intern_if.curr_mstatus.mpp; + end + end + + assign prv_intern_if.curr_privilege_level = curr_priv_level; + +endmodule \ No newline at end of file diff --git a/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv b/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv new file mode 100644 index 000000000..575a33f36 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pipe_control.sv @@ -0,0 +1,52 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_pipe_control.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 09/27/2022 +* Description: Controls when the pipeline should jump to a different + address after an exception or interrupt +*/ + +`include "priv_1_12_internal_if.vh" + +module priv_1_12_pipe_control ( + priv_1_12_internal_if.pipe_ctrl prv_intern_if +); + + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + assign prv_intern_if.insert_pc = prv_intern_if.mret | prv_intern_if.sret | prv_intern_if.intr; + + always_comb begin + prv_intern_if.priv_pc = '0; + + if (prv_intern_if.intr) begin + if (prv_intern_if.curr_mtvec.mode == VECTORED & prv_intern_if.next_mcause.interrupt) begin + prv_intern_if.priv_pc = (prv_intern_if.curr_mtvec.base << 2) + + (prv_intern_if.next_mcause.cause << 2); + end else begin + prv_intern_if.priv_pc = prv_intern_if.curr_mtvec.base << 2; + end + end else if (prv_intern_if.mret) begin + prv_intern_if.priv_pc = prv_intern_if.curr_mepc; // Leaving ISR + end + end + +endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_pma.sv b/source_code/privs/priv_1_12/priv_1_12_pma.sv new file mode 100644 index 000000000..aeb1d735b --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pma.sv @@ -0,0 +1,138 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_pma.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 04/05/2022 +* Description: PMA Checker, version 1.12 +*/ + +`include "priv_1_12_internal_if.vh" +`include "priv_ext_if.vh" + +module priv_1_12_pma ( + input logic CLK, nRST, + priv_1_12_internal_if.pma prv_intern_if, + priv_ext_if.ext priv_ext_if +); + + import pma_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + pma_reg_t [15:0] pma_regs, nxt_pma_regs; + pma_reg_t active_reg_d, active_reg_i; + pma_cfg_t pma_cfg_d, pma_cfg_i; + pma_reg_t new_val; + + // Some easy to use config constants + // ROM_PMA - reserved, no W, R, X, WordAcc, Idm, Cache, Coh, RsrvEventual, AMONone, Memory + `define ROM_PMA pma_cfg_t'({2'b0, 1'b0, 1'b1, 1'b1, WordAcc, 1'b1, 1'b1, 1'b1, RsrvEventual, AMONone, 1'b1}) + // RAM_PMA - reserved, W, R, X, WordAcc, Idm, Cache, Coh, RsrvEventual, AMONone, Memory + `define RAM_PMA pma_cfg_t'({2'b0, 1'b1, 1'b1, 1'b1, WordAcc, 1'b1, 1'b1, 1'b1, RsrvEventual, AMONone, 1'b1}) + // IO_PMA - reserved, W, R, X, WordAcc, no Idm, no Cache, Coh, RsrvEventual, AMONone, I/O + `define IO_PMA pma_cfg_t'({2'b0, 1'b1, 1'b1, 1'b1, WordAcc, 1'b0, 1'b0, 1'b1, RsrvEventual, AMONone, 1'b0}) + // NONE_PMA - reserved, no W, no R, no X, WordAcc, no Idm, no Cache, no Coh, RsrvEventual, AMONone, Memory + `define NONE_PMA pma_cfg_t'({2'b0, 1'b0, 1'b0, 1'b0, WordAcc, 1'b0, 1'b0, 1'b0, RsrvNone, AMONone, 1'b1}) + + // Core State Registers + always_ff @ (posedge CLK, negedge nRST) begin + if (~nRST) begin + pma_regs[00] <= pma_reg_t'({`RAM_PMA, `ROM_PMA}); + pma_regs[01] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[02] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[03] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[04] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[05] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[06] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[07] <= pma_reg_t'({`RAM_PMA, `RAM_PMA}); + pma_regs[08] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[09] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[10] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[11] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[12] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[13] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[14] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + pma_regs[15] <= pma_reg_t'({`IO_PMA, `IO_PMA}); + end else begin + pma_regs <= nxt_pma_regs; + end + end + + // Core State Logic + always_comb begin + nxt_pma_regs = pma_regs; + priv_ext_if.ack = 1'b0; + new_val = pma_reg_t'(priv_ext_if.value_in); + if (priv_ext_if.csr_addr[11:4] == 8'hBC) begin + priv_ext_if.ack = 1'b1; + if (priv_ext_if.csr_active) begin + // WARL checks + if (new_val.pma_cfg_0.Rsrv == RsrvReserved) begin + new_val.pma_cfg_0.Rsrv = RsrvNone; + end + if (new_val.pma_cfg_1.Rsrv == RsrvReserved) begin + new_val.pma_cfg_1.Rsrv = RsrvNone; + end + if (new_val.pma_cfg_0.AccWidth == AccWidthReserved) begin + new_val.pma_cfg_0.AccWidth = WordAcc; + end + if (new_val.pma_cfg_1.AccWidth == AccWidthReserved) begin + new_val.pma_cfg_1.AccWidth = WordAcc; + end + + nxt_pma_regs[priv_ext_if.csr_addr[3:0]] = new_val; + end + end else if (priv_ext_if.csr_addr[11:4] == 8'hCC) begin + priv_ext_if.ack = 1'b1; + end + end + + assign priv_ext_if.invalid_csr = 1'b0; + assign priv_ext_if.value_out = pma_regs[priv_ext_if.csr_addr[3:0]]; + + // PMA Logic Block + always_comb begin + prv_intern_if.pma_l_fault = 1'b0; + prv_intern_if.pma_s_fault = 1'b0; + prv_intern_if.pma_i_fault = 1'b0; + + active_reg_d = pma_regs[prv_intern_if.daddr[31:28]]; + active_reg_i = pma_regs[prv_intern_if.iaddr[31:28]]; + + if (~prv_intern_if.daddr[27]) begin + pma_cfg_d = active_reg_d.pma_cfg_0; + end else begin + pma_cfg_d = active_reg_d.pma_cfg_1; + end + + if (~prv_intern_if.iaddr[27]) begin + pma_cfg_i = active_reg_i.pma_cfg_0; + end else begin + pma_cfg_i = active_reg_i.pma_cfg_1; + end + + if (prv_intern_if.ren & (~pma_cfg_d.R || (prv_intern_if.d_acc_width > pma_cfg_d.AccWidth))) begin + prv_intern_if.pma_l_fault = 1'b1; + end else if (prv_intern_if.wen & (~pma_cfg_d.W || (prv_intern_if.d_acc_width > pma_cfg_d.AccWidth))) begin + prv_intern_if.pma_s_fault = 1'b1; + end else if (prv_intern_if.xen & (~pma_cfg_i.X || (prv_intern_if.i_acc_width > pma_cfg_i.AccWidth))) begin + prv_intern_if.pma_i_fault = 1'b1; + end + end + +endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_pmp.sv b/source_code/privs/priv_1_12/priv_1_12_pmp.sv new file mode 100644 index 000000000..61b2513d7 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pmp.sv @@ -0,0 +1,275 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_pmp.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 10/31/2022 +* Description: PMP Unit, version 1.12 +*/ + +`include "priv_1_12_internal_if.vh" +`include "priv_ext_if.vh" + +module priv_1_12_pmp ( + input logic CLK, nRST, + priv_1_12_internal_if.pmp prv_intern_if, + priv_ext_if.ext priv_ext_if +); + + import pmp_types_1_12_pkg::*; + import machine_mode_types_1_12_pkg::*; + import rv32i_types_pkg::*; + + pmpcfg_t [3:0] pmp_cfg_regs, nxt_pmp_cfg; + pmpaddr_t [15:0] pmp_addr_regs, nxt_pmp_addr; + pmpcfg_t new_cfg; + + // Core State Registers + always_ff @ (posedge CLK, negedge nRST) begin + if (~nRST) begin + pmp_cfg_regs <= '0; + pmp_addr_regs <= '0; + end else begin + pmp_cfg_regs <= nxt_pmp_cfg; + pmp_addr_regs <= nxt_pmp_addr; + end + end + + // Core State Logic + logic [1:0] pmp_cfg_addr_add_one_reg; // exists because TOR is weird + logic [1:0] pmp_cfg_addr_add_one_cfg; // exists because TOR is weird + + assign pmp_cfg_addr_add_one_reg = (priv_ext_if.csr_addr[3:0] + 1) >> 2; // exists because TOR is weird + assign pmp_cfg_addr_add_one_cfg = (priv_ext_if.csr_addr[3:0] + 1) & 2'h3; // exists because TOR is weird + always_comb begin + nxt_pmp_addr = pmp_addr_regs; + nxt_pmp_cfg = pmp_cfg_regs; + new_cfg = pmpcfg_t'(priv_ext_if.value_in); + if (priv_ext_if.csr_active) begin + casez(priv_ext_if.csr_addr) + 12'b0011_1010_00??: begin // 0x3A0 + // WARL check (reserved) + new_cfg[0].reserved = '0; + new_cfg[1].reserved = '0; + new_cfg[2].reserved = '0; + new_cfg[3].reserved = '0; + // WARL check (R/W) + if (new_cfg[0].R == 1'b0 && new_cfg[0].W == 1'b1) begin + new_cfg[0].W = 1'b0; + end + if (new_cfg[1].R == 1'b0 && new_cfg[1].W == 1'b1) begin + new_cfg[1].W = 1'b0; + end + if (new_cfg[2].R == 1'b0 && new_cfg[2].W == 1'b1) begin + new_cfg[2].W = 1'b0; + end + if (new_cfg[3].R == 1'b0 && new_cfg[3].W == 1'b1) begin + new_cfg[3].W = 1'b0; + end + + // Make sure we cannot write to locked CSRs + if (pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][0].L) begin + new_cfg[0] = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][0]; + end + if (pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][1].L) begin + new_cfg[1] = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][1]; + end + if (pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][2].L) begin + new_cfg[2] = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][2]; + end + if (pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][3].L) begin + new_cfg[3] = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]][3]; + end + + // Assign field + nxt_pmp_cfg[priv_ext_if.csr_addr[1:0]] = new_cfg; + end + 12'b0011_1011_????: begin // 0x3B0 + // Make sure we cannot write to locked CSRs + if (~pmp_cfg_regs[priv_ext_if.csr_addr[3:2]][priv_ext_if.csr_addr[1:0]].L) begin + // But wait, TOR messes things up - we need to check the cfg above it + // pmpcfg(i) might be TOR, which means it uses both pmpaddr(i) and pmpaddr(i-1) + // pg 60 of the v1.12 specification for more info + if (priv_ext_if.csr_addr[3:0] != 15) begin // 15 is the last valid register, can't check the one above it + if (pmp_cfg_regs[pmp_cfg_addr_add_one_reg][pmp_cfg_addr_add_one_cfg].A != TOR) begin // If not TOR, everything is good + nxt_pmp_addr[priv_ext_if.csr_addr[3:0]] = priv_ext_if.value_in; + end else if (~pmp_cfg_regs[pmp_cfg_addr_add_one_reg][pmp_cfg_addr_add_one_cfg].L) begin // It was TOR, and is not locked + nxt_pmp_addr[priv_ext_if.csr_addr[3:0]] = priv_ext_if.value_in; + end + end + end + end + endcase + end + end + + assign priv_ext_if.invalid_csr = 1'b0; + + // Return the right value back + always_comb begin + priv_ext_if.value_out = '0; + priv_ext_if.ack = 1'b1; + casez(priv_ext_if.csr_addr) + 12'b0011_1010_00??: begin + priv_ext_if.value_out = pmp_cfg_regs[priv_ext_if.csr_addr[1:0]]; + end + 12'b0011_1011_????: begin + priv_ext_if.value_out = pmp_addr_regs[priv_ext_if.csr_addr[3:0]]; + end + default: begin + priv_ext_if.ack = 1'b0; + end + endcase + end + + /***** Data PMP checker unit *****/ + logic [15:0] d_cfg_match; + genvar i; + + generate + for (i=0; i<16; i++) begin : g_dpmp_match + priv_1_12_pmp_matcher matcher ( + {2'b00, prv_intern_if.daddr[31:2]}, + pmp_cfg_regs[i>>2][i & 3], + pmp_addr_regs[i], + i == 0 ? '0 : pmp_addr_regs[i-1], + d_cfg_match[i] + ); + end + endgenerate + + // time to use the matches + pmpcfg_base_t d_match; + logic d_match_found; + logic d_prot_fault; + always_comb begin + d_match = '0; + d_match_found = 1'b1; + d_prot_fault = 1'b0; + casez(d_cfg_match) + 16'b????_????_????_???1: d_match = pmp_cfg_regs[0][0]; + 16'b????_????_????_??10: d_match = pmp_cfg_regs[0][1]; + 16'b????_????_????_?100: d_match = pmp_cfg_regs[0][2]; + 16'b????_????_????_1000: d_match = pmp_cfg_regs[0][3]; + 16'b????_????_???1_0000: d_match = pmp_cfg_regs[1][0]; + 16'b????_????_??10_0000: d_match = pmp_cfg_regs[1][1]; + 16'b????_????_?100_0000: d_match = pmp_cfg_regs[1][2]; + 16'b????_????_1000_0000: d_match = pmp_cfg_regs[1][3]; + 16'b????_???1_0000_0000: d_match = pmp_cfg_regs[2][0]; + 16'b????_??10_0000_0000: d_match = pmp_cfg_regs[2][1]; + 16'b????_?100_0000_0000: d_match = pmp_cfg_regs[2][2]; + 16'b????_1000_0000_0000: d_match = pmp_cfg_regs[2][3]; + 16'b???1_0000_0000_0000: d_match = pmp_cfg_regs[3][0]; + 16'b??10_0000_0000_0000: d_match = pmp_cfg_regs[3][1]; + 16'b?100_0000_0000_0000: d_match = pmp_cfg_regs[3][2]; + 16'b1000_0000_0000_0000: d_match = pmp_cfg_regs[3][3]; + default: d_match_found = 1'b0; + endcase + + if (prv_intern_if.curr_privilege_level != M_MODE || (prv_intern_if.curr_mstatus.mprv && prv_intern_if.curr_mstatus.mpp != M_MODE)) begin // Core is in an unprivileged state or needs privilege checks + if (~d_match_found) begin + d_prot_fault = 1'b1; + end else begin + if ((prv_intern_if.ren & ~d_match.R) || (prv_intern_if.wen & ~d_match.W)) begin + d_prot_fault = 1'b1; + end + end + end else begin // Core is in M_MODE with no privilege check requirements + if (d_match_found & d_match.L) begin + if ((prv_intern_if.ren & ~d_match.R) || (prv_intern_if.wen & ~d_match.W)) begin + d_prot_fault = 1'b1; + end + end + end + end + + /***** Instruction PMP checker unit *****/ + logic [15:0] i_cfg_match; + genvar j; + + generate + for (j=0; j<16; j++) begin : g_ipmp_matchers + priv_1_12_pmp_matcher matcher ( + {2'b00, prv_intern_if.iaddr[31:2]}, + pmp_cfg_regs[j>>2][j%4], + pmp_addr_regs[j], + j == 0 ? '0 : pmp_addr_regs[j-1], + i_cfg_match[j] + ); + end + endgenerate + + // time to use the matches + pmpcfg_base_t i_match; + logic i_match_found; + logic i_prot_fault; + always_comb begin + i_match = '0; + i_match_found = 1'b1; + i_prot_fault = 1'b0; + casez(i_cfg_match) + 16'b????_????_????_???1: i_match = pmp_cfg_regs[0][0]; + 16'b????_????_????_??10: i_match = pmp_cfg_regs[0][1]; + 16'b????_????_????_?100: i_match = pmp_cfg_regs[0][2]; + 16'b????_????_????_1000: i_match = pmp_cfg_regs[0][3]; + 16'b????_????_???1_0000: i_match = pmp_cfg_regs[1][0]; + 16'b????_????_??10_0000: i_match = pmp_cfg_regs[1][1]; + 16'b????_????_?100_0000: i_match = pmp_cfg_regs[1][2]; + 16'b????_????_1000_0000: i_match = pmp_cfg_regs[1][3]; + 16'b????_???1_0000_0000: i_match = pmp_cfg_regs[2][0]; + 16'b????_??10_0000_0000: i_match = pmp_cfg_regs[2][1]; + 16'b????_?100_0000_0000: i_match = pmp_cfg_regs[2][2]; + 16'b????_1000_0000_0000: i_match = pmp_cfg_regs[2][3]; + 16'b???1_0000_0000_0000: i_match = pmp_cfg_regs[3][0]; + 16'b??10_0000_0000_0000: i_match = pmp_cfg_regs[3][1]; + 16'b?100_0000_0000_0000: i_match = pmp_cfg_regs[3][2]; + 16'b1000_0000_0000_0000: i_match = pmp_cfg_regs[3][3]; + default: i_match_found = 1'b0; + endcase + + if (prv_intern_if.curr_privilege_level != M_MODE || (prv_intern_if.curr_mstatus.mprv && prv_intern_if.curr_mstatus.mpp != M_MODE)) begin // Core is in an unprivileged state or needs privilege checks + if (~i_match_found) begin + i_prot_fault = 1'b1; + end else begin + if (prv_intern_if.xen & ~i_match.X) begin + i_prot_fault = 1'b1; + end + end + end else begin // Core is in M_MODE with no privilege check requirements + if (d_match_found & i_match.L) begin + if (prv_intern_if.xen & ~i_match.X) begin + i_prot_fault = 1'b1; + end + end + end + end + + /***** Resolve and output D and I faults *****/ + always_comb begin + prv_intern_if.pmp_s_fault = 1'b0; + prv_intern_if.pmp_l_fault = 1'b0; + prv_intern_if.pmp_i_fault = 1'b0; + if (d_prot_fault) begin + prv_intern_if.pmp_s_fault = prv_intern_if.wen; + prv_intern_if.pmp_l_fault = prv_intern_if.ren; + end else if (i_prot_fault) begin + prv_intern_if.pmp_i_fault = prv_intern_if.xen; + end + end + +endmodule diff --git a/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.py b/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.py new file mode 100644 index 000000000..70d5a1793 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.py @@ -0,0 +1,14 @@ +# Generates the NAPOT encoder +# really only works for 32-bit addresses + +XLEN = 32 + +print("casez(cfg_addr)") + +for i in range(1, XLEN+1): + print(" 32'b", end="") + print(("?" * (XLEN - i)) + ("0" if i != (XLEN+1) else "") + ("1"*(i-1)) + ": ", end="") + print(f"match = phys_addr[31:{i:02}] == cfg_addr[31:{i:02}];" if i < XLEN-1 else ("match = phys_addr[31] == cfg_addr[31];" if i == XLEN-1 else "match = 1'b1;")) + +print(" default: match = 1'b0;") +print("endcase") \ No newline at end of file diff --git a/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.sv b/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.sv new file mode 100644 index 000000000..b342fc7e6 --- /dev/null +++ b/source_code/privs/priv_1_12/priv_1_12_pmp_matcher.sv @@ -0,0 +1,98 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: priv_1_12_pmp_matcher.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 11/08/2022 +* Description: Internal module used to indicate a PMP + configuration match. +*/ + +import pmp_types_1_12_pkg::*; + +module priv_1_12_pmp_matcher( + input logic[31:0] phys_addr, // address to check + input pmpcfg_base_t check_cfg, // configuration to match + input pmpaddr_t cfg_addr, // configuration address + input pmpaddr_t cfg_addr_before, // configuration address of previous cfg_addr, used for TOR + output logic match // did the config match? +); + + always_comb begin + match = 1'b0; + casez(check_cfg.A) + NA4: begin + match = phys_addr == cfg_addr; + end + + TOR: begin + if (cfg_addr_before >= cfg_addr) begin + match = 1'b0; // If the previous address is greater/equal, no match + end else begin + match = (phys_addr >= cfg_addr_before) && (phys_addr < cfg_addr); + end + end + + NAPOT: begin + // I highly suggest using the script in this folder to create the table below. + // the all 1's case doesn't make sense to me, so I didn't include it here. + casez(cfg_addr) + 32'b???????????????????????????????0: match = phys_addr[31:01] == cfg_addr[31:01]; + 32'b??????????????????????????????01: match = phys_addr[31:02] == cfg_addr[31:02]; + 32'b?????????????????????????????011: match = phys_addr[31:03] == cfg_addr[31:03]; + 32'b????????????????????????????0111: match = phys_addr[31:04] == cfg_addr[31:04]; + 32'b???????????????????????????01111: match = phys_addr[31:05] == cfg_addr[31:05]; + 32'b??????????????????????????011111: match = phys_addr[31:06] == cfg_addr[31:06]; + 32'b?????????????????????????0111111: match = phys_addr[31:07] == cfg_addr[31:07]; + 32'b????????????????????????01111111: match = phys_addr[31:08] == cfg_addr[31:08]; + 32'b???????????????????????011111111: match = phys_addr[31:09] == cfg_addr[31:09]; + 32'b??????????????????????0111111111: match = phys_addr[31:10] == cfg_addr[31:10]; + 32'b?????????????????????01111111111: match = phys_addr[31:11] == cfg_addr[31:11]; + 32'b????????????????????011111111111: match = phys_addr[31:12] == cfg_addr[31:12]; + 32'b???????????????????0111111111111: match = phys_addr[31:13] == cfg_addr[31:13]; + 32'b??????????????????01111111111111: match = phys_addr[31:14] == cfg_addr[31:14]; + 32'b?????????????????011111111111111: match = phys_addr[31:15] == cfg_addr[31:15]; + 32'b????????????????0111111111111111: match = phys_addr[31:16] == cfg_addr[31:16]; + 32'b???????????????01111111111111111: match = phys_addr[31:17] == cfg_addr[31:17]; + 32'b??????????????011111111111111111: match = phys_addr[31:18] == cfg_addr[31:18]; + 32'b?????????????0111111111111111111: match = phys_addr[31:19] == cfg_addr[31:19]; + 32'b????????????01111111111111111111: match = phys_addr[31:20] == cfg_addr[31:20]; + 32'b???????????011111111111111111111: match = phys_addr[31:21] == cfg_addr[31:21]; + 32'b??????????0111111111111111111111: match = phys_addr[31:22] == cfg_addr[31:22]; + 32'b?????????01111111111111111111111: match = phys_addr[31:23] == cfg_addr[31:23]; + 32'b????????011111111111111111111111: match = phys_addr[31:24] == cfg_addr[31:24]; + 32'b???????0111111111111111111111111: match = phys_addr[31:25] == cfg_addr[31:25]; + 32'b??????01111111111111111111111111: match = phys_addr[31:26] == cfg_addr[31:26]; + 32'b?????011111111111111111111111111: match = phys_addr[31:27] == cfg_addr[31:27]; + 32'b????0111111111111111111111111111: match = phys_addr[31:28] == cfg_addr[31:28]; + 32'b???01111111111111111111111111111: match = phys_addr[31:29] == cfg_addr[31:29]; + 32'b??011111111111111111111111111111: match = phys_addr[31:30] == cfg_addr[31:30]; + 32'b?0111111111111111111111111111111: match = phys_addr[31] == cfg_addr[31]; + 32'b01111111111111111111111111111111, + 32'b11111111111111111111111111111111: match = 1'b1; + default: match = 1'b0; + endcase + end + + OFF: begin + match = 1'b0; + end + endcase + end + +endmodule \ No newline at end of file diff --git a/source_code/privs/priv_wrapper.sv b/source_code/privs/priv_wrapper.sv index 66f114825..0590abaea 100644 --- a/source_code/privs/priv_wrapper.sv +++ b/source_code/privs/priv_wrapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,17 +19,19 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 05/18/2017 -* Description: Top level wrapper for the priv ISA implementation. +* Description: Top level wrapper for the priv ISA implementation. */ `include "prv_pipeline_if.vh" +`include "core_interrupt_if.vh" module priv_wrapper ( - input logic CLK, nRST, - prv_pipeline_if.priv_block prv_pipe_if + input logic CLK, nRST, + input logic [63:0] mtime, + prv_pipeline_if.priv_block prv_pipe_if, + core_interrupt_if.core interrupt_if ); - //TODO: Select different priv blocks for backwards compatibility - priv_1_11_block priv_block_i(.*); + priv_1_12_block priv_block_i(.*); endmodule diff --git a/source_code/privs/wscript b/source_code/privs/wscript deleted file mode 100644 index 91a6677cb..000000000 --- a/source_code/privs/wscript +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(priv_1_7) - -def sim_source(cnf): - cnf.recurse(priv_1_7) diff --git a/source_code/ram/config_ram_wrapper.sv b/source_code/ram/config_ram_wrapper.sv index 6720d92de..6cd3f2fbc 100644 --- a/source_code/ram/config_ram_wrapper.sv +++ b/source_code/ram/config_ram_wrapper.sv @@ -1,62 +1,65 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: config_ram_wrapper.sv -* -* Created by: John Skubic +* +* Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 -* Description: Config Ram wrapper should contain the ram module provided by the -* simulation environment being used. If no ram modules are -* provided, an emulated ram module must be created. +* Description: Config Ram wrapper should contain the ram module provided by the +* simulation environment being used. If no ram modules are +* provided, an emulated ram module must be created. * Config ram differs from ram wrapper in that it is not restricted -* to the word size of the core. For that reason, no interface is +* to the word size of the core. For that reason, no interface is * used for config_ram_wrapper to preserve its flexibility. */ module config_ram_wrapper #( - parameter N_BYTES = 4, - parameter DEPTH = 256, - parameter LAT = 0, - parameter ADDR_BITS = $clog2(DEPTH), - parameter N_BITS = N_BYTES*8 + parameter N_BYTES = 4, + parameter DEPTH = 256, + parameter LAT = 0, + parameter ADDR_BITS = $clog2(DEPTH), + parameter N_BITS = N_BYTES * 8 ) ( - input logic CLK, nRST, - input logic [N_BITS-1:0] wdata, - input logic [ADDR_BITS-1:0] addr, - input logic [N_BYTES-1:0] byte_en, - input logic wen, ren, - output logic [N_BITS-1:0] rdata, - output logic busy + input logic CLK, + nRST, + input logic [N_BITS-1:0] wdata, + input logic [ADDR_BITS-1:0] addr, + input logic [N_BYTES-1:0] byte_en, + input logic wen, + ren, + output logic [N_BITS-1:0] rdata, + output logic busy ); - ram_sim_model #(.LAT(0), - .ENDIANNESS("little"), - .N_BYTES(N_BYTES), - .DEPTH(DEPTH) - ) v_lat_ram ( - .CLK(CLK), - .nRST(nRST), - .wdata_in(wdata), - .addr_in(addr), - .byte_en_in(byte_en), - .wen_in(wen), - .ren_in(ren), - .rdata_out(rdata), - .busy_out(busy) - ); + ram_sim_model #( + .LAT(0), + .ENDIANNESS("little"), + .N_BYTES(N_BYTES), + .DEPTH(DEPTH) + ) v_lat_ram ( + .CLK(CLK), + .nRST(nRST), + .wdata_in(wdata), + .addr_in(addr), + .byte_en_in(byte_en), + .wen_in(wen), + .ren_in(ren), + .rdata_out(rdata), + .busy_out(busy) + ); endmodule diff --git a/source_code/ram/ram_sim_model.sv b/source_code/ram/ram_sim_model.sv index 91089e4c8..dfec14a45 100644 --- a/source_code/ram/ram_sim_model.sv +++ b/source_code/ram/ram_sim_model.sv @@ -1,175 +1,229 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: ram_sim_model.sv -* -* Created by: John Skubic +* +* Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 04/27/2017 * Description: SRAM model. This is NOT SYNTHESIZABLE but allows this -* project to run independent of tools in simulation. +* project to run independent of tools in simulation. */ module ram_sim_model #( - parameter LAT = 0, // ram latency - parameter ENDIANNESS = "little", - parameter N_BYTES = 4, - parameter DEPTH = 8192, - parameter MEM_INIT_FILE = "meminit.hex", - parameter MEM_DEFAULT = 32'h0000_0000, - parameter ADDR_BITS = $clog2(DEPTH), - parameter COUNT_BITS = $clog2(LAT) + 1, - parameter N_BITS = N_BYTES*8 + parameter LAT = 0, // ram latency + parameter ENDIANNESS = "little", + parameter N_BYTES = 4, + parameter DEPTH = 8192, + parameter MEM_INIT_FILE = "meminit.bin", + parameter MEM_DEFAULT = 32'h0000_0000, + parameter ADDR_BITS = $clog2(DEPTH), + parameter COUNT_BITS = $clog2(LAT) + 1, + parameter N_BITS = N_BYTES * 8 ) ( - input logic CLK, nRST, - input logic [N_BITS-1:0] wdata_in, - input logic [ADDR_BITS-1:0] addr_in, - input logic [N_BYTES-1:0] byte_en_in, - input logic wen_in, ren_in, - output logic [N_BITS-1:0] rdata_out, - output logic busy_out + input logic CLK, + nRST, + input logic [N_BITS-1:0] wdata_in, + input logic [ADDR_BITS-1:0] addr_in, + input logic [N_BYTES-1:0] byte_en_in, + input logic wen_in, + ren_in, + output logic [N_BITS-1:0] rdata_out, + output logic busy_out ); - - // Memory as associative array to try and save space at runtime - logic [N_BITS-1:0] memory [*]; - - // Variables for file IO - integer fptr; - logic [ADDR_BITS-1:0] faddr; - logic [7:0] line_type; - logic [31:0] fdata; - logic [1:0] t0, t1; - - // RAM signals - logic [COUNT_BITS-1:0] counter; - logic [ADDR_BITS-1:0] addr, addr_r, addr_ram; - logic ren, ren_r, ren_ram; - logic wen, wen_r, wen_ram; - logic [N_BYTES-1:0] byte_en; - logic [N_BITS-1:0] rdata, wdata, wdata_r, wdata_ram, mask; - logic input_diff; - logic access; - string line; - int res; - - genvar i; - - // Load in meminit - initial begin - if (MEM_INIT_FILE != "") begin - fptr = $fopen(MEM_INIT_FILE, "r"); - if (!fptr) begin - $info("Warning: Couldn't open memory init file %s", MEM_INIT_FILE); - end else begin - while (!$feof(fptr)) begin - res = $fgets(line, fptr); - res = $sscanf(line, ":%2h%4h%2h%8h%2h", t0, faddr, line_type, fdata, t1); - if (line_type == 8'h00) begin //data - memory[faddr] = fdata; - //$info("Loading %0h into addr %0h", fdata, faddr); - end + genvar i; + // Memory as associative array to try and save space at runtime +`ifndef SYNTHESIS + logic [N_BITS-1:0] memory [*]; +`else + logic [N_BITS-1:0] memory [DEPTH-1:0]; +`endif + // Variables for file IO + integer fptr; + logic [ADDR_BITS-1:0] faddr; + logic [7:0] line_type; + logic [31:0] fdata; + logic [1:0] t0, t1; + + // RAM signals + logic [COUNT_BITS-1:0] counter; + logic [ADDR_BITS-1:0] addr, addr_r, addr_ram; + logic ren, ren_r, ren_ram; + logic wen, wen_r, wen_ram; + logic [N_BYTES-1:0] byte_en; + logic [N_BITS-1:0] rdata, wdata, wdata_r, wdata_ram, mask; + logic input_diff; + logic access; + string line; + int res; + + logic [7:0] byte_buf; + logic [31:0] word_buf; + int bytes_read; + + + // Load in meminit + /*initial begin + if (MEM_INIT_FILE != "") begin + fptr = $fopen(MEM_INIT_FILE, "r"); + if (!fptr) begin + $info("Warning: Couldn't open memory init file %s", MEM_INIT_FILE); + end else begin + while (!$feof( + fptr + )) begin + res = $fgets(line, fptr); + res = $sscanf(line, ":%2h%4h%2h%8h%2h", t0, faddr, line_type, fdata, t1); + if (line_type == 8'h00) begin //data + memory[faddr] = fdata; + //$info("Loading %0h into addr %0h", fdata, faddr); + end + end + $fclose(fptr); + end + end + end*/ + + // assumes binary file starts at bottom of memory! + initial begin + if(MEM_INIT_FILE != "") begin + fptr = $fopen(MEM_INIT_FILE, "r"); + if(!fptr) begin + $info("Warning: Couldn't open memory init file %s", MEM_INIT_FILE); + end else begin + faddr = 32'h8000_0000; + while(!$feof(fptr)) begin + word_buf = 32'b0; + for(int i = 0; i < 4; i++) begin + bytes_read = $fread(byte_buf, fptr); + if(bytes_read < 1) begin + word_buf <<= (3-i)*8; + break; + end else begin + word_buf = {word_buf[23:0], byte_buf}; + end + end + memory[faddr] = word_buf; + faddr += 1; + end + end end - $fclose(fptr); - end end - end - /* + /* * * Begin steady state ram functionality * */ - // Changes for bus endianness - generate - if (ENDIANNESS == "big") begin - // swap endianness - endian_swapper # (.N_BYTES(N_BYTES)) write_swap(.word_in(wdata_in), .word_out(wdata)); - endian_swapper # (.N_BYTES(N_BYTES)) read_swap(.word_in(rdata), .word_out(rdata_out)); - - // swap byte enables - for (i=0; i < N_BYTES/2; i++) begin - assign byte_en[N_BYTES-1-i] = byte_en_in[i]; - assign byte_en[i] = byte_en_in[N_BYTES-1-i]; - end - if (N_BYTES%2) - assign byte_en[N_BYTES/2] = byte_en_in[N_BYTES/2]; - - end else if (ENDIANNESS == "little") begin - assign wdata = wdata_in; - assign byte_en = byte_en_in; - assign rdata_out = rdata; + // Changes for bus endianness + generate + if (ENDIANNESS == "big") begin : g_ram_be + // swap endianness + endian_swapper #( + .N_BYTES(N_BYTES) + ) write_swap ( + .word_in (wdata_in), + .word_out(wdata) + ); + endian_swapper #( + .N_BYTES(N_BYTES) + ) read_swap ( + .word_in (rdata), + .word_out(rdata_out) + ); + + // swap byte enables + for (i = 0; i < N_BYTES / 2; i++) begin : g_ram_be_byte_enable + assign byte_en[N_BYTES-1-i] = byte_en_in[i]; + assign byte_en[i] = byte_en_in[N_BYTES-1-i]; + end + if (N_BYTES % 2) assign byte_en[N_BYTES/2] = byte_en_in[N_BYTES/2]; + + end else if (ENDIANNESS == "little") begin : g_ram_le + assign wdata = wdata_in; + assign byte_en = byte_en_in; + assign rdata_out = rdata; + end + endgenerate + + generate + for (i = 0; i < N_BYTES; i++) begin : g_ram_mask + assign mask[i*8+:8] = {8{byte_en[i]}}; + end + endgenerate + +`ifndef SYNTHESIS + always begin + @(posedge CLK); + if (access && wen_ram) begin + if (memory.exists(addr_ram)) + memory[addr_ram] = (wdata_ram & mask) | (memory[addr_ram] & ~mask); + else memory[addr_ram] = wdata_ram & mask; + end end - endgenerate - - generate - for(i=0; i < N_BYTES; i++) - assign mask[i*8+:8] = {8{byte_en[i]}}; - endgenerate - - always begin - @(posedge CLK); - if (access && wen_ram) begin - if (memory.exists(addr_ram)) - memory[addr_ram] = (wdata_ram & mask) | (memory[addr_ram] & ~mask); - else - memory[addr_ram] = wdata_ram & mask; +`else + always_ff @(posedge CLK) begin + if (access && wen_ram) begin + memory[addr_ram] <= (wdata_ram & mask) | (memory[addr_ram] & ~mask); + end end - end - - assign addr = addr_in[ADDR_BITS-1:0]; - assign ren = ren_in; - assign wen = wen_in; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) begin - addr_r <= '0; - ren_r <= '0; - wen_r <= '0; - wdata_r <= '0; - end else begin - addr_r <= addr; - ren_r <= ren; - wen_r <= wen; - wdata_r <= wdata; +`endif + + assign addr = addr_in[ADDR_BITS-1:0]; + assign ren = ren_in; + assign wen = wen_in; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + addr_r <= '0; + ren_r <= '0; + wen_r <= '0; + wdata_r <= '0; + end else begin + addr_r <= addr; + ren_r <= ren; + wen_r <= wen; + wdata_r <= wdata; + end end - end - - assign input_diff = (addr_r !== addr) || (ren_r !== ren) || + + assign input_diff = (addr_r !== addr) || (ren_r !== ren) || (wen_r !== wen) || (wdata_r !== wdata); - // mux inputs to ram - assign addr_ram = input_diff ? addr : addr_r; - assign ren_ram = input_diff ? ren : ren_r; - assign wen_ram = input_diff ? wen : wen_r; - assign wdata_ram = input_diff ? wdata: wdata_r; - - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - counter <= '0; - else if (input_diff) - counter <= '0; - if (counter != LAT) - counter <= counter + 1; - end - - assign access = (counter == LAT) && !input_diff; - - assign rdata = (^addr_ram !== 1'bx) && memory.exists(addr_ram) ? memory[addr_ram] : MEM_DEFAULT; - assign busy_out = ~access; + // mux inputs to ram + assign addr_ram = input_diff ? addr : addr_r; + assign ren_ram = input_diff ? ren : ren_r; + assign wen_ram = input_diff ? wen : wen_r; + assign wdata_ram = input_diff ? wdata : wdata_r; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) counter <= '0; + else if (input_diff) counter <= '0; + if (counter != LAT) counter <= counter + 1; + end + + assign access = (counter == LAT) && !input_diff; + +`ifndef SYNTHESIS + assign rdata = (^addr_ram !== 1'bx) && memory.exists(addr_ram) ? memory[addr_ram] : MEM_DEFAULT; +`else + assign rdata = memory[addr_ram]; +`endif + assign busy_out = ~access; endmodule diff --git a/source_code/ram/ram_wrapper.sv b/source_code/ram/ram_wrapper.sv index 8e7f72cf7..8a207be4c 100644 --- a/source_code/ram/ram_wrapper.sv +++ b/source_code/ram/ram_wrapper.sv @@ -1,26 +1,26 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: ram_wrapper.sv -* -* Created by: John Skubic +* +* Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 -* Description: Ram wrapper should contain the ram module provided by the -* simulation environment being used. If no ram modules are +* Description: Ram wrapper should contain the ram module provided by the +* simulation environment being used. If no ram modules are * provided, an emulated ram module must be created. */ @@ -28,28 +28,30 @@ `include "component_selection_defines.vh" module ram_wrapper ( - input logic CLK, nRST, - generic_bus_if.generic_bus gen_bus_if + input logic CLK, + nRST, + generic_bus_if.generic_bus gen_bus_if ); - import rv32i_types_pkg::*; - - logic [RAM_ADDR_SIZE-3:0] word_addr; - assign word_addr = gen_bus_if.addr[WORD_SIZE-1:2]; + import rv32i_types_pkg::*; - ram_sim_model #( - .LAT(0), - .ENDIANNESS(BUS_ENDIANNESS), - .N_BYTES(4) - ) v_lat_ram ( - .CLK(CLK), - .nRST(nRST), - .wdata_in(gen_bus_if.wdata), - .addr_in(word_addr), - .byte_en_in(gen_bus_if.byte_en), - .wen_in(gen_bus_if.wen), - .ren_in(gen_bus_if.ren), - .rdata_out(gen_bus_if.rdata), - .busy_out(gen_bus_if.busy) - ); + logic [RAM_ADDR_SIZE-3:0] word_addr; + assign word_addr = gen_bus_if.addr[WORD_SIZE-1:2]; + assign gen_bus_if.error = (gen_bus_if.addr < 32'h8000_0000); + + ram_sim_model #( + .LAT(0), + .ENDIANNESS(BUS_ENDIANNESS), + .N_BYTES(4) + ) v_lat_ram ( + .CLK(CLK), + .nRST(nRST), + .wdata_in(gen_bus_if.wdata), + .addr_in(word_addr), + .byte_en_in(gen_bus_if.byte_en), + .wen_in(gen_bus_if.wen), + .ren_in(gen_bus_if.ren), + .rdata_out(gen_bus_if.rdata), + .busy_out(gen_bus_if.busy) + ); endmodule diff --git a/source_code/risc_mgmt/extensions/crc32/crc32.sv b/source_code/risc_mgmt/extensions/crc32/crc32.sv index 04927f835..a9509e350 100644 --- a/source_code/risc_mgmt/extensions/crc32/crc32.sv +++ b/source_code/risc_mgmt/extensions/crc32/crc32.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,51 +20,46 @@ * Email: jskubic@purdue.edu * Date Created: 04/06/2017 * Description: CRC32 calculator. Takes one byte and keeps the running -* crc32 calculation. +* crc32 calculation. */ module crc32 ( - input logic CLK, nRST, - input logic[7:0] in_byte, - input logic reset, new_byte, - output logic done, - output rv32i_types_pkg::word_t result + input logic CLK, + nRST, + input logic [7:0] in_byte, + input logic reset, + new_byte, + output logic done, + output rv32i_types_pkg::word_t result ); -import rv32i_types_pkg::*; + import rv32i_types_pkg::*; -parameter POLY = 32'h04c1_1db7; -parameter POLY_REV = 32'hedb8_8320; -parameter POLY_REV_REC = 32'h8260_8edb; + parameter logic [31:0] POLY = 32'h04c1_1db7; + parameter logic [31:0] POLY_REV = 32'hedb8_8320; + parameter logic [31:0] POLY_REV_REC = 32'h8260_8edb; -word_t next_crc, curr_crc, mask; -logic update; -logic [3:0] count; + word_t next_crc, curr_crc, mask; + logic update; + logic [3:0] count; -assign result = ~curr_crc; -assign done = (count[3] & ~new_byte) | reset; -assign update = ~count[3]; -assign mask = {32{curr_crc[0]}}; -assign next_crc = (curr_crc >> 1) ^ (POLY_REV & mask); + assign result = ~curr_crc; + assign done = (count[3] & ~new_byte) | reset; + assign update = ~count[3]; + assign mask = {32{curr_crc[0]}}; + assign next_crc = (curr_crc >> 1) ^ (POLY_REV & mask); -always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) - curr_crc <= '1; - else if (reset) - curr_crc <= '1; - else if (new_byte) - curr_crc <= curr_crc ^ {24'h0, in_byte}; - else if (update) - curr_crc <= next_crc; -end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) curr_crc <= '1; + else if (reset) curr_crc <= '1; + else if (new_byte) curr_crc <= curr_crc ^ {24'h0, in_byte}; + else if (update) curr_crc <= next_crc; + end -always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) - count <= 4'b1000; - else if (new_byte) - count <= 4'b0000; - else if (update) - count <= count + 1; -end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) count <= 4'b1000; + else if (new_byte) count <= 4'b0000; + else if (update) count <= count + 1; + end endmodule diff --git a/source_code/risc_mgmt/extensions/crc32/crc32_decode.sv b/source_code/risc_mgmt/extensions/crc32/crc32_decode.sv index e9043aaaf..0b8effb70 100644 --- a/source_code/risc_mgmt/extensions/crc32/crc32_decode.sv +++ b/source_code/risc_mgmt/extensions/crc32/crc32_decode.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,35 +25,36 @@ `include "risc_mgmt_decode_if.vh" module crc32_decode ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_decode_if.ext dif, - //stage to stage connection - output crc32_pkg::decode_execute_t idex + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_decode_if.ext dif, + //stage to stage connection + output crc32_pkg::decode_execute_t idex ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; + + parameter logic [6:0] OPCODE = 7'b000_1011; - parameter OPCODE = 7'b000_1011; + rtype_t insn_rtype; + logic [9:0] funct10; - rtype_t insn_rtype; - logic [9:0] funct10; + // prevent this extension from accessing core + assign dif.insn_claim = (dif.insn[6:0] == OPCODE); + assign dif.mem_to_reg = 1'b0; - // prevent this extension from accessing core - assign dif.insn_claim = (dif.insn[6:0] == OPCODE); - assign dif.mem_to_reg = 1'b0; + // Decoding rtype + assign insn_rtype = rtype_t'(dif.insn); + assign funct10 = {insn_rtype.funct7, insn_rtype.funct3}; - // Decoding rtype - assign insn_rtype = rtype_t'(dif.insn); - assign funct10 = {insn_rtype.funct7, insn_rtype.funct3}; + // Assigning RMGMT-Decode IF + assign dif.rsel_s_0 = insn_rtype.rs1; + assign dif.rsel_s_1 = insn_rtype.rs2; + assign dif.rsel_d = insn_rtype.rd; - // Assigning RMGMT-Decode IF - assign dif.rsel_s_0 = insn_rtype.rs1; - assign dif.rsel_s_1 = insn_rtype.rs2; - assign dif.rsel_d = insn_rtype.rd; - - // Assigning CRC Decode-Execute IF - assign idex.reset = (funct10 == 10'h0) && dif.insn_claim; - assign idex.new_byte = (funct10 == 10'h1) && dif.insn_claim; + // Assigning CRC Decode-Execute IF + assign idex.reset = (funct10 == 10'h0) && dif.insn_claim; + assign idex.new_byte = (funct10 == 10'h1) && dif.insn_claim; endmodule diff --git a/source_code/risc_mgmt/extensions/crc32/crc32_execute.sv b/source_code/risc_mgmt/extensions/crc32/crc32_execute.sv index ccce7d478..8df718c71 100644 --- a/source_code/risc_mgmt/extensions/crc32/crc32_execute.sv +++ b/source_code/risc_mgmt/extensions/crc32/crc32_execute.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,32 +25,33 @@ `include "risc_mgmt_execute_if.vh" module crc32_execute ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_execute_if.ext eif, - //stage to stage connection - input crc32_pkg::decode_execute_t idex, - output crc32_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_execute_if.ext eif, + //stage to stage connection + input crc32_pkg::decode_execute_t idex, + output crc32_pkg::execute_memory_t exmem ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; - logic [7:0] in_byte; - logic reset, new_byte, done; - word_t result; + logic [7:0] in_byte; + logic reset, new_byte, done; + word_t result; - //prevent this extension from accessing the core - assign eif.exception = 1'b0; - assign eif.branch_jump = 1'b0; + //prevent this extension from accessing the core + assign eif.exception = 1'b0; + assign eif.branch_jump = 1'b0; - // Instantiation of CRC calculation block - crc32 CRC32 (.*); + // Instantiation of CRC calculation block + crc32 CRC32 (.*); - assign new_byte = eif.start & idex.new_byte; - assign reset = idex.reset; - assign in_byte = eif.rdata_s_0[7:0]; - assign eif.busy = ~done; - assign eif.reg_w = idex.new_byte & done; - assign eif.reg_wdata = result; + assign new_byte = eif.start & idex.new_byte; + assign reset = idex.reset; + assign in_byte = eif.rdata_s_0[7:0]; + assign eif.busy = ~done; + assign eif.reg_w = idex.new_byte & done; + assign eif.reg_wdata = result; endmodule diff --git a/source_code/risc_mgmt/extensions/crc32/crc32_memory.sv b/source_code/risc_mgmt/extensions/crc32/crc32_memory.sv index 7861488c2..86091d7d1 100644 --- a/source_code/risc_mgmt/extensions/crc32/crc32_memory.sv +++ b/source_code/risc_mgmt/extensions/crc32/crc32_memory.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,24 +19,25 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: Memory stage of crc32 ISA extension. +* Description: Memory stage of crc32 ISA extension. */ `include "risc_mgmt_memory_if.vh" module crc32_memory ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_memory_if.ext mif, - //stage to stage connection - input crc32_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_memory_if.ext mif, + //stage to stage connection + input crc32_pkg::execute_memory_t exmem ); - //prevent this extension from accessing the core - assign mif.exception = 1'b0; - assign mif.busy = 1'b0; - assign mif.reg_w = 1'b0; - assign mif.mem_ren = 1'b0; - assign mif.mem_wen = 1'b0; + //prevent this extension from accessing the core + assign mif.exception = 1'b0; + assign mif.busy = 1'b0; + assign mif.reg_w = 1'b0; + assign mif.mem_ren = 1'b0; + assign mif.mem_wen = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/Makefile b/source_code/risc_mgmt/extensions/rv32m/Makefile new file mode 100644 index 000000000..d312ae99c --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/Makefile @@ -0,0 +1,49 @@ +TOP = tb_mult.sv +AGENT = tb/agent +TEST = tb/test +ENV = tb/env +SEQ = tb/sequencer +COMP = tb/comparator +DRIVER = tb/driver +MONITOR = tb/monitor +MULT_if = tb/mult_if +PRED = tb/predictor +TRANS = tb/transaction +REG_MODEL = $(REG)/vreguvm_pkg_uvm.sv + + +all: build run + +build: + vlog +incdir+src \ + +incdir+$(TOP) \ + +incdir+$(AGENT) \ + +incdir+$(TEST) \ + +incdir+$(ENV) \ + +incdir+$(SEQ) \ + +incdir+$(COMP) \ + +incdir+$(DRIVER) \ + +incdir+$(MONITOR) \ + +incdir+$(MULT_if) \ + +incdir+$(PRED) \ + +incdir+$(TRANS) \ + +acc \ + +cover \ + -L $$QUESTA_HOME/uvm-1.2 $(REG_MODEL) tb/$(TOP) + +run: + vsim -i tb_mult -L \ + $$QUESTA_HOME/uvm-1.2 \ + -voptargs=+acc \ + -coverage \ + +UVM_TESTNAME="mult_test" \ + +UVM_VERBOSITY=UVM_LOW \ + -do "do wave.do" -do "run -all" + +clean: + rm -rf work + rm -rf mitll90_Dec2019_all + rm -rf covhtmlreport + rm *.log + rm transcript + rm *.wlf diff --git a/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv b/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv new file mode 100644 index 000000000..f1cfe0379 --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/carry_save_adder.sv @@ -0,0 +1,24 @@ +module carry_save_adder #( + parameter int BIT_WIDTH = 32 +) ( + input logic [(BIT_WIDTH-1):0] x, + input logic [(BIT_WIDTH-1):0] y, + input logic [(BIT_WIDTH-1):0] z, + output logic [(BIT_WIDTH-1):0] cout, + output logic [(BIT_WIDTH-1):0] sum +); + genvar i; + logic [(BIT_WIDTH-1):0] c; + generate + for (i = 0; i < BIT_WIDTH; i = i + 1) begin : g_mul_csa + full_adder FA ( + .x(x[i]), + .y(y[i]), + .cin(z[i]), + .cout(c[i]), + .sum(sum[i]) + ); + end + endgenerate + assign cout = c << 1; +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv b/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv new file mode 100644 index 000000000..9bce1660c --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/flex_counter_mul.sv @@ -0,0 +1,38 @@ +module flex_counter_mul #( + parameter int NUM_CNT_BITS = 4 +) ( + input wire clk, + input wire n_rst, + input wire clear, + input wire count_enable, + input reg [(NUM_CNT_BITS-1):0] rollover_val, + output reg [(NUM_CNT_BITS-1):0] count_out, + output reg rollover_flag +); + + reg [(NUM_CNT_BITS-1):0] next_count; + reg next_flag; + always_ff @(posedge clk, negedge n_rst) begin + if (n_rst == 0) begin + count_out <= '0; + rollover_flag <= 1'b0; + end else begin + count_out <= next_count; + rollover_flag <= next_flag; + end + end + + always_comb begin + if (clear == 1) next_flag = 0; + else if (count_enable == 0) next_flag = rollover_flag; + else if (count_out == (rollover_val - 1) & clear == 1'b0) next_flag = 1; + else next_flag = 0; + + + if (clear) next_count = 0; + else if (count_enable) begin + if (count_out == rollover_val) next_count = 1; + else next_count = count_out + 1; + end else next_count = count_out; + end +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/full_adder.sv b/source_code/risc_mgmt/extensions/rv32m/full_adder.sv new file mode 100644 index 000000000..d531e031c --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/full_adder.sv @@ -0,0 +1,11 @@ +module full_adder ( + input logic x, + input logic y, + input logic cin, + output logic cout, + output logic sum +); + assign sum = x ^ y ^ cin; + assign cout = (x & y) | (x & cin) | (y & cin); + +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv b/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv new file mode 100644 index 000000000..0c4a0b4f4 --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/pp_mul32.sv @@ -0,0 +1,347 @@ +// Pipelined multiplier - 32 bits +module pp_mul32 ( + input logic CLK, + input logic nRST, + input logic [31:0] multiplicand, + input logic [31:0] multiplier, + input logic [1:0] is_signed, + input logic start, + output logic finished, + output logic [63:0] product +); + //logic start_reg; + logic [31:0] multiplicand_reg; + logic [31:0] multiplier_reg; + logic [63:0] result; + logic [63:0] result2; + logic [63:0] temp_product; + logic [63:0] temp_product2; + logic [31:0] multiplicand_mod; + logic [31:0] multiplier_mod; + logic adjust_product; + logic [63:0] partial_product[16]; + logic [63:0] + pp0, pp1, pp2, pp3, pp4, pp5, pp6, pp7, pp8, pp9, pp10, pp11, pp12, pp13, pp14, pp15; + logic [32:0] mul_plus2, mul_minus2, mul_minus1; + logic [63:0] pp[16]; + logic [32:0] modified_in; + logic [63:0] + sum0, sum1, sum2, sum3, sum4, sum5, sum6, sum7, sum8, sum9, sum10, sum11, sum12, sum13; + logic [63:0] + cout0, + cout1, + cout2, + cout3, + cout4, + cout5, + cout6, + cout7, + cout8, + cout9, + cout10, + cout11, + cout12, + cout13; + logic [1:0] count; + logic mult_complete; + //logic [63:0] sum13_pip, cout13_pip; + logic [63:0] sum5_pip, cout5_pip, sum6_pip, cout6_pip, sum7_pip, cout7_pip; + logic [1:0] is_signed_reg; + logic done; + logic count_ena; + integer i, j; + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + multiplicand_reg <= '0; + multiplier_reg <= '0; + is_signed_reg <= '0; + end else if (start) begin + multiplicand_reg <= multiplicand; + multiplier_reg <= multiplier; + is_signed_reg <= is_signed; + end + end + // Modify multiplicand and multiplier if they are signed + assign multiplicand_mod = is_signed_reg[1] && multiplicand_reg[31] ? + (~(multiplicand_reg)+1) : multiplicand_reg; + assign multiplier_mod = is_signed_reg[0] && multiplier_reg[31] ? + (~(multiplier_reg)+1) : multiplier_reg; + // Control signal to modify final product + assign adjust_product = (is_signed_reg[0] & multiplier_reg[31]) + ^ (is_signed_reg[1] & multiplicand_reg[31]); + // For bit pair recoding part + assign mul_plus2 = multiplicand_mod + multiplicand_mod; + assign mul_minus2 = ~mul_plus2 + 1; + assign mul_minus1 = ~multiplicand_mod + 1; + assign modified_in = {multiplier_mod, 1'b0}; + + // STAGE 1: BOOTH ENCODER + // Bit pair recoding to generate partial product + always_comb begin + for (i = 0; i < 32; i = i + 2) begin + case ({ + modified_in[i+2], modified_in[i+1], modified_in[i] + }) + 3'b000: pp[i/2] = '0; //0 + 3'b001: pp[i/2] = {{32'd0}, multiplicand_mod}; // +1M + 3'b010: pp[i/2] = {{32'd0}, multiplicand_mod}; // +1M + 3'b011: pp[i/2] = {{31'd0}, mul_plus2}; // +2M + 3'b100: + if (mul_minus2 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus2}; // -2M + 3'b101: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus1}; // -1M + 3'b110: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus1}; // -1M + 3'b111: pp[i/2] = '0; + endcase + end + end + // Shift partial product + always_comb begin + for (j = 0; j < 16; j = j + 1) begin + partial_product[j] = pp[j] << (2 * j); // Shift with multiple of 2 (Radix 4) + end + end + + // Pipeline register before wallace tree + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + pp0 <= '0; + pp1 <= '0; + pp2 <= '0; + pp3 <= '0; + pp4 <= '0; + pp5 <= '0; + pp6 <= '0; + pp7 <= '0; + pp8 <= '0; + pp9 <= '0; + pp10 <= '0; + pp11 <= '0; + pp12 <= '0; + pp13 <= '0; + pp14 <= '0; + pp15 <= '0; + end else begin + pp0 <= partial_product[0]; + pp1 <= partial_product[1]; + pp2 <= partial_product[2]; + pp3 <= partial_product[3]; + pp4 <= partial_product[4]; + pp5 <= partial_product[5]; + pp6 <= partial_product[6]; + pp7 <= partial_product[7]; + pp8 <= partial_product[8]; + pp9 <= partial_product[9]; + pp10 <= partial_product[10]; + pp11 <= partial_product[11]; + pp12 <= partial_product[12]; + pp13 <= partial_product[13]; + pp14 <= partial_product[14]; + pp15 <= partial_product[15]; + end + end + + // STAGE 2: WALLACE TREE + // Layer 1 + carry_save_adder #(64) CSA0 ( + .x(pp0), + .y(pp1), + .z(pp2), + .cout(cout0), + .sum(sum0) + ); + carry_save_adder #(64) CSA1 ( + .x(pp3), + .y(pp4), + .z(pp5), + .cout(cout1), + .sum(sum1) + ); + carry_save_adder #(64) CSA2 ( + .x(pp6), + .y(pp7), + .z(pp8), + .cout(cout2), + .sum(sum2) + ); + carry_save_adder #(64) CSA3 ( + .x(pp9), + .y(pp10), + .z(pp11), + .cout(cout3), + .sum(sum3) + ); + carry_save_adder #(64) CSA4 ( + .x(pp12), + .y(pp13), + .z(pp14), + .cout(cout4), + .sum(sum4) + ); // remaining partialproduct 15 + // Layer 2 + carry_save_adder #(64) CSA5 ( + .x(cout0), + .y(sum0), + .z(cout1), + .cout(cout5), + .sum(sum5) + ); + carry_save_adder #(64) CSA6 ( + .x(sum1), + .y(cout2), + .z(sum2), + .cout(cout6), + .sum(sum6) + ); + carry_save_adder #(64) CSA7 ( + .x(cout3), + .y(sum3), + .z(cout4), + .cout(cout7), + .sum(sum7) + ); // remaining sum4 + // Pipeline register in wallace tree between layer 2 and layer 3 + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + cout5_pip <= '0; + sum5_pip <= '0; + cout6_pip <= '0; + sum6_pip <= '0; + cout7_pip <= '0; + sum7_pip <= '0; + end else begin + cout5_pip <= cout5; + sum5_pip <= sum5; + cout6_pip <= cout6; + sum6_pip <= sum6; + cout7_pip <= cout7; + sum7_pip <= sum7; + end + end + + // Layer 3 + carry_save_adder #(64) CSA8 ( + .x(cout5), + .y(sum5), + .z(cout6), + .cout(cout8), + .sum(sum8) + ); + carry_save_adder #(64) CSA9 ( + .x(sum6), + .y(cout7), + .z(sum7), + .cout(cout9), + .sum(sum9) + ); + // Layer 4 + carry_save_adder #(64) CSA10 ( + .x(cout8), + .y(sum8), + .z(cout9), + .cout(cout10), + .sum(sum10) + ); + carry_save_adder #(64) CSA11 ( + .x(sum9), + .y(pp15), + .z(sum4), + .cout(cout11), + .sum(sum11) + ); + // Layer 5 + carry_save_adder #(64) CSA12 ( + .x(cout10), + .y(sum10), + .z(cout11), + .cout(cout12), + .sum(sum12) + ); // remaining sum11 + // Layer 6 + carry_save_adder #(64) CSA13 ( + .x(cout12), + .y(sum12), + .z(sum11), + .cout(cout13), + .sum(sum13) + ); + + // STAGE 3: NORMAL ADDER + flex_counter_mul #(2) FC ( + .clk(CLK), + .n_rst(nRST), + .clear(start), + .count_enable(count_ena), + .rollover_val(2'd2), + .count_out(count), + .rollover_flag(finished) + ); + assign temp_product = cout13 + sum13; + assign temp_product2 = is_signed_reg[0] == 0 && multiplier_reg[31] ? + temp_product + ({{33{multiplicand_mod[31]}},multiplicand_mod} << 32) + : temp_product; // plus extra 1M + assign result = adjust_product ? (~temp_product2) + 1 : temp_product2; + assign mult_complete = count == 2'd1 | count == 2'd2; + assign result2 = mult_complete ? result : '0; + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + product <= '0; + end else begin + product <= result2; + end + end + + //Small FSM to control flex counter + typedef enum logic { + IDLE, + START + } state_t; + state_t state, next_state; + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) state <= IDLE; + else state <= next_state; + end + + always_comb begin + /* + next_state = state; + case (state) + IDLE: begin + if (start) + next_state = START; + end + START: begin + if (finished) + next_state = IDLE; + end + endcase + */ + next_state = state; + if (state == IDLE && start) begin + next_state = START; + end else if (state == START && finished) begin + next_state = IDLE; + end else begin + next_state = state; + end + end + + always_comb begin + count_ena = 0; + case (state) + IDLE: begin + count_ena = 0; + end + START: begin + count_ena = ~finished; + end + endcase + end + +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv b/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv new file mode 100644 index 000000000..361b60b6c --- /dev/null +++ b/source_code/risc_mgmt/extensions/rv32m/radix4_divider.sv @@ -0,0 +1,141 @@ +module radix4_divider #( + parameter int NUM_BITS = 32 +) ( + input logic CLK, + input logic nRST, + input logic start, + input logic is_signed, //new + input logic [NUM_BITS-1:0] dividend, + input logic [NUM_BITS-1:0] divisor, + output logic [NUM_BITS-1:0] quotient, + output logic [NUM_BITS-1:0] remainder, + output logic finished + +); + logic [NUM_BITS-1:0] + next_remainder, + next_quotient, + shifted_remainder, + shifted_quotient, + temp_quotient, + temp_remainder; + logic [NUM_BITS:0] Result1, Result2, Result3; + logic [NUM_BITS-1:0] DivisorX2, DivisorX3; + logic [4:0] count, next_count; + + logic [NUM_BITS-1:0] usign_divisor, usign_dividend; + logic adjustment_possible, adjust_quotient, adjust_remainder; + logic div_done; + + assign usign_divisor = is_signed & divisor[NUM_BITS-1] ? (~divisor) + 1 : divisor; + assign usign_dividend = is_signed & dividend[NUM_BITS-1] ? (~dividend) + 1 : dividend; + assign adjustment_possible = is_signed && (divisor[NUM_BITS-1] ^ dividend[NUM_BITS-1]); + assign adjust_quotient = adjustment_possible && ~quotient[NUM_BITS-1]; + assign adjust_remainder = is_signed && dividend[NUM_BITS-1]; + assign div_done = (count == 0); + assign quotient = temp_quotient; + assign remainder = temp_remainder; + + /* + always_comb begin + quotient = temp_quotient; + remainder = temp_remainder; + if (count == 5'b1) begin + quotient = adjust_quotient ? ~temp_quotient + 1 : temp_quotient; + remainder = adjust_remainder ? ~temp_remainder + 1 : temp_remainder; + end + end +*/ + /* + always_ff @(posedge CLK, negedge nRST) begin + if (~finished && adjust_quotient) + quotient <= ~quotient + 1; + + else if(~finished && adjust_remainder ) + remainder <= ~remainder + 1; + + else begin + quotient <= quotient; + remainder <= remainder; + end + end +*/ + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + finished <= 1'b0; + end else if (start) begin + finished <= 1'b0; + end else if (div_done) begin + finished <= 1'b1; + end + end + //initialize d2 d3 + assign DivisorX2 = usign_divisor << 1; //Divisor*2 + assign DivisorX3 = (usign_divisor << 1) + usign_divisor; //Divisor*3 + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + + count <= 5'd16; + temp_quotient <= '0; + temp_remainder <= '0; + end else if (start) begin + temp_quotient <= usign_dividend; + temp_remainder <= '0; + count <= 5'd16; + + end else begin + temp_quotient <= next_quotient; + temp_remainder <= next_remainder; + count <= next_count; + end + end + + always_comb begin + + next_quotient = temp_quotient; + next_remainder = temp_remainder; + next_count = count; + shifted_remainder = '0; + shifted_quotient = '0; + Result1 = '0; + Result2 = '0; + Result3 = '0; + + if (count != 0) begin + next_count = count - 1; + shifted_remainder = (temp_remainder << 2) | temp_quotient[NUM_BITS-1:NUM_BITS-2]; + shifted_quotient = temp_quotient << 2; + Result1 = shifted_remainder - usign_divisor; + Result2 = shifted_remainder - DivisorX2; + Result3 = shifted_remainder - DivisorX3; + if (Result1[NUM_BITS-1] | Result1[NUM_BITS]) begin + next_remainder = shifted_remainder; + next_quotient = shifted_quotient | 0; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + + end else if (Result2[NUM_BITS-1] | Result2[NUM_BITS]) begin + next_remainder = Result1[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 1; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end else if (Result3[NUM_BITS-1] | Result3[NUM_BITS]) begin + next_remainder = Result2[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 2; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end else begin + next_remainder = Result3[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 3; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end + end + + end +endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/rv32m_decode.sv b/source_code/risc_mgmt/extensions/rv32m/rv32m_decode.sv index 371192b7d..4ba4cc713 100644 --- a/source_code/risc_mgmt/extensions/rv32m/rv32m_decode.sv +++ b/source_code/risc_mgmt/extensions/rv32m/rv32m_decode.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,46 +19,46 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: Decoding for standard multiply extension +* Description: Decoding for standard multiply extension */ `include "risc_mgmt_decode_if.vh" module rv32m_decode ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_decode_if.ext dif, - //stage to stage connection - output rv32m_pkg::decode_execute_t idex + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_decode_if.ext dif, + //stage to stage connection + output rv32m_pkg::decode_execute_t idex ); - import rv32m_pkg::*; + import rv32m_pkg::*; + + rv32m_insn_t insn; - parameter OPCODE = RV32M_OPCODE; + assign dif.mem_to_reg = 1'b0; - rv32m_insn_t insn; + assign insn = rv32m_insn_t'(dif.insn); - assign dif.mem_to_reg = 1'b0; - - assign insn = rv32m_insn_t'(dif.insn); + assign dif.insn_claim = (insn.opcode_major == RV32M_OPCODE) + && (insn.opcode_minor == RV32M_OPCODE_MINOR); + assign dif.rsel_s_0 = insn.rs1; + assign dif.rsel_s_1 = insn.rs2; + assign dif.rsel_d = insn.rd; - assign dif.insn_claim = (insn.opcode_major == OPCODE) && (insn.opcode_minor == RV32M_OPCODE_MINOR); - assign dif.rsel_s_0 = insn.rs1; - assign dif.rsel_s_1 = insn.rs2; - assign dif.rsel_d = insn.rd; - - // decode funct - assign idex.start = dif.insn_claim; - assign idex.mul = ~insn.funct[2]; - assign idex.div = insn.funct[2:1] == 2'b10; - assign idex.rem = insn.funct[2:1] == 2'b11; + // decode funct + assign idex.start = dif.insn_claim; + assign idex.mul = ~insn.funct[2]; + assign idex.div = insn.funct[2:1] == 2'b10; + assign idex.rem = insn.funct[2:1] == 2'b11; - assign idex.usign_usign = (insn.funct == 3'b011) || (insn.funct == 3'b101) - || (insn.funct == 3'b111); - assign idex.sign_sign = (insn.funct == 3'b001) || (insn.funct == 3'b100) + assign idex.usign_usign = (insn.funct == 3'b011) || (insn.funct == 3'b101) + || (insn.funct == 3'b111); + assign idex.sign_sign = (insn.funct == 3'b001) || (insn.funct == 3'b100) || (insn.funct == 3'b110) || (insn.funct == 3'b000); - assign idex.sign_usign = insn.funct == 3'b010; + assign idex.sign_usign = insn.funct == 3'b010; - assign idex.lower_word = ~(|insn.funct[1:0]); // only valid for mul + assign idex.lower_word = ~(|insn.funct[1:0]); // only valid for mul endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv b/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv index 8a6308f50..b3ba5660c 100644 --- a/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv +++ b/source_code/risc_mgmt/extensions/rv32m/rv32m_execute.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,148 +19,149 @@ * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 02/07/2017 -* Description: Execute stage for standard RV32M +* Description: Execute stage for standard RV32M */ `include "risc_mgmt_execute_if.vh" module rv32m_execute ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_execute_if.ext eif, - //stage to stage connection - input rv32m_pkg::decode_execute_t idex, - output rv32m_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_execute_if.ext eif, + //stage to stage connection + input rv32m_pkg::decode_execute_t idex, + output rv32m_pkg::execute_memory_t exmem ); - import rv32m_pkg::*; - import rv32i_types_pkg::*; + import rv32m_pkg::*; + import rv32i_types_pkg::*; - /* Static RISC-MGMT assignments */ + /* Static RISC-MGMT assignments */ - assign eif.exception = 1'b0; - assign eif.reg_w = 1'b1; - assign eif.branch_jump = 1'b0; + assign eif.exception = 1'b0; + assign eif.reg_w = 1'b1; + assign eif.branch_jump = 1'b0; - /* Operand Saver to detect new request */ + /* Operand Saver to detect new request */ - // operand saver - word_t op_a, op_b, op_a_save, op_b_save; - logic [2:0] operation, operation_save; - logic [1:0] is_signed_save, is_signed_curr, is_signed; - logic operand_diff; + // operand saver + word_t op_a, op_b, op_a_save, op_b_save; + logic [2:0] operation, operation_save; + logic [1:0] is_signed_save, is_signed_curr, is_signed; + logic operand_diff; - assign op_a = operand_diff ? eif.rdata_s_0 : op_a_save; - assign op_b = operand_diff ? eif.rdata_s_1 : op_b_save; - assign operand_diff = ((op_a_save != eif.rdata_s_0) || + assign op_a = operand_diff ? eif.rdata_s_0 : op_a_save; + assign op_b = operand_diff ? eif.rdata_s_1 : op_b_save; + assign operand_diff = ((op_a_save != eif.rdata_s_0) || (op_b_save != eif.rdata_s_1) || - (is_signed_save != is_signed_curr) || + (is_signed_save != is_signed_curr) || (operation_save != {idex.mul, idex.div, idex.rem})) && idex.start ; - assign is_signed_curr = idex.usign_usign ? 2'b00 : ( - idex.sign_sign ? 2'b11 : 2'b10); - assign is_signed = operand_diff ? is_signed_curr : is_signed_save; - assign operation = operand_diff ? {idex.mul, idex.div, idex.rem} : operation_save; - - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) begin - op_a_save <= '0; - op_b_save <= '0; - is_signed_save <= '0; - operation_save <= '0; + assign is_signed_curr = idex.usign_usign ? 2'b00 : (idex.sign_sign ? 2'b11 : 2'b10); + assign is_signed = operand_diff ? is_signed_curr : is_signed_save; + assign operation = operand_diff ? {idex.mul, idex.div, idex.rem} : operation_save; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + op_a_save <= '0; + op_b_save <= '0; + is_signed_save <= '0; + operation_save <= '0; + end else if (operand_diff) begin + op_a_save <= eif.rdata_s_0; + op_b_save <= eif.rdata_s_1; + is_signed_save <= is_signed_curr; + operation_save <= {idex.mul, idex.div, idex.rem}; + end end - else if (operand_diff) begin - op_a_save <= eif.rdata_s_0; - op_b_save <= eif.rdata_s_1; - is_signed_save <= is_signed_curr; - operation_save <= {idex.mul, idex.div, idex.rem}; + + + /* MULTIPLICATION */ + + // multiplier signals + word_t multiplicand, multiplier; + logic [(WORD_SIZE*2)-1:0] product; + logic mul_finished; + logic mul_start; + + assign multiplicand = op_a; + assign multiplier = op_b; + assign mul_start = operand_diff && operation[2]; + + // Module instantiations + pp_mul32 mult_i ( + .CLK(CLK), + .nRST(nRST), + .multiplicand(multiplicand), + .multiplier(multiplier), + .product(product), + .is_signed(is_signed), + .start(mul_start), + .finished(mul_finished) + ); + + + /* DIVISION / REMAINDER */ + + logic overflow, div_zero, div_finished; + word_t divisor, dividend, quotient, remainder, divisor_save, dividend_save; + logic div_operand_diff; + logic div_start; + + assign divisor = op_b; + assign dividend = op_a; + assign overflow = (dividend == 32'h8000_0000) && (divisor == 32'hffff_ffff) && idex.sign_sign; + assign div_zero = (divisor == 32'h0); + assign div_start = operand_diff && ~operation[2] & ~overflow & ~div_zero; + + radix4_divider div_i ( + .CLK(CLK), + .nRST(nRST), + .divisor(divisor), + .dividend(dividend), + .is_signed(idex.sign_sign), + .start(div_start), + .remainder(remainder), + .quotient(quotient), + .finished(div_finished) + ); + + /* Result */ + + always_comb begin + casez (operation) + 3'b1??: begin // MUL + eif.busy = ~mul_finished; + eif.reg_wdata = idex.lower_word ? + product[WORD_SIZE-1:0] + : product[(WORD_SIZE*2)-1 : WORD_SIZE]; + end + 3'b01?: begin // DIV + eif.busy = ~div_finished & ~(div_zero | overflow); + if (div_zero) begin + eif.reg_wdata = idex.sign_sign ? 32'hffff_ffff : 32'h7fff_ffff; + end else if (overflow) begin + eif.reg_wdata = 32'h8000_0000; + end else begin + eif.reg_wdata = quotient; + end + end + 3'b001: begin // REM + eif.busy = ~div_finished & ~(div_zero | overflow); + if (div_zero) begin + eif.reg_wdata = dividend; + end else if (overflow) begin + eif.reg_wdata = 32'h0000_0000; + end else begin + eif.reg_wdata = remainder; + end + end + default: begin + eif.busy = 1'b0; + eif.reg_wdata = 32'hBAD3_BAD3; + end + endcase end - end - - - /* MULTIPLICATION */ - - // multiplier signals - word_t multiplicand, multiplier; - logic [(WORD_SIZE*2)-1:0] product; - logic mul_finished; - logic mul_start; - - assign multiplicand = op_a; - assign multiplier = op_b; - assign mul_start = operand_diff && operation[2]; - - // Module instantiations - shift_add_multiplier #(.N(WORD_SIZE)) mult_i ( - .CLK(CLK), - .nRST(nRST), - .multiplicand(multiplicand), - .multiplier(multiplier), - .product(product), - .is_signed(is_signed), - .start(mul_start), - .finished(mul_finished) - ); - - - /* DIVISION / REMAINDER */ - - logic overflow, div_zero, div_finished; - word_t divisor, dividend, quotient, remainder, divisor_save, dividend_save; - logic div_operand_diff; - logic div_start; - - assign divisor = op_b; - assign dividend = op_a; - assign overflow = (dividend == 32'h8000_0000) && (divisor == 32'hffff_ffff) && idex.sign_sign; - assign div_zero = (divisor == 32'h0); - assign div_start = operand_diff && ~operation[2] & ~overflow & ~div_zero; - - shift_test_restore_divider #(.N(WORD_SIZE)) div_i ( - .CLK(CLK), - .nRST(nRST), - .divisor(divisor), - .dividend(dividend), - .is_signed(idex.sign_sign), - .start(div_start), - .remainder(remainder), - .quotient(quotient), - .finished(div_finished) - ); - - /* Result */ - - always_comb begin - casez (operation) - 3'b1?? : begin // MUL - eif.busy = ~mul_finished; - eif.reg_wdata = idex.lower_word ? product[WORD_SIZE-1:0] : product[(WORD_SIZE*2)-1 : WORD_SIZE]; - end - 3'b01? : begin // DIV - eif.busy = ~div_finished & ~(div_zero | overflow); - if(div_zero) begin - eif.reg_wdata = idex.sign_sign ? 32'hffff_ffff : 32'h7fff_ffff; - end else if (overflow) begin - eif.reg_wdata = 32'h8000_0000; - end else begin - eif.reg_wdata = quotient; - end - end - 3'b001 : begin // REM - eif.busy = ~div_finished & ~(div_zero | overflow); - if(div_zero) begin - eif.reg_wdata = dividend; - end else if (overflow) begin - eif.reg_wdata = 32'h0000_0000; - end else begin - eif.reg_wdata = remainder; - end - end - default : begin - eif.busy = 1'b0; - eif.reg_wdata = 32'hBAD3_BAD3; - end - endcase - end endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/rv32m_memory.sv b/source_code/risc_mgmt/extensions/rv32m/rv32m_memory.sv index 5e952455f..52b5a1b67 100644 --- a/source_code/risc_mgmt/extensions/rv32m/rv32m_memory.sv +++ b/source_code/risc_mgmt/extensions/rv32m/rv32m_memory.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,24 +20,25 @@ * Email: jskubic@purdue.edu * Date Created: 02/07/2017 * Description: Memory stage for standard RV32M extension -* This stage will do nothing. +* This stage will do nothing. */ `include "risc_mgmt_memory_if.vh" module rv32m_memory ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_memory_if.ext mif, - //stage to stage connection - input rv32m_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_memory_if.ext mif, + //stage to stage connection + input rv32m_pkg::execute_memory_t exmem ); - //prevent this extension from accessing the core - assign mif.exception = 1'b0; - assign mif.busy = 1'b0; - assign mif.reg_w = 1'b0; - assign mif.mem_ren = 1'b0; - assign mif.mem_wen = 1'b0; + //prevent this extension from accessing the core + assign mif.exception = 1'b0; + assign mif.busy = 1'b0; + assign mif.reg_w = 1'b0; + assign mif.mem_ren = 1'b0; + assign mif.mem_wen = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/shift_add_multiplier.sv b/source_code/risc_mgmt/extensions/rv32m/shift_add_multiplier.sv index a3c845637..41227fb89 100644 --- a/source_code/risc_mgmt/extensions/rv32m/shift_add_multiplier.sv +++ b/source_code/risc_mgmt/extensions/rv32m/shift_add_multiplier.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,60 +20,59 @@ * Email: jskubic@purdue.edu * Date Created: 02/15/2017 * Description: N bit parameterized shift and add multiplier -* Takes up to N+1 cycles to compute the product. +* Takes up to N+1 cycles to compute the product. */ -module shift_add_multiplier # ( - parameter N = 32 -) -( - input logic CLK, nRST, - input logic [N-1:0] multiplicand, - input logic [N-1:0] multiplier, - input logic [1:0] is_signed, - input logic start, - output logic [(N*2)-1:0] product, - output logic finished +module shift_add_multiplier #( + parameter int N = 32 +) ( + input logic CLK, + nRST, + input logic [N-1:0] multiplicand, + input logic [N-1:0] multiplier, + input logic [1:0] is_signed, + input logic start, + output logic [(N*2)-1:0] product, + output logic finished ); - logic [(N*2)-1:0] multiplier_reg, multiplicand_reg; - logic [(N*2)-1:0] multiplier_ext, multiplicand_ext; - logic [(N*2)-1:0] partial_product; - logic mult_complete, adjust_product; + logic [(N*2)-1:0] multiplier_reg, multiplicand_reg; + logic [(N*2)-1:0] multiplier_ext, multiplicand_ext; + logic [(N*2)-1:0] partial_product; + logic mult_complete, adjust_product; - assign mult_complete = !(|multiplier_reg); - assign adjust_product = (is_signed[0] & multiplier[N-1]) ^ (is_signed[1] & multiplicand[N-1]); - assign partial_product = multiplier_reg[0] ? multiplicand_reg : '0; - assign multiplier_ext = (~{{N{multiplier[N-1]}},multiplier}) + 1; - assign multiplicand_ext = (~{{N{multiplicand[N-1]}},multiplicand}) + 1; + assign mult_complete = !(|multiplier_reg); + assign adjust_product = (is_signed[0] & multiplier[N-1]) ^ (is_signed[1] & multiplicand[N-1]); + assign partial_product = multiplier_reg[0] ? multiplicand_reg : '0; + assign multiplier_ext = (~{{N{multiplier[N-1]}},multiplier}) + 1; + assign multiplicand_ext = (~{{N{multiplicand[N-1]}},multiplicand}) + 1; - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - finished <= 1'b0; - else if (start) - finished <= 1'b0; - else if (mult_complete) - finished <= 1'b1; - end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) finished <= 1'b0; + else if (start) finished <= 1'b0; + else if (mult_complete) finished <= 1'b1; + end - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) begin - multiplicand_reg <= '0; - multiplier_reg <= '0; - product <= '0; - end else if(start) begin - multiplicand_reg <= (is_signed[1] && multiplicand[N-1]) ? multiplicand_ext : {{N{1'b0}}, multiplicand}; - multiplier_reg <= (is_signed[0] && multiplier[N-1]) ? multiplier_ext : {{N{1'b0}}, multiplier}; - product <= '0; - end else if (mult_complete & ~finished) begin // adjust sign on product - multiplicand_reg <= multiplicand_reg; - multiplier_reg <= multiplier_reg; - product <= adjust_product ? (~product)+1 : product; - end else if (~finished) begin - multiplicand_reg <= multiplicand_reg << 1; - multiplier_reg <= multiplier_reg >> 1; - product <= product + partial_product; + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + multiplicand_reg <= '0; + multiplier_reg <= '0; + product <= '0; + end else if (start) begin + multiplicand_reg <= (is_signed[1] && multiplicand[N-1]) ? + multiplicand_ext : {{N{1'b0}}, multiplicand}; + multiplier_reg <= (is_signed[0] && multiplier[N-1]) ? + multiplier_ext : {{N{1'b0}}, multiplier}; + product <= '0; + end else if (mult_complete & ~finished) begin // adjust sign on product + multiplicand_reg <= multiplicand_reg; + multiplier_reg <= multiplier_reg; + product <= adjust_product ? (~product) + 1 : product; + end else if (~finished) begin + multiplicand_reg <= multiplicand_reg << 1; + multiplier_reg <= multiplier_reg >> 1; + product <= product + partial_product; + end end - end endmodule diff --git a/source_code/risc_mgmt/extensions/rv32m/shift_test_restore_divider.sv b/source_code/risc_mgmt/extensions/rv32m/shift_test_restore_divider.sv index eda79f514..ba9e441cd 100644 --- a/source_code/risc_mgmt/extensions/rv32m/shift_test_restore_divider.sv +++ b/source_code/risc_mgmt/extensions/rv32m/shift_test_restore_divider.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,73 +23,70 @@ */ module shift_test_restore_divider #( - parameter N = 32 -)( - input logic CLK, nRST, - input logic [N-1:0] divisor, dividend, - input logic is_signed, - input logic start, - output logic [N-1:0] remainder, quotient, - output logic finished + parameter int N = 32 +) ( + input logic CLK, + nRST, + input logic [N-1:0] divisor, + dividend, + input logic is_signed, + input logic start, + output logic [N-1:0] remainder, + quotient, + output logic finished ); - localparam COUNTER_BITS = $clog2(N) + 1; - localparam U_Q = N-1; - localparam U_R = (2*N)-1; + localparam int COUNTER_BITS = $clog2(N) + 1; + localparam int U_Q = N - 1; + localparam int U_R = (2 * N) - 1; - logic [(2*N)+1:0] result; - assign {remainder, quotient} = result[(2*N)-1:0]; - logic test_phase; - logic [COUNTER_BITS-1:0] counter; - logic [N-1:0] usign_divisor, usign_dividend; - logic adjustment_possible, adjust_quotient, adjust_remainder; - logic div_done; + logic [(2*N)+1:0] result; + assign {remainder, quotient} = result[(2*N)-1:0]; + logic test_phase; + logic [COUNTER_BITS-1:0] counter; + logic [N-1:0] usign_divisor, usign_dividend; + logic adjustment_possible, adjust_quotient, adjust_remainder; + logic div_done; - assign usign_divisor = is_signed & divisor[N-1] ? (~divisor)+1 : divisor; - assign usign_dividend = is_signed & dividend[N-1] ? (~dividend)+1 : dividend; - assign adjustment_possible = is_signed && (divisor[N-1] ^ dividend[N-1]); - assign adjust_quotient = adjustment_possible && ~quotient[N-1]; - assign adjust_remainder = is_signed && dividend[N-1]; - assign div_done = (counter == 0); + assign usign_divisor = is_signed & divisor[N-1] ? (~divisor) + 1 : divisor; + assign usign_dividend = is_signed & dividend[N-1] ? (~dividend) + 1 : dividend; + assign adjustment_possible = is_signed && (divisor[N-1] ^ dividend[N-1]); + assign adjust_quotient = adjustment_possible && ~quotient[N-1]; + assign adjust_remainder = is_signed && dividend[N-1]; + assign div_done = (counter == 0); - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) begin - result <= '0; - counter <= N; - test_phase <= 1'b0; - end else if (start) begin - result <= {{(N-1){1'b0}}, usign_dividend, 1'b0}; - counter <= N; - test_phase <= 1'b0; - end else if (counter > 0) begin - if(~test_phase) begin // shift and sub - result[U_R+1-:N+1] <= result[U_R+1-:N+1] - usign_divisor; - end else begin // check result - counter <= counter - 1; - if(result[U_R+1]) // negative remainder, must restore - result <= {(result[U_R+1-:N+1] + usign_divisor), result[U_Q:0]} << 1; - else - result <= {result[U_R-1:0], 1'b1}; - end - test_phase <= ~test_phase; - end else if (~finished) begin - if(adjust_quotient) - result[U_Q:0] <= (~result[U_Q:0])+1; - if(adjust_remainder) - result[U_R-:N] <= (~result[U_R+1-:N])+1; - //result[U_R-:N] <= (~({result[U_R],result[U_R-:N-1]}))+1; - else - result[U_R-:N] <= result[U_R+1-:N]; + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + result <= '0; + counter <= N; + test_phase <= 1'b0; + end else if (start) begin + result <= {{(N - 1) {1'b0}}, usign_dividend, 1'b0}; + counter <= N; + test_phase <= 1'b0; + end else if (counter > 0) begin + if (~test_phase) begin // shift and sub + result[U_R+1-:N+1] <= result[U_R+1-:N+1] - usign_divisor; + end else begin // check result + counter <= counter - 1; + if (result[U_R+1]) // negative remainder, must restore + result <= {(result[U_R+1-:N+1] + usign_divisor), result[U_Q:0]} << 1; + else result <= {result[U_R-1:0], 1'b1}; + end + test_phase <= ~test_phase; + end else if (~finished) begin + if (adjust_quotient) result[U_Q:0] <= (~result[U_Q:0]) + 1; + if (adjust_remainder) result[U_R-:N] <= (~result[U_R+1-:N]) + 1; + //result[U_R-:N] <= (~({result[U_R],result[U_R-:N-1]}))+1; + else + result[U_R-:N] <= result[U_R+1-:N]; + end end - end - always_ff @ (posedge CLK, negedge nRST) begin - if(~nRST) - finished <= 1'b0; - else if (start) - finished <= 1'b0; - else if (div_done) - finished <= 1'b1; - end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) finished <= 1'b0; + else if (start) finished <= 1'b0; + else if (div_done) finished <= 1'b1; + end endmodule diff --git a/source_code/risc_mgmt/extensions/template/template_decode.sv b/source_code/risc_mgmt/extensions/template/template_decode.sv index 2f68ad665..79f00d8f2 100644 --- a/source_code/risc_mgmt/extensions/template/template_decode.sv +++ b/source_code/risc_mgmt/extensions/template/template_decode.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,33 +16,34 @@ * * Filename: template_decode.sv * -* Created by: +* Created by: * Email: * Date Created: * Description: This extension is the Template for creating rytpe custom -* instructions. +* instructions. */ `include "risc_mgmt_decode_if.vh" module template_decode ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_decode_if.ext dif, - //stage to stage connection - output template_pkg::decode_execute_t idex + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_decode_if.ext dif, + //stage to stage connection + output template_pkg::decode_execute_t idex ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; - parameter OPCODE = 7'b000_1011; + parameter logic [7:0] OPCODE = 7'b000_1011; - rtype_t insn_rtype; - logic [9:0] funct10; + rtype_t insn_rtype; + logic [9:0] funct10; - // prevent this extension from accessing core - assign dif.insn_claim = 1'b0; // (dif.insn[6:0] == OPCODE); - assign funct10 = {insn_rtype.funct7, insn_rtype.funct3}; - assign dif.mem_to_reg = 1'b0; + // prevent this extension from accessing core + assign dif.insn_claim = 1'b0; // (dif.insn[6:0] == OPCODE); + assign funct10 = {insn_rtype.funct7, insn_rtype.funct3}; + assign dif.mem_to_reg = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/template/template_execute.sv b/source_code/risc_mgmt/extensions/template/template_execute.sv index 1f6f13932..aa9f6df59 100644 --- a/source_code/risc_mgmt/extensions/template/template_execute.sv +++ b/source_code/risc_mgmt/extensions/template/template_execute.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,26 +18,27 @@ * * Created by: * Email: -* Date Created: +* Date Created: * Description: This extension is the Template for creating rytpe custom -* instructions. +* instructions. */ `include "risc_mgmt_execute_if.vh" module template_execute ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_execute_if.ext eif, - //stage to stage connection - input template_pkg::decode_execute_t idex, - output template_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_execute_if.ext eif, + //stage to stage connection + input template_pkg::decode_execute_t idex, + output template_pkg::execute_memory_t exmem ); - //prevent this extension from accessing the core - assign eif.exception = 1'b0; - assign eif.busy = 1'b0; - assign eif.reg_w = 1'b0; - assign eif.branch_jump = 1'b0; + //prevent this extension from accessing the core + assign eif.exception = 1'b0; + assign eif.busy = 1'b0; + assign eif.reg_w = 1'b0; + assign eif.branch_jump = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/template/template_memory.sv b/source_code/risc_mgmt/extensions/template/template_memory.sv index 6eed54265..e5da82896 100644 --- a/source_code/risc_mgmt/extensions/template/template_memory.sv +++ b/source_code/risc_mgmt/extensions/template/template_memory.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,7 +18,7 @@ * * Created by: * Email: -* Date Created: +* Date Created: * Description: This extension is the Template for creating rytpe custom * instructions */ @@ -26,18 +26,19 @@ `include "risc_mgmt_memory_if.vh" module template_memory ( - input logic CLK, nRST, - //risc mgmt connection - risc_mgmt_memory_if.ext mif, - //stage to stage connection - input template_pkg::execute_memory_t exmem + input logic CLK, + nRST, + //risc mgmt connection + risc_mgmt_memory_if.ext mif, + //stage to stage connection + input template_pkg::execute_memory_t exmem ); - //prevent this extension from accessing the core - assign mif.exception = 1'b0; - assign mif.busy = 1'b0; - assign mif.reg_w = 1'b0; - assign mif.mem_ren = 1'b0; - assign mif.mem_wen = 1'b0; + //prevent this extension from accessing the core + assign mif.exception = 1'b0; + assign mif.busy = 1'b0; + assign mif.reg_w = 1'b0; + assign mif.mem_ren = 1'b0; + assign mif.mem_wen = 1'b0; endmodule diff --git a/source_code/risc_mgmt/extensions/wscript b/source_code/risc_mgmt/extensions/wscript deleted file mode 100644 index 260d8f489..000000000 --- a/source_code/risc_mgmt/extensions/wscript +++ /dev/null @@ -1,14 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -# Add your extension as a directory to recurse into here - -def configure(cnf): - cnf.recurse(template) - cnf.recurse(rv32m) - cnf.recurse(crc32) - -def sim_source(cnf): - cnf.recurse(template) - cnf.recurse(rv32m) - cnf.recurse(crc32) diff --git a/source_code/risc_mgmt/risc_mgmt.core b/source_code/risc_mgmt/risc_mgmt.core new file mode 100644 index 000000000..c43cc65ff --- /dev/null +++ b/source_code/risc_mgmt/risc_mgmt.core @@ -0,0 +1,33 @@ +CAPI=2: +name: socet:riscv:risc_mgmt:0.1.0 +description: risc management + +filesets: + rtl: + files: + - risc_mgmt_wrapper.sv + - extensions/crc32/crc32.sv + - extensions/crc32/crc32_decode.sv + - extensions/crc32/crc32_execute.sv + - extensions/crc32/crc32_memory.sv + - extensions/rv32m/rv32m_memory.sv + - extensions/rv32m/shift_add_multiplier.sv + - extensions/rv32m/shift_test_restore_divider.sv + - extensions/rv32m/carry_save_adder.sv + - extensions/rv32m/flex_counter_mul.sv + - extensions/rv32m/full_adder.sv + - extensions/rv32m/pp_mul32.sv + - extensions/rv32m/radix4_divider.sv + - extensions/rv32m/rv32m_execute.sv + - extensions/rv32m/rv32m_decode.sv + - extensions/template/template_decode.sv + - extensions/template/template_execute.sv + - extensions/template/template_memory.sv + - tspp/tspp_risc_mgmt.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + toplevel: risc_mgmt_wrapper \ No newline at end of file diff --git a/source_code/risc_mgmt/risc_mgmt_wrapper.sv b/source_code/risc_mgmt/risc_mgmt_wrapper.sv index 2e91676da..3feb965f5 100644 --- a/source_code/risc_mgmt/risc_mgmt_wrapper.sv +++ b/source_code/risc_mgmt/risc_mgmt_wrapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -26,10 +26,11 @@ `include "risc_mgmt_if.vh" module risc_mgmt_wrapper ( - input logic CLK, nRST, - risc_mgmt_if.ts_rmgmt rm_if + input logic CLK, + nRST, + risc_mgmt_if.ts_rmgmt rm_if ); - tspp_risc_mgmt risc_mgmt_i(.*); + tspp_risc_mgmt risc_mgmt_i (.*); endmodule diff --git a/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv b/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv index ce5414617..f06b9d5a0 100644 --- a/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv +++ b/source_code/risc_mgmt/tspp/tspp_risc_mgmt.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,8 +20,8 @@ * Email: jskubic@purdue.edu * Date Created: 02/07/2017 * Description: Top Level Module for RISC-MGMT -* Provides the interface between extensions -* and the standard core. +* Provides the interface between extensions +* and the standard core. * * This version will connect extensions * to a two stage pipeline (tspp). @@ -32,154 +32,152 @@ `include "risc_mgmt_if.vh" module tspp_risc_mgmt ( - input logic CLK, nRST, - risc_mgmt_if.ts_rmgmt rm_if + input logic CLK, + nRST, + risc_mgmt_if.ts_rmgmt rm_if ); - import rv32i_types_pkg::*; - - parameter N_EXTENSIONS = `NUM_EXTENSIONS; - localparam N_EXT_BITS = $clog2(N_EXTENSIONS); - - /****************************************************************** - * Signal Instantiations - ******************************************************************/ - - // Decode Stage Signals - word_t [N_EXTENSIONS-1:0] d_insn; - logic [N_EXTENSIONS-1:0] d_insn_claim; - logic [N_EXTENSIONS-1:0] d_mem_to_reg; // ignored in two stage pipe rmgmt - logic [N_EXTENSIONS-1:0][4:0] d_rsel_s_0; - logic [N_EXTENSIONS-1:0][4:0] d_rsel_s_1; - logic [N_EXTENSIONS-1:0][4:0] d_rsel_d; - - //Execute Stage Signals - logic [N_EXTENSIONS-1:0] e_start; - logic [N_EXTENSIONS-1:0] e_exception; - logic [N_EXTENSIONS-1:0] e_busy; - word_t [N_EXTENSIONS-1:0] e_rdata_s_0; - word_t [N_EXTENSIONS-1:0] e_rdata_s_1; - logic [N_EXTENSIONS-1:0] e_branch_jump; - word_t [N_EXTENSIONS-1:0] e_pc; - word_t [N_EXTENSIONS-1:0] e_br_j_addr; - word_t [N_EXTENSIONS-1:0] e_reg_wdata; - logic [N_EXTENSIONS-1:0] e_reg_w; - - //Memory Stage Signals - logic [N_EXTENSIONS-1:0] m_exception; - logic [N_EXTENSIONS-1:0] m_busy; - word_t [N_EXTENSIONS-1:0] m_mem_addr; - logic [N_EXTENSIONS-1:0] m_mem_ren; - logic [N_EXTENSIONS-1:0] m_mem_wen; - logic [N_EXTENSIONS-1:0] m_mem_busy; - logic [N_EXTENSIONS-1:0][3:0] m_mem_byte_en; - word_t [N_EXTENSIONS-1:0] m_mem_load; - word_t [N_EXTENSIONS-1:0] m_mem_store; - logic [N_EXTENSIONS-1:0] m_reg_wdata; - logic [N_EXTENSIONS-1:0] m_reg_w; - - - /****************************************************************** + import rv32i_types_pkg::*; + + parameter int N_EXTENSIONS = `NUM_EXTENSIONS; + localparam int N_EXT_BITS = $clog2(N_EXTENSIONS); + + /****************************************************************** + * Signal Instantiations + ******************************************************************/ + + // Decode Stage Signals + word_t [N_EXTENSIONS-1:0] d_insn; + logic [N_EXTENSIONS-1:0] d_insn_claim; + logic [N_EXTENSIONS-1:0] d_mem_to_reg; // ignored in two stage pipe rmgmt + logic [N_EXTENSIONS-1:0][4:0] d_rsel_s_0; + logic [N_EXTENSIONS-1:0][4:0] d_rsel_s_1; + logic [N_EXTENSIONS-1:0][4:0] d_rsel_d; + + //Execute Stage Signals + logic [N_EXTENSIONS-1:0] e_start; + logic [N_EXTENSIONS-1:0] e_exception; + logic [N_EXTENSIONS-1:0] e_busy; + word_t [N_EXTENSIONS-1:0] e_rdata_s_0; + word_t [N_EXTENSIONS-1:0] e_rdata_s_1; + logic [N_EXTENSIONS-1:0] e_branch_jump; + word_t [N_EXTENSIONS-1:0] e_pc; + word_t [N_EXTENSIONS-1:0] e_br_j_addr; + word_t [N_EXTENSIONS-1:0] e_reg_wdata; + logic [N_EXTENSIONS-1:0] e_reg_w; + + //Memory Stage Signals + logic [N_EXTENSIONS-1:0] m_exception; + logic [N_EXTENSIONS-1:0] m_busy; + word_t [N_EXTENSIONS-1:0] m_mem_addr; + logic [N_EXTENSIONS-1:0] m_mem_ren; + logic [N_EXTENSIONS-1:0] m_mem_wen; + logic [N_EXTENSIONS-1:0] m_mem_busy; + logic [N_EXTENSIONS-1:0][3:0] m_mem_byte_en; + word_t [N_EXTENSIONS-1:0] m_mem_load; + word_t [N_EXTENSIONS-1:0] m_mem_store; + logic [N_EXTENSIONS-1:0] m_reg_wdata; + logic [N_EXTENSIONS-1:0] m_reg_w; + + + /****************************************************************** * Extension connections * - * Modify RISC_MGMT_EXTENSIONS in component_selection_defines.vh + * Modify RISC_MGMT_EXTENSIONS in component_selection_defines.vh * to edit the included instruction extensions * - ******************************************************************/ - - `RISC_MGMT_EXTENSIONS - - - /****************************************************************** - * Begin RISC-MGMT Logic - ******************************************************************/ - - /* Send instruction to extensions */ - assign d_insn = {N_EXTENSIONS{rm_if.insn}}; - - /* Extension Tokens */ - - integer i; - logic [N_EXTENSIONS-1:0] tokens; - logic ext_is_active; - logic [N_EXT_BITS-1:0] active_ext; - - assign tokens = d_insn_claim; - assign ext_is_active = |tokens; - assign rm_if.active_insn = ext_is_active; - - assign rm_if.ex_token = ext_is_active; - - always_comb begin - active_ext = 0; - for(i = 0; i < N_EXTENSIONS; i++) begin - if(tokens[i]) - active_ext = i; + ******************************************************************/ + + `RISC_MGMT_EXTENSIONS + + + /****************************************************************** + * Begin RISC-MGMT Logic + ******************************************************************/ + + /* Send instruction to extensions */ + assign d_insn = {N_EXTENSIONS{rm_if.insn}}; + + /* Extension Tokens */ + + integer i; + logic [N_EXTENSIONS-1:0] tokens; + logic ext_is_active; + logic [N_EXT_BITS-1:0] active_ext; + + assign tokens = d_insn_claim; + assign ext_is_active = |tokens; + assign rm_if.active_insn = ext_is_active; + + assign rm_if.ex_token = ext_is_active; + + always_comb begin + active_ext = 0; + for (i = 0; i < N_EXTENSIONS; i++) begin + if (tokens[i]) active_ext = i; + end end - end - - /* Pipeline Control / Automatic Clock Gating + + /* Pipeline Control / Automatic Clock Gating * Not present in 2 stage pipeline implementation * All pipeline control is handled in standard core automatically * Stalls will be forwarded to the standard core */ - assign rm_if.execute_stall = e_busy[active_ext] && ext_is_active; - assign rm_if.memory_stall = m_busy[active_ext] && ext_is_active; + assign rm_if.execute_stall = e_busy[active_ext] && ext_is_active; + assign rm_if.memory_stall = m_busy[active_ext] && ext_is_active; - // start signal for multicycle execute stages - logic ex_start; + // start signal for multicycle execute stages + logic ex_start; - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) - ex_start <= 1'b0; - else if (rm_if.if_ex_enable) - ex_start <= 1'b1; - else - ex_start <= 1'b0; - end + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) ex_start <= 1'b0; + else if (rm_if.if_ex_enable) ex_start <= 1'b1; + else ex_start <= 1'b0; + end - assign e_start = {N_EXTENSIONS{ex_start}} & tokens; + assign e_start = {N_EXTENSIONS{ex_start}} & tokens; + assign rm_if.risc_mgmt_start = e_start; // to stall rv32c load buffer - /* Registerfile / Forwarding Logic + /* Registerfile / Forwarding Logic * Forwarding not present in 2 stage pipeline */ - - // Reg reads and decode - assign rm_if.req_reg_r = ext_is_active; - assign rm_if.rsel_s_0 = d_rsel_s_0[active_ext]; - assign rm_if.rsel_s_1 = d_rsel_s_1[active_ext]; - assign rm_if.rsel_d = d_rsel_d[active_ext]; - assign e_rdata_s_0 = {N_EXTENSIONS{rm_if.rdata_s_0}}; - assign e_rdata_s_1 = {N_EXTENSIONS{rm_if.rdata_s_1}}; - - // Reg Writeback - assign rm_if.req_reg_w = (e_reg_w[active_ext] || m_reg_w[active_ext]) && ext_is_active; - assign rm_if.reg_w = e_reg_w[active_ext] || m_reg_w[active_ext]; - assign rm_if.reg_wdata = e_reg_w[active_ext] ? e_reg_wdata[active_ext] : m_reg_wdata[active_ext]; - - - /* Branch Jump Control */ - - assign rm_if.req_br_j = e_branch_jump[active_ext] && ext_is_active; - assign rm_if.branch_jump = e_branch_jump[active_ext]; - assign rm_if.br_j_addr = e_br_j_addr[active_ext]; - assign e_pc = {N_EXTENSIONS{rm_if.pc}}; - - /* Memory Access Control */ - - assign rm_if.req_mem = (m_mem_ren[active_ext] || m_mem_wen[active_ext]) && ext_is_active; - assign rm_if.mem_addr = m_mem_addr[active_ext]; - assign rm_if.mem_byte_en = m_mem_byte_en[active_ext]; - assign rm_if.mem_store = m_mem_store[active_ext]; - assign rm_if.mem_ren = m_mem_ren[active_ext]; - assign rm_if.mem_wen = m_mem_wen[active_ext]; - assign m_mem_busy = {N_EXTENSIONS{rm_if.mem_busy}}; - assign m_mem_load = {N_EXTENSIONS{rm_if.mem_load}}; - - - /* Exception Reporting */ - assign rm_if.exception = (e_exception[active_ext] || m_exception[active_ext]) && ext_is_active; - assign rm_if.ex_cause = active_ext; - + + // Reg reads and decode + assign rm_if.req_reg_r = ext_is_active; + assign rm_if.rsel_s_0 = d_rsel_s_0[active_ext]; + assign rm_if.rsel_s_1 = d_rsel_s_1[active_ext]; + assign rm_if.rsel_d = d_rsel_d[active_ext]; + assign e_rdata_s_0 = {N_EXTENSIONS{rm_if.rdata_s_0}}; + assign e_rdata_s_1 = {N_EXTENSIONS{rm_if.rdata_s_1}}; + + // Reg Writeback + assign rm_if.req_reg_w = (e_reg_w[active_ext] || m_reg_w[active_ext]) && ext_is_active; + assign rm_if.reg_w = e_reg_w[active_ext] || m_reg_w[active_ext]; + assign rm_if.reg_wdata = e_reg_w[active_ext] ? e_reg_wdata[active_ext] : m_reg_wdata[active_ext]; + + + /* Branch Jump Control */ + + assign rm_if.req_br_j = e_branch_jump[active_ext] && ext_is_active; + assign rm_if.branch_jump = e_branch_jump[active_ext]; + assign rm_if.br_j_addr = e_br_j_addr[active_ext]; + assign e_pc = {N_EXTENSIONS{rm_if.pc}}; + + /* Memory Access Control */ + + assign rm_if.req_mem = (m_mem_ren[active_ext] || m_mem_wen[active_ext]) && ext_is_active; + assign rm_if.mem_addr = m_mem_addr[active_ext]; + assign rm_if.mem_byte_en = m_mem_byte_en[active_ext]; + assign rm_if.mem_store = m_mem_store[active_ext]; + assign rm_if.mem_ren = m_mem_ren[active_ext]; + assign rm_if.mem_wen = m_mem_wen[active_ext]; + assign m_mem_busy = {N_EXTENSIONS{rm_if.mem_busy}}; + assign m_mem_load = {N_EXTENSIONS{rm_if.mem_load}}; + + + /* Exception Reporting */ + assign rm_if.exception = (e_exception[active_ext] || m_exception[active_ext]) && ext_is_active; + assign rm_if.ex_cause = active_ext; + endmodule diff --git a/source_code/risc_mgmt/wscript b/source_code/risc_mgmt/wscript deleted file mode 100644 index 88a3b7fae..000000000 --- a/source_code/risc_mgmt/wscript +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/env python -#encoding: utf-8 - -def configure(cnf): - cnf.recurse(extensions) - cnf.recurse(tspp) - -def sim_source(cnf): - cnf.recurse(extensions) - cnf.recurse(tspp) diff --git a/source_code/rv32c/decompressor.sv b/source_code/rv32c/decompressor.sv new file mode 100644 index 000000000..7c60d5607 --- /dev/null +++ b/source_code/rv32c/decompressor.sv @@ -0,0 +1,287 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: decompressor.sv +* +* Created by: Jing Yin See +* Email: see4@purdue.edu +* Date Created: 12/20/2020 +* Description: Decompress RV32C instructions into full-size instruction +*/ + +`include "decompressor_if.vh" +module decompressor ( + decompressor_if.dcpr dcpr_if +); + logic c0_format, c1_format, c2_format; + logic upper3_0, upper3_1, upper3_2, upper3_3, upper3_4, upper3_5, upper3_6, upper3_7; + logic c_addi, c_addi16sp, c_addi14spn, c_slli, c_andi, c_srli, c_srai; + logic c_mv, c_add, c_and, c_or, c_xor, c_sub; + logic + c_lw, c_sw, c_lwsp, c_swsp, c_flw, c_fsw, c_flwsp, c_fswsp, c_fld, c_fsd, c_fldsp, c_fsdsp; + logic c_j, c_jal, c_jr, c_jalr, c_beqz, c_bnez; + logic c_li, c_lui, c_nop, c_ebreak; + logic rtype, itype, stype, btype, utype, jtype; + logic [ 2:0] funct3; + logic [ 6:0] funct7; + logic [ 5:0] imm_i_c; + logic [11:0] imm_i; + logic [10:0] imm_j; + logic [ 7:0] imm_b; + logic [4:0] rd, rs2; + logic [4:0] offset_c, offset_c_df; + logic [5:0] offset_csp, offset_csp_df; + logic [11:0] offset, offset_df; + logic [20:0] jump_offset; + logic [12:0] branch_offset; + + assign c0_format = dcpr_if.inst16[1:0] == 2'b00; + assign c1_format = dcpr_if.inst16[1:0] == 2'b01; + assign c2_format = dcpr_if.inst16[1:0] == 2'b10; + + assign upper3_0 = dcpr_if.inst16[15:13] == 3'b000; + assign upper3_1 = dcpr_if.inst16[15:13] == 3'b001; + assign upper3_2 = dcpr_if.inst16[15:13] == 3'b010; + assign upper3_3 = dcpr_if.inst16[15:13] == 3'b011; + assign upper3_4 = dcpr_if.inst16[15:13] == 3'b100; + assign upper3_5 = dcpr_if.inst16[15:13] == 3'b101; + assign upper3_6 = dcpr_if.inst16[15:13] == 3'b110; + assign upper3_7 = dcpr_if.inst16[15:13] == 3'b111; + + // Immediate + assign c_addi = upper3_0 & (dcpr_if.inst16[11:7] != 5'd0) & c1_format; + assign c_addi16sp = upper3_3 & (dcpr_if.inst16[11:7] == 5'd2) & c1_format; + assign c_addi14spn = upper3_0 & (dcpr_if.inst16[12:5] != 8'd0) & c0_format; + assign c_slli = upper3_0 & c2_format; + assign c_andi = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd2); + assign c_srli = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd0); + assign c_srai = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd1); + + // Register + assign c_mv = upper3_4 & c2_format & dcpr_if.inst16[12] == 1'b0 & dcpr_if.inst16[6:2] != 5'd0; + assign c_add = upper3_4 & c2_format & dcpr_if.inst16[12] + & dcpr_if.inst16[6:2] != 5'd0 & dcpr_if.inst16[11:7] != 5'd0; + assign c_and = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) + & (dcpr_if.inst16[6:5] == 2'd3); + assign c_or = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) + & (dcpr_if.inst16[6:5] == 2'd2); + assign c_xor = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) + & (dcpr_if.inst16[6:5] == 2'd1); + assign c_sub = upper3_4 & c1_format & (dcpr_if.inst16[11:10] == 2'd3) + & (dcpr_if.inst16[6:5] == 2'd0); + + // Load/Store + assign c_lw = upper3_2 & c0_format; + assign c_sw = upper3_6 & c0_format; + assign c_flw = upper3_3 & c0_format; + assign c_fsw = upper3_7 & c0_format; + assign c_fld = upper3_1 & c0_format; + assign c_fsd = upper3_5 & c0_format; + assign c_lwsp = upper3_2 & c2_format; + assign c_swsp = upper3_6 & c2_format; + assign c_flwsp = upper3_3 & c2_format; + assign c_fswsp = upper3_7 & c2_format; + assign c_fldsp = upper3_1 & c2_format; + assign c_fsdsp = upper3_5 & c2_format; + + // Control Transfer + assign c_j = upper3_5 & c1_format; + assign c_jal = upper3_1 & c1_format; + assign c_jr = upper3_4 & dcpr_if.inst16[12] == 1'b0 & dcpr_if.inst16[6:2] == 5'd0 & c2_format; + assign c_jalr = upper3_4 & c2_format & dcpr_if.inst16[12] + & dcpr_if.inst16[6:2] == 5'd0 & dcpr_if.inst16[11:7] != 5'd0; + assign c_beqz = upper3_6 & c1_format; + assign c_bnez = upper3_7 & c1_format; + + // Constant Generation + assign c_li = upper3_2 & c1_format; + assign c_lui = upper3_3 & (dcpr_if.inst16[11:7] != 5'd0) + & (dcpr_if.inst16[11:7] != 5'd2) & c1_format; + + // Others + //assign illegal = dcpr_if.inst16 == 16'd0; + assign c_nop = upper3_0 & (dcpr_if.inst16[12:2] == 11'd0) & c1_format; + assign c_ebreak = upper3_4 & c2_format & dcpr_if.inst16[12] & dcpr_if.inst16[11:2] == 10'd0; + + // Inst type + assign rtype = c_mv | c_add | c_and | c_or | c_xor | c_sub; + assign itype = c_addi | c_addi16sp | c_addi14spn | c_slli | c_andi | c_srli + | c_lw | c_flw | c_lwsp | c_flwsp | c_li | c_nop | c_srai | c_fld | c_fldsp; + assign stype = c_sw | c_swsp | c_fsw | c_fswsp | c_fsd | c_fsdsp; + assign btype = c_beqz | c_bnez; + assign utype = c_lui; + assign jtype = c_j | c_jal | c_jr | c_jalr; + + assign imm_i_c = {dcpr_if.inst16[12], dcpr_if.inst16[6:2]}; + assign imm_j = { + dcpr_if.inst16[12], + dcpr_if.inst16[8], + dcpr_if.inst16[10:9], + dcpr_if.inst16[6], + dcpr_if.inst16[7], + dcpr_if.inst16[2], + dcpr_if.inst16[11], + dcpr_if.inst16[5:3] + }; + assign imm_b = { + dcpr_if.inst16[12], + dcpr_if.inst16[6:5], + dcpr_if.inst16[2], + dcpr_if.inst16[11:10], + dcpr_if.inst16[4:3] + }; + assign offset_c = {dcpr_if.inst16[5], dcpr_if.inst16[12:10], dcpr_if.inst16[6]}; + assign offset_csp = (c_swsp | c_fswsp) ? {dcpr_if.inst16[8:7], dcpr_if.inst16[12:9]} + : {dcpr_if.inst16[3:2], dcpr_if.inst16[12], dcpr_if.inst16[6:4]}; + assign offset_c_df = {dcpr_if.inst16[6:5], dcpr_if.inst16[12:10]}; + assign offset_csp_df = c_fsdsp ? {dcpr_if.inst16[9:7], dcpr_if.inst16[12:10]} + : {dcpr_if.inst16[4:2], dcpr_if.inst16[12], dcpr_if.inst16[6:5]}; + + // Sign Extend immediate + always_comb begin + if (c_srai) imm_i = {7'b0100000, imm_i_c[4:0]}; + else if (c_srli | c_slli) imm_i = {7'b0000000, imm_i_c[4:0]}; + else if (c_addi16sp) + imm_i = { + {2{dcpr_if.inst16[12]}}, + { + dcpr_if.inst16[12], + dcpr_if.inst16[4:3], + dcpr_if.inst16[5], + dcpr_if.inst16[2], + dcpr_if.inst16[6] + }, + 4'd0 + }; + else if (c_addi14spn) + imm_i = { + 2'd0, + { + dcpr_if.inst16[10:7], + dcpr_if.inst16[12], + dcpr_if.inst16[11], + dcpr_if.inst16[5], + dcpr_if.inst16[6] + }, + 2'd0 + }; + else imm_i = {{6{imm_i_c[5]}}, imm_i_c}; + end + + // Sign Extend offset + assign offset = (c_sw | c_lw | c_fsw | c_flw) ? {5'd0, offset_c, 2'd0} + : {4'd0, offset_csp, 2'd0}; + assign offset_df = (c_fld | c_fsd) ? {4'd0, offset_c_df, 3'd0} : {3'd0, offset_csp_df, 3'd0}; + assign jump_offset = {{9{imm_j[10]}}, imm_j, 1'b0}; + assign branch_offset = {{4{imm_b[7]}}, imm_b, 1'b0}; + + // Select funct3 in 32 bit instruction + always_comb begin + // if (c_addi | c_addi16sp | c_addi14spn | c_add | c_sub | c_mv | c_li | c_nop | c_beqz) funct3 = 3'b000; + if (c_slli | c_bnez) funct3 = 3'b001; + else if (c_andi | c_and) funct3 = 3'b111; + else if (c_srli | c_srai) funct3 = 3'b101; + else if (c_or) funct3 = 3'b110; + else if (c_xor) funct3 = 3'b100; + else if (c_sw | c_fsw | c_swsp | c_fswsp | c_lw | c_flw | c_lwsp | c_flwsp) funct3 = 3'b010; + else if (c_fld | c_fsd | c_fldsp | c_fsdsp) funct3 = 3'b011; + else funct3 = 3'b000; + end + + // Select register + assign rd = (c_srli | c_srai | c_and | c_or | c_xor | c_sub | c_sw | c_lw | c_fsw | c_fsd | c_flw | c_fld | c_beqz | c_bnez | c_andi) + ? {2'd1, dcpr_if.inst16[9:7]} : dcpr_if.inst16[11:7]; + assign rs2 = (c_mv | c_add) ? dcpr_if.inst16[6:2] : {2'd1, dcpr_if.inst16[4:2]}; + + // Encode full 32 bit instruction + always_comb begin + if (rtype) begin + if (c_sub) dcpr_if.inst32 = {7'b0100000, rs2, rd, funct3, rd, 7'b0110011}; + else if (c_mv) dcpr_if.inst32 = {7'b0000000, rs2, 5'd0, funct3, rd, 7'b0110011}; + else dcpr_if.inst32 = {7'b0000000, rs2, rd, funct3, rd, 7'b0110011}; + end else if (itype) begin + if (c_addi16sp) dcpr_if.inst32 = {imm_i, 5'd2, funct3, 5'd2, 7'b0010011}; + else if (c_addi14spn) dcpr_if.inst32 = {imm_i, 5'd2, funct3, rs2, 7'b0010011}; + else if (c_li) dcpr_if.inst32 = {imm_i, 5'd0, funct3, rd, 7'b0010011}; + else if (c_lw) dcpr_if.inst32 = {offset, rd, funct3, rs2, 7'b0000011}; + else if (c_flw) dcpr_if.inst32 = {offset, rd, funct3, rs2, 7'b0000111}; + else if (c_fld) dcpr_if.inst32 = {offset_df, rd, funct3, rs2, 7'b0000111}; + else if (c_lwsp) dcpr_if.inst32 = {offset, 5'd2, funct3, rd, 7'b0000011}; + else if (c_flwsp) dcpr_if.inst32 = {offset, 5'd2, funct3, rd, 7'b0000111}; + else if (c_fldsp) dcpr_if.inst32 = {offset_df, 5'd2, funct3, rd, 7'b0000111}; + else if (c_nop) dcpr_if.inst32 = {17'd0, funct3, 5'd0, 7'b0010011}; + else dcpr_if.inst32 = {imm_i, rd, funct3, rd, 7'b0010011}; + end else if (stype) begin + if (c_sw) dcpr_if.inst32 = {offset[11:5], rs2, rd, funct3, offset[4:0], 7'b0100011}; + else if (c_fsw) + dcpr_if.inst32 = {offset[11:5], rs2, rd, funct3, offset[4:0], 7'b0100111}; + else if (c_fsd) + dcpr_if.inst32 = {offset_df[11:5], rs2, rd, funct3, offset_df[4:0], 7'b0100111}; + else if (c_fswsp) + dcpr_if.inst32 = { + offset[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset[4:0], 7'b0100111 + }; + else if (c_fsdsp) + dcpr_if.inst32 = { + offset_df[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset_df[4:0], 7'b0100111 + }; + else + dcpr_if.inst32 = { + offset[11:5], dcpr_if.inst16[6:2], 5'd2, funct3, offset[4:0], 7'b0100011 + }; + end else if (btype) + dcpr_if.inst32 = { + branch_offset[12], + branch_offset[10:5], + 5'd0, + rd, + funct3, + branch_offset[4:1], + branch_offset[11], + 7'b1100011 + }; + else if (utype) dcpr_if.inst32 = {{{14{imm_i_c[5]}}, imm_i_c}, rd, 7'b0110111}; + else if (jtype) begin + if (c_j) + dcpr_if.inst32 = { + jump_offset[20], + jump_offset[10:1], + jump_offset[11], + jump_offset[19:12], + 5'd0, + 7'b1101111 + }; + else if (c_jal) + dcpr_if.inst32 = { + jump_offset[20], + jump_offset[10:1], + jump_offset[11], + jump_offset[19:12], + 5'd1, + 7'b1101111 + }; + else if (c_jr) dcpr_if.inst32 = {12'd0, rd, 3'd0, 5'd0, 7'b1100111}; + else dcpr_if.inst32 = {12'd0, rd, 3'd0, 5'd1, 7'b1100111}; + end else if (c_ebreak) dcpr_if.inst32 = {12'b000000000001, 5'd0, 3'd0, 5'd0, 7'b1110011}; + else dcpr_if.inst32 = 32'd0; + end + + // assign dcpr_if.edit_rd = c_addi14spn | c_lw | c_and | c_sub | c_or | c_xor | c_srli | c_srai | c_andi; + // assign dcpr_if.edit_rs1 = c_lw | c_sw | c_and | c_sub | c_or | c_xor | c_beqz | c_bnez | c_srli | c_srai | c_andi; + // assign dcpr_if.edit_rs2 = c_sw | c_and | c_sub | c_or | c_xor; + assign dcpr_if.c_ena = dcpr_if.inst16[1:0] != 2'b11; +endmodule + diff --git a/source_code/rv32c/fetch_buffer.sv b/source_code/rv32c/fetch_buffer.sv new file mode 100644 index 000000000..e9bd3f3e7 --- /dev/null +++ b/source_code/rv32c/fetch_buffer.sv @@ -0,0 +1,145 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: fetch_buffer.sv +* +* Created by: Jing Yin See +* Email: see4@purdue.edu +* Date Created: 01/15/2021 +* Description: Generate next pc and instruction memory pc, output instructions +*/ + +`include "fetch_buffer_if.vh" +module fetch_buffer ( + input logic clk, + n_rst, + fetch_buffer_if.fb fb_if +); + logic [15:0] buffer, nextbuffer; + logic [31:0] pc, next_imem_pc; + logic combine, combine_reg, waitnext, waitnext_reg; + logic [31:0] final_inst_store, final_inst; + logic reset_next, finished; + logic inst_arrived_delay; + + always_ff @(posedge clk, negedge n_rst) begin + if (n_rst == 0) inst_arrived_delay <= 1'b0; + else inst_arrived_delay <= fb_if.inst_arrived; + end + + + assign fb_if.done_earlier = inst_arrived_delay & waitnext_reg & !fb_if.ex_busy; + //assign fb_if.done_earlier = 0; + + // Buffer and PC logic + always_ff @(posedge clk, negedge n_rst) begin + if (n_rst == 0) begin + buffer <= 16'd0; + combine_reg <= 1'b0; + waitnext_reg <= 1'b0; + fb_if.imem_pc <= fb_if.reset_pc_val; + pc <= fb_if.reset_pc_val; + final_inst_store <= 32'd0; + reset_next <= 1'b0; + end else if (fb_if.reset_en) begin + buffer <= nextbuffer; + combine_reg <= combine; + waitnext_reg <= waitnext; + fb_if.imem_pc <= next_imem_pc; + pc <= fb_if.nextpc; + final_inst_store <= final_inst; + reset_next <= 1'b1; + end else if ((fb_if.inst_arrived || fb_if.done_earlier) && fb_if.pc_update) begin + buffer <= nextbuffer; + combine_reg <= combine; + waitnext_reg <= waitnext; + fb_if.imem_pc <= next_imem_pc; + pc <= fb_if.nextpc; + final_inst_store <= final_inst; + reset_next <= 1'b0; + end + end + + always_comb begin + next_imem_pc = fb_if.imem_pc; + fb_if.nextpc = pc; + nextbuffer = buffer; + combine = 1'b0; + waitnext = 1'b0; + final_inst = final_inst_store; + finished = 1'b0; + // Jump/Branch condition when misaligned + if (fb_if.inst_arrived & reset_next & (pc != fb_if.imem_pc)) begin + next_imem_pc = fb_if.imem_pc + 4; + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + final_inst = {16'd0, fb_if.inst[31:16]}; + fb_if.nextpc = pc + 2; + finished = 1'b1; + end else begin + combine = 1; + nextbuffer = fb_if.inst[31:16]; + // nextpc = pc + 4; + finished = 1'b0; + end + end else if (fb_if.reset_en) begin + next_imem_pc = {fb_if.reset_pc[31:2], 2'b0}; // Always aligned + fb_if.nextpc = fb_if.reset_pc; // Can be misaligned + nextbuffer = 16'd0; + final_inst = 32'd0; + end else if (fb_if.inst_arrived & combine_reg) begin + final_inst = {fb_if.inst[15:0], buffer}; + nextbuffer = fb_if.inst[31:16]; + fb_if.nextpc = pc + 4; + finished = 1'b1; + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + waitnext = 1; + next_imem_pc = fb_if.imem_pc; + end else begin + combine = 1; + next_imem_pc = fb_if.imem_pc + 4; + end + end else if (fb_if.done_earlier | (fb_if.inst_arrived & waitnext_reg)) begin + final_inst = {16'b0, buffer}; + finished = 1'b1; + fb_if.nextpc = pc + 2; + next_imem_pc = fb_if.imem_pc + 4; + end else if (fb_if.inst[1:0] != 2'b11) begin // lower 16 bits are compressed + final_inst = fb_if.inst[15:0]; + nextbuffer = fb_if.inst[31:16]; + fb_if.nextpc = pc + 2; + finished = 1'b1; + if (fb_if.inst[17:16] != 2'b11) begin // upper 16 bits are compressed + waitnext = 1; + next_imem_pc = fb_if.imem_pc; + end else begin + combine = 1; + next_imem_pc = fb_if.imem_pc + 4; + end + end else if (fb_if.inst[1:0] == 2'b11) begin + final_inst = fb_if.inst; + next_imem_pc = fb_if.imem_pc + 4; + fb_if.nextpc = pc + 4; + finished = 1'b1; + end + end + + //logic [31:0] debug; + assign fb_if.result = final_inst; + //assign debug = fb_if.inst_arrived ? final_inst : final_inst_store; + //assign c_ena = fb_if.result[1:0] != 2'b11; + assign fb_if.done = fb_if.inst_arrived & finished; + +endmodule diff --git a/source_code/rv32c/rv32c.core b/source_code/rv32c/rv32c.core new file mode 100644 index 000000000..ede7e0416 --- /dev/null +++ b/source_code/rv32c/rv32c.core @@ -0,0 +1,18 @@ +CAPI=2: +name: socet:riscv:rv32c:0.1.0 +description: rv32c + +filesets: + rtl: + files: + - decompressor.sv + - rv32c_disabled.sv + - rv32c_enabled.sv + - fetch_buffer.sv + - rv32c_wrapper.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl \ No newline at end of file diff --git a/source_code/rv32c/rv32c_disabled.sv b/source_code/rv32c/rv32c_disabled.sv new file mode 100644 index 000000000..7ff190932 --- /dev/null +++ b/source_code/rv32c/rv32c_disabled.sv @@ -0,0 +1,22 @@ +`include "rv32c_if.vh" +`include "fetch_buffer_if.vh" +`include "decompressor_if.vh" + +module rv32c_disabled ( + input logic clk, + nrst, + rv32c_if.rv32c rv32cif +); + + + assign rv32cif.done = '0; + assign rv32cif.done_earlier = '0; + assign rv32cif.nextpc = '0; + assign rv32cif.imem_pc = '0; + assign rv32cif.result = '0; + assign rv32cif.inst32 = '0; + assign rv32cif.c_ena = '0; + + assign rv32cif.rv32c_ena = '0; + +endmodule diff --git a/source_code/rv32c/rv32c_enabled.sv b/source_code/rv32c/rv32c_enabled.sv new file mode 100644 index 000000000..78a8cf100 --- /dev/null +++ b/source_code/rv32c/rv32c_enabled.sv @@ -0,0 +1,41 @@ +`include "rv32c_if.vh" +`include "fetch_buffer_if.vh" +`include "decompressor_if.vh" + +module rv32c_enabled ( + input logic clk, + nrst, + rv32c_if.rv32c rv32cif +); + //parameter RESET_PC = 32'h80000000; + // Fetch Buffer + fetch_buffer_if fb_if (); + fetch_buffer BUFFER ( + .clk (clk), + .n_rst(nrst), + .fb_if(fb_if) + ); + assign fb_if.inst = rv32cif.inst; + assign fb_if.reset_en = rv32cif.reset_en; + assign fb_if.reset_pc = rv32cif.reset_pc; + assign fb_if.inst_arrived = rv32cif.inst_arrived; + assign fb_if.pc_update = rv32cif.pc_update; + assign fb_if.ex_busy = rv32cif.ex_busy; + assign fb_if.reset_pc_val = rv32cif.reset_pc_val; + assign rv32cif.done = fb_if.done; + assign rv32cif.done_earlier = fb_if.done_earlier & (rv32cif.halt == 0); + ///assign rv32cif.done_earlier_send = fb_if.done_earlier_send; + assign rv32cif.nextpc = fb_if.nextpc; + assign rv32cif.imem_pc = fb_if.imem_pc; + assign rv32cif.result = fb_if.result; + + // Decompressor + decompressor_if dcpr_if (); + decompressor DECOMPRESSOR (dcpr_if); + assign dcpr_if.inst16 = rv32cif.inst16; + assign rv32cif.inst32 = dcpr_if.inst32; + assign rv32cif.c_ena = dcpr_if.c_ena; + + assign rv32cif.rv32c_ena = 1; + +endmodule diff --git a/source_code/rv32c/rv32c_wrapper.sv b/source_code/rv32c/rv32c_wrapper.sv new file mode 100644 index 000000000..9277bed27 --- /dev/null +++ b/source_code/rv32c/rv32c_wrapper.sv @@ -0,0 +1,19 @@ +`include "rv32c_if.vh" +`include "component_selection_defines.vh" + +module rv32c_wrapper ( + input logic CLK, + nRST, + rv32c_if.rv32c rv32cif +); + + generate + /* verilator lint_off width */ + case (RV32C_ENABLED) + /* verilator lint_on width */ + "disabled": rv32c_disabled RV32C (.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); + "enabled": rv32c_enabled RV32C (.clk(CLK), .nrst(nRST), .rv32cif(rv32cif)); + endcase + endgenerate + +endmodule diff --git a/source_code/rv32m/carry_save_adder.sv b/source_code/rv32m/carry_save_adder.sv new file mode 100644 index 000000000..f1cfe0379 --- /dev/null +++ b/source_code/rv32m/carry_save_adder.sv @@ -0,0 +1,24 @@ +module carry_save_adder #( + parameter int BIT_WIDTH = 32 +) ( + input logic [(BIT_WIDTH-1):0] x, + input logic [(BIT_WIDTH-1):0] y, + input logic [(BIT_WIDTH-1):0] z, + output logic [(BIT_WIDTH-1):0] cout, + output logic [(BIT_WIDTH-1):0] sum +); + genvar i; + logic [(BIT_WIDTH-1):0] c; + generate + for (i = 0; i < BIT_WIDTH; i = i + 1) begin : g_mul_csa + full_adder FA ( + .x(x[i]), + .y(y[i]), + .cin(z[i]), + .cout(c[i]), + .sum(sum[i]) + ); + end + endgenerate + assign cout = c << 1; +endmodule diff --git a/source_code/rv32m/flex_counter_mul.sv b/source_code/rv32m/flex_counter_mul.sv new file mode 100644 index 000000000..9bce1660c --- /dev/null +++ b/source_code/rv32m/flex_counter_mul.sv @@ -0,0 +1,38 @@ +module flex_counter_mul #( + parameter int NUM_CNT_BITS = 4 +) ( + input wire clk, + input wire n_rst, + input wire clear, + input wire count_enable, + input reg [(NUM_CNT_BITS-1):0] rollover_val, + output reg [(NUM_CNT_BITS-1):0] count_out, + output reg rollover_flag +); + + reg [(NUM_CNT_BITS-1):0] next_count; + reg next_flag; + always_ff @(posedge clk, negedge n_rst) begin + if (n_rst == 0) begin + count_out <= '0; + rollover_flag <= 1'b0; + end else begin + count_out <= next_count; + rollover_flag <= next_flag; + end + end + + always_comb begin + if (clear == 1) next_flag = 0; + else if (count_enable == 0) next_flag = rollover_flag; + else if (count_out == (rollover_val - 1) & clear == 1'b0) next_flag = 1; + else next_flag = 0; + + + if (clear) next_count = 0; + else if (count_enable) begin + if (count_out == rollover_val) next_count = 1; + else next_count = count_out + 1; + end else next_count = count_out; + end +endmodule diff --git a/source_code/rv32m/full_adder.sv b/source_code/rv32m/full_adder.sv new file mode 100644 index 000000000..d531e031c --- /dev/null +++ b/source_code/rv32m/full_adder.sv @@ -0,0 +1,11 @@ +module full_adder ( + input logic x, + input logic y, + input logic cin, + output logic cout, + output logic sum +); + assign sum = x ^ y ^ cin; + assign cout = (x & y) | (x & cin) | (y & cin); + +endmodule diff --git a/source_code/rv32m/pp_mul32.sv b/source_code/rv32m/pp_mul32.sv new file mode 100644 index 000000000..0c4a0b4f4 --- /dev/null +++ b/source_code/rv32m/pp_mul32.sv @@ -0,0 +1,347 @@ +// Pipelined multiplier - 32 bits +module pp_mul32 ( + input logic CLK, + input logic nRST, + input logic [31:0] multiplicand, + input logic [31:0] multiplier, + input logic [1:0] is_signed, + input logic start, + output logic finished, + output logic [63:0] product +); + //logic start_reg; + logic [31:0] multiplicand_reg; + logic [31:0] multiplier_reg; + logic [63:0] result; + logic [63:0] result2; + logic [63:0] temp_product; + logic [63:0] temp_product2; + logic [31:0] multiplicand_mod; + logic [31:0] multiplier_mod; + logic adjust_product; + logic [63:0] partial_product[16]; + logic [63:0] + pp0, pp1, pp2, pp3, pp4, pp5, pp6, pp7, pp8, pp9, pp10, pp11, pp12, pp13, pp14, pp15; + logic [32:0] mul_plus2, mul_minus2, mul_minus1; + logic [63:0] pp[16]; + logic [32:0] modified_in; + logic [63:0] + sum0, sum1, sum2, sum3, sum4, sum5, sum6, sum7, sum8, sum9, sum10, sum11, sum12, sum13; + logic [63:0] + cout0, + cout1, + cout2, + cout3, + cout4, + cout5, + cout6, + cout7, + cout8, + cout9, + cout10, + cout11, + cout12, + cout13; + logic [1:0] count; + logic mult_complete; + //logic [63:0] sum13_pip, cout13_pip; + logic [63:0] sum5_pip, cout5_pip, sum6_pip, cout6_pip, sum7_pip, cout7_pip; + logic [1:0] is_signed_reg; + logic done; + logic count_ena; + integer i, j; + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + multiplicand_reg <= '0; + multiplier_reg <= '0; + is_signed_reg <= '0; + end else if (start) begin + multiplicand_reg <= multiplicand; + multiplier_reg <= multiplier; + is_signed_reg <= is_signed; + end + end + // Modify multiplicand and multiplier if they are signed + assign multiplicand_mod = is_signed_reg[1] && multiplicand_reg[31] ? + (~(multiplicand_reg)+1) : multiplicand_reg; + assign multiplier_mod = is_signed_reg[0] && multiplier_reg[31] ? + (~(multiplier_reg)+1) : multiplier_reg; + // Control signal to modify final product + assign adjust_product = (is_signed_reg[0] & multiplier_reg[31]) + ^ (is_signed_reg[1] & multiplicand_reg[31]); + // For bit pair recoding part + assign mul_plus2 = multiplicand_mod + multiplicand_mod; + assign mul_minus2 = ~mul_plus2 + 1; + assign mul_minus1 = ~multiplicand_mod + 1; + assign modified_in = {multiplier_mod, 1'b0}; + + // STAGE 1: BOOTH ENCODER + // Bit pair recoding to generate partial product + always_comb begin + for (i = 0; i < 32; i = i + 2) begin + case ({ + modified_in[i+2], modified_in[i+1], modified_in[i] + }) + 3'b000: pp[i/2] = '0; //0 + 3'b001: pp[i/2] = {{32'd0}, multiplicand_mod}; // +1M + 3'b010: pp[i/2] = {{32'd0}, multiplicand_mod}; // +1M + 3'b011: pp[i/2] = {{31'd0}, mul_plus2}; // +2M + 3'b100: + if (mul_minus2 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus2}; // -2M + 3'b101: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus1}; // -1M + 3'b110: + if (mul_minus1 == 0) pp[i/2] = '0; + else pp[i/2] = {{31{1'b1}}, mul_minus1}; // -1M + 3'b111: pp[i/2] = '0; + endcase + end + end + // Shift partial product + always_comb begin + for (j = 0; j < 16; j = j + 1) begin + partial_product[j] = pp[j] << (2 * j); // Shift with multiple of 2 (Radix 4) + end + end + + // Pipeline register before wallace tree + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + pp0 <= '0; + pp1 <= '0; + pp2 <= '0; + pp3 <= '0; + pp4 <= '0; + pp5 <= '0; + pp6 <= '0; + pp7 <= '0; + pp8 <= '0; + pp9 <= '0; + pp10 <= '0; + pp11 <= '0; + pp12 <= '0; + pp13 <= '0; + pp14 <= '0; + pp15 <= '0; + end else begin + pp0 <= partial_product[0]; + pp1 <= partial_product[1]; + pp2 <= partial_product[2]; + pp3 <= partial_product[3]; + pp4 <= partial_product[4]; + pp5 <= partial_product[5]; + pp6 <= partial_product[6]; + pp7 <= partial_product[7]; + pp8 <= partial_product[8]; + pp9 <= partial_product[9]; + pp10 <= partial_product[10]; + pp11 <= partial_product[11]; + pp12 <= partial_product[12]; + pp13 <= partial_product[13]; + pp14 <= partial_product[14]; + pp15 <= partial_product[15]; + end + end + + // STAGE 2: WALLACE TREE + // Layer 1 + carry_save_adder #(64) CSA0 ( + .x(pp0), + .y(pp1), + .z(pp2), + .cout(cout0), + .sum(sum0) + ); + carry_save_adder #(64) CSA1 ( + .x(pp3), + .y(pp4), + .z(pp5), + .cout(cout1), + .sum(sum1) + ); + carry_save_adder #(64) CSA2 ( + .x(pp6), + .y(pp7), + .z(pp8), + .cout(cout2), + .sum(sum2) + ); + carry_save_adder #(64) CSA3 ( + .x(pp9), + .y(pp10), + .z(pp11), + .cout(cout3), + .sum(sum3) + ); + carry_save_adder #(64) CSA4 ( + .x(pp12), + .y(pp13), + .z(pp14), + .cout(cout4), + .sum(sum4) + ); // remaining partialproduct 15 + // Layer 2 + carry_save_adder #(64) CSA5 ( + .x(cout0), + .y(sum0), + .z(cout1), + .cout(cout5), + .sum(sum5) + ); + carry_save_adder #(64) CSA6 ( + .x(sum1), + .y(cout2), + .z(sum2), + .cout(cout6), + .sum(sum6) + ); + carry_save_adder #(64) CSA7 ( + .x(cout3), + .y(sum3), + .z(cout4), + .cout(cout7), + .sum(sum7) + ); // remaining sum4 + // Pipeline register in wallace tree between layer 2 and layer 3 + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + cout5_pip <= '0; + sum5_pip <= '0; + cout6_pip <= '0; + sum6_pip <= '0; + cout7_pip <= '0; + sum7_pip <= '0; + end else begin + cout5_pip <= cout5; + sum5_pip <= sum5; + cout6_pip <= cout6; + sum6_pip <= sum6; + cout7_pip <= cout7; + sum7_pip <= sum7; + end + end + + // Layer 3 + carry_save_adder #(64) CSA8 ( + .x(cout5), + .y(sum5), + .z(cout6), + .cout(cout8), + .sum(sum8) + ); + carry_save_adder #(64) CSA9 ( + .x(sum6), + .y(cout7), + .z(sum7), + .cout(cout9), + .sum(sum9) + ); + // Layer 4 + carry_save_adder #(64) CSA10 ( + .x(cout8), + .y(sum8), + .z(cout9), + .cout(cout10), + .sum(sum10) + ); + carry_save_adder #(64) CSA11 ( + .x(sum9), + .y(pp15), + .z(sum4), + .cout(cout11), + .sum(sum11) + ); + // Layer 5 + carry_save_adder #(64) CSA12 ( + .x(cout10), + .y(sum10), + .z(cout11), + .cout(cout12), + .sum(sum12) + ); // remaining sum11 + // Layer 6 + carry_save_adder #(64) CSA13 ( + .x(cout12), + .y(sum12), + .z(sum11), + .cout(cout13), + .sum(sum13) + ); + + // STAGE 3: NORMAL ADDER + flex_counter_mul #(2) FC ( + .clk(CLK), + .n_rst(nRST), + .clear(start), + .count_enable(count_ena), + .rollover_val(2'd2), + .count_out(count), + .rollover_flag(finished) + ); + assign temp_product = cout13 + sum13; + assign temp_product2 = is_signed_reg[0] == 0 && multiplier_reg[31] ? + temp_product + ({{33{multiplicand_mod[31]}},multiplicand_mod} << 32) + : temp_product; // plus extra 1M + assign result = adjust_product ? (~temp_product2) + 1 : temp_product2; + assign mult_complete = count == 2'd1 | count == 2'd2; + assign result2 = mult_complete ? result : '0; + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + product <= '0; + end else begin + product <= result2; + end + end + + //Small FSM to control flex counter + typedef enum logic { + IDLE, + START + } state_t; + state_t state, next_state; + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) state <= IDLE; + else state <= next_state; + end + + always_comb begin + /* + next_state = state; + case (state) + IDLE: begin + if (start) + next_state = START; + end + START: begin + if (finished) + next_state = IDLE; + end + endcase + */ + next_state = state; + if (state == IDLE && start) begin + next_state = START; + end else if (state == START && finished) begin + next_state = IDLE; + end else begin + next_state = state; + end + end + + always_comb begin + count_ena = 0; + case (state) + IDLE: begin + count_ena = 0; + end + START: begin + count_ena = ~finished; + end + endcase + end + +endmodule diff --git a/source_code/rv32m/radix4_divider.sv b/source_code/rv32m/radix4_divider.sv new file mode 100644 index 000000000..361b60b6c --- /dev/null +++ b/source_code/rv32m/radix4_divider.sv @@ -0,0 +1,141 @@ +module radix4_divider #( + parameter int NUM_BITS = 32 +) ( + input logic CLK, + input logic nRST, + input logic start, + input logic is_signed, //new + input logic [NUM_BITS-1:0] dividend, + input logic [NUM_BITS-1:0] divisor, + output logic [NUM_BITS-1:0] quotient, + output logic [NUM_BITS-1:0] remainder, + output logic finished + +); + logic [NUM_BITS-1:0] + next_remainder, + next_quotient, + shifted_remainder, + shifted_quotient, + temp_quotient, + temp_remainder; + logic [NUM_BITS:0] Result1, Result2, Result3; + logic [NUM_BITS-1:0] DivisorX2, DivisorX3; + logic [4:0] count, next_count; + + logic [NUM_BITS-1:0] usign_divisor, usign_dividend; + logic adjustment_possible, adjust_quotient, adjust_remainder; + logic div_done; + + assign usign_divisor = is_signed & divisor[NUM_BITS-1] ? (~divisor) + 1 : divisor; + assign usign_dividend = is_signed & dividend[NUM_BITS-1] ? (~dividend) + 1 : dividend; + assign adjustment_possible = is_signed && (divisor[NUM_BITS-1] ^ dividend[NUM_BITS-1]); + assign adjust_quotient = adjustment_possible && ~quotient[NUM_BITS-1]; + assign adjust_remainder = is_signed && dividend[NUM_BITS-1]; + assign div_done = (count == 0); + assign quotient = temp_quotient; + assign remainder = temp_remainder; + + /* + always_comb begin + quotient = temp_quotient; + remainder = temp_remainder; + if (count == 5'b1) begin + quotient = adjust_quotient ? ~temp_quotient + 1 : temp_quotient; + remainder = adjust_remainder ? ~temp_remainder + 1 : temp_remainder; + end + end +*/ + /* + always_ff @(posedge CLK, negedge nRST) begin + if (~finished && adjust_quotient) + quotient <= ~quotient + 1; + + else if(~finished && adjust_remainder ) + remainder <= ~remainder + 1; + + else begin + quotient <= quotient; + remainder <= remainder; + end + end +*/ + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + finished <= 1'b0; + end else if (start) begin + finished <= 1'b0; + end else if (div_done) begin + finished <= 1'b1; + end + end + //initialize d2 d3 + assign DivisorX2 = usign_divisor << 1; //Divisor*2 + assign DivisorX3 = (usign_divisor << 1) + usign_divisor; //Divisor*3 + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) begin + + count <= 5'd16; + temp_quotient <= '0; + temp_remainder <= '0; + end else if (start) begin + temp_quotient <= usign_dividend; + temp_remainder <= '0; + count <= 5'd16; + + end else begin + temp_quotient <= next_quotient; + temp_remainder <= next_remainder; + count <= next_count; + end + end + + always_comb begin + + next_quotient = temp_quotient; + next_remainder = temp_remainder; + next_count = count; + shifted_remainder = '0; + shifted_quotient = '0; + Result1 = '0; + Result2 = '0; + Result3 = '0; + + if (count != 0) begin + next_count = count - 1; + shifted_remainder = (temp_remainder << 2) | temp_quotient[NUM_BITS-1:NUM_BITS-2]; + shifted_quotient = temp_quotient << 2; + Result1 = shifted_remainder - usign_divisor; + Result2 = shifted_remainder - DivisorX2; + Result3 = shifted_remainder - DivisorX3; + if (Result1[NUM_BITS-1] | Result1[NUM_BITS]) begin + next_remainder = shifted_remainder; + next_quotient = shifted_quotient | 0; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + + end else if (Result2[NUM_BITS-1] | Result2[NUM_BITS]) begin + next_remainder = Result1[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 1; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end else if (Result3[NUM_BITS-1] | Result3[NUM_BITS]) begin + next_remainder = Result2[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 2; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end else begin + next_remainder = Result3[NUM_BITS-1:0]; + next_quotient = shifted_quotient | 3; + if (count == 1 && adjust_quotient) next_quotient = ~next_quotient + 1; + + if (count == 1 && adjust_remainder) next_remainder = ~next_remainder + 1; + end + end + + end +endmodule diff --git a/source_code/rv32m/rv32m.core b/source_code/rv32m/rv32m.core new file mode 100644 index 000000000..3a634dce8 --- /dev/null +++ b/source_code/rv32m/rv32m.core @@ -0,0 +1,27 @@ +CAPI=2: +name: socet:riscv:rv32m:0.1.0 +description: RV32M Extension for RISCVBusiness + +filesets: + rtl: + depend: + - "socet:riscv:riscv_include" + - "socet:riscv:packages" + files: + #- rv32m_pkg.sv + - carry_save_adder.sv + - full_adder.sv + - flex_counter_mul.sv + - pp_mul32.sv + - radix4_divider.sv + - rv32m_decode.sv + - rv32m_disabled.sv + - rv32m_enabled.sv + - rv32m_wrapper.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + toplevel: rv32m_wrapper \ No newline at end of file diff --git a/source_code/rv32m/rv32m_decode.sv b/source_code/rv32m/rv32m_decode.sv new file mode 100644 index 000000000..afeb939ff --- /dev/null +++ b/source_code/rv32m/rv32m_decode.sv @@ -0,0 +1,41 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32m_decode.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/07/2017 +* Description: Decoding for standard multiply extension +*/ +module rv32m_decode ( + input [31:0] insn, + output logic claim, + output rv32m_pkg::rv32m_decode_t rv32m_control +); + + import rv32m_pkg::*; + + rv32m_insn_t insn_split; + + assign insn_split = rv32m_insn_t'(insn); + assign claim = (insn_split.opcode_major == RV32M_OPCODE) + && (insn_split.opcode_minor == RV32M_OPCODE_MINOR); + + assign rv32m_control.select = claim; + assign rv32m_control.op = rv32m_op_t'(insn_split.funct); + +endmodule diff --git a/source_code/rv32m/rv32m_disabled.sv b/source_code/rv32m/rv32m_disabled.sv new file mode 100644 index 000000000..21fec2181 --- /dev/null +++ b/source_code/rv32m/rv32m_disabled.sv @@ -0,0 +1,16 @@ + +module rv32m_disabled( + input CLK, + input nRST, + input rv32m_start, + input rv32m_pkg::rv32m_op_t operation, + input [31:0] rv32m_a, + input [31:0] rv32m_b, + output rv32m_busy, + output logic [31:0] rv32m_out +); + + assign rv32m_busy = 1'b0; + assign rv32m_out = 32'b0; + +endmodule \ No newline at end of file diff --git a/source_code/rv32m/rv32m_enabled.sv b/source_code/rv32m/rv32m_enabled.sv new file mode 100644 index 000000000..d61f13db8 --- /dev/null +++ b/source_code/rv32m/rv32m_enabled.sv @@ -0,0 +1,227 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32m_execute.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/07/2017 +* Description: Execute stage for standard RV32M +*/ +`include "component_selection_defines.vh" + +module rv32m_enabled ( + input CLK, + input nRST, + input rv32m_start, + input rv32m_pkg::rv32m_op_t operation, + input [31:0] rv32m_a, + input [31:0] rv32m_b, + output logic rv32m_busy, + output logic [31:0] rv32m_out +); + + import rv32m_pkg::*; + import rv32i_types_pkg::*; + + + /* Operand Saver to detect new request */ + + // operand saver + word_t op_a, op_b, op_a_save, op_b_save; + rv32m_op_t operation_save; + //logic [2:0] operation, operation_save; + //logic [1:0] is_signed_save, is_signed_curr, is_signed; + logic operand_diff; + logic is_multiply; + logic is_divide; + logic [1:0] is_signed; + + assign is_multiply = (operation == MUL) || (operation == MULH) || (operation == MULHU) || (operation == MULHSU); + assign is_divide = (operation == DIV) || (operation == DIVU) || (operation == REM) || (operation == REMU); + + + assign op_a = operand_diff ? rv32m_a : op_a_save; + assign op_b = operand_diff ? rv32m_b : op_b_save; + assign operand_diff = rv32m_start && ((op_a_save != rv32m_a) || (op_b_save != rv32m_b) || (operation_save != operation)); + /*assign operand_diff = ((op_a_save != rv32m_a) || + (op_b_save != rv32m_b) || + (is_signed_save != is_signed_curr) || + (operation_save != {idex.mul, idex.div, idex.rem})) && + idex.start ; + assign is_signed_curr = idex.usign_usign ? 2'b00 : (idex.sign_sign ? 2'b11 : 2'b10); + // Is signed + operation = func3? Seems like we could potentially just save off the func3 wholesale + assign is_signed = operand_diff ? is_signed_curr : is_signed_save; + assign operation = operand_diff ? {idex.mul, idex.div, idex.rem} : operation_save;*/ + + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) begin + op_a_save <= '0; + op_b_save <= '0; + //is_signed_save <= '0; + operation_save <= MUL; + end else if (operand_diff) begin + op_a_save <= rv32m_a; + op_b_save <= rv32m_b; + //is_signed_save <= is_signed_curr; + operation_save <= operation; + end + end + + + /* MULTIPLICATION */ + + // multiplier signals + word_t multiplicand, multiplier; + logic [(WORD_SIZE*2)-1:0] product; + logic mul_finished; + logic mul_start; + + assign multiplicand = op_a; + assign multiplier = op_b; + assign mul_start = operand_diff && is_multiply && rv32m_start; + + // Module instantiations + // TODO: Case for which multiplier/divider to use + pp_mul32 mult_i ( + .CLK(CLK), + .nRST(nRST), + .multiplicand(multiplicand), + .multiplier(multiplier), + .product(product), + .is_signed(is_signed), + .start(mul_start), + .finished(mul_finished) + ); + + + /* DIVISION / REMAINDER */ + + logic overflow, div_zero, div_finished; + word_t divisor, dividend, quotient, remainder, divisor_save, dividend_save; + logic div_operand_diff; + logic div_start; + + assign divisor = op_b; + assign dividend = op_a; + assign overflow = (dividend == 32'h8000_0000) && (divisor == 32'hffff_ffff) && is_signed[0]; + assign div_zero = (divisor == 32'h0); + assign div_start = operand_diff && is_divide && !overflow && !div_zero && rv32m_start; + + radix4_divider div_i ( + .CLK(CLK), + .nRST(nRST), + .divisor(divisor), + .dividend(dividend), + .is_signed(is_signed[0]), // For division, only 00 or 11, input is 1 bit, so take one of the bits for "is_signed" (arbitrary) + .start(div_start), + .remainder(remainder), + .quotient(quotient), + .finished(div_finished) + ); + + /* Operation decoding */ + always_comb begin + casez (operation) + MUL, MULH, DIV, REM: is_signed = 2'b11; + MULHU, DIVU, REMU: is_signed = 2'b00; + MULHSU: is_signed = 2'b10; + default: is_signed = 2'b11; + endcase + end + + /* Result */ + always_comb begin + if(rv32m_start) begin + // Note: operand_diff on all these cases is to fix condition where + // "done" flag asserted by FU due to previous op. RV32M will always + // take at least 1 extra cycle if we aren't reusing a value. + casez(operation) + MUL: begin + rv32m_busy = operand_diff || !mul_finished; + rv32m_out = product[WORD_SIZE-1:0]; + end + + MULH, MULHU, MULHSU: begin + rv32m_busy = operand_diff || !mul_finished; + rv32m_out = product[(WORD_SIZE*2)-1 : WORD_SIZE]; + end + + // TODO: Is there a better way to decode this? Lots of repetition. + DIV: begin + rv32m_busy = operand_diff || (!div_finished && !div_zero && !overflow); + rv32m_out = div_zero ? 32'hffff_ffff : (overflow ? 32'h8000_0000 : quotient); + end + + DIVU: begin + rv32m_busy = operand_diff || (!div_finished && !div_zero && !overflow); + rv32m_out = div_zero ? 32'h7fff_ffff : (overflow ? 32'h8000_0000 : quotient); + end + + REM, REMU: begin + rv32m_busy = operand_diff || (!div_finished && !div_zero && !overflow); + rv32m_out = div_zero ? dividend : (overflow ? 32'h0000_0000 : remainder); + end + + default: begin + rv32m_busy = 1'b0; + rv32m_out = 32'b0; // TODO: Should this return BAD3? + end + endcase + end else begin + rv32m_busy = 1'b0; + rv32m_out = 32'b0; + end + end + + /* + always_comb begin + casez (operation) + 3'b1??: begin // MUL + eif.busy = ~mul_finished; + eif.reg_wdata = idex.lower_word ? + product[WORD_SIZE-1:0] + : product[(WORD_SIZE*2)-1 : WORD_SIZE]; + end + 3'b01?: begin // DIV + eif.busy = ~div_finished & ~(div_zero | overflow); + if (div_zero) begin + eif.reg_wdata = idex.sign_sign ? 32'hffff_ffff : 32'h7fff_ffff; + end else if (overflow) begin + eif.reg_wdata = 32'h8000_0000; + end else begin + eif.reg_wdata = quotient; + end + end + 3'b001: begin // REM + eif.busy = ~div_finished & ~(div_zero | overflow); + if (div_zero) begin + eif.reg_wdata = dividend; + end else if (overflow) begin + eif.reg_wdata = 32'h0000_0000; + end else begin + eif.reg_wdata = remainder; + end + end + default: begin + eif.busy = 1'b0; + eif.reg_wdata = 32'hBAD3_BAD3; + end + endcase + end + */ + +endmodule diff --git a/source_code/rv32m/rv32m_wrapper.sv b/source_code/rv32m/rv32m_wrapper.sv new file mode 100644 index 000000000..1641da5a7 --- /dev/null +++ b/source_code/rv32m/rv32m_wrapper.sv @@ -0,0 +1,27 @@ +`include "component_selection_defines.vh" + +module rv32m_wrapper( + input CLK, + input nRST, + input rv32m_start, + input rv32m_pkg::rv32m_op_t operation, + input [31:0] rv32m_a, + input [31:0] rv32m_b, + output rv32m_busy, + output logic [31:0] rv32m_out +); + import rv32m_pkg::*; + + /*generate + case(RV32M_ENABLED) + "disabled": rv32m_disabled RV32M(.*); + "enabled": rv32m_enabled RV32M(.*); + endcase + endgenerate*/ + `ifdef RV32M_SUPPORTED + rv32m_enabled RV32M(.*); + `else + rv32m_disabled RV32M(.*); + `endif + +endmodule \ No newline at end of file diff --git a/source_code/rv32m/shift_add_multiplier.sv b/source_code/rv32m/shift_add_multiplier.sv new file mode 100644 index 000000000..41227fb89 --- /dev/null +++ b/source_code/rv32m/shift_add_multiplier.sv @@ -0,0 +1,78 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: shift_add_multiplier.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/15/2017 +* Description: N bit parameterized shift and add multiplier +* Takes up to N+1 cycles to compute the product. +*/ + +module shift_add_multiplier #( + parameter int N = 32 +) ( + input logic CLK, + nRST, + input logic [N-1:0] multiplicand, + input logic [N-1:0] multiplier, + input logic [1:0] is_signed, + input logic start, + output logic [(N*2)-1:0] product, + output logic finished +); + + logic [(N*2)-1:0] multiplier_reg, multiplicand_reg; + logic [(N*2)-1:0] multiplier_ext, multiplicand_ext; + logic [(N*2)-1:0] partial_product; + logic mult_complete, adjust_product; + + assign mult_complete = !(|multiplier_reg); + assign adjust_product = (is_signed[0] & multiplier[N-1]) ^ (is_signed[1] & multiplicand[N-1]); + assign partial_product = multiplier_reg[0] ? multiplicand_reg : '0; + assign multiplier_ext = (~{{N{multiplier[N-1]}},multiplier}) + 1; + assign multiplicand_ext = (~{{N{multiplicand[N-1]}},multiplicand}) + 1; + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) finished <= 1'b0; + else if (start) finished <= 1'b0; + else if (mult_complete) finished <= 1'b1; + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + multiplicand_reg <= '0; + multiplier_reg <= '0; + product <= '0; + end else if (start) begin + multiplicand_reg <= (is_signed[1] && multiplicand[N-1]) ? + multiplicand_ext : {{N{1'b0}}, multiplicand}; + multiplier_reg <= (is_signed[0] && multiplier[N-1]) ? + multiplier_ext : {{N{1'b0}}, multiplier}; + product <= '0; + end else if (mult_complete & ~finished) begin // adjust sign on product + multiplicand_reg <= multiplicand_reg; + multiplier_reg <= multiplier_reg; + product <= adjust_product ? (~product) + 1 : product; + end else if (~finished) begin + multiplicand_reg <= multiplicand_reg << 1; + multiplier_reg <= multiplier_reg >> 1; + product <= product + partial_product; + end + end + +endmodule diff --git a/source_code/rv32m/shift_test_restore_divider.sv b/source_code/rv32m/shift_test_restore_divider.sv new file mode 100644 index 000000000..ba9e441cd --- /dev/null +++ b/source_code/rv32m/shift_test_restore_divider.sv @@ -0,0 +1,92 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: shift_test_restore_divider.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 02/21/2017 +* Description: NxN bit divider using the shift-test-restore algorithm +*/ + +module shift_test_restore_divider #( + parameter int N = 32 +) ( + input logic CLK, + nRST, + input logic [N-1:0] divisor, + dividend, + input logic is_signed, + input logic start, + output logic [N-1:0] remainder, + quotient, + output logic finished +); + + localparam int COUNTER_BITS = $clog2(N) + 1; + localparam int U_Q = N - 1; + localparam int U_R = (2 * N) - 1; + + logic [(2*N)+1:0] result; + assign {remainder, quotient} = result[(2*N)-1:0]; + logic test_phase; + logic [COUNTER_BITS-1:0] counter; + logic [N-1:0] usign_divisor, usign_dividend; + logic adjustment_possible, adjust_quotient, adjust_remainder; + logic div_done; + + assign usign_divisor = is_signed & divisor[N-1] ? (~divisor) + 1 : divisor; + assign usign_dividend = is_signed & dividend[N-1] ? (~dividend) + 1 : dividend; + assign adjustment_possible = is_signed && (divisor[N-1] ^ dividend[N-1]); + assign adjust_quotient = adjustment_possible && ~quotient[N-1]; + assign adjust_remainder = is_signed && dividend[N-1]; + assign div_done = (counter == 0); + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + result <= '0; + counter <= N; + test_phase <= 1'b0; + end else if (start) begin + result <= {{(N - 1) {1'b0}}, usign_dividend, 1'b0}; + counter <= N; + test_phase <= 1'b0; + end else if (counter > 0) begin + if (~test_phase) begin // shift and sub + result[U_R+1-:N+1] <= result[U_R+1-:N+1] - usign_divisor; + end else begin // check result + counter <= counter - 1; + if (result[U_R+1]) // negative remainder, must restore + result <= {(result[U_R+1-:N+1] + usign_divisor), result[U_Q:0]} << 1; + else result <= {result[U_R-1:0], 1'b1}; + end + test_phase <= ~test_phase; + end else if (~finished) begin + if (adjust_quotient) result[U_Q:0] <= (~result[U_Q:0]) + 1; + if (adjust_remainder) result[U_R-:N] <= (~result[U_R+1-:N]) + 1; + //result[U_R-:N] <= (~({result[U_R],result[U_R-:N-1]}))+1; + else + result[U_R-:N] <= result[U_R+1-:N]; + end + end + + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) finished <= 1'b0; + else if (start) finished <= 1'b0; + else if (div_done) finished <= 1'b1; + end + +endmodule diff --git a/source_code/sparce/sparce_disabled/sparce_disabled.sv b/source_code/sparce/sparce_disabled/sparce_disabled.sv new file mode 100644 index 000000000..564a9dcd4 --- /dev/null +++ b/source_code/sparce/sparce_disabled/sparce_disabled.sv @@ -0,0 +1,41 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_disabled.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/29/2019 +* Description: The top level file for a CPU without sparsity optimizations +* enabled +*/ + +`include "sparce_pipeline_if.vh" + +module sparce_disabled ( + input logic CLK, + nRST, + sparce_pipeline_if.sparce sparce_if +); + + // when disabled, sparce should never force the pipeline to skip + + // all inputs are to be ignored + assign sparce_if.skipping = 1'b0; + assign sparce_if.sparce_target = '0; + + +endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_cfid.sv b/source_code/sparce/sparce_enabled/sparce_cfid.sv new file mode 100644 index 000000000..dedebea4e --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_cfid.sv @@ -0,0 +1,46 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_cfid.sv +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: Oct 1st, 2019 +* Description: The file containing the control flow instruction detector. +*/ + +// modport cfid ( +// output ctrl_flow_enable, +// input rdata +// ); + +`include "sparce_internal_if.vh" + +module sparce_cfid ( + sparce_internal_if.cfid cfid_if +); + import rv32i_types_pkg::*; + + opcode_t cf_op; + assign cf_op = opcode_t'(cfid_if.rdata[OP_W-1:0]); + + always_comb begin + if (cf_op == BRANCH || cf_op == JAL || cf_op == JALR) cfid_if.ctrl_flow_enable = 0; + else cfid_if.ctrl_flow_enable = 1; + end + +endmodule + diff --git a/source_code/sparce/sparce_enabled/sparce_enabled.sv b/source_code/sparce/sparce_enabled/sparce_enabled.sv new file mode 100644 index 000000000..b2e80dd19 --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_enabled.sv @@ -0,0 +1,69 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_enabled.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/29/2019 +* Description: The top level file for sparsity enabled for the CPU +*/ + +`include "sparce_pipeline_if.vh" +`include "sparce_internal_if.vh" + +module sparce_enabled ( + input logic CLK, + nRST, + sparce_pipeline_if.sparce sparce_if +); + + sparce_internal_if internal_if (); + + // assign inputs to the internal modules + assign internal_if.pc = sparce_if.pc; + assign internal_if.wb_data = sparce_if.wb_data; + assign internal_if.wb_en = sparce_if.wb_en; + assign internal_if.sasa_data = sparce_if.sasa_data; + assign internal_if.sasa_addr = sparce_if.sasa_addr; + assign internal_if.sasa_wen = sparce_if.sasa_wen; + assign internal_if.rd = sparce_if.rd; + assign internal_if.sasa_enable = sparce_if.if_ex_enable; + assign internal_if.rdata = sparce_if.rdata; + + // assign to sparce module outputs + assign sparce_if.skipping = internal_if.skipping; + assign sparce_if.sparce_target = internal_if.sparce_target; + + // instantiate internal modules + sparce_svc sparce_svc_i (internal_if.svc); + sparce_sprf sparce_sprf_i ( + CLK, + nRST, + internal_if.sprf + ); + sparce_sasa_table #( + .SASA_SETS(4) + ) sparce_sasa_table_i ( + CLK, + nRST, + internal_if.sasa_table + ); + sparce_psru sparce_psru_i (internal_if.psru); + sparce_cfid sparce_cfid_i (internal_if.cfid); + + +endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_psru.sv b/source_code/sparce/sparce_enabled/sparce_psru.sv new file mode 100644 index 000000000..79c62f6b3 --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_psru.sv @@ -0,0 +1,54 @@ + +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_psru.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/14/2019 +* Description: The file containing pre-identify and skip redundancy unit +*/ + +`include "sparce_internal_if.vh" + +// modport psru ( +// output skipping, sparce_target, +// input valid, insts_to_skip, preceding_pc, condition, rs1_sparsity, rs2_sparsity, ctrl_flow_enable +// ); + +module sparce_psru ( + sparce_internal_if.psru psru_if +); + + always_comb begin + if (psru_if.valid) begin + // choose the correct condition to evaluate + if (psru_if.condition == SASA_COND_OR) begin + psru_if.skipping = (psru_if.rs1_sparsity || psru_if.rs2_sparsity) && psru_if.ctrl_flow_enable; + end else begin + psru_if.skipping = (psru_if.rs1_sparsity && psru_if.rs2_sparsity) && psru_if.ctrl_flow_enable; + end + // calculate the new program counter + psru_if.sparce_target = psru_if.preceding_pc + (psru_if.insts_to_skip << 2) + 4; + end else begin + // don't skip if the SASA table entry is invalid + psru_if.skipping = 1'b0; + psru_if.sparce_target = '1; + end + end + +endmodule diff --git a/source_code/sparce/sparce_enabled/sparce_sasa_table.sv b/source_code/sparce/sparce_enabled/sparce_sasa_table.sv new file mode 100644 index 000000000..39b6b2944 --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_sasa_table.sv @@ -0,0 +1,209 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_sasa_table.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/6/2019 +* Description: The file containing the SASA table +*/ + +`include "sparce_internal_if.vh" + +// defined because $clog2 is not universally supported +`define CLOG2(x) \ + (((x) <= 1) ? 0 : \ + ((x) <= 2) ? 1 : \ + ((x) <= 4) ? 2 : \ + ((x) <= 8) ? 3 : \ + ((x) <= 16) ? 4 : \ + ((x) <= 32) ? 5 : \ + ((x) <= 64) ? 6 : \ + -1) + +// modport sasa_table ( +// output sasa_rs1, sasa_rs2, insts_to_skip, preceding_pc, condition, valid, +// input pc, sasa_addr, sasa_data, sasa_wen +// ); + + +// struct for the data read from memory +typedef struct packed { + logic [15:0] prev_pc; + logic [4:0] rs1; + logic [4:0] rs2; + sasa_cond_t sasa_cond; + logic [4:0] insts_to_skip; +} sasa_input_t; + + +module sparce_sasa_table #(parameter SASA_ENTRIES = 16, parameter SASA_SETS = 1, parameter SASA_ADDR = 32'h90000000) (input logic CLK, nRST, sparce_internal_if.sasa_table sasa_if); + + // struct for each entry in the SASA table + typedef struct packed { + logic [1:0] usage; + logic valid; + logic [15:0] tag; + logic [4:0] rs1; + logic [4:0] rs2; + sasa_cond_t sasa_cond; + logic [4:0] insts_to_skip; + } sasa_entry_t; + + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][`CLOG2(SASA_SETS) - 1 :0] usage; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0] valid; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][15- `CLOG2(SASA_SETS) :0] tag; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][4:0] rs1; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][4:0] rs2; + sasa_cond_t [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0] sasa_cond; + logic [SASA_SETS-1:0][(SASA_ENTRIES/SASA_SETS)-1:0][4:0] insts_to_skip; + sasa_input_t input_data; + + logic [`CLOG2(SASA_ENTRIES/SASA_SETS)-1:0] input_idx; + logic [`CLOG2(SASA_ENTRIES/SASA_SETS)-1:0] pc_idx; + logic [15-`CLOG2(SASA_ENTRIES/SASA_SETS):0] pc_tag; + logic sasa_match; + logic existing_entry; + logic [`CLOG2(SASA_SETS)-1:0] existing_entry_set; + + logic [SASA_SETS:0] sasa_hits; + logic sasa_config, sasa_config_match; + + // sasa table configuration register + assign sasa_config_match = sasa_if.sasa_enable && sasa_if.sasa_wen && (sasa_if.sasa_addr == SASA_ADDR + 4); + + always_ff @(posedge CLK, negedge nRST) begin : sasa_configuration + if (!nRST) begin + sasa_config <= '0; + end + else begin + sasa_config <= sasa_config; + if (sasa_config_match) begin + sasa_config <= sasa_if.sasa_data; + end + end + end + + // wiring for indexing of the cache arrays + assign input_idx = (SASA_ENTRIES == SASA_SETS) ? 0 : input_data.prev_pc; + assign pc_idx = (SASA_ENTRIES == SASA_SETS) ? 0 : (sasa_if.pc >> 2); // ignore byte addressed aspect + assign pc_tag = sasa_if.pc >> (`CLOG2(SASA_ENTRIES/SASA_SETS) + 2); + assign sasa_match = sasa_if.sasa_enable && sasa_if.sasa_wen && (sasa_if.sasa_addr == SASA_ADDR); + + + always_comb begin : sasa_input_conversion + input_data.prev_pc = sasa_if.sasa_data[31:16]; + input_data.rs1 = sasa_if.sasa_data[15:11]; + input_data.rs2 = sasa_if.sasa_data[10:6]; + input_data.sasa_cond = sasa_cond_t'(sasa_if.sasa_data[5]); + input_data.insts_to_skip = sasa_if.sasa_data[4:0]; + end + + always_ff @(posedge CLK, negedge nRST) begin : sasa_table_entries + if (!nRST) begin + // for loops just to set different usage values per entry + for(int i = 0; i < SASA_SETS; i++) begin + for(int j = 0; j < SASA_ENTRIES/SASA_SETS; j++) begin + // set default usage values to 0, 1, 2, 3 for LRU + usage[i][j]<= (SASA_SETS-1)-i; + valid[i][j]<= 1'b0; + tag[i][j]<= '0; + rs1[i][j]<= '0; + rs2[i][j]<= '0; + sasa_cond[i][j]<= SASA_COND_OR; + insts_to_skip[i][j]<= '0; + end + end + end else begin + usage <= usage; + valid <= valid; + tag <= tag; + rs1 <= rs1; + rs2 <= rs2; + sasa_cond <= sasa_cond; + insts_to_skip <= insts_to_skip; + // If the software is attempting to write to the SASA table, write in + // the data and then update the LRU usage + if(sasa_match) begin + if(!existing_entry) begin + for (int i = 0; i < SASA_SETS; i++) begin + if (usage[i][input_idx]== '1 || SASA_SETS == 1) begin + valid[i][input_idx]<= 1; + tag[i][input_idx]<= input_data.prev_pc >> (`CLOG2(SASA_ENTRIES/SASA_SETS)); + rs1[i][input_idx]<= input_data.rs1; + rs2[i][input_idx]<= input_data.rs2; + sasa_cond[i][input_idx]<= input_data.sasa_cond; + insts_to_skip[i][input_idx]<= input_data.insts_to_skip; + end + usage[i][input_idx]<= (usage[i][input_idx]+ 1) % SASA_SETS; + end + end else begin + valid[existing_entry_set][input_idx]<= 1; + tag[existing_entry_set][input_idx]<= input_data.prev_pc >> (`CLOG2(SASA_ENTRIES/SASA_SETS)); + rs1[existing_entry_set][input_idx]<= input_data.rs1; + rs2[existing_entry_set][input_idx]<= input_data.rs2; + sasa_cond[existing_entry_set][input_idx]<= input_data.sasa_cond; + insts_to_skip[existing_entry_set][input_idx]<= input_data.insts_to_skip; + for (int i = 0; i < SASA_SETS; i++) begin + if (usage[i][input_idx] < usage[existing_entry_set][input_idx]) begin + usage[i][input_idx]<= (usage[i][input_idx]+ 1) % SASA_SETS; + end + end + end + end + // If the PC matches in the SASA table, update the LRU usage + else if (sasa_hits != 0) begin + for (int i = 0; i < SASA_SETS; i++) begin + if(usage[i][pc_idx] < usage[sasa_hits-1][pc_idx]) begin + usage[i][pc_idx]<= (usage[i][pc_idx]+ 1) % SASA_SETS; + end else if (i == sasa_hits - 1) begin + usage[i][pc_idx] <= '0; + end + end + end + end + end + + always_comb begin : sasa_outputs + sasa_if.sasa_rs1 = '0; + sasa_if.sasa_rs2 = '0; + sasa_if.insts_to_skip = '0; + sasa_if.preceding_pc = sasa_if.pc; + sasa_if.condition = SASA_COND_OR; + sasa_if.valid = 1'b0; + sasa_hits = '0; + existing_entry = 0; + for (int i = 0; i < SASA_SETS; i++) begin + if (valid[i][pc_idx]&& (tag[i][pc_idx]== pc_tag)) begin + sasa_hits = i+1; + // ensure skipping is only valid for PC < 'hFFFF FFFC + //sasa_if.valid = (sasa_config == '0); + sasa_if.valid = (sasa_config == '0) && (sasa_if.pc[31:18] == '0); + sasa_if.sasa_rs1 = rs1[i][pc_idx]; + sasa_if.sasa_rs2 = rs2[i][pc_idx]; + sasa_if.condition = sasa_cond[i][pc_idx]; + sasa_if.insts_to_skip = insts_to_skip[i][pc_idx]; + end + if (valid[i][input_idx]&& (tag[i][input_idx] == input_data.prev_pc >> (`CLOG2(SASA_ENTRIES/SASA_SETS)))) begin + existing_entry = 1; + existing_entry_set = i; + end + end + end + +endmodule + diff --git a/source_code/sparce/sparce_enabled/sparce_sprf.sv b/source_code/sparce/sparce_enabled/sparce_sprf.sv new file mode 100644 index 000000000..7949c6f7c --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_sprf.sv @@ -0,0 +1,77 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_sprf.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/4/2019 +* Description: The file containing the sparsity register file (SpRF) +* module +*/ + +`include "sparce_internal_if.vh" + +module sparce_sprf ( + input logic CLK, + nRST, + sparce_internal_if.sprf sprf_if +); + + logic [31:1] sparsity_reg; + logic [31:0] sparsity_out; + + // all register outputs are tied to the registers, except for + // register[0] which is always 1 + assign sparsity_out[0] = 1'b1; + assign sparsity_out[31:1] = sparsity_reg[31:1]; + + // register update logic + always_ff @(posedge CLK, negedge nRST) begin + if (!nRST) begin + // even through regs are initialized to 0, don't treat + // uninitialized registers as sparse registers + sparsity_reg[31:1] <= '0; + end else begin + // keep registers as old values when not modified + sparsity_reg[31:1] <= sparsity_reg[31:1]; + // modify the chosen register if the pipeline is writing back + if (sprf_if.wb_en) begin + sparsity_reg[sprf_if.rd] <= sprf_if.is_sparse; + end + end + end + + // register output logic + always_comb begin + if (sprf_if.sasa_rs1 == '0) begin + sprf_if.rs1_sparsity = 1'b1; + end else if (sprf_if.sasa_rs1 == sprf_if.rd && sprf_if.wb_en) begin + sprf_if.rs1_sparsity = sprf_if.is_sparse; + end else begin + sprf_if.rs1_sparsity = sparsity_out[sprf_if.sasa_rs1]; + end + if (sprf_if.sasa_rs2 == '0) begin + sprf_if.rs2_sparsity = 1'b1; + end else if (sprf_if.sasa_rs2 == sprf_if.rd && sprf_if.wb_en) begin + sprf_if.rs2_sparsity = sprf_if.is_sparse; + end else begin + sprf_if.rs2_sparsity = sparsity_out[sprf_if.sasa_rs2]; + end + end + +endmodule + diff --git a/source_code/sparce/sparce_enabled/sparce_svc.sv b/source_code/sparce/sparce_enabled/sparce_svc.sv new file mode 100644 index 000000000..8811f6035 --- /dev/null +++ b/source_code/sparce/sparce_enabled/sparce_svc.sv @@ -0,0 +1,36 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_svc.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/4/2019 +* Description: The file containing the sparsity value chekcer (SVC) +* module +*/ + +`include "sparce_internal_if.vh" + +module sparce_svc ( + sparce_internal_if.svc svc_if +); + + // sparsity only when the writeback data is zero + assign svc_if.is_sparse = (svc_if.wb_data == 0); + +endmodule + diff --git a/source_code/sparce/sparce_wrapper.sv b/source_code/sparce/sparce_wrapper.sv new file mode 100644 index 000000000..8916861b0 --- /dev/null +++ b/source_code/sparce/sparce_wrapper.sv @@ -0,0 +1,44 @@ +/* +* Copyright 2019 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce_wrapper.sv +* +* Created by: Vadim V. Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 04/17/2019 +* Description: The top level wrapper file for implementations of +* sparisity optimiations based on the SparCE paper +*/ + +`include "sparce_pipeline_if.vh" +`include "component_selection_defines.vh" + +module sparce_wrapper ( + input logic CLK, + nRST, + sparce_pipeline_if.sparce sparce_if +); + + // Sparsity blocks used based on the SPARCE_ENABLED definition + generate + case (SPARCE_ENABLED) + "disabled": sparce_disabled sparce (.*); + "enabled": sparce_enabled sparce (.*); + endcase + endgenerate + +endmodule + diff --git a/source_code/standard_core/RISCVBusiness.sv b/source_code/standard_core/RISCVBusiness.sv index 9a0844b55..9a78a2c39 100644 --- a/source_code/standard_core/RISCVBusiness.sv +++ b/source_code/standard_core/RISCVBusiness.sv @@ -1,21 +1,21 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: RISCVBusiness.sv -* +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 @@ -23,66 +23,134 @@ */ `include "generic_bus_if.vh" -`include "ahb_if.vh" `include "component_selection_defines.vh" `include "risc_mgmt_if.vh" `include "cache_control_if.vh" +`include "sparce_pipeline_if.vh" +`include "tspp_fetch_execute_if.vh" +`include "tspp_hazard_unit_if.vh" +`include "core_interrupt_if.vh" +`include "rv32c_if.vh" module RISCVBusiness ( - input logic CLK, nRST, - output logic halt, - - `ifdef BUS_INTERFACE_GENERIC_BUS - generic_bus_if.cpu gen_bus_if - `elsif BUS_INTERFACE_AHB - ahb_if.ahb_m ahb_master - `endif -); - - // Interface instantiations + input logic CLK, nRST, + input logic [63:0] mtime, + output logic wfi, + halt, + core_interrupt_if.core interrupt_if, +`ifdef BUS_INTERFACE_GENERIC_BUS + generic_bus_if.cpu gen_bus_if +`elsif BUS_INTERFACE_AHB + ahb_if.manager ahb_manager +`elsif BUS_INTERFACE_APB + apb_if.requester apb_requester +`endif - generic_bus_if tspp_icache_gen_bus_if(); - generic_bus_if tspp_dcache_gen_bus_if(); - generic_bus_if icache_mc_if(); - generic_bus_if dcache_mc_if(); - generic_bus_if pipeline_trans_if(); - risc_mgmt_if rm_if(); - predictor_pipeline_if predict_if(); - prv_pipeline_if prv_pipe_if(); - cache_control_if cc_if(); - - // Module Instantiations +); + parameter logic [31:0] RESET_PC = 32'h80000000; + + // Interface instantiations + + generic_bus_if tspp_icache_gen_bus_if (); + generic_bus_if tspp_dcache_gen_bus_if (); + generic_bus_if icache_mc_if (); + generic_bus_if dcache_mc_if (); + generic_bus_if pipeline_trans_if (); + risc_mgmt_if rm_if (); + predictor_pipeline_if predict_if (); + prv_pipeline_if prv_pipe_if (); + cache_control_if cc_if (); + sparce_pipeline_if sparce_if (); + rv32c_if rv32cif (); + + //interface instantiations + tspp_fetch_execute_if fetch_ex_if (); + tspp_hazard_unit_if hazard_if (); + + stage3 #(.RESET_PC(RESET_PC)) pipeline( + .igen_bus_if(tspp_icache_gen_bus_if), + .dgen_bus_if(tspp_dcache_gen_bus_if), + .* + ); + + // Module Instantiations + /* pipeline_wrapper pipeline ( .CLK(CLK), .nRST(nRST), .halt(halt), .igen_bus_if(tspp_icache_gen_bus_if), .dgen_bus_if(tspp_dcache_gen_bus_if), - .prv_pipe_if(prv_pipe_if), + .prv_pipe_if(prv_pipe_if), // TODO: Look at the communications between pipeline_wrapper and priv_wrapper .predict_if(predict_if), .rm_if(rm_if), - .cc_if(cc_if) - ); - - branch_predictor_wrapper branch_predictor_i ( - .CLK(CLK), - .nRST(nRST), - .predict_if(predict_if) - ); - - priv_wrapper priv_wrapper_i ( - .CLK(CLK), - .nRST(nRST), - .prv_pipe_if(prv_pipe_if) + .cc_if(cc_if), + .sparce_if(sparce_if) ); +*/ - risc_mgmt_wrapper rmgmt ( - .CLK(CLK), - .nRST(nRST), - .rm_if(rm_if) - ); +/* + tspp_fetch_stage #( + .RESET_PC(RESET_PC) + ) fetch_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .igen_bus_if(tspp_icache_gen_bus_if), + .sparce_if(sparce_if), + .rv32cif(rv32cif), + .prv_pipe_if(prv_pipe_if) + ); + + tspp_execute_stage execute_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .dgen_bus_if(tspp_dcache_gen_bus_if), + .prv_pipe_if(prv_pipe_if), + .halt(halt), + .rm_if(rm_if), + .cc_if(cc_if), + .sparce_if(sparce_if), + .rv32cif(rv32cif), + .wfi(wfi) + ); + + tspp_hazard_unit hazard_unit_i ( + .hazard_if(hazard_if), + .prv_pipe_if(prv_pipe_if), + .rm_if(rm_if), + .sparce_if(sparce_if) + ); +*/ + branch_predictor_wrapper branch_predictor_i ( + .CLK(CLK), + .nRST(nRST), + .predict_if(predict_if) + ); + + priv_wrapper priv_wrapper_i ( + .CLK(CLK), + .nRST(nRST), + .prv_pipe_if(prv_pipe_if), + .interrupt_if, + .mtime(mtime) + ); + +/* TODO: Adding back RISC-MGMT to 3-stage pipeline + risc_mgmt_wrapper rmgmt ( + .CLK (CLK), + .nRST (nRST), + .rm_if(rm_if) + ); +*/ + /* caches_wrapper caches ( .CLK(CLK), .nRST(nRST), @@ -92,37 +160,75 @@ module RISCVBusiness ( .dcache_mem_gen_bus_if(dcache_mc_if), .cc_if(cc_if) ); +*/ - memory_controller mc ( - .CLK(CLK), - .nRST(nRST), - .d_gen_bus_if(dcache_mc_if), - .i_gen_bus_if(icache_mc_if), - .out_gen_bus_if(pipeline_trans_if) - ); - - // Instantiate the chosen bus interface - - generate - case (BUS_INTERFACE_TYPE) - "generic_bus_if" : begin - generic_nonpipeline bt( - .CLK(CLK), - .nRST(nRST), - .pipeline_trans_if(pipeline_trans_if), - .out_gen_bus_if(gen_bus_if) - ); - end - "ahb_if" : begin - ahb bt ( - .CLK(CLK), - .nRST(nRST), - .out_gen_bus_if(pipeline_trans_if), - .ahb_m(ahb_master) - ); - end - endcase - - endgenerate + separate_caches sep_caches ( + .CLK(CLK), + .nRST(nRST), + .icache_proc_gen_bus_if(tspp_icache_gen_bus_if), + .icache_mem_gen_bus_if(icache_mc_if), + .dcache_proc_gen_bus_if(tspp_dcache_gen_bus_if), + .dcache_mem_gen_bus_if(dcache_mc_if), + .cc_if(cc_if) + ); + + memory_controller mc ( + .CLK(CLK), + .nRST(nRST), + .d_gen_bus_if(dcache_mc_if), + .i_gen_bus_if(icache_mc_if), + .out_gen_bus_if(pipeline_trans_if) + ); + + /* + sparce_wrapper sparce_wrapper_i ( + .CLK(CLK), + .nRST(nRST), + .sparce_if(sparce_if) + );*/ + + sparce_disabled sparce_disabled_i ( + .CLK(CLK), + .nRST(nRST), + .sparce_if(sparce_if) + ); + + rv32c_wrapper rv32c ( + .CLK(CLK), + .nRST(nRST), + .rv32cif(rv32cif) + ); + + // Instantiate the chosen bus interface + + generate + case (BUS_INTERFACE_TYPE) + "generic_bus_if": begin : g_generic_bus_if + generic_nonpipeline bt ( + .CLK(CLK), + .nRST(nRST), + .pipeline_trans_if(pipeline_trans_if), + .out_gen_bus_if(gen_bus_if) + ); + end + "ahb_if": begin : g_ahb_if + ahb bt ( + .CLK(CLK), + .nRST(nRST), + .out_gen_bus_if(pipeline_trans_if), + .ahb_m(ahb_manager) + ); + end + /*"apb_if": begin : g_apb_if + apb bt( + .CLK(CLK), + .nRST(nRST), + .out_gen_bus_if(pipeline_trans_if), + .apbif(apb_requester) + ); + end*/ + endcase + + endgenerate endmodule diff --git a/source_code/standard_core/RISCVBusiness_no_memory.sv b/source_code/standard_core/RISCVBusiness_no_memory.sv new file mode 100644 index 000000000..63ac60041 --- /dev/null +++ b/source_code/standard_core/RISCVBusiness_no_memory.sv @@ -0,0 +1,179 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: RISCVBusiness.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Top level module for RISCVBusiness +*/ + +`include "generic_bus_if.vh" +`include "component_selection_defines.vh" +`include "risc_mgmt_if.vh" +`include "cache_control_if.vh" +`include "sparce_pipeline_if.vh" +`include "tspp_fetch_execute_if.vh" +`include "tspp_hazard_unit_if.vh" +`include "core_interrupt_if.vh" +`include "rv32c_if.vh" + +module RISCVBusiness_no_memory ( + input logic CLK, + nRST, + output logic wfi, + halt, + core_interrupt_if.core interrupt_if, + generic_bus_if.cpu igen_bus_if, + generic_bus_if.cpu dgen_bus_if + +); + + parameter logic [31:0] RESET_PC = 32'h80000000; + + // Interface instantiations + function logic [31:0] get_x28(); + // verilator public + return pipeline.execute_stage_i.g_rfile_select.rf.registers[28]; + endfunction + + risc_mgmt_if rm_if (); + predictor_pipeline_if predict_if (); + prv_pipeline_if prv_pipe_if (); + cache_control_if cc_if (); + sparce_pipeline_if sparce_if (); + rv32c_if rv32cif (); + + //interface instantiations + tspp_fetch_execute_if fetch_ex_if (); + tspp_hazard_unit_if hazard_if (); + + stage3 #(.RESET_PC(RESET_PC)) pipeline( + .* + ); + + // hardwire cache control signals + assign cc_if.iflush_done = 1'b1; + assign cc_if.iclear_done = 1'b1; + assign cc_if.dflush_done = 1'b1; + assign cc_if.dclear_done = 1'b1; + + // Module Instantiations + /* + pipeline_wrapper pipeline ( + .CLK(CLK), + .nRST(nRST), + .halt(halt), + .igen_bus_if(tspp_icache_gen_bus_if), + .dgen_bus_if(tspp_dcache_gen_bus_if), + .prv_pipe_if(prv_pipe_if), // TODO: Look at the communications between pipeline_wrapper and priv_wrapper + .predict_if(predict_if), + .rm_if(rm_if), + .cc_if(cc_if), + .sparce_if(sparce_if) + ); +*/ + +/* + tspp_fetch_stage #( + .RESET_PC(RESET_PC) + ) fetch_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .igen_bus_if(tspp_icache_gen_bus_if), + .sparce_if(sparce_if), + .rv32cif(rv32cif), + .prv_pipe_if(prv_pipe_if) + ); + + tspp_execute_stage execute_stage_i ( + .CLK(CLK), + .nRST(nRST), + .fetch_ex_if(fetch_ex_if), + .hazard_if(hazard_if), + .predict_if(predict_if), + .dgen_bus_if(tspp_dcache_gen_bus_if), + .prv_pipe_if(prv_pipe_if), + .halt(halt), + .rm_if(rm_if), + .cc_if(cc_if), + .sparce_if(sparce_if), + .rv32cif(rv32cif), + .wfi(wfi) + ); + + tspp_hazard_unit hazard_unit_i ( + .hazard_if(hazard_if), + .prv_pipe_if(prv_pipe_if), + .rm_if(rm_if), + .sparce_if(sparce_if) + ); +*/ + + branch_predictor_wrapper branch_predictor_i ( + .CLK(CLK), + .nRST(nRST), + .predict_if(predict_if) + ); + + priv_wrapper priv_wrapper_i ( + .CLK(CLK), + .nRST(nRST), + .prv_pipe_if(prv_pipe_if), + .interrupt_if + ); + + risc_mgmt_wrapper rmgmt ( + .CLK (CLK), + .nRST (nRST), + .rm_if(rm_if) + ); + + /* + caches_wrapper caches ( + .CLK(CLK), + .nRST(nRST), + .icache_proc_gen_bus_if(tspp_icache_gen_bus_if), + .icache_mem_gen_bus_if(icache_mc_if), + .dcache_proc_gen_bus_if(tspp_dcache_gen_bus_if), + .dcache_mem_gen_bus_if(dcache_mc_if), + .cc_if(cc_if) + ); +*/ + /* + sparce_wrapper sparce_wrapper_i ( + .CLK(CLK), + .nRST(nRST), + .sparce_if(sparce_if) + );*/ + + sparce_disabled sparce_disabled_i ( + .CLK(CLK), + .nRST(nRST), + .sparce_if(sparce_if) + ); + + rv32c_wrapper rv32c ( + .CLK(CLK), + .nRST(nRST), + .rv32cif(rv32cif) + ); + +endmodule diff --git a/source_code/standard_core/alu.sv b/source_code/standard_core/alu.sv index 5f5be44fc..8af0f4b1d 100644 --- a/source_code/standard_core/alu.sv +++ b/source_code/standard_core/alu.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,54 +27,53 @@ `include "alu_if.vh" module alu ( - alu_if.alu alu_if + alu_if.alu alu_if ); - - import alu_types_pkg::*; - import rv32i_types_pkg::*; - word_t adder_result, op_a, op_b, twos_comp_b; - logic carry_out, sign_r, sign_a, sign_b; - logic [WORD_SIZE : 0] adder_out, op_a_ext, op_b_ext; + import alu_types_pkg::*; + import rv32i_types_pkg::*; + + word_t adder_result, op_a, op_b, twos_comp_b; + logic carry_out, sign_r, sign_a, sign_b; + logic [WORD_SIZE : 0] adder_out, op_a_ext, op_b_ext; - //sign bits of adder result and operands - assign sign_r = adder_out[WORD_SIZE-1]; - assign sign_a = alu_if.port_a[WORD_SIZE-1]; - assign sign_b = alu_if.port_b[WORD_SIZE-1]; + //sign bits of adder result and operands + assign sign_r = adder_out[WORD_SIZE-1]; + assign sign_a = alu_if.port_a[WORD_SIZE-1]; + assign sign_b = alu_if.port_b[WORD_SIZE-1]; - //assign adder operands (2's compilment b for subtraction) - assign op_a = alu_if.port_a; - assign op_b = (alu_if.aluop == ALU_ADD) ? alu_if.port_b : twos_comp_b; - assign twos_comp_b = (~alu_if.port_b) + 1; + //assign adder operands (2's compilment b for subtraction) + assign op_a = alu_if.port_a; + assign op_b = (alu_if.aluop == ALU_ADD) ? alu_if.port_b : twos_comp_b; + assign twos_comp_b = (~alu_if.port_b) + 1; - //extend operands a and b for the adder - assign op_a_ext[WORD_SIZE] = (alu_if.aluop == ALU_SLTU) ? 1'b0 : op_a[WORD_SIZE-1]; - assign op_b_ext[WORD_SIZE] = (alu_if.aluop == ALU_SLTU) ? 1'b0 : op_b[WORD_SIZE-1]; - assign op_a_ext[WORD_SIZE-1:0] = op_a; - assign op_b_ext[WORD_SIZE-1:0] = op_b; + //extend operands a and b for the adder + assign op_a_ext[WORD_SIZE] = (alu_if.aluop == ALU_SLTU) ? 1'b0 : op_a[WORD_SIZE-1]; + assign op_b_ext[WORD_SIZE] = (alu_if.aluop == ALU_SLTU) ? 1'b0 : op_b[WORD_SIZE-1]; + assign op_a_ext[WORD_SIZE-1:0] = op_a; + assign op_b_ext[WORD_SIZE-1:0] = op_b; - //separate the carry out and result - assign adder_out = op_a_ext + op_b_ext; - assign adder_result = adder_out[WORD_SIZE-1:0]; - assign carry_out = adder_out[WORD_SIZE]; + //separate the carry out and result + assign adder_out = op_a_ext + op_b_ext; + assign adder_result = adder_out[WORD_SIZE-1:0]; + assign carry_out = adder_out[WORD_SIZE]; - always_comb begin - casez (alu_if.aluop) - ALU_SLL : alu_if.port_out = alu_if.port_a << alu_if.port_b[4:0]; - ALU_SRL : alu_if.port_out = alu_if.port_a >> alu_if.port_b[4:0]; - ALU_SRA : alu_if.port_out = $signed(alu_if.port_a) >>> alu_if.port_b[4:0]; - ALU_AND : alu_if.port_out = alu_if.port_a & alu_if.port_b; - ALU_OR : alu_if.port_out = alu_if.port_a | alu_if.port_b; - ALU_XOR : alu_if.port_out = alu_if.port_a ^ alu_if.port_b; - ALU_SLT : begin - alu_if.port_out = (sign_a & !sign_b) ? 1 : - ((!sign_a & sign_b) ? 0 : sign_r); - end - ALU_SLTU : alu_if.port_out = ~carry_out & |(op_b_ext); - ALU_ADD : alu_if.port_out = adder_result; - ALU_SUB : alu_if.port_out = adder_result; - default : alu_if.port_out = '0; - endcase - end + always_comb begin + casez (alu_if.aluop) + ALU_SLL: alu_if.port_out = alu_if.port_a << alu_if.port_b[4:0]; + ALU_SRL: alu_if.port_out = alu_if.port_a >> alu_if.port_b[4:0]; + ALU_SRA: alu_if.port_out = $signed(alu_if.port_a) >>> alu_if.port_b[4:0]; + ALU_AND: alu_if.port_out = alu_if.port_a & alu_if.port_b; + ALU_OR: alu_if.port_out = alu_if.port_a | alu_if.port_b; + ALU_XOR: alu_if.port_out = alu_if.port_a ^ alu_if.port_b; + ALU_SLT: begin + alu_if.port_out = (sign_a & !sign_b) ? 1 : ((!sign_a & sign_b) ? 0 : sign_r); + end + ALU_SLTU: alu_if.port_out = ~carry_out & |(op_b_ext); + ALU_ADD: alu_if.port_out = adder_result; + ALU_SUB: alu_if.port_out = adder_result; + default: alu_if.port_out = '0; + endcase + end endmodule diff --git a/source_code/standard_core/branch_res.sv b/source_code/standard_core/branch_res.sv index f04f66e5d..a8459c132 100644 --- a/source_code/standard_core/branch_res.sv +++ b/source_code/standard_core/branch_res.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,67 +20,66 @@ * Email: jskubic@purdue.edu * Date Created: 06/14/2016 * Description: Determines if a branch should be taken and outputs -* the target address. Optimized to use only one adder +* the target address. Optimized to use only one adder */ `include "branch_res_if.vh" module branch_res ( - branch_res_if.bres br_if + branch_res_if.bres br_if ); - - import rv32i_types_pkg::*; - - word_t offset; - logic lt, eq, ltu; - logic sign_1, sign_2, sign_r, carry_out; - logic [WORD_SIZE : 0] adder_out, op_1_ext, op_2_ext; - // target addr generation - assign offset = $signed(br_if.imm_sb); - assign br_if.branch_addr = br_if.pc + offset; + import rv32i_types_pkg::*; - //sign bits - assign sign_1 = br_if.rs1_data[WORD_SIZE-1]; - assign sign_2 = br_if.rs2_data[WORD_SIZE-1]; - assign sign_r = adder_out[WORD_SIZE-1]; - - //build operands - assign op_1_ext[WORD_SIZE-1:0] = br_if.rs1_data; - assign op_2_ext[WORD_SIZE-1:0] = (~br_if.rs2_data) + 1; + word_t offset; + logic lt, eq, ltu; + logic sign_1, sign_2, sign_r, carry_out; + logic [WORD_SIZE : 0] + adder_out, op_1_ext /* verilator split_var */, op_2_ext /* verilator split_var */; - always_comb begin - if(br_if.branch_type == BLTU || br_if.branch_type == BGEU) begin - op_1_ext[WORD_SIZE] = 1'b0; - op_2_ext[WORD_SIZE] = 1'b0; - end - else begin - op_1_ext[WORD_SIZE] = op_1_ext[WORD_SIZE-1]; - op_2_ext[WORD_SIZE] = op_2_ext[WORD_SIZE-1]; + // target addr generation + assign offset = $signed(br_if.imm_sb); + assign br_if.branch_addr = br_if.pc + offset; + + //sign bits + assign sign_1 = br_if.rs1_data[WORD_SIZE-1]; + assign sign_2 = br_if.rs2_data[WORD_SIZE-1]; + assign sign_r = adder_out[WORD_SIZE-1]; + + //build operands + assign op_1_ext[WORD_SIZE-1:0] = br_if.rs1_data; + assign op_2_ext[WORD_SIZE-1:0] = (~br_if.rs2_data) + 1; + + always_comb begin + if (br_if.branch_type == BLTU || br_if.branch_type == BGEU) begin + op_1_ext[WORD_SIZE] = 1'b0; + op_2_ext[WORD_SIZE] = 1'b0; + end else begin + op_1_ext[WORD_SIZE] = op_1_ext[WORD_SIZE-1]; + op_2_ext[WORD_SIZE] = op_2_ext[WORD_SIZE-1]; + end end - end - //adder - assign adder_out = op_1_ext + op_2_ext; - assign carry_out = adder_out[WORD_SIZE]; - - // condition calculations - assign eq = br_if.rs1_data == br_if.rs2_data; - assign lt = (sign_1 & ~sign_2) ? 1 : - ((~sign_1 & sign_2) ? 0 : sign_r); + //adder + assign adder_out = op_1_ext + op_2_ext; + assign carry_out = adder_out[WORD_SIZE]; - assign ltu = ~carry_out & |(op_2_ext); + // condition calculations + assign eq = br_if.rs1_data == br_if.rs2_data; + assign lt = (sign_1 & ~sign_2) ? 1 : ((~sign_1 & sign_2) ? 0 : sign_r); - always_comb begin - casez (br_if.branch_type) - BEQ : br_if.branch_taken = eq; - BNE : br_if.branch_taken = ~eq; - BLT : br_if.branch_taken = lt; - BGE : br_if.branch_taken = ~lt; - BLTU : br_if.branch_taken = ltu; - BGEU : br_if.branch_taken = ~ltu; - default : br_if.branch_taken = 1'b0; - endcase - end + assign ltu = ~carry_out & |(op_2_ext); + + always_comb begin + casez (br_if.branch_type) + BEQ : br_if.branch_taken = eq; + BNE : br_if.branch_taken = ~eq; + BLT : br_if.branch_taken = lt; + BGE : br_if.branch_taken = ~lt; + BLTU : br_if.branch_taken = ltu; + BGEU : br_if.branch_taken = ~ltu; + default : br_if.branch_taken = 1'b0; + endcase + end endmodule diff --git a/source_code/standard_core/control_unit.sv b/source_code/standard_core/control_unit.sv index 71e756830..364e6b01e 100644 --- a/source_code/standard_core/control_unit.sv +++ b/source_code/standard_core/control_unit.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,237 +20,252 @@ * Email: steven69@purdue.edu * Date Created: 06/09/2016 * Description: The control unit combinationally sets all of the control -* signals used in the processor based on the incoming instruction. +* signals used in the processor based on the incoming instruction. */ `include "control_unit_if.vh" `include "rv32i_reg_file_if.vh" `include "risc_mgmt_if.vh" +`include "decompressor_if.vh" +`include "component_selection_defines.vh" -module control_unit -( - control_unit_if.control_unit cu_if, - rv32i_reg_file_if.cu rf_if, - input logic [4:0] rmgmt_rsel_s_0, rmgmt_rsel_s_1, rmgmt_rsel_d, - input logic rmgmt_req_reg_r, rmgmt_req_reg_w +module control_unit ( + control_unit_if.control_unit cu_if, + rv32i_reg_file_if.cu rf_if, + input logic [4:0] rmgmt_rsel_s_0, + rmgmt_rsel_s_1, + rmgmt_rsel_d, + input logic rmgmt_req_reg_r, + rmgmt_req_reg_w ); - import alu_types_pkg::*; - import rv32i_types_pkg::*; - import machine_mode_types_1_11_pkg::*; + import alu_types_pkg::*; + import rv32i_types_pkg::*; + import machine_mode_types_1_12_pkg::*; + import rv32m_pkg::*; - stype_t instr_s; - itype_t instr_i; - rtype_t instr_r; - sbtype_t instr_sb; - utype_t instr_u; - ujtype_t instr_uj; + stype_t instr_s; + itype_t instr_i; + rtype_t instr_r; + sbtype_t instr_sb; + utype_t instr_u; + ujtype_t instr_uj; - assign instr_s = stype_t'(cu_if.instr); - assign instr_i = itype_t'(cu_if.instr); - assign instr_r = rtype_t'(cu_if.instr); - assign instr_sb = sbtype_t'(cu_if.instr); - assign instr_u = utype_t'(cu_if.instr); - assign instr_uj = ujtype_t'(cu_if.instr); + // Set if base ISA doesn't have this instruction, but overriden by claim from extension + logic maybe_illegal; + logic claimed; + // Per-extension claim signals + logic rv32m_claim; - assign cu_if.opcode = opcode_t'(cu_if.instr[6:0]); - assign rf_if.rs1 = rmgmt_req_reg_r ? rmgmt_rsel_s_0 : cu_if.instr[19:15]; - assign rf_if.rs2 = rmgmt_req_reg_r ? rmgmt_rsel_s_1 : cu_if.instr[24:20]; - assign rf_if.rd = rmgmt_req_reg_w ? rmgmt_rsel_d : cu_if.instr[11:7]; - assign cu_if.shamt = cu_if.instr[24:20]; - - // Assign the immediate values - assign cu_if.imm_I = instr_i.imm11_00; - assign cu_if.imm_S = {instr_s.imm11_05, instr_s.imm04_00}; - assign cu_if.imm_SB = {instr_sb.imm12, instr_sb.imm11, instr_sb.imm10_05, - instr_sb.imm04_01, 1'b0}; - assign cu_if.imm_UJ = {instr_uj.imm20, instr_uj.imm19_12, instr_uj.imm11, - instr_uj.imm10_01, 1'b0}; - assign cu_if.imm_U = {instr_u.imm31_12, 12'b0}; + assign instr_s = stype_t'(cu_if.instr); + assign instr_i = itype_t'(cu_if.instr); + assign instr_r = rtype_t'(cu_if.instr); + assign instr_sb = sbtype_t'(cu_if.instr); + assign instr_u = utype_t'(cu_if.instr); + assign instr_uj = ujtype_t'(cu_if.instr); - assign cu_if.imm_shamt_sel = (cu_if.opcode == IMMED && - (instr_i.funct3 == SLLI || instr_i.funct3 == SRI)); + assign cu_if.opcode = opcode_t'(cu_if.instr[6:0]); + assign rf_if.rs1 = rmgmt_req_reg_r ? rmgmt_rsel_s_0 : cu_if.instr[19:15]; + assign rf_if.rs2 = rmgmt_req_reg_r ? rmgmt_rsel_s_1 : cu_if.instr[24:20]; + assign cu_if.rd = rmgmt_req_reg_w ? rmgmt_rsel_d : cu_if.instr[11:7]; + assign cu_if.shamt = cu_if.instr[24:20]; - // Assign branch and load type - assign cu_if.load_type = load_t'(instr_i.funct3); - assign cu_if.branch_type = branch_t'(instr_sb.funct3); + // Assign the immediate values + assign cu_if.imm_I = instr_i.imm11_00; + assign cu_if.imm_S = {instr_s.imm11_05, instr_s.imm04_00}; + assign cu_if.imm_SB = { + instr_sb.imm12, instr_sb.imm11, instr_sb.imm10_05, instr_sb.imm04_01, 1'b0 + }; + assign cu_if.imm_UJ = { + instr_uj.imm20, instr_uj.imm19_12, instr_uj.imm11, instr_uj.imm10_01, 1'b0 + }; + assign cu_if.imm_U = {instr_u.imm31_12, 12'b0}; - // Assign memory read/write enables - assign cu_if.dwen = (cu_if.opcode == STORE); - assign cu_if.dren = (cu_if.opcode == LOAD); - assign cu_if.ifence = (cu_if.opcode == MISCMEM) && (rv32i_miscmem_t'(instr_r.funct3) == FENCEI); + assign cu_if.imm_shamt_sel = (cu_if.opcode == IMMED && + (instr_i.funct3 == SLLI || instr_i.funct3 == SRI)); - // Assign control flow signals - assign cu_if.branch = (cu_if.opcode == BRANCH); - assign cu_if.jump = (cu_if.opcode == JAL || cu_if.opcode == JALR); - assign cu_if.ex_pc_sel = (cu_if.opcode == JAL || cu_if.opcode == JALR); - assign cu_if.j_sel = (cu_if.opcode == JAL); + // Assign branch and load type + assign cu_if.load_type = load_t'(instr_i.funct3); + assign cu_if.branch_type = branch_t'(instr_sb.funct3); - // Assign alu operands - always_comb begin - case(cu_if.opcode) - REGREG, IMMED, LOAD : cu_if.alu_a_sel = 2'd0; - STORE : cu_if.alu_a_sel = 2'd1; - AUIPC : cu_if.alu_a_sel = 2'd2; - default : cu_if.alu_a_sel = 2'd2; - endcase - end + // Assign memory read/write enables + assign cu_if.dwen = (cu_if.opcode == STORE); + assign cu_if.dren = (cu_if.opcode == LOAD); + assign cu_if.ifence = (cu_if.opcode == MISCMEM) && (rv32i_miscmem_t'(instr_r.funct3) == FENCEI); - always_comb begin - case(cu_if.opcode) - STORE : cu_if.alu_b_sel = 2'd0; - REGREG : cu_if.alu_b_sel = 2'd1; - IMMED, LOAD : cu_if.alu_b_sel = 2'd2; - AUIPC : cu_if.alu_b_sel = 2'd3; - default : cu_if.alu_b_sel = 2'd1; - endcase - end + // Assign control flow signals + assign cu_if.branch = (cu_if.opcode == BRANCH); + assign cu_if.jump = (cu_if.opcode == JAL || cu_if.opcode == JALR); + assign cu_if.ex_pc_sel = (cu_if.opcode == JAL || cu_if.opcode == JALR); + assign cu_if.j_sel = (cu_if.opcode == JAL); + // Assign alu operands + always_comb begin + case (cu_if.opcode) + REGREG, IMMED, LOAD: cu_if.alu_a_sel = 2'd0; + STORE: cu_if.alu_a_sel = 2'd1; + AUIPC: cu_if.alu_a_sel = 2'd2; + default: cu_if.alu_a_sel = 2'd2; + endcase + end - // Assign write select - always_comb begin - case(cu_if.opcode) - LOAD : cu_if.w_sel = 3'd0; - JAL, JALR : cu_if.w_sel = 3'd1; - LUI : cu_if.w_sel = 3'd2; - IMMED, AUIPC, REGREG : cu_if.w_sel = 3'd3; - SYSTEM : cu_if.w_sel = 3'd4; - default : cu_if.w_sel = 3'd0; - endcase - end + always_comb begin + case (cu_if.opcode) + STORE: cu_if.alu_b_sel = 2'd0; + REGREG: cu_if.alu_b_sel = 2'd1; + IMMED, LOAD: cu_if.alu_b_sel = 2'd2; + AUIPC: cu_if.alu_b_sel = 2'd3; + default: cu_if.alu_b_sel = 2'd1; + endcase + end - // Assign register write enable - always_comb begin - case(cu_if.opcode) - STORE, BRANCH : cu_if.wen = 1'b0; - IMMED, LUI, AUIPC, - REGREG, JAL, JALR, - LOAD : cu_if.wen = 1'b1; - SYSTEM : cu_if.wen = cu_if.csr_rw_valid; - default: cu_if.wen = 1'b0; - endcase - end + // Assign write select + always_comb begin + case (cu_if.opcode) + LOAD: cu_if.w_sel = 3'd0; + JAL, JALR: cu_if.w_sel = 3'd1; + LUI: cu_if.w_sel = 3'd2; + IMMED, AUIPC, REGREG: cu_if.w_sel = 3'd3; // RV32M: Opcodes are REGREG, no change needed + SYSTEM: cu_if.w_sel = 3'd4; + default: cu_if.w_sel = 3'd0; + endcase + end - // Assign alu opcode - logic sr, aluop_srl, aluop_sra, aluop_add, aluop_sub, aluop_and, aluop_or; - logic aluop_sll, aluop_xor, aluop_slt, aluop_sltu, add_sub; + // Assign register write enable + always_comb begin + case (cu_if.opcode) + STORE, BRANCH: cu_if.wen = 1'b0; + IMMED, LUI, AUIPC, REGREG, JAL, JALR, LOAD: cu_if.wen = 1'b1; + SYSTEM: cu_if.wen = cu_if.csr_rw_valid; + default: cu_if.wen = 1'b0; + endcase + end + // Assign alu opcode + logic sr, aluop_srl, aluop_sra, aluop_add, aluop_sub, aluop_and, aluop_or; + logic aluop_sll, aluop_xor, aluop_slt, aluop_sltu, add_sub; - assign sr = ((cu_if.opcode == IMMED && instr_i.funct3 == SRI) || + + assign sr = ((cu_if.opcode == IMMED && instr_i.funct3 == SRI) || (cu_if.opcode == REGREG && instr_r.funct3 == SR)); - assign add_sub = (cu_if.opcode == REGREG && instr_r.funct3 == ADDSUB); - - assign aluop_sll = ((cu_if.opcode == IMMED && instr_i.funct3 == SLLI) || + assign add_sub = (cu_if.opcode == REGREG && instr_r.funct3 == ADDSUB); + + assign aluop_sll = ((cu_if.opcode == IMMED && instr_i.funct3 == SLLI) || (cu_if.opcode == REGREG && instr_r.funct3 == SLL)); - assign aluop_sra = sr && cu_if.instr[30]; - assign aluop_srl = sr && ~cu_if.instr[30]; - assign aluop_add = ((cu_if.opcode == IMMED && instr_i.funct3 == ADDI) || + assign aluop_sra = sr && cu_if.instr[30]; + assign aluop_srl = sr && ~cu_if.instr[30]; + assign aluop_add = ((cu_if.opcode == IMMED && instr_i.funct3 == ADDI) || (cu_if.opcode == AUIPC) || (add_sub && ~cu_if.instr[30]) || (cu_if.opcode == LOAD) || (cu_if.opcode == STORE)); - assign aluop_sub = (add_sub && cu_if.instr[30]); - assign aluop_and = ((cu_if.opcode == IMMED && instr_i.funct3 == ANDI) || + assign aluop_sub = (add_sub && cu_if.instr[30]); + assign aluop_and = ((cu_if.opcode == IMMED && instr_i.funct3 == ANDI) || (cu_if.opcode == REGREG && instr_r.funct3 == AND)); - assign aluop_or = ((cu_if.opcode == IMMED && instr_i.funct3 == ORI) || + assign aluop_or = ((cu_if.opcode == IMMED && instr_i.funct3 == ORI) || (cu_if.opcode == REGREG && instr_r.funct3 == OR)); - assign aluop_xor = ((cu_if.opcode == IMMED && instr_i.funct3 == XORI) || + assign aluop_xor = ((cu_if.opcode == IMMED && instr_i.funct3 == XORI) || (cu_if.opcode == REGREG && instr_r.funct3 == XOR)); - assign aluop_slt = ((cu_if.opcode == IMMED && instr_i.funct3 == SLTI) || + assign aluop_slt = ((cu_if.opcode == IMMED && instr_i.funct3 == SLTI) || (cu_if.opcode == REGREG && instr_r.funct3 == SLT)); - assign aluop_sltu = ((cu_if.opcode == IMMED && instr_i.funct3 == SLTIU) || + assign aluop_sltu = ((cu_if.opcode == IMMED && instr_i.funct3 == SLTIU) || (cu_if.opcode == REGREG && instr_r.funct3 == SLTU)); - always_comb begin - if (aluop_sll) - cu_if.alu_op = ALU_SLL; - else if (aluop_sra) - cu_if.alu_op = ALU_SRA; - else if (aluop_srl) - cu_if.alu_op = ALU_SRL; - else if (aluop_add) - cu_if.alu_op = ALU_ADD; - else if (aluop_sub) - cu_if.alu_op = ALU_SUB; - else if (aluop_and) - cu_if.alu_op = ALU_AND; - else if (aluop_or) - cu_if.alu_op = ALU_OR; - else if (aluop_xor) - cu_if.alu_op = ALU_XOR; - else if (aluop_slt) - cu_if.alu_op = ALU_SLT; - else if (aluop_sltu) - cu_if.alu_op = ALU_SLTU; - else - cu_if.alu_op = ALU_ADD; - end + always_comb begin + if (aluop_sll) cu_if.alu_op = ALU_SLL; + else if (aluop_sra) cu_if.alu_op = ALU_SRA; + else if (aluop_srl) cu_if.alu_op = ALU_SRL; + else if (aluop_add) cu_if.alu_op = ALU_ADD; + else if (aluop_sub) cu_if.alu_op = ALU_SUB; + else if (aluop_and) cu_if.alu_op = ALU_AND; + else if (aluop_or) cu_if.alu_op = ALU_OR; + else if (aluop_xor) cu_if.alu_op = ALU_XOR; + else if (aluop_slt) cu_if.alu_op = ALU_SLT; + else if (aluop_sltu) cu_if.alu_op = ALU_SLTU; + else cu_if.alu_op = ALU_ADD; + end + + // HALT HACK. Just looking for j + 0x0 (infinite loop) + // Halt required for unit testing, but not useful in tapeout context + // Due to presence of interrupts, infinite loops are valid + generate + if (INFINITE_LOOP_HALTS == "true") begin : g_inf_loop_halt + assign cu_if.halt = (cu_if.instr == 32'h0000006f); + end else begin : g_no_halt + assign cu_if.halt = '0; + end + endgenerate + // Privilege Control Signals + assign cu_if.fault_insn = '0; - // HALT HACK. Just looking for j + 0x0 (infinite loop) - // TODO: FIX ME WHEN IMPLEMENTING INTERRUPTS - assign cu_if.halt = (cu_if.instr == 32'h0000006f); - // Privilege Control Signals - assign cu_if.fault_insn = 'b0; - - always_comb begin - case(cu_if.opcode) - REGREG: cu_if.illegal_insn = instr_r.funct7[0]; - LUI, AUIPC, JAL, JALR, - BRANCH, LOAD, STORE, - IMMED, SYSTEM, - MISCMEM, opcode_t'('0) : cu_if.illegal_insn = 1'b0; - default : cu_if.illegal_insn = 1'b1; - endcase - end - - //Decoding of System Priv Instructions - always_comb begin - cu_if.ret_insn = 1'b0; - cu_if.breakpoint = 1'b0; - cu_if.ecall_insn = 1'b0; + always_comb begin + case (cu_if.opcode) + REGREG: maybe_illegal = instr_r.funct7[0]; + LUI, AUIPC, JAL, JALR, BRANCH, LOAD, STORE, IMMED, SYSTEM, MISCMEM, opcode_t'('0): + maybe_illegal = 1'b0; + default: maybe_illegal = 1'b1; + endcase + end + + assign cu_if.illegal_insn = maybe_illegal && !claimed; + assign claimed = rv32m_claim; // Add OR conditions for new extensions - if (cu_if.opcode == SYSTEM) begin - if (rv32i_system_t'(instr_i.funct3) == PRIV) begin - if (priv_insn_t'(instr_i.imm11_00) == MRET) - cu_if.ret_insn = 1'b1; - if (priv_insn_t'(instr_i.imm11_00) == EBREAK) - cu_if.breakpoint = 1'b1; - if (priv_insn_t'(instr_i.imm11_00) == ECALL) - cu_if.ecall_insn = 1'b1; - end + //Decoding of System Priv Instructions + always_comb begin + cu_if.ret_insn = 1'b0; + cu_if.breakpoint = 1'b0; + cu_if.ecall_insn = 1'b0; + cu_if.wfi = 1'b0; + + if (cu_if.opcode == SYSTEM) begin + if (rv32i_system_t'(instr_i.funct3) == PRIV) begin + if (priv_insn_t'(instr_i.imm11_00) == MRET) cu_if.ret_insn = 1'b1; + if (priv_insn_t'(instr_i.imm11_00) == EBREAK) cu_if.breakpoint = 1'b1; + if (priv_insn_t'(instr_i.imm11_00) == ECALL) cu_if.ecall_insn = 1'b1; + if (priv_insn_t'(instr_i.imm11_00) == WFI) cu_if.wfi = 1'b1; + end + end end - end - //CSR Insns - always_comb begin - cu_if.csr_swap = 1'b0; - cu_if.csr_clr = 1'b0; - cu_if.csr_set = 1'b0; - cu_if.csr_imm = 1'b0; + //CSR Insns + always_comb begin + cu_if.csr_swap = 1'b0; + cu_if.csr_clr = 1'b0; + cu_if.csr_set = 1'b0; + cu_if.csr_imm = 1'b0; - if (cu_if.opcode == SYSTEM) begin - if (rv32i_system_t'(instr_r.funct3) == CSRRW) begin - cu_if.csr_swap = 1'b1; - end else - if (rv32i_system_t'(instr_r.funct3) == CSRRS) begin - cu_if.csr_set = 1'b1; - end else if (rv32i_system_t'(instr_r.funct3) == CSRRC) begin - cu_if.csr_clr = 1'b1; - end else if (rv32i_system_t'(instr_r.funct3) == CSRRWI) begin - cu_if.csr_swap = 1'b1; - cu_if.csr_imm = 1'b1; - end else if (rv32i_system_t'(instr_r.funct3) == CSRRSI) begin - cu_if.csr_set = 1'b1; - cu_if.csr_imm = 1'b1; - end else if (rv32i_system_t'(instr_r.funct3) == CSRRCI) begin - cu_if.csr_clr = 1'b1; - cu_if.csr_imm = 1'b1; - end + if (cu_if.opcode == SYSTEM) begin + if (rv32i_system_t'(instr_r.funct3) == CSRRW) begin + cu_if.csr_swap = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRS) begin + cu_if.csr_set = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRC) begin + cu_if.csr_clr = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRWI) begin + cu_if.csr_swap = 1'b1; + cu_if.csr_imm = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRSI) begin + cu_if.csr_set = 1'b1; + cu_if.csr_imm = 1'b1; + end else if (rv32i_system_t'(instr_r.funct3) == CSRRCI) begin + cu_if.csr_clr = 1'b1; + cu_if.csr_imm = 1'b1; + end + end end - end + assign cu_if.csr_rw_valid = (cu_if.csr_swap | cu_if.csr_set | cu_if.csr_clr); - assign cu_if.csr_rw_valid = (cu_if.csr_swap | cu_if.csr_set | cu_if.csr_clr); + assign cu_if.csr_addr = csr_addr_t'(instr_i.imm11_00); + assign cu_if.zimm = cu_if.instr[19:15]; - assign cu_if.csr_addr = csr_addr_t'(instr_i.imm11_00); - assign cu_if.zimm = cu_if.instr[19:15]; + // Extension decoding + `ifdef RV32M_SUPPORTED + rv32m_decode RV32M_DECODE( + .insn(cu_if.instr), + .claim(rv32m_claim), + .rv32m_control(cu_if.rv32m_control) + ); + `else + assign cu_if.rv32m_control = {1'b0, rv32m_op_t'(0)}; + assign rv32m_claim = 1'b0; + `endif // RV32M_SUPPORTED endmodule - diff --git a/source_code/standard_core/dmem_extender.sv b/source_code/standard_core/dmem_extender.sv index e3a951bce..c9c07970c 100644 --- a/source_code/standard_core/dmem_extender.sv +++ b/source_code/standard_core/dmem_extender.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,52 +20,52 @@ * Email: jskubic@purdue.edu * Date Created: 06/16/2016 * Description: Swaps the endianess and bit slices the data based on the -* load type +* load type */ -import rv32i_types_pkg::*; module dmem_extender ( - input word_t dmem_in, - input load_t load_type, - input logic [3:0] byte_en, - output word_t ext_out + input rv32i_types_pkg::word_t dmem_in, + input rv32i_types_pkg::load_t load_type, + input logic [3:0] byte_en, + output rv32i_types_pkg::word_t ext_out ); - + import rv32i_types_pkg::*; + /* always_comb begin - casez (load_type) + casez (load_type) LB : begin casez (byte_en) - 4'b0001 : ext_out = $signed(dmem_in[7:0]); - 4'b0010 : ext_out = $signed(dmem_in[15:8]); - 4'b0100 : ext_out = $signed(dmem_in[23:16]); - 4'b1000 : ext_out = $signed(dmem_in[31:24]); + 4'b0001 : ext_out = 32'(signed'(dmem_in[7:0])); + 4'b0010 : ext_out = 32'(signed'(dmem_in[15:8])); + 4'b0100 : ext_out = 32'(signed'(dmem_in[23:16])); + 4'b1000 : ext_out = 32'(signed'(dmem_in[31:24])); default : ext_out = '0; endcase end LBU : begin casez (byte_en) - 4'b0001 : ext_out = dmem_in[7:0]; - 4'b0010 : ext_out = dmem_in[15:8]; - 4'b0100 : ext_out = dmem_in[23:16]; - 4'b1000 : ext_out = dmem_in[31:24]; + 4'b0001 : ext_out = 32'({'0,dmem_in[7:0]}); + 4'b0010 : ext_out = 32'({'0,dmem_in[15:8]}); + 4'b0100 : ext_out = 32'({'0,dmem_in[23:16]}); + 4'b1000 : ext_out = 32'({'0,dmem_in[31:24]}); default : ext_out = '0; endcase end LH : begin casez (byte_en) - 4'b0011 : ext_out = $signed(dmem_in[15:0]); - 4'b1100 : ext_out = $signed(dmem_in[31:16]); + 4'b0011 : ext_out = 32'(signed'(dmem_in[15:0])); + 4'b1100 : ext_out = 32'(signed'(dmem_in[31:16])); default : ext_out = '0; endcase end LHU : begin casez (byte_en) - 4'b0011 : ext_out = dmem_in[15:0]; - 4'b1100 : ext_out = dmem_in[31:16]; + 4'b0011 : ext_out = 32'({'0,dmem_in[15:0]}); + 4'b1100 : ext_out = 32'({'0,dmem_in[31:16]}); default : ext_out = '0; endcase end @@ -75,5 +75,53 @@ module dmem_extender ( default : ext_out = '0; endcase end +*/ + + + + + always_comb begin + casez (load_type) + LB: begin + casez (byte_en) + 4'b0001: ext_out = $signed(dmem_in[7:0]); + 4'b0010: ext_out = $signed(dmem_in[15:8]); + 4'b0100: ext_out = $signed(dmem_in[23:16]); + 4'b1000: ext_out = $signed(dmem_in[31:24]); + default: ext_out = '0; + endcase + end + + LBU: begin + casez (byte_en) + 4'b0001: ext_out = dmem_in[7:0]; + 4'b0010: ext_out = dmem_in[15:8]; + 4'b0100: ext_out = dmem_in[23:16]; + 4'b1000: ext_out = dmem_in[31:24]; + default: ext_out = '0; + endcase + end + + LH: begin + casez (byte_en) + 4'b0011: ext_out = $signed(dmem_in[15:0]); + 4'b1100: ext_out = $signed(dmem_in[31:16]); + default: ext_out = '0; + endcase + end + + LHU: begin + casez (byte_en) + 4'b0011: ext_out = dmem_in[15:0]; + 4'b1100: ext_out = dmem_in[31:16]; + default: ext_out = '0; + endcase + end + + LW: ext_out = dmem_in; + + default: ext_out = '0; + endcase + end endmodule diff --git a/source_code/standard_core/endian_swapper.sv b/source_code/standard_core/endian_swapper.sv index c4658ada9..80f0c19d4 100644 --- a/source_code/standard_core/endian_swapper.sv +++ b/source_code/standard_core/endian_swapper.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,20 +22,21 @@ * Description: Swaps the endianess of the input word */ -import rv32i_types_pkg::*; -module endian_swapper # ( - parameter N_BYTES = WORD_SIZE/8, - parameter N_BITS = N_BYTES*8 +module endian_swapper #( + parameter int N_BYTES = rv32i_types_pkg::WORD_SIZE / 8, + parameter int N_BITS = N_BYTES * 8 ) ( - input [N_BITS-1:0] word_in, - output [N_BITS-1:0] word_out + input [N_BITS-1:0] word_in, + output [N_BITS-1:0] word_out ); - generate - genvar i; - for(i=0; i < N_BYTES; i++) begin : word_assign - assign word_out[N_BITS - (8*i) - 1 : N_BITS - (8 * (i+1))] = word_in[((i+1)*8)-1:(i*8)]; - end : word_assign - endgenerate + import rv32i_types_pkg::*; + + generate + genvar i; + for (i = 0; i < N_BYTES; i++) begin : g_word_assign + assign word_out[N_BITS-(8*i)-1 : N_BITS-(8*(i+1))] = word_in[((i+1)*8)-1:(i*8)]; + end + endgenerate endmodule diff --git a/source_code/standard_core/jump_calc.sv b/source_code/standard_core/jump_calc.sv index ed957a1b5..7654aac18 100644 --- a/source_code/standard_core/jump_calc.sv +++ b/source_code/standard_core/jump_calc.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,22 +19,21 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 06/15/2016 -* Description: A simple adder for calculating branch targets +* Description: A simple adder for calculating branch targets */ `include "jump_calc_if.vh" -module jump_calc -( - jump_calc_if.jump_calc jump_if +module jump_calc ( + jump_calc_if.jump_calc jump_if ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; - word_t jump_addr; - assign jump_addr = jump_if.base + jump_if.offset; + word_t jump_addr; + assign jump_addr = jump_if.base + jump_if.offset; - assign jump_if.jal_addr = jump_addr; - assign jump_if.jalr_addr = {jump_addr[31:1], 1'b0}; + assign jump_if.jal_addr = jump_addr; + assign jump_if.jalr_addr = {jump_addr[31:1], 1'b0}; endmodule diff --git a/source_code/standard_core/memory_controller.sv b/source_code/standard_core/memory_controller.sv index cd850d0e8..eda5fc448 100644 --- a/source_code/standard_core/memory_controller.sv +++ b/source_code/standard_core/memory_controller.sv @@ -1,21 +1,21 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* +* +* * Filename: memory_controller.sv -* +* * Created by: John Skubic * Modified by: Chuan Yean Tan * Email: jskubic@purdue.edu , tan56@purdue.edu @@ -28,188 +28,238 @@ `include "component_selection_defines.vh" module memory_controller ( - input logic CLK, nRST, - generic_bus_if.generic_bus d_gen_bus_if, - generic_bus_if.generic_bus i_gen_bus_if, - generic_bus_if.cpu out_gen_bus_if + input logic CLK, + nRST, + generic_bus_if.generic_bus d_gen_bus_if, + generic_bus_if.generic_bus i_gen_bus_if, + generic_bus_if.cpu out_gen_bus_if ); - /* State Declaration */ - typedef enum { - IDLE, - INSTR_REQ , - INSTR_DATA_REQ, - INSTR_WAIT, - DATA_REQ , - DATA_INSTR_REQ , - DATA_WAIT - } state_t; - - state_t current_state, next_state; - - /* Internal Signals */ - logic [31:0] wdata, rdata; - - always_ff @ (posedge CLK, negedge nRST) - begin - if (nRST == 0) - current_state <= IDLE; - else - current_state <= next_state; - end - - /* State Transition Logic */ - always_comb - begin - case(current_state) - IDLE: begin - if(d_gen_bus_if.ren || d_gen_bus_if.wen) - next_state = DATA_REQ; - else if(i_gen_bus_if.ren) - next_state = INSTR_REQ; - else - next_state = IDLE; - end - - INSTR_REQ: begin - if(d_gen_bus_if.ren || d_gen_bus_if.wen) - next_state = INSTR_DATA_REQ; - else - next_state = INSTR_WAIT; - end - - INSTR_DATA_REQ : begin - if (out_gen_bus_if.busy == 1'b0) - next_state = DATA_WAIT; - else - next_state = INSTR_DATA_REQ; - end - - DATA_REQ: begin - if (i_gen_bus_if.ren) - next_state = DATA_INSTR_REQ; - else - next_state = DATA_WAIT; - end - - DATA_INSTR_REQ: begin - if( out_gen_bus_if.busy == 1'b0 ) - next_state = INSTR_WAIT; - else - next_state = DATA_INSTR_REQ; - end - - INSTR_WAIT: begin - if ( out_gen_bus_if.busy == 1'b0 ) begin - if (d_gen_bus_if.ren || d_gen_bus_if.wen) - next_state = DATA_REQ; - else - next_state = IDLE; - end else if (d_gen_bus_if.ren || d_gen_bus_if.wen) - next_state = INSTR_DATA_REQ; - else - next_state = INSTR_WAIT; - end - - DATA_WAIT: begin - if ( out_gen_bus_if.busy == 1'b0 ) begin - if (i_gen_bus_if.ren) - next_state = INSTR_REQ; - else - next_state = IDLE; - end else if (i_gen_bus_if.ren) - next_state = DATA_INSTR_REQ; - else - next_state = DATA_WAIT; - end - - default: next_state = IDLE; - endcase - end - - /* State Output Logic */ - always_comb - begin - case(current_state) - IDLE: begin - out_gen_bus_if.wen = 0; - out_gen_bus_if.ren = 0; - out_gen_bus_if.addr = 0; - out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; - d_gen_bus_if.busy = 1'b1; - i_gen_bus_if.busy = 1'b1; - end - - //-- INSTRUCTION REQUEST --// - INSTR_REQ: begin - out_gen_bus_if.wen = i_gen_bus_if.wen; - out_gen_bus_if.ren = i_gen_bus_if.ren; - out_gen_bus_if.addr = i_gen_bus_if.addr; - out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; - d_gen_bus_if.busy = 1'b1; - i_gen_bus_if.busy = 1'b1; - end - INSTR_DATA_REQ: begin - out_gen_bus_if.wen = d_gen_bus_if.wen; - out_gen_bus_if.ren = d_gen_bus_if.ren; - out_gen_bus_if.addr = d_gen_bus_if.addr; - out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; - i_gen_bus_if.busy = out_gen_bus_if.busy; - d_gen_bus_if.busy = 1'b1; - end - INSTR_WAIT: begin - out_gen_bus_if.wen = 0; - out_gen_bus_if.ren = 0; - out_gen_bus_if.addr = 0; - out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; - d_gen_bus_if.busy = 1'b1; - i_gen_bus_if.busy = out_gen_bus_if.busy; - end - - //-- DATA REQUEST --// - DATA_REQ: begin - out_gen_bus_if.wen = d_gen_bus_if.wen; - out_gen_bus_if.ren = d_gen_bus_if.ren; - out_gen_bus_if.addr = d_gen_bus_if.addr; - out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; - d_gen_bus_if.busy = 1'b1; - i_gen_bus_if.busy = 1'b1; - end - DATA_INSTR_REQ: begin - out_gen_bus_if.wen = i_gen_bus_if.wen; - out_gen_bus_if.ren = i_gen_bus_if.ren; - out_gen_bus_if.addr = i_gen_bus_if.addr; - out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; - d_gen_bus_if.busy = out_gen_bus_if.busy; - i_gen_bus_if.busy = 1'b1; - end - DATA_WAIT: begin - out_gen_bus_if.wen = d_gen_bus_if.wen; - out_gen_bus_if.ren = d_gen_bus_if.ren; - out_gen_bus_if.addr = d_gen_bus_if.addr; - out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; - i_gen_bus_if.busy = 1'b1; - d_gen_bus_if.busy = out_gen_bus_if.busy; - end - endcase - end - - generate - if(BUS_ENDIANNESS == "big") - begin - assign wdata = d_gen_bus_if.wdata; - assign rdata = out_gen_bus_if.rdata; - end else if (BUS_ENDIANNESS == "little") - begin - logic [31:0] little_endian_wdata, little_endian_rdata; - endian_swapper wswap(d_gen_bus_if.wdata, little_endian_wdata); - endian_swapper rswap(out_gen_bus_if.rdata, little_endian_rdata); - assign wdata = little_endian_wdata; - assign rdata = little_endian_rdata; + /* State Declaration */ + typedef enum { + IDLE, + INSTR_REQ, + INSTR_DATA_REQ, + INSTR_WAIT, + DATA_REQ, + DATA_INSTR_REQ, + DATA_WAIT, + CANCEL_REQ_WAIT + } state_t; + + state_t current_state, next_state; + + /* Internal Signals */ + logic [31:0] wdata, rdata; + + always_ff @(posedge CLK, negedge nRST) begin + if (nRST == 0) current_state <= IDLE; + else current_state <= next_state; + end + + /* State Transition Logic */ + /* + * Note: After interrupts were integrated, receiving an interrupt forces IREN + * to go low. On an instruction request, the FSM assumed IREN high, and unconditionally + * proceeded to an instruction wait state (either INSTR_WAIT or INSTR_DATA_REQ). However, + * since IREN was low, the AHB master did not actually receive a request, and therefore the + * I-Bus would never see a "ready" condition; the AHB master would be locked in IDLE, and + * this FSM would be locked in the instruction wait state forever. + * + * To fix, I added logic to abort an instruction request if the IREN signal was low in + * the INSTR_REQ or DATA_INSTR_REQ state; this only happens on an interrupt, so simply aborting + * the transaction on the next transition should be safe since the pipeline will be flushed anyways; + * the instruction request in question should not be fetched since the next instruction should be from + * the interrupt handler after the new PC is inserted. + */ + always_comb begin + case (current_state) + IDLE: begin + if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = DATA_REQ; + else if (i_gen_bus_if.ren) next_state = INSTR_REQ; + else next_state = IDLE; + end + + INSTR_REQ: begin + if (!i_gen_bus_if.ren) // Abort request, received an interrupt + next_state = IDLE; + else if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = INSTR_DATA_REQ; + else next_state = INSTR_WAIT; + end + + INSTR_DATA_REQ: begin + if (out_gen_bus_if.busy == 1'b0) next_state = DATA_WAIT; + else next_state = INSTR_DATA_REQ; + end + + DATA_REQ: begin + if (i_gen_bus_if.ren) next_state = DATA_INSTR_REQ; + else next_state = DATA_WAIT; + end + + DATA_INSTR_REQ: begin + // Abort request, received an interrupt + if(!i_gen_bus_if.ren && out_gen_bus_if.busy == 1'b0) + next_state = IDLE; + else if (out_gen_bus_if.busy == 1'b0) next_state = INSTR_WAIT; + else next_state = DATA_INSTR_REQ; + end + + INSTR_WAIT: begin + if (out_gen_bus_if.busy == 1'b0) begin + if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = DATA_REQ; + else next_state = IDLE; + end else if (d_gen_bus_if.ren || d_gen_bus_if.wen) next_state = INSTR_DATA_REQ; + else if(!i_gen_bus_if.ren) next_state = CANCEL_REQ_WAIT; + else next_state = INSTR_WAIT; + end + + DATA_WAIT: begin + if (out_gen_bus_if.busy == 1'b0) begin + if (i_gen_bus_if.ren) next_state = INSTR_REQ; + else next_state = IDLE; + end else if (i_gen_bus_if.ren) next_state = DATA_INSTR_REQ; + else next_state = DATA_WAIT; + end + + CANCEL_REQ_WAIT: begin + if(out_gen_bus_if.busy == 1'b0) begin + next_state = IDLE; + end else begin + next_state = CANCEL_REQ_WAIT; + end + end + + default: next_state = IDLE; + endcase + end + + /* State Output Logic */ + always_comb begin + case (current_state) + IDLE: begin + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; + i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; + end + + //-- INSTRUCTION REQUEST --// + // Byte enable is relevant to data phase + INSTR_REQ: begin + out_gen_bus_if.wen = i_gen_bus_if.wen; + out_gen_bus_if.ren = i_gen_bus_if.ren; + out_gen_bus_if.addr = i_gen_bus_if.addr; + out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; + i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; + end + INSTR_DATA_REQ: begin + out_gen_bus_if.wen = d_gen_bus_if.wen; + out_gen_bus_if.ren = d_gen_bus_if.ren; + out_gen_bus_if.addr = d_gen_bus_if.addr; + out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; + i_gen_bus_if.busy = out_gen_bus_if.busy; + i_gen_bus_if.error = out_gen_bus_if.error; + d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; + end + INSTR_WAIT: begin + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; + out_gen_bus_if.byte_en = i_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; + i_gen_bus_if.busy = out_gen_bus_if.busy; + i_gen_bus_if.error = out_gen_bus_if.error; + end + + //-- DATA REQUEST --// + DATA_REQ: begin + out_gen_bus_if.wen = d_gen_bus_if.wen; + out_gen_bus_if.ren = d_gen_bus_if.ren; + out_gen_bus_if.addr = d_gen_bus_if.addr; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; + i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; + end + DATA_INSTR_REQ: begin + out_gen_bus_if.wen = i_gen_bus_if.wen; + out_gen_bus_if.ren = i_gen_bus_if.ren; + out_gen_bus_if.addr = i_gen_bus_if.addr; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + d_gen_bus_if.busy = out_gen_bus_if.busy; + d_gen_bus_if.error = out_gen_bus_if.error; + i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; + end + DATA_WAIT: begin + //out_gen_bus_if.wen = d_gen_bus_if.wen; + //out_gen_bus_if.ren = d_gen_bus_if.ren; + //out_gen_bus_if.addr = d_gen_bus_if.addr; + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; + d_gen_bus_if.busy = out_gen_bus_if.busy; + d_gen_bus_if.error = out_gen_bus_if.error; + end + + CANCEL_REQ_WAIT: begin + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; + out_gen_bus_if.byte_en = 0; + i_gen_bus_if.busy = 1; + i_gen_bus_if.error = 0; + d_gen_bus_if.busy = 1; + d_gen_bus_if.error = 0; + end + + default: begin + out_gen_bus_if.wen = 0; + out_gen_bus_if.ren = 0; + out_gen_bus_if.addr = 0; + out_gen_bus_if.byte_en = d_gen_bus_if.byte_en; + d_gen_bus_if.busy = 1'b1; + d_gen_bus_if.error = 1'b0; + i_gen_bus_if.busy = 1'b1; + i_gen_bus_if.error = 1'b0; + end + endcase end - endgenerate - assign out_gen_bus_if.wdata = wdata; - assign d_gen_bus_if.rdata = rdata; - assign i_gen_bus_if.rdata = rdata; + generate + if (BUS_ENDIANNESS == "big") begin : g_mc_bus_be + assign wdata = d_gen_bus_if.wdata; + assign rdata = out_gen_bus_if.rdata; + end else if (BUS_ENDIANNESS == "little") begin : g_mc_bus_le + logic [31:0] little_endian_wdata, little_endian_rdata; + endian_swapper wswap ( + .word_in(d_gen_bus_if.wdata), + .word_out(little_endian_wdata) + ); + endian_swapper rswap ( + .word_in(out_gen_bus_if.rdata), + .word_out(little_endian_rdata) + ); + assign wdata = little_endian_wdata; + assign rdata = little_endian_rdata; + end + endgenerate + + assign out_gen_bus_if.wdata = wdata; + assign d_gen_bus_if.rdata = rdata; + assign i_gen_bus_if.rdata = rdata; endmodule diff --git a/source_code/standard_core/rv32i_reg_file.sv b/source_code/standard_core/rv32i_reg_file.sv index 4328005b2..1fea48c83 100644 --- a/source_code/standard_core/rv32i_reg_file.sv +++ b/source_code/standard_core/rv32i_reg_file.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,25 +25,26 @@ `include "rv32i_reg_file_if.vh" module rv32i_reg_file ( - input CLK, nRST, - rv32i_reg_file_if.rf rf_if + input CLK, + nRST, + rv32i_reg_file_if.rf rf_if ); - import rv32i_types_pkg::*; + import rv32i_types_pkg::*; - parameter NUM_REGS = 32; + localparam int NUM_REGS = 32; - word_t [NUM_REGS-1:0] registers; + word_t [NUM_REGS-1:0] registers; - always_ff @ (posedge CLK, negedge nRST) begin - if (~nRST) begin - registers <= '0; - end else if (rf_if.wen && rf_if.rd) begin - registers[rf_if.rd] <= rf_if.w_data; + always_ff @(posedge CLK, negedge nRST) begin + if (~nRST) begin + registers <= '0; + end else if (rf_if.wen && rf_if.rd) begin + registers[rf_if.rd] <= rf_if.w_data; + end end - end - assign rf_if.rs1_data = registers[rf_if.rs1]; - assign rf_if.rs2_data = registers[rf_if.rs2]; + assign rf_if.rs1_data = registers[rf_if.rs1]; + assign rf_if.rs2_data = registers[rf_if.rs2]; endmodule diff --git a/source_code/standard_core/standard_core.core b/source_code/standard_core/standard_core.core new file mode 100644 index 000000000..0eef09e76 --- /dev/null +++ b/source_code/standard_core/standard_core.core @@ -0,0 +1,33 @@ +CAPI=2: +name: socet:riscv:riscv_standard:0.1.0 +description: Standard core files for RISCVBusiness + +filesets: + rtl: + files: + - alu.sv + - branch_res.sv + - dmem_extender.sv + - endian_swapper.sv + - memory_controller.sv + - control_unit.sv + - jump_calc.sv + - rv32i_reg_file.sv + - RISCVBusiness.sv + - top_core.sv + file_type: systemVerilogSource + +targets: + default: &default + filesets: + - rtl + lint: + filesets: + - rtl + description: Linting + default_tool: veriblelint + toplevel: top_core + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search'] + diff --git a/source_code/standard_core/top_core.sv b/source_code/standard_core/top_core.sv new file mode 100644 index 000000000..e615789dc --- /dev/null +++ b/source_code/standard_core/top_core.sv @@ -0,0 +1,97 @@ +`include "core_interrupt_if.vh" +`include "generic_bus_if.vh" +`include "component_selection_defines.vh" + +module top_core #( + parameter logic [31:0] RESET_PC = 32'h80000000 +) ( + input CLK, + nRST, + input [63:0] mtime, + output wfi, + halt, + // generic bus if case +`ifdef BUS_INTERFACE_GENERIC_BUS + input busy, + input error, + input [31:0] rdata, + output ren, + wen, + output [3:0] byte_en, + output [31:0] addr, + wdata, + // ahb if case +`elsif BUS_INTERFACE_AHB + // TODO +`else + +`endif + // core_interrupt_if + input ext_int, + ext_int_clear, + input soft_int, + soft_int_clear, + input timer_int, + timer_int_clear +); + + + function [31:0] get_x28; + // verilator public + get_x28 = CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[28]; + endfunction + + + bind stage3_mem_stage cpu_tracker cpu_track1 ( + .CLK(CLK), + .wb_stall(wb_stall), + .instr(ex_mem_if.ex_mem_reg.instr), + .pc(ex_mem_if.ex_mem_reg.pc), + .opcode(rv32i_types_pkg::opcode_t'(ex_mem_if.ex_mem_reg.instr[6:0])), + .funct3(funct3), + .funct12(funct12), + .rs1(ex_mem_if.ex_mem_reg.instr[19:15]), + .rs2(ex_mem_if.ex_mem_reg.instr[24:20]), + .rd(ex_mem_if.ex_mem_reg.rd_m), + .imm_S(ex_mem_if.ex_mem_reg.tracker_signals.imm_S), // TODO: Extract constants. Maybe we could pass these in the pipeline and they'd be removed by synthesis? + .imm_I(ex_mem_if.ex_mem_reg.tracker_signals.imm_I), + .imm_U(ex_mem_if.ex_mem_reg.tracker_signals.imm_U), + .imm_UJ(ex_mem_if.ex_mem_reg.tracker_signals.imm_UJ), + .imm_SB(ex_mem_if.ex_mem_reg.tracker_signals.imm_SB), + .instr_30(instr_30) + ); + + + + core_interrupt_if interrupt_if (); + assign interrupt_if.ext_int = ext_int; + assign interrupt_if.ext_int_clear = ext_int_clear; + assign interrupt_if.soft_int = soft_int; + assign interrupt_if.soft_int_clear = soft_int_clear; + assign interrupt_if.timer_int = timer_int; + assign interrupt_if.timer_int_clear = timer_int_clear; + +`ifdef BUS_INTERFACE_GENERIC_BUS + generic_bus_if gen_bus_if (); + assign gen_bus_if.busy = busy; + assign gen_bus_if.rdata = rdata; + assign gen_bus_if.error = error; + assign ren = gen_bus_if.ren; + assign wen = gen_bus_if.wen; + assign byte_en = gen_bus_if.byte_en; + assign addr = gen_bus_if.addr; + assign wdata = gen_bus_if.wdata; +`elsif BUS_INTERFACE_AHB + ahb_if ahb_master (); + // TODO + +`elsif BUS_INTERFACE_APB + apb_if apb_requester (CLK, nRST); +`else + +`endif + + + RISCVBusiness #(.RESET_PC(RESET_PC)) CORE (.*); + +endmodule diff --git a/source_code/standard_core/top_core_no_memory.sv b/source_code/standard_core/top_core_no_memory.sv new file mode 100644 index 000000000..cc53ba420 --- /dev/null +++ b/source_code/standard_core/top_core_no_memory.sv @@ -0,0 +1,98 @@ +`include "core_interrupt_if.vh" +`include "generic_bus_if.vh" +`include "component_selection_defines.vh" + +module top_core #( + parameter logic [31:0] RESET_PC = 32'h80000000 +) ( + input CLK, + nRST, + output wfi, + halt, + + // I-bus + input i_busy, + input [31:0] i_rdata, + output i_ren, + output i_wen, + output [3:0] i_byte_en, + output [31:0] i_addr, + output [31:0] i_wdata, + + // D-bus + input d_busy, + input [31:0] d_rdata, + output d_ren, + output d_wen, + output [3:0] d_byte_en, + output [31:0] d_addr, + output [31:0] d_wdata, + + // core_interrupt_if + input ext_int, + ext_int_clear, + input soft_int, + soft_int_clear, + input timer_int, + timer_int_clear +); + + + function [31:0] get_x28; + // verilator public + get_x28 = CORE.pipeline.execute_stage_i.g_rfile_select.rf.registers[28]; + endfunction + + bind stage3_mem_stage cpu_tracker cpu_track1 ( + .CLK(CLK), + .wb_stall(wb_stall), + .instr(ex_mem_if.ex_mem_reg.instr), + .pc(ex_mem_if.ex_mem_reg.pc), + .opcode(rv32i_types_pkg::opcode_t'(ex_mem_if.ex_mem_reg.instr[6:0])), + .funct3(funct3), + .funct12(funct12), + .rs1(ex_mem_if.ex_mem_reg.instr[19:15]), + .rs2(ex_mem_if.ex_mem_reg.instr[24:20]), + .rd(ex_mem_if.ex_mem_reg.rd_m), + .imm_S(ex_mem_if.ex_mem_reg.tracker_signals.imm_S), // TODO: Extract constants. Maybe we could pass these in the pipeline and they'd be removed by synthesis? + .imm_I(ex_mem_if.ex_mem_reg.tracker_signals.imm_I), + .imm_U(ex_mem_if.ex_mem_reg.tracker_signals.imm_U), + .imm_UJ(ex_mem_if.ex_mem_reg.tracker_signals.imm_UJ), + .imm_SB(ex_mem_if.ex_mem_reg.tracker_signals.imm_SB), + .instr_30(instr_30) + ); + + + + core_interrupt_if interrupt_if (); + assign interrupt_if.ext_int = ext_int; + assign interrupt_if.ext_int_clear = ext_int_clear; + assign interrupt_if.soft_int = soft_int; + assign interrupt_if.soft_int_clear = soft_int_clear; + assign interrupt_if.timer_int = timer_int; + assign interrupt_if.timer_int_clear = timer_int_clear; + + + generic_bus_if igen_bus_if (); + assign igen_bus_if.busy = i_busy; + assign igen_bus_if.rdata = i_rdata; + assign i_ren = igen_bus_if.ren; + assign i_wen = igen_bus_if.wen; + assign i_byte_en = igen_bus_if.byte_en; + assign i_addr = igen_bus_if.addr; + assign i_wdata = igen_bus_if.wdata; + + generic_bus_if dgen_bus_if (); + assign dgen_bus_if.busy = d_busy; + assign dgen_bus_if.rdata = d_rdata; + assign d_ren = dgen_bus_if.ren; + assign d_wen = dgen_bus_if.wen; + assign d_byte_en = dgen_bus_if.byte_en; + assign d_addr = dgen_bus_if.addr; + assign d_wdata = dgen_bus_if.wdata; + + + + RISCVBusiness_no_memory #(.RESET_PC(RESET_PC)) CORE (.*); + +endmodule diff --git a/source_code/tb/tb_RISCVBusiness.sv b/source_code/tb/tb_RISCVBusiness.sv index 5ddc0a990..42f070513 100644 --- a/source_code/tb/tb_RISCVBusiness.sv +++ b/source_code/tb/tb_RISCVBusiness.sv @@ -49,14 +49,22 @@ module tb_RISCVBusiness (); generic_bus_if gen_bus_if(); generic_bus_if rvb_gen_bus_if(); generic_bus_if tb_gen_bus_if(); + core_interrupt_if interrupt_if(); + + assign interrupt_if.timer_int = '0; + assign interrupt_if.timer_int_clear = '0; + assign interrupt_if.ext_int = '0; + assign interrupt_if.ext_int_clear = '0; + assign interrupt_if.soft_int = '0; + assign interrupt_if.soft_int_clear = '0; //Module Instantiations RISCVBusiness DUT ( .CLK(CLK), .nRST(nRST), - .halt(halt), - .gen_bus_if(rvb_gen_bus_if) + .gen_bus_if(rvb_gen_bus_if), + .interrupt_if ); ram_wrapper ram ( @@ -145,7 +153,7 @@ module tb_RISCVBusiness (); nRST = 1; - while (halt == 0 && clk_count != `RVB_CLK_TIMEOUT) begin + while (DUT.halt == 0 && clk_count != `RVB_CLK_TIMEOUT) begin @(posedge CLK); clk_count++; if (gen_bus_if.addr == 16'h0000 & !gen_bus_if.busy & gen_bus_if.wen) diff --git a/source_code/tb/tb_RISCVBusiness_self_test.sv b/source_code/tb/tb_RISCVBusiness_self_test.sv index ef38c5e69..04588fcbd 100644 --- a/source_code/tb/tb_RISCVBusiness_self_test.sv +++ b/source_code/tb/tb_RISCVBusiness_self_test.sv @@ -1,27 +1,27 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -* -* -* Filename: tb_RISCVBusiness_self_test.sv -* +* +* +* Filename: tb_RISCVBusiness_self_test.sv +* * Created by: John Skubic * Email: jskubic@purdue.edu * Date Created: 06/01/2016 * Description: Testbench for running RISCVBusiness until a halt condition. * The test bench monitors memory location 0x1000 and prints -* the char representation of what is written. +* the char representation of what is written. */ `timescale 1ns/100ps @@ -34,9 +34,9 @@ `define RVBSELF_CLK_TIMEOUT 5000000 module tb_RISCVBusiness_self_test (); - + parameter PERIOD = 20; - + logic CLK, nRST; logic ram_control; // 1 -> CORE, 0 -> TB logic halt; @@ -50,14 +50,22 @@ module tb_RISCVBusiness_self_test (); generic_bus_if gen_bus_if(); generic_bus_if rvb_gen_bus_if(); generic_bus_if tb_gen_bus_if(); + core_interrupt_if interrupt_if(); + + assign interrupt_if.timer_int = '0; + assign interrupt_if.timer_int_clear = '0; + assign interrupt_if.ext_int = '0; + assign interrupt_if.ext_int_clear = '0; + assign interrupt_if.soft_int = '0; + assign interrupt_if.soft_int_clear = '0; //Module Instantiations RISCVBusiness DUT ( .CLK(CLK), .nRST(nRST), - .halt(halt), - .gen_bus_if(rvb_gen_bus_if) + .gen_bus_if(rvb_gen_bus_if), + .interrupt_if ); ram_wrapper ram ( @@ -72,7 +80,8 @@ module tb_RISCVBusiness_self_test (); assign data = data_temp; else ;//TODO:ERROR - bind tspp_execute_stage cpu_tracker cpu_track1 ( + /* + bind stage3_execute_stage cpu_tracker cpu_track1 ( .CLK(CLK), .wb_stall(wb_stall), .instr(fetch_ex_if.fetch_ex_reg.instr), @@ -97,7 +106,7 @@ module tb_RISCVBusiness_self_test (); .update_predictor(predict_if.update_predictor), .prediction(predict_if.prediction), .branch_result(predict_if.branch_result) - ); + );*/ //Ramif Mux always_comb begin @@ -137,13 +146,13 @@ module tb_RISCVBusiness_self_test (); nRST = 0; ram_control = 1; clk_count = 0; - + @(posedge CLK); @(posedge CLK); nRST = 1; - - while (halt == 0 && clk_count != `RVBSELF_CLK_TIMEOUT) begin + + while (DUT.halt == 0 && clk_count != `RVBSELF_CLK_TIMEOUT) begin @(posedge CLK); clk_count++; if(gen_bus_if.addr == 16'h0000 & !gen_bus_if.busy & gen_bus_if.wen) begin @@ -159,10 +168,10 @@ module tb_RISCVBusiness_self_test (); // Check Register 28 to see if test passed or failed if (clk_count == `RVBSELF_CLK_TIMEOUT) $display("ERROR: Test timed out"); - else if(DUT.pipeline.tspp_pipeline.execute_stage_i.rf.registers[28] != 32'h1) + else if(DUT.pipeline.execute_stage_i.g_rfile_select.rf.registers[28] != 32'h1) $display("ERROR: Test %0d did not pass", - (DUT.pipeline.tspp_pipeline.execute_stage_i.rf.registers[28] - 1)/2); - else + (DUT.pipeline.execute_stage_i.g_rfile_select.rf.registers[28] - 1)/2); + else $display("SUCCESS"); $finish; @@ -170,8 +179,8 @@ module tb_RISCVBusiness_self_test (); task dump_stats(); logic [63:0] instret, cycles; - instret = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.instretfull; - cycles = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.cyclefull; + instret = DUT.priv_wrapper_i.priv_block_i.csr.instret_full; + cycles = DUT.priv_wrapper_i.priv_block_i.csr.cycles_full; if (cycles != clk_count) $info("Cycles CSR (%0d) != clk_count (%0d)", cycles, clk_count); stats_ptr = $fopen(`STATS_FILE_NAME, "w"); $fwrite(stats_ptr, "Instructions retired: %2d\n", instret); @@ -200,7 +209,7 @@ module tb_RISCVBusiness_self_test (); $fwrite(fptr, ":%2h%4h00%8h%2h\n", 8'h4, addr[15:0]>>2, data, checksum); end // add the EOL entry to the file - $fwrite(fptr, ":00000001FF"); + $fwrite(fptr, ":00000001FF"); endtask @@ -221,11 +230,10 @@ module tb_RISCVBusiness_self_test (); checksum = hex_line[7:0] + hex_line[15:8] + hex_line[23:16] + hex_line[31:24] + hex_line[39:32] + hex_line[47:40] + hex_line[55:48] + hex_line[63:56]; - + //take two's complement checksum = (~checksum) + 1; return checksum; endfunction endmodule - diff --git a/source_code/tb/tb_RISCVBusiness_self_test_plic.sv b/source_code/tb/tb_RISCVBusiness_self_test_plic.sv new file mode 100644 index 000000000..34934ce5e --- /dev/null +++ b/source_code/tb/tb_RISCVBusiness_self_test_plic.sv @@ -0,0 +1,278 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_RISCVBusiness_self_test.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Testbench for running RISCVBusiness until a halt condition. +* The test bench monitors memory location 0x1000 and prints +* the char representation of what is written. +*/ + +`timescale 1ns/100ps + +`include "generic_bus_if.vh" +`include "component_selection_defines.vh" +`include "core_interrupt_if.vh" + +`define OUTPUT_FILE_NAME "cpu.hex" +`define STATS_FILE_NAME "stats.txt" +`define RVBSELF_CLK_TIMEOUT 5000000 + +module tb_RISCVBusiness_self_test_plic (); + + parameter PERIOD = 20; + + logic CLK, nRST; + logic ram_control; // 1 -> CORE, 0 -> TB + logic halt; + logic [31:0] addr, data, data_temp; + logic [63:0] hexdump_temp; + logic [7:0] checksum; + integer fptr, stats_ptr; + integer clk_count; + logic plic_ext_int; //plic_clear_ext_int_m; + + //Interface Instantiations + generic_bus_if gen_bus_if(); + generic_bus_if rvb_gen_bus_if(); + generic_bus_if tb_gen_bus_if(); + core_interrupt_if interrupt_if(); + + // additional instantiation for the priv unit to control the external interrupt signal + + //Module Instantiations + + RISCVBusiness DUT ( + .CLK(CLK), + .nRST(nRST), + //.halt(halt), + //.ahb_master(ahb_bus_if), + .gen_bus_if(rvb_gen_bus_if), + .interrupt_if + //.plic_ext_int(plic_ext_int) + //plic_clear_ext_int_m(//plic_clear_ext_int_m) + ); + + ram_wrapper ram ( + .CLK(CLK), + .nRST(nRST), + .gen_bus_if(gen_bus_if) + ); + + if (BUS_ENDIANNESS == "big") + endian_swapper swap(data_temp, data); + else if (BUS_ENDIANNESS == "little") + assign data = data_temp; + else ;//TODO:ERROR + + bind tspp_execute_stage cpu_tracker cpu_track1 ( + .CLK(CLK), + .wb_stall(wb_stall), + .instr(fetch_ex_if.fetch_ex_reg.instr), + .pc(fetch_ex_if.fetch_ex_reg.pc), + .opcode(cu_if.opcode), + .funct3(funct3), + .funct12(funct12), + .rs1(rf_if.rs1), + .rs2(rf_if.rs2), + .rd(rf_if.rd), + .imm_S(cu_if.imm_S), + .imm_I(cu_if.imm_I), + .imm_U(cu_if.imm_U), + .imm_UJ(imm_UJ_ext), + .imm_SB(cu_if.imm_SB), + .instr_30(instr_30) + ); + + bind branch_predictor_wrapper branch_tracker branch_perf( + .CLK(CLK), + .nRST(nRST), + .update_predictor(predict_if.update_predictor), + .prediction(predict_if.prediction), + .branch_result(predict_if.branch_result) + ); + + //Ramif Mux + always_comb begin + if(ram_control) begin + /* No actual bus, so directly connect ram to generic bus interface */ + gen_bus_if.addr = rvb_gen_bus_if.addr; + gen_bus_if.ren = rvb_gen_bus_if.ren; + gen_bus_if.wen = rvb_gen_bus_if.wen; + gen_bus_if.wdata = rvb_gen_bus_if.wdata; + gen_bus_if.byte_en = rvb_gen_bus_if.byte_en; //ahb_bus_if.byte_en; + end else begin + gen_bus_if.addr = tb_gen_bus_if.addr; + gen_bus_if.ren = tb_gen_bus_if.ren; + gen_bus_if.wen = tb_gen_bus_if.wen; + gen_bus_if.wdata = tb_gen_bus_if.wdata; + gen_bus_if.byte_en = tb_gen_bus_if.byte_en; + end + end + + /* No actual bus, so directly connect ram to generic bus interface */ + assign rvb_gen_bus_if.rdata = gen_bus_if.rdata; + assign rvb_gen_bus_if.busy = gen_bus_if.busy; + assign tb_gen_bus_if.rdata = gen_bus_if.rdata; + assign tb_gen_bus_if.busy = gen_bus_if.busy; + + + //Clock generation + initial begin : INIT + CLK = 0; + end : INIT + + always begin : CLOCK_GEN + #(PERIOD/2) CLK = ~CLK; + end : CLOCK_GEN + + //Setup core and let it run + initial begin : CORE_RUN + nRST = 0; + ram_control = 1; + clk_count = 0; + + @(posedge CLK); + @(posedge CLK); + + nRST = 1; + interrupt_if.ext_int = 1'b0; + interrupt_if.soft_int = '0; + interrupt_if.timer_int = '0; + interrupt_if.ext_int_clear = '0; + interrupt_if.soft_int_clear = '0; + interrupt_if.timer_int_clear = '0; + + // Give start time, then trigger external interrupt + #(PERIOD * 200); + interrupt_if.ext_int = 1'b1; + #(PERIOD); + interrupt_if.ext_int = 1'b0; + #(PERIOD * 2); + interrupt_if.ext_int_clear = 1'b1; + #(PERIOD); + interrupt_if.ext_int_clear = 1'b0; + + // Trigger software interrupt + #(PERIOD * 20); + interrupt_if.soft_int = 1'b1; + #(PERIOD); + interrupt_if.soft_int = 1'b0; + #(PERIOD * 2); + interrupt_if.soft_int_clear = 1'b1; + #(PERIOD); + interrupt_if.soft_int_clear = 1'b0; + + // Trigger timer interrupt + #(PERIOD * 20); + interrupt_if.timer_int = 1'b1; + #(PERIOD); + interrupt_if.timer_int = 1'b0; + #(PERIOD * 2); + interrupt_if.timer_int_clear = 1'b1; + #(PERIOD); + interrupt_if.timer_int_clear = 1'b0; + + while (DUT.halt == 0 && clk_count != `RVBSELF_CLK_TIMEOUT) begin + @(posedge CLK); + clk_count++; + if(gen_bus_if.addr == 16'h0000 & !gen_bus_if.busy & gen_bus_if.wen) begin + $write("%c", gen_bus_if.wdata[31:24]); + end + // TODO: Check clock count + end + + #(1); + + dump_stats(); + dump_ram(); + + // Check Register 28 to see if test passed or failed + if (clk_count == `RVBSELF_CLK_TIMEOUT) + $display("ERROR: Test timed out"); + else if(DUT.execute_stage_i.rf.registers[28] != 32'h1) + $display("ERROR: Test %0d did not pass", + (DUT.execute_stage_i.rf.registers[28] - 1)/2); + else + $display("SUCCESS"); + $finish; + + end : CORE_RUN + + task dump_stats(); + logic [63:0] instret, cycles; + instret = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.instretfull; + cycles = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.cyclefull; + if (cycles != clk_count) $info("Cycles CSR (%0d) != clk_count (%0d)", cycles, clk_count); + stats_ptr = $fopen(`STATS_FILE_NAME, "w"); + $fwrite(stats_ptr, "Instructions retired: %2d\n", instret); + $fwrite(stats_ptr, "Cycles taken: %2d\n", cycles); + $fwrite(stats_ptr, "CPI: %5f\n", real'(cycles)/instret); + $fwrite(stats_ptr, "IPC: %5f\n", real'(instret)/cycles); + $fclose(stats_ptr); + endtask + + task dump_ram (); + ram_control = 0; + tb_gen_bus_if.addr = 0; + tb_gen_bus_if.ren = 0; + tb_gen_bus_if.wen = 0; + tb_gen_bus_if.wdata = 0; + tb_gen_bus_if.byte_en = 4'hf; + + fptr = $fopen(`OUTPUT_FILE_NAME, "w"); + + for(addr = 32'h100; addr < 32'h2000; addr+=4) begin + read_ram(addr, data_temp); + #(PERIOD/4); + hexdump_temp = {8'h04, addr[15:0]>>2, 8'h00, data}; + checksum = calculate_crc(hexdump_temp); + if(data != 0) + $fwrite(fptr, ":%2h%4h00%8h%2h\n", 8'h4, addr[15:0]>>2, data, checksum); + end + // add the EOL entry to the file + $fwrite(fptr, ":00000001FF"); + + endtask + + task read_ram (input logic [31:0] raddr, output logic [31:0] rdata); + @(posedge CLK); + tb_gen_bus_if.addr = raddr; + tb_gen_bus_if.ren = 1; + @(posedge CLK); + while(tb_gen_bus_if.busy == 1) @(posedge CLK); + rdata = tb_gen_bus_if.rdata; + tb_gen_bus_if.ren = 0; + endtask + + function [7:0] calculate_crc (logic [63:0] hex_line); + static logic [7:0] checksum = 0; + int i; + + checksum = hex_line[7:0] + hex_line[15:8] + hex_line[23:16] + + hex_line[31:24] + hex_line[39:32] + hex_line[47:40] + + hex_line[55:48] + hex_line[63:56]; + + //take two's complement + checksum = (~checksum) + 1; + return checksum; + endfunction + +endmodule + diff --git a/source_code/tb/tb_div.sv b/source_code/tb/tb_div.sv new file mode 100644 index 000000000..1db20c78e --- /dev/null +++ b/source_code/tb/tb_div.sv @@ -0,0 +1,83 @@ +module tb_div (); + + parameter PERIOD = 20; + + /* Signal Instantiations */ + logic CLK, nRST; + logic [31:0] divisor; + logic [31:0] dividend; + logic is_signed; + logic start, finished; + logic [31:0] remainder, quotient; + logic [31:0] test_num; + + /* Module Instantiations */ + shift_test_restore_divider #(32) DUT (.*); + + /* CLK generation */ + + initial begin + CLK = 0; + end + + always begin + CLK = ~CLK; + #(PERIOD/2); + end + + + task test_div( + input logic [31:0] a, + input logic [31:0] b, + input logic is_signed_t, + input logic [31:0] exp_quot,exp_rem +); +divisor = a; +dividend = b; +is_signed = is_signed_t; +start = 1; +@(posedge CLK); +@(posedge CLK); +#(9) +start=0; +while(finished==0) +@(posedge CLK); + +assert (quotient == exp_quot) else $error ("Division failed for test %0d: Expected %h Received %h\n",test_num, exp_quot, quotient); + +assert (remainder == exp_rem) else $error ("Remainder failed for test %0d: Expected %h Received %h\n",test_num, exp_rem, remainder); + +test_num++; +endtask + + +initial begin +// reset +test_num = 0; +nRST = 1'b0; +divisor = 0; +dividend = 0; +is_signed = 0; +start = 0; +@(posedge CLK); +@(posedge CLK); +#(PERIOD/2); +nRST = 1'b1; +@(posedge CLK); +@(posedge CLK); +@(posedge CLK); + +// 80/2 unsigned = 40 .... 0 +test_div(32'd8,32'd20,1'd0,32'd2, 32'h4); + +test_div(32'h7,32'hFFFFFFF7,1'h1,32'hFFFFFFFE, 32'h2);//-9 / 7 = -1...-2 + + +$finish; +end +endmodule + + + + + diff --git a/source_code/tb/tb_memory_controller.sv b/source_code/tb/tb_memory_controller.sv index 563a97a1d..9377a48c5 100644 --- a/source_code/tb/tb_memory_controller.sv +++ b/source_code/tb/tb_memory_controller.sv @@ -39,7 +39,7 @@ module memory_controller_tb (); ahb_if ahb_m(); memory_controller DUT ( CLK, nRST, d_gen_bus_if, i_gen_bus_if, out_gen_bus_if ); - ahb_master DUT2 ( CLK, nRST, ahb_m, out_gen_bus_if); + ahb DUT2 ( CLK, nRST, ahb_m, out_gen_bus_if); //-- CLOCK INITIALIZATION --// initial begin : INIT @@ -98,8 +98,9 @@ module memory_controller_tb (); i_gen_bus_if.wen = 0; i_gen_bus_if.addr = i_gen_bus_if.addr + 4; #(5 * PERIOD) + @(posedge CLK) ahb_m.HRDATA = 32'hDEADBEEF; - ahb_m.HWDATA = 0; + ahb_m.HWDATA = 0; ahb_m.HREADY = 1; #(PERIOD) ahb_m.HRDATA = 32'hbad1bad1; @@ -121,7 +122,7 @@ module memory_controller_tb (); #(5 * PERIOD) ahb_m.HRDATA = 32'hDEADBEEF; ahb_m.HWDATA = 0; - ahb_m.HREADY = 1; + ahb_m.HREADY = 0; #(PERIOD) ahb_m.HRDATA = 32'hDEADBEEF; ahb_m.HREADY = 0; @@ -132,7 +133,7 @@ module memory_controller_tb (); #(5 * PERIOD) ahb_m.HRDATA = 32'hbad1bad1; ahb_m.HWDATA = 0; - ahb_m.HREADY = 1; + ahb_m.HREADY = 0; #(PERIOD) ahb_m.HRDATA = 32'hbad1bad1; ahb_m.HREADY = 0; @@ -156,7 +157,7 @@ module memory_controller_tb (); ahb_m.HREADY = 1; #(PERIOD) ahb_m.HRDATA = 32'hDEADBEEF; - ahb_m.HREADY = 0; + ahb_m.HREADY = 1; d_gen_bus_if.ren = 0; d_gen_bus_if.wen = 1; i_gen_bus_if.ren = 1; @@ -167,7 +168,7 @@ module memory_controller_tb (); ahb_m.HREADY = 1; #(PERIOD) ahb_m.HRDATA = 32'hbad1bad1; - ahb_m.HREADY = 0; + ahb_m.HREADY = 1; d_gen_bus_if.ren = 0; d_gen_bus_if.wen = 0; i_gen_bus_if.ren = 1; diff --git a/source_code/tb/tb_pp_mul32.sv b/source_code/tb/tb_pp_mul32.sv new file mode 100644 index 000000000..4aff3a1b1 --- /dev/null +++ b/source_code/tb/tb_pp_mul32.sv @@ -0,0 +1,117 @@ +`timescale 1ns/10ps +module tb_pp_mul32 (); + parameter BIT_WIDTH = 32; + parameter CLOCK_PERIOD = 10ns; + logic tb_CLK, tb_nRST; + logic tb_start, tb_finished; + logic [(BIT_WIDTH-1):0] tb_multiplicand; + logic [(BIT_WIDTH-1):0] tb_multiplier; + logic [(2*BIT_WIDTH-1):0] tb_product; + logic [1:0] tb_is_signed; + logic [(2*BIT_WIDTH+5):0] tb_expected_out; + integer tb_test_case_num; + typedef struct { + string test_name; + logic [(BIT_WIDTH-1):0] test_multiplicand; + logic [(BIT_WIDTH-1):0] test_multiplier; + logic [1:0] test_is_signed; + } testvector; + testvector tb_test_case []; + + pp_mul32 DUT (.CLK(tb_CLK), .nRST(tb_nRST), .multiplicand(tb_multiplicand), .multiplier(tb_multiplier), .is_signed(tb_is_signed), .start(tb_start), .finished(tb_finished), .product(tb_product)); + + always begin + tb_CLK=0; + #(CLOCK_PERIOD/2.0); + tb_CLK=1; + #(CLOCK_PERIOD/2.0); + end + + task reset_dut(); + @(negedge tb_CLK); + tb_nRST = 0; + @(posedge tb_CLK); + @(posedge tb_CLK); + #(CLOCK_PERIOD/4.0); + tb_nRST = 1; + endtask + + initial begin + tb_test_case = new[9]; + // Random multiplier and multiplicand + tb_test_case[0].test_name = "Random multiplier and multiplicand"; + tb_test_case[0].test_multiplicand = 32'd183978223; + tb_test_case[0].test_multiplier = 32'd490177653; + tb_test_case[0].test_is_signed = 2'b00; + // Multiplier with concatenation of all possible 3-bits values in bitpair recoding + tb_test_case[1].test_name = "Multiplier with concatenation of all possible 3-bits values in bitpair recoding"; + tb_test_case[1].test_multiplicand = 32'd478013; + tb_test_case[1].test_multiplier = {{10'd0}, {22'b1110100110011100100100}}; + tb_test_case[1].test_is_signed = 2'b00; + // Unsigned multiplicand and unsigned multiplier + tb_test_case[2].test_name = "Unsigned multiplicand and unsigned multiplier"; + tb_test_case[2].test_multiplicand = '1 >> 1; + tb_test_case[2].test_multiplier = '1 >> 1; + tb_test_case[2].test_is_signed = 2'b00; + // Signed multiplicand and unsigned multiplier + tb_test_case[3].test_name = "Signed multiplicand and unsigned multiplier"; + tb_test_case[3].test_multiplicand = -28752; + tb_test_case[3].test_multiplier = 32'd839011; + tb_test_case[3].test_is_signed = 2'b10; + // Unsigned multiplicand and signed multiplier + tb_test_case[4].test_name = "Unsigned multiplicand and signed multiplier"; + tb_test_case[4].test_multiplicand = 32'd7212691; + tb_test_case[4].test_multiplier = -43892; + tb_test_case[4].test_is_signed = 2'b01; + // Signed multiplicand and signed multiplier + tb_test_case[5].test_name = "Signed multiplicand and signed multiplier"; + tb_test_case[5].test_multiplicand = -7268; + tb_test_case[5].test_multiplier = -897192; + tb_test_case[5].test_is_signed = 2'b11; + // MSB is 1 but unsigned - Both + tb_test_case[6].test_name = "MSB is 1 but unsigned - Both"; + tb_test_case[6].test_multiplicand = '1; + tb_test_case[6].test_multiplier = '1; + tb_test_case[6].test_is_signed = 2'b00; + // MSB is 1 but unsigned - Multiplicand + tb_test_case[7].test_name = "MSB is 1 but unsigned - Multiplicand"; + tb_test_case[7].test_multiplicand = '1; + tb_test_case[7].test_multiplier = 32'd59; + tb_test_case[7].test_is_signed = 2'b00; + // MSB is 1 but unsigned - Multiplier + tb_test_case[8].test_name = "MSB is 1 but unsigned - Multiplier"; + tb_test_case[8].test_multiplicand = 32'd38013; + tb_test_case[8].test_multiplier = '1; + tb_test_case[8].test_is_signed = 2'b00; + end + initial begin + tb_multiplier = '0; + tb_multiplicand = '0; + tb_is_signed = '0; + tb_start = 0; + tb_nRST = 1; + for (tb_test_case_num = 0; tb_test_case_num < tb_test_case.size(); tb_test_case_num ++) begin + $display("TEST CASE %d - %s", tb_test_case_num, tb_test_case[tb_test_case_num].test_name); + reset_dut(); + @(posedge tb_CLK); + #(CLOCK_PERIOD/4.0); + tb_multiplicand = tb_test_case[tb_test_case_num].test_multiplicand; + tb_multiplier = tb_test_case[tb_test_case_num].test_multiplier; + tb_is_signed = tb_test_case[tb_test_case_num].test_is_signed; + @(negedge tb_CLK); + tb_start = 1; + @(negedge tb_CLK); // First Clock Cycle + tb_start = 0; + tb_expected_out = tb_multiplicand * tb_multiplier; + @(posedge tb_CLK); // Second Clock Cycle + @(posedge tb_CLK); // Third Clock Cycle + #(CLOCK_PERIOD/4.0); + assert (tb_product == tb_expected_out) + $info ("CORRECT MULTIPLICATION"); + else + $error ("ACTUAL: %d, EXPECTED: %d", tb_product, tb_expected_out); // see waveform value for test case 3-5 (waveform will show negative value) + end + $finish; + end + +endmodule diff --git a/source_code/tb/tb_priv_1_11_block.sv b/source_code/tb/tb_priv_1_11_block.sv new file mode 100644 index 000000000..cf8641d0b --- /dev/null +++ b/source_code/tb/tb_priv_1_11_block.sv @@ -0,0 +1,201 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_RISCVBusiness.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Testbench for running RISCVBusiness until a halt condition. +* A hexdump of memory will occur after the halt condition. +*/ + +// Note: Figure out whether or not I need to change the name to Enes Shaltami. + +`timescale 1ns/100ps + +`include "prv_pipeline_if.vh" +`include "priv_1_11_internal_if.vh" + +`define OUTPUT_FILE_NAME "cpu.hex" +`define STATS_FILE_NAME "stats.txt" +`define RVB_CLK_TIMEOUT 10000 + +module tb_priv_1_11_block (); + + parameter PERIOD = 20; + + logic CLK, nRST; + logic ram_control; // 1 -> CORE, 0 -> TB + logic halt; + logic [31:0] addr, data_temp, data; + logic [63:0] hexdump_temp; + logic [7:0] checksum; + integer fptr, stats_ptr; + //integer clk_count; + + //Interface Instantiations + prv_pipeline_if prv_pipeline_if(); + + //Module Instantiations + + priv_1_11_block DUT ( + .CLK(CLK), + .nRST(nRST), + .prv_pipe_if(prv_pipeline_if) // using the "priv_block" modport of the prv_pieline_if.vh file + ); // TODO: Figure out IO for the priv pipeline unit + + //Clock generation + initial begin : INIT + CLK = 0; + end : INIT + + always begin : CLOCK_GEN + #(PERIOD/2) CLK = ~CLK; + end : CLOCK_GEN + + //Setup core and let it run + initial begin : CORE_RUN + nRST = 0; + + // TODO: Expected output signals: ex_src, exception, + + +/* + // Resetting all of the inputs for the priv_block unit + prv_pipeline_if.pipe_clear = '0; + prv_pipeline_if.ret = '0; + prv_pipeline_if.epc = '0; + + // control signals for the exception combinational logic + prv_pipeline_if.fault_insn = 1'b0; + prv_pipeline_if.mal_insn = 1'b0; + prv_pipeline_if.illegal_insn = 1'b0; + prv_pipeline_if.fault_l = 1'b0; + prv_pipeline_if.mal_l = 1'b0; + prv_pipeline_if.fault_s = 1'b0; + prv_pipeline_if.mal_s = 1'b0; + prv_pipeline_if.breakpoint = 1'b0; + prv_pipeline_if.env_m = 1'b0; + + + prv_pipeline_if.badaddr = '0; // TODO: Below signal is not being used!!! + prv_pipeline_if.wdata = '0; + prv_pipeline_if.addr = '0; + prv_pipeline_if.valid_write = '0; + prv_pipeline_if.wb_enable = '0; + prv_pipeline_if.instr = '0; + + // TODO: SIGNALS ARE NOT BEING USED!!! Below 3 signals will check the funct3 op code for an R-type instruction. Output of control unit, but not used in the priv unit + prv_pipeline_if.swap = '0; // CSRRW field which means to atomically swap values in the CSRs and integer registers + prv_pipeline_if.clr = '0; // CSRRC field which means to perform an atomic read and clear the bit in CSR + prv_pipeline_if.set = '0; // CSRRS where the instruction reads the value of the CSR, and writes it to integer register rd + + prv_pipeline_if.ex_rmgmt = '0; + prv_pipeline_if.ex_rmgmt_cause = '0; + + + + + prv_pipeline_if.mal_insn = 1'b1; + prv_pipeline_if.breakpoint = 1'b1; + // tb_expected_ex_src = BREAKPOINT; + tb_expected_exception = 1'b1; */ + + @(posedge CLK); + @(posedge CLK); + + nRST = 1; + + /*while (halt == 0 && clk_count != `RVB_CLK_TIMEOUT) begin + @(posedge CLK); + clk_count++; + if (gen_bus_if.addr == 16'h0000 & !gen_bus_if.busy & gen_bus_if.wen) + $write("%c",gen_bus_if.wdata[31:24]); + end + + #(1); + + dump_stats(); + dump_ram(); + + if (clk_count == `RVB_CLK_TIMEOUT) + $display("ERROR: Test timed out"); */ + + $finish; + + end : CORE_RUN + + /* task dump_stats(); + integer instret, cycles; + instret = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.instretfull; + cycles = DUT.priv_wrapper_i.priv_block_i.csr_rfile_i.cyclefull; + if (cycles != clk_count) $info("Cycles CSR != clk_count"); + stats_ptr = $fopen(`STATS_FILE_NAME, "w"); + $fwrite(stats_ptr, "Instructions retired: %2d\n", instret); + $fwrite(stats_ptr, "Cycles taken: %2d\n", cycles); + $fwrite(stats_ptr, "CPI: %5f\n", real'(cycles)/instret); + $fwrite(stats_ptr, "IPC: %5f\n", real'(instret)/cycles); + $fclose(stats_ptr); + endtask + + task dump_ram (); + ram_control = 0; + tb_gen_bus_if.addr = 0; + tb_gen_bus_if.ren = 0; + tb_gen_bus_if.wen = 0; + tb_gen_bus_if.wdata = 0; + tb_gen_bus_if.byte_en = 4'hf; + + fptr = $fopen(`OUTPUT_FILE_NAME, "w"); + + for(addr = 32'h80000000; addr < 32'h80007000; addr+=4) begin + read_ram(addr, data_temp); + #(PERIOD/4); + hexdump_temp = {8'h04, addr[15:0]>>2, 8'h00, data}; + checksum = calculate_crc(hexdump_temp); + if(data != 0) + $fwrite(fptr, ":%2h%4h00%8h%2h\n", 8'h4, addr[15:0]>>2, data, checksum); + end + // add the EOL entry to the file + $fwrite(fptr, ":00000001FF"); + + endtask + + task read_ram (input logic [31:0] raddr, output logic [31:0] rdata); + @(posedge CLK); + tb_gen_bus_if.addr = raddr; + tb_gen_bus_if.ren = 1; + @(posedge CLK); + while(tb_gen_bus_if.busy == 1) @(posedge CLK); + rdata = tb_gen_bus_if.rdata; + tb_gen_bus_if.ren = 0; + endtask + + function [7:0] calculate_crc (logic [63:0] hex_line); + static logic [7:0] checksum = 0; + int i; + + checksum = hex_line[7:0] + hex_line[15:8] + hex_line[23:16] + + hex_line[31:24] + hex_line[39:32] + hex_line[47:40] + + hex_line[55:48] + hex_line[63:56]; + + //take two's complement + checksum = (~checksum) + 1; + return checksum; + endfunction */ + +endmodule diff --git a/source_code/tb/tb_priv_1_11_control.sv b/source_code/tb/tb_priv_1_11_control.sv new file mode 100644 index 000000000..69a93e1ff --- /dev/null +++ b/source_code/tb/tb_priv_1_11_control.sv @@ -0,0 +1,198 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_RISCVBusiness.sv +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Testbench for running RISCVBusiness until a halt condition. +* A hexdump of memory will occur after the halt condition. +*/ + +// Note: Figure out whether or not I need to change the name to Enes Shaltami. + +`timescale 1ns/100ps + +`include "priv_1_11_internal_if.vh" +`include "component_selection_defines.vh" + +`define OUTPUT_FILE_NAME "cpu.hex" +`define STATS_FILE_NAME "stats.txt" +`define RVB_CLK_TIMEOUT 10000 + +module tb_priv_1_11_control (); + + parameter PERIOD = 20; + import rv32i_types_pkg::*; + import machine_mode_types_1_11_pkg::*; + + logic CLK, nRST; + logic ram_control; // 1 -> CORE, 0 -> TB + logic halt; + logic [31:0] addr, data_temp, data; + logic [63:0] hexdump_temp; + logic [7:0] checksum; + //integer fpt output mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup,r, stats_ptr; + + + // Expected Outputs + logic tb_expected_mip_rup, tb_expected_mtval_rup, tb_expected_mcause_rup, tb_expected_mepc_rup, tb_expected_mstatus_rup, tb_expected_intr; + + mip_t tb_expected_mip_next; + mcause_t tb_expected_mcause_next; + mepc_t tb_expected_mepc_next; + mstatus_t tb_expected_mstatus_next; + mtval_t tb_expected_mtval_next; + + + //Interface Instantiations + priv_1_11_internal_if prv_internal_if(); + + //Module Instantiations + + priv_1_11_control DUT ( + .CLK(CLK), + .nRST(nRST), + .prv_intern_if(prv_internal_if) + ); // The modport is prv_control under the priv_1_11_internal_if.vh file + + + task reset_dut(); + nRST = 1'b0; + @(posedge CLK); + @(posedge CLK); + + nRST = 1'b1; + +/* Commenting out because package change made some signals unusable. Can be deleted later + // Resetting all of the inputs for the priv_block unit + prv_internal_if.pipe_clear = '0; + prv_internal_if.ret = '0; + prv_internal_if.epc = '0; + + // control signals for the exception combinational logic + prv_internal_if.fault_insn = 1'b0; + prv_internal_if.mal_insn = 1'b0; + prv_internal_if.illegal_insn = 1'b0; + prv_internal_if.fault_l = 1'b0; + prv_internal_if.mal_l = 1'b0; + prv_internal_if.fault_s = 1'b0; + prv_internal_if.mal_s = 1'b0; + prv_internal_if.breakpoint = 1'b0; + prv_internal_if.env_m = 1'b0; + + prv_internal_if.mepc = mepc_t'(32'h0); + prv_internal_if.mie = mie_t'(32'h0); // defined as a struct + prv_internal_if.mip = mip_t'(32'h0); // defined as a struct + prv_internal_if.mcause = mcause_t'(32'h0); + prv_internal_if.mstatus = mstatus_t'(32'h0); + prv_internal_if.clear_timer_int = 1'b0; + prv_internal_if.timer_int = 1'b0; + prv_internal_if.soft_int = 1'b0; + prv_internal_if.ext_int = 1'b0; + prv_internal_if.mtval = word_t'(32'h0); + + prv_internal_if.ex_rmgmt = 1'b0; + prv_internal_if.ex_rmgmt_cause = '0; + + // Expected Outputs + tb_expected_mcause_next.cause = ex_code_t'(31'h0); // INSN_MAL value + tb_expected_mcause_next.interrupt = 1'b0; // the source cannot be an interrupt + tb_expected_mip_rup = 1'b0; + tb_expected_mtval_rup = 1'b0; + tb_expected_mcause_rup = 1'b0; // neither an exception nor an interrupt occurred + tb_expected_mepc_rup = 1'b0; + tb_expected_mstatus_rup = 1'b0; + tb_expected_mip_next.mtip = 1'b0; + tb_expected_mip_next.msip = 1'b0; + tb_expected_mepc_next = 1'b0; // takes on value of epc + tb_expected_mstatus_next.ie = 1'b0; + tb_expected_mtval_next = 1'b0; + tb_expected_intr = 1'b0; */ + + endtask + + //Clock generation + initial begin : INIT + CLK = 0; + end : INIT + + always begin : CLOCK_GEN + #(PERIOD/2) CLK = ~CLK; + end : CLOCK_GEN + + //Setup core and let it run + initial begin : CORE_RUN + reset_dut(); + + // TODO: Expected output signals: mip_rup, mtval_rup, mcause_rup, mepc_rup, mstatus_rup, mip_next, mcause_next, mepc_next, mstatus_next, mtval_next, intr + + // Test Case #1: Check mal instruction Exception + prv_internal_if.mal_insn = 1'b1; + prv_internal_if.breakpoint = 1'b1; // this will trigger an exception + tb_expected_mcause_next.cause = ex_code_t'(31'h0); // INSN_MAL value + tb_expected_mcause_next.interrupt = 1'b0; // the source cannot be an interrupt + tb_expected_intr = 1'b1; + tb_expected_mcause_rup = 1'b1; + tb_expected_mepc_rup = 1'b1; + tb_expected_mstatus_rup = 1'b1; + tb_expected_mtval_rup = 1'b1; + + + + prv_internal_if.fault_insn = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.illegal_insn = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.fault_l = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.fault_s = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.mal_s = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.mal_insn = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.ext_int = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.soft_int = 1'b1; + reset_dut(); + #(PERIOD * 2); + prv_internal_if.timer_int = 1'b1; + + + + @(posedge CLK); + @(posedge CLK); + + nRST = 1; + + + + $finish; + + end : CORE_RUN + + + +endmodule diff --git a/source_code/tb/tb_priv_1_12_block.sv b/source_code/tb/tb_priv_1_12_block.sv new file mode 100644 index 000000000..f56ec56a8 --- /dev/null +++ b/source_code/tb/tb_priv_1_12_block.sv @@ -0,0 +1,303 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_priv_1_12_block.sv +* +* Created by: Hadi Ahmed +* Email: ahmed138@purdue.edu +* Date Created: 04/02/2022 +* Description: Testbench for running the privileged unit v1.12. +*/ + +`timescale 1ns/1ns + +`include "rv32i_types_pkg.sv" +`include "machine_mode_types_1_12_pkg.sv" +`include "prv_pipeline_if.vh" +`include "priv_1_12_internal_if.vh" +`include "core_interrupt_if.vh" + +`define OUTPUT_FILE_NAME "cpu.hex" +`define STATS_FILE_NAME "stats.txt" +`define RVB_CLK_TIMEOUT 10000 + +module tb_priv_1_12_block (); + + parameter PERIOD = 10; + + localparam PROP_DELAY = 1ns; + + logic CLK, nRST; + logic ram_control; // 1 -> CORE, 0 -> TB + logic halt; + logic [31:0] addr, data_temp, data; + logic [63:0] hexdump_temp; + logic [7:0] checksum; + integer fptr, stats_ptr; + + integer test_num; + + //Interface Instantiations + prv_pipeline_if prv_pipeline_if(); + core_interrupt_if core_int_if(); + + // Package Instantiations + import machine_mode_types_1_12_pkg::*; + + //Module Instantiations + priv_1_12_block DUT ( + .CLK(CLK), + .nRST(nRST), + .prv_pipe_if(prv_pipeline_if), + .interrupt_if(core_int_if) + ); + + //Clock generation + initial begin : INIT + CLK = 0; + end : INIT + + always begin : CLOCK_GEN + #(PERIOD/2) CLK = ~CLK; + end : CLOCK_GEN + + //Setup core and let it run + initial begin : CORE_RUN + $display("\n"); + $display("==== Starting tests"); + + nRST = 0; + test_num = 0; + + prv_pipeline_if.pipe_clear = '0; + prv_pipeline_if.ret = '0; + prv_pipeline_if.epc = '0; + prv_pipeline_if.fault_insn = '0; + prv_pipeline_if.mal_insn = '0; + prv_pipeline_if.illegal_insn = '0; + prv_pipeline_if.fault_l = '0; + prv_pipeline_if.mal_l = '0; + prv_pipeline_if.fault_s = '0; + prv_pipeline_if.mal_s = '0; + prv_pipeline_if.breakpoint = '0; + prv_pipeline_if.env_m = '0; + prv_pipeline_if.badaddr = '0; + prv_pipeline_if.swap = '0; + prv_pipeline_if.clr = '0; + prv_pipeline_if.set = '0; + prv_pipeline_if.wdata = '0; + prv_pipeline_if.csr_addr = MISA_ADDR; + prv_pipeline_if.valid_write = '0; + prv_pipeline_if.wb_enable = '0; + prv_pipeline_if.instr = '0; + prv_pipeline_if.ex_rmgmt = '0; + prv_pipeline_if.ex_rmgmt_cause = '0; + + @(posedge CLK); + @(posedge CLK); + nRST = 1; + + $display("=== DUT reset"); + + @(posedge CLK); + #PROP_DELAY; + + $display("== CSR cases"); + // **************** + // Test Case 0: Read mvendorid + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MVENDORID_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 1: Read marchid + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MARCHID_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 2: Read mimpid + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MIMPID_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 3: Read mhartid + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MHARTID_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 4: Read mconfigptr + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.csr_addr = MCONFIGPTR_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'b0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'b0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 5: Emulate CSRRW to mstatus + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.wdata = 32'h1088; + prv_pipeline_if.csr_addr = MSTATUS_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'h201800) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'h201800); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 6: Emulate CSRRW to mstatus pt2 + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.wdata = 32'h00001088; + prv_pipeline_if.csr_addr = MSTATUS_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'h88) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'h88); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 7: Emulate CSRRW to mscratch + // *************** + prv_pipeline_if.swap = 1'b1; + prv_pipeline_if.wdata = 32'hdeadbeef; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'h0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'h0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 8: Emulate CSRRS to mscratch + // *************** + prv_pipeline_if.swap = 1'b0; + prv_pipeline_if.set = 1'b1; + prv_pipeline_if.wdata = 32'h21524110; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'hdeadbeef) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'hdeadbeef); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 9: Emulate CSRRS to mscratch pt2 + // *************** + prv_pipeline_if.set = 1'b1; + prv_pipeline_if.wdata = 32'h21524110; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'hffffffff) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'hffffffff); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 10: Emulate CSRRC to mscratch + // *************** + prv_pipeline_if.set = 1'b0; + prv_pipeline_if.clr = 1'b1; + prv_pipeline_if.wdata = 32'hffffffff; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'hffffffff) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'hffffffff); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + // **************** + // Test Case 11: Emulate CSRRC to mscratch pt2 + // *************** + prv_pipeline_if.clr = 1'b1; + prv_pipeline_if.wdata = 32'h0; + prv_pipeline_if.csr_addr = MSCRATCH_ADDR; + #PROP_DELAY; + if (prv_pipeline_if.rdata == 32'h0) + $display("> Test %d: PASS", test_num); + else + $display("> Test %d: FAIL (got %h) (expected %h)", test_num, prv_pipeline_if.rdata, 32'h0); + @(posedge CLK); + #PROP_DELAY; + test_num++; + + + $display("==== Finishing tests"); + $display("\n"); + $finish; + + end : CORE_RUN + +endmodule diff --git a/source_code/tb/tb_radix4_divider.sv b/source_code/tb/tb_radix4_divider.sv new file mode 100644 index 000000000..c3dcb374c --- /dev/null +++ b/source_code/tb/tb_radix4_divider.sv @@ -0,0 +1,105 @@ +`timescale 1ns/10ps +module tb_radix4_divider (); + + parameter PERIOD = 10; + + /* Signal Instantiations */ + logic CLK, nRST; + logic [31:0] divisor; + logic [31:0] dividend; + logic is_signed; + logic start, finished; + logic [31:0] remainder, quotient; + logic [31:0] test_num; + + /* Module Instantiations */ + radix4_divider #(32) DUT + ( + .CLK(CLK), + .nRST(nRST), + .start(start), + .dividend(dividend), + .divisor(divisor), + .quotient(quotient), + .remainder(remainder) + ); + /* CLK generation */ + + initial begin + CLK = 0; + end + + always begin + CLK = ~CLK; + #(PERIOD/2); + end + + task wait_result(); + for (int i = 0; i < 20; i++) + @(posedge CLK); + endtask + + task test_div( + input logic [31:0] a, + input logic [31:0] b, + input logic is_signed_t, + input logic [31:0] exp_quot, exp_rem + ); + divisor = a; + dividend = b; + is_signed = is_signed_t; + start = 1; + @(posedge CLK); + @(posedge CLK); + #(9); + start = 0; + while(!finished) + @(posedge CLK); + + assert (quotient == exp_quot) else $error("Division failed for test %0d: Expected %h Received %h\n", test_num, exp_quot, quotient); + assert (remainder == exp_rem) else $error("Remainder failed for test %0d: Expected %h Received %h\n", test_num, exp_rem, remainder); + test_num++; + endtask + + /* Begin Testing */ + + initial begin + // reset divider + test_num = 0; + nRST = 1'b0; + divisor = 0; + dividend = 0; + is_signed = 0; + start = 0; + @(posedge CLK); + @(posedge CLK); + #(PERIOD/2); + nRST = 1'b1; + @(posedge CLK); + @(posedge CLK); + @(posedge CLK); + + @(negedge CLK); + divisor = 32'd6; + dividend = 32'd18; + start = 1'b1; + #(PERIOD); + start = 1'b0; + wait_result(); + + @(negedge CLK); + divisor = 32'd23910; + dividend = 32'd8212148; + start = 1'b1; + #(PERIOD); + start = 1'b0; + wait_result(); + + //Basic test divisor, dividend, signed, quot, rem + //test_div(32'h80, 32'h2, 1'b0, 32'h40, 32'h0); + + + $finish; + end + +endmodule diff --git a/source_code/tb/tb_sparce_cfid.sv b/source_code/tb/tb_sparce_cfid.sv new file mode 100644 index 000000000..81d8b2a58 --- /dev/null +++ b/source_code/tb/tb_sparce_cfid.sv @@ -0,0 +1,74 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_cfid.sv +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: Oct 3rd, 2019 +* Description: Testbench for the control flow instruction detector +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + +typedef struct packed +{ + word_t rdata; + logic enable; +} sparce_cfid_testvec_t; + +module tb_sparce_cfid (); + + parameter PERIOD = 20; + + sparce_internal_if sparce_if(); + sparce_cfid DUT(sparce_if); + + logic tb_clk; + integer i; + sparce_cfid_testvec_t testvec[4:0]; + + assign testvec = + { + {{{25{1'b0}}, JAL}, 1'b0 }, + {{{25{1'b0}}, JALR}, 1'b0 }, + {{{25{1'b0}}, LOAD}, 1'b1 }, + {{{25{1'b0}}, BRANCH}, 1'b0 }, + {{{25{1'b0}}, IMMED}, 1'b1 } + }; + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + initial begin + tb_clk <= 0; + i = 0; + @(posedge tb_clk); + while (i<$size(testvec)) begin + @(negedge tb_clk); + sparce_if.rdata = testvec[i].rdata; + @(posedge tb_clk); + assert (sparce_if.ctrl_flow_enable == testvec[i].enable) + else + $error("INCORRECT ENABLE"); + i = i+1; + end + $finish; + end +endmodule diff --git a/source_code/tb/tb_sparce_psru.sv b/source_code/tb/tb_sparce_psru.sv new file mode 100644 index 000000000..84c1e3d00 --- /dev/null +++ b/source_code/tb/tb_sparce_psru.sv @@ -0,0 +1,152 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_psru.sv +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: 08/27/2019 +* Description: Testbench for the Pre-identify and Skip Redundancy Unit +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + + // modport psru ( + // output skipping, sparce_target, + // input valid, insts_to_skip, preceding_pc, condition, rs1_sparsity, rs2_sparsity, ctrl_flow_enable + // ); + + +module tb_sparce_psru (); + + parameter PERIOD = 20; + integer i; + logic tb_clk; + logic temp_skip; + + sparce_internal_if sparce_if(); + sparce_psru DUT(sparce_if); + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + initial begin + tb_clk = 0; + $display("TEST0: Verify Sparce skipping and target values when SASA is invalid"); + initialize; + test_sasa_invalid; + $display("TEST1: Verify Sparce skipping when SASA is valid"); + initialize; + test_psru_skipping; + $display("TEST2: Verify Sparce target when SASA is valid"); + test_psru_target; + $display("TEST3: Verify control flow enabling"); + initialize; + test_ctrl_flow_enable; + $finish; + end + + // initialize psru port values + task initialize; + i = 0; + sparce_if.valid = 1'b0; + sparce_if.insts_to_skip = 5'd4; + sparce_if.preceding_pc = 32'h3000; + sparce_if.condition = SASA_COND_OR; + sparce_if.rs1_sparsity= 1'b0; + sparce_if.rs2_sparsity= 1'b0; + sparce_if.ctrl_flow_enable = 1'b1; + endtask + + // Test0: Verify sparce skipping and target values when SASA is invalid + task test_sasa_invalid; + for (i = 0; i < 8; i++) begin + @(negedge tb_clk); + sparce_if.condition = sasa_cond_t'(i[2]); + sparce_if.rs1_sparsity = i[1]; + sparce_if.rs2_sparsity = i[0]; + @(posedge tb_clk); + assert (sparce_if.skipping == 1'b0) $display ("PASSED: PSRU not skipped when SASA invalid"); + else $error("FAILED: SASA invalid but psru skipped"); + end + endtask + + // Test1: Verify sparce skipping when skip conditions are correct + task test_psru_skipping; + sparce_if.valid = 1'b1; + sparce_if.condition = SASA_COND_OR; + for (i = 0; i < 4; i++) begin + @(negedge tb_clk); + sparce_if.rs1_sparsity = i[1]; + sparce_if.rs2_sparsity = i[0]; + @(posedge tb_clk); + assert (sparce_if.skipping == (i[0] | i[1])) $display ("PASSED: PSRU skipped correctly when SASA valid"); + else $error("FAILED: SASA valid but psru skipped incorrectly"); + end + + sparce_if.condition = SASA_COND_AND; + for (i = 0; i < 4; i++) begin + @(negedge tb_clk); + sparce_if.rs1_sparsity = i[1]; + sparce_if.rs2_sparsity = i[0]; + @(posedge tb_clk); + assert (sparce_if.skipping == (i[0] & i[1])) $display ("PASSED: PSRU skipped correctly when SASA valid"); + else $error("FAILED: SASA valid but psru skipped incorrectly"); + end + endtask + + // Test2: Verify sparce target calculated correctly + task test_psru_target; + sparce_if.valid = 1'b1; + sparce_if.condition = SASA_COND_OR; + sparce_if.rs1_sparsity = 1'b1; + sparce_if.rs2_sparsity = 1'b1; + for (i = 0; i <= 5'h1F; i++) begin + @(negedge tb_clk); + sparce_if.insts_to_skip = i; + @(posedge tb_clk); + assert (sparce_if.sparce_target == sparce_if.preceding_pc + (i << 2) + 4) + else $error("FAILED: Skip target incorrect"); + end + $display("Test2 finished. PASSED if no assertion errors"); + endtask + + // Test3: Verify skipping suppression/enable for control flow instructions + task test_ctrl_flow_enable; + sparce_if.valid = 1'b1; + for (i = 0; i < 8; i++) begin + @(negedge tb_clk); + sparce_if.condition = i[0]; + sparce_if.rs1_sparsity = i[1]; + sparce_if.rs2_sparsity = i[2]; + sparce_if.ctrl_flow_enable = i[3]; + @(posedge tb_clk); + if (sparce_if.condition == SASA_COND_OR) + temp_skip = sparce_if.rs1_sparsity || sparce_if.rs2_sparsity; + else + temp_skip = sparce_if.rs1_sparsity && sparce_if.rs2_sparsity; + + assert ((temp_skip && sparce_if.ctrl_flow_enable) == sparce_if.skipping) + else + $error("Test3 ENABLE for skipping incorrect"); + end + $display("Test3 finished. PASSED if no assertion errors"); + endtask + +endmodule diff --git a/source_code/tb/tb_sparce_sasa_table.sv b/source_code/tb/tb_sparce_sasa_table.sv new file mode 100644 index 000000000..cf522c9b8 --- /dev/null +++ b/source_code/tb/tb_sparce_sasa_table.sv @@ -0,0 +1,484 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_sasa_table.sv +* +* Created by: Vadim Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 08/23/2019 +* Updated by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Last updated: 10/30/2019 +* Description: Testbench for the sasa table file +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + +// macro for getting the index of the sasa table out of all the generated +// tables. i is the index representing the number of table sizes and +// j is the index representing the number of sets +`define GET_IDX(i,j)\ + ((i)*NUM_SETS + (j)) + +// macro to generate the data entry to load into the sasa table. Note that the +// pc is only shifted by 14 bits instead of 16 bits because the datapath PC is +// byte addressed while the sasa table expects word addressed data to save +// space +`define SASA_DATA(pc, rs1, rs2, cond, insts_to_skip)\ + (((pc) << 14) + (((rs1) & 'h1F) << 11) + (((rs2) & 'h1F) << 6) + (((cond) & 'h1) << 5) + ((insts_to_skip) & 'h1F)) + +typedef struct +{ + word_t pc; + word_t sasa_addr; + word_t sasa_data; + word_t preceding_pc; + logic sasa_wen; + logic valid; + logic sasa_enable; + logic[4:0] sasa_rs1; + logic[4:0] sasa_rs2; + logic[4:0] rd; + sasa_cond_t condition; + logic [4:0] insts_to_skip; +} tb_sasa_port_t; + +module tb_sparce_sasa_table (); + + parameter PERIOD = 20; + parameter NUM_TABLE_SIZES = 5; + parameter NUM_SETS = 3; + parameter NUM_SASA_TABLES = NUM_TABLE_SIZES * NUM_SETS; + parameter SASA_ADDR = 32'h90000000; + parameter SASA_CONF_ADDR = SASA_ADDR + 4; + + logic tb_clk; + logic tb_nRST; + + sparce_internal_if sparce_if_arr[NUM_SASA_TABLES](); + tb_sasa_port_t sasa_port_arr[NUM_SASA_TABLES]; + + genvar i, j; + integer tb_i; + integer tb_j; + + generate + begin : tb_variable_sasa + for (i=0; i < NUM_TABLE_SIZES; i++) begin + for (j=0; j < NUM_SETS; j++) begin + sparce_sasa_table #(.SASA_ENTRIES(2**(i+2)),.SASA_SETS(2**j), .SASA_ADDR(SASA_ADDR)) DUT (tb_clk, tb_nRST, sparce_if_arr[`GET_IDX(i,j)]); + assign sparce_if_arr[`GET_IDX(i,j)].pc = sasa_port_arr[`GET_IDX(i,j)].pc; + assign sparce_if_arr[`GET_IDX(i,j)].sasa_addr = sasa_port_arr[`GET_IDX(i,j)].sasa_addr; + assign sparce_if_arr[`GET_IDX(i,j)].sasa_data = sasa_port_arr[`GET_IDX(i,j)].sasa_data; + assign sparce_if_arr[`GET_IDX(i,j)].sasa_wen = sasa_port_arr[`GET_IDX(i,j)].sasa_wen; + assign sparce_if_arr[`GET_IDX(i,j)].sasa_enable = sasa_port_arr[`GET_IDX(i,j)].sasa_enable; + assign sasa_port_arr[`GET_IDX(i,j)].sasa_rs1 = sparce_if_arr[`GET_IDX(i,j)].sasa_rs1; + assign sasa_port_arr[`GET_IDX(i,j)].sasa_rs2 = sparce_if_arr[`GET_IDX(i,j)].sasa_rs2; + assign sasa_port_arr[`GET_IDX(i,j)].insts_to_skip = sparce_if_arr[`GET_IDX(i,j)].insts_to_skip; + assign sasa_port_arr[`GET_IDX(i,j)].preceding_pc = sparce_if_arr[`GET_IDX(i,j)].preceding_pc; + assign sasa_port_arr[`GET_IDX(i,j)].condition = sparce_if_arr[`GET_IDX(i,j)].condition; + assign sasa_port_arr[`GET_IDX(i,j)].valid = sparce_if_arr[`GET_IDX(i,j)].valid; + end + end + end + endgenerate + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + /************************************************************************* + * Initial block; Call tasks here. + *************************************************************************/ + initial begin + tb_clk = 0; + tb_nRST = 1; + for (tb_i=0; tb_i < NUM_TABLE_SIZES ; tb_i++) begin + for (tb_j=0; tb_j < NUM_SETS; tb_j++) begin + $display("Testing size %d table with %d sets", 2**(tb_i+2),2**tb_j); + // test that the table is initialized correctly + test_default_values(tb_i,tb_j); + // test that the table can be loaded correctly + load_sasa_table(tb_i,tb_j); + load_duplicate_entries(tb_i,tb_j); + test_associativity(tb_i,tb_j); + test_lru(tb_i,tb_j); + disable_sasa_table(tb_i,tb_j); + reenable_sasa_table(tb_i, tb_j); + pc_out_of_range(tb_i, tb_j); + end + end + $finish; + end + + /************************************************************************* + * TEST #0: + * Ensure that when initialized, the SASA table outputs every + * entry as not valid + *************************************************************************/ + task test_default_values(integer size_idx, integer set_idx); + integer ii; + integer idx; + initialize(size_idx, set_idx); + idx = `GET_IDX(size_idx, set_idx); + // loop through the sasa table and try to fetch data from each index. + // The table should always output its data as invalid + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].sasa_data = `SASA_DATA(ii << 2, ii, ii, ii, ii); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + assert (sasa_port_arr[idx].valid == 1'b0) else $error("Uninitialized entry in SASA table outputs as valid"); + end + endtask + + /************************************************************************* + * TEST #1: + * Ensure that basic loading of the sasa table functions correctly (and + * consequently that reading loaded values functions properly as well) + *************************************************************************/ + task load_sasa_table(integer size_idx, integer set_idx); + integer ii; + integer idx; + initialize(size_idx, set_idx); + idx = `GET_IDX(size_idx, set_idx); + + @(negedge tb_clk); + + // loop through every possible entry in the sasa table. Because these are + // consecutive tests, there should be no collisions, and every value + // should be readable immediately after writes + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 1); + end + + // after all writes have been completed, ensure that all entries can still + // be read + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 1); + end + endtask + + /************************************************************************* + * TEST #2: + * Ensure when a SASA entry has the same program counter that already exists + * in the SASA table, it will update the existing entry instead of writing to + * the other set. + *************************************************************************/ + task load_duplicate_entries(integer size_idx, integer set_idx); + integer ii; + integer idx; + initialize(size_idx, set_idx); + idx = `GET_IDX(size_idx, set_idx); + + @(negedge tb_clk); + + // write entry to set 1 + write_sasa_entry(size_idx, set_idx, `SASA_DATA('h1000, 1, 2, 3, 4)); + + // loop through every possible entry in the sasa table. Because these are + // consecutive tests, there should be no collisions, and every value + // should be readable immediately after writes + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = 0; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(0, ii, ii, ii, ii)); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(0, ii, ii, ii,ii), 1); + + // make sure that set 1 is not replaced + sasa_port_arr[idx].pc = 'h1000; + read_sasa_entry(size_idx, set_idx, `SASA_DATA('h1000, 1, 2, 3, 4), set_idx != 0); + end + endtask + + /************************************************************************* + * TEST #3: + * Ensures that when the SASA table reaches full capacity, it forces the + * original entry out of the table. + *************************************************************************/ + task test_associativity(integer size_idx, integer set_idx); + integer ii; + integer idx; + word_t pc; + initialize(size_idx, set_idx); + idx = `GET_IDX(size_idx, set_idx); + // Write to all entries, and then keep writing after capacity has been + // reached, forcing the original entries out + for (ii = 0; ii < (2**(size_idx+2)) + ((2**(size_idx+2)) / (2**set_idx)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + // Read the initial entries written in each set, and ensure that they are + // no longer valid (due to being replaced) + for (ii = 0; ii < (2**(size_idx+2)) / (2**set_idx); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii), 0); + end + endtask + + /************************************************************************* + * TEST #4: + * Ensures that when the SASA table reaches full capacity, it forces the LRU + * entry out of the table + *************************************************************************/ + task test_lru(integer size_idx, integer set_idx); + integer ii; + integer idx; + word_t pc; + idx = `GET_IDX(size_idx, set_idx); + + initialize(size_idx, set_idx); + + // write data to every entry in the SASA table + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + // read all data from the first set to reset the LRU + for (ii = 0; ii < (2**(size_idx+2)) / (2**set_idx); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii), 1); + end + // write a set of data to the SASA table + for (ii = (2**(size_idx+2)); ii < (2**(size_idx+2)) + ((2**(size_idx+2)) / (2**set_idx)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + // verify that the data in the first set is still present from the + // original write. + // Note, don't expect valid data from direct-mapped cache configurations + for (ii = 0; ii < (2**(size_idx+2)) / (2**set_idx); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + // set_idx != 0 to avoid expecting data to be present for direct-mapped + // caches + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii), set_idx != 0); + end + endtask + + /************************************************************************* + * TEST #5: + * Ensure that after sasa table is disabled, the outputs are invalid. + *************************************************************************/ + task disable_sasa_table(integer size_idx, integer set_idx); + integer ii; + integer idx; + idx = `GET_IDX(size_idx, set_idx); + initialize(size_idx, set_idx); + + // disable sasa table by writing not 0 to config register + write_to_sasa_config (size_idx, set_idx, `SASA_DATA(0, 0, 0, 0, 7)); + + // write data to every entry in the SASA table + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + + // read all entries and outputs are invalid + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 0); + end + endtask + + /************************************************************************* + * TEST #6: + * Ensure that after sasa table is re-enabled, the outputs are valid. + *************************************************************************/ + task reenable_sasa_table(integer size_idx, integer set_idx); + integer ii; + integer idx; + idx = `GET_IDX(size_idx, set_idx); + initialize(size_idx, set_idx); + + // disable sasa table by writing 1 to config register + write_to_sasa_config (size_idx, set_idx, `SASA_DATA(0, 0, 0, 0, 1)); + + // write data to every entry in the SASA table + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + + // enable sasa table by writing 0 to config register + write_to_sasa_config (size_idx, set_idx, `SASA_DATA(0, 0, 0, 0, 0)); + + // read all entries + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 1); + end + endtask + + /************************************************************************* + * TEST #7: + * Ensure that only PC <= 'hFFFC 0000 can be skipped + *************************************************************************/ + task pc_out_of_range(integer size_idx, integer set_idx); + integer ii; + integer idx; + idx = `GET_IDX(size_idx, set_idx); + initialize(size_idx, set_idx); + + // write data to every entry in the SASA table + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + sasa_port_arr[idx].pc = ii << 2; + write_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii, ii)); + end + + // read all entries + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_data = '1; + sasa_port_arr[idx].sasa_addr = '1; + sasa_port_arr[idx].sasa_enable = '0; + for (ii = 0; ii < (2**(size_idx+2)); ii++) begin + @(negedge tb_clk); + sasa_port_arr[idx].pc = ii << 2; + sasa_port_arr[idx].pc[31:18] = (ii + 1) << 2; + @(posedge tb_clk); + read_sasa_entry(size_idx, set_idx, `SASA_DATA(ii << 2, ii, ii, ii,ii), 0); + end + endtask + + /************************************************************************* + * Helper function #1: read sasa entry + *************************************************************************/ + task read_sasa_entry(integer size_idx, integer set_idx, word_t data, logic valid); + integer expected; + integer idx; + idx = `GET_IDX(size_idx, set_idx); + #(PERIOD/20); + if (!valid) begin + assert (sasa_port_arr[idx].valid == '0) else $error("Unitialized entry in SASA table outputs as valid"); + end else begin + assert (sasa_port_arr[idx].valid == '1) else $error("Initialized entry in SASA table outputs as invalid"); + + expected = (data >> 11) & 'h1F; + assert (sasa_port_arr[idx].sasa_rs1 == expected) + else $error("Initialized entry in SASA table outputs incorrect sasa_rs1 value (exp: %d, got: %d)", expected , sasa_port_arr[idx].sasa_rs1); + + expected = (data >> 6) & 'h1F; + assert (sasa_port_arr[idx].sasa_rs2 == expected) + else $error("Initialized entry in SASA table outputs incorrect sasa_rs2 value (exp: %d, got: %d)", expected, sasa_port_arr[idx].sasa_rs2); + + expected = data & 'h1F; + assert (sasa_port_arr[idx].insts_to_skip == expected) + else $error("Initialized entry in SASA table outputs incorrect insts_to_skip value (exp: %d, got: %d)", expected, sasa_port_arr[idx].insts_to_skip); + + assert (sasa_port_arr[idx].preceding_pc == sasa_port_arr[idx].pc) + else $error("Initialized entry in SASA table outputs incorrect preceding_pc value (exp: %d, got: %d)", sasa_port_arr[idx].pc, sasa_port_arr[idx].insts_to_skip); + + expected = (data >> 5) & '1; + assert (sasa_port_arr[idx].condition == sasa_cond_t'(expected)) + else $error("Initialized entry in SASA table outputs incorrect preceding_pc value (exp: %d, got: %d)", sasa_cond_t'(expected), sasa_port_arr[idx].condition); + end + endtask + + /************************************************************************* + * Helper function #2: write sasa entry + *************************************************************************/ + task write_sasa_entry(integer size_idx, integer set_idx, word_t data); + integer idx; + idx = `GET_IDX(size_idx, set_idx); + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '1; + sasa_port_arr[idx].sasa_data = data; + sasa_port_arr[idx].sasa_addr = SASA_ADDR; + sasa_port_arr[idx].sasa_enable = '1; + @(posedge tb_clk); + endtask + + /************************************************************************* + * Helper function #3: initialize signals + *************************************************************************/ + task initialize(integer size_idx, integer set_idx); + integer idx; + idx = `GET_IDX(size_idx, set_idx); + @(negedge tb_clk); + tb_nRST = 0; + sasa_port_arr[idx].pc = '0; + sasa_port_arr[idx].sasa_addr = '0; + sasa_port_arr[idx].sasa_data = '0; + sasa_port_arr[idx].sasa_wen = '0; + sasa_port_arr[idx].sasa_enable = '0; + @(negedge tb_clk); + @(negedge tb_clk); + tb_nRST = 1; + @(negedge tb_clk); + endtask + + /************************************************************************* + * Helper function #4: write to configuration register + *************************************************************************/ + task write_to_sasa_config (integer size_idx, integer set_idx, word_t data); + integer idx; + idx = `GET_IDX(size_idx, set_idx); + @(negedge tb_clk); + sasa_port_arr[idx].sasa_wen = '1; + sasa_port_arr[idx].sasa_data = data; + sasa_port_arr[idx].sasa_addr = SASA_CONF_ADDR; + sasa_port_arr[idx].sasa_enable = '1; + @(posedge tb_clk); + endtask + +endmodule + diff --git a/source_code/tb/tb_sparce_sprf.sv b/source_code/tb/tb_sparce_sprf.sv new file mode 100644 index 000000000..c2e3230e3 --- /dev/null +++ b/source_code/tb/tb_sparce_sprf.sv @@ -0,0 +1,188 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_sprf.sv +* +* Created by: Vadim Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 08/18/2019 +* Description: Testbench for the sparsity register file +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + + +typedef struct packed +{ + logic wb_en; + logic [4:0] rd; + logic is_sparse; + logic [4:0] sasa_rs1; + logic [4:0] sasa_rs2; + logic rs1_sparsity; + logic rs2_sparsity; +} sparce_sprf_testvec_t; + +// modport sprf ( +// output rs1_sparsity, rs2_sparsity, +// input wb_en, rd, is_sparse, sasa_rs1, sasa_rs2 +// ); + +module tb_sparce_sprf (); + + parameter PERIOD = 20; + + logic tb_clk; + logic tb_nRST; + + sparce_internal_if sparce_if(); + sparce_sprf DUT(tb_clk, tb_nRST, sparce_if); + + integer i; + sparce_sprf_testvec_t testvec[2:0]; + + assign testvec = + { + {32'b0, 1'b1}, + {32'b1, 1'b0}, + {'1, 1'b0} + }; + + + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + initial begin + tb_clk = 0; + tb_nRST = 1; + i = 0; + initialize; + test_initial_values; + test_writes; + test_no_enable; + test_writes_in_flight; + $finish; + end + + task initialize; + sparce_if.wb_en = 1'b0; + sparce_if.rd = 5'b0; + sparce_if.is_sparse = 1'b0; + sparce_if.sasa_rs1 = 5'b0; + sparce_if.sasa_rs2 = 5'b0; + @(negedge tb_clk); + tb_nRST = 0; + @(negedge tb_clk); + tb_nRST = 1; + endtask + + task test_initial_values; + @(negedge tb_clk); + for (i=0; i< 32; i++) begin + @(negedge tb_clk); + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = 31-i; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == (i == 0)) else $error("SPRF has wrong init values for register %2d", i); + assert (sparce_if.rs2_sparsity == (i == 31)) else $error("SPRF has wrong init values for register %2d", 31-i); + end + endtask + + task test_writes; + initialize; + @(negedge tb_clk); + // Test writes of 1 to the sprf + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 1; + sparce_if.is_sparse = 1; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(negedge tb_clk); + sparce_if.wb_en = 0; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == 1) else $error("SPRF did not store sparse value for register %2d", i); + assert (sparce_if.rs2_sparsity == 1) else $error("SPRF did not store sparse value for register %2d", i); + end + // Test writes of 0 to the sprf + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 1; + sparce_if.is_sparse = 0; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(negedge tb_clk); + sparce_if.wb_en = 0; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == (i==0)) else $error("SPRF did not store non-sparse value for register %2d", i); + assert (sparce_if.rs2_sparsity == (i==0)) else $error("SPRF did not store non-sparse value for register %2d", i); + end + endtask + + task test_no_enable; + initialize; + @(negedge tb_clk); + // Test writes of 1 to the sprf without the enable bit set + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 0; + sparce_if.is_sparse = 1; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(negedge tb_clk); + sparce_if.wb_en = 0; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == (i==0)) else $error("SPRF stored sparse value for register %2d without enable set", i); + assert (sparce_if.rs2_sparsity == (i==0)) else $error("SPRF stored sparse value for register %2d without enable set", i); + end + endtask + + task test_writes_in_flight; + // Test writes of 1 to the sprf in flight + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 1; + sparce_if.is_sparse = 1; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == 1) else $error("SPRF did not process in-flight sparse value for register %2d", i); + assert (sparce_if.rs2_sparsity == 1) else $error("SPRF did not process in-flight sparse value for register %2d", i); + end + // Test writes of 0 to the sprf in flight + for (i=0; i<32; i++) begin + @(negedge tb_clk); + sparce_if.rd = i; + sparce_if.wb_en = 1; + sparce_if.is_sparse = 0; + sparce_if.sasa_rs1 = i; + sparce_if.sasa_rs2 = i; + @(posedge tb_clk); + assert (sparce_if.rs1_sparsity == (i==0)) else $error("SPRF did not process in-flight non-sparse value for register %2d", i); + assert (sparce_if.rs2_sparsity == (i==0)) else $error("SPRF did not process in-flight non-sparse value for register %2d", i); + end + endtask + + +endmodule diff --git a/source_code/tb/tb_sparce_svc.sv b/source_code/tb/tb_sparce_svc.sv new file mode 100644 index 000000000..ae2e1111b --- /dev/null +++ b/source_code/tb/tb_sparce_svc.sv @@ -0,0 +1,75 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb/tb_sparce_svc.sv +* +* Created by: Vadim Nikiforov +* Email: vnikifor@purdue.edu +* Date Created: 08/16/2019 +* Description: Testbench for the sparsity value checker +*/ + +`include "sparce_internal_if.vh" + import rv32i_types_pkg::*; + + +typedef struct packed +{ + word_t wb_data; + logic is_sparse; +} sparce_svc_testvec_t; + +module tb_sparce_svc (); + + parameter PERIOD = 20; + + sparce_internal_if sparce_if(); + sparce_svc DUT(sparce_if); + + logic tb_clk; + integer i; + sparce_svc_testvec_t testvec[2:0]; + + assign testvec = + { + {32'b0, 1'b1}, + {32'b1, 1'b0}, + {'1, 1'b0} + }; + + + + always begin + #(PERIOD/2); + tb_clk <= ~tb_clk; + end + + initial begin + tb_clk <= 0; + i = 0; + @(posedge tb_clk); + while( i < $size(testvec)) begin + @(negedge tb_clk); + sparce_if.wb_data = testvec[i].wb_data; + @(posedge tb_clk); + assert (sparce_if.is_sparse == testvec[i].is_sparse) else $error("Sparsity not detected properly - Input: %d, Output: %d", testvec[i].wb_data, sparce_if.is_sparse); + i = i+1; + end + $finish; + end + + +endmodule diff --git a/source_code/trackers/branch_tracker.sv b/source_code/trackers/branch_tracker.sv index 0f06ae000..651fcad8f 100644 --- a/source_code/trackers/branch_tracker.sv +++ b/source_code/trackers/branch_tracker.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,76 +19,78 @@ * Created by: Jacob R. Stevens * Email: steven69@purdue.edu * Date Created: 09/26/2016 -* Description: Track performance stats associated with branch prediction +* Description: Track performance stats associated with branch prediction */ -`define STATS_FILE_NAME "stats.txt" -module branch_tracker( - input logic CLK, nRST, - input logic update_predictor, - input logic prediction, - input logic branch_result +`define STATS_FILE_NAME "stats.txt" +module branch_tracker ( + input logic CLK, + nRST, + input logic update_predictor, + input logic prediction, + input logic branch_result ); - // Branch Performance Signals - logic [63:0] prediction_count, misprediction_count, correct_pred_count, - pred_taken_count, pred_not_taken_count, - taken_incorrect_count, not_taken_incorrect_count; - - always_ff @(posedge CLK, negedge nRST) begin : tracked_registers - if (!nRST) begin - prediction_count <= '0; - misprediction_count <= '0; - correct_pred_count <= '0; - pred_taken_count <= '0; - pred_not_taken_count <= '0; - taken_incorrect_count <= '0; - not_taken_incorrect_count <= '0; - end else if (update_predictor & !prediction & !branch_result) begin - // Predicted not taken and branch result is not taken - prediction_count <= prediction_count + 1; - correct_pred_count <= correct_pred_count + 1; - pred_not_taken_count <= pred_not_taken_count + 1; - end else if (update_predictor & !prediction & branch_result) begin - // Predicted not taken and branch result is taken - prediction_count <= prediction_count + 1; - misprediction_count <= misprediction_count + 1; - pred_not_taken_count <= pred_not_taken_count + 1; - not_taken_incorrect_count <= not_taken_incorrect_count + 1; - end else if (update_predictor & prediction & !branch_result) begin - // Predicted taken and branch result is not taken - prediction_count <= prediction_count + 1; - misprediction_count <= misprediction_count + 1; - pred_taken_count <= pred_taken_count + 1; - taken_incorrect_count <= taken_incorrect_count + 1; - end else if (update_predictor & prediction & branch_result) begin - // Predicted taken and branch result is taken - prediction_count <= prediction_count + 1; - correct_pred_count <= correct_pred_count + 1; - pred_taken_count <= pred_taken_count + 1; - end - end : tracked_registers + // Branch Performance Signals + logic [63:0] + prediction_count, + misprediction_count, + correct_pred_count, + pred_taken_count, + pred_not_taken_count, + taken_incorrect_count, + not_taken_incorrect_count; + + always_ff @(posedge CLK, negedge nRST) begin : tracked_registers + if (!nRST) begin + prediction_count <= '0; + misprediction_count <= '0; + correct_pred_count <= '0; + pred_taken_count <= '0; + pred_not_taken_count <= '0; + taken_incorrect_count <= '0; + not_taken_incorrect_count <= '0; + end else if (update_predictor & !prediction & !branch_result) begin + // Predicted not taken and branch result is not taken + prediction_count <= prediction_count + 1; + correct_pred_count <= correct_pred_count + 1; + pred_not_taken_count <= pred_not_taken_count + 1; + end else if (update_predictor & !prediction & branch_result) begin + // Predicted not taken and branch result is taken + prediction_count <= prediction_count + 1; + misprediction_count <= misprediction_count + 1; + pred_not_taken_count <= pred_not_taken_count + 1; + not_taken_incorrect_count <= not_taken_incorrect_count + 1; + end else if (update_predictor & prediction & !branch_result) begin + // Predicted taken and branch result is not taken + prediction_count <= prediction_count + 1; + misprediction_count <= misprediction_count + 1; + pred_taken_count <= pred_taken_count + 1; + taken_incorrect_count <= taken_incorrect_count + 1; + end else if (update_predictor & prediction & branch_result) begin + // Predicted taken and branch result is taken + prediction_count <= prediction_count + 1; + correct_pred_count <= correct_pred_count + 1; + pred_taken_count <= pred_taken_count + 1; + end + end : tracked_registers - final begin : OUTPUT_STATS - integer stats_fptr; - stats_fptr = $fopen(`STATS_FILE_NAME, "a"); - $fwrite(stats_fptr, "Conditional branches predicted: %2d\n", prediction_count); - $fwrite(stats_fptr, "Conditional branches predicted incorrectly: %2d\n", - misprediction_count); - $fwrite(stats_fptr, "Conditional branches predicted correctly: %2d\n", - correct_pred_count); - $fwrite(stats_fptr, "Branch prediction accuracy: %5f\n", - real'(correct_pred_count)/prediction_count); - $fwrite(stats_fptr, "Branches predicted as taken: %2d\n", - pred_taken_count); - $fwrite(stats_fptr, "Branches predicted as taken, incorrect: %2d\n", - taken_incorrect_count); - $fwrite(stats_fptr, "Branches predicted as taken, correct: %2d\n", - pred_taken_count - taken_incorrect_count); - $fwrite(stats_fptr, "Branches predicted as not taken: %2d\n", - pred_not_taken_count); - $fwrite(stats_fptr, "Branches predicted as not taken, incorrect: %2d\n", - not_taken_incorrect_count); - $fwrite(stats_fptr, "Branches predicted as not taken, correct: %2d\n", - pred_not_taken_count - not_taken_incorrect_count); - $fclose(stats_fptr); - end : OUTPUT_STATS + final begin : OUTPUT_STATS + integer stats_fptr; + stats_fptr = $fopen(`STATS_FILE_NAME, "a"); + $fwrite(stats_fptr, "Conditional branches predicted: %2d\n", prediction_count); + $fwrite(stats_fptr, "Conditional branches predicted incorrectly: %2d\n", + misprediction_count); + $fwrite(stats_fptr, "Conditional branches predicted correctly: %2d\n", correct_pred_count); + $fwrite(stats_fptr, "Branch prediction accuracy: %5f\n", + real'(correct_pred_count) / prediction_count); + $fwrite(stats_fptr, "Branches predicted as taken: %2d\n", pred_taken_count); + $fwrite(stats_fptr, "Branches predicted as taken, incorrect: %2d\n", taken_incorrect_count); + $fwrite(stats_fptr, "Branches predicted as taken, correct: %2d\n", + pred_taken_count - taken_incorrect_count); + $fwrite(stats_fptr, "Branches predicted as not taken: %2d\n", pred_not_taken_count); + $fwrite(stats_fptr, "Branches predicted as not taken, incorrect: %2d\n", + not_taken_incorrect_count); + $fwrite(stats_fptr, "Branches predicted as not taken, correct: %2d\n", + pred_not_taken_count - not_taken_incorrect_count); + $fclose(stats_fptr); + end : OUTPUT_STATS endmodule diff --git a/source_code/trackers/cpu_tracker.core b/source_code/trackers/cpu_tracker.core new file mode 100644 index 000000000..d2fec27e6 --- /dev/null +++ b/source_code/trackers/cpu_tracker.core @@ -0,0 +1,21 @@ +CAPI=2: +name: socet:riscv:cpu-tracker:0.1.0 +description: RISC-V Tracker modules + +filesets: + rtl: + files: + - cpu_tracker.sv + file_type: systemVerilogSource + +targets: + lint: + filesets: + - rtl + description: Linting + default_tool: veriblelint + toplevel: cpu_tracker + tools: + veriblelint: + verible_lint_args: ['--autofix=inplace-interactive', '--rules_config_search'] + diff --git a/source_code/trackers/cpu_tracker.sv b/source_code/trackers/cpu_tracker.sv index d35d27b95..d44503e07 100644 --- a/source_code/trackers/cpu_tracker.sv +++ b/source_code/trackers/cpu_tracker.sv @@ -1,12 +1,12 @@ /* * Copyright 2016 Purdue University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at -* +* * http://www.apache.org/licenses/LICENSE-2.0 -* +* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,260 +20,255 @@ * Email: steven69@purdue.edu * Date Created: 06/27/2016 * Description: Prints out a trace of the cpu executing that can be -* compared against the trace generated by Spike +* compared against the trace generated by Spike */ `define TRACE_FILE_NAME "trace.log" -import rv32i_types_pkg::*; -import machine_mode_types_pkg::*; -module cpu_tracker( - input logic CLK, wb_stall, instr_30, - input word_t instr, pc, - input opcode_t opcode, - input logic [2:0] funct3, - input logic [11:0] funct12, - input logic [4:0] rs1, rs2, rd, - input logic [12:0] imm_SB, - input logic [11:0] imm_S, imm_I, - input logic [20:0] imm_UJ, - input logic [31:0] imm_U +module cpu_tracker ( + input logic CLK, wb_stall, instr_30, + input rv32i_types_pkg::word_t instr, pc, + input rv32i_types_pkg::opcode_t opcode, + input logic [2:0] funct3, + input logic [11:0] funct12, + input logic [4:0] rs1, rs2, rd, + input logic [12:0] imm_SB, + input logic [11:0] imm_S, imm_I, + input logic [20:0] imm_UJ, + input logic [31:0] imm_U ); - parameter CPUID = 0; + import rv32i_types_pkg::*; + import machine_mode_types_1_12_pkg::*; + import rv32m_pkg::*; - integer fptr; - string instr_mnemonic, output_str, src1, src2, dest, operands; - string csr, temp_str; - logic [63:0] pc64; - assign pc64 = {{32{1'b0}}, pc}; - initial begin: INIT_FILE - fptr = $fopen(`TRACE_FILE_NAME, "w"); - end + parameter int CPUID = 0; - always_comb begin - src1 = registerAssign(rs1); - src2 = registerAssign(rs2); - dest = registerAssign(rd); - csr = csrRegisterAssign(funct12); - end + integer fptr; + string instr_mnemonic, output_str, src1, src2, dest, operands; + string csr, temp_str; + logic [63:0] pc64; + assign pc64 = {{32{1'b0}}, pc}; + initial begin : INIT_FILE + fptr = $fopen(`TRACE_FILE_NAME, "w"); + end - always_comb begin - case (opcode) - LUI, AUIPC: $sformat(operands, "%s, %d", dest, imm_U[31:12]); - JAL: $sformat(operands, "%s, pc + %d", dest, signed'(imm_UJ)); - JALR: $sformat(operands, "%s, %s, %d", dest, src1, signed'(imm_I)); - BRANCH: $sformat(operands, "%s, %s, pc + %d", src1, src2, signed'(imm_SB)); - STORE: $sformat(operands, "%s, %d(%s)", src2, signed'(imm_S), src1); - LOAD: $sformat(operands, "%s, %d(%s)", dest, signed'(imm_I), src1); - IMMED: $sformat(operands, "%s, %s, %d", dest, src1, signed'(imm_I)); - REGREG: $sformat(operands, "%s, %s, %s", dest, src1, src2); - SYSTEM: begin - case(rv32i_system_t'(funct3)) - CSRRS, CSRRW, - CSRRC: $sformat(operands, "%s, %s, %s", dest, csr, src1); - CSRRSI, CSRRWI, - CSRRCI: $sformat(operands, "%s, %s, %d", dest, csr, rs1); - PRIV: operands = ""; - endcase - end - default: operands = ""; - endcase - end + always_comb begin + src1 = registerAssign(rs1); + src2 = registerAssign(rs2); + dest = registerAssign(rd); + csr = csrRegisterAssign(funct12); + end - always_comb begin - case (opcode) - LUI: instr_mnemonic = "lui"; - AUIPC: instr_mnemonic = "auipc"; - JAL: instr_mnemonic = "jal"; - JALR: instr_mnemonic = "jalr"; - BRANCH: begin - case(branch_t'(funct3)) - BEQ: instr_mnemonic = "beq"; - BNE: instr_mnemonic = "bne"; - BLT: instr_mnemonic = "blt"; - BGE: instr_mnemonic = "bge"; - BLTU: instr_mnemonic = "bltu"; - BGEU: instr_mnemonic = "bgeu"; - default: instr_mnemonic = "unknown branch op"; - endcase - end - LOAD: begin - case(load_t'(funct3)) - LB: instr_mnemonic = "lb"; - LH: instr_mnemonic = "lh"; - LW: instr_mnemonic = "lw"; - LBU: instr_mnemonic = "lbu"; - LHU: instr_mnemonic = "lhu"; - default: instr_mnemonic = "unknown load op"; - endcase - end - STORE: begin - case(store_t'(funct3)) - SB: instr_mnemonic = "sb"; - SH: instr_mnemonic = "sh"; - SW: instr_mnemonic = "sw"; - default: instr_mnemonic = "unknown store op"; - endcase - end - IMMED: begin - case(imm_t'(funct3)) - ADDI: instr_mnemonic = "addi"; - SLTI: instr_mnemonic = "slti"; - SLTIU: instr_mnemonic = "sltiu"; - XORI: instr_mnemonic = "xori"; - ORI: instr_mnemonic = "ori"; - ANDI: instr_mnemonic = "andi"; - SLLI: instr_mnemonic = "slli"; - SRI: begin - if (instr_30) - instr_mnemonic = "srai"; - else - instr_mnemonic = "srli"; - end - default: instr_mnemonic = "unknown immed op"; + always_comb begin + case (opcode) + LUI, AUIPC: $sformat(operands, "%s, %d", dest, imm_U[31:12]); + JAL: $sformat(operands, "%s, pc + %d", dest, signed'(imm_UJ)); + JALR: $sformat(operands, "%s, %s, %d", dest, src1, signed'(imm_I)); + BRANCH: $sformat(operands, "%s, %s, pc + %d", src1, src2, signed'(imm_SB)); + STORE: $sformat(operands, "%s, %d(%s)", src2, signed'(imm_S), src1); + LOAD: $sformat(operands, "%s, %d(%s)", dest, signed'(imm_I), src1); + IMMED: $sformat(operands, "%s, %s, %d", dest, src1, signed'(imm_I)); + REGREG: $sformat(operands, "%s, %s, %s", dest, src1, src2); + SYSTEM: begin + case (rv32i_system_t'(funct3)) + CSRRS, CSRRW, CSRRC: $sformat(operands, "%s, %s, %s", dest, csr, src1); + CSRRSI, CSRRWI, CSRRCI: $sformat(operands, "%s, %s, %d", dest, csr, rs1); + PRIV: operands = ""; + default: operands = ""; + endcase + end + default: operands = ""; endcase - end - REGREG: begin - case(regreg_t'(funct3)) - ADDSUB: begin - if (instr_30) - instr_mnemonic = "sub"; - else - instr_mnemonic = "add"; - end - SLL: instr_mnemonic = "sll"; - SLT: instr_mnemonic = "slt"; - SLTU: instr_mnemonic = "sltu"; - XOR: instr_mnemonic = "xor"; - SR: begin - if (instr_30) - instr_mnemonic = "sra"; - else - instr_mnemonic = "srl"; - end - OR: instr_mnemonic = "or"; - AND: instr_mnemonic = "and"; - default: instr_mnemonic = "unknown regreg op"; - endcase - end - SYSTEM: begin - case(rv32i_system_t'(funct3)) - CSRRW: instr_mnemonic = "csrrw"; - CSRRS: instr_mnemonic = "csrrs"; - CSRRC: instr_mnemonic = "csrrc"; - CSRRWI: instr_mnemonic = "csrrwi"; - CSRRSI: instr_mnemonic = "csrrsi"; - CSRRCI: instr_mnemonic = "csrrci"; - PRIV: begin - case(priv_insn_t'(funct12)) - ECALL: instr_mnemonic = "ecall"; - EBREAK: instr_mnemonic = "ebreak"; - MRET: instr_mnemonic = "mret"; - default: begin - instr_mnemonic = "errr"; - $display("%b", priv_insn_t'(funct12)); - end - endcase - end - default: instr_mnemonic = "unknown system op"; - endcase - end - MISCMEM: begin - case(rv32i_miscmem_t'(funct3)) - FENCE: instr_mnemonic = "fence"; - FENCEI: instr_mnemonic = "fence.i"; - default: instr_mnemonic = "unknown misc-mem op"; + end + + always_comb begin + case (opcode) + LUI: instr_mnemonic = "lui"; + AUIPC: instr_mnemonic = "auipc"; + JAL: instr_mnemonic = "jal"; + JALR: instr_mnemonic = "jalr"; + BRANCH: begin + case (branch_t'(funct3)) + BEQ: instr_mnemonic = "beq"; + BNE: instr_mnemonic = "bne"; + BLT: instr_mnemonic = "blt"; + BGE: instr_mnemonic = "bge"; + BLTU: instr_mnemonic = "bltu"; + BGEU: instr_mnemonic = "bgeu"; + default: instr_mnemonic = "unknown branch op"; + endcase + end + LOAD: begin + case (load_t'(funct3)) + LB: instr_mnemonic = "lb"; + LH: instr_mnemonic = "lh"; + LW: instr_mnemonic = "lw"; + LBU: instr_mnemonic = "lbu"; + LHU: instr_mnemonic = "lhu"; + default: instr_mnemonic = "unknown load op"; + endcase + end + STORE: begin + case (store_t'(funct3)) + SB: instr_mnemonic = "sb"; + SH: instr_mnemonic = "sh"; + SW: instr_mnemonic = "sw"; + default: instr_mnemonic = "unknown store op"; + endcase + end + IMMED: begin + case (imm_t'(funct3)) + ADDI: instr_mnemonic = "addi"; + SLTI: instr_mnemonic = "slti"; + SLTIU: instr_mnemonic = "sltiu"; + XORI: instr_mnemonic = "xori"; + ORI: instr_mnemonic = "ori"; + ANDI: instr_mnemonic = "andi"; + SLLI: instr_mnemonic = "slli"; + SRI: begin + if (instr_30) instr_mnemonic = "srai"; + else instr_mnemonic = "srli"; + end + default: instr_mnemonic = "unknown immed op"; + endcase + end + REGREG: begin + if(instr[31 -: 7] == RV32M_OPCODE_MINOR) begin + case (rv32m_op_t'(funct3)) + MUL: instr_mnemonic = "mul"; + MULH: instr_mnemonic = "mulh"; + MULHSU: instr_mnemonic = "mulhsu"; + MULHU: instr_mnemonic = "mulhu"; + DIV: instr_mnemonic = "div"; + DIVU: instr_mnemonic = "divu"; + REM: instr_mnemonic = "rem"; + REMU: instr_mnemonic = "remu"; + // No default -- full case + endcase + end else begin + case (regreg_t'(funct3)) + ADDSUB: begin + if (instr_30) instr_mnemonic = "sub"; + else instr_mnemonic = "add"; + end + SLL: instr_mnemonic = "sll"; + SLT: instr_mnemonic = "slt"; + SLTU: instr_mnemonic = "sltu"; + XOR: instr_mnemonic = "xor"; + SR: begin + if (instr_30) instr_mnemonic = "sra"; + else instr_mnemonic = "srl"; + end + OR: instr_mnemonic = "or"; + AND: instr_mnemonic = "and"; + + default: instr_mnemonic = "unknown regreg op"; + endcase + end + end + SYSTEM: begin + case (rv32i_system_t'(funct3)) + CSRRW: instr_mnemonic = "csrrw"; + CSRRS: instr_mnemonic = "csrrs"; + CSRRC: instr_mnemonic = "csrrc"; + CSRRWI: instr_mnemonic = "csrrwi"; + CSRRSI: instr_mnemonic = "csrrsi"; + CSRRCI: instr_mnemonic = "csrrci"; + PRIV: begin + case (priv_insn_t'(funct12)) + ECALL: instr_mnemonic = "ecall"; + EBREAK: instr_mnemonic = "ebreak"; + MRET: instr_mnemonic = "mret"; + WFI: instr_mnemonic = "wfi"; + default: begin + instr_mnemonic = "unknown system op"; + //$display("%b", priv_insn_t'(funct12)); + end + endcase + end + default: instr_mnemonic = "unknown system op"; + endcase + end + MISCMEM: begin + case (rv32i_miscmem_t'(funct3)) + FENCE: instr_mnemonic = "fence"; + FENCEI: instr_mnemonic = "fence.i"; + default: instr_mnemonic = "unknown misc-mem op"; + endcase + end + default: instr_mnemonic = "xxx"; endcase - end - default: instr_mnemonic = "xxx"; - endcase - end + end - function string registerAssign(input logic [4:0] register); - case (register) - 5'd0: registerAssign = "zero"; - 5'd1: registerAssign = "ra"; - 5'd2: registerAssign = "sp"; - 5'd3: registerAssign = "gp"; - 5'd4: registerAssign = "tp"; - 5'd5: registerAssign = "t0"; - 5'd6: registerAssign = "t1"; - 5'd7: registerAssign = "t2"; - 5'd8: registerAssign = "s0"; - 5'd9: registerAssign = "s1"; - 5'd10: registerAssign = "a0"; - 5'd11: registerAssign = "a1"; - 5'd12: registerAssign = "a2"; - 5'd13: registerAssign = "a3"; - 5'd14: registerAssign = "a4"; - 5'd15: registerAssign = "a5"; - 5'd16: registerAssign = "a6"; - 5'd17: registerAssign = "a7"; - 5'd18: registerAssign = "s2"; - 5'd19: registerAssign = "s3"; - 5'd20: registerAssign = "s4"; - 5'd21: registerAssign = "s5"; - 5'd22: registerAssign = "s6"; - 5'd23: registerAssign = "s7"; - 5'd24: registerAssign = "s8"; - 5'd25: registerAssign = "s9"; - 5'd26: registerAssign = "s10"; - 5'd27: registerAssign = "s11"; - 5'd28: registerAssign = "t3"; - 5'd29: registerAssign = "t4"; - 5'd30: registerAssign = "t5"; - 5'd31: registerAssign = "t6"; - endcase - endfunction + function string registerAssign(input logic [4:0] register); + case (register) + 5'd0: registerAssign = "zero"; + 5'd1: registerAssign = "ra"; + 5'd2: registerAssign = "sp"; + 5'd3: registerAssign = "gp"; + 5'd4: registerAssign = "tp"; + 5'd5: registerAssign = "t0"; + 5'd6: registerAssign = "t1"; + 5'd7: registerAssign = "t2"; + 5'd8: registerAssign = "s0"; + 5'd9: registerAssign = "s1"; + 5'd10: registerAssign = "a0"; + 5'd11: registerAssign = "a1"; + 5'd12: registerAssign = "a2"; + 5'd13: registerAssign = "a3"; + 5'd14: registerAssign = "a4"; + 5'd15: registerAssign = "a5"; + 5'd16: registerAssign = "a6"; + 5'd17: registerAssign = "a7"; + 5'd18: registerAssign = "s2"; + 5'd19: registerAssign = "s3"; + 5'd20: registerAssign = "s4"; + 5'd21: registerAssign = "s5"; + 5'd22: registerAssign = "s6"; + 5'd23: registerAssign = "s7"; + 5'd24: registerAssign = "s8"; + 5'd25: registerAssign = "s9"; + 5'd26: registerAssign = "s10"; + 5'd27: registerAssign = "s11"; + 5'd28: registerAssign = "t3"; + 5'd29: registerAssign = "t4"; + 5'd30: registerAssign = "t5"; + 5'd31: registerAssign = "t6"; + default: registerAssign = "UNKNOWN REGISTER"; + endcase + endfunction - function string csrRegisterAssign(input logic [11:0] csr_register); - case (csr_addr_t'(csr_register)) - MVENDORID_ADDR : csrRegisterAssign = "mvendorid"; - MARCHID_ADDR : csrRegisterAssign = "marchid"; - MIMPID_ADDR : csrRegisterAssign = "mimpid"; - MHARTID_ADDR : csrRegisterAssign = "mhartid"; - MSTATUS_ADDR : csrRegisterAssign = "mstatus"; - MISA_ADDR : csrRegisterAssign = "misa"; - MEDELEG_ADDR : csrRegisterAssign = "medeleg"; - MIDELEG_ADDR : csrRegisterAssign = "mideleg"; - MTVEC_ADDR : csrRegisterAssign = "mtvec"; - MIE_ADDR : csrRegisterAssign = "mie"; - MSCRATCH_ADDR : csrRegisterAssign = "mscratch"; - MEPC_ADDR : csrRegisterAssign = "mepc"; - MCAUSE_ADDR : csrRegisterAssign = "mcause"; - MTVAL_ADDR : csrRegisterAssign = "mtval"; - MIP_ADDR : csrRegisterAssign = "mip"; - // TODO: MAY BE ABLE TO REMOVE BELOW - MBASE_ADDR : csrRegisterAssign = "mbase"; - MBOUND_ADDR : csrRegisterAssign = "mbound"; - MIBASE_ADDR : csrRegisterAssign = "mibase"; - MIBOUND_ADDR : csrRegisterAssign = "mibound"; - MDBASE_ADDR : csrRegisterAssign = "mdbase"; - MDBOUND_ADDR : csrRegisterAssign = "mdbound"; - // TODO: MAY BE ABLE TO REMOVE ABOVE - // TODO: BELOW MUST BE REMOVED - HTIMEW_ADDR : csrRegisterAssign = "htimew"; - HTIMEHW_ADDR : csrRegisterAssign = "htimehw"; - MTIMECMP_ADDR : csrRegisterAssign = "mtimecmp"; - MTIME_ADDR : csrRegisterAssign = "mtime"; - MTIMEH_ADDR : csrRegisterAssign = "mtimeh"; - MTOHOST_ADDR : csrRegisterAssign = "mtohost"; - MFROMHOST_ADDR : csrRegisterAssign = "mfromhost"; - // TODO: ABOVE MUST BE REMOVED - default : csrRegisterAssign = "csr register not tracked"; - endcase - endfunction + function string csrRegisterAssign(input logic [11:0] csr_register); + case (csr_addr_t'(csr_register)) + MVENDORID_ADDR: csrRegisterAssign = "mvendorid"; + MARCHID_ADDR: csrRegisterAssign = "marchid"; + MIMPID_ADDR: csrRegisterAssign = "mimpid"; + MHARTID_ADDR: csrRegisterAssign = "mhartid"; + MSTATUS_ADDR: csrRegisterAssign = "mstatus"; + MISA_ADDR: csrRegisterAssign = "misa"; + MEDELEG_ADDR: csrRegisterAssign = "medeleg"; + MIDELEG_ADDR: csrRegisterAssign = "mideleg"; + MTVEC_ADDR: csrRegisterAssign = "mtvec"; + MIE_ADDR: csrRegisterAssign = "mie"; + MSCRATCH_ADDR: csrRegisterAssign = "mscratch"; + MEPC_ADDR: csrRegisterAssign = "mepc"; + MCAUSE_ADDR: csrRegisterAssign = "mcause"; + MTVAL_ADDR: csrRegisterAssign = "mtval"; + MIP_ADDR: csrRegisterAssign = "mip"; + default: csrRegisterAssign = "csr register not tracked"; + endcase + endfunction - always_ff @ (posedge CLK) begin - if (!wb_stall && instr != 0) begin - $sformat(temp_str, "core%d: 0x%h (0x%h)", CPUID, pc64, instr); - $sformat(output_str, "%s %s %s\n", temp_str, instr_mnemonic, operands); - $fwrite(fptr, output_str); + always_ff @(posedge CLK) begin + if (!wb_stall && instr != 0) begin + $sformat(temp_str, "core%d: 0x%h (0x%h)", CPUID, pc64, instr); + $sformat(output_str, "%s %s %s\n", temp_str, instr_mnemonic, operands); + $fwrite(fptr, output_str); + end end - end - final begin: CLOSE_FILE - $fclose(fptr); - end + final begin : CLOSE_FILE + $fclose(fptr); + end endmodule diff --git a/source_code/include/ahb_if.vh b/source_code/uvm_tb/ahb_master/ahb_if.vh similarity index 96% rename from source_code/include/ahb_if.vh rename to source_code/uvm_tb/ahb_master/ahb_if.vh index 4bd188a6d..e1c75bf5c 100644 --- a/source_code/include/ahb_if.vh +++ b/source_code/uvm_tb/ahb_master/ahb_if.vh @@ -7,7 +7,7 @@ `ifndef AHB_IF_VH `define AHB_IF_VH -interface ahb_if; +interface ahb_if(input logic HCLK); logic [1:0] HTRANS, HRESP; logic [2:0] HSIZE; logic [31:0] HADDR; diff --git a/source_code/uvm_tb/ahb_master/compile.sh b/source_code/uvm_tb/ahb_master/compile.sh new file mode 100755 index 000000000..a99b6ebe0 --- /dev/null +++ b/source_code/uvm_tb/ahb_master/compile.sh @@ -0,0 +1 @@ +vlog -L $QUESTA_HOME/uvm-1.2 tb_ahb.sv diff --git a/source_code/uvm_tb/ahb_master/generic_bus_if.vh b/source_code/uvm_tb/ahb_master/generic_bus_if.vh new file mode 100644 index 000000000..f4d36110e --- /dev/null +++ b/source_code/uvm_tb/ahb_master/generic_bus_if.vh @@ -0,0 +1,51 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: generic_bus_if.vh +* +* Created by: John Skubic +* Email: jskubic@purdue.edu +* Date Created: 06/01/2016 +* Description: Interface for connecting a requestor to ram. +*/ + +`ifndef GENERIC_BUS_IF_VH +`define GENERIC_BUS_IF_VH +`include "rv32i_types_pkg.sv" //for testbench purpose + +interface generic_bus_if (input logic clk); + import rv32i_types_pkg::*; + + logic [RAM_ADDR_SIZE-1:0] addr; + word_t wdata; + word_t rdata; + logic ren,wen; + logic busy; + logic [3:0] byte_en; + + modport generic_bus ( + input addr, ren, wen, wdata, byte_en, + output rdata, busy + ); + + modport cpu ( + input rdata, busy, + output addr, ren, wen, wdata, byte_en + ); + +endinterface + +`endif //GENERIC_BUS_IF_VH diff --git a/source_code/uvm_tb/ahb_master/run.sh b/source_code/uvm_tb/ahb_master/run.sh new file mode 100755 index 000000000..bc398d079 --- /dev/null +++ b/source_code/uvm_tb/ahb_master/run.sh @@ -0,0 +1 @@ +vsim -i tb_ahb -L $QUESTA_HOME/uvm-1.2 -novopt -do wave.do diff --git a/source_code/uvm_tb/ahb_master/rv32i_types_pkg.sv b/source_code/uvm_tb/ahb_master/rv32i_types_pkg.sv new file mode 100644 index 000000000..43968cc7e --- /dev/null +++ b/source_code/uvm_tb/ahb_master/rv32i_types_pkg.sv @@ -0,0 +1,206 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: rv32i_types_pkg.sv +* +* Created by: Jacob R. Stevens +* Email: steven69@purdue.edu +* Date Created: 06/01/2016 +* Description: Package containing types used for a RV32I implementation +*/ + +`ifndef RV32I_TYPES_PKG_SV +`define RV32I_TYPES_PKG_SV +package rv32i_types_pkg; + parameter WORD_SIZE = 32; + parameter RAM_ADDR_SIZE = 32; + parameter OP_W = 7; + parameter BR_W = 3; + parameter LD_W = 3; + parameter SW_W = 3; + parameter IMM_W = 3; + parameter REG_W = 3; + + typedef logic [WORD_SIZE-1:0] word_t; + + typedef enum logic [OP_W-1:0] { + LUI = 7'b0110111, + AUIPC = 7'b0010111, + JAL = 7'b1101111, + JALR = 7'b1100111, + // All branching instructions share an opcode + BRANCH = 7'b1100011, + // All load instructions share an opcode + LOAD = 7'b0000011, + // All store instructions share an opcode + STORE = 7'b0100011, + // All immediate ALU instructions share an opcode + IMMED = 7'b0010011, + // All register-register instructions share an opcode + REGREG = 7'b0110011, + // All system instructions share an opcode + SYSTEM = 7'b1110011, + MISCMEM = 7'b0001111 + } opcode_t; + + typedef enum logic [BR_W-1:0] { + BEQ = 3'b000, + BNE = 3'b001, + BLT = 3'b100, + BGE = 3'b101, + BLTU = 3'b110, + BGEU = 3'b111 + } branch_t; + + typedef enum logic [LD_W-1:0] { + LB = 3'b000, + LH = 3'b001, + LW = 3'b010, + LBU = 3'b100, + LHU = 3'b101 + } load_t; + + typedef enum logic [SW_W-1:0] { + SB = 3'b000, + SH = 3'b001, + SW = 3'b010 + } store_t; + + typedef enum logic [IMM_W-1:0] { + ADDI = 3'b000, + SLTI = 3'b010, + SLTIU = 3'b011, + XORI = 3'b100, + ORI = 3'b110, + ANDI = 3'b111, + SLLI = 3'b001, + // Logical/Arithmetic based on bit 30 of instruction + // 0 / 1 + SRI = 3'b101 + } imm_t; + + typedef enum logic [REG_W-1:0] { + // Add/Sub based on bit 30 of instruction + // 0 / 1 + ADDSUB = 3'b000, + SLL = 3'b001, + SLT = 3'b010, + SLTU = 3'b011, + XOR = 3'b100, + // Logical/Arithmetic based on bit 30 of instruction + // 0 / 1 + SR = 3'b101, + OR = 3'b110, + AND = 3'b111 + } regreg_t; + + typedef enum logic [2:0] { + // Non CSR contains ECALL, EBREAK, and xRET instructions + // ECALL/EBREAK based on bit 20 of instruction + // 0 / 1 + // xRET based on bits 28 and 29 of instruction + PRIV = 3'b000, + CSRRW = 3'b001, + CSRRS = 3'b010, + CSRRC = 3'b011, + CSRRWI = 3'b101, + CSRRSI = 3'b110, + CSRRCI = 3'b111 + } rv32i_system_t; + + typedef enum logic [11:0] { + ECALL = 12'b0000000_00000, + EBREAK = 12'b0000000_00001, + MRET = 12'b0011000_00010, + SRET = 12'b0001000_00010, + URET = 12'b0000000_00010 + } priv_insn_t; + + typedef enum logic [2:0] { + FENCE = 3'b000, + FENCEI = 3'b001 + } rv32i_miscmem_t; + + typedef struct packed { + logic [6:0] funct7; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } rtype_t; + + typedef struct packed { + logic [11:0] imm11_00; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } itype_t; + + typedef struct packed { + logic [6:0] imm11_05; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [4:0] imm04_00; + opcode_t opcode; + } stype_t; + + typedef struct packed { + logic imm12; + logic [5:0] imm10_05; + logic [4:0] rs2; + logic [4:0] rs1; + logic [2:0] funct3; + logic [3:0] imm04_01; + logic imm11; + opcode_t opcode; + } sbtype_t; + + typedef struct packed { + logic [19:0] imm31_12; + logic [4:0] rd; + opcode_t opcode; + } utype_t; + + typedef struct packed { + logic imm20; + logic [9:0] imm10_01; + logic imm11; + logic [7:0] imm19_12; + logic [4:0] rd; + opcode_t opcode; + } ujtype_t; + + typedef struct packed { + logic [11:0] csr; + logic [4:0] rs1_zimm; + logic [2:0] funct3; + logic [4:0] rd; + opcode_t opcode; + } systype_t; + + typedef struct packed { + logic token; + word_t pc; + word_t pc4; + word_t instr; + word_t prediction; + } fetch_ex_pipeline_reg_t; + +endpackage +`endif diff --git a/source_code/uvm_tb/ahb_master/tb_ahb.sv b/source_code/uvm_tb/ahb_master/tb_ahb.sv new file mode 100644 index 000000000..c23828e1b --- /dev/null +++ b/source_code/uvm_tb/ahb_master/tb_ahb.sv @@ -0,0 +1,42 @@ +//By : Zhengsen Fu +//Description : tb for ahb master but without burst mode +//Last Updated : 8/21/20 + +`include "ahb.sv" +`include "uvm_macros.svh" +`include "tb_components.svh" +import uvm_pkg::*; + +module tb_ahb (); + logic clk; + logic n_rst; + + // generate clock + initial begin + clk = 0; + forever #10 clk = !clk; + end + + ahb_if ahbif(clk); + generic_bus_if bus_if(clk); + + ahb DUT(.CLK(clk), .nRST(n_rst), .ahb_m(ahbif.ahb_m), .out_gen_bus_if(bus_if.generic_bus)); + + // reset DUT + initial begin + n_rst = 0; + @(posedge clk); + n_rst = 1; + end + + initial begin + uvm_config_db#(virtual ahb_if)::set( null, "", "ahb_vif", ahbif); + uvm_config_db#(virtual generic_bus_if)::set( null, "", "bus_vif", bus_if); + // uvm_config_db#(logic)::set( null, "", "n_rst", n_rst); + run_test("ahb_test"); // initiate test component + + end + +endmodule + + diff --git a/source_code/uvm_tb/ahb_master/tb_components.svh b/source_code/uvm_tb/ahb_master/tb_components.svh new file mode 100644 index 000000000..58e5d9e31 --- /dev/null +++ b/source_code/uvm_tb/ahb_master/tb_components.svh @@ -0,0 +1,724 @@ +//By : Zhengsen Fu +//Description : all the UVM components needed in tb_ahb.sv +//Last Updated : 8/21/20 + +`include "uvm_macros.svh" +import uvm_pkg::*; + + +class transaction extends uvm_sequence_item; + // parameter RAM_SIZE = 256; + localparam IDLE = '0; + localparam READ = 2'b01; + localparam WRITE = 2'b10; + + rand bit[1:0] trans; //trasaction type. Possible values indicated above + rand logic [31:0] wdata; + rand logic [31:0] addr; + rand logic [3:0] byte_en; + + `uvm_object_utils_begin(transaction) + `uvm_field_int(trans, UVM_DEFAULT) + `uvm_field_int(wdata, UVM_DEFAULT) + `uvm_field_int(addr, UVM_DEFAULT) + `uvm_field_int(byte_en, UVM_DEFAULT) + `uvm_object_utils_end + + constraint address_limit {byte_en == 4'b1111 -> addr <= 4'hf - 3; + byte_en == 4'b0011 || byte_en == 4'b1100 -> addr <= 4'hf - 1;} + constraint trans_constraint {trans == IDLE || trans == READ || trans == WRITE;} + constraint addr_size {addr <= 4'hf;} //testing with a simulated memory smaller than 16 registers + + function new(string name = "transaction"); + super.new(name); + endfunction: new +endclass //transaction + + +//a queue that record all the transactions. +//search function still needs debugging +class transactionSeq; //transaction sequence + localparam MAX_SIZE = 4000; + transaction arr[MAX_SIZE - 1:0]; + integer time_arr[MAX_SIZE - 1:0]; //arr which recording the time that the transaction has happened + int index; //points to most recent transactoin + function new(); + index = -1; + endfunction //new() + + //push one transaction into the arr + function void push(transaction item); + if(index == MAX_SIZE) begin //can be implemented as a queue is needed in the future + $fatal("transactionSeq: sequence cannot hold more items"); + end + index++; + arr[index] = item; + time_arr[index] = $time(); + endfunction + + //search for the most recent transaction that write to an addr + function transaction search(logic[31:0] addr); + transaction item; + for(int lcv = index; lcv > 0; lcv--) begin + item = arr[lcv]; + //if not WRITE, pass + if (item.trans != item.WRITE) begin + continue; + end + if(item.byte_en == 4'b1111) begin + if (addr == item.addr || addr == (item.addr - 1) || addr == (item.addr - 2) || addr == (item.addr - 3)) begin + return item; + end + end else if(item.byte_en == 4'b1100 || item.byte_en == 4'b0011) begin + if (addr == item.addr || addr == (item.addr - 1)) begin + return item; + end + end else begin + if (addr == item.addr) begin + return item; + end + end + end + $fatal("transaction not found!\n"); + endfunction + + //search for the index of the most recent transaction that write to an addr + function int search_index(logic[31:0] addr); + transaction item; + for(int lcv = index; lcv > 0; lcv--) begin + item = arr[lcv]; + //if not WRITE, pass + if (item.trans != item.WRITE) begin + continue; + end + if(item.byte_en == 4'b1111) begin + if (addr == item.addr || addr == (item.addr - 1) || addr == (item.addr - 2) || addr == (item.addr - 3)) begin + return lcv; + end + end else if(item.byte_en == 4'b1100 || item.byte_en == 4'b0011) begin + if (addr == item.addr || addr == (item.addr - 1)) begin + return lcv; + end + end else begin + if (addr == item.addr) begin + return lcv; + end + end + end + $fatal("transaction not found!\n"); + endfunction + + //search for the time of the most recent transaction that write to an addr + function int search_time(logic[31:0] addr); + transaction item; + for(int lcv = index; lcv > 0; lcv--) begin + item = arr[lcv]; + //if not WRITE, pass + if (item.trans != item.WRITE) begin + continue; + end + if(item.byte_en == 4'b1111) begin + if (addr == item.addr || addr == (item.addr - 1) || addr == (item.addr - 2) || addr == (item.addr - 3)) begin + return time_arr[lcv]; + end + end else if(item.byte_en == 4'b1100 || item.byte_en == 4'b0011) begin + if (addr == item.addr || addr == (item.addr - 1)) begin + return time_arr[lcv]; + end + end else begin + if (addr == item.addr) begin + return time_arr[lcv]; + end + end + end + // $fatal("transaction not found!\n"); + endfunction +endclass //transactionSeq + + + +//simulated memory that is used in the simulated slave +class sim_mem extends uvm_object; + parameter SIZE = 4; //the number of bits in the addr + rand bit [7:0] registers[2 ** SIZE - 1:0]; + + `uvm_object_utils_begin(sim_mem) + `uvm_field_sarray_int(registers, UVM_DEFAULT) + `uvm_object_utils_end + + function new(string name = "sim_mem"); + super.new(name); + endfunction: new + + //print the value in all registers for debugging purpose + function void print_all(); + for (int lcv = 0; lcv < 2**SIZE; lcv++) begin + $display("%h: %h",lcv, registers[lcv]); + end + + endfunction + + //write the least significant byte in HWDATA to addr + function void write_byte(logic[SIZE - 1:0] addr, logic[31:0] HWDATA); + registers[addr] = HWDATA[7:0]; + endfunction + + //write two least significant bytes in HWDATA to addr + function void write_half(logic[SIZE - 1:0] addr, logic[31:0] HWDATA); + registers[addr++] = HWDATA[7:0]; + registers[addr] = HWDATA[15:8]; + endfunction + + //write four least significant bytes in HWDATA to addr + function void write_word(logic[SIZE - 1:0] addr, logic[31:0] HWDATA); + registers[addr++] = HWDATA[7:0]; + registers[addr++] = HWDATA[15:8]; + registers[addr++] = HWDATA[23:16]; + registers[addr] = HWDATA[31:24]; + endfunction + + //read a byte stored in addr to the least significant byte to HRDATA + function logic[31:0] read_byte(logic[SIZE - 1:0] addr); + logic[31:0] result; + result = '0; + result[7:0] = registers[addr]; + return result; + endfunction + + //read two bytes stored in addr to the least significant byte to HRDATA + function logic[31:0] read_half(logic[SIZE - 1:0] addr); + logic[31:0] result; + result = '0; + result[7:0] = registers[addr++]; + result[15:8] = registers[addr]; + return result; + endfunction + + //read four bytes stored in addr to the least significant byte to HRDATA + function logic[31:0] read_word(logic[SIZE - 1:0] addr); + logic[31:0] result; + result = '0; + result[7:0] = registers[addr++]; + result[15:8] = registers[addr++]; + result[23:16] = registers[addr++]; + result[31:24] = registers[addr]; + return result; + endfunction +endclass //sim_mem + + + +// simulated slave interface +class sim_slave extends uvm_monitor; + `uvm_component_utils(sim_slave) + sim_mem slave_mem; + sim_mem input_mem; //simulated memory from ahb_env + logic rand_ready; //randomized wait between transactions + virtual ahb_if ahbif; + transaction prev_tx; //transaction in the previous clk cycle + logic [2:0] prev_HSIZE; //HSIZE in the previous clk cycle + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction: new + + //randomly generate a slave busy of random length + task random_wait(); + rand_ready = $urandom(); + while(rand_ready) begin + ahbif.HREADY = 0; + @(posedge ahbif.HCLK); + rand_ready = $urandom(); + end + ahbif.HREADY = 1; + endtask + + virtual function void build_phase(uvm_phase phase); + slave_mem = sim_mem::type_id::create("slave_mem"); + prev_tx = transaction::type_id::create("prev_tx"); + + // get interface + if (!uvm_config_db#(virtual ahb_if)::get(this, "", "ahb_vif", ahbif)) begin + `uvm_fatal("sim_slave", "No virtual interface specified for this monitor instance") + end + + // get sim_mem from ahb_slave + if (!uvm_config_db#(sim_mem)::get(this, "", "slave_mem", input_mem)) begin + `uvm_fatal("sim_slave", "No input mem") + end + //copy so that they will have the same initial value + slave_mem.copy(input_mem); + endfunction + + task run_phase(uvm_phase phase); + super.run_phase(phase); + ahbif.HREADY = 1; + + //TODO:missing HRESP + forever begin + @(posedge ahbif.HCLK); + // $info("SLAVE DEBUG prev trans: %h addr: %h", prev_tx.trans, prev_tx.addr); + + //data phase: responses from the slave + if(prev_tx.trans == '0) begin //if previous transaction is IDLE, do nothing + ; + end else if(prev_tx.trans == prev_tx.WRITE) begin //if previous transaction is WRITE, write HWDATA to mem + random_wait(); + #2; // wait two nano seconds before sample HWDATA + // $info("SLAVE DEBUG write addr: %h value: %h",prev_tx.addr, ahbif.HWDATA); + case(prev_HSIZE) + 3'b000: slave_mem.write_byte(prev_tx.addr, ahbif.HWDATA); //byte + 3'b001: slave_mem.write_half(prev_tx.addr, ahbif.HWDATA); //half word + 3'b010: slave_mem.write_word(prev_tx.addr, ahbif.HWDATA); //word + endcase + end else begin //if previous transaction is READ, put the data onto HRDATA + random_wait(); + case(prev_HSIZE) + 3'b000: ahbif.HRDATA = slave_mem.read_byte(prev_tx.addr); //byte + 3'b001: ahbif.HRDATA = slave_mem.read_half(prev_tx.addr); //half word + 3'b010: ahbif.HRDATA = slave_mem.read_word(prev_tx.addr); //word + endcase + // $info("SLAVE DEBUG read addr: %h value: %h size: %h",prev_tx.addr, ahbif.HRDATA, prev_HSIZE); + // slave_mem.print_all(); + end + + //addr phase: samples transaction details + #2; + if (ahbif.HTRANS == '0) begin + prev_tx.trans = prev_tx.IDLE; + end + else if (ahbif.HWRITE) begin + prev_tx.trans = prev_tx.WRITE; + end + else begin + prev_tx.trans = prev_tx.READ; + end + prev_tx.addr = ahbif.HADDR; + prev_tx.wdata = ahbif.HWDATA; + prev_HSIZE = ahbif.HSIZE; + + end + endtask + +endclass //sim_slave + + + +//simulated cpu which uses DUT (ahb master) to communicate with the simulated slave +class sim_cpu extends uvm_driver#(transaction); + `uvm_component_utils(sim_cpu) + + virtual generic_bus_if bus_if; + + uvm_analysis_port #(transaction) cpu_ap; //writes addr phase transaction to predictor + uvm_analysis_port #(transaction) response_ap; //writes data phase READ transaction to comparator + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction: new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + cpu_ap = new("cpu_ap", this); + response_ap = new("response_ap", this); + + // get interface from database + if( !uvm_config_db#(virtual generic_bus_if)::get(this, "", "bus_vif", bus_if) ) begin + // if the interface was not correctly set, raise a fatal message + `uvm_fatal("simulated cpu", "No virtual interface specified for this test instance"); + end + + endfunction: build_phase + + task run_phase(uvm_phase phase); + transaction req_item; + transaction prev; //previous addr phase transaction + transaction response; //data phase response + bit busy_wait = 0; //whether there is or not a wait within addr phase of this transaction + + prev = transaction::type_id::create("prev"); + response = transaction::type_id::create("response"); + + forever begin + seq_item_port.get_next_item(req_item); + + //if there was an wait in previous loop + //then remove an extra wait and clear busy_wait + if(!busy_wait) + @(posedge bus_if.clk); + else + busy_wait = 0; + + //at the start of the simulation, do not sending void transaction + if(prev.addr !== 'x) + cpu_ap.write(prev); + + //addr phase + if(req_item.trans == req_item.IDLE) begin + bus_if.ren = 0; + bus_if.wen = 0; + // bus_if.addr = req_item.addr; + // bus_if.wdata = req_item.wdata; + // bus_if.byte_en = req_item.byte_en; + end + else if(req_item.trans == req_item.READ) begin + bus_if.ren = 1; + bus_if.wen = 0; + bus_if.addr = req_item.addr; + // bus_if.wdata = req_item.wdata; + bus_if.byte_en = req_item.byte_en; + end + else if(req_item.trans == req_item.WRITE) begin + bus_if.ren = 0; + bus_if.wen = 1; + bus_if.addr = req_item.addr; + // bus_if.wdata = req_item.wdata; + bus_if.byte_en = req_item.byte_en; + end + + //data phase + if(prev.trans == prev.WRITE) begin + bus_if.wdata = prev.wdata; + end + + #1; + if(!bus_if.busy) begin + if(prev.trans == req_item.READ) begin + response.copy(prev); + response.wdata = bus_if.rdata; + response_ap.write(response); + end + end + + seq_item_port.item_done(); + //if busy is asserted by the slave, wait until busy is cleared + while (bus_if.busy) begin + if(req_item.trans == req_item.IDLE) begin + break; + end + @(posedge bus_if.clk); + busy_wait = 1; + if(prev.trans == req_item.READ) begin + #1; + if(!bus_if.busy) begin + // $info("CPU DEBUG rear busy reading point"); //DEBUG + response.copy(prev); + response.wdata = bus_if.rdata; + response_ap.write(response); + @(posedge bus_if.clk); + end + end + end + prev.copy(req_item); + end + endtask + +endclass //sim_cpu + + + +class ahb_seq extends uvm_sequence #(transaction); + `uvm_object_utils(ahb_seq) + + int number; + + function new(string name = ""); + super.new(name); + endfunction: new + + task body(); + transaction req_item; + req_item = transaction::type_id::create("req_item"); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.READ; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.WRITE; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.WRITE; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.IDLE; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.WRITE; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.READ; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.READ; + finish_item(req_item); + + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + req_item.trans = req_item.IDLE; + finish_item(req_item); + + + number = 0; + repeat(3000) begin + start_item(req_item); + if(!req_item.randomize()) begin + // if the transaction is unable to be randomized, send a fatal message + `uvm_fatal("ahb_seq", "not able to randomize") + end + number++; + $info("DEBUG: item sent! %d", number); + + finish_item(req_item); + end + $info("DEBUG: reached end"); + endtask: body +endclass //ahb_seq + + + +class sequencer extends uvm_sequencer#(transaction); + `uvm_component_utils(sequencer) + function new(input string name= "sequencer", uvm_component parent=null); + super.new(name, parent); + endfunction : new +endclass : sequencer + + + +class ahb_agent extends uvm_agent; + `uvm_component_utils(ahb_agent) + sim_cpu cpu; + sim_slave slave; + sequencer sqr; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + sqr = sequencer::type_id::create("sqr", this); + slave = sim_slave::type_id::create("slave", this); + cpu = sim_cpu::type_id::create("cpu", this); + endfunction + + virtual function void connect_phase(uvm_phase phase); + cpu.seq_item_port.connect(sqr.seq_item_export); + endfunction +endclass //ahb_agent + + + +class predictor extends uvm_subscriber #(transaction); + `uvm_component_utils(predictor) + uvm_analysis_port #(transaction) pred_ap; + sim_mem mem; + logic [2:0] HSIZE; + transactionSeq tx_seq; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction: new + + function void build_phase(uvm_phase phase); + pred_ap = new("pred_ap", this); + endfunction + + function void write(transaction t); + tx_seq.push(t); + //TODO: need to verify + if(t.byte_en == 4'b1111) + HSIZE = 3'b010; // word + else if(t.byte_en == 4'b1100 || t.byte_en == 4'b0011) + HSIZE = 3'b001; // half word + else + HSIZE = 3'b000; // byte + + + if(t.trans == t.WRITE) begin + case(HSIZE) + 3'b000: mem.write_byte(t.addr, t.wdata); //byte + 3'b001: mem.write_half(t.addr, t.wdata); //half word + 3'b010: mem.write_word(t.addr, t.wdata); //word + endcase + + // $info("PREDICTOR DEBUG write addr: %h value: %h size: %h",t.addr, t.wdata, HSIZE); + + // $display("predictor"); + // $display("predictor mem"); + // mem.print_all(); + // $display(""); + end + endfunction +endclass //predictor + + + +class comparator extends uvm_scoreboard; + `uvm_component_utils(comparator) + uvm_analysis_export #(transaction) response_export; + uvm_tlm_analysis_fifo #(transaction) response_fifo; + sim_mem mem; + logic [2:0] HSIZE; + transaction response; + logic [31:0] expected; + transactionSeq tx_seq; + + int m_matches, m_mismatches; // records number of matches and mismatches + + function new( string name , uvm_component parent) ; + super.new( name , parent ); + m_matches = 0; + m_mismatches = 0; + endfunction + + function void connect_phase(uvm_phase phase); + response_export.connect(response_fifo.analysis_export); + endfunction + + function void build_phase( uvm_phase phase ); + response_export = new("response_export", this); + response_fifo = new("response_fifo", this); + endfunction + + task run_phase(uvm_phase phase); + int error_tx_time; //time that error write transaction happens + forever begin + response_fifo.get(response); + if(response.byte_en == 4'b1111) + HSIZE = 3'b010; // word + else if(response.byte_en == 4'b1100 || response.byte_en == 4'b0011) + HSIZE = 3'b001; // half word + else + HSIZE = 3'b000; // byte + + case(HSIZE) + 3'b000: expected = mem.read_byte(response.addr); //byte + 3'b001: expected = mem.read_half(response.addr); //half word + 3'b010: expected = mem.read_word(response.addr); //word + endcase + + if(expected == response.wdata) begin + m_matches++; + uvm_report_info("Comparator", "Data Match"); + end else begin + m_mismatches++; + uvm_report_error("Comparator", "Error: Data Mismatch"); + + $display("comparator"); + $info("response: %h", response.wdata); + $display("expected addr:%h value: %h size: %h byte_en: %b", response.addr , expected, HSIZE, response.byte_en); + + error_tx_time = tx_seq.search_time(response.addr); + $info("error transaction happens at %d", error_tx_time); + end + end + endtask + + function void report_phase(uvm_phase phase); + uvm_report_info("Comparator", $sformatf("Matches: %0d", m_matches)); + uvm_report_info("Comparator", $sformatf("Mismatches: %0d", m_mismatches)); + endfunction + +endclass //comparator + +class ahb_env extends uvm_env; + `uvm_component_utils(ahb_env) + ahb_agent agt; + comparator comp; // scoreboard + predictor pred; + sim_mem mem; + transactionSeq tx_seq; //to record and lookup transactions + + function new(string name = "env", uvm_component parent = null); + super.new(name, parent); + endfunction + + function void build_phase(uvm_phase phase); + agt = ahb_agent::type_id::create("agt", this); + comp = comparator::type_id::create("comp", this); + pred = predictor::type_id::create("pred", this); + tx_seq = new(); + mem = new(); + if(!mem.randomize()) begin + `uvm_fatal("environment", "not able to randomize mem") + end + pred.mem = mem; + comp.mem = mem; + uvm_config_db#(sim_mem)::set( null, "", "slave_mem", mem); + pred.tx_seq = tx_seq; + comp.tx_seq = tx_seq; + endfunction + + function void connect_phase(uvm_phase phase); + agt.cpu.cpu_ap.connect(pred.analysis_export); + agt.cpu.response_ap.connect(comp.response_export); + endfunction + +endclass //ahb_env + + + +class ahb_test extends uvm_test; + `uvm_component_utils(ahb_test) + + ahb_env env; + ahb_seq seq; + + function new(string name = "ahb_test", uvm_component parent); + super.new(name, parent); + endfunction: new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + env = ahb_env::type_id::create("env",this); + seq = ahb_seq::type_id::create("seq"); + endfunction: build_phase + + task run_phase(uvm_phase phase); + phase.raise_objection( this, "Starting sequence in main phase" ); + $display("%t Starting sequence run_phase",$time); + + seq.start(env.agt.sqr); + #100ns; + phase.drop_objection( this , "Finished in main phase" ); + endtask + +endclass //ahb_test + diff --git a/source_code/wscript b/source_code/wscript deleted file mode 100644 index d69e74d76..000000000 --- a/source_code/wscript +++ /dev/null @@ -1,78 +0,0 @@ -def configure(ctx): - rvb_srcs = ['caches', 'branch_predictors', 'bus_bridges', 'trackers', 'risc_mgmt', 'privs', 'pipelines', 'standard_core', 'ram'] - ctx.SFFUnits.add('RISCVBusiness_packages', - src_dir = ['packages'] - ) - ctx.SFFUnits.add('RISCVBusiness', - includes = ['include'], - src_dir = rvb_srcs, - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_RISCVBusiness', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('RISCVBusiness_self_test', - includes = ['include'], - src_dir = rvb_srcs, - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_RISCVBusiness_self_test', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('alu', - includes = ['include'], - src_dir = ['standard_core'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_alu', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('ahb_master', - includes = ['include'], - src_dir = ['bus_bridges', 'standard_core', 'ram'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_ahb_master', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('memory_controller', - includes = ['include'], - src_dir = ['bus_bridges', 'standard_core', 'ram'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_memory_controller', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('risc_mgmt', - includes = ['include'], - src_dir = ['risc_mgmt'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_risc_mgmt', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('shift_add_multiplier', - includes = ['include'], - src_dir = ['risc_mgmt'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_shift_add_multiplier', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('shift_test_restore_divider', - includes = ['include'], - src_dir = ['risc_mgmt'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_shift_test_restore_divider', - tb_includes = ['include'] - ) - ctx.SFFUnits.add('caches', - includes = ['include'], - src_dir = ['ram', 'bus_bridges', 'standard_core', 'caches'], - use = ['RISCVBusiness_packages'], - tb_use = ['RISCVBusiness_packages'], - tb = 'tb_caches', - tb_includes = ['include'] - ) - diff --git a/sparce.yml b/sparce.yml new file mode 100644 index 000000000..a71cc82d7 --- /dev/null +++ b/sparce.yml @@ -0,0 +1,32 @@ +# ISA Configurations +isa_params: + xlen : 32 + +# Microarchitectural Configurations +microarch_params: + # Branch/Jump Configurations + br_predictor_type : "not_taken" + + # Cache configurations + cache_config : "separate" + dcache_type : "pass_through" + icache_type : "pass_through" + + # Bus configurations + bus_endianness : "big" + bus_interface_type : "generic_bus_if" + + # Sparisty Optimizations + sparce_enabled : "enabled" + + # RV32C + rv32c_enabled : "disabled" + + # Halt + infinite_loop_halts : "true" + +# RISC-MGMT Extension Configuration +risc_mgmt_params: + standard_extensions: + nonstandard_extensions: + diff --git a/tb_core.cc b/tb_core.cc new file mode 100644 index 000000000..d1d89f8ab --- /dev/null +++ b/tb_core.cc @@ -0,0 +1,332 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "verilated.h" +#include "verilated_fst_c.h" +#include "Vtop_core.h" +#include "Vtop_core_top_core.h" + +#define MTIME_ADDR 0xFFFFFFE0 +#define MTIMEH_ADDR 0xFFFFFFE4 +#define MTIMECMP_ADDR 0xFFFFFFE8 +#define MTIMECMPH_ADDR 0xFFFFFFEC +#define MSIP_ADDR 0xFFFFFFF0 +#define EXT_ADDR_SET 0xFFFFFFF4 +#define EXT_ADDR_CLEAR 0xFFFFFFF8 +#define MAGIC_ADDR 0xFFFFFFFC +#define BUS_ERROR_TOP 0x20000000 + +// Inclusive range of memory-mapped peripherals +#define MMIO_RANGE_BEGIN (MTIME_ADDR) +#define MMIO_RANGE_END (MAGIC_ADDR) + +// doubles as mtime counter +vluint64_t sim_time = 0; +Vtop_core *dut_ptr; +VerilatedFstC *trace_ptr; + +/* + * Emulate memory-mapped CSRs + */ +uint64_t mtimecmp = 0; +uint32_t msip = 0; + +// Interrupt signals +bool ext_int = false; + +void signal_handler(int signum) { + std::cout << "Got signal " << signum << std::endl; + dut_ptr->final(); + trace_ptr->close(); + exit(1); +} + +class MemoryMap { +private: + + // NOTE: Assuming 0 for uninitialized memory + // This is because bare-metal tests may not set up bss, + // but the program will nonetheless have a bss section. + const uint32_t c_default_value = 0x00000000; + const char *dumpfile = "memsim.dump"; + std::map mmap; + +protected: + inline uint32_t expand_mask(uint8_t mask) { + uint32_t acc = 0; + for(int i = 0; i < 4; i++) { + auto bit = ((mask & (1 << i)) != 0); + if(bit) { + acc |= (0xFF << (i * 8)); + } + } + + return acc; + } + +public: + + MemoryMap(const char *fname) { + uint32_t address = 0x80000000; + std::ifstream myFile(fname, std::ios::in | std::ios::binary); + if(!myFile) { + std::ostringstream ss; + ss << "Couldn't open " << fname << std::endl; + throw ss.str(); + } + + while(!myFile.eof()) { + uint32_t data; + myFile.read((char *)&data, sizeof(data)); + + mmap.insert(std::make_pair(address, data)); + + address += 4; + } + } + + uint32_t mmio_read(uint32_t addr) { + uint32_t value = 0xBAD1BAD1; + switch(addr) { + case MTIME_ADDR: value = (uint32_t)(sim_time & 0xFFFFFFFF); + break; + case MTIMEH_ADDR: value = (uint32_t)(sim_time >> 32); + break; + case MTIMECMP_ADDR: value = (uint32_t)(mtimecmp & 0xFFFFFFFF); + break; + case MTIMECMPH_ADDR: value = (uint32_t)(mtimecmp >> 32); + break; + case MSIP_ADDR: value = msip; + break; + case EXT_ADDR_SET: value = ext_int; // Marker value for peripheral memory + break; + case EXT_ADDR_CLEAR: value = 0; // Clear 'register' doesn't return a value + break; + case MAGIC_ADDR: value = 0; // TODO: Should this be able to take stdin inputs? Any use? + break; + default: std::cout << "Warning: read of empty MMIO region @ " << std::hex << addr << std::dec << std::endl; + }; + + + return value; + } + + inline static bool is_mmio_region(uint32_t addr) { + return (addr >= MMIO_RANGE_BEGIN && addr <= MMIO_RANGE_END); + } + + void mmio_write(uint32_t addr, uint32_t value, uint8_t mask) { + auto mask_exp = expand_mask(mask); + + if(addr == MTIMECMP_ADDR) { + uint32_t mtimecmp_l = (uint32_t)(mtimecmp & 0xFFFFFFFF); + mtimecmp_l = (value & mask_exp) | (mtimecmp_l & ~mask_exp); + mtimecmp = (mtimecmp & 0xFFFFFFFF00000000) | ((uint64_t)mtimecmp_l); + } else if(addr == MTIMECMPH_ADDR) { + uint32_t mtimecmp_h = (uint32_t)(mtimecmp >> 32); + mtimecmp_h = (value & mask_exp) | (mtimecmp_h & ~mask_exp); + mtimecmp = (mtimecmp & 0xFFFFFFFF) | (((uint64_t)mtimecmp_h) << 32); + } else if(addr == MSIP_ADDR) { + msip = value; + } else if(addr == EXT_ADDR_SET) { + ext_int = true; + } else if(addr == EXT_ADDR_CLEAR) { + ext_int = false; + } else if(addr == MAGIC_ADDR) { + std::cout << (char)(value & 0xFF); + } else { + std::cout << "Warning: write to read-only MMIO region @ " << std::hex << addr << std::dec << std::endl; + } + } + + // TODO: Add simulation for SWI/mtime? + uint32_t read(uint32_t addr) { + auto it = mmap.find(addr); + if(it != mmap.end()) { + return it->second; + } else { + return c_default_value; + } + } + + void write(uint32_t addr, uint32_t value, uint8_t mask) { + // NOTE: For now, assuming that all memory is legally acessible. + auto it = mmap.find(addr); + if(it != mmap.end()) { + auto mask_exp = expand_mask(mask); + it->second = (value & mask_exp) | (it->second & ~mask_exp); + } else { + mmap.insert(std::make_pair(addr, value)); + } + } + + void dump() { + std::ofstream outfile; + outfile.open(dumpfile); + if(!outfile) { + std::ostringstream ss; + ss << "Couldn't open " << dumpfile << std::endl; + throw ss.str(); + } + + // Account for endianness + for(auto p : mmap) { + if(p.second != 0) { + char buf[80]; + snprintf(buf, 80, "%08x : %02x%02x%02x%02x", p.first, + (p.second & 0xFF000000) >> 24, + (p.second & 0x00FF0000) >> 16, + (p.second & 0x0000FF00) >> 8, + p.second & 0x000000FF); + outfile << buf << std::endl; + } + } + } +}; + +void update_interrupt_signals(Vtop_core& dut) { + if(msip & 0x1) { + dut.soft_int = 1; + } else if(dut.soft_int) { + dut.soft_int = 0; + dut.soft_int_clear = 1; + } else if(dut.soft_int_clear) { + dut.soft_int_clear = 0; + } + + if(mtimecmp <= sim_time) { + dut.timer_int = 1; + } else if(dut.timer_int) { + dut.timer_int = 0; + dut.timer_int_clear = 1; + } else if(dut.timer_int_clear) { + dut.timer_int_clear = 0; + } + + if(ext_int) { + dut.ext_int = 1; + } else if(dut.ext_int) { + dut.ext_int = 0; + dut.ext_int_clear = 1; + } else if(dut.ext_int_clear) { + dut.ext_int_clear = 0; + } +} + +void tick(Vtop_core& dut, VerilatedFstC& trace) { + dut.CLK = 0; + dut.eval(); + trace.dump(sim_time); + sim_time++; + dut.CLK = 1; + dut.eval(); + trace.dump(sim_time); + sim_time++; +} + +void reset(Vtop_core& dut, VerilatedFstC& trace) { + // Initialize signals + dut.CLK = 0; + dut.nRST = 0; + dut.ext_int = 0; + dut.ext_int_clear = 0; + dut.soft_int = 0; + dut.soft_int_clear = 0; + dut.timer_int = 0; + dut.timer_int_clear = 0; + dut.busy = 1; + dut.rdata = 0; + dut.mtime = 0; + + tick(dut, trace); + dut.nRST = 0; + tick(dut, trace); + dut.nRST = 1; + tick(dut, trace); +} + + +int main(int argc, char **argv) { + + const char *fname; + + if(argc < 2) { + std::cout << "Warning: No bin file name provided, assuming './meminit.bin' as file location!" << std::endl; + fname = "meminit.bin"; + } else { + fname = argv[1]; + } + + MemoryMap memory(fname); + + Vtop_core dut; + + Verilated::traceEverOn(true); + VerilatedFstC m_trace; + dut.trace(&m_trace, 5); + m_trace.open("waveform.fst"); + + mtimecmp = 0xFFFFFFFFFFFFFFFF; // Default to a massive value + + + dut_ptr = &dut; + trace_ptr = &m_trace; + + signal(SIGINT, signal_handler); + + + reset(dut, m_trace); + while(!dut.halt && sim_time < 100000) { + dut.error = 0; + // TODO: Variable latency + if((dut.ren || dut.wen) && dut.busy) { + dut.busy = 0; + if(dut.ren) { + uint32_t addr = dut.addr & 0xFFFFFFFC; + if(addr < BUS_ERROR_TOP) { + dut.error = 1; + }else if(!MemoryMap::is_mmio_region(addr)) { + dut.rdata = memory.read(addr); + } else { + dut.rdata = memory.mmio_read(addr); + } + } else if(dut.wen) { + uint32_t addr = dut.addr & 0xFFFFFFFC; + uint32_t value = dut.wdata; + uint8_t mask = dut.byte_en; + if(addr < BUS_ERROR_TOP) { + dut.error = 1; + } else if(!MemoryMap::is_mmio_region(addr)) { + memory.write(addr, value, mask); + } else { + memory.mmio_write(addr, value, mask); + } + } + } else if(!dut.busy) { + dut.busy = 1; + } + + dut.mtime = sim_time; + + tick(dut, m_trace); + update_interrupt_signals(dut); + } + + if(sim_time >= 100000) { + std::cout << "Test TIMED OUT" << std::endl; + } else if(dut.top_core->get_x28() == 1) { + std::cout << "Test PASSED" << std::endl; + } else { + std::cout << "Test FAILED: Test " << dut.top_core->get_x28() << std::endl; + } + m_trace.close(); + memory.dump(); + dut.final(); + + return 0; +} diff --git a/tb_core_no_memory.cc b/tb_core_no_memory.cc new file mode 100644 index 000000000..7a1d05a66 --- /dev/null +++ b/tb_core_no_memory.cc @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "verilated.h" +#include "verilated_fst_c.h" +#include "Vtop_core.h" +#include "Vtop_core_top_core.h" + +#define MTIME_ADDR 0xFFFFFFE0 +#define MTIMEH_ADDR 0xFFFFFFE4 +#define MTIMECMP_ADDR 0xFFFFFFE8 +#define MTIMECMPH_ADDR 0xFFFFFFEC +#define MSIP_ADDR 0xFFFFFFF0 +#define EXT_ADDR_SET 0xFFFFFFF4 +#define EXT_ADDR_CLEAR 0xFFFFFFF8 +#define MAGIC_ADDR 0xFFFFFFFC + +// Inclusive range of memory-mapped peripherals +#define MMIO_RANGE_BEGIN (MTIME_ADDR) +#define MMIO_RANGE_END (MAGIC_ADDR) + +// Predeclare class -- TODO: Move MemoryMap into a header file +class MemoryMap; + +// doubles as mtime counter +vluint64_t sim_time = 0; +Vtop_core *dut_ptr; +VerilatedFstC *trace_ptr; +MemoryMap *mem_ptr; + +/* + * Emulate memory-mapped CSRs + */ +uint64_t mtimecmp = 0; +uint32_t msip = 0; + +// Interrupt signals +bool ext_int = false; + +void signal_handler(int signum) { + std::cout << "Got signal " << signum << std::endl; + dut_ptr->final(); + trace_ptr->close(); + exit(1); +} + +class MemoryMap { +private: + + // NOTE: Assuming 0 for uninitialized memory + // This is because bare-metal tests may not set up bss, + // but the program will nonetheless have a bss section. + const uint32_t c_default_value = 0x00000000; + const char *dumpfile = "memsim.dump"; + std::map mmap; + +protected: + inline uint32_t expand_mask(uint8_t mask) { + uint32_t acc = 0; + for(int i = 0; i < 4; i++) { + auto bit = ((mask & (1 << i)) != 0); + if(bit) { + acc |= (0xFF << (i * 8)); + } + } + + return acc; + } + +public: + + MemoryMap(const char *fname) { + uint32_t address = 0x80000000; + std::ifstream myFile(fname, std::ios::in | std::ios::binary); + if(!myFile) { + std::ostringstream ss; + ss << "Couldn't open " << fname << std::endl; + throw ss.str(); + } + + while(!myFile.eof()) { + uint32_t data; + myFile.read((char *)&data, sizeof(data)); + + mmap.insert(std::make_pair(address, data)); + + address += 4; + } + } + + uint32_t mmio_read(uint32_t addr) { + uint32_t value = 0xBAD1BAD1; + switch(addr) { + case MTIME_ADDR: value = (uint32_t)(sim_time & 0xFFFFFFFF); + break; + case MTIMEH_ADDR: value = (uint32_t)(sim_time >> 32); + break; + case MTIMECMP_ADDR: value = (uint32_t)(mtimecmp & 0xFFFFFFFF); + break; + case MTIMECMPH_ADDR: value = (uint32_t)(mtimecmp >> 32); + break; + case MSIP_ADDR: value = msip; + break; + case EXT_ADDR_SET: value = ext_int; // Marker value for peripheral memory + break; + case EXT_ADDR_CLEAR: value = 0; // Clear 'register' doesn't return a value + break; + case MAGIC_ADDR: value = 0; // TODO: Should this be able to take stdin inputs? Any use? + break; + default: std::cout << "Warning: read of empty MMIO region @ " << std::hex << addr << std::dec << std::endl; + }; + + + return value; + } + + inline static bool is_mmio_region(uint32_t addr) { + return (addr >= MMIO_RANGE_BEGIN && addr <= MMIO_RANGE_END); + } + + void mmio_write(uint32_t addr, uint32_t value, uint8_t mask) { + auto mask_exp = expand_mask(mask); + + if(addr == MTIMECMP_ADDR) { + uint32_t mtimecmp_l = (uint32_t)(mtimecmp & 0xFFFFFFFF); + mtimecmp_l = (value & mask_exp) | (mtimecmp_l & ~mask_exp); + mtimecmp = (mtimecmp & 0xFFFFFFFF00000000) | ((uint64_t)mtimecmp_l); + } else if(addr == MTIMECMPH_ADDR) { + uint32_t mtimecmp_h = (uint32_t)(mtimecmp >> 32); + mtimecmp_h = (value & mask_exp) | (mtimecmp_h & ~mask_exp); + mtimecmp = (mtimecmp & 0xFFFFFFFF) | (((uint64_t)mtimecmp_h) << 32); + } else if(addr == MSIP_ADDR) { + msip = value; + } else if(addr == EXT_ADDR_SET) { + ext_int = true; + } else if(addr == EXT_ADDR_CLEAR) { + ext_int = false; + } else if(addr == MAGIC_ADDR) { + std::cout << (char)(value & 0xFF); + } else { + std::cout << "Warning: write to read-only MMIO region @ " << std::hex << addr << std::dec << std::endl; + } + } + + // TODO: Add simulation for SWI/mtime? + uint32_t read(uint32_t addr) { + auto it = mmap.find(addr); + if(it != mmap.end()) { + return it->second; + } else { + return c_default_value; + } + } + + void write(uint32_t addr, uint32_t value, uint8_t mask) { + // NOTE: For now, assuming that all memory is legally acessible. + auto it = mmap.find(addr); + if(it != mmap.end()) { + auto mask_exp = expand_mask(mask); + it->second = (value & mask_exp) | (it->second & ~mask_exp); + } else { + mmap.insert(std::make_pair(addr, value)); + } + } + + void dump() { + std::ofstream outfile; + outfile.open(dumpfile); + if(!outfile) { + std::ostringstream ss; + ss << "Couldn't open " << dumpfile << std::endl; + throw ss.str(); + } + + // Account for endianness + for(auto p : mmap) { + if(p.second != 0) { + char buf[80]; + snprintf(buf, 80, "%08x : %02x%02x%02x%02x", p.first, + (p.second & 0xFF000000) >> 24, + (p.second & 0x00FF0000) >> 16, + (p.second & 0x0000FF00) >> 8, + p.second & 0x000000FF); + outfile << buf << std::endl; + } + } + } +}; + +void update_interrupt_signals(Vtop_core& dut) { + if(msip & 0x1) { + dut.soft_int = 1; + } else if(dut.soft_int) { + dut.soft_int = 0; + dut.soft_int_clear = 1; + } else if(dut.soft_int_clear) { + dut.soft_int_clear = 0; + } + + if(mtimecmp <= sim_time) { + dut.timer_int = 1; + } else if(dut.timer_int) { + dut.timer_int = 0; + dut.timer_int_clear = 1; + } else if(dut.timer_int_clear) { + dut.timer_int_clear = 0; + } + + if(ext_int) { + dut.ext_int = 1; + } else if(dut.ext_int) { + dut.ext_int = 0; + dut.ext_int_clear = 1; + } else if(dut.ext_int_clear) { + dut.ext_int_clear = 0; + } +} + +void tick(Vtop_core& dut, VerilatedFstC& trace) { + dut.CLK = 0; + dut.eval(); + trace.dump(sim_time); + sim_time++; + dut.CLK = 1; + dut.eval(); + trace.dump(sim_time); + sim_time++; +} + +void reset(Vtop_core& dut, VerilatedFstC& trace) { + // Initialize signals + dut.CLK = 0; + dut.nRST = 0; + dut.ext_int = 0; + dut.ext_int_clear = 0; + dut.soft_int = 0; + dut.soft_int_clear = 0; + dut.timer_int = 0; + dut.timer_int_clear = 0; + dut.i_busy = 1; + dut.i_rdata = 0; + dut.d_busy = 1; + dut.d_rdata = 1; + + tick(dut, trace); + dut.nRST = 0; + tick(dut, trace); + dut.nRST = 1; + tick(dut, trace); +} + +uint32_t do_memory_txn(uint8_t ren, uint8_t wen, uint32_t addr, uint8_t byte_en, uint32_t wdata) { + assert(ren || wen); + if(ren) { + uint32_t addr_in = addr & 0xFFFFFFFC; + if(!MemoryMap::is_mmio_region(addr_in)) { + return mem_ptr->read(addr_in); + } else { + return mem_ptr->mmio_read(addr_in); + } + } else if(wen) { + uint32_t addr_in = addr & 0xFFFFFFFC; + uint32_t value = wdata; + uint8_t mask = byte_en; + if(!MemoryMap::is_mmio_region(addr_in)) { + mem_ptr->write(addr_in, value, mask); + } else { + mem_ptr->mmio_write(addr_in, value, mask); + } + + return 0x0; + } else { + return 0x0; + } +} + + +int main(int argc, char **argv) { + + const char *fname; + + if(argc < 2) { + std::cout << "Warning: No bin file name provided, assuming './meminit.bin' as file location!" << std::endl; + fname = "meminit.bin"; + } else { + fname = argv[1]; + } + + std::cout << "Running no-memory controller TB" << std::endl; + + MemoryMap memory(fname); + + Vtop_core dut; + + Verilated::traceEverOn(true); + VerilatedFstC m_trace; + dut.trace(&m_trace, 5); + m_trace.open("waveform.fst"); + + dut_ptr = &dut; + trace_ptr = &m_trace; + mem_ptr = &memory; + + signal(SIGINT, signal_handler); + + + reset(dut, m_trace); + + + while(!dut.halt && sim_time < 100000) { + // TODO: Variable latency + // Service data first to simulate forwarding from data write to instruction read (required for fence.i w/o caches) + if(dut.d_ren || dut.d_wen) { + dut.d_rdata = do_memory_txn(dut.d_ren, dut.d_wen, dut.d_addr, dut.d_byte_en, dut.d_wdata); + dut.d_busy = 0; + } else { + dut.d_rdata = 0xBAD1BAD1; + dut.d_busy = 1; + } + + if(dut.i_ren || dut.i_wen) { + dut.i_rdata = do_memory_txn(dut.i_ren, dut.i_wen, dut.i_addr, dut.i_byte_en, dut.i_wdata); + dut.i_busy = 0; + } else { + dut.i_rdata = 0xBAD1BAD1; + dut.i_busy = 1; + } + + + tick(dut, m_trace); + update_interrupt_signals(dut); + } + + if(sim_time >= 100000) { + std::cout << "Test TIMED OUT" << std::endl; + } else if(dut.top_core->get_x28() == 1) { + std::cout << "Test PASSED" << std::endl; + } else { + std::cout << "Test FAILED: Test " << dut.top_core->get_x28() << std::endl; + } + m_trace.close(); + memory.dump(); + dut.final(); + + return 0; +} diff --git a/updated_run_tests.py b/updated_run_tests.py new file mode 100644 index 000000000..3371a3133 --- /dev/null +++ b/updated_run_tests.py @@ -0,0 +1,435 @@ +#!/usr/bin/python3 + +# +# Copyright 2022 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: updated_run_tests.py +# +# Created by: Nicholas Gildenhuys +# Email: ngildenh@purdue.edu +# Date Created: 03/29/2022 +# Description: A better script for running processor tests + + +######## Library Imports ######## +import argparse +from asyncio.log import logger +import sys +import os +import re +from typing import List, Type +import logging +import subprocess +import pathlib +import glob +import json +import itertools + + +######## Globals ######## +END_COLOR = "\033[0m" +GREEN = "\033[92m" +RED = "\033[31m" +#WAF_LOGFILE = "waf_output.log" +#BUILD_LOGFILE = "build_log.log" +DEFAULT_CONFIG_FILE = "run_tests_config.json" +MEMINIT_HEX_FILE = "meminit.hex" +ELF2HEX_COMAND = "/home/ecegrid/a/socpub/Public/riscv_dev/riscv_installs/RV_current/bin/elf2hex" +FAILED = "Failed" +PASSED = "Passed" + +######## Error Code Exception Class ######## +class Error(Exception): + """A custom exception class for raising exceptions from sub processes""" + def __init__(self, error_string: str): + self.error_string = error_string + def __str__(self) -> str: + return self.error_string + +class run_config(): + def __init__(self, config_json:dict): + # top level info + self.arch = str(config_json["arch"]) + self.abi = str(config_json["abi"]) + self.xlen = str(config_json["xlen"]) + self.test_type = str(config_json["test_type"]) + self.top_level = str(config_json["top_level"]) + # read directory definitions + self.build_dir = pathlib.Path(config_json["build_dir"]) + self.verif_dir = pathlib.Path(config_json["verif_dir"]) + self.asm_env = pathlib.Path(config_json["asm_env"]) + # directory of all where the test files are + self.test_dir = self.verif_dir/self.test_type/self.arch + + # output directories + self.sim_dir = pathlib.Path(config_json["sim_dir"]) + self.out_dir = self.sim_dir/self.arch + + # file definitions + self.link_file = self.verif_dir/pathlib.Path("asm-env")/config_json["link_file"] + # the cache file name, expects it in same as run directory + self.cache_file = pathlib.Path(config_json["cache_file"]) + # location of the test files + if(not self.test_dir.exists()): + raise Error(f"Test Directory: {self.test_dir} does not exists") + # init for apending later + self.test_filepaths = [] + self.test_filenames = config_json["test_filenames"] + return + +######## Bulk Work Functions ######## +def run_tests(config: Type[run_config]) -> List[str]: + """ + Should return the list of tests that were failed + """ + print(f"Running Strings: {config.test_filepaths}") + # setup the sim out directory + if(not config.out_dir.exists()): + os.makedirs(config.out_dir) + + # setup the loggers + # the log names don't matter as they will be replaced in the for loop + build_logger = setup_logger("build_logger", pathlib.Path("./build_log.log")) + waf_logger = setup_logger("waf_logger", pathlib.Path("./waf_log.log")) + + # setup caching dict mechanism + test_status = dict() + # read in the cached results if possible + if(pathlib.Path(config.cache_file).exists()): + with open(config.cache_file, "r") as cache_fp: + test_status = json.load(cache_fp) + + # run the tests + try: + for file in config.test_filepaths: + filepath = pathlib.Path(file) + # check if there is a cached result + test_cached_result = FAILED + if(filepath.stem in test_status): + test_cached_result = test_status[filepath.stem] + + # skip files that begin with _ + # or files that have already passed + if(filepath.name[0] == '_' or test_cached_result == PASSED): + print(f"Skipping Test: {filepath.name}") + continue + print(f"-----------------------------") + print(f"Running Test: {filepath.name}") + # setup the test output directory if it is not there + test_out_dir = config.out_dir/filepath.stem + # setup the sim out folder + if(not test_out_dir.exists()): + os.makedirs(test_out_dir) + # update the loggers + build_log_filepath = test_out_dir/str(filepath.stem+"_build.log") + build_logger = change_logger_file_handlers(build_logger, build_log_filepath) + waf_log_filepath = test_out_dir/str(filepath.stem+"_waf.log") + waf_logger = change_logger_file_handlers(waf_logger, waf_log_filepath) + + # compile the assembly file + print(f" - Compiling...") + outpath = test_out_dir/str(filepath.stem+".elf") + hex_filepath = compile_asm(filepath, outpath, config, build_logger) + # clean up the hex file - needs writting + clean_init_hex(hex_filepath) + print(f" - Running Waf...") + # run self sim - basic done + run_sim(config.top_level, waf_logger) + waf_log_filepath = pathlib.Path(waf_logger.handlers[0].baseFilename) + print(f" - Checking Results...") + # check results - + result = check_self_results(waf_log_filepath, build_logger) + # cache the result in the dict + test_status[filepath.stem] = result + #print(test_status) + # catch keyboard interrupts to flush the cache file + except KeyboardInterrupt: + pass # yes I know that this is not the pest practice + + with open(config.cache_file, "w") as cache_fp: + json.dump(test_status, cache_fp) + + return + + +# compile the assembly file - done +def compile_asm(filepath: Type[pathlib.Path], outpath: Type[pathlib.Path],\ + config: Type[run_config], logger: Type[logging.Logger])\ + -> Type[pathlib.Path]: + # main compile arguments + # notes: need to parameratize the .T, .I, abi, and xlen flags + # also probably pass the filepath too + compile_cmd_arr = ["riscv64-unknown-elf-gcc", + "-march=" + config.xlen, "-mabi=" + config.abi, + "-static", "-mcmodel=medany", "-fvisibility=hidden", + "-nostdlib", "-nostartfiles", + "-T"+str(config.link_file), + "-I"+str(config.asm_env), str(filepath), "-o", + str(outpath)] + + log_header("riscv64-unknown-elf-gcc", logger) + try: + compile_process: Type[subprocess.CompletedProcess] = subprocess.run(compile_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except subprocess.CalledProcessError as p_error: + compile_process = p_error + finally: + log_subprocess(compile_process, logger) + + + # create the mem init file + # NOTE: Hard coded values here that I am too lazy to config + elf_2_hex_cmd_arr = [ELF2HEX_COMAND, "8", "65536", str(outpath), "2147483648"] + # hex file return this for cleaning + hex_filepath = outpath.parent/MEMINIT_HEX_FILE + + log_header("elf2hex", logger) + elf_2_hex_process: Type[subprocess.CompletedProcess] = subprocess.run(elf_2_hex_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + log_subprocess(elf_2_hex_process, logger, level=logging.ERROR) + with open(hex_filepath, "w") as hex_file: + hex_file.write(str(elf_2_hex_process.stdout, encoding="utf-8")) + + logger.info(f"Finished {filepath.name} Compilation") + + return hex_filepath + +# run the simulation +def run_sim(top_level: str, logger: Type[logging.Logger]) -> None: + + config_cmd_arr = ["waf", "configure", "--top_level=" + top_level] + log_header("waf configure", logger) + config_process = subprocess.run(config_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + log_subprocess(config_process, logger) + + sim_cmd_arr = ["waf", "verify_source"] + log_header("waf verify_source", logger) + sim_process = subprocess.run(sim_cmd_arr, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + log_subprocess(sim_process, logger) + + return + +def run_spike_asm(filepath: str) -> None: + return +# compare the results +def compare_results(filepath: str) -> bool: + return False +# compare spike results +def compare_spike_results(): + return +# just check selfasm results +def check_self_results(filepath: Type[pathlib.Path],\ + logger: Type[logging.Logger]) -> str: + # filepath should be path to the waf_output.log + short_name = filepath.parent.stem + pass_msg = '{0:<10}{1}'.format(short_name,GREEN + '[PASSED]' + END_COLOR) + fail_msg = '{0:<10}{1}'.format(short_name,RED + '[FAILED]' + END_COLOR) + with open(filepath, 'r') as waf_output: + waf_output_text = waf_output.read() + match = re.search(r'SUCCESS', waf_output_text) + if match: + print(pass_msg) + logger.info(pass_msg) + return PASSED + else: + match = re.search(r'(ERROR:)\s+(.*)', waf_output_text) + print(match.group(2)) + print(fail_msg) + logger.error(fail_msg) + return FAILED + +######## Side Helper Function ######## +def log_subprocess(complete_proc: Type[subprocess.CompletedProcess],\ + logger: Type[logging.Logger], level=logging.NOTSET) -> None: + """ Logs the std output to the logfile, then if there is an error + it raises an exception that prints out the output from stderr + """ + # this is mainly for the elf2hex so we dont get the hexdump in the log + # but we can still get errors + if(level <= logging.INFO): + logger.info(str(complete_proc.stdout, encoding="utf-8")) + # put in error if there are any and raise an exception + if(complete_proc.returncode): + error_string = str(complete_proc.stderr, encoding="utf-8") + logger.error(error_string) + raise Error(error_string=error_string) + return +def log_header(process: str, logger: Type[logging.Logger]) -> None: + logger.info(f"============ Starting {process} Process ============") + return + +# Create a temp file that consists of the Intel HEX format +# version of the meminit.hex file, delete the original log file +# and rename the temp file to the original's name +def clean_init_hex(filepath: Type[pathlib.Path]) -> None: + # filepath is the path object to the dirty meminit.hex file + # the one that was generated by the elf to hex + #short_name = file_name.split(ARCH+'/')[1][:-2] + #short_name = filepath.stem + #output_dir = './sim_out/' + ARCH + '/' + short_name + '/' + #output_dir = filepath.parent + #init_output = output_dir + 'meminit.hex' + init_output = filepath + build_dir = pathlib.Path("./build/") + + #cleaned_location = init_output[:len(file_name)-4] + "_clean.hex" + cleaned_location = filepath.parent/"meminit_clean.hex" + addr = 0x00 + with open(init_output, 'r') as init_file: + with open(cleaned_location, 'w')as cleaned_file: + for line in init_file: + stripped_line = line[:len(line)-1] + for i in range(len(stripped_line), 0, -8): + data_word = stripped_line[i-8:i] + new_data_word = data_word[6:8] + data_word[4:6] + new_data_word += data_word[2:4] + data_word[0:2] + checksum = calculate_checksum_str(int(new_data_word, 16), addr) + if len(checksum) < 2: + checksum = '0' + checksum + addr_str = hex(addr//4)[2:] + #left pad the string with 0s until 4 hex digits + while len(addr_str) < 4: + addr_str = '0' + addr_str + if new_data_word != "00000000": + out = ":04" + addr_str + "00" + new_data_word + checksum + '\n' + cleaned_file.write(out) + addr += 0x4 + # add the EOL record to the file + cleaned_file.write(":00000001FF") + + + subprocess.call(['mv', str(init_output), str(init_output.parent/"meminit_dirty.hex")]) + subprocess.call(['mv', str(cleaned_location), str(init_output)]) + if not os.path.exists(build_dir): + os.makedirs(build_dir) + subprocess.call(['cp', str(init_output), str(build_dir/"meminit.hex")]) + return + +# Returns the string representation of the +# checksum for the given data and address values +def calculate_checksum_str(data: int, addr: int) -> str: + addr = addr//4 + high_addr = (addr & 0xFF00) >> 8 + low_addr = addr & 0x00FF + data1 = data & 0x000000FF + data2 = (data & 0x0000FF00) >> 8 + data3 = (data & 0x00FF0000) >> 16 + data4 = (data & 0xFF000000) >> 24 + checksum = 4 + high_addr + low_addr + checksum += data1 + data2 + data3 + data4 + checksum = checksum & 0xFF + checksum = int(invert_bin_string(bin(checksum)[2:]),2) + checksum += 1 + checksum_lower_byte = hex(checksum)[2:] + if len(checksum_lower_byte) > 2: + checksum_lower_byte = checksum_lower_byte[-2:] + return checksum_lower_byte + +def invert_bin_string(bin_string: str) -> str: + inverted = '' + while len(bin_string) < 8: + bin_string = '0' + bin_string + for bit in bin_string: + if bit == '0': + inverted = inverted + '1' + else: + inverted = inverted + '0' + return inverted + +######## Logger Related Functions ######## +def setup_logger(name: str, log_filepath: Type[pathlib.Path],\ + level=logging.DEBUG) -> Type[logging.Logger]: + handler = logging.FileHandler(filename=log_filepath, mode="w") + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + logger = logging.getLogger(name) + logger.setLevel(level) + logger.addHandler(handler) + return logger + +def change_logger_file_handlers(logger: Type[logging.Logger],\ + new_filepath: Type[pathlib.Path]) -> Type[logging.Logger]: + """ Removes all old file handlers and replaces it with a new one """ + # remove all old handlers + for handler in logger.handlers: + logger.removeHandler(handler) + handler.close() + # add the new one + new_handler = logging.FileHandler(filename=new_filepath, mode="w") + formatter = logging.Formatter('[%(asctime)s] %(levelname)-4s: %(message)s') + new_handler.setFormatter(formatter) + + logger.addHandler(new_handler) + return logger + +######## Main Function ######## +def main(): + return + +def parse_args()-> Type[run_config]: + parser = argparse.ArgumentParser(description="Run various processor tests at the top level of RISCVBusisness") + parser.add_argument("--config_file", "-c", dest="config_file", + type=str, default=DEFAULT_CONFIG_FILE, + help="Specify the config file path") + parser.add_argument("--arch", "-a", dest="arch", + type=str, default=None, + help="Specify the architecture targeted. Option(s): RV32I Default: RV32I") + parser.add_argument("--test_type", "-t", dest="test_type", + type=str, default=None, + help="Specify what type of tests to run. Option(s): asm, selfasm,c Default: selfasm") + parser.add_argument("file_names", metavar="file_names", + type=str, nargs="*", + help="Run all tests that begin with this string. Optional") + parser.add_argument("--clean", action="store_true", dest="clean", + help="Clean the cache file before running") + args = parser.parse_args() + + conf_dict = dict() + with open(args.config_file, "r") as conf_fp: + conf_dict = json.load(conf_fp) + + config = run_config(conf_dict) + if(args.arch): + config.arch = args.arch + if(args.test_type): + config.test_type = args.test_type + if(args.file_names): + # find all the files that match the pattern + print(args.file_names) + config.test_filenames = args.file_names + # get the list of test files + test_files =[] + for filename in config.test_filenames: + test_files.append(glob.glob(str(config.test_dir/filename))) + config.test_filepaths = list(itertools.chain(*test_files)) + + # process the clean flag, remove the cache file if want a clean run + if(args.clean): + try: + os.remove(config.cache_file) + except FileNotFoundError: + pass # ignore if the file is not there + + + return config + +######## Main Function ######## +if __name__ == "__main__": + print("Running Main...") + # setup the logfile + #logging.basicConfig(filename=log_filepath, mode="w", level=logging.DEBUG) + config = parse_args() + run_tests(config) + # shutdown any remaining loggers + logging.shutdown() diff --git a/verification/asm-env/selfasm/riscv_test.h b/verification/asm-env/selfasm/riscv_test.h index 4466f9989..8e26e16a6 100644 --- a/verification/asm-env/selfasm/riscv_test.h +++ b/verification/asm-env/selfasm/riscv_test.h @@ -66,8 +66,7 @@ done: \ j done - -#endif +#endif // merged in //----------------------------------------------------------------------- // Pass/Fail Macro @@ -87,6 +86,15 @@ j done +//----------------------------------------------------------------------- +// End Macro +//----------------------------------------------------------------------- + +//#define RVTEST_CODE_END \ +//ecall: ecall; \ +// j ecall + + //----------------------------------------------------------------------- // Data Section Macro //----------------------------------------------------------------------- diff --git a/verification/asm-env/selfasm/riscv_test.h.old b/verification/asm-env/selfasm/riscv_test.h.old deleted file mode 100644 index 22c1b833f..000000000 --- a/verification/asm-env/selfasm/riscv_test.h.old +++ /dev/null @@ -1,184 +0,0 @@ -// See LICENSE for license details. - -#ifndef _ENV_PHYSICAL_SINGLE_CORE_H -#define _ENV_PHYSICAL_SINGLE_CORE_H - -#include "../encoding.h" - -//----------------------------------------------------------------------- -// Begin Macro -//----------------------------------------------------------------------- - -#define RVTEST_RV64U \ - .macro init; \ - .endm - -#define RVTEST_RV64UF \ - .macro init; \ - RVTEST_FP_ENABLE; \ - .endm - -#define RVTEST_RV32U \ - .macro init; \ - .endm - -#define RVTEST_RV32UF \ - .macro init; \ - RVTEST_FP_ENABLE; \ - .endm - -#define RVTEST_RV64M \ - .macro init; \ - RVTEST_ENABLE_MACHINE; \ - .endm - -#define RVTEST_RV64S \ - .macro init; \ - RVTEST_ENABLE_SUPERVISOR; \ - .endm - -#define RVTEST_RV64SV \ - .macro init; \ - RVTEST_ENABLE_SUPERVISOR; \ - .endm - -#define RVTEST_RV32M \ - .macro init; \ - RVTEST_ENABLE_MACHINE; \ - .endm - -#define RVTEST_RV32S \ - .macro init; \ - RVTEST_ENABLE_SUPERVISOR; \ - .endm - -#ifdef __riscv64 -# define CHECK_XLEN csrr a0, mcpuid; bltz a0, 1f; RVTEST_PASS; 1: -#else -# define CHECK_XLEN csrr a0, mcpuid; bgez a0, 1f; RVTEST_PASS; 1: -#endif - -#define RVTEST_ENABLE_SUPERVISOR \ - li a0, MSTATUS_PRV1 & (MSTATUS_PRV1 >> 1); \ - csrs mstatus, a0; \ - -#define RVTEST_ENABLE_MACHINE \ - li a0, MSTATUS_PRV1; \ - csrs mstatus, a0; \ - -#define RVTEST_FP_ENABLE \ - li a0, MSTATUS_FS & (MSTATUS_FS >> 1); \ - csrs mstatus, a0; \ - csrwi fcsr, 0 - -#define RISCV_MULTICORE_DISABLE \ - csrr a0, mhartid; \ - 1: bnez a0, 1b - -#define EXTRA_TVEC_USER -#define EXTRA_TVEC_SUPERVISOR -#define EXTRA_TVEC_HYPERVISOR -#define EXTRA_TVEC_MACHINE -#define EXTRA_INIT -#define EXTRA_INIT_TIMER - -#define RVTEST_CODE_BEGIN \ - .text; \ - .align 6; \ - .weak stvec_handler; \ - .weak mtvec_handler; \ -tvec_user: \ - EXTRA_TVEC_USER; \ - /* test whether the test came from pass/fail */ \ - la t5, ecall; \ - csrr t6, mepc; \ - beq t5, t6, write_tohost; \ - /* test whether the stvec_handler target exists */ \ - la t5, stvec_handler; \ - bnez t5, mrts_routine; \ - /* test whether the mtvec_handler target exists */ \ - la t5, mtvec_handler; \ - bnez t5, mtvec_handler; \ - /* some other exception occurred */ \ - j other_exception; \ - .align 6; \ -tvec_supervisor: \ - EXTRA_TVEC_SUPERVISOR; \ - csrr t5, mcause; \ - bgez t5, tvec_user; \ - mrts_routine: \ - mrts; \ - .align 6; \ -tvec_hypervisor: \ - EXTRA_TVEC_HYPERVISOR; \ - /* renting some space out here */ \ - other_exception: \ - 1: ori TESTNUM, TESTNUM, 1337; /* some other exception occurred */ \ - write_tohost: \ - csrw mtohost, TESTNUM; \ - j write_tohost; \ - .align 6; \ -tvec_machine: \ - EXTRA_TVEC_MACHINE; \ - la t5, ecall; \ - csrr t6, mepc; \ - beq t5, t6, write_tohost; \ - la t5, mtvec_handler; \ - bnez t5, mtvec_handler; \ - j other_exception; \ - .align 6; \ - .globl _start; \ -_start: \ - RISCV_MULTICORE_DISABLE; \ - CHECK_XLEN; \ - li TESTNUM, 0; \ - la t0, stvec_handler; \ - beqz t0, 1f; \ - csrw stvec, t0; \ -1: li t0, MSTATUS_PRV1 | MSTATUS_PRV2 | MSTATUS_IE1 | MSTATUS_IE2; \ - csrc mstatus, t0; \ - init; \ - EXTRA_INIT; \ - EXTRA_INIT_TIMER; \ - la t0, 1f; \ - csrw mepc, t0; \ - csrr a0, mhartid; \ - eret; \ -1: - -//----------------------------------------------------------------------- -// End Macro -//----------------------------------------------------------------------- - -#define RVTEST_CODE_END \ -ecall: ecall; \ - j ecall - -//----------------------------------------------------------------------- -// Pass/Fail Macro -//----------------------------------------------------------------------- - -#define RVTEST_PASS \ - fence; \ - li TESTNUM, 1; \ - j ecall - -#define TESTNUM x28 -#define RVTEST_FAIL \ - fence; \ -1: beqz TESTNUM, 1b; \ - sll TESTNUM, TESTNUM, 1; \ - or TESTNUM, TESTNUM, 1; \ - j ecall - -//----------------------------------------------------------------------- -// Data Section Macro -//----------------------------------------------------------------------- - -#define EXTRA_DATA - -#define RVTEST_DATA_BEGIN EXTRA_DATA .align 4; .global begin_signature; begin_signature: -#define RVTEST_DATA_END .align 4; .global end_signature; end_signature: - - -#endif diff --git a/verification/asm-env/selfasm/test_macros.h b/verification/asm-env/selfasm/test_macros.h index 778dc82c4..c225e17fb 100644 --- a/verification/asm-env/selfasm/test_macros.h +++ b/verification/asm-env/selfasm/test_macros.h @@ -7,6 +7,8 @@ #----------------------------------------------------------------------- # Helper macros #----------------------------------------------------------------------- +#define SPARCE_ADDR 0x90000000 +#define SPARCE_CONFIG_ADDR (SPARCE_ADDR + 4) #define TEST_CASE( testnum, testreg, correctval, code... ) \ test_ ## testnum: \ @@ -15,6 +17,27 @@ test_ ## testnum: \ li TESTNUM, testnum; \ bne testreg, x29, fail; +#define SASA_COND_OR 0 +#define SASA_COND_AND 1 + +#define CALC_SASA_VAL(reg1, reg2, condition, insts_to_skip) \ +(((reg1) << 11) + ((reg2) << 6) + ((condition << 5)) + ((insts_to_skip))); + +#define STORE_SASA_FROM_MEM(mem_label, reg_offset, tempreg1, tempreg2) \ +slli tempreg1, reg_offset, 3; \ +la tempreg2, mem_label; \ +add tempreg2, tempreg2, tempreg1; \ +lw tempreg1, 0(tempreg2); \ +slli tempreg1, tempreg1, 14; \ +lw tempreg2, 4(tempreg2); \ +or tempreg1, tempreg1, tempreg2; \ +li tempreg2, SPARCE_ADDR; \ +sw tempreg1, 0(tempreg2); + +#define CREATE_SASA_MEMORY_ENTRIES(skip_label, reg1, reg2, condition, insts_to_skip) \ +.word skip_label; \ +.word CALC_SASA_VAL(reg1, reg2, condition, insts_to_skip); \ + # We use a macro hack to simpify code generation for various numbers # of bubble cycles. @@ -572,6 +595,367 @@ test_ ## testnum: \ .double result; \ 1: +#----------------------------------------------------------------------- +# Tests compressed instructions (RV32C) +#----------------------------------------------------------------------- +#define TEST_RV32C_R_OP( testnum, inst, result, predefined_val, val1) \ + TEST_CASE( testnum, x13, result, \ + li x11, val1; \ + li x13, predefined_val; \ + inst x13, x11; \ + ) + +#define TEST_RV32C_RR_SRC12_EQ_DEST( testnum, inst, result, val1 ) \ + TEST_CASE( testnum, x11, result, \ + li x11, val1; \ + inst x11, x11; \ + ) + +#define TEST_RV32C_RR_DEST_BYPASS( testnum, nop_cycles, inst, result, predefined_val, val1) \ + TEST_CASE( testnum, x16, result, \ + li x14, 0; \ +1: li x11, val1; \ + li x13, predefined_val; \ + inst x13, x11; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x16, x13, 0; \ + addi x14, x14, 1; \ + li x15, 2; \ + bne x14, x15, 1b \ + ) + +#define TEST_RV32C_RR_SRC12_BYPASS( testnum, src1_nops, src2_nops, inst, result, predefined_val, val1) \ + TEST_CASE( testnum, x13, result, \ + li x14, 0; \ +1: li x11, val1; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x13, predefined_val; \ + TEST_INSERT_NOPS_ ## src2_nops \ + inst x13, x11; \ + addi x14, x14, 1; \ + li x15, 2; \ + bne x14, x15, 1b \ + ) + +#define TEST_RV32C_RR_SRC21_BYPASS( testnum, src1_nops, src2_nops, inst, result, predefined_val, val1) \ + TEST_CASE( testnum, x13, result, \ + li x14, 0; \ +1: li x13, predefined_val; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x11, val1; \ + TEST_INSERT_NOPS_ ## src2_nops \ + inst x13, x11; \ + addi x14, x14, 1; \ + li x15, 2; \ + bne x14, x15, 1b \ + ) + +#define TEST_RV32C_BR2_OP_TAKEN( testnum, inst, val1) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x12, val1; \ + inst x12, 2f; \ + bne x0, TESTNUM, fail; \ +1: bne x0, TESTNUM, 3f; \ +2: inst x12, 1b; \ + bne x0, TESTNUM, fail; \ +3: + +#define TEST_RV32C_BR2_OP_NOTTAKEN( testnum, inst, val1) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x9, val1; \ + inst x9, 1f; \ + bne x0, TESTNUM, 2f; \ +1: bne x0, TESTNUM, fail; \ +2: inst x9, 1b; \ +3: + +#define TEST_RV32C_BR2_SRC_BYPASS( testnum, src1_nops, inst, val1) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x6, 0; \ +1: li x14, val1; \ + TEST_INSERT_NOPS_ ## src1_nops \ + inst x14, fail; \ + addi x6, x6, 1; \ + li x5, 2; \ + bne x6, x5, 1b \ + +#define TEST_RV32C_JALR_SRC1_BYPASS( testnum, nop_cycles, inst ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x6, 2f; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x6; \ + bne x0, TESTNUM, fail; \ +2: addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_JAL_SRC1_BYPASS( testnum, nop_cycles, inst ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: TEST_INSERT_NOPS_ ## nop_cycles \ + inst 2f; \ + bne x0, TESTNUM, fail; \ +2: addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define SEXT_IMM_5(x) ((x) | (-(((x) >> 5) & 1) << 5)) + +#define TEST_RV32C_IMM_OP( testnum, inst, result, val1, imm ) \ + TEST_CASE( testnum, x10, result, \ + li x10, val1; \ + inst x10, SEXT_IMM_5(imm); \ + ) + +#define TEST_RV32C_IMM_DEST_BYPASS( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x6, result, \ + li x4, 0; \ +1: li x12, val1; \ + inst x12, SEXT_IMM_5(imm); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x12, 0; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RV32C_IMM_SRC1_BYPASS( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x14, result, \ + li x4, 0; \ +1: li x14, val1; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x14, SEXT_IMM_5(imm); \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define SEXT_IMM_9(x) ((x) | (-(((x) >> 9) & 1) << 9)) + +#define TEST_RV32C_IMM_OP_SP( testnum, inst, result, val1, imm ) \ + TEST_CASE( testnum, x2, result, \ + li x2, val1; \ + inst x2, SEXT_IMM_9(imm); \ + ) + +#define TEST_RV32C_IMM_DEST_BYPASS_SP( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x6, result, \ + li x4, 0; \ +1: li x2, val1; \ + inst x2, SEXT_IMM_9(imm); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x2, 0; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RV32C_IMM_SRC1_BYPASS_SP( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x2, result, \ + li x4, 0; \ +1: li x2, val1; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x2, SEXT_IMM_9(imm); \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define ZERO_IMM_9(x) ((x) | (0 << 10)) + +#define TEST_RV32C_IMM_OP_SP_REG( testnum, inst, result, val1, imm ) \ + TEST_CASE( testnum, x10, result, \ + li x2, val1; \ + inst x10, x2, ZERO_IMM_9(imm); \ + ) + +#define TEST_RV32C_IMM_DEST_BYPASS_SP_REG( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x6, result, \ + li x4, 0; \ +1: li x2, val1; \ + inst x11, x2, ZERO_IMM_9(imm); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x11, 0; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RV32C_IMM_SRC1_BYPASS_SP_REG( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x12, result, \ + li x4, 0; \ +1: li x2, val1; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x12, x2, ZERO_IMM_9(imm); \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RV32C_LI( testnum, inst, result, imm, shamt ) \ + TEST_CASE( testnum, x4, result, \ + inst x4, SEXT_IMM_5(imm); \ + sra x4, x4, shamt; \ + ) + +#define TEST_RV32C_LUI( testnum, inst, result, imm, shamt ) \ + TEST_CASE( testnum, x4, result, \ + inst x4, imm; \ + sra x4, x4, shamt; \ + ) + +#define TEST_RV32C_LD_OP( testnum, inst, result, offset, base ) \ + TEST_CASE( testnum, x13, result, \ + la x11, base; \ + inst x13, offset(x11); \ + ) + +#define TEST_RV32C_LD_DEST_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x11, base; \ + inst x13, offset(x11); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x13, 0; \ + li x29, result; \ + bne x6, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b; \ + +#define TEST_RV32C_LD_SRC1_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x11, base; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x13, offset(x11); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_ST_OP( testnum, load_inst, store_inst, result, offset, base ) \ + TEST_CASE( testnum, x13, result, \ + la x11, base; \ + li x12, result; \ + store_inst x12, offset(x11); \ + load_inst x13, offset(x11); \ + ) + +#define TEST_RV32C_ST_SRC12_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: li x11, result; \ + TEST_INSERT_NOPS_ ## src1_nops \ + la x12, base; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x11, offset(x12); \ + load_inst x13, offset(x12); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_ST_SRC21_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x12, base; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x11, result; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x11, offset(x12); \ + load_inst x13, offset(x12); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_LDSP_OP( testnum, inst, result, offset, base ) \ + TEST_CASE( testnum, x13, result, \ + la x2, base; \ + inst x13, offset(x2); \ + ) + +#define TEST_RV32C_LDSP_DEST_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x2, base; \ + inst x13, offset(x2); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x13, 0; \ + li x29, result; \ + bne x6, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b; \ + +#define TEST_RV32C_LDSP_SRC1_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x2, base; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x13, offset(x2); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_STSP_OP( testnum, load_inst, store_inst, result, offset, base ) \ + TEST_CASE( testnum, x13, result, \ + la x2, base; \ + li x12, result; \ + store_inst x12, offset(x2); \ + load_inst x13, offset(x2); \ + ) + +#define TEST_RV32C_STSP_SRC12_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: li x11, result; \ + TEST_INSERT_NOPS_ ## src1_nops \ + la x2, base; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x11, offset(x2); \ + load_inst x13, offset(x2); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_RV32C_STSP_SRC21_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x2, base; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x11, result; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x11, offset(x2); \ + load_inst x13, offset(x2); \ + li x29, result; \ + bne x13, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + #----------------------------------------------------------------------- # Pass and fail code (assumes test num is in TESTNUM) #----------------------------------------------------------------------- diff --git a/verification/asm-tests/RV32I/mult.S b/verification/asm-tests/RV32I/mult.S new file mode 100755 index 000000000..f00875d30 --- /dev/null +++ b/verification/asm-tests/RV32I/mult.S @@ -0,0 +1,63 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mult.S +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: 04/17/2019 +* Description: Function that multiplies 2 numbers +*/ + +#include "riscv_test.h" + +RVTEST_DATA_DUMP_BEGIN + +RVTEST_INTVEC_USER_BEGIN + nop +RVTEST_INTVEC_SUPER_BEGIN + nop +RVTEST_INTVEC_HYPER_BEGIN + nop +RVTEST_INTVEC_MACH_BEGIN + nop + +RVTEST_CODE_BEGIN + +main: + la x5, tdat0 + ori x1, x0, 0x03 // numbers to multiply + ori x2, x0, 0x06 + +mult: + andi x3, x2, 1 // if reg2 is odd + beq x3, x0, shift + add x4, x4, x1 // solution stored in reg4 + +shift: + slli x1, x1, 1 + srli x2, x2, 1 + bne x2, x0, mult + sw x4, 0(x5) + +RVTEST_CODE_END + +.data +#test data goes here +tdat0: .word 0xdeadcafe + +RVTEST_DATA_DUMP_END + diff --git a/verification/asm-tests/RV32I/sparce.S b/verification/asm-tests/RV32I/sparce.S new file mode 100755 index 000000000..c73bdadfd --- /dev/null +++ b/verification/asm-tests/RV32I/sparce.S @@ -0,0 +1,59 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: sparce.S +* +* Created by: Wengyan Chan +* Email: cwengyan@purdue.edu +* Date Created: 04/07/2019 +* Description: Preliminary test for sparcity +*/ + +#include "riscv_test.h" + +RVTEST_DATA_DUMP_BEGIN + +RVTEST_INTVEC_USER_BEGIN + nop +RVTEST_INTVEC_SUPER_BEGIN + nop +RVTEST_INTVEC_HYPER_BEGIN + nop +RVTEST_INTVEC_MACH_BEGIN + nop + +RVTEST_CODE_BEGIN + +main: + ori x1, x0, 0xf0 + ori x2, x0, 0x55 + ori x3, x0, 0x00 + ori x5, x0, 0x00 + + // generate sparse and nonsparse values + and x6, x1, x2 + and x7, x1, x3 + + or x8, x3, x5 + or x9, x3, x1 + +RVTEST_CODE_END + + +RVTEST_DATA_DUMP_END + + + diff --git a/verification/interrupts-exceptions/build_all.py b/verification/interrupts-exceptions/build_all.py new file mode 100755 index 000000000..47646f028 --- /dev/null +++ b/verification/interrupts-exceptions/build_all.py @@ -0,0 +1,51 @@ +#! /usr/bin/python3 + +import subprocess +import glob +import os +import pathlib + + +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i_zicsr', '-mabi=ilp32', '-mcmodel=medany', + '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] + +cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] + + +if not os.path.isfile('./start.S') or not os.path.isfile('link.ld'): + print('Error: Could not find AFTx06.S or link.ld in this directory') + exit(1) + + +for fname in (glob.glob('./*.c') + glob.glob('./*.S')): + if 'start' in fname or 'utility' in fname: + print("Skipping {} as top-level file, appears to be a utility".format(fname)); + continue + print('Compiling {}'.format(fname)) + basename = pathlib.Path(fname).stem + + rv = subprocess.run(compile_cmd + [fname, '-o', basename + '.elf'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + print('Converting {} to binary'.format(fname)) + rv = subprocess.run(cvt_cmd + [basename + '.elf', basename + '.bin']) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + +print( +''' + Finished compilation. Now, pass the '.bin' file corresponding to + the example to run as an argument to 'VbASIC_wrapper' to run an example! +''') diff --git a/verification/interrupts-exceptions/bus_fault.c b/verification/interrupts-exceptions/bus_fault.c new file mode 100644 index 000000000..b29db02e0 --- /dev/null +++ b/verification/interrupts-exceptions/bus_fault.c @@ -0,0 +1,39 @@ +#include +#include "utility.h" + +extern int flag; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + /* + asm volatile( + "mv t0, zero; csrrs t0, mepc, t0; addi t0, t0, 4; csrw mepc, t0;" + : + : + : "t0" + );*/ + print("Made it to handler!\n"); + flag = 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t)handler; + uint32_t mstatus_value = 0x8; + + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + print("Read 0x0\n"); + int x = *(volatile int *)(0x4); + + if(flag != 1) { + print("Failed!\n"); + } else { + print("Bus fault, pass!\n"); + } + + return 0; +} \ No newline at end of file diff --git a/verification/interrupts-exceptions/ecall.c b/verification/interrupts-exceptions/ecall.c new file mode 100644 index 000000000..51eb9778a --- /dev/null +++ b/verification/interrupts-exceptions/ecall.c @@ -0,0 +1,41 @@ + +#include +#include "utility.h" + +extern volatile int flag; + + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + /* + asm volatile( + "mv t0, zero; csrrs t0, mepc, t0; addi t0, t0, 4; csrw mepc, t0;" + : + : + : "t0" + );*/ + print("Made it to handler!\n"); + flag = 1; +} + + +int main() { + + uint32_t mtvec_value = (uint32_t)handler; + uint32_t mstatus_value = 0x8; + + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + print("Jumping to handler...\n"); + asm volatile("ecall"); + + print("Flag is 0x"); + put_uint32_hex(flag); + print("\n"); + + return 0; +} diff --git a/verification/interrupts-exceptions/ex_after_int.c b/verification/interrupts-exceptions/ex_after_int.c new file mode 100644 index 000000000..2d333b307 --- /dev/null +++ b/verification/interrupts-exceptions/ex_after_int.c @@ -0,0 +1,127 @@ + +#include +#include "utility.h" + +extern volatile int flag; + +volatile uint32_t *mtime = (uint32_t *)MTIME_ADDR; +volatile uint32_t *mtimecmp = (uint32_t *)MTIMECMP_ADDR; +volatile uint32_t *mtimecmph = (uint32_t *)MTIMECMPH_ADDR; +volatile uint32_t *msip = (uint32_t *)MSIP_ADDR; +volatile uint32_t *ext_trigger = (uint32_t *)EXT_ADDR_SET; +volatile uint32_t *ext_clear = (uint32_t *)EXT_ADDR_CLEAR; + +// For this test: need to subtract 0xFE from flag to make flag = 1 +// Each handler should be called once. If not, flag will be wrong + +/* + * RISC-V Vector Layout + * 0 - reserved/exception (overlap of interrupt cause & exception cause) + * 1 - S-SW + * 2 - reserved + * 3 - M-SW + * 4 - reserved + * 5 - S-Timer + * 6 - reserved + * 7 - M-Timer + * 8 - reserved + * 9 - S-Ext + * 10- reserved + * 11- M-Ext + */ + +void __attribute__((interrupt)) exception_handler() { + uint32_t mepc, mcause; + asm volatile("csrr %0, mepc" : "=r"(mepc)); + asm volatile("csrr %0, mcause" : "=r"(mcause)); + print("Exception with mepc: "); + put_uint32_hex(mepc); + print(" mcause: "); + put_uint32_hex(mcause); + print("\n"); + mepc += 4; // NOT PORTABLE TO RV32IC + asm volatile("csrw mepc, %0" : : "r"(mepc)); + flag -= 2; +} + +void __attribute__((interrupt)) m_timer_handler() { + uint32_t mepc, mcause; + asm volatile("csrr %0, mepc" : "=r"(mepc)); + asm volatile("csrr %0, mcause" : "=r"(mcause)); + print("Time interrupt with mepc: "); + put_uint32_hex(mepc); + print(" mcause: "); + put_uint32_hex(mcause); + print("\n"); + flag -= 1; + (*mtimecmph) = 0xFF; // setting mtimecmph makes a very large value +} + +void __attribute__((interrupt)) default_handler() { + uint32_t mcause, mepc; + asm volatile("csrr %0, mcause" : "=r"(mcause)); + asm volatile("csrr %0, mepc" : "=r"(mepc)); + print("Hit default handler, mepc: \n"); + put_uint32_hex(mcause); + print(" mcause: "); + put_uint32_hex(mepc); + print("\n"); + flag = 0; + done(); // Go to done and fail test +} // should not end up here, this is a fail! + +// mtvec value MUST be aligned +// Note: .align 2 forces the jumps to be on multiple-of-4 boundaries here, +// which is required for vectored mode which computes the address as +// (mtvec.base + cause) x 4 +// If this test breaks, it may mean that the alignment is wrong, check the +// disassembly! +void __attribute__((naked)) __attribute__((aligned(4))) handler_dispatch() { + asm volatile(".align 2; j exception_handler"); // 0 + asm volatile(".align 2; j default_handler"); // 1 + asm volatile(".align 2; j default_handler"); // 2 + asm volatile(".align 2; j default_handler"); // 3 + asm volatile(".align 2; j default_handler"); // 4 + asm volatile(".align 2; j default_handler"); // 5 + asm volatile(".align 2; j default_handler"); // 6 + asm volatile(".align 2; j m_timer_handler"); // 7 + asm volatile(".align 2; j default_handler"); // 8 + asm volatile(".align 2; j default_handler"); // 9 + asm volatile(".align 2; j default_handler"); // 10 + asm volatile(".align 2; j default_handler"); // 11 +} + +/* + + This test case attempts an exception after an interrupt occurs. + This was to verify an issue with the previous interrupt/exception handler + where an exception after an interrupt while in VECTORED mode would + incorrectly jump to the wrong handler for the exception. + While that issue is most likely resolved, this case is included for verification. + +*/ +int main() { + uint32_t mtvec_value = (uint32_t)handler_dispatch; + mtvec_value |= 1; // set vectored mode + uint32_t mie_value = 0x888; + uint32_t mstatus_value = 0x8; + + + // set mtimecmp away so interrupt doesn't fire immediately + *mtimecmph = 0x00; + *mtimecmp = 0xFF; + + flag = 4; + + // Setup interrupts + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + asm volatile("csrw mie, %0" : : "r" (mie_value)); + // Interrupts active + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + + while((*mtime) < 0xFF); + + asm volatile("ecall"); + + return 0; +} diff --git a/verification/interrupts-exceptions/illegal.c b/verification/interrupts-exceptions/illegal.c new file mode 100644 index 000000000..06ddb1a1f --- /dev/null +++ b/verification/interrupts-exceptions/illegal.c @@ -0,0 +1,32 @@ + +#include +#include "utility.h" + +extern volatile int flag; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + print("Made it to handler!"); + flag = 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t)handler; + uint32_t mstatus_value = 0x8; + + asm volatile("csrw mstatus, %0" : : "r"(mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r"(mtvec_value)); + + print("Executing illegal instruction\n"); + asm volatile(".word 0xFFFFFFFF"); + + print("Flag is 0x"); + put_uint32_hex(flag); + print("\n"); + + return 0; +} \ No newline at end of file diff --git a/verification/interrupts-exceptions/link.ld b/verification/interrupts-exceptions/link.ld new file mode 100755 index 000000000..4fd20ea69 --- /dev/null +++ b/verification/interrupts-exceptions/link.ld @@ -0,0 +1,14 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x0000000080000000; + .text.init : { *(.text.init) } + .text : { *(.text) } + .data ALIGN(0x1000) : { *(.data) } + .bss : { *(.bss) } + . = 0x0000000080007000; + .statuses : { *(.statuses) } + _end = .; +} diff --git a/verification/interrupts-exceptions/start.S b/verification/interrupts-exceptions/start.S new file mode 100644 index 000000000..9e588dd07 --- /dev/null +++ b/verification/interrupts-exceptions/start.S @@ -0,0 +1,17 @@ + +.global _start +_start: + li sp, 0x8000F000 + jal main + +.global done +done: + la x28, flag + lw x28, 0(x28) +__inf_loop: + j __inf_loop + + +.global flag +.data +flag: .word 0xFF diff --git a/verification/interrupts-exceptions/utility.c b/verification/interrupts-exceptions/utility.c new file mode 100644 index 000000000..17d82f6d1 --- /dev/null +++ b/verification/interrupts-exceptions/utility.c @@ -0,0 +1,25 @@ + +#include "utility.h" + +void print(char *string) { + volatile char *magic = (volatile char *)MAGIC_ADDR; + + for(int i = 0; string[i]; i++) { + (*magic) = string[i]; + } +} + +void put_uint32_hex(uint32_t x) { + char buf[10] = {0}; + + for(int i = 0; i < 8; i++) { + uint8_t value = (x & 0xF); + if(value >= 10) { + buf[7-i] = ((value-10) + 'A'); + } else { + buf[7-i] = (value + '0'); + } + x >>= 4; + } + print(buf); +} diff --git a/verification/interrupts-exceptions/utility.h b/verification/interrupts-exceptions/utility.h new file mode 100644 index 000000000..6caaee7c5 --- /dev/null +++ b/verification/interrupts-exceptions/utility.h @@ -0,0 +1,19 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + + #include + + #define MTIME_ADDR 0xFFFFFFE0 + #define MTIMEH_ADDR 0xFFFFFFE4 + #define MTIMECMP_ADDR 0xFFFFFFE8 + #define MTIMECMPH_ADDR 0xFFFFFFEC + #define MSIP_ADDR 0xFFFFFFF0 + #define EXT_ADDR_SET 0xFFFFFFF4 + #define EXT_ADDR_CLEAR 0xFFFFFFF8 + #define MAGIC_ADDR 0xFFFFFFFC + + + void print(char *string); + void put_uint32_hex(uint32_t hex); + +#endif diff --git a/verification/interrupts-exceptions/vectored.c b/verification/interrupts-exceptions/vectored.c new file mode 100644 index 000000000..2b531f7fc --- /dev/null +++ b/verification/interrupts-exceptions/vectored.c @@ -0,0 +1,99 @@ + +#include +#include "utility.h" + +extern volatile int flag; + +volatile uint32_t *mtime = (uint32_t *)MTIME_ADDR; +volatile uint32_t *mtimecmp = (uint32_t *)MTIMECMP_ADDR; +volatile uint32_t *mtimecmph = (uint32_t *)MTIMECMPH_ADDR; +volatile uint32_t *msip = (uint32_t *)MSIP_ADDR; +volatile uint32_t *ext_trigger = (uint32_t *)EXT_ADDR_SET; +volatile uint32_t *ext_clear = (uint32_t *)EXT_ADDR_CLEAR; + +// For this test: need to subtract 0xFE from flag to make flag = 1 +// Each handler should be called once. If not, flag will be wrong + +/* + * RISC-V Vector Layout + * 0 - reserved/exception (overlap of interrupt cause & exception cause) + * 1 - S-SW + * 2 - reserved + * 3 - M-SW + * 4 - reserved + * 5 - S-Timer + * 6 - reserved + * 7 - M-Timer + * 8 - reserved + * 9 - S-Ext + * 10- reserved + * 11- M-Ext + */ + +void __attribute__((interrupt)) m_ext_handler() { + flag -= 0xE; + (*ext_clear) = 0x1; // writing anything simulates clearing interrupt + uint32_t mie_value = 0x088; + asm volatile("csrw mie, %0" : : "r" (mie_value)); +} + +void __attribute__((interrupt)) m_timer_handler() { + flag -= 0xE0; + (*mtimecmph) = 0xFF; // setting mtimecmph makes a very large value +} + +void __attribute__((interrupt)) m_sw_handler() { + flag -= 0x10; + (*msip) = 0x0; // writing 0 clears this +} + +void __attribute__((interrupt)) default_handler() { + done(); // Go to done and fail test +} // should not end up here, this is a fail! + +// mtvec value MUST be aligned +// Note: .align 2 forces the jumps to be on multiple-of-4 boundaries here, +// which is required for vectored mode which computes the address as +// (mtvec.base + cause) x 4 +// If this test breaks, it may mean that the alignment is wrong, check the +// disassembly! +void __attribute__((naked)) __attribute__((aligned(4))) handler_dispatch() { + asm volatile(".align 2; j default_handler"); // 0 + asm volatile(".align 2; j default_handler"); // 1 + asm volatile(".align 2; j default_handler"); // 2 + asm volatile(".align 2; j m_sw_handler"); // 3 + asm volatile(".align 2; j default_handler"); // 4 + asm volatile(".align 2; j default_handler"); // 5 + asm volatile(".align 2; j default_handler"); // 6 + asm volatile(".align 2; j m_timer_handler"); // 7 + asm volatile(".align 2; j default_handler"); // 8 + asm volatile(".align 2; j default_handler"); // 9 + asm volatile(".align 2; j default_handler"); // 10 + asm volatile(".align 2; j m_ext_handler"); // 11 +} + + +int main() { + uint32_t mtvec_value = (uint32_t)handler_dispatch; + mtvec_value |= 1; // set vectored mode + uint32_t mie_value = 0x888; + uint32_t mstatus_value = 0x8; + + + // set mtimecmp away so interrupt doesn't fire immediately + *mtimecmph = 0x00; + *mtimecmp = 0xFF; + + // Setup interrupts + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + asm volatile("csrw mie, %0" : : "r" (mie_value)); + // Interrupts active + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + + (*msip) = 1; // trigger SW interrupt + (*ext_trigger) = 1; // trigger EXT interrupt + + while((*mtime) < 0xFF); + + return 0; +} diff --git a/verification/pma-tests/build_all.py b/verification/pma-tests/build_all.py new file mode 100755 index 000000000..47646f028 --- /dev/null +++ b/verification/pma-tests/build_all.py @@ -0,0 +1,51 @@ +#! /usr/bin/python3 + +import subprocess +import glob +import os +import pathlib + + +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i_zicsr', '-mabi=ilp32', '-mcmodel=medany', + '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] + +cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] + + +if not os.path.isfile('./start.S') or not os.path.isfile('link.ld'): + print('Error: Could not find AFTx06.S or link.ld in this directory') + exit(1) + + +for fname in (glob.glob('./*.c') + glob.glob('./*.S')): + if 'start' in fname or 'utility' in fname: + print("Skipping {} as top-level file, appears to be a utility".format(fname)); + continue + print('Compiling {}'.format(fname)) + basename = pathlib.Path(fname).stem + + rv = subprocess.run(compile_cmd + [fname, '-o', basename + '.elf'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + print('Converting {} to binary'.format(fname)) + rv = subprocess.run(cvt_cmd + [basename + '.elf', basename + '.bin']) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + +print( +''' + Finished compilation. Now, pass the '.bin' file corresponding to + the example to run as an argument to 'VbASIC_wrapper' to run an example! +''') diff --git a/verification/pma-tests/link.ld b/verification/pma-tests/link.ld new file mode 100755 index 000000000..4fd20ea69 --- /dev/null +++ b/verification/pma-tests/link.ld @@ -0,0 +1,14 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x0000000080000000; + .text.init : { *(.text.init) } + .text : { *(.text) } + .data ALIGN(0x1000) : { *(.data) } + .bss : { *(.bss) } + . = 0x0000000080007000; + .statuses : { *(.statuses) } + _end = .; +} diff --git a/verification/pma-tests/pma.c b/verification/pma-tests/pma.c new file mode 100644 index 000000000..7032a6ec9 --- /dev/null +++ b/verification/pma-tests/pma.c @@ -0,0 +1,37 @@ +#include +#include "utility.h" + +extern volatile int flag; + +#define PMA_ROM_ADDR 0x100 +#define PMA_RAM_ADDR 0x20000000 +volatile uint32_t *pma_rom_addr = (uint32_t*) PMA_ROM_ADDR; +volatile uint32_t *pma_ram_addr = (uint32_t*) PMA_RAM_ADDR; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + print("PMA Checker failed (expected)\n"); + flag = 2; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + uint32_t mstatus_value = 0x8; + + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + // This should fail + *pma_rom_addr = 0xDEADBEEF; + + // This should succeed + *pma_ram_addr = 0xDEADBEEF; + flag -= 1; + + return 0; +} diff --git a/verification/pma-tests/pma_i.c b/verification/pma-tests/pma_i.c new file mode 100644 index 000000000..41358d865 --- /dev/null +++ b/verification/pma-tests/pma_i.c @@ -0,0 +1,45 @@ +#include +#include "utility.h" + +extern volatile int flag; +extern void done(); + +#define PMA_ROM_ADDR 0x100 +#define PMA_RAM_ADDR 0x20000000 +volatile uint32_t *pma_rom_addr = (uint32_t*) PMA_ROM_ADDR; +volatile uint32_t *pma_ram_addr = (uint32_t*) PMA_RAM_ADDR; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + uint32_t mtval_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + asm volatile("csrr %0, mtval" : "=r"(mtval_value)); + mepc_value = (uint32_t)done; // return to the spot after we did the bad jump + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + print("PMA Checker failed (expected)\n"); + print("MTVAL: "); + put_uint32_hex(mtval_value); + print("\n"); + flag = 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + uint32_t mstatus_value = 0x8; + + // Disable all permissions for "rom" region of PMA + uint32_t pma_value = 0x3BF1 & ~(0x3800); // 0x3800 masks out RWX permissions + asm volatile("csrw 0xBC0, %0" : : "r"(pma_value)); + + // Set up interrupts + asm volatile("csrw mstatus, %0" : : "r" (mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + // After jump, instruction fetch will cause instruction fault + asm volatile("jr %0" : : "r" (pma_rom_addr)); + + // Never reached if successful since handler will jump directly to "done" + return 0; +} diff --git a/verification/pma-tests/start.S b/verification/pma-tests/start.S new file mode 100644 index 000000000..9e588dd07 --- /dev/null +++ b/verification/pma-tests/start.S @@ -0,0 +1,17 @@ + +.global _start +_start: + li sp, 0x8000F000 + jal main + +.global done +done: + la x28, flag + lw x28, 0(x28) +__inf_loop: + j __inf_loop + + +.global flag +.data +flag: .word 0xFF diff --git a/verification/pma-tests/utility.c b/verification/pma-tests/utility.c new file mode 100644 index 000000000..d89404f7e --- /dev/null +++ b/verification/pma-tests/utility.c @@ -0,0 +1,25 @@ + +#include "utility.h" + +void print(char *string) { + volatile char *magic = (volatile char *)MAGIC_ADDR; + + for(int i = 0; string[i]; i++) { + (*magic) = string[i]; + } +} + +void put_uint32_hex(uint32_t x) { + char buf[10] = {0}; + + for(int i = 0; i < 8; i++) { + uint8_t value = (x & 0xF); + if(value >= 10) { + buf[7-i] = (value + 'A'); + } else { + buf[7-i] = (value + '0'); + } + x >>= 4; + } + print(buf); +} diff --git a/verification/pma-tests/utility.h b/verification/pma-tests/utility.h new file mode 100644 index 000000000..6caaee7c5 --- /dev/null +++ b/verification/pma-tests/utility.h @@ -0,0 +1,19 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + + #include + + #define MTIME_ADDR 0xFFFFFFE0 + #define MTIMEH_ADDR 0xFFFFFFE4 + #define MTIMECMP_ADDR 0xFFFFFFE8 + #define MTIMECMPH_ADDR 0xFFFFFFEC + #define MSIP_ADDR 0xFFFFFFF0 + #define EXT_ADDR_SET 0xFFFFFFF4 + #define EXT_ADDR_CLEAR 0xFFFFFFF8 + #define MAGIC_ADDR 0xFFFFFFFC + + + void print(char *string); + void put_uint32_hex(uint32_t hex); + +#endif diff --git a/verification/pmp-tests/build_all.py b/verification/pmp-tests/build_all.py new file mode 100755 index 000000000..47646f028 --- /dev/null +++ b/verification/pmp-tests/build_all.py @@ -0,0 +1,51 @@ +#! /usr/bin/python3 + +import subprocess +import glob +import os +import pathlib + + +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i_zicsr', '-mabi=ilp32', '-mcmodel=medany', + '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] + +cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] + + +if not os.path.isfile('./start.S') or not os.path.isfile('link.ld'): + print('Error: Could not find AFTx06.S or link.ld in this directory') + exit(1) + + +for fname in (glob.glob('./*.c') + glob.glob('./*.S')): + if 'start' in fname or 'utility' in fname: + print("Skipping {} as top-level file, appears to be a utility".format(fname)); + continue + print('Compiling {}'.format(fname)) + basename = pathlib.Path(fname).stem + + rv = subprocess.run(compile_cmd + [fname, '-o', basename + '.elf'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + print('Converting {} to binary'.format(fname)) + rv = subprocess.run(cvt_cmd + [basename + '.elf', basename + '.bin']) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + +print( +''' + Finished compilation. Now, pass the '.bin' file corresponding to + the example to run as an argument to 'VbASIC_wrapper' to run an example! +''') diff --git a/verification/pmp-tests/link.ld b/verification/pmp-tests/link.ld new file mode 100755 index 000000000..4fd20ea69 --- /dev/null +++ b/verification/pmp-tests/link.ld @@ -0,0 +1,14 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x0000000080000000; + .text.init : { *(.text.init) } + .text : { *(.text) } + .data ALIGN(0x1000) : { *(.data) } + .bss : { *(.bss) } + . = 0x0000000080007000; + .statuses : { *(.statuses) } + _end = .; +} diff --git a/verification/pmp-tests/pmp_csr.c b/verification/pmp-tests/pmp_csr.c new file mode 100644 index 000000000..504575e51 --- /dev/null +++ b/verification/pmp-tests/pmp_csr.c @@ -0,0 +1,137 @@ +#include +#include "utility.h" + +extern volatile int flag; + +int main() { + flag = 9; + + // 0.0 Try to write a valid value to the pmpaddr0 register + uint32_t pmp_addr = 0xDEADBEEF; + asm volatile("csrw pmpaddr0, %0" : : "r"(pmp_addr)); + // 0.0 Read the value back + pmp_addr = 0x0; + asm volatile("csrr %0, pmpaddr0" : "=r"(pmp_addr)); + if (pmp_addr == 0xDEADBEEF) + { + flag -= 1; + } + else + { + print("Case 0 wrong value\n"); + } + + // 1.0 Try to write a valid configuration to the pmpcfg0 register + uint32_t pmp_cfg = 0x00170017; + asm volatile("csrw pmpcfg0, %0" : : "r"(pmp_cfg)); + // 1.1 Read the value back + pmp_cfg = 0x0; + asm volatile("csrr %0, pmpcfg0" : "=r"(pmp_cfg)); + if (pmp_cfg == 0x00170017) + { + flag -= 1; + } + else + { + print("Case 1 wrong value\n"); + put_uint32_hex(pmp_cfg); + } + + // 2.0 Try to write an invalid configuration to the pmpcfg1 register + pmp_cfg = 0x001A0027; // pmp4cfg has a non-0 reserved field, pmp6cfg has an invalid permission + asm volatile("csrw pmpcfg1, %0" : : "r"(pmp_cfg)); + // 2.1 Read the value back + pmp_cfg = 0x0; + asm volatile("csrr %0, pmpcfg1" : "=r"(pmp_cfg)); + if (pmp_cfg == 0x00180007) + { + flag -= 1; + } + else + { + print("Case 2 wrong value\n"); + put_uint32_hex(pmp_cfg); + } + + // 3.0 Lock and try to write a configuration to the pmpcfg2 register + pmp_cfg = 0x00000088; + asm volatile("csrw pmpcfg2, %0" : : "r"(pmp_cfg)); + pmp_cfg = 0x00000007; + asm volatile("csrs pmpcfg2, %0" : : "r"(pmp_cfg)); + // 3.1 Try to read the value back + pmp_cfg = 0x0; + asm volatile("csrr %0, pmpcfg2" : "=r"(pmp_cfg)); + if (pmp_cfg == 0x00000088) + { + flag -= 1; + } + else + { + print("Case 3.0 wrong value\n"); + put_uint32_hex(pmp_cfg); + } + // 3.2 Try to write a neighbor config + pmp_cfg = 0x00070000; + asm volatile("csrs pmpcfg2, %0" : : "r"(pmp_cfg)); + // 3.3 Read and verify the second one wrote + pmp_cfg = 0x0; + asm volatile("csrr %0, pmpcfg2" : "=r"(pmp_cfg)); + if (pmp_cfg = 0x00070088) + { + flag -= 1; + } + else + { + print("Case 3.2 wrong value\n"); + put_uint32_hex(pmp_cfg); + } + + // 4.0 Try to write a value to the pmpaddr8 register + pmp_addr = 0xDEADBEEF; + asm volatile("csrw pmpaddr8, %0" : : "r"(pmp_addr)); + // 4.1 Try to read it back + pmp_addr = 0x0; + asm volatile("csrr %0, pmpaddr8" : "=r"(pmp_addr)); + if (pmp_addr == 0x00000000) + { + flag -= 1; + } + else + { + print("Case 4.0 wrong value\n"); + put_uint32_hex(pmp_addr); + } + // 4.2 Try to write a value to the pmpaddr7 register + // this should fail because pmp8cfg is TOR + pmp_addr = 0xDEADBEEF; + asm volatile("csrw pmpaddr7, %0" : : "r"(pmp_addr)); + // 4.3 Try to read it back + pmp_addr = 0x0; + asm volatile("csrr %0, pmpaddr7" : "=r"(pmp_addr)); + if (pmp_addr == 0x00000000) + { + flag -= 1; + } + else + { + print("Case 4.2 wrong value\n"); + put_uint32_hex(pmp_addr); + } + // 4.4 Write to a different address + pmp_addr = 0xDEADBEEF; + asm volatile("csrw pmpaddr9, %0" : : "r"(pmp_addr)); + // 4.5 Read and verify + pmp_addr = 0x0; + asm volatile("csrr %0, pmpaddr9" : "=r"(pmp_addr)); + if (pmp_addr == 0xDEADBEEF) + { + flag -= 1; + } + else + { + print("Case 4.4 wrong value\n"); + put_uint32_hex(pmp_addr); + } + + return 0; +} diff --git a/verification/pmp-tests/pmp_na4.c b/verification/pmp-tests/pmp_na4.c new file mode 100644 index 000000000..3bde3cfa9 --- /dev/null +++ b/verification/pmp-tests/pmp_na4.c @@ -0,0 +1,54 @@ +#include +#include "utility.h" + +extern volatile int flag; + +#define BAD_PMP_ADDR 0x40000000 // This is a 32-bit address +volatile uint32_t *bad_pmp_addr = (uint32_t*) BAD_PMP_ADDR; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + print("PMP Unit Handler tripped\n"); + flag -= 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + flag = 4; + + // 0. Setup the instruction/stack/MMIO regions + uint32_t pmp_cfg = 0x001F1F00; + asm volatile("csrw pmpcfg0, %0" : : "r" (pmp_cfg)); + uint32_t pmp_addr = (0x80000000 >> 2) & ~((1 << 14) - 1) | ((1 << (14 - 1)) - 1); + asm volatile("csrw pmpaddr1, %0" : : "r" (pmp_addr)); + pmp_addr = (0xFFFFFFE0 >> 2) & ~((1 << 4) - 1) | ((1 << (4 - 1)) - 1); + asm volatile("csrw pmpaddr2, %0" : : "r" (pmp_addr)); + + // 1. Test PMP, NA4 in M Mode + pmp_cfg = 0x00000010; // set pmpcfg0.pmp0cfg to (no L, NA4, no RWX) + pmp_addr = BAD_PMP_ADDR >> 2; // set pmpaddr0 to the bad address, chop off bottom 2 bits + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + asm volatile("csrw pmpaddr0, %0" : : "r" (pmp_addr)); + *bad_pmp_addr = 0xDEADBEEF; // should succeed + flag -= 1; + + // 2. Test PMP, NA4 with MPRV + uint32_t mstatus = 0x20000; // set mstatus.mprv, mpp should be 2'b00 + asm volatile("csrw mstatus, %0" : : "r" (mstatus)); + *bad_pmp_addr = 0xABCD1234; // should fail + + // 3. Test PMP, NA4 with L register + asm volatile("csrc mstatus, %0" : : "r" (mstatus)); // clear mstatus.mprv + pmp_cfg = 0x00000090; // set pmpcfg0.pmp0cfg to (L, NA4, no RWX) + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + *bad_pmp_addr = 0x0987FEDC; // should fail + + return 0; +} diff --git a/verification/pmp-tests/pmp_napot.c b/verification/pmp-tests/pmp_napot.c new file mode 100644 index 000000000..17a94c5d0 --- /dev/null +++ b/verification/pmp-tests/pmp_napot.c @@ -0,0 +1,63 @@ +#include +#include "utility.h" + +extern volatile int flag; + +#define BAD_PMP_ADDR 0x40000000 // This is a 32-bit address +#define G 4 +volatile uint32_t *bad_pmp_addr = (uint32_t*) BAD_PMP_ADDR; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + uint32_t mtval; + asm volatile("csrr %0, mtval" : "=r"(mtval)); + + print("PMP Unit Handler tripped: "); + put_uint32_hex(mtval >>2); + flag -= 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + flag = 7; + + // 0. Setup the instruction/stack/MMIO regions + uint32_t pmp_cfg = 0x001F1F00; + asm volatile("csrw pmpcfg0, %0" : : "r" (pmp_cfg)); + uint32_t pmp_addr = (0x80000000 >> 2) & ~((1 << 14) - 1) | ((1 << (14 - 1)) - 1); + asm volatile("csrw pmpaddr1, %0" : : "r" (pmp_addr)); + pmp_addr = (0xFFFFFFE0 >> 2) & ~((1 << 4) - 1) | ((1 << (4 - 1)) - 1); + asm volatile("csrw pmpaddr2, %0" : : "r" (pmp_addr)); + + // 1. Test PMP, NAPOT in M Mode + pmp_cfg = 0x00000018; // set pmpcfg0.pmp0cfg to (no L, NAPOT, no RWX) + pmp_addr = ((BAD_PMP_ADDR >> 2) & ~((1 << G) - 1)) | ((1 << (G - 1)) - 1); // set pmpaddr0 to the bad address, chop off bottom 2 bits and add in the NAPOT mask + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + asm volatile("csrw pmpaddr0, %0" : : "r" (pmp_addr)); + *bad_pmp_addr = 0xDEADBEEF; // should succeed + flag -= 1; + *(bad_pmp_addr + 4) = 0xDEADBEEF; //should succeed + flag -= 1; + + // 2. Test PMP, NAPOT with MPRV + uint32_t mstatus = 0x20000; // set mstatus.mprv, mpp should be 2'b00 + asm volatile("csrw mstatus, %0" : : "r" (mstatus)); + *bad_pmp_addr = 0xABCD1234; // should fail + *(bad_pmp_addr + 4) = 0xABCD1234; //should fail + + // 3. Test PMP, NAPOT with L register + asm volatile("csrc mstatus, %0" : : "r" (mstatus)); // clear mstatus.mprv + pmp_cfg = 0x00000098; // set pmpcfg0.pmp0cfg to (L, NAPOT, no RWX) + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + *bad_pmp_addr = 0x0987FEDC; // should fail + *(bad_pmp_addr + 4) = 0x0987FEDC; //should fail + + return 0; +} diff --git a/verification/pmp-tests/pmp_tor.c b/verification/pmp-tests/pmp_tor.c new file mode 100644 index 000000000..d454ca048 --- /dev/null +++ b/verification/pmp-tests/pmp_tor.c @@ -0,0 +1,67 @@ +#include +#include "utility.h" + +extern volatile int flag; + +#define BAD_PMP_BOT 0x40000000 // This is a 32-bit address +#define BAD_PMP_TOP 0x40000020 // This is a 32-bit address +volatile uint32_t *bad_pmp_addr_top = (uint32_t*) BAD_PMP_TOP; +volatile uint32_t *bad_pmp_addr_bot = (uint32_t*) BAD_PMP_BOT; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + // In a real program, a fault should be handled differently + uint32_t mepc_value; + asm volatile("csrr %0, mepc" : "=r"(mepc_value)); + mepc_value += 4; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + + uint32_t mtval; + asm volatile("csrr %0, mtval" : "=r"(mtval)); + + print("PMP Unit Handler tripped: "); + put_uint32_hex(mtval >> 2); + flag -= 1; +} + +int main() { + uint32_t mtvec_value = (uint32_t) handler; + asm volatile("csrw mtvec, %0" : : "r" (mtvec_value)); + + flag = 7; + + // 0. Setup the instruction/stack/MMIO regions + uint32_t pmp_cfg = 0x1F1F0000; + asm volatile("csrw pmpcfg0, %0" : : "r" (pmp_cfg)); + uint32_t pmp_addr = (0x80000000 >> 2) & ~((1 << 14) - 1) | ((1 << (14 - 1)) - 1); + asm volatile("csrw pmpaddr2, %0" : : "r" (pmp_addr)); + pmp_addr = (0xFFFFFFE0 >> 2) & ~((1 << 4) - 1) | ((1 << (4 - 1)) - 1); + asm volatile("csrw pmpaddr3, %0" : : "r" (pmp_addr)); + + // 1. Test PMP, TOR in M Mode + pmp_cfg = 0x00000800; // set pmpcfg0.pmp1cfg to (no L, TOR, no RWX) + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + pmp_addr = (BAD_PMP_TOP >> 2); // set pmpaddr1 to the top of range address + asm volatile("csrw pmpaddr1, %0" : : "r" (pmp_addr)); + pmp_addr = (BAD_PMP_BOT >> 2); // set pmpaddr0 to the bottom of range address + asm volatile("csrw pmpaddr0, %0" : : "r" (pmp_addr)); + *(bad_pmp_addr_bot + 4) = 0xDEADBEEF; // should succeed + flag -= 1; + *(bad_pmp_addr_top) = 0xDEADBEEF; // should succeed + flag -= 1; + + // 2. Test PMP, NAPOT with MPRV + uint32_t mstatus = 0x20000; // set mstatus.mprv, mpp should be 2'b00 + asm volatile("csrw mstatus, %0" : : "r" (mstatus)); + *(bad_pmp_addr_bot + 4) = 0xABCD1234; // should fail + *(bad_pmp_addr_top) = 0xABCD1234; // should fail + + // 3. Test PMP, NAPOT with L register + asm volatile("csrc mstatus, %0" : : "r" (mstatus)); // clear mstatus.mprv + pmp_cfg = 0x00008000; // set pmpcfg0.pmp0cfg to (L, TOR, no RWX) + asm volatile("csrs pmpcfg0, %0" : : "r" (pmp_cfg)); + *(bad_pmp_addr_bot + 4) = 0x0987FEDC; // should fail + *(bad_pmp_addr_top) = 0x0987FEDC; // should succeed + flag -= 1; + + return 0; +} diff --git a/verification/pmp-tests/start.S b/verification/pmp-tests/start.S new file mode 100644 index 000000000..9e588dd07 --- /dev/null +++ b/verification/pmp-tests/start.S @@ -0,0 +1,17 @@ + +.global _start +_start: + li sp, 0x8000F000 + jal main + +.global done +done: + la x28, flag + lw x28, 0(x28) +__inf_loop: + j __inf_loop + + +.global flag +.data +flag: .word 0xFF diff --git a/verification/pmp-tests/utility.c b/verification/pmp-tests/utility.c new file mode 100644 index 000000000..b346c8e39 --- /dev/null +++ b/verification/pmp-tests/utility.c @@ -0,0 +1,26 @@ + +#include "utility.h" + +void print(char *string) { + volatile char *magic = (volatile char *)MAGIC_ADDR; + + for(int i = 0; string[i]; i++) { + (*magic) = string[i]; + } +} + +void put_uint32_hex(uint32_t x) { + char buf[10] = {0}; + + for(int i = 0; i < 8; i++) { + uint8_t value = (x & 0xF); + if(value >= 10) { + buf[7-i] = ((value-10) + 'A'); + } else { + buf[7-i] = (value + '0'); + } + x >>= 4; + } + buf[8] = '\n'; + print(buf); +} diff --git a/verification/pmp-tests/utility.h b/verification/pmp-tests/utility.h new file mode 100644 index 000000000..6caaee7c5 --- /dev/null +++ b/verification/pmp-tests/utility.h @@ -0,0 +1,19 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + + #include + + #define MTIME_ADDR 0xFFFFFFE0 + #define MTIMEH_ADDR 0xFFFFFFE4 + #define MTIMECMP_ADDR 0xFFFFFFE8 + #define MTIMECMPH_ADDR 0xFFFFFFEC + #define MSIP_ADDR 0xFFFFFFF0 + #define EXT_ADDR_SET 0xFFFFFFF4 + #define EXT_ADDR_CLEAR 0xFFFFFFF8 + #define MAGIC_ADDR 0xFFFFFFFC + + + void print(char *string); + void put_uint32_hex(uint32_t hex); + +#endif diff --git a/verification/self-tests/RV32C/c_add.S b/verification/self-tests/RV32C/c_add.S new file mode 100644 index 000000000..99cfcfd1d --- /dev/null +++ b/verification/self-tests/RV32C/c_add.S @@ -0,0 +1,81 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_add.S +#----------------------------------------------------------------------------- +# +# Test c_add instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.add, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RV32C_R_OP( 3, c.add, 0x00000002, 0x00000001, 0x00000001 ); + TEST_RV32C_R_OP( 4, c.add, 0x0000000a, 0x00000003, 0x00000007 ); + + TEST_RV32C_R_OP( 5, c.add, 0xffff8000, 0x00000000, 0xffff8000 ); + TEST_RV32C_R_OP( 6, c.add, 0x80000000, 0x80000000, 0x00000000 ); + TEST_RV32C_R_OP( 7, c.add, 0x7fff8000, 0x80000000, 0xffff8000 ); + + TEST_RV32C_R_OP( 8, c.add, 0x00007fff, 0x00000000, 0x00007fff ); + TEST_RV32C_R_OP( 9, c.add, 0x7fffffff, 0x7fffffff, 0x00000000 ); + TEST_RV32C_R_OP( 10, c.add, 0x80007ffe, 0x7fffffff, 0x00007fff ); + + TEST_RV32C_R_OP( 11, c.add, 0x80007fff, 0x80000000, 0x00007fff ); + TEST_RV32C_R_OP( 12, c.add, 0x7fff7fff, 0x7fffffff, 0xffff8000 ); + + TEST_RV32C_R_OP( 13, c.add, 0xffffffff, 0x00000000, 0xffffffff ); + TEST_RV32C_R_OP( 14, c.add, 0x00000000, 0xffffffff, 0x00000001 ); + TEST_RV32C_R_OP( 15, c.add, 0xfffffffe, 0xffffffff, 0xffffffff ); + + TEST_RV32C_R_OP( 16, c.add, 0x80000000, 0x00000001, 0x7fffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 17, c.add, 26, 13 ); + TEST_RV32C_RR_SRC12_EQ_DEST( 18, c.add, -200, -100 ); + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 19, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 20, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 21, 2, c.add, 26, 15, 11 ); + + TEST_RV32C_RR_SRC12_BYPASS( 22, 0, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 23, 0, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 24, 0, 2, c.add, 26, 15, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 25, 1, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 26, 1, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 27, 2, 0, c.add, 26, 15, 11 ); + + TEST_RV32C_RR_SRC21_BYPASS( 28, 0, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 29, 0, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 30, 0, 2, c.add, 26, 15, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 31, 1, 0, c.add, 24, 13, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 32, 1, 1, c.add, 25, 14, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 33, 2, 0, c.add, 26, 15, 11 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_addi.S b/verification/self-tests/RV32C/c_addi.S new file mode 100644 index 000000000..4239b7b77 --- /dev/null +++ b/verification/self-tests/RV32C/c_addi.S @@ -0,0 +1,63 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_addi.S +#----------------------------------------------------------------------------- +# +# Test c_addi instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 2, c.addi, 0x00000000, 0x00000000, 0x00 ); + TEST_RV32C_IMM_OP( 3, c.addi, 0x00000002, 0x00000001, 0x01 ); + TEST_RV32C_IMM_OP( 4, c.addi, 0x0000000a, 0x00000003, 0x07 ); + + TEST_RV32C_IMM_OP( 5, c.addi, 0x00000020, 0x00000001, 0x1f ); + TEST_RV32C_IMM_OP( 6, c.addi, 0x80000000, 0x80000000, 0x00 ); + TEST_RV32C_IMM_OP( 7, c.addi, 0x7fffffef, 0x80000000, 0x2f ); + + TEST_RV32C_IMM_OP( 8, c.addi, 0x1000000e, 0x0fffffff, 0x0f ); + TEST_RV32C_IMM_OP( 9, c.addi, 0x7fffffff, 0x7fffffff, 0x00 ); + TEST_RV32C_IMM_OP( 10, c.addi, 0x8000001e, 0x7fffffff, 0x1f ); + + TEST_RV32C_IMM_OP( 11, c.addi, 0x7fffffe2, 0x80000000, 0x22 ); + TEST_RV32C_IMM_OP( 12, c.addi, 0x7fffffdf, 0x7fffffff, 0x20 ); + + TEST_RV32C_IMM_OP( 13, c.addi, 0xffffffff, 0x00000000, 0x3f ); + TEST_RV32C_IMM_OP( 14, c.addi, 0x00000000, 0xffffffff, 0x01 ); + TEST_RV32C_IMM_OP( 15, c.addi, 0xfffffffe, 0xffffffff, 0x3f ); + + TEST_RV32C_IMM_OP( 16, c.addi, 0x80000000, 0x7fffffff, 0x01 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 17, 0, c.addi, 24, 13, 11 ); + TEST_RV32C_IMM_DEST_BYPASS( 18, 1, c.addi, 23, 13, 10 ); + TEST_RV32C_IMM_DEST_BYPASS( 19, 2, c.addi, 22, 13, 9 ); + + TEST_RV32C_IMM_SRC1_BYPASS( 20, 0, c.addi, 24, 13, 11 ); + TEST_RV32C_IMM_SRC1_BYPASS( 21, 1, c.addi, 23, 13, 10 ); + TEST_RV32C_IMM_SRC1_BYPASS( 22, 2, c.addi, 22, 13, 9 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_addi16sp.S b/verification/self-tests/RV32C/c_addi16sp.S new file mode 100644 index 000000000..a619a0075 --- /dev/null +++ b/verification/self-tests/RV32C/c_addi16sp.S @@ -0,0 +1,65 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_addi16sp.S +#----------------------------------------------------------------------------- +# +# Test c_addi16sp instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + # Immediate values should be multiple of 16 + + TEST_RV32C_IMM_OP_SP( 2, c.addi16sp, 0x00000010, 0x00000000, 0x010 ); + TEST_RV32C_IMM_OP_SP( 3, c.addi16sp, 0x00000111, 0x00000001, 0x110 ); + TEST_RV32C_IMM_OP_SP( 4, c.addi16sp, 0x000000aa, 0x0000003a, 0x070 ); + + TEST_RV32C_IMM_OP_SP( 5, c.addi16sp, 0x00000200, 0x00000010, 0x1f0 ); + TEST_RV32C_IMM_OP_SP( 6, c.addi16sp, 0x800000f0, 0x80000000, 0x0f0 ); + TEST_RV32C_IMM_OP_SP( 7, c.addi16sp, 0x7ffffef0, 0x80000000, 0x2f0 ); + + TEST_RV32C_IMM_OP_SP( 8, c.addi16sp, 0x000000e0, 0xfffffff0, 0x0f0 ); + TEST_RV32C_IMM_OP_SP( 9, c.addi16sp, 0x7ffffe9f, 0x7fffffff, 0x2a0 ); + TEST_RV32C_IMM_OP_SP( 10, c.addi16sp, 0x800001ef, 0x7fffffff, 0x1f0 ); + + TEST_RV32C_IMM_OP_SP( 11, c.addi16sp, 0x7ffffe20, 0x80000000, 0x220 ); + TEST_RV32C_IMM_OP_SP( 12, c.addi16sp, 0x7ffffdff, 0x7fffffff, 0x200 ); + + TEST_RV32C_IMM_OP_SP( 13, c.addi16sp, 0xfffffff0, 0x00000000, 0x3f0 ); + TEST_RV32C_IMM_OP_SP( 14, c.addi16sp, 0x00000000, 0xfffffff0, 0x010 ); + TEST_RV32C_IMM_OP_SP( 15, c.addi16sp, 0xffffffe0, 0xfffffff0, 0x3f0 ); + + TEST_RV32C_IMM_OP_SP( 16, c.addi16sp, 0x80000000, 0x7ffffff0, 0x010 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS_SP( 17, 0, c.addi16sp, 116, 100, 16 ); + TEST_RV32C_IMM_DEST_BYPASS_SP( 18, 1, c.addi16sp, 132, 100, 32 ); + TEST_RV32C_IMM_DEST_BYPASS_SP( 19, 2, c.addi16sp, 148, 100, 48 ); + + TEST_RV32C_IMM_SRC1_BYPASS_SP( 20, 0, c.addi16sp, 116, 100, 16 ); + TEST_RV32C_IMM_SRC1_BYPASS_SP( 21, 1, c.addi16sp, 132, 100, 32 ); + TEST_RV32C_IMM_SRC1_BYPASS_SP( 22, 2, c.addi16sp, 148, 100, 48 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_addi4spn.S b/verification/self-tests/RV32C/c_addi4spn.S new file mode 100644 index 000000000..18c7f4636 --- /dev/null +++ b/verification/self-tests/RV32C/c_addi4spn.S @@ -0,0 +1,66 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_addi4spn.S +#----------------------------------------------------------------------------- +# +# Test c_addi4spn instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + # Immediate values should be multiple of 4 + + TEST_RV32C_IMM_OP_SP_REG( 2, c.addi4spn, 0x00000010, 0x00000000, 0x010 ); + TEST_RV32C_IMM_OP_SP_REG( 3, c.addi4spn, 0x00000111, 0x00000001, 0x110 ); + TEST_RV32C_IMM_OP_SP_REG( 4, c.addi4spn, 0x000000aa, 0x0000003a, 0x070 ); + + TEST_RV32C_IMM_OP_SP_REG( 5, c.addi4spn, 0x00000200, 0x00000010, 0x1f0 ); + TEST_RV32C_IMM_OP_SP_REG( 6, c.addi4spn, 0x800000f0, 0x80000000, 0x0f0 ); + TEST_RV32C_IMM_OP_SP_REG( 7, c.addi4spn, 0x800002fc, 0x80000000, 0x2fc ); + + TEST_RV32C_IMM_OP_SP_REG( 8, c.addi4spn, 0x000000e0, 0xfffffff0, 0x0f0 ); + TEST_RV32C_IMM_OP_SP_REG( 9, c.addi4spn, 0x800002a7, 0x7fffffff, 0x2a8 ); + TEST_RV32C_IMM_OP_SP_REG( 10, c.addi4spn, 0x800001ef, 0x7fffffff, 0x1f0 ); + + TEST_RV32C_IMM_OP_SP_REG( 11, c.addi4spn, 0x800003fc, 0x80000000, 0x3fc ); + TEST_RV32C_IMM_OP_SP_REG( 12, c.addi4spn, 0x100003fb, 0x0fffffff, 0x3fc ); + + TEST_RV32C_IMM_OP_SP_REG( 13, c.addi4spn, 0x000003f0, 0x00000000, 0x3f0 ); + TEST_RV32C_IMM_OP_SP_REG( 14, c.addi4spn, 0x00000000, 0xfffffff0, 0x010 ); + TEST_RV32C_IMM_OP_SP_REG( 15, c.addi4spn, 0x000003e0, 0xfffffff0, 0x3f0 ); + + TEST_RV32C_IMM_OP_SP_REG( 16, c.addi4spn, 0x80000000, 0x7ffffff0, 0x010 ); + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS_SP_REG( 17, 0, c.addi4spn, 154, 150, 4 ); + TEST_RV32C_IMM_DEST_BYPASS_SP_REG( 18, 1, c.addi4spn, 158, 150, 8 ); + TEST_RV32C_IMM_DEST_BYPASS_SP_REG( 19, 2, c.addi4spn, 162, 150, 12 ); + + TEST_RV32C_IMM_SRC1_BYPASS_SP_REG( 20, 0, c.addi4spn, 154, 150, 4 ); + TEST_RV32C_IMM_SRC1_BYPASS_SP_REG( 21, 1, c.addi4spn, 158, 150, 8 ); + TEST_RV32C_IMM_SRC1_BYPASS_SP_REG( 22, 2, c.addi4spn, 162, 150, 12 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_and.S b/verification/self-tests/RV32C/c_and.S new file mode 100644 index 000000000..14adee5f7 --- /dev/null +++ b/verification/self-tests/RV32C/c_and.S @@ -0,0 +1,64 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_and.S +#----------------------------------------------------------------------------- +# +# Test c_and instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 3, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_R_OP( 4, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 5, c.and, 0xf000f000, 0xf00ff00f, 0xf0f0f0f0 ); + TEST_RV32C_R_OP( 6, c.and, 0x00000000, 0x00000000, 0xf0f0f0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 7, c.and, 0xff00ff00, 0xff00ff00 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 8, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_DEST_BYPASS( 9, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_DEST_BYPASS( 10, 2, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC12_BYPASS( 11, 0, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 12, 0, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 13, 0, 2, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 14, 1, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 15, 1, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 16, 2, 0, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC21_BYPASS( 17, 0, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 18, 0, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 19, 0, 2, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 20, 1, 0, c.and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 21, 1, 1, c.and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 22, 2, 0, c.and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_andi.S b/verification/self-tests/RV32C/c_andi.S new file mode 100644 index 000000000..3d00e2f6c --- /dev/null +++ b/verification/self-tests/RV32C/c_andi.S @@ -0,0 +1,47 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_andi.S +#----------------------------------------------------------------------------- +# +# Test c_andi instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 2, c.andi, 0xff00ff00, 0xff00ff00, 0x3f ); + TEST_RV32C_IMM_OP( 3, c.andi, 0x00000010, 0x0ff00ff0, 0x10 ); + TEST_RV32C_IMM_OP( 4, c.andi, 0x0000000f, 0x00ff00ff, 0x0f ); + TEST_RV32C_IMM_OP( 5, c.andi, 0x00000000, 0xf00ff00f, 0x00 ); + TEST_RV32C_IMM_OP( 6, c.andi, 0x00000000, 0x00000000, 0x20 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 7, 0, c.andi, 0xfff00ff0, 0xfff00ff0, 0x3f ); + TEST_RV32C_IMM_DEST_BYPASS( 8, 1, c.andi, 0xe0ff00f0, 0xe0ff00ff, 0x30 ); + TEST_RV32C_IMM_DEST_BYPASS( 9, 2, c.andi, 0x0000000f, 0xf00ff00f, 0x0f ); + + TEST_RV32C_IMM_SRC1_BYPASS( 10, 0, c.andi, 0x0ff00ff0, 0x0ff00ff0, 0x3f ); + TEST_RV32C_IMM_SRC1_BYPASS( 11, 1, c.andi, 0x00ff00f0, 0x00ff00ff, 0x30 ); + TEST_RV32C_IMM_SRC1_BYPASS( 12, 2, c.andi, 0x0000000f, 0xf00ff00f, 0x0f ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_beqz.S b/verification/self-tests/RV32C/c_beqz.S new file mode 100644 index 000000000..5a7ad379c --- /dev/null +++ b/verification/self-tests/RV32C/c_beqz.S @@ -0,0 +1,60 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_beqz.S +#----------------------------------------------------------------------------- +# +# Test c_beqz instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_RV32C_BR2_OP_TAKEN( 3, c.beqz, 0); + + TEST_RV32C_BR2_OP_NOTTAKEN( 4, c.beqz, 1); + TEST_RV32C_BR2_OP_NOTTAKEN( 5, c.beqz, -1); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_BR2_SRC_BYPASS( 6, 0, c.beqz, -1); + TEST_RV32C_BR2_SRC_BYPASS( 7, 1, c.beqz, -1); + TEST_RV32C_BR2_SRC_BYPASS( 8, 2, c.beqz, -1); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 9, x8, 3, \ + li x8, 1; \ + li x15, 0; + c.beqz x15, 1f; \ + addi x8, x8, 1; \ + addi x8, x8, 1; \ + addi x8, x8, 1; \ + addi x8, x8, 1; \ +1: addi x8, x8, 1; \ + addi x8, x8, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_bnez.S b/verification/self-tests/RV32C/c_bnez.S new file mode 100644 index 000000000..3f705516a --- /dev/null +++ b/verification/self-tests/RV32C/c_bnez.S @@ -0,0 +1,59 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_bnez.S +#----------------------------------------------------------------------------- +# +# Test c_bnez instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_RV32C_BR2_OP_TAKEN( 3, c.bnez, 1); + TEST_RV32C_BR2_OP_TAKEN( 4, c.bnez, -1); + + TEST_RV32C_BR2_OP_NOTTAKEN( 5, c.bnez, 0); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_BR2_SRC_BYPASS( 6, 0, c.bnez, 0); + TEST_RV32C_BR2_SRC_BYPASS( 7, 1, c.bnez, 0); + TEST_RV32C_BR2_SRC_BYPASS( 8, 2, c.bnez, 0); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 9, x11, 3, \ + li x11, 1; \ + c.bnez x11, 1f; \ + addi x11, x11, 1; \ + addi x11, x11, 1; \ + addi x11, x11, 1; \ + addi x11, x11, 1; \ +1: addi x11, x11, 1; \ + addi x11, x11, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_j.S b/verification/self-tests/RV32C/c_j.S new file mode 100644 index 000000000..cb09ed2df --- /dev/null +++ b/verification/self-tests/RV32C/c_j.S @@ -0,0 +1,86 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_j.S +#----------------------------------------------------------------------------- +# +# Test c_j instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li x31, 0 + +linkaddr_2: + c.j target_2 + nop + nop + + j fail + +target_2: + la x3, linkaddr_2 + addi x3, x3, 2 + beq x3, x1, fail + + #------------------------------------------------------------- + # Test 3: Check r0 target and that r31 is not modified + #------------------------------------------------------------- + +test_3: + li TESTNUM, 3 + li x31, 0 + +linkaddr_3: + c.j target_3 + nop + + j fail + +target_3: + bne x31, x0, fail + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_JAL_SRC1_BYPASS( 4, 0, c.j ); + TEST_RV32C_JAL_SRC1_BYPASS( 5, 1, c.j ); + TEST_RV32C_JAL_SRC1_BYPASS( 6, 2, c.j ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 7, x3, 3, \ + li x3, 1; \ + c.j 1f; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ +1: addi x3, x3, 1; \ + addi x3, x3, 1; \ + ) + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_jal.S b/verification/self-tests/RV32C/c_jal.S new file mode 100644 index 000000000..458c3039d --- /dev/null +++ b/verification/self-tests/RV32C/c_jal.S @@ -0,0 +1,86 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_jal.S +#----------------------------------------------------------------------------- +# +# Test c_jal instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li x31, 0 + +linkaddr_2: + c.jal target_2 + nop + nop + + j fail + +target_2: + la x3, linkaddr_2 + addi x3, x3, 2 + bne x3, x1, fail + + #------------------------------------------------------------- + # Test 3: Check r0 target and that r31 is not modified + #------------------------------------------------------------- + +test_3: + li TESTNUM, 3 + li x31, 0 + +linkaddr_3: + c.jal target_3 + nop + + j fail + +target_3: + bne x31, x0, fail + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_JAL_SRC1_BYPASS( 4, 0, c.jal ); + TEST_RV32C_JAL_SRC1_BYPASS( 5, 1, c.jal ); + TEST_RV32C_JAL_SRC1_BYPASS( 6, 2, c.jal ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 7, x3, 3, \ + li x3, 1; \ + c.jal 1f; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ +1: addi x3, x3, 1; \ + addi x3, x3, 1; \ + ) + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_jalr.S b/verification/self-tests/RV32C/c_jalr.S new file mode 100644 index 000000000..e2d41ba54 --- /dev/null +++ b/verification/self-tests/RV32C/c_jalr.S @@ -0,0 +1,89 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_jalr.S +#----------------------------------------------------------------------------- +# +# Test c_jalr instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li x31, 0 + la x2, target_2 + +linkaddr_2: + c.jalr x2 + nop + nop + + j fail + +target_2: + la x3, linkaddr_2 + addi x3, x3, 2 + bne x3, x1, fail + + #------------------------------------------------------------- + # Test 3: Check r0 target and that r31 is not modified + #------------------------------------------------------------- + +test_3: + li TESTNUM, 3 + li x31, 0 + la x3, target_3 + +linkaddr_3: + c.jalr x3 + nop + + j fail + +target_3: + bne x31, x0, fail + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_JALR_SRC1_BYPASS( 4, 0, c.jalr ); + TEST_RV32C_JALR_SRC1_BYPASS( 5, 1, c.jalr ); + TEST_RV32C_JALR_SRC1_BYPASS( 6, 2, c.jalr ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 7, x3, 3, \ + li x3, 1; \ + la x2, 1f; + c.jalr x2; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ +1: addi x3, x3, 1; \ + addi x3, x3, 1; \ + ) + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_jr.S b/verification/self-tests/RV32C/c_jr.S new file mode 100644 index 000000000..f4616e2ef --- /dev/null +++ b/verification/self-tests/RV32C/c_jr.S @@ -0,0 +1,89 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_jr.S +#----------------------------------------------------------------------------- +# +# Test c_jr instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li x31, 0 + la x2, target_2 + +linkaddr_2: + c.jr x2 + nop + nop + + j fail + +target_2: + la x3, linkaddr_2 + addi x3, x3, 2 + beq x3, x1, fail + + #------------------------------------------------------------- + # Test 3: Check r0 target and that r31 is not modified + #------------------------------------------------------------- + +test_3: + li TESTNUM, 3 + li x31, 0 + la x3, target_3 + +linkaddr_3: + c.jr x3 + nop + + j fail + +target_3: + bne x31, x0, fail + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_JALR_SRC1_BYPASS( 4, 0, c.jr ); + TEST_RV32C_JALR_SRC1_BYPASS( 5, 1, c.jr ); + TEST_RV32C_JALR_SRC1_BYPASS( 6, 2, c.jr ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 7, x3, 3, \ + li x3, 1; \ + la x2, 1f; + c.jr x2; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ + addi x3, x3, 1; \ +1: addi x3, x3, 1; \ + addi x3, x3, 1; \ + ) + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_li.S b/verification/self-tests/RV32C/c_li.S new file mode 100644 index 000000000..3db5b5e71 --- /dev/null +++ b/verification/self-tests/RV32C/c_li.S @@ -0,0 +1,34 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_li.S +#----------------------------------------------------------------------------- +# +# Test c_li instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_LI(2, c.li, 0xffffffff, 0x3f, 0); + TEST_RV32C_LI(3, c.li, 0x0000001f, 0x1f, 0); + TEST_RV32C_LI(4, c.li, 0x00000001, 0x10, 4); + TEST_RV32C_LI(5, c.li, 0xffffffff, 0x20, 20); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_lui.S b/verification/self-tests/RV32C/c_lui.S new file mode 100644 index 000000000..e5db357b2 --- /dev/null +++ b/verification/self-tests/RV32C/c_lui.S @@ -0,0 +1,34 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_lui.S +#----------------------------------------------------------------------------- +# +# Test c_lui instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_LUI(2, c.lui, 0x0001f000, 0x1f, 0); + TEST_RV32C_LUI(3, c.lui, 0xfffff000, 0xfffff, 0); + TEST_RV32C_LUI(4, c.lui, 0xffffe000, 0xfffe0, 4); + TEST_RV32C_LUI(5, c.lui, 0xfffffff0, 0xffff0, 12); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_lw.S b/verification/self-tests/RV32C/c_lw.S new file mode 100644 index 000000000..152b88925 --- /dev/null +++ b/verification/self-tests/RV32C/c_lw.S @@ -0,0 +1,77 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_lw.S +#----------------------------------------------------------------------------- +# +# Test c_lw instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_LD_OP( 2, c.lw, 0x00ff00ff, 0, tdat ); + TEST_RV32C_LD_OP( 3, c.lw, 0xff00ff00, 4, tdat ); + TEST_RV32C_LD_OP( 4, c.lw, 0x0ff00ff0, 8, tdat ); + TEST_RV32C_LD_OP( 5, c.lw, 0xf00ff00f, 12, tdat ); + + # Test with a negative base + + TEST_CASE( 6, x13, 0x00ff00ff, \ + la x11, tdat; \ + addi x11, x11, -32; \ + c.lw x13, 32(x11); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_LD_DEST_BYPASS( 7, 0, c.lw, 0x0ff00ff0, 4, tdat2 ); + TEST_RV32C_LD_DEST_BYPASS( 8, 1, c.lw, 0xf00ff00f, 4, tdat3 ); + TEST_RV32C_LD_DEST_BYPASS( 9, 2, c.lw, 0xff00ff00, 4, tdat1 ); + + TEST_RV32C_LD_SRC1_BYPASS( 10, 0, c.lw, 0x0ff00ff0, 4, tdat2 ); + TEST_RV32C_LD_SRC1_BYPASS( 11, 1, c.lw, 0xf00ff00f, 4, tdat3 ); + TEST_RV32C_LD_SRC1_BYPASS( 12, 2, c.lw, 0xff00ff00, 4, tdat1 ); + + #------------------------------------------------------------- + # Test write-after-write hazard + #------------------------------------------------------------- + + TEST_CASE( 13, x12, 2, \ + la x13, tdat; \ + c.lw x12, 0(x13); \ + li x12, 2; \ + ) + + TEST_CASE( 14, x12, 2, \ + la x13, tdat; \ + c.lw x12, 0(x13); \ + nop; \ + li x12, 2; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0x00ff00ff +tdat2: .word 0xff00ff00 +tdat3: .word 0x0ff00ff0 +tdat4: .word 0xf00ff00f + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_lwsp.S b/verification/self-tests/RV32C/c_lwsp.S new file mode 100644 index 000000000..42642702c --- /dev/null +++ b/verification/self-tests/RV32C/c_lwsp.S @@ -0,0 +1,77 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_lwsp.S +#----------------------------------------------------------------------------- +# +# Test c_lwsp instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_LDSP_OP( 2, c.lwsp, 0x00ff00ff, 0, tdat ); + TEST_RV32C_LDSP_OP( 3, c.lwsp, 0xff00ff00, 4, tdat ); + TEST_RV32C_LDSP_OP( 4, c.lwsp, 0x0ff00ff0, 8, tdat ); + TEST_RV32C_LDSP_OP( 5, c.lwsp, 0xf00ff00f, 12, tdat ); + + # Test with a negative base + + TEST_CASE( 6, x13, 0x00ff00ff, \ + la x2, tdat; \ + addi x2, x2, -32; \ + c.lwsp x13, 32(x2); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_LDSP_DEST_BYPASS( 7, 0, c.lwsp, 0x0ff00ff0, 4, tdat2 ); + TEST_RV32C_LDSP_DEST_BYPASS( 8, 1, c.lwsp, 0xf00ff00f, 4, tdat3 ); + TEST_RV32C_LDSP_DEST_BYPASS( 9, 2, c.lwsp, 0xff00ff00, 4, tdat1 ); + + TEST_RV32C_LDSP_SRC1_BYPASS( 10, 0, c.lwsp, 0x0ff00ff0, 4, tdat2 ); + TEST_RV32C_LDSP_SRC1_BYPASS( 11, 1, c.lwsp, 0xf00ff00f, 4, tdat3 ); + TEST_RV32C_LDSP_SRC1_BYPASS( 12, 2, c.lwsp, 0xff00ff00, 4, tdat1 ); + + #------------------------------------------------------------- + # Test write-after-write hazard + #------------------------------------------------------------- + + TEST_CASE( 13, x12, 2, \ + la x2, tdat; \ + c.lwsp x12, 0(x2); \ + li x12, 2; \ + ) + + TEST_CASE( 14, x12, 2, \ + la x2, tdat; \ + c.lwsp x12, 0(x2); \ + nop; \ + li x12, 2; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0x00ff00ff +tdat2: .word 0xff00ff00 +tdat3: .word 0x0ff00ff0 +tdat4: .word 0xf00ff00f + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_mv.S b/verification/self-tests/RV32C/c_mv.S new file mode 100644 index 000000000..0ddd8023d --- /dev/null +++ b/verification/self-tests/RV32C/c_mv.S @@ -0,0 +1,85 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_mv.S +#----------------------------------------------------------------------------- +# +# Test c_mv instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + # 4th argument has no effect + + TEST_RV32C_R_OP( 2, c.mv, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RV32C_R_OP( 3, c.mv, 0x00000002, 0x00000000, 0x00000002 ); + TEST_RV32C_R_OP( 4, c.mv, 0x0000000a, 0x00000000, 0x0000000a ); + + TEST_RV32C_R_OP( 5, c.mv, 0xffff8000, 0x00000000, 0xffff8000 ); + TEST_RV32C_R_OP( 6, c.mv, 0x80000000, 0x00000000, 0x80000000 ); + TEST_RV32C_R_OP( 7, c.mv, 0x7fff8000, 0x00000000, 0x7fff8000 ); + + TEST_RV32C_R_OP( 8, c.mv, 0x00007fff, 0x00000000, 0x00007fff ); + TEST_RV32C_R_OP( 9, c.mv, 0x7fffffff, 0x00000000, 0x7fffffff ); + TEST_RV32C_R_OP( 10, c.mv, 0x80007ffe, 0x00000000, 0x80007ffe ); + + TEST_RV32C_R_OP( 11, c.mv, 0x80007fff, 0x00000000, 0x80007fff ); + TEST_RV32C_R_OP( 12, c.mv, 0x7fff7fff, 0x00000000, 0x7fff7fff ); + + TEST_RV32C_R_OP( 13, c.mv, 0xffffffff, 0x00000000, 0xffffffff ); + TEST_RV32C_R_OP( 14, c.mv, 0x00000001, 0x00000000, 0x00000001 ); + TEST_RV32C_R_OP( 15, c.mv, 0xfffffffe, 0x00000000, 0xfffffffe ); + + TEST_RV32C_R_OP( 16, c.mv, 0x80000000, 0x00000000, 0x80000000 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 17, c.mv, 13, 13 ); + TEST_RV32C_RR_SRC12_EQ_DEST( 18, c.mv, -100, -100 ); + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + # 5th argument has no effect + + TEST_RV32C_RR_DEST_BYPASS( 19, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 20, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_DEST_BYPASS( 21, 2, c.mv, 13, 0, 13 ); + + TEST_RV32C_RR_SRC12_BYPASS( 22, 0, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 23, 0, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_SRC12_BYPASS( 24, 0, 2, c.mv, 13, 0, 13 ); + TEST_RV32C_RR_SRC12_BYPASS( 25, 1, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 26, 1, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_SRC12_BYPASS( 27, 2, 0, c.mv, 13, 0, 13 ); + + TEST_RV32C_RR_SRC21_BYPASS( 28, 0, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 29, 0, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_SRC21_BYPASS( 30, 0, 2, c.mv, 13, 0, 13 ); + TEST_RV32C_RR_SRC21_BYPASS( 31, 1, 0, c.mv, 11, 0, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 32, 1, 1, c.mv, 12, 0, 12 ); + TEST_RV32C_RR_SRC21_BYPASS( 33, 2, 0, c.mv, 13, 0, 13 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_or.S b/verification/self-tests/RV32C/c_or.S new file mode 100644 index 000000000..3e97e1482 --- /dev/null +++ b/verification/self-tests/RV32C/c_or.S @@ -0,0 +1,62 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_or.S +#----------------------------------------------------------------------------- +# +# Test c_or instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 3, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_R_OP( 4, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 5, c.or, 0xf0fff0ff, 0xf00ff00f, 0xf0f0f0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 6, c.or, 0xff00ff00, 0xff00ff00 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 7, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_DEST_BYPASS( 8, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_DEST_BYPASS( 9, 2, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC12_BYPASS( 10, 0, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 11, 0, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 12, 0, 2, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 13, 1, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 14, 1, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 15, 2, 0, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC21_BYPASS( 16, 0, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 17, 0, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 18, 0, 2, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 19, 1, 0, c.or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 20, 1, 1, c.or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 21, 2, 0, c.or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_slli.S b/verification/self-tests/RV32C/c_slli.S new file mode 100644 index 000000000..68369f879 --- /dev/null +++ b/verification/self-tests/RV32C/c_slli.S @@ -0,0 +1,60 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_slli.S +#----------------------------------------------------------------------------- +# +# Test c_slli instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 2, c.slli, 0x00000000, 0x00000000, 11 ); + TEST_RV32C_IMM_OP( 3, c.slli, 0x00000002, 0x00000001, 1 ); + TEST_RV32C_IMM_OP( 4, c.slli, 0x00000080, 0x00000001, 7 ); + TEST_RV32C_IMM_OP( 5, c.slli, 0x00004000, 0x00000001, 14 ); + TEST_RV32C_IMM_OP( 6, c.slli, 0x80000000, 0x00000001, 31 ); + + TEST_RV32C_IMM_OP( 7, c.slli, 0x00000000, 0x00000000, 23 ); + TEST_RV32C_IMM_OP( 8, c.slli, 0xfffffffe, 0xffffffff, 1 ); + TEST_RV32C_IMM_OP( 9, c.slli, 0xffffff80, 0xffffffff, 7 ); + TEST_RV32C_IMM_OP( 10, c.slli, 0xffffc000, 0xffffffff, 14 ); + TEST_RV32C_IMM_OP( 11, c.slli, 0x80000000, 0xffffffff, 31 ); + + TEST_RV32C_IMM_OP( 12, c.slli, 0x00000000, 0x00000000, 30 ); + TEST_RV32C_IMM_OP( 13, c.slli, 0x42424242, 0x21212121, 1 ); + TEST_RV32C_IMM_OP( 14, c.slli, 0x90909080, 0x21212121, 7 ); + TEST_RV32C_IMM_OP( 15, c.slli, 0x48484000, 0x21212121, 14 ); + TEST_RV32C_IMM_OP( 16, c.slli, 0x80000000, 0x21212121, 31 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 17, 0, c.slli, 0x00000080, 0x00000001, 7 ); + TEST_RV32C_IMM_DEST_BYPASS( 18, 1, c.slli, 0x00004000, 0x00000001, 14 ); + TEST_RV32C_IMM_DEST_BYPASS( 19, 2, c.slli, 0x80000000, 0x00000001, 31 ); + + TEST_RV32C_IMM_SRC1_BYPASS( 20, 0, c.slli, 0x00000080, 0x00000001, 7 ); + TEST_RV32C_IMM_SRC1_BYPASS( 21, 1, c.slli, 0x00004000, 0x00000001, 14 ); + TEST_RV32C_IMM_SRC1_BYPASS( 22, 2, c.slli, 0x80000000, 0x00000001, 31 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_srai.S b/verification/self-tests/RV32C/c_srai.S new file mode 100644 index 000000000..0155bc141 --- /dev/null +++ b/verification/self-tests/RV32C/c_srai.S @@ -0,0 +1,60 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_srai.S +#----------------------------------------------------------------------------- +# +# Test c_srai instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 2, c.srai, 0x00000000, 0x00000000, 16 ); + TEST_RV32C_IMM_OP( 3, c.srai, 0xc0000000, 0x80000000, 1 ); + TEST_RV32C_IMM_OP( 4, c.srai, 0xff000000, 0x80000000, 7 ); + TEST_RV32C_IMM_OP( 5, c.srai, 0xfffe0000, 0x80000000, 14 ); + TEST_RV32C_IMM_OP( 6, c.srai, 0xffffffff, 0x80000001, 31 ); + + TEST_RV32C_IMM_OP( 7, c.srai, 0x00007fff, 0x7fffffff, 16 ); + TEST_RV32C_IMM_OP( 8, c.srai, 0x3fffffff, 0x7fffffff, 1 ); + TEST_RV32C_IMM_OP( 9, c.srai, 0x00ffffff, 0x7fffffff, 7 ); + TEST_RV32C_IMM_OP( 10, c.srai, 0x0001ffff, 0x7fffffff, 14 ); + TEST_RV32C_IMM_OP( 11, c.srai, 0x00000000, 0x7fffffff, 31 ); + + TEST_RV32C_IMM_OP( 12, c.srai, 0xffff8181, 0x81818181, 16 ); + TEST_RV32C_IMM_OP( 13, c.srai, 0xc0c0c0c0, 0x81818181, 1 ); + TEST_RV32C_IMM_OP( 14, c.srai, 0xff030303, 0x81818181, 7 ); + TEST_RV32C_IMM_OP( 15, c.srai, 0xfffe0606, 0x81818181, 14 ); + TEST_RV32C_IMM_OP( 16, c.srai, 0xffffffff, 0x81818181, 31 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 17, 0, c.srai, 0xff000000, 0x80000000, 7 ); + TEST_RV32C_IMM_DEST_BYPASS( 18, 1, c.srai, 0xfffe0000, 0x80000000, 14 ); + TEST_RV32C_IMM_DEST_BYPASS( 19, 2, c.srai, 0xffffffff, 0x80000001, 31 ); + + TEST_RV32C_IMM_SRC1_BYPASS( 20, 0, c.srai, 0xff000000, 0x80000000, 7 ); + TEST_RV32C_IMM_SRC1_BYPASS( 21, 1, c.srai, 0xfffe0000, 0x80000000, 14 ); + TEST_RV32C_IMM_SRC1_BYPASS( 22, 2, c.srai, 0xffffffff, 0x80000001, 31 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_srli.S b/verification/self-tests/RV32C/c_srli.S new file mode 100644 index 000000000..aa82f94ea --- /dev/null +++ b/verification/self-tests/RV32C/c_srli.S @@ -0,0 +1,59 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_srli.S +#----------------------------------------------------------------------------- +# +# Test c_srli instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_OP( 3, c.srli, 0x7fffc000, 0xffff8000, 1 ); + TEST_RV32C_IMM_OP( 4, c.srli, 0x01ffff00, 0xffff8000, 7 ); + TEST_RV32C_IMM_OP( 5, c.srli, 0x0003fffe, 0xffff8000, 14 ); + TEST_RV32C_IMM_OP( 6, c.srli, 0x0001ffff, 0xffff8001, 15 ); + + TEST_RV32C_IMM_OP( 7, c.srli, 0x00000000, 0x00000000, 17 ); + TEST_RV32C_IMM_OP( 8, c.srli, 0x7fffffff, 0xffffffff, 1 ); + TEST_RV32C_IMM_OP( 9, c.srli, 0x01ffffff, 0xffffffff, 7 ); + TEST_RV32C_IMM_OP( 10, c.srli, 0x0003ffff, 0xffffffff, 14 ); + TEST_RV32C_IMM_OP( 11, c.srli, 0x00000001, 0xffffffff, 31 ); + + TEST_RV32C_IMM_OP( 12, c.srli, 0x00000000, 0x00000000, 30 ); + TEST_RV32C_IMM_OP( 13, c.srli, 0x10909090, 0x21212121, 1 ); + TEST_RV32C_IMM_OP( 14, c.srli, 0x00424242, 0x21212121, 7 ); + TEST_RV32C_IMM_OP( 15, c.srli, 0x00008484, 0x21212121, 14 ); + TEST_RV32C_IMM_OP( 16, c.srli, 0x00000000, 0x21212121, 31 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_IMM_DEST_BYPASS( 17, 0, c.srli, 0x7fffc000, 0xffff8000, 1 ); + TEST_RV32C_IMM_DEST_BYPASS( 18, 1, c.srli, 0x0003fffe, 0xffff8000, 14 ); + TEST_RV32C_IMM_DEST_BYPASS( 19, 2, c.srli, 0x0001ffff, 0xffff8000, 15 ); + + TEST_RV32C_IMM_SRC1_BYPASS( 20, 0, c.srli, 0x7fffc000, 0xffff8000, 1 ); + TEST_RV32C_IMM_SRC1_BYPASS( 21, 1, c.srli, 0x0003fffe, 0xffff8000, 14 ); + TEST_RV32C_IMM_SRC1_BYPASS( 22, 2, c.srli, 0x0001ffff, 0xffff8000, 15 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_sub.S b/verification/self-tests/RV32C/c_sub.S new file mode 100644 index 000000000..8e467caf4 --- /dev/null +++ b/verification/self-tests/RV32C/c_sub.S @@ -0,0 +1,77 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_sub.S +#----------------------------------------------------------------------------- +# +# Test c_sub instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.sub, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RV32C_R_OP( 3, c.sub, 0x00000000, 0x00000001, 0x00000001 ); + TEST_RV32C_R_OP( 4, c.sub, 0xfffffffc, 0x00000003, 0x00000007 ); + + TEST_RV32C_R_OP( 5, c.sub, 0x00008000, 0x00000000, 0xffff8000 ); + TEST_RV32C_R_OP( 6, c.sub, 0x80000000, 0x80000000, 0x00000000 ); + TEST_RV32C_R_OP( 7, c.sub, 0x80008000, 0x80000000, 0xffff8000 ); + + TEST_RV32C_R_OP( 8, c.sub, 0xffff8001, 0x00000000, 0x00007fff ); + TEST_RV32C_R_OP( 9, c.sub, 0x7fffffff, 0x7fffffff, 0x00000000 ); + TEST_RV32C_R_OP( 10, c.sub, 0x7fff8000, 0x7fffffff, 0x00007fff ); + + TEST_RV32C_R_OP( 11, c.sub, 0x7fff8001, 0x80000000, 0x00007fff ); + TEST_RV32C_R_OP( 12, c.sub, 0x80007fff, 0x7fffffff, 0xffff8000 ); + + TEST_RV32C_R_OP( 13, c.sub, 0x00000001, 0x00000000, 0xffffffff ); + TEST_RV32C_R_OP( 14, c.sub, 0xfffffffe, 0xffffffff, 0x00000001 ); + TEST_RV32C_R_OP( 15, c.sub, 0x00000000, 0xffffffff, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 16, c.sub, 0, 13 ); + TEST_RV32C_RR_SRC12_EQ_DEST( 17, c.sub, 0, -5 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 18, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 19, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_DEST_BYPASS( 20, 2, c.sub, 4, 15, 11 ); + + TEST_RV32C_RR_SRC12_BYPASS( 21, 0, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 22, 0, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 23, 0, 2, c.sub, 4, 15, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 24, 1, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 25, 1, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_SRC12_BYPASS( 26, 2, 0, c.sub, 4, 15, 11 ); + + TEST_RV32C_RR_SRC21_BYPASS( 27, 0, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 28, 0, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 29, 0, 2, c.sub, 4, 15, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 30, 1, 0, c.sub, 2, 13, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 31, 1, 1, c.sub, 3, 14, 11 ); + TEST_RV32C_RR_SRC21_BYPASS( 32, 2, 0, c.sub, 4, 15, 11 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_sw.S b/verification/self-tests/RV32C/c_sw.S new file mode 100644 index 000000000..8df5f2219 --- /dev/null +++ b/verification/self-tests/RV32C/c_sw.S @@ -0,0 +1,75 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_sw.S +#----------------------------------------------------------------------------- +# +# Test c_sw instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_ST_OP( 2, c.lw, c.sw, 0x00aa00aa, 0, tdat ); + TEST_RV32C_ST_OP( 3, c.lw, c.sw, 0xaa00aa00, 4, tdat ); + TEST_RV32C_ST_OP( 4, c.lw, c.sw, 0x0aa00aa0, 8, tdat ); + TEST_RV32C_ST_OP( 5, c.lw, c.sw, 0xa00aa00a, 12, tdat ); + + # Test with a negative base + + TEST_CASE( 6, x13, 0x12345678, \ + la x11, tdat9; \ + li x12, 0x12345678; \ + addi x14, x11, -32; \ + c.sw x12, 32(x14); \ + c.lw x13, 0(x11); \ + ) + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_ST_SRC12_BYPASS( 7, 0, 0, c.lw, c.sw, 0xaabbccdd, 0, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 8, 0, 1, c.lw, c.sw, 0xdaabbccd, 4, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 9, 0, 2, c.lw, c.sw, 0xddaabbcc, 8, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 10, 1, 0, c.lw, c.sw, 0xcddaabbc, 12, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 11, 1, 1, c.lw, c.sw, 0xccddaabb, 16, tdat ); + TEST_RV32C_ST_SRC12_BYPASS( 12, 2, 0, c.lw, c.sw, 0xbccddaab, 20, tdat ); + + TEST_RV32C_ST_SRC21_BYPASS( 13, 0, 0, c.lw, c.sw, 0x00112233, 0, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 14, 0, 1, c.lw, c.sw, 0x30011223, 4, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 15, 0, 2, c.lw, c.sw, 0x33001122, 8, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 16, 1, 0, c.lw, c.sw, 0x23300112, 12, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 17, 1, 1, c.lw, c.sw, 0x22330011, 16, tdat ); + TEST_RV32C_ST_SRC21_BYPASS( 18, 2, 0, c.lw, c.sw, 0x12233001, 20, tdat ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0xdeadbeef +tdat2: .word 0xdeadbeef +tdat3: .word 0xdeadbeef +tdat4: .word 0xdeadbeef +tdat5: .word 0xdeadbeef +tdat6: .word 0xdeadbeef +tdat7: .word 0xdeadbeef +tdat8: .word 0xdeadbeef +tdat9: .word 0xdeadbeef +tdat10: .word 0xdeadbeef + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_swsp.S b/verification/self-tests/RV32C/c_swsp.S new file mode 100644 index 000000000..7b6250616 --- /dev/null +++ b/verification/self-tests/RV32C/c_swsp.S @@ -0,0 +1,75 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_swsp.S +#----------------------------------------------------------------------------- +# +# Test c_swsp instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_RV32C_STSP_OP( 2, c.lwsp, c.swsp, 0x00aa00aa, 0, tdat ); + TEST_RV32C_STSP_OP( 3, c.lwsp, c.swsp, 0xaa00aa00, 4, tdat ); + TEST_RV32C_STSP_OP( 4, c.lwsp, c.swsp, 0x0aa00aa0, 8, tdat ); + TEST_RV32C_STSP_OP( 5, c.lwsp, c.swsp, 0xa00aa00a, 12, tdat ); + + # Test with a negative base + + TEST_CASE( 6, x13, 0x12345678, \ + la x11, tdat9; \ + li x12, 0x12345678; \ + addi x2, x11, -32; \ + c.swsp x12, 32(x2); \ + c.lwsp x13, 32(x2); \ + ) + + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_STSP_SRC12_BYPASS( 7, 0, 0, c.lwsp, c.swsp, 0xaabbccdd, 0, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 8, 0, 1, c.lwsp, c.swsp, 0xdaabbccd, 4, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 9, 0, 2, c.lwsp, c.swsp, 0xddaabbcc, 8, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 10, 1, 0, c.lwsp, c.swsp, 0xcddaabbc, 12, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 11, 1, 1, c.lwsp, c.swsp, 0xccddaabb, 16, tdat ); + TEST_RV32C_STSP_SRC12_BYPASS( 12, 2, 0, c.lwsp, c.swsp, 0xbccddaab, 20, tdat ); + + TEST_RV32C_STSP_SRC21_BYPASS( 13, 0, 0, c.lwsp, c.swsp, 0x00112233, 0, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 14, 0, 1, c.lwsp, c.swsp, 0x30011223, 4, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 15, 0, 2, c.lwsp, c.swsp, 0x33001122, 8, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 16, 1, 0, c.lwsp, c.swsp, 0x23300112, 12, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 17, 1, 1, c.lwsp, c.swsp, 0x22330011, 16, tdat ); + TEST_RV32C_STSP_SRC21_BYPASS( 18, 2, 0, c.lwsp, c.swsp, 0x12233001, 20, tdat ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0xdeadbeef +tdat2: .word 0xdeadbeef +tdat3: .word 0xdeadbeef +tdat4: .word 0xdeadbeef +tdat5: .word 0xdeadbeef +tdat6: .word 0xdeadbeef +tdat7: .word 0xdeadbeef +tdat8: .word 0xdeadbeef +tdat9: .word 0xdeadbeef +tdat10: .word 0xdeadbeef + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32C/c_xor.S b/verification/self-tests/RV32C/c_xor.S new file mode 100644 index 000000000..7a6235ab5 --- /dev/null +++ b/verification/self-tests/RV32C/c_xor.S @@ -0,0 +1,62 @@ +# See LICENSE for license details. + +#***************************************************************************** +# c_xor.S +#----------------------------------------------------------------------------- +# +# Test c_xor instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RV32C_R_OP( 2, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 3, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_R_OP( 4, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_R_OP( 5, c.xor, 0x00ff00ff, 0xf00ff00f, 0xf0f0f0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RV32C_RR_SRC12_EQ_DEST( 6, c.xor, 0x00000000, 0xff00ff00 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RV32C_RR_DEST_BYPASS( 7, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_DEST_BYPASS( 8, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_DEST_BYPASS( 9, 2, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC12_BYPASS( 10, 0, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 11, 0, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 12, 0, 2, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 13, 1, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC12_BYPASS( 14, 1, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC12_BYPASS( 15, 2, 0, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RV32C_RR_SRC21_BYPASS( 16, 0, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 17, 0, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 18, 0, 2, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 19, 1, 0, c.xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RV32C_RR_SRC21_BYPASS( 20, 1, 1, c.xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RV32C_RR_SRC21_BYPASS( 21, 2, 0, c.xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/.link.ld.swp b/verification/self-tests/RV32I/.link.ld.swp deleted file mode 100644 index 0b2547e8a..000000000 Binary files a/verification/self-tests/RV32I/.link.ld.swp and /dev/null differ diff --git a/verification/self-tests/RV32I/misa.S b/verification/self-tests/RV32I/misa.S new file mode 100644 index 000000000..b166e3310 --- /dev/null +++ b/verification/self-tests/RV32I/misa.S @@ -0,0 +1,37 @@ +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + # Record original MISA value + csrr x1, misa + + # Make a bad value for MISA + ori x2, x0, -1 + + # Attempt to write, read back + csrw misa, x2 + csrr x2, misa + + bne x1, x2, failure + + # Attempt to write a good value to MISA - Enable "A" extension + csrr x2, misa + ori x2, x2, 0x1 + csrw misa, x2 + csrr x1, misa + + bne x1, x2, failure + + RVTEST_PASS + +failure: + RVTEST_FAIL + +RVTEST_CODE_END + +.data +RVTEST_DATA_BEGIN + TEST_DATA +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_ctrlflow_nottaken.S b/verification/self-tests/RV32I/sparce_ctrlflow_nottaken.S new file mode 100644 index 000000000..8b18a9c31 --- /dev/null +++ b/verification/self-tests/RV32I/sparce_ctrlflow_nottaken.S @@ -0,0 +1,67 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_ctrlflow_nottaken.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction AT the SASA preceding PC will be +# executed but does not affect the skipping conditions. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop + +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + bne x0, x0, sasa_test_case_end_fail + j skip_point_1 + j sasa_test_case_end_fail + +skip_point_1: # SASA_AND: 0,0 -> skip 2 instructions + beq x5, x0, sasa_test_case_end_fail + j skip_point_2 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_2: # SASA_OR: 0,0 -> skip 2 instructions + blt x5, x0, sasa_test_case_end_fail + j sasa_test_case_end + j sasa_test_case_end_fail + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_ctrlflow_taken.S b/verification/self-tests/RV32I/sparce_ctrlflow_taken.S new file mode 100644 index 000000000..401913b9a --- /dev/null +++ b/verification/self-tests/RV32I/sparce_ctrlflow_taken.S @@ -0,0 +1,88 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_ctrlflow_taken.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction AT the SASA preceding PC will be +# executed but does not affect the skipping conditions. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro +# x8 = temp reg + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop +la x5, skip_point_2 # load return addr + +ori x8, x0, 7 +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + j skip_point_1 # jump and skip have same destination; jump has priority + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_1: # SASA_AND: 0,0 -> skip 2 instructions + jalr x5 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_2: # SASA_OR: 0,0 -> skip 2 instructions: + beq x0, x0, skip_point_3 # branch and skip have same destination + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_3: # SASA_OR: 0,0 -> skip 2 instructions: + bne x0, x8, skip_point_4 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_4: # SASA_OR: 0,0 -> skip 2 instructions: + blt x0, x8, sasa_test_case_end # branch and skip have same destination + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_3, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_4, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_inflight_execute.S b/verification/self-tests/RV32I/sparce_inflight_execute.S new file mode 100644 index 000000000..60ceeb125 --- /dev/null +++ b/verification/self-tests/RV32I/sparce_inflight_execute.S @@ -0,0 +1,83 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_inflight_execute.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction BEFORE the SASA preceding PC will be +# executed and forwarded from the execute stage (e.i. it will affect the +# skipping condition). +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 1 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop + +ori x3, x0, 0 # this line will execute and affect the skip conditions +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + nop + j sasa_test_case_end_fail + j sasa_test_case_end_fail + bne x0, x3, sasa_test_case_end_fail + +ori x4, x0, 1 # this line will execute and affect the skip conditions +skip_point_1: # SASA_AND: 0,1 -> NO skipping + bne x0, x4, checkpoint_0 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +checkpoint_0: + ori x3, x0, 1 # this line will execute and affect the skip conditions +skip_point_2: # SASA_OR: 1,1 -> NO skipping + bne x0, x4, checkpoint_1 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +checkpoint_1: + ori x3, x0, 0 # this line will execute and affect the skip conditions +skip_point_3: # SASA_OR: 0,1 -> skip 2 instructions + nop + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end + + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_3, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_inflight_fetch.S b/verification/self-tests/RV32I/sparce_inflight_fetch.S new file mode 100644 index 000000000..56f51d67c --- /dev/null +++ b/verification/self-tests/RV32I/sparce_inflight_fetch.S @@ -0,0 +1,85 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_inflight_fetch.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction AT the SASA preceding PC will be +# executed but does not affect the skipping conditions. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop + +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + ori x3, x0, 1 # this line will execute but has no effect on the skip conditions + # even though x3 is a condition register + j sasa_test_case_end_fail + j sasa_test_case_end_fail + beq x0, x3, sasa_test_case_end_fail + +skip_point_1: # SASA_AND: 1,0 -> NO skipping + ori x3, x0, 0 # this line will execute but has no effect on the skip conditions + # even though x3 is a condition register + beq x0, x3, skip_point_2 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_2: # SASA_OR: 0,0 -> skip 2 instructions + ori x3, x0, 1 # this line will execute but has no effect on the skip conditions + # even though x3 is a condition register + j sasa_test_case_end_fail + j sasa_test_case_end_fail + ori x4, x0, 1 + beq x0, x3, sasa_test_case_end_fail + +skip_point_3: # SASA_OR: 1,1 -> NO skipping + ori x3, x0, 0 # this line will execute but has no effect on the skip conditions + # even though x3 is a condition register + beq x0, x3, sasa_test_case_end + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_3, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_sasa_disable.S b/verification/self-tests/RV32I/sparce_sasa_disable.S new file mode 100644 index 000000000..1c5597674 --- /dev/null +++ b/verification/self-tests/RV32I/sparce_sasa_disable.S @@ -0,0 +1,88 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_sasa_disable.S +#----------------------------------------------------------------------------- +# +# This test ensures that the instruction AT the SASA preceding PC will be +# executed but does not affect the skipping conditions. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro +# x8 = temp regs to store SASA_CONFIG_ADDR + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +nop +nop +nop + +skip_point_0: # SASA_AND: 0,0 -> skip 2 instructions + nop + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +li x8, SPARCE_CONFIG_ADDR +sw t0, 0(x8) # disable SASA table + +# try storing to SASA table after disabled +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) + +skip_point_1: # SASA_AND: 0,0 -> skip 2 instructions but DISABLED + nop + j skip_point_2 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_2: # SASA_OR: 0,0 -> skip 2 instructions but DISABLED + nop + sw x0, 0(x8) # enable SASA table + j skip_point_3 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_3: # SASA_OR: 0,0 -> skip 2 instructions + nop + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_2, 3, 4, SASA_COND_OR, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_3, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_single_skip.S b/verification/self-tests/RV32I/sparce_single_skip.S new file mode 100644 index 000000000..193718ba8 --- /dev/null +++ b/verification/self-tests/RV32I/sparce_single_skip.S @@ -0,0 +1,50 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_single_skip.S +#----------------------------------------------------------------------------- +# +# This is the most basic test for the SparCE optimizations. An entry is loaded +# into the SASA table, and the conditions for skipping are met. The test fails +# when any of the instructions to be skipped are executed, and passes otherwise. +# +# Register 5 is a word offset value from address label 'sasa_entry_0'. +# This is a parameter required by the STORE_SASA_FROM_MEM. +# This parameter should skip the first four SASA data constructed +# by CREATE_SASA_MEMORY_ENTRIES at the bottom. + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori x5, x0, 5 # offset +STORE_SASA_FROM_MEM(sasa_entry_0, x5, x1, x2) +nop +nop +skip_point: + nop + li TESTNUM, 0 + li TESTNUM, 0 + li TESTNUM, 0 + li TESTNUM, 0 + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) # ignored +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 4) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_skip_range.S b/verification/self-tests/RV32I/sparce_skip_range.S new file mode 100644 index 000000000..2b6240efb --- /dev/null +++ b/verification/self-tests/RV32I/sparce_skip_range.S @@ -0,0 +1,138 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_skip_range.S +#----------------------------------------------------------------------------- +# +# This test ensures that each skip range works for the sparce module. +# The block of CREATE_SASA_MEMORY_ENTRIES at the bottom first constructs +# the SASA data. Then for every loop the STORE_SASA_FROM_MEM macro +# stores one entry to the SASA table. +# +# When the PC reaches 'skip_point', it skips x instructions based on the +# SASA entry. Then JAL to 'check_skip_range' subroutine to check if it +# is skipping from the expected address by reading the ra register. +# +# Register usage: +# x1 (ra) = return address +# x3, x4 = input regs to SASA entries +# x5 (t0) = loop counter/jump range (0 to 31) +# x6, x7 (t1, t2) = temp regs passed to macro +# x8 = addr of the start of skip region +# x9 = number of test cases (start from 0) +# x10 = temp + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori x3, x0, 0 +ori x4, x0, 0 +ori t0, x0, 0 # loop counter (0 to 31) +ori x9, x0, 31 # number of test cases (start from 0) +la x8, skip_point # loads addr of skip_point + +loop_start: +blt x9, t0, sasa_test_case_end # end test if num of test cases < loop_counter + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) # creates new SASA entry for the same PC + # but +1 skip range for every loop +nop +nop +skip_point: + nop + jal check_skip_range # skip 0 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 5 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 10 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 15 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 20 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 25 instructions + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range + jal check_skip_range # skip 31 instructions + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +check_skip_range: + slli x10, t0, 2 # x10 = (loop_counter * 4) + addi ra, ra, -8 # ra - 8 - x10 + sub ra, ra, x10 + addi t0, t0, 1 # increment loop counter + beq ra, x8, loop_start # expects ra - 4 - (loop_counter * 4) = skip_point + + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 0) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 1) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 3) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 4) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 5) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 6) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 7) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 8) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 9) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 10) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 11) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 12) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 13) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 14) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 15) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 16) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 17) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 18) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 19) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 20) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 21) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 22) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 23) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 24) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 25) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 26) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 27) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 28) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 29) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 30) +CREATE_SASA_MEMORY_ENTRIES(skip_point, 3, 4, SASA_COND_AND, 31) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/sparce_sprf_uninitialized.S b/verification/self-tests/RV32I/sparce_sprf_uninitialized.S new file mode 100644 index 000000000..11ea9eedf --- /dev/null +++ b/verification/self-tests/RV32I/sparce_sprf_uninitialized.S @@ -0,0 +1,57 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sparce_sprf_uninitialized.S +#----------------------------------------------------------------------------- +# +# This test ensures that skipping does not occur when registers have not been +# initialized after reset. +# +# Register usage: +# x3, x4 = condition regs to SASA entries +# x5 (t0) = word address offset to access the SASA data +# x6, x7 (t1, t2) = temp regs passed to macro + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +li TESTNUM, 1 +ori t0, x0, 0 + +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +addi t0, t0, 1 +STORE_SASA_FROM_MEM(sasa_entry_0, t0, t1, t2) +nop +nop + +skip_point_0: # SASA_AND: X,X -> NO skipping + nop + j skip_point_1 + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +skip_point_1: # SASA_OR: X,X -> NO skipping + nop + j sasa_test_case_end + j sasa_test_case_end_fail + j sasa_test_case_end_fail + +sasa_test_case_end_fail: +li TESTNUM, 0 + +sasa_test_case_end: + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN +sasa_entry_0: +CREATE_SASA_MEMORY_ENTRIES(skip_point_0, 3, 4, SASA_COND_AND, 2) +CREATE_SASA_MEMORY_ENTRIES(skip_point_1, 3, 4, SASA_COND_OR, 2) + +TEST_DATA + +RVTEST_DATA_END diff --git a/verification/self-tests/RV32I/timer.S b/verification/self-tests/RV32I/timer.S deleted file mode 100644 index 5c7caf9a7..000000000 --- a/verification/self-tests/RV32I/timer.S +++ /dev/null @@ -1,72 +0,0 @@ -/* -* Copyright 2016 Purdue University -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* -* Filename: basic_branch.S -* -* Created by: Jacob R. Stevens -* Email: steven69@purdue.edu -* Date Created: 07/01/2016 -* Description: Test of basic branching behavior -*/ - -#include "riscv_test.h" -#include "test_macros.h" - - -RVTEST_RV32U - - -RVTEST_CODE_BEGIN - - -main: - # Define trap vector address - la t0, mtvec_handler - csrw mtvec, t0 - - addi x6, x0, 0xFF - addi x7, x0, 0xFF - addi x3, x0, 0x1 - csrw mstatus, x3 - la x1, mtime - lw x2, 0(x1) - addi x2, x2, 50 - la x1, mtimecmp - sw x2, 0(x1) - ori x3, x0, 0x80 - csrw mie, x3 -idle_loop: - beq x6, x7, idle_loop - RVTEST_PASS - -.align 2 -mtvec_handler: - # Write to mtimecmp using the zero register - # Check that nothing happened to interrupt - # Actually write to mtimecmp - # Check that the interrupt cleared - # Change sigil to show interrupt was entered - ori x6, x0, 0x0F - # Return - mret - -RVTEST_CODE_END - -.data -RVTEST_DATA_BEGIN - TEST_DATA -RVTEST_DATA_END - diff --git a/verification/self-tests/RV32I/timer2.S b/verification/self-tests/RV32I/timer2.S deleted file mode 100644 index d3d694a81..000000000 --- a/verification/self-tests/RV32I/timer2.S +++ /dev/null @@ -1,97 +0,0 @@ -/* -* Copyright 2016 Purdue University -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* -* Filename: timer2.S -* -* Created by: John Skubic -* Email: jskubic69@purdue.edu -* Date Created: 09/27/2016 -* Description: Test of returning from interrupts, make sure every insn is -* executed and none are executed twice -*/ - -#include "riscv_test.h" -#include "test_macros.h" - - -RVTEST_RV32U - - -RVTEST_CODE_BEGIN - - -main: - # Define trap vector address - la t0, mtvec_handler - csrw mtvec, t0 - - addi x6, x0, 0xFF - addi x7, x0, 0xFF - addi x3, x0, 0x1 - csrw mstatus, x3 - la x1, mtime - lw x2, 0(x1) - addi x2, x2, 50 - la x1, mtimecmp - sw x2, 0(x1) - ori x3, x0, 0x80 - csrw mie, x3 - #unique add insns that timer should interrupt - addi x24, x24, 1 - addi x24, x24, 2 - addi x24, x24, 3 - addi x24, x24, 4 - addi x24, x24, 5 - addi x24, x24, 6 - addi x24, x24, 7 - addi x24, x24, 8 - addi x24, x24, 9 - addi x24, x24, 10 - addi x24, x24, 11 - addi x24, x24, 12 - addi x24, x24, 13 - addi x24, x24, 14 - addi x24, x24, 15 - addi x24, x24, 16 - addi x24, x24, 17 - addi x24, x24, 18 - addi x24, x24, 19 - addi x24, x24, 20 - addi x9, x0, 210 - beq x9, x24, test_pass - addi x28, x0, 1 - RVTEST_FAIL -test_pass: - RVTEST_PASS - -.align 2 -mtvec_handler: - # Write to mtimecmp using the zero register - # Check that nothing happened to interrupt - # Actually write to mtimecmp - # Check that the interrupt cleared - # Change sigil to show interrupt was entered - ori x6, x0, 0x0F - # Return - mret - -RVTEST_CODE_END - -.data -RVTEST_DATA_BEGIN - TEST_DATA -RVTEST_DATA_END - diff --git a/verification/u-mode/build_all.py b/verification/u-mode/build_all.py new file mode 100755 index 000000000..47646f028 --- /dev/null +++ b/verification/u-mode/build_all.py @@ -0,0 +1,51 @@ +#! /usr/bin/python3 + +import subprocess +import glob +import os +import pathlib + + +compile_cmd = ['riscv64-unknown-elf-gcc', '-march=rv32i_zicsr', '-mabi=ilp32', '-mcmodel=medany', + '-static', '-nostdlib', '-O2', '-Tlink.ld', 'start.S', 'utility.c'] + +cvt_cmd = ['riscv64-unknown-elf-objcopy', '-O', 'binary'] + + +if not os.path.isfile('./start.S') or not os.path.isfile('link.ld'): + print('Error: Could not find AFTx06.S or link.ld in this directory') + exit(1) + + +for fname in (glob.glob('./*.c') + glob.glob('./*.S')): + if 'start' in fname or 'utility' in fname: + print("Skipping {} as top-level file, appears to be a utility".format(fname)); + continue + print('Compiling {}'.format(fname)) + basename = pathlib.Path(fname).stem + + rv = subprocess.run(compile_cmd + [fname, '-o', basename + '.elf'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + print('Converting {} to binary'.format(fname)) + rv = subprocess.run(cvt_cmd + [basename + '.elf', basename + '.bin']) + if rv.returncode != 0: + print('Exited with error {}, printing command, stdout, stderr!'.format(rv.returncode)) + print('Command: {}\n\n'.format(compile_cmd + [fname, '-o', basename + '.elf'])) + print('stdout:\n\n{}'.format(rv.stdout)) + print('stderr:\n\n{}'.format(rv.stderr)) + print('Exiting...') + exit(1) + + +print( +''' + Finished compilation. Now, pass the '.bin' file corresponding to + the example to run as an argument to 'VbASIC_wrapper' to run an example! +''') diff --git a/verification/u-mode/csr.c b/verification/u-mode/csr.c new file mode 100644 index 000000000..fedb1e3f0 --- /dev/null +++ b/verification/u-mode/csr.c @@ -0,0 +1,97 @@ +#include +#include "utility.h" + +extern volatile int flag; +extern volatile int done; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc, mtval; + mcause_t mcause; + asm volatile("csrr %0, mepc" : "=r"(mepc)); + asm volatile("csrr %0, mtval" : "=r"(mtval)); + asm volatile("csrr %0, mcause" : "=r"((mcause_t) mcause)); + + print("mepc: "); + put_uint32_hex(mepc); + print("\n"); + + print("mtval: "); + put_uint32_hex(mtval); + print("\n"); + + print("mcause: "); + put_uint32_hex(mcause.interrupt & 0x1); + print(" "); + put_uint32_hex(mcause.ex_code); + print("\n"); + + print("-----\n"); + + if (~mcause.interrupt && mcause.ex_code == 8){ + uint32_t mstatus = 0x1800; // set mpp back to M_MODE + asm volatile("csrs mstatus, %0" : : "r"(mstatus)); + mepc = (uint32_t) &done; + } + else + { + mepc += 4; + flag -= 1; + } + asm volatile("csrw mepc, %0" : : "r"(mepc)); +} + +void __attribute__((noreturn)) user_main(void) { + uint32_t csr_val; + + asm volatile("csrr %0, cycle" : "=r"(csr_val)); + + asm volatile("csrr %0, time" : "=r"(csr_val)); + + asm volatile("ecall"); // ends user_main + + __builtin_unreachable(); + +} + +int main(void) { + // Setup exceptions + uint32_t mtvec_value = (uint32_t) handler; + uint32_t mstatus_value = 0x08; + asm volatile("csrs mstatus, %0" : : "r"(mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r"(mtvec_value)); + + // Setup PMP + uint32_t pmp_addr = ((uint32_t) (&flag)) >> 2; // Protect flag + asm volatile("csrw pmpaddr0, %0" : : "r"(pmp_addr)); + pmp_addr = 0x20001FFF; // Allows for the entire text, bss, stack section + asm volatile("csrw pmpaddr1, %0" : : "r"(pmp_addr)); + uint32_t pmp_cfg = 0x00001F11; // [NAPOT, RWX, no L] [NA4, R, no L] + asm volatile("csrw pmpcfg0, %0" : : "r"(pmp_cfg)); + + flag = 3; + + // Test registers in M_MODE + uint32_t csr_val_0, csr_val_1; + csr_val_0 = 0x0; + asm volatile("csrw mcycle, %0" : : "r"(csr_val_0)); + asm volatile("csrr %0, mcycle" : "=r"(csr_val_0)); + asm volatile("nop; nop; nop;"); + asm volatile("csrr %0, cycle" : "=r"(csr_val_1)); + + // TODO: This test is highly dependent on I-fetch speed + if (csr_val_1 - csr_val_0 < 30) // Are the two cycle counts close? + { + flag -= 1; + } + + asm volatile("csrw cycle, %0" : : "r"(csr_val_0)); // Trying to write to a R/O register + + + // Jump to user program + uint32_t mepc_value = (uint32_t) user_main; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + asm volatile("mret"); + + __builtin_unreachable(); + +} diff --git a/verification/u-mode/link.ld b/verification/u-mode/link.ld new file mode 100755 index 000000000..4fd20ea69 --- /dev/null +++ b/verification/u-mode/link.ld @@ -0,0 +1,14 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x0000000080000000; + .text.init : { *(.text.init) } + .text : { *(.text) } + .data ALIGN(0x1000) : { *(.data) } + .bss : { *(.bss) } + . = 0x0000000080007000; + .statuses : { *(.statuses) } + _end = .; +} diff --git a/verification/u-mode/start.S b/verification/u-mode/start.S new file mode 100644 index 000000000..9e588dd07 --- /dev/null +++ b/verification/u-mode/start.S @@ -0,0 +1,17 @@ + +.global _start +_start: + li sp, 0x8000F000 + jal main + +.global done +done: + la x28, flag + lw x28, 0(x28) +__inf_loop: + j __inf_loop + + +.global flag +.data +flag: .word 0xFF diff --git a/verification/u-mode/u-mode.c b/verification/u-mode/u-mode.c new file mode 100644 index 000000000..f4f0d53dc --- /dev/null +++ b/verification/u-mode/u-mode.c @@ -0,0 +1,86 @@ +#include +#include "utility.h" + +extern volatile int flag; +extern volatile int done; + +void __attribute__((interrupt)) __attribute__((aligned(4))) handler() { + uint32_t mepc, mtval; + mcause_t mcause; + asm volatile("csrr %0, mepc" : "=r"(mepc)); + asm volatile("csrr %0, mtval" : "=r"(mtval)); + asm volatile("csrr %0, mcause" : "=r"((mcause_t) mcause)); + + print("mepc: "); + put_uint32_hex(mepc); + print("\n"); + + print("mtval: "); + put_uint32_hex(mtval); + print("\n"); + + print("mcause: "); + put_uint32_hex(mcause.interrupt & 0x1); + print(" "); + put_uint32_hex(mcause.ex_code); + print("\n"); + + print("-----\n"); + + if (~mcause.interrupt && mcause.ex_code == 8){ + uint32_t mstatus = 0x1800; // set mpp back to M_MODE + asm volatile("csrs mstatus, %0" : : "r"(mstatus)); + mepc = (uint32_t) &done; + } + else + { + mepc += 4; + flag -= 1; + } + asm volatile("csrw mepc, %0" : : "r"(mepc)); +} + + +void __attribute__((noreturn)) user_main(void) { + print("A"); // MMIO region is not allowed in PMP, should fail + + flag = 0; // Flag is protected, should fail + + asm volatile("mret"); // privileged instruction + + uint32_t temp; + asm volatile("csrr %0, mstatus" : "=r"(temp)); // Machine mode CSR + + asm volatile("wfi"); // No timeout wait enabled + + asm volatile("ecall"); // ends user_main + + __builtin_unreachable(); + +} + +int main(void) { + // Setup exceptions + uint32_t mtvec_value = (uint32_t) handler; + uint32_t mstatus_value = 0x08; + asm volatile("csrs mstatus, %0" : : "r"(mstatus_value)); + asm volatile("csrw mtvec, %0" : : "r"(mtvec_value)); + + // Setup PMP + uint32_t pmp_addr = ((uint32_t) (&flag)) >> 2; // Protect flag + asm volatile("csrw pmpaddr0, %0" : : "r"(pmp_addr)); + pmp_addr = 0x20001FFF; // Allows for the entire text, bss, stack section + asm volatile("csrw pmpaddr1, %0" : : "r"(pmp_addr)); + uint32_t pmp_cfg = 0x00001F11; // [NAPOT, RWX, no L] [NA4, R, no L] + asm volatile("csrw pmpcfg0, %0" : : "r"(pmp_cfg)); + + + // Jump to user program + flag = 6; + uint32_t mepc_value = (uint32_t) user_main; + asm volatile("csrw mepc, %0" : : "r"(mepc_value)); + asm volatile("mret"); + + __builtin_unreachable(); + +} diff --git a/verification/u-mode/utility.c b/verification/u-mode/utility.c new file mode 100644 index 000000000..3a5488725 --- /dev/null +++ b/verification/u-mode/utility.c @@ -0,0 +1,25 @@ + +#include "utility.h" + +void print(char *string) { + volatile char *magic = (volatile char *)MAGIC_ADDR; + + for(int i = 0; string[i]; i++) { + (*magic) = string[i]; + } +} + +void put_uint32_hex(uint32_t x) { + char buf[10] = {0}; + + for(int i = 0; i < 8; i++) { + uint8_t value = (x & 0xF); + if(value >= 10) { + buf[7-i] = ((value-10) + 'A'); + } else { + buf[7-i] = (value + '0'); + } + x >>= 4; + } + print(buf); +} diff --git a/verification/u-mode/utility.h b/verification/u-mode/utility.h new file mode 100644 index 000000000..508607bdd --- /dev/null +++ b/verification/u-mode/utility.h @@ -0,0 +1,24 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + + #include + + #define MTIME_ADDR 0xFFFFFFE0 + #define MTIMEH_ADDR 0xFFFFFFE4 + #define MTIMECMP_ADDR 0xFFFFFFE8 + #define MTIMECMPH_ADDR 0xFFFFFFEC + #define MSIP_ADDR 0xFFFFFFF0 + #define EXT_ADDR_SET 0xFFFFFFF4 + #define EXT_ADDR_CLEAR 0xFFFFFFF8 + #define MAGIC_ADDR 0xFFFFFFFC + + + void print(char *string); + void put_uint32_hex(uint32_t hex); + + typedef struct { + int ex_code : 31; + int interrupt : 1; + } mcause_t; + +#endif diff --git a/verification/uvm/caches/bfm/memory_bfm.svh b/verification/uvm/caches/bfm/memory_bfm.svh new file mode 100644 index 000000000..ede69779a --- /dev/null +++ b/verification/uvm/caches/bfm/memory_bfm.svh @@ -0,0 +1,174 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: memory_bfm.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Bus Functional Model (BFM) for the memory bus, including memory mapped I/O +*/ + +`ifndef MEMORY_BFM_SVH +`define MEMORY_BFM_SVH + +`include "cache_env_config.svh" + +`include "uvm_macros.svh" + +`include "dut_params.svh" + +`include "Utils.svh" + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +class memory_bfm extends uvm_component; + `uvm_component_utils(memory_bfm) + + virtual cache_if cif; + virtual generic_bus_if bus_if; + + cache_env_config env_config; + + word_t mem[word_t]; // initialized memory array + word_t mmio[word_t]; // initialized memory mapped array + + function new(string name = "memory_bfm", uvm_component parent); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + string bus_if_str; + super.build_phase(phase); + + // get config from database + if (!uvm_config_db#(cache_env_config)::get(this, "", "env_config", env_config)) begin + `uvm_fatal(this.get_name(), "env config not registered to db") + end + + // get interface from database + if (!uvm_config_db#(virtual cache_if)::get(this, "", "d_cif", cif)) begin + `uvm_fatal($sformatf("%s/d_cif", this.get_name()), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), "pulled from db", UVM_FULL) + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", "mem_bus_if", bus_if)) begin + `uvm_fatal($sformatf("%s/mem_bus_if", this.get_name()), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), "pulled from db", UVM_FULL) + endfunction : build_phase + + virtual task run_phase(uvm_phase phase); + forever begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + + // default values on bus + bus_if.busy = '0; + bus_if.rdata = 32'hbad0_bad0; + + if (bus_if.addr < `NONCACHE_START_ADDR) begin + if (bus_if.ren) begin + mem_read(); + end else if (bus_if.wen) begin + mem_write(); + end + end else if (bus_if.addr >= `NONCACHE_START_ADDR) begin + if (bus_if.ren) begin + mmio_read(); + end else if (bus_if.wen) begin + mmio_write(); + end + end + end + endtask : run_phase + + task mem_read(); + int count; + + bus_if.busy = '1; + count = 1; + while (count < env_config.mem_latency && bus_if.ren) begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + count++; + end + + bus_if.rdata = read(bus_if.addr); + bus_if.busy = '0; + endtask : mem_read + + task mem_write(); + int count; + + bus_if.busy = '1; + count = 1; + while (count < env_config.mem_latency) begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + count++; + end + + mem[bus_if.addr] = bus_if.wdata; + + bus_if.busy = '0; + endtask : mem_write + + task mmio_read(); + int count; + bus_if.busy = '1; + //TODO: COME UP WITH SOME MEANINGFUL DATA TO PUT FOR MMIO, MAYBE SIMULATE A PERIFERAL REGISTER + + count = 1; + while (count < env_config.mmio_latency && bus_if.ren) begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + count++; + end + + bus_if.rdata = {env_config.mmio_tag, bus_if.addr[15:0]}; + bus_if.busy = '0; + endtask : mmio_read + + task mmio_write(); + int count; + bus_if.busy = '1; + + count = 1; + while (count < env_config.mmio_latency && bus_if.wen) begin + @(posedge cif.CLK); + `PROPAGATION_DELAY + count++; + end + + // mmio[bus_if.addr] = bus_if.wdata; //TODO: DO SOMETHING MORE MEANINGFUL FOR WRITING TO MMIO, REGISTER MODEL? + bus_if.busy = '0; + endtask : mmio_write + + function word_t read(word_t addr); + if (mem.exists(bus_if.addr)) begin + return mem[bus_if.addr]; + end else begin + return {env_config.mem_tag, addr[15:0]}; // return non-initialized data + end + endfunction : read + +endclass : memory_bfm + +`endif diff --git a/verification/uvm/caches/bus_agents/l2_agent.svh b/verification/uvm/caches/bus_agents/l2_agent.svh new file mode 100644 index 000000000..f2d04f150 --- /dev/null +++ b/verification/uvm/caches/bus_agents/l2_agent.svh @@ -0,0 +1,52 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: l2_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: UVM Agent to monitor the processor/l1 side of l2 cache +*/ + +`ifndef L2_AGENT_SHV +`define L2_AGENT_SHV + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "bus_monitor.svh" + +// typedef bus_monitor#(0, "l2_cif", "arb_l2_bus_if") l2_monitor; + +class l2_agent extends bus_agent; + `uvm_component_utils(l2_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + mon = bus_monitor::type_id::create("L2_MON", this); + mon.set_precedence(0); + mon.set_cif_str("l2_cif"); + mon.set_bus_if_str("arb_l2_bus_if"); + + `uvm_info(this.get_name(), $sformatf("Created <%s>", mon.get_name()), UVM_FULL) + endfunction + +endclass : l2_agent + +`endif diff --git a/verification/uvm/caches/bus_agents/mem_agent.svh b/verification/uvm/caches/bus_agents/mem_agent.svh new file mode 100644 index 000000000..7eaae21e9 --- /dev/null +++ b/verification/uvm/caches/bus_agents/mem_agent.svh @@ -0,0 +1,51 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mem_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Agent to stand in for the memory side of the caches +*/ + +`ifndef MEM_AGENT_SHV +`define MEM_AGENT_SHV + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "bus_monitor.svh" + +// typedef bus_monitor#(0, "l2_cif", "mem_bus_if") mem_monitor; + +class mem_agent extends bus_agent; + `uvm_component_utils(mem_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + mon = bus_monitor::type_id::create("MEM_MON", this); + mon.set_precedence(0); + mon.set_cif_str("l2_cif"); + mon.set_bus_if_str("mem_bus_if"); + `uvm_info(this.get_name(), $sformatf("Created <%s>", mon.get_name()), UVM_FULL) + endfunction + +endclass : mem_agent + +`endif diff --git a/verification/uvm/caches/bus_agents/mem_arb_agent.svh b/verification/uvm/caches/bus_agents/mem_arb_agent.svh new file mode 100644 index 000000000..a2ac4e2e6 --- /dev/null +++ b/verification/uvm/caches/bus_agents/mem_arb_agent.svh @@ -0,0 +1,72 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mem_arb_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/12/2022 +* Description: UVM Agent to stand in for the memory arbiter side of the l2 cache +*/ + +`ifndef MEM_ARB_AGENT_SVH +`define MEM_ARB_AGENT_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "cpu_driver.svh" +`include "bus_monitor.svh" +`include "bus_agent.svh" +`include "cpu_sequencer.svh" + +class mem_arb_driver extends cpu_driver #("l2_cif", "arb_l2_bus_if"); + `uvm_component_utils(mem_arb_driver) + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction +endclass + +class mem_arb_agent extends bus_agent #(mem_arb_driver); + `uvm_component_utils(mem_arb_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + sqr = cpu_sequencer::type_id::create("MEM_ARB_SQR", this); + drv = mem_arb_driver::type_id::create("MEM_ARB_DRV", this); + mon = bus_monitor::type_id::create("MEM_ARB_MON", this); + mon.set_precedence(1); + mon.set_cif_str("l2_cif"); + mon.set_bus_if_str("arb_l2_bus_if"); + + `uvm_info(this.get_name(), $sformatf( + "Created <%s>, <%s>, <%s>", drv.get_name(), sqr.get_name(), mon.get_name()), UVM_FULL) + endfunction + + virtual function void connect_phase(uvm_phase phase); + drv.seq_item_port.connect(sqr.seq_item_export); + `uvm_info(this.get_name(), $sformatf("Connected <%s> to <%s>", drv.get_name(), sqr.get_name()), + UVM_FULL) + endfunction + +endclass : mem_arb_agent +`endif diff --git a/verification/uvm/caches/coverage/.gitignore b/verification/uvm/caches/coverage/.gitignore new file mode 100644 index 000000000..bd14f4040 --- /dev/null +++ b/verification/uvm/caches/coverage/.gitignore @@ -0,0 +1,4 @@ +# Note: this folder needs to exist to hold the coverage files + +# However; we ignore all its contents +*.ucdb \ No newline at end of file diff --git a/verification/uvm/caches/cpu_agent/cpu_driver.svh b/verification/uvm/caches/cpu_agent/cpu_driver.svh new file mode 100644 index 000000000..121787b2e --- /dev/null +++ b/verification/uvm/caches/cpu_agent/cpu_driver.svh @@ -0,0 +1,124 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cpu_driver.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Driver class for initiating processor side requests to caches +*/ + +`ifndef CPU_DRIVER_SVH +`define CPU_DRIVER_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "generic_bus_if.vh" +`include "cache_if.svh" + +class cpu_driver #( + string cif_str, + string bus_if_str +) extends uvm_driver #(cpu_transaction); + `uvm_component_param_utils(cpu_driver#(cif_str, bus_if_str)) + + virtual cache_if cif; + virtual generic_bus_if cpu_bus_if; + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction : new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + // get interface from database + if (!uvm_config_db#(virtual cache_if)::get(this, "", cif_str, cif)) begin + `uvm_fatal($sformatf("%s/%s", this.get_name(), cif_str), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), $sformatf("pulled <%s> from db", cif_str), UVM_FULL) + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", bus_if_str, cpu_bus_if)) begin + `uvm_fatal($sformatf("%s/%s", this.get_name(), bus_if_str), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), $sformatf("pulled <%s> from db", bus_if_str), UVM_FULL) + endfunction : build_phase + + task run_phase(uvm_phase phase); + cpu_transaction req_item; + + DUT_reset(); + + forever begin + seq_item_port.get_next_item(req_item); + `uvm_info(this.get_name(), $sformatf("Received new sequence item:\n%s", req_item.sprint()), + UVM_HIGH) + + cpu_bus_if.addr = req_item.addr; + cpu_bus_if.wdata = req_item.data; + cpu_bus_if.ren = ~req_item.rw; // read = 0 + cpu_bus_if.wen = req_item.rw; // write = 1 + + cpu_bus_if.byte_en = req_item.byte_en; + cif.flush = req_item.flush; + + //FIXME: NEED TO ADD CLEAR FUNCTIONALITY + cif.clear = '0; + + if (cif.flush) begin + do begin + @(posedge cif.CLK); //wait for flush to complete + end while (cif.flush_done == 1'b0); + end else begin + do begin + @(posedge cif.CLK); //wait for cache operation to complete + end while (cpu_bus_if.busy == 1'b1); + end + + seq_item_port.item_done(); + end + endtask : run_phase + + task DUT_reset(); + // reset all cpu bus signals + cpu_bus_if.addr = '0; + cpu_bus_if.wdata = '0; + cpu_bus_if.ren = '0; + cpu_bus_if.wen = '0; + cpu_bus_if.byte_en = '0; + + // reset all cpu cif request signals + cif.nRST = 0; + cif.clear = 0; + cif.flush = 0; + + @(posedge cif.CLK); + cif.nRST = 1; + @(posedge cif.CLK); + cif.nRST = 0; + @(posedge cif.CLK); + cif.nRST = 1; + @(posedge cif.CLK); + endtask + +endclass : cpu_driver + +`endif diff --git a/verification/uvm/caches/cpu_agent/cpu_sequencer.svh b/verification/uvm/caches/cpu_agent/cpu_sequencer.svh new file mode 100644 index 000000000..0acca680a --- /dev/null +++ b/verification/uvm/caches/cpu_agent/cpu_sequencer.svh @@ -0,0 +1,32 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cpu_sequencer.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: UVM Sequencer class for initiating processor side requests to caches +*/ + +`ifndef CPU_SEQUENCER_SVH +`define CPU_SEQUENCER_SVH + +`include "cpu_transaction.svh" + +typedef uvm_sequencer#(cpu_transaction) cpu_sequencer; + +`endif diff --git a/verification/uvm/caches/cpu_agent/cpu_transaction.svh b/verification/uvm/caches/cpu_agent/cpu_transaction.svh new file mode 100644 index 000000000..966b5f642 --- /dev/null +++ b/verification/uvm/caches/cpu_agent/cpu_transaction.svh @@ -0,0 +1,69 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cpu_transaction.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Abstracted memory request transaction +*/ + +`ifndef CPU_TRANSACTION_SVH +`define CPU_TRANSACTION_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; +`include "uvm_macros.svh" +`include "cache_env.svh" +`include "dut_params.svh" + +class cpu_transaction extends uvm_sequence_item; + + rand bit rw; // 0 -> read; 1 -> write + rand word_t addr; + rand word_t data; + + rand logic [3:0] byte_en; + + rand bit flush; + //TODO: ADD CLEAR + + `uvm_object_utils_begin(cpu_transaction) + `uvm_field_int(rw, UVM_ALL_ON) + `uvm_field_int(addr, UVM_ALL_ON) + `uvm_field_int(data, UVM_ALL_ON) + `uvm_field_int(byte_en, UVM_ALL_ON) + `uvm_field_int(flush, UVM_ALL_ON) + `uvm_object_utils_end + + constraint valid_addr { + addr >= '0; + soft addr < `NONCACHE_START_ADDR; + addr[1:0] == '0; + } + + constraint usable_byte_en { + byte_en inside {4'b0001, 4'b0010, 4'b0100, 4'b1000, 4'b0011, 4'b1100, 4'b1111}; + } + + function new(string name = "cpu_transaction"); + super.new(name); + endfunction : new + +endclass : cpu_transaction + +`endif diff --git a/verification/uvm/caches/cpu_agent/d_cpu_agent.svh b/verification/uvm/caches/cpu_agent/d_cpu_agent.svh new file mode 100644 index 000000000..65af0ca92 --- /dev/null +++ b/verification/uvm/caches/cpu_agent/d_cpu_agent.svh @@ -0,0 +1,73 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: d_cpu_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: UVM Agent to stand in for the processor side of the data cache +*/ + +`ifndef D_CPU_AGENT_SVH +`define D_CPU_AGENT_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "cpu_driver.svh" +`include "bus_monitor.svh" +`include "bus_agent.svh" +`include "cpu_sequencer.svh" + +class d_cpu_driver extends cpu_driver #("d_cif", "d_cpu_bus_if"); + `uvm_component_utils(d_cpu_driver) + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction +endclass + +class d_cpu_agent extends bus_agent #(d_cpu_driver); + `uvm_component_utils(d_cpu_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + sqr = cpu_sequencer::type_id::create("D_CPU_SQR", this); + drv = d_cpu_driver::type_id::create("D_CPU_DRV", this); + mon = bus_monitor::type_id::create("D_CPU_MON", this); + mon.set_precedence(1); + mon.set_cif_str("d_cif"); + mon.set_bus_if_str("d_cpu_bus_if"); + + `uvm_info(this.get_name(), $sformatf( + "Created <%s>, <%s>, <%s>", drv.get_name(), sqr.get_name(), mon.get_name()), UVM_FULL) + endfunction + + virtual function void connect_phase(uvm_phase phase); + drv.seq_item_port.connect(sqr.seq_item_export); + `uvm_info(this.get_name(), $sformatf("Connected <%s> to <%s>", drv.get_name(), sqr.get_name()), + UVM_FULL) + endfunction + +endclass : d_cpu_agent + +`endif diff --git a/verification/uvm/caches/cpu_agent/i_cpu_agent.svh b/verification/uvm/caches/cpu_agent/i_cpu_agent.svh new file mode 100644 index 000000000..0a73e5f50 --- /dev/null +++ b/verification/uvm/caches/cpu_agent/i_cpu_agent.svh @@ -0,0 +1,73 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: i_cpu_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: UVM Agent to stand in for the processor side of the instruction cache +*/ + +`ifndef I_CPU_AGENT_SVH +`define I_CPU_AGENT_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "cpu_driver.svh" +`include "bus_monitor.svh" +`include "bus_agent.svh" +`include "cpu_sequencer.svh" + +class i_cpu_driver extends cpu_driver #("i_cif", "i_cpu_bus_if"); + `uvm_component_utils(i_cpu_driver) + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction +endclass + +class i_cpu_agent extends bus_agent #(i_cpu_driver); + `uvm_component_utils(i_cpu_agent) + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + + virtual function void build_phase(uvm_phase phase); + sqr = cpu_sequencer::type_id::create("I_CPU_SQR", this); + drv = i_cpu_driver::type_id::create("I_CPU_DRV", this); + mon = bus_monitor::type_id::create("I_CPU_MON", this); + mon.set_precedence(1); + mon.set_cif_str("i_cif"); + mon.set_bus_if_str("i_cpu_bus_if"); + + `uvm_info(this.get_name(), $sformatf( + "Created <%s>, <%s>, <%s>", drv.get_name(), sqr.get_name(), mon.get_name()), UVM_FULL) + endfunction + + virtual function void connect_phase(uvm_phase phase); + drv.seq_item_port.connect(sqr.seq_item_export); + `uvm_info(this.get_name(), $sformatf("Connected <%s> to <%s>", drv.get_name(), sqr.get_name()), + UVM_FULL) + endfunction + +endclass : i_cpu_agent + +`endif diff --git a/verification/uvm/caches/end2end/end2end.svh b/verification/uvm/caches/end2end/end2end.svh new file mode 100644 index 000000000..891a45bd6 --- /dev/null +++ b/verification/uvm/caches/end2end/end2end.svh @@ -0,0 +1,233 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: end2end.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Component for ensuring proper translation between processor and memory side of the caches +*/ + +`ifndef END2END_SVH +`define END2END_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "dut_params.svh" +`include "cpu_transaction.svh" +`include "cache_model.svh" +`include "Utils.svh" + +`uvm_analysis_imp_decl(_src_req) +`uvm_analysis_imp_decl(_src_resp) +`uvm_analysis_imp_decl(_dest_resp) + +class end2end extends uvm_component; + `uvm_component_utils(end2end) + + uvm_analysis_imp_src_req #(cpu_transaction, end2end) src_req_export; // src's request + uvm_analysis_imp_src_resp #(cpu_transaction, end2end) src_resp_export; // dest's response to src's request + uvm_analysis_imp_dest_resp #(cpu_transaction, end2end) dest_resp_export; // + + cache_model cache; // holds values currently stored in cache + + cpu_transaction history[$]; // holds recent mem bus transactions + + int successes, errors; // records number of matches and mismatches + + function new(string name, uvm_component parent = null); + super.new(name, parent); + src_req_export = new("src_req_ap", this); + src_resp_export = new("src_resp_ap", this); + dest_resp_export = new("dest_resp_ap", this); + endfunction : new + + function void write_src_req(cpu_transaction t); + cpu_transaction tx = cpu_transaction::type_id::create("src_req_tx", this); + tx.copy(t); + + `uvm_info(this.get_name(), $sformatf("Detected CPU Request @%h", tx.addr), UVM_MEDIUM); + + if (history.size() > 0) begin + flush_history(); + end + endfunction : write_src_req + + function void write_src_resp(cpu_transaction t); + cpu_transaction tx = cpu_transaction::type_id::create("src_resp_tx", this); + tx.copy(t); + + `uvm_info(this.get_name(), $sformatf("Detected CPU Response @%h", tx.addr), UVM_MEDIUM); + `uvm_info(this.get_name(), $sformatf("cache before:\n%s", cache.sprint()), UVM_HIGH) + + if (tx.flush) begin : FLUSH + // flush request + + flush_history(); // takes care of any dirty data WBs from flush + + if (cache.dirty()) begin + errors++; + `uvm_error(this.get_name(), "Error: Cache Flush -> Cache Contains Dirty Data"); + `uvm_info(this.get_name(), $sformatf("%s", cache.sprint()), UVM_LOW); + end else begin + successes++; + `uvm_info(this.get_name(), "Success: Cache Flush -> No Dirty Data", UVM_LOW); + end + + cache.flush(); // flush all entries from cache model + end : FLUSH + else if (tx.addr < `NONCACHE_START_ADDR) begin : CACHEABLE + // memory request + if (history.size() == 0) begin : QUIET_MEM_BUS + // quiet memory bus + + if (cache.exists(tx.addr)) begin + // data is cached + successes++; + `uvm_info(this.get_name(), "Success: Cache Hit -> Quiet Mem Bus", UVM_LOW); + end else begin + // data not in cache + errors++; + `uvm_error(this.get_name(), "Error: Cache Miss -> Quiet Mem Bus"); + end + end : QUIET_MEM_BUS + else begin : ACTIVE_MEM_BUS + // active memory bus + if (cache.exists(tx.addr)) begin + // data is already cached + errors++; + `uvm_error(this.get_name(), "Error: Cache Hit -> Active Mem Bus"); + end else begin + // data not in cache, need to get data from memory + flush_history(); + + if (cache.exists(tx.addr)) begin + successes++; + `uvm_info(this.get_name(), "Success: Cache Miss -> Active Mem Bus", UVM_LOW); + end else begin + errors++; + `uvm_error(this.get_name(), + "Error: Data Requested by CPU is not pressent in cache after mem bus txns"); + end + end + end : ACTIVE_MEM_BUS + + if (tx.rw) begin + // update cache on PrWr + cache.update(tx.addr, tx.data, tx.byte_en); + end + end : CACHEABLE + else begin : MEM_MAPPED + // memory mapped io request + + if (history.size() == 1) begin + cpu_transaction mapped = history.pop_front(); + //FIXME:CHECK THAT THIS IS PROPER WAY TO DEAL WITH THIS + if (!cache.ignore_mask & tx.rw) begin + `uvm_info(this.get_name(), "Using Byte Mask for Memory Mapped Data", UVM_LOW); + tx.data = Utils::byte_mask(tx.byte_en) & tx.data; + end + if (mapped.compare(tx)) begin + successes++; + `uvm_info(this.get_name(), "Success: Mem Mapped I/O Pass Through Match", UVM_LOW); + end else begin + errors++; + `uvm_error(this.get_name(), "Error: Mem Mapped I/O Pass Through Mismatch"); + `uvm_info(this.get_name(), $sformatf( + "\ncpu req:\n%s\nmem bus:\n%s", tx.sprint(), mapped.sprint()), UVM_LOW) + end + end else begin + errors++; + `uvm_error( + this.get_name(), + $sformatf( + "Error: Mem Mapped I/O Pass Through Transaction Size Mismatch: expected 1, actual %0d", + history.size())); + end + end : MEM_MAPPED + endfunction : write_src_resp + + function void write_dest_resp(cpu_transaction t); + cpu_transaction tx = cpu_transaction::type_id::create("dest_resp_tx", this); + tx.copy(t); + + `uvm_info(this.get_name(), $sformatf("Detected Memory Response:: addr=%h", tx.addr), + UVM_MEDIUM); + + history.push_back(tx); + endfunction : write_dest_resp + + function void report_phase(uvm_phase phase); + `uvm_info(this.get_name(), $sformatf("Successes: %0d", successes), UVM_LOW); + `uvm_info(this.get_name(), $sformatf("Errors: %0d", errors), UVM_LOW); + endfunction + + function void handle_mem_tx(cpu_transaction mem_tx); + if (mem_tx.rw) begin + // write + // writes are cache evictions + if (cache.remove(mem_tx.addr, mem_tx.data)) begin + successes++; + `uvm_info(this.get_name(), $sformatf("Word sucessfully removed from cache model: 0x%h", + mem_tx.addr), UVM_LOW); + end else begin + errors++; + `uvm_error(this.get_name(), $sformatf("Error when removing word from cache model: 0x%h", + mem_tx.addr)); + end + end else begin + // read + cache.insert(mem_tx.addr, mem_tx.data, mem_tx.byte_en); + end + endfunction : handle_mem_tx + + function void flush_history(); + int block_idx = 0; + + if (history.size() % `L1_BLOCK_SIZE != 0) begin + errors++; + `uvm_error( + this.get_name(), + $sformatf( + "memory word requests do not match block size: requested %0d, not evenly divisible by: %0d", + history.size(), `L1_BLOCK_SIZE)); + end + + while (history.size() > 0) begin + cpu_transaction t = history.pop_front(); + handle_mem_tx(t); + block_idx++; + if (block_idx % `L1_BLOCK_SIZE == 0) begin + // last word of block + if (cache.is_valid_block(t.addr)) begin + successes++; + `uvm_info(this.get_name(), $sformatf("Valid block txn with memory: %h", t.addr), UVM_LOW); + end else begin + errors++; + `uvm_error(this.get_name(), + $sformatf("Invalid word addresses for block txn with memory: %h", t.addr)); + `uvm_info(this.get_name(), $sformatf("%s", cache.sprint()), UVM_LOW); + end + end + end + endfunction : flush_history + +endclass : end2end + +`endif diff --git a/verification/uvm/caches/env/cache_env.svh b/verification/uvm/caches/env/cache_env.svh new file mode 100644 index 000000000..b2cd29afe --- /dev/null +++ b/verification/uvm/caches/env/cache_env.svh @@ -0,0 +1,237 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cache_env.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Environment for cache verification +*/ + +`ifndef CACHE_ENV_SVH +`define CACHE_ENV_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" + +`include "cache_env_config.svh" // config +`include "memory_bfm.svh" // bfm + +`include "bus_predictor.svh" // uvm_subscriber +`include "bus_scoreboard.svh" // uvm_scoreboard + +`include "d_cpu_agent.svh" +`include "i_cpu_agent.svh" +`include "mem_arb_agent.svh" + +`include "cpu_transaction.svh" // uvm_sequence_item + +`include "mem_agent.svh" +`include "l2_agent.svh" +`include "mem_arb_agent.svh" + +`include "end2end.svh" // uvm_scoreboard + +typedef uvm_analysis_port#(cpu_transaction) cpu_ap; + +class cache_env extends uvm_env; + `uvm_component_utils(cache_env) + + function new(string name = "env", uvm_component parent = null); + super.new(name, parent); + endfunction + + cache_env_config env_config; //environment configuration + memory_bfm mem_bfm; //memory bus functional model + +`ifdef TB_L1_CONFIG + d_cpu_agent cpu_agt; // contains monitor and driver + bus_predictor cpu_pred; // a reference model to check the result + bus_scoreboard cpu_score; // scoreboard + + mem_agent mem_agt; // contains monitor + bus_predictor mem_pred; // a reference model to check the result + bus_scoreboard mem_score; // scoreboard + + end2end e2e; //end to end checker from l1 cache to memory bus + + function void build_phase(uvm_phase phase); + mem_bfm = memory_bfm::type_id::create("MEM_BFM", this); + + cpu_agt = d_cpu_agent::type_id::create("CPU_AGT", this); + cpu_pred = bus_predictor::type_id::create("CPU_PRED", this); + cpu_pred.cache = new("CPU_PRED_CACHE", mem_bfm, 0); + cpu_score = bus_scoreboard::type_id::create("CPU_SCORE", this); + + mem_agt = mem_agent::type_id::create("MEM_AGT", this); + mem_pred = bus_predictor::type_id::create("MEM_PRED", this); + mem_pred.cache = new("MEM_PRED_CACHE", mem_bfm, 1); + mem_score = bus_scoreboard::type_id::create("MEM_SCORE", this); + + e2e = end2end::type_id::create("E2E", this); + e2e.cache = new("E2E_CACHE", mem_bfm, 0); + endfunction + + function void connect_phase(uvm_phase phase); + // L1 CACHE AGENT + bus_connect(cpu_agt, cpu_pred, cpu_score); + + // MEMORY AGENT + bus_connect(mem_agt, mem_pred, mem_score); + + // L1 CACHE <-> MEMORY :: END TO END CHECKER + cpu_agt.mon.req_ap.connect(e2e.src_req_export); + cpu_agt.mon.resp_ap.connect(e2e.src_resp_export); + mem_agt.mon.resp_ap.connect(e2e.dest_resp_export); + endfunction : connect_phase +`endif + +`ifdef TB_L2_CONFIG + mem_arb_agent mem_arb_agt; // contains monitor and driver + bus_predictor mem_arb_pred; // a reference model to check the result + bus_scoreboard mem_arb_score; // scoreboard + + mem_agent mem_agt; // contains monitor + bus_predictor mem_pred; // a reference model to check the result + bus_scoreboard mem_score; // scoreboard + + end2end e2e; //end to end checker from l2 cache to memory bus + + function void build_phase(uvm_phase phase); + mem_bfm = memory_bfm::type_id::create("MEM_BFM", this); + + mem_arb_agt = mem_arb_agent::type_id::create("MEM_ARB_AGT", this); + mem_arb_pred = bus_predictor::type_id::create("MEM_ARB_PRED", this); + mem_arb_pred.cache = new("MEM_ARB_PRED_CACHE", mem_bfm, 1); + mem_arb_score = bus_scoreboard::type_id::create("MEM_ARB_SCORE", this); + + mem_agt = mem_agent::type_id::create("MEM_AGT", this); + mem_pred = bus_predictor::type_id::create("MEM_PRED", this); + mem_pred.cache = new("MEM_PRED_CACHE", mem_bfm, 1); + mem_score = bus_scoreboard::type_id::create("MEM_SCORE", this); + + e2e = end2end::type_id::create("E2E", this); + e2e.cache = new("E2E_CACHE", mem_bfm, 1); + endfunction + + function void connect_phase(uvm_phase phase); + // L1 CACHE AGENT + bus_connect(mem_arb_agt, mem_arb_pred, mem_arb_score); + + // MEMORY AGENT + bus_connect(mem_agt, mem_pred, mem_score); + + // L1 CACHE <-> MEMORY :: END TO END CHECKER + mem_arb_agt.mon.req_ap.connect(e2e.src_req_export); + mem_arb_agt.mon.resp_ap.connect(e2e.src_resp_export); + mem_agt.mon.resp_ap.connect(e2e.dest_resp_export); + endfunction : connect_phase +`endif + +`ifdef TB_FULL_CONFIG + d_cpu_agent d_cpu_agt; // contains monitor and driver + bus_predictor d_cpu_pred; // a reference model to check the result + bus_scoreboard d_cpu_score; // scoreboard + + i_cpu_agent i_cpu_agt; // contains monitor and driver + bus_predictor i_cpu_pred; // a reference model to check the result + bus_scoreboard i_cpu_score; // scoreboard + + l2_agent l2_agt; // contains monitor + bus_predictor l2_pred; // a reference model to check the result + bus_scoreboard l2_score; // scoreboard + + mem_agent mem_agt; // contains monitor + bus_predictor mem_pred; // a reference model to check the result + bus_scoreboard mem_score; // scoreboard + + end2end dl1_l2_e2e; //end to end checker from data l1 cache to l2 cache + end2end il1_l2_e2e; //end to end checker from instr l1 cache to l2 cache + end2end l2_mem_e2e; //end to end checker from l2 cache to memory bus + + function void build_phase(uvm_phase phase); + mem_bfm = memory_bfm::type_id::create("MEM_BFM", this); + + d_cpu_agt = d_cpu_agent::type_id::create("D_CPU_AGT", this); + d_cpu_pred = bus_predictor::type_id::create("D_CPU_PRED", this); + d_cpu_pred.cache = new("D_CPU_PRED_CACHE", mem_bfm, 0); + d_cpu_score = bus_scoreboard::type_id::create("D_CPU_SCORE", this); + + i_cpu_agt = i_cpu_agent::type_id::create("I_CPU_AGT", this); + i_cpu_pred = bus_predictor::type_id::create("I_CPU_PRED", this); + i_cpu_pred.cache = new("I_CPU_PRED_CACHE", mem_bfm, 0); + i_cpu_score = bus_scoreboard::type_id::create("I_CPU_SCORE", this); + + l2_agt = l2_agent::type_id::create("L2_AGT", this); + l2_pred = bus_predictor::type_id::create("L2_PRED", this); + l2_pred.cache = new("L2_PRED_CACHE", mem_bfm, 1); + l2_score = bus_scoreboard::type_id::create("L2_SCORE", this); + + mem_agt = mem_agent::type_id::create("MEM_AGT", this); + mem_pred = bus_predictor::type_id::create("MEM_PRED", this); + mem_pred.cache = new("MEM_PRED_CACHE", mem_bfm, 1); + mem_score = bus_scoreboard::type_id::create("MEM_SCORE", this); + + dl1_l2_e2e = end2end::type_id::create("DL1_L2_E2E", this); + dl1_l2_e2e.cache = new("DL1_L2_E2E_CACHE", mem_bfm, 0); + + il1_l2_e2e = end2end::type_id::create("IL1_L2_E2E", this); + il1_l2_e2e.cache = new("IL1_L2_E2E_CACHE", mem_bfm, 0); + + l2_mem_e2e = end2end::type_id::create("L2_MEM_E2E", this); + l2_mem_e2e.cache = new("L2_MEM_E2E_CACHE", mem_bfm, 1); + endfunction + + function void connect_phase(uvm_phase phase); + // DATA L1 CACHE AGENT + bus_connect(d_cpu_agt, d_cpu_pred, d_cpu_score); + + // INSTRUCTION L1 CACHE AGENT + bus_connect(i_cpu_agt, i_cpu_pred, i_cpu_score); + + // MEMORY AGENT + bus_connect(mem_agt, mem_pred, mem_score); + + // DATA L1 CACHE <-> L2 CACHE :: END TO END CHECKER + d_cpu_agt.mon.req_ap.connect(dl1_l2_e2e.src_req_export); + d_cpu_agt.mon.resp_ap.connect(dl1_l2_e2e.src_resp_export); + l2_agt.mon.resp_ap.connect(dl1_l2_e2e.dest_resp_export); + + // L2 CACHE <-> MEMORY :: END TO END CHECKER + l2_agt.mon.req_ap.connect(l2_mem_e2e.src_req_export); + l2_agt.mon.resp_ap.connect(l2_mem_e2e.src_resp_export); + mem_agt.mon.resp_ap.connect(l2_mem_e2e.dest_resp_export); + endfunction : connect_phase +`endif + + function void bus_connect(bus_agent agt, bus_predictor pred, bus_scoreboard score); + agt.mon.req_ap.connect(pred.analysis_export); // connect monitor to predictor + `uvm_info(this.get_name(), $sformatf( + "Connected <%s>-req_ap to <%s>", agt.mon.get_name(), pred.get_name()), UVM_FULL) + + pred.pred_ap.connect(score.expected_export); // connect predictor to scoreboard + `uvm_info(this.get_name(), $sformatf("Connected <%s> to <%s>", pred.get_name(), score.get_name() + ), UVM_FULL) + + agt.mon.resp_ap.connect(score.actual_export); // connect monitor to scoreboard + `uvm_info(this.get_name(), $sformatf( + "Connected <%s>-resp_ap to <%s>", agt.mon.get_name(), score.get_name()), UVM_FULL) + endfunction : bus_connect + +endclass : cache_env + +`endif diff --git a/verification/uvm/caches/env/cache_env_config.svh b/verification/uvm/caches/env/cache_env_config.svh new file mode 100644 index 000000000..90a6f4fbd --- /dev/null +++ b/verification/uvm/caches/env/cache_env_config.svh @@ -0,0 +1,79 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cache_evn_config.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Environment configurable parameters +*/ + +`ifndef CACHE_ENV_CONFIG +`define CACHE_ENV_CONFIG + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +class cache_env_config extends uvm_object; + int mem_timeout; + int mem_latency; + int mmio_latency; + int iterations; + + rand logic [15:0] mem_tag; // used by mem bfm and bus predictors for non-initialized memory + rand + logic [15:0] + mmio_tag; // used by mem bfm and bus predictors for memory mapped io response to reads + + `uvm_object_utils_begin(cache_env_config) + `uvm_field_int(iterations, UVM_ALL_ON) + `uvm_field_int(mem_timeout, UVM_ALL_ON) + `uvm_field_int(mem_latency, UVM_ALL_ON) + `uvm_field_int(mmio_latency, UVM_ALL_ON) + `uvm_field_int(mem_tag, UVM_ALL_ON) + `uvm_field_int(mmio_tag, UVM_ALL_ON) + `uvm_object_utils_end + + function new(string name = "cache_env_config"); + super.new(name); + if (!uvm_config_db#(uvm_bitstream_t)::get(null, "", "mem_timeout", mem_timeout)) begin + `uvm_fatal("mem_timeout", "No parameter passed in from command line with uvm_set_config_int"); + end + + if (!uvm_config_db#(uvm_bitstream_t)::get(null, "", "mem_latency", mem_latency)) begin + `uvm_fatal("mem_latency", "No parameter passed in from command line with uvm_set_config_int"); + end + + if (!uvm_config_db#(uvm_bitstream_t)::get(null, "", "mmio_latency", mmio_latency)) begin + `uvm_fatal("mmio_latency", + "No parameter passed in from command line with uvm_set_config_int"); + end + + if (!uvm_config_db#(uvm_bitstream_t)::get(null, "", "iterations", iterations)) begin + `uvm_fatal("iterations", "No parameter passed in from command line with uvm_set_config_int"); + end + endfunction + + function void post_randomize(); + `uvm_info(this.get_name(), $sformatf("env_config params:\n%s", this.sprint()), UVM_LOW) + endfunction : post_randomize + +endclass : cache_env_config + +`endif diff --git a/verification/uvm/caches/env/dut_params.svh b/verification/uvm/caches/env/dut_params.svh new file mode 100644 index 000000000..a8f35435c --- /dev/null +++ b/verification/uvm/caches/env/dut_params.svh @@ -0,0 +1,59 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: dut_params.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Hard coded configuration parameters for the DUT +*/ + +`ifndef DUT_PARAMS_SVH +`define DUT_PARAMS_SVH + +// time in ns +`define CLK_PERIOD 10 +// time in ps +`define PROPAGATION_DELAY #(2000); +// time in ps +`define MONITOR_DELAY #(3000); + +`define NONCACHE_START_ADDR 32'h8000_0000 + +//32 bit addr - word offset - byte offset +`define BYTE_INDEX_BITS 2 + +// L2 Params +`define L1_BLOCK_SIZE 4 +`define L1_CACHE_SIZE 2048 +`define L1_ASSOC 2 + +`define L1_WORD_INDEX_BITS ($clog2(`L1_BLOCK_SIZE)) +`define L1_FRAME_INDEX_BITS ($clog2((`L1_CACHE_SIZE / (32 / 8) ) / `L1_ASSOC / `L1_BLOCK_SIZE)) +`define L1_INDEX_BITS (`L1_FRAME_INDEX_BITS + `L1_WORD_INDEX_BITS + `BYTE_INDEX_BITS) +`define L1_TAG_BITS (32 - `L1_INDEX_BITS) + +`define L1_ADDR_IDX_END (`BYTE_INDEX_BITS+`L1_WORD_INDEX_BITS) +`define L1_ADDR_IDX_SIZE (32 - `L1_ADDR_IDX_END) + +// L2 Params + +`define L2_CACHE_SIZE 4096 +`define L2_BLOCK_SIZE 4 +`define L2_ASSOC 4 + +`endif diff --git a/verification/uvm/caches/env/interface_checker.svh b/verification/uvm/caches/env/interface_checker.svh new file mode 100644 index 000000000..770b0cd04 --- /dev/null +++ b/verification/uvm/caches/env/interface_checker.svh @@ -0,0 +1,74 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: interface_checker.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Checks for invalid combinations of input/output signals for the DUT +*/ + +`ifndef INTERFACE_CHECKER_SVH +`define INTERFACE_CHECKER_SVH + +module interface_checker ( + cache_if.cache d_cif, + cache_if.cache i_cif, + cache_if.cache l2_cif, + generic_bus_if.generic_bus d_cpu_if, + generic_bus_if.generic_bus i_cpu_if, + generic_bus_if.generic_bus d_l1_arb_bus_if, + generic_bus_if.generic_bus i_l1_arb_bus_if, + generic_bus_if.generic_bus arb_l2_bus_if, + generic_bus_if.generic_bus mem_if +); + + // Flush done without a flush request + d_cif_flush_done : + assert property (@(posedge d_cif.CLK) d_cif.flush_done |-> (d_cif.flush)) + else $fatal(1, "'d_cif.flush_done' should never be asserted without a cpu flush request"); + i_cif_flush_done : + assert property (@(posedge i_cif.CLK) i_cif.flush_done |-> (i_cif.flush)) + else $fatal(1, "'i_cif.flush_done' should never be asserted without a cpu flush request"); + l2_cif_flush_done : + assert property (@(posedge l2_cif.CLK) l2_cif.flush_done |-> (l2_cif.flush)) + else $fatal(1, "'l2_cif.flush_done' should never be asserted without a cpu flush request"); + + // L2 Response without a read/write request + arb_l2_bus_if_busy : + assert property (@(posedge l2_cif.CLK) !arb_l2_bus_if.busy |-> (arb_l2_bus_if.ren | arb_l2_bus_if.wen)) + else $fatal(1, "'arb_l2_bus_if.busy' should never be low without a cpu read/write request"); + + // Ensure all bus transactions only occur when there is a read/write request + d_l1_arb_bus_if_ren : + assert property (@(posedge d_cif.CLK) d_l1_arb_bus_if.ren |-> (!d_cpu_if.ren)) + else $fatal(1, "'d_l1_arb_bus_if.ren' should never be asserted without a cpu read request"); + d_l1_arb_bus_if_wen : + assert property (@(posedge d_cif.CLK) d_l1_arb_bus_if.wen |-> (!d_cpu_if.wen)) + else $fatal(1, "'d_l1_arb_bus_if.wen' should never be asserted without a cpu write request"); + + i_l1_arb_bus_if_ren : + assert property (@(posedge d_cif.CLK) i_l1_arb_bus_if.ren |-> (i_cpu_if.ren)) + else $fatal(1, "'i_l1_arb_bus_if.ren' should never be asserted without a cpu read request"); + i_l1_arb_bus_if_wen : + assert property (@(posedge d_cif.CLK) i_l1_arb_bus_if.wen |-> (i_cpu_if.wen)) + else $fatal(1, "'i_l1_arb_bus_if.wen' should never be asserted without a cpu write request"); + + //TODO: IMPLEMENT CHECK FOR INVALID BYTE_EN +endmodule : interface_checker + +`endif diff --git a/verification/uvm/caches/generic_bus_agent_comps/bus_agent.svh b/verification/uvm/caches/generic_bus_agent_comps/bus_agent.svh new file mode 100644 index 000000000..a3e14d77d --- /dev/null +++ b/verification/uvm/caches/generic_bus_agent_comps/bus_agent.svh @@ -0,0 +1,55 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: bus_agent.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/04/2022 +* Description: Generic UVM Agent to for a generic_bus_if +*/ + +`ifndef BUS_AGENT_SVH +`define BUS_AGENT_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "cpu_driver.svh" +`include "bus_monitor.svh" +`include "cpu_sequencer.svh" + +class null_driver; +endclass + +class bus_agent #( + type driver = null_driver +) extends uvm_agent; + `uvm_component_param_utils(bus_agent#(driver)) + cpu_sequencer sqr; + driver drv; + bus_monitor mon; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction + +endclass : bus_agent + +`endif diff --git a/verification/uvm/caches/generic_bus_agent_comps/bus_monitor.svh b/verification/uvm/caches/generic_bus_agent_comps/bus_monitor.svh new file mode 100644 index 000000000..33ea587c2 --- /dev/null +++ b/verification/uvm/caches/generic_bus_agent_comps/bus_monitor.svh @@ -0,0 +1,168 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: bus_monitor.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Monitor class for monitoring a generic_bus_if +*/ + +`ifndef BUS_MONITOR_SVH +`define BUS_MONITOR_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" + +`include "generic_bus_if.vh" +`include "cache_if.svh" +`include "dut_params.svh" + +class bus_monitor extends uvm_monitor; + // precedence breaks ties for transactions that come during the same tick (lower is higher precedence) + // TODO: change precedence to using uvm_wait_for_nba_region() + `uvm_component_utils(bus_monitor) + + virtual cache_if cif; + virtual generic_bus_if bus_if; + + cache_env_config env_config; + + uvm_analysis_port #(cpu_transaction) req_ap; + uvm_analysis_port #(cpu_transaction) resp_ap; + + int cycle; // number of clock cycles that have elapsed + + int precedence; + string cif_str; + string bus_if_str; + + function new(string name, uvm_component parent = null); + super.new(name, parent); + req_ap = new("req_ap", this); + resp_ap = new("resp_ap", this); + precedence = 0; + cif_str = ""; + bus_if_str = ""; + endfunction : new + + function void set_cif_str(string str); + this.cif_str = str; + endfunction : set_cif_str + + function void set_bus_if_str(string str); + this.bus_if_str = str; + endfunction : set_bus_if_str + + function void set_precedence(int p); + this.precedence = p; + endfunction : set_precedence + + // Build Phase - Get handle to virtual if from config_db + virtual function void build_phase(uvm_phase phase); + super.build_phase(phase); + // get config from database + if (!uvm_config_db#(cache_env_config)::get(this, "", "env_config", env_config)) begin + `uvm_fatal(this.get_name(), "env config not registered to db") + end + `uvm_info(this.get_name(), "pulled from db", UVM_FULL) + + if (!uvm_config_db#(virtual cache_if)::get(this, "", cif_str, cif)) begin + `uvm_fatal($sformatf("%s/%s", this.get_name(), cif_str), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), $sformatf("pulled <%s> from db", cif_str), UVM_FULL) + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", bus_if_str, bus_if)) begin + `uvm_fatal($sformatf("%s/%s", this.get_name(), bus_if_str), + "No virtual interface specified for this test instance"); + end + `uvm_info(this.get_name(), $sformatf("pulled <%s> from db", bus_if_str), UVM_FULL) + endfunction : build_phase + + virtual task run_phase(uvm_phase phase); + super.run_phase(phase); + + forever begin + cpu_transaction tx; + + @(posedge cif.CLK); + `MONITOR_DELAY // delay to pick up new value from bus + + if (cif.flush) begin + tx = cpu_transaction::type_id::create("tx"); + tx.addr = bus_if.addr; + tx.data = 'x; + tx.byte_en = bus_if.byte_en; + tx.flush = cif.flush; + + `uvm_info(this.get_name(), $sformatf("Writing Req AP:\nReq Ap:\n%s", tx.sprint()), UVM_FULL) + req_ap.write(tx); + + mem_wait(1'b1, cif.flush_done); + + `uvm_info(this.get_name(), $sformatf("Writing Resp AP:\nReq Ap:\n%s", tx.sprint()), + UVM_FULL) + resp_ap.write(tx); + end else if (bus_if.ren || bus_if.wen) begin + // captures activity between the driver and DUT + tx = cpu_transaction::type_id::create("tx"); + + tx.addr = bus_if.addr; + tx.byte_en = bus_if.byte_en; + tx.flush = cif.flush; + + if (bus_if.ren) begin + tx.rw = '0; // 0 -> read; 1 -> write + tx.data = 'x; //fill with garbage data + end else if (bus_if.wen) begin + tx.rw = '1; // 0 -> read; 1 -> write + tx.data = bus_if.wdata; + end + + `uvm_info(this.get_name(), $sformatf("Writing Req AP:\nReq Ap:\n%s", tx.sprint()), UVM_FULL) + req_ap.write(tx); + + mem_wait(1'b0, bus_if.busy); + + if (bus_if.ren) begin + tx.data = bus_if.rdata; + end + + #(precedence); + `uvm_info(this.get_name(), $sformatf("Writing Resp AP:\nReq Ap:\n%s", tx.sprint()), + UVM_FULL) + resp_ap.write(tx); + end + end + endtask : run_phase + + task mem_wait(logic clear, const ref logic flag); + int cycle = 0; + while (flag != clear) begin + @(posedge cif.CLK); + `MONITOR_DELAY // delay to pick up new value from bus + cycle++; //wait for memory to return + if (cycle > env_config.mem_timeout) begin + `uvm_fatal(this.get_name(), "memory timeout reached") + end + end + endtask : mem_wait + +endclass : bus_monitor + +`endif diff --git a/verification/uvm/caches/generic_bus_agent_comps/bus_predictor.svh b/verification/uvm/caches/generic_bus_agent_comps/bus_predictor.svh new file mode 100644 index 000000000..901fe060c --- /dev/null +++ b/verification/uvm/caches/generic_bus_agent_comps/bus_predictor.svh @@ -0,0 +1,106 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: bus_predictor.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM subscriber class for predicting the operation of a generic_bus_if +*/ + +`ifndef BUS_PREDICTOR_SHV +`define BUS_PREDICTOR_SHV + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "cpu_transaction.svh" +`include "cache_model.svh" + +class bus_predictor extends uvm_subscriber #(cpu_transaction); + `uvm_component_utils(bus_predictor) + + uvm_analysis_port #(cpu_transaction) pred_ap; + cpu_transaction pred_tx; + + cache_env_config env_config; + + cache_model cache; //software cache + + function new(string name, uvm_component parent = null); + super.new(name, parent); + endfunction : new + + function void build_phase(uvm_phase phase); + + pred_ap = new("pred_ap", this); + + // get config from database + if (!uvm_config_db#(cache_env_config)::get(this, "", "env_config", env_config)) begin + `uvm_fatal(this.get_name(), "env config not registered to db") + end + + endfunction + + function void write(cpu_transaction t); + // t is the transaction sent from monitor + pred_tx = cpu_transaction::type_id::create("pred_tx", this); + pred_tx.copy(t); + + `uvm_info(this.get_name(), $sformatf("Recevied Transaction:\n%s", pred_tx.sprint()), UVM_HIGH) + + `uvm_info(this.get_name(), $sformatf("cache before:\n%s", cache.sprint()), UVM_HIGH) + + if (pred_tx.flush) begin + pred_ap.write(pred_tx); // flush doesn't return any data + // don't update cache model because we need reads to return same data as was written + end else begin + // no cache flush + if (pred_tx.rw) begin + // 1 -> write + if (pred_tx.addr < `NONCACHE_START_ADDR) begin + if (cache.exists(pred_tx.addr)) begin + cache.update(pred_tx.addr, pred_tx.data, pred_tx.byte_en); + end else begin + cache.insert(pred_tx.addr, pred_tx.data, pred_tx.byte_en); + end + end + end else begin + // 0 -> read + if (pred_tx.addr < `NONCACHE_START_ADDR) begin + // cache/cache responds + pred_tx.data = cache.read(pred_tx.addr); + end else begin + // mmio responds + pred_tx.data = {env_config.mmio_tag, pred_tx.addr[15:0]}; + `uvm_info( + this.get_name(), $sformatf( + "Reading from Memory Mapped Address Space, Defaulting to value <%h>", pred_tx.data), + UVM_MEDIUM) + end + end + // after prediction, the expected output send to the scoreboard + pred_ap.write(pred_tx); + end + + `uvm_info(this.get_name(), $sformatf("cache after:\n%s", cache.sprint()), UVM_HIGH) + endfunction : write + +endclass : bus_predictor + +`endif diff --git a/verification/uvm/caches/generic_bus_agent_comps/bus_scoreboard.svh b/verification/uvm/caches/generic_bus_agent_comps/bus_scoreboard.svh new file mode 100644 index 000000000..ceb4f0b22 --- /dev/null +++ b/verification/uvm/caches/generic_bus_agent_comps/bus_scoreboard.svh @@ -0,0 +1,97 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: bus_scoreboard.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Scoreboard class for checking the operation of a generic_bus_if +*/ + +`ifndef BUS_SCOREBOARD_SVH +`define BUS_SCOREBOARD_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" + +class bus_scoreboard extends uvm_scoreboard; + `uvm_component_utils(bus_scoreboard) + uvm_analysis_export #(cpu_transaction) expected_export; // receive result from predictor + uvm_analysis_export #(cpu_transaction) actual_export; // receive result from DUT + uvm_tlm_analysis_fifo #(cpu_transaction) expected_fifo; + uvm_tlm_analysis_fifo #(cpu_transaction) actual_fifo; + + int m_matches, m_mismatches; // records number of matches and mismatches + + function new(string name, uvm_component parent); + super.new(name, parent); + m_matches = 0; + m_mismatches = 0; + endfunction + + function void build_phase(uvm_phase phase); + expected_export = new("expected_export", this); + actual_export = new("actual_export", this); + expected_fifo = new("expected_fifo", this); + actual_fifo = new("actual_fifo", this); + endfunction + + function void connect_phase(uvm_phase phase); + expected_export.connect(expected_fifo.analysis_export); + actual_export.connect(actual_fifo.analysis_export); + endfunction + + task run_phase(uvm_phase phase); + cpu_transaction expected_tx; //transaction from predictor + cpu_transaction actual_tx; //transaction from DUT + forever begin + expected_fifo.get(expected_tx); + `uvm_info(this.get_name(), $sformatf("Recieved new expected value:\n%s", + expected_tx.sprint()), UVM_HIGH); + + actual_fifo.get(actual_tx); + `uvm_info(this.get_name(), $sformatf("Recieved new actual value:\n%s", actual_tx.sprint()), + UVM_HIGH); + + if (expected_tx.compare(actual_tx)) begin + m_matches++; + `uvm_info(this.get_name(), "Success: Data Match", UVM_LOW); + `uvm_info(this.get_name(), $sformatf("\nExpected:\n%s\nReceived:\n%s", + expected_tx.sprint(), actual_tx.sprint()), UVM_MEDIUM) + end else begin + m_mismatches++; + `uvm_error(this.get_name(), "Error: Data Mismatch"); + `uvm_info(this.get_name(), $sformatf( + "\nExpected:\n%s\nReceived:\n%s", expected_tx.sprint(), actual_tx.sprint()), + UVM_LOW) + end + end + endtask + + + + function void report_phase(uvm_phase phase); + `uvm_info($sformatf("%s", this.get_name()), $sformatf("Matches: %0d", m_matches), UVM_LOW); + `uvm_info($sformatf("%s", this.get_name()), $sformatf("Mismatches: %0d", m_mismatches), + UVM_LOW); + `uvm_info($sformatf("%s", this.get_name()), $sformatf("TXN_Total: %0d", + m_matches + m_mismatches), UVM_LOW); + endfunction + +endclass : bus_scoreboard + +`endif diff --git a/verification/uvm/caches/models/Utils.svh b/verification/uvm/caches/models/Utils.svh new file mode 100644 index 000000000..20bd36eb5 --- /dev/null +++ b/verification/uvm/caches/models/Utils.svh @@ -0,0 +1,43 @@ +/* +* Copyright 2016 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: utils.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/1/2022 +* Description: utility class for: +* - helping with byte_en masking +*/ + +`ifndef UTILS_SHV +`define UTILS_SHV + +class Utils; + static function word_t byte_mask(logic [3:0] byte_en); + word_t mask; + + mask = '0; + for (int i = 0; i < 4; i++) begin + if (byte_en[i]) begin + mask |= 32'hff << (8 * i); + end + end + return mask; + endfunction : byte_mask +endclass : Utils + +`endif diff --git a/verification/uvm/caches/models/cache_model.svh b/verification/uvm/caches/models/cache_model.svh new file mode 100644 index 000000000..daaa0432b --- /dev/null +++ b/verification/uvm/caches/models/cache_model.svh @@ -0,0 +1,240 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: cache_model.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Abstracted software model of the caches +*/ + +`ifndef CACHE_MODEL_SVH +`define CACHE_MODEL_SVH + +`include "dut_params.svh" +`include "memory_bfm.svh" +`include "Utils.svh" + +import uvm_pkg::*; +import rv32i_types_pkg::*; + + +typedef enum { + CLEAN, + DIRTY +} cache_state; + + +/****************** Cache Block Model **********************/ + +class cache_block; + word_t words[word_t]; // key -> value :: addr -> data + cache_state state; + + function bit size_check(); + if (words.size() >= `L1_BLOCK_SIZE) return '0; + else return '1; + endfunction : size_check + + function void insert(word_t addr, word_t data); + if (!size_check()) begin + `uvm_error( + "cache_block", + $sformatf( + "Block overflow when attempting to insert item into cache: size %d:\ncache[%h]=%h", + words.size(), addr, data)); + end + if (words.exists(addr)) begin + `uvm_error("cache_block", + $sformatf("Attempted to insert item into cache that already exists:\ncache[%h]=%h", + addr, data)) + end else begin + words[addr] = data; + `uvm_info("cache_block", $sformatf("inserted cache word, cache[%h] = %h", addr, data), + UVM_MEDIUM) + end + endfunction : insert + + function void update(word_t addr, word_t data); + if (!words.exists(addr)) begin + `uvm_error("cache_block", $sformatf( + "Attempted to update item into cache that DNE:\ncache[%h]=%h", + addr, data)) + end else begin + words[addr] = data; + state = DIRTY; + `uvm_info("cache_block", $sformatf("updated cache word, cache[%h] = %h", addr, data), + UVM_MEDIUM) + end + endfunction : update + + function bit dirty(); + return this.state == DIRTY; + endfunction : dirty + + function string sprint(); + string str = "["; + foreach (words[w]) begin + str = {str, $sformatf("%h, ", words[w])}; + end + str = {str, $sformatf("] - %s\n", this.state.name)}; + return str; + endfunction : sprint +endclass + +/******************** Cache Model *************************/ + +class cache_model extends uvm_object; + cache_block blocks[word_t]; // key -> value :: base addr -> block + + memory_bfm bfm; + + bit ignore_mask; + + function new(string name = "cache_model", memory_bfm bfm, bit ignore_mask); + super.new(name); + this.bfm = bfm; + this.ignore_mask = ignore_mask; + endfunction + + function bit dirty(); + foreach (blocks[addr]) begin + if (blocks[addr].dirty()) begin + return 1'b1; + end + end + + return 1'b0; + endfunction : dirty + + function word_t get_base_addr(word_t addr); + word_t base = {addr[31:`L1_ADDR_IDX_END], {`L1_ADDR_IDX_END{1'b0}}}; + return base; + endfunction : get_base_addr + + function void insert(word_t addr, word_t data, logic [3:0] byte_en); + word_t base = get_base_addr(addr); + + if (exists(addr)) begin + // cache block formation started + `uvm_error(this.get_name(), + $sformatf("Attempted to insert item from cache that already exists:\ncache[%h]=%h", + addr, data)) + end else begin + word_t m_data; + word_t mask = Utils::byte_mask(byte_en); + if (ignore_mask) begin + m_data = data; + end else begin + m_data = (mask & data) | (~mask & read(addr)); + end + if (!blocks.exists(base)) begin + blocks[base] = new(); + end + blocks[base].insert(addr, m_data); + end + endfunction : insert + + function bit remove(word_t addr, word_t data); + if (exists(addr)) begin + word_t base = get_base_addr(addr); + blocks[base].words.delete(addr); + + if (!blocks[base].dirty()) begin + `uvm_error(this.get_name(), + $sformatf("Attempted to remove item from cache that is not dirty:\ncache[%h]=%h", + addr, data)) + return 0; + end + end else begin + `uvm_error(this.get_name(), $sformatf( + "Attempted to remove item from cache that DNE:\ncache[%h]=%h", addr, data)) + return 0; + end + return 1; + endfunction : remove + + function void update(word_t addr, word_t data, logic [3:0] byte_en); + if (exists(addr)) begin + word_t m_data; + word_t base = get_base_addr(addr); + word_t mask = Utils::byte_mask(byte_en); + if (ignore_mask) begin + m_data = data; + end else begin + m_data = (mask & data) | (~mask & read(addr)); + end + blocks[base].update(addr, m_data); + end else begin + `uvm_error(this.get_name(), $sformatf( + "Attempted to update item from cache that DNE:\ncache[%h]=%h", addr, data)) + end + endfunction : update + + function word_t read(word_t addr); + if (this.exists(addr)) begin + // check if data is cached + word_t base = get_base_addr(addr); + `uvm_info(this.get_name(), $sformatf("Reading from Initialized Data, value <%h>", + blocks[base].words[addr]), UVM_MEDIUM) + return blocks[base].words[addr]; + end else begin + // otherwise get expected value from bfm + word_t default_val = bfm.read(addr); + `uvm_info(this.get_name(), $sformatf( + "Reading from Non-Initialized Data, Defaulting to value <%h>", default_val), + UVM_MEDIUM) + return default_val; + end + endfunction : read + + function bit exists(word_t addr); + word_t base = get_base_addr(addr); + return blocks.exists(base) && blocks[base].words.exists(addr); + endfunction : exists + + function bit is_valid_block(word_t addr); + word_t base = get_base_addr(addr); + if (blocks.exists(base)) begin + if (blocks[base].words.size() == 0) begin + blocks.delete(base); // clean up evicted blocks + return '1; + end else begin + return blocks[base].words.size() == `L1_BLOCK_SIZE; // ensure block is full + end + end else begin + `uvm_info(this.get_name(), $sformatf("Unable to find block %h for requested addr: %h", base, + addr), UVM_LOW); + return '0; + end + endfunction : is_valid_block + + function void flush(); + blocks.delete(); // clear all data in cache + endfunction : flush + + function string sprint(); + string str = "cache:\n"; + foreach (blocks[b]) begin + str = {str, $sformatf("%h :: %s", b, blocks[b].sprint())}; + end + return str; + endfunction : sprint + +endclass : cache_model + +`endif diff --git a/verification/uvm/caches/readme.md b/verification/uvm/caches/readme.md new file mode 100644 index 000000000..29479e248 --- /dev/null +++ b/verification/uvm/caches/readme.md @@ -0,0 +1,89 @@ +# Caches UVM Testbench Setup Guide + +### .bashrc +> Note that there may be some env variables listed here that are not required. +``` +source ~/init_vlsi -p mitll90_Dec2019 -e all +export MGC_FDI_OA_VERSION=22.50 +export SOCET_FILES="/home/ecegrid/a/socpub/Public" +# WAF Build System +export SFF_ADMIN=$SOCET_FILES/SoCFoundationFlow/admin +source $SFF_ADMIN/setup_env.bash +export SFF_SIM_ENV="incisive" +# RISCV Setup +export TOOLS=$SOCET_FILES/riscv_dev +export RISCV=$TOOLS/riscv_installs/RV_current +export PATH=$TOOLS/scripts\:$PATH +export PATH=$RISCV/bin\:$PATH +export PATH=/opt/gcc/5.3.0/bin\:$PATH +export LD_LIBRARY_PATH=$RISCV/lib\:/opt/gcc/5.3.0/lib64 +export LIBRARY_PATH=/opt/gcc/5.3.0/lib64 +export PYTHONPATH=$TOOLS/python_libs/lib64/python +export PERL5LIB=$TOOLS/perl_libs/installs/Verilog-Perl/lib/perl5 +export PATH=$PATH\:$TOOLS/perl_libs/installs/Verilog-Perl/bin/ +# Various system variables +export SOCROOT="$HOME/SoCET_Public" +export PATH=/package/eda/cadence/GENUS191/tools/bin:$PATH +export QUESTA_HOME=/package/eda/mg/questa10.6b/questasim +#load newer version of git +module load git +``` + +### Build/Run Params +Everything related to building and running the uvm testbench is handled by the run.py script. To view the parameters: +```bash +run.py -h +``` +`Note:` you may need to change permissions of the run.py file: +```bash +chmod u+x run.py +``` + +## How to Debug DUT +The best way to use this UVM testbench for debugging is to utilize a combination of the `transcript` file and the waveforms. + +First run the design with the desired test configuration in gui mode: +``` +run.py -g --config l2 -s 12345 +``` +`Note`: when debugging it is helpful to have a static/non-random test for consistency. This is why I have added the `-s 12345` flag here. This is optional. + +This command will invoke the QuestaSim Gui and will auto load the waveforms for the correct config. Make sure to answer `no` to the `Are you sure you want to finish?` prompt. You can now search through the `transcript` file for any errors (Ctrl + f for "error"). You can now view the error and know the time step where the error occurred on the waveform. + +Let's walk through an example: +```bash +# 753000: uvm_test_top.ENV.MEM_ARB_SCORE [MEM_ARB_SCORE] Error: Data Mismatch +# UVM_INFO generic_bus_agent_comps/bus_scoreboard.svh(75) @ 753000: uvm_test_top.ENV.MEM_ARB_SCORE [MEM_ARB_SCORE] +# Expected: +# -------------------------------------------- +# Name Type Size Value +# -------------------------------------------- +# pred_tx cpu_transaction - @1297 +# rw integral 1 'h0 +# addr integral 32 'h2dc2bc5c +# data integral 32 'hced4bc5c +# byte_en integral 4 'h3 +# -------------------------------------------- +# +# Received: +# -------------------------------------------- +# Name Type Size Value +# -------------------------------------------- +# tx cpu_transaction - @1289 +# rw integral 1 'h0 +# addr integral 32 'h2dc2bc5c +# data integral 32 'hced8d351 +# byte_en integral 4 'h3 +# -------------------------------------------- +# +``` + +We see that the first line gives an error message and the time step, in this case at time 753000 there was a mismatch in data from the memory arbiter (MEM_ARB_SCORE). We also have the expected and actual values from the test. Most of the fields of each transaction are straight forward to understand except for `rw`. This value indicates if the transaction is a read or a write. If `rw == 1`, it was a `write` request, `otherwise`, it was a `read`. With this information the design engineering is armed with great information to begin reading through the waveforms to determine the cause of the issue. + +## Design Notes: +- Need to drive byte_en to memory, at least full word (4'b1000) +- evicting the right data but wrong address + +## Extension Ideas: +- Timing Agent + - responsible for monitoring both buses like end2end and checking if the correct number of cycles for hits/misses \ No newline at end of file diff --git a/verification/uvm/caches/run.py b/verification/uvm/caches/run.py new file mode 100755 index 000000000..0e9ed5680 --- /dev/null +++ b/verification/uvm/caches/run.py @@ -0,0 +1,132 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: run.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/04/2022 +# Description: Script for configuring and running UVM TB for the caches + +import argparse +import os +from scripts.cprint import cprint +from scripts.cprint import csprint +from scripts.cprint import tags, styles +from scripts.build import build +from scripts.run import run +from scripts.post_run import post_run +from scripts.repeat import repeat + +def seed_type(arg): + try: + return int(arg) # try convert to int + except ValueError: + pass + if arg == "random": + return arg + raise argparse.ArgumentTypeError("Seed must be an integer type or 'random'") + +def parse_arguments(): + parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, + description=csprint("Build and Run the UVM Testbench for the cache hierarchy\n" + " Note that all runtime parameters are saved in ", + styles.PURPLE, styles.BOLD) + + csprint("run_summary.log", styles.PURPLE, styles.BOLD, styles.UNDERLINE) + ) + parser.add_argument('--clean', action="store_true", + help=csprint("Remove build artifacts", styles.BLUE)) + parser.add_argument('--build', action="store_true", + help=csprint("Build project without run", styles.BLUE)) + parser.add_argument('--repeat', action="store_true", + help=csprint("Run with the last (most recent) parameters stored in run_summary.log", styles.BLUE)) + parser.add_argument('--testcase', '-t', type=str, default="random", + choices=["nominal", "evict", "index", "mmio", "flush", "random"], + help=csprint("Specify name of the uvm test:\n", styles.YELLOW) + + " nominal: read back values previously written to caches\n" + " evict: write to same index with different tag bits to force cache eviction\n" + " index: read/write to same block of data to ensure proper block indexing\n" + " mmio: read/write to memory mapped address space\n" + " flush: perform cache flush after nominal read/writes\n" + " random: random interleaving of previous test cases" + ) + parser.add_argument('--gui', '-g', action='store_true', + help=csprint("Specify whether to run with gui or terminal only", styles.YELLOW)) + parser.add_argument('--verbosity', '-v', type=str, default="low", + choices=["none", "low", "medium", "high", "full", "debug"], + help=csprint("Specify the verbosity level to be used for UVM Logging, each stage builds on the next\n", styles.YELLOW) + + " none: - only error messages shown\n" + " low: - actual and expected values for scoreboard errors\n" + " - success msg for data matches\n" + " - sequence parameters\n" + " medium - actual and expected values for all scoreboard checks\n" + " - predictor default values for non-initialized memory\n" + " - end2end transaction/propagation details\n" + " high - all uvm transactions detected in monitors, predictors, scoreboards\n" + " - predictor memory before:after\n" + " full - all connections between analysis ports\n" + " - all agent sub-object instantiations\n" + " - all virtual interface accesses to uvm db\n" + " debug - all messages") + parser.add_argument('--seed', '-s', type=seed_type, default="random", + help=csprint("Specify starter seed for uvm randomization\n", styles.YELLOW) + + "Identical seeds will produce identical runs") + parser.add_argument('--iterations', '-i', type=int, default=0, + help=csprint("Specify the requested number of memory accesses for a test", styles.YELLOW)) + parser.add_argument('--no-if-check', action="store_true", + help=csprint("Remove interface checks from test", styles.YELLOW)) + parser.add_argument('--mem-timeout', type=int, default=1000, + help=csprint("Specify the max memory latency before a fatal timeout error", styles.YELLOW)) + parser.add_argument('--mem-latency', type=int, default=1, + help=csprint("Specify the number of clock cycles before main memory returns", styles.YELLOW)) + parser.add_argument('--mmio-latency', type=int, default=2, + help=csprint("Specify the number of clock cycles before memory mapped IO returns", styles.YELLOW)) + parser.add_argument('--config', type=str, default="full", + choices=["l1", "l2", "full"], + help=csprint("Specify the configuration of the testbench to determine which agents and modules are activated", styles.YELLOW)) + return parser.parse_args() + + +if __name__ == '__main__': + params = parse_arguments() + + if params.clean: + cprint("Cleaning Directory...", tags.LOG) + os.system("rm -rf *.vstf work mitll90_Dec2019_all covhtmlreport *.log transcript *.wlf coverage/*.ucdb **/*.pyc") + exit() + elif params.repeat: + repeat(params) + + build(params) + + if (params.build): + exit() # stop after build + + run(params) + + cprint("Run Parameters:", tags.LOG) + + # print parameters + keep = ["mem_timeout", "iterations", "mem_latency", "testcase", "config", "mmio_latency"] + for arg in vars(params): + if arg in keep: + cprint("{key:<15}<- {val}".format(key=arg, val=getattr(params, arg)), tags.INFO) + + cprint("Running Post Run Script...", tags.LOG) + + post_run(params) \ No newline at end of file diff --git a/verification/uvm/caches/scripts/__init__.py b/verification/uvm/caches/scripts/__init__.py new file mode 100644 index 000000000..66245bbd4 --- /dev/null +++ b/verification/uvm/caches/scripts/__init__.py @@ -0,0 +1,24 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: __init__.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Required for python module imports \ No newline at end of file diff --git a/verification/uvm/caches/scripts/build.py b/verification/uvm/caches/scripts/build.py new file mode 100644 index 000000000..5a4ccc218 --- /dev/null +++ b/verification/uvm/caches/scripts/build.py @@ -0,0 +1,74 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: build.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Script for building UVM TB for the caches + +import os + +from cprint import cprint +from cprint import tags + +def build(params): + cprint("Building Sources...", tags.LOG) + SRC = "../../../source_code/" + + TB_GLOBAL_CONFIG = "TB_{}_CONFIG".format(params.config) + + res = os.system('''\ + vlog\ + +incdir+{CACHES} \ + +incdir+{L1} \ + +incdir+{L2} \ + +incdir+{INCLUDE} \ + +incdir+{PACKAGES} \ + +incdir+models \ + +incdir+cpu_agent \ + +incdir+bus_agents \ + +incdir+end2end \ + +incdir+generic_bus_agent_comps \ + +incdir+bfm \ + +incdir+env \ + +incdir+sequences \ + +incdir+tests \ + +define+TB_{TB_GLOBAL_CONFIG}_CONFIG \ + +define+INTERFACE_CHECKER={IF_CHECKER} \ + +acc \ + +cover \ + -L {QUESTA_HOME}/uvm-1.2 {SRAM} tb_caches_top.sv + '''.format( + CACHES=SRC + "caches", + L1=SRC + "caches/l1", + L2=SRC + "caches/l2", + SRAM=SRC + "caches/sram/sram.sv", + INCLUDE=SRC + "include", + PACKAGES=SRC + "packages", + QUESTA_HOME=os.getenv('QUESTA_HOME'), + TB_GLOBAL_CONFIG=params.config.upper(), + IF_CHECKER="0" if params.no_if_check else "1" + )) + + if (res == 0): + cprint("Build Finished", tags.SUCCESS) + else: + cprint("Build Failed", tags.FAIL) + exit() \ No newline at end of file diff --git a/verification/uvm/caches/scripts/cprint.py b/verification/uvm/caches/scripts/cprint.py new file mode 100644 index 000000000..e77683e1b --- /dev/null +++ b/verification/uvm/caches/scripts/cprint.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: cprint.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Script for colored printing to terminal + +class styles: + PURPLE = '\033[95m' + BLUE = '\033[94m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + ENDC = '\033[0m' + +class tags: + LOG = '{}[{:<7}]:'.format(styles.PURPLE, "LOG") + INFO = '{}[{:<7}]:'.format(styles.BLUE, "INFO") + SUCCESS = '{}[{:<7}]:'.format(styles.GREEN, "SUCCESS") + WARNING = '{}[{:<7}]:'.format(styles.YELLOW, "WARNING") + FAIL = '{}[{:<7}]:'.format(styles.RED, "FAIL") + +def csprint(msg, *formats): + res = "" + for f in formats: + res += f + res += msg + res += styles.ENDC + return res + +def cprint(msg, *formats): + for f in formats: + print(f), + print(msg), + print(styles.ENDC) \ No newline at end of file diff --git a/verification/uvm/caches/scripts/post_run.py b/verification/uvm/caches/scripts/post_run.py new file mode 100644 index 000000000..e16e23bbc --- /dev/null +++ b/verification/uvm/caches/scripts/post_run.py @@ -0,0 +1,130 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: post-run.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Script for parsing run results + +from datetime import datetime + +from cprint import cprint, csprint, tags, styles + +def post_run(params): + + if (params.gui): + resp = raw_input(csprint("Save run to run_summary.log?(Y/n)", styles.YELLOW)) + if (resp == "n"): + exit() + if params.config == "l1": + keys = ["seed", "cpu_txns", "mem_txns", "uvm_error", "uvm_fatal"] # keys to log variable + elif params.config == "l2": + keys = ["seed", "mem_arb_txns", "mem_txns", "uvm_error", "uvm_fatal"] # keys to log variable + elif params.config == "full": + keys = ["seed", "d_cpu_txns", "i_cpu_txns", "mem_txns", "uvm_error", "uvm_fatal"] # keys to log variable + + log = {} + if (params.seed != "random"): + log["seed"] = params.seed + + with open("transcript", "r") as transcript: + lines = transcript.readlines() + try: + sim_err = lines[-1].split(",")[0].split(":")[1].strip() + sim_err = int(sim_err) + if (sim_err > 0): + cprint("Fatal Simulation Error Detected", tags.FAIL) + cprint("For more details, view " + csprint("verification/uvm/caches/transcript", styles.UNDERLINE), tags.FAIL) + # exit() + except Exception as err: + cprint("Unable to parse simulator errors from transcript", tags.WARNING) + cprint(err, tags.WARNING) + + for line in lines: + words = line.strip().split() + for i, word in enumerate(words): + if not log.has_key("seed") and "seed" in word.lower(): + if words[i+1] == "=": + log["seed"] = words[i+2] + elif words[i+1] != "random": + log["seed"] = words[i+1] + + if not log.has_key("uvm_fatal") and "UVM_FATAL" in word: + if (words[i+1] == ":"): + log["uvm_fatal"] = words[i+2] + + if not log.has_key("uvm_error") and "UVM_ERROR" in word: + if (words[i+1] == ":"): + log["uvm_error"] = words[i+2] + + if "TXN_Total" in word: + if words[i-1] == "[MEM_SCORE]": + log["mem_txns"] = words[i+1] + elif words[i-1] == "[I_CPU_SCORE]": + log["i_cpu_txns"] = words[i+1] + elif words[i-1] == "[D_CPU_SCORE]": + log["d_cpu_txns"] = words[i+1] + elif words[i-1] == "[CPU_SCORE]": + log["cpu_txns"] = words[i+1] + elif words[i-1] == "[MEM_ARB_SCORE]": + log["mem_arb_txns"] = words[i+1] + + if len(log) == len(keys): + break # ignore the rest of the file + + for key in keys: + try: + if key == "uvm_error" or key == "uvm_fatal": + num = int(log[key]) + if (num != 0): + cprint("{key:<15}-> {val}".format(key=key, val=log[key]), tags.FAIL) + else: + cprint("{key:<15}-> {val}".format(key=key, val=log[key]), tags.SUCCESS) + continue + + cprint("{key:<15}-> {val}".format(key=key, val=log[key]), tags.SUCCESS) + + except: + cprint("{key:<15}-> {val}".format(key=key, val="None"), tags.FAIL) + + with open("run_summary.log", "a") as out: + now = datetime.now() + dt_string = now.strftime("%m-%d-%Y %H:%M:%S") + + msg = "[{date}]: ".format(date=dt_string) + + msg += "testcase: {}, ".format(params.testcase) + msg += "seed: {}, ".format(log["seed"]) + msg += "config: {}, ".format(params.config) + msg += "iterations: {}, ".format(params.iterations) + msg += "mem_timeout: {}, ".format(params.mem_timeout) + msg += "mem_latency: {}, ".format(params.mem_latency) + msg += "mmio_latency: {}, ".format(params.mmio_latency) + + for key in keys: + if key == "seed": + continue + try: + msg += "{key}: {val}, ".format(key=key, val=log[key]) + except: + msg += "{key}: None, ".format(key=key) + + out.write(msg) + out.write("\n") diff --git a/verification/uvm/caches/scripts/repeat.py b/verification/uvm/caches/scripts/repeat.py new file mode 100644 index 000000000..96057b054 --- /dev/null +++ b/verification/uvm/caches/scripts/repeat.py @@ -0,0 +1,42 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: repeat.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/17/2022 +# Description: Script for re-running the cache UVM test with the last used parameters +import re + +from cprint import cprint +from cprint import tags + +def repeat(params): + cprint("Repeating Previous Run...", tags.LOG) + try: + with open('run_summary.log', 'r') as f: + last_line = re.sub(r"\[.*\]:", "", f.readlines()[-1]) + except: + cprint("Couldn't open 'run_summary.log' file", tags.FAIL) + cprint("Make sure you have at least one run logged before repeating...", tags.FAIL) + exit() + for param in last_line.split(",")[0:-3]: + pair = param.strip().replace(",", "").split(": ") + if ("txns" not in pair[0]): + setattr(params, pair[0], pair[1]) diff --git a/verification/uvm/caches/scripts/run.do b/verification/uvm/caches/scripts/run.do new file mode 100644 index 000000000..a48285910 --- /dev/null +++ b/verification/uvm/caches/scripts/run.do @@ -0,0 +1,4 @@ +# onerror must be located in .do file +# cannot be inline -do from command line +onerror { quit -f } +run -all \ No newline at end of file diff --git a/verification/uvm/caches/scripts/run.py b/verification/uvm/caches/scripts/run.py new file mode 100644 index 000000000..59f9dbf7c --- /dev/null +++ b/verification/uvm/caches/scripts/run.py @@ -0,0 +1,80 @@ +#!/usr/bin/python + +# +# Copyright 2016 Purdue University +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Filename: run.py +# +# Created by: Mitch Arndt +# Email: arndt20@purdue.edu +# Date Created: 04/16/2022 +# Description: Script for running UVM TB for the caches + +import os + +from cprint import cprint +from cprint import tags + +def run(params): + RUN_COMMON = ''' + tb_caches_top -L + {QUESTA_HOME}/uvm-1.2 + -voptargs=+acc + -iterationlimit=100k + -sv_seed {SEED} + +UVM_TESTNAME={TESTCASE}_test + +UVM_VERBOSITY=UVM_{VERBOSITY} + +uvm_set_config_int=*,iterations,{ITERATIONS} + +uvm_set_config_int=*,mem_timeout,{MEM_TIMEOUT} + +uvm_set_config_int=*,mem_latency,{MEM_LATENCY} + +uvm_set_config_int=*,mmio_latency,{MMIO_LATENCY} + -do "coverage save -onexit -p coverage/{TESTCASE}.ucdb" + '''.format( + QUESTA_HOME=os.getenv('QUESTA_HOME'), + SEED=params.seed, + TESTCASE=params.testcase, + VERBOSITY=params.verbosity.upper(), + ITERATIONS=params.iterations, + MEM_TIMEOUT=params.mem_timeout, + MEM_LATENCY=params.mem_latency, + MMIO_LATENCY=params.mmio_latency, + ) + + if (params.gui): + cprint("Running with GUI...", tags.LOG) + res = os.system(''' + vsim -i + {RUN_COMMON} + -do "waves/{WAVE}.do" + -do "scripts/run.do" + '''.format( + RUN_COMMON=RUN_COMMON, + WAVE=params.config + ).replace("\n", " ")) + else: + cprint("Running with Terminal...", tags.LOG) + res = os.system(''' + vsim -c + {RUN_COMMON} + -do "scripts/run.do" + '''.format( + RUN_COMMON=RUN_COMMON, + ).replace("\n", " ")) + + if (res == 0): + cprint("Run Finished", tags.SUCCESS) + else: + cprint("Run Failed", tags.FAIL) \ No newline at end of file diff --git a/verification/uvm/caches/sequences/base_sequence.svh b/verification/uvm/caches/sequences/base_sequence.svh new file mode 100644 index 000000000..4a18fbc7b --- /dev/null +++ b/verification/uvm/caches/sequences/base_sequence.svh @@ -0,0 +1,45 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: base_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/03/2022 +* Description: Base sequence class to abstract N for random sequencing +*/ + +`ifndef BASE_SEQUENCE_SVH +`define BASE_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "cpu_transaction.svh" + +/** Sequence to test read after writes to the same location */ +class base_sequence extends uvm_sequence #(cpu_transaction); + `uvm_object_utils(base_sequence) + function new(string name = ""); + super.new(name); + endfunction : new + + rand int N; // total number of processor side transactions + +endclass : base_sequence +`endif diff --git a/verification/uvm/caches/sequences/evict_sequence.svh b/verification/uvm/caches/sequences/evict_sequence.svh new file mode 100644 index 000000000..2d016be47 --- /dev/null +++ b/verification/uvm/caches/sequences/evict_sequence.svh @@ -0,0 +1,83 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: evict_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence of addresses which map to the same cache frame, forcing a cache eviction +*/ + +`ifndef EVICT_SEQUENCE_SVH +`define EVICT_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "cpu_transaction.svh" +`include "dut_params.svh" +`include "base_sequence.svh" + +class evict_sequence extends base_sequence; + `uvm_object_utils(evict_sequence) + + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + int N_reps; // used to back calculate proper repetitions to used when combined with inner for loop + logic [`L1_INDEX_BITS-1:0] index; + + req_item = cpu_transaction::type_id::create("req_item"); + + N_reps = N / (`L1_ASSOC + 1); + + `uvm_info(this.get_name(), $sformatf("Requested size: %0d; Creating sequence with size N=%0d", + N, N_reps * (`L1_ASSOC + 1)), UVM_LOW) + + if (N_reps <= 0) begin + `uvm_fatal(this.get_name(), + $sformatf( + "Invalid Sequence Size: N must be at least %0d to trigger an eviction event", + (`L1_ASSOC + 1))) + end + + repeat (N_reps) begin + for (int i = 0; i < `L1_ASSOC + 1; i++) begin + start_item(req_item); + if (!req_item.randomize() with { + flush == 0; //TODO: DO WE WANT ANY FLUSH SIGNALS? + if (i != 0) {addr[`L1_INDEX_BITS-1:0] == index;} + rw == '1; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + index = req_item.addr[`L1_INDEX_BITS:0]; + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + end + endtask : body +endclass : evict_sequence + +`endif diff --git a/verification/uvm/caches/sequences/flush_sequence.svh b/verification/uvm/caches/sequences/flush_sequence.svh new file mode 100644 index 000000000..6f1a080f4 --- /dev/null +++ b/verification/uvm/caches/sequences/flush_sequence.svh @@ -0,0 +1,93 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: flush_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 04/18/2022 +* Description: Sequence that performs randomized flush after read/write +*/ + +`ifndef FLUSH_SEQUENCE_SVH +`define FLUSH_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "cpu_transaction.svh" +`include "base_sequence.svh" + +class flush_sequence extends base_sequence; + `uvm_object_utils(flush_sequence) + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + word_t accesses[word_t]; // queue of previous reads/writes + word_t flushes[word_t]; // queue of previous flushed addresses that need to be checked + + req_item = cpu_transaction::type_id::create("req_item"); + + `uvm_info(this.get_name(), $sformatf("Creating sequence with size N=%0d", N), UVM_LOW) + + repeat (N) begin + start_item(req_item); + + if (!req_item.randomize() with { + flush dist { + 1 := 25, + 0 := 75 + }; //force a 25%/75% distribution of flushes + rw dist { + 1 := 50, + 0 := 50 + }; //force a 50%/50% distribution of reads/writes + + if (flush == 1) { + //flush from previously accessed addr + addr inside {accesses}; + } else + if (flushes.size() > 0) {addr inside {flushes};} + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (req_item.flush == 0) begin + accesses[req_item.addr] = req_item.addr; + + if (flushes.exists(req_item.addr)) begin + flushes.delete(req_item.addr); + end + end else begin + // flush + accesses.delete(req_item.addr); + flushes[req_item.addr] = req_item.addr; + end + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + endtask : body +endclass : flush_sequence + +`endif diff --git a/verification/uvm/caches/sequences/index_sequence.svh b/verification/uvm/caches/sequences/index_sequence.svh new file mode 100644 index 000000000..282778af7 --- /dev/null +++ b/verification/uvm/caches/sequences/index_sequence.svh @@ -0,0 +1,98 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: index_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence that reads/writes to the same cache block to ensure proper word indexing inside a block +*/ + +`ifndef INDEX_SEQUENCE_SVH +`define INDEX_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "cpu_transaction.svh" +`include "dut_params.svh" +`include "base_sequence.svh" + +class index_sequence extends base_sequence; + `uvm_object_utils(index_sequence) + + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + int N_reps; // used to back calculate proper repetitions to used when combined with inner for loop + + logic [`L1_ADDR_IDX_SIZE-1:0] index; + word_t block_words[word_t]; + + req_item = cpu_transaction::type_id::create("req_item"); + + N_reps = N / (`L1_BLOCK_SIZE); + + `uvm_info(this.get_name(), $sformatf("Requested size: %0d; Creating sequence with size N=%0d", + N, N_reps * (`L1_BLOCK_SIZE)), UVM_LOW) + + if (N_reps <= 0) begin + `uvm_fatal(this.get_name(), + $sformatf( + "Invalid Sequence Size: N must be at least %0d to touch all words of a block", + (`L1_BLOCK_SIZE))) + end + + repeat (N_reps) begin + for (int i = 0; i < `L1_BLOCK_SIZE; i++) begin + start_item(req_item); + if (!req_item.randomize() with { + flush == 0; //TODO: DO WE WANT ANY FLUSH SIGNALS? + if (i != 0) { + // first iteration is completely random txn + addr[31:`L1_ADDR_IDX_END] == index; + addr inside {block_words}; + } + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + index = req_item.addr[31:`L1_ADDR_IDX_END]; + + if (i == 0) begin + // initialize block words arrays + for (int j = 0; j < `L1_BLOCK_SIZE * 4; j += 4) begin + word_t temp = {index, j[`L1_ADDR_IDX_END-1:0]}; + block_words[temp] = temp; + end + end + + block_words.delete(req_item.addr); // remove from list of addresses to r/w + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + end + endtask : body +endclass : index_sequence + +`endif diff --git a/verification/uvm/caches/sequences/master_sequence.svh b/verification/uvm/caches/sequences/master_sequence.svh new file mode 100644 index 000000000..23fde3d28 --- /dev/null +++ b/verification/uvm/caches/sequences/master_sequence.svh @@ -0,0 +1,174 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: master_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence that randomly interleaves all other sequences +*/ + +`ifndef MASTER_SEQUENCE_SVH +`define MASTER_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "dut_params.svh" + +`include "nominal_sequence.svh" +`include "index_sequence.svh" +`include "evict_sequence.svh" +`include "mmio_sequence.svh" +`include "flush_sequence.svh" +`include "base_sequence.svh" + +`include "cpu_transaction.svh" + + +class bounds; + rand int upper; + int lower; + + constraint ub {upper > lower;} + + function new(int _lower); + lower = _lower; + endfunction : new + + function string sprint(); + return $sformatf("(%0d, %0d)", lower, upper); + endfunction : sprint +endclass : bounds + +class sub_master_sequence; + rand bounds nom_bounds; + rand bounds evt_bounds; + rand bounds idx_bounds; + rand bounds mmio_bounds; + rand bounds flush_bounds; + + constraint nom {nom_bounds.upper < 10;} + constraint evt {evt_bounds.upper < 10;} + constraint idx {idx_bounds.upper < 10;} + constraint mmio {mmio_bounds.upper < 10;} + constraint flush {flush_bounds.upper < 10;} + + function new(); + nom_bounds = new(1); + evt_bounds = new(`L1_ASSOC + 1); + idx_bounds = new(`L1_BLOCK_SIZE); + mmio_bounds = new(1); + flush_bounds = new(1); + endfunction : new + + function void show(); + `uvm_info( + "sub_master_seq", + $sformatf( + "evt_bounds: %s, idx_bounds: %s, nom_bounds: %s, mmio_bounds: %s, flush_bounds: %s", + evt_bounds.sprint(), idx_bounds.sprint(), nom_bounds.sprint(), mmio_bounds.sprint(), + flush_bounds.sprint()), UVM_LOW); + endfunction +endclass : sub_master_sequence + +class master_sequence extends base_sequence; + `uvm_object_utils(master_sequence) + `uvm_declare_p_sequencer(cpu_sequencer) + + sub_master_sequence seq_param; + + nominal_sequence nom_seq; + index_sequence idx_seq; + evict_sequence evt_seq; + mmio_sequence mmio_seq; + flush_sequence flush_seq; + + function new(string name = ""); + super.new(name); + nom_seq = nominal_sequence::type_id::create("nom_seq"); + idx_seq = index_sequence::type_id::create("idx_seq"); + evt_seq = evict_sequence::type_id::create("evt_seq"); + mmio_seq = mmio_sequence::type_id::create("mmio_seq"); + flush_seq = flush_sequence::type_id::create("flush_seq"); + seq_param = new(); + endfunction : new + + function void sub_randomize(); + //randomize sub-sequences + if (!nom_seq.randomize() with { + N inside {[seq_param.nom_bounds.lower : seq_param.nom_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (!idx_seq.randomize() with { + N inside {[seq_param.idx_bounds.lower : seq_param.idx_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (!evt_seq.randomize() with { + N inside {[seq_param.evt_bounds.lower : seq_param.evt_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (!mmio_seq.randomize() with { + N inside {[seq_param.mmio_bounds.lower : seq_param.mmio_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (!flush_seq.randomize() with { + N inside {[seq_param.flush_bounds.lower : seq_param.flush_bounds.upper]}; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + endfunction + + task body(); + cpu_transaction req_item; + base_sequence seq_list [$]; + seq_list.push_back(nom_seq); + seq_list.push_back(idx_seq); + seq_list.push_back(evt_seq); + seq_list.push_back(mmio_seq); + seq_list.push_back(flush_seq); + + `uvm_info(this.get_name(), $sformatf("running %0d iterations", N), UVM_LOW) + + while (N > 0) begin + if (!seq_param.randomize()) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + seq_param.show(); // display sequence parameters + sub_randomize(); // randomize sub sequences + + seq_list.shuffle(); // reorder list elements to get random ordering + + for (int i = 0; i < seq_list.size(); i++) begin + seq_list[i].start(p_sequencer); + N -= seq_list[i].N; + end + end + endtask : body +endclass : master_sequence + +`endif diff --git a/verification/uvm/caches/sequences/mmio_sequence.svh b/verification/uvm/caches/sequences/mmio_sequence.svh new file mode 100644 index 000000000..6c9d19435 --- /dev/null +++ b/verification/uvm/caches/sequences/mmio_sequence.svh @@ -0,0 +1,66 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mmio_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence that reads/writes to memory mapped I/O address space +*/ + +`ifndef MMIO_SEQUENCE_SVH +`define MMIO_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" +`include "cpu_transaction.svh" +`include "dut_params.svh" + +class mmio_sequence extends base_sequence; + `uvm_object_utils(mmio_sequence) + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + + req_item = cpu_transaction::type_id::create("req_item"); + + `uvm_info(this.get_name(), $sformatf("Creating sequence with size N=%0d", N), UVM_LOW) + + repeat (N) begin + start_item(req_item); + + if (!req_item.randomize() with { + flush == 0; //TODO: DO WE WANT ANY FLUSH SIGNALS? + addr >= `NONCACHE_START_ADDR; + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + endtask : body +endclass : mmio_sequence + +`endif diff --git a/verification/uvm/caches/sequences/nominal_sequence.svh b/verification/uvm/caches/sequences/nominal_sequence.svh new file mode 100644 index 000000000..f110aca14 --- /dev/null +++ b/verification/uvm/caches/sequences/nominal_sequence.svh @@ -0,0 +1,91 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: nominal_sequence.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Sequence that performs randomized read after writes +*/ + +`ifndef NOMINAL_SEQUENCE_SVH +`define NOMINAL_SEQUENCE_SVH + +import uvm_pkg::*; +import rv32i_types_pkg::*; + +`include "uvm_macros.svh" + +`include "cpu_transaction.svh" +`include "base_sequence.svh" + +class nominal_sequence extends base_sequence; + `uvm_object_utils(nominal_sequence) + function new(string name = ""); + super.new(name); + endfunction : new + + task body(); + cpu_transaction req_item; + int write_count; // current number of writes + word_t writes[word_t]; // queue of write addresses + + req_item = cpu_transaction::type_id::create("req_item"); + + write_count = 0; + + `uvm_info(this.get_name(), $sformatf("Creating sequence with size N=%0d", N), UVM_LOW) + + repeat (N) begin + start_item(req_item); + + if (!req_item.randomize() with { + flush == 0; //TODO: DO WE WANT ANY FLUSH SIGNALS IN NOMINAL OPPERATION? + rw dist { + 1 := 1, + 0 := 1 + }; //force a 50/50 distribution of reads/writes + if (write_count > N / 2) { + //only reads allowed + rw == 0; + } + if (rw == 0) { + //read from previously written addr + addr inside {writes}; + } + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + if (req_item.rw) begin + // write + write_count++; + writes[req_item.addr] = req_item.addr; + end else begin + // read + writes.delete(req_item.addr); + end + + `uvm_info(this.get_name(), $sformatf("Generated New Sequence Item:\n%s", req_item.sprint()), + UVM_HIGH) + + finish_item(req_item); + end + endtask : body +endclass : nominal_sequence + +`endif diff --git a/verification/uvm/caches/tb_caches_top.sv b/verification/uvm/caches/tb_caches_top.sv new file mode 100644 index 000000000..e04b210f8 --- /dev/null +++ b/verification/uvm/caches/tb_caches_top.sv @@ -0,0 +1,217 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: tb_caches_top.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: Top Level Module for UVM Cache Verification +*/ + +// package file +`include "rv32i_types_pkg.sv" + +// design file +`include "l1_cache.sv" +// `include "l2_cache.sv" +`include "memory_arbiter.sv" + +// Interface checker file +`include "interface_checker.svh" + +// interface file +`include "generic_bus_if.vh" +`include "cache_if.svh" + +// UVM test file +`include "nominal_test.svh" +`include "index_test.svh" +`include "evict_test.svh" +`include "mmio_test.svh" +`include "flush_test.svh" +`include "random_test.svh" + +// Device Parameter Build Constants +`include "dut_params.svh" + +`timescale 1ns / 1ps +// import uvm packages +import uvm_pkg::*; + +module tb_caches_top (); + logic clk; + + // generate clock + initial begin + clk = 0; + forever #(`CLK_PERIOD) clk = !clk; + end + + // instantiate the interface + generic_bus_if i_cpu_bus_if (); // from processor to instruction l1 cache + generic_bus_if d_cpu_bus_if (); // from processor to data l1 cache + generic_bus_if i_l1_arb_bus_if (); // from instruction l1 cache to memory arbiter + generic_bus_if d_l1_arb_bus_if (); // from data l1 cache to memory arbiter + generic_bus_if arb_l2_bus_if (); // from memory arbiter to l2 cache + generic_bus_if mem_bus_if (); // from l2 cache to memory bus + + cache_if i_cif (clk); // holds flush, clear signals for i cache + cache_if d_cif (clk); // holds flush, clear signals for d cache + cache_if l2_cif (clk); // holds flush, clear signals for l2 cache + + if (`INTERFACE_CHECKER == 1) begin + interface_checker if_check ( //FIXME: THIS NEEDS TO BE UPDATED WITH PROPER INTERFACES + .d_cif(d_cif.cache), + .i_cif(i_cif.cache), + .l2_cif(l2_cif.cache), + .d_cpu_if(d_cpu_bus_if.generic_bus), + .i_cpu_if(i_cpu_bus_if.generic_bus), + .d_l1_arb_bus_if(d_l1_arb_bus_if.generic_bus), + .i_l1_arb_bus_if(i_l1_arb_bus_if.generic_bus), + .arb_l2_bus_if(arb_l2_bus_if.generic_bus), + .mem_if(mem_bus_if.generic_bus) + ); + end + + + /********************** Instantiate the DUT **********************/ + + +`ifdef TB_L1_CONFIG + // L1 Cache + l1_cache #( + .CACHE_SIZE(`L1_CACHE_SIZE), + .BLOCK_SIZE(`L1_BLOCK_SIZE), + .ASSOC(`L1_ASSOC), + .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + ) l1 ( + .CLK(d_cif.CLK), + .nRST(d_cif.nRST), + .clear(d_cif.clear), + .flush(d_cif.flush), + .clear_done(d_cif.clear_done), + .flush_done(d_cif.flush_done), + .proc_gen_bus_if(d_cpu_bus_if.generic_bus), + .mem_gen_bus_if(mem_bus_if.cpu) + ); +`endif + +`ifdef TB_L2_CONFIG + // L2 + // l2_cache #( + // .CACHE_SIZE(`L2_CACHE_SIZE), + // .BLOCK_SIZE(`L2_BLOCK_SIZE), + // .ASSOC(`L2_ASSOC), + // .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + // ) l2 ( + // .CLK(l2_cif.CLK), + // .nRST(l2_cif.nRST), + // .clear(l2_cif.clear), + // .flush(l2_cif.flush), + // .clear_done(l2_cif.clear_done), + // .flush_done(l2_cif.flush_done), + // .proc_gen_bus_if(arb_l2_bus_if.generic_bus), + // .mem_gen_bus_if(mem_bus_if.cpu) + // ); +`endif + +`ifdef TB_FULL_CONFIG + // Data L1 + l1_cache #( + .CACHE_SIZE(`L1_CACHE_SIZE), + .BLOCK_SIZE(`L1_BLOCK_SIZE), + .ASSOC(`L1_ASSOC), + .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + ) d_l1 ( + .CLK(d_cif.CLK), + .nRST(d_cif.nRST), + .clear(d_cif.clear), + .flush(d_cif.flush), + .clear_done(d_cif.clear_done), + .flush_done(d_cif.flush_done), + .proc_gen_bus_if(d_cpu_bus_if.generic_bus), + .mem_gen_bus_if(d_l1_arb_bus_if.cpu) + ); + + assign i_cif.nRST = d_cif.nRST; + + // Instruction L1 + l1_cache #( + .CACHE_SIZE(`L1_CACHE_SIZE), + .BLOCK_SIZE(`L1_BLOCK_SIZE), + .ASSOC(`L1_ASSOC), + .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + ) i_l1 ( + .CLK(i_cif.CLK), + .nRST(i_cif.nRST), + .clear(i_cif.clear), + .flush(i_cif.flush), + .clear_done(i_cif.clear_done), + .flush_done(i_cif.flush_done), + .proc_gen_bus_if(i_cpu_bus_if.generic_bus), + .mem_gen_bus_if(i_l1_arb_bus_if.cpu) + ); + + // Memory Arbiter + memory_arbiter mem_arb ( + .CLK(d_cif.CLK), + .nRST(d_cif.nRST), + .icache_if(i_l1_arb_bus_if.generic_bus), + .dcache_if(d_l1_arb_bus_if.generic_bus), + .mem_arb_if(arb_l2_bus_if.cpu) + ); + + assign l2_cif.nRST = d_cif.nRST; + assign l2_cif.flush = d_cif.flush; + assign l2_cif.clear = d_cif.clear; + + // L2 + // l2_cache #( + // .CACHE_SIZE(`L2_CACHE_SIZE), + // .BLOCK_SIZE(`L2_BLOCK_SIZE), + // .ASSOC(`L2_ASSOC), + // .NONCACHE_START_ADDR(`NONCACHE_START_ADDR) + // ) l2 ( + // .CLK(l2_cif.CLK), + // .nRST(l2_cif.nRST), + // .clear(l2_cif.clear), + // .flush(l2_cif.flush), + // .clear_done(l2_cif.clear_done), + // .flush_done(l2_cif.flush_done), + // .proc_gen_bus_if(arb_l2_bus_if.generic_bus), + // .mem_gen_bus_if(mem_bus_if.cpu) + // ); +`endif + + initial begin + uvm_config_db#(virtual cache_if)::set(null, "", "i_cif", i_cif); + uvm_config_db#(virtual cache_if)::set(null, "", "d_cif", d_cif); + uvm_config_db#(virtual cache_if)::set(null, "", "l2_cif", l2_cif); + + uvm_config_db#(virtual generic_bus_if)::set(null, "", "i_cpu_bus_if", i_cpu_bus_if); + uvm_config_db#(virtual generic_bus_if)::set(null, "", "d_cpu_bus_if", d_cpu_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(null, "", "i_l1_arb_bus_if", i_l1_arb_bus_if); + uvm_config_db#(virtual generic_bus_if)::set(null, "", "d_l1_arb_bus_if", d_l1_arb_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(null, "", "arb_l2_bus_if", arb_l2_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(null, "", "mem_bus_if", mem_bus_if); + + run_test(); + end +endmodule diff --git a/verification/uvm/caches/testplan/testplan.ods b/verification/uvm/caches/testplan/testplan.ods new file mode 100644 index 000000000..020f0a387 Binary files /dev/null and b/verification/uvm/caches/testplan/testplan.ods differ diff --git a/verification/uvm/caches/tests/base_test.svh b/verification/uvm/caches/tests/base_test.svh new file mode 100644 index 000000000..0d180f13f --- /dev/null +++ b/verification/uvm/caches/tests/base_test.svh @@ -0,0 +1,174 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: base_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test with default settings/configurations +*/ + +`ifndef BASE_TEST_SVH +`define BASE_TEST_SVH + +import uvm_pkg::*; +`include "uvm_macros.svh" +`include "cache_env.svh" +`include "cache_env_config.svh" + +`include "generic_bus_if.vh" +`include "cache_if.svh" + +class base_test #( + type sequence_type = nominal_sequence, + string sequence_name = "BASE_TEST" +) extends uvm_test; + `uvm_component_utils(base_test) + + sequence_type seq; + + cache_env_config env_config; + cache_env env; + virtual cache_if d_cif; + virtual cache_if i_cif; + virtual cache_if l2_cif; + + virtual generic_bus_if d_cpu_bus_if; + virtual generic_bus_if i_cpu_bus_if; + + virtual generic_bus_if d_l1_arb_bus_if; + virtual generic_bus_if i_l1_arb_bus_if; + + virtual generic_bus_if arb_l2_bus_if; + virtual generic_bus_if mem_bus_if; + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + env_config = cache_env_config::type_id::create("ENV_CONFIG", this); + if (!env_config.randomize()) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + + env = cache_env::type_id::create("ENV", this); + env.env_config = env_config; + + seq = sequence_type::type_id::create(sequence_name); + + // get interfaces from db + if (!uvm_config_db#(virtual cache_if)::get(this, "", "d_cif", d_cif)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/d_cif", "No virtual interface specified for this test instance") + end + if (!uvm_config_db#(virtual cache_if)::get(this, "", "i_cif", i_cif)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/i_cif", "No virtual interface specified for this test instance") + end + if (!uvm_config_db#(virtual cache_if)::get(this, "", "l2_cif", l2_cif)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/l2_cif", "No virtual interface specified for this test instance") + end + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", "d_cpu_bus_if", d_cpu_bus_if)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/d_cpu_bus_if", "No virtual interface specified for this test instance") + end + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", "i_cpu_bus_if", i_cpu_bus_if)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/i_cpu_bus_if", "No virtual interface specified for this test instance") + end + + if (!uvm_config_db#(virtual generic_bus_if)::get( + this, "", "d_l1_arb_bus_if", d_l1_arb_bus_if + )) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/d_l1_arb_bus_if", "No virtual interface specified for this test instance") + end + if (!uvm_config_db#(virtual generic_bus_if)::get( + this, "", "i_l1_arb_bus_if", i_l1_arb_bus_if + )) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/i_l1_arb_bus_if", "No virtual interface specified for this test instance") + end + + if (!uvm_config_db#(virtual generic_bus_if)::get( + this, "", "arb_l2_bus_if", arb_l2_bus_if + )) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/arb_l2_bus_if", "No virtual interface specified for this test instance") + end + + if (!uvm_config_db#(virtual generic_bus_if)::get(this, "", "mem_bus_if", mem_bus_if)) begin + // check if interface is correctly set in testbench top level + `uvm_fatal("Base/mem_bus_if", "No virtual interface specified for this test instance") + end + + // send the interfaces down + //TODO: SHOULD I NARROW THE SCOPE OF THE ENV_CONFIG? + uvm_config_db#(cache_env_config)::set(this, "*", "env_config", env_config); + + uvm_config_db#(virtual cache_if)::set(this, "env.agt*", "i_cif", i_cif); + uvm_config_db#(virtual cache_if)::set(this, "env.agt*", "d_cif", d_cif); + uvm_config_db#(virtual cache_if)::set(this, "env.agt*", "l2_cif", l2_cif); + + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "d_cpu_bus_if", d_cpu_bus_if); + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "i_cpu_bus_if", i_cpu_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "d_l1_arb_bus_if", + d_l1_arb_bus_if); + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "i_l1_arb_bus_if", + i_l1_arb_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "arb_l2_bus_if", arb_l2_bus_if); + + uvm_config_db#(virtual generic_bus_if)::set(this, "env.agt*", "mem_bus_if", mem_bus_if); + endfunction : build_phase + + task run_phase(uvm_phase phase); + phase.raise_objection(this, $sformatf("Starting <%s> in main phase", sequence_name)); + if (!seq.randomize() with { + if (env_config.iterations > 0) { + N == env_config.iterations; //command line request for iterations + } else { + N inside {[20 : 100]}; //default number of memory accesses + } + }) begin + `uvm_fatal("Randomize Error", "not able to randomize") + end + +`ifdef TB_L1_CONFIG + seq.start(env.cpu_agt.sqr); +`endif + +`ifdef TB_L2_CONFIG + seq.start(env.mem_arb_agt.sqr); +`endif + +`ifdef TB_FULL_CONFIG + seq.start(env.d_cpu_agt.sqr); +`endif + #5ns; + phase.drop_objection(this, $sformatf("Finished <%s> in main phase", sequence_name)); + endtask + +endclass : base_test + +`endif diff --git a/verification/uvm/caches/tests/evict_test.svh b/verification/uvm/caches/tests/evict_test.svh new file mode 100644 index 000000000..067f7e514 --- /dev/null +++ b/verification/uvm/caches/tests/evict_test.svh @@ -0,0 +1,41 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: evict_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for eviction_sequence +*/ + +`ifndef EVICT_TEST_SVH +`define EVICT_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "uvm_macros.svh" + +class evict_test extends base_test #(evict_sequence, "EVICT_SEQ"); + `uvm_component_utils(evict_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : evict_test + +`endif diff --git a/verification/uvm/caches/tests/flush_test.svh b/verification/uvm/caches/tests/flush_test.svh new file mode 100644 index 000000000..9473ffe02 --- /dev/null +++ b/verification/uvm/caches/tests/flush_test.svh @@ -0,0 +1,45 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: flush_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for nominal_test +*/ + +`ifndef FLUSH_TEST_SVH +`define FLUSH_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "flush_sequence.svh" +`include "uvm_macros.svh" + + +class flush_test extends base_test #(flush_sequence, "FLUSH_SEQ"); + `uvm_component_utils(flush_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : flush_test + +`endif + + diff --git a/verification/uvm/caches/tests/index_test.svh b/verification/uvm/caches/tests/index_test.svh new file mode 100644 index 000000000..646b25068 --- /dev/null +++ b/verification/uvm/caches/tests/index_test.svh @@ -0,0 +1,42 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: index_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for index_sequence +*/ + +`ifndef INDEX_TEST_SVH +`define INDEX_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "uvm_macros.svh" +`include "index_sequence.svh" + +class index_test extends base_test #(index_sequence, "INDEX_SEQ"); + `uvm_component_utils(index_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : index_test + +`endif diff --git a/verification/uvm/caches/tests/mmio_test.svh b/verification/uvm/caches/tests/mmio_test.svh new file mode 100644 index 000000000..c78b1167a --- /dev/null +++ b/verification/uvm/caches/tests/mmio_test.svh @@ -0,0 +1,45 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: mmio_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for mmio_sequence +*/ + +`ifndef MMIO_TEST_SVH +`define MMIO_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "mmio_sequence.svh" +`include "uvm_macros.svh" + + +class mmio_test extends base_test #(mmio_sequence, "MMIO_SEQ"); + `uvm_component_utils(mmio_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : mmio_test + +`endif + + diff --git a/verification/uvm/caches/tests/nominal_test.svh b/verification/uvm/caches/tests/nominal_test.svh new file mode 100644 index 000000000..0e5d348f2 --- /dev/null +++ b/verification/uvm/caches/tests/nominal_test.svh @@ -0,0 +1,47 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: nominal_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for nominal_test +*/ + +`ifndef NOMINAL_TEST_SVH +`define NOMINAL_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "nominal_sequence.svh" +`include "uvm_macros.svh" + +//TODO: TRY TO FIGURE OUT HOW TO MAKE THIS TYPEDEF WORK +// typedef base_test#(nominal_sequence, "NOMINAL_SEQ") nominal_test; + +class nominal_test extends base_test #(nominal_sequence, "NOMINAL_SEQ"); + `uvm_component_utils(nominal_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : nominal_test + +`endif + + diff --git a/verification/uvm/caches/tests/random_test.svh b/verification/uvm/caches/tests/random_test.svh new file mode 100644 index 000000000..baf038708 --- /dev/null +++ b/verification/uvm/caches/tests/random_test.svh @@ -0,0 +1,44 @@ +/* +* Copyright 2022 Purdue University +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* +* Filename: index_test.svh +* +* Created by: Mitch Arndt +* Email: arndt20@purdue.edu +* Date Created: 03/27/2022 +* Description: UVM Test for master_sequence +*/ + +`ifndef RANDOM_TEST_SVH +`define RANDOM_TEST_SVH + +import uvm_pkg::*; +`include "base_test.svh" +`include "master_sequence.svh" +`include "uvm_macros.svh" + +class random_test extends base_test #(master_sequence, "RANDOM_TEST"); + `uvm_component_utils(random_test) + + function new(string name = "", uvm_component parent); + super.new(name, parent); + endfunction : new + +endclass : random_test + +`endif + + diff --git a/verification/uvm/caches/waves/full.do b/verification/uvm/caches/waves/full.do new file mode 100644 index 000000000..f8768697d --- /dev/null +++ b/verification/uvm/caches/waves/full.do @@ -0,0 +1,206 @@ +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate /tb_caches_top/i_cif/CLK +add wave -noupdate -color Salmon /tb_caches_top/i_cif/nRST +add wave -noupdate -group i_cpu_bus_if /tb_caches_top/i_cpu_bus_if/addr +add wave -noupdate -group i_cpu_bus_if -color Salmon /tb_caches_top/i_cpu_bus_if/wdata +add wave -noupdate -group i_cpu_bus_if /tb_caches_top/i_cpu_bus_if/rdata +add wave -noupdate -group i_cpu_bus_if -color Cyan /tb_caches_top/i_cpu_bus_if/ren +add wave -noupdate -group i_cpu_bus_if -color Violet /tb_caches_top/i_cpu_bus_if/wen +add wave -noupdate -group i_cpu_bus_if -color Orange /tb_caches_top/i_cpu_bus_if/busy +add wave -noupdate -group i_cpu_bus_if /tb_caches_top/i_cpu_bus_if/byte_en +add wave -noupdate -expand -group d_cpu_bus_if /tb_caches_top/d_cpu_bus_if/addr +add wave -noupdate -expand -group d_cpu_bus_if -color Salmon /tb_caches_top/d_cpu_bus_if/wdata +add wave -noupdate -expand -group d_cpu_bus_if /tb_caches_top/d_cpu_bus_if/rdata +add wave -noupdate -expand -group d_cpu_bus_if -color Cyan /tb_caches_top/d_cpu_bus_if/ren +add wave -noupdate -expand -group d_cpu_bus_if -color Violet /tb_caches_top/d_cpu_bus_if/wen +add wave -noupdate -expand -group d_cpu_bus_if -color Orange /tb_caches_top/d_cpu_bus_if/busy +add wave -noupdate -expand -group d_cpu_bus_if /tb_caches_top/d_cpu_bus_if/byte_en +add wave -noupdate -group i_l1_arb_bus_if /tb_caches_top/i_l1_arb_bus_if/addr +add wave -noupdate -group i_l1_arb_bus_if -color Salmon /tb_caches_top/i_l1_arb_bus_if/wdata +add wave -noupdate -group i_l1_arb_bus_if /tb_caches_top/i_l1_arb_bus_if/rdata +add wave -noupdate -group i_l1_arb_bus_if -color Cyan /tb_caches_top/i_l1_arb_bus_if/ren +add wave -noupdate -group i_l1_arb_bus_if -color Violet /tb_caches_top/i_l1_arb_bus_if/wen +add wave -noupdate -group i_l1_arb_bus_if -color Orange /tb_caches_top/i_l1_arb_bus_if/busy +add wave -noupdate -group i_l1_arb_bus_if /tb_caches_top/i_l1_arb_bus_if/byte_en +add wave -noupdate -expand -group d_l1_arb_bus_if /tb_caches_top/d_l1_arb_bus_if/addr +add wave -noupdate -expand -group d_l1_arb_bus_if -color Salmon /tb_caches_top/d_l1_arb_bus_if/wdata +add wave -noupdate -expand -group d_l1_arb_bus_if /tb_caches_top/d_l1_arb_bus_if/rdata +add wave -noupdate -expand -group d_l1_arb_bus_if -color Cyan /tb_caches_top/d_l1_arb_bus_if/ren +add wave -noupdate -expand -group d_l1_arb_bus_if -color Violet /tb_caches_top/d_l1_arb_bus_if/wen +add wave -noupdate -expand -group d_l1_arb_bus_if -color Orange /tb_caches_top/d_l1_arb_bus_if/busy +add wave -noupdate -expand -group d_l1_arb_bus_if /tb_caches_top/d_l1_arb_bus_if/byte_en +add wave -noupdate -expand -group arb_l2_bus_if /tb_caches_top/arb_l2_bus_if/addr +add wave -noupdate -expand -group arb_l2_bus_if -color Salmon /tb_caches_top/arb_l2_bus_if/wdata +add wave -noupdate -expand -group arb_l2_bus_if /tb_caches_top/arb_l2_bus_if/rdata +add wave -noupdate -expand -group arb_l2_bus_if -color Cyan /tb_caches_top/arb_l2_bus_if/ren +add wave -noupdate -expand -group arb_l2_bus_if -color Violet /tb_caches_top/arb_l2_bus_if/wen +add wave -noupdate -expand -group arb_l2_bus_if -color Orange /tb_caches_top/arb_l2_bus_if/busy +add wave -noupdate -expand -group arb_l2_bus_if /tb_caches_top/arb_l2_bus_if/byte_en +add wave -noupdate -expand -group mem_bus_if /tb_caches_top/mem_bus_if/addr +add wave -noupdate -expand -group mem_bus_if -color Salmon /tb_caches_top/mem_bus_if/wdata +add wave -noupdate -expand -group mem_bus_if /tb_caches_top/mem_bus_if/rdata +add wave -noupdate -expand -group mem_bus_if -color Cyan /tb_caches_top/mem_bus_if/ren +add wave -noupdate -expand -group mem_bus_if -color Violet /tb_caches_top/mem_bus_if/wen +add wave -noupdate -expand -group mem_bus_if -color Orange /tb_caches_top/mem_bus_if/busy +add wave -noupdate -expand -group mem_bus_if /tb_caches_top/mem_bus_if/byte_en +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/CACHE_SIZE +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/BLOCK_SIZE +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/ASSOC +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/NONCACHE_START_ADDR +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_TOTAL_FRAMES +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_SETS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_FRAME_BITS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_SET_BITS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_BLOCK_BITS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/N_TAG_BITS +add wave -noupdate -group i_l1 -group params /tb_caches_top/i_l1/FRAME_SIZE +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/CLK +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/nRST +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/clear +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/flush +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/clear_done +add wave -noupdate -group i_l1 -group if /tb_caches_top/i_l1/flush_done +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/set_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_set_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/en_set_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/clr_set_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/frame_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_frame_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/en_frame_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/clr_frame_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/word_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_word_num +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/en_word_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/clr_word_ctr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/finish_word +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/finish_frame +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/finish_set +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/state +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_state +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/cache +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_cache +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/ridx +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/last_used +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_last_used +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/read_addr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/next_read_addr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/decoded_addr +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/hit +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/pass_through +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/hit_data +add wave -noupdate -group i_l1 /tb_caches_top/i_l1/hit_idx +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/CACHE_SIZE +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/BLOCK_SIZE +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/ASSOC +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/NONCACHE_START_ADDR +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_TOTAL_FRAMES +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_SETS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_FRAME_BITS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_SET_BITS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_BLOCK_BITS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/N_TAG_BITS +add wave -noupdate -group d_l1 -group params /tb_caches_top/d_l1/FRAME_SIZE +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/CLK +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/nRST +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/clear +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/flush +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/clear_done +add wave -noupdate -group d_l1 -group if /tb_caches_top/d_l1/flush_done +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/set_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_set_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/en_set_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/clr_set_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/frame_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_frame_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/en_frame_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/clr_frame_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/word_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_word_num +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/en_word_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/clr_word_ctr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/finish_word +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/finish_frame +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/finish_set +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/state +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_state +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/cache +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_cache +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/ridx +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/last_used +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_last_used +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/read_addr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/next_read_addr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/decoded_addr +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/hit +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/pass_through +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/hit_data +add wave -noupdate -group d_l1 /tb_caches_top/d_l1/hit_idx +add wave -noupdate -group mem_arb /tb_caches_top/mem_arb/CLK +add wave -noupdate -group mem_arb /tb_caches_top/mem_arb/nRST +add wave -noupdate -group mem_arb /tb_caches_top/mem_arb/state +add wave -noupdate -group mem_arb /tb_caches_top/mem_arb/next_state +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/CACHE_SIZE +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/BLOCK_SIZE +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/ASSOC +add wave -noupdate -group l2 -group params /tb_caches_top/l2/NONCACHE_START_ADDR +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TOTAL_FRAMES +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_SETS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_FRAME_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_SET_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_BLOCK_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TAG_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/FRAME_SIZE +add wave -noupdate -group l2 -group if /tb_caches_top/l2/CLK +add wave -noupdate -group l2 -group if /tb_caches_top/l2/nRST +add wave -noupdate -group l2 -group if /tb_caches_top/l2/clear +add wave -noupdate -group l2 -group if /tb_caches_top/l2/flush +add wave -noupdate -group l2 -group if /tb_caches_top/l2/clear_done +add wave -noupdate -group l2 -group if /tb_caches_top/l2/flush_done +add wave -noupdate -group l2 /tb_caches_top/l2/set_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_set_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_set_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_set_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/frame_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_frame_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_frame_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_frame_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/word_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_word_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_word_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_word_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/finish_word +add wave -noupdate -group l2 /tb_caches_top/l2/finish_frame +add wave -noupdate -group l2 /tb_caches_top/l2/finish_set +add wave -noupdate -group l2 /tb_caches_top/l2/state +add wave -noupdate -group l2 /tb_caches_top/l2/next_state +add wave -noupdate -group l2 /tb_caches_top/l2/cache +add wave -noupdate -group l2 /tb_caches_top/l2/next_cache +add wave -noupdate -group l2 /tb_caches_top/l2/decoded_addr +add wave -noupdate -group l2 /tb_caches_top/l2/hit +add wave -noupdate -group l2 /tb_caches_top/l2/pass_through +add wave -noupdate -group l2 /tb_caches_top/l2/hit_data +add wave -noupdate -group l2 /tb_caches_top/l2/hit_idx +add wave -noupdate -group l2 /tb_caches_top/l2/lru +add wave -noupdate -group l2 /tb_caches_top/l2/nextlru +add wave -noupdate -group l2 /tb_caches_top/l2/ridx +add wave -noupdate -group l2 /tb_caches_top/l2/read_addr +add wave -noupdate -group l2 /tb_caches_top/l2/next_read_addr +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {198037 ps} 0} +quietly wave cursor active 1 +configure wave -namecolwidth 150 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 1 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ps +update +WaveRestoreZoom {14943475 ps} {15163975 ps} diff --git a/verification/uvm/caches/waves/l1.do b/verification/uvm/caches/waves/l1.do new file mode 100644 index 000000000..2e4df743a --- /dev/null +++ b/verification/uvm/caches/waves/l1.do @@ -0,0 +1,88 @@ +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate /tb_caches_top/d_cif/CLK +add wave -noupdate -color Firebrick /tb_caches_top/d_cif/nRST +add wave -noupdate -expand -group cif /tb_caches_top/d_cif/clear +add wave -noupdate -expand -group cif -color {Medium Spring Green} /tb_caches_top/d_cif/flush +add wave -noupdate -expand -group cif /tb_caches_top/d_cif/clear_done +add wave -noupdate -expand -group cif -color {Medium Spring Green} /tb_caches_top/d_cif/flush_done +add wave -noupdate -expand -group cpu_bus_if -color Magenta /tb_caches_top/d_cpu_bus_if/addr +add wave -noupdate -expand -group cpu_bus_if -color Cyan /tb_caches_top/d_cpu_bus_if/wdata +add wave -noupdate -expand -group cpu_bus_if /tb_caches_top/d_cpu_bus_if/rdata +add wave -noupdate -expand -group cpu_bus_if -color {Indian Red} /tb_caches_top/d_cpu_bus_if/ren +add wave -noupdate -expand -group cpu_bus_if -color {Green Yellow} /tb_caches_top/d_cpu_bus_if/wen +add wave -noupdate -expand -group cpu_bus_if -color Red /tb_caches_top/d_cpu_bus_if/busy +add wave -noupdate -expand -group cpu_bus_if -color Gold /tb_caches_top/d_cpu_bus_if/byte_en +add wave -noupdate -expand -group mem_bus_if -color Magenta /tb_caches_top/mem_bus_if/addr +add wave -noupdate -expand -group mem_bus_if -color Cyan /tb_caches_top/mem_bus_if/wdata +add wave -noupdate -expand -group mem_bus_if /tb_caches_top/mem_bus_if/rdata +add wave -noupdate -expand -group mem_bus_if -color {Indian Red} /tb_caches_top/mem_bus_if/ren +add wave -noupdate -expand -group mem_bus_if -color {Green Yellow} /tb_caches_top/mem_bus_if/wen +add wave -noupdate -expand -group mem_bus_if -color Red /tb_caches_top/mem_bus_if/busy +add wave -noupdate -expand -group mem_bus_if -color Gold /tb_caches_top/mem_bus_if/byte_en +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/CACHE_SIZE +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/BLOCK_SIZE +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/ASSOC +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/NONCACHE_START_ADDR +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_TOTAL_FRAMES +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_SETS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_FRAME_BITS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_SET_BITS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_BLOCK_BITS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/N_TAG_BITS +add wave -noupdate -expand -group l1 -group params /tb_caches_top/l1/FRAME_SIZE +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/CLK +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/nRST +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/clear +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/flush +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/clear_done +add wave -noupdate -expand -group l1 -group if /tb_caches_top/l1/flush_done +add wave -noupdate -expand -group l1 /tb_caches_top/l1/set_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_set_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/en_set_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/clr_set_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/frame_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_frame_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/en_frame_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/clr_frame_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/word_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_word_num +add wave -noupdate -expand -group l1 /tb_caches_top/l1/en_word_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/clr_word_ctr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/finish_word +add wave -noupdate -expand -group l1 /tb_caches_top/l1/finish_frame +add wave -noupdate -expand -group l1 /tb_caches_top/l1/finish_set +add wave -noupdate -expand -group l1 /tb_caches_top/l1/abort +add wave -noupdate -expand -group l1 /tb_caches_top/l1/state +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_state +add wave -noupdate -expand -group l1 /tb_caches_top/l1/cache +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_cache +add wave -noupdate -expand -group l1 /tb_caches_top/l1/ridx +add wave -noupdate -expand -group l1 /tb_caches_top/l1/last_used +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_last_used +add wave -noupdate -expand -group l1 /tb_caches_top/l1/read_addr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/next_read_addr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/decoded_req_addr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/decoded_addr +add wave -noupdate -expand -group l1 /tb_caches_top/l1/hit +add wave -noupdate -expand -group l1 /tb_caches_top/l1/pass_through +add wave -noupdate -expand -group l1 /tb_caches_top/l1/hit_data +add wave -noupdate -expand -group l1 /tb_caches_top/l1/hit_idx +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {70000 ps} 0} +quietly wave cursor active 1 +configure wave -namecolwidth 150 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 1 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ps +update +WaveRestoreZoom {0 ps} {450840 ps} diff --git a/verification/uvm/caches/waves/l2.do b/verification/uvm/caches/waves/l2.do new file mode 100644 index 000000000..825c60de0 --- /dev/null +++ b/verification/uvm/caches/waves/l2.do @@ -0,0 +1,88 @@ +onerror {resume} +quietly WaveActivateNextPane {} 0 +add wave -noupdate /tb_caches_top/l2_cif/CLK +add wave -noupdate -color Red /tb_caches_top/l2_cif/nRST +add wave -noupdate -expand -group l2_cif /tb_caches_top/l2_cif/clear +add wave -noupdate -expand -group l2_cif -color Goldenrod /tb_caches_top/l2_cif/flush +add wave -noupdate -expand -group l2_cif /tb_caches_top/l2_cif/clear_done +add wave -noupdate -expand -group l2_cif -color Goldenrod /tb_caches_top/l2_cif/flush_done +add wave -noupdate -expand -group arb_l2_bus_if -color Magenta /tb_caches_top/arb_l2_bus_if/addr +add wave -noupdate -expand -group arb_l2_bus_if -color {Spring Green} /tb_caches_top/arb_l2_bus_if/wdata +add wave -noupdate -expand -group arb_l2_bus_if -color Coral /tb_caches_top/arb_l2_bus_if/rdata +add wave -noupdate -expand -group arb_l2_bus_if -color Firebrick /tb_caches_top/arb_l2_bus_if/ren +add wave -noupdate -expand -group arb_l2_bus_if -color {Green Yellow} /tb_caches_top/arb_l2_bus_if/wen +add wave -noupdate -expand -group arb_l2_bus_if -color {Sky Blue} /tb_caches_top/arb_l2_bus_if/busy +add wave -noupdate -expand -group arb_l2_bus_if -color Pink /tb_caches_top/arb_l2_bus_if/byte_en +add wave -noupdate -expand -group mem_bus_if -color Magenta /tb_caches_top/mem_bus_if/addr +add wave -noupdate -expand -group mem_bus_if -color {Spring Green} /tb_caches_top/mem_bus_if/wdata +add wave -noupdate -expand -group mem_bus_if -color Coral /tb_caches_top/mem_bus_if/rdata +add wave -noupdate -expand -group mem_bus_if -color Firebrick /tb_caches_top/mem_bus_if/ren +add wave -noupdate -expand -group mem_bus_if -color {Green Yellow} /tb_caches_top/mem_bus_if/wen +add wave -noupdate -expand -group mem_bus_if -color {Sky Blue} /tb_caches_top/mem_bus_if/busy +add wave -noupdate -expand -group mem_bus_if -color Pink /tb_caches_top/mem_bus_if/byte_en +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/CACHE_SIZE +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/BLOCK_SIZE +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/ASSOC +add wave -noupdate -group l2 -group params /tb_caches_top/l2/NONCACHE_START_ADDR +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TOTAL_BYTES +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TOTAL_WORDS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TOTAL_FRAMES +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_SETS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_FRAME_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_SET_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_BLOCK_BITS +add wave -noupdate -group l2 -group params -radix decimal /tb_caches_top/l2/N_TAG_BITS +add wave -noupdate -group l2 -group params /tb_caches_top/l2/FRAME_SIZE +add wave -noupdate -group l2 -group if /tb_caches_top/l2/CLK +add wave -noupdate -group l2 -group if /tb_caches_top/l2/nRST +add wave -noupdate -group l2 -group if /tb_caches_top/l2/clear +add wave -noupdate -group l2 -group if /tb_caches_top/l2/flush +add wave -noupdate -group l2 -group if /tb_caches_top/l2/clear_done +add wave -noupdate -group l2 -group if /tb_caches_top/l2/flush_done +add wave -noupdate -group l2 /tb_caches_top/l2/set_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_set_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_set_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_set_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/frame_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_frame_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_frame_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_frame_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/word_num +add wave -noupdate -group l2 /tb_caches_top/l2/next_word_num +add wave -noupdate -group l2 /tb_caches_top/l2/en_word_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/clr_word_ctr +add wave -noupdate -group l2 /tb_caches_top/l2/finish_word +add wave -noupdate -group l2 /tb_caches_top/l2/finish_frame +add wave -noupdate -group l2 /tb_caches_top/l2/finish_set +add wave -noupdate -group l2 /tb_caches_top/l2/state +add wave -noupdate -group l2 /tb_caches_top/l2/next_state +add wave -noupdate -group l2 /tb_caches_top/l2/cache +add wave -noupdate -group l2 /tb_caches_top/l2/next_cache +add wave -noupdate -group l2 /tb_caches_top/l2/decoded_addr +add wave -noupdate -group l2 /tb_caches_top/l2/hit +add wave -noupdate -group l2 /tb_caches_top/l2/pass_through +add wave -noupdate -group l2 /tb_caches_top/l2/hit_data +add wave -noupdate -group l2 /tb_caches_top/l2/hit_idx +add wave -noupdate -group l2 /tb_caches_top/l2/lru +add wave -noupdate -group l2 /tb_caches_top/l2/nextlru +add wave -noupdate -group l2 /tb_caches_top/l2/ridx +add wave -noupdate -group l2 /tb_caches_top/l2/read_addr +add wave -noupdate -group l2 /tb_caches_top/l2/next_read_addr +TreeUpdate [SetDefaultTree] +WaveRestoreCursors {{Cursor 1} {1897968 ps} 0} +quietly wave cursor active 1 +configure wave -namecolwidth 206 +configure wave -valuecolwidth 100 +configure wave -justifyvalue left +configure wave -signalnamewidth 1 +configure wave -snapdistance 10 +configure wave -datasetprefix 0 +configure wave -rowmargin 4 +configure wave -childrowmargin 2 +configure wave -gridoffset 0 +configure wave -gridperiod 1 +configure wave -griddelta 40 +configure wave -timeline 0 +configure wave -timelineunits ps +update +WaveRestoreZoom {0 ps} {4798927 ps} diff --git a/wscript b/wscript deleted file mode 100644 index d8fee75a2..000000000 --- a/wscript +++ /dev/null @@ -1,31 +0,0 @@ -#! /usr/bin/env python -# encoding: utf-8 - -import os -toolpath = os.environ['WAFDIR'] + '/../waf-extensions' - -top = '.' -out = 'build' - -def options(ctx): - if len(ctx.stack_path) == 1 and ctx.stack_path[0] is None: - ctx.load('SFFbuildmgr', tooldir=toolpath) - ctx.load('SFFbuild', tooldir=toolpath) - #ctx.load('Syn_support', tooldir=toolpath) - ctx.load('why') - else: - pass - -def configure(ctx): - if len(ctx.stack_path) == 1 and ctx.stack_path[0] is None: - ctx.load('SFFbuildmgr', tooldir=toolpath) - ctx.load('SFFbuild', tooldir=toolpath) - ctx.recurse('source_code') - if len(ctx.stack_path) == 1 and ctx.stack_path[0] is None: - ctx.SFFUnits.finalize() - -def sim_source(ctx): - ctx.recurse('source_code') - -def build(ctx): - pass