diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..acaf3e533 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,36 @@ +name: Lint + +on: + push: + paths: + - '**/*.py' + - 'bin/*' + - 'sim/vcs/run_vcs' + - '.ruff.toml' + - '!addins/*' + - '!tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/Q/*' + - '!tests/fp/quad/fpdatasetgen.py' + pull_request: + paths: + - '**/*.py' + - 'bin/*' + - 'sim/vcs/run_vcs' + - '.ruff.toml' + - '!addins/*' + - '!tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/Q/*' + - '!tests/fp/quad/fpdatasetgen.py' + +jobs: + lint: + name: Python ${{matrix.version}} lint + strategy: + matrix: + version: [39, 312] # Test on oldest and newest verions used in wally-package-install.sh + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set Python version + run: sed -i '/^target-version/c\target-version = "py${{matrix.version}}"' .ruff.toml + - name: Run ruff + uses: astral-sh/ruff-action@v3 diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 000000000..44b788c48 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,31 @@ +# Lint all .py files and extra python scripts without extensions +include = ["*.py", "bin/wsim", "bin/regression-wally", "bin/iterelf", "sim/vcs/run_vcs"] +exclude = ["addins/*", "tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/Q/*", "tests/fp/quad/fpdatasetgen.py"] + +# Target oldest version of Python used (Python 3.9 for Ubuntu 20.04 LTS) +target-version = "py39" + +line-length=250 + +[lint] +select = [ + "F", # various basic rules + "E101", # indentation contains mixed spaces and tabs + "E4", # imports + "E7", # various improvements + "E9", # error + "W1", # tabs used instead of spaces + "W292", # no newline at end of file + "UP", # Upgraded version available in newer Python + "EXE", # Executable file shebangs + "Q003", # Avoidable escaped quotes + "Q004", # Unnecessary esacpe character + "RUF", # Ruff specific rules +] + +ignore = [ + "E701", "E702", # multiple statements on one line + "E722", # do not use bare 'except' + "E74", # ambiguous name + "RUF005", # iterable unpacking in list +] diff --git a/README.md b/README.md index 32b93d36d..b700df728 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Installation CI](https://github.com/openhwgroup/cvw/actions/workflows/install.yml/badge.svg?branch=main) +[![Installation CI](https://github.com/openhwgroup/cvw/actions/workflows/install.yml/badge.svg?branch=main)](https://github.com/openhwgroup/cvw/actions/workflows/install.yml) # core-v-wally diff --git a/addins/cvw-arch-verif b/addins/cvw-arch-verif index 66b675017..44278d9a9 160000 --- a/addins/cvw-arch-verif +++ b/addins/cvw-arch-verif @@ -1 +1 @@ -Subproject commit 66b675017878032974c537ab7aa81758b9812530 +Subproject commit 44278d9a918dbc337ac214cd5dba5f71aa26dcfa diff --git a/benchmarks/coremark/coremark_sweep.py b/benchmarks/coremark/coremark_sweep.py index 8cfce025d..9a2bb48d9 100755 --- a/benchmarks/coremark/coremark_sweep.py +++ b/benchmarks/coremark/coremark_sweep.py @@ -65,10 +65,7 @@ with open(resultfile, mode='w', newline='') as csvfile: # Loop through each architecture and run the make commands for arch in arch_list: - if(str in arch): - xlen_value='32' - else: - xlen_value='64' + xlen_value = "32" if str in arch else "64" os.system("make clean") make_all = f"make all XLEN={xlen_value} ARCH={arch}" os.system(make_all) diff --git a/benchmarks/embench/embench_arch_sweep.py b/benchmarks/embench/embench_arch_sweep.py index 99d49e7be..20c0c800e 100755 --- a/benchmarks/embench/embench_arch_sweep.py +++ b/benchmarks/embench/embench_arch_sweep.py @@ -30,12 +30,8 @@ def tabulate_arch_sweep(directory): file = case+"_"+arch+".json" file_path = os.path.join(directory, file) lines = [] - try: - f = open(file_path, "r") + with open(file_path) as f: lines = f.readlines() - except: - f.close() - #print(file_path+" does not exist") for line in lines: #print("File: "+file+" Line: "+line) #p = re.compile('".*" : .*,') @@ -43,8 +39,8 @@ def tabulate_arch_sweep(directory): match = re.search(p, line) if match: prog = match.group(1) - result = match.group(2); - d[arch][prog] = result; + result = match.group(2) + d[arch][prog] = result #print(match.group(1)+" " + match.group(2)) f.close() for arch in [""] + archs: @@ -53,7 +49,7 @@ def tabulate_arch_sweep(directory): for prog in d[archs[0]]: print(prog, end="\t") for arch in archs: - entry = d[arch].get(prog, "n/a"); + entry = d[arch].get(prog, "n/a") print (entry, end="\t") print("") print("New geo mean", end="\t") @@ -84,4 +80,4 @@ def run_arch_sweep(): directory = run_arch_sweep() #directory = "run_20231120_072037-caches" -tabulate_arch_sweep(directory) \ No newline at end of file +tabulate_arch_sweep(directory) diff --git a/benchmarks/graphGen.py b/benchmarks/graphGen.py index 95e48db6e..91a62be76 100755 --- a/benchmarks/graphGen.py +++ b/benchmarks/graphGen.py @@ -10,13 +10,13 @@ from plotly.subplots import make_subplots debug = True -def loadCoremark(): +def loadCoremark(coremarkData): """loads the coremark data dictionary""" coremarkPath = "riscv-coremark/work/coremark.sim.log" keywordlist = ["CoreMark 1.0", "CoreMark Size", "MTIME", "MINSTRET", "Branches Miss Predictions", "BTB Misses"] for keyword in keywordlist: - bashInst = "cat " + coremarkPath + " | grep \"" + keyword + "\" | cut -d \':\' -f 2 | cut -d \" \" -f 2 | tail -1" + bashInst = "cat " + coremarkPath + ' | grep "' + keyword + "\" | cut -d ':' -f 2 | cut -d \" \" -f 2 | tail -1" result = subprocess.run(bashInst, stdout=subprocess.PIPE, shell=True) if (debug): print(result) coremarkData[keyword] = int(result.stdout) @@ -25,8 +25,8 @@ def loadCoremark(): def loadEmbench(embenchPath, embenchData): """loads the embench data dictionary""" - f = open(embenchPath) - embenchData = json.load(f) + with open(embenchPath) as f: + embenchData = json.load(f) if (debug): print(embenchData) return embenchData @@ -93,7 +93,7 @@ def main(): embenchSpeedOpt_SpeedData = {} embenchSizeOpt_SizeData = {} embenchSpeedOpt_SizeData = {} - # coremarkData = loadCoremark() + coremarkData = loadCoremark(coremarkData) embenchSpeedOpt_SpeedData = loadEmbench("embench/wallySpeedOpt_speed.json", embenchSpeedOpt_SpeedData) embenchSizeOpt_SpeedData = loadEmbench("embench/wallySizeOpt_speed.json", embenchSizeOpt_SpeedData) embenchSpeedOpt_SizeData = loadEmbench("embench/wallySpeedOpt_size.json", embenchSpeedOpt_SizeData) @@ -104,4 +104,4 @@ def main(): if __name__ == '__main__': sys.exit(main()) -# "ls -Art ../addins/embench-iot/logs/*speed* | tail -n 1 " # gets most recent embench speed log \ No newline at end of file +# "ls -Art ../addins/embench-iot/logs/*speed* | tail -n 1 " # gets most recent embench speed log diff --git a/bin/CacheSim.py b/bin/CacheSim.py index 2ac8b5461..f95c1ed03 100755 --- a/bin/CacheSim.py +++ b/bin/CacheSim.py @@ -16,19 +16,19 @@ ## ## SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 ## -## Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -## except in compliance with the License, or, at your option, the Apache License version 2.0. You +## Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +## except in compliance with the License, or, at your option, the Apache License version 2.0. You ## may obtain a copy of the License at ## ## https:##solderpad.org/licenses/SHL-2.1/ ## -## Unless required by applicable law or agreed to in writing, any work distributed under the -## License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -## either express or implied. See the License for the specific language governing permissions +## Unless required by applicable law or agreed to in writing, any work distributed under the +## License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +## either express or implied. See the License for the specific language governing permissions ## and limitations under the License. ################################################################################################ -# how to invoke this simulator: +# how to invoke this simulator: # CacheSim.py -f (-v) # so the default invocation for rv64gc is 'CacheSim.py 64 4 56 44 -f ' # the log files to run this simulator on can be generated from testbench.sv @@ -37,25 +37,26 @@ # This helps avoid unexpected logger behavior. # With verbose mode off, the simulator only reports mismatches between its and Wally's behavior. # With verbose mode on, the simulator logs each access into the cache. -# Add -p or --perf to report the hit/miss ratio. +# Add -p or --perf to report the hit/miss ratio. # Add -d or --dist to report the distribution of loads, stores, and atomic ops. # These distributions may not add up to 100; this is because of flushes or invalidations. import math import argparse import os +import sys class CacheLine: def __init__(self): self.tag = 0 self.valid = False self.dirty = False - + def __str__(self): - string = "(V: " + str(self.valid) + ", D: " + str(self.dirty) - string += ", Tag: " + str(hex(self.tag)) + ")" + string = f"(V: {self.valid}, D: {self.dirty}" + string += f", Tag: {hex(self.tag)})" return string - + def __repr__(self): return self.__str__() @@ -72,13 +73,13 @@ class Cache: self.ways = [] for i in range(numways): self.ways.append([]) - for j in range(numsets): + for _ in range(numsets): self.ways[i].append(CacheLine()) - + self.pLRU = [] for i in range(self.numsets): self.pLRU.append([0]*(self.numways-1)) - + # flushes the cache by setting all dirty bits to False def flush(self): for way in self.ways: @@ -92,20 +93,21 @@ class Cache: line = self.ways[waynum][setnum] if line.tag == tag and line.valid: line.dirty = 0 - if invalidate: line.valid = 0 - + if invalidate: + line.valid = 0 + # invalidates the cache by setting all valid bits to False def invalidate(self): for way in self.ways: for line in way: line.valid = False - + # resets the pLRU to a fresh 2-D array of 0s def clear_pLRU(self): self.pLRU = [] - for i in range(self.numsets): + for _ in range(self.numsets): self.pLRU.append([0]*(self.numways-1)) - + # splits the given address into tag, set, and offset def splitaddr(self, addr): # no need for offset in the sim, but it's here for debug @@ -113,7 +115,7 @@ class Cache: setnum = (addr >> self.offsetlen) & int('1'*self.setlen, 2) offset = addr & int('1'*self.offsetlen, 2) return tag, setnum, offset - + # performs a cache access with the given address. # returns a character representing the outcome: # H/M/E/D - hit, miss, eviction, or eviction with writeback @@ -138,7 +140,7 @@ class Cache: line.dirty = write self.update_pLRU(waynum, setnum) return 'M' - + # we need to evict. Select a victim and overwrite. victim = self.getvictimway(setnum) line = self.ways[victim][setnum] @@ -154,14 +156,14 @@ class Cache: def update_pLRU(self, waynum, setnum): if self.numways == 1: return - + tree = self.pLRU[setnum] bottomrow = (self.numways - 1)//2 index = (waynum // 2) + bottomrow - tree[index] = int(not (waynum % 2)) + tree[index] = int(not waynum % 2) while index > 0: parent = (index-1) // 2 - tree[parent] = index % 2 + tree[parent] = index % 2 index = parent # uses the psuedo-LRU tree to select @@ -170,7 +172,7 @@ class Cache: def getvictimway(self, setnum): if self.numways == 1: return 0 - + tree = self.pLRU[setnum] index = 0 bottomrow = (self.numways - 1) // 2 #first index on the bottom row of the tree @@ -180,28 +182,28 @@ class Cache: index = index*2 + 1 else: #tree[index] == 1 # Go to the right child - index = index*2 + 2 - + index = index*2 + 2 + victim = (index - bottomrow)*2 if tree[index] == 1: victim += 1 - + return victim - + def __str__(self): string = "" for i in range(self.numways): - string += "Way " + str(i) + ": " + string += f"Way {i}: " for line in self.ways[i]: - string += str(line) + ", " + string += f"{line}, " string += "\n\n" return string def __repr__(self): return self.__str__() - -def main(): + +def parseArgs(): parser = argparse.ArgumentParser(description="Simulates a L1 cache.") parser.add_argument('numlines', type=int, help="The number of lines per way (a power of 2)", metavar="L") parser.add_argument('numways', type=int, help="The number of ways (a power of 2)", metavar='W') @@ -211,8 +213,9 @@ def main(): parser.add_argument('-v', "--verbose", action='store_true', help="verbose/full-trace mode") parser.add_argument('-p', "--perf", action='store_true', help="Report hit/miss ratio") parser.add_argument('-d', "--dist", action='store_true', help="Report distribution of operations") + return parser.parse_args() - args = parser.parse_args() +def main(args): cache = Cache(args.numlines, args.numways, args.addrlen, args.taglen) extfile = os.path.expanduser(args.file) mismatches = 0 @@ -227,7 +230,7 @@ def main(): atoms = 0 totalops = 0 - with open(extfile, "r") as f: + with open(extfile) as f: for ln in f: ln = ln.strip() lninfo = ln.split() @@ -239,11 +242,11 @@ def main(): cache.clear_pLRU() if args.verbose: print("New Test") - + else: if args.dist: totalops += 1 - + if lninfo[1] == 'F': cache.flush() if args.verbose: @@ -257,22 +260,22 @@ def main(): IsCBOClean = lninfo[1] != 'C' cache.cbo(addr, IsCBOClean) if args.verbose: - print(lninfo[1]); + print(lninfo[1]) else: addr = int(lninfo[0], 16) iswrite = lninfo[1] == 'W' or lninfo[1] == 'A' or lninfo[1] == 'Z' result = cache.cacheaccess(addr, iswrite) - + if args.verbose: tag, setnum, offset = cache.splitaddr(addr) print(hex(addr), hex(tag), hex(setnum), hex(offset), lninfo[2], result) - + if args.perf: if result == 'H': hits += 1 else: misses += 1 - + if args.dist: if lninfo[1] == 'R': loads += 1 @@ -280,23 +283,24 @@ def main(): stores += 1 elif lninfo[1] == 'A': atoms += 1 - - if not result == lninfo[2]: - print("Result mismatch at address", lninfo[0]+ ". Wally:", lninfo[2]+", Sim:", result) + + if result != lninfo[2]: + print(f"Result mismatch at address {lninfo[0]}. Wally: {lninfo[2]}, Sim: {result}") mismatches += 1 if args.dist: percent_loads = str(round(100*loads/totalops)) percent_stores = str(round(100*stores/totalops)) percent_atoms = str(round(100*atoms/totalops)) - print("This log had", percent_loads+"% loads,", percent_stores+"% stores, and", percent_atoms+"% atomic operations.") - + print(f"This log had {percent_loads}% loads, {percent_stores}% stores, and {percent_atoms}% atomic operations.") + if args.perf: ratio = round(hits/misses,3) print("There were", hits, "hits and", misses, "misses. The hit/miss ratio was", str(ratio)+".") - + if mismatches == 0: print("SUCCESS! There were no mismatches between Wally and the sim.") return mismatches if __name__ == '__main__': - exit(main()) + args = parseArgs() + sys.exit(main(args)) diff --git a/bin/iterelf b/bin/iterelf index ad39b69da..766988cbb 100755 --- a/bin/iterelf +++ b/bin/iterelf @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # iterelf # David_Harris@hmc.edu and Rose Thompson 7/3/2024 @@ -7,20 +7,21 @@ import argparse import os +import sys import multiprocessing -from multiprocessing import Pool, TimeoutError +from multiprocessing import Pool, TimeoutError as MPTimeoutError TIMEOUT_DUR = 60 # 1` minute class bcolors: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKCYAN = '\033[96m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' + HEADER = "\033[95m" + OKBLUE = "\033[94m" + OKCYAN = "\033[96m" + OKGREEN = "\033[92m" + WARNING = "\033[93m" + FAIL = "\033[91m" + ENDC = "\033[0m" + BOLD = "\033[1m" + UNDERLINE = "\033[4m" def search_log_for_mismatches(logfile): """Search through the given log file for text, returning True if it is found or False if it is not""" @@ -28,33 +29,32 @@ def search_log_for_mismatches(logfile): os.system(grepwarn) greperr = "grep -H Error: " + logfile os.system(greperr) - grepcmd = "grep -a -e 'Mismatches : 0' '%s' > /dev/null" % logfile -# print(" search_log_for_text invoking %s" % grepcmd) + grepcmd = f"grep -a -e 'Mismatches : 0' '{logfile}' > /dev/null" return os.system(grepcmd) == 0 def run_test_case(elf): """Run the given test case, and return 0 if the test suceeds and 1 if it fails""" - WALLY = os.environ.get('WALLY') - fields = elf.rsplit('/', 3) - if (fields[2] == "ref"): + WALLY = os.environ.get("WALLY") + fields = elf.rsplit("/", 3) + if fields[2] == "ref": shortelf = fields[1] + "_" + fields[3] else: shortelf = fields[2] + "_" + fields[3] -# shortelf = fields[1] + "_" + fields[2] + # shortelf = fields[1] + "_" + fields[2] logfile = WALLY + "/sim/" + args.sim + "/logs/" + shortelf + ".log" - cmd = "wsim " + args.config + " " + shortelf + " --elf " + elf + " --sim " + args.sim + " --lockstep > " + logfile # add coveerage flags if necessary -# print("cmd = " + cmd) + cmd = "wsim " + args.config + " " + shortelf + " --elf " + elf + " --sim " + args.sim + " --lockstep > " + logfile # add coveerage flags if necessary + # print("cmd = " + cmd) os.system(cmd) if search_log_for_mismatches(logfile): - print(f"{bcolors.OKGREEN}%s: Success{bcolors.ENDC}" % (cmd)) + print(f"{bcolors.OKGREEN}{cmd}: Success{bcolors.ENDC}") return 0 - elif("WALLY-cbom-01" in elf): + elif "WALLY-cbom-01" in elf: # Remove this when CBO instructions are modeled in ImperasDV - print(f"{bcolors.OKCYAN}%s: Expected mismatch because ImperasDV does not yet model cache for CBO instructions {bcolors.ENDC}" % (cmd)) + print(f"{bcolors.OKCYAN}{cmd}: Expected mismatch because ImperasDV does not yet model cache for CBO instructions {bcolors.ENDC}") return 0 else: - print(f"{bcolors.FAIL}%s: Failures detected in output{bcolors.ENDC}" % (cmd)) - print(" Check %s" % logfile) + print(f"{bcolors.FAIL}{cmd}: Failures detected in output{bcolors.ENDC}") + print(f" Check {logfile}") return 1 ################################## @@ -74,34 +74,32 @@ args = parser.parse_args() # find all ELF files in directory ElfList = [] -if (os.path.isdir(args.dir)): +if os.path.isdir(args.dir): DirectorMode = 1 for dirpath, dirnames, filenames in os.walk(os.path.abspath(args.dir)): for file in filenames: - if (file.endswith("elf") and not file.endswith(args.exclude)): + if file.endswith("elf") and not file.endswith(args.exclude): ElfList.append(os.path.join(dirpath, file)) else: print(args.dir + " is not a directory") - exit(1) -#print(ElfList) + sys.exit(1) # spawn parallel wsim jobs for each ELF file ImperasDVLicenseCount = 8 -with Pool(processes=min(len(ElfList),multiprocessing.cpu_count(), ImperasDVLicenseCount)) as pool: +with Pool(processes=min(len(ElfList), multiprocessing.cpu_count(), ImperasDVLicenseCount)) as pool: num_fail = 0 results = {} for elf in ElfList: - results[elf] = pool.apply_async(run_test_case,(elf,)) - for (elf,result) in results.items(): + results[elf] = pool.apply_async(run_test_case, (elf,)) + for elf, result in results.items(): try: - num_fail+=result.get(timeout=TIMEOUT_DUR) - except TimeoutError: - num_fail+=1 - print(f"{bcolors.FAIL}%s: Timeout - runtime exceeded %d seconds{bcolors.ENDC}" % (elf, TIMEOUT_DUR)) + num_fail += result.get(timeout=TIMEOUT_DUR) + except MPTimeoutError: + num_fail += 1 + print(f"{bcolors.FAIL}{elf}: Timeout - runtime exceeded {TIMEOUT_DUR} seconds{bcolors.ENDC}") -if (num_fail == 0): +if num_fail == 0: print(f"{bcolors.OKGREEN}SUCCESS! All tests ran without failures{bcolors.ENDC}") else: - print(f"{bcolors.FAIL}Completed %d tests with %d failures{bcolors.ENDC}" % (len(ElfList), num_fail)) - + print(f"{bcolors.FAIL}Completed {len(ElfList)} tests with {num_fail} failures{bcolors.ENDC}") diff --git a/bin/nightly_build.py b/bin/nightly_build.py index b675fceca..00360e80d 100755 --- a/bin/nightly_build.py +++ b/bin/nightly_build.py @@ -68,7 +68,7 @@ In summary, this Python script facilitates the automation of nightly regression import os import shutil -from datetime import datetime, timedelta +from datetime import datetime import time import re import markdown @@ -78,9 +78,6 @@ import logging from pathlib import Path - - - class FolderManager: """A class for managing folders and repository cloning.""" @@ -115,9 +112,6 @@ class FolderManager: """ for folder in folders: - folder_path = os.path.join(self.base_parent_dir, folder) - # if not os.path.exists(folder_path): - # os.makedirs(folder_path) if not os.path.exists(folder): os.makedirs(folder) @@ -171,7 +165,6 @@ class FolderManager: Returns: None """ - todays_date = datetime.now().strftime("%Y-%m-%d") cvw = folder.joinpath("cvw") tmp_folder = os.path.join(cvw, "tmp") # temprorary files will be stored in here if not cvw.exists(): @@ -287,7 +280,7 @@ class TestRunner: if target: output_file = self.log_dir.joinpath(f"make-{target}-output.log") - else: output_file = self.log_dir.joinpath(f"make-output.log") + else: output_file = self.log_dir.joinpath("make-output.log") # Source setup script and execute make with target and cores/2 if target: @@ -398,7 +391,7 @@ class TestRunner: # Implement cleaning and formatting logic here # Open up the file with only read permissions - with open(input_file, 'r') as input_file: + with open(input_file) as input_file: uncleaned_output = input_file.read() # use something like this function to detect pass and fail @@ -461,7 +454,6 @@ class TestRunner: None """ # Implement markdown rewriting logic here - timestamp = datetime.now().strftime("%Y-%m-%d") # output_directory = self.base_parent_dir.joinpath("results") os.chdir(self.results_dir) @@ -470,7 +462,7 @@ class TestRunner: with open(output_file, 'w') as md_file: - + # Title md_file.write(f"\n\n# Regression Test Results - {self.todays_date}\n\n") #md_file.write(f"\n\n
\n# Regression Test Results - {timestamp}\n
\n\n") @@ -481,15 +473,15 @@ class TestRunner: if failed_configs: md_file.write("## Failed Configurations\n\n") for config, log_file in failed_configs: - md_file.write(f"- {config} ({log_file})\n") + md_file.write(f'- {config} ({log_file})\n') md_file.write("\n") else: md_file.write("## Failed Configurations\n") - md_file.write(f"No Failures\n") + md_file.write("No Failures\n") md_file.write("\n## Passed Configurations\n") for config in passed_configs: - md_file.write(f"- {config}\n") + md_file.write(f'- {config}\n') self.logger.info("writing test outputs to markdown") @@ -534,7 +526,7 @@ class TestRunner: md_file.write("\n") except subprocess.CalledProcessError as e: # Handle if the command fails - md_file.write(f"Failed to identify host and Operating System information: {str(e)}") + md_file.write(f"Failed to identify host and Operating System information: {e!s}") # Which tests did we run md_file.write(f"\n**Tests made:** `make {test_type}`\n") @@ -545,18 +537,18 @@ class TestRunner: md_file.write(f"**Total Failures: {total_number_failures}**\n") # Failed Tests - md_file.write(f"\n\n## Failed Tests") + md_file.write("\n\n## Failed Tests") md_file.write(f"\n**Total failed tests: {total_number_failures}**") for (test_item, item) in zip(test_list, failed_tests): md_file.write(f"\n\n### {test_item[1]} test") md_file.write(f"\n**Command used:** {test_item[0]} {test_item[1]} {' '.join(test_item[2])}\n\n") - md_file.write(f"**Failed Tests:**\n") + md_file.write("**Failed Tests:**\n") if len(item) == 0: md_file.write("\n") - md_file.write(f"* No failures\n") + md_file.write('* No failures\n') md_file.write("\n") else: for failed_test in item: @@ -564,29 +556,29 @@ class TestRunner: log_file = failed_test[1] md_file.write("\n") - md_file.write(f"* {config} ({log_file})\n") + md_file.write(f'* {config} ({log_file})\n') md_file.write("\n") # Successful Tests - md_file.write(f"\n\n## Successful Tests") + md_file.write("\n\n## Successful Tests") md_file.write(f"\n**Total successful tests: {total_number_success}**") for (test_item, item) in zip(test_list, passed_tests): md_file.write(f"\n\n### {test_item[1]} test") md_file.write(f"\n**Command used:** {test_item[0]} {test_item[1]} {' '.join(test_item[2])}\n\n") - md_file.write(f"\n**Successful Tests:**\n") + md_file.write("\n**Successful Tests:**\n") if len(item) == 0: md_file.write("\n") - md_file.write(f"* No successes\n") + md_file.write('* No successes\n') md_file.write("\n") else: for passed_tests in item: config = passed_tests md_file.write("\n") - md_file.write(f"* {config}\n") + md_file.write(f'* {config}\n') md_file.write("\n") self.logger.info("Combining markdown files") @@ -606,7 +598,7 @@ class TestRunner: # Implement markdown to HTML conversion logic here os.chdir(self.results_dir) - with open(markdown_file, 'r') as md_file: + with open(markdown_file) as md_file: md_content = md_file.read() html_content = markdown.markdown(md_content) @@ -614,7 +606,7 @@ class TestRunner: html_file.write(html_content) self.logger.info("Converting markdown file to html file.") - + def send_email(self, receiver_emails=None, subject="Nightly Regression Test"): """ Send email with HTML content. @@ -640,7 +632,7 @@ class TestRunner: os.chdir(self.results_dir) html_file = "results.html" - with open(html_file, 'r') as html_file: + with open(html_file) as html_file: body = html_file.read() try: @@ -688,13 +680,10 @@ def main(): # file paths for where the results and repos will be saved: repos and results can be changed to whatever today = datetime.now().strftime("%Y-%m-%d") - yesterday_dt = datetime.now() - timedelta(days=1) - yesterday = yesterday_dt.strftime("%Y-%m-%d") cvw_path = Path.home().joinpath(args.path, today) results_path = Path.home().joinpath(args.path, today, "results") log_path = Path.home().joinpath(args.path, today, "logs") log_file_path = log_path.joinpath("nightly_build.log") - previous_cvw_path = Path.home().joinpath(args.path,f"{yesterday}/cvw") # creates the object folder_manager = FolderManager(basedir=args.path) @@ -765,12 +754,6 @@ def main(): if args.target != "no": test_runner.execute_makefile(target = args.target, makefile_path=test_runner.cvw) - # TODO: remove vestigial code if no longer wanted - # if args.target == "all": - # # Compile Linux for local testing - # test_runner.set_env_var("RISCV",str(test_runner.cvw)) - # linux_path = test_runner.cvw / "linux" - # test_runner.execute_makefile(target = "all", makefile_path=linux_path) ############################################# # RUN TESTS # @@ -817,7 +800,7 @@ def main(): logger.info(f"The total failures for all tests ran are: {total_number_failures}") # Copy actual test logs from sim/questa, sim/verilator, sim/vcs - if not args.tests == "test_lint": + if args.tests != 'test_lint': test_runner.copy_sim_logs([test_runner.cvw / "sim/questa/logs", test_runner.cvw / "sim/verilator/logs", test_runner.cvw / "sim/vcs/logs"]) ############################################# diff --git a/bin/parseHPMC.py b/bin/parseHPMC.py index c0137916f..c34671ac4 100755 --- a/bin/parseHPMC.py +++ b/bin/parseHPMC.py @@ -46,14 +46,14 @@ def ParseBranchListFile(path): is formated in row columns. Each row is a trace with the file, branch predictor type, and the parameters. parameters can be any number and depend on the predictor type. Returns a list of lists.''' lst = [] - BranchList = open(path, 'r') - for line in BranchList: - tokens = line.split() - predictorLog = os.path.dirname(path) + '/' + tokens[0] - predictorType = tokens[1] - predictorParams = tokens[2::] - lst.append([predictorLog, predictorType, predictorParams]) - #print(predictorLog, predictorType, predictorParams) + with open(path) as BranchList: + for line in BranchList: + tokens = line.split() + predictorLog = os.path.dirname(path) + '/' + tokens[0] + predictorType = tokens[1] + predictorParams = tokens[2::] + lst.append([predictorLog, predictorType, predictorParams]) + #print(predictorLog, predictorType, predictorParams) return lst def ProcessFile(fileName): @@ -62,22 +62,22 @@ def ProcessFile(fileName): # 1 find lines with Read memfile and extract test name # 2 parse counters into a list of (name, value) tuples (dictionary maybe?) benchmarks = [] - transcript = open(fileName, 'r') HPMClist = { } testName = '' - for line in transcript.readlines(): - lineToken = line.split() - if(len(lineToken) > 3 and lineToken[1] == 'Read' and lineToken[2] == 'memfile'): - opt = lineToken[3].split('/')[-4] - testName = lineToken[3].split('/')[-1].split('.')[0] - HPMClist = { } - elif(len(lineToken) > 4 and lineToken[1][0:3] == 'Cnt'): - countToken = line.split('=')[1].split() - value = int(countToken[0]) if countToken[0] != 'x' else 0 - name = ' '.join(countToken[1:]) - HPMClist[name] = value - elif ('is done' in line): - benchmarks.append((testName, opt, HPMClist)) + with open(fileName) as transcript: + for line in transcript.readlines(): + lineToken = line.split() + if(len(lineToken) > 3 and lineToken[1] == 'Read' and lineToken[2] == 'memfile'): + opt = lineToken[3].split('/')[-4] + testName = lineToken[3].split('/')[-1].split('.')[0] + HPMClist = { } + elif(len(lineToken) > 4 and lineToken[1][0:3] == 'Cnt'): + countToken = line.split('=')[1].split() + value = int(countToken[0]) if countToken[0] != 'x' else 0 + name = ' '.join(countToken[1:]) + HPMClist[name] = value + elif ('is done' in line): + benchmarks.append((testName, opt, HPMClist)) return benchmarks @@ -227,13 +227,13 @@ def ReportAsTable(benchmarkDict): sys.stdout.write('benchmark\t\t') for name in FirstLine: - if(len(name) < 8): sys.stdout.write('%s\t\t' % name) - else: sys.stdout.write('%s\t' % name) + if(len(name) < 8): sys.stdout.write(f'{name}\t\t') + else: sys.stdout.write(f'{name}\t') sys.stdout.write('\n') sys.stdout.write('size\t\t\t') for size in SecondLine: - if(len(str(size)) < 8): sys.stdout.write('%d\t\t' % size) - else: sys.stdout.write('%d\t' % size) + if(len(str(size)) < 8): sys.stdout.write(f'{size}\t\t') + else: sys.stdout.write(f'{size}\t') sys.stdout.write('\n') if(args.summary): @@ -245,9 +245,9 @@ def ReportAsTable(benchmarkDict): if(not args.summary): for benchmark in benchmarkDict: length = len(benchmark) - if(length < 8): sys.stdout.write('%s\t\t\t' % benchmark) - elif(length < 16): sys.stdout.write('%s\t\t' % benchmark) - else: sys.stdout.write('%s\t' % benchmark) + if(length < 8): sys.stdout.write(f'{benchmark}\t\t\t') + elif(length < 16): sys.stdout.write(f'{benchmark}\t\t') + else: sys.stdout.write(f'{benchmark}\t') for (name, typ, entries, size, val) in benchmarkDict[benchmark]: sys.stdout.write('%0.2f\t\t' % (val if not args.invert else 100 -val)) sys.stdout.write('\n') @@ -256,14 +256,14 @@ def ReportAsText(benchmarkDict): if(args.summary): mean = benchmarkDict['Mean'] print('Mean') - for (name, typ, entries. size, val) in mean: - sys.stdout.write('%s %s %0.2f\n' % (name, entries if not args.size else size, val if not args.invert else 100 - val)) + for (name, typ, entries, size, val) in mean: + sys.stdout.write(f'{name} {entries if not args.size else size} {val if not args.invert else 100 - val:0.2f}\n') if(not args.summary): for benchmark in benchmarkDict: print(benchmark) for (name, type, entries, size, val) in benchmarkDict[benchmark]: - sys.stdout.write('%s %s %0.2f\n' % (name, entries if not args.size else size, val if not args.invert else 100 - val)) + sys.stdout.write(f'{name} {entries if not args.size else size} {val if not args.invert else 100 - val:0.2f}\n') def Inversion(lst): return [x if not args.invert else 100 - x for x in lst] @@ -275,7 +275,7 @@ def BarGraph(seriesDict, xlabelList, BenchPerRow, FileName, IncludeLegend): # the space between groups is 1 EffectiveNumInGroup = NumberInGroup + 2 barWidth = 1 / EffectiveNumInGroup - fig = plt.subplots(figsize = (EffectiveNumInGroup*BenchPerRow/8, 4)) + _ = plt.subplots(figsize = (EffectiveNumInGroup*BenchPerRow/8, 4)) colors = ['blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'black', 'black', 'black', 'black', 'black', 'black'] for name in seriesDict: values = seriesDict[name] @@ -322,14 +322,13 @@ def ReportAsGraph(benchmarkDict, bar, FileName): if(args.summary): markers = ['x', '.', '+', '*', '^', 'o', ',', 's'] colors = ['blue', 'black', 'gray', 'dodgerblue', 'lightsteelblue', 'turquoise', 'black', 'blue'] - temp = benchmarkDict['Mean'] # the benchmarkDict['Mean'] contains sequencies of results for multiple # branch predictors with various parameterizations # group the parameterizations by the common typ. sequencies = {} for (name, typ, entries, size, value) in benchmarkDict['Mean']: - if not typ in sequencies: + if typ not in sequencies: sequencies[typ] = [(entries if not args.size else int(size/8), value)] else: sequencies[typ].append((entries if not args.size else int(size/8) ,value)) @@ -354,7 +353,7 @@ def ReportAsGraph(benchmarkDict, bar, FileName): axes.set_xticks(xdata) axes.set_xticklabels(xdata) axes.grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5) - if(FileName == None): plt.show() + if FileName is None: plt.show() else: plt.savefig(FileName) # if(not args.summary): @@ -378,10 +377,10 @@ def ReportAsGraph(benchmarkDict, bar, FileName): if(not args.summary): NumBenchmarks = len(benchmarkDict) - NumBenchmarksSqrt = math.sqrt(NumBenchmarks) - isSquare = math.isclose(NumBenchmarksSqrt, round(NumBenchmarksSqrt)) - numCol = math.floor(NumBenchmarksSqrt) - numRow = numCol + (0 if isSquare else 1) + # NumBenchmarksSqrt = math.sqrt(NumBenchmarks) + # isSquare = math.isclose(NumBenchmarksSqrt, round(NumBenchmarksSqrt)) + # numCol = math.floor(NumBenchmarksSqrt) + # numRow = numCol + (0 if isSquare else 1) index = 1 BenchPerRow = 5 @@ -414,7 +413,7 @@ def ReportAsGraph(benchmarkDict, bar, FileName): # on each piece. for row in range(0, math.ceil(NumBenchmarks / BenchPerRow)): (xlabelListTrunk, seriesDictTrunk) = SelectPartition(xlabelListBig, seriesDictBig, row, BenchPerRow) - FileName = 'barSegment%d.svg' % row + FileName = f'barSegment{row}.svg' groupLen = len(xlabelListTrunk) BarGraph(seriesDictTrunk, xlabelListTrunk, groupLen, FileName, (row == 0)) diff --git a/bin/regression-wally b/bin/regression-wally index 185f03826..4ec41bf19 100755 --- a/bin/regression-wally +++ b/bin/regression-wally @@ -4,6 +4,7 @@ # regression-wally # David_Harris@Hmc.edu 25 January 2021 # Modified by Jarred Allen and many others +# jcarlin@hmc.edu December 2024 # SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 # # Run a regression with multiple configurations in parallel and exit with @@ -11,65 +12,66 @@ # output. # ################################## -import sys,os,shutil +import sys +import shutil +import os import argparse import multiprocessing from collections import namedtuple -from multiprocessing import Pool, TimeoutError +from multiprocessing import Pool, TimeoutError as MPTimeoutError + +# Globals +WALLY = os.environ.get('WALLY') +regressionDir = f'{WALLY}/sim' +archVerifDir = f'{WALLY}/addins/cvw-arch-verif' +coveragesim = "questa" # Questa is required for code/functional coverage +defaultsim = "verilator" # Default simulator for all other tests +lockstepsim = "questa" +testfloatsim = "questa" # change to Verilator when Issue #707 about testfloat not running Verilator is resolved + ################################## # Define lists of configurations and tests to run on each configuration ################################## # The tests are a list with one element for each configuration -# The element consists of the configuration name, a list of test suites to run, +# The element consists of the configuration name, a list of test suites to run, # optionally a string to pass to the simulator, and optionally a nonstandard grep string to check for success -tests = [ +standard_tests = [ ["rv32e", ["arch32e"]], ["rv32i", ["arch32i"]], ["rv32imc", ["arch32i", "arch32c", "arch32m", "wally32periph"]], - ["rv32gc", ["arch32f", "arch32d", "arch32f_fma", "arch32d_fma", "arch32f_divsqrt", "arch32d_divsqrt", - "arch32i", "arch32priv", "arch32c", "arch32m", "arch32a_amo", "arch32zifencei", "arch32zicond", - "arch32zba", "arch32zbb", "arch32zbc", "arch32zbs", "arch32zfh", "arch32zfh_fma", + ["rv32gc", ["arch32f", "arch32d", "arch32f_fma", "arch32d_fma", "arch32f_divsqrt", "arch32d_divsqrt", + "arch32i", "arch32priv", "arch32c", "arch32m", "arch32a_amo", "arch32zifencei", "arch32zicond", + "arch32zba", "arch32zbb", "arch32zbc", "arch32zbs", "arch32zfh", "arch32zfh_fma", "arch32zfh_divsqrt", "arch32zfaf", "arch32zfad", "wally32a_lrsc", "wally32priv", "wally32periph", "arch32zcb", "arch32zbkb", "arch32zbkc", "arch32zbkx", "arch32zknd", "arch32zkne", "arch32zknh", "arch32vm_sv32", "arch32pmp"]], - ["rv64i", ["arch64i"]] - ] + ["rv64i", ["arch64i"]], + ["rv64gc", ["arch64f", "arch64d", "arch64zfh", "arch64f_fma", "arch64d_fma", "arch64zfh_fma", "arch64f_divsqrt", + "arch64d_divsqrt", "arch64zfh_divsqrt", "arch64zfaf", "arch64zfad", "coverage64gc", "arch64i", "arch64priv", + "arch64c", "arch64m", "arch64zcb", "arch64zifencei", "arch64zicond", "arch64a_amo", "wally64a_lrsc", + "wally64periph", "wally64priv", "arch64zbkb", "arch64zbkc", "arch64zbkx", "arch64zknd", "arch64zkne", "arch64zknh", + "arch64zba", "arch64zbb", "arch64zbc", "arch64zbs", "arch64pmp"]], # add when working: "arch64zicboz" + ] # Separate test for short buildroot run through OpenSBI UART output tests_buildrootshort = [ - ["buildroot", ["buildroot"], [f"+INSTR_LIMIT=1400000"], # Instruction limit gets to first OpenSBI UART output + ["buildroot", ["buildroot"], "--args +INSTR_LIMIT=1400000", # Instruction limit gets to first OpenSBI UART output "OpenSBI v", "buildroot_uart.out"] ] # Separate test for full buildroot run tests_buildrootboot = [ - ["buildroot", ["buildroot"], [f"+INSTR_LIMIT=600000000"], # boot entire buildroot Linux to login prompt + ["buildroot", ["buildroot"], "--args +INSTR_LIMIT=600000000", # boot entire buildroot Linux to login prompt "WallyHostname login: ", "buildroot_uart.out"] ] tests_buildrootbootlockstep = [ - ["buildroot", ["buildroot"], [f"+INSTR_LIMIT=600000000 --lockstep"], # boot entire buildroot Linux to login prompt + ["buildroot", ["buildroot"], "--args +INSTR_LIMIT=600000000 --lockstep", # boot entire buildroot Linux to login prompt "WallyHostname login: ", "buildroot_uart.out"] ] - -# Separate out floating-point tests for RV64 to speed up coverage -tests64gc_nofp = [ - ["rv64gc", ["coverage64gc", "arch64i", "arch64priv", "arch64c", "arch64m", "arch64zcb", - "arch64zifencei", "arch64zicond", "arch64a_amo", "wally64a_lrsc", "wally64periph", "wally64priv", - "arch64zbkb", "arch64zbkc", "arch64zbkx", "arch64zknd", "arch64zkne", "arch64zknh", - "arch64zba", "arch64zbb", "arch64zbc", "arch64zbs", "arch64pmp"]] # add when working: "arch64zicboz" - ] - -tests64gc_fp = [ - ["rv64gc", ["arch64f", "arch64d", "arch64zfh", - "arch64f_fma", "arch64d_fma", "arch64zfh_fma", - "arch64f_divsqrt", "arch64d_divsqrt", "arch64zfh_divsqrt", - "arch64zfaf", "arch64zfad"]] - ] - derivconfigtests = [ # memory system ["tlb2_rv32gc", ["wally32priv"]], @@ -92,21 +94,21 @@ derivconfigtests = [ ["ram_1_1_rv64gc", ["ahb64"]], ["ram_2_0_rv64gc", ["ahb64"]], ["ram_2_1_rv64gc", ["ahb64"]], -# RV32 cacheless designs will not work unless DTIM supports FLEN > XLEN. This support is not planned. -# ["nodcache_rv32gc", ["ahb32"]], -# ["nocache_rv32gc", ["ahb32"]], + # RV32 cacheless designs will not work unless DTIM supports FLEN > XLEN. This support is not planned. + # ["nodcache_rv32gc", ["ahb32"]], + # ["nocache_rv32gc", ["ahb32"]], ["noicache_rv32gc", ["ahb32"]], ["noicache_rv64gc", ["ahb64"]], ["nodcache_rv64gc", ["ahb64"]], ["nocache_rv64gc", ["ahb64"]], -# Atomic variants + # Atomic variants ["zaamo_rv64gc", ["arch64i", "arch64a_amo"]], ["zalrsc_rv64gc", ["arch64i", "wally64a_lrsc"]], ["zaamo_rv32gc", ["arch32i", "arch32a_amo"]], ["zalrsc_rv32gc", ["arch32i", "wally32a_lrsc"]], -# Bit manipulation and crypto variants + # Bit manipulation and crypto variants ["zba_rv32gc", ["arch32i", "arch32zba"]], ["zbb_rv32gc", ["arch32i", "arch32zbb"]], ["zbc_rv32gc", ["arch32i", "arch32zbc"]], @@ -129,7 +131,7 @@ derivconfigtests = [ ["zknd_rv64gc", ["arch64i", "arch64zknd"]], ["zknh_rv64gc", ["arch64i", "arch64zknh"]], -# No privilege modes variants + # No privilege modes variants ["noS_rv32gc", ["arch32i", "arch32f", "arch32priv", "arch32c", "arch32m", "arch32a_amo", "arch32zifencei", "arch32zicond", "arch32zba", "arch32zfaf", "arch32zfad", "wally32a_lrsc", "arch32zcb", "arch32zbkx", "arch32zknd"]], ["noS_rv64gc", ["arch64i", "arch64f", "arch64priv", "arch64c", "arch64m", "arch64a_amo", "arch64zifencei", "arch64zicond", @@ -165,7 +167,7 @@ derivconfigtests = [ ["div_4_2_rv64gc", ["arch64f_divsqrt", "arch64d_divsqrt", "arch64m"]], ["div_4_2i_rv64gc", ["arch64f_divsqrt", "arch64d_divsqrt", "arch64m"]], ["div_4_4_rv64gc", ["arch64f_divsqrt", "arch64d_divsqrt", "arch64m"]], - ["div_4_4i_rv64gc", ["arch64f_divsqrt", "arch64d_divsqrt", "arch64m"]], + ["div_4_4i_rv64gc", ["arch64f_divsqrt", "arch64d_divsqrt", "arch64m"]], # fpu permutations ["f_rv32gc", ["arch32f", "arch32f_divsqrt", "arch32f_fma", "arch32zfaf"]], @@ -174,26 +176,25 @@ derivconfigtests = [ ["fdq_rv32gc", ["arch32f", "arch32f_divsqrt", "arch32f_fma", "arch32d", "arch32d_divsqrt", "arch32d_fma", "arch32i", "arch32zfaf", "arch32zfad"]], ["fdqh_rv32gc", ["arch32f", "arch32f_divsqrt", "arch32f_fma", "arch32d", "arch32d_divsqrt", "arch32d_fma", "arch32zfh", "arch32zfh_divsqrt", "arch32i", "arch32zfaf", "arch32zfad"]], ["f_rv64gc", ["arch64f", "arch64f_divsqrt", "arch64f_fma", "arch64zfaf"]], - ["fh_rv64gc", ["arch64f", "arch64f_divsqrt", "arch64f_fma", "arch64zfh", "arch64zfh_divsqrt", "arch64zfaf"]], + ["fh_rv64gc", ["arch64f", "arch64f_divsqrt", "arch64f_fma", "arch64zfh", "arch64zfh_divsqrt", "arch64zfaf"]], ["fdh_rv64gc", ["arch64f", "arch64f_divsqrt", "arch64f_fma", "arch64d", "arch64d_divsqrt", "arch64d_fma", "arch64zfh", "arch64zfh_divsqrt", "arch64zfaf", "arch64zfad"]], ["fdq_rv64gc", ["arch64f", "arch64f_divsqrt", "arch64f_fma", "arch64d", "arch64d_divsqrt", "arch64d_fma", "arch64i", "arch64zfaf", "arch64zfad"]], ["fdqh_rv64gc", ["arch64f", "arch64f_divsqrt", "arch64f_fma", "arch64d", "arch64d_divsqrt", "arch64d_fma", "arch64zfh", "arch64zfh_divsqrt", "arch64i", "arch64zfaf", "arch64zfad"]], # "wally64q" when Q is supported again in riscof config file ] bpredtests = [ - ["nobpred_rv32gc", ["rv32i"]], ["bpred_TWOBIT_6_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], ["bpred_TWOBIT_8_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], - ["bpred_TWOBIT_10_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], + ["bpred_TWOBIT_10_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], ["bpred_TWOBIT_12_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], - ["bpred_TWOBIT_14_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], + ["bpred_TWOBIT_14_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], ["bpred_TWOBIT_16_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], ["bpred_TWOBIT_6_16_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"], ["bpred_TWOBIT_8_16_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"], - ["bpred_TWOBIT_10_16_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"], + ["bpred_TWOBIT_10_16_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"], ["bpred_TWOBIT_12_16_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"], - ["bpred_TWOBIT_14_16_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"], + ["bpred_TWOBIT_14_16_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"], ["bpred_TWOBIT_16_16_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"], ["bpred_GSHARE_6_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], @@ -230,6 +231,33 @@ bpredtests = [ ["bpred_GSHARE_10_10_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"] ] +testfloatdivconfigs = [ + "fdh_div_2_1_rv32gc", "fdh_div_2_1_rv64gc", "fdh_div_2_2_rv32gc", + "fdh_div_2_2_rv64gc", "fdh_div_2_4_rv32gc", "fdh_div_2_4_rv64gc", + "fdh_div_4_1_rv32gc", "fdh_div_4_1_rv64gc", "fdh_div_4_2_rv32gc", + "fdh_div_4_2_rv64gc", "fdh_div_4_4_rv32gc", "fdh_div_4_4_rv64gc", + "fd_div_2_1_rv32gc", "fd_div_2_1_rv64gc", "fd_div_2_2_rv32gc", + "fd_div_2_2_rv64gc", "fd_div_2_4_rv32gc", "fd_div_2_4_rv64gc", + "fd_div_4_1_rv32gc", "fd_div_4_1_rv64gc", "fd_div_4_2_rv32gc", + "fd_div_4_2_rv64gc", "fd_div_4_4_rv32gc", "fd_div_4_4_rv64gc", + "fdqh_div_2_1_rv32gc", "fdqh_div_2_1_rv64gc", "fdqh_div_2_2_rv32gc", + "fdqh_div_2_2_rv64gc", "fdqh_div_2_4_rv32gc", "fdqh_div_2_4_rv64gc", + "fdqh_div_4_1_rv32gc", "fdqh_div_4_1_rv64gc", "fdqh_div_4_2_rv32gc", + "fdqh_div_4_2_rv64gc", "fdqh_div_4_4_rv32gc", "fdqh_div_4_4_rv64gc", + "fdq_div_2_1_rv32gc", "fdq_div_2_1_rv64gc", "fdq_div_2_2_rv32gc", + "fdq_div_2_2_rv64gc", "fdq_div_2_4_rv32gc", "fdq_div_2_4_rv64gc", + "fdq_div_4_1_rv32gc", "fdq_div_4_1_rv64gc", "fdq_div_4_2_rv32gc", + "fdq_div_4_2_rv64gc", "fdq_div_4_4_rv32gc", "fdq_div_4_4_rv64gc", + "fh_div_2_1_rv32gc", "fh_div_2_1_rv64gc", "fh_div_2_2_rv32gc", + "fh_div_2_2_rv64gc", "fh_div_2_4_rv32gc", "fh_div_2_4_rv64gc", + "fh_div_4_1_rv32gc", "fh_div_4_1_rv64gc", "fh_div_4_2_rv32gc", + "fh_div_4_2_rv64gc", "fh_div_4_4_rv32gc", "fh_div_4_4_rv64gc", + "f_div_2_1_rv32gc", "f_div_2_1_rv64gc", "f_div_2_2_rv32gc", + "f_div_2_2_rv64gc", "f_div_2_4_rv32gc", "f_div_2_4_rv64gc", + "f_div_4_1_rv32gc", "f_div_4_1_rv64gc", "f_div_4_2_rv32gc", + "f_div_4_2_rv64gc", "f_div_4_4_rv32gc", "f_div_4_4_rv64gc" +] + # list of tests not supported by ImperasDV yet that should be waived during lockstep testing lockstepwaivers = [ "WALLY-q-01.S_ref.elf", # Q extension is not supported by ImperasDV @@ -261,109 +289,76 @@ class bcolors: BOLD = '\033[1m' UNDERLINE = '\033[4m' -def addTests(tests, sim): - sim_logdir = WALLY+ "/sim/" + sim + "/logs/" - for test in tests: + +def addTests(testList, sim, coverStr, configs): + sim_logdir = f"{regressionDir}/{sim}/logs/" + for test in testList: config = test[0] suites = test[1] - if (len(test) >= 3): - args = " --args " + " ".join(test[2]) - else: - args = "" - if (len(test) >= 4): - gs = test[3] - else: - gs = "All tests ran without failures" - cmdPrefix="wsim --sim " + sim + " " + coverStr + " " + config + flags = f"{test[2]}" if len(test) >= 3 else "" + gs = test[3] if len(test) >= 4 else "All tests ran without failures" + cmdPrefix=f"wsim --sim {sim} {coverStr} {flags} {config}" for t in suites: - sim_log = sim_logdir + config + "_" + t + ".log" - if (len(test) >= 5): - grepfile = sim_logdir + test[4] - else: - grepfile = sim_log + sim_log = f"{sim_logdir}{config}_{t}.log" + grepfile = sim_logdir + test[4] if len(test) >= 5 else sim_log tc = TestCase( name=t, variant=config, - cmd=cmdPrefix + " " + t + args + " > " + sim_log, + cmd=f"{cmdPrefix} {t} > {sim_log}", grepstr=gs, grepfile = grepfile) configs.append(tc) -def addTestsByDir(dir, config, sim, lockstepMode=0, brekerMode=0): - if os.path.isdir(dir): - sim_logdir = WALLY+ "/sim/" + sim + "/logs/" - if coverStr == "--fcov": # use --fcov in place of --lockstep - cmdPrefix="wsim --sim " + sim + " " + coverStr + " " + config - gs="Mismatches : 0" - if ("cvw-arch-verif/tests" in dir and not "priv" in dir): - fileEnd = "ALL.elf" - else: - fileEnd = ".elf" - elif coverStr == "--ccov": - cmdPrefix="wsim --sim " + sim + " " + coverStr + " " + config - gs="Single Elf file tests are not signatured verified." - if ("cvw-arch-verif/tests" in dir and not "priv" in dir): - fileEnd = "ALL.elf" - else: - fileEnd = ".elf" - elif lockstepMode: - cmdPrefix="wsim --lockstep --sim " + sim + " " + config - gs="Mismatches : 0" - fileEnd = ".elf" - elif brekerMode: - cmdPrefix="wsim --sim " + sim + " " + config - gs="# trek: info: summary: Test PASSED" - fileEnd = ".elf" - else: - cmdPrefix="wsim --sim " + sim + " " + config - gs="Single Elf file tests are not signatured verified." - fileEnd = ".elf" - for dirpath, dirnames, filenames in os.walk(os.path.abspath(dir)): - for file in filenames: - # fcov lockstep only runs on WALLY-COV-ALL.elf files; other lockstep runs on all files - if file.endswith(fileEnd): - fullfile = os.path.join(dirpath, file) - fields = fullfile.rsplit('/', 3) - if (fields[2] == "ref"): - shortelf = fields[1] + "_" + fields[3] - else: - shortelf = fields[2] + "_" + fields[3] - if (shortelf in lockstepwaivers): # skip tests that itch bugs in ImperasDV - print(f"{bcolors.WARNING}Skipping waived test {shortelf}{bcolors.ENDC}") - continue - sim_log = sim_logdir + config + "_" + shortelf + ".log" - grepstring = "" - tc = TestCase( - name=file, - variant=config, - cmd=cmdPrefix + " " + fullfile + " > " + sim_log, - grepstr=gs, - grepfile = sim_log) - configs.append(tc) +def addTestsByDir(testDir, config, sim, coverStr, configs, lockstepMode=0, brekerMode=0): + if not os.path.isdir(testDir): + print(f"Error: Directory not found: {testDir}") + sys.exit(1) + + sim_logdir = f"{regressionDir}/{sim}/logs/" + cmdPrefix = f"wsim --sim {sim} {coverStr} {'--lockstep' if lockstepMode else ''} {config}" + # fcov/ccov only runs on WALLY-COV-ALL.elf files; other lockstep runs on all files + fileEnd = "ALL.elf" if "cvw-arch-verif/tests" in testDir and "priv" not in testDir and (coverStr == "--fcov" or coverStr == "--ccov") else ".elf" + if lockstepMode or coverStr == "--fcov": + gs = "Mismatches : 0" + elif brekerMode: + gs="# trek: info: summary: Test PASSED" else: - print("Error: Directory not found: " + dir) - exit(1) + gs = "Single Elf file tests are not signatured verified." + for dirpath, _, filenames in os.walk(os.path.abspath(testDir)): + for file in filenames: + if file.endswith(fileEnd): + fullfile = os.path.join(dirpath, file) + fields = fullfile.rsplit('/', 3) + if fields[2] == "ref": + shortelf = f"{fields[1]}_{fields[3]}" + else: + shortelf = f"{fields[2]}_{fields[3]}" + if shortelf in lockstepwaivers: # skip tests that itch bugs in ImperasDV + print(f"{bcolors.WARNING}Skipping waived test {shortelf}{bcolors.ENDC}") + continue + sim_log = f"{sim_logdir}{config}_{shortelf}.log" + tc = TestCase( + name=file, + variant=config, + cmd=f"{cmdPrefix} {fullfile} > {sim_log}", + grepstr=gs, + grepfile = sim_log) + configs.append(tc) def search_log_for_text(text, grepfile): - """Search through the given log file for text, returning True if it is found or False if it is not""" - grepwarn = "grep -i -H Warning: " + grepfile - os.system(grepwarn) - greperr = "grep -i -H Error: " + grepfile - os.system(greperr) - grepcmd = "grep -a -e '%s' '%s' > /dev/null" % (text, grepfile) -# print(" search_log_for_text invoking %s" % grepcmd) - return os.system(grepcmd) == 0 + with open(grepfile, errors="ignore") as file: + content = file.readlines() + for line in content: + if "warning:" in line.lower(): + print(f"{bcolors.WARNING}{line.strip()}{bcolors.ENDC}") + if "error:" in line.lower(): + print(f"{bcolors.FAIL}{line.strip()}{bcolors.ENDC}") + return any(text in line for line in content) def run_test_case(config, dryrun: bool = False): - """ - Run the given test case, and return 0 if the test suceeds and 1 if it fails - - Do not execute commands if dryrun - """ grepfile = config.grepfile cmd = config.cmd - os.chdir(regressionDir) if dryrun: print(f"Executing {cmd}", flush=True) return 0 @@ -371,194 +366,130 @@ def run_test_case(config, dryrun: bool = False): os.system(cmd) if search_log_for_text(config.grepstr, grepfile): # Flush is needed to flush output to stdout when running in multiprocessing Pool - # print(f"{bcolors.OKGREEN}%s_%s: Success{bcolors.ENDC}" % (config.variant, config.name), flush=True) - print(f"{bcolors.OKGREEN}%s: Success{bcolors.ENDC}" % (config.cmd), flush=True) + print(f"{bcolors.OKGREEN}{cmd}: Success{bcolors.ENDC}", flush=True) return 0 else: - print(f"{bcolors.FAIL}%s: Failures detected in output{bcolors.ENDC}" % (config.cmd), flush=True) - print(" Check %s" % grepfile) + print(f"{bcolors.FAIL}{cmd}: Failures detected in output{bcolors.ENDC}", flush=True) + print(f" Check {grepfile}", flush=True) return 1 -################################## -# Main body -################################## + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--ccov", help="Code Coverage", action="store_true") + parser.add_argument("--fcov", help="Functional Coverage", action="store_true") + parser.add_argument("--nightly", help="Run large nightly regression", action="store_true") + parser.add_argument("--buildroot", help="Include Buildroot Linux boot test (takes many hours, done along with --nightly)", action="store_true") + parser.add_argument("--testfloat", help="Include Testfloat floating-point unit tests", action="store_true") + parser.add_argument("--fp", help="Include floating-point tests in coverage (slower runtime)", action="store_true") # Currently not used + parser.add_argument("--breker", help="Run Breker tests", action="store_true") # Requires a license for the breker tool. See tests/breker/README.md for details + parser.add_argument("--dryrun", help="Print commands invoked to console without running regression", action="store_true") + return parser.parse_args() -WALLY = os.environ.get('WALLY') -regressionDir = WALLY + '/sim' -os.chdir(regressionDir) - -coveragesim = "questa" # Questa is required for code/functional coverage -#defaultsim = "questa" # Default simulator for all other tests; change to Verilator when flow is ready -defaultsim = "verilator" # Default simulator for all other tests -lockstepsim = "questa" - -parser = argparse.ArgumentParser() -parser.add_argument("--ccov", help="Code Coverage", action="store_true") -parser.add_argument("--fcov", help="Functional Coverage", action="store_true") -parser.add_argument("--nightly", help="Run large nightly regression", action="store_true") -parser.add_argument("--buildroot", help="Include Buildroot Linux boot test (takes many hours, done along with --nightly)", action="store_true") -parser.add_argument("--testfloat", help="Include Testfloat floating-point unit tests", action="store_true") -parser.add_argument("--fp", help="Include floating-point tests in coverage (slower runtime)", action="store_true") # Currently not used -parser.add_argument("--breker", help="Run Breker tests", action="store_true") # Requires a license for the breker tool. See tests/breker/README.md for details -parser.add_argument("--dryrun", help="Print commands invoked to console without running regression", action="store_true") -args = parser.parse_args() - -if (args.nightly): - nightMode = "--nightly" - sims = ["questa", "verilator", "vcs"] # exercise all simulators; can omit a sim if no license is available -else: - nightMode = "" - sims = [defaultsim] - -if (args.ccov): # only run RV64GC tests in coverage mode - coverStr = '--ccov' -elif (args.fcov): # only run RV64GC tests in lockstep in coverage mode - coverStr = '--fcov' -else: - coverStr = '' - - -# Run Lint -configs = [ - TestCase( - name="lints", - variant="all", - cmd="lint-wally " + nightMode + " | tee " + WALLY + "/sim/verilator/logs/all_lints.log", - grepstr="lints run with no errors or warnings", - grepfile = WALLY + "/sim/verilator/logs/all_lints.log") - ] - - - -# run full buildroot boot simulation (slow) if buildroot flag is set. Start it early to overlap with other tests -if (args.buildroot): - # addTests(tests_buildrootboot, defaultsim) # non-lockstep with Verilator runs in about 2 hours - addTests(tests_buildrootbootlockstep, lockstepsim) # lockstep with Questa and ImperasDV runs overnight - -if (args.ccov): # only run RV64GC tests on Questa in code coverage mode - addTestsByDir(WALLY+"/addins/cvw-arch-verif/tests/lockstep/rv64/", "rv64gc", coveragesim) - addTestsByDir(WALLY+"/addins/cvw-arch-verif/tests/lockstep/priv/rv64/", "rv64gc", coveragesim) - addTestsByDir(WALLY+"/tests/coverage/", "rv64gc", coveragesim) -elif (args.fcov): # run tests in lockstep in functional coverage mode - addTestsByDir(WALLY+"/addins/cvw-arch-verif/tests/lockstep/rv32/", "rv32gc", coveragesim) - addTestsByDir(WALLY+"/addins/cvw-arch-verif/tests/lockstep/rv64/", "rv64gc", coveragesim) - addTestsByDir(WALLY+"/addins/cvw-arch-verif/tests/lockstep/priv/rv32/", "rv32gc", coveragesim) - addTestsByDir(WALLY+"/addins/cvw-arch-verif/tests/lockstep/priv/rv64/", "rv64gc", coveragesim) -elif (args.breker): - addTestsByDir(WALLY+"/tests/breker/work", "breker", "questa", brekerMode=1) -else: - for sim in sims: - if (not (args.buildroot and sim == lockstepsim)): # skip short buildroot sim if running long one - addTests(tests_buildrootshort, sim) - addTests(tests, sim) - addTests(tests64gc_nofp, sim) - addTests(tests64gc_fp, sim) - -# run derivative configurations and lockstep tests in nightly regression -if (args.nightly): - addTestsByDir(WALLY+"/tests/coverage", "rv64gc", lockstepsim, lockstepMode=1) - addTestsByDir(WALLY+"/tests/riscof/work/wally-riscv-arch-test/rv64i_m", "rv64gc", lockstepsim, lockstepMode=1) - addTestsByDir(WALLY+"/tests/riscof/work/wally-riscv-arch-test/rv32i_m", "rv32gc", lockstepsim, lockstepMode=1) - addTests(derivconfigtests, defaultsim) - # addTests(bpredtests, defaultsim) # This is currently broken in regression due to something related to the new wsim script. - -# testfloat tests -if (args.testfloat): # for testfloat alone, just run testfloat tests - configs = [] -if (args.testfloat or args.nightly): # for nightly, run testfloat along with others - testfloatsim = "questa" # change to Verilator when Issue #707 about testfloat not running Verilator is resolved - testfloatconfigs = ["fdqh_rv64gc", "fdq_rv64gc", "fdh_rv64gc", "fd_rv64gc", "fh_rv64gc", "f_rv64gc", "fdqh_rv32gc", "f_rv32gc"] - for config in testfloatconfigs: - tests = ["div", "sqrt", "add", "sub", "mul", "cvtint", "cvtfp", "fma", "cmp"] - if ("f_" in config): - tests.remove("cvtfp") - for test in tests: - sim_log = WALLY + "/sim/" + testfloatsim + "/logs/"+config+"_"+test+".log" - tc = TestCase( - name=test, - variant=config, - cmd="wsim --tb testbench_fp --sim " + testfloatsim + " " + config + " " + test + " > " + sim_log, - grepstr="All Tests completed with 0 errors", - grepfile = sim_log) - configs.append(tc) - - - testfloatdivconfigs = [ - "fdh_div_2_1_rv32gc", "fdh_div_2_1_rv64gc", "fdh_div_2_2_rv32gc", - "fdh_div_2_2_rv64gc", "fdh_div_2_4_rv32gc", "fdh_div_2_4_rv64gc", - "fdh_div_4_1_rv32gc", "fdh_div_4_1_rv64gc", "fdh_div_4_2_rv32gc", - "fdh_div_4_2_rv64gc", "fdh_div_4_4_rv32gc", "fdh_div_4_4_rv64gc", - "fd_div_2_1_rv32gc", "fd_div_2_1_rv64gc", "fd_div_2_2_rv32gc", - "fd_div_2_2_rv64gc", "fd_div_2_4_rv32gc", "fd_div_2_4_rv64gc", - "fd_div_4_1_rv32gc", "fd_div_4_1_rv64gc", "fd_div_4_2_rv32gc", - "fd_div_4_2_rv64gc", "fd_div_4_4_rv32gc", "fd_div_4_4_rv64gc", - "fdqh_div_2_1_rv32gc", "fdqh_div_2_1_rv64gc", "fdqh_div_2_2_rv32gc", - "fdqh_div_2_2_rv64gc", "fdqh_div_2_4_rv32gc", "fdqh_div_2_4_rv64gc", - "fdqh_div_4_1_rv32gc", "fdqh_div_4_1_rv64gc", "fdqh_div_4_2_rv32gc", - "fdqh_div_4_2_rv64gc", "fdqh_div_4_4_rv32gc", "fdqh_div_4_4_rv64gc", - "fdq_div_2_1_rv32gc", "fdq_div_2_1_rv64gc", "fdq_div_2_2_rv32gc", - "fdq_div_2_2_rv64gc", "fdq_div_2_4_rv32gc", "fdq_div_2_4_rv64gc", - "fdq_div_4_1_rv32gc", "fdq_div_4_1_rv64gc", "fdq_div_4_2_rv32gc", - "fdq_div_4_2_rv64gc", "fdq_div_4_4_rv32gc", "fdq_div_4_4_rv64gc", - "fh_div_2_1_rv32gc", "fh_div_2_1_rv64gc", "fh_div_2_2_rv32gc", - "fh_div_2_2_rv64gc", "fh_div_2_4_rv32gc", "fh_div_2_4_rv64gc", - "fh_div_4_1_rv32gc", "fh_div_4_1_rv64gc", "fh_div_4_2_rv32gc", - "fh_div_4_2_rv64gc", "fh_div_4_4_rv32gc", "fh_div_4_4_rv64gc", - "f_div_2_1_rv32gc", "f_div_2_1_rv64gc", "f_div_2_2_rv32gc", - "f_div_2_2_rv64gc", "f_div_2_4_rv32gc", "f_div_2_4_rv64gc", - "f_div_4_1_rv32gc", "f_div_4_1_rv64gc", "f_div_4_2_rv32gc", - "f_div_4_2_rv64gc", "f_div_4_4_rv32gc", "f_div_4_4_rv64gc" - ] - for config in testfloatdivconfigs: - # div test case - tests = ["div", "sqrt", "cvtint", "cvtfp"] - if ("f_" in config): - tests.remove("cvtfp") - for test in tests: - sim_log = WALLY + "/sim/questa/logs/"+config+"_"+test+".log" - tc = TestCase( - name=test, - variant=config, - cmd="wsim --tb testbench_fp " + config + " " + test + " > " + sim_log, - grepstr="All Tests completed with 0 errors", - grepfile = WALLY + "/sim/questa/logs/"+config+"_"+test+".log") - configs.append(tc) - - -def main(): - """Run the tests and count the failures""" - global configs, args - os.chdir(regressionDir) - dirs = ["questa/logs", "questa/wkdir", "verilator/logs", "verilator/wkdir", "vcs/logs", "vcs/wkdir"] - for d in dirs: - try: - os.system('rm -rf %s' % d) - os.mkdir(d) - except: - pass +def process_args(args): + coverStr = "" + # exercise all simulators in nightly; can omit a sim if no license is available + sims = ["questa", "verilator", "vcs"] if args.nightly else [defaultsim] if args.ccov: + coverStr = "--ccov" TIMEOUT_DUR = 20*60 # seconds - os.system('rm -f questa/ucdb/* questa/cov/*') + for d in ["ucdb", "cov"]: + shutil.rmtree(f"{regressionDir}/questa/{d}", ignore_errors=True) + os.makedirs(f"{regressionDir}/questa/{d}", exist_ok=True) elif args.fcov: + coverStr = "--fcov" TIMEOUT_DUR = 8*60 - os.system('rm -f questa/fcov_ucdb/* questa/fcov_logs/* questa/fcov/*') + shutil.rmtree(f"{regressionDir}/questa/fcov_ucdb", ignore_errors=True) + os.makedirs(f"{regressionDir}/questa/fcov_ucdb", exist_ok=True) elif args.buildroot: TIMEOUT_DUR = 60*1440 # 1 day - elif args.testfloat: - TIMEOUT_DUR = 30*60 # seconds - elif args.nightly: + elif args.testfloat or args.nightly: TIMEOUT_DUR = 30*60 # seconds else: TIMEOUT_DUR = 10*60 # seconds + return sims, coverStr, TIMEOUT_DUR + + +def selectTests(args, sims, coverStr): + # Run Lint + configs = [ + TestCase( + name="lints", + variant="all", + cmd=f"lint-wally {'--nightly' if args.nightly else ''} | tee {regressionDir}/verilator/logs/all_lints.log", + grepstr="lints run with no errors or warnings", + grepfile = f"{regressionDir}/verilator/logs/all_lints.log") + ] + + # run full buildroot boot simulation (slow) if buildroot flag is set. Start it early to overlap with other tests + if args.buildroot: + # addTests(tests_buildrootboot, defaultsim) # non-lockstep with Verilator runs in about 2 hours + addTests(tests_buildrootbootlockstep, lockstepsim, coverStr, configs) # lockstep with Questa and ImperasDV runs overnight + + if args.ccov: # only run RV64GC tests on Questa in code coverage mode + addTestsByDir(f"{archVerifDir}/tests/lockstep/rv64/", "rv64gc", coveragesim, coverStr, configs) + addTestsByDir(f"{archVerifDir}/tests/lockstep/priv/rv64/", "rv64gc", coveragesim, coverStr, configs) + addTestsByDir(WALLY+"/tests/coverage/", "rv64gc", coveragesim, coverStr, configs) + elif args.fcov: # run tests in lockstep in functional coverage mode + addTestsByDir(f"{archVerifDir}/tests/lockstep/rv32/", "rv32gc", coveragesim, coverStr, configs) + addTestsByDir(f"{archVerifDir}/tests/lockstep/rv64/", "rv64gc", coveragesim, coverStr, configs) + addTestsByDir(f"{archVerifDir}/tests/lockstep/priv/rv32/", "rv32gc", coveragesim, coverStr, configs) + addTestsByDir(f"{archVerifDir}/tests/lockstep/priv/rv64/", "rv64gc", coveragesim, coverStr, configs) + elif args.breker: + addTestsByDir(WALLY+"/tests/breker/work", "breker", "questa", coverStr, configs, brekerMode=1) + elif not args.testfloat: + for sim in sims: + if not (args.buildroot and sim == lockstepsim): # skip short buildroot sim if running long one + addTests(tests_buildrootshort, sim, coverStr, configs) + addTests(standard_tests, sim, coverStr, configs) + + # run derivative configurations and lockstep tests in nightly regression + if args.nightly: + addTestsByDir(WALLY+"/tests/coverage", "rv64gc", lockstepsim, coverStr, configs, lockstepMode=1) + addTestsByDir(WALLY+"/tests/riscof/work/wally-riscv-arch-test/rv64i_m", "rv64gc", lockstepsim, coverStr, configs, lockstepMode=1) + addTestsByDir(WALLY+"/tests/riscof/work/wally-riscv-arch-test/rv32i_m", "rv32gc", lockstepsim, coverStr, configs, lockstepMode=1) + addTests(derivconfigtests, defaultsim, coverStr, configs) + # addTests(bpredtests, defaultsim) # This is currently broken in regression due to something related to the new wsim script. + + # testfloat tests + if (args.testfloat or args.nightly): # for nightly, run testfloat along with others + testfloatconfigs = ["fdqh_rv64gc", "fdq_rv64gc", "fdh_rv64gc", "fd_rv64gc", "fh_rv64gc", "f_rv64gc", "fdqh_rv32gc", "f_rv32gc"] + for config in testfloatconfigs + testfloatdivconfigs: + if config in testfloatconfigs: + tests = ["div", "sqrt", "add", "sub", "mul", "cvtint", "cvtfp", "fma", "cmp"] + else: + tests = ["div", "sqrt", "cvtint", "cvtfp"] + if "f_" in config: + tests.remove("cvtfp") + for test in tests: + sim_log = f"{regressionDir}/{testfloatsim}/logs/{config}_{test}.log" + tc = TestCase( + name=test, + variant=config, + cmd=f"wsim --tb testbench_fp --sim {testfloatsim} {config} {test} > {sim_log}", + grepstr="All Tests completed with 0 errors", + grepfile = sim_log) + configs.append(tc) + return configs + + +def makeDirs(sims): + for sim in sims: + dirs = [f"{regressionDir}/{sim}/wkdir", f"{regressionDir}/{sim}/logs"] + for d in dirs: + shutil.rmtree(d) + os.makedirs(d, exist_ok=True) + + +def main(args): + sims, coverStr, TIMEOUT_DUR = process_args(args) + configs = selectTests(args, sims, coverStr) # Scale the number of concurrent processes to the number of test cases, but # max out at a limited number of concurrent processes to not overwhelm the system - # right now fcov, ccov, nightly all use Imperas - if (args.ccov or args.fcov or args.nightly): - ImperasDVLicenseCount = 16 # limit number of concurrent processes to avoid overloading ImperasDV licenses - else: - ImperasDVLicenseCount = 10000 # effectively no license limit for non-lockstep tests + # right now fcov and nightly use Imperas + ImperasDVLicenseCount = 16 if args.fcov or args.nightly else 10000 with Pool(processes=min(len(configs),multiprocessing.cpu_count(), ImperasDVLicenseCount)) as pool: num_fail = 0 results = {} @@ -567,22 +498,24 @@ def main(): for (config,result) in results.items(): try: num_fail+=result.get(timeout=TIMEOUT_DUR) - except TimeoutError: + except MPTimeoutError: pool.terminate() + pool.join() num_fail+=1 - print(f"{bcolors.FAIL}%s: Timeout - runtime exceeded %d seconds{bcolors.ENDC}" % (config.cmd, TIMEOUT_DUR)) + print(f"{bcolors.FAIL}{config.cmd}: Timeout - runtime exceeded {TIMEOUT_DUR} seconds{bcolors.ENDC}") # Coverage report if args.ccov: - os.system('make QuestaCodeCoverage') + os.system(f"make -C {regressionDir}/QuestaCodeCoverage") if args.fcov: - os.system('make -C '+WALLY+'/addins/cvw-arch-verif merge') + os.system(f"make -C {archVerifDir} merge") # Count the number of failures if num_fail: - print(f"{bcolors.FAIL}Regression failed with %s failed configurations{bcolors.ENDC}" % num_fail) + print(f"{bcolors.FAIL}Regression failed with {num_fail} failed configurations{bcolors.ENDC}") else: print(f"{bcolors.OKGREEN}SUCCESS! All tests ran without failures{bcolors.ENDC}") return num_fail if __name__ == '__main__': - exit(main()) + args = parse_args() + sys.exit(main(args)) diff --git a/bin/wsim b/bin/wsim index 843d4cfe6..96b857363 100755 --- a/bin/wsim +++ b/bin/wsim @@ -16,7 +16,7 @@ import os import sys # Global variable -WALLY = os.environ.get('WALLY') +WALLY = os.environ.get("WALLY") def parseArgs(): parser = argparse.ArgumentParser() @@ -41,13 +41,13 @@ def validateArgs(args): if not args.testsuite and not args.elf: print("Error: Missing test suite or ELF file") sys.exit(1) - if any([args.lockstep, args.lockstepverbose, args.fcov]) and not (args.testsuite.endswith('.elf') or args.elf) and args.testsuite != "buildroot": + if any([args.lockstep, args.lockstepverbose, args.fcov]) and not (args.testsuite.endswith(".elf") or args.elf) and args.testsuite != "buildroot": print(f"Invalid Options. Cannot run a testsuite, {args.testsuite} with lockstep or fcov. Must run a single elf or buildroot.") sys.exit(1) elif any([args.gui, args.ccov, args.fcov, args.lockstep, args.lockstepverbose]) and args.sim not in ["questa", "vcs"]: print("Option only supported for Questa and VCS") sys.exit(1) - elif (args.tb == "testbench_fp" and args.sim != "questa"): + elif args.tb == "testbench_fp" and args.sim != "questa": print("Error: testbench_fp presently only supported by Questa, not VCS or Verilator, because of a touchy testbench") sys.exit(1) elif (args.config == "breker" and args.sim != "questa"): @@ -61,11 +61,11 @@ def elfFileCheck(args): elif args.elf: print(f"ELF file not found: {args.elf}") sys.exit(1) - elif args.testsuite.endswith('.elf'): # No --elf argument; check if testsuite has a .elf extension and use that instead + elif args.testsuite.endswith(".elf"): # No --elf argument; check if testsuite has a .elf extension and use that instead if os.path.isfile(args.testsuite): ElfFile = os.path.abspath(args.testsuite) # extract the elf name from the path to be the test suite - fields = args.testsuite.rsplit('/', 3) + fields = args.testsuite.rsplit("/", 3) # if the name is just ref.elf in a deep path (riscv-arch-test/wally-riscv-arch-test), then use the directory name as the test suite to make it unique; otherwise work directory will have duplicates. if "breker" in args.testsuite: args.testsuite = fields[-1] @@ -74,8 +74,8 @@ def elfFileCheck(args): args.testsuite = f"{fields[1]}_{fields[3]}" else: args.testsuite = f"{fields[2]}_{fields[3]}" - elif '/' in args.testsuite: - args.testsuite=args.testsuite.rsplit('/', 1)[1] # strip off path if present + elif "/" in args.testsuite: + args.testsuite = args.testsuite.rsplit("/", 1)[1] # strip off path if present else: print(f"ELF file not found: {args.testsuite}") sys.exit(1) @@ -116,9 +116,9 @@ def prepSim(args, ElfFile): defineList.append("+define+USE_TREK_DV") argsList.append(f"+TREK_TBX_FILE={ElfFileNoExtension}.tbx") # Combine into a single string - args.args += " ".join(argsList) - args.params += " ".join(paramsList) - args.define += " ".join(defineList) + args.args += " " + " ".join(argsList) + args.params += " " + " ".join(paramsList) + args.define += " " + " ".join(defineList) flags = " ".join(flagsList) return flags, prefix @@ -154,7 +154,7 @@ def runQuesta(args, flags, prefix): args.params = fr'--params \"{args.params}\"' if args.define: args.define = fr'--define \"{args.define}\"' - # Questa cannot accept more than 9 arguments. fcov implies lockstep + # fcov implies lockstep cmd = f"do wally.do {args.config} {args.testsuite} {args.tb} {args.args} {args.params} {args.define} {flags}" cmd = f'cd $WALLY/sim/questa; {prefix} vsim {"-c" if not args.gui else ""} -do "{cmd}"' print(f"Running Questa with command: {cmd}") diff --git a/docs/docker/get_buildroot_testvector.py b/docs/docker/get_buildroot_testvector.py index f2feb0789..06923ccd7 100644 --- a/docs/docker/get_buildroot_testvector.py +++ b/docs/docker/get_buildroot_testvector.py @@ -1,4 +1,5 @@ -import shutil, os +import shutil +import os # if WALLY is defined, then get it WALLY_HOME = os.getenv("WALLY") @@ -10,4 +11,4 @@ BUILDROOT_SRC = "linux/buildroot-config-src/wally" TESTVECTOR_SRC = "linux/testvector-generation" shutil.copytree(os.path.join(WALLY_HOME, BUILDROOT_SRC), "./buildroot-config-src") -shutil.copytree(os.path.join(WALLY_HOME, TESTVECTOR_SRC), "./testvector-generation") \ No newline at end of file +shutil.copytree(os.path.join(WALLY_HOME, TESTVECTOR_SRC), "./testvector-generation") diff --git a/fpga/renumber.py b/fpga/renumber.py index 84a2543d7..50efa2f06 100755 --- a/fpga/renumber.py +++ b/fpga/renumber.py @@ -13,7 +13,7 @@ def main(args): probenum = 0 countLines = 1 - with open(args[0],'r') as xdcfile, open(args[1], 'w') as outfile: + with open(args[0]) as xdcfile, open(args[1], 'w') as outfile: Lines = xdcfile.readlines() for line in Lines: t = re.sub("probe[0-9]+", f"probe{probenum}",line) diff --git a/sim/questa/fpga-wave.do b/sim/questa/fpga-wave.do index e95443fbe..ebff54cd3 100644 --- a/sim/questa/fpga-wave.do +++ b/sim/questa/fpga-wave.do @@ -53,7 +53,7 @@ add wave -noupdate -group {Execution Stage} /testbench/dut/core/ifu/PCE add wave -noupdate -group {Execution Stage} /testbench/dut/core/ifu/InstrE add wave -noupdate -group {Execution Stage} /testbench/InstrEName add wave -noupdate -group {Execution Stage} /testbench/dut/core/ieu/c/InstrValidE -add wave -noupdate -group {Execution Stage} /testbench/FunctionName/FunctionName/FunctionName +add wave -noupdate -group {Execution Stage} /testbench/functionName/functionName/FunctionName add wave -noupdate -expand -group {Memory Stage} /testbench/dut/core/PCM add wave -noupdate -expand -group {Memory Stage} /testbench/dut/core/InstrM add wave -noupdate -expand -group {Memory Stage} /testbench/InstrMName diff --git a/sim/rv64gc_CacheSim.py b/sim/rv64gc_CacheSim.py index 9de361237..1151e6305 100755 --- a/sim/rv64gc_CacheSim.py +++ b/sim/rv64gc_CacheSim.py @@ -67,8 +67,8 @@ def main(): parser.add_argument('-d', "--dist", action='store_true', help="Report distribution of operations") parser.add_argument('-s', "--sim", help="Simulator", choices=["questa", "verilator", "vcs"], default="verilator") args = parser.parse_args() - simargs = "I_CACHE_ADDR_LOGGER=1\\\'b1 D_CACHE_ADDR_LOGGER=1\\\'b1" - testcmd = "wsim --sim " + args.sim + " rv64gc {} --params \"" + simargs + "\" > /dev/null" + simargs = "I_CACHE_ADDR_LOGGER=1\\'b1 D_CACHE_ADDR_LOGGER=1\\'b1" + testcmd = "wsim --sim " + args.sim + ' rv64gc {} --params "' + simargs + '" > /dev/null' #cachecmd = "CacheSim.py 64 4 56 44 -f {} --verbose" cachecmd = "CacheSim.py 64 4 56 44 -f {}" mismatches = 0 diff --git a/sim/slack-notifier/slack-notifier.py b/sim/slack-notifier/slack-notifier.py deleted file mode 100755 index ac824feec..000000000 --- a/sim/slack-notifier/slack-notifier.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -import os,sys,subprocess -from datetime import datetime, timezone, timedelta - -if not os.path.isfile(sys.path[0]+'/slack-webhook-url.txt'): - print('==============================================================') - print(' HOWDY! ') - print('slack-notifier.py can help let you know when your sim is done.') - print('To make it work, please supply your Slack bot webhook URL in:') - print(sys.path[0]+'/slack-webhook-url.txt') - print('Tutorial for slack webhook urls: https://bit.ly/BenSlackNotifier') - print('==============================================================') -else: - urlFile = open(sys.path[0]+'/slack-webhook-url.txt','r') - url = urlFile.readline().strip('\n') - - # Traverse 3 parents up the process tree - result = subprocess.check_output('ps -o ppid -p $PPID',shell=True) - PPID2 = str(result).split('\\n')[1] - result = subprocess.check_output('ps -o ppid -p '+PPID2,shell=True) - PPID3 = str(result).split('\\n')[1] - # Get command name - result = subprocess.check_output('ps -o cmd -p '+PPID3,shell=True) - cmdName = str(result).split('\\n')[1] - # Get current time - timezone_offset = -8.0 # Pacific Standard Time (UTC−08:00) - tzinfo = timezone(timedelta(hours=timezone_offset)) - time = datetime.now(tzinfo).strftime('%I:%M %p') - # Send message - message = 'Command `'+cmdName+'` completed at '+time+' PST' - result = subprocess.run('curl -X POST -H \'Content-type: application/json\' --data \'{"text":"'+message+'"}\' '+url,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL) - print('Simulation stopped. Sending Slack message.') diff --git a/sim/vcs/run_vcs b/sim/vcs/run_vcs index 7437d2725..94971bca1 100755 --- a/sim/vcs/run_vcs +++ b/sim/vcs/run_vcs @@ -12,7 +12,7 @@ import subprocess import sys # Global variables -WALLY = os.environ.get('WALLY') +WALLY = os.environ.get("WALLY") simdir = f"{WALLY}/sim/vcs" cfgdir = f"{WALLY}/config" srcdir = f"{WALLY}/src" @@ -21,10 +21,10 @@ logdir = f"{simdir}/logs" # run a Linux command and return the result as a string in a form that VCS can use def runFindCommand(cmd): - res = subprocess.check_output(cmd, shell=True, ) + res = subprocess.check_output(cmd, shell=True) res = str(res) res = res.replace("\\n", " ") # replace newline with space - res = res.replace("\'", "") # strip off quotation marks + res = res.replace("'", "") # strip off quotation marks res = res[1:] # strip off leading b from byte string return res @@ -42,20 +42,20 @@ def parseArgs(): #parser.add_argument("--gui", "-g", help="Simulate with GUI", action="store_true") # GUI not yet implemented return parser.parse_args() -def createDirs(args): - wkdir = f"{simdir}/wkdir/{args.config}_{args.testsuite}" - covdir = f"{simdir}/cov/{args.config}_{args.testsuite}" +def createDirs(config, testsuite): + wkdir = f"{simdir}/wkdir/{config}_{testsuite}" + covdir = f"{simdir}/cov/{config}_{testsuite}" os.makedirs(wkdir, exist_ok=True) os.makedirs(covdir, exist_ok=True) os.makedirs(logdir, exist_ok=True) - return wkdir, covdir + return wkdir -def generateFileList(): +def generateFileList(testbench): rtlsrc_cmd = f'find {srcdir} -name "*.sv" ! -path "{srcdir}/generic/mem/rom1p1r_128x64.sv" ! -path "{srcdir}/generic/mem/ram2p1r1wbe_128x64.sv" ! -path "{srcdir}/generic/mem/rom1p1r_128x32.sv" ! -path "{srcdir}/generic/mem/ram2p1r1wbe_2048x64.sv"' rtlsrc_files = runFindCommand(rtlsrc_cmd) tbcommon_cmd = f'find {tbdir}/common -name "*.sv"' tbcommon_files = runFindCommand(tbcommon_cmd) - tb_file = f'{tbdir}/{args.tb}.sv' + tb_file = f"{tbdir}/{testbench}.sv" return f"{tb_file} {rtlsrc_files} {tbcommon_files}" def processArgs(wkdir, args): @@ -76,7 +76,7 @@ def processArgs(wkdir, args): # if args.gui: # compileOptions.append("-debug_access+all+reverse -kdb +vcs+vcdpluson") compileOptions = " ".join(compileOptions) - simvOptions = " ".join(simvOptions) + simvOptions = " ".join(simvOptions) return compileOptions, simvOptions def setupParamOverrides(wkdir, args): @@ -84,35 +84,35 @@ def setupParamOverrides(wkdir, args): with open(paramOverrideFile, "w") as f: for param in args.params.split(): [param, value] = param.split("=") - if fr"\'" in value: # for bit values - value = value.replace(fr"\'", "'") - else: # for strings - value = f'"{value}"' + value = value.replace("\\'", "'") if "\\'" in value else f'"{value}"' # transform quotes/bit indicators f.write(f"assign {value} {args.tb}/{param}\n") return f" -parameters {wkdir}/param_overrides.txt " def setupCommands(wkdir, rtlFiles, compileOptions, simvOptions, args): - includePath=f"+incdir+{cfgdir}/{args.config} +incdir+{cfgdir}/deriv/{args.config} +incdir+{cfgdir}/shared +incdir+$WALLY/tests +incdir+{tbdir} +incdir+{srcdir}" + includePath = f"+incdir+{cfgdir}/{args.config} +incdir+{cfgdir}/deriv/{args.config} +incdir+{cfgdir}/shared +incdir+$WALLY/tests +incdir+{tbdir} +incdir+{srcdir}" vcsStandardFlags = "+lint=all,noGCWM,noUI,noSVA-UA,noIDTS,noNS,noULCO,noCAWM-L,noWMIA-L,noSV-PIU,noSTASKW_CO,noSTASKW_CO1,noSTASKW_RMCOF -suppress +warn -sverilog +vc -Mupdate -line -full64 -lca -ntb_opts sensitive_dyn" vcsCMD = f"vcs {vcsStandardFlags} -top {args.tb} {compileOptions} -Mdir={wkdir} {includePath} {srcdir}/cvw.sv {rtlFiles} -o {wkdir}/sim_out -work {wkdir} -Mlib={wkdir} -l {logdir}/{args.config}_{args.testsuite}.log" simvCMD = f"{wkdir}/sim_out +TEST={args.testsuite} {args.args} -no_save {simvOptions}" return vcsCMD, simvCMD -def runVCS(wkdir, vcsCMD, simvCMD): +def runVCS(vcsCMD, simvCMD): print(f"Executing: {vcsCMD}") - subprocess.run(vcsCMD, shell=True) - subprocess.run(simvCMD, shell=True) - if (args.ccov): - COV_RUN = f"urg -dir {wkdir}/coverage.vdb -format text -report IndividualCovReport/{args.config}_{args.testsuite}" - subprocess.run(COV_RUN, shell=True) + subprocess.run(vcsCMD, shell=True, check=True) + subprocess.run(simvCMD, shell=True, check=True) + +def runCoverage(wkdir, config, testsuite): + COV_RUN = f"urg -dir {wkdir}/coverage.vdb -format text -report IndividualCovReport/{config}_{testsuite}" + subprocess.run(COV_RUN, shell=True, check=True) def main(args): print(f"run_vcs Config={args.config} tests={args.testsuite} lockstep={args.lockstep} args='{args.args}' params='{args.params}' define='{args.define}'") - wkdir, covdir = createDirs(args) - rtlFiles = generateFileList() + wkdir = createDirs(args.config, args.testsuite) + rtlFiles = generateFileList(args.tb) compileOptions, simvOptions = processArgs(wkdir, args) vcsCMD, simvCMD = setupCommands(wkdir, rtlFiles, compileOptions, simvOptions, args) - runVCS(wkdir, vcsCMD, simvCMD) + runVCS(vcsCMD, simvCMD) + if args.ccov: + runCoverage(wkdir, args.config, args.testsuite) if __name__ == "__main__": args = parseArgs() diff --git a/synthDC/extractSummary.py b/synthDC/extractSummary.py index 826be54d8..9f2624391 100755 --- a/synthDC/extractSummary.py +++ b/synthDC/extractSummary.py @@ -27,50 +27,46 @@ def synthsintocsv(): specReg = re.compile('[a-zA-Z0-9]+') metricReg = re.compile('-?\d+\.\d+[e]?[-+]?\d*') - file = open("Summary.csv", "w") - writer = csv.writer(file) - writer.writerow(['Width', 'Config', 'Mod', 'Tech', 'Target Freq', 'Delay', 'Area']) + with open("Summary.csv", "w") as file: + writer = csv.writer(file) + writer.writerow(['Width', 'Config', 'Mod', 'Tech', 'Target Freq', 'Delay', 'Area']) + + for oneSynth in allSynths: + descrip = specReg.findall(oneSynth) + # print("From " + oneSynth + " Find ") + # for d in descrip: + # print(d) + base = 4 if descrip[3] == "sram" else 3 + width = descrip[base][:4] + config = descrip[base][4:] + if descrip[base+1][-2:] == 'nm': + mod = '' + else: + mod = descrip[base+1] + descrip = descrip[1:] + tech = descrip[base+1][:-2] + freq = descrip[base+2] + # print(width, config, mod, tech, freq) + metrics = [] + for phrase in ['Path Slack', 'Design Area']: + bashCommand = 'grep "{}" '+ oneSynth[2:]+'/reports/*qor*' + bashCommand = bashCommand.format(phrase) + # print(bashCommand) + try: + output = subprocess.check_output(['bash','-c', bashCommand]) + nums = metricReg.findall(str(output)) + nums = [float(m) for m in nums] + metrics += nums + except: + print(width + config + tech + '_' + freq + " doesn't have reports") + if metrics == []: + pass + else: + delay = 1000/int(freq) - metrics[0] + area = metrics[1] + writer.writerow([width, config, mod, tech, freq, delay, area]) - for oneSynth in allSynths: - descrip = specReg.findall(oneSynth) -# print("From " + oneSynth + " Find ") -# for d in descrip: -# print(d) - if (descrip[3] == "sram"): - base = 4 - else: - base = 3 - width = descrip[base][:4] - config = descrip[base][4:] - if descrip[base+1][-2:] == 'nm': - mod = '' - else: - mod = descrip[base+1] - descrip = descrip[1:] - tech = descrip[base+1][:-2] - freq = descrip[base+2] -# print(width, config, mod, tech, freq) - metrics = [] - for phrase in ['Path Slack', 'Design Area']: - bashCommand = 'grep "{}" '+ oneSynth[2:]+'/reports/*qor*' - bashCommand = bashCommand.format(phrase) -# print(bashCommand) - try: - output = subprocess.check_output(['bash','-c', bashCommand]) - nums = metricReg.findall(str(output)) - nums = [float(m) for m in nums] - metrics += nums - except: - print(width + config + tech + '_' + freq + " doesn't have reports") - if metrics == []: - pass - else: - delay = 1000/int(freq) - metrics[0] - area = metrics[1] - writer.writerow([width, config, mod, tech, freq, delay, area]) - file.close() - def synthsfromcsv(filename): Synth = namedtuple("Synth", "width config mod tech freq delay area") with open(filename, newline='') as csvfile: @@ -93,7 +89,7 @@ def freqPlot(tech, width, config): freqsL, delaysL, areasL = ([[], []] for i in range(3)) for oneSynth in allSynths: - if (width == oneSynth.width) & (config == oneSynth.config) & (tech == oneSynth.tech) & ('orig' == oneSynth.mod): + if (width == oneSynth.width) & (config == oneSynth.config) & (tech == oneSynth.tech) & (oneSynth.mod == 'orig'): ind = (1000/oneSynth.delay < (0.95*oneSynth.freq)) # when delay is within target clock period freqsL[ind] += [oneSynth.freq] delaysL[ind] += [oneSynth.delay] @@ -101,10 +97,7 @@ def freqPlot(tech, width, config): fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) allFreqs = list(flatten(freqsL)) - if allFreqs != []: - median = np.median(allFreqs) - else: - median = 0 + median = np.median(allFreqs) if allFreqs != [] else 0 for ind in [0,1]: areas = areasL[ind] @@ -169,11 +162,10 @@ def plotFeatures(tech, width, config): delays, areas, labels = ([] for i in range(3)) freq = techdict[tech].targfreq for oneSynth in allSynths: - if (tech == oneSynth.tech) & (freq == oneSynth.freq): - if (oneSynth.config == config) & (width == oneSynth.width): - delays += [oneSynth.delay] - areas += [oneSynth.area] - labels += [oneSynth.mod] + if (tech == oneSynth.tech) & (freq == oneSynth.freq) & (oneSynth.config == config) & (width == oneSynth.width): + delays += [oneSynth.delay] + areas += [oneSynth.area] + labels += [oneSynth.mod] if (delays == []): print("No delays found for tech ", tech, " freq ", freq, ". Did you set --sky130freq, --sky90freq and --tsmcfreq?\n") @@ -186,7 +178,7 @@ def plotFeatures(tech, width, config): plt.title(titlestr) plt.savefig(final_directory + '/features_'+titlestr+'.png') - + def plotConfigs(tech, mod=''): delays, areas, labels = ([] for i in range(3)) freq = techdict[tech].targfreq @@ -227,7 +219,7 @@ def normAreaDelay(mod=''): ax.legend(handles = fullLeg, loc='upper left') plt.savefig(final_directory + '/normAreaDelay.png') - + def addFO4axis(fig, ax, tech): fo4 = techdict[tech].fo4 @@ -282,4 +274,4 @@ if __name__ == '__main__': plotConfigs('sky130', mod='orig') plotConfigs('tsmc28psyn', mod='orig') normAreaDelay(mod='orig') - os.system("./extractArea.pl"); + os.system("./extractArea.pl") diff --git a/synthDC/ppa/ppaAnalyze.py b/synthDC/ppa/ppaAnalyze.py index a43de80bb..4baa2f8be 100755 --- a/synthDC/ppa/ppaAnalyze.py +++ b/synthDC/ppa/ppaAnalyze.py @@ -50,49 +50,48 @@ def synthsintocsv(): specReg = re.compile("[a-zA-Z0-9]+") metricReg = re.compile("-?\d+\.\d+[e]?[-+]?\d*") - file = open("ppaData.csv", "w") - writer = csv.writer(file) - writer.writerow( - [ - "Module", - "Tech", - "Width", - "Target Freq", - "Delay", - "Area", - "L Power (nW)", - "D energy (nJ)", - ] - ) + with open("ppaData.csv", "w") as file: + writer = csv.writer(file) + writer.writerow( + [ + "Module", + "Tech", + "Width", + "Target Freq", + "Delay", + "Area", + "L Power (nW)", + "D energy (nJ)", + ] + ) - for oneSynth in allSynths: - module, width, risc, tech, freq = specReg.findall(oneSynth)[1:6] - tech = tech[:-2] - metrics = [] - for phrase in [["Path Slack", "qor"], ["Design Area", "qor"], ["100", "power"]]: - bashCommand = 'grep "{}" ' + oneSynth[2:] + "/reports/*{}*" - bashCommand = bashCommand.format(*phrase) - try: - output = subprocess.check_output(["bash", "-c", bashCommand]) - except: - print(module + width + tech + freq + " doesn't have reports") - print("Consider running cleanup() first") - nums = metricReg.findall(str(output)) - nums = [float(m) for m in nums] - metrics += nums - delay = 1000 / int(freq) - metrics[0] - area = metrics[1] - lpower = metrics[4] - tpower = (metrics[2] + metrics[3] + metrics[4]*.000001) - denergy = ( - (tpower) / int(freq) * 1000 - ) # (switching + internal powers)*delay, more practical units for regression coefs + for oneSynth in allSynths: + module, width, risc, tech, freq = specReg.findall(oneSynth)[1:6] + tech = tech[:-2] + metrics = [] + for phrase in [["Path Slack", "qor"], ["Design Area", "qor"], ["100", "power"]]: + bashCommand = 'grep "{}" ' + oneSynth[2:] + "/reports/*{}*" + bashCommand = bashCommand.format(*phrase) + try: + output = subprocess.check_output(["bash", "-c", bashCommand]) + except: + print(module + width + tech + freq + " doesn't have reports") + print("Consider running cleanup() first") + nums = metricReg.findall(str(output)) + nums = [float(m) for m in nums] + metrics += nums + delay = 1000 / int(freq) - metrics[0] + area = metrics[1] + lpower = metrics[4] + tpower = (metrics[2] + metrics[3] + metrics[4]*.000001) + denergy = ( + (tpower) / int(freq) * 1000 + ) # (switching + internal powers)*delay, more practical units for regression coefs - if "flop" in module: # since two flops in each module - [area, lpower, denergy] = [n / 2 for n in [area, lpower, denergy]] + if "flop" in module: # since two flops in each module + [area, lpower, denergy] = [n / 2 for n in [area, lpower, denergy]] - writer.writerow([module, tech, width, freq, delay, area, lpower, denergy]) - file.close() + writer.writerow([module, tech, width, freq, delay, area, lpower, denergy]) def cleanup(): @@ -129,15 +128,12 @@ def getVals(tech, module, var, freq=None, width=None): works at a specified target frequency or if none is given, uses the synthesis with the best achievable delay for each width """ - if width != None: - widthsToGet = width - else: - widthsToGet = widths + widthsToGet = width if width is not None else widths metric = [] widthL = [] - if freq != None: + if freq is not None: for oneSynth in allSynths: if ( (oneSynth.freq == freq) @@ -171,37 +167,30 @@ def csvOfBest(filename): m = np.Inf # large number to start best = None for oneSynth in allSynths: # best achievable, rightmost green - if ( - (oneSynth.width == w) - & (oneSynth.tech == tech) - & (oneSynth.module == mod) - ): - if (oneSynth.delay < m) & ( - 1000 / oneSynth.delay > oneSynth.freq - ): + if (oneSynth.width == w) & (oneSynth.tech == tech) & (oneSynth.module == mod): + if (oneSynth.delay < m) & (1000 / oneSynth.delay > oneSynth.freq): m = oneSynth.delay best = oneSynth - if (best != None) & (best not in bestSynths): + if (best is not None) & (best not in bestSynths): bestSynths += [best] - file = open(filename, "w") - writer = csv.writer(file) - writer.writerow( - [ - "Module", - "Tech", - "Width", - "Target Freq", - "Delay", - "Area", - "L Power (nW)", - "D energy (nJ)", - ] - ) - for synth in bestSynths: - writer.writerow(list(synth)) - file.close() + with open(filename, "w") as file: + writer = csv.writer(file) + writer.writerow( + [ + "Module", + "Tech", + "Width", + "Target Freq", + "Delay", + "Area", + "L Power (nW)", + "D energy (nJ)", + ] + ) + for synth in bestSynths: + writer.writerow(list(synth)) return bestSynths @@ -229,7 +218,7 @@ def genLegend(fits, coefs, r2=None, spec=None, ale=False): eq = "" ind = 0 - for k in eqDict.keys(): + for k in eqDict: if k in fits: if str(coefsr[ind]) != "0": eq += " + " + coefsr[ind] + eqDict[k] @@ -237,7 +226,7 @@ def genLegend(fits, coefs, r2=None, spec=None, ale=False): eq = eq[3:] # chop off leading ' + ' - if (r2 == None) or (spec == None): + if (r2 is None) or (spec is None): return eq else: legend_elements = [lines.Line2D([0], [0], color=spec.color, label=eq)] @@ -277,10 +266,7 @@ def oneMetricPlot( modFit = fitDict[module] fits = modFit[ale] - if freq: - ls = "--" - else: - ls = "-" + ls = "--" if freq else "-" for spec in techSpecs: # print(f"Searching for module of spec {spec} and module {module} and var {var}") @@ -339,7 +325,7 @@ def oneMetricPlot( ax.add_artist(ax.legend(handles=fullLeg, loc=legLoc)) titleStr = ( " (target " + str(freq) + "MHz)" - if freq != None + if freq is not None else " (best achievable delay)" ) ax.set_title(module + titleStr) @@ -403,72 +389,16 @@ def makeCoefTable(): """writes CSV with each line containing the coefficients for a regression fit to a particular combination of module, metric (including both techs, normalized) """ - file = open("ppaFitting.csv", "w") - writer = csv.writer(file) - writer.writerow( - ["Module", "Metric", "Target", "1", "N", "N^2", "log2(N)", "Nlog2(N)", "R^2"] - ) + with open("ppaFitting.csv", "w") as file: + writer = csv.writer(file) + writer.writerow( + ["Module", "Metric", "Target", "1", "N", "N^2", "log2(N)", "Nlog2(N)", "R^2"] + ) - for module in modules: - for freq in [10, None]: - target = "easy" if freq else "hard" - for var in ["delay", "area", "lpower", "denergy"]: - ale = var != "delay" - metL = [] - modFit = fitDict[module] - fits = modFit[ale] - - for spec in techSpecs: - metric = getVals(spec.tech, module, var, freq=freq) - techdict = spec._asdict() - norm = techdict[var] - metL += [m / norm for m in metric] - - xp, pred, coefs, r2 = regress(widths * 2, metL, fits, ale) - coefs = np.ndarray.tolist(coefs) - coefsToWrite = [None] * 5 - fitTerms = "clsgn" - ind = 0 - for i in range(len(fitTerms)): - if fitTerms[i] in fits: - coefsToWrite[i] = coefs[ind] - ind += 1 - row = [module, var, target] + coefsToWrite + [r2] - writer.writerow(row) - - file.close() - - -def sigfig(num, figs): - return "{:g}".format(float("{:.{p}g}".format(num, p=figs))) - - -def makeEqTable(): - """writes CSV with each line containing the equations for fits for each metric - to a particular module (including both techs, normalized) - """ - file = open("ppaEquations.csv", "w") - writer = csv.writer(file) - writer.writerow( - [ - "Element", - "Best delay", - "Fast area", - "Fast leakage", - "Fast energy", - "Small area", - "Small leakage", - "Small energy", - ] - ) - - for module in modules: - eqs = [] - for freq in [None, 10]: - for var in ["delay", "area", "lpower", "denergy"]: - if (var == "delay") and (freq == 10): - pass - else: + for module in modules: + for freq in [10, None]: + target = "easy" if freq else "hard" + for var in ["delay", "area", "lpower", "denergy"]: ale = var != "delay" metL = [] modFit = fitDict[module] @@ -482,12 +412,63 @@ def makeEqTable(): xp, pred, coefs, r2 = regress(widths * 2, metL, fits, ale) coefs = np.ndarray.tolist(coefs) - eqs += [genLegend(fits, coefs, ale=ale)] - row = [module] + eqs - writer.writerow(row) + coefsToWrite = [None] * 5 + fitTerms = "clsgn" + ind = 0 + for i in range(len(fitTerms)): + if fitTerms[i] in fits: + coefsToWrite[i] = coefs[ind] + ind += 1 + row = [module, var, target] + coefsToWrite + [r2] + writer.writerow(row) - file.close() +def sigfig(num, figs): + return "{:g}".format(float("{:.{p}g}".format(num, p=figs))) + + +def makeEqTable(): + """writes CSV with each line containing the equations for fits for each metric + to a particular module (including both techs, normalized) + """ + with open("ppaEquations.csv", "w") as file: + writer = csv.writer(file) + writer.writerow( + [ + "Element", + "Best delay", + "Fast area", + "Fast leakage", + "Fast energy", + "Small area", + "Small leakage", + "Small energy", + ] + ) + + for module in modules: + eqs = [] + for freq in [None, 10]: + for var in ["delay", "area", "lpower", "denergy"]: + if (var == "delay") and (freq == 10): + pass + else: + ale = var != "delay" + metL = [] + modFit = fitDict[module] + fits = modFit[ale] + + for spec in techSpecs: + metric = getVals(spec.tech, module, var, freq=freq) + techdict = spec._asdict() + norm = techdict[var] + metL += [m / norm for m in metric] + + xp, pred, coefs, r2 = regress(widths * 2, metL, fits, ale) + coefs = np.ndarray.tolist(coefs) + eqs += [genLegend(fits, coefs, ale=ale)] + row = [module] + eqs + writer.writerow(row) def genFuncs(fits="clsgn"): """helper function for regress() @@ -719,7 +700,7 @@ def plotPPA(mod, freq=None, norm=True, aleOpt=False): else: axs[i, j].legend(handles=leg, handlelength=1.5) - titleStr = " (target " + str(freq) + "MHz)" if freq != None else "" + titleStr = f" (target {freq} MHz)" if freq is not None else "" plt.suptitle(mod + titleStr) plt.tight_layout(pad=0.05, w_pad=1, h_pad=0.5, rect=(0, 0, 1, 0.97)) @@ -819,10 +800,7 @@ def stdDevError(): norm = techdict[var] metL += [m / norm for m in metric] - if ale: - ws = [w / normAddWidth for w in widths] - else: - ws = widths + ws = [w / normAddWidth for w in widths] if ale else widths ws = ws * 2 mat = [] for w in ws: @@ -896,7 +874,7 @@ if __name__ == "__main__": "flop": ["c", "l", "l"], "binencoder": ["cg", "l", "l"], } - fitDict.update(dict.fromkeys(["mux2", "mux4", "mux8"], ["cg", "l", "l"])) + fitDict.update({key: ["cg", "l", "l"] for key in ["mux2", "mux4", "mux8"]}) TechSpec = namedtuple("TechSpec", "tech color shape delay area lpower denergy") # FO4 delay information information diff --git a/synthDC/ppa/ppaSynth.py b/synthDC/ppa/ppaSynth.py index 1947a5809..ddc2017fa 100755 --- a/synthDC/ppa/ppaSynth.py +++ b/synthDC/ppa/ppaSynth.py @@ -11,7 +11,7 @@ from multiprocessing import Pool from ppaAnalyze import synthsfromcsv def runCommand(module, width, tech, freq): - command = "make synth DESIGN={} WIDTH={} TECH={} DRIVE=INV FREQ={} MAXOPT=1 MAXCORES=1".format(module, width, tech, freq) + command = f"make synth DESIGN={module} WIDTH={width} TECH={tech} DRIVE=INV FREQ={freq} MAXOPT=1 MAXCORES=1" subprocess.call(command, shell=True) def deleteRedundant(synthsToRun): @@ -19,7 +19,7 @@ def deleteRedundant(synthsToRun): synthStr = "rm -rf runs/{}_{}_rv32e_{}_{}_*" for synth in synthsToRun: bashCommand = synthStr.format(*synth) - outputCPL = subprocess.check_output(['bash','-c', bashCommand]) + subprocess.check_output(['bash','-c', bashCommand]) def freqSweep(module, width, tech): synthsToRun = [] @@ -71,29 +71,29 @@ def allCombos(widths, modules, techs, freqs): if __name__ == '__main__': ##### Run specific syntheses for a specific frequency - widths = [8, 16, 32, 64, 128] - modules = ['mul', 'adder', 'shifter', 'flop', 'comparator', 'binencoder', 'csa', 'mux2', 'mux4', 'mux8'] - techs = ['sky90', 'sky130', 'tsmc28', 'tsmc28psyn'] - freqs = [5000] - synthsToRun = allCombos(widths, modules, techs, freqs) + widths = [8, 16, 32, 64, 128] + modules = ['mul', 'adder', 'shifter', 'flop', 'comparator', 'binencoder', 'csa', 'mux2', 'mux4', 'mux8'] + techs = ['sky90', 'sky130', 'tsmc28', 'tsmc28psyn'] + freqs = [5000] + synthsToRun = allCombos(widths, modules, techs, freqs) ##### Run a sweep based on best delay found in existing syntheses - module = 'adder' - width = 32 - tech = 'tsmc28psyn' - synthsToRun = freqSweep(module, width, tech) + module = 'adder' + width = 32 + tech = 'tsmc28psyn' + synthsToRun = freqSweep(module, width, tech) ##### Run a sweep for multiple modules/widths based on best delay found in existing syntheses - modules = ['adder'] + modules = ['adder'] # widths = [8, 16, 32, 64, 128] - widths = [32] - tech = 'sky130' - synthsToRun = freqModuleSweep(widths, modules, tech) + widths = [32] + tech = 'sky130' + synthsToRun = freqModuleSweep(widths, modules, tech) ##### Only do syntheses for which a run doesn't already exist - synthsToRun = filterRedundant(synthsToRun) - pool = Pool(processes=25) + synthsToRun = filterRedundant(synthsToRun) + pool = Pool(processes=25) pool.starmap(runCommand, synthsToRun) pool.close() -pool.join() \ No newline at end of file +pool.join() diff --git a/synthDC/scripts/wrapperGen.py b/synthDC/scripts/wrapperGen.py index f91052638..7cc406369 100755 --- a/synthDC/scripts/wrapperGen.py +++ b/synthDC/scripts/wrapperGen.py @@ -15,59 +15,52 @@ import os parser = argparse.ArgumentParser() parser.add_argument("DESIGN") -parser.add_argument("HDLPATH"); +parser.add_argument("HDLPATH") args=parser.parse_args() fin_path = glob.glob(f"{os.getenv('WALLY')}/src/**/{args.DESIGN}.sv",recursive=True)[0] -fin = open(fin_path, "r", encoding='utf-8') +with open(fin_path) as fin: + lines = fin.readlines() -lines = fin.readlines() + # keeps track of what line number the module header begins + lineModuleStart = 0 -# keeps track of what line number the module header begins -lineModuleStart = 0 + # keeps track of what line number the module header ends + lineModuleEnd = 0 -# keeps track of what line number the module header ends -lineModuleEnd = 0 + # keeps track of module name + moduleName = "" -# keeps track of module name -moduleName = "" + # string that will keep track of the running module header + buf = 'import cvw::*;\n`include "config.vh"\n`include "parameter-defs.vh"\n' -# string that will keep track of the running module header -buf = "import cvw::*;\n`include \"config.vh\"\n`include \"parameter-defs.vh\"\n" + # are we writing into the buffer + writeBuf=False -# are we writing into the buffer -writeBuf=False + index=0 -index=0 + # string copy logic + for l in lines: + if l.lstrip().find("module") == 0: + lineModuleStart = index + moduleName = l.split()[1] + writeBuf = True + buf += f"module {moduleName}wrapper (\n" + continue + if (writeBuf): + buf += l + if l.lstrip().find (");") == 0: + lineModuleEnd = index + break + index+=1 -# string copy logic -for l in lines: - if l.lstrip().find("module") == 0: - lineModuleStart = index - moduleName = l.split()[1] - writeBuf = True - buf += f"module {moduleName}wrapper (\n" - continue - if (writeBuf): - buf += l - if l.lstrip().find (");") == 0: - lineModuleEnd = index - break - index+=1 + # post-processing buffer: add DUT and endmodule lines + buf += f"\t{moduleName} #(P) dut(.*);\nendmodule" -# post-processing buffer: add DUT and endmodule lines -buf += f"\t{moduleName} #(P) dut(.*);\nendmodule" + # path to wrapper + wrapperPath = f"{args.HDLPATH}/{moduleName}wrapper.sv" -# path to wrapper -wrapperPath = f"{args.HDLPATH}/{moduleName}wrapper.sv" - -fout = open(wrapperPath, "w") - -fout.write(buf) - -fin.close() -fout.close() - -#print(buf) + with open(wrapperPath, "w") as fout: + fout.write(buf) diff --git a/synthDC/wallySynth.py b/synthDC/wallySynth.py index caf7e1697..350064a0d 100755 --- a/synthDC/wallySynth.py +++ b/synthDC/wallySynth.py @@ -7,12 +7,9 @@ import argparse def runSynth(config, mod, tech, freq, maxopt, usesram): global pool - if (usesram): - prefix = "syn_sram_" - else: - prefix = "syn_" + prefix = "syn_sram_" if usesram else "syn_" cfg = prefix + config - command = "make synth DESIGN=wallypipelinedcore CONFIG={} MOD={} TECH={} DRIVE=FLOP FREQ={} MAXOPT={} USESRAM={} MAXCORES=1".format(cfg, mod, tech, freq, maxopt, usesram) + command = f"make synth DESIGN=wallypipelinedcore CONFIG={cfg} MOD={mod} TECH={tech} DRIVE=FLOP FREQ={freq} MAXOPT={maxopt} USESRAM={usesram} MAXCORES=1" pool.map(mask, [command]) def mask(command): diff --git a/testbench/common/checksignature.sv b/testbench/common/checksignature.sv deleted file mode 100644 index 7fb589bb6..000000000 --- a/testbench/common/checksignature.sv +++ /dev/null @@ -1,26 +0,0 @@ -/////////////////////////////////////////// -// checksignature.sv -// -// Written: David Harris David_Harris@hmc.edu -// Modified: 14 June 2023 -// -// Purpose: Verifies the memory signature. -// -// A component of the Wally configurable RISC-V project. -// -// Copyright (C) 2021 Harvey Mudd College & Oklahoma State University -// -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at -// -// https://solderpad.org/licenses/SHL-2.1/ -// -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions -// and limitations under the License. -//////////////////////////////////////////////////////////////////////////////////////////////// - diff --git a/testbench/common/functionName.sv b/testbench/common/functionName.sv index 2d257ce32..141dcd88f 100644 --- a/testbench/common/functionName.sv +++ b/testbench/common/functionName.sv @@ -23,7 +23,7 @@ // and limitations under the License. //////////////////////////////////////////////////////////////////////////////////////////////// -module FunctionName import cvw::*; #(parameter cvw_t P) ( +module functionName import cvw::*; #(parameter cvw_t P) ( input logic reset, input logic clk, input string ProgramAddrMapFile, diff --git a/testbench/common/loggers.sv b/testbench/common/loggers.sv index 1a88bc052..5ed07b6d6 100644 --- a/testbench/common/loggers.sv +++ b/testbench/common/loggers.sv @@ -84,11 +84,11 @@ module loggers import cvw::*; #(parameter cvw_t P, always_comb if (TEST == "embench") begin - StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_trigger"; - EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_trigger"; + StartSampleFirst = functionName.functionName.FunctionName == "start_trigger"; + EndSampleFirst = functionName.functionName.FunctionName == "stop_trigger"; end else if (TEST == "coremark") begin - StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_time"; - EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_time"; + StartSampleFirst = functionName.functionName.FunctionName == "start_time"; + EndSampleFirst = functionName.functionName.FunctionName == "stop_time"; end else begin StartSampleFirst = reset; EndSampleFirst = '0; @@ -106,22 +106,22 @@ module loggers import cvw::*; #(parameter cvw_t P, if(TEST == "embench") begin // embench runs warmup then runs start_trigger // embench end with stop_trigger. - //assign StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_trigger"; + //assign StartSampleFirst = functionName.functionName.FunctionName == "start_trigger"; //flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed); //assign StartSample = StartSampleFirst & ~ StartSampleDelayed; - //assign EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_trigger"; + //assign EndSampleFirst = functionName.functionName.FunctionName == "stop_trigger"; flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed); assign EndSample = EndSampleFirst & ~ EndSampleDelayed; end else if(TEST == "coremark") begin // embench runs warmup then runs start_trigger // embench end with stop_trigger. - //assign StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_time"; + //assign StartSampleFirst = functionName.functionName.FunctionName == "start_time"; //flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed); //assign StartSample = StartSampleFirst & ~ StartSampleDelayed; - //assign EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_time"; + //assign EndSampleFirst = functionName.functionName.FunctionName == "stop_time"; flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed); assign EndSample = EndSampleFirst & ~ EndSampleDelayed; diff --git a/testbench/sdc/ram2sdLoad.py b/testbench/sdc/ram2sdLoad.py index ebfa37939..6a6be9886 100755 --- a/testbench/sdc/ram2sdLoad.py +++ b/testbench/sdc/ram2sdLoad.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 -import sys, fileinput +import sys +import fileinput address = 0 -for line in fileinput.input('-'): - # the 14- is to reverse the byte order to little endian - formatedLine = ' '.join(line[14-i:14-i+2] for i in range(0, len(line), 2)) - sys.stdout.write('@{:08x} {:s}\n'.format(address, formatedLine)) - address+=8 +with fileinput.input('-') as f: + for line in f: + # the 14- is to reverse the byte order to little endian + formatedLine = ' '.join(line[14-i:14-i+2] for i in range(0, len(line), 2)) + sys.stdout.write(f'@{address:08x} {formatedLine:s}\n') + address+=8 diff --git a/testbench/testbench.sv b/testbench/testbench.sv index eb1c88955..696a288f1 100644 --- a/testbench/testbench.sv +++ b/testbench/testbench.sv @@ -684,8 +684,8 @@ module testbench; loggers (clk, reset, DCacheFlushStart, DCacheFlushDone, memfilename, TEST); // track the current function or global label - if (DEBUG > 0 | ((PrintHPMCounters | BPRED_LOGGER) & P.ZICNTR_SUPPORTED)) begin : FunctionName - FunctionName #(P) FunctionName(.reset(reset_ext | TestBenchReset), + if (DEBUG > 0 | ((PrintHPMCounters | BPRED_LOGGER) & P.ZICNTR_SUPPORTED)) begin : functionName + functionName #(P) functionName(.reset(reset_ext | TestBenchReset), .clk(clk), .ProgramAddrMapFile(ProgramAddrMapFile), .ProgramLabelMapFile(ProgramLabelMapFile)); end @@ -710,11 +710,11 @@ module testbench; always @(posedge clk) begin // if (reset) PrevPCZero <= 0; - // else if (dut.core.InstrValidM) PrevPCZero <= (FunctionName.PCM == 0 & dut.core.ifu.InstrM == 0); + // else if (dut.core.InstrValidM) PrevPCZero <= (functionName.PCM == 0 & dut.core.ifu.InstrM == 0); TestComplete <= ((InstrM == 32'h6f) & dut.core.InstrValidM ) | ((dut.core.lsu.IEUAdrM == ProgramAddrLabelArray["tohost"] & dut.core.lsu.IEUAdrM != 0) & InstrMName == "SW"); // | - // (FunctionName.PCM == 0 & dut.core.ifu.InstrM == 0 & dut.core.InstrValidM & PrevPCZero)); - // if (FunctionName.PCM == 0 & dut.core.ifu.InstrM == 0 & dut.core.InstrValidM & PrevPCZero) + // (functionName.PCM == 0 & dut.core.ifu.InstrM == 0 & dut.core.InstrValidM & PrevPCZero)); + // if (functionName.PCM == 0 & dut.core.ifu.InstrM == 0 & dut.core.InstrValidM & PrevPCZero) // $error("Program fetched illegal instruction 0x00000000 from address 0x00000000 twice in a row. Usually due to fault with no fault handler."); end diff --git a/tests/coverage/dcache1.py b/tests/coverage/dcache1.py index d8740bdda..728d58ae9 100644 --- a/tests/coverage/dcache1.py +++ b/tests/coverage/dcache1.py @@ -40,9 +40,9 @@ mem_addr = mem_start_addr def wl(line="", comment=None, fname=test_name): with open(fname, "a") as f: - instr = False if (":" in line or - ".align" in line or - "# include" in line) else True + instr = not (":" in line or + ".align" in line or + "# include" in line) indent = 6 if instr else 0 comment = "// " + comment if comment is not None else "" to_write = " " * indent + line + comment + "\n" @@ -78,7 +78,7 @@ if __name__ == "__main__": for i in range(dcache_num_ways): wl(comment=f"start way test #{i+1}") wl(f'li t0, {hex(mem_addr)}') - wl(f'.align 6') # start at i$ set boundary. 6 lsb bits are zero. + wl('.align 6') # start at i$ set boundary. 6 lsb bits are zero. wl(comment=f"i$ boundary, way test #{i+1}") write_repro_instrs() mem_addr += dcache_way_size_in_bytes # so that we excercise a new D$ way. diff --git a/tests/fp/combined_IF_vectors/extract_arch_vectors.py b/tests/fp/combined_IF_vectors/extract_arch_vectors.py index ddb3e0c35..14caa796e 100755 --- a/tests/fp/combined_IF_vectors/extract_arch_vectors.py +++ b/tests/fp/combined_IF_vectors/extract_arch_vectors.py @@ -60,9 +60,9 @@ class Config: def create_vectors(my_config): suite_folder_num = my_config.bits if my_config.bits == 64 and my_config.letter == "F": suite_folder_num = 32 - source_dir1 = "{}/addins/riscv-arch-test/riscv-test-suite/rv{}i_m/{}/src/".format(wally, suite_folder_num, my_config.letter) - source_dir2 = "{}/tests/riscof/work/riscv-arch-test/rv{}i_m/{}/src/".format(wally, my_config.bits, my_config.letter) - dest_dir = "{}/tests/fp/combined_IF_vectors/IF_vectors/".format(wally) + source_dir1 = f"{wally}/addins/riscv-arch-test/riscv-test-suite/rv{suite_folder_num}i_m/{my_config.letter}/src/" + source_dir2 = f"{wally}/tests/riscof/work/riscv-arch-test/rv{my_config.bits}i_m/{my_config.letter}/src/" + dest_dir = f"{wally}/tests/fp/combined_IF_vectors/IF_vectors/" all_vectors1 = os.listdir(source_dir1) filt_vectors1 = [v for v in all_vectors1 if my_config.filt in v] @@ -76,218 +76,212 @@ def create_vectors(my_config): operation = my_config.op_code rounding_mode = "X" flags = "XX" - # use name to create our new tv - dest_file = open("{}cvw_{}_{}.tv".format(dest_dir, my_config.bits, vector1[:-2]), 'w') - # open vectors - src_file1 = open(source_dir1 + vector1,'r') - src_file2 = open(source_dir2 + vector2,'r') - # for each test in the vector - reading = True - src_file2.readline() #skip first bc junk - # print(my_config.bits, my_config.letter) - if my_config.letter == "F" and my_config.bits == 64: + # use name to create our new tv and open vectors + with open(f"{dest_dir}cvw_{my_config.bits}_{vector1[:-2]}.tv", 'w') as dest_file, open(source_dir1 + vector1) as src_file1, open(source_dir2 + vector2) as src_file2: + # for each test in the vector reading = True - # print("trigger 64F") - #skip first 2 lines bc junk - src_file2.readline() - while reading: - # get answer and flags from Ref...signature - # answers are before deadbeef (first line of 4) - # flags are after deadbeef (third line of 4) - answer = src_file2.readline().strip() - deadbeef = src_file2.readline().strip() - # print(answer) - if not (answer == "e7d4b281" and deadbeef == "6f5ca309"): # if there is still stuff to read - # get flags - packed = src_file2.readline().strip()[6:] - flags, rounding_mode = unpack_rf(packed) - # skip 00000000 buffer - src_file2.readline() - - # parse through .S file - detected = False - done = False - op1val = "0" - op2val = "0" - while not (detected or done): - # print("det1") - line = src_file1.readline() - # print(line) - if "op1val" in line: - # print("det2") - # parse line - - # handle special case where destination register is hardwired to zero - if "dest:x0" in line: - answer = "x" * len(answer) - - op1val = line.split("op1val")[1].split("x")[1].split(";")[0] - if my_config.op != "fsqrt": # sqrt doesn't have two input vals - op2val = line.split("op2val")[1].split("x")[1].strip() - if op2val[-1] == ";": op2val = op2val[:-1] # remove ; if it's there - else: - op2val = 32*"X" - # go to next test in vector - detected = True - elif "RVTEST_CODE_END" in line: - done = True - # put it all together - if not done: - translation = "{}_{}_{}_{}_{}_{}".format(operation, ext_bits(op1val), ext_bits(op2val), ext_bits(answer.strip()), flags, rounding_mode) - dest_file.write(translation + "\n") - else: - # print("read false") - reading = False - elif my_config.letter == "M" and my_config.bits == 64: - reading = True - #skip first 2 lines bc junk - src_file2.readline() - while reading: - # print("trigger 64M") - # get answer from Ref...signature - # answers span two lines and are reversed - answer2 = src_file2.readline().strip() - answer1 = src_file2.readline().strip() - answer = answer1 + answer2 - #print(answer1,answer2) - if not (answer2 == "e7d4b281" and answer1 == "6f5ca309"): # if there is still stuff to read - # parse through .S file - detected = False - done = False - op1val = "0" - op2val = "0" - while not (detected or done): - # print("det1") - line = src_file1.readline() - # print(line) - if "op1val" in line: - # print("det2") - # parse line - # handle special case where destination register is hardwired to zero - if "dest:x0" in line: - answer = "x" * len(answer) - op1val = line.split("op1val")[1].split("x")[1].split(";")[0] - if "-" in line.split("op1val")[1].split("x")[0]: # neg sign handling - op1val = twos_comp(my_config.bits, op1val) - if my_config.op != "fsqrt": # sqrt doesn't have two input vals, unnec here but keeping for later - op2val = line.split("op2val")[1].split("x")[1].strip() - if op2val[-1] == ";": op2val = op2val[:-1] # remove ; if it's there - if "-" in line.split("op2val")[1].split("x")[0]: # neg sign handling - op2val = twos_comp(my_config.bits, op2val) - # go to next test in vector - detected = True - elif "RVTEST_CODE_END" in line: - done = True - # ints don't have flags - flags = "XX" - # put it all together - if not done: - translation = "{}_{}_{}_{}_{}_{}".format(operation, ext_bits(op1val), ext_bits(op2val), ext_bits(answer.strip()), flags.strip(), rounding_mode) - dest_file.write(translation + "\n") - else: - # print("read false") - reading = False - elif my_config.letter == "M" and my_config.bits == 32: - reading = True - while reading: - # print("trigger 64M") - # get answer from Ref...signature - # answers span two lines and are reversed - answer = src_file2.readline().strip() - # print(f"Answer: {answer}") - #print(answer1,answer2) - if not (answer == "6f5ca309"): # if there is still stuff to read - # parse through .S file - detected = False - done = False - op1val = "0" - op2val = "0" - while not (detected or done): - # print("det1") - line = src_file1.readline() - # print(line) - if "op1val" in line: - # print("det2") - # parse line - # handle special case where destination register is hardwired to zero - if "dest:x0" in line: - answer = "x" * len(answer) - op1val = line.split("op1val")[1].split("x")[1].split(";")[0] - if "-" in line.split("op1val")[1].split("x")[0]: # neg sign handling - op1val = line.split("op1val")[1].split("x")[1].split(";")[0] - if "-" in line.split("op1val")[1].split("x")[0]: # neg sign handling - op1val = twos_comp(my_config.bits, op1val) - if my_config.op != "fsqrt": # sqrt doesn't have two input vals, unnec here but keeping for later - op2val = line.split("op2val")[1].split("x")[1].strip() - if op2val[-1] == ";": op2val = op2val[:-1] # remove ; if it's there - if "-" in line.split("op2val")[1].split("x")[0]: # neg sign handling - op2val = twos_comp(my_config.bits, op2val) - # go to next test in vector - detected = True - elif "RVTEST_CODE_END" in line: - done = True - # ints don't have flags - flags = "XX" - # put it all together - if not done: - translation = "{}_{}_{}_{}_{}_{}".format(operation, ext_bits(op1val), ext_bits(op2val), ext_bits(answer.strip()), flags.strip(), rounding_mode) - dest_file.write(translation + "\n") - else: - # print("read false") - reading = False - else: - while reading: - # get answer and flags from Ref...signature - answer = src_file2.readline() - #print(answer) - packed = src_file2.readline()[6:] - #print("Packed: ", packed) - if len(packed.strip())>0: # if there is still stuff to read - # print("packed") - # parse through .S file - detected = False - done = False - op1val = "0" - op2val = "0" - while not (detected or done): - # print("det1") - line = src_file1.readline() - # print(line) - if "op1val" in line: - # print("det2") - # parse line - - # handle special case where destination register is hardwired to zero - if "dest:x0" in line: - answer = "x" * len(answer) - - op1val = line.split("op1val")[1].split("x")[1].split(";")[0] - if "-" in line.split("op1val")[1].split("x")[0]: # neg sign handling - op1val = twos_comp(my_config.bits, op1val) - if my_config.op != "fsqrt": # sqrt doesn't have two input vals - op2val = line.split("op2val")[1].split("x")[1].strip() - if op2val[-1] == ";": op2val = op2val[:-1] # remove ; if it's there - if "-" in line.split("op2val")[1].split("x")[0]: # neg sign handling - op2val = twos_comp(my_config.bits, op2val) - # go to next test in vector - detected = True - elif "RVTEST_CODE_END" in line: - done = True - # rounding mode for float - if not done and (my_config.op == "fsqrt" or my_config.op == "fdiv"): + src_file2.readline() #skip first bc junk + # print(my_config.bits, my_config.letter) + if my_config.letter == "F" and my_config.bits == 64: + reading = True + # print("trigger 64F") + #skip first 2 lines bc junk + src_file2.readline() + while reading: + # get answer and flags from Ref...signature + # answers are before deadbeef (first line of 4) + # flags are after deadbeef (third line of 4) + answer = src_file2.readline().strip() + deadbeef = src_file2.readline().strip() + # print(answer) + if not (answer == "e7d4b281" and deadbeef == "6f5ca309"): # if there is still stuff to read + # get flags + packed = src_file2.readline().strip()[6:] flags, rounding_mode = unpack_rf(packed) - - # put it all together - if not done: - translation = "{}_{}_{}_{}_{}_{}".format(operation, ext_bits(op1val), ext_bits(op2val), ext_bits(answer.strip()), flags, rounding_mode) - dest_file.write(translation + "\n") - else: - # print("read false") - reading = False - # print("out") - dest_file.close() - src_file1.close() - src_file2.close() + # skip 00000000 buffer + src_file2.readline() + + # parse through .S file + detected = False + done = False + op1val = "0" + op2val = "0" + while not (detected or done): + # print("det1") + line = src_file1.readline() + # print(line) + if "op1val" in line: + # print("det2") + # parse line + + # handle special case where destination register is hardwired to zero + if "dest:x0" in line: + answer = "x" * len(answer) + + op1val = line.split("op1val")[1].split("x")[1].split(";")[0] + if my_config.op != "fsqrt": # sqrt doesn't have two input vals + op2val = line.split("op2val")[1].split("x")[1].strip() + if op2val[-1] == ";": op2val = op2val[:-1] # remove ; if it's there + else: + op2val = 32*"X" + # go to next test in vector + detected = True + elif "RVTEST_CODE_END" in line: + done = True + # put it all together + if not done: + translation = f"{operation}_{ext_bits(op1val)}_{ext_bits(op2val)}_{ext_bits(answer.strip())}_{flags}_{rounding_mode}" + dest_file.write(translation + "\n") + else: + # print("read false") + reading = False + elif my_config.letter == "M" and my_config.bits == 64: + reading = True + #skip first 2 lines bc junk + src_file2.readline() + while reading: + # print("trigger 64M") + # get answer from Ref...signature + # answers span two lines and are reversed + answer2 = src_file2.readline().strip() + answer1 = src_file2.readline().strip() + answer = answer1 + answer2 + #print(answer1,answer2) + if not (answer2 == "e7d4b281" and answer1 == "6f5ca309"): # if there is still stuff to read + # parse through .S file + detected = False + done = False + op1val = "0" + op2val = "0" + while not (detected or done): + # print("det1") + line = src_file1.readline() + # print(line) + if "op1val" in line: + # print("det2") + # parse line + # handle special case where destination register is hardwired to zero + if "dest:x0" in line: + answer = "x" * len(answer) + op1val = line.split("op1val")[1].split("x")[1].split(";")[0] + if "-" in line.split("op1val")[1].split("x")[0]: # neg sign handling + op1val = twos_comp(my_config.bits, op1val) + if my_config.op != "fsqrt": # sqrt doesn't have two input vals, unnec here but keeping for later + op2val = line.split("op2val")[1].split("x")[1].strip() + if op2val[-1] == ";": op2val = op2val[:-1] # remove ; if it's there + if "-" in line.split("op2val")[1].split("x")[0]: # neg sign handling + op2val = twos_comp(my_config.bits, op2val) + # go to next test in vector + detected = True + elif "RVTEST_CODE_END" in line: + done = True + # ints don't have flags + flags = "XX" + # put it all together + if not done: + translation = f"{operation}_{ext_bits(op1val)}_{ext_bits(op2val)}_{ext_bits(answer.strip())}_{flags.strip()}_{rounding_mode}" + dest_file.write(translation + "\n") + else: + # print("read false") + reading = False + elif my_config.letter == "M" and my_config.bits == 32: + reading = True + while reading: + # print("trigger 64M") + # get answer from Ref...signature + # answers span two lines and are reversed + answer = src_file2.readline().strip() + # print(f"Answer: {answer}") + #print(answer1,answer2) + if answer != '6f5ca309': # if there is still stuff to read + # parse through .S file + detected = False + done = False + op1val = "0" + op2val = "0" + while not (detected or done): + # print("det1") + line = src_file1.readline() + # print(line) + if "op1val" in line: + # print("det2") + # parse line + # handle special case where destination register is hardwired to zero + if "dest:x0" in line: + answer = "x" * len(answer) + op1val = line.split("op1val")[1].split("x")[1].split(";")[0] + if "-" in line.split("op1val")[1].split("x")[0]: # neg sign handling + op1val = line.split("op1val")[1].split("x")[1].split(";")[0] + if "-" in line.split("op1val")[1].split("x")[0]: # neg sign handling + op1val = twos_comp(my_config.bits, op1val) + if my_config.op != "fsqrt": # sqrt doesn't have two input vals, unnec here but keeping for later + op2val = line.split("op2val")[1].split("x")[1].strip() + if op2val[-1] == ";": op2val = op2val[:-1] # remove ; if it's there + if "-" in line.split("op2val")[1].split("x")[0]: # neg sign handling + op2val = twos_comp(my_config.bits, op2val) + # go to next test in vector + detected = True + elif "RVTEST_CODE_END" in line: + done = True + # ints don't have flags + flags = "XX" + # put it all together + if not done: + translation = f"{operation}_{ext_bits(op1val)}_{ext_bits(op2val)}_{ext_bits(answer.strip())}_{flags.strip()}_{rounding_mode}" + dest_file.write(translation + "\n") + else: + # print("read false") + reading = False + else: + while reading: + # get answer and flags from Ref...signature + answer = src_file2.readline() + #print(answer) + packed = src_file2.readline()[6:] + #print("Packed: ", packed) + if len(packed.strip())>0: # if there is still stuff to read + # print("packed") + # parse through .S file + detected = False + done = False + op1val = "0" + op2val = "0" + while not (detected or done): + # print("det1") + line = src_file1.readline() + # print(line) + if "op1val" in line: + # print("det2") + # parse line + + # handle special case where destination register is hardwired to zero + if "dest:x0" in line: + answer = "x" * len(answer) + + op1val = line.split("op1val")[1].split("x")[1].split(";")[0] + if "-" in line.split("op1val")[1].split("x")[0]: # neg sign handling + op1val = twos_comp(my_config.bits, op1val) + if my_config.op != "fsqrt": # sqrt doesn't have two input vals + op2val = line.split("op2val")[1].split("x")[1].strip() + if op2val[-1] == ";": op2val = op2val[:-1] # remove ; if it's there + if "-" in line.split("op2val")[1].split("x")[0]: # neg sign handling + op2val = twos_comp(my_config.bits, op2val) + # go to next test in vector + detected = True + elif "RVTEST_CODE_END" in line: + done = True + # rounding mode for float + if not done and (my_config.op == "fsqrt" or my_config.op == "fdiv"): + flags, rounding_mode = unpack_rf(packed) + + # put it all together + if not done: + translation = f"{operation}_{ext_bits(op1val)}_{ext_bits(op2val)}_{ext_bits(answer.strip())}_{flags}_{rounding_mode}" + dest_file.write(translation + "\n") + else: + # print("read false") + reading = False + # print("out") config_list = [ Config(32, "M", "div", "div-", 0), @@ -309,4 +303,4 @@ Config(64, "M", "remuw", "remuw-", 9) ] for c in config_list: - create_vectors(c) \ No newline at end of file + create_vectors(c) diff --git a/tests/fp/combined_IF_vectors/extract_testfloat_vectors.py b/tests/fp/combined_IF_vectors/extract_testfloat_vectors.py index dce1fc2aa..d6aaa75e1 100755 --- a/tests/fp/combined_IF_vectors/extract_testfloat_vectors.py +++ b/tests/fp/combined_IF_vectors/extract_testfloat_vectors.py @@ -27,8 +27,8 @@ round_dict = { print("creating testfloat div test vectors") -source_dir = "{}/tests/fp/vectors/".format(wally) -dest_dir = "{}/tests/fp/combined_IF_vectors/IF_vectors/".format(wally) +source_dir = f"{wally}/tests/fp/vectors/" +dest_dir = f"{wally}/tests/fp/combined_IF_vectors/IF_vectors/" all_vectors = os.listdir(source_dir) div_vectors = [v for v in all_vectors if "div" in v] @@ -39,19 +39,15 @@ for vector in div_vectors: config_list = vector.split(".")[0].split("_") operation = "1" #float div rounding_mode = round_dict[str(config_list[2])] - # use name to create our new tv - dest_file = open(dest_dir + "cvw_" + vector, 'a') - # open vector - src_file = open(source_dir + vector,'r') - # for each test in the vector - for i in src_file.readlines(): - translation = "" # this stores the test that we are currently working on - [input_1, input_2, answer, flags] = i.split("_") # separate inputs, answer, and flags - # put it all together, strip nec for removing \n on the end of the flags - translation = "{}_{}_{}_{}_{}_{}".format(operation, ext_bits(input_1), ext_bits(input_2), ext_bits(answer), flags.strip(), rounding_mode) - dest_file.write(translation + "\n") - dest_file.close() - src_file.close() + # use name to create our new tv and open vector + with open(dest_dir + "cvw_" + vector, 'a') as dest_file, open(source_dir + vector) as src_file: + # for each test in the vector + for i in src_file.readlines(): + translation = "" # this stores the test that we are currently working on + [input_1, input_2, answer, flags] = i.split("_") # separate inputs, answer, and flags + # put it all together, strip nec for removing \n on the end of the flags + translation = f"{operation}_{ext_bits(input_1)}_{ext_bits(input_2)}_{ext_bits(answer)}_{flags.strip()}_{rounding_mode}" + dest_file.write(translation + "\n") print("creating testfloat sqrt test vectors") @@ -64,16 +60,12 @@ for vector in sqrt_vectors: config_list = vector.split(".")[0].split("_") operation = "2" #sqrt rounding_mode = round_dict[str(config_list[2])] - # use name to create our new tv - dest_file = open(dest_dir + "cvw_" + vector, 'a') - # open vector - src_file = open(source_dir + vector,'r') - # for each test in the vector - for i in src_file.readlines(): - translation = "" # this stores the test that we are currently working on - [input_1, answer, flags] = i.split("_") # separate inputs, answer, and flags - # put it all together, strip nec for removing \n on the end of the flags - translation = "{}_{}_{}_{}_{}_{}".format(operation, ext_bits(input_1), "X"*32, ext_bits(answer), flags.strip(), rounding_mode) - dest_file.write(translation + "\n") - dest_file.close() - src_file.close() \ No newline at end of file + # use name to create our new tv and open vector + with open(dest_dir + "cvw_" + vector, 'a') as dest_file, open(source_dir + vector) as src_file: + # for each test in the vector + for i in src_file.readlines(): + translation = "" # this stores the test that we are currently working on + [input_1, answer, flags] = i.split("_") # separate inputs, answer, and flags + # put it all together, strip nec for removing \n on the end of the flags + translation = "{}_{}_{}_{}_{}_{}".format(operation, ext_bits(input_1), "X"*32, ext_bits(answer), flags.strip(), rounding_mode) + dest_file.write(translation + "\n") diff --git a/tests/riscof/sail_cSim/__init__.py b/tests/riscof/sail_cSim/__init__.py index 0bfb5a62b..3ad9513f4 100644 --- a/tests/riscof/sail_cSim/__init__.py +++ b/tests/riscof/sail_cSim/__init__.py @@ -1,2 +1,2 @@ from pkgutil import extend_path -__path__ = extend_path(__path__, __name__) \ No newline at end of file +__path__ = extend_path(__path__, __name__) diff --git a/tests/riscof/sail_cSim/riscof_sail_cSim.py b/tests/riscof/sail_cSim/riscof_sail_cSim.py index 557d1af04..e739af411 100644 --- a/tests/riscof/sail_cSim/riscof_sail_cSim.py +++ b/tests/riscof/sail_cSim/riscof_sail_cSim.py @@ -1,17 +1,10 @@ import os import re import shutil -import subprocess -import shlex import logging -import random -import string -from string import Template import riscof.utils as utils from riscof.pluginTemplate import pluginTemplate -import riscof.constants as constants -from riscv_isac.isac import isac logger = logging.getLogger() @@ -72,11 +65,11 @@ class sail_cSim(pluginTemplate): self.sailargs += "--enable-zcb" if "Q" in ispec["ISA"]: self.isa += 'q' - objdump = "riscv64-unknown-elf-objdump".format(self.xlen) + objdump = "riscv64-unknown-elf-objdump" if shutil.which(objdump) is None: logger.error(objdump+": executable not found. Please check environment setup.") raise SystemExit(1) - compiler = "riscv64-unknown-elf-gcc".format(self.xlen) + compiler = "riscv64-unknown-elf-gcc" if shutil.which(compiler) is None: logger.error(compiler+": executable not found. Please check environment setup.") raise SystemExit(1) @@ -114,9 +107,9 @@ class sail_cSim(pluginTemplate): if ('NO_SAIL=True' in testentry['macros']): # if the tests can't run on SAIL we copy the reference output to the src directory reference_output = re.sub("/src/","/references/", re.sub(".S",".reference_output", test)) - execute += 'cut -c-{0:g} {1} > {2}'.format(8, reference_output, sig_file) #use cut to remove comments when copying + execute += f'cut -c-{8:g} {reference_output} > {sig_file}' #use cut to remove comments when copying else: - execute += self.sail_exe[self.xlen] + ' -z268435455 -i --trace=step ' + self.sailargs + ' --test-signature={0} {1} > {2}.log 2>&1;'.format(sig_file, elf, test_name) + execute += self.sail_exe[self.xlen] + ' -z268435455 -i --trace=step ' + self.sailargs + f' --test-signature={sig_file} {elf} > {test_name}.log 2>&1;' cov_str = ' ' for label in testentry['coverage_labels']: @@ -124,10 +117,10 @@ class sail_cSim(pluginTemplate): if cgf_file is not None: coverage_cmd = 'riscv_isac --verbose info coverage -d \ - -t {0}.log --parser-name c_sail -o coverage.rpt \ + -t {}.log --parser-name c_sail -o coverage.rpt \ --sig-label begin_signature end_signature \ --test-label rvtest_code_begin rvtest_code_end \ - -e ref.elf -c {1} -x{2} {3};'.format(\ + -e ref.elf -c {} -x{} {};'.format(\ test_name, ' -c '.join(cgf_file), self.xlen, cov_str) else: coverage_cmd = '' diff --git a/tests/riscof/spike/riscof_spike.py b/tests/riscof/spike/riscof_spike.py index 2d3eb2a37..73874f3b9 100644 --- a/tests/riscof/spike/riscof_spike.py +++ b/tests/riscof/spike/riscof_spike.py @@ -1,16 +1,8 @@ import os import re -import shutil -import subprocess -import shlex import logging -import random -import string -from string import Template -import sys import riscof.utils as utils -import riscof.constants as constants from riscof.pluginTemplate import pluginTemplate logger = logging.getLogger() @@ -194,22 +186,22 @@ class spike(pluginTemplate): # cmd = self.compile_cmd.format(testentry['isa'].lower().replace('zicsr', ' ', 2), self.xlen, test, elf, compile_macros) cmd = self.compile_cmd.format(testentry['isa'].lower(), self.xlen, test, elf, compile_macros) - # if the user wants to disable running the tests and only compile the tests, then - # the "else" clause is executed below assigning the sim command to simple no action - # echo statement. + # if the user wants to disable running the tests and only compile the tests, then + # the "else" clause is executed below assigning the sim command to simple no action + # echo statement. if self.target_run: # set up the simulation command. Template is for spike. Please change. if ('NO_SAIL=True' in testentry['macros']): # if the tests can't run on SAIL we copy the reference output to the src directory reference_output = re.sub("/src/","/references/", re.sub(".S",".reference_output", test)) - simcmd = 'cut -c-{0:g} {1} > {2}'.format(8, reference_output, sig_file) #use cut to remove comments when copying + simcmd = f'cut -c-{8:g} {reference_output} > {sig_file}' #use cut to remove comments when copying else: - simcmd = self.dut_exe + ' --isa={0} +signature={1} +signature-granularity=4 {2}'.format(self.isa, sig_file, elf) + simcmd = self.dut_exe + f' --isa={self.isa} +signature={sig_file} +signature-granularity=4 {elf}' else: simcmd = 'echo "NO RUN"' # concatenate all commands that need to be executed within a make-target. - execute = '@cd {0}; {1}; {2};'.format(testentry['work_dir'], cmd, simcmd) + execute = '@cd {}; {}; {};'.format(testentry['work_dir'], cmd, simcmd) # create a target. The makeutil will create a target with the name "TARGET" where num # starts from 0 and increments automatically for each new target that is added diff --git a/tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/privilege/src/periph-test-signature-address-calculator.py b/tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/privilege/src/periph-test-signature-address-calculator.py index 5eb7b45b4..1569482da 100755 --- a/tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/privilege/src/periph-test-signature-address-calculator.py +++ b/tests/wally-riscv-arch-test/riscv-test-suite/rv64i_m/privilege/src/periph-test-signature-address-calculator.py @@ -17,7 +17,7 @@ if __name__ == "__main__": line_num = int(sig_adr / 4) + 1 offset = sig_adr & 0x3F test_num = int((sig_adr-offset)/int("40",16)) - print("IntrNum 0x{:02X}".format(test_num)) - print("Offset 0x{:02X}".format(offset)) + print(f"IntrNum 0x{test_num:02X}") + print(f"Offset 0x{offset:02X}") print("LineNum "+str(line_num))