From fde8794282729e0fe7de6844e47571baa13d6821 Mon Sep 17 00:00:00 2001 From: Bob Vanhoof Date: Mon, 1 Mar 2021 09:56:25 +0100 Subject: [PATCH] calibre pex modifications to run hierarchical pex --- compiler/characterizer/delay.py | 16 +++-- compiler/characterizer/simulation.py | 9 ++- compiler/characterizer/stimuli.py | 2 +- compiler/sram/sram.py | 1 + compiler/sram/sram_base.py | 4 +- compiler/verify/calibre.py | 99 ++++++++++++++++++++++------ 6 files changed, 99 insertions(+), 32 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 5d2dd09a..a7114293 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -185,10 +185,10 @@ class delay(simulation): meas.targ_name_no_port)) self.dout_volt_meas[-1].meta_str = meas.meta_str - if not OPTS.use_pex: - self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name + "{}", "FALL", "RISE", measure_scale=1e9) - else: + if OPTS.use_pex and OPTS.pex_exe[0] != 'calibre': self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9) + else: + self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name + "{}", "FALL", "RISE", measure_scale=1e9) self.sen_meas.meta_str = sram_op.READ_ZERO self.sen_meas.meta_add_delay = True @@ -235,13 +235,15 @@ class delay(simulation): storage_names = cell_inst.mod.get_storage_net_names() debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes" "supported for characterization. Storage nets={}").format(storage_names)) - if not OPTS.use_pex: - q_name = cell_name + '.' + str(storage_names[0]) - qbar_name = cell_name + '.' + str(storage_names[1]) - else: + + #todo: bob vanhoof's modification: hierarchical pex + if OPTS.use_pex and OPTS.pex_exe[0] != 'calibre': bank_num = self.sram.get_bank_num(self.sram.name, bit_row, bit_col) q_name = "bitcell_Q_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col) qbar_name = "bitcell_Q_bar_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col) + else: + q_name = cell_name + '.' + str(storage_names[0]) + qbar_name = cell_name + '.' + str(storage_names[1]) # Bit measures, measurements times to be defined later. The measurement names must be unique # but they is enforced externally. {} added to names to differentiate between ports allow the diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index f6ee260d..965dd087 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -426,7 +426,8 @@ class simulation(): """ port = self.read_ports[0] - if not OPTS.use_pex: + #todo: modified by bob vanhoof to take into account calibre pex + if not OPTS.use_pex or (OPTS.use_pex and OPTS.pex_exe[0] == 'calibre'): self.graph.get_all_paths('{}{}'.format("clk", port), '{}{}_{}'.format(self.dout_name, port, self.probe_data)) @@ -482,7 +483,8 @@ class simulation(): debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.") enable_name = sa_mods[0].get_enable_name() sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0]) - if OPTS.use_pex: + # todo: modified by bob vanhoof + if OPTS.use_pex and (OPTS.pex_exe[0] != 'calibre'): sen_name = sen_name.split('.')[-1] return sen_name @@ -540,7 +542,8 @@ class simulation(): exclude_set = self.get_bl_name_search_exclusions() for int_net in [cell_bl, cell_br]: bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set)) - if OPTS.use_pex: + #todo modified by bob vanhoof + if OPTS.use_pex and OPTS.pex_exe[0] != 'calibre': for i in range(len(bl_names)): bl_names[i] = bl_names[i].split('.')[-1] return bl_names[0], bl_names[1] diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index b7a84cb6..046a3faf 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -52,7 +52,7 @@ class stimuli(): def inst_model(self, pins, model_name): """ Function to instantiate a generic model with a set of pins """ - if OPTS.use_pex: + if OPTS.use_pex and OPTS.pex_exe[0] != 'calibre': self.inst_pex_model(pins, model_name) else: self.sf.write("X{0} ".format(model_name)) diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index d5e223f8..d872e118 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -136,6 +136,7 @@ class sram(): if OPTS.use_pex: start_time = datetime.datetime.now() # Output the extracted design if requested + #todo: bob vanhoof: re-generate the layout so that it now does include the pex labels pexname = OPTS.output_path + self.s.name + ".pex.sp" spname = OPTS.output_path + self.s.name + ".sp" verify.run_pex(self.s.name, gdsname, spname, output=pexname) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index a3beacfa..3d91dbb1 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -201,7 +201,9 @@ class sram_base(design, verilog, lef): highest_coord = self.find_highest_coords() self.width = highest_coord[0] self.height = highest_coord[1] - if OPTS.use_pex: + #todo: bob vanhoof: this now does not automatically propagate the pex labels when the lvs tool is calibre + if OPTS.use_pex and not OPTS.lvs_exe[0] == "calibre": + debug.info(2,"adding global pex labels") self.add_global_pex_labels() self.add_boundary(ll=vector(0, 0), ur=vector(self.width, self.height)) diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 852451ce..71989911 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -125,6 +125,8 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out run_file = output_path + "run_lvs.sh" f = open(run_file, "w") f.write("#!/bin/sh\n") + #PDK_DIR=os.environ.get("PDK_DIR") + #f.write("export PDK_DIR={}\n".format(PDK_DIR)) cmd = "{0} -gui -lvs lvs_runset -batch".format(OPTS.lvs_exe[1]) f.write(cmd) @@ -154,38 +156,80 @@ def write_pex_script(cell_name, extract, output, final_verification=False, outpu from tech import drc pex_rules = drc["xrc_rules"] - pex_runset = { - 'pexRulesFile': pex_rules, - 'pexRunDir': output_path, - 'pexLayoutPaths': cell_name + ".gds", - 'pexLayoutPrimary': cell_name, - 'pexSourcePath': cell_name + ".sp", - 'pexSourcePrimary': cell_name, - 'pexReportFile': cell_name + ".pex.report", - 'pexPexNetlistFile': output, - 'pexPexReportFile': cell_name + ".pex.report", - 'pexMaskDBFile': cell_name + ".maskdb", - 'cmnFDIDEFLayoutPath': cell_name + ".def", - } - # write the runset file - f = open(output_path + "pex_runset", "w") - for k in sorted(iter(pex_runset.keys())): - f.write("*{0}: {1}\n".format(k, pex_runset[k])) + # write the rules file + f = open(OPTS.openram_temp + "pex_rules", "w") + f.write('// Rules file, created by OpenRAM, (c) Bob Vanhoof\n') + f.write('\n') + f.write('LAYOUT PATH "' + OPTS.openram_temp + cell_name + '.gds"\n') + f.write('LAYOUT PRIMARY ' + cell_name + '\n') + f.write('LAYOUT SYSTEM GDSII\n') + f.write('\n') + f.write('SOURCE PATH "' + OPTS.openram_temp + cell_name + '.sp"\n') + f.write('SOURCE PRIMARY ' + cell_name +'\n') + f.write('SOURCE SYSTEM SPICE\n') + f.write('SOURCE CASE YES\n') + f.write('\n') + f.write('MASK SVDB DIRECTORY "svdb" QUERY XRC\n') + f.write('\n') + f.write('LVS REPORT "' + OPTS.openram_temp + cell_name + '.pex.report"\n') + f.write('LVS REPORT OPTION NONE\n') + f.write('LVS FILTER UNUSED OPTION NONE SOURCE\n') + f.write('LVS FILTER UNUSED OPTION NONE LAYOUT\n') + f.write('LVS POWER NAME vdd\n') + f.write('LVS GROUND NAME gnd\n') + f.write('LVS RECOGNIZE GATES ALL\n') + f.write('LVS CELL SUPPLY YES\n') + f.write('LVS PUSH DEVICES SEPARATE PROPERTIES YES\n') + f.write('\n') + f.write('PEX NETLIST "' + output + '" HSPICE 1 SOURCENAMES GROUND gnd\n') + f.write('PEX REDUCE ANALOG NO\n') + f.write('PEX NETLIST UPPERCASE KEYWORDS NO\n') + f.write('PEX NETLIST VIRTUAL CONNECT YES\n') + f.write('PEX NETLIST NOXREF NET NAMES YES\n') + f.write('PEX NETLIST MUTUAL RESISTANCE YES\n') + f.write('PEX NETLIST EXPORT PORTS YES\n') + f.write('PEX PROBE FILE "probe_file"\n') + f.write('\n') + f.write('VIRTUAL CONNECT COLON NO\n') + f.write('VIRTUAL CONNECT REPORT NO\n') + f.write('VIRTUAL CONNECT NAME vdd gnd\n') + f.write('\n') + f.write('DRC ICSTATION YES\n') + f.write('\n') + f.write('INCLUDE "'+ pex_rules +'"\n') + f.close() + + # write probe file + #TODO: get from cell name + f = open(OPTS.openram_temp + "probe_file", "w") + f.write('CELL cell_1rw\n') + f.write(' Q 0.100 0.510 11\n') + f.write(' Q_bar 0.520 0.510 11\n') f.close() # Create an auxiliary script to run calibre with the runset run_file = output_path + "run_pex.sh" f = open(run_file, "w") f.write("#!/bin/sh\n") - cmd = "{0} -gui -pex pex_runset -batch".format(OPTS.pex_exe[1]) - + cmd = "{0} -lvs -hier -genhcells -spice svdb/{1}.sp -turbo -hyper cmp {2}".format(OPTS.pex_exe[1], + cell_name, + 'pex_rules') + f.write(cmd) + f.write("\n") + cmd = "sed '/dummy/d' svdb/{0}.hcells | sed '/replica_column/d' | sed '/replica_cell/d' > hcell_file".format(cell_name) + f.write(cmd) + f.write("\n") + cmd = "{0} -xrc -pdb -turbo -xcell hcell_file -full -rc {1}".format(OPTS.pex_exe[1], 'pex_rules') + f.write(cmd) + f.write("\n") + cmd = "{0} -xrc -fmt -full {1}".format(OPTS.pex_exe[1],'pex_rules') f.write(cmd) f.write("\n") f.close() os.system("chmod u+x {}".format(run_file)) - return pex_runset + return None def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=False): @@ -194,6 +238,9 @@ def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=Fals global num_drc_runs num_drc_runs += 1 + # Copy file to local dir if it isn't already + #if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)): + # hutil.copy(gds_name, OPTS.openram_temp) drc_runset = write_drc_script(cell_name, gds_name, extract, final_verification, OPTS.openram_temp) @@ -237,6 +284,12 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): lvs_runset = write_lvs_script(cell_name, gds_name, sp_name, final_verification, OPTS.openram_temp) + # Copy file to local dir if it isn't already + #if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)): + # shutil.copy(gds_name, OPTS.openram_temp) + #if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)): + # shutil.copy(sp_name, OPTS.openram_temp) + (outfile, errfile, resultsfile) = run_script(cell_name, "lvs") # check the result for these lines in the summary: @@ -318,6 +371,12 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False) write_pex_script(cell_name, True, output, final_verification, OPTS.openram_temp) + # Copy file to local dir if it isn't already + #if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)): + # shutil.copy(gds_name, OPTS.openram_temp) + #if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)): + # shutil.copy(sp_name, OPTS.openram_temp) + (outfile, errfile, resultsfile) = run_script(cell_name, "pex")