This commit is contained in:
Roman De Santos 2025-01-28 08:24:05 -08:00
commit e9e856d2a2
36 changed files with 1007 additions and 1154 deletions

36
.github/workflows/lint.yml vendored Normal file
View File

@ -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

31
.ruff.toml Normal file
View File

@ -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
]

View File

@ -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 # core-v-wally

@ -1 +1 @@
Subproject commit 66b675017878032974c537ab7aa81758b9812530 Subproject commit 44278d9a918dbc337ac214cd5dba5f71aa26dcfa

View File

@ -65,10 +65,7 @@ with open(resultfile, mode='w', newline='') as csvfile:
# Loop through each architecture and run the make commands # Loop through each architecture and run the make commands
for arch in arch_list: for arch in arch_list:
if(str in arch): xlen_value = "32" if str in arch else "64"
xlen_value='32'
else:
xlen_value='64'
os.system("make clean") os.system("make clean")
make_all = f"make all XLEN={xlen_value} ARCH={arch}" make_all = f"make all XLEN={xlen_value} ARCH={arch}"
os.system(make_all) os.system(make_all)

View File

@ -30,12 +30,8 @@ def tabulate_arch_sweep(directory):
file = case+"_"+arch+".json" file = case+"_"+arch+".json"
file_path = os.path.join(directory, file) file_path = os.path.join(directory, file)
lines = [] lines = []
try: with open(file_path) as f:
f = open(file_path, "r")
lines = f.readlines() lines = f.readlines()
except:
f.close()
#print(file_path+" does not exist")
for line in lines: for line in lines:
#print("File: "+file+" Line: "+line) #print("File: "+file+" Line: "+line)
#p = re.compile('".*" : .*,') #p = re.compile('".*" : .*,')
@ -43,8 +39,8 @@ def tabulate_arch_sweep(directory):
match = re.search(p, line) match = re.search(p, line)
if match: if match:
prog = match.group(1) prog = match.group(1)
result = match.group(2); result = match.group(2)
d[arch][prog] = result; d[arch][prog] = result
#print(match.group(1)+" " + match.group(2)) #print(match.group(1)+" " + match.group(2))
f.close() f.close()
for arch in [""] + archs: for arch in [""] + archs:
@ -53,7 +49,7 @@ def tabulate_arch_sweep(directory):
for prog in d[archs[0]]: for prog in d[archs[0]]:
print(prog, end="\t") print(prog, end="\t")
for arch in archs: for arch in archs:
entry = d[arch].get(prog, "n/a"); entry = d[arch].get(prog, "n/a")
print (entry, end="\t") print (entry, end="\t")
print("") print("")
print("New geo mean", end="\t") print("New geo mean", end="\t")
@ -84,4 +80,4 @@ def run_arch_sweep():
directory = run_arch_sweep() directory = run_arch_sweep()
#directory = "run_20231120_072037-caches" #directory = "run_20231120_072037-caches"
tabulate_arch_sweep(directory) tabulate_arch_sweep(directory)

View File

@ -10,13 +10,13 @@ from plotly.subplots import make_subplots
debug = True debug = True
def loadCoremark(): def loadCoremark(coremarkData):
"""loads the coremark data dictionary""" """loads the coremark data dictionary"""
coremarkPath = "riscv-coremark/work/coremark.sim.log" coremarkPath = "riscv-coremark/work/coremark.sim.log"
keywordlist = ["CoreMark 1.0", "CoreMark Size", "MTIME", "MINSTRET", "Branches Miss Predictions", "BTB Misses"] keywordlist = ["CoreMark 1.0", "CoreMark Size", "MTIME", "MINSTRET", "Branches Miss Predictions", "BTB Misses"]
for keyword in keywordlist: 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) result = subprocess.run(bashInst, stdout=subprocess.PIPE, shell=True)
if (debug): print(result) if (debug): print(result)
coremarkData[keyword] = int(result.stdout) coremarkData[keyword] = int(result.stdout)
@ -25,8 +25,8 @@ def loadCoremark():
def loadEmbench(embenchPath, embenchData): def loadEmbench(embenchPath, embenchData):
"""loads the embench data dictionary""" """loads the embench data dictionary"""
f = open(embenchPath) with open(embenchPath) as f:
embenchData = json.load(f) embenchData = json.load(f)
if (debug): print(embenchData) if (debug): print(embenchData)
return embenchData return embenchData
@ -93,7 +93,7 @@ def main():
embenchSpeedOpt_SpeedData = {} embenchSpeedOpt_SpeedData = {}
embenchSizeOpt_SizeData = {} embenchSizeOpt_SizeData = {}
embenchSpeedOpt_SizeData = {} embenchSpeedOpt_SizeData = {}
# coremarkData = loadCoremark() coremarkData = loadCoremark(coremarkData)
embenchSpeedOpt_SpeedData = loadEmbench("embench/wallySpeedOpt_speed.json", embenchSpeedOpt_SpeedData) embenchSpeedOpt_SpeedData = loadEmbench("embench/wallySpeedOpt_speed.json", embenchSpeedOpt_SpeedData)
embenchSizeOpt_SpeedData = loadEmbench("embench/wallySizeOpt_speed.json", embenchSizeOpt_SpeedData) embenchSizeOpt_SpeedData = loadEmbench("embench/wallySizeOpt_speed.json", embenchSizeOpt_SpeedData)
embenchSpeedOpt_SizeData = loadEmbench("embench/wallySpeedOpt_size.json", embenchSpeedOpt_SizeData) embenchSpeedOpt_SizeData = loadEmbench("embench/wallySpeedOpt_size.json", embenchSpeedOpt_SizeData)
@ -104,4 +104,4 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())
# "ls -Art ../addins/embench-iot/logs/*speed* | tail -n 1 " # gets most recent embench speed log # "ls -Art ../addins/embench-iot/logs/*speed* | tail -n 1 " # gets most recent embench speed log

View File

@ -16,19 +16,19 @@
## ##
## SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 ## 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 ## 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 ## 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 ## may obtain a copy of the License at
## ##
## https:##solderpad.org/licenses/SHL-2.1/ ## https:##solderpad.org/licenses/SHL-2.1/
## ##
## Unless required by applicable law or agreed to in writing, any work distributed under the ## 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, ## 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 ## either express or implied. See the License for the specific language governing permissions
## and limitations under the License. ## and limitations under the License.
################################################################################################ ################################################################################################
# how to invoke this simulator: # how to invoke this simulator:
# CacheSim.py <number of lines> <number of ways> <length of physical address> <length of tag> -f <log file> (-v) # CacheSim.py <number of lines> <number of ways> <length of physical address> <length of tag> -f <log file> (-v)
# so the default invocation for rv64gc is 'CacheSim.py 64 4 56 44 -f <log file>' # so the default invocation for rv64gc is 'CacheSim.py 64 4 56 44 -f <log file>'
# the log files to run this simulator on can be generated from testbench.sv # the log files to run this simulator on can be generated from testbench.sv
@ -37,25 +37,26 @@
# This helps avoid unexpected logger behavior. # This helps avoid unexpected logger behavior.
# With verbose mode off, the simulator only reports mismatches between its and Wally's 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. # 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. # 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. # These distributions may not add up to 100; this is because of flushes or invalidations.
import math import math
import argparse import argparse
import os import os
import sys
class CacheLine: class CacheLine:
def __init__(self): def __init__(self):
self.tag = 0 self.tag = 0
self.valid = False self.valid = False
self.dirty = False self.dirty = False
def __str__(self): def __str__(self):
string = "(V: " + str(self.valid) + ", D: " + str(self.dirty) string = f"(V: {self.valid}, D: {self.dirty}"
string += ", Tag: " + str(hex(self.tag)) + ")" string += f", Tag: {hex(self.tag)})"
return string return string
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
@ -72,13 +73,13 @@ class Cache:
self.ways = [] self.ways = []
for i in range(numways): for i in range(numways):
self.ways.append([]) self.ways.append([])
for j in range(numsets): for _ in range(numsets):
self.ways[i].append(CacheLine()) self.ways[i].append(CacheLine())
self.pLRU = [] self.pLRU = []
for i in range(self.numsets): for i in range(self.numsets):
self.pLRU.append([0]*(self.numways-1)) self.pLRU.append([0]*(self.numways-1))
# flushes the cache by setting all dirty bits to False # flushes the cache by setting all dirty bits to False
def flush(self): def flush(self):
for way in self.ways: for way in self.ways:
@ -92,20 +93,21 @@ class Cache:
line = self.ways[waynum][setnum] line = self.ways[waynum][setnum]
if line.tag == tag and line.valid: if line.tag == tag and line.valid:
line.dirty = 0 line.dirty = 0
if invalidate: line.valid = 0 if invalidate:
line.valid = 0
# invalidates the cache by setting all valid bits to False # invalidates the cache by setting all valid bits to False
def invalidate(self): def invalidate(self):
for way in self.ways: for way in self.ways:
for line in way: for line in way:
line.valid = False line.valid = False
# resets the pLRU to a fresh 2-D array of 0s # resets the pLRU to a fresh 2-D array of 0s
def clear_pLRU(self): def clear_pLRU(self):
self.pLRU = [] self.pLRU = []
for i in range(self.numsets): for _ in range(self.numsets):
self.pLRU.append([0]*(self.numways-1)) self.pLRU.append([0]*(self.numways-1))
# splits the given address into tag, set, and offset # splits the given address into tag, set, and offset
def splitaddr(self, addr): def splitaddr(self, addr):
# no need for offset in the sim, but it's here for debug # 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) setnum = (addr >> self.offsetlen) & int('1'*self.setlen, 2)
offset = addr & int('1'*self.offsetlen, 2) offset = addr & int('1'*self.offsetlen, 2)
return tag, setnum, offset return tag, setnum, offset
# performs a cache access with the given address. # performs a cache access with the given address.
# returns a character representing the outcome: # returns a character representing the outcome:
# H/M/E/D - hit, miss, eviction, or eviction with writeback # H/M/E/D - hit, miss, eviction, or eviction with writeback
@ -138,7 +140,7 @@ class Cache:
line.dirty = write line.dirty = write
self.update_pLRU(waynum, setnum) self.update_pLRU(waynum, setnum)
return 'M' return 'M'
# we need to evict. Select a victim and overwrite. # we need to evict. Select a victim and overwrite.
victim = self.getvictimway(setnum) victim = self.getvictimway(setnum)
line = self.ways[victim][setnum] line = self.ways[victim][setnum]
@ -154,14 +156,14 @@ class Cache:
def update_pLRU(self, waynum, setnum): def update_pLRU(self, waynum, setnum):
if self.numways == 1: if self.numways == 1:
return return
tree = self.pLRU[setnum] tree = self.pLRU[setnum]
bottomrow = (self.numways - 1)//2 bottomrow = (self.numways - 1)//2
index = (waynum // 2) + bottomrow index = (waynum // 2) + bottomrow
tree[index] = int(not (waynum % 2)) tree[index] = int(not waynum % 2)
while index > 0: while index > 0:
parent = (index-1) // 2 parent = (index-1) // 2
tree[parent] = index % 2 tree[parent] = index % 2
index = parent index = parent
# uses the psuedo-LRU tree to select # uses the psuedo-LRU tree to select
@ -170,7 +172,7 @@ class Cache:
def getvictimway(self, setnum): def getvictimway(self, setnum):
if self.numways == 1: if self.numways == 1:
return 0 return 0
tree = self.pLRU[setnum] tree = self.pLRU[setnum]
index = 0 index = 0
bottomrow = (self.numways - 1) // 2 #first index on the bottom row of the tree bottomrow = (self.numways - 1) // 2 #first index on the bottom row of the tree
@ -180,28 +182,28 @@ class Cache:
index = index*2 + 1 index = index*2 + 1
else: #tree[index] == 1 else: #tree[index] == 1
# Go to the right child # Go to the right child
index = index*2 + 2 index = index*2 + 2
victim = (index - bottomrow)*2 victim = (index - bottomrow)*2
if tree[index] == 1: if tree[index] == 1:
victim += 1 victim += 1
return victim return victim
def __str__(self): def __str__(self):
string = "" string = ""
for i in range(self.numways): for i in range(self.numways):
string += "Way " + str(i) + ": " string += f"Way {i}: "
for line in self.ways[i]: for line in self.ways[i]:
string += str(line) + ", " string += f"{line}, "
string += "\n\n" string += "\n\n"
return string return string
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
def main():
def parseArgs():
parser = argparse.ArgumentParser(description="Simulates a L1 cache.") 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('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') 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('-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('-p', "--perf", action='store_true', help="Report hit/miss ratio")
parser.add_argument('-d', "--dist", action='store_true', help="Report distribution of operations") 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) cache = Cache(args.numlines, args.numways, args.addrlen, args.taglen)
extfile = os.path.expanduser(args.file) extfile = os.path.expanduser(args.file)
mismatches = 0 mismatches = 0
@ -227,7 +230,7 @@ def main():
atoms = 0 atoms = 0
totalops = 0 totalops = 0
with open(extfile, "r") as f: with open(extfile) as f:
for ln in f: for ln in f:
ln = ln.strip() ln = ln.strip()
lninfo = ln.split() lninfo = ln.split()
@ -239,11 +242,11 @@ def main():
cache.clear_pLRU() cache.clear_pLRU()
if args.verbose: if args.verbose:
print("New Test") print("New Test")
else: else:
if args.dist: if args.dist:
totalops += 1 totalops += 1
if lninfo[1] == 'F': if lninfo[1] == 'F':
cache.flush() cache.flush()
if args.verbose: if args.verbose:
@ -257,22 +260,22 @@ def main():
IsCBOClean = lninfo[1] != 'C' IsCBOClean = lninfo[1] != 'C'
cache.cbo(addr, IsCBOClean) cache.cbo(addr, IsCBOClean)
if args.verbose: if args.verbose:
print(lninfo[1]); print(lninfo[1])
else: else:
addr = int(lninfo[0], 16) addr = int(lninfo[0], 16)
iswrite = lninfo[1] == 'W' or lninfo[1] == 'A' or lninfo[1] == 'Z' iswrite = lninfo[1] == 'W' or lninfo[1] == 'A' or lninfo[1] == 'Z'
result = cache.cacheaccess(addr, iswrite) result = cache.cacheaccess(addr, iswrite)
if args.verbose: if args.verbose:
tag, setnum, offset = cache.splitaddr(addr) tag, setnum, offset = cache.splitaddr(addr)
print(hex(addr), hex(tag), hex(setnum), hex(offset), lninfo[2], result) print(hex(addr), hex(tag), hex(setnum), hex(offset), lninfo[2], result)
if args.perf: if args.perf:
if result == 'H': if result == 'H':
hits += 1 hits += 1
else: else:
misses += 1 misses += 1
if args.dist: if args.dist:
if lninfo[1] == 'R': if lninfo[1] == 'R':
loads += 1 loads += 1
@ -280,23 +283,24 @@ def main():
stores += 1 stores += 1
elif lninfo[1] == 'A': elif lninfo[1] == 'A':
atoms += 1 atoms += 1
if not result == lninfo[2]: if result != lninfo[2]:
print("Result mismatch at address", lninfo[0]+ ". Wally:", lninfo[2]+", Sim:", result) print(f"Result mismatch at address {lninfo[0]}. Wally: {lninfo[2]}, Sim: {result}")
mismatches += 1 mismatches += 1
if args.dist: if args.dist:
percent_loads = str(round(100*loads/totalops)) percent_loads = str(round(100*loads/totalops))
percent_stores = str(round(100*stores/totalops)) percent_stores = str(round(100*stores/totalops))
percent_atoms = str(round(100*atoms/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: if args.perf:
ratio = round(hits/misses,3) ratio = round(hits/misses,3)
print("There were", hits, "hits and", misses, "misses. The hit/miss ratio was", str(ratio)+".") print("There were", hits, "hits and", misses, "misses. The hit/miss ratio was", str(ratio)+".")
if mismatches == 0: if mismatches == 0:
print("SUCCESS! There were no mismatches between Wally and the sim.") print("SUCCESS! There were no mismatches between Wally and the sim.")
return mismatches return mismatches
if __name__ == '__main__': if __name__ == '__main__':
exit(main()) args = parseArgs()
sys.exit(main(args))

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
# iterelf # iterelf
# David_Harris@hmc.edu and Rose Thompson 7/3/2024 # David_Harris@hmc.edu and Rose Thompson 7/3/2024
@ -7,20 +7,21 @@
import argparse import argparse
import os import os
import sys
import multiprocessing import multiprocessing
from multiprocessing import Pool, TimeoutError from multiprocessing import Pool, TimeoutError as MPTimeoutError
TIMEOUT_DUR = 60 # 1` minute TIMEOUT_DUR = 60 # 1` minute
class bcolors: class bcolors:
HEADER = '\033[95m' HEADER = "\033[95m"
OKBLUE = '\033[94m' OKBLUE = "\033[94m"
OKCYAN = '\033[96m' OKCYAN = "\033[96m"
OKGREEN = '\033[92m' OKGREEN = "\033[92m"
WARNING = '\033[93m' WARNING = "\033[93m"
FAIL = '\033[91m' FAIL = "\033[91m"
ENDC = '\033[0m' ENDC = "\033[0m"
BOLD = '\033[1m' BOLD = "\033[1m"
UNDERLINE = '\033[4m' UNDERLINE = "\033[4m"
def search_log_for_mismatches(logfile): 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""" """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) os.system(grepwarn)
greperr = "grep -H Error: " + logfile greperr = "grep -H Error: " + logfile
os.system(greperr) os.system(greperr)
grepcmd = "grep -a -e 'Mismatches : 0' '%s' > /dev/null" % logfile grepcmd = f"grep -a -e 'Mismatches : 0' '{logfile}' > /dev/null"
# print(" search_log_for_text invoking %s" % grepcmd)
return os.system(grepcmd) == 0 return os.system(grepcmd) == 0
def run_test_case(elf): def run_test_case(elf):
"""Run the given test case, and return 0 if the test suceeds and 1 if it fails""" """Run the given test case, and return 0 if the test suceeds and 1 if it fails"""
WALLY = os.environ.get('WALLY') WALLY = os.environ.get("WALLY")
fields = elf.rsplit('/', 3) fields = elf.rsplit("/", 3)
if (fields[2] == "ref"): if fields[2] == "ref":
shortelf = fields[1] + "_" + fields[3] shortelf = fields[1] + "_" + fields[3]
else: else:
shortelf = fields[2] + "_" + fields[3] shortelf = fields[2] + "_" + fields[3]
# shortelf = fields[1] + "_" + fields[2] # shortelf = fields[1] + "_" + fields[2]
logfile = WALLY + "/sim/" + args.sim + "/logs/" + shortelf + ".log" 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 cmd = "wsim " + args.config + " " + shortelf + " --elf " + elf + " --sim " + args.sim + " --lockstep > " + logfile # add coveerage flags if necessary
# print("cmd = " + cmd) # print("cmd = " + cmd)
os.system(cmd) os.system(cmd)
if search_log_for_mismatches(logfile): 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 return 0
elif("WALLY-cbom-01" in elf): elif "WALLY-cbom-01" in elf:
# Remove this when CBO instructions are modeled in ImperasDV # 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 return 0
else: else:
print(f"{bcolors.FAIL}%s: Failures detected in output{bcolors.ENDC}" % (cmd)) print(f"{bcolors.FAIL}{cmd}: Failures detected in output{bcolors.ENDC}")
print(" Check %s" % logfile) print(f" Check {logfile}")
return 1 return 1
################################## ##################################
@ -74,34 +74,32 @@ args = parser.parse_args()
# find all ELF files in directory # find all ELF files in directory
ElfList = [] ElfList = []
if (os.path.isdir(args.dir)): if os.path.isdir(args.dir):
DirectorMode = 1 DirectorMode = 1
for dirpath, dirnames, filenames in os.walk(os.path.abspath(args.dir)): for dirpath, dirnames, filenames in os.walk(os.path.abspath(args.dir)):
for file in filenames: 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)) ElfList.append(os.path.join(dirpath, file))
else: else:
print(args.dir + " is not a directory") print(args.dir + " is not a directory")
exit(1) sys.exit(1)
#print(ElfList)
# spawn parallel wsim jobs for each ELF file # spawn parallel wsim jobs for each ELF file
ImperasDVLicenseCount = 8 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 num_fail = 0
results = {} results = {}
for elf in ElfList: for elf in ElfList:
results[elf] = pool.apply_async(run_test_case,(elf,)) results[elf] = pool.apply_async(run_test_case, (elf,))
for (elf,result) in results.items(): for elf, result in results.items():
try: try:
num_fail+=result.get(timeout=TIMEOUT_DUR) num_fail += result.get(timeout=TIMEOUT_DUR)
except TimeoutError: except MPTimeoutError:
num_fail+=1 num_fail += 1
print(f"{bcolors.FAIL}%s: Timeout - runtime exceeded %d seconds{bcolors.ENDC}" % (elf, TIMEOUT_DUR)) 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}") print(f"{bcolors.OKGREEN}SUCCESS! All tests ran without failures{bcolors.ENDC}")
else: 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}")

View File

@ -68,7 +68,7 @@ In summary, this Python script facilitates the automation of nightly regression
import os import os
import shutil import shutil
from datetime import datetime, timedelta from datetime import datetime
import time import time
import re import re
import markdown import markdown
@ -78,9 +78,6 @@ import logging
from pathlib import Path from pathlib import Path
class FolderManager: class FolderManager:
"""A class for managing folders and repository cloning.""" """A class for managing folders and repository cloning."""
@ -115,9 +112,6 @@ class FolderManager:
""" """
for folder in folders: 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): if not os.path.exists(folder):
os.makedirs(folder) os.makedirs(folder)
@ -171,7 +165,6 @@ class FolderManager:
Returns: Returns:
None None
""" """
todays_date = datetime.now().strftime("%Y-%m-%d")
cvw = folder.joinpath("cvw") cvw = folder.joinpath("cvw")
tmp_folder = os.path.join(cvw, "tmp") # temprorary files will be stored in here tmp_folder = os.path.join(cvw, "tmp") # temprorary files will be stored in here
if not cvw.exists(): if not cvw.exists():
@ -287,7 +280,7 @@ class TestRunner:
if target: if target:
output_file = self.log_dir.joinpath(f"make-{target}-output.log") 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 # Source setup script and execute make with target and cores/2
if target: if target:
@ -398,7 +391,7 @@ class TestRunner:
# Implement cleaning and formatting logic here # Implement cleaning and formatting logic here
# Open up the file with only read permissions # 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() uncleaned_output = input_file.read()
# use something like this function to detect pass and fail # use something like this function to detect pass and fail
@ -461,7 +454,6 @@ class TestRunner:
None None
""" """
# Implement markdown rewriting logic here # Implement markdown rewriting logic here
timestamp = datetime.now().strftime("%Y-%m-%d")
# output_directory = self.base_parent_dir.joinpath("results") # output_directory = self.base_parent_dir.joinpath("results")
os.chdir(self.results_dir) os.chdir(self.results_dir)
@ -470,7 +462,7 @@ class TestRunner:
with open(output_file, 'w') as md_file: with open(output_file, 'w') as md_file:
# Title # Title
md_file.write(f"\n\n# Regression Test Results - {self.todays_date}\n\n") md_file.write(f"\n\n# Regression Test Results - {self.todays_date}\n\n")
#md_file.write(f"\n\n<div class=\"regression\">\n# Regression Test Results - {timestamp}\n</div>\n\n") #md_file.write(f"\n\n<div class=\"regression\">\n# Regression Test Results - {timestamp}\n</div>\n\n")
@ -481,15 +473,15 @@ class TestRunner:
if failed_configs: if failed_configs:
md_file.write("## Failed Configurations\n\n") md_file.write("## Failed Configurations\n\n")
for config, log_file in failed_configs: for config, log_file in failed_configs:
md_file.write(f"- <span class=\"failure\" style=\"color: red;\">{config}</span> ({log_file})\n") md_file.write(f'- <span class="failure" style="color: red;">{config}</span> ({log_file})\n')
md_file.write("\n") md_file.write("\n")
else: else:
md_file.write("## Failed Configurations\n") 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") md_file.write("\n## Passed Configurations\n")
for config in passed_configs: for config in passed_configs:
md_file.write(f"- <span class=\"success\" style=\"color: green;\">{config}</span>\n") md_file.write(f'- <span class="success" style="color: green;">{config}</span>\n')
self.logger.info("writing test outputs to markdown") self.logger.info("writing test outputs to markdown")
@ -534,7 +526,7 @@ class TestRunner:
md_file.write("\n") md_file.write("\n")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
# Handle if the command fails # 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 # Which tests did we run
md_file.write(f"\n**Tests made:** `make {test_type}`\n") 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") md_file.write(f"**Total Failures: {total_number_failures}**\n")
# Failed Tests # 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}**") md_file.write(f"\n**Total failed tests: {total_number_failures}**")
for (test_item, item) in zip(test_list, failed_tests): 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\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**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: if len(item) == 0:
md_file.write("\n") md_file.write("\n")
md_file.write(f"* <span class=\"no-failure\" style=\"color: green;\">No failures</span>\n") md_file.write('* <span class="no-failure" style="color: green;">No failures</span>\n')
md_file.write("\n") md_file.write("\n")
else: else:
for failed_test in item: for failed_test in item:
@ -564,29 +556,29 @@ class TestRunner:
log_file = failed_test[1] log_file = failed_test[1]
md_file.write("\n") md_file.write("\n")
md_file.write(f"* <span class=\"failure\" style=\"color: red;\">{config}</span> ({log_file})\n") md_file.write(f'* <span class="failure" style="color: red;">{config}</span> ({log_file})\n')
md_file.write("\n") md_file.write("\n")
# Successful Tests # 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}**") md_file.write(f"\n**Total successful tests: {total_number_success}**")
for (test_item, item) in zip(test_list, passed_tests): 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\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**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: if len(item) == 0:
md_file.write("\n") md_file.write("\n")
md_file.write(f"* <span class=\"no-successes\" style=\"color: red;\">No successes</span>\n") md_file.write('* <span class="no-successes" style="color: red;">No successes</span>\n')
md_file.write("\n") md_file.write("\n")
else: else:
for passed_tests in item: for passed_tests in item:
config = passed_tests config = passed_tests
md_file.write("\n") md_file.write("\n")
md_file.write(f"* <span class=\"success\" style=\"color: green;\">{config}</span>\n") md_file.write(f'* <span class="success" style="color: green;">{config}</span>\n')
md_file.write("\n") md_file.write("\n")
self.logger.info("Combining markdown files") self.logger.info("Combining markdown files")
@ -606,7 +598,7 @@ class TestRunner:
# Implement markdown to HTML conversion logic here # Implement markdown to HTML conversion logic here
os.chdir(self.results_dir) 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() md_content = md_file.read()
html_content = markdown.markdown(md_content) html_content = markdown.markdown(md_content)
@ -614,7 +606,7 @@ class TestRunner:
html_file.write(html_content) html_file.write(html_content)
self.logger.info("Converting markdown file to html file.") self.logger.info("Converting markdown file to html file.")
def send_email(self, receiver_emails=None, subject="Nightly Regression Test"): def send_email(self, receiver_emails=None, subject="Nightly Regression Test"):
""" """
Send email with HTML content. Send email with HTML content.
@ -640,7 +632,7 @@ class TestRunner:
os.chdir(self.results_dir) os.chdir(self.results_dir)
html_file = "results.html" html_file = "results.html"
with open(html_file, 'r') as html_file: with open(html_file) as html_file:
body = html_file.read() body = html_file.read()
try: 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 # 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") 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) cvw_path = Path.home().joinpath(args.path, today)
results_path = Path.home().joinpath(args.path, today, "results") results_path = Path.home().joinpath(args.path, today, "results")
log_path = Path.home().joinpath(args.path, today, "logs") log_path = Path.home().joinpath(args.path, today, "logs")
log_file_path = log_path.joinpath("nightly_build.log") log_file_path = log_path.joinpath("nightly_build.log")
previous_cvw_path = Path.home().joinpath(args.path,f"{yesterday}/cvw")
# creates the object # creates the object
folder_manager = FolderManager(basedir=args.path) folder_manager = FolderManager(basedir=args.path)
@ -765,12 +754,6 @@ def main():
if args.target != "no": if args.target != "no":
test_runner.execute_makefile(target = args.target, makefile_path=test_runner.cvw) 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 # # RUN TESTS #
@ -817,7 +800,7 @@ def main():
logger.info(f"The total failures for all tests ran are: {total_number_failures}") 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 # 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"]) test_runner.copy_sim_logs([test_runner.cvw / "sim/questa/logs", test_runner.cvw / "sim/verilator/logs", test_runner.cvw / "sim/vcs/logs"])
############################################# #############################################

View File

@ -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. 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.''' parameters can be any number and depend on the predictor type. Returns a list of lists.'''
lst = [] lst = []
BranchList = open(path, 'r') with open(path) as BranchList:
for line in BranchList: for line in BranchList:
tokens = line.split() tokens = line.split()
predictorLog = os.path.dirname(path) + '/' + tokens[0] predictorLog = os.path.dirname(path) + '/' + tokens[0]
predictorType = tokens[1] predictorType = tokens[1]
predictorParams = tokens[2::] predictorParams = tokens[2::]
lst.append([predictorLog, predictorType, predictorParams]) lst.append([predictorLog, predictorType, predictorParams])
#print(predictorLog, predictorType, predictorParams) #print(predictorLog, predictorType, predictorParams)
return lst return lst
def ProcessFile(fileName): def ProcessFile(fileName):
@ -62,22 +62,22 @@ def ProcessFile(fileName):
# 1 find lines with Read memfile and extract test name # 1 find lines with Read memfile and extract test name
# 2 parse counters into a list of (name, value) tuples (dictionary maybe?) # 2 parse counters into a list of (name, value) tuples (dictionary maybe?)
benchmarks = [] benchmarks = []
transcript = open(fileName, 'r')
HPMClist = { } HPMClist = { }
testName = '' testName = ''
for line in transcript.readlines(): with open(fileName) as transcript:
lineToken = line.split() for line in transcript.readlines():
if(len(lineToken) > 3 and lineToken[1] == 'Read' and lineToken[2] == 'memfile'): lineToken = line.split()
opt = lineToken[3].split('/')[-4] if(len(lineToken) > 3 and lineToken[1] == 'Read' and lineToken[2] == 'memfile'):
testName = lineToken[3].split('/')[-1].split('.')[0] opt = lineToken[3].split('/')[-4]
HPMClist = { } testName = lineToken[3].split('/')[-1].split('.')[0]
elif(len(lineToken) > 4 and lineToken[1][0:3] == 'Cnt'): HPMClist = { }
countToken = line.split('=')[1].split() elif(len(lineToken) > 4 and lineToken[1][0:3] == 'Cnt'):
value = int(countToken[0]) if countToken[0] != 'x' else 0 countToken = line.split('=')[1].split()
name = ' '.join(countToken[1:]) value = int(countToken[0]) if countToken[0] != 'x' else 0
HPMClist[name] = value name = ' '.join(countToken[1:])
elif ('is done' in line): HPMClist[name] = value
benchmarks.append((testName, opt, HPMClist)) elif ('is done' in line):
benchmarks.append((testName, opt, HPMClist))
return benchmarks return benchmarks
@ -227,13 +227,13 @@ def ReportAsTable(benchmarkDict):
sys.stdout.write('benchmark\t\t') sys.stdout.write('benchmark\t\t')
for name in FirstLine: for name in FirstLine:
if(len(name) < 8): sys.stdout.write('%s\t\t' % name) if(len(name) < 8): sys.stdout.write(f'{name}\t\t')
else: sys.stdout.write('%s\t' % name) else: sys.stdout.write(f'{name}\t')
sys.stdout.write('\n') sys.stdout.write('\n')
sys.stdout.write('size\t\t\t') sys.stdout.write('size\t\t\t')
for size in SecondLine: for size in SecondLine:
if(len(str(size)) < 8): sys.stdout.write('%d\t\t' % size) if(len(str(size)) < 8): sys.stdout.write(f'{size}\t\t')
else: sys.stdout.write('%d\t' % size) else: sys.stdout.write(f'{size}\t')
sys.stdout.write('\n') sys.stdout.write('\n')
if(args.summary): if(args.summary):
@ -245,9 +245,9 @@ def ReportAsTable(benchmarkDict):
if(not args.summary): if(not args.summary):
for benchmark in benchmarkDict: for benchmark in benchmarkDict:
length = len(benchmark) length = len(benchmark)
if(length < 8): sys.stdout.write('%s\t\t\t' % benchmark) if(length < 8): sys.stdout.write(f'{benchmark}\t\t\t')
elif(length < 16): sys.stdout.write('%s\t\t' % benchmark) elif(length < 16): sys.stdout.write(f'{benchmark}\t\t')
else: sys.stdout.write('%s\t' % benchmark) else: sys.stdout.write(f'{benchmark}\t')
for (name, typ, entries, size, val) in benchmarkDict[benchmark]: 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('%0.2f\t\t' % (val if not args.invert else 100 -val))
sys.stdout.write('\n') sys.stdout.write('\n')
@ -256,14 +256,14 @@ def ReportAsText(benchmarkDict):
if(args.summary): if(args.summary):
mean = benchmarkDict['Mean'] mean = benchmarkDict['Mean']
print('Mean') print('Mean')
for (name, typ, entries. size, val) in 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)) 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): if(not args.summary):
for benchmark in benchmarkDict: for benchmark in benchmarkDict:
print(benchmark) print(benchmark)
for (name, type, entries, size, val) in benchmarkDict[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): def Inversion(lst):
return [x if not args.invert else 100 - x for x in 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 # the space between groups is 1
EffectiveNumInGroup = NumberInGroup + 2 EffectiveNumInGroup = NumberInGroup + 2
barWidth = 1 / EffectiveNumInGroup 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'] colors = ['blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'black', 'black', 'black', 'black', 'black', 'black']
for name in seriesDict: for name in seriesDict:
values = seriesDict[name] values = seriesDict[name]
@ -322,14 +322,13 @@ def ReportAsGraph(benchmarkDict, bar, FileName):
if(args.summary): if(args.summary):
markers = ['x', '.', '+', '*', '^', 'o', ',', 's'] markers = ['x', '.', '+', '*', '^', 'o', ',', 's']
colors = ['blue', 'black', 'gray', 'dodgerblue', 'lightsteelblue', 'turquoise', 'black', 'blue'] colors = ['blue', 'black', 'gray', 'dodgerblue', 'lightsteelblue', 'turquoise', 'black', 'blue']
temp = benchmarkDict['Mean']
# the benchmarkDict['Mean'] contains sequencies of results for multiple # the benchmarkDict['Mean'] contains sequencies of results for multiple
# branch predictors with various parameterizations # branch predictors with various parameterizations
# group the parameterizations by the common typ. # group the parameterizations by the common typ.
sequencies = {} sequencies = {}
for (name, typ, entries, size, value) in benchmarkDict['Mean']: 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)] sequencies[typ] = [(entries if not args.size else int(size/8), value)]
else: else:
sequencies[typ].append((entries if not args.size else int(size/8) ,value)) 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_xticks(xdata)
axes.set_xticklabels(xdata) axes.set_xticklabels(xdata)
axes.grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5) 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) else: plt.savefig(FileName)
# if(not args.summary): # if(not args.summary):
@ -378,10 +377,10 @@ def ReportAsGraph(benchmarkDict, bar, FileName):
if(not args.summary): if(not args.summary):
NumBenchmarks = len(benchmarkDict) NumBenchmarks = len(benchmarkDict)
NumBenchmarksSqrt = math.sqrt(NumBenchmarks) # NumBenchmarksSqrt = math.sqrt(NumBenchmarks)
isSquare = math.isclose(NumBenchmarksSqrt, round(NumBenchmarksSqrt)) # isSquare = math.isclose(NumBenchmarksSqrt, round(NumBenchmarksSqrt))
numCol = math.floor(NumBenchmarksSqrt) # numCol = math.floor(NumBenchmarksSqrt)
numRow = numCol + (0 if isSquare else 1) # numRow = numCol + (0 if isSquare else 1)
index = 1 index = 1
BenchPerRow = 5 BenchPerRow = 5
@ -414,7 +413,7 @@ def ReportAsGraph(benchmarkDict, bar, FileName):
# on each piece. # on each piece.
for row in range(0, math.ceil(NumBenchmarks / BenchPerRow)): for row in range(0, math.ceil(NumBenchmarks / BenchPerRow)):
(xlabelListTrunk, seriesDictTrunk) = SelectPartition(xlabelListBig, seriesDictBig, row, BenchPerRow) (xlabelListTrunk, seriesDictTrunk) = SelectPartition(xlabelListBig, seriesDictBig, row, BenchPerRow)
FileName = 'barSegment%d.svg' % row FileName = f'barSegment{row}.svg'
groupLen = len(xlabelListTrunk) groupLen = len(xlabelListTrunk)
BarGraph(seriesDictTrunk, xlabelListTrunk, groupLen, FileName, (row == 0)) BarGraph(seriesDictTrunk, xlabelListTrunk, groupLen, FileName, (row == 0))

