2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 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.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2016-11-08 18:57:35 +01:00
|
|
|
import hierarchy_layout
|
|
|
|
|
import hierarchy_spice
|
|
|
|
|
import globals
|
2017-11-14 23:59:14 +01:00
|
|
|
import verify
|
2017-06-02 20:11:57 +02:00
|
|
|
import debug
|
2016-11-08 18:57:35 +01:00
|
|
|
import os
|
2017-11-16 22:52:58 +01:00
|
|
|
from globals import OPTS
|
2019-04-19 10:27:06 +02:00
|
|
|
import graph_util
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-11-01 22:02:33 +01:00
|
|
|
total_drc_errors = 0
|
|
|
|
|
total_lvs_errors = 0
|
2017-06-02 20:11:57 +02:00
|
|
|
|
2018-07-10 00:42:46 +02:00
|
|
|
class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
|
|
|
|
Design Class for all modules to inherit the base features.
|
|
|
|
|
Class consisting of a set of modules and instances of these modules
|
|
|
|
|
"""
|
2017-06-02 20:11:57 +02:00
|
|
|
name_map = []
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def __init__(self, name):
|
2019-01-17 01:15:38 +01:00
|
|
|
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds"
|
|
|
|
|
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
self.name = name
|
|
|
|
|
hierarchy_spice.spice.__init__(self, name)
|
2019-04-26 20:57:29 +02:00
|
|
|
hierarchy_layout.layout.__init__(self, name)
|
2019-04-24 23:23:22 +02:00
|
|
|
self.init_graph_params()
|
2019-01-17 01:15:38 +01:00
|
|
|
|
2017-08-07 19:24:45 +02:00
|
|
|
def get_layout_pins(self,inst):
|
|
|
|
|
""" Return a map of pin locations of the instance offset """
|
|
|
|
|
# find the instance
|
|
|
|
|
for i in self.insts:
|
|
|
|
|
if i.name == inst.name:
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
debug.error("Couldn't find instance {0}".format(inst_name),-1)
|
|
|
|
|
inst_map = inst.mod.pin_map
|
|
|
|
|
return inst_map
|
2017-06-02 20:11:57 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2019-04-01 19:35:17 +02:00
|
|
|
def DRC_LVS(self, final_verification=False, top_level=False):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Checks both DRC and LVS for a module"""
|
2019-04-01 19:35:17 +02:00
|
|
|
|
|
|
|
|
# Final verification option does not allow nets to be connected by label.
|
2018-07-11 01:39:32 +02:00
|
|
|
# Unit tests will check themselves.
|
2019-04-01 19:35:17 +02:00
|
|
|
if OPTS.is_unit_test:
|
|
|
|
|
return
|
|
|
|
|
if not OPTS.check_lvsdrc:
|
|
|
|
|
return
|
2018-07-11 01:39:32 +02:00
|
|
|
# Do not run if disabled in options.
|
2019-04-01 19:35:17 +02:00
|
|
|
if (OPTS.inline_lvsdrc or top_level):
|
2018-11-15 19:45:33 +01:00
|
|
|
|
2018-11-01 22:02:33 +01:00
|
|
|
global total_drc_errors
|
|
|
|
|
global total_lvs_errors
|
2019-02-21 20:16:21 +01:00
|
|
|
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name)
|
|
|
|
|
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.sp_write(tempspice)
|
|
|
|
|
self.gds_write(tempgds)
|
2018-11-15 19:45:33 +01:00
|
|
|
|
2019-09-03 20:23:35 +02:00
|
|
|
num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification)
|
|
|
|
|
num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
|
2018-11-01 22:02:33 +01:00
|
|
|
debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors))
|
|
|
|
|
debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors))
|
|
|
|
|
total_drc_errors += num_drc_errors
|
|
|
|
|
total_lvs_errors += num_lvs_errors
|
2018-11-15 19:45:33 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
os.remove(tempspice)
|
|
|
|
|
os.remove(tempgds)
|
|
|
|
|
|
2018-11-14 01:51:19 +01:00
|
|
|
def DRC(self, final_verification=False):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Checks DRC for a module"""
|
2018-07-11 01:39:32 +02:00
|
|
|
# Unit tests will check themselves.
|
|
|
|
|
# Do not run if disabled in options.
|
2018-11-15 19:45:33 +01:00
|
|
|
|
2018-11-14 02:41:32 +01:00
|
|
|
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
2018-11-01 22:02:33 +01:00
|
|
|
global total_drc_errors
|
2019-02-24 16:26:21 +01:00
|
|
|
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.gds_write(tempgds)
|
2019-09-03 20:23:35 +02:00
|
|
|
num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification)
|
2018-11-01 22:02:33 +01:00
|
|
|
total_drc_errors += num_errors
|
|
|
|
|
debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error))
|
2018-11-15 19:45:33 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
os.remove(tempgds)
|
|
|
|
|
|
2018-02-05 23:52:51 +01:00
|
|
|
def LVS(self, final_verification=False):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Checks LVS for a module"""
|
2018-07-11 01:39:32 +02:00
|
|
|
# Unit tests will check themselves.
|
|
|
|
|
# Do not run if disabled in options.
|
2018-11-15 19:45:33 +01:00
|
|
|
|
2018-11-14 02:41:32 +01:00
|
|
|
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
2018-11-01 22:02:33 +01:00
|
|
|
global total_lvs_errors
|
2019-02-24 16:26:21 +01:00
|
|
|
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name)
|
|
|
|
|
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.sp_write(tempspice)
|
|
|
|
|
self.gds_write(tempgds)
|
2019-09-03 20:23:35 +02:00
|
|
|
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
|
2018-11-01 22:02:33 +01:00
|
|
|
total_lvs_errors += num_errors
|
|
|
|
|
debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors))
|
2016-11-08 18:57:35 +01:00
|
|
|
os.remove(tempspice)
|
2019-05-27 22:08:59 +02:00
|
|
|
os.remove(tempgds)
|
2019-04-19 10:27:06 +02:00
|
|
|
|
2019-04-24 23:23:22 +02:00
|
|
|
def init_graph_params(self):
|
|
|
|
|
"""Initializes parameters relevant to the graph creation"""
|
|
|
|
|
#Only initializes a set for checking instances which should not be added
|
|
|
|
|
self.graph_inst_exclude = set()
|
2019-08-09 03:26:12 +02:00
|
|
|
|
2019-04-19 10:27:06 +02:00
|
|
|
def build_graph(self, graph, inst_name, port_nets):
|
|
|
|
|
"""Recursively create graph from instances in module."""
|
|
|
|
|
|
|
|
|
|
#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),1)
|
2019-05-07 09:52:27 +02:00
|
|
|
port_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
|
2019-04-24 23:23:22 +02:00
|
|
|
debug.info(3, "Instance name={}".format(inst_name))
|
2019-04-19 10:27:06 +02:00
|
|
|
for subinst, conns in zip(self.insts, self.conns):
|
2019-04-24 23:23:22 +02:00
|
|
|
if subinst in self.graph_inst_exclude:
|
|
|
|
|
continue
|
2019-04-19 10:27:06 +02:00
|
|
|
subinst_name = inst_name+'.X'+subinst.name
|
|
|
|
|
subinst_ports = self.translate_nets(conns, port_dict, inst_name)
|
|
|
|
|
subinst.mod.build_graph(graph, subinst_name, subinst_ports)
|
|
|
|
|
|
2019-05-27 22:08:59 +02:00
|
|
|
def build_names(self, name_dict, inst_name, port_nets):
|
|
|
|
|
"""Collects all the nets and the parent inst of that net."""
|
|
|
|
|
#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),1)
|
|
|
|
|
port_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
|
|
|
|
|
debug.info(3, "Instance name={}".format(inst_name))
|
|
|
|
|
for subinst, conns in zip(self.insts, self.conns):
|
|
|
|
|
subinst_name = inst_name+'.X'+subinst.name
|
|
|
|
|
subinst_ports = self.translate_nets(conns, port_dict, inst_name)
|
|
|
|
|
for si_port, conn in zip(subinst_ports, conns):
|
|
|
|
|
#Only add for first occurrence
|
|
|
|
|
if si_port.lower() not in name_dict:
|
|
|
|
|
mod_info = {'mod':self, 'int_net':conn}
|
|
|
|
|
name_dict[si_port.lower()] = mod_info
|
|
|
|
|
subinst.mod.build_names(name_dict, subinst_name, subinst_ports)
|
|
|
|
|
|
2019-05-29 01:55:09 +02:00
|
|
|
def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None):
|
2019-05-27 22:08:59 +02:00
|
|
|
"""Given a list of nets, will compare the internal alias of a mod to determine
|
|
|
|
|
if the nets have a connection to this mod's net (but not inst).
|
|
|
|
|
"""
|
2019-05-29 01:55:09 +02:00
|
|
|
if exclusion_set == None:
|
|
|
|
|
exclusion_set = set()
|
2019-05-27 22:08:59 +02:00
|
|
|
try:
|
|
|
|
|
self.name_dict
|
|
|
|
|
except AttributeError:
|
|
|
|
|
self.name_dict = {}
|
|
|
|
|
self.build_names(self.name_dict, inst_name, port_nets)
|
|
|
|
|
aliases = []
|
|
|
|
|
for net in path_nets:
|
|
|
|
|
net = net.lower()
|
|
|
|
|
int_net = self.name_dict[net]['int_net']
|
|
|
|
|
int_mod = self.name_dict[net]['mod']
|
2019-05-29 01:55:09 +02:00
|
|
|
if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set):
|
2019-05-27 22:08:59 +02:00
|
|
|
aliases.append(net)
|
|
|
|
|
return aliases
|
|
|
|
|
|
2019-05-29 01:55:09 +02:00
|
|
|
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
|
2019-06-26 01:37:35 +02:00
|
|
|
"""Checks if the alias_net in input mod is the same as the input net for this mod (self)."""
|
2019-05-29 01:55:09 +02:00
|
|
|
if self in exclusion_set:
|
|
|
|
|
return False
|
2019-05-27 22:08:59 +02:00
|
|
|
#Check ports of this mod
|
|
|
|
|
for pin in self.pins:
|
|
|
|
|
if self.is_net_alias_name_check(known_net, pin, net_alias, mod):
|
|
|
|
|
return True
|
2019-05-29 01:55:09 +02:00
|
|
|
#Check connections of all other subinsts
|
|
|
|
|
mod_set = set()
|
2019-05-27 22:08:59 +02:00
|
|
|
for subinst, inst_conns in zip(self.insts, self.conns):
|
|
|
|
|
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
|
2019-05-29 01:55:09 +02:00
|
|
|
elif inst_conn.lower() == known_net.lower() and subinst.mod not in mod_set:
|
|
|
|
|
if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set):
|
|
|
|
|
return True
|
|
|
|
|
mod_set.add(subinst.mod)
|
2019-05-27 22:08:59 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod):
|
|
|
|
|
"""Utility function for checking single net alias."""
|
|
|
|
|
return self == mod and \
|
|
|
|
|
child_net.lower() == alias_net.lower() and \
|
|
|
|
|
parent_net.lower() == alias_net.lower()
|
|
|
|
|
|
|
|
|
|
def get_mod_net(self, parent_net, child_inst, child_conns):
|
|
|
|
|
"""Given an instance and net, returns the internal net in the mod
|
|
|
|
|
corresponding to input net."""
|
|
|
|
|
for conn, pin in zip(child_conns, child_inst.mod.pins):
|
|
|
|
|
if parent_net.lower() == conn.lower():
|
|
|
|
|
return pin
|
|
|
|
|
return None
|
|
|
|
|
|
2019-04-19 10:27:06 +02:00
|
|
|
def translate_nets(self, subinst_ports, port_dict, inst_name):
|
|
|
|
|
"""Converts connection names to their spice hierarchy equivalent"""
|
|
|
|
|
converted_conns = []
|
|
|
|
|
for conn in subinst_ports:
|
|
|
|
|
if conn in port_dict:
|
|
|
|
|
converted_conns.append(port_dict[conn])
|
|
|
|
|
else:
|
|
|
|
|
converted_conns.append("{}.{}".format(inst_name, conn))
|
|
|
|
|
return converted_conns
|
|
|
|
|
|
2019-05-07 09:52:27 +02:00
|
|
|
def add_graph_edges(self, graph, port_nets):
|
|
|
|
|
"""For every input, adds an edge to every output.
|
|
|
|
|
Only intended to be used for gates and other simple modules."""
|
|
|
|
|
#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)}
|
|
|
|
|
input_pins = self.get_inputs()
|
|
|
|
|
output_pins = self.get_outputs()
|
|
|
|
|
inout_pins = self.get_inouts()
|
|
|
|
|
for inp in input_pins+inout_pins:
|
|
|
|
|
for out in output_pins+inout_pins:
|
|
|
|
|
if inp != out: #do not add self loops
|
2019-08-07 10:50:48 +02:00
|
|
|
graph.add_edge(pin_dict[inp], pin_dict[out], self)
|
2019-05-27 22:08:59 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def __str__(self):
|
|
|
|
|
""" override print function output """
|
2019-04-17 22:41:17 +02:00
|
|
|
pins = ",".join(self.pins)
|
|
|
|
|
insts = [" {}".format(x) for x in self.insts]
|
|
|
|
|
objs = [" {}".format(x) for x in self.objs]
|
|
|
|
|
s = "********** design {0} **********".format(self.name)
|
|
|
|
|
s += "\n pins ({0})={1}\n".format(len(self.pins), pins)
|
|
|
|
|
s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs))
|
|
|
|
|
s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts))
|
|
|
|
|
return s
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
""" override print function output """
|
|
|
|
|
text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n"
|
|
|
|
|
for i in self.objs:
|
|
|
|
|
text+=str(i)+",\n"
|
|
|
|
|
for i in self.insts:
|
|
|
|
|
text+=str(i)+",\n"
|
|
|
|
|
return text
|
2018-02-21 03:22:23 +01:00
|
|
|
|