mirror of https://github.com/VLSIDA/OpenRAM.git
Merged with dev
This commit is contained in:
commit
84ba5c55d1
|
|
@ -17,7 +17,7 @@ class channel_net():
|
||||||
self.name = net_name
|
self.name = net_name
|
||||||
self.pins = pins
|
self.pins = pins
|
||||||
self.vertical = vertical
|
self.vertical = vertical
|
||||||
|
|
||||||
# Keep track of the internval
|
# Keep track of the internval
|
||||||
if vertical:
|
if vertical:
|
||||||
self.min_value = min(i.by() for i in pins)
|
self.min_value = min(i.by() for i in pins)
|
||||||
|
|
@ -25,34 +25,34 @@ class channel_net():
|
||||||
else:
|
else:
|
||||||
self.min_value = min(i.lx() for i in pins)
|
self.min_value = min(i.lx() for i in pins)
|
||||||
self.max_value = max(i.rx() for i in pins)
|
self.max_value = max(i.rx() for i in pins)
|
||||||
|
|
||||||
# Keep track of the conflicts
|
# Keep track of the conflicts
|
||||||
self.conflicts = []
|
self.conflicts = []
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
return self.min_value < other.min_value
|
return self.min_value < other.min_value
|
||||||
|
|
||||||
def pin_overlap(self, pin1, pin2, pitch):
|
def pin_overlap(self, pin1, pin2, pitch):
|
||||||
""" Check for vertical or horizontal overlap of the two pins """
|
""" Check for vertical or horizontal overlap of the two pins """
|
||||||
|
|
||||||
# FIXME: If the pins are not in a row, this may break.
|
# FIXME: If the pins are not in a row, this may break.
|
||||||
# However, a top pin shouldn't overlap another top pin,
|
# However, a top pin shouldn't overlap another top pin,
|
||||||
# for example, so the extra comparison *shouldn't* matter.
|
# for example, so the extra comparison *shouldn't* matter.
|
||||||
|
|
||||||
# Pin 1 must be in the "BOTTOM" set
|
# Pin 1 must be in the "BOTTOM" set
|
||||||
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
|
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
|
||||||
|
|
||||||
# Pin 1 must be in the "LEFT" set
|
# Pin 1 must be in the "LEFT" set
|
||||||
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
|
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
|
||||||
overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap)
|
overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap)
|
||||||
return overlaps
|
return overlaps
|
||||||
|
|
||||||
def pins_overlap(self, other, pitch):
|
def pins_overlap(self, other, pitch):
|
||||||
"""
|
"""
|
||||||
Check all the pin pairs on two nets and return a pin
|
Check all the pin pairs on two nets and return a pin
|
||||||
|
|
@ -73,8 +73,8 @@ class channel_net():
|
||||||
min_overlap = self.min_value >= other.min_value and self.min_value <= other.max_value
|
min_overlap = self.min_value >= other.min_value and self.min_value <= other.max_value
|
||||||
max_overlap = self.max_value >= other.min_value and self.max_value <= other.max_value
|
max_overlap = self.max_value >= other.min_value and self.max_value <= other.max_value
|
||||||
return min_overlap or max_overlap
|
return min_overlap or max_overlap
|
||||||
|
|
||||||
|
|
||||||
class channel_route(design.design):
|
class channel_route(design.design):
|
||||||
|
|
||||||
unique_id = 0
|
unique_id = 0
|
||||||
|
|
@ -98,7 +98,7 @@ class channel_route(design.design):
|
||||||
name = "cr_{0}".format(channel_route.unique_id)
|
name = "cr_{0}".format(channel_route.unique_id)
|
||||||
channel_route.unique_id += 1
|
channel_route.unique_id += 1
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.netlist = netlist
|
self.netlist = netlist
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
self.layer_stack = layer_stack
|
self.layer_stack = layer_stack
|
||||||
|
|
@ -106,7 +106,7 @@ class channel_route(design.design):
|
||||||
self.vertical = vertical
|
self.vertical = vertical
|
||||||
# For debugging...
|
# For debugging...
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
if not directions or directions == "pref":
|
if not directions or directions == "pref":
|
||||||
# Use the preferred layer directions
|
# Use the preferred layer directions
|
||||||
if self.get_preferred_direction(layer_stack[0]) == "V":
|
if self.get_preferred_direction(layer_stack[0]) == "V":
|
||||||
|
|
@ -154,7 +154,7 @@ class channel_route(design.design):
|
||||||
if pin in conflicts:
|
if pin in conflicts:
|
||||||
g[other_pin].remove(pin)
|
g[other_pin].remove(pin)
|
||||||
return g
|
return g
|
||||||
|
|
||||||
def route(self):
|
def route(self):
|
||||||
# Create names for the nets for the graphs
|
# Create names for the nets for the graphs
|
||||||
nets = []
|
nets = []
|
||||||
|
|
@ -180,7 +180,7 @@ class channel_route(design.design):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
hcg[net2.name] = set([net1.name])
|
hcg[net2.name] = set([net1.name])
|
||||||
|
|
||||||
|
|
||||||
# Initialize the vertical conflict graph (vcg)
|
# Initialize the vertical conflict graph (vcg)
|
||||||
# and make a list of all pins
|
# and make a list of all pins
|
||||||
vcg = collections.OrderedDict()
|
vcg = collections.OrderedDict()
|
||||||
|
|
@ -204,12 +204,12 @@ class channel_route(design.design):
|
||||||
# Skip yourself
|
# Skip yourself
|
||||||
if net1.name == net2.name:
|
if net1.name == net2.name:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if net1.pins_overlap(net2, pitch):
|
if net1.pins_overlap(net2, pitch):
|
||||||
vcg[net2.name].add(net1.name)
|
vcg[net2.name].add(net1.name)
|
||||||
|
|
||||||
# Check if there are any cycles net1 <---> net2 in the VCG
|
# Check if there are any cycles net1 <---> net2 in the VCG
|
||||||
|
|
||||||
|
|
||||||
# Some of the pins may be to the left/below the channel offset,
|
# Some of the pins may be to the left/below the channel offset,
|
||||||
# so adjust if this is the case
|
# so adjust if this is the case
|
||||||
|
|
@ -226,7 +226,7 @@ class channel_route(design.design):
|
||||||
while len(nets) > 0:
|
while len(nets) > 0:
|
||||||
|
|
||||||
current_offset_value = current_offset.y if self.vertical else current_offset.x
|
current_offset_value = current_offset.y if self.vertical else current_offset.x
|
||||||
|
|
||||||
# from pprint import pformat
|
# from pprint import pformat
|
||||||
# print("VCG:\n", pformat(vcg))
|
# print("VCG:\n", pformat(vcg))
|
||||||
# for name,net in vcg.items():
|
# for name,net in vcg.items():
|
||||||
|
|
@ -253,7 +253,7 @@ class channel_route(design.design):
|
||||||
# Remove the net from other constriants in the VCG
|
# Remove the net from other constriants in the VCG
|
||||||
vcg = self.remove_net_from_graph(net.name, vcg)
|
vcg = self.remove_net_from_graph(net.name, vcg)
|
||||||
nets.remove(net)
|
nets.remove(net)
|
||||||
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# If we made a full pass and the offset didn't change...
|
# If we made a full pass and the offset didn't change...
|
||||||
|
|
@ -276,7 +276,7 @@ class channel_route(design.design):
|
||||||
current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, real_channel_offset.y)
|
current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, real_channel_offset.y)
|
||||||
else:
|
else:
|
||||||
current_offset = vector(real_channel_offset.x, current_offset.y + self.vertical_nonpref_pitch)
|
current_offset = vector(real_channel_offset.x, current_offset.y + self.vertical_nonpref_pitch)
|
||||||
|
|
||||||
# Return the size of the channel
|
# Return the size of the channel
|
||||||
if self.vertical:
|
if self.vertical:
|
||||||
self.width = current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
|
self.width = current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
|
||||||
|
|
@ -284,7 +284,7 @@ class channel_route(design.design):
|
||||||
else:
|
else:
|
||||||
self.width = self.max_value + self.horizontal_nonpref_pitch - self.offset.x
|
self.width = self.max_value + self.horizontal_nonpref_pitch - self.offset.x
|
||||||
self.height = current_offset.y + self.vertical_nonpref_pitch - self.offset.y
|
self.height = current_offset.y + self.vertical_nonpref_pitch - self.offset.y
|
||||||
|
|
||||||
def get_layer_pitch(self, layer):
|
def get_layer_pitch(self, layer):
|
||||||
""" Return the track pitch on a given layer """
|
""" Return the track pitch on a given layer """
|
||||||
try:
|
try:
|
||||||
|
|
@ -307,10 +307,10 @@ class channel_route(design.design):
|
||||||
"""
|
"""
|
||||||
max_x = max([pin.center().x for pin in pins])
|
max_x = max([pin.center().x for pin in pins])
|
||||||
min_x = min([pin.center().x for pin in pins])
|
min_x = min([pin.center().x for pin in pins])
|
||||||
|
|
||||||
# if we are less than a pitch, just create a non-preferred layer jog
|
# if we are less than a pitch, just create a non-preferred layer jog
|
||||||
non_preferred_route = max_x - min_x <= pitch
|
non_preferred_route = max_x - min_x <= pitch
|
||||||
|
|
||||||
if non_preferred_route:
|
if non_preferred_route:
|
||||||
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
|
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
|
||||||
# Add the horizontal trunk on the vertical layer!
|
# Add the horizontal trunk on the vertical layer!
|
||||||
|
|
@ -324,7 +324,7 @@ class channel_route(design.design):
|
||||||
pin_pos = pin.uc()
|
pin_pos = pin.uc()
|
||||||
else:
|
else:
|
||||||
pin_pos = pin.bc()
|
pin_pos = pin.bc()
|
||||||
|
|
||||||
# No bend needed here
|
# No bend needed here
|
||||||
mid = vector(pin_pos.x, trunk_offset.y)
|
mid = vector(pin_pos.x, trunk_offset.y)
|
||||||
self.add_path(self.vertical_layer, [pin_pos, mid])
|
self.add_path(self.vertical_layer, [pin_pos, mid])
|
||||||
|
|
@ -361,10 +361,10 @@ class channel_route(design.design):
|
||||||
"""
|
"""
|
||||||
max_y = max([pin.center().y for pin in pins])
|
max_y = max([pin.center().y for pin in pins])
|
||||||
min_y = min([pin.center().y for pin in pins])
|
min_y = min([pin.center().y for pin in pins])
|
||||||
|
|
||||||
# if we are less than a pitch, just create a non-preferred layer jog
|
# if we are less than a pitch, just create a non-preferred layer jog
|
||||||
non_preferred_route = max_y - min_y <= pitch
|
non_preferred_route = max_y - min_y <= pitch
|
||||||
|
|
||||||
if non_preferred_route:
|
if non_preferred_route:
|
||||||
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
|
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
|
||||||
# Add the vertical trunk on the horizontal layer!
|
# Add the vertical trunk on the horizontal layer!
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
implant_type=None, well_type=None, name=""):
|
implant_type=None, well_type=None, name=""):
|
||||||
# This will ignore the name parameter since
|
# This will ignore the name parameter since
|
||||||
# we can guarantee a unique name here
|
# we can guarantee a unique name here
|
||||||
|
|
||||||
super().__init__(name)
|
super().__init__(name, name)
|
||||||
debug.info(4, "create contact object {0}".format(name))
|
debug.info(4, "create contact object {0}".format(name))
|
||||||
|
|
||||||
self.add_comment("layers: {0}".format(layer_stack))
|
self.add_comment("layers: {0}".format(layer_stack))
|
||||||
|
|
@ -51,8 +51,8 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
|
|
||||||
# Non-preferred directions
|
# Non-preferred directions
|
||||||
if directions == "nonpref":
|
if directions == "nonpref":
|
||||||
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V"
|
first_dir = "H" if tech.preferred_directions[layer_stack[0]]=="V" else "V"
|
||||||
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V"
|
second_dir = "H" if tech.preferred_directions[layer_stack[2]]=="V" else "V"
|
||||||
self.directions = (first_dir, second_dir)
|
self.directions = (first_dir, second_dir)
|
||||||
# Preferred directions
|
# Preferred directions
|
||||||
elif directions == "pref":
|
elif directions == "pref":
|
||||||
|
|
@ -80,7 +80,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
self.create_first_layer_enclosure()
|
self.create_first_layer_enclosure()
|
||||||
self.create_second_layer_enclosure()
|
self.create_second_layer_enclosure()
|
||||||
self.create_nitride_cut_enclosure()
|
self.create_nitride_cut_enclosure()
|
||||||
|
|
||||||
self.height = max(self.first_layer_position.y + self.first_layer_height,
|
self.height = max(self.first_layer_position.y + self.first_layer_height,
|
||||||
self.second_layer_position.y + self.second_layer_height)
|
self.second_layer_position.y + self.second_layer_height)
|
||||||
self.width = max(self.first_layer_position.x + self.first_layer_width,
|
self.width = max(self.first_layer_position.x + self.first_layer_width,
|
||||||
|
|
@ -99,7 +99,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
(first_layer, via_layer, second_layer) = self.layer_stack
|
(first_layer, via_layer, second_layer) = self.layer_stack
|
||||||
self.first_layer_name = first_layer
|
self.first_layer_name = first_layer
|
||||||
self.second_layer_name = second_layer
|
self.second_layer_name = second_layer
|
||||||
|
|
||||||
# Contacts will have unique per first layer
|
# Contacts will have unique per first layer
|
||||||
if via_layer in tech.layer:
|
if via_layer in tech.layer:
|
||||||
self.via_layer_name = via_layer
|
self.via_layer_name = via_layer
|
||||||
|
|
@ -115,7 +115,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
""" Determine the design rules for the enclosure layers """
|
""" Determine the design rules for the enclosure layers """
|
||||||
|
|
||||||
self.contact_width = drc("minwidth_{0}". format(self.via_layer_name))
|
self.contact_width = drc("minwidth_{0}". format(self.via_layer_name))
|
||||||
contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name))
|
contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name))
|
||||||
self.contact_pitch = self.contact_width + contact_to_contact
|
self.contact_pitch = self.contact_width + contact_to_contact
|
||||||
|
|
@ -126,7 +126,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
# DRC rules
|
# DRC rules
|
||||||
# The extend rule applies to asymmetric enclosures in one direction.
|
# The extend rule applies to asymmetric enclosures in one direction.
|
||||||
# The enclosure rule applies to symmetric enclosure component.
|
# The enclosure rule applies to symmetric enclosure component.
|
||||||
|
|
||||||
self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
|
self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
|
||||||
self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name))
|
self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name))
|
||||||
# If there's a different rule for active
|
# If there's a different rule for active
|
||||||
|
|
@ -171,7 +171,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
(self.second_layer_minwidth - self.contact_array_width) / 2)
|
(self.second_layer_minwidth - self.contact_array_width) / 2)
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1)
|
debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1)
|
||||||
|
|
||||||
def create_contact_array(self):
|
def create_contact_array(self):
|
||||||
""" Create the contact array at the origin"""
|
""" Create the contact array at the origin"""
|
||||||
# offset for the via array
|
# offset for the via array
|
||||||
|
|
@ -210,7 +210,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
offset=self.second_layer_position - npc_enclose_offset,
|
offset=self.second_layer_position - npc_enclose_offset,
|
||||||
width=self.second_layer_width + 2 * npc_enclose_poly,
|
width=self.second_layer_width + 2 * npc_enclose_poly,
|
||||||
height=self.second_layer_height + 2 * npc_enclose_poly)
|
height=self.second_layer_height + 2 * npc_enclose_poly)
|
||||||
|
|
||||||
def create_first_layer_enclosure(self):
|
def create_first_layer_enclosure(self):
|
||||||
# this is if the first and second layers are different
|
# this is if the first and second layers are different
|
||||||
self.first_layer_position = vector(
|
self.first_layer_position = vector(
|
||||||
|
|
@ -269,12 +269,12 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
offset=well_position,
|
offset=well_position,
|
||||||
width=self.well_width,
|
width=self.well_width,
|
||||||
height=self.well_height)
|
height=self.well_height)
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
""" Get total power of a module """
|
""" Get total power of a module """
|
||||||
return self.return_power()
|
return self.return_power()
|
||||||
|
|
||||||
|
|
||||||
# Set up a static for each layer to be used for measurements
|
# Set up a static for each layer to be used for measurements
|
||||||
for layer_stack in tech.layer_stacks:
|
for layer_stack in tech.layer_stacks:
|
||||||
(layer1, via, layer2) = layer_stack
|
(layer1, via, layer2) = layer_stack
|
||||||
|
|
@ -295,7 +295,7 @@ if "nwell" in tech.layer:
|
||||||
well_type="n")
|
well_type="n")
|
||||||
module = sys.modules[__name__]
|
module = sys.modules[__name__]
|
||||||
setattr(module, "nwell_contact", cont)
|
setattr(module, "nwell_contact", cont)
|
||||||
|
|
||||||
if "pwell" in tech.layer:
|
if "pwell" in tech.layer:
|
||||||
cont = factory.create(module_type="contact",
|
cont = factory.create(module_type="contact",
|
||||||
layer_stack=tech.active_stack,
|
layer_stack=tech.active_stack,
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,15 @@
|
||||||
#
|
#
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class _pins:
|
class _pins:
|
||||||
def __init__(self, pin_dict):
|
def __init__(self, pin_dict):
|
||||||
# make the pins elements of the class to allow "." access.
|
# make the pins elements of the class to allow "." access.
|
||||||
# For example: props.bitcell.cell_6t.pin.bl = "foobar"
|
# For example: props.bitcell.cell_6t.pin.bl = "foobar"
|
||||||
for k,v in pin_dict.items():
|
for k, v in pin_dict.items():
|
||||||
self.__dict__[k] = v
|
self.__dict__[k] = v
|
||||||
|
|
||||||
|
|
||||||
class _cell:
|
class _cell:
|
||||||
def __init__(self, pin_dict):
|
def __init__(self, pin_dict):
|
||||||
pin_dict.update(self._default_power_pins())
|
pin_dict.update(self._default_power_pins())
|
||||||
|
|
@ -24,13 +26,27 @@ class _cell:
|
||||||
return self._pins
|
return self._pins
|
||||||
|
|
||||||
def _default_power_pins(self):
|
def _default_power_pins(self):
|
||||||
return { 'vdd' : 'vdd', 'gnd' : 'gnd' }
|
return {'vdd': 'vdd',
|
||||||
|
'gnd': 'gnd'}
|
||||||
|
|
||||||
|
|
||||||
class _mirror_axis:
|
class _mirror_axis:
|
||||||
def __init__(self, x, y):
|
def __init__(self, x, y):
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
|
|
||||||
|
|
||||||
|
class _ptx:
|
||||||
|
def __init__(self, model_is_subckt, bin_spice_models):
|
||||||
|
self.model_is_subckt = model_is_subckt
|
||||||
|
self.bin_spice_models = bin_spice_models
|
||||||
|
|
||||||
|
|
||||||
|
class _pgate:
|
||||||
|
def __init__(self, add_implants):
|
||||||
|
self.add_implants = add_implants
|
||||||
|
|
||||||
|
|
||||||
class _bitcell:
|
class _bitcell:
|
||||||
def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r):
|
def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r):
|
||||||
self.mirror = mirror
|
self.mirror = mirror
|
||||||
|
|
@ -42,27 +58,27 @@ class _bitcell:
|
||||||
def _default():
|
def _default():
|
||||||
axis = _mirror_axis(True, False)
|
axis = _mirror_axis(True, False)
|
||||||
|
|
||||||
cell_s8_6t = _cell({'bl' : 'bl',
|
cell_s8_6t = _cell({'bl': 'bl',
|
||||||
'br' : 'br',
|
'br': 'br',
|
||||||
'wl': 'wl'})
|
'wl': 'wl'})
|
||||||
|
|
||||||
cell_6t = _cell({'bl' : 'bl',
|
cell_6t = _cell({'bl': 'bl',
|
||||||
'br' : 'br',
|
'br': 'br',
|
||||||
'wl' : 'wl'})
|
'wl': 'wl'})
|
||||||
|
|
||||||
cell_1rw1r = _cell({'bl0' : 'bl0',
|
cell_1rw1r = _cell({'bl0': 'bl0',
|
||||||
'br0' : 'br0',
|
'br0': 'br0',
|
||||||
'bl1' : 'bl1',
|
'bl1': 'bl1',
|
||||||
'br1' : 'br1',
|
'br1': 'br1',
|
||||||
'wl0' : 'wl0',
|
'wl0': 'wl0',
|
||||||
'wl1' : 'wl1'})
|
'wl1': 'wl1'})
|
||||||
|
|
||||||
cell_1w1r = _cell({'bl0' : 'bl0',
|
cell_1w1r = _cell({'bl0': 'bl0',
|
||||||
'br0' : 'br0',
|
'br0': 'br0',
|
||||||
'bl1' : 'bl1',
|
'bl1': 'bl1',
|
||||||
'br1' : 'br1',
|
'br1': 'br1',
|
||||||
'wl0' : 'wl0',
|
'wl0': 'wl0',
|
||||||
'wl1' : 'wl1'})
|
'wl1': 'wl1'})
|
||||||
|
|
||||||
return _bitcell(cell_s8_6t=cell_s8_6t,
|
return _bitcell(cell_s8_6t=cell_s8_6t,
|
||||||
cell_6t=cell_6t,
|
cell_6t=cell_6t,
|
||||||
|
|
@ -94,21 +110,25 @@ class _dff:
|
||||||
self.custom_type_list = custom_type_list
|
self.custom_type_list = custom_type_list
|
||||||
self.clk_pin = clk_pin
|
self.clk_pin = clk_pin
|
||||||
|
|
||||||
|
|
||||||
class _dff_buff:
|
class _dff_buff:
|
||||||
def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts):
|
def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts):
|
||||||
self.use_custom_ports = use_custom_ports
|
self.use_custom_ports = use_custom_ports
|
||||||
self.buf_ports = custom_buff_ports
|
self.buf_ports = custom_buff_ports
|
||||||
self.add_body_contacts = add_body_contacts
|
self.add_body_contacts = add_body_contacts
|
||||||
|
|
||||||
|
|
||||||
class _dff_buff_array:
|
class _dff_buff_array:
|
||||||
def __init__(self, use_custom_ports, add_body_contacts):
|
def __init__(self, use_custom_ports, add_body_contacts):
|
||||||
self.use_custom_ports = use_custom_ports
|
self.use_custom_ports = use_custom_ports
|
||||||
self.add_body_contacts = add_body_contacts
|
self.add_body_contacts = add_body_contacts
|
||||||
|
|
||||||
|
|
||||||
class _bitcell_array:
|
class _bitcell_array:
|
||||||
def __init__(self, use_custom_cell_arrangement):
|
def __init__(self, use_custom_cell_arrangement):
|
||||||
self.use_custom_cell_arrangement = use_custom_cell_arrangement
|
self.use_custom_cell_arrangement = use_custom_cell_arrangement
|
||||||
|
|
||||||
|
|
||||||
class cell_properties():
|
class cell_properties():
|
||||||
"""
|
"""
|
||||||
This contains meta information about the custom designed cells. For
|
This contains meta information about the custom designed cells. For
|
||||||
|
|
@ -117,41 +137,69 @@ class cell_properties():
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.names = {}
|
self.names = {}
|
||||||
|
self.names["bitcell"] = "cell_6t"
|
||||||
|
self.names["bitcell_1rw_1r"] = "cell_1rw_1r"
|
||||||
|
self.names["bitcell_1w_1r"] = "cell_1w_1r"
|
||||||
|
self.names["dummy_bitcell"] = "dummy_cell_6t"
|
||||||
|
self.names["dummy_bitcell_1rw_1r"] = "dummy_cell_1rw_1r"
|
||||||
|
self.names["dummy_bitcell_1w_1r"] = "dummy_cell_1w_1r"
|
||||||
|
self.names["replica_bitcell"] = "replica_cell_6t"
|
||||||
|
self.names["replica_bitcell_1rw_1r"] = "replica_cell_1rw_1r"
|
||||||
|
self.names["replica_bitcell_1w_1r"] = "replica_cell_1w_1r"
|
||||||
|
self.names["col_cap_bitcell_6t"] = "col_cap_cell_6t"
|
||||||
|
self.names["col_cap_bitcell_1rw_1r"] = "col_cap_cell_1rw_1r"
|
||||||
|
self.names["col_cap_bitcell_1w_1r"] = "col_cap_cell_1w_1r"
|
||||||
|
self.names["row_cap_bitcell_6t"] = "row_cap_cell_6t"
|
||||||
|
self.names["row_cap_bitcell_1rw_1r"] = "row_cap_cell_1rw_1r"
|
||||||
|
self.names["row_cap_bitcell_1w_1r"] = "row_cap_cell_1w_1r"
|
||||||
|
|
||||||
self._bitcell = _bitcell._default()
|
self._bitcell = _bitcell._default()
|
||||||
|
|
||||||
self._dff = _dff(use_custom_ports = False,
|
|
||||||
custom_port_list = ["D", "Q", "clk", "vdd", "gnd"],
|
|
||||||
custom_type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
|
|
||||||
clk_pin= "clk")
|
|
||||||
|
|
||||||
self._dff_buff = _dff_buff(use_custom_ports = False,
|
|
||||||
custom_buff_ports = ["D", "qint", "clk", "vdd", "gnd"],
|
|
||||||
add_body_contacts = False)
|
|
||||||
|
|
||||||
self._dff_buff_array = _dff_buff_array(use_custom_ports = False,
|
self._ptx = _ptx(model_is_subckt=False,
|
||||||
add_body_contacts = False)
|
bin_spice_models=False)
|
||||||
|
|
||||||
|
self._pgate = _pgate(add_implants=False)
|
||||||
|
|
||||||
|
self._dff = _dff(use_custom_ports=False,
|
||||||
|
custom_port_list=["D", "Q", "clk", "vdd", "gnd"],
|
||||||
|
custom_type_list=["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
|
||||||
|
clk_pin="clk")
|
||||||
|
|
||||||
|
self._dff_buff = _dff_buff(use_custom_ports=False,
|
||||||
|
custom_buff_ports=["D", "qint", "clk", "vdd", "gnd"],
|
||||||
|
add_body_contacts=False)
|
||||||
|
|
||||||
|
self._dff_buff_array = _dff_buff_array(use_custom_ports=False,
|
||||||
|
add_body_contacts=False)
|
||||||
|
|
||||||
self._write_driver = _cell({'din': 'din',
|
self._write_driver = _cell({'din': 'din',
|
||||||
'bl' : 'bl',
|
'bl': 'bl',
|
||||||
'br' : 'br',
|
'br': 'br',
|
||||||
'en' : 'en'})
|
'en': 'en'})
|
||||||
|
|
||||||
self._sense_amp = _cell({'bl' : 'bl',
|
self._sense_amp = _cell({'bl': 'bl',
|
||||||
'br' : 'br',
|
'br': 'br',
|
||||||
'dout' : 'dout',
|
'dout': 'dout',
|
||||||
'en' : 'en'})
|
'en': 'en'})
|
||||||
|
|
||||||
self._bitcell_array = _bitcell_array(use_custom_cell_arrangement = [])
|
self._bitcell_array = _bitcell_array(use_custom_cell_arrangement=[])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bitcell(self):
|
def bitcell(self):
|
||||||
return self._bitcell
|
return self._bitcell
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ptx(self):
|
||||||
|
return self._ptx
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pgate(self):
|
||||||
|
return self._pgate
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dff(self):
|
def dff(self):
|
||||||
return self._dff
|
return self._dff
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dff_buff(self):
|
def dff_buff(self):
|
||||||
return self._dff_buff
|
return self._dff_buff
|
||||||
|
|
@ -167,7 +215,7 @@ class cell_properties():
|
||||||
@property
|
@property
|
||||||
def sense_amp(self):
|
def sense_amp(self):
|
||||||
return self._sense_amp
|
return self._sense_amp
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bitcell_array(self):
|
def bitcell_array(self):
|
||||||
return self._bitcell_array
|
return self._bitcell_array
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,193 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2020 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class _bank:
|
||||||
|
def __init__(self, stack, pitch):
|
||||||
|
# bank
|
||||||
|
# column address route: stack, pitch
|
||||||
|
# m1_stack, m2_pitch (default)
|
||||||
|
# m2_stack, m3_pitch (sky130)
|
||||||
|
self.stack = stack
|
||||||
|
self.pitch = pitch
|
||||||
|
|
||||||
|
|
||||||
|
class _hierarchical_decoder:
|
||||||
|
def __init__(self,
|
||||||
|
bus_layer,
|
||||||
|
bus_directions,
|
||||||
|
input_layer,
|
||||||
|
output_layer,
|
||||||
|
vertical_supply):
|
||||||
|
# hierarchical_decoder
|
||||||
|
# bus_layer, bus_directions, bus_pitch, bus_space, input_layer, output_layer, output_layer_pitch
|
||||||
|
# m2, pref, m2_pitch, m2_space, m1, m3, m3_pitch
|
||||||
|
# m1, nonpref, m1_pitch, m2_space, m2, li, li_pitch (sky130)
|
||||||
|
#
|
||||||
|
# vertical vdd/gnd
|
||||||
|
# special jogging
|
||||||
|
self.bus_layer = bus_layer
|
||||||
|
self.bus_directions = bus_directions
|
||||||
|
self.input_layer = input_layer
|
||||||
|
self.output_layer = output_layer
|
||||||
|
self.vertical_supply = vertical_supply
|
||||||
|
|
||||||
|
|
||||||
|
class _hierarchical_predecode:
|
||||||
|
def __init__(self,
|
||||||
|
bus_layer,
|
||||||
|
bus_directions,
|
||||||
|
bus_space_factor,
|
||||||
|
input_layer,
|
||||||
|
output_layer,
|
||||||
|
vertical_supply):
|
||||||
|
# hierarchical_predecode
|
||||||
|
# bus_layer, bus_directions, bus_pitch, bus_space, input_layer, output_layer, output_layer_pitch
|
||||||
|
# m2, pref, m2_pitch, m2_space, m1, m1, m1_pitch
|
||||||
|
# m1, nonpref, m1_pitch, 1`.5*m1_space, m2, li, li_pitch (sky130)
|
||||||
|
#
|
||||||
|
# vertical vdd/gnd
|
||||||
|
# special jogging
|
||||||
|
self.bus_layer = bus_layer
|
||||||
|
self.bus_directions = bus_directions
|
||||||
|
self.bus_space_factor = bus_space_factor
|
||||||
|
self.input_layer = input_layer
|
||||||
|
self.output_layer = output_layer
|
||||||
|
self.vertical_supply = vertical_supply
|
||||||
|
|
||||||
|
|
||||||
|
class _column_mux_array:
|
||||||
|
def __init__(self,
|
||||||
|
select_layer,
|
||||||
|
select_pitch,
|
||||||
|
bitline_layer):
|
||||||
|
# column_mux_array
|
||||||
|
# sel_layer, sel_pitch, bitline_layer
|
||||||
|
# m1, m2_pitch, m2
|
||||||
|
# m3, m3_pitch, m1 (sky130)
|
||||||
|
self.select_layer = select_layer
|
||||||
|
self.select_pitch= select_pitch
|
||||||
|
self.bitline_layer = bitline_layer
|
||||||
|
|
||||||
|
|
||||||
|
class _port_address:
|
||||||
|
def __init__(self,
|
||||||
|
supply_offset):
|
||||||
|
# port_adress
|
||||||
|
# special supply offset
|
||||||
|
self.supply_offset = supply_offset
|
||||||
|
|
||||||
|
|
||||||
|
class _port_data:
|
||||||
|
def __init__(self,
|
||||||
|
channel_route_bitlines,
|
||||||
|
enable_layer):
|
||||||
|
# port_data
|
||||||
|
# connect bitlines instead of chanel route
|
||||||
|
|
||||||
|
# sense_amp_array
|
||||||
|
# en_layer
|
||||||
|
# m1
|
||||||
|
# m3 (sky130)
|
||||||
|
|
||||||
|
# precharge_array
|
||||||
|
# en_bar_layer
|
||||||
|
# m1
|
||||||
|
# m3 (sky130)
|
||||||
|
self.channel_route_bitlines = channel_route_bitlines
|
||||||
|
self.enable_layer = enable_layer
|
||||||
|
|
||||||
|
|
||||||
|
class _replica_column:
|
||||||
|
def __init__(self,
|
||||||
|
even_rows):
|
||||||
|
# replica_column
|
||||||
|
# even row check (sky130)
|
||||||
|
self.even_rows = even_rows
|
||||||
|
|
||||||
|
|
||||||
|
class _wordline_driver:
|
||||||
|
def __init__(self,
|
||||||
|
vertical_supply):
|
||||||
|
# wordline_buffer_array
|
||||||
|
# vertical vdd/gnd (sky130)
|
||||||
|
# wordline_driver_array
|
||||||
|
# vertical vdd/gnd (sky130)
|
||||||
|
# wordline_driver
|
||||||
|
# vertical vdd/gnd (sky130)
|
||||||
|
self.vertical_supply = vertical_supply
|
||||||
|
|
||||||
|
|
||||||
|
class layer_properties():
|
||||||
|
"""
|
||||||
|
This contains meta information about the module routing layers. These
|
||||||
|
can be overriden in the tech.py file.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self._bank = _bank(stack="m1_stack",
|
||||||
|
pitch="m2_pitch")
|
||||||
|
|
||||||
|
self._hierarchical_decoder = _hierarchical_decoder(bus_layer="m2",
|
||||||
|
bus_directions="pref",
|
||||||
|
input_layer="m1",
|
||||||
|
output_layer="m3",
|
||||||
|
vertical_supply=False)
|
||||||
|
|
||||||
|
self._hierarchical_predecode = _hierarchical_predecode(bus_layer="m2",
|
||||||
|
bus_directions="pref",
|
||||||
|
bus_space_factor=1,
|
||||||
|
input_layer="m1",
|
||||||
|
output_layer="m1",
|
||||||
|
vertical_supply=False)
|
||||||
|
|
||||||
|
self._column_mux_array = _column_mux_array(select_layer="m1",
|
||||||
|
select_pitch="m2_pitch",
|
||||||
|
bitline_layer="m2")
|
||||||
|
|
||||||
|
self._port_address = _port_address(supply_offset=False)
|
||||||
|
|
||||||
|
self._port_data = _port_data(channel_route_bitlines=True,
|
||||||
|
enable_layer="m1")
|
||||||
|
|
||||||
|
self._replica_column = _replica_column(even_rows=False)
|
||||||
|
|
||||||
|
self._wordline_driver = _wordline_driver(vertical_supply=False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bank(self):
|
||||||
|
return self._bank
|
||||||
|
|
||||||
|
@property
|
||||||
|
def column_mux_array(self):
|
||||||
|
return self._column_mux_array
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hierarchical_decoder(self):
|
||||||
|
return self._hierarchical_decoder
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hierarchical_predecode(self):
|
||||||
|
return self._hierarchical_predecode
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port_address(self):
|
||||||
|
return self._port_address
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port_data(self):
|
||||||
|
return self._port_data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def replica_column(self):
|
||||||
|
return self._replica_column
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wordline_driver(self):
|
||||||
|
return self._wordline_driver
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ class delay_data():
|
||||||
assert isinstance(other, delay_data)
|
assert isinstance(other, delay_data)
|
||||||
return delay_data(other.delay + self.delay,
|
return delay_data(other.delay + self.delay,
|
||||||
self.slew)
|
self.slew)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,22 +8,29 @@
|
||||||
from hierarchy_design import hierarchy_design
|
from hierarchy_design import hierarchy_design
|
||||||
from utils import round_to_grid
|
from utils import round_to_grid
|
||||||
import contact
|
import contact
|
||||||
|
from tech import preferred_directions
|
||||||
|
from tech import cell_properties as props
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import re
|
import re
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
class design(hierarchy_design):
|
class design(hierarchy_design):
|
||||||
"""
|
"""
|
||||||
This is the same as the hierarchy_design class except it contains
|
This is the same as the hierarchy_design class except it contains
|
||||||
some DRC/layer constants and analytical models for other modules to reuse.
|
some DRC/layer constants and analytical models for other modules to reuse.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name, cell_name=None):
|
||||||
super().__init__(name)
|
# This allows us to use different GDS/spice circuits for hard cells instead of the default ones
|
||||||
|
# Except bitcell names are generated automatically by the globals.py setup_bitcells routines
|
||||||
self.setup_drc_constants()
|
# depending on the number of ports.
|
||||||
self.setup_layer_constants()
|
if name in props.names:
|
||||||
|
cell_name = props.names[name]
|
||||||
|
elif not cell_name:
|
||||||
|
cell_name = name
|
||||||
|
super().__init__(name, cell_name)
|
||||||
|
|
||||||
self.setup_multiport_constants()
|
self.setup_multiport_constants()
|
||||||
|
|
||||||
def check_pins(self):
|
def check_pins(self):
|
||||||
|
|
@ -31,106 +38,9 @@ class design(hierarchy_design):
|
||||||
pins = self.get_pins(pin_name)
|
pins = self.get_pins(pin_name)
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
print(pin_name, pin)
|
print(pin_name, pin)
|
||||||
|
|
||||||
def setup_layer_constants(self):
|
|
||||||
"""
|
|
||||||
These are some layer constants used
|
|
||||||
in many places in the compiler.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from tech import layer_indices
|
|
||||||
import tech
|
|
||||||
for layer in layer_indices:
|
|
||||||
key = "{}_stack".format(layer)
|
|
||||||
|
|
||||||
# Set the stack as a local helper
|
@classmethod
|
||||||
try:
|
def setup_drc_constants(design):
|
||||||
layer_stack = getattr(tech, key)
|
|
||||||
setattr(self, key, layer_stack)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Skip computing the pitch for active
|
|
||||||
if layer == "active":
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Add the pitch
|
|
||||||
setattr(self,
|
|
||||||
"{}_pitch".format(layer),
|
|
||||||
self.compute_pitch(layer, True))
|
|
||||||
|
|
||||||
# Add the non-preferrd pitch (which has vias in the "wrong" way)
|
|
||||||
setattr(self,
|
|
||||||
"{}_nonpref_pitch".format(layer),
|
|
||||||
self.compute_pitch(layer, False))
|
|
||||||
|
|
||||||
if False:
|
|
||||||
from tech import preferred_directions
|
|
||||||
print(preferred_directions)
|
|
||||||
from tech import layer, layer_indices
|
|
||||||
for name in layer_indices:
|
|
||||||
if name == "active":
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
print("{0} width {1} space {2}".format(name,
|
|
||||||
getattr(self, "{}_width".format(name)),
|
|
||||||
getattr(self, "{}_space".format(name))))
|
|
||||||
|
|
||||||
print("pitch {0} nonpref {1}".format(getattr(self, "{}_pitch".format(name)),
|
|
||||||
getattr(self, "{}_nonpref_pitch".format(name))))
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
import sys
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def compute_pitch(self, layer, preferred=True):
|
|
||||||
|
|
||||||
"""
|
|
||||||
This is the preferred direction pitch
|
|
||||||
i.e. we take the minimum or maximum contact dimension
|
|
||||||
"""
|
|
||||||
# Find the layer stacks this is used in
|
|
||||||
from tech import layer_stacks
|
|
||||||
pitches = []
|
|
||||||
for stack in layer_stacks:
|
|
||||||
# Compute the pitch with both vias above and below (if they exist)
|
|
||||||
if stack[0] == layer:
|
|
||||||
pitches.append(self.compute_layer_pitch(stack, preferred))
|
|
||||||
if stack[2] == layer:
|
|
||||||
pitches.append(self.compute_layer_pitch(stack[::-1], True))
|
|
||||||
|
|
||||||
return max(pitches)
|
|
||||||
|
|
||||||
def compute_layer_pitch(self, layer_stack, preferred):
|
|
||||||
|
|
||||||
(layer1, via, layer2) = layer_stack
|
|
||||||
try:
|
|
||||||
if layer1 == "poly" or layer1 == "active":
|
|
||||||
contact1 = getattr(contact, layer1 + "_contact")
|
|
||||||
else:
|
|
||||||
contact1 = getattr(contact, layer1 + "_via")
|
|
||||||
except AttributeError:
|
|
||||||
contact1 = getattr(contact, layer2 + "_via")
|
|
||||||
|
|
||||||
if preferred:
|
|
||||||
if self.get_preferred_direction(layer1) == "V":
|
|
||||||
contact_width = contact1.first_layer_width
|
|
||||||
else:
|
|
||||||
contact_width = contact1.first_layer_height
|
|
||||||
else:
|
|
||||||
if self.get_preferred_direction(layer1) == "V":
|
|
||||||
contact_width = contact1.first_layer_height
|
|
||||||
else:
|
|
||||||
contact_width = contact1.first_layer_width
|
|
||||||
layer_space = getattr(self, layer1 + "_space")
|
|
||||||
|
|
||||||
#print(layer_stack)
|
|
||||||
#print(contact1)
|
|
||||||
pitch = contact_width + layer_space
|
|
||||||
|
|
||||||
return round_to_grid(pitch)
|
|
||||||
|
|
||||||
def setup_drc_constants(self):
|
|
||||||
"""
|
"""
|
||||||
These are some DRC constants used in many places
|
These are some DRC constants used in many places
|
||||||
in the compiler.
|
in the compiler.
|
||||||
|
|
@ -142,85 +52,191 @@ class design(hierarchy_design):
|
||||||
match = re.search(r"minwidth_(.*)", rule)
|
match = re.search(r"minwidth_(.*)", rule)
|
||||||
if match:
|
if match:
|
||||||
if match.group(1) == "active_contact":
|
if match.group(1) == "active_contact":
|
||||||
setattr(self, "contact_width", drc(match.group(0)))
|
setattr(design, "contact_width", drc(match.group(0)))
|
||||||
else:
|
else:
|
||||||
setattr(self, match.group(1) + "_width", drc(match.group(0)))
|
setattr(design, match.group(1) + "_width", drc(match.group(0)))
|
||||||
|
|
||||||
# Single layer area rules
|
# Single layer area rules
|
||||||
match = re.search(r"minarea_(.*)", rule)
|
match = re.search(r"minarea_(.*)", rule)
|
||||||
if match:
|
if match:
|
||||||
setattr(self, match.group(0), drc(match.group(0)))
|
setattr(design, match.group(0), drc(match.group(0)))
|
||||||
|
|
||||||
# Single layer spacing rules
|
# Single layer spacing rules
|
||||||
match = re.search(r"(.*)_to_(.*)", rule)
|
match = re.search(r"(.*)_to_(.*)", rule)
|
||||||
if match and match.group(1) == match.group(2):
|
if match and match.group(1) == match.group(2):
|
||||||
setattr(self, match.group(1) + "_space", drc(match.group(0)))
|
setattr(design, match.group(1) + "_space", drc(match.group(0)))
|
||||||
elif match and match.group(1) != match.group(2):
|
elif match and match.group(1) != match.group(2):
|
||||||
if match.group(2) == "poly_active":
|
if match.group(2) == "poly_active":
|
||||||
setattr(self, match.group(1) + "_to_contact",
|
setattr(design, match.group(1) + "_to_contact",
|
||||||
drc(match.group(0)))
|
drc(match.group(0)))
|
||||||
else:
|
else:
|
||||||
setattr(self, match.group(0), drc(match.group(0)))
|
setattr(design, match.group(0), drc(match.group(0)))
|
||||||
|
|
||||||
match = re.search(r"(.*)_enclose_(.*)", rule)
|
match = re.search(r"(.*)_enclose_(.*)", rule)
|
||||||
if match:
|
if match:
|
||||||
setattr(self, match.group(0), drc(match.group(0)))
|
setattr(design, match.group(0), drc(match.group(0)))
|
||||||
|
|
||||||
match = re.search(r"(.*)_extend_(.*)", rule)
|
match = re.search(r"(.*)_extend_(.*)", rule)
|
||||||
if match:
|
if match:
|
||||||
setattr(self, match.group(0), drc(match.group(0)))
|
setattr(design, match.group(0), drc(match.group(0)))
|
||||||
|
|
||||||
# Create the maximum well extend active that gets used
|
# Create the maximum well extend active that gets used
|
||||||
# by cells to extend the wells for interaction with other cells
|
# by cells to extend the wells for interaction with other cells
|
||||||
from tech import layer
|
from tech import layer
|
||||||
self.well_extend_active = 0
|
design.well_extend_active = 0
|
||||||
if "nwell" in layer:
|
if "nwell" in layer:
|
||||||
self.well_extend_active = max(self.well_extend_active, self.nwell_extend_active)
|
design.well_extend_active = max(design.well_extend_active, design.nwell_extend_active)
|
||||||
if "pwell" in layer:
|
if "pwell" in layer:
|
||||||
self.well_extend_active = max(self.well_extend_active, self.pwell_extend_active)
|
design.well_extend_active = max(design.well_extend_active, design.pwell_extend_active)
|
||||||
|
|
||||||
# The active offset is due to the well extension
|
# The active offset is due to the well extension
|
||||||
if "pwell" in layer:
|
if "pwell" in layer:
|
||||||
self.pwell_enclose_active = drc("pwell_enclose_active")
|
design.pwell_enclose_active = drc("pwell_enclose_active")
|
||||||
else:
|
else:
|
||||||
self.pwell_enclose_active = 0
|
design.pwell_enclose_active = 0
|
||||||
if "nwell" in layer:
|
if "nwell" in layer:
|
||||||
self.nwell_enclose_active = drc("nwell_enclose_active")
|
design.nwell_enclose_active = drc("nwell_enclose_active")
|
||||||
else:
|
else:
|
||||||
self.nwell_enclose_active = 0
|
design.nwell_enclose_active = 0
|
||||||
# Use the max of either so that the poly gates will align properly
|
# Use the max of either so that the poly gates will align properly
|
||||||
self.well_enclose_active = max(self.pwell_enclose_active,
|
design.well_enclose_active = max(design.pwell_enclose_active,
|
||||||
self.nwell_enclose_active,
|
design.nwell_enclose_active,
|
||||||
self.active_space)
|
design.active_space)
|
||||||
|
|
||||||
# These are for debugging previous manual rules
|
# These are for debugging previous manual rules
|
||||||
if False:
|
if False:
|
||||||
print("poly_width", self.poly_width)
|
print("poly_width", design.poly_width)
|
||||||
print("poly_space", self.poly_space)
|
print("poly_space", design.poly_space)
|
||||||
print("m1_width", self.m1_width)
|
print("m1_width", design.m1_width)
|
||||||
print("m1_space", self.m1_space)
|
print("m1_space", design.m1_space)
|
||||||
print("m2_width", self.m2_width)
|
print("m2_width", design.m2_width)
|
||||||
print("m2_space", self.m2_space)
|
print("m2_space", design.m2_space)
|
||||||
print("m3_width", self.m3_width)
|
print("m3_width", design.m3_width)
|
||||||
print("m3_space", self.m3_space)
|
print("m3_space", design.m3_space)
|
||||||
print("m4_width", self.m4_width)
|
print("m4_width", design.m4_width)
|
||||||
print("m4_space", self.m4_space)
|
print("m4_space", design.m4_space)
|
||||||
print("active_width", self.active_width)
|
print("active_width", design.active_width)
|
||||||
print("active_space", self.active_space)
|
print("active_space", design.active_space)
|
||||||
print("contact_width", self.contact_width)
|
print("contact_width", design.contact_width)
|
||||||
print("poly_to_active", self.poly_to_active)
|
print("poly_to_active", design.poly_to_active)
|
||||||
print("poly_extend_active", self.poly_extend_active)
|
print("poly_extend_active", design.poly_extend_active)
|
||||||
print("poly_to_contact", self.poly_to_contact)
|
print("poly_to_contact", design.poly_to_contact)
|
||||||
print("active_contact_to_gate", self.active_contact_to_gate)
|
print("active_contact_to_gate", design.active_contact_to_gate)
|
||||||
print("poly_contact_to_gate", self.poly_contact_to_gate)
|
print("poly_contact_to_gate", design.poly_contact_to_gate)
|
||||||
print("well_enclose_active", self.well_enclose_active)
|
print("well_enclose_active", design.well_enclose_active)
|
||||||
print("implant_enclose_active", self.implant_enclose_active)
|
print("implant_enclose_active", design.implant_enclose_active)
|
||||||
print("implant_space", self.implant_space)
|
print("implant_space", design.implant_space)
|
||||||
import sys
|
import sys
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_layer_constants(design):
|
||||||
|
"""
|
||||||
|
These are some layer constants used
|
||||||
|
in many places in the compiler.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from tech import layer_indices
|
||||||
|
import tech
|
||||||
|
for layer in layer_indices:
|
||||||
|
key = "{}_stack".format(layer)
|
||||||
|
|
||||||
|
# Set the stack as a local helper
|
||||||
|
try:
|
||||||
|
layer_stack = getattr(tech, key)
|
||||||
|
setattr(design, key, layer_stack)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Skip computing the pitch for active
|
||||||
|
if layer == "active":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add the pitch
|
||||||
|
setattr(design,
|
||||||
|
"{}_pitch".format(layer),
|
||||||
|
design.compute_pitch(layer, True))
|
||||||
|
|
||||||
|
# Add the non-preferrd pitch (which has vias in the "wrong" way)
|
||||||
|
setattr(design,
|
||||||
|
"{}_nonpref_pitch".format(layer),
|
||||||
|
design.compute_pitch(layer, False))
|
||||||
|
|
||||||
|
if False:
|
||||||
|
from tech import preferred_directions
|
||||||
|
print(preferred_directions)
|
||||||
|
from tech import layer, layer_indices
|
||||||
|
for name in layer_indices:
|
||||||
|
if name == "active":
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
print("{0} width {1} space {2}".format(name,
|
||||||
|
getattr(design, "{}_width".format(name)),
|
||||||
|
getattr(design, "{}_space".format(name))))
|
||||||
|
|
||||||
|
print("pitch {0} nonpref {1}".format(getattr(design, "{}_pitch".format(name)),
|
||||||
|
getattr(design, "{}_nonpref_pitch".format(name))))
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
import sys
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def compute_pitch(layer, preferred=True):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is the preferred direction pitch
|
||||||
|
i.e. we take the minimum or maximum contact dimension
|
||||||
|
"""
|
||||||
|
# Find the layer stacks this is used in
|
||||||
|
from tech import layer_stacks
|
||||||
|
pitches = []
|
||||||
|
for stack in layer_stacks:
|
||||||
|
# Compute the pitch with both vias above and below (if they exist)
|
||||||
|
if stack[0] == layer:
|
||||||
|
pitches.append(design.compute_layer_pitch(stack, preferred))
|
||||||
|
if stack[2] == layer:
|
||||||
|
pitches.append(design.compute_layer_pitch(stack[::-1], True))
|
||||||
|
|
||||||
|
return max(pitches)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_preferred_direction(layer):
|
||||||
|
return preferred_directions[layer]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def compute_layer_pitch(layer_stack, preferred):
|
||||||
|
|
||||||
|
(layer1, via, layer2) = layer_stack
|
||||||
|
try:
|
||||||
|
if layer1 == "poly" or layer1 == "active":
|
||||||
|
contact1 = getattr(contact, layer1 + "_contact")
|
||||||
|
else:
|
||||||
|
contact1 = getattr(contact, layer1 + "_via")
|
||||||
|
except AttributeError:
|
||||||
|
contact1 = getattr(contact, layer2 + "_via")
|
||||||
|
|
||||||
|
if preferred:
|
||||||
|
if preferred_directions[layer1] == "V":
|
||||||
|
contact_width = contact1.first_layer_width
|
||||||
|
else:
|
||||||
|
contact_width = contact1.first_layer_height
|
||||||
|
else:
|
||||||
|
if preferred_directions[layer1] == "V":
|
||||||
|
contact_width = contact1.first_layer_height
|
||||||
|
else:
|
||||||
|
contact_width = contact1.first_layer_width
|
||||||
|
layer_space = getattr(design, layer1 + "_space")
|
||||||
|
|
||||||
|
#print(layer_stack)
|
||||||
|
#print(contact1)
|
||||||
|
pitch = contact_width + layer_space
|
||||||
|
|
||||||
|
return round_to_grid(pitch)
|
||||||
|
|
||||||
|
|
||||||
def setup_multiport_constants(self):
|
def setup_multiport_constants(self):
|
||||||
"""
|
"""
|
||||||
These are contants and lists that aid multiport design.
|
These are contants and lists that aid multiport design.
|
||||||
Ports are always in the order RW, W, R.
|
Ports are always in the order RW, W, R.
|
||||||
Port indices start from 0 and increment.
|
Port indices start from 0 and increment.
|
||||||
|
|
@ -258,11 +274,14 @@ class design(hierarchy_design):
|
||||||
self.read_ports.append(port_number)
|
self.read_ports.append(port_number)
|
||||||
self.readonly_ports.append(port_number)
|
self.readonly_ports.append(port_number)
|
||||||
port_number += 1
|
port_number += 1
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
""" Get total power of a module """
|
""" Get total power of a module """
|
||||||
total_module_power = self.return_power()
|
total_module_power = self.return_power()
|
||||||
for inst in self.insts:
|
for inst in self.insts:
|
||||||
total_module_power += inst.mod.analytical_power(corner, load)
|
total_module_power += inst.mod.analytical_power(corner, load)
|
||||||
return total_module_power
|
return total_module_power
|
||||||
|
|
||||||
|
design.setup_drc_constants()
|
||||||
|
design.setup_layer_constants()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ class geometry:
|
||||||
def center(self):
|
def center(self):
|
||||||
""" Return the center coordinate """
|
""" Return the center coordinate """
|
||||||
return vector(self.cx(), self.cy())
|
return vector(self.cx(), self.cy())
|
||||||
|
|
||||||
|
|
||||||
class instance(geometry):
|
class instance(geometry):
|
||||||
"""
|
"""
|
||||||
|
|
@ -227,7 +227,7 @@ class instance(geometry):
|
||||||
self.mod.gds_write_file(self.gds)
|
self.mod.gds_write_file(self.gds)
|
||||||
# now write an instance of my module/structure
|
# now write an instance of my module/structure
|
||||||
new_layout.addInstance(self.gds,
|
new_layout.addInstance(self.gds,
|
||||||
self.mod.name,
|
self.mod.cell_name,
|
||||||
offsetInMicrons=self.offset,
|
offsetInMicrons=self.offset,
|
||||||
mirror=self.mirror,
|
mirror=self.mirror,
|
||||||
rotate=self.rotate)
|
rotate=self.rotate)
|
||||||
|
|
@ -271,9 +271,9 @@ class instance(geometry):
|
||||||
p.transform(self.offset, self.mirror, self.rotate)
|
p.transform(self.offset, self.mirror, self.rotate)
|
||||||
new_pins.append(p)
|
new_pins.append(p)
|
||||||
return new_pins
|
return new_pins
|
||||||
|
|
||||||
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))
|
||||||
mRotate = np.array([[math.cos(angle),-math.sin(angle),0.0],
|
mRotate = np.array([[math.cos(angle),-math.sin(angle),0.0],
|
||||||
[math.sin(angle),math.cos(angle),0.0],
|
[math.sin(angle),math.cos(angle),0.0],
|
||||||
|
|
@ -285,7 +285,7 @@ class instance(geometry):
|
||||||
mTranslate = np.array([[1.0,0.0,translateX],
|
mTranslate = np.array([[1.0,0.0,translateX],
|
||||||
[0.0,1.0,translateY],
|
[0.0,1.0,translateY],
|
||||||
[0.0,0.0,1.0]])
|
[0.0,0.0,1.0]])
|
||||||
|
|
||||||
#set up the scale matrix (handles mirror X)
|
#set up the scale matrix (handles mirror X)
|
||||||
scaleX = 1.0
|
scaleX = 1.0
|
||||||
if(node.mirror == 'MX'):
|
if(node.mirror == 'MX'):
|
||||||
|
|
@ -295,7 +295,7 @@ class instance(geometry):
|
||||||
mScale = np.array([[scaleX,0.0,0.0],
|
mScale = np.array([[scaleX,0.0,0.0],
|
||||||
[0.0,scaleY,0.0],
|
[0.0,scaleY,0.0],
|
||||||
[0.0,0.0,1.0]])
|
[0.0,0.0,1.0]])
|
||||||
|
|
||||||
return (mRotate, mScale, mTranslate)
|
return (mRotate, mScale, mTranslate)
|
||||||
|
|
||||||
def apply_transform(self, mtransforms, uVector, vVector, origin):
|
def apply_transform(self, mtransforms, uVector, vVector, origin):
|
||||||
|
|
@ -312,13 +312,13 @@ class instance(geometry):
|
||||||
def apply_path_transform(self, path):
|
def apply_path_transform(self, path):
|
||||||
uVector = np.array([[1.0],[0.0],[0.0]])
|
uVector = np.array([[1.0],[0.0],[0.0]])
|
||||||
vVector = np.array([[0.0],[1.0],[0.0]])
|
vVector = np.array([[0.0],[1.0],[0.0]])
|
||||||
origin = np.array([[0.0],[0.0],[1.0]])
|
origin = np.array([[0.0],[0.0],[1.0]])
|
||||||
|
|
||||||
while(path):
|
while(path):
|
||||||
instance = path.pop(-1)
|
instance = path.pop(-1)
|
||||||
mtransforms = self.calculate_transform(instance)
|
mtransforms = self.calculate_transform(instance)
|
||||||
(uVector, vVector, origin) = self.apply_transform(mtransforms, uVector, vVector, origin)
|
(uVector, vVector, origin) = self.apply_transform(mtransforms, uVector, vVector, origin)
|
||||||
|
|
||||||
return (uVector, vVector, origin)
|
return (uVector, vVector, origin)
|
||||||
|
|
||||||
def reverse_transformation_bitcell(self, cell_name):
|
def reverse_transformation_bitcell(self, cell_name):
|
||||||
|
|
@ -339,7 +339,7 @@ class instance(geometry):
|
||||||
cell_paths.append(copy.copy(path))
|
cell_paths.append(copy.copy(path))
|
||||||
|
|
||||||
inst_name = path[-1].name
|
inst_name = path[-1].name
|
||||||
|
|
||||||
# get the row and col names from the path
|
# get the row and col names from the path
|
||||||
row = int(path[-1].name.split('_')[-2][1:])
|
row = int(path[-1].name.split('_')[-2][1:])
|
||||||
col = int(path[-1].name.split('_')[-1][1:])
|
col = int(path[-1].name.split('_')[-1][1:])
|
||||||
|
|
@ -349,7 +349,7 @@ class instance(geometry):
|
||||||
|
|
||||||
normalized_storage_nets = node.mod.get_normalized_storage_nets_offset()
|
normalized_storage_nets = node.mod.get_normalized_storage_nets_offset()
|
||||||
(normalized_bl_offsets, normalized_br_offsets, bl_names, br_names) = node.mod.get_normalized_bitline_offset()
|
(normalized_bl_offsets, normalized_br_offsets, bl_names, br_names) = node.mod.get_normalized_bitline_offset()
|
||||||
|
|
||||||
for offset in range(len(normalized_bl_offsets)):
|
for offset in range(len(normalized_bl_offsets)):
|
||||||
for port in range(len(bl_names)):
|
for port in range(len(bl_names)):
|
||||||
cell_bl_meta.append([bl_names[offset], row, col, port])
|
cell_bl_meta.append([bl_names[offset], row, col, port])
|
||||||
|
|
@ -369,18 +369,18 @@ class instance(geometry):
|
||||||
Q_bar_y = -1 * Q_bar_y
|
Q_bar_y = -1 * Q_bar_y
|
||||||
|
|
||||||
for pair in range(len(normalized_bl_offsets)):
|
for pair in range(len(normalized_bl_offsets)):
|
||||||
normalized_bl_offsets[pair] = (normalized_bl_offsets[pair][0],
|
normalized_bl_offsets[pair] = (normalized_bl_offsets[pair][0],
|
||||||
-1 * normalized_bl_offsets[pair][1])
|
-1 * normalized_bl_offsets[pair][1])
|
||||||
|
|
||||||
for pair in range(len(normalized_br_offsets)):
|
for pair in range(len(normalized_br_offsets)):
|
||||||
normalized_br_offsets[pair] = (normalized_br_offsets[pair][0],
|
normalized_br_offsets[pair] = (normalized_br_offsets[pair][0],
|
||||||
-1 * normalized_br_offsets[pair][1])
|
-1 * normalized_br_offsets[pair][1])
|
||||||
|
|
||||||
|
|
||||||
Q_offsets.append([Q_x, Q_y])
|
|
||||||
|
Q_offsets.append([Q_x, Q_y])
|
||||||
Q_bar_offsets.append([Q_bar_x, Q_bar_y])
|
Q_bar_offsets.append([Q_bar_x, Q_bar_y])
|
||||||
|
|
||||||
|
|
||||||
bl_offsets.append(normalized_bl_offsets)
|
bl_offsets.append(normalized_bl_offsets)
|
||||||
br_offsets.append(normalized_br_offsets)
|
br_offsets.append(normalized_br_offsets)
|
||||||
|
|
||||||
|
|
@ -402,13 +402,13 @@ class instance(geometry):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
|
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.cell_name + " " + self.mirror + " R=" + str(self.rotate) + ")"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
|
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.cell_name + " " + self.mirror + " R=" + str(self.rotate) + ")"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class path(geometry):
|
class path(geometry):
|
||||||
"""Represents a Path"""
|
"""Represents a Path"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ import copy
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
|
||||||
class timing_graph():
|
class timing_graph():
|
||||||
"""
|
"""
|
||||||
Implements a directed graph
|
Implements a directed graph
|
||||||
Nodes are currently just Strings.
|
Nodes are currently just Strings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.graph = defaultdict(set)
|
self.graph = defaultdict(set)
|
||||||
self.all_paths = []
|
self.all_paths = []
|
||||||
|
|
@ -17,7 +17,7 @@ class timing_graph():
|
||||||
def add_edge(self, src_node, dest_node, edge_mod):
|
def add_edge(self, src_node, dest_node, edge_mod):
|
||||||
"""Adds edge to graph. Nodes added as well if they do not exist.
|
"""Adds edge to graph. Nodes added as well if they do not exist.
|
||||||
Module which defines the edge must be provided for timing information."""
|
Module which defines the edge must be provided for timing information."""
|
||||||
|
|
||||||
src_node = src_node.lower()
|
src_node = src_node.lower()
|
||||||
dest_node = dest_node.lower()
|
dest_node = dest_node.lower()
|
||||||
self.graph[src_node].add(dest_node)
|
self.graph[src_node].add(dest_node)
|
||||||
|
|
@ -25,99 +25,99 @@ class timing_graph():
|
||||||
|
|
||||||
def add_node(self, node):
|
def add_node(self, node):
|
||||||
"""Add node to graph with no edges"""
|
"""Add node to graph with no edges"""
|
||||||
|
|
||||||
node = node.lower()
|
node = node.lower()
|
||||||
if node not in self.graph:
|
if node not in self.graph:
|
||||||
self.graph[node] = set()
|
self.graph[node] = set()
|
||||||
|
|
||||||
def remove_edges(self, node):
|
def remove_edges(self, node):
|
||||||
"""Helper function to remove edges, useful for removing vdd/gnd"""
|
"""Helper function to remove edges, useful for removing vdd/gnd"""
|
||||||
|
|
||||||
node = node.lower()
|
node = node.lower()
|
||||||
self.graph[node] = set()
|
self.graph[node] = set()
|
||||||
|
|
||||||
def get_all_paths(self, src_node, dest_node, remove_rail_nodes=True, reduce_paths=True):
|
def get_all_paths(self, src_node, dest_node, remove_rail_nodes=True, reduce_paths=True):
|
||||||
"""Traverse all paths from source to destination"""
|
"""Traverse all paths from source to destination"""
|
||||||
|
|
||||||
src_node = src_node.lower()
|
src_node = src_node.lower()
|
||||||
dest_node = dest_node.lower()
|
dest_node = dest_node.lower()
|
||||||
|
|
||||||
# Remove vdd and gnd by default
|
# Remove vdd and gnd by default
|
||||||
# Will require edits if separate supplies are implemented.
|
# Will require edits if separate supplies are implemented.
|
||||||
if remove_rail_nodes:
|
if remove_rail_nodes:
|
||||||
# Names are also assumed.
|
# Names are also assumed.
|
||||||
self.remove_edges('vdd')
|
self.remove_edges('vdd')
|
||||||
self.remove_edges('gnd')
|
self.remove_edges('gnd')
|
||||||
|
|
||||||
# Mark all the vertices as not visited
|
# Mark all the vertices as not visited
|
||||||
visited = set()
|
visited = set()
|
||||||
|
|
||||||
# Create an array to store paths
|
# Create an array to store paths
|
||||||
path = []
|
path = []
|
||||||
self.all_paths = []
|
self.all_paths = []
|
||||||
|
|
||||||
# Call the recursive helper function to print all paths
|
# Call the recursive helper function to print all paths
|
||||||
self.get_all_paths_util(src_node, dest_node, visited, path)
|
self.get_all_paths_util(src_node, dest_node, visited, path)
|
||||||
debug.info(2, "Paths found={}".format(len(self.all_paths)))
|
debug.info(2, "Paths found={}".format(len(self.all_paths)))
|
||||||
|
|
||||||
if reduce_paths:
|
if reduce_paths:
|
||||||
self.reduce_paths()
|
self.reduce_paths()
|
||||||
|
|
||||||
return self.all_paths
|
return self.all_paths
|
||||||
|
|
||||||
def reduce_paths(self):
|
def reduce_paths(self):
|
||||||
""" Remove any path that is a subset of another path """
|
""" Remove any path that is a subset of another path """
|
||||||
|
|
||||||
self.all_paths = [p1 for p1 in self.all_paths if not any(set(p1)<=set(p2) for p2 in self.all_paths if p1 is not p2)]
|
self.all_paths = [p1 for p1 in self.all_paths if not any(set(p1)<=set(p2) for p2 in self.all_paths if p1 is not p2)]
|
||||||
|
|
||||||
def get_all_paths_util(self, cur_node, dest_node, visited, path):
|
def get_all_paths_util(self, cur_node, dest_node, visited, path):
|
||||||
"""Recursive function to find all paths in a Depth First Search manner"""
|
"""Recursive function to find all paths in a Depth First Search manner"""
|
||||||
|
|
||||||
# Mark the current node as visited and store in path
|
# Mark the current node as visited and store in path
|
||||||
visited.add(cur_node)
|
visited.add(cur_node)
|
||||||
path.append(cur_node)
|
path.append(cur_node)
|
||||||
|
|
||||||
# If current vertex is same as destination, then print
|
# If current vertex is same as destination, then print
|
||||||
# current path[]
|
# current path[]
|
||||||
if cur_node == dest_node:
|
if cur_node == dest_node:
|
||||||
self.all_paths.append(copy.deepcopy(path))
|
self.all_paths.append(copy.deepcopy(path))
|
||||||
else:
|
else:
|
||||||
# If current vertex is not destination
|
# If current vertex is not destination
|
||||||
# Recur for all the vertices adjacent to this vertex
|
# Recur for all the vertices adjacent to this vertex
|
||||||
for node in self.graph[cur_node]:
|
for node in self.graph[cur_node]:
|
||||||
if node not in visited:
|
if node not in visited:
|
||||||
self.get_all_paths_util(node, dest_node, visited, path)
|
self.get_all_paths_util(node, dest_node, visited, path)
|
||||||
|
|
||||||
# Remove current vertex from path[] and mark it as unvisited
|
# Remove current vertex from path[] and mark it as unvisited
|
||||||
path.pop()
|
path.pop()
|
||||||
visited.remove(cur_node)
|
visited.remove(cur_node)
|
||||||
|
|
||||||
def get_timing(self, path, corner, slew, load):
|
def get_timing(self, path, corner, slew, load):
|
||||||
"""Returns the analytical delays in the input path"""
|
"""Returns the analytical delays in the input path"""
|
||||||
|
|
||||||
if len(path) == 0:
|
if len(path) == 0:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
delays = []
|
delays = []
|
||||||
cur_slew = slew
|
cur_slew = slew
|
||||||
for i in range(len(path) - 1):
|
for i in range(len(path) - 1):
|
||||||
|
|
||||||
path_edge_mod = self.edge_mods[(path[i], path[i + 1])]
|
path_edge_mod = self.edge_mods[(path[i], path[i + 1])]
|
||||||
|
|
||||||
# On the output of the current stage, get COUT from all other mods connected
|
# On the output of the current stage, get COUT from all other mods connected
|
||||||
cout = 0
|
cout = 0
|
||||||
for node in self.graph[path[i + 1]]:
|
for node in self.graph[path[i + 1]]:
|
||||||
output_edge_mod = self.edge_mods[(path[i + 1], node)]
|
output_edge_mod = self.edge_mods[(path[i + 1], node)]
|
||||||
cout+=output_edge_mod.get_cin()
|
cout+=output_edge_mod.get_cin()
|
||||||
# If at the last output, include the final output load
|
# If at the last output, include the final output load
|
||||||
if i == len(path) - 2:
|
if i == len(path) - 2:
|
||||||
cout += load
|
cout += load
|
||||||
|
|
||||||
delays.append(path_edge_mod.analytical_delay(corner, cur_slew, cout))
|
delays.append(path_edge_mod.analytical_delay(corner, cur_slew, cout))
|
||||||
cur_slew = delays[-1].slew
|
cur_slew = delays[-1].slew
|
||||||
|
|
||||||
return delays
|
return delays
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
||||||
|
|
@ -132,4 +132,4 @@ class timing_graph():
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
"""
|
"""
|
||||||
name_map = []
|
name_map = []
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name, cell_name):
|
||||||
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds"
|
self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds"
|
||||||
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
|
self.sp_file = OPTS.openram_tech + "sp_lib/" + cell_name + ".sp"
|
||||||
|
|
||||||
# If we have a separate lvs directory, then all the lvs files
|
# If we have a separate lvs directory, then all the lvs files
|
||||||
# should be in there (all or nothing!)
|
# should be in there (all or nothing!)
|
||||||
|
|
@ -33,7 +33,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
lvs_dir = OPTS.openram_tech + lvs_subdir + "/"
|
lvs_dir = OPTS.openram_tech + lvs_subdir + "/"
|
||||||
|
|
||||||
if os.path.exists(lvs_dir):
|
if os.path.exists(lvs_dir):
|
||||||
self.lvs_file = lvs_dir + name + ".sp"
|
self.lvs_file = lvs_dir + cell_name + ".sp"
|
||||||
else:
|
else:
|
||||||
self.lvs_file = self.sp_file
|
self.lvs_file = self.sp_file
|
||||||
|
|
||||||
|
|
@ -41,8 +41,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
self.lvs_errors = "skipped"
|
self.lvs_errors = "skipped"
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
hierarchy_spice.spice.__init__(self, name)
|
self.cell_name = cell_name
|
||||||
hierarchy_layout.layout.__init__(self, name)
|
hierarchy_spice.spice.__init__(self, name, cell_name)
|
||||||
|
hierarchy_layout.layout.__init__(self, name, cell_name)
|
||||||
self.init_graph_params()
|
self.init_graph_params()
|
||||||
|
|
||||||
def get_layout_pins(self, inst):
|
def get_layout_pins(self, inst):
|
||||||
|
|
@ -55,7 +56,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
debug.error("Couldn't find instance {0}".format(inst.name), -1)
|
debug.error("Couldn't find instance {0}".format(inst.name), -1)
|
||||||
inst_map = inst.mod.pin_map
|
inst_map = inst.mod.pin_map
|
||||||
return inst_map
|
return inst_map
|
||||||
|
|
||||||
def DRC_LVS(self, final_verification=False, force_check=False):
|
def DRC_LVS(self, final_verification=False, force_check=False):
|
||||||
"""Checks both DRC and LVS for a module"""
|
"""Checks both DRC and LVS for a module"""
|
||||||
import verify
|
import verify
|
||||||
|
|
@ -76,23 +77,23 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
self.lvs_write(tempspice)
|
self.lvs_write(tempspice)
|
||||||
self.gds_write(tempgds)
|
self.gds_write(tempgds)
|
||||||
# Final verification option does not allow nets to be connected by label.
|
# Final verification option does not allow nets to be connected by label.
|
||||||
self.drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification)
|
self.drc_errors = verify.run_drc(self.cell_name, tempgds, extract=True, final_verification=final_verification)
|
||||||
self.lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
|
self.lvs_errors = verify.run_lvs(self.cell_name, tempgds, tempspice, final_verification=final_verification)
|
||||||
|
|
||||||
# force_check is used to determine decoder height and other things, so we shouldn't fail
|
# force_check is used to determine decoder height and other things, so we shouldn't fail
|
||||||
# if that flag is set
|
# if that flag is set
|
||||||
if OPTS.inline_lvsdrc and not force_check:
|
if OPTS.inline_lvsdrc and not force_check:
|
||||||
debug.check(self.drc_errors == 0,
|
debug.check(self.drc_errors == 0,
|
||||||
"DRC failed for {0} with {1} error(s)".format(self.name,
|
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
|
||||||
self.drc_errors))
|
self.drc_errors))
|
||||||
debug.check(self.lvs_errors == 0,
|
debug.check(self.lvs_errors == 0,
|
||||||
"LVS failed for {0} with {1} errors(s)".format(self.name,
|
"LVS failed for {0} with {1} errors(s)".format(self.cell_name,
|
||||||
self.lvs_errors))
|
self.lvs_errors))
|
||||||
|
|
||||||
if OPTS.purge_temp:
|
if not OPTS.keep_temp:
|
||||||
os.remove(tempspice)
|
os.remove(tempspice)
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
def DRC(self, final_verification=False):
|
def DRC(self, final_verification=False):
|
||||||
"""Checks DRC for a module"""
|
"""Checks DRC for a module"""
|
||||||
import verify
|
import verify
|
||||||
|
|
@ -104,14 +105,14 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
return
|
return
|
||||||
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
|
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.cell_name)
|
||||||
self.gds_write(tempgds)
|
self.gds_write(tempgds)
|
||||||
num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification)
|
num_errors = verify.run_drc(self.cell_name, tempgds, final_verification=final_verification)
|
||||||
debug.check(num_errors == 0,
|
debug.check(num_errors == 0,
|
||||||
"DRC failed for {0} with {1} error(s)".format(self.name,
|
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
|
||||||
num_errors))
|
num_errors))
|
||||||
|
|
||||||
if OPTS.purge_temp:
|
if not OPTS.keep_temp:
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
def LVS(self, final_verification=False):
|
def LVS(self, final_verification=False):
|
||||||
|
|
@ -125,30 +126,30 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
return
|
return
|
||||||
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name)
|
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.cell_name)
|
||||||
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
|
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
|
||||||
self.lvs_write(tempspice)
|
self.lvs_write(tempspice)
|
||||||
self.gds_write(tempgds)
|
self.gds_write(tempgds)
|
||||||
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
|
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
|
||||||
debug.check(num_errors == 0,
|
debug.check(num_errors == 0,
|
||||||
"LVS failed for {0} with {1} error(s)".format(self.name,
|
"LVS failed for {0} with {1} error(s)".format(self.cell_name,
|
||||||
num_errors))
|
num_errors))
|
||||||
if OPTS.purge_temp:
|
if not OPTS.keep_temp:
|
||||||
os.remove(tempspice)
|
os.remove(tempspice)
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
def init_graph_params(self):
|
def init_graph_params(self):
|
||||||
"""
|
"""
|
||||||
Initializes parameters relevant to the graph creation
|
Initializes parameters relevant to the graph creation
|
||||||
"""
|
"""
|
||||||
# Only initializes a set for checking instances which should not be added
|
# Only initializes a set for checking instances which should not be added
|
||||||
self.graph_inst_exclude = set()
|
self.graph_inst_exclude = set()
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
Recursively create graph from instances in module.
|
Recursively create graph from instances in module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 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,
|
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
|
||||||
|
|
@ -162,7 +163,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
subinst_name = inst_name + '.X' + subinst.name
|
subinst_name = inst_name + '.X' + subinst.name
|
||||||
subinst_ports = self.translate_nets(conns, port_dict, inst_name)
|
subinst_ports = self.translate_nets(conns, port_dict, inst_name)
|
||||||
subinst.mod.build_graph(graph, subinst_name, subinst_ports)
|
subinst.mod.build_graph(graph, subinst_name, subinst_ports)
|
||||||
|
|
||||||
def build_names(self, name_dict, inst_name, port_nets):
|
def build_names(self, name_dict, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
Collects all the nets and the parent inst of that net.
|
Collects all the nets and the parent inst of that net.
|
||||||
|
|
@ -195,7 +196,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
else:
|
else:
|
||||||
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):
|
def add_graph_edges(self, graph, port_nets):
|
||||||
"""
|
"""
|
||||||
For every input, adds an edge to every output.
|
For every input, adds an edge to every output.
|
||||||
|
|
@ -211,13 +212,13 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
for out in output_pins + inout_pins:
|
for out in output_pins + inout_pins:
|
||||||
if inp != out: # do not add self loops
|
if inp != out: # do not add self loops
|
||||||
graph.add_edge(pin_dict[inp], pin_dict[out], self)
|
graph.add_edge(pin_dict[inp], pin_dict[out], self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
pins = ",".join(self.pins)
|
pins = ",".join(self.pins)
|
||||||
insts = [" {}".format(x) for x in self.insts]
|
insts = [" {}".format(x) for x in self.insts]
|
||||||
objs = [" {}".format(x) for x in self.objs]
|
objs = [" {}".format(x) for x in self.objs]
|
||||||
s = "********** design {0} **********".format(self.name)
|
s = "********** design {0} **********".format(self.cell_name)
|
||||||
s += "\n pins ({0})={1}\n".format(len(self.pins), pins)
|
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 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))
|
s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts))
|
||||||
|
|
@ -231,4 +232,4 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
for i in self.insts:
|
for i in self.insts:
|
||||||
text+=str(i) + ",\n"
|
text+=str(i) + ",\n"
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,9 @@ class layout():
|
||||||
layout/netlist and perform LVS/DRC.
|
layout/netlist and perform LVS/DRC.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name, cell_name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.cell_name = cell_name
|
||||||
self.width = None
|
self.width = None
|
||||||
self.height = None
|
self.height = None
|
||||||
self.bounding_box = None
|
self.bounding_box = None
|
||||||
|
|
@ -67,13 +68,13 @@ class layout():
|
||||||
def offset_x_coordinates(self):
|
def offset_x_coordinates(self):
|
||||||
"""
|
"""
|
||||||
This function is called after everything is placed to
|
This function is called after everything is placed to
|
||||||
shift the origin to the furthest left point.
|
shift the origin to the furthest left point.
|
||||||
Y offset is unchanged.
|
Y offset is unchanged.
|
||||||
"""
|
"""
|
||||||
offset = self.find_lowest_coords()
|
offset = self.find_lowest_coords()
|
||||||
self.translate_all(offset.scale(1, 0))
|
self.translate_all(offset.scale(1, 0))
|
||||||
return offset
|
return offset
|
||||||
|
|
||||||
def get_gate_offset(self, x_offset, height, inv_num):
|
def get_gate_offset(self, x_offset, height, inv_num):
|
||||||
"""
|
"""
|
||||||
Gets the base offset and y orientation of stacked rows of gates
|
Gets the base offset and y orientation of stacked rows of gates
|
||||||
|
|
@ -215,7 +216,7 @@ class layout():
|
||||||
# Contacts are not really instances, so skip them
|
# Contacts are not really instances, so skip them
|
||||||
if "contact" not in mod.name:
|
if "contact" not in mod.name:
|
||||||
# Check that the instance name is unique
|
# Check that the instance name is unique
|
||||||
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.name, name))
|
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name))
|
||||||
|
|
||||||
self.inst_names.add(name)
|
self.inst_names.add(name)
|
||||||
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
|
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
|
||||||
|
|
@ -300,9 +301,9 @@ class layout():
|
||||||
tx_list.append(i)
|
tx_list.append(i)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return tx_list
|
return tx_list
|
||||||
|
|
||||||
def get_pin(self, text):
|
def get_pin(self, text):
|
||||||
"""
|
"""
|
||||||
Return the pin or list of pins
|
Return the pin or list of pins
|
||||||
|
|
@ -316,7 +317,7 @@ class layout():
|
||||||
return any_pin
|
return any_pin
|
||||||
except Exception:
|
except Exception:
|
||||||
self.gds_write("missing_pin.gds")
|
self.gds_write("missing_pin.gds")
|
||||||
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.name), -1)
|
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.cell_name), -1)
|
||||||
|
|
||||||
def get_pins(self, text):
|
def get_pins(self, text):
|
||||||
"""
|
"""
|
||||||
|
|
@ -537,10 +538,6 @@ class layout():
|
||||||
position_list=coordinates,
|
position_list=coordinates,
|
||||||
widen_short_wires=widen_short_wires)
|
widen_short_wires=widen_short_wires)
|
||||||
|
|
||||||
def get_preferred_direction(self, layer):
|
|
||||||
""" Return the preferred routing directions """
|
|
||||||
return preferred_directions[layer]
|
|
||||||
|
|
||||||
def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
|
def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
|
||||||
""" Add a three layer via structure. """
|
""" Add a three layer via structure. """
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
@ -617,24 +614,24 @@ class layout():
|
||||||
next_id = 0
|
next_id = 0
|
||||||
|
|
||||||
curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None)
|
curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None)
|
||||||
|
|
||||||
via = self.add_via_center(layers=curr_stack,
|
via = self.add_via_center(layers=curr_stack,
|
||||||
size=size,
|
size=size,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
directions=directions,
|
directions=directions,
|
||||||
implant_type=implant_type,
|
implant_type=implant_type,
|
||||||
well_type=well_type)
|
well_type=well_type)
|
||||||
|
|
||||||
if cur_layer != from_layer:
|
if cur_layer != from_layer:
|
||||||
self.add_min_area_rect_center(cur_layer,
|
self.add_min_area_rect_center(cur_layer,
|
||||||
offset,
|
offset,
|
||||||
via.mod.first_layer_width,
|
via.mod.first_layer_width,
|
||||||
via.mod.first_layer_height)
|
via.mod.first_layer_height)
|
||||||
|
|
||||||
cur_layer = curr_stack[next_id]
|
cur_layer = curr_stack[next_id]
|
||||||
|
|
||||||
return via
|
return via
|
||||||
|
|
||||||
def add_min_area_rect_center(self,
|
def add_min_area_rect_center(self,
|
||||||
layer,
|
layer,
|
||||||
offset,
|
offset,
|
||||||
|
|
@ -648,14 +645,14 @@ class layout():
|
||||||
min_area = drc("minarea_{}".format(layer))
|
min_area = drc("minarea_{}".format(layer))
|
||||||
if min_area == 0:
|
if min_area == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
min_width = drc("minwidth_{}".format(layer))
|
min_width = drc("minwidth_{}".format(layer))
|
||||||
|
|
||||||
if preferred_directions[layer] == "V":
|
if preferred_directions[layer] == "V":
|
||||||
height = max(min_area / width, min_width)
|
height = max(min_area / width, min_width)
|
||||||
else:
|
else:
|
||||||
width = max(min_area / height, min_width)
|
width = max(min_area / height, min_width)
|
||||||
|
|
||||||
self.add_rect_center(layer=layer,
|
self.add_rect_center(layer=layer,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
width=width,
|
width=width,
|
||||||
|
|
@ -739,7 +736,7 @@ class layout():
|
||||||
|
|
||||||
height = boundary[1][1] - boundary[0][1]
|
height = boundary[1][1] - boundary[0][1]
|
||||||
width = boundary[1][0] - boundary[0][0]
|
width = boundary[1][0] - boundary[0][0]
|
||||||
|
|
||||||
for boundary_layer in boundary_layers:
|
for boundary_layer in boundary_layers:
|
||||||
(layer_number, layer_purpose) = techlayer[boundary_layer]
|
(layer_number, layer_purpose) = techlayer[boundary_layer]
|
||||||
gds_layout.addBox(layerNumber=layer_number,
|
gds_layout.addBox(layerNumber=layer_number,
|
||||||
|
|
@ -891,7 +888,7 @@ class layout():
|
||||||
new_pin = pin_layout(names[i],
|
new_pin = pin_layout(names[i],
|
||||||
[rect.ll(), rect.ur()],
|
[rect.ll(), rect.ur()],
|
||||||
layer)
|
layer)
|
||||||
|
|
||||||
pins[names[i]] = new_pin
|
pins[names[i]] = new_pin
|
||||||
else:
|
else:
|
||||||
for i in range(len(names)):
|
for i in range(len(names)):
|
||||||
|
|
@ -909,7 +906,7 @@ class layout():
|
||||||
new_pin = pin_layout(names[i],
|
new_pin = pin_layout(names[i],
|
||||||
[rect.ll(), rect.ur()],
|
[rect.ll(), rect.ur()],
|
||||||
layer)
|
layer)
|
||||||
|
|
||||||
pins[names[i]] = new_pin
|
pins[names[i]] = new_pin
|
||||||
|
|
||||||
return pins
|
return pins
|
||||||
|
|
@ -1047,7 +1044,7 @@ class layout():
|
||||||
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
|
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
|
||||||
self.add_inst(cr.name, cr)
|
self.add_inst(cr.name, cr)
|
||||||
self.connect_inst([])
|
self.connect_inst([])
|
||||||
|
|
||||||
def add_boundary(self, ll=vector(0, 0), ur=None):
|
def add_boundary(self, ll=vector(0, 0), ur=None):
|
||||||
""" Add boundary for debugging dimensions """
|
""" Add boundary for debugging dimensions """
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
|
|
@ -1112,7 +1109,7 @@ class layout():
|
||||||
width=xmax - xmin,
|
width=xmax - xmin,
|
||||||
height=ymax - ymin)
|
height=ymax - ymin)
|
||||||
return rect
|
return rect
|
||||||
|
|
||||||
def copy_power_pins(self, inst, name, add_vias=True):
|
def copy_power_pins(self, inst, name, add_vias=True):
|
||||||
"""
|
"""
|
||||||
This will copy a power pin if it is on the lowest power_grid layer.
|
This will copy a power pin if it is on the lowest power_grid layer.
|
||||||
|
|
@ -1171,7 +1168,7 @@ class layout():
|
||||||
bottom = ll.y
|
bottom = ll.y
|
||||||
right = ur.x
|
right = ur.x
|
||||||
top = ur.y
|
top = ur.y
|
||||||
|
|
||||||
pin_loc = pin.center()
|
pin_loc = pin.center()
|
||||||
if side == "left":
|
if side == "left":
|
||||||
peri_pin_loc = vector(left, pin_loc.y)
|
peri_pin_loc = vector(left, pin_loc.y)
|
||||||
|
|
@ -1189,14 +1186,14 @@ class layout():
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
to_layer=layer,
|
to_layer=layer,
|
||||||
offset=pin_loc)
|
offset=pin_loc)
|
||||||
|
|
||||||
self.add_path(layer,
|
self.add_path(layer,
|
||||||
[pin_loc, peri_pin_loc])
|
[pin_loc, peri_pin_loc])
|
||||||
|
|
||||||
return self.add_layout_pin_rect_center(text=name,
|
return self.add_layout_pin_rect_center(text=name,
|
||||||
layer=layer,
|
layer=layer,
|
||||||
offset=peri_pin_loc)
|
offset=peri_pin_loc)
|
||||||
|
|
||||||
def add_power_ring(self, bbox):
|
def add_power_ring(self, bbox):
|
||||||
"""
|
"""
|
||||||
Create vdd and gnd power rings around an area of the bounding box
|
Create vdd and gnd power rings around an area of the bounding box
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import re
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
import tech
|
import tech
|
||||||
|
from pprint import pformat
|
||||||
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
|
||||||
|
|
@ -26,8 +27,9 @@ class spice():
|
||||||
Class consisting of a set of modules and instances of these modules
|
Class consisting of a set of modules and instances of these modules
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name, cell_name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.cell_name = cell_name
|
||||||
|
|
||||||
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
# Holds subckts/mods for this module
|
# Holds subckts/mods for this module
|
||||||
|
|
@ -63,7 +65,7 @@ class spice():
|
||||||
self.comments = []
|
self.comments = []
|
||||||
|
|
||||||
self.comments.append(comment)
|
self.comments.append(comment)
|
||||||
|
|
||||||
def add_pin(self, name, pin_type="INOUT"):
|
def add_pin(self, name, pin_type="INOUT"):
|
||||||
""" Adds a pin to the pins list. Default type is INOUT signal. """
|
""" Adds a pin to the pins list. Default type is INOUT signal. """
|
||||||
self.pins.append(name)
|
self.pins.append(name)
|
||||||
|
|
@ -82,7 +84,7 @@ class spice():
|
||||||
"Invalid signaltype for {0}: {1}".format(pin,
|
"Invalid signaltype for {0}: {1}".format(pin,
|
||||||
pin_type))
|
pin_type))
|
||||||
self.add_pin(pin, pin_type)
|
self.add_pin(pin, pin_type)
|
||||||
|
|
||||||
elif len(pin_type)==len(pin_list):
|
elif len(pin_type)==len(pin_list):
|
||||||
for (pin, ptype) in zip(pin_list, pin_type):
|
for (pin, ptype) in zip(pin_list, pin_type):
|
||||||
debug.check(ptype in self.valid_signal_types,
|
debug.check(ptype in self.valid_signal_types,
|
||||||
|
|
@ -104,7 +106,7 @@ class spice():
|
||||||
\n Module names={}\
|
\n Module names={}\
|
||||||
".format(self.name, self.pin_names, self.pins), 1)
|
".format(self.name, self.pin_names, self.pins), 1)
|
||||||
self.pin_type = {pin: type for pin, type in zip(self.pin_names, type_list)}
|
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. """
|
||||||
pin_type = self.pin_type[name]
|
pin_type = self.pin_type[name]
|
||||||
|
|
@ -118,7 +120,7 @@ class spice():
|
||||||
return "INOUT"
|
return "INOUT"
|
||||||
else:
|
else:
|
||||||
return self.pin_type[name]
|
return self.pin_type[name]
|
||||||
|
|
||||||
def get_inputs(self):
|
def get_inputs(self):
|
||||||
""" These use pin types to determine pin lists. These
|
""" These use pin types to determine pin lists. These
|
||||||
may be over-ridden by submodules that didn't use pin directions yet."""
|
may be over-ridden by submodules that didn't use pin directions yet."""
|
||||||
|
|
@ -164,7 +166,6 @@ class spice():
|
||||||
num_pins = len(self.insts[-1].mod.pins)
|
num_pins = len(self.insts[-1].mod.pins)
|
||||||
num_args = len(args)
|
num_args = len(args)
|
||||||
if (check and num_pins != num_args):
|
if (check and num_pins != num_args):
|
||||||
from pprint import pformat
|
|
||||||
if num_pins < num_args:
|
if num_pins < num_args:
|
||||||
mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins)
|
mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins)
|
||||||
arg_pins = args
|
arg_pins = args
|
||||||
|
|
@ -181,10 +182,9 @@ class spice():
|
||||||
self.conns.append(args)
|
self.conns.append(args)
|
||||||
|
|
||||||
if check and (len(self.insts)!=len(self.conns)):
|
if check and (len(self.insts)!=len(self.conns)):
|
||||||
from pprint import pformat
|
|
||||||
insts_string=pformat(self.insts)
|
insts_string=pformat(self.insts)
|
||||||
conns_string=pformat(self.conns)
|
conns_string=pformat(self.conns)
|
||||||
|
|
||||||
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
|
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
|
||||||
len(self.insts),
|
len(self.insts),
|
||||||
len(self.conns)))
|
len(self.conns)))
|
||||||
|
|
@ -214,7 +214,7 @@ class spice():
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# find the correct subckt line in the file
|
# find the correct subckt line in the file
|
||||||
subckt = re.compile("^.subckt {}".format(self.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
|
||||||
self.pins = subckt_line.split(" ")[2:]
|
self.pins = subckt_line.split(" ")[2:]
|
||||||
|
|
@ -234,12 +234,12 @@ class spice():
|
||||||
|
|
||||||
# pins and subckt should be the same
|
# pins and subckt should be the same
|
||||||
# find the correct subckt line in the file
|
# find the correct subckt line in the file
|
||||||
subckt = re.compile("^.subckt {}".format(self.name), re.IGNORECASE)
|
subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE)
|
||||||
subckt_line = list(filter(subckt.search, self.lvs))[0]
|
subckt_line = list(filter(subckt.search, self.lvs))[0]
|
||||||
# parses line into ports and remove subckt
|
# parses line into ports and remove subckt
|
||||||
lvs_pins = subckt_line.split(" ")[2:]
|
lvs_pins = subckt_line.split(" ")[2:]
|
||||||
debug.check(lvs_pins == self.pins, "LVS and spice file pin mismatch.")
|
debug.check(lvs_pins == self.pins, "LVS and spice file pin mismatch.")
|
||||||
|
|
||||||
def check_net_in_spice(self, net_name):
|
def check_net_in_spice(self, net_name):
|
||||||
"""Checks if a net name exists in the current. Intended to be check nets in hand-made cells."""
|
"""Checks if a net name exists in the current. Intended to be check nets in hand-made cells."""
|
||||||
# Remove spaces and lower case then add spaces.
|
# Remove spaces and lower case then add spaces.
|
||||||
|
|
@ -255,14 +255,14 @@ class spice():
|
||||||
if net_formatted in line:
|
if net_formatted in line:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def do_nets_exist(self, nets):
|
def do_nets_exist(self, nets):
|
||||||
"""For handmade cell, checks sp file contains the storage nodes."""
|
"""For handmade cell, checks sp file contains the storage nodes."""
|
||||||
nets_match = True
|
nets_match = True
|
||||||
for net in nets:
|
for net in nets:
|
||||||
nets_match = nets_match and self.check_net_in_spice(net)
|
nets_match = nets_match and self.check_net_in_spice(net)
|
||||||
return nets_match
|
return nets_match
|
||||||
|
|
||||||
def contains(self, mod, modlist):
|
def contains(self, mod, modlist):
|
||||||
for x in modlist:
|
for x in modlist:
|
||||||
if x.name == mod.name:
|
if x.name == mod.name:
|
||||||
|
|
@ -279,7 +279,7 @@ class spice():
|
||||||
return
|
return
|
||||||
elif not self.spice:
|
elif not self.spice:
|
||||||
# If spice isn't defined, we dynamically generate one.
|
# If spice isn't defined, we dynamically generate one.
|
||||||
|
|
||||||
# recursively write the modules
|
# recursively write the modules
|
||||||
for i in self.mods:
|
for i in self.mods:
|
||||||
if self.contains(i, usedMODS):
|
if self.contains(i, usedMODS):
|
||||||
|
|
@ -293,18 +293,18 @@ class spice():
|
||||||
return
|
return
|
||||||
|
|
||||||
# write out the first spice line (the subcircuit)
|
# write out the first spice line (the subcircuit)
|
||||||
sp.write("\n.SUBCKT {0} {1}\n".format(self.name,
|
sp.write("\n.SUBCKT {0} {1}\n".format(self.cell_name,
|
||||||
" ".join(self.pins)))
|
" ".join(self.pins)))
|
||||||
|
|
||||||
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, self.pin_type[pin]))
|
||||||
|
|
||||||
for line in self.comments:
|
for line in self.comments:
|
||||||
sp.write("* {}\n".format(line))
|
sp.write("* {}\n".format(line))
|
||||||
|
|
||||||
# every instance must have a set of connections, even if it is empty.
|
# every instance must have a set of connections, even if it is empty.
|
||||||
if len(self.insts) != len(self.conns):
|
if len(self.insts) != len(self.conns):
|
||||||
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
|
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.cell_name,
|
||||||
len(self.insts),
|
len(self.insts),
|
||||||
len(self.conns)))
|
len(self.conns)))
|
||||||
debug.error("Instances: \n" + str(self.insts))
|
debug.error("Instances: \n" + str(self.insts))
|
||||||
|
|
@ -330,9 +330,9 @@ class spice():
|
||||||
else:
|
else:
|
||||||
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
|
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
|
||||||
" ".join(self.conns[i]),
|
" ".join(self.conns[i]),
|
||||||
self.insts[i].mod.name))
|
self.insts[i].mod.cell_name))
|
||||||
|
|
||||||
sp.write(".ENDS {0}\n".format(self.name))
|
sp.write(".ENDS {0}\n".format(self.cell_name))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# If spice is a hard module, output the spice file contents.
|
# If spice is a hard module, output the spice file contents.
|
||||||
|
|
@ -343,7 +343,7 @@ class spice():
|
||||||
sp.write("\n".join(self.lvs))
|
sp.write("\n".join(self.lvs))
|
||||||
else:
|
else:
|
||||||
sp.write("\n".join(self.spice))
|
sp.write("\n".join(self.spice))
|
||||||
|
|
||||||
sp.write("\n")
|
sp.write("\n")
|
||||||
|
|
||||||
def sp_write(self, spname):
|
def sp_write(self, spname):
|
||||||
|
|
@ -365,19 +365,19 @@ class spice():
|
||||||
self.sp_write_file(spfile, usedMODS, True)
|
self.sp_write_file(spfile, usedMODS, True)
|
||||||
del usedMODS
|
del usedMODS
|
||||||
spfile.close()
|
spfile.close()
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
def analytical_delay(self, corner, slew, load=0.0):
|
||||||
"""Inform users undefined delay module while building new modules"""
|
"""Inform users undefined delay module while building new modules"""
|
||||||
|
|
||||||
# FIXME: Slew is not used in the model right now.
|
# FIXME: Slew is not used in the model right now.
|
||||||
# Can be added heuristically as linear factor
|
# Can be added heuristically as linear factor
|
||||||
relative_cap = logical_effort.convert_farad_to_relative_c(load)
|
relative_cap = logical_effort.convert_farad_to_relative_c(load)
|
||||||
stage_effort = self.get_stage_effort(relative_cap)
|
stage_effort = self.get_stage_effort(relative_cap)
|
||||||
|
|
||||||
# If it fails, then keep running with a valid object.
|
# If it fails, then keep running with a valid object.
|
||||||
if not stage_effort:
|
if not stage_effort:
|
||||||
return delay_data(0.0, 0.0)
|
return delay_data(0.0, 0.0)
|
||||||
|
|
||||||
abs_delay = stage_effort.get_absolute_delay()
|
abs_delay = stage_effort.get_absolute_delay()
|
||||||
corner_delay = self.apply_corners_analytically(abs_delay, corner)
|
corner_delay = self.apply_corners_analytically(abs_delay, corner)
|
||||||
SLEW_APPROXIMATION = 0.1
|
SLEW_APPROXIMATION = 0.1
|
||||||
|
|
@ -390,27 +390,27 @@ class spice():
|
||||||
.format(self.__class__.__name__))
|
.format(self.__class__.__name__))
|
||||||
debug.warning("Class {0} name {1}"
|
debug.warning("Class {0} name {1}"
|
||||||
.format(self.__class__.__name__,
|
.format(self.__class__.__name__,
|
||||||
self.name))
|
self.cell_name))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_cin(self):
|
def get_cin(self):
|
||||||
"""Returns input load in Femto-Farads. All values generated using
|
"""Returns input load in Femto-Farads. All values generated using
|
||||||
relative capacitance function then converted based on tech file parameter."""
|
relative capacitance function then converted based on tech file parameter."""
|
||||||
|
|
||||||
# Override this function within a module if a more accurate input capacitance is needed.
|
# Override this function within a module if a more accurate input capacitance is needed.
|
||||||
# Input/outputs with differing capacitances is not implemented.
|
# Input/outputs with differing capacitances is not implemented.
|
||||||
relative_cap = self.input_load()
|
relative_cap = self.input_load()
|
||||||
return logical_effort.convert_relative_c_to_farad(relative_cap)
|
return logical_effort.convert_relative_c_to_farad(relative_cap)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Inform users undefined relative capacitance functions used for analytical delays."""
|
"""Inform users undefined relative capacitance functions used for analytical delays."""
|
||||||
debug.warning("Design Class {0} input capacitance function needs to be defined"
|
debug.warning("Design Class {0} input capacitance function needs to be defined"
|
||||||
.format(self.__class__.__name__))
|
.format(self.__class__.__name__))
|
||||||
debug.warning("Class {0} name {1}"
|
debug.warning("Class {0} name {1}"
|
||||||
.format(self.__class__.__name__,
|
.format(self.__class__.__name__,
|
||||||
self.name))
|
self.cell_name))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5):
|
def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5):
|
||||||
"""
|
"""
|
||||||
Calculate the delay of a mosfet by
|
Calculate the delay of a mosfet by
|
||||||
|
|
@ -420,7 +420,7 @@ class spice():
|
||||||
delay = swing_factor * r * c # c is in ff and delay is in fs
|
delay = swing_factor * r * c # c is in ff and delay is in fs
|
||||||
delay = self.apply_corners_analytically(delay, corner)
|
delay = self.apply_corners_analytically(delay, corner)
|
||||||
delay = delay * 0.001 # make the unit to ps
|
delay = delay * 0.001 # make the unit to ps
|
||||||
|
|
||||||
# Output slew should be linear to input slew which is described
|
# Output slew should be linear to input slew which is described
|
||||||
# as 0.005* slew.
|
# as 0.005* slew.
|
||||||
|
|
||||||
|
|
@ -439,7 +439,7 @@ class spice():
|
||||||
volt_mult = self.get_voltage_delay_factor(vdd)
|
volt_mult = self.get_voltage_delay_factor(vdd)
|
||||||
temp_mult = self.get_temp_delay_factor(temp)
|
temp_mult = self.get_temp_delay_factor(temp)
|
||||||
return delay * proc_mult * volt_mult * temp_mult
|
return delay * proc_mult * volt_mult * temp_mult
|
||||||
|
|
||||||
def get_process_delay_factor(self, proc):
|
def get_process_delay_factor(self, proc):
|
||||||
"""Returns delay increase estimate based off process
|
"""Returns delay increase estimate based off process
|
||||||
Currently does +/-10 for fast/slow corners."""
|
Currently does +/-10 for fast/slow corners."""
|
||||||
|
|
@ -452,13 +452,13 @@ class spice():
|
||||||
elif mos_proc == 'S':
|
elif mos_proc == 'S':
|
||||||
proc_factors.append(1.1)
|
proc_factors.append(1.1)
|
||||||
return proc_factors
|
return proc_factors
|
||||||
|
|
||||||
def get_voltage_delay_factor(self, voltage):
|
def get_voltage_delay_factor(self, voltage):
|
||||||
"""Returns delay increase due to voltage.
|
"""Returns delay increase due to voltage.
|
||||||
Implemented as linear factor based off nominal voltage.
|
Implemented as linear factor based off nominal voltage.
|
||||||
"""
|
"""
|
||||||
return tech.spice["nom_supply_voltage"] / voltage
|
return tech.spice["nom_supply_voltage"] / voltage
|
||||||
|
|
||||||
def get_temp_delay_factor(self, temp):
|
def get_temp_delay_factor(self, temp):
|
||||||
"""Returns delay increase due to temperature (in C).
|
"""Returns delay increase due to temperature (in C).
|
||||||
Determines effect on threshold voltage and then linear factor is estimated.
|
Determines effect on threshold voltage and then linear factor is estimated.
|
||||||
|
|
@ -478,7 +478,7 @@ class spice():
|
||||||
|
|
||||||
def generate_rc_net(self, lump_num, wire_length, wire_width):
|
def generate_rc_net(self, lump_num, wire_length, wire_width):
|
||||||
return wire_spice_model(lump_num, wire_length, wire_width)
|
return wire_spice_model(lump_num, wire_length, wire_width)
|
||||||
|
|
||||||
def calc_dynamic_power(self, corner, c, freq, swing=1.0):
|
def calc_dynamic_power(self, corner, c, freq, swing=1.0):
|
||||||
"""
|
"""
|
||||||
Calculate dynamic power using effective capacitance, frequency, and corner (PVT)
|
Calculate dynamic power using effective capacitance, frequency, and corner (PVT)
|
||||||
|
|
@ -486,16 +486,16 @@ class spice():
|
||||||
proc, vdd, temp = corner
|
proc, vdd, temp = corner
|
||||||
net_vswing = vdd * swing
|
net_vswing = vdd * swing
|
||||||
power_dyn = c * vdd * net_vswing * freq
|
power_dyn = c * vdd * net_vswing * freq
|
||||||
|
|
||||||
# A pply process and temperature factors.
|
# A pply process and temperature factors.
|
||||||
# Roughly, process and Vdd affect the delay which affects the power.
|
# Roughly, process and Vdd affect the delay which affects the power.
|
||||||
# No other estimations are currently used. Increased delay->slower freq.->less power
|
# No other estimations are currently used. Increased delay->slower freq.->less power
|
||||||
proc_div = max(self.get_process_delay_factor(proc))
|
proc_div = max(self.get_process_delay_factor(proc))
|
||||||
temp_div = self.get_temp_delay_factor(temp)
|
temp_div = self.get_temp_delay_factor(temp)
|
||||||
power_dyn = power_dyn / (proc_div * temp_div)
|
power_dyn = power_dyn / (proc_div * temp_div)
|
||||||
|
|
||||||
return power_dyn
|
return power_dyn
|
||||||
|
|
||||||
def return_power(self, dynamic=0.0, leakage=0.0):
|
def return_power(self, dynamic=0.0, leakage=0.0):
|
||||||
return power_data(dynamic, leakage)
|
return power_data(dynamic, leakage)
|
||||||
|
|
||||||
|
|
@ -519,7 +519,7 @@ class spice():
|
||||||
if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set):
|
if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set):
|
||||||
aliases.append(net)
|
aliases.append(net)
|
||||||
return aliases
|
return aliases
|
||||||
|
|
||||||
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
|
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
|
||||||
"""
|
"""
|
||||||
Checks if the alias_net in input mod is the same as the input net for this mod (self).
|
Checks if the alias_net in input mod is the same as the input net for this mod (self).
|
||||||
|
|
@ -541,7 +541,7 @@ class spice():
|
||||||
return True
|
return True
|
||||||
mod_set.add(subinst.mod)
|
mod_set.add(subinst.mod)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod):
|
def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod):
|
||||||
"""
|
"""
|
||||||
Utility function for checking single net alias.
|
Utility function for checking single net alias.
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,9 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import gdsMill
|
|
||||||
import tech
|
|
||||||
import globals
|
|
||||||
import math
|
|
||||||
import debug
|
import debug
|
||||||
import datetime
|
from tech import layer_names
|
||||||
from collections import defaultdict
|
|
||||||
import pdb
|
|
||||||
|
|
||||||
class lef:
|
class lef:
|
||||||
"""
|
"""
|
||||||
|
|
@ -20,13 +15,13 @@ class lef:
|
||||||
and write them to LEF file.
|
and write them to LEF file.
|
||||||
This is inherited by the sram_base class.
|
This is inherited by the sram_base class.
|
||||||
"""
|
"""
|
||||||
def __init__(self,layers):
|
def __init__(self, layers):
|
||||||
# LEF db units per micron
|
# LEF db units per micron
|
||||||
self.lef_units = 2000
|
self.lef_units = 2000
|
||||||
# These are the layers of the obstructions
|
# These are the layers of the obstructions
|
||||||
self.lef_layers = layers
|
self.lef_layers = layers
|
||||||
# Round to ensure float values are divisible by 0.0025 (the manufacturing grid)
|
# Round to ensure float values are divisible by 0.0025 (the manufacturing grid)
|
||||||
self.round_grid = 4;
|
self.round_grid = 4
|
||||||
|
|
||||||
def lef_write(self, lef_name):
|
def lef_write(self, lef_name):
|
||||||
"""Write the entire lef of the object to the file."""
|
"""Write the entire lef of the object to the file."""
|
||||||
|
|
@ -34,14 +29,14 @@ class lef:
|
||||||
|
|
||||||
self.indent = "" # To maintain the indent level easily
|
self.indent = "" # To maintain the indent level easily
|
||||||
|
|
||||||
self.lef = open(lef_name,"w")
|
self.lef = open(lef_name, "w")
|
||||||
self.lef_write_header()
|
self.lef_write_header()
|
||||||
for pin in self.pins:
|
for pin in self.pins:
|
||||||
self.lef_write_pin(pin)
|
self.lef_write_pin(pin)
|
||||||
self.lef_write_obstructions()
|
self.lef_write_obstructions()
|
||||||
self.lef_write_footer()
|
self.lef_write_footer()
|
||||||
self.lef.close()
|
self.lef.close()
|
||||||
|
|
||||||
def lef_write_header(self):
|
def lef_write_header(self):
|
||||||
""" Header of LEF file """
|
""" Header of LEF file """
|
||||||
self.lef.write("VERSION 5.4 ;\n")
|
self.lef.write("VERSION 5.4 ;\n")
|
||||||
|
|
@ -51,78 +46,80 @@ class lef:
|
||||||
self.lef.write("UNITS\n")
|
self.lef.write("UNITS\n")
|
||||||
self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units))
|
self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units))
|
||||||
self.lef.write("END UNITS\n")
|
self.lef.write("END UNITS\n")
|
||||||
|
|
||||||
self.lef.write("{0}MACRO {1}\n".format(self.indent,self.name))
|
self.lef.write("{0}MACRO {1}\n".format(self.indent, self.name))
|
||||||
self.indent += " "
|
self.indent += " "
|
||||||
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
|
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
|
||||||
self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent,
|
self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent,
|
||||||
round(self.width,self.round_grid),
|
round(self.width, self.round_grid),
|
||||||
round(self.height,self.round_grid)))
|
round(self.height, self.round_grid)))
|
||||||
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
|
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
|
||||||
|
|
||||||
def lef_write_footer(self):
|
def lef_write_footer(self):
|
||||||
self.lef.write("{0}END {1}\n".format(self.indent,self.name))
|
self.lef.write("{0}END {1}\n".format(self.indent, self.name))
|
||||||
self.indent = self.indent[:-3]
|
self.indent = self.indent[:-3]
|
||||||
self.lef.write("END LIBRARY\n")
|
self.lef.write("END LIBRARY\n")
|
||||||
|
|
||||||
|
|
||||||
def lef_write_pin(self, name):
|
def lef_write_pin(self, name):
|
||||||
pin_dir = self.get_pin_dir(name)
|
pin_dir = self.get_pin_dir(name)
|
||||||
pin_type = self.get_pin_type(name)
|
pin_type = self.get_pin_type(name)
|
||||||
self.lef.write("{0}PIN {1}\n".format(self.indent,name))
|
self.lef.write("{0}PIN {1}\n".format(self.indent, name))
|
||||||
self.indent += " "
|
self.indent += " "
|
||||||
|
|
||||||
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent,pin_dir))
|
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent, pin_dir))
|
||||||
|
|
||||||
if pin_type in ["POWER","GROUND"]:
|
if pin_type in ["POWER", "GROUND"]:
|
||||||
self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type))
|
self.lef.write("{0}USE {1} ; \n".format(self.indent, pin_type))
|
||||||
self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent))
|
self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent))
|
||||||
|
|
||||||
self.lef.write("{0}PORT\n".format(self.indent))
|
self.lef.write("{0}PORT\n".format(self.indent))
|
||||||
self.indent += " "
|
self.indent += " "
|
||||||
|
|
||||||
# We could sort these together to minimize different layer sections, but meh.
|
# We could sort these together to minimize different layer sections, but meh.
|
||||||
pin_list = self.get_pins(name)
|
pin_list = self.get_pins(name)
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer))
|
self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[pin.layer]))
|
||||||
self.lef_write_shape(pin.rect)
|
self.lef_write_shape(pin.rect)
|
||||||
|
|
||||||
# End the PORT
|
# End the PORT
|
||||||
self.indent = self.indent[:-3]
|
self.indent = self.indent[:-3]
|
||||||
self.lef.write("{0}END\n".format(self.indent))
|
self.lef.write("{0}END\n".format(self.indent))
|
||||||
|
|
||||||
# End the PIN
|
# End the PIN
|
||||||
self.indent = self.indent[:-3]
|
self.indent = self.indent[:-3]
|
||||||
self.lef.write("{0}END {1}\n".format(self.indent,name))
|
self.lef.write("{0}END {1}\n".format(self.indent, name))
|
||||||
|
|
||||||
def lef_write_obstructions(self):
|
def lef_write_obstructions(self):
|
||||||
""" Write all the obstructions on each layer """
|
""" Write all the obstructions on each layer """
|
||||||
self.lef.write("{0}OBS\n".format(self.indent))
|
self.lef.write("{0}OBS\n".format(self.indent))
|
||||||
for layer in self.lef_layers:
|
for layer in self.lef_layers:
|
||||||
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,layer))
|
self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[layer]))
|
||||||
self.indent += " "
|
self.indent += " "
|
||||||
# pdb.set_trace()
|
blockages = self.get_blockages(layer, True)
|
||||||
blockages = self.get_blockages(layer,True)
|
|
||||||
for b in blockages:
|
for b in blockages:
|
||||||
# if len(b) > 2:
|
|
||||||
# print(b)
|
|
||||||
self.lef_write_shape(b)
|
self.lef_write_shape(b)
|
||||||
self.indent = self.indent[:-3]
|
self.indent = self.indent[:-3]
|
||||||
self.lef.write("{0}END\n".format(self.indent))
|
self.lef.write("{0}END\n".format(self.indent))
|
||||||
|
|
||||||
def lef_write_shape(self, rect):
|
def lef_write_shape(self, rect):
|
||||||
if len(rect) == 2:
|
if len(rect) == 2:
|
||||||
""" Write a LEF rectangle """
|
""" Write a LEF rectangle """
|
||||||
self.lef.write("{0}RECT ".format(self.indent))
|
self.lef.write("{0}RECT ".format(self.indent))
|
||||||
for item in rect:
|
for item in rect:
|
||||||
# print(rect)
|
# print(rect)
|
||||||
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
|
self.lef.write(" {0} {1}".format(round(item[0],
|
||||||
|
self.round_grid),
|
||||||
|
round(item[1],
|
||||||
|
self.round_grid)))
|
||||||
self.lef.write(" ;\n")
|
self.lef.write(" ;\n")
|
||||||
else:
|
else:
|
||||||
""" Write a LEF polygon """
|
""" Write a LEF polygon """
|
||||||
self.lef.write("{0}POLYGON ".format(self.indent))
|
self.lef.write("{0}POLYGON ".format(self.indent))
|
||||||
for item in rect:
|
for item in rect:
|
||||||
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
|
self.lef.write(" {0} {1}".format(round(item[0],
|
||||||
|
self.round_grid),
|
||||||
|
round(item[1],
|
||||||
|
self.round_grid)))
|
||||||
# for i in range(0,len(rect)):
|
# for i in range(0,len(rect)):
|
||||||
# self.lef.write(" {0} {1}".format(round(rect[i][0],self.round_grid), round(rect[i][1],self.round_grid)))
|
# self.lef.write(" {0} {1}".format(round(rect[i][0],self.round_grid), round(rect[i][1],self.round_grid)))
|
||||||
self.lef.write(" ;\n")
|
self.lef.write(" ;\n")
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class pin_layout:
|
||||||
|
|
||||||
# These are the valid pin layers
|
# These are the valid pin layers
|
||||||
valid_layers = { x: layer[x] for x in layer_indices.keys()}
|
valid_layers = { x: layer[x] for x in layer_indices.keys()}
|
||||||
|
|
||||||
# if it's a string, use the name
|
# if it's a string, use the name
|
||||||
if type(layer_name_pp) == str:
|
if type(layer_name_pp) == str:
|
||||||
self._layer = layer_name_pp
|
self._layer = layer_name_pp
|
||||||
|
|
@ -378,7 +378,7 @@ class pin_layout:
|
||||||
from tech import label_purpose
|
from tech import label_purpose
|
||||||
except ImportError:
|
except ImportError:
|
||||||
label_purpose = purpose
|
label_purpose = purpose
|
||||||
|
|
||||||
newLayout.addBox(layerNumber=layer_num,
|
newLayout.addBox(layerNumber=layer_num,
|
||||||
purposeNumber=pin_purpose,
|
purposeNumber=pin_purpose,
|
||||||
offsetInMicrons=self.ll(),
|
offsetInMicrons=self.ll(),
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@ from vector3d import vector3d
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
class route(design):
|
class route(design):
|
||||||
"""
|
"""
|
||||||
Object route (used by the router module)
|
Object route (used by the router module)
|
||||||
Add a route of minimium metal width between a set of points.
|
Add a route of minimium metal width between a set of points.
|
||||||
The widths are the layer widths of the layer stack.
|
The widths are the layer widths of the layer stack.
|
||||||
(Vias are in numer of vias.)
|
(Vias are in numer of vias.)
|
||||||
The wire must be completely rectilinear and the
|
The wire must be completely rectilinear and the
|
||||||
z-dimension of the points refers to the layers.
|
z-dimension of the points refers to the layers.
|
||||||
The points are the center of the wire.
|
The points are the center of the wire.
|
||||||
This can have non-preferred direction routing.
|
This can have non-preferred direction routing.
|
||||||
|
|
@ -45,12 +45,12 @@ class route(design):
|
||||||
def setup_layers(self):
|
def setup_layers(self):
|
||||||
(self.horiz_layer_name, self.via_layer, self.vert_layer_name) = self.layer_stack
|
(self.horiz_layer_name, self.via_layer, self.vert_layer_name) = self.layer_stack
|
||||||
(self.horiz_layer_width, self.num_vias, self.vert_layer_width) = self.layer_widths
|
(self.horiz_layer_width, self.num_vias, self.vert_layer_width) = self.layer_widths
|
||||||
|
|
||||||
if not self.vert_layer_width:
|
if not self.vert_layer_width:
|
||||||
self.vert_layer_width = drc("minwidth_{0}".format(self.vert_layer_name))
|
self.vert_layer_width = drc("minwidth_{0}".format(self.vert_layer_name))
|
||||||
if not self.horiz_layer_width:
|
if not self.horiz_layer_width:
|
||||||
self.horiz_layer_width = drc("minwidth_{0}".format(self.horiz_layer_name))
|
self.horiz_layer_width = drc("minwidth_{0}".format(self.horiz_layer_name))
|
||||||
|
|
||||||
# offset this by 1/2 the via size
|
# offset this by 1/2 the via size
|
||||||
self.c=factory.create(module_type="contact",
|
self.c=factory.create(module_type="contact",
|
||||||
layer_stack=self.layer_stack,
|
layer_stack=self.layer_stack,
|
||||||
|
|
@ -58,7 +58,7 @@ class route(design):
|
||||||
|
|
||||||
|
|
||||||
def create_wires(self):
|
def create_wires(self):
|
||||||
"""
|
"""
|
||||||
Add the wire segments of the route.
|
Add the wire segments of the route.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -67,7 +67,7 @@ class route(design):
|
||||||
a, b = tee(iterable)
|
a, b = tee(iterable)
|
||||||
next(b, None)
|
next(b, None)
|
||||||
return zip(a, b)
|
return zip(a, b)
|
||||||
|
|
||||||
plist = list(pairwise(self.path))
|
plist = list(pairwise(self.path))
|
||||||
for p0,p1 in plist:
|
for p0,p1 in plist:
|
||||||
if p0.z != p1.z: # via
|
if p0.z != p1.z: # via
|
||||||
|
|
@ -87,10 +87,10 @@ class route(design):
|
||||||
self.draw_corner_wire(plist[0][0])
|
self.draw_corner_wire(plist[0][0])
|
||||||
self.draw_corner_wire(plist[-1][1])
|
self.draw_corner_wire(plist[-1][1])
|
||||||
|
|
||||||
|
|
||||||
def get_layer_width(self, layer_zindex):
|
def get_layer_width(self, layer_zindex):
|
||||||
"""
|
"""
|
||||||
Return the layer width
|
Return the layer width
|
||||||
"""
|
"""
|
||||||
if layer_zindex==0:
|
if layer_zindex==0:
|
||||||
return self.horiz_layer_width
|
return self.horiz_layer_width
|
||||||
|
|
@ -109,11 +109,11 @@ class route(design):
|
||||||
return self.vert_layer_name
|
return self.vert_layer_name
|
||||||
else:
|
else:
|
||||||
debug.error("Incorrect layer zindex.",-1)
|
debug.error("Incorrect layer zindex.",-1)
|
||||||
|
|
||||||
|
|
||||||
def draw_wire(self, p0, p1):
|
def draw_wire(self, p0, p1):
|
||||||
"""
|
"""
|
||||||
This draws a straight wire with layer_minwidth
|
This draws a straight wire with layer_minwidth
|
||||||
"""
|
"""
|
||||||
|
|
||||||
layer_width = self.get_layer_width(p0.z)
|
layer_width = self.get_layer_width(p0.z)
|
||||||
|
|
@ -145,8 +145,8 @@ class route(design):
|
||||||
offset=vector(offset.x,offset.y),
|
offset=vector(offset.x,offset.y),
|
||||||
width=width,
|
width=width,
|
||||||
height=height)
|
height=height)
|
||||||
|
|
||||||
|
|
||||||
def draw_corner_wire(self, p0):
|
def draw_corner_wire(self, p0):
|
||||||
""" This function adds the corner squares since the center
|
""" This function adds the corner squares since the center
|
||||||
line convention only draws to the center of the corner."""
|
line convention only draws to the center of the corner."""
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
|
||||||
|
import os
|
||||||
|
import math
|
||||||
|
|
||||||
import gdsMill
|
import gdsMill
|
||||||
import tech
|
import tech
|
||||||
import math
|
|
||||||
import globals
|
import globals
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from vector import vector
|
||||||
|
|
@ -57,10 +59,11 @@ def auto_measure_libcell(pin_list, name, units, lpp):
|
||||||
Return these as a set of properties including the cell width/height too.
|
Return these as a set of properties including the cell width/height too.
|
||||||
"""
|
"""
|
||||||
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
|
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
|
||||||
cell_vlsi = gdsMill.VlsiLayout(units=units)
|
|
||||||
reader = gdsMill.Gds2reader(cell_vlsi)
|
|
||||||
reader.loadFromFile(cell_gds)
|
|
||||||
|
|
||||||
|
cell_vlsi = _get_gds_reader(units, cell_gds)
|
||||||
|
|
||||||
|
# FIXME: This duplicates a lot of functionality of get_gds_size and
|
||||||
|
# get_gds_pins, it should probably just call those functions?
|
||||||
cell = {}
|
cell = {}
|
||||||
measure_result = cell_vlsi.getLayoutBorder(lpp[0])
|
measure_result = cell_vlsi.getLayoutBorder(lpp[0])
|
||||||
if measure_result:
|
if measure_result:
|
||||||
|
|
@ -73,22 +76,47 @@ def auto_measure_libcell(pin_list, name, units, lpp):
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
|
|
||||||
|
_GDS_READER_CACHE = {}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_gds_reader(units, gds_filename):
|
||||||
|
gds_absname = os.path.realpath(gds_filename)
|
||||||
|
k = (units, gds_absname)
|
||||||
|
try:
|
||||||
|
return _GDS_READER_CACHE[k]
|
||||||
|
except KeyError:
|
||||||
|
debug.info(4, "Creating VLSI layout from {}".format(gds_absname))
|
||||||
|
cell_vlsi = gdsMill.VlsiLayout(units=units)
|
||||||
|
reader = gdsMill.Gds2reader(cell_vlsi)
|
||||||
|
reader.loadFromFile(gds_absname)
|
||||||
|
|
||||||
|
_GDS_READER_CACHE[k] = cell_vlsi
|
||||||
|
return cell_vlsi
|
||||||
|
|
||||||
|
|
||||||
|
_GDS_SIZE_CACHE = {}
|
||||||
|
|
||||||
|
|
||||||
def get_gds_size(name, gds_filename, units, lpp):
|
def get_gds_size(name, gds_filename, units, lpp):
|
||||||
"""
|
"""
|
||||||
Open a GDS file and return the size from either the
|
Open a GDS file and return the size from either the
|
||||||
bounding box or a border layer.
|
bounding box or a border layer.
|
||||||
"""
|
"""
|
||||||
debug.info(4, "Creating VLSI layout for {}".format(name))
|
k = (name, os.path.realpath(gds_filename), units, lpp)
|
||||||
cell_vlsi = gdsMill.VlsiLayout(units=units)
|
try:
|
||||||
reader = gdsMill.Gds2reader(cell_vlsi)
|
return _GDS_SIZE_CACHE[k]
|
||||||
reader.loadFromFile(gds_filename)
|
except KeyError:
|
||||||
|
cell_vlsi = _get_gds_reader(units, gds_filename)
|
||||||
|
|
||||||
measure_result = cell_vlsi.getLayoutBorder(lpp)
|
measure_result = cell_vlsi.getLayoutBorder(lpp)
|
||||||
if not measure_result:
|
if not measure_result:
|
||||||
debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
|
debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
|
||||||
measure_result = cell_vlsi.measureSize(name)
|
measure_result = cell_vlsi.measureSize(name)
|
||||||
# returns width,height
|
|
||||||
return measure_result
|
_GDS_SIZE_CACHE[k] = measure_result
|
||||||
|
|
||||||
|
# returns width,height
|
||||||
|
return measure_result
|
||||||
|
|
||||||
|
|
||||||
def get_libcell_size(name, units, lpp):
|
def get_libcell_size(name, units, lpp):
|
||||||
|
|
@ -101,27 +129,34 @@ def get_libcell_size(name, units, lpp):
|
||||||
return(get_gds_size(name, cell_gds, units, lpp))
|
return(get_gds_size(name, cell_gds, units, lpp))
|
||||||
|
|
||||||
|
|
||||||
|
_GDS_PINS_CACHE = {}
|
||||||
|
|
||||||
|
|
||||||
def get_gds_pins(pin_names, name, gds_filename, units):
|
def get_gds_pins(pin_names, name, gds_filename, units):
|
||||||
"""
|
"""
|
||||||
Open a GDS file and find the pins in pin_names as text on a given layer.
|
Open a GDS file and find the pins in pin_names as text on a given layer.
|
||||||
Return these as a rectangle layer pair for each pin.
|
Return these as a rectangle layer pair for each pin.
|
||||||
"""
|
"""
|
||||||
cell_vlsi = gdsMill.VlsiLayout(units=units)
|
k = (tuple(pin_names), name, os.path.realpath(gds_filename), units)
|
||||||
reader = gdsMill.Gds2reader(cell_vlsi)
|
try:
|
||||||
reader.loadFromFile(gds_filename)
|
return dict(_GDS_PINS_CACHE[k])
|
||||||
|
except KeyError:
|
||||||
|
cell_vlsi = _get_gds_reader(units, gds_filename)
|
||||||
|
|
||||||
cell = {}
|
cell = {}
|
||||||
for pin_name in pin_names:
|
for pin_name in pin_names:
|
||||||
cell[str(pin_name)] = []
|
cell[str(pin_name)] = []
|
||||||
pin_list = cell_vlsi.getPinShape(str(pin_name))
|
pin_list = cell_vlsi.getPinShape(str(pin_name))
|
||||||
for pin_shape in pin_list:
|
for pin_shape in pin_list:
|
||||||
(lpp, boundary) = pin_shape
|
(lpp, boundary) = pin_shape
|
||||||
rect = [vector(boundary[0], boundary[1]),
|
rect = [vector(boundary[0], boundary[1]),
|
||||||
vector(boundary[2], boundary[3])]
|
vector(boundary[2], boundary[3])]
|
||||||
# this is a list because other cells/designs
|
# this is a list because other cells/designs
|
||||||
# may have must-connect pins
|
# may have must-connect pins
|
||||||
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
|
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
|
||||||
return cell
|
|
||||||
|
_GDS_PINS_CACHE[k] = cell
|
||||||
|
return dict(cell)
|
||||||
|
|
||||||
|
|
||||||
def get_libcell_pins(pin_list, name, units):
|
def get_libcell_pins(pin_list, name, units):
|
||||||
|
|
@ -132,7 +167,3 @@ def get_libcell_pins(pin_list, name, units):
|
||||||
|
|
||||||
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
|
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
|
||||||
return(get_gds_pins(pin_list, name, cell_gds, units))
|
return(get_gds_pins(pin_list, name, cell_gds, units))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@ class vector():
|
||||||
return "v["+str(self.x)+","+str(self.y)+"]"
|
return "v["+str(self.x)+","+str(self.y)+"]"
|
||||||
|
|
||||||
def __setitem__(self, index, value):
|
def __setitem__(self, index, value):
|
||||||
"""
|
"""
|
||||||
override setitem function
|
override setitem function
|
||||||
can set value by vector[index]=value
|
can set value by vector[index]=value
|
||||||
"""
|
"""
|
||||||
if index==0:
|
if index==0:
|
||||||
|
|
@ -50,10 +50,10 @@ class vector():
|
||||||
else:
|
else:
|
||||||
self.x=float(value[0])
|
self.x=float(value[0])
|
||||||
self.y=float(value[1])
|
self.y=float(value[1])
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"""
|
"""
|
||||||
override getitem function
|
override getitem function
|
||||||
can get value by value=vector[index]
|
can get value by value=vector[index]
|
||||||
"""
|
"""
|
||||||
if index==0:
|
if index==0:
|
||||||
|
|
@ -61,7 +61,7 @@ class vector():
|
||||||
elif index==1:
|
elif index==1:
|
||||||
return self.y
|
return self.y
|
||||||
else:
|
else:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
"""
|
"""
|
||||||
|
|
@ -109,7 +109,7 @@ class vector():
|
||||||
"""
|
"""
|
||||||
Changes the coodrinate to match the grid settings
|
Changes the coodrinate to match the grid settings
|
||||||
"""
|
"""
|
||||||
grid = tech.drc["grid"]
|
grid = tech.drc["grid"]
|
||||||
# this gets the nearest integer value
|
# this gets the nearest integer value
|
||||||
off_in_grid = int(round(round((offset / grid), 2), 0))
|
off_in_grid = int(round(round((offset / grid), 2), 0))
|
||||||
offset = off_in_grid * grid
|
offset = off_in_grid * grid
|
||||||
|
|
@ -150,8 +150,8 @@ class vector():
|
||||||
Override round function
|
Override round function
|
||||||
"""
|
"""
|
||||||
return vector(int(round(self.x)),int(round(self.y)))
|
return vector(int(round(self.x)),int(round(self.y)))
|
||||||
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Override the default Equals behavior"""
|
"""Override the default Equals behavior"""
|
||||||
if isinstance(other, self.__class__):
|
if isinstance(other, self.__class__):
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ import debug
|
||||||
import math
|
import math
|
||||||
|
|
||||||
class verilog:
|
class verilog:
|
||||||
"""
|
"""
|
||||||
Create a behavioral Verilog file for simulation.
|
Create a behavioral Verilog file for simulation.
|
||||||
This is inherited by the sram_base class.
|
This is inherited by the sram_base class.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def verilog_write(self,verilog_name):
|
def verilog_write(self,verilog_name):
|
||||||
""" Write a behavioral Verilog model. """
|
""" Write a behavioral Verilog model. """
|
||||||
self.vf = open(verilog_name, "w")
|
self.vf = open(verilog_name, "w")
|
||||||
|
|
@ -67,7 +67,7 @@ class verilog:
|
||||||
self.add_inputs_outputs(port)
|
self.add_inputs_outputs(port)
|
||||||
|
|
||||||
self.vf.write("\n")
|
self.vf.write("\n")
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.register_inputs(port)
|
self.register_inputs(port)
|
||||||
|
|
||||||
|
|
@ -79,8 +79,8 @@ class verilog:
|
||||||
self.add_write_block(port)
|
self.add_write_block(port)
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
self.add_read_block(port)
|
self.add_read_block(port)
|
||||||
|
|
||||||
self.vf.write("\n")
|
self.vf.write("\n")
|
||||||
self.vf.write("endmodule\n")
|
self.vf.write("endmodule\n")
|
||||||
self.vf.close()
|
self.vf.close()
|
||||||
|
|
||||||
|
|
@ -91,9 +91,9 @@ class verilog:
|
||||||
"""
|
"""
|
||||||
self.add_regs(port)
|
self.add_regs(port)
|
||||||
self.add_flops(port)
|
self.add_flops(port)
|
||||||
|
|
||||||
def add_regs(self, port):
|
def add_regs(self, port):
|
||||||
"""
|
"""
|
||||||
Create the input regs for the given port.
|
Create the input regs for the given port.
|
||||||
"""
|
"""
|
||||||
self.vf.write(" reg csb{0}_reg;\n".format(port))
|
self.vf.write(" reg csb{0}_reg;\n".format(port))
|
||||||
|
|
@ -107,7 +107,7 @@ class verilog:
|
||||||
self.vf.write(" reg [DATA_WIDTH-1:0] din{0}_reg;\n".format(port))
|
self.vf.write(" reg [DATA_WIDTH-1:0] din{0}_reg;\n".format(port))
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
self.vf.write(" reg [DATA_WIDTH-1:0] dout{0};\n".format(port))
|
self.vf.write(" reg [DATA_WIDTH-1:0] dout{0};\n".format(port))
|
||||||
|
|
||||||
def add_flops(self, port):
|
def add_flops(self, port):
|
||||||
"""
|
"""
|
||||||
Add the flop behavior logic for a port.
|
Add the flop behavior logic for a port.
|
||||||
|
|
@ -125,7 +125,7 @@ class verilog:
|
||||||
self.vf.write(" addr{0}_reg = addr{0};\n".format(port))
|
self.vf.write(" addr{0}_reg = addr{0};\n".format(port))
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
self.add_write_read_checks(port)
|
self.add_write_read_checks(port)
|
||||||
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
self.vf.write(" din{0}_reg = din{0};\n".format(port))
|
self.vf.write(" din{0}_reg = din{0};\n".format(port))
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
|
|
@ -150,7 +150,7 @@ class verilog:
|
||||||
self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b\",addr{0}_reg,din{0}_reg);\n".format(port))
|
self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b\",addr{0}_reg,din{0}_reg);\n".format(port))
|
||||||
|
|
||||||
self.vf.write(" end\n\n")
|
self.vf.write(" end\n\n")
|
||||||
|
|
||||||
|
|
||||||
def add_inputs_outputs(self, port):
|
def add_inputs_outputs(self, port):
|
||||||
"""
|
"""
|
||||||
|
|
@ -203,7 +203,7 @@ class verilog:
|
||||||
else:
|
else:
|
||||||
self.vf.write(" mem[addr{0}_reg] = din{0}_reg;\n".format(port))
|
self.vf.write(" mem[addr{0}_reg] = din{0}_reg;\n".format(port))
|
||||||
self.vf.write(" end\n")
|
self.vf.write(" end\n")
|
||||||
|
|
||||||
def add_read_block(self, port):
|
def add_read_block(self, port):
|
||||||
"""
|
"""
|
||||||
Add a read port block.
|
Add a read port block.
|
||||||
|
|
@ -231,12 +231,12 @@ class verilog:
|
||||||
wport_control = "!csb{0} && !web{0}".format(wport)
|
wport_control = "!csb{0} && !web{0}".format(wport)
|
||||||
else:
|
else:
|
||||||
wport_control = "!csb{0}".format(wport)
|
wport_control = "!csb{0}".format(wport)
|
||||||
|
|
||||||
self.vf.write(" if ({1} && {3} && (addr{0} == addr{2}))\n".format(wport,wport_control,rport,rport_control))
|
self.vf.write(" if ({1} && {3} && (addr{0} == addr{2}))\n".format(wport,wport_control,rport,rport_control))
|
||||||
self.vf.write(" $display($time,\" WARNING: Writing and reading addr{0}=%b and addr{1}=%b simultaneously!\",addr{0},addr{1});\n".format(wport,rport))
|
self.vf.write(" $display($time,\" WARNING: Writing and reading addr{0}=%b and addr{1}=%b simultaneously!\",addr{0},addr{1});\n".format(wport,rport))
|
||||||
|
|
||||||
def add_write_read_checks(self, rport):
|
def add_write_read_checks(self, rport):
|
||||||
"""
|
"""
|
||||||
Add a warning if we read from an address that we are currently writing.
|
Add a warning if we read from an address that we are currently writing.
|
||||||
Can be fixed if we appropriately size the write drivers to do this .
|
Can be fixed if we appropriately size the write drivers to do this .
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -58,13 +58,13 @@ class wire(wire_path):
|
||||||
via_connect.first_layer_width)
|
via_connect.first_layer_width)
|
||||||
self.horiz_layer_contact_width = max(via_connect.second_layer_height,
|
self.horiz_layer_contact_width = max(via_connect.second_layer_height,
|
||||||
via_connect.first_layer_height)
|
via_connect.first_layer_height)
|
||||||
|
|
||||||
self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width,
|
self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width,
|
||||||
drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height]
|
drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height]
|
||||||
self.pitch = self.compute_pitch(self.layer_stack)
|
self.pitch = self.compute_pitch(self.layer_stack)
|
||||||
|
|
||||||
def compute_pitch(self, layer_stack):
|
def compute_pitch(self, layer_stack):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This is contact direction independent pitch,
|
This is contact direction independent pitch,
|
||||||
i.e. we take the maximum contact dimension
|
i.e. we take the maximum contact dimension
|
||||||
|
|
@ -79,13 +79,13 @@ class wire(wire_path):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
contact1 = getattr(contact, layer2 + "_via")
|
contact1 = getattr(contact, layer2 + "_via")
|
||||||
max_contact = max(contact1.width, contact1.height)
|
max_contact = max(contact1.width, contact1.height)
|
||||||
|
|
||||||
layer1_space = drc("{0}_to_{0}".format(layer1))
|
layer1_space = drc("{0}_to_{0}".format(layer1))
|
||||||
layer2_space = drc("{0}_to_{0}".format(layer2))
|
layer2_space = drc("{0}_to_{0}".format(layer2))
|
||||||
pitch = max_contact + max(layer1_space, layer2_space)
|
pitch = max_contact + max(layer1_space, layer2_space)
|
||||||
|
|
||||||
return pitch
|
return pitch
|
||||||
|
|
||||||
# create a 1x1 contact
|
# create a 1x1 contact
|
||||||
def create_vias(self):
|
def create_vias(self):
|
||||||
""" Add a via and corner square at every corner of the path."""
|
""" Add a via and corner square at every corner of the path."""
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ def create_rectilinear_route(my_list):
|
||||||
""" Add intermediate nodes if it isn't rectilinear. Also skip
|
""" Add intermediate nodes if it isn't rectilinear. Also skip
|
||||||
repeated nodes. Also, convert to vector if the aren't."""
|
repeated nodes. Also, convert to vector if the aren't."""
|
||||||
pl = [snap_to_grid(x) for x in my_list]
|
pl = [snap_to_grid(x) for x in my_list]
|
||||||
|
|
||||||
my_list = []
|
my_list = []
|
||||||
for index in range(len(pl) - 1):
|
for index in range(len(pl) - 1):
|
||||||
if pl[index] != pl[index + 1]:
|
if pl[index] != pl[index + 1]:
|
||||||
|
|
@ -121,7 +121,7 @@ class wire_path():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
width = layer_width
|
width = layer_width
|
||||||
height = length
|
height = length
|
||||||
|
|
||||||
if orientation == "horizontal":
|
if orientation == "horizontal":
|
||||||
width = length
|
width = length
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class wire_spice_model():
|
||||||
"""
|
"""
|
||||||
def __init__(self, lump_num, wire_length, wire_width):
|
def __init__(self, lump_num, wire_length, wire_width):
|
||||||
self.lump_num = lump_num # the number of segment the wire delay has
|
self.lump_num = lump_num # the number of segment the wire delay has
|
||||||
self.wire_c = self.cal_wire_c(wire_length, wire_width) # c in each segment
|
self.wire_c = self.cal_wire_c(wire_length, wire_width) # c in each segment
|
||||||
self.wire_r = self.cal_wire_r(wire_length, wire_width) # r in each segment
|
self.wire_r = self.cal_wire_r(wire_length, wire_width) # r in each segment
|
||||||
|
|
||||||
def cal_wire_c(self, wire_length, wire_width):
|
def cal_wire_c(self, wire_length, wire_width):
|
||||||
|
|
@ -36,7 +36,7 @@ class wire_spice_model():
|
||||||
|
|
||||||
swing_factor = abs(math.log(1-swing)) # time constant based on swing
|
swing_factor = abs(math.log(1-swing)) # time constant based on swing
|
||||||
sum_factor = (1+self.lump_num) * self.lump_num * 0.5 # sum of the arithmetic sequence
|
sum_factor = (1+self.lump_num) * self.lump_num * 0.5 # sum of the arithmetic sequence
|
||||||
delay = sum_factor * swing_factor * self.wire_r * self.wire_c
|
delay = sum_factor * swing_factor * self.wire_r * self.wire_c
|
||||||
slew = delay * 2 + slew
|
slew = delay * 2 + slew
|
||||||
result= delay_data(delay, slew)
|
result= delay_data(delay, slew)
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,9 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
from globals import OPTS
|
|
||||||
|
|
||||||
class bitcell(bitcell_base.bitcell_base):
|
class bitcell(bitcell_base.bitcell_base):
|
||||||
"""
|
"""
|
||||||
|
|
@ -20,8 +18,6 @@ class bitcell(bitcell_base.bitcell_base):
|
||||||
library.
|
library.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# If we have a split WL bitcell, if not be backwards
|
|
||||||
# compatible in the tech file
|
|
||||||
pin_names = [props.bitcell.cell_6t.pin.bl,
|
pin_names = [props.bitcell.cell_6t.pin.bl,
|
||||||
props.bitcell.cell_6t.pin.br,
|
props.bitcell.cell_6t.pin.br,
|
||||||
props.bitcell.cell_6t.pin.wl,
|
props.bitcell.cell_6t.pin.wl,
|
||||||
|
|
@ -30,24 +26,12 @@ class bitcell(bitcell_base.bitcell_base):
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
storage_nets = ['Q', 'Q_bar']
|
storage_nets = ['Q', 'Q_bar']
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("cell_6t",
|
def __init__(self, name):
|
||||||
GDS["unit"],
|
super().__init__(name)
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
|
||||||
# Ignore the name argument
|
|
||||||
bitcell_base.bitcell_base.__init__(self, "cell_6t")
|
|
||||||
debug.info(2, "Create bitcell")
|
debug.info(2, "Create bitcell")
|
||||||
|
|
||||||
self.width = bitcell.width
|
|
||||||
self.height = bitcell.height
|
|
||||||
self.pin_map = bitcell.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
self.nets_match = self.do_nets_exist(self.storage_nets)
|
self.nets_match = self.do_nets_exist(self.storage_nets)
|
||||||
|
|
||||||
#debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells")
|
|
||||||
|
|
||||||
def get_all_wl_names(self):
|
def get_all_wl_names(self):
|
||||||
""" Creates a list of all wordline pin names """
|
""" Creates a list of all wordline pin names """
|
||||||
row_pins = [props.bitcell.cell_6t.pin.wl]
|
row_pins = [props.bitcell.cell_6t.pin.wl]
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer, parameter, drc
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import logical_effort
|
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -29,33 +26,23 @@ class bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
props.bitcell.cell_1rw1r.pin.wl1,
|
props.bitcell.cell_1rw1r.pin.wl1,
|
||||||
props.bitcell.cell_1rw1r.pin.vdd,
|
props.bitcell.cell_1rw1r.pin.vdd,
|
||||||
props.bitcell.cell_1rw1r.pin.gnd]
|
props.bitcell.cell_1rw1r.pin.gnd]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
||||||
"INPUT", "INPUT", "POWER", "GROUND"]
|
"INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
storage_nets = ['Q', 'Q_bar']
|
storage_nets = ['Q', 'Q_bar']
|
||||||
(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"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
bitcell_base.bitcell_base.__init__(self, "cell_1rw_1r")
|
|
||||||
debug.info(2, "Create bitcell with 1RW and 1R Port")
|
debug.info(2, "Create bitcell with 1RW and 1R Port")
|
||||||
|
|
||||||
self.width = bitcell_1rw_1r.width
|
|
||||||
self.height = bitcell_1rw_1r.height
|
|
||||||
self.pin_map = bitcell_1rw_1r.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
self.nets_match = self.do_nets_exist(self.storage_nets)
|
self.nets_match = self.do_nets_exist(self.storage_nets)
|
||||||
|
|
||||||
pin_names = bitcell_1rw_1r.pin_names
|
pin_names = self.pin_names
|
||||||
self.bl_names = [pin_names[0], pin_names[2]]
|
self.bl_names = [pin_names[0], pin_names[2]]
|
||||||
self.br_names = [pin_names[1], pin_names[3]]
|
self.br_names = [pin_names[1], pin_names[3]]
|
||||||
self.wl_names = [pin_names[4], pin_names[5]]
|
self.wl_names = [pin_names[4], pin_names[5]]
|
||||||
|
|
||||||
def get_bitcell_pins(self, col, row):
|
def get_bitcell_pins(self, col, row):
|
||||||
"""
|
"""
|
||||||
Creates a list of connections in the bitcell,
|
Creates a list of connections in the bitcell,
|
||||||
indexed by column and row, for instance use in bitcell_array
|
indexed by column and row, for instance use in bitcell_array
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
@ -31,28 +29,18 @@ class bitcell_1w_1r(bitcell_base.bitcell_base):
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
|
||||||
"INPUT", "INPUT", "POWER", "GROUND"]
|
"INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
storage_nets = ['Q', 'Q_bar']
|
storage_nets = ['Q', 'Q_bar']
|
||||||
(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"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
bitcell_base.bitcell_base.__init__(self, "cell_1w_1r")
|
|
||||||
debug.info(2, "Create bitcell with 1W and 1R Port")
|
debug.info(2, "Create bitcell with 1W and 1R Port")
|
||||||
|
|
||||||
self.width = bitcell_1w_1r.width
|
|
||||||
self.height = bitcell_1w_1r.height
|
|
||||||
self.pin_map = bitcell_1w_1r.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
self.nets_match = self.do_nets_exist(self.storage_nets)
|
self.nets_match = self.do_nets_exist(self.storage_nets)
|
||||||
|
|
||||||
pin_names = bitcell_1w_1r.pin_names
|
pin_names = self.pin_names
|
||||||
self.bl_names = [pin_names[0], pin_names[2]]
|
self.bl_names = [pin_names[0], pin_names[2]]
|
||||||
self.br_names = [pin_names[1], pin_names[3]]
|
self.br_names = [pin_names[1], pin_names[3]]
|
||||||
self.wl_names = [pin_names[4], pin_names[5]]
|
self.wl_names = [pin_names[4], pin_names[5]]
|
||||||
|
|
||||||
|
|
||||||
def get_bitcell_pins(self, col, row):
|
def get_bitcell_pins(self, col, row):
|
||||||
"""
|
"""
|
||||||
Creates a list of connections in the bitcell,
|
Creates a list of connections in the bitcell,
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,30 @@
|
||||||
|
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
|
import utils
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import logical_effort
|
import logical_effort
|
||||||
from tech import parameter, drc, layer
|
from tech import GDS, parameter, drc, layer
|
||||||
|
|
||||||
|
|
||||||
class bitcell_base(design.design):
|
class bitcell_base(design.design):
|
||||||
"""
|
"""
|
||||||
Base bitcell parameters to be over-riden.
|
Base bitcell parameters to be over-riden.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name):
|
cell_size_layer = "boundary"
|
||||||
|
|
||||||
|
def __init__(self, name, hard_cell=True):
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
if hard_cell:
|
||||||
|
(self.width, self.height) = utils.get_libcell_size(self.cell_name,
|
||||||
|
GDS["unit"],
|
||||||
|
layer[self.cell_size_layer])
|
||||||
|
self.pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
self.cell_name,
|
||||||
|
GDS["unit"])
|
||||||
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def get_stage_effort(self, load):
|
def get_stage_effort(self, load):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
# This accounts for bitline being drained
|
# This accounts for bitline being drained
|
||||||
|
|
@ -49,13 +61,13 @@ class bitcell_base(design.design):
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
""" Return the relative capacitance of the access transistor gates """
|
""" Return the relative capacitance of the access transistor gates """
|
||||||
|
|
||||||
# FIXME: This applies to bitline capacitances as well.
|
# FIXME: This applies to bitline capacitances as well.
|
||||||
# FIXME: sizing is not accurate with the handmade cell.
|
# FIXME: sizing is not accurate with the handmade cell.
|
||||||
# Change once cell widths are fixed.
|
# Change once cell widths are fixed.
|
||||||
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
|
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
|
||||||
return 2 * access_tx_cin
|
return 2 * access_tx_cin
|
||||||
|
|
||||||
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"""
|
||||||
# This is a handmade cell so the value must be entered
|
# This is a handmade cell so the value must be entered
|
||||||
|
|
@ -82,7 +94,7 @@ class bitcell_base(design.design):
|
||||||
|
|
||||||
def get_storage_net_offset(self):
|
def get_storage_net_offset(self):
|
||||||
"""
|
"""
|
||||||
Gets the location of the storage net labels to add top level
|
Gets the location of the storage net labels to add top level
|
||||||
labels for pex simulation.
|
labels for pex simulation.
|
||||||
"""
|
"""
|
||||||
# If we generated the bitcell, we already know where Q and Q_bar are
|
# If we generated the bitcell, we already know where Q and Q_bar are
|
||||||
|
|
@ -92,7 +104,7 @@ class bitcell_base(design.design):
|
||||||
for text in self.gds.getTexts(layer["m1"]):
|
for text in self.gds.getTexts(layer["m1"]):
|
||||||
if self.storage_nets[i] == text.textString.rstrip('\x00'):
|
if self.storage_nets[i] == text.textString.rstrip('\x00'):
|
||||||
self.storage_net_offsets.append(text.coordinates[0])
|
self.storage_net_offsets.append(text.coordinates[0])
|
||||||
|
|
||||||
for i in range(len(self.storage_net_offsets)):
|
for i in range(len(self.storage_net_offsets)):
|
||||||
self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]])
|
self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]])
|
||||||
|
|
||||||
|
|
@ -116,7 +128,7 @@ class bitcell_base(design.design):
|
||||||
if bl_names[i] == text.textString.rstrip('\x00'):
|
if bl_names[i] == text.textString.rstrip('\x00'):
|
||||||
self.bl_offsets.append(text.coordinates[0])
|
self.bl_offsets.append(text.coordinates[0])
|
||||||
found_bl.append(bl_names[i])
|
found_bl.append(bl_names[i])
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for i in range(len(br_names)):
|
for i in range(len(br_names)):
|
||||||
|
|
@ -131,16 +143,16 @@ class bitcell_base(design.design):
|
||||||
self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]])
|
self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]])
|
||||||
|
|
||||||
for i in range(len(self.br_offsets)):
|
for i in range(len(self.br_offsets)):
|
||||||
self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]])
|
self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]])
|
||||||
|
|
||||||
return(self.bl_offsets, self.br_offsets, found_bl, found_br)
|
return(self.bl_offsets, self.br_offsets, found_bl, found_br)
|
||||||
|
|
||||||
def get_normalized_storage_nets_offset(self):
|
def get_normalized_storage_nets_offset(self):
|
||||||
"""
|
"""
|
||||||
Convert storage net offset to be relative to the bottom left corner
|
Convert storage net offset to be relative to the bottom left corner
|
||||||
of the bitcell. This is useful for making sense of offsets outside
|
of the bitcell. This is useful for making sense of offsets outside
|
||||||
of the bitcell.
|
of the bitcell.
|
||||||
"""
|
"""
|
||||||
if OPTS.bitcell is not "pbitcell":
|
if OPTS.bitcell is not "pbitcell":
|
||||||
normalized_storage_net_offset = self.get_storage_net_offset()
|
normalized_storage_net_offset = self.get_storage_net_offset()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,39 +6,25 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
||||||
class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
"""
|
"""
|
||||||
todo"""
|
Column end cap cell.
|
||||||
|
"""
|
||||||
|
|
||||||
pin_names = [props.bitcell.cell_1rw1r.pin.bl0,
|
pin_names = [props.bitcell.cell_1rw1r.pin.bl0,
|
||||||
props.bitcell.cell_1rw1r.pin.br0,
|
props.bitcell.cell_1rw1r.pin.br0,
|
||||||
props.bitcell.cell_1rw1r.pin.bl1,
|
props.bitcell.cell_1rw1r.pin.bl1,
|
||||||
props.bitcell.cell_1rw1r.pin.br1,
|
props.bitcell.cell_1rw1r.pin.br1,
|
||||||
props.bitcell.cell_1rw1r.pin.vdd]
|
props.bitcell.cell_1rw1r.pin.vdd]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
||||||
"POWER", "GROUND"]
|
"POWER", "GROUND"]
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r",
|
def __init__(self, name="col_cap_cell_1rw_1r"):
|
||||||
GDS["unit"],
|
bitcell_base.bitcell_base.__init__(self, name)
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names,
|
|
||||||
"col_cap_cell_1rw_1r",
|
|
||||||
GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
|
||||||
# Ignore the name argument
|
|
||||||
bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r")
|
|
||||||
debug.info(2, "Create col_cap bitcell 1rw+1r object")
|
debug.info(2, "Create col_cap bitcell 1rw+1r object")
|
||||||
|
|
||||||
self.width = col_cap_bitcell_1rw_1r.width
|
|
||||||
self.height = col_cap_bitcell_1rw_1r.height
|
|
||||||
self.pin_map = col_cap_bitcell_1rw_1r.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
self.no_instances = True
|
self.no_instances = True
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
@ -24,19 +22,10 @@ class dummy_bitcell(bitcell_base.bitcell_base):
|
||||||
props.bitcell.cell_6t.pin.wl,
|
props.bitcell.cell_6t.pin.wl,
|
||||||
props.bitcell.cell_6t.pin.vdd,
|
props.bitcell.cell_6t.pin.vdd,
|
||||||
props.bitcell.cell_6t.pin.gnd]
|
props.bitcell.cell_6t.pin.gnd]
|
||||||
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("dummy_cell_6t",
|
def __init__(self, name):
|
||||||
GDS["unit"],
|
super().__init__(name)
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
|
||||||
# Ignore the name argument
|
|
||||||
bitcell_base.bitcell_base.__init__(self, "dummy_cell_6t")
|
|
||||||
debug.info(2, "Create dummy bitcell")
|
debug.info(2, "Create dummy bitcell")
|
||||||
|
|
||||||
self.width = dummy_bitcell.width
|
|
||||||
self.height = dummy_bitcell.height
|
|
||||||
self.pin_map = dummy_bitcell.pin_map
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
@ -27,23 +25,11 @@ class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
props.bitcell.cell_1rw1r.pin.wl1,
|
props.bitcell.cell_1rw1r.pin.wl1,
|
||||||
props.bitcell.cell_1rw1r.pin.vdd,
|
props.bitcell.cell_1rw1r.pin.vdd,
|
||||||
props.bitcell.cell_1rw1r.pin.gnd]
|
props.bitcell.cell_1rw1r.pin.gnd]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
||||||
"INPUT", "INPUT", "POWER", "GROUND"]
|
"INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
(width, height) = utils.get_libcell_size("dummy_cell_1rw_1r",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names,
|
|
||||||
"dummy_cell_1rw_1r",
|
|
||||||
GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r")
|
|
||||||
debug.info(2, "Create dummy bitcell 1rw+1r object")
|
debug.info(2, "Create dummy bitcell 1rw+1r object")
|
||||||
|
|
||||||
self.width = dummy_bitcell_1rw_1r.width
|
|
||||||
self.height = dummy_bitcell_1rw_1r.height
|
|
||||||
self.pin_map = dummy_bitcell_1rw_1r.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
@ -29,21 +27,11 @@ class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
|
||||||
props.bitcell.cell_1w1r.pin.gnd]
|
props.bitcell.cell_1w1r.pin.gnd]
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
|
||||||
"INPUT", "INPUT", "POWER", "GROUND"]
|
"INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
(width, height) = utils.get_libcell_size("dummy_cell_1w_1r",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names,
|
|
||||||
"dummy_cell_1w_1r",
|
|
||||||
GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r")
|
|
||||||
debug.info(2, "Create dummy bitcell 1w+1r object")
|
debug.info(2, "Create dummy bitcell 1w+1r object")
|
||||||
|
|
||||||
self.width = dummy_bitcell_1w_1r.width
|
|
||||||
self.height = dummy_bitcell_1w_1r.height
|
|
||||||
self.pin_map = dummy_bitcell_1w_1r.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,85 +7,86 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
from tech import drc, spice,parameter
|
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class dummy_pbitcell(design.design):
|
class dummy_pbitcell(design.design):
|
||||||
"""
|
"""
|
||||||
Creates a replica bitcell using pbitcell
|
Creates a replica bitcell using pbitcell
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name, cell_name=None):
|
||||||
self.num_rw_ports = OPTS.num_rw_ports
|
self.num_rw_ports = OPTS.num_rw_ports
|
||||||
self.num_w_ports = OPTS.num_w_ports
|
self.num_w_ports = OPTS.num_w_ports
|
||||||
self.num_r_ports = OPTS.num_r_ports
|
self.num_r_ports = OPTS.num_r_ports
|
||||||
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
|
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
|
||||||
|
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(1, "create a dummy bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
|
debug.info(1, "create a dummy bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
|
||||||
self.num_w_ports,
|
self.num_w_ports,
|
||||||
self.num_r_ports))
|
self.num_r_ports))
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_modules()
|
self.create_modules()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.place_pbitcell()
|
self.place_pbitcell()
|
||||||
self.route_rbc_connections()
|
self.route_rbc_connections()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
self.add_pin("bl{}".format(port))
|
self.add_pin("bl{}".format(port))
|
||||||
self.add_pin("br{}".format(port))
|
self.add_pin("br{}".format(port))
|
||||||
|
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
self.add_pin("wl{}".format(port))
|
self.add_pin("wl{}".format(port))
|
||||||
|
|
||||||
self.add_pin("vdd")
|
self.add_pin("vdd")
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.prbc = factory.create(module_type="pbitcell",dummy_bitcell=True)
|
self.prbc = factory.create(module_type="pbitcell",
|
||||||
|
dummy_bitcell=True)
|
||||||
self.add_mod(self.prbc)
|
self.add_mod(self.prbc)
|
||||||
|
|
||||||
self.height = self.prbc.height
|
self.height = self.prbc.height
|
||||||
self.width = self.prbc.width
|
self.width = self.prbc.width
|
||||||
|
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
self.prbc_inst = self.add_inst(name="pbitcell",
|
self.prbc_inst = self.add_inst(name="pbitcell",
|
||||||
mod=self.prbc)
|
mod=self.prbc)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
temp.append("bl{}".format(port))
|
temp.append("bl{}".format(port))
|
||||||
temp.append("br{}".format(port))
|
temp.append("br{}".format(port))
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
temp.append("wl{}".format(port))
|
temp.append("wl{}".format(port))
|
||||||
temp.append("vdd")
|
temp.append("vdd")
|
||||||
temp.append("gnd")
|
temp.append("gnd")
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
def place_pbitcell(self):
|
def place_pbitcell(self):
|
||||||
self.prbc_inst.place(offset=vector(0,0))
|
self.prbc_inst.place(offset=vector(0, 0))
|
||||||
|
|
||||||
def route_rbc_connections(self):
|
def route_rbc_connections(self):
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
self.copy_layout_pin(self.prbc_inst, "bl{}".format(port))
|
self.copy_layout_pin(self.prbc_inst, "bl{}".format(port))
|
||||||
self.copy_layout_pin(self.prbc_inst, "br{}".format(port))
|
self.copy_layout_pin(self.prbc_inst, "br{}".format(port))
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
|
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
|
||||||
self.copy_layout_pin(self.prbc_inst, "vdd")
|
self.copy_layout_pin(self.prbc_inst, "vdd")
|
||||||
self.copy_layout_pin(self.prbc_inst, "gnd")
|
self.copy_layout_pin(self.prbc_inst, "gnd")
|
||||||
|
|
||||||
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"""
|
||||||
#This module is made using a pbitcell. Get the cin from that module
|
#This module is made using a pbitcell. Get the cin from that module
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.num_w_ports = OPTS.num_w_ports
|
self.num_w_ports = OPTS.num_w_ports
|
||||||
self.num_r_ports = OPTS.num_r_ports
|
self.num_r_ports = OPTS.num_r_ports
|
||||||
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
|
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
|
||||||
|
|
||||||
self.replica_bitcell = replica_bitcell
|
self.replica_bitcell = replica_bitcell
|
||||||
self.dummy_bitcell = dummy_bitcell
|
self.dummy_bitcell = dummy_bitcell
|
||||||
|
|
||||||
bitcell_base.bitcell_base.__init__(self, name)
|
bitcell_base.bitcell_base.__init__(self, name, hard_cell=False)
|
||||||
fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
|
fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
|
||||||
info_string = fmt_str.format(self.num_rw_ports,
|
info_string = fmt_str.format(self.num_rw_ports,
|
||||||
self.num_w_ports,
|
self.num_w_ports,
|
||||||
|
|
@ -295,7 +295,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.width = -2 * self.leftmost_xpos
|
self.width = -2 * self.leftmost_xpos
|
||||||
self.height = self.topmost_ypos - self.botmost_ypos
|
self.height = self.topmost_ypos - self.botmost_ypos
|
||||||
self.center_ypos = 0.5 * (self.topmost_ypos + self.botmost_ypos)
|
self.center_ypos = 0.5 * (self.topmost_ypos + self.botmost_ypos)
|
||||||
|
|
||||||
def create_storage(self):
|
def create_storage(self):
|
||||||
"""
|
"""
|
||||||
Creates the crossed coupled inverters that act
|
Creates the crossed coupled inverters that act
|
||||||
|
|
@ -322,7 +322,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.connect_inst(["vdd", self.Q, self.Q_bar, "vdd"])
|
self.connect_inst(["vdd", self.Q, self.Q_bar, "vdd"])
|
||||||
|
|
||||||
def place_storage(self):
|
def place_storage(self):
|
||||||
"""
|
"""
|
||||||
Places the transistors for the crossed
|
Places the transistors for the crossed
|
||||||
coupled inverters in the bitcell
|
coupled inverters in the bitcell
|
||||||
"""
|
"""
|
||||||
|
|
@ -406,7 +406,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \
|
contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \
|
||||||
+ 0.5 * contact.poly.height,
|
+ 0.5 * contact.poly.height,
|
||||||
self.cross_couple_upper_ypos)
|
self.cross_couple_upper_ypos)
|
||||||
|
|
||||||
contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \
|
contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \
|
||||||
- 0.5*contact.poly.height,
|
- 0.5*contact.poly.height,
|
||||||
self.cross_couple_lower_ypos)
|
self.cross_couple_lower_ypos)
|
||||||
|
|
@ -421,8 +421,8 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
offset=self.gnd_position,
|
offset=self.gnd_position,
|
||||||
width=self.width)
|
width=self.width)
|
||||||
self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H"))
|
self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H"))
|
||||||
|
|
||||||
|
|
||||||
vdd_ypos = self.inverter_nmos_ypos \
|
vdd_ypos = self.inverter_nmos_ypos \
|
||||||
+ self.inverter_nmos.active_height \
|
+ self.inverter_nmos.active_height \
|
||||||
+ self.inverter_gap \
|
+ self.inverter_gap \
|
||||||
|
|
@ -433,7 +433,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
offset=self.vdd_position,
|
offset=self.vdd_position,
|
||||||
width=self.width)
|
width=self.width)
|
||||||
self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H"))
|
self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H"))
|
||||||
|
|
||||||
def create_readwrite_ports(self):
|
def create_readwrite_ports(self):
|
||||||
"""
|
"""
|
||||||
Creates read/write ports to the bit cell. A differential
|
Creates read/write ports to the bit cell. A differential
|
||||||
|
|
@ -461,7 +461,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
if self.dummy_bitcell:
|
if self.dummy_bitcell:
|
||||||
bl_name += "_noconn"
|
bl_name += "_noconn"
|
||||||
br_name += "_noconn"
|
br_name += "_noconn"
|
||||||
|
|
||||||
# add read/write transistors
|
# add read/write transistors
|
||||||
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
|
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
|
||||||
mod=self.readwrite_nmos)
|
mod=self.readwrite_nmos)
|
||||||
|
|
@ -662,7 +662,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
if self.dummy_bitcell:
|
if self.dummy_bitcell:
|
||||||
bl_name += "_noconn"
|
bl_name += "_noconn"
|
||||||
br_name += "_noconn"
|
br_name += "_noconn"
|
||||||
|
|
||||||
# add read-access transistors
|
# add read-access transistors
|
||||||
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k),
|
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k),
|
||||||
mod=self.read_nmos)
|
mod=self.read_nmos)
|
||||||
|
|
@ -897,7 +897,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
[self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right])
|
[self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right])
|
||||||
|
|
||||||
def route_readwrite_access(self):
|
def route_readwrite_access(self):
|
||||||
"""
|
"""
|
||||||
Routes read/write transistors to the storage
|
Routes read/write transistors to the storage
|
||||||
component of the bitcell
|
component of the bitcell
|
||||||
"""
|
"""
|
||||||
|
|
@ -917,7 +917,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
[self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
|
[self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
|
||||||
|
|
||||||
def route_write_access(self):
|
def route_write_access(self):
|
||||||
"""
|
"""
|
||||||
Routes read/write transistors to the storage
|
Routes read/write transistors to the storage
|
||||||
component of the bitcell
|
component of the bitcell
|
||||||
"""
|
"""
|
||||||
|
|
@ -937,7 +937,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
[self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
|
[self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
|
||||||
|
|
||||||
def route_read_access(self):
|
def route_read_access(self):
|
||||||
"""
|
"""
|
||||||
Routes read access transistors to the storage
|
Routes read access transistors to the storage
|
||||||
component of the bitcell
|
component of the bitcell
|
||||||
"""
|
"""
|
||||||
|
|
@ -1016,7 +1016,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
offset=offset,
|
offset=offset,
|
||||||
width=well_width,
|
width=well_width,
|
||||||
height=well_height)
|
height=well_height)
|
||||||
|
|
||||||
# extend nwell to encompass inverter_pmos
|
# extend nwell to encompass inverter_pmos
|
||||||
# calculate offset of the left pmos well
|
# calculate offset of the left pmos well
|
||||||
if "nwell" in layer:
|
if "nwell" in layer:
|
||||||
|
|
@ -1024,7 +1024,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
- self.nwell_enclose_active
|
- self.nwell_enclose_active
|
||||||
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \
|
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \
|
||||||
+ self.inverter_gap - self.nwell_enclose_active
|
+ self.inverter_gap - self.nwell_enclose_active
|
||||||
|
|
||||||
# calculate width of the two combined nwells
|
# calculate width of the two combined nwells
|
||||||
# calculate height to encompass nimplant connected to vdd
|
# calculate height to encompass nimplant connected to vdd
|
||||||
well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
|
well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
|
||||||
|
|
@ -1099,18 +1099,18 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
Q_bar_pos = self.inverter_pmos_right.get_pin("S").center()
|
Q_bar_pos = self.inverter_pmos_right.get_pin("S").center()
|
||||||
vdd_pos = self.inverter_pmos_right.get_pin("D").center()
|
vdd_pos = self.inverter_pmos_right.get_pin("D").center()
|
||||||
self.add_path("m1", [Q_bar_pos, vdd_pos])
|
self.add_path("m1", [Q_bar_pos, vdd_pos])
|
||||||
|
|
||||||
def get_storage_net_names(self):
|
def get_storage_net_names(self):
|
||||||
"""
|
"""
|
||||||
Returns names of storage nodes in bitcell in
|
Returns names of storage nodes in bitcell in
|
||||||
[non-inverting, inverting] format.
|
[non-inverting, inverting] format.
|
||||||
"""
|
"""
|
||||||
return self.storage_nets
|
return self.storage_nets
|
||||||
|
|
||||||
def get_bl_name(self, port=0):
|
def get_bl_name(self, port=0):
|
||||||
"""Get bl name by port"""
|
"""Get bl name by port"""
|
||||||
return "bl{}".format(port)
|
return "bl{}".format(port)
|
||||||
|
|
||||||
def get_br_name(self, port=0):
|
def get_br_name(self, port=0):
|
||||||
"""Get bl name by port"""
|
"""Get bl name by port"""
|
||||||
return "br{}".format(port)
|
return "br{}".format(port)
|
||||||
|
|
@ -1119,7 +1119,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
"""Get wl name by port"""
|
"""Get wl name by port"""
|
||||||
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
|
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
|
||||||
return "wl{}".format(port)
|
return "wl{}".format(port)
|
||||||
|
|
||||||
def get_stage_effort(self, load):
|
def get_stage_effort(self, load):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
# This accounts for bitline being drained thought the access
|
# This accounts for bitline being drained thought the access
|
||||||
|
|
@ -1128,7 +1128,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
# Assumes always a minimum sizes inverter. Could be
|
# Assumes always a minimum sizes inverter. Could be
|
||||||
# specified in the tech.py file.
|
# specified in the tech.py file.
|
||||||
cin = 3
|
cin = 3
|
||||||
|
|
||||||
# Internal loads due to port configs are halved.
|
# Internal loads due to port configs are halved.
|
||||||
# This is to account for the size already being halved
|
# This is to account for the size already being halved
|
||||||
# for stacked TXs, but internal loads do not see this size
|
# for stacked TXs, but internal loads do not see this size
|
||||||
|
|
@ -1144,10 +1144,10 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
load + read_port_load,
|
load + read_port_load,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
False)
|
False)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
""" Return the relative capacitance of the access transistor gates """
|
""" Return the relative capacitance of the access transistor gates """
|
||||||
|
|
||||||
# FIXME: This applies to bitline capacitances as well.
|
# FIXME: This applies to bitline capacitances as well.
|
||||||
# pbitcell uses the different sizing for the port access tx's. Not accounted for in this model.
|
# pbitcell uses the different sizing for the port access tx's. Not accounted for in this model.
|
||||||
access_tx_cin = self.readwrite_nmos.get_cin()
|
access_tx_cin = self.readwrite_nmos.get_cin()
|
||||||
|
|
@ -1155,10 +1155,10 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""Adds edges to graph for pbitcell. Only readwrite and read ports."""
|
"""Adds edges to graph for pbitcell. Only readwrite and read ports."""
|
||||||
|
|
||||||
if self.dummy_bitcell:
|
if self.dummy_bitcell:
|
||||||
return
|
return
|
||||||
|
|
||||||
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
|
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
|
||||||
# Edges added wl->bl, wl->br for every port except write ports
|
# Edges added wl->bl, wl->br for every port except write ports
|
||||||
rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names)
|
rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names)
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,14 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
|
||||||
import debug
|
import debug
|
||||||
import utils
|
import bitcell_base
|
||||||
from tech import GDS,layer,drc,parameter,cell_properties
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
|
from tech import parameter, drc
|
||||||
|
import logical_effort
|
||||||
|
|
||||||
from globals import OPTS
|
|
||||||
|
|
||||||
class replica_bitcell(design.design):
|
class replica_bitcell(bitcell_base.bitcell_base):
|
||||||
"""
|
"""
|
||||||
A single bit cell (6T, 8T, etc.)
|
A single bit cell (6T, 8T, etc.)
|
||||||
This module implements the single memory cell used in the design. It
|
This module implements the single memory cell used in the design. It
|
||||||
|
|
@ -26,46 +25,33 @@ class replica_bitcell(design.design):
|
||||||
props.bitcell.cell_6t.pin.vdd,
|
props.bitcell.cell_6t.pin.vdd,
|
||||||
props.bitcell.cell_6t.pin.gnd]
|
props.bitcell.cell_6t.pin.gnd]
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
if not OPTS.netlist_only:
|
|
||||||
(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"])
|
|
||||||
else:
|
|
||||||
(width,height) = (0,0)
|
|
||||||
pin_map = []
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
design.design.__init__(self, "replica_cell_6t")
|
|
||||||
debug.info(2, "Create replica bitcell object")
|
debug.info(2, "Create replica bitcell object")
|
||||||
|
|
||||||
self.width = replica_bitcell.width
|
|
||||||
self.height = replica_bitcell.height
|
|
||||||
self.pin_map = replica_bitcell.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
|
|
||||||
def get_stage_effort(self, load):
|
def get_stage_effort(self, load):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
size = 0.5 # This accounts for bitline being drained thought the access TX and internal node
|
||||||
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||||
read_port_load = 0.5 #min size NMOS gate load
|
read_port_load = 0.5 # min size NMOS gate load
|
||||||
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
|
return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""Return the relative capacitance of the access transistor gates"""
|
||||||
|
|
||||||
# FIXME: This applies to bitline capacitances as well.
|
# FIXME: This applies to bitline capacitances as well.
|
||||||
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
|
||||||
return 2*access_tx_cin
|
return 2 * access_tx_cin
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Bitcell power in nW. Only characterizes leakage."""
|
"""Bitcell power in nW. Only characterizes leakage."""
|
||||||
from tech import spice
|
from tech import spice
|
||||||
leakage = spice["bitcell_leakage"]
|
leakage = spice["bitcell_leakage"]
|
||||||
dynamic = 0 #temporary
|
dynamic = 0 # FIXME
|
||||||
total_power = self.return_power(dynamic, leakage)
|
total_power = self.return_power(dynamic, leakage)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
|
||||||
import debug
|
import debug
|
||||||
import utils
|
import bitcell_base
|
||||||
from tech import GDS,layer,drc,parameter
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
|
from tech import parameter, drc
|
||||||
|
import logical_effort
|
||||||
|
|
||||||
class replica_bitcell_1rw_1r(design.design):
|
|
||||||
|
class replica_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
"""
|
"""
|
||||||
A single bit cell which is forced to store a 0.
|
A single bit cell which is forced to store a 0.
|
||||||
This module implements the single memory cell used in the design. It
|
This module implements the single memory cell used in the design. It
|
||||||
|
|
@ -26,42 +27,33 @@ class replica_bitcell_1rw_1r(design.design):
|
||||||
props.bitcell.cell_1rw1r.pin.wl1,
|
props.bitcell.cell_1rw1r.pin.wl1,
|
||||||
props.bitcell.cell_1rw1r.pin.vdd,
|
props.bitcell.cell_1rw1r.pin.vdd,
|
||||||
props.bitcell.cell_1rw1r.pin.gnd]
|
props.bitcell.cell_1rw1r.pin.gnd]
|
||||||
|
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
def __init__(self, name):
|
||||||
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
|
super().__init__(name)
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
|
||||||
# Ignore the name argument
|
|
||||||
design.design.__init__(self, "replica_cell_1rw_1r")
|
|
||||||
debug.info(2, "Create replica bitcell 1rw+1r object")
|
debug.info(2, "Create replica bitcell 1rw+1r object")
|
||||||
|
|
||||||
self.width = replica_bitcell_1rw_1r.width
|
|
||||||
self.height = replica_bitcell_1rw_1r.height
|
|
||||||
self.pin_map = replica_bitcell_1rw_1r.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
|
|
||||||
def get_stage_effort(self, load):
|
def get_stage_effort(self, load):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
size = 0.5 # This accounts for bitline being drained thought the access TX and internal node
|
||||||
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||||
read_port_load = 0.5 #min size NMOS gate load
|
read_port_load = 0.5 # min size NMOS gate load
|
||||||
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
|
return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""Return the relative capacitance of the access transistor gates"""
|
||||||
|
|
||||||
# FIXME: This applies to bitline capacitances as well.
|
# FIXME: This applies to bitline capacitances as well.
|
||||||
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
||||||
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
|
||||||
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. Multiport bitcell timing graph is too complex
|
"""Adds edges to graph. Multiport bitcell timing graph is too complex
|
||||||
to use the add_graph_edges function."""
|
to use the add_graph_edges function."""
|
||||||
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
|
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
|
||||||
pins = props.bitcell.cell_1rw1r.pin
|
pins = props.bitcell.cell_1rw1r.pin
|
||||||
#Edges hardcoded here. Essentially wl->bl/br for both ports.
|
# Edges hardcoded here. Essentially wl->bl/br for both ports.
|
||||||
# Port 0 edges
|
# Port 0 edges
|
||||||
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.bl0], self)
|
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.bl0], self)
|
||||||
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self)
|
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self)
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
|
||||||
import debug
|
import debug
|
||||||
import utils
|
import bitcell_base
|
||||||
from tech import GDS,layer,drc,parameter
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
|
from tech import parameter, drc
|
||||||
|
import logical_effort
|
||||||
|
|
||||||
class replica_bitcell_1w_1r(design.design):
|
|
||||||
|
class replica_bitcell_1w_1r(bitcell_base.bitcell_base):
|
||||||
"""
|
"""
|
||||||
A single bit cell which is forced to store a 0.
|
A single bit cell which is forced to store a 0.
|
||||||
This module implements the single memory cell used in the design. It
|
This module implements the single memory cell used in the design. It
|
||||||
|
|
@ -26,43 +27,34 @@ class replica_bitcell_1w_1r(design.design):
|
||||||
props.bitcell.cell_1w1r.pin.wl1,
|
props.bitcell.cell_1w1r.pin.wl1,
|
||||||
props.bitcell.cell_1w1r.pin.vdd,
|
props.bitcell.cell_1w1r.pin.vdd,
|
||||||
props.bitcell.cell_1w1r.pin.gnd]
|
props.bitcell.cell_1w1r.pin.gnd]
|
||||||
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
def __init__(self, name):
|
||||||
(width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"])
|
super().__init__(name)
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
|
||||||
# Ignore the name argument
|
|
||||||
design.design.__init__(self, "replica_cell_1w_1r")
|
|
||||||
debug.info(2, "Create replica bitcell 1w+1r object")
|
debug.info(2, "Create replica bitcell 1w+1r object")
|
||||||
|
|
||||||
self.width = replica_bitcell_1w_1r.width
|
|
||||||
self.height = replica_bitcell_1w_1r.height
|
|
||||||
self.pin_map = replica_bitcell_1w_1r.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
|
|
||||||
def get_stage_effort(self, load):
|
def get_stage_effort(self, load):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
size = 0.5 # This accounts for bitline being drained thought the access TX and internal node
|
||||||
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||||
read_port_load = 0.5 #min size NMOS gate load
|
read_port_load = 0.5 # min size NMOS gate load
|
||||||
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
|
return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""Return the relative capacitance of the access transistor gates"""
|
||||||
|
|
||||||
# FIXME: This applies to bitline capacitances as well.
|
# FIXME: This applies to bitline capacitances as well.
|
||||||
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
||||||
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
|
||||||
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. Multiport bitcell timing graph is too complex
|
"""Adds edges to graph. Multiport bitcell timing graph is too complex
|
||||||
to use the add_graph_edges function."""
|
to use the add_graph_edges function."""
|
||||||
debug.info(1,'Adding edges for {}'.format(inst_name))
|
debug.info(1, 'Adding edges for {}'.format(inst_name))
|
||||||
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
|
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
|
||||||
pins = props.bitcell.cell_1w1r.pin
|
pins = props.bitcell.cell_1w1r.pin
|
||||||
#Edges hardcoded here. Essentially wl->bl/br for the read port.
|
# Edges hardcoded here. Essentially wl->bl/br for the read port.
|
||||||
# Port 1 edges
|
# Port 1 edges
|
||||||
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self)
|
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self)
|
||||||
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)
|
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)
|
||||||
|
|
|
||||||
|
|
@ -7,82 +7,85 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
from tech import drc, spice,parameter
|
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class replica_pbitcell(design.design):
|
class replica_pbitcell(design.design):
|
||||||
"""
|
"""
|
||||||
Creates a replica bitcell using pbitcell
|
Creates a replica bitcell using pbitcell
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name, cell_name=None):
|
||||||
|
if not cell_name:
|
||||||
|
cell_name = name
|
||||||
self.num_rw_ports = OPTS.num_rw_ports
|
self.num_rw_ports = OPTS.num_rw_ports
|
||||||
self.num_w_ports = OPTS.num_w_ports
|
self.num_w_ports = OPTS.num_w_ports
|
||||||
self.num_r_ports = OPTS.num_r_ports
|
self.num_r_ports = OPTS.num_r_ports
|
||||||
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
|
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
|
||||||
|
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name, cell_name)
|
||||||
debug.info(1, "create a replica bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
|
debug.info(1, "create a replica bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
|
||||||
self.num_w_ports,
|
self.num_w_ports,
|
||||||
self.num_r_ports))
|
self.num_r_ports))
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_modules()
|
self.create_modules()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.place_pbitcell()
|
self.place_pbitcell()
|
||||||
self.route_rbc_connections()
|
self.route_rbc_connections()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
self.add_pin("bl{}".format(port))
|
self.add_pin("bl{}".format(port))
|
||||||
self.add_pin("br{}".format(port))
|
self.add_pin("br{}".format(port))
|
||||||
|
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
self.add_pin("wl{}".format(port))
|
self.add_pin("wl{}".format(port))
|
||||||
|
|
||||||
self.add_pin("vdd")
|
self.add_pin("vdd")
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.prbc = factory.create(module_type="pbitcell",replica_bitcell=True)
|
self.prbc = factory.create(module_type="pbitcell",
|
||||||
|
replica_bitcell=True)
|
||||||
self.add_mod(self.prbc)
|
self.add_mod(self.prbc)
|
||||||
|
|
||||||
self.height = self.prbc.height
|
self.height = self.prbc.height
|
||||||
self.width = self.prbc.width
|
self.width = self.prbc.width
|
||||||
|
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
self.prbc_inst = self.add_inst(name="pbitcell",
|
self.prbc_inst = self.add_inst(name="pbitcell",
|
||||||
mod=self.prbc)
|
mod=self.prbc)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
temp.append("bl{}".format(port))
|
temp.append("bl{}".format(port))
|
||||||
temp.append("br{}".format(port))
|
temp.append("br{}".format(port))
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
temp.append("wl{}".format(port))
|
temp.append("wl{}".format(port))
|
||||||
temp.append("vdd")
|
temp.append("vdd")
|
||||||
temp.append("gnd")
|
temp.append("gnd")
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
def place_pbitcell(self):
|
def place_pbitcell(self):
|
||||||
self.prbc_inst.place(offset=vector(0,0))
|
self.prbc_inst.place(offset=vector(0, 0))
|
||||||
|
|
||||||
def route_rbc_connections(self):
|
def route_rbc_connections(self):
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
self.copy_layout_pin(self.prbc_inst, "bl{}".format(port))
|
self.copy_layout_pin(self.prbc_inst, "bl{}".format(port))
|
||||||
self.copy_layout_pin(self.prbc_inst, "br{}".format(port))
|
self.copy_layout_pin(self.prbc_inst, "br{}".format(port))
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
|
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
|
||||||
self.copy_layout_pin(self.prbc_inst, "vdd")
|
self.copy_layout_pin(self.prbc_inst, "vdd")
|
||||||
self.copy_layout_pin(self.prbc_inst, "gnd")
|
self.copy_layout_pin(self.prbc_inst, "gnd")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,39 +6,22 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
||||||
class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
"""
|
"""
|
||||||
A single bit cell which is forced to store a 0.
|
Row end cap cell.
|
||||||
This module implements the single memory cell used in the design. It
|
"""
|
||||||
is a hand-made cell, so the layout and netlist should be available in
|
|
||||||
the technology library. """
|
|
||||||
|
|
||||||
pin_names = [props.bitcell.cell_1rw1r.pin.wl0,
|
pin_names = [props.bitcell.cell_1rw1r.pin.wl0,
|
||||||
props.bitcell.cell_1rw1r.pin.wl1,
|
props.bitcell.cell_1rw1r.pin.wl1,
|
||||||
props.bitcell.cell_1rw1r.pin.gnd]
|
props.bitcell.cell_1rw1r.pin.gnd]
|
||||||
|
|
||||||
type_list = ["INPUT", "INPUT", "GROUND"]
|
type_list = ["INPUT", "INPUT", "GROUND"]
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names,
|
|
||||||
"row_cap_cell_1rw_1r",
|
|
||||||
GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
def __init__(self, name="row_cap_cell_1rw_1r"):
|
||||||
# Ignore the name argument
|
bitcell_base.bitcell_base.__init__(self, name)
|
||||||
bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r")
|
|
||||||
debug.info(2, "Create row_cap bitcell 1rw+1r object")
|
debug.info(2, "Create row_cap bitcell 1rw+1r object")
|
||||||
|
|
||||||
self.width = row_cap_bitcell_1rw_1r.width
|
|
||||||
self.height = row_cap_bitcell_1rw_1r.height
|
|
||||||
self.pin_map = row_cap_bitcell_1rw_1r.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
self.no_instances = True
|
self.no_instances = True
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@ if not OPTS.analytical_delay:
|
||||||
else:
|
else:
|
||||||
(OPTS.spice_name,OPTS.spice_exe) = get_tool("spice",["hspice", "ngspice", "ngspice.exe", "xa"])
|
(OPTS.spice_name,OPTS.spice_exe) = get_tool("spice",["hspice", "ngspice", "ngspice.exe", "xa"])
|
||||||
|
|
||||||
# set the input dir for spice files if using ngspice
|
# set the input dir for spice files if using ngspice
|
||||||
if OPTS.spice_name == "ngspice":
|
if OPTS.spice_name == "ngspice":
|
||||||
os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp)
|
os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp)
|
||||||
|
|
||||||
if OPTS.spice_exe == "":
|
if OPTS.spice_exe == "":
|
||||||
debug.error("No recognizable spice version found. Unable to perform characterization.",1)
|
debug.error("No recognizable spice version found. Unable to perform characterization.",1)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,4 @@ from enum import Enum
|
||||||
class bit_polarity(Enum):
|
class bit_polarity(Enum):
|
||||||
NONINVERTING = 0
|
NONINVERTING = 0
|
||||||
INVERTING = 1
|
INVERTING = 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import re
|
||||||
import debug
|
import debug
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
def relative_compare(value1,value2,error_tolerance=0.001):
|
def relative_compare(value1,value2,error_tolerance=0.001):
|
||||||
""" This is used to compare relative values for convergence. """
|
""" This is used to compare relative values for convergence. """
|
||||||
return (abs(value1 - value2) / abs(max(value1,value2)) <= error_tolerance)
|
return (abs(value1 - value2) / abs(max(value1,value2)) <= error_tolerance)
|
||||||
|
|
@ -37,7 +37,7 @@ def parse_spice_list(filename, key):
|
||||||
return convert_to_float(val.group(1))
|
return convert_to_float(val.group(1))
|
||||||
else:
|
else:
|
||||||
return "Failed"
|
return "Failed"
|
||||||
|
|
||||||
def round_time(time,time_precision=3):
|
def round_time(time,time_precision=3):
|
||||||
# times are in ns, so this is how many digits of precision
|
# times are in ns, so this is how many digits of precision
|
||||||
# 3 digits = 1ps
|
# 3 digits = 1ps
|
||||||
|
|
@ -58,10 +58,10 @@ def convert_to_float(number):
|
||||||
"""Converts a string into a (float) number; also converts units(m,u,n,p)"""
|
"""Converts a string into a (float) number; also converts units(m,u,n,p)"""
|
||||||
if number == "Failed":
|
if number == "Failed":
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# start out with a binary value
|
# start out with a binary value
|
||||||
float_value = False
|
float_value = False
|
||||||
try:
|
try:
|
||||||
# checks if string is a float without letter units
|
# checks if string is a float without letter units
|
||||||
float_value = float(number)
|
float_value = float(number)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|
@ -69,7 +69,7 @@ def convert_to_float(number):
|
||||||
unit = re.search(r"(-?\d+\.?\d*)e(\-?\+?\d+)", number)
|
unit = re.search(r"(-?\d+\.?\d*)e(\-?\+?\d+)", number)
|
||||||
if unit != None:
|
if unit != None:
|
||||||
float_value=float(unit.group(1)) * (10 ^ float(unit.group(2)))
|
float_value=float(unit.group(1)) * (10 ^ float(unit.group(2)))
|
||||||
|
|
||||||
# see if it is in spice notation
|
# see if it is in spice notation
|
||||||
unit = re.search(r"(-?\d+\.?\d*)(m?u?n?p?f?)", number)
|
unit = re.search(r"(-?\d+\.?\d*)(m?u?n?p?f?)", number)
|
||||||
if unit != None:
|
if unit != None:
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -21,13 +21,24 @@ class functional(simulation):
|
||||||
for successful SRAM operation.
|
for successful SRAM operation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, sram, spfile, corner, cycles=15):
|
def __init__(self, sram, spfile, corner=None, cycles=15, period=None, output_path=None):
|
||||||
super().__init__(sram, spfile, corner)
|
super().__init__(sram, spfile, corner)
|
||||||
|
|
||||||
# Seed the characterizer with a constant seed for unit tests
|
# Seed the characterizer with a constant seed for unit tests
|
||||||
if OPTS.is_unit_test:
|
if OPTS.is_unit_test:
|
||||||
random.seed(12345)
|
random.seed(12345)
|
||||||
|
|
||||||
|
if not corner:
|
||||||
|
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||||
|
|
||||||
|
if period:
|
||||||
|
self.period = period
|
||||||
|
|
||||||
|
if not output_path:
|
||||||
|
self.output_path = OPTS.openram_temp
|
||||||
|
else:
|
||||||
|
self.output_path = output_path
|
||||||
|
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
|
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
|
||||||
else:
|
else:
|
||||||
|
|
@ -50,7 +61,7 @@ class functional(simulation):
|
||||||
self.set_internal_spice_names()
|
self.set_internal_spice_names()
|
||||||
self.q_name, self.qbar_name = self.get_bit_name()
|
self.q_name, self.qbar_name = self.get_bit_name()
|
||||||
debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name))
|
debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name))
|
||||||
|
|
||||||
# Number of checks can be changed
|
# Number of checks can be changed
|
||||||
self.num_cycles = cycles
|
self.num_cycles = cycles
|
||||||
# This is to have ordered keys for random selection
|
# This is to have ordered keys for random selection
|
||||||
|
|
@ -58,21 +69,20 @@ class functional(simulation):
|
||||||
self.read_check = []
|
self.read_check = []
|
||||||
self.read_results = []
|
self.read_results = []
|
||||||
|
|
||||||
def run(self, feasible_period=None):
|
|
||||||
if feasible_period: # period defaults to tech.py feasible period otherwise.
|
|
||||||
self.period = feasible_period
|
|
||||||
# Generate a random sequence of reads and writes
|
# Generate a random sequence of reads and writes
|
||||||
self.create_random_memory_sequence()
|
self.create_random_memory_sequence()
|
||||||
|
|
||||||
# Run SPICE simulation
|
# Write SPICE simulation
|
||||||
self.write_functional_stimulus()
|
self.write_functional_stimulus()
|
||||||
self.stim.run_sim()
|
|
||||||
|
def run(self):
|
||||||
|
self.stim.run_sim(self.stim_sp)
|
||||||
|
|
||||||
# read dout values from SPICE simulation. If the values do not fall within the noise margins, return the error.
|
# read dout values from SPICE simulation. If the values do not fall within the noise margins, return the error.
|
||||||
(success, error) = self.read_stim_results()
|
(success, error) = self.read_stim_results()
|
||||||
if not success:
|
if not success:
|
||||||
return (0, error)
|
return (0, error)
|
||||||
|
|
||||||
# Check read values with written values. If the values do not match, return an error.
|
# Check read values with written values. If the values do not match, return an error.
|
||||||
return self.check_stim_results()
|
return self.check_stim_results()
|
||||||
|
|
||||||
|
|
@ -94,7 +104,7 @@ class functional(simulation):
|
||||||
len(val),
|
len(val),
|
||||||
port,
|
port,
|
||||||
name))
|
name))
|
||||||
|
|
||||||
def create_random_memory_sequence(self):
|
def create_random_memory_sequence(self):
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
rw_ops = ["noop", "write", "partial_write", "read"]
|
rw_ops = ["noop", "write", "partial_write", "read"]
|
||||||
|
|
@ -123,7 +133,7 @@ class functional(simulation):
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
self.check_lengths()
|
self.check_lengths()
|
||||||
|
|
||||||
# 2. Read at least once. For multiport, it is important that one
|
# 2. Read at least once. For multiport, it is important that one
|
||||||
# read cycle uses all RW and R port to read from the same
|
# read cycle uses all RW and R port to read from the same
|
||||||
# address simultaniously. This will test the viablilty of the
|
# address simultaniously. This will test the viablilty of the
|
||||||
|
|
@ -138,7 +148,7 @@ class functional(simulation):
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
self.check_lengths()
|
self.check_lengths()
|
||||||
|
|
||||||
# 3. Perform a random sequence of writes and reads on random
|
# 3. Perform a random sequence of writes and reads on random
|
||||||
# ports, using random addresses and random words and random
|
# ports, using random addresses and random words and random
|
||||||
# write masks (if applicable)
|
# write masks (if applicable)
|
||||||
|
|
@ -151,7 +161,7 @@ class functional(simulation):
|
||||||
op = random.choice(w_ops)
|
op = random.choice(w_ops)
|
||||||
else:
|
else:
|
||||||
op = random.choice(r_ops)
|
op = random.choice(r_ops)
|
||||||
|
|
||||||
if op == "noop":
|
if op == "noop":
|
||||||
self.add_noop_one_port(port)
|
self.add_noop_one_port(port)
|
||||||
elif op == "write":
|
elif op == "write":
|
||||||
|
|
@ -191,10 +201,10 @@ class functional(simulation):
|
||||||
comment = self.gen_cycle_comment("read", word, addr, "0" * self.num_wmasks, port, self.t_current)
|
comment = self.gen_cycle_comment("read", word, addr, "0" * self.num_wmasks, port, self.t_current)
|
||||||
self.add_read_one_port(comment, addr, port)
|
self.add_read_one_port(comment, addr, port)
|
||||||
self.add_read_check(word, port)
|
self.add_read_check(word, port)
|
||||||
|
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
|
|
||||||
# Last cycle idle needed to correctly measure the value on the second to last clock edge
|
# Last cycle idle needed to correctly measure the value on the second to last clock edge
|
||||||
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current)
|
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current)
|
||||||
self.add_noop_all_ports(comment)
|
self.add_noop_all_ports(comment)
|
||||||
|
|
@ -203,7 +213,7 @@ class functional(simulation):
|
||||||
""" Create the masked data word """
|
""" Create the masked data word """
|
||||||
# Start with the new word
|
# Start with the new word
|
||||||
new_word = word
|
new_word = word
|
||||||
|
|
||||||
# When the write mask's bits are 0, the old data values should appear in the new word
|
# When the write mask's bits are 0, the old data values should appear in the new word
|
||||||
# as to not overwrite the old values
|
# as to not overwrite the old values
|
||||||
for bit in range(len(wmask)):
|
for bit in range(len(wmask)):
|
||||||
|
|
@ -211,9 +221,9 @@ class functional(simulation):
|
||||||
lower = bit * self.write_size
|
lower = bit * self.write_size
|
||||||
upper = lower + self.write_size - 1
|
upper = lower + self.write_size - 1
|
||||||
new_word = new_word[:lower] + old_word[lower:upper + 1] + new_word[upper + 1:]
|
new_word = new_word[:lower] + old_word[lower:upper + 1] + new_word[upper + 1:]
|
||||||
|
|
||||||
return new_word
|
return new_word
|
||||||
|
|
||||||
def add_read_check(self, word, port):
|
def add_read_check(self, word, port):
|
||||||
""" Add to the check array to ensure a read works. """
|
""" Add to the check array to ensure a read works. """
|
||||||
try:
|
try:
|
||||||
|
|
@ -222,7 +232,7 @@ class functional(simulation):
|
||||||
self.check = 0
|
self.check = 0
|
||||||
self.read_check.append([word, "{0}{1}".format(self.dout_name, port), self.t_current + self.period, self.check])
|
self.read_check.append([word, "{0}{1}".format(self.dout_name, port), self.t_current + self.period, self.check])
|
||||||
self.check += 1
|
self.check += 1
|
||||||
|
|
||||||
def read_stim_results(self):
|
def read_stim_results(self):
|
||||||
# Extract dout values from spice timing.lis
|
# Extract dout values from spice timing.lis
|
||||||
for (word, dout_port, eo_period, check) in self.read_check:
|
for (word, dout_port, eo_period, check) in self.read_check:
|
||||||
|
|
@ -247,12 +257,12 @@ class functional(simulation):
|
||||||
bit,
|
bit,
|
||||||
value,
|
value,
|
||||||
eo_period)
|
eo_period)
|
||||||
|
|
||||||
return (0, error)
|
return (0, error)
|
||||||
|
|
||||||
self.read_results.append([sp_read_value, dout_port, eo_period, check])
|
self.read_results.append([sp_read_value, dout_port, eo_period, check])
|
||||||
return (1, "SUCCESS")
|
return (1, "SUCCESS")
|
||||||
|
|
||||||
def check_stim_results(self):
|
def check_stim_results(self):
|
||||||
for i in range(len(self.read_check)):
|
for i in range(len(self.read_check)):
|
||||||
if self.read_check[i][0] != self.read_results[i][0]:
|
if self.read_check[i][0] != self.read_results[i][0]:
|
||||||
|
|
@ -307,14 +317,14 @@ class functional(simulation):
|
||||||
random_value = random.randint(0, ((2 ** (self.addr_size - 1) - 1)) + (self.num_spare_rows * self.words_per_row))
|
random_value = random.randint(0, ((2 ** (self.addr_size - 1) - 1)) + (self.num_spare_rows * self.words_per_row))
|
||||||
addr_bits = self.convert_to_bin(random_value, True)
|
addr_bits = self.convert_to_bin(random_value, True)
|
||||||
return addr_bits
|
return addr_bits
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
""" Gets an available address and corresponding word. """
|
""" Gets an available address and corresponding word. """
|
||||||
# Used for write masks since they should be writing to previously written addresses
|
# Used for write masks since they should be writing to previously written addresses
|
||||||
addr = random.choice(list(self.stored_words.keys()))
|
addr = random.choice(list(self.stored_words.keys()))
|
||||||
word = self.stored_words[addr]
|
word = self.stored_words[addr]
|
||||||
return (addr, word)
|
return (addr, word)
|
||||||
|
|
||||||
def convert_to_bin(self, value, is_addr):
|
def convert_to_bin(self, value, is_addr):
|
||||||
""" Converts addr & word to usable binary values. """
|
""" Converts addr & word to usable binary values. """
|
||||||
new_value = str.replace(bin(value), "0b", "")
|
new_value = str.replace(bin(value), "0b", "")
|
||||||
|
|
@ -324,13 +334,14 @@ class functional(simulation):
|
||||||
expected_value = self.word_size + self.num_spare_cols
|
expected_value = self.word_size + self.num_spare_cols
|
||||||
for i in range(expected_value - len(new_value)):
|
for i in range(expected_value - len(new_value)):
|
||||||
new_value = "0" + new_value
|
new_value = "0" + new_value
|
||||||
|
|
||||||
# print("Binary Conversion: {} to {}".format(value, new_value))
|
# print("Binary Conversion: {} to {}".format(value, new_value))
|
||||||
return new_value
|
return new_value
|
||||||
|
|
||||||
def write_functional_stimulus(self):
|
def write_functional_stimulus(self):
|
||||||
""" Writes SPICE stimulus. """
|
""" Writes SPICE stimulus. """
|
||||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
self.stim_sp = "functional_stim.sp"
|
||||||
|
temp_stim = "{0}/{1}".format(self.output_path, self.stim_sp)
|
||||||
self.sf = open(temp_stim, "w")
|
self.sf = open(temp_stim, "w")
|
||||||
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period))
|
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period))
|
||||||
self.stim = stimuli(self.sf, self.corner)
|
self.stim = stimuli(self.sf, self.corner)
|
||||||
|
|
@ -341,7 +352,7 @@ class functional(simulation):
|
||||||
# Write Vdd/Gnd statements
|
# Write Vdd/Gnd statements
|
||||||
self.sf.write("\n* Global Power Supplies\n")
|
self.sf.write("\n* Global Power Supplies\n")
|
||||||
self.stim.write_supply()
|
self.stim.write_supply()
|
||||||
|
|
||||||
# Instantiate the SRAM
|
# Instantiate the SRAM
|
||||||
self.sf.write("\n* Instantiation of the SRAM\n")
|
self.sf.write("\n* Instantiation of the SRAM\n")
|
||||||
self.stim.inst_model(pins=self.pins,
|
self.stim.inst_model(pins=self.pins,
|
||||||
|
|
@ -361,19 +372,19 @@ class functional(simulation):
|
||||||
self.sf.write("* s_en: {}\n".format(self.sen_name))
|
self.sf.write("* s_en: {}\n".format(self.sen_name))
|
||||||
self.sf.write("* q: {}\n".format(self.q_name))
|
self.sf.write("* q: {}\n".format(self.q_name))
|
||||||
self.sf.write("* qbar: {}\n".format(self.qbar_name))
|
self.sf.write("* qbar: {}\n".format(self.qbar_name))
|
||||||
|
|
||||||
# Write debug comments to stim file
|
# Write debug comments to stim file
|
||||||
self.sf.write("\n\n* Sequence of operations\n")
|
self.sf.write("\n\n* Sequence of operations\n")
|
||||||
for comment in self.fn_cycle_comments:
|
for comment in self.fn_cycle_comments:
|
||||||
self.sf.write("*{}\n".format(comment))
|
self.sf.write("*{}\n".format(comment))
|
||||||
|
|
||||||
# Generate data input bits
|
# Generate data input bits
|
||||||
self.sf.write("\n* Generation of data and address signals\n")
|
self.sf.write("\n* Generation of data and address signals\n")
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
for bit in range(self.word_size + self.num_spare_cols):
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
sig_name="{0}{1}_{2} ".format(self.din_name, port, bit)
|
sig_name="{0}{1}_{2} ".format(self.din_name, port, bit)
|
||||||
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05)
|
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05)
|
||||||
|
|
||||||
# Generate address bits
|
# Generate address bits
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
for bit in range(self.addr_size):
|
for bit in range(self.addr_size):
|
||||||
|
|
@ -384,7 +395,7 @@ class functional(simulation):
|
||||||
self.sf.write("\n * Generation of control signals\n")
|
self.sf.write("\n * Generation of control signals\n")
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.stim.gen_pwl("CSB{}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
|
self.stim.gen_pwl("CSB{}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
|
||||||
|
|
||||||
for port in self.readwrite_ports:
|
for port in self.readwrite_ports:
|
||||||
self.stim.gen_pwl("WEB{}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
|
self.stim.gen_pwl("WEB{}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
|
||||||
|
|
||||||
|
|
@ -417,7 +428,7 @@ class functional(simulation):
|
||||||
period=self.period,
|
period=self.period,
|
||||||
t_rise=self.slew,
|
t_rise=self.slew,
|
||||||
t_fall=self.slew)
|
t_fall=self.slew)
|
||||||
|
|
||||||
# Generate dout value measurements
|
# Generate dout value measurements
|
||||||
self.sf.write("\n * Generation of dout measurements\n")
|
self.sf.write("\n * Generation of dout measurements\n")
|
||||||
for (word, dout_port, eo_period, check) in self.read_check:
|
for (word, dout_port, eo_period, check) in self.read_check:
|
||||||
|
|
@ -428,10 +439,10 @@ class functional(simulation):
|
||||||
dout="{0}_{1}".format(dout_port, bit),
|
dout="{0}_{1}".format(dout_port, bit),
|
||||||
t_intital=t_intital,
|
t_intital=t_intital,
|
||||||
t_final=t_final)
|
t_final=t_final)
|
||||||
|
|
||||||
self.stim.write_control(self.cycle_times[-1] + self.period)
|
self.stim.write_control(self.cycle_times[-1] + self.period)
|
||||||
self.sf.close()
|
self.sf.close()
|
||||||
|
|
||||||
#FIXME: Similar function to delay.py, refactor this
|
#FIXME: Similar function to delay.py, refactor this
|
||||||
def get_bit_name(self):
|
def get_bit_name(self):
|
||||||
""" Get a bit cell name """
|
""" Get a bit cell name """
|
||||||
|
|
@ -444,4 +455,4 @@ class functional(simulation):
|
||||||
|
|
||||||
return (q_name, qbar_name)
|
return (q_name, qbar_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,21 +18,21 @@ from globals import OPTS
|
||||||
|
|
||||||
class lib:
|
class lib:
|
||||||
""" lib file generation."""
|
""" lib file generation."""
|
||||||
|
|
||||||
def __init__(self, out_dir, sram, sp_file, use_model=OPTS.analytical_delay):
|
def __init__(self, out_dir, sram, sp_file, use_model=OPTS.analytical_delay):
|
||||||
|
|
||||||
self.out_dir = out_dir
|
self.out_dir = out_dir
|
||||||
self.sram = sram
|
self.sram = sram
|
||||||
self.sp_file = sp_file
|
self.sp_file = sp_file
|
||||||
self.use_model = use_model
|
self.use_model = use_model
|
||||||
self.set_port_indices()
|
self.set_port_indices()
|
||||||
|
|
||||||
self.prepare_tables()
|
self.prepare_tables()
|
||||||
|
|
||||||
self.create_corners()
|
self.create_corners()
|
||||||
|
|
||||||
self.characterize_corners()
|
self.characterize_corners()
|
||||||
|
|
||||||
def set_port_indices(self):
|
def set_port_indices(self):
|
||||||
"""Copies port information set in the SRAM instance"""
|
"""Copies port information set in the SRAM instance"""
|
||||||
self.total_port_num = len(self.sram.all_ports)
|
self.total_port_num = len(self.sram.all_ports)
|
||||||
|
|
@ -40,7 +40,7 @@ class lib:
|
||||||
self.readwrite_ports = self.sram.readwrite_ports
|
self.readwrite_ports = self.sram.readwrite_ports
|
||||||
self.read_ports = self.sram.read_ports
|
self.read_ports = self.sram.read_ports
|
||||||
self.write_ports = self.sram.write_ports
|
self.write_ports = self.sram.write_ports
|
||||||
|
|
||||||
def prepare_tables(self):
|
def prepare_tables(self):
|
||||||
""" Determine the load/slews if they aren't specified in the config file. """
|
""" Determine the load/slews if they aren't specified in the config file. """
|
||||||
# These are the parameters to determine the table sizes
|
# These are the parameters to determine the table sizes
|
||||||
|
|
@ -48,12 +48,12 @@ class lib:
|
||||||
self.load = tech.spice["dff_in_cap"]
|
self.load = tech.spice["dff_in_cap"]
|
||||||
self.loads = self.load_scales * self.load
|
self.loads = self.load_scales * self.load
|
||||||
debug.info(1, "Loads: {0}".format(self.loads))
|
debug.info(1, "Loads: {0}".format(self.loads))
|
||||||
|
|
||||||
self.slew_scales = np.array(OPTS.slew_scales)
|
self.slew_scales = np.array(OPTS.slew_scales)
|
||||||
self.slew = tech.spice["rise_time"]
|
self.slew = tech.spice["rise_time"]
|
||||||
self.slews = self.slew_scales * self.slew
|
self.slews = self.slew_scales * self.slew
|
||||||
debug.info(1, "Slews: {0}".format(self.slews))
|
debug.info(1, "Slews: {0}".format(self.slews))
|
||||||
|
|
||||||
def create_corners(self):
|
def create_corners(self):
|
||||||
""" Create corners for characterization. """
|
""" Create corners for characterization. """
|
||||||
# Get the corners from the options file
|
# Get the corners from the options file
|
||||||
|
|
@ -71,7 +71,7 @@ class lib:
|
||||||
min_process = "FF"
|
min_process = "FF"
|
||||||
nom_process = "TT"
|
nom_process = "TT"
|
||||||
max_process = "SS"
|
max_process = "SS"
|
||||||
|
|
||||||
self.corners = []
|
self.corners = []
|
||||||
self.lib_files = []
|
self.lib_files = []
|
||||||
|
|
||||||
|
|
@ -103,15 +103,15 @@ class lib:
|
||||||
temp)
|
temp)
|
||||||
self.corner_name = self.corner_name.replace(".","p") # Remove decimals
|
self.corner_name = self.corner_name.replace(".","p") # Remove decimals
|
||||||
lib_name = self.out_dir+"{}.lib".format(self.corner_name)
|
lib_name = self.out_dir+"{}.lib".format(self.corner_name)
|
||||||
|
|
||||||
# A corner is a tuple of PVT
|
# A corner is a tuple of PVT
|
||||||
self.corners.append((proc, volt, temp))
|
self.corners.append((proc, volt, temp))
|
||||||
self.lib_files.append(lib_name)
|
self.lib_files.append(lib_name)
|
||||||
|
|
||||||
|
|
||||||
def characterize_corners(self):
|
def characterize_corners(self):
|
||||||
""" Characterize the list of corners. """
|
""" Characterize the list of corners. """
|
||||||
debug.info(1,"Characterizing corners: " + str(self.corners))
|
debug.info(1,"Characterizing corners: " + str(self.corners))
|
||||||
for (self.corner,lib_name) in zip(self.corners,self.lib_files):
|
for (self.corner,lib_name) in zip(self.corners,self.lib_files):
|
||||||
debug.info(1,"Corner: " + str(self.corner))
|
debug.info(1,"Corner: " + str(self.corner))
|
||||||
(self.process, self.voltage, self.temperature) = self.corner
|
(self.process, self.voltage, self.temperature) = self.corner
|
||||||
|
|
@ -121,7 +121,7 @@ class lib:
|
||||||
self.characterize()
|
self.characterize()
|
||||||
self.lib.close()
|
self.lib.close()
|
||||||
self.parse_info(self.corner,lib_name)
|
self.parse_info(self.corner,lib_name)
|
||||||
|
|
||||||
def characterize(self):
|
def characterize(self):
|
||||||
""" Characterize the current corner. """
|
""" Characterize the current corner. """
|
||||||
|
|
||||||
|
|
@ -130,8 +130,8 @@ class lib:
|
||||||
self.compute_setup_hold()
|
self.compute_setup_hold()
|
||||||
|
|
||||||
self.write_header()
|
self.write_header()
|
||||||
|
|
||||||
# Loop over all ports.
|
# Loop over all ports.
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
# set the read and write port as inputs.
|
# set the read and write port as inputs.
|
||||||
self.write_data_bus(port)
|
self.write_data_bus(port)
|
||||||
|
|
@ -143,7 +143,7 @@ class lib:
|
||||||
self.write_clk_timing_power(port)
|
self.write_clk_timing_power(port)
|
||||||
|
|
||||||
self.write_footer()
|
self.write_footer()
|
||||||
|
|
||||||
def write_footer(self):
|
def write_footer(self):
|
||||||
""" Write the footer """
|
""" Write the footer """
|
||||||
self.lib.write(" }\n") #Closing brace for the cell
|
self.lib.write(" }\n") #Closing brace for the cell
|
||||||
|
|
@ -154,13 +154,13 @@ class lib:
|
||||||
self.lib.write("library ({0}_lib)".format(self.corner_name))
|
self.lib.write("library ({0}_lib)".format(self.corner_name))
|
||||||
self.lib.write("{\n")
|
self.lib.write("{\n")
|
||||||
self.lib.write(" delay_model : \"table_lookup\";\n")
|
self.lib.write(" delay_model : \"table_lookup\";\n")
|
||||||
|
|
||||||
self.write_units()
|
self.write_units()
|
||||||
self.write_defaults()
|
self.write_defaults()
|
||||||
self.write_LUT_templates()
|
self.write_LUT_templates()
|
||||||
|
|
||||||
self.lib.write(" default_operating_conditions : OC; \n")
|
self.lib.write(" default_operating_conditions : OC; \n")
|
||||||
|
|
||||||
self.write_bus()
|
self.write_bus()
|
||||||
|
|
||||||
self.lib.write("cell ({0})".format(self.sram.name))
|
self.lib.write("cell ({0})".format(self.sram.name))
|
||||||
|
|
@ -182,7 +182,7 @@ class lib:
|
||||||
control_str = 'csb0' #assume at least 1 port
|
control_str = 'csb0' #assume at least 1 port
|
||||||
for i in range(1, self.total_port_num):
|
for i in range(1, self.total_port_num):
|
||||||
control_str += ' & csb{0}'.format(i)
|
control_str += ' & csb{0}'.format(i)
|
||||||
|
|
||||||
# Leakage is included in dynamic when macro is enabled
|
# Leakage is included in dynamic when macro is enabled
|
||||||
self.lib.write(" leakage_power () {\n")
|
self.lib.write(" leakage_power () {\n")
|
||||||
# 'when' condition unnecessary when cs pin does not turn power to devices
|
# 'when' condition unnecessary when cs pin does not turn power to devices
|
||||||
|
|
@ -190,15 +190,15 @@ class lib:
|
||||||
self.lib.write(" value : {};\n".format(self.char_sram_results["leakage_power"]))
|
self.lib.write(" value : {};\n".format(self.char_sram_results["leakage_power"]))
|
||||||
self.lib.write(" }\n")
|
self.lib.write(" }\n")
|
||||||
self.lib.write(" cell_leakage_power : {};\n".format(self.char_sram_results["leakage_power"]))
|
self.lib.write(" cell_leakage_power : {};\n".format(self.char_sram_results["leakage_power"]))
|
||||||
|
|
||||||
|
|
||||||
def write_units(self):
|
def write_units(self):
|
||||||
""" Adds default units for time, voltage, current,...
|
""" Adds default units for time, voltage, current,...
|
||||||
Valid values are 1mV, 10mV, 100mV, and 1V.
|
Valid values are 1mV, 10mV, 100mV, and 1V.
|
||||||
For time: Valid values are 1ps, 10ps, 100ps, and 1ns.
|
For time: Valid values are 1ps, 10ps, 100ps, and 1ns.
|
||||||
For power: Valid values are 1mW, 100uW (for 100mW), 10uW (for 10mW),
|
For power: Valid values are 1mW, 100uW (for 100mW), 10uW (for 10mW),
|
||||||
1uW (for 1mW), 100nW, 10nW, 1nW, 100pW, 10pW, and 1pW.
|
1uW (for 1mW), 100nW, 10nW, 1nW, 100pW, 10pW, and 1pW.
|
||||||
"""
|
"""
|
||||||
self.lib.write(" time_unit : \"1ns\" ;\n")
|
self.lib.write(" time_unit : \"1ns\" ;\n")
|
||||||
self.lib.write(" voltage_unit : \"1V\" ;\n")
|
self.lib.write(" voltage_unit : \"1V\" ;\n")
|
||||||
self.lib.write(" current_unit : \"1mA\" ;\n")
|
self.lib.write(" current_unit : \"1mA\" ;\n")
|
||||||
|
|
@ -214,7 +214,7 @@ class lib:
|
||||||
|
|
||||||
def write_defaults(self):
|
def write_defaults(self):
|
||||||
""" Adds default values for slew and capacitance."""
|
""" Adds default values for slew and capacitance."""
|
||||||
|
|
||||||
self.lib.write(" input_threshold_pct_fall : 50.0 ;\n")
|
self.lib.write(" input_threshold_pct_fall : 50.0 ;\n")
|
||||||
self.lib.write(" output_threshold_pct_fall : 50.0 ;\n")
|
self.lib.write(" output_threshold_pct_fall : 50.0 ;\n")
|
||||||
self.lib.write(" input_threshold_pct_rise : 50.0 ;\n")
|
self.lib.write(" input_threshold_pct_rise : 50.0 ;\n")
|
||||||
|
|
@ -255,7 +255,7 @@ class lib:
|
||||||
formatted_rows = list(map(self.create_list,split_values))
|
formatted_rows = list(map(self.create_list,split_values))
|
||||||
formatted_array = ",\\\n".join(formatted_rows)
|
formatted_array = ",\\\n".join(formatted_rows)
|
||||||
return formatted_array
|
return formatted_array
|
||||||
|
|
||||||
def write_index(self, number, values):
|
def write_index(self, number, values):
|
||||||
""" Write the index """
|
""" Write the index """
|
||||||
quoted_string = self.create_list(values)
|
quoted_string = self.create_list(values)
|
||||||
|
|
@ -267,10 +267,10 @@ class lib:
|
||||||
# indent each newline plus extra spaces for word values
|
# indent each newline plus extra spaces for word values
|
||||||
indented_string = quoted_string.replace('\n', '\n' + indent +" ")
|
indented_string = quoted_string.replace('\n', '\n' + indent +" ")
|
||||||
self.lib.write("{0}values({1});\n".format(indent,indented_string))
|
self.lib.write("{0}values({1});\n".format(indent,indented_string))
|
||||||
|
|
||||||
def write_LUT_templates(self):
|
def write_LUT_templates(self):
|
||||||
""" Adds lookup_table format (A 1x1 lookup_table)."""
|
""" Adds lookup_table format (A 1x1 lookup_table)."""
|
||||||
|
|
||||||
Tran = ["CELL_TABLE"]
|
Tran = ["CELL_TABLE"]
|
||||||
for i in Tran:
|
for i in Tran:
|
||||||
self.lib.write(" lu_table_template({0})".format(i))
|
self.lib.write(" lu_table_template({0})".format(i))
|
||||||
|
|
@ -278,8 +278,8 @@ class lib:
|
||||||
self.lib.write(" variable_1 : input_net_transition;\n")
|
self.lib.write(" variable_1 : input_net_transition;\n")
|
||||||
self.lib.write(" variable_2 : total_output_net_capacitance;\n")
|
self.lib.write(" variable_2 : total_output_net_capacitance;\n")
|
||||||
self.write_index(1,self.slews)
|
self.write_index(1,self.slews)
|
||||||
# Dividing by 1000 to all cap values since output of .sp is in fF,
|
# Dividing by 1000 to all cap values since output of .sp is in fF,
|
||||||
# and it needs to be in pF for Innovus.
|
# and it needs to be in pF for Innovus.
|
||||||
self.write_index(2,self.loads/1000)
|
self.write_index(2,self.loads/1000)
|
||||||
self.lib.write(" }\n\n")
|
self.lib.write(" }\n\n")
|
||||||
|
|
||||||
|
|
@ -292,12 +292,12 @@ class lib:
|
||||||
self.write_index(1,self.slews)
|
self.write_index(1,self.slews)
|
||||||
self.write_index(2,self.slews)
|
self.write_index(2,self.slews)
|
||||||
self.lib.write(" }\n\n")
|
self.lib.write(" }\n\n")
|
||||||
|
|
||||||
# self.lib.write(" lu_table_template(CLK_TRAN) {\n")
|
# self.lib.write(" lu_table_template(CLK_TRAN) {\n")
|
||||||
# self.lib.write(" variable_1 : constrained_pin_transition;\n")
|
# self.lib.write(" variable_1 : constrained_pin_transition;\n")
|
||||||
# self.write_index(1,self.slews)
|
# self.write_index(1,self.slews)
|
||||||
# self.lib.write(" }\n\n")
|
# self.lib.write(" }\n\n")
|
||||||
|
|
||||||
# self.lib.write(" lu_table_template(TRAN) {\n")
|
# self.lib.write(" lu_table_template(TRAN) {\n")
|
||||||
# self.lib.write(" variable_1 : total_output_net_capacitance;\n")
|
# self.lib.write(" variable_1 : total_output_net_capacitance;\n")
|
||||||
# self.write_index(1,self.slews)
|
# self.write_index(1,self.slews)
|
||||||
|
|
@ -311,10 +311,10 @@ class lib:
|
||||||
# #self.write_index(1,self.slews)
|
# #self.write_index(1,self.slews)
|
||||||
# self.write_index(1,[self.slews[0]])
|
# self.write_index(1,[self.slews[0]])
|
||||||
# self.lib.write(" }\n\n")
|
# self.lib.write(" }\n\n")
|
||||||
|
|
||||||
def write_bus(self):
|
def write_bus(self):
|
||||||
""" Adds format of data and addr bus."""
|
""" Adds format of data and addr bus."""
|
||||||
|
|
||||||
self.lib.write("\n\n")
|
self.lib.write("\n\n")
|
||||||
self.lib.write(" type (data){\n")
|
self.lib.write(" type (data){\n")
|
||||||
self.lib.write(" base_type : array;\n")
|
self.lib.write(" base_type : array;\n")
|
||||||
|
|
@ -378,11 +378,11 @@ class lib:
|
||||||
self.lib.write(" direction : output; \n")
|
self.lib.write(" direction : output; \n")
|
||||||
# This is conservative, but limit to range that we characterized.
|
# This is conservative, but limit to range that we characterized.
|
||||||
self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads)/1000))
|
self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads)/1000))
|
||||||
self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads)/1000))
|
self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads)/1000))
|
||||||
self.lib.write(" memory_read(){ \n")
|
self.lib.write(" memory_read(){ \n")
|
||||||
self.lib.write(" address : addr{0}; \n".format(read_port))
|
self.lib.write(" address : addr{0}; \n".format(read_port))
|
||||||
self.lib.write(" }\n")
|
self.lib.write(" }\n")
|
||||||
|
|
||||||
|
|
||||||
self.lib.write(" pin(dout{0}[{1}:0]){{\n".format(read_port,self.sram.word_size-1))
|
self.lib.write(" pin(dout{0}[{1}:0]){{\n".format(read_port,self.sram.word_size-1))
|
||||||
self.lib.write(" timing(){ \n")
|
self.lib.write(" timing(){ \n")
|
||||||
|
|
@ -402,7 +402,7 @@ class lib:
|
||||||
self.write_values(self.char_port_results[read_port]["slew_hl"],len(self.loads)," ")
|
self.write_values(self.char_port_results[read_port]["slew_hl"],len(self.loads)," ")
|
||||||
self.lib.write(" }\n") # fall trans
|
self.lib.write(" }\n") # fall trans
|
||||||
self.lib.write(" }\n") # timing
|
self.lib.write(" }\n") # timing
|
||||||
self.lib.write(" }\n") # pin
|
self.lib.write(" }\n") # pin
|
||||||
self.lib.write(" }\n\n") # bus
|
self.lib.write(" }\n\n") # bus
|
||||||
|
|
||||||
def write_data_bus_input(self, write_port):
|
def write_data_bus_input(self, write_port):
|
||||||
|
|
@ -416,10 +416,10 @@ class lib:
|
||||||
self.lib.write(" memory_write(){ \n")
|
self.lib.write(" memory_write(){ \n")
|
||||||
self.lib.write(" address : addr{0}; \n".format(write_port))
|
self.lib.write(" address : addr{0}; \n".format(write_port))
|
||||||
self.lib.write(" clocked_on : clk{0}; \n".format(write_port))
|
self.lib.write(" clocked_on : clk{0}; \n".format(write_port))
|
||||||
self.lib.write(" }\n")
|
self.lib.write(" }\n")
|
||||||
self.lib.write(" pin(din{0}[{1}:0]){{\n".format(write_port,self.sram.word_size-1))
|
self.lib.write(" pin(din{0}[{1}:0]){{\n".format(write_port,self.sram.word_size-1))
|
||||||
self.write_FF_setuphold(write_port)
|
self.write_FF_setuphold(write_port)
|
||||||
self.lib.write(" }\n") # pin
|
self.lib.write(" }\n") # pin
|
||||||
self.lib.write(" }\n") #bus
|
self.lib.write(" }\n") #bus
|
||||||
|
|
||||||
def write_data_bus(self, port):
|
def write_data_bus(self, port):
|
||||||
|
|
@ -431,7 +431,7 @@ class lib:
|
||||||
|
|
||||||
def write_addr_bus(self, port):
|
def write_addr_bus(self, port):
|
||||||
""" Adds addr bus timing results."""
|
""" Adds addr bus timing results."""
|
||||||
|
|
||||||
self.lib.write(" bus(addr{0}){{\n".format(port))
|
self.lib.write(" bus(addr{0}){{\n".format(port))
|
||||||
self.lib.write(" bus_type : addr; \n")
|
self.lib.write(" bus_type : addr; \n")
|
||||||
self.lib.write(" direction : input; \n")
|
self.lib.write(" direction : input; \n")
|
||||||
|
|
@ -439,9 +439,9 @@ class lib:
|
||||||
self.lib.write(" max_transition : {0};\n".format(self.slews[-1]))
|
self.lib.write(" max_transition : {0};\n".format(self.slews[-1]))
|
||||||
self.lib.write(" pin(addr{0}[{1}:0])".format(port,self.sram.addr_size-1))
|
self.lib.write(" pin(addr{0}[{1}:0])".format(port,self.sram.addr_size-1))
|
||||||
self.lib.write("{\n")
|
self.lib.write("{\n")
|
||||||
|
|
||||||
self.write_FF_setuphold(port)
|
self.write_FF_setuphold(port)
|
||||||
self.lib.write(" }\n")
|
self.lib.write(" }\n")
|
||||||
self.lib.write(" }\n\n")
|
self.lib.write(" }\n\n")
|
||||||
|
|
||||||
def write_wmask_bus(self, port):
|
def write_wmask_bus(self, port):
|
||||||
|
|
@ -465,7 +465,7 @@ class lib:
|
||||||
ctrl_pin_names = ["csb{0}".format(port)]
|
ctrl_pin_names = ["csb{0}".format(port)]
|
||||||
if port in self.readwrite_ports:
|
if port in self.readwrite_ports:
|
||||||
ctrl_pin_names.append("web{0}".format(port))
|
ctrl_pin_names.append("web{0}".format(port))
|
||||||
|
|
||||||
for i in ctrl_pin_names:
|
for i in ctrl_pin_names:
|
||||||
self.lib.write(" pin({0})".format(i))
|
self.lib.write(" pin({0})".format(i))
|
||||||
self.lib.write("{\n")
|
self.lib.write("{\n")
|
||||||
|
|
@ -508,12 +508,12 @@ class lib:
|
||||||
self.lib.write(" }\n")
|
self.lib.write(" }\n")
|
||||||
self.lib.write(" }\n")
|
self.lib.write(" }\n")
|
||||||
self.lib.write(" }\n\n")
|
self.lib.write(" }\n\n")
|
||||||
|
|
||||||
def add_clk_control_power(self, port):
|
def add_clk_control_power(self, port):
|
||||||
"""Writes powers under the clock pin group for a specified port"""
|
"""Writes powers under the clock pin group for a specified port"""
|
||||||
#Web added to read/write ports. Likely to change when control logic finished.
|
#Web added to read/write ports. Likely to change when control logic finished.
|
||||||
web_name = ""
|
web_name = ""
|
||||||
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
web_name = " & !web{0}".format(port)
|
web_name = " & !web{0}".format(port)
|
||||||
|
|
@ -556,7 +556,7 @@ class lib:
|
||||||
self.lib.write(" values(\"{0:.6e}\");\n".format(read0_power))
|
self.lib.write(" values(\"{0:.6e}\");\n".format(read0_power))
|
||||||
self.lib.write(" }\n")
|
self.lib.write(" }\n")
|
||||||
self.lib.write(" }\n")
|
self.lib.write(" }\n")
|
||||||
|
|
||||||
# Disabled power.
|
# Disabled power.
|
||||||
disabled_read1_power = np.mean(self.char_port_results[port]["disabled_read1_power"])
|
disabled_read1_power = np.mean(self.char_port_results[port]["disabled_read1_power"])
|
||||||
disabled_read0_power = np.mean(self.char_port_results[port]["disabled_read0_power"])
|
disabled_read0_power = np.mean(self.char_port_results[port]["disabled_read0_power"])
|
||||||
|
|
@ -585,7 +585,7 @@ class lib:
|
||||||
self.d = delay(self.sram, self.sp_file, self.corner)
|
self.d = delay(self.sram, self.sp_file, self.corner)
|
||||||
if self.use_model:
|
if self.use_model:
|
||||||
char_results = self.d.analytical_delay(self.slews,self.loads)
|
char_results = self.d.analytical_delay(self.slews,self.loads)
|
||||||
self.char_sram_results, self.char_port_results = char_results
|
self.char_sram_results, self.char_port_results = char_results
|
||||||
else:
|
else:
|
||||||
if (self.sram.num_spare_rows == 0):
|
if (self.sram.num_spare_rows == 0):
|
||||||
probe_address = "1" * self.sram.addr_size
|
probe_address = "1" * self.sram.addr_size
|
||||||
|
|
@ -593,8 +593,8 @@ class lib:
|
||||||
probe_address = "0" + "1" * (self.sram.addr_size - 1)
|
probe_address = "0" + "1" * (self.sram.addr_size - 1)
|
||||||
probe_data = self.sram.word_size - 1
|
probe_data = self.sram.word_size - 1
|
||||||
char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
|
char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
|
||||||
self.char_sram_results, self.char_port_results = char_results
|
self.char_sram_results, self.char_port_results = char_results
|
||||||
|
|
||||||
def compute_setup_hold(self):
|
def compute_setup_hold(self):
|
||||||
""" Do the analysis if we haven't characterized a FF yet """
|
""" Do the analysis if we haven't characterized a FF yet """
|
||||||
# Do the analysis if we haven't characterized a FF yet
|
# Do the analysis if we haven't characterized a FF yet
|
||||||
|
|
@ -604,8 +604,8 @@ class lib:
|
||||||
self.times = self.sh.analytical_setuphold(self.slews,self.loads)
|
self.times = self.sh.analytical_setuphold(self.slews,self.loads)
|
||||||
else:
|
else:
|
||||||
self.times = self.sh.analyze(self.slews,self.slews)
|
self.times = self.sh.analyze(self.slews,self.slews)
|
||||||
|
|
||||||
|
|
||||||
def parse_info(self,corner,lib_name):
|
def parse_info(self,corner,lib_name):
|
||||||
""" Copies important characterization data to datasheet.info to be added to datasheet """
|
""" Copies important characterization data to datasheet.info to be added to datasheet """
|
||||||
if OPTS.is_unit_test:
|
if OPTS.is_unit_test:
|
||||||
|
|
@ -617,9 +617,9 @@ class lib:
|
||||||
proc = subprocess.Popen(['git','rev-parse','HEAD'], cwd=os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/', stdout=subprocess.PIPE)
|
proc = subprocess.Popen(['git','rev-parse','HEAD'], cwd=os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/', stdout=subprocess.PIPE)
|
||||||
|
|
||||||
git_id = str(proc.stdout.read())
|
git_id = str(proc.stdout.read())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
git_id = git_id[2:-3]
|
git_id = git_id[2:-3]
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
# check if git id is valid
|
# check if git id is valid
|
||||||
|
|
@ -628,7 +628,7 @@ class lib:
|
||||||
git_id = 'Failed to retruieve'
|
git_id = 'Failed to retruieve'
|
||||||
|
|
||||||
datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+')
|
datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+')
|
||||||
|
|
||||||
current_time = datetime.date.today()
|
current_time = datetime.date.today()
|
||||||
# write static information to be parser later
|
# write static information to be parser later
|
||||||
datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},".format(
|
datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},".format(
|
||||||
|
|
@ -654,10 +654,10 @@ class lib:
|
||||||
# information of checks
|
# information of checks
|
||||||
# run it only the first time
|
# run it only the first time
|
||||||
datasheet.write("{0},{1},".format(self.sram.drc_errors, self.sram.lvs_errors))
|
datasheet.write("{0},{1},".format(self.sram.drc_errors, self.sram.lvs_errors))
|
||||||
|
|
||||||
# write area
|
# write area
|
||||||
datasheet.write(str(self.sram.width * self.sram.height) + ',')
|
datasheet.write(str(self.sram.width * self.sram.height) + ',')
|
||||||
|
|
||||||
# write timing information for all ports
|
# write timing information for all ports
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
#din timings
|
#din timings
|
||||||
|
|
@ -675,7 +675,7 @@ class lib:
|
||||||
|
|
||||||
min(list(map(round_time,self.times["hold_times_HL"]))),
|
min(list(map(round_time,self.times["hold_times_HL"]))),
|
||||||
max(list(map(round_time,self.times["hold_times_HL"])))
|
max(list(map(round_time,self.times["hold_times_HL"])))
|
||||||
|
|
||||||
))
|
))
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -695,7 +695,7 @@ class lib:
|
||||||
min(list(map(round_time,self.char_port_results[port]["slew_hl"]))),
|
min(list(map(round_time,self.char_port_results[port]["slew_hl"]))),
|
||||||
max(list(map(round_time,self.char_port_results[port]["slew_hl"])))
|
max(list(map(round_time,self.char_port_results[port]["slew_hl"])))
|
||||||
|
|
||||||
|
|
||||||
))
|
))
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -791,9 +791,9 @@ class lib:
|
||||||
control_str = 'csb0'
|
control_str = 'csb0'
|
||||||
for i in range(1, self.total_port_num):
|
for i in range(1, self.total_port_num):
|
||||||
control_str += ' & csb{0}'.format(i)
|
control_str += ' & csb{0}'.format(i)
|
||||||
|
|
||||||
datasheet.write("{0},{1},{2},".format('leak', control_str, self.char_sram_results["leakage_power"]))
|
datasheet.write("{0},{1},{2},".format('leak', control_str, self.char_sram_results["leakage_power"]))
|
||||||
|
|
||||||
|
|
||||||
datasheet.write("END\n")
|
datasheet.write("END\n")
|
||||||
datasheet.close()
|
datasheet.close()
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class logical_effort():
|
||||||
min_inv_cin = 1+beta
|
min_inv_cin = 1+beta
|
||||||
pinv=parameter["min_inv_para_delay"]
|
pinv=parameter["min_inv_para_delay"]
|
||||||
tau = parameter['le_tau']
|
tau = parameter['le_tau']
|
||||||
|
|
||||||
def __init__(self, name, size, cin, cout, parasitic, out_is_rise=True):
|
def __init__(self, name, size, cin, cout, parasitic, out_is_rise=True):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.cin = cin
|
self.cin = cin
|
||||||
|
|
@ -26,31 +26,31 @@ class logical_effort():
|
||||||
self.electrical_effort = self.cout/self.cin
|
self.electrical_effort = self.cout/self.cin
|
||||||
self.parasitic_scale = parasitic
|
self.parasitic_scale = parasitic
|
||||||
self.is_rise = out_is_rise
|
self.is_rise = out_is_rise
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Name={}, g={}, h={}, p={}*pinv, rise_delay={}".format(self.name,
|
return "Name={}, g={}, h={}, p={}*pinv, rise_delay={}".format(self.name,
|
||||||
self.logical_effort,
|
self.logical_effort,
|
||||||
self.electrical_effort,
|
self.electrical_effort,
|
||||||
self.parasitic_scale,
|
self.parasitic_scale,
|
||||||
self.is_rise
|
self.is_rise
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_stage_effort(self):
|
def get_stage_effort(self):
|
||||||
return self.logical_effort*self.electrical_effort
|
return self.logical_effort*self.electrical_effort
|
||||||
|
|
||||||
def get_parasitic_delay(self):
|
def get_parasitic_delay(self):
|
||||||
return logical_effort.pinv*self.parasitic_scale
|
return logical_effort.pinv*self.parasitic_scale
|
||||||
|
|
||||||
def get_stage_delay(self):
|
def get_stage_delay(self):
|
||||||
return self.get_stage_effort()+self.get_parasitic_delay()
|
return self.get_stage_effort()+self.get_parasitic_delay()
|
||||||
|
|
||||||
def get_absolute_delay(self):
|
def get_absolute_delay(self):
|
||||||
return logical_effort.tau*self.get_stage_delay()
|
return logical_effort.tau*self.get_stage_delay()
|
||||||
|
|
||||||
def calculate_delays(stage_effort_list):
|
def calculate_delays(stage_effort_list):
|
||||||
"""Convert stage effort objects to list of delay values"""
|
"""Convert stage effort objects to list of delay values"""
|
||||||
return [stage.get_stage_delay() for stage in stage_effort_list]
|
return [stage.get_stage_delay() for stage in stage_effort_list]
|
||||||
|
|
||||||
def calculate_relative_delay(stage_effort_list):
|
def calculate_relative_delay(stage_effort_list):
|
||||||
"""Calculates the total delay of a given delay path made of a list of logical effort objects."""
|
"""Calculates the total delay of a given delay path made of a list of logical effort objects."""
|
||||||
total_rise_delay, total_fall_delay = calculate_relative_rise_fall_delays(stage_effort_list)
|
total_rise_delay, total_fall_delay = calculate_relative_rise_fall_delays(stage_effort_list)
|
||||||
|
|
@ -62,7 +62,7 @@ def calculate_absolute_delay(stage_effort_list):
|
||||||
for stage in stage_effort_list:
|
for stage in stage_effort_list:
|
||||||
total_delay+=stage.get_absolute_delay()
|
total_delay+=stage.get_absolute_delay()
|
||||||
return total_delay
|
return total_delay
|
||||||
|
|
||||||
def calculate_relative_rise_fall_delays(stage_effort_list):
|
def calculate_relative_rise_fall_delays(stage_effort_list):
|
||||||
"""Calculates the rise/fall delays of a given delay path made of a list of logical effort objects."""
|
"""Calculates the rise/fall delays of a given delay path made of a list of logical effort objects."""
|
||||||
debug.info(2, "Calculating rise/fall relative delays")
|
debug.info(2, "Calculating rise/fall relative delays")
|
||||||
|
|
@ -74,11 +74,11 @@ def calculate_relative_rise_fall_delays(stage_effort_list):
|
||||||
else:
|
else:
|
||||||
total_fall_delay += stage.get_stage_delay()
|
total_fall_delay += stage.get_stage_delay()
|
||||||
return total_rise_delay, total_fall_delay
|
return total_rise_delay, total_fall_delay
|
||||||
|
|
||||||
def convert_farad_to_relative_c(c_farad):
|
def convert_farad_to_relative_c(c_farad):
|
||||||
"""Converts capacitance in Femto-Farads to relative capacitance."""
|
"""Converts capacitance in Femto-Farads to relative capacitance."""
|
||||||
return c_farad*parameter['cap_relative_per_ff']
|
return c_farad*parameter['cap_relative_per_ff']
|
||||||
|
|
||||||
def convert_relative_c_to_farad(c_relative):
|
def convert_relative_c_to_farad(c_relative):
|
||||||
"""Converts capacitance in logical effort relative units to Femto-Farads."""
|
"""Converts capacitance in logical effort relative units to Femto-Farads."""
|
||||||
return c_relative/parameter['cap_relative_per_ff']
|
return c_relative/parameter['cap_relative_per_ff']
|
||||||
|
|
@ -19,63 +19,63 @@ class spice_measurement(ABC):
|
||||||
self.measure_scale = measure_scale
|
self.measure_scale = measure_scale
|
||||||
self.has_port = has_port #Needed for error checking
|
self.has_port = has_port #Needed for error checking
|
||||||
#Some meta values used externally. variables are added here for consistency accross the objects
|
#Some meta values used externally. variables are added here for consistency accross the objects
|
||||||
self.meta_str = None
|
self.meta_str = None
|
||||||
self.meta_add_delay = False
|
self.meta_add_delay = False
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_measure_function(self):
|
def get_measure_function(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_measure_values(self):
|
def get_measure_values(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def write_measure(self, stim_obj, input_tuple):
|
def write_measure(self, stim_obj, input_tuple):
|
||||||
measure_func = self.get_measure_function()
|
measure_func = self.get_measure_function()
|
||||||
if measure_func == None:
|
if measure_func == None:
|
||||||
debug.error("Did not set measure function",1)
|
debug.error("Did not set measure function",1)
|
||||||
measure_vals = self.get_measure_values(*input_tuple)
|
measure_vals = self.get_measure_values(*input_tuple)
|
||||||
measure_func(stim_obj, *measure_vals)
|
measure_func(stim_obj, *measure_vals)
|
||||||
|
|
||||||
def retrieve_measure(self, port=None):
|
def retrieve_measure(self, port=None):
|
||||||
self.port_error_check(port)
|
self.port_error_check(port)
|
||||||
if port != None:
|
if port != None:
|
||||||
value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port))
|
value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port))
|
||||||
else:
|
else:
|
||||||
value = parse_spice_list("timing", "{0}".format(self.name.lower()))
|
value = parse_spice_list("timing", "{0}".format(self.name.lower()))
|
||||||
if type(value)!=float or self.measure_scale == None:
|
if type(value)!=float or self.measure_scale == None:
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
return value*self.measure_scale
|
return value*self.measure_scale
|
||||||
|
|
||||||
def port_error_check(self, port):
|
def port_error_check(self, port):
|
||||||
if self.has_port and port == None:
|
if self.has_port and port == None:
|
||||||
debug.error("Cannot retrieve measurement, port input was expected.",1)
|
debug.error("Cannot retrieve measurement, port input was expected.",1)
|
||||||
elif not self.has_port and port != None:
|
elif not self.has_port and port != None:
|
||||||
debug.error("Unexpected port input received during measure retrieval.",1)
|
debug.error("Unexpected port input received during measure retrieval.",1)
|
||||||
|
|
||||||
class delay_measure(spice_measurement):
|
class delay_measure(spice_measurement):
|
||||||
"""Generates a spice measurement for the delay of 50%-to-50% points of two signals."""
|
"""Generates a spice measurement for the delay of 50%-to-50% points of two signals."""
|
||||||
|
|
||||||
def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str,\
|
def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str,\
|
||||||
trig_vdd=0.5, targ_vdd=0.5, measure_scale=None, has_port=True):
|
trig_vdd=0.5, targ_vdd=0.5, measure_scale=None, has_port=True):
|
||||||
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
||||||
self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd)
|
self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd)
|
||||||
|
|
||||||
def get_measure_function(self):
|
def get_measure_function(self):
|
||||||
return stimuli.gen_meas_delay
|
return stimuli.gen_meas_delay
|
||||||
|
|
||||||
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd):
|
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd):
|
||||||
"""Set the constants for this measurement: signal names, directions, and trigger scales"""
|
"""Set the constants for this measurement: signal names, directions, and trigger scales"""
|
||||||
self.trig_dir_str = trig_dir_str
|
self.trig_dir_str = trig_dir_str
|
||||||
self.targ_dir_str = targ_dir_str
|
self.targ_dir_str = targ_dir_str
|
||||||
self.trig_val_of_vdd = trig_vdd
|
self.trig_val_of_vdd = trig_vdd
|
||||||
self.targ_val_of_vdd = targ_vdd
|
self.targ_val_of_vdd = targ_vdd
|
||||||
self.trig_name_no_port = trig_name
|
self.trig_name_no_port = trig_name
|
||||||
self.targ_name_no_port = targ_name
|
self.targ_name_no_port = targ_name
|
||||||
|
|
||||||
#Time delays and ports are variant and needed as inputs when writing the measurement
|
#Time delays and ports are variant and needed as inputs when writing the measurement
|
||||||
|
|
||||||
def get_measure_values(self, trig_td, targ_td, vdd_voltage, port=None):
|
def get_measure_values(self, trig_td, targ_td, vdd_voltage, port=None):
|
||||||
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
||||||
self.port_error_check(port)
|
self.port_error_check(port)
|
||||||
trig_val = self.trig_val_of_vdd * vdd_voltage
|
trig_val = self.trig_val_of_vdd * vdd_voltage
|
||||||
|
|
@ -90,74 +90,74 @@ class delay_measure(spice_measurement):
|
||||||
meas_name = self.name
|
meas_name = self.name
|
||||||
trig_name = self.trig_name_no_port
|
trig_name = self.trig_name_no_port
|
||||||
targ_name = self.targ_name_no_port
|
targ_name = self.targ_name_no_port
|
||||||
return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td)
|
return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td)
|
||||||
|
|
||||||
|
class slew_measure(delay_measure):
|
||||||
|
|
||||||
class slew_measure(delay_measure):
|
|
||||||
|
|
||||||
def __init__(self, measure_name, signal_name, slew_dir_str, measure_scale=None, has_port=True):
|
def __init__(self, measure_name, signal_name, slew_dir_str, measure_scale=None, has_port=True):
|
||||||
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
||||||
self.set_meas_constants(signal_name, slew_dir_str)
|
self.set_meas_constants(signal_name, slew_dir_str)
|
||||||
|
|
||||||
def set_meas_constants(self, signal_name, slew_dir_str):
|
def set_meas_constants(self, signal_name, slew_dir_str):
|
||||||
"""Set the values needed to generate a Spice measurement statement based on the name of the measurement."""
|
"""Set the values needed to generate a Spice measurement statement based on the name of the measurement."""
|
||||||
self.trig_dir_str = slew_dir_str
|
self.trig_dir_str = slew_dir_str
|
||||||
self.targ_dir_str = slew_dir_str
|
self.targ_dir_str = slew_dir_str
|
||||||
|
|
||||||
if slew_dir_str == "RISE":
|
if slew_dir_str == "RISE":
|
||||||
self.trig_val_of_vdd = 0.1
|
self.trig_val_of_vdd = 0.1
|
||||||
self.targ_val_of_vdd = 0.9
|
self.targ_val_of_vdd = 0.9
|
||||||
elif slew_dir_str == "FALL":
|
elif slew_dir_str == "FALL":
|
||||||
self.trig_val_of_vdd = 0.9
|
self.trig_val_of_vdd = 0.9
|
||||||
self.targ_val_of_vdd = 0.1
|
self.targ_val_of_vdd = 0.1
|
||||||
else:
|
else:
|
||||||
debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str),1)
|
debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str),1)
|
||||||
self.trig_name_no_port = signal_name
|
self.trig_name_no_port = signal_name
|
||||||
self.targ_name_no_port = signal_name
|
self.targ_name_no_port = signal_name
|
||||||
|
|
||||||
#Time delays and ports are variant and needed as inputs when writing the measurement
|
#Time delays and ports are variant and needed as inputs when writing the measurement
|
||||||
|
|
||||||
class power_measure(spice_measurement):
|
class power_measure(spice_measurement):
|
||||||
"""Generates a spice measurement for the average power between two time points."""
|
"""Generates a spice measurement for the average power between two time points."""
|
||||||
|
|
||||||
def __init__(self, measure_name, power_type="", measure_scale=None, has_port=True):
|
def __init__(self, measure_name, power_type="", measure_scale=None, has_port=True):
|
||||||
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
||||||
self.set_meas_constants(power_type)
|
self.set_meas_constants(power_type)
|
||||||
|
|
||||||
def get_measure_function(self):
|
def get_measure_function(self):
|
||||||
return stimuli.gen_meas_power
|
return stimuli.gen_meas_power
|
||||||
|
|
||||||
def set_meas_constants(self, power_type):
|
def set_meas_constants(self, power_type):
|
||||||
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
||||||
#Not needed for power simulation
|
#Not needed for power simulation
|
||||||
self.power_type = power_type #Expected to be "RISE"/"FALL"
|
self.power_type = power_type #Expected to be "RISE"/"FALL"
|
||||||
|
|
||||||
def get_measure_values(self, t_initial, t_final, port=None):
|
def get_measure_values(self, t_initial, t_final, port=None):
|
||||||
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
||||||
self.port_error_check(port)
|
self.port_error_check(port)
|
||||||
if port != None:
|
if port != None:
|
||||||
meas_name = "{}{}".format(self.name, port)
|
meas_name = "{}{}".format(self.name, port)
|
||||||
else:
|
else:
|
||||||
meas_name = self.name
|
meas_name = self.name
|
||||||
return (meas_name,t_initial,t_final)
|
return (meas_name,t_initial,t_final)
|
||||||
|
|
||||||
class voltage_when_measure(spice_measurement):
|
class voltage_when_measure(spice_measurement):
|
||||||
"""Generates a spice measurement to measure the voltage of a signal based on the voltage of another."""
|
"""Generates a spice measurement to measure the voltage of a signal based on the voltage of another."""
|
||||||
|
|
||||||
def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, trig_vdd, measure_scale=None, has_port=True):
|
def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, trig_vdd, measure_scale=None, has_port=True):
|
||||||
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
||||||
self.set_meas_constants(trig_name, targ_name, trig_dir_str, trig_vdd)
|
self.set_meas_constants(trig_name, targ_name, trig_dir_str, trig_vdd)
|
||||||
|
|
||||||
def get_measure_function(self):
|
def get_measure_function(self):
|
||||||
return stimuli.gen_meas_find_voltage
|
return stimuli.gen_meas_find_voltage
|
||||||
|
|
||||||
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, trig_vdd):
|
def set_meas_constants(self, trig_name, targ_name, trig_dir_str, trig_vdd):
|
||||||
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
||||||
self.trig_dir_str = trig_dir_str
|
self.trig_dir_str = trig_dir_str
|
||||||
self.trig_val_of_vdd = trig_vdd
|
self.trig_val_of_vdd = trig_vdd
|
||||||
self.trig_name_no_port = trig_name
|
self.trig_name_no_port = trig_name
|
||||||
self.targ_name_no_port = targ_name
|
self.targ_name_no_port = targ_name
|
||||||
|
|
||||||
def get_measure_values(self, trig_td, vdd_voltage, port=None):
|
def get_measure_values(self, trig_td, vdd_voltage, port=None):
|
||||||
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
||||||
self.port_error_check(port)
|
self.port_error_check(port)
|
||||||
if port != None:
|
if port != None:
|
||||||
|
|
@ -169,25 +169,25 @@ class voltage_when_measure(spice_measurement):
|
||||||
meas_name = self.name
|
meas_name = self.name
|
||||||
trig_name = self.trig_name_no_port
|
trig_name = self.trig_name_no_port
|
||||||
targ_name = self.targ_name_no_port
|
targ_name = self.targ_name_no_port
|
||||||
trig_voltage = self.trig_val_of_vdd*vdd_voltage
|
trig_voltage = self.trig_val_of_vdd*vdd_voltage
|
||||||
return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td)
|
return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td)
|
||||||
|
|
||||||
class voltage_at_measure(spice_measurement):
|
class voltage_at_measure(spice_measurement):
|
||||||
"""Generates a spice measurement to measure the voltage at a specific time.
|
"""Generates a spice measurement to measure the voltage at a specific time.
|
||||||
The time is considered variant with different periods."""
|
The time is considered variant with different periods."""
|
||||||
|
|
||||||
def __init__(self, measure_name, targ_name, measure_scale=None, has_port=True):
|
def __init__(self, measure_name, targ_name, measure_scale=None, has_port=True):
|
||||||
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
||||||
self.set_meas_constants(targ_name)
|
self.set_meas_constants(targ_name)
|
||||||
|
|
||||||
def get_measure_function(self):
|
def get_measure_function(self):
|
||||||
return stimuli.gen_meas_find_voltage_at_time
|
return stimuli.gen_meas_find_voltage_at_time
|
||||||
|
|
||||||
def set_meas_constants(self, targ_name):
|
def set_meas_constants(self, targ_name):
|
||||||
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
||||||
self.targ_name_no_port = targ_name
|
self.targ_name_no_port = targ_name
|
||||||
|
|
||||||
def get_measure_values(self, time_at, port=None):
|
def get_measure_values(self, time_at, port=None):
|
||||||
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
||||||
self.port_error_check(port)
|
self.port_error_check(port)
|
||||||
if port != None:
|
if port != None:
|
||||||
|
|
@ -196,6 +196,6 @@ class voltage_at_measure(spice_measurement):
|
||||||
targ_name = self.targ_name_no_port.format(port)
|
targ_name = self.targ_name_no_port.format(port)
|
||||||
else:
|
else:
|
||||||
meas_name = self.name
|
meas_name = self.name
|
||||||
targ_name = self.targ_name_no_port
|
targ_name = self.targ_name_no_port
|
||||||
return (meas_name,targ_name,time_at)
|
return (meas_name,targ_name,time_at)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,14 @@ class model_check(delay):
|
||||||
self.period = tech.spice["feasible_period"]
|
self.period = tech.spice["feasible_period"]
|
||||||
self.create_data_names()
|
self.create_data_names()
|
||||||
self.custom_delaychain=custom_delaychain
|
self.custom_delaychain=custom_delaychain
|
||||||
|
|
||||||
def create_data_names(self):
|
def create_data_names(self):
|
||||||
self.wl_meas_name, self.wl_model_name = "wl_measures", "wl_model"
|
self.wl_meas_name, self.wl_model_name = "wl_measures", "wl_model"
|
||||||
self.sae_meas_name, self.sae_model_name = "sae_measures", "sae_model"
|
self.sae_meas_name, self.sae_model_name = "sae_measures", "sae_model"
|
||||||
self.wl_slew_name, self.sae_slew_name = "wl_slews", "sae_slews"
|
self.wl_slew_name, self.sae_slew_name = "wl_slews", "sae_slews"
|
||||||
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, port):
|
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
|
||||||
|
|
@ -73,10 +73,10 @@ class model_check(delay):
|
||||||
else:
|
else:
|
||||||
self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar"]+dc_slew_names
|
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, port):
|
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
|
||||||
|
|
@ -90,7 +90,7 @@ class model_check(delay):
|
||||||
if self.custom_delaychain:
|
if self.custom_delaychain:
|
||||||
delay_chain_signal_names = []
|
delay_chain_signal_names = []
|
||||||
else:
|
else:
|
||||||
delay_chain_signal_names = ["Xsram.Xcontrol{}.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:
|
if len(self.sram.all_ports) > 1:
|
||||||
port_format = '{}'
|
port_format = '{}'
|
||||||
else:
|
else:
|
||||||
|
|
@ -103,21 +103,21 @@ class model_check(delay):
|
||||||
pre_delay_chain_names = ["Xsram.Xcontrol{}.gated_clk_bar".format('{}')]
|
pre_delay_chain_names = ["Xsram.Xcontrol{}.gated_clk_bar".format('{}')]
|
||||||
if port not in self.sram.readonly_ports:
|
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('{}')]
|
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.Xcontrol{}.Xreplica_bitline.delayed_en".format('{}')]
|
["Xsram.Xcontrol{}.Xreplica_bitline.delayed_en".format('{}')]
|
||||||
|
|
||||||
|
|
||||||
self.sae_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.bl0_0".format('{}'), "Xsram.Xcontrol{}.pre_s_en".format('{}')]+\
|
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_en{}".format('{}')]
|
["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(port_format, self.wordline_row),\
|
self.bl_signal_names = ["Xsram.Xbank0.wl{}_{}".format(port_format, self.wordline_row),\
|
||||||
"Xsram.Xbank0.bl{}_{}".format(port_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):
|
||||||
"""Create the measurements used for read and write ports"""
|
"""Create the measurements used for read and write ports"""
|
||||||
self.create_wordline_meas_objs()
|
self.create_wordline_meas_objs()
|
||||||
|
|
@ -125,101 +125,101 @@ class model_check(delay):
|
||||||
self.create_bl_meas_objs()
|
self.create_bl_meas_objs()
|
||||||
self.create_power_meas_objs()
|
self.create_power_meas_objs()
|
||||||
self.all_measures = self.wl_meas_objs+self.sae_meas_objs+self.bl_meas_objs+self.power_meas_objs
|
self.all_measures = self.wl_meas_objs+self.sae_meas_objs+self.bl_meas_objs+self.power_meas_objs
|
||||||
|
|
||||||
def create_power_meas_objs(self):
|
def create_power_meas_objs(self):
|
||||||
"""Create power measurement object. Only one."""
|
"""Create power measurement object. Only one."""
|
||||||
self.power_meas_objs = []
|
self.power_meas_objs = []
|
||||||
self.power_meas_objs.append(power_measure(self.power_meas_names[0], "FALL", measure_scale=1e3))
|
self.power_meas_objs.append(power_measure(self.power_meas_names[0], "FALL", measure_scale=1e3))
|
||||||
|
|
||||||
def create_wordline_meas_objs(self):
|
def create_wordline_meas_objs(self):
|
||||||
"""Create the measurements to measure the wordline path from the gated_clk_bar signal"""
|
"""Create the measurements to measure the wordline path from the gated_clk_bar signal"""
|
||||||
self.wl_meas_objs = []
|
self.wl_meas_objs = []
|
||||||
trig_dir = "RISE"
|
trig_dir = "RISE"
|
||||||
targ_dir = "FALL"
|
targ_dir = "FALL"
|
||||||
|
|
||||||
for i in range(1, len(self.wl_signal_names)):
|
for i in range(1, len(self.wl_signal_names)):
|
||||||
self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1],
|
self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1],
|
||||||
self.wl_signal_names[i-1],
|
self.wl_signal_names[i-1],
|
||||||
self.wl_signal_names[i],
|
self.wl_signal_names[i],
|
||||||
trig_dir,
|
trig_dir,
|
||||||
targ_dir,
|
targ_dir,
|
||||||
measure_scale=1e9))
|
measure_scale=1e9))
|
||||||
self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1],
|
self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1],
|
||||||
self.wl_signal_names[i-1],
|
self.wl_signal_names[i-1],
|
||||||
trig_dir,
|
trig_dir,
|
||||||
measure_scale=1e9))
|
measure_scale=1e9))
|
||||||
temp_dir = trig_dir
|
temp_dir = trig_dir
|
||||||
trig_dir = targ_dir
|
trig_dir = targ_dir
|
||||||
targ_dir = temp_dir
|
targ_dir = temp_dir
|
||||||
self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[-1], self.wl_signal_names[-1], trig_dir, measure_scale=1e9))
|
self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[-1], self.wl_signal_names[-1], trig_dir, measure_scale=1e9))
|
||||||
|
|
||||||
def create_bl_meas_objs(self):
|
def create_bl_meas_objs(self):
|
||||||
"""Create the measurements to measure the bitline to dout, static stages"""
|
"""Create the measurements to measure the bitline to dout, static stages"""
|
||||||
#Bitline has slightly different measurements, objects appends hardcoded.
|
#Bitline has slightly different measurements, objects appends hardcoded.
|
||||||
self.bl_meas_objs = []
|
self.bl_meas_objs = []
|
||||||
trig_dir, targ_dir = "RISE", "FALL" #Only check read 0
|
trig_dir, targ_dir = "RISE", "FALL" #Only check read 0
|
||||||
self.bl_meas_objs.append(delay_measure(self.bitline_meas_names[0],
|
self.bl_meas_objs.append(delay_measure(self.bitline_meas_names[0],
|
||||||
self.bl_signal_names[0],
|
self.bl_signal_names[0],
|
||||||
self.bl_signal_names[-1],
|
self.bl_signal_names[-1],
|
||||||
trig_dir,
|
trig_dir,
|
||||||
targ_dir,
|
targ_dir,
|
||||||
measure_scale=1e9))
|
measure_scale=1e9))
|
||||||
|
|
||||||
def create_sae_meas_objs(self):
|
def create_sae_meas_objs(self):
|
||||||
"""Create the measurements to measure the sense amp enable path from the gated_clk_bar signal. The RBL splits this path into two."""
|
"""Create the measurements to measure the sense amp enable path from the gated_clk_bar signal. The RBL splits this path into two."""
|
||||||
|
|
||||||
self.sae_meas_objs = []
|
self.sae_meas_objs = []
|
||||||
trig_dir = "RISE"
|
trig_dir = "RISE"
|
||||||
targ_dir = "FALL"
|
targ_dir = "FALL"
|
||||||
#Add measurements from gated_clk_bar to RBL
|
#Add measurements from gated_clk_bar to RBL
|
||||||
for i in range(1, len(self.rbl_en_signal_names)):
|
for i in range(1, len(self.rbl_en_signal_names)):
|
||||||
self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1],
|
self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1],
|
||||||
self.rbl_en_signal_names[i-1],
|
self.rbl_en_signal_names[i-1],
|
||||||
self.rbl_en_signal_names[i],
|
self.rbl_en_signal_names[i],
|
||||||
trig_dir,
|
trig_dir,
|
||||||
targ_dir,
|
targ_dir,
|
||||||
measure_scale=1e9))
|
measure_scale=1e9))
|
||||||
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1],
|
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1],
|
||||||
self.rbl_en_signal_names[i-1],
|
self.rbl_en_signal_names[i-1],
|
||||||
trig_dir,
|
trig_dir,
|
||||||
measure_scale=1e9))
|
measure_scale=1e9))
|
||||||
temp_dir = trig_dir
|
temp_dir = trig_dir
|
||||||
trig_dir = targ_dir
|
trig_dir = targ_dir
|
||||||
targ_dir = temp_dir
|
targ_dir = temp_dir
|
||||||
if self.custom_delaychain: #Hack for custom delay chains
|
if self.custom_delaychain: #Hack for custom delay chains
|
||||||
self.sae_meas_objs[-2] = delay_measure(self.rbl_delay_meas_names[-1],
|
self.sae_meas_objs[-2] = delay_measure(self.rbl_delay_meas_names[-1],
|
||||||
self.rbl_en_signal_names[-2],
|
self.rbl_en_signal_names[-2],
|
||||||
self.rbl_en_signal_names[-1],
|
self.rbl_en_signal_names[-1],
|
||||||
"RISE",
|
"RISE",
|
||||||
"RISE",
|
"RISE",
|
||||||
measure_scale=1e9)
|
measure_scale=1e9)
|
||||||
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1],
|
self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1],
|
||||||
self.rbl_en_signal_names[-1],
|
self.rbl_en_signal_names[-1],
|
||||||
trig_dir,
|
trig_dir,
|
||||||
measure_scale=1e9))
|
measure_scale=1e9))
|
||||||
|
|
||||||
#Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL.
|
#Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL.
|
||||||
trig_dir = "FALL"
|
trig_dir = "FALL"
|
||||||
targ_dir = "RISE"
|
targ_dir = "RISE"
|
||||||
for i in range(1, len(self.sae_signal_names)):
|
for i in range(1, len(self.sae_signal_names)):
|
||||||
self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1],
|
self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1],
|
||||||
self.sae_signal_names[i-1],
|
self.sae_signal_names[i-1],
|
||||||
self.sae_signal_names[i],
|
self.sae_signal_names[i],
|
||||||
trig_dir,
|
trig_dir,
|
||||||
targ_dir,
|
targ_dir,
|
||||||
measure_scale=1e9))
|
measure_scale=1e9))
|
||||||
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1],
|
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1],
|
||||||
self.sae_signal_names[i-1],
|
self.sae_signal_names[i-1],
|
||||||
trig_dir,
|
trig_dir,
|
||||||
measure_scale=1e9))
|
measure_scale=1e9))
|
||||||
temp_dir = trig_dir
|
temp_dir = trig_dir
|
||||||
trig_dir = targ_dir
|
trig_dir = targ_dir
|
||||||
targ_dir = temp_dir
|
targ_dir = temp_dir
|
||||||
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1],
|
self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1],
|
||||||
self.sae_signal_names[-1],
|
self.sae_signal_names[-1],
|
||||||
trig_dir,
|
trig_dir,
|
||||||
measure_scale=1e9))
|
measure_scale=1e9))
|
||||||
|
|
||||||
def write_delay_measures(self):
|
def write_delay_measures(self):
|
||||||
"""
|
"""
|
||||||
Write the measure statements to quantify the delay and power results for all targeted ports.
|
Write the measure statements to quantify the delay and power results for all targeted ports.
|
||||||
|
|
@ -229,12 +229,12 @@ class model_check(delay):
|
||||||
# Output some comments to aid where cycles start and what is happening
|
# Output some comments to aid where cycles start and what is happening
|
||||||
for comment in self.cycle_comments:
|
for comment in self.cycle_comments:
|
||||||
self.sf.write("* {}\n".format(comment))
|
self.sf.write("* {}\n".format(comment))
|
||||||
|
|
||||||
for read_port in self.targ_read_ports:
|
for read_port in self.targ_read_ports:
|
||||||
self.write_measures_read_port(read_port)
|
self.write_measures_read_port(read_port)
|
||||||
|
|
||||||
def get_delay_measure_variants(self, port, measure_obj):
|
def get_delay_measure_variants(self, port, measure_obj):
|
||||||
"""Get the measurement values that can either vary from simulation to simulation (vdd, address)
|
"""Get the measurement values that can either vary from simulation to simulation (vdd, address)
|
||||||
or port to port (time delays)"""
|
or port to port (time delays)"""
|
||||||
#Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port
|
#Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port
|
||||||
#Assuming only read 0 for now
|
#Assuming only read 0 for now
|
||||||
|
|
@ -246,15 +246,15 @@ class model_check(delay):
|
||||||
return self.get_power_measure_variants(port, measure_obj, "read")
|
return self.get_power_measure_variants(port, measure_obj, "read")
|
||||||
else:
|
else:
|
||||||
debug.error("Measurement not recognized by the model checker.",1)
|
debug.error("Measurement not recognized by the model checker.",1)
|
||||||
|
|
||||||
def get_power_measure_variants(self, port, power_obj, operation):
|
def get_power_measure_variants(self, port, power_obj, operation):
|
||||||
"""Get the measurement values that can either vary port to port (time delays)"""
|
"""Get the measurement values that can either vary port to port (time delays)"""
|
||||||
#Return value is intended to match the power measure format: t_initial, t_final, port
|
#Return value is intended to match the power measure format: t_initial, t_final, port
|
||||||
t_initial = self.cycle_times[self.measure_cycles[port]["read0"]]
|
t_initial = self.cycle_times[self.measure_cycles[port]["read0"]]
|
||||||
t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1]
|
t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1]
|
||||||
|
|
||||||
return (t_initial, t_final, port)
|
return (t_initial, t_final, port)
|
||||||
|
|
||||||
def write_measures_read_port(self, port):
|
def write_measures_read_port(self, port):
|
||||||
"""
|
"""
|
||||||
Write the measure statements for all nodes along the wordline path.
|
Write the measure statements for all nodes along the wordline path.
|
||||||
|
|
@ -263,16 +263,16 @@ class model_check(delay):
|
||||||
for measure in self.all_measures:
|
for measure in self.all_measures:
|
||||||
measure_variant_inp_tuple = self.get_delay_measure_variants(port, measure)
|
measure_variant_inp_tuple = self.get_delay_measure_variants(port, measure)
|
||||||
measure.write_measure(self.stim, measure_variant_inp_tuple)
|
measure.write_measure(self.stim, measure_variant_inp_tuple)
|
||||||
|
|
||||||
def get_measurement_values(self, meas_objs, port):
|
def get_measurement_values(self, meas_objs, port):
|
||||||
"""Gets the delays and slews from a specified port from the spice output file and returns them as lists."""
|
"""Gets the delays and slews from a specified port from the spice output file and returns them as lists."""
|
||||||
delay_meas_list = []
|
delay_meas_list = []
|
||||||
slew_meas_list = []
|
slew_meas_list = []
|
||||||
power_meas_list=[]
|
power_meas_list=[]
|
||||||
for measure in meas_objs:
|
for measure in meas_objs:
|
||||||
measure_value = measure.retrieve_measure(port=port)
|
measure_value = measure.retrieve_measure(port=port)
|
||||||
if type(measure_value) != float:
|
if type(measure_value) != float:
|
||||||
debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, measure_value),1)
|
debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, measure_value),1)
|
||||||
if type(measure) is delay_measure:
|
if type(measure) is delay_measure:
|
||||||
delay_meas_list.append(measure_value)
|
delay_meas_list.append(measure_value)
|
||||||
elif type(measure)is slew_measure:
|
elif type(measure)is slew_measure:
|
||||||
|
|
@ -282,7 +282,7 @@ class model_check(delay):
|
||||||
else:
|
else:
|
||||||
debug.error("Measurement object not recognized.",1)
|
debug.error("Measurement object not recognized.",1)
|
||||||
return delay_meas_list, slew_meas_list,power_meas_list
|
return delay_meas_list, slew_meas_list,power_meas_list
|
||||||
|
|
||||||
def run_delay_simulation(self):
|
def run_delay_simulation(self):
|
||||||
"""
|
"""
|
||||||
This tries to simulate a period and checks if the result works. If
|
This tries to simulate a period and checks if the result works. If
|
||||||
|
|
@ -291,8 +291,8 @@ class model_check(delay):
|
||||||
include leakage of all cells.
|
include leakage of all cells.
|
||||||
"""
|
"""
|
||||||
#Sanity Check
|
#Sanity Check
|
||||||
debug.check(self.period > 0, "Target simulation period non-positive")
|
debug.check(self.period > 0, "Target simulation period non-positive")
|
||||||
|
|
||||||
wl_delay_result = [[] for i in self.all_ports]
|
wl_delay_result = [[] for i in self.all_ports]
|
||||||
wl_slew_result = [[] for i in self.all_ports]
|
wl_slew_result = [[] for i in self.all_ports]
|
||||||
sae_delay_result = [[] for i in self.all_ports]
|
sae_delay_result = [[] for i in self.all_ports]
|
||||||
|
|
@ -304,44 +304,44 @@ class model_check(delay):
|
||||||
self.write_delay_stimulus()
|
self.write_delay_stimulus()
|
||||||
|
|
||||||
self.stim.run_sim() #running sim prodoces spice output file.
|
self.stim.run_sim() #running sim prodoces spice output file.
|
||||||
|
|
||||||
#Retrieve the results from the output file
|
#Retrieve the results from the output file
|
||||||
for port in self.targ_read_ports:
|
for port in self.targ_read_ports:
|
||||||
#Parse and check the voltage measurements
|
#Parse and check the voltage measurements
|
||||||
wl_delay_result[port], wl_slew_result[port],_ = self.get_measurement_values(self.wl_meas_objs, port)
|
wl_delay_result[port], wl_slew_result[port],_ = self.get_measurement_values(self.wl_meas_objs, port)
|
||||||
sae_delay_result[port], sae_slew_result[port],_ = self.get_measurement_values(self.sae_meas_objs, port)
|
sae_delay_result[port], sae_slew_result[port],_ = self.get_measurement_values(self.sae_meas_objs, port)
|
||||||
bl_delay_result[port], bl_slew_result[port],_ = self.get_measurement_values(self.bl_meas_objs, port)
|
bl_delay_result[port], bl_slew_result[port],_ = self.get_measurement_values(self.bl_meas_objs, port)
|
||||||
_,__,power_result[port] = self.get_measurement_values(self.power_meas_objs, port)
|
_,__,power_result[port] = self.get_measurement_values(self.power_meas_objs, port)
|
||||||
return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result, bl_delay_result, bl_slew_result, power_result)
|
return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result, bl_delay_result, bl_slew_result, power_result)
|
||||||
|
|
||||||
def get_model_delays(self, port):
|
def get_model_delays(self, port):
|
||||||
"""Get model delays based on port. Currently assumes single RW port."""
|
"""Get model delays based on port. Currently assumes single RW port."""
|
||||||
return self.sram.control_logic_rw.get_wl_sen_delays()
|
return self.sram.control_logic_rw.get_wl_sen_delays()
|
||||||
|
|
||||||
def get_num_delay_stages(self):
|
def get_num_delay_stages(self):
|
||||||
"""Gets the number of stages in the delay chain from the control logic"""
|
"""Gets the number of stages in the delay chain from the control logic"""
|
||||||
return len(self.sram.control_logic_rw.replica_bitline.delay_fanout_list)
|
return len(self.sram.control_logic_rw.replica_bitline.delay_fanout_list)
|
||||||
|
|
||||||
def get_num_delay_fanout_list(self):
|
def get_num_delay_fanout_list(self):
|
||||||
"""Gets the number of stages in the delay chain from the control logic"""
|
"""Gets the number of stages in the delay chain from the control logic"""
|
||||||
return self.sram.control_logic_rw.replica_bitline.delay_fanout_list
|
return self.sram.control_logic_rw.replica_bitline.delay_fanout_list
|
||||||
|
|
||||||
def get_num_delay_stage_fanout(self):
|
def get_num_delay_stage_fanout(self):
|
||||||
"""Gets fanout in each stage in the delay chain. Assumes each stage is the same"""
|
"""Gets fanout in each stage in the delay chain. Assumes each stage is the same"""
|
||||||
return self.sram.control_logic_rw.replica_bitline.delay_fanout_list[0]
|
return self.sram.control_logic_rw.replica_bitline.delay_fanout_list[0]
|
||||||
|
|
||||||
def get_num_wl_en_driver_stages(self):
|
def get_num_wl_en_driver_stages(self):
|
||||||
"""Gets the number of stages in the wl_en driver from the control logic"""
|
"""Gets the number of stages in the wl_en driver from the control logic"""
|
||||||
return self.sram.control_logic_rw.wl_en_driver.num_stages
|
return self.sram.control_logic_rw.wl_en_driver.num_stages
|
||||||
|
|
||||||
def get_num_sen_driver_stages(self):
|
def get_num_sen_driver_stages(self):
|
||||||
"""Gets the number of stages in the sen driver from the control logic"""
|
"""Gets the number of stages in the sen driver from the control logic"""
|
||||||
return self.sram.control_logic_rw.s_en_driver.num_stages
|
return self.sram.control_logic_rw.s_en_driver.num_stages
|
||||||
|
|
||||||
def get_num_wl_driver_stages(self):
|
def get_num_wl_driver_stages(self):
|
||||||
"""Gets the number of stages in the wordline driver from the control logic"""
|
"""Gets the number of stages in the wordline driver from the control logic"""
|
||||||
return self.sram.bank.wordline_driver.inv.num_stages
|
return self.sram.bank.wordline_driver.inv.num_stages
|
||||||
|
|
||||||
def scale_delays(self, delay_list):
|
def scale_delays(self, delay_list):
|
||||||
"""Takes in a list of measured delays and convert it to simple units to easily compare to model values."""
|
"""Takes in a list of measured delays and convert it to simple units to easily compare to model values."""
|
||||||
converted_values = []
|
converted_values = []
|
||||||
|
|
@ -350,12 +350,12 @@ class model_check(delay):
|
||||||
for meas_value in delay_list:
|
for meas_value in delay_list:
|
||||||
total+=meas_value
|
total+=meas_value
|
||||||
average = total/len(delay_list)
|
average = total/len(delay_list)
|
||||||
|
|
||||||
#Convert values
|
#Convert values
|
||||||
for meas_value in delay_list:
|
for meas_value in delay_list:
|
||||||
converted_values.append(meas_value/average)
|
converted_values.append(meas_value/average)
|
||||||
return converted_values
|
return converted_values
|
||||||
|
|
||||||
def min_max_normalization(self, value_list):
|
def min_max_normalization(self, value_list):
|
||||||
"""Re-scales input values on a range from 0-1 where min(list)=0, max(list)=1"""
|
"""Re-scales input values on a range from 0-1 where min(list)=0, max(list)=1"""
|
||||||
scaled_values = []
|
scaled_values = []
|
||||||
|
|
@ -364,14 +364,14 @@ class model_check(delay):
|
||||||
for value in value_list:
|
for value in value_list:
|
||||||
scaled_values.append((value-average)/(min_max_diff))
|
scaled_values.append((value-average)/(min_max_diff))
|
||||||
return scaled_values
|
return scaled_values
|
||||||
|
|
||||||
def calculate_error_l2_norm(self, list_a, list_b):
|
def calculate_error_l2_norm(self, list_a, list_b):
|
||||||
"""Calculates error between two lists using the l2 norm"""
|
"""Calculates error between two lists using the l2 norm"""
|
||||||
error_list = []
|
error_list = []
|
||||||
for val_a, val_b in zip(list_a, list_b):
|
for val_a, val_b in zip(list_a, list_b):
|
||||||
error_list.append((val_a-val_b)**2)
|
error_list.append((val_a-val_b)**2)
|
||||||
return error_list
|
return error_list
|
||||||
|
|
||||||
def compare_measured_and_model(self, measured_vals, model_vals):
|
def compare_measured_and_model(self, measured_vals, model_vals):
|
||||||
"""First scales both inputs into similar ranges and then compares the error between both."""
|
"""First scales both inputs into similar ranges and then compares the error between both."""
|
||||||
scaled_meas = self.min_max_normalization(measured_vals)
|
scaled_meas = self.min_max_normalization(measured_vals)
|
||||||
|
|
@ -380,7 +380,7 @@ class model_check(delay):
|
||||||
debug.info(1, "Scaled model:\n{}".format(scaled_model))
|
debug.info(1, "Scaled model:\n{}".format(scaled_model))
|
||||||
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, port):
|
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)
|
||||||
|
|
@ -390,7 +390,7 @@ class model_check(delay):
|
||||||
self.create_measurement_names(port)
|
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
|
read_port = port
|
||||||
self.targ_read_ports = [read_port]
|
self.targ_read_ports = [read_port]
|
||||||
|
|
@ -398,33 +398,33 @@ class model_check(delay):
|
||||||
debug.info(1,"Model test: corner {}".format(self.corner))
|
debug.info(1,"Model test: corner {}".format(self.corner))
|
||||||
(success, wl_delays, sae_delays, wl_slews, sae_slews, bl_delays, bl_slews, powers)=self.run_delay_simulation()
|
(success, wl_delays, sae_delays, wl_slews, sae_slews, bl_delays, bl_slews, powers)=self.run_delay_simulation()
|
||||||
debug.check(success, "Model measurements Failed: period={}".format(self.period))
|
debug.check(success, "Model measurements Failed: period={}".format(self.period))
|
||||||
|
|
||||||
debug.info(1,"Measured Wordline delays (ns):\n\t {}".format(wl_delays[read_port]))
|
debug.info(1,"Measured Wordline delays (ns):\n\t {}".format(wl_delays[read_port]))
|
||||||
debug.info(1,"Measured Wordline slews:\n\t {}".format(wl_slews[read_port]))
|
debug.info(1,"Measured Wordline slews:\n\t {}".format(wl_slews[read_port]))
|
||||||
debug.info(1,"Measured SAE delays (ns):\n\t {}".format(sae_delays[read_port]))
|
debug.info(1,"Measured SAE delays (ns):\n\t {}".format(sae_delays[read_port]))
|
||||||
debug.info(1,"Measured SAE slews:\n\t {}".format(sae_slews[read_port]))
|
debug.info(1,"Measured SAE slews:\n\t {}".format(sae_slews[read_port]))
|
||||||
debug.info(1,"Measured Bitline delays (ns):\n\t {}".format(bl_delays[read_port]))
|
debug.info(1,"Measured Bitline delays (ns):\n\t {}".format(bl_delays[read_port]))
|
||||||
|
|
||||||
data_dict[self.wl_meas_name] = wl_delays[read_port]
|
data_dict[self.wl_meas_name] = wl_delays[read_port]
|
||||||
data_dict[self.sae_meas_name] = sae_delays[read_port]
|
data_dict[self.sae_meas_name] = sae_delays[read_port]
|
||||||
data_dict[self.wl_slew_name] = wl_slews[read_port]
|
data_dict[self.wl_slew_name] = wl_slews[read_port]
|
||||||
data_dict[self.sae_slew_name] = sae_slews[read_port]
|
data_dict[self.sae_slew_name] = sae_slews[read_port]
|
||||||
data_dict[self.bl_meas_name] = bl_delays[read_port]
|
data_dict[self.bl_meas_name] = bl_delays[read_port]
|
||||||
data_dict[self.power_name] = powers[read_port]
|
data_dict[self.power_name] = powers[read_port]
|
||||||
|
|
||||||
if not OPTS.use_tech_delay_chain_size: #Model is not used in this case
|
if not OPTS.use_tech_delay_chain_size: #Model is not used in this case
|
||||||
wl_model_delays, sae_model_delays = self.get_model_delays(read_port)
|
wl_model_delays, sae_model_delays = self.get_model_delays(read_port)
|
||||||
debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays))
|
debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays))
|
||||||
debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays))
|
debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays))
|
||||||
data_dict[self.wl_model_name] = wl_model_delays
|
data_dict[self.wl_model_name] = wl_model_delays
|
||||||
data_dict[self.sae_model_name] = sae_model_delays
|
data_dict[self.sae_model_name] = sae_model_delays
|
||||||
|
|
||||||
#Some evaluations of the model and measured values
|
#Some evaluations of the model and measured values
|
||||||
# debug.info(1, "Comparing wordline measurements and model.")
|
# debug.info(1, "Comparing wordline measurements and model.")
|
||||||
# self.compare_measured_and_model(wl_delays[read_port], wl_model_delays)
|
# self.compare_measured_and_model(wl_delays[read_port], wl_model_delays)
|
||||||
# debug.info(1, "Comparing SAE measurements and model")
|
# debug.info(1, "Comparing SAE measurements and model")
|
||||||
# self.compare_measured_and_model(sae_delays[read_port], sae_model_delays)
|
# self.compare_measured_and_model(sae_delays[read_port], sae_model_delays)
|
||||||
|
|
||||||
return data_dict
|
return data_dict
|
||||||
|
|
||||||
def get_all_signal_names(self):
|
def get_all_signal_names(self):
|
||||||
|
|
@ -438,12 +438,12 @@ class model_check(delay):
|
||||||
name_dict[self.bl_meas_name] = self.bitline_meas_names[0:1]
|
name_dict[self.bl_meas_name] = self.bitline_meas_names[0:1]
|
||||||
name_dict[self.power_name] = self.power_meas_names
|
name_dict[self.power_name] = self.power_meas_names
|
||||||
#name_dict[self.wl_slew_name] = self.wl_slew_meas_names
|
#name_dict[self.wl_slew_name] = self.wl_slew_meas_names
|
||||||
|
|
||||||
if not OPTS.use_tech_delay_chain_size:
|
if not OPTS.use_tech_delay_chain_size:
|
||||||
name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured.
|
name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured.
|
||||||
name_dict[self.sae_model_name] = name_dict["sae_measures"]
|
name_dict[self.sae_model_name] = name_dict["sae_measures"]
|
||||||
|
|
||||||
return name_dict
|
return name_dict
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,23 +27,22 @@ class setup_hold():
|
||||||
self.model_location = OPTS.openram_tech + "sp_lib/dff.sp"
|
self.model_location = OPTS.openram_tech + "sp_lib/dff.sp"
|
||||||
self.period = tech.spice["feasible_period"]
|
self.period = tech.spice["feasible_period"]
|
||||||
|
|
||||||
debug.info(2,"Feasible period from technology file: {0} ".format(self.period))
|
debug.info(2, "Feasible period from technology file: {0} ".format(self.period))
|
||||||
|
|
||||||
self.set_corner(corner)
|
self.set_corner(corner)
|
||||||
|
|
||||||
|
def set_corner(self, corner):
|
||||||
def set_corner(self,corner):
|
|
||||||
""" Set the corner values """
|
""" Set the corner values """
|
||||||
self.corner = corner
|
self.corner = corner
|
||||||
(self.process, self.vdd_voltage, self.temperature) = corner
|
(self.process, self.vdd_voltage, self.temperature) = corner
|
||||||
self.gnd_voltage = 0
|
self.gnd_voltage = 0
|
||||||
|
|
||||||
|
|
||||||
def write_stimulus(self, mode, target_time, correct_value):
|
def write_stimulus(self, mode, target_time, correct_value):
|
||||||
"""Creates a stimulus file for SRAM setup/hold time calculation"""
|
"""Creates a stimulus file for SRAM setup/hold time calculation"""
|
||||||
|
|
||||||
# creates and opens the stimulus file for writing
|
# creates and opens the stimulus file for writing
|
||||||
temp_stim = OPTS.openram_temp + "stim.sp"
|
self.stim_sp = "sh_stim.sp"
|
||||||
|
temp_stim = OPTS.openram_temp + self.stim_sp
|
||||||
self.sf = open(temp_stim, "w")
|
self.sf = open(temp_stim, "w")
|
||||||
self.stim = stimuli(self.sf, self.corner)
|
self.stim = stimuli(self.sf, self.corner)
|
||||||
|
|
||||||
|
|
@ -60,11 +59,10 @@ class setup_hold():
|
||||||
|
|
||||||
self.write_clock()
|
self.write_clock()
|
||||||
|
|
||||||
self.write_measures(mode=mode,
|
self.write_measures(mode=mode,
|
||||||
correct_value=correct_value)
|
correct_value=correct_value)
|
||||||
|
|
||||||
|
|
||||||
self.stim.write_control(4*self.period)
|
self.stim.write_control(4 * self.period)
|
||||||
|
|
||||||
self.sf.close()
|
self.sf.close()
|
||||||
|
|
||||||
|
|
@ -79,7 +77,6 @@ class setup_hold():
|
||||||
self.sf.write("\n* Global Power Supplies\n")
|
self.sf.write("\n* Global Power Supplies\n")
|
||||||
self.stim.write_supply()
|
self.stim.write_supply()
|
||||||
|
|
||||||
|
|
||||||
def write_data(self, mode, target_time, correct_value):
|
def write_data(self, mode, target_time, correct_value):
|
||||||
"""Create the data signals for setup/hold analysis. First period is to
|
"""Create the data signals for setup/hold analysis. First period is to
|
||||||
initialize it to the opposite polarity. Second period is used for
|
initialize it to the opposite polarity. Second period is used for
|
||||||
|
|
@ -102,24 +99,22 @@ class setup_hold():
|
||||||
data_values=[init_value, start_value, end_value],
|
data_values=[init_value, start_value, end_value],
|
||||||
period=target_time,
|
period=target_time,
|
||||||
slew=self.constrained_input_slew,
|
slew=self.constrained_input_slew,
|
||||||
setup=0)
|
setup=0)
|
||||||
|
|
||||||
def write_clock(self):
|
def write_clock(self):
|
||||||
""" Create the clock signal for setup/hold analysis. First period initializes the FF
|
""" Create the clock signal for setup/hold analysis. First period initializes the FF
|
||||||
while the second is used for characterization."""
|
while the second is used for characterization."""
|
||||||
|
|
||||||
self.stim.gen_pwl(sig_name="clk",
|
self.stim.gen_pwl(sig_name="clk",
|
||||||
# initial clk edge is right after the 0 time to initialize a flop
|
# initial clk edge is right after the 0 time to initialize a flop
|
||||||
# without using .IC on an internal node.
|
# without using .IC on an internal node.
|
||||||
# Return input to value after one period.
|
# Return input to value after one period.
|
||||||
# The second pulse is the characterization one at 2*period
|
# The second pulse is the characterization one at 2*period
|
||||||
clk_times=[0, 0.1*self.period,self.period,2*self.period],
|
clk_times=[0, 0.1 * self.period, self.period, 2 * self.period],
|
||||||
data_values=[0, 1, 0, 1],
|
data_values=[0, 1, 0, 1],
|
||||||
period=2*self.period,
|
period=2 * self.period,
|
||||||
slew=self.constrained_input_slew,
|
slew=self.constrained_input_slew,
|
||||||
setup=0)
|
setup=0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def write_measures(self, mode, correct_value):
|
def write_measures(self, mode, correct_value):
|
||||||
""" Measure statements for setup/hold with right phases. """
|
""" Measure statements for setup/hold with right phases. """
|
||||||
|
|
@ -139,7 +134,6 @@ class setup_hold():
|
||||||
else:
|
else:
|
||||||
din_rise_or_fall = "RISE"
|
din_rise_or_fall = "RISE"
|
||||||
|
|
||||||
|
|
||||||
self.sf.write("\n* Measure statements for pass/fail verification\n")
|
self.sf.write("\n* Measure statements for pass/fail verification\n")
|
||||||
trig_name = "clk"
|
trig_name = "clk"
|
||||||
targ_name = "dout"
|
targ_name = "dout"
|
||||||
|
|
@ -152,9 +146,9 @@ class setup_hold():
|
||||||
targ_val=targ_val,
|
targ_val=targ_val,
|
||||||
trig_dir="RISE",
|
trig_dir="RISE",
|
||||||
targ_dir=dout_rise_or_fall,
|
targ_dir=dout_rise_or_fall,
|
||||||
trig_td=1.9*self.period,
|
trig_td=1.9 * self.period,
|
||||||
targ_td=1.9*self.period)
|
targ_td=1.9 * self.period)
|
||||||
|
|
||||||
targ_name = "data"
|
targ_name = "data"
|
||||||
# Start triggers right after initialize value is returned to normal
|
# Start triggers right after initialize value is returned to normal
|
||||||
# at one period
|
# at one period
|
||||||
|
|
@ -165,16 +159,13 @@ class setup_hold():
|
||||||
targ_val=targ_val,
|
targ_val=targ_val,
|
||||||
trig_dir="RISE",
|
trig_dir="RISE",
|
||||||
targ_dir=din_rise_or_fall,
|
targ_dir=din_rise_or_fall,
|
||||||
trig_td=1.2*self.period,
|
trig_td=1.2 * self.period,
|
||||||
targ_td=1.2*self.period)
|
targ_td=1.2 * self.period)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def bidir_search(self, correct_value, mode):
|
def bidir_search(self, correct_value, mode):
|
||||||
""" This will perform a bidirectional search for either setup or hold times.
|
""" This will perform a bidirectional search for either setup or hold times.
|
||||||
It starts with the feasible priod and looks a half period beyond or before it
|
It starts with the feasible priod and looks a half period beyond or before it
|
||||||
depending on whether we are doing setup or hold.
|
depending on whether we are doing setup or hold.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# NOTE: The feasible bound is always feasible. This is why they are different for setup and hold.
|
# NOTE: The feasible bound is always feasible. This is why they are different for setup and hold.
|
||||||
|
|
@ -182,86 +173,85 @@ class setup_hold():
|
||||||
# this time. They are also unbalanced so that the average won't be right on the clock edge in the
|
# this time. They are also unbalanced so that the average won't be right on the clock edge in the
|
||||||
# first iteration.
|
# first iteration.
|
||||||
if mode == "SETUP":
|
if mode == "SETUP":
|
||||||
feasible_bound = 1.25*self.period
|
feasible_bound = 1.25 * self.period
|
||||||
infeasible_bound = 2.5*self.period
|
infeasible_bound = 2.5 * self.period
|
||||||
else:
|
else:
|
||||||
infeasible_bound = 1.5*self.period
|
infeasible_bound = 1.5 * self.period
|
||||||
feasible_bound = 2.75*self.period
|
feasible_bound = 2.75 * self.period
|
||||||
|
|
||||||
# Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search!
|
# Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search!
|
||||||
self.write_stimulus(mode=mode,
|
self.write_stimulus(mode=mode,
|
||||||
target_time=feasible_bound,
|
target_time=feasible_bound,
|
||||||
correct_value=correct_value)
|
correct_value=correct_value)
|
||||||
self.stim.run_sim()
|
self.stim.run_sim(self.stim_sp)
|
||||||
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
||||||
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
|
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
|
||||||
debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time))
|
debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time))
|
||||||
|
|
||||||
if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float:
|
if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float:
|
||||||
debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,ideal_clk_to_q,setuphold_time),2)
|
debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,
|
||||||
|
ideal_clk_to_q,
|
||||||
|
setuphold_time),
|
||||||
|
2)
|
||||||
|
|
||||||
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
||||||
setuphold_time *= -1e9
|
setuphold_time *= -1e9
|
||||||
else:
|
else:
|
||||||
setuphold_time *= 1e9
|
setuphold_time *= 1e9
|
||||||
|
|
||||||
passing_setuphold_time = setuphold_time
|
passing_setuphold_time = setuphold_time
|
||||||
debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
|
debug.info(2, "Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
|
||||||
setuphold_time,
|
setuphold_time,
|
||||||
feasible_bound,
|
feasible_bound,
|
||||||
2*self.period))
|
2 * self.period))
|
||||||
#raw_input("Press Enter to continue...")
|
#raw_input("Press Enter to continue...")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
target_time = (feasible_bound + infeasible_bound)/2
|
target_time = (feasible_bound + infeasible_bound) / 2
|
||||||
self.write_stimulus(mode=mode,
|
self.write_stimulus(mode=mode,
|
||||||
target_time=target_time,
|
target_time=target_time,
|
||||||
correct_value=correct_value)
|
correct_value=correct_value)
|
||||||
|
|
||||||
debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
|
debug.info(2, "{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
|
||||||
correct_value,
|
correct_value,
|
||||||
target_time,
|
target_time,
|
||||||
infeasible_bound,
|
infeasible_bound,
|
||||||
feasible_bound))
|
feasible_bound))
|
||||||
|
|
||||||
|
self.stim.run_sim(self.stim_sp)
|
||||||
self.stim.run_sim()
|
|
||||||
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
||||||
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
|
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
|
||||||
if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float:
|
if type(clk_to_q) == float and (clk_to_q < 1.1 * ideal_clk_to_q) and type(setuphold_time)==float:
|
||||||
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
||||||
setuphold_time *= -1e9
|
setuphold_time *= -1e9
|
||||||
else:
|
else:
|
||||||
setuphold_time *= 1e9
|
setuphold_time *= 1e9
|
||||||
|
|
||||||
debug.info(2,"PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time))
|
debug.info(2, "PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
|
||||||
passing_setuphold_time = setuphold_time
|
passing_setuphold_time = setuphold_time
|
||||||
feasible_bound = target_time
|
feasible_bound = target_time
|
||||||
else:
|
else:
|
||||||
debug.info(2,"FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time))
|
debug.info(2, "FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
|
||||||
infeasible_bound = target_time
|
infeasible_bound = target_time
|
||||||
|
|
||||||
#raw_input("Press Enter to continue...")
|
|
||||||
if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001):
|
if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001):
|
||||||
debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound))
|
debug.info(3, "CONVERGE {0} vs {1}".format(feasible_bound, infeasible_bound))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time))
|
|
||||||
|
debug.info(2, "Converged on {0} time {1}.".format(mode, passing_setuphold_time))
|
||||||
return passing_setuphold_time
|
return passing_setuphold_time
|
||||||
|
|
||||||
|
|
||||||
def setup_LH_time(self):
|
def setup_LH_time(self):
|
||||||
"""Calculates the setup time for low-to-high transition for a DFF
|
"""Calculates the setup time for low-to-high transition for a DFF
|
||||||
"""
|
"""
|
||||||
return self.bidir_search(1, "SETUP")
|
return self.bidir_search(1, "SETUP")
|
||||||
|
|
||||||
|
|
||||||
def setup_HL_time(self):
|
def setup_HL_time(self):
|
||||||
"""Calculates the setup time for high-to-low transition for a DFF
|
"""Calculates the setup time for high-to-low transition for a DFF
|
||||||
"""
|
"""
|
||||||
return self.bidir_search(0, "SETUP")
|
return self.bidir_search(0, "SETUP")
|
||||||
|
|
||||||
def hold_LH_time(self):
|
def hold_LH_time(self):
|
||||||
"""Calculates the hold time for low-to-high transition for a DFF
|
"""Calculates the hold time for low-to-high transition for a DFF
|
||||||
"""
|
"""
|
||||||
|
|
@ -272,7 +262,6 @@ class setup_hold():
|
||||||
"""
|
"""
|
||||||
return self.bidir_search(0, "HOLD")
|
return self.bidir_search(0, "HOLD")
|
||||||
|
|
||||||
|
|
||||||
def analyze(self, related_slews, constrained_slews):
|
def analyze(self, related_slews, constrained_slews):
|
||||||
"""main function to calculate both setup and hold time for the
|
"""main function to calculate both setup and hold time for the
|
||||||
DFF and returns a dictionary that contains 4 lists for both
|
DFF and returns a dictionary that contains 4 lists for both
|
||||||
|
|
@ -283,7 +272,7 @@ class setup_hold():
|
||||||
HL_setup = []
|
HL_setup = []
|
||||||
LH_hold = []
|
LH_hold = []
|
||||||
HL_hold = []
|
HL_hold = []
|
||||||
|
|
||||||
#For debugging, skips characterization and returns dummy values.
|
#For debugging, skips characterization and returns dummy values.
|
||||||
# i = 1.0
|
# i = 1.0
|
||||||
# for self.related_input_slew in related_slews:
|
# for self.related_input_slew in related_slews:
|
||||||
|
|
@ -293,18 +282,18 @@ class setup_hold():
|
||||||
# LH_hold.append(i+2.0)
|
# LH_hold.append(i+2.0)
|
||||||
# HL_hold.append(i+3.0)
|
# HL_hold.append(i+3.0)
|
||||||
# i+=4.0
|
# i+=4.0
|
||||||
|
|
||||||
# times = {"setup_times_LH": LH_setup,
|
# times = {"setup_times_LH": LH_setup,
|
||||||
# "setup_times_HL": HL_setup,
|
# "setup_times_HL": HL_setup,
|
||||||
# "hold_times_LH": LH_hold,
|
# "hold_times_LH": LH_hold,
|
||||||
# "hold_times_HL": HL_hold
|
# "hold_times_HL": HL_hold
|
||||||
# }
|
# }
|
||||||
# return times
|
# return times
|
||||||
|
|
||||||
|
|
||||||
for self.related_input_slew in related_slews:
|
for self.related_input_slew in related_slews:
|
||||||
for self.constrained_input_slew in constrained_slews:
|
for self.constrained_input_slew in constrained_slews:
|
||||||
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew))
|
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,
|
||||||
|
self.constrained_input_slew))
|
||||||
LH_setup_time = self.setup_LH_time()
|
LH_setup_time = self.setup_LH_time()
|
||||||
debug.info(1, " Setup Time for low_to_high transition: {0}".format(LH_setup_time))
|
debug.info(1, " Setup Time for low_to_high transition: {0}".format(LH_setup_time))
|
||||||
HL_setup_time = self.setup_HL_time()
|
HL_setup_time = self.setup_HL_time()
|
||||||
|
|
@ -317,7 +306,7 @@ class setup_hold():
|
||||||
HL_setup.append(HL_setup_time)
|
HL_setup.append(HL_setup_time)
|
||||||
LH_hold.append(LH_hold_time)
|
LH_hold.append(LH_hold_time)
|
||||||
HL_hold.append(HL_hold_time)
|
HL_hold.append(HL_hold_time)
|
||||||
|
|
||||||
times = {"setup_times_LH": LH_setup,
|
times = {"setup_times_LH": LH_setup,
|
||||||
"setup_times_HL": HL_setup,
|
"setup_times_HL": HL_setup,
|
||||||
"hold_times_LH": LH_hold,
|
"hold_times_LH": LH_hold,
|
||||||
|
|
@ -325,22 +314,22 @@ class setup_hold():
|
||||||
}
|
}
|
||||||
return times
|
return times
|
||||||
|
|
||||||
def analytical_setuphold(self,related_slews, constrained_slews):
|
def analytical_setuphold(self, related_slews, constrained_slews):
|
||||||
""" Just return the fixed setup/hold times from the technology.
|
""" Just return the fixed setup/hold times from the technology.
|
||||||
"""
|
"""
|
||||||
LH_setup = []
|
LH_setup = []
|
||||||
HL_setup = []
|
HL_setup = []
|
||||||
LH_hold = []
|
LH_hold = []
|
||||||
HL_hold = []
|
HL_hold = []
|
||||||
|
|
||||||
for self.related_input_slew in related_slews:
|
for self.related_input_slew in related_slews:
|
||||||
for self.constrained_input_slew in constrained_slews:
|
for self.constrained_input_slew in constrained_slews:
|
||||||
# convert from ps to ns
|
# convert from ps to ns
|
||||||
LH_setup.append(tech.spice["dff_setup"]/1e3)
|
LH_setup.append(tech.spice["dff_setup"] / 1e3)
|
||||||
HL_setup.append(tech.spice["dff_setup"]/1e3)
|
HL_setup.append(tech.spice["dff_setup"] / 1e3)
|
||||||
LH_hold.append(tech.spice["dff_hold"]/1e3)
|
LH_hold.append(tech.spice["dff_hold"] / 1e3)
|
||||||
HL_hold.append(tech.spice["dff_hold"]/1e3)
|
HL_hold.append(tech.spice["dff_hold"] / 1e3)
|
||||||
|
|
||||||
times = {"setup_times_LH": LH_setup,
|
times = {"setup_times_LH": LH_setup,
|
||||||
"setup_times_HL": HL_setup,
|
"setup_times_HL": HL_setup,
|
||||||
"hold_times_LH": LH_hold,
|
"hold_times_LH": LH_hold,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class simulation():
|
||||||
|
|
||||||
def __init__(self, sram, spfile, corner):
|
def __init__(self, sram, spfile, corner):
|
||||||
self.sram = sram
|
self.sram = sram
|
||||||
|
|
||||||
self.name = self.sram.name
|
self.name = self.sram.name
|
||||||
self.word_size = self.sram.word_size
|
self.word_size = self.sram.word_size
|
||||||
self.addr_size = self.sram.addr_size
|
self.addr_size = self.sram.addr_size
|
||||||
|
|
@ -28,7 +28,7 @@ class simulation():
|
||||||
else:
|
else:
|
||||||
self.num_spare_cols = self.sram.num_spare_cols
|
self.num_spare_cols = self.sram.num_spare_cols
|
||||||
self.sp_file = spfile
|
self.sp_file = spfile
|
||||||
|
|
||||||
self.all_ports = self.sram.all_ports
|
self.all_ports = self.sram.all_ports
|
||||||
self.readwrite_ports = self.sram.readwrite_ports
|
self.readwrite_ports = self.sram.readwrite_ports
|
||||||
self.read_ports = self.sram.read_ports
|
self.read_ports = self.sram.read_ports
|
||||||
|
|
@ -53,7 +53,7 @@ class simulation():
|
||||||
self.v_high = self.vdd_voltage - tech.spice["nom_threshold"]
|
self.v_high = self.vdd_voltage - tech.spice["nom_threshold"]
|
||||||
self.v_low = tech.spice["nom_threshold"]
|
self.v_low = tech.spice["nom_threshold"]
|
||||||
self.gnd_voltage = 0
|
self.gnd_voltage = 0
|
||||||
|
|
||||||
def create_signal_names(self):
|
def create_signal_names(self):
|
||||||
self.addr_name = "a"
|
self.addr_name = "a"
|
||||||
self.din_name = "din"
|
self.din_name = "din"
|
||||||
|
|
@ -66,12 +66,12 @@ class simulation():
|
||||||
"Number of pins generated for characterization \
|
"Number of pins generated for characterization \
|
||||||
do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,
|
do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,
|
||||||
self.pins))
|
self.pins))
|
||||||
|
|
||||||
def set_stimulus_variables(self):
|
def set_stimulus_variables(self):
|
||||||
# Clock signals
|
# Clock signals
|
||||||
self.cycle_times = []
|
self.cycle_times = []
|
||||||
self.t_current = 0
|
self.t_current = 0
|
||||||
|
|
||||||
# control signals: only one cs_b for entire multiported sram, one we_b for each write port
|
# control signals: only one cs_b for entire multiported sram, one we_b for each write port
|
||||||
self.csb_values = {port: [] for port in self.all_ports}
|
self.csb_values = {port: [] for port in self.all_ports}
|
||||||
self.web_values = {port: [] for port in self.readwrite_ports}
|
self.web_values = {port: [] for port in self.readwrite_ports}
|
||||||
|
|
@ -81,13 +81,13 @@ class simulation():
|
||||||
self.data_value = {port: [] for port in self.write_ports}
|
self.data_value = {port: [] for port in self.write_ports}
|
||||||
self.wmask_value = {port: [] for port in self.write_ports}
|
self.wmask_value = {port: [] for port in self.write_ports}
|
||||||
self.spare_wen_value = {port: [] for port in self.write_ports}
|
self.spare_wen_value = {port: [] for port in self.write_ports}
|
||||||
|
|
||||||
# Three dimensional list to handle each addr and data bits for each port over the number of checks
|
# Three dimensional list to handle each addr and data bits for each port over the number of checks
|
||||||
self.addr_values = {port: [[] for bit in range(self.addr_size)] for port in self.all_ports}
|
self.addr_values = {port: [[] for bit in range(self.addr_size)] for port in self.all_ports}
|
||||||
self.data_values = {port: [[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
|
self.data_values = {port: [[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
|
||||||
self.wmask_values = {port: [[] for bit in range(self.num_wmasks)] for port in self.write_ports}
|
self.wmask_values = {port: [[] for bit in range(self.num_wmasks)] for port in self.write_ports}
|
||||||
self.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
|
self.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
|
||||||
|
|
||||||
# For generating comments in SPICE stimulus
|
# For generating comments in SPICE stimulus
|
||||||
self.cycle_comments = []
|
self.cycle_comments = []
|
||||||
self.fn_cycle_comments = []
|
self.fn_cycle_comments = []
|
||||||
|
|
@ -104,13 +104,13 @@ class simulation():
|
||||||
web_val = 0
|
web_val = 0
|
||||||
elif op != "noop":
|
elif op != "noop":
|
||||||
debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port, op), 1)
|
debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port, op), 1)
|
||||||
|
|
||||||
# Append the values depending on the type of port
|
# Append the values depending on the type of port
|
||||||
self.csb_values[port].append(csb_val)
|
self.csb_values[port].append(csb_val)
|
||||||
# If port is in both lists, add rw control signal. Condition indicates its a RW port.
|
# If port is in both lists, add rw control signal. Condition indicates its a RW port.
|
||||||
if port in self.readwrite_ports:
|
if port in self.readwrite_ports:
|
||||||
self.web_values[port].append(web_val)
|
self.web_values[port].append(web_val)
|
||||||
|
|
||||||
def add_data(self, data, port):
|
def add_data(self, data, port):
|
||||||
""" Add the array of data values """
|
""" Add the array of data values """
|
||||||
debug.check(len(data)==(self.word_size + self.num_spare_cols), "Invalid data word size.")
|
debug.check(len(data)==(self.word_size + self.num_spare_cols), "Invalid data word size.")
|
||||||
|
|
@ -129,7 +129,7 @@ class simulation():
|
||||||
def add_address(self, address, port):
|
def add_address(self, address, port):
|
||||||
""" Add the array of address values """
|
""" Add the array of address values """
|
||||||
debug.check(len(address)==self.addr_size, "Invalid address size.")
|
debug.check(len(address)==self.addr_size, "Invalid address size.")
|
||||||
|
|
||||||
self.addr_value[port].append(address)
|
self.addr_value[port].append(address)
|
||||||
bit = self.addr_size - 1
|
bit = self.addr_size - 1
|
||||||
for c in address:
|
for c in address:
|
||||||
|
|
@ -170,7 +170,7 @@ class simulation():
|
||||||
else:
|
else:
|
||||||
debug.error("Non-binary spare enable signal string", 1)
|
debug.error("Non-binary spare enable signal string", 1)
|
||||||
bit -= 1
|
bit -= 1
|
||||||
|
|
||||||
def add_write(self, comment, address, data, wmask, port):
|
def add_write(self, comment, address, data, wmask, port):
|
||||||
""" Add the control values for a write cycle. """
|
""" Add the control values for a write cycle. """
|
||||||
debug.check(port in self.write_ports,
|
debug.check(port in self.write_ports,
|
||||||
|
|
@ -182,18 +182,18 @@ class simulation():
|
||||||
|
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
|
|
||||||
self.add_control_one_port(port, "write")
|
self.add_control_one_port(port, "write")
|
||||||
self.add_data(data, port)
|
self.add_data(data, port)
|
||||||
self.add_address(address, port)
|
self.add_address(address, port)
|
||||||
self.add_wmask(wmask, port)
|
self.add_wmask(wmask, port)
|
||||||
self.add_spare_wen("1" * self.num_spare_cols, port)
|
self.add_spare_wen("1" * self.num_spare_cols, port)
|
||||||
|
|
||||||
#Add noops to all other ports.
|
#Add noops to all other ports.
|
||||||
for unselected_port in self.all_ports:
|
for unselected_port in self.all_ports:
|
||||||
if unselected_port != port:
|
if unselected_port != port:
|
||||||
self.add_noop_one_port(unselected_port)
|
self.add_noop_one_port(unselected_port)
|
||||||
|
|
||||||
def add_read(self, comment, address, port):
|
def add_read(self, comment, address, port):
|
||||||
""" Add the control values for a read cycle. """
|
""" Add the control values for a read cycle. """
|
||||||
debug.check(port in self.read_ports,
|
debug.check(port in self.read_ports,
|
||||||
|
|
@ -206,8 +206,8 @@ class simulation():
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
self.add_control_one_port(port, "read")
|
self.add_control_one_port(port, "read")
|
||||||
self.add_address(address, port)
|
self.add_address(address, port)
|
||||||
|
|
||||||
# If the port is also a readwrite then add
|
# If the port is also a readwrite then add
|
||||||
# the same value as previous cycle
|
# the same value as previous cycle
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
|
|
@ -220,7 +220,7 @@ class simulation():
|
||||||
except:
|
except:
|
||||||
self.add_wmask("0" * self.num_wmasks, port)
|
self.add_wmask("0" * self.num_wmasks, port)
|
||||||
self.add_spare_wen("0" * self.num_spare_cols, port)
|
self.add_spare_wen("0" * self.num_spare_cols, port)
|
||||||
|
|
||||||
#Add noops to all other ports.
|
#Add noops to all other ports.
|
||||||
for unselected_port in self.all_ports:
|
for unselected_port in self.all_ports:
|
||||||
if unselected_port != port:
|
if unselected_port != port:
|
||||||
|
|
@ -234,7 +234,7 @@ class simulation():
|
||||||
|
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.add_noop_one_port(port)
|
self.add_noop_one_port(port)
|
||||||
|
|
||||||
|
|
@ -251,7 +251,7 @@ class simulation():
|
||||||
self.add_address(address, port)
|
self.add_address(address, port)
|
||||||
self.add_wmask(wmask, port)
|
self.add_wmask(wmask, port)
|
||||||
self.add_spare_wen("1" * self.num_spare_cols, port)
|
self.add_spare_wen("1" * self.num_spare_cols, port)
|
||||||
|
|
||||||
def add_read_one_port(self, comment, address, port):
|
def add_read_one_port(self, comment, address, port):
|
||||||
""" Add the control values for a read cycle. Does not increment the period. """
|
""" Add the control values for a read cycle. Does not increment the period. """
|
||||||
debug.check(port in self.read_ports,
|
debug.check(port in self.read_ports,
|
||||||
|
|
@ -259,7 +259,7 @@ class simulation():
|
||||||
self.read_ports))
|
self.read_ports))
|
||||||
debug.info(2, comment)
|
debug.info(2, comment)
|
||||||
self.fn_cycle_comments.append(comment)
|
self.fn_cycle_comments.append(comment)
|
||||||
|
|
||||||
self.add_control_one_port(port, "read")
|
self.add_control_one_port(port, "read")
|
||||||
self.add_address(address, port)
|
self.add_address(address, port)
|
||||||
|
|
||||||
|
|
@ -275,16 +275,16 @@ class simulation():
|
||||||
except:
|
except:
|
||||||
self.add_wmask("0" * self.num_wmasks, port)
|
self.add_wmask("0" * self.num_wmasks, port)
|
||||||
self.add_spare_wen("0" * self.num_spare_cols, port)
|
self.add_spare_wen("0" * self.num_spare_cols, port)
|
||||||
|
|
||||||
def add_noop_one_port(self, port):
|
def add_noop_one_port(self, port):
|
||||||
""" Add the control values for a noop to a single port. Does not increment the period. """
|
""" Add the control values for a noop to a single port. Does not increment the period. """
|
||||||
self.add_control_one_port(port, "noop")
|
self.add_control_one_port(port, "noop")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.add_address(self.addr_value[port][-1], port)
|
self.add_address(self.addr_value[port][-1], port)
|
||||||
except:
|
except:
|
||||||
self.add_address("0" * self.addr_size, port)
|
self.add_address("0" * self.addr_size, port)
|
||||||
|
|
||||||
# If the port is also a readwrite then add
|
# If the port is also a readwrite then add
|
||||||
# the same value as previous cycle
|
# the same value as previous cycle
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
|
|
@ -297,7 +297,7 @@ class simulation():
|
||||||
except:
|
except:
|
||||||
self.add_wmask("0" * self.num_wmasks, port)
|
self.add_wmask("0" * self.num_wmasks, port)
|
||||||
self.add_spare_wen("0" * self.num_spare_cols, port)
|
self.add_spare_wen("0" * self.num_spare_cols, port)
|
||||||
|
|
||||||
def add_noop_clock_one_port(self, port):
|
def add_noop_clock_one_port(self, port):
|
||||||
""" Add the control values for a noop to a single port. Increments the period. """
|
""" Add the control values for a noop to a single port. Increments the period. """
|
||||||
debug.info(2, 'Clock only on port {}'.format(port))
|
debug.info(2, 'Clock only on port {}'.format(port))
|
||||||
|
|
@ -324,7 +324,7 @@ class simulation():
|
||||||
time,
|
time,
|
||||||
time_spacing,
|
time_spacing,
|
||||||
comment))
|
comment))
|
||||||
|
|
||||||
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
|
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
|
||||||
if op == "noop":
|
if op == "noop":
|
||||||
str = "\tIdle during cycle {0} ({1}ns - {2}ns)"
|
str = "\tIdle during cycle {0} ({1}ns - {2}ns)"
|
||||||
|
|
@ -355,22 +355,22 @@ class simulation():
|
||||||
int(t_current / self.period),
|
int(t_current / self.period),
|
||||||
t_current,
|
t_current,
|
||||||
t_current + self.period)
|
t_current + self.period)
|
||||||
|
|
||||||
return comment
|
return comment
|
||||||
|
|
||||||
def gen_pin_names(self, port_signal_names, port_info, abits, dbits):
|
def gen_pin_names(self, port_signal_names, port_info, abits, dbits):
|
||||||
"""Creates the pins names of the SRAM based on the no. of ports."""
|
"""Creates the pins names of the SRAM based on the no. of ports."""
|
||||||
# This may seem redundant as the pin names are already defined in the sram. However, it is difficult
|
# This may seem redundant as the pin names are already defined in the sram. However, it is difficult
|
||||||
# to extract the functionality from the names, so they are recreated. As the order is static, changing
|
# to extract the functionality from the names, so they are recreated. As the order is static, changing
|
||||||
# the order of the pin names will cause issues here.
|
# the order of the pin names will cause issues here.
|
||||||
pin_names = []
|
pin_names = []
|
||||||
(addr_name, din_name, dout_name) = port_signal_names
|
(addr_name, din_name, dout_name) = port_signal_names
|
||||||
(total_ports, write_index, read_index) = port_info
|
(total_ports, write_index, read_index) = port_info
|
||||||
|
|
||||||
for write_input in write_index:
|
for write_input in write_index:
|
||||||
for i in range(dbits):
|
for i in range(dbits):
|
||||||
pin_names.append("{0}{1}_{2}".format(din_name, write_input, i))
|
pin_names.append("{0}{1}_{2}".format(din_name, write_input, i))
|
||||||
|
|
||||||
for port in range(total_ports):
|
for port in range(total_ports):
|
||||||
for i in range(abits):
|
for i in range(abits):
|
||||||
pin_names.append("{0}{1}_{2}".format(addr_name, port, i))
|
pin_names.append("{0}{1}_{2}".format(addr_name, port, i))
|
||||||
|
|
@ -389,25 +389,25 @@ class simulation():
|
||||||
for port in write_index:
|
for port in write_index:
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
pin_names.append("WMASK{0}_{1}".format(port, bit))
|
pin_names.append("WMASK{0}_{1}".format(port, bit))
|
||||||
|
|
||||||
if self.num_spare_cols:
|
if self.num_spare_cols:
|
||||||
for port in write_index:
|
for port in write_index:
|
||||||
for bit in range(self.num_spare_cols):
|
for bit in range(self.num_spare_cols):
|
||||||
pin_names.append("SPARE_WEN{0}_{1}".format(port, bit))
|
pin_names.append("SPARE_WEN{0}_{1}".format(port, bit))
|
||||||
|
|
||||||
for read_output in read_index:
|
for read_output in read_index:
|
||||||
for i in range(dbits):
|
for i in range(dbits):
|
||||||
pin_names.append("{0}{1}_{2}".format(dout_name, read_output, i))
|
pin_names.append("{0}{1}_{2}".format(dout_name, read_output, i))
|
||||||
|
|
||||||
pin_names.append("{0}".format("vdd"))
|
pin_names.append("{0}".format("vdd"))
|
||||||
pin_names.append("{0}".format("gnd"))
|
pin_names.append("{0}".format("gnd"))
|
||||||
return pin_names
|
return pin_names
|
||||||
|
|
||||||
def add_graph_exclusions(self):
|
def add_graph_exclusions(self):
|
||||||
"""
|
"""
|
||||||
Exclude portions of SRAM from timing graph which are not relevant
|
Exclude portions of SRAM from timing graph which are not relevant
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# other initializations can only be done during analysis when a bit has been selected
|
# other initializations can only be done during analysis when a bit has been selected
|
||||||
# for testing.
|
# for testing.
|
||||||
self.sram.bank.graph_exclude_precharge()
|
self.sram.bank.graph_exclude_precharge()
|
||||||
|
|
@ -415,29 +415,29 @@ class simulation():
|
||||||
self.sram.graph_exclude_data_dff()
|
self.sram.graph_exclude_data_dff()
|
||||||
self.sram.graph_exclude_ctrl_dffs()
|
self.sram.graph_exclude_ctrl_dffs()
|
||||||
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
|
self.sram.bank.bitcell_array.graph_exclude_replica_col_bits()
|
||||||
|
|
||||||
def set_internal_spice_names(self):
|
def set_internal_spice_names(self):
|
||||||
"""
|
"""
|
||||||
Sets important names for characterization such as Sense amp enable and internal bit nets.
|
Sets important names for characterization such as Sense amp enable and internal bit nets.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
port = self.read_ports[0]
|
port = self.read_ports[0]
|
||||||
if not OPTS.use_pex:
|
if not OPTS.use_pex:
|
||||||
self.graph.get_all_paths('{}{}'.format("clk", port),
|
self.graph.get_all_paths('{}{}'.format("clk", port),
|
||||||
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
||||||
|
|
||||||
sen_with_port = self.get_sen_name(self.graph.all_paths)
|
sen_with_port = self.get_sen_name(self.graph.all_paths)
|
||||||
if sen_with_port.endswith(str(port)):
|
if sen_with_port.endswith(str(port)):
|
||||||
self.sen_name = sen_with_port[:-len(str(port))]
|
self.sen_name = sen_with_port[:-len(str(port))]
|
||||||
else:
|
else:
|
||||||
self.sen_name = sen_with_port
|
self.sen_name = sen_with_port
|
||||||
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.")
|
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.")
|
||||||
|
|
||||||
debug.info(2, "s_en name = {}".format(self.sen_name))
|
debug.info(2, "s_en name = {}".format(self.sen_name))
|
||||||
|
|
||||||
bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port)
|
bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port)
|
||||||
port_pos = -1 - len(str(self.probe_data)) - len(str(port))
|
port_pos = -1 - len(str(self.probe_data)) - len(str(port))
|
||||||
|
|
||||||
if bl_name_port.endswith(str(port) + "_" + str(self.probe_data)):
|
if bl_name_port.endswith(str(port) + "_" + str(self.probe_data)):
|
||||||
self.bl_name = bl_name_port[:port_pos] + "{}" + bl_name_port[port_pos + len(str(port)):]
|
self.bl_name = bl_name_port[:port_pos] + "{}" + bl_name_port[port_pos + len(str(port)):]
|
||||||
elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
|
elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
|
||||||
|
|
@ -445,7 +445,7 @@ class simulation():
|
||||||
else:
|
else:
|
||||||
self.bl_name = bl_name_port
|
self.bl_name = bl_name_port
|
||||||
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
|
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
|
||||||
|
|
||||||
if br_name_port.endswith(str(port) + "_" + str(self.probe_data)):
|
if br_name_port.endswith(str(port) + "_" + str(self.probe_data)):
|
||||||
self.br_name = br_name_port[:port_pos] + "{}" + br_name_port[port_pos + len(str(port)):]
|
self.br_name = br_name_port[:port_pos] + "{}" + br_name_port[port_pos + len(str(port)):]
|
||||||
elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
|
elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
|
||||||
|
|
@ -457,22 +457,22 @@ class simulation():
|
||||||
else:
|
else:
|
||||||
self.graph.get_all_paths('{}{}'.format("clk", port),
|
self.graph.get_all_paths('{}{}'.format("clk", port),
|
||||||
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
||||||
|
|
||||||
self.sen_name = self.get_sen_name(self.graph.all_paths)
|
self.sen_name = self.get_sen_name(self.graph.all_paths)
|
||||||
debug.info(2, "s_en name = {}".format(self.sen_name))
|
debug.info(2, "s_en name = {}".format(self.sen_name))
|
||||||
|
|
||||||
self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1)
|
self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1)
|
||||||
self.br_name = "br{0}_{1}".format(port, OPTS.word_size - 1)
|
self.br_name = "br{0}_{1}".format(port, OPTS.word_size - 1)
|
||||||
debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name))
|
debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name))
|
||||||
|
|
||||||
def get_sen_name(self, paths, assumed_port=None):
|
def get_sen_name(self, paths, assumed_port=None):
|
||||||
"""
|
"""
|
||||||
Gets the signal name associated with the sense amp enable from input paths.
|
Gets the signal name associated with the sense amp enable from input paths.
|
||||||
Only expects a single path to contain the sen signal name.
|
Only expects a single path to contain the sen signal name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
sa_mods = factory.get_mods(OPTS.sense_amp)
|
sa_mods = factory.get_mods(OPTS.sense_amp)
|
||||||
# Any sense amp instantiated should be identical, any change to that
|
# Any sense amp instantiated should be identical, any change to that
|
||||||
# will require some identification to determine the mod desired.
|
# will require some identification to determine the mod desired.
|
||||||
debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.")
|
debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.")
|
||||||
enable_name = sa_mods[0].get_enable_name()
|
enable_name = sa_mods[0].get_enable_name()
|
||||||
|
|
@ -480,15 +480,15 @@ class simulation():
|
||||||
if OPTS.use_pex:
|
if OPTS.use_pex:
|
||||||
sen_name = sen_name.split('.')[-1]
|
sen_name = sen_name.split('.')[-1]
|
||||||
return sen_name
|
return sen_name
|
||||||
|
|
||||||
def create_graph(self):
|
def create_graph(self):
|
||||||
"""
|
"""
|
||||||
Creates timing graph to generate the timing paths for the SRAM output.
|
Creates timing graph to generate the timing paths for the SRAM output.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.sram.clear_exclude_bits() # Removes previous bit exclusions
|
self.sram.clear_exclude_bits() # Removes previous bit exclusions
|
||||||
self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column)
|
self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column)
|
||||||
|
|
||||||
# Generate new graph every analysis as edges might change depending on test bit
|
# Generate new graph every analysis as edges might change depending on test bit
|
||||||
self.graph = graph_util.timing_graph()
|
self.graph = graph_util.timing_graph()
|
||||||
self.sram_instance_name = "X{}".format(self.sram.name)
|
self.sram_instance_name = "X{}".format(self.sram.name)
|
||||||
|
|
@ -498,14 +498,14 @@ class simulation():
|
||||||
"""
|
"""
|
||||||
Gets the mods as a set which should be excluded while searching for name.
|
Gets the mods as a set which should be excluded while searching for name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Exclude the RBL as it contains bitcells which are not in the main bitcell array
|
# Exclude the RBL as it contains bitcells which are not in the main bitcell array
|
||||||
# so it makes the search awkward
|
# so it makes the search awkward
|
||||||
return set(factory.get_mods(OPTS.replica_bitline))
|
return set(factory.get_mods(OPTS.replica_bitline))
|
||||||
|
|
||||||
def get_alias_in_path(self, paths, internal_net, mod, exclusion_set=None):
|
def get_alias_in_path(self, paths, internal_net, mod, exclusion_set=None):
|
||||||
"""
|
"""
|
||||||
Finds a single alias for the internal_net in given paths.
|
Finds a single alias for the internal_net in given paths.
|
||||||
More or less hits cause an error
|
More or less hits cause an error
|
||||||
"""
|
"""
|
||||||
net_found = False
|
net_found = False
|
||||||
|
|
@ -520,7 +520,7 @@ class simulation():
|
||||||
net_found = True
|
net_found = True
|
||||||
if not net_found:
|
if not net_found:
|
||||||
debug.error("Could not find {} net in timing paths.".format(internal_net), 1)
|
debug.error("Could not find {} net in timing paths.".format(internal_net), 1)
|
||||||
|
|
||||||
return path_net_name
|
return path_net_name
|
||||||
|
|
||||||
def get_bl_name(self, paths, port):
|
def get_bl_name(self, paths, port):
|
||||||
|
|
@ -530,7 +530,7 @@ class simulation():
|
||||||
cell_mod = factory.create(module_type=OPTS.bitcell)
|
cell_mod = factory.create(module_type=OPTS.bitcell)
|
||||||
cell_bl = cell_mod.get_bl_name(port)
|
cell_bl = cell_mod.get_bl_name(port)
|
||||||
cell_br = cell_mod.get_br_name(port)
|
cell_br = cell_mod.get_br_name(port)
|
||||||
|
|
||||||
bl_names = []
|
bl_names = []
|
||||||
exclude_set = self.get_bl_name_search_exclusions()
|
exclude_set = self.get_bl_name_search_exclusions()
|
||||||
for int_net in [cell_bl, cell_br]:
|
for int_net in [cell_bl, cell_br]:
|
||||||
|
|
@ -540,5 +540,5 @@ class simulation():
|
||||||
bl_names[i] = bl_names[i].split('.')[-1]
|
bl_names[i] = bl_names[i].split('.')[-1]
|
||||||
return bl_names[0], bl_names[1]
|
return bl_names[0], bl_names[1]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,27 @@ class stimuli():
|
||||||
self.tx_length = tech.drc["minlength_channel"]
|
self.tx_length = tech.drc["minlength_channel"]
|
||||||
|
|
||||||
self.sf = stim_file
|
self.sf = stim_file
|
||||||
|
|
||||||
(self.process, self.voltage, self.temperature) = corner
|
(self.process, self.voltage, self.temperature) = corner
|
||||||
|
found = False
|
||||||
|
self.device_libraries = []
|
||||||
|
self.device_models = []
|
||||||
try:
|
try:
|
||||||
self.device_libraries = tech.spice["fet_libraries"][self.process]
|
self.device_libraries += tech.spice["fet_libraries"][self.process]
|
||||||
except:
|
found = True
|
||||||
self.device_models = tech.spice["fet_models"][self.process]
|
except KeyError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.device_models += tech.spice["fet_models"][self.process]
|
||||||
|
found = True
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
if not found:
|
||||||
|
debug.error("Must define either fet_libraries or fet_models.", -1)
|
||||||
|
|
||||||
def inst_model(self, pins, model_name):
|
def inst_model(self, pins, model_name):
|
||||||
""" Function to instantiate a generic model with a set of pins """
|
""" Function to instantiate a generic model with a set of pins """
|
||||||
|
|
||||||
if OPTS.use_pex:
|
if OPTS.use_pex:
|
||||||
self.inst_pex_model(pins, model_name)
|
self.inst_pex_model(pins, model_name)
|
||||||
else:
|
else:
|
||||||
|
|
@ -48,7 +59,7 @@ class stimuli():
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
self.sf.write("{0} ".format(pin))
|
self.sf.write("{0} ".format(pin))
|
||||||
self.sf.write("{0}\n".format(model_name))
|
self.sf.write("{0}\n".format(model_name))
|
||||||
|
|
||||||
def inst_pex_model(self, pins, model_name):
|
def inst_pex_model(self, pins, model_name):
|
||||||
self.sf.write("X{0} ".format(model_name))
|
self.sf.write("X{0} ".format(model_name))
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
|
|
@ -88,7 +99,7 @@ class stimuli():
|
||||||
def create_buffer(self, buffer_name, size=[1, 3], beta=2.5):
|
def create_buffer(self, buffer_name, size=[1, 3], beta=2.5):
|
||||||
"""
|
"""
|
||||||
Generates buffer for top level signals (only for sim
|
Generates buffer for top level signals (only for sim
|
||||||
purposes). Size is pair for PMOS, NMOS width multiple.
|
purposes). Size is pair for PMOS, NMOS width multiple.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
|
self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
|
||||||
|
|
@ -113,23 +124,23 @@ class stimuli():
|
||||||
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
|
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
|
||||||
|
|
||||||
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
|
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
|
||||||
"""
|
"""
|
||||||
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
|
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
|
||||||
from 50% to 50%.
|
from 50% to 50%.
|
||||||
"""
|
"""
|
||||||
self.sf.write("* PULSE: period={0}\n".format(period))
|
self.sf.write("* PULSE: period={0}\n".format(period))
|
||||||
pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n"
|
pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n"
|
||||||
self.sf.write(pulse_string.format(sig_name,
|
self.sf.write(pulse_string.format(sig_name,
|
||||||
v1,
|
v1,
|
||||||
v2,
|
v2,
|
||||||
offset,
|
offset,
|
||||||
t_rise,
|
t_rise,
|
||||||
t_fall,
|
t_fall,
|
||||||
0.5*period-0.5*t_rise-0.5*t_fall,
|
0.5*period-0.5*t_rise-0.5*t_fall,
|
||||||
period))
|
period))
|
||||||
|
|
||||||
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
|
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
|
||||||
"""
|
"""
|
||||||
Generate a PWL stimulus given a signal name and data values at each period.
|
Generate a PWL stimulus given a signal name and data values at each period.
|
||||||
Automatically creates slews and ensures each data occurs a setup before the clock
|
Automatically creates slews and ensures each data occurs a setup before the clock
|
||||||
edge. The first clk_time should be 0 and is the initial time that corresponds
|
edge. The first clk_time should be 0 and is the initial time that corresponds
|
||||||
|
|
@ -141,7 +152,7 @@ class stimuli():
|
||||||
str.format(len(clk_times),
|
str.format(len(clk_times),
|
||||||
len(data_values),
|
len(data_values),
|
||||||
sig_name))
|
sig_name))
|
||||||
|
|
||||||
# shift signal times earlier for setup time
|
# shift signal times earlier for setup time
|
||||||
times = np.array(clk_times) - setup * period
|
times = np.array(clk_times) - setup * period
|
||||||
values = np.array(data_values) * self.voltage
|
values = np.array(data_values) * self.voltage
|
||||||
|
|
@ -174,7 +185,7 @@ class stimuli():
|
||||||
return 1
|
return 1
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
||||||
|
|
||||||
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
|
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
|
||||||
""" Creates the .meas statement for the measurement of delay """
|
""" Creates the .meas statement for the measurement of delay """
|
||||||
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
|
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
|
||||||
|
|
@ -187,7 +198,7 @@ class stimuli():
|
||||||
targ_val,
|
targ_val,
|
||||||
targ_dir,
|
targ_dir,
|
||||||
targ_td))
|
targ_td))
|
||||||
|
|
||||||
def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td):
|
def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td):
|
||||||
""" Creates the .meas statement for the measurement of delay """
|
""" Creates the .meas statement for the measurement of delay """
|
||||||
measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n"
|
measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n"
|
||||||
|
|
@ -197,7 +208,7 @@ class stimuli():
|
||||||
trig_val,
|
trig_val,
|
||||||
trig_dir,
|
trig_dir,
|
||||||
trig_td))
|
trig_td))
|
||||||
|
|
||||||
def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at):
|
def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at):
|
||||||
""" Creates the .meas statement for voltage at time"""
|
""" Creates the .meas statement for voltage at time"""
|
||||||
measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n"
|
measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n"
|
||||||
|
|
@ -216,15 +227,15 @@ class stimuli():
|
||||||
power_exp,
|
power_exp,
|
||||||
t_initial,
|
t_initial,
|
||||||
t_final))
|
t_final))
|
||||||
|
|
||||||
def gen_meas_value(self, meas_name, dout, t_intital, t_final):
|
def gen_meas_value(self, meas_name, dout, t_intital, t_final):
|
||||||
measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final)
|
measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final)
|
||||||
self.sf.write(measure_string)
|
self.sf.write(measure_string)
|
||||||
|
|
||||||
def write_control(self, end_time, runlvl=4):
|
def write_control(self, end_time, runlvl=4):
|
||||||
""" Write the control cards to run and end the simulation """
|
""" Write the control cards to run and end the simulation """
|
||||||
|
|
||||||
# These are guesses...
|
# These are guesses...
|
||||||
if runlvl==1:
|
if runlvl==1:
|
||||||
reltol = 0.02 # 2%
|
reltol = 0.02 # 2%
|
||||||
elif runlvl==2:
|
elif runlvl==2:
|
||||||
|
|
@ -234,7 +245,7 @@ class stimuli():
|
||||||
else:
|
else:
|
||||||
reltol = 0.001 # 0.1%
|
reltol = 0.001 # 0.1%
|
||||||
timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
|
timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
|
||||||
|
|
||||||
# UIC is needed for ngspice to converge
|
# UIC is needed for ngspice to converge
|
||||||
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
|
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
|
||||||
self.sf.write(".TEMP {}\n".format(self.temperature))
|
self.sf.write(".TEMP {}\n".format(self.temperature))
|
||||||
|
|
@ -249,7 +260,7 @@ class stimuli():
|
||||||
|
|
||||||
# create plots for all signals
|
# create plots for all signals
|
||||||
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
||||||
if OPTS.debug_level>0:
|
if OPTS.verbose_level>0:
|
||||||
if OPTS.spice_name in ["hspice", "xa"]:
|
if OPTS.spice_name in ["hspice", "xa"]:
|
||||||
self.sf.write(".probe V(*)\n")
|
self.sf.write(".probe V(*)\n")
|
||||||
else:
|
else:
|
||||||
|
|
@ -265,21 +276,16 @@ class stimuli():
|
||||||
"""Writes include statements, inputs are lists of model files"""
|
"""Writes include statements, inputs are lists of model files"""
|
||||||
|
|
||||||
self.sf.write("* {} process corner\n".format(self.process))
|
self.sf.write("* {} process corner\n".format(self.process))
|
||||||
if OPTS.tech_name == "sky130":
|
for item in self.device_libraries:
|
||||||
for item in self.device_libraries:
|
if os.path.isfile(item[0]):
|
||||||
if os.path.isfile(item[0]):
|
self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1]))
|
||||||
self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1]))
|
|
||||||
else:
|
|
||||||
debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]))
|
|
||||||
includes = [circuit]
|
|
||||||
else:
|
|
||||||
includes = self.device_models + [circuit]
|
|
||||||
|
|
||||||
for item in list(includes):
|
|
||||||
if os.path.isfile(item):
|
|
||||||
self.sf.write(".include \"{0}\"\n".format(item))
|
|
||||||
else:
|
else:
|
||||||
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
|
debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]))
|
||||||
|
|
||||||
|
includes = self.device_models + [circuit]
|
||||||
|
|
||||||
|
for item in list(includes):
|
||||||
|
self.sf.write(".include \"{0}\"\n".format(item))
|
||||||
|
|
||||||
def write_supply(self):
|
def write_supply(self):
|
||||||
""" Writes supply voltage statements """
|
""" Writes supply voltage statements """
|
||||||
|
|
@ -290,13 +296,13 @@ class stimuli():
|
||||||
self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n")
|
self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n")
|
||||||
self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0))
|
self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0))
|
||||||
|
|
||||||
def run_sim(self):
|
def run_sim(self, name):
|
||||||
""" Run hspice in batch mode and output rawfile to parse. """
|
""" Run hspice in batch mode and output rawfile to parse. """
|
||||||
temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
|
temp_stim = "{0}{1}".format(OPTS.openram_temp, name)
|
||||||
import datetime
|
import datetime
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")
|
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")
|
||||||
|
|
||||||
if OPTS.spice_name == "xa":
|
if OPTS.spice_name == "xa":
|
||||||
# Output the xa configurations here. FIXME: Move this to write it once.
|
# Output the xa configurations here. FIXME: Move this to write it once.
|
||||||
xa_cfg = open("{}xa.cfg".format(OPTS.openram_temp), "w")
|
xa_cfg = open("{}xa.cfg".format(OPTS.openram_temp), "w")
|
||||||
|
|
@ -331,13 +337,13 @@ class stimuli():
|
||||||
|
|
||||||
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
|
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
|
||||||
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
|
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
|
||||||
|
|
||||||
debug.info(3, cmd)
|
debug.info(3, cmd)
|
||||||
retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
|
retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
|
||||||
|
|
||||||
spice_stdout.close()
|
spice_stdout.close()
|
||||||
spice_stderr.close()
|
spice_stderr.close()
|
||||||
|
|
||||||
if (retcode > valid_retcode):
|
if (retcode > valid_retcode):
|
||||||
debug.error("Spice simulation error: " + cmd, -1)
|
debug.error("Spice simulation error: " + cmd, -1)
|
||||||
else:
|
else:
|
||||||
|
|
@ -345,4 +351,4 @@ class stimuli():
|
||||||
delta_time = round((end_time - start_time).total_seconds(), 1)
|
delta_time = round((end_time - start_time).total_seconds(), 1)
|
||||||
debug.info(2, "*** Spice: {} seconds".format(delta_time))
|
debug.info(2, "*** Spice: {} seconds".format(delta_time))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,33 +11,33 @@ import re
|
||||||
|
|
||||||
class trim_spice():
|
class trim_spice():
|
||||||
"""
|
"""
|
||||||
A utility to trim redundant parts of an SRAM spice netlist.
|
A utility to trim redundant parts of an SRAM spice netlist.
|
||||||
Input is an SRAM spice file. Output is an equivalent netlist
|
Input is an SRAM spice file. Output is an equivalent netlist
|
||||||
that works for a single address and range of data bits.
|
that works for a single address and range of data bits.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, spfile, reduced_spfile):
|
def __init__(self, spfile, reduced_spfile):
|
||||||
self.sp_file = spfile
|
self.sp_file = spfile
|
||||||
self.reduced_spfile = reduced_spfile
|
self.reduced_spfile = reduced_spfile
|
||||||
|
|
||||||
debug.info(1,"Trimming non-critical cells to speed-up characterization: {}.".format(reduced_spfile))
|
debug.info(1,"Trimming non-critical cells to speed-up characterization: {}.".format(reduced_spfile))
|
||||||
|
|
||||||
# Load the file into a buffer for performance
|
# Load the file into a buffer for performance
|
||||||
sp = open(self.sp_file, "r")
|
sp = open(self.sp_file, "r")
|
||||||
self.spice = sp.readlines()
|
self.spice = sp.readlines()
|
||||||
sp.close()
|
sp.close()
|
||||||
for i in range(len(self.spice)):
|
for i in range(len(self.spice)):
|
||||||
self.spice[i] = self.spice[i].rstrip(" \n")
|
self.spice[i] = self.spice[i].rstrip(" \n")
|
||||||
|
|
||||||
|
|
||||||
self.sp_buffer = self.spice
|
self.sp_buffer = self.spice
|
||||||
|
|
||||||
def set_configuration(self, banks, rows, columns, word_size):
|
def set_configuration(self, banks, rows, columns, word_size):
|
||||||
""" Set the configuration of SRAM sizes that we are simulating.
|
""" Set the configuration of SRAM sizes that we are simulating.
|
||||||
Need the: number of banks, number of rows in each bank, number of
|
Need the: number of banks, number of rows in each bank, number of
|
||||||
columns in each bank, and data word size."""
|
columns in each bank, and data word size."""
|
||||||
self.num_banks = banks
|
self.num_banks = banks
|
||||||
self.num_rows = rows
|
self.num_rows = rows
|
||||||
self.num_columns = columns
|
self.num_columns = columns
|
||||||
self.word_size = word_size
|
self.word_size = word_size
|
||||||
|
|
||||||
|
|
@ -80,8 +80,8 @@ class trim_spice():
|
||||||
debug.info(1,wl_msg)
|
debug.info(1,wl_msg)
|
||||||
self.sp_buffer.insert(0, "* It should NOT be used for LVS!!")
|
self.sp_buffer.insert(0, "* It should NOT be used for LVS!!")
|
||||||
self.sp_buffer.insert(0, "* WARNING: This is a TRIMMED NETLIST.")
|
self.sp_buffer.insert(0, "* WARNING: This is a TRIMMED NETLIST.")
|
||||||
|
|
||||||
|
|
||||||
wl_regex = r"wl\d*_{}".format(wl_address)
|
wl_regex = r"wl\d*_{}".format(wl_address)
|
||||||
bl_regex = r"bl\d*_{}".format(int(self.words_per_row*data_bit + col_address))
|
bl_regex = r"bl\d*_{}".format(int(self.words_per_row*data_bit + col_address))
|
||||||
self.remove_insts("bitcell_array",[wl_regex,bl_regex])
|
self.remove_insts("bitcell_array",[wl_regex,bl_regex])
|
||||||
|
|
@ -92,7 +92,7 @@ class trim_spice():
|
||||||
|
|
||||||
# 3. Keep column muxes basd on BL
|
# 3. Keep column muxes basd on BL
|
||||||
self.remove_insts("column_mux_array",[bl_regex])
|
self.remove_insts("column_mux_array",[bl_regex])
|
||||||
|
|
||||||
# 4. Keep write driver based on DATA
|
# 4. Keep write driver based on DATA
|
||||||
data_regex = r"data_{}".format(data_bit)
|
data_regex = r"data_{}".format(data_bit)
|
||||||
self.remove_insts("write_driver_array",[data_regex])
|
self.remove_insts("write_driver_array",[data_regex])
|
||||||
|
|
@ -100,18 +100,18 @@ class trim_spice():
|
||||||
# 5. Keep wordline driver based on WL
|
# 5. Keep wordline driver based on WL
|
||||||
# Need to keep the gater too
|
# Need to keep the gater too
|
||||||
#self.remove_insts("wordline_driver",wl_regex)
|
#self.remove_insts("wordline_driver",wl_regex)
|
||||||
|
|
||||||
# 6. Keep precharges based on BL
|
# 6. Keep precharges based on BL
|
||||||
self.remove_insts("precharge_array",[bl_regex])
|
self.remove_insts("precharge_array",[bl_regex])
|
||||||
|
|
||||||
# Everything else isn't worth removing. :)
|
# Everything else isn't worth removing. :)
|
||||||
|
|
||||||
# Finally, write out the buffer as the new reduced file
|
# Finally, write out the buffer as the new reduced file
|
||||||
sp = open(self.reduced_spfile, "w")
|
sp = open(self.reduced_spfile, "w")
|
||||||
sp.write("\n".join(self.sp_buffer))
|
sp.write("\n".join(self.sp_buffer))
|
||||||
sp.close()
|
sp.close()
|
||||||
|
|
||||||
|
|
||||||
def remove_insts(self, subckt_name, keep_inst_list):
|
def remove_insts(self, subckt_name, keep_inst_list):
|
||||||
"""This will remove all of the instances in the list from the named
|
"""This will remove all of the instances in the list from the named
|
||||||
subckt that DO NOT contain a term in the list. It just does a
|
subckt that DO NOT contain a term in the list. It just does a
|
||||||
|
|
@ -121,7 +121,7 @@ class trim_spice():
|
||||||
removed_insts = 0
|
removed_insts = 0
|
||||||
#Expects keep_inst_list are regex patterns. Compile them here.
|
#Expects keep_inst_list are regex patterns. Compile them here.
|
||||||
compiled_patterns = [re.compile(pattern) for pattern in keep_inst_list]
|
compiled_patterns = [re.compile(pattern) for pattern in keep_inst_list]
|
||||||
|
|
||||||
start_name = ".SUBCKT {}".format(subckt_name)
|
start_name = ".SUBCKT {}".format(subckt_name)
|
||||||
end_name = ".ENDS {}".format(subckt_name)
|
end_name = ".ENDS {}".format(subckt_name)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
import design
|
||||||
from tech import GDS, layer, spice, parameter
|
from tech import GDS, layer, spice
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
|
@ -23,39 +23,42 @@ class dff(design.design):
|
||||||
pin_names = props.dff.custom_port_list
|
pin_names = props.dff.custom_port_list
|
||||||
type_list = props.dff.custom_type_list
|
type_list = props.dff.custom_type_list
|
||||||
clk_pin = props.dff.clk_pin
|
clk_pin = props.dff.clk_pin
|
||||||
|
cell_size_layer = "boundary"
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("dff",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="dff"):
|
def __init__(self, name="dff"):
|
||||||
design.design.__init__(self, name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.width = dff.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = dff.height
|
GDS["unit"],
|
||||||
self.pin_map = dff.pin_map
|
layer[self.cell_size_layer])
|
||||||
|
|
||||||
|
pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
self.cell_name,
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.pin_map = pin_map
|
||||||
self.add_pin_types(self.type_list)
|
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"""
|
||||||
c_eff = self.calculate_effective_capacitance(load)
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
freq = spice["default_event_frequency"]
|
freq = spice["default_event_frequency"]
|
||||||
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||||
power_leak = spice["dff_leakage"]
|
power_leak = spice["dff_leakage"]
|
||||||
|
|
||||||
total_power = self.return_power(power_dyn, power_leak)
|
total_power = self.return_power(power_dyn, power_leak)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def calculate_effective_capacitance(self, load):
|
def calculate_effective_capacitance(self, load):
|
||||||
"""Computes effective capacitance. Results in fF"""
|
"""Computes effective capacitance. Results in fF"""
|
||||||
from tech import parameter
|
|
||||||
c_load = load
|
c_load = load
|
||||||
c_para = spice["dff_out_cap"]#ff
|
c_para = spice["dff_out_cap"]#ff
|
||||||
transition_prob = 0.5
|
transition_prob = 0.5
|
||||||
return transition_prob*(c_load + c_para)
|
return transition_prob*(c_load + c_para)
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,28 +9,31 @@ import design
|
||||||
from tech import GDS, layer, spice, parameter
|
from tech import GDS, layer, spice, parameter
|
||||||
import logical_effort
|
import logical_effort
|
||||||
import utils
|
import utils
|
||||||
import debug
|
|
||||||
|
|
||||||
|
|
||||||
class inv_dec(design.design):
|
class inv_dec(design.design):
|
||||||
"""
|
"""
|
||||||
INV for address decoders.
|
INV for address decoders.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pin_names = ["A", "Z", "vdd", "gnd"]
|
pin_names = ["A", "Z", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("inv_dec",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="inv_dec", height=None):
|
|
||||||
design.design.__init__(self, name)
|
|
||||||
|
|
||||||
self.width = inv_dec.width
|
def __init__(self, name="inv_dec", height=None):
|
||||||
self.height = inv_dec.height
|
super().__init__(name)
|
||||||
self.pin_map = inv_dec.pin_map
|
|
||||||
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
|
GDS["unit"],
|
||||||
|
layer[self.cell_size_layer])
|
||||||
|
|
||||||
|
pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
self.cell_name,
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.pin_map = pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
|
|
@ -39,10 +42,10 @@ class inv_dec(design.design):
|
||||||
freq = spice["default_event_frequency"]
|
freq = spice["default_event_frequency"]
|
||||||
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||||
power_leak = spice["inv_leakage"]
|
power_leak = spice["inv_leakage"]
|
||||||
|
|
||||||
total_power = self.return_power(power_dyn, power_leak)
|
total_power = self.return_power(power_dyn, power_leak)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def calculate_effective_capacitance(self, load):
|
def calculate_effective_capacitance(self, load):
|
||||||
"""Computes effective capacitance. Results in fF"""
|
"""Computes effective capacitance. Results in fF"""
|
||||||
c_load = load
|
c_load = load
|
||||||
|
|
@ -57,7 +60,7 @@ class inv_dec(design.design):
|
||||||
units relative to the minimum width of a transistor
|
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):
|
||||||
"""
|
"""
|
||||||
Returns an object representing the parameters for delay in tau units.
|
Returns an object representing the parameters for delay in tau units.
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,25 @@ class nand2_dec(design.design):
|
||||||
"""
|
"""
|
||||||
2-input NAND decoder for address decoders.
|
2-input NAND decoder for address decoders.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pin_names = ["A", "B", "Z", "vdd", "gnd"]
|
pin_names = ["A", "B", "Z", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("nand2_dec",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="nand2_dec", height=None):
|
|
||||||
design.design.__init__(self, name)
|
|
||||||
|
|
||||||
self.width = nand2_dec.width
|
def __init__(self, name="nand2_dec", height=None):
|
||||||
self.height = nand2_dec.height
|
super().__init__(name)
|
||||||
self.pin_map = nand2_dec.pin_map
|
|
||||||
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
|
GDS["unit"],
|
||||||
|
layer[self.cell_size_layer])
|
||||||
|
|
||||||
|
pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
self.cell_name,
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.pin_map = pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
# FIXME: For now...
|
# FIXME: For now...
|
||||||
|
|
@ -39,17 +43,17 @@ class nand2_dec(design.design):
|
||||||
self.pmos_size = parameter["beta"] * size
|
self.pmos_size = parameter["beta"] * size
|
||||||
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
||||||
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||||
|
|
||||||
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"""
|
||||||
c_eff = self.calculate_effective_capacitance(load)
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
freq = spice["default_event_frequency"]
|
freq = spice["default_event_frequency"]
|
||||||
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||||
power_leak = spice["nand2_leakage"]
|
power_leak = spice["nand2_leakage"]
|
||||||
|
|
||||||
total_power = self.return_power(power_dyn, power_leak)
|
total_power = self.return_power(power_dyn, power_leak)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def calculate_effective_capacitance(self, load):
|
def calculate_effective_capacitance(self, load):
|
||||||
"""Computes effective capacitance. Results in fF"""
|
"""Computes effective capacitance. Results in fF"""
|
||||||
c_load = load
|
c_load = load
|
||||||
|
|
@ -61,7 +65,7 @@ class nand2_dec(design.design):
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Return the relative input capacitance of a single input"""
|
"""Return the relative input capacitance of a single input"""
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
Returns an object representing the parameters for delay in tau units.
|
Returns an object representing the parameters for delay in tau units.
|
||||||
|
|
@ -82,4 +86,4 @@ class nand2_dec(design.design):
|
||||||
Overrides base class function.
|
Overrides base class function.
|
||||||
"""
|
"""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,25 @@ class nand3_dec(design.design):
|
||||||
"""
|
"""
|
||||||
3-input NAND decoder for address decoders.
|
3-input NAND decoder for address decoders.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pin_names = ["A", "B", "C", "Z", "vdd", "gnd"]
|
pin_names = ["A", "B", "C", "Z", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("nand3_dec",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="nand3_dec", height=None):
|
|
||||||
design.design.__init__(self, name)
|
|
||||||
|
|
||||||
self.width = nand3_dec.width
|
def __init__(self, name="nand3_dec", height=None):
|
||||||
self.height = nand3_dec.height
|
super().__init__(name)
|
||||||
self.pin_map = nand3_dec.pin_map
|
|
||||||
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
|
GDS["unit"],
|
||||||
|
layer[self.cell_size_layer])
|
||||||
|
|
||||||
|
pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
self.cell_name,
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.pin_map = pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
# FIXME: For now...
|
# FIXME: For now...
|
||||||
|
|
@ -39,17 +43,17 @@ class nand3_dec(design.design):
|
||||||
self.pmos_size = parameter["beta"] * size
|
self.pmos_size = parameter["beta"] * size
|
||||||
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
||||||
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||||
|
|
||||||
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"""
|
||||||
c_eff = self.calculate_effective_capacitance(load)
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
freq = spice["default_event_frequency"]
|
freq = spice["default_event_frequency"]
|
||||||
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||||
power_leak = spice["nand3_leakage"]
|
power_leak = spice["nand3_leakage"]
|
||||||
|
|
||||||
total_power = self.return_power(power_dyn, power_leak)
|
total_power = self.return_power(power_dyn, power_leak)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def calculate_effective_capacitance(self, load):
|
def calculate_effective_capacitance(self, load):
|
||||||
"""Computes effective capacitance. Results in fF"""
|
"""Computes effective capacitance. Results in fF"""
|
||||||
c_load = load
|
c_load = load
|
||||||
|
|
@ -61,7 +65,7 @@ class nand3_dec(design.design):
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Return the relative input capacitance of a single input"""
|
"""Return the relative input capacitance of a single input"""
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
Returns an object representing the parameters for delay in tau units.
|
Returns an object representing the parameters for delay in tau units.
|
||||||
|
|
@ -82,4 +86,4 @@ class nand3_dec(design.design):
|
||||||
Overrides base class function.
|
Overrides base class function.
|
||||||
"""
|
"""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,25 @@ class nand4_dec(design.design):
|
||||||
"""
|
"""
|
||||||
2-input NAND decoder for address decoders.
|
2-input NAND decoder for address decoders.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"]
|
pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("nand4_dec",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="nand4_dec", height=None):
|
|
||||||
design.design.__init__(self, name)
|
|
||||||
|
|
||||||
self.width = nand4_dec.width
|
def __init__(self, name="nand4_dec", height=None):
|
||||||
self.height = nand4_dec.height
|
super().__init__(name)
|
||||||
self.pin_map = nand4_dec.pin_map
|
|
||||||
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
|
GDS["unit"],
|
||||||
|
layer[self.cell_size_layer])
|
||||||
|
|
||||||
|
pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
self.cell_name,
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.pin_map = pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
# FIXME: For now...
|
# FIXME: For now...
|
||||||
|
|
@ -39,17 +43,17 @@ class nand4_dec(design.design):
|
||||||
self.pmos_size = parameter["beta"] * size
|
self.pmos_size = parameter["beta"] * size
|
||||||
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
||||||
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||||
|
|
||||||
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"""
|
||||||
c_eff = self.calculate_effective_capacitance(load)
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
freq = spice["default_event_frequency"]
|
freq = spice["default_event_frequency"]
|
||||||
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||||
power_leak = spice["nand4_leakage"]
|
power_leak = spice["nand4_leakage"]
|
||||||
|
|
||||||
total_power = self.return_power(power_dyn, power_leak)
|
total_power = self.return_power(power_dyn, power_leak)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def calculate_effective_capacitance(self, load):
|
def calculate_effective_capacitance(self, load):
|
||||||
"""Computes effective capacitance. Results in fF"""
|
"""Computes effective capacitance. Results in fF"""
|
||||||
c_load = load
|
c_load = load
|
||||||
|
|
@ -61,7 +65,7 @@ class nand4_dec(design.design):
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Return the relative input capacitance of a single input"""
|
"""Return the relative input capacitance of a single input"""
|
||||||
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):
|
||||||
"""
|
"""
|
||||||
Returns an object representing the parameters for delay in tau units.
|
Returns an object representing the parameters for delay in tau units.
|
||||||
|
|
@ -82,4 +86,4 @@ class nand4_dec(design.design):
|
||||||
Overrides base class function.
|
Overrides base class function.
|
||||||
"""
|
"""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import debug
|
||||||
import utils
|
import utils
|
||||||
from tech import GDS, layer, parameter, drc
|
from tech import GDS, layer, parameter, drc
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
from globals import OPTS
|
|
||||||
import logical_effort
|
import logical_effort
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -28,12 +27,24 @@ class sense_amp(design.design):
|
||||||
props.sense_amp.pin.vdd,
|
props.sense_amp.pin.vdd,
|
||||||
props.sense_amp.pin.gnd]
|
props.sense_amp.pin.gnd]
|
||||||
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
if not OPTS.netlist_only:
|
cell_size_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"])
|
def __init__(self, name="sense_amp"):
|
||||||
else:
|
super().__init__(name)
|
||||||
(width, height) = (0, 0)
|
debug.info(2, "Create sense_amp")
|
||||||
pin_map = []
|
|
||||||
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
|
GDS["unit"],
|
||||||
|
layer[self.cell_size_layer])
|
||||||
|
|
||||||
|
pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
self.cell_name,
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.pin_map = pin_map
|
||||||
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def get_bl_names(self):
|
def get_bl_names(self):
|
||||||
return props.sense_amp.pin.bl
|
return props.sense_amp.pin.bl
|
||||||
|
|
@ -49,26 +60,17 @@ class sense_amp(design.design):
|
||||||
def en_name(self):
|
def en_name(self):
|
||||||
return props.sense_amp.pin.en
|
return props.sense_amp.pin.en
|
||||||
|
|
||||||
def __init__(self, name):
|
|
||||||
super().__init__(name)
|
|
||||||
debug.info(2, "Create sense_amp")
|
|
||||||
|
|
||||||
self.width = sense_amp.width
|
|
||||||
self.height = sense_amp.height
|
|
||||||
self.pin_map = sense_amp.pin_map
|
|
||||||
self.add_pin_types(self.type_list)
|
|
||||||
|
|
||||||
def get_cin(self):
|
def get_cin(self):
|
||||||
|
|
||||||
# FIXME: This input load will be applied to both the s_en timing and bitline timing.
|
# FIXME: This input load will be applied to both the s_en timing and bitline timing.
|
||||||
|
|
||||||
# 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.
|
||||||
from tech import spice
|
from tech import spice
|
||||||
# Default is 8x. Per Samira and Hodges-Jackson book:
|
# Default is 8x. Per Samira and Hodges-Jackson book:
|
||||||
# "Column-mux transistors driven by the decoder must be sized for optimal speed"
|
# "Column-mux transistors driven by the decoder must be sized for optimal speed"
|
||||||
bitline_pmos_size = 8 # FIXME: This should be set somewhere and referenced. Probably in tech file.
|
bitline_pmos_size = 8 # FIXME: This should be set somewhere and referenced. Probably in tech file.
|
||||||
return spice["min_tx_drain_c"] * bitline_pmos_size # ff
|
return spice["min_tx_drain_c"] * bitline_pmos_size # ff
|
||||||
|
|
||||||
def get_stage_effort(self, load):
|
def get_stage_effort(self, load):
|
||||||
# Delay of the sense amp will depend on the size of the amp and the output load.
|
# Delay of the sense amp will depend on the size of the amp and the output load.
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
|
|
@ -82,14 +84,14 @@ class sense_amp(design.design):
|
||||||
# Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
|
# Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
|
||||||
total_power = self.return_power()
|
total_power = self.return_power()
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def get_enable_name(self):
|
def get_enable_name(self):
|
||||||
"""Returns name used for enable net"""
|
"""Returns name used for enable net"""
|
||||||
# FIXME: A better programmatic solution to designate pins
|
# FIXME: A better programmatic solution to designate pins
|
||||||
enable_name = self.en_name
|
enable_name = self.en_name
|
||||||
debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name))
|
debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name))
|
||||||
return enable_name
|
return enable_name
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
@ -8,43 +8,51 @@
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
import utils
|
import utils
|
||||||
from tech import GDS,layer
|
from tech import GDS, layer
|
||||||
|
|
||||||
|
|
||||||
class tri_gate(design.design):
|
class tri_gate(design.design):
|
||||||
"""
|
"""
|
||||||
This module implements the tri gate cell used in the design forS
|
This module implements the tri gate cell used in the design forS
|
||||||
bit-line isolation. It is a hand-made cell, so the layout and
|
bit-line isolation. It is a hand-made cell, so the layout and
|
||||||
netlist should be available in the technology library.
|
netlist should be available in the technology library.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pin_names = ["in", "out", "en", "en_bar", "vdd", "gnd"]
|
pin_names = ["in", "out", "en", "en_bar", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
|
cell_size_layer = "boundary"
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"])
|
|
||||||
|
|
||||||
unique_id = 1
|
unique_id = 1
|
||||||
|
|
||||||
def __init__(self, name=""):
|
def __init__(self, name=""):
|
||||||
if name=="":
|
if name=="":
|
||||||
name = "tri{0}".format(tri_gate.unique_id)
|
name = "tri{0}".format(tri_gate.unique_id)
|
||||||
tri_gate.unique_id += 1
|
tri_gate.unique_id += 1
|
||||||
design.design.__init__(self, name)
|
super().__init__(self, name)
|
||||||
debug.info(2, "Create tri_gate")
|
debug.info(2, "Create tri_gate")
|
||||||
|
|
||||||
self.width = tri_gate.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = tri_gate.height
|
GDS["unit"],
|
||||||
self.pin_map = tri_gate.pin_map
|
layer[self.cell_size_layer])
|
||||||
|
|
||||||
|
pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
self.cell_name,
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.pin_map = pin_map
|
||||||
self.add_pin_types(self.type_list)
|
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"""
|
||||||
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
|
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
|
||||||
total_power = self.return_power()
|
total_power = self.return_power()
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def get_cin(self):
|
def get_cin(self):
|
||||||
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 based on inputs/outputs. Overrides base class function."""
|
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
import utils
|
import utils
|
||||||
from globals import OPTS
|
from tech import GDS, layer
|
||||||
from tech import GDS,layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
|
|
||||||
|
|
||||||
class write_driver(design.design):
|
class write_driver(design.design):
|
||||||
"""
|
"""
|
||||||
Tristate write driver to be active during write operations only.
|
Tristate write driver to be active during write operations only.
|
||||||
This module implements the write driver cell used in the design. It
|
This module implements the write driver cell used in the design. It
|
||||||
is a hand-made cell, so the layout and netlist should be available in
|
is a hand-made cell, so the layout and netlist should be available in
|
||||||
the technology library.
|
the technology library.
|
||||||
|
|
@ -28,20 +28,23 @@ class write_driver(design.design):
|
||||||
props.write_driver.pin.gnd]
|
props.write_driver.pin.gnd]
|
||||||
|
|
||||||
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
if not OPTS.netlist_only:
|
cell_size_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"])
|
|
||||||
else:
|
|
||||||
(width,height) = (0,0)
|
|
||||||
pin_map = []
|
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
design.design.__init__(self, name)
|
super().__init__(name)
|
||||||
debug.info(2, "Create write_driver")
|
debug.info(2, "Create write_driver")
|
||||||
|
|
||||||
self.width = write_driver.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = write_driver.height
|
GDS["unit"],
|
||||||
self.pin_map = write_driver.pin_map
|
layer[self.cell_size_layer])
|
||||||
|
|
||||||
|
pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
self.cell_name,
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.pin_map = pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def get_bl_names(self):
|
def get_bl_names(self):
|
||||||
|
|
@ -63,6 +66,6 @@ class write_driver(design.design):
|
||||||
# This is approximated from SCMOS. It has roughly 5 3x transistor gates.
|
# This is approximated from SCMOS. It has roughly 5 3x transistor gates.
|
||||||
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 based on inputs/outputs. Overrides base class function."""
|
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,16 @@ def parse_html(file, comment):
|
||||||
end_tag = comment+'-->'
|
end_tag = comment+'-->'
|
||||||
|
|
||||||
with open(file, 'r') as f:
|
with open(file, 'r') as f:
|
||||||
|
|
||||||
file_string = f.read()
|
file_string = f.read()
|
||||||
|
|
||||||
with open(file, 'w') as f:
|
with open(file, 'w') as f:
|
||||||
|
|
||||||
file_string = file_string.replace(start_tag,"")
|
file_string = file_string.replace(start_tag,"")
|
||||||
file_string = file_string.replace(end_tag,"")
|
file_string = file_string.replace(end_tag,"")
|
||||||
|
|
||||||
f.write(file_string)
|
f.write(file_string)
|
||||||
|
|
||||||
def uncomment(comments):
|
def uncomment(comments):
|
||||||
comment_files = []
|
comment_files = []
|
||||||
for datasheet in datasheet_list:
|
for datasheet in datasheet_list:
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ def parse_characterizer_csv(f, pages):
|
||||||
|
|
||||||
DATETIME = row[col]
|
DATETIME = row[col]
|
||||||
col += 1
|
col += 1
|
||||||
|
|
||||||
ANALYTICAL_MODEL = row[col]
|
ANALYTICAL_MODEL = row[col]
|
||||||
col += 1
|
col += 1
|
||||||
|
|
||||||
|
|
@ -121,7 +121,7 @@ def parse_characterizer_csv(f, pages):
|
||||||
|
|
||||||
LVS = row[col]
|
LVS = row[col]
|
||||||
col += 1
|
col += 1
|
||||||
|
|
||||||
AREA = row[col]
|
AREA = row[col]
|
||||||
col += 1
|
col += 1
|
||||||
|
|
||||||
|
|
@ -565,7 +565,7 @@ def parse_characterizer_csv(f, pages):
|
||||||
for element in row[col_start:col-1]:
|
for element in row[col_start:col-1]:
|
||||||
sheet.description.append(str(element))
|
sheet.description.append(str(element))
|
||||||
break
|
break
|
||||||
# parse initial power and leakage information
|
# parse initial power and leakage information
|
||||||
while(True):
|
while(True):
|
||||||
start = col
|
start = col
|
||||||
if(row[col].startswith('power')):
|
if(row[col].startswith('power')):
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import os
|
||||||
import inspect
|
import inspect
|
||||||
import globals
|
import globals
|
||||||
import sys
|
import sys
|
||||||
|
import pdb
|
||||||
|
|
||||||
# the debug levels:
|
# the debug levels:
|
||||||
# 0 = minimum output (default)
|
# 0 = minimum output (default)
|
||||||
|
|
@ -26,9 +27,9 @@ def check(check, str):
|
||||||
log("ERROR: file {0}: line {1}: {2}\n".format(
|
log("ERROR: file {0}: line {1}: {2}\n".format(
|
||||||
os.path.basename(filename), line_number, str))
|
os.path.basename(filename), line_number, str))
|
||||||
|
|
||||||
if globals.OPTS.debug_level > 0:
|
if globals.OPTS.debug:
|
||||||
import pdb
|
|
||||||
pdb.set_trace()
|
pdb.set_trace()
|
||||||
|
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -40,9 +41,9 @@ def error(str, return_value=0):
|
||||||
log("ERROR: file {0}: line {1}: {2}\n".format(
|
log("ERROR: file {0}: line {1}: {2}\n".format(
|
||||||
os.path.basename(filename), line_number, str))
|
os.path.basename(filename), line_number, str))
|
||||||
|
|
||||||
if globals.OPTS.debug_level > 0 and return_value != 0:
|
if globals.OPTS.debug:
|
||||||
import pdb
|
|
||||||
pdb.set_trace()
|
pdb.set_trace()
|
||||||
|
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -96,7 +97,7 @@ log.create_file = True
|
||||||
|
|
||||||
def info(lev, str):
|
def info(lev, str):
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
if (OPTS.debug_level >= lev):
|
if (OPTS.verbose_level >= lev):
|
||||||
frm = inspect.stack()[1]
|
frm = inspect.stack()[1]
|
||||||
mod = inspect.getmodule(frm[0])
|
mod = inspect.getmodule(frm[0])
|
||||||
# classname = frm.f_globals['__name__']
|
# classname = frm.f_globals['__name__']
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class design_rules(dict):
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
return self.rules.keys()
|
return self.rules.keys()
|
||||||
|
|
||||||
def add_layer(self, name, width, spacing, area=0):
|
def add_layer(self, name, width, spacing, area=0):
|
||||||
# Minimum width
|
# Minimum width
|
||||||
self.add("minwidth_{}".format(name), width)
|
self.add("minwidth_{}".format(name), width)
|
||||||
|
|
@ -54,7 +54,7 @@ class design_rules(dict):
|
||||||
self.add("{0}_to_{0}".format(name), spacing)
|
self.add("{0}_to_{0}".format(name), spacing)
|
||||||
# Minimum area
|
# Minimum area
|
||||||
self.add("minarea_{}".format(name), area)
|
self.add("minarea_{}".format(name), area)
|
||||||
|
|
||||||
def add_enclosure(self, name, layer, enclosure, extension=None):
|
def add_enclosure(self, name, layer, enclosure, extension=None):
|
||||||
self.add("{0}_enclose_{1}".format(name, layer), enclosure)
|
self.add("{0}_enclose_{1}".format(name, layer), enclosure)
|
||||||
# Reserved for asymmetric enclosures
|
# Reserved for asymmetric enclosures
|
||||||
|
|
@ -62,4 +62,4 @@ class design_rules(dict):
|
||||||
self.add("{0}_extend_{1}".format(name, layer), extension)
|
self.add("{0}_extend_{1}".format(name, layer), extension)
|
||||||
else:
|
else:
|
||||||
self.add("{0}_extend_{1}".format(name, layer), enclosure)
|
self.add("{0}_extend_{1}".format(name, layer), enclosure)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class drc_lut():
|
||||||
if k1 < k2:
|
if k1 < k2:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class drc_value():
|
||||||
Return the value.
|
Return the value.
|
||||||
"""
|
"""
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ word_size = 32
|
||||||
num_words = 128
|
num_words = 128
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
word_size = 4
|
||||||
|
num_words = 16
|
||||||
|
write_size = 2
|
||||||
|
|
||||||
|
num_rw_ports = 1
|
||||||
|
num_r_ports = 0
|
||||||
|
num_w_ports = 1
|
||||||
|
|
||||||
|
tech_name = "scn4m_subm"
|
||||||
|
nominal_corner_only = False
|
||||||
|
process_corners = ["TT"]
|
||||||
|
supply_voltages = [5.0]
|
||||||
|
temperatures = [25]
|
||||||
|
|
||||||
|
route_supplies = False
|
||||||
|
check_lvsdrc = True
|
||||||
|
|
||||||
|
output_path = "temp"
|
||||||
|
output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,
|
||||||
|
num_words,
|
||||||
|
tech_name)
|
||||||
|
|
@ -7,7 +7,7 @@ num_r_ports = 0
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 1
|
num_w_ports = 1
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
word_size = 2
|
||||||
|
num_words = 16
|
||||||
|
|
||||||
|
num_rw_ports = 2
|
||||||
|
num_r_ports = 0
|
||||||
|
num_w_ports = 0
|
||||||
|
|
||||||
|
tech_name = "scn4m_subm"
|
||||||
|
nominal_corner_only = False
|
||||||
|
process_corners = ["TT"]
|
||||||
|
supply_voltages = [5.0]
|
||||||
|
temperatures = [25]
|
||||||
|
|
||||||
|
route_supplies = False
|
||||||
|
check_lvsdrc = True
|
||||||
|
|
||||||
|
output_path = "temp"
|
||||||
|
output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,
|
||||||
|
num_words,
|
||||||
|
tech_name)
|
||||||
|
|
||||||
|
|
@ -2,14 +2,14 @@ word_size = 2
|
||||||
num_words = 16
|
num_words = 16
|
||||||
|
|
||||||
tech_name = "freepdk45"
|
tech_name = "freepdk45"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [1.0]
|
supply_voltages = [1.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
# nominal_corners_only = True
|
# nominal_corner_only = True
|
||||||
load_scales = [0.5, 1, 4]
|
load_scales = [0.5, 1, 4]
|
||||||
slew_scales = [0.5, 1]
|
slew_scales = [0.5, 1]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ word_size = 2
|
||||||
num_words = 16
|
num_words = 16
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ word_size = 64
|
||||||
num_words = 1024
|
num_words = 1024
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [ 5.0 ]
|
supply_voltages = [ 5.0 ]
|
||||||
temperatures = [ 25 ]
|
temperatures = [ 25 ]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ word_size = 16
|
||||||
num_words = 256
|
num_words = 256
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "freepdk45"
|
tech_name = "freepdk45"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = False
|
check_lvsdrc = False
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = False
|
check_lvsdrc = False
|
||||||
|
|
@ -7,7 +7,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = True
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
|
@ -7,7 +7,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = True
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
|
@ -9,7 +9,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = False
|
check_lvsdrc = False
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = True
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
|
@ -9,7 +9,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = False
|
check_lvsdrc = False
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
word_size = 32
|
||||||
|
num_words = 256
|
||||||
|
write_size = 8
|
||||||
|
|
||||||
|
local_array_size = 16
|
||||||
|
|
||||||
|
num_rw_ports = 1
|
||||||
|
num_r_ports = 0
|
||||||
|
num_w_ports = 0
|
||||||
|
|
||||||
|
tech_name = "sky130"
|
||||||
|
nominal_corner_only = True
|
||||||
|
|
||||||
|
route_supplies = False
|
||||||
|
check_lvsdrc = True
|
||||||
|
perimeter_pins = False
|
||||||
|
#netlist_only = True
|
||||||
|
#analytical_delay = False
|
||||||
|
output_path = "macros/sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
num_words,
|
||||||
|
write_size,
|
||||||
|
tech_name)
|
||||||
|
output_name = "sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
num_words,
|
||||||
|
write_size,
|
||||||
|
tech_name)
|
||||||
|
|
@ -9,11 +9,11 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "sky130"
|
tech_name = "sky130"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = False
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
perimeter_pins = True
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
word_size = 32
|
||||||
|
num_words = 512
|
||||||
|
write_size = 8
|
||||||
|
|
||||||
|
local_array_size = 16
|
||||||
|
|
||||||
|
num_rw_ports = 1
|
||||||
|
num_r_ports = 0
|
||||||
|
num_w_ports = 0
|
||||||
|
|
||||||
|
tech_name = "sky130"
|
||||||
|
nominal_corner_only = True
|
||||||
|
|
||||||
|
route_supplies = False
|
||||||
|
check_lvsdrc = True
|
||||||
|
perimeter_pins = False
|
||||||
|
#netlist_only = True
|
||||||
|
#analytical_delay = False
|
||||||
|
output_path = "macros/sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
num_words,
|
||||||
|
write_size,
|
||||||
|
tech_name)
|
||||||
|
output_name = "sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
num_words,
|
||||||
|
write_size,
|
||||||
|
tech_name)
|
||||||
|
|
@ -9,11 +9,11 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "sky130"
|
tech_name = "sky130"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = False
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
perimeter_pins = True
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
word_size = 32
|
||||||
|
num_words = 1024
|
||||||
|
write_size = 8
|
||||||
|
|
||||||
|
local_array_size = 16
|
||||||
|
|
||||||
|
num_rw_ports = 1
|
||||||
|
num_r_ports = 0
|
||||||
|
num_w_ports = 0
|
||||||
|
|
||||||
|
tech_name = "sky130"
|
||||||
|
nominal_corner_only = True
|
||||||
|
|
||||||
|
route_supplies = False
|
||||||
|
check_lvsdrc = True
|
||||||
|
perimeter_pins = False
|
||||||
|
#netlist_only = True
|
||||||
|
#analytical_delay = False
|
||||||
|
output_path = "macros/sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
num_words,
|
||||||
|
write_size,
|
||||||
|
tech_name)
|
||||||
|
output_name = "sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
num_words,
|
||||||
|
write_size,
|
||||||
|
tech_name)
|
||||||
|
|
@ -9,11 +9,11 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "sky130"
|
tech_name = "sky130"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = False
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
perimeter_pins = True
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
word_size = 2
|
|
||||||
num_words = 16
|
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
|
||||||
process_corners = ["TT"]
|
|
||||||
supply_voltages = [5.0]
|
|
||||||
temperatures = [25]
|
|
||||||
|
|
||||||
#netlist_only = True
|
|
||||||
route_supplies = True
|
|
||||||
check_lvsdrc = True
|
|
||||||
|
|
||||||
output_name = "sram_{0}_{1}_{2}".format(word_size,
|
|
||||||
num_words,
|
|
||||||
tech_name)
|
|
||||||
|
|
||||||
drc_name = "magic"
|
|
||||||
lvs_name = "netgen"
|
|
||||||
pex_name = "magic"
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
word_size = 8
|
|
||||||
num_words = 128
|
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
|
||||||
process_corners = ["TT"]
|
|
||||||
supply_voltages = [5.0]
|
|
||||||
temperatures = [25]
|
|
||||||
|
|
||||||
route_supplies = True
|
|
||||||
check_lvsdrc = True
|
|
||||||
|
|
||||||
netlist_only = True
|
|
||||||
output_name = "sram_{0}_{1}_{2}".format(word_size,
|
|
||||||
num_words,
|
|
||||||
tech_name)
|
|
||||||
|
|
||||||
drc_name = "magic"
|
|
||||||
lvs_name = "netgen"
|
|
||||||
pex_name = "magic"
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
word_size = 16
|
|
||||||
num_words = 256
|
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
|
||||||
process_corners = ["TT"]
|
|
||||||
supply_voltages = [5.0]
|
|
||||||
temperatures = [25]
|
|
||||||
|
|
||||||
route_supplies = True
|
|
||||||
check_lvsdrc = True
|
|
||||||
netlist_only = True
|
|
||||||
output_name = "sram_{0}_{1}_{2}".format(word_size,
|
|
||||||
num_words,
|
|
||||||
tech_name)
|
|
||||||
|
|
||||||
drc_name = "magic"
|
|
||||||
lvs_name = "netgen"
|
|
||||||
pex_name = "magic"
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
word_size = 32
|
|
||||||
num_words = 128
|
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
|
||||||
process_corners = ["TT"]
|
|
||||||
supply_voltages = [5.0]
|
|
||||||
temperatures = [25]
|
|
||||||
|
|
||||||
route_supplies = True
|
|
||||||
check_lvsdrc = True
|
|
||||||
netlist_only = True
|
|
||||||
output_name = "sram_{0}_{1}_{2}".format(word_size,
|
|
||||||
num_words,
|
|
||||||
tech_name)
|
|
||||||
|
|
||||||
drc_name = "magic"
|
|
||||||
lvs_name = "netgen"
|
|
||||||
pex_name = "magic"
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
word_size = 64
|
|
||||||
num_words = 128
|
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
|
||||||
process_corners = ["TT"]
|
|
||||||
supply_voltages = [5.0]
|
|
||||||
temperatures = [25]
|
|
||||||
|
|
||||||
route_supplies = True
|
|
||||||
check_lvsdrc = True
|
|
||||||
|
|
||||||
output_path = "/home/jesse/thesis/outputs/run5"
|
|
||||||
output_name = "sram_{0}_{1}_{2}".format(word_size,
|
|
||||||
num_words,
|
|
||||||
tech_name)
|
|
||||||
|
|
||||||
drc_name = "magic"
|
|
||||||
lvs_name = "netgen"
|
|
||||||
pex_name = "magic"
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
word_size = 16
|
|
||||||
num_words = 16
|
|
||||||
|
|
||||||
num_rw_ports = 1
|
|
||||||
num_r_ports = 1
|
|
||||||
num_w_ports = 0
|
|
||||||
|
|
||||||
tech_name = "sky130"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
accuracy_requirement = 0.05
|
|
||||||
magic_exe = ("magic", "magic")
|
|
||||||
nominal_corners_only = False
|
|
||||||
process_corners = ["TT"]
|
|
||||||
supply_voltages = [5.0]
|
|
||||||
temperatures = [25]
|
|
||||||
|
|
||||||
netlist_only = False
|
|
||||||
route_supplies = "grid"
|
|
||||||
check_lvsdrc = False
|
|
||||||
|
|
||||||
#replica_bitcell_array = "/home/jesse/openram/technology/sky130/modules/replica_bitcell_array.py"
|
|
||||||
|
|
||||||
output_path = "sram_" + str(accuracy_requirement)
|
|
||||||
output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,
|
|
||||||
num_words,
|
|
||||||
tech_name,
|
|
||||||
accuracy_requirement
|
|
||||||
)
|
|
||||||
write_size=8
|
|
||||||
|
|
@ -6,7 +6,7 @@ class GdsStreamer:
|
||||||
"""
|
"""
|
||||||
def __init__(self, workingDirectory = "."):
|
def __init__(self, workingDirectory = "."):
|
||||||
self.workingDirectory = os.path.abspath(workingDirectory)
|
self.workingDirectory = os.path.abspath(workingDirectory)
|
||||||
|
|
||||||
def createStreamOutTemplate(self, sourceLibraryName, sourceCellName, gdsDestinationPath):
|
def createStreamOutTemplate(self, sourceLibraryName, sourceCellName, gdsDestinationPath):
|
||||||
templateFile = open(self.workingDirectory+"/partStreamOut.tmpl","w")
|
templateFile = open(self.workingDirectory+"/partStreamOut.tmpl","w")
|
||||||
templateFile.write("streamOutKeys = list(nil\n")
|
templateFile.write("streamOutKeys = list(nil\n")
|
||||||
|
|
@ -70,7 +70,7 @@ class GdsStreamer:
|
||||||
templateFile = open(self.workingDirectory+"/partStreamIn.tmpl","w")
|
templateFile = open(self.workingDirectory+"/partStreamIn.tmpl","w")
|
||||||
templateFile.write("streamInKeys = list(nil\n")
|
templateFile.write("streamInKeys = list(nil\n")
|
||||||
templateFile.write("'runDir \".\"\n")
|
templateFile.write("'runDir \".\"\n")
|
||||||
templateFile.write("'inFile \""+inputGdsPath+"\"\n")
|
templateFile.write("'inFile \""+inputGdsPath+"\"\n")
|
||||||
templateFile.write("'primaryCell \"\"\n")
|
templateFile.write("'primaryCell \"\"\n")
|
||||||
templateFile.write("'libName \""+sourceLibraryName+"\"\n")
|
templateFile.write("'libName \""+sourceLibraryName+"\"\n")
|
||||||
templateFile.write("'techFileName \"\"\n")
|
templateFile.write("'techFileName \"\"\n")
|
||||||
|
|
@ -88,7 +88,7 @@ class GdsStreamer:
|
||||||
templateFile.write("'convertNode \"ignore\"\n")
|
templateFile.write("'convertNode \"ignore\"\n")
|
||||||
templateFile.write("'keepPcell nil\n")
|
templateFile.write("'keepPcell nil\n")
|
||||||
templateFile.write("'replaceBusBitChar nil\n")
|
templateFile.write("'replaceBusBitChar nil\n")
|
||||||
templateFile.write("'skipUndefinedLPP nil\n")
|
templateFile.write("'skipUndefinedLPP nil\n")
|
||||||
templateFile.write("'ignoreBox nil\n")
|
templateFile.write("'ignoreBox nil\n")
|
||||||
templateFile.write("'mergeUndefPurposToDrawing nil\n")
|
templateFile.write("'mergeUndefPurposToDrawing nil\n")
|
||||||
templateFile.write("'reportPrecision nil\n")
|
templateFile.write("'reportPrecision nil\n")
|
||||||
|
|
@ -109,10 +109,10 @@ class GdsStreamer:
|
||||||
templateFile.write("'propSeparator \",\"\n")
|
templateFile.write("'propSeparator \",\"\n")
|
||||||
templateFile.write("'userSkillFile \"\"\n")
|
templateFile.write("'userSkillFile \"\"\n")
|
||||||
templateFile.write("'rodDir \"\"\n")
|
templateFile.write("'rodDir \"\"\n")
|
||||||
templateFile.write("'refLibOrder \"\"\n")
|
templateFile.write("'refLibOrder \"\"\n")
|
||||||
templateFile.write(")\n")
|
templateFile.write(")\n")
|
||||||
templateFile.close()
|
templateFile.close()
|
||||||
|
|
||||||
def streamFromCadence(self, cadenceLibraryContainerPath, libraryName, cellName, outputPath):
|
def streamFromCadence(self, cadenceLibraryContainerPath, libraryName, cellName, outputPath):
|
||||||
#change into the cadence directory
|
#change into the cadence directory
|
||||||
outputPath = os.path.abspath(outputPath)
|
outputPath = os.path.abspath(outputPath)
|
||||||
|
|
@ -132,7 +132,7 @@ class GdsStreamer:
|
||||||
os.remove(self.workingDirectory+"/partStreamOut.tmpl")
|
os.remove(self.workingDirectory+"/partStreamOut.tmpl")
|
||||||
#and go back to whever it was we started from
|
#and go back to whever it was we started from
|
||||||
os.chdir(currentPath)
|
os.chdir(currentPath)
|
||||||
|
|
||||||
def streamToCadence(self,cadenceLibraryContainerPath, libraryName, inputPath):
|
def streamToCadence(self,cadenceLibraryContainerPath, libraryName, inputPath):
|
||||||
#change into the cadence directory
|
#change into the cadence directory
|
||||||
inputPath = os.path.abspath(inputPath)
|
inputPath = os.path.abspath(inputPath)
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,19 @@ class pdfLayout:
|
||||||
self.layout = theLayout
|
self.layout = theLayout
|
||||||
self.layerColors=dict()
|
self.layerColors=dict()
|
||||||
self.scale = 1.0
|
self.scale = 1.0
|
||||||
|
|
||||||
def setScale(self,newScale):
|
def setScale(self,newScale):
|
||||||
self.scale = float(newScale)
|
self.scale = float(newScale)
|
||||||
|
|
||||||
def hexToRgb(self,hexColor):
|
def hexToRgb(self,hexColor):
|
||||||
"""
|
"""
|
||||||
Takes a hexadecimal color string i.e. "#219E1C" and converts it to an rgb float triplet ranging 0->1
|
Takes a hexadecimal color string i.e. "#219E1C" and converts it to an rgb float triplet ranging 0->1
|
||||||
"""
|
"""
|
||||||
red = int(hexColor[1:3],16)
|
red = int(hexColor[1:3],16)
|
||||||
green = int(hexColor[3:5],16)
|
green = int(hexColor[3:5],16)
|
||||||
blue = int(hexColor[5:7],16)
|
blue = int(hexColor[5:7],16)
|
||||||
return (float(red)/255,float(green)/255,float(blue)/255)
|
return (float(red)/255,float(green)/255,float(blue)/255)
|
||||||
|
|
||||||
def randomHexColor(self):
|
def randomHexColor(self):
|
||||||
"""
|
"""
|
||||||
Generates a random color in hex using the format #ABC123
|
Generates a random color in hex using the format #ABC123
|
||||||
|
|
@ -50,26 +50,26 @@ class pdfLayout:
|
||||||
xyPoint = tMatrix * xyPoint
|
xyPoint = tMatrix * xyPoint
|
||||||
xyCoordinates += [(xyPoint[0],xyPoint[1])]
|
xyCoordinates += [(xyPoint[0],xyPoint[1])]
|
||||||
return xyCoordinates
|
return xyCoordinates
|
||||||
|
|
||||||
def drawBoundary(self,boundary,origin,uVector,vVector):
|
def drawBoundary(self,boundary,origin,uVector,vVector):
|
||||||
#get the coordinates in the correct coordinate space
|
#get the coordinates in the correct coordinate space
|
||||||
coordinates = self.transformCoordinates(boundary.coordinates,origin,uVector,vVector)
|
coordinates = self.transformCoordinates(boundary.coordinates,origin,uVector,vVector)
|
||||||
#method to draw a boundary with an XY offset
|
#method to draw a boundary with an XY offset
|
||||||
x=(coordinates[0][0])/self.scale
|
x=(coordinates[0][0])/self.scale
|
||||||
y=(coordinates[0][1])/self.scale
|
y=(coordinates[0][1])/self.scale
|
||||||
shape = pyx.path.path(pyx.path.moveto(x, y))
|
shape = pyx.path.path(pyx.path.moveto(x, y))
|
||||||
for index in range(1,len(coordinates)):
|
for index in range(1,len(coordinates)):
|
||||||
x=(coordinates[index][0])/self.scale
|
x=(coordinates[index][0])/self.scale
|
||||||
y=(coordinates[index][1])/self.scale
|
y=(coordinates[index][1])/self.scale
|
||||||
shape.append(pyx.path.lineto(x,y))
|
shape.append(pyx.path.lineto(x,y))
|
||||||
self.canvas.stroke(shape, [pyx.style.linewidth.thick])
|
self.canvas.stroke(shape, [pyx.style.linewidth.thick])
|
||||||
if(boundary.drawingLayer in self.layerColors):
|
if(boundary.drawingLayer in self.layerColors):
|
||||||
layerColor = self.hexToRgb(self.layerColors[boundary.drawingLayer])
|
layerColor = self.hexToRgb(self.layerColors[boundary.drawingLayer])
|
||||||
self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)])
|
self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)])
|
||||||
|
|
||||||
def drawPath(self,path,origin,uVector,vVector):
|
def drawPath(self,path,origin,uVector,vVector):
|
||||||
#method to draw a path with an XY offset
|
#method to draw a path with an XY offset
|
||||||
boundaryCoordinates = self.transformCoordinates(path.equivalentBoundaryCoordinates(),origin,uVector,vVector)
|
boundaryCoordinates = self.transformCoordinates(path.equivalentBoundaryCoordinates(),origin,uVector,vVector)
|
||||||
shape = pyx.path.path(pyx.path.moveto((boundaryCoordinates[0][0])/self.scale,(boundaryCoordinates[0][1])/self.scale))
|
shape = pyx.path.path(pyx.path.moveto((boundaryCoordinates[0][0])/self.scale,(boundaryCoordinates[0][1])/self.scale))
|
||||||
for coordinate in boundaryCoordinates[1::]:
|
for coordinate in boundaryCoordinates[1::]:
|
||||||
shape.append(pyx.path.lineto((coordinate[0])/self.scale,(coordinate[1])/self.scale))
|
shape.append(pyx.path.lineto((coordinate[0])/self.scale,(coordinate[1])/self.scale))
|
||||||
|
|
@ -77,7 +77,7 @@ class pdfLayout:
|
||||||
if(path.drawingLayer in self.layerColors):
|
if(path.drawingLayer in self.layerColors):
|
||||||
layerColor = self.hexToRgb(self.layerColors[path.drawingLayer])
|
layerColor = self.hexToRgb(self.layerColors[path.drawingLayer])
|
||||||
self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)])
|
self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)])
|
||||||
|
|
||||||
def drawLayout(self):
|
def drawLayout(self):
|
||||||
#use the layout xyTree and structureList
|
#use the layout xyTree and structureList
|
||||||
#to draw ONLY the geometry in each structure
|
#to draw ONLY the geometry in each structure
|
||||||
|
|
@ -89,6 +89,6 @@ class pdfLayout:
|
||||||
self.drawBoundary(boundary,element[1],element[2], element[3])
|
self.drawBoundary(boundary,element[1],element[2], element[3])
|
||||||
for path in structureToDraw.paths:
|
for path in structureToDraw.paths:
|
||||||
self.drawPath(path,element[1],element[2], element[3])
|
self.drawPath(path,element[1],element[2], element[3])
|
||||||
|
|
||||||
def writeToFile(self,filename):
|
def writeToFile(self,filename):
|
||||||
self.canvas.writePDFfile(filename)
|
self.canvas.writePDFfile(filename)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import unit
|
||||||
#
|
#
|
||||||
|
|
||||||
class bbox_pt:
|
class bbox_pt:
|
||||||
|
|
||||||
"""class for bounding boxes
|
"""class for bounding boxes
|
||||||
|
|
||||||
This variant requires points in the constructor, and is used for internal
|
This variant requires points in the constructor, and is used for internal
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class canvasitem:
|
||||||
- the PS code corresponding to the canvasitem has to be written in the
|
- the PS code corresponding to the canvasitem has to be written in the
|
||||||
stream file, which provides a write(string) method
|
stream file, which provides a write(string) method
|
||||||
- writer is the PSwriter used for the output
|
- writer is the PSwriter used for the output
|
||||||
- context is an instance of pswriter.context which is used for keeping
|
- context is an instance of pswriter.context which is used for keeping
|
||||||
track of the graphics state (current linewidth, colorspace and font))
|
track of the graphics state (current linewidth, colorspace and font))
|
||||||
- registry is used for tracking resources needed by the canvasitem
|
- registry is used for tracking resources needed by the canvasitem
|
||||||
- bbox has to be updated to include the bounding box of the canvasitem
|
- bbox has to be updated to include the bounding box of the canvasitem
|
||||||
|
|
@ -63,7 +63,7 @@ class canvasitem:
|
||||||
- writer is the PDFwriter used for the output, which contains properties
|
- writer is the PDFwriter used for the output, which contains properties
|
||||||
like whether streamcompression is used
|
like whether streamcompression is used
|
||||||
- context is an instance of pdfwriter.context which is used for keeping
|
- context is an instance of pdfwriter.context which is used for keeping
|
||||||
track of the graphics state, in particular for the emulation of PS
|
track of the graphics state, in particular for the emulation of PS
|
||||||
behaviour regarding fill and stroke styles, for keeping track of the
|
behaviour regarding fill and stroke styles, for keeping track of the
|
||||||
currently selected font as well as of text regions.
|
currently selected font as well as of text regions.
|
||||||
- registry is used for tracking resources needed by the canvasitem
|
- registry is used for tracking resources needed by the canvasitem
|
||||||
|
|
@ -145,8 +145,8 @@ class _canvas(canvasitem):
|
||||||
attr.checkattrs(attrs, [trafo.trafo_pt, clip, style.strokestyle, style.fillstyle])
|
attr.checkattrs(attrs, [trafo.trafo_pt, clip, style.strokestyle, style.fillstyle])
|
||||||
# We have to reverse the trafos such that the PostScript concat operators
|
# We have to reverse the trafos such that the PostScript concat operators
|
||||||
# are in the right order. Correspondingly, we below multiply the current self.trafo
|
# are in the right order. Correspondingly, we below multiply the current self.trafo
|
||||||
# from the right.
|
# from the right.
|
||||||
# Note that while for the stroke and fill styles the order doesn't matter at all,
|
# Note that while for the stroke and fill styles the order doesn't matter at all,
|
||||||
# this is not true for the clip operation.
|
# this is not true for the clip operation.
|
||||||
attrs = attrs[:]
|
attrs = attrs[:]
|
||||||
attrs.reverse()
|
attrs.reverse()
|
||||||
|
|
|
||||||
|
|
@ -1188,7 +1188,7 @@ class parallel(deformer): # <<<
|
||||||
intsparams = np[nsp_i][nspitem_i].intersect(np[nsp_j][nspitem_j], epsilon)
|
intsparams = np[nsp_i][nspitem_i].intersect(np[nsp_j][nspitem_j], epsilon)
|
||||||
if intsparams:
|
if intsparams:
|
||||||
for intsparam_i, intsparam_j in intsparams:
|
for intsparam_i, intsparam_j in intsparams:
|
||||||
if ( (abs(intsparam_i) < epsilon and abs(1-intsparam_j) < epsilon) or
|
if ( (abs(intsparam_i) < epsilon and abs(1-intsparam_j) < epsilon) or
|
||||||
(abs(intsparam_j) < epsilon and abs(1-intsparam_i) < epsilon) ):
|
(abs(intsparam_j) < epsilon and abs(1-intsparam_i) < epsilon) ):
|
||||||
continue
|
continue
|
||||||
npp_i = normpath.normpathparam(np, nsp_i, float(nspitem_i)+intsparam_i)
|
npp_i = normpath.normpathparam(np, nsp_i, float(nspitem_i)+intsparam_i)
|
||||||
|
|
|
||||||
|
|
@ -467,7 +467,7 @@ class font:
|
||||||
return fontinfo
|
return fontinfo
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name,
|
return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name,
|
||||||
16.0*self.d/16777216L,
|
16.0*self.d/16777216L,
|
||||||
16.0*self.q/16777216L)
|
16.0*self.q/16777216L)
|
||||||
__repr__ = __str__
|
__repr__ = __str__
|
||||||
|
|
@ -510,7 +510,7 @@ class font:
|
||||||
|
|
||||||
def _convert_tfm_to_ds(self, length):
|
def _convert_tfm_to_ds(self, length):
|
||||||
return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv * 1000 / self.getsize_pt()
|
return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv * 1000 / self.getsize_pt()
|
||||||
|
|
||||||
def _convert_tfm_to_pt(self, length):
|
def _convert_tfm_to_pt(self, length):
|
||||||
return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv
|
return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv
|
||||||
|
|
||||||
|
|
@ -528,7 +528,7 @@ class font:
|
||||||
def getitalic_dvi(self, charcode):
|
def getitalic_dvi(self, charcode):
|
||||||
return self._convert_tfm_to_dvi(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
|
return self._convert_tfm_to_dvi(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
|
||||||
|
|
||||||
# routines returning lengths as integers in design size (AFM) units
|
# routines returning lengths as integers in design size (AFM) units
|
||||||
|
|
||||||
def getwidth_ds(self, charcode):
|
def getwidth_ds(self, charcode):
|
||||||
return self._convert_tfm_to_ds(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index])
|
return self._convert_tfm_to_ds(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index])
|
||||||
|
|
@ -767,7 +767,7 @@ class dvifile:
|
||||||
if fontslant is not None:
|
if fontslant is not None:
|
||||||
fontslant = float(fontslant)
|
fontslant = float(fontslant)
|
||||||
|
|
||||||
# XXX we currently misuse use self.activefont as metric
|
# XXX we currently misuse use self.activefont as metric
|
||||||
font = type1font.font(fontbasefontname, fontfilename, fontencoding, fontslant, self.activefont)
|
font = type1font.font(fontbasefontname, fontfilename, fontencoding, fontslant, self.activefont)
|
||||||
|
|
||||||
self.activetext = type1font.text_pt(self.pos[_POS_H] * self.pyxconv, -self.pos[_POS_V] * self.pyxconv, font)
|
self.activetext = type1font.text_pt(self.pos[_POS_H] * self.pyxconv, -self.pos[_POS_V] * self.pyxconv, font)
|
||||||
|
|
@ -973,14 +973,14 @@ class dvifile:
|
||||||
den = afile.readuint32()
|
den = afile.readuint32()
|
||||||
self.mag = afile.readuint32()
|
self.mag = afile.readuint32()
|
||||||
|
|
||||||
# For the interpretation of the lengths in dvi and tfm files,
|
# For the interpretation of the lengths in dvi and tfm files,
|
||||||
# three conversion factors are relevant:
|
# three conversion factors are relevant:
|
||||||
# - self.tfmconv: tfm units -> dvi units
|
# - self.tfmconv: tfm units -> dvi units
|
||||||
# - self.pyxconv: dvi units -> (PostScript) points
|
# - self.pyxconv: dvi units -> (PostScript) points
|
||||||
# - self.conv: dvi units -> pixels
|
# - self.conv: dvi units -> pixels
|
||||||
self.tfmconv = (25400000.0/num)*(den/473628672.0)/16.0
|
self.tfmconv = (25400000.0/num)*(den/473628672.0)/16.0
|
||||||
|
|
||||||
# calculate conv as described in the DVIType docu using
|
# calculate conv as described in the DVIType docu using
|
||||||
# a given resolution in dpi
|
# a given resolution in dpi
|
||||||
self.resolution = 300.0
|
self.resolution = 300.0
|
||||||
self.conv = (num/254000.0)*(self.resolution/den)
|
self.conv = (num/254000.0)*(self.resolution/den)
|
||||||
|
|
|
||||||
|
|
@ -319,7 +319,7 @@ class epsfile(canvas.canvasitem):
|
||||||
try:
|
try:
|
||||||
epsfile=open(self.filename,"rb")
|
epsfile=open(self.filename,"rb")
|
||||||
except:
|
except:
|
||||||
raise IOError, "cannot open EPS file '%s'" % self.filename
|
raise IOError, "cannot open EPS file '%s'" % self.filename
|
||||||
|
|
||||||
file.write("BeginEPSF\n")
|
file.write("BeginEPSF\n")
|
||||||
|
|
||||||
|
|
@ -330,7 +330,7 @@ class epsfile(canvas.canvasitem):
|
||||||
self.trafo.processPS(file, writer, context, registry, bbox)
|
self.trafo.processPS(file, writer, context, registry, bbox)
|
||||||
|
|
||||||
file.write("%%%%BeginDocument: %s\n" % self.filename)
|
file.write("%%%%BeginDocument: %s\n" % self.filename)
|
||||||
file.write(epsfile.read())
|
file.write(epsfile.read())
|
||||||
file.write("%%EndDocument\n")
|
file.write("%%EndDocument\n")
|
||||||
file.write("EndEPSF\n")
|
file.write("EndEPSF\n")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -166,4 +166,4 @@ def realpolyroots(*cs):
|
||||||
# else:
|
# else:
|
||||||
# rs.append(r)
|
# rs.append(r)
|
||||||
# return rs
|
# return rs
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -710,9 +710,9 @@ class arct_pt(pathitem):
|
||||||
# Negative (positive) angles alpha corresponds to a turn to the right (left)
|
# Negative (positive) angles alpha corresponds to a turn to the right (left)
|
||||||
# as seen from currentpoint.
|
# as seen from currentpoint.
|
||||||
if dx1*dy2-dy1*dx2 > 0:
|
if dx1*dy2-dy1*dx2 > 0:
|
||||||
alpha = acos(dx1*dx2+dy1*dy2)
|
alpha = acos(dx1*dx2+dy1*dy2)
|
||||||
else:
|
else:
|
||||||
alpha = -acos(dx1*dx2+dy1*dy2)
|
alpha = -acos(dx1*dx2+dy1*dy2)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# two tangent points
|
# two tangent points
|
||||||
|
|
@ -744,7 +744,7 @@ class arct_pt(pathitem):
|
||||||
return [line, arcn_pt(mx_pt, my_pt, self.r_pt, phi-deltaphi, phi+deltaphi)]
|
return [line, arcn_pt(mx_pt, my_pt, self.r_pt, phi-deltaphi, phi+deltaphi)]
|
||||||
|
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
# in the degenerate case, we just return a line as specified by the PS
|
# in the degenerate case, we just return a line as specified by the PS
|
||||||
# language reference
|
# language reference
|
||||||
return [lineto_pt(self.x1_pt, self.y1_pt)]
|
return [lineto_pt(self.x1_pt, self.y1_pt)]
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue