mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-11 06:05:49 +00:00
Merge branch 'main' of https://github.com/openhwgroup/cvw
This commit is contained in:
commit
e9e856d2a2
36
.github/workflows/lint.yml
vendored
Normal file
36
.github/workflows/lint.yml
vendored
Normal 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
31
.ruff.toml
Normal 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
|
||||||
|
]
|
@ -1,4 +1,4 @@
|
|||||||

|
[](https://github.com/openhwgroup/cvw/actions/workflows/install.yml)
|
||||||
|
|
||||||
# core-v-wally
|
# core-v-wally
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 66b675017878032974c537ab7aa81758b9812530
|
Subproject commit 44278d9a918dbc337ac214cd5dba5f71aa26dcfa
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
100
bin/CacheSim.py
100
bin/CacheSim.py
@ -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))
|
||||||
|
74
bin/iterelf
74
bin/iterelf
@ -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}")
|
||||||
|
|
||||||
|
@ -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"])
|
||||||
|
|
||||||
#############################################
|
#############################################
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
22
bin/wsim
22
bin/wsim
@ -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}")
|
||||||
|
@ -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")
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import os,sys,subprocess
|
|
||||||
from datetime import datetime, timezone, timedelta
|
|
||||||
|
|
||||||
if not os.path.isfile(sys.path[0]+'/slack-webhook-url.txt'):
|
|
||||||
print('==============================================================')
|
|
||||||
print(' HOWDY! ')
|
|
||||||
print('slack-notifier.py can help let you know when your sim is done.')
|
|
||||||
print('To make it work, please supply your Slack bot webhook URL in:')
|
|
||||||
print(sys.path[0]+'/slack-webhook-url.txt')
|
|
||||||
print('Tutorial for slack webhook urls: https://bit.ly/BenSlackNotifier')
|
|
||||||
print('==============================================================')
|
|
||||||
else:
|
|
||||||
urlFile = open(sys.path[0]+'/slack-webhook-url.txt','r')
|
|
||||||
url = urlFile.readline().strip('\n')
|
|
||||||
|
|
||||||
# Traverse 3 parents up the process tree
|
|
||||||
result = subprocess.check_output('ps -o ppid -p $PPID',shell=True)
|
|
||||||
PPID2 = str(result).split('\\n')[1]
|
|
||||||
result = subprocess.check_output('ps -o ppid -p '+PPID2,shell=True)
|
|
||||||
PPID3 = str(result).split('\\n')[1]
|
|
||||||
# Get command name
|
|
||||||
result = subprocess.check_output('ps -o cmd -p '+PPID3,shell=True)
|
|
||||||
cmdName = str(result).split('\\n')[1]
|
|
||||||
# Get current time
|
|
||||||
timezone_offset = -8.0 # Pacific Standard Time (UTC−08:00)
|
|
||||||
tzinfo = timezone(timedelta(hours=timezone_offset))
|
|
||||||
time = datetime.now(tzinfo).strftime('%I:%M %p')
|
|
||||||
# Send message
|
|
||||||
message = 'Command `'+cmdName+'` completed at '+time+' PST'
|
|
||||||
result = subprocess.run('curl -X POST -H \'Content-type: application/json\' --data \'{"text":"'+message+'"}\' '+url,shell=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
|
|
||||||
print('Simulation stopped. Sending Slack message.')
|
|
@ -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()
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
|
||||||
|
@ -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):
|
||||||
|
@ -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.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
from pkgutil import extend_path
|
from pkgutil import extend_path
|
||||||
__path__ = extend_path(__path__, __name__)
|
__path__ = extend_path(__path__, __name__)
|
||||||
|
@ -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 = ''
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user