From 81a869c92111377c94832b4aca8c51e1805c08d8 Mon Sep 17 00:00:00 2001 From: Madeleine Masser-Frye <51804758+mmasserfrye@users.noreply.github.com> Date: Wed, 25 May 2022 13:52:20 +0000 Subject: [PATCH] ppaAnalyze: docstrings and tsmc28 plotting --- pipelined/src/ppa/ppa.sv | 91 ++++++++++--------- synthDC/ppaAnalyze.py | 187 +++++++++++++++++++++++---------------- synthDC/ppaSynth.py | 44 ++++----- 3 files changed, 180 insertions(+), 142 deletions(-) diff --git a/pipelined/src/ppa/ppa.sv b/pipelined/src/ppa/ppa.sv index 61840a92c..49429e1ca 100644 --- a/pipelined/src/ppa/ppa.sv +++ b/pipelined/src/ppa/ppa.sv @@ -276,51 +276,6 @@ module ppa_shiftleft_128 #(parameter WIDTH=128) ( assign y = a << amt; endmodule -module ppa_shifter_8 #(parameter WIDTH=8) ( - input logic [WIDTH-1:0] A, - input logic [$clog2(WIDTH)-1:0] Amt, - input logic Right, Arith, W64, - output logic [WIDTH-1:0] Y); - - ppa_shifter #(WIDTH) sh (.*); -endmodule - -module ppa_shifter_16 #(parameter WIDTH=16) ( - input logic [WIDTH-1:0] A, - input logic [$clog2(WIDTH)-1:0] Amt, - input logic Right, Arith, W64, - output logic [WIDTH-1:0] Y); - - ppa_shifter #(WIDTH) sh (.*); -endmodule - -module ppa_shifter_32 #(parameter WIDTH=32) ( - input logic [WIDTH-1:0] A, - input logic [$clog2(WIDTH)-1:0] Amt, - input logic Right, Arith, W64, - output logic [WIDTH-1:0] Y); - - ppa_shifter #(WIDTH) sh (.*); -endmodule - -module ppa_shifter_64 #(parameter WIDTH=64) ( - input logic [WIDTH-1:0] A, - input logic [$clog2(WIDTH)-1:0] Amt, - input logic Right, Arith, W64, - output logic [WIDTH-1:0] Y); - - ppa_shifter #(WIDTH) sh (.*); -endmodule - -module ppa_shifter_128 #(parameter WIDTH=128) ( - input logic [WIDTH-1:0] A, - input logic [$clog2(WIDTH)-1:0] Amt, - input logic Right, Arith, W64, - output logic [WIDTH-1:0] Y); - - ppa_shifter #(WIDTH) sh (.*); -endmodule - module ppa_shifter #(parameter WIDTH=32) ( input logic [WIDTH-1:0] A, input logic [$clog2(WIDTH)-1:0] Amt, @@ -373,7 +328,51 @@ module ppa_shifter #(parameter WIDTH=32) ( assign Y = zshift[WIDTH-1:0]; endmodule -// just report one hot + // module ppa_shifter_8 #(parameter WIDTH=8) ( + // input logic [WIDTH-1:0] A, + // input logic [$clog2(WIDTH)-1:0] Amt, + // input logic Right, Arith, W64, + // output logic [WIDTH-1:0] Y); + + // ppa_shifter #(WIDTH) sh (.*); + // endmodule + + // module ppa_shifter_16 #(parameter WIDTH=16) ( + // input logic [WIDTH-1:0] A, + // input logic [$clog2(WIDTH)-1:0] Amt, + // input logic Right, Arith, W64, + // output logic [WIDTH-1:0] Y); + + // ppa_shifter #(WIDTH) sh (.*); + // endmodule + + // module ppa_shifter_32 #(parameter WIDTH=32) ( + // input logic [WIDTH-1:0] A, + // input logic [$clog2(WIDTH)-1:0] Amt, + // input logic Right, Arith, W64, + // output logic [WIDTH-1:0] Y); + + // ppa_shifter #(WIDTH) sh (.*); + // endmodule + + // module ppa_shifter_64 #(parameter WIDTH=64) ( + // input logic [WIDTH-1:0] A, + // input logic [$clog2(WIDTH)-1:0] Amt, + // input logic Right, Arith, W64, + // output logic [WIDTH-1:0] Y); + + // ppa_shifter #(WIDTH) sh (.*); + // endmodule + + // module ppa_shifter_128 #(parameter WIDTH=128) ( + // input logic [WIDTH-1:0] A, + // input logic [$clog2(WIDTH)-1:0] Amt, + // input logic Right, Arith, W64, + // output logic [WIDTH-1:0] Y); + + // ppa_shifter #(WIDTH) sh (.*); + // endmodule + module ppa_prioritythermometer #(parameter N = 8) ( input logic [N-1:0] a, output logic [N-1:0] y); diff --git a/synthDC/ppaAnalyze.py b/synthDC/ppaAnalyze.py index 0200d316d..935564afc 100755 --- a/synthDC/ppaAnalyze.py +++ b/synthDC/ppaAnalyze.py @@ -2,6 +2,7 @@ # Madeleine Masser-Frye mmasserfrye@hmc.edu 5/22 from distutils.log import error +from operator import index from statistics import median import subprocess import statistics @@ -12,13 +13,16 @@ import matplotlib.lines as lines import numpy as np -def getData(mod=None, width=None): +def getData(tech, mod=None, width=None): + ''' returns a list of lists + each list contains results of one synthesis that matches the input specs + ''' specStr = '' if mod != None: specStr = mod if width != None: specStr += ('_'+str(width)) - specStr += '*' + specStr += '*{}*'.format(tech) bashCommand = "grep 'Critical Path Length' runs/ppa_{}/reports/*qor*".format(specStr) outputCPL = subprocess.check_output(['bash','-c', bashCommand]) @@ -57,8 +61,13 @@ def getData(mod=None, width=None): return allSynths -def getVals(module, var, freq=None): - allSynths = getData(mod=module) +def getVals(tech, module, var, freq=None): + ''' for a specified tech, module, and variable/metric + returns a list of widths and the corresponding values for that metric with the appropriate units + works at a specified target frequency or if none is given, uses the synthesis with the min delay for each width + ''' + + allSynths = getData(tech, mod=module) if (var == 'delay'): ind = 3 @@ -96,10 +105,14 @@ def getVals(module, var, freq=None): if ('flop' in module) & (var == 'area'): metric = [m/2 for m in metric] # since two flops in each module + return widths, metric, units -def writeCSV(): - allSynths = getData() +def writeCSV(tech): + ''' writes a CSV with one line for every available synthesis for a specified tech + each line contains the module, width, target freq, and resulting metrics + ''' + allSynths = getData(tech) file = open("ppaData.csv", "w") writer = csv.writer(file) writer.writerow(['Module', 'Width', 'Target Freq', 'Delay', 'Area', 'L Power (nW)', 'D energy (mJ)']) @@ -109,7 +122,10 @@ def writeCSV(): file.close() -def genLegend(fits, coefs, module, r2): +def genLegend(fits, coefs, r2, tech): + ''' generates a list of two legend elements + labels line with fit equation and dots with tech and r squared of the fit + ''' coefsr = [str(round(c, 3)) for c in coefs] @@ -131,26 +147,18 @@ def genLegend(fits, coefs, module, r2): eq += " + " + coefsr[ind] + "*Nlog2(N)" ind += 1 - legend_elements = [lines.Line2D([0], [0], color='orange', label=eq), - lines.Line2D([0], [0], color='steelblue', ls='', marker='o', label=' R^2='+ str(round(r2, 4)))] + c = 'blue' if (tech == 'sky90') else 'green' + legend_elements = [lines.Line2D([0], [0], color=c, label=eq), + lines.Line2D([0], [0], color=c, ls='', marker='o', label=tech +' $R^2$='+ str(round(r2, 4)))] return legend_elements def oneMetricPlot(module, var, freq=None, ax=None, fits='clsgn'): + ''' 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 ''' - module: string module name - freq: int freq (MHz) - var: string delay, area, lpower, or denergy - fits: constant, linear, square, log2, Nlog2 - plots chosen variable vs width for all matching syntheses with regression - ''' - widths, metric, units = getVals(module, var, freq=freq) - coefs, r2, funcArr = regress(widths, metric, fits) - - xp = np.linspace(8, 140, 200) - pred = [] - for x in xp: - y = [func(x) for func in funcArr] - pred += [sum(np.multiply(coefs, y))] if ax is None: singlePlot = True @@ -158,11 +166,17 @@ def oneMetricPlot(module, var, freq=None, ax=None, fits='clsgn'): else: singlePlot = False - ax.scatter(widths, metric) - ax.plot(xp, pred, color='orange') + fullLeg = [] + for tech in ['sky90', 'tsmc28']: + c = 'blue' if (tech == 'sky90') else 'green' + widths, metric, units = getVals(tech, module, var, freq=freq) + xp, pred, leg = regress(widths, metric, tech, fits) + fullLeg += leg - legend_elements = genLegend(fits, coefs, module, r2) - ax.legend(handles=legend_elements) + ax.scatter(widths, metric, color=c) + ax.plot(xp, pred, color=c) + + ax.legend(handles=fullLeg) ax.set_xticks(widths) ax.set_xlabel("Width (bits)") @@ -172,7 +186,10 @@ def oneMetricPlot(module, var, freq=None, ax=None, fits='clsgn'): ax.set_title(module + " (target " + str(freq) + "MHz)") plt.show() -def regress(widths, var, fits='clsgn'): +def regress(widths, var, tech, fits='clsgn'): + ''' fits a curve to the given points + returns lists of x and y values to plot that curve and legend elements with the equation + ''' funcArr = genFuncs(fits) @@ -191,9 +208,21 @@ def regress(widths, var, fits='clsgn'): except: resid = 0 r2 = 1 - resid / (y.size * y.var()) - return coefs, r2, funcArr -def makeCoefTable(): + xp = np.linspace(8, 140, 200) + pred = [] + for x in xp: + n = [func(x) for func in funcArr] + pred += [sum(np.multiply(coefs, n))] + + leg = genLegend(fits, coefs, r2, tech) + + return xp, pred, leg + +def makeCoefTable(tech): + ''' writes CSV with each line containing the coefficients for a regression fit + to a particular combination of module, metric, and target frequency + ''' file = open("ppaFitting.csv", "w") writer = csv.writer(file) writer.writerow(['Module', 'Metric', 'Freq', '1', 'N', 'N^2', 'log2(N)', 'Nlog2(N)', 'R^2']) @@ -202,7 +231,7 @@ def makeCoefTable(): for comb in [['delay', 5000], ['area', 5000], ['area', 10]]: var = comb[0] freq = comb[1] - widths, metric, units = getVals(mod, freq, var) + widths, metric, units = getVals(tech, mod, freq, var) coefs, r2, funcArr = regress(widths, metric) row = [mod] + comb + np.ndarray.tolist(coefs) + [r2] writer.writerow(row) @@ -210,6 +239,9 @@ def makeCoefTable(): 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] @@ -224,11 +256,17 @@ def genFuncs(fits='clsgn'): return funcArr def noOutliers(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=[] + try: - med = statistics.median(freqs) + ind = delays.index(min(delays)) + med = freqs[ind] for i in range(len(freqs)): norm = freqs[i]/med if (norm > 0.25) & (norm<1.75): @@ -239,65 +277,67 @@ def noOutliers(freqs, delays, areas): return f, d, a -def freqPlot(mod, width): - allSynths = getData(mod=mod, width=width) +def freqPlot(tech, mod, width): + ''' plots delay, area, area*delay, and area*delay^2 for syntheses with specified tech, module, width + ''' + allSynths = getData(tech, mod=mod, width=width) - freqsV, delaysV, areasV, freqsA, delaysA, areasA = ([] for i in range(6)) + freqsL, delaysL, areasL = ([[], []] for i in range(3)) for oneSynth in allSynths: if (mod == oneSynth[0]) & (width == oneSynth[1]): - if (1000/oneSynth[3] < oneSynth[2]): - freqsV += [oneSynth[2]] - delaysV += [oneSynth[3]] - areasV += [oneSynth[4]] - else: - freqsA += [oneSynth[2]] - delaysA += [oneSynth[3]] - areasA += [oneSynth[4]] + ind = (1000/oneSynth[3] < oneSynth[2]) # when delay is within target clock period + freqsL[ind] += [oneSynth[2]] + delaysL[ind] += [oneSynth[3]] + areasL[ind] += [oneSynth[4]] - if ('flop' in mod): # since two flops in each module - areasA = [m/2 for m in areasA] - areasV = [m/2 for m in areasV] + f, (ax1, ax2, ax3, ax4, ax5) = plt.subplots(5, 1, sharex=True) - freqsA, delaysA, areasA = noOutliers(freqsA, delaysA, areasA) - freqsV, delaysV, areasV = noOutliers(freqsV, delaysV, areasV) + for ind in [0,1]: + areas = areasL[ind] + delays = delaysL[ind] + freqs = freqsL[ind] - adprodA, adprodV = adprodpow(areasA, delaysA, areasV, delaysV, 1) - adpowA, adpowV = adprodpow(areasA, delaysA, areasV, delaysV, 2) + if ('flop' in mod): areas = [m/2 for m in areas] # since two flops in each module + freqs, delays, areas = noOutliers(freqs, delays, areas) + c = 'blue' if ind else 'green' + adprod = adprodpow(areas, delays, 2) + adpow = adprodpow(areas, delays, 3) + adpow2 = adprodpow(areas, delays, 4) + ax1.scatter(freqs, delays, color=c) + ax2.scatter(freqs, areas, color=c) + ax3.scatter(freqs, adprod, color=c) + ax4.scatter(freqs, adpow, color=c) + ax5.scatter(freqs, adpow2, 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')] - f, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, sharex=True) - ax1.scatter(freqsA, delaysA, color='green') - ax1.scatter(freqsV, delaysV, color='blue') - ax2.scatter(freqsA, areasA, color='green') - ax2.scatter(freqsV, areasV, color='blue') - ax3.scatter(freqsA, adprodA, color='green') - ax3.scatter(freqsV, adprodV, color='blue') - ax4.scatter(freqsA, adpowA, color='green') - ax4.scatter(freqsV, adpowV, color='blue') ax1.legend(handles=legend_elements) + ax4.set_xlabel("Target Freq (MHz)") ax1.set_ylabel('Delay (ns)') ax2.set_ylabel('Area (sq microns)') ax3.set_ylabel('Area * Delay') - ax4.set_ylabel('Area * Delay^2') + ax4.set_ylabel('Area * $Delay^2$') ax1.set_title(mod + '_' + str(width)) plt.show() -def adprodpow(areasA, delaysA, areasV, delaysV, pow): - resultA = [] - resultV = [] +def adprodpow(areas, delays, pow): + ''' for each value in [areas] returns area*delay^pow + helper function for freqPlot''' + result = [] - for i in range(len(areasA)): - resultA += [(areasA[i])*(delaysA[i])**pow] - for i in range(len(areasV)): - resultV += [(areasV[i])*(delaysV[i])**pow] + for i in range(len(areas)): + result += [(areas[i])*(delays[i])**pow] - return resultA, resultV + return result def plotPPA(mod, freq=None): + ''' for the module specified, plots width vs delay, area, leakage power, and dynamic energy with fits + if no freq specified, uses the synthesis with min delay for each width + overlays data from both techs + ''' fig, axs = plt.subplots(2, 2) oneMetricPlot(mod, 'delay', ax=axs[0,0], fits='clg', freq=freq) oneMetricPlot(mod, 'area', ax=axs[0,1], fits='s', freq=freq) @@ -308,13 +348,12 @@ def plotPPA(mod, freq=None): plt.show() -# plotPPA('alu') # writeCSV() + # look at comparaotro 32 -# for x in ['add', 'mult', 'comparator']: -# for y in [16, 32, 64, 128]: -# freqPlot(x, y) -freqPlot('flop', 8) +# for x in ['add', 'mult', 'comparator', 'alu', 'csa']: +# for y in [8, 16, 32, 64, 128]: +# freqPlot('sky90', x, y) -# plotPPA('alu') \ No newline at end of file +freqPlot('sky90', 'mult', 32) \ No newline at end of file diff --git a/synthDC/ppaSynth.py b/synthDC/ppaSynth.py index 62c87045d..949c93988 100755 --- a/synthDC/ppaSynth.py +++ b/synthDC/ppaSynth.py @@ -42,34 +42,34 @@ def getData(): return allSynths allSynths = getData() -arr = [-40, -20, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 14, 20, 40] +arr = [-40, -20, -8, -6, -4, -2, 0, 2, 4, 6, 8, 12, 20, 40] -widths = [32, 64] -modules = ['flop', 'flopr'] -tech = 'sky90' +widths = [8, 16, 32, 64, 128] +modules = ['add'] +tech = 'tsmc28' LoT = [] -# # # # initial sweep to get estimate of min delay -# freqs = ['10000', '15000', '20000'] -# for module in modules: -# for width in widths: -# for freq in freqs: -# LoT += [[module, width, tech, freq]] +# # # initial sweep to get estimate of min delay +freqs = [10000, 15000, 20000] +for module in modules: + for width in widths: + for freq in freqs: + LoT += [[module, width, tech, freq]] -# thorough sweep based on estimate of min delay -for m in modules: - for w in widths: - delays = [] - for oneSynth in allSynths: - if (oneSynth[0] == m) & (oneSynth[1] == w): - delays += [oneSynth[3]] - try: f = 1000/min(delays) - except: print(m) - for freq in [str(round(f+f*x/100)) for x in arr]: - LoT += [[m, w, tech, freq]] + +# # thorough sweep based on estimate of min delay +# for m in modules: +# for w in widths: +# delays = [] +# for oneSynth in allSynths: +# if (oneSynth[0] == m) & (oneSynth[1] == w): +# delays += [oneSynth[3]] +# try: f = 1000/min(delays) +# except: print(m) +# for freq in [str(round(f+f*x/100)) for x in arr]: +# LoT += [[m, w, tech, freq]] deleteRedundant(LoT) - pool = Pool() pool.starmap(runCommand, LoT) pool.close() \ No newline at end of file