forked from Github_Repos/cvw
		
	
		
			
				
	
	
		
			711 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			711 lines
		
	
	
		
			26 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 numpy as np
 | 
						|
from collections import namedtuple
 | 
						|
import sklearn.metrics as skm
 | 
						|
 | 
						|
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/PPA/'+ 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
 | 
						|
    '''
 | 
						|
    plt.rcParams["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/PPA/'+ n + '/' + mod + '.png'
 | 
						|
        plt.savefig(saveStr)
 | 
						|
    # plt.show()
 | 
						|
 | 
						|
def makeLineLegend():
 | 
						|
    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/PPA/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/PPA/mux.png')
 | 
						|
 | 
						|
def stdDevError():
 | 
						|
    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)
 | 
						|
 | 
						|
    
 | 
						|
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')
 | 
						|
 | 
						|
    # ### function examples
 | 
						|
    # squareAreaDelay('sky90', 'add', 32)
 | 
						|
    # oneMetricPlot('mult', 'lpower')
 | 
						|
    # freqPlot('sky90', 'mux4', 16)
 | 
						|
    # plotBestAreas('add')
 | 
						|
    # makeCoefTable()
 | 
						|
    # makeEqTable()
 | 
						|
    # makeLineLegend()
 | 
						|
    # muxPlot()
 | 
						|
    # stdDevError()
 | 
						|
 | 
						|
    for mod in modules:
 | 
						|
        plotPPA(mod, norm=False)
 | 
						|
        plotPPA(mod, aleOpt=True)
 | 
						|
        for w in widths:
 | 
						|
            freqPlot('sky90', mod, w)
 | 
						|
            freqPlot('tsmc28', mod, w)
 | 
						|
        plt.close('all') |