diff --git a/README.md b/README.md index 674d13f2..48b15766 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ things that need to be fixed. ## Dependencies The OpenRAM compiler has very few dependencies: -+ [Ngspice] 26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later) ++ [Ngspice] 26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later) or [Xyce] 7.2 (or later) + Python 3.5 or higher + Various Python packages (pip install -r requirements.txt) @@ -214,6 +214,7 @@ If I forgot to add you, please let me know! [Netgen]: http://opencircuitdesign.com/netgen/ [Qflow]: http://opencircuitdesign.com/qflow/history.html [Ngspice]: http://ngspice.sourceforge.net/ +[Xyce]: http://xyce.sandia.gov/ [OSUPDK]: https://vlsiarch.ecen.okstate.edu/flow/ [FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index d99c7363..fe295273 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -132,7 +132,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): for subinst, conns in zip(self.insts, self.conns): if subinst in self.graph_inst_exclude: continue - subinst_name = inst_name + '.X' + subinst.name + subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name subinst_ports = self.translate_nets(conns, port_dict, inst_name) subinst.mod.build_graph(graph, subinst_name, subinst_ports) @@ -148,7 +148,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} debug.info(3, "Instance name={}".format(inst_name)) for subinst, conns in zip(self.insts, self.conns): - subinst_name = inst_name + '.X' + subinst.name + subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name subinst_ports = self.translate_nets(conns, port_dict, inst_name) for si_port, conn in zip(subinst_ports, conns): # Only add for first occurrence @@ -166,7 +166,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if conn in port_dict: converted_conns.append(port_dict[conn]) else: - converted_conns.append("{}.{}".format(inst_name, conn)) + converted_conns.append("{0}{2}{1}".format(inst_name, conn, OPTS.hier_seperator)) return converted_conns def add_graph_edges(self, graph, port_nets): diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index 00ad3b3e..d5bcdbc6 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -24,9 +24,10 @@ debug.info(1, "Initializing characterizer...") OPTS.spice_exe = "" if not OPTS.analytical_delay: - debug.info(1, "Finding spice simulator.") - if OPTS.spice_name != "": + # Capitalize Xyce + if OPTS.spice_name == "xyce": + OPTS.spice_name = "Xyce" OPTS.spice_exe=find_exe(OPTS.spice_name) if OPTS.spice_exe=="" or OPTS.spice_exe==None: debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_name), 1) @@ -35,6 +36,7 @@ if not OPTS.analytical_delay: if OPTS.spice_name == "Xyce": (OPTS.mpi_name, OPTS.mpi_exe) = get_tool("mpi", ["mpirun"]) + OPTS.hier_seperator = ":" else: OPTS.mpi_name = None OPTS.mpi_exe = "" @@ -45,6 +47,12 @@ if not OPTS.analytical_delay: if OPTS.spice_exe == "": debug.error("No recognizable spice version found. Unable to perform characterization.", 1) + else: + debug.info(1, "Finding spice simulator: {} ({})".format(OPTS.spice_name, OPTS.spice_exe)) + if OPTS.mpi_name: + debug.info(1, "MPI for spice simulator: {} ({})".format(OPTS.mpi_name, OPTS.mpi_exe)) + debug.info(1, "Simulation threads: {}".format(OPTS.num_sim_threads)) + else: debug.info(1, "Analytical model enabled.") diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 168a73e9..3e071a69 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -69,7 +69,7 @@ class delay(simulation): for meas in meas_list: name = meas.name.lower() debug.check(name not in name_set, ("SPICE measurements must have unique names. " - "Duplicate name={}").format(name)) + "Duplicate name={0}").format(name)) name_set.add(name) def create_read_port_measurement_objects(self): @@ -77,7 +77,7 @@ class delay(simulation): self.read_lib_meas = [] self.clk_frmt = "clk{0}" # Unformatted clock name - targ_name = "{0}{1}_{2}".format(self.dout_name, "{}", self.probe_data) # Empty values are the port and probe data bit + targ_name = "{0}{{}}_{1}".format(self.dout_name, self.probe_data) # Empty values are the port and probe data bit self.delay_meas = [] self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "RISE", "RISE", measure_scale=1e9)) self.delay_meas[-1].meta_str = sram_op.READ_ONE # Used to index time delay values when measurements written to spice file. @@ -166,7 +166,7 @@ class delay(simulation): self.dout_volt_meas = [] for meas in self.delay_meas: # Output voltage measures - self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name), + self.dout_volt_meas.append(voltage_at_measure("v_{0}".format(meas.name), meas.targ_name_no_port)) self.dout_volt_meas[-1].meta_str = meas.meta_str @@ -186,7 +186,7 @@ class delay(simulation): self.read_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []} meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE) for cycle in meas_cycles: - meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) + meas_tag = "a{0}_b{1}_{2}".format(self.probe_address, self.probe_data, cycle.name) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) for polarity, meas in single_bit_meas.items(): meas.meta_str = cycle @@ -200,7 +200,7 @@ class delay(simulation): self.write_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []} meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE) for cycle in meas_cycles: - meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) + meas_tag = "a{0}_b{1}_{2}".format(self.probe_address, self.probe_data, cycle.name) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) for polarity, meas in single_bit_meas.items(): meas.meta_str = cycle @@ -219,20 +219,20 @@ class delay(simulation): (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, bit_row, bit_col) 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)) + "supported for characterization. Storage nets={0}").format(storage_names)) 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]) + q_name = cell_name + OPTS.hier_seperator + str(storage_names[0]) + qbar_name = cell_name + OPTS.hier_seperator + 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 # measurements are independent of the ports - q_meas = voltage_at_measure("v_q_{}".format(meas_tag), q_name) - qbar_meas = voltage_at_measure("v_qbar_{}".format(meas_tag), qbar_name) + q_meas = voltage_at_measure("v_q_{0}".format(meas_tag), q_name) + qbar_meas = voltage_at_measure("v_qbar_{0}".format(meas_tag), qbar_name) return {bit_polarity.NONINVERTING: q_meas, bit_polarity.INVERTING: qbar_meas} @@ -242,15 +242,15 @@ class delay(simulation): # FIXME: There should be a default_read_port variable in this case, pathing is done with this # but is never mentioned otherwise port = self.read_ports[0] - sen_and_port = self.sen_name+str(port) + sen_and_port = self.sen_name + str(port) bl_and_port = self.bl_name.format(port) # bl_name contains a '{}' for the port # Isolate the s_en and bitline paths - debug.info(1, "self.bl_name = {}".format(self.bl_name)) - debug.info(1, "self.graph.all_paths = {}".format(self.graph.all_paths)) + debug.info(1, "self.bl_name = {0}".format(self.bl_name)) + debug.info(1, "self.graph.all_paths = {0}".format(self.graph.all_paths)) sen_paths = [path for path in self.graph.all_paths if sen_and_port in path] bl_paths = [path for path in self.graph.all_paths if bl_and_port in path] - debug.check(len(sen_paths)==1, 'Found {} paths which contain the s_en net.'.format(len(sen_paths))) - debug.check(len(bl_paths)==1, 'Found {} paths which contain the bitline net.'.format(len(bl_paths))) + debug.check(len(sen_paths)==1, 'Found {0} paths which contain the s_en net.'.format(len(sen_paths))) + debug.check(len(bl_paths)==1, 'Found {0} paths which contain the bitline net.'.format(len(bl_paths))) sen_path = sen_paths[0] bitline_path = bl_paths[0] @@ -286,11 +286,11 @@ class delay(simulation): # Create the measurements path_meas = [] - for i in range(len(path)-1): - cur_net, next_net = path[i], path[i+1] - cur_dir, next_dir = path_dirs[i], path_dirs[i+1] - meas_name = "delay_{}_to_{}".format(cur_net, next_net) - if i+1 != len(path)-1: + for i in range(len(path) - 1): + cur_net, next_net = path[i], path[i + 1] + cur_dir, next_dir = path_dirs[i], path_dirs[i + 1] + meas_name = "delay_{0}_to_{1}".format(cur_net, next_net) + if i + 1 != len(path) - 1: path_meas.append(delay_measure(meas_name, cur_net, next_net, cur_dir, next_dir, measure_scale=1e9, has_port=False)) else: # Make the last measurement always measure on FALL because is a read 0 path_meas.append(delay_measure(meas_name, cur_net, next_net, cur_dir, "FALL", measure_scale=1e9, has_port=False)) @@ -309,13 +309,13 @@ class delay(simulation): # Convert to booleans based on function of modules (inverting/non-inverting) mod_type_bools = [mod.is_non_inverting() for mod in edge_mods] - #FIXME: obtuse hack to differentiate s_en input from bitline in sense amps + # FIXME: obtuse hack to differentiate s_en input from bitline in sense amps if self.sen_name in path: # Force the sense amp to be inverting for s_en->DOUT. # bitline->DOUT is non-inverting, but the module cannot differentiate inputs. s_en_index = path.index(self.sen_name) mod_type_bools[s_en_index] = False - debug.info(2,'Forcing sen->dout to be inverting.') + debug.info(2, 'Forcing sen->dout to be inverting.') # Use these to determine direction list assuming delay start on neg. edge of clock (FALL) # Also, use shorthand that 'FALL' == False, 'RISE' == True to simplify logic @@ -493,7 +493,7 @@ class delay(simulation): elif meas_type is voltage_at_measure: variant_tuple = self.get_volt_at_measure_variants(port, measure_obj) else: - debug.error("Input function not defined for measurement type={}".format(meas_type)) + debug.error("Input function not defined for measurement type={0}".format(meas_type)) # Removes port input from any object which does not use it. This shorthand only works if # the measurement has port as the last input. Could be implemented by measurement type or # remove entirely from measurement classes. @@ -515,7 +515,7 @@ class delay(simulation): elif delay_obj.meta_str == sram_op.READ_ONE: meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] else: - debug.error("Unrecognized delay Index={}".format(delay_obj.meta_str),1) + debug.error("Unrecognized delay Index={0}".format(delay_obj.meta_str), 1) # These measurements have there time further delayed to the neg. edge of the clock. if delay_obj.meta_add_delay: @@ -587,20 +587,20 @@ class delay(simulation): # Output some comments to aid where cycles start and # what is happening for comment in self.cycle_comments: - self.sf.write("* {}\n".format(comment)) + self.sf.write("* {0}\n".format(comment)) self.sf.write("\n") for read_port in self.targ_read_ports: - self.sf.write("* Read ports {}\n".format(read_port)) + self.sf.write("* Read ports {0}\n".format(read_port)) self.write_delay_measures_read_port(read_port) for write_port in self.targ_write_ports: - self.sf.write("* Write ports {}\n".format(write_port)) + self.sf.write("* Write ports {0}\n".format(write_port)) self.write_delay_measures_write_port(write_port) def load_pex_net(self, net: str): from subprocess import check_output, CalledProcessError - prefix = (self.sram_instance_name + ".").lower() + prefix = (self.sram_instance_name + OPTS.hier_seperator).lower() if not net.lower().startswith(prefix) or not OPTS.use_pex or not OPTS.calibre_pex: return net original_net = net @@ -640,26 +640,41 @@ class delay(simulation): col = self.bitline_column row = self.wordline_row for port in set(self.targ_read_ports + self.targ_write_ports): - probe_nets.add("WEB{}".format(port)) - probe_nets.add("{}.w_en{}".format(self.sram_instance_name, port)) - probe_nets.add("{0}.Xbank0.Xport_data{1}.Xwrite_driver_array{1}.Xwrite_driver{2}.en_bar".format( - self.sram_instance_name, port, self.bitline_column)) - probe_nets.add("{}.Xbank0.br_{}_{}".format(self.sram_instance_name, port, - self.bitline_column)) + probe_nets.add("WEB{0}".format(port)) + probe_nets.add("{0}{2}w_en{1}".format(self.sram_instance_name, port, OPTS.hier_seperator)) + probe_nets.add("{0}{3}Xbank0{3}Xport_data{1}{3}Xwrite_driver_array{1}{3}Xwrite_driver{2}{3}en_bar".format(self.sram_instance_name, + port, + self.bitline_column, + OPTS.hier_seperator)) + probe_nets.add("{0}{3}Xbank0{3}br_{1}_{2}".format(self.sram_instance_name, + port, + self.bitline_column, + OPTS.hier_seperator)) if not OPTS.use_pex: continue probe_nets.add( - "{0}.vdd_Xbank0_Xbitcell_array_xbitcell_array_xbit_r{1}_c{2}".format(sram_name, row, col - 1)) + "{0}{3}vdd_Xbank0_Xbitcell_array_xbitcell_array_xbit_r{1}_c{2}".format(sram_name, + row, + col - 1, + OPTS.hier_seperator)) probe_nets.add( - "{0}.p_en_bar{1}_Xbank0_Xport_data{1}_Xprecharge_array{1}_Xpre_column_{2}".format(sram_name, port, col)) + "{0}{3}p_en_bar{1}_Xbank0_Xport_data{1}_Xprecharge_array{1}_Xpre_column_{2}".format(sram_name, + port, + col, + OPTS.hier_seperator)) probe_nets.add( - "{0}.vdd_Xbank0_Xport_data{1}_Xprecharge_array{1}_xpre_column_{2}".format(sram_name, port, col)) - probe_nets.add("{0}.vdd_Xbank0_Xport_data{1}_Xwrite_driver_array{1}_xwrite_driver{2}".format(sram_name, - port, col)) + "{0}{3}vdd_Xbank0_Xport_data{1}_Xprecharge_array{1}_xpre_column_{2}".format(sram_name, + port, + col, + OPTS.hier_seperator)) + probe_nets.add("{0}{3}vdd_Xbank0_Xport_data{1}_Xwrite_driver_array{1}_xwrite_driver{2}".format(sram_name, + port, + col, + OPTS.hier_seperator)) probe_nets.update(self.measurement_nets) for net in probe_nets: - debug.info(2, "Probe: {}".format(net)) - self.sf.write(".plot V({}) \n".format(self.load_pex_net(net))) + debug.info(2, "Probe: {0}".format(net)) + self.sf.write(".plot V({0}) \n".format(self.load_pex_net(net))) def write_power_measures(self): """ @@ -778,7 +793,7 @@ class delay(simulation): if not self.check_bit_measures(self.write_bit_meas, port): return(False, {}) - debug.info(2, "Checking write values for port {}".format(port)) + debug.info(2, "Checking write values for port {0}".format(port)) write_port_dict = {} for measure in self.write_lib_meas: write_port_dict[measure.name] = measure.retrieve_measure(port=port) @@ -792,7 +807,7 @@ class delay(simulation): if not self.check_bit_measures(self.read_bit_meas, port): return(False, {}) - debug.info(2, "Checking read delay values for port {}".format(port)) + debug.info(2, "Checking read delay values for port {0}".format(port)) # Check sen timing, then bitlines, then general measurements. if not self.check_sen_measure(port): return (False, {}) @@ -821,7 +836,7 @@ class delay(simulation): """Checks that the sen occurred within a half-period""" sen_val = self.sen_meas.retrieve_measure(port=port) - debug.info(2, "s_en delay={}ns".format(sen_val)) + debug.info(2, "s_en delay={0}ns".format(sen_val)) if self.sen_meas.meta_add_delay: max_delay = self.period / 2 else: @@ -843,22 +858,22 @@ class delay(simulation): elif self.br_name == meas.targ_name_no_port: br_vals[meas.meta_str] = val - debug.info(2, "{}={}".format(meas.name, val)) + debug.info(2, "{0}={1}".format(meas.name, val)) dout_success = True bl_success = False for meas in self.dout_volt_meas: val = meas.retrieve_measure(port=port) - debug.info(2, "{}={}".format(meas.name, val)) + debug.info(2, "{0}={1}".format(meas.name, val)) debug.check(type(val)==float, "Error retrieving numeric measurement: {0} {1}".format(meas.name, val)) if meas.meta_str == sram_op.READ_ONE and val < self.vdd_voltage * 0.1: dout_success = False - debug.info(1, "Debug measurement failed. Value {}V was read on read 1 cycle.".format(val)) + debug.info(1, "Debug measurement failed. Value {0}V was read on read 1 cycle.".format(val)) bl_success = self.check_bitline_meas(bl_vals[sram_op.READ_ONE], br_vals[sram_op.READ_ONE]) elif meas.meta_str == sram_op.READ_ZERO and val > self.vdd_voltage * 0.9: dout_success = False - debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val)) + debug.info(1, "Debug measurement failed. Value {0}V was read on read 0 cycle.".format(val)) bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE]) # If the bitlines have a correct value while the output does not then that is a @@ -877,7 +892,7 @@ class delay(simulation): for polarity, meas_list in bit_measures.items(): for meas in meas_list: val = meas.retrieve_measure(port=port) - debug.info(2, "{}={}".format(meas.name, val)) + debug.info(2, "{0}={1}".format(meas.name, val)) if type(val) != float: continue meas_cycle = meas.meta_str @@ -896,8 +911,8 @@ class delay(simulation): success = val < self.vdd_voltage / 2 if not success: debug.info(1, ("Wrong value detected on probe bit during read/write cycle. " - "Check writes and control logic for bugs.\n measure={}, op={}, " - "bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name, val)) + "Check writes and control logic for bugs.\n measure={0}, op={1}, " + "bit_storage={2}, V(bit)={3}").format(meas.name, meas_cycle.name, polarity.name, val)) return success @@ -912,7 +927,7 @@ class delay(simulation): min_dicharge = v_discharged_bl < self.vdd_voltage * 0.9 min_diff = (v_charged_bl - v_discharged_bl) > self.vdd_voltage * 0.1 - debug.info(1, "min_dicharge={}, min_diff={}".format(min_dicharge, min_diff)) + debug.info(1, "min_dicharge={0}, min_diff={1}".format(min_dicharge, min_diff)) return (min_dicharge and min_diff) def check_path_measures(self): @@ -921,11 +936,11 @@ class delay(simulation): # Get and set measurement, no error checking done other than prints. debug.info(2, "Checking measures in Delay Path") value_dict = {} - for meas in self.sen_path_meas+self.bl_path_meas: + for meas in self.sen_path_meas + self.bl_path_meas: val = meas.retrieve_measure() - debug.info(2, '{}={}'.format(meas.name, val)) - if type(val) != float or val > self.period/2: - debug.info(1,'Failed measurement:{}={}'.format(meas.name, val)) + debug.info(2, '{0}={1}'.format(meas.name, val)) + if type(val) != float or val > self.period / 2: + debug.info(1, 'Failed measurement:{}={}'.format(meas.name, val)) value_dict[meas.name] = val return value_dict @@ -1100,14 +1115,14 @@ class delay(simulation): # Set up to trim the netlist here if that is enabled if OPTS.trim_netlist: - self.trim_sp_file = "{}trimmed.sp".format(OPTS.openram_temp) + self.trim_sp_file = "{0}trimmed.sp".format(OPTS.openram_temp) self.sram.sp_write(self.trim_sp_file, lvs=False, trim=True) else: # The non-reduced netlist file when it is disabled - self.trim_sp_file = "{}sram.sp".format(OPTS.openram_temp) + self.trim_sp_file = "{0}sram.sp".format(OPTS.openram_temp) # The non-reduced netlist file for power simulation - self.sim_sp_file = "{}sram.sp".format(OPTS.openram_temp) + self.sim_sp_file = "{0}sram.sp".format(OPTS.openram_temp) # Make a copy in temp for debugging shutil.copy(self.sp_file, self.sim_sp_file) @@ -1182,6 +1197,7 @@ class delay(simulation): for mname, value in delay_results[port].items(): if "power" in mname: # Subtract partial array leakage and add full array leakage for the power measures + debug.info(1, "Adding leakage offset to {0} {1} + {2} = {3}".format(mname, value, leakage_offset, value + leakage_offset)) measure_data[port][mname].append(value + leakage_offset) else: measure_data[port][mname].append(value) @@ -1218,13 +1234,13 @@ class delay(simulation): if self.t_current == 0: self.add_noop_all_ports("Idle cycle (no positive clock edge)") - self.add_write("W data 1 address {}".format(inverse_address), + self.add_write("W data 1 address {0}".format(inverse_address), inverse_address, data_ones, wmask_ones, write_port) - self.add_write("W data 0 address {} to write value".format(self.probe_address), + self.add_write("W data 0 address {0} to write value".format(self.probe_address), self.probe_address, data_zeros, wmask_ones, @@ -1235,11 +1251,11 @@ class delay(simulation): self.measure_cycles[write_port]["disabled_write0"] = len(self.cycle_times) - 1 # This also ensures we will have a H->L transition on the next read - self.add_read("R data 1 address {} to set dout caps".format(inverse_address), + self.add_read("R data 1 address {0} to set dout caps".format(inverse_address), inverse_address, read_port) - self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address), + self.add_read("R data 0 address {0} to check W0 worked".format(self.probe_address), self.probe_address, read_port) self.measure_cycles[read_port][sram_op.READ_ZERO] = len(self.cycle_times) - 1 @@ -1249,7 +1265,7 @@ class delay(simulation): self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)") - self.add_write("W data 1 address {} to write value".format(self.probe_address), + self.add_write("W data 1 address {0} to write value".format(self.probe_address), self.probe_address, data_ones, wmask_ones, @@ -1259,7 +1275,7 @@ class delay(simulation): self.add_noop_clock_one_port(write_port) self.measure_cycles[write_port]["disabled_write1"] = len(self.cycle_times) - 1 - self.add_write("W data 0 address {} to clear din caps".format(inverse_address), + self.add_write("W data 0 address {0} to clear din caps".format(inverse_address), inverse_address, data_zeros, wmask_ones, @@ -1269,11 +1285,11 @@ class delay(simulation): self.measure_cycles[read_port]["disabled_read1"] = len(self.cycle_times) - 1 # This also ensures we will have a L->H transition on the next read - self.add_read("R data 0 address {} to clear dout caps".format(inverse_address), + self.add_read("R data 0 address {0} to clear dout caps".format(inverse_address), inverse_address, read_port) - self.add_read("R data 1 address {} to check W1 worked".format(self.probe_address), + self.add_read("R data 1 address {0} to check W1 worked".format(self.probe_address), self.probe_address, read_port) self.measure_cycles[read_port][sram_op.READ_ONE] = len(self.cycle_times) - 1 diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index f93de85c..db01708b 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -81,7 +81,7 @@ class functional(simulation): self.create_graph() self.set_internal_spice_names() self.q_name, self.qbar_name = self.get_bit_name() - debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name)) + debug.info(2, "q name={0}\nqbar name={1}".format(self.q_name, self.qbar_name)) # Number of checks can be changed self.num_cycles = cycles @@ -144,7 +144,7 @@ class functional(simulation): for port in self.write_ports: addr = self.gen_addr() (word, spare) = self.gen_data() - combined_word = "{}+{}".format(word, spare) + combined_word = "{0}+{1}".format(word, spare) comment = self.gen_cycle_comment("write", combined_word, addr, "1" * self.num_wmasks, port, self.t_current) self.add_write_one_port(comment, addr, word + spare, "1" * self.num_wmasks, port) self.stored_words[addr] = word @@ -167,7 +167,7 @@ class functional(simulation): self.add_noop_one_port(port) else: (addr, word, spare) = self.get_data() - combined_word = "{}+{}".format(word, spare) + combined_word = "{0}+{1}".format(word, spare) comment = self.gen_cycle_comment("read", combined_word, addr, "0" * self.num_wmasks, port, self.t_current) self.add_read_one_port(comment, addr, port) self.add_read_check(word, port) @@ -197,7 +197,7 @@ class functional(simulation): self.add_noop_one_port(port) else: (word, spare) = self.gen_data() - combined_word = "{}+{}".format(word, spare) + combined_word = "{0}+{1}".format(word, spare) comment = self.gen_cycle_comment("write", combined_word, addr, "1" * self.num_wmasks, port, self.t_current) self.add_write_one_port(comment, addr, word + spare, "1" * self.num_wmasks, port) self.stored_words[addr] = word @@ -213,7 +213,7 @@ class functional(simulation): (word, spare) = self.gen_data() wmask = self.gen_wmask() new_word = self.gen_masked_data(old_word, word, wmask) - combined_word = "{}+{}".format(word, spare) + combined_word = "{0}+{1}".format(word, spare) comment = self.gen_cycle_comment("partial_write", combined_word, addr, wmask, port, self.t_current) self.add_write_one_port(comment, addr, word + spare, wmask, port) self.stored_words[addr] = new_word @@ -222,7 +222,7 @@ class functional(simulation): else: (addr, word) = random.choice(list(self.stored_words.items())) spare = self.stored_spares[addr[:self.addr_spare_index]] - combined_word = "{}+{}".format(word, spare) + combined_word = "{0}+{1}".format(word, spare) # The write driver is not sized sufficiently to drive through the two # bitcell access transistors to the read port. So, for now, we do not allow # a simultaneous write and read to the same address on different ports. This @@ -363,7 +363,7 @@ class functional(simulation): self.stim_sp = "functional_stim.sp" temp_stim = "{0}/{1}".format(self.output_path, self.stim_sp) self.sf = open(temp_stim, "w") - self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period)) + self.sf.write("* Functional test stimulus file for {0}ns period\n\n".format(self.period)) self.stim = stimuli(self.sf, self.corner) # Write include statements @@ -387,16 +387,16 @@ class functional(simulation): # Write important signals to stim file self.sf.write("\n\n* Important signals for debug\n") - self.sf.write("* bl: {}\n".format(self.bl_name.format(port))) - self.sf.write("* br: {}\n".format(self.br_name.format(port))) - self.sf.write("* s_en: {}\n".format(self.sen_name)) - self.sf.write("* q: {}\n".format(self.q_name)) - self.sf.write("* qbar: {}\n".format(self.qbar_name)) + self.sf.write("* bl: {0}\n".format(self.bl_name.format(port))) + self.sf.write("* br: {0}\n".format(self.br_name.format(port))) + self.sf.write("* s_en: {0}\n".format(self.sen_name)) + self.sf.write("* q: {0}\n".format(self.q_name)) + self.sf.write("* qbar: {0}\n".format(self.qbar_name)) # Write debug comments to stim file self.sf.write("\n\n* Sequence of operations\n") for comment in self.fn_cycle_comments: - self.sf.write("*{}\n".format(comment)) + self.sf.write("*{0}\n".format(comment)) # Generate data input bits self.sf.write("\n* Generation of data and address signals\n") @@ -414,10 +414,10 @@ class functional(simulation): # Generate control signals self.sf.write("\n * Generation of control signals\n") for port in self.all_ports: - self.stim.gen_pwl("CSB{}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) + self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) for port in self.readwrite_ports: - self.stim.gen_pwl("WEB{}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) + self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) # Generate wmask bits for port in self.write_ports: @@ -472,15 +472,15 @@ class functional(simulation): self.stim.write_control(self.cycle_times[-1] + self.period) self.sf.close() - #FIXME: Similar function to delay.py, refactor this + # FIXME: Similar function to delay.py, refactor this def get_bit_name(self): """ Get a bit cell name """ (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0) 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)) - q_name = cell_name + '.' + str(storage_names[0]) - qbar_name = cell_name + '.' + str(storage_names[1]) + "supported for characterization. Storage nets={0}").format(storage_names)) + q_name = cell_name + OPTS.hier_seperator + str(storage_names[0]) + qbar_name = cell_name + OPTS.hier_seperator + str(storage_names[1]) return (q_name, qbar_name) diff --git a/compiler/characterizer/measurements.py b/compiler/characterizer/measurements.py index b1896880..448dee36 100644 --- a/compiler/characterizer/measurements.py +++ b/compiler/characterizer/measurements.py @@ -53,11 +53,20 @@ class spice_measurement(ABC): elif not self.has_port and port != None: debug.error("Unexpected port input received during measure retrieval.",1) + class delay_measure(spice_measurement): """Generates a spice measurement for the delay of 50%-to-50% points of two signals.""" - def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str,\ - trig_vdd=0.5, targ_vdd=0.5, measure_scale=None, has_port=True): + def __init__(self, + measure_name, + trig_name, + targ_name, + trig_dir_str, + targ_dir_str, + trig_vdd=0.5, + targ_vdd=0.5, + measure_scale=None, + has_port=True): spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd) @@ -73,7 +82,7 @@ class delay_measure(spice_measurement): self.trig_name_no_port = trig_name self.targ_name_no_port = targ_name - #Time delays and ports are variant and needed as inputs when writing the measurement + # Time delays and ports are variant and needed as inputs when writing the measurement def get_measure_values(self, trig_td, targ_td, vdd_voltage, port=None): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" @@ -82,7 +91,7 @@ class delay_measure(spice_measurement): targ_val = self.targ_val_of_vdd * vdd_voltage if port != None: - #For dictionary indexing reasons, the name is formatted differently than the signals + # For dictionary indexing reasons, the name is formatted differently than the signals meas_name = "{}{}".format(self.name, port) trig_name = self.trig_name_no_port.format(port) targ_name = self.targ_name_no_port.format(port) @@ -90,7 +99,8 @@ class delay_measure(spice_measurement): meas_name = self.name trig_name = self.trig_name_no_port targ_name = self.targ_name_no_port - return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td) + return (meas_name, trig_name, targ_name, trig_val, targ_val, self.trig_dir_str, self.targ_dir_str, trig_td, targ_td) + class slew_measure(delay_measure): @@ -114,7 +124,8 @@ class slew_measure(delay_measure): self.trig_name_no_port = signal_name self.targ_name_no_port = signal_name - #Time delays and ports are variant and needed as inputs when writing the measurement + # Time delays and ports are variant and needed as inputs when writing the measurement + class power_measure(spice_measurement): """Generates a spice measurement for the average power between two time points.""" @@ -128,8 +139,8 @@ class power_measure(spice_measurement): def set_meas_constants(self, power_type): """Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)""" - #Not needed for power simulation - self.power_type = power_type #Expected to be "RISE"/"FALL" + # Not needed for power simulation + self.power_type = power_type # Expected to be "RISE"/"FALL" def get_measure_values(self, t_initial, t_final, port=None): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" @@ -138,7 +149,8 @@ class power_measure(spice_measurement): meas_name = "{}{}".format(self.name, port) else: meas_name = self.name - return (meas_name,t_initial,t_final) + return (meas_name, t_initial, t_final) + class voltage_when_measure(spice_measurement): """Generates a spice measurement to measure the voltage of a signal based on the voltage of another.""" @@ -161,7 +173,7 @@ class voltage_when_measure(spice_measurement): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" self.port_error_check(port) if port != None: - #For dictionary indexing reasons, the name is formatted differently than the signals + # For dictionary indexing reasons, the name is formatted differently than the signals meas_name = "{}{}".format(self.name, port) trig_name = self.trig_name_no_port.format(port) targ_name = self.targ_name_no_port.format(port) @@ -169,9 +181,10 @@ class voltage_when_measure(spice_measurement): meas_name = self.name trig_name = self.trig_name_no_port targ_name = self.targ_name_no_port - trig_voltage = self.trig_val_of_vdd*vdd_voltage - return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td) + trig_voltage = self.trig_val_of_vdd * vdd_voltage + return (meas_name, trig_name, targ_name, trig_voltage, self.trig_dir_str, trig_td) + class voltage_at_measure(spice_measurement): """Generates a spice measurement to measure the voltage at a specific time. The time is considered variant with different periods.""" @@ -191,11 +204,11 @@ class voltage_at_measure(spice_measurement): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" self.port_error_check(port) if port != None: - #For dictionary indexing reasons, the name is formatted differently than the signals + # For dictionary indexing reasons, the name is formatted differently than the signals meas_name = "{}{}".format(self.name, port) targ_name = self.targ_name_no_port.format(port) else: meas_name = self.name targ_name = self.targ_name_no_port - return (meas_name,targ_name,time_at) + return (meas_name, targ_name, time_at) diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index f72c3211..fcbc51c2 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -5,18 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import sys,re,shutil import debug import tech -import math from .stimuli import * from .trim_spice import * from .charutils import * -import utils from globals import OPTS from .delay import delay from .measurements import * + class model_check(delay): """Functions to test for the worst case delay in a target SRAM @@ -39,43 +37,44 @@ class model_check(delay): self.power_name = "total_power" def create_measurement_names(self, port): - """Create measurement names. The names themselves currently define the type of measurement""" - #Create delay measurement names - wl_en_driver_delay_names = ["delay_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] - wl_driver_delay_names = ["delay_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] - sen_driver_delay_names = ["delay_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] + """ + Create measurement names. The names themselves currently define the type of measurement + """ + wl_en_driver_delay_names = ["delay_wl_en_dvr_{0}".format(stage) for stage in range(1, self.get_num_wl_en_driver_stages())] + wl_driver_delay_names = ["delay_wl_dvr_{0}".format(stage) for stage in range(1, self.get_num_wl_driver_stages())] + sen_driver_delay_names = ["delay_sen_dvr_{0}".format(stage) for stage in range(1, self.get_num_sen_driver_stages())] if self.custom_delaychain: - dc_delay_names = ['delay_dc_out_final'] + dc_delay_names = ["delay_dc_out_final"] else: - dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] - self.wl_delay_meas_names = wl_en_driver_delay_names+["delay_wl_en", "delay_wl_bar"]+wl_driver_delay_names+["delay_wl"] + dc_delay_names = ["delay_delay_chain_stage_{0}".format(stage) for stage in range(1, self.get_num_delay_stages() + 1)] + self.wl_delay_meas_names = wl_en_driver_delay_names + ["delay_wl_en", "delay_wl_bar"] + wl_driver_delay_names + ["delay_wl"] if port not in self.sram.readonly_ports: - self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names + self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"] + dc_delay_names else: - self.rbl_delay_meas_names = ["delay_gated_clk_nand"]+dc_delay_names - self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"] + self.rbl_delay_meas_names = ["delay_gated_clk_nand"] + dc_delay_names + self.sae_delay_meas_names = ["delay_pre_sen"] + sen_driver_delay_names + ["delay_sen"] # if self.custom_delaychain: - # self.delay_chain_indices = (len(self.rbl_delay_meas_names), len(self.rbl_delay_meas_names)+1) + # self.delay_chain_indices = (len(self.rbl_delay_meas_names), len(self.rbl_delay_meas_names)+1) # else: - self.delay_chain_indices = (len(self.rbl_delay_meas_names)-len(dc_delay_names), len(self.rbl_delay_meas_names)) - #Create slew measurement names - wl_en_driver_slew_names = ["slew_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] - wl_driver_slew_names = ["slew_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] - sen_driver_slew_names = ["slew_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] + self.delay_chain_indices = (len(self.rbl_delay_meas_names) - len(dc_delay_names), len(self.rbl_delay_meas_names)) + # Create slew measurement names + wl_en_driver_slew_names = ["slew_wl_en_dvr_{0}".format(stage) for stage in range(1, self.get_num_wl_en_driver_stages())] + wl_driver_slew_names = ["slew_wl_dvr_{0}".format(stage) for stage in range(1, self.get_num_wl_driver_stages())] + sen_driver_slew_names = ["slew_sen_dvr_{0}".format(stage) for stage in range(1, self.get_num_sen_driver_stages())] if self.custom_delaychain: - dc_slew_names = ['slew_dc_out_final'] + dc_slew_names = ["slew_dc_out_final"] else: - dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] - self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"] + dc_slew_names = ["slew_delay_chain_stage_{0}".format(stage) for stage in range(1, self.get_num_delay_stages() + 1)] + self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"] + wl_en_driver_slew_names + ["slew_wl_en", "slew_wl_bar"] + wl_driver_slew_names + ["slew_wl"] if port not in self.sram.readonly_ports: - self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names + self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar", "slew_gated_clk_nand", "slew_delay_chain_in"] + dc_slew_names else: - self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar"]+dc_slew_names - self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen"]+sen_driver_slew_names+["slew_sen"] + self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar"] + dc_slew_names + self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen"] + sen_driver_slew_names + ["slew_sen"] self.bitline_meas_names = ["delay_wl_to_bl", "delay_bl_to_dout"] - self.power_meas_names = ['read0_power'] + self.power_meas_names = ["read0_power"] def create_signal_names(self, port): """Creates list of the signal names used in the spice file along the wl and sen paths. @@ -83,40 +82,45 @@ class model_check(delay): replicated here. """ delay.create_signal_names(self) - #Signal names are all hardcoded, need to update to make it work for probe address and different configurations. - wl_en_driver_signals = ["Xsram.Xcontrol{}.Xbuf_wl_en.Zb{}_int".format('{}', stage) for stage in range(1,self.get_num_wl_en_driver_stages())] - wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver{}.Xwl_driver_inv{}.Zb{}_int".format('{}', self.wordline_row, stage) for stage in range(1,self.get_num_wl_driver_stages())] - sen_driver_signals = ["Xsram.Xcontrol{}.Xbuf_s_en.Zb{}_int".format('{}',stage) for stage in range(1,self.get_num_sen_driver_stages())] + + # Signal names are all hardcoded, need to update to make it work for probe address and different configurations. + wl_en_driver_signals = ["Xsram{1}Xcontrol{{}}.Xbuf_wl_en.Zb{0}_int".format(stage, OPTS.hier_seperator) for stage in range(1, self.get_num_wl_en_driver_stages())] + wl_driver_signals = ["Xsram{2}Xbank0{2}Xwordline_driver{{}}{2}Xwl_driver_inv{0}{2}Zb{1}_int".format(self.wordline_row, stage, OPTS.hier_seperator) for stage in range(1, self.get_num_wl_driver_stages())] + sen_driver_signals = ["Xsram{1}Xcontrol{{}}{1}Xbuf_s_en{1}Zb{0}_int".format(stage, OPTS.hier_seperator) for stage in range(1, self.get_num_sen_driver_stages())] if self.custom_delaychain: delay_chain_signal_names = [] else: - delay_chain_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.Xdelay_chain.dout_{}".format('{}', stage) for stage in range(1,self.get_num_delay_stages())] + delay_chain_signal_names = ["Xsram{1}Xcontrol{{}}{1}Xreplica_bitline{1}Xdelay_chain{1}dout_{0}".format(stage, OPTS.hier_seperator) for stage in range(1, self.get_num_delay_stages())] if len(self.sram.all_ports) > 1: port_format = '{}' else: port_format = '' - self.wl_signal_names = ["Xsram.Xcontrol{}.gated_clk_bar".format('{}')]+\ - wl_en_driver_signals+\ - ["Xsram.wl_en{}".format('{}'), "Xsram.Xbank0.Xwordline_driver{}.wl_bar_{}".format('{}',self.wordline_row)]+\ - wl_driver_signals+\ - ["Xsram.Xbank0.wl{}_{}".format(port_format, self.wordline_row)] - pre_delay_chain_names = ["Xsram.Xcontrol{}.gated_clk_bar".format('{}')] + self.wl_signal_names = ["Xsram{0}Xcontrol{{}}{0}gated_clk_bar".format(OPTS.hier_seperator)] + \ + wl_en_driver_signals + \ + ["Xsram{0}wl_en{{}}".format(OPTS.hier_seperator), + "Xsram{1}Xbank0{1}Xwordline_driver{{}}{1}wl_bar_{0}".format(self.wordline_row, + OPTS.hier_seperator)] + \ + wl_driver_signals + \ + ["Xsram{2}Xbank0{2}wl{0}_{1}".format(port_format, + self.wordline_row, + OPTS.hier_seperator)] + pre_delay_chain_names = ["Xsram.Xcontrol{{}}{0}gated_clk_bar".format(OPTS.hier_seperator)] if port not in self.sram.readonly_ports: - pre_delay_chain_names+= ["Xsram.Xcontrol{}.Xand2_rbl_in.zb_int".format('{}'), "Xsram.Xcontrol{}.rbl_in".format('{}')] + pre_delay_chain_names+= ["Xsram{0}Xcontrol{{}}{0}Xand2_rbl_in{0}zb_int".format(OPTS.hier_seperator), + "Xsram{0}Xcontrol{{}}{0}rbl_in".format(OPTS.hier_seperator)] - self.rbl_en_signal_names = pre_delay_chain_names+\ - delay_chain_signal_names+\ - ["Xsram.Xcontrol{}.Xreplica_bitline.delayed_en".format('{}')] + self.rbl_en_signal_names = pre_delay_chain_names + \ + delay_chain_signal_names + \ + ["Xsram{0}Xcontrol{{}}{0}Xreplica_bitline{0}delayed_en".format(OPTS.hier_seperator)] + self.sae_signal_names = ["Xsram{0}Xcontrol{{}}{0}Xreplica_bitline{0}bl0_0".format(OPTS.hier_seperator), + "Xsram{0}Xcontrol{{}}{0}pre_s_en".format(OPTS.hier_seperator)] + \ + sen_driver_signals + \ + ["Xsram{0}s_en{{}}".format(OPTS.hier_seperator)] - self.sae_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.bl0_0".format('{}'), "Xsram.Xcontrol{}.pre_s_en".format('{}')]+\ - sen_driver_signals+\ - ["Xsram.s_en{}".format('{}')] - - dout_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit - self.bl_signal_names = ["Xsram.Xbank0.wl{}_{}".format(port_format, self.wordline_row),\ - "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column),\ - dout_name] + self.bl_signal_names = ["Xsram{2}Xbank0{2}wl{0}_{1}".format(port_format, self.wordline_row, OPTS.hier_seperator), + "Xsram{2}Xbank0{2}bl{0}_{1}".format(port_format, self.bitline_column, OPTS.hier_seperator), + "{0}{{}}_{1}".format(self.dout_name, self.probe_data)] # Empty values are the port and probe data bit def create_measurement_objects(self): """Create the measurements used for read and write ports""" @@ -124,7 +128,7 @@ class model_check(delay): self.create_sae_meas_objs() self.create_bl_meas_objs() self.create_power_meas_objs() - self.all_measures = self.wl_meas_objs+self.sae_meas_objs+self.bl_meas_objs+self.power_meas_objs + self.all_measures = self.wl_meas_objs + self.sae_meas_objs + self.bl_meas_objs + self.power_meas_objs def create_power_meas_objs(self): """Create power measurement object. Only one.""" @@ -138,14 +142,14 @@ class model_check(delay): targ_dir = "FALL" for i in range(1, len(self.wl_signal_names)): - self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1], - self.wl_signal_names[i-1], + self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i - 1], + self.wl_signal_names[i - 1], self.wl_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) - self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1], - self.wl_signal_names[i-1], + self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i - 1], + self.wl_signal_names[i - 1], trig_dir, measure_scale=1e9)) temp_dir = trig_dir @@ -155,9 +159,9 @@ class model_check(delay): def create_bl_meas_objs(self): """Create the measurements to measure the bitline to dout, static stages""" - #Bitline has slightly different measurements, objects appends hardcoded. + # Bitline has slightly different measurements, objects appends hardcoded. self.bl_meas_objs = [] - trig_dir, targ_dir = "RISE", "FALL" #Only check read 0 + trig_dir, targ_dir = "RISE", "FALL" # Only check read 0 self.bl_meas_objs.append(delay_measure(self.bitline_meas_names[0], self.bl_signal_names[0], self.bl_signal_names[-1], @@ -171,22 +175,22 @@ class model_check(delay): self.sae_meas_objs = [] trig_dir = "RISE" targ_dir = "FALL" - #Add measurements from gated_clk_bar to RBL + # Add measurements from gated_clk_bar to RBL for i in range(1, len(self.rbl_en_signal_names)): - self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1], - self.rbl_en_signal_names[i-1], + self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i - 1], + self.rbl_en_signal_names[i - 1], self.rbl_en_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) - self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1], - self.rbl_en_signal_names[i-1], + self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i - 1], + self.rbl_en_signal_names[i - 1], trig_dir, measure_scale=1e9)) temp_dir = trig_dir trig_dir = targ_dir targ_dir = temp_dir - if self.custom_delaychain: #Hack for custom delay chains + if self.custom_delaychain: # Hack for custom delay chains self.sae_meas_objs[-2] = delay_measure(self.rbl_delay_meas_names[-1], self.rbl_en_signal_names[-2], self.rbl_en_signal_names[-1], @@ -198,18 +202,18 @@ class model_check(delay): trig_dir, measure_scale=1e9)) - #Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL. + # Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL. trig_dir = "FALL" targ_dir = "RISE" for i in range(1, len(self.sae_signal_names)): - self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1], - self.sae_signal_names[i-1], + self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i - 1], + self.sae_signal_names[i - 1], self.sae_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) - self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1], - self.sae_signal_names[i-1], + self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i - 1], + self.sae_signal_names[i - 1], trig_dir, measure_scale=1e9)) temp_dir = trig_dir @@ -231,16 +235,16 @@ class model_check(delay): self.sf.write("* {}\n".format(comment)) for read_port in self.targ_read_ports: - self.write_measures_read_port(read_port) + self.write_measures_read_port(read_port) def get_delay_measure_variants(self, port, measure_obj): """Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)""" - #Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port - #Assuming only read 0 for now - debug.info(3,"Power measurement={}".format(measure_obj)) + # Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port + # Assuming only read 0 for now + debug.info(3, "Power measurement={}".format(measure_obj)) if (type(measure_obj) is delay_measure or type(measure_obj) is slew_measure): - meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2 + meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period / 2 return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) elif type(measure_obj) is power_measure: return self.get_power_measure_variants(port, measure_obj, "read") @@ -249,9 +253,9 @@ class model_check(delay): def get_power_measure_variants(self, port, power_obj, operation): """Get the measurement values that can either vary port to port (time delays)""" - #Return value is intended to match the power measure format: t_initial, t_final, port + # Return value is intended to match the power measure format: t_initial, t_final, port t_initial = self.cycle_times[self.measure_cycles[port]["read0"]] - t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1] + t_final = self.cycle_times[self.measure_cycles[port]["read0"] + 1] return (t_initial, t_final, port) @@ -280,8 +284,8 @@ class model_check(delay): elif type(measure)is power_measure: power_meas_list.append(measure_value) else: - debug.error("Measurement object not recognized.",1) - return delay_meas_list, slew_meas_list,power_meas_list + debug.error("Measurement object not recognized.", 1) + return delay_meas_list, slew_meas_list, power_meas_list def run_delay_simulation(self): """ @@ -290,7 +294,7 @@ class model_check(delay): works on the trimmed netlist by default, so powers do not include leakage of all cells. """ - #Sanity Check + # Sanity Check debug.check(self.period > 0, "Target simulation period non-positive") wl_delay_result = [[] for i in self.all_ports] @@ -303,16 +307,16 @@ class model_check(delay): # Checking from not data_value to data_value self.write_delay_stimulus() - self.stim.run_sim() #running sim prodoces spice output file. + self.stim.run_sim() # running sim prodoces spice output file. - #Retrieve the results from the output file + # Retrieve the results from the output file for port in self.targ_read_ports: - #Parse and check the voltage measurements - wl_delay_result[port], wl_slew_result[port],_ = self.get_measurement_values(self.wl_meas_objs, port) - sae_delay_result[port], sae_slew_result[port],_ = self.get_measurement_values(self.sae_meas_objs, port) - bl_delay_result[port], bl_slew_result[port],_ = self.get_measurement_values(self.bl_meas_objs, port) - _,__,power_result[port] = self.get_measurement_values(self.power_meas_objs, port) - return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result, bl_delay_result, bl_slew_result, power_result) + # Parse and check the voltage measurements + wl_delay_result[port], wl_slew_result[port], _ = self.get_measurement_values(self.wl_meas_objs, port) + sae_delay_result[port], sae_slew_result[port], _ = self.get_measurement_values(self.sae_meas_objs, port) + bl_delay_result[port], bl_slew_result[port], _ = self.get_measurement_values(self.bl_meas_objs, port) + _, __, power_result[port] = self.get_measurement_values(self.power_meas_objs, port) + return (True, wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result, bl_delay_result, bl_slew_result, power_result) def get_model_delays(self, port): """Get model delays based on port. Currently assumes single RW port.""" @@ -345,41 +349,41 @@ class model_check(delay): def scale_delays(self, delay_list): """Takes in a list of measured delays and convert it to simple units to easily compare to model values.""" converted_values = [] - #Calculate average + # Calculate average total = 0 for meas_value in delay_list: total+=meas_value - average = total/len(delay_list) + average = total / len(delay_list) - #Convert values + # Convert values for meas_value in delay_list: - converted_values.append(meas_value/average) + converted_values.append(meas_value / average) return converted_values def min_max_normalization(self, value_list): """Re-scales input values on a range from 0-1 where min(list)=0, max(list)=1""" scaled_values = [] min_max_diff = max(value_list) - min(value_list) - average = sum(value_list)/len(value_list) + average = sum(value_list) / len(value_list) for value in value_list: - scaled_values.append((value-average)/(min_max_diff)) + scaled_values.append((value - average) / (min_max_diff)) return scaled_values def calculate_error_l2_norm(self, list_a, list_b): """Calculates error between two lists using the l2 norm""" error_list = [] for val_a, val_b in zip(list_a, list_b): - error_list.append((val_a-val_b)**2) + error_list.append((val_a - val_b)**2) return error_list def compare_measured_and_model(self, measured_vals, model_vals): """First scales both inputs into similar ranges and then compares the error between both.""" scaled_meas = self.min_max_normalization(measured_vals) - debug.info(1, "Scaled measurements:\n{}".format(scaled_meas)) + debug.info(1, "Scaled measurements:\n{0}".format(scaled_meas)) scaled_model = self.min_max_normalization(model_vals) - debug.info(1, "Scaled model:\n{}".format(scaled_model)) + debug.info(1, "Scaled model:\n{0}".format(scaled_model)) errors = self.calculate_error_l2_norm(scaled_meas, scaled_model) - debug.info(1, "Errors:\n{}\n".format(errors)) + debug.info(1, "Errors:\n{0}\n".format(errors)) def analyze(self, probe_address, probe_data, slews, loads, port): """Measures entire delay path along the wordline and sense amp enable and compare it to the model delays.""" @@ -391,19 +395,19 @@ class model_check(delay): self.create_measurement_objects() data_dict = {} - read_port = self.read_ports[0] #only test the first read port + read_port = self.read_ports[0] # only test the first read port read_port = port self.targ_read_ports = [read_port] self.targ_write_ports = [self.write_ports[0]] - debug.info(1,"Model test: corner {}".format(self.corner)) + debug.info(1, "Model test: corner {0}".format(self.corner)) (success, wl_delays, sae_delays, wl_slews, sae_slews, bl_delays, bl_slews, powers)=self.run_delay_simulation() - debug.check(success, "Model measurements Failed: period={}".format(self.period)) + debug.check(success, "Model measurements Failed: period={0}".format(self.period)) - debug.info(1,"Measured Wordline delays (ns):\n\t {}".format(wl_delays[read_port])) - debug.info(1,"Measured Wordline slews:\n\t {}".format(wl_slews[read_port])) - debug.info(1,"Measured SAE delays (ns):\n\t {}".format(sae_delays[read_port])) - debug.info(1,"Measured SAE slews:\n\t {}".format(sae_slews[read_port])) - debug.info(1,"Measured Bitline delays (ns):\n\t {}".format(bl_delays[read_port])) + debug.info(1, "Measured Wordline delays (ns):\n\t {0}".format(wl_delays[read_port])) + debug.info(1, "Measured Wordline slews:\n\t {0}".format(wl_slews[read_port])) + debug.info(1, "Measured SAE delays (ns):\n\t {0}".format(sae_delays[read_port])) + debug.info(1, "Measured SAE slews:\n\t {0}".format(sae_slews[read_port])) + debug.info(1, "Measured Bitline delays (ns):\n\t {0}".format(bl_delays[read_port])) data_dict[self.wl_meas_name] = wl_delays[read_port] data_dict[self.sae_meas_name] = sae_delays[read_port] @@ -412,14 +416,14 @@ class model_check(delay): data_dict[self.bl_meas_name] = bl_delays[read_port] data_dict[self.power_name] = powers[read_port] - if OPTS.auto_delay_chain_sizing: #Model is not used in this case + if OPTS.auto_delay_chain_sizing: # Model is not used in this case wl_model_delays, sae_model_delays = self.get_model_delays(read_port) - debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays)) - debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays)) + debug.info(1, "Wordline model delays:\n\t {0}".format(wl_model_delays)) + debug.info(1, "SAE model delays:\n\t {0}".format(sae_model_delays)) data_dict[self.wl_model_name] = wl_model_delays data_dict[self.sae_model_name] = sae_model_delays - #Some evaluations of the model and measured values + # Some evaluations of the model and measured values # debug.info(1, "Comparing wordline measurements and model.") # self.compare_measured_and_model(wl_delays[read_port], wl_model_delays) # debug.info(1, "Comparing SAE measurements and model") @@ -430,17 +434,17 @@ class model_check(delay): def get_all_signal_names(self): """Returns all signals names as a dict indexed by hardcoded names. Useful for writing the head of the CSV.""" name_dict = {} - #Signal names are more descriptive than the measurement names, first value trimmed to match size of measurements names. + # Signal names are more descriptive than the measurement names, first value trimmed to match size of measurements names. name_dict[self.wl_meas_name] = self.wl_signal_names[1:] - name_dict[self.sae_meas_name] = self.rbl_en_signal_names[1:]+self.sae_signal_names[1:] + name_dict[self.sae_meas_name] = self.rbl_en_signal_names[1:] + self.sae_signal_names[1:] name_dict[self.wl_slew_name] = self.wl_slew_meas_names - name_dict[self.sae_slew_name] = self.rbl_slew_meas_names+self.sae_slew_meas_names + name_dict[self.sae_slew_name] = self.rbl_slew_meas_names + self.sae_slew_meas_names name_dict[self.bl_meas_name] = self.bitline_meas_names[0:1] name_dict[self.power_name] = self.power_meas_names - #name_dict[self.wl_slew_name] = self.wl_slew_meas_names + # pname_dict[self.wl_slew_name] = self.wl_slew_meas_names if OPTS.auto_delay_chain_sizing: - name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured. + name_dict[self.wl_model_name] = name_dict["wl_measures"] # model uses same names as measured. name_dict[self.sae_model_name] = name_dict["sae_measures"] return name_dict diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 5becbacf..e985e951 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -586,7 +586,7 @@ class simulation(): bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set)) 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] + bl_names[i] = bl_names[i].split(OPTS.hier_seperator)[-1] return bl_names[0], bl_names[1] def get_empty_measure_data_dict(self): diff --git a/compiler/globals.py b/compiler/globals.py index 78ba997b..d64c727f 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -66,7 +66,7 @@ def parse_args(): optparse.make_option("-m", "--sim_threads", action="store", type="int", - help="Specify the number of spice simulation threads (default: 2)", + help="Specify the number of spice simulation threads (default: 3)", dest="num_sim_threads"), optparse.make_option("-v", "--verbose", diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index a76d0d80..a10a4924 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -1075,7 +1075,7 @@ class bank(design.design): """ Gets the spice name of the target bitcell. """ - return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, + return self.bitcell_array_inst.mod.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.bitcell_array_inst.name, row, col) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 9d1cc0de..238d499f 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -121,4 +121,4 @@ class bitcell_array(bitcell_base_array): def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col] + return inst_name + "{}x".format(OPTS.hier_seperator) + self.cell_inst[row, col].name, self.cell_inst[row, col] diff --git a/compiler/modules/global_bitcell_array.py b/compiler/modules/global_bitcell_array.py index 8eb527d2..dbd56e35 100644 --- a/compiler/modules/global_bitcell_array.py +++ b/compiler/modules/global_bitcell_array.py @@ -330,7 +330,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): # We must also translate the global array column number to the local array column number local_col = col - self.col_offsets[i - 1] - return local_array.get_cell_name(inst_name + '.x' + local_inst.name, row, local_col) + return local_array.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + local_inst.name, row, local_col) def clear_exclude_bits(self): """ diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index cbea3ce9..e48cff5c 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -295,7 +295,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col) + return self.bitcell_array.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.bitcell_array_inst.name, row, col) def clear_exclude_bits(self): """ diff --git a/compiler/modules/orig_bitcell_array.py b/compiler/modules/orig_bitcell_array.py index 8bf498a4..42ebdc33 100644 --- a/compiler/modules/orig_bitcell_array.py +++ b/compiler/modules/orig_bitcell_array.py @@ -114,4 +114,4 @@ class bitcell_array(bitcell_base_array): def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col] + return inst_name + "{}x".format(OPTS.hier_seperator) + self.cell_inst[row, col].name, self.cell_inst[row, col] diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 828941ae..0173f1a0 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -553,7 +553,7 @@ class replica_bitcell_array(bitcell_base_array): """ Gets the spice name of the target bitcell. """ - return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col) + return self.bitcell_array.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.bitcell_array_inst.name, row, col) def clear_exclude_bits(self): """ diff --git a/compiler/options.py b/compiler/options.py index c1a3043b..1aacdf9c 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -6,9 +6,9 @@ # All rights reserved. # import optparse -import getpass import os + class options(optparse.Values): """ Class for holding all of the OpenRAM options. All @@ -137,6 +137,9 @@ class options(optparse.Values): # Number of threads to use in ngspice/hspice num_sim_threads = 3 + # Some tools (e.g. Xyce) use other separators like ":" + hier_seperator = "." + # Should we print out the banner at startup print_banner = True diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index f9ea1d43..a6eb9b71 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -613,7 +613,7 @@ class sram_1bank(sram_base): # Sanity check in case it was forgotten if inst_name.find("x") != 0: inst_name = "x" + inst_name - return self.bank_inst.mod.get_cell_name(inst_name + ".x" + self.bank_inst.name, row, col) + return self.bank_inst.mod.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.bank_inst.name, row, col) def get_bank_num(self, inst_name, row, col): return 0