Merge remote-tracking branch 'origin/dev'

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

View File

@ -8,6 +8,8 @@ omit =
*/tests/*
# 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

View File

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

View File

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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import hierarchy_design
import 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"))

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from hierarchy_design import hierarchy_design
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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
"""
This provides a set of useful generic types for the gdsMill interface.
"""
@ -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) + " )"

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import hierarchy_layout
import hierarchy_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 """

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import itertools
import 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",

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import 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."""

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import gdsMill
import tech
import globals

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
from tech import GDS, drc
from vector import vector

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from tech import drc
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:

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import os
import gdsMill
import tech

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import math
import tech

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
class verilog:

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from tech import drc
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]:

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from tech import drc
from tech import layer as techlayer
import debug

View File

@ -1,7 +1,15 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
import 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 """

View File

@ -1,7 +1,15 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
import 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),

View File

@ -1,7 +1,15 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
import 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),

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import contact
import 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."""

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
import debug
import utils

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
import debug
import utils

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
import debug
import utils

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import 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):

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import os
import debug
import globals

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys,re,shutil
import debug
import tech

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import re
import debug
from globals import OPTS

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys,re,shutil
import 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:

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys,re,shutil
from design import design
import debug

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import os,sys,re
import debug
import math

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
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']

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
from tech import drc, parameter, spice
from abc import ABC, abstractmethod

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys,re,shutil
import 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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys
import tech
from .stimuli import *

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys,re,shutil
from design import design
import debug

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
"""
This file generates simple spice cards for simulation. There are
various functions that can be be used to generate stimulus for other

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
from math import log
import re

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys,re,shutil
import debug
import tech

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from pathlib import Path
import glob
import os

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from table_gen import *
import os
import base64

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
#!/usr/bin/env python3
"""
This is a script to load data from the characterization and layout processes into

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
class table_gen:
"""small library of functions to generate the html tables"""

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import os
import 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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
from drc_value import *
from drc_lut import *

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
class drc_lut():

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
class drc_value():
"""

View File

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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
"""
This is called globals.py, but it actually parses all the arguments and performs
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).")

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys
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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys
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)

View File

@ -1,9 +1,17 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import 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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from math import log
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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import design
from tech import drc

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import globals
import design
from math import log

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import design
from tech import drc

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import design
from tech import drc,parameter

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import design
from tech import drc

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import design
from tech import drc

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import design
from tech import drc

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from tech import drc
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):

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import 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)

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from tech import drc
import debug
import design

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from tech import drc
import debug
import design

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys
from tech import drc, parameter
import debug

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
import debug
from tech import drc

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import 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(),

View File

@ -1,7 +1,15 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
import 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"""

View File

@ -1,9 +1,17 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from math import log
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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import design
import utils

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
from tech import drc
import design

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from tech import drc, parameter
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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import design
import utils

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from math import log
import design
from tech import drc

View File

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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import optparse
import getpass
import os

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
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()

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
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):

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import 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")

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import contact
import 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,

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import contact
import 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

View File

@ -1,18 +1,28 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
import 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")

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import contact
import 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])

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import contact
import 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,

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import pgate
import 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,

View File

@ -1,31 +1,42 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import contact
import 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):
"""

View File

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

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import design
import 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,

View File

@ -1,12 +1,20 @@
import design
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import pgate
import debug
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)

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from enum import Enum
from vector3d import vector3d

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import numpy as np
import string
import debug

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
class grid_cell:
"""
A single cell that can be occupied in a given layer, blocked,

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import debug
from vector3d import vector3d
from itertools import tee

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
"""
Some utility functions for sets of grid cells.
"""

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from direction import direction
from 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()

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import sys
import 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):
"""

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from tech import drc,layer
from contact import contact
from pin_group import pin_group

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
from itertools import tee
import debug
from heapq import heappush,heappop

View File

@ -1,3 +1,10 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
#
import gdsMill
import tech
from contact import contact

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