mirror of https://github.com/VLSIDA/OpenRAM.git
Merge remote-tracking branch 'origin/dev'
This commit is contained in:
commit
76e2ab88fe
|
|
@ -8,6 +8,8 @@ omit =
|
|||
*/tests/*
|
||||
# ignore the debug utilities
|
||||
debug.py
|
||||
# ignore the no DRC/LVS tool options
|
||||
none.py
|
||||
[paths]
|
||||
source =
|
||||
../..
|
||||
|
|
@ -21,6 +23,8 @@ source =
|
|||
exclude_lines =
|
||||
pragma: no cover
|
||||
def __repr__
|
||||
def __str__
|
||||
class gdsStreamer
|
||||
except Exception
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
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
|
||||
(acting for and on behalf of Oklahoma State University)
|
||||
All rights reserved.
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
[](./LICENSE)
|
||||
|
||||
Master:
|
||||
[](https://github.com/VLSIDA/OpenRAM/commits/master)
|
||||

|
||||
[](https://github.com/VLSIDA/OpenRAM/commits/master)
|
||||

|
||||
[](https://github.com/VLSIDA/OpenRAM/archive/master.zip)
|
||||
|
||||
Dev:
|
||||
[](https://github.com/VLSIDA/OpenRAM/commits/dev)
|
||||

|
||||
[](https://github.com/VLSIDA/OpenRAM/commits/dev)
|
||||

|
||||
[](https://github.com/VLSIDA/OpenRAM/archive/dev.zip)
|
||||
|
||||
An open-source static random access memory (SRAM) compiler.
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import utils
|
||||
|
|
@ -9,44 +16,36 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
"""
|
||||
Object for a contact shape with its conductor enclosures.
|
||||
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
|
||||
have extension on two or four sides.
|
||||
This class has enclosure on two or four sides of the contact.
|
||||
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
|
||||
necessary to import layouts into Magic which requires the select to be in the same GDS
|
||||
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
|
||||
|
||||
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)
|
||||
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.dimensions = dimensions
|
||||
self.directions = directions
|
||||
self.offset = vector(0,0)
|
||||
self.implant_type = implant_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()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.setup_layers()
|
||||
self.setup_layout_constants()
|
||||
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.")
|
||||
|
||||
def setup_layers(self):
|
||||
""" Locally assign the layer names. """
|
||||
|
||||
(first_layer, via_layer, second_layer) = self.layer_stack
|
||||
self.first_layer_name = first_layer
|
||||
self.via_layer_name = via_layer
|
||||
|
|
@ -75,37 +76,58 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
self.second_layer_name = second_layer
|
||||
|
||||
def setup_layout_constants(self):
|
||||
""" Determine the design rules for the enclosure layers """
|
||||
|
||||
self.contact_width = drc("minwidth_{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_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
|
||||
|
||||
# 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_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_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_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_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,
|
||||
second_layer_enclosure)
|
||||
self.second_layer_vertical_enclosure = max(utils.ceil((second_layer_minarea
|
||||
/ (self.contact_array_width + 2*self.second_layer_horizontal_enclosure)
|
||||
- self.contact_array_height)/2),
|
||||
(second_layer_minwidth - self.contact_array_height)/2,
|
||||
second_layer_extend)
|
||||
# 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[0] == "V":
|
||||
self.first_layer_horizontal_enclosure = max(first_layer_enclosure,
|
||||
(first_layer_minwidth - self.contact_array_width)/2)
|
||||
self.first_layer_vertical_enclosure = max(first_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):
|
||||
""" Create the contact array at the origin"""
|
||||
|
|
@ -169,10 +191,10 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
from sram_factory import factory
|
||||
# This is not instantiated and used for calculations only.
|
||||
# These are static 1x1 contacts to reuse in all the design modules.
|
||||
well = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"))
|
||||
active = factory.create(module_type="contact", layer_stack=("active", "contact", "poly"))
|
||||
poly = factory.create(module_type="contact", layer_stack=("poly", "contact", "metal1"))
|
||||
m1m2 = factory.create(module_type="contact", layer_stack=("metal1", "via1", "metal2"))
|
||||
m2m3 = factory.create(module_type="contact", layer_stack=("metal2", "via2", "metal3"))
|
||||
m3m4 = factory.create(module_type="contact", layer_stack=("metal3", "via3", "metal4"))
|
||||
well = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"), directions=("H","V"))
|
||||
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"), directions=("V","H"))
|
||||
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"), directions=("V","H"))
|
||||
m3m4 = factory.create(module_type="contact", layer_stack=("metal3", "via3", "metal4"), directions=("H","V"))
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import contact
|
||||
import globals
|
||||
|
|
@ -95,13 +102,3 @@ class design(hierarchy_design):
|
|||
total_module_power += inst.mod.analytical_power(corner, load)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
"""
|
||||
|
|
@ -371,8 +378,8 @@ class rectangle(geometry):
|
|||
|
||||
def __str__(self):
|
||||
""" 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):
|
||||
""" 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) + " )"
|
||||
|
|
|
|||
|
|
@ -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_spice
|
||||
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.name = name
|
||||
hierarchy_layout.layout.__init__(self, name)
|
||||
hierarchy_spice.spice.__init__(self, name)
|
||||
hierarchy_layout.layout.__init__(self, name)
|
||||
|
||||
|
||||
def get_layout_pins(self,inst):
|
||||
|
|
@ -100,7 +107,14 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
|
||||
def __str__(self):
|
||||
""" 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):
|
||||
""" override print function output """
|
||||
|
|
|
|||
|
|
@ -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 geometry
|
||||
import gdsMill
|
||||
|
|
@ -355,75 +362,59 @@ class layout():
|
|||
layer_stack=layers,
|
||||
position_list=coordinates)
|
||||
|
||||
def add_contact(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None):
|
||||
""" This is just an alias for a via."""
|
||||
return self.add_via(layers=layers,
|
||||
offset=offset,
|
||||
size=size,
|
||||
mirror=mirror,
|
||||
rotate=rotate,
|
||||
implant_type=implant_type,
|
||||
well_type=well_type)
|
||||
def get_preferred_direction(self, layer):
|
||||
""" Return the preferred routing directions """
|
||||
if layer in ["metal1", "metal3", "metal5"]:
|
||||
return "H"
|
||||
elif layer in ["active", "poly", "metal2", "metal4"]:
|
||||
return "V"
|
||||
else:
|
||||
return "N"
|
||||
|
||||
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."""
|
||||
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):
|
||||
|
||||
def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None):
|
||||
""" 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
|
||||
via = factory.create(module_type="contact",
|
||||
layer_stack=layers,
|
||||
dimensions=size,
|
||||
directions=directions,
|
||||
implant_type=implant_type,
|
||||
well_type=well_type)
|
||||
self.add_mod(via)
|
||||
inst=self.add_inst(name=via.name,
|
||||
mod=via,
|
||||
offset=offset,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
offset=offset)
|
||||
# We don't model the logical connectivity of wires/paths
|
||||
self.connect_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. """
|
||||
|
||||
if directions==None:
|
||||
directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2]))
|
||||
|
||||
from sram_factory import factory
|
||||
via = factory.create(module_type="contact",
|
||||
layer_stack=layers,
|
||||
dimensions=size,
|
||||
directions=directions,
|
||||
implant_type=implant_type,
|
||||
well_type=well_type)
|
||||
height = via.height
|
||||
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)
|
||||
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)
|
||||
|
||||
corrected_offset = offset + vector(-0.5*width,-0.5*height)
|
||||
|
||||
#print(rotate,offset,"->",corrected_offset)
|
||||
self.add_mod(via)
|
||||
inst=self.add_inst(name=via.name,
|
||||
mod=via,
|
||||
offset=corrected_offset,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
offset=corrected_offset)
|
||||
# We don't model the logical connectivity of wires/paths
|
||||
self.connect_inst([])
|
||||
return inst
|
||||
|
|
@ -748,8 +739,7 @@ class layout():
|
|||
mid = vector(trunk_offset.x, pin.center().y)
|
||||
self.add_path(layer_stack[0], [pin.center(), mid])
|
||||
self.add_via_center(layers=layer_stack,
|
||||
offset=mid,
|
||||
rotate=90)
|
||||
offset=mid)
|
||||
|
||||
|
||||
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.
|
||||
The starting layer is specified to determine which vias are needed.
|
||||
"""
|
||||
|
||||
if vertical:
|
||||
direction=("V","V")
|
||||
else:
|
||||
direction=("H","H")
|
||||
|
||||
if start_layer=="metal1":
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=loc,
|
||||
rotate=float(rotate))
|
||||
directions=direction)
|
||||
|
||||
|
||||
if start_layer=="metal1" or start_layer=="metal2":
|
||||
via=self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=loc,
|
||||
rotate=float(rotate))
|
||||
directions=direction)
|
||||
|
||||
if start_layer=="metal3":
|
||||
self.add_layout_pin_rect_center(text=name,
|
||||
layer="metal3",
|
||||
|
|
|
|||
|
|
@ -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 re
|
||||
import os
|
||||
|
|
@ -28,7 +35,10 @@ class spice():
|
|||
# Spice format)
|
||||
self.conns = []
|
||||
# Keep track of any comments to add the the spice
|
||||
self.comments = []
|
||||
try:
|
||||
self.commments
|
||||
except:
|
||||
self.comments = []
|
||||
|
||||
self.sp_read()
|
||||
|
||||
|
|
@ -38,7 +48,12 @@ class spice():
|
|||
|
||||
def add_comment(self, comment):
|
||||
""" 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"):
|
||||
""" 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
|
||||
"""
|
||||
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 = delay * proc_mult * volt_mult * temp_mult
|
||||
delay = self.apply_corners_analytically(delay, corner)
|
||||
delay = delay * 0.001 #make the unit to ps
|
||||
|
||||
# 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
|
||||
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):
|
||||
"""Returns delay increase estimate based off process
|
||||
Currently does +/-10 for fast/slow corners."""
|
||||
|
|
|
|||
|
|
@ -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 tech
|
||||
import globals
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import GDS, drc
|
||||
from vector import vector
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import debug
|
||||
from design import design
|
||||
|
|
@ -65,7 +72,7 @@ class route(design):
|
|||
for p0,p1 in plist:
|
||||
if p0.z != p1.z: # via
|
||||
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!
|
||||
debug.error("Diagonal route! {}".format(self.path),-3)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -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 gdsMill
|
||||
import tech
|
||||
|
|
|
|||
|
|
@ -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 math
|
||||
import tech
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
class verilog:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import debug
|
||||
from wire_path import wire_path
|
||||
|
|
@ -62,18 +69,15 @@ class wire(wire_path):
|
|||
continue
|
||||
if a[1] == c[1]:
|
||||
continue
|
||||
via_offset = [offset[0] + 0.5*c_height,
|
||||
offset[1] - 0.5*c_width]
|
||||
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)]
|
||||
self.obj.add_via_center(layers=self.layer_stack,
|
||||
offset=offset)
|
||||
|
||||
|
||||
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
|
||||
for index in range(len(pl) - 1):
|
||||
if pl[index][0] != pl[index + 1][0]:
|
||||
|
|
|
|||
|
|
@ -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 layer as techlayer
|
||||
import debug
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import utils
|
||||
from tech import GDS,layer,parameter,drc
|
||||
import logical_effort
|
||||
|
||||
class bitcell(design.design):
|
||||
"""
|
||||
|
|
@ -23,19 +31,12 @@ class bitcell(design.design):
|
|||
self.width = bitcell.width
|
||||
self.height = bitcell.height
|
||||
self.pin_map = bitcell.pin_map
|
||||
|
||||
|
||||
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
|
||||
# delay of bit cell is not like a driver(from WL)
|
||||
# so the slew used should be 0
|
||||
# it should not be slew dependent?
|
||||
# because the value is there
|
||||
# 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
|
||||
|
||||
parasitic_delay = 1
|
||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
||||
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||
return logical_effort.logical_effort('bitline', size, cin, load, parasitic_delay, False)
|
||||
|
||||
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 """
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import utils
|
||||
from tech import GDS,layer,parameter,drc
|
||||
import logical_effort
|
||||
|
||||
class bitcell_1rw_1r(design.design):
|
||||
"""
|
||||
|
|
@ -25,18 +33,12 @@ class bitcell_1rw_1r(design.design):
|
|||
self.pin_map = bitcell_1rw_1r.pin_map
|
||||
|
||||
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
|
||||
# delay of bit cell is not like a driver(from WL)
|
||||
# so the slew used should be 0
|
||||
# it should not be slew dependent?
|
||||
# because the value is there
|
||||
# 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
|
||||
parasitic_delay = 1
|
||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
||||
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||
read_port_load = 0.5 #min size NMOS gate load
|
||||
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
|
||||
|
||||
|
||||
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 """
|
||||
bitcell_pins = ["bl0_{0}".format(col),
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import utils
|
||||
from tech import GDS,layer,parameter,drc
|
||||
import logical_effort
|
||||
|
||||
class bitcell_1w_1r(design.design):
|
||||
"""
|
||||
|
|
@ -25,18 +33,12 @@ class bitcell_1w_1r(design.design):
|
|||
self.pin_map = bitcell_1w_1r.pin_map
|
||||
|
||||
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
|
||||
# delay of bit cell is not like a driver(from WL)
|
||||
# so the slew used should be 0
|
||||
# it should not be slew dependent?
|
||||
# because the value is there
|
||||
# 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
|
||||
parasitic_delay = 1
|
||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
||||
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||
read_port_load = 0.5 #min size NMOS gate load
|
||||
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
|
||||
|
||||
|
||||
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 """
|
||||
bitcell_pins = ["bl0_{0}".format(col),
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
import debug
|
||||
|
|
@ -5,6 +12,7 @@ from tech import drc, parameter, spice
|
|||
from vector import vector
|
||||
from ptx import ptx
|
||||
from globals import OPTS
|
||||
import logical_effort
|
||||
|
||||
class pbitcell(design.design):
|
||||
"""
|
||||
|
|
@ -197,7 +205,7 @@ class pbitcell(design.design):
|
|||
self.read_port_spacing = self.bitline_offset + self.m2_space
|
||||
|
||||
# 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
|
||||
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()])
|
||||
|
||||
# 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.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.second_layer_width)
|
||||
self.add_path("metal1",
|
||||
[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)
|
||||
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"),
|
||||
offset=contact_offset_left,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=contact_offset_left,
|
||||
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)
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=contact_offset_right,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=contact_offset_right,
|
||||
directions=("H","H"))
|
||||
|
||||
# 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)
|
||||
|
|
@ -394,7 +407,6 @@ class pbitcell(design.design):
|
|||
self.add_layout_pin_rect_center(text=self.rw_bl_names[k],
|
||||
layer="metal2",
|
||||
offset=self.rwbl_positions[k],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
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],
|
||||
layer="metal2",
|
||||
offset=self.rwbr_positions[k],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
# 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],
|
||||
layer="metal2",
|
||||
offset=self.wbl_positions[k],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
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],
|
||||
layer="metal2",
|
||||
offset=self.wbr_positions[k],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
# 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],
|
||||
layer="metal2",
|
||||
offset=self.rbl_positions[k],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
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],
|
||||
layer="metal2",
|
||||
offset=self.rbr_positions[k],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
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
|
||||
if (k == 0) or (k == 1):
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=port_contact_offset)
|
||||
self.add_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=port_contact_offset)
|
||||
|
||||
self.add_path("poly", [gate_offset, port_contact_offset])
|
||||
self.add_path("metal1", [port_contact_offset, wl_contact_offset])
|
||||
|
||||
else:
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=port_contact_offset)
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=port_contact_offset)
|
||||
self.add_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=port_contact_offset)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=port_contact_offset)
|
||||
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=wl_contact_offset,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=wl_contact_offset,
|
||||
directions=("H","H"))
|
||||
|
||||
self.add_path("poly", [gate_offset, port_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()
|
||||
bl_offset = vector(bl_positions[k].x, port_contact_offest.y)
|
||||
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=port_contact_offest)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=port_contact_offest)
|
||||
|
||||
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()
|
||||
br_offset = vector(br_positions[k].x, port_contact_offest.y)
|
||||
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=port_contact_offest)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=port_contact_offest)
|
||||
|
||||
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())
|
||||
|
||||
for position in nmos_contact_positions:
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=position)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=position)
|
||||
|
||||
if position.x > 0:
|
||||
contact_correct = 0.5*contact.m1m2.height
|
||||
else:
|
||||
contact_correct = -0.5*contact.m1m2.height
|
||||
supply_offset = vector(position.x + contact_correct, self.gnd_position.y)
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=supply_offset,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=supply_offset,
|
||||
directions=("H","H"))
|
||||
|
||||
self.add_path("metal2", [position, supply_offset])
|
||||
|
||||
|
|
@ -712,39 +719,35 @@ class pbitcell(design.design):
|
|||
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)
|
||||
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", [mid, Q_pos])
|
||||
self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid, Q_pos])
|
||||
|
||||
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)
|
||||
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", [mid, Q_bar_pos])
|
||||
self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
|
||||
|
||||
def route_write_access(self):
|
||||
""" Routes read/write transistors to the storage component of the bitcell """
|
||||
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)
|
||||
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", [mid, Q_pos])
|
||||
self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid, Q_pos])
|
||||
|
||||
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)
|
||||
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", [mid, Q_bar_pos])
|
||||
self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
|
||||
|
||||
def route_read_access(self):
|
||||
""" Routes read access transistors to the storage component of the bitcell """
|
||||
# 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)
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=left_storage_contact,
|
||||
rotate=90)
|
||||
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_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=left_storage_contact,
|
||||
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)
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=right_storage_contact,
|
||||
rotate=90)
|
||||
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_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=right_storage_contact,
|
||||
directions=("H","H"))
|
||||
|
||||
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])
|
||||
|
|
@ -757,25 +760,23 @@ class pbitcell(design.design):
|
|||
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)
|
||||
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=port_contact_offset)
|
||||
self.add_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=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)
|
||||
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", [mid, left_storage_contact])
|
||||
self.add_path("metal1", [port_contact_offset, 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)
|
||||
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=port_contact_offset)
|
||||
self.add_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=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)
|
||||
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", [mid, right_storage_contact])
|
||||
self.add_path("metal1", [port_contact_offset, mid, right_storage_contact])
|
||||
|
||||
def extend_well(self):
|
||||
"""
|
||||
|
|
@ -794,13 +795,13 @@ class pbitcell(design.design):
|
|||
|
||||
# extend nwell to encompass inverter_pmos
|
||||
# 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_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - 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 - self.well_enclose_active
|
||||
|
||||
# calculate width of the two combined nwells
|
||||
# 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_height = self.vdd_position.y - inverter_well_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"]
|
||||
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 + self.well_enclose_active + drc["minwidth_tx"]
|
||||
|
||||
offset = [inverter_well_xpos,inverter_well_ypos]
|
||||
self.add_rect(layer="nwell",
|
||||
|
|
@ -811,19 +812,19 @@ class pbitcell(design.design):
|
|||
# add well contacts
|
||||
# connect pimplants to gnd
|
||||
offset = vector(0, self.gnd_position.y)
|
||||
self.add_contact_center(layers=("active", "contact", "metal1"),
|
||||
offset=offset,
|
||||
rotate=90,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
self.add_via_center(layers=("active", "contact", "metal1"),
|
||||
offset=offset,
|
||||
directions=("H","H"),
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
|
||||
# connect nimplants to vdd
|
||||
offset = vector(0, self.vdd_position.y)
|
||||
self.add_contact_center(layers=("active", "contact", "metal1"),
|
||||
offset=offset,
|
||||
rotate=90,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
self.add_via_center(layers=("active", "contact", "metal1"),
|
||||
offset=offset,
|
||||
directions=("H","H"),
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
|
||||
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 """
|
||||
|
|
@ -867,12 +868,16 @@ class pbitcell(design.design):
|
|||
self.add_path("metal1", [Q_bar_pos, vdd_pos])
|
||||
|
||||
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
|
||||
#FIXME: Delay copied exactly over from bitcell
|
||||
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
|
||||
parasitic_delay = 1
|
||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
||||
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||
|
||||
#Internal loads due to port configs are halved. This is to account for the size already being halved
|
||||
#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):
|
||||
"""Bitcell power in nW. Only characterizes leakage."""
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import utils
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import utils
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import utils
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
from tech import drc, spice,parameter
|
||||
|
|
@ -67,8 +74,7 @@ class replica_pbitcell(design.design):
|
|||
self.connect_inst(temp)
|
||||
|
||||
def place_pbitcell(self):
|
||||
offset = [0,0]
|
||||
self.prbc_inst.place(offset=offset)
|
||||
self.prbc_inst.place(offset=vector(0,0))
|
||||
|
||||
def route_rbc_connections(self):
|
||||
for port in range(self.total_ports):
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import globals
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import tech
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
from globals import OPTS
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import tech
|
||||
|
|
@ -9,6 +16,7 @@ import utils
|
|||
from globals import OPTS
|
||||
from .simulation import simulation
|
||||
from .measurements import *
|
||||
import logical_effort
|
||||
|
||||
class delay(simulation):
|
||||
"""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()
|
||||
power = self.analytical_power(slews, loads)
|
||||
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 load in loads:
|
||||
for load in relative_loads:
|
||||
self.set_load_slew(load,slew)
|
||||
bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load)
|
||||
for port in self.all_ports:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from design import design
|
||||
import debug
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import math
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import drc, parameter, spice
|
||||
|
||||
|
|
@ -9,6 +16,7 @@ class logical_effort():
|
|||
beta = parameter["beta"]
|
||||
min_inv_cin = 1+beta
|
||||
pinv=parameter["min_inv_para_delay"]
|
||||
tau = parameter['le_tau']
|
||||
|
||||
def __init__(self, name, size, cin, cout, parasitic, out_is_rise=True):
|
||||
self.name = name
|
||||
|
|
@ -30,30 +38,44 @@ class logical_effort():
|
|||
def get_stage_effort(self):
|
||||
return self.logical_effort*self.eletrical_effort
|
||||
|
||||
def get_parasitic_delay(self, pinv):
|
||||
return pinv * self.parasitic_scale
|
||||
def get_parasitic_delay(self):
|
||||
return logical_effort.pinv * self.parasitic_scale
|
||||
|
||||
def get_stage_delay(self, pinv):
|
||||
return self.get_stage_effort()+self.get_parasitic_delay(pinv)
|
||||
def get_stage_delay(self):
|
||||
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"""
|
||||
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."""
|
||||
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
|
||||
|
||||
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."""
|
||||
debug.info(2, "Calculating rise/fall relative delays")
|
||||
total_rise_delay, total_fall_delay = 0,0
|
||||
for stage in stage_effort_list:
|
||||
debug.info(2, stage)
|
||||
if stage.is_rise:
|
||||
total_rise_delay += stage.get_stage_delay(pinv)
|
||||
total_rise_delay += stage.get_stage_delay()
|
||||
else:
|
||||
total_fall_delay += stage.get_stage_delay(pinv)
|
||||
total_fall_delay += stage.get_stage_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']
|
||||
|
||||
|
|
@ -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
|
||||
from tech import drc, parameter, spice
|
||||
from abc import ABC, abstractmethod
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
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)
|
||||
self.period = tech.spice["feasible_period"]
|
||||
self.create_data_names()
|
||||
self.custom_delaychain=custom_delaychain
|
||||
|
||||
def create_data_names(self):
|
||||
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.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):
|
||||
"""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_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())]
|
||||
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.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"]
|
||||
|
||||
# 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))
|
||||
#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_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())]
|
||||
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.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.bitline_meas_names = ["delay_wl_to_bl", "delay_bl_to_dout"]
|
||||
self.power_meas_names = ['read0_power']
|
||||
|
||||
def create_signal_names(self):
|
||||
"""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
|
||||
|
|
@ -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_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())]
|
||||
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"]+\
|
||||
wl_en_driver_signals+\
|
||||
|
|
@ -70,17 +95,31 @@ class model_check(delay):
|
|||
self.rbl_en_signal_names = pre_delay_chain_names+\
|
||||
delay_chain_signal_names+\
|
||||
["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"]
|
||||
|
||||
|
||||
self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+\
|
||||
sen_driver_signals+\
|
||||
["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):
|
||||
"""Create the measurements used for read and write ports"""
|
||||
self.create_wordline_measurement_objects()
|
||||
self.create_sae_measurement_objects()
|
||||
self.all_measures = self.wl_meas_objs+self.sae_meas_objs
|
||||
self.create_wordline_meas_objs()
|
||||
self.create_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"""
|
||||
self.wl_meas_objs = []
|
||||
trig_dir = "RISE"
|
||||
|
|
@ -102,7 +141,19 @@ class model_check(delay):
|
|||
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))
|
||||
|
||||
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."""
|
||||
|
||||
self.sae_meas_objs = []
|
||||
|
|
@ -123,6 +174,13 @@ class model_check(delay):
|
|||
temp_dir = trig_dir
|
||||
trig_dir = targ_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.rbl_en_signal_names[-1],
|
||||
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.
|
||||
trig_dir = "FALL"
|
||||
targ_dir = "RISE"
|
||||
#Add measurements from gated_clk_bar to RBL
|
||||
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_signal_names[i-1],
|
||||
|
|
@ -169,10 +226,22 @@ class model_check(delay):
|
|||
or port to port (time delays)"""
|
||||
#Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port
|
||||
#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)
|
||||
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):
|
||||
"""
|
||||
|
|
@ -186,7 +255,8 @@ class model_check(delay):
|
|||
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."""
|
||||
delay_meas_list = []
|
||||
slew_meas_list = []
|
||||
slew_meas_list = []
|
||||
power_meas_list=[]
|
||||
for measure in meas_objs:
|
||||
measure_value = measure.retrieve_measure(port=port)
|
||||
if type(measure_value) != float:
|
||||
|
|
@ -195,9 +265,11 @@ class model_check(delay):
|
|||
delay_meas_list.append(measure_value)
|
||||
elif type(measure)is slew_measure:
|
||||
slew_meas_list.append(measure_value)
|
||||
elif type(measure)is power_measure:
|
||||
power_meas_list.append(measure_value)
|
||||
else:
|
||||
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):
|
||||
"""
|
||||
|
|
@ -213,6 +285,9 @@ class model_check(delay):
|
|||
wl_slew_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]
|
||||
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
|
||||
self.write_delay_stimulus()
|
||||
|
||||
|
|
@ -221,9 +296,11 @@ class model_check(delay):
|
|||
#Retrieve the results from the output file
|
||||
for port in self.targ_read_ports:
|
||||
#Parse and check the voltage measurements
|
||||
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)
|
||||
return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result)
|
||||
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)
|
||||
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):
|
||||
"""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_write_ports = [self.write_ports[0]]
|
||||
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))
|
||||
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,"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 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 Bitline delays (ns):\n\t {}".format(bl_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_model_name] = sae_model_delays
|
||||
data_dict[self.wl_slew_name] = wl_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
|
||||
# debug.info(1, "Comparing wordline measurements and model.")
|
||||
|
|
@ -337,11 +419,17 @@ class model_check(delay):
|
|||
name_dict = {}
|
||||
#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_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_model_name] = name_dict["sae_measures"]
|
||||
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.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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 tech
|
||||
from .stimuli import *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from design import design
|
||||
import debug
|
||||
|
|
|
|||
|
|
@ -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
|
||||
various functions that can be be used to generate stimulus for other
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from math import log
|
||||
import re
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import tech
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import glob
|
||||
import os
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
import os
|
||||
import base64
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"""
|
||||
This is a script to load data from the characterization and layout processes into
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
"""small library of functions to generate the html tables"""
|
||||
|
||||
|
|
|
|||
|
|
@ -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 inspect
|
||||
import globals
|
||||
|
|
@ -58,6 +65,8 @@ def log(str):
|
|||
# in another log file if the path or name changes.
|
||||
if not globals.OPTS.output_path.endswith('/'):
|
||||
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 +
|
||||
globals.OPTS.output_name + '.log', "w+")
|
||||
log.create_file = 0
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from drc_value import *
|
||||
from drc_lut import *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
class drc_lut():
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
#!/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
|
||||
for the given dimension SRAM. It is useful for debugging after an SRAM has been
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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).")
|
||||
|
||||
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:
|
||||
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:
|
||||
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).")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import drc, parameter
|
||||
import debug
|
||||
|
|
@ -800,14 +807,11 @@ class bank(design.design):
|
|||
bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y)
|
||||
self.add_path("metal3",[out_pos, bus_pos])
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=bus_pos,
|
||||
rotate=90)
|
||||
offset=bus_pos)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=out_pos,
|
||||
rotate=90)
|
||||
offset=out_pos)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=out_pos,
|
||||
rotate=90)
|
||||
offset=out_pos)
|
||||
|
||||
|
||||
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)
|
||||
self.add_path("metal1", [control_pos, pin_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=control_pos,
|
||||
rotate=90)
|
||||
offset=control_pos)
|
||||
|
||||
# clk to wordline_driver
|
||||
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)
|
||||
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=control_pos,
|
||||
rotate=90)
|
||||
offset=control_pos)
|
||||
|
||||
|
||||
def analytical_delay(self, corner, slew, load):
|
||||
""" return analytical delay of the bank"""
|
||||
results = []
|
||||
|
||||
decoder_delay = self.row_decoder.analytical_delay(corner, slew, self.wordline_driver.input_load())
|
||||
|
||||
word_driver_delay = self.wordline_driver.analytical_delay(corner,
|
||||
decoder_delay.slew,
|
||||
self.bitcell_array.input_load())
|
||||
def analytical_delay(self, corner, slew, load, port):
|
||||
""" return analytical delay of the bank. This will track the clock to output path"""
|
||||
#FIXME: This delay is determined in the control logic. Should be moved here.
|
||||
# word_driver_delay = self.wordline_driver.analytical_delay(corner,
|
||||
# slew,
|
||||
# self.bitcell_array.input_load())
|
||||
|
||||
#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
|
||||
for port in self.all_ports:
|
||||
if self.words_per_row > 1:
|
||||
column_mux_delay = self.column_mux_array[port].analytical_delay(corner,
|
||||
bitcell_array_delay.slew,
|
||||
self.sense_amp_array.input_load())
|
||||
else:
|
||||
column_mux_delay = self.return_delay(delay = 0.0, slew=word_driver_delay.slew)
|
||||
|
||||
bl_t_data_out_delay = self.sense_amp_array.analytical_delay(corner,
|
||||
column_mux_delay.slew,
|
||||
self.bitcell_array.output_load())
|
||||
# output load of bitcell_array is set to be only small part of bl for sense amp.
|
||||
results.append(decoder_delay + word_driver_delay + bitcell_array_delay + column_mux_delay + bl_t_data_out_delay)
|
||||
|
||||
return results
|
||||
|
||||
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,
|
||||
bitcell_array_slew,
|
||||
sa_load)
|
||||
else:
|
||||
column_mux_delay = []
|
||||
|
||||
column_mux_slew = 0
|
||||
sense_amp_delay = self.sense_amp_array.analytical_delay(corner,
|
||||
column_mux_slew,
|
||||
load)
|
||||
# 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
|
||||
|
||||
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"""
|
||||
#Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import drc, parameter
|
||||
import debug
|
||||
|
|
@ -213,7 +220,7 @@ class bank_select(design.design):
|
|||
end=bank_sel_pin_end)
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=bank_sel_pin_end,
|
||||
rotate=90)
|
||||
directions=("H","H"))
|
||||
|
||||
# bank_sel_bar is vertical wire
|
||||
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_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=logic_pos,
|
||||
rotate=90)
|
||||
directions=("H","H"))
|
||||
|
||||
|
||||
# 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)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=logic_pos,
|
||||
rotate=90)
|
||||
directions=("H","H"))
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=logic_pos,
|
||||
rotate=90)
|
||||
directions=("H","H"))
|
||||
self.add_layout_pin_segment_center(text=input_name,
|
||||
layer="metal3",
|
||||
start=input_pos,
|
||||
|
|
@ -295,10 +302,10 @@ class bank_select(design.design):
|
|||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pin_pos,
|
||||
rotate=90)
|
||||
directions=("H","H"))
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=pin_pos,
|
||||
rotate=90)
|
||||
directions=("H","H"))
|
||||
self.add_layout_pin_rect_center(text=n,
|
||||
layer="metal3",
|
||||
offset=pin_pos)
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
from tech import drc, spice
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import logical_effort
|
||||
|
||||
class bitcell_array(design.design):
|
||||
"""
|
||||
|
|
@ -127,26 +135,18 @@ class bitcell_array(design.design):
|
|||
inst = self.cell_inst[row,col]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
for pin in inst.get_pins(pin_name):
|
||||
self.add_power_pin(pin_name, pin.center(), 0, pin.layer)
|
||||
|
||||
|
||||
def analytical_delay(self, corner, slew, load=0):
|
||||
from tech import drc
|
||||
wl_wire = self.gen_wl_wire()
|
||||
wl_wire.return_delay_over_wire(slew)
|
||||
|
||||
wl_to_cell_delay = wl_wire.return_delay_over_wire(slew)
|
||||
# hypothetical delay from cell to bl end without sense amp
|
||||
bl_wire = self.gen_bl_wire()
|
||||
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)
|
||||
|
||||
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
|
||||
|
||||
def analytical_delay(self, corner, slew, load):
|
||||
"""Returns relative delay of the bitline in the bitcell array"""
|
||||
from tech import parameter
|
||||
#The load being driven/drained is mostly the bitline but could include the sense amp or the column mux.
|
||||
#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'])
|
||||
wire_unit_load = .05 * drain_load #Wires add 5% to this.
|
||||
bitline_load = (drain_load+wire_unit_load)*self.row_size
|
||||
return [self.cell.analytical_delay(corner, slew, load+bitline_load)]
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
from tech import drc, parameter
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import design
|
||||
from tech import drc, parameter
|
||||
|
|
@ -33,10 +40,10 @@ class control_logic(design.design):
|
|||
self.num_words = num_rows*words_per_row
|
||||
|
||||
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.
|
||||
self.wl_timing_tolerance = 1
|
||||
self.parasitic_inv_delay = parameter["min_inv_para_delay"]
|
||||
self.wl_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):
|
||||
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
|
||||
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))
|
||||
|
||||
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
|
||||
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")
|
||||
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.
|
||||
delay_stages+=1
|
||||
#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):
|
||||
"""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))
|
||||
|
||||
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):
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
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)
|
||||
self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos])
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=out_pos,
|
||||
rotate=90)
|
||||
offset=out_pos)
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=out_pos,
|
||||
rotate=90)
|
||||
offset=out_pos)
|
||||
|
||||
def create_pen_row(self):
|
||||
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)
|
||||
self.add_wire(("metal1","via1","metal2"),[in_pos, mid_pos, rail_pos])
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=rail_pos,
|
||||
rotate=90)
|
||||
offset=rail_pos)
|
||||
|
||||
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
|
||||
if (self.port_type == "rw"):
|
||||
|
|
@ -850,14 +854,14 @@ class control_logic(design.design):
|
|||
def get_delays_to_wl(self):
|
||||
"""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.")
|
||||
self.wl_stage_efforts = self.determine_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)
|
||||
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)
|
||||
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))
|
||||
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"""
|
||||
stage_effort_list = []
|
||||
|
||||
|
|
@ -871,7 +875,7 @@ class control_logic(design.design):
|
|||
last_stage_is_rise = stage_effort_list[-1].is_rise
|
||||
|
||||
#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
|
||||
|
||||
|
|
@ -880,17 +884,15 @@ class control_logic(design.design):
|
|||
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.")
|
||||
self.sen_stage_efforts = self.determine_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)
|
||||
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)
|
||||
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))
|
||||
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"""
|
||||
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
|
||||
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."""
|
||||
if self.sen_stage_efforts == None or self.wl_stage_efforts == None:
|
||||
debug.error("Model delays not calculated for SRAM.", 1)
|
||||
wl_delays = logical_effort.calculate_delays(self.wl_stage_efforts, self.parasitic_inv_delay)
|
||||
sen_delays = logical_effort.calculate_delays(self.sen_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)
|
||||
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
|
||||
|
|
@ -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 design
|
||||
from tech import drc
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
from math import log
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
from tech import drc
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
from tech import drc,parameter
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
from tech import drc
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
from tech import drc
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
from tech import drc
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import debug
|
||||
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 """
|
||||
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=input_offset,
|
||||
rotate=90)
|
||||
offset=input_offset)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=output_offset,
|
||||
rotate=90)
|
||||
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)
|
||||
self.add_path("metal1", [rail_pos, pin.lc()])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=rail_pos,
|
||||
rotate=90)
|
||||
offset=rail_pos)
|
||||
|
||||
|
||||
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)
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pin.center(),
|
||||
rotate=90)
|
||||
offset=pin.center())
|
||||
self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()])
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=rail_pos,
|
||||
rotate=90)
|
||||
offset=rail_pos)
|
||||
|
||||
|
||||
def analytical_delay(self, corner, slew, load = 0.0):
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
import math
|
||||
|
|
@ -178,11 +185,9 @@ class hierarchical_predecode(design.design):
|
|||
a_pos = vector(self.decode_rails[a_pin].x,y_offset)
|
||||
self.add_path("metal1",[in_pos, a_pos])
|
||||
self.add_via_center(layers = ("metal1", "via1", "metal2"),
|
||||
offset=[self.input_rails[in_pin].x, y_offset],
|
||||
rotate=90)
|
||||
offset=[self.input_rails[in_pin].x, y_offset])
|
||||
self.add_via_center(layers = ("metal1", "via1", "metal2"),
|
||||
offset=[self.decode_rails[a_pin].x, y_offset],
|
||||
rotate=90)
|
||||
offset=[self.decode_rails[a_pin].x, y_offset])
|
||||
|
||||
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)
|
||||
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"),
|
||||
offset=rail_pos,
|
||||
rotate=90)
|
||||
offset=rail_pos)
|
||||
|
||||
|
||||
#route input
|
||||
|
|
@ -232,8 +236,7 @@ class hierarchical_predecode(design.design):
|
|||
in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y)
|
||||
self.add_path("metal1", [in_pos, inv_in_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=in_pos,
|
||||
rotate=90)
|
||||
offset=in_pos)
|
||||
|
||||
|
||||
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)
|
||||
self.add_path("metal1", [rail_pos, pin_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=rail_pos,
|
||||
rotate=90)
|
||||
offset=rail_pos)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import debug
|
||||
import design
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import debug
|
||||
import design
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import drc, parameter
|
||||
import debug
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
from tech import drc
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
from tech import drc
|
||||
|
|
@ -39,8 +46,8 @@ class replica_bitline(design.design):
|
|||
#self.add_lvs_correspondence_points()
|
||||
|
||||
# Extra pitch on top and right
|
||||
self.width = self.rbl_inst.rx() - self.dc_inst.lx() + self.m2_pitch
|
||||
self.height = max(self.rbl_inst.uy(), self.dc_inst.uy()) + self.m3_pitch
|
||||
self.width = self.replica_column_inst.rx() - self.delay_chain_inst.lx() + self.m2_pitch
|
||||
self.height = max(self.replica_column_inst.uy(), self.delay_chain_inst.uy()) + self.m3_pitch
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
|
|
@ -111,12 +118,12 @@ class replica_bitline(design.design):
|
|||
self.connect_inst(["vdd", "delayed_en", "bl0_0", "vdd"])
|
||||
# add the well and poly contact
|
||||
|
||||
self.dc_inst=self.add_inst(name="delay_chain",
|
||||
mod=self.delay_chain)
|
||||
self.delay_chain_inst=self.add_inst(name="delay_chain",
|
||||
mod=self.delay_chain)
|
||||
self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
|
||||
|
||||
self.rbc_inst=self.add_inst(name="bitcell",
|
||||
mod=self.replica_bitcell)
|
||||
self.replica_cell_inst=self.add_inst(name="bitcell",
|
||||
mod=self.replica_bitcell)
|
||||
temp = []
|
||||
for port in self.all_ports:
|
||||
temp.append("bl{}_0".format(port))
|
||||
|
|
@ -127,8 +134,8 @@ class replica_bitline(design.design):
|
|||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
self.rbl_inst=self.add_inst(name="load",
|
||||
mod=self.rbl)
|
||||
self.replica_column_inst=self.add_inst(name="load",
|
||||
mod=self.rbl)
|
||||
|
||||
temp = []
|
||||
for port in self.all_ports:
|
||||
|
|
@ -153,12 +160,12 @@ class replica_bitline(design.design):
|
|||
|
||||
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")
|
||||
|
||||
self.rbl_inst.place(self.rbl_offset)
|
||||
self.replica_column_inst.place(self.rbl_offset)
|
||||
|
||||
|
||||
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
|
||||
for row in range(self.bitcell_loads):
|
||||
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
|
||||
# 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.
|
||||
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))
|
||||
|
||||
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:
|
||||
if is_replica_cell:
|
||||
wl = self.wl_list[port]
|
||||
pin = self.rbc_inst.get_pin(wl)
|
||||
pin = self.replica_cell_inst.get_pin(wl)
|
||||
else:
|
||||
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":
|
||||
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 """
|
||||
|
||||
# These are the instances that every bank has
|
||||
top_instances = [self.rbl_inst,
|
||||
self.dc_inst]
|
||||
top_instances = [self.replica_column_inst,
|
||||
self.delay_chain_inst]
|
||||
for inst in top_instances:
|
||||
self.copy_layout_pin(inst, "vdd")
|
||||
self.copy_layout_pin(inst, "gnd")
|
||||
|
|
@ -257,11 +264,11 @@ class replica_bitline(design.design):
|
|||
pin = self.rbl_inv_inst.get_pin("vdd")
|
||||
self.add_power_pin("vdd", pin.lc())
|
||||
|
||||
pin=self.rbc_inst.get_pin("vdd")
|
||||
self.add_power_pin("vdd", pin.center(), 0, pin.layer)
|
||||
for pin in self.replica_cell_inst.get_pins("vdd"):
|
||||
self.add_power_pin(name="vdd", loc=pin.center(), vertical=True, start_layer=pin.layer)
|
||||
|
||||
for pin in self.rbc_inst.get_pins("gnd"):
|
||||
self.add_power_pin("gnd", pin.center())
|
||||
for pin in self.replica_cell_inst.get_pins("gnd"):
|
||||
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()
|
||||
# This centers the contact above the poly by one pitch
|
||||
contact_offset = poly_offset + vector(0,self.m2_pitch)
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=contact_offset)
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=contact_offset)
|
||||
self.add_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=contact_offset)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=contact_offset)
|
||||
self.add_segment_center(layer="poly",
|
||||
start=poly_offset,
|
||||
end=contact_offset)
|
||||
|
|
@ -289,12 +296,12 @@ class replica_bitline(design.design):
|
|||
# height=self.delay_chain_offset.y-nwell_offset.y)
|
||||
|
||||
# 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])
|
||||
|
||||
# 3. Route the contact of previous route to the 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_mid2 = vector(wl_mid1.x, contact_offset.y)
|
||||
#xmid_point= 0.5*(wl_offset.x+contact_offset.x)
|
||||
|
|
@ -305,8 +312,8 @@ class replica_bitline(design.design):
|
|||
# 4. Short wodlines if multiport
|
||||
wl = self.wl_list[0]
|
||||
wl_last = self.wl_list[-1]
|
||||
pin = self.rbc_inst.get_pin(wl)
|
||||
pin_last = self.rbc_inst.get_pin(wl_last)
|
||||
pin = self.replica_cell_inst.get_pin(wl)
|
||||
pin_last = self.replica_cell_inst.get_pin(wl_last)
|
||||
x_offset = self.short_wordlines(pin, pin_last, "left", True)
|
||||
|
||||
#correct = vector(0.5*drc("minwidth_metal1"), 0)
|
||||
|
|
@ -315,7 +322,7 @@ class replica_bitline(design.design):
|
|||
# DRAIN ROUTE
|
||||
# Route the drain to the vdd rail
|
||||
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
|
||||
# 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)
|
||||
# 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
|
||||
bl_down_offset = bl_offset - vector(0, self.m2_pitch)
|
||||
self.add_path("metal2",[source_offset, bl_down_offset, bl_offset])
|
||||
|
|
@ -344,12 +351,12 @@ class replica_bitline(design.design):
|
|||
def route_vdd(self):
|
||||
""" Route all signals connected to vdd """
|
||||
|
||||
self.copy_layout_pin(self.dc_inst,"vdd")
|
||||
self.copy_layout_pin(self.rbc_inst,"vdd")
|
||||
self.copy_layout_pin(self.delay_chain_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 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:
|
||||
if pin.layer != "metal1":
|
||||
continue
|
||||
|
|
@ -360,11 +367,9 @@ class replica_bitline(design.design):
|
|||
start=start,
|
||||
end=end)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=start,
|
||||
rotate=90)
|
||||
offset=start)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=end,
|
||||
rotate=90)
|
||||
offset=end)
|
||||
|
||||
# Add via for the inverter
|
||||
pin = self.rbl_inv_inst.get_pin("vdd")
|
||||
|
|
@ -374,38 +379,34 @@ class replica_bitline(design.design):
|
|||
start=start,
|
||||
end=end)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=start,
|
||||
rotate=90)
|
||||
offset=start)
|
||||
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=end,
|
||||
rotate=90)
|
||||
offset=end)
|
||||
|
||||
|
||||
# Add via for the RBC
|
||||
pin = self.rbc_inst.get_pin("vdd")
|
||||
pin = self.replica_cell_inst.get_pin("vdd")
|
||||
start = pin.lc()
|
||||
end = vector(self.right_vdd_pin.cx(),pin.cy())
|
||||
self.add_segment_center(layer="metal1",
|
||||
start=start,
|
||||
end=end)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=end,
|
||||
rotate=90)
|
||||
offset=end)
|
||||
|
||||
# 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:
|
||||
if pin.layer != "metal1":
|
||||
continue
|
||||
# If above the delay line, route the full width
|
||||
left = vector(self.left_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
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=left,
|
||||
rotate=90)
|
||||
offset=left)
|
||||
else:
|
||||
start = center
|
||||
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
|
||||
|
||||
# Add via for the delay chain
|
||||
left_gnd_start = self.dc_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_start = self.delay_chain_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0)
|
||||
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",
|
||||
start=left_gnd_start,
|
||||
end=left_gnd_end)
|
||||
|
||||
# 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_end = vector(center_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch)
|
||||
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.replica_column_inst.uy()+self.m2_pitch)
|
||||
self.center_gnd_pin=self.add_segment_center(layer="metal2",
|
||||
start=center_gnd_start,
|
||||
end=center_gnd_end)
|
||||
|
||||
# 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_end = vector(right_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch)
|
||||
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.replica_column_inst.uy()+self.m2_pitch)
|
||||
self.right_gnd_pin=self.add_segment_center(layer="metal2",
|
||||
start=right_gnd_start,
|
||||
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
|
||||
for row in range(self.bitcell_loads):
|
||||
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":
|
||||
continue
|
||||
# If above the delay line, route the full width
|
||||
left = vector(self.left_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
|
||||
else:
|
||||
start = center
|
||||
|
|
@ -467,47 +468,41 @@ class replica_bitline(design.design):
|
|||
end=end)
|
||||
if start == left:
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=left,
|
||||
rotate=90)
|
||||
offset=left)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=center,
|
||||
rotate=90)
|
||||
offset=center)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=end,
|
||||
rotate=90)
|
||||
offset=end)
|
||||
|
||||
|
||||
rbl_gnd_pins = self.rbl_inst.get_pins("gnd")
|
||||
# Add L shapes to each vertical gnd rail
|
||||
for pin in rbl_gnd_pins:
|
||||
if pin.layer != "metal1":
|
||||
continue
|
||||
# If above the delay line, route the full width
|
||||
left = vector(self.left_gnd_pin.cx(),pin.cy())
|
||||
center = vector(self.center_gnd_pin.cx(),pin.cy())
|
||||
if pin.cy() > self.dc_inst.uy() + self.m1_pitch:
|
||||
start = left
|
||||
else:
|
||||
start = center
|
||||
end = vector(self.right_gnd_pin.cx(),pin.cy())
|
||||
self.add_segment_center(layer="metal1",
|
||||
start=start,
|
||||
end=end)
|
||||
if start == left:
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=left,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=center,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=end,
|
||||
rotate=90)
|
||||
# rbl_gnd_pins = self.replica_column_inst.get_pins("gnd")
|
||||
# # Add L shapes to each vertical gnd rail
|
||||
# for pin in rbl_gnd_pins:
|
||||
# if pin.layer != "metal1":
|
||||
# continue
|
||||
# # If above the delay line, route the full width
|
||||
# left = vector(self.left_gnd_pin.cx(),pin.cy())
|
||||
# center = vector(self.center_gnd_pin.cx(),pin.cy())
|
||||
# if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
|
||||
# start = left
|
||||
# else:
|
||||
# start = center
|
||||
# end = vector(self.right_gnd_pin.cx(),pin.cy())
|
||||
# self.add_segment_center(layer="metal1",
|
||||
# start=start,
|
||||
# end=end)
|
||||
# if start == left:
|
||||
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
# offset=left)
|
||||
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
# offset=center)
|
||||
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
# offset=end)
|
||||
|
||||
|
||||
|
||||
# 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:
|
||||
if pin.layer != "metal1":
|
||||
continue
|
||||
|
|
@ -520,12 +515,10 @@ class replica_bitline(design.design):
|
|||
start=start,
|
||||
end=end)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=start,
|
||||
rotate=90)
|
||||
offset=start)
|
||||
|
||||
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
# offset=end,
|
||||
#rotate=90)
|
||||
# offset=end)
|
||||
|
||||
|
||||
# Add via for the inverter
|
||||
|
|
@ -536,16 +529,14 @@ class replica_bitline(design.design):
|
|||
# start=start,
|
||||
# end=end)
|
||||
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
# offset=start,
|
||||
#rotate=90)
|
||||
# offset=start)
|
||||
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
# offset=end,
|
||||
#rotate=90)
|
||||
# offset=end)
|
||||
|
||||
|
||||
|
||||
# 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:
|
||||
if pin.layer != "metal2":
|
||||
continue
|
||||
|
|
@ -560,7 +551,7 @@ class replica_bitline(design.design):
|
|||
|
||||
def add_layout_pins(self):
|
||||
""" 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",
|
||||
layer="metal2",
|
||||
start=en_offset,
|
||||
|
|
@ -587,7 +578,7 @@ class replica_bitline(design.design):
|
|||
height=pin.height(),
|
||||
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",
|
||||
layer=pin.layer,
|
||||
offset=pin.ll(),
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
import utils
|
||||
from tech import GDS,layer, parameter,drc
|
||||
import logical_effort
|
||||
|
||||
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.
|
||||
return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff
|
||||
|
||||
def analytical_delay(self, corner, slew, load=0.0):
|
||||
from tech import spice
|
||||
r = spice["min_tx_r"]/(10)
|
||||
c_para = spice["min_tx_drain_c"]
|
||||
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
|
||||
return self.return_delay(result.delay, result.slew)
|
||||
def analytical_delay(self, corner, slew, load):
|
||||
#Delay of the sense amp will depend on the size of the amp and the output load.
|
||||
parasitic_delay = 1
|
||||
cin = (parameter["sa_inv_pmos_size"] + parameter["sa_inv_nmos_size"])/drc("minwidth_tx")
|
||||
sa_size = parameter["sa_inv_nmos_size"]/drc("minwidth_tx")
|
||||
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):
|
||||
"""Returns dynamic and leakage power. Results in nW"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import drc
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
from globals import OPTS
|
||||
import logical_effort
|
||||
|
||||
class sense_amp_array(design.design):
|
||||
"""
|
||||
|
|
@ -136,10 +144,17 @@ class sense_amp_array(design.design):
|
|||
def input_load(self):
|
||||
return self.amp.input_load()
|
||||
|
||||
def analytical_delay(self, corner, slew, load=0.0):
|
||||
return self.amp.analytical_delay(corner, slew=slew, load=load)
|
||||
def analytical_delay(self, corner, slew, load):
|
||||
return [self.amp.analytical_delay(corner, slew=slew, load=load)]
|
||||
|
||||
def get_en_cin(self):
|
||||
"""Get the relative capacitance of all the sense amp enable connections in the array"""
|
||||
sense_amp_en_cin = self.amp.get_en_cin()
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import design
|
||||
import contact
|
||||
|
|
@ -7,6 +14,7 @@ import math
|
|||
from vector import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
import logical_effort
|
||||
|
||||
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),
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
width=self.mux.width * self.columns,
|
||||
height=contact.m1m2.width)
|
||||
width=self.mux.width * self.columns)
|
||||
|
||||
def add_vertical_poly_rail(self):
|
||||
""" 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())
|
||||
# Add the poly contact with a shift to account for the rotation
|
||||
self.add_via_center(layers=("metal1", "contact", "poly"),
|
||||
offset=offset,
|
||||
rotate=90)
|
||||
offset=offset)
|
||||
self.add_path("poly", [offset, gate_offset])
|
||||
|
||||
def route_bitlines(self):
|
||||
""" Connect the output bit-lines to form the appropriate width mux """
|
||||
for j in range(self.columns):
|
||||
bl_offset = self.mux_inst[j].get_pin("bl_out").ll()
|
||||
br_offset = self.mux_inst[j].get_pin("br_out").ll()
|
||||
bl_offset = self.mux_inst[j].get_pin("bl_out").bc()
|
||||
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)
|
||||
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:
|
||||
# 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
|
||||
# to ensure vias are enclosed and M1 min width rules.
|
||||
width = contact.m1m2.width + self.mux.width * (self.words_per_row - 1)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=bl_out_offset,
|
||||
width=width,
|
||||
height=drc("minwidth_metal2"))
|
||||
self.add_rect(layer="metal1",
|
||||
offset=br_out_offset,
|
||||
width=width,
|
||||
height=drc("minwidth_metal2"))
|
||||
|
||||
width = self.m2_width + self.mux.width * (self.words_per_row - 1)
|
||||
self.add_path("metal1", [bl_out_offset, bl_out_offset+vector(width,0)])
|
||||
self.add_path("metal1", [br_out_offset, br_out_offset+vector(width,0)])
|
||||
|
||||
# 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)),
|
||||
layer="metal2",
|
||||
offset=bl_out_offset.scale(1,0),
|
||||
width=drc('minwidth_metal2'),
|
||||
height=self.route_height)
|
||||
self.add_layout_pin(text="br_out_{}".format(int(j/self.words_per_row)),
|
||||
layer="metal2",
|
||||
offset=br_out_offset.scale(1,0),
|
||||
width=drc('minwidth_metal2'),
|
||||
height=self.route_height)
|
||||
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)),
|
||||
layer="metal2",
|
||||
start=bl_out_offset,
|
||||
end=bl_out_offset_end)
|
||||
self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)),
|
||||
layer="metal2",
|
||||
start=br_out_offset,
|
||||
end=br_out_offset_end)
|
||||
|
||||
|
||||
# This via is on the right of the wire
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=bl_out_offset + vector(contact.m1m2.height,0),
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=bl_out_offset)
|
||||
|
||||
# This via is on the left of the wire
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset= br_out_offset,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=br_out_offset)
|
||||
|
||||
else:
|
||||
|
||||
self.add_rect(layer="metal2",
|
||||
offset=bl_out_offset,
|
||||
width=drc('minwidth_metal2'),
|
||||
height=self.route_height-bl_out_offset.y)
|
||||
self.add_path("metal2", [ bl_out_offset, bl_out_offset_end])
|
||||
self.add_path("metal2", [ br_out_offset, br_out_offset_end])
|
||||
|
||||
# This via is on the right of the wire
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=bl_out_offset + vector(contact.m1m2.height,0),
|
||||
rotate=90)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=br_out_offset,
|
||||
width=drc('minwidth_metal2'),
|
||||
height=self.route_height-br_out_offset.y)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=bl_out_offset)
|
||||
# This via is on the left of the wire
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset= br_out_offset,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=br_out_offset)
|
||||
|
||||
def analytical_delay(self, corner, vdd, slew, load=0.0):
|
||||
from tech import spice, parameter
|
||||
proc,vdd,temp = corner
|
||||
r = spice["min_tx_r"]/(self.mux.ptx_width/parameter["min_tx_size"])
|
||||
#Drains of mux transistors make up capacitance.
|
||||
c_para = spice["min_tx_drain_c"]*(self.mux.ptx_width/parameter["min_tx_size"])*self.words_per_row#ff
|
||||
volt_swing = spice["v_threshold_typical"]/vdd
|
||||
|
||||
def analytical_delay(self, corner, slew, load):
|
||||
from tech import parameter
|
||||
"""Returns relative delay that the column mux adds"""
|
||||
#Single level column mux will add parasitic loads from other mux pass transistors and the sense amp.
|
||||
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
|
||||
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)
|
||||
return self.return_delay(result.delay, result.slew)
|
||||
|
||||
def get_drain_cin(self):
|
||||
"""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
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
import utils
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import drc
|
||||
import design
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import debug
|
||||
import design
|
||||
|
|
@ -81,21 +88,13 @@ class wordline_driver(design.design):
|
|||
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
|
||||
|
||||
# Route both supplies
|
||||
for n in ["vdd", "gnd"]:
|
||||
supply_pin = self.inv2_inst[num].get_pin(n)
|
||||
for name in ["vdd", "gnd"]:
|
||||
supply_pin = self.inv2_inst[num].get_pin(name)
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [a_xoffset, b_xoffset]:
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
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)
|
||||
self.add_power_pin(name, pin_pos)
|
||||
|
||||
|
||||
|
||||
|
|
@ -193,13 +192,14 @@ class wordline_driver(design.design):
|
|||
start=input_offset,
|
||||
end=mid_via_offset)
|
||||
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
|
||||
self.add_path("metal2", [mid_via_offset, b_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=b_pos - vector(0.5*contact.m1m2.height,0),
|
||||
rotate=90)
|
||||
directions=("H","H"))
|
||||
|
||||
|
||||
# output each WL on the right
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
import utils
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import design
|
||||
from tech import drc
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
#!/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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 getpass
|
||||
import os
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import drc
|
||||
from math import log
|
||||
|
|
@ -11,16 +18,13 @@ class pand2(pgate.pgate):
|
|||
This is a simple buffer used for driving loads.
|
||||
"""
|
||||
def __init__(self, name, size=1, height=None):
|
||||
self.size = size
|
||||
|
||||
pgate.pgate.__init__(self, name, height)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
debug.info(1, "reating pnand2 {}".format(name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
self.size = size
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
|
@ -40,6 +44,7 @@ class pand2(pgate.pgate):
|
|||
self.place_insts()
|
||||
self.add_wires()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("A")
|
||||
|
|
@ -125,3 +130,8 @@ class pand2(pgate.pgate):
|
|||
stage_effort_list.append(stage2)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_cin(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
return self.nand.get_cin()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import drc
|
||||
from math import log
|
||||
|
|
@ -12,17 +19,15 @@ class pbuf(pgate.pgate):
|
|||
"""
|
||||
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.size = size
|
||||
self.height = height
|
||||
|
||||
# Creates the netlist and layout
|
||||
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):
|
||||
|
|
|
|||
|
|
@ -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 pgate
|
||||
import math
|
||||
|
|
@ -13,27 +20,24 @@ class pdriver(pgate.pgate):
|
|||
"""
|
||||
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.height = height
|
||||
self.neg_polarity = neg_polarity
|
||||
self.size_list = size_list
|
||||
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:
|
||||
debug.error("Cannot specify both size_list and fanout.", -1)
|
||||
if self.size_list and self.neg_polarity:
|
||||
debug.error("Cannot specify both size_list and neg_polarity.", -1)
|
||||
|
||||
# Creates the netlist and layout
|
||||
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):
|
||||
# size_list specified
|
||||
|
|
@ -61,6 +65,8 @@ class pdriver(pgate.pgate):
|
|||
|
||||
|
||||
def create_netlist(self):
|
||||
self.compute_sizes()
|
||||
self.add_comment("sizes: {}".format(str(self.size_list)))
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_insts()
|
||||
|
|
@ -73,7 +79,6 @@ class pdriver(pgate.pgate):
|
|||
self.width = self.inv_inst_list[-1].rx()
|
||||
self.height = self.inv_inst_list[0].height
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("A")
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
import debug
|
||||
|
|
@ -21,7 +28,20 @@ class pgate(design.design):
|
|||
b = factory.create(module_type="bitcell")
|
||||
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):
|
||||
""" Connects a ptx pin to a supply rail. """
|
||||
source_pin = inst.get_pin(pin)
|
||||
|
|
@ -42,7 +62,7 @@ class pgate(design.design):
|
|||
height=height,
|
||||
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.
|
||||
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)
|
||||
|
||||
# Center is completely symmetric.
|
||||
if rotate==90:
|
||||
if rotate:
|
||||
contact_width = contact.poly.height
|
||||
contact_m1_width = contact.poly.second_layer_height
|
||||
contact_m1_height = contact.poly.second_layer_width
|
||||
directions = ("H","V")
|
||||
else:
|
||||
contact_width = contact.poly.width
|
||||
contact_m1_width = contact.poly.second_layer_width
|
||||
contact_m1_height = contact.poly.second_layer_height
|
||||
directions = ("V","H")
|
||||
|
||||
if position=="center":
|
||||
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":
|
||||
contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0)
|
||||
elif position=="right":
|
||||
|
|
@ -79,9 +103,12 @@ class pgate(design.design):
|
|||
else:
|
||||
debug.error("Invalid contact placement option.", -1)
|
||||
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=contact_offset,
|
||||
rotate=rotate)
|
||||
# Non-preferred direction via
|
||||
|
||||
self.add_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=contact_offset,
|
||||
directions=directions)
|
||||
|
||||
# self.add_layout_pin_segment_center(text=name,
|
||||
# layer="metal1",
|
||||
# start=left_gate_offset.scale(0,1),
|
||||
|
|
@ -145,10 +172,11 @@ class pgate(design.design):
|
|||
# Offset by half a contact in x and y
|
||||
contact_offset += vector(0.5*pmos.active_contact.first_layer_width,
|
||||
0.5*pmos.active_contact.first_layer_height)
|
||||
self.nwell_contact=self.add_contact_center(layers=layer_stack,
|
||||
offset=contact_offset,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
self.nwell_contact=self.add_via_center(layers=layer_stack,
|
||||
offset=contact_offset,
|
||||
directions=("H","V"),
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
self.add_rect_center(layer="metal1",
|
||||
offset=contact_offset + vector(0,0.5*(self.height-contact_offset.y)),
|
||||
width=self.nwell_contact.mod.second_layer_width,
|
||||
|
|
@ -191,10 +219,11 @@ class pgate(design.design):
|
|||
# Offset by half a contact
|
||||
contact_offset += vector(0.5*nmos.active_contact.first_layer_width,
|
||||
0.5*nmos.active_contact.first_layer_height)
|
||||
self.pwell_contact=self.add_contact_center(layers=layer_stack,
|
||||
offset=contact_offset,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
self.pwell_contact=self.add_via_center(layers=layer_stack,
|
||||
offset=contact_offset,
|
||||
directions=("H","V"),
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
self.add_rect_center(layer="metal1",
|
||||
offset=contact_offset.scale(1,0.5),
|
||||
width=self.pwell_contact.mod.second_layer_width,
|
||||
|
|
|
|||
|
|
@ -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 pgate
|
||||
import debug
|
||||
|
|
@ -20,28 +27,18 @@ class pinv(pgate.pgate):
|
|||
"""
|
||||
|
||||
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
|
||||
# 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))
|
||||
|
||||
debug.info(2, "creating pinv structure {0} with size of {1}".format(name, size))
|
||||
self.add_comment("size: {}".format(size))
|
||||
|
||||
|
||||
self.size = size
|
||||
self.nmos_size = size
|
||||
self.pmos_size = beta*size
|
||||
self.beta = beta
|
||||
self.route_output = False
|
||||
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
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()
|
||||
# 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 """
|
||||
|
|
@ -58,7 +55,7 @@ class pinv(pgate.pgate):
|
|||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
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()
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -222,8 +219,8 @@ class pinv(pgate.pgate):
|
|||
pmos_drain_pin = self.pmos_inst.get_pin("D")
|
||||
|
||||
# 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)
|
||||
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.bc().y)
|
||||
nmos_drain_pos = nmos_drain_pin.bc()
|
||||
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
|
||||
self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos])
|
||||
|
||||
# Remember the mid for the output
|
||||
|
|
|
|||
|
|
@ -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 design
|
||||
import pgate
|
||||
from tech import drc
|
||||
from math import log
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
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
|
||||
used in the column decoder for 1:2 decoding and as the clock buffer.
|
||||
"""
|
||||
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.row_height = height
|
||||
# FIXME: Change the number of stages to support high drives.
|
||||
|
|
@ -23,13 +33,8 @@ class pinvbuf(design.design):
|
|||
self.size = size
|
||||
self.predriver_size = max(int(self.size/(self.stage_effort/2)),1)
|
||||
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name)
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
|
|
@ -48,7 +53,6 @@ class pinvbuf(design.design):
|
|||
|
||||
self.offset_all_coordinates()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("A")
|
||||
|
|
|
|||
|
|
@ -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 pgate
|
||||
import debug
|
||||
|
|
@ -14,8 +21,8 @@ class pnand2(pgate.pgate):
|
|||
"""
|
||||
def __init__(self, name, size=1, height=None):
|
||||
""" 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.size = size
|
||||
|
|
@ -28,9 +35,8 @@ class pnand2(pgate.pgate):
|
|||
debug.check(size==1,"Size 1 pnand2 is only supported now.")
|
||||
self.tx_mults = 1
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height)
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
|
|
@ -203,13 +209,15 @@ class pnand2(pgate.pgate):
|
|||
mid1_offset = vector(out_offset.x, top_pin_offset.y)
|
||||
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
|
||||
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pmos_pin.center())
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=nmos_pin.center())
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=out_offset,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pmos_pin.center(),
|
||||
directions=("V","H"))
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=nmos_pin.center(),
|
||||
directions=("V","H"))
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=out_offset)
|
||||
|
||||
|
||||
# PMOS1 to mid-drain to NMOS2 drain
|
||||
self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset])
|
||||
|
|
|
|||
|
|
@ -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 pgate
|
||||
import debug
|
||||
|
|
@ -14,8 +21,8 @@ class pnand3(pgate.pgate):
|
|||
"""
|
||||
def __init__(self, name, size=1, height=None):
|
||||
""" 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))
|
||||
|
||||
# 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.")
|
||||
self.tx_mults = 1
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height)
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -213,12 +219,12 @@ class pnand3(pgate.pgate):
|
|||
nmos3_pin = self.nmos3_inst.get_pin("D")
|
||||
|
||||
# Go up to metal2 for ease on all output pins
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pmos1_pin.center())
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pmos3_pin.center())
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=nmos3_pin.center())
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pmos1_pin.center())
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pmos3_pin.center())
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=nmos3_pin.center())
|
||||
|
||||
# PMOS3 and NMOS3 are drain aligned
|
||||
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()])
|
||||
|
||||
# This extends the output to the edge of the cell
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=mid_offset)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=mid_offset)
|
||||
self.add_layout_pin_rect_center(text="Z",
|
||||
layer="metal1",
|
||||
offset=mid_offset,
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
from tech import drc, parameter, spice
|
||||
|
|
@ -13,8 +20,8 @@ class pnor2(pgate.pgate):
|
|||
"""
|
||||
def __init__(self, name, size=1, height=None):
|
||||
""" 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.nmos_size = size
|
||||
|
|
@ -27,9 +34,8 @@ class pnor2(pgate.pgate):
|
|||
debug.check(size==1,"Size 1 pnor2 is only supported now.")
|
||||
self.tx_mults = 1
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
#self.DRC_LVS()
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height)
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -38,12 +44,11 @@ class pnor2(pgate.pgate):
|
|||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_ptx()
|
||||
self.setup_layout_constants()
|
||||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.create_ptx()
|
||||
self.setup_layout_constants()
|
||||
self.add_supply_rails()
|
||||
self.add_ptx()
|
||||
self.connect_rails()
|
||||
|
|
@ -184,11 +189,11 @@ class pnor2(pgate.pgate):
|
|||
nmos2_pin = self.nmos2_inst.get_pin("D")
|
||||
|
||||
# Go up to metal2 for ease on all output pins
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pmos_pin.center())
|
||||
m1m2_contact=self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=nmos_pin.center(),
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pmos_pin.center())
|
||||
m1m2_contact=self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=nmos_pin.center())
|
||||
|
||||
|
||||
mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y)
|
||||
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",[nmos_pin.rc(), mid1_offset, mid2_offset])
|
||||
# This extends the output to the edge of the cell
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=mid3_offset,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=mid3_offset)
|
||||
self.add_layout_pin_rect_center(text="Z",
|
||||
layer="metal1",
|
||||
offset=mid3_offset,
|
||||
|
|
|
|||
|
|
@ -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 pgate
|
||||
import design
|
||||
import debug
|
||||
from tech import drc, parameter
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
class precharge(pgate.pgate):
|
||||
class precharge(design.design):
|
||||
"""
|
||||
Creates a single precharge cell
|
||||
This module implements the precharge bitline cell used in the design.
|
||||
"""
|
||||
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.beta = parameter["beta"]
|
||||
self.ptx_width = self.beta*parameter["min_tx_size"]
|
||||
self.width = self.bitcell.width
|
||||
self.bitcell_bl = bitcell_bl
|
||||
self.bitcell_br = bitcell_br
|
||||
|
||||
|
||||
|
||||
# Creates the netlist and layout
|
||||
# Since it has variable height, it is not a pgate.
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
|
@ -40,7 +51,6 @@ class precharge(pgate.pgate):
|
|||
self.route_vdd_rail()
|
||||
self.route_bitlines()
|
||||
self.connect_to_bitlines()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
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])
|
||||
|
||||
# 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):
|
||||
|
|
@ -149,9 +159,8 @@ class precharge(pgate.pgate):
|
|||
|
||||
# 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)
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=offset,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("poly", "contact", "metal1"),
|
||||
offset=offset)
|
||||
|
||||
# adds the en rail on metal1
|
||||
self.add_layout_pin_segment_center(text="en_bar",
|
||||
|
|
@ -168,10 +177,10 @@ class precharge(pgate.pgate):
|
|||
# adds the contact from active to metal1
|
||||
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"))
|
||||
self.add_contact_center(layers=("active", "contact", "metal1"),
|
||||
offset=well_contact_pos,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
self.add_via_center(layers=("active", "contact", "metal1"),
|
||||
offset=well_contact_pos,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
|
||||
# leave an extra pitch for the height
|
||||
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")
|
||||
|
||||
# BL goes up to M2 at the transistor
|
||||
self.bl_contact=self.add_contact_center(layers=stack,
|
||||
offset=upper_pin.center())
|
||||
self.add_contact_center(layers=stack,
|
||||
offset=lower_pin.center())
|
||||
self.bl_contact=self.add_via_center(layers=stack,
|
||||
offset=upper_pin.center(),
|
||||
directions=("V","V"))
|
||||
self.add_via_center(layers=stack,
|
||||
offset=lower_pin.center(),
|
||||
directions=("V","V"))
|
||||
|
||||
# BR routes over on M1 first
|
||||
self.add_contact_center(layers=stack,
|
||||
offset = vector(self.br_pin.cx(), upper_pin.cy()))
|
||||
self.add_contact_center(layers=stack,
|
||||
offset = vector(self.br_pin.cx(), lower_pin.cy()))
|
||||
self.add_via_center(layers=stack,
|
||||
offset = vector(self.br_pin.cx(), upper_pin.cy()),
|
||||
directions=("V","V"))
|
||||
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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
@ -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 debug
|
||||
from tech import drc, spice
|
||||
|
|
@ -28,9 +35,8 @@ class ptx(design.design):
|
|||
name += "_c{}".format(num_contacts)
|
||||
# replace periods with underscore for newer spice compatibility
|
||||
name=name.replace('.','_')
|
||||
|
||||
debug.info(3, "creating ptx {0}".format(name))
|
||||
design.design.__init__(self, name)
|
||||
debug.info(3, "create ptx2 structure {0}".format(name))
|
||||
|
||||
self.tx_type = tx_type
|
||||
self.mults = mults
|
||||
|
|
@ -39,6 +45,8 @@ class ptx(design.design):
|
|||
self.connect_poly = connect_poly
|
||||
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()
|
||||
# We must always create ptx layout for 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()
|
||||
|
||||
for pos in source_positions:
|
||||
contact=self.add_contact_center(layers=("active", "contact", "metal1"),
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
contact=self.add_via_center(layers=("active", "contact", "metal1"),
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("H","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text="S",
|
||||
layer="metal1",
|
||||
offset=pos,
|
||||
|
|
@ -339,11 +348,12 @@ class ptx(design.design):
|
|||
|
||||
|
||||
for pos in drain_positions:
|
||||
contact=self.add_contact_center(layers=("active", "contact", "metal1"),
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
contact=self.add_via_center(layers=("active", "contact", "metal1"),
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("H","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text="D",
|
||||
layer="metal1",
|
||||
offset=pos,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from tech import drc
|
||||
from vector import vector
|
||||
import contact
|
||||
from globals import OPTS
|
||||
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.
|
||||
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
|
||||
"""
|
||||
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)
|
||||
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create single column mux cell: {0}".format(name))
|
||||
|
||||
self.bitcell_bl = bitcell_bl
|
||||
self.bitcell_br = bitcell_br
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
pgate.pgate.__init__(self, name)
|
||||
|
||||
def create_netlist(self):
|
||||
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
|
||||
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"),
|
||||
offset=br_out_pin.uc())
|
||||
offset=br_out_pin.uc(),
|
||||
directions=("V","V"))
|
||||
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"),
|
||||
offset=nmos_lower_d_pin.center())
|
||||
offset=nmos_lower_d_pin.center(),
|
||||
directions=("V","V"))
|
||||
|
||||
|
||||
# bl -> nmos_upper/D on metal1
|
||||
# bl_out -> nmos_upper/S on metal2
|
||||
|
|
@ -180,5 +189,9 @@ class single_level_column_mux(design.design):
|
|||
width=self.bitcell.width,
|
||||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 vector3d import vector3d
|
||||
|
||||
|
|
|
|||
|
|
@ -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 string
|
||||
import debug
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
"""
|
||||
A single cell that can be occupied in a given layer, blocked,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from vector3d import vector3d
|
||||
from itertools import tee
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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 pin_layout import pin_layout
|
||||
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
|
||||
# and these are represented by separate lists
|
||||
self.pins = [set(irredundant_pin_set)]
|
||||
self.pins = set(irredundant_pin_set)
|
||||
|
||||
self.router = router
|
||||
# These are the corresponding pin grids for each pin group.
|
||||
|
|
@ -55,7 +62,7 @@ class pin_group:
|
|||
total_string += grids_string
|
||||
|
||||
if self.enclosed:
|
||||
enlosure_string = "\n enclose={}".format(self.enclosures)
|
||||
enclosure_string = "\n enclose={}".format(self.enclosures)
|
||||
total_string += enclosure_string
|
||||
|
||||
total_string += ")"
|
||||
|
|
@ -74,25 +81,6 @@ class pin_group:
|
|||
def is_routed(self):
|
||||
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):
|
||||
"""
|
||||
Remove any pin layout that is contained within another.
|
||||
|
|
@ -135,7 +123,6 @@ class pin_group:
|
|||
|
||||
return new_pin_list
|
||||
|
||||
# FIXME: This relies on some technology parameters from router which is not clean.
|
||||
def compute_enclosures(self):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
This encloses a single pin component with a rectangle
|
||||
starting with the seed and expanding right until blocked
|
||||
and then up until blocked.
|
||||
starting with the seed and expanding dir1 until blocked
|
||||
and then dir2 until blocked.
|
||||
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
|
||||
self.enclosures = self.compute_enclosures()
|
||||
|
||||
for pin_list in self.pins:
|
||||
for pin in pin_list:
|
||||
# Find a connector to every pin and add it to the enclosures
|
||||
for pin in self.pins:
|
||||
|
||||
# If it is contained, it won't need a connector
|
||||
if pin.contained_by_any(self.enclosures):
|
||||
continue
|
||||
# If it is contained, it won't need a connector
|
||||
if pin.contained_by_any(self.enclosures):
|
||||
continue
|
||||
|
||||
# Find a connector in the cardinal directions
|
||||
# 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
|
||||
left_connector = self.find_left_connector(pin, self.enclosures)
|
||||
right_connector = self.find_right_connector(pin, self.enclosures)
|
||||
above_connector = self.find_above_connector(pin, self.enclosures)
|
||||
below_connector = self.find_below_connector(pin, self.enclosures)
|
||||
connector_list = [left_connector, right_connector, above_connector, below_connector]
|
||||
filtered_list = list(filter(lambda x: x!=None, connector_list))
|
||||
if (len(filtered_list)>0):
|
||||
import copy
|
||||
bbox_connector = copy.copy(pin)
|
||||
bbox_connector.bbox(filtered_list)
|
||||
self.enclosures.append(bbox_connector)
|
||||
# Find a connector in the cardinal directions
|
||||
# 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
|
||||
left_connector = self.find_left_connector(pin, self.enclosures)
|
||||
right_connector = self.find_right_connector(pin, self.enclosures)
|
||||
above_connector = self.find_above_connector(pin, self.enclosures)
|
||||
below_connector = self.find_below_connector(pin, self.enclosures)
|
||||
connector_list = [left_connector, right_connector, above_connector, below_connector]
|
||||
filtered_list = list(filter(lambda x: x!=None, connector_list))
|
||||
if (len(filtered_list)>0):
|
||||
import copy
|
||||
bbox_connector = copy.copy(pin)
|
||||
bbox_connector.bbox(filtered_list)
|
||||
self.enclosures.append(bbox_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
|
||||
for pin_list in self.pins:
|
||||
if not self.overlap_any_shape(pin_list, self.enclosures):
|
||||
connector = self.find_smallest_connector(pin_list, self.enclosures)
|
||||
if connector==None:
|
||||
debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures))
|
||||
self.router.write_debug_gds("no_connector.gds")
|
||||
self.enclosures.append(connector)
|
||||
|
||||
if not self.overlap_any_shape(self.pins, self.enclosures):
|
||||
connector = self.find_smallest_connector(pin_list, self.enclosures)
|
||||
if connector==None:
|
||||
debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures))
|
||||
self.router.write_debug_gds("no_connector.gds")
|
||||
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,
|
||||
self.pins,
|
||||
self.grids,
|
||||
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):
|
||||
"""
|
||||
Combine the pin group into this one. This will add to the first item in the pins
|
||||
so this should be used before there are disconnected pins.
|
||||
"""
|
||||
debug.check(len(self.pins)==1,"Don't know which group to add pins to.")
|
||||
self.pins[0].update(*pg.pins) # Join the two lists of pins
|
||||
self.grids |= pg.grids # OR the set of grid locations
|
||||
self.secondary_grids |= pg.secondary_grids
|
||||
augmented_shape_list = set(shape_list)
|
||||
old_connected_set = set()
|
||||
connected_set = set([shape])
|
||||
# Repeat as long as we expand the set
|
||||
while len(connected_set) > len(old_connected_set):
|
||||
old_connected_set = connected_set
|
||||
connected_set = set([shape])
|
||||
for old_shape in old_connected_set:
|
||||
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):
|
||||
"""
|
||||
|
|
@ -579,17 +591,16 @@ class pin_group:
|
|||
partial_set = set()
|
||||
blockage_set = set()
|
||||
|
||||
for pin_list in self.pins:
|
||||
for pin in pin_list:
|
||||
debug.info(2," Converting {0}".format(pin))
|
||||
# Determine which tracks the pin overlaps
|
||||
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin)
|
||||
pin_set.update(sufficient)
|
||||
partial_set.update(insufficient)
|
||||
for pin in self.pins:
|
||||
debug.info(2," Converting {0}".format(pin))
|
||||
# Determine which tracks the pin overlaps
|
||||
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin)
|
||||
pin_set.update(sufficient)
|
||||
partial_set.update(insufficient)
|
||||
|
||||
# Blockages will be a super-set of pins since it uses the inflated pin shape.
|
||||
blockage_in_tracks = self.router.convert_blockage(pin)
|
||||
blockage_set.update(blockage_in_tracks)
|
||||
# Blockages will be a super-set of pins since it uses the inflated pin shape.
|
||||
blockage_in_tracks = self.router.convert_blockage(pin)
|
||||
blockage_set.update(blockage_in_tracks)
|
||||
|
||||
# If we have a blockage, we must remove the grids
|
||||
# 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):
|
||||
#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 pin_list:
|
||||
debug.warning(" Expanding conversion {0}".format(pin))
|
||||
# Determine which tracks the pin overlaps
|
||||
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
|
||||
pin_set.update(sufficient)
|
||||
partial_set.update(insufficient)
|
||||
for pin in self.pins:
|
||||
debug.warning(" Expanding conversion {0}".format(pin))
|
||||
# Determine which tracks the pin overlaps
|
||||
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
|
||||
pin_set.update(sufficient)
|
||||
partial_set.update(insufficient)
|
||||
|
||||
if len(pin_set)==0 and len(partial_set)==0:
|
||||
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," 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()
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 gdsMill
|
||||
from tech import drc,GDS
|
||||
|
|
@ -187,60 +194,64 @@ class router(router_tech):
|
|||
start_time = datetime.now()
|
||||
self.enclose_pins()
|
||||
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):
|
||||
"""
|
||||
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)
|
||||
# # Now reconstruct the new groups
|
||||
# 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)
|
||||
|
||||
# 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]))])
|
||||
# # 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))
|
||||
|
||||
# Now reconstruct the new groups
|
||||
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
|
||||
# return removed_pairs
|
||||
|
||||
|
||||
def separate_adjacent_pins(self, separation):
|
||||
|
|
@ -748,44 +759,10 @@ class router(router_tech):
|
|||
if gid not in group_map:
|
||||
group_map[gid] = pin_group(name=pin_name, pin_set=[], router=self)
|
||||
# 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())
|
||||
|
||||
# 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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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 contact import contact
|
||||
from pin_group import pin_group
|
||||
|
|
|
|||
|
|
@ -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
|
||||
import debug
|
||||
from heapq import heappush,heappop
|
||||
|
|
|
|||
|
|
@ -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 tech
|
||||
from contact import contact
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue