forked from Github_Repos/cvw
		
	
		
			
				
	
	
		
			737 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			737 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/python3
 | |
| # Madeleine Masser-Frye mmasserfrye@hmc.edu 5/22
 | |
| 
 | |
| import scipy.optimize as opt
 | |
| import subprocess
 | |
| import csv
 | |
| import re
 | |
| from matplotlib.cbook import flatten
 | |
| import matplotlib.pyplot as plt
 | |
| import matplotlib.lines as lines
 | |
| import matplotlib as mpl
 | |
| import numpy as np
 | |
| 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:
 | |
|         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])
 | |
|                 except: 
 | |
|                     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
 | |
|         each line contains the module, tech, width, target freq, and resulting metrics
 | |
|     '''
 | |
|     print("This takes a moment...")
 | |
|     bashCommand = "find . -path '*runs/ppa*rv32e*' -prune"
 | |
|     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*')
 | |
| 
 | |
|     file = open("ppaData.csv", "w")
 | |
|     writer = csv.writer(file)
 | |
|     writer.writerow(['Module', 'Tech', 'Width', 'Target Freq', 'Delay', 'Area', 'L Power (nW)', 'D energy (nJ)'])
 | |
| 
 | |
|     for oneSynth in allSynths:
 | |
|         module, width, risc, tech, freq = specReg.findall(oneSynth)[2:7]
 | |
|         tech = tech[:-2]
 | |
|         metrics = []
 | |
|         for phrase in [['Path Slack', 'qor'], ['Design Area', 'qor'], ['100', 'power']]:
 | |
|             bashCommand = 'grep "{}" '+ oneSynth[2:]+'/reports/*{}*'
 | |
|             bashCommand = bashCommand.format(*phrase)
 | |
|             try: output = subprocess.check_output(['bash','-c', bashCommand])
 | |
|             except: 
 | |
|                 print(module + width + tech + freq + " doesn't have reports")
 | |
|                 print("Consider running cleanup() first")
 | |
|             nums = metricReg.findall(str(output))
 | |
|             nums = [float(m) for m in nums]
 | |
|             metrics += nums
 | |
|         delay = 1000/int(freq) - metrics[0]
 | |
|         area = metrics[1]
 | |
|         lpower = metrics[4]
 | |
|         denergy = (metrics[2] + metrics[3])/int(freq)*1000 # (switching + internal powers)*delay, more practical units for regression coefs
 | |
| 
 | |
|         if ('flop' in module): # since two flops in each module 
 | |
|             [area, lpower, denergy] = [n/2 for n in [area, lpower, denergy]] 
 | |
| 
 | |
|         writer.writerow([module, tech, width, freq, delay, area, lpower, denergy])
 | |
|     file.close()
 | |
| 
 | |
| def cleanup():
 | |
|     ''' 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]
 | |
|         for run in allSynths:
 | |
|             run = run.split('MHz')[0]
 | |
|             bc = 'rm -r '+ run + '*'
 | |
|             output = subprocess.check_output(['bash','-c', bc])
 | |
|     except: pass
 | |
| 
 | |
|     bashCommand = "find . -path '*runs/ppa*rv32e*' -prune"
 | |
|     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/*{}*'
 | |
|             bashCommand = bashCommand.format(*phrase)
 | |
|             try: output = subprocess.check_output(['bash','-c', bashCommand])
 | |
|             except: 
 | |
|                 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
 | |
|         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
 | |
|     else:
 | |
|         widthsToGet = widths
 | |
| 
 | |
|     metric = []
 | |
|     widthL = []
 | |
| 
 | |
|     if (freq != None):
 | |
|         for oneSynth in allSynths:
 | |
|             if (oneSynth.freq == freq) & (oneSynth.tech == tech) & (oneSynth.module == module) & (oneSynth.width != 1):
 | |
|                 widthL += [oneSynth.width]
 | |
|                 osdict = oneSynth._asdict()
 | |
|                 metric += [osdict[var]]
 | |
|         metric = [x for _, x in sorted(zip(widthL, metric))] # ordering
 | |
|     else:
 | |
|         for w in widthsToGet:
 | |
|             for oneSynth in bestSynths:
 | |
|                 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]:
 | |
|         for mod in modules:
 | |
|             for w in widths:
 | |
|                 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): 
 | |
|                             m = oneSynth.delay
 | |
|                             best = oneSynth
 | |
| 
 | |
|                 if (best != None) & (best not in bestSynths):
 | |
|                     bestSynths += [best]
 | |
|     
 | |
|     file = open(filename, "w")
 | |
|     writer = csv.writer(file)
 | |
|     writer.writerow(['Module', 'Tech', 'Width', 'Target Freq', 'Delay', 'Area', 'L Power (nW)', 'D energy (nJ)'])
 | |
|     for synth in bestSynths:
 | |
|         writer.writerow(list(synth))
 | |
|     file.close()
 | |
|     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)
 | |
|         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'
 | |
|         elif normAddWidth != 1:
 | |
|             print('Equations are wrong, check normAddWidth')
 | |
|     else:
 | |
|         sub = 'N'
 | |
| 
 | |
|     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]
 | |
|             ind += 1
 | |
| 
 | |
|     eq = eq[3:] # chop off leading ' + '
 | |
| 
 | |
|     if (r2==None) or (spec==None):
 | |
|         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)))]
 | |
|         return legend_elements
 | |
| 
 | |
| def oneMetricPlot(module, 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
 | |
|     if ax is None:
 | |
|         ax = plt.gca()
 | |
| 
 | |
|     fullLeg = []
 | |
|     allWidths = []
 | |
|     allMetrics = []
 | |
| 
 | |
|     ale = (var != 'delay') # if not delay, must be area, leakage, or energy
 | |
|     modFit = fitDict[module]
 | |
|     fits = modFit[ale]
 | |
| 
 | |
|     if freq:
 | |
|         ls = '--'
 | |
|     else:
 | |
|         ls = '-'
 | |
| 
 | |
|     for spec in techSpecs:
 | |
|         metric = getVals(spec.tech, module, var, freq=freq)
 | |
|         
 | |
|         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
 | |
|             xp, pred, coefs, r2 = regress(widths, metric, fits, ale)
 | |
|             fullLeg += genLegend(fits, coefs, r2, spec, ale=ale)
 | |
|             c = color if color else spec.color
 | |
|             ax.scatter(widths, metric, color=c, marker=spec.shape)
 | |
|             ax.plot(xp, pred, color=c, linestyle=ls)
 | |
|             allWidths += widths
 | |
|             allMetrics += metric
 | |
| 
 | |
|     xp, pred, coefs, r2 = regress(allWidths, allMetrics, fits)
 | |
|     ax.plot(xp, pred, color='red', linestyle=ls)
 | |
| 
 | |
|     if norm:
 | |
|         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)"}
 | |
| 
 | |
|     ax.set_ylabel(ylabeldic[var])
 | |
|     ax.set_xticks(widths)
 | |
| 
 | |
|     if singlePlot or (var == 'lpower') or (var == 'denergy'):
 | |
|         ax.set_xlabel("Width (bits)")
 | |
|     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'
 | |
|         ax.add_artist(ax.legend(handles=fullLeg, loc=legLoc))
 | |
|         titleStr = "  (target  " + str(freq)+ "MHz)" if freq != None else " (best achievable delay)"
 | |
|         ax.set_title(module + titleStr)
 | |
|         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
 | |
|     '''
 | |
| 
 | |
|     funcArr = genFuncs(fits)
 | |
|     xp = np.linspace(min(widths)/2, max(widths)*1.1, 200)
 | |
|     xpToCalc = xp
 | |
| 
 | |
|     if ale:
 | |
|         widths = [w/normAddWidth for w in widths]
 | |
|         xpToCalc = [x/normAddWidth for x in xp]
 | |
| 
 | |
|     mat = []
 | |
|     for w in widths:
 | |
|         row = []
 | |
|         for func in funcArr:
 | |
|             row += [func(w)]
 | |
|         mat += [row]
 | |
|     
 | |
|     y = np.array(var, dtype=np.float)
 | |
|     coefs = opt.nnls(mat, y)[0]
 | |
| 
 | |
|     yp = []
 | |
|     for w in widths:
 | |
|         n = [func(w) for func in funcArr]
 | |
|         yp += [sum(np.multiply(coefs, n))]
 | |
|     r2 = skm.r2_score(y, yp)
 | |
| 
 | |
|     pred = []
 | |
|     for x in xpToCalc:
 | |
|         n = [func(x) for func in funcArr]
 | |
|         pred += [sum(np.multiply(coefs, n))]
 | |
| 
 | |
|     return xp, pred, coefs, r2
 | |
| 
 | |
| def makeCoefTable():
 | |
|     ''' writes CSV with each line containing the coefficients for a regression fit 
 | |
|         to a particular combination of module, metric (including both techs, normalized)
 | |
|     '''
 | |
|     file = open("ppaFitting.csv", "w")
 | |
|     writer = csv.writer(file)
 | |
|     writer.writerow(['Module', 'Metric', 'Target', '1', 'N', 'N^2', 'log2(N)', 'Nlog2(N)', 'R^2'])
 | |
| 
 | |
|     for module in modules:
 | |
|         for freq in [10, None]:
 | |
|             target = 'easy' if freq else 'hard'
 | |
|             for var in ['delay', 'area', 'lpower', 'denergy']:
 | |
|                 ale = (var != 'delay')
 | |
|                 metL = []
 | |
|                 modFit = fitDict[module]
 | |
|                 fits = modFit[ale]
 | |
| 
 | |
|                 for spec in techSpecs:
 | |
|                     metric = getVals(spec.tech, module, var, freq=freq)
 | |
|                     techdict = spec._asdict()
 | |
|                     norm = techdict[var]
 | |
|                     metL += [m/norm for m in metric]
 | |
| 
 | |
|                 xp, pred, coefs, r2 = regress(widths*2, metL, fits, ale)
 | |
|                 coefs = np.ndarray.tolist(coefs)
 | |
|                 coefsToWrite  = [None]*5
 | |
|                 fitTerms = 'clsgn'
 | |
|                 ind = 0
 | |
|                 for i in range(len(fitTerms)):
 | |
|                     if fitTerms[i] in fits:
 | |
|                         coefsToWrite[i] = coefs[ind]
 | |
|                         ind += 1
 | |
|                 row = [module, var, target] + coefsToWrite + [r2]
 | |
|                 writer.writerow(row)
 | |
| 
 | |
|     file.close()
 | |
| 
 | |
| def sigfig(num, figs):
 | |
|     return '{:g}'.format(float('{:.{p}g}'.format(num, p=figs)))
 | |
| 
 | |
| def makeEqTable():
 | |
|     ''' writes CSV with each line containing the equations for fits for each metric 
 | |
|         to a particular module (including both techs, normalized)
 | |
|     '''
 | |
|     file = open("ppaEquations.csv", "w")
 | |
|     writer = csv.writer(file)
 | |
|     writer.writerow(['Element', 'Best delay', 'Fast area', 'Fast leakage', 'Fast energy', 'Small area', 'Small leakage', 'Small energy'])
 | |
| 
 | |
|     for module in modules:
 | |
|         eqs = []
 | |
|         for freq in [None, 10]:
 | |
|             for var in ['delay', 'area', 'lpower', 'denergy']:
 | |
|                 if (var == 'delay') and (freq == 10):
 | |
|                     pass
 | |
|                 else:
 | |
|                     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)
 | |
| 
 | |
|     file.close()
 | |
| 
 | |
| 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:
 | |
|         funcArr += [lambda x: 1]
 | |
|     if 'l' in fits:
 | |
|         funcArr += [lambda x: x]
 | |
|     if 's' in fits:
 | |
|         funcArr += [lambda x: x**2]
 | |
|     if 'g' in fits:
 | |
|         funcArr += [lambda x: np.log2(x)]
 | |
|     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 
 | |
|         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=[]
 | |
|     for i in range(len(freqs)):
 | |
|         norm = freqs[i]/median
 | |
|         if (norm > 0.4) & (norm<1.4):
 | |
|             f += [freqs[i]]
 | |
|             d += [delays[i]]
 | |
|             a += [areas[i]]
 | |
|     
 | |
|     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
 | |
|     '''
 | |
| 
 | |
|     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
 | |
|             freqsL[ind] += [oneSynth.freq]
 | |
|             delaysL[ind] += [oneSynth.delay]
 | |
|             areasL[ind] += [oneSynth.area]
 | |
| 
 | |
|     median = np.median(list(flatten(freqsL)))
 | |
|     
 | |
|     f, (ax1, ax2) = plt.subplots(2, 1, sharex=True)
 | |
|     for ax in (ax1, ax2):
 | |
|         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
 | |
| 
 | |
|         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')]
 | |
| 
 | |
|     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):
 | |
|         width = mod
 | |
|         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
 | |
|     '''
 | |
|     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
 | |
|             freqsL[ind] += [oneSynth.freq]
 | |
|             delaysL[ind] += [oneSynth.delay]
 | |
|             areasL[ind] += [oneSynth.area]
 | |
| 
 | |
|     f, (ax1) = plt.subplots(1, 1)
 | |
|     ax2 = ax1.twinx()
 | |
| 
 | |
|     for ind in [0,1]:
 | |
|         areas = areasL[ind]
 | |
|         delays = delaysL[ind]
 | |
|         targets = freqsL[ind]
 | |
|         targets = [1000/f for f in targets]
 | |
|         
 | |
|         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)
 | |
|     
 | |
|     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)')]
 | |
| 
 | |
|     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))
 | |
| 
 | |
|     squarify(f)
 | |
| 
 | |
|     xvals = np.array(ax1.get_xlim())
 | |
|     frac = (min(flatten(delaysL))-xvals[0])/(xvals[1]-xvals[0])
 | |
|     areaLowerLim = min(flatten(areasL))-100
 | |
|     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='--')
 | |
| 
 | |
|     plt.savefig('./plots/squareareadelay_' + mod + '_' + str(width) + '.png')
 | |
|     # plt.show()
 | |
| 
 | |
| def squarify(fig):
 | |
|     ''' 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
 | |
|         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
 | |
|         fig.subplots_adjust(bottom=l, top=1-l)
 | |
| 
 | |
| 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']]
 | |
| 
 | |
|     freqs = [freq]
 | |
|     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):
 | |
|                     pass
 | |
|                 else:
 | |
|                     r2 = oneMetricPlot(mod, 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)]
 | |
| 
 | |
|             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)
 | |
|             else:
 | |
|                 axs[i, j].legend(handles=leg, handlelength=1.5)
 | |
|     
 | |
|     titleStr = "  (target  " + str(freq)+ "MHz)" if freq != None else ""
 | |
|     plt.suptitle(mod + titleStr)
 | |
|     plt.tight_layout(pad=0.05, w_pad=1, h_pad=0.5, rect=(0,0,1,0.97))
 | |
| 
 | |
|     if freq != 10: 
 | |
|         n = 'normalized' if norm else 'unnormalized'
 | |
|         saveStr = './plots/'+ n + '/' + mod + '.png'
 | |
|         plt.savefig(saveStr)
 | |
|     # plt.show()
 | |
| 
 | |
| def makeLineLegend():
 | |
|     ''' 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='green', label='sky90', marker='o')]
 | |
|     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'
 | |
|     plt.savefig(saveStr)
 | |
| 
 | |
| 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']:
 | |
|         allMetrics = []
 | |
|         muxes = ['mux2', 'mux4', 'mux8']
 | |
| 
 | |
|         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])
 | |
|             
 | |
|             if norm:
 | |
|                 techdict = spec._asdict()
 | |
|                 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
 | |
|                 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.set_ylabel('Delay (FO4)')
 | |
|     ax.set_xticks(inputs)
 | |
|     ax.set_xlabel("Number of inputs")
 | |
|     ax.set_title('mux timing')
 | |
|     
 | |
|     ax.legend(handles = fullLeg)
 | |
|     plt.savefig('./plots/mux.png')
 | |
| 
 | |
| def stdDevError():
 | |
|     ''' calculates std deviation and error for paper-writing purposes
 | |
|     '''
 | |
|     for var in ['delay', 'area', 'lpower', 'denergy']:
 | |
|         errlist = []
 | |
|         for module in modules:
 | |
|             ale = (var != 'delay')
 | |
|             metL = []
 | |
|             modFit = fitDict[module]
 | |
|             fits = modFit[ale]
 | |
|             funcArr = genFuncs(fits)
 | |
| 
 | |
|             for spec in techSpecs:
 | |
|                 metric = getVals(spec.tech, module, var)
 | |
|                 techdict = spec._asdict()
 | |
|                 norm = techdict[var]
 | |
|                 metL += [m/norm for m in metric]
 | |
| 
 | |
|             if ale:
 | |
|                 ws = [w/normAddWidth for w in widths]
 | |
|             else:
 | |
|                 ws = widths
 | |
|             ws = ws*2
 | |
|             mat = []
 | |
|             for w in ws:
 | |
|                 row = []
 | |
|                 for func in funcArr:
 | |
|                     row += [func(w)]
 | |
|                 mat += [row]
 | |
|             
 | |
|             y = np.array(metL, dtype=np.float)
 | |
|             coefs = opt.nnls(mat, y)[0]
 | |
| 
 | |
|             yp = []
 | |
|             for w in ws:
 | |
|                 n = [func(w) for func in funcArr]
 | |
|                 yp += [sum(np.multiply(coefs, n))]
 | |
| 
 | |
|             if (var == 'delay') & (module == 'flop'):
 | |
|                 pass
 | |
|             elif (module == 'mult') & ale:
 | |
|                 pass
 | |
|             else:
 | |
|                 for i in range(len(y)):
 | |
|                     errlist += [abs(y[i]/yp[i]-1)]
 | |
|                 # print(module, ' ', var, ' ', np.mean(errlist[-10:]))
 | |
|             
 | |
|         avgErr = np.mean(errlist)
 | |
|         stdv = np.std(errlist)
 | |
| 
 | |
|         print(var, ' ', avgErr, ' ', stdv)
 | |
| 
 | |
| def makePlotDirectory():
 | |
|     ''' 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')
 | |
|     if not os.path.exists(final_directory):
 | |
|         os.makedirs(final_directory)
 | |
|     os.chdir(final_directory)
 | |
| 
 | |
|     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', 'tsmc28']:
 | |
|                 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(current_directory)
 | |
|     
 | |
| if __name__ == '__main__':
 | |
|     ##############################
 | |
|     # set up stuff, global variables
 | |
|     widths = [8, 16, 32, 64, 128]
 | |
|     modules = ['priorityencoder', 'add', 'csa', 'shiftleft', 'comparator', 'flop', 'mux2', 'mux4', 'mux8', 'mult'] #, 'mux2d', 'mux4d', 'mux8d']
 | |
|     normAddWidth = 32 # divisor to use with N since normalizing to add_32
 | |
| 
 | |
|     fitDict = {'add': ['cg', 'l', 'l'], 'mult': ['cg', 's', 's'], 'comparator': ['cg', 'l', 'l'], 'csa': ['c', 'l', 'l'], 'shiftleft': ['cg', 'l', 'ln'], 'flop': ['c', 'l', 'l'], 'priorityencoder': ['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],  ['tsmc28', 'blue', '^', 12.2e-3, 209.286002, 1060.0, .08153281695882594]]
 | |
|     techSpecs = [TechSpec(*t) for t in techSpecs]
 | |
|     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')
 | |
|     makePlotDirectory()
 | |
| 
 | |
|     # ### other functions
 | |
|     # makeCoefTable()
 | |
|     # makeEqTable()
 | |
|     # muxPlot()
 | |
|     # stdDevError()
 | |
| 
 | |
|     for mod in modules:
 | |
|         for w in widths:
 | |
|             freqPlot('sky90', mod, w)
 | |
|             freqPlot('tsmc28', mod, w)
 | |
|         plotPPA(mod, norm=False)
 | |
|         plotPPA(mod, aleOpt=True)
 | |
|         plt.close('all') |