View File

@ -4,6 +4,7 @@
# regression-wally # regression-wally
# David_Harris@Hmc.edu 25 January 2021 # David_Harris@Hmc.edu 25 January 2021
# Modified by Jarred Allen <jaallen@g.hmc.edu> and many others # Modified by Jarred Allen <jaallen@g.hmc.edu> and many others
# jcarlin@hmc.edu December 2024
# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 # SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
# #
# Run a regression with multiple configurations in parallel and exit with # Run a regression with multiple configurations in parallel and exit with
@ -11,65 +12,66 @@
# output. # output.
# #
################################## ##################################
import sys,os,shutil import sys
import shutil
import os
import argparse import argparse
import multiprocessing import multiprocessing
from collections import namedtuple 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 # Define lists of configurations and tests to run on each configuration
################################## ##################################
# The tests are a list with one element for 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 # optionally a string to pass to the simulator, and optionally a nonstandard grep string to check for success
tests = [ standard_tests = [
["rv32e", ["arch32e"]], ["rv32e", ["arch32e"]],
["rv32i", ["arch32i"]], ["rv32i", ["arch32i"]],
["rv32imc", ["arch32i", "arch32c", "arch32m", "wally32periph"]], ["rv32imc", ["arch32i", "arch32c", "arch32m", "wally32periph"]],
["rv32gc", ["arch32f", "arch32d", "arch32f_fma", "arch32d_fma", "arch32f_divsqrt", "arch32d_divsqrt", ["rv32gc", ["arch32f", "arch32d", "arch32f_fma", "arch32d_fma", "arch32f_divsqrt", "arch32d_divsqrt",
"arch32i", "arch32priv", "arch32c", "arch32m", "arch32a_amo", "arch32zifencei", "arch32zicond", "arch32i", "arch32priv", "arch32c", "arch32m", "arch32a_amo", "arch32zifencei", "arch32zicond",
"arch32zba", "arch32zbb", "arch32zbc", "arch32zbs", "arch32zfh", "arch32zfh_fma", "arch32zba", "arch32zbb", "arch32zbc", "arch32zbs", "arch32zfh", "arch32zfh_fma",
"arch32zfh_divsqrt", "arch32zfaf", "arch32zfad", "wally32a_lrsc", "wally32priv", "wally32periph", "arch32zcb", "arch32zfh_divsqrt", "arch32zfaf", "arch32zfad", "wally32a_lrsc", "wally32priv", "wally32periph", "arch32zcb",
"arch32zbkb", "arch32zbkc", "arch32zbkx", "arch32zknd", "arch32zkne", "arch32zknh", "arch32vm_sv32", "arch32pmp"]], "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 # Separate test for short buildroot run through OpenSBI UART output
tests_buildrootshort = [ 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"] "OpenSBI v", "buildroot_uart.out"]
] ]
# Separate test for full buildroot run # Separate test for full buildroot run
tests_buildrootboot = [ 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"] "WallyHostname login: ", "buildroot_uart.out"]
] ]
tests_buildrootbootlockstep = [ 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"] "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 = [ derivconfigtests = [
# memory system # memory system
["tlb2_rv32gc", ["wally32priv"]], ["tlb2_rv32gc", ["wally32priv"]],
@ -92,21 +94,21 @@ derivconfigtests = [
["ram_1_1_rv64gc", ["ahb64"]], ["ram_1_1_rv64gc", ["ahb64"]],
["ram_2_0_rv64gc", ["ahb64"]], ["ram_2_0_rv64gc", ["ahb64"]],
["ram_2_1_rv64gc", ["ahb64"]], ["ram_2_1_rv64gc", ["ahb64"]],
# RV32 cacheless designs will not work unless DTIM supports FLEN > XLEN. This support is not planned. # RV32 cacheless designs will not work unless DTIM supports FLEN > XLEN. This support is not planned.
# ["nodcache_rv32gc", ["ahb32"]], # ["nodcache_rv32gc", ["ahb32"]],
# ["nocache_rv32gc", ["ahb32"]], # ["nocache_rv32gc", ["ahb32"]],
["noicache_rv32gc", ["ahb32"]], ["noicache_rv32gc", ["ahb32"]],
["noicache_rv64gc", ["ahb64"]], ["noicache_rv64gc", ["ahb64"]],
["nodcache_rv64gc", ["ahb64"]], ["nodcache_rv64gc", ["ahb64"]],
["nocache_rv64gc", ["ahb64"]], ["nocache_rv64gc", ["ahb64"]],
# Atomic variants # Atomic variants
["zaamo_rv64gc", ["arch64i", "arch64a_amo"]], ["zaamo_rv64gc", ["arch64i", "arch64a_amo"]],
["zalrsc_rv64gc", ["arch64i", "wally64a_lrsc"]], ["zalrsc_rv64gc", ["arch64i", "wally64a_lrsc"]],
["zaamo_rv32gc", ["arch32i", "arch32a_amo"]], ["zaamo_rv32gc", ["arch32i", "arch32a_amo"]],
["zalrsc_rv32gc", ["arch32i", "wally32a_lrsc"]], ["zalrsc_rv32gc", ["arch32i", "wally32a_lrsc"]],
# Bit manipulation and crypto variants # Bit manipulation and crypto variants
["zba_rv32gc", ["arch32i", "arch32zba"]], ["zba_rv32gc", ["arch32i", "arch32zba"]],
["zbb_rv32gc", ["arch32i", "arch32zbb"]], ["zbb_rv32gc", ["arch32i", "arch32zbb"]],
["zbc_rv32gc", ["arch32i", "arch32zbc"]], ["zbc_rv32gc", ["arch32i", "arch32zbc"]],
@ -129,7 +131,7 @@ derivconfigtests = [
["zknd_rv64gc", ["arch64i", "arch64zknd"]], ["zknd_rv64gc", ["arch64i", "arch64zknd"]],
["zknh_rv64gc", ["arch64i", "arch64zknh"]], ["zknh_rv64gc", ["arch64i", "arch64zknh"]],
# No privilege modes variants # No privilege modes variants
["noS_rv32gc", ["arch32i", "arch32f", "arch32priv", "arch32c", "arch32m", "arch32a_amo", "arch32zifencei", "arch32zicond", ["noS_rv32gc", ["arch32i", "arch32f", "arch32priv", "arch32c", "arch32m", "arch32a_amo", "arch32zifencei", "arch32zicond",
"arch32zba", "arch32zfaf", "arch32zfad", "wally32a_lrsc", "arch32zcb", "arch32zbkx", "arch32zknd"]], "arch32zba", "arch32zfaf", "arch32zfad", "wally32a_lrsc", "arch32zcb", "arch32zbkx", "arch32zknd"]],
["noS_rv64gc", ["arch64i", "arch64f", "arch64priv", "arch64c", "arch64m", "arch64a_amo", "arch64zifencei", "arch64zicond", ["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_2_rv64gc", ["arch64f_divsqrt", "arch64d_divsqrt", "arch64m"]],
["div_4_2i_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_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 # fpu permutations
["f_rv32gc", ["arch32f", "arch32f_divsqrt", "arch32f_fma", "arch32zfaf"]], ["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"]], ["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"]], ["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"]], ["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"]], ["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"]], ["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 ["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 = [ bpredtests = [
["nobpred_rv32gc", ["rv32i"]], ["nobpred_rv32gc", ["rv32i"]],
["bpred_TWOBIT_6_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"], ["bpred_TWOBIT_6_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"],
["bpred_TWOBIT_8_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_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_16_16_10_0_rv32gc", ["embench"], "-GPrintHPMCounters=1"],
["bpred_TWOBIT_6_16_10_1_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_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_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_TWOBIT_16_16_10_1_rv32gc", ["embench"], "-GPrintHPMCounters=1"],
["bpred_GSHARE_6_16_10_0_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"] ["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 # list of tests not supported by ImperasDV yet that should be waived during lockstep testing
lockstepwaivers = [ lockstepwaivers = [
"WALLY-q-01.S_ref.elf", # Q extension is not supported by ImperasDV "WALLY-q-01.S_ref.elf", # Q extension is not supported by ImperasDV
@ -261,109 +289,76 @@ class bcolors:
BOLD = '\033[1m' BOLD = '\033[1m'
UNDERLINE = '\033[4m' UNDERLINE = '\033[4m'
def addTests(tests, sim):
sim_logdir = WALLY+ "/sim/" + sim + "/logs/" def addTests(testList, sim, coverStr, configs):
for test in tests: sim_logdir = f"{regressionDir}/{sim}/logs/"
for test in testList:
config = test[0] config = test[0]
suites = test[1] suites = test[1]
if (len(test) >= 3): flags = f"{test[2]}" if len(test) >= 3 else ""
args = " --args " + " ".join(test[2]) gs = test[3] if len(test) >= 4 else "All tests ran without failures"
else: cmdPrefix=f"wsim --sim {sim} {coverStr} {flags} {config}"
args = ""
if (len(test) >= 4):
gs = test[3]
else:
gs = "All tests ran without failures"
cmdPrefix="wsim --sim " + sim + " " + coverStr + " " + config
for t in suites: for t in suites:
sim_log = sim_logdir + config + "_" + t + ".log" sim_log = f"{sim_logdir}{config}_{t}.log"
if (len(test) >= 5): grepfile = sim_logdir + test[4] if len(test) >= 5 else sim_log
grepfile = sim_logdir + test[4]
else:
grepfile = sim_log
tc = TestCase( tc = TestCase(
name=t, name=t,
variant=config, variant=config,
cmd=cmdPrefix + " " + t + args + " > " + sim_log, cmd=f"{cmdPrefix} {t} > {sim_log}",
grepstr=gs, grepstr=gs,
grepfile = grepfile) grepfile = grepfile)
configs.append(tc) configs.append(tc)
def addTestsByDir(dir, config, sim, lockstepMode=0, brekerMode=0): def addTestsByDir(testDir, config, sim, coverStr, configs, lockstepMode=0, brekerMode=0):
if os.path.isdir(dir): if not os.path.isdir(testDir):
sim_logdir = WALLY+ "/sim/" + sim + "/logs/" print(f"Error: Directory not found: {testDir}")
if coverStr == "--fcov": # use --fcov in place of --lockstep sys.exit(1)
cmdPrefix="wsim --sim " + sim + " " + coverStr + " " + config
gs="Mismatches : 0" sim_logdir = f"{regressionDir}/{sim}/logs/"
if ("cvw-arch-verif/tests" in dir and not "priv" in dir): cmdPrefix = f"wsim --sim {sim} {coverStr} {'--lockstep' if lockstepMode else ''} {config}"
fileEnd = "ALL.elf" # fcov/ccov only runs on WALLY-COV-ALL.elf files; other lockstep runs on all files
else: fileEnd = "ALL.elf" if "cvw-arch-verif/tests" in testDir and "priv" not in testDir and (coverStr == "--fcov" or coverStr == "--ccov") else ".elf"
fileEnd = ".elf" if lockstepMode or coverStr == "--fcov":
elif coverStr == "--ccov": gs = "Mismatches : 0"
cmdPrefix="wsim --sim " + sim + " " + coverStr + " " + config elif brekerMode:
gs="Single Elf file tests are not signatured verified." gs="# trek: info: summary: Test PASSED"
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)
else: else:
print("Error: Directory not found: " + dir) gs = "Single Elf file tests are not signatured verified."
exit(1) 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): 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""" with open(grepfile, errors="ignore") as file:
grepwarn = "grep -i -H Warning: " + grepfile content = file.readlines()
os.system(grepwarn) for line in content:
greperr = "grep -i -H Error: " + grepfile if "warning:" in line.lower():
os.system(greperr) print(f"{bcolors.WARNING}{line.strip()}{bcolors.ENDC}")
grepcmd = "grep -a -e '%s' '%s' > /dev/null" % (text, grepfile) if "error:" in line.lower():
# print(" search_log_for_text invoking %s" % grepcmd) print(f"{bcolors.FAIL}{line.strip()}{bcolors.ENDC}")
return os.system(grepcmd) == 0 return any(text in line for line in content)
def run_test_case(config, dryrun: bool = False): 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 grepfile = config.grepfile
cmd = config.cmd cmd = config.cmd
os.chdir(regressionDir)
if dryrun: if dryrun:
print(f"Executing {cmd}", flush=True) print(f"Executing {cmd}", flush=True)
return 0 return 0
@ -371,194 +366,130 @@ def run_test_case(config, dryrun: bool = False):
os.system(cmd) os.system(cmd)
if search_log_for_text(config.grepstr, grepfile): if search_log_for_text(config.grepstr, grepfile):
# Flush is needed to flush output to stdout when running in multiprocessing Pool # 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}{cmd}: Success{bcolors.ENDC}", flush=True)
print(f"{bcolors.OKGREEN}%s: Success{bcolors.ENDC}" % (config.cmd), flush=True)
return 0 return 0
else: else:
print(f"{bcolors.FAIL}%s: Failures detected in output{bcolors.ENDC}" % (config.cmd), flush=True) print(f"{bcolors.FAIL}{cmd}: Failures detected in output{bcolors.ENDC}", flush=True)
print(" Check %s" % grepfile) print(f" Check {grepfile}", flush=True)
return 1 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') def process_args(args):
regressionDir = WALLY + '/sim' coverStr = ""
os.chdir(regressionDir) # exercise all simulators in nightly; can omit a sim if no license is available
sims = ["questa", "verilator", "vcs"] if args.nightly else [defaultsim]
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
if args.ccov: if args.ccov:
coverStr = "--ccov"
TIMEOUT_DUR = 20*60 # seconds 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: elif args.fcov:
coverStr = "--fcov"
TIMEOUT_DUR = 8*60 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: elif args.buildroot:
TIMEOUT_DUR = 60*1440 # 1 day TIMEOUT_DUR = 60*1440 # 1 day
elif args.testfloat: elif args.testfloat or args.nightly:
TIMEOUT_DUR = 30*60 # seconds
elif args.nightly:
TIMEOUT_DUR = 30*60 # seconds TIMEOUT_DUR = 30*60 # seconds
else: else:
TIMEOUT_DUR = 10*60 # seconds 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 # 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 # max out at a limited number of concurrent processes to not overwhelm the system
# right now fcov, ccov, nightly all use Imperas # right now fcov and nightly use Imperas
if (args.ccov or args.fcov or args.nightly): ImperasDVLicenseCount = 16 if args.fcov or args.nightly else 10000
ImperasDVLicenseCount = 16 # limit number of concurrent processes to avoid overloading ImperasDV licenses
else:
ImperasDVLicenseCount = 10000 # effectively no license limit for non-lockstep tests
with Pool(processes=min(len(configs),multiprocessing.cpu_count(), ImperasDVLicenseCount)) as pool: with Pool(processes=min(len(configs),multiprocessing.cpu_count(), ImperasDVLicenseCount)) as pool:
num_fail = 0 num_fail = 0
results = {} results = {}
@ -567,22 +498,24 @@ def main():
for (config,result) in results.items(): for (config,result) in results.items():
try: try:
num_fail+=result.get(timeout=TIMEOUT_DUR) num_fail+=result.get(timeout=TIMEOUT_DUR)
except TimeoutError: except MPTimeoutError:
pool.terminate() pool.terminate()
pool.join()
num_fail+=1 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 # Coverage report
if args.ccov: if args.ccov:
os.system('make QuestaCodeCoverage') os.system(f"make -C {regressionDir}/QuestaCodeCoverage")
if args.fcov: 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 # Count the number of failures
if num_fail: 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: else:
print(f"{bcolors.OKGREEN}SUCCESS! All tests ran without failures{bcolors.ENDC}") print(f"{bcolors.OKGREEN}SUCCESS! All tests ran without failures{bcolors.ENDC}")
return num_fail return num_fail
if __name__ == '__main__': if __name__ == '__main__':
exit(main()) args = parse_args()
sys.exit(main(args))

View File

@ -16,7 +16,7 @@ import os
import sys import sys
# Global variable # Global variable
WALLY = os.environ.get('WALLY') WALLY = os.environ.get("WALLY")
def parseArgs(): def parseArgs():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
@ -41,13 +41,13 @@ def validateArgs(args):
if not args.testsuite and not args.elf: if not args.testsuite and not args.elf:
print("Error: Missing test suite or ELF file") print("Error: Missing test suite or ELF file")
sys.exit(1) 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.") print(f"Invalid Options. Cannot run a testsuite, {args.testsuite} with lockstep or fcov. Must run a single elf or buildroot.")
sys.exit(1) sys.exit(1)
elif any([args.gui, args.ccov, args.fcov, args.lockstep, args.lockstepverbose]) and args.sim not in ["questa", "vcs"]: 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") print("Option only supported for Questa and VCS")
sys.exit(1) 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") print("Error: testbench_fp presently only supported by Questa, not VCS or Verilator, because of a touchy testbench")
sys.exit(1) sys.exit(1)
elif (args.config == "breker" and args.sim != "questa"): elif (args.config == "breker" and args.sim != "questa"):
@ -61,11 +61,11 @@ def elfFileCheck(args):
elif args.elf: elif args.elf:
print(f"ELF file not found: {args.elf}") print(f"ELF file not found: {args.elf}")
sys.exit(1) 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): if os.path.isfile(args.testsuite):
ElfFile = os.path.abspath(args.testsuite) ElfFile = os.path.abspath(args.testsuite)
# extract the elf name from the path to be the test suite # 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 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: if "breker" in args.testsuite:
args.testsuite = fields[-1] args.testsuite = fields[-1]
@ -74,8 +74,8 @@ def elfFileCheck(args):
args.testsuite = f"{fields[1]}_{fields[3]}" args.testsuite = f"{fields[1]}_{fields[3]}"
else: else:
args.testsuite = f"{fields[2]}_{fields[3]}" args.testsuite = f"{fields[2]}_{fields[3]}"
elif '/' in args.testsuite: elif "/" in args.testsuite:
args.testsuite=args.testsuite.rsplit('/', 1)[1] # strip off path if present args.testsuite = args.testsuite.rsplit("/", 1)[1] # strip off path if present
else: else:
print(f"ELF file not found: {args.testsuite}") print(f"ELF file not found: {args.testsuite}")
sys.exit(1) sys.exit(1)
@ -116,9 +116,9 @@ def prepSim(args, ElfFile):
defineList.append("+define+USE_TREK_DV") defineList.append("+define+USE_TREK_DV")
argsList.append(f"+TREK_TBX_FILE={ElfFileNoExtension}.tbx") argsList.append(f"+TREK_TBX_FILE={ElfFileNoExtension}.tbx")
# Combine into a single string # Combine into a single string
args.args += " ".join(argsList) args.args += " " + " ".join(argsList)
args.params += " ".join(paramsList) args.params += " " + " ".join(paramsList)
args.define += " ".join(defineList) args.define += " " + " ".join(defineList)
flags = " ".join(flagsList) flags = " ".join(flagsList)
return flags, prefix return flags, prefix
@ -154,7 +154,7 @@ def runQuesta(args, flags, prefix):
args.params = fr'--params \"{args.params}\"' args.params = fr'--params \"{args.params}\"'
if args.define: if args.define:
args.define = fr'--define \"{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"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}"' cmd = f'cd $WALLY/sim/questa; {prefix} vsim {"-c" if not args.gui else ""} -do "{cmd}"'
print(f"Running Questa with command: {cmd}") print(f"Running Questa with command: {cmd}")

View File

@ -1,4 +1,5 @@
import shutil, os import shutil
import os
# if WALLY is defined, then get it # if WALLY is defined, then get it
WALLY_HOME = os.getenv("WALLY") WALLY_HOME = os.getenv("WALLY")
@ -10,4 +11,4 @@ BUILDROOT_SRC = "linux/buildroot-config-src/wally"
TESTVECTOR_SRC = "linux/testvector-generation" TESTVECTOR_SRC = "linux/testvector-generation"
shutil.copytree(os.path.join(WALLY_HOME, BUILDROOT_SRC), "./buildroot-config-src") shutil.copytree(os.path.join(WALLY_HOME, BUILDROOT_SRC), "./buildroot-config-src")
shutil.copytree(os.path.join(WALLY_HOME, TESTVECTOR_SRC), "./testvector-generation") shutil.copytree(os.path.join(WALLY_HOME, TESTVECTOR_SRC), "./testvector-generation")

View File

@ -13,7 +13,7 @@ def main(args):
probenum = 0 probenum = 0
countLines = 1 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() Lines = xdcfile.readlines()
for line in Lines: for line in Lines:
t = re.sub("probe[0-9]+", f"probe{probenum}",line) t = re.sub("probe[0-9]+", f"probe{probenum}",line)

View File

@ -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/dut/core/ifu/InstrE
add wave -noupdate -group {Execution Stage} /testbench/InstrEName 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/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/PCM
add wave -noupdate -expand -group {Memory Stage} /testbench/dut/core/InstrM add wave -noupdate -expand -group {Memory Stage} /testbench/dut/core/InstrM
add wave -noupdate -expand -group {Memory Stage} /testbench/InstrMName add wave -noupdate -expand -group {Memory Stage} /testbench/InstrMName

View File

@ -67,8 +67,8 @@ def main():
parser.add_argument('-d', "--dist", action='store_true', help="Report distribution of operations") 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") parser.add_argument('-s', "--sim", help="Simulator", choices=["questa", "verilator", "vcs"], default="verilator")
args = parser.parse_args() args = parser.parse_args()
simargs = "I_CACHE_ADDR_LOGGER=1\\\'b1 D_CACHE_ADDR_LOGGER=1\\\'b1" simargs = "I_CACHE_ADDR_LOGGER=1\\'b1 D_CACHE_ADDR_LOGGER=1\\'b1"
testcmd = "wsim --sim " + args.sim + " rv64gc {} --params \"" + simargs + "\" > /dev/null" 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 {} --verbose"
cachecmd = "CacheSim.py 64 4 56 44 -f {}" cachecmd = "CacheSim.py 64 4 56 44 -f {}"
mismatches = 0 mismatches = 0

View File

@ -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 (UTC08: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.')

View File

@ -12,7 +12,7 @@ import subprocess
import sys import sys
# Global variables # Global variables
WALLY = os.environ.get('WALLY') WALLY = os.environ.get("WALLY")
simdir = f"{WALLY}/sim/vcs" simdir = f"{WALLY}/sim/vcs"
cfgdir = f"{WALLY}/config" cfgdir = f"{WALLY}/config"
srcdir = f"{WALLY}/src" 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 # run a Linux command and return the result as a string in a form that VCS can use
def runFindCommand(cmd): def runFindCommand(cmd):
res = subprocess.check_output(cmd, shell=True, ) res = subprocess.check_output(cmd, shell=True)
res = str(res) res = str(res)
res = res.replace("\\n", " ") # replace newline with space 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 res = res[1:] # strip off leading b from byte string
return res return res
@ -42,20 +42,20 @@ def parseArgs():
#parser.add_argument("--gui", "-g", help="Simulate with GUI", action="store_true") # GUI not yet implemented #parser.add_argument("--gui", "-g", help="Simulate with GUI", action="store_true") # GUI not yet implemented
return parser.parse_args() return parser.parse_args()
def createDirs(args): def createDirs(config, testsuite):
wkdir = f"{simdir}/wkdir/{args.config}_{args.testsuite}" wkdir = f"{simdir}/wkdir/{config}_{testsuite}"
covdir = f"{simdir}/cov/{args.config}_{args.testsuite}" covdir = f"{simdir}/cov/{config}_{testsuite}"
os.makedirs(wkdir, exist_ok=True) os.makedirs(wkdir, exist_ok=True)
os.makedirs(covdir, exist_ok=True) os.makedirs(covdir, exist_ok=True)
os.makedirs(logdir, 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_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) rtlsrc_files = runFindCommand(rtlsrc_cmd)
tbcommon_cmd = f'find {tbdir}/common -name "*.sv"' tbcommon_cmd = f'find {tbdir}/common -name "*.sv"'
tbcommon_files = runFindCommand(tbcommon_cmd) 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}" return f"{tb_file} {rtlsrc_files} {tbcommon_files}"
def processArgs(wkdir, args): def processArgs(wkdir, args):
@ -76,7 +76,7 @@ def processArgs(wkdir, args):
# if args.gui: # if args.gui:
# compileOptions.append("-debug_access+all+reverse -kdb +vcs+vcdpluson") # compileOptions.append("-debug_access+all+reverse -kdb +vcs+vcdpluson")
compileOptions = " ".join(compileOptions) compileOptions = " ".join(compileOptions)
simvOptions = " ".join(simvOptions) simvOptions = " ".join(simvOptions)
return compileOptions, simvOptions return compileOptions, simvOptions
def setupParamOverrides(wkdir, args): def setupParamOverrides(wkdir, args):
@ -84,35 +84,35 @@ def setupParamOverrides(wkdir, args):
with open(paramOverrideFile, "w") as f: with open(paramOverrideFile, "w") as f:
for param in args.params.split(): for param in args.params.split():
[param, value] = param.split("=") [param, value] = param.split("=")
if fr"\'" in value: # for bit values value = value.replace("\\'", "'") if "\\'" in value else f'"{value}"' # transform quotes/bit indicators
value = value.replace(fr"\'", "'")
else: # for strings
value = f'"{value}"'
f.write(f"assign {value} {args.tb}/{param}\n") f.write(f"assign {value} {args.tb}/{param}\n")
return f" -parameters {wkdir}/param_overrides.txt " return f" -parameters {wkdir}/param_overrides.txt "
def setupCommands(wkdir, rtlFiles, compileOptions, simvOptions, args): 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" 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" 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}" simvCMD = f"{wkdir}/sim_out +TEST={args.testsuite} {args.args} -no_save {simvOptions}"
return vcsCMD, simvCMD return vcsCMD, simvCMD
def runVCS(wkdir, vcsCMD, simvCMD): def runVCS(vcsCMD, simvCMD):
print(f"Executing: {vcsCMD}") print(f"Executing: {vcsCMD}")
subprocess.run(vcsCMD, shell=True) subprocess.run(vcsCMD, shell=True, check=True)
subprocess.run(simvCMD, shell=True) subprocess.run(simvCMD, shell=True, check=True)
if (args.ccov):
COV_RUN = f"urg -dir {wkdir}/coverage.vdb -format text -report IndividualCovReport/{args.config}_{args.testsuite}" def runCoverage(wkdir, config, testsuite):
subprocess.run(COV_RUN, shell=True) 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): 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}'") 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) wkdir = createDirs(args.config, args.testsuite)
rtlFiles = generateFileList() rtlFiles = generateFileList(args.tb)
compileOptions, simvOptions = processArgs(wkdir, args) compileOptions, simvOptions = processArgs(wkdir, args)
vcsCMD, simvCMD = setupCommands(wkdir, rtlFiles, compileOptions, simvOptions, 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__": if __name__ == "__main__":
args = parseArgs() args = parseArgs()

View File

@ -27,50 +27,46 @@ def synthsintocsv():
specReg = re.compile('[a-zA-Z0-9]+') specReg = re.compile('[a-zA-Z0-9]+')
metricReg = re.compile('-?\d+\.\d+[e]?[-+]?\d*') metricReg = re.compile('-?\d+\.\d+[e]?[-+]?\d*')
file = open("Summary.csv", "w") with open("Summary.csv", "w") as file:
writer = csv.writer(file) writer = csv.writer(file)
writer.writerow(['Width', 'Config', 'Mod', 'Tech', 'Target Freq', 'Delay', 'Area']) 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): def synthsfromcsv(filename):
Synth = namedtuple("Synth", "width config mod tech freq delay area") Synth = namedtuple("Synth", "width config mod tech freq delay area")
with open(filename, newline='') as csvfile: with open(filename, newline='') as csvfile:
@ -93,7 +89,7 @@ def freqPlot(tech, width, config):
freqsL, delaysL, areasL = ([[], []] for i in range(3)) freqsL, delaysL, areasL = ([[], []] for i in range(3))
for oneSynth in allSynths: 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 ind = (1000/oneSynth.delay < (0.95*oneSynth.freq)) # when delay is within target clock period
freqsL[ind] += [oneSynth.freq] freqsL[ind] += [oneSynth.freq]
delaysL[ind] += [oneSynth.delay] delaysL[ind] += [oneSynth.delay]
@ -101,10 +97,7 @@ def freqPlot(tech, width, config):
fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)
allFreqs = list(flatten(freqsL)) allFreqs = list(flatten(freqsL))
if allFreqs != []: median = np.median(allFreqs) if allFreqs != [] else 0
median = np.median(allFreqs)
else:
median = 0
for ind in [0,1]: for ind in [0,1]:
areas = areasL[ind] areas = areasL[ind]
@ -169,11 +162,10 @@ def plotFeatures(tech, width, config):
delays, areas, labels = ([] for i in range(3)) delays, areas, labels = ([] for i in range(3))
freq = techdict[tech].targfreq freq = techdict[tech].targfreq
for oneSynth in allSynths: for oneSynth in allSynths:
if (tech == oneSynth.tech) & (freq == oneSynth.freq): if (tech == oneSynth.tech) & (freq == oneSynth.freq) & (oneSynth.config == config) & (width == oneSynth.width):
if (oneSynth.config == config) & (width == oneSynth.width): delays += [oneSynth.delay]
delays += [oneSynth.delay] areas += [oneSynth.area]
areas += [oneSynth.area] labels += [oneSynth.mod]
labels += [oneSynth.mod]
if (delays == []): if (delays == []):
print("No delays found for tech ", tech, " freq ", freq, ". Did you set --sky130freq, --sky90freq and --tsmcfreq?\n") 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.title(titlestr)
plt.savefig(final_directory + '/features_'+titlestr+'.png') plt.savefig(final_directory + '/features_'+titlestr+'.png')
def plotConfigs(tech, mod=''): def plotConfigs(tech, mod=''):
delays, areas, labels = ([] for i in range(3)) delays, areas, labels = ([] for i in range(3))
freq = techdict[tech].targfreq freq = techdict[tech].targfreq
@ -227,7 +219,7 @@ def normAreaDelay(mod=''):
ax.legend(handles = fullLeg, loc='upper left') ax.legend(handles = fullLeg, loc='upper left')
plt.savefig(final_directory + '/normAreaDelay.png') plt.savefig(final_directory + '/normAreaDelay.png')
def addFO4axis(fig, ax, tech): def addFO4axis(fig, ax, tech):
fo4 = techdict[tech].fo4 fo4 = techdict[tech].fo4
@ -282,4 +274,4 @@ if __name__ == '__main__':
plotConfigs('sky130', mod='orig') plotConfigs('sky130', mod='orig')
plotConfigs('tsmc28psyn', mod='orig') plotConfigs('tsmc28psyn', mod='orig')
normAreaDelay(mod='orig') normAreaDelay(mod='orig')
os.system("./extractArea.pl"); os.system("./extractArea.pl")

View File

@ -50,49 +50,48 @@ def synthsintocsv():
specReg = re.compile("[a-zA-Z0-9]+") specReg = re.compile("[a-zA-Z0-9]+")
metricReg = re.compile("-?\d+\.\d+[e]?[-+]?\d*") metricReg = re.compile("-?\d+\.\d+[e]?[-+]?\d*")
file = open("ppaData.csv", "w") with open("ppaData.csv", "w") as file:
writer = csv.writer(file) writer = csv.writer(file)
writer.writerow( writer.writerow(
[ [
"Module", "Module",
"Tech", "Tech",
"Width", "Width",
"Target Freq", "Target Freq",
"Delay", "Delay",
"Area", "Area",
"L Power (nW)", "L Power (nW)",
"D energy (nJ)", "D energy (nJ)",
] ]
) )
for oneSynth in allSynths: for oneSynth in allSynths:
module, width, risc, tech, freq = specReg.findall(oneSynth)[1:6] module, width, risc, tech, freq = specReg.findall(oneSynth)[1:6]
tech = tech[:-2] tech = tech[:-2]
metrics = [] metrics = []
for phrase in [["Path Slack", "qor"], ["Design Area", "qor"], ["100", "power"]]: for phrase in [["Path Slack", "qor"], ["Design Area", "qor"], ["100", "power"]]:
bashCommand = 'grep "{}" ' + oneSynth[2:] + "/reports/*{}*" bashCommand = 'grep "{}" ' + oneSynth[2:] + "/reports/*{}*"
bashCommand = bashCommand.format(*phrase) bashCommand = bashCommand.format(*phrase)
try: try:
output = subprocess.check_output(["bash", "-c", bashCommand]) output = subprocess.check_output(["bash", "-c", bashCommand])
except: except:
print(module + width + tech + freq + " doesn't have reports") print(module + width + tech + freq + " doesn't have reports")
print("Consider running cleanup() first") print("Consider running cleanup() first")
nums = metricReg.findall(str(output)) nums = metricReg.findall(str(output))
nums = [float(m) for m in nums] nums = [float(m) for m in nums]
metrics += nums metrics += nums
delay = 1000 / int(freq) - metrics[0] delay = 1000 / int(freq) - metrics[0]
area = metrics[1] area = metrics[1]
lpower = metrics[4] lpower = metrics[4]
tpower = (metrics[2] + metrics[3] + metrics[4]*.000001) tpower = (metrics[2] + metrics[3] + metrics[4]*.000001)
denergy = ( denergy = (
(tpower) / int(freq) * 1000 (tpower) / int(freq) * 1000
) # (switching + internal powers)*delay, more practical units for regression coefs ) # (switching + internal powers)*delay, more practical units for regression coefs
if "flop" in module: # since two flops in each module if "flop" in module: # since two flops in each module
[area, lpower, denergy] = [n / 2 for n in [area, lpower, denergy]] [area, lpower, denergy] = [n / 2 for n in [area, lpower, denergy]]
writer.writerow([module, tech, width, freq, delay, area, lpower, denergy]) writer.writerow([module, tech, width, freq, delay, area, lpower, denergy])
file.close()
def cleanup(): 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 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 if width is not None else widths
widthsToGet = width
else:
widthsToGet = widths
metric = [] metric = []
widthL = [] widthL = []
if freq != None: if freq is not None:
for oneSynth in allSynths: for oneSynth in allSynths:
if ( if (
(oneSynth.freq == freq) (oneSynth.freq == freq)
@ -171,37 +167,30 @@ def csvOfBest(filename):
m = np.Inf # large number to start m = np.Inf # large number to start
best = None best = None
for oneSynth in allSynths: # best achievable, rightmost green for oneSynth in allSynths: # best achievable, rightmost green
if ( if (oneSynth.width == w) & (oneSynth.tech == tech) & (oneSynth.module == mod):
(oneSynth.width == w) if (oneSynth.delay < m) & (1000 / oneSynth.delay > oneSynth.freq):
& (oneSynth.tech == tech)
& (oneSynth.module == mod)
):
if (oneSynth.delay < m) & (
1000 / oneSynth.delay > oneSynth.freq
):
m = oneSynth.delay m = oneSynth.delay
best = oneSynth best = oneSynth
if (best != None) & (best not in bestSynths): if (best is not None) & (best not in bestSynths):
bestSynths += [best] bestSynths += [best]
file = open(filename, "w") with open(filename, "w") as file:
writer = csv.writer(file) writer = csv.writer(file)
writer.writerow( writer.writerow(
[ [
"Module", "Module",
"Tech", "Tech",
"Width", "Width",
"Target Freq", "Target Freq",
"Delay", "Delay",
"Area", "Area",
"L Power (nW)", "L Power (nW)",
"D energy (nJ)", "D energy (nJ)",
] ]
) )
for synth in bestSynths: for synth in bestSynths:
writer.writerow(list(synth)) writer.writerow(list(synth))
file.close()
return bestSynths return bestSynths
@ -229,7 +218,7 @@ def genLegend(fits, coefs, r2=None, spec=None, ale=False):
eq = "" eq = ""
ind = 0 ind = 0
for k in eqDict.keys(): for k in eqDict:
if k in fits: if k in fits:
if str(coefsr[ind]) != "0": if str(coefsr[ind]) != "0":
eq += " + " + coefsr[ind] + eqDict[k] 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 ' + ' eq = eq[3:] # chop off leading ' + '
if (r2 == None) or (spec == None): if (r2 is None) or (spec is None):
return eq return eq
else: else:
legend_elements = [lines.Line2D([0], [0], color=spec.color, label=eq)] legend_elements = [lines.Line2D([0], [0], color=spec.color, label=eq)]
@ -277,10 +266,7 @@ def oneMetricPlot(
modFit = fitDict[module] modFit = fitDict[module]
fits = modFit[ale] fits = modFit[ale]
if freq: ls = "--" if freq else "-"
ls = "--"
else:
ls = "-"
for spec in techSpecs: for spec in techSpecs:
# print(f"Searching for module of spec {spec} and module {module} and var {var}") # 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)) ax.add_artist(ax.legend(handles=fullLeg, loc=legLoc))
titleStr = ( titleStr = (
" (target " + str(freq) + "MHz)" " (target " + str(freq) + "MHz)"
if freq != None if freq is not None
else " (best achievable delay)" else " (best achievable delay)"
) )
ax.set_title(module + titleStr) ax.set_title(module + titleStr)
@ -403,72 +389,16 @@ def makeCoefTable():
"""writes CSV with each line containing the coefficients for a regression fit """writes CSV with each line containing the coefficients for a regression fit
to a particular combination of module, metric (including both techs, normalized) to a particular combination of module, metric (including both techs, normalized)
""" """
file = open("ppaFitting.csv", "w") with open("ppaFitting.csv", "w") as file:
writer = csv.writer(file) writer = csv.writer(file)
writer.writerow( writer.writerow(
["Module", "Metric", "Target", "1", "N", "N^2", "log2(N)", "Nlog2(N)", "R^2"] ["Module", "Metric", "Target", "1", "N", "N^2", "log2(N)", "Nlog2(N)", "R^2"]
) )
for module in modules: for module in modules:
for freq in [10, None]: for freq in [10, None]:
target = "easy" if freq else "hard" target = "easy" if freq else "hard"
for var in ["delay", "area", "lpower", "denergy"]: 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:
ale = var != "delay" ale = var != "delay"
metL = [] metL = []
modFit = fitDict[module] modFit = fitDict[module]
@ -482,12 +412,63 @@ def makeEqTable():
xp, pred, coefs, r2 = regress(widths * 2, metL, fits, ale) xp, pred, coefs, r2 = regress(widths * 2, metL, fits, ale)
coefs = np.ndarray.tolist(coefs) coefs = np.ndarray.tolist(coefs)
eqs += [genLegend(fits, coefs, ale=ale)] coefsToWrite = [None] * 5
row = [module] + eqs fitTerms = "clsgn"
writer.writerow(row) 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"): def genFuncs(fits="clsgn"):
"""helper function for regress() """helper function for regress()
@ -719,7 +700,7 @@ def plotPPA(mod, freq=None, norm=True, aleOpt=False):
else: else:
axs[i, j].legend(handles=leg, handlelength=1.5) 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.suptitle(mod + titleStr)
plt.tight_layout(pad=0.05, w_pad=1, h_pad=0.5, rect=(0, 0, 1, 0.97)) 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] norm = techdict[var]
metL += [m / norm for m in metric] metL += [m / norm for m in metric]
if ale: ws = [w / normAddWidth for w in widths] if ale else widths
ws = [w / normAddWidth for w in widths]
else:
ws = widths
ws = ws * 2 ws = ws * 2
mat = [] mat = []
for w in ws: for w in ws:
@ -896,7 +874,7 @@ if __name__ == "__main__":
"flop": ["c", "l", "l"], "flop": ["c", "l", "l"],
"binencoder": ["cg", "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") TechSpec = namedtuple("TechSpec", "tech color shape delay area lpower denergy")
# FO4 delay information information # FO4 delay information information

View File

@ -11,7 +11,7 @@ from multiprocessing import Pool
from ppaAnalyze import synthsfromcsv from ppaAnalyze import synthsfromcsv
def runCommand(module, width, tech, freq): 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) subprocess.call(command, shell=True)
def deleteRedundant(synthsToRun): def deleteRedundant(synthsToRun):
@ -19,7 +19,7 @@ def deleteRedundant(synthsToRun):
synthStr = "rm -rf runs/{}_{}_rv32e_{}_{}_*" synthStr = "rm -rf runs/{}_{}_rv32e_{}_{}_*"
for synth in synthsToRun: for synth in synthsToRun:
bashCommand = synthStr.format(*synth) bashCommand = synthStr.format(*synth)
outputCPL = subprocess.check_output(['bash','-c', bashCommand]) subprocess.check_output(['bash','-c', bashCommand])
def freqSweep(module, width, tech): def freqSweep(module, width, tech):
synthsToRun = [] synthsToRun = []
@ -71,29 +71,29 @@ def allCombos(widths, modules, techs, freqs):
if __name__ == '__main__': if __name__ == '__main__':
##### Run specific syntheses for a specific frequency ##### Run specific syntheses for a specific frequency
widths = [8, 16, 32, 64, 128] widths = [8, 16, 32, 64, 128]
modules = ['mul', 'adder', 'shifter', 'flop', 'comparator', 'binencoder', 'csa', 'mux2', 'mux4', 'mux8'] modules = ['mul', 'adder', 'shifter', 'flop', 'comparator', 'binencoder', 'csa', 'mux2', 'mux4', 'mux8']
techs = ['sky90', 'sky130', 'tsmc28', 'tsmc28psyn'] techs = ['sky90', 'sky130', 'tsmc28', 'tsmc28psyn']
freqs = [5000] freqs = [5000]
synthsToRun = allCombos(widths, modules, techs, freqs) synthsToRun = allCombos(widths, modules, techs, freqs)
##### Run a sweep based on best delay found in existing syntheses ##### Run a sweep based on best delay found in existing syntheses
module = 'adder' module = 'adder'
width = 32 width = 32
tech = 'tsmc28psyn' tech = 'tsmc28psyn'
synthsToRun = freqSweep(module, width, tech) synthsToRun = freqSweep(module, width, tech)
##### Run a sweep for multiple modules/widths based on best delay found in existing syntheses ##### 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 = [8, 16, 32, 64, 128]
widths = [32] widths = [32]
tech = 'sky130' tech = 'sky130'
synthsToRun = freqModuleSweep(widths, modules, tech) synthsToRun = freqModuleSweep(widths, modules, tech)
##### Only do syntheses for which a run doesn't already exist ##### Only do syntheses for which a run doesn't already exist
synthsToRun = filterRedundant(synthsToRun) synthsToRun = filterRedundant(synthsToRun)
pool = Pool(processes=25) pool = Pool(processes=25)
pool.starmap(runCommand, synthsToRun) pool.starmap(runCommand, synthsToRun)
pool.close() pool.close()
pool.join() pool.join()

View File

@ -15,59 +15,52 @@ import os
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("DESIGN") parser.add_argument("DESIGN")
parser.add_argument("HDLPATH"); parser.add_argument("HDLPATH")
args=parser.parse_args() args=parser.parse_args()
fin_path = glob.glob(f"{os.getenv('WALLY')}/src/**/{args.DESIGN}.sv",recursive=True)[0] 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 # keeps track of what line number the module header ends
lineModuleStart = 0 lineModuleEnd = 0
# keeps track of what line number the module header ends # keeps track of module name
lineModuleEnd = 0 moduleName = ""
# keeps track of module name # string that will keep track of the running module header
moduleName = "" buf = 'import cvw::*;\n`include "config.vh"\n`include "parameter-defs.vh"\n'
# string that will keep track of the running module header # are we writing into the buffer
buf = "import cvw::*;\n`include \"config.vh\"\n`include \"parameter-defs.vh\"\n" writeBuf=False
# are we writing into the buffer index=0
writeBuf=False
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 # post-processing buffer: add DUT and endmodule lines
for l in lines: buf += f"\t{moduleName} #(P) dut(.*);\nendmodule"
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 # path to wrapper
buf += f"\t{moduleName} #(P) dut(.*);\nendmodule" wrapperPath = f"{args.HDLPATH}/{moduleName}wrapper.sv"
# path to wrapper with open(wrapperPath, "w") as fout:
wrapperPath = f"{args.HDLPATH}/{moduleName}wrapper.sv" fout.write(buf)
fout = open(wrapperPath, "w")
fout.write(buf)
fin.close()
fout.close()
#print(buf)

View File

@ -7,12 +7,9 @@ import argparse
def runSynth(config, mod, tech, freq, maxopt, usesram): def runSynth(config, mod, tech, freq, maxopt, usesram):
global pool global pool
if (usesram): prefix = "syn_sram_" if usesram else "syn_"
prefix = "syn_sram_"
else:
prefix = "syn_"
cfg = prefix + config 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]) pool.map(mask, [command])
def mask(command): def mask(command):

View File

@ -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.
////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -23,7 +23,7 @@
// and limitations under the License. // 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 reset,
input logic clk, input logic clk,
input string ProgramAddrMapFile, input string ProgramAddrMapFile,

View File

@ -84,11 +84,11 @@ module loggers import cvw::*; #(parameter cvw_t P,
always_comb always_comb
if (TEST == "embench") begin if (TEST == "embench") begin
StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_trigger"; StartSampleFirst = functionName.functionName.FunctionName == "start_trigger";
EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_trigger"; EndSampleFirst = functionName.functionName.FunctionName == "stop_trigger";
end else if (TEST == "coremark") begin end else if (TEST == "coremark") begin
StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_time"; StartSampleFirst = functionName.functionName.FunctionName == "start_time";
EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_time"; EndSampleFirst = functionName.functionName.FunctionName == "stop_time";
end else begin end else begin
StartSampleFirst = reset; StartSampleFirst = reset;
EndSampleFirst = '0; EndSampleFirst = '0;
@ -106,22 +106,22 @@ module loggers import cvw::*; #(parameter cvw_t P,
if(TEST == "embench") begin if(TEST == "embench") begin
// embench runs warmup then runs start_trigger // embench runs warmup then runs start_trigger
// embench end with stop_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); //flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed);
//assign StartSample = 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); flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed);
assign EndSample = EndSampleFirst & ~ EndSampleDelayed; assign EndSample = EndSampleFirst & ~ EndSampleDelayed;
end else if(TEST == "coremark") begin end else if(TEST == "coremark") begin
// embench runs warmup then runs start_trigger // embench runs warmup then runs start_trigger
// embench end with stop_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); //flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed);
//assign StartSample = 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); flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed);
assign EndSample = EndSampleFirst & ~ EndSampleDelayed; assign EndSample = EndSampleFirst & ~ EndSampleDelayed;

View File

@ -1,12 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys, fileinput import sys
import fileinput
address = 0 address = 0
for line in fileinput.input('-'): with fileinput.input('-') as f:
# the 14- is to reverse the byte order to little endian for line in f:
formatedLine = ' '.join(line[14-i:14-i+2] for i in range(0, len(line), 2)) # the 14- is to reverse the byte order to little endian
sys.stdout.write('@{:08x} {:s}\n'.format(address, formatedLine)) formatedLine = ' '.join(line[14-i:14-i+2] for i in range(0, len(line), 2))
address+=8 sys.stdout.write(f'@{address:08x} {formatedLine:s}\n')
address+=8

View File

@ -684,8 +684,8 @@ module testbench;
loggers (clk, reset, DCacheFlushStart, DCacheFlushDone, memfilename, TEST); loggers (clk, reset, DCacheFlushStart, DCacheFlushDone, memfilename, TEST);
// track the current function or global label // track the current function or global label
if (DEBUG > 0 | ((PrintHPMCounters | BPRED_LOGGER) & P.ZICNTR_SUPPORTED)) begin : FunctionName if (DEBUG > 0 | ((PrintHPMCounters | BPRED_LOGGER) & P.ZICNTR_SUPPORTED)) begin : functionName
FunctionName #(P) FunctionName(.reset(reset_ext | TestBenchReset), functionName #(P) functionName(.reset(reset_ext | TestBenchReset),
.clk(clk), .ProgramAddrMapFile(ProgramAddrMapFile), .ProgramLabelMapFile(ProgramLabelMapFile)); .clk(clk), .ProgramAddrMapFile(ProgramAddrMapFile), .ProgramLabelMapFile(ProgramLabelMapFile));
end end
@ -710,11 +710,11 @@ module testbench;
always @(posedge clk) begin always @(posedge clk) begin
// if (reset) PrevPCZero <= 0; // 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 ) | TestComplete <= ((InstrM == 32'h6f) & dut.core.InstrValidM ) |
((dut.core.lsu.IEUAdrM == ProgramAddrLabelArray["tohost"] & dut.core.lsu.IEUAdrM != 0) & InstrMName == "SW"); // | ((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)); // (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) // 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."); // $error("Program fetched illegal instruction 0x00000000 from address 0x00000000 twice in a row. Usually due to fault with no fault handler.");
end end

View File

@ -40,9 +40,9 @@ mem_addr = mem_start_addr
def wl(line="", comment=None, fname=test_name): def wl(line="", comment=None, fname=test_name):
with open(fname, "a") as f: with open(fname, "a") as f:
instr = False if (":" in line or instr = not (":" in line or
".align" in line or ".align" in line or
"# include" in line) else True "# include" in line)
indent = 6 if instr else 0 indent = 6 if instr else 0
comment = "// " + comment if comment is not None else "" comment = "// " + comment if comment is not None else ""
to_write = " " * indent + line + comment + "\n" to_write = " " * indent + line + comment + "\n"
@ -78,7 +78,7 @@ if __name__ == "__main__":
for i in range(dcache_num_ways): for i in range(dcache_num_ways):
wl(comment=f"start way test #{i+1}") wl(comment=f"start way test #{i+1}")
wl(f'li t0, {hex(mem_addr)}') 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}") wl(comment=f"i$ boundary, way test #{i+1}")
write_repro_instrs() write_repro_instrs()
mem_addr += dcache_way_size_in_bytes # so that we excercise a new D$ way. mem_addr += dcache_way_size_in_bytes # so that we excercise a new D$ way.

View File

@ -60,9 +60,9 @@ class Config:
def create_vectors(my_config): def create_vectors(my_config):
suite_folder_num = my_config.bits suite_folder_num = my_config.bits
if my_config.bits == 64 and my_config.letter == "F": suite_folder_num = 32 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_dir1 = f"{wally}/addins/riscv-arch-test/riscv-test-suite/rv{suite_folder_num}i_m/{my_config.letter}/src/"
source_dir2 = "{}/tests/riscof/work/riscv-arch-test/rv{}i_m/{}/src/".format(wally, my_config.bits, my_config.letter) source_dir2 = f"{wally}/tests/riscof/work/riscv-arch-test/rv{my_config.bits}i_m/{my_config.letter}/src/"
dest_dir = "{}/tests/fp/combined_IF_vectors/IF_vectors/".format(wally) dest_dir = f"{wally}/tests/fp/combined_IF_vectors/IF_vectors/"
all_vectors1 = os.listdir(source_dir1) all_vectors1 = os.listdir(source_dir1)
filt_vectors1 = [v for v in all_vectors1 if my_config.filt in v] 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 operation = my_config.op_code
rounding_mode = "X" rounding_mode = "X"
flags = "XX" flags = "XX"
# use name to create our new tv # use name to create our new tv and open vectors
dest_file = open("{}cvw_{}_{}.tv".format(dest_dir, my_config.bits, vector1[:-2]), 'w') 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:
# open vectors # for each test in the vector
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:
reading = True reading = True
# print("trigger 64F") src_file2.readline() #skip first bc junk
#skip first 2 lines bc junk # print(my_config.bits, my_config.letter)
src_file2.readline() if my_config.letter == "F" and my_config.bits == 64:
while reading: reading = True
# get answer and flags from Ref...signature # print("trigger 64F")
# answers are before deadbeef (first line of 4) #skip first 2 lines bc junk
# flags are after deadbeef (third line of 4) src_file2.readline()
answer = src_file2.readline().strip() while reading:
deadbeef = src_file2.readline().strip() # get answer and flags from Ref...signature
# print(answer) # answers are before deadbeef (first line of 4)
if not (answer == "e7d4b281" and deadbeef == "6f5ca309"): # if there is still stuff to read # flags are after deadbeef (third line of 4)
# get flags answer = src_file2.readline().strip()
packed = src_file2.readline().strip()[6:] deadbeef = src_file2.readline().strip()
flags, rounding_mode = unpack_rf(packed) # print(answer)
# skip 00000000 buffer if not (answer == "e7d4b281" and deadbeef == "6f5ca309"): # if there is still stuff to read
src_file2.readline() # get flags
packed = src_file2.readline().strip()[6:]
# 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"):
flags, rounding_mode = unpack_rf(packed) flags, rounding_mode = unpack_rf(packed)
# skip 00000000 buffer
# put it all together src_file2.readline()
if not done:
translation = "{}_{}_{}_{}_{}_{}".format(operation, ext_bits(op1val), ext_bits(op2val), ext_bits(answer.strip()), flags, rounding_mode) # parse through .S file
dest_file.write(translation + "\n") detected = False
else: done = False
# print("read false") op1val = "0"
reading = False op2val = "0"
# print("out") while not (detected or done):
dest_file.close() # print("det1")
src_file1.close() line = src_file1.readline()
src_file2.close() # 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_list = [
Config(32, "M", "div", "div-", 0), Config(32, "M", "div", "div-", 0),
@ -309,4 +303,4 @@ Config(64, "M", "remuw", "remuw-", 9)
] ]
for c in config_list: for c in config_list:
create_vectors(c) create_vectors(c)

View File

@ -27,8 +27,8 @@ round_dict = {
print("creating testfloat div test vectors") print("creating testfloat div test vectors")
source_dir = "{}/tests/fp/vectors/".format(wally) source_dir = f"{wally}/tests/fp/vectors/"
dest_dir = "{}/tests/fp/combined_IF_vectors/IF_vectors/".format(wally) dest_dir = f"{wally}/tests/fp/combined_IF_vectors/IF_vectors/"
all_vectors = os.listdir(source_dir) all_vectors = os.listdir(source_dir)
div_vectors = [v for v in all_vectors if "div" in v] 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("_") config_list = vector.split(".")[0].split("_")
operation = "1" #float div operation = "1" #float div
rounding_mode = round_dict[str(config_list[2])] rounding_mode = round_dict[str(config_list[2])]
# use name to create our new tv # use name to create our new tv and open vector
dest_file = open(dest_dir + "cvw_" + vector, 'a') with open(dest_dir + "cvw_" + vector, 'a') as dest_file, open(source_dir + vector) as src_file:
# open vector # for each test in the vector
src_file = open(source_dir + vector,'r') for i in src_file.readlines():
# for each test in the vector translation = "" # this stores the test that we are currently working on
for i in src_file.readlines(): [input_1, input_2, answer, flags] = i.split("_") # separate inputs, answer, and flags
translation = "" # this stores the test that we are currently working on # put it all together, strip nec for removing \n on the end of the flags
[input_1, input_2, answer, flags] = i.split("_") # separate inputs, answer, and flags translation = f"{operation}_{ext_bits(input_1)}_{ext_bits(input_2)}_{ext_bits(answer)}_{flags.strip()}_{rounding_mode}"
# put it all together, strip nec for removing \n on the end of the flags dest_file.write(translation + "\n")
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()
print("creating testfloat sqrt test vectors") print("creating testfloat sqrt test vectors")
@ -64,16 +60,12 @@ for vector in sqrt_vectors:
config_list = vector.split(".")[0].split("_") config_list = vector.split(".")[0].split("_")
operation = "2" #sqrt operation = "2" #sqrt
rounding_mode = round_dict[str(config_list[2])] rounding_mode = round_dict[str(config_list[2])]
# use name to create our new tv # use name to create our new tv and open vector
dest_file = open(dest_dir + "cvw_" + vector, 'a') with open(dest_dir + "cvw_" + vector, 'a') as dest_file, open(source_dir + vector) as src_file:
# open vector # for each test in the vector
src_file = open(source_dir + vector,'r') for i in src_file.readlines():
# for each test in the vector translation = "" # this stores the test that we are currently working on
for i in src_file.readlines(): [input_1, answer, flags] = i.split("_") # separate inputs, answer, and flags
translation = "" # this stores the test that we are currently working on # put it all together, strip nec for removing \n on the end of the flags
[input_1, answer, flags] = i.split("_") # separate inputs, answer, and flags translation = "{}_{}_{}_{}_{}_{}".format(operation, ext_bits(input_1), "X"*32, ext_bits(answer), flags.strip(), rounding_mode)
# put it all together, strip nec for removing \n on the end of the flags dest_file.write(translation + "\n")
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()

View File

@ -1,2 +1,2 @@
from pkgutil import extend_path from pkgutil import extend_path
__path__ = extend_path(__path__, __name__) __path__ = extend_path(__path__, __name__)

View File

@ -1,17 +1,10 @@
import os import os
import re import re
import shutil import shutil
import subprocess
import shlex
import logging import logging
import random
import string
from string import Template
import riscof.utils as utils import riscof.utils as utils
from riscof.pluginTemplate import pluginTemplate from riscof.pluginTemplate import pluginTemplate
import riscof.constants as constants
from riscv_isac.isac import isac
logger = logging.getLogger() logger = logging.getLogger()
@ -72,11 +65,11 @@ class sail_cSim(pluginTemplate):
self.sailargs += "--enable-zcb" self.sailargs += "--enable-zcb"
if "Q" in ispec["ISA"]: if "Q" in ispec["ISA"]:
self.isa += 'q' self.isa += 'q'
objdump = "riscv64-unknown-elf-objdump".format(self.xlen) objdump = "riscv64-unknown-elf-objdump"
if shutil.which(objdump) is None: if shutil.which(objdump) is None:
logger.error(objdump+": executable not found. Please check environment setup.") logger.error(objdump+": executable not found. Please check environment setup.")
raise SystemExit(1) raise SystemExit(1)
compiler = "riscv64-unknown-elf-gcc".format(self.xlen) compiler = "riscv64-unknown-elf-gcc"
if shutil.which(compiler) is None: if shutil.which(compiler) is None:
logger.error(compiler+": executable not found. Please check environment setup.") logger.error(compiler+": executable not found. Please check environment setup.")
raise SystemExit(1) raise SystemExit(1)
@ -114,9 +107,9 @@ class sail_cSim(pluginTemplate):
if ('NO_SAIL=True' in testentry['macros']): if ('NO_SAIL=True' in testentry['macros']):
# if the tests can't run on SAIL we copy the reference output to the src directory # 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)) 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: 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 = ' ' cov_str = ' '
for label in testentry['coverage_labels']: for label in testentry['coverage_labels']:
@ -124,10 +117,10 @@ class sail_cSim(pluginTemplate):
if cgf_file is not None: if cgf_file is not None:
coverage_cmd = 'riscv_isac --verbose info coverage -d \ 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 \ --sig-label begin_signature end_signature \
--test-label rvtest_code_begin rvtest_code_end \ --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) test_name, ' -c '.join(cgf_file), self.xlen, cov_str)
else: else:
coverage_cmd = '' coverage_cmd = ''

View File

@ -1,16 +1,8 @@
import os import os
import re import re
import shutil
import subprocess
import shlex
import logging import logging
import random
import string
from string import Template
import sys
import riscof.utils as utils import riscof.utils as utils
import riscof.constants as constants
from riscof.pluginTemplate import pluginTemplate from riscof.pluginTemplate import pluginTemplate
logger = logging.getLogger() 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().replace('zicsr', ' ', 2), self.xlen, test, elf, compile_macros)
cmd = self.compile_cmd.format(testentry['isa'].lower(), 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 # 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 # the "else" clause is executed below assigning the sim command to simple no action
# echo statement. # echo statement.
if self.target_run: if self.target_run:
# set up the simulation command. Template is for spike. Please change. # set up the simulation command. Template is for spike. Please change.
if ('NO_SAIL=True' in testentry['macros']): if ('NO_SAIL=True' in testentry['macros']):
# if the tests can't run on SAIL we copy the reference output to the src directory # 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)) 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: 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: else:
simcmd = 'echo "NO RUN"' simcmd = 'echo "NO RUN"'
# concatenate all commands that need to be executed within a make-target. # 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<num>" where num # create a target. The makeutil will create a target with the name "TARGET<num>" where num
# starts from 0 and increments automatically for each new target that is added # starts from 0 and increments automatically for each new target that is added

View File

@ -17,7 +17,7 @@ if __name__ == "__main__":
line_num = int(sig_adr / 4) + 1 line_num = int(sig_adr / 4) + 1
offset = sig_adr & 0x3F offset = sig_adr & 0x3F
test_num = int((sig_adr-offset)/int("40",16)) test_num = int((sig_adr-offset)/int("40",16))
print("IntrNum 0x{:02X}".format(test_num)) print(f"IntrNum 0x{test_num:02X}")
print("Offset 0x{:02X}".format(offset)) print(f"Offset 0x{offset:02X}")
print("LineNum "+str(line_num)) print("LineNum "+str(line_num))