Merge branch 'dev' into s8_single_port

This commit is contained in:
jcirimel 2020-11-03 15:21:03 -08:00
commit e1d7d9dff7
310 changed files with 3204 additions and 3466 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))
@ -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

@ -15,7 +15,7 @@ class _pins:
for k, v in pin_dict.items(): for k, v in pin_dict.items():
self.__dict__[k] = v self.__dict__[k] = v
class _cell: class _cell:
def __init__(self, pin_dict): def __init__(self, pin_dict):
pin_dict.update(self._default_power_pins()) pin_dict.update(self._default_power_pins())
@ -29,24 +29,24 @@ class _cell:
return {'vdd': 'vdd', return {'vdd': 'vdd',
'gnd': 'gnd'} 'gnd': 'gnd'}
class _mirror_axis: class _mirror_axis:
def __init__(self, x, y): def __init__(self, x, y):
self.x = x self.x = x
self.y = y self.y = y
class _ptx: class _ptx:
def __init__(self, model_is_subckt, bin_spice_models): def __init__(self, model_is_subckt, bin_spice_models):
self.model_is_subckt = model_is_subckt self.model_is_subckt = model_is_subckt
self.bin_spice_models = bin_spice_models self.bin_spice_models = bin_spice_models
class _pgate: class _pgate:
def __init__(self, add_implants): def __init__(self, add_implants):
self.add_implants = add_implants self.add_implants = add_implants
class _bitcell: class _bitcell:
def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r): def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r):
self.mirror = mirror self.mirror = mirror
@ -110,25 +110,25 @@ class _dff:
self.custom_type_list = custom_type_list self.custom_type_list = custom_type_list
self.clk_pin = clk_pin self.clk_pin = clk_pin
class _dff_buff: class _dff_buff:
def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts): def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports self.use_custom_ports = use_custom_ports
self.buf_ports = custom_buff_ports self.buf_ports = custom_buff_ports
self.add_body_contacts = add_body_contacts self.add_body_contacts = add_body_contacts
class _dff_buff_array: class _dff_buff_array:
def __init__(self, use_custom_ports, add_body_contacts): def __init__(self, use_custom_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports self.use_custom_ports = use_custom_ports
self.add_body_contacts = add_body_contacts self.add_body_contacts = add_body_contacts
class _bitcell_array: class _bitcell_array:
def __init__(self, use_custom_cell_arrangement): def __init__(self, use_custom_cell_arrangement):
self.use_custom_cell_arrangement = use_custom_cell_arrangement self.use_custom_cell_arrangement = use_custom_cell_arrangement
class cell_properties(): class cell_properties():
""" """
This contains meta information about the custom designed cells. For This contains meta information about the custom designed cells. For
@ -142,14 +142,14 @@ class cell_properties():
self._ptx = _ptx(model_is_subckt=False, self._ptx = _ptx(model_is_subckt=False,
bin_spice_models=False) bin_spice_models=False)
self._pgate = _pgate(add_implants=False) self._pgate = _pgate(add_implants=False)
self._dff = _dff(use_custom_ports=False, self._dff = _dff(use_custom_ports=False,
custom_port_list=["D", "Q", "clk", "vdd", "gnd"], custom_port_list=["D", "Q", "clk", "vdd", "gnd"],
custom_type_list=["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"], custom_type_list=["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
clk_pin="clk") clk_pin="clk")
self._dff_buff = _dff_buff(use_custom_ports=False, self._dff_buff = _dff_buff(use_custom_ports=False,
custom_buff_ports=["D", "qint", "clk", "vdd", "gnd"], custom_buff_ports=["D", "qint", "clk", "vdd", "gnd"],
add_body_contacts=False) add_body_contacts=False)
@ -176,7 +176,7 @@ class cell_properties():
@property @property
def ptx(self): def ptx(self):
return self._ptx return self._ptx
@property @property
def pgate(self): def pgate(self):
return self._pgate return self._pgate
@ -184,7 +184,7 @@ class cell_properties():
@property @property
def dff(self): def dff(self):
return self._dff return self._dff
@property @property
def dff_buff(self): def dff_buff(self):
return self._dff_buff return self._dff_buff
@ -200,7 +200,7 @@ class cell_properties():
@property @property
def sense_amp(self): def sense_amp(self):
return self._sense_amp return self._sense_amp
@property @property
def bitcell_array(self): def bitcell_array(self):
return self._bitcell_array return self._bitcell_array

View File

@ -6,7 +6,7 @@
# All rights reserved. # All rights reserved.
# #
class _bank: class _bank:
def __init__(self, stack, pitch): def __init__(self, stack, pitch):
# bank # bank
@ -15,8 +15,8 @@ class _bank:
# m2_stack, m3_pitch (sky130) # m2_stack, m3_pitch (sky130)
self.stack = stack self.stack = stack
self.pitch = pitch self.pitch = pitch
class _hierarchical_decoder: class _hierarchical_decoder:
def __init__(self, def __init__(self,
bus_layer, bus_layer,
@ -60,7 +60,7 @@ class _hierarchical_predecode:
self.output_layer = output_layer self.output_layer = output_layer
self.vertical_supply = vertical_supply self.vertical_supply = vertical_supply
class _column_mux_array: class _column_mux_array:
def __init__(self, def __init__(self,
select_layer, select_layer,
@ -74,7 +74,7 @@ class _column_mux_array:
self.select_pitch= select_pitch self.select_pitch= select_pitch
self.bitline_layer = bitline_layer self.bitline_layer = bitline_layer
class _port_address: class _port_address:
def __init__(self, def __init__(self,
supply_offset): supply_offset):
@ -82,7 +82,7 @@ class _port_address:
# special supply offset # special supply offset
self.supply_offset = supply_offset self.supply_offset = supply_offset
class _port_data: class _port_data:
def __init__(self, def __init__(self,
channel_route_bitlines, channel_route_bitlines,
@ -94,7 +94,7 @@ class _port_data:
# en_layer # en_layer
# m1 # m1
# m3 (sky130) # m3 (sky130)
# precharge_array # precharge_array
# en_bar_layer # en_bar_layer
# m1 # m1
@ -110,7 +110,7 @@ class _replica_column:
# even row check (sky130) # even row check (sky130)
self.even_rows = even_rows self.even_rows = even_rows
class _wordline_driver: class _wordline_driver:
def __init__(self, def __init__(self,
vertical_supply): vertical_supply):
@ -122,14 +122,14 @@ class _wordline_driver:
# vertical vdd/gnd (sky130) # vertical vdd/gnd (sky130)
self.vertical_supply = vertical_supply self.vertical_supply = vertical_supply
class layer_properties(): class layer_properties():
""" """
This contains meta information about the module routing layers. These This contains meta information about the module routing layers. These
can be overriden in the tech.py file. can be overriden in the tech.py file.
""" """
def __init__(self): def __init__(self):
self._bank = _bank(stack="m1_stack", self._bank = _bank(stack="m1_stack",
pitch="m2_pitch") pitch="m2_pitch")
@ -138,7 +138,7 @@ class layer_properties():
input_layer="m1", input_layer="m1",
output_layer="m3", output_layer="m3",
vertical_supply=False) vertical_supply=False)
self._hierarchical_predecode = _hierarchical_predecode(bus_layer="m2", self._hierarchical_predecode = _hierarchical_predecode(bus_layer="m2",
bus_directions="pref", bus_directions="pref",
bus_space_factor=1, bus_space_factor=1,
@ -156,13 +156,13 @@ class layer_properties():
enable_layer="m1") enable_layer="m1")
self._replica_column = _replica_column(even_rows=False) self._replica_column = _replica_column(even_rows=False)
self._wordline_driver = _wordline_driver(vertical_supply=False) self._wordline_driver = _wordline_driver(vertical_supply=False)
@property @property
def bank(self): def bank(self):
return self._bank return self._bank
@property @property
def column_mux_array(self): def column_mux_array(self):
return self._column_mux_array return self._column_mux_array
@ -174,7 +174,7 @@ class layer_properties():
@property @property
def hierarchical_predecode(self): def hierarchical_predecode(self):
return self._hierarchical_predecode return self._hierarchical_predecode
@property @property
def port_address(self): def port_address(self):
return self._port_address return self._port_address
@ -190,4 +190,4 @@ class layer_properties():
@property @property
def wordline_driver(self): def wordline_driver(self):
return self._wordline_driver return self._wordline_driver

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

@ -9,22 +9,30 @@ from hierarchy_design import hierarchy_design
from utils import round_to_grid from utils import round_to_grid
import contact import contact
from tech import preferred_directions from tech import preferred_directions
from tech import cell_properties as props
from globals import OPTS from globals import OPTS
import re import re
import debug
class design(hierarchy_design): class design(hierarchy_design):
""" """
This is the same as the hierarchy_design class except it contains This is the same as the hierarchy_design class except it contains
some DRC/layer constants and analytical models for other modules to reuse. some DRC/layer constants and analytical models for other modules to reuse.
""" """
def __init__(self, name): def __init__(self, name, cell_name=None):
super().__init__(name) # This allows us to use different GDS/spice circuits for hard cells instead of the default ones
# Except bitcell names are generated automatically by the globals.py setup_bitcells routines
# depending on the number of ports.
if name in props.names:
cell_name = props.names[name]
elif not cell_name:
cell_name = name
super().__init__(name, cell_name)
self.setup_multiport_constants() self.setup_multiport_constants()
def check_pins(self): def check_pins(self):
for pin_name in self.pins: for pin_name in self.pins:
pins = self.get_pins(pin_name) pins = self.get_pins(pin_name)
@ -52,7 +60,7 @@ class design(hierarchy_design):
match = re.search(r"minarea_(.*)", rule) match = re.search(r"minarea_(.*)", rule)
if match: if match:
setattr(design, match.group(0), drc(match.group(0))) setattr(design, match.group(0), drc(match.group(0)))
# Single layer spacing rules # Single layer spacing rules
match = re.search(r"(.*)_to_(.*)", rule) match = re.search(r"(.*)_to_(.*)", rule)
if match and match.group(1) == match.group(2): if match and match.group(1) == match.group(2):
@ -63,7 +71,7 @@ class design(hierarchy_design):
drc(match.group(0))) drc(match.group(0)))
else: else:
setattr(design, match.group(0), drc(match.group(0))) setattr(design, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_enclose_(.*)", rule) match = re.search(r"(.*)_enclose_(.*)", rule)
if match: if match:
setattr(design, match.group(0), drc(match.group(0))) setattr(design, match.group(0), drc(match.group(0)))
@ -94,7 +102,7 @@ class design(hierarchy_design):
design.well_enclose_active = max(design.pwell_enclose_active, design.well_enclose_active = max(design.pwell_enclose_active,
design.nwell_enclose_active, design.nwell_enclose_active,
design.active_space) design.active_space)
# These are for debugging previous manual rules # These are for debugging previous manual rules
if False: if False:
print("poly_width", design.poly_width) print("poly_width", design.poly_width)
@ -127,7 +135,7 @@ class design(hierarchy_design):
These are some layer constants used These are some layer constants used
in many places in the compiler. in many places in the compiler.
""" """
from tech import layer_indices from tech import layer_indices
import tech import tech
for layer in layer_indices: for layer in layer_indices:
@ -143,17 +151,17 @@ class design(hierarchy_design):
# Skip computing the pitch for active # Skip computing the pitch for active
if layer == "active": if layer == "active":
continue continue
# Add the pitch # Add the pitch
setattr(design, setattr(design,
"{}_pitch".format(layer), "{}_pitch".format(layer),
design.compute_pitch(layer, True)) design.compute_pitch(layer, True))
# Add the non-preferrd pitch (which has vias in the "wrong" way) # Add the non-preferrd pitch (which has vias in the "wrong" way)
setattr(design, setattr(design,
"{}_nonpref_pitch".format(layer), "{}_nonpref_pitch".format(layer),
design.compute_pitch(layer, False)) design.compute_pitch(layer, False))
if False: if False:
from tech import preferred_directions from tech import preferred_directions
print(preferred_directions) print(preferred_directions)
@ -173,9 +181,9 @@ class design(hierarchy_design):
import sys import sys
sys.exit(1) sys.exit(1)
@staticmethod @staticmethod
def compute_pitch(layer, preferred=True): def compute_pitch(layer, preferred=True):
""" """
This is the preferred direction pitch This is the preferred direction pitch
i.e. we take the minimum or maximum contact dimension i.e. we take the minimum or maximum contact dimension
@ -195,7 +203,7 @@ class design(hierarchy_design):
@staticmethod @staticmethod
def get_preferred_direction(layer): def get_preferred_direction(layer):
return preferred_directions[layer] return preferred_directions[layer]
@staticmethod @staticmethod
def compute_layer_pitch(layer_stack, preferred): def compute_layer_pitch(layer_stack, preferred):
@ -228,7 +236,7 @@ class design(hierarchy_design):
def setup_multiport_constants(self): def setup_multiport_constants(self):
""" """
These are contants and lists that aid multiport design. These are contants and lists that aid multiport design.
Ports are always in the order RW, W, R. Ports are always in the order RW, W, R.
Port indices start from 0 and increment. Port indices start from 0 and increment.
@ -266,14 +274,14 @@ class design(hierarchy_design):
self.read_ports.append(port_number) self.read_ports.append(port_number)
self.readonly_ports.append(port_number) self.readonly_ports.append(port_number)
port_number += 1 port_number += 1
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
""" Get total power of a module """ """ Get total power of a module """
total_module_power = self.return_power() total_module_power = self.return_power()
for inst in self.insts: for inst in self.insts:
total_module_power += inst.mod.analytical_power(corner, load) total_module_power += inst.mod.analytical_power(corner, load)
return total_module_power return total_module_power
design.setup_drc_constants() design.setup_drc_constants()
design.setup_layer_constants() design.setup_layer_constants()

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 OPTS.purge_temp:
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) os.remove(tempgds)
def DRC(self, final_verification=False): def DRC(self, final_verification=False):
"""Checks DRC for a module""" """Checks DRC for a module"""
import verify import verify
@ -104,11 +105,11 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only: if OPTS.netlist_only:
return return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.cell_name)
self.gds_write(tempgds) self.gds_write(tempgds)
num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) num_errors = verify.run_drc(self.cell_name, tempgds, final_verification=final_verification)
debug.check(num_errors == 0, debug.check(num_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.name, "DRC failed for {0} with {1} error(s)".format(self.cell_name,
num_errors)) num_errors))
if OPTS.purge_temp: if OPTS.purge_temp:
@ -125,30 +126,30 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only: if OPTS.netlist_only:
return return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.cell_name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
self.lvs_write(tempspice) self.lvs_write(tempspice)
self.gds_write(tempgds) self.gds_write(tempgds)
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
debug.check(num_errors == 0, debug.check(num_errors == 0,
"LVS failed for {0} with {1} error(s)".format(self.name, "LVS failed for {0} with {1} error(s)".format(self.cell_name,
num_errors)) num_errors))
if OPTS.purge_temp: if OPTS.purge_temp:
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) os.remove(tempgds)
def init_graph_params(self): def init_graph_params(self):
""" """
Initializes parameters relevant to the graph creation Initializes parameters relevant to the graph creation
""" """
# Only initializes a set for checking instances which should not be added # Only initializes a set for checking instances which should not be added
self.graph_inst_exclude = set() self.graph_inst_exclude = set()
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
""" """
Recursively create graph from instances in module. Recursively create graph from instances in module.
""" """
# Translate port names to external nets # Translate port names to external nets
if len(port_nets) != len(self.pins): if len(port_nets) != len(self.pins):
debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,
@ -162,7 +163,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
subinst_name = inst_name + '.X' + subinst.name subinst_name = inst_name + '.X' + subinst.name
subinst_ports = self.translate_nets(conns, port_dict, inst_name) subinst_ports = self.translate_nets(conns, port_dict, inst_name)
subinst.mod.build_graph(graph, subinst_name, subinst_ports) subinst.mod.build_graph(graph, subinst_name, subinst_ports)
def build_names(self, name_dict, inst_name, port_nets): def build_names(self, name_dict, inst_name, port_nets):
""" """
Collects all the nets and the parent inst of that net. Collects all the nets and the parent inst of that net.
@ -195,7 +196,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
else: else:
converted_conns.append("{}.{}".format(inst_name, conn)) converted_conns.append("{}.{}".format(inst_name, conn))
return converted_conns return converted_conns
def add_graph_edges(self, graph, port_nets): def add_graph_edges(self, graph, port_nets):
""" """
For every input, adds an edge to every output. For every input, adds an edge to every output.
@ -211,13 +212,13 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
for out in output_pins + inout_pins: for out in output_pins + inout_pins:
if inp != out: # do not add self loops if inp != out: # do not add self loops
graph.add_edge(pin_dict[inp], pin_dict[out], self) graph.add_edge(pin_dict[inp], pin_dict[out], self)
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
pins = ",".join(self.pins) pins = ",".join(self.pins)
insts = [" {}".format(x) for x in self.insts] insts = [" {}".format(x) for x in self.insts]
objs = [" {}".format(x) for x in self.objs] objs = [" {}".format(x) for x in self.objs]
s = "********** design {0} **********".format(self.name) s = "********** design {0} **********".format(self.cell_name)
s += "\n pins ({0})={1}\n".format(len(self.pins), pins) s += "\n pins ({0})={1}\n".format(len(self.pins), pins)
s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs)) s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs))
s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts)) s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts))
@ -231,4 +232,4 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
for i in self.insts: for i in self.insts:
text+=str(i) + ",\n" text+=str(i) + ",\n"
return text return text

View File

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

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

@ -41,7 +41,7 @@ class lef:
self.lef_write_obstructions() self.lef_write_obstructions()
self.lef_write_footer() self.lef_write_footer()
self.lef.close() self.lef.close()
def lef_write_header(self): def lef_write_header(self):
""" Header of LEF file """ """ Header of LEF file """
self.lef.write("VERSION 5.4 ;\n") self.lef.write("VERSION 5.4 ;\n")
@ -51,7 +51,7 @@ class lef:
self.lef.write("UNITS\n") self.lef.write("UNITS\n")
self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units)) self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units))
self.lef.write("END UNITS\n") self.lef.write("END UNITS\n")
self.lef.write("{0}MACRO {1}\n".format(self.indent,self.name)) self.lef.write("{0}MACRO {1}\n".format(self.indent,self.name))
self.indent += " " self.indent += " "
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent)) self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
@ -59,25 +59,25 @@ class lef:
round(self.width,self.round_grid), round(self.width,self.round_grid),
round(self.height,self.round_grid))) round(self.height,self.round_grid)))
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent)) self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
def lef_write_footer(self): def lef_write_footer(self):
self.lef.write("{0}END {1}\n".format(self.indent,self.name)) self.lef.write("{0}END {1}\n".format(self.indent,self.name))
self.indent = self.indent[:-3] self.indent = self.indent[:-3]
self.lef.write("END LIBRARY\n") self.lef.write("END LIBRARY\n")
def lef_write_pin(self, name): def lef_write_pin(self, name):
pin_dir = self.get_pin_dir(name) pin_dir = self.get_pin_dir(name)
pin_type = self.get_pin_type(name) pin_type = self.get_pin_type(name)
self.lef.write("{0}PIN {1}\n".format(self.indent,name)) self.lef.write("{0}PIN {1}\n".format(self.indent,name))
self.indent += " " self.indent += " "
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent,pin_dir)) self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent,pin_dir))
if pin_type in ["POWER","GROUND"]: if pin_type in ["POWER","GROUND"]:
self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type)) self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type))
self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent)) self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent))
self.lef.write("{0}PORT\n".format(self.indent)) self.lef.write("{0}PORT\n".format(self.indent))
self.indent += " " self.indent += " "
@ -86,7 +86,7 @@ class lef:
for pin in pin_list: for pin in pin_list:
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer)) self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer))
self.lef_write_shape(pin.rect) self.lef_write_shape(pin.rect)
# End the PORT # End the PORT
self.indent = self.indent[:-3] self.indent = self.indent[:-3]
self.lef.write("{0}END\n".format(self.indent)) self.lef.write("{0}END\n".format(self.indent))
@ -94,7 +94,7 @@ class lef:
# End the PIN # End the PIN
self.indent = self.indent[:-3] self.indent = self.indent[:-3]
self.lef.write("{0}END {1}\n".format(self.indent,name)) self.lef.write("{0}END {1}\n".format(self.indent,name))
def lef_write_obstructions(self): def lef_write_obstructions(self):
""" Write all the obstructions on each layer """ """ Write all the obstructions on each layer """
self.lef.write("{0}OBS\n".format(self.indent)) self.lef.write("{0}OBS\n".format(self.indent))
@ -111,16 +111,16 @@ class lef:
self.lef.write("{0}END\n".format(self.indent)) self.lef.write("{0}END\n".format(self.indent))
def lef_write_shape(self, rect): def lef_write_shape(self, rect):
if len(rect) == 2: if len(rect) == 2:
""" Write a LEF rectangle """ """ Write a LEF rectangle """
self.lef.write("{0}RECT ".format(self.indent)) self.lef.write("{0}RECT ".format(self.indent))
for item in rect: for item in rect:
# print(rect) # print(rect)
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid))) self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
self.lef.write(" ;\n") self.lef.write(" ;\n")
else: else:
""" Write a LEF polygon """ """ Write a LEF polygon """
self.lef.write("{0}POLYGON ".format(self.indent)) self.lef.write("{0}POLYGON ".format(self.indent))
for item in rect: for item in rect:
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid))) self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
# for i in range(0,len(rect)): # for i in range(0,len(rect)):

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,9 +6,8 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props from tech import cell_properties as props
from globals import OPTS
import bitcell_base import bitcell_base
@ -20,8 +19,6 @@ class bitcell(bitcell_base.bitcell_base):
library. library.
""" """
# If we have a split WL bitcell, if not be backwards
# compatible in the tech file
pin_names = [props.bitcell.cell_6t.pin.bl, pin_names = [props.bitcell.cell_6t.pin.bl,
props.bitcell.cell_6t.pin.br, props.bitcell.cell_6t.pin.br,
props.bitcell.cell_6t.pin.wl, props.bitcell.cell_6t.pin.wl,
@ -30,20 +27,12 @@ class bitcell(bitcell_base.bitcell_base):
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar'] storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_6t", def __init__(self, name, cell_name=None):
GDS["unit"], if not cell_name:
layer["boundary"]) cell_name = OPTS.bitcell_name
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) super().__init__(name, cell_name)
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "cell_6t")
debug.info(2, "Create bitcell") debug.info(2, "Create bitcell")
self.width = bitcell.width
self.height = bitcell.height
self.pin_map = bitcell.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
def get_all_wl_names(self): def get_all_wl_names(self):

View File

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

View File

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

View File

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

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
@ -21,24 +19,14 @@ class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1rw1r.pin.bl1, props.bitcell.cell_1rw1r.pin.bl1,
props.bitcell.cell_1rw1r.pin.br1, props.bitcell.cell_1rw1r.pin.br1,
props.bitcell.cell_1rw1r.pin.vdd] props.bitcell.cell_1rw1r.pin.vdd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"POWER", "GROUND"] "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r", def __init__(self, name="col_cap_cell_1rw_1r", cell_name=None):
GDS["unit"], if not cell_name:
layer["boundary"]) cell_name = name
pin_map = utils.get_libcell_pins(pin_names,
"col_cap_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r") bitcell_base.bitcell_base.__init__(self, name, cell_name)
debug.info(2, "Create col_cap bitcell 1rw+1r object") debug.info(2, "Create col_cap bitcell 1rw+1r object")
self.width = col_cap_bitcell_1rw_1r.width
self.height = col_cap_bitcell_1rw_1r.height
self.pin_map = col_cap_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.no_instances = True self.no_instances = True

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import bitcell_base
from tech import GDS,layer,drc,parameter
from tech import cell_properties as props from tech import cell_properties as props
from globals import OPTS
class replica_bitcell_1rw_1r(design.design):
class replica_bitcell_1rw_1r(bitcell_base.bitcell_base):
""" """
A single bit cell which is forced to store a 0. A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It This module implements the single memory cell used in the design. It
@ -26,31 +26,24 @@ class replica_bitcell_1rw_1r(design.design):
props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd, props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd] props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] def __init__(self, name, cell_name=None):
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"]) if not cell_name:
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"]) cell_name = OPTS.replica_bitcell_name
super().__init__(name, cell_name)
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_1rw_1r")
debug.info(2, "Create replica bitcell 1rw+1r object") debug.info(2, "Create replica bitcell 1rw+1r object")
self.width = replica_bitcell_1rw_1r.width
self.height = replica_bitcell_1rw_1r.height
self.pin_map = replica_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load): def get_stage_effort(self, load):
parasitic_delay = 1 parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def input_load(self): def input_load(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well. # FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]

View File

@ -5,13 +5,15 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import bitcell_base
from tech import GDS,layer,drc,parameter
from tech import cell_properties as props from tech import cell_properties as props
from globals import OPTS
from tech import GDS, layer
import utils
class replica_bitcell_1w_1r(design.design):
class replica_bitcell_1w_1r(bitcell_base.bitcell_base):
""" """
A single bit cell which is forced to store a 0. A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It This module implements the single memory cell used in the design. It
@ -26,20 +28,14 @@ class replica_bitcell_1w_1r(design.design):
props.bitcell.cell_1w1r.pin.wl1, props.bitcell.cell_1w1r.pin.wl1,
props.bitcell.cell_1w1r.pin.vdd, props.bitcell.cell_1w1r.pin.vdd,
props.bitcell.cell_1w1r.pin.gnd] props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] def __init__(self, name, cell_name=None):
(width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"]) if not cell_name:
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"]) cell_name = OPTS.replica_bitcell_name
super().__init__(name, cell_name)
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_1w_1r")
debug.info(2, "Create replica bitcell 1w+1r object") debug.info(2, "Create replica bitcell 1w+1r object")
self.width = replica_bitcell_1w_1r.width
self.height = replica_bitcell_1w_1r.height
self.pin_map = replica_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load): def get_stage_effort(self, load):
parasitic_delay = 1 parasitic_delay = 1
@ -47,10 +43,10 @@ class replica_bitcell_1w_1r(design.design):
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def input_load(self): def input_load(self):
"""Return the relative capacitance of the access transistor gates""" """Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well. # FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]

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,8 +6,6 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props from tech import cell_properties as props
import bitcell_base import bitcell_base
@ -22,23 +20,12 @@ class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
pin_names = [props.bitcell.cell_1rw1r.pin.wl0, pin_names = [props.bitcell.cell_1rw1r.pin.wl0,
props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.gnd] props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["INPUT", "INPUT", "GROUND"] type_list = ["INPUT", "INPUT", "GROUND"]
(width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"row_cap_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""): def __init__(self, name="row_cap_cell_1rw_1r", cell_name=None):
# Ignore the name argument if not cell_name:
bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r") cell_name = name
bitcell_base.bitcell_base.__init__(self, name, cell_name)
debug.info(2, "Create row_cap bitcell 1rw+1r object") debug.info(2, "Create row_cap bitcell 1rw+1r object")
self.width = row_cap_bitcell_1rw_1r.width
self.height = row_cap_bitcell_1rw_1r.height
self.pin_map = row_cap_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.no_instances = True self.no_instances = True

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

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

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

@ -31,7 +31,7 @@ class setup_hold():
self.set_corner(corner) self.set_corner(corner)
def set_corner(self,corner): def set_corner(self,corner):
""" Set the corner values """ """ Set the corner values """
self.corner = corner self.corner = corner
@ -60,9 +60,9 @@ class setup_hold():
self.write_clock() self.write_clock()
self.write_measures(mode=mode, self.write_measures(mode=mode,
correct_value=correct_value) correct_value=correct_value)
self.stim.write_control(4*self.period) self.stim.write_control(4*self.period)
@ -102,14 +102,14 @@ class setup_hold():
data_values=[init_value, start_value, end_value], data_values=[init_value, start_value, end_value],
period=target_time, period=target_time,
slew=self.constrained_input_slew, slew=self.constrained_input_slew,
setup=0) setup=0)
def write_clock(self): def write_clock(self):
""" Create the clock signal for setup/hold analysis. First period initializes the FF """ Create the clock signal for setup/hold analysis. First period initializes the FF
while the second is used for characterization.""" while the second is used for characterization."""
self.stim.gen_pwl(sig_name="clk", self.stim.gen_pwl(sig_name="clk",
# initial clk edge is right after the 0 time to initialize a flop # initial clk edge is right after the 0 time to initialize a flop
# without using .IC on an internal node. # without using .IC on an internal node.
# Return input to value after one period. # Return input to value after one period.
# The second pulse is the characterization one at 2*period # The second pulse is the characterization one at 2*period
@ -117,7 +117,7 @@ class setup_hold():
data_values=[0, 1, 0, 1], data_values=[0, 1, 0, 1],
period=2*self.period, period=2*self.period,
slew=self.constrained_input_slew, slew=self.constrained_input_slew,
setup=0) setup=0)
@ -154,7 +154,7 @@ class setup_hold():
targ_dir=dout_rise_or_fall, targ_dir=dout_rise_or_fall,
trig_td=1.9*self.period, trig_td=1.9*self.period,
targ_td=1.9*self.period) targ_td=1.9*self.period)
targ_name = "data" targ_name = "data"
# Start triggers right after initialize value is returned to normal # Start triggers right after initialize value is returned to normal
# at one period # at one period
@ -167,14 +167,14 @@ class setup_hold():
targ_dir=din_rise_or_fall, targ_dir=din_rise_or_fall,
trig_td=1.2*self.period, trig_td=1.2*self.period,
targ_td=1.2*self.period) targ_td=1.2*self.period)
def bidir_search(self, correct_value, mode): def bidir_search(self, correct_value, mode):
""" This will perform a bidirectional search for either setup or hold times. """ This will perform a bidirectional search for either setup or hold times.
It starts with the feasible priod and looks a half period beyond or before it It starts with the feasible priod and looks a half period beyond or before it
depending on whether we are doing setup or hold. depending on whether we are doing setup or hold.
""" """
# NOTE: The feasible bound is always feasible. This is why they are different for setup and hold. # NOTE: The feasible bound is always feasible. This is why they are different for setup and hold.
@ -189,8 +189,8 @@ class setup_hold():
feasible_bound = 2.75*self.period feasible_bound = 2.75*self.period
# Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search! # Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search!
self.write_stimulus(mode=mode, self.write_stimulus(mode=mode,
target_time=feasible_bound, target_time=feasible_bound,
correct_value=correct_value) correct_value=correct_value)
self.stim.run_sim() self.stim.run_sim()
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay")) ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
@ -204,18 +204,18 @@ class setup_hold():
setuphold_time *= -1e9 setuphold_time *= -1e9
else: else:
setuphold_time *= 1e9 setuphold_time *= 1e9
passing_setuphold_time = setuphold_time passing_setuphold_time = setuphold_time
debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode, debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
setuphold_time, setuphold_time,
feasible_bound, feasible_bound,
2*self.period)) 2*self.period))
#raw_input("Press Enter to continue...") #raw_input("Press Enter to continue...")
while True: while True:
target_time = (feasible_bound + infeasible_bound)/2 target_time = (feasible_bound + infeasible_bound)/2
self.write_stimulus(mode=mode, self.write_stimulus(mode=mode,
target_time=target_time, target_time=target_time,
correct_value=correct_value) correct_value=correct_value)
debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode, debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
@ -245,7 +245,7 @@ class setup_hold():
if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001): if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001):
debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound)) debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound))
break break
debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time)) debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time))
return passing_setuphold_time return passing_setuphold_time
@ -261,7 +261,7 @@ class setup_hold():
"""Calculates the setup time for high-to-low transition for a DFF """Calculates the setup time for high-to-low transition for a DFF
""" """
return self.bidir_search(0, "SETUP") return self.bidir_search(0, "SETUP")
def hold_LH_time(self): def hold_LH_time(self):
"""Calculates the hold time for low-to-high transition for a DFF """Calculates the hold time for low-to-high transition for a DFF
""" """
@ -283,7 +283,7 @@ class setup_hold():
HL_setup = [] HL_setup = []
LH_hold = [] LH_hold = []
HL_hold = [] HL_hold = []
#For debugging, skips characterization and returns dummy values. #For debugging, skips characterization and returns dummy values.
# i = 1.0 # i = 1.0
# for self.related_input_slew in related_slews: # for self.related_input_slew in related_slews:
@ -293,15 +293,15 @@ class setup_hold():
# LH_hold.append(i+2.0) # LH_hold.append(i+2.0)
# HL_hold.append(i+3.0) # HL_hold.append(i+3.0)
# i+=4.0 # i+=4.0
# times = {"setup_times_LH": LH_setup, # times = {"setup_times_LH": LH_setup,
# "setup_times_HL": HL_setup, # "setup_times_HL": HL_setup,
# "hold_times_LH": LH_hold, # "hold_times_LH": LH_hold,
# "hold_times_HL": HL_hold # "hold_times_HL": HL_hold
# } # }
# return times # return times
for self.related_input_slew in related_slews: for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews: for self.constrained_input_slew in constrained_slews:
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew)) debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew))
@ -317,7 +317,7 @@ class setup_hold():
HL_setup.append(HL_setup_time) HL_setup.append(HL_setup_time)
LH_hold.append(LH_hold_time) LH_hold.append(LH_hold_time)
HL_hold.append(HL_hold_time) HL_hold.append(HL_hold_time)
times = {"setup_times_LH": LH_setup, times = {"setup_times_LH": LH_setup,
"setup_times_HL": HL_setup, "setup_times_HL": HL_setup,
"hold_times_LH": LH_hold, "hold_times_LH": LH_hold,
@ -332,7 +332,7 @@ class setup_hold():
HL_setup = [] HL_setup = []
LH_hold = [] LH_hold = []
HL_hold = [] HL_hold = []
for self.related_input_slew in related_slews: for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews: for self.constrained_input_slew in constrained_slews:
# convert from ps to ns # convert from ps to ns
@ -340,7 +340,7 @@ class setup_hold():
HL_setup.append(tech.spice["dff_setup"]/1e3) HL_setup.append(tech.spice["dff_setup"]/1e3)
LH_hold.append(tech.spice["dff_hold"]/1e3) LH_hold.append(tech.spice["dff_hold"]/1e3)
HL_hold.append(tech.spice["dff_hold"]/1e3) HL_hold.append(tech.spice["dff_hold"]/1e3)
times = {"setup_times_LH": LH_setup, times = {"setup_times_LH": LH_setup,
"setup_times_HL": HL_setup, "setup_times_HL": HL_setup,
"hold_times_LH": LH_hold, "hold_times_LH": LH_hold,

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

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

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

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

View File

@ -48,7 +48,7 @@ class pattern(canvas._canvas, attr.exclusiveattr, style.fillstyle):
self.patternbbox = bbox self.patternbbox = bbox
self.patterntrafo = trafo self.patterntrafo = trafo
def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker, def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker,
bbox=_marker, trafo=_marker): bbox=_marker, trafo=_marker):
if painttype is _marker: if painttype is _marker:
painttype = self.painttype painttype = self.painttype

View File

@ -38,7 +38,7 @@ def set(epsilon=None):
def _rmatrix(angle): def _rmatrix(angle):
phi = math.pi*angle/180.0 phi = math.pi*angle/180.0
return ((math.cos(phi), -math.sin(phi)), return ((math.cos(phi), -math.sin(phi)),
(math.sin(phi), math.cos(phi))) (math.sin(phi), math.cos(phi)))
def _rvector(angle, x, y): def _rvector(angle, x, y):
@ -198,7 +198,7 @@ class trafo(trafo_pt):
epsilon=epsilon) epsilon=epsilon)
# #
# some standard transformations # some standard transformations
# #
class mirror(trafo): class mirror(trafo):
@ -219,7 +219,7 @@ class rotate_pt(trafo_pt):
class rotate(trafo_pt): class rotate(trafo_pt):
def __init__(self, angle, x=None, y=None, epsilon=_marker): def __init__(self, angle, x=None, y=None, epsilon=_marker):
vector = 0, 0 vector = 0, 0
if x is not None or y is not None: if x is not None or y is not None:
if x is None or y is None: if x is None or y is None:
raise TrafoException("either specify both x and y or none of them") raise TrafoException("either specify both x and y or none of them")

View File

@ -26,7 +26,7 @@ scale = { 't':1, 'u':1, 'v':1, 'w':1, 'x':1 }
_default_unit = "cm" _default_unit = "cm"
_m = { _m = {
'm' : 1, 'm' : 1,
'cm': 0.01, 'cm': 0.01,
'mm': 0.001, 'mm': 0.001,
@ -51,7 +51,7 @@ def set(uscale=None, vscale=None, wscale=None, xscale=None, defaultunit=None):
def _convert_to(l, dest_unit="m"): def _convert_to(l, dest_unit="m"):
if type(l) in (types.IntType, types.LongType, types.FloatType): if type(l) in (types.IntType, types.LongType, types.FloatType):
return l * _m[_default_unit] * scale['u'] / _m[dest_unit] return l * _m[_default_unit] * scale['u'] / _m[dest_unit]
elif not isinstance(l, length): elif not isinstance(l, length):
l = length(l) # convert to length instance if necessary l = length(l) # convert to length instance if necessary
return (l.t + l.u*scale['u'] + l.v*scale['v'] + l.w*scale['w'] + l.x*scale['x']) / _m[dest_unit] return (l.t + l.u*scale['u'] + l.v*scale['v'] + l.w*scale['w'] + l.x*scale['x']) / _m[dest_unit]

View File

@ -144,18 +144,18 @@ def check_versions():
minor_required = 5 minor_required = 5
if not (major_python_version == major_required and minor_python_version >= minor_required): if not (major_python_version == major_required and minor_python_version >= minor_required):
debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1) debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1)
# FIXME: Check versions of other tools here?? # FIXME: Check versions of other tools here??
# or, this could be done in each module (e.g. verify, characterizer, etc.) # or, this could be done in each module (e.g. verify, characterizer, etc.)
global OPTS global OPTS
try: try:
import coverage import coverage
OPTS.coverage = 1 OPTS.coverage = 1
except: except:
OPTS.coverage = 0 OPTS.coverage = 0
def init_openram(config_file, is_unit_test=True): def init_openram(config_file, is_unit_test=True):
""" Initialize the technology, paths, simulators, etc. """ """ Initialize the technology, paths, simulators, etc. """
@ -164,7 +164,7 @@ def init_openram(config_file, is_unit_test=True):
debug.info(1, "Initializing OpenRAM...") debug.info(1, "Initializing OpenRAM...")
setup_paths() setup_paths()
read_config(config_file, is_unit_test) read_config(config_file, is_unit_test)
import_tech() import_tech()
@ -188,7 +188,10 @@ def init_openram(config_file, is_unit_test=True):
if is_unit_test and CHECKPOINT_OPTS: if is_unit_test and CHECKPOINT_OPTS:
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy() OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
return return
# Setup correct bitcell names
setup_bitcell()
# Import these to find the executables for checkpointing # Import these to find the executables for checkpointing
import characterizer import characterizer
import verify import verify
@ -197,22 +200,19 @@ def init_openram(config_file, is_unit_test=True):
if not CHECKPOINT_OPTS: if not CHECKPOINT_OPTS:
CHECKPOINT_OPTS = copy.copy(OPTS) CHECKPOINT_OPTS = copy.copy(OPTS)
def setup_bitcell(): def setup_bitcell():
""" """
Determine the correct custom or parameterized bitcell for the design. Determine the correct custom or parameterized bitcell for the design.
""" """
global OPTS
# If we have non-1rw ports, # If we have non-1rw ports,
# and the user didn't over-ride the bitcell manually, # and the user didn't over-ride the bitcell manually,
# figure out the right bitcell to use # figure out the right bitcell to use
if (OPTS.bitcell == "bitcell"): if (OPTS.bitcell == "bitcell"):
if (OPTS.num_rw_ports == 1 and OPTS.num_w_ports == 0 and OPTS.num_r_ports == 0): if (OPTS.num_rw_ports == 1 and OPTS.num_w_ports == 0 and OPTS.num_r_ports == 0):
OPTS.bitcell = "bitcell" OPTS.bitcell = "bitcell"
OPTS.replica_bitcell = "replica_bitcell" OPTS.bitcell_name = "cell_6t"
OPTS.dummy_bitcell = "dummy_bitcell"
else: else:
ports = "" ports = ""
if OPTS.num_rw_ports > 0: if OPTS.num_rw_ports > 0:
@ -225,6 +225,20 @@ def setup_bitcell():
if ports != "": if ports != "":
OPTS.bitcell_suffix = "_" + ports OPTS.bitcell_suffix = "_" + ports
OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix
OPTS.bitcell_name = "cell" + OPTS.bitcell_suffix
OPTS.dummy_bitcell = "dummy_" + OPTS.bitcell
OPTS.dummy_bitcell_name = "dummy_" + OPTS.bitcell_name
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
OPTS.replica_bitcell_name = "replica_" + OPTS.bitcell_name
elif (OPTS.bitcell == "pbitcell"):
OPTS.bitcell = "pbitcell"
OPTS.bitcell_name = "pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.dummy_bitcell_name = "dummy_pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.replica_bitcell_name = "replica_pbitcell"
# See if bitcell exists # See if bitcell exists
try: try:
@ -234,6 +248,11 @@ def setup_bitcell():
# or its custom replica bitcell # or its custom replica bitcell
# Use the pbitcell (and give a warning if not in unit test mode) # Use the pbitcell (and give a warning if not in unit test mode)
OPTS.bitcell = "pbitcell" OPTS.bitcell = "pbitcell"
OPTS.bitcell_name = "pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.dummy_bitcell_name = "dummy_pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.replica_bitcell_name = "replica_pbitcell"
if not OPTS.is_unit_test: if not OPTS.is_unit_test:
debug.warning("Using the parameterized bitcell which may have suboptimal density.") debug.warning("Using the parameterized bitcell which may have suboptimal density.")
debug.info(1, "Using bitcell: {}".format(OPTS.bitcell)) debug.info(1, "Using bitcell: {}".format(OPTS.bitcell))
@ -269,7 +288,7 @@ def get_tool(tool_type, preferences, default_name=None):
else: else:
return(None, "") return(None, "")
def read_config(config_file, is_unit_test=True): def read_config(config_file, is_unit_test=True):
""" """
Read the configuration file that defines a few parameters. The Read the configuration file that defines a few parameters. The
@ -282,14 +301,14 @@ def read_config(config_file, is_unit_test=True):
# it is already not an abs path, make it one # it is already not an abs path, make it one
if not os.path.isabs(config_file): if not os.path.isabs(config_file):
config_file = os.getcwd() + "/" + config_file config_file = os.getcwd() + "/" + config_file
# Make it a python file if the base name was only given # Make it a python file if the base name was only given
config_file = re.sub(r'\.py$', "", config_file) config_file = re.sub(r'\.py$', "", config_file)
# Expand the user if it is used # Expand the user if it is used
config_file = os.path.expanduser(config_file) config_file = os.path.expanduser(config_file)
OPTS.config_file = config_file + ".py" OPTS.config_file = config_file + ".py"
# Add the path to the system path # Add the path to the system path
# so we can import things in the other directory # so we can import things in the other directory
@ -328,7 +347,7 @@ def read_config(config_file, is_unit_test=True):
# If we are only generating a netlist, we can't do DRC/LVS # If we are only generating a netlist, we can't do DRC/LVS
if OPTS.netlist_only: if OPTS.netlist_only:
OPTS.check_lvsdrc = False OPTS.check_lvsdrc = False
# If config didn't set output name, make a reasonable default. # If config didn't set output name, make a reasonable default.
if (OPTS.output_name == ""): if (OPTS.output_name == ""):
ports = "" ports = ""
@ -343,7 +362,7 @@ def read_config(config_file, is_unit_test=True):
ports, ports,
OPTS.tech_name) OPTS.tech_name)
def end_openram(): def end_openram():
""" Clean up openram for a proper exit """ """ Clean up openram for a proper exit """
cleanup_paths() cleanup_paths()
@ -353,8 +372,8 @@ def end_openram():
verify.print_drc_stats() verify.print_drc_stats()
verify.print_lvs_stats() verify.print_lvs_stats()
verify.print_pex_stats() verify.print_pex_stats()
def cleanup_paths(): def cleanup_paths():
""" """
We should clean up the temp directory after execution. We should clean up the temp directory after execution.
@ -376,8 +395,8 @@ def cleanup_paths():
os.remove(i) os.remove(i)
else: else:
shutil.rmtree(i) shutil.rmtree(i)
def setup_paths(): def setup_paths():
""" Set up the non-tech related paths. """ """ Set up the non-tech related paths. """
debug.info(2, "Setting up paths...") debug.info(2, "Setting up paths...")
@ -400,7 +419,7 @@ def setup_paths():
debug.check(os.path.isdir(full_path), debug.check(os.path.isdir(full_path),
"$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path)) "$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path))
if "__pycache__" not in full_path: if "__pycache__" not in full_path:
sys.path.append("{0}".format(full_path)) sys.path.append("{0}".format(full_path))
if not OPTS.openram_temp.endswith('/'): if not OPTS.openram_temp.endswith('/'):
OPTS.openram_temp += "/" OPTS.openram_temp += "/"
@ -413,9 +432,9 @@ def is_exe(fpath):
def find_exe(check_exe): def find_exe(check_exe):
""" """
Check if the binary exists in any path dir Check if the binary exists in any path dir
and return the full path. and return the full path.
""" """
# Check if the preferred spice option exists in the path # Check if the preferred spice option exists in the path
for path in os.environ["PATH"].split(os.pathsep): for path in os.environ["PATH"].split(os.pathsep):
@ -437,7 +456,7 @@ def init_paths():
except OSError as e: except OSError as e:
if e.errno == 17: # errno.EEXIST if e.errno == 17: # errno.EEXIST
os.chmod(OPTS.openram_temp, 0o750) os.chmod(OPTS.openram_temp, 0o750)
# Don't delete the output dir, it may have other files! # Don't delete the output dir, it may have other files!
# make the directory if it doesn't exist # make the directory if it doesn't exist
try: try:
@ -448,7 +467,7 @@ def init_paths():
except: except:
debug.error("Unable to make output directory.", -1) debug.error("Unable to make output directory.", -1)
def set_default_corner(): def set_default_corner():
""" Set the default corner. """ """ Set the default corner. """
@ -459,13 +478,13 @@ def set_default_corner():
OPTS.process_corners = ["TT"] OPTS.process_corners = ["TT"]
else: else:
OPTS.process_corners = tech.spice["fet_models"].keys() OPTS.process_corners = tech.spice["fet_models"].keys()
if (OPTS.supply_voltages == ""): if (OPTS.supply_voltages == ""):
if OPTS.nominal_corner_only: if OPTS.nominal_corner_only:
OPTS.supply_voltages = [tech.spice["supply_voltages"][1]] OPTS.supply_voltages = [tech.spice["supply_voltages"][1]]
else: else:
OPTS.supply_voltages = tech.spice["supply_voltages"] OPTS.supply_voltages = tech.spice["supply_voltages"]
if (OPTS.temperatures == ""): if (OPTS.temperatures == ""):
if OPTS.nominal_corner_only: if OPTS.nominal_corner_only:
OPTS.temperatures = [tech.spice["temperatures"][1]] OPTS.temperatures = [tech.spice["temperatures"][1]]
@ -479,8 +498,8 @@ def set_default_corner():
# Load scales are fanout multiples of the default spice input slew # Load scales are fanout multiples of the default spice input slew
if (OPTS.slew_scales == ""): if (OPTS.slew_scales == ""):
OPTS.slew_scales = [0.25, 1, 8] OPTS.slew_scales = [0.25, 1, 8]
def import_tech(): def import_tech():
""" Dynamically adds the tech directory to the path and imports it. """ """ Dynamically adds the tech directory to the path and imports it. """
global OPTS global OPTS
@ -501,7 +520,7 @@ def import_tech():
sys.path.append(tech_path) sys.path.append(tech_path)
debug.info(1, "Adding technology path: {}".format(tech_path)) debug.info(1, "Adding technology path: {}".format(tech_path))
# Import the tech # Import the tech
try: try:
tech_mod = __import__(OPTS.tech_name) tech_mod = __import__(OPTS.tech_name)
except ImportError: except ImportError:
@ -526,7 +545,7 @@ def import_tech():
def print_time(name, now_time, last_time=None, indentation=2): def print_time(name, now_time, last_time=None, indentation=2):
""" Print a statement about the time delta. """ """ Print a statement about the time delta. """
global OPTS global OPTS
# Don't print during testing # Don't print during testing
if not OPTS.is_unit_test or OPTS.debug_level > 0: if not OPTS.is_unit_test or OPTS.debug_level > 0:
if last_time: if last_time:
@ -537,12 +556,12 @@ def print_time(name, now_time, last_time=None, indentation=2):
def report_status(): def report_status():
""" """
Check for valid arguments and report the Check for valid arguments and report the
info about the SRAM being generated info about the SRAM being generated
""" """
global OPTS global OPTS
# Check if all arguments are integers for bits, size, banks # Check if all arguments are integers for bits, size, banks
if type(OPTS.word_size) != int: if type(OPTS.word_size) != int:
debug.error("{0} is not an integer in config file.".format(OPTS.word_size)) debug.error("{0} is not an integer in config file.".format(OPTS.word_size))
@ -550,7 +569,7 @@ def report_status():
debug.error("{0} is not an integer in config file.".format(OPTS.sram_size)) debug.error("{0} is not an integer in config file.".format(OPTS.sram_size))
if type(OPTS.write_size) is not int and OPTS.write_size is not None: if type(OPTS.write_size) is not int and OPTS.write_size is not None:
debug.error("{0} is not an integer in config file.".format(OPTS.write_size)) debug.error("{0} is not an integer in config file.".format(OPTS.write_size))
# If a write mask is specified by the user, the mask write size should be the same as # If a write mask is specified by the user, the mask write size should be the same as
# the word size so that an entire word is written at once. # the word size so that an entire word is written at once.
if OPTS.write_size is not None: if OPTS.write_size is not None:
@ -582,10 +601,10 @@ def report_status():
if OPTS.netlist_only: if OPTS.netlist_only:
debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).") debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).")
if not OPTS.route_supplies: if not OPTS.route_supplies:
debug.print_raw("Design supply routing skipped. Supplies will have multiple must-connect pins. (route_supplies=True to enable supply routing).") debug.print_raw("Design supply routing skipped. Supplies will have multiple must-connect pins. (route_supplies=True to enable supply routing).")
if not OPTS.inline_lvsdrc: if not OPTS.inline_lvsdrc:
debug.print_raw("DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to do inline checking).") debug.print_raw("DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to do inline checking).")

View File

@ -18,18 +18,18 @@ class and2_dec(design.design):
This is an AND with configurable drive strength. This is an AND with configurable drive strength.
""" """
def __init__(self, name, size=1, height=None, add_wells=True): def __init__(self, name, size=1, height=None, add_wells=True):
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating and2_dec {}".format(name)) debug.info(1, "Creating and2_dec {}".format(name))
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
self.size = size self.size = size
self.height = height self.height = height
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.create_modules() self.create_modules()
@ -38,14 +38,14 @@ class and2_dec(design.design):
def create_modules(self): def create_modules(self):
self.nand = factory.create(module_type="nand2_dec", self.nand = factory.create(module_type="nand2_dec",
height=self.height) height=self.height)
self.inv = factory.create(module_type="inv_dec", self.inv = factory.create(module_type="inv_dec",
height=self.height, height=self.height,
size=self.size) size=self.size)
self.add_mod(self.nand) self.add_mod(self.nand)
self.add_mod(self.inv) self.add_mod(self.inv)
def create_layout(self): def create_layout(self):
if "li" in layer: if "li" in layer:
@ -54,14 +54,14 @@ class and2_dec(design.design):
self.route_layer = "m1" self.route_layer = "m1"
self.width = self.nand.width + self.inv.width self.width = self.nand.width + self.inv.width
self.height = self.nand.height self.height = self.nand.height
self.place_insts() self.place_insts()
self.add_wires() self.add_wires()
self.add_layout_pins() self.add_layout_pins()
self.route_supply_rails() self.route_supply_rails()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.add_pin("A", "INPUT") self.add_pin("A", "INPUT")
self.add_pin("B", "INPUT") self.add_pin("B", "INPUT")
@ -73,7 +73,7 @@ class and2_dec(design.design):
self.nand_inst = self.add_inst(name="pand2_dec_nand", self.nand_inst = self.add_inst(name="pand2_dec_nand",
mod=self.nand) mod=self.nand)
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
self.inv_inst = self.add_inst(name="pand2_dec_inv", self.inv_inst = self.add_inst(name="pand2_dec_inv",
mod=self.inv) mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
@ -100,7 +100,7 @@ class and2_dec(design.design):
layer=self.route_layer, layer=self.route_layer,
offset=vector(0.5 * self.width, self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def add_wires(self): def add_wires(self):
# nand Z to inv A # nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z") z1_pin = self.nand_inst.get_pin("Z")
@ -111,7 +111,7 @@ class and2_dec(design.design):
mid1_point = vector(z1_pin.cx(), a2_pin.cy()) mid1_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path(self.route_layer, self.add_path(self.route_layer,
[z1_pin.center(), mid1_point, a2_pin.center()]) [z1_pin.center(), mid1_point, a2_pin.center()])
def add_layout_pins(self): def add_layout_pins(self):
pin = self.inv_inst.get_pin("Z") pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
@ -127,7 +127,7 @@ class and2_dec(design.design):
offset=pin.center(), offset=pin.center(),
width=pin.width(), width=pin.width(),
height=pin.height()) height=pin.height())
def get_stage_efforts(self, external_cout, inp_is_rise=False): def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A or B -> Z path""" """Get the stage efforts of the A or B -> Z path"""
stage_effort_list = [] stage_effort_list = []
@ -135,13 +135,13 @@ class and2_dec(design.design):
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
stage_effort_list.append(stage1) stage_effort_list.append(stage1)
last_stage_is_rise = stage1.is_rise last_stage_is_rise = stage1.is_rise
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
stage_effort_list.append(stage2) stage_effort_list.append(stage2)
return stage_effort_list return stage_effort_list
def get_cin(self): def get_cin(self):
"""Return the relative input capacitance of a single input""" """Return the relative input capacitance of a single input"""
return self.nand.get_cin() return self.nand.get_cin()

View File

@ -23,7 +23,7 @@ class and3_dec(design.design):
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
self.size = size self.size = size
self.height = height self.height = height
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -52,14 +52,14 @@ class and3_dec(design.design):
self.width = self.nand.width + self.inv.width self.width = self.nand.width + self.inv.width
self.height = self.nand.height self.height = self.nand.height
self.place_insts() self.place_insts()
self.add_wires() self.add_wires()
self.add_layout_pins() self.add_layout_pins()
self.route_supply_rails() self.route_supply_rails()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.add_pin("A", "INPUT") self.add_pin("A", "INPUT")
self.add_pin("B", "INPUT") self.add_pin("B", "INPUT")
@ -72,7 +72,7 @@ class and3_dec(design.design):
self.nand_inst = self.add_inst(name="pand3_dec_nand", self.nand_inst = self.add_inst(name="pand3_dec_nand",
mod=self.nand) mod=self.nand)
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
self.inv_inst = self.add_inst(name="pand3_dec_inv", self.inv_inst = self.add_inst(name="pand3_dec_inv",
mod=self.inv) mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
@ -99,7 +99,7 @@ class and3_dec(design.design):
layer=self.route_layer, layer=self.route_layer,
offset=vector(0.5 * self.width, self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def add_wires(self): def add_wires(self):
# nand Z to inv A # nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z") z1_pin = self.nand_inst.get_pin("Z")
@ -136,7 +136,7 @@ class and3_dec(design.design):
slew=nand_delay.slew, slew=nand_delay.slew,
load=load) load=load)
return nand_delay + inv_delay return nand_delay + inv_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False): def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A or B -> Z path""" """Get the stage efforts of the A or B -> Z path"""
stage_effort_list = [] stage_effort_list = []
@ -144,13 +144,13 @@ class and3_dec(design.design):
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
stage_effort_list.append(stage1) stage_effort_list.append(stage1)
last_stage_is_rise = stage1.is_rise last_stage_is_rise = stage1.is_rise
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
stage_effort_list.append(stage2) stage_effort_list.append(stage2)
return stage_effort_list return stage_effort_list
def get_cin(self): def get_cin(self):
"""Return the relative input capacitance of a single input""" """Return the relative input capacitance of a single input"""
return self.nand.get_cin() return self.nand.get_cin()

View File

@ -18,14 +18,14 @@ class and4_dec(design.design):
This is an AND with configurable drive strength. This is an AND with configurable drive strength.
""" """
def __init__(self, name, size=1, height=None, add_wells=True): def __init__(self, name, size=1, height=None, add_wells=True):
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating and4_dec {}".format(name)) debug.info(1, "Creating and4_dec {}".format(name))
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
self.size = size self.size = size
self.height = height self.height = height
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -61,7 +61,7 @@ class and4_dec(design.design):
self.route_supply_rails() self.route_supply_rails()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.add_pin("A", "INPUT") self.add_pin("A", "INPUT")
self.add_pin("B", "INPUT") self.add_pin("B", "INPUT")
@ -75,7 +75,7 @@ class and4_dec(design.design):
self.nand_inst = self.add_inst(name="pand4_dec_nand", self.nand_inst = self.add_inst(name="pand4_dec_nand",
mod=self.nand) mod=self.nand)
self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"]) self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"])
self.inv_inst = self.add_inst(name="pand4_dec_inv", self.inv_inst = self.add_inst(name="pand4_dec_inv",
mod=self.inv) mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
@ -102,7 +102,7 @@ class and4_dec(design.design):
layer=self.route_layer, layer=self.route_layer,
offset=vector(0.5 * self.width, self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def add_wires(self): def add_wires(self):
# nand Z to inv A # nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z") z1_pin = self.nand_inst.get_pin("Z")
@ -139,7 +139,7 @@ class and4_dec(design.design):
slew=nand_delay.slew, slew=nand_delay.slew,
load=load) load=load)
return nand_delay + inv_delay return nand_delay + inv_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False): def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A or B -> Z path""" """Get the stage efforts of the A or B -> Z path"""
stage_effort_list = [] stage_effort_list = []
@ -147,13 +147,13 @@ class and4_dec(design.design):
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
stage_effort_list.append(stage1) stage_effort_list.append(stage1)
last_stage_is_rise = stage1.is_rise last_stage_is_rise = stage1.is_rise
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
stage_effort_list.append(stage2) stage_effort_list.append(stage2)
return stage_effort_list return stage_effort_list
def get_cin(self): def get_cin(self):
"""Return the relative input capacitance of a single input""" """Return the relative input capacitance of a single input"""
return self.nand.get_cin() return self.nand.get_cin()

View File

@ -31,16 +31,16 @@ class bank(design.design):
self.num_wmasks = int(ceil(self.word_size / self.write_size)) self.num_wmasks = int(ceil(self.word_size / self.write_size))
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
if not self.num_spare_cols: if not self.num_spare_cols:
self.num_spare_cols = 0 self.num_spare_cols = 0
if name == "": if name == "":
name = "bank_{0}_{1}".format(self.word_size, self.num_words) name = "bank_{0}_{1}".format(self.word_size, self.num_words)
super().__init__(name) super().__init__(name)
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size, debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
self.num_words)) self.num_words))
# The local control signals are gated when we have bank select logic, # The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the input signals to create # so this prefix will be added to all of the input signals to create
# the internal gated signals. # the internal gated signals.
@ -62,12 +62,12 @@ class bank(design.design):
self.add_modules() self.add_modules()
self.add_pins() # Must create the replica bitcell array first self.add_pins() # Must create the replica bitcell array first
self.create_instances() self.create_instances()
def create_layout(self): def create_layout(self):
self.place_instances() self.place_instances()
self.setup_routing_constraints() self.setup_routing_constraints()
self.route_layout() self.route_layout()
# Can remove the following, but it helps for debug! # Can remove the following, but it helps for debug!
# self.add_lvs_correspondence_points() # self.add_lvs_correspondence_points()
@ -110,13 +110,13 @@ class bank(design.design):
self.add_pin("wl_en{0}".format(port), "INPUT") self.add_pin("wl_en{0}".format(port), "INPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def route_layout(self): def route_layout(self):
""" Create routing amoung the modules """ """ Create routing amoung the modules """
self.route_central_bus() self.route_central_bus()
self.route_unused_wordlines() self.route_unused_wordlines()
for port in self.all_ports: for port in self.all_ports:
self.route_bitlines(port) self.route_bitlines(port)
self.route_rbl(port) self.route_rbl(port)
@ -125,7 +125,7 @@ class bank(design.design):
self.route_control_lines(port) self.route_control_lines(port)
if self.num_banks > 1: if self.num_banks > 1:
self.route_bank_select(port) self.route_bank_select(port)
self.route_supplies() self.route_supplies()
def route_rbl(self, port): def route_rbl(self, port):
@ -149,7 +149,7 @@ class bank(design.design):
layer="m3", layer="m3",
start=left_right_offset, start=left_right_offset,
end=pin_offset) end=pin_offset)
def route_bitlines(self, port): def route_bitlines(self, port):
""" Route the bitlines depending on the port type rw, w, or r. """ """ Route the bitlines depending on the port type rw, w, or r. """
@ -158,7 +158,7 @@ class bank(design.design):
if port in self.read_ports: if port in self.read_ports:
self.route_port_data_out(port) self.route_port_data_out(port)
self.route_port_data_to_bitcell_array(port) self.route_port_data_to_bitcell_array(port)
def create_instances(self): def create_instances(self):
""" Create the instances of the netlist. """ """ Create the instances of the netlist. """
@ -185,7 +185,7 @@ class bank(design.design):
# The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis. # The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
self.bitcell_array_top = self.bitcell_array.height self.bitcell_array_top = self.bitcell_array.height
self.bitcell_array_right = self.bitcell_array.width self.bitcell_array_right = self.bitcell_array.width
# These are the offsets of the main array (excluding dummy and replica rows/cols) # These are the offsets of the main array (excluding dummy and replica rows/cols)
self.main_bitcell_array_top = self.bitcell_array.get_main_array_top() self.main_bitcell_array_top = self.bitcell_array.get_main_array_top()
# Just past the dummy column # Just past the dummy column
@ -213,7 +213,7 @@ class bank(design.design):
""" """
port = 0 port = 0
# UPPER RIGHT QUADRANT # UPPER RIGHT QUADRANT
# Bitcell array is placed at (0,0) # Bitcell array is placed at (0,0)
self.bitcell_array_offset = vector(0, 0) self.bitcell_array_offset = vector(0, 0)
@ -258,14 +258,14 @@ class bank(design.design):
""" """
port=1 port=1
# LOWER LEFT QUADRANT # LOWER LEFT QUADRANT
# Bitcell array is placed at (0,0) # Bitcell array is placed at (0,0)
# UPPER LEFT QUADRANT # UPPER LEFT QUADRANT
# Above the bitcell array # Above the bitcell array
self.port_data_offsets[port] = vector(0, self.bitcell_array_top) self.port_data_offsets[port] = vector(0, self.bitcell_array_top)
# LOWER RIGHT QUADRANT # LOWER RIGHT QUADRANT
# To the right of the bitcell array # To the right of the bitcell array
x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap
@ -294,12 +294,12 @@ class bank(design.design):
else: else:
y_offset = self.port_address_offsets[port].y y_offset = self.port_address_offsets[port].y
self.bank_select_offsets[port] = vector(x_offset, y_offset) self.bank_select_offsets[port] = vector(x_offset, y_offset)
def place_instances(self): def place_instances(self):
""" Place the instances. """ """ Place the instances. """
self.compute_instance_offsets() self.compute_instance_offsets()
self.place_bitcell_array(self.bitcell_array_offset) self.place_bitcell_array(self.bitcell_array_offset)
self.place_port_data(self.port_data_offsets) self.place_port_data(self.port_data_offsets)
@ -308,7 +308,7 @@ class bank(design.design):
self.place_column_decoder(self.column_decoder_offsets) self.place_column_decoder(self.column_decoder_offsets)
self.place_bank_select(self.bank_select_offsets) self.place_bank_select(self.bank_select_offsets)
def compute_sizes(self): def compute_sizes(self):
""" Computes the required sizes to create the bank """ """ Computes the required sizes to create the bank """
@ -351,7 +351,7 @@ class bank(design.design):
self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]]) self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]])
else: else:
self.control_signals.append(self.input_control_signals[port]) self.control_signals.append(self.input_control_signals[port])
# The central bus is the column address (one hot) and row address (binary) # The central bus is the column address (one hot) and row address (binary)
if self.col_addr_size>0: if self.col_addr_size>0:
@ -379,7 +379,7 @@ class bank(design.design):
local_array_size = OPTS.local_array_size local_array_size = OPTS.local_array_size
except AttributeError: except AttributeError:
local_array_size = 0 local_array_size = 0
if local_array_size > 0: if local_array_size > 0:
# Find the even multiple that satisfies the fanout with equal sized local arrays # Find the even multiple that satisfies the fanout with equal sized local arrays
total_cols = self.num_cols + self.num_spare_cols total_cols = self.num_cols + self.num_spare_cols
@ -406,11 +406,11 @@ class bank(design.design):
bit_offsets=self.bit_offsets) bit_offsets=self.bit_offsets)
self.port_data.append(temp_pre) self.port_data.append(temp_pre)
self.add_mod(self.port_data[port]) self.add_mod(self.port_data[port])
if(self.num_banks > 1): if(self.num_banks > 1):
self.bank_select = factory.create(module_type="bank_select") self.bank_select = factory.create(module_type="bank_select")
self.add_mod(self.bank_select) self.add_mod(self.bank_select)
def create_bitcell_array(self): def create_bitcell_array(self):
""" Creating Bitcell Array """ """ Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="bitcell_array", self.bitcell_array_inst=self.add_inst(name="bitcell_array",
@ -426,12 +426,12 @@ class bank(design.design):
temp.extend(self.bitcell_array.get_wordline_names()) temp.extend(self.bitcell_array.get_wordline_names())
if len(self.all_ports) > 1: if len(self.all_ports) > 1:
temp.append("rbl_wl1") temp.append("rbl_wl1")
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place_bitcell_array(self, offset): def place_bitcell_array(self, offset):
""" Placing Bitcell Array """ """ Placing Bitcell Array """
self.bitcell_array_inst.place(offset) self.bitcell_array_inst.place(offset)
@ -470,7 +470,7 @@ class bank(design.design):
def place_port_data(self, offsets): def place_port_data(self, offsets):
""" Placing Port Data """ """ Placing Port Data """
for port in self.all_ports: for port in self.all_ports:
# Top one is unflipped, bottom is flipped along X direction # Top one is unflipped, bottom is flipped along X direction
if port % 2 == 1: if port % 2 == 1:
@ -481,7 +481,7 @@ class bank(design.design):
def create_port_address(self): def create_port_address(self):
""" Create the hierarchical row decoder """ """ Create the hierarchical row decoder """
self.port_address_inst = [None] * len(self.all_ports) self.port_address_inst = [None] * len(self.all_ports)
for port in self.all_ports: for port in self.all_ports:
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port), self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
@ -496,32 +496,32 @@ class bank(design.design):
temp.append("rbl_wl{}".format(port)) temp.append("rbl_wl{}".format(port))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_port_address(self, offsets): def place_port_address(self, offsets):
""" Place the hierarchical row decoder """ """ Place the hierarchical row decoder """
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.") debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.")
# The address and control bus will be in between decoder and the main memory array # The address and control bus will be in between decoder and the main memory array
# This bus will route address bits to the decoder input and column mux inputs. # This bus will route address bits to the decoder input and column mux inputs.
# The wires are actually routed after we placed the stuff on both sides. # The wires are actually routed after we placed the stuff on both sides.
# The predecoder is below the x-axis and the main decoder is above the x-axis # The predecoder is below the x-axis and the main decoder is above the x-axis
# The address flop and decoder are aligned in the x coord. # The address flop and decoder are aligned in the x coord.
for port in self.all_ports: for port in self.all_ports:
if port % 2: if port % 2:
mirror = "MY" mirror = "MY"
else: else:
mirror = "R0" mirror = "R0"
self.port_address_inst[port].place(offset=offsets[port], mirror=mirror) self.port_address_inst[port].place(offset=offsets[port], mirror=mirror)
def create_column_decoder(self): def create_column_decoder(self):
""" """
Create a 2:4 or 3:8 column address decoder. Create a 2:4 or 3:8 column address decoder.
""" """
self.dff =factory.create(module_type="dff") self.dff =factory.create(module_type="dff")
if self.col_addr_size == 0: if self.col_addr_size == 0:
return return
elif self.col_addr_size == 1: elif self.col_addr_size == 1:
@ -541,7 +541,7 @@ class bank(design.design):
# No error checking before? # No error checking before?
debug.error("Invalid column decoder?", -1) debug.error("Invalid column decoder?", -1)
self.add_mod(self.column_decoder) self.add_mod(self.column_decoder)
self.column_decoder_inst = [None] * len(self.all_ports) self.column_decoder_inst = [None] * len(self.all_ports)
for port in self.all_ports: for port in self.all_ports:
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port), self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
@ -554,14 +554,14 @@ class bank(design.design):
temp.append("sel{0}_{1}".format(port, bit)) temp.append("sel{0}_{1}".format(port, bit))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_column_decoder(self, offsets): def place_column_decoder(self, offsets):
""" """
Place a 2:4 or 3:8 column address decoder. Place a 2:4 or 3:8 column address decoder.
""" """
if self.col_addr_size == 0: if self.col_addr_size == 0:
return return
debug.check(len(offsets)>=len(self.all_ports), debug.check(len(offsets)>=len(self.all_ports),
"Insufficient offsets to place column decoder.") "Insufficient offsets to place column decoder.")
@ -571,7 +571,7 @@ class bank(design.design):
else: else:
mirror = "R0" mirror = "R0"
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror) self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
def create_bank_select(self): def create_bank_select(self):
""" Create the bank select logic. """ """ Create the bank select logic. """
@ -582,7 +582,7 @@ class bank(design.design):
for port in self.all_ports: for port in self.all_ports:
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port), self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
mod=self.bank_select) mod=self.bank_select)
temp = [] temp = []
temp.extend(self.input_control_signals[port]) temp.extend(self.input_control_signals[port])
temp.append("bank_sel{}".format(port)) temp.append("bank_sel{}".format(port))
@ -601,7 +601,7 @@ class bank(design.design):
for port in self.all_ports: for port in self.all_ports:
self.bank_select_inst[port].place(offsets[port]) self.bank_select_inst[port].place(offsets[port])
def route_supplies(self): def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """ """ Propagate all vdd/gnd pins up to this level for all modules """
# Copy only the power pins already on the power layer # Copy only the power pins already on the power layer
@ -624,7 +624,7 @@ class bank(design.design):
def route_bank_select(self, port): def route_bank_select(self, port):
""" Route the bank select logic. """ """ Route the bank select logic. """
if self.port_id[port] == "rw": if self.port_id[port] == "rw":
bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"] bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"] gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
@ -634,11 +634,11 @@ class bank(design.design):
else: else:
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"] bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"] gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)] copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)]
for signal in range(len(copy_control_signals)): for signal in range(len(copy_control_signals)):
self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal]) self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
for signal in range(len(gated_bank_sel_signals)): for signal in range(len(gated_bank_sel_signals)):
# Connect the inverter output to the central bus # Connect the inverter output to the central bus
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc() out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
@ -651,7 +651,7 @@ class bank(design.design):
offset=out_pos) offset=out_pos)
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=out_pos) offset=out_pos)
def setup_routing_constraints(self): def setup_routing_constraints(self):
""" """
After the modules are instantiated, find the dimensions for the After the modules are instantiated, find the dimensions for the
@ -660,7 +660,7 @@ class bank(design.design):
self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width
self.min_y_offset = min([x.by() for x in self.insts]) self.min_y_offset = min([x.by() for x in self.insts])
self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width
self.min_x_offset = min([x.lx() for x in self.insts]) self.min_x_offset = min([x.lx() for x in self.insts])
@ -668,7 +668,7 @@ class bank(design.design):
ur = vector(self.max_x_offset, self.max_y_offset) ur = vector(self.max_x_offset, self.max_y_offset)
ll = vector(self.min_x_offset, self.min_y_offset) ll = vector(self.min_x_offset, self.min_y_offset)
self.core_bbox = [ll, ur] self.core_bbox = [ll, ur]
self.height = ur.y - ll.y self.height = ur.y - ll.y
self.width = ur.x - ll.x self.width = ur.x - ll.x
@ -692,7 +692,7 @@ class bank(design.design):
vertical=True, vertical=True,
make_pins=(self.num_banks==1), make_pins=(self.num_banks==1),
pitch=self.m3_pitch) pitch=self.m3_pitch)
# Port 1 # Port 1
if len(self.all_ports)==2: if len(self.all_ports)==2:
# The other control bus is routed up to two pitches above the bitcell array # The other control bus is routed up to two pitches above the bitcell array
@ -731,12 +731,12 @@ class bank(design.design):
inst1_br_name=inst1_br_name, inst1_br_name=inst1_br_name,
inst2_bl_name=inst2_bl_name, inst2_bl_name=inst2_bl_name,
inst2_br_name=inst2_br_name) inst2_br_name=inst2_br_name)
# Connect the replica bitlines # Connect the replica bitlines
for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]): for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]):
self.connect_bitline(inst1, inst2, array_name, data_name) self.connect_bitline(inst1, inst2, array_name, data_name)
def route_port_data_out(self, port): def route_port_data_out(self, port):
""" Add pins for the port data out """ """ Add pins for the port data out """
@ -747,7 +747,7 @@ class bank(design.design):
offset=data_pin.center(), offset=data_pin.center(),
height=data_pin.height(), height=data_pin.height(),
width=data_pin.width()) width=data_pin.width())
def route_port_address_in(self, port): def route_port_address_in(self, port):
""" Routes the row decoder inputs and supplies """ """ Routes the row decoder inputs and supplies """
@ -771,19 +771,19 @@ class bank(design.design):
wmask_name = "bank_wmask_{}".format(row) wmask_name = "bank_wmask_{}".format(row)
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row) bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name) self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
for col in range(self.num_spare_cols): for col in range(self.num_spare_cols):
sparecol_name = "bank_spare_wen{}".format(col) sparecol_name = "bank_spare_wen{}".format(col)
bank_sparecol_name = "bank_spare_wen{0}_{1}".format(port, col) bank_sparecol_name = "bank_spare_wen{0}_{1}".format(port, col)
self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name) self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name)
def channel_route_bitlines(self, inst1, inst2, num_bits, def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"): inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
""" """
Route the bl and br of two modules using the channel router. Route the bl and br of two modules using the channel router.
""" """
# determine top and bottom automatically. # determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate. # since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by(): if inst1.by() < inst2.by():
@ -801,14 +801,14 @@ class bank(design.design):
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))] top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
route_map = list(zip(bottom_names, top_names)) route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset, self.m1_stack) self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name): def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
""" """
Connect two pins of two modules. Connect two pins of two modules.
This assumes that they have sufficient space to create a jog This assumes that they have sufficient space to create a jog
in the middle between the two modules (if needed). in the middle between the two modules (if needed).
""" """
# determine top and bottom automatically. # determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate. # since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by(): if inst1.by() < inst2.by():
@ -821,17 +821,17 @@ class bank(design.design):
bottom_pin = bottom_inst.get_pin(bottom_name) bottom_pin = bottom_inst.get_pin(bottom_name)
top_pin = top_inst.get_pin(top_name) top_pin = top_inst.get_pin(top_name)
debug.check(bottom_pin.layer == top_pin.layer, "Pin layers do not match.") debug.check(bottom_pin.layer == top_pin.layer, "Pin layers do not match.")
bottom_loc = bottom_pin.uc() bottom_loc = bottom_pin.uc()
top_loc = top_pin.bc() top_loc = top_pin.bc()
yoffset = 0.5 * (top_loc.y + bottom_loc.y) yoffset = 0.5 * (top_loc.y + bottom_loc.y)
self.add_path(top_pin.layer, self.add_path(top_pin.layer,
[bottom_loc, [bottom_loc,
vector(bottom_loc.x, yoffset), vector(bottom_loc.x, yoffset),
vector(top_loc.x, yoffset), vector(top_loc.x, yoffset),
top_loc]) top_loc])
def connect_bitlines(self, inst1, inst2, def connect_bitlines(self, inst1, inst2,
inst1_bl_name, inst1_br_name, inst1_bl_name, inst1_br_name,
inst2_bl_name, inst2_br_name): inst2_bl_name, inst2_br_name):
@ -847,12 +847,12 @@ class bank(design.design):
""" Connect Wordline driver to bitcell array wordline """ """ Connect Wordline driver to bitcell array wordline """
self.route_port_address_in(port) self.route_port_address_in(port)
if port % 2: if port % 2:
self.route_port_address_out(port, "right") self.route_port_address_out(port, "right")
else: else:
self.route_port_address_out(port, "left") self.route_port_address_out(port, "left")
def route_port_address_out(self, port, side="left"): def route_port_address_out(self, port, side="left"):
""" Connecting Wordline driver output to Bitcell WL connection """ """ Connecting Wordline driver output to Bitcell WL connection """
@ -866,7 +866,7 @@ class bank(design.design):
else: else:
driver_wl_pos = driver_wl_pin.lc() driver_wl_pos = driver_wl_pin.lc()
bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name) bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name)
if side == "left": if side == "left":
bitcell_wl_pos = bitcell_wl_pin.lc() bitcell_wl_pos = bitcell_wl_pin.lc()
port_address_pos = self.port_address_inst[port].rx() port_address_pos = self.port_address_inst[port].rx()
@ -875,7 +875,7 @@ class bank(design.design):
bitcell_wl_pos = bitcell_wl_pin.rc() bitcell_wl_pos = bitcell_wl_pin.rc()
port_address_pos = self.port_address_inst[port].lx() port_address_pos = self.port_address_inst[port].lx()
bitcell_array_pos = self.bitcell_array_inst.rx() bitcell_array_pos = self.bitcell_array_inst.rx()
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0) mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1) mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
if driver_wl_pin.layer != bitcell_wl_pin.layer: if driver_wl_pin.layer != bitcell_wl_pin.layer:
@ -886,7 +886,7 @@ class bank(design.design):
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos]) self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
else: else:
self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_port_address_right(self, port): def route_port_address_right(self, port):
""" Connecting Wordline driver output to Bitcell WL connection """ """ Connecting Wordline driver output to Bitcell WL connection """
@ -906,7 +906,7 @@ class bank(design.design):
to_layer=bitcell_wl_pin.layer, to_layer=bitcell_wl_pin.layer,
offset=mid2) offset=mid2)
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos]) self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
def route_column_address_lines(self, port): def route_column_address_lines(self, port):
""" Connecting the select lines of column mux to the address bus """ """ Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0: if not self.col_addr_size>0:
@ -914,15 +914,15 @@ class bank(design.design):
stack = getattr(self, layer_props.bank.stack) stack = getattr(self, layer_props.bank.stack)
pitch = getattr(self, layer_props.bank.pitch) pitch = getattr(self, layer_props.bank.pitch)
if self.col_addr_size == 1: if self.col_addr_size == 1:
# Connect to sel[0] and sel[1] # Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"] decode_names = ["Zb", "Z"]
# The Address LSB # The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1: elif self.col_addr_size > 1:
decode_names = [] decode_names = []
for i in range(self.num_col_addr_lines): for i in range(self.num_col_addr_lines):
@ -942,7 +942,7 @@ class bank(design.design):
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names] column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
route_map = list(zip(decode_pins, column_mux_pins)) route_map = list(zip(decode_pins, column_mux_pins))
self.create_vertical_channel_route(route_map, self.create_vertical_channel_route(route_map,
offset, offset,
@ -964,7 +964,7 @@ class bank(design.design):
# self.add_label(text=wl_name, # self.add_label(text=wl_name,
# layer="m1", # layer="m1",
# offset=wl_pin.center()) # offset=wl_pin.center())
# # Add the bitline names # # Add the bitline names
# for i in range(self.num_cols): # for i in range(self.num_cols):
# bl_name = "bl_{}".format(i) # bl_name = "bl_{}".format(i)
@ -1025,10 +1025,10 @@ class bank(design.design):
# Add a path to connect to the array # Add a path to connect to the array
self.add_path(pin_layer, [left_loc, left_pin_loc]) self.add_path(pin_layer, [left_loc, left_pin_loc])
self.add_path(pin_layer, [right_loc, right_pin_loc]) self.add_path(pin_layer, [right_loc, right_pin_loc])
def route_control_lines(self, port): def route_control_lines(self, port):
""" Route the control lines of the entire bank """ """ Route the control lines of the entire bank """
# Make a list of tuples that we will connect. # Make a list of tuples that we will connect.
# From control signal to the module pin # From control signal to the module pin
# Connection from the central bus to the main control block crosses # Connection from the central bus to the main control block crosses
@ -1040,7 +1040,7 @@ class bank(design.design):
if port in self.write_ports: if port in self.write_ports:
connection.append((self.prefix + "w_en{}".format(port), connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en"))) self.port_data_inst[port].get_pin("w_en")))
if port in self.read_ports: if port in self.read_ports:
connection.append((self.prefix + "s_en{}".format(port), connection.append((self.prefix + "s_en{}".format(port),
self.port_data_inst[port].get_pin("s_en"))) self.port_data_inst[port].get_pin("s_en")))
@ -1057,7 +1057,7 @@ class bank(design.design):
self.add_via_stack_center(from_layer=pin.layer, self.add_via_stack_center(from_layer=pin.layer,
to_layer="m2", to_layer="m2",
offset=control_pos) offset=control_pos)
# clk to wordline_driver # clk to wordline_driver
control_signal = self.prefix + "wl_en{}".format(port) control_signal = self.prefix + "wl_en{}".format(port)
if port % 2: if port % 2:
@ -1081,7 +1081,7 @@ class bank(design.design):
for port in self.read_ports: for port in self.read_ports:
if self.port_data[port]: if self.port_data[port]:
self.port_data[port].graph_exclude_precharge() self.port_data[port].graph_exclude_precharge()
def get_cell_name(self, inst_name, row, col): def get_cell_name(self, inst_name, row, col):
""" """
Gets the spice name of the target bitcell. Gets the spice name of the target bitcell.
@ -1097,8 +1097,8 @@ class bank(design.design):
self.bitcell_array.graph_exclude_bits(targ_row, targ_col) self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def clear_exclude_bits(self): def clear_exclude_bits(self):
""" """
Clears the bit exclusions Clears the bit exclusions
""" """
self.bitcell_array.clear_exclude_bits() self.bitcell_array.clear_exclude_bits()

View File

@ -27,7 +27,7 @@ class bank_select(design.design):
super().__init__(name) super().__init__(name)
self.port = port self.port = port
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -36,7 +36,7 @@ class bank_select(design.design):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_instances() self.create_instances()
def create_layout(self): def create_layout(self):
self.calculate_module_offsets() self.calculate_module_offsets()
self.place_instances() self.place_instances()
@ -44,12 +44,12 @@ class bank_select(design.design):
self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width
self.width = max([x.rx() for x in self.inv_inst]) self.width = max([x.rx() for x in self.inv_inst])
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
# Number of control lines in the bus # Number of control lines in the bus
if self.port == "rw": if self.port == "rw":
self.num_control_lines = 4 self.num_control_lines = 4
@ -86,7 +86,7 @@ class bank_select(design.design):
self.nor2 = factory.create(module_type="pnor2", height=height) self.nor2 = factory.create(module_type="pnor2", height=height)
self.add_mod(self.nor2) self.add_mod(self.nor2)
self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4) self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4)
self.add_mod(self.inv4x_nor) self.add_mod(self.inv4x_nor)
@ -94,15 +94,15 @@ class bank_select(design.design):
self.add_mod(self.nand2) self.add_mod(self.nand2)
def calculate_module_offsets(self): def calculate_module_offsets(self):
self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell") self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell") self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_bank_sel_inv = 0 self.xoffset_bank_sel_inv = 0
self.xoffset_inputs = 0 self.xoffset_inputs = 0
self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height
def create_instances(self): def create_instances(self):
self.bank_sel_inv=self.add_inst(name="bank_sel_inv", self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
mod=self.inv_sel) mod=self.inv_sel)
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
@ -119,7 +119,7 @@ class bank_select(design.design):
# These require OR (nor2+inv) gates since they are active low. # These require OR (nor2+inv) gates since they are active low.
# (writes occur on clk low) # (writes occur on clk low)
if input_name in ("clk_buf"): if input_name in ("clk_buf"):
self.logic_inst.append(self.add_inst(name=name_nor, self.logic_inst.append(self.add_inst(name=name_nor,
mod=self.nor2)) mod=self.nor2))
self.connect_inst([input_name, self.connect_inst([input_name,
@ -127,7 +127,7 @@ class bank_select(design.design):
gated_name + "_temp_bar", gated_name + "_temp_bar",
"vdd", "vdd",
"gnd"]) "gnd"])
# They all get inverters on the output # They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv, self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x_nor)) mod=self.inv4x_nor))
@ -135,7 +135,7 @@ class bank_select(design.design):
gated_name, gated_name,
"vdd", "vdd",
"gnd"]) "gnd"])
# the rest are AND (nand2+inv) gates # the rest are AND (nand2+inv) gates
else: else:
self.logic_inst.append(self.add_inst(name=name_nand, self.logic_inst.append(self.add_inst(name=name_nand,
@ -155,7 +155,7 @@ class bank_select(design.design):
"gnd"]) "gnd"])
def place_instances(self): def place_instances(self):
# bank select inverter # bank select inverter
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0) self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
@ -166,27 +166,27 @@ class bank_select(design.design):
logic_inst = self.logic_inst[i] logic_inst = self.logic_inst[i]
inv_inst = self.inv_inst[i] inv_inst = self.inv_inst[i]
input_name = self.input_control_signals[i] input_name = self.input_control_signals[i]
if i == 0: if i == 0:
y_offset = 0 y_offset = 0
else: else:
y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1) y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1)
if i % 2: if i % 2:
y_offset += self.inv4x.height y_offset += self.inv4x.height
mirror = "MX" mirror = "MX"
else: else:
mirror = "" mirror = ""
# These require OR (nor2+inv) gates since they are active low. # These require OR (nor2+inv) gates since they are active low.
# (writes occur on clk low) # (writes occur on clk low)
if input_name in ("clk_buf"): if input_name in ("clk_buf"):
logic_inst.place(offset=[self.xoffset_nor, y_offset], logic_inst.place(offset=[self.xoffset_nor, y_offset],
mirror=mirror) mirror=mirror)
# the rest are AND (nand2+inv) gates # the rest are AND (nand2+inv) gates
else: else:
logic_inst.place(offset=[self.xoffset_nand, y_offset], logic_inst.place(offset=[self.xoffset_nand, y_offset],
@ -197,7 +197,7 @@ class bank_select(design.design):
mirror=mirror) mirror=mirror)
def route_instances(self): def route_instances(self):
# bank_sel is vertical wire # bank_sel is vertical wire
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A") bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
xoffset_bank_sel = bank_sel_inv_pin.lx() xoffset_bank_sel = bank_sel_inv_pin.lx()
@ -227,19 +227,19 @@ class bank_select(design.design):
height=self.inv4x.height) height=self.inv4x.height)
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=bank_sel_bar_pin.rc()) offset=bank_sel_bar_pin.rc())
for i in range(self.num_control_lines): for i in range(self.num_control_lines):
logic_inst = self.logic_inst[i] logic_inst = self.logic_inst[i]
inv_inst = self.inv_inst[i] inv_inst = self.inv_inst[i]
input_name = self.input_control_signals[i] input_name = self.input_control_signals[i]
gated_name = self.control_signals[i] gated_name = self.control_signals[i]
if input_name in ("clk_buf"): if input_name in ("clk_buf"):
xoffset_bank_signal = xoffset_bank_sel_bar xoffset_bank_signal = xoffset_bank_sel_bar
else: else:
xoffset_bank_signal = xoffset_bank_sel xoffset_bank_signal = xoffset_bank_sel
# Connect the logic output to inverter input # Connect the logic output to inverter input
out_pin = logic_inst.get_pin("Z") out_pin = logic_inst.get_pin("Z")
out_pos = out_pin.center() out_pos = out_pin.center()
@ -248,7 +248,7 @@ class bank_select(design.design):
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y) mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y) mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos]) self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
# Connect the logic B input to bank_sel / bank_sel_bar # Connect the logic B input to bank_sel / bank_sel_bar
logic_pin = logic_inst.get_pin("B") logic_pin = logic_inst.get_pin("B")
logic_pos = logic_pin.center() logic_pos = logic_pin.center()
@ -304,7 +304,7 @@ class bank_select(design.design):
self.add_layout_pin_rect_center(text=n, self.add_layout_pin_rect_center(text=n,
layer="m3", layer="m3",
offset=pin_pos) offset=pin_pos)
# Add vdd/gnd supply rails # Add vdd/gnd supply rails
gnd_pin = self.inv_inst[num].get_pin("gnd") gnd_pin = self.inv_inst[num].get_pin("gnd")
left_gnd_pos = vector(0, gnd_pin.cy()) left_gnd_pos = vector(0, gnd_pin.cy())
@ -312,7 +312,7 @@ class bank_select(design.design):
layer="m1", layer="m1",
start=left_gnd_pos, start=left_gnd_pos,
end=gnd_pin.rc()) end=gnd_pin.rc())
vdd_pin = self.inv_inst[num].get_pin("vdd") vdd_pin = self.inv_inst[num].get_pin("vdd")
left_vdd_pos = vector(0, vdd_pin.cy()) left_vdd_pos = vector(0, vdd_pin.cy())
self.add_layout_pin_segment_center(text="vdd", self.add_layout_pin_segment_center(text="vdd",

View File

@ -25,11 +25,11 @@ class bitcell_array(bitcell_base_array):
# This will create a default set of bitline/wordline names # This will create a default set of bitline/wordline names
self.create_all_bitline_names() self.create_all_bitline_names()
self.create_all_wordline_names() self.create_all_wordline_names()
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
# We don't offset this because we need to align # We don't offset this because we need to align
# the replica bitcell in the control logic # the replica bitcell in the control logic
# self.offset_all_coordinates() # self.offset_all_coordinates()

View File

@ -47,11 +47,11 @@ class bitcell_base_array(design.design):
"br_{0}_{1}".format(port, col)]) "br_{0}_{1}".format(port, col)])
# Make a flat list too # Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
def create_all_wordline_names(self, row_size=None): def create_all_wordline_names(self, row_size=None):
if row_size == None: if row_size == None:
row_size = self.row_size row_size = self.row_size
for row in range(row_size): for row in range(row_size):
for port in self.all_ports: for port in self.all_ports:
self.wordline_names[port].append("wl_{0}_{1}".format(port, row)) self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
@ -69,7 +69,7 @@ class bitcell_base_array(design.design):
def get_bitcell_pins(self, row, col): def get_bitcell_pins(self, row, col):
""" """
Creates a list of connections in the bitcell, Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array indexed by column and row, for instance use in bitcell_array
""" """
bitcell_pins = [] bitcell_pins = []
for port in self.all_ports: for port in self.all_ports:
@ -81,7 +81,7 @@ class bitcell_base_array(design.design):
return bitcell_pins return bitcell_pins
def get_rbl_wordline_names(self, port=None): def get_rbl_wordline_names(self, port=None):
""" """
Return the WL for the given RBL port. Return the WL for the given RBL port.
""" """
if port == None: if port == None:
@ -102,7 +102,7 @@ class bitcell_base_array(design.design):
return self.all_bitline_names return self.all_bitline_names
else: else:
return self.bitline_names[port] return self.bitline_names[port]
def get_all_bitline_names(self, port=None): def get_all_bitline_names(self, port=None):
""" Return ALL the bitline names (including rbl) """ """ Return ALL the bitline names (including rbl) """
temp = [] temp = []
@ -121,7 +121,7 @@ class bitcell_base_array(design.design):
return self.all_wordline_names return self.all_wordline_names
else: else:
return self.wordline_names[port] return self.wordline_names[port]
def get_all_wordline_names(self, port=None): def get_all_wordline_names(self, port=None):
""" Return all the wordline names """ """ Return all the wordline names """
temp = [] temp = []
@ -133,7 +133,7 @@ class bitcell_base_array(design.design):
if len(self.all_ports) > 1: if len(self.all_ports) > 1:
temp.extend(self.get_rbl_wordline_names(1)) temp.extend(self.get_rbl_wordline_names(1))
return temp return temp
def add_layout_pins(self): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """
bitline_names = self.cell.get_all_bitline_names() bitline_names = self.cell.get_all_bitline_names()
@ -161,7 +161,7 @@ class bitcell_base_array(design.design):
offset=wl_pin.ll().scale(0, 1), offset=wl_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=wl_pin.height()) height=wl_pin.height())
# Copy a vdd/gnd layout pin from every cell # Copy a vdd/gnd layout pin from every cell
for row in range(self.row_size): for row in range(self.row_size):
for col in range(self.column_size): for col in range(self.column_size):

View File

@ -34,7 +34,7 @@ class col_cap_array(bitcell_base_array):
if not end_caps_enabled: if not end_caps_enabled:
self.create_all_wordline_names() self.create_all_wordline_names()
self.create_all_bitline_names() self.create_all_bitline_names()
self.add_modules() self.add_modules()
self.add_pins() self.add_pins()
self.create_instances() self.create_instances()

View File

@ -46,7 +46,7 @@ class column_mux_array(design.design):
# self.sel_layer = "m1" # self.sel_layer = "m1"
# self.sel_pitch = self.m2_pitch # self.sel_pitch = self.m2_pitch
# self.bitline_layer = "m2" # self.bitline_layer = "m2"
if preferred_directions[self.sel_layer] == "V": if preferred_directions[self.sel_layer] == "V":
self.via_directions = ("H", "H") self.via_directions = ("H", "H")
else: else:
@ -125,7 +125,7 @@ class column_mux_array(design.design):
# Default to single spaced columns # Default to single spaced columns
if not self.offsets: if not self.offsets:
self.offsets = [n * self.mux.width for n in range(self.columns)] self.offsets = [n * self.mux.width for n in range(self.columns)]
# For every column, add a pass gate # For every column, add a pass gate
for col_num, xoffset in enumerate(self.offsets[0:self.columns]): for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
if cell_props.bitcell.mirror.y and (col_num + self.column_offset) % 2: if cell_props.bitcell.mirror.y and (col_num + self.column_offset) % 2:
@ -209,7 +209,7 @@ class column_mux_array(design.design):
br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc() br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc()
bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.sel_pitch) bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.sel_pitch)
br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.sel_pitch) br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.sel_pitch)
self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end]) self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end])
self.add_path(self.sel_layer, [br_out_offset_begin, br_out_offset_end]) self.add_path(self.sel_layer, [br_out_offset_begin, br_out_offset_end])

View File

@ -28,7 +28,7 @@ class control_logic(design.design):
self.add_comment("num_rows: {0}".format(num_rows)) self.add_comment("num_rows: {0}".format(num_rows))
self.add_comment("words_per_row: {0}".format(words_per_row)) self.add_comment("words_per_row: {0}".format(words_per_row))
self.add_comment("word_size {0}".format(word_size)) self.add_comment("word_size {0}".format(word_size))
self.sram=sram self.sram=sram
self.num_rows = num_rows self.num_rows = num_rows
self.words_per_row = words_per_row self.words_per_row = words_per_row
@ -42,21 +42,21 @@ class control_logic(design.design):
self.num_cols = word_size * words_per_row + self.num_spare_cols self.num_cols = word_size * words_per_row + self.num_spare_cols
self.num_words = num_rows * words_per_row self.num_words = num_rows * words_per_row
self.enable_delay_chain_resizing = False self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.logical_effort.pinv self.inv_parasitic_delay = logical_effort.logical_effort.pinv
# Determines how much larger the sen delay should be. Accounts for possible error in model. # Determines how much larger the sen delay should be. Accounts for possible error in model.
# FIXME: This should be made a parameter # FIXME: This should be made a parameter
self.wl_timing_tolerance = 1 self.wl_timing_tolerance = 1
self.wl_stage_efforts = None self.wl_stage_efforts = None
self.sen_stage_efforts = None self.sen_stage_efforts = None
if self.port_type == "rw": if self.port_type == "rw":
self.num_control_signals = 2 self.num_control_signals = 2
else: else:
self.num_control_signals = 1 self.num_control_signals = 1
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -66,7 +66,7 @@ class control_logic(design.design):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_instances() self.create_instances()
def create_layout(self): def create_layout(self):
""" Create layout and route between modules """ """ Create layout and route between modules """
self.place_instances() self.place_instances()
@ -84,16 +84,16 @@ class control_logic(design.design):
def add_modules(self): def add_modules(self):
""" Add all the required modules """ """ Add all the required modules """
self.dff = factory.create(module_type="dff_buf") self.dff = factory.create(module_type="dff_buf")
dff_height = self.dff.height dff_height = self.dff.height
self.ctrl_dff_array = factory.create(module_type="dff_buf_array", self.ctrl_dff_array = factory.create(module_type="dff_buf_array",
rows=self.num_control_signals, rows=self.num_control_signals,
columns=1) columns=1)
self.add_mod(self.ctrl_dff_array) self.add_mod(self.ctrl_dff_array)
self.and2 = factory.create(module_type="pand2", self.and2 = factory.create(module_type="pand2",
size=12, size=12,
height=dff_height) height=dff_height)
@ -103,7 +103,7 @@ class control_logic(design.design):
size=self.num_cols, size=self.num_cols,
height=dff_height) height=dff_height)
self.add_mod(self.rbl_driver) self.add_mod(self.rbl_driver)
# clk_buf drives a flop for every address # clk_buf drives a flop for every address
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2) addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
# plus data flops and control flops # plus data flops and control flops
@ -114,13 +114,13 @@ class control_logic(design.design):
self.clk_buf_driver = factory.create(module_type="pdriver", self.clk_buf_driver = factory.create(module_type="pdriver",
fanout=clock_fanout, fanout=clock_fanout,
height=dff_height) height=dff_height)
self.add_mod(self.clk_buf_driver) self.add_mod(self.clk_buf_driver)
# We will use the maximum since this same value is used to size the wl_en # We will use the maximum since this same value is used to size the wl_en
# and the p_en_bar drivers # and the p_en_bar drivers
# max_fanout = max(self.num_rows, self.num_cols) # max_fanout = max(self.num_rows, self.num_cols)
# wl_en drives every row in the bank # wl_en drives every row in the bank
self.wl_en_driver = factory.create(module_type="pdriver", self.wl_en_driver = factory.create(module_type="pdriver",
fanout=self.num_rows, fanout=self.num_rows,
@ -144,7 +144,7 @@ class control_logic(design.design):
size=1, size=1,
height=dff_height) height=dff_height)
self.add_mod(self.inv) self.add_mod(self.inv)
# p_en_bar drives every column in the bitcell array # p_en_bar drives every column in the bitcell array
# but it is sized the same as the wl_en driver with # but it is sized the same as the wl_en driver with
# prepended 3 inverter stages to guarantee it is slower and odd polarity # prepended 3 inverter stages to guarantee it is slower and odd polarity
@ -183,14 +183,14 @@ class control_logic(design.design):
# Fanout can be varied as well but is a little more complicated but potentially optimal. # Fanout can be varied as well but is a little more complicated but potentially optimal.
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay)) debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
return (delay_stages, delay_fanout) return (delay_stages, delay_fanout)
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout): def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays""" """Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay
previous_delay_chain_delay = previous_delay_per_stage * previous_stages previous_delay_chain_delay = previous_delay_per_stage * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
fanout_rise = fanout_fall = 2 # This can be anything >=2 fanout_rise = fanout_fall = 2 # This can be anything >=2
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each # The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value # inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
@ -201,7 +201,7 @@ class control_logic(design.design):
debug.info(2, debug.info(2,
"Required delays from chain: fall={}, rise={}".format(required_delay_fall, "Required delays from chain: fall={}, rise={}".format(required_delay_fall,
required_delay_rise)) required_delay_rise))
# If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic. # If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5 WARNING_FANOUT_DIFF = 5
stages_close = False stages_close = False
@ -218,7 +218,7 @@ class control_logic(design.design):
stages_close = True stages_close = True
safe_fanout_rise = fanout_rise safe_fanout_rise = fanout_rise
safe_fanout_fall = fanout_fall safe_fanout_fall = fanout_fall
if stages_fall == stages_rise: if stages_fall == stages_rise:
break break
elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise): elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
@ -232,14 +232,14 @@ class control_logic(design.design):
fanout_fall+=1 fanout_fall+=1
else: else:
fanout_rise+=1 fanout_rise+=1
total_stages = max(stages_fall, stages_rise) * 2 total_stages = max(stages_fall, stages_rise) * 2
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall)) debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
# Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage. # Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)] stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)]
return stage_list return stage_list
def calculate_stages_with_fixed_fanout(self, required_delay, fanout): def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
from math import ceil from math import ceil
# Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay # Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
@ -249,7 +249,7 @@ class control_logic(design.design):
delay_per_stage = fanout + 1 + self.inv_parasitic_delay delay_per_stage = fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage) delay_stages = ceil(required_delay / delay_per_stage)
return delay_stages return delay_stages
def setup_signal_busses(self): def setup_signal_busses(self):
""" Setup bus names, determine the size of the busses etc """ """ Setup bus names, determine the size of the busses etc """
@ -265,7 +265,7 @@ class control_logic(design.design):
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"] self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
else: else:
self.dff_output_list = ["cs_bar", "cs"] self.dff_output_list = ["cs_bar", "cs"]
# list of output control signals (for making a vertical bus) # list of output control signals (for making a vertical bus)
if self.port_type == "rw": if self.port_type == "rw":
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"] self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"]
@ -275,7 +275,7 @@ class control_logic(design.design):
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"] self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
# leave space for the bus plus one extra space # leave space for the bus plus one extra space
self.internal_bus_width = (len(self.internal_bus_list) + 1) * self.m2_pitch self.internal_bus_width = (len(self.internal_bus_list) + 1) * self.m2_pitch
# Outputs to the bank # Outputs to the bank
if self.port_type == "rw": if self.port_type == "rw":
self.output_list = ["s_en", "w_en"] self.output_list = ["s_en", "w_en"]
@ -286,14 +286,14 @@ class control_logic(design.design):
self.output_list.append("p_en_bar") self.output_list.append("p_en_bar")
self.output_list.append("wl_en") self.output_list.append("wl_en")
self.output_list.append("clk_buf") self.output_list.append("clk_buf")
self.supply_list = ["vdd", "gnd"] self.supply_list = ["vdd", "gnd"]
def route_rails(self): def route_rails(self):
""" Add the input signal inverted tracks """ """ Add the input signal inverted tracks """
height = self.control_logic_center.y - self.m2_pitch height = self.control_logic_center.y - self.m2_pitch
offset = vector(self.ctrl_dff_array.width, 0) offset = vector(self.ctrl_dff_array.width, 0)
self.input_bus = self.create_vertical_bus("m2", self.input_bus = self.create_vertical_bus("m2",
offset, offset,
self.internal_bus_list, self.internal_bus_list,
@ -325,7 +325,7 @@ class control_logic(design.design):
# All of the control logic is placed to the right of the DFFs and bus # All of the control logic is placed to the right of the DFFs and bus
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width
row = 0 row = 0
# Add the logic on the right of the bus # Add the logic on the right of the bus
self.place_clk_buf_row(row) self.place_clk_buf_row(row)
@ -396,7 +396,7 @@ class control_logic(design.design):
# Add to the right of the control rows and routing channel # Add to the right of the control rows and routing channel
offset = vector(self.delay_chain.width, y_off) offset = vector(self.delay_chain.width, y_off)
self.delay_inst.place(offset, mirror="MY") self.delay_inst.place(offset, mirror="MY")
def route_delay(self): def route_delay(self):
out_pos = self.delay_inst.get_pin("out").bc() out_pos = self.delay_inst.get_pin("out").bc()
@ -408,21 +408,21 @@ class control_logic(design.design):
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos]) self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=in_pos) offset=in_pos)
# Input from RBL goes to the delay line for futher delay # Input from RBL goes to the delay line for futher delay
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl") self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
def create_clk_buf_row(self): def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """ """ Create the multistage and gated clock buffer """
self.clk_buf_inst = self.add_inst(name="clkbuf", self.clk_buf_inst = self.add_inst(name="clkbuf",
mod=self.clk_buf_driver) mod=self.clk_buf_driver)
self.connect_inst(["clk", "clk_buf", "vdd", "gnd"]) self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
def place_clk_buf_row(self, row): def place_clk_buf_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_buf_inst, x_offset, row) x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
self.row_end_inst.append(self.clk_buf_inst) self.row_end_inst.append(self.clk_buf_inst)
def route_clk_buf(self): def route_clk_buf(self):
@ -443,17 +443,17 @@ class control_logic(design.design):
self.clk_bar_inst = self.add_inst(name="inv_clk_bar", self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
mod=self.inv) mod=self.inv)
self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"]) self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"])
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar", self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
mod=self.and2) mod=self.and2)
self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"]) self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
def place_gated_clk_bar_row(self, row): def place_gated_clk_bar_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_bar_inst, x_offset, row) x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row) x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
self.row_end_inst.append(self.gated_clk_bar_inst) self.row_end_inst.append(self.gated_clk_bar_inst)
def route_gated_clk_bar(self): def route_gated_clk_bar(self):
@ -468,7 +468,7 @@ class control_logic(design.design):
self.add_via_stack_center(from_layer=out_pin.layer, self.add_via_stack_center(from_layer=out_pin.layer,
to_layer=in_pin.layer, to_layer=in_pin.layer,
offset=in_pos) offset=in_pos)
# This is the second gate over, so it needs to be on M3 # This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["B"], ["cs"]) clkbuf_map = zip(["B"], ["cs"])
@ -495,9 +495,9 @@ class control_logic(design.design):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row) x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
self.row_end_inst.append(self.gated_clk_buf_inst) self.row_end_inst.append(self.gated_clk_buf_inst)
def route_gated_clk_buf(self): def route_gated_clk_buf(self):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"]) clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map, self.connect_vertical_bus(clkbuf_map,
@ -514,7 +514,7 @@ class control_logic(design.design):
self.add_via_stack_center(from_layer=z_pin.layer, self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m2", to_layer="m2",
offset=z_pin.center()) offset=z_pin.center())
def create_wlen_row(self): def create_wlen_row(self):
# input pre_p_en, output: wl_en # input pre_p_en, output: wl_en
self.wl_en_inst=self.add_inst(name="buf_wl_en", self.wl_en_inst=self.add_inst(name="buf_wl_en",
@ -531,7 +531,7 @@ class control_logic(design.design):
def route_wlen(self): def route_wlen(self):
wlen_map = zip(["A"], ["gated_clk_bar"]) wlen_map = zip(["A"], ["gated_clk_bar"])
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.input_bus) self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.input_bus)
self.connect_output(self.wl_en_inst, "Z", "wl_en") self.connect_output(self.wl_en_inst, "Z", "wl_en")
def create_pen_row(self): def create_pen_row(self):
@ -544,7 +544,7 @@ class control_logic(design.design):
self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar", self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar",
mod=self.p_en_bar_driver) mod=self.p_en_bar_driver)
self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"]) self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"])
def place_pen_row(self, row): def place_pen_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
@ -568,7 +568,7 @@ class control_logic(design.design):
offset=in_pin.center()) offset=in_pin.center())
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar") self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
def create_sen_row(self): def create_sen_row(self):
""" Create the sense enable buffer. """ """ Create the sense enable buffer. """
if self.port_type=="rw": if self.port_type=="rw":
@ -582,24 +582,24 @@ class control_logic(design.design):
# we also must wait until the bitline has been discharged enough for proper sensing # we also must wait until the bitline has been discharged enough for proper sensing
# hence we use rbl_bl_delay as well. # hence we use rbl_bl_delay as well.
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"]) self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
def place_sen_row(self, row): def place_sen_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.s_en_gate_inst, x_offset, row) x_offset = self.place_util(self.s_en_gate_inst, x_offset, row)
self.row_end_inst.append(self.s_en_gate_inst) self.row_end_inst.append(self.s_en_gate_inst)
def route_sen(self): def route_sen(self):
if self.port_type=="rw": if self.port_type=="rw":
input_name = "we_bar" input_name = "we_bar"
else: else:
input_name = "cs" input_name = "cs"
sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name]) sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name])
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.input_bus) self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.input_bus)
self.connect_output(self.s_en_gate_inst, "Z", "s_en") self.connect_output(self.s_en_gate_inst, "Z", "s_en")
def create_rbl_delay_row(self): def create_rbl_delay_row(self):
@ -612,15 +612,15 @@ class control_logic(design.design):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row) x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row)
self.row_end_inst.append(self.rbl_bl_delay_inv_inst) self.row_end_inst.append(self.rbl_bl_delay_inv_inst)
def route_rbl_delay(self): def route_rbl_delay(self):
# Connect from delay line # Connect from delay line
# Connect to rail # Connect to rail
self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar") self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar")
rbl_map = zip(["A"], ["rbl_bl_delay"]) rbl_map = zip(["A"], ["rbl_bl_delay"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.input_bus) self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.input_bus)
@ -638,26 +638,26 @@ class control_logic(design.design):
mod=self.wen_and) mod=self.wen_and)
# Only drive the writes in the second half of the clock cycle during a write operation. # Only drive the writes in the second half of the clock cycle during a write operation.
self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"]) self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"])
def place_wen_row(self, row): def place_wen_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.w_en_gate_inst, x_offset, row) x_offset = self.place_util(self.w_en_gate_inst, x_offset, row)
self.row_end_inst.append(self.w_en_gate_inst) self.row_end_inst.append(self.w_en_gate_inst)
def route_wen(self): def route_wen(self):
if self.port_type == "rw": if self.port_type == "rw":
input_name = "we" input_name = "we"
else: else:
# No we for write-only reports, so use cs # No we for write-only reports, so use cs
input_name = "cs" input_name = "cs"
wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"]) wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"])
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus) self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus)
self.connect_output(self.w_en_gate_inst, "Z", "w_en") self.connect_output(self.w_en_gate_inst, "Z", "w_en")
def create_dffs(self): def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs", self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array) mod=self.ctrl_dff_array)
@ -669,7 +669,7 @@ class control_logic(design.design):
def place_dffs(self): def place_dffs(self):
self.ctrl_dff_inst.place(vector(0, 0)) self.ctrl_dff_inst.place(vector(0, 0))
def route_dffs(self): def route_dffs(self):
if self.port_type == "rw": if self.port_type == "rw":
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"]) dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
@ -678,7 +678,7 @@ class control_logic(design.design):
else: else:
dff_out_map = zip(["dout_bar_0"], ["cs"]) dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1]) self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1])
# Connect the clock rail to the other clock rail # Connect the clock rail to the other clock rail
# by routing in the supply rail track to avoid channel conflicts # by routing in the supply rail track to avoid channel conflicts
in_pos = self.ctrl_dff_inst.get_pin("clk").uc() in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
@ -691,7 +691,7 @@ class control_logic(design.design):
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb") self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
if (self.port_type == "rw"): if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web") self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def get_offset(self, row): def get_offset(self, row):
""" Compute the y-offset and mirroring """ """ Compute the y-offset and mirroring """
y_off = row * self.and2.height y_off = row * self.and2.height
@ -702,14 +702,14 @@ class control_logic(design.design):
mirror="R0" mirror="R0"
return (y_off, mirror) return (y_off, mirror)
def connect_output(self, inst, pin_name, out_name): def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """ """ Create an output pin on the right side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name) out_pin = inst.get_pin(pin_name)
out_pos = out_pin.center() out_pos = out_pin.center()
right_pos = out_pos + vector(self.width - out_pin.cx(), 0) right_pos = out_pos + vector(self.width - out_pin.cx(), 0)
self.add_via_stack_center(from_layer=out_pin.layer, self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2", to_layer="m2",
offset=out_pos) offset=out_pos)
@ -722,7 +722,7 @@ class control_logic(design.design):
""" Add vdd and gnd to the instance cells """ """ Add vdd and gnd to the instance cells """
supply_layer = self.dff.get_pin("vdd").layer supply_layer = self.dff.get_pin("vdd").layer
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
for inst in self.row_end_inst: for inst in self.row_end_inst:
pins = inst.get_pins("vdd") pins = inst.get_pins("vdd")
@ -740,13 +740,13 @@ class control_logic(design.design):
pin_loc = vector(max_row_x_loc, pin.rc().y) pin_loc = vector(max_row_x_loc, pin.rc().y)
self.add_power_pin("gnd", pin_loc, start_layer=pin.layer) self.add_power_pin("gnd", pin_loc, start_layer=pin.layer)
self.add_path(supply_layer, [row_loc, pin_loc]) self.add_path(supply_layer, [row_loc, pin_loc])
self.copy_layout_pin(self.delay_inst, "gnd") self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst, "vdd") self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst, "gnd") self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd") self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
def add_lvs_correspondence_points(self): def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong. """ This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction These should probably be turned off by default though, since extraction
@ -772,10 +772,10 @@ class control_logic(design.design):
offset=pin.ll(), offset=pin.ll(),
height=pin.height(), height=pin.height(),
width=pin.width()) width=pin.width())
def graph_exclude_dffs(self): def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path""" """Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst) self.graph_inst_exclude.add(self.ctrl_dff_inst)
if self.port_type=="rw" or self.port_type=="w": if self.port_type=="rw" or self.port_type=="w":
self.graph_inst_exclude.add(self.w_en_gate_inst) self.graph_inst_exclude.add(self.w_en_gate_inst)
@ -799,4 +799,4 @@ class control_logic(design.design):
self.add_via_stack_center(from_layer=out_pin.layer, self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2", to_layer="m2",
offset=out_pos) offset=out_pos)

View File

@ -1,271 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
from tech import drc
from sram_factory import factory
from vector import vector
from globals import OPTS
class custom_cell(design.design):
"""
Array of tristate drivers to write to the bitlines through the column mux.
Dynamically generated write driver array of all bitlines.
"""
def __init__(self, name, pins, mod):
design.design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("columns: {0}".format(columns))
self.add_comment("word_size {0}".format(word_size))
self.columns = columns
self.word_size = word_size
self.write_size = write_size
self.column_offset = column_offset
self.words_per_row = int(columns / word_size)
if not num_spare_cols:
self.num_spare_cols = 0
else:
self.num_spare_cols = num_spare_cols
if self.write_size:
self.num_wmasks = int(self.word_size / self.write_size)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self):
bl_name = "bl"
return bl_name
def get_br_name(self):
br_name = "br"
return br_name
@property
def data_name(self):
return "data"
@property
def en_name(self):
return "en"
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_write_array()
def create_layout(self):
if self.bitcell.width > self.driver.width:
self.width = (self.columns + self.num_spare_cols) * self.bitcell.width
self.width_regular_cols = self.columns * self.bitcell.width
self.single_col_width = self.bitcell.width
else:
self.width = (self.columns + self.num_spare_cols) * self.driver.width
self.width_regular_cols = self.columns * self.driver.width
self.single_col_width = self.driver.width
self.height = self.driver.height
self.place_write_array()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
for i in range(self.word_size + self.num_spare_cols):
self.add_pin(self.data_name + "_{0}".format(i), "INPUT")
for i in range(self.word_size + self.num_spare_cols):
self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT")
self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT")
if self.write_size:
for i in range(self.num_wmasks + self.num_spare_cols):
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
elif self.num_spare_cols and not self.write_size:
for i in range(self.num_spare_cols + 1):
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
else:
self.add_pin(self.en_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.driver = factory.create(module_type="write_driver")
self.add_mod(self.driver)
# This is just used for measurements,
# so don't add the module
self.bitcell = factory.create(module_type="bitcell")
def create_write_array(self):
self.driver_insts = {}
w = 0
windex=0
for i in range(0, self.columns, self.words_per_row):
name = "write_driver{}".format(i)
index = int(i / self.words_per_row)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)
if self.write_size:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(windex), "vdd", "gnd"])
w+=1
# when w equals write size, the next en pin can be connected since we are now at the next wmask bit
if w == self.write_size:
w = 0
windex+=1
elif self.num_spare_cols and not self.write_size:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(0), "vdd", "gnd"])
else:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name, "vdd", "gnd"])
for i in range(self.num_spare_cols):
index = self.word_size + i
if self.write_size:
offset = self.num_wmasks
else:
offset = 1
name = "write_driver{}".format(self.columns + i)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(i + offset), "vdd", "gnd"])
def place_write_array(self):
from tech import cell_properties
if self.bitcell.width > self.driver.width:
self.driver_spacing = self.bitcell.width
else:
self.driver_spacing = self.driver.width
for i in range(0, self.columns, self.words_per_row):
index = int(i / self.words_per_row)
xoffset = i * self.driver_spacing
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
mirror = "MY"
xoffset = xoffset + self.driver.width
else:
mirror = ""
base = vector(xoffset, 0)
self.driver_insts[index].place(offset=base, mirror=mirror)
# place spare write drivers (if spare columns are specified)
for i in range(self.num_spare_cols):
index = self.word_size + i
xoffset = (self.columns + i) * self.driver_spacing
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
mirror = "MY"
xoffset = xoffset + self.driver.width
else:
mirror = ""
base = vector(xoffset, 0)
self.driver_insts[index].place(offset=base, mirror=mirror)
def add_layout_pins(self):
for i in range(self.word_size + self.num_spare_cols):
inst = self.driver_insts[i]
din_pin = inst.get_pin(inst.mod.din_name)
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
bl_pin = inst.get_pin(inst.mod.get_bl_names())
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
layer=bl_pin.layer,
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
br_pin = inst.get_pin(inst.mod.get_br_names())
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
layer=br_pin.layer,
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
for n in ["vdd", "gnd"]:
pin_list = self.driver_insts[i].get_pins(n)
for pin in pin_list:
self.add_power_pin(name=n,
loc=pin.center(),
directions=("V", "V"),
start_layer=pin.layer)
if self.write_size:
for bit in range(self.num_wmasks):
inst = self.driver_insts[bit * self.write_size]
en_pin = inst.get_pin(inst.mod.en_name)
# Determine width of wmask modified en_pin with/without col mux
wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing)
if (self.words_per_row == 1):
en_gap = self.driver_spacing - en_pin.width()
else:
en_gap = self.driver_spacing
self.add_layout_pin(text=self.en_name + "_{0}".format(bit),
layer=en_pin.layer,
offset=en_pin.ll(),
width=wmask_en_len - en_gap,
height=en_pin.height())
for i in range(self.num_spare_cols):
inst = self.driver_insts[self.word_size + i]
en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks),
layer="m1",
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
elif self.num_spare_cols and not self.write_size:
# shorten enable rail to accomodate those for spare write drivers
inst = self.driver_insts[0]
en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(0),
layer="m1",
offset=en_pin.ll(),
width=self.width_regular_cols - self.words_per_row * en_pin.width())
# individual enables for every spare write driver
for i in range(self.num_spare_cols):
inst = self.driver_insts[self.word_size + i]
en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1),
layer="m1",
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
else:
inst = self.driver_insts[0]
self.add_layout_pin(text=self.en_name,
layer="m1",
offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1),
width=self.width)
def get_w_en_cin(self):
"""Get the relative capacitance of all the enable connections in the bank"""
# The enable is connected to a nand2 for every row.
return self.driver.get_w_en_cin() * len(self.driver_insts)

View File

@ -24,14 +24,14 @@ class delay_chain(design.design):
super().__init__(name) super().__init__(name)
debug.info(1, "creating delay chain {0}".format(str(fanout_list))) debug.info(1, "creating delay chain {0}".format(str(fanout_list)))
self.add_comment("fanouts: {0}".format(str(fanout_list))) self.add_comment("fanouts: {0}".format(str(fanout_list)))
# Two fanouts are needed so that we can route the vdd/gnd connections # Two fanouts are needed so that we can route the vdd/gnd connections
for f in fanout_list: for f in fanout_list:
debug.check(f>=2, "Must have >=2 fanouts for each stage.") debug.check(f>=2, "Must have >=2 fanouts for each stage.")
# number of inverters including any fanout loads. # number of inverters including any fanout loads.
self.fanout_list = fanout_list self.fanout_list = fanout_list
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -40,7 +40,7 @@ class delay_chain(design.design):
self.add_modules() self.add_modules()
self.add_pins() self.add_pins()
self.create_inverters() self.create_inverters()
def create_layout(self): def create_layout(self):
# Each stage is a a row # Each stage is a a row
self.height = len(self.fanout_list) * self.inv.height self.height = len(self.fanout_list) * self.inv.height
@ -53,7 +53,7 @@ class delay_chain(design.design):
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
""" Add the pins of the delay chain""" """ Add the pins of the delay chain"""
self.add_pin("in", "INPUT") self.add_pin("in", "INPUT")
@ -86,7 +86,7 @@ class delay_chain(design.design):
else: else:
stagein_name = "dout_{}".format(stage_num) stagein_name = "dout_{}".format(stage_num)
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"]) self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
# Now add the dummy loads to the right # Now add the dummy loads to the right
self.load_inst_map[cur_driver]=[] self.load_inst_map[cur_driver]=[]
for i in range(fanout_size): for i in range(fanout_size):
@ -95,7 +95,7 @@ class delay_chain(design.design):
# Fanout stage is always driven by driver and output is disconnected # Fanout stage is always driven by driver and output is disconnected
disconnect_name = "n_{0}_{1}".format(stage_num, i) disconnect_name = "n_{0}_{1}".format(stage_num, i)
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"]) self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
# Keep track of all the loads to connect their inputs as a load # Keep track of all the loads to connect their inputs as a load
self.load_inst_map[cur_driver].append(cur_load) self.load_inst_map[cur_driver].append(cur_load)
@ -108,19 +108,19 @@ class delay_chain(design.design):
else: else:
inv_mirror = "R0" inv_mirror = "R0"
inv_offset = vector(0, stage_num * self.inv.height) inv_offset = vector(0, stage_num * self.inv.height)
# Add the inverter # Add the inverter
cur_driver=self.driver_inst_list[stage_num] cur_driver=self.driver_inst_list[stage_num]
cur_driver.place(offset=inv_offset, cur_driver.place(offset=inv_offset,
mirror=inv_mirror) mirror=inv_mirror)
# Now add the dummy loads to the right # Now add the dummy loads to the right
load_list = self.load_inst_map[cur_driver] load_list = self.load_inst_map[cur_driver]
for i in range(fanout_size): for i in range(fanout_size):
inv_offset += vector(self.inv.width, 0) inv_offset += vector(self.inv.width, 0)
load_list[i].place(offset=inv_offset, load_list[i].place(offset=inv_offset,
mirror=inv_mirror) mirror=inv_mirror)
def add_route(self, pin1, pin2): def add_route(self, pin1, pin2):
""" This guarantees that we route from the top to bottom row correctly. """ """ This guarantees that we route from the top to bottom row correctly. """
pin1_pos = pin1.center() pin1_pos = pin1.center()
@ -131,7 +131,7 @@ class delay_chain(design.design):
mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y)) mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y))
# Written this way to guarantee it goes right first if we are switching rows # Written this way to guarantee it goes right first if we are switching rows
self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos]) self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos])
def route_inverters(self): def route_inverters(self):
""" Add metal routing for each of the fanout stages """ """ Add metal routing for each of the fanout stages """
@ -180,12 +180,12 @@ class delay_chain(design.design):
self.add_power_pin(pin_name, self.add_power_pin(pin_name,
pin.rc() - vector(self.m1_pitch, 0), pin.rc() - vector(self.m1_pitch, 0),
start_layer=pin.layer) start_layer=pin.layer)
pin = load_list[-2].get_pin(pin_name) pin = load_list[-2].get_pin(pin_name)
self.add_power_pin(pin_name, self.add_power_pin(pin_name,
pin.rc() - vector(self.m1_pitch, 0), pin.rc() - vector(self.m1_pitch, 0),
start_layer=pin.layer) start_layer=pin.layer)
def add_layout_pins(self): def add_layout_pins(self):
# input is A pin of first inverter # input is A pin of first inverter

View File

@ -27,7 +27,7 @@ class dff_array(design.design):
super().__init__(name) super().__init__(name)
debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns)) debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns))
self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("rows: {0} cols: {1}".format(rows, columns))
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -36,11 +36,11 @@ class dff_array(design.design):
self.add_modules() self.add_modules()
self.add_pins() self.add_pins()
self.create_dff_array() self.create_dff_array()
def create_layout(self): def create_layout(self):
self.width = self.columns * self.dff.width self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height self.height = self.rows * self.dff.height
self.place_dff_array() self.place_dff_array()
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
@ -49,7 +49,7 @@ class dff_array(design.design):
def add_modules(self): def add_modules(self):
self.dff = factory.create(module_type="dff") self.dff = factory.create(module_type="dff")
self.add_mod(self.dff) self.add_mod(self.dff)
def add_pins(self): def add_pins(self):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
@ -86,7 +86,7 @@ class dff_array(design.design):
mirror = "MX" mirror = "MX"
self.dff_insts[row, col].place(offset=base, self.dff_insts[row, col].place(offset=base,
mirror=mirror) mirror=mirror)
def get_din_name(self, row, col): def get_din_name(self, row, col):
if self.columns == 1: if self.columns == 1:
din_name = "din_{0}".format(row) din_name = "din_{0}".format(row)
@ -96,7 +96,7 @@ class dff_array(design.design):
din_name = "din_{0}_{1}".format(row, col) din_name = "din_{0}_{1}".format(row, col)
return din_name return din_name
def get_dout_name(self, row, col): def get_dout_name(self, row, col):
if self.columns == 1: if self.columns == 1:
dout_name = "dout_{0}".format(row) dout_name = "dout_{0}".format(row)
@ -106,7 +106,7 @@ class dff_array(design.design):
dout_name = "dout_{0}_{1}".format(row, col) dout_name = "dout_{0}_{1}".format(row, col)
return dout_name return dout_name
def add_layout_pins(self): def add_layout_pins(self):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
@ -117,7 +117,7 @@ class dff_array(design.design):
# Continous gnd rail along with label. # Continous gnd rail along with label.
gnd_pin=self.dff_insts[row, col].get_pin("gnd") gnd_pin=self.dff_insts[row, col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.center(), start_layer=gnd_pin.layer) self.add_power_pin("gnd", gnd_pin.center(), start_layer=gnd_pin.layer)
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
din_pin = self.dff_insts[row, col].get_pin("D") din_pin = self.dff_insts[row, col].get_pin("D")

View File

@ -7,7 +7,7 @@
# #
import debug import debug
import design import design
from tech import parameter, layer from tech import layer
from tech import cell_properties as props from tech import cell_properties as props
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
@ -21,16 +21,15 @@ class dff_buf(design.design):
and qbar. This is to enable driving large fanout loads. and qbar. This is to enable driving large fanout loads.
""" """
unique_id = 1 unique_id = 1
def __init__(self, inv1_size=2, inv2_size=4, name=""):
def __init__(self, inv1_size=2, inv2_size=4, name=""):
if name=="": if name=="":
name = "dff_buf_{0}".format(dff_buf.unique_id) name = "dff_buf_{0}".format(dff_buf.unique_id)
dff_buf.unique_id += 1 dff_buf.unique_id += 1
super().__init__(name) super().__init__(name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size)) self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width. # This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
# This causes a DRC in the pinv which assumes min width rails. This ensures the output # This causes a DRC in the pinv which assumes min width rails. This ensures the output
# contact does not violate spacing to the rail in the NMOS. # contact does not violate spacing to the rail in the NMOS.
@ -39,7 +38,7 @@ class dff_buf(design.design):
self.inv1_size=inv1_size self.inv1_size=inv1_size
self.inv2_size=inv2_size self.inv2_size=inv2_size
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -57,7 +56,7 @@ class dff_buf(design.design):
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_modules(self): def add_modules(self):
self.dff = factory.create(module_type="dff") self.dff = factory.create(module_type="dff")
self.add_mod(self.dff) self.add_mod(self.dff)
@ -71,7 +70,7 @@ class dff_buf(design.design):
size=self.inv2_size, size=self.inv2_size,
height=self.dff.height) height=self.dff.height)
self.add_mod(self.inv2) self.add_mod(self.inv2)
def add_pins(self): def add_pins(self):
self.add_pin("D", "INPUT") self.add_pin("D", "INPUT")
self.add_pin("Q", "OUTPUT") self.add_pin("Q", "OUTPUT")
@ -93,7 +92,7 @@ class dff_buf(design.design):
self.inv1_inst=self.add_inst(name="dff_buf_inv1", self.inv1_inst=self.add_inst(name="dff_buf_inv1",
mod=self.inv1) mod=self.inv1)
self.connect_inst(["qint", "Qb", "vdd", "gnd"]) self.connect_inst(["qint", "Qb", "vdd", "gnd"])
self.inv2_inst=self.add_inst(name="dff_buf_inv2", self.inv2_inst=self.add_inst(name="dff_buf_inv2",
mod=self.inv2) mod=self.inv2)
self.connect_inst(["Qb", "Q", "vdd", "gnd"]) self.connect_inst(["Qb", "Q", "vdd", "gnd"])
@ -119,16 +118,16 @@ class dff_buf(design.design):
except AttributeError: except AttributeError:
pass pass
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0)) self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0))
# Add INV2 to the right # Add INV2 to the right
self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
def route_wires(self): def route_wires(self):
if "li" in layer: if "li" in layer:
self.route_layer = "li" self.route_layer = "li"
else: else:
self.route_layer = "m1" self.route_layer = "m1"
# Route dff q to inv1 a # Route dff q to inv1 a
q_pin = self.dff_inst.get_pin("Q") q_pin = self.dff_inst.get_pin("Q")
a1_pin = self.inv1_inst.get_pin("A") a1_pin = self.inv1_inst.get_pin("A")
@ -143,7 +142,7 @@ class dff_buf(design.design):
a2_pin = self.inv2_inst.get_pin("A") a2_pin = self.inv2_inst.get_pin("A")
self.mid_qb_pos = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy()) self.mid_qb_pos = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
self.add_zjog(z1_pin.layer, z1_pin.center(), a2_pin.center()) self.add_zjog(z1_pin.layer, z1_pin.center(), a2_pin.center())
def add_layout_pins(self): def add_layout_pins(self):
# Continous vdd rail along with label. # Continous vdd rail along with label.
@ -161,7 +160,7 @@ class dff_buf(design.design):
offset=gnd_pin.ll(), offset=gnd_pin.ll(),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
clk_pin = self.dff_inst.get_pin("clk") clk_pin = self.dff_inst.get_pin("clk")
self.add_layout_pin(text="clk", self.add_layout_pin(text="clk",
layer=clk_pin.layer, layer=clk_pin.layer,

View File

@ -19,7 +19,7 @@ class dff_buf_array(design.design):
Unlike the data flops, these are never spaced out. Unlike the data flops, these are never spaced out.
""" """
unique_id = 1 unique_id = 1
def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""): def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""):
self.rows = rows self.rows = rows
self.columns = columns self.columns = columns
@ -31,10 +31,10 @@ class dff_buf_array(design.design):
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("rows: {0} cols: {1}".format(rows, columns))
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size)) self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))
self.inv1_size = inv1_size self.inv1_size = inv1_size
self.inv2_size = inv2_size self.inv2_size = inv2_size
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -110,7 +110,7 @@ class dff_buf_array(design.design):
pass pass
dff_pitch = self.dff.width + well_spacing + self.well_extend_active dff_pitch = self.dff.width + well_spacing + self.well_extend_active
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
# name = "Xdff_r{0}_c{1}".format(row, col) # name = "Xdff_r{0}_c{1}".format(row, col)
@ -122,7 +122,7 @@ class dff_buf_array(design.design):
mirror = "MX" mirror = "MX"
self.dff_insts[row, col].place(offset=base, self.dff_insts[row, col].place(offset=base,
mirror=mirror) mirror=mirror)
def get_din_name(self, row, col): def get_din_name(self, row, col):
if self.columns == 1: if self.columns == 1:
din_name = "din_{0}".format(row) din_name = "din_{0}".format(row)
@ -132,7 +132,7 @@ class dff_buf_array(design.design):
din_name = "din_{0}_{1}".format(row, col) din_name = "din_{0}_{1}".format(row, col)
return din_name return din_name
def get_dout_name(self, row, col): def get_dout_name(self, row, col):
if self.columns == 1: if self.columns == 1:
dout_name = "dout_{0}".format(row) dout_name = "dout_{0}".format(row)
@ -142,7 +142,7 @@ class dff_buf_array(design.design):
dout_name = "dout_{0}_{1}".format(row, col) dout_name = "dout_{0}_{1}".format(row, col)
return dout_name return dout_name
def get_dout_bar_name(self, row, col): def get_dout_bar_name(self, row, col):
if self.columns == 1: if self.columns == 1:
dout_bar_name = "dout_bar_{0}".format(row) dout_bar_name = "dout_bar_{0}".format(row)
@ -158,11 +158,11 @@ class dff_buf_array(design.design):
vdd0_pin=self.dff_insts[row, 0].get_pin("vdd") vdd0_pin=self.dff_insts[row, 0].get_pin("vdd")
vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd") vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd")
self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height()) self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height())
gnd0_pin=self.dff_insts[row, 0].get_pin("gnd") gnd0_pin=self.dff_insts[row, 0].get_pin("gnd")
gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd") gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd")
self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height()) self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height())
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
# Continous vdd rail along with label. # Continous vdd rail along with label.
@ -172,9 +172,9 @@ class dff_buf_array(design.design):
# Continous gnd rail along with label. # Continous gnd rail along with label.
gnd_pin=self.dff_insts[row, col].get_pin("gnd") gnd_pin=self.dff_insts[row, col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.lc(), start_layer=gnd_pin.layer) self.add_power_pin("gnd", gnd_pin.lc(), start_layer=gnd_pin.layer)
def add_layout_pins(self): def add_layout_pins(self):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
din_pin = self.dff_insts[row, col].get_pin("D") din_pin = self.dff_insts[row, col].get_pin("D")
@ -200,7 +200,7 @@ class dff_buf_array(design.design):
offset=dout_bar_pin.ll(), offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(), width=dout_bar_pin.width(),
height=dout_bar_pin.height()) height=dout_bar_pin.height())
# Create vertical spines to a single horizontal rail # Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0, 0].get_pin("clk") clk_pin = self.dff_insts[0, 0].get_pin("clk")
clk_ypos = 2 * self.m3_pitch + self.m3_width clk_ypos = 2 * self.m3_pitch + self.m3_width

View File

@ -19,7 +19,7 @@ class dff_inv(design.design):
do not have Qbar, so this will create it. do not have Qbar, so this will create it.
""" """
unique_id = 1 unique_id = 1
def __init__(self, inv_size=2, name=""): def __init__(self, inv_size=2, name=""):
if name=="": if name=="":
@ -30,7 +30,7 @@ class dff_inv(design.design):
self.add_comment("inv: {0}".format(inv_size)) self.add_comment("inv: {0}".format(inv_size))
self.inv_size = inv_size self.inv_size = inv_size
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width. # This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
# This causes a DRC in the pinv which assumes min width rails. This ensures the output # This causes a DRC in the pinv which assumes min width rails. This ensures the output
# contact does not violate spacing to the rail in the NMOS. # contact does not violate spacing to the rail in the NMOS.
@ -44,7 +44,7 @@ class dff_inv(design.design):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_modules() self.create_modules()
def create_layout(self): def create_layout(self):
self.width = self.dff.width + self.inv1.width self.width = self.dff.width + self.inv1.width
self.height = self.dff.height self.height = self.dff.height
@ -52,10 +52,10 @@ class dff_inv(design.design):
self.place_modules() self.place_modules()
self.add_wires() self.add_wires()
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.add_pin("D") self.add_pin("D")
self.add_pin("Q") self.add_pin("Q")
@ -67,7 +67,7 @@ class dff_inv(design.design):
def add_modules(self): def add_modules(self):
self.dff = dff_inv.dff_inv(self.inv_size) self.dff = dff_inv.dff_inv(self.inv_size)
self.add_mod(self.dff) self.add_mod(self.dff)
self.inv1 = factory.create(module_type="pinv", self.inv1 = factory.create(module_type="pinv",
size=self.inv_size, size=self.inv_size,
height=self.dff.height) height=self.dff.height)
@ -88,8 +88,8 @@ class dff_inv(design.design):
# Place the INV1 to the right # Place the INV1 to the right
self.inv1_inst.place(vector(self.dff_inst.rx(),0)) self.inv1_inst.place(vector(self.dff_inst.rx(),0))
def add_wires(self): def add_wires(self):
# Route dff q to inv1 a # Route dff q to inv1 a
q_pin = self.dff_inst.get_pin("Q") q_pin = self.dff_inst.get_pin("Q")
@ -106,7 +106,7 @@ class dff_inv(design.design):
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=a1_pin.center()) offset=a1_pin.center())
def add_layout_pins(self): def add_layout_pins(self):
# Continous vdd rail along with label. # Continous vdd rail along with label.
@ -124,7 +124,7 @@ class dff_inv(design.design):
offset=gnd_pin.ll(), offset=gnd_pin.ll(),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
clk_pin = self.dff_inst.get_pin("clk") clk_pin = self.dff_inst.get_pin("clk")
self.add_layout_pin(text="clk", self.add_layout_pin(text="clk",
layer=clk_pin.layer, layer=clk_pin.layer,

View File

@ -19,7 +19,7 @@ class dff_inv_array(design.design):
Unlike the data flops, these are never spaced out. Unlike the data flops, these are never spaced out.
""" """
unique_id = 1 unique_id = 1
def __init__(self, rows, columns, inv_size=2, name=""): def __init__(self, rows, columns, inv_size=2, name=""):
self.rows = rows self.rows = rows
self.columns = columns self.columns = columns
@ -31,9 +31,9 @@ class dff_inv_array(design.design):
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("rows: {0} cols: {1}".format(rows, columns))
self.add_comment("inv1: {0}".format(inv1_size)) self.add_comment("inv1: {0}".format(inv1_size))
self.inv_size = inv_size self.inv_size = inv_size
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -42,7 +42,7 @@ class dff_inv_array(design.design):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_dff_array() self.create_dff_array()
def create_layout(self): def create_layout(self):
self.width = self.columns * self.dff.width self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height self.height = self.rows * self.dff.height
@ -53,14 +53,14 @@ class dff_inv_array(design.design):
self.DRC_LVS() self.DRC_LVS()
def add_modules(self): def add_modules(self):
self.dff = factory.create(module_type="dff") self.dff = factory.create(module_type="dff")
self.add_mod(self.dff) self.add_mod(self.dff)
def add_pins(self): def add_pins(self):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
self.add_pin(self.get_din_name(row,col), "INPUT") self.add_pin(self.get_din_name(row,col), "INPUT")
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
self.add_pin(self.get_dout_name(row,col), "OUTPUT") self.add_pin(self.get_dout_name(row,col), "OUTPUT")
self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT") self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT")
@ -70,20 +70,20 @@ class dff_inv_array(design.design):
def create_dff_array(self): def create_dff_array(self):
self.dff_insts={} self.dff_insts={}
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col) name = "Xdff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name, self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff) mod=self.dff)
self.connect_inst([self.get_din_name(row,col), self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col), self.get_dout_name(row,col),
self.get_dout_bar_name(row,col), self.get_dout_bar_name(row,col),
"clk", "clk",
"vdd", "vdd",
"gnd"]) "gnd"])
def place_dff_array(self): def place_dff_array(self):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col) name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0): if (row % 2 == 0):
@ -92,9 +92,9 @@ class dff_inv_array(design.design):
else: else:
base = vector(col*self.dff.width,(row+1)*self.dff.height) base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX" mirror = "MX"
self.dff_insts[row,col].place(offset=base, self.dff_insts[row,col].place(offset=base,
mirror=mirror) mirror=mirror)
def get_din_name(self, row, col): def get_din_name(self, row, col):
if self.columns == 1: if self.columns == 1:
din_name = "din_{0}".format(row) din_name = "din_{0}".format(row)
@ -104,7 +104,7 @@ class dff_inv_array(design.design):
din_name = "din_{0}_{1}".format(row,col) din_name = "din_{0}_{1}".format(row,col)
return din_name return din_name
def get_dout_name(self, row, col): def get_dout_name(self, row, col):
if self.columns == 1: if self.columns == 1:
dout_name = "dout_{0}".format(row) dout_name = "dout_{0}".format(row)
@ -114,7 +114,7 @@ class dff_inv_array(design.design):
dout_name = "dout_{0}_{1}".format(row,col) dout_name = "dout_{0}_{1}".format(row,col)
return dout_name return dout_name
def get_dout_bar_name(self, row, col): def get_dout_bar_name(self, row, col):
if self.columns == 1: if self.columns == 1:
dout_bar_name = "dout_bar_{0}".format(row) dout_bar_name = "dout_bar_{0}".format(row)
@ -124,10 +124,10 @@ class dff_inv_array(design.design):
dout_bar_name = "dout_bar_{0}_{1}".format(row,col) dout_bar_name = "dout_bar_{0}_{1}".format(row,col)
return dout_bar_name return dout_bar_name
def add_layout_pins(self): def add_layout_pins(self):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
# Adds power pin on left of row # Adds power pin on left of row
vdd_pin=self.dff_insts[row,col].get_pin("vdd") vdd_pin=self.dff_insts[row,col].get_pin("vdd")
self.add_power_pin("vdd", vdd_pin.lc()) self.add_power_pin("vdd", vdd_pin.lc())
@ -135,10 +135,10 @@ class dff_inv_array(design.design):
# Adds gnd pin on left of row # Adds gnd pin on left of row
gnd_pin=self.dff_insts[row,col].get_pin("gnd") gnd_pin=self.dff_insts[row,col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.lc()) self.add_power_pin("gnd", gnd_pin.lc())
for row in range(self.rows):
for col in range(self.columns): for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row,col].get_pin("D") din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="m2","DFF D pin not on metal2") debug.check(din_pin.layer=="m2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(row,col), self.add_layout_pin(text=self.get_din_name(row,col),
@ -163,7 +163,7 @@ class dff_inv_array(design.design):
width=dout_bar_pin.width(), width=dout_bar_pin.width(),
height=dout_bar_pin.height()) height=dout_bar_pin.height())
# Create vertical spines to a single horizontal rail # Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk") clk_pin = self.dff_insts[0,0].get_pin("clk")
clk_ypos = 2*self.m3_pitch+self.m3_width clk_ypos = 2*self.m3_pitch+self.m3_width
@ -188,4 +188,4 @@ class dff_inv_array(design.design):
height=self.height) height=self.height)
# Drop a via to the M3 pin # Drop a via to the M3 pin
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=vector(clk_pin.cx(),clk_ypos)) offset=vector(clk_pin.cx(),clk_ypos))

View File

@ -1,6 +1,6 @@
# See LICENSE for licensing information. # See LICENSE for licensing information.
# #
# Copyright (c) 2016-2019 Regents of the University of California # Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved. # All rights reserved.
# #
from bitcell_base_array import bitcell_base_array from bitcell_base_array import bitcell_base_array
@ -20,13 +20,13 @@ class dummy_array(bitcell_base_array):
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
def create_netlist(self): def create_netlist(self):
""" Create and connect the netlist """ """ Create and connect the netlist """
# This will create a default set of bitline/wordline names # This will create a default set of bitline/wordline names
self.create_all_bitline_names() self.create_all_bitline_names()
self.create_all_wordline_names() self.create_all_wordline_names()
self.add_modules() self.add_modules()
self.add_pins() self.add_pins()
self.create_instances() self.create_instances()
@ -38,16 +38,16 @@ class dummy_array(bitcell_base_array):
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_modules(self): def add_modules(self):
""" Add the modules used in this design """ """ Add the modules used in this design """
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell)) self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
self.cell = factory.create(module_type="bitcell") self.cell = factory.create(module_type="bitcell")
self.add_mod(self.dummy_cell) self.add_mod(self.dummy_cell)
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ """ Create the module instances used in this design """
self.cell_inst = {} self.cell_inst = {}
@ -67,7 +67,7 @@ class dummy_array(bitcell_base_array):
self.add_pin(wl_name, "INPUT") self.add_pin(wl_name, "INPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_layout_pins(self): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """
@ -86,7 +86,7 @@ class dummy_array(bitcell_base_array):
offset=br_pin.ll().scale(1, 0), offset=br_pin.ll().scale(1, 0),
width=br_pin.width(), width=br_pin.width(),
height=self.height) height=self.height)
wl_names = self.cell.get_all_wl_names() wl_names = self.cell.get_all_wl_names()
for row in range(self.row_size): for row in range(self.row_size):
for port in self.all_ports: for port in self.all_ports:
@ -104,7 +104,7 @@ class dummy_array(bitcell_base_array):
inst = self.cell_inst[row, col] inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name) self.copy_layout_pin(inst, pin_name)
def input_load(self): def input_load(self):
# FIXME: This appears to be old code from previous characterization. Needs to be updated. # FIXME: This appears to be old code from previous characterization. Needs to be updated.
wl_wire = self.gen_wl_wire() wl_wire = self.gen_wl_wire()

View File

@ -27,11 +27,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
debug.check(len(self.all_ports)<=2, "Only support dual port or less in global bitcell array.") debug.check(len(self.all_ports)<=2, "Only support dual port or less in global bitcell array.")
self.rbl = [1, 1 if len(self.all_ports)>1 else 0] self.rbl = [1, 1 if len(self.all_ports)>1 else 0]
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
def create_netlist(self): def create_netlist(self):
""" Create and connect the netlist """ """ Create and connect the netlist """
self.add_modules() self.add_modules()
@ -43,11 +43,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
self.place() self.place()
self.route() self.route()
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_modules(self): def add_modules(self):
@ -88,7 +88,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
rows=self.row_size, rows=self.row_size,
cols=cols, cols=cols,
rbl=self.rbl) rbl=self.rbl)
self.add_mod(la) self.add_mod(la)
self.local_mods.append(la) self.local_mods.append(la)
@ -108,7 +108,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
self.rbl_bitline_names[0].append("rbl_bl_{}_0".format(port)) self.rbl_bitline_names[0].append("rbl_bl_{}_0".format(port))
for port in self.all_ports: for port in self.all_ports:
self.rbl_bitline_names[0].append("rbl_br_{}_0".format(port)) self.rbl_bitline_names[0].append("rbl_br_{}_0".format(port))
for col in range(self.column_size): for col in range(self.column_size):
for port in self.all_ports: for port in self.all_ports:
self.bitline_names[port].append("bl_{0}_{1}".format(port, col)) self.bitline_names[port].append("bl_{0}_{1}".format(port, col))
@ -120,7 +120,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
self.rbl_bitline_names[1].append("rbl_bl_{}_1".format(port)) self.rbl_bitline_names[1].append("rbl_bl_{}_1".format(port))
for port in self.all_ports: for port in self.all_ports:
self.rbl_bitline_names[1].append("rbl_br_{}_1".format(port)) self.rbl_bitline_names[1].append("rbl_br_{}_1".format(port))
# Make a flat list too # Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
# Make a flat list too # Make a flat list too
@ -130,19 +130,19 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
self.add_pin_list(self.all_bitline_names, "INOUT") self.add_pin_list(self.all_bitline_names, "INOUT")
if len(self.all_ports) > 1: if len(self.all_ports) > 1:
self.add_pin_list(self.rbl_bitline_names[1], "INOUT") self.add_pin_list(self.rbl_bitline_names[1], "INOUT")
def add_wordline_pins(self): def add_wordline_pins(self):
self.rbl_wordline_names = [[] for x in self.all_ports] self.rbl_wordline_names = [[] for x in self.all_ports]
self.wordline_names = [[] for x in self.all_ports] self.wordline_names = [[] for x in self.all_ports]
for bit in self.all_ports: for bit in self.all_ports:
for port in self.all_ports: for port in self.all_ports:
self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit)) self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit))
self.all_rbl_wordline_names = [x for sl in zip(*self.rbl_wordline_names) for x in sl] self.all_rbl_wordline_names = [x for sl in zip(*self.rbl_wordline_names) for x in sl]
# Regular WLs # Regular WLs
for row in range(self.row_size): for row in range(self.row_size):
for port in self.all_ports: for port in self.all_ports:
@ -163,11 +163,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
name = "la_{0}".format(col) name = "la_{0}".format(col)
self.local_insts.append(self.add_inst(name=name, self.local_insts.append(self.add_inst(name=name,
mod=mod)) mod=mod))
temp = [] temp = []
if col == 0: if col == 0:
temp.extend(self.get_rbl_bitline_names(0)) temp.extend(self.get_rbl_bitline_names(0))
port_inouts = [x for x in mod.get_inouts() if x.startswith("bl") or x.startswith("br")] port_inouts = [x for x in mod.get_inouts() if x.startswith("bl") or x.startswith("br")]
for pin_name in port_inouts: for pin_name in port_inouts:
# Offset of the last underscore that defines the bit number # Offset of the last underscore that defines the bit number
@ -180,18 +180,18 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
# Strip the bit and add the new one # Strip the bit and add the new one
new_name = "{0}_{1}".format(base_name, col + col_value) new_name = "{0}_{1}".format(base_name, col + col_value)
temp.append(new_name) temp.append(new_name)
if len(self.all_ports) > 1 and mod == self.local_mods[-1]: if len(self.all_ports) > 1 and mod == self.local_mods[-1]:
temp.extend(self.get_rbl_bitline_names(1)) temp.extend(self.get_rbl_bitline_names(1))
for port in self.all_ports: for port in self.all_ports:
port_inputs = [x for x in mod.get_inputs() if "wl_{}".format(port) in x] port_inputs = [x for x in mod.get_inputs() if "wl_{}".format(port) in x]
temp.extend(port_inputs) temp.extend(port_inputs)
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place(self): def place(self):
offset = vector(0, 0) offset = vector(0, 0)
for inst in self.local_insts: for inst in self.local_insts:
@ -204,7 +204,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
def route(self): def route(self):
pass pass
def add_layout_pins(self): def add_layout_pins(self):
# Regular bitlines # Regular bitlines
@ -230,11 +230,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
layer=left_pin.layer, layer=left_pin.layer,
start=left_pin.lc(), start=left_pin.lc(),
end=right_pin.rc()) end=right_pin.rc())
# Replica bitlines # Replica bitlines
self.copy_layout_pin(self.local_insts[0], "rbl_bl_0_0") self.copy_layout_pin(self.local_insts[0], "rbl_bl_0_0")
self.copy_layout_pin(self.local_insts[0], "rbl_br_0_0") self.copy_layout_pin(self.local_insts[0], "rbl_br_0_0")
if len(self.all_ports) > 1: if len(self.all_ports) > 1:
self.copy_layout_pin(self.local_insts[0], "rbl_bl_1_0") self.copy_layout_pin(self.local_insts[0], "rbl_bl_1_0")
self.copy_layout_pin(self.local_insts[0], "rbl_br_1_0") self.copy_layout_pin(self.local_insts[0], "rbl_br_1_0")
@ -269,10 +269,10 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
return offsets return offsets
def graph_exclude_bits(self, targ_row, targ_col): def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
""" """
Excludes bits in column from being added to graph except target
"""
# This must find which local array includes the specified column # This must find which local array includes the specified column
# Find the summation of columns that is large and take the one before # Find the summation of columns that is large and take the one before
for i, col in enumerate(self.col_offsets): for i, col in enumerate(self.col_offsets):
@ -303,7 +303,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
def get_cell_name(self, inst_name, row, col): def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell.""" """Gets the spice name of the target bitcell."""
# This must find which local array includes the specified column # This must find which local array includes the specified column
# Find the summation of columns that is large and take the one before # Find the summation of columns that is large and take the one before
for i, local_col in enumerate(self.col_offsets): for i, local_col in enumerate(self.col_offsets):
@ -321,9 +321,9 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
local_col = col - self.col_offsets[i - 1] local_col = col - self.col_offsets[i - 1]
return local_array.get_cell_name(inst_name + '.x' + local_inst.name, row, local_col) return local_array.get_cell_name(inst_name + '.x' + local_inst.name, row, local_col)
def clear_exclude_bits(self): def clear_exclude_bits(self):
""" """
Clears the bit exclusions Clears the bit exclusions
""" """
for mod in self.local_mods: for mod in self.local_mods:
@ -331,6 +331,6 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
def graph_exclude_dffs(self): def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path""" """Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst) self.graph_inst_exclude.add(self.ctrl_dff_inst)

View File

@ -22,14 +22,14 @@ class hierarchical_decoder(design.design):
super().__init__(name) super().__init__(name)
self.AND_FORMAT = "DEC_AND_{0}" self.AND_FORMAT = "DEC_AND_{0}"
self.pre2x4_inst = [] self.pre2x4_inst = []
self.pre3x8_inst = [] self.pre3x8_inst = []
self.pre4x16_inst = [] self.pre4x16_inst = []
b = factory.create(module_type="bitcell") b = factory.create(module_type="bitcell")
self.cell_height = b.height self.cell_height = b.height
self.num_outputs = num_outputs self.num_outputs = num_outputs
self.num_inputs = math.ceil(math.log(self.num_outputs, 2)) self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
(self.no_of_pre2x4, self.no_of_pre3x8, self.no_of_pre4x16)=self.determine_predecodes(self.num_inputs) (self.no_of_pre2x4, self.no_of_pre3x8, self.no_of_pre4x16)=self.determine_predecodes(self.num_inputs)
@ -37,7 +37,7 @@ class hierarchical_decoder(design.design):
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
def create_netlist(self): def create_netlist(self):
self.add_modules() self.add_modules()
self.setup_netlist_constants() self.setup_netlist_constants()
@ -49,33 +49,33 @@ class hierarchical_decoder(design.design):
self.setup_layout_constants() self.setup_layout_constants()
self.place_pre_decoder() self.place_pre_decoder()
self.place_row_decoder() self.place_row_decoder()
self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space
self.route_inputs() self.route_inputs()
self.route_outputs() self.route_outputs()
self.route_decoder_bus() self.route_decoder_bus()
self.route_vdd_gnd() self.route_vdd_gnd()
self.offset_x_coordinates() self.offset_x_coordinates()
self.width = self.and_inst[0].rx() + 0.5 * self.m1_width self.width = self.and_inst[0].rx() + 0.5 * self.m1_width
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_modules(self): def add_modules(self):
self.and2 = factory.create(module_type="and2_dec", self.and2 = factory.create(module_type="and2_dec",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.and2) self.add_mod(self.and2)
self.and3 = factory.create(module_type="and3_dec", self.and3 = factory.create(module_type="and3_dec",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.and3) self.add_mod(self.and3)
# TBD # TBD
# self.and4 = factory.create(module_type="and4_dec") # self.and4 = factory.create(module_type="and4_dec")
# self.add_mod(self.and4) # self.add_mod(self.and4)
self.add_decoders() self.add_decoders()
def add_decoders(self): def add_decoders(self):
@ -83,7 +83,7 @@ class hierarchical_decoder(design.design):
self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4", self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.pre2_4) self.add_mod(self.pre2_4)
self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8", self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.pre3_8) self.add_mod(self.pre3_8)
@ -91,11 +91,11 @@ class hierarchical_decoder(design.design):
self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16", self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.pre4_16) self.add_mod(self.pre4_16)
def determine_predecodes(self, num_inputs): def determine_predecodes(self, num_inputs):
""" """
Determines the number of 2:4, 3:8 and 4:16 pre-decoders Determines the number of 2:4, 3:8 and 4:16 pre-decoders
needed based on the number of inputs needed based on the number of inputs
""" """
if (num_inputs == 2): if (num_inputs == 2):
return (1, 0, 0) return (1, 0, 0)
@ -151,7 +151,7 @@ class hierarchical_decoder(design.design):
lines.append(index) lines.append(index)
index = index + 1 index = index + 1
self.predec_groups.append(lines) self.predec_groups.append(lines)
def setup_layout_constants(self): def setup_layout_constants(self):
""" Calculate the overall dimensions of the hierarchical decoder """ """ Calculate the overall dimensions of the hierarchical decoder """
@ -190,7 +190,7 @@ class hierarchical_decoder(design.design):
self.input_layer = layer_props.hierarchical_decoder.input_layer self.input_layer = layer_props.hierarchical_decoder.input_layer
self.output_layer = layer_props.hierarchical_decoder.output_layer self.output_layer = layer_props.hierarchical_decoder.output_layer
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch") self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
# Two extra pitches between modules on left and right # Two extra pitches between modules on left and right
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch
self.row_decoder_height = self.and2.height * self.num_outputs self.row_decoder_height = self.and2.height * self.num_outputs
@ -215,7 +215,7 @@ class hierarchical_decoder(design.design):
offset=input_offset, offset=input_offset,
names=input_bus_names, names=input_bus_names,
length=self.predecoder_height) length=self.predecoder_height)
self.route_input_to_predecodes() self.route_input_to_predecodes()
def route_input_to_predecodes(self): def route_input_to_predecodes(self):
@ -231,13 +231,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.center() decoder_offset = decoder_pin.center()
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_bus(decoder_offset, input_offset) self.route_input_bus(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre3x8): for pre_num in range(self.no_of_pre3x8):
for i in range(3): for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2 index = pre_num * 3 + i + self.no_of_pre2x4 * 2
input_pos = self.input_bus["addr_{}".format(index)].center() input_pos = self.input_bus["addr_{}".format(index)].center()
in_name = "in_{}".format(i) in_name = "in_{}".format(i)
@ -245,13 +245,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.center() decoder_offset = decoder_pin.center()
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_bus(decoder_offset, input_offset) self.route_input_bus(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre4x16): for pre_num in range(self.no_of_pre4x16):
for i in range(4): for i in range(4):
index = pre_num * 4 + i + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2 index = pre_num * 4 + i + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2
input_pos = self.input_bus["addr_{}".format(index)].center() input_pos = self.input_bus["addr_{}".format(index)].center()
in_name = "in_{}".format(i) in_name = "in_{}".format(i)
@ -259,15 +259,15 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.center() decoder_offset = decoder_pin.center()
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_bus(decoder_offset, input_offset) self.route_input_bus(decoder_offset, input_offset)
def route_input_bus(self, input_offset, output_offset): def route_input_bus(self, input_offset, output_offset):
""" """
Route a vertical M2 coordinate to another Route a vertical M2 coordinate to another
vertical M2 coordinate to the predecode inputs vertical M2 coordinate to the predecode inputs
""" """
self.add_via_stack_center(from_layer=self.bus_layer, self.add_via_stack_center(from_layer=self.bus_layer,
to_layer=self.input_layer, to_layer=self.input_layer,
offset=input_offset) offset=input_offset)
@ -276,10 +276,10 @@ class hierarchical_decoder(design.design):
offset=output_offset, offset=output_offset,
directions=self.bus_directions) directions=self.bus_directions)
self.add_path(self.input_layer, [input_offset, output_offset]) self.add_path(self.input_layer, [input_offset, output_offset])
def add_pins(self): def add_pins(self):
""" Add the module pins """ """ Add the module pins """
for i in range(self.num_inputs): for i in range(self.num_inputs):
self.add_pin("addr_{0}".format(i), "INPUT") self.add_pin("addr_{0}".format(i), "INPUT")
@ -290,10 +290,10 @@ class hierarchical_decoder(design.design):
def create_pre_decoder(self): def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """ """ Creates pre-decoder and places labels input address [A] """
for i in range(self.no_of_pre2x4): for i in range(self.no_of_pre2x4):
self.create_pre2x4(i) self.create_pre2x4(i)
for i in range(self.no_of_pre3x8): for i in range(self.no_of_pre3x8):
self.create_pre3x8(i) self.create_pre3x8(i)
@ -302,7 +302,7 @@ class hierarchical_decoder(design.design):
def create_pre2x4(self, num): def create_pre2x4(self, num):
""" Add a 2x4 predecoder to the left of the origin """ """ Add a 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2): if (self.num_inputs == 2):
index_off1 = index_off2 = 0 index_off1 = index_off2 = 0
else: else:
@ -355,19 +355,19 @@ class hierarchical_decoder(design.design):
self.pre4x16_inst.append(self.add_inst(name="pre4x16_{0}".format(num), self.pre4x16_inst.append(self.add_inst(name="pre4x16_{0}".format(num),
mod=self.pre4_16)) mod=self.pre4_16))
self.connect_inst(pins) self.connect_inst(pins)
def place_pre_decoder(self): def place_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """ """ Creates pre-decoder and places labels input address [A] """
for i in range(self.no_of_pre2x4): for i in range(self.no_of_pre2x4):
self.place_pre2x4(i) self.place_pre2x4(i)
for i in range(self.no_of_pre3x8): for i in range(self.no_of_pre3x8):
self.place_pre3x8(i) self.place_pre3x8(i)
for i in range(self.no_of_pre4x16): for i in range(self.no_of_pre4x16):
self.place_pre4x16(i) self.place_pre4x16(i)
self.predecode_height = 0 self.predecode_height = 0
if self.no_of_pre2x4 > 0: if self.no_of_pre2x4 > 0:
self.predecode_height = self.pre2x4_inst[-1].uy() self.predecode_height = self.pre2x4_inst[-1].uy()
@ -378,10 +378,10 @@ class hierarchical_decoder(design.design):
def place_pre2x4(self, num): def place_pre2x4(self, num):
""" Place 2x4 predecoder to the left of the origin """ """ Place 2x4 predecoder to the left of the origin """
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing)) base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
self.pre2x4_inst[num].place(base) self.pre2x4_inst[num].place(base)
def place_pre3x8(self, num): def place_pre3x8(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \ height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \
@ -396,7 +396,7 @@ class hierarchical_decoder(design.design):
+ num * (self.pre4_16.height + self.predecoder_spacing) + num * (self.pre4_16.height + self.predecoder_spacing)
offset = vector(-self.pre4_16.width, height) offset = vector(-self.pre4_16.width, height)
self.pre4x16_inst[num].place(offset) self.pre4x16_inst[num].place(offset)
def create_row_decoder(self): def create_row_decoder(self):
""" Create the row-decoder by placing AND2/AND3 and Inverters """ Create the row-decoder by placing AND2/AND3 and Inverters
and add the primary decoder output pins. """ and add the primary decoder output pins. """
@ -407,7 +407,7 @@ class hierarchical_decoder(design.design):
""" Add a column of AND gates for final decode """ """ Add a column of AND gates for final decode """
self.and_inst = [] self.and_inst = []
# Row Decoder AND GATE array for address inputs <5. # Row Decoder AND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
for i in range(len(self.predec_groups[0])): for i in range(len(self.predec_groups[0])):
@ -435,7 +435,7 @@ class hierarchical_decoder(design.design):
name = self.AND_FORMAT.format(output) name = self.AND_FORMAT.format(output)
self.and_inst.append(self.add_inst(name=name, self.and_inst.append(self.add_inst(name=name,
mod=self.and3)) mod=self.and3))
pins = ["out_{0}".format(i), pins = ["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(j + len(self.predec_groups[0])),
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), "out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
@ -456,7 +456,7 @@ class hierarchical_decoder(design.design):
Add a column of AND gates for final decode. Add a column of AND gates for final decode.
This may have more than one decoder per row to match the bitcell height. This may have more than one decoder per row to match the bitcell height.
""" """
# Row Decoder AND GATE array for address inputs <5. # Row Decoder AND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
self.place_and_array(and_mod=self.and2) self.place_and_array(and_mod=self.and2)
@ -489,7 +489,7 @@ class hierarchical_decoder(design.design):
for row in range(self.num_outputs): for row in range(self.num_outputs):
and_inst = self.and_inst[row] and_inst = self.and_inst[row]
self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row)) self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row))
def route_decoder_bus(self): def route_decoder_bus(self):
""" """
Creates vertical metal 2 bus to connect predecoder and decoder stages. Creates vertical metal 2 bus to connect predecoder and decoder stages.
@ -522,7 +522,7 @@ class hierarchical_decoder(design.design):
x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch
y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
# FIXME: convert to connect_bus # FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre3x8): for pre_num in range(self.no_of_pre3x8):
for i in range(8): for i in range(8):
@ -542,7 +542,7 @@ class hierarchical_decoder(design.design):
x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch
y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
def route_bus_to_decoder(self): def route_bus_to_decoder(self):
""" """
Use the self.predec_groups to determine the connections to the decoder AND gates. Use the self.predec_groups to determine the connections to the decoder AND gates.
@ -555,7 +555,7 @@ class hierarchical_decoder(design.design):
and the 128th AND3 is connected to [3,7,15] and the 128th AND3 is connected to [3,7,15]
""" """
output_index = 0 output_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
for index_A in self.predec_groups[0]: for index_A in self.predec_groups[0]:
@ -596,7 +596,7 @@ class hierarchical_decoder(design.design):
Add a pin for each row of vdd/gnd which are Add a pin for each row of vdd/gnd which are
must-connects next level up. must-connects next level up.
""" """
if layer_props.hierarchical_decoder.vertical_supply: if layer_props.hierarchical_decoder.vertical_supply:
for n in ["vdd", "gnd"]: for n in ["vdd", "gnd"]:
pins = self.and_inst[0].get_pins(n) pins = self.and_inst[0].get_pins(n)
@ -636,7 +636,7 @@ class hierarchical_decoder(design.design):
for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst: for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst:
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(pre, pin_name) self.copy_layout_pin(pre, pin_name)
def route_predecode_bus_outputs(self, rail_name, pin, row): def route_predecode_bus_outputs(self, rail_name, pin, row):
""" """
Connect the routing rail to the given metal1 pin Connect the routing rail to the given metal1 pin
@ -646,17 +646,17 @@ class hierarchical_decoder(design.design):
pin_pos = pin.center() pin_pos = pin.center()
rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y) rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y)
self.add_path(self.input_layer, [rail_pos, pin_pos]) self.add_path(self.input_layer, [rail_pos, pin_pos])
self.add_via_stack_center(from_layer=self.bus_layer, self.add_via_stack_center(from_layer=self.bus_layer,
to_layer=self.input_layer, to_layer=self.input_layer,
offset=rail_pos, offset=rail_pos,
directions=self.bus_directions) directions=self.bus_directions)
self.add_via_stack_center(from_layer=pin.layer, self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.input_layer, to_layer=self.input_layer,
offset=pin_pos, offset=pin_pos,
directions=("H", "H")) directions=("H", "H"))
def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset): def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset):
""" """
Connect the routing rail to the given metal1 pin using a jog Connect the routing rail to the given metal1 pin using a jog

View File

@ -31,10 +31,10 @@ class hierarchical_predecode(design.design):
# If we are pitch matched to the bitcell, it's a predecoder # If we are pitch matched to the bitcell, it's a predecoder
# otherwise it's a column decoder (out of pgates) # otherwise it's a column decoder (out of pgates)
self.column_decoder = (height != b.height) self.column_decoder = (height != b.height)
self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
super().__init__(name) super().__init__(name)
def add_pins(self): def add_pins(self):
for k in range(self.number_of_inputs): for k in range(self.number_of_inputs):
self.add_pin("in_{0}".format(k), "INPUT") self.add_pin("in_{0}".format(k), "INPUT")
@ -48,7 +48,7 @@ class hierarchical_predecode(design.design):
debug.check(self.number_of_inputs <= 4, debug.check(self.number_of_inputs <= 4,
"Invalid number of predecode inputs: {}".format(self.number_of_inputs)) "Invalid number of predecode inputs: {}".format(self.number_of_inputs))
if self.column_decoder: if self.column_decoder:
and_type = "pand{}".format(self.number_of_inputs) and_type = "pand{}".format(self.number_of_inputs)
inv_type = "pinv" inv_type = "pinv"
@ -79,7 +79,7 @@ class hierarchical_predecode(design.design):
self.route() self.route()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def setup_layout_constraints(self): def setup_layout_constraints(self):
# Inputs to cells are on input layer # Inputs to cells are on input layer
@ -92,7 +92,7 @@ class hierarchical_predecode(design.design):
self.input_layer = layer_props.hierarchical_predecode.input_layer self.input_layer = layer_props.hierarchical_predecode.input_layer
self.output_layer = layer_props.hierarchical_predecode.output_layer self.output_layer = layer_props.hierarchical_predecode.output_layer
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch") self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
self.height = self.number_of_outputs * self.and_mod.height self.height = self.number_of_outputs * self.and_mod.height
# x offset for input inverters # x offset for input inverters
@ -139,7 +139,7 @@ class hierarchical_predecode(design.design):
def place_input_inverters(self): def place_input_inverters(self):
""" Place the input inverters to invert input signals for the decode stage. """ """ Place the input inverters to invert input signals for the decode stage. """
for inv_num in range(self.number_of_inputs): for inv_num in range(self.number_of_inputs):
if (inv_num % 2 == 0): if (inv_num % 2 == 0):
y_off = inv_num * (self.inv.height) y_off = inv_num * (self.inv.height)
mirror = "R0" mirror = "R0"
@ -149,7 +149,7 @@ class hierarchical_predecode(design.design):
offset = vector(self.x_off_inv_1, y_off) offset = vector(self.x_off_inv_1, y_off)
self.inv_inst[inv_num].place(offset=offset, self.inv_inst[inv_num].place(offset=offset,
mirror=mirror) mirror=mirror)
def create_and_array(self, connections): def create_and_array(self, connections):
""" Create the AND stage for the decodes """ """ Create the AND stage for the decodes """
self.and_inst = [] self.and_inst = []
@ -196,7 +196,7 @@ class hierarchical_predecode(design.design):
pin = top_and_gate.get_pin("D") pin = top_and_gate.get_pin("D")
else: else:
debug.error("Too many inputs for predecoder.", -1) debug.error("Too many inputs for predecoder.", -1)
y_offset = pin.cy() y_offset = pin.cy()
in_pin = "in_{}".format(num) in_pin = "in_{}".format(num)
a_pin = "A_{}".format(num) a_pin = "A_{}".format(num)
@ -222,7 +222,7 @@ class hierarchical_predecode(design.design):
offset=z_pin.ll(), offset=z_pin.ll(),
height=z_pin.height(), height=z_pin.height(),
width=z_pin.width()) width=z_pin.width())
def route_input_inverters(self): def route_input_inverters(self):
""" """
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd] Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
@ -232,7 +232,7 @@ class hierarchical_predecode(design.design):
in_pin = "in_{}".format(inv_num) in_pin = "in_{}".format(inv_num)
inv_out_pin = self.inv_inst[inv_num].get_pin("Z") inv_out_pin = self.inv_inst[inv_num].get_pin("Z")
# add output so that it is just below the vdd or gnd rail # add output so that it is just below the vdd or gnd rail
# since this is where the p/n devices are and there are no # since this is where the p/n devices are and there are no
# pins in the and gates. # pins in the and gates.
@ -241,7 +241,7 @@ class hierarchical_predecode(design.design):
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0)
rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset) rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset)
self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
self.add_via_stack_center(from_layer=inv_out_pin.layer, self.add_via_stack_center(from_layer=inv_out_pin.layer,
to_layer=self.output_layer, to_layer=self.output_layer,
offset=inv_out_pos) offset=inv_out_pos)
@ -249,7 +249,7 @@ class hierarchical_predecode(design.design):
to_layer=self.bus_layer, to_layer=self.bus_layer,
offset=rail_pos, offset=rail_pos,
directions=self.bus_directions) directions=self.bus_directions)
# route input # route input
pin = self.inv_inst[inv_num].get_pin("A") pin = self.inv_inst[inv_num].get_pin("A")
inv_in_pos = pin.center() inv_in_pos = pin.center()
@ -267,11 +267,11 @@ class hierarchical_predecode(design.design):
offset=in_pos, offset=in_pos,
height=via.mod.second_layer_height, height=via.mod.second_layer_height,
width=via.mod.second_layer_width) width=via.mod.second_layer_width)
if layer_props.hierarchical_predecode.vertical_supply: if layer_props.hierarchical_predecode.vertical_supply:
below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height / 2)) below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height / 2))
self.add_path(self.bus_layer, [rail_pos, below_rail], width=self.li_width + self.m1_enclose_mcon * 2) self.add_path(self.bus_layer, [rail_pos, below_rail], width=self.li_width + self.m1_enclose_mcon * 2)
def route_and_to_rails(self): def route_and_to_rails(self):
# This 2D array defines the connection mapping # This 2D array defines the connection mapping
and_input_line_combination = self.get_and_input_line_combination() and_input_line_combination = self.get_and_input_line_combination()
@ -302,7 +302,7 @@ class hierarchical_predecode(design.design):
direction = None direction = None
else: else:
direction = ("H", "H") direction = ("H", "H")
self.add_via_stack_center(from_layer=pin.layer, self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.input_layer, to_layer=self.input_layer,
offset=pin_pos, offset=pin_pos,
@ -334,7 +334,7 @@ class hierarchical_predecode(design.design):
self.add_power_pin(name=n, self.add_power_pin(name=n,
loc=pin.uc(), loc=pin.uc(),
start_layer=pin.layer) start_layer=pin.layer)
# In other techs, we are using standard cell decoder cells with horizontal power # In other techs, we are using standard cell decoder cells with horizontal power
else: else:
for num in range(0, self.number_of_outputs): for num in range(0, self.number_of_outputs):
@ -354,6 +354,6 @@ class hierarchical_predecode(design.design):
self.add_power_pin(name=n, self.add_power_pin(name=n,
loc=pin_pos, loc=pin_pos,
start_layer=and_pin.layer) start_layer=and_pin.layer)

View File

@ -25,9 +25,9 @@ class hierarchical_predecode3x8(hierarchical_predecode):
self.add_modules() self.add_modules()
self.create_input_inverters() self.create_input_inverters()
connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"], connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"], ["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"], ["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"], ["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"], ["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"], ["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"], ["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
@ -41,7 +41,7 @@ class hierarchical_predecode3x8(hierarchical_predecode):
["Abar_0", "A_1", "Abar_2"], ["Abar_0", "A_1", "Abar_2"],
["A_0", "A_1", "Abar_2"], ["A_0", "A_1", "Abar_2"],
["Abar_0", "Abar_1", "A_2"], ["Abar_0", "Abar_1", "A_2"],
["A_0", "Abar_1", "A_2"], ["A_0", "Abar_1", "A_2"],
["Abar_0", "A_1", "A_2"], ["Abar_0", "A_1", "A_2"],
["A_0", "A_1", "A_2"]] ["A_0", "A_1", "A_2"]]
return combination return combination

View File

@ -25,17 +25,17 @@ class hierarchical_predecode4x16(hierarchical_predecode):
self.add_modules() self.add_modules()
self.create_input_inverters() self.create_input_inverters()
connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"], connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"], ["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"], ["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"], ["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"], ["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"], ["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"], ["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"],
["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"], ["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"],
["inbar_0", "inbar_1", "inbar_2", "in_3", "out_8", "vdd", "gnd"], ["inbar_0", "inbar_1", "inbar_2", "in_3", "out_8", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"], ["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "in_3", "out_10", "vdd", "gnd"], ["inbar_0", "in_1", "inbar_2", "in_3", "out_10", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"], ["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "in_3", "out_12", "vdd", "gnd"], ["inbar_0", "inbar_1", "in_2", "in_3", "out_12", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "in_3", "out_13", "vdd", "gnd"], ["in_0", "inbar_1", "in_2", "in_3", "out_13", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "in_3", "out_14", "vdd", "gnd"], ["inbar_0", "in_1", "in_2", "in_3", "out_14", "vdd", "gnd"],
@ -50,15 +50,15 @@ class hierarchical_predecode4x16(hierarchical_predecode):
["Abar_0", "A_1", "Abar_2", "Abar_3"], ["Abar_0", "A_1", "Abar_2", "Abar_3"],
["A_0", "A_1", "Abar_2", "Abar_3"], ["A_0", "A_1", "Abar_2", "Abar_3"],
["Abar_0", "Abar_1", "A_2" , "Abar_3"], ["Abar_0", "Abar_1", "A_2" , "Abar_3"],
["A_0", "Abar_1", "A_2" , "Abar_3"], ["A_0", "Abar_1", "A_2" , "Abar_3"],
["Abar_0", "A_1", "A_2" , "Abar_3"], ["Abar_0", "A_1", "A_2" , "Abar_3"],
["A_0", "A_1", "A_2" , "Abar_3"], ["A_0", "A_1", "A_2" , "Abar_3"],
["Abar_0", "Abar_1", "Abar_2", "A_3"], ["Abar_0", "Abar_1", "Abar_2", "A_3"],
["A_0", "Abar_1", "Abar_2", "A_3"], ["A_0", "Abar_1", "Abar_2", "A_3"],
["Abar_0", "A_1", "Abar_2", "A_3"], ["Abar_0", "A_1", "Abar_2", "A_3"],
["A_0", "A_1", "Abar_2", "A_3"], ["A_0", "A_1", "Abar_2", "A_3"],
["Abar_0", "Abar_1", "A_2", "A_3"], ["Abar_0", "Abar_1", "A_2", "A_3"],
["A_0", "Abar_1", "A_2", "A_3"], ["A_0", "Abar_1", "A_2", "A_3"],
["Abar_0", "A_1", "A_2", "A_3"], ["Abar_0", "A_1", "A_2", "A_3"],
["A_0", "A_1", "A_2", "A_3"]] ["A_0", "A_1", "A_2", "A_3"]]
return combination return combination

View File

@ -32,9 +32,9 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.rbl = rbl self.rbl = rbl
self.left_rbl = left_rbl self.left_rbl = left_rbl
self.right_rbl = right_rbl self.right_rbl = right_rbl
debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.") debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.")
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -42,7 +42,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
# We don't offset this because we need to align # We don't offset this because we need to align
# the replica bitcell in the control logic # the replica bitcell in the control logic
# self.offset_all_coordinates() # self.offset_all_coordinates()
def create_netlist(self): def create_netlist(self):
""" Create and connect the netlist """ """ Create and connect the netlist """
self.add_modules() self.add_modules()
@ -56,7 +56,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.add_layout_pins() self.add_layout_pins()
self.route() self.route()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -78,7 +78,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
rows=self.rows + 1, rows=self.rows + 1,
cols=self.cols) cols=self.cols)
self.add_mod(self.wl_array) self.add_mod(self.wl_array)
def add_pins(self): def add_pins(self):
# Outputs from the wordline driver (by port) # Outputs from the wordline driver (by port)
self.driver_wordline_outputs = [] self.driver_wordline_outputs = []
@ -87,18 +87,18 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.wordline_names = self.bitcell_array.wordline_names self.wordline_names = self.bitcell_array.wordline_names
self.all_wordline_names = self.bitcell_array.all_wordline_names self.all_wordline_names = self.bitcell_array.all_wordline_names
self.bitline_names = self.bitcell_array.bitline_names self.bitline_names = self.bitcell_array.bitline_names
self.all_bitline_names = self.bitcell_array.all_bitline_names self.all_bitline_names = self.bitcell_array.all_bitline_names
self.rbl_wordline_names = self.bitcell_array.rbl_wordline_names self.rbl_wordline_names = self.bitcell_array.rbl_wordline_names
self.all_rbl_wordline_names = self.bitcell_array.all_rbl_wordline_names self.all_rbl_wordline_names = self.bitcell_array.all_rbl_wordline_names
self.rbl_bitline_names = self.bitcell_array.rbl_bitline_names self.rbl_bitline_names = self.bitcell_array.rbl_bitline_names
self.all_rbl_bitline_names = self.bitcell_array.all_rbl_bitline_names self.all_rbl_bitline_names = self.bitcell_array.all_rbl_bitline_names
self.all_array_wordline_inputs = [x + "i" for x in self.bitcell_array.get_all_wordline_names()] self.all_array_wordline_inputs = [x + "i" for x in self.bitcell_array.get_all_wordline_names()]
# Arrays are always: # Arrays are always:
# bit lines (left to right) # bit lines (left to right)
# word lines (bottom to top) # word lines (bottom to top)
@ -133,10 +133,10 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
else: else:
temp += self.get_wordline_names(port)[::-1] temp += self.get_wordline_names(port)[::-1]
self.driver_wordline_outputs.append([x + "i" for x in temp]) self.driver_wordline_outputs.append([x + "i" for x in temp])
temp += self.driver_wordline_outputs[-1] temp += self.driver_wordline_outputs[-1]
temp += ["vdd", "gnd"] temp += ["vdd", "gnd"]
self.connect_inst(temp) self.connect_inst(temp)
self.bitcell_array_inst = self.add_inst(name="array", self.bitcell_array_inst = self.add_inst(name="array",
@ -180,7 +180,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
for x in self.get_inouts(): for x in self.get_inouts():
self.copy_layout_pin(self.bitcell_array_inst, x) self.copy_layout_pin(self.bitcell_array_inst, x)
supply_insts = [*self.wl_insts, self.bitcell_array_inst] supply_insts = [*self.wl_insts, self.bitcell_array_inst]
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
for inst in supply_insts: for inst in supply_insts:
@ -189,7 +189,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
self.add_power_pin(name=pin_name, self.add_power_pin(name=pin_name,
loc=pin.center(), loc=pin.center(),
start_layer=pin.layer) start_layer=pin.layer)
def route(self): def route(self):
# Route the global wordlines # Route the global wordlines
@ -198,27 +198,27 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port) wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)
else: else:
wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)[::-1] wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)[::-1]
wordline_pins = self.wl_array.get_inputs() wordline_pins = self.wl_array.get_inputs()
for (wl_name, in_pin_name) in zip(wordline_names, wordline_pins): for (wl_name, in_pin_name) in zip(wordline_names, wordline_pins):
# wl_pin = self.bitcell_array_inst.get_pin(wl_name) # wl_pin = self.bitcell_array_inst.get_pin(wl_name)
in_pin = self.wl_insts[port].get_pin(in_pin_name) in_pin = self.wl_insts[port].get_pin(in_pin_name)
y_offset = in_pin.cy() y_offset = in_pin.cy()
if port == 0: if port == 0:
y_offset -= 2 * self.m3_pitch y_offset -= 2 * self.m3_pitch
else: else:
y_offset += 2 * self.m3_pitch y_offset += 2 * self.m3_pitch
self.add_layout_pin_segment_center(text=wl_name, self.add_layout_pin_segment_center(text=wl_name,
layer="m3", layer="m3",
start=vector(self.wl_insts[port].lx(), y_offset), start=vector(self.wl_insts[port].lx(), y_offset),
end=vector(self.wl_insts[port].lx() + self.wl_array.width, y_offset)) end=vector(self.wl_insts[port].lx() + self.wl_array.width, y_offset))
mid = vector(in_pin.cx(), y_offset) mid = vector(in_pin.cx(), y_offset)
self.add_path("m2", [in_pin.center(), mid]) self.add_path("m2", [in_pin.center(), mid])
self.add_via_stack_center(from_layer=in_pin.layer, self.add_via_stack_center(from_layer=in_pin.layer,
to_layer="m2", to_layer="m2",
offset=in_pin.center()) offset=in_pin.center())
@ -228,7 +228,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
# Route the buffers # Route the buffers
for port in self.all_ports: for port in self.all_ports:
driver_outputs = self.driver_wordline_outputs[port] driver_outputs = self.driver_wordline_outputs[port]
for (driver_name, net_name) in zip(self.wl_insts[port].mod.get_outputs(), driver_outputs): for (driver_name, net_name) in zip(self.wl_insts[port].mod.get_outputs(), driver_outputs):
array_name = net_name[:-1] array_name = net_name[:-1]
out_pin = self.wl_insts[port].get_pin(driver_name) out_pin = self.wl_insts[port].get_pin(driver_name)
@ -242,7 +242,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
mid_loc = vector(self.wl_insts[port].lx() - 1.5 * self.m3_pitch, out_loc.y) mid_loc = vector(self.wl_insts[port].lx() - 1.5 * self.m3_pitch, out_loc.y)
in_loc = in_pin.rc() in_loc = in_pin.rc()
self.add_path(out_pin.layer, [out_loc, mid_loc, in_loc]) self.add_path(out_pin.layer, [out_loc, mid_loc, in_loc])
def get_main_array_top(self): def get_main_array_top(self):
return self.bitcell_array_inst.by() + self.bitcell_array.get_main_array_top() return self.bitcell_array_inst.by() + self.bitcell_array.get_main_array_top()
@ -262,13 +262,13 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
# must add the offset of the instance # must add the offset of the instance
offsets = [self.bitcell_array_inst.lx() + x for x in self.bitcell_array.get_column_offsets()] offsets = [self.bitcell_array_inst.lx() + x for x in self.bitcell_array.get_column_offsets()]
return offsets return offsets
def graph_exclude_bits(self, targ_row=None, targ_col=None): def graph_exclude_bits(self, targ_row=None, targ_col=None):
""" """
Excludes bits in column from being added to graph except target Excludes bits in column from being added to graph except target
""" """
self.bitcell_array.graph_exclude_bits(targ_row, targ_col) self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def graph_exclude_replica_col_bits(self): def graph_exclude_replica_col_bits(self):
""" """
Exclude all but replica in the local array. Exclude all but replica in the local array.
@ -281,8 +281,8 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col) return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col)
def clear_exclude_bits(self): def clear_exclude_bits(self):
""" """
Clears the bit exclusions Clears the bit exclusions
""" """
self.bitcell_array.clear_exclude_bits() self.bitcell_array.clear_exclude_bits()

View File

@ -19,9 +19,9 @@ from globals import OPTS
class multibank(design.design): class multibank(design.design):
""" """
Dynamically generated a single bank including bitcell array, Dynamically generated a single bank including bitcell array,
hierarchical_decoder, precharge, (optional column_mux and column decoder), hierarchical_decoder, precharge, (optional column_mux and column decoder),
write driver and sense amplifiers. write driver and sense amplifiers.
This module includes the tristate and bank select logic. This module includes the tristate and bank select logic.
""" """
def __init__(self, name, word_size, num_words, words_per_row, num_banks=1): def __init__(self, name, word_size, num_words, words_per_row, num_banks=1):
@ -41,7 +41,7 @@ class multibank(design.design):
self.prefix="gated_" self.prefix="gated_"
else: else:
self.prefix="" self.prefix=""
self.compute_sizes() self.compute_sizes()
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
@ -50,16 +50,16 @@ class multibank(design.design):
# FIXME: Move this to the add modules function # FIXME: Move this to the add modules function
self.add_bank_select() self.add_bank_select()
self.route_layout() self.route_layout()
# Can remove the following, but it helps for debug! # Can remove the following, but it helps for debug!
self.add_lvs_correspondence_points() self.add_lvs_correspondence_points()
# Remember the bank center for further placement # Remember the bank center for further placement
self.bank_center=self.offset_all_coordinates().scale(-1,-1) self.bank_center=self.offset_all_coordinates().scale(-1,-1)
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
@ -91,23 +91,23 @@ class multibank(design.design):
#self.route_tri_gate_out() #self.route_tri_gate_out()
self.route_sense_amp_out() self.route_sense_amp_out()
self.route_wordline_driver() self.route_wordline_driver()
self.route_write_driver() self.route_write_driver()
self.route_row_decoder() self.route_row_decoder()
self.route_column_address_lines() self.route_column_address_lines()
self.route_control_lines() self.route_control_lines()
self.add_control_pins() self.add_control_pins()
if self.num_banks > 1: if self.num_banks > 1:
self.route_bank_select() self.route_bank_select()
self.route_supplies() self.route_supplies()
def create_instances(self): def create_instances(self):
""" Add modules. The order should not matter! """ """ Add modules. The order should not matter! """
# Above the bitcell array # Above the bitcell array
self.add_bitcell_array() self.add_bitcell_array()
self.add_precharge_array() self.add_precharge_array()
# Below the bitcell array # Below the bitcell array
self.add_column_mux_array() self.add_column_mux_array()
self.add_sense_amp_array() self.add_sense_amp_array()
@ -120,7 +120,7 @@ class multibank(design.design):
self.add_wordline_driver() self.add_wordline_driver()
self.add_column_decoder() self.add_column_decoder()
def compute_sizes(self): def compute_sizes(self):
""" Computes the required sizes to create the bank """ """ Computes the required sizes to create the bank """
@ -139,7 +139,7 @@ class multibank(design.design):
self.supply_rail_width = 4*self.m2_width self.supply_rail_width = 4*self.m2_width
# FIXME: This spacing should be width dependent... # FIXME: This spacing should be width dependent...
self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space
# Number of control lines in the bus # Number of control lines in the bus
self.num_control_lines = 6 self.num_control_lines = 6
# The order of the control signals on the control bus: # The order of the control signals on the control bus:
@ -153,7 +153,7 @@ class multibank(design.design):
if self.col_addr_size>0: if self.col_addr_size>0:
self.num_col_addr_lines = 2**self.col_addr_size self.num_col_addr_lines = 2**self.col_addr_size
else: else:
self.num_col_addr_lines = 0 self.num_col_addr_lines = 0
# The width of this bus is needed to place other modules (e.g. decoder) # The width of this bus is needed to place other modules (e.g. decoder)
# A width on each side too # A width on each side too
@ -169,7 +169,7 @@ class multibank(design.design):
""" Add all the modules using the class loader """ """ Add all the modules using the class loader """
self.tri = self.mod_tri_gate() self.tri = self.mod_tri_gate()
self.bitcell = self.mod_bitcell() self.bitcell = self.mod_bitcell()
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
rows=self.num_rows) rows=self.num_rows)
self.add_mod(self.bitcell_array) self.add_mod(self.bitcell_array)
@ -178,12 +178,12 @@ class multibank(design.design):
self.add_mod(self.precharge_array) self.add_mod(self.precharge_array)
if self.col_addr_size > 0: if self.col_addr_size > 0:
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols,
word_size=self.word_size) word_size=self.word_size)
self.add_mod(self.column_mux_array) self.add_mod(self.column_mux_array)
self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size, self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size,
words_per_row=self.words_per_row) words_per_row=self.words_per_row)
self.add_mod(self.sense_amp_array) self.add_mod(self.sense_amp_array)
@ -199,8 +199,8 @@ class multibank(design.design):
self.row_decoder = self.mod_decoder(rows=self.num_rows) self.row_decoder = self.mod_decoder(rows=self.num_rows)
self.add_mod(self.row_decoder) self.add_mod(self.row_decoder)
self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols, self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols,
word_size=self.word_size) word_size=self.word_size)
self.add_mod(self.tri_gate_array) self.add_mod(self.tri_gate_array)
@ -213,12 +213,12 @@ class multibank(design.design):
if(self.num_banks > 1): if(self.num_banks > 1):
self.bank_select = self.mod_bank_select() self.bank_select = self.mod_bank_select()
self.add_mod(self.bank_select) self.add_mod(self.bank_select)
def add_bitcell_array(self): def add_bitcell_array(self):
""" Adding Bitcell Array """ """ Adding Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="bitcell_array", self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array, mod=self.bitcell_array,
offset=vector(0,0)) offset=vector(0,0))
temp = [] temp = []
@ -238,7 +238,7 @@ class multibank(design.design):
# The enclosure is for the well and the spacing is to the bitcell wells # The enclosure is for the well and the spacing is to the bitcell wells
y_offset = self.bitcell_array.height + self.m2_gap y_offset = self.bitcell_array.height + self.m2_gap
self.precharge_array_inst=self.add_inst(name="precharge_array", self.precharge_array_inst=self.add_inst(name="precharge_array",
mod=self.precharge_array, mod=self.precharge_array,
offset=vector(0,y_offset)) offset=vector(0,y_offset))
temp = [] temp = []
for i in range(self.num_cols): for i in range(self.num_cols):
@ -255,7 +255,7 @@ class multibank(design.design):
self.column_mux_height = 0 self.column_mux_height = 0
return return
y_offset = self.column_mux_height y_offset = self.column_mux_height
self.col_mux_array_inst=self.add_inst(name="column_mux_array", self.col_mux_array_inst=self.add_inst(name="column_mux_array",
mod=self.column_mux_array, mod=self.column_mux_array,
offset=vector(0,y_offset).scale(-1,-1)) offset=vector(0,y_offset).scale(-1,-1))
@ -287,7 +287,7 @@ class multibank(design.design):
else: else:
temp.append("bl_out_{0}".format(i)) temp.append("bl_out_{0}".format(i))
temp.append("br_out_{0}".format(i)) temp.append("br_out_{0}".format(i))
temp.extend([self.prefix+"s_en", "vdd", "gnd"]) temp.extend([self.prefix+"s_en", "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
@ -295,16 +295,16 @@ class multibank(design.design):
""" Adding Write Driver """ """ Adding Write Driver """
y_offset = self.sense_amp_array.height + self.column_mux_height \ y_offset = self.sense_amp_array.height + self.column_mux_height \
+ self.m2_gap + self.write_driver_array.height + self.m2_gap + self.write_driver_array.height
self.write_driver_array_inst=self.add_inst(name="write_driver_array", self.write_driver_array_inst=self.add_inst(name="write_driver_array",
mod=self.write_driver_array, mod=self.write_driver_array,
offset=vector(0,y_offset).scale(-1,-1)) offset=vector(0,y_offset).scale(-1,-1))
temp = [] temp = []
for i in range(self.word_size): for i in range(self.word_size):
temp.append("bank_din_{0}".format(i)) temp.append("bank_din_{0}".format(i))
for i in range(self.word_size): for i in range(self.word_size):
if (self.words_per_row == 1): if (self.words_per_row == 1):
temp.append("bl_{0}".format(i)) temp.append("bl_{0}".format(i))
temp.append("br_{0}".format(i)) temp.append("br_{0}".format(i))
else: else:
@ -317,10 +317,10 @@ class multibank(design.design):
""" data tri gate to drive the data bus """ """ data tri gate to drive the data bus """
y_offset = self.sense_amp_array.height+self.column_mux_height \ y_offset = self.sense_amp_array.height+self.column_mux_height \
+ self.m2_gap + self.tri_gate_array.height + self.m2_gap + self.tri_gate_array.height
self.tri_gate_array_inst=self.add_inst(name="tri_gate_array", self.tri_gate_array_inst=self.add_inst(name="tri_gate_array",
mod=self.tri_gate_array, mod=self.tri_gate_array,
offset=vector(0,y_offset).scale(-1,-1)) offset=vector(0,y_offset).scale(-1,-1))
temp = [] temp = []
for i in range(self.word_size): for i in range(self.word_size):
temp.append("sa_out_{0}".format(i)) temp.append("sa_out_{0}".format(i))
@ -332,16 +332,16 @@ class multibank(design.design):
def add_row_decoder(self): def add_row_decoder(self):
""" Add the hierarchical row decoder """ """ Add the hierarchical row decoder """
# The address and control bus will be in between decoder and the main memory array # The address and control bus will be in between decoder and the main memory array
# This bus will route address bits to the decoder input and column mux inputs. # This bus will route address bits to the decoder input and column mux inputs.
# The wires are actually routed after we placed the stuff on both sides. # The wires are actually routed after we placed the stuff on both sides.
# The predecoder is below the x-axis and the main decoder is above the x-axis # The predecoder is below the x-axis and the main decoder is above the x-axis
# The address flop and decoder are aligned in the x coord. # The address flop and decoder are aligned in the x coord.
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
self.row_decoder_inst=self.add_inst(name="row_decoder", self.row_decoder_inst=self.add_inst(name="row_decoder",
mod=self.row_decoder, mod=self.row_decoder,
offset=vector(x_offset,0)) offset=vector(x_offset,0))
temp = [] temp = []
@ -357,8 +357,8 @@ class multibank(design.design):
# The wordline driver is placed to the right of the main decoder width. # The wordline driver is placed to the right of the main decoder width.
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
self.wordline_driver_inst=self.add_inst(name="wordline_driver", self.wordline_driver_inst=self.add_inst(name="wordline_driver",
mod=self.wordline_driver, mod=self.wordline_driver,
offset=vector(x_offset,0)) offset=vector(x_offset,0))
temp = [] temp = []
@ -371,16 +371,16 @@ class multibank(design.design):
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def add_column_decoder_module(self): def add_column_decoder_module(self):
""" """
Create a 2:4 or 3:8 column address decoder. Create a 2:4 or 3:8 column address decoder.
""" """
# Place the col decoder right aligned with row decoder # Place the col decoder right aligned with row decoder
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width)
y_off = -(self.col_decoder.height + 2*drc("well_to_well")) y_off = -(self.col_decoder.height + 2*drc("well_to_well"))
self.col_decoder_inst=self.add_inst(name="col_address_decoder", self.col_decoder_inst=self.add_inst(name="col_address_decoder",
mod=self.col_decoder, mod=self.col_decoder,
offset=vector(x_off,y_off)) offset=vector(x_off,y_off))
temp = [] temp = []
@ -390,9 +390,9 @@ class multibank(design.design):
temp.append("sel_{0}".format(j)) temp.append("sel_{0}".format(j))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def add_column_decoder(self): def add_column_decoder(self):
""" """
Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2, Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2,
2:4 decoder, or 3:8 decoder. 2:4 decoder, or 3:8 decoder.
""" """
@ -408,7 +408,7 @@ class multibank(design.design):
else: else:
# No error checking before? # No error checking before?
debug.error("Invalid column decoder?",-1) debug.error("Invalid column decoder?",-1)
self.add_column_decoder_module() self.add_column_decoder_module()
@ -417,7 +417,7 @@ class multibank(design.design):
if not self.num_banks > 1: if not self.num_banks > 1:
return return
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
if self.col_addr_size > 0: if self.col_addr_size > 0:
y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by()) y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by())
@ -441,7 +441,7 @@ class multibank(design.design):
for inst in self.insts: for inst in self.insts:
self.copy_power_pins(inst,"vdd") self.copy_power_pins(inst,"vdd")
self.copy_power_pins(inst,"gnd") self.copy_power_pins(inst,"gnd")
def route_bank_select(self): def route_bank_select(self):
""" Route the bank select logic. """ """ Route the bank select logic. """
for input_name in self.input_control_signals+["bank_sel"]: for input_name in self.input_control_signals+["bank_sel"]:
@ -461,8 +461,8 @@ class multibank(design.design):
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=out_pos, offset=out_pos,
rotate=90) rotate=90)
def setup_layout_constraints(self): def setup_layout_constraints(self):
""" After the modules are instantiated, find the dimensions for the """ After the modules are instantiated, find the dimensions for the
control bus, power ring, etc. """ control bus, power ring, etc. """
@ -472,36 +472,36 @@ class multibank(design.design):
#driver. #driver.
# Leave room for the output below the tri gate. # Leave room for the output below the tri gate.
#tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch #tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch
write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch
row_decoder_min_y_offset = self.row_decoder_inst.by() row_decoder_min_y_offset = self.row_decoder_inst.by()
if self.col_addr_size > 0: if self.col_addr_size > 0:
col_decoder_min_y_offset = self.col_decoder_inst.by() col_decoder_min_y_offset = self.col_decoder_inst.by()
else: else:
col_decoder_min_y_offset = row_decoder_min_y_offset col_decoder_min_y_offset = row_decoder_min_y_offset
if self.num_banks>1: if self.num_banks>1:
# The control gating logic is below the decoder # The control gating logic is below the decoder
# Min of the control gating logic and tri gate. # Min of the control gating logic and tri gate.
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset) self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset)
else: else:
# Just the min of the decoder logic logic and tri gate. # Just the min of the decoder logic logic and tri gate.
self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset) self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset)
# The max point is always the top of the precharge bitlines # The max point is always the top of the precharge bitlines
# Add a vdd and gnd power rail above the array # Add a vdd and gnd power rail above the array
self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width
self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width
self.min_x_offset = self.row_decoder_inst.lx() self.min_x_offset = self.row_decoder_inst.lx()
# # Create the core bbox for the power rings # # Create the core bbox for the power rings
ur = vector(self.max_x_offset, self.max_y_offset) ur = vector(self.max_x_offset, self.max_y_offset)
ll = vector(self.min_x_offset, self.min_y_offset) ll = vector(self.min_x_offset, self.min_y_offset)
self.core_bbox = [ll, ur] self.core_bbox = [ll, ur]
self.height = ur.y - ll.y self.height = ur.y - ll.y
self.width = ur.x - ll.x self.width = ur.x - ll.x
def route_central_bus(self): def route_central_bus(self):
""" Create the address, supply, and control signal central bus lines. """ """ Create the address, supply, and control signal central bus lines. """
@ -509,7 +509,7 @@ class multibank(design.design):
# Overall central bus width. It includes all the column mux lines, # Overall central bus width. It includes all the column mux lines,
# and control lines. # and control lines.
# The bank is at (0,0), so this is to the left of the y-axis. # The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs # 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0) control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0)
control_bus_length = self.bitcell_array_inst.uy() control_bus_length = self.bitcell_array_inst.uy()
self.bus_xoffset = self.create_vertical_bus(layer="m2", self.bus_xoffset = self.create_vertical_bus(layer="m2",
@ -542,7 +542,7 @@ class multibank(design.design):
# Only do this if we have a column mux! # Only do this if we have a column mux!
if self.col_addr_size==0: if self.col_addr_size==0:
return return
for i in range(self.num_cols): for i in range(self.num_cols):
col_mux_bl = self.col_mux_array_inst.get_pin("bl_{}".format(i)).uc() col_mux_bl = self.col_mux_array_inst.get_pin("bl_{}".format(i)).uc()
col_mux_br = self.col_mux_array_inst.get_pin("br_{}".format(i)).uc() col_mux_br = self.col_mux_array_inst.get_pin("br_{}".format(i)).uc()
@ -554,7 +554,7 @@ class multibank(design.design):
vector(bitcell_bl.x,yoffset), bitcell_bl]) vector(bitcell_bl.x,yoffset), bitcell_bl])
self.add_path("m2",[col_mux_br, vector(col_mux_br.x,yoffset), self.add_path("m2",[col_mux_br, vector(col_mux_br.x,yoffset),
vector(bitcell_br.x,yoffset), bitcell_br]) vector(bitcell_br.x,yoffset), bitcell_br])
def route_sense_amp_to_col_mux_or_bitcell_array(self): def route_sense_amp_to_col_mux_or_bitcell_array(self):
""" Routing of BL and BR between sense_amp and column mux or bitcell array """ """ Routing of BL and BR between sense_amp and column mux or bitcell array """
@ -570,19 +570,19 @@ class multibank(design.design):
# Sense amp is directly connected to the bitcell array # Sense amp is directly connected to the bitcell array
connect_bl = self.bitcell_array_inst.get_pin("bl_{}".format(i)).bc() connect_bl = self.bitcell_array_inst.get_pin("bl_{}".format(i)).bc()
connect_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc() connect_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc()
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
self.add_path("m2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset), self.add_path("m2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
vector(connect_bl.x,yoffset), connect_bl]) vector(connect_bl.x,yoffset), connect_bl])
self.add_path("m2",[sense_amp_br, vector(sense_amp_br.x,yoffset), self.add_path("m2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
vector(connect_br.x,yoffset), connect_br]) vector(connect_br.x,yoffset), connect_br])
def route_sense_amp_to_trigate(self): def route_sense_amp_to_trigate(self):
""" Routing of sense amp output to tri_gate input """ """ Routing of sense amp output to tri_gate input """
for i in range(self.word_size): for i in range(self.word_size):
# Connection of data_out of sense amp to data_in # Connection of data_out of sense amp to data_in
tri_gate_in = self.tri_gate_array_inst.get_pin("in_{}".format(i)).lc() tri_gate_in = self.tri_gate_array_inst.get_pin("in_{}".format(i)).lc()
sa_data_out = self.sense_amp_array_inst.get_pin("data_{}".format(i)).bc() sa_data_out = self.sense_amp_array_inst.get_pin("data_{}".format(i)).bc()
@ -597,17 +597,17 @@ class multibank(design.design):
for i in range(self.word_size): for i in range(self.word_size):
data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(i)) data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(i))
self.add_layout_pin_rect_center(text="dout_{}".format(i), self.add_layout_pin_rect_center(text="dout_{}".format(i),
layer=data_pin.layer, layer=data_pin.layer,
offset=data_pin.center(), offset=data_pin.center(),
height=data_pin.height(), height=data_pin.height(),
width=data_pin.width()), width=data_pin.width()),
def route_tri_gate_out(self): def route_tri_gate_out(self):
""" Metal 3 routing of tri_gate output data """ """ Metal 3 routing of tri_gate output data """
for i in range(self.word_size): for i in range(self.word_size):
data_pin = self.tri_gate_array_inst.get_pin("out_{}".format(i)) data_pin = self.tri_gate_array_inst.get_pin("out_{}".format(i))
self.add_layout_pin_rect_center(text="dout_{}".format(i), self.add_layout_pin_rect_center(text="dout_{}".format(i),
layer=data_pin.layer, layer=data_pin.layer,
offset=data_pin.center(), offset=data_pin.center(),
height=data_pin.height(), height=data_pin.height(),
width=data_pin.width()), width=data_pin.width()),
@ -622,21 +622,21 @@ class multibank(design.design):
decoder_name = "a_{}".format(i) decoder_name = "a_{}".format(i)
addr_name = "a_{}".format(addr_idx) addr_name = "a_{}".format(addr_idx)
self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name) self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
def route_write_driver(self): def route_write_driver(self):
""" Connecting write driver """ """ Connecting write driver """
for i in range(self.word_size): for i in range(self.word_size):
data_name = "data_{}".format(i) data_name = "data_{}".format(i)
din_name = "bank_din_{}".format(i) din_name = "bank_din_{}".format(i)
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
def route_wordline_driver(self): def route_wordline_driver(self):
""" Connecting Wordline driver output to Bitcell WL connection """ """ Connecting Wordline driver output to Bitcell WL connection """
# we don't care about bends after connecting to the input pin, so let the path code decide. # we don't care about bends after connecting to the input pin, so let the path code decide.
for i in range(self.num_rows): for i in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs # The pre/post is to access the pin from "outside" the cell to avoid DRCs
@ -653,23 +653,23 @@ class multibank(design.design):
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_column_address_lines(self): def route_column_address_lines(self):
""" Connecting the select lines of column mux to the address bus """ """ Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0: if not self.col_addr_size>0:
return return
if self.col_addr_size == 1: if self.col_addr_size == 1:
# Connect to sel[0] and sel[1] # Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"] decode_names = ["Zb", "Z"]
# The Address LSB # The Address LSB
self.copy_layout_pin(self.col_decoder_inst, "A", "a[0]") self.copy_layout_pin(self.col_decoder_inst, "A", "a[0]")
elif self.col_addr_size > 1: elif self.col_addr_size > 1:
decode_names = [] decode_names = []
for i in range(self.num_col_addr_lines): for i in range(self.num_col_addr_lines):
@ -679,7 +679,7 @@ class multibank(design.design):
decoder_name = "in_{}".format(i) decoder_name = "in_{}".format(i)
addr_name = "a_{}".format(i) addr_name = "a_{}".format(i)
self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name) self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name)
# This will do a quick "river route" on two layers. # This will do a quick "river route" on two layers.
# When above the top select line it will offset "inward" again to prevent conflicts. # When above the top select line it will offset "inward" again to prevent conflicts.
@ -688,7 +688,7 @@ class multibank(design.design):
for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)): for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
mux_name = "sel_{}".format(i) mux_name = "sel_{}".format(i)
mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc() mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc()
decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center() decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center()
# To get to the edge of the decoder and one track out # To get to the edge of the decoder and one track out
@ -700,13 +700,13 @@ class multibank(design.design):
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y) mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
#self.add_wire(self.m1_stack,[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) #self.add_wire(self.m1_stack,[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
self.add_path("m1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) self.add_path("m1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
def add_lvs_correspondence_points(self): def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong. """ This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist. will show these as ports in the extracted netlist.
""" """
@ -715,9 +715,9 @@ class multibank(design.design):
wl_name = "wl_{}".format(i) wl_name = "wl_{}".format(i)
wl_pin = self.bitcell_array_inst.get_pin(wl_name) wl_pin = self.bitcell_array_inst.get_pin(wl_name)
self.add_label(text=wl_name, self.add_label(text=wl_name,
layer="m1", layer="m1",
offset=wl_pin.center()) offset=wl_pin.center())
# Add the bitline names # Add the bitline names
for i in range(self.num_cols): for i in range(self.num_cols):
bl_name = "bl_{}".format(i) bl_name = "bl_{}".format(i)
@ -725,35 +725,35 @@ class multibank(design.design):
bl_pin = self.bitcell_array_inst.get_pin(bl_name) bl_pin = self.bitcell_array_inst.get_pin(bl_name)
br_pin = self.bitcell_array_inst.get_pin(br_name) br_pin = self.bitcell_array_inst.get_pin(br_name)
self.add_label(text=bl_name, self.add_label(text=bl_name,
layer="m2", layer="m2",
offset=bl_pin.center()) offset=bl_pin.center())
self.add_label(text=br_name, self.add_label(text=br_name,
layer="m2", layer="m2",
offset=br_pin.center()) offset=br_pin.center())
# # Add the data output names to the sense amp output # # Add the data output names to the sense amp output
# for i in range(self.word_size): # for i in range(self.word_size):
# data_name = "data_{}".format(i) # data_name = "data_{}".format(i)
# data_pin = self.sense_amp_array_inst.get_pin(data_name) # data_pin = self.sense_amp_array_inst.get_pin(data_name)
# self.add_label(text="sa_out_{}".format(i), # self.add_label(text="sa_out_{}".format(i),
# layer="m2", # layer="m2",
# offset=data_pin.center()) # offset=data_pin.center())
# Add labels on the decoder # Add labels on the decoder
for i in range(self.word_size): for i in range(self.word_size):
data_name = "dec_out_{}".format(i) data_name = "dec_out_{}".format(i)
pin_name = "in_{}".format(i) pin_name = "in_{}".format(i)
data_pin = self.wordline_driver_inst.get_pin(pin_name) data_pin = self.wordline_driver_inst.get_pin(pin_name)
self.add_label(text=data_name, self.add_label(text=data_name,
layer="m1", layer="m1",
offset=data_pin.center()) offset=data_pin.center())
def route_control_lines(self): def route_control_lines(self):
""" Route the control lines of the entire bank """ """ Route the control lines of the entire bank """
# Make a list of tuples that we will connect. # Make a list of tuples that we will connect.
# From control signal to the module pin # From control signal to the module pin
# Connection from the central bus to the main control block crosses # Connection from the central bus to the main control block crosses
# pre-decoder and this connection is in metal3 # pre-decoder and this connection is in metal3
connection = [] connection = []
@ -762,7 +762,7 @@ class multibank(design.design):
connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc())) connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc()))
connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc())) connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc())) connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
for (control_signal, pin_pos) in connection: for (control_signal, pin_pos) in connection:
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
self.add_path("m1", [control_pos, pin_pos]) self.add_path("m1", [control_pos, pin_pos])
@ -781,7 +781,7 @@ class multibank(design.design):
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=control_via_pos, offset=control_via_pos,
rotate=90) rotate=90)
def add_control_pins(self): def add_control_pins(self):
""" Add the control signal input pins """ """ Add the control signal input pins """
@ -791,15 +791,15 @@ class multibank(design.design):
if self.num_banks > 1: if self.num_banks > 1:
# it's not an input pin if we have multiple banks # it's not an input pin if we have multiple banks
self.add_label_pin(text=ctrl, self.add_label_pin(text=ctrl,
layer="m2", layer="m2",
offset=vector(x_offset, self.min_y_offset), offset=vector(x_offset, self.min_y_offset),
width=self.m2_width, width=self.m2_width,
height=self.max_y_offset-self.min_y_offset) height=self.max_y_offset-self.min_y_offset)
else: else:
self.add_layout_pin(text=ctrl, self.add_layout_pin(text=ctrl,
layer="m2", layer="m2",
offset=vector(x_offset, self.min_y_offset), offset=vector(x_offset, self.min_y_offset),
width=self.m2_width, width=self.m2_width,
height=self.max_y_offset-self.min_y_offset) height=self.max_y_offset-self.min_y_offset)
@ -815,8 +815,8 @@ class multibank(design.design):
self.add_via(layers=self.m2_stack, self.add_via(layers=self.m2_stack,
offset=in_pin + self.m2m3_via_offset, offset=in_pin + self.m2m3_via_offset,
rotate=90) rotate=90)
def connect_rail_from_left(self,inst, pin, rail): def connect_rail_from_left(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """ """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).rc() in_pin = inst.get_pin(pin).rc()

View File

@ -27,7 +27,7 @@ class bitcell_array(bitcell_base_array):
# We don't offset this because we need to align # We don't offset this because we need to align
# the replica bitcell in the control logic # the replica bitcell in the control logic
# self.offset_all_coordinates() # self.offset_all_coordinates()
def create_netlist(self): def create_netlist(self):
""" Create and connect the netlist """ """ Create and connect the netlist """
self.add_modules() self.add_modules()
@ -41,7 +41,7 @@ class bitcell_array(bitcell_base_array):
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_modules(self): def add_modules(self):
@ -58,20 +58,20 @@ class bitcell_array(bitcell_base_array):
self.cell_inst[row, col]=self.add_inst(name=name, self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.cell) mod=self.cell)
self.connect_inst(self.get_bitcell_pins(col, row)) self.connect_inst(self.get_bitcell_pins(col, row))
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW.""" """Power of Bitcell array and bitline in nW."""
# Dynamic Power from Bitline # Dynamic Power from Bitline
bl_wire = self.gen_bl_wire() bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap() cell_load = 2 * bl_wire.return_input_cap()
bl_swing = OPTS.rbl_delay_percentage bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_frequency"] freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
# Calculate the bitcell power which currently only includes leakage # Calculate the bitcell power which currently only includes leakage
cell_power = self.cell.analytical_power(corner, load) cell_power = self.cell.analytical_power(corner, load)
# Leakage power grows with entire array and bitlines. # Leakage power grows with entire array and bitlines.
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
cell_power.leakage * self.column_size * self.row_size) cell_power.leakage * self.column_size * self.row_size)
@ -102,7 +102,7 @@ class bitcell_array(bitcell_base_array):
bitcell_wl_cin = self.cell.get_wl_cin() bitcell_wl_cin = self.cell.get_wl_cin()
total_cin = bitcell_wl_cin * self.column_size total_cin = bitcell_wl_cin * self.column_size
return total_cin return total_cin
def graph_exclude_bits(self, targ_row, targ_col): def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target""" """Excludes bits in column from being added to graph except target"""
# Function is not robust with column mux configurations # Function is not robust with column mux configurations
@ -111,7 +111,7 @@ class bitcell_array(bitcell_base_array):
if row == targ_row and col == targ_col: if row == targ_row and col == targ_col:
continue continue
self.graph_inst_exclude.add(self.cell_inst[row, col]) self.graph_inst_exclude.add(self.cell_inst[row, col])
def get_cell_name(self, inst_name, row, col): def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell.""" """Gets the spice name of the target bitcell."""
return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col] return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col]

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