Made timing graph more gate-level. Changed edges to be defined by inputs/ouputs and name based.

This commit is contained in:
Hunter Nichols 2019-05-07 00:52:27 -07:00
parent 5bfc42fdbb
commit d54074d68e
20 changed files with 205 additions and 151 deletions

View File

@ -120,7 +120,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
#Translate port names to external nets #Translate port names to external nets
if len(port_nets) != len(self.pins): if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,self.pins),1) debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,self.pins),1)
port_dict = {i:j for i,j in zip(self.pins, port_nets)} port_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
debug.info(3, "Instance name={}".format(inst_name)) 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.conns):
if subinst in self.graph_inst_exclude: if subinst in self.graph_inst_exclude:
@ -139,6 +139,20 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
converted_conns.append("{}.{}".format(inst_name, conn)) converted_conns.append("{}.{}".format(inst_name, conn))
return converted_conns return converted_conns
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
graph.add_edge(pin_dict[inp], pin_dict[out])
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
return "design: " + self.name return "design: " + self.name

View File

@ -58,6 +58,17 @@ class spice():
else: else:
debug.error("Mismatch in type and pin list lengths.", -1) debug.error("Mismatch in type and pin list lengths.", -1)
def add_pin_types(self, type_list):
"""Add pin types for all the cell's pins.
Typically, should only be used for handmade cells."""
#This only works if self.pins == bitcell.pin_names
if self.pin_names != self.pins:
debug.error("{} spice subcircuit port names do not match pin_names\
\n SPICE names={}\
\n Module names={}\
".format(self.name, self.pin_names, self.pins),1)
self.pin_type = {pin:type for pin,type in zip(self.pin_names, type_list)}
def get_pin_type(self, name): def get_pin_type(self, name):
""" Returns the type of the signal pin. """ """ Returns the type of the signal pin. """
return self.pin_type[name] return self.pin_type[name]
@ -87,6 +98,14 @@ class spice():
output_list.append(pin) output_list.append(pin)
return output_list return output_list
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 add_mod(self, mod): def add_mod(self, mod):
"""Adds a subckt/submodule to the subckt hierarchy""" """Adds a subckt/submodule to the subckt hierarchy"""

View File

@ -13,6 +13,7 @@ class bitcell(design.design):
""" """
pin_names = ["bl", "br", "wl", "vdd", "gnd"] pin_names = ["bl", "br", "wl", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
@ -24,6 +25,7 @@ class bitcell(design.design):
self.width = bitcell.width self.width = bitcell.width
self.height = bitcell.height self.height = bitcell.height
self.pin_map = bitcell.pin_map self.pin_map = bitcell.pin_map
self.add_pin_types(self.type_list)
def analytical_delay(self, corner, slew, load=0, swing = 0.5): def analytical_delay(self, corner, slew, load=0, swing = 0.5):
parasitic_delay = 1 parasitic_delay = 1
@ -76,10 +78,5 @@ class bitcell(design.design):
return 2*access_tx_cin return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges based on inputs/outputs. Overrides base class function."""
#The bitcell has 5 net ports hard-coded in self.pin_names. The edges self.add_graph_edges(graph, port_nets)
#are based on the hard-coded name positions.
# The edges added are: wl->bl, wl->br.
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph.
graph.add_edge(port_nets[2],port_nets[0])
graph.add_edge(port_nets[2],port_nets[1])

View File

@ -13,6 +13,7 @@ class bitcell_1rw_1r(design.design):
""" """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("cell_1rw_1r", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"])
@ -24,6 +25,7 @@ class bitcell_1rw_1r(design.design):
self.width = bitcell_1rw_1r.width self.width = bitcell_1rw_1r.width
self.height = bitcell_1rw_1r.height self.height = bitcell_1rw_1r.height
self.pin_map = bitcell_1rw_1r.pin_map self.pin_map = bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
def analytical_delay(self, corner, slew, load=0, swing = 0.5): def analytical_delay(self, corner, slew, load=0, swing = 0.5):
parasitic_delay = 1 parasitic_delay = 1
@ -101,13 +103,13 @@ class bitcell_1rw_1r(design.design):
return 2*access_tx_cin return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges to graph. Multiport bitcell timing graph is too complex
#The bitcell has 8 net ports hard-coded in self.pin_names. The edges to use the add_graph_edges function."""
#are based on the hard-coded name positions. pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
# The edges added are: wl0->bl0, wl0->br0, wl1->bl1, wl1->br1. #Edges hardcoded here. Essentially wl->bl/br for both ports.
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph. # Port 0 edges
graph.add_edge(port_nets[4],port_nets[0]) graph.add_edge(pin_dict["wl0"], pin_dict["bl0"])
graph.add_edge(port_nets[4],port_nets[1]) graph.add_edge(pin_dict["wl0"], pin_dict["br0"])
graph.add_edge(port_nets[5],port_nets[2]) # Port 1 edges
graph.add_edge(port_nets[5],port_nets[3]) graph.add_edge(pin_dict["wl1"], pin_dict["bl1"])
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])

View File

@ -13,6 +13,7 @@ class bitcell_1w_1r(design.design):
""" """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("cell_1w_1r", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"])
@ -24,6 +25,7 @@ class bitcell_1w_1r(design.design):
self.width = bitcell_1w_1r.width self.width = bitcell_1w_1r.width
self.height = bitcell_1w_1r.height self.height = bitcell_1w_1r.height
self.pin_map = bitcell_1w_1r.pin_map self.pin_map = bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
def analytical_delay(self, corner, slew, load=0, swing = 0.5): def analytical_delay(self, corner, slew, load=0, swing = 0.5):
parasitic_delay = 1 parasitic_delay = 1
@ -101,12 +103,11 @@ class bitcell_1w_1r(design.design):
return 2*access_tx_cin return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges to graph. Multiport bitcell timing graph is too complex
#The bitcell has 8 net ports hard-coded in self.pin_names. The edges to use the add_graph_edges function."""
#are based on the hard-coded name positions. pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
# The edges added are: wl0->bl0, wl0->br0, wl1->bl1, wl1->br1. #Edges hardcoded here. Essentially wl->bl/br for both ports.
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph. # Port 0 edges
graph.add_edge(port_nets[4],port_nets[0]) graph.add_edge(pin_dict["wl0"], pin_dict["bl0"])
graph.add_edge(port_nets[4],port_nets[1]) graph.add_edge(pin_dict["wl0"], pin_dict["br0"])
graph.add_edge(port_nets[5],port_nets[2]) # Port 1 is a write port, so its timing is not considered here.
graph.add_edge(port_nets[5],port_nets[3])

View File

@ -91,40 +91,40 @@ class pbitcell(design.design):
port = 0 port = 0
for k in range(self.num_rw_ports): for k in range(self.num_rw_ports):
self.add_pin("bl{}".format(port)) self.add_pin("bl{}".format(port), "OUTPUT")
self.add_pin("br{}".format(port)) self.add_pin("br{}".format(port), "OUTPUT")
self.rw_bl_names.append("bl{}".format(port)) self.rw_bl_names.append("bl{}".format(port))
self.rw_br_names.append("br{}".format(port)) self.rw_br_names.append("br{}".format(port))
port += 1 port += 1
for k in range(self.num_w_ports): for k in range(self.num_w_ports):
self.add_pin("bl{}".format(port)) self.add_pin("bl{}".format(port), "INPUT")
self.add_pin("br{}".format(port)) self.add_pin("br{}".format(port), "INPUT")
self.w_bl_names.append("bl{}".format(port)) self.w_bl_names.append("bl{}".format(port))
self.w_br_names.append("br{}".format(port)) self.w_br_names.append("br{}".format(port))
port += 1 port += 1
for k in range(self.num_r_ports): for k in range(self.num_r_ports):
self.add_pin("bl{}".format(port)) self.add_pin("bl{}".format(port), "OUTPUT")
self.add_pin("br{}".format(port)) self.add_pin("br{}".format(port), "OUTPUT")
self.r_bl_names.append("bl{}".format(port)) self.r_bl_names.append("bl{}".format(port))
self.r_br_names.append("br{}".format(port)) self.r_br_names.append("br{}".format(port))
port += 1 port += 1
port = 0 port = 0
for k in range(self.num_rw_ports): for k in range(self.num_rw_ports):
self.add_pin("wl{}".format(port)) self.add_pin("wl{}".format(port), "INPUT")
self.rw_wl_names.append("wl{}".format(port)) self.rw_wl_names.append("wl{}".format(port))
port += 1 port += 1
for k in range(self.num_w_ports): for k in range(self.num_w_ports):
self.add_pin("wl{}".format(port)) self.add_pin("wl{}".format(port), "INPUT")
self.w_wl_names.append("wl{}".format(port)) self.w_wl_names.append("wl{}".format(port))
port += 1 port += 1
for k in range(self.num_r_ports): for k in range(self.num_r_ports):
self.add_pin("wl{}".format(port)) self.add_pin("wl{}".format(port), "INPUT")
self.r_wl_names.append("wl{}".format(port)) self.r_wl_names.append("wl{}".format(port))
port += 1 port += 1
self.add_pin("vdd") self.add_pin("vdd", "POWER")
self.add_pin("gnd") self.add_pin("gnd", "GROUND")
# if this is a replica bitcell, replace the instances of Q_bar with vdd # if this is a replica bitcell, replace the instances of Q_bar with vdd
if self.replica_bitcell: if self.replica_bitcell:
@ -894,14 +894,13 @@ class pbitcell(design.design):
return 2*access_tx_cin return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Done manually to make the graph acyclic.""" """Adds edges to graph for pbitcell. Only readwrite and read ports."""
#The base graph function can make this but it would contain loops and path pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
#searches would essentially be useless. # Edges added wl->bl, wl->br for every port except write ports
# Edges added wl->bl, wl->br for every port rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names)
r_pin_names = zip(self.rw_wl_names, self.rw_bl_names, self.rw_br_names)
for pin_zip in zip(rw_pin_names, r_pin_names):
for wl,bl,br in pin_zip:
graph.add_edge(pin_dict[wl],pin_dict[bl])
graph.add_edge(pin_dict[wl],pin_dict[br])
num_wl = len(self.rw_wl_names) + len(self.w_wl_names) + len(self.r_wl_names)
wl_pos = 2*num_wl #there are this many bitlines nets before the wls in the port list
for i in range(num_wl):
bl_pos = i*2
graph.add_edge(port_nets[wl_pos+i],port_nets[bl_pos])
graph.add_edge(port_nets[wl_pos+i],port_nets[bl_pos+1])

View File

@ -11,6 +11,7 @@ class replica_bitcell(design.design):
the technology library. """ the technology library. """
pin_names = ["bl", "br", "wl", "vdd", "gnd"] pin_names = ["bl", "br", "wl", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"])
@ -22,6 +23,7 @@ class replica_bitcell(design.design):
self.width = replica_bitcell.width self.width = replica_bitcell.width
self.height = replica_bitcell.height self.height = replica_bitcell.height
self.pin_map = replica_bitcell.pin_map self.pin_map = replica_bitcell.pin_map
self.add_pin_types(self.type_list)
def get_wl_cin(self): def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
@ -31,10 +33,5 @@ class replica_bitcell(design.design):
return 2*access_tx_cin return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges based on inputs/outputs. Overrides base class function."""
#The bitcell has 5 net ports hard-coded in self.pin_names. The edges self.add_graph_edges(graph, port_nets)
#are based on the hard-coded name positions.
# The edges added are: wl->bl, wl->br.
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph.
graph.add_edge(port_nets[2],port_nets[0])
graph.add_edge(port_nets[2],port_nets[1])

View File

@ -11,6 +11,7 @@ class replica_bitcell_1rw_1r(design.design):
the technology library. """ the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"])
@ -22,6 +23,7 @@ class replica_bitcell_1rw_1r(design.design):
self.width = replica_bitcell_1rw_1r.width self.width = replica_bitcell_1rw_1r.width
self.height = replica_bitcell_1rw_1r.height self.height = replica_bitcell_1rw_1r.height
self.pin_map = replica_bitcell_1rw_1r.pin_map self.pin_map = replica_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
def get_wl_cin(self): def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
@ -32,12 +34,13 @@ class replica_bitcell_1rw_1r(design.design):
return 2*access_tx_cin return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges to graph. Multiport bitcell timing graph is too complex
#The bitcell has 8 net ports hard-coded in self.pin_names. The edges to use the add_graph_edges function."""
#are based on the hard-coded name positions. pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
# The edges added are: wl0->bl0, wl0->br0, wl1->bl1, wl1->br1. #Edges hardcoded here. Essentially wl->bl/br for both ports.
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph. # Port 0 edges
graph.add_edge(port_nets[4],port_nets[0]) graph.add_edge(pin_dict["wl0"], pin_dict["bl0"])
graph.add_edge(port_nets[4],port_nets[1]) graph.add_edge(pin_dict["wl0"], pin_dict["br0"])
graph.add_edge(port_nets[5],port_nets[2]) # Port 1 edges
graph.add_edge(port_nets[5],port_nets[3]) graph.add_edge(pin_dict["wl1"], pin_dict["bl1"])
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])

View File

@ -11,6 +11,7 @@ class replica_bitcell_1w_1r(design.design):
the technology library. """ the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"])
@ -22,6 +23,7 @@ class replica_bitcell_1w_1r(design.design):
self.width = replica_bitcell_1w_1r.width self.width = replica_bitcell_1w_1r.width
self.height = replica_bitcell_1w_1r.height self.height = replica_bitcell_1w_1r.height
self.pin_map = replica_bitcell_1w_1r.pin_map self.pin_map = replica_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
def get_wl_cin(self): def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
@ -32,12 +34,11 @@ class replica_bitcell_1w_1r(design.design):
return 2*access_tx_cin return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges to graph. Multiport bitcell timing graph is too complex
#The bitcell has 8 net ports hard-coded in self.pin_names. The edges to use the add_graph_edges function."""
#are based on the hard-coded name positions. pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
# The edges added are: wl0->bl0, wl0->br0, wl1->bl1, wl1->br1. #Edges hardcoded here. Essentially wl->bl/br for both ports.
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph. # Port 0 edges
graph.add_edge(port_nets[4],port_nets[0]) graph.add_edge(pin_dict["wl0"], pin_dict["bl0"])
graph.add_edge(port_nets[4],port_nets[1]) graph.add_edge(pin_dict["wl0"], pin_dict["br0"])
graph.add_edge(port_nets[5],port_nets[2]) # Port 1 is a write port, so its timing is not considered here.
graph.add_edge(port_nets[5],port_nets[3])

View File

@ -31,7 +31,7 @@ class model_check(delay):
self.bl_meas_name, self.bl_slew_name = "bl_measures", "bl_slews" self.bl_meas_name, self.bl_slew_name = "bl_measures", "bl_slews"
self.power_name = "total_power" self.power_name = "total_power"
def create_measurement_names(self): def create_measurement_names(self, port):
"""Create measurement names. The names themselves currently define the type of measurement""" """Create measurement names. The names themselves currently define the type of measurement"""
#Create delay measurement names #Create delay measurement names
wl_en_driver_delay_names = ["delay_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] wl_en_driver_delay_names = ["delay_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())]
@ -42,7 +42,10 @@ class model_check(delay):
else: else:
dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)]
self.wl_delay_meas_names = wl_en_driver_delay_names+["delay_wl_en", "delay_wl_bar"]+wl_driver_delay_names+["delay_wl"] self.wl_delay_meas_names = wl_en_driver_delay_names+["delay_wl_en", "delay_wl_bar"]+wl_driver_delay_names+["delay_wl"]
if port not in self.sram.readonly_ports:
self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names
else:
self.rbl_delay_meas_names = ["delay_gated_clk_nand"]+dc_delay_names
self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"] self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"]
# if self.custom_delaychain: # if self.custom_delaychain:
@ -58,45 +61,54 @@ class model_check(delay):
else: else:
dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)]
self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"] self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"]
if port not in self.sram.readonly_ports:
self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names
else:
self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar"]+dc_slew_names
self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen"]+sen_driver_slew_names+["slew_sen"] self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen"]+sen_driver_slew_names+["slew_sen"]
self.bitline_meas_names = ["delay_wl_to_bl", "delay_bl_to_dout"] self.bitline_meas_names = ["delay_wl_to_bl", "delay_bl_to_dout"]
self.power_meas_names = ['read0_power'] self.power_meas_names = ['read0_power']
def create_signal_names(self): def create_signal_names(self, port):
"""Creates list of the signal names used in the spice file along the wl and sen paths. """Creates list of the signal names used in the spice file along the wl and sen paths.
Names are re-harded coded here; i.e. the names are hardcoded in most of OpenRAM and are Names are re-harded coded here; i.e. the names are hardcoded in most of OpenRAM and are
replicated here. replicated here.
""" """
delay.create_signal_names(self) delay.create_signal_names(self)
#Signal names are all hardcoded, need to update to make it work for probe address and different configurations. #Signal names are all hardcoded, need to update to make it work for probe address and different configurations.
wl_en_driver_signals = ["Xsram.Xcontrol0.Xbuf_wl_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] wl_en_driver_signals = ["Xsram.Xcontrol{}.Xbuf_wl_en.Zb{}_int".format('{}', stage) for stage in range(1,self.get_num_wl_en_driver_stages())]
wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver0.Xwl_driver_inv{}.Zb{}_int".format(self.wordline_row, stage) for stage in range(1,self.get_num_wl_driver_stages())] wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver{}.Xwl_driver_inv{}.Zb{}_int".format('{}', self.wordline_row, stage) for stage in range(1,self.get_num_wl_driver_stages())]
sen_driver_signals = ["Xsram.Xcontrol0.Xbuf_s_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] sen_driver_signals = ["Xsram.Xcontrol{}.Xbuf_s_en.Zb{}_int".format('{}',stage) for stage in range(1,self.get_num_sen_driver_stages())]
if self.custom_delaychain: if self.custom_delaychain:
delay_chain_signal_names = [] delay_chain_signal_names = []
else: else:
delay_chain_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_{}".format(stage) for stage in range(1,self.get_num_delay_stages())] delay_chain_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.Xdelay_chain.dout_{}".format('{}', stage) for stage in range(1,self.get_num_delay_stages())]
if len(self.sram.all_ports) > 1:
self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar"]+\ port_format = '{}'
else:
port_format = ''
self.wl_signal_names = ["Xsram.Xcontrol{}.gated_clk_bar".format('{}')]+\
wl_en_driver_signals+\ wl_en_driver_signals+\
["Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_{}".format(self.wordline_row)]+\ ["Xsram.wl_en{}".format('{}'), "Xsram.Xbank0.Xwordline_driver{}.wl_bar_{}".format('{}',self.wordline_row)]+\
wl_driver_signals+\ wl_driver_signals+\
["Xsram.Xbank0.wl_{}".format(self.wordline_row)] ["Xsram.Xbank0.wl{}_{}".format(port_format, self.wordline_row)]
pre_delay_chain_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in"] pre_delay_chain_names = ["Xsram.Xcontrol{}.gated_clk_bar".format('{}')]
if port not in self.sram.readonly_ports:
pre_delay_chain_names+= ["Xsram.Xcontrol{}.Xand2_rbl_in.zb_int".format('{}'), "Xsram.Xcontrol{}.rbl_in".format('{}')]
self.rbl_en_signal_names = pre_delay_chain_names+\ self.rbl_en_signal_names = pre_delay_chain_names+\
delay_chain_signal_names+\ delay_chain_signal_names+\
["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] ["Xsram.Xcontrol{}.Xreplica_bitline.delayed_en".format('{}')]
self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+\ self.sae_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.bl0_0".format('{}'), "Xsram.Xcontrol{}.pre_s_en".format('{}')]+\
sen_driver_signals+\ sen_driver_signals+\
["Xsram.s_en0"] ["Xsram.s_en{}".format('{}')]
dout_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit dout_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit
self.bl_signal_names = ["Xsram.Xbank0.wl_{}".format(self.wordline_row),\ self.bl_signal_names = ["Xsram.Xbank0.wl{}_{}".format(port_format, self.wordline_row),\
"Xsram.Xbank0.bl_{}".format(self.bitline_column),\ "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column),\
dout_name] dout_name]
def create_measurement_objects(self): def create_measurement_objects(self):
@ -362,17 +374,18 @@ class model_check(delay):
errors = self.calculate_error_l2_norm(scaled_meas, scaled_model) errors = self.calculate_error_l2_norm(scaled_meas, scaled_model)
debug.info(1, "Errors:\n{}\n".format(errors)) debug.info(1, "Errors:\n{}\n".format(errors))
def analyze(self, probe_address, probe_data, slews, loads): def analyze(self, probe_address, probe_data, slews, loads, port):
"""Measures entire delay path along the wordline and sense amp enable and compare it to the model delays.""" """Measures entire delay path along the wordline and sense amp enable and compare it to the model delays."""
self.load=max(loads) self.load=max(loads)
self.slew=max(slews) self.slew=max(slews)
self.set_probe(probe_address, probe_data) self.set_probe(probe_address, probe_data)
self.create_signal_names() self.create_signal_names(port)
self.create_measurement_names() self.create_measurement_names(port)
self.create_measurement_objects() self.create_measurement_objects()
data_dict = {} data_dict = {}
read_port = self.read_ports[0] #only test the first read port read_port = self.read_ports[0] #only test the first read port
read_port = port
self.targ_read_ports = [read_port] self.targ_read_ports = [read_port]
self.targ_write_ports = [self.write_ports[0]] self.targ_write_ports = [self.write_ports[0]]
debug.info(1,"Model test: corner {}".format(self.corner)) debug.info(1,"Model test: corner {}".format(self.corner))

View File

@ -11,6 +11,7 @@ class dff(design.design):
""" """
pin_names = ["D", "Q", "clk", "vdd", "gnd"] pin_names = ["D", "Q", "clk", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"])
@ -20,6 +21,7 @@ class dff(design.design):
self.width = dff.width self.width = dff.width
self.height = dff.height self.height = dff.height
self.pin_map = dff.pin_map self.pin_map = dff.pin_map
self.add_pin_types(self.type_list)
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""
@ -51,10 +53,6 @@ class dff(design.design):
return parameter["dff_clk_cin"] return parameter["dff_clk_cin"]
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges based on inputs/outputs. Overrides base class function."""
#The cell has 5 net ports hard-coded in self.pin_names. The edges self.add_graph_edges(graph, port_nets)
#are based on the hard-coded name positions.
# The edges added are: clk->Q.
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph.
graph.add_edge(port_nets[2],port_nets[1])

View File

@ -13,6 +13,7 @@ class sense_amp(design.design):
""" """
pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"] pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"]
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"])
@ -23,6 +24,7 @@ class sense_amp(design.design):
self.width = sense_amp.width self.width = sense_amp.width
self.height = sense_amp.height self.height = sense_amp.height
self.pin_map = sense_amp.pin_map self.pin_map = sense_amp.pin_map
self.add_pin_types(self.type_list)
def input_load(self): def input_load(self):
#Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. #Input load for the bitlines which are connected to the source/drain of a TX. Not the selects.
@ -54,12 +56,6 @@ class sense_amp(design.design):
return 2*pmos_cin + nmos_cin return 2*pmos_cin + nmos_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges based on inputs/outputs. Overrides base class function."""
#The cell has 6 net ports hard-coded in self.pin_names. The edges self.add_graph_edges(graph, port_nets)
#are based on the hard-coded name positions.
# The edges added are: en->dout, bl->dout
# br->dout not included to reduce complexity
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph.
graph.add_edge(port_nets[0],port_nets[2])
graph.add_edge(port_nets[3],port_nets[2])

View File

@ -11,6 +11,7 @@ class tri_gate(design.design):
""" """
pin_names = ["in", "out", "en", "en_bar", "gnd", "vdd"] pin_names = ["in", "out", "en", "en_bar", "gnd", "vdd"]
type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"])
@ -26,6 +27,7 @@ class tri_gate(design.design):
self.width = tri_gate.width self.width = tri_gate.width
self.height = tri_gate.height self.height = tri_gate.height
self.pin_map = tri_gate.pin_map self.pin_map = tri_gate.pin_map
self.add_pin_types(self.type_list)
def analytical_delay(self, corner, slew, load=0.0): def analytical_delay(self, corner, slew, load=0.0):
from tech import spice from tech import spice
@ -43,12 +45,5 @@ class tri_gate(design.design):
return 9*spice["min_tx_gate_c"] return 9*spice["min_tx_gate_c"]
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges based on inputs/outputs. Overrides base class function."""
#The cell has 6 net ports hard-coded in self.pin_names. The edges self.add_graph_edges(graph, port_nets)
#are based on the hard-coded name positions.
# The edges added are: en->out, en_bar->out, in->out.
# A liberal amount of edges were added, may be reduced later for complexity.
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph.
graph.add_edge(port_nets[0],port_nets[1])
graph.add_edge(port_nets[2],port_nets[1])
graph.add_edge(port_nets[3],port_nets[1])

View File

@ -11,7 +11,8 @@ class write_driver(design.design):
the technology library. the technology library.
""" """
pin_names = ["din", "bl", "br", "en", "gnd", "vdd"] pin_names = ["din", "bl", "br", "en", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) (width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"])
@ -22,7 +23,7 @@ class write_driver(design.design):
self.width = write_driver.width self.width = write_driver.width
self.height = write_driver.height self.height = write_driver.height
self.pin_map = write_driver.pin_map self.pin_map = write_driver.pin_map
self.add_pin_types(self.type_list)
def get_w_en_cin(self): def get_w_en_cin(self):
"""Get the relative capacitance of a single input""" """Get the relative capacitance of a single input"""
@ -30,13 +31,5 @@ class write_driver(design.design):
return 5*3 return 5*3
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Handmade cells must implement this manually.""" """Adds edges based on inputs/outputs. Overrides base class function."""
#The cell has 6 net ports hard-coded in self.pin_names. The edges self.add_graph_edges(graph, port_nets)
#are based on the hard-coded name positions.
# The edges added are: din->bl, din->br, en->bl, en->br
# A liberal amount of edges were added, may be reduced later for complexity.
# Internal nodes of the handmade cell not considered, only ports. vdd/gnd ignored for graph.
graph.add_edge(port_nets[0],port_nets[1])
graph.add_edge(port_nets[0],port_nets[2])
graph.add_edge(port_nets[3],port_nets[1])
graph.add_edge(port_nets[3],port_nets[2])

View File

@ -216,4 +216,3 @@ class pgate(design.design):
# offset=implant_offset, # offset=implant_offset,
# width=implant_width, # width=implant_width,
# height=implant_height) # height=implant_height)

View File

@ -62,7 +62,9 @@ class pinv(pgate.pgate):
def add_pins(self): def add_pins(self):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
self.add_pin_list(["A", "Z", "vdd", "gnd"]) pin_list = ["A", "Z", "vdd", "gnd"]
dir_list = ['INPUT', 'OUTPUT', 'POWER', 'GROUND']
self.add_pin_list(pin_list, dir_list)
def determine_tx_mults(self): def determine_tx_mults(self):
@ -283,7 +285,8 @@ class pinv(pgate.pgate):
return transition_prob*(c_load + c_para) return transition_prob*(c_load + c_para)
def get_cin(self): def get_cin(self):
"""Return the capacitance of the gate connection in generic capacitive units relative to the minimum width of a transistor""" """Return the capacitance of the gate connection in generic capacitive
units relative to the minimum width of a transistor"""
return self.nmos_size + self.pmos_size return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True): def get_stage_effort(self, cout, inp_is_rise=True):
@ -291,4 +294,13 @@ class pinv(pgate.pgate):
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
""" """
parasitic_delay = 1 parasitic_delay = 1
return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) return logical_effort.logical_effort(self.name,
self.size,
self.get_cin(),
cout,
parasitic_delay,
not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -33,7 +33,7 @@ class pnand2(pgate.pgate):
self.create_layout() self.create_layout()
#For characterization purposes only #For characterization purposes only
self.exclude_nmos_from_graph() #self.exclude_nmos_from_graph()
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
@ -54,7 +54,9 @@ class pnand2(pgate.pgate):
def add_pins(self): def add_pins(self):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) pin_list = ["A", "B", "Z", "vdd", "gnd"]
dir_list = ['INPUT', 'INPUT', 'OUTPUT', 'POWER', 'GROUND']
self.add_pin_list(pin_list, dir_list)
def add_ptx(self): def add_ptx(self):
@ -268,3 +270,7 @@ class pnand2(pgate.pgate):
#Removing them simplifies generic path searching. #Removing them simplifies generic path searching.
self.graph_inst_exclude.add(self.nmos1_inst) self.graph_inst_exclude.add(self.nmos1_inst)
self.graph_inst_exclude.add(self.nmos2_inst) self.graph_inst_exclude.add(self.nmos2_inst)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -37,7 +37,9 @@ class pnand3(pgate.pgate):
def add_pins(self): def add_pins(self):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
self.add_pin_list(["A", "B", "C", "Z", "vdd", "gnd"]) pin_list = ["A", "B", "C", "Z", "vdd", "gnd"]
dir_list = ['INPUT', 'INPUT', 'INPUT', 'OUTPUT', 'POWER', 'GROUND']
self.add_pin_list(pin_list, dir_list)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
@ -272,3 +274,7 @@ class pnand3(pgate.pgate):
""" """
parasitic_delay = 3 parasitic_delay = 3
return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -34,7 +34,9 @@ class pnor2(pgate.pgate):
def add_pins(self): def add_pins(self):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) pin_list = ["A", "B", "Z", "vdd", "gnd"]
dir_list = ['INPUT', 'INPUT', 'OUTPUT', 'INOUT', 'INOUT']
self.add_pin_list(pin_list, dir_list)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
@ -234,3 +236,6 @@ class pnor2(pgate.pgate):
transition_prob = spice["nor2_transition_prob"] transition_prob = spice["nor2_transition_prob"]
return transition_prob*(c_load + c_para) return transition_prob*(c_load + c_para)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -60,7 +60,13 @@ class ptx(design.design):
#self.DRC() #self.DRC()
def create_netlist(self): def create_netlist(self):
self.add_pin_list(["D", "G", "S", "B"]) pin_list = ["D", "G", "S", "B"]
if self.tx_type=="nmos":
body_dir = 'GROUND'
else: #Assumed that the check for either pmos or nmos is done elsewhere.
body_dir = 'POWER'
dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir]
self.add_pin_list(pin_list, dir_list)
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
# " ".join(self.pins))) # " ".join(self.pins)))
@ -358,14 +364,6 @@ class ptx(design.design):
return self.tx_width/drc("minwidth_tx") return self.tx_width/drc("minwidth_tx")
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds ptx edges to graph. Lowest graph level.""" """Adds edges based on inputs/outputs. Overrides base class function."""
#The ptx has four connections: (S)ource, (G)ate, (D)rain, (B)ody. The positions in spice self.add_graph_edges(graph, port_nets)
#are hardcoded which is represented here as well.
#Edges are connected as follows: G->S, G->D, D<->S. Body not represented in graph.
if len(port_nets) != 4:
debug.error("Transistor has non-standard connections.",1)
graph.add_edge(port_nets[1],port_nets[0])
graph.add_edge(port_nets[1],port_nets[2])
graph.add_edge(port_nets[0],port_nets[2])
graph.add_edge(port_nets[2],port_nets[0])