Merge remote-tracking branch 'origin/dev'

This commit is contained in:
Matt Guthaus 2019-05-06 06:55:45 -07:00
commit 76e2ab88fe
332 changed files with 3354 additions and 1632 deletions

View File

@ -8,6 +8,8 @@ omit =
*/tests/* */tests/*
# ignore the debug utilities # ignore the debug utilities
debug.py debug.py
# ignore the no DRC/LVS tool options
none.py
[paths] [paths]
source = source =
../.. ../..
@ -21,6 +23,8 @@ source =
exclude_lines = exclude_lines =
pragma: no cover pragma: no cover
def __repr__ def __repr__
def __str__
class gdsStreamer
except Exception except Exception
raise AssertionError raise AssertionError
raise NotImplementedError raise NotImplementedError

View File

@ -1,6 +1,6 @@
BSD 3-Clause License BSD 3-Clause License
Copyright (c) 2018-2019 Regents of the University of California and The Board Copyright (c) 2019 Regents of the University of California and The Board
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.

View File

@ -4,13 +4,13 @@
[![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE) [![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE)
Master: Master:
[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/OpenRAM/commits/master) [![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/pipeline.svg)](https://github.com/VLSIDA/OpenRAM/commits/master)
![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/coverage.svg?private_token=ynB6rSFLzvKUseoBPcwV) ![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/coverage.svg)
[![Download](./images/download-stable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/master.zip) [![Download](./images/download-stable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/master.zip)
Dev: Dev:
[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/OpenRAM/commits/dev) [![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/pipeline.svg)](https://github.com/VLSIDA/OpenRAM/commits/dev)
![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/coverage.svg?private_token=ynB6rSFLzvKUseoBPcwV) ![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/coverage.svg)
[![Download](./images/download-unstable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/dev.zip) [![Download](./images/download-unstable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/dev.zip)
An open-source static random access memory (SRAM) compiler. An open-source static random access memory (SRAM) compiler.

View File

@ -1,3 +1,10 @@
# 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 hierarchy_design import hierarchy_design
import debug import debug
import utils import utils
@ -9,44 +16,36 @@ class contact(hierarchy_design.hierarchy_design):
""" """
Object for a contact shape with its conductor enclosures. Object for a contact shape with its conductor enclosures.
Creates a contact array minimum active or poly enclosure and metal1 enclosure. Creates a contact array minimum active or poly enclosure and metal1 enclosure.
This class has enclosure on multiple sides of the contact whereas a via may This class has enclosure on two or four sides of the contact.
have extension on two or four sides. The direction specifies whether the first and second layer have asymmetric extension in the H or V direction.
The well/implant_type is an option to add a select/implant layer enclosing the contact. This is The well/implant_type is an option to add a select/implant layer enclosing the contact. This is
necessary to import layouts into Magic which requires the select to be in the same GDS necessary to import layouts into Magic which requires the select to be in the same GDS
hierarchy as the contact. hierarchy as the contact.
""" """
def __init__(self, layer_stack, dimensions=[1,1], implant_type=None, well_type=None, name=""): def __init__(self, layer_stack, dimensions=(1,1), directions=("V","V"), implant_type=None, well_type=None, name=""):
# This will ignore the name parameter since we can guarantee a unique name here # This will ignore the name parameter since we can guarantee a unique name here
if implant_type or well_type:
name = "{0}_{1}_{2}_{3}x{4}_{5}{6}".format(layer_stack[0],
layer_stack[1],
layer_stack[2],
dimensions[0],
dimensions[1],
implant_type,
well_type)
else:
name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0],
layer_stack[1],
layer_stack[2],
dimensions[0],
dimensions[1])
hierarchy_design.hierarchy_design.__init__(self, name) hierarchy_design.hierarchy_design.__init__(self, 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("dimensions: {0}".format(dimensions))
if implant_type or well_type:
self.add_comment("implant type: {0}\nwell_type: {1}".format(implant_type,well_type))
self.layer_stack = layer_stack self.layer_stack = layer_stack
self.dimensions = dimensions self.dimensions = dimensions
self.directions = directions
self.offset = vector(0,0) self.offset = vector(0,0)
self.implant_type = implant_type self.implant_type = implant_type
self.well_type = well_type self.well_type = well_type
self.pins = [] # used for matching parm lengths # Module does not have pins, but has empty pin list.
self.pins = []
self.create_layout() self.create_layout()
def create_layout(self): def create_layout(self):
self.setup_layers() self.setup_layers()
self.setup_layout_constants() self.setup_layout_constants()
self.create_contact_array() self.create_contact_array()
@ -63,6 +62,8 @@ class contact(hierarchy_design.hierarchy_design):
debug.error(-1,"Must define both implant and well type or none at all.") debug.error(-1,"Must define both implant and well type or none at all.")
def setup_layers(self): def setup_layers(self):
""" Locally assign the layer names. """
(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.via_layer_name = via_layer self.via_layer_name = via_layer
@ -75,37 +76,58 @@ class contact(hierarchy_design.hierarchy_design):
self.second_layer_name = second_layer self.second_layer_name = second_layer
def setup_layout_constants(self): def setup_layout_constants(self):
""" 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
self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch
self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch
# DRC rules # DRC rules
# The extend rule applies to asymmetric enclosures in one direction.
# The enclosure rule applies to symmetric enclosure component.
first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
first_layer_minarea = drc("minarea_{0}".format(self.first_layer_name))
first_layer_enclosure = drc("{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)) first_layer_enclosure = drc("{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name))
first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name))
second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name)) second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name))
second_layer_minarea = drc("minarea_{0}".format(self.second_layer_name))
second_layer_enclosure = drc("{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)) second_layer_enclosure = drc("{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name))
second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)) second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name))
self.first_layer_horizontal_enclosure = max((first_layer_minwidth - self.contact_array_width) / 2,
first_layer_enclosure)
self.first_layer_vertical_enclosure = max(utils.ceil((first_layer_minarea
/ (self.contact_array_width + 2*self.first_layer_horizontal_enclosure)
- self.contact_array_height)/2),
(first_layer_minwidth - self.contact_array_height)/2,
first_layer_extend)
self.second_layer_horizontal_enclosure = max((second_layer_minwidth - self.contact_array_width)/2, # In some technologies, the minimum width may be larger than the overlap requirement around the via, so
second_layer_enclosure) # check this for each dimension.
self.second_layer_vertical_enclosure = max(utils.ceil((second_layer_minarea if self.directions[0] == "V":
/ (self.contact_array_width + 2*self.second_layer_horizontal_enclosure) self.first_layer_horizontal_enclosure = max(first_layer_enclosure,
- self.contact_array_height)/2), (first_layer_minwidth - self.contact_array_width)/2)
(second_layer_minwidth - self.contact_array_height)/2, self.first_layer_vertical_enclosure = max(first_layer_extend,
second_layer_extend) (first_layer_minwidth - self.contact_array_height)/2)
elif self.directions[0] == "H":
self.first_layer_horizontal_enclosure = max(first_layer_extend,
(first_layer_minwidth - self.contact_array_width)/2)
self.first_layer_vertical_enclosure = max(first_layer_enclosure,
(first_layer_minwidth - self.contact_array_height)/2)
else:
debug.error("Invalid first layer direction.", -1)
# In some technologies, the minimum width may be larger than the overlap requirement around the via, so
# check this for each dimension.
if self.directions[1] == "V":
self.second_layer_horizontal_enclosure = max(second_layer_enclosure,
(second_layer_minwidth - self.contact_array_width)/2)
self.second_layer_vertical_enclosure = max(second_layer_extend,
(second_layer_minwidth - self.contact_array_height)/2)
elif self.directions[1] == "H":
self.second_layer_horizontal_enclosure = max(second_layer_extend,
(second_layer_minwidth - self.contact_array_height)/2)
self.second_layer_vertical_enclosure = max(second_layer_enclosure,
(second_layer_minwidth - self.contact_array_width)/2)
else:
debug.error("Invalid second layer direction.", -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"""
@ -169,10 +191,10 @@ class contact(hierarchy_design.hierarchy_design):
from sram_factory import factory from sram_factory import factory
# This is not instantiated and used for calculations only. # This is not instantiated and used for calculations only.
# These are static 1x1 contacts to reuse in all the design modules. # These are static 1x1 contacts to reuse in all the design modules.
well = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1")) well = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"), directions=("H","V"))
active = factory.create(module_type="contact", layer_stack=("active", "contact", "poly")) active = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"), directions=("H","V"))
poly = factory.create(module_type="contact", layer_stack=("poly", "contact", "metal1")) poly = factory.create(module_type="contact", layer_stack=("poly", "contact", "metal1"), directions=("V","H"))
m1m2 = factory.create(module_type="contact", layer_stack=("metal1", "via1", "metal2")) m1m2 = factory.create(module_type="contact", layer_stack=("metal1", "via1", "metal2"), directions=("H","V"))
m2m3 = factory.create(module_type="contact", layer_stack=("metal2", "via2", "metal3")) m2m3 = factory.create(module_type="contact", layer_stack=("metal2", "via2", "metal3"), directions=("V","H"))
m3m4 = factory.create(module_type="contact", layer_stack=("metal3", "via3", "metal4")) m3m4 = factory.create(module_type="contact", layer_stack=("metal3", "via3", "metal4"), directions=("H","V"))

View File

@ -1,3 +1,10 @@
# 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.
#
from hierarchy_design import hierarchy_design from hierarchy_design import hierarchy_design
import contact import contact
import globals import globals
@ -95,13 +102,3 @@ class design(hierarchy_design):
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
def __str__(self):
""" override print function output """
pins = ",".join(self.pins)
insts = [" {}".format(x) for x in self.insts]
objs = [" {}".format(x) for x in self.objs]
s = "********** design {0} **********\n".format(self.name)
s += "\n pins ({0})={1}\n".format(len(self.pins), pins)
s += "\n objs ({0})=\n{1}".format(len(self.objs), "\n".join(objs))
s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts))
return s

View File

@ -1,3 +1,10 @@
# 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.
#
""" """
This provides a set of useful generic types for the gdsMill interface. This provides a set of useful generic types for the gdsMill interface.
""" """
@ -371,8 +378,8 @@ class rectangle(geometry):
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
return "rect: @" + str(self.offset) + " " + str(self.width) + "x" + str(self.height) + " layer=" +str(self.layerNumber) return self.__repr__()
def __repr__(self): def __repr__(self):
""" override print function output """ """ override print function output """
return "( rect: @" + str(self.offset) + " " + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " )" return "( rect: @" + str(self.offset) + " WxH=" + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " )"

View File

@ -1,3 +1,10 @@
# 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 hierarchy_layout import hierarchy_layout
import hierarchy_spice import hierarchy_spice
import globals import globals
@ -21,8 +28,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
self.name = name self.name = name
hierarchy_layout.layout.__init__(self, name)
hierarchy_spice.spice.__init__(self, name) hierarchy_spice.spice.__init__(self, name)
hierarchy_layout.layout.__init__(self, name)
def get_layout_pins(self,inst): def get_layout_pins(self,inst):
@ -100,7 +107,14 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
return "design: " + self.name pins = ",".join(self.pins)
insts = [" {}".format(x) for x in self.insts]
objs = [" {}".format(x) for x in self.objs]
s = "********** design {0} **********".format(self.name)
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 insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts))
return s
def __repr__(self): def __repr__(self):
""" override print function output """ """ override print function output """

View File

@ -1,3 +1,10 @@
# 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 itertools import itertools
import geometry import geometry
import gdsMill import gdsMill
@ -355,75 +362,59 @@ class layout():
layer_stack=layers, layer_stack=layers,
position_list=coordinates) position_list=coordinates)
def add_contact(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): def get_preferred_direction(self, layer):
""" This is just an alias for a via.""" """ Return the preferred routing directions """
return self.add_via(layers=layers, if layer in ["metal1", "metal3", "metal5"]:
offset=offset, return "H"
size=size, elif layer in ["active", "poly", "metal2", "metal4"]:
mirror=mirror, return "V"
rotate=rotate, else:
implant_type=implant_type, return "N"
well_type=well_type)
def add_contact_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None):
""" This is just an alias for a via.""" def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None):
return self.add_via_center(layers=layers,
offset=offset,
size=size,
mirror=mirror,
rotate=rotate,
implant_type=implant_type,
well_type=well_type)
def add_via(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None):
""" Add a three layer via structure. """ """ Add a three layer via structure. """
if directions==None:
directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2]))
from sram_factory import factory from sram_factory import factory
via = factory.create(module_type="contact", via = factory.create(module_type="contact",
layer_stack=layers, layer_stack=layers,
dimensions=size, dimensions=size,
directions=directions,
implant_type=implant_type, implant_type=implant_type,
well_type=well_type) well_type=well_type)
self.add_mod(via) self.add_mod(via)
inst=self.add_inst(name=via.name, inst=self.add_inst(name=via.name,
mod=via, mod=via,
offset=offset, offset=offset)
mirror=mirror,
rotate=rotate)
# We don't model the logical connectivity of wires/paths # We don't model the logical connectivity of wires/paths
self.connect_inst([]) self.connect_inst([])
return inst return inst
def add_via_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None):
""" Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """ """ Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """
if directions==None:
directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2]))
from sram_factory import factory from sram_factory import factory
via = factory.create(module_type="contact", via = factory.create(module_type="contact",
layer_stack=layers, layer_stack=layers,
dimensions=size, dimensions=size,
directions=directions,
implant_type=implant_type, implant_type=implant_type,
well_type=well_type) well_type=well_type)
height = via.height height = via.height
width = via.width width = via.width
debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.")
if rotate==0: corrected_offset = offset + vector(-0.5*width,-0.5*height)
corrected_offset = offset + vector(-0.5*width,-0.5*height)
elif rotate==90:
corrected_offset = offset + vector(0.5*height,-0.5*width)
elif rotate==180:
corrected_offset = offset + vector(0.5*width,0.5*height)
elif rotate==270:
corrected_offset = offset + vector(-0.5*height,0.5*width)
else:
debug.error("Invalid rotation argument.",-1)
#print(rotate,offset,"->",corrected_offset)
self.add_mod(via) self.add_mod(via)
inst=self.add_inst(name=via.name, inst=self.add_inst(name=via.name,
mod=via, mod=via,
offset=corrected_offset, offset=corrected_offset)
mirror=mirror,
rotate=rotate)
# We don't model the logical connectivity of wires/paths # We don't model the logical connectivity of wires/paths
self.connect_inst([]) self.connect_inst([])
return inst return inst
@ -748,8 +739,7 @@ class layout():
mid = vector(trunk_offset.x, pin.center().y) mid = vector(trunk_offset.x, pin.center().y)
self.add_path(layer_stack[0], [pin.center(), mid]) self.add_path(layer_stack[0], [pin.center(), mid])
self.add_via_center(layers=layer_stack, self.add_via_center(layers=layer_stack,
offset=mid, offset=mid)
rotate=90)
def create_channel_route(self, netlist, def create_channel_route(self, netlist,
@ -926,20 +916,27 @@ class layout():
def add_power_pin(self, name, loc, rotate=90, start_layer="metal1"): def add_power_pin(self, name, loc, vertical=False, start_layer="metal1"):
""" """
Add a single power pin from M3 down to M1 at the given center location. Add a single power pin from M3 down to M1 at the given center location.
The starting layer is specified to determine which vias are needed. The starting layer is specified to determine which vias are needed.
""" """
if vertical:
direction=("V","V")
else:
direction=("H","H")
if start_layer=="metal1": if start_layer=="metal1":
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=loc, offset=loc,
rotate=float(rotate)) directions=direction)
if start_layer=="metal1" or start_layer=="metal2": if start_layer=="metal1" or start_layer=="metal2":
via=self.add_via_center(layers=("metal2", "via2", "metal3"), via=self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=loc, offset=loc,
rotate=float(rotate)) directions=direction)
if start_layer=="metal3": if start_layer=="metal3":
self.add_layout_pin_rect_center(text=name, self.add_layout_pin_rect_center(text=name,
layer="metal3", layer="metal3",

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import re import re
import os import os
@ -28,7 +35,10 @@ class spice():
# Spice format) # Spice format)
self.conns = [] self.conns = []
# Keep track of any comments to add the the spice # Keep track of any comments to add the the spice
self.comments = [] try:
self.commments
except:
self.comments = []
self.sp_read() self.sp_read()
@ -38,7 +48,12 @@ class spice():
def add_comment(self, comment): def add_comment(self, comment):
""" Add a comment to the spice file """ """ Add a comment to the spice file """
self.comments.append(comment) try:
self.commments
except:
self.comments = []
else:
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. """
@ -235,13 +250,8 @@ class spice():
modeling it as a resistance driving a capacitance modeling it as a resistance driving a capacitance
""" """
swing_factor = abs(math.log(1-swing)) # time constant based on swing swing_factor = abs(math.log(1-swing)) # time constant based on swing
proc,vdd,temp = corner
#FIXME: type of delay is needed to know which process to use.
proc_mult = max(self.get_process_delay_factor(proc))
volt_mult = self.get_voltage_delay_factor(vdd)
temp_mult = self.get_temp_delay_factor(temp)
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 = delay * proc_mult * volt_mult * temp_mult 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
@ -254,6 +264,15 @@ class spice():
slew = delay * 0.6 * 2 + 0.005 * slew slew = delay * 0.6 * 2 + 0.005 * slew
return delay_data(delay = delay, slew = slew) return delay_data(delay = delay, slew = slew)
def apply_corners_analytically(self, delay, corner):
"""Multiply delay by corner factors"""
proc,vdd,temp = corner
#FIXME: type of delay is needed to know which process to use.
proc_mult = max(self.get_process_delay_factor(proc))
volt_mult = self.get_voltage_delay_factor(vdd)
temp_mult = self.get_temp_delay_factor(temp)
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."""

View File

@ -1,3 +1,10 @@
# 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 gdsMill import gdsMill
import tech import tech
import globals import globals

View File

@ -1,3 +1,10 @@
# 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 debug import debug
from tech import GDS, drc from tech import GDS, drc
from vector import vector from vector import vector

View File

@ -1,3 +1,10 @@
# 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.
#
from tech import drc from tech import drc
import debug import debug
from design import design from design import design
@ -65,7 +72,7 @@ class route(design):
for p0,p1 in plist: for p0,p1 in plist:
if p0.z != p1.z: # via if p0.z != p1.z: # via
via_size = [self.num_vias]*2 via_size = [self.num_vias]*2
self.obj.add_via_center(self.layer_stack,vector(p0.x,p0.y),size=via_size,rotate=90) self.obj.add_via_center(self.layer_stack,vector(p0.x,p0.y),size=via_size)
elif p0.x != p1.x and p0.y != p1.y: # diagonal! elif p0.x != p1.x and p0.y != p1.y: # diagonal!
debug.error("Diagonal route! {}".format(self.path),-3) debug.error("Diagonal route! {}".format(self.path),-3)
else: else:

View File

@ -1,3 +1,10 @@
# 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 os import os
import gdsMill import gdsMill
import tech import tech

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import math import math
import tech import tech

View File

@ -1,3 +1,10 @@
# 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 debug import debug
class verilog: class verilog:

View File

@ -1,3 +1,10 @@
# 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.
#
from tech import drc from tech import drc
import debug import debug
from wire_path import wire_path from wire_path import wire_path
@ -62,18 +69,15 @@ class wire(wire_path):
continue continue
if a[1] == c[1]: if a[1] == c[1]:
continue continue
via_offset = [offset[0] + 0.5*c_height, self.obj.add_via_center(layers=self.layer_stack,
offset[1] - 0.5*c_width] offset=offset)
self.obj.add_via(layers=self.layer_stack,
offset=via_offset,
rotate=90)
corner_offset = [offset[0] - 0.5*(c_height + self.vert_layer_width),
offset[1] + 0.5*(c_width - self.horiz_layer_width)]
def create_rectangles(self): def create_rectangles(self):
""" Create the actual rectangles on teh appropriate layers """
using the position list of the corners. """ Create the actual rectangles on the appropriate layers
using the position list of the corners.
"""
pl = self.position_list # position list pl = self.position_list # position list
for index in range(len(pl) - 1): for index in range(len(pl) - 1):
if pl[index][0] != pl[index + 1][0]: if pl[index][0] != pl[index + 1][0]:

View File

@ -1,3 +1,10 @@
# 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.
#
from tech import drc from tech import drc
from tech import layer as techlayer from tech import layer as techlayer
import debug import debug

View File

@ -1,7 +1,15 @@
# 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 design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS,layer,parameter,drc
import logical_effort
class bitcell(design.design): class bitcell(design.design):
""" """
@ -23,19 +31,12 @@ class bitcell(design.design):
self.width = bitcell.width self.width = bitcell.width
self.height = bitcell.height self.height = bitcell.height
self.pin_map = bitcell.pin_map self.pin_map = bitcell.pin_map
def analytical_delay(self, corner, slew, load=0, swing = 0.5): def analytical_delay(self, corner, slew, load=0, swing = 0.5):
# delay of bit cell is not like a driver(from WL) parasitic_delay = 1
# so the slew used should be 0 size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
# it should not be slew dependent? cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
# because the value is there return logical_effort.logical_effort('bitline', size, cin, load, parasitic_delay, False)
# the delay is only over half transsmission gate
from tech import spice
r = spice["min_tx_r"]*3
c_para = spice["min_tx_drain_c"]
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = swing)
return result
def list_bitcell_pins(self, col, row): def list_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """

View File

@ -1,7 +1,15 @@
# 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 design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS,layer,parameter,drc
import logical_effort
class bitcell_1rw_1r(design.design): class bitcell_1rw_1r(design.design):
""" """
@ -25,18 +33,12 @@ class bitcell_1rw_1r(design.design):
self.pin_map = bitcell_1rw_1r.pin_map self.pin_map = bitcell_1rw_1r.pin_map
def analytical_delay(self, corner, slew, load=0, swing = 0.5): def analytical_delay(self, corner, slew, load=0, swing = 0.5):
# delay of bit cell is not like a driver(from WL) parasitic_delay = 1
# so the slew used should be 0 size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
# it should not be slew dependent? cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
# because the value is there read_port_load = 0.5 #min size NMOS gate load
# the delay is only over half transsmission gate return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
from tech import spice
r = spice["min_tx_r"]*3
c_para = spice["min_tx_drain_c"]
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = swing)
return result
def list_bitcell_pins(self, col, row): def list_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
bitcell_pins = ["bl0_{0}".format(col), bitcell_pins = ["bl0_{0}".format(col),

View File

@ -1,7 +1,15 @@
# 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 design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS,layer,parameter,drc
import logical_effort
class bitcell_1w_1r(design.design): class bitcell_1w_1r(design.design):
""" """
@ -25,18 +33,12 @@ class bitcell_1w_1r(design.design):
self.pin_map = bitcell_1w_1r.pin_map self.pin_map = bitcell_1w_1r.pin_map
def analytical_delay(self, corner, slew, load=0, swing = 0.5): def analytical_delay(self, corner, slew, load=0, swing = 0.5):
# delay of bit cell is not like a driver(from WL) parasitic_delay = 1
# so the slew used should be 0 size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
# it should not be slew dependent? cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
# because the value is there read_port_load = 0.5 #min size NMOS gate load
# the delay is only over half transsmission gate return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
from tech import spice
r = spice["min_tx_r"]*3
c_para = spice["min_tx_drain_c"]
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = swing)
return result
def list_bitcell_pins(self, col, row): def list_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
bitcell_pins = ["bl0_{0}".format(col), bitcell_pins = ["bl0_{0}".format(col),

View File

@ -1,3 +1,10 @@
# 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 contact import contact
import design import design
import debug import debug
@ -5,6 +12,7 @@ from tech import drc, parameter, spice
from vector import vector from vector import vector
from ptx import ptx from ptx import ptx
from globals import OPTS from globals import OPTS
import logical_effort
class pbitcell(design.design): class pbitcell(design.design):
""" """
@ -197,7 +205,7 @@ class pbitcell(design.design):
self.read_port_spacing = self.bitline_offset + self.m2_space self.read_port_spacing = self.bitline_offset + self.m2_space
# spacing between cross coupled inverters # spacing between cross coupled inverters
self.inverter_to_inverter_spacing = contact.poly.height + self.m1_space self.inverter_to_inverter_spacing = contact.poly.width + self.m1_space
# calculations related to inverter connections # calculations related to inverter connections
inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height)
@ -291,19 +299,24 @@ class pbitcell(design.design):
self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()]) self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()])
# connect output (drain/source) of inverters # connect output (drain/source) of inverters
self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.well.second_layer_width) self.add_path("metal1",
self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.second_layer_width) [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()],
width=contact.active.second_layer_width)
self.add_path("metal1",
[self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()],
width=contact.active.second_layer_width)
# add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar) # add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar)
contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, self.cross_couple_upper_ypos) contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, self.cross_couple_upper_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset_left, offset=contact_offset_left,
rotate=90) directions=("H","H"))
contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.height, self.cross_couple_lower_ypos) contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.height, self.cross_couple_lower_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset_right, offset=contact_offset_right,
rotate=90) directions=("H","H"))
# connect contacts to gate poly (cross couple connections) # connect contacts to gate poly (cross couple connections)
gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y) gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y)
@ -394,7 +407,6 @@ class pbitcell(design.design):
self.add_layout_pin_rect_center(text=self.rw_bl_names[k], self.add_layout_pin_rect_center(text=self.rw_bl_names[k],
layer="metal2", layer="metal2",
offset=self.rwbl_positions[k], offset=self.rwbl_positions[k],
width=drc["minwidth_metal2"],
height=self.height) height=self.height)
rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + self.bitline_offset - 0.5*self.m2_width rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + self.bitline_offset - 0.5*self.m2_width
@ -402,7 +414,6 @@ class pbitcell(design.design):
self.add_layout_pin_rect_center(text=self.rw_br_names[k], self.add_layout_pin_rect_center(text=self.rw_br_names[k],
layer="metal2", layer="metal2",
offset=self.rwbr_positions[k], offset=self.rwbr_positions[k],
width=drc["minwidth_metal2"],
height=self.height) height=self.height)
# update furthest left and right transistor edges # update furthest left and right transistor edges
@ -471,7 +482,6 @@ class pbitcell(design.design):
self.add_layout_pin_rect_center(text=self.w_bl_names[k], self.add_layout_pin_rect_center(text=self.w_bl_names[k],
layer="metal2", layer="metal2",
offset=self.wbl_positions[k], offset=self.wbl_positions[k],
width=drc["minwidth_metal2"],
height=self.height) height=self.height)
wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset - 0.5*self.m2_width wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset - 0.5*self.m2_width
@ -479,7 +489,6 @@ class pbitcell(design.design):
self.add_layout_pin_rect_center(text=self.w_br_names[k], self.add_layout_pin_rect_center(text=self.w_br_names[k],
layer="metal2", layer="metal2",
offset=self.wbr_positions[k], offset=self.wbr_positions[k],
width=drc["minwidth_metal2"],
height=self.height) height=self.height)
# update furthest left and right transistor edges # update furthest left and right transistor edges
@ -570,7 +579,6 @@ class pbitcell(design.design):
self.add_layout_pin_rect_center(text=self.r_bl_names[k], self.add_layout_pin_rect_center(text=self.r_bl_names[k],
layer="metal2", layer="metal2",
offset=self.rbl_positions[k], offset=self.rbl_positions[k],
width=drc["minwidth_metal2"],
height=self.height) height=self.height)
rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - 0.5*self.m2_width rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - 0.5*self.m2_width
@ -578,7 +586,6 @@ class pbitcell(design.design):
self.add_layout_pin_rect_center(text=self.r_br_names[k], self.add_layout_pin_rect_center(text=self.r_br_names[k],
layer="metal2", layer="metal2",
offset=self.rbr_positions[k], offset=self.rbr_positions[k],
width=drc["minwidth_metal2"],
height=self.height) height=self.height)
def route_wordlines(self): def route_wordlines(self):
@ -612,21 +619,21 @@ class pbitcell(design.design):
# first transistor on either side of the cross coupled inverters does not need to route to wordline on metal2 # first transistor on either side of the cross coupled inverters does not need to route to wordline on metal2
if (k == 0) or (k == 1): if (k == 0) or (k == 1):
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=port_contact_offset) offset=port_contact_offset)
self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("poly", [gate_offset, port_contact_offset])
self.add_path("metal1", [port_contact_offset, wl_contact_offset]) self.add_path("metal1", [port_contact_offset, wl_contact_offset])
else: else:
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=port_contact_offset) offset=port_contact_offset)
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=port_contact_offset) offset=port_contact_offset)
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=wl_contact_offset, offset=wl_contact_offset,
rotate=90) directions=("H","H"))
self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("poly", [gate_offset, port_contact_offset])
self.add_path("metal2", [port_contact_offset, wl_contact_offset]) self.add_path("metal2", [port_contact_offset, wl_contact_offset])
@ -661,8 +668,8 @@ class pbitcell(design.design):
port_contact_offest = left_port_transistors[k].get_pin("S").center() port_contact_offest = left_port_transistors[k].get_pin("S").center()
bl_offset = vector(bl_positions[k].x, port_contact_offest.y) bl_offset = vector(bl_positions[k].x, port_contact_offest.y)
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=port_contact_offest) offset=port_contact_offest)
self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height) self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height)
@ -670,8 +677,8 @@ class pbitcell(design.design):
port_contact_offest = right_port_transistors[k].get_pin("D").center() port_contact_offest = right_port_transistors[k].get_pin("D").center()
br_offset = vector(br_positions[k].x, port_contact_offest.y) br_offset = vector(br_positions[k].x, port_contact_offest.y)
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=port_contact_offest) offset=port_contact_offest)
self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height) self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height)
@ -686,17 +693,17 @@ class pbitcell(design.design):
nmos_contact_positions.append(self.read_access_nmos_right[k].get_pin("S").center()) nmos_contact_positions.append(self.read_access_nmos_right[k].get_pin("S").center())
for position in nmos_contact_positions: for position in nmos_contact_positions:
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=position) offset=position)
if position.x > 0: if position.x > 0:
contact_correct = 0.5*contact.m1m2.height contact_correct = 0.5*contact.m1m2.height
else: else:
contact_correct = -0.5*contact.m1m2.height contact_correct = -0.5*contact.m1m2.height
supply_offset = vector(position.x + contact_correct, self.gnd_position.y) supply_offset = vector(position.x + contact_correct, self.gnd_position.y)
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=supply_offset, offset=supply_offset,
rotate=90) directions=("H","H"))
self.add_path("metal2", [position, supply_offset]) self.add_path("metal2", [position, supply_offset])
@ -712,39 +719,35 @@ class pbitcell(design.design):
for k in range(self.num_rw_ports): for k in range(self.num_rw_ports):
mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos)
Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos)
self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid, Q_pos])
self.add_path("metal1", [mid, Q_pos])
mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos)
Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos)
self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
self.add_path("metal1", [mid, Q_bar_pos])
def route_write_access(self): def route_write_access(self):
""" Routes read/write transistors to the storage component of the bitcell """ """ Routes read/write transistors to the storage component of the bitcell """
for k in range(self.num_w_ports): for k in range(self.num_w_ports):
mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos)
Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos)
self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid, Q_pos])
self.add_path("metal1", [mid, Q_pos])
mid = vector(self.write_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) mid = vector(self.write_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos)
Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos)
self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
self.add_path("metal1", [mid, Q_bar_pos])
def route_read_access(self): def route_read_access(self):
""" Routes read access transistors to the storage component of the bitcell """ """ Routes read access transistors to the storage component of the bitcell """
# add poly to metal1 contacts for gates of the inverters # add poly to metal1 contacts for gates of the inverters
left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_upper_ypos) left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - self.poly_to_polycontact - 0.5*contact.poly.width, self.cross_couple_upper_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=left_storage_contact, offset=left_storage_contact,
rotate=90) directions=("H","H"))
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_upper_ypos) right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + self.poly_to_polycontact + 0.5*contact.poly.width, self.cross_couple_upper_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=right_storage_contact, offset=right_storage_contact,
rotate=90) directions=("H","H"))
inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos) inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos)
self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) self.add_path("poly", [left_storage_contact, inverter_gate_offset_left])
@ -757,25 +760,23 @@ class pbitcell(design.design):
for k in range(self.num_r_ports): for k in range(self.num_r_ports):
port_contact_offset = self.read_access_nmos_left[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) port_contact_offset = self.read_access_nmos_left[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=port_contact_offset) offset=port_contact_offset)
self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").uc(), port_contact_offset]) self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").uc(), port_contact_offset])
mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, self.cross_couple_upper_ypos)
self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [port_contact_offset, mid, left_storage_contact])
self.add_path("metal1", [mid, left_storage_contact])
port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=port_contact_offset) offset=port_contact_offset)
self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").uc(), port_contact_offset]) self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").uc(), port_contact_offset])
mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, self.cross_couple_upper_ypos)
self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [port_contact_offset, mid, right_storage_contact])
self.add_path("metal1", [mid, right_storage_contact])
def extend_well(self): def extend_well(self):
""" """
@ -794,13 +795,13 @@ class pbitcell(design.design):
# 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
inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) - drc["well_enclosure_active"] inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) - self.well_enclose_active
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"] inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - self.well_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) + 2*drc["well_enclosure_active"] well_width = 2*(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) + 2*self.well_enclose_active
well_height = self.vdd_position.y - inverter_well_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"] well_height = self.vdd_position.y - inverter_well_ypos + self.well_enclose_active + drc["minwidth_tx"]
offset = [inverter_well_xpos,inverter_well_ypos] offset = [inverter_well_xpos,inverter_well_ypos]
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
@ -811,19 +812,19 @@ class pbitcell(design.design):
# add well contacts # add well contacts
# connect pimplants to gnd # connect pimplants to gnd
offset = vector(0, self.gnd_position.y) offset = vector(0, self.gnd_position.y)
self.add_contact_center(layers=("active", "contact", "metal1"), self.add_via_center(layers=("active", "contact", "metal1"),
offset=offset, offset=offset,
rotate=90, directions=("H","H"),
implant_type="p", implant_type="p",
well_type="p") well_type="p")
# connect nimplants to vdd # connect nimplants to vdd
offset = vector(0, self.vdd_position.y) offset = vector(0, self.vdd_position.y)
self.add_contact_center(layers=("active", "contact", "metal1"), self.add_via_center(layers=("active", "contact", "metal1"),
offset=offset, offset=offset,
rotate=90, directions=("H","H"),
implant_type="n", implant_type="n",
well_type="n") well_type="n")
def list_bitcell_pins(self, col, row): def list_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
@ -867,12 +868,16 @@ class pbitcell(design.design):
self.add_path("metal1", [Q_bar_pos, vdd_pos]) self.add_path("metal1", [Q_bar_pos, vdd_pos])
def analytical_delay(self, corner, slew, load=0, swing = 0.5): def analytical_delay(self, corner, slew, load=0, swing = 0.5):
#FIXME: Delay copied exactly over from bitcell parasitic_delay = 1
from tech import spice size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
r = spice["min_tx_r"]*3 cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
c_para = spice["min_tx_drain_c"]
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = swing) #Internal loads due to port configs are halved. This is to account for the size already being halved
return result #for stacked TXs, but internal loads do not see this size estimation.
write_port_load = self.num_w_ports*logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])/2
read_port_load = self.num_r_ports/2 #min size NMOS gate load
total_load = load+read_port_load+write_port_load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
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."""

View File

@ -1,3 +1,10 @@
# 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 design
import debug import debug
import utils import utils

View File

@ -1,3 +1,10 @@
# 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 design
import debug import debug
import utils import utils

View File

@ -1,3 +1,10 @@
# 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 design
import debug import debug
import utils import utils

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
from tech import drc, spice,parameter from tech import drc, spice,parameter
@ -67,8 +74,7 @@ class replica_pbitcell(design.design):
self.connect_inst(temp) self.connect_inst(temp)
def place_pbitcell(self): def place_pbitcell(self):
offset = [0,0] self.prbc_inst.place(offset=vector(0,0))
self.prbc_inst.place(offset=offset)
def route_rbc_connections(self): def route_rbc_connections(self):
for port in range(self.total_ports): for port in range(self.total_ports):

View File

@ -1,3 +1,10 @@
# 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 os import os
import debug import debug
import globals import globals

View File

@ -1,3 +1,10 @@
# 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 sys,re,shutil import sys,re,shutil
import debug import debug
import tech import tech

View File

@ -1,3 +1,10 @@
# 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 re import re
import debug import debug
from globals import OPTS from globals import OPTS

View File

@ -1,3 +1,10 @@
# 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 sys,re,shutil import sys,re,shutil
import debug import debug
import tech import tech
@ -9,6 +16,7 @@ import utils
from globals import OPTS from globals import OPTS
from .simulation import simulation from .simulation import simulation
from .measurements import * from .measurements import *
import logical_effort
class delay(simulation): class delay(simulation):
"""Functions to measure the delay and power of an SRAM at a given address and """Functions to measure the delay and power of an SRAM at a given address and
@ -904,8 +912,9 @@ class delay(simulation):
self.create_measurement_names() self.create_measurement_names()
power = self.analytical_power(slews, loads) power = self.analytical_power(slews, loads)
port_data = self.get_empty_measure_data_dict() port_data = self.get_empty_measure_data_dict()
relative_loads = [logical_effort.convert_farad_to_relative_c(c_farad) for c_farad in loads]
for slew in slews: for slew in slews:
for load in loads: for load in relative_loads:
self.set_load_slew(load,slew) self.set_load_slew(load,slew)
bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load) bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load)
for port in self.all_ports: for port in self.all_ports:

View File

@ -1,3 +1,10 @@
# 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 sys,re,shutil import sys,re,shutil
from design import design from design import design
import debug import debug

View File

@ -1,3 +1,10 @@
# 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 os,sys,re import os,sys,re
import debug import debug
import math import math

View File

@ -1,3 +1,10 @@
# 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 debug import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
@ -9,6 +16,7 @@ class logical_effort():
beta = parameter["beta"] beta = parameter["beta"]
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']
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
@ -30,30 +38,44 @@ class logical_effort():
def get_stage_effort(self): def get_stage_effort(self):
return self.logical_effort*self.eletrical_effort return self.logical_effort*self.eletrical_effort
def get_parasitic_delay(self, pinv): def get_parasitic_delay(self):
return pinv * self.parasitic_scale return logical_effort.pinv * self.parasitic_scale
def get_stage_delay(self, pinv): def get_stage_delay(self):
return self.get_stage_effort()+self.get_parasitic_delay(pinv) return self.get_stage_effort()+self.get_parasitic_delay()
def calculate_delays(stage_effort_list, pinv): def get_absolute_delay(self):
return logical_effort.tau*self.get_stage_delay()
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(pinv) for stage in stage_effort_list] return [stage.get_stage_delay() for stage in stage_effort_list]
def calculate_relative_delay(stage_effort_list, pinv=parameter["min_inv_para_delay"]): 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, pinv) total_rise_delay, total_fall_delay = calculate_relative_rise_fall_delays(stage_effort_list)
return total_rise_delay + total_fall_delay return total_rise_delay + total_fall_delay
def calculate_absolute_delay(stage_effort_list):
"""Calculates the total delay of a given delay path made of a list of logical effort objects."""
total_delay = 0
for stage in stage_effort_list:
total_delay+=stage.get_absolute_delay()
return total_delay
def calculate_relative_rise_fall_delays(stage_effort_list, pinv=parameter["min_inv_para_delay"]): 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")
total_rise_delay, total_fall_delay = 0,0 total_rise_delay, total_fall_delay = 0,0
for stage in stage_effort_list: for stage in stage_effort_list:
debug.info(2, stage) debug.info(2, stage)
if stage.is_rise: if stage.is_rise:
total_rise_delay += stage.get_stage_delay(pinv) total_rise_delay += stage.get_stage_delay()
else: else:
total_fall_delay += stage.get_stage_delay(pinv) 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):
"""Converts capacitance in Femto-Farads to relative capacitance."""
return c_farad*parameter['cap_relative_per_ff']

View File

@ -1,3 +1,10 @@
# 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 debug import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
from abc import ABC, abstractmethod from abc import ABC, abstractmethod

View File

@ -1,3 +1,10 @@
# 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 sys,re,shutil import sys,re,shutil
import debug import debug
import tech import tech
@ -18,15 +25,18 @@ class model_check(delay):
""" """
def __init__(self, sram, spfile, corner): def __init__(self, sram, spfile, corner, custom_delaychain=False):
delay.__init__(self,sram,spfile,corner) delay.__init__(self,sram,spfile,corner)
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
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.power_name = "total_power"
def create_measurement_names(self): def create_measurement_names(self):
"""Create measurement names. The names themselves currently define the type of measurement""" """Create measurement names. The names themselves currently define the type of measurement"""
@ -34,21 +44,33 @@ class model_check(delay):
wl_en_driver_delay_names = ["delay_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] wl_en_driver_delay_names = ["delay_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())]
wl_driver_delay_names = ["delay_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] wl_driver_delay_names = ["delay_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())]
sen_driver_delay_names = ["delay_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] sen_driver_delay_names = ["delay_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())]
dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] if self.custom_delaychain:
dc_delay_names = ['delay_dc_out_final']
else:
dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)]
self.wl_delay_meas_names = wl_en_driver_delay_names+["delay_wl_en", "delay_wl_bar"]+wl_driver_delay_names+["delay_wl"] self.wl_delay_meas_names = wl_en_driver_delay_names+["delay_wl_en", "delay_wl_bar"]+wl_driver_delay_names+["delay_wl"]
self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names
self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"] self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"]
# if self.custom_delaychain:
# self.delay_chain_indices = (len(self.rbl_delay_meas_names), len(self.rbl_delay_meas_names)+1)
# else:
self.delay_chain_indices = (len(self.rbl_delay_meas_names)-len(dc_delay_names), len(self.rbl_delay_meas_names)) self.delay_chain_indices = (len(self.rbl_delay_meas_names)-len(dc_delay_names), len(self.rbl_delay_meas_names))
#Create slew measurement names #Create slew measurement names
wl_en_driver_slew_names = ["slew_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] wl_en_driver_slew_names = ["slew_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())]
wl_driver_slew_names = ["slew_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] wl_driver_slew_names = ["slew_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())]
sen_driver_slew_names = ["slew_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] sen_driver_slew_names = ["slew_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())]
dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] if self.custom_delaychain:
dc_slew_names = ['slew_dc_out_final']
else:
dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)]
self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"] self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"]
self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names
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.power_meas_names = ['read0_power']
def create_signal_names(self): def create_signal_names(self):
"""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
@ -59,7 +81,10 @@ class model_check(delay):
wl_en_driver_signals = ["Xsram.Xcontrol0.Xbuf_wl_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] wl_en_driver_signals = ["Xsram.Xcontrol0.Xbuf_wl_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())]
wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver0.Xwl_driver_inv{}.Zb{}_int".format(self.wordline_row, stage) for stage in range(1,self.get_num_wl_driver_stages())] wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver0.Xwl_driver_inv{}.Zb{}_int".format(self.wordline_row, stage) for stage in range(1,self.get_num_wl_driver_stages())]
sen_driver_signals = ["Xsram.Xcontrol0.Xbuf_s_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] sen_driver_signals = ["Xsram.Xcontrol0.Xbuf_s_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_sen_driver_stages())]
delay_chain_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_{}".format(stage) for stage in range(1,self.get_num_delay_stages())] if self.custom_delaychain:
delay_chain_signal_names = []
else:
delay_chain_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_{}".format(stage) for stage in range(1,self.get_num_delay_stages())]
self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar"]+\ self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar"]+\
wl_en_driver_signals+\ wl_en_driver_signals+\
@ -70,17 +95,31 @@ class model_check(delay):
self.rbl_en_signal_names = pre_delay_chain_names+\ self.rbl_en_signal_names = pre_delay_chain_names+\
delay_chain_signal_names+\ delay_chain_signal_names+\
["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] ["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"]
self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+\ self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+\
sen_driver_signals+\ sen_driver_signals+\
["Xsram.s_en0"] ["Xsram.s_en0"]
dout_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit
self.bl_signal_names = ["Xsram.Xbank0.wl_{}".format(self.wordline_row),\
"Xsram.Xbank0.bl_{}".format(self.bitline_column),\
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_measurement_objects() self.create_wordline_meas_objs()
self.create_sae_measurement_objects() self.create_sae_meas_objs()
self.all_measures = self.wl_meas_objs+self.sae_meas_objs self.create_bl_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
def create_wordline_measurement_objects(self): def create_power_meas_objs(self):
"""Create power measurement object. Only one."""
self.power_meas_objs = []
self.power_meas_objs.append(power_measure(self.power_meas_names[0], "FALL", measure_scale=1e3))
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"
@ -102,7 +141,19 @@ class model_check(delay):
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_sae_measurement_objects(self): def create_bl_meas_objs(self):
"""Create the measurements to measure the bitline to dout, static stages"""
#Bitline has slightly different measurements, objects appends hardcoded.
self.bl_meas_objs = []
trig_dir, targ_dir = "RISE", "FALL" #Only check read 0
self.bl_meas_objs.append(delay_measure(self.bitline_meas_names[0],
self.bl_signal_names[0],
self.bl_signal_names[-1],
trig_dir,
targ_dir,
measure_scale=1e9))
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 = []
@ -123,6 +174,13 @@ class model_check(delay):
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
self.sae_meas_objs[-2] = delay_measure(self.rbl_delay_meas_names[-1],
self.rbl_en_signal_names[-2],
self.rbl_en_signal_names[-1],
"RISE",
"RISE",
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,
@ -131,7 +189,6 @@ class model_check(delay):
#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"
#Add measurements from gated_clk_bar to RBL
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],
@ -169,10 +226,22 @@ class model_check(delay):
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
if not (type(measure_obj) is delay_measure or type(measure_obj) is slew_measure): debug.info(3,"Power measurement={}".format(measure_obj))
if (type(measure_obj) is delay_measure or type(measure_obj) is slew_measure):
meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2
return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port)
elif type(measure_obj) is power_measure:
return self.get_power_measure_variants(port, measure_obj, "read")
else:
debug.error("Measurement not recognized by the model checker.",1) debug.error("Measurement not recognized by the model checker.",1)
meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2
return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) def get_power_measure_variants(self, port, power_obj, operation):
"""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
t_initial = self.cycle_times[self.measure_cycles[port]["read0"]]
t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1]
return (t_initial, t_final, port)
def write_measures_read_port(self, port): def write_measures_read_port(self, port):
""" """
@ -186,7 +255,8 @@ class model_check(delay):
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=[]
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:
@ -195,9 +265,11 @@ class model_check(delay):
delay_meas_list.append(measure_value) delay_meas_list.append(measure_value)
elif type(measure)is slew_measure: elif type(measure)is slew_measure:
slew_meas_list.append(measure_value) slew_meas_list.append(measure_value)
elif type(measure)is power_measure:
power_meas_list.append(measure_value)
else: else:
debug.error("Measurement object not recognized.",1) debug.error("Measurement object not recognized.",1)
return delay_meas_list, slew_meas_list return delay_meas_list, slew_meas_list,power_meas_list
def run_delay_simulation(self): def run_delay_simulation(self):
""" """
@ -213,6 +285,9 @@ class model_check(delay):
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]
sae_slew_result = [[] for i in self.all_ports] sae_slew_result = [[] for i in self.all_ports]
bl_delay_result = [[] for i in self.all_ports]
bl_slew_result = [[] for i in self.all_ports]
power_result = [[] for i in self.all_ports]
# Checking from not data_value to data_value # Checking from not data_value to data_value
self.write_delay_stimulus() self.write_delay_stimulus()
@ -221,9 +296,11 @@ class model_check(delay):
#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)
return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result) 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)
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."""
@ -306,23 +383,28 @@ class model_check(delay):
self.targ_read_ports = [read_port] self.targ_read_ports = [read_port]
self.targ_write_ports = [self.write_ports[0]] self.targ_write_ports = [self.write_ports[0]]
debug.info(1,"Model test: corner {}".format(self.corner)) debug.info(1,"Model test: corner {}".format(self.corner))
(success, wl_delays, sae_delays, wl_slews, sae_slews)=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))
wl_model_delays, sae_model_delays = self.get_model_delays(read_port)
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,"Wordline model delays:\n\t {}".format(wl_model_delays))
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,"SAE model delays:\n\t {}".format(sae_model_delays))
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]))
data_dict[self.wl_meas_name] = wl_delays[read_port] data_dict[self.wl_meas_name] = wl_delays[read_port]
data_dict[self.wl_model_name] = wl_model_delays
data_dict[self.sae_meas_name] = sae_delays[read_port] data_dict[self.sae_meas_name] = sae_delays[read_port]
data_dict[self.sae_model_name] = sae_model_delays
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.power_name] = powers[read_port]
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)
debug.info(1,"Wordline model delays:\n\t {}".format(wl_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.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.")
@ -337,11 +419,17 @@ class model_check(delay):
name_dict = {} name_dict = {}
#Signal names are more descriptive than the measurement names, first value trimmed to match size of measurements names. #Signal names are more descriptive than the measurement names, first value trimmed to match size of measurements names.
name_dict[self.wl_meas_name] = self.wl_signal_names[1:] name_dict[self.wl_meas_name] = self.wl_signal_names[1:]
name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured.
name_dict[self.sae_meas_name] = self.rbl_en_signal_names[1:]+self.sae_signal_names[1:] name_dict[self.sae_meas_name] = self.rbl_en_signal_names[1:]+self.sae_signal_names[1:]
name_dict[self.sae_model_name] = name_dict["sae_measures"]
name_dict[self.wl_slew_name] = self.wl_slew_meas_names name_dict[self.wl_slew_name] = self.wl_slew_meas_names
name_dict[self.sae_slew_name] = self.rbl_slew_meas_names+self.sae_slew_meas_names name_dict[self.sae_slew_name] = self.rbl_slew_meas_names+self.sae_slew_meas_names
name_dict[self.bl_meas_name] = self.bitline_meas_names[0:1]
name_dict[self.power_name] = self.power_meas_names
#name_dict[self.wl_slew_name] = self.wl_slew_meas_names
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.sae_model_name] = name_dict["sae_measures"]
return name_dict return name_dict

View File

@ -1,3 +1,10 @@
# 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 sys import sys
import tech import tech
from .stimuli import * from .stimuli import *

View File

@ -1,3 +1,10 @@
# 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 sys,re,shutil import sys,re,shutil
from design import design from design import design
import debug import debug

View File

@ -1,3 +1,10 @@
# 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.
#
""" """
This file generates simple spice cards for simulation. There are This file generates simple spice cards for simulation. There are
various functions that can be be used to generate stimulus for other various functions that can be be used to generate stimulus for other

View File

@ -1,3 +1,10 @@
# 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 debug import debug
from math import log from math import log
import re import re

View File

@ -1,3 +1,10 @@
# 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 sys,re,shutil import sys,re,shutil
import debug import debug
import tech import tech

View File

@ -1,3 +1,10 @@
# 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.
#
from pathlib import Path from pathlib import Path
import glob import glob
import os import os

View File

@ -1,3 +1,10 @@
# 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.
#
from table_gen import * from table_gen import *
import os import os
import base64 import base64

View File

@ -1,3 +1,10 @@
# 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.
#
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
This is a script to load data from the characterization and layout processes into This is a script to load data from the characterization and layout processes into

View File

@ -1,3 +1,10 @@
# 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.
#
class table_gen: class table_gen:
"""small library of functions to generate the html tables""" """small library of functions to generate the html tables"""

View File

@ -1,3 +1,10 @@
# 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 os import os
import inspect import inspect
import globals import globals
@ -58,6 +65,8 @@ def log(str):
# in another log file if the path or name changes. # in another log file if the path or name changes.
if not globals.OPTS.output_path.endswith('/'): if not globals.OPTS.output_path.endswith('/'):
globals.OPTS.output_path += "/" globals.OPTS.output_path += "/"
if not os.path.isdir(globals.OPTS.output_path):
os.mkdir(globals.OPTS.output_path)
compile_log = open(globals.OPTS.output_path + compile_log = open(globals.OPTS.output_path +
globals.OPTS.output_name + '.log', "w+") globals.OPTS.output_name + '.log', "w+")
log.create_file = 0 log.create_file = 0

View File

@ -1,3 +1,10 @@
# 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 debug import debug
from drc_value import * from drc_value import *
from drc_lut import * from drc_lut import *

View File

@ -1,3 +1,10 @@
# 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 debug import debug
class drc_lut(): class drc_lut():

View File

@ -1,3 +1,10 @@
# 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.
#
class drc_value(): class drc_value():
""" """

View File

@ -1,4 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
# 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.
#
""" """
This script will generate a stimulus file for a given period, load, and slew input This script will generate a stimulus file for a given period, load, and slew input
for the given dimension SRAM. It is useful for debugging after an SRAM has been for the given dimension SRAM. It is useful for debugging after an SRAM has been

View File

@ -1,3 +1,10 @@
# 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.
#
""" """
This is called globals.py, but it actually parses all the arguments and performs This is called globals.py, but it actually parses all the arguments and performs
the global OpenRAM setup as well. the global OpenRAM setup as well.
@ -477,11 +484,20 @@ def report_status():
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 for run-time (incomplete GDS will not be saved, route_supplies=True to enable).") debug.print_raw("Design supply routing skipped for run-time (incomplete GDS will not be saved) (route_supplies=True to enable).")
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 enable).") debug.print_raw("DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to enable).")
if not OPTS.check_lvsdrc: if not OPTS.check_lvsdrc:
debug.print_raw("DRC/LVS/PEX is disabled (check_lvsdrc=True to enable).") debug.print_raw("DRC/LVS/PEX is disabled (check_lvsdrc=True to enable).")
if OPTS.analytical_delay:
debug.print_raw("Characterization is disabled (using analytical delay models) (analytical_delay=False to enable).")
else:
if OPTS.spice_name!="":
debug.print_raw("Performing simulation-based characterization with {}".format(OPTS.spice_name))
if OPTS.trim_netlist:
debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).")

View File

@ -1,3 +1,10 @@
# 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 sys import sys
from tech import drc, parameter from tech import drc, parameter
import debug import debug
@ -800,14 +807,11 @@ class bank(design.design):
bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y)
self.add_path("metal3",[out_pos, bus_pos]) self.add_path("metal3",[out_pos, bus_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=bus_pos, offset=bus_pos)
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_pos, offset=out_pos)
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=out_pos, offset=out_pos)
rotate=90)
def setup_routing_constraints(self): def setup_routing_constraints(self):
@ -1197,8 +1201,7 @@ class bank(design.design):
control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y) control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y)
self.add_path("metal1", [control_pos, pin_pos]) self.add_path("metal1", [control_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_pos, offset=control_pos)
rotate=90)
# clk to wordline_driver # clk to wordline_driver
control_signal = self.prefix+"wl_en{}".format(port) control_signal = self.prefix+"wl_en{}".format(port)
@ -1212,40 +1215,41 @@ class bank(design.design):
control_pos = vector(control_x_offset, mid_pos.y) control_pos = vector(control_x_offset, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_pos, offset=control_pos)
rotate=90)
def analytical_delay(self, corner, slew, load, port):
def analytical_delay(self, corner, slew, load): """ return analytical delay of the bank. This will track the clock to output path"""
""" return analytical delay of the bank""" #FIXME: This delay is determined in the control logic. Should be moved here.
results = [] # word_driver_delay = self.wordline_driver.analytical_delay(corner,
# slew,
decoder_delay = self.row_decoder.analytical_delay(corner, slew, self.wordline_driver.input_load()) # self.bitcell_array.input_load())
word_driver_delay = self.wordline_driver.analytical_delay(corner,
decoder_delay.slew,
self.bitcell_array.input_load())
#FIXME: Array delay is the same for every port. #FIXME: Array delay is the same for every port.
bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_delay.slew) word_driver_slew = 0
if self.words_per_row > 1:
bitline_ext_load = self.column_mux_array[port].get_drain_cin()
else:
bitline_ext_load = self.sense_amp_array.get_drain_cin()
bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_slew, bitline_ext_load)
bitcell_array_slew = 0
#This also essentially creates the same delay for each port. Good structure, no substance #This also essentially creates the same delay for each port. Good structure, no substance
for port in self.all_ports: if self.words_per_row > 1:
if self.words_per_row > 1: sa_load = self.sense_amp_array.get_drain_cin()
column_mux_delay = self.column_mux_array[port].analytical_delay(corner, column_mux_delay = self.column_mux_array[port].analytical_delay(corner,
bitcell_array_delay.slew, bitcell_array_slew,
self.sense_amp_array.input_load()) sa_load)
else: else:
column_mux_delay = self.return_delay(delay = 0.0, slew=word_driver_delay.slew) column_mux_delay = []
bl_t_data_out_delay = self.sense_amp_array.analytical_delay(corner, column_mux_slew = 0
column_mux_delay.slew, sense_amp_delay = self.sense_amp_array.analytical_delay(corner,
self.bitcell_array.output_load()) column_mux_slew,
# output load of bitcell_array is set to be only small part of bl for sense amp. load)
results.append(decoder_delay + word_driver_delay + bitcell_array_delay + column_mux_delay + bl_t_data_out_delay) # output load of bitcell_array is set to be only small part of bl for sense amp.
return bitcell_array_delay + column_mux_delay + sense_amp_delay
return results
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline""" """Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
#Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption #Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption

View File

@ -1,3 +1,10 @@
# 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 sys import sys
from tech import drc, parameter from tech import drc, parameter
import debug import debug
@ -213,7 +220,7 @@ class bank_select(design.design):
end=bank_sel_pin_end) end=bank_sel_pin_end)
self.add_via_center(layers=("metal2","via2","metal3"), self.add_via_center(layers=("metal2","via2","metal3"),
offset=bank_sel_pin_end, offset=bank_sel_pin_end,
rotate=90) directions=("H","H"))
# bank_sel_bar is vertical wire # bank_sel_bar is vertical wire
bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z")
@ -252,7 +259,7 @@ class bank_select(design.design):
self.add_path("metal2",[logic_pos, input_pos]) self.add_path("metal2",[logic_pos, input_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=logic_pos, offset=logic_pos,
rotate=90) directions=("H","H"))
# Connect the logic A input to the input pin # Connect the logic A input to the input pin
@ -260,10 +267,10 @@ class bank_select(design.design):
input_pos = vector(0,logic_pos.y) input_pos = vector(0,logic_pos.y)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=logic_pos, offset=logic_pos,
rotate=90) directions=("H","H"))
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=logic_pos, offset=logic_pos,
rotate=90) directions=("H","H"))
self.add_layout_pin_segment_center(text=input_name, self.add_layout_pin_segment_center(text=input_name,
layer="metal3", layer="metal3",
start=input_pos, start=input_pos,
@ -295,10 +302,10 @@ class bank_select(design.design):
pin_pos = vector(xoffset, supply_pin.cy()) pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin_pos, offset=pin_pos,
rotate=90) directions=("H","H"))
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos, offset=pin_pos,
rotate=90) directions=("H","H"))
self.add_layout_pin_rect_center(text=n, self.add_layout_pin_rect_center(text=n,
layer="metal3", layer="metal3",
offset=pin_pos) offset=pin_pos)

View File

@ -1,9 +1,17 @@
# 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 debug import debug
import design import design
from tech import drc, spice from tech import drc, spice
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
import logical_effort
class bitcell_array(design.design): class bitcell_array(design.design):
""" """
@ -127,26 +135,18 @@ class bitcell_array(design.design):
inst = self.cell_inst[row,col] inst = self.cell_inst[row,col]
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name): for pin in inst.get_pins(pin_name):
self.add_power_pin(pin_name, pin.center(), 0, pin.layer) self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
def analytical_delay(self, corner, slew, load):
def analytical_delay(self, corner, slew, load=0): """Returns relative delay of the bitline in the bitcell array"""
from tech import drc from tech import parameter
wl_wire = self.gen_wl_wire() #The load being driven/drained is mostly the bitline but could include the sense amp or the column mux.
wl_wire.return_delay_over_wire(slew) #The load from the bitlines is due to the drain capacitances from all the other bitlines and wire parasitics.
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
wl_to_cell_delay = wl_wire.return_delay_over_wire(slew) wire_unit_load = .05 * drain_load #Wires add 5% to this.
# hypothetical delay from cell to bl end without sense amp bitline_load = (drain_load+wire_unit_load)*self.row_size
bl_wire = self.gen_bl_wire() return [self.cell.analytical_delay(corner, slew, load+bitline_load)]
cell_load = 2 * bl_wire.return_input_cap() # we ingore the wire r
# hence just use the whole c
bl_swing = 0.1
cell_delay = self.cell.analytical_delay(corner, wl_to_cell_delay.slew, cell_load, swing = bl_swing)
#we do not consider the delay over the wire for now
return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay,
wl_to_cell_delay.slew)
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."""
from tech import drc, parameter from tech import drc, parameter

View File

@ -1,3 +1,10 @@
# 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.
#
from math import log from math import log
import design import design
from tech import drc, parameter from tech import drc, parameter
@ -33,10 +40,10 @@ class control_logic(design.design):
self.num_words = num_rows*words_per_row self.num_words = num_rows*words_per_row
self.enable_delay_chain_resizing = True self.enable_delay_chain_resizing = True
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.
self.wl_timing_tolerance = 1 self.wl_timing_tolerance = 1
self.parasitic_inv_delay = parameter["min_inv_para_delay"]
self.wl_stage_efforts = None self.wl_stage_efforts = None
self.sen_stage_efforts = None self.sen_stage_efforts = None
@ -219,7 +226,7 @@ class control_logic(design.design):
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout): def get_dynamic_delay_chain_size(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"""
from math import ceil from math import ceil
previous_delay_chain_delay = (previous_fanout+1+self.parasitic_inv_delay)*previous_stages previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*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))
delay_fanout = 3 # This can be anything >=2 delay_fanout = 3 # This can be anything >=2
@ -227,7 +234,7 @@ class control_logic(design.design):
#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
required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay) required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay)
debug.check(required_delay > 0, "Cannot size delay chain to have negative delay") debug.check(required_delay > 0, "Cannot size delay chain to have negative delay")
delay_stages = ceil(required_delay/(delay_fanout+1+self.parasitic_inv_delay)) delay_stages = ceil(required_delay/(delay_fanout+1+self.inv_parasitic_delay))
if delay_stages%2 == 1: #force an even number of stages. if delay_stages%2 == 1: #force an even number of stages.
delay_stages+=1 delay_stages+=1
#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.
@ -237,7 +244,7 @@ class control_logic(design.design):
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_chain_delay = (previous_fanout+1+self.parasitic_inv_delay)*previous_stages previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*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
@ -284,9 +291,9 @@ class control_logic(design.design):
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
if required_delay <= 3+self.parasitic_inv_delay: #3 is the minimum delay per stage (with pinv=0). if required_delay <= 3+self.inv_parasitic_delay: #3 is the minimum delay per stage (with pinv=0).
return 1 return 1
delay_stages = ceil(required_delay/(fanout+1+self.parasitic_inv_delay)) delay_stages = ceil(required_delay/(fanout+1+self.inv_parasitic_delay))
return delay_stages return delay_stages
def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall): def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall):
@ -615,11 +622,9 @@ class control_logic(design.design):
mid1 = vector(in_pos.x,out_pos.y) mid1 = vector(in_pos.x,out_pos.y)
self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos]) self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos])
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1","via1","metal2"),
offset=out_pos, offset=out_pos)
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"), self.add_via_center(layers=("metal2","via2","metal3"),
offset=out_pos, offset=out_pos)
rotate=90)
def create_pen_row(self): def create_pen_row(self):
if self.port_type == "rw": if self.port_type == "rw":
@ -759,8 +764,7 @@ class control_logic(design.design):
rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y) rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, mid_pos, rail_pos]) self.add_wire(("metal1","via1","metal2"),[in_pos, mid_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos, offset=rail_pos)
rotate=90)
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"):
@ -850,14 +854,14 @@ class control_logic(design.design):
def get_delays_to_wl(self): def get_delays_to_wl(self):
"""Get the delay (in delay units) of the clk to a wordline in the bitcell array""" """Get the delay (in delay units) of the clk to a wordline in the bitcell array"""
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
self.wl_stage_efforts = self.determine_wordline_stage_efforts() self.wl_stage_efforts = self.get_wordline_stage_efforts()
clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts, self.parasitic_inv_delay) clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts)
total_delay = clk_to_wl_rise + clk_to_wl_fall total_delay = clk_to_wl_rise + clk_to_wl_fall
debug.info(1, "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, clk_to_wl_fall,total_delay)) debug.info(1, "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, clk_to_wl_fall,total_delay))
return clk_to_wl_rise,clk_to_wl_fall return clk_to_wl_rise,clk_to_wl_fall
def determine_wordline_stage_efforts(self): def get_wordline_stage_efforts(self):
"""Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts""" """Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts"""
stage_effort_list = [] stage_effort_list = []
@ -871,7 +875,7 @@ class control_logic(design.design):
last_stage_is_rise = stage_effort_list[-1].is_rise last_stage_is_rise = stage_effort_list[-1].is_rise
#Then ask the sram for the other path delays (from the bank) #Then ask the sram for the other path delays (from the bank)
stage_effort_list += self.sram.determine_wordline_stage_efforts(last_stage_is_rise) stage_effort_list += self.sram.get_wordline_stage_efforts(last_stage_is_rise)
return stage_effort_list return stage_effort_list
@ -880,17 +884,15 @@ class control_logic(design.design):
This does not incorporate the delay of the replica bitline. This does not incorporate the delay of the replica bitline.
""" """
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
self.sen_stage_efforts = self.determine_sa_enable_stage_efforts() self.sen_stage_efforts = self.get_sa_enable_stage_efforts()
clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts, self.parasitic_inv_delay) clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts)
total_delay = clk_to_sen_rise + clk_to_sen_fall total_delay = clk_to_sen_rise + clk_to_sen_fall
debug.info(1, "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, clk_to_sen_fall,total_delay)) debug.info(1, "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, clk_to_sen_fall,total_delay))
return clk_to_sen_rise, clk_to_sen_fall return clk_to_sen_rise, clk_to_sen_fall
def determine_sa_enable_stage_efforts(self): def get_sa_enable_stage_efforts(self):
"""Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list""" """Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list"""
stage_effort_list = [] stage_effort_list = []
#Calculate the load on clk_buf_bar
ext_clk_buf_cout = self.sram.get_clk_bar_cin()
#Initial direction of clock signal for this path #Initial direction of clock signal for this path
last_stage_rise = True last_stage_rise = True
@ -917,7 +919,54 @@ class control_logic(design.design):
"""Gets a list of the stages and delays in order of their path.""" """Gets a list of the stages and delays in order of their path."""
if self.sen_stage_efforts == None or self.wl_stage_efforts == None: if self.sen_stage_efforts == None or self.wl_stage_efforts == None:
debug.error("Model delays not calculated for SRAM.", 1) debug.error("Model delays not calculated for SRAM.", 1)
wl_delays = logical_effort.calculate_delays(self.wl_stage_efforts, self.parasitic_inv_delay) wl_delays = logical_effort.calculate_delays(self.wl_stage_efforts)
sen_delays = logical_effort.calculate_delays(self.sen_stage_efforts, self.parasitic_inv_delay) sen_delays = logical_effort.calculate_delays(self.sen_stage_efforts)
return wl_delays, sen_delays return wl_delays, sen_delays
def analytical_delay(self, corner, slew, load):
"""Gets the analytical delay from clk input to wl_en output"""
stage_effort_list = []
#Calculate the load on clk_buf_bar
ext_clk_buf_cout = self.sram.get_clk_bar_cin()
#Operations logic starts on negative edge
last_stage_rise = False
#First stage(s), clk -(pdriver)-> clk_buf.
clk_buf_cout = self.replica_bitline.get_en_cin()
stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise
#Second stage, clk_buf -(inv)-> clk_bar
clk_bar_cout = self.and2.get_cin()
stage_effort_list += self.and2.get_stage_efforts(clk_bar_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise
#Third stage clk_bar -(and)-> gated_clk_bar
gated_clk_bar_cin = self.get_gated_clk_bar_cin()
stage_effort_list.append(self.inv.get_stage_effort(gated_clk_bar_cin, last_stage_rise))
last_stage_rise = stage_effort_list[-1].is_rise
#Stages from gated_clk_bar -------> wordline
stage_effort_list += self.get_wordline_stage_efforts()
return stage_effort_list
def get_clk_buf_cin(self):
"""Get the loads that are connected to the buffered clock.
Includes all the DFFs and some logic."""
#Control logic internal load
int_clk_buf_cap = self.inv.get_cin() + self.ctrl_dff_array.get_clk_cin() + self.and2.get_cin()
#Control logic external load (in the other parts of the SRAM)
ext_clk_buf_cap = self.sram.get_clk_bar_cin()
return int_clk_buf_cap + ext_clk_buf_cap
def get_gated_clk_bar_cin(self):
"""Get intermediates net gated_clk_bar's capacitance"""
total_cin = 0
total_cin += self.wl_en_driver.get_cin()
if self.port_type == 'rw':
total_cin +=self.and2.get_cin()
return total_cin

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
from tech import drc from tech import drc

View File

@ -1,3 +1,10 @@
# 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 globals import globals
import design import design
from math import log from math import log

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
from tech import drc from tech import drc

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
from tech import drc,parameter from tech import drc,parameter

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
from tech import drc from tech import drc

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
from tech import drc from tech import drc

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
from tech import drc from tech import drc

View File

@ -1,3 +1,10 @@
# 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.
#
from tech import drc from tech import drc
import debug import debug
import design import design
@ -213,11 +220,9 @@ class hierarchical_decoder(design.design):
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=input_offset, offset=input_offset)
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=output_offset, offset=output_offset)
rotate=90)
self.add_path(("metal3"), [input_offset, output_offset]) self.add_path(("metal3"), [input_offset, output_offset])
@ -575,8 +580,7 @@ class hierarchical_decoder(design.design):
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y) rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
self.add_path("metal1", [rail_pos, pin.lc()]) self.add_path("metal1", [rail_pos, pin.lc()])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos, offset=rail_pos)
rotate=90)
def route_predecode_rail_m3(self, rail_name, pin): def route_predecode_rail_m3(self, rail_name, pin):
@ -586,12 +590,10 @@ class hierarchical_decoder(design.design):
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2) mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y) rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin.center(), offset=pin.center())
rotate=90)
self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()]) self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()])
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=rail_pos, offset=rail_pos)
rotate=90)
def analytical_delay(self, corner, slew, load = 0.0): def analytical_delay(self, corner, slew, load = 0.0):

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
import math import math
@ -178,11 +185,9 @@ class hierarchical_predecode(design.design):
a_pos = vector(self.decode_rails[a_pin].x,y_offset) a_pos = vector(self.decode_rails[a_pin].x,y_offset)
self.add_path("metal1",[in_pos, a_pos]) self.add_path("metal1",[in_pos, a_pos])
self.add_via_center(layers = ("metal1", "via1", "metal2"), self.add_via_center(layers = ("metal1", "via1", "metal2"),
offset=[self.input_rails[in_pin].x, y_offset], offset=[self.input_rails[in_pin].x, y_offset])
rotate=90)
self.add_via_center(layers = ("metal1", "via1", "metal2"), self.add_via_center(layers = ("metal1", "via1", "metal2"),
offset=[self.decode_rails[a_pin].x, y_offset], offset=[self.decode_rails[a_pin].x, y_offset])
rotate=90)
def route_output_inverters(self): def route_output_inverters(self):
""" """
@ -223,8 +228,7 @@ class hierarchical_predecode(design.design):
rail_pos = vector(self.decode_rails[out_pin].x,y_offset) rail_pos = vector(self.decode_rails[out_pin].x,y_offset)
self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
self.add_via_center(layers = ("metal1", "via1", "metal2"), self.add_via_center(layers = ("metal1", "via1", "metal2"),
offset=rail_pos, offset=rail_pos)
rotate=90)
#route input #route input
@ -232,8 +236,7 @@ class hierarchical_predecode(design.design):
in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y) in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y)
self.add_path("metal1", [in_pos, inv_in_pos]) self.add_path("metal1", [in_pos, inv_in_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=in_pos, offset=in_pos)
rotate=90)
def route_nand_to_rails(self): def route_nand_to_rails(self):
@ -254,8 +257,8 @@ class hierarchical_predecode(design.design):
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
self.add_path("metal1", [rail_pos, pin_pos]) self.add_path("metal1", [rail_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos, offset=rail_pos)
rotate=90)

View File

@ -1,3 +1,10 @@
# 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.
#
from tech import drc from tech import drc
import debug import debug
import design import design

View File

@ -1,3 +1,10 @@
# 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.
#
from tech import drc from tech import drc
import debug import debug
import design import design

View File

@ -1,3 +1,10 @@
# 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 sys import sys
from tech import drc, parameter from tech import drc, parameter
import debug import debug

View File

@ -1,3 +1,10 @@
# 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 design
import debug import debug
from tech import drc from tech import drc

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
from tech import drc from tech import drc
@ -39,8 +46,8 @@ class replica_bitline(design.design):
#self.add_lvs_correspondence_points() #self.add_lvs_correspondence_points()
# Extra pitch on top and right # Extra pitch on top and right
self.width = self.rbl_inst.rx() - self.dc_inst.lx() + self.m2_pitch self.width = self.replica_column_inst.rx() - self.delay_chain_inst.lx() + self.m2_pitch
self.height = max(self.rbl_inst.uy(), self.dc_inst.uy()) + self.m3_pitch self.height = max(self.replica_column_inst.uy(), self.delay_chain_inst.uy()) + self.m3_pitch
self.DRC_LVS() self.DRC_LVS()
@ -111,12 +118,12 @@ class replica_bitline(design.design):
self.connect_inst(["vdd", "delayed_en", "bl0_0", "vdd"]) self.connect_inst(["vdd", "delayed_en", "bl0_0", "vdd"])
# add the well and poly contact # add the well and poly contact
self.dc_inst=self.add_inst(name="delay_chain", self.delay_chain_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain) mod=self.delay_chain)
self.connect_inst(["en", "delayed_en", "vdd", "gnd"]) self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
self.rbc_inst=self.add_inst(name="bitcell", self.replica_cell_inst=self.add_inst(name="bitcell",
mod=self.replica_bitcell) mod=self.replica_bitcell)
temp = [] temp = []
for port in self.all_ports: for port in self.all_ports:
temp.append("bl{}_0".format(port)) temp.append("bl{}_0".format(port))
@ -127,8 +134,8 @@ class replica_bitline(design.design):
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
self.rbl_inst=self.add_inst(name="load", self.replica_column_inst=self.add_inst(name="load",
mod=self.rbl) mod=self.rbl)
temp = [] temp = []
for port in self.all_ports: for port in self.all_ports:
@ -153,12 +160,12 @@ class replica_bitline(design.design):
self.tx_inst.place(self.access_tx_offset) self.tx_inst.place(self.access_tx_offset)
self.dc_inst.place(self.delay_chain_offset) self.delay_chain_inst.place(self.delay_chain_offset)
self.rbc_inst.place(offset=self.bitcell_offset, self.replica_cell_inst.place(offset=self.bitcell_offset,
mirror="MX") mirror="MX")
self.rbl_inst.place(self.rbl_offset) self.replica_column_inst.place(self.rbl_offset)
def route(self): def route(self):
@ -172,7 +179,7 @@ class replica_bitline(design.design):
# Connect the WL and gnd pins directly to the center and right gnd rails # Connect the WL and gnd pins directly to the center and right gnd rails
for row in range(self.bitcell_loads): for row in range(self.bitcell_loads):
wl = self.wl_list[0]+"_{}".format(row) wl = self.wl_list[0]+"_{}".format(row)
pin = self.rbl_inst.get_pin(wl) pin = self.replica_column_inst.get_pin(wl)
# Route the connection to the right so that it doesn't interfere with the cells # Route the connection to the right so that it doesn't interfere with the cells
# Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions # Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions
@ -188,7 +195,7 @@ class replica_bitline(design.design):
# for multiport, need to short wordlines to each other so they all connect to gnd. # for multiport, need to short wordlines to each other so they all connect to gnd.
wl_last = self.wl_list[-1]+"_{}".format(row) wl_last = self.wl_list[-1]+"_{}".format(row)
pin_last = self.rbl_inst.get_pin(wl_last) pin_last = self.replica_column_inst.get_pin(wl_last)
self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0)) self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0))
def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None): def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None):
@ -229,10 +236,10 @@ class replica_bitline(design.design):
for port in self.all_ports: for port in self.all_ports:
if is_replica_cell: if is_replica_cell:
wl = self.wl_list[port] wl = self.wl_list[port]
pin = self.rbc_inst.get_pin(wl) pin = self.replica_cell_inst.get_pin(wl)
else: else:
wl = self.wl_list[port]+"_{}".format(cell_row) wl = self.wl_list[port]+"_{}".format(cell_row)
pin = self.rbl_inst.get_pin(wl) pin = self.replica_column_inst.get_pin(wl)
if pin_side == "left": if pin_side == "left":
self.add_path("metal1", [pin.lc()-correct_x, pin.lc()]) self.add_path("metal1", [pin.lc()-correct_x, pin.lc()])
@ -245,8 +252,8 @@ class replica_bitline(design.design):
""" Propagate all vdd/gnd pins up to this level for all modules """ """ Propagate all vdd/gnd pins up to this level for all modules """
# These are the instances that every bank has # These are the instances that every bank has
top_instances = [self.rbl_inst, top_instances = [self.replica_column_inst,
self.dc_inst] self.delay_chain_inst]
for inst in top_instances: for inst in top_instances:
self.copy_layout_pin(inst, "vdd") self.copy_layout_pin(inst, "vdd")
self.copy_layout_pin(inst, "gnd") self.copy_layout_pin(inst, "gnd")
@ -257,11 +264,11 @@ class replica_bitline(design.design):
pin = self.rbl_inv_inst.get_pin("vdd") pin = self.rbl_inv_inst.get_pin("vdd")
self.add_power_pin("vdd", pin.lc()) self.add_power_pin("vdd", pin.lc())
pin=self.rbc_inst.get_pin("vdd") for pin in self.replica_cell_inst.get_pins("vdd"):
self.add_power_pin("vdd", pin.center(), 0, pin.layer) self.add_power_pin(name="vdd", loc=pin.center(), vertical=True, start_layer=pin.layer)
for pin in self.rbc_inst.get_pins("gnd"): for pin in self.replica_cell_inst.get_pins("gnd"):
self.add_power_pin("gnd", pin.center()) self.add_power_pin("gnd", pin.center(), vertical=True, start_layer=pin.layer)
@ -275,10 +282,10 @@ class replica_bitline(design.design):
poly_offset = poly_pin.uc() poly_offset = poly_pin.uc()
# This centers the contact above the poly by one pitch # This centers the contact above the poly by one pitch
contact_offset = poly_offset + vector(0,self.m2_pitch) contact_offset = poly_offset + vector(0,self.m2_pitch)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset) offset=contact_offset)
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=contact_offset) offset=contact_offset)
self.add_segment_center(layer="poly", self.add_segment_center(layer="poly",
start=poly_offset, start=poly_offset,
end=contact_offset) end=contact_offset)
@ -289,12 +296,12 @@ class replica_bitline(design.design):
# height=self.delay_chain_offset.y-nwell_offset.y) # height=self.delay_chain_offset.y-nwell_offset.y)
# 2. Route delay chain output to access tx gate # 2. Route delay chain output to access tx gate
delay_en_offset = self.dc_inst.get_pin("out").bc() delay_en_offset = self.delay_chain_inst.get_pin("out").bc()
self.add_path("metal2", [delay_en_offset,contact_offset]) self.add_path("metal2", [delay_en_offset,contact_offset])
# 3. Route the contact of previous route to the bitcell WL # 3. Route the contact of previous route to the bitcell WL
# route bend of previous net to bitcell WL # route bend of previous net to bitcell WL
wl_offset = self.rbc_inst.get_pin(self.wl_list[0]).lc() wl_offset = self.replica_cell_inst.get_pin(self.wl_list[0]).lc()
wl_mid1 = wl_offset - vector(1.5*drc("minwidth_metal1"), 0) wl_mid1 = wl_offset - vector(1.5*drc("minwidth_metal1"), 0)
wl_mid2 = vector(wl_mid1.x, contact_offset.y) wl_mid2 = vector(wl_mid1.x, contact_offset.y)
#xmid_point= 0.5*(wl_offset.x+contact_offset.x) #xmid_point= 0.5*(wl_offset.x+contact_offset.x)
@ -305,8 +312,8 @@ class replica_bitline(design.design):
# 4. Short wodlines if multiport # 4. Short wodlines if multiport
wl = self.wl_list[0] wl = self.wl_list[0]
wl_last = self.wl_list[-1] wl_last = self.wl_list[-1]
pin = self.rbc_inst.get_pin(wl) pin = self.replica_cell_inst.get_pin(wl)
pin_last = self.rbc_inst.get_pin(wl_last) pin_last = self.replica_cell_inst.get_pin(wl_last)
x_offset = self.short_wordlines(pin, pin_last, "left", True) x_offset = self.short_wordlines(pin, pin_last, "left", True)
#correct = vector(0.5*drc("minwidth_metal1"), 0) #correct = vector(0.5*drc("minwidth_metal1"), 0)
@ -315,7 +322,7 @@ class replica_bitline(design.design):
# DRAIN ROUTE # DRAIN ROUTE
# Route the drain to the vdd rail # Route the drain to the vdd rail
drain_offset = self.tx_inst.get_pin("D").center() drain_offset = self.tx_inst.get_pin("D").center()
self.add_power_pin("vdd", drain_offset, rotate=0) self.add_power_pin("vdd", drain_offset, vertical=True)
# SOURCE ROUTE # SOURCE ROUTE
# Route the drain to the RBL inverter input # Route the drain to the RBL inverter input
@ -325,7 +332,7 @@ class replica_bitline(design.design):
# Route the connection of the source route to the RBL bitline (left) # Route the connection of the source route to the RBL bitline (left)
# Via will go halfway down from the bitcell # Via will go halfway down from the bitcell
bl_offset = self.rbc_inst.get_pin(self.bl_list[0]).bc() bl_offset = self.replica_cell_inst.get_pin(self.bl_list[0]).bc()
# Route down a pitch so we can use M2 routing # Route down a pitch so we can use M2 routing
bl_down_offset = bl_offset - vector(0, self.m2_pitch) bl_down_offset = bl_offset - vector(0, self.m2_pitch)
self.add_path("metal2",[source_offset, bl_down_offset, bl_offset]) self.add_path("metal2",[source_offset, bl_down_offset, bl_offset])
@ -344,12 +351,12 @@ class replica_bitline(design.design):
def route_vdd(self): def route_vdd(self):
""" Route all signals connected to vdd """ """ Route all signals connected to vdd """
self.copy_layout_pin(self.dc_inst,"vdd") self.copy_layout_pin(self.delay_chain_inst,"vdd")
self.copy_layout_pin(self.rbc_inst,"vdd") self.copy_layout_pin(self.replica_cell_inst,"vdd")
# Connect the WL and vdd pins directly to the center and right vdd rails # Connect the WL and vdd pins directly to the center and right vdd rails
# Connect RBL vdd pins to center and right rails # Connect RBL vdd pins to center and right rails
rbl_vdd_pins = self.rbl_inst.get_pins("vdd") rbl_vdd_pins = self.replica_column_inst.get_pins("vdd")
for pin in rbl_vdd_pins: for pin in rbl_vdd_pins:
if pin.layer != "metal1": if pin.layer != "metal1":
continue continue
@ -360,11 +367,9 @@ class replica_bitline(design.design):
start=start, start=start,
end=end) end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start, offset=start)
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end, offset=end)
rotate=90)
# Add via for the inverter # Add via for the inverter
pin = self.rbl_inv_inst.get_pin("vdd") pin = self.rbl_inv_inst.get_pin("vdd")
@ -374,38 +379,34 @@ class replica_bitline(design.design):
start=start, start=start,
end=end) end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start, offset=start)
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end, offset=end)
rotate=90)
# Add via for the RBC # Add via for the RBC
pin = self.rbc_inst.get_pin("vdd") pin = self.replica_cell_inst.get_pin("vdd")
start = pin.lc() start = pin.lc()
end = vector(self.right_vdd_pin.cx(),pin.cy()) end = vector(self.right_vdd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1", self.add_segment_center(layer="metal1",
start=start, start=start,
end=end) end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end, offset=end)
rotate=90)
# Create the RBL rails too # Create the RBL rails too
rbl_pins = self.rbl_inst.get_pins("vdd") rbl_pins = self.replica_column_inst.get_pins("vdd")
for pin in rbl_pins: for pin in rbl_pins:
if pin.layer != "metal1": if pin.layer != "metal1":
continue continue
# If above the delay line, route the full width # If above the delay line, route the full width
left = vector(self.left_vdd_pin.cx(),pin.cy()) left = vector(self.left_vdd_pin.cx(),pin.cy())
center = vector(self.center_vdd_pin.cx(),pin.cy()) center = vector(self.center_vdd_pin.cx(),pin.cy())
if pin.cy() > self.dc_inst.uy() + self.m1_pitch: if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
start = left start = left
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left, offset=left)
rotate=90)
else: else:
start = center start = center
end = vector(self.right_vdd_pin.cx()+0.5*self.m1_width,pin.cy()) end = vector(self.right_vdd_pin.cx()+0.5*self.m1_width,pin.cy())
@ -425,22 +426,22 @@ class replica_bitline(design.design):
# Route the gnd lines from left to right # Route the gnd lines from left to right
# Add via for the delay chain # Add via for the delay chain
left_gnd_start = self.dc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) left_gnd_start = self.delay_chain_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0)
left_gnd_end = vector(left_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch) left_gnd_end = vector(left_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch)
self.left_gnd_pin=self.add_segment_center(layer="metal2", self.left_gnd_pin=self.add_segment_center(layer="metal2",
start=left_gnd_start, start=left_gnd_start,
end=left_gnd_end) end=left_gnd_end)
# Gnd line to the left of the replica bitline # Gnd line to the left of the replica bitline
center_gnd_start = self.rbc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) center_gnd_start = self.replica_cell_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0)
center_gnd_end = vector(center_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch) center_gnd_end = vector(center_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch)
self.center_gnd_pin=self.add_segment_center(layer="metal2", self.center_gnd_pin=self.add_segment_center(layer="metal2",
start=center_gnd_start, start=center_gnd_start,
end=center_gnd_end) end=center_gnd_end)
# Gnd line to the right of the replica bitline # Gnd line to the right of the replica bitline
right_gnd_start = self.rbc_inst.lr().scale(1,0) + vector(self.m2_pitch,0) right_gnd_start = self.replica_cell_inst.lr().scale(1,0) + vector(self.m2_pitch,0)
right_gnd_end = vector(right_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch) right_gnd_end = vector(right_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch)
self.right_gnd_pin=self.add_segment_center(layer="metal2", self.right_gnd_pin=self.add_segment_center(layer="metal2",
start=right_gnd_start, start=right_gnd_start,
end=right_gnd_end) end=right_gnd_end)
@ -450,13 +451,13 @@ class replica_bitline(design.design):
# Connect the WL and gnd pins directly to the center and right gnd rails # Connect the WL and gnd pins directly to the center and right gnd rails
for row in range(self.bitcell_loads): for row in range(self.bitcell_loads):
wl = self.wl_list[0]+"_{}".format(row) wl = self.wl_list[0]+"_{}".format(row)
pin = self.rbl_inst.get_pin(wl) pin = self.replica_column_inst.get_pin(wl)
if pin.layer != "metal1": if pin.layer != "metal1":
continue continue
# If above the delay line, route the full width # If above the delay line, route the full width
left = vector(self.left_gnd_pin.cx(),pin.cy()) left = vector(self.left_gnd_pin.cx(),pin.cy())
center = vector(self.center_gnd_pin.cx(),pin.cy()) center = vector(self.center_gnd_pin.cx(),pin.cy())
if pin.cy() > self.dc_inst.uy() + self.m1_pitch: if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
start = left start = left
else: else:
start = center start = center
@ -467,47 +468,41 @@ class replica_bitline(design.design):
end=end) end=end)
if start == left: if start == left:
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left, offset=left)
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=center, offset=center)
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end, offset=end)
rotate=90)
rbl_gnd_pins = self.rbl_inst.get_pins("gnd") # rbl_gnd_pins = self.replica_column_inst.get_pins("gnd")
# Add L shapes to each vertical gnd rail # # Add L shapes to each vertical gnd rail
for pin in rbl_gnd_pins: # for pin in rbl_gnd_pins:
if pin.layer != "metal1": # if pin.layer != "metal1":
continue # continue
# If above the delay line, route the full width # # If above the delay line, route the full width
left = vector(self.left_gnd_pin.cx(),pin.cy()) # left = vector(self.left_gnd_pin.cx(),pin.cy())
center = vector(self.center_gnd_pin.cx(),pin.cy()) # center = vector(self.center_gnd_pin.cx(),pin.cy())
if pin.cy() > self.dc_inst.uy() + self.m1_pitch: # if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
start = left # start = left
else: # else:
start = center # start = center
end = vector(self.right_gnd_pin.cx(),pin.cy()) # end = vector(self.right_gnd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1", # self.add_segment_center(layer="metal1",
start=start, # start=start,
end=end) # end=end)
if start == left: # if start == left:
self.add_via_center(layers=("metal1", "via1", "metal2"), # self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left, # offset=left)
rotate=90) # self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=("metal1", "via1", "metal2"), # offset=center)
offset=center, # self.add_via_center(layers=("metal1", "via1", "metal2"),
rotate=90) # offset=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Connect the gnd pins of the delay chain to the left rails # Connect the gnd pins of the delay chain to the left rails
dc_gnd_pins = self.dc_inst.get_pins("gnd") dc_gnd_pins = self.delay_chain_inst.get_pins("gnd")
for pin in dc_gnd_pins: for pin in dc_gnd_pins:
if pin.layer != "metal1": if pin.layer != "metal1":
continue continue
@ -520,12 +515,10 @@ class replica_bitline(design.design):
start=start, start=start,
end=end) end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start, offset=start)
rotate=90)
# self.add_via_center(layers=("metal1", "via1", "metal2"), # self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end, # offset=end)
#rotate=90)
# Add via for the inverter # Add via for the inverter
@ -536,16 +529,14 @@ class replica_bitline(design.design):
# start=start, # start=start,
# end=end) # end=end)
# self.add_via_center(layers=("metal1", "via1", "metal2"), # self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=start, # offset=start)
#rotate=90)
# self.add_via_center(layers=("metal1", "via1", "metal2"), # self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end, # offset=end)
#rotate=90)
# Create RBL rails too # Create RBL rails too
rbl_pins = self.rbl_inst.get_pins("gnd") rbl_pins = self.replica_column_inst.get_pins("gnd")
for pin in rbl_pins: for pin in rbl_pins:
if pin.layer != "metal2": if pin.layer != "metal2":
continue continue
@ -560,7 +551,7 @@ class replica_bitline(design.design):
def add_layout_pins(self): def add_layout_pins(self):
""" Route the input and output signal """ """ Route the input and output signal """
en_offset = self.dc_inst.get_pin("in").bc() en_offset = self.delay_chain_inst.get_pin("in").bc()
self.add_layout_pin_segment_center(text="en", self.add_layout_pin_segment_center(text="en",
layer="metal2", layer="metal2",
start=en_offset, start=en_offset,
@ -587,7 +578,7 @@ class replica_bitline(design.design):
height=pin.height(), height=pin.height(),
width=pin.width()) width=pin.width())
pin = self.dc_inst.get_pin("out") pin = self.delay_chain_inst.get_pin("out")
self.add_label_pin(text="delayed_en", self.add_label_pin(text="delayed_en",
layer=pin.layer, layer=pin.layer,
offset=pin.ll(), offset=pin.ll(),

View File

@ -1,7 +1,15 @@
# 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 design
import debug import debug
import utils import utils
from tech import GDS,layer, parameter,drc from tech import GDS,layer, parameter,drc
import logical_effort
class sense_amp(design.design): class sense_amp(design.design):
""" """
@ -31,12 +39,13 @@ class sense_amp(design.design):
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/parameter["min_tx_size"])#ff return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff
def analytical_delay(self, corner, slew, load=0.0): def analytical_delay(self, corner, slew, load):
from tech import spice #Delay of the sense amp will depend on the size of the amp and the output load.
r = spice["min_tx_r"]/(10) parasitic_delay = 1
c_para = spice["min_tx_drain_c"] cin = (parameter["sa_inv_pmos_size"] + parameter["sa_inv_nmos_size"])/drc("minwidth_tx")
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) sa_size = parameter["sa_inv_nmos_size"]/drc("minwidth_tx")
return self.return_delay(result.delay, result.slew) cc_inv_cin = cin
return logical_effort.logical_effort('column_mux', sa_size, cin, load+cc_inv_cin, parasitic_delay, False)
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"""

View File

@ -1,9 +1,17 @@
# 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 design
from tech import drc from tech import drc
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
import debug import debug
from globals import OPTS from globals import OPTS
import logical_effort
class sense_amp_array(design.design): class sense_amp_array(design.design):
""" """
@ -136,10 +144,17 @@ class sense_amp_array(design.design):
def input_load(self): def input_load(self):
return self.amp.input_load() return self.amp.input_load()
def analytical_delay(self, corner, slew, load=0.0): def analytical_delay(self, corner, slew, load):
return self.amp.analytical_delay(corner, slew=slew, load=load) return [self.amp.analytical_delay(corner, slew=slew, load=load)]
def get_en_cin(self): def get_en_cin(self):
"""Get the relative capacitance of all the sense amp enable connections in the array""" """Get the relative capacitance of all the sense amp enable connections in the array"""
sense_amp_en_cin = self.amp.get_en_cin() sense_amp_en_cin = self.amp.get_en_cin()
return sense_amp_en_cin * self.word_size return sense_amp_en_cin * self.word_size
def get_drain_cin(self):
"""Get the relative capacitance of the drain of the PMOS isolation TX"""
from tech import parameter
#Bitcell drain load being used to estimate PMOS drain load
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
return drain_load

View File

@ -1,3 +1,10 @@
# 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.
#
from math import log from math import log
import design import design
import contact import contact
@ -7,6 +14,7 @@ import math
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
from globals import OPTS from globals import OPTS
import logical_effort
class single_level_column_mux_array(design.design): class single_level_column_mux_array(design.design):
""" """
@ -130,8 +138,7 @@ class single_level_column_mux_array(design.design):
self.add_layout_pin(text="sel_{}".format(j), self.add_layout_pin(text="sel_{}".format(j),
layer="metal1", layer="metal1",
offset=offset, offset=offset,
width=self.mux.width * self.columns, width=self.mux.width * self.columns)
height=contact.m1m2.width)
def add_vertical_poly_rail(self): def add_vertical_poly_rail(self):
""" Connect the poly to the address rails """ """ Connect the poly to the address rails """
@ -148,82 +155,72 @@ class single_level_column_mux_array(design.design):
offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy()) offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy())
# Add the poly contact with a shift to account for the rotation # Add the poly contact with a shift to account for the rotation
self.add_via_center(layers=("metal1", "contact", "poly"), self.add_via_center(layers=("metal1", "contact", "poly"),
offset=offset, offset=offset)
rotate=90)
self.add_path("poly", [offset, gate_offset]) self.add_path("poly", [offset, gate_offset])
def route_bitlines(self): def route_bitlines(self):
""" Connect the output bit-lines to form the appropriate width mux """ """ Connect the output bit-lines to form the appropriate width mux """
for j in range(self.columns): for j in range(self.columns):
bl_offset = self.mux_inst[j].get_pin("bl_out").ll() bl_offset = self.mux_inst[j].get_pin("bl_out").bc()
br_offset = self.mux_inst[j].get_pin("br_out").ll() br_offset = self.mux_inst[j].get_pin("br_out").bc()
bl_out_offset = bl_offset - vector(0,(self.words_per_row+1)*self.m1_pitch) bl_out_offset = bl_offset - vector(0,(self.words_per_row+1)*self.m1_pitch)
br_out_offset = br_offset - vector(0,(self.words_per_row+2)*self.m1_pitch) br_out_offset = br_offset - vector(0,(self.words_per_row+2)*self.m1_pitch)
bl_out_offset_end = bl_out_offset + vector(0,self.route_height)
br_out_offset_end = br_out_offset + vector(0,self.route_height)
if (j % self.words_per_row) == 0: if (j % self.words_per_row) == 0:
# Create the metal1 to connect the n-way mux output from the pass gate # Create the metal1 to connect the n-way mux output from the pass gate
# These will be located below the select lines. Yes, these are M2 width # These will be located below the select lines. Yes, these are M2 width
# to ensure vias are enclosed and M1 min width rules. # to ensure vias are enclosed and M1 min width rules.
width = contact.m1m2.width + self.mux.width * (self.words_per_row - 1) width = self.m2_width + self.mux.width * (self.words_per_row - 1)
self.add_rect(layer="metal1", self.add_path("metal1", [bl_out_offset, bl_out_offset+vector(width,0)])
offset=bl_out_offset, self.add_path("metal1", [br_out_offset, br_out_offset+vector(width,0)])
width=width,
height=drc("minwidth_metal2"))
self.add_rect(layer="metal1",
offset=br_out_offset,
width=width,
height=drc("minwidth_metal2"))
# Extend the bitline output rails and gnd downward on the first bit of each n-way mux # Extend the bitline output rails and gnd downward on the first bit of each n-way mux
self.add_layout_pin(text="bl_out_{}".format(int(j/self.words_per_row)), self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)),
layer="metal2", layer="metal2",
offset=bl_out_offset.scale(1,0), start=bl_out_offset,
width=drc('minwidth_metal2'), end=bl_out_offset_end)
height=self.route_height) self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)),
self.add_layout_pin(text="br_out_{}".format(int(j/self.words_per_row)), layer="metal2",
layer="metal2", start=br_out_offset,
offset=br_out_offset.scale(1,0), end=br_out_offset_end)
width=drc('minwidth_metal2'),
height=self.route_height)
# This via is on the right of the wire # This via is on the right of the wire
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=bl_out_offset + vector(contact.m1m2.height,0), offset=bl_out_offset)
rotate=90)
# This via is on the left of the wire # This via is on the left of the wire
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset= br_out_offset, offset=br_out_offset)
rotate=90)
else: else:
self.add_rect(layer="metal2", self.add_path("metal2", [ bl_out_offset, bl_out_offset_end])
offset=bl_out_offset, self.add_path("metal2", [ br_out_offset, br_out_offset_end])
width=drc('minwidth_metal2'),
height=self.route_height-bl_out_offset.y)
# This via is on the right of the wire # This via is on the right of the wire
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=bl_out_offset + vector(contact.m1m2.height,0), offset=bl_out_offset)
rotate=90)
self.add_rect(layer="metal2",
offset=br_out_offset,
width=drc('minwidth_metal2'),
height=self.route_height-br_out_offset.y)
# This via is on the left of the wire # This via is on the left of the wire
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset= br_out_offset, offset=br_out_offset)
rotate=90)
def analytical_delay(self, corner, vdd, slew, load=0.0):
from tech import spice, parameter def analytical_delay(self, corner, slew, load):
proc,vdd,temp = corner from tech import parameter
r = spice["min_tx_r"]/(self.mux.ptx_width/parameter["min_tx_size"]) """Returns relative delay that the column mux adds"""
#Drains of mux transistors make up capacitance. #Single level column mux will add parasitic loads from other mux pass transistors and the sense amp.
c_para = spice["min_tx_drain_c"]*(self.mux.ptx_width/parameter["min_tx_size"])*self.words_per_row#ff drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
volt_swing = spice["v_threshold_typical"]/vdd array_load = drain_load*self.words_per_row
return [self.mux.analytical_delay(corner, slew, load+array_load)]
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = volt_swing) def get_drain_cin(self):
return self.return_delay(result.delay, result.slew) """Get the relative capacitance of the drain of the NMOS pass TX"""
from tech import parameter
#Bitcell drain load being used to estimate mux NMOS drain load
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
return drain_load

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
import utils import utils

View File

@ -1,3 +1,10 @@
# 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 debug import debug
from tech import drc from tech import drc
import design import design

View File

@ -1,3 +1,10 @@
# 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.
#
from tech import drc, parameter from tech import drc, parameter
import debug import debug
import design import design
@ -81,21 +88,13 @@ class wordline_driver(design.design):
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num) (gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
# Route both supplies # Route both supplies
for n in ["vdd", "gnd"]: for name in ["vdd", "gnd"]:
supply_pin = self.inv2_inst[num].get_pin(n) supply_pin = self.inv2_inst[num].get_pin(name)
# Add pins in two locations # Add pins in two locations
for xoffset in [a_xoffset, b_xoffset]: for xoffset in [a_xoffset, b_xoffset]:
pin_pos = vector(xoffset, supply_pin.cy()) pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_power_pin(name, pin_pos)
offset=pin_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos,
rotate=90)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
@ -193,13 +192,14 @@ class wordline_driver(design.design):
start=input_offset, start=input_offset,
end=mid_via_offset) end=mid_via_offset)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid_via_offset) offset=mid_via_offset,
directions=("V","V"))
# now connect to the nand2 B # now connect to the nand2 B
self.add_path("metal2", [mid_via_offset, b_pos]) self.add_path("metal2", [mid_via_offset, b_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=b_pos - vector(0.5*contact.m1m2.height,0), offset=b_pos - vector(0.5*contact.m1m2.height,0),
rotate=90) directions=("H","H"))
# output each WL on the right # output each WL on the right

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import design import design
import utils import utils

View File

@ -1,3 +1,10 @@
# 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.
#
from math import log from math import log
import design import design
from tech import drc from tech import drc

View File

@ -1,4 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# 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.
#
""" """
SRAM Compiler SRAM Compiler

View File

@ -1,3 +1,10 @@
# 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 optparse import optparse
import getpass import getpass
import os import os

View File

@ -1,3 +1,10 @@
# 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 debug import debug
from tech import drc from tech import drc
from math import log from math import log
@ -11,16 +18,13 @@ class pand2(pgate.pgate):
This is a simple buffer used for driving loads. This is a simple buffer used for driving loads.
""" """
def __init__(self, name, size=1, height=None): def __init__(self, name, size=1, height=None):
self.size = size debug.info(1, "reating pnand2 {}".format(name))
pgate.pgate.__init__(self, name, height)
debug.info(1, "Creating {}".format(self.name))
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
self.create_netlist() self.size = size
if not OPTS.netlist_only:
self.create_layout() # Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
@ -40,6 +44,7 @@ class pand2(pgate.pgate):
self.place_insts() self.place_insts()
self.add_wires() self.add_wires()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.add_pin("A") self.add_pin("A")
@ -125,3 +130,8 @@ class pand2(pgate.pgate):
stage_effort_list.append(stage2) stage_effort_list.append(stage2)
return stage_effort_list return stage_effort_list
def get_cin(self):
"""Return the relative input capacitance of a single input"""
return self.nand.get_cin()

View File

@ -1,3 +1,10 @@
# 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 debug import debug
from tech import drc from tech import drc
from math import log from math import log
@ -12,17 +19,15 @@ class pbuf(pgate.pgate):
""" """
def __init__(self, name, size=4, height=None): def __init__(self, name, size=4, height=None):
debug.info(1, "creating {0} with size of {1}".format(name,size))
self.add_comment("size: {}".format(size))
self.stage_effort = 4 self.stage_effort = 4
self.size = size self.size = size
self.height = height self.height = height
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
debug.info(1, "creating {0} with size of {1}".format(self.name,self.size))
self.add_comment("size: {}".format(size))
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self): def create_netlist(self):

View File

@ -1,3 +1,10 @@
# 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 debug import debug
import pgate import pgate
import math import math
@ -13,27 +20,24 @@ class pdriver(pgate.pgate):
""" """
def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None): def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None):
debug.info(1, "creating pdriver {}".format(name))
self.stage_effort = 3 self.stage_effort = 3
self.height = height self.height = height
self.neg_polarity = neg_polarity self.neg_polarity = neg_polarity
self.size_list = size_list self.size_list = size_list
self.fanout = fanout self.fanout = fanout
if size_list == None and self.fanout == 0:
debug.error("Either fanout or size list must be specified.", -1)
if self.size_list and self.fanout != 0: if self.size_list and self.fanout != 0:
debug.error("Cannot specify both size_list and fanout.", -1) debug.error("Cannot specify both size_list and fanout.", -1)
if self.size_list and self.neg_polarity: if self.size_list and self.neg_polarity:
debug.error("Cannot specify both size_list and neg_polarity.", -1) debug.error("Cannot specify both size_list and neg_polarity.", -1)
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
debug.info(1, "Creating {}".format(self.name))
self.compute_sizes()
self.add_comment("sizes: {}".format(str(self.size_list)))
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def compute_sizes(self): def compute_sizes(self):
# size_list specified # size_list specified
@ -61,6 +65,8 @@ class pdriver(pgate.pgate):
def create_netlist(self): def create_netlist(self):
self.compute_sizes()
self.add_comment("sizes: {}".format(str(self.size_list)))
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_insts() self.create_insts()
@ -73,7 +79,6 @@ class pdriver(pgate.pgate):
self.width = self.inv_inst_list[-1].rx() self.width = self.inv_inst_list[-1].rx()
self.height = self.inv_inst_list[0].height self.height = self.inv_inst_list[0].height
self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.add_pin("A") self.add_pin("A")

View File

@ -1,3 +1,10 @@
# 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 contact import contact
import design import design
import debug import debug
@ -21,7 +28,20 @@ class pgate(design.design):
b = factory.create(module_type="bitcell") b = factory.create(module_type="bitcell")
self.height = b.height self.height = b.height
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
self.DRC_LVS()
def create_netlist():
""" Pure virtual function """
debug.error("Must over-ride create_netlist.",-1)
def create_layout():
""" Pure virtual function """
debug.error("Must over-ride create_layout.",-1)
def connect_pin_to_rail(self,inst,pin,supply): def connect_pin_to_rail(self,inst,pin,supply):
""" Connects a ptx pin to a supply rail. """ """ Connects a ptx pin to a supply rail. """
source_pin = inst.get_pin(pin) source_pin = inst.get_pin(pin)
@ -42,7 +62,7 @@ class pgate(design.design):
height=height, height=height,
width=source_pin.width()) width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=90): def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False):
""" Route the input gate to the left side of the cell for access. """ Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or right of gate. """ Position specifies to place the contact the left, center, or right of gate. """
@ -61,17 +81,21 @@ class pgate(design.design):
left_gate_offset = vector(nmos_gate_pin.lx(),ypos) left_gate_offset = vector(nmos_gate_pin.lx(),ypos)
# Center is completely symmetric. # Center is completely symmetric.
if rotate==90: if rotate:
contact_width = contact.poly.height contact_width = contact.poly.height
contact_m1_width = contact.poly.second_layer_height contact_m1_width = contact.poly.second_layer_height
contact_m1_height = contact.poly.second_layer_width contact_m1_height = contact.poly.second_layer_width
directions = ("H","V")
else: else:
contact_width = contact.poly.width contact_width = contact.poly.width
contact_m1_width = contact.poly.second_layer_width contact_m1_width = contact.poly.second_layer_width
contact_m1_height = contact.poly.second_layer_height contact_m1_height = contact.poly.second_layer_height
directions = ("V","H")
if position=="center": if position=="center":
contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0) contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0)
elif position=="farleft":
contact_offset = left_gate_offset - vector(0.5*contact.poly.width, 0)
elif position=="left": elif position=="left":
contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0) contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0)
elif position=="right": elif position=="right":
@ -79,9 +103,12 @@ class pgate(design.design):
else: else:
debug.error("Invalid contact placement option.", -1) debug.error("Invalid contact placement option.", -1)
self.add_contact_center(layers=("poly", "contact", "metal1"), # Non-preferred direction via
offset=contact_offset,
rotate=rotate) self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset,
directions=directions)
# self.add_layout_pin_segment_center(text=name, # self.add_layout_pin_segment_center(text=name,
# layer="metal1", # layer="metal1",
# start=left_gate_offset.scale(0,1), # start=left_gate_offset.scale(0,1),
@ -145,10 +172,11 @@ class pgate(design.design):
# Offset by half a contact in x and y # Offset by half a contact in x and y
contact_offset += vector(0.5*pmos.active_contact.first_layer_width, contact_offset += vector(0.5*pmos.active_contact.first_layer_width,
0.5*pmos.active_contact.first_layer_height) 0.5*pmos.active_contact.first_layer_height)
self.nwell_contact=self.add_contact_center(layers=layer_stack, self.nwell_contact=self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
implant_type="n", directions=("H","V"),
well_type="n") implant_type="n",
well_type="n")
self.add_rect_center(layer="metal1", self.add_rect_center(layer="metal1",
offset=contact_offset + vector(0,0.5*(self.height-contact_offset.y)), offset=contact_offset + vector(0,0.5*(self.height-contact_offset.y)),
width=self.nwell_contact.mod.second_layer_width, width=self.nwell_contact.mod.second_layer_width,
@ -191,10 +219,11 @@ class pgate(design.design):
# Offset by half a contact # Offset by half a contact
contact_offset += vector(0.5*nmos.active_contact.first_layer_width, contact_offset += vector(0.5*nmos.active_contact.first_layer_width,
0.5*nmos.active_contact.first_layer_height) 0.5*nmos.active_contact.first_layer_height)
self.pwell_contact=self.add_contact_center(layers=layer_stack, self.pwell_contact=self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
implant_type="p", directions=("H","V"),
well_type="p") implant_type="p",
well_type="p")
self.add_rect_center(layer="metal1", self.add_rect_center(layer="metal1",
offset=contact_offset.scale(1,0.5), offset=contact_offset.scale(1,0.5),
width=self.pwell_contact.mod.second_layer_width, width=self.pwell_contact.mod.second_layer_width,

View File

@ -1,3 +1,10 @@
# 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 contact import contact
import pgate import pgate
import debug import debug
@ -20,28 +27,18 @@ class pinv(pgate.pgate):
""" """
def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True): def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True):
# We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will debug.info(2, "creating pinv structure {0} with size of {1}".format(name, size))
# over-write a design in GDS if one has and the other doesn't
# have poly connected, for example.
pgate.pgate.__init__(self, name, height)
debug.info(2, "create pinv structure {0} with size of {1}".format(name, size))
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
self.size = size self.size = size
self.nmos_size = size self.nmos_size = size
self.pmos_size = beta*size self.pmos_size = beta*size
self.beta = beta self.beta = beta
self.route_output = False self.route_output = False
self.create_netlist() # Creates the netlist and layout
if not OPTS.netlist_only: pgate.pgate.__init__(self, name, height)
self.create_layout()
# for run-time, we won't check every transitor DRC/LVS independently
# but this may be uncommented for debug purposes
#self.DRC_LVS()
def create_netlist(self): def create_netlist(self):
""" Calls all functions related to the generation of the netlist """ """ Calls all functions related to the generation of the netlist """
@ -58,7 +55,7 @@ class pinv(pgate.pgate):
self.add_well_contacts() self.add_well_contacts()
self.extend_wells(self.well_pos) self.extend_wells(self.well_pos)
self.connect_rails() self.connect_rails()
self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", rotate=0) self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", position="farleft")
self.route_outputs() self.route_outputs()
def add_pins(self): def add_pins(self):
@ -222,8 +219,8 @@ class pinv(pgate.pgate):
pmos_drain_pin = self.pmos_inst.get_pin("D") pmos_drain_pin = self.pmos_inst.get_pin("D")
# Pick point at right most of NMOS and connect down to PMOS # Pick point at right most of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.lr() - vector(0.5*self.m1_width,0) nmos_drain_pos = nmos_drain_pin.bc()
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.bc().y) pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos]) self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos])
# Remember the mid for the output # Remember the mid for the output

View File

@ -1,18 +1,28 @@
# 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 debug import debug
import design import pgate
from tech import drc from tech import drc
from math import log from math import log
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 pinvbuf(design.design): class pinvbuf(pgate.pgate):
""" """
This is a simple inverter/buffer used for driving loads. It is This is a simple inverter/buffer used for driving loads. It is
used in the column decoder for 1:2 decoding and as the clock buffer. used in the column decoder for 1:2 decoding and as the clock buffer.
""" """
def __init__(self, name, size=4, height=None): def __init__(self, name, size=4, height=None):
debug.info(1, "creating pinvbuf {}".format(name))
self.add_comment("size: {}".format(size))
self.stage_effort = 4 self.stage_effort = 4
self.row_height = height self.row_height = height
# FIXME: Change the number of stages to support high drives. # FIXME: Change the number of stages to support high drives.
@ -23,13 +33,8 @@ class pinvbuf(design.design):
self.size = size self.size = size
self.predriver_size = max(int(self.size/(self.stage_effort/2)),1) self.predriver_size = max(int(self.size/(self.stage_effort/2)),1)
design.design.__init__(self, name) # Creates the netlist and layout
debug.info(1, "Creating {}".format(self.name)) pgate.pgate.__init__(self, name)
self.add_comment("size: {}".format(size))
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self): def create_netlist(self):
@ -48,7 +53,6 @@ class pinvbuf(design.design):
self.offset_all_coordinates() self.offset_all_coordinates()
self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.add_pin("A") self.add_pin("A")

View File

@ -1,3 +1,10 @@
# 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 contact import contact
import pgate import pgate
import debug import debug
@ -14,8 +21,8 @@ class pnand2(pgate.pgate):
""" """
def __init__(self, name, size=1, height=None): def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nand """ """ Creates a cell for a simple 2 input nand """
pgate.pgate.__init__(self, name, height)
debug.info(2, "create pnand2 structure {0} with size of {1}".format(name, size)) debug.info(2, "creating pnand2 structure {0} with size of {1}".format(name, size))
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
self.size = size self.size = size
@ -28,9 +35,8 @@ class pnand2(pgate.pgate):
debug.check(size==1,"Size 1 pnand2 is only supported now.") debug.check(size==1,"Size 1 pnand2 is only supported now.")
self.tx_mults = 1 self.tx_mults = 1
self.create_netlist() # Creates the netlist and layout
if not OPTS.netlist_only: pgate.pgate.__init__(self, name, height)
self.create_layout()
def create_netlist(self): def create_netlist(self):
@ -203,13 +209,15 @@ class pnand2(pgate.pgate):
mid1_offset = vector(out_offset.x, top_pin_offset.y) mid1_offset = vector(out_offset.x, top_pin_offset.y)
mid2_offset = vector(out_offset.x, bottom_pin_offset.y) mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center()) offset=pmos_pin.center(),
self.add_contact_center(layers=("metal1", "via1", "metal2"), directions=("V","H"))
offset=nmos_pin.center()) self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=nmos_pin.center(),
offset=out_offset, directions=("V","H"))
rotate=90) self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_offset)
# PMOS1 to mid-drain to NMOS2 drain # PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset]) self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset])

View File

@ -1,3 +1,10 @@
# 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 contact import contact
import pgate import pgate
import debug import debug
@ -14,8 +21,8 @@ class pnand3(pgate.pgate):
""" """
def __init__(self, name, size=1, height=None): def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 3 input nand """ """ Creates a cell for a simple 3 input nand """
pgate.pgate.__init__(self, name, height)
debug.info(2, "create pnand3 structure {0} with size of {1}".format(name, size)) debug.info(2, "creating pnand3 structure {0} with size of {1}".format(name, size))
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
# We have trouble pitch matching a 3x sizes to the bitcell... # We have trouble pitch matching a 3x sizes to the bitcell...
@ -30,9 +37,8 @@ class pnand3(pgate.pgate):
debug.check(size==1,"Size 1 pnand3 is only supported now.") debug.check(size==1,"Size 1 pnand3 is only supported now.")
self.tx_mults = 1 self.tx_mults = 1
self.create_netlist() # Creates the netlist and layout
if not OPTS.netlist_only: pgate.pgate.__init__(self, name, height)
self.create_layout()
def add_pins(self): def add_pins(self):
@ -213,12 +219,12 @@ class pnand3(pgate.pgate):
nmos3_pin = self.nmos3_inst.get_pin("D") nmos3_pin = self.nmos3_inst.get_pin("D")
# Go up to metal2 for ease on all output pins # Go up to metal2 for ease on all output pins
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos1_pin.center()) offset=pmos1_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos3_pin.center()) offset=pmos3_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos3_pin.center()) offset=nmos3_pin.center())
# PMOS3 and NMOS3 are drain aligned # PMOS3 and NMOS3 are drain aligned
self.add_path("metal2",[pmos3_pin.bc(), nmos3_pin.uc()]) self.add_path("metal2",[pmos3_pin.bc(), nmos3_pin.uc()])
@ -227,8 +233,8 @@ class pnand3(pgate.pgate):
self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell # This extends the output to the edge of the cell
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid_offset) offset=mid_offset)
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="metal1", layer="metal1",
offset=mid_offset, offset=mid_offset,

View File

@ -1,3 +1,10 @@
# 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 pgate import pgate
import debug import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
@ -13,8 +20,8 @@ class pnor2(pgate.pgate):
""" """
def __init__(self, name, size=1, height=None): def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nor """ """ Creates a cell for a simple 2 input nor """
pgate.pgate.__init__(self, name, height)
debug.info(2, "create pnor2 structure {0} with size of {1}".format(name, size)) debug.info(2, "creating pnor2 structure {0} with size of {1}".format(name, size))
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
self.nmos_size = size self.nmos_size = size
@ -27,9 +34,8 @@ class pnor2(pgate.pgate):
debug.check(size==1,"Size 1 pnor2 is only supported now.") debug.check(size==1,"Size 1 pnor2 is only supported now.")
self.tx_mults = 1 self.tx_mults = 1
self.create_netlist() # Creates the netlist and layout
self.create_layout() pgate.pgate.__init__(self, name, height)
#self.DRC_LVS()
def add_pins(self): def add_pins(self):
@ -38,12 +44,11 @@ class pnor2(pgate.pgate):
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.create_ptx()
self.setup_layout_constants()
def create_layout(self): def create_layout(self):
""" Calls all functions related to the generation of the layout """ """ Calls all functions related to the generation of the layout """
self.create_ptx()
self.setup_layout_constants()
self.add_supply_rails() self.add_supply_rails()
self.add_ptx() self.add_ptx()
self.connect_rails() self.connect_rails()
@ -184,11 +189,11 @@ class pnor2(pgate.pgate):
nmos2_pin = self.nmos2_inst.get_pin("D") nmos2_pin = self.nmos2_inst.get_pin("D")
# Go up to metal2 for ease on all output pins # Go up to metal2 for ease on all output pins
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center()) offset=pmos_pin.center())
m1m2_contact=self.add_contact_center(layers=("metal1", "via1", "metal2"), m1m2_contact=self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center(), offset=nmos_pin.center())
rotate=90)
mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y) mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y)
mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset) mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset)
@ -198,9 +203,8 @@ class pnor2(pgate.pgate):
self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset]) self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset])
self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset]) self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset])
# This extends the output to the edge of the cell # This extends the output to the edge of the cell
self.add_contact_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid3_offset, offset=mid3_offset)
rotate=90)
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="metal1", layer="metal1",
offset=mid3_offset, offset=mid3_offset,

View File

@ -1,31 +1,42 @@
# 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 contact import contact
import pgate import design
import debug import debug
from tech import drc, parameter from tech import drc, 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 precharge(pgate.pgate): class precharge(design.design):
""" """
Creates a single precharge cell Creates a single precharge cell
This module implements the precharge bitline cell used in the design. This module implements the precharge bitline cell used in the design.
""" """
def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"): def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"):
pgate.pgate.__init__(self, name)
debug.info(2, "create single precharge cell: {0}".format(name)) debug.info(2, "creating precharge cell {0}".format(name))
design.design.__init__(self, name)
self.bitcell = factory.create(module_type="bitcell") self.bitcell = factory.create(module_type="bitcell")
self.beta = parameter["beta"] self.beta = parameter["beta"]
self.ptx_width = self.beta*parameter["min_tx_size"] self.ptx_width = self.beta*parameter["min_tx_size"]
self.width = self.bitcell.width self.width = self.bitcell.width
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br self.bitcell_br = bitcell_br
# Creates the netlist and layout
# Since it has variable height, it is not a pgate.
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
self.DRC_LVS()
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
@ -40,7 +51,6 @@ class precharge(pgate.pgate):
self.route_vdd_rail() self.route_vdd_rail()
self.route_bitlines() self.route_bitlines()
self.connect_to_bitlines() self.connect_to_bitlines()
self.DRC_LVS()
def add_pins(self): def add_pins(self):
self.add_pin_list(["bl", "br", "en_bar", "vdd"]) self.add_pin_list(["bl", "br", "en_bar", "vdd"])
@ -75,7 +85,7 @@ class precharge(pgate.pgate):
self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos]) self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos])
# Add vdd pin above the transistor # Add vdd pin above the transistor
self.add_power_pin("vdd", pmos_pin.center(), rotate=0) self.add_power_pin("vdd", pmos_pin.center(), vertical=True)
def create_ptx(self): def create_ptx(self):
@ -149,9 +159,8 @@ class precharge(pgate.pgate):
# adds the en contact to connect the gates to the en rail on metal1 # adds the en contact to connect the gates to the en rail on metal1
offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space) offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=offset, offset=offset)
rotate=90)
# adds the en rail on metal1 # adds the en rail on metal1
self.add_layout_pin_segment_center(text="en_bar", self.add_layout_pin_segment_center(text="en_bar",
@ -168,10 +177,10 @@ class precharge(pgate.pgate):
# adds the contact from active to metal1 # adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active")) + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active"))
self.add_contact_center(layers=("active", "contact", "metal1"), self.add_via_center(layers=("active", "contact", "metal1"),
offset=well_contact_pos, offset=well_contact_pos,
implant_type="n", implant_type="n",
well_type="n") well_type="n")
# leave an extra pitch for the height # leave an extra pitch for the height
self.height = well_contact_pos.y + contact.well.height + self.m1_pitch self.height = well_contact_pos.y + contact.well.height + self.m1_pitch
@ -225,16 +234,20 @@ class precharge(pgate.pgate):
lower_pin = self.lower_pmos_inst.get_pin("S") lower_pin = self.lower_pmos_inst.get_pin("S")
# BL goes up to M2 at the transistor # BL goes up to M2 at the transistor
self.bl_contact=self.add_contact_center(layers=stack, self.bl_contact=self.add_via_center(layers=stack,
offset=upper_pin.center()) offset=upper_pin.center(),
self.add_contact_center(layers=stack, directions=("V","V"))
offset=lower_pin.center()) self.add_via_center(layers=stack,
offset=lower_pin.center(),
directions=("V","V"))
# BR routes over on M1 first # BR routes over on M1 first
self.add_contact_center(layers=stack, self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), upper_pin.cy())) offset = vector(self.br_pin.cx(), upper_pin.cy()),
self.add_contact_center(layers=stack, directions=("V","V"))
offset = vector(self.br_pin.cx(), lower_pin.cy())) self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), lower_pin.cy()),
directions=("V","V"))
def connect_pmos_m1(self, pmos_pin, bit_pin): def connect_pmos_m1(self, pmos_pin, bit_pin):
""" """

View File

@ -0,0 +1,227 @@
# 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 contact
import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from math import ceil
from globals import OPTS
from utils import round_to_grid
import logical_effort
from sram_factory import factory
class ptristate_inv(pgate.pgate):
"""
ptristate generates gds of a parametrically sized tristate inverter.
There is some flexibility in the size, but we do not allow multiple fingers
to fit in the cell height.
"""
def __init__(self, name, size=1, height=None):
debug.info(2, "creating ptristate inv {0} with size of {1}".format(name, size))
self.add_comment("size: {}".format(size))
# We are 2x since there are two series devices
self.size = 2*size
self.nmos_size = size
self.beta = parameter["beta"]
self.pmos_size = self.beta*size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self):
""" Calls all functions related to the generation of the netlist """
self.add_pins()
self.add_ptx()
self.create_ptx()
def create_layout(self):
""" Calls all functions related to the generation of the layout """
self.setup_layout_constants()
self.route_supply_rails()
self.place_ptx()
self.add_well_contacts()
self.extend_wells(self.well_pos)
self.connect_rails()
self.route_inputs()
self.route_outputs()
def add_pins(self):
""" Adds pins for spice netlist """
self.add_pin_list(["in", "out", "en", "en_bar", "vdd", "gnd"])
def setup_layout_constants(self):
"""
Pre-compute some handy layout parameters.
"""
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + drc("well_enclosure_active")
# Add an extra space because we route the output on the right of the S/D
self.width = self.well_width + 0.5*self.m1_space
# Height is an input parameter, so it is not recomputed.
# Make sure we can put a well above and below
self.top_bottom_space = max(contact.well.width, contact.well.height)
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",
width=self.nmos_width,
mults=1,
tx_type="nmos")
self.add_mod(self.nmos)
self.pmos = factory.create(module_type="ptx",
width=self.pmos_width,
mults=1,
tx_type="pmos")
self.add_mod(self.pmos)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
width=self.width)
def create_ptx(self):
"""
Create the PMOS and NMOS netlist.
"""
# These are the inverter PMOS/NMOS
self.pmos1_inst=self.add_inst(name="ptri_pmos1", mod=self.pmos)
self.connect_inst(["vdd", "in", "n1", "vdd"])
self.nmos1_inst=self.add_inst(name="ptri_nmos1", mod=self.nmos)
self.connect_inst(["gnd", "in", "n2", "gnd"])
# These are the tristate PMOS/NMOS
self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos)
self.connect_inst(["out", "en_bar", "n1", "vdd"])
self.nmos2_inst=self.add_inst(name="ptri_nmos2", mod=self.nmos)
self.connect_inst(["out", "en", "n2", "gnd"])
def place_ptx(self):
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos_yoff = self.height - self.pmos.active_height - self.top_bottom_space - 0.5*contact.well.height
nmos_yoff = self.top_bottom_space + 0.5*contact.well.height
# Tristate transistors
pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff)
self.pmos1_inst.place(pmos1_pos)
nmos1_pos = vector(self.pmos.active_offset.x, nmos_yoff)
self.nmos1_inst.place(nmos1_pos)
# Inverter transistors
self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst.place(self.pmos2_pos)
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0, 0.5*(pmos_yoff + nmos_yoff + self.nmos.height))
# This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy())
def route_inputs(self):
""" Route the gates """
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.output_pos.y, "in", position="farleft")
self.route_single_gate(self.pmos2_inst, "en_bar", position="left")
self.route_single_gate(self.nmos2_inst, "en", position="left")
def route_outputs(self):
""" Route the output (drains) together. """
nmos_drain_pin = self.nmos2_inst.get_pin("D")
pmos_drain_pin = self.pmos2_inst.get_pin("D")
nmos_drain_pos = nmos_drain_pin.lr()
pmos_drain_pos = pmos_drain_pin.ur()
self.add_layout_pin(text="out",
layer="metal1",
offset=nmos_drain_pos,
height=pmos_drain_pos.y-nmos_drain_pos.y)
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies AFTER the wells are created """
layer_stack = ("active", "contact", "metal1")
drain_pos = self.nmos1_inst.get_pin("S").center()
vdd_pos = self.get_pin("vdd").center()
self.nwell_contact=self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,vdd_pos.y),
implant_type="n",
well_type="n")
gnd_pos = self.get_pin("gnd").center()
self.pwell_contact=self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,gnd_pos.y),
implant_type="p",
well_type="p")
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
def analytical_delay(self, corner, slew, load=0.0):
from tech import spice
r = spice["min_tx_r"]
c_para = spice["min_tx_drain_c"]
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
return total_power
def input_load(self):
return 9*spice["min_tx_gate_c"]

View File

@ -1,3 +1,10 @@
# 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 design
import debug import debug
from tech import drc, spice from tech import drc, spice
@ -28,9 +35,8 @@ class ptx(design.design):
name += "_c{}".format(num_contacts) name += "_c{}".format(num_contacts)
# replace periods with underscore for newer spice compatibility # replace periods with underscore for newer spice compatibility
name=name.replace('.','_') name=name.replace('.','_')
debug.info(3, "creating ptx {0}".format(name))
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(3, "create ptx2 structure {0}".format(name))
self.tx_type = tx_type self.tx_type = tx_type
self.mults = mults self.mults = mults
@ -39,6 +45,8 @@ class ptx(design.design):
self.connect_poly = connect_poly self.connect_poly = connect_poly
self.num_contacts = num_contacts self.num_contacts = num_contacts
# Do NOT create the netlist and layout (not a pgate)
# Since it has variable height, it is not a pgate.
self.create_netlist() self.create_netlist()
# We must always create ptx layout for pbitcell # We must always create ptx layout for pbitcell
# some transistor sizes in other netlist depend on pbitcell # some transistor sizes in other netlist depend on pbitcell
@ -326,11 +334,12 @@ class ptx(design.design):
[source_positions,drain_positions] = self.get_contact_positions() [source_positions,drain_positions] = self.get_contact_positions()
for pos in source_positions: for pos in source_positions:
contact=self.add_contact_center(layers=("active", "contact", "metal1"), contact=self.add_via_center(layers=("active", "contact", "metal1"),
offset=pos, offset=pos,
size=(1, self.num_contacts), size=(1, self.num_contacts),
implant_type=self.implant_type, directions=("H","V"),
well_type=self.well_type) implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text="S", self.add_layout_pin_rect_center(text="S",
layer="metal1", layer="metal1",
offset=pos, offset=pos,
@ -339,11 +348,12 @@ class ptx(design.design):
for pos in drain_positions: for pos in drain_positions:
contact=self.add_contact_center(layers=("active", "contact", "metal1"), contact=self.add_via_center(layers=("active", "contact", "metal1"),
offset=pos, offset=pos,
size=(1, self.num_contacts), size=(1, self.num_contacts),
implant_type=self.implant_type, directions=("H","V"),
well_type=self.well_type) implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text="D", self.add_layout_pin_rect_center(text="D",
layer="metal1", layer="metal1",
offset=pos, offset=pos,

View File

@ -1,12 +1,20 @@
import design # 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 pgate
import debug import debug
from tech import drc from tech import drc
from vector import vector from vector import vector
import contact import contact
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
import logical_effort
class single_level_column_mux(design.design): class single_level_column_mux(pgate.pgate):
""" """
This module implements the columnmux bitline cell used in the design. This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell with the given integer size relative Creates a single columnmux cell with the given integer size relative
@ -14,18 +22,14 @@ class single_level_column_mux(design.design):
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
""" """
def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"): def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"):
debug.info(2, "creating single column mux cell: {0}".format(name))
self.tx_size = int(tx_size) self.tx_size = int(tx_size)
design.design.__init__(self, name)
debug.info(2, "create single column mux cell: {0}".format(name))
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br self.bitcell_br = bitcell_br
self.create_netlist() pgate.pgate.__init__(self, name)
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self): def create_netlist(self):
self.add_modules() self.add_modules()
@ -126,13 +130,18 @@ class single_level_column_mux(design.design):
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1","via1","metal2"),
offset=bl_pin.bc()) offset=bl_pin.bc(),
directions=("V","V"))
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1","via1","metal2"),
offset=br_out_pin.uc()) offset=br_out_pin.uc(),
directions=("V","V"))
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1","via1","metal2"),
offset=nmos_upper_s_pin.center()) offset=nmos_upper_s_pin.center(),
directions=("V","V"))
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1","via1","metal2"),
offset=nmos_lower_d_pin.center()) offset=nmos_lower_d_pin.center(),
directions=("V","V"))
# bl -> nmos_upper/D on metal1 # bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2 # bl_out -> nmos_upper/S on metal2
@ -180,5 +189,9 @@ class single_level_column_mux(design.design):
width=self.bitcell.width, width=self.bitcell.width,
height=self.height) height=self.height)
def analytical_delay(self, corner, slew, load):
"""Returns relative delay that the column mux. Difficult to convert to LE model."""
parasitic_delay = 1
cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect.
return logical_effort.logical_effort('column_mux', self.tx_size, cin, load, parasitic_delay, False)

View File

@ -1,3 +1,10 @@
# 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.
#
from enum import Enum from enum import Enum
from vector3d import vector3d from vector3d import vector3d

View File

@ -1,3 +1,10 @@
# 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 numpy as np import numpy as np
import string import string
import debug import debug

View File

@ -1,3 +1,10 @@
# 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.
#
class grid_cell: class grid_cell:
""" """
A single cell that can be occupied in a given layer, blocked, A single cell that can be occupied in a given layer, blocked,

View File

@ -1,3 +1,10 @@
# 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 debug import debug
from vector3d import vector3d from vector3d import vector3d
from itertools import tee from itertools import tee

View File

@ -1,3 +1,10 @@
# 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.
#
""" """
Some utility functions for sets of grid cells. Some utility functions for sets of grid cells.
""" """

View File

@ -1,3 +1,10 @@
# 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.
#
from direction import direction from direction import direction
from pin_layout import pin_layout from pin_layout import pin_layout
from vector3d import vector3d from vector3d import vector3d
@ -25,7 +32,7 @@ class pin_group:
# This is a list because we can have a pin group of disconnected sets of pins # This is a list because we can have a pin group of disconnected sets of pins
# and these are represented by separate lists # and these are represented by separate lists
self.pins = [set(irredundant_pin_set)] self.pins = set(irredundant_pin_set)
self.router = router self.router = router
# These are the corresponding pin grids for each pin group. # These are the corresponding pin grids for each pin group.
@ -55,7 +62,7 @@ class pin_group:
total_string += grids_string total_string += grids_string
if self.enclosed: if self.enclosed:
enlosure_string = "\n enclose={}".format(self.enclosures) enclosure_string = "\n enclose={}".format(self.enclosures)
total_string += enclosure_string total_string += enclosure_string
total_string += ")" total_string += ")"
@ -74,25 +81,6 @@ class pin_group:
def is_routed(self): def is_routed(self):
return self.routed return self.routed
def pins_enclosed(self):
"""
Check if all of the pin shapes are enclosed.
Does not check if the DRC is correct, but just touching.
"""
for pin_list in self.pins:
pin_is_enclosed=False
for pin in pin_list:
if pin_is_enclosed:
break
for encosure in self.enclosures:
if pin.overlaps(enclosure):
pin_is_enclosed=True
break
else:
return False
return True
def remove_redundant_shapes(self, pin_list): def remove_redundant_shapes(self, pin_list):
""" """
Remove any pin layout that is contained within another. Remove any pin layout that is contained within another.
@ -135,7 +123,6 @@ class pin_group:
return new_pin_list return new_pin_list
# FIXME: This relies on some technology parameters from router which is not clean.
def compute_enclosures(self): def compute_enclosures(self):
""" """
Find the minimum rectangle enclosures of the given tracks. Find the minimum rectangle enclosures of the given tracks.
@ -406,8 +393,8 @@ class pin_group:
def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST): def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST):
""" """
This encloses a single pin component with a rectangle This encloses a single pin component with a rectangle
starting with the seed and expanding right until blocked starting with the seed and expanding dir1 until blocked
and then up until blocked. and then dir2 until blocked.
dir1 and dir2 should be two orthogonal directions. dir1 and dir2 should be two orthogonal directions.
""" """
@ -458,61 +445,86 @@ class pin_group:
# Compute the enclosure pin_layout list of the set of tracks # Compute the enclosure pin_layout list of the set of tracks
self.enclosures = self.compute_enclosures() self.enclosures = self.compute_enclosures()
for pin_list in self.pins: # Find a connector to every pin and add it to the enclosures
for pin in pin_list: for pin in self.pins:
# If it is contained, it won't need a connector # If it is contained, it won't need a connector
if pin.contained_by_any(self.enclosures): if pin.contained_by_any(self.enclosures):
continue continue
# Find a connector in the cardinal directions # Find a connector in the cardinal directions
# If there is overlap, but it isn't contained, these could all be None # If there is overlap, but it isn't contained, these could all be None
# These could also be none if the pin is diagonal from the enclosure # These could also be none if the pin is diagonal from the enclosure
left_connector = self.find_left_connector(pin, self.enclosures) left_connector = self.find_left_connector(pin, self.enclosures)
right_connector = self.find_right_connector(pin, self.enclosures) right_connector = self.find_right_connector(pin, self.enclosures)
above_connector = self.find_above_connector(pin, self.enclosures) above_connector = self.find_above_connector(pin, self.enclosures)
below_connector = self.find_below_connector(pin, self.enclosures) below_connector = self.find_below_connector(pin, self.enclosures)
connector_list = [left_connector, right_connector, above_connector, below_connector] connector_list = [left_connector, right_connector, above_connector, below_connector]
filtered_list = list(filter(lambda x: x!=None, connector_list)) filtered_list = list(filter(lambda x: x!=None, connector_list))
if (len(filtered_list)>0): if (len(filtered_list)>0):
import copy import copy
bbox_connector = copy.copy(pin) bbox_connector = copy.copy(pin)
bbox_connector.bbox(filtered_list) bbox_connector.bbox(filtered_list)
self.enclosures.append(bbox_connector) self.enclosures.append(bbox_connector)
# Now, make sure each pin touches an enclosure. If not, add another (diagonal) connector. # Now, make sure each pin touches an enclosure. If not, add another (diagonal) connector.
# This could only happen when there was no enclosure in any cardinal direction from a pin # This could only happen when there was no enclosure in any cardinal direction from a pin
for pin_list in self.pins: if not self.overlap_any_shape(self.pins, self.enclosures):
if not self.overlap_any_shape(pin_list, self.enclosures): connector = self.find_smallest_connector(pin_list, self.enclosures)
connector = self.find_smallest_connector(pin_list, self.enclosures) if connector==None:
if connector==None: debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures))
debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures)) self.router.write_debug_gds("no_connector.gds")
self.router.write_debug_gds("no_connector.gds") self.enclosures.append(connector)
self.enclosures.append(connector)
# At this point, the pins are overlapping, but there might be more than one!
overlap_set = set()
for pin in self.pins:
overlap_set.update(self.transitive_overlap(pin, self.enclosures))
# Use the new enclosures and recompute the grids that correspond to them
if len(overlap_set)<len(self.enclosures):
self.enclosures = overlap_set
self.grids=set()
# Also update the grid locations with the new (possibly pruned) enclosures
for enclosure in self.enclosures:
(sufficient,insufficient) = self.router.convert_pin_to_tracks(self.name,enclosure)
self.grids.update(sufficient)
debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name, debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
self.pins, self.pins,
self.grids, self.grids,
self.enclosures)) self.enclosures))
def combine_groups(self, pg1, pg2): def transitive_overlap(self, shape, shape_list):
""" """
Combine two pin groups into one. Given shape, find the elements in shape_list that overlap transitively.
I.e. if shape overlaps A and A overlaps B, return both A and B.
""" """
self.pins = [*pg1.pins, *pg2.pins] # Join the two lists of pins
self.grids = pg1.grids | pg2.grids # OR the set of grid locations
self.secondary_grids = pg1.secondary_grids | pg2.secondary_grids
def add_group(self, pg): augmented_shape_list = set(shape_list)
""" old_connected_set = set()
Combine the pin group into this one. This will add to the first item in the pins connected_set = set([shape])
so this should be used before there are disconnected pins. # Repeat as long as we expand the set
""" while len(connected_set) > len(old_connected_set):
debug.check(len(self.pins)==1,"Don't know which group to add pins to.") old_connected_set = connected_set
self.pins[0].update(*pg.pins) # Join the two lists of pins connected_set = set([shape])
self.grids |= pg.grids # OR the set of grid locations for old_shape in old_connected_set:
self.secondary_grids |= pg.secondary_grids for cur_shape in augmented_shape_list:
if old_shape.overlaps(cur_shape):
connected_set.add(cur_shape)
# Remove the original shape
connected_set.remove(shape)
# if len(connected_set)<len(shape_list):
# import pprint
# print("S: ",shape)
# pprint.pprint(shape_list)
# pprint.pprint(connected_set)
return connected_set
def add_enclosure(self, cell): def add_enclosure(self, cell):
""" """
@ -579,17 +591,16 @@ class pin_group:
partial_set = set() partial_set = set()
blockage_set = set() blockage_set = set()
for pin_list in self.pins: for pin in self.pins:
for pin in pin_list: debug.info(2," Converting {0}".format(pin))
debug.info(2," Converting {0}".format(pin)) # Determine which tracks the pin overlaps
# Determine which tracks the pin overlaps (sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin)
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin) pin_set.update(sufficient)
pin_set.update(sufficient) partial_set.update(insufficient)
partial_set.update(insufficient)
# Blockages will be a super-set of pins since it uses the inflated pin shape. # Blockages will be a super-set of pins since it uses the inflated pin shape.
blockage_in_tracks = self.router.convert_blockage(pin) blockage_in_tracks = self.router.convert_blockage(pin)
blockage_set.update(blockage_in_tracks) blockage_set.update(blockage_in_tracks)
# If we have a blockage, we must remove the grids # If we have a blockage, we must remove the grids
# Remember, this excludes the pin blockages already # Remember, this excludes the pin blockages already
@ -610,13 +621,12 @@ class pin_group:
if (len(pin_set)==0 and len(partial_set)==0 and len(blockage_set)==0): if (len(pin_set)==0 and len(partial_set)==0 and len(blockage_set)==0):
#debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins)) #debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
for pin_list in self.pins: for pin in self.pins:
for pin in pin_list: debug.warning(" Expanding conversion {0}".format(pin))
debug.warning(" Expanding conversion {0}".format(pin)) # Determine which tracks the pin overlaps
# Determine which tracks the pin overlaps (sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1) pin_set.update(sufficient)
pin_set.update(sufficient) partial_set.update(insufficient)
partial_set.update(insufficient)
if len(pin_set)==0 and len(partial_set)==0: if len(pin_set)==0 and len(partial_set)==0:
debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins)) debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins))
@ -630,62 +640,5 @@ class pin_group:
debug.info(2," pins {}".format(self.grids)) debug.info(2," pins {}".format(self.grids))
debug.info(2," secondary {}".format(self.secondary_grids)) debug.info(2," secondary {}".format(self.secondary_grids))
# def recurse_simple_overlap_enclosure(self, start_set, direct):
# """
# Recursive function to return set of tracks that connects to
# the actual supply rail wire in a given direction (or terminating
# when any track is no longer in the supply rail.
# """
# next_set = grid_utils.expand_border(start_set, direct)
# supply_tracks = self.router.supply_rail_tracks[self.name]
# supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
# supply_overlap = next_set & supply_tracks
# wire_overlap = next_set & supply_wire_tracks
# # If the rail overlap is the same, we are done, since we connected to the actual wire
# if len(wire_overlap)==len(start_set):
# new_set = start_set | wire_overlap
# # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region
# elif len(supply_overlap)==len(start_set):
# recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
# new_set = start_set | supply_overlap | recurse_set
# else:
# # If we got no next set, we are done, can't expand!
# new_set = set()
# return new_set
# def create_simple_overlap_enclosure(self, start_set):
# """
# This takes a set of tracks that overlap a supply rail and creates an enclosure
# that is ensured to overlap the supply rail wire.
# It then adds rectangle(s) for the enclosure.
# """
# additional_set = set()
# # Check the layer of any element in the pin to determine which direction to route it
# e = next(iter(start_set))
# new_set = start_set.copy()
# if e.z==0:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
# if not new_set:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
# else:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
# if not new_set:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST)
# # Expand the pin grid set to include some extra grids that connect the supply rail
# self.grids.update(new_set)
# # Block the grids
# self.blockages.update(new_set)
# # Add the polygon enclosures and set this pin group as routed
# self.set_routed()
# self.enclosures = self.compute_enclosures()

View File

@ -1,3 +1,10 @@
# 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 sys import sys
import gdsMill import gdsMill
from tech import drc,GDS from tech import drc,GDS
@ -187,60 +194,64 @@ class router(router_tech):
start_time = datetime.now() start_time = datetime.now()
self.enclose_pins() self.enclose_pins()
print_time("Enclosing pins",datetime.now(), start_time, 4) print_time("Enclosing pins",datetime.now(), start_time, 4)
# MRG: Removing this code for now. The later compute enclosure code
# assumes that all pins are touching and this may produce sets of pins
# that are not connected.
# def combine_adjacent_pins(self, pin_name):
# """
# Find pins that have adjacent routing tracks and merge them into a
# single pin_group. The pins themselves may not be touching, but
# enclose_pins in the next step will ensure they are touching.
# """
# debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
# # Find all adjacencies
# adjacent_pins = {}
# for index1,pg1 in enumerate(self.pin_groups[pin_name]):
# for index2,pg2 in enumerate(self.pin_groups[pin_name]):
# # Cannot combine with yourself, also don't repeat
# if index1<=index2:
# continue
# # Combine if at least 1 grid cell is adjacent
# if pg1.adjacent(pg2):
# if not index1 in adjacent_pins:
# adjacent_pins[index1] = set([index2])
# else:
# adjacent_pins[index1].add(index2)
# # Make a list of indices to ensure every group gets in the new set
# all_indices = set([x for x in range(len(self.pin_groups[pin_name]))])
def combine_adjacent_pins(self, pin_name): # # Now reconstruct the new groups
""" # new_pin_groups = []
Find pins that have adjacent routing tracks and merge them into a # for index1,index2_set in adjacent_pins.items():
single pin_group. The pins themselves may not be touching, but # # Remove the indices if they are added to the new set
enclose_pins in the next step will ensure they are touching. # all_indices.discard(index1)
""" # all_indices.difference_update(index2_set)
debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
# Find all adjacencies
adjacent_pins = {}
for index1,pg1 in enumerate(self.pin_groups[pin_name]):
for index2,pg2 in enumerate(self.pin_groups[pin_name]):
# Cannot combine with yourself, also don't repeat
if index1<=index2:
continue
# Combine if at least 1 grid cell is adjacent
if pg1.adjacent(pg2):
if not index1 in adjacent_pins:
adjacent_pins[index1] = set([index2])
else:
adjacent_pins[index1].add(index2)
# Make a list of indices to ensure every group gets in the new set # # Create the combined group starting with the first item
all_indices = set([x for x in range(len(self.pin_groups[pin_name]))]) # combined = self.pin_groups[pin_name][index1]
# # Add all of the other items that overlapped
# for index2 in index2_set:
# pg = self.pin_groups[pin_name][index2]
# combined.add_group(pg)
# debug.info(3,"Combining {0} {1}:".format(pin_name, index2))
# debug.info(3, " {0}\n {1}".format(combined.pins, pg.pins))
# debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids))
# new_pin_groups.append(combined)
# # Add the pin groups that weren't added to the new set
# for index in all_indices:
# new_pin_groups.append(self.pin_groups[pin_name][index])
# old_size = len(self.pin_groups[pin_name])
# # Use the new pin group!
# self.pin_groups[pin_name] = new_pin_groups
# removed_pairs = old_size - len(new_pin_groups)
# debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name))
# Now reconstruct the new groups # return removed_pairs
new_pin_groups = []
for index1,index2_set in adjacent_pins.items():
# Remove the indices if they are added to the new set
all_indices.discard(index1)
all_indices.difference_update(index2_set)
# Create the combined group starting with the first item
combined = self.pin_groups[pin_name][index1]
# Add all of the other items that overlapped
for index2 in index2_set:
pg = self.pin_groups[pin_name][index2]
combined.add_group(pg)
debug.info(3,"Combining {0} {1}:".format(pin_name, index2))
debug.info(3, " {0}\n {1}".format(combined.pins, pg.pins))
debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids))
new_pin_groups.append(combined)
# Add the pin groups that weren't added to the new set
for index in all_indices:
new_pin_groups.append(self.pin_groups[pin_name][index])
old_size = len(self.pin_groups[pin_name])
# Use the new pin group!
self.pin_groups[pin_name] = new_pin_groups
removed_pairs = old_size - len(new_pin_groups)
debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name))
return removed_pairs
def separate_adjacent_pins(self, separation): def separate_adjacent_pins(self, separation):
@ -748,44 +759,10 @@ class router(router_tech):
if gid not in group_map: if gid not in group_map:
group_map[gid] = pin_group(name=pin_name, pin_set=[], router=self) group_map[gid] = pin_group(name=pin_name, pin_set=[], router=self)
# We always add it to the first set since they are touching # We always add it to the first set since they are touching
group_map[gid].pins[0].add(pin) group_map[gid].pins.add(pin)
self.pin_groups[pin_name] = list(group_map.values()) self.pin_groups[pin_name] = list(group_map.values())
# This is the old O(n^2) implementation
# def analyze_pins(self, pin_name):
# """
# Analyze the shapes of a pin and combine them into pin_groups which are connected.
# """
# debug.info(2,"Analyzing pin groups for {}.".format(pin_name))
# pin_set = self.pins[pin_name]
# # Put each pin in an equivalence class of it's own
# equiv_classes = [set([x]) for x in pin_set]
# def combine_classes(equiv_classes):
# for class1 in equiv_classes:
# for class2 in equiv_classes:
# if class1 == class2:
# continue
# # Compare each pin in each class,
# # and if any overlap, update equiv_classes to include the combined the class
# for p1 in class1:
# for p2 in class2:
# if p1.overlaps(p2):
# combined_class = class1 | class2
# equiv_classes.remove(class1)
# equiv_classes.remove(class2)
# equiv_classes.append(combined_class)
# return(equiv_classes)
# return(equiv_classes)
# old_length = math.inf
# while (len(equiv_classes)<old_length):
# old_length = len(equiv_classes)
# equiv_classes = combine_classes(equiv_classes)
# self.pin_groups[pin_name] = [pin_group(name=pin_name, pin_set=x, router=self) for x in equiv_classes]
def convert_pins(self, pin_name): def convert_pins(self, pin_name):
""" """

View File

@ -1,3 +1,10 @@
# 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.
#
from tech import drc,layer from tech import drc,layer
from contact import contact from contact import contact
from pin_group import pin_group from pin_group import pin_group

View File

@ -1,3 +1,10 @@
# 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.
#
from itertools import tee from itertools import tee
import debug import debug
from heapq import heappush,heappop from heapq import heappush,heappop

View File

@ -1,3 +1,10 @@
# 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 gdsMill import gdsMill
import tech import tech
from contact import contact from contact import contact

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