mirror of https://github.com/VLSIDA/OpenRAM.git
Fix fake_sram bit sizes
This commit is contained in:
commit
78e5b60380
|
|
@ -66,8 +66,6 @@ class contact(hierarchy_design):
|
|||
self.offset = vector(0, 0)
|
||||
self.implant_type = implant_type
|
||||
self.well_type = well_type
|
||||
# Module does not have pins, but has empty pin list.
|
||||
self.pins = []
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
|
|
|
|||
|
|
@ -41,17 +41,17 @@ class design(hierarchy_design):
|
|||
if prop and prop.hard_cell:
|
||||
# The pins get added from the spice file, so just check
|
||||
# that they matched here
|
||||
debug.check(prop.port_names == self.pins,
|
||||
"Custom cell pin names do not match spice file:\n{0} vs {1}".format(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, list(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,
|
||||
GDS["unit"],
|
||||
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,
|
||||
GDS["unit"])
|
||||
|
||||
|
|
|
|||
|
|
@ -161,8 +161,8 @@ class geometry:
|
|||
|
||||
class instance(geometry):
|
||||
"""
|
||||
An instance of an instance/module with a specified location and
|
||||
rotation
|
||||
An instance of a module with a specified location, rotation,
|
||||
spice pins, and spice nets
|
||||
"""
|
||||
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
|
||||
"""Initializes an instance to represent a module"""
|
||||
|
|
@ -176,6 +176,18 @@ class instance(geometry):
|
|||
self.rotate = rotate
|
||||
self.offset = vector(offset).snap_to_grid()
|
||||
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:
|
||||
self.width = 0
|
||||
self.height = 0
|
||||
|
|
@ -274,6 +286,34 @@ class instance(geometry):
|
|||
new_pins.append(p)
|
||||
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):
|
||||
#set up the rotation matrix
|
||||
angle = math.radians(float(node.rotate))
|
||||
|
|
|
|||
|
|
@ -135,11 +135,11 @@ class hierarchy_design(spice, layout):
|
|||
# Translate port names to external nets
|
||||
if len(port_nets) != len(self.pins):
|
||||
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
|
||||
self.pins),
|
||||
list(self.pins)),
|
||||
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))
|
||||
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:
|
||||
continue
|
||||
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
|
||||
if len(port_nets) != len(self.pins):
|
||||
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
|
||||
self.pins),
|
||||
list(self.pins)),
|
||||
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))
|
||||
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_ports = self.translate_nets(conns, port_dict, inst_name)
|
||||
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
|
||||
# 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()
|
||||
output_pins = self.get_outputs()
|
||||
inout_pins = self.get_inouts()
|
||||
|
|
@ -197,7 +197,7 @@ class hierarchy_design(spice, layout):
|
|||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
pins = ",".join(self.pins)
|
||||
pins = ",".join(list(self.pins))
|
||||
insts = [" {}".format(x) for x in self.insts]
|
||||
objs = [" {}".format(x) for x in self.objs]
|
||||
s = "********** design {0} **********".format(self.cell_name)
|
||||
|
|
@ -208,7 +208,7 @@ class hierarchy_design(spice, layout):
|
|||
|
||||
def __repr__(self):
|
||||
""" 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:
|
||||
text+=str(i) + ",\n"
|
||||
for i in self.insts:
|
||||
|
|
|
|||
|
|
@ -629,7 +629,7 @@ class layout():
|
|||
"""
|
||||
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)):
|
||||
"""
|
||||
|
|
@ -1523,6 +1523,7 @@ class layout():
|
|||
""" 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
|
||||
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)
|
||||
if self.name.startswith("pmos") or self.name.startswith("nmos"):
|
||||
pin_names.remove("B")
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from pprint import pformat
|
|||
from openram import debug
|
||||
from openram import tech
|
||||
from openram import OPTS
|
||||
from collections import OrderedDict
|
||||
from .delay_data import delay_data
|
||||
from .wire_spice_model import wire_spice_model
|
||||
from .power_data import power_data
|
||||
|
|
@ -49,20 +50,18 @@ 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 = {}
|
||||
# on Python3.7+ regular dictionaries guarantee order too, but we allow use of v3.5+
|
||||
self.pins = OrderedDict()
|
||||
# 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
|
||||
# Spice format)
|
||||
self.conns = []
|
||||
# If this is set, it will out output subckt or isntances of this (for row/col caps etc.)
|
||||
# internal nets, which may or may not be connected to pins of the same name
|
||||
self.nets = {}
|
||||
# If this is set, it will not output subckt or instances of this (for row/col caps etc.)
|
||||
self.no_instances = False
|
||||
# If we are doing a trimmed netlist, these are the instance that will be filtered
|
||||
self.trim_insts = set()
|
||||
|
|
@ -90,128 +89,114 @@ 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))
|
||||
debug.check(name not in self.pins, "cannot add duplicate spice pin {}".format(name))
|
||||
self.pins[name] = pin_spice(name, pin_type, self)
|
||||
|
||||
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 isinstance(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
|
||||
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)}
|
||||
def update_pin_types(self, type_list):
|
||||
""" Change 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\
|
||||
\n pin names={}\n port types={}".format(self.name, list(self.pins), type_list))
|
||||
for pin, type in zip(self.pins.values(), type_list):
|
||||
pin.set_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
|
||||
pin = self.pins.get(name)
|
||||
debug.check(pin is not None, "Spice pin {} not found".format(name))
|
||||
return pin.type
|
||||
|
||||
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)
|
||||
for pin in self.pins.values():
|
||||
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)
|
||||
for pin in self.pins.values():
|
||||
if pin.type == "OUTPUT":
|
||||
output_list.append(pin.name)
|
||||
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=""):
|
||||
""" 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))
|
||||
for pin in other_module.pins.values():
|
||||
self.add_pin(pin.name + suffix, pin.type)
|
||||
|
||||
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)
|
||||
return inout_list
|
||||
|
||||
def connect_inst(self, args, check=True):
|
||||
def connect_inst(self, args):
|
||||
"""
|
||||
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)
|
||||
|
||||
# Order the arguments if the hard cell has a custom port order
|
||||
ordered_args = self.get_ordered_inputs(args)
|
||||
|
||||
if (check and 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
|
||||
else:
|
||||
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)])
|
||||
debug.error("Connection mismatch:\nInst ({0}) -> Mod ({1})\n{2}".format(num_args,
|
||||
|
|
@ -219,27 +204,17 @@ class spice():
|
|||
modpins_string),
|
||||
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
|
||||
if check and (len(self.insts)!=len(self.conns)):
|
||||
insts_string=pformat(self.insts)
|
||||
conns_string=pformat(self.conns)
|
||||
|
||||
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
|
||||
len(self.insts),
|
||||
len(self.conns)))
|
||||
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 create_nets(self, names_list):
|
||||
nets = []
|
||||
for name in names_list:
|
||||
# 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
|
||||
net = self.nets.setdefault(name, net_spice(name, self))
|
||||
nets.append(net)
|
||||
return nets
|
||||
|
||||
def sp_read(self):
|
||||
"""
|
||||
|
|
@ -258,7 +233,7 @@ class spice():
|
|||
subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE)
|
||||
subckt_line = list(filter(subckt.search, self.spice))[0]
|
||||
# parses line into ports and remove subckt
|
||||
self.pins = subckt_line.split(" ")[2:]
|
||||
self.add_pin_list(subckt_line.split(" ")[2:])
|
||||
else:
|
||||
debug.info(4, "no spfile {0}".format(self.sp_file))
|
||||
self.spice = []
|
||||
|
|
@ -279,10 +254,10 @@ class spice():
|
|||
subckt_line = list(filter(subckt.search, self.lvs))[0]
|
||||
# parses line into ports and remove subckt
|
||||
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,
|
||||
self.lvs_file,
|
||||
self.pins,
|
||||
list(self.pins),
|
||||
self.sp_file))
|
||||
|
||||
def check_net_in_spice(self, net_name):
|
||||
|
|
@ -327,84 +302,72 @@ class spice():
|
|||
# If spice isn't defined, we dynamically generate one.
|
||||
|
||||
# recursively write the modules
|
||||
for i in self.mods:
|
||||
if self.contains(i, usedMODS):
|
||||
for mod in self.mods:
|
||||
if self.contains(mod, usedMODS):
|
||||
continue
|
||||
usedMODS.append(i)
|
||||
i.sp_write_file(sp, usedMODS, lvs, trim)
|
||||
usedMODS.append(mod)
|
||||
mod.sp_write_file(sp, usedMODS, lvs, trim)
|
||||
|
||||
if len(self.insts) == 0:
|
||||
return
|
||||
if self.pins == []:
|
||||
if len(self.pins) == 0:
|
||||
return
|
||||
|
||||
# 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,
|
||||
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
|
||||
for pin in self.pins:
|
||||
sp.write("* {1:6}: {0} \n".format(pin, self.pin_type[pin]))
|
||||
for pin in self.pins.values():
|
||||
sp.write("* {1:6}: {0} \n".format(pin.name, pin.type))
|
||||
|
||||
for line in self.comments:
|
||||
sp.write("* {}\n".format(line))
|
||||
|
||||
# every instance must have a set of connections, even if it is empty.
|
||||
if len(self.insts) != len(self.conns):
|
||||
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.cell_name,
|
||||
len(self.insts),
|
||||
len(self.conns)))
|
||||
debug.error("Instances: \n" + str(self.insts))
|
||||
debug.error("-----")
|
||||
debug.error("Connections: \n" + str(self.conns), 1)
|
||||
# every instance must be connected with the connect_inst function
|
||||
# TODO: may run into empty pin lists edge case, not sure yet
|
||||
connected = True
|
||||
for inst in self.insts:
|
||||
if inst.connected:
|
||||
continue
|
||||
debug.error("Instance {} spice pins not connected".format(str(inst)))
|
||||
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.
|
||||
# these are wires and paths
|
||||
if self.conns[i] == []:
|
||||
if len(inst.spice_pins) == 0:
|
||||
continue
|
||||
|
||||
# Instance with no devices in it needs no subckt/instance
|
||||
if self.insts[i].mod.no_instances:
|
||||
if inst.mod.no_instances:
|
||||
continue
|
||||
|
||||
# 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("* ")
|
||||
|
||||
if lvs and hasattr(self.insts[i].mod, "lvs_device"):
|
||||
sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name,
|
||||
" ".join(self.conns[i])))
|
||||
if lvs and hasattr(inst.mod, "lvs_device"):
|
||||
sp.write(inst.mod.lvs_device.format(inst.name,
|
||||
" ".join(inst.get_connections())))
|
||||
sp.write("\n")
|
||||
elif hasattr(self.insts[i].mod, "spice_device"):
|
||||
sp.write(self.insts[i].mod.spice_device.format(self.insts[i].name,
|
||||
" ".join(self.conns[i])))
|
||||
elif hasattr(inst.mod, "spice_device"):
|
||||
sp.write(inst.mod.spice_device.format(inst.name,
|
||||
" ".join(inst.get_connections())))
|
||||
sp.write("\n")
|
||||
else:
|
||||
if trim and self.insts[i].name in self.trim_insts:
|
||||
wrapped_connections = "\n*+ ".join(tr.wrap(" ".join(self.conns[i])))
|
||||
sp.write("X{0}\n*+ {1}\n*+ {2}\n".format(self.insts[i].name,
|
||||
if trim and inst.name in self.trim_insts:
|
||||
wrapped_connections = "\n*+ ".join(tr.wrap(" ".join(inst.get_connections())))
|
||||
sp.write("X{0}\n*+ {1}\n*+ {2}\n".format(inst.name,
|
||||
wrapped_connections,
|
||||
self.insts[i].mod.cell_name))
|
||||
inst.mod.cell_name))
|
||||
else:
|
||||
wrapped_connections = "\n+ ".join(tr.wrap(" ".join(self.conns[i])))
|
||||
sp.write("X{0}\n+ {1}\n+ {2}\n".format(self.insts[i].name,
|
||||
wrapped_connections = "\n+ ".join(tr.wrap(" ".join(inst.get_connections())))
|
||||
sp.write("X{0}\n+ {1}\n+ {2}\n".format(inst.name,
|
||||
wrapped_connections,
|
||||
self.insts[i].mod.cell_name))
|
||||
|
||||
inst.mod.cell_name))
|
||||
|
||||
sp.write(".ENDS {0}\n".format(self.cell_name))
|
||||
|
||||
|
|
@ -733,6 +696,12 @@ class spice():
|
|||
aliases.append(net)
|
||||
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):
|
||||
"""
|
||||
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
|
||||
# Check connections of all other subinsts
|
||||
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):
|
||||
if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod):
|
||||
return True
|
||||
|
|
@ -762,3 +731,149 @@ class spice():
|
|||
return self == mod and \
|
||||
child_net.lower() == alias_net.lower() and \
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class fake_sram(sram_config):
|
|||
self.pins.extend(['dout{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.word_size + self.num_spare_cols)])
|
||||
self.pins.extend(['addr{0}[{1}]'.format(port, bit)
|
||||
for bit in range(self.word_size)])
|
||||
for bit in range(self.addr_size)])
|
||||
|
||||
self.pins.extend(['csb{}'.format(port)])
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class column_decoder(design):
|
|||
def create_instances(self):
|
||||
self.column_decoder_inst = self.add_inst(name="column_decoder",
|
||||
mod=self.column_decoder)
|
||||
self.connect_inst(self.pins)
|
||||
self.connect_inst(list(self.pins))
|
||||
|
||||
def create_layout(self):
|
||||
self.column_decoder_inst.place(vector(0,0))
|
||||
|
|
|
|||
|
|
@ -1212,7 +1212,7 @@ class pbitcell(bitcell_base):
|
|||
if self.dummy_bitcell:
|
||||
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
|
||||
rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names)
|
||||
|
|
|
|||
Loading…
Reference in New Issue