Merged with dev

This commit is contained in:
Hunter Nichols 2020-11-10 15:47:56 -08:00
commit 84ba5c55d1
375 changed files with 20170 additions and 15576 deletions

View File

@ -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!

View File

@ -33,8 +33,8 @@ class contact(hierarchy_design.hierarchy_design):
implant_type=None, well_type=None, name=""): implant_type=None, well_type=None, name=""):
# This will ignore the name parameter since # This will ignore the name parameter since
# we can guarantee a unique name here # we can guarantee a unique name here
super().__init__(name) super().__init__(name, name)
debug.info(4, "create contact object {0}".format(name)) debug.info(4, "create contact object {0}".format(name))
self.add_comment("layers: {0}".format(layer_stack)) self.add_comment("layers: {0}".format(layer_stack))
@ -51,8 +51,8 @@ class contact(hierarchy_design.hierarchy_design):
# Non-preferred directions # Non-preferred directions
if directions == "nonpref": if directions == "nonpref":
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V" first_dir = "H" if tech.preferred_directions[layer_stack[0]]=="V" else "V"
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V" second_dir = "H" if tech.preferred_directions[layer_stack[2]]=="V" else "V"
self.directions = (first_dir, second_dir) self.directions = (first_dir, second_dir)
# Preferred directions # Preferred directions
elif directions == "pref": elif directions == "pref":
@ -80,7 +80,7 @@ class contact(hierarchy_design.hierarchy_design):
self.create_first_layer_enclosure() self.create_first_layer_enclosure()
self.create_second_layer_enclosure() self.create_second_layer_enclosure()
self.create_nitride_cut_enclosure() self.create_nitride_cut_enclosure()
self.height = max(self.first_layer_position.y + self.first_layer_height, self.height = max(self.first_layer_position.y + self.first_layer_height,
self.second_layer_position.y + self.second_layer_height) self.second_layer_position.y + self.second_layer_height)
self.width = max(self.first_layer_position.x + self.first_layer_width, self.width = max(self.first_layer_position.x + self.first_layer_width,
@ -99,7 +99,7 @@ class contact(hierarchy_design.hierarchy_design):
(first_layer, via_layer, second_layer) = self.layer_stack (first_layer, via_layer, second_layer) = self.layer_stack
self.first_layer_name = first_layer self.first_layer_name = first_layer
self.second_layer_name = second_layer self.second_layer_name = second_layer
# Contacts will have unique per first layer # Contacts will have unique per first layer
if via_layer in tech.layer: if via_layer in tech.layer:
self.via_layer_name = via_layer self.via_layer_name = via_layer
@ -115,7 +115,7 @@ class contact(hierarchy_design.hierarchy_design):
def setup_layout_constants(self): def setup_layout_constants(self):
""" Determine the design rules for the enclosure layers """ """ Determine the design rules for the enclosure layers """
self.contact_width = drc("minwidth_{0}". format(self.via_layer_name)) self.contact_width = drc("minwidth_{0}". format(self.via_layer_name))
contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name)) contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name))
self.contact_pitch = self.contact_width + contact_to_contact self.contact_pitch = self.contact_width + contact_to_contact
@ -126,7 +126,7 @@ class contact(hierarchy_design.hierarchy_design):
# DRC rules # DRC rules
# The extend rule applies to asymmetric enclosures in one direction. # The extend rule applies to asymmetric enclosures in one direction.
# The enclosure rule applies to symmetric enclosure component. # The enclosure rule applies to symmetric enclosure component.
self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name)) self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name))
# If there's a different rule for active # If there's a different rule for active
@ -171,7 +171,7 @@ class contact(hierarchy_design.hierarchy_design):
(self.second_layer_minwidth - self.contact_array_width) / 2) (self.second_layer_minwidth - self.contact_array_width) / 2)
else: else:
debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1) debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1)
def create_contact_array(self): def create_contact_array(self):
""" Create the contact array at the origin""" """ Create the contact array at the origin"""
# offset for the via array # offset for the via array
@ -210,7 +210,7 @@ class contact(hierarchy_design.hierarchy_design):
offset=self.second_layer_position - npc_enclose_offset, offset=self.second_layer_position - npc_enclose_offset,
width=self.second_layer_width + 2 * npc_enclose_poly, width=self.second_layer_width + 2 * npc_enclose_poly,
height=self.second_layer_height + 2 * npc_enclose_poly) height=self.second_layer_height + 2 * npc_enclose_poly)
def create_first_layer_enclosure(self): def create_first_layer_enclosure(self):
# this is if the first and second layers are different # this is if the first and second layers are different
self.first_layer_position = vector( self.first_layer_position = vector(
@ -269,12 +269,12 @@ class contact(hierarchy_design.hierarchy_design):
offset=well_position, offset=well_position,
width=self.well_width, width=self.well_width,
height=self.well_height) height=self.well_height)
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
""" Get total power of a module """ """ Get total power of a module """
return self.return_power() return self.return_power()
# Set up a static for each layer to be used for measurements # Set up a static for each layer to be used for measurements
for layer_stack in tech.layer_stacks: for layer_stack in tech.layer_stacks:
(layer1, via, layer2) = layer_stack (layer1, via, layer2) = layer_stack
@ -295,7 +295,7 @@ if "nwell" in tech.layer:
well_type="n") well_type="n")
module = sys.modules[__name__] module = sys.modules[__name__]
setattr(module, "nwell_contact", cont) setattr(module, "nwell_contact", cont)
if "pwell" in tech.layer: if "pwell" in tech.layer:
cont = factory.create(module_type="contact", cont = factory.create(module_type="contact",
layer_stack=tech.active_stack, layer_stack=tech.active_stack,

View File

@ -7,13 +7,15 @@
# #
from globals import OPTS from globals import OPTS
class _pins: class _pins:
def __init__(self, pin_dict): def __init__(self, pin_dict):
# make the pins elements of the class to allow "." access. # make the pins elements of the class to allow "." access.
# For example: props.bitcell.cell_6t.pin.bl = "foobar" # For example: props.bitcell.cell_6t.pin.bl = "foobar"
for k,v in pin_dict.items(): for k, v in pin_dict.items():
self.__dict__[k] = v self.__dict__[k] = v
class _cell: class _cell:
def __init__(self, pin_dict): def __init__(self, pin_dict):
pin_dict.update(self._default_power_pins()) pin_dict.update(self._default_power_pins())
@ -24,13 +26,27 @@ class _cell:
return self._pins return self._pins
def _default_power_pins(self): def _default_power_pins(self):
return { 'vdd' : 'vdd', 'gnd' : 'gnd' } return {'vdd': 'vdd',
'gnd': 'gnd'}
class _mirror_axis: class _mirror_axis:
def __init__(self, x, y): def __init__(self, x, y):
self.x = x self.x = x
self.y = y self.y = y
class _ptx:
def __init__(self, model_is_subckt, bin_spice_models):
self.model_is_subckt = model_is_subckt
self.bin_spice_models = bin_spice_models
class _pgate:
def __init__(self, add_implants):
self.add_implants = add_implants
class _bitcell: class _bitcell:
def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r): def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r):
self.mirror = mirror self.mirror = mirror
@ -42,27 +58,27 @@ class _bitcell:
def _default(): def _default():
axis = _mirror_axis(True, False) axis = _mirror_axis(True, False)
cell_s8_6t = _cell({'bl' : 'bl', cell_s8_6t = _cell({'bl': 'bl',
'br' : 'br', 'br': 'br',
'wl': 'wl'}) 'wl': 'wl'})
cell_6t = _cell({'bl' : 'bl', cell_6t = _cell({'bl': 'bl',
'br' : 'br', 'br': 'br',
'wl' : 'wl'}) 'wl': 'wl'})
cell_1rw1r = _cell({'bl0' : 'bl0', cell_1rw1r = _cell({'bl0': 'bl0',
'br0' : 'br0', 'br0': 'br0',
'bl1' : 'bl1', 'bl1': 'bl1',
'br1' : 'br1', 'br1': 'br1',
'wl0' : 'wl0', 'wl0': 'wl0',
'wl1' : 'wl1'}) 'wl1': 'wl1'})
cell_1w1r = _cell({'bl0' : 'bl0', cell_1w1r = _cell({'bl0': 'bl0',
'br0' : 'br0', 'br0': 'br0',
'bl1' : 'bl1', 'bl1': 'bl1',
'br1' : 'br1', 'br1': 'br1',
'wl0' : 'wl0', 'wl0': 'wl0',
'wl1' : 'wl1'}) 'wl1': 'wl1'})
return _bitcell(cell_s8_6t=cell_s8_6t, return _bitcell(cell_s8_6t=cell_s8_6t,
cell_6t=cell_6t, cell_6t=cell_6t,
@ -94,21 +110,25 @@ class _dff:
self.custom_type_list = custom_type_list self.custom_type_list = custom_type_list
self.clk_pin = clk_pin self.clk_pin = clk_pin
class _dff_buff: class _dff_buff:
def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts): def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports self.use_custom_ports = use_custom_ports
self.buf_ports = custom_buff_ports self.buf_ports = custom_buff_ports
self.add_body_contacts = add_body_contacts self.add_body_contacts = add_body_contacts
class _dff_buff_array: class _dff_buff_array:
def __init__(self, use_custom_ports, add_body_contacts): def __init__(self, use_custom_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports self.use_custom_ports = use_custom_ports
self.add_body_contacts = add_body_contacts self.add_body_contacts = add_body_contacts
class _bitcell_array: class _bitcell_array:
def __init__(self, use_custom_cell_arrangement): def __init__(self, use_custom_cell_arrangement):
self.use_custom_cell_arrangement = use_custom_cell_arrangement self.use_custom_cell_arrangement = use_custom_cell_arrangement
class cell_properties(): class cell_properties():
""" """
This contains meta information about the custom designed cells. For This contains meta information about the custom designed cells. For
@ -117,41 +137,69 @@ class cell_properties():
""" """
def __init__(self): def __init__(self):
self.names = {} self.names = {}
self.names["bitcell"] = "cell_6t"
self.names["bitcell_1rw_1r"] = "cell_1rw_1r"
self.names["bitcell_1w_1r"] = "cell_1w_1r"
self.names["dummy_bitcell"] = "dummy_cell_6t"
self.names["dummy_bitcell_1rw_1r"] = "dummy_cell_1rw_1r"
self.names["dummy_bitcell_1w_1r"] = "dummy_cell_1w_1r"
self.names["replica_bitcell"] = "replica_cell_6t"
self.names["replica_bitcell_1rw_1r"] = "replica_cell_1rw_1r"
self.names["replica_bitcell_1w_1r"] = "replica_cell_1w_1r"
self.names["col_cap_bitcell_6t"] = "col_cap_cell_6t"
self.names["col_cap_bitcell_1rw_1r"] = "col_cap_cell_1rw_1r"
self.names["col_cap_bitcell_1w_1r"] = "col_cap_cell_1w_1r"
self.names["row_cap_bitcell_6t"] = "row_cap_cell_6t"
self.names["row_cap_bitcell_1rw_1r"] = "row_cap_cell_1rw_1r"
self.names["row_cap_bitcell_1w_1r"] = "row_cap_cell_1w_1r"
self._bitcell = _bitcell._default() self._bitcell = _bitcell._default()
self._dff = _dff(use_custom_ports = False,
custom_port_list = ["D", "Q", "clk", "vdd", "gnd"],
custom_type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
clk_pin= "clk")
self._dff_buff = _dff_buff(use_custom_ports = False,
custom_buff_ports = ["D", "qint", "clk", "vdd", "gnd"],
add_body_contacts = False)
self._dff_buff_array = _dff_buff_array(use_custom_ports = False, self._ptx = _ptx(model_is_subckt=False,
add_body_contacts = False) bin_spice_models=False)
self._pgate = _pgate(add_implants=False)
self._dff = _dff(use_custom_ports=False,
custom_port_list=["D", "Q", "clk", "vdd", "gnd"],
custom_type_list=["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
clk_pin="clk")
self._dff_buff = _dff_buff(use_custom_ports=False,
custom_buff_ports=["D", "qint", "clk", "vdd", "gnd"],
add_body_contacts=False)
self._dff_buff_array = _dff_buff_array(use_custom_ports=False,
add_body_contacts=False)
self._write_driver = _cell({'din': 'din', self._write_driver = _cell({'din': 'din',
'bl' : 'bl', 'bl': 'bl',
'br' : 'br', 'br': 'br',
'en' : 'en'}) 'en': 'en'})
self._sense_amp = _cell({'bl' : 'bl', self._sense_amp = _cell({'bl': 'bl',
'br' : 'br', 'br': 'br',
'dout' : 'dout', 'dout': 'dout',
'en' : 'en'}) 'en': 'en'})
self._bitcell_array = _bitcell_array(use_custom_cell_arrangement = []) self._bitcell_array = _bitcell_array(use_custom_cell_arrangement=[])
@property @property
def bitcell(self): def bitcell(self):
return self._bitcell return self._bitcell
@property
def ptx(self):
return self._ptx
@property
def pgate(self):
return self._pgate
@property @property
def dff(self): def dff(self):
return self._dff return self._dff
@property @property
def dff_buff(self): def dff_buff(self):
return self._dff_buff return self._dff_buff
@ -167,7 +215,7 @@ class cell_properties():
@property @property
def sense_amp(self): def sense_amp(self):
return self._sense_amp return self._sense_amp
@property @property
def bitcell_array(self): def bitcell_array(self):
return self._bitcell_array return self._bitcell_array

View File

@ -0,0 +1,193 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2020 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
class _bank:
def __init__(self, stack, pitch):
# bank
# column address route: stack, pitch
# m1_stack, m2_pitch (default)
# m2_stack, m3_pitch (sky130)
self.stack = stack
self.pitch = pitch
class _hierarchical_decoder:
def __init__(self,
bus_layer,
bus_directions,
input_layer,
output_layer,
vertical_supply):
# hierarchical_decoder
# bus_layer, bus_directions, bus_pitch, bus_space, input_layer, output_layer, output_layer_pitch
# m2, pref, m2_pitch, m2_space, m1, m3, m3_pitch
# m1, nonpref, m1_pitch, m2_space, m2, li, li_pitch (sky130)
#
# vertical vdd/gnd
# special jogging
self.bus_layer = bus_layer
self.bus_directions = bus_directions
self.input_layer = input_layer
self.output_layer = output_layer
self.vertical_supply = vertical_supply
class _hierarchical_predecode:
def __init__(self,
bus_layer,
bus_directions,
bus_space_factor,
input_layer,
output_layer,
vertical_supply):
# hierarchical_predecode
# bus_layer, bus_directions, bus_pitch, bus_space, input_layer, output_layer, output_layer_pitch
# m2, pref, m2_pitch, m2_space, m1, m1, m1_pitch
# m1, nonpref, m1_pitch, 1`.5*m1_space, m2, li, li_pitch (sky130)
#
# vertical vdd/gnd
# special jogging
self.bus_layer = bus_layer
self.bus_directions = bus_directions
self.bus_space_factor = bus_space_factor
self.input_layer = input_layer
self.output_layer = output_layer
self.vertical_supply = vertical_supply
class _column_mux_array:
def __init__(self,
select_layer,
select_pitch,
bitline_layer):
# column_mux_array
# sel_layer, sel_pitch, bitline_layer
# m1, m2_pitch, m2
# m3, m3_pitch, m1 (sky130)
self.select_layer = select_layer
self.select_pitch= select_pitch
self.bitline_layer = bitline_layer
class _port_address:
def __init__(self,
supply_offset):
# port_adress
# special supply offset
self.supply_offset = supply_offset
class _port_data:
def __init__(self,
channel_route_bitlines,
enable_layer):
# port_data
# connect bitlines instead of chanel route
# sense_amp_array
# en_layer
# m1
# m3 (sky130)
# precharge_array
# en_bar_layer
# m1
# m3 (sky130)
self.channel_route_bitlines = channel_route_bitlines
self.enable_layer = enable_layer
class _replica_column:
def __init__(self,
even_rows):
# replica_column
# even row check (sky130)
self.even_rows = even_rows
class _wordline_driver:
def __init__(self,
vertical_supply):
# wordline_buffer_array
# vertical vdd/gnd (sky130)
# wordline_driver_array
# vertical vdd/gnd (sky130)
# wordline_driver
# vertical vdd/gnd (sky130)
self.vertical_supply = vertical_supply
class layer_properties():
"""
This contains meta information about the module routing layers. These
can be overriden in the tech.py file.
"""
def __init__(self):
self._bank = _bank(stack="m1_stack",
pitch="m2_pitch")
self._hierarchical_decoder = _hierarchical_decoder(bus_layer="m2",
bus_directions="pref",
input_layer="m1",
output_layer="m3",
vertical_supply=False)
self._hierarchical_predecode = _hierarchical_predecode(bus_layer="m2",
bus_directions="pref",
bus_space_factor=1,
input_layer="m1",
output_layer="m1",
vertical_supply=False)
self._column_mux_array = _column_mux_array(select_layer="m1",
select_pitch="m2_pitch",
bitline_layer="m2")
self._port_address = _port_address(supply_offset=False)
self._port_data = _port_data(channel_route_bitlines=True,
enable_layer="m1")
self._replica_column = _replica_column(even_rows=False)
self._wordline_driver = _wordline_driver(vertical_supply=False)
@property
def bank(self):
return self._bank
@property
def column_mux_array(self):
return self._column_mux_array
@property
def hierarchical_decoder(self):
return self._hierarchical_decoder
@property
def hierarchical_predecode(self):
return self._hierarchical_predecode
@property
def port_address(self):
return self._port_address
@property
def port_data(self):
return self._port_data
@property
def replica_column(self):
return self._replica_column
@property
def wordline_driver(self):
return self._wordline_driver

View File

@ -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)

View File

@ -8,22 +8,29 @@
from hierarchy_design import hierarchy_design from hierarchy_design import hierarchy_design
from utils import round_to_grid from utils import round_to_grid
import contact import contact
from tech import preferred_directions
from tech import cell_properties as props
from globals import OPTS from globals import OPTS
import re import re
import debug
class design(hierarchy_design): class design(hierarchy_design):
""" """
This is the same as the hierarchy_design class except it contains This is the same as the hierarchy_design class except it contains
some DRC/layer constants and analytical models for other modules to reuse. some DRC/layer constants and analytical models for other modules to reuse.
""" """
def __init__(self, name): def __init__(self, name, cell_name=None):
super().__init__(name) # This allows us to use different GDS/spice circuits for hard cells instead of the default ones
# Except bitcell names are generated automatically by the globals.py setup_bitcells routines
self.setup_drc_constants() # depending on the number of ports.
self.setup_layer_constants() if name in props.names:
cell_name = props.names[name]
elif not cell_name:
cell_name = name
super().__init__(name, cell_name)
self.setup_multiport_constants() self.setup_multiport_constants()
def check_pins(self): def check_pins(self):
@ -31,106 +38,9 @@ class design(hierarchy_design):
pins = self.get_pins(pin_name) pins = self.get_pins(pin_name)
for pin in pins: for pin in pins:
print(pin_name, pin) print(pin_name, pin)
def setup_layer_constants(self):
"""
These are some layer constants used
in many places in the compiler.
"""
from tech import layer_indices
import tech
for layer in layer_indices:
key = "{}_stack".format(layer)
# Set the stack as a local helper @classmethod
try: def setup_drc_constants(design):
layer_stack = getattr(tech, key)
setattr(self, key, layer_stack)
except AttributeError:
pass
# Skip computing the pitch for active
if layer == "active":
continue
# Add the pitch
setattr(self,
"{}_pitch".format(layer),
self.compute_pitch(layer, True))
# Add the non-preferrd pitch (which has vias in the "wrong" way)
setattr(self,
"{}_nonpref_pitch".format(layer),
self.compute_pitch(layer, False))
if False:
from tech import preferred_directions
print(preferred_directions)
from tech import layer, layer_indices
for name in layer_indices:
if name == "active":
continue
try:
print("{0} width {1} space {2}".format(name,
getattr(self, "{}_width".format(name)),
getattr(self, "{}_space".format(name))))
print("pitch {0} nonpref {1}".format(getattr(self, "{}_pitch".format(name)),
getattr(self, "{}_nonpref_pitch".format(name))))
except AttributeError:
pass
import sys
sys.exit(1)
def compute_pitch(self, layer, preferred=True):
"""
This is the preferred direction pitch
i.e. we take the minimum or maximum contact dimension
"""
# Find the layer stacks this is used in
from tech import layer_stacks
pitches = []
for stack in layer_stacks:
# Compute the pitch with both vias above and below (if they exist)
if stack[0] == layer:
pitches.append(self.compute_layer_pitch(stack, preferred))
if stack[2] == layer:
pitches.append(self.compute_layer_pitch(stack[::-1], True))
return max(pitches)
def compute_layer_pitch(self, layer_stack, preferred):
(layer1, via, layer2) = layer_stack
try:
if layer1 == "poly" or layer1 == "active":
contact1 = getattr(contact, layer1 + "_contact")
else:
contact1 = getattr(contact, layer1 + "_via")
except AttributeError:
contact1 = getattr(contact, layer2 + "_via")
if preferred:
if self.get_preferred_direction(layer1) == "V":
contact_width = contact1.first_layer_width
else:
contact_width = contact1.first_layer_height
else:
if self.get_preferred_direction(layer1) == "V":
contact_width = contact1.first_layer_height
else:
contact_width = contact1.first_layer_width
layer_space = getattr(self, layer1 + "_space")
#print(layer_stack)
#print(contact1)
pitch = contact_width + layer_space
return round_to_grid(pitch)
def setup_drc_constants(self):
""" """
These are some DRC constants used in many places These are some DRC constants used in many places
in the compiler. in the compiler.
@ -142,85 +52,191 @@ class design(hierarchy_design):
match = re.search(r"minwidth_(.*)", rule) match = re.search(r"minwidth_(.*)", rule)
if match: if match:
if match.group(1) == "active_contact": if match.group(1) == "active_contact":
setattr(self, "contact_width", drc(match.group(0))) setattr(design, "contact_width", drc(match.group(0)))
else: else:
setattr(self, match.group(1) + "_width", drc(match.group(0))) setattr(design, match.group(1) + "_width", drc(match.group(0)))
# Single layer area rules # Single layer area rules
match = re.search(r"minarea_(.*)", rule) match = re.search(r"minarea_(.*)", rule)
if match: if match:
setattr(self, match.group(0), drc(match.group(0))) setattr(design, match.group(0), drc(match.group(0)))
# Single layer spacing rules # Single layer spacing rules
match = re.search(r"(.*)_to_(.*)", rule) match = re.search(r"(.*)_to_(.*)", rule)
if match and match.group(1) == match.group(2): if match and match.group(1) == match.group(2):
setattr(self, match.group(1) + "_space", drc(match.group(0))) setattr(design, match.group(1) + "_space", drc(match.group(0)))
elif match and match.group(1) != match.group(2): elif match and match.group(1) != match.group(2):
if match.group(2) == "poly_active": if match.group(2) == "poly_active":
setattr(self, match.group(1) + "_to_contact", setattr(design, match.group(1) + "_to_contact",
drc(match.group(0))) drc(match.group(0)))
else: else:
setattr(self, match.group(0), drc(match.group(0))) setattr(design, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_enclose_(.*)", rule) match = re.search(r"(.*)_enclose_(.*)", rule)
if match: if match:
setattr(self, match.group(0), drc(match.group(0))) setattr(design, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_extend_(.*)", rule) match = re.search(r"(.*)_extend_(.*)", rule)
if match: if match:
setattr(self, match.group(0), drc(match.group(0))) setattr(design, match.group(0), drc(match.group(0)))
# Create the maximum well extend active that gets used # Create the maximum well extend active that gets used
# by cells to extend the wells for interaction with other cells # by cells to extend the wells for interaction with other cells
from tech import layer from tech import layer
self.well_extend_active = 0 design.well_extend_active = 0
if "nwell" in layer: if "nwell" in layer:
self.well_extend_active = max(self.well_extend_active, self.nwell_extend_active) design.well_extend_active = max(design.well_extend_active, design.nwell_extend_active)
if "pwell" in layer: if "pwell" in layer:
self.well_extend_active = max(self.well_extend_active, self.pwell_extend_active) design.well_extend_active = max(design.well_extend_active, design.pwell_extend_active)
# The active offset is due to the well extension # The active offset is due to the well extension
if "pwell" in layer: if "pwell" in layer:
self.pwell_enclose_active = drc("pwell_enclose_active") design.pwell_enclose_active = drc("pwell_enclose_active")
else: else:
self.pwell_enclose_active = 0 design.pwell_enclose_active = 0
if "nwell" in layer: if "nwell" in layer:
self.nwell_enclose_active = drc("nwell_enclose_active") design.nwell_enclose_active = drc("nwell_enclose_active")
else: else:
self.nwell_enclose_active = 0 design.nwell_enclose_active = 0
# Use the max of either so that the poly gates will align properly # Use the max of either so that the poly gates will align properly
self.well_enclose_active = max(self.pwell_enclose_active, design.well_enclose_active = max(design.pwell_enclose_active,
self.nwell_enclose_active, design.nwell_enclose_active,
self.active_space) design.active_space)
# These are for debugging previous manual rules # These are for debugging previous manual rules
if False: if False:
print("poly_width", self.poly_width) print("poly_width", design.poly_width)
print("poly_space", self.poly_space) print("poly_space", design.poly_space)
print("m1_width", self.m1_width) print("m1_width", design.m1_width)
print("m1_space", self.m1_space) print("m1_space", design.m1_space)
print("m2_width", self.m2_width) print("m2_width", design.m2_width)
print("m2_space", self.m2_space) print("m2_space", design.m2_space)
print("m3_width", self.m3_width) print("m3_width", design.m3_width)
print("m3_space", self.m3_space) print("m3_space", design.m3_space)
print("m4_width", self.m4_width) print("m4_width", design.m4_width)
print("m4_space", self.m4_space) print("m4_space", design.m4_space)
print("active_width", self.active_width) print("active_width", design.active_width)
print("active_space", self.active_space) print("active_space", design.active_space)
print("contact_width", self.contact_width) print("contact_width", design.contact_width)
print("poly_to_active", self.poly_to_active) print("poly_to_active", design.poly_to_active)
print("poly_extend_active", self.poly_extend_active) print("poly_extend_active", design.poly_extend_active)
print("poly_to_contact", self.poly_to_contact) print("poly_to_contact", design.poly_to_contact)
print("active_contact_to_gate", self.active_contact_to_gate) print("active_contact_to_gate", design.active_contact_to_gate)
print("poly_contact_to_gate", self.poly_contact_to_gate) print("poly_contact_to_gate", design.poly_contact_to_gate)
print("well_enclose_active", self.well_enclose_active) print("well_enclose_active", design.well_enclose_active)
print("implant_enclose_active", self.implant_enclose_active) print("implant_enclose_active", design.implant_enclose_active)
print("implant_space", self.implant_space) print("implant_space", design.implant_space)
import sys import sys
sys.exit(1) sys.exit(1)
@classmethod
def setup_layer_constants(design):
"""
These are some layer constants used
in many places in the compiler.
"""
from tech import layer_indices
import tech
for layer in layer_indices:
key = "{}_stack".format(layer)
# Set the stack as a local helper
try:
layer_stack = getattr(tech, key)
setattr(design, key, layer_stack)
except AttributeError:
pass
# Skip computing the pitch for active
if layer == "active":
continue
# Add the pitch
setattr(design,
"{}_pitch".format(layer),
design.compute_pitch(layer, True))
# Add the non-preferrd pitch (which has vias in the "wrong" way)
setattr(design,
"{}_nonpref_pitch".format(layer),
design.compute_pitch(layer, False))
if False:
from tech import preferred_directions
print(preferred_directions)
from tech import layer, layer_indices
for name in layer_indices:
if name == "active":
continue
try:
print("{0} width {1} space {2}".format(name,
getattr(design, "{}_width".format(name)),
getattr(design, "{}_space".format(name))))
print("pitch {0} nonpref {1}".format(getattr(design, "{}_pitch".format(name)),
getattr(design, "{}_nonpref_pitch".format(name))))
except AttributeError:
pass
import sys
sys.exit(1)
@staticmethod
def compute_pitch(layer, preferred=True):
"""
This is the preferred direction pitch
i.e. we take the minimum or maximum contact dimension
"""
# Find the layer stacks this is used in
from tech import layer_stacks
pitches = []
for stack in layer_stacks:
# Compute the pitch with both vias above and below (if they exist)
if stack[0] == layer:
pitches.append(design.compute_layer_pitch(stack, preferred))
if stack[2] == layer:
pitches.append(design.compute_layer_pitch(stack[::-1], True))
return max(pitches)
@staticmethod
def get_preferred_direction(layer):
return preferred_directions[layer]
@staticmethod
def compute_layer_pitch(layer_stack, preferred):
(layer1, via, layer2) = layer_stack
try:
if layer1 == "poly" or layer1 == "active":
contact1 = getattr(contact, layer1 + "_contact")
else:
contact1 = getattr(contact, layer1 + "_via")
except AttributeError:
contact1 = getattr(contact, layer2 + "_via")
if preferred:
if preferred_directions[layer1] == "V":
contact_width = contact1.first_layer_width
else:
contact_width = contact1.first_layer_height
else:
if preferred_directions[layer1] == "V":
contact_width = contact1.first_layer_height
else:
contact_width = contact1.first_layer_width
layer_space = getattr(design, layer1 + "_space")
#print(layer_stack)
#print(contact1)
pitch = contact_width + layer_space
return round_to_grid(pitch)
def setup_multiport_constants(self): def setup_multiport_constants(self):
""" """
These are contants and lists that aid multiport design. These are contants and lists that aid multiport design.
Ports are always in the order RW, W, R. Ports are always in the order RW, W, R.
Port indices start from 0 and increment. Port indices start from 0 and increment.
@ -258,11 +274,14 @@ class design(hierarchy_design):
self.read_ports.append(port_number) self.read_ports.append(port_number)
self.readonly_ports.append(port_number) self.readonly_ports.append(port_number)
port_number += 1 port_number += 1
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
""" Get total power of a module """ """ Get total power of a module """
total_module_power = self.return_power() total_module_power = self.return_power()
for inst in self.insts: for inst in self.insts:
total_module_power += inst.mod.analytical_power(corner, load) total_module_power += inst.mod.analytical_power(corner, load)
return total_module_power return total_module_power
design.setup_drc_constants()
design.setup_layer_constants()

View File

@ -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"""

View File

@ -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)

View File

@ -20,9 +20,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
""" """
name_map = [] name_map = []
def __init__(self, name): def __init__(self, name, cell_name):
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds" self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds"
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" self.sp_file = OPTS.openram_tech + "sp_lib/" + cell_name + ".sp"
# If we have a separate lvs directory, then all the lvs files # If we have a separate lvs directory, then all the lvs files
# should be in there (all or nothing!) # should be in there (all or nothing!)
@ -33,7 +33,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
lvs_dir = OPTS.openram_tech + lvs_subdir + "/" lvs_dir = OPTS.openram_tech + lvs_subdir + "/"
if os.path.exists(lvs_dir): if os.path.exists(lvs_dir):
self.lvs_file = lvs_dir + name + ".sp" self.lvs_file = lvs_dir + cell_name + ".sp"
else: else:
self.lvs_file = self.sp_file self.lvs_file = self.sp_file
@ -41,8 +41,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.lvs_errors = "skipped" self.lvs_errors = "skipped"
self.name = name self.name = name
hierarchy_spice.spice.__init__(self, name) self.cell_name = cell_name
hierarchy_layout.layout.__init__(self, name) hierarchy_spice.spice.__init__(self, name, cell_name)
hierarchy_layout.layout.__init__(self, name, cell_name)
self.init_graph_params() self.init_graph_params()
def get_layout_pins(self, inst): def get_layout_pins(self, inst):
@ -55,7 +56,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
debug.error("Couldn't find instance {0}".format(inst.name), -1) debug.error("Couldn't find instance {0}".format(inst.name), -1)
inst_map = inst.mod.pin_map inst_map = inst.mod.pin_map
return inst_map return inst_map
def DRC_LVS(self, final_verification=False, force_check=False): def DRC_LVS(self, final_verification=False, force_check=False):
"""Checks both DRC and LVS for a module""" """Checks both DRC and LVS for a module"""
import verify import verify
@ -76,23 +77,23 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.lvs_write(tempspice) self.lvs_write(tempspice)
self.gds_write(tempgds) self.gds_write(tempgds)
# Final verification option does not allow nets to be connected by label. # Final verification option does not allow nets to be connected by label.
self.drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) self.drc_errors = verify.run_drc(self.cell_name, tempgds, extract=True, final_verification=final_verification)
self.lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) self.lvs_errors = verify.run_lvs(self.cell_name, tempgds, tempspice, final_verification=final_verification)
# force_check is used to determine decoder height and other things, so we shouldn't fail # force_check is used to determine decoder height and other things, so we shouldn't fail
# if that flag is set # if that flag is set
if OPTS.inline_lvsdrc and not force_check: if OPTS.inline_lvsdrc and not force_check:
debug.check(self.drc_errors == 0, debug.check(self.drc_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.name, "DRC failed for {0} with {1} error(s)".format(self.cell_name,
self.drc_errors)) self.drc_errors))
debug.check(self.lvs_errors == 0, debug.check(self.lvs_errors == 0,
"LVS failed for {0} with {1} errors(s)".format(self.name, "LVS failed for {0} with {1} errors(s)".format(self.cell_name,
self.lvs_errors)) self.lvs_errors))
if OPTS.purge_temp: if not OPTS.keep_temp:
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) os.remove(tempgds)
def DRC(self, final_verification=False): def DRC(self, final_verification=False):
"""Checks DRC for a module""" """Checks DRC for a module"""
import verify import verify
@ -104,14 +105,14 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only: if OPTS.netlist_only:
return return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.cell_name)
self.gds_write(tempgds) self.gds_write(tempgds)
num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) num_errors = verify.run_drc(self.cell_name, tempgds, final_verification=final_verification)
debug.check(num_errors == 0, debug.check(num_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.name, "DRC failed for {0} with {1} error(s)".format(self.cell_name,
num_errors)) num_errors))
if OPTS.purge_temp: if not OPTS.keep_temp:
os.remove(tempgds) os.remove(tempgds)
def LVS(self, final_verification=False): def LVS(self, final_verification=False):
@ -125,30 +126,30 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only: if OPTS.netlist_only:
return return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.cell_name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
self.lvs_write(tempspice) self.lvs_write(tempspice)
self.gds_write(tempgds) self.gds_write(tempgds)
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
debug.check(num_errors == 0, debug.check(num_errors == 0,
"LVS failed for {0} with {1} error(s)".format(self.name, "LVS failed for {0} with {1} error(s)".format(self.cell_name,
num_errors)) num_errors))
if OPTS.purge_temp: if not OPTS.keep_temp:
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) os.remove(tempgds)
def init_graph_params(self): def init_graph_params(self):
""" """
Initializes parameters relevant to the graph creation Initializes parameters relevant to the graph creation
""" """
# Only initializes a set for checking instances which should not be added # Only initializes a set for checking instances which should not be added
self.graph_inst_exclude = set() self.graph_inst_exclude = set()
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
""" """
Recursively create graph from instances in module. Recursively create graph from instances in module.
""" """
# Translate port names to external nets # Translate port names to external nets
if len(port_nets) != len(self.pins): if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
@ -162,7 +163,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
subinst_name = inst_name + '.X' + subinst.name subinst_name = inst_name + '.X' + subinst.name
subinst_ports = self.translate_nets(conns, port_dict, inst_name) subinst_ports = self.translate_nets(conns, port_dict, inst_name)
subinst.mod.build_graph(graph, subinst_name, subinst_ports) subinst.mod.build_graph(graph, subinst_name, subinst_ports)
def build_names(self, name_dict, inst_name, port_nets): def build_names(self, name_dict, inst_name, port_nets):
""" """
Collects all the nets and the parent inst of that net. Collects all the nets and the parent inst of that net.
@ -195,7 +196,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
else: else:
converted_conns.append("{}.{}".format(inst_name, conn)) converted_conns.append("{}.{}".format(inst_name, conn))
return converted_conns return converted_conns
def add_graph_edges(self, graph, port_nets): def add_graph_edges(self, graph, port_nets):
""" """
For every input, adds an edge to every output. For every input, adds an edge to every output.
@ -211,13 +212,13 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
for out in output_pins + inout_pins: for out in output_pins + inout_pins:
if inp != out: # do not add self loops if inp != out: # do not add self loops
graph.add_edge(pin_dict[inp], pin_dict[out], self) graph.add_edge(pin_dict[inp], pin_dict[out], self)
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
pins = ",".join(self.pins) pins = ",".join(self.pins)
insts = [" {}".format(x) for x in self.insts] insts = [" {}".format(x) for x in self.insts]
objs = [" {}".format(x) for x in self.objs] objs = [" {}".format(x) for x in self.objs]
s = "********** design {0} **********".format(self.name) s = "********** design {0} **********".format(self.cell_name)
s += "\n pins ({0})={1}\n".format(len(self.pins), pins) s += "\n pins ({0})={1}\n".format(len(self.pins), pins)
s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs)) s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs))
s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts)) s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts))
@ -231,4 +232,4 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
for i in self.insts: for i in self.insts:
text+=str(i) + ",\n" text+=str(i) + ",\n"
return text return text

View File

@ -31,8 +31,9 @@ class layout():
layout/netlist and perform LVS/DRC. layout/netlist and perform LVS/DRC.
""" """
def __init__(self, name): def __init__(self, name, cell_name):
self.name = name self.name = name
self.cell_name = cell_name
self.width = None self.width = None
self.height = None self.height = None
self.bounding_box = None self.bounding_box = None
@ -67,13 +68,13 @@ class layout():
def offset_x_coordinates(self): def offset_x_coordinates(self):
""" """
This function is called after everything is placed to This function is called after everything is placed to
shift the origin to the furthest left point. shift the origin to the furthest left point.
Y offset is unchanged. Y offset is unchanged.
""" """
offset = self.find_lowest_coords() offset = self.find_lowest_coords()
self.translate_all(offset.scale(1, 0)) self.translate_all(offset.scale(1, 0))
return offset return offset
def get_gate_offset(self, x_offset, height, inv_num): def get_gate_offset(self, x_offset, height, inv_num):
""" """
Gets the base offset and y orientation of stacked rows of gates Gets the base offset and y orientation of stacked rows of gates
@ -215,7 +216,7 @@ class layout():
# Contacts are not really instances, so skip them # Contacts are not really instances, so skip them
if "contact" not in mod.name: if "contact" not in mod.name:
# Check that the instance name is unique # Check that the instance name is unique
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.name, name)) debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name))
self.inst_names.add(name) self.inst_names.add(name)
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
@ -300,9 +301,9 @@ class layout():
tx_list.append(i) tx_list.append(i)
except AttributeError: except AttributeError:
pass pass
return tx_list return tx_list
def get_pin(self, text): def get_pin(self, text):
""" """
Return the pin or list of pins Return the pin or list of pins
@ -316,7 +317,7 @@ class layout():
return any_pin return any_pin
except Exception: except Exception:
self.gds_write("missing_pin.gds") self.gds_write("missing_pin.gds")
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.name), -1) debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.cell_name), -1)
def get_pins(self, text): def get_pins(self, text):
""" """
@ -537,10 +538,6 @@ class layout():
position_list=coordinates, position_list=coordinates,
widen_short_wires=widen_short_wires) widen_short_wires=widen_short_wires)
def get_preferred_direction(self, layer):
""" Return the preferred routing directions """
return preferred_directions[layer]
def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None): def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
""" Add a three layer via structure. """ """ Add a three layer via structure. """
from sram_factory import factory from sram_factory import factory
@ -617,24 +614,24 @@ class layout():
next_id = 0 next_id = 0
curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None) curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None)
via = self.add_via_center(layers=curr_stack, via = self.add_via_center(layers=curr_stack,
size=size, size=size,
offset=offset, offset=offset,
directions=directions, directions=directions,
implant_type=implant_type, implant_type=implant_type,
well_type=well_type) well_type=well_type)
if cur_layer != from_layer: if cur_layer != from_layer:
self.add_min_area_rect_center(cur_layer, self.add_min_area_rect_center(cur_layer,
offset, offset,
via.mod.first_layer_width, via.mod.first_layer_width,
via.mod.first_layer_height) via.mod.first_layer_height)
cur_layer = curr_stack[next_id] cur_layer = curr_stack[next_id]
return via return via
def add_min_area_rect_center(self, def add_min_area_rect_center(self,
layer, layer,
offset, offset,
@ -648,14 +645,14 @@ class layout():
min_area = drc("minarea_{}".format(layer)) min_area = drc("minarea_{}".format(layer))
if min_area == 0: if min_area == 0:
return return
min_width = drc("minwidth_{}".format(layer)) min_width = drc("minwidth_{}".format(layer))
if preferred_directions[layer] == "V": if preferred_directions[layer] == "V":
height = max(min_area / width, min_width) height = max(min_area / width, min_width)
else: else:
width = max(min_area / height, min_width) width = max(min_area / height, min_width)
self.add_rect_center(layer=layer, self.add_rect_center(layer=layer,
offset=offset, offset=offset,
width=width, width=width,
@ -739,7 +736,7 @@ class layout():
height = boundary[1][1] - boundary[0][1] height = boundary[1][1] - boundary[0][1]
width = boundary[1][0] - boundary[0][0] width = boundary[1][0] - boundary[0][0]
for boundary_layer in boundary_layers: for boundary_layer in boundary_layers:
(layer_number, layer_purpose) = techlayer[boundary_layer] (layer_number, layer_purpose) = techlayer[boundary_layer]
gds_layout.addBox(layerNumber=layer_number, gds_layout.addBox(layerNumber=layer_number,
@ -891,7 +888,7 @@ class layout():
new_pin = pin_layout(names[i], new_pin = pin_layout(names[i],
[rect.ll(), rect.ur()], [rect.ll(), rect.ur()],
layer) layer)
pins[names[i]] = new_pin pins[names[i]] = new_pin
else: else:
for i in range(len(names)): for i in range(len(names)):
@ -909,7 +906,7 @@ class layout():
new_pin = pin_layout(names[i], new_pin = pin_layout(names[i],
[rect.ll(), rect.ur()], [rect.ll(), rect.ur()],
layer) layer)
pins[names[i]] = new_pin pins[names[i]] = new_pin
return pins return pins
@ -1047,7 +1044,7 @@ class layout():
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self) cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
self.add_inst(cr.name, cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
def add_boundary(self, ll=vector(0, 0), ur=None): def add_boundary(self, ll=vector(0, 0), ur=None):
""" Add boundary for debugging dimensions """ """ Add boundary for debugging dimensions """
if OPTS.netlist_only: if OPTS.netlist_only:
@ -1112,7 +1109,7 @@ class layout():
width=xmax - xmin, width=xmax - xmin,
height=ymax - ymin) height=ymax - ymin)
return rect return rect
def copy_power_pins(self, inst, name, add_vias=True): def copy_power_pins(self, inst, name, add_vias=True):
""" """
This will copy a power pin if it is on the lowest power_grid layer. This will copy a power pin if it is on the lowest power_grid layer.
@ -1171,7 +1168,7 @@ class layout():
bottom = ll.y bottom = ll.y
right = ur.x right = ur.x
top = ur.y top = ur.y
pin_loc = pin.center() pin_loc = pin.center()
if side == "left": if side == "left":
peri_pin_loc = vector(left, pin_loc.y) peri_pin_loc = vector(left, pin_loc.y)
@ -1189,14 +1186,14 @@ class layout():
self.add_via_stack_center(from_layer=pin.layer, self.add_via_stack_center(from_layer=pin.layer,
to_layer=layer, to_layer=layer,
offset=pin_loc) offset=pin_loc)
self.add_path(layer, self.add_path(layer,
[pin_loc, peri_pin_loc]) [pin_loc, peri_pin_loc])
return self.add_layout_pin_rect_center(text=name, return self.add_layout_pin_rect_center(text=name,
layer=layer, layer=layer,
offset=peri_pin_loc) offset=peri_pin_loc)
def add_power_ring(self, bbox): def add_power_ring(self, bbox):
""" """
Create vdd and gnd power rings around an area of the bounding box Create vdd and gnd power rings around an area of the bounding box

View File

@ -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.

View File

@ -5,14 +5,9 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import gdsMill
import tech
import globals
import math
import debug import debug
import datetime from tech import layer_names
from collections import defaultdict
import pdb
class lef: class lef:
""" """
@ -20,13 +15,13 @@ class lef:
and write them to LEF file. and write them to LEF file.
This is inherited by the sram_base class. This is inherited by the sram_base class.
""" """
def __init__(self,layers): def __init__(self, layers):
# LEF db units per micron # LEF db units per micron
self.lef_units = 2000 self.lef_units = 2000
# These are the layers of the obstructions # These are the layers of the obstructions
self.lef_layers = layers self.lef_layers = layers
# Round to ensure float values are divisible by 0.0025 (the manufacturing grid) # Round to ensure float values are divisible by 0.0025 (the manufacturing grid)
self.round_grid = 4; self.round_grid = 4
def lef_write(self, lef_name): def lef_write(self, lef_name):
"""Write the entire lef of the object to the file.""" """Write the entire lef of the object to the file."""
@ -34,14 +29,14 @@ class lef:
self.indent = "" # To maintain the indent level easily self.indent = "" # To maintain the indent level easily
self.lef = open(lef_name,"w") self.lef = open(lef_name, "w")
self.lef_write_header() self.lef_write_header()
for pin in self.pins: for pin in self.pins:
self.lef_write_pin(pin) self.lef_write_pin(pin)
self.lef_write_obstructions() self.lef_write_obstructions()
self.lef_write_footer() self.lef_write_footer()
self.lef.close() self.lef.close()
def lef_write_header(self): def lef_write_header(self):
""" Header of LEF file """ """ Header of LEF file """
self.lef.write("VERSION 5.4 ;\n") self.lef.write("VERSION 5.4 ;\n")
@ -51,78 +46,80 @@ class lef:
self.lef.write("UNITS\n") self.lef.write("UNITS\n")
self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units)) self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units))
self.lef.write("END UNITS\n") self.lef.write("END UNITS\n")
self.lef.write("{0}MACRO {1}\n".format(self.indent,self.name)) self.lef.write("{0}MACRO {1}\n".format(self.indent, self.name))
self.indent += " " self.indent += " "
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent)) self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent, self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent,
round(self.width,self.round_grid), round(self.width, self.round_grid),
round(self.height,self.round_grid))) round(self.height, self.round_grid)))
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent)) self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
def lef_write_footer(self): def lef_write_footer(self):
self.lef.write("{0}END {1}\n".format(self.indent,self.name)) self.lef.write("{0}END {1}\n".format(self.indent, self.name))
self.indent = self.indent[:-3] self.indent = self.indent[:-3]
self.lef.write("END LIBRARY\n") self.lef.write("END LIBRARY\n")
def lef_write_pin(self, name): def lef_write_pin(self, name):
pin_dir = self.get_pin_dir(name) pin_dir = self.get_pin_dir(name)
pin_type = self.get_pin_type(name) pin_type = self.get_pin_type(name)
self.lef.write("{0}PIN {1}\n".format(self.indent,name)) self.lef.write("{0}PIN {1}\n".format(self.indent, name))
self.indent += " " self.indent += " "
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent,pin_dir)) self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent, pin_dir))
if pin_type in ["POWER","GROUND"]: if pin_type in ["POWER", "GROUND"]:
self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type)) self.lef.write("{0}USE {1} ; \n".format(self.indent, pin_type))
self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent)) self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent))
self.lef.write("{0}PORT\n".format(self.indent)) self.lef.write("{0}PORT\n".format(self.indent))
self.indent += " " self.indent += " "
# We could sort these together to minimize different layer sections, but meh. # We could sort these together to minimize different layer sections, but meh.
pin_list = self.get_pins(name) pin_list = self.get_pins(name)
for pin in pin_list: for pin in pin_list:
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer)) self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[pin.layer]))
self.lef_write_shape(pin.rect) self.lef_write_shape(pin.rect)
# End the PORT # End the PORT
self.indent = self.indent[:-3] self.indent = self.indent[:-3]
self.lef.write("{0}END\n".format(self.indent)) self.lef.write("{0}END\n".format(self.indent))
# End the PIN # End the PIN
self.indent = self.indent[:-3] self.indent = self.indent[:-3]
self.lef.write("{0}END {1}\n".format(self.indent,name)) self.lef.write("{0}END {1}\n".format(self.indent, name))
def lef_write_obstructions(self): def lef_write_obstructions(self):
""" Write all the obstructions on each layer """ """ Write all the obstructions on each layer """
self.lef.write("{0}OBS\n".format(self.indent)) self.lef.write("{0}OBS\n".format(self.indent))
for layer in self.lef_layers: for layer in self.lef_layers:
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,layer)) self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[layer]))
self.indent += " " self.indent += " "
# pdb.set_trace() blockages = self.get_blockages(layer, True)
blockages = self.get_blockages(layer,True)
for b in blockages: for b in blockages:
# if len(b) > 2:
# print(b)
self.lef_write_shape(b) self.lef_write_shape(b)
self.indent = self.indent[:-3] self.indent = self.indent[:-3]
self.lef.write("{0}END\n".format(self.indent)) self.lef.write("{0}END\n".format(self.indent))
def lef_write_shape(self, rect): def lef_write_shape(self, rect):
if len(rect) == 2: if len(rect) == 2:
""" Write a LEF rectangle """ """ Write a LEF rectangle """
self.lef.write("{0}RECT ".format(self.indent)) self.lef.write("{0}RECT ".format(self.indent))
for item in rect: for item in rect:
# print(rect) # print(rect)
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid))) self.lef.write(" {0} {1}".format(round(item[0],
self.round_grid),
round(item[1],
self.round_grid)))
self.lef.write(" ;\n") self.lef.write(" ;\n")
else: else:
""" Write a LEF polygon """ """ Write a LEF polygon """
self.lef.write("{0}POLYGON ".format(self.indent)) self.lef.write("{0}POLYGON ".format(self.indent))
for item in rect: for item in rect:
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid))) self.lef.write(" {0} {1}".format(round(item[0],
self.round_grid),
round(item[1],
self.round_grid)))
# for i in range(0,len(rect)): # for i in range(0,len(rect)):
# self.lef.write(" {0} {1}".format(round(rect[i][0],self.round_grid), round(rect[i][1],self.round_grid))) # self.lef.write(" {0} {1}".format(round(rect[i][0],self.round_grid), round(rect[i][1],self.round_grid)))
self.lef.write(" ;\n") self.lef.write(" ;\n")

View File

@ -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(),

View File

@ -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."""

View File

@ -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))

View File

@ -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__):

View File

@ -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 .
""" """

View File

@ -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."""

View File

@ -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

View File

@ -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

View File

@ -6,11 +6,9 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props from tech import cell_properties as props
import bitcell_base import bitcell_base
from globals import OPTS
class bitcell(bitcell_base.bitcell_base): class bitcell(bitcell_base.bitcell_base):
""" """
@ -20,8 +18,6 @@ class bitcell(bitcell_base.bitcell_base):
library. library.
""" """
# If we have a split WL bitcell, if not be backwards
# compatible in the tech file
pin_names = [props.bitcell.cell_6t.pin.bl, pin_names = [props.bitcell.cell_6t.pin.bl,
props.bitcell.cell_6t.pin.br, props.bitcell.cell_6t.pin.br,
props.bitcell.cell_6t.pin.wl, props.bitcell.cell_6t.pin.wl,
@ -30,24 +26,12 @@ class bitcell(bitcell_base.bitcell_base):
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar'] storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_6t", def __init__(self, name):
GDS["unit"], super().__init__(name)
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "cell_6t")
debug.info(2, "Create bitcell") debug.info(2, "Create bitcell")
self.width = bitcell.width
self.height = bitcell.height
self.pin_map = bitcell.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
#debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells")
def get_all_wl_names(self): def get_all_wl_names(self):
""" Creates a list of all wordline pin names """ """ Creates a list of all wordline pin names """
row_pins = [props.bitcell.cell_6t.pin.wl] row_pins = [props.bitcell.cell_6t.pin.wl]

View File

@ -6,10 +6,7 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer, parameter, drc
from tech import cell_properties as props from tech import cell_properties as props
import logical_effort
import bitcell_base import bitcell_base
@ -29,33 +26,23 @@ class bitcell_1rw_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd, props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd] props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"] "INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar'] storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"])
def __init__(self, name=""): def __init__(self, name):
# Ignore the name argument super().__init__(name)
bitcell_base.bitcell_base.__init__(self, "cell_1rw_1r")
debug.info(2, "Create bitcell with 1RW and 1R Port") debug.info(2, "Create bitcell with 1RW and 1R Port")
self.width = bitcell_1rw_1r.width
self.height = bitcell_1rw_1r.height
self.pin_map = bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
pin_names = bitcell_1rw_1r.pin_names pin_names = self.pin_names
self.bl_names = [pin_names[0], pin_names[2]] self.bl_names = [pin_names[0], pin_names[2]]
self.br_names = [pin_names[1], pin_names[3]] self.br_names = [pin_names[1], pin_names[3]]
self.wl_names = [pin_names[4], pin_names[5]] self.wl_names = [pin_names[4], pin_names[5]]
def get_bitcell_pins(self, col, row): def get_bitcell_pins(self, col, row):
""" """
Creates a list of connections in the bitcell, Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array indexed by column and row, for instance use in bitcell_array
""" """

View File

@ -6,8 +6,6 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props from tech import cell_properties as props
import bitcell_base import bitcell_base
@ -31,28 +29,18 @@ class bitcell_1w_1r(bitcell_base.bitcell_base):
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"] "INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar'] storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"])
def __init__(self, name=""): def __init__(self, name):
# Ignore the name argument super().__init__(name)
bitcell_base.bitcell_base.__init__(self, "cell_1w_1r")
debug.info(2, "Create bitcell with 1W and 1R Port") debug.info(2, "Create bitcell with 1W and 1R Port")
self.width = bitcell_1w_1r.width
self.height = bitcell_1w_1r.height
self.pin_map = bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
pin_names = bitcell_1w_1r.pin_names pin_names = self.pin_names
self.bl_names = [pin_names[0], pin_names[2]] self.bl_names = [pin_names[0], pin_names[2]]
self.br_names = [pin_names[1], pin_names[3]] self.br_names = [pin_names[1], pin_names[3]]
self.wl_names = [pin_names[4], pin_names[5]] self.wl_names = [pin_names[4], pin_names[5]]
def get_bitcell_pins(self, col, row): def get_bitcell_pins(self, col, row):
""" """
Creates a list of connections in the bitcell, Creates a list of connections in the bitcell,

View File

@ -8,18 +8,30 @@
import debug import debug
import design import design
import utils
from globals import OPTS from globals import OPTS
import logical_effort import logical_effort
from tech import parameter, drc, layer from tech import GDS, parameter, drc, layer
class bitcell_base(design.design): class bitcell_base(design.design):
""" """
Base bitcell parameters to be over-riden. Base bitcell parameters to be over-riden.
""" """
def __init__(self, name): cell_size_layer = "boundary"
def __init__(self, name, hard_cell=True):
design.design.__init__(self, name) design.design.__init__(self, name)
if hard_cell:
(self.width, self.height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
self.pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.add_pin_types(self.type_list)
def get_stage_effort(self, load): def get_stage_effort(self, load):
parasitic_delay = 1 parasitic_delay = 1
# This accounts for bitline being drained # This accounts for bitline being drained
@ -49,13 +61,13 @@ class bitcell_base(design.design):
def input_load(self): def input_load(self):
""" Return the relative capacitance of the access transistor gates """ """ Return the relative capacitance of the access transistor gates """
# FIXME: This applies to bitline capacitances as well. # FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. # FIXME: sizing is not accurate with the handmade cell.
# Change once cell widths are fixed. # Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"] access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin return 2 * access_tx_cin
def get_wl_cin(self): def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
# This is a handmade cell so the value must be entered # This is a handmade cell so the value must be entered
@ -82,7 +94,7 @@ class bitcell_base(design.design):
def get_storage_net_offset(self): def get_storage_net_offset(self):
""" """
Gets the location of the storage net labels to add top level Gets the location of the storage net labels to add top level
labels for pex simulation. labels for pex simulation.
""" """
# If we generated the bitcell, we already know where Q and Q_bar are # If we generated the bitcell, we already know where Q and Q_bar are
@ -92,7 +104,7 @@ class bitcell_base(design.design):
for text in self.gds.getTexts(layer["m1"]): for text in self.gds.getTexts(layer["m1"]):
if self.storage_nets[i] == text.textString.rstrip('\x00'): if self.storage_nets[i] == text.textString.rstrip('\x00'):
self.storage_net_offsets.append(text.coordinates[0]) self.storage_net_offsets.append(text.coordinates[0])
for i in range(len(self.storage_net_offsets)): for i in range(len(self.storage_net_offsets)):
self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]]) self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]])
@ -116,7 +128,7 @@ class bitcell_base(design.design):
if bl_names[i] == text.textString.rstrip('\x00'): if bl_names[i] == text.textString.rstrip('\x00'):
self.bl_offsets.append(text.coordinates[0]) self.bl_offsets.append(text.coordinates[0])
found_bl.append(bl_names[i]) found_bl.append(bl_names[i])
continue continue
for i in range(len(br_names)): for i in range(len(br_names)):
@ -131,16 +143,16 @@ class bitcell_base(design.design):
self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]]) self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]])
for i in range(len(self.br_offsets)): for i in range(len(self.br_offsets)):
self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]]) self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]])
return(self.bl_offsets, self.br_offsets, found_bl, found_br) return(self.bl_offsets, self.br_offsets, found_bl, found_br)
def get_normalized_storage_nets_offset(self): def get_normalized_storage_nets_offset(self):
""" """
Convert storage net offset to be relative to the bottom left corner Convert storage net offset to be relative to the bottom left corner
of the bitcell. This is useful for making sense of offsets outside of the bitcell. This is useful for making sense of offsets outside
of the bitcell. of the bitcell.
""" """
if OPTS.bitcell is not "pbitcell": if OPTS.bitcell is not "pbitcell":
normalized_storage_net_offset = self.get_storage_net_offset() normalized_storage_net_offset = self.get_storage_net_offset()

View File

@ -6,39 +6,25 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props from tech import cell_properties as props
import bitcell_base import bitcell_base
class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
""" """
todo""" Column end cap cell.
"""
pin_names = [props.bitcell.cell_1rw1r.pin.bl0, pin_names = [props.bitcell.cell_1rw1r.pin.bl0,
props.bitcell.cell_1rw1r.pin.br0, props.bitcell.cell_1rw1r.pin.br0,
props.bitcell.cell_1rw1r.pin.bl1, props.bitcell.cell_1rw1r.pin.bl1,
props.bitcell.cell_1rw1r.pin.br1, props.bitcell.cell_1rw1r.pin.br1,
props.bitcell.cell_1rw1r.pin.vdd] props.bitcell.cell_1rw1r.pin.vdd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"POWER", "GROUND"] "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r", def __init__(self, name="col_cap_cell_1rw_1r"):
GDS["unit"], bitcell_base.bitcell_base.__init__(self, name)
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"col_cap_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r")
debug.info(2, "Create col_cap bitcell 1rw+1r object") debug.info(2, "Create col_cap bitcell 1rw+1r object")
self.width = col_cap_bitcell_1rw_1r.width
self.height = col_cap_bitcell_1rw_1r.height
self.pin_map = col_cap_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.no_instances = True self.no_instances = True

View File

@ -6,8 +6,6 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props from tech import cell_properties as props
import bitcell_base import bitcell_base
@ -24,19 +22,10 @@ class dummy_bitcell(bitcell_base.bitcell_base):
props.bitcell.cell_6t.pin.wl, props.bitcell.cell_6t.pin.wl,
props.bitcell.cell_6t.pin.vdd, props.bitcell.cell_6t.pin.vdd,
props.bitcell.cell_6t.pin.gnd] props.bitcell.cell_6t.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_6t", def __init__(self, name):
GDS["unit"], super().__init__(name)
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "dummy_cell_6t")
debug.info(2, "Create dummy bitcell") debug.info(2, "Create dummy bitcell")
self.width = dummy_bitcell.width
self.height = dummy_bitcell.height
self.pin_map = dummy_bitcell.pin_map

View File

@ -6,8 +6,6 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props from tech import cell_properties as props
import bitcell_base import bitcell_base
@ -27,23 +25,11 @@ class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd, props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd] props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"] "INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""): def __init__(self, name):
# Ignore the name argument super().__init__(name)
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r")
debug.info(2, "Create dummy bitcell 1rw+1r object") debug.info(2, "Create dummy bitcell 1rw+1r object")
self.width = dummy_bitcell_1rw_1r.width
self.height = dummy_bitcell_1rw_1r.height
self.pin_map = dummy_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)

View File

@ -6,8 +6,6 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props from tech import cell_properties as props
import bitcell_base import bitcell_base
@ -29,21 +27,11 @@ class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1w1r.pin.gnd] props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"] "INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1w_1r",
GDS["unit"])
def __init__(self, name=""): def __init__(self, name):
# Ignore the name argument super().__init__(name)
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r")
debug.info(2, "Create dummy bitcell 1w+1r object") debug.info(2, "Create dummy bitcell 1w+1r object")
self.width = dummy_bitcell_1w_1r.width
self.height = dummy_bitcell_1w_1r.height
self.pin_map = dummy_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)

View File

@ -7,85 +7,86 @@
# #
import debug import debug
import design import design
from tech import drc, spice,parameter
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
class dummy_pbitcell(design.design): class dummy_pbitcell(design.design):
""" """
Creates a replica bitcell using pbitcell Creates a replica bitcell using pbitcell
""" """
def __init__(self, name): def __init__(self, name, cell_name=None):
self.num_rw_ports = OPTS.num_rw_ports self.num_rw_ports = OPTS.num_rw_ports
self.num_w_ports = OPTS.num_w_ports self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports self.num_r_ports = OPTS.num_r_ports
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "create a dummy bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, debug.info(1, "create a dummy bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
self.num_w_ports, self.num_w_ports,
self.num_r_ports)) self.num_r_ports))
self.create_netlist() self.create_netlist()
self.create_layout() self.create_layout()
self.add_boundary() self.add_boundary()
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_modules() self.create_modules()
def create_layout(self): def create_layout(self):
self.place_pbitcell() self.place_pbitcell()
self.route_rbc_connections() self.route_rbc_connections()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
for port in range(self.total_ports): for port in range(self.total_ports):
self.add_pin("bl{}".format(port)) self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port)) self.add_pin("br{}".format(port))
for port in range(self.total_ports): for port in range(self.total_ports):
self.add_pin("wl{}".format(port)) self.add_pin("wl{}".format(port))
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def add_modules(self): def add_modules(self):
self.prbc = factory.create(module_type="pbitcell",dummy_bitcell=True) self.prbc = factory.create(module_type="pbitcell",
dummy_bitcell=True)
self.add_mod(self.prbc) self.add_mod(self.prbc)
self.height = self.prbc.height self.height = self.prbc.height
self.width = self.prbc.width self.width = self.prbc.width
def create_modules(self): def create_modules(self):
self.prbc_inst = self.add_inst(name="pbitcell", self.prbc_inst = self.add_inst(name="pbitcell",
mod=self.prbc) mod=self.prbc)
temp = [] temp = []
for port in range(self.total_ports): for port in range(self.total_ports):
temp.append("bl{}".format(port)) temp.append("bl{}".format(port))
temp.append("br{}".format(port)) temp.append("br{}".format(port))
for port in range(self.total_ports): for port in range(self.total_ports):
temp.append("wl{}".format(port)) temp.append("wl{}".format(port))
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place_pbitcell(self): def place_pbitcell(self):
self.prbc_inst.place(offset=vector(0,0)) self.prbc_inst.place(offset=vector(0, 0))
def route_rbc_connections(self): def route_rbc_connections(self):
for port in range(self.total_ports): for port in range(self.total_ports):
self.copy_layout_pin(self.prbc_inst, "bl{}".format(port)) self.copy_layout_pin(self.prbc_inst, "bl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "br{}".format(port)) self.copy_layout_pin(self.prbc_inst, "br{}".format(port))
for port in range(self.total_ports): for port in range(self.total_ports):
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port)) self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "vdd") self.copy_layout_pin(self.prbc_inst, "vdd")
self.copy_layout_pin(self.prbc_inst, "gnd") self.copy_layout_pin(self.prbc_inst, "gnd")
def get_wl_cin(self): def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
#This module is made using a pbitcell. Get the cin from that module #This module is made using a pbitcell. Get the cin from that module

View File

@ -26,11 +26,11 @@ class pbitcell(bitcell_base.bitcell_base):
self.num_w_ports = OPTS.num_w_ports self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports self.num_r_ports = OPTS.num_r_ports
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
self.replica_bitcell = replica_bitcell self.replica_bitcell = replica_bitcell
self.dummy_bitcell = dummy_bitcell self.dummy_bitcell = dummy_bitcell
bitcell_base.bitcell_base.__init__(self, name) bitcell_base.bitcell_base.__init__(self, name, hard_cell=False)
fmt_str = "{0} rw ports, {1} w ports and {2} r ports" fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
info_string = fmt_str.format(self.num_rw_ports, info_string = fmt_str.format(self.num_rw_ports,
self.num_w_ports, self.num_w_ports,
@ -295,7 +295,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.width = -2 * self.leftmost_xpos self.width = -2 * self.leftmost_xpos
self.height = self.topmost_ypos - self.botmost_ypos self.height = self.topmost_ypos - self.botmost_ypos
self.center_ypos = 0.5 * (self.topmost_ypos + self.botmost_ypos) self.center_ypos = 0.5 * (self.topmost_ypos + self.botmost_ypos)
def create_storage(self): def create_storage(self):
""" """
Creates the crossed coupled inverters that act Creates the crossed coupled inverters that act
@ -322,7 +322,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.connect_inst(["vdd", self.Q, self.Q_bar, "vdd"]) self.connect_inst(["vdd", self.Q, self.Q_bar, "vdd"])
def place_storage(self): def place_storage(self):
""" """
Places the transistors for the crossed Places the transistors for the crossed
coupled inverters in the bitcell coupled inverters in the bitcell
""" """
@ -406,7 +406,7 @@ class pbitcell(bitcell_base.bitcell_base):
contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \ contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \
+ 0.5 * contact.poly.height, + 0.5 * contact.poly.height,
self.cross_couple_upper_ypos) self.cross_couple_upper_ypos)
contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \ contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \
- 0.5*contact.poly.height, - 0.5*contact.poly.height,
self.cross_couple_lower_ypos) self.cross_couple_lower_ypos)
@ -421,8 +421,8 @@ class pbitcell(bitcell_base.bitcell_base):
offset=self.gnd_position, offset=self.gnd_position,
width=self.width) width=self.width)
self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H")) self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H"))
vdd_ypos = self.inverter_nmos_ypos \ vdd_ypos = self.inverter_nmos_ypos \
+ self.inverter_nmos.active_height \ + self.inverter_nmos.active_height \
+ self.inverter_gap \ + self.inverter_gap \
@ -433,7 +433,7 @@ class pbitcell(bitcell_base.bitcell_base):
offset=self.vdd_position, offset=self.vdd_position,
width=self.width) width=self.width)
self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H")) self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H"))
def create_readwrite_ports(self): def create_readwrite_ports(self):
""" """
Creates read/write ports to the bit cell. A differential Creates read/write ports to the bit cell. A differential
@ -461,7 +461,7 @@ class pbitcell(bitcell_base.bitcell_base):
if self.dummy_bitcell: if self.dummy_bitcell:
bl_name += "_noconn" bl_name += "_noconn"
br_name += "_noconn" br_name += "_noconn"
# add read/write transistors # add read/write transistors
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
mod=self.readwrite_nmos) mod=self.readwrite_nmos)
@ -662,7 +662,7 @@ class pbitcell(bitcell_base.bitcell_base):
if self.dummy_bitcell: if self.dummy_bitcell:
bl_name += "_noconn" bl_name += "_noconn"
br_name += "_noconn" br_name += "_noconn"
# add read-access transistors # add read-access transistors
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k),
mod=self.read_nmos) mod=self.read_nmos)
@ -897,7 +897,7 @@ class pbitcell(bitcell_base.bitcell_base):
[self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right])
def route_readwrite_access(self): def route_readwrite_access(self):
""" """
Routes read/write transistors to the storage Routes read/write transistors to the storage
component of the bitcell component of the bitcell
""" """
@ -917,7 +917,7 @@ class pbitcell(bitcell_base.bitcell_base):
[self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) [self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
def route_write_access(self): def route_write_access(self):
""" """
Routes read/write transistors to the storage Routes read/write transistors to the storage
component of the bitcell component of the bitcell
""" """
@ -937,7 +937,7 @@ class pbitcell(bitcell_base.bitcell_base):
[self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) [self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
def route_read_access(self): def route_read_access(self):
""" """
Routes read access transistors to the storage Routes read access transistors to the storage
component of the bitcell component of the bitcell
""" """
@ -1016,7 +1016,7 @@ class pbitcell(bitcell_base.bitcell_base):
offset=offset, offset=offset,
width=well_width, width=well_width,
height=well_height) height=well_height)
# extend nwell to encompass inverter_pmos # extend nwell to encompass inverter_pmos
# calculate offset of the left pmos well # calculate offset of the left pmos well
if "nwell" in layer: if "nwell" in layer:
@ -1024,7 +1024,7 @@ class pbitcell(bitcell_base.bitcell_base):
- self.nwell_enclose_active - self.nwell_enclose_active
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \
+ self.inverter_gap - self.nwell_enclose_active + self.inverter_gap - self.nwell_enclose_active
# calculate width of the two combined nwells # calculate width of the two combined nwells
# calculate height to encompass nimplant connected to vdd # calculate height to encompass nimplant connected to vdd
well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
@ -1099,18 +1099,18 @@ class pbitcell(bitcell_base.bitcell_base):
Q_bar_pos = self.inverter_pmos_right.get_pin("S").center() Q_bar_pos = self.inverter_pmos_right.get_pin("S").center()
vdd_pos = self.inverter_pmos_right.get_pin("D").center() vdd_pos = self.inverter_pmos_right.get_pin("D").center()
self.add_path("m1", [Q_bar_pos, vdd_pos]) self.add_path("m1", [Q_bar_pos, vdd_pos])
def get_storage_net_names(self): def get_storage_net_names(self):
""" """
Returns names of storage nodes in bitcell in Returns names of storage nodes in bitcell in
[non-inverting, inverting] format. [non-inverting, inverting] format.
""" """
return self.storage_nets return self.storage_nets
def get_bl_name(self, port=0): def get_bl_name(self, port=0):
"""Get bl name by port""" """Get bl name by port"""
return "bl{}".format(port) return "bl{}".format(port)
def get_br_name(self, port=0): def get_br_name(self, port=0):
"""Get bl name by port""" """Get bl name by port"""
return "br{}".format(port) return "br{}".format(port)
@ -1119,7 +1119,7 @@ class pbitcell(bitcell_base.bitcell_base):
"""Get wl name by port""" """Get wl name by port"""
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port) return "wl{}".format(port)
def get_stage_effort(self, load): def get_stage_effort(self, load):
parasitic_delay = 1 parasitic_delay = 1
# This accounts for bitline being drained thought the access # This accounts for bitline being drained thought the access
@ -1128,7 +1128,7 @@ class pbitcell(bitcell_base.bitcell_base):
# Assumes always a minimum sizes inverter. Could be # Assumes always a minimum sizes inverter. Could be
# specified in the tech.py file. # specified in the tech.py file.
cin = 3 cin = 3
# Internal loads due to port configs are halved. # Internal loads due to port configs are halved.
# This is to account for the size already being halved # This is to account for the size already being halved
# for stacked TXs, but internal loads do not see this size # for stacked TXs, but internal loads do not see this size
@ -1144,10 +1144,10 @@ class pbitcell(bitcell_base.bitcell_base):
load + read_port_load, load + read_port_load,
parasitic_delay, parasitic_delay,
False) False)
def input_load(self): def input_load(self):
""" Return the relative capacitance of the access transistor gates """ """ Return the relative capacitance of the access transistor gates """
# FIXME: This applies to bitline capacitances as well. # FIXME: This applies to bitline capacitances as well.
# pbitcell uses the different sizing for the port access tx's. Not accounted for in this model. # pbitcell uses the different sizing for the port access tx's. Not accounted for in this model.
access_tx_cin = self.readwrite_nmos.get_cin() access_tx_cin = self.readwrite_nmos.get_cin()
@ -1155,10 +1155,10 @@ class pbitcell(bitcell_base.bitcell_base):
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph for pbitcell. Only readwrite and read ports.""" """Adds edges to graph for pbitcell. Only readwrite and read ports."""
if self.dummy_bitcell: if self.dummy_bitcell:
return return
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
# Edges added wl->bl, wl->br for every port except write ports # Edges added wl->bl, wl->br for every port except write ports
rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names) rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names)

