diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 171210ad..0ba8e858 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -12,6 +12,8 @@ import debug from vector import vector import tech import math +import copy +import numpy as np from globals import OPTS from utils import round_to_grid @@ -269,6 +271,134 @@ class instance(geometry): p.transform(self.offset, self.mirror, self.rotate) new_pins.append(p) return new_pins + + def calculate_transform(self, node): + #set up the rotation matrix + angle = math.radians(float(node.rotate)) + mRotate = np.array([[math.cos(angle),-math.sin(angle),0.0], + [math.sin(angle),math.cos(angle),0.0], + [0.0,0.0,1.0]]) + + #set up translation matrix + translateX = float(node.offset[0]) + translateY = float(node.offset[1]) + mTranslate = np.array([[1.0,0.0,translateX], + [0.0,1.0,translateY], + [0.0,0.0,1.0]]) + + #set up the scale matrix (handles mirror X) + scaleX = 1.0 + if(node.mirror == 'MX'): + scaleY = -1.0 + else: + scaleY = 1.0 + mScale = np.array([[scaleX,0.0,0.0], + [0.0,scaleY,0.0], + [0.0,0.0,1.0]]) + + return (mRotate, mScale, mTranslate) + + def apply_transform(self, mtransforms, uVector, vVector, origin): + origin = np.dot(mtransforms[0], origin) #rotate + uVector = np.dot(mtransforms[0], uVector) #rotate + vVector = np.dot(mtransforms[0], vVector) #rotate + origin = np.dot(mtransforms[1], origin) #scale + uVector = np.dot(mtransforms[1], uVector) #scale + vVector = np.dot(mtransforms[1], vVector) #scale + origin = np.dot(mtransforms[2], origin) + + return(uVector, vVector, origin) + + def apply_path_transform(self, path): + uVector = np.array([[1.0],[0.0],[0.0]]) + vVector = np.array([[0.0],[1.0],[0.0]]) + origin = np.array([[0.0],[0.0],[1.0]]) + + while(path): + instance = path.pop(-1) + mtransforms = self.calculate_transform(instance) + (uVector, vVector, origin) = self.apply_transform(mtransforms, uVector, vVector, origin) + + return (uVector, vVector, origin) + + def reverse_transformation_bitcell(self, cell_name): + path = [] # path currently follwed in bitcell search + cell_paths = [] # saved paths to bitcells + origin_offsets = [] # cell to bank offset + Q_offsets = [] # Q to cell offet + Q_bar_offsets = [] # Q_bar to cell offset + bl_offsets = [] # bl to cell offset + br_offsets = [] # br to cell offset + bl_meta = [] # bl offset metadata (row,col,name) + br_meta = [] #br offset metadata (row,col,name) + + def walk_subtree(node): + path.append(node) + + if node.mod.name == cell_name: + cell_paths.append(copy.copy(path)) + + inst_name = path[-1].name + + # get the row and col names from the path + row = int(path[-1].name.split('_')[-2][1:]) + col = int(path[-1].name.split('_')[-1][1:]) + + cell_bl_meta = [] + cell_br_meta = [] + + normalized_storage_nets = node.mod.get_normalized_storage_nets_offset() + (normalized_bl_offsets, normalized_br_offsets, bl_names, br_names) = node.mod.get_normalized_bitline_offset() + + for offset in range(len(normalized_bl_offsets)): + for port in range(len(bl_names)): + cell_bl_meta.append([bl_names[offset], row, col, port]) + + for offset in range(len(normalized_br_offsets)): + for port in range(len(br_names)): + cell_br_meta.append([br_names[offset], row, col, port]) + + Q_x = normalized_storage_nets[0][0] + Q_y = normalized_storage_nets[0][1] + + Q_bar_x = normalized_storage_nets[1][0] + Q_bar_y = normalized_storage_nets[1][1] + + if node.mirror == 'MX': + Q_y = -1 * Q_y + Q_bar_y = -1 * Q_bar_y + + for pair in range(len(normalized_bl_offsets)): + normalized_bl_offsets[pair] = (normalized_bl_offsets[pair][0], + -1 * normalized_bl_offsets[pair][1]) + + for pair in range(len(normalized_br_offsets)): + normalized_br_offsets[pair] = (normalized_br_offsets[pair][0], + -1 * normalized_br_offsets[pair][1]) + + + Q_offsets.append([Q_x, Q_y]) + Q_bar_offsets.append([Q_bar_x, Q_bar_y]) + + + bl_offsets.append(normalized_bl_offsets) + br_offsets.append(normalized_br_offsets) + + bl_meta.append(cell_bl_meta) + br_meta.append(cell_br_meta) + + elif node.mod.insts is not []: + for instance in node.mod.insts: + walk_subtree(instance) + path.pop(-1) + + walk_subtree(self) + for path in cell_paths: + vector_spaces = self.apply_path_transform(path) + origin = vector_spaces[2] + origin_offsets.append([origin[0], origin[1]]) + + return(origin_offsets, Q_offsets, Q_bar_offsets, bl_offsets, br_offsets, bl_meta, br_meta) def __str__(self): """ override print function output """ diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 79b5a53a..16d2bb4e 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -177,62 +177,6 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): name_dict[si_port.lower()] = mod_info subinst.mod.build_names(name_dict, subinst_name, subinst_ports) - def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None): - """Given a list of nets, will compare the internal alias of a mod to determine - if the nets have a connection to this mod's net (but not inst). - """ - if not exclusion_set: - exclusion_set = set() - try: - self.name_dict - except AttributeError: - self.name_dict = {} - self.build_names(self.name_dict, inst_name, port_nets) - aliases = [] - for net in path_nets: - net = net.lower() - int_net = self.name_dict[net]['int_net'] - int_mod = self.name_dict[net]['mod'] - if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set): - aliases.append(net) - return aliases - - def is_net_alias(self, known_net, net_alias, mod, exclusion_set): - """Checks if the alias_net in input mod is the same as the input net for this mod (self).""" - if self in exclusion_set: - return False - # Check ports of this mod - for pin in self.pins: - if self.is_net_alias_name_check(known_net, pin, net_alias, mod): - return True - # Check connections of all other subinsts - mod_set = set() - for subinst, inst_conns in zip(self.insts, self.conns): - for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins): - if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod): - return True - elif inst_conn.lower() == known_net.lower() and subinst.mod not in mod_set: - if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set): - return True - mod_set.add(subinst.mod) - return False - - def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod): - """Utility function for checking single net alias.""" - return self == mod and \ - child_net.lower() == alias_net.lower() and \ - parent_net.lower() == alias_net.lower() - - def get_mod_net(self, parent_net, child_inst, child_conns): - """ - Given an instance and net, returns the internal net in the mod - corresponding to input net. - """ - for conn, pin in zip(child_conns, child_inst.mod.pins): - if parent_net.lower() == conn.lower(): - return pin - return None - def translate_nets(self, subinst_ports, port_dict, inst_name): """Converts connection names to their spice hierarchy equivalent""" converted_conns = [] diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 22826054..d350c156 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1324,6 +1324,8 @@ class layout(): pdf.drawLayout() pdf.writeToFile(pdf_name) + + def print_attr(self): """Prints a list of attributes for the current layout object""" debug.info(0, diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 30e58809..cfb9f9fa 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -499,3 +499,48 @@ class spice(): def return_power(self, dynamic=0.0, leakage=0.0): return power_data(dynamic, leakage) + def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None): + """Given a list of nets, will compare the internal alias of a mod to determine + if the nets have a connection to this mod's net (but not inst). + """ + if not exclusion_set: + exclusion_set = set() + try: + self.name_dict + except AttributeError: + self.name_dict = {} + self.build_names(self.name_dict, inst_name, port_nets) + aliases = [] + for net in path_nets: + net = net.lower() + int_net = self.name_dict[net]['int_net'] + int_mod = self.name_dict[net]['mod'] + if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set): + aliases.append(net) + return aliases + + def is_net_alias(self, known_net, net_alias, mod, exclusion_set): + """Checks if the alias_net in input mod is the same as the input net for this mod (self).""" + if self in exclusion_set: + return False + # Check ports of this mod + for pin in self.pins: + if self.is_net_alias_name_check(known_net, pin, net_alias, mod): + return True + # Check connections of all other subinsts + mod_set = set() + for subinst, inst_conns in zip(self.insts, self.conns): + for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins): + if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod): + return True + elif inst_conn.lower() == known_net.lower() and subinst.mod not in mod_set: + if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set): + return True + mod_set.add(subinst.mod) + return False + + def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod): + """Utility function for checking single net alias.""" + return self == mod and \ + child_net.lower() == alias_net.lower() and \ + parent_net.lower() == alias_net.lower() diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index e91d8c2f..814d9308 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -33,7 +33,7 @@ class bitcell(bitcell_base.bitcell_base): props.bitcell.cell_6t.pin.vdd, props.bitcell.cell_6t.pin.gnd] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - storage_nets = ['Q', 'Qbar'] + storage_nets = ['Q', 'Q_bar'] (width, height) = utils.get_libcell_size("cell_6t", GDS["unit"], @@ -51,7 +51,7 @@ class bitcell(bitcell_base.bitcell_base): self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) - debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells") +# debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells") def get_all_wl_names(self): """ Creates a list of all wordline pin names """ diff --git a/compiler/bitcells/bitcell_base.py b/compiler/bitcells/bitcell_base.py index 5265904b..690e98fa 100644 --- a/compiler/bitcells/bitcell_base.py +++ b/compiler/bitcells/bitcell_base.py @@ -8,8 +8,9 @@ import debug import design +from globals import OPTS import logical_effort -from tech import parameter, drc +from tech import parameter, drc, layer class bitcell_base(design.design): @@ -78,7 +79,85 @@ class bitcell_base(design.design): fmt_str = "Storage nodes={} not found in spice file." debug.info(1, fmt_str.format(self.storage_nets)) return None - + + def get_storage_net_offset(self): + """ + Gets the location of the storage net labels to add top level + labels for pex simulation. + """ + # If we generated the bitcell, we already know where Q and Q_bar are + if OPTS.bitcell is not "pbitcell": + self.storage_net_offsets = [] + for i in range(len(self.get_storage_net_names())): + for text in self.gds.getTexts(layer["m1"]): + if self.storage_nets[i] == text.textString.rstrip('\x00'): + self.storage_net_offsets.append(text.coordinates[0]) + + for i in range(len(self.storage_net_offsets)): + self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]]) + + + return(self.storage_net_offsets) + + def get_bitline_offset(self): + + bl_names = self.get_all_bl_names() + br_names = self.get_all_br_names() + + found_bl = [] + found_br = [] + + self.bl_offsets = [] + self.br_offsets = [] + + for i in range(len(bl_names)): + for text in self.gds.getTexts(layer["m2"]): + if not bl_names[i] in found_bl: + if bl_names[i] == text.textString.rstrip('\x00'): + self.bl_offsets.append(text.coordinates[0]) + found_bl.append(bl_names[i]) + + continue + + for i in range(len(br_names)): + for text in self.gds.getTexts(layer["m2"]): + if not br_names[i] in found_br: + if br_names[i] == text.textString.rstrip('\x00'): + self.br_offsets.append(text.coordinates[0]) + found_br.append(br_names[i]) + continue + + for i in range(len(self.bl_offsets)): + self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]]) + + for i in range(len(self.br_offsets)): + self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]]) + + return(self.bl_offsets, self.br_offsets, found_bl, found_br) + + def get_normalized_storage_nets_offset(self): + """ + Convert storage net offset to be relative to the bottom left corner + of the bitcell. This is useful for making sense of offsets outside + of the bitcell. + """ + if OPTS.bitcell is not "pbitcell": + normalized_storage_net_offset = self.get_storage_net_offset() + + else: + net_offset = self.get_storage_net_offset() + Q_x = net_offset[0][0] - self.leftmost_xpos + Q_y = net_offset[0][1] - self.botmost_ypos + Q_bar_x = net_offset[1][0] - self.leftmost_xpos + Q_bar_y = net_offset[1][1] - self.botmost_ypos + + normalized_storage_net_offset = [[Q_x,Q_y],[Q_bar_x,Q_bar_y]] + + return normalized_storage_net_offset + + def get_normalized_bitline_offset(self): + return self.get_bitline_offset() + def build_graph(self, graph, inst_name, port_nets): """ By default, bitcells won't be part of the graph. diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index aea7a6dc..25868df5 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -26,7 +26,7 @@ class pbitcell(bitcell_base.bitcell_base): self.num_w_ports = OPTS.num_w_ports self.num_r_ports = OPTS.num_r_ports self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports - + self.replica_bitcell = replica_bitcell self.dummy_bitcell = dummy_bitcell @@ -152,7 +152,7 @@ class pbitcell(bitcell_base.bitcell_base): self.Q_bar = "Q_bar" self.Q = "Q" self.storage_nets = [self.Q, self.Q_bar] - + def add_modules(self): """ Determine size of transistors and add ptx modules """ # if there are any read/write ports, @@ -352,6 +352,11 @@ class pbitcell(bitcell_base.bitcell_base): self.right_building_edge = right_inverter_xpos \ + self.inverter_nmos.active_width + def add_pex_labels(self, left_inverter_offset, right_inverter_offset): + self.add_label("Q", "metal1", left_inverter_offset) + self.add_label("Q_bar", "metal1", right_inverter_offset) + self.storage_net_offsets = [left_inverter_offset, right_inverter_offset] + def route_storage(self): """ Routes inputs and outputs of inverters to cross couple them """ # connect input (gate) of inverters @@ -396,6 +401,16 @@ class pbitcell(bitcell_base.bitcell_base): gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x, contact_offset_right.y) self.add_path("poly", [contact_offset_right, gate_offset_left]) + if OPTS.use_pex: + # add labels to cross couple inverter for extracted simulation + contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \ + + 0.5 * contact.poly.height, + self.cross_couple_upper_ypos) + + contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \ + - 0.5*contact.poly.height, + self.cross_couple_lower_ypos) + self.add_pex_labels(contact_offset_left_output, contact_offset_right_output) def route_rails(self): """ Adds gnd and vdd rails and connects them to the inverters """ diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 54d6217c..f04965e5 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -136,19 +136,18 @@ class delay(simulation): """ self.bitline_volt_meas = [] - self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO", - self.bl_name)) + self.bl_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO", - self.br_name)) + self.br_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE", - self.bl_name)) + self.bl_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE", - self.br_name)) + self.br_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE return self.bitline_volt_meas @@ -181,8 +180,12 @@ class delay(simulation): self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name), meas.targ_name_no_port)) self.dout_volt_meas[-1].meta_str = meas.meta_str - - self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9) + + if not OPTS.use_pex: + 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 @@ -228,8 +231,13 @@ 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)) - q_name = cell_name+'.'+str(storage_names[0]) - qbar_name = cell_name+'.'+str(storage_names[1]) + if not OPTS.use_pex: + q_name = cell_name+'.'+str(storage_names[0]) + qbar_name = cell_name+'.'+str(storage_names[1]) + else: + 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) # 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 @@ -244,18 +252,7 @@ class delay(simulation): self.load = load self.slew = slew - - def add_graph_exclusions(self): - """Exclude portions of SRAM from timing graph which are not relevant""" - - # other initializations can only be done during analysis when a bit has been selected - # for testing. - self.sram.bank.graph_exclude_precharge() - self.sram.graph_exclude_addr_dff() - self.sram.graph_exclude_data_dff() - self.sram.graph_exclude_ctrl_dffs() - self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() - + def create_graph(self): """Creates timing graph to generate the timing paths for the SRAM output.""" @@ -267,74 +264,6 @@ class delay(simulation): self.sram_spc_name = "X{}".format(self.sram.name) self.sram.build_graph(self.graph,self.sram_spc_name,self.pins) - def set_internal_spice_names(self): - """Sets important names for characterization such as Sense amp enable and internal bit nets.""" - - port = self.read_ports[0] - self.graph.get_all_paths('{}{}'.format("clk", port), - '{}{}_{}'.format(self.dout_name, port, self.probe_data)) - - sen_with_port = self.get_sen_name(self.graph.all_paths) - if sen_with_port.endswith(str(port)): - self.sen_name = sen_with_port[:-len(str(port))] - else: - self.sen_name = sen_with_port - debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.") - - debug.info(2,"s_en name = {}".format(self.sen_name)) - - bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port) - port_pos = -1-len(str(self.probe_data))-len(str(port)) - - if bl_name_port.endswith(str(port)+"_"+str(self.probe_data)): - self.bl_name = bl_name_port[:port_pos] +"{}"+ bl_name_port[port_pos+len(str(port)):] - elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 - self.bl_name = bl_name_port - else: - self.bl_name = bl_name_port - debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") - - if br_name_port.endswith(str(port)+"_"+str(self.probe_data)): - self.br_name = br_name_port[:port_pos] +"{}"+ br_name_port[port_pos+len(str(port)):] - elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 - self.br_name = br_name_port - else: - self.br_name = br_name_port - debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") - debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) - - def get_sen_name(self, paths, assumed_port=None): - """ - Gets the signal name associated with the sense amp enable from input paths. - Only expects a single path to contain the sen signal name. - """ - - sa_mods = factory.get_mods(OPTS.sense_amp) - # Any sense amp instantiated should be identical, any change to that - # will require some identification to determine the mod desired. - 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]) - - return sen_name - - def get_bl_name(self, paths, port): - """Gets the signal name associated with the bitlines in the bank.""" - - cell_mod = factory.create(module_type=OPTS.bitcell) - cell_bl = cell_mod.get_bl_name(port) - cell_br = cell_mod.get_br_name(port) - - bl_found = False - # Only a single path should contain a single s_en name. Anything else is an error. - bl_names = [] - 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)) - - return bl_names[0], bl_names[1] - - def get_bl_name_search_exclusions(self): """Gets the mods as a set which should be excluded while searching for name.""" @@ -342,36 +271,6 @@ class delay(simulation): # so it makes the search awkward return set(factory.get_mods(OPTS.replica_bitline)) - def get_primary_cell_mod(self, cell_mods): - """ - Distinguish bitcell array mod from replica bitline array. - Assume there are no replica bitcells in the primary array. - """ - if len(cell_mods) == 1: - return cell_mods[0] - rbc_mods = factory.get_mods(OPTS.replica_bitcell) - non_rbc_mods = [] - for bitcell in cell_mods: - has_cell = False - for replica_cell in rbc_mods: - has_cell = has_cell or replica_cell.contains(bitcell, replica_cell.mods) - if not has_cell: - non_rbc_mods.append(bitcell) - if len(non_rbc_mods) != 1: - debug.error('Multiple bitcell mods found. Cannot distinguish for characterization',1) - return non_rbc_mods[0] - - def are_mod_pins_equal(self, mods): - """Determines if there are pins differences in the input mods""" - - if len(mods) == 0: - return True - pins = mods[0].pins - for mod in mods[1:]: - if pins != mod.pins: - return False - return True - def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None): """ Finds a single alias for the int_net in given paths. @@ -422,8 +321,13 @@ class delay(simulation): # instantiate the sram self.sf.write("\n* Instantiation of the SRAM\n") - self.stim.inst_model(pins=self.pins, - model_name=self.sram.name) + if not OPTS.use_pex: + self.stim.inst_model(pins=self.pins, + model_name=self.sram.name) + else: + self.stim.inst_sram_pex(pins=self.pins, + model_name=self.sram.name) + self.sf.write("\n* SRAM output loads\n") for port in self.read_ports: for i in range(self.word_size): diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 8574c4f6..395c612b 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -38,6 +38,7 @@ class functional(simulation): if not self.num_spare_cols: self.num_spare_cols = 0 + self.probe_address, self.probe_data = '0'*self.addr_size,0 self.set_corner(corner) self.set_spice_constants() self.set_stimulus_variables() @@ -47,6 +48,8 @@ class functional(simulation): self.add_graph_exclusions() 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)) # Number of checks can be changed self.num_cycles = 15 @@ -419,18 +422,6 @@ class functional(simulation): self.stim.write_control(self.cycle_times[-1] + self.period) self.sf.close() - - # FIXME: refactor to share with delay.py - def add_graph_exclusions(self): - """Exclude portions of SRAM from timing graph which are not relevant""" - - # other initializations can only be done during analysis when a bit has been selected - # for testing. - self.sram.bank.graph_exclude_precharge() - self.sram.graph_exclude_addr_dff() - self.sram.graph_exclude_data_dff() - self.sram.graph_exclude_ctrl_dffs() - self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() # FIXME: refactor to share with delay.py def create_graph(self): @@ -444,25 +435,8 @@ class functional(simulation): self.graph = graph_util.timing_graph() self.sram_spc_name = "X{}".format(self.sram.name) self.sram.build_graph(self.graph, self.sram_spc_name, self.pins) - - # FIXME: refactor to share with delay.py - def set_internal_spice_names(self): - """Sets important names for characterization such as Sense amp enable and internal bit nets.""" - - # For now, only testing these using first read port. - port = self.read_ports[0] - self.graph.get_all_paths('{}{}'.format("clk", port), - '{}{}_{}'.format(self.dout_name, port, 0).lower()) - - self.sen_name = self.get_sen_name(self.graph.all_paths) - debug.info(2, "s_en name = {}".format(self.sen_name)) - - self.bl_name, self.br_name = self.get_bl_name(self.graph.all_paths, port) - debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name)) - - self.q_name, self.qbar_name = self.get_bit_name() - debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name)) - + + #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) @@ -473,37 +447,6 @@ class functional(simulation): qbar_name = cell_name + '.' + str(storage_names[1]) return (q_name, qbar_name) - - # FIXME: refactor to share with delay.py - def get_sen_name(self, paths): - """ - Gets the signal name associated with the sense amp enable from input paths. - Only expects a single path to contain the sen signal name. - """ - - sa_mods = factory.get_mods(OPTS.sense_amp) - # Any sense amp instantiated should be identical, any change to that - # will require some identification to determine the mod desired. - 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]) - return sen_name - - # FIXME: refactor to share with delay.py - def get_bl_name(self, paths, port): - """Gets the signal name associated with the bitlines in the bank.""" - - cell_mod = factory.create(module_type=OPTS.bitcell) - cell_bl = cell_mod.get_bl_name(port) - cell_br = cell_mod.get_br_name(port) - - # Only a single path should contain a single s_en name. Anything else is an error. - bl_names = [] - 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)) - - return bl_names[0], bl_names[1] def get_bl_name_search_exclusions(self): """Gets the mods as a set which should be excluded while searching for name.""" diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 064eb2c5..b73af4f6 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -15,6 +15,7 @@ from .trim_spice import * from .charutils import * import utils from globals import OPTS +from sram_factory import factory class simulation(): @@ -408,3 +409,96 @@ class simulation(): pin_names.append("{0}".format("gnd")) return pin_names + def add_graph_exclusions(self): + """Exclude portions of SRAM from timing graph which are not relevant""" + + # other initializations can only be done during analysis when a bit has been selected + # for testing. + self.sram.bank.graph_exclude_precharge() + self.sram.graph_exclude_addr_dff() + self.sram.graph_exclude_data_dff() + self.sram.graph_exclude_ctrl_dffs() + self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() + + def set_internal_spice_names(self): + """Sets important names for characterization such as Sense amp enable and internal bit nets.""" + + port = self.read_ports[0] + if not OPTS.use_pex: + self.graph.get_all_paths('{}{}'.format("clk", port), + '{}{}_{}'.format(self.dout_name, port, self.probe_data)) + + sen_with_port = self.get_sen_name(self.graph.all_paths) + if sen_with_port.endswith(str(port)): + self.sen_name = sen_with_port[:-len(str(port))] + else: + self.sen_name = sen_with_port + debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.") + + debug.info(2,"s_en name = {}".format(self.sen_name)) + + bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port) + port_pos = -1-len(str(self.probe_data))-len(str(port)) + + if bl_name_port.endswith(str(port)+"_"+str(self.probe_data)): + self.bl_name = bl_name_port[:port_pos] +"{}"+ bl_name_port[port_pos+len(str(port)):] + elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 + self.bl_name = bl_name_port + else: + self.bl_name = bl_name_port + debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") + + if br_name_port.endswith(str(port)+"_"+str(self.probe_data)): + self.br_name = br_name_port[:port_pos] +"{}"+ br_name_port[port_pos+len(str(port)):] + elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 + self.br_name = br_name_port + else: + self.br_name = br_name_port + debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") + debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) + else: + self.graph.get_all_paths('{}{}'.format("clk", port), + '{}{}_{}'.format(self.dout_name, port, self.probe_data)) + + self.sen_name = self.get_sen_name(self.graph.all_paths) + debug.info(2,"s_en name = {}".format(self.sen_name)) + + + self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size-1) + self.br_name = "br{0}_{1}".format(port, OPTS.word_size-1) + debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) + + def get_sen_name(self, paths, assumed_port=None): + """ + Gets the signal name associated with the sense amp enable from input paths. + Only expects a single path to contain the sen signal name. + """ + + sa_mods = factory.get_mods(OPTS.sense_amp) + # Any sense amp instantiated should be identical, any change to that + # will require some identification to determine the mod desired. + 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: + sen_name = sen_name.split('.')[-1] + return sen_name + + + def get_bl_name(self, paths, port): + """Gets the signal name associated with the bitlines in the bank.""" + + cell_mod = factory.create(module_type=OPTS.bitcell) + cell_bl = cell_mod.get_bl_name(port) + cell_br = cell_mod.get_br_name(port) + + bl_found = False + # Only a single path should contain a single s_en name. Anything else is an error. + bl_names = [] + 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: + for i in range(len(bl_names)): + bl_names[i] = bl_names[i].split('.')[-1] + return bl_names[0], bl_names[1] \ No newline at end of file diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 11dc449a..90fd6213 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -56,7 +56,30 @@ class stimuli(): for pin in pins: self.sf.write("{0} ".format(pin)) self.sf.write("{0}\n".format(model_name)) + + def inst_sram_pex(self, pins, model_name): + self.sf.write("X{0} ".format(model_name)) + for pin in pins: + self.sf.write("{0} ".format(pin)) + for bank in range(OPTS.num_banks): + row = int(OPTS.num_words / OPTS.words_per_row) - 1 + col = int(OPTS.word_size * OPTS.words_per_row) - 1 + self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col)) + self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col)) + # can't add all bitcells to top level due to ngspice max port count of 1005 + # for row in range(int(OPTS.num_words / OPTS.words_per_row)): + # for col in range(int(OPTS.word_size * OPTS.words_per_row)): + # self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col)) + # self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col)) + for bank in range(OPTS.num_banks): + for col in range(OPTS.word_size * OPTS.words_per_row): + for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports): + self.sf.write("bl{0}_{1} ".format(port, col)) + self.sf.write("br{0}_{1} ".format(port, col)) + + self.sf.write("s_en{0} ".format(bank)) + self.sf.write("{0}\n".format(model_name)) def create_inverter(self, size=1, beta=2.5): """ Generates inverter for the top level signals (only for sim purposes) """ diff --git a/compiler/custom/dff.py b/compiler/custom/dff.py index c8fdb4b0..cb703707 100644 --- a/compiler/custom/dff.py +++ b/compiler/custom/dff.py @@ -55,12 +55,6 @@ class dff(design.design): transition_prob = 0.5 return transition_prob*(c_load + c_para) - def get_clk_cin(self): - """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. - return parameter["dff_clk_cin"] - def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" self.add_graph_edges(graph, port_nets) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 545f394b..aa117446 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -74,7 +74,6 @@ class bank(design.design): self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1) self.bank_array_ur = self.bitcell_array_inst.ur() self.bank_array_ul = self.bitcell_array_inst.ul() - self.DRC_LVS() def add_pins(self): @@ -1049,42 +1048,6 @@ class bank(design.design): self.add_via_center(layers=self.m1_stack, offset=control_pos) - def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): - """Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline""" - # Decoder is assumed to have settled before the negative edge of the clock. - # Delay model relies on this assumption - stage_effort_list = [] - wordline_cout = self.bitcell_array.get_wordline_cin() + external_cout - stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout, - inp_is_rise) - - return stage_effort_list - - def get_wl_en_cin(self): - """Get the relative capacitance of all the clk connections in the bank""" - # wl_en only used in the wordline driver. - return self.port_address.wordline_driver.get_wl_en_cin() - - def get_w_en_cin(self): - """Get the relative capacitance of all the clk connections in the bank""" - # wl_en only used in the wordline driver. - port = self.write_ports[0] - return self.port_data[port].write_driver.get_w_en_cin() - - def get_clk_bar_cin(self): - """Get the relative capacitance of all the clk_bar connections in the bank""" - # Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array. - - # Precharges are the all the same in Mulitport, one is picked - port = self.read_ports[0] - return self.port_data[port].precharge_array.get_en_cin() - - def get_sen_cin(self): - """Get the relative capacitance of all the sense amp enable connections in the bank""" - # Current bank only uses sen as an enable for the sense amps. - port = self.read_ports[0] - return self.port_data[port].sense_amp_array.get_en_cin() - def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" for port in self.read_ports: diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index a5d09ab6..111f2795 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -104,13 +104,6 @@ class bitcell_array(bitcell_base_array): bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c return bl_wire - def get_wordline_cin(self): - """Get the relative input capacitance from the wordline connections in all the bitcell""" - # A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns - bitcell_wl_cin = self.cell.get_wl_cin() - total_cin = bitcell_wl_cin * self.column_size - return total_cin - def graph_exclude_bits(self, targ_row, targ_col): """Excludes bits in column from being added to graph except target""" # Function is not robust with column mux configurations diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index a17f5aa7..e99d7b89 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -156,96 +156,12 @@ class control_logic(design.design): self.nand2 = factory.create(module_type="pnand2", height=dff_height) self.add_mod(self.nand2) - - # if (self.port_type == "rw") or (self.port_type == "r"): - # from importlib import reload - # self.delay_chain_resized = False - # c = reload(__import__(OPTS.replica_bitline)) - # replica_bitline = getattr(c, OPTS.replica_bitline) - # bitcell_loads = int(math.ceil(self.num_rows * OPTS.rbl_delay_percentage)) - # #Use a model to determine the delays with that heuristic - # if OPTS.use_tech_delay_chain_size: #Use tech parameters if set. - # fanout_list = OPTS.delay_chain_stages*[OPTS.delay_chain_fanout_per_stage] - # debug.info(1, "Using tech parameters to size delay chain: fanout_list={}".format(fanout_list)) - # self.replica_bitline = factory.create(module_type="replica_bitline", - # delay_fanout_list=fanout_list, - # bitcell_loads=bitcell_loads) - # if self.sram != None: #Calculate model value even for specified sizes - # self.set_sen_wl_delays() - - # else: #Otherwise, use a heuristic and/or model based sizing. - # #First use a heuristic - # delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size() - # self.replica_bitline = factory.create(module_type="replica_bitline", - # delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic, - # bitcell_loads=bitcell_loads) - # #Resize if necessary, condition depends on resizing method - # if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match(): - # #This resizes to match fall and rise delays, can make the delay chain weird sizes. - # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) - # self.replica_bitline = factory.create(module_type="replica_bitline", - # delay_fanout_list=stage_list, - # bitcell_loads=bitcell_loads) - - # #This resizes based on total delay. - # # delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) - # # self.replica_bitline = factory.create(module_type="replica_bitline", - # # delay_fanout_list=[delay_fanout]*delay_stages, - # # bitcell_loads=bitcell_loads) - - # self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing - # self.delay_chain_resized = True debug.check(OPTS.delay_chain_stages % 2, "Must use odd number of delay chain stages for inverting delay chain.") self.delay_chain=factory.create(module_type="delay_chain", fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ]) self.add_mod(self.delay_chain) - - def get_heuristic_delay_chain_size(self): - """Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """ - # FIXME: The minimum was 2 fanout, now it will not pass DRC unless it is 3. Why? - delay_fanout = 3 # This can be anything >=3 - # Model poorly captures delay of the column mux. Be pessismistic for column mux - if self.words_per_row >= 2: - delay_stages = 8 - else: - delay_stages = 2 - - # Read ports have a shorter s_en delay. The model is not accurate enough to catch this difference - # on certain sram configs. - if self.port_type == "r": - delay_stages+=2 - - return (delay_stages, delay_fanout) - - def set_sen_wl_delays(self): - """Set delays for wordline and sense amp enable""" - self.wl_delay_rise, self.wl_delay_fall = self.get_delays_to_wl() - self.sen_delay_rise, self.sen_delay_fall = self.get_delays_to_sen() - self.wl_delay = self.wl_delay_rise + self.wl_delay_fall - self.sen_delay = self.sen_delay_rise + self.sen_delay_fall - - def does_sen_rise_fall_timing_match(self): - """Compare the relative rise/fall delays of the sense amp enable and wordline""" - self.set_sen_wl_delays() - # This is not necessarily more reliable than total delay in some cases. - if (self.wl_delay_rise * self.wl_timing_tolerance >= self.sen_delay_rise or - self.wl_delay_fall * self.wl_timing_tolerance >= self.sen_delay_fall): - return False - else: - return True - - def does_sen_total_timing_match(self): - """Compare the total delays of the sense amp enable and wordline""" - self.set_sen_wl_delays() - # The sen delay must always be bigger than than the wl - # delay. This decides how much larger the sen delay must be - # before a re-size is warranted. - if self.wl_delay * self.wl_timing_tolerance >= self.sen_delay: - return False - else: - return True def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout): """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" @@ -333,17 +249,6 @@ class control_logic(design.design): delay_per_stage = fanout + 1 + self.inv_parasitic_delay delay_stages = ceil(required_delay / delay_per_stage) return delay_stages - - def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall): - """ - Produces a list of fanouts which determine the size of the delay chain. - List length is the number of stages. - Assumes the first stage is falling. - """ - stage_list = [] - for i in range(total_stages): - if i % 2 == 0: - stage_list.append() def setup_signal_busses(self): """ Setup bus names, determine the size of the busses etc """ @@ -869,138 +774,6 @@ class control_logic(design.design): offset=pin.ll(), height=pin.height(), width=pin.width()) - - def get_delays_to_wl(self): - """Get the delay (in delay units) of the clk to a wordline in the bitcell array""" - debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") - self.wl_stage_efforts = self.get_wordline_stage_efforts() - clk_to_wl_rise, clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts) - total_delay = clk_to_wl_rise + clk_to_wl_fall - debug.info(1, - "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, - clk_to_wl_fall, - total_delay)) - return clk_to_wl_rise, clk_to_wl_fall - - def get_wordline_stage_efforts(self): - """Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts""" - stage_effort_list = [] - - # Initial direction of gated_clk_bar signal for this path - is_clk_bar_rise = True - - # Calculate the load on wl_en within the module and add it to external load - external_cout = self.sram.get_wl_en_cin() - # First stage is the clock buffer - stage_effort_list += self.clk_buf_driver.get_stage_efforts(external_cout, is_clk_bar_rise) - last_stage_is_rise = stage_effort_list[-1].is_rise - - # Then ask the sram for the other path delays (from the bank) - stage_effort_list += self.sram.get_wordline_stage_efforts(last_stage_is_rise) - - return stage_effort_list - - def get_delays_to_sen(self): - """ - Get the delay (in delay units) of the clk to a sense amp enable. - This does not incorporate the delay of the replica bitline. - """ - debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") - self.sen_stage_efforts = self.get_sa_enable_stage_efforts() - clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts) - total_delay = clk_to_sen_rise + clk_to_sen_fall - debug.info(1, - "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, - clk_to_sen_fall, - total_delay)) - return clk_to_sen_rise, clk_to_sen_fall - - def get_sa_enable_stage_efforts(self): - """Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list""" - stage_effort_list = [] - - # Initial direction of clock signal for this path - last_stage_rise = True - - # First stage, gated_clk_bar -(and2)-> rbl_in. Only for RW ports. - if self.port_type == "rw": - stage1_cout = self.replica_bitline.get_en_cin() - stage_effort_list += self.and2.get_stage_efforts(stage1_cout, last_stage_rise) - last_stage_rise = stage_effort_list[-1].is_rise - - # Replica bitline stage, rbl_in -(rbl)-> pre_s_en - stage2_cout = self.sen_and2.get_cin() - stage_effort_list += self.replica_bitline.determine_sen_stage_efforts(stage2_cout, last_stage_rise) - last_stage_rise = stage_effort_list[-1].is_rise - - # buffer stage, pre_s_en -(buffer)-> s_en - stage3_cout = self.sram.get_sen_cin() - stage_effort_list += self.s_en_driver.get_stage_efforts(stage3_cout, last_stage_rise) - last_stage_rise = stage_effort_list[-1].is_rise - - return stage_effort_list - - def get_wl_sen_delays(self): - """ Gets a list of the stages and delays in order of their path. """ - - if self.sen_stage_efforts == None or self.wl_stage_efforts == None: - debug.error("Model delays not calculated for SRAM.", 1) - wl_delays = logical_effort.calculate_delays(self.wl_stage_efforts) - sen_delays = logical_effort.calculate_delays(self.sen_stage_efforts) - return wl_delays, sen_delays - - def analytical_delay(self, corner, slew, load): - """ Gets the analytical delay from clk input to wl_en output """ - - stage_effort_list = [] - # Calculate the load on clk_buf_bar - # ext_clk_buf_cout = self.sram.get_clk_bar_cin() - - # Operations logic starts on negative edge - last_stage_rise = False - - # First stage(s), clk -(pdriver)-> clk_buf. - # clk_buf_cout = self.replica_bitline.get_en_cin() - clk_buf_cout = 0 - stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise) - last_stage_rise = stage_effort_list[-1].is_rise - - # Second stage, clk_buf -(inv)-> clk_bar - clk_bar_cout = self.and2.get_cin() - stage_effort_list += self.and2.get_stage_efforts(clk_bar_cout, last_stage_rise) - last_stage_rise = stage_effort_list[-1].is_rise - - # Third stage clk_bar -(and)-> gated_clk_bar - gated_clk_bar_cin = self.get_gated_clk_bar_cin() - stage_effort_list.append(self.inv.get_stage_effort(gated_clk_bar_cin, last_stage_rise)) - last_stage_rise = stage_effort_list[-1].is_rise - - # Stages from gated_clk_bar -------> wordline - stage_effort_list += self.get_wordline_stage_efforts() - return stage_effort_list - - def get_clk_buf_cin(self): - """ - Get the loads that are connected to the buffered clock. - Includes all the DFFs and some logic. - """ - - # Control logic internal load - int_clk_buf_cap = self.inv.get_cin() + self.ctrl_dff_array.get_clk_cin() + self.and2.get_cin() - - # Control logic external load (in the other parts of the SRAM) - ext_clk_buf_cap = self.sram.get_clk_bar_cin() - - return int_clk_buf_cap + ext_clk_buf_cap - - def get_gated_clk_bar_cin(self): - """Get intermediates net gated_clk_bar's capacitance""" - - total_cin = 0 - total_cin += self.wl_en_driver.get_cin() - if self.port_type == 'rw': - total_cin += self.and2.get_cin() - return total_cin def graph_exclude_dffs(self): """Exclude dffs from graph as they do not represent critical path""" diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index 246299c1..30126b63 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -210,25 +210,3 @@ class delay_chain(design.design): layer="m2", start=mid_point, end=mid_point.scale(1, 0)) - - def get_cin(self): - """Get the enable input ralative capacitance""" - # Only 1 input to the delay chain which is connected to an inverter. - dc_cin = self.inv.get_cin() - return dc_cin - - def determine_delayed_en_stage_efforts(self, ext_delayed_en_cout, inp_is_rise=True): - """Get the stage efforts from the en to s_en. Does not compute the delay for the bitline load.""" - stage_effort_list = [] - # Add a stage to the list for every stage in delay chain. - # Stages only differ in fanout except the last which has an external cout. - last_stage_is_rise = inp_is_rise - for stage_fanout in self.fanout_list: - stage_cout = self.inv.get_cin() * (stage_fanout + 1) - if len(stage_effort_list) == len(self.fanout_list) - 1: - stage_cout+=ext_delayed_en_cout - stage = self.inv.get_stage_effort(stage_cout, last_stage_is_rise) - stage_effort_list.append(stage) - last_stage_is_rise = stage.is_rise - - return stage_effort_list diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index c4f85a6d..cb82443b 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -155,9 +155,3 @@ class dff_array(design.design): self.add_via_stack_center(from_layer=clk_pin.layer, to_layer="m3", offset=vector(clk_pin.cx(), clk_ypos)) - - def get_clk_cin(self): - """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" - dff_clk_cin = self.dff.get_clk_cin() - total_cin = dff_clk_cin * self.rows * self.columns - return total_cin diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 1657d7a8..1290bf12 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -196,10 +196,3 @@ class dff_buf(design.design): self.add_via_stack_center(from_layer=a2_pin.layer, to_layer="m2", offset=qb_pos) - - def get_clk_cin(self): - """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" - # This is a handmade cell so the value must be entered in the tech.py file or estimated. - # Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. - # FIXME: Dff changed in a past commit. The parameter need to be updated. - return parameter["dff_clk_cin"] diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 88852d45..d6382973 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -227,9 +227,3 @@ class dff_buf_array(design.design): # Drop a via to the M3 pin self.add_via_center(layers=self.m2_stack, offset=vector(clk_pin.cx(), clk_ypos)) - - def get_clk_cin(self): - """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" - dff_clk_cin = self.dff.get_clk_cin() - total_cin = dff_clk_cin * self.rows * self.columns - return total_cin diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index 033312ef..8f50f9e8 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -150,7 +150,3 @@ class dff_inv(design.design): offset=dout_pin.center()) self.add_via_center(layers=self.m1_stack, offset=dout_pin.center()) - - def get_clk_cin(self): - """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" - return self.dff.get_clk_cin() diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index 6b08bcce..1687e043 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -188,10 +188,4 @@ class dff_inv_array(design.design): height=self.height) # Drop a via to the M3 pin self.add_via_center(layers=self.m2_stack, - offset=vector(clk_pin.cx(),clk_ypos)) - - def get_clk_cin(self): - """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" - dff_clk_cin = self.dff.get_clk_cin() - total_cin = dff_clk_cin * self.rows * self.columns - return total_cin + offset=vector(clk_pin.cx(),clk_ypos)) diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 0fd7d648..4858c5f2 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -101,16 +101,6 @@ class dummy_array(bitcell_base_array): self.copy_layout_pin(inst, pin_name) def input_load(self): + # FIXME: This appears to be old code from previous characterization. Needs to be updated. wl_wire = self.gen_wl_wire() return wl_wire.return_input_cap() - - def get_wordline_cin(self): - """ - Get the relative input capacitance from the - wordline connections in all the bitcell - """ - # A single wordline is connected to all the bitcells - # in a single row meaning the capacitance depends on the # of columns - bitcell_wl_cin = self.cell.get_wl_cin() - total_cin = bitcell_wl_cin * self.column_size - return total_cin diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index e32fe1c6..ca26993f 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -606,11 +606,3 @@ class hierarchical_decoder(design.design): to_layer=self.output_layer, offset=rail_pos, directions=self.bus_directions) - - def input_load(self): - if self.determine_predecodes(self.num_inputs)[1]==0: - pre = self.pre2_4 - else: - pre = self.pre3_8 - return pre.input_load() - diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index c2d3d986..00fe9885 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -125,13 +125,3 @@ class precharge_array(design.design): offset = vector(tempx, 0) self.local_insts[i].place(offset=offset, mirror=mirror) xoffset = xoffset + self.pc_cell.width - - def get_en_cin(self): - """ - Get the relative capacitance of all the clk connections - in the precharge array - """ - # Assume single port - precharge_en_cin = self.pc_cell.get_en_cin() - return precharge_en_cin * self.columns - diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 40341639..f1bca739 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -489,13 +489,6 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire - def get_wordline_cin(self): - """Get the relative input capacitance from the wordline connections in all the bitcell""" - # A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns - bitcell_wl_cin = self.cell.get_wl_cin() - total_cin = bitcell_wl_cin * self.column_size - return total_cin - def graph_exclude_bits(self, targ_row, targ_col): """Excludes bits in column from being added to graph except target""" self.bitcell_array.graph_exclude_bits(targ_row, targ_col) diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index 67703903..f1d5de92 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -82,13 +82,6 @@ class sense_amp(design.design): # Power in this module currently not defined. Returns 0 nW (leakage and dynamic). total_power = self.return_power() return total_power - - def get_en_cin(self): - """Get the relative capacitance of sense amp enable gate cin""" - pmos_cin = parameter["sa_en_pmos_size"] / drc("minwidth_tx") - nmos_cin = parameter["sa_en_nmos_size"] / drc("minwidth_tx") - # sen is connected to 2 pmos isolation TX and 1 nmos per sense amp. - return 2 * pmos_cin + nmos_cin def get_enable_name(self): """Returns name used for enable net""" diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 20f6e06f..6d0b85d2 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -190,18 +190,3 @@ class sense_amp_array(design.design): self.add_via_stack_center(from_layer=en_pin.layer, to_layer=self.en_layer, offset=inst.get_pin(self.amp.en_name).center()) - - def input_load(self): - return self.amp.input_load() - - def get_en_cin(self): - """Get the relative capacitance of all the sense amp enable connections in the array""" - sense_amp_en_cin = self.amp.get_en_cin() - return sense_amp_en_cin * self.word_size - - def get_drain_cin(self): - """Get the relative capacitance of the drain of the PMOS isolation TX""" - from tech import parameter - # Bitcell drain load being used to estimate PMOS drain load - drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - return drain_load diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index f57bbb20..267b2a6a 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -230,10 +230,3 @@ class single_level_column_mux_array(design.design): to_layer=self.sel_layer, offset=br_out_offset_begin, directions=self.via_directions) - - def get_drain_cin(self): - """Get the relative capacitance of the drain of the NMOS pass TX""" - from tech import parameter - # Bitcell drain load being used to estimate mux NMOS drain load - drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - return drain_load diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index f82938de..ac0a0cb2 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -159,24 +159,3 @@ class wordline_driver_array(design.design): layer=self.route_layer, start=wl_offset, end=wl_offset - vector(self.m1_width, 0)) - - def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): - """ - Follows the clk_buf to a wordline signal adding - each stages stage effort to a list. - """ - stage_effort_list = [] - - stage1 = self.wl_driver.get_stage_effort(external_cout, inp_is_rise) - stage_effort_list.append(stage1) - - return stage_effort_list - - def get_wl_en_cin(self): - """ - Get the relative capacitance of all - the enable connections in the bank - """ - # The enable is connected to a and2 for every row. - total_cin = self.wl_driver.get_cin() * self.rows - return total_cin diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 665142ec..b7bcd6b3 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -265,7 +265,3 @@ class write_driver_array(design.design): offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1), width=self.width) - def get_w_en_cin(self): - """Get the relative capacitance of all the enable connections in the bank""" - # The enable is connected to a nand2 for every row. - return self.driver.get_w_en_cin() * len(self.driver_insts) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 9b083512..04563209 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -140,8 +140,3 @@ class write_mask_and_array(design.design): supply_pin_left = self.and2_insts[0].get_pin(supply) supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply) self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()]) - - def get_cin(self): - """Get the relative capacitance of all the input connections in the bank""" - # The enable is connected to an and2 for every row. - return self.and2.get_cin() * len(self.and2_insts) diff --git a/compiler/options.py b/compiler/options.py index d97ea300..8d8e3b42 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -93,6 +93,8 @@ class options(optparse.Values): trim_netlist = False # Run with extracted parasitics use_pex = False + # Output config with all options + output_extended_config = False ################### diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index a46485d0..21241056 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -146,21 +146,4 @@ class pand2(pgate.pgate): offset=pin.center(), width=pin.width(), height=pin.height()) - - def get_stage_efforts(self, external_cout, inp_is_rise=False): - """Get the stage efforts of the A or B -> Z path""" - stage_effort_list = [] - stage1_cout = self.inv.get_cin() - stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) - stage_effort_list.append(stage1) - last_stage_is_rise = stage1.is_rise - - stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) - stage_effort_list.append(stage2) - - return stage_effort_list - - def get_cin(self): - """Return the relative input capacitance of a single input""" - return self.nand.get_cin() - + \ No newline at end of file diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 72a57f74..63d1cd0f 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -161,21 +161,4 @@ class pand3(pgate.pgate): slew=nand_delay.slew, load=load) return nand_delay + inv_delay - - def get_stage_efforts(self, external_cout, inp_is_rise=False): - """Get the stage efforts of the A or B -> Z path""" - stage_effort_list = [] - stage1_cout = self.inv.get_cin() - stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) - stage_effort_list.append(stage1) - last_stage_is_rise = stage1.is_rise - - stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) - stage_effort_list.append(stage2) - - return stage_effort_list - - def get_cin(self): - """Return the relative input capacitance of a single input""" - return self.nand.get_cin() - + \ No newline at end of file diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index d82e2091..e504b89e 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -96,21 +96,4 @@ class pbuf(pgate.pgate): offset=a_pin.center(), width=a_pin.width(), height=a_pin.height()) - - def get_stage_efforts(self, external_cout, inp_is_rise=False): - """Get the stage efforts of the A -> Z path""" - stage_effort_list = [] - stage1_cout = self.inv2.get_cin() - stage1 = self.inv1.get_stage_effort(stage1_cout, inp_is_rise) - stage_effort_list.append(stage1) - last_stage_is_rise = stage1.is_rise - - stage2 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise) - stage_effort_list.append(stage2) - - return stage_effort_list - - def get_cin(self): - """Returns the relative capacitance of the input""" - input_cin = self.inv1.get_cin() - return input_cin + \ No newline at end of file diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 8916f0fa..e48f9f6c 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -168,24 +168,4 @@ class pdriver(pgate.pgate): def get_sizes(self): """ Return the relative sizes of the buffers """ return self.size_list - - def get_stage_efforts(self, external_cout, inp_is_rise=False): - """ Get the stage efforts of the A -> Z path """ - cout_list = [] - for prev_inv, inv in zip(self.inv_list, self.inv_list[1:]): - cout_list.append(inv.get_cin()) - - cout_list.append(external_cout) - - stage_effort_list = [] - last_inp_is_rise = inp_is_rise - for inv, cout in zip(self.inv_list, cout_list): - stage = inv.get_stage_effort(cout, last_inp_is_rise) - stage_effort_list.append(stage) - last_inp_is_rise = stage.is_rise - - return stage_effort_list - - def get_cin(self): - """ Returns the relative capacitance of the input """ - return self.inv_list[0].get_cin() + \ No newline at end of file diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index f746736c..497bd3df 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -184,36 +184,3 @@ class pinvbuf(pgate.pgate): self.add_layout_pin_rect_center(text="A", layer=a_pin.layer, offset=a_pin.center()) - - def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False): - """Get the stage efforts of the clk -> clk_buf path""" - stage_effort_list = [] - stage1_cout = self.inv1.get_cin() + self.inv2.get_cin() - stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise) - stage_effort_list.append(stage1) - last_stage_is_rise = stage1.is_rise - - stage2 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise) - stage_effort_list.append(stage2) - - return stage_effort_list - - def determine_clk_buf_bar_stage_efforts(self, external_cout, inp_is_rise=False): - """Get the stage efforts of the clk -> clk_buf path""" - - # After (almost) every stage, the direction of the signal inverts. - stage_effort_list = [] - stage1_cout = self.inv1.get_cin() + self.inv2.get_cin() - stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise) - stage_effort_list.append(stage1) - last_stage_is_rise = stage_effort_list[-1].is_rise - - stage2_cout = self.inv2.get_cin() - stage2 = self.inv1.get_stage_effort(stage2_cout, last_stage_is_rise) - stage_effort_list.append(stage2) - last_stage_is_rise = stage_effort_list[-1].is_rise - - stage3 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise) - stage_effort_list.append(stage3) - - return stage_effort_list diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index aefdbb86..4ae48167 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -300,11 +300,4 @@ class precharge(design.design): self.add_path(self.bitline_layer, [left_pos, right_pos], width=pmos_pin.height()) - - def get_en_cin(self): - """Get the relative capacitance of the enable in the precharge cell""" - # The enable connect to three pmos gates - # They all use the same size pmos. - pmos_cin = self.pmos.get_cin() - return 3 * pmos_cin - + \ No newline at end of file diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index cd9be887..4873e6fc 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -241,18 +241,3 @@ class single_level_column_mux(pgate.pgate): offset=vector(0, 0), width=self.bitcell.width, height=self.height) - - def get_stage_effort(self, corner, slew, load): - """ - Returns relative delay that the column mux. - Difficult to convert to LE model. - """ - parasitic_delay = 1 - # This is not CMOS, so using this may be incorrect. - cin = 2 * self.tx_size - return logical_effort.logical_effort("column_mux", - self.tx_size, - cin, - load, - parasitic_delay, - False) diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 8cf926c6..6b5d117d 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -63,6 +63,18 @@ class sram(): def verilog_write(self, name): self.s.verilog_write(name) + def extended_config_write(self, name): + """Dump config file with all options. + Include defaults and anything changed by input config.""" + f = open(name, "w") + var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name))) + for var_name, var_value in var_dict.items(): + if isinstance(var_value, str): + f.write(str(var_name) + " = " + "\"" + str(var_value) + "\"\n") + else: + f.write(str(var_name) + " = " + str(var_value)+ "\n") + f.close() + def save(self): """ Save all the output files while reporting time to do it as well. """ @@ -137,3 +149,11 @@ class sram(): debug.print_raw("Verilog: Writing to {0}".format(vname)) self.verilog_write(vname) print_time("Verilog", datetime.datetime.now(), start_time) + + # Write out options if specified + if OPTS.output_extended_config: + start_time = datetime.datetime.now() + oname = OPTS.output_path + OPTS.output_name + "_extended.py" + debug.print_raw("Extended Config: Writing to {0}".format(oname)) + self.extended_config_write(oname) + print_time("Extended Config", datetime.datetime.now(), start_time) \ No newline at end of file diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index af1b7a2b..c67f7233 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -626,24 +626,13 @@ class sram_1bank(sram_base): # Insts located in control logic, exclusion function called here for inst in self.control_logic_insts: inst.mod.graph_exclude_dffs() - - def get_sen_name(self, sram_name, port=0): - """Returns the s_en spice name.""" - # Naming scheme is hardcoded using this function, should be built into the - # graph in someway. - sen_name = "s_en{}".format(port) - control_conns = self.get_conns(self.control_logic_insts[port]) - # Sanity checks - if sen_name not in control_conns: - debug.error("Signal={} not contained in control logic connections={}".format(sen_name, - control_conns)) - if sen_name in self.pins: - debug.error("Internal signal={} contained in port list. Name defined by the parent.".format(sen_name)) - return "X{}.{}".format(sram_name, sen_name) def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" # 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) + inst_name = 'x'+inst_name + return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col) + + def get_bank_num(self, inst_name, row, col): + return 0; diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 12af7cf6..c9eb4ea1 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -15,6 +15,9 @@ from design import design from verilog import verilog from lef import lef from sram_factory import factory +from tech import drc +import numpy as np +import logical_effort class sram_base(design, verilog, lef): @@ -84,8 +87,71 @@ class sram_base(design, verilog, lef): for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") + self.add_pin("vdd","POWER") + self.add_pin("gnd","GROUND") + + def add_global_pex_labels(self): + """ + Add pex labels at the sram level for spice analysis + """ + + # add pex labels for bitcells + for bank_num in range(len(self.bank_insts)): + bank = self.bank_insts[bank_num] + pex_data = bank.reverse_transformation_bitcell(bank.mod.bitcell.name) + + bank_offset = pex_data[0] # offset bank relative to sram + Q_offset = pex_data[1] # offset of storage relative to bank + Q_bar_offset = pex_data[2] # offset of storage relative to bank + bl_offsets = pex_data[3] + br_offsets = pex_data[4] + bl_meta = pex_data[5] + br_meta = pex_data[6] + + bl = [] + br = [] + + storage_layer_name = "m1" + bitline_layer_name = "m2" + + for cell in range(len(bank_offset)): + Q = [bank_offset[cell][0] + Q_offset[cell][0], bank_offset[cell][1] + Q_offset[cell][1]] + Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], bank_offset[cell][1] + Q_bar_offset[cell][1]] + OPTS.words_per_row = self.words_per_row + self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))) , storage_layer_name, Q) + self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))), storage_layer_name, Q_bar) + + for cell in range(len(bl_offsets)): + col = bl_meta[cell][0][2] + for bitline in range(len(bl_offsets[cell])): + bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]] + bl.append([bitline_location, bl_meta[cell][bitline][3], col]) + + for cell in range(len(br_offsets)): + col = br_meta[cell][0][2] + for bitline in range(len(br_offsets[cell])): + bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]] + br.append([bitline_location, br_meta[cell][bitline][3], col]) + + for i in range(len(bl)): + self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), bitline_layer_name, bl[i][0]) + + for i in range(len(br)): + self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), bitline_layer_name, br[i][0]) + + # add pex labels for control logic + for i in range (len(self.control_logic_insts)): + instance = self.control_logic_insts[i] + control_logic_offset = instance.offset + for output in instance.mod.output_list: + pin = instance.mod.get_pin(output) + pin.transform([0,0], instance.mirror, instance.rotate) + offset = [control_logic_offset[0] + pin.center()[0], control_logic_offset[1] + pin.center()[1]] + self.add_layout_pin_rect_center("{0}{1}".format(pin.name,i), storage_layer_name, offset) + + + + def create_netlist(self): """ Netlist creation """ @@ -124,6 +190,8 @@ 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: + self.add_global_pex_labels() self.add_boundary(ll=vector(0, 0), ur=vector(self.width, self.height)) @@ -562,50 +630,4 @@ class sram_base(design, verilog, lef): def lvs_write(self, sp_name): self.sp_write(sp_name, lvs_netlist=True) - - def get_wordline_stage_efforts(self, inp_is_rise=True): - """Get the all the stage efforts for each stage in the path from clk_buf to a wordline""" - stage_effort_list = [] - - # Clk_buf originates from the control logic so only the bank is related to the wordline path - # No loading on the wordline other than in the bank. - external_wordline_cout = 0 - stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout, inp_is_rise) - - return stage_effort_list - - def get_wl_en_cin(self): - """Gets the capacitive load the of clock (clk_buf) for the sram""" - # Only the wordline drivers within the bank use this signal - return self.bank.get_wl_en_cin() - - def get_w_en_cin(self): - """Gets the capacitive load the of write enable (w_en) for the sram""" - # Only the write drivers within the bank use this signal - return self.bank.get_w_en_cin() - - def get_p_en_bar_cin(self): - """Gets the capacitive load the of precharge enable (p_en_bar) for the sram""" - # Only the precharges within the bank use this signal - return self.bank.get_p_en_bar_cin() - - def get_clk_bar_cin(self): - """Gets the capacitive load the of clock (clk_buf_bar) for the sram""" - # As clk_buf_bar is an output of the control logic. The cap for that module is not determined here. - # Only the precharge cells use this signal (other than the control logic) - return self.bank.get_clk_bar_cin() - - def get_sen_cin(self): - """Gets the capacitive load the of sense amp enable for the sram""" - # Only the sense_amps use this signal (other than the control logic) - return self.bank.get_sen_cin() - - def get_dff_clk_buf_cin(self): - """Get the relative capacitance of the clk_buf signal. - Does not get the control logic loading but everything else""" - total_cin = 0 - total_cin += self.row_addr_dff.get_clk_cin() - total_cin += self.data_dff.get_clk_cin() - if self.col_addr_size > 0: - total_cin += self.col_addr_dff.get_clk_cin() - return total_cin + \ No newline at end of file diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 49c7c94a..930316ff 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -109,21 +109,21 @@ def write_magic_script(cell_name, extract=False, final_verification=False): # f.write(pre + "ext2spice hierarchy on\n") # f.write(pre + "ext2spice scale off\n") # lvs exists in 8.2.79, but be backword compatible for now - # f.write(pre + "ext2spice lvs\n") - f.write(pre + "ext2spice hierarchy on\n") - f.write(pre + "ext2spice format ngspice\n") - f.write(pre + "ext2spice cthresh infinite\n") - f.write(pre + "ext2spice rthresh infinite\n") - f.write(pre + "ext2spice renumber off\n") - f.write(pre + "ext2spice scale off\n") - f.write(pre + "ext2spice blackbox on\n") - f.write(pre + "ext2spice subcircuit top auto\n") - f.write(pre + "ext2spice global off\n") + #f.write(pre+"ext2spice lvs\n") + f.write(pre+"ext2spice hierarchy on\n") + f.write(pre+"ext2spice format ngspice\n") + f.write(pre+"ext2spice cthresh infinite\n") + f.write(pre+"ext2spice rthresh infinite\n") + f.write(pre+"ext2spice renumber off\n") + f.write(pre+"ext2spice scale off\n") + f.write(pre+"ext2spice blackbox on\n") + f.write(pre+"ext2spice subcircuit top on\n") + f.write(pre+"ext2spice global off\n") # Can choose hspice, ngspice, or spice3, # but they all seem compatible enough. - #f.write(pre + "ext2spice format ngspice\n") - f.write(pre + "ext2spice {}\n".format(cell_name)) + f.write(pre+"ext2spice format ngspice\n") + f.write(pre+"ext2spice {}\n".format(cell_name)) f.write("quit -noprompt\n") f.write("EOF\n") @@ -413,13 +413,13 @@ def write_script_pex_rule(gds_name,cell_name,output): else: pre = "" f.write(pre+"extract\n".format(cell_name)) - #f.write(pre+"ext2spice hierarchy on\n") - #f.write(pre+"ext2spice format ngspice\n") - #f.write(pre+"ext2spice renumber off\n") - #f.write(pre+"ext2spice scale off\n") - #f.write(pre+"ext2spice blackbox on\n") + f.write(pre+"ext2spice hierarchy off\n") + f.write(pre+"ext2spice format ngspice\n") + f.write(pre+"ext2spice renumber off\n") + f.write(pre+"ext2spice scale off\n") + f.write(pre+"ext2spice blackbox on\n") f.write(pre+"ext2spice subcircuit top on\n") - #f.write(pre+"ext2spice global off\n") + f.write(pre+"ext2spice global off\n") f.write(pre+"ext2spice {}\n".format(cell_name)) f.write("quit -noprompt\n") f.write("eof\n") @@ -442,31 +442,49 @@ def correct_port(name, output_file_name, ref_file_name): pex_file = open(output_file_name, "r") contents = pex_file.read() # locate the start of circuit definition line - match = re.search(".subckt " + str(name) + ".*", contents) + match = re.search(r'^\.subckt+[^M]*', contents, re.MULTILINE) match_index_start = match.start() - pex_file.seek(match_index_start) - rest_text = pex_file.read() - # locate the end of circuit definition line - match = re.search(r'\n', rest_text) - match_index_end = match.start() + match_index_end = match.end() # store the unchanged part of pex file in memory pex_file.seek(0) part1 = pex_file.read(match_index_start) - pex_file.seek(match_index_start + match_index_end) + pex_file.seek(match_index_end) part2 = pex_file.read() + + bitcell_list = "+ " + if OPTS.words_per_row: + for bank in range(OPTS.num_banks): + for bank in range(OPTS.num_banks): + row = int(OPTS.num_words / OPTS.words_per_row) - 1 + col = int(OPTS.word_size * OPTS.words_per_row) - 1 + bitcell_list += "bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col) + bitcell_list += "bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col) + for col in range(OPTS.word_size * OPTS.words_per_row): + for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports): + bitcell_list += "bl{0}_{1} ".format(bank, col) + bitcell_list += "br{0}_{1} ".format(bank, col) + bitcell_list += "\n" + control_list = "+ " + if OPTS.words_per_row: + for bank in range(OPTS.num_banks): + control_list += "bank_{}/s_en0".format(bank) + control_list += '\n' + + part2 = bitcell_list + control_list + part2 + pex_file.close() # obtain the correct definition line from the original spice file sp_file = open(ref_file_name, "r") contents = sp_file.read() - circuit_title = re.search(".SUBCKT " + str(name) + ".*\n", contents) + circuit_title = re.search(".SUBCKT " + str(name) + ".*", contents) circuit_title = circuit_title.group() sp_file.close() # write the new pex file with info in the memory output_file = open(output_file_name, "w") output_file.write(part1) - output_file.write(circuit_title) + output_file.write(circuit_title+'\n') output_file.write(part2) output_file.close() diff --git a/technology/freepdk45/sp_lib/cell_6t.sp b/technology/freepdk45/sp_lib/cell_6t.sp index e1e4936d..b39427d7 100644 --- a/technology/freepdk45/sp_lib/cell_6t.sp +++ b/technology/freepdk45/sp_lib/cell_6t.sp @@ -1,15 +1,15 @@ .SUBCKT cell_6t bl br wl vdd gnd * Inverter 1 -MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n -MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n +MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n * Inverer 2 -MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n -MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n +MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n * Access transistors MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n -MM2 br wl Qbar gnd NMOS_VTG W=135.00n L=50n +MM2 br wl Q_bar gnd NMOS_VTG W=135.00n L=50n .ENDS cell_6t diff --git a/technology/freepdk45/sp_lib/dummy_cell_6t.sp b/technology/freepdk45/sp_lib/dummy_cell_6t.sp index ab862ec5..6e192049 100644 --- a/technology/freepdk45/sp_lib/dummy_cell_6t.sp +++ b/technology/freepdk45/sp_lib/dummy_cell_6t.sp @@ -1,15 +1,15 @@ .SUBCKT dummy_cell_6t bl br wl vdd gnd * Inverter 1 -MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n -MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n +MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n * Inverer 2 -MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n -MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n +MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n * Access transistors MM3 bl_noconn wl Q gnd NMOS_VTG W=135.00n L=50n -MM2 br_noconn wl Qbar gnd NMOS_VTG W=135.00n L=50n +MM2 br_noconn wl Q_bar gnd NMOS_VTG W=135.00n L=50n .ENDS cell_6t diff --git a/technology/scn4m_subm/gds_lib/cell_1w_1r.gds b/technology/scn4m_subm/gds_lib/cell_1w_1r.gds index 782c43dc..06f79ba5 100644 Binary files a/technology/scn4m_subm/gds_lib/cell_1w_1r.gds and b/technology/scn4m_subm/gds_lib/cell_1w_1r.gds differ diff --git a/technology/scn4m_subm/gds_lib/cell_6t.gds b/technology/scn4m_subm/gds_lib/cell_6t.gds index 14d6ab7e..6bfc4431 100644 Binary files a/technology/scn4m_subm/gds_lib/cell_6t.gds and b/technology/scn4m_subm/gds_lib/cell_6t.gds differ diff --git a/technology/scn4m_subm/gds_lib/dff.gds b/technology/scn4m_subm/gds_lib/dff.gds index 7825c3bc..ca6d2b6d 100644 Binary files a/technology/scn4m_subm/gds_lib/dff.gds and b/technology/scn4m_subm/gds_lib/dff.gds differ diff --git a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds index 191f1206..30f5a37e 100644 Binary files a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds and b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds differ diff --git a/technology/scn4m_subm/gds_lib/sense_amp.gds b/technology/scn4m_subm/gds_lib/sense_amp.gds index cf5fa587..5ffbb0d5 100644 Binary files a/technology/scn4m_subm/gds_lib/sense_amp.gds and b/technology/scn4m_subm/gds_lib/sense_amp.gds differ diff --git a/technology/scn4m_subm/gds_lib/tri_gate.gds b/technology/scn4m_subm/gds_lib/tri_gate.gds index ad83f4c6..088e8870 100644 Binary files a/technology/scn4m_subm/gds_lib/tri_gate.gds and b/technology/scn4m_subm/gds_lib/tri_gate.gds differ diff --git a/technology/scn4m_subm/gds_lib/write_driver.gds b/technology/scn4m_subm/gds_lib/write_driver.gds index 8223c795..9e201f24 100644 Binary files a/technology/scn4m_subm/gds_lib/write_driver.gds and b/technology/scn4m_subm/gds_lib/write_driver.gds differ diff --git a/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag index 9aec1c5d..0484df70 100644 --- a/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag +++ b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1542220294 +timestamp 1577066542 << nwell >> rect 0 46 54 75 << pwell >> @@ -47,12 +47,17 @@ rect 17 33 21 37 rect 25 29 29 37 rect 33 33 37 37 rect 41 33 45 37 +rect 9 17 13 21 rect 25 17 29 23 +rect 41 17 45 21 << pdcontact >> rect 17 54 21 58 +rect 25 54 29 58 rect 33 54 37 58 << psubstratepcontact >> rect 25 9 29 13 +<< nsubstratencontact >> +rect 25 68 29 72 << polysilicon >> rect 22 57 24 60 rect 30 57 32 60 @@ -103,19 +108,16 @@ rect 0 2 16 6 rect 20 2 34 6 rect 38 2 54 6 << m2contact >> +rect 25 68 29 72 +rect 25 54 29 58 rect 2 33 6 37 rect 48 33 52 37 rect 16 24 20 28 rect 34 24 38 28 -rect 16 2 20 6 -rect 34 2 38 6 -<< pdm12contact >> -rect 25 54 29 58 -<< ndm12contact >> rect 9 17 13 21 rect 41 17 45 21 -<< nsm12contact >> -rect 25 68 29 72 +rect 16 2 20 6 +rect 34 2 38 6 << metal2 >> rect 2 37 6 72 rect 2 0 6 33 @@ -139,4 +141,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/cell_1w_1r.mag b/technology/scn4m_subm/mag_lib/cell_1w_1r.mag index 9aec1c5d..71a3b1bc 100644 --- a/technology/scn4m_subm/mag_lib/cell_1w_1r.mag +++ b/technology/scn4m_subm/mag_lib/cell_1w_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1542220294 +timestamp 1577067400 << nwell >> rect 0 46 54 75 << pwell >> @@ -47,12 +47,17 @@ rect 17 33 21 37 rect 25 29 29 37 rect 33 33 37 37 rect 41 33 45 37 +rect 9 17 13 21 rect 25 17 29 23 +rect 41 17 45 21 << pdcontact >> rect 17 54 21 58 +rect 25 54 29 58 rect 33 54 37 58 << psubstratepcontact >> rect 25 9 29 13 +<< nsubstratencontact >> +rect 25 68 29 72 << polysilicon >> rect 22 57 24 60 rect 30 57 32 60 @@ -103,19 +108,16 @@ rect 0 2 16 6 rect 20 2 34 6 rect 38 2 54 6 << m2contact >> +rect 25 68 29 72 +rect 25 54 29 58 rect 2 33 6 37 rect 48 33 52 37 rect 16 24 20 28 rect 34 24 38 28 -rect 16 2 20 6 -rect 34 2 38 6 -<< pdm12contact >> -rect 25 54 29 58 -<< ndm12contact >> rect 9 17 13 21 rect 41 17 45 21 -<< nsm12contact >> -rect 25 68 29 72 +rect 16 2 20 6 +rect 34 2 38 6 << metal2 >> rect 2 37 6 72 rect 2 0 6 33 @@ -139,4 +141,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/cell_6t.mag b/technology/scn4m_subm/mag_lib/cell_6t.mag index bb9d943d..e8c16eff 100644 --- a/technology/scn4m_subm/mag_lib/cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1560809302 +timestamp 1577163318 << nwell >> rect -8 35 42 57 << pwell >> @@ -115,4 +115,6 @@ rlabel m2contact 17 52 17 52 5 vdd rlabel metal2 8 49 8 49 1 bl rlabel metal2 26 49 26 49 1 br rlabel metal1 4 13 4 13 1 wl +rlabel polycontact 17 37 17 37 1 Q +rlabel polycontact 23 28 23 28 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag index 60e24aca..1931485f 100644 --- a/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag +++ b/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1562188987 +timestamp 1577067400 << nwell >> rect 0 46 54 75 << pwell >> @@ -133,4 +133,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag b/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag index 03e49f03..1931485f 100644 --- a/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag +++ b/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1562189027 +timestamp 1577067400 << nwell >> rect 0 46 54 75 << pwell >> @@ -133,4 +133,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag b/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag index 74562f15..515ef682 100644 --- a/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1560809362 +timestamp 1577067400 << nwell >> rect -8 35 42 57 << pwell >> @@ -112,4 +112,6 @@ rlabel m2contact 17 52 17 52 5 vdd rlabel metal2 8 49 8 49 1 bl rlabel metal2 26 49 26 49 1 br rlabel metal1 4 13 4 13 1 wl +rlabel polycontact 17 37 17 37 1 Q +rlabel polycontact 23 28 23 28 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag index f215ff04..982e1630 100644 --- a/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag +++ b/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1542221056 +timestamp 1577067400 << nwell >> rect 0 46 54 75 << pwell >> @@ -142,4 +142,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag b/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag index f215ff04..61add325 100644 --- a/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag +++ b/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1542221056 +timestamp 1577067446 << nwell >> rect 0 46 54 75 << pwell >> @@ -142,4 +142,6 @@ rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel polycontact 29 49 29 49 1 Q +rlabel polycontact 25 42 25 42 1 Q_bar << end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag index b5a5f7b8..61a7646e 100644 --- a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1560809329 +timestamp 1577067503 << nwell >> rect -8 35 42 57 << pwell >> @@ -116,4 +116,6 @@ rlabel m2contact 17 52 17 52 5 vdd rlabel metal2 8 49 8 49 1 bl rlabel metal2 26 49 26 49 1 br rlabel metal1 4 13 4 13 1 wl +rlabel polycontact 17 37 17 37 1 Q +rlabel polycontact 23 28 23 28 1 Q_bar << end >> diff --git a/technology/scn4m_subm/sp_lib/cell_6t.sp b/technology/scn4m_subm/sp_lib/cell_6t.sp index bb430893..dc724007 100644 --- a/technology/scn4m_subm/sp_lib/cell_6t.sp +++ b/technology/scn4m_subm/sp_lib/cell_6t.sp @@ -4,15 +4,15 @@ * SPICE3 file created from cell_6t.ext - technology: scmos * Inverter 1 -M1000 Q Qbar vdd vdd p w=0.6u l=0.8u -M1002 Q Qbar gnd gnd n w=1.6u l=0.4u +M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u +M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u * Inverter 2 -M1001 vdd Q Qbar vdd p w=0.6u l=0.8u -M1003 gnd Q Qbar gnd n w=1.6u l=0.4u +M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u +M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u * Access transistors M1004 Q wl bl gnd n w=0.8u l=0.4u -M1005 Qbar wl br gnd n w=0.8u l=0.4u +M1005 Q_bar wl br gnd n w=0.8u l=0.4u .ENDS diff --git a/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp b/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp index 3b0584df..c5b6ff9d 100644 --- a/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp +++ b/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp @@ -3,15 +3,15 @@ .SUBCKT dummy_cell_6t bl br wl vdd gnd * Inverter 1 -M1000 Q Qbar vdd vdd p w=0.6u l=0.8u -M1002 Q Qbar gnd gnd n w=1.6u l=0.4u +M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u +M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u * Inverter 2 -M1001 vdd Q Qbar vdd p w=0.6u l=0.8u -M1003 gnd Q Qbar gnd n w=1.6u l=0.4u +M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u +M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u * Access transistors M1004 Q wl bl_noconn gnd n w=0.8u l=0.4u -M1005 Qbar wl br_noconn gnd n w=0.8u l=0.4u +M1005 Q_bar wl br_noconn gnd n w=0.8u l=0.4u .ENDS