mirror of https://github.com/VLSIDA/OpenRAM.git
149 lines
5.9 KiB
Python
149 lines
5.9 KiB
Python
|
|
import debug
|
||
|
|
import re
|
||
|
|
import os
|
||
|
|
|
||
|
|
|
||
|
|
class spice:
|
||
|
|
"""
|
||
|
|
This provides a set of useful generic types for hierarchy
|
||
|
|
management. If a module is a custom designed cell, it will read from
|
||
|
|
the GDS and spice files and perform LVS/DRC. If it is dynamically
|
||
|
|
generated, it should implement a constructor to create the
|
||
|
|
layout/netlist and perform LVS/DRC.
|
||
|
|
Class consisting of a set of modules and instances of these modules
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(self, name):
|
||
|
|
self.name = name
|
||
|
|
|
||
|
|
self.mods = [] # Holds subckts/mods for this module
|
||
|
|
self.pins = [] # Holds the pins for this module
|
||
|
|
|
||
|
|
# for each instance, this is the set of nets/nodes that map to the pins for this instance
|
||
|
|
# THIS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the
|
||
|
|
# Spice format)
|
||
|
|
self.conns = []
|
||
|
|
|
||
|
|
self.sp_read()
|
||
|
|
|
||
|
|
############################################################
|
||
|
|
# Spice circuit
|
||
|
|
############################################################
|
||
|
|
|
||
|
|
def add_pin(self, name):
|
||
|
|
"""Adds a pin to the pins list"""
|
||
|
|
self.pins.append(name)
|
||
|
|
|
||
|
|
def add_pin_list(self, pin_list):
|
||
|
|
"""Adds a pin_list to the pins list"""
|
||
|
|
self.pins = self.pins + pin_list
|
||
|
|
|
||
|
|
def add_mod(self, mod):
|
||
|
|
"""Adds a subckt/submodule to the subckt hierarchy"""
|
||
|
|
self.mods.append(mod)
|
||
|
|
|
||
|
|
def connect_inst(self, args, check=True):
|
||
|
|
"""Connects the pins of the last instance added
|
||
|
|
It is preferred to use the other function with the check to find if
|
||
|
|
there is a problem. The check otion can be set to false
|
||
|
|
where we dynamically generate groups of connections after a
|
||
|
|
group of modules are generated."""
|
||
|
|
|
||
|
|
if (check and (len(self.insts[-1].mod.pins) != len(args))):
|
||
|
|
debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins),
|
||
|
|
len(args)), 1)
|
||
|
|
self.conns.append(args)
|
||
|
|
|
||
|
|
if check and (len(self.insts)!=len(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(self.insts))
|
||
|
|
debug.error("-----")
|
||
|
|
debug.error("Connections: \n"+str(self.conns),1)
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
def sp_read(self):
|
||
|
|
"""Reads the sp file (and parse the pins) from the library
|
||
|
|
Otherwise, initialize it to null for dynamic generation"""
|
||
|
|
if os.path.isfile(self.sp_file):
|
||
|
|
debug.info(2, "opening {0}".format(self.sp_file))
|
||
|
|
f = open(self.sp_file)
|
||
|
|
self.spice = f.readlines()
|
||
|
|
for i in range(len(self.spice)):
|
||
|
|
self.spice[i] = self.spice[i].rstrip(" \n")
|
||
|
|
|
||
|
|
# find first subckt line in the file
|
||
|
|
subckt = re.compile("^.subckt", re.IGNORECASE)
|
||
|
|
subckt_line = filter(subckt.search, self.spice)[0]
|
||
|
|
# parses line into ports and remove subckt
|
||
|
|
self.pins = subckt_line.split(" ")[2:]
|
||
|
|
else:
|
||
|
|
self.spice = []
|
||
|
|
|
||
|
|
def contains(self, mod, modlist):
|
||
|
|
for x in modlist:
|
||
|
|
if x.name == mod.name:
|
||
|
|
return True
|
||
|
|
return False
|
||
|
|
|
||
|
|
def sp_write_file(self, sp, usedMODS):
|
||
|
|
""" Recursive spice subcircuit write;
|
||
|
|
Writes the spice subcircuit from the library or the dynamically generated one"""
|
||
|
|
if not self.spice:
|
||
|
|
# recursively write the modules
|
||
|
|
for i in self.mods:
|
||
|
|
if self.contains(i, usedMODS):
|
||
|
|
continue
|
||
|
|
usedMODS.append(i)
|
||
|
|
i.sp_write_file(sp, usedMODS)
|
||
|
|
|
||
|
|
if len(self.insts) == 0:
|
||
|
|
return
|
||
|
|
if self.pins == []:
|
||
|
|
return
|
||
|
|
# write out the first spice line (the subcircuit)
|
||
|
|
sp.write("\n.SUBCKT {0} {1}\n".format(self.name,
|
||
|
|
" ".join(self.pins)))
|
||
|
|
|
||
|
|
# 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.name,
|
||
|
|
len(self.insts),
|
||
|
|
len(self.conns)))
|
||
|
|
debug.error("Instances: \n"+str(self.insts))
|
||
|
|
debug.error("-----")
|
||
|
|
debug.error("Connections: \n"+str(self.conns),1)
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
for i in range(len(self.insts)):
|
||
|
|
# we don't need to output connections of empty instances.
|
||
|
|
# these are wires and paths
|
||
|
|
if self.conns[i] == []:
|
||
|
|
continue
|
||
|
|
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
|
||
|
|
" ".join(self.conns[i]),
|
||
|
|
self.insts[i].mod.name))
|
||
|
|
|
||
|
|
sp.write(".ENDS {0}\n".format(self.name))
|
||
|
|
|
||
|
|
else:
|
||
|
|
# write the subcircuit itself
|
||
|
|
# Including the file path makes the unit test fail for other users.
|
||
|
|
#if os.path.isfile(self.sp_file):
|
||
|
|
# sp.write("\n* {0}\n".format(self.sp_file))
|
||
|
|
sp.write("\n".join(self.spice))
|
||
|
|
sp.write("\n")
|
||
|
|
|
||
|
|
def sp_write(self, spname):
|
||
|
|
"""Writes the spice to files"""
|
||
|
|
debug.info(3, "Writing to {0}".format(spname))
|
||
|
|
spfile = open(spname, 'w')
|
||
|
|
spfile.write("*FIRST LINE IS A COMMENT\n")
|
||
|
|
usedMODS = list()
|
||
|
|
self.sp_write_file(spfile, usedMODS)
|
||
|
|
del usedMODS
|
||
|
|
spfile.close()
|