Merge branch 'STA-refactor' into dev

This commit is contained in:
Sam Crow 2023-07-19 15:14:20 -07:00
commit 2051f54f70
9 changed files with 321 additions and 167 deletions

View File

@ -66,8 +66,6 @@ class contact(hierarchy_design):
self.offset = vector(0, 0) self.offset = vector(0, 0)
self.implant_type = implant_type self.implant_type = implant_type
self.well_type = well_type self.well_type = well_type
# Module does not have pins, but has empty pin list.
self.pins = []
self.create_layout() self.create_layout()
def create_layout(self): def create_layout(self):

View File

@ -41,17 +41,17 @@ class design(hierarchy_design):
if prop and prop.hard_cell: if prop and prop.hard_cell:
# The pins get added from the spice file, so just check # The pins get added from the spice file, so just check
# that they matched here # that they matched here
debug.check(prop.port_names == self.pins, debug.check(prop.port_names == list(self.pins),
"Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, self.pins)) "Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, list(self.pins)))
self.add_pin_indices(prop.port_indices) self.add_pin_indices(prop.port_indices)
self.add_pin_names(prop.port_map) 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, (width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"], GDS["unit"],
layer[prop.boundary_layer]) layer[prop.boundary_layer])
self.pin_map = utils.get_libcell_pins(self.pins, self.pin_map = utils.get_libcell_pins(list(self.pins),
self.cell_name, self.cell_name,
GDS["unit"]) GDS["unit"])

View File

@ -161,8 +161,8 @@ class geometry:
class instance(geometry): class instance(geometry):
""" """
An instance of an instance/module with a specified location and An instance of a module with a specified location, rotation,
rotation spice pins, and spice nets
""" """
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
"""Initializes an instance to represent a module""" """Initializes an instance to represent a module"""
@ -176,6 +176,18 @@ class instance(geometry):
self.rotate = rotate self.rotate = rotate
self.offset = vector(offset).snap_to_grid() self.offset = vector(offset).snap_to_grid()
self.mirror = mirror self.mirror = mirror
# track if the instance's spice pin connections have been made
self.connected = False
# deepcopy because this instance needs to
# change attributes in these spice objects
self.spice_pins = copy.deepcopy(self.mod.pins)
self.spice_nets = copy.deepcopy(self.mod.nets)
for pin in self.spice_pins.values():
pin.set_inst(self)
for net in self.spice_nets.values():
net.set_inst(self)
if OPTS.netlist_only: if OPTS.netlist_only:
self.width = 0 self.width = 0
self.height = 0 self.height = 0
@ -274,6 +286,34 @@ class instance(geometry):
new_pins.append(p) new_pins.append(p)
return new_pins return new_pins
def connect_spice_pins(self, nets_list):
"""
add the connection between instance pins and module nets
to both of their respective objects
nets_list must be the same length as self.spice_pins
"""
if len(nets_list) == 0 and len(self.spice_pins) == 0:
# this is the only valid case to skip the following debug check
# because this with no pins are often connected arbitrarily
self.connected = True
return
debug.check(not self.connected,
"instance {} has already been connected".format(self.name))
debug.check(len(self.spice_pins) == len(nets_list),
"must provide list of nets the same length as pin list\
when connecting an instance")
for pin in self.spice_pins.values():
net = nets_list.pop(0)
pin.set_inst_net(net)
net.connect_pin(pin)
self.connected = True
def get_connections(self):
conns = []
for pin in self.spice_pins.values():
conns.append(pin.inst_net.name)
return conns
def calculate_transform(self, node): def calculate_transform(self, node):
#set up the rotation matrix #set up the rotation matrix
angle = math.radians(float(node.rotate)) angle = math.radians(float(node.rotate))

View File

@ -135,11 +135,11 @@ class hierarchy_design(spice, layout):
# Translate port names to external nets # Translate port names to external nets
if len(port_nets) != len(self.pins): if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
self.pins), list(self.pins)),
1) 1)
port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} port_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)}
debug.info(3, "Instance name={}".format(inst_name)) debug.info(3, "Instance name={}".format(inst_name))
for subinst, conns in zip(self.insts, self.conns): for subinst, conns in zip(self.insts, self.get_instance_connections()):
if subinst in self.graph_inst_exclude: if subinst in self.graph_inst_exclude:
continue continue
subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name
@ -153,11 +153,11 @@ class hierarchy_design(spice, layout):
# Translate port names to external nets # Translate port names to external nets
if len(port_nets) != len(self.pins): if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
self.pins), list(self.pins)),
1) 1)
port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} port_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)}
debug.info(3, "Instance name={}".format(inst_name)) debug.info(3, "Instance name={}".format(inst_name))
for subinst, conns in zip(self.insts, self.conns): for subinst, conns in zip(self.insts, self.get_instance_connections()):
subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name
subinst_ports = self.translate_nets(conns, port_dict, inst_name) subinst_ports = self.translate_nets(conns, port_dict, inst_name)
for si_port, conn in zip(subinst_ports, conns): for si_port, conn in zip(subinst_ports, conns):
@ -186,7 +186,7 @@ class hierarchy_design(spice, layout):
""" """
# The final pin names will depend on the spice hierarchy, so # The final pin names will depend on the spice hierarchy, so
# they are passed as an input. # they are passed as an input.
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} pin_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)}
input_pins = self.get_inputs() input_pins = self.get_inputs()
output_pins = self.get_outputs() output_pins = self.get_outputs()
inout_pins = self.get_inouts() inout_pins = self.get_inouts()
@ -197,7 +197,7 @@ class hierarchy_design(spice, layout):
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
pins = ",".join(self.pins) pins = ",".join(list(self.pins))
insts = [" {}".format(x) for x in self.insts] insts = [" {}".format(x) for x in self.insts]
objs = [" {}".format(x) for x in self.objs] objs = [" {}".format(x) for x in self.objs]
s = "********** design {0} **********".format(self.cell_name) s = "********** design {0} **********".format(self.cell_name)
@ -208,7 +208,7 @@ class hierarchy_design(spice, layout):
def __repr__(self): def __repr__(self):
""" override print function output """ """ override print function output """
text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n" text="( design: " + self.name + " pins=" + str(list(self.pins)) + " " + str(self.width) + "x" + str(self.height) + " )\n"
for i in self.objs: for i in self.objs:
text+=str(i) + ",\n" text+=str(i) + ",\n"
for i in self.insts: for i in self.insts:

View File

@ -629,7 +629,7 @@ class layout():
""" """
Return a pin list of all pins Return a pin list of all pins
""" """
return self.pins return list(self.pins)
def copy_layout_pin(self, instance, pin_name, new_name="", relative_offset=vector(0, 0)): def copy_layout_pin(self, instance, pin_name, new_name="", relative_offset=vector(0, 0)):
""" """
@ -1523,6 +1523,7 @@ class layout():
""" Return the pin shapes as blockages for non-top-level blocks. """ """ Return the pin shapes as blockages for non-top-level blocks. """
# FIXME: We don't have a body contact in ptx, so just ignore it for now # FIXME: We don't have a body contact in ptx, so just ignore it for now
import copy import copy
# FIXME: this may not work now that self.pins is a dict as defined in hierarchy_spice
pin_names = copy.deepcopy(self.pins) pin_names = copy.deepcopy(self.pins)
if self.name.startswith("pmos") or self.name.startswith("nmos"): if self.name.startswith("pmos") or self.name.startswith("nmos"):
pin_names.remove("B") pin_names.remove("B")

View File

@ -13,6 +13,7 @@ from pprint import pformat
from openram import debug from openram import debug
from openram import tech from openram import tech
from openram import OPTS from openram import OPTS
from collections import OrderedDict
from .delay_data import delay_data from .delay_data import delay_data
from .wire_spice_model import wire_spice_model from .wire_spice_model import wire_spice_model
from .power_data import power_data from .power_data import power_data
@ -49,20 +50,18 @@ class spice():
if not os.path.exists(self.lvs_file): if not os.path.exists(self.lvs_file):
self.lvs_file = self.sp_file self.lvs_file = self.sp_file
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"]
# Holds subckts/mods for this module # Holds subckts/mods for this module
self.mods = set() self.mods = set()
# Holds the pins for this module (in order) # Holds the pins for this module (in order)
self.pins = [] # on Python3.7+ regular dictionaries guarantee order too, but we allow use of v3.5+
# The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND self.pins = OrderedDict()
# 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. # An (optional) list of indices to reorder the pins to match the spice.
self.pin_indices = [] self.pin_indices = []
# THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the # THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the
# Spice format) # Spice format)
self.conns = [] # internal nets, which may or may not be connected to pins of the same name
# If this is set, it will out output subckt or isntances of this (for row/col caps etc.) self.nets = {}
# If this is set, it will not output subckt or instances of this (for row/col caps etc.)
self.no_instances = False self.no_instances = False
# If we are doing a trimmed netlist, these are the instance that will be filtered # If we are doing a trimmed netlist, these are the instance that will be filtered
self.trim_insts = set() self.trim_insts = set()
@ -90,128 +89,114 @@ class spice():
def add_pin(self, name, pin_type="INOUT"): def add_pin(self, name, pin_type="INOUT"):
""" Adds a pin to the pins list. Default type is INOUT signal. """ """ Adds a pin to the pins list. Default type is INOUT signal. """
self.pins.append(name) debug.check(name not in self.pins, "cannot add duplicate spice pin {}".format(name))
self.pin_type[name]=pin_type self.pins[name] = pin_spice(name, pin_type, self)
debug.check(pin_type in self.valid_signal_types,
"Invalid signaltype for {0}: {1}".format(name,
pin_type))
def add_pin_list(self, pin_list, pin_type="INOUT"): def add_pin_list(self, pin_list, pin_type="INOUT"):
""" Adds a pin_list to the pins list """ """ 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. # or a list that is the same length as the pin list.
if type(pin_type)==str: if isinstance(pin_type, str):
for pin in pin_list: 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) self.add_pin(pin, pin_type)
elif len(pin_type)==len(pin_list): elif len(pin_type)==len(pin_list):
for (pin, ptype) in zip(pin_list, pin_type): for (pin, type) in zip(pin_list, pin_type):
debug.check(ptype in self.valid_signal_types, self.add_pin(pin, type)
"Invalid signaltype for {0}: {1}".format(pin,
ptype))
self.add_pin(pin, ptype)
else: 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): 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 self.pin_indices = index_list
def get_ordered_inputs(self, input_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: if not self.pin_indices:
return input_list return input_list
new_list = [input_list[x] for x in self.pin_indices] new_list = [input_list[x] for x in self.pin_indices]
return new_list return new_list
def add_pin_types(self, type_list): def update_pin_types(self, type_list):
""" """ Change pin types for all the cell's pins. """
Add pin types for all the cell's pins. debug.check(len(type_list) == len(self.pins),
""" "{} spice subcircuit number of port types does not match number of pins\
# This only works if self.pins == bitcell.pin_names \n pin names={}\n port types={}".format(self.name, list(self.pins), type_list))
if len(type_list) != len(self.pins): for pin, type in zip(self.pins.values(), type_list):
debug.error("{} spice subcircuit number of port types does not match number of pins\ pin.set_type(type)
\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)}
def get_pin_type(self, name): def get_pin_type(self, name):
""" Returns the type of the signal pin. """ """ Returns the type of the signal pin. """
pin_type = self.pin_type[name] pin = self.pins.get(name)
debug.check(pin_type in self.valid_signal_types, debug.check(pin is not None, "Spice pin {} not found".format(name))
"Invalid signaltype for {0}: {1}".format(name, pin_type)) return pin.type
return pin_type
def get_pin_dir(self, name): def get_pin_dir(self, name):
""" Returns the direction of the pin. (Supply/ground are INOUT). """ """ 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" return "INOUT"
else: else:
return self.pin_type[name] return pin_type
def get_inputs(self): 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 = [] input_list = []
for pin in self.pins: for pin in self.pins.values():
if self.pin_type[pin]=="INPUT": if pin.type == "INPUT":
input_list.append(pin) input_list.append(pin.name)
return input_list return input_list
def get_outputs(self): 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 = [] output_list = []
for pin in self.pins: for pin in self.pins.values():
if self.pin_type[pin]=="OUTPUT": if pin.type == "OUTPUT":
output_list.append(pin) output_list.append(pin.name)
return output_list return output_list
def get_inouts(self):
"""
These use pin types to determine pin lists.
Returns names only, to maintain historical interface.
"""
inout_list = []
for pin in self.pins.values():
if pin.type == "INOUT":
inout_list.append(pin.name)
return inout_list
def copy_pins(self, other_module, suffix=""): def copy_pins(self, other_module, suffix=""):
""" This will copy all of the pins from the other module and add an optional suffix.""" """ This will copy all of the pins from the other module and add an optional suffix."""
for pin in other_module.pins: for pin in other_module.pins.values():
self.add_pin(pin + suffix, other_module.get_pin_type(pin)) self.add_pin(pin.name + suffix, pin.type)
def get_inouts(self): def connect_inst(self, args):
""" 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)
return inout_list
def connect_inst(self, args, check=True):
""" """
Connects the pins of the last instance added Connects the pins of the last instance added
It is preferred to use the function with the check to find if
there is a problem. The check option can be set to false
where we dynamically generate groups of connections after a
group of modules are generated.
""" """
num_pins = len(self.insts[-1].mod.pins) spice_pins = list(self.insts[-1].spice_pins)
num_pins = len(spice_pins)
num_args = len(args) num_args = len(args)
# Order the arguments if the hard cell has a custom port order # Order the arguments if the hard cell has a custom port order
ordered_args = self.get_ordered_inputs(args) ordered_args = self.get_ordered_inputs(args)
if (check and num_pins != num_args): if (num_pins != num_args):
if num_pins < num_args: if num_pins < num_args:
mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins) mod_pins = spice_pins + [""] * (num_args - num_pins)
arg_pins = ordered_args arg_pins = ordered_args
else: else:
arg_pins = ordered_args + [""] * (num_pins - num_args) arg_pins = ordered_args + [""] * (num_pins - num_args)
mod_pins = self.insts[-1].mod.pins mod_pins = spice_pins
modpins_string = "\n".join(["{0} -> {1}".format(arg, mod) for (arg, mod) in zip(arg_pins, mod_pins)]) modpins_string = "\n".join(["{0} -> {1}".format(arg, mod) for (arg, mod) in zip(arg_pins, mod_pins)])
debug.error("Connection mismatch:\nInst ({0}) -> Mod ({1})\n{2}".format(num_args, debug.error("Connection mismatch:\nInst ({0}) -> Mod ({1})\n{2}".format(num_args,
@ -219,27 +204,17 @@ class spice():
modpins_string), modpins_string),
1) 1)
self.conns.append(ordered_args) ordered_nets = self.create_nets(ordered_args)
self.insts[-1].connect_spice_pins(ordered_nets)
# This checks if we don't have enough instance port connections for the number of insts def create_nets(self, names_list):
if check and (len(self.insts)!=len(self.conns)): nets = []
insts_string=pformat(self.insts) for name in names_list:
conns_string=pformat(self.conns) # setdefault adds to the dict if it doesn't find the net in it already
# then it returns the net it found or created, a net_spice object
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, net = self.nets.setdefault(name, net_spice(name, self))
len(self.insts), nets.append(net)
len(self.conns))) return nets
debug.error("Instances: \n" + str(insts_string))
debug.error("-----")
debug.error("Connections: \n" + str(conns_string), 1)
def get_conns(self, inst):
"""Returns the connections of a given instance."""
for i in range(len(self.insts)):
if inst is self.insts[i]:
return self.conns[i]
# If not found, returns None
return None
def sp_read(self): def sp_read(self):
""" """
@ -258,7 +233,7 @@ class spice():
subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE) subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE)
subckt_line = list(filter(subckt.search, self.spice))[0] subckt_line = list(filter(subckt.search, self.spice))[0]
# parses line into ports and remove subckt # parses line into ports and remove subckt
self.pins = subckt_line.split(" ")[2:] self.add_pin_list(subckt_line.split(" ")[2:])
else: else:
debug.info(4, "no spfile {0}".format(self.sp_file)) debug.info(4, "no spfile {0}".format(self.sp_file))
self.spice = [] self.spice = []
@ -279,10 +254,10 @@ class spice():
subckt_line = list(filter(subckt.search, self.lvs))[0] subckt_line = list(filter(subckt.search, self.lvs))[0]
# parses line into ports and remove subckt # parses line into ports and remove subckt
lvs_pins = subckt_line.split(" ")[2:] lvs_pins = subckt_line.split(" ")[2:]
debug.check(lvs_pins == self.pins, debug.check(lvs_pins == list(self.pins),
"Spice netlists for LVS and simulation have port mismatches:\n{0} (LVS {1})\nvs\n{2} (sim {3})".format(lvs_pins, "Spice netlists for LVS and simulation have port mismatches:\n{0} (LVS {1})\nvs\n{2} (sim {3})".format(lvs_pins,
self.lvs_file, self.lvs_file,
self.pins, list(self.pins),
self.sp_file)) self.sp_file))
def check_net_in_spice(self, net_name): def check_net_in_spice(self, net_name):
@ -327,84 +302,72 @@ class spice():
# If spice isn't defined, we dynamically generate one. # If spice isn't defined, we dynamically generate one.
# recursively write the modules # recursively write the modules
for i in self.mods: for mod in self.mods:
if self.contains(i, usedMODS): if self.contains(mod, usedMODS):
continue continue
usedMODS.append(i) usedMODS.append(mod)
i.sp_write_file(sp, usedMODS, lvs, trim) mod.sp_write_file(sp, usedMODS, lvs, trim)
if len(self.insts) == 0: if len(self.insts) == 0:
return return
if self.pins == []: if len(self.pins) == 0:
return return
# write out the first spice line (the subcircuit) # write out the first spice line (the subcircuit)
wrapped_pins = "\n+ ".join(tr.wrap(" ".join(self.pins))) wrapped_pins = "\n+ ".join(tr.wrap(" ".join(list(self.pins))))
sp.write("\n.SUBCKT {0}\n+ {1}\n".format(self.cell_name, sp.write("\n.SUBCKT {0}\n+ {1}\n".format(self.cell_name,
wrapped_pins)) wrapped_pins))
# write a PININFO line
if False:
pin_info = "*.PININFO"
for pin in self.pins:
if self.pin_type[pin] == "INPUT":
pin_info += " {0}:I".format(pin)
elif self.pin_type[pin] == "OUTPUT":
pin_info += " {0}:O".format(pin)
else:
pin_info += " {0}:B".format(pin)
sp.write(pin_info + "\n")
# Also write pins as comments # Also write pins as comments
for pin in self.pins: for pin in self.pins.values():
sp.write("* {1:6}: {0} \n".format(pin, self.pin_type[pin])) sp.write("* {1:6}: {0} \n".format(pin.name, pin.type))
for line in self.comments: for line in self.comments:
sp.write("* {}\n".format(line)) sp.write("* {}\n".format(line))
# every instance must have a set of connections, even if it is empty. # every instance must be connected with the connect_inst function
if len(self.insts) != len(self.conns): # TODO: may run into empty pin lists edge case, not sure yet
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.cell_name, connected = True
len(self.insts), for inst in self.insts:
len(self.conns))) if inst.connected:
debug.error("Instances: \n" + str(self.insts)) continue
debug.error("-----") debug.error("Instance {} spice pins not connected".format(str(inst)))
debug.error("Connections: \n" + str(self.conns), 1) connected = False
debug.check(connected, "{0} : Not all instance spice pins are connected.".format(self.cell_name))
for i in range(len(self.insts)): for inst in self.insts:
# we don't need to output connections of empty instances. # we don't need to output connections of empty instances.
# these are wires and paths # these are wires and paths
if self.conns[i] == []: if len(inst.spice_pins) == 0:
continue continue
# Instance with no devices in it needs no subckt/instance # Instance with no devices in it needs no subckt/instance
if self.insts[i].mod.no_instances: if inst.mod.no_instances:
continue continue
# If this is a trimmed netlist, skip it by adding comment char # If this is a trimmed netlist, skip it by adding comment char
if trim and self.insts[i].name in self.trim_insts: if trim and inst.name in self.trim_insts:
sp.write("* ") sp.write("* ")
if lvs and hasattr(self.insts[i].mod, "lvs_device"): if lvs and hasattr(inst.mod, "lvs_device"):
sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name, sp.write(inst.mod.lvs_device.format(inst.name,
" ".join(self.conns[i]))) " ".join(inst.get_connections())))
sp.write("\n") sp.write("\n")
elif hasattr(self.insts[i].mod, "spice_device"): elif hasattr(inst.mod, "spice_device"):
sp.write(self.insts[i].mod.spice_device.format(self.insts[i].name, sp.write(inst.mod.spice_device.format(inst.name,
" ".join(self.conns[i]))) " ".join(inst.get_connections())))
sp.write("\n") sp.write("\n")
else: else:
if trim and self.insts[i].name in self.trim_insts: if trim and inst.name in self.trim_insts:
wrapped_connections = "\n*+ ".join(tr.wrap(" ".join(self.conns[i]))) wrapped_connections = "\n*+ ".join(tr.wrap(" ".join(inst.get_connections())))
sp.write("X{0}\n*+ {1}\n*+ {2}\n".format(self.insts[i].name, sp.write("X{0}\n*+ {1}\n*+ {2}\n".format(inst.name,
wrapped_connections, wrapped_connections,
self.insts[i].mod.cell_name)) inst.mod.cell_name))
else: else:
wrapped_connections = "\n+ ".join(tr.wrap(" ".join(self.conns[i]))) wrapped_connections = "\n+ ".join(tr.wrap(" ".join(inst.get_connections())))
sp.write("X{0}\n+ {1}\n+ {2}\n".format(self.insts[i].name, sp.write("X{0}\n+ {1}\n+ {2}\n".format(inst.name,
wrapped_connections, wrapped_connections,
self.insts[i].mod.cell_name)) inst.mod.cell_name))
sp.write(".ENDS {0}\n".format(self.cell_name)) sp.write(".ENDS {0}\n".format(self.cell_name))
@ -733,6 +696,12 @@ class spice():
aliases.append(net) aliases.append(net)
return aliases return aliases
def get_instance_connections(self):
conns = []
for inst in self.insts:
conns.append(inst.get_connections())
return conns
def is_net_alias(self, known_net, net_alias, mod, exclusion_set): 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). Checks if the alias_net in input mod is the same as the input net for this mod (self).
@ -745,7 +714,7 @@ class spice():
return True return True
# Check connections of all other subinsts # Check connections of all other subinsts
mod_set = set() mod_set = set()
for subinst, inst_conns in zip(self.insts, self.conns): for subinst, inst_conns in zip(self.insts, self.get_instance_connections()):
for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins): 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): if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod):
return True return True
@ -762,3 +731,149 @@ class spice():
return self == mod and \ return self == mod and \
child_net.lower() == alias_net.lower() and \ child_net.lower() == alias_net.lower() and \
parent_net.lower() == alias_net.lower() parent_net.lower() == alias_net.lower()
class pin_spice():
"""
A class to represent a spice netlist pin.
mod is the parent module that created this pin.
mod_net is the net object of this pin's parent module. It must have the same name as the pin.
inst is the instance this pin is a part of, if any.
inst_net is the net object from mod's nets which connects to this pin.
"""
valid_pin_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND", "BIAS"]
def __init__(self, name, type, mod):
self.name = name
self.set_type(type)
self.mod = mod
self.mod_net = None
self.inst = None
self.inst_net = None
# TODO: evaluate if this makes sense... and works
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 set_mod_net(self, net):
debug.check(isinstance(net, net_spice), "net must be a net_spice object")
debug.check(net.name == self.name, "module spice net must have same name as spice pin")
self.mod_net = net
def set_inst(self, inst):
self.inst = inst
def set_inst_net(self, net):
if self.inst_net is not None:
debug.error("pin {} is already connected to net {}\
so it cannot also be connected to net {}\
".format(self.name, self.inst_net.name, net.name), 1)
debug.check(isinstance(net, net_spice), "net must be a net_spice object")
self.inst_net = net
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 __eq__(self, name):
return (name == self.name) if isinstance(name, str) else super().__eq__(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
def __deepcopy__(original, memo):
"""
This function is defined so that instances of modules can make deep
copies of their parent module's pins dictionary. It is only expected
to be called by the instance class __init__ function. Mod and mod_net
should not be deep copies but references to the existing mod and net
objects they refer to in the original. If inst is already defined this
function will throw an error because that means it was called on a pin
from an instance, which is not defined behavior.
"""
debug.check(original.inst is None,
"cannot make a deepcopy of a spice pin from an inst")
pin = pin_spice(original.name, original.type, original.mod)
if original.mod_net is not None:
pin.set_mod_net(original.mod_net)
return pin
class net_spice():
"""
A class to represent a spice net.
mod is the parent module that created this net.
pins are all the pins connected to this net.
inst is the instance this net is a part of, if any.
"""
def __init__(self, name, mod):
self.name = name
self.pins = []
self.mod = mod
self.inst = None
# TODO: evaluate if this makes sense... and works
self._hash = hash(self.name)
def connect_pin(self, pin):
debug.check(isinstance(pin, pin_spice), "pin must be a pin_spice object")
if pin in self.pins:
debug.warning("pin {} was already connected to net {} ... why was it connected again?".format(pin.name, self.name))
else:
self.pins.append(pin)
def set_inst(self, inst):
self.inst = inst
def __str__(self):
""" override print function output """
return "(net_name={} type={})".format(self.name, self.type)
def __repr__(self):
""" override repr function output """
return self.name
def __eq__(self, name):
return (name == self.name) if isinstance(name, str) else super().__eq__(name)
def __hash__(self):
"""
Implement the hash function for sets etc.
Only hash name since spice does not allow two nets to share a name
(on the same level of hierarchy, or rather they will be the same net).
Provides a speedup if net_spice is used as a key for dicts.
"""
return self._hash
def __deepcopy__(original, memo):
"""
This function is defined so that instances of modules can make deep
copies of their parent module's nets dictionary. It is only expected
to be called by the instance class __init__ function. Mod
should not be a deep copy but a reference to the existing mod
object it refers to in the original. If inst is already defined this
function will throw an error because that means it was called on a net
from an instance, which is not defined behavior.
"""
debug.check(original.inst is None,
"cannot make a deepcopy of a spice net from an inst")
net = net_spice(original.name, original.mod)
if original.pins != []:
# TODO: honestly I'm not sure if this is right but we'll see...
net.pins = original.pins
return net

View File

@ -18,7 +18,7 @@ class lef:
""" """
SRAM LEF Class open GDS file, read pins information, obstruction SRAM LEF Class open GDS file, read pins information, obstruction
and write them to LEF file. 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): def __init__(self, layers):
# LEF db units per micron # LEF db units per micron

View File

@ -42,7 +42,7 @@ class column_decoder(design):
def create_instances(self): def create_instances(self):
self.column_decoder_inst = self.add_inst(name="column_decoder", self.column_decoder_inst = self.add_inst(name="column_decoder",
mod=self.column_decoder) mod=self.column_decoder)
self.connect_inst(self.pins) self.connect_inst(list(self.pins))
def create_layout(self): def create_layout(self):
self.column_decoder_inst.place(vector(0,0)) self.column_decoder_inst.place(vector(0,0))

View File

@ -1212,7 +1212,7 @@ class pbitcell(bitcell_base):
if self.dummy_bitcell: if self.dummy_bitcell:
return return
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} pin_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)}
# Edges added wl->bl, wl->br for every port except write ports # Edges added wl->bl, wl->br for every port except write ports
rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names) rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names)