mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into s8_single_port
This commit is contained in:
commit
e1d7d9dff7
|
|
@ -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))
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ class _pins:
|
||||||
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())
|
||||||
|
|
@ -29,24 +29,24 @@ class _cell:
|
||||||
return {'vdd': 'vdd',
|
return {'vdd': 'vdd',
|
||||||
'gnd': 'gnd'}
|
'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:
|
class _ptx:
|
||||||
def __init__(self, model_is_subckt, bin_spice_models):
|
def __init__(self, model_is_subckt, bin_spice_models):
|
||||||
self.model_is_subckt = model_is_subckt
|
self.model_is_subckt = model_is_subckt
|
||||||
self.bin_spice_models = bin_spice_models
|
self.bin_spice_models = bin_spice_models
|
||||||
|
|
||||||
|
|
||||||
class _pgate:
|
class _pgate:
|
||||||
def __init__(self, add_implants):
|
def __init__(self, add_implants):
|
||||||
self.add_implants = 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
|
||||||
|
|
@ -110,25 +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
|
||||||
|
|
@ -142,14 +142,14 @@ class cell_properties():
|
||||||
|
|
||||||
self._ptx = _ptx(model_is_subckt=False,
|
self._ptx = _ptx(model_is_subckt=False,
|
||||||
bin_spice_models=False)
|
bin_spice_models=False)
|
||||||
|
|
||||||
self._pgate = _pgate(add_implants=False)
|
self._pgate = _pgate(add_implants=False)
|
||||||
|
|
||||||
self._dff = _dff(use_custom_ports=False,
|
self._dff = _dff(use_custom_ports=False,
|
||||||
custom_port_list=["D", "Q", "clk", "vdd", "gnd"],
|
custom_port_list=["D", "Q", "clk", "vdd", "gnd"],
|
||||||
custom_type_list=["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
|
custom_type_list=["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
|
||||||
clk_pin="clk")
|
clk_pin="clk")
|
||||||
|
|
||||||
self._dff_buff = _dff_buff(use_custom_ports=False,
|
self._dff_buff = _dff_buff(use_custom_ports=False,
|
||||||
custom_buff_ports=["D", "qint", "clk", "vdd", "gnd"],
|
custom_buff_ports=["D", "qint", "clk", "vdd", "gnd"],
|
||||||
add_body_contacts=False)
|
add_body_contacts=False)
|
||||||
|
|
@ -176,7 +176,7 @@ class cell_properties():
|
||||||
@property
|
@property
|
||||||
def ptx(self):
|
def ptx(self):
|
||||||
return self._ptx
|
return self._ptx
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pgate(self):
|
def pgate(self):
|
||||||
return self._pgate
|
return self._pgate
|
||||||
|
|
@ -184,7 +184,7 @@ class cell_properties():
|
||||||
@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
|
||||||
|
|
@ -200,7 +200,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
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class _bank:
|
class _bank:
|
||||||
def __init__(self, stack, pitch):
|
def __init__(self, stack, pitch):
|
||||||
# bank
|
# bank
|
||||||
|
|
@ -15,8 +15,8 @@ class _bank:
|
||||||
# m2_stack, m3_pitch (sky130)
|
# m2_stack, m3_pitch (sky130)
|
||||||
self.stack = stack
|
self.stack = stack
|
||||||
self.pitch = pitch
|
self.pitch = pitch
|
||||||
|
|
||||||
|
|
||||||
class _hierarchical_decoder:
|
class _hierarchical_decoder:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
bus_layer,
|
bus_layer,
|
||||||
|
|
@ -60,7 +60,7 @@ class _hierarchical_predecode:
|
||||||
self.output_layer = output_layer
|
self.output_layer = output_layer
|
||||||
self.vertical_supply = vertical_supply
|
self.vertical_supply = vertical_supply
|
||||||
|
|
||||||
|
|
||||||
class _column_mux_array:
|
class _column_mux_array:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
select_layer,
|
select_layer,
|
||||||
|
|
@ -74,7 +74,7 @@ class _column_mux_array:
|
||||||
self.select_pitch= select_pitch
|
self.select_pitch= select_pitch
|
||||||
self.bitline_layer = bitline_layer
|
self.bitline_layer = bitline_layer
|
||||||
|
|
||||||
|
|
||||||
class _port_address:
|
class _port_address:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
supply_offset):
|
supply_offset):
|
||||||
|
|
@ -82,7 +82,7 @@ class _port_address:
|
||||||
# special supply offset
|
# special supply offset
|
||||||
self.supply_offset = supply_offset
|
self.supply_offset = supply_offset
|
||||||
|
|
||||||
|
|
||||||
class _port_data:
|
class _port_data:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
channel_route_bitlines,
|
channel_route_bitlines,
|
||||||
|
|
@ -94,7 +94,7 @@ class _port_data:
|
||||||
# en_layer
|
# en_layer
|
||||||
# m1
|
# m1
|
||||||
# m3 (sky130)
|
# m3 (sky130)
|
||||||
|
|
||||||
# precharge_array
|
# precharge_array
|
||||||
# en_bar_layer
|
# en_bar_layer
|
||||||
# m1
|
# m1
|
||||||
|
|
@ -110,7 +110,7 @@ class _replica_column:
|
||||||
# even row check (sky130)
|
# even row check (sky130)
|
||||||
self.even_rows = even_rows
|
self.even_rows = even_rows
|
||||||
|
|
||||||
|
|
||||||
class _wordline_driver:
|
class _wordline_driver:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
vertical_supply):
|
vertical_supply):
|
||||||
|
|
@ -122,14 +122,14 @@ class _wordline_driver:
|
||||||
# vertical vdd/gnd (sky130)
|
# vertical vdd/gnd (sky130)
|
||||||
self.vertical_supply = vertical_supply
|
self.vertical_supply = vertical_supply
|
||||||
|
|
||||||
|
|
||||||
class layer_properties():
|
class layer_properties():
|
||||||
"""
|
"""
|
||||||
This contains meta information about the module routing layers. These
|
This contains meta information about the module routing layers. These
|
||||||
can be overriden in the tech.py file.
|
can be overriden in the tech.py file.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self._bank = _bank(stack="m1_stack",
|
self._bank = _bank(stack="m1_stack",
|
||||||
pitch="m2_pitch")
|
pitch="m2_pitch")
|
||||||
|
|
||||||
|
|
@ -138,7 +138,7 @@ class layer_properties():
|
||||||
input_layer="m1",
|
input_layer="m1",
|
||||||
output_layer="m3",
|
output_layer="m3",
|
||||||
vertical_supply=False)
|
vertical_supply=False)
|
||||||
|
|
||||||
self._hierarchical_predecode = _hierarchical_predecode(bus_layer="m2",
|
self._hierarchical_predecode = _hierarchical_predecode(bus_layer="m2",
|
||||||
bus_directions="pref",
|
bus_directions="pref",
|
||||||
bus_space_factor=1,
|
bus_space_factor=1,
|
||||||
|
|
@ -156,13 +156,13 @@ class layer_properties():
|
||||||
enable_layer="m1")
|
enable_layer="m1")
|
||||||
|
|
||||||
self._replica_column = _replica_column(even_rows=False)
|
self._replica_column = _replica_column(even_rows=False)
|
||||||
|
|
||||||
self._wordline_driver = _wordline_driver(vertical_supply=False)
|
self._wordline_driver = _wordline_driver(vertical_supply=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bank(self):
|
def bank(self):
|
||||||
return self._bank
|
return self._bank
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def column_mux_array(self):
|
def column_mux_array(self):
|
||||||
return self._column_mux_array
|
return self._column_mux_array
|
||||||
|
|
@ -174,7 +174,7 @@ class layer_properties():
|
||||||
@property
|
@property
|
||||||
def hierarchical_predecode(self):
|
def hierarchical_predecode(self):
|
||||||
return self._hierarchical_predecode
|
return self._hierarchical_predecode
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def port_address(self):
|
def port_address(self):
|
||||||
return self._port_address
|
return self._port_address
|
||||||
|
|
@ -190,4 +190,4 @@ class layer_properties():
|
||||||
@property
|
@property
|
||||||
def wordline_driver(self):
|
def wordline_driver(self):
|
||||||
return self._wordline_driver
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,22 +9,30 @@ 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 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
|
||||||
|
# depending on the number of ports.
|
||||||
|
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):
|
||||||
for pin_name in self.pins:
|
for pin_name in self.pins:
|
||||||
pins = self.get_pins(pin_name)
|
pins = self.get_pins(pin_name)
|
||||||
|
|
@ -52,7 +60,7 @@ class design(hierarchy_design):
|
||||||
match = re.search(r"minarea_(.*)", rule)
|
match = re.search(r"minarea_(.*)", rule)
|
||||||
if match:
|
if match:
|
||||||
setattr(design, 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):
|
||||||
|
|
@ -63,7 +71,7 @@ class design(hierarchy_design):
|
||||||
drc(match.group(0)))
|
drc(match.group(0)))
|
||||||
else:
|
else:
|
||||||
setattr(design, 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(design, match.group(0), drc(match.group(0)))
|
setattr(design, match.group(0), drc(match.group(0)))
|
||||||
|
|
@ -94,7 +102,7 @@ class design(hierarchy_design):
|
||||||
design.well_enclose_active = max(design.pwell_enclose_active,
|
design.well_enclose_active = max(design.pwell_enclose_active,
|
||||||
design.nwell_enclose_active,
|
design.nwell_enclose_active,
|
||||||
design.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", design.poly_width)
|
print("poly_width", design.poly_width)
|
||||||
|
|
@ -127,7 +135,7 @@ class design(hierarchy_design):
|
||||||
These are some layer constants used
|
These are some layer constants used
|
||||||
in many places in the compiler.
|
in many places in the compiler.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from tech import layer_indices
|
from tech import layer_indices
|
||||||
import tech
|
import tech
|
||||||
for layer in layer_indices:
|
for layer in layer_indices:
|
||||||
|
|
@ -143,17 +151,17 @@ class design(hierarchy_design):
|
||||||
# Skip computing the pitch for active
|
# Skip computing the pitch for active
|
||||||
if layer == "active":
|
if layer == "active":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Add the pitch
|
# Add the pitch
|
||||||
setattr(design,
|
setattr(design,
|
||||||
"{}_pitch".format(layer),
|
"{}_pitch".format(layer),
|
||||||
design.compute_pitch(layer, True))
|
design.compute_pitch(layer, True))
|
||||||
|
|
||||||
# Add the non-preferrd pitch (which has vias in the "wrong" way)
|
# Add the non-preferrd pitch (which has vias in the "wrong" way)
|
||||||
setattr(design,
|
setattr(design,
|
||||||
"{}_nonpref_pitch".format(layer),
|
"{}_nonpref_pitch".format(layer),
|
||||||
design.compute_pitch(layer, False))
|
design.compute_pitch(layer, False))
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from tech import preferred_directions
|
from tech import preferred_directions
|
||||||
print(preferred_directions)
|
print(preferred_directions)
|
||||||
|
|
@ -173,9 +181,9 @@ class design(hierarchy_design):
|
||||||
import sys
|
import sys
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def compute_pitch(layer, preferred=True):
|
def compute_pitch(layer, preferred=True):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This is the preferred direction pitch
|
This is the preferred direction pitch
|
||||||
i.e. we take the minimum or maximum contact dimension
|
i.e. we take the minimum or maximum contact dimension
|
||||||
|
|
@ -195,7 +203,7 @@ class design(hierarchy_design):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_preferred_direction(layer):
|
def get_preferred_direction(layer):
|
||||||
return preferred_directions[layer]
|
return preferred_directions[layer]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def compute_layer_pitch(layer_stack, preferred):
|
def compute_layer_pitch(layer_stack, preferred):
|
||||||
|
|
||||||
|
|
@ -228,7 +236,7 @@ class design(hierarchy_design):
|
||||||
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
@ -266,14 +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_drc_constants()
|
||||||
design.setup_layer_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 OPTS.purge_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,11 +105,11 @@ 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 OPTS.purge_temp:
|
||||||
|
|
@ -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 OPTS.purge_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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from tech import drc, GDS
|
||||||
from tech import layer as techlayer
|
from tech import layer as techlayer
|
||||||
from tech import layer_indices
|
from tech import layer_indices
|
||||||
from tech import layer_stacks
|
from tech import layer_stacks
|
||||||
|
from tech import preferred_directions
|
||||||
import os
|
import os
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from vector import vector
|
from vector import vector
|
||||||
|
|
@ -30,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
|
||||||
|
|
@ -66,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
|
||||||
|
|
@ -214,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))
|
||||||
|
|
@ -299,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
|
||||||
|
|
@ -315,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):
|
||||||
"""
|
"""
|
||||||
|
|
@ -612,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,
|
||||||
|
|
@ -643,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,
|
||||||
|
|
@ -734,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,
|
||||||
|
|
@ -886,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)):
|
||||||
|
|
@ -904,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
|
||||||
|
|
@ -1042,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:
|
||||||
|
|
@ -1107,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.
|
||||||
|
|
@ -1166,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)
|
||||||
|
|
@ -1184,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.
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class lef:
|
||||||
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,7 +51,7 @@ 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))
|
||||||
|
|
@ -59,25 +59,25 @@ class lef:
|
||||||
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 += " "
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ class lef:
|
||||||
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,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))
|
||||||
|
|
@ -94,7 +94,7 @@ class lef:
|
||||||
# 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))
|
||||||
|
|
@ -111,16 +111,16 @@ class lef:
|
||||||
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)):
|
||||||
|
|
|
||||||
|
|
@ -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,9 +6,8 @@
|
||||||
# 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
|
||||||
|
from globals import OPTS
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,8 +19,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,20 +27,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, cell_name=None):
|
||||||
GDS["unit"],
|
if not cell_name:
|
||||||
layer["boundary"])
|
cell_name = OPTS.bitcell_name
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
|
super().__init__(name, cell_name)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
def get_all_wl_names(self):
|
def get_all_wl_names(self):
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,9 @@
|
||||||
# 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
|
||||||
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class bitcell_1rw_1r(bitcell_base.bitcell_base):
|
class bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
|
|
@ -29,33 +27,25 @@ 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, cell_name=None):
|
||||||
# Ignore the name argument
|
if not cell_name:
|
||||||
bitcell_base.bitcell_base.__init__(self, "cell_1rw_1r")
|
cell_name = OPTS.bitcell_name
|
||||||
|
super().__init__(name, cell_name)
|
||||||
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,10 +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_1w_1r(bitcell_base.bitcell_base):
|
class bitcell_1w_1r(bitcell_base.bitcell_base):
|
||||||
|
|
@ -31,28 +30,20 @@ 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, cell_name=None):
|
||||||
# Ignore the name argument
|
if not cell_name:
|
||||||
bitcell_base.bitcell_base.__init__(self, "cell_1w_1r")
|
cell_name = OPTS.bitcell_name
|
||||||
|
super().__init__(name, cell_name)
|
||||||
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,17 +8,29 @@
|
||||||
|
|
||||||
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"
|
||||||
design.design.__init__(self, name)
|
|
||||||
|
def __init__(self, name, cell_name, hard_cell=True):
|
||||||
|
design.design.__init__(self, name, cell_name)
|
||||||
|
|
||||||
|
if hard_cell:
|
||||||
|
(self.width, self.height) = utils.get_libcell_size(cell_name,
|
||||||
|
GDS["unit"],
|
||||||
|
layer[self.cell_size_layer])
|
||||||
|
self.pin_map = utils.get_libcell_pins(self.pin_names,
|
||||||
|
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
|
||||||
|
|
@ -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,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
|
||||||
|
|
||||||
|
|
@ -21,24 +19,14 @@ class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
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", cell_name=None):
|
||||||
GDS["unit"],
|
if not cell_name:
|
||||||
layer["boundary"])
|
cell_name = name
|
||||||
pin_map = utils.get_libcell_pins(pin_names,
|
|
||||||
"col_cap_cell_1rw_1r",
|
|
||||||
GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name=""):
|
|
||||||
# Ignore the name argument
|
# Ignore the name argument
|
||||||
bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r")
|
bitcell_base.bitcell_base.__init__(self, name, cell_name)
|
||||||
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,10 +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 dummy_bitcell(bitcell_base.bitcell_base):
|
class dummy_bitcell(bitcell_base.bitcell_base):
|
||||||
|
|
@ -24,19 +23,12 @@ 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, cell_name=None):
|
||||||
GDS["unit"],
|
if not cell_name:
|
||||||
layer["boundary"])
|
cell_name = OPTS.dummy_bitcell_name
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"])
|
super().__init__(name, cell_name)
|
||||||
|
|
||||||
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,10 +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 dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
|
|
@ -27,23 +26,13 @@ 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, cell_name=None):
|
||||||
# Ignore the name argument
|
if not cell_name:
|
||||||
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r")
|
cell_name = OPTS.dummy_bitcell_name
|
||||||
|
super().__init__(name, cell_name)
|
||||||
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,10 +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 dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
|
class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
|
||||||
|
|
@ -29,21 +28,13 @@ 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, cell_name=None):
|
||||||
# Ignore the name argument
|
if not cell_name:
|
||||||
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r")
|
cell_name = OPTS.dummy_bitcell_name
|
||||||
|
super().__init__(name, cell_name)
|
||||||
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,88 @@
|
||||||
#
|
#
|
||||||
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):
|
||||||
|
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 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
|
||||||
|
|
|
||||||
|
|
@ -21,16 +21,18 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
with a variable number of read/write, write, and read ports
|
with a variable number of read/write, write, and read ports
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, replica_bitcell=False, dummy_bitcell=False):
|
def __init__(self, name, cell_name=None, replica_bitcell=False, dummy_bitcell=False):
|
||||||
|
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
|
||||||
|
|
||||||
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, cell_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 +297,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 +324,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 +408,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 +423,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 +435,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 +463,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 +664,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 +899,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 +919,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 +939,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 +1018,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 +1026,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 +1101,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 +1121,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 +1130,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 +1146,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 +1157,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,15 @@
|
||||||
# (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 utils
|
||||||
from tech import GDS,layer,drc,parameter,cell_properties
|
import bitcell_base
|
||||||
|
from tech import GDS, layer
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
|
|
||||||
from globals import OPTS
|
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,38 +26,28 @@ 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, cell_name=None):
|
||||||
|
if not cell_name:
|
||||||
|
cell_name = OPTS.replica_bitcell_name
|
||||||
# Ignore the name argument
|
# Ignore the name argument
|
||||||
design.design.__init__(self, "replica_cell_6t")
|
super().__init__(name, cell_name)
|
||||||
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
|
||||||
|
|
@ -66,6 +56,6 @@ class replica_bitcell(design.design):
|
||||||
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,13 @@
|
||||||
# (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 globals import OPTS
|
||||||
|
|
||||||
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,31 +26,24 @@ 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, cell_name=None):
|
||||||
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
|
if not cell_name:
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"])
|
cell_name = OPTS.replica_bitcell_name
|
||||||
|
super().__init__(name, cell_name)
|
||||||
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"]
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,15 @@
|
||||||
# (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 globals import OPTS
|
||||||
|
from tech import GDS, layer
|
||||||
|
import utils
|
||||||
|
|
||||||
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,20 +28,14 @@ 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, cell_name=None):
|
||||||
(width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"])
|
if not cell_name:
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"])
|
cell_name = OPTS.replica_bitcell_name
|
||||||
|
super().__init__(name, cell_name)
|
||||||
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
|
||||||
|
|
@ -47,10 +43,10 @@ class replica_bitcell_1w_1r(design.design):
|
||||||
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"]
|
||||||
|
|
|
||||||
|
|
@ -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,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
|
||||||
|
|
||||||
|
|
@ -22,23 +20,12 @@ class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
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", cell_name=None):
|
||||||
# Ignore the name argument
|
if not cell_name:
|
||||||
bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r")
|
cell_name = name
|
||||||
|
bitcell_base.bitcell_base.__init__(self, name, cell_name)
|
||||||
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
|
|
@ -23,7 +23,7 @@ class functional(simulation):
|
||||||
|
|
||||||
def __init__(self, sram, spfile, corner, cycles=15):
|
def __init__(self, sram, spfile, corner, cycles=15):
|
||||||
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)
|
||||||
|
|
@ -50,7 +50,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
|
||||||
|
|
@ -63,16 +63,16 @@ class functional(simulation):
|
||||||
self.period = feasible_period
|
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
|
# Run SPICE simulation
|
||||||
self.write_functional_stimulus()
|
self.write_functional_stimulus()
|
||||||
self.stim.run_sim()
|
self.stim.run_sim()
|
||||||
|
|
||||||
# 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 +94,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 +123,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 +138,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 +151,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 +191,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 +203,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 +211,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 +222,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 +247,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 +307,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,10 +324,10 @@ 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)
|
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||||
|
|
@ -341,7 +341,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 +361,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 +384,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 +417,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 +428,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 +444,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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class setup_hold():
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -60,9 +60,9 @@ 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)
|
||||||
|
|
||||||
|
|
@ -102,14 +102,14 @@ 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
|
||||||
|
|
@ -117,7 +117,7 @@ class setup_hold():
|
||||||
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -154,7 +154,7 @@ class setup_hold():
|
||||||
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
|
||||||
|
|
@ -167,14 +167,14 @@ class setup_hold():
|
||||||
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.
|
||||||
|
|
@ -189,8 +189,8 @@ class setup_hold():
|
||||||
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()
|
||||||
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"))
|
||||||
|
|
@ -204,18 +204,18 @@ class setup_hold():
|
||||||
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,
|
||||||
|
|
@ -245,7 +245,7 @@ class setup_hold():
|
||||||
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
|
||||||
|
|
@ -261,7 +261,7 @@ class setup_hold():
|
||||||
"""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
|
||||||
"""
|
"""
|
||||||
|
|
@ -283,7 +283,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,15 +293,15 @@ 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))
|
||||||
|
|
@ -317,7 +317,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,
|
||||||
|
|
@ -332,7 +332,7 @@ class setup_hold():
|
||||||
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
|
||||||
|
|
@ -340,7 +340,7 @@ class setup_hold():
|
||||||
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,7 +31,7 @@ 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
|
found = False
|
||||||
self.device_libraries = []
|
self.device_libraries = []
|
||||||
|
|
@ -48,10 +48,10 @@ class stimuli():
|
||||||
pass
|
pass
|
||||||
if not found:
|
if not found:
|
||||||
debug.error("Must define either fet_libraries or fet_models.", -1)
|
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:
|
||||||
|
|
@ -59,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:
|
||||||
|
|
@ -99,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,
|
||||||
|
|
@ -124,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
|
||||||
|
|
@ -152,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
|
||||||
|
|
@ -185,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"
|
||||||
|
|
@ -198,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"
|
||||||
|
|
@ -208,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"
|
||||||
|
|
@ -227,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:
|
||||||
|
|
@ -245,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))
|
||||||
|
|
@ -281,9 +281,9 @@ class stimuli():
|
||||||
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:
|
else:
|
||||||
debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]))
|
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]
|
includes = self.device_models + [circuit]
|
||||||
|
|
||||||
for item in list(includes):
|
for item in list(includes):
|
||||||
if os.path.isfile(item):
|
if os.path.isfile(item):
|
||||||
self.sf.write(".include \"{0}\"\n".format(item))
|
self.sf.write(".include \"{0}\"\n".format(item))
|
||||||
|
|
@ -305,7 +305,7 @@ class stimuli():
|
||||||
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")
|
||||||
|
|
@ -340,13 +340,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:
|
||||||
|
|
@ -354,4 +354,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')):
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class pattern(canvas._canvas, attr.exclusiveattr, style.fillstyle):
|
||||||
self.patternbbox = bbox
|
self.patternbbox = bbox
|
||||||
self.patterntrafo = trafo
|
self.patterntrafo = trafo
|
||||||
|
|
||||||
def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker,
|
def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker,
|
||||||
bbox=_marker, trafo=_marker):
|
bbox=_marker, trafo=_marker):
|
||||||
if painttype is _marker:
|
if painttype is _marker:
|
||||||
painttype = self.painttype
|
painttype = self.painttype
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ def set(epsilon=None):
|
||||||
def _rmatrix(angle):
|
def _rmatrix(angle):
|
||||||
phi = math.pi*angle/180.0
|
phi = math.pi*angle/180.0
|
||||||
|
|
||||||
return ((math.cos(phi), -math.sin(phi)),
|
return ((math.cos(phi), -math.sin(phi)),
|
||||||
(math.sin(phi), math.cos(phi)))
|
(math.sin(phi), math.cos(phi)))
|
||||||
|
|
||||||
def _rvector(angle, x, y):
|
def _rvector(angle, x, y):
|
||||||
|
|
@ -198,7 +198,7 @@ class trafo(trafo_pt):
|
||||||
epsilon=epsilon)
|
epsilon=epsilon)
|
||||||
|
|
||||||
#
|
#
|
||||||
# some standard transformations
|
# some standard transformations
|
||||||
#
|
#
|
||||||
|
|
||||||
class mirror(trafo):
|
class mirror(trafo):
|
||||||
|
|
@ -219,7 +219,7 @@ class rotate_pt(trafo_pt):
|
||||||
|
|
||||||
class rotate(trafo_pt):
|
class rotate(trafo_pt):
|
||||||
def __init__(self, angle, x=None, y=None, epsilon=_marker):
|
def __init__(self, angle, x=None, y=None, epsilon=_marker):
|
||||||
vector = 0, 0
|
vector = 0, 0
|
||||||
if x is not None or y is not None:
|
if x is not None or y is not None:
|
||||||
if x is None or y is None:
|
if x is None or y is None:
|
||||||
raise TrafoException("either specify both x and y or none of them")
|
raise TrafoException("either specify both x and y or none of them")
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ scale = { 't':1, 'u':1, 'v':1, 'w':1, 'x':1 }
|
||||||
|
|
||||||
_default_unit = "cm"
|
_default_unit = "cm"
|
||||||
|
|
||||||
_m = {
|
_m = {
|
||||||
'm' : 1,
|
'm' : 1,
|
||||||
'cm': 0.01,
|
'cm': 0.01,
|
||||||
'mm': 0.001,
|
'mm': 0.001,
|
||||||
|
|
@ -51,7 +51,7 @@ def set(uscale=None, vscale=None, wscale=None, xscale=None, defaultunit=None):
|
||||||
def _convert_to(l, dest_unit="m"):
|
def _convert_to(l, dest_unit="m"):
|
||||||
if type(l) in (types.IntType, types.LongType, types.FloatType):
|
if type(l) in (types.IntType, types.LongType, types.FloatType):
|
||||||
return l * _m[_default_unit] * scale['u'] / _m[dest_unit]
|
return l * _m[_default_unit] * scale['u'] / _m[dest_unit]
|
||||||
elif not isinstance(l, length):
|
elif not isinstance(l, length):
|
||||||
l = length(l) # convert to length instance if necessary
|
l = length(l) # convert to length instance if necessary
|
||||||
|
|
||||||
return (l.t + l.u*scale['u'] + l.v*scale['v'] + l.w*scale['w'] + l.x*scale['x']) / _m[dest_unit]
|
return (l.t + l.u*scale['u'] + l.v*scale['v'] + l.w*scale['w'] + l.x*scale['x']) / _m[dest_unit]
|
||||||
|
|
|
||||||
|
|
@ -144,18 +144,18 @@ def check_versions():
|
||||||
minor_required = 5
|
minor_required = 5
|
||||||
if not (major_python_version == major_required and minor_python_version >= minor_required):
|
if not (major_python_version == major_required and minor_python_version >= minor_required):
|
||||||
debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1)
|
debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1)
|
||||||
|
|
||||||
# FIXME: Check versions of other tools here??
|
# FIXME: Check versions of other tools here??
|
||||||
# or, this could be done in each module (e.g. verify, characterizer, etc.)
|
# or, this could be done in each module (e.g. verify, characterizer, etc.)
|
||||||
global OPTS
|
global OPTS
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import coverage
|
import coverage
|
||||||
OPTS.coverage = 1
|
OPTS.coverage = 1
|
||||||
except:
|
except:
|
||||||
OPTS.coverage = 0
|
OPTS.coverage = 0
|
||||||
|
|
||||||
|
|
||||||
def init_openram(config_file, is_unit_test=True):
|
def init_openram(config_file, is_unit_test=True):
|
||||||
""" Initialize the technology, paths, simulators, etc. """
|
""" Initialize the technology, paths, simulators, etc. """
|
||||||
|
|
||||||
|
|
@ -164,7 +164,7 @@ def init_openram(config_file, is_unit_test=True):
|
||||||
debug.info(1, "Initializing OpenRAM...")
|
debug.info(1, "Initializing OpenRAM...")
|
||||||
|
|
||||||
setup_paths()
|
setup_paths()
|
||||||
|
|
||||||
read_config(config_file, is_unit_test)
|
read_config(config_file, is_unit_test)
|
||||||
|
|
||||||
import_tech()
|
import_tech()
|
||||||
|
|
@ -188,7 +188,10 @@ def init_openram(config_file, is_unit_test=True):
|
||||||
if is_unit_test and CHECKPOINT_OPTS:
|
if is_unit_test and CHECKPOINT_OPTS:
|
||||||
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
|
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Setup correct bitcell names
|
||||||
|
setup_bitcell()
|
||||||
|
|
||||||
# Import these to find the executables for checkpointing
|
# Import these to find the executables for checkpointing
|
||||||
import characterizer
|
import characterizer
|
||||||
import verify
|
import verify
|
||||||
|
|
@ -197,22 +200,19 @@ def init_openram(config_file, is_unit_test=True):
|
||||||
if not CHECKPOINT_OPTS:
|
if not CHECKPOINT_OPTS:
|
||||||
CHECKPOINT_OPTS = copy.copy(OPTS)
|
CHECKPOINT_OPTS = copy.copy(OPTS)
|
||||||
|
|
||||||
|
|
||||||
def setup_bitcell():
|
def setup_bitcell():
|
||||||
"""
|
"""
|
||||||
Determine the correct custom or parameterized bitcell for the design.
|
Determine the correct custom or parameterized bitcell for the design.
|
||||||
"""
|
"""
|
||||||
global OPTS
|
|
||||||
|
|
||||||
# If we have non-1rw ports,
|
# If we have non-1rw ports,
|
||||||
# and the user didn't over-ride the bitcell manually,
|
# and the user didn't over-ride the bitcell manually,
|
||||||
# figure out the right bitcell to use
|
# figure out the right bitcell to use
|
||||||
if (OPTS.bitcell == "bitcell"):
|
if (OPTS.bitcell == "bitcell"):
|
||||||
|
|
||||||
if (OPTS.num_rw_ports == 1 and OPTS.num_w_ports == 0 and OPTS.num_r_ports == 0):
|
if (OPTS.num_rw_ports == 1 and OPTS.num_w_ports == 0 and OPTS.num_r_ports == 0):
|
||||||
OPTS.bitcell = "bitcell"
|
OPTS.bitcell = "bitcell"
|
||||||
OPTS.replica_bitcell = "replica_bitcell"
|
OPTS.bitcell_name = "cell_6t"
|
||||||
OPTS.dummy_bitcell = "dummy_bitcell"
|
|
||||||
else:
|
else:
|
||||||
ports = ""
|
ports = ""
|
||||||
if OPTS.num_rw_ports > 0:
|
if OPTS.num_rw_ports > 0:
|
||||||
|
|
@ -225,6 +225,20 @@ def setup_bitcell():
|
||||||
if ports != "":
|
if ports != "":
|
||||||
OPTS.bitcell_suffix = "_" + ports
|
OPTS.bitcell_suffix = "_" + ports
|
||||||
OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix
|
OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix
|
||||||
|
OPTS.bitcell_name = "cell" + OPTS.bitcell_suffix
|
||||||
|
|
||||||
|
OPTS.dummy_bitcell = "dummy_" + OPTS.bitcell
|
||||||
|
OPTS.dummy_bitcell_name = "dummy_" + OPTS.bitcell_name
|
||||||
|
|
||||||
|
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
|
||||||
|
OPTS.replica_bitcell_name = "replica_" + OPTS.bitcell_name
|
||||||
|
elif (OPTS.bitcell == "pbitcell"):
|
||||||
|
OPTS.bitcell = "pbitcell"
|
||||||
|
OPTS.bitcell_name = "pbitcell"
|
||||||
|
OPTS.dummy_bitcell = "dummy_pbitcell"
|
||||||
|
OPTS.dummy_bitcell_name = "dummy_pbitcell"
|
||||||
|
OPTS.replica_bitcell = "replica_pbitcell"
|
||||||
|
OPTS.replica_bitcell_name = "replica_pbitcell"
|
||||||
|
|
||||||
# See if bitcell exists
|
# See if bitcell exists
|
||||||
try:
|
try:
|
||||||
|
|
@ -234,6 +248,11 @@ def setup_bitcell():
|
||||||
# or its custom replica bitcell
|
# or its custom replica bitcell
|
||||||
# Use the pbitcell (and give a warning if not in unit test mode)
|
# Use the pbitcell (and give a warning if not in unit test mode)
|
||||||
OPTS.bitcell = "pbitcell"
|
OPTS.bitcell = "pbitcell"
|
||||||
|
OPTS.bitcell_name = "pbitcell"
|
||||||
|
OPTS.dummy_bitcell = "dummy_pbitcell"
|
||||||
|
OPTS.dummy_bitcell_name = "dummy_pbitcell"
|
||||||
|
OPTS.replica_bitcell = "replica_pbitcell"
|
||||||
|
OPTS.replica_bitcell_name = "replica_pbitcell"
|
||||||
if not OPTS.is_unit_test:
|
if not OPTS.is_unit_test:
|
||||||
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
|
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
|
||||||
debug.info(1, "Using bitcell: {}".format(OPTS.bitcell))
|
debug.info(1, "Using bitcell: {}".format(OPTS.bitcell))
|
||||||
|
|
@ -269,7 +288,7 @@ def get_tool(tool_type, preferences, default_name=None):
|
||||||
else:
|
else:
|
||||||
return(None, "")
|
return(None, "")
|
||||||
|
|
||||||
|
|
||||||
def read_config(config_file, is_unit_test=True):
|
def read_config(config_file, is_unit_test=True):
|
||||||
"""
|
"""
|
||||||
Read the configuration file that defines a few parameters. The
|
Read the configuration file that defines a few parameters. The
|
||||||
|
|
@ -282,14 +301,14 @@ def read_config(config_file, is_unit_test=True):
|
||||||
# it is already not an abs path, make it one
|
# it is already not an abs path, make it one
|
||||||
if not os.path.isabs(config_file):
|
if not os.path.isabs(config_file):
|
||||||
config_file = os.getcwd() + "/" + config_file
|
config_file = os.getcwd() + "/" + config_file
|
||||||
|
|
||||||
# Make it a python file if the base name was only given
|
# Make it a python file if the base name was only given
|
||||||
config_file = re.sub(r'\.py$', "", config_file)
|
config_file = re.sub(r'\.py$', "", config_file)
|
||||||
|
|
||||||
|
|
||||||
# Expand the user if it is used
|
# Expand the user if it is used
|
||||||
config_file = os.path.expanduser(config_file)
|
config_file = os.path.expanduser(config_file)
|
||||||
|
|
||||||
OPTS.config_file = config_file + ".py"
|
OPTS.config_file = config_file + ".py"
|
||||||
# Add the path to the system path
|
# Add the path to the system path
|
||||||
# so we can import things in the other directory
|
# so we can import things in the other directory
|
||||||
|
|
@ -328,7 +347,7 @@ def read_config(config_file, is_unit_test=True):
|
||||||
# If we are only generating a netlist, we can't do DRC/LVS
|
# If we are only generating a netlist, we can't do DRC/LVS
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
OPTS.check_lvsdrc = False
|
OPTS.check_lvsdrc = False
|
||||||
|
|
||||||
# If config didn't set output name, make a reasonable default.
|
# If config didn't set output name, make a reasonable default.
|
||||||
if (OPTS.output_name == ""):
|
if (OPTS.output_name == ""):
|
||||||
ports = ""
|
ports = ""
|
||||||
|
|
@ -343,7 +362,7 @@ def read_config(config_file, is_unit_test=True):
|
||||||
ports,
|
ports,
|
||||||
OPTS.tech_name)
|
OPTS.tech_name)
|
||||||
|
|
||||||
|
|
||||||
def end_openram():
|
def end_openram():
|
||||||
""" Clean up openram for a proper exit """
|
""" Clean up openram for a proper exit """
|
||||||
cleanup_paths()
|
cleanup_paths()
|
||||||
|
|
@ -353,8 +372,8 @@ def end_openram():
|
||||||
verify.print_drc_stats()
|
verify.print_drc_stats()
|
||||||
verify.print_lvs_stats()
|
verify.print_lvs_stats()
|
||||||
verify.print_pex_stats()
|
verify.print_pex_stats()
|
||||||
|
|
||||||
|
|
||||||
def cleanup_paths():
|
def cleanup_paths():
|
||||||
"""
|
"""
|
||||||
We should clean up the temp directory after execution.
|
We should clean up the temp directory after execution.
|
||||||
|
|
@ -376,8 +395,8 @@ def cleanup_paths():
|
||||||
os.remove(i)
|
os.remove(i)
|
||||||
else:
|
else:
|
||||||
shutil.rmtree(i)
|
shutil.rmtree(i)
|
||||||
|
|
||||||
|
|
||||||
def setup_paths():
|
def setup_paths():
|
||||||
""" Set up the non-tech related paths. """
|
""" Set up the non-tech related paths. """
|
||||||
debug.info(2, "Setting up paths...")
|
debug.info(2, "Setting up paths...")
|
||||||
|
|
@ -400,7 +419,7 @@ def setup_paths():
|
||||||
debug.check(os.path.isdir(full_path),
|
debug.check(os.path.isdir(full_path),
|
||||||
"$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path))
|
"$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path))
|
||||||
if "__pycache__" not in full_path:
|
if "__pycache__" not in full_path:
|
||||||
sys.path.append("{0}".format(full_path))
|
sys.path.append("{0}".format(full_path))
|
||||||
|
|
||||||
if not OPTS.openram_temp.endswith('/'):
|
if not OPTS.openram_temp.endswith('/'):
|
||||||
OPTS.openram_temp += "/"
|
OPTS.openram_temp += "/"
|
||||||
|
|
@ -413,9 +432,9 @@ def is_exe(fpath):
|
||||||
|
|
||||||
|
|
||||||
def find_exe(check_exe):
|
def find_exe(check_exe):
|
||||||
"""
|
"""
|
||||||
Check if the binary exists in any path dir
|
Check if the binary exists in any path dir
|
||||||
and return the full path.
|
and return the full path.
|
||||||
"""
|
"""
|
||||||
# Check if the preferred spice option exists in the path
|
# Check if the preferred spice option exists in the path
|
||||||
for path in os.environ["PATH"].split(os.pathsep):
|
for path in os.environ["PATH"].split(os.pathsep):
|
||||||
|
|
@ -437,7 +456,7 @@ def init_paths():
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == 17: # errno.EEXIST
|
if e.errno == 17: # errno.EEXIST
|
||||||
os.chmod(OPTS.openram_temp, 0o750)
|
os.chmod(OPTS.openram_temp, 0o750)
|
||||||
|
|
||||||
# Don't delete the output dir, it may have other files!
|
# Don't delete the output dir, it may have other files!
|
||||||
# make the directory if it doesn't exist
|
# make the directory if it doesn't exist
|
||||||
try:
|
try:
|
||||||
|
|
@ -448,7 +467,7 @@ def init_paths():
|
||||||
except:
|
except:
|
||||||
debug.error("Unable to make output directory.", -1)
|
debug.error("Unable to make output directory.", -1)
|
||||||
|
|
||||||
|
|
||||||
def set_default_corner():
|
def set_default_corner():
|
||||||
""" Set the default corner. """
|
""" Set the default corner. """
|
||||||
|
|
||||||
|
|
@ -459,13 +478,13 @@ def set_default_corner():
|
||||||
OPTS.process_corners = ["TT"]
|
OPTS.process_corners = ["TT"]
|
||||||
else:
|
else:
|
||||||
OPTS.process_corners = tech.spice["fet_models"].keys()
|
OPTS.process_corners = tech.spice["fet_models"].keys()
|
||||||
|
|
||||||
if (OPTS.supply_voltages == ""):
|
if (OPTS.supply_voltages == ""):
|
||||||
if OPTS.nominal_corner_only:
|
if OPTS.nominal_corner_only:
|
||||||
OPTS.supply_voltages = [tech.spice["supply_voltages"][1]]
|
OPTS.supply_voltages = [tech.spice["supply_voltages"][1]]
|
||||||
else:
|
else:
|
||||||
OPTS.supply_voltages = tech.spice["supply_voltages"]
|
OPTS.supply_voltages = tech.spice["supply_voltages"]
|
||||||
|
|
||||||
if (OPTS.temperatures == ""):
|
if (OPTS.temperatures == ""):
|
||||||
if OPTS.nominal_corner_only:
|
if OPTS.nominal_corner_only:
|
||||||
OPTS.temperatures = [tech.spice["temperatures"][1]]
|
OPTS.temperatures = [tech.spice["temperatures"][1]]
|
||||||
|
|
@ -479,8 +498,8 @@ def set_default_corner():
|
||||||
# Load scales are fanout multiples of the default spice input slew
|
# Load scales are fanout multiples of the default spice input slew
|
||||||
if (OPTS.slew_scales == ""):
|
if (OPTS.slew_scales == ""):
|
||||||
OPTS.slew_scales = [0.25, 1, 8]
|
OPTS.slew_scales = [0.25, 1, 8]
|
||||||
|
|
||||||
|
|
||||||
def import_tech():
|
def import_tech():
|
||||||
""" Dynamically adds the tech directory to the path and imports it. """
|
""" Dynamically adds the tech directory to the path and imports it. """
|
||||||
global OPTS
|
global OPTS
|
||||||
|
|
@ -501,7 +520,7 @@ def import_tech():
|
||||||
sys.path.append(tech_path)
|
sys.path.append(tech_path)
|
||||||
debug.info(1, "Adding technology path: {}".format(tech_path))
|
debug.info(1, "Adding technology path: {}".format(tech_path))
|
||||||
|
|
||||||
# Import the tech
|
# Import the tech
|
||||||
try:
|
try:
|
||||||
tech_mod = __import__(OPTS.tech_name)
|
tech_mod = __import__(OPTS.tech_name)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -526,7 +545,7 @@ def import_tech():
|
||||||
def print_time(name, now_time, last_time=None, indentation=2):
|
def print_time(name, now_time, last_time=None, indentation=2):
|
||||||
""" Print a statement about the time delta. """
|
""" Print a statement about the time delta. """
|
||||||
global OPTS
|
global OPTS
|
||||||
|
|
||||||
# Don't print during testing
|
# Don't print during testing
|
||||||
if not OPTS.is_unit_test or OPTS.debug_level > 0:
|
if not OPTS.is_unit_test or OPTS.debug_level > 0:
|
||||||
if last_time:
|
if last_time:
|
||||||
|
|
@ -537,12 +556,12 @@ def print_time(name, now_time, last_time=None, indentation=2):
|
||||||
|
|
||||||
|
|
||||||
def report_status():
|
def report_status():
|
||||||
"""
|
"""
|
||||||
Check for valid arguments and report the
|
Check for valid arguments and report the
|
||||||
info about the SRAM being generated
|
info about the SRAM being generated
|
||||||
"""
|
"""
|
||||||
global OPTS
|
global OPTS
|
||||||
|
|
||||||
# Check if all arguments are integers for bits, size, banks
|
# Check if all arguments are integers for bits, size, banks
|
||||||
if type(OPTS.word_size) != int:
|
if type(OPTS.word_size) != int:
|
||||||
debug.error("{0} is not an integer in config file.".format(OPTS.word_size))
|
debug.error("{0} is not an integer in config file.".format(OPTS.word_size))
|
||||||
|
|
@ -550,7 +569,7 @@ def report_status():
|
||||||
debug.error("{0} is not an integer in config file.".format(OPTS.sram_size))
|
debug.error("{0} is not an integer in config file.".format(OPTS.sram_size))
|
||||||
if type(OPTS.write_size) is not int and OPTS.write_size is not None:
|
if type(OPTS.write_size) is not int and OPTS.write_size is not None:
|
||||||
debug.error("{0} is not an integer in config file.".format(OPTS.write_size))
|
debug.error("{0} is not an integer in config file.".format(OPTS.write_size))
|
||||||
|
|
||||||
# If a write mask is specified by the user, the mask write size should be the same as
|
# If a write mask is specified by the user, the mask write size should be the same as
|
||||||
# the word size so that an entire word is written at once.
|
# the word size so that an entire word is written at once.
|
||||||
if OPTS.write_size is not None:
|
if OPTS.write_size is not None:
|
||||||
|
|
@ -582,10 +601,10 @@ def report_status():
|
||||||
|
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).")
|
debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).")
|
||||||
|
|
||||||
if not OPTS.route_supplies:
|
if not OPTS.route_supplies:
|
||||||
debug.print_raw("Design supply routing skipped. Supplies will have multiple must-connect pins. (route_supplies=True to enable supply routing).")
|
debug.print_raw("Design supply routing skipped. Supplies will have multiple must-connect pins. (route_supplies=True to enable supply routing).")
|
||||||
|
|
||||||
if not OPTS.inline_lvsdrc:
|
if not OPTS.inline_lvsdrc:
|
||||||
debug.print_raw("DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to do inline checking).")
|
debug.print_raw("DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to do inline checking).")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,18 @@ class and2_dec(design.design):
|
||||||
This is an AND with configurable drive strength.
|
This is an AND with configurable drive strength.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||||
|
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
debug.info(1, "Creating and2_dec {}".format(name))
|
debug.info(1, "Creating and2_dec {}".format(name))
|
||||||
self.add_comment("size: {}".format(size))
|
self.add_comment("size: {}".format(size))
|
||||||
self.size = size
|
self.size = size
|
||||||
self.height = height
|
self.height = height
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.create_modules()
|
||||||
|
|
@ -38,14 +38,14 @@ class and2_dec(design.design):
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
self.nand = factory.create(module_type="nand2_dec",
|
self.nand = factory.create(module_type="nand2_dec",
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
self.inv = factory.create(module_type="inv_dec",
|
self.inv = factory.create(module_type="inv_dec",
|
||||||
height=self.height,
|
height=self.height,
|
||||||
size=self.size)
|
size=self.size)
|
||||||
|
|
||||||
self.add_mod(self.nand)
|
self.add_mod(self.nand)
|
||||||
self.add_mod(self.inv)
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
|
|
||||||
if "li" in layer:
|
if "li" in layer:
|
||||||
|
|
@ -54,14 +54,14 @@ class and2_dec(design.design):
|
||||||
self.route_layer = "m1"
|
self.route_layer = "m1"
|
||||||
self.width = self.nand.width + self.inv.width
|
self.width = self.nand.width + self.inv.width
|
||||||
self.height = self.nand.height
|
self.height = self.nand.height
|
||||||
|
|
||||||
self.place_insts()
|
self.place_insts()
|
||||||
self.add_wires()
|
self.add_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
self.route_supply_rails()
|
self.route_supply_rails()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
self.add_pin("A", "INPUT")
|
self.add_pin("A", "INPUT")
|
||||||
self.add_pin("B", "INPUT")
|
self.add_pin("B", "INPUT")
|
||||||
|
|
@ -73,7 +73,7 @@ class and2_dec(design.design):
|
||||||
self.nand_inst = self.add_inst(name="pand2_dec_nand",
|
self.nand_inst = self.add_inst(name="pand2_dec_nand",
|
||||||
mod=self.nand)
|
mod=self.nand)
|
||||||
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
|
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
self.inv_inst = self.add_inst(name="pand2_dec_inv",
|
self.inv_inst = self.add_inst(name="pand2_dec_inv",
|
||||||
mod=self.inv)
|
mod=self.inv)
|
||||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
@ -100,7 +100,7 @@ class and2_dec(design.design):
|
||||||
layer=self.route_layer,
|
layer=self.route_layer,
|
||||||
offset=vector(0.5 * self.width, self.height),
|
offset=vector(0.5 * self.width, self.height),
|
||||||
width=self.width)
|
width=self.width)
|
||||||
|
|
||||||
def add_wires(self):
|
def add_wires(self):
|
||||||
# nand Z to inv A
|
# nand Z to inv A
|
||||||
z1_pin = self.nand_inst.get_pin("Z")
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
|
|
@ -111,7 +111,7 @@ class and2_dec(design.design):
|
||||||
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||||
self.add_path(self.route_layer,
|
self.add_path(self.route_layer,
|
||||||
[z1_pin.center(), mid1_point, a2_pin.center()])
|
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
pin = self.inv_inst.get_pin("Z")
|
pin = self.inv_inst.get_pin("Z")
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
|
|
@ -127,7 +127,7 @@ class and2_dec(design.design):
|
||||||
offset=pin.center(),
|
offset=pin.center(),
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=pin.height())
|
height=pin.height())
|
||||||
|
|
||||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
"""Get the stage efforts of the A or B -> Z path"""
|
"""Get the stage efforts of the A or B -> Z path"""
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
|
|
@ -135,13 +135,13 @@ class and2_dec(design.design):
|
||||||
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||||
stage_effort_list.append(stage1)
|
stage_effort_list.append(stage1)
|
||||||
last_stage_is_rise = stage1.is_rise
|
last_stage_is_rise = stage1.is_rise
|
||||||
|
|
||||||
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||||
stage_effort_list.append(stage2)
|
stage_effort_list.append(stage2)
|
||||||
|
|
||||||
return stage_effort_list
|
return stage_effort_list
|
||||||
|
|
||||||
def get_cin(self):
|
def get_cin(self):
|
||||||
"""Return the relative input capacitance of a single input"""
|
"""Return the relative input capacitance of a single input"""
|
||||||
return self.nand.get_cin()
|
return self.nand.get_cin()
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ class and3_dec(design.design):
|
||||||
self.add_comment("size: {}".format(size))
|
self.add_comment("size: {}".format(size))
|
||||||
self.size = size
|
self.size = size
|
||||||
self.height = height
|
self.height = height
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -52,14 +52,14 @@ class and3_dec(design.design):
|
||||||
|
|
||||||
self.width = self.nand.width + self.inv.width
|
self.width = self.nand.width + self.inv.width
|
||||||
self.height = self.nand.height
|
self.height = self.nand.height
|
||||||
|
|
||||||
self.place_insts()
|
self.place_insts()
|
||||||
self.add_wires()
|
self.add_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
self.route_supply_rails()
|
self.route_supply_rails()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
self.add_pin("A", "INPUT")
|
self.add_pin("A", "INPUT")
|
||||||
self.add_pin("B", "INPUT")
|
self.add_pin("B", "INPUT")
|
||||||
|
|
@ -72,7 +72,7 @@ class and3_dec(design.design):
|
||||||
self.nand_inst = self.add_inst(name="pand3_dec_nand",
|
self.nand_inst = self.add_inst(name="pand3_dec_nand",
|
||||||
mod=self.nand)
|
mod=self.nand)
|
||||||
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
|
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
self.inv_inst = self.add_inst(name="pand3_dec_inv",
|
self.inv_inst = self.add_inst(name="pand3_dec_inv",
|
||||||
mod=self.inv)
|
mod=self.inv)
|
||||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
@ -99,7 +99,7 @@ class and3_dec(design.design):
|
||||||
layer=self.route_layer,
|
layer=self.route_layer,
|
||||||
offset=vector(0.5 * self.width, self.height),
|
offset=vector(0.5 * self.width, self.height),
|
||||||
width=self.width)
|
width=self.width)
|
||||||
|
|
||||||
def add_wires(self):
|
def add_wires(self):
|
||||||
# nand Z to inv A
|
# nand Z to inv A
|
||||||
z1_pin = self.nand_inst.get_pin("Z")
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
|
|
@ -136,7 +136,7 @@ class and3_dec(design.design):
|
||||||
slew=nand_delay.slew,
|
slew=nand_delay.slew,
|
||||||
load=load)
|
load=load)
|
||||||
return nand_delay + inv_delay
|
return nand_delay + inv_delay
|
||||||
|
|
||||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
"""Get the stage efforts of the A or B -> Z path"""
|
"""Get the stage efforts of the A or B -> Z path"""
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
|
|
@ -144,13 +144,13 @@ class and3_dec(design.design):
|
||||||
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||||
stage_effort_list.append(stage1)
|
stage_effort_list.append(stage1)
|
||||||
last_stage_is_rise = stage1.is_rise
|
last_stage_is_rise = stage1.is_rise
|
||||||
|
|
||||||
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||||
stage_effort_list.append(stage2)
|
stage_effort_list.append(stage2)
|
||||||
|
|
||||||
return stage_effort_list
|
return stage_effort_list
|
||||||
|
|
||||||
def get_cin(self):
|
def get_cin(self):
|
||||||
"""Return the relative input capacitance of a single input"""
|
"""Return the relative input capacitance of a single input"""
|
||||||
return self.nand.get_cin()
|
return self.nand.get_cin()
|
||||||
|
|
||||||
|
|
@ -18,14 +18,14 @@ class and4_dec(design.design):
|
||||||
This is an AND with configurable drive strength.
|
This is an AND with configurable drive strength.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||||
|
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
debug.info(1, "Creating and4_dec {}".format(name))
|
debug.info(1, "Creating and4_dec {}".format(name))
|
||||||
self.add_comment("size: {}".format(size))
|
self.add_comment("size: {}".format(size))
|
||||||
self.size = size
|
self.size = size
|
||||||
self.height = height
|
self.height = height
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -61,7 +61,7 @@ class and4_dec(design.design):
|
||||||
self.route_supply_rails()
|
self.route_supply_rails()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
self.add_pin("A", "INPUT")
|
self.add_pin("A", "INPUT")
|
||||||
self.add_pin("B", "INPUT")
|
self.add_pin("B", "INPUT")
|
||||||
|
|
@ -75,7 +75,7 @@ class and4_dec(design.design):
|
||||||
self.nand_inst = self.add_inst(name="pand4_dec_nand",
|
self.nand_inst = self.add_inst(name="pand4_dec_nand",
|
||||||
mod=self.nand)
|
mod=self.nand)
|
||||||
self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"])
|
self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
self.inv_inst = self.add_inst(name="pand4_dec_inv",
|
self.inv_inst = self.add_inst(name="pand4_dec_inv",
|
||||||
mod=self.inv)
|
mod=self.inv)
|
||||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
@ -102,7 +102,7 @@ class and4_dec(design.design):
|
||||||
layer=self.route_layer,
|
layer=self.route_layer,
|
||||||
offset=vector(0.5 * self.width, self.height),
|
offset=vector(0.5 * self.width, self.height),
|
||||||
width=self.width)
|
width=self.width)
|
||||||
|
|
||||||
def add_wires(self):
|
def add_wires(self):
|
||||||
# nand Z to inv A
|
# nand Z to inv A
|
||||||
z1_pin = self.nand_inst.get_pin("Z")
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
|
|
@ -139,7 +139,7 @@ class and4_dec(design.design):
|
||||||
slew=nand_delay.slew,
|
slew=nand_delay.slew,
|
||||||
load=load)
|
load=load)
|
||||||
return nand_delay + inv_delay
|
return nand_delay + inv_delay
|
||||||
|
|
||||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
"""Get the stage efforts of the A or B -> Z path"""
|
"""Get the stage efforts of the A or B -> Z path"""
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
|
|
@ -147,13 +147,13 @@ class and4_dec(design.design):
|
||||||
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||||
stage_effort_list.append(stage1)
|
stage_effort_list.append(stage1)
|
||||||
last_stage_is_rise = stage1.is_rise
|
last_stage_is_rise = stage1.is_rise
|
||||||
|
|
||||||
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||||
stage_effort_list.append(stage2)
|
stage_effort_list.append(stage2)
|
||||||
|
|
||||||
return stage_effort_list
|
return stage_effort_list
|
||||||
|
|
||||||
def get_cin(self):
|
def get_cin(self):
|
||||||
"""Return the relative input capacitance of a single input"""
|
"""Return the relative input capacitance of a single input"""
|
||||||
return self.nand.get_cin()
|
return self.nand.get_cin()
|
||||||
|
|
||||||
|
|
@ -31,16 +31,16 @@ class bank(design.design):
|
||||||
self.num_wmasks = int(ceil(self.word_size / self.write_size))
|
self.num_wmasks = int(ceil(self.word_size / self.write_size))
|
||||||
else:
|
else:
|
||||||
self.num_wmasks = 0
|
self.num_wmasks = 0
|
||||||
|
|
||||||
if not self.num_spare_cols:
|
if not self.num_spare_cols:
|
||||||
self.num_spare_cols = 0
|
self.num_spare_cols = 0
|
||||||
|
|
||||||
if name == "":
|
if name == "":
|
||||||
name = "bank_{0}_{1}".format(self.word_size, self.num_words)
|
name = "bank_{0}_{1}".format(self.word_size, self.num_words)
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
|
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
|
||||||
self.num_words))
|
self.num_words))
|
||||||
|
|
||||||
# The local control signals are gated when we have bank select logic,
|
# The local control signals are gated when we have bank select logic,
|
||||||
# so this prefix will be added to all of the input signals to create
|
# so this prefix will be added to all of the input signals to create
|
||||||
# the internal gated signals.
|
# the internal gated signals.
|
||||||
|
|
@ -62,12 +62,12 @@ class bank(design.design):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins() # Must create the replica bitcell array first
|
self.add_pins() # Must create the replica bitcell array first
|
||||||
self.create_instances()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.place_instances()
|
self.place_instances()
|
||||||
self.setup_routing_constraints()
|
self.setup_routing_constraints()
|
||||||
self.route_layout()
|
self.route_layout()
|
||||||
|
|
||||||
# Can remove the following, but it helps for debug!
|
# Can remove the following, but it helps for debug!
|
||||||
# self.add_lvs_correspondence_points()
|
# self.add_lvs_correspondence_points()
|
||||||
|
|
||||||
|
|
@ -110,13 +110,13 @@ class bank(design.design):
|
||||||
self.add_pin("wl_en{0}".format(port), "INPUT")
|
self.add_pin("wl_en{0}".format(port), "INPUT")
|
||||||
self.add_pin("vdd", "POWER")
|
self.add_pin("vdd", "POWER")
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
def route_layout(self):
|
def route_layout(self):
|
||||||
""" Create routing amoung the modules """
|
""" Create routing amoung the modules """
|
||||||
self.route_central_bus()
|
self.route_central_bus()
|
||||||
|
|
||||||
self.route_unused_wordlines()
|
self.route_unused_wordlines()
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.route_bitlines(port)
|
self.route_bitlines(port)
|
||||||
self.route_rbl(port)
|
self.route_rbl(port)
|
||||||
|
|
@ -125,7 +125,7 @@ class bank(design.design):
|
||||||
self.route_control_lines(port)
|
self.route_control_lines(port)
|
||||||
if self.num_banks > 1:
|
if self.num_banks > 1:
|
||||||
self.route_bank_select(port)
|
self.route_bank_select(port)
|
||||||
|
|
||||||
self.route_supplies()
|
self.route_supplies()
|
||||||
|
|
||||||
def route_rbl(self, port):
|
def route_rbl(self, port):
|
||||||
|
|
@ -149,7 +149,7 @@ class bank(design.design):
|
||||||
layer="m3",
|
layer="m3",
|
||||||
start=left_right_offset,
|
start=left_right_offset,
|
||||||
end=pin_offset)
|
end=pin_offset)
|
||||||
|
|
||||||
def route_bitlines(self, port):
|
def route_bitlines(self, port):
|
||||||
""" Route the bitlines depending on the port type rw, w, or r. """
|
""" Route the bitlines depending on the port type rw, w, or r. """
|
||||||
|
|
||||||
|
|
@ -158,7 +158,7 @@ class bank(design.design):
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
self.route_port_data_out(port)
|
self.route_port_data_out(port)
|
||||||
self.route_port_data_to_bitcell_array(port)
|
self.route_port_data_to_bitcell_array(port)
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create the instances of the netlist. """
|
""" Create the instances of the netlist. """
|
||||||
|
|
||||||
|
|
@ -185,7 +185,7 @@ class bank(design.design):
|
||||||
# The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
|
# The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
|
||||||
self.bitcell_array_top = self.bitcell_array.height
|
self.bitcell_array_top = self.bitcell_array.height
|
||||||
self.bitcell_array_right = self.bitcell_array.width
|
self.bitcell_array_right = self.bitcell_array.width
|
||||||
|
|
||||||
# These are the offsets of the main array (excluding dummy and replica rows/cols)
|
# These are the offsets of the main array (excluding dummy and replica rows/cols)
|
||||||
self.main_bitcell_array_top = self.bitcell_array.get_main_array_top()
|
self.main_bitcell_array_top = self.bitcell_array.get_main_array_top()
|
||||||
# Just past the dummy column
|
# Just past the dummy column
|
||||||
|
|
@ -213,7 +213,7 @@ class bank(design.design):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
port = 0
|
port = 0
|
||||||
|
|
||||||
# UPPER RIGHT QUADRANT
|
# UPPER RIGHT QUADRANT
|
||||||
# Bitcell array is placed at (0,0)
|
# Bitcell array is placed at (0,0)
|
||||||
self.bitcell_array_offset = vector(0, 0)
|
self.bitcell_array_offset = vector(0, 0)
|
||||||
|
|
@ -258,14 +258,14 @@ class bank(design.design):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
port=1
|
port=1
|
||||||
|
|
||||||
# LOWER LEFT QUADRANT
|
# LOWER LEFT QUADRANT
|
||||||
# Bitcell array is placed at (0,0)
|
# Bitcell array is placed at (0,0)
|
||||||
|
|
||||||
# UPPER LEFT QUADRANT
|
# UPPER LEFT QUADRANT
|
||||||
# Above the bitcell array
|
# Above the bitcell array
|
||||||
self.port_data_offsets[port] = vector(0, self.bitcell_array_top)
|
self.port_data_offsets[port] = vector(0, self.bitcell_array_top)
|
||||||
|
|
||||||
# LOWER RIGHT QUADRANT
|
# LOWER RIGHT QUADRANT
|
||||||
# To the right of the bitcell array
|
# To the right of the bitcell array
|
||||||
x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap
|
x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap
|
||||||
|
|
@ -294,12 +294,12 @@ class bank(design.design):
|
||||||
else:
|
else:
|
||||||
y_offset = self.port_address_offsets[port].y
|
y_offset = self.port_address_offsets[port].y
|
||||||
self.bank_select_offsets[port] = vector(x_offset, y_offset)
|
self.bank_select_offsets[port] = vector(x_offset, y_offset)
|
||||||
|
|
||||||
def place_instances(self):
|
def place_instances(self):
|
||||||
""" Place the instances. """
|
""" Place the instances. """
|
||||||
|
|
||||||
self.compute_instance_offsets()
|
self.compute_instance_offsets()
|
||||||
|
|
||||||
self.place_bitcell_array(self.bitcell_array_offset)
|
self.place_bitcell_array(self.bitcell_array_offset)
|
||||||
|
|
||||||
self.place_port_data(self.port_data_offsets)
|
self.place_port_data(self.port_data_offsets)
|
||||||
|
|
@ -308,7 +308,7 @@ class bank(design.design):
|
||||||
|
|
||||||
self.place_column_decoder(self.column_decoder_offsets)
|
self.place_column_decoder(self.column_decoder_offsets)
|
||||||
self.place_bank_select(self.bank_select_offsets)
|
self.place_bank_select(self.bank_select_offsets)
|
||||||
|
|
||||||
def compute_sizes(self):
|
def compute_sizes(self):
|
||||||
""" Computes the required sizes to create the bank """
|
""" Computes the required sizes to create the bank """
|
||||||
|
|
||||||
|
|
@ -351,7 +351,7 @@ class bank(design.design):
|
||||||
self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]])
|
self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]])
|
||||||
else:
|
else:
|
||||||
self.control_signals.append(self.input_control_signals[port])
|
self.control_signals.append(self.input_control_signals[port])
|
||||||
|
|
||||||
|
|
||||||
# The central bus is the column address (one hot) and row address (binary)
|
# The central bus is the column address (one hot) and row address (binary)
|
||||||
if self.col_addr_size>0:
|
if self.col_addr_size>0:
|
||||||
|
|
@ -379,7 +379,7 @@ class bank(design.design):
|
||||||
local_array_size = OPTS.local_array_size
|
local_array_size = OPTS.local_array_size
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
local_array_size = 0
|
local_array_size = 0
|
||||||
|
|
||||||
if local_array_size > 0:
|
if local_array_size > 0:
|
||||||
# Find the even multiple that satisfies the fanout with equal sized local arrays
|
# Find the even multiple that satisfies the fanout with equal sized local arrays
|
||||||
total_cols = self.num_cols + self.num_spare_cols
|
total_cols = self.num_cols + self.num_spare_cols
|
||||||
|
|
@ -406,11 +406,11 @@ class bank(design.design):
|
||||||
bit_offsets=self.bit_offsets)
|
bit_offsets=self.bit_offsets)
|
||||||
self.port_data.append(temp_pre)
|
self.port_data.append(temp_pre)
|
||||||
self.add_mod(self.port_data[port])
|
self.add_mod(self.port_data[port])
|
||||||
|
|
||||||
if(self.num_banks > 1):
|
if(self.num_banks > 1):
|
||||||
self.bank_select = factory.create(module_type="bank_select")
|
self.bank_select = factory.create(module_type="bank_select")
|
||||||
self.add_mod(self.bank_select)
|
self.add_mod(self.bank_select)
|
||||||
|
|
||||||
def create_bitcell_array(self):
|
def create_bitcell_array(self):
|
||||||
""" Creating Bitcell Array """
|
""" Creating Bitcell Array """
|
||||||
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
||||||
|
|
@ -426,12 +426,12 @@ class bank(design.design):
|
||||||
temp.extend(self.bitcell_array.get_wordline_names())
|
temp.extend(self.bitcell_array.get_wordline_names())
|
||||||
if len(self.all_ports) > 1:
|
if len(self.all_ports) > 1:
|
||||||
temp.append("rbl_wl1")
|
temp.append("rbl_wl1")
|
||||||
|
|
||||||
temp.append("vdd")
|
temp.append("vdd")
|
||||||
temp.append("gnd")
|
temp.append("gnd")
|
||||||
|
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
def place_bitcell_array(self, offset):
|
def place_bitcell_array(self, offset):
|
||||||
""" Placing Bitcell Array """
|
""" Placing Bitcell Array """
|
||||||
self.bitcell_array_inst.place(offset)
|
self.bitcell_array_inst.place(offset)
|
||||||
|
|
@ -470,7 +470,7 @@ class bank(design.design):
|
||||||
|
|
||||||
def place_port_data(self, offsets):
|
def place_port_data(self, offsets):
|
||||||
""" Placing Port Data """
|
""" Placing Port Data """
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
# Top one is unflipped, bottom is flipped along X direction
|
# Top one is unflipped, bottom is flipped along X direction
|
||||||
if port % 2 == 1:
|
if port % 2 == 1:
|
||||||
|
|
@ -481,7 +481,7 @@ class bank(design.design):
|
||||||
|
|
||||||
def create_port_address(self):
|
def create_port_address(self):
|
||||||
""" Create the hierarchical row decoder """
|
""" Create the hierarchical row decoder """
|
||||||
|
|
||||||
self.port_address_inst = [None] * len(self.all_ports)
|
self.port_address_inst = [None] * len(self.all_ports)
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
|
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
|
||||||
|
|
@ -496,32 +496,32 @@ class bank(design.design):
|
||||||
temp.append("rbl_wl{}".format(port))
|
temp.append("rbl_wl{}".format(port))
|
||||||
temp.extend(["vdd", "gnd"])
|
temp.extend(["vdd", "gnd"])
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
def place_port_address(self, offsets):
|
def place_port_address(self, offsets):
|
||||||
""" Place the hierarchical row decoder """
|
""" Place the hierarchical row decoder """
|
||||||
|
|
||||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.")
|
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.")
|
||||||
|
|
||||||
# The address and control bus will be in between decoder and the main memory array
|
# The address and control bus will be in between decoder and the main memory array
|
||||||
# This bus will route address bits to the decoder input and column mux inputs.
|
# This bus will route address bits to the decoder input and column mux inputs.
|
||||||
# The wires are actually routed after we placed the stuff on both sides.
|
# The wires are actually routed after we placed the stuff on both sides.
|
||||||
# The predecoder is below the x-axis and the main decoder is above the x-axis
|
# The predecoder is below the x-axis and the main decoder is above the x-axis
|
||||||
# The address flop and decoder are aligned in the x coord.
|
# The address flop and decoder are aligned in the x coord.
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
if port % 2:
|
if port % 2:
|
||||||
mirror = "MY"
|
mirror = "MY"
|
||||||
else:
|
else:
|
||||||
mirror = "R0"
|
mirror = "R0"
|
||||||
self.port_address_inst[port].place(offset=offsets[port], mirror=mirror)
|
self.port_address_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||||
|
|
||||||
def create_column_decoder(self):
|
def create_column_decoder(self):
|
||||||
"""
|
"""
|
||||||
Create a 2:4 or 3:8 column address decoder.
|
Create a 2:4 or 3:8 column address decoder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.dff =factory.create(module_type="dff")
|
self.dff =factory.create(module_type="dff")
|
||||||
|
|
||||||
if self.col_addr_size == 0:
|
if self.col_addr_size == 0:
|
||||||
return
|
return
|
||||||
elif self.col_addr_size == 1:
|
elif self.col_addr_size == 1:
|
||||||
|
|
@ -541,7 +541,7 @@ class bank(design.design):
|
||||||
# No error checking before?
|
# No error checking before?
|
||||||
debug.error("Invalid column decoder?", -1)
|
debug.error("Invalid column decoder?", -1)
|
||||||
self.add_mod(self.column_decoder)
|
self.add_mod(self.column_decoder)
|
||||||
|
|
||||||
self.column_decoder_inst = [None] * len(self.all_ports)
|
self.column_decoder_inst = [None] * len(self.all_ports)
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
|
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
|
||||||
|
|
@ -554,14 +554,14 @@ class bank(design.design):
|
||||||
temp.append("sel{0}_{1}".format(port, bit))
|
temp.append("sel{0}_{1}".format(port, bit))
|
||||||
temp.extend(["vdd", "gnd"])
|
temp.extend(["vdd", "gnd"])
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
def place_column_decoder(self, offsets):
|
def place_column_decoder(self, offsets):
|
||||||
"""
|
"""
|
||||||
Place a 2:4 or 3:8 column address decoder.
|
Place a 2:4 or 3:8 column address decoder.
|
||||||
"""
|
"""
|
||||||
if self.col_addr_size == 0:
|
if self.col_addr_size == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
debug.check(len(offsets)>=len(self.all_ports),
|
debug.check(len(offsets)>=len(self.all_ports),
|
||||||
"Insufficient offsets to place column decoder.")
|
"Insufficient offsets to place column decoder.")
|
||||||
|
|
||||||
|
|
@ -571,7 +571,7 @@ class bank(design.design):
|
||||||
else:
|
else:
|
||||||
mirror = "R0"
|
mirror = "R0"
|
||||||
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
|
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||||
|
|
||||||
def create_bank_select(self):
|
def create_bank_select(self):
|
||||||
""" Create the bank select logic. """
|
""" Create the bank select logic. """
|
||||||
|
|
||||||
|
|
@ -582,7 +582,7 @@ class bank(design.design):
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
|
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
|
||||||
mod=self.bank_select)
|
mod=self.bank_select)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
temp.extend(self.input_control_signals[port])
|
temp.extend(self.input_control_signals[port])
|
||||||
temp.append("bank_sel{}".format(port))
|
temp.append("bank_sel{}".format(port))
|
||||||
|
|
@ -601,7 +601,7 @@ class bank(design.design):
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.bank_select_inst[port].place(offsets[port])
|
self.bank_select_inst[port].place(offsets[port])
|
||||||
|
|
||||||
def route_supplies(self):
|
def route_supplies(self):
|
||||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||||
# Copy only the power pins already on the power layer
|
# Copy only the power pins already on the power layer
|
||||||
|
|
@ -624,7 +624,7 @@ class bank(design.design):
|
||||||
|
|
||||||
def route_bank_select(self, port):
|
def route_bank_select(self, port):
|
||||||
""" Route the bank select logic. """
|
""" Route the bank select logic. """
|
||||||
|
|
||||||
if self.port_id[port] == "rw":
|
if self.port_id[port] == "rw":
|
||||||
bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
|
bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
|
||||||
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
|
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
|
||||||
|
|
@ -634,11 +634,11 @@ class bank(design.design):
|
||||||
else:
|
else:
|
||||||
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
|
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
|
||||||
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
|
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
|
||||||
|
|
||||||
copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)]
|
copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)]
|
||||||
for signal in range(len(copy_control_signals)):
|
for signal in range(len(copy_control_signals)):
|
||||||
self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
|
self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
|
||||||
|
|
||||||
for signal in range(len(gated_bank_sel_signals)):
|
for signal in range(len(gated_bank_sel_signals)):
|
||||||
# Connect the inverter output to the central bus
|
# Connect the inverter output to the central bus
|
||||||
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
|
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
|
||||||
|
|
@ -651,7 +651,7 @@ class bank(design.design):
|
||||||
offset=out_pos)
|
offset=out_pos)
|
||||||
self.add_via_center(layers=self.m2_stack,
|
self.add_via_center(layers=self.m2_stack,
|
||||||
offset=out_pos)
|
offset=out_pos)
|
||||||
|
|
||||||
def setup_routing_constraints(self):
|
def setup_routing_constraints(self):
|
||||||
"""
|
"""
|
||||||
After the modules are instantiated, find the dimensions for the
|
After the modules are instantiated, find the dimensions for the
|
||||||
|
|
@ -660,7 +660,7 @@ class bank(design.design):
|
||||||
|
|
||||||
self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width
|
self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width
|
||||||
self.min_y_offset = min([x.by() for x in self.insts])
|
self.min_y_offset = min([x.by() for x in self.insts])
|
||||||
|
|
||||||
self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width
|
self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width
|
||||||
self.min_x_offset = min([x.lx() for x in self.insts])
|
self.min_x_offset = min([x.lx() for x in self.insts])
|
||||||
|
|
||||||
|
|
@ -668,7 +668,7 @@ class bank(design.design):
|
||||||
ur = vector(self.max_x_offset, self.max_y_offset)
|
ur = vector(self.max_x_offset, self.max_y_offset)
|
||||||
ll = vector(self.min_x_offset, self.min_y_offset)
|
ll = vector(self.min_x_offset, self.min_y_offset)
|
||||||
self.core_bbox = [ll, ur]
|
self.core_bbox = [ll, ur]
|
||||||
|
|
||||||
self.height = ur.y - ll.y
|
self.height = ur.y - ll.y
|
||||||
self.width = ur.x - ll.x
|
self.width = ur.x - ll.x
|
||||||
|
|
||||||
|
|
@ -692,7 +692,7 @@ class bank(design.design):
|
||||||
vertical=True,
|
vertical=True,
|
||||||
make_pins=(self.num_banks==1),
|
make_pins=(self.num_banks==1),
|
||||||
pitch=self.m3_pitch)
|
pitch=self.m3_pitch)
|
||||||
|
|
||||||
# Port 1
|
# Port 1
|
||||||
if len(self.all_ports)==2:
|
if len(self.all_ports)==2:
|
||||||
# The other control bus is routed up to two pitches above the bitcell array
|
# The other control bus is routed up to two pitches above the bitcell array
|
||||||
|
|
@ -731,12 +731,12 @@ class bank(design.design):
|
||||||
inst1_br_name=inst1_br_name,
|
inst1_br_name=inst1_br_name,
|
||||||
inst2_bl_name=inst2_bl_name,
|
inst2_bl_name=inst2_bl_name,
|
||||||
inst2_br_name=inst2_br_name)
|
inst2_br_name=inst2_br_name)
|
||||||
|
|
||||||
|
|
||||||
# Connect the replica bitlines
|
# Connect the replica bitlines
|
||||||
for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]):
|
for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]):
|
||||||
self.connect_bitline(inst1, inst2, array_name, data_name)
|
self.connect_bitline(inst1, inst2, array_name, data_name)
|
||||||
|
|
||||||
def route_port_data_out(self, port):
|
def route_port_data_out(self, port):
|
||||||
""" Add pins for the port data out """
|
""" Add pins for the port data out """
|
||||||
|
|
||||||
|
|
@ -747,7 +747,7 @@ class bank(design.design):
|
||||||
offset=data_pin.center(),
|
offset=data_pin.center(),
|
||||||
height=data_pin.height(),
|
height=data_pin.height(),
|
||||||
width=data_pin.width())
|
width=data_pin.width())
|
||||||
|
|
||||||
def route_port_address_in(self, port):
|
def route_port_address_in(self, port):
|
||||||
""" Routes the row decoder inputs and supplies """
|
""" Routes the row decoder inputs and supplies """
|
||||||
|
|
||||||
|
|
@ -771,19 +771,19 @@ class bank(design.design):
|
||||||
wmask_name = "bank_wmask_{}".format(row)
|
wmask_name = "bank_wmask_{}".format(row)
|
||||||
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)
|
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)
|
||||||
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
|
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
|
||||||
|
|
||||||
for col in range(self.num_spare_cols):
|
for col in range(self.num_spare_cols):
|
||||||
sparecol_name = "bank_spare_wen{}".format(col)
|
sparecol_name = "bank_spare_wen{}".format(col)
|
||||||
bank_sparecol_name = "bank_spare_wen{0}_{1}".format(port, col)
|
bank_sparecol_name = "bank_spare_wen{0}_{1}".format(port, col)
|
||||||
self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name)
|
self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name)
|
||||||
|
|
||||||
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
||||||
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
|
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
|
||||||
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
|
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
|
||||||
"""
|
"""
|
||||||
Route the bl and br of two modules using the channel router.
|
Route the bl and br of two modules using the channel router.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# determine top and bottom automatically.
|
# determine top and bottom automatically.
|
||||||
# since they don't overlap, we can just check the bottom y coordinate.
|
# since they don't overlap, we can just check the bottom y coordinate.
|
||||||
if inst1.by() < inst2.by():
|
if inst1.by() < inst2.by():
|
||||||
|
|
@ -801,14 +801,14 @@ class bank(design.design):
|
||||||
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
|
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
|
||||||
route_map = list(zip(bottom_names, top_names))
|
route_map = list(zip(bottom_names, top_names))
|
||||||
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
|
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
|
||||||
|
|
||||||
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
|
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
|
||||||
"""
|
"""
|
||||||
Connect two pins of two modules.
|
Connect two pins of two modules.
|
||||||
This assumes that they have sufficient space to create a jog
|
This assumes that they have sufficient space to create a jog
|
||||||
in the middle between the two modules (if needed).
|
in the middle between the two modules (if needed).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# determine top and bottom automatically.
|
# determine top and bottom automatically.
|
||||||
# since they don't overlap, we can just check the bottom y coordinate.
|
# since they don't overlap, we can just check the bottom y coordinate.
|
||||||
if inst1.by() < inst2.by():
|
if inst1.by() < inst2.by():
|
||||||
|
|
@ -821,17 +821,17 @@ class bank(design.design):
|
||||||
bottom_pin = bottom_inst.get_pin(bottom_name)
|
bottom_pin = bottom_inst.get_pin(bottom_name)
|
||||||
top_pin = top_inst.get_pin(top_name)
|
top_pin = top_inst.get_pin(top_name)
|
||||||
debug.check(bottom_pin.layer == top_pin.layer, "Pin layers do not match.")
|
debug.check(bottom_pin.layer == top_pin.layer, "Pin layers do not match.")
|
||||||
|
|
||||||
bottom_loc = bottom_pin.uc()
|
bottom_loc = bottom_pin.uc()
|
||||||
top_loc = top_pin.bc()
|
top_loc = top_pin.bc()
|
||||||
|
|
||||||
yoffset = 0.5 * (top_loc.y + bottom_loc.y)
|
yoffset = 0.5 * (top_loc.y + bottom_loc.y)
|
||||||
self.add_path(top_pin.layer,
|
self.add_path(top_pin.layer,
|
||||||
[bottom_loc,
|
[bottom_loc,
|
||||||
vector(bottom_loc.x, yoffset),
|
vector(bottom_loc.x, yoffset),
|
||||||
vector(top_loc.x, yoffset),
|
vector(top_loc.x, yoffset),
|
||||||
top_loc])
|
top_loc])
|
||||||
|
|
||||||
def connect_bitlines(self, inst1, inst2,
|
def connect_bitlines(self, inst1, inst2,
|
||||||
inst1_bl_name, inst1_br_name,
|
inst1_bl_name, inst1_br_name,
|
||||||
inst2_bl_name, inst2_br_name):
|
inst2_bl_name, inst2_br_name):
|
||||||
|
|
@ -847,12 +847,12 @@ class bank(design.design):
|
||||||
""" Connect Wordline driver to bitcell array wordline """
|
""" Connect Wordline driver to bitcell array wordline """
|
||||||
|
|
||||||
self.route_port_address_in(port)
|
self.route_port_address_in(port)
|
||||||
|
|
||||||
if port % 2:
|
if port % 2:
|
||||||
self.route_port_address_out(port, "right")
|
self.route_port_address_out(port, "right")
|
||||||
else:
|
else:
|
||||||
self.route_port_address_out(port, "left")
|
self.route_port_address_out(port, "left")
|
||||||
|
|
||||||
def route_port_address_out(self, port, side="left"):
|
def route_port_address_out(self, port, side="left"):
|
||||||
""" Connecting Wordline driver output to Bitcell WL connection """
|
""" Connecting Wordline driver output to Bitcell WL connection """
|
||||||
|
|
||||||
|
|
@ -866,7 +866,7 @@ class bank(design.design):
|
||||||
else:
|
else:
|
||||||
driver_wl_pos = driver_wl_pin.lc()
|
driver_wl_pos = driver_wl_pin.lc()
|
||||||
bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name)
|
bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name)
|
||||||
|
|
||||||
if side == "left":
|
if side == "left":
|
||||||
bitcell_wl_pos = bitcell_wl_pin.lc()
|
bitcell_wl_pos = bitcell_wl_pin.lc()
|
||||||
port_address_pos = self.port_address_inst[port].rx()
|
port_address_pos = self.port_address_inst[port].rx()
|
||||||
|
|
@ -875,7 +875,7 @@ class bank(design.design):
|
||||||
bitcell_wl_pos = bitcell_wl_pin.rc()
|
bitcell_wl_pos = bitcell_wl_pin.rc()
|
||||||
port_address_pos = self.port_address_inst[port].lx()
|
port_address_pos = self.port_address_inst[port].lx()
|
||||||
bitcell_array_pos = self.bitcell_array_inst.rx()
|
bitcell_array_pos = self.bitcell_array_inst.rx()
|
||||||
|
|
||||||
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0)
|
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0)
|
||||||
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
|
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
|
||||||
if driver_wl_pin.layer != bitcell_wl_pin.layer:
|
if driver_wl_pin.layer != bitcell_wl_pin.layer:
|
||||||
|
|
@ -886,7 +886,7 @@ class bank(design.design):
|
||||||
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
|
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
|
||||||
else:
|
else:
|
||||||
self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||||
|
|
||||||
|
|
||||||
def route_port_address_right(self, port):
|
def route_port_address_right(self, port):
|
||||||
""" Connecting Wordline driver output to Bitcell WL connection """
|
""" Connecting Wordline driver output to Bitcell WL connection """
|
||||||
|
|
@ -906,7 +906,7 @@ class bank(design.design):
|
||||||
to_layer=bitcell_wl_pin.layer,
|
to_layer=bitcell_wl_pin.layer,
|
||||||
offset=mid2)
|
offset=mid2)
|
||||||
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
|
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
|
||||||
|
|
||||||
def route_column_address_lines(self, port):
|
def route_column_address_lines(self, port):
|
||||||
""" Connecting the select lines of column mux to the address bus """
|
""" Connecting the select lines of column mux to the address bus """
|
||||||
if not self.col_addr_size>0:
|
if not self.col_addr_size>0:
|
||||||
|
|
@ -914,15 +914,15 @@ class bank(design.design):
|
||||||
|
|
||||||
stack = getattr(self, layer_props.bank.stack)
|
stack = getattr(self, layer_props.bank.stack)
|
||||||
pitch = getattr(self, layer_props.bank.pitch)
|
pitch = getattr(self, layer_props.bank.pitch)
|
||||||
|
|
||||||
if self.col_addr_size == 1:
|
if self.col_addr_size == 1:
|
||||||
|
|
||||||
# Connect to sel[0] and sel[1]
|
# Connect to sel[0] and sel[1]
|
||||||
decode_names = ["Zb", "Z"]
|
decode_names = ["Zb", "Z"]
|
||||||
|
|
||||||
# The Address LSB
|
# The Address LSB
|
||||||
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
||||||
|
|
||||||
elif self.col_addr_size > 1:
|
elif self.col_addr_size > 1:
|
||||||
decode_names = []
|
decode_names = []
|
||||||
for i in range(self.num_col_addr_lines):
|
for i in range(self.num_col_addr_lines):
|
||||||
|
|
@ -942,7 +942,7 @@ class bank(design.design):
|
||||||
|
|
||||||
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
||||||
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
|
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
|
||||||
|
|
||||||
route_map = list(zip(decode_pins, column_mux_pins))
|
route_map = list(zip(decode_pins, column_mux_pins))
|
||||||
self.create_vertical_channel_route(route_map,
|
self.create_vertical_channel_route(route_map,
|
||||||
offset,
|
offset,
|
||||||
|
|
@ -964,7 +964,7 @@ class bank(design.design):
|
||||||
# self.add_label(text=wl_name,
|
# self.add_label(text=wl_name,
|
||||||
# layer="m1",
|
# layer="m1",
|
||||||
# offset=wl_pin.center())
|
# offset=wl_pin.center())
|
||||||
|
|
||||||
# # Add the bitline names
|
# # Add the bitline names
|
||||||
# for i in range(self.num_cols):
|
# for i in range(self.num_cols):
|
||||||
# bl_name = "bl_{}".format(i)
|
# bl_name = "bl_{}".format(i)
|
||||||
|
|
@ -1025,10 +1025,10 @@ class bank(design.design):
|
||||||
# Add a path to connect to the array
|
# Add a path to connect to the array
|
||||||
self.add_path(pin_layer, [left_loc, left_pin_loc])
|
self.add_path(pin_layer, [left_loc, left_pin_loc])
|
||||||
self.add_path(pin_layer, [right_loc, right_pin_loc])
|
self.add_path(pin_layer, [right_loc, right_pin_loc])
|
||||||
|
|
||||||
def route_control_lines(self, port):
|
def route_control_lines(self, port):
|
||||||
""" Route the control lines of the entire bank """
|
""" Route the control lines of the entire bank """
|
||||||
|
|
||||||
# Make a list of tuples that we will connect.
|
# Make a list of tuples that we will connect.
|
||||||
# From control signal to the module pin
|
# From control signal to the module pin
|
||||||
# Connection from the central bus to the main control block crosses
|
# Connection from the central bus to the main control block crosses
|
||||||
|
|
@ -1040,7 +1040,7 @@ class bank(design.design):
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
connection.append((self.prefix + "w_en{}".format(port),
|
connection.append((self.prefix + "w_en{}".format(port),
|
||||||
self.port_data_inst[port].get_pin("w_en")))
|
self.port_data_inst[port].get_pin("w_en")))
|
||||||
|
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
connection.append((self.prefix + "s_en{}".format(port),
|
connection.append((self.prefix + "s_en{}".format(port),
|
||||||
self.port_data_inst[port].get_pin("s_en")))
|
self.port_data_inst[port].get_pin("s_en")))
|
||||||
|
|
@ -1057,7 +1057,7 @@ class bank(design.design):
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
to_layer="m2",
|
to_layer="m2",
|
||||||
offset=control_pos)
|
offset=control_pos)
|
||||||
|
|
||||||
# clk to wordline_driver
|
# clk to wordline_driver
|
||||||
control_signal = self.prefix + "wl_en{}".format(port)
|
control_signal = self.prefix + "wl_en{}".format(port)
|
||||||
if port % 2:
|
if port % 2:
|
||||||
|
|
@ -1081,7 +1081,7 @@ class bank(design.design):
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
if self.port_data[port]:
|
if self.port_data[port]:
|
||||||
self.port_data[port].graph_exclude_precharge()
|
self.port_data[port].graph_exclude_precharge()
|
||||||
|
|
||||||
def get_cell_name(self, inst_name, row, col):
|
def get_cell_name(self, inst_name, row, col):
|
||||||
"""
|
"""
|
||||||
Gets the spice name of the target bitcell.
|
Gets the spice name of the target bitcell.
|
||||||
|
|
@ -1097,8 +1097,8 @@ class bank(design.design):
|
||||||
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
|
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
|
||||||
|
|
||||||
def clear_exclude_bits(self):
|
def clear_exclude_bits(self):
|
||||||
"""
|
"""
|
||||||
Clears the bit exclusions
|
Clears the bit exclusions
|
||||||
"""
|
"""
|
||||||
self.bitcell_array.clear_exclude_bits()
|
self.bitcell_array.clear_exclude_bits()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class bank_select(design.design):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -36,7 +36,7 @@ class bank_select(design.design):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_instances()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.calculate_module_offsets()
|
self.calculate_module_offsets()
|
||||||
self.place_instances()
|
self.place_instances()
|
||||||
|
|
@ -44,12 +44,12 @@ class bank_select(design.design):
|
||||||
|
|
||||||
self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width
|
self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width
|
||||||
self.width = max([x.rx() for x in self.inv_inst])
|
self.width = max([x.rx() for x in self.inv_inst])
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
||||||
# Number of control lines in the bus
|
# Number of control lines in the bus
|
||||||
if self.port == "rw":
|
if self.port == "rw":
|
||||||
self.num_control_lines = 4
|
self.num_control_lines = 4
|
||||||
|
|
@ -86,7 +86,7 @@ class bank_select(design.design):
|
||||||
|
|
||||||
self.nor2 = factory.create(module_type="pnor2", height=height)
|
self.nor2 = factory.create(module_type="pnor2", height=height)
|
||||||
self.add_mod(self.nor2)
|
self.add_mod(self.nor2)
|
||||||
|
|
||||||
self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4)
|
self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4)
|
||||||
self.add_mod(self.inv4x_nor)
|
self.add_mod(self.inv4x_nor)
|
||||||
|
|
||||||
|
|
@ -94,15 +94,15 @@ class bank_select(design.design):
|
||||||
self.add_mod(self.nand2)
|
self.add_mod(self.nand2)
|
||||||
|
|
||||||
def calculate_module_offsets(self):
|
def calculate_module_offsets(self):
|
||||||
|
|
||||||
self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
|
self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
|
||||||
self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
|
self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
|
||||||
self.xoffset_bank_sel_inv = 0
|
self.xoffset_bank_sel_inv = 0
|
||||||
self.xoffset_inputs = 0
|
self.xoffset_inputs = 0
|
||||||
self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height
|
self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
|
|
||||||
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
|
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
|
||||||
mod=self.inv_sel)
|
mod=self.inv_sel)
|
||||||
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
|
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
|
||||||
|
|
@ -119,7 +119,7 @@ class bank_select(design.design):
|
||||||
# These require OR (nor2+inv) gates since they are active low.
|
# These require OR (nor2+inv) gates since they are active low.
|
||||||
# (writes occur on clk low)
|
# (writes occur on clk low)
|
||||||
if input_name in ("clk_buf"):
|
if input_name in ("clk_buf"):
|
||||||
|
|
||||||
self.logic_inst.append(self.add_inst(name=name_nor,
|
self.logic_inst.append(self.add_inst(name=name_nor,
|
||||||
mod=self.nor2))
|
mod=self.nor2))
|
||||||
self.connect_inst([input_name,
|
self.connect_inst([input_name,
|
||||||
|
|
@ -127,7 +127,7 @@ class bank_select(design.design):
|
||||||
gated_name + "_temp_bar",
|
gated_name + "_temp_bar",
|
||||||
"vdd",
|
"vdd",
|
||||||
"gnd"])
|
"gnd"])
|
||||||
|
|
||||||
# They all get inverters on the output
|
# They all get inverters on the output
|
||||||
self.inv_inst.append(self.add_inst(name=name_inv,
|
self.inv_inst.append(self.add_inst(name=name_inv,
|
||||||
mod=self.inv4x_nor))
|
mod=self.inv4x_nor))
|
||||||
|
|
@ -135,7 +135,7 @@ class bank_select(design.design):
|
||||||
gated_name,
|
gated_name,
|
||||||
"vdd",
|
"vdd",
|
||||||
"gnd"])
|
"gnd"])
|
||||||
|
|
||||||
# the rest are AND (nand2+inv) gates
|
# the rest are AND (nand2+inv) gates
|
||||||
else:
|
else:
|
||||||
self.logic_inst.append(self.add_inst(name=name_nand,
|
self.logic_inst.append(self.add_inst(name=name_nand,
|
||||||
|
|
@ -155,7 +155,7 @@ class bank_select(design.design):
|
||||||
"gnd"])
|
"gnd"])
|
||||||
|
|
||||||
def place_instances(self):
|
def place_instances(self):
|
||||||
|
|
||||||
# bank select inverter
|
# bank select inverter
|
||||||
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
|
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
|
||||||
|
|
||||||
|
|
@ -166,27 +166,27 @@ class bank_select(design.design):
|
||||||
|
|
||||||
logic_inst = self.logic_inst[i]
|
logic_inst = self.logic_inst[i]
|
||||||
inv_inst = self.inv_inst[i]
|
inv_inst = self.inv_inst[i]
|
||||||
|
|
||||||
input_name = self.input_control_signals[i]
|
input_name = self.input_control_signals[i]
|
||||||
|
|
||||||
if i == 0:
|
if i == 0:
|
||||||
y_offset = 0
|
y_offset = 0
|
||||||
else:
|
else:
|
||||||
y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1)
|
y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1)
|
||||||
|
|
||||||
if i % 2:
|
if i % 2:
|
||||||
y_offset += self.inv4x.height
|
y_offset += self.inv4x.height
|
||||||
mirror = "MX"
|
mirror = "MX"
|
||||||
else:
|
else:
|
||||||
mirror = ""
|
mirror = ""
|
||||||
|
|
||||||
# These require OR (nor2+inv) gates since they are active low.
|
# These require OR (nor2+inv) gates since they are active low.
|
||||||
# (writes occur on clk low)
|
# (writes occur on clk low)
|
||||||
if input_name in ("clk_buf"):
|
if input_name in ("clk_buf"):
|
||||||
|
|
||||||
logic_inst.place(offset=[self.xoffset_nor, y_offset],
|
logic_inst.place(offset=[self.xoffset_nor, y_offset],
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
# the rest are AND (nand2+inv) gates
|
# the rest are AND (nand2+inv) gates
|
||||||
else:
|
else:
|
||||||
logic_inst.place(offset=[self.xoffset_nand, y_offset],
|
logic_inst.place(offset=[self.xoffset_nand, y_offset],
|
||||||
|
|
@ -197,7 +197,7 @@ class bank_select(design.design):
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
def route_instances(self):
|
def route_instances(self):
|
||||||
|
|
||||||
# bank_sel is vertical wire
|
# bank_sel is vertical wire
|
||||||
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
|
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
|
||||||
xoffset_bank_sel = bank_sel_inv_pin.lx()
|
xoffset_bank_sel = bank_sel_inv_pin.lx()
|
||||||
|
|
@ -227,19 +227,19 @@ class bank_select(design.design):
|
||||||
height=self.inv4x.height)
|
height=self.inv4x.height)
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=bank_sel_bar_pin.rc())
|
offset=bank_sel_bar_pin.rc())
|
||||||
|
|
||||||
for i in range(self.num_control_lines):
|
for i in range(self.num_control_lines):
|
||||||
|
|
||||||
logic_inst = self.logic_inst[i]
|
logic_inst = self.logic_inst[i]
|
||||||
inv_inst = self.inv_inst[i]
|
inv_inst = self.inv_inst[i]
|
||||||
|
|
||||||
input_name = self.input_control_signals[i]
|
input_name = self.input_control_signals[i]
|
||||||
gated_name = self.control_signals[i]
|
gated_name = self.control_signals[i]
|
||||||
if input_name in ("clk_buf"):
|
if input_name in ("clk_buf"):
|
||||||
xoffset_bank_signal = xoffset_bank_sel_bar
|
xoffset_bank_signal = xoffset_bank_sel_bar
|
||||||
else:
|
else:
|
||||||
xoffset_bank_signal = xoffset_bank_sel
|
xoffset_bank_signal = xoffset_bank_sel
|
||||||
|
|
||||||
# Connect the logic output to inverter input
|
# Connect the logic output to inverter input
|
||||||
out_pin = logic_inst.get_pin("Z")
|
out_pin = logic_inst.get_pin("Z")
|
||||||
out_pos = out_pin.center()
|
out_pos = out_pin.center()
|
||||||
|
|
@ -248,7 +248,7 @@ class bank_select(design.design):
|
||||||
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
|
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
|
||||||
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
|
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
|
||||||
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
|
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
|
||||||
|
|
||||||
# Connect the logic B input to bank_sel / bank_sel_bar
|
# Connect the logic B input to bank_sel / bank_sel_bar
|
||||||
logic_pin = logic_inst.get_pin("B")
|
logic_pin = logic_inst.get_pin("B")
|
||||||
logic_pos = logic_pin.center()
|
logic_pos = logic_pin.center()
|
||||||
|
|
@ -304,7 +304,7 @@ class bank_select(design.design):
|
||||||
self.add_layout_pin_rect_center(text=n,
|
self.add_layout_pin_rect_center(text=n,
|
||||||
layer="m3",
|
layer="m3",
|
||||||
offset=pin_pos)
|
offset=pin_pos)
|
||||||
|
|
||||||
# Add vdd/gnd supply rails
|
# Add vdd/gnd supply rails
|
||||||
gnd_pin = self.inv_inst[num].get_pin("gnd")
|
gnd_pin = self.inv_inst[num].get_pin("gnd")
|
||||||
left_gnd_pos = vector(0, gnd_pin.cy())
|
left_gnd_pos = vector(0, gnd_pin.cy())
|
||||||
|
|
@ -312,7 +312,7 @@ class bank_select(design.design):
|
||||||
layer="m1",
|
layer="m1",
|
||||||
start=left_gnd_pos,
|
start=left_gnd_pos,
|
||||||
end=gnd_pin.rc())
|
end=gnd_pin.rc())
|
||||||
|
|
||||||
vdd_pin = self.inv_inst[num].get_pin("vdd")
|
vdd_pin = self.inv_inst[num].get_pin("vdd")
|
||||||
left_vdd_pos = vector(0, vdd_pin.cy())
|
left_vdd_pos = vector(0, vdd_pin.cy())
|
||||||
self.add_layout_pin_segment_center(text="vdd",
|
self.add_layout_pin_segment_center(text="vdd",
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,11 @@ class bitcell_array(bitcell_base_array):
|
||||||
# This will create a default set of bitline/wordline names
|
# This will create a default set of bitline/wordline names
|
||||||
self.create_all_bitline_names()
|
self.create_all_bitline_names()
|
||||||
self.create_all_wordline_names()
|
self.create_all_wordline_names()
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
# We don't offset this because we need to align
|
# We don't offset this because we need to align
|
||||||
# the replica bitcell in the control logic
|
# the replica bitcell in the control logic
|
||||||
# self.offset_all_coordinates()
|
# self.offset_all_coordinates()
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,11 @@ class bitcell_base_array(design.design):
|
||||||
"br_{0}_{1}".format(port, col)])
|
"br_{0}_{1}".format(port, col)])
|
||||||
# Make a flat list too
|
# Make a flat list too
|
||||||
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
|
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
|
||||||
|
|
||||||
def create_all_wordline_names(self, row_size=None):
|
def create_all_wordline_names(self, row_size=None):
|
||||||
if row_size == None:
|
if row_size == None:
|
||||||
row_size = self.row_size
|
row_size = self.row_size
|
||||||
|
|
||||||
for row in range(row_size):
|
for row in range(row_size):
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
|
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
|
||||||
|
|
@ -69,7 +69,7 @@ class bitcell_base_array(design.design):
|
||||||
def get_bitcell_pins(self, row, col):
|
def get_bitcell_pins(self, row, col):
|
||||||
"""
|
"""
|
||||||
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
|
||||||
"""
|
"""
|
||||||
bitcell_pins = []
|
bitcell_pins = []
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -81,7 +81,7 @@ class bitcell_base_array(design.design):
|
||||||
return bitcell_pins
|
return bitcell_pins
|
||||||
|
|
||||||
def get_rbl_wordline_names(self, port=None):
|
def get_rbl_wordline_names(self, port=None):
|
||||||
"""
|
"""
|
||||||
Return the WL for the given RBL port.
|
Return the WL for the given RBL port.
|
||||||
"""
|
"""
|
||||||
if port == None:
|
if port == None:
|
||||||
|
|
@ -102,7 +102,7 @@ class bitcell_base_array(design.design):
|
||||||
return self.all_bitline_names
|
return self.all_bitline_names
|
||||||
else:
|
else:
|
||||||
return self.bitline_names[port]
|
return self.bitline_names[port]
|
||||||
|
|
||||||
def get_all_bitline_names(self, port=None):
|
def get_all_bitline_names(self, port=None):
|
||||||
""" Return ALL the bitline names (including rbl) """
|
""" Return ALL the bitline names (including rbl) """
|
||||||
temp = []
|
temp = []
|
||||||
|
|
@ -121,7 +121,7 @@ class bitcell_base_array(design.design):
|
||||||
return self.all_wordline_names
|
return self.all_wordline_names
|
||||||
else:
|
else:
|
||||||
return self.wordline_names[port]
|
return self.wordline_names[port]
|
||||||
|
|
||||||
def get_all_wordline_names(self, port=None):
|
def get_all_wordline_names(self, port=None):
|
||||||
""" Return all the wordline names """
|
""" Return all the wordline names """
|
||||||
temp = []
|
temp = []
|
||||||
|
|
@ -133,7 +133,7 @@ class bitcell_base_array(design.design):
|
||||||
if len(self.all_ports) > 1:
|
if len(self.all_ports) > 1:
|
||||||
temp.extend(self.get_rbl_wordline_names(1))
|
temp.extend(self.get_rbl_wordline_names(1))
|
||||||
return temp
|
return temp
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
""" Add the layout pins """
|
""" Add the layout pins """
|
||||||
bitline_names = self.cell.get_all_bitline_names()
|
bitline_names = self.cell.get_all_bitline_names()
|
||||||
|
|
@ -161,7 +161,7 @@ class bitcell_base_array(design.design):
|
||||||
offset=wl_pin.ll().scale(0, 1),
|
offset=wl_pin.ll().scale(0, 1),
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=wl_pin.height())
|
height=wl_pin.height())
|
||||||
|
|
||||||
# Copy a vdd/gnd layout pin from every cell
|
# Copy a vdd/gnd layout pin from every cell
|
||||||
for row in range(self.row_size):
|
for row in range(self.row_size):
|
||||||
for col in range(self.column_size):
|
for col in range(self.column_size):
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class col_cap_array(bitcell_base_array):
|
||||||
if not end_caps_enabled:
|
if not end_caps_enabled:
|
||||||
self.create_all_wordline_names()
|
self.create_all_wordline_names()
|
||||||
self.create_all_bitline_names()
|
self.create_all_bitline_names()
|
||||||
|
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_instances()
|
self.create_instances()
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class column_mux_array(design.design):
|
||||||
# self.sel_layer = "m1"
|
# self.sel_layer = "m1"
|
||||||
# self.sel_pitch = self.m2_pitch
|
# self.sel_pitch = self.m2_pitch
|
||||||
# self.bitline_layer = "m2"
|
# self.bitline_layer = "m2"
|
||||||
|
|
||||||
if preferred_directions[self.sel_layer] == "V":
|
if preferred_directions[self.sel_layer] == "V":
|
||||||
self.via_directions = ("H", "H")
|
self.via_directions = ("H", "H")
|
||||||
else:
|
else:
|
||||||
|
|
@ -125,7 +125,7 @@ class column_mux_array(design.design):
|
||||||
# Default to single spaced columns
|
# Default to single spaced columns
|
||||||
if not self.offsets:
|
if not self.offsets:
|
||||||
self.offsets = [n * self.mux.width for n in range(self.columns)]
|
self.offsets = [n * self.mux.width for n in range(self.columns)]
|
||||||
|
|
||||||
# For every column, add a pass gate
|
# For every column, add a pass gate
|
||||||
for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
|
for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
|
||||||
if cell_props.bitcell.mirror.y and (col_num + self.column_offset) % 2:
|
if cell_props.bitcell.mirror.y and (col_num + self.column_offset) % 2:
|
||||||
|
|
@ -209,7 +209,7 @@ class column_mux_array(design.design):
|
||||||
br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc()
|
br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc()
|
||||||
bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.sel_pitch)
|
bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.sel_pitch)
|
||||||
br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.sel_pitch)
|
br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.sel_pitch)
|
||||||
|
|
||||||
self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end])
|
self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end])
|
||||||
self.add_path(self.sel_layer, [br_out_offset_begin, br_out_offset_end])
|
self.add_path(self.sel_layer, [br_out_offset_begin, br_out_offset_end])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class control_logic(design.design):
|
||||||
self.add_comment("num_rows: {0}".format(num_rows))
|
self.add_comment("num_rows: {0}".format(num_rows))
|
||||||
self.add_comment("words_per_row: {0}".format(words_per_row))
|
self.add_comment("words_per_row: {0}".format(words_per_row))
|
||||||
self.add_comment("word_size {0}".format(word_size))
|
self.add_comment("word_size {0}".format(word_size))
|
||||||
|
|
||||||
self.sram=sram
|
self.sram=sram
|
||||||
self.num_rows = num_rows
|
self.num_rows = num_rows
|
||||||
self.words_per_row = words_per_row
|
self.words_per_row = words_per_row
|
||||||
|
|
@ -42,21 +42,21 @@ class control_logic(design.design):
|
||||||
|
|
||||||
self.num_cols = word_size * words_per_row + self.num_spare_cols
|
self.num_cols = word_size * words_per_row + self.num_spare_cols
|
||||||
self.num_words = num_rows * words_per_row
|
self.num_words = num_rows * words_per_row
|
||||||
|
|
||||||
self.enable_delay_chain_resizing = False
|
self.enable_delay_chain_resizing = False
|
||||||
self.inv_parasitic_delay = logical_effort.logical_effort.pinv
|
self.inv_parasitic_delay = logical_effort.logical_effort.pinv
|
||||||
|
|
||||||
# Determines how much larger the sen delay should be. Accounts for possible error in model.
|
# Determines how much larger the sen delay should be. Accounts for possible error in model.
|
||||||
# FIXME: This should be made a parameter
|
# FIXME: This should be made a parameter
|
||||||
self.wl_timing_tolerance = 1
|
self.wl_timing_tolerance = 1
|
||||||
self.wl_stage_efforts = None
|
self.wl_stage_efforts = None
|
||||||
self.sen_stage_efforts = None
|
self.sen_stage_efforts = None
|
||||||
|
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
self.num_control_signals = 2
|
self.num_control_signals = 2
|
||||||
else:
|
else:
|
||||||
self.num_control_signals = 1
|
self.num_control_signals = 1
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -66,7 +66,7 @@ class control_logic(design.design):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_instances()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
""" Create layout and route between modules """
|
""" Create layout and route between modules """
|
||||||
self.place_instances()
|
self.place_instances()
|
||||||
|
|
@ -84,16 +84,16 @@ class control_logic(design.design):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add all the required modules """
|
""" Add all the required modules """
|
||||||
|
|
||||||
self.dff = factory.create(module_type="dff_buf")
|
self.dff = factory.create(module_type="dff_buf")
|
||||||
dff_height = self.dff.height
|
dff_height = self.dff.height
|
||||||
|
|
||||||
self.ctrl_dff_array = factory.create(module_type="dff_buf_array",
|
self.ctrl_dff_array = factory.create(module_type="dff_buf_array",
|
||||||
rows=self.num_control_signals,
|
rows=self.num_control_signals,
|
||||||
columns=1)
|
columns=1)
|
||||||
|
|
||||||
self.add_mod(self.ctrl_dff_array)
|
self.add_mod(self.ctrl_dff_array)
|
||||||
|
|
||||||
self.and2 = factory.create(module_type="pand2",
|
self.and2 = factory.create(module_type="pand2",
|
||||||
size=12,
|
size=12,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
|
|
@ -103,7 +103,7 @@ class control_logic(design.design):
|
||||||
size=self.num_cols,
|
size=self.num_cols,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.rbl_driver)
|
self.add_mod(self.rbl_driver)
|
||||||
|
|
||||||
# clk_buf drives a flop for every address
|
# clk_buf drives a flop for every address
|
||||||
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
|
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
|
||||||
# plus data flops and control flops
|
# plus data flops and control flops
|
||||||
|
|
@ -114,13 +114,13 @@ class control_logic(design.design):
|
||||||
self.clk_buf_driver = factory.create(module_type="pdriver",
|
self.clk_buf_driver = factory.create(module_type="pdriver",
|
||||||
fanout=clock_fanout,
|
fanout=clock_fanout,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
|
|
||||||
self.add_mod(self.clk_buf_driver)
|
self.add_mod(self.clk_buf_driver)
|
||||||
|
|
||||||
# We will use the maximum since this same value is used to size the wl_en
|
# We will use the maximum since this same value is used to size the wl_en
|
||||||
# and the p_en_bar drivers
|
# and the p_en_bar drivers
|
||||||
# max_fanout = max(self.num_rows, self.num_cols)
|
# max_fanout = max(self.num_rows, self.num_cols)
|
||||||
|
|
||||||
# wl_en drives every row in the bank
|
# wl_en drives every row in the bank
|
||||||
self.wl_en_driver = factory.create(module_type="pdriver",
|
self.wl_en_driver = factory.create(module_type="pdriver",
|
||||||
fanout=self.num_rows,
|
fanout=self.num_rows,
|
||||||
|
|
@ -144,7 +144,7 @@ class control_logic(design.design):
|
||||||
size=1,
|
size=1,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.inv)
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
# p_en_bar drives every column in the bitcell array
|
# p_en_bar drives every column in the bitcell array
|
||||||
# but it is sized the same as the wl_en driver with
|
# but it is sized the same as the wl_en driver with
|
||||||
# prepended 3 inverter stages to guarantee it is slower and odd polarity
|
# prepended 3 inverter stages to guarantee it is slower and odd polarity
|
||||||
|
|
@ -183,14 +183,14 @@ class control_logic(design.design):
|
||||||
# Fanout can be varied as well but is a little more complicated but potentially optimal.
|
# Fanout can be varied as well but is a little more complicated but potentially optimal.
|
||||||
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
|
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
|
||||||
return (delay_stages, delay_fanout)
|
return (delay_stages, delay_fanout)
|
||||||
|
|
||||||
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout):
|
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout):
|
||||||
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
|
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
|
||||||
|
|
||||||
previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay
|
previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay
|
||||||
previous_delay_chain_delay = previous_delay_per_stage * previous_stages
|
previous_delay_chain_delay = previous_delay_per_stage * previous_stages
|
||||||
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
|
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
|
||||||
|
|
||||||
fanout_rise = fanout_fall = 2 # This can be anything >=2
|
fanout_rise = fanout_fall = 2 # This can be anything >=2
|
||||||
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
|
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
|
||||||
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
|
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
|
||||||
|
|
@ -201,7 +201,7 @@ class control_logic(design.design):
|
||||||
debug.info(2,
|
debug.info(2,
|
||||||
"Required delays from chain: fall={}, rise={}".format(required_delay_fall,
|
"Required delays from chain: fall={}, rise={}".format(required_delay_fall,
|
||||||
required_delay_rise))
|
required_delay_rise))
|
||||||
|
|
||||||
# If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
|
# If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
|
||||||
WARNING_FANOUT_DIFF = 5
|
WARNING_FANOUT_DIFF = 5
|
||||||
stages_close = False
|
stages_close = False
|
||||||
|
|
@ -218,7 +218,7 @@ class control_logic(design.design):
|
||||||
stages_close = True
|
stages_close = True
|
||||||
safe_fanout_rise = fanout_rise
|
safe_fanout_rise = fanout_rise
|
||||||
safe_fanout_fall = fanout_fall
|
safe_fanout_fall = fanout_fall
|
||||||
|
|
||||||
if stages_fall == stages_rise:
|
if stages_fall == stages_rise:
|
||||||
break
|
break
|
||||||
elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
|
elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
|
||||||
|
|
@ -232,14 +232,14 @@ class control_logic(design.design):
|
||||||
fanout_fall+=1
|
fanout_fall+=1
|
||||||
else:
|
else:
|
||||||
fanout_rise+=1
|
fanout_rise+=1
|
||||||
|
|
||||||
total_stages = max(stages_fall, stages_rise) * 2
|
total_stages = max(stages_fall, stages_rise) * 2
|
||||||
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
|
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
|
||||||
|
|
||||||
# Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
|
# Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
|
||||||
stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)]
|
stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)]
|
||||||
return stage_list
|
return stage_list
|
||||||
|
|
||||||
def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
|
def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
|
||||||
from math import ceil
|
from math import ceil
|
||||||
# Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
|
# Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
|
||||||
|
|
@ -249,7 +249,7 @@ class control_logic(design.design):
|
||||||
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
|
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
|
||||||
delay_stages = ceil(required_delay / delay_per_stage)
|
delay_stages = ceil(required_delay / delay_per_stage)
|
||||||
return delay_stages
|
return delay_stages
|
||||||
|
|
||||||
def setup_signal_busses(self):
|
def setup_signal_busses(self):
|
||||||
""" Setup bus names, determine the size of the busses etc """
|
""" Setup bus names, determine the size of the busses etc """
|
||||||
|
|
||||||
|
|
@ -265,7 +265,7 @@ class control_logic(design.design):
|
||||||
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
|
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
|
||||||
else:
|
else:
|
||||||
self.dff_output_list = ["cs_bar", "cs"]
|
self.dff_output_list = ["cs_bar", "cs"]
|
||||||
|
|
||||||
# list of output control signals (for making a vertical bus)
|
# list of output control signals (for making a vertical bus)
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"]
|
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"]
|
||||||
|
|
@ -275,7 +275,7 @@ class control_logic(design.design):
|
||||||
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
|
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
|
||||||
# leave space for the bus plus one extra space
|
# leave space for the bus plus one extra space
|
||||||
self.internal_bus_width = (len(self.internal_bus_list) + 1) * self.m2_pitch
|
self.internal_bus_width = (len(self.internal_bus_list) + 1) * self.m2_pitch
|
||||||
|
|
||||||
# Outputs to the bank
|
# Outputs to the bank
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
self.output_list = ["s_en", "w_en"]
|
self.output_list = ["s_en", "w_en"]
|
||||||
|
|
@ -286,14 +286,14 @@ class control_logic(design.design):
|
||||||
self.output_list.append("p_en_bar")
|
self.output_list.append("p_en_bar")
|
||||||
self.output_list.append("wl_en")
|
self.output_list.append("wl_en")
|
||||||
self.output_list.append("clk_buf")
|
self.output_list.append("clk_buf")
|
||||||
|
|
||||||
self.supply_list = ["vdd", "gnd"]
|
self.supply_list = ["vdd", "gnd"]
|
||||||
|
|
||||||
def route_rails(self):
|
def route_rails(self):
|
||||||
""" Add the input signal inverted tracks """
|
""" Add the input signal inverted tracks """
|
||||||
height = self.control_logic_center.y - self.m2_pitch
|
height = self.control_logic_center.y - self.m2_pitch
|
||||||
offset = vector(self.ctrl_dff_array.width, 0)
|
offset = vector(self.ctrl_dff_array.width, 0)
|
||||||
|
|
||||||
self.input_bus = self.create_vertical_bus("m2",
|
self.input_bus = self.create_vertical_bus("m2",
|
||||||
offset,
|
offset,
|
||||||
self.internal_bus_list,
|
self.internal_bus_list,
|
||||||
|
|
@ -325,7 +325,7 @@ class control_logic(design.design):
|
||||||
|
|
||||||
# All of the control logic is placed to the right of the DFFs and bus
|
# All of the control logic is placed to the right of the DFFs and bus
|
||||||
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width
|
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width
|
||||||
|
|
||||||
row = 0
|
row = 0
|
||||||
# Add the logic on the right of the bus
|
# Add the logic on the right of the bus
|
||||||
self.place_clk_buf_row(row)
|
self.place_clk_buf_row(row)
|
||||||
|
|
@ -396,7 +396,7 @@ class control_logic(design.design):
|
||||||
# Add to the right of the control rows and routing channel
|
# Add to the right of the control rows and routing channel
|
||||||
offset = vector(self.delay_chain.width, y_off)
|
offset = vector(self.delay_chain.width, y_off)
|
||||||
self.delay_inst.place(offset, mirror="MY")
|
self.delay_inst.place(offset, mirror="MY")
|
||||||
|
|
||||||
def route_delay(self):
|
def route_delay(self):
|
||||||
|
|
||||||
out_pos = self.delay_inst.get_pin("out").bc()
|
out_pos = self.delay_inst.get_pin("out").bc()
|
||||||
|
|
@ -408,21 +408,21 @@ class control_logic(design.design):
|
||||||
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
|
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=in_pos)
|
offset=in_pos)
|
||||||
|
|
||||||
# Input from RBL goes to the delay line for futher delay
|
# Input from RBL goes to the delay line for futher delay
|
||||||
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
|
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
|
||||||
|
|
||||||
def create_clk_buf_row(self):
|
def create_clk_buf_row(self):
|
||||||
""" Create the multistage and gated clock buffer """
|
""" Create the multistage and gated clock buffer """
|
||||||
self.clk_buf_inst = self.add_inst(name="clkbuf",
|
self.clk_buf_inst = self.add_inst(name="clkbuf",
|
||||||
mod=self.clk_buf_driver)
|
mod=self.clk_buf_driver)
|
||||||
self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
|
self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_clk_buf_row(self, row):
|
def place_clk_buf_row(self, row):
|
||||||
x_offset = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
|
|
||||||
x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
|
x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
|
||||||
|
|
||||||
self.row_end_inst.append(self.clk_buf_inst)
|
self.row_end_inst.append(self.clk_buf_inst)
|
||||||
|
|
||||||
def route_clk_buf(self):
|
def route_clk_buf(self):
|
||||||
|
|
@ -443,17 +443,17 @@ class control_logic(design.design):
|
||||||
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
|
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
|
||||||
mod=self.inv)
|
mod=self.inv)
|
||||||
self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"])
|
self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"])
|
||||||
|
|
||||||
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
|
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
|
||||||
mod=self.and2)
|
mod=self.and2)
|
||||||
self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
|
self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_gated_clk_bar_row(self, row):
|
def place_gated_clk_bar_row(self, row):
|
||||||
x_offset = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
|
|
||||||
x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
|
x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
|
||||||
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
|
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
|
||||||
|
|
||||||
self.row_end_inst.append(self.gated_clk_bar_inst)
|
self.row_end_inst.append(self.gated_clk_bar_inst)
|
||||||
|
|
||||||
def route_gated_clk_bar(self):
|
def route_gated_clk_bar(self):
|
||||||
|
|
@ -468,7 +468,7 @@ class control_logic(design.design):
|
||||||
self.add_via_stack_center(from_layer=out_pin.layer,
|
self.add_via_stack_center(from_layer=out_pin.layer,
|
||||||
to_layer=in_pin.layer,
|
to_layer=in_pin.layer,
|
||||||
offset=in_pos)
|
offset=in_pos)
|
||||||
|
|
||||||
|
|
||||||
# This is the second gate over, so it needs to be on M3
|
# This is the second gate over, so it needs to be on M3
|
||||||
clkbuf_map = zip(["B"], ["cs"])
|
clkbuf_map = zip(["B"], ["cs"])
|
||||||
|
|
@ -495,9 +495,9 @@ class control_logic(design.design):
|
||||||
x_offset = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
|
|
||||||
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
|
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
|
||||||
|
|
||||||
self.row_end_inst.append(self.gated_clk_buf_inst)
|
self.row_end_inst.append(self.gated_clk_buf_inst)
|
||||||
|
|
||||||
def route_gated_clk_buf(self):
|
def route_gated_clk_buf(self):
|
||||||
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
|
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
|
||||||
self.connect_vertical_bus(clkbuf_map,
|
self.connect_vertical_bus(clkbuf_map,
|
||||||
|
|
@ -514,7 +514,7 @@ class control_logic(design.design):
|
||||||
self.add_via_stack_center(from_layer=z_pin.layer,
|
self.add_via_stack_center(from_layer=z_pin.layer,
|
||||||
to_layer="m2",
|
to_layer="m2",
|
||||||
offset=z_pin.center())
|
offset=z_pin.center())
|
||||||
|
|
||||||
def create_wlen_row(self):
|
def create_wlen_row(self):
|
||||||
# input pre_p_en, output: wl_en
|
# input pre_p_en, output: wl_en
|
||||||
self.wl_en_inst=self.add_inst(name="buf_wl_en",
|
self.wl_en_inst=self.add_inst(name="buf_wl_en",
|
||||||
|
|
@ -531,7 +531,7 @@ class control_logic(design.design):
|
||||||
def route_wlen(self):
|
def route_wlen(self):
|
||||||
wlen_map = zip(["A"], ["gated_clk_bar"])
|
wlen_map = zip(["A"], ["gated_clk_bar"])
|
||||||
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.input_bus)
|
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.input_bus)
|
||||||
|
|
||||||
self.connect_output(self.wl_en_inst, "Z", "wl_en")
|
self.connect_output(self.wl_en_inst, "Z", "wl_en")
|
||||||
|
|
||||||
def create_pen_row(self):
|
def create_pen_row(self):
|
||||||
|
|
@ -544,7 +544,7 @@ class control_logic(design.design):
|
||||||
self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar",
|
self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar",
|
||||||
mod=self.p_en_bar_driver)
|
mod=self.p_en_bar_driver)
|
||||||
self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"])
|
self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_pen_row(self, row):
|
def place_pen_row(self, row):
|
||||||
x_offset = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
|
|
||||||
|
|
@ -568,7 +568,7 @@ class control_logic(design.design):
|
||||||
offset=in_pin.center())
|
offset=in_pin.center())
|
||||||
|
|
||||||
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
|
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
|
||||||
|
|
||||||
def create_sen_row(self):
|
def create_sen_row(self):
|
||||||
""" Create the sense enable buffer. """
|
""" Create the sense enable buffer. """
|
||||||
if self.port_type=="rw":
|
if self.port_type=="rw":
|
||||||
|
|
@ -582,24 +582,24 @@ class control_logic(design.design):
|
||||||
# we also must wait until the bitline has been discharged enough for proper sensing
|
# we also must wait until the bitline has been discharged enough for proper sensing
|
||||||
# hence we use rbl_bl_delay as well.
|
# hence we use rbl_bl_delay as well.
|
||||||
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
|
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_sen_row(self, row):
|
def place_sen_row(self, row):
|
||||||
x_offset = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
|
|
||||||
x_offset = self.place_util(self.s_en_gate_inst, x_offset, row)
|
x_offset = self.place_util(self.s_en_gate_inst, x_offset, row)
|
||||||
|
|
||||||
self.row_end_inst.append(self.s_en_gate_inst)
|
self.row_end_inst.append(self.s_en_gate_inst)
|
||||||
|
|
||||||
def route_sen(self):
|
def route_sen(self):
|
||||||
|
|
||||||
if self.port_type=="rw":
|
if self.port_type=="rw":
|
||||||
input_name = "we_bar"
|
input_name = "we_bar"
|
||||||
else:
|
else:
|
||||||
input_name = "cs"
|
input_name = "cs"
|
||||||
|
|
||||||
sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name])
|
sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name])
|
||||||
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.input_bus)
|
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.input_bus)
|
||||||
|
|
||||||
self.connect_output(self.s_en_gate_inst, "Z", "s_en")
|
self.connect_output(self.s_en_gate_inst, "Z", "s_en")
|
||||||
|
|
||||||
def create_rbl_delay_row(self):
|
def create_rbl_delay_row(self):
|
||||||
|
|
@ -612,15 +612,15 @@ class control_logic(design.design):
|
||||||
x_offset = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
|
|
||||||
x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row)
|
x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row)
|
||||||
|
|
||||||
self.row_end_inst.append(self.rbl_bl_delay_inv_inst)
|
self.row_end_inst.append(self.rbl_bl_delay_inv_inst)
|
||||||
|
|
||||||
def route_rbl_delay(self):
|
def route_rbl_delay(self):
|
||||||
# Connect from delay line
|
# Connect from delay line
|
||||||
# Connect to rail
|
# Connect to rail
|
||||||
|
|
||||||
self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar")
|
self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar")
|
||||||
|
|
||||||
rbl_map = zip(["A"], ["rbl_bl_delay"])
|
rbl_map = zip(["A"], ["rbl_bl_delay"])
|
||||||
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.input_bus)
|
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.input_bus)
|
||||||
|
|
||||||
|
|
@ -638,26 +638,26 @@ class control_logic(design.design):
|
||||||
mod=self.wen_and)
|
mod=self.wen_and)
|
||||||
# Only drive the writes in the second half of the clock cycle during a write operation.
|
# Only drive the writes in the second half of the clock cycle during a write operation.
|
||||||
self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"])
|
self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_wen_row(self, row):
|
def place_wen_row(self, row):
|
||||||
x_offset = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
|
|
||||||
x_offset = self.place_util(self.w_en_gate_inst, x_offset, row)
|
x_offset = self.place_util(self.w_en_gate_inst, x_offset, row)
|
||||||
|
|
||||||
self.row_end_inst.append(self.w_en_gate_inst)
|
self.row_end_inst.append(self.w_en_gate_inst)
|
||||||
|
|
||||||
def route_wen(self):
|
def route_wen(self):
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
input_name = "we"
|
input_name = "we"
|
||||||
else:
|
else:
|
||||||
# No we for write-only reports, so use cs
|
# No we for write-only reports, so use cs
|
||||||
input_name = "cs"
|
input_name = "cs"
|
||||||
|
|
||||||
wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"])
|
wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"])
|
||||||
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus)
|
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus)
|
||||||
|
|
||||||
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
|
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
|
||||||
|
|
||||||
def create_dffs(self):
|
def create_dffs(self):
|
||||||
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
|
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
|
||||||
mod=self.ctrl_dff_array)
|
mod=self.ctrl_dff_array)
|
||||||
|
|
@ -669,7 +669,7 @@ class control_logic(design.design):
|
||||||
|
|
||||||
def place_dffs(self):
|
def place_dffs(self):
|
||||||
self.ctrl_dff_inst.place(vector(0, 0))
|
self.ctrl_dff_inst.place(vector(0, 0))
|
||||||
|
|
||||||
def route_dffs(self):
|
def route_dffs(self):
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
|
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
|
||||||
|
|
@ -678,7 +678,7 @@ class control_logic(design.design):
|
||||||
else:
|
else:
|
||||||
dff_out_map = zip(["dout_bar_0"], ["cs"])
|
dff_out_map = zip(["dout_bar_0"], ["cs"])
|
||||||
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1])
|
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1])
|
||||||
|
|
||||||
# Connect the clock rail to the other clock rail
|
# Connect the clock rail to the other clock rail
|
||||||
# by routing in the supply rail track to avoid channel conflicts
|
# by routing in the supply rail track to avoid channel conflicts
|
||||||
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
|
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
|
||||||
|
|
@ -691,7 +691,7 @@ class control_logic(design.design):
|
||||||
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
|
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
|
||||||
if (self.port_type == "rw"):
|
if (self.port_type == "rw"):
|
||||||
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
|
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
|
||||||
|
|
||||||
def get_offset(self, row):
|
def get_offset(self, row):
|
||||||
""" Compute the y-offset and mirroring """
|
""" Compute the y-offset and mirroring """
|
||||||
y_off = row * self.and2.height
|
y_off = row * self.and2.height
|
||||||
|
|
@ -702,14 +702,14 @@ class control_logic(design.design):
|
||||||
mirror="R0"
|
mirror="R0"
|
||||||
|
|
||||||
return (y_off, mirror)
|
return (y_off, mirror)
|
||||||
|
|
||||||
def connect_output(self, inst, pin_name, out_name):
|
def connect_output(self, inst, pin_name, out_name):
|
||||||
""" Create an output pin on the right side from the pin of a given instance. """
|
""" Create an output pin on the right side from the pin of a given instance. """
|
||||||
|
|
||||||
out_pin = inst.get_pin(pin_name)
|
out_pin = inst.get_pin(pin_name)
|
||||||
out_pos = out_pin.center()
|
out_pos = out_pin.center()
|
||||||
right_pos = out_pos + vector(self.width - out_pin.cx(), 0)
|
right_pos = out_pos + vector(self.width - out_pin.cx(), 0)
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=out_pin.layer,
|
self.add_via_stack_center(from_layer=out_pin.layer,
|
||||||
to_layer="m2",
|
to_layer="m2",
|
||||||
offset=out_pos)
|
offset=out_pos)
|
||||||
|
|
@ -722,7 +722,7 @@ class control_logic(design.design):
|
||||||
""" Add vdd and gnd to the instance cells """
|
""" Add vdd and gnd to the instance cells """
|
||||||
|
|
||||||
supply_layer = self.dff.get_pin("vdd").layer
|
supply_layer = self.dff.get_pin("vdd").layer
|
||||||
|
|
||||||
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
|
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
|
||||||
for inst in self.row_end_inst:
|
for inst in self.row_end_inst:
|
||||||
pins = inst.get_pins("vdd")
|
pins = inst.get_pins("vdd")
|
||||||
|
|
@ -740,13 +740,13 @@ class control_logic(design.design):
|
||||||
pin_loc = vector(max_row_x_loc, pin.rc().y)
|
pin_loc = vector(max_row_x_loc, pin.rc().y)
|
||||||
self.add_power_pin("gnd", pin_loc, start_layer=pin.layer)
|
self.add_power_pin("gnd", pin_loc, start_layer=pin.layer)
|
||||||
self.add_path(supply_layer, [row_loc, pin_loc])
|
self.add_path(supply_layer, [row_loc, pin_loc])
|
||||||
|
|
||||||
self.copy_layout_pin(self.delay_inst, "gnd")
|
self.copy_layout_pin(self.delay_inst, "gnd")
|
||||||
self.copy_layout_pin(self.delay_inst, "vdd")
|
self.copy_layout_pin(self.delay_inst, "vdd")
|
||||||
|
|
||||||
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
|
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
|
||||||
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
|
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
|
||||||
|
|
||||||
def add_lvs_correspondence_points(self):
|
def add_lvs_correspondence_points(self):
|
||||||
""" This adds some points for easier debugging if LVS goes wrong.
|
""" This adds some points for easier debugging if LVS goes wrong.
|
||||||
These should probably be turned off by default though, since extraction
|
These should probably be turned off by default though, since extraction
|
||||||
|
|
@ -772,10 +772,10 @@ class control_logic(design.design):
|
||||||
offset=pin.ll(),
|
offset=pin.ll(),
|
||||||
height=pin.height(),
|
height=pin.height(),
|
||||||
width=pin.width())
|
width=pin.width())
|
||||||
|
|
||||||
def graph_exclude_dffs(self):
|
def graph_exclude_dffs(self):
|
||||||
"""Exclude dffs from graph as they do not represent critical path"""
|
"""Exclude dffs from graph as they do not represent critical path"""
|
||||||
|
|
||||||
self.graph_inst_exclude.add(self.ctrl_dff_inst)
|
self.graph_inst_exclude.add(self.ctrl_dff_inst)
|
||||||
if self.port_type=="rw" or self.port_type=="w":
|
if self.port_type=="rw" or self.port_type=="w":
|
||||||
self.graph_inst_exclude.add(self.w_en_gate_inst)
|
self.graph_inst_exclude.add(self.w_en_gate_inst)
|
||||||
|
|
@ -799,4 +799,4 @@ class control_logic(design.design):
|
||||||
self.add_via_stack_center(from_layer=out_pin.layer,
|
self.add_via_stack_center(from_layer=out_pin.layer,
|
||||||
to_layer="m2",
|
to_layer="m2",
|
||||||
offset=out_pos)
|
offset=out_pos)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,271 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
import design
|
|
||||||
import debug
|
|
||||||
from tech import drc
|
|
||||||
from sram_factory import factory
|
|
||||||
from vector import vector
|
|
||||||
from globals import OPTS
|
|
||||||
|
|
||||||
|
|
||||||
class custom_cell(design.design):
|
|
||||||
"""
|
|
||||||
Array of tristate drivers to write to the bitlines through the column mux.
|
|
||||||
Dynamically generated write driver array of all bitlines.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, name, pins, mod):
|
|
||||||
|
|
||||||
design.design.__init__(self, name)
|
|
||||||
debug.info(1, "Creating {0}".format(self.name))
|
|
||||||
self.add_comment("columns: {0}".format(columns))
|
|
||||||
self.add_comment("word_size {0}".format(word_size))
|
|
||||||
|
|
||||||
self.columns = columns
|
|
||||||
self.word_size = word_size
|
|
||||||
self.write_size = write_size
|
|
||||||
self.column_offset = column_offset
|
|
||||||
self.words_per_row = int(columns / word_size)
|
|
||||||
if not num_spare_cols:
|
|
||||||
self.num_spare_cols = 0
|
|
||||||
else:
|
|
||||||
self.num_spare_cols = num_spare_cols
|
|
||||||
|
|
||||||
if self.write_size:
|
|
||||||
self.num_wmasks = int(self.word_size / self.write_size)
|
|
||||||
|
|
||||||
self.create_netlist()
|
|
||||||
if not OPTS.netlist_only:
|
|
||||||
self.create_layout()
|
|
||||||
|
|
||||||
def get_bl_name(self):
|
|
||||||
bl_name = "bl"
|
|
||||||
return bl_name
|
|
||||||
|
|
||||||
def get_br_name(self):
|
|
||||||
br_name = "br"
|
|
||||||
return br_name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data_name(self):
|
|
||||||
return "data"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def en_name(self):
|
|
||||||
return "en"
|
|
||||||
|
|
||||||
def create_netlist(self):
|
|
||||||
self.add_modules()
|
|
||||||
self.add_pins()
|
|
||||||
self.create_write_array()
|
|
||||||
|
|
||||||
def create_layout(self):
|
|
||||||
|
|
||||||
if self.bitcell.width > self.driver.width:
|
|
||||||
self.width = (self.columns + self.num_spare_cols) * self.bitcell.width
|
|
||||||
self.width_regular_cols = self.columns * self.bitcell.width
|
|
||||||
self.single_col_width = self.bitcell.width
|
|
||||||
else:
|
|
||||||
self.width = (self.columns + self.num_spare_cols) * self.driver.width
|
|
||||||
self.width_regular_cols = self.columns * self.driver.width
|
|
||||||
self.single_col_width = self.driver.width
|
|
||||||
self.height = self.driver.height
|
|
||||||
|
|
||||||
self.place_write_array()
|
|
||||||
self.add_layout_pins()
|
|
||||||
self.add_boundary()
|
|
||||||
self.DRC_LVS()
|
|
||||||
|
|
||||||
def add_pins(self):
|
|
||||||
for i in range(self.word_size + self.num_spare_cols):
|
|
||||||
self.add_pin(self.data_name + "_{0}".format(i), "INPUT")
|
|
||||||
for i in range(self.word_size + self.num_spare_cols):
|
|
||||||
self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT")
|
|
||||||
self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT")
|
|
||||||
if self.write_size:
|
|
||||||
for i in range(self.num_wmasks + self.num_spare_cols):
|
|
||||||
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
|
|
||||||
elif self.num_spare_cols and not self.write_size:
|
|
||||||
for i in range(self.num_spare_cols + 1):
|
|
||||||
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
|
|
||||||
else:
|
|
||||||
self.add_pin(self.en_name, "INPUT")
|
|
||||||
self.add_pin("vdd", "POWER")
|
|
||||||
self.add_pin("gnd", "GROUND")
|
|
||||||
|
|
||||||
def add_modules(self):
|
|
||||||
self.driver = factory.create(module_type="write_driver")
|
|
||||||
self.add_mod(self.driver)
|
|
||||||
|
|
||||||
# This is just used for measurements,
|
|
||||||
# so don't add the module
|
|
||||||
self.bitcell = factory.create(module_type="bitcell")
|
|
||||||
|
|
||||||
def create_write_array(self):
|
|
||||||
self.driver_insts = {}
|
|
||||||
w = 0
|
|
||||||
windex=0
|
|
||||||
for i in range(0, self.columns, self.words_per_row):
|
|
||||||
name = "write_driver{}".format(i)
|
|
||||||
index = int(i / self.words_per_row)
|
|
||||||
self.driver_insts[index]=self.add_inst(name=name,
|
|
||||||
mod=self.driver)
|
|
||||||
|
|
||||||
if self.write_size:
|
|
||||||
self.connect_inst([self.data_name + "_{0}".format(index),
|
|
||||||
self.get_bl_name() + "_{0}".format(index),
|
|
||||||
self.get_br_name() + "_{0}".format(index),
|
|
||||||
self.en_name + "_{0}".format(windex), "vdd", "gnd"])
|
|
||||||
w+=1
|
|
||||||
# when w equals write size, the next en pin can be connected since we are now at the next wmask bit
|
|
||||||
if w == self.write_size:
|
|
||||||
w = 0
|
|
||||||
windex+=1
|
|
||||||
|
|
||||||
elif self.num_spare_cols and not self.write_size:
|
|
||||||
self.connect_inst([self.data_name + "_{0}".format(index),
|
|
||||||
self.get_bl_name() + "_{0}".format(index),
|
|
||||||
self.get_br_name() + "_{0}".format(index),
|
|
||||||
self.en_name + "_{0}".format(0), "vdd", "gnd"])
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.connect_inst([self.data_name + "_{0}".format(index),
|
|
||||||
self.get_bl_name() + "_{0}".format(index),
|
|
||||||
self.get_br_name() + "_{0}".format(index),
|
|
||||||
self.en_name, "vdd", "gnd"])
|
|
||||||
|
|
||||||
for i in range(self.num_spare_cols):
|
|
||||||
index = self.word_size + i
|
|
||||||
if self.write_size:
|
|
||||||
offset = self.num_wmasks
|
|
||||||
else:
|
|
||||||
offset = 1
|
|
||||||
name = "write_driver{}".format(self.columns + i)
|
|
||||||
self.driver_insts[index]=self.add_inst(name=name,
|
|
||||||
mod=self.driver)
|
|
||||||
|
|
||||||
self.connect_inst([self.data_name + "_{0}".format(index),
|
|
||||||
self.get_bl_name() + "_{0}".format(index),
|
|
||||||
self.get_br_name() + "_{0}".format(index),
|
|
||||||
self.en_name + "_{0}".format(i + offset), "vdd", "gnd"])
|
|
||||||
|
|
||||||
def place_write_array(self):
|
|
||||||
from tech import cell_properties
|
|
||||||
if self.bitcell.width > self.driver.width:
|
|
||||||
self.driver_spacing = self.bitcell.width
|
|
||||||
else:
|
|
||||||
self.driver_spacing = self.driver.width
|
|
||||||
for i in range(0, self.columns, self.words_per_row):
|
|
||||||
index = int(i / self.words_per_row)
|
|
||||||
xoffset = i * self.driver_spacing
|
|
||||||
|
|
||||||
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
|
|
||||||
mirror = "MY"
|
|
||||||
xoffset = xoffset + self.driver.width
|
|
||||||
else:
|
|
||||||
mirror = ""
|
|
||||||
|
|
||||||
base = vector(xoffset, 0)
|
|
||||||
self.driver_insts[index].place(offset=base, mirror=mirror)
|
|
||||||
|
|
||||||
# place spare write drivers (if spare columns are specified)
|
|
||||||
for i in range(self.num_spare_cols):
|
|
||||||
index = self.word_size + i
|
|
||||||
xoffset = (self.columns + i) * self.driver_spacing
|
|
||||||
|
|
||||||
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
|
|
||||||
mirror = "MY"
|
|
||||||
xoffset = xoffset + self.driver.width
|
|
||||||
else:
|
|
||||||
mirror = ""
|
|
||||||
|
|
||||||
base = vector(xoffset, 0)
|
|
||||||
self.driver_insts[index].place(offset=base, mirror=mirror)
|
|
||||||
|
|
||||||
def add_layout_pins(self):
|
|
||||||
for i in range(self.word_size + self.num_spare_cols):
|
|
||||||
inst = self.driver_insts[i]
|
|
||||||
din_pin = inst.get_pin(inst.mod.din_name)
|
|
||||||
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
|
|
||||||
layer=din_pin.layer,
|
|
||||||
offset=din_pin.ll(),
|
|
||||||
width=din_pin.width(),
|
|
||||||
height=din_pin.height())
|
|
||||||
bl_pin = inst.get_pin(inst.mod.get_bl_names())
|
|
||||||
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
|
|
||||||
layer=bl_pin.layer,
|
|
||||||
offset=bl_pin.ll(),
|
|
||||||
width=bl_pin.width(),
|
|
||||||
height=bl_pin.height())
|
|
||||||
|
|
||||||
br_pin = inst.get_pin(inst.mod.get_br_names())
|
|
||||||
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
|
|
||||||
layer=br_pin.layer,
|
|
||||||
offset=br_pin.ll(),
|
|
||||||
width=br_pin.width(),
|
|
||||||
height=br_pin.height())
|
|
||||||
|
|
||||||
for n in ["vdd", "gnd"]:
|
|
||||||
pin_list = self.driver_insts[i].get_pins(n)
|
|
||||||
for pin in pin_list:
|
|
||||||
self.add_power_pin(name=n,
|
|
||||||
loc=pin.center(),
|
|
||||||
directions=("V", "V"),
|
|
||||||
start_layer=pin.layer)
|
|
||||||
if self.write_size:
|
|
||||||
for bit in range(self.num_wmasks):
|
|
||||||
inst = self.driver_insts[bit * self.write_size]
|
|
||||||
en_pin = inst.get_pin(inst.mod.en_name)
|
|
||||||
# Determine width of wmask modified en_pin with/without col mux
|
|
||||||
wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing)
|
|
||||||
if (self.words_per_row == 1):
|
|
||||||
en_gap = self.driver_spacing - en_pin.width()
|
|
||||||
else:
|
|
||||||
en_gap = self.driver_spacing
|
|
||||||
|
|
||||||
self.add_layout_pin(text=self.en_name + "_{0}".format(bit),
|
|
||||||
layer=en_pin.layer,
|
|
||||||
offset=en_pin.ll(),
|
|
||||||
width=wmask_en_len - en_gap,
|
|
||||||
height=en_pin.height())
|
|
||||||
|
|
||||||
for i in range(self.num_spare_cols):
|
|
||||||
inst = self.driver_insts[self.word_size + i]
|
|
||||||
en_pin = inst.get_pin(inst.mod.en_name)
|
|
||||||
self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks),
|
|
||||||
layer="m1",
|
|
||||||
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
|
|
||||||
|
|
||||||
elif self.num_spare_cols and not self.write_size:
|
|
||||||
# shorten enable rail to accomodate those for spare write drivers
|
|
||||||
inst = self.driver_insts[0]
|
|
||||||
en_pin = inst.get_pin(inst.mod.en_name)
|
|
||||||
self.add_layout_pin(text=self.en_name + "_{0}".format(0),
|
|
||||||
layer="m1",
|
|
||||||
offset=en_pin.ll(),
|
|
||||||
width=self.width_regular_cols - self.words_per_row * en_pin.width())
|
|
||||||
|
|
||||||
# individual enables for every spare write driver
|
|
||||||
for i in range(self.num_spare_cols):
|
|
||||||
inst = self.driver_insts[self.word_size + i]
|
|
||||||
en_pin = inst.get_pin(inst.mod.en_name)
|
|
||||||
self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1),
|
|
||||||
layer="m1",
|
|
||||||
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
|
|
||||||
|
|
||||||
else:
|
|
||||||
inst = self.driver_insts[0]
|
|
||||||
self.add_layout_pin(text=self.en_name,
|
|
||||||
layer="m1",
|
|
||||||
offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1),
|
|
||||||
width=self.width)
|
|
||||||
|
|
||||||
def get_w_en_cin(self):
|
|
||||||
"""Get the relative capacitance of all the enable connections in the bank"""
|
|
||||||
# The enable is connected to a nand2 for every row.
|
|
||||||
return self.driver.get_w_en_cin() * len(self.driver_insts)
|
|
||||||
|
|
@ -24,14 +24,14 @@ class delay_chain(design.design):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
debug.info(1, "creating delay chain {0}".format(str(fanout_list)))
|
debug.info(1, "creating delay chain {0}".format(str(fanout_list)))
|
||||||
self.add_comment("fanouts: {0}".format(str(fanout_list)))
|
self.add_comment("fanouts: {0}".format(str(fanout_list)))
|
||||||
|
|
||||||
# Two fanouts are needed so that we can route the vdd/gnd connections
|
# Two fanouts are needed so that we can route the vdd/gnd connections
|
||||||
for f in fanout_list:
|
for f in fanout_list:
|
||||||
debug.check(f>=2, "Must have >=2 fanouts for each stage.")
|
debug.check(f>=2, "Must have >=2 fanouts for each stage.")
|
||||||
|
|
||||||
# number of inverters including any fanout loads.
|
# number of inverters including any fanout loads.
|
||||||
self.fanout_list = fanout_list
|
self.fanout_list = fanout_list
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -40,7 +40,7 @@ class delay_chain(design.design):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_inverters()
|
self.create_inverters()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
# Each stage is a a row
|
# Each stage is a a row
|
||||||
self.height = len(self.fanout_list) * self.inv.height
|
self.height = len(self.fanout_list) * self.inv.height
|
||||||
|
|
@ -53,7 +53,7 @@ class delay_chain(design.design):
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
""" Add the pins of the delay chain"""
|
""" Add the pins of the delay chain"""
|
||||||
self.add_pin("in", "INPUT")
|
self.add_pin("in", "INPUT")
|
||||||
|
|
@ -86,7 +86,7 @@ class delay_chain(design.design):
|
||||||
else:
|
else:
|
||||||
stagein_name = "dout_{}".format(stage_num)
|
stagein_name = "dout_{}".format(stage_num)
|
||||||
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
|
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
|
||||||
|
|
||||||
# Now add the dummy loads to the right
|
# Now add the dummy loads to the right
|
||||||
self.load_inst_map[cur_driver]=[]
|
self.load_inst_map[cur_driver]=[]
|
||||||
for i in range(fanout_size):
|
for i in range(fanout_size):
|
||||||
|
|
@ -95,7 +95,7 @@ class delay_chain(design.design):
|
||||||
# Fanout stage is always driven by driver and output is disconnected
|
# Fanout stage is always driven by driver and output is disconnected
|
||||||
disconnect_name = "n_{0}_{1}".format(stage_num, i)
|
disconnect_name = "n_{0}_{1}".format(stage_num, i)
|
||||||
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
|
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
|
||||||
|
|
||||||
# Keep track of all the loads to connect their inputs as a load
|
# Keep track of all the loads to connect their inputs as a load
|
||||||
self.load_inst_map[cur_driver].append(cur_load)
|
self.load_inst_map[cur_driver].append(cur_load)
|
||||||
|
|
||||||
|
|
@ -108,19 +108,19 @@ class delay_chain(design.design):
|
||||||
else:
|
else:
|
||||||
inv_mirror = "R0"
|
inv_mirror = "R0"
|
||||||
inv_offset = vector(0, stage_num * self.inv.height)
|
inv_offset = vector(0, stage_num * self.inv.height)
|
||||||
|
|
||||||
# Add the inverter
|
# Add the inverter
|
||||||
cur_driver=self.driver_inst_list[stage_num]
|
cur_driver=self.driver_inst_list[stage_num]
|
||||||
cur_driver.place(offset=inv_offset,
|
cur_driver.place(offset=inv_offset,
|
||||||
mirror=inv_mirror)
|
mirror=inv_mirror)
|
||||||
|
|
||||||
# Now add the dummy loads to the right
|
# Now add the dummy loads to the right
|
||||||
load_list = self.load_inst_map[cur_driver]
|
load_list = self.load_inst_map[cur_driver]
|
||||||
for i in range(fanout_size):
|
for i in range(fanout_size):
|
||||||
inv_offset += vector(self.inv.width, 0)
|
inv_offset += vector(self.inv.width, 0)
|
||||||
load_list[i].place(offset=inv_offset,
|
load_list[i].place(offset=inv_offset,
|
||||||
mirror=inv_mirror)
|
mirror=inv_mirror)
|
||||||
|
|
||||||
def add_route(self, pin1, pin2):
|
def add_route(self, pin1, pin2):
|
||||||
""" This guarantees that we route from the top to bottom row correctly. """
|
""" This guarantees that we route from the top to bottom row correctly. """
|
||||||
pin1_pos = pin1.center()
|
pin1_pos = pin1.center()
|
||||||
|
|
@ -131,7 +131,7 @@ class delay_chain(design.design):
|
||||||
mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y))
|
mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y))
|
||||||
# Written this way to guarantee it goes right first if we are switching rows
|
# Written this way to guarantee it goes right first if we are switching rows
|
||||||
self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos])
|
self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos])
|
||||||
|
|
||||||
def route_inverters(self):
|
def route_inverters(self):
|
||||||
""" Add metal routing for each of the fanout stages """
|
""" Add metal routing for each of the fanout stages """
|
||||||
|
|
||||||
|
|
@ -180,12 +180,12 @@ class delay_chain(design.design):
|
||||||
self.add_power_pin(pin_name,
|
self.add_power_pin(pin_name,
|
||||||
pin.rc() - vector(self.m1_pitch, 0),
|
pin.rc() - vector(self.m1_pitch, 0),
|
||||||
start_layer=pin.layer)
|
start_layer=pin.layer)
|
||||||
|
|
||||||
pin = load_list[-2].get_pin(pin_name)
|
pin = load_list[-2].get_pin(pin_name)
|
||||||
self.add_power_pin(pin_name,
|
self.add_power_pin(pin_name,
|
||||||
pin.rc() - vector(self.m1_pitch, 0),
|
pin.rc() - vector(self.m1_pitch, 0),
|
||||||
start_layer=pin.layer)
|
start_layer=pin.layer)
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
# input is A pin of first inverter
|
# input is A pin of first inverter
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class dff_array(design.design):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns))
|
debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns))
|
||||||
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -36,11 +36,11 @@ class dff_array(design.design):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_dff_array()
|
self.create_dff_array()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.width = self.columns * self.dff.width
|
self.width = self.columns * self.dff.width
|
||||||
self.height = self.rows * self.dff.height
|
self.height = self.rows * self.dff.height
|
||||||
|
|
||||||
self.place_dff_array()
|
self.place_dff_array()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
@ -49,7 +49,7 @@ class dff_array(design.design):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.dff = factory.create(module_type="dff")
|
self.dff = factory.create(module_type="dff")
|
||||||
self.add_mod(self.dff)
|
self.add_mod(self.dff)
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
|
|
@ -86,7 +86,7 @@ class dff_array(design.design):
|
||||||
mirror = "MX"
|
mirror = "MX"
|
||||||
self.dff_insts[row, col].place(offset=base,
|
self.dff_insts[row, col].place(offset=base,
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
def get_din_name(self, row, col):
|
def get_din_name(self, row, col):
|
||||||
if self.columns == 1:
|
if self.columns == 1:
|
||||||
din_name = "din_{0}".format(row)
|
din_name = "din_{0}".format(row)
|
||||||
|
|
@ -96,7 +96,7 @@ class dff_array(design.design):
|
||||||
din_name = "din_{0}_{1}".format(row, col)
|
din_name = "din_{0}_{1}".format(row, col)
|
||||||
|
|
||||||
return din_name
|
return din_name
|
||||||
|
|
||||||
def get_dout_name(self, row, col):
|
def get_dout_name(self, row, col):
|
||||||
if self.columns == 1:
|
if self.columns == 1:
|
||||||
dout_name = "dout_{0}".format(row)
|
dout_name = "dout_{0}".format(row)
|
||||||
|
|
@ -106,7 +106,7 @@ class dff_array(design.design):
|
||||||
dout_name = "dout_{0}_{1}".format(row, col)
|
dout_name = "dout_{0}_{1}".format(row, col)
|
||||||
|
|
||||||
return dout_name
|
return dout_name
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
|
|
@ -117,7 +117,7 @@ class dff_array(design.design):
|
||||||
# Continous gnd rail along with label.
|
# Continous gnd rail along with label.
|
||||||
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
|
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
|
||||||
self.add_power_pin("gnd", gnd_pin.center(), start_layer=gnd_pin.layer)
|
self.add_power_pin("gnd", gnd_pin.center(), start_layer=gnd_pin.layer)
|
||||||
|
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
din_pin = self.dff_insts[row, col].get_pin("D")
|
din_pin = self.dff_insts[row, col].get_pin("D")
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
from tech import parameter, layer
|
from tech import layer
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
@ -21,16 +21,15 @@ class dff_buf(design.design):
|
||||||
and qbar. This is to enable driving large fanout loads.
|
and qbar. This is to enable driving large fanout loads.
|
||||||
"""
|
"""
|
||||||
unique_id = 1
|
unique_id = 1
|
||||||
|
|
||||||
def __init__(self, inv1_size=2, inv2_size=4, name=""):
|
|
||||||
|
|
||||||
|
def __init__(self, inv1_size=2, inv2_size=4, name=""):
|
||||||
if name=="":
|
if name=="":
|
||||||
name = "dff_buf_{0}".format(dff_buf.unique_id)
|
name = "dff_buf_{0}".format(dff_buf.unique_id)
|
||||||
dff_buf.unique_id += 1
|
dff_buf.unique_id += 1
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
debug.info(1, "Creating {}".format(self.name))
|
debug.info(1, "Creating {}".format(self.name))
|
||||||
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
|
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
|
||||||
|
|
||||||
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
|
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
|
||||||
# This causes a DRC in the pinv which assumes min width rails. This ensures the output
|
# This causes a DRC in the pinv which assumes min width rails. This ensures the output
|
||||||
# contact does not violate spacing to the rail in the NMOS.
|
# contact does not violate spacing to the rail in the NMOS.
|
||||||
|
|
@ -39,7 +38,7 @@ class dff_buf(design.design):
|
||||||
|
|
||||||
self.inv1_size=inv1_size
|
self.inv1_size=inv1_size
|
||||||
self.inv2_size=inv2_size
|
self.inv2_size=inv2_size
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -57,7 +56,7 @@ class dff_buf(design.design):
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.dff = factory.create(module_type="dff")
|
self.dff = factory.create(module_type="dff")
|
||||||
self.add_mod(self.dff)
|
self.add_mod(self.dff)
|
||||||
|
|
@ -71,7 +70,7 @@ class dff_buf(design.design):
|
||||||
size=self.inv2_size,
|
size=self.inv2_size,
|
||||||
height=self.dff.height)
|
height=self.dff.height)
|
||||||
self.add_mod(self.inv2)
|
self.add_mod(self.inv2)
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
self.add_pin("D", "INPUT")
|
self.add_pin("D", "INPUT")
|
||||||
self.add_pin("Q", "OUTPUT")
|
self.add_pin("Q", "OUTPUT")
|
||||||
|
|
@ -93,7 +92,7 @@ class dff_buf(design.design):
|
||||||
self.inv1_inst=self.add_inst(name="dff_buf_inv1",
|
self.inv1_inst=self.add_inst(name="dff_buf_inv1",
|
||||||
mod=self.inv1)
|
mod=self.inv1)
|
||||||
self.connect_inst(["qint", "Qb", "vdd", "gnd"])
|
self.connect_inst(["qint", "Qb", "vdd", "gnd"])
|
||||||
|
|
||||||
self.inv2_inst=self.add_inst(name="dff_buf_inv2",
|
self.inv2_inst=self.add_inst(name="dff_buf_inv2",
|
||||||
mod=self.inv2)
|
mod=self.inv2)
|
||||||
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
|
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
|
||||||
|
|
@ -119,16 +118,16 @@ class dff_buf(design.design):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0))
|
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0))
|
||||||
|
|
||||||
# Add INV2 to the right
|
# Add INV2 to the right
|
||||||
self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
|
self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
|
||||||
|
|
||||||
def route_wires(self):
|
def route_wires(self):
|
||||||
if "li" in layer:
|
if "li" in layer:
|
||||||
self.route_layer = "li"
|
self.route_layer = "li"
|
||||||
else:
|
else:
|
||||||
self.route_layer = "m1"
|
self.route_layer = "m1"
|
||||||
|
|
||||||
# Route dff q to inv1 a
|
# Route dff q to inv1 a
|
||||||
q_pin = self.dff_inst.get_pin("Q")
|
q_pin = self.dff_inst.get_pin("Q")
|
||||||
a1_pin = self.inv1_inst.get_pin("A")
|
a1_pin = self.inv1_inst.get_pin("A")
|
||||||
|
|
@ -143,7 +142,7 @@ class dff_buf(design.design):
|
||||||
a2_pin = self.inv2_inst.get_pin("A")
|
a2_pin = self.inv2_inst.get_pin("A")
|
||||||
self.mid_qb_pos = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
|
self.mid_qb_pos = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
|
||||||
self.add_zjog(z1_pin.layer, z1_pin.center(), a2_pin.center())
|
self.add_zjog(z1_pin.layer, z1_pin.center(), a2_pin.center())
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
# Continous vdd rail along with label.
|
# Continous vdd rail along with label.
|
||||||
|
|
@ -161,7 +160,7 @@ class dff_buf(design.design):
|
||||||
offset=gnd_pin.ll(),
|
offset=gnd_pin.ll(),
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=vdd_pin.height())
|
height=vdd_pin.height())
|
||||||
|
|
||||||
clk_pin = self.dff_inst.get_pin("clk")
|
clk_pin = self.dff_inst.get_pin("clk")
|
||||||
self.add_layout_pin(text="clk",
|
self.add_layout_pin(text="clk",
|
||||||
layer=clk_pin.layer,
|
layer=clk_pin.layer,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class dff_buf_array(design.design):
|
||||||
Unlike the data flops, these are never spaced out.
|
Unlike the data flops, these are never spaced out.
|
||||||
"""
|
"""
|
||||||
unique_id = 1
|
unique_id = 1
|
||||||
|
|
||||||
def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""):
|
def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""):
|
||||||
self.rows = rows
|
self.rows = rows
|
||||||
self.columns = columns
|
self.columns = columns
|
||||||
|
|
@ -31,10 +31,10 @@ class dff_buf_array(design.design):
|
||||||
debug.info(1, "Creating {}".format(self.name))
|
debug.info(1, "Creating {}".format(self.name))
|
||||||
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
||||||
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
|
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
|
||||||
|
|
||||||
self.inv1_size = inv1_size
|
self.inv1_size = inv1_size
|
||||||
self.inv2_size = inv2_size
|
self.inv2_size = inv2_size
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -110,7 +110,7 @@ class dff_buf_array(design.design):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
dff_pitch = self.dff.width + well_spacing + self.well_extend_active
|
dff_pitch = self.dff.width + well_spacing + self.well_extend_active
|
||||||
|
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
# name = "Xdff_r{0}_c{1}".format(row, col)
|
# name = "Xdff_r{0}_c{1}".format(row, col)
|
||||||
|
|
@ -122,7 +122,7 @@ class dff_buf_array(design.design):
|
||||||
mirror = "MX"
|
mirror = "MX"
|
||||||
self.dff_insts[row, col].place(offset=base,
|
self.dff_insts[row, col].place(offset=base,
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
def get_din_name(self, row, col):
|
def get_din_name(self, row, col):
|
||||||
if self.columns == 1:
|
if self.columns == 1:
|
||||||
din_name = "din_{0}".format(row)
|
din_name = "din_{0}".format(row)
|
||||||
|
|
@ -132,7 +132,7 @@ class dff_buf_array(design.design):
|
||||||
din_name = "din_{0}_{1}".format(row, col)
|
din_name = "din_{0}_{1}".format(row, col)
|
||||||
|
|
||||||
return din_name
|
return din_name
|
||||||
|
|
||||||
def get_dout_name(self, row, col):
|
def get_dout_name(self, row, col):
|
||||||
if self.columns == 1:
|
if self.columns == 1:
|
||||||
dout_name = "dout_{0}".format(row)
|
dout_name = "dout_{0}".format(row)
|
||||||
|
|
@ -142,7 +142,7 @@ class dff_buf_array(design.design):
|
||||||
dout_name = "dout_{0}_{1}".format(row, col)
|
dout_name = "dout_{0}_{1}".format(row, col)
|
||||||
|
|
||||||
return dout_name
|
return dout_name
|
||||||
|
|
||||||
def get_dout_bar_name(self, row, col):
|
def get_dout_bar_name(self, row, col):
|
||||||
if self.columns == 1:
|
if self.columns == 1:
|
||||||
dout_bar_name = "dout_bar_{0}".format(row)
|
dout_bar_name = "dout_bar_{0}".format(row)
|
||||||
|
|
@ -158,11 +158,11 @@ class dff_buf_array(design.design):
|
||||||
vdd0_pin=self.dff_insts[row, 0].get_pin("vdd")
|
vdd0_pin=self.dff_insts[row, 0].get_pin("vdd")
|
||||||
vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd")
|
vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd")
|
||||||
self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height())
|
self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height())
|
||||||
|
|
||||||
gnd0_pin=self.dff_insts[row, 0].get_pin("gnd")
|
gnd0_pin=self.dff_insts[row, 0].get_pin("gnd")
|
||||||
gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd")
|
gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd")
|
||||||
self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height())
|
self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height())
|
||||||
|
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
# Continous vdd rail along with label.
|
# Continous vdd rail along with label.
|
||||||
|
|
@ -172,9 +172,9 @@ class dff_buf_array(design.design):
|
||||||
# Continous gnd rail along with label.
|
# Continous gnd rail along with label.
|
||||||
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
|
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
|
||||||
self.add_power_pin("gnd", gnd_pin.lc(), start_layer=gnd_pin.layer)
|
self.add_power_pin("gnd", gnd_pin.lc(), start_layer=gnd_pin.layer)
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
din_pin = self.dff_insts[row, col].get_pin("D")
|
din_pin = self.dff_insts[row, col].get_pin("D")
|
||||||
|
|
@ -200,7 +200,7 @@ class dff_buf_array(design.design):
|
||||||
offset=dout_bar_pin.ll(),
|
offset=dout_bar_pin.ll(),
|
||||||
width=dout_bar_pin.width(),
|
width=dout_bar_pin.width(),
|
||||||
height=dout_bar_pin.height())
|
height=dout_bar_pin.height())
|
||||||
|
|
||||||
# Create vertical spines to a single horizontal rail
|
# Create vertical spines to a single horizontal rail
|
||||||
clk_pin = self.dff_insts[0, 0].get_pin("clk")
|
clk_pin = self.dff_insts[0, 0].get_pin("clk")
|
||||||
clk_ypos = 2 * self.m3_pitch + self.m3_width
|
clk_ypos = 2 * self.m3_pitch + self.m3_width
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class dff_inv(design.design):
|
||||||
do not have Qbar, so this will create it.
|
do not have Qbar, so this will create it.
|
||||||
"""
|
"""
|
||||||
unique_id = 1
|
unique_id = 1
|
||||||
|
|
||||||
def __init__(self, inv_size=2, name=""):
|
def __init__(self, inv_size=2, name=""):
|
||||||
|
|
||||||
if name=="":
|
if name=="":
|
||||||
|
|
@ -30,7 +30,7 @@ class dff_inv(design.design):
|
||||||
self.add_comment("inv: {0}".format(inv_size))
|
self.add_comment("inv: {0}".format(inv_size))
|
||||||
|
|
||||||
self.inv_size = inv_size
|
self.inv_size = inv_size
|
||||||
|
|
||||||
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
|
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
|
||||||
# This causes a DRC in the pinv which assumes min width rails. This ensures the output
|
# This causes a DRC in the pinv which assumes min width rails. This ensures the output
|
||||||
# contact does not violate spacing to the rail in the NMOS.
|
# contact does not violate spacing to the rail in the NMOS.
|
||||||
|
|
@ -44,7 +44,7 @@ class dff_inv(design.design):
|
||||||
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.width = self.dff.width + self.inv1.width
|
self.width = self.dff.width + self.inv1.width
|
||||||
self.height = self.dff.height
|
self.height = self.dff.height
|
||||||
|
|
@ -52,10 +52,10 @@ class dff_inv(design.design):
|
||||||
self.place_modules()
|
self.place_modules()
|
||||||
self.add_wires()
|
self.add_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
self.add_pin("D")
|
self.add_pin("D")
|
||||||
self.add_pin("Q")
|
self.add_pin("Q")
|
||||||
|
|
@ -67,7 +67,7 @@ class dff_inv(design.design):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.dff = dff_inv.dff_inv(self.inv_size)
|
self.dff = dff_inv.dff_inv(self.inv_size)
|
||||||
self.add_mod(self.dff)
|
self.add_mod(self.dff)
|
||||||
|
|
||||||
self.inv1 = factory.create(module_type="pinv",
|
self.inv1 = factory.create(module_type="pinv",
|
||||||
size=self.inv_size,
|
size=self.inv_size,
|
||||||
height=self.dff.height)
|
height=self.dff.height)
|
||||||
|
|
@ -88,8 +88,8 @@ class dff_inv(design.design):
|
||||||
|
|
||||||
# Place the INV1 to the right
|
# Place the INV1 to the right
|
||||||
self.inv1_inst.place(vector(self.dff_inst.rx(),0))
|
self.inv1_inst.place(vector(self.dff_inst.rx(),0))
|
||||||
|
|
||||||
|
|
||||||
def add_wires(self):
|
def add_wires(self):
|
||||||
# Route dff q to inv1 a
|
# Route dff q to inv1 a
|
||||||
q_pin = self.dff_inst.get_pin("Q")
|
q_pin = self.dff_inst.get_pin("Q")
|
||||||
|
|
@ -106,7 +106,7 @@ class dff_inv(design.design):
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=a1_pin.center())
|
offset=a1_pin.center())
|
||||||
|
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
# Continous vdd rail along with label.
|
# Continous vdd rail along with label.
|
||||||
|
|
@ -124,7 +124,7 @@ class dff_inv(design.design):
|
||||||
offset=gnd_pin.ll(),
|
offset=gnd_pin.ll(),
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=vdd_pin.height())
|
height=vdd_pin.height())
|
||||||
|
|
||||||
clk_pin = self.dff_inst.get_pin("clk")
|
clk_pin = self.dff_inst.get_pin("clk")
|
||||||
self.add_layout_pin(text="clk",
|
self.add_layout_pin(text="clk",
|
||||||
layer=clk_pin.layer,
|
layer=clk_pin.layer,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class dff_inv_array(design.design):
|
||||||
Unlike the data flops, these are never spaced out.
|
Unlike the data flops, these are never spaced out.
|
||||||
"""
|
"""
|
||||||
unique_id = 1
|
unique_id = 1
|
||||||
|
|
||||||
def __init__(self, rows, columns, inv_size=2, name=""):
|
def __init__(self, rows, columns, inv_size=2, name=""):
|
||||||
self.rows = rows
|
self.rows = rows
|
||||||
self.columns = columns
|
self.columns = columns
|
||||||
|
|
@ -31,9 +31,9 @@ class dff_inv_array(design.design):
|
||||||
debug.info(1, "Creating {}".format(self.name))
|
debug.info(1, "Creating {}".format(self.name))
|
||||||
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
self.add_comment("rows: {0} cols: {1}".format(rows, columns))
|
||||||
self.add_comment("inv1: {0}".format(inv1_size))
|
self.add_comment("inv1: {0}".format(inv1_size))
|
||||||
|
|
||||||
self.inv_size = inv_size
|
self.inv_size = inv_size
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -42,7 +42,7 @@ class dff_inv_array(design.design):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_dff_array()
|
self.create_dff_array()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.width = self.columns * self.dff.width
|
self.width = self.columns * self.dff.width
|
||||||
self.height = self.rows * self.dff.height
|
self.height = self.rows * self.dff.height
|
||||||
|
|
@ -53,14 +53,14 @@ class dff_inv_array(design.design):
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.dff = factory.create(module_type="dff")
|
self.dff = factory.create(module_type="dff")
|
||||||
self.add_mod(self.dff)
|
self.add_mod(self.dff)
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
self.add_pin(self.get_din_name(row,col), "INPUT")
|
self.add_pin(self.get_din_name(row,col), "INPUT")
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
self.add_pin(self.get_dout_name(row,col), "OUTPUT")
|
self.add_pin(self.get_dout_name(row,col), "OUTPUT")
|
||||||
self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT")
|
self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT")
|
||||||
|
|
@ -70,20 +70,20 @@ class dff_inv_array(design.design):
|
||||||
|
|
||||||
def create_dff_array(self):
|
def create_dff_array(self):
|
||||||
self.dff_insts={}
|
self.dff_insts={}
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||||
mod=self.dff)
|
mod=self.dff)
|
||||||
self.connect_inst([self.get_din_name(row,col),
|
self.connect_inst([self.get_din_name(row,col),
|
||||||
self.get_dout_name(row,col),
|
self.get_dout_name(row,col),
|
||||||
self.get_dout_bar_name(row,col),
|
self.get_dout_bar_name(row,col),
|
||||||
"clk",
|
"clk",
|
||||||
"vdd",
|
"vdd",
|
||||||
"gnd"])
|
"gnd"])
|
||||||
|
|
||||||
def place_dff_array(self):
|
def place_dff_array(self):
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||||
if (row % 2 == 0):
|
if (row % 2 == 0):
|
||||||
|
|
@ -92,9 +92,9 @@ class dff_inv_array(design.design):
|
||||||
else:
|
else:
|
||||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||||
mirror = "MX"
|
mirror = "MX"
|
||||||
self.dff_insts[row,col].place(offset=base,
|
self.dff_insts[row,col].place(offset=base,
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
def get_din_name(self, row, col):
|
def get_din_name(self, row, col):
|
||||||
if self.columns == 1:
|
if self.columns == 1:
|
||||||
din_name = "din_{0}".format(row)
|
din_name = "din_{0}".format(row)
|
||||||
|
|
@ -104,7 +104,7 @@ class dff_inv_array(design.design):
|
||||||
din_name = "din_{0}_{1}".format(row,col)
|
din_name = "din_{0}_{1}".format(row,col)
|
||||||
|
|
||||||
return din_name
|
return din_name
|
||||||
|
|
||||||
def get_dout_name(self, row, col):
|
def get_dout_name(self, row, col):
|
||||||
if self.columns == 1:
|
if self.columns == 1:
|
||||||
dout_name = "dout_{0}".format(row)
|
dout_name = "dout_{0}".format(row)
|
||||||
|
|
@ -114,7 +114,7 @@ class dff_inv_array(design.design):
|
||||||
dout_name = "dout_{0}_{1}".format(row,col)
|
dout_name = "dout_{0}_{1}".format(row,col)
|
||||||
|
|
||||||
return dout_name
|
return dout_name
|
||||||
|
|
||||||
def get_dout_bar_name(self, row, col):
|
def get_dout_bar_name(self, row, col):
|
||||||
if self.columns == 1:
|
if self.columns == 1:
|
||||||
dout_bar_name = "dout_bar_{0}".format(row)
|
dout_bar_name = "dout_bar_{0}".format(row)
|
||||||
|
|
@ -124,10 +124,10 @@ class dff_inv_array(design.design):
|
||||||
dout_bar_name = "dout_bar_{0}_{1}".format(row,col)
|
dout_bar_name = "dout_bar_{0}_{1}".format(row,col)
|
||||||
|
|
||||||
return dout_bar_name
|
return dout_bar_name
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
# Adds power pin on left of row
|
# Adds power pin on left of row
|
||||||
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
||||||
self.add_power_pin("vdd", vdd_pin.lc())
|
self.add_power_pin("vdd", vdd_pin.lc())
|
||||||
|
|
@ -135,10 +135,10 @@ class dff_inv_array(design.design):
|
||||||
# Adds gnd pin on left of row
|
# Adds gnd pin on left of row
|
||||||
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
||||||
self.add_power_pin("gnd", gnd_pin.lc())
|
self.add_power_pin("gnd", gnd_pin.lc())
|
||||||
|
|
||||||
|
|
||||||
for row in range(self.rows):
|
|
||||||
for col in range(self.columns):
|
for row in range(self.rows):
|
||||||
|
for col in range(self.columns):
|
||||||
din_pin = self.dff_insts[row,col].get_pin("D")
|
din_pin = self.dff_insts[row,col].get_pin("D")
|
||||||
debug.check(din_pin.layer=="m2","DFF D pin not on metal2")
|
debug.check(din_pin.layer=="m2","DFF D pin not on metal2")
|
||||||
self.add_layout_pin(text=self.get_din_name(row,col),
|
self.add_layout_pin(text=self.get_din_name(row,col),
|
||||||
|
|
@ -163,7 +163,7 @@ class dff_inv_array(design.design):
|
||||||
width=dout_bar_pin.width(),
|
width=dout_bar_pin.width(),
|
||||||
height=dout_bar_pin.height())
|
height=dout_bar_pin.height())
|
||||||
|
|
||||||
|
|
||||||
# Create vertical spines to a single horizontal rail
|
# Create vertical spines to a single horizontal rail
|
||||||
clk_pin = self.dff_insts[0,0].get_pin("clk")
|
clk_pin = self.dff_insts[0,0].get_pin("clk")
|
||||||
clk_ypos = 2*self.m3_pitch+self.m3_width
|
clk_ypos = 2*self.m3_pitch+self.m3_width
|
||||||
|
|
@ -188,4 +188,4 @@ class dff_inv_array(design.design):
|
||||||
height=self.height)
|
height=self.height)
|
||||||
# Drop a via to the M3 pin
|
# Drop a via to the M3 pin
|
||||||
self.add_via_center(layers=self.m2_stack,
|
self.add_via_center(layers=self.m2_stack,
|
||||||
offset=vector(clk_pin.cx(),clk_ypos))
|
offset=vector(clk_pin.cx(),clk_ypos))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# See LICENSE for licensing information.
|
# See LICENSE for licensing information.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California
|
# Copyright (c) 2016-2019 Regents of the University of California
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from bitcell_base_array import bitcell_base_array
|
from bitcell_base_array import bitcell_base_array
|
||||||
|
|
@ -20,13 +20,13 @@ class dummy_array(bitcell_base_array):
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
""" Create and connect the netlist """
|
""" Create and connect the netlist """
|
||||||
# This will create a default set of bitline/wordline names
|
# This will create a default set of bitline/wordline names
|
||||||
self.create_all_bitline_names()
|
self.create_all_bitline_names()
|
||||||
self.create_all_wordline_names()
|
self.create_all_wordline_names()
|
||||||
|
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_instances()
|
self.create_instances()
|
||||||
|
|
@ -38,16 +38,16 @@ class dummy_array(bitcell_base_array):
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the modules used in this design """
|
""" Add the modules used in this design """
|
||||||
|
|
||||||
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
|
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
|
||||||
self.cell = factory.create(module_type="bitcell")
|
self.cell = factory.create(module_type="bitcell")
|
||||||
self.add_mod(self.dummy_cell)
|
self.add_mod(self.dummy_cell)
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
self.cell_inst = {}
|
self.cell_inst = {}
|
||||||
|
|
@ -67,7 +67,7 @@ class dummy_array(bitcell_base_array):
|
||||||
self.add_pin(wl_name, "INPUT")
|
self.add_pin(wl_name, "INPUT")
|
||||||
self.add_pin("vdd", "POWER")
|
self.add_pin("vdd", "POWER")
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
""" Add the layout pins """
|
""" Add the layout pins """
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ class dummy_array(bitcell_base_array):
|
||||||
offset=br_pin.ll().scale(1, 0),
|
offset=br_pin.ll().scale(1, 0),
|
||||||
width=br_pin.width(),
|
width=br_pin.width(),
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
wl_names = self.cell.get_all_wl_names()
|
wl_names = self.cell.get_all_wl_names()
|
||||||
for row in range(self.row_size):
|
for row in range(self.row_size):
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -104,7 +104,7 @@ class dummy_array(bitcell_base_array):
|
||||||
inst = self.cell_inst[row, col]
|
inst = self.cell_inst[row, col]
|
||||||
for pin_name in ["vdd", "gnd"]:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
self.copy_layout_pin(inst, pin_name)
|
self.copy_layout_pin(inst, pin_name)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
# FIXME: This appears to be old code from previous characterization. Needs to be updated.
|
# FIXME: This appears to be old code from previous characterization. Needs to be updated.
|
||||||
wl_wire = self.gen_wl_wire()
|
wl_wire = self.gen_wl_wire()
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
|
|
||||||
debug.check(len(self.all_ports)<=2, "Only support dual port or less in global bitcell array.")
|
debug.check(len(self.all_ports)<=2, "Only support dual port or less in global bitcell array.")
|
||||||
self.rbl = [1, 1 if len(self.all_ports)>1 else 0]
|
self.rbl = [1, 1 if len(self.all_ports)>1 else 0]
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
""" Create and connect the netlist """
|
""" Create and connect the netlist """
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
|
|
@ -43,11 +43,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
self.place()
|
self.place()
|
||||||
|
|
||||||
self.route()
|
self.route()
|
||||||
|
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
|
|
@ -88,7 +88,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
rows=self.row_size,
|
rows=self.row_size,
|
||||||
cols=cols,
|
cols=cols,
|
||||||
rbl=self.rbl)
|
rbl=self.rbl)
|
||||||
|
|
||||||
self.add_mod(la)
|
self.add_mod(la)
|
||||||
self.local_mods.append(la)
|
self.local_mods.append(la)
|
||||||
|
|
||||||
|
|
@ -108,7 +108,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
self.rbl_bitline_names[0].append("rbl_bl_{}_0".format(port))
|
self.rbl_bitline_names[0].append("rbl_bl_{}_0".format(port))
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.rbl_bitline_names[0].append("rbl_br_{}_0".format(port))
|
self.rbl_bitline_names[0].append("rbl_br_{}_0".format(port))
|
||||||
|
|
||||||
for col in range(self.column_size):
|
for col in range(self.column_size):
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.bitline_names[port].append("bl_{0}_{1}".format(port, col))
|
self.bitline_names[port].append("bl_{0}_{1}".format(port, col))
|
||||||
|
|
@ -120,7 +120,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
self.rbl_bitline_names[1].append("rbl_bl_{}_1".format(port))
|
self.rbl_bitline_names[1].append("rbl_bl_{}_1".format(port))
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.rbl_bitline_names[1].append("rbl_br_{}_1".format(port))
|
self.rbl_bitline_names[1].append("rbl_br_{}_1".format(port))
|
||||||
|
|
||||||
# Make a flat list too
|
# Make a flat list too
|
||||||
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
|
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
|
||||||
# Make a flat list too
|
# Make a flat list too
|
||||||
|
|
@ -130,19 +130,19 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
self.add_pin_list(self.all_bitline_names, "INOUT")
|
self.add_pin_list(self.all_bitline_names, "INOUT")
|
||||||
if len(self.all_ports) > 1:
|
if len(self.all_ports) > 1:
|
||||||
self.add_pin_list(self.rbl_bitline_names[1], "INOUT")
|
self.add_pin_list(self.rbl_bitline_names[1], "INOUT")
|
||||||
|
|
||||||
def add_wordline_pins(self):
|
def add_wordline_pins(self):
|
||||||
|
|
||||||
self.rbl_wordline_names = [[] for x in self.all_ports]
|
self.rbl_wordline_names = [[] for x in self.all_ports]
|
||||||
|
|
||||||
self.wordline_names = [[] for x in self.all_ports]
|
self.wordline_names = [[] for x in self.all_ports]
|
||||||
|
|
||||||
for bit in self.all_ports:
|
for bit in self.all_ports:
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit))
|
self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit))
|
||||||
|
|
||||||
self.all_rbl_wordline_names = [x for sl in zip(*self.rbl_wordline_names) for x in sl]
|
self.all_rbl_wordline_names = [x for sl in zip(*self.rbl_wordline_names) for x in sl]
|
||||||
|
|
||||||
# Regular WLs
|
# Regular WLs
|
||||||
for row in range(self.row_size):
|
for row in range(self.row_size):
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -163,11 +163,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
name = "la_{0}".format(col)
|
name = "la_{0}".format(col)
|
||||||
self.local_insts.append(self.add_inst(name=name,
|
self.local_insts.append(self.add_inst(name=name,
|
||||||
mod=mod))
|
mod=mod))
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
if col == 0:
|
if col == 0:
|
||||||
temp.extend(self.get_rbl_bitline_names(0))
|
temp.extend(self.get_rbl_bitline_names(0))
|
||||||
|
|
||||||
port_inouts = [x for x in mod.get_inouts() if x.startswith("bl") or x.startswith("br")]
|
port_inouts = [x for x in mod.get_inouts() if x.startswith("bl") or x.startswith("br")]
|
||||||
for pin_name in port_inouts:
|
for pin_name in port_inouts:
|
||||||
# Offset of the last underscore that defines the bit number
|
# Offset of the last underscore that defines the bit number
|
||||||
|
|
@ -180,18 +180,18 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
# Strip the bit and add the new one
|
# Strip the bit and add the new one
|
||||||
new_name = "{0}_{1}".format(base_name, col + col_value)
|
new_name = "{0}_{1}".format(base_name, col + col_value)
|
||||||
temp.append(new_name)
|
temp.append(new_name)
|
||||||
|
|
||||||
if len(self.all_ports) > 1 and mod == self.local_mods[-1]:
|
if len(self.all_ports) > 1 and mod == self.local_mods[-1]:
|
||||||
temp.extend(self.get_rbl_bitline_names(1))
|
temp.extend(self.get_rbl_bitline_names(1))
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
port_inputs = [x for x in mod.get_inputs() if "wl_{}".format(port) in x]
|
port_inputs = [x for x in mod.get_inputs() if "wl_{}".format(port) in x]
|
||||||
temp.extend(port_inputs)
|
temp.extend(port_inputs)
|
||||||
|
|
||||||
temp.append("vdd")
|
temp.append("vdd")
|
||||||
temp.append("gnd")
|
temp.append("gnd")
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
def place(self):
|
def place(self):
|
||||||
offset = vector(0, 0)
|
offset = vector(0, 0)
|
||||||
for inst in self.local_insts:
|
for inst in self.local_insts:
|
||||||
|
|
@ -204,7 +204,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
def route(self):
|
def route(self):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
# Regular bitlines
|
# Regular bitlines
|
||||||
|
|
@ -230,11 +230,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
layer=left_pin.layer,
|
layer=left_pin.layer,
|
||||||
start=left_pin.lc(),
|
start=left_pin.lc(),
|
||||||
end=right_pin.rc())
|
end=right_pin.rc())
|
||||||
|
|
||||||
# Replica bitlines
|
# Replica bitlines
|
||||||
self.copy_layout_pin(self.local_insts[0], "rbl_bl_0_0")
|
self.copy_layout_pin(self.local_insts[0], "rbl_bl_0_0")
|
||||||
self.copy_layout_pin(self.local_insts[0], "rbl_br_0_0")
|
self.copy_layout_pin(self.local_insts[0], "rbl_br_0_0")
|
||||||
|
|
||||||
if len(self.all_ports) > 1:
|
if len(self.all_ports) > 1:
|
||||||
self.copy_layout_pin(self.local_insts[0], "rbl_bl_1_0")
|
self.copy_layout_pin(self.local_insts[0], "rbl_bl_1_0")
|
||||||
self.copy_layout_pin(self.local_insts[0], "rbl_br_1_0")
|
self.copy_layout_pin(self.local_insts[0], "rbl_br_1_0")
|
||||||
|
|
@ -269,10 +269,10 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
return offsets
|
return offsets
|
||||||
|
|
||||||
def graph_exclude_bits(self, targ_row, targ_col):
|
def graph_exclude_bits(self, targ_row, targ_col):
|
||||||
"""
|
|
||||||
Excludes bits in column from being added to graph except target
|
|
||||||
"""
|
"""
|
||||||
|
Excludes bits in column from being added to graph except target
|
||||||
|
"""
|
||||||
|
|
||||||
# This must find which local array includes the specified column
|
# This must find which local array includes the specified column
|
||||||
# Find the summation of columns that is large and take the one before
|
# Find the summation of columns that is large and take the one before
|
||||||
for i, col in enumerate(self.col_offsets):
|
for i, col in enumerate(self.col_offsets):
|
||||||
|
|
@ -303,7 +303,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
|
|
||||||
def get_cell_name(self, inst_name, row, col):
|
def get_cell_name(self, inst_name, row, col):
|
||||||
"""Gets the spice name of the target bitcell."""
|
"""Gets the spice name of the target bitcell."""
|
||||||
|
|
||||||
# This must find which local array includes the specified column
|
# This must find which local array includes the specified column
|
||||||
# Find the summation of columns that is large and take the one before
|
# Find the summation of columns that is large and take the one before
|
||||||
for i, local_col in enumerate(self.col_offsets):
|
for i, local_col in enumerate(self.col_offsets):
|
||||||
|
|
@ -321,9 +321,9 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
local_col = col - self.col_offsets[i - 1]
|
local_col = col - self.col_offsets[i - 1]
|
||||||
|
|
||||||
return local_array.get_cell_name(inst_name + '.x' + local_inst.name, row, local_col)
|
return local_array.get_cell_name(inst_name + '.x' + local_inst.name, row, local_col)
|
||||||
|
|
||||||
def clear_exclude_bits(self):
|
def clear_exclude_bits(self):
|
||||||
"""
|
"""
|
||||||
Clears the bit exclusions
|
Clears the bit exclusions
|
||||||
"""
|
"""
|
||||||
for mod in self.local_mods:
|
for mod in self.local_mods:
|
||||||
|
|
@ -331,6 +331,6 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
|
|
||||||
def graph_exclude_dffs(self):
|
def graph_exclude_dffs(self):
|
||||||
"""Exclude dffs from graph as they do not represent critical path"""
|
"""Exclude dffs from graph as they do not represent critical path"""
|
||||||
|
|
||||||
self.graph_inst_exclude.add(self.ctrl_dff_inst)
|
self.graph_inst_exclude.add(self.ctrl_dff_inst)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,14 @@ class hierarchical_decoder(design.design):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.AND_FORMAT = "DEC_AND_{0}"
|
self.AND_FORMAT = "DEC_AND_{0}"
|
||||||
|
|
||||||
self.pre2x4_inst = []
|
self.pre2x4_inst = []
|
||||||
self.pre3x8_inst = []
|
self.pre3x8_inst = []
|
||||||
self.pre4x16_inst = []
|
self.pre4x16_inst = []
|
||||||
|
|
||||||
b = factory.create(module_type="bitcell")
|
b = factory.create(module_type="bitcell")
|
||||||
self.cell_height = b.height
|
self.cell_height = b.height
|
||||||
|
|
||||||
self.num_outputs = num_outputs
|
self.num_outputs = num_outputs
|
||||||
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
|
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
|
||||||
(self.no_of_pre2x4, self.no_of_pre3x8, self.no_of_pre4x16)=self.determine_predecodes(self.num_inputs)
|
(self.no_of_pre2x4, self.no_of_pre3x8, self.no_of_pre4x16)=self.determine_predecodes(self.num_inputs)
|
||||||
|
|
@ -37,7 +37,7 @@ class hierarchical_decoder(design.design):
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.setup_netlist_constants()
|
self.setup_netlist_constants()
|
||||||
|
|
@ -49,33 +49,33 @@ class hierarchical_decoder(design.design):
|
||||||
self.setup_layout_constants()
|
self.setup_layout_constants()
|
||||||
self.place_pre_decoder()
|
self.place_pre_decoder()
|
||||||
self.place_row_decoder()
|
self.place_row_decoder()
|
||||||
|
|
||||||
self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space
|
self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space
|
||||||
|
|
||||||
self.route_inputs()
|
self.route_inputs()
|
||||||
self.route_outputs()
|
self.route_outputs()
|
||||||
self.route_decoder_bus()
|
self.route_decoder_bus()
|
||||||
self.route_vdd_gnd()
|
self.route_vdd_gnd()
|
||||||
|
|
||||||
self.offset_x_coordinates()
|
self.offset_x_coordinates()
|
||||||
|
|
||||||
self.width = self.and_inst[0].rx() + 0.5 * self.m1_width
|
self.width = self.and_inst[0].rx() + 0.5 * self.m1_width
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.and2 = factory.create(module_type="and2_dec",
|
self.and2 = factory.create(module_type="and2_dec",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.and2)
|
self.add_mod(self.and2)
|
||||||
|
|
||||||
self.and3 = factory.create(module_type="and3_dec",
|
self.and3 = factory.create(module_type="and3_dec",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.and3)
|
self.add_mod(self.and3)
|
||||||
# TBD
|
# TBD
|
||||||
# self.and4 = factory.create(module_type="and4_dec")
|
# self.and4 = factory.create(module_type="and4_dec")
|
||||||
# self.add_mod(self.and4)
|
# self.add_mod(self.and4)
|
||||||
|
|
||||||
self.add_decoders()
|
self.add_decoders()
|
||||||
|
|
||||||
def add_decoders(self):
|
def add_decoders(self):
|
||||||
|
|
@ -83,7 +83,7 @@ class hierarchical_decoder(design.design):
|
||||||
self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4",
|
self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.pre2_4)
|
self.add_mod(self.pre2_4)
|
||||||
|
|
||||||
self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8",
|
self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.pre3_8)
|
self.add_mod(self.pre3_8)
|
||||||
|
|
@ -91,11 +91,11 @@ class hierarchical_decoder(design.design):
|
||||||
self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16",
|
self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.pre4_16)
|
self.add_mod(self.pre4_16)
|
||||||
|
|
||||||
def determine_predecodes(self, num_inputs):
|
def determine_predecodes(self, num_inputs):
|
||||||
"""
|
"""
|
||||||
Determines the number of 2:4, 3:8 and 4:16 pre-decoders
|
Determines the number of 2:4, 3:8 and 4:16 pre-decoders
|
||||||
needed based on the number of inputs
|
needed based on the number of inputs
|
||||||
"""
|
"""
|
||||||
if (num_inputs == 2):
|
if (num_inputs == 2):
|
||||||
return (1, 0, 0)
|
return (1, 0, 0)
|
||||||
|
|
@ -151,7 +151,7 @@ class hierarchical_decoder(design.design):
|
||||||
lines.append(index)
|
lines.append(index)
|
||||||
index = index + 1
|
index = index + 1
|
||||||
self.predec_groups.append(lines)
|
self.predec_groups.append(lines)
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
""" Calculate the overall dimensions of the hierarchical decoder """
|
""" Calculate the overall dimensions of the hierarchical decoder """
|
||||||
|
|
||||||
|
|
@ -190,7 +190,7 @@ class hierarchical_decoder(design.design):
|
||||||
self.input_layer = layer_props.hierarchical_decoder.input_layer
|
self.input_layer = layer_props.hierarchical_decoder.input_layer
|
||||||
self.output_layer = layer_props.hierarchical_decoder.output_layer
|
self.output_layer = layer_props.hierarchical_decoder.output_layer
|
||||||
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
|
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
|
||||||
|
|
||||||
# Two extra pitches between modules on left and right
|
# Two extra pitches between modules on left and right
|
||||||
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch
|
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch
|
||||||
self.row_decoder_height = self.and2.height * self.num_outputs
|
self.row_decoder_height = self.and2.height * self.num_outputs
|
||||||
|
|
@ -215,7 +215,7 @@ class hierarchical_decoder(design.design):
|
||||||
offset=input_offset,
|
offset=input_offset,
|
||||||
names=input_bus_names,
|
names=input_bus_names,
|
||||||
length=self.predecoder_height)
|
length=self.predecoder_height)
|
||||||
|
|
||||||
self.route_input_to_predecodes()
|
self.route_input_to_predecodes()
|
||||||
|
|
||||||
def route_input_to_predecodes(self):
|
def route_input_to_predecodes(self):
|
||||||
|
|
@ -231,13 +231,13 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
decoder_offset = decoder_pin.center()
|
decoder_offset = decoder_pin.center()
|
||||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||||
|
|
||||||
self.route_input_bus(decoder_offset, input_offset)
|
self.route_input_bus(decoder_offset, input_offset)
|
||||||
|
|
||||||
for pre_num in range(self.no_of_pre3x8):
|
for pre_num in range(self.no_of_pre3x8):
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
|
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
|
||||||
|
|
||||||
input_pos = self.input_bus["addr_{}".format(index)].center()
|
input_pos = self.input_bus["addr_{}".format(index)].center()
|
||||||
|
|
||||||
in_name = "in_{}".format(i)
|
in_name = "in_{}".format(i)
|
||||||
|
|
@ -245,13 +245,13 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
decoder_offset = decoder_pin.center()
|
decoder_offset = decoder_pin.center()
|
||||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||||
|
|
||||||
self.route_input_bus(decoder_offset, input_offset)
|
self.route_input_bus(decoder_offset, input_offset)
|
||||||
|
|
||||||
for pre_num in range(self.no_of_pre4x16):
|
for pre_num in range(self.no_of_pre4x16):
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
index = pre_num * 4 + i + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2
|
index = pre_num * 4 + i + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2
|
||||||
|
|
||||||
input_pos = self.input_bus["addr_{}".format(index)].center()
|
input_pos = self.input_bus["addr_{}".format(index)].center()
|
||||||
|
|
||||||
in_name = "in_{}".format(i)
|
in_name = "in_{}".format(i)
|
||||||
|
|
@ -259,15 +259,15 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
decoder_offset = decoder_pin.center()
|
decoder_offset = decoder_pin.center()
|
||||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||||
|
|
||||||
self.route_input_bus(decoder_offset, input_offset)
|
self.route_input_bus(decoder_offset, input_offset)
|
||||||
|
|
||||||
def route_input_bus(self, input_offset, output_offset):
|
def route_input_bus(self, input_offset, output_offset):
|
||||||
"""
|
"""
|
||||||
Route a vertical M2 coordinate to another
|
Route a vertical M2 coordinate to another
|
||||||
vertical M2 coordinate to the predecode inputs
|
vertical M2 coordinate to the predecode inputs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||||
to_layer=self.input_layer,
|
to_layer=self.input_layer,
|
||||||
offset=input_offset)
|
offset=input_offset)
|
||||||
|
|
@ -276,10 +276,10 @@ class hierarchical_decoder(design.design):
|
||||||
offset=output_offset,
|
offset=output_offset,
|
||||||
directions=self.bus_directions)
|
directions=self.bus_directions)
|
||||||
self.add_path(self.input_layer, [input_offset, output_offset])
|
self.add_path(self.input_layer, [input_offset, output_offset])
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
""" Add the module pins """
|
""" Add the module pins """
|
||||||
|
|
||||||
for i in range(self.num_inputs):
|
for i in range(self.num_inputs):
|
||||||
self.add_pin("addr_{0}".format(i), "INPUT")
|
self.add_pin("addr_{0}".format(i), "INPUT")
|
||||||
|
|
||||||
|
|
@ -290,10 +290,10 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
def create_pre_decoder(self):
|
def create_pre_decoder(self):
|
||||||
""" Creates pre-decoder and places labels input address [A] """
|
""" Creates pre-decoder and places labels input address [A] """
|
||||||
|
|
||||||
for i in range(self.no_of_pre2x4):
|
for i in range(self.no_of_pre2x4):
|
||||||
self.create_pre2x4(i)
|
self.create_pre2x4(i)
|
||||||
|
|
||||||
for i in range(self.no_of_pre3x8):
|
for i in range(self.no_of_pre3x8):
|
||||||
self.create_pre3x8(i)
|
self.create_pre3x8(i)
|
||||||
|
|
||||||
|
|
@ -302,7 +302,7 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
def create_pre2x4(self, num):
|
def create_pre2x4(self, num):
|
||||||
""" Add a 2x4 predecoder to the left of the origin """
|
""" Add a 2x4 predecoder to the left of the origin """
|
||||||
|
|
||||||
if (self.num_inputs == 2):
|
if (self.num_inputs == 2):
|
||||||
index_off1 = index_off2 = 0
|
index_off1 = index_off2 = 0
|
||||||
else:
|
else:
|
||||||
|
|
@ -355,19 +355,19 @@ class hierarchical_decoder(design.design):
|
||||||
self.pre4x16_inst.append(self.add_inst(name="pre4x16_{0}".format(num),
|
self.pre4x16_inst.append(self.add_inst(name="pre4x16_{0}".format(num),
|
||||||
mod=self.pre4_16))
|
mod=self.pre4_16))
|
||||||
self.connect_inst(pins)
|
self.connect_inst(pins)
|
||||||
|
|
||||||
def place_pre_decoder(self):
|
def place_pre_decoder(self):
|
||||||
""" Creates pre-decoder and places labels input address [A] """
|
""" Creates pre-decoder and places labels input address [A] """
|
||||||
|
|
||||||
for i in range(self.no_of_pre2x4):
|
for i in range(self.no_of_pre2x4):
|
||||||
self.place_pre2x4(i)
|
self.place_pre2x4(i)
|
||||||
|
|
||||||
for i in range(self.no_of_pre3x8):
|
for i in range(self.no_of_pre3x8):
|
||||||
self.place_pre3x8(i)
|
self.place_pre3x8(i)
|
||||||
|
|
||||||
for i in range(self.no_of_pre4x16):
|
for i in range(self.no_of_pre4x16):
|
||||||
self.place_pre4x16(i)
|
self.place_pre4x16(i)
|
||||||
|
|
||||||
self.predecode_height = 0
|
self.predecode_height = 0
|
||||||
if self.no_of_pre2x4 > 0:
|
if self.no_of_pre2x4 > 0:
|
||||||
self.predecode_height = self.pre2x4_inst[-1].uy()
|
self.predecode_height = self.pre2x4_inst[-1].uy()
|
||||||
|
|
@ -378,10 +378,10 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
def place_pre2x4(self, num):
|
def place_pre2x4(self, num):
|
||||||
""" Place 2x4 predecoder to the left of the origin """
|
""" Place 2x4 predecoder to the left of the origin """
|
||||||
|
|
||||||
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
|
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
|
||||||
self.pre2x4_inst[num].place(base)
|
self.pre2x4_inst[num].place(base)
|
||||||
|
|
||||||
def place_pre3x8(self, num):
|
def place_pre3x8(self, num):
|
||||||
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||||
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \
|
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \
|
||||||
|
|
@ -396,7 +396,7 @@ class hierarchical_decoder(design.design):
|
||||||
+ num * (self.pre4_16.height + self.predecoder_spacing)
|
+ num * (self.pre4_16.height + self.predecoder_spacing)
|
||||||
offset = vector(-self.pre4_16.width, height)
|
offset = vector(-self.pre4_16.width, height)
|
||||||
self.pre4x16_inst[num].place(offset)
|
self.pre4x16_inst[num].place(offset)
|
||||||
|
|
||||||
def create_row_decoder(self):
|
def create_row_decoder(self):
|
||||||
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
||||||
and add the primary decoder output pins. """
|
and add the primary decoder output pins. """
|
||||||
|
|
@ -407,7 +407,7 @@ class hierarchical_decoder(design.design):
|
||||||
""" Add a column of AND gates for final decode """
|
""" Add a column of AND gates for final decode """
|
||||||
|
|
||||||
self.and_inst = []
|
self.and_inst = []
|
||||||
|
|
||||||
# Row Decoder AND GATE array for address inputs <5.
|
# Row Decoder AND GATE array for address inputs <5.
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
for i in range(len(self.predec_groups[0])):
|
for i in range(len(self.predec_groups[0])):
|
||||||
|
|
@ -435,7 +435,7 @@ class hierarchical_decoder(design.design):
|
||||||
name = self.AND_FORMAT.format(output)
|
name = self.AND_FORMAT.format(output)
|
||||||
self.and_inst.append(self.add_inst(name=name,
|
self.and_inst.append(self.add_inst(name=name,
|
||||||
mod=self.and3))
|
mod=self.and3))
|
||||||
|
|
||||||
pins = ["out_{0}".format(i),
|
pins = ["out_{0}".format(i),
|
||||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||||
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
||||||
|
|
@ -456,7 +456,7 @@ class hierarchical_decoder(design.design):
|
||||||
Add a column of AND gates for final decode.
|
Add a column of AND gates for final decode.
|
||||||
This may have more than one decoder per row to match the bitcell height.
|
This may have more than one decoder per row to match the bitcell height.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Row Decoder AND GATE array for address inputs <5.
|
# Row Decoder AND GATE array for address inputs <5.
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
self.place_and_array(and_mod=self.and2)
|
self.place_and_array(and_mod=self.and2)
|
||||||
|
|
@ -489,7 +489,7 @@ class hierarchical_decoder(design.design):
|
||||||
for row in range(self.num_outputs):
|
for row in range(self.num_outputs):
|
||||||
and_inst = self.and_inst[row]
|
and_inst = self.and_inst[row]
|
||||||
self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row))
|
self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row))
|
||||||
|
|
||||||
def route_decoder_bus(self):
|
def route_decoder_bus(self):
|
||||||
"""
|
"""
|
||||||
Creates vertical metal 2 bus to connect predecoder and decoder stages.
|
Creates vertical metal 2 bus to connect predecoder and decoder stages.
|
||||||
|
|
@ -522,7 +522,7 @@ class hierarchical_decoder(design.design):
|
||||||
x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch
|
x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch
|
||||||
y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height
|
y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height
|
||||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
||||||
|
|
||||||
# FIXME: convert to connect_bus
|
# FIXME: convert to connect_bus
|
||||||
for pre_num in range(self.no_of_pre3x8):
|
for pre_num in range(self.no_of_pre3x8):
|
||||||
for i in range(8):
|
for i in range(8):
|
||||||
|
|
@ -542,7 +542,7 @@ class hierarchical_decoder(design.design):
|
||||||
x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch
|
x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch
|
||||||
y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height
|
y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height
|
||||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
||||||
|
|
||||||
def route_bus_to_decoder(self):
|
def route_bus_to_decoder(self):
|
||||||
"""
|
"""
|
||||||
Use the self.predec_groups to determine the connections to the decoder AND gates.
|
Use the self.predec_groups to determine the connections to the decoder AND gates.
|
||||||
|
|
@ -555,7 +555,7 @@ class hierarchical_decoder(design.design):
|
||||||
and the 128th AND3 is connected to [3,7,15]
|
and the 128th AND3 is connected to [3,7,15]
|
||||||
"""
|
"""
|
||||||
output_index = 0
|
output_index = 0
|
||||||
|
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
for index_B in self.predec_groups[1]:
|
for index_B in self.predec_groups[1]:
|
||||||
for index_A in self.predec_groups[0]:
|
for index_A in self.predec_groups[0]:
|
||||||
|
|
@ -596,7 +596,7 @@ class hierarchical_decoder(design.design):
|
||||||
Add a pin for each row of vdd/gnd which are
|
Add a pin for each row of vdd/gnd which are
|
||||||
must-connects next level up.
|
must-connects next level up.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if layer_props.hierarchical_decoder.vertical_supply:
|
if layer_props.hierarchical_decoder.vertical_supply:
|
||||||
for n in ["vdd", "gnd"]:
|
for n in ["vdd", "gnd"]:
|
||||||
pins = self.and_inst[0].get_pins(n)
|
pins = self.and_inst[0].get_pins(n)
|
||||||
|
|
@ -636,7 +636,7 @@ class hierarchical_decoder(design.design):
|
||||||
for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst:
|
for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst:
|
||||||
for pin_name in ["vdd", "gnd"]:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
self.copy_layout_pin(pre, pin_name)
|
self.copy_layout_pin(pre, pin_name)
|
||||||
|
|
||||||
def route_predecode_bus_outputs(self, rail_name, pin, row):
|
def route_predecode_bus_outputs(self, rail_name, pin, row):
|
||||||
"""
|
"""
|
||||||
Connect the routing rail to the given metal1 pin
|
Connect the routing rail to the given metal1 pin
|
||||||
|
|
@ -646,17 +646,17 @@ class hierarchical_decoder(design.design):
|
||||||
pin_pos = pin.center()
|
pin_pos = pin.center()
|
||||||
rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y)
|
rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y)
|
||||||
self.add_path(self.input_layer, [rail_pos, pin_pos])
|
self.add_path(self.input_layer, [rail_pos, pin_pos])
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||||
to_layer=self.input_layer,
|
to_layer=self.input_layer,
|
||||||
offset=rail_pos,
|
offset=rail_pos,
|
||||||
directions=self.bus_directions)
|
directions=self.bus_directions)
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
to_layer=self.input_layer,
|
to_layer=self.input_layer,
|
||||||
offset=pin_pos,
|
offset=pin_pos,
|
||||||
directions=("H", "H"))
|
directions=("H", "H"))
|
||||||
|
|
||||||
def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset):
|
def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset):
|
||||||
"""
|
"""
|
||||||
Connect the routing rail to the given metal1 pin using a jog
|
Connect the routing rail to the given metal1 pin using a jog
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,10 @@ class hierarchical_predecode(design.design):
|
||||||
# If we are pitch matched to the bitcell, it's a predecoder
|
# If we are pitch matched to the bitcell, it's a predecoder
|
||||||
# otherwise it's a column decoder (out of pgates)
|
# otherwise it's a column decoder (out of pgates)
|
||||||
self.column_decoder = (height != b.height)
|
self.column_decoder = (height != b.height)
|
||||||
|
|
||||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
for k in range(self.number_of_inputs):
|
for k in range(self.number_of_inputs):
|
||||||
self.add_pin("in_{0}".format(k), "INPUT")
|
self.add_pin("in_{0}".format(k), "INPUT")
|
||||||
|
|
@ -48,7 +48,7 @@ class hierarchical_predecode(design.design):
|
||||||
|
|
||||||
debug.check(self.number_of_inputs <= 4,
|
debug.check(self.number_of_inputs <= 4,
|
||||||
"Invalid number of predecode inputs: {}".format(self.number_of_inputs))
|
"Invalid number of predecode inputs: {}".format(self.number_of_inputs))
|
||||||
|
|
||||||
if self.column_decoder:
|
if self.column_decoder:
|
||||||
and_type = "pand{}".format(self.number_of_inputs)
|
and_type = "pand{}".format(self.number_of_inputs)
|
||||||
inv_type = "pinv"
|
inv_type = "pinv"
|
||||||
|
|
@ -79,7 +79,7 @@ class hierarchical_predecode(design.design):
|
||||||
self.route()
|
self.route()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def setup_layout_constraints(self):
|
def setup_layout_constraints(self):
|
||||||
|
|
||||||
# Inputs to cells are on input layer
|
# Inputs to cells are on input layer
|
||||||
|
|
@ -92,7 +92,7 @@ class hierarchical_predecode(design.design):
|
||||||
self.input_layer = layer_props.hierarchical_predecode.input_layer
|
self.input_layer = layer_props.hierarchical_predecode.input_layer
|
||||||
self.output_layer = layer_props.hierarchical_predecode.output_layer
|
self.output_layer = layer_props.hierarchical_predecode.output_layer
|
||||||
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
|
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
|
||||||
|
|
||||||
self.height = self.number_of_outputs * self.and_mod.height
|
self.height = self.number_of_outputs * self.and_mod.height
|
||||||
|
|
||||||
# x offset for input inverters
|
# x offset for input inverters
|
||||||
|
|
@ -139,7 +139,7 @@ class hierarchical_predecode(design.design):
|
||||||
def place_input_inverters(self):
|
def place_input_inverters(self):
|
||||||
""" Place the input inverters to invert input signals for the decode stage. """
|
""" Place the input inverters to invert input signals for the decode stage. """
|
||||||
for inv_num in range(self.number_of_inputs):
|
for inv_num in range(self.number_of_inputs):
|
||||||
|
|
||||||
if (inv_num % 2 == 0):
|
if (inv_num % 2 == 0):
|
||||||
y_off = inv_num * (self.inv.height)
|
y_off = inv_num * (self.inv.height)
|
||||||
mirror = "R0"
|
mirror = "R0"
|
||||||
|
|
@ -149,7 +149,7 @@ class hierarchical_predecode(design.design):
|
||||||
offset = vector(self.x_off_inv_1, y_off)
|
offset = vector(self.x_off_inv_1, y_off)
|
||||||
self.inv_inst[inv_num].place(offset=offset,
|
self.inv_inst[inv_num].place(offset=offset,
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
def create_and_array(self, connections):
|
def create_and_array(self, connections):
|
||||||
""" Create the AND stage for the decodes """
|
""" Create the AND stage for the decodes """
|
||||||
self.and_inst = []
|
self.and_inst = []
|
||||||
|
|
@ -196,7 +196,7 @@ class hierarchical_predecode(design.design):
|
||||||
pin = top_and_gate.get_pin("D")
|
pin = top_and_gate.get_pin("D")
|
||||||
else:
|
else:
|
||||||
debug.error("Too many inputs for predecoder.", -1)
|
debug.error("Too many inputs for predecoder.", -1)
|
||||||
|
|
||||||
y_offset = pin.cy()
|
y_offset = pin.cy()
|
||||||
in_pin = "in_{}".format(num)
|
in_pin = "in_{}".format(num)
|
||||||
a_pin = "A_{}".format(num)
|
a_pin = "A_{}".format(num)
|
||||||
|
|
@ -222,7 +222,7 @@ class hierarchical_predecode(design.design):
|
||||||
offset=z_pin.ll(),
|
offset=z_pin.ll(),
|
||||||
height=z_pin.height(),
|
height=z_pin.height(),
|
||||||
width=z_pin.width())
|
width=z_pin.width())
|
||||||
|
|
||||||
def route_input_inverters(self):
|
def route_input_inverters(self):
|
||||||
"""
|
"""
|
||||||
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
|
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
|
||||||
|
|
@ -232,7 +232,7 @@ class hierarchical_predecode(design.design):
|
||||||
in_pin = "in_{}".format(inv_num)
|
in_pin = "in_{}".format(inv_num)
|
||||||
|
|
||||||
inv_out_pin = self.inv_inst[inv_num].get_pin("Z")
|
inv_out_pin = self.inv_inst[inv_num].get_pin("Z")
|
||||||
|
|
||||||
# add output so that it is just below the vdd or gnd rail
|
# add output so that it is just below the vdd or gnd rail
|
||||||
# since this is where the p/n devices are and there are no
|
# since this is where the p/n devices are and there are no
|
||||||
# pins in the and gates.
|
# pins in the and gates.
|
||||||
|
|
@ -241,7 +241,7 @@ class hierarchical_predecode(design.design):
|
||||||
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0)
|
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0)
|
||||||
rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset)
|
rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset)
|
||||||
self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=inv_out_pin.layer,
|
self.add_via_stack_center(from_layer=inv_out_pin.layer,
|
||||||
to_layer=self.output_layer,
|
to_layer=self.output_layer,
|
||||||
offset=inv_out_pos)
|
offset=inv_out_pos)
|
||||||
|
|
@ -249,7 +249,7 @@ class hierarchical_predecode(design.design):
|
||||||
to_layer=self.bus_layer,
|
to_layer=self.bus_layer,
|
||||||
offset=rail_pos,
|
offset=rail_pos,
|
||||||
directions=self.bus_directions)
|
directions=self.bus_directions)
|
||||||
|
|
||||||
# route input
|
# route input
|
||||||
pin = self.inv_inst[inv_num].get_pin("A")
|
pin = self.inv_inst[inv_num].get_pin("A")
|
||||||
inv_in_pos = pin.center()
|
inv_in_pos = pin.center()
|
||||||
|
|
@ -267,11 +267,11 @@ class hierarchical_predecode(design.design):
|
||||||
offset=in_pos,
|
offset=in_pos,
|
||||||
height=via.mod.second_layer_height,
|
height=via.mod.second_layer_height,
|
||||||
width=via.mod.second_layer_width)
|
width=via.mod.second_layer_width)
|
||||||
|
|
||||||
if layer_props.hierarchical_predecode.vertical_supply:
|
if layer_props.hierarchical_predecode.vertical_supply:
|
||||||
below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height / 2))
|
below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height / 2))
|
||||||
self.add_path(self.bus_layer, [rail_pos, below_rail], width=self.li_width + self.m1_enclose_mcon * 2)
|
self.add_path(self.bus_layer, [rail_pos, below_rail], width=self.li_width + self.m1_enclose_mcon * 2)
|
||||||
|
|
||||||
def route_and_to_rails(self):
|
def route_and_to_rails(self):
|
||||||
# This 2D array defines the connection mapping
|
# This 2D array defines the connection mapping
|
||||||
and_input_line_combination = self.get_and_input_line_combination()
|
and_input_line_combination = self.get_and_input_line_combination()
|
||||||
|
|
@ -302,7 +302,7 @@ class hierarchical_predecode(design.design):
|
||||||
direction = None
|
direction = None
|
||||||
else:
|
else:
|
||||||
direction = ("H", "H")
|
direction = ("H", "H")
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
to_layer=self.input_layer,
|
to_layer=self.input_layer,
|
||||||
offset=pin_pos,
|
offset=pin_pos,
|
||||||
|
|
@ -334,7 +334,7 @@ class hierarchical_predecode(design.design):
|
||||||
self.add_power_pin(name=n,
|
self.add_power_pin(name=n,
|
||||||
loc=pin.uc(),
|
loc=pin.uc(),
|
||||||
start_layer=pin.layer)
|
start_layer=pin.layer)
|
||||||
|
|
||||||
# In other techs, we are using standard cell decoder cells with horizontal power
|
# In other techs, we are using standard cell decoder cells with horizontal power
|
||||||
else:
|
else:
|
||||||
for num in range(0, self.number_of_outputs):
|
for num in range(0, self.number_of_outputs):
|
||||||
|
|
@ -354,6 +354,6 @@ class hierarchical_predecode(design.design):
|
||||||
self.add_power_pin(name=n,
|
self.add_power_pin(name=n,
|
||||||
loc=pin_pos,
|
loc=pin_pos,
|
||||||
start_layer=and_pin.layer)
|
start_layer=and_pin.layer)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_input_inverters()
|
self.create_input_inverters()
|
||||||
connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
|
connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
|
||||||
["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
|
["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
|
||||||
["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
|
["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
|
||||||
["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
|
["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
|
||||||
["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
|
["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
|
||||||
["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
|
["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
|
||||||
["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
|
["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
|
||||||
|
|
@ -41,7 +41,7 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
||||||
["Abar_0", "A_1", "Abar_2"],
|
["Abar_0", "A_1", "Abar_2"],
|
||||||
["A_0", "A_1", "Abar_2"],
|
["A_0", "A_1", "Abar_2"],
|
||||||
["Abar_0", "Abar_1", "A_2"],
|
["Abar_0", "Abar_1", "A_2"],
|
||||||
["A_0", "Abar_1", "A_2"],
|
["A_0", "Abar_1", "A_2"],
|
||||||
["Abar_0", "A_1", "A_2"],
|
["Abar_0", "A_1", "A_2"],
|
||||||
["A_0", "A_1", "A_2"]]
|
["A_0", "A_1", "A_2"]]
|
||||||
return combination
|
return combination
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,17 @@ class hierarchical_predecode4x16(hierarchical_predecode):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_input_inverters()
|
self.create_input_inverters()
|
||||||
connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"],
|
connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"],
|
||||||
["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"],
|
["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"],
|
||||||
["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"],
|
["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"],
|
||||||
["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"],
|
["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"],
|
||||||
["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"],
|
["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"],
|
||||||
["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"],
|
["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"],
|
||||||
["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"],
|
["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"],
|
||||||
["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"],
|
["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"],
|
||||||
["inbar_0", "inbar_1", "inbar_2", "in_3", "out_8", "vdd", "gnd"],
|
["inbar_0", "inbar_1", "inbar_2", "in_3", "out_8", "vdd", "gnd"],
|
||||||
["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"],
|
["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"],
|
||||||
["inbar_0", "in_1", "inbar_2", "in_3", "out_10", "vdd", "gnd"],
|
["inbar_0", "in_1", "inbar_2", "in_3", "out_10", "vdd", "gnd"],
|
||||||
["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"],
|
["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"],
|
||||||
["inbar_0", "inbar_1", "in_2", "in_3", "out_12", "vdd", "gnd"],
|
["inbar_0", "inbar_1", "in_2", "in_3", "out_12", "vdd", "gnd"],
|
||||||
["in_0", "inbar_1", "in_2", "in_3", "out_13", "vdd", "gnd"],
|
["in_0", "inbar_1", "in_2", "in_3", "out_13", "vdd", "gnd"],
|
||||||
["inbar_0", "in_1", "in_2", "in_3", "out_14", "vdd", "gnd"],
|
["inbar_0", "in_1", "in_2", "in_3", "out_14", "vdd", "gnd"],
|
||||||
|
|
@ -50,15 +50,15 @@ class hierarchical_predecode4x16(hierarchical_predecode):
|
||||||
["Abar_0", "A_1", "Abar_2", "Abar_3"],
|
["Abar_0", "A_1", "Abar_2", "Abar_3"],
|
||||||
["A_0", "A_1", "Abar_2", "Abar_3"],
|
["A_0", "A_1", "Abar_2", "Abar_3"],
|
||||||
["Abar_0", "Abar_1", "A_2" , "Abar_3"],
|
["Abar_0", "Abar_1", "A_2" , "Abar_3"],
|
||||||
["A_0", "Abar_1", "A_2" , "Abar_3"],
|
["A_0", "Abar_1", "A_2" , "Abar_3"],
|
||||||
["Abar_0", "A_1", "A_2" , "Abar_3"],
|
["Abar_0", "A_1", "A_2" , "Abar_3"],
|
||||||
["A_0", "A_1", "A_2" , "Abar_3"],
|
["A_0", "A_1", "A_2" , "Abar_3"],
|
||||||
["Abar_0", "Abar_1", "Abar_2", "A_3"],
|
["Abar_0", "Abar_1", "Abar_2", "A_3"],
|
||||||
["A_0", "Abar_1", "Abar_2", "A_3"],
|
["A_0", "Abar_1", "Abar_2", "A_3"],
|
||||||
["Abar_0", "A_1", "Abar_2", "A_3"],
|
["Abar_0", "A_1", "Abar_2", "A_3"],
|
||||||
["A_0", "A_1", "Abar_2", "A_3"],
|
["A_0", "A_1", "Abar_2", "A_3"],
|
||||||
["Abar_0", "Abar_1", "A_2", "A_3"],
|
["Abar_0", "Abar_1", "A_2", "A_3"],
|
||||||
["A_0", "Abar_1", "A_2", "A_3"],
|
["A_0", "Abar_1", "A_2", "A_3"],
|
||||||
["Abar_0", "A_1", "A_2", "A_3"],
|
["Abar_0", "A_1", "A_2", "A_3"],
|
||||||
["A_0", "A_1", "A_2", "A_3"]]
|
["A_0", "A_1", "A_2", "A_3"]]
|
||||||
return combination
|
return combination
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,9 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
self.rbl = rbl
|
self.rbl = rbl
|
||||||
self.left_rbl = left_rbl
|
self.left_rbl = left_rbl
|
||||||
self.right_rbl = right_rbl
|
self.right_rbl = right_rbl
|
||||||
|
|
||||||
debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.")
|
debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.")
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -42,7 +42,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
# We don't offset this because we need to align
|
# We don't offset this because we need to align
|
||||||
# the replica bitcell in the control logic
|
# the replica bitcell in the control logic
|
||||||
# self.offset_all_coordinates()
|
# self.offset_all_coordinates()
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
""" Create and connect the netlist """
|
""" Create and connect the netlist """
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
|
|
@ -56,7 +56,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
self.route()
|
self.route()
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
@ -78,7 +78,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
rows=self.rows + 1,
|
rows=self.rows + 1,
|
||||||
cols=self.cols)
|
cols=self.cols)
|
||||||
self.add_mod(self.wl_array)
|
self.add_mod(self.wl_array)
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
# Outputs from the wordline driver (by port)
|
# Outputs from the wordline driver (by port)
|
||||||
self.driver_wordline_outputs = []
|
self.driver_wordline_outputs = []
|
||||||
|
|
@ -87,18 +87,18 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
|
|
||||||
self.wordline_names = self.bitcell_array.wordline_names
|
self.wordline_names = self.bitcell_array.wordline_names
|
||||||
self.all_wordline_names = self.bitcell_array.all_wordline_names
|
self.all_wordline_names = self.bitcell_array.all_wordline_names
|
||||||
|
|
||||||
self.bitline_names = self.bitcell_array.bitline_names
|
self.bitline_names = self.bitcell_array.bitline_names
|
||||||
self.all_bitline_names = self.bitcell_array.all_bitline_names
|
self.all_bitline_names = self.bitcell_array.all_bitline_names
|
||||||
|
|
||||||
self.rbl_wordline_names = self.bitcell_array.rbl_wordline_names
|
self.rbl_wordline_names = self.bitcell_array.rbl_wordline_names
|
||||||
self.all_rbl_wordline_names = self.bitcell_array.all_rbl_wordline_names
|
self.all_rbl_wordline_names = self.bitcell_array.all_rbl_wordline_names
|
||||||
|
|
||||||
self.rbl_bitline_names = self.bitcell_array.rbl_bitline_names
|
self.rbl_bitline_names = self.bitcell_array.rbl_bitline_names
|
||||||
self.all_rbl_bitline_names = self.bitcell_array.all_rbl_bitline_names
|
self.all_rbl_bitline_names = self.bitcell_array.all_rbl_bitline_names
|
||||||
|
|
||||||
self.all_array_wordline_inputs = [x + "i" for x in self.bitcell_array.get_all_wordline_names()]
|
self.all_array_wordline_inputs = [x + "i" for x in self.bitcell_array.get_all_wordline_names()]
|
||||||
|
|
||||||
# Arrays are always:
|
# Arrays are always:
|
||||||
# bit lines (left to right)
|
# bit lines (left to right)
|
||||||
# word lines (bottom to top)
|
# word lines (bottom to top)
|
||||||
|
|
@ -133,10 +133,10 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
else:
|
else:
|
||||||
temp += self.get_wordline_names(port)[::-1]
|
temp += self.get_wordline_names(port)[::-1]
|
||||||
self.driver_wordline_outputs.append([x + "i" for x in temp])
|
self.driver_wordline_outputs.append([x + "i" for x in temp])
|
||||||
|
|
||||||
temp += self.driver_wordline_outputs[-1]
|
temp += self.driver_wordline_outputs[-1]
|
||||||
temp += ["vdd", "gnd"]
|
temp += ["vdd", "gnd"]
|
||||||
|
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
self.bitcell_array_inst = self.add_inst(name="array",
|
self.bitcell_array_inst = self.add_inst(name="array",
|
||||||
|
|
@ -180,7 +180,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
|
|
||||||
for x in self.get_inouts():
|
for x in self.get_inouts():
|
||||||
self.copy_layout_pin(self.bitcell_array_inst, x)
|
self.copy_layout_pin(self.bitcell_array_inst, x)
|
||||||
|
|
||||||
supply_insts = [*self.wl_insts, self.bitcell_array_inst]
|
supply_insts = [*self.wl_insts, self.bitcell_array_inst]
|
||||||
for pin_name in ["vdd", "gnd"]:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
for inst in supply_insts:
|
for inst in supply_insts:
|
||||||
|
|
@ -189,7 +189,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
self.add_power_pin(name=pin_name,
|
self.add_power_pin(name=pin_name,
|
||||||
loc=pin.center(),
|
loc=pin.center(),
|
||||||
start_layer=pin.layer)
|
start_layer=pin.layer)
|
||||||
|
|
||||||
def route(self):
|
def route(self):
|
||||||
|
|
||||||
# Route the global wordlines
|
# Route the global wordlines
|
||||||
|
|
@ -198,27 +198,27 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)
|
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)
|
||||||
else:
|
else:
|
||||||
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)[::-1]
|
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)[::-1]
|
||||||
|
|
||||||
wordline_pins = self.wl_array.get_inputs()
|
wordline_pins = self.wl_array.get_inputs()
|
||||||
|
|
||||||
for (wl_name, in_pin_name) in zip(wordline_names, wordline_pins):
|
for (wl_name, in_pin_name) in zip(wordline_names, wordline_pins):
|
||||||
# wl_pin = self.bitcell_array_inst.get_pin(wl_name)
|
# wl_pin = self.bitcell_array_inst.get_pin(wl_name)
|
||||||
in_pin = self.wl_insts[port].get_pin(in_pin_name)
|
in_pin = self.wl_insts[port].get_pin(in_pin_name)
|
||||||
|
|
||||||
y_offset = in_pin.cy()
|
y_offset = in_pin.cy()
|
||||||
if port == 0:
|
if port == 0:
|
||||||
y_offset -= 2 * self.m3_pitch
|
y_offset -= 2 * self.m3_pitch
|
||||||
else:
|
else:
|
||||||
y_offset += 2 * self.m3_pitch
|
y_offset += 2 * self.m3_pitch
|
||||||
|
|
||||||
self.add_layout_pin_segment_center(text=wl_name,
|
self.add_layout_pin_segment_center(text=wl_name,
|
||||||
layer="m3",
|
layer="m3",
|
||||||
start=vector(self.wl_insts[port].lx(), y_offset),
|
start=vector(self.wl_insts[port].lx(), y_offset),
|
||||||
end=vector(self.wl_insts[port].lx() + self.wl_array.width, y_offset))
|
end=vector(self.wl_insts[port].lx() + self.wl_array.width, y_offset))
|
||||||
|
|
||||||
mid = vector(in_pin.cx(), y_offset)
|
mid = vector(in_pin.cx(), y_offset)
|
||||||
self.add_path("m2", [in_pin.center(), mid])
|
self.add_path("m2", [in_pin.center(), mid])
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=in_pin.layer,
|
self.add_via_stack_center(from_layer=in_pin.layer,
|
||||||
to_layer="m2",
|
to_layer="m2",
|
||||||
offset=in_pin.center())
|
offset=in_pin.center())
|
||||||
|
|
@ -228,7 +228,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
# Route the buffers
|
# Route the buffers
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
driver_outputs = self.driver_wordline_outputs[port]
|
driver_outputs = self.driver_wordline_outputs[port]
|
||||||
|
|
||||||
for (driver_name, net_name) in zip(self.wl_insts[port].mod.get_outputs(), driver_outputs):
|
for (driver_name, net_name) in zip(self.wl_insts[port].mod.get_outputs(), driver_outputs):
|
||||||
array_name = net_name[:-1]
|
array_name = net_name[:-1]
|
||||||
out_pin = self.wl_insts[port].get_pin(driver_name)
|
out_pin = self.wl_insts[port].get_pin(driver_name)
|
||||||
|
|
@ -242,7 +242,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
mid_loc = vector(self.wl_insts[port].lx() - 1.5 * self.m3_pitch, out_loc.y)
|
mid_loc = vector(self.wl_insts[port].lx() - 1.5 * self.m3_pitch, out_loc.y)
|
||||||
in_loc = in_pin.rc()
|
in_loc = in_pin.rc()
|
||||||
self.add_path(out_pin.layer, [out_loc, mid_loc, in_loc])
|
self.add_path(out_pin.layer, [out_loc, mid_loc, in_loc])
|
||||||
|
|
||||||
def get_main_array_top(self):
|
def get_main_array_top(self):
|
||||||
return self.bitcell_array_inst.by() + self.bitcell_array.get_main_array_top()
|
return self.bitcell_array_inst.by() + self.bitcell_array.get_main_array_top()
|
||||||
|
|
||||||
|
|
@ -262,13 +262,13 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
# must add the offset of the instance
|
# must add the offset of the instance
|
||||||
offsets = [self.bitcell_array_inst.lx() + x for x in self.bitcell_array.get_column_offsets()]
|
offsets = [self.bitcell_array_inst.lx() + x for x in self.bitcell_array.get_column_offsets()]
|
||||||
return offsets
|
return offsets
|
||||||
|
|
||||||
def graph_exclude_bits(self, targ_row=None, targ_col=None):
|
def graph_exclude_bits(self, targ_row=None, targ_col=None):
|
||||||
"""
|
"""
|
||||||
Excludes bits in column from being added to graph except target
|
Excludes bits in column from being added to graph except target
|
||||||
"""
|
"""
|
||||||
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
|
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
|
||||||
|
|
||||||
def graph_exclude_replica_col_bits(self):
|
def graph_exclude_replica_col_bits(self):
|
||||||
"""
|
"""
|
||||||
Exclude all but replica in the local array.
|
Exclude all but replica in the local array.
|
||||||
|
|
@ -281,8 +281,8 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col)
|
return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col)
|
||||||
|
|
||||||
def clear_exclude_bits(self):
|
def clear_exclude_bits(self):
|
||||||
"""
|
"""
|
||||||
Clears the bit exclusions
|
Clears the bit exclusions
|
||||||
"""
|
"""
|
||||||
self.bitcell_array.clear_exclude_bits()
|
self.bitcell_array.clear_exclude_bits()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ from globals import OPTS
|
||||||
class multibank(design.design):
|
class multibank(design.design):
|
||||||
"""
|
"""
|
||||||
Dynamically generated a single bank including bitcell array,
|
Dynamically generated a single bank including bitcell array,
|
||||||
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
||||||
write driver and sense amplifiers.
|
write driver and sense amplifiers.
|
||||||
This module includes the tristate and bank select logic.
|
This module includes the tristate and bank select logic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, word_size, num_words, words_per_row, num_banks=1):
|
def __init__(self, name, word_size, num_words, words_per_row, num_banks=1):
|
||||||
|
|
@ -41,7 +41,7 @@ class multibank(design.design):
|
||||||
self.prefix="gated_"
|
self.prefix="gated_"
|
||||||
else:
|
else:
|
||||||
self.prefix=""
|
self.prefix=""
|
||||||
|
|
||||||
self.compute_sizes()
|
self.compute_sizes()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
|
|
@ -50,16 +50,16 @@ class multibank(design.design):
|
||||||
|
|
||||||
# FIXME: Move this to the add modules function
|
# FIXME: Move this to the add modules function
|
||||||
self.add_bank_select()
|
self.add_bank_select()
|
||||||
|
|
||||||
self.route_layout()
|
self.route_layout()
|
||||||
|
|
||||||
|
|
||||||
# Can remove the following, but it helps for debug!
|
# Can remove the following, but it helps for debug!
|
||||||
self.add_lvs_correspondence_points()
|
self.add_lvs_correspondence_points()
|
||||||
|
|
||||||
# Remember the bank center for further placement
|
# Remember the bank center for further placement
|
||||||
self.bank_center=self.offset_all_coordinates().scale(-1,-1)
|
self.bank_center=self.offset_all_coordinates().scale(-1,-1)
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
@ -91,23 +91,23 @@ class multibank(design.design):
|
||||||
#self.route_tri_gate_out()
|
#self.route_tri_gate_out()
|
||||||
self.route_sense_amp_out()
|
self.route_sense_amp_out()
|
||||||
self.route_wordline_driver()
|
self.route_wordline_driver()
|
||||||
self.route_write_driver()
|
self.route_write_driver()
|
||||||
self.route_row_decoder()
|
self.route_row_decoder()
|
||||||
self.route_column_address_lines()
|
self.route_column_address_lines()
|
||||||
self.route_control_lines()
|
self.route_control_lines()
|
||||||
self.add_control_pins()
|
self.add_control_pins()
|
||||||
if self.num_banks > 1:
|
if self.num_banks > 1:
|
||||||
self.route_bank_select()
|
self.route_bank_select()
|
||||||
|
|
||||||
self.route_supplies()
|
self.route_supplies()
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Add modules. The order should not matter! """
|
""" Add modules. The order should not matter! """
|
||||||
|
|
||||||
# Above the bitcell array
|
# Above the bitcell array
|
||||||
self.add_bitcell_array()
|
self.add_bitcell_array()
|
||||||
self.add_precharge_array()
|
self.add_precharge_array()
|
||||||
|
|
||||||
# Below the bitcell array
|
# Below the bitcell array
|
||||||
self.add_column_mux_array()
|
self.add_column_mux_array()
|
||||||
self.add_sense_amp_array()
|
self.add_sense_amp_array()
|
||||||
|
|
@ -120,7 +120,7 @@ class multibank(design.design):
|
||||||
self.add_wordline_driver()
|
self.add_wordline_driver()
|
||||||
self.add_column_decoder()
|
self.add_column_decoder()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def compute_sizes(self):
|
def compute_sizes(self):
|
||||||
""" Computes the required sizes to create the bank """
|
""" Computes the required sizes to create the bank """
|
||||||
|
|
@ -139,7 +139,7 @@ class multibank(design.design):
|
||||||
self.supply_rail_width = 4*self.m2_width
|
self.supply_rail_width = 4*self.m2_width
|
||||||
# FIXME: This spacing should be width dependent...
|
# FIXME: This spacing should be width dependent...
|
||||||
self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space
|
self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space
|
||||||
|
|
||||||
# Number of control lines in the bus
|
# Number of control lines in the bus
|
||||||
self.num_control_lines = 6
|
self.num_control_lines = 6
|
||||||
# The order of the control signals on the control bus:
|
# The order of the control signals on the control bus:
|
||||||
|
|
@ -153,7 +153,7 @@ class multibank(design.design):
|
||||||
if self.col_addr_size>0:
|
if self.col_addr_size>0:
|
||||||
self.num_col_addr_lines = 2**self.col_addr_size
|
self.num_col_addr_lines = 2**self.col_addr_size
|
||||||
else:
|
else:
|
||||||
self.num_col_addr_lines = 0
|
self.num_col_addr_lines = 0
|
||||||
|
|
||||||
# The width of this bus is needed to place other modules (e.g. decoder)
|
# The width of this bus is needed to place other modules (e.g. decoder)
|
||||||
# A width on each side too
|
# A width on each side too
|
||||||
|
|
@ -169,7 +169,7 @@ class multibank(design.design):
|
||||||
""" Add all the modules using the class loader """
|
""" Add all the modules using the class loader """
|
||||||
self.tri = self.mod_tri_gate()
|
self.tri = self.mod_tri_gate()
|
||||||
self.bitcell = self.mod_bitcell()
|
self.bitcell = self.mod_bitcell()
|
||||||
|
|
||||||
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
|
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
|
||||||
rows=self.num_rows)
|
rows=self.num_rows)
|
||||||
self.add_mod(self.bitcell_array)
|
self.add_mod(self.bitcell_array)
|
||||||
|
|
@ -178,12 +178,12 @@ class multibank(design.design):
|
||||||
self.add_mod(self.precharge_array)
|
self.add_mod(self.precharge_array)
|
||||||
|
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols,
|
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols,
|
||||||
word_size=self.word_size)
|
word_size=self.word_size)
|
||||||
self.add_mod(self.column_mux_array)
|
self.add_mod(self.column_mux_array)
|
||||||
|
|
||||||
|
|
||||||
self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size,
|
self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size,
|
||||||
words_per_row=self.words_per_row)
|
words_per_row=self.words_per_row)
|
||||||
self.add_mod(self.sense_amp_array)
|
self.add_mod(self.sense_amp_array)
|
||||||
|
|
||||||
|
|
@ -199,8 +199,8 @@ class multibank(design.design):
|
||||||
|
|
||||||
self.row_decoder = self.mod_decoder(rows=self.num_rows)
|
self.row_decoder = self.mod_decoder(rows=self.num_rows)
|
||||||
self.add_mod(self.row_decoder)
|
self.add_mod(self.row_decoder)
|
||||||
|
|
||||||
self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols,
|
self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols,
|
||||||
word_size=self.word_size)
|
word_size=self.word_size)
|
||||||
self.add_mod(self.tri_gate_array)
|
self.add_mod(self.tri_gate_array)
|
||||||
|
|
||||||
|
|
@ -213,12 +213,12 @@ class multibank(design.design):
|
||||||
if(self.num_banks > 1):
|
if(self.num_banks > 1):
|
||||||
self.bank_select = self.mod_bank_select()
|
self.bank_select = self.mod_bank_select()
|
||||||
self.add_mod(self.bank_select)
|
self.add_mod(self.bank_select)
|
||||||
|
|
||||||
|
|
||||||
def add_bitcell_array(self):
|
def add_bitcell_array(self):
|
||||||
""" Adding Bitcell Array """
|
""" Adding Bitcell Array """
|
||||||
|
|
||||||
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
||||||
mod=self.bitcell_array,
|
mod=self.bitcell_array,
|
||||||
offset=vector(0,0))
|
offset=vector(0,0))
|
||||||
temp = []
|
temp = []
|
||||||
|
|
@ -238,7 +238,7 @@ class multibank(design.design):
|
||||||
# The enclosure is for the well and the spacing is to the bitcell wells
|
# The enclosure is for the well and the spacing is to the bitcell wells
|
||||||
y_offset = self.bitcell_array.height + self.m2_gap
|
y_offset = self.bitcell_array.height + self.m2_gap
|
||||||
self.precharge_array_inst=self.add_inst(name="precharge_array",
|
self.precharge_array_inst=self.add_inst(name="precharge_array",
|
||||||
mod=self.precharge_array,
|
mod=self.precharge_array,
|
||||||
offset=vector(0,y_offset))
|
offset=vector(0,y_offset))
|
||||||
temp = []
|
temp = []
|
||||||
for i in range(self.num_cols):
|
for i in range(self.num_cols):
|
||||||
|
|
@ -255,7 +255,7 @@ class multibank(design.design):
|
||||||
self.column_mux_height = 0
|
self.column_mux_height = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
y_offset = self.column_mux_height
|
y_offset = self.column_mux_height
|
||||||
self.col_mux_array_inst=self.add_inst(name="column_mux_array",
|
self.col_mux_array_inst=self.add_inst(name="column_mux_array",
|
||||||
mod=self.column_mux_array,
|
mod=self.column_mux_array,
|
||||||
offset=vector(0,y_offset).scale(-1,-1))
|
offset=vector(0,y_offset).scale(-1,-1))
|
||||||
|
|
@ -287,7 +287,7 @@ class multibank(design.design):
|
||||||
else:
|
else:
|
||||||
temp.append("bl_out_{0}".format(i))
|
temp.append("bl_out_{0}".format(i))
|
||||||
temp.append("br_out_{0}".format(i))
|
temp.append("br_out_{0}".format(i))
|
||||||
|
|
||||||
temp.extend([self.prefix+"s_en", "vdd", "gnd"])
|
temp.extend([self.prefix+"s_en", "vdd", "gnd"])
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
|
|
@ -295,16 +295,16 @@ class multibank(design.design):
|
||||||
""" Adding Write Driver """
|
""" Adding Write Driver """
|
||||||
|
|
||||||
y_offset = self.sense_amp_array.height + self.column_mux_height \
|
y_offset = self.sense_amp_array.height + self.column_mux_height \
|
||||||
+ self.m2_gap + self.write_driver_array.height
|
+ self.m2_gap + self.write_driver_array.height
|
||||||
self.write_driver_array_inst=self.add_inst(name="write_driver_array",
|
self.write_driver_array_inst=self.add_inst(name="write_driver_array",
|
||||||
mod=self.write_driver_array,
|
mod=self.write_driver_array,
|
||||||
offset=vector(0,y_offset).scale(-1,-1))
|
offset=vector(0,y_offset).scale(-1,-1))
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
temp.append("bank_din_{0}".format(i))
|
temp.append("bank_din_{0}".format(i))
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
if (self.words_per_row == 1):
|
if (self.words_per_row == 1):
|
||||||
temp.append("bl_{0}".format(i))
|
temp.append("bl_{0}".format(i))
|
||||||
temp.append("br_{0}".format(i))
|
temp.append("br_{0}".format(i))
|
||||||
else:
|
else:
|
||||||
|
|
@ -317,10 +317,10 @@ class multibank(design.design):
|
||||||
""" data tri gate to drive the data bus """
|
""" data tri gate to drive the data bus """
|
||||||
y_offset = self.sense_amp_array.height+self.column_mux_height \
|
y_offset = self.sense_amp_array.height+self.column_mux_height \
|
||||||
+ self.m2_gap + self.tri_gate_array.height
|
+ self.m2_gap + self.tri_gate_array.height
|
||||||
self.tri_gate_array_inst=self.add_inst(name="tri_gate_array",
|
self.tri_gate_array_inst=self.add_inst(name="tri_gate_array",
|
||||||
mod=self.tri_gate_array,
|
mod=self.tri_gate_array,
|
||||||
offset=vector(0,y_offset).scale(-1,-1))
|
offset=vector(0,y_offset).scale(-1,-1))
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
temp.append("sa_out_{0}".format(i))
|
temp.append("sa_out_{0}".format(i))
|
||||||
|
|
@ -332,16 +332,16 @@ class multibank(design.design):
|
||||||
def add_row_decoder(self):
|
def add_row_decoder(self):
|
||||||
""" Add the hierarchical row decoder """
|
""" Add the hierarchical row decoder """
|
||||||
|
|
||||||
|
|
||||||
# The address and control bus will be in between decoder and the main memory array
|
# The address and control bus will be in between decoder and the main memory array
|
||||||
# This bus will route address bits to the decoder input and column mux inputs.
|
# This bus will route address bits to the decoder input and column mux inputs.
|
||||||
# The wires are actually routed after we placed the stuff on both sides.
|
# The wires are actually routed after we placed the stuff on both sides.
|
||||||
# The predecoder is below the x-axis and the main decoder is above the x-axis
|
# The predecoder is below the x-axis and the main decoder is above the x-axis
|
||||||
# The address flop and decoder are aligned in the x coord.
|
# The address flop and decoder are aligned in the x coord.
|
||||||
|
|
||||||
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||||
self.row_decoder_inst=self.add_inst(name="row_decoder",
|
self.row_decoder_inst=self.add_inst(name="row_decoder",
|
||||||
mod=self.row_decoder,
|
mod=self.row_decoder,
|
||||||
offset=vector(x_offset,0))
|
offset=vector(x_offset,0))
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
|
|
@ -357,8 +357,8 @@ class multibank(design.design):
|
||||||
|
|
||||||
# The wordline driver is placed to the right of the main decoder width.
|
# The wordline driver is placed to the right of the main decoder width.
|
||||||
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
|
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
|
||||||
self.wordline_driver_inst=self.add_inst(name="wordline_driver",
|
self.wordline_driver_inst=self.add_inst(name="wordline_driver",
|
||||||
mod=self.wordline_driver,
|
mod=self.wordline_driver,
|
||||||
offset=vector(x_offset,0))
|
offset=vector(x_offset,0))
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
|
|
@ -371,16 +371,16 @@ class multibank(design.design):
|
||||||
temp.append("gnd")
|
temp.append("gnd")
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
|
|
||||||
def add_column_decoder_module(self):
|
def add_column_decoder_module(self):
|
||||||
"""
|
"""
|
||||||
Create a 2:4 or 3:8 column address decoder.
|
Create a 2:4 or 3:8 column address decoder.
|
||||||
"""
|
"""
|
||||||
# Place the col decoder right aligned with row decoder
|
# Place the col decoder right aligned with row decoder
|
||||||
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width)
|
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width)
|
||||||
y_off = -(self.col_decoder.height + 2*drc("well_to_well"))
|
y_off = -(self.col_decoder.height + 2*drc("well_to_well"))
|
||||||
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
|
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
|
||||||
mod=self.col_decoder,
|
mod=self.col_decoder,
|
||||||
offset=vector(x_off,y_off))
|
offset=vector(x_off,y_off))
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
|
|
@ -390,9 +390,9 @@ class multibank(design.design):
|
||||||
temp.append("sel_{0}".format(j))
|
temp.append("sel_{0}".format(j))
|
||||||
temp.extend(["vdd", "gnd"])
|
temp.extend(["vdd", "gnd"])
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
def add_column_decoder(self):
|
def add_column_decoder(self):
|
||||||
"""
|
"""
|
||||||
Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2,
|
Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2,
|
||||||
2:4 decoder, or 3:8 decoder.
|
2:4 decoder, or 3:8 decoder.
|
||||||
"""
|
"""
|
||||||
|
|
@ -408,7 +408,7 @@ class multibank(design.design):
|
||||||
else:
|
else:
|
||||||
# No error checking before?
|
# No error checking before?
|
||||||
debug.error("Invalid column decoder?",-1)
|
debug.error("Invalid column decoder?",-1)
|
||||||
|
|
||||||
self.add_column_decoder_module()
|
self.add_column_decoder_module()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -417,7 +417,7 @@ class multibank(design.design):
|
||||||
|
|
||||||
if not self.num_banks > 1:
|
if not self.num_banks > 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by())
|
y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by())
|
||||||
|
|
@ -441,7 +441,7 @@ class multibank(design.design):
|
||||||
for inst in self.insts:
|
for inst in self.insts:
|
||||||
self.copy_power_pins(inst,"vdd")
|
self.copy_power_pins(inst,"vdd")
|
||||||
self.copy_power_pins(inst,"gnd")
|
self.copy_power_pins(inst,"gnd")
|
||||||
|
|
||||||
def route_bank_select(self):
|
def route_bank_select(self):
|
||||||
""" Route the bank select logic. """
|
""" Route the bank select logic. """
|
||||||
for input_name in self.input_control_signals+["bank_sel"]:
|
for input_name in self.input_control_signals+["bank_sel"]:
|
||||||
|
|
@ -461,8 +461,8 @@ class multibank(design.design):
|
||||||
self.add_via_center(layers=self.m2_stack,
|
self.add_via_center(layers=self.m2_stack,
|
||||||
offset=out_pos,
|
offset=out_pos,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
|
|
||||||
def setup_layout_constraints(self):
|
def setup_layout_constraints(self):
|
||||||
""" After the modules are instantiated, find the dimensions for the
|
""" After the modules are instantiated, find the dimensions for the
|
||||||
control bus, power ring, etc. """
|
control bus, power ring, etc. """
|
||||||
|
|
@ -472,36 +472,36 @@ class multibank(design.design):
|
||||||
#driver.
|
#driver.
|
||||||
# Leave room for the output below the tri gate.
|
# Leave room for the output below the tri gate.
|
||||||
#tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch
|
#tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch
|
||||||
write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch
|
write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch
|
||||||
row_decoder_min_y_offset = self.row_decoder_inst.by()
|
row_decoder_min_y_offset = self.row_decoder_inst.by()
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
col_decoder_min_y_offset = self.col_decoder_inst.by()
|
col_decoder_min_y_offset = self.col_decoder_inst.by()
|
||||||
else:
|
else:
|
||||||
col_decoder_min_y_offset = row_decoder_min_y_offset
|
col_decoder_min_y_offset = row_decoder_min_y_offset
|
||||||
|
|
||||||
if self.num_banks>1:
|
if self.num_banks>1:
|
||||||
# The control gating logic is below the decoder
|
# The control gating logic is below the decoder
|
||||||
# Min of the control gating logic and tri gate.
|
# Min of the control gating logic and tri gate.
|
||||||
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset)
|
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset)
|
||||||
else:
|
else:
|
||||||
# Just the min of the decoder logic logic and tri gate.
|
# Just the min of the decoder logic logic and tri gate.
|
||||||
self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset)
|
self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset)
|
||||||
|
|
||||||
# The max point is always the top of the precharge bitlines
|
# The max point is always the top of the precharge bitlines
|
||||||
# Add a vdd and gnd power rail above the array
|
# Add a vdd and gnd power rail above the array
|
||||||
self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width
|
self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width
|
||||||
self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width
|
self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width
|
||||||
self.min_x_offset = self.row_decoder_inst.lx()
|
self.min_x_offset = self.row_decoder_inst.lx()
|
||||||
|
|
||||||
# # Create the core bbox for the power rings
|
# # Create the core bbox for the power rings
|
||||||
ur = vector(self.max_x_offset, self.max_y_offset)
|
ur = vector(self.max_x_offset, self.max_y_offset)
|
||||||
ll = vector(self.min_x_offset, self.min_y_offset)
|
ll = vector(self.min_x_offset, self.min_y_offset)
|
||||||
self.core_bbox = [ll, ur]
|
self.core_bbox = [ll, ur]
|
||||||
|
|
||||||
self.height = ur.y - ll.y
|
self.height = ur.y - ll.y
|
||||||
self.width = ur.x - ll.x
|
self.width = ur.x - ll.x
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def route_central_bus(self):
|
def route_central_bus(self):
|
||||||
""" Create the address, supply, and control signal central bus lines. """
|
""" Create the address, supply, and control signal central bus lines. """
|
||||||
|
|
@ -509,7 +509,7 @@ class multibank(design.design):
|
||||||
# Overall central bus width. It includes all the column mux lines,
|
# Overall central bus width. It includes all the column mux lines,
|
||||||
# and control lines.
|
# and control lines.
|
||||||
# The bank is at (0,0), so this is to the left of the y-axis.
|
# The bank is at (0,0), so this is to the left of the y-axis.
|
||||||
# 2 pitches on the right for vias/jogs to access the inputs
|
# 2 pitches on the right for vias/jogs to access the inputs
|
||||||
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0)
|
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0)
|
||||||
control_bus_length = self.bitcell_array_inst.uy()
|
control_bus_length = self.bitcell_array_inst.uy()
|
||||||
self.bus_xoffset = self.create_vertical_bus(layer="m2",
|
self.bus_xoffset = self.create_vertical_bus(layer="m2",
|
||||||
|
|
@ -542,7 +542,7 @@ class multibank(design.design):
|
||||||
# Only do this if we have a column mux!
|
# Only do this if we have a column mux!
|
||||||
if self.col_addr_size==0:
|
if self.col_addr_size==0:
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(self.num_cols):
|
for i in range(self.num_cols):
|
||||||
col_mux_bl = self.col_mux_array_inst.get_pin("bl_{}".format(i)).uc()
|
col_mux_bl = self.col_mux_array_inst.get_pin("bl_{}".format(i)).uc()
|
||||||
col_mux_br = self.col_mux_array_inst.get_pin("br_{}".format(i)).uc()
|
col_mux_br = self.col_mux_array_inst.get_pin("br_{}".format(i)).uc()
|
||||||
|
|
@ -554,7 +554,7 @@ class multibank(design.design):
|
||||||
vector(bitcell_bl.x,yoffset), bitcell_bl])
|
vector(bitcell_bl.x,yoffset), bitcell_bl])
|
||||||
self.add_path("m2",[col_mux_br, vector(col_mux_br.x,yoffset),
|
self.add_path("m2",[col_mux_br, vector(col_mux_br.x,yoffset),
|
||||||
vector(bitcell_br.x,yoffset), bitcell_br])
|
vector(bitcell_br.x,yoffset), bitcell_br])
|
||||||
|
|
||||||
def route_sense_amp_to_col_mux_or_bitcell_array(self):
|
def route_sense_amp_to_col_mux_or_bitcell_array(self):
|
||||||
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
|
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
|
||||||
|
|
||||||
|
|
@ -570,19 +570,19 @@ class multibank(design.design):
|
||||||
# Sense amp is directly connected to the bitcell array
|
# Sense amp is directly connected to the bitcell array
|
||||||
connect_bl = self.bitcell_array_inst.get_pin("bl_{}".format(i)).bc()
|
connect_bl = self.bitcell_array_inst.get_pin("bl_{}".format(i)).bc()
|
||||||
connect_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc()
|
connect_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc()
|
||||||
|
|
||||||
|
|
||||||
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
|
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
|
||||||
self.add_path("m2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
|
self.add_path("m2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
|
||||||
vector(connect_bl.x,yoffset), connect_bl])
|
vector(connect_bl.x,yoffset), connect_bl])
|
||||||
self.add_path("m2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
|
self.add_path("m2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
|
||||||
vector(connect_br.x,yoffset), connect_br])
|
vector(connect_br.x,yoffset), connect_br])
|
||||||
|
|
||||||
def route_sense_amp_to_trigate(self):
|
def route_sense_amp_to_trigate(self):
|
||||||
""" Routing of sense amp output to tri_gate input """
|
""" Routing of sense amp output to tri_gate input """
|
||||||
|
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
# Connection of data_out of sense amp to data_in
|
# Connection of data_out of sense amp to data_in
|
||||||
tri_gate_in = self.tri_gate_array_inst.get_pin("in_{}".format(i)).lc()
|
tri_gate_in = self.tri_gate_array_inst.get_pin("in_{}".format(i)).lc()
|
||||||
sa_data_out = self.sense_amp_array_inst.get_pin("data_{}".format(i)).bc()
|
sa_data_out = self.sense_amp_array_inst.get_pin("data_{}".format(i)).bc()
|
||||||
|
|
||||||
|
|
@ -597,17 +597,17 @@ class multibank(design.design):
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(i))
|
data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(i))
|
||||||
self.add_layout_pin_rect_center(text="dout_{}".format(i),
|
self.add_layout_pin_rect_center(text="dout_{}".format(i),
|
||||||
layer=data_pin.layer,
|
layer=data_pin.layer,
|
||||||
offset=data_pin.center(),
|
offset=data_pin.center(),
|
||||||
height=data_pin.height(),
|
height=data_pin.height(),
|
||||||
width=data_pin.width()),
|
width=data_pin.width()),
|
||||||
|
|
||||||
def route_tri_gate_out(self):
|
def route_tri_gate_out(self):
|
||||||
""" Metal 3 routing of tri_gate output data """
|
""" Metal 3 routing of tri_gate output data """
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
data_pin = self.tri_gate_array_inst.get_pin("out_{}".format(i))
|
data_pin = self.tri_gate_array_inst.get_pin("out_{}".format(i))
|
||||||
self.add_layout_pin_rect_center(text="dout_{}".format(i),
|
self.add_layout_pin_rect_center(text="dout_{}".format(i),
|
||||||
layer=data_pin.layer,
|
layer=data_pin.layer,
|
||||||
offset=data_pin.center(),
|
offset=data_pin.center(),
|
||||||
height=data_pin.height(),
|
height=data_pin.height(),
|
||||||
width=data_pin.width()),
|
width=data_pin.width()),
|
||||||
|
|
@ -622,21 +622,21 @@ class multibank(design.design):
|
||||||
decoder_name = "a_{}".format(i)
|
decoder_name = "a_{}".format(i)
|
||||||
addr_name = "a_{}".format(addr_idx)
|
addr_name = "a_{}".format(addr_idx)
|
||||||
self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
|
self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
|
||||||
|
|
||||||
|
|
||||||
def route_write_driver(self):
|
def route_write_driver(self):
|
||||||
""" Connecting write driver """
|
""" Connecting write driver """
|
||||||
|
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
data_name = "data_{}".format(i)
|
data_name = "data_{}".format(i)
|
||||||
din_name = "bank_din_{}".format(i)
|
din_name = "bank_din_{}".format(i)
|
||||||
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
|
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def route_wordline_driver(self):
|
def route_wordline_driver(self):
|
||||||
""" Connecting Wordline driver output to Bitcell WL connection """
|
""" Connecting Wordline driver output to Bitcell WL connection """
|
||||||
|
|
||||||
# we don't care about bends after connecting to the input pin, so let the path code decide.
|
# we don't care about bends after connecting to the input pin, so let the path code decide.
|
||||||
for i in range(self.num_rows):
|
for i in range(self.num_rows):
|
||||||
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
|
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
|
||||||
|
|
@ -653,23 +653,23 @@ class multibank(design.design):
|
||||||
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
|
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
|
||||||
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def route_column_address_lines(self):
|
def route_column_address_lines(self):
|
||||||
""" Connecting the select lines of column mux to the address bus """
|
""" Connecting the select lines of column mux to the address bus """
|
||||||
if not self.col_addr_size>0:
|
if not self.col_addr_size>0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if self.col_addr_size == 1:
|
if self.col_addr_size == 1:
|
||||||
|
|
||||||
# Connect to sel[0] and sel[1]
|
# Connect to sel[0] and sel[1]
|
||||||
decode_names = ["Zb", "Z"]
|
decode_names = ["Zb", "Z"]
|
||||||
|
|
||||||
# The Address LSB
|
# The Address LSB
|
||||||
self.copy_layout_pin(self.col_decoder_inst, "A", "a[0]")
|
self.copy_layout_pin(self.col_decoder_inst, "A", "a[0]")
|
||||||
|
|
||||||
elif self.col_addr_size > 1:
|
elif self.col_addr_size > 1:
|
||||||
decode_names = []
|
decode_names = []
|
||||||
for i in range(self.num_col_addr_lines):
|
for i in range(self.num_col_addr_lines):
|
||||||
|
|
@ -679,7 +679,7 @@ class multibank(design.design):
|
||||||
decoder_name = "in_{}".format(i)
|
decoder_name = "in_{}".format(i)
|
||||||
addr_name = "a_{}".format(i)
|
addr_name = "a_{}".format(i)
|
||||||
self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name)
|
self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name)
|
||||||
|
|
||||||
|
|
||||||
# This will do a quick "river route" on two layers.
|
# This will do a quick "river route" on two layers.
|
||||||
# When above the top select line it will offset "inward" again to prevent conflicts.
|
# When above the top select line it will offset "inward" again to prevent conflicts.
|
||||||
|
|
@ -688,7 +688,7 @@ class multibank(design.design):
|
||||||
for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
|
for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
|
||||||
mux_name = "sel_{}".format(i)
|
mux_name = "sel_{}".format(i)
|
||||||
mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc()
|
mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc()
|
||||||
|
|
||||||
decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center()
|
decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center()
|
||||||
|
|
||||||
# To get to the edge of the decoder and one track out
|
# To get to the edge of the decoder and one track out
|
||||||
|
|
@ -700,13 +700,13 @@ class multibank(design.design):
|
||||||
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
|
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
|
||||||
#self.add_wire(self.m1_stack,[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
|
#self.add_wire(self.m1_stack,[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
|
||||||
self.add_path("m1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
|
self.add_path("m1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_lvs_correspondence_points(self):
|
def add_lvs_correspondence_points(self):
|
||||||
""" This adds some points for easier debugging if LVS goes wrong.
|
""" This adds some points for easier debugging if LVS goes wrong.
|
||||||
These should probably be turned off by default though, since extraction
|
These should probably be turned off by default though, since extraction
|
||||||
will show these as ports in the extracted netlist.
|
will show these as ports in the extracted netlist.
|
||||||
"""
|
"""
|
||||||
|
|
@ -715,9 +715,9 @@ class multibank(design.design):
|
||||||
wl_name = "wl_{}".format(i)
|
wl_name = "wl_{}".format(i)
|
||||||
wl_pin = self.bitcell_array_inst.get_pin(wl_name)
|
wl_pin = self.bitcell_array_inst.get_pin(wl_name)
|
||||||
self.add_label(text=wl_name,
|
self.add_label(text=wl_name,
|
||||||
layer="m1",
|
layer="m1",
|
||||||
offset=wl_pin.center())
|
offset=wl_pin.center())
|
||||||
|
|
||||||
# Add the bitline names
|
# Add the bitline names
|
||||||
for i in range(self.num_cols):
|
for i in range(self.num_cols):
|
||||||
bl_name = "bl_{}".format(i)
|
bl_name = "bl_{}".format(i)
|
||||||
|
|
@ -725,35 +725,35 @@ class multibank(design.design):
|
||||||
bl_pin = self.bitcell_array_inst.get_pin(bl_name)
|
bl_pin = self.bitcell_array_inst.get_pin(bl_name)
|
||||||
br_pin = self.bitcell_array_inst.get_pin(br_name)
|
br_pin = self.bitcell_array_inst.get_pin(br_name)
|
||||||
self.add_label(text=bl_name,
|
self.add_label(text=bl_name,
|
||||||
layer="m2",
|
layer="m2",
|
||||||
offset=bl_pin.center())
|
offset=bl_pin.center())
|
||||||
self.add_label(text=br_name,
|
self.add_label(text=br_name,
|
||||||
layer="m2",
|
layer="m2",
|
||||||
offset=br_pin.center())
|
offset=br_pin.center())
|
||||||
|
|
||||||
# # Add the data output names to the sense amp output
|
# # Add the data output names to the sense amp output
|
||||||
# for i in range(self.word_size):
|
# for i in range(self.word_size):
|
||||||
# data_name = "data_{}".format(i)
|
# data_name = "data_{}".format(i)
|
||||||
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
|
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
|
||||||
# self.add_label(text="sa_out_{}".format(i),
|
# self.add_label(text="sa_out_{}".format(i),
|
||||||
# layer="m2",
|
# layer="m2",
|
||||||
# offset=data_pin.center())
|
# offset=data_pin.center())
|
||||||
|
|
||||||
# Add labels on the decoder
|
# Add labels on the decoder
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
data_name = "dec_out_{}".format(i)
|
data_name = "dec_out_{}".format(i)
|
||||||
pin_name = "in_{}".format(i)
|
pin_name = "in_{}".format(i)
|
||||||
data_pin = self.wordline_driver_inst.get_pin(pin_name)
|
data_pin = self.wordline_driver_inst.get_pin(pin_name)
|
||||||
self.add_label(text=data_name,
|
self.add_label(text=data_name,
|
||||||
layer="m1",
|
layer="m1",
|
||||||
offset=data_pin.center())
|
offset=data_pin.center())
|
||||||
|
|
||||||
|
|
||||||
def route_control_lines(self):
|
def route_control_lines(self):
|
||||||
""" Route the control lines of the entire bank """
|
""" Route the control lines of the entire bank """
|
||||||
|
|
||||||
# Make a list of tuples that we will connect.
|
# Make a list of tuples that we will connect.
|
||||||
# From control signal to the module pin
|
# From control signal to the module pin
|
||||||
# Connection from the central bus to the main control block crosses
|
# Connection from the central bus to the main control block crosses
|
||||||
# pre-decoder and this connection is in metal3
|
# pre-decoder and this connection is in metal3
|
||||||
connection = []
|
connection = []
|
||||||
|
|
@ -762,7 +762,7 @@ class multibank(design.design):
|
||||||
connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc()))
|
connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc()))
|
||||||
connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
|
connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
|
||||||
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
|
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
|
||||||
|
|
||||||
for (control_signal, pin_pos) in connection:
|
for (control_signal, pin_pos) in connection:
|
||||||
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
|
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
|
||||||
self.add_path("m1", [control_pos, pin_pos])
|
self.add_path("m1", [control_pos, pin_pos])
|
||||||
|
|
@ -781,7 +781,7 @@ class multibank(design.design):
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=control_via_pos,
|
offset=control_via_pos,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
def add_control_pins(self):
|
def add_control_pins(self):
|
||||||
""" Add the control signal input pins """
|
""" Add the control signal input pins """
|
||||||
|
|
||||||
|
|
@ -791,15 +791,15 @@ class multibank(design.design):
|
||||||
if self.num_banks > 1:
|
if self.num_banks > 1:
|
||||||
# it's not an input pin if we have multiple banks
|
# it's not an input pin if we have multiple banks
|
||||||
self.add_label_pin(text=ctrl,
|
self.add_label_pin(text=ctrl,
|
||||||
layer="m2",
|
layer="m2",
|
||||||
offset=vector(x_offset, self.min_y_offset),
|
offset=vector(x_offset, self.min_y_offset),
|
||||||
width=self.m2_width,
|
width=self.m2_width,
|
||||||
height=self.max_y_offset-self.min_y_offset)
|
height=self.max_y_offset-self.min_y_offset)
|
||||||
else:
|
else:
|
||||||
self.add_layout_pin(text=ctrl,
|
self.add_layout_pin(text=ctrl,
|
||||||
layer="m2",
|
layer="m2",
|
||||||
offset=vector(x_offset, self.min_y_offset),
|
offset=vector(x_offset, self.min_y_offset),
|
||||||
width=self.m2_width,
|
width=self.m2_width,
|
||||||
height=self.max_y_offset-self.min_y_offset)
|
height=self.max_y_offset-self.min_y_offset)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -815,8 +815,8 @@ class multibank(design.design):
|
||||||
self.add_via(layers=self.m2_stack,
|
self.add_via(layers=self.m2_stack,
|
||||||
offset=in_pin + self.m2m3_via_offset,
|
offset=in_pin + self.m2m3_via_offset,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
|
|
||||||
def connect_rail_from_left(self,inst, pin, rail):
|
def connect_rail_from_left(self,inst, pin, rail):
|
||||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
||||||
in_pin = inst.get_pin(pin).rc()
|
in_pin = inst.get_pin(pin).rc()
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class bitcell_array(bitcell_base_array):
|
||||||
# We don't offset this because we need to align
|
# We don't offset this because we need to align
|
||||||
# the replica bitcell in the control logic
|
# the replica bitcell in the control logic
|
||||||
# self.offset_all_coordinates()
|
# self.offset_all_coordinates()
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
""" Create and connect the netlist """
|
""" Create and connect the netlist """
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
|
|
@ -41,7 +41,7 @@ class bitcell_array(bitcell_base_array):
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
|
|
@ -58,20 +58,20 @@ class bitcell_array(bitcell_base_array):
|
||||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||||
mod=self.cell)
|
mod=self.cell)
|
||||||
self.connect_inst(self.get_bitcell_pins(col, row))
|
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Power of Bitcell array and bitline in nW."""
|
"""Power of Bitcell array and bitline in nW."""
|
||||||
|
|
||||||
# Dynamic Power from Bitline
|
# Dynamic Power from Bitline
|
||||||
bl_wire = self.gen_bl_wire()
|
bl_wire = self.gen_bl_wire()
|
||||||
cell_load = 2 * bl_wire.return_input_cap()
|
cell_load = 2 * bl_wire.return_input_cap()
|
||||||
bl_swing = OPTS.rbl_delay_percentage
|
bl_swing = OPTS.rbl_delay_percentage
|
||||||
freq = spice["default_event_frequency"]
|
freq = spice["default_event_frequency"]
|
||||||
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
|
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
|
||||||
|
|
||||||
# Calculate the bitcell power which currently only includes leakage
|
# Calculate the bitcell power which currently only includes leakage
|
||||||
cell_power = self.cell.analytical_power(corner, load)
|
cell_power = self.cell.analytical_power(corner, load)
|
||||||
|
|
||||||
# Leakage power grows with entire array and bitlines.
|
# Leakage power grows with entire array and bitlines.
|
||||||
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
|
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
|
||||||
cell_power.leakage * self.column_size * self.row_size)
|
cell_power.leakage * self.column_size * self.row_size)
|
||||||
|
|
@ -102,7 +102,7 @@ class bitcell_array(bitcell_base_array):
|
||||||
bitcell_wl_cin = self.cell.get_wl_cin()
|
bitcell_wl_cin = self.cell.get_wl_cin()
|
||||||
total_cin = bitcell_wl_cin * self.column_size
|
total_cin = bitcell_wl_cin * self.column_size
|
||||||
return total_cin
|
return total_cin
|
||||||
|
|
||||||
def graph_exclude_bits(self, targ_row, targ_col):
|
def graph_exclude_bits(self, targ_row, targ_col):
|
||||||
"""Excludes bits in column from being added to graph except target"""
|
"""Excludes bits in column from being added to graph except target"""
|
||||||
# Function is not robust with column mux configurations
|
# Function is not robust with column mux configurations
|
||||||
|
|
@ -111,7 +111,7 @@ class bitcell_array(bitcell_base_array):
|
||||||
if row == targ_row and col == targ_col:
|
if row == targ_row and col == targ_col:
|
||||||
continue
|
continue
|
||||||
self.graph_inst_exclude.add(self.cell_inst[row, col])
|
self.graph_inst_exclude.add(self.cell_inst[row, col])
|
||||||
|
|
||||||
def get_cell_name(self, inst_name, row, col):
|
def get_cell_name(self, inst_name, row, col):
|
||||||
"""Gets the spice name of the target bitcell."""
|
"""Gets the spice name of the target bitcell."""
|
||||||
return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col]
|
return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col]
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue