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):
"""
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,14 @@ class instance(geometry):
self.rotate = rotate
self.offset = vector(offset).snap_to_grid()
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:
self.width = 0
self.height = 0
@ -274,6 +282,11 @@ class instance(geometry):
new_pins.append(p)
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):
#set up the rotation matrix
angle = math.radians(float(node.rotate))

View File

@ -13,6 +13,8 @@ from pprint import pformat
from openram import debug
from openram import tech
from openram import OPTS
from compiler.base.net_spice import net_spice
from .delay_data import delay_data
from .wire_spice_model import wire_spice_model
from .power_data import power_data
@ -58,7 +60,8 @@ class spice():
self.pin_indices = []
# THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the
# 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.)
self.no_instances = False
# 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:
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
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)
# 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 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,
@ -207,27 +207,22 @@ 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)
# TODO: replace functionality that checked if user forgot to connect an inst before connecting the next
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:
try:
i = self.nets.index(name)
nets.append(self.nets[i])
except ValueError:
net = net_spice(name)
self.nets.append(net)
nets.append(net)
return nets
def sp_read(self):
"""
@ -246,6 +241,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
# FIXME: needs to use new pins interface
self.pins = subckt_line.split(" ")[2:]
else:
debug.info(4, "no spfile {0}".format(self.sp_file))
@ -345,7 +341,7 @@ class spice():
# Also write pins as comments
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:
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.
#
from openram import debug
from .net_spice import net_spice
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.
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"]
def __init__(self, name, type):
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):
@ -26,6 +36,18 @@ class pin_spice:
"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):
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)
@ -34,6 +56,9 @@ class pin_spice:
""" 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.