View File

@ -5,15 +5,14 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import bitcell_base
from tech import GDS,layer,drc,parameter,cell_properties
from tech import cell_properties as props from tech import cell_properties as props
from tech import parameter, drc
import logical_effort
from globals import OPTS
class replica_bitcell(design.design): class replica_bitcell(bitcell_base.bitcell_base):
""" """
A single bit cell (6T, 8T, etc.) A single bit cell (6T, 8T, etc.)
This module implements the single memory cell used in the design. It This module implements the single memory cell used in the design. It
@ -26,46 +25,33 @@ class replica_bitcell(design.design):
props.bitcell.cell_6t.pin.vdd, props.bitcell.cell_6t.pin.vdd,
props.bitcell.cell_6t.pin.gnd] props.bitcell.cell_6t.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"])
else:
(width,height) = (0,0)
pin_map = []
def __init__(self, name=""): def __init__(self, name):
# Ignore the name argument super().__init__(name)
design.design.__init__(self, "replica_cell_6t")
debug.info(2, "Create replica bitcell object") debug.info(2, "Create replica bitcell object")
self.width = replica_bitcell.width
self.height = replica_bitcell.height
self.pin_map = replica_bitcell.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load): def get_stage_effort(self, load):
parasitic_delay = 1 parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node size = 0.5 # This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load read_port_load = 0.5 # min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
def input_load(self): def input_load(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well. # FIXME: This applies to bitline capacitances as well.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2*access_tx_cin return 2 * access_tx_cin
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage.""" """Bitcell power in nW. Only characterizes leakage."""
from tech import spice from tech import spice
leakage = spice["bitcell_leakage"] leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary dynamic = 0 # FIXME
total_power = self.return_power(dynamic, leakage) total_power = self.return_power(dynamic, leakage)
return total_power return total_power
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function.""" """Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets) self.add_graph_edges(graph, port_nets)

View File

@ -5,13 +5,14 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import bitcell_base
from tech import GDS,layer,drc,parameter
from tech import cell_properties as props from tech import cell_properties as props
from tech import parameter, drc
import logical_effort
class replica_bitcell_1rw_1r(design.design):
class replica_bitcell_1rw_1r(bitcell_base.bitcell_base):
""" """
A single bit cell which is forced to store a 0. A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It This module implements the single memory cell used in the design. It
@ -26,42 +27,33 @@ class replica_bitcell_1rw_1r(design.design):
props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd, props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd] props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] def __init__(self, name):
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"]) super().__init__(name)
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_1rw_1r")
debug.info(2, "Create replica bitcell 1rw+1r object") debug.info(2, "Create replica bitcell 1rw+1r object")
self.width = replica_bitcell_1rw_1r.width
self.height = replica_bitcell_1rw_1r.height
self.pin_map = replica_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load): def get_stage_effort(self, load):
parasitic_delay = 1 parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node size = 0.5 # This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load read_port_load = 0.5 # min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
def input_load(self): def input_load(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well. # FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2*access_tx_cin return 2 * access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex """Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function.""" to use the add_graph_edges function."""
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
pins = props.bitcell.cell_1rw1r.pin pins = props.bitcell.cell_1rw1r.pin
#Edges hardcoded here. Essentially wl->bl/br for both ports. # Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges # Port 0 edges
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.bl0], self) graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.bl0], self)
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self) graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self)

View File

@ -5,13 +5,14 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import bitcell_base
from tech import GDS,layer,drc,parameter
from tech import cell_properties as props from tech import cell_properties as props
from tech import parameter, drc
import logical_effort
class replica_bitcell_1w_1r(design.design):
class replica_bitcell_1w_1r(bitcell_base.bitcell_base):
""" """
A single bit cell which is forced to store a 0. A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It This module implements the single memory cell used in the design. It
@ -26,43 +27,34 @@ class replica_bitcell_1w_1r(design.design):
props.bitcell.cell_1w1r.pin.wl1, props.bitcell.cell_1w1r.pin.wl1,
props.bitcell.cell_1w1r.pin.vdd, props.bitcell.cell_1w1r.pin.vdd,
props.bitcell.cell_1w1r.pin.gnd] props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] def __init__(self, name):
(width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"]) super().__init__(name)
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_1w_1r")
debug.info(2, "Create replica bitcell 1w+1r object") debug.info(2, "Create replica bitcell 1w+1r object")
self.width = replica_bitcell_1w_1r.width
self.height = replica_bitcell_1w_1r.height
self.pin_map = replica_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load): def get_stage_effort(self, load):
parasitic_delay = 1 parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node size = 0.5 # This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load read_port_load = 0.5 # min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
def input_load(self): def input_load(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well. # FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2*access_tx_cin return 2 * access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex """Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function.""" to use the add_graph_edges function."""
debug.info(1,'Adding edges for {}'.format(inst_name)) debug.info(1, 'Adding edges for {}'.format(inst_name))
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
pins = props.bitcell.cell_1w1r.pin pins = props.bitcell.cell_1w1r.pin
#Edges hardcoded here. Essentially wl->bl/br for the read port. # Edges hardcoded here. Essentially wl->bl/br for the read port.
# Port 1 edges # Port 1 edges
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self) graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self)
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self) graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)

View File

@ -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")

View File

@ -6,39 +6,22 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props from tech import cell_properties as props
import bitcell_base import bitcell_base
class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
""" """
A single bit cell which is forced to store a 0. Row end cap cell.
This module implements the single memory cell used in the design. It """
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
pin_names = [props.bitcell.cell_1rw1r.pin.wl0, pin_names = [props.bitcell.cell_1rw1r.pin.wl0,
props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.gnd] props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["INPUT", "INPUT", "GROUND"] type_list = ["INPUT", "INPUT", "GROUND"]
(width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"row_cap_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""): def __init__(self, name="row_cap_cell_1rw_1r"):
# Ignore the name argument bitcell_base.bitcell_base.__init__(self, name)
bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r")
debug.info(2, "Create row_cap bitcell 1rw+1r object") debug.info(2, "Create row_cap bitcell 1rw+1r object")
self.width = row_cap_bitcell_1rw_1r.width
self.height = row_cap_bitcell_1rw_1r.height
self.pin_map = row_cap_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.no_instances = True self.no_instances = True

View File

@ -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:

View File

@ -11,4 +11,4 @@ from enum import Enum
class bit_polarity(Enum): class bit_polarity(Enum):
NONINVERTING = 0 NONINVERTING = 0
INVERTING = 1 INVERTING = 1

View File

@ -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

View File

@ -21,13 +21,24 @@ class functional(simulation):
for successful SRAM operation. for successful SRAM operation.
""" """
def __init__(self, sram, spfile, corner, cycles=15): def __init__(self, sram, spfile, corner=None, cycles=15, period=None, output_path=None):
super().__init__(sram, spfile, corner) super().__init__(sram, spfile, corner)
# Seed the characterizer with a constant seed for unit tests # Seed the characterizer with a constant seed for unit tests
if OPTS.is_unit_test: if OPTS.is_unit_test:
random.seed(12345) random.seed(12345)
if not corner:
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
if period:
self.period = period
if not output_path:
self.output_path = OPTS.openram_temp
else:
self.output_path = output_path
if self.write_size: if self.write_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size)) self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else: else:
@ -50,7 +61,7 @@ class functional(simulation):
self.set_internal_spice_names() self.set_internal_spice_names()
self.q_name, self.qbar_name = self.get_bit_name() self.q_name, self.qbar_name = self.get_bit_name()
debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name)) debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name))
# Number of checks can be changed # Number of checks can be changed
self.num_cycles = cycles self.num_cycles = cycles
# This is to have ordered keys for random selection # This is to have ordered keys for random selection
@ -58,21 +69,20 @@ class functional(simulation):
self.read_check = [] self.read_check = []
self.read_results = [] self.read_results = []
def run(self, feasible_period=None):
if feasible_period: # period defaults to tech.py feasible period otherwise.
self.period = feasible_period
# Generate a random sequence of reads and writes # Generate a random sequence of reads and writes
self.create_random_memory_sequence() self.create_random_memory_sequence()
# Run SPICE simulation # Write SPICE simulation
self.write_functional_stimulus() self.write_functional_stimulus()
self.stim.run_sim()
def run(self):
self.stim.run_sim(self.stim_sp)
# read dout values from SPICE simulation. If the values do not fall within the noise margins, return the error. # read dout values from SPICE simulation. If the values do not fall within the noise margins, return the error.
(success, error) = self.read_stim_results() (success, error) = self.read_stim_results()
if not success: if not success:
return (0, error) return (0, error)
# Check read values with written values. If the values do not match, return an error. # Check read values with written values. If the values do not match, return an error.
return self.check_stim_results() return self.check_stim_results()
@ -94,7 +104,7 @@ class functional(simulation):
len(val), len(val),
port, port,
name)) name))
def create_random_memory_sequence(self): def create_random_memory_sequence(self):
if self.write_size: if self.write_size:
rw_ops = ["noop", "write", "partial_write", "read"] rw_ops = ["noop", "write", "partial_write", "read"]
@ -123,7 +133,7 @@ class functional(simulation):
self.cycle_times.append(self.t_current) self.cycle_times.append(self.t_current)
self.t_current += self.period self.t_current += self.period
self.check_lengths() self.check_lengths()
# 2. Read at least once. For multiport, it is important that one # 2. Read at least once. For multiport, it is important that one
# read cycle uses all RW and R port to read from the same # read cycle uses all RW and R port to read from the same
# address simultaniously. This will test the viablilty of the # address simultaniously. This will test the viablilty of the
@ -138,7 +148,7 @@ class functional(simulation):
self.cycle_times.append(self.t_current) self.cycle_times.append(self.t_current)
self.t_current += self.period self.t_current += self.period
self.check_lengths() self.check_lengths()
# 3. Perform a random sequence of writes and reads on random # 3. Perform a random sequence of writes and reads on random
# ports, using random addresses and random words and random # ports, using random addresses and random words and random
# write masks (if applicable) # write masks (if applicable)
@ -151,7 +161,7 @@ class functional(simulation):
op = random.choice(w_ops) op = random.choice(w_ops)
else: else:
op = random.choice(r_ops) op = random.choice(r_ops)
if op == "noop": if op == "noop":
self.add_noop_one_port(port) self.add_noop_one_port(port)
elif op == "write": elif op == "write":
@ -191,10 +201,10 @@ class functional(simulation):
comment = self.gen_cycle_comment("read", word, addr, "0" * self.num_wmasks, port, self.t_current) comment = self.gen_cycle_comment("read", word, addr, "0" * self.num_wmasks, port, self.t_current)
self.add_read_one_port(comment, addr, port) self.add_read_one_port(comment, addr, port)
self.add_read_check(word, port) self.add_read_check(word, port)
self.cycle_times.append(self.t_current) self.cycle_times.append(self.t_current)
self.t_current += self.period self.t_current += self.period
# Last cycle idle needed to correctly measure the value on the second to last clock edge # Last cycle idle needed to correctly measure the value on the second to last clock edge
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current) comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current)
self.add_noop_all_ports(comment) self.add_noop_all_ports(comment)
@ -203,7 +213,7 @@ class functional(simulation):
""" Create the masked data word """ """ Create the masked data word """
# Start with the new word # Start with the new word
new_word = word new_word = word
# When the write mask's bits are 0, the old data values should appear in the new word # When the write mask's bits are 0, the old data values should appear in the new word
# as to not overwrite the old values # as to not overwrite the old values
for bit in range(len(wmask)): for bit in range(len(wmask)):
@ -211,9 +221,9 @@ class functional(simulation):
lower = bit * self.write_size lower = bit * self.write_size
upper = lower + self.write_size - 1 upper = lower + self.write_size - 1
new_word = new_word[:lower] + old_word[lower:upper + 1] + new_word[upper + 1:] new_word = new_word[:lower] + old_word[lower:upper + 1] + new_word[upper + 1:]
return new_word return new_word
def add_read_check(self, word, port): def add_read_check(self, word, port):
""" Add to the check array to ensure a read works. """ """ Add to the check array to ensure a read works. """
try: try:
@ -222,7 +232,7 @@ class functional(simulation):
self.check = 0 self.check = 0
self.read_check.append([word, "{0}{1}".format(self.dout_name, port), self.t_current + self.period, self.check]) self.read_check.append([word, "{0}{1}".format(self.dout_name, port), self.t_current + self.period, self.check])
self.check += 1 self.check += 1
def read_stim_results(self): def read_stim_results(self):
# Extract dout values from spice timing.lis # Extract dout values from spice timing.lis
for (word, dout_port, eo_period, check) in self.read_check: for (word, dout_port, eo_period, check) in self.read_check:
@ -247,12 +257,12 @@ class functional(simulation):
bit, bit,
value, value,
eo_period) eo_period)
return (0, error) return (0, error)
self.read_results.append([sp_read_value, dout_port, eo_period, check]) self.read_results.append([sp_read_value, dout_port, eo_period, check])
return (1, "SUCCESS") return (1, "SUCCESS")
def check_stim_results(self): def check_stim_results(self):
for i in range(len(self.read_check)): for i in range(len(self.read_check)):
if self.read_check[i][0] != self.read_results[i][0]: if self.read_check[i][0] != self.read_results[i][0]:
@ -307,14 +317,14 @@ class functional(simulation):
random_value = random.randint(0, ((2 ** (self.addr_size - 1) - 1)) + (self.num_spare_rows * self.words_per_row)) random_value = random.randint(0, ((2 ** (self.addr_size - 1) - 1)) + (self.num_spare_rows * self.words_per_row))
addr_bits = self.convert_to_bin(random_value, True) addr_bits = self.convert_to_bin(random_value, True)
return addr_bits return addr_bits
def get_data(self): def get_data(self):
""" Gets an available address and corresponding word. """ """ Gets an available address and corresponding word. """
# Used for write masks since they should be writing to previously written addresses # Used for write masks since they should be writing to previously written addresses
addr = random.choice(list(self.stored_words.keys())) addr = random.choice(list(self.stored_words.keys()))
word = self.stored_words[addr] word = self.stored_words[addr]
return (addr, word) return (addr, word)
def convert_to_bin(self, value, is_addr): def convert_to_bin(self, value, is_addr):
""" Converts addr & word to usable binary values. """ """ Converts addr & word to usable binary values. """
new_value = str.replace(bin(value), "0b", "") new_value = str.replace(bin(value), "0b", "")
@ -324,13 +334,14 @@ class functional(simulation):
expected_value = self.word_size + self.num_spare_cols expected_value = self.word_size + self.num_spare_cols
for i in range(expected_value - len(new_value)): for i in range(expected_value - len(new_value)):
new_value = "0" + new_value new_value = "0" + new_value
# print("Binary Conversion: {} to {}".format(value, new_value)) # print("Binary Conversion: {} to {}".format(value, new_value))
return new_value return new_value
def write_functional_stimulus(self): def write_functional_stimulus(self):
""" Writes SPICE stimulus. """ """ Writes SPICE stimulus. """
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) self.stim_sp = "functional_stim.sp"
temp_stim = "{0}/{1}".format(self.output_path, self.stim_sp)
self.sf = open(temp_stim, "w") self.sf = open(temp_stim, "w")
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period)) self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period))
self.stim = stimuli(self.sf, self.corner) self.stim = stimuli(self.sf, self.corner)
@ -341,7 +352,7 @@ class functional(simulation):
# Write Vdd/Gnd statements # Write Vdd/Gnd statements
self.sf.write("\n* Global Power Supplies\n") self.sf.write("\n* Global Power Supplies\n")
self.stim.write_supply() self.stim.write_supply()
# Instantiate the SRAM # Instantiate the SRAM
self.sf.write("\n* Instantiation of the SRAM\n") self.sf.write("\n* Instantiation of the SRAM\n")
self.stim.inst_model(pins=self.pins, self.stim.inst_model(pins=self.pins,
@ -361,19 +372,19 @@ class functional(simulation):
self.sf.write("* s_en: {}\n".format(self.sen_name)) self.sf.write("* s_en: {}\n".format(self.sen_name))
self.sf.write("* q: {}\n".format(self.q_name)) self.sf.write("* q: {}\n".format(self.q_name))
self.sf.write("* qbar: {}\n".format(self.qbar_name)) self.sf.write("* qbar: {}\n".format(self.qbar_name))
# Write debug comments to stim file # Write debug comments to stim file
self.sf.write("\n\n* Sequence of operations\n") self.sf.write("\n\n* Sequence of operations\n")
for comment in self.fn_cycle_comments: for comment in self.fn_cycle_comments:
self.sf.write("*{}\n".format(comment)) self.sf.write("*{}\n".format(comment))
# Generate data input bits # Generate data input bits
self.sf.write("\n* Generation of data and address signals\n") self.sf.write("\n* Generation of data and address signals\n")
for port in self.write_ports: for port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols): for bit in range(self.word_size + self.num_spare_cols):
sig_name="{0}{1}_{2} ".format(self.din_name, port, bit) sig_name="{0}{1}_{2} ".format(self.din_name, port, bit)
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05)
# Generate address bits # Generate address bits
for port in self.all_ports: for port in self.all_ports:
for bit in range(self.addr_size): for bit in range(self.addr_size):
@ -384,7 +395,7 @@ class functional(simulation):
self.sf.write("\n * Generation of control signals\n") self.sf.write("\n * Generation of control signals\n")
for port in self.all_ports: for port in self.all_ports:
self.stim.gen_pwl("CSB{}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("CSB{}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
for port in self.readwrite_ports: for port in self.readwrite_ports:
self.stim.gen_pwl("WEB{}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("WEB{}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
@ -417,7 +428,7 @@ class functional(simulation):
period=self.period, period=self.period,
t_rise=self.slew, t_rise=self.slew,
t_fall=self.slew) t_fall=self.slew)
# Generate dout value measurements # Generate dout value measurements
self.sf.write("\n * Generation of dout measurements\n") self.sf.write("\n * Generation of dout measurements\n")
for (word, dout_port, eo_period, check) in self.read_check: for (word, dout_port, eo_period, check) in self.read_check:
@ -428,10 +439,10 @@ class functional(simulation):
dout="{0}_{1}".format(dout_port, bit), dout="{0}_{1}".format(dout_port, bit),
t_intital=t_intital, t_intital=t_intital,
t_final=t_final) t_final=t_final)
self.stim.write_control(self.cycle_times[-1] + self.period) self.stim.write_control(self.cycle_times[-1] + self.period)
self.sf.close() self.sf.close()
#FIXME: Similar function to delay.py, refactor this #FIXME: Similar function to delay.py, refactor this
def get_bit_name(self): def get_bit_name(self):
""" Get a bit cell name """ """ Get a bit cell name """
@ -444,4 +455,4 @@ class functional(simulation):
return (q_name, qbar_name) return (q_name, qbar_name)

View File

@ -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()

View File

@ -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']

View File

@ -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)

View File

@ -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

View File

@ -27,23 +27,22 @@ class setup_hold():
self.model_location = OPTS.openram_tech + "sp_lib/dff.sp" self.model_location = OPTS.openram_tech + "sp_lib/dff.sp"
self.period = tech.spice["feasible_period"] self.period = tech.spice["feasible_period"]
debug.info(2,"Feasible period from technology file: {0} ".format(self.period)) debug.info(2, "Feasible period from technology file: {0} ".format(self.period))
self.set_corner(corner) self.set_corner(corner)
def set_corner(self, corner):
def set_corner(self,corner):
""" Set the corner values """ """ Set the corner values """
self.corner = corner self.corner = corner
(self.process, self.vdd_voltage, self.temperature) = corner (self.process, self.vdd_voltage, self.temperature) = corner
self.gnd_voltage = 0 self.gnd_voltage = 0
def write_stimulus(self, mode, target_time, correct_value): def write_stimulus(self, mode, target_time, correct_value):
"""Creates a stimulus file for SRAM setup/hold time calculation""" """Creates a stimulus file for SRAM setup/hold time calculation"""
# creates and opens the stimulus file for writing # creates and opens the stimulus file for writing
temp_stim = OPTS.openram_temp + "stim.sp" self.stim_sp = "sh_stim.sp"
temp_stim = OPTS.openram_temp + self.stim_sp
self.sf = open(temp_stim, "w") self.sf = open(temp_stim, "w")
self.stim = stimuli(self.sf, self.corner) self.stim = stimuli(self.sf, self.corner)
@ -60,11 +59,10 @@ class setup_hold():
self.write_clock() self.write_clock()
self.write_measures(mode=mode, self.write_measures(mode=mode,
correct_value=correct_value) correct_value=correct_value)
self.stim.write_control(4*self.period) self.stim.write_control(4 * self.period)
self.sf.close() self.sf.close()
@ -79,7 +77,6 @@ class setup_hold():
self.sf.write("\n* Global Power Supplies\n") self.sf.write("\n* Global Power Supplies\n")
self.stim.write_supply() self.stim.write_supply()
def write_data(self, mode, target_time, correct_value): def write_data(self, mode, target_time, correct_value):
"""Create the data signals for setup/hold analysis. First period is to """Create the data signals for setup/hold analysis. First period is to
initialize it to the opposite polarity. Second period is used for initialize it to the opposite polarity. Second period is used for
@ -102,24 +99,22 @@ class setup_hold():
data_values=[init_value, start_value, end_value], data_values=[init_value, start_value, end_value],
period=target_time, period=target_time,
slew=self.constrained_input_slew, slew=self.constrained_input_slew,
setup=0) setup=0)
def write_clock(self): def write_clock(self):
""" Create the clock signal for setup/hold analysis. First period initializes the FF """ Create the clock signal for setup/hold analysis. First period initializes the FF
while the second is used for characterization.""" while the second is used for characterization."""
self.stim.gen_pwl(sig_name="clk", self.stim.gen_pwl(sig_name="clk",
# initial clk edge is right after the 0 time to initialize a flop # initial clk edge is right after the 0 time to initialize a flop
# without using .IC on an internal node. # without using .IC on an internal node.
# Return input to value after one period. # Return input to value after one period.
# The second pulse is the characterization one at 2*period # The second pulse is the characterization one at 2*period
clk_times=[0, 0.1*self.period,self.period,2*self.period], clk_times=[0, 0.1 * self.period, self.period, 2 * self.period],
data_values=[0, 1, 0, 1], data_values=[0, 1, 0, 1],
period=2*self.period, period=2 * self.period,
slew=self.constrained_input_slew, slew=self.constrained_input_slew,
setup=0) setup=0)
def write_measures(self, mode, correct_value): def write_measures(self, mode, correct_value):
""" Measure statements for setup/hold with right phases. """ """ Measure statements for setup/hold with right phases. """
@ -139,7 +134,6 @@ class setup_hold():
else: else:
din_rise_or_fall = "RISE" din_rise_or_fall = "RISE"
self.sf.write("\n* Measure statements for pass/fail verification\n") self.sf.write("\n* Measure statements for pass/fail verification\n")
trig_name = "clk" trig_name = "clk"
targ_name = "dout" targ_name = "dout"
@ -152,9 +146,9 @@ class setup_hold():
targ_val=targ_val, targ_val=targ_val,
trig_dir="RISE", trig_dir="RISE",
targ_dir=dout_rise_or_fall, targ_dir=dout_rise_or_fall,
trig_td=1.9*self.period, trig_td=1.9 * self.period,
targ_td=1.9*self.period) targ_td=1.9 * self.period)
targ_name = "data" targ_name = "data"
# Start triggers right after initialize value is returned to normal # Start triggers right after initialize value is returned to normal
# at one period # at one period
@ -165,16 +159,13 @@ class setup_hold():
targ_val=targ_val, targ_val=targ_val,
trig_dir="RISE", trig_dir="RISE",
targ_dir=din_rise_or_fall, targ_dir=din_rise_or_fall,
trig_td=1.2*self.period, trig_td=1.2 * self.period,
targ_td=1.2*self.period) targ_td=1.2 * self.period)
def bidir_search(self, correct_value, mode): def bidir_search(self, correct_value, mode):
""" This will perform a bidirectional search for either setup or hold times. """ This will perform a bidirectional search for either setup or hold times.
It starts with the feasible priod and looks a half period beyond or before it It starts with the feasible priod and looks a half period beyond or before it
depending on whether we are doing setup or hold. depending on whether we are doing setup or hold.
""" """
# NOTE: The feasible bound is always feasible. This is why they are different for setup and hold. # NOTE: The feasible bound is always feasible. This is why they are different for setup and hold.
@ -182,86 +173,85 @@ class setup_hold():
# this time. They are also unbalanced so that the average won't be right on the clock edge in the # this time. They are also unbalanced so that the average won't be right on the clock edge in the
# first iteration. # first iteration.
if mode == "SETUP": if mode == "SETUP":
feasible_bound = 1.25*self.period feasible_bound = 1.25 * self.period
infeasible_bound = 2.5*self.period infeasible_bound = 2.5 * self.period
else: else:
infeasible_bound = 1.5*self.period infeasible_bound = 1.5 * self.period
feasible_bound = 2.75*self.period feasible_bound = 2.75 * self.period
# Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search! # Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search!
self.write_stimulus(mode=mode, self.write_stimulus(mode=mode,
target_time=feasible_bound, target_time=feasible_bound,
correct_value=correct_value) correct_value=correct_value)
self.stim.run_sim() self.stim.run_sim(self.stim_sp)
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay")) ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time")) setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time)) debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time))
if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float: if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float:
debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,ideal_clk_to_q,setuphold_time),2) debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,
ideal_clk_to_q,
setuphold_time),
2)
if mode == "SETUP": # SETUP is clk-din, not din-clk if mode == "SETUP": # SETUP is clk-din, not din-clk
setuphold_time *= -1e9 setuphold_time *= -1e9
else: else:
setuphold_time *= 1e9 setuphold_time *= 1e9
passing_setuphold_time = setuphold_time passing_setuphold_time = setuphold_time
debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode, debug.info(2, "Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
setuphold_time, setuphold_time,
feasible_bound, feasible_bound,
2*self.period)) 2 * self.period))
#raw_input("Press Enter to continue...") #raw_input("Press Enter to continue...")
while True: while True:
target_time = (feasible_bound + infeasible_bound)/2 target_time = (feasible_bound + infeasible_bound) / 2
self.write_stimulus(mode=mode, self.write_stimulus(mode=mode,
target_time=target_time, target_time=target_time,
correct_value=correct_value) correct_value=correct_value)
debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode, debug.info(2, "{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
correct_value, correct_value,
target_time, target_time,
infeasible_bound, infeasible_bound,
feasible_bound)) feasible_bound))
self.stim.run_sim(self.stim_sp)
self.stim.run_sim()
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay")) clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time")) setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float: if type(clk_to_q) == float and (clk_to_q < 1.1 * ideal_clk_to_q) and type(setuphold_time)==float:
if mode == "SETUP": # SETUP is clk-din, not din-clk if mode == "SETUP": # SETUP is clk-din, not din-clk
setuphold_time *= -1e9 setuphold_time *= -1e9
else: else:
setuphold_time *= 1e9 setuphold_time *= 1e9
debug.info(2,"PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time)) debug.info(2, "PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
passing_setuphold_time = setuphold_time passing_setuphold_time = setuphold_time
feasible_bound = target_time feasible_bound = target_time
else: else:
debug.info(2,"FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time)) debug.info(2, "FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
infeasible_bound = target_time infeasible_bound = target_time
#raw_input("Press Enter to continue...")
if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001): if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001):
debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound)) debug.info(3, "CONVERGE {0} vs {1}".format(feasible_bound, infeasible_bound))
break break
debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time))
debug.info(2, "Converged on {0} time {1}.".format(mode, passing_setuphold_time))
return passing_setuphold_time return passing_setuphold_time
def setup_LH_time(self): def setup_LH_time(self):
"""Calculates the setup time for low-to-high transition for a DFF """Calculates the setup time for low-to-high transition for a DFF
""" """
return self.bidir_search(1, "SETUP") return self.bidir_search(1, "SETUP")
def setup_HL_time(self): def setup_HL_time(self):
"""Calculates the setup time for high-to-low transition for a DFF """Calculates the setup time for high-to-low transition for a DFF
""" """
return self.bidir_search(0, "SETUP") return self.bidir_search(0, "SETUP")
def hold_LH_time(self): def hold_LH_time(self):
"""Calculates the hold time for low-to-high transition for a DFF """Calculates the hold time for low-to-high transition for a DFF
""" """
@ -272,7 +262,6 @@ class setup_hold():
""" """
return self.bidir_search(0, "HOLD") return self.bidir_search(0, "HOLD")
def analyze(self, related_slews, constrained_slews): def analyze(self, related_slews, constrained_slews):
"""main function to calculate both setup and hold time for the """main function to calculate both setup and hold time for the
DFF and returns a dictionary that contains 4 lists for both DFF and returns a dictionary that contains 4 lists for both
@ -283,7 +272,7 @@ class setup_hold():
HL_setup = [] HL_setup = []
LH_hold = [] LH_hold = []
HL_hold = [] HL_hold = []
#For debugging, skips characterization and returns dummy values. #For debugging, skips characterization and returns dummy values.
# i = 1.0 # i = 1.0
# for self.related_input_slew in related_slews: # for self.related_input_slew in related_slews:
@ -293,18 +282,18 @@ class setup_hold():
# LH_hold.append(i+2.0) # LH_hold.append(i+2.0)
# HL_hold.append(i+3.0) # HL_hold.append(i+3.0)
# i+=4.0 # i+=4.0
# times = {"setup_times_LH": LH_setup, # times = {"setup_times_LH": LH_setup,
# "setup_times_HL": HL_setup, # "setup_times_HL": HL_setup,
# "hold_times_LH": LH_hold, # "hold_times_LH": LH_hold,
# "hold_times_HL": HL_hold # "hold_times_HL": HL_hold
# } # }
# return times # return times
for self.related_input_slew in related_slews: for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews: for self.constrained_input_slew in constrained_slews:
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew)) debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,
self.constrained_input_slew))
LH_setup_time = self.setup_LH_time() LH_setup_time = self.setup_LH_time()
debug.info(1, " Setup Time for low_to_high transition: {0}".format(LH_setup_time)) debug.info(1, " Setup Time for low_to_high transition: {0}".format(LH_setup_time))
HL_setup_time = self.setup_HL_time() HL_setup_time = self.setup_HL_time()
@ -317,7 +306,7 @@ class setup_hold():
HL_setup.append(HL_setup_time) HL_setup.append(HL_setup_time)
LH_hold.append(LH_hold_time) LH_hold.append(LH_hold_time)
HL_hold.append(HL_hold_time) HL_hold.append(HL_hold_time)
times = {"setup_times_LH": LH_setup, times = {"setup_times_LH": LH_setup,
"setup_times_HL": HL_setup, "setup_times_HL": HL_setup,
"hold_times_LH": LH_hold, "hold_times_LH": LH_hold,
@ -325,22 +314,22 @@ class setup_hold():
} }
return times return times
def analytical_setuphold(self,related_slews, constrained_slews): def analytical_setuphold(self, related_slews, constrained_slews):
""" Just return the fixed setup/hold times from the technology. """ Just return the fixed setup/hold times from the technology.
""" """
LH_setup = [] LH_setup = []
HL_setup = [] HL_setup = []
LH_hold = [] LH_hold = []
HL_hold = [] HL_hold = []
for self.related_input_slew in related_slews: for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews: for self.constrained_input_slew in constrained_slews:
# convert from ps to ns # convert from ps to ns
LH_setup.append(tech.spice["dff_setup"]/1e3) LH_setup.append(tech.spice["dff_setup"] / 1e3)
HL_setup.append(tech.spice["dff_setup"]/1e3) HL_setup.append(tech.spice["dff_setup"] / 1e3)
LH_hold.append(tech.spice["dff_hold"]/1e3) LH_hold.append(tech.spice["dff_hold"] / 1e3)
HL_hold.append(tech.spice["dff_hold"]/1e3) HL_hold.append(tech.spice["dff_hold"] / 1e3)
times = {"setup_times_LH": LH_setup, times = {"setup_times_LH": LH_setup,
"setup_times_HL": HL_setup, "setup_times_HL": HL_setup,
"hold_times_LH": LH_hold, "hold_times_LH": LH_hold,

View File

@ -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]

View File

@ -31,16 +31,27 @@ class stimuli():
self.tx_length = tech.drc["minlength_channel"] self.tx_length = tech.drc["minlength_channel"]
self.sf = stim_file self.sf = stim_file
(self.process, self.voltage, self.temperature) = corner (self.process, self.voltage, self.temperature) = corner
found = False
self.device_libraries = []
self.device_models = []
try: try:
self.device_libraries = tech.spice["fet_libraries"][self.process] self.device_libraries += tech.spice["fet_libraries"][self.process]
except: found = True
self.device_models = tech.spice["fet_models"][self.process] except KeyError:
pass
try:
self.device_models += tech.spice["fet_models"][self.process]
found = True
except KeyError:
pass
if not found:
debug.error("Must define either fet_libraries or fet_models.", -1)
def inst_model(self, pins, model_name): def inst_model(self, pins, model_name):
""" Function to instantiate a generic model with a set of pins """ """ Function to instantiate a generic model with a set of pins """
if OPTS.use_pex: if OPTS.use_pex:
self.inst_pex_model(pins, model_name) self.inst_pex_model(pins, model_name)
else: else:
@ -48,7 +59,7 @@ class stimuli():
for pin in pins: for pin in pins:
self.sf.write("{0} ".format(pin)) self.sf.write("{0} ".format(pin))
self.sf.write("{0}\n".format(model_name)) self.sf.write("{0}\n".format(model_name))
def inst_pex_model(self, pins, model_name): def inst_pex_model(self, pins, model_name):
self.sf.write("X{0} ".format(model_name)) self.sf.write("X{0} ".format(model_name))
for pin in pins: for pin in pins:
@ -88,7 +99,7 @@ class stimuli():
def create_buffer(self, buffer_name, size=[1, 3], beta=2.5): def create_buffer(self, buffer_name, size=[1, 3], beta=2.5):
""" """
Generates buffer for top level signals (only for sim Generates buffer for top level signals (only for sim
purposes). Size is pair for PMOS, NMOS width multiple. purposes). Size is pair for PMOS, NMOS width multiple.
""" """
self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name, self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
@ -113,23 +124,23 @@ class stimuli():
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name)) self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall): def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
""" """
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
from 50% to 50%. from 50% to 50%.
""" """
self.sf.write("* PULSE: period={0}\n".format(period)) self.sf.write("* PULSE: period={0}\n".format(period))
pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n" pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n"
self.sf.write(pulse_string.format(sig_name, self.sf.write(pulse_string.format(sig_name,
v1, v1,
v2, v2,
offset, offset,
t_rise, t_rise,
t_fall, t_fall,
0.5*period-0.5*t_rise-0.5*t_fall, 0.5*period-0.5*t_rise-0.5*t_fall,
period)) period))
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup): def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
""" """
Generate a PWL stimulus given a signal name and data values at each period. Generate a PWL stimulus given a signal name and data values at each period.
Automatically creates slews and ensures each data occurs a setup before the clock Automatically creates slews and ensures each data occurs a setup before the clock
edge. The first clk_time should be 0 and is the initial time that corresponds edge. The first clk_time should be 0 and is the initial time that corresponds
@ -141,7 +152,7 @@ class stimuli():
str.format(len(clk_times), str.format(len(clk_times),
len(data_values), len(data_values),
sig_name)) sig_name))
# shift signal times earlier for setup time # shift signal times earlier for setup time
times = np.array(clk_times) - setup * period times = np.array(clk_times) - setup * period
values = np.array(data_values) * self.voltage values = np.array(data_values) * self.voltage
@ -174,7 +185,7 @@ class stimuli():
return 1 return 1
else: else:
debug.error("Invalid value to get an inverse of: {0}".format(value)) debug.error("Invalid value to get an inverse of: {0}".format(value))
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td): def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
""" Creates the .meas statement for the measurement of delay """ """ Creates the .meas statement for the measurement of delay """
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n" measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
@ -187,7 +198,7 @@ class stimuli():
targ_val, targ_val,
targ_dir, targ_dir,
targ_td)) targ_td))
def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td): def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td):
""" Creates the .meas statement for the measurement of delay """ """ Creates the .meas statement for the measurement of delay """
measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n" measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n"
@ -197,7 +208,7 @@ class stimuli():
trig_val, trig_val,
trig_dir, trig_dir,
trig_td)) trig_td))
def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at): def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at):
""" Creates the .meas statement for voltage at time""" """ Creates the .meas statement for voltage at time"""
measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n" measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n"
@ -216,15 +227,15 @@ class stimuli():
power_exp, power_exp,
t_initial, t_initial,
t_final)) t_final))
def gen_meas_value(self, meas_name, dout, t_intital, t_final): def gen_meas_value(self, meas_name, dout, t_intital, t_final):
measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final) measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final)
self.sf.write(measure_string) self.sf.write(measure_string)
def write_control(self, end_time, runlvl=4): def write_control(self, end_time, runlvl=4):
""" Write the control cards to run and end the simulation """ """ Write the control cards to run and end the simulation """
# These are guesses... # These are guesses...
if runlvl==1: if runlvl==1:
reltol = 0.02 # 2% reltol = 0.02 # 2%
elif runlvl==2: elif runlvl==2:
@ -234,7 +245,7 @@ class stimuli():
else: else:
reltol = 0.001 # 0.1% reltol = 0.001 # 0.1%
timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests. timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
# UIC is needed for ngspice to converge # UIC is needed for ngspice to converge
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time)) self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
self.sf.write(".TEMP {}\n".format(self.temperature)) self.sf.write(".TEMP {}\n".format(self.temperature))
@ -249,7 +260,7 @@ class stimuli():
# create plots for all signals # create plots for all signals
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n") self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
if OPTS.debug_level>0: if OPTS.verbose_level>0:
if OPTS.spice_name in ["hspice", "xa"]: if OPTS.spice_name in ["hspice", "xa"]:
self.sf.write(".probe V(*)\n") self.sf.write(".probe V(*)\n")
else: else:
@ -265,21 +276,16 @@ class stimuli():
"""Writes include statements, inputs are lists of model files""" """Writes include statements, inputs are lists of model files"""
self.sf.write("* {} process corner\n".format(self.process)) self.sf.write("* {} process corner\n".format(self.process))
if OPTS.tech_name == "sky130": for item in self.device_libraries:
for item in self.device_libraries: if os.path.isfile(item[0]):
if os.path.isfile(item[0]): self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1]))
self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1]))
else:
debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]))
includes = [circuit]
else:
includes = self.device_models + [circuit]
for item in list(includes):
if os.path.isfile(item):
self.sf.write(".include \"{0}\"\n".format(item))
else: else:
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item)) debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]))
includes = self.device_models + [circuit]
for item in list(includes):
self.sf.write(".include \"{0}\"\n".format(item))
def write_supply(self): def write_supply(self):
""" Writes supply voltage statements """ """ Writes supply voltage statements """
@ -290,13 +296,13 @@ class stimuli():
self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n") self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n")
self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0)) self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0))
def run_sim(self): def run_sim(self, name):
""" Run hspice in batch mode and output rawfile to parse. """ """ Run hspice in batch mode and output rawfile to parse. """
temp_stim = "{0}stim.sp".format(OPTS.openram_temp) temp_stim = "{0}{1}".format(OPTS.openram_temp, name)
import datetime import datetime
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.") debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")
if OPTS.spice_name == "xa": if OPTS.spice_name == "xa":
# Output the xa configurations here. FIXME: Move this to write it once. # Output the xa configurations here. FIXME: Move this to write it once.
xa_cfg = open("{}xa.cfg".format(OPTS.openram_temp), "w") xa_cfg = open("{}xa.cfg".format(OPTS.openram_temp), "w")
@ -331,13 +337,13 @@ class stimuli():
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w') spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w') spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
debug.info(3, cmd) debug.info(3, cmd)
retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True) retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
spice_stdout.close() spice_stdout.close()
spice_stderr.close() spice_stderr.close()
if (retcode > valid_retcode): if (retcode > valid_retcode):
debug.error("Spice simulation error: " + cmd, -1) debug.error("Spice simulation error: " + cmd, -1)
else: else:
@ -345,4 +351,4 @@ class stimuli():
delta_time = round((end_time - start_time).total_seconds(), 1) delta_time = round((end_time - start_time).total_seconds(), 1)
debug.info(2, "*** Spice: {} seconds".format(delta_time)) debug.info(2, "*** Spice: {} seconds".format(delta_time))

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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')):

View File

@ -9,6 +9,7 @@ import os
import inspect import inspect
import globals import globals
import sys import sys
import pdb
# the debug levels: # the debug levels:
# 0 = minimum output (default) # 0 = minimum output (default)
@ -26,9 +27,9 @@ def check(check, str):
log("ERROR: file {0}: line {1}: {2}\n".format( log("ERROR: file {0}: line {1}: {2}\n".format(
os.path.basename(filename), line_number, str)) os.path.basename(filename), line_number, str))
if globals.OPTS.debug_level > 0: if globals.OPTS.debug:
import pdb
pdb.set_trace() pdb.set_trace()
assert 0 assert 0
@ -40,9 +41,9 @@ def error(str, return_value=0):
log("ERROR: file {0}: line {1}: {2}\n".format( log("ERROR: file {0}: line {1}: {2}\n".format(
os.path.basename(filename), line_number, str)) os.path.basename(filename), line_number, str))
if globals.OPTS.debug_level > 0 and return_value != 0: if globals.OPTS.debug:
import pdb
pdb.set_trace() pdb.set_trace()
assert return_value == 0 assert return_value == 0
@ -96,7 +97,7 @@ log.create_file = True
def info(lev, str): def info(lev, str):
from globals import OPTS from globals import OPTS
if (OPTS.debug_level >= lev): if (OPTS.verbose_level >= lev):
frm = inspect.stack()[1] frm = inspect.stack()[1]
mod = inspect.getmodule(frm[0]) mod = inspect.getmodule(frm[0])
# classname = frm.f_globals['__name__'] # classname = frm.f_globals['__name__']

View File

@ -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)

View File

@ -44,7 +44,7 @@ class drc_lut():
if k1 < k2: if k1 < k2:
return False return False
return True return True

View File

@ -19,7 +19,7 @@ class drc_value():
Return the value. Return the value.
""" """
return self.value return self.value

View File

@ -2,7 +2,7 @@ word_size = 32
num_words = 128 num_words = 128
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = False nominal_corner_only = False
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [5.0] supply_voltages = [5.0]
temperatures = [25] temperatures = [25]

View File

@ -6,7 +6,7 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = False nominal_corner_only = False
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [5.0] supply_voltages = [5.0]
temperatures = [25] temperatures = [25]

View File

@ -0,0 +1,21 @@
word_size = 4
num_words = 16
write_size = 2
num_rw_ports = 1
num_r_ports = 0
num_w_ports = 1
tech_name = "scn4m_subm"
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = False
check_lvsdrc = True
output_path = "temp"
output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)

View File

@ -7,7 +7,7 @@ num_r_ports = 0
num_w_ports = 0 num_w_ports = 0
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = False nominal_corner_only = False
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [5.0] supply_voltages = [5.0]
temperatures = [25] temperatures = [25]

View File

@ -6,7 +6,7 @@ num_r_ports = 1
num_w_ports = 1 num_w_ports = 1
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = False nominal_corner_only = False
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [5.0] supply_voltages = [5.0]
temperatures = [25] temperatures = [25]

View File

@ -0,0 +1,21 @@
word_size = 2
num_words = 16
num_rw_ports = 2
num_r_ports = 0
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = False
check_lvsdrc = True
output_path = "temp"
output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)

View File

@ -2,14 +2,14 @@ word_size = 2
num_words = 16 num_words = 16
tech_name = "freepdk45" tech_name = "freepdk45"
nominal_corners_only = False nominal_corner_only = False
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [1.0] supply_voltages = [1.0]
temperatures = [25] temperatures = [25]
route_supplies = False route_supplies = False
check_lvsdrc = True check_lvsdrc = True
# nominal_corners_only = True # nominal_corner_only = True
load_scales = [0.5, 1, 4] load_scales = [0.5, 1, 4]
slew_scales = [0.5, 1] slew_scales = [0.5, 1]

View File

@ -2,7 +2,7 @@ word_size = 2
num_words = 16 num_words = 16
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = False nominal_corner_only = False
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [5.0] supply_voltages = [5.0]
temperatures = [25] temperatures = [25]

View File

@ -2,7 +2,7 @@ word_size = 64
num_words = 1024 num_words = 1024
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = False nominal_corner_only = False
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [ 5.0 ] supply_voltages = [ 5.0 ]
temperatures = [ 25 ] temperatures = [ 25 ]

View File

@ -2,7 +2,7 @@ word_size = 16
num_words = 256 num_words = 256
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = False nominal_corner_only = False
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [5.0] supply_voltages = [5.0]
temperatures = [25] temperatures = [25]

View File

@ -9,7 +9,7 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "freepdk45" tech_name = "freepdk45"
nominal_corners_only = True nominal_corner_only = True
route_supplies = False route_supplies = False
check_lvsdrc = False check_lvsdrc = False

View File

@ -9,7 +9,7 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = True nominal_corner_only = True
route_supplies = False route_supplies = False
check_lvsdrc = False check_lvsdrc = False

View File

@ -7,7 +7,7 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = True nominal_corner_only = True
route_supplies = True route_supplies = True
check_lvsdrc = True check_lvsdrc = True

View File

@ -7,7 +7,7 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = True nominal_corner_only = True
route_supplies = True route_supplies = True
check_lvsdrc = True check_lvsdrc = True

View File

@ -9,7 +9,7 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = True nominal_corner_only = True
route_supplies = False route_supplies = False
check_lvsdrc = False check_lvsdrc = False

View File

@ -7,7 +7,7 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = True nominal_corner_only = True
route_supplies = True route_supplies = True
check_lvsdrc = True check_lvsdrc = True

View File

@ -9,7 +9,7 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = True nominal_corner_only = True
route_supplies = False route_supplies = False
check_lvsdrc = False check_lvsdrc = False

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 256
write_size = 8
local_array_size = 16
num_rw_ports = 1
num_r_ports = 0
num_w_ports = 0
tech_name = "sky130"
nominal_corner_only = True
route_supplies = False
check_lvsdrc = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -9,11 +9,11 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "sky130" tech_name = "sky130"
nominal_corners_only = True nominal_corner_only = True
route_supplies = True route_supplies = False
check_lvsdrc = True check_lvsdrc = True
perimeter_pins = True perimeter_pins = False
#netlist_only = True #netlist_only = True
#analytical_delay = False #analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 512
write_size = 8
local_array_size = 16
num_rw_ports = 1
num_r_ports = 0
num_w_ports = 0
tech_name = "sky130"
nominal_corner_only = True
route_supplies = False
check_lvsdrc = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -9,11 +9,11 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "sky130" tech_name = "sky130"
nominal_corners_only = True nominal_corner_only = True
route_supplies = True route_supplies = False
check_lvsdrc = True check_lvsdrc = True
perimeter_pins = True perimeter_pins = False
#netlist_only = True #netlist_only = True
#analytical_delay = False #analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 1024
write_size = 8
local_array_size = 16
num_rw_ports = 1
num_r_ports = 0
num_w_ports = 0
tech_name = "sky130"
nominal_corner_only = True
route_supplies = False
check_lvsdrc = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -9,11 +9,11 @@ num_r_ports = 1
num_w_ports = 0 num_w_ports = 0
tech_name = "sky130" tech_name = "sky130"
nominal_corners_only = True nominal_corner_only = True
route_supplies = True route_supplies = False
check_lvsdrc = True check_lvsdrc = True
perimeter_pins = True perimeter_pins = False
#netlist_only = True #netlist_only = True
#analytical_delay = False #analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,

View File

@ -1,19 +0,0 @@
word_size = 2
num_words = 16
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
#netlist_only = True
route_supplies = True
check_lvsdrc = True
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,19 +0,0 @@
word_size = 8
num_words = 128
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = True
check_lvsdrc = True
netlist_only = True
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,18 +0,0 @@
word_size = 16
num_words = 256
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = True
check_lvsdrc = True
netlist_only = True
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,18 +0,0 @@
word_size = 32
num_words = 128
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = True
check_lvsdrc = True
netlist_only = True
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,19 +0,0 @@
word_size = 64
num_words = 128
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = True
check_lvsdrc = True
output_path = "/home/jesse/thesis/outputs/run5"
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,31 +0,0 @@
word_size = 16
num_words = 16
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "sky130"
accuracy_requirement = 0.05
magic_exe = ("magic", "magic")
nominal_corners_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
netlist_only = False
route_supplies = "grid"
check_lvsdrc = False
#replica_bitcell_array = "/home/jesse/openram/technology/sky130/modules/replica_bitcell_array.py"
output_path = "sram_" + str(accuracy_requirement)
output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,
num_words,
tech_name,
accuracy_requirement
)
write_size=8

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -166,4 +166,4 @@ def realpolyroots(*cs):
# else: # else:
# rs.append(r) # rs.append(r)
# return rs # return rs
# #

View File

@ -710,9 +710,9 @@ class arct_pt(pathitem):
# Negative (positive) angles alpha corresponds to a turn to the right (left) # Negative (positive) angles alpha corresponds to a turn to the right (left)
# as seen from currentpoint. # as seen from currentpoint.
if dx1*dy2-dy1*dx2 > 0: if dx1*dy2-dy1*dx2 > 0:
alpha = acos(dx1*dx2+dy1*dy2) alpha = acos(dx1*dx2+dy1*dy2)
else: else:
alpha = -acos(dx1*dx2+dy1*dy2) alpha = -acos(dx1*dx2+dy1*dy2)
try: try:
# two tangent points # two tangent points
@ -744,7 +744,7 @@ class arct_pt(pathitem):
return [line, arcn_pt(mx_pt, my_pt, self.r_pt, phi-deltaphi, phi+deltaphi)] return [line, arcn_pt(mx_pt, my_pt, self.r_pt, phi-deltaphi, phi+deltaphi)]
except ZeroDivisionError: except ZeroDivisionError:
# in the degenerate case, we just return a line as specified by the PS # in the degenerate case, we just return a line as specified by the PS
# language reference # language reference
return [lineto_pt(self.x1_pt, self.y1_pt)] return [lineto_pt(self.x1_pt, self.y1_pt)]

Some files were not shown because too many files have changed in this diff Show More