add spice nets and a way to connect them to pins

This commit is contained in:
Sam Crow 2023-07-14 16:18:10 -07:00
parent 146efc5070
commit c8c43f75d9
4 changed files with 117 additions and 32 deletions

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,14 @@ 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
# 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 spice_obj in self.spice_pins + self.spice_nets:
spice_obj.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 +282,11 @@ class instance(geometry):
new_pins.append(p) new_pins.append(p)
return new_pins return new_pins
def connect_spice_pins(self, nets_list):
for i in range(len(self.pins)):
self.pins[i].set_inst_net(nets_list[i])
nets_list[i].connect_pin(self.pins[i])
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

@ -13,6 +13,8 @@ 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 compiler.base.net_spice import net_spice
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
@ -58,7 +60,8 @@ class 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
self.nets = []
# If this is set, it will out output subckt or isntances of this (for row/col caps etc.) # If this is set, it will out output subckt or isntances 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
@ -178,16 +181,13 @@ class spice():
for pin in other_module.pins: for pin in other_module.pins:
self.add_pin(pin.name + suffix, pin.type) self.add_pin(pin.name + suffix, pin.type)
def connect_inst(self, args, check=True): def connect_inst(self, args):
""" """
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 = 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
@ -195,11 +195,11 @@ class spice():
if (check and num_pins != num_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 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,
@ -207,27 +207,22 @@ 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 # TODO: replace functionality that checked if user forgot to connect an inst before connecting the next
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, def create_nets(self, names_list):
len(self.insts), nets = []
len(self.conns))) for name in names_list:
debug.error("Instances: \n" + str(insts_string)) try:
debug.error("-----") i = self.nets.index(name)
debug.error("Connections: \n" + str(conns_string), 1) nets.append(self.nets[i])
except ValueError:
def get_conns(self, inst): net = net_spice(name)
"""Returns the connections of a given instance.""" self.nets.append(net)
for i in range(len(self.insts)): nets.append(net)
if inst is self.insts[i]: return nets
return self.conns[i]
# If not found, returns None
return None
def sp_read(self): def sp_read(self):
""" """
@ -246,6 +241,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
# FIXME: needs to use new pins interface
self.pins = subckt_line.split(" ")[2:] self.pins = 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))
@ -345,7 +341,7 @@ class spice():
# Also write pins as comments # Also write pins as comments
for pin in self.pins: for pin in self.pins:
sp.write("* {1:6}: {0} \n".format(pin, self.pin_type[pin])) sp.write("* {1:6}: {0} \n".format(pin, pin.type))
for line in self.comments: for line in self.comments:
sp.write("* {}\n".format(line)) sp.write("* {}\n".format(line))

View File

@ -0,0 +1,51 @@
# 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
from .pin_spice import pin_spice
class net_spice:
"""
A class to represent a spice 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):
self.name = name
self.pins = []
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 __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

View File

@ -6,19 +6,29 @@
# All rights reserved. # All rights reserved.
# #
from openram import debug from openram import debug
from .net_spice import net_spice
class pin_spice: class pin_spice:
""" """
A class to represent a spice netlist pin. 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.
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"] valid_pin_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
def __init__(self, name, type): def __init__(self, name, type, mod):
self.name = name self.name = name
self.set_type(type) 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) self._hash = hash(self.name)
def set_type(self, type): def set_type(self, type):
@ -26,6 +36,18 @@ class pin_spice:
"Invalid pin type for {0}: {1}".format(self.name, type)) "Invalid pin type for {0}: {1}".format(self.name, type))
self.type = 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):
debug.check(isinstance(net, net_spice), "net must be a net_spice object")
self.inst_net = net
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
return "(pin_name={} type={})".format(self.name, self.type) return "(pin_name={} type={})".format(self.name, self.type)
@ -34,6 +56,9 @@ class pin_spice:
""" override repr function output """ """ override repr function output """
return self.name return self.name
def __eq__(self, name):
return (name == self.name) if isinstance(name, str) else super().__eq__(name)
def __hash__(self): def __hash__(self):
""" """
Implement the hash function for sets etc. Implement the hash function for sets etc.