Merge pull request #471 from stineje/main

Fix multitude of issues with plotPPA as well as issue related to Popen issuing too many synthesis
This commit is contained in:
David Harris 2023-11-14 05:51:20 -08:00 committed by GitHub
commit 78be798336
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 510 additions and 320 deletions

View File

@ -4,10 +4,10 @@ binencoder,sky130,16,1000,1.0000,136.220003,77.243,0.021773774467348
binencoder,sky130,32,1000,1.0000,372.400007,189.626,0.04371111111111111
binencoder,sky130,64,1000,1.0000,797.720015,382.205,0.07393850658857981
binencoder,sky130,128,900,1.1111,1602.300031,610.009,0.1261366969785861
adder,sky130,8,1000,1.0000,253.820005,154.438,0.10825587752870422
adder,sky130,16,1000,1.0000,722.260013,485.109,0.32460910944935417
adder,sky130,32,1000,1.0000,1440.600027,714.057,0.6580226904376014
adder,sky130,64,1000,1.0000,2781.240054,1050.0,0.9392239364188874
adder,sky130,8,1700,0.588235,253.820005,154.438,0.10825587752870422
adder,sky130,16,1300,0.7692307,722.260013,485.109,0.32460910944935417
adder,sky130,32,1100,0.90909,1440.600027,714.057,0.6580226904376014
adder,sky130,64,950,1.0526315,2781.240054,1050.0,0.9392239364188874
adder,sky130,128,900,1.1111,6186.740118,2230.0,2.1480106100795755
csa,sky130,8,1000,1.0000,266.560005,154.202,0.13650573115665163
csa,sky130,16,1000,1.0000,533.12001,308.404,0.27263530601922104
@ -19,11 +19,11 @@ shifter,sky130,16,1000,1.0000,666.400006,558.433,0.19552906110283155
shifter,sky130,32,1000,1.0000,1475.880027,768.262,0.3807431082700759
shifter,sky130,64,1000,1.0000,3914.120062,2680.0,1.144802541988198
shifter,sky130,128,900,1.1111,9192.400136,6080.0,2.9008914525432616
comparator,sky130,8,1000,1.0000,200.900004,136.6,0.05001033271337053
comparator,sky130,16,1000,1.0000,358.680007,189.253,0.06321553011448482
comparator,sky130,32,1500,0.666666,690.900013,315.709,0.10771793448084398
comparator,sky130,64,1300,0.7692307,1372.980026,508.393,0.2048577820389901
comparator,sky130,128,1100,0.909090,2744.980052,796.047,0.34396273737011823
comparator,sky130,8,1700,0.588235,200.900004,136.6,0.05001033271337053
comparator,sky130,16,1500,0.6666667,358.680007,189.253,0.06321553011448482
comparator,sky130,32,1300,0.7692307,690.900013,315.709,0.10771793448084398
comparator,sky130,64,1200,0.8333333,1372.980026,508.393,0.2048577820389901
comparator,sky130,128,1150,0.869565,2744.980052,796.047,0.34396273737011823
flop,sky130,8,1000,1.0000,133.279999,64.8145,0.193835
flop,sky130,16,1000,1.0000,266.5599975,129.629,0.38715000000000005
flop,sky130,32,1000,1.0000,533.119995,259.258,0.7723000000000001

1 Module Tech Width Target Freq Delay Area L Power (nW) D energy (nJ)
4 binencoder sky130 32 1000 1.0000 372.400007 189.626 0.04371111111111111
5 binencoder sky130 64 1000 1.0000 797.720015 382.205 0.07393850658857981
6 binencoder sky130 128 900 1.1111 1602.300031 610.009 0.1261366969785861
7 adder sky130 8 1000 1700 1.0000 0.588235 253.820005 154.438 0.10825587752870422
8 adder sky130 16 1000 1300 1.0000 0.7692307 722.260013 485.109 0.32460910944935417
9 adder sky130 32 1000 1100 1.0000 0.90909 1440.600027 714.057 0.6580226904376014
10 adder sky130 64 1000 950 1.0000 1.0526315 2781.240054 1050.0 0.9392239364188874
11 adder sky130 128 900 1.1111 6186.740118 2230.0 2.1480106100795755
12 csa sky130 8 1000 1.0000 266.560005 154.202 0.13650573115665163
13 csa sky130 16 1000 1.0000 533.12001 308.404 0.27263530601922104
19 shifter sky130 32 1000 1.0000 1475.880027 768.262 0.3807431082700759
20 shifter sky130 64 1000 1.0000 3914.120062 2680.0 1.144802541988198
21 shifter sky130 128 900 1.1111 9192.400136 6080.0 2.9008914525432616
22 comparator sky130 8 1000 1700 1.0000 0.588235 200.900004 136.6 0.05001033271337053
23 comparator sky130 16 1000 1500 1.0000 0.6666667 358.680007 189.253 0.06321553011448482
24 comparator sky130 32 1500 1300 0.666666 0.7692307 690.900013 315.709 0.10771793448084398
25 comparator sky130 64 1300 1200 0.7692307 0.8333333 1372.980026 508.393 0.2048577820389901
26 comparator sky130 128 1100 1150 0.909090 0.869565 2744.980052 796.047 0.34396273737011823
27 flop sky130 8 1000 1.0000 133.279999 64.8145 0.193835
28 flop sky130 16 1000 1.0000 266.5599975 129.629 0.38715000000000005
29 flop sky130 32 1000 1.0000 533.119995 259.258 0.7723000000000001

View File

@ -18,44 +18,61 @@ from collections import namedtuple
import sklearn.metrics as skm # depricated, will need to replace with scikit-learn
import os
def synthsfromcsv(filename):
Synth = namedtuple("Synth", "module tech width freq delay area lpower denergy")
with open(filename, newline='') as csvfile:
with open(filename, newline="") as csvfile:
csvreader = csv.reader(csvfile)
global allSynths
allSynths = list(csvreader)[1:]
for i in range(len(allSynths)):
for j in range(len(allSynths[0])):
try: allSynths[i][j] = int(allSynths[i][j])
try:
allSynths[i][j] = int(allSynths[i][j])
except:
try: allSynths[i][j] = float(allSynths[i][j])
except: pass
try:
allSynths[i][j] = float(allSynths[i][j])
except:
pass
allSynths[i] = Synth(*allSynths[i])
return allSynths
def synthsintocsv():
''' writes a CSV with one line for every available synthesis
"""writes a CSV with one line for every available synthesis
each line contains the module, tech, width, target freq, and resulting metrics
'''
"""
print("This takes a moment...")
bashCommand = "find . -path '*runs/*' -prune"
output = subprocess.check_output(['bash','-c', bashCommand])
allSynths = output.decode("utf-8").split('\n')[:-1]
output = subprocess.check_output(["bash", "-c", bashCommand])
allSynths = output.decode("utf-8").split("\n")[:-1]
specReg = re.compile('[a-zA-Z0-9]+')
metricReg = re.compile('-?\d+\.\d+[e]?[-+]?\d*')
specReg = re.compile("[a-zA-Z0-9]+")
metricReg = re.compile("-?\d+\.\d+[e]?[-+]?\d*")
file = open("ppaData.csv", "w")
writer = csv.writer(file)
writer.writerow(['Module', 'Tech', 'Width', 'Target Freq', 'Delay', 'Area', 'L Power (nW)', 'D energy (nJ)'])
writer.writerow(
[
"Module",
"Tech",
"Width",
"Target Freq",
"Delay",
"Area",
"L Power (nW)",
"D energy (nJ)",
]
)
for oneSynth in allSynths:
module, width, risc, tech, freq = specReg.findall(oneSynth)[1:6]
metrics = []
for phrase in [['Path Slack', 'qor'], ['Design Area', 'qor'], ['100', 'power']]:
bashCommand = 'grep "{}" '+ oneSynth[2:]+'/reports/*{}*'
for phrase in [["Path Slack", "qor"], ["Design Area", "qor"], ["100", "power"]]:
bashCommand = 'grep "{}" ' + oneSynth[2:] + "/reports/*{}*"
bashCommand = bashCommand.format(*phrase)
try: output = subprocess.check_output(['bash','-c', bashCommand])
try:
output = subprocess.check_output(["bash", "-c", bashCommand])
except:
print(module + width + tech + freq + " doesn't have reports")
print("Consider running cleanup() first")
@ -65,45 +82,51 @@ def synthsintocsv():
delay = 1000 / int(freq) - metrics[0]
area = metrics[1]
lpower = metrics[4]
denergy = (metrics[2] + metrics[3])/int(freq)*1000 # (switching + internal powers)*delay, more practical units for regression coefs
tpower = (metrics[2] + metrics[3] + metrics[4]*.000001)
denergy = (
(tpower) / int(freq) * 1000
) # (switching + internal powers)*delay, more practical units for regression coefs
if ('flop' in module): # since two flops in each module
if "flop" in module: # since two flops in each module
[area, lpower, denergy] = [n / 2 for n in [area, lpower, denergy]]
writer.writerow([module, tech, width, freq, delay, area, lpower, denergy])
file.close()
def cleanup():
''' removes runs that didn't work
'''
"""removes runs that didn't work"""
bashCommand = 'grep -r "Error" runs/ppa*/reports/*qor*'
try:
output = subprocess.check_output(['bash','-c', bashCommand])
allSynths = output.decode("utf-8").split('\n')[:-1]
output = subprocess.check_output(["bash", "-c", bashCommand])
allSynths = output.decode("utf-8").split("\n")[:-1]
for run in allSynths:
run = run.split('MHz')[0]
bc = 'rm -r '+ run + '*'
output = subprocess.check_output(['bash','-c', bc])
except: pass
run = run.split("MHz")[0]
bc = "rm -r " + run + "*"
output = subprocess.check_output(["bash", "-c", bc])
except:
pass
bashCommand = "find . -path '*runs/*' -prune"
output = subprocess.check_output(['bash','-c', bashCommand])
allSynths = output.decode("utf-8").split('\n')[:-1]
output = subprocess.check_output(["bash", "-c", bashCommand])
allSynths = output.decode("utf-8").split("\n")[:-1]
for oneSynth in allSynths:
for phrase in [['Path Length', 'qor']]:
bashCommand = 'grep "{}" '+ oneSynth[2:]+'/reports/*{}*'
for phrase in [["Path Length", "qor"]]:
bashCommand = 'grep "{}" ' + oneSynth[2:] + "/reports/*{}*"
bashCommand = bashCommand.format(*phrase)
try: output = subprocess.check_output(['bash','-c', bashCommand])
try:
output = subprocess.check_output(["bash", "-c", bashCommand])
except:
bc = 'rm -r '+ oneSynth[2:]
output = subprocess.check_output(['bash','-c', bc])
bc = "rm -r " + oneSynth[2:]
output = subprocess.check_output(["bash", "-c", bc])
print("All cleaned up!")
def getVals(tech, module, var, freq=None, width=None):
''' for a specified tech, module, and variable/metric
"""for a specified tech, module, and variable/metric
returns a list of values for that metric in ascending width order
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
@ -113,9 +136,14 @@ def getVals(tech, module, var, freq=None, width=None):
metric = []
widthL = []
if (freq != None):
if freq != None:
for oneSynth in allSynths:
if (oneSynth.freq == freq) & (oneSynth.tech == tech) & (oneSynth.module == module) & (oneSynth.width != 1):
if (
(oneSynth.freq == freq)
& (oneSynth.tech == tech)
& (oneSynth.module == module)
& (oneSynth.width != 1)
):
widthL += [oneSynth.width]
osdict = oneSynth._asdict()
metric += [osdict[var]]
@ -123,12 +151,17 @@ def getVals(tech, module, var, freq=None, width=None):
else:
for w in widthsToGet:
for oneSynth in bestSynths:
if (oneSynth.width == w) & (oneSynth.tech == tech) & (oneSynth.module == module):
if (
(oneSynth.width == w)
& (oneSynth.tech == tech)
& (oneSynth.module == module)
):
osdict = oneSynth._asdict()
met = osdict[var]
metric += [met]
return metric
def csvOfBest(filename):
bestSynths = []
for tech in [x.tech for x in techSpecs]:
@ -137,8 +170,14 @@ def csvOfBest(filename):
m = np.Inf # large number to start
best = None
for oneSynth in allSynths: # best achievable, rightmost green
if (oneSynth.width == w) & (oneSynth.tech == tech) & (oneSynth.module == mod):
if (oneSynth.delay < m) & (1000/oneSynth.delay > oneSynth.freq):
if (
(oneSynth.width == w)
& (oneSynth.tech == tech)
& (oneSynth.module == mod)
):
if (oneSynth.delay < m) & (
1000 / oneSynth.delay > oneSynth.freq
):
m = oneSynth.delay
best = oneSynth
@ -147,33 +186,52 @@ def csvOfBest(filename):
file = open(filename, "w")
writer = csv.writer(file)
writer.writerow(['Module', 'Tech', 'Width', 'Target Freq', 'Delay', 'Area', 'L Power (nW)', 'D energy (nJ)'])
writer.writerow(
[
"Module",
"Tech",
"Width",
"Target Freq",
"Delay",
"Area",
"L Power (nW)",
"D energy (nJ)",
]
)
for synth in bestSynths:
writer.writerow(list(synth))
file.close()
return bestSynths
def genLegend(fits, coefs, r2=None, spec=None, ale=False):
''' generates a list of two legend elements (or just an equation if no r2 or spec)
"""generates a list of two legend elements (or just an equation if no r2 or spec)
labels line with fit equation and dots with r squared of the fit
'''
"""
coefsr = [str(sigfig(c, 2)) for c in coefs]
if ale:
if (normAddWidth == 32):
sub = 'S'
if normAddWidth == 32:
sub = "S"
elif normAddWidth != 1:
print('Equations are wrong, check normAddWidth')
print("Equations are wrong, check normAddWidth")
else:
sub = 'N'
sub = "N"
eqDict = {'c': '', 'l': sub, 's': '$'+sub+'^2$', 'g': '$log_2$('+sub+')', 'n': ''+sub+'$log_2$('+sub+')'}
eq = ''
eqDict = {
"c": "",
"l": sub,
"s": "$" + sub + "^2$",
"g": "$log_2$(" + sub + ")",
"n": "" + sub + "$log_2$(" + sub + ")",
}
eq = ""
ind = 0
for k in eqDict.keys():
if k in fits:
if str(coefsr[ind]) != '0': eq += " + " + coefsr[ind] + eqDict[k]
if str(coefsr[ind]) != "0":
eq += " + " + coefsr[ind] + eqDict[k]
ind += 1
eq = eq[3:] # chop off leading ' + '
@ -182,16 +240,28 @@ def genLegend(fits, coefs, r2=None, spec=None, ale=False):
return eq
else:
legend_elements = [lines.Line2D([0], [0], color=spec.color, label=eq)]
legend_elements += [lines.Line2D([0], [0], color=spec.color, ls='', marker=spec.shape, label='$R^2$='+ str(round(r2, 4)))]
legend_elements += [
lines.Line2D(
[0],
[0],
color=spec.color,
ls="",
marker=spec.shape,
label="$R^2$=" + str(round(r2, 4)),
)
]
return legend_elements
def oneMetricPlot(module, widths, var, freq=None, ax=None, fits='clsgn', norm=True, color=None):
''' module: string module name
def oneMetricPlot(
module, widths, var, freq=None, ax=None, fits="clsgn", norm=True, color=None
):
"""module: string module name
freq: int freq (MHz)
var: string delay, area, lpower, or denergy
fits: constant, linear, square, log2, Nlog2
plots given variable vs width for all matching syntheses with regression
'''
"""
singlePlot = True
if ax or (freq == 10):
singlePlot = False
@ -202,24 +272,27 @@ def oneMetricPlot(module, widths, var, freq=None, ax=None, fits='clsgn', norm=Tr
allWidths = []
allMetrics = []
ale = (var != 'delay') # if not delay, must be area, leakage, or energy
ale = var != "delay" # if not delay, must be area, leakage, or energy
modFit = fitDict[module]
fits = modFit[ale]
if freq:
ls = '--'
ls = "--"
else:
ls = '-'
ls = "-"
for spec in techSpecs:
# print(f"Searching for module of spec {spec} and module {module} and var {var}")
metric = getVals(spec.tech, module, var, freq=freq)
# print(f"Found metric : {metric}")
if norm:
techdict = spec._asdict()
norm = techdict[var]
metric = [m / norm for m in metric]
if len(metric) == 5: # don't include the spec if we don't have points for all widths
if len(widths) == len(metric):
# don't include the spec if we don't have points for all widths
# print(f"Width \neq Metric")
xp, pred, coefs, r2 = regress(widths, metric, fits, ale)
fullLeg += genLegend(fits, coefs, r2, spec, ale=ale)
c = color if color else spec.color
@ -228,37 +301,70 @@ def oneMetricPlot(module, widths, var, freq=None, ax=None, fits='clsgn', norm=Tr
allWidths += widths
allMetrics += metric
# print(f"Widths passed into regress : {allWidths}")
if len(allWidths) > 0:
xp, pred, coefs, r2 = regress(allWidths, allMetrics, fits)
ax.plot(xp, pred, color='red', linestyle=ls)
ax.plot(xp, pred, color="orange", linestyle=ls)
else:
xp, pred, coefs, r2 = regress(widths, metric, fits)
ax.plot(xp, pred, color="orange", linestyle=ls)
if norm:
ylabeldic = {"lpower": "Leakage Power (add32)", "denergy": "Energy/Op (add32)", "area": "Area (add32)", "delay": "Delay (FO4)"}
ylabeldic = {
"lpower": "Leakage Power (add32)",
"denergy": "Energy/Op (add32)",
"area": "Area (add32)",
"delay": "Delay (FO4)",
}
else:
ylabeldic = {"lpower": "Leakage Power (nW)", "denergy": "Dynamic Energy (nJ)", "area": "Area (sq microns)", "delay": "Delay (ns)"}
ylabeldic = {
"lpower": "Leakage Power (nW)",
"denergy": "Dynamic Energy (nJ)",
"area": "Area (sq microns)",
"delay": "Delay (ns)",
}
ax.set_ylabel(ylabeldic[var])
ax.set_xticks(widths)
if singlePlot or (var == 'lpower') or (var == 'denergy'):
if singlePlot or (var == "lpower") or (var == "denergy"):
ax.set_xlabel("Width (bits)")
if not singlePlot and ((var == 'delay') or (var == 'area')):
if not singlePlot and ((var == "delay") or (var == "area")):
ax.tick_params(labelbottom=False)
if singlePlot:
fullLeg += genLegend(fits, coefs, r2, combined, ale=ale)
legLoc = 'upper left' if ale else 'center right'
legLoc = "upper left" if ale else "center right"
ax.add_artist(ax.legend(handles=fullLeg, loc=legLoc))
titleStr = " (target " + str(freq)+ "MHz)" if freq != None else " (best achievable delay)"
titleStr = (
" (target " + str(freq) + "MHz)"
if freq != None
else " (best achievable delay)"
)
ax.set_title(module + titleStr)
plt.savefig('.plots/'+ module + '_' + var + '.png')
plt.savefig(".plots/" + module + "_" + var + ".png")
# plt.show()
return r2
def regress(widths, var, fits='clsgn', ale=False):
''' fits a curve to the given points
returns lists of x and y values to plot that curve and coefs for the eq with r2
'''
def regress(widths, var, fits="clsgn", ale=False):
"""fits a curve to the given points
returns lists of x and y values to plot that curve and coefs for the eq with r2
"""
if len(var) != len(widths):
# print(
# f"There are not enough variables to match widths. Widths : {widths} Variables Found : {var}, padding to match may affect correctness (doing it anyways)\n"
# )
if len(widths) > len(var):
while len(widths) > len(var):
var.append(0.0)
if len(var) > len(widths):
while len(var) > len(widths):
widths.append(0)
# widths = [8, 16, 32, 64, 128]
# print(f"Regress var : {var}")
# print(f"Regress widths : {widths}")
funcArr = genFuncs(fits)
xp = np.linspace(min(widths) / 2, max(widths) * 1.1, 200)
xpToCalc = xp
@ -274,7 +380,8 @@ def regress(widths, var, fits='clsgn', ale=False):
row += [func(w)]
mat += [row]
y = np.array(var, dtype=np.float)
# var = [0, 1, 2, 3, 4]
y = np.array(var, dtype=np.float64)
coefs = opt.nnls(mat, y)[0]
yp = []
@ -290,19 +397,22 @@ def regress(widths, var, fits='clsgn', ale=False):
return xp, pred, coefs, r2
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)
'''
"""
file = open("ppaFitting.csv", "w")
writer = csv.writer(file)
writer.writerow(['Module', 'Metric', 'Target', '1', 'N', 'N^2', 'log2(N)', 'Nlog2(N)', 'R^2'])
writer.writerow(
["Module", "Metric", "Target", "1", "N", "N^2", "log2(N)", "Nlog2(N)", "R^2"]
)
for module in modules:
for freq in [10, None]:
target = 'easy' if freq else 'hard'
for var in ['delay', 'area', 'lpower', 'denergy']:
ale = (var != 'delay')
target = "easy" if freq else "hard"
for var in ["delay", "area", "lpower", "denergy"]:
ale = var != "delay"
metL = []
modFit = fitDict[module]
fits = modFit[ale]
@ -316,7 +426,7 @@ def makeCoefTable():
xp, pred, coefs, r2 = regress(widths * 2, metL, fits, ale)
coefs = np.ndarray.tolist(coefs)
coefsToWrite = [None] * 5
fitTerms = 'clsgn'
fitTerms = "clsgn"
ind = 0
for i in range(len(fitTerms)):
if fitTerms[i] in fits:
@ -327,25 +437,38 @@ def makeCoefTable():
file.close()
def sigfig(num, figs):
return '{:g}'.format(float('{:.{p}g}'.format(num, p=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
"""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'])
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):
for var in ["delay", "area", "lpower", "denergy"]:
if (var == "delay") and (freq == 10):
pass
else:
ale = (var != 'delay')
ale = var != "delay"
metL = []
modFit = fitDict[module]
fits = modFit[ale]
@ -364,28 +487,30 @@ def makeEqTable():
file.close()
def genFuncs(fits='clsgn'):
''' helper function for regress()
def genFuncs(fits="clsgn"):
"""helper function for regress()
returns array of functions with one for each term desired in the regression fit
'''
"""
funcArr = []
if 'c' in fits:
if "c" in fits:
funcArr += [lambda x: 1]
if 'l' in fits:
if "l" in fits:
funcArr += [lambda x: x]
if 's' in fits:
if "s" in fits:
funcArr += [lambda x: x**2]
if 'g' in fits:
if "g" in fits:
funcArr += [lambda x: np.log2(x)]
if 'n' in fits:
if "n" in fits:
funcArr += [lambda x: x * np.log2(x)]
return funcArr
def noOutliers(median, freqs, delays, areas):
''' returns a pared down list of freqs, delays, and areas
"""returns a pared down list of freqs, delays, and areas
cuts out any syntheses in which target freq isn't within 75% of the min delay target to focus on interesting area
helper function to freqPlot()
'''
"""
f = []
d = []
a = []
@ -398,14 +523,20 @@ def noOutliers(median, freqs, delays, areas):
return f, d, a
def freqPlot(tech, mod, width):
''' plots delay, area, area*delay, and area*delay^2 for syntheses with specified tech, module, width
'''
"""plots delay, area, area*delay, and area*delay^2 for syntheses with specified tech, module, width"""
freqsL, delaysL, areasL = ([[], []] for i in range(3))
for oneSynth in allSynths:
if (mod == oneSynth.module) & (width == oneSynth.width) & (tech == oneSynth.tech):
ind = (1000/oneSynth.delay < oneSynth.freq) # when delay is within target clock period
if (
(mod == oneSynth.module)
& (width == oneSynth.width)
& (tech == oneSynth.tech)
):
ind = (
1000 / oneSynth.delay < oneSynth.freq
) # when delay is within target clock period
freqsL[ind] += [oneSynth.freq]
delaysL[ind] += [oneSynth.delay]
areasL[ind] += [oneSynth.area]
@ -414,43 +545,55 @@ def freqPlot(tech, mod, width):
f, (ax1, ax2) = plt.subplots(2, 1, sharex=True)
for ax in (ax1, ax2):
ax.ticklabel_format(useOffset=False, style='plain')
ax.ticklabel_format(useOffset=False, style="plain")
for ind in [0, 1]:
areas = areasL[ind]
delays = delaysL[ind]
freqs = freqsL[ind]
freqs, delays, areas = noOutliers(median, freqs, delays, areas) # comment out to see all syntheses
freqs, delays, areas = noOutliers(
median, freqs, delays, areas
) # comment out to see all syntheses
c = 'blue' if ind else 'green'
c = "blue" if ind else "green"
ax1.scatter(freqs, delays, color=c)
ax2.scatter(freqs, areas, color=c)
legend_elements = [lines.Line2D([0], [0], color='green', ls='', marker='o', label='timing achieved'),
lines.Line2D([0], [0], color='blue', ls='', marker='o', label='slack violated')]
legend_elements = [
lines.Line2D(
[0], [0], color="green", ls="", marker="o", label="timing achieved"
),
lines.Line2D([0], [0], color="blue", ls="", marker="o", label="slack violated"),
]
ax1.legend(handles=legend_elements)
width = str(width)
ax2.set_xlabel("Target Freq (MHz)")
ax1.set_ylabel('Delay (ns)')
ax2.set_ylabel('Area (sq microns)')
ax1.set_title(mod + '_' + width)
if ('mux' in mod) & ('d' in mod):
ax1.set_ylabel("Delay (ns)")
ax2.set_ylabel("Area (sq microns)")
ax1.set_title(mod + "_" + width)
if ("mux" in mod) & ("d" in mod):
width = mod
mod = 'muxd'
plt.savefig('./plots/freqBuckshot/' + tech + '/' + mod + '/' + width + '.png')
mod = "muxd"
plt.savefig("./plots/freqBuckshot/" + tech + "/" + mod + "/" + width + ".png")
# plt.show()
def squareAreaDelay(tech, mod, width):
''' plots delay, area, area*delay, and area*delay^2 for syntheses with specified tech, module, width
'''
"""plots delay, area, area*delay, and area*delay^2 for syntheses with specified tech, module, width"""
global allSynths
freqsL, delaysL, areasL = ([[], []] for i in range(3))
for oneSynth in allSynths:
if (mod == oneSynth.module) & (width == oneSynth.width) & (tech == oneSynth.tech):
ind = (1000/oneSynth.delay < oneSynth.freq) # when delay is within target clock period
if (
(mod == oneSynth.module)
& (width == oneSynth.width)
& (tech == oneSynth.tech)
):
ind = (
1000 / oneSynth.delay < oneSynth.freq
) # when delay is within target clock period
freqsL[ind] += [oneSynth.freq]
delaysL[ind] += [oneSynth.delay]
areasL[ind] += [oneSynth.area]
@ -464,28 +607,40 @@ def squareAreaDelay(tech, mod, width):
targets = freqsL[ind]
targets = [1000 / f for f in targets]
targets, delays, areas = noOutliers(targets, delays, areas) # comment out to see all
targets, delays, areas = noOutliers(
targets, delays, areas
) # comment out to see all
if not ind:
achievedDelays = delays
c = 'blue' if ind else 'green'
ax1.scatter(targets, delays, marker='^', color=c)
ax2.scatter(targets, areas, marker='s', color=c)
c = "blue" if ind else "green"
ax1.scatter(targets, delays, marker="^", color=c)
ax2.scatter(targets, areas, marker="s", color=c)
bestAchieved = min(achievedDelays)
legend_elements = [lines.Line2D([0], [0], color='green', ls='', marker='^', label='delay (timing achieved)'),
lines.Line2D([0], [0], color='green', ls='', marker='s', label='area (timing achieved)'),
lines.Line2D([0], [0], color='blue', ls='', marker='^', label='delay (timing violated)'),
lines.Line2D([0], [0], color='blue', ls='', marker='s', label='area (timing violated)')]
legend_elements = [
lines.Line2D(
[0], [0], color="green", ls="", marker="^", label="delay (timing achieved)"
),
lines.Line2D(
[0], [0], color="green", ls="", marker="s", label="area (timing achieved)"
),
lines.Line2D(
[0], [0], color="blue", ls="", marker="^", label="delay (timing violated)"
),
lines.Line2D(
[0], [0], color="blue", ls="", marker="s", label="area (timing violated)"
),
]
ax2.legend(handles=legend_elements, loc='upper left')
ax2.legend(handles=legend_elements, loc="upper left")
ax1.set_xlabel("Delay Targeted (ns)")
ax1.set_ylabel("Delay Achieved (ns)")
ax2.set_ylabel('Area (sq microns)')
ax1.set_title(mod + '_' + str(width))
ax2.set_ylabel("Area (sq microns)")
ax1.set_title(mod + "_" + str(width))
squarify(f)
@ -495,54 +650,68 @@ def squareAreaDelay(tech, mod, width):
areaUpperLim = max(flatten(areasL)) / frac + areaLowerLim
ax2.set_ylim([areaLowerLim, areaUpperLim])
ax1.plot(xvals, xvals, ls="--", c=".3")
ax1.hlines(y=bestAchieved, xmin=xvals[0], xmax=xvals[1], color="black", ls='--')
ax1.hlines(y=bestAchieved, xmin=xvals[0], xmax=xvals[1], color="black", ls="--")
plt.savefig('./plots/squareareadelay_' + mod + '_' + str(width) + '.png')
plt.savefig("./plots/squareareadelay_" + mod + "_" + str(width) + ".png")
# plt.show()
def squarify(fig):
''' helper function for squareAreaDelay()
"""helper function for squareAreaDelay()
forces matplotlib figure to be a square
'''
"""
w, h = fig.get_size_inches()
if w > h:
t = fig.subplotpars.top
b = fig.subplotpars.bottom
axs = h * (t - b)
l = (1.-axs/w)/2
l = (1.0 - axs / w) / 2
fig.subplots_adjust(left=l, right=1 - l)
else:
t = fig.subplotpars.right
b = fig.subplotpars.left
axs = w * (t - b)
l = (1.-axs/h)/2
l = (1.0 - axs / h) / 2
fig.subplots_adjust(bottom=l, top=1 - l)
def plotPPA(mod, widths, freq=None, norm=True, aleOpt=False):
''' for the module specified, plots width vs delay, area, leakage power, and dynamic energy with fits
def plotPPA(mod, freq=None, norm=True, aleOpt=False):
"""for the module specified, plots width vs delay, area, leakage power, and dynamic energy with fits
if no freq specified, uses the synthesis with best achievable delay for each width
overlays data from both techs
'''
"""
with mpl.rc_context({"figure.figsize": (7, 3.46)}):
fig, axs = plt.subplots(2, 2)
arr = [['delay', 'area'], ['lpower', 'denergy']]
arr = [["delay", "area"], ["lpower", "denergy"]]
freqs = [freq]
if aleOpt: freqs += [10]
if aleOpt:
freqs += [10]
for i in [0, 1]:
for j in [0, 1]:
leg = []
for f in freqs:
if (arr[i][j]=='delay') and (f==10):
if (arr[i][j] == "delay") and (f == 10):
pass
else:
r2 = oneMetricPlot(mod, widths, arr[i][j], ax=axs[i, j], freq=f, norm=norm)
ls = '--' if f else '-'
leg += [lines.Line2D([0], [0], color='red', label='$R^2$='+str(round(r2, 4)), linestyle=ls)]
# print(f"Pasing in widths {widths}")
r2 = oneMetricPlot(
mod, widths, arr[i][j], ax=axs[i, j], freq=f, norm=norm
)
ls = "--" if f else "-"
leg += [
lines.Line2D(
[0],
[0],
color="orange",
label="$R^2$=" + str(round(r2, 4)),
linestyle=ls,
)
]
if (mod in ['flop', 'csa']) & (arr[i][j] == 'delay'):
if (mod in ["flop", "csa"]) & (arr[i][j] == "delay"):
axs[i, j].set_ylim(ymin=0)
ytop = axs[i, j].get_ylim()[1]
axs[i, j].set_ylim(ymax=1.1 * ytop)
@ -554,86 +723,90 @@ def plotPPA(mod, widths, freq=None, norm=True, aleOpt=False):
plt.tight_layout(pad=0.05, w_pad=1, h_pad=0.5, rect=(0, 0, 1, 0.97))
if freq != 10:
n = 'normalized' if norm else 'unnormalized'
saveStr = './plots/'+ n + '/' + mod + '.png'
n = "normalized" if norm else "unnormalized"
saveStr = "./plots/" + n + "/" + mod + "_" + ".png"
print(f"Saving to {saveStr}")
plt.savefig(saveStr)
# plt.show()
def makeLineLegend():
''' generates legend to accompany normalized plots
'''
"""generates legend to accompany normalized plots"""
plt.rcParams["figure.figsize"] = (5.5, 0.3)
fig = plt.figure()
fullLeg = [lines.Line2D([0], [0], color='black', label='fastest', linestyle='-')]
fullLeg += [lines.Line2D([0], [0], color='black', label='smallest', linestyle='--')]
fullLeg += [lines.Line2D([0], [0], color='blue', label='tsmc28', marker='^')]
fullLeg += [lines.Line2D([0], [0], color='blue', label='tsmc28psyn', marker='x')]
fullLeg += [lines.Line2D([0], [0], color='green', label='sky90', marker='o')]
fullLeg += [lines.Line2D([0], [0], color='green', label='sky130', marker='+')]
fullLeg += [lines.Line2D([0], [0], color='red', label='combined', marker='_')]
fig.legend(handles=fullLeg, ncol=5, handlelength=1.4, loc='center')
saveStr = './plots/legend.png'
fullLeg = [lines.Line2D([0], [0], color="black", label="fastest", linestyle="-")]
fullLeg += [lines.Line2D([0], [0], color="black", label="smallest", linestyle="--")]
fullLeg += [lines.Line2D([0], [0], color="blue", label="tsmc28", marker="^")]
fullLeg += [lines.Line2D([0], [0], color="blue", label="tsmc28psyn", marker="x")]
fullLeg += [lines.Line2D([0], [0], color="green", label="sky90", marker="o")]
fullLeg += [lines.Line2D([0], [0], color="purple", label="sky130", marker="+")]
fullLeg += [lines.Line2D([0], [0], color="orange", label="combined", marker="_")]
fig.legend(handles=fullLeg, ncol=5, handlelength=1.4, loc="center")
saveStr = "./plots/legend.png"
plt.savefig(saveStr)
def muxPlot(fits='clsgn', norm=True):
''' module: string module name
def muxPlot(fits="clsgn", norm=True):
"""module: string module name
freq: int freq (MHz)
var: string delay, area, lpower, or denergy
fits: constant, linear, square, log2, Nlog2
plots given variable vs width for all matching syntheses with regression
'''
"""
ax = plt.gca()
inputs = [2, 4, 8]
allInputs = inputs * 2
fullLeg = []
for crit in ['data', 'control']:
for crit in ["data", "control"]:
allMetrics = []
muxes = ['mux2', 'mux4', 'mux8']
muxes = ["mux2", "mux4", "mux8"]
if crit == 'data':
ls = '--'
muxes = [m + 'd' for m in muxes]
elif crit == 'control':
ls = '-'
if crit == "data":
ls = "--"
muxes = [m + "d" for m in muxes]
elif crit == "control":
ls = "-"
for spec in techSpecs:
metric = []
for module in muxes:
metric += getVals(spec.tech, module, 'delay', width=[1])
metric += getVals(spec.tech, module, "delay", width=[1])
if norm:
techdict = spec._asdict()
norm = techdict['delay']
norm = techdict["delay"]
metric = [m / norm for m in metric]
# print(spec.tech, ' ', metric)
if len(metric) == 3: # don't include the spec if we don't have points for all
if (
len(metric) == 3
): # don't include the spec if we don't have points for all
xp, pred, coefs, r2 = regress(inputs, metric, fits, ale=False)
ax.scatter(inputs, metric, color=spec.color, marker=spec.shape)
ax.plot(xp, pred, color=spec.color, linestyle=ls)
allMetrics += metric
xp, pred, coefs, r2 = regress(allInputs, allMetrics, fits)
ax.plot(xp, pred, color='red', linestyle=ls)
fullLeg += [lines.Line2D([0], [0], color='red', label=crit, linestyle=ls)]
ax.plot(xp, pred, color="red", linestyle=ls)
fullLeg += [lines.Line2D([0], [0], color="red", label=crit, linestyle=ls)]
ax.set_ylabel('Delay (FO4)')
ax.set_ylabel("Delay (FO4)")
ax.set_xticks(inputs)
ax.set_xlabel("Number of inputs")
ax.set_title('mux timing')
ax.set_title("mux timing")
ax.legend(handles=fullLeg)
plt.savefig('./plots/mux.png')
plt.savefig("./plots/mux.png")
def stdDevError():
''' calculates std deviation and error for paper-writing purposes
'''
for var in ['delay', 'area', 'lpower', 'denergy']:
"""calculates std deviation and error for paper-writing purposes"""
for var in ["delay", "area", "lpower", "denergy"]:
errlist = []
for module in modules:
ale = (var != 'delay')
ale = var != "delay"
metL = []
modFit = fitDict[module]
fits = modFit[ale]
@ -665,9 +838,9 @@ def stdDevError():
n = [func(w) for func in funcArr]
yp += [sum(np.multiply(coefs, n))]
if (var == 'delay') & (module == 'flop'):
if (var == "delay") & (module == "flop"):
pass
elif (module == 'mult') & ale:
elif (module == "mult") & ale:
pass
else:
for i in range(len(y)):
@ -677,55 +850,71 @@ def stdDevError():
avgErr = np.mean(errlist)
stdv = np.std(errlist)
print(var, ' ', avgErr, ' ', stdv)
print(var, " ", avgErr, " ", stdv)
def makePlotDirectory():
''' creates plots directory in same level as this script to store plots in
'''
"""creates plots directory in same level as this script to store plots in"""
current_directory = os.getcwd()
final_directory = os.path.join(current_directory, 'plots')
final_directory = os.path.join(current_directory, "plots")
if not os.path.exists(final_directory):
os.makedirs(final_directory)
os.chdir(final_directory)
for folder in ['freqBuckshot', 'normalized', 'unnormalized']:
for folder in ["freqBuckshot", "normalized", "unnormalized"]:
new_directory = os.path.join(final_directory, folder)
if not os.path.exists(new_directory):
os.makedirs(new_directory)
os.chdir(new_directory)
if 'freq' in folder:
for tech in ['sky90', 'sky130', 'tsmc28', 'tsmc28psyn']:
if "freq" in folder:
for tech in ["sky90", "sky130", "tsmc28", "tsmc28psyn"]:
for mod in modules:
tech_directory = os.path.join(new_directory, tech)
mod_directory = os.path.join(tech_directory, mod)
if not os.path.exists(mod_directory):
os.makedirs(mod_directory)
os.chdir('..')
os.chdir("..")
os.chdir(current_directory)
if __name__ == '__main__':
if __name__ == "__main__":
##############################
# set up stuff, global variables
widths = [64, 128]
modules = ['adder', 'comparator']
widths = [8, 16, 32, 64, 128]
modules = ["adder"]
normAddWidth = 32 # divisor to use with N since normalizing to add_32
fitDict = {'adder': ['cg', 'l', 'l'], 'mul': ['cg', 's', 's'], 'comparator': ['cg', 'l', 'l'], 'csa': ['c', 'l', 'l'], 'shifter': ['cg', 'l', 'ln'], 'flop': ['c', 'l', 'l'], 'binencoder': ['cg', 'l', 'l']}
fitDict.update(dict.fromkeys(['mux2', 'mux4', 'mux8'], ['cg', 'l', 'l']))
fitDict = {
"adder": ["cg", "l", "l"],
"mul": ["cg", "s", "s"],
"comparator": ["cg", "l", "l"],
"csa": ["c", "l", "l"],
"shifter": ["cg", "l", "ln"],
"flop": ["c", "l", "l"],
"binencoder": ["cg", "l", "l"],
}
fitDict.update(dict.fromkeys(["mux2", "mux4", "mux8"], ["cg", "l", "l"]))
TechSpec = namedtuple("TechSpec", "tech color shape delay area lpower denergy")
techSpecs = [['sky90', 'green', 'o', 43.2e-3, 1440.600027, 714.057, 0.658022690438], ['sky130', 'red', 'o', 43.2e-3, 1440.600027, 714.057, 0.658022690438], ['tsmc28', 'blue', '^', 12.2e-3, 209.286002, 1060.0, .08153281695882594], ['tsmc28psyn', 'blue', '^', 12.2e-3, 209.286002, 1060.0, .08153281695882594]]
# FO4 delay information information
techSpecs = [
#["sky90", "green", "o", 43.2e-3, 1440.600027, 714.057, 0.658022690438],
# Area/Lpower/Denergy needs to be corrected here (jes)
["sky130", "orange", "o", 99.5e-3, 1440.600027, 714.057, 0.658022690438],
# ["tsmc28", "blue", "^", 12.2e-3, 209.286002, 1060.0, 0.08153281695882594],
# ["tsmc28psyn", "blue", "^", 12.2e-3, 209.286002, 1060.0, 0.08153281695882594],
]
techSpecs = [TechSpec(*t) for t in techSpecs]
combined = TechSpec('combined fit', 'red', '_', 0, 0, 0, 0)
combined = TechSpec("combined fit", "red", "_", 0, 0, 0, 0)
##############################
# cleanup() # run to remove garbage synth runs
synthsintocsv() # slow, run only when new synth runs to add to csv
allSynths = synthsfromcsv('ppaData.csv') # your csv here!
bestSynths = csvOfBest('bestSynths.csv')
allSynths = synthsfromcsv("ppaData.csv") # your csv here!
bestSynths = csvOfBest("bestSynths.csv")
makePlotDirectory()
# ### other functions
@ -737,9 +926,9 @@ if __name__ == '__main__':
for mod in modules:
for w in widths:
#freqPlot('sky90', mod, w)
freqPlot('sky130', mod, w)
freqPlot("sky130", mod, w)
# freqPlot('tsmc28', mod, w)
# freqPlot('tsmc28psyn', mod, w)
#plotPPA(mod, widths, norm=False)
#plotPPA(mod, aleOpt=True)
plt.close('all')
plotPPA(mod, norm=False)
plotPPA(mod, aleOpt=True)
plt.close("all")

View File

@ -12,11 +12,11 @@ from ppaAnalyze import synthsfromcsv
def runCommand(module, width, tech, freq):
command = "make synth DESIGN={} WIDTH={} TECH={} DRIVE=INV FREQ={} MAXOPT=1 MAXCORES=1".format(module, width, tech, freq)
subprocess.Popen(command, shell=True)
subprocess.call(command, shell=True)
def deleteRedundant(synthsToRun):
'''removes any previous runs for the current synthesis specifications'''
synthStr = "rm -rf runs/ppa_{}_{}_rv32e_{}nm_{}_*"
synthStr = "rm -rf runs/{}_{}_rv32e_{}_{}_*"
for synth in synthsToRun:
bashCommand = synthStr.format(*synth)
outputCPL = subprocess.check_output(['bash','-c', bashCommand])
@ -46,7 +46,7 @@ def freqModuleSweep(widths, modules, tech):
return synthsToRun
def filterRedundant(synthsToRun):
bashCommand = "find . -path '*runs/ppa*rv32e*' -prune"
bashCommand = "find . -path '*runs/*' -prune"
output = subprocess.check_output(['bash','-c', bashCommand])
specReg = re.compile('[a-zA-Z0-9]+')
allSynths = output.decode("utf-8").split('\n')[:-1]
@ -84,14 +84,15 @@ if __name__ == '__main__':
synthsToRun = freqSweep(module, width, tech)
##### Run a sweep for multiple modules/widths based on best delay found in existing syntheses
modules = ['adder', 'comparator']
widths = [64, 128]
modules = ['adder']
widths = [8, 16, 32, 64, 128]
tech = 'sky130'
synthsToRun = freqModuleSweep(widths, modules, tech)
##### Only do syntheses for which a run doesn't already exist
synthsToRun = filterRedundant(synthsToRun)
pool = Pool(processes=25)
pool.starmap(runCommand, synthsToRun)
pool.close()
pool.join()