diff --git a/compiler/base/design.py b/compiler/base/design.py index 94fd407f..329883c0 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -45,7 +45,7 @@ class design(hierarchy_design): "Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, self.pins)) self.add_pin_indices(prop.port_indices) self.add_pin_names(prop.port_map) - self.add_pin_types(prop.port_types) + self.update_pin_types(prop.port_types) (width, height) = utils.get_libcell_size(self.cell_name, diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 64dc0b2b..90c65109 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -17,6 +17,7 @@ from .delay_data import delay_data from .wire_spice_model import wire_spice_model from .power_data import power_data from .logical_effort import convert_relative_c_to_farad, convert_farad_to_relative_c +from .pin_spice import pin_spice class spice(): @@ -49,14 +50,10 @@ class spice(): if not os.path.exists(self.lvs_file): self.lvs_file = self.sp_file - self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"] # Holds subckts/mods for this module self.mods = set() # Holds the pins for this module (in order) self.pins = [] - # The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND - # for each instance, this is the set of nets/nodes that map to the pins for this instance - self.pin_type = {} # An (optional) list of indices to reorder the pins to match the spice. self.pin_indices = [] # THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the @@ -90,106 +87,97 @@ class spice(): def add_pin(self, name, pin_type="INOUT"): """ Adds a pin to the pins list. Default type is INOUT signal. """ - self.pins.append(name) - self.pin_type[name]=pin_type - debug.check(pin_type in self.valid_signal_types, - "Invalid signaltype for {0}: {1}".format(name, - pin_type)) + new_pin = pin_spice(name, pin_type) + debug.check(new_pin not in self.pins, "cannot add duplicate spice pin") + self.pins.append(new_pin) def add_pin_list(self, pin_list, pin_type="INOUT"): """ Adds a pin_list to the pins list """ - # The type list can be a single type for all pins + # The pin type list can be a single type for all pins # or a list that is the same length as the pin list. - if type(pin_type)==str: + if type(pin_type) == str: for pin in pin_list: - debug.check(pin_type in self.valid_signal_types, - "Invalid signaltype for {0}: {1}".format(pin, - pin_type)) self.add_pin(pin, pin_type) elif len(pin_type)==len(pin_list): - for (pin, ptype) in zip(pin_list, pin_type): - debug.check(ptype in self.valid_signal_types, - "Invalid signaltype for {0}: {1}".format(pin, - ptype)) - self.add_pin(pin, ptype) + for (pin, type) in zip(pin_list, pin_type): + self.add_pin(pin, type) else: - debug.error("Mismatch in type and pin list lengths.", -1) + debug.error("Pin type must be a string or list of strings the same length as pin_list", -1) def add_pin_indices(self, index_list): - """ - Add pin indices for all the cell's pins. - """ + """ Add pin indices for all the cell's pins. """ self.pin_indices = index_list def get_ordered_inputs(self, input_list): - """ - Return the inputs reordered to match the pins. - """ + """ Return the inputs reordered to match the pins. """ if not self.pin_indices: return input_list new_list = [input_list[x] for x in self.pin_indices] return new_list - def add_pin_types(self, type_list): - """ - Add pin types for all the cell's pins. - """ - # This only works if self.pins == bitcell.pin_names + def update_pin_types(self, type_list): + """ Change pin types for all the cell's pins. """ if len(type_list) != len(self.pins): debug.error("{} spice subcircuit number of port types does not match number of pins\ \n SPICE names={}\ \n Module names={}\ ".format(self.name, self.pins, type_list), 1) - self.pin_type = {pin: type for pin, type in zip(self.pins, type_list)} + for pin, type in zip(self.pins, type_list): + pin.set_pin_type(type) def get_pin_type(self, name): """ Returns the type of the signal pin. """ - pin_type = self.pin_type[name] - debug.check(pin_type in self.valid_signal_types, - "Invalid signaltype for {0}: {1}".format(name, pin_type)) - return pin_type + for pin in self.pins: + if pin.name == name: + return pin.type + debug.error("Spice pin {} not found".format(name)) def get_pin_dir(self, name): """ Returns the direction of the pin. (Supply/ground are INOUT). """ - if self.pin_type[name] in ["POWER", "GROUND"]: + pin_type = self.get_pin_type(name) + if pin_type in ["POWER", "GROUND"]: return "INOUT" else: - return self.pin_type[name] + return pin_type def get_inputs(self): - """ These use pin types to determine pin lists. These - may be over-ridden by submodules that didn't use pin directions yet.""" + """ + These use pin types to determine pin lists. + Returns names only to maintain historical interface. + """ input_list = [] for pin in self.pins: - if self.pin_type[pin]=="INPUT": - input_list.append(pin) + if pin.type == "INPUT": + input_list.append(pin.name) return input_list def get_outputs(self): - """ These use pin types to determine pin lists. These - may be over-ridden by submodules that didn't use pin directions yet.""" + """ + These use pin types to determine pin lists. + Returns names only to maintain historical interface. + """ output_list = [] for pin in self.pins: - if self.pin_type[pin]=="OUTPUT": - output_list.append(pin) + if pin.type == "OUTPUT": + output_list.append(pin.name) return output_list - def copy_pins(self, other_module, suffix=""): - """ This will copy all of the pins from the other module and add an optional suffix.""" - for pin in other_module.pins: - self.add_pin(pin + suffix, other_module.get_pin_type(pin)) - def get_inouts(self): """ These use pin types to determine pin lists. These may be over-ridden by submodules that didn't use pin directions yet.""" inout_list = [] for pin in self.pins: - if self.pin_type[pin]=="INOUT": - inout_list.append(pin) + if pin.type == "INOUT": + inout_list.append(pin.name) return inout_list + def copy_pins(self, other_module, suffix=""): + """ This will copy all of the pins from the other module and add an optional suffix.""" + for pin in other_module.pins: + self.add_pin(pin.name + suffix, pin.type) + def connect_inst(self, args, check=True): """ Connects the pins of the last instance added diff --git a/compiler/base/lef.py b/compiler/base/lef.py index b138799b..e08f0efc 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -18,7 +18,7 @@ class lef: """ SRAM LEF Class open GDS file, read pins information, obstruction and write them to LEF file. - This is inherited by the sram_base class. + This is inherited by the sram_1bank class. """ def __init__(self, layers): # LEF db units per micron diff --git a/compiler/base/pin_spice.py b/compiler/base/pin_spice.py new file mode 100644 index 00000000..1754550e --- /dev/null +++ b/compiler/base/pin_spice.py @@ -0,0 +1,43 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from openram import debug + + +class pin_spice: + """ + A class to represent a spice netlist pin. + """ + + valid_pin_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + def __init__(self, name, type): + self.name = name + self.set_type(type) + + self._hash = hash(self.name) + + def set_type(self, type): + debug.check(type in pin_spice.valid_pin_types, + "Invalid pin type for {0}: {1}".format(self.name, type)) + self.type = type + + def __str__(self): + """ override print function output """ + return "(pin_name={} type={})".format(self.name, self.type) + + def __repr__(self): + """ override repr function output """ + return self.name + + def __hash__(self): + """ + Implement the hash function for sets etc. + Only hash name since spice does not allow two pins to share a name. + Provides a speedup if pin_spice is used as a key for dicts. + """ + return self._hash