diff --git a/synthDC/ppa/README b/synthDC/ppa/README new file mode 100644 index 00000000..2bdf2eda --- /dev/null +++ b/synthDC/ppa/README @@ -0,0 +1,32 @@ +Wally PPA Study +July 8, 2022 +Madeleine Masser-Frye +mmasserfrye@hmc.edu +___________________ +Apologies for issues in this folder, code was written originally for individual use and documentation was compiled in haste. Please feel free to contact the author with questions. + +------------------- +ppaSynth.py + +Run to synthesize datapath modules from src/ppa. +To run a specific combination of widths, modules, techs, and freqs, +modify those lists and use allCombos() to generate synthsToRun (comment out freqSweep). +To run a sweep of frequencies around the best delay found in existing syntheses (according to bestSynths.csv), modify the parameters and use freqSweep to generate synthsToRun. +To remove synths to be run that already exist in /runs from synthsToRun, use filterRedundant(). +Syntheses run in parallel but you may encounter issues doing more than a dozen or so at once. +------------------- +ppaAnalyze.py + +Run to plot results of PPA syntheses. See docstrings for individual function info. +------------------- +bestSynths.csv + +Results of the synthesis for each combination of module, width, and tech with the best achievable delay. Generated by csvOfBest() in ppaAnalyze.py +------------------- +ppaFitting.csv & ppaEquations.csv + +Representations of the regression fit for each module and metric. Generated in ppaAnalyze.py by makeCoefTable() and makeEqTable(). +------------------- +ppaData.csv + +Results from all synthesis runs. Generated by synthsintocsv() and used by synthsfromcsv in ppaAnalyze.py. diff --git a/synthDC/ppa/ppaAnalyze.py b/synthDC/ppa/ppaAnalyze.py index 9e2d3640..2dce62ae 100755 --- a/synthDC/ppa/ppaAnalyze.py +++ b/synthDC/ppa/ppaAnalyze.py @@ -8,9 +8,11 @@ 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 +import os def synthsfromcsv(filename): Synth = namedtuple("Synth", "module tech width freq delay area lpower denergy") @@ -518,8 +520,8 @@ def plotPPA(mod, freq=None, norm=True, aleOpt=False): 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) + with mpl.rc_context({"figure.figsize": (7,3.46)}): + fig, axs = plt.subplots(2, 2) arr = [['delay', 'area'], ['lpower', 'denergy']] @@ -555,6 +557,8 @@ def plotPPA(mod, freq=None, norm=True, aleOpt=False): # 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='-')] @@ -619,6 +623,8 @@ def muxPlot(fits='clsgn', norm=True): 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: @@ -668,6 +674,30 @@ def stdDevError(): 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__': ############################## @@ -686,26 +716,22 @@ if __name__ == '__main__': ############################## # cleanup() # run to remove garbage synth runs - # synthsintocsv() # slow, run only when new synth runs to add to csv + synthsintocsv() # slow, run only when new synth runs to add to csv allSynths = synthsfromcsv('ppaData.csv') # your csv here! bestSynths = csvOfBest('bestSynths.csv') + makePlotDirectory() - # ### function examples - # squareAreaDelay('sky90', 'add', 32) - # oneMetricPlot('mult', 'lpower') - # freqPlot('sky90', 'mux4', 16) - # plotBestAreas('add') + # ### other functions # 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) + plotPPA(mod, norm=False) + plotPPA(mod, aleOpt=True) plt.close('all') \ No newline at end of file diff --git a/synthDC/ppa/ppaSynth.py b/synthDC/ppa/ppaSynth.py index 842140b5..cbfd5253 100755 --- a/synthDC/ppa/ppaSynth.py +++ b/synthDC/ppa/ppaSynth.py @@ -10,47 +10,64 @@ def runCommand(module, width, tech, freq): command = "make synth DESIGN=ppa_{}_{} TECH={} DRIVE=INV FREQ={} MAXOPT=1 MAXCORES=1".format(module, width, tech, freq) subprocess.Popen(command, shell=True) -def deleteRedundant(LoT): +def deleteRedundant(synthsToRun): '''removes any previous runs for the current synthesis specifications''' synthStr = "rm -rf runs/ppa_{}_{}_rv32e_{}nm_{}_*" - for synth in LoT: + for synth in synthsToRun: bashCommand = synthStr.format(*synth) outputCPL = subprocess.check_output(['bash','-c', bashCommand]) -if __name__ == '__main__': - - LoT = [] +def freqSweep(module, width, tech): synthsToRun = [] - - ##### Run specific syntheses - # widths = [8] - # modules = ['mult', 'add', 'shiftleft', 'flop', 'comparator', 'priorityencoder', 'add', 'csa', 'mux2', 'mux4', 'mux8'] - # techs = ['sky90'] - # freqs = [5000] - # for w in widths: - # for module in modules: - # for tech in techs: - # for freq in freqs: - # LoT += [[module, str(w), tech, str(freq)]] - - ##### Run a sweep based on best delay found in existing syntheses arr = [-8, -6, -4, -2, 0, 2, 4, 6, 8] allSynths = synthsfromcsv('bestSynths.csv') for synth in allSynths: - f = 1000/synth.delay - for freq in [round(f+f*x/100) for x in arr]: - LoT += [[synth.module, str(synth.width), synth.tech, str(freq)]] - - ##### Only do syntheses for which a run doesn't already exist + if (synth.module == module) & (synth.tech == tech) & (synth.width == width): + f = 1000/synth.delay + for freq in [round(f+f*x/100) for x in arr]: + synthsToRun += [[synth.module, str(synth.width), synth.tech, str(freq)]] + return synthsToRun + +def filterRedundant(synthsToRun): bashCommand = "find . -path '*runs/ppa*rv32e*' -prune" output = subprocess.check_output(['bash','-c', bashCommand]) specReg = re.compile('[a-zA-Z0-9]+') allSynths = output.decode("utf-8").split('\n')[:-1] allSynths = [specReg.findall(oneSynth)[2:7] for oneSynth in allSynths] allSynths = [oneSynth[0:2] + [oneSynth[3][:-2]] + [oneSynth[4]] for oneSynth in allSynths] - for synth in LoT: + output = [] + for synth in synthsToRun: if (synth not in allSynths): - synthsToRun += [synth] + output += [synth] + return output + +def allCombos(widths, modules, techs, freqs): + synthsToRun = [] + for w in widths: + for module in modules: + for tech in techs: + for freq in freqs: + synthsToRun += [[module, str(w), tech, str(freq)]] + return synthsToRun + + +if __name__ == '__main__': + + ##### Run specific syntheses + widths = [8, 16, 32, 64, 128] + modules = ['mult', 'add', 'shiftleft', 'flop', 'comparator', 'priorityencoder', 'add', 'csa', 'mux2', 'mux4', 'mux8'] + techs = ['sky90', 'tsmc28'] + freqs = [5000] + synthsToRun = allCombos(widths, modules, techs, freqs) + + ##### Run a sweep based on best delay found in existing syntheses + module = 'add' + width = 32 + tech = 'sky90' + synthsToRun = freqSweep(module, width, tech) + + ##### Only do syntheses for which a run doesn't already exist + synthsToRun = filterRedundant(synthsToRun) pool = Pool(processes=25) - pool.starmap(runCommand, synthsToRun) \ No newline at end of file + pool.starmap(print, synthsToRun) \ No newline at end of file