Merged and addressed conflict in delay.py

This commit is contained in:
Hunter Nichols 2021-01-06 01:37:16 -08:00
commit cd84cf1973
86 changed files with 916 additions and 17029 deletions

View File

@ -79,25 +79,6 @@ You may get the entire [FreePDK45 PDK here][FreePDK45].
If you are using [SCMOS], you should install [Magic] and [Netgen].
We have included the most recent SCN4M_SUBM design rules from [Qflow].
## Docker Image
**WARNING! Some OpenRAM dependency tools installed in the Docker image are out-of-date.**
We have a pre-configured Ubuntu [Docker](https://www.docker.com/) image
available that has all tools installed for the [SCMOS] process. It is
available at [docker hub](https://hub.docker.com/r/vlsida/openram-ubuntu).
Please see
[our README.md](https://github.com/VLSIDA/openram-docker-images/blob/master/README.md)
for information on how to use this docker image.
## Vagrant Image
We have a pre-configured Ubuntu [Vagrant](https://www.vagrantup.com/) image
available that has all tools installed for the [SCMOS] process.
Please see
[our README.md](https://github.com/VLSIDA/openram-vagrant-image/blob/master/README.md)
for information on how to use this image.
# Basic Usage
Once you have defined the environment, you can run OpenRAM from the command line
@ -142,7 +123,6 @@ python3 $OPENRAM_HOME/openram.py myconfig
You can see all of the options for the configuration file in
$OPENRAM\_HOME/options.py
# Unit Tests
Regression testing performs a number of tests for all modules in OpenRAM.

View File

@ -446,7 +446,7 @@ class path(geometry):
class label(geometry):
"""Represents a text label"""
def __init__(self, text, lpp, offset, zoom=-1):
def __init__(self, text, lpp, offset, zoom=None):
"""Initializes a text label for specified layer"""
super().__init__()
self.name = "label"
@ -455,8 +455,11 @@ class label(geometry):
self.layerPurpose = lpp[1]
self.offset = vector(offset).snap_to_grid()
if zoom<0:
self.zoom = tech.GDS["zoom"]
if not zoom:
try:
self.zoom = tech.GDS["zoom"]
except:
self.zoom = None
else:
self.zoom = zoom

View File

@ -52,10 +52,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
# Do not run if disabled in options.
elif (OPTS.inline_lvsdrc or force_check or final_verification):
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name)
self.lvs_write(tempspice)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
self.gds_write(tempgds)
tempspice = "{}.sp".format(self.name)
self.lvs_write("{0}{1}".format(OPTS.openram_temp, tempspice))
tempgds = "{}.gds".format(self.name)
self.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
# Final verification option does not allow nets to be connected by label.
self.drc_errors = verify.run_drc(self.cell_name, tempgds, tempspice, extract=True, final_verification=final_verification)
self.lvs_errors = verify.run_lvs(self.cell_name, tempgds, tempspice, final_verification=final_verification)
@ -81,10 +81,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only:
return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, self.name)
self.lvs_write(tempspice)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, self.cell_name)
self.gds_write(tempgds)
tempspice = "{}.sp".format(self.name)
self.lvs_write("{0}{1}".format(OPTS.openram_temp, tempspice))
tempgds = "{}.gds".format(self.cell_name)
self.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
num_errors = verify.run_drc(self.cell_name, tempgds, tempspice, final_verification=final_verification)
debug.check(num_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
@ -101,10 +101,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only:
return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, self.cell_name)
self.lvs_write(tempspice)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, self.name)
self.gds_write(tempgds)
tempspice = "{}.sp".format(self.cell_name)
self.lvs_write("{0}{1}".format(OPTS.openram_temp, tempspice))
tempgds = "{}.gds".format(self.name)
self.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
debug.check(num_errors == 0,
"LVS failed for {0} with {1} error(s)".format(self.cell_name,

View File

@ -236,7 +236,7 @@ class layout():
# This is commented out for runtime reasons
# debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
return self.insts[-1]
def get_inst(self, name):
""" Retrieve an instance by name """
for inst in self.insts:
@ -244,6 +244,19 @@ class layout():
return inst
return None
def add_flat_inst(self, name, mod, offset=[0, 0]):
""" Copies all of the items in instance into this module """
for item in mod.objs:
item.offset += offset
self.objs.append(item)
for item in mod.insts:
item.offset += offset
self.insts.append(item)
debug.check(len(item.mod.pins) == 0, "Cannot add flat instance with subinstances.")
self.connect_inst([])
debug.info(3, "adding flat instance {}".format(name))
return None
def add_rect(self, layer, offset, width=None, height=None):
"""
Adds a rectangle on a given layer,offset with width and height
@ -454,6 +467,23 @@ class layout():
"""
self.pin_map[text] = set()
def remove_layout_pins(self):
"""
Delete all the layout pins
"""
self.pin_map = {}
def replace_layout_pin(self, text, pin):
"""
Remove the old pin and replace with a new one
"""
self.remove_layout_pin(text)
self.add_layout_pin(text=text,
layer=pin.layer,
offset=pin.ll(),
width=pin.width(),
height=pin.height())
def add_layout_pin(self, text, layer, offset, width=None, height=None):
"""
Create a labeled pin
@ -498,7 +528,7 @@ class layout():
offset=offset + vector(0.5 * width,
0.5 * height))
def add_label(self, text, layer, offset=[0, 0], zoom=-1):
def add_label(self, text, layer, offset=[0, 0], zoom=None):
"""Adds a text label on the given layer,offset, and zoom level"""
debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset))
lpp = techlayer[layer]
@ -976,7 +1006,7 @@ class layout():
(horizontal_layer, via_layer, vertical_layer) = layer_stack
if horizontal:
route_layer = vertical_layer
bys_layer = horizontal_layer
bus_layer = horizontal_layer
else:
route_layer = horizontal_layer
bus_layer = vertical_layer
@ -1078,18 +1108,24 @@ class layout():
"""
import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self)
self.add_inst(cr.name, cr)
self.connect_inst([])
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
# with no active devices.
# self.add_inst(cr.name, cr)
# self.connect_inst([])
self.add_flat_inst(cr.name, cr)
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
"""
Wrapper to create a horizontal channel route
"""
import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
self.add_inst(cr.name, cr)
self.connect_inst([])
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
# with no active devices.
# self.add_inst(cr.name, cr)
# self.connect_inst([])
self.add_flat_inst(cr.name, cr)
def add_boundary(self, ll=vector(0, 0), ur=None):
""" Add boundary for debugging dimensions """
if OPTS.netlist_only:
@ -1172,6 +1208,21 @@ class layout():
elif add_vias:
self.add_power_pin(name, pin.center(), start_layer=pin.layer)
def add_io_pin(self, instance, pin_name, new_name="", start_layer=None):
"""
Add a signle input or output pin up to metal 3.
"""
pin = instance.get_pin(pin_name)
if new_name == "":
new_name = pin_name
if not start_layer:
start_layer = pin.layer
# Just use the power pin function for now to save code
self.add_power_pin(name=new_name, loc=pin.center(), start_layer=start_layer)
def add_power_pin(self, name, loc, size=[1, 1], directions=None, start_layer="m1"):
"""
Add a single power pin from the lowest power_grid layer down to M1 (or li) at

View File

@ -380,20 +380,26 @@ class pin_layout:
label_purpose = purpose
newLayout.addBox(layerNumber=layer_num,
purposeNumber=pin_purpose,
purposeNumber=purpose,
offsetInMicrons=self.ll(),
width=self.width(),
height=self.height(),
center=False)
# Add the tet in the middle of the pin.
# Draw a second pin shape too
if pin_purpose != purpose:
newLayout.addBox(layerNumber=layer_num,
purposeNumber=pin_purpose,
offsetInMicrons=self.ll(),
width=self.width(),
height=self.height(),
center=False)
# Add the text in the middle of the pin.
# This fixes some pin label offsetting when GDS gets
# imported into Magic.
newLayout.addText(text=self.name,
layerNumber=layer_num,
purposeNumber=label_purpose,
offsetInMicrons=self.center(),
magnification=GDS["zoom"],
rotate=None)
offsetInMicrons=self.center())
def compute_overlap(self, other):
""" Calculate the rectangular overlap of two rectangles. """

View File

@ -0,0 +1,22 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California 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 cell_properties as props
import bitcell_base
class col_cap_bitcell_1port(bitcell_base.bitcell_base):
"""
Column end cap cell.
"""
def __init__(self, name="col_cap_bitcell_1port"):
bitcell_base.bitcell_base.__init__(self, name, prop=props.col_cap_1port)
debug.info(2, "Create col_cap bitcell 1 port object")
self.no_instances = True

View File

@ -0,0 +1,22 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California 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 cell_properties as props
import bitcell_base
class row_cap_bitcell_1port(bitcell_base.bitcell_base):
"""
Row end cap cell.
"""
def __init__(self, name="row_cap_bitcell_1port"):
bitcell_base.bitcell_base.__init__(self, name, prop=props.row_cap_1port)
debug.info(2, "Create row_cap bitcell 1 port object")
self.no_instances = True

View File

@ -11,7 +11,7 @@ nominal_corner_only = True
route_supplies = True
check_lvsdrc = True
perimeter_pins = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,

View File

@ -11,8 +11,8 @@ num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corner_only = True
route_supplies = False
check_lvsdrc = False
route_supplies = "tree"
check_lvsdrc = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False

View File

@ -1,4 +1,4 @@
import math
class GdsStructure:
"""Class represent a GDS Structure Object"""
@ -15,6 +15,7 @@ class GdsStructure:
self.nodes=[]
self.boxes=[]
class GdsBoundary:
"""Class represent a GDS Boundary Object"""
def __init__(self):
@ -24,6 +25,7 @@ class GdsBoundary:
self.purposeLayer=0
self.coordinates=""
class GdsPath:
"""Class represent a GDS Path Object"""
def __init__(self):
@ -112,6 +114,7 @@ class GdsPath:
lastY = y
return boundaryEquivalent
class GdsSref:
"""Class represent a GDS structure reference Object"""
def __init__(self):
@ -123,6 +126,7 @@ class GdsSref:
self.rotateAngle=""
self.coordinates=""
class GdsAref:
"""Class represent a GDS array reference Object"""
def __init__(self):
@ -134,6 +138,7 @@ class GdsAref:
self.rotateAngle=""
self.coordinates=""
class GdsText:
"""Class represent a GDS text Object"""
def __init__(self):
@ -150,6 +155,7 @@ class GdsText:
self.coordinates=""
self.textString = ""
class GdsNode:
"""Class represent a GDS Node Object"""
def __init__(self):
@ -159,6 +165,7 @@ class GdsNode:
self.nodeType=""
self.coordinates=""
class GdsBox:
"""Class represent a GDS Box Object"""
def __init__(self):

View File

@ -1,26 +1,25 @@
from .gdsPrimitives import *
from datetime import *
#from mpmath import matrix
#from numpy import matrix
import numpy as np
#import gdsPrimitives
import math
import debug
class VlsiLayout:
"""Class represent a hierarchical layout"""
def __init__(self, name=None, units=(0.001,1e-9), libraryName = "DEFAULT.DB", gdsVersion=5):
#keep a list of all the structures in this layout
def __init__(self, name=None, units=(0.001,1e-9), libraryName="DEFAULT.DB", gdsVersion=5):
# keep a list of all the structures in this layout
self.units = units
#print(units)
# print(units)
modDate = datetime.now()
self.structures=dict()
self.layerNumbersInUse = []
self.debug = False
if name:
#take the root structure and copy it to a new structure with the new name
# take the root structure and copy it to a new structure with the new name
self.rootStructureName=self.padText(name)
#create the ROOT structure
# create the ROOT structure
self.structures[self.rootStructureName] = GdsStructure()
self.structures[self.rootStructureName].name = name
self.structures[self.rootStructureName].createDate = (modDate.year,
@ -36,7 +35,7 @@ class VlsiLayout:
modDate.minute,
modDate.second)
self.info = dict() #information gathered from the GDSII header
self.info = dict() # information gathered from the GDSII header
self.info['units']=self.units
self.info['dates']=(modDate.year,
modDate.month,
@ -53,12 +52,13 @@ class VlsiLayout:
self.info['libraryName']=libraryName
self.info['gdsVersion']=gdsVersion
self.xyTree = [] #This will contain a list of all structure names
#expanded to include srefs / arefs separately.
#each structure will have an X,Y,offset, and rotate associated
#with it. Populate via traverseTheHierarchy method.
# This will contain a list of all structure names
# expanded to include srefs / arefs separately.
# each structure will have an X,Y,offset, and rotate associated
# with it. Populate via traverseTheHierarchy method.
self.xyTree = []
#temp variables used in delegate functions
# temp variables used in delegate functions
self.tempCoordinates=None
self.tempPassFail = True
@ -68,12 +68,12 @@ class VlsiLayout:
self.pins = {}
def rotatedCoordinates(self,coordinatesToRotate,rotateAngle):
#helper method to rotate a list of coordinates
# helper method to rotate a list of coordinates
angle=math.radians(float(0))
if(rotateAngle):
angle = math.radians(float(rotateAngle))
coordinatesRotate = [] #this will hold the rotated values
coordinatesRotate = [] # this will hold the rotated values
for coordinate in coordinatesToRotate:
# This is the CCW rotation matrix
newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle)
@ -82,53 +82,51 @@ class VlsiLayout:
return coordinatesRotate
def rename(self,newName):
#take the root structure and copy it to a new structure with the new name
# take the root structure and copy it to a new structure with the new name
self.structures[newName] = self.structures[self.rootStructureName]
self.structures[newName].name = self.padText(newName)
#and delete the old root
# and delete the old root
del self.structures[self.rootStructureName]
self.rootStructureName = newName
#repopulate the 2d map so drawing occurs correctly
# repopulate the 2d map so drawing occurs correctly
del self.xyTree[:]
self.populateCoordinateMap()
def newLayout(self,newName):
#if (newName == "" | newName == 0):
# print("ERROR: vlsiLayout.py:newLayout newName is null")
# if (newName == "" | newName == 0):
# print("ERROR: vlsiLayout.py:newLayout newName is null")
#make sure the newName is a multiple of 2 characters
#if(len(newName)%2 == 1):
#pad with a zero
#newName = newName + '\x00'
#take the root structure and copy it to a new structure with the new name
#self.structures[newName] = self.structures[self.rootStructureName]
# make sure the newName is a multiple of 2 characters
# if(len(newName)%2 == 1):
# pad with a zero
# newName = newName + '\x00'
# take the root structure and copy it to a new structure with the new name
# self.structures[newName] = self.structures[self.rootStructureName]
modDate = datetime.now()
self.structures[newName] = GdsStructure()
self.structures[newName].name = newName
self.rootStructureName = newName
self.rootStructureName=newName
#create the ROOT structure
# create the ROOT structure
self.structures[self.rootStructureName] = GdsStructure()
#self.structures[self.rootStructureName].name = name
# self.structures[self.rootStructureName].name = name
self.structures[self.rootStructureName].createDate = (modDate.year,
modDate.month,
modDate.day,
modDate.hour,
modDate.minute,
modDate.second)
modDate.month,
modDate.day,
modDate.hour,
modDate.minute,
modDate.second)
self.structures[self.rootStructureName].modDate = (modDate.year,
modDate.month,
modDate.day,
modDate.hour,
modDate.minute,
modDate.second)
modDate.month,
modDate.day,
modDate.hour,
modDate.minute,
modDate.second)
#repopulate the 2d map so drawing occurs correctly
@ -155,47 +153,50 @@ class VlsiLayout:
debug.check(len(structureNames)==1,"Multiple possible root structures in the layout: {}".format(str(structureNames)))
self.rootStructureName = structureNames[0]
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None,
transformPath = [], rotateAngle = 0, transFlags = [0,0,0], coordinates = (0,0)):
#since this is a recursive function, must deal with the default
#parameters explicitly
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction=None,
transformPath=[], rotateAngle=0, transFlags=[0, 0, 0], coordinates=(0, 0)):
# since this is a recursive function, must deal with the default
# parameters explicitly
if startingStructureName == None:
startingStructureName = self.rootStructureName
#set up the rotation matrix
# set up the rotation matrix
if(rotateAngle == None or rotateAngle == ""):
angle = 0
else:
# MRG: Added negative to make CCW rotate 8/29/18
angle = math.radians(float(rotateAngle))
mRotate = np.array([[math.cos(angle),-math.sin(angle),0.0],
[math.sin(angle),math.cos(angle),0.0],
[0.0,0.0,1.0]])
#set up the translation matrix
mRotate = np.array([[math.cos(angle), -math.sin(angle), 0.0],
[math.sin(angle), math.cos(angle), 0.0],
[0.0, 0.0, 1.0]])
# set up the translation matrix
translateX = float(coordinates[0])
translateY = float(coordinates[1])
mTranslate = np.array([[1.0,0.0,translateX],[0.0,1.0,translateY],[0.0,0.0,1.0]])
#set up the scale matrix (handles mirror X)
mTranslate = np.array([[1.0, 0.0, translateX],
[0.0, 1.0, translateY],
[0.0, 0.0, 1.0]])
# set up the scale matrix (handles mirror X)
scaleX = 1.0
if(transFlags[0]):
if (transFlags[0]):
scaleY = -1.0
else:
scaleY = 1.0
mScale = np.array([[scaleX,0.0,0.0],[0.0,scaleY,0.0],[0.0,0.0,1.0]])
#we need to keep track of all transforms in the hierarchy
#when we add an element to the xy tree, we apply all transforms from the bottom up
mScale = np.array([[scaleX, 0.0, 0.0],
[0.0, scaleY, 0.0],
[0.0, 0.0, 1.0]])
# we need to keep track of all transforms in the hierarchy
# when we add an element to the xy tree, we apply all transforms from the bottom up
transformPath.append((mRotate,mScale,mTranslate))
if delegateFunction != None:
delegateFunction(startingStructureName, transformPath)
#starting with a particular structure, we will recursively traverse the tree
#********might have to set the recursion level deeper for big layouts!
# starting with a particular structure, we will recursively traverse the tree
# ********might have to set the recursion level deeper for big layouts!
try:
if(len(self.structures[startingStructureName].srefs)>0): #does this structure reference any others?
#if so, go through each and call this function again
#if not, return back to the caller (caller can be this function)
# if so, go through each and call this function again
# if not, return back to the caller (caller can be this function)
for sref in self.structures[startingStructureName].srefs:
#here, we are going to modify the sref coordinates based on the parent objects rotation
# here, we are going to modify the sref coordinates based on the parent objects rotation
self.traverseTheHierarchy(startingStructureName = sref.sName,
delegateFunction = delegateFunction,
transformPath = transformPath,
@ -205,8 +206,8 @@ class VlsiLayout:
except KeyError:
debug.error("Could not find structure {} in GDS file.".format(startingStructureName),-1)
#MUST HANDLE AREFs HERE AS WELL
#when we return, drop the last transform from the transformPath
# MUST HANDLE AREFs HERE AS WELL
# when we return, drop the last transform from the transformPath
del transformPath[-1]
return
@ -218,7 +219,6 @@ class VlsiLayout:
for layerNumber in self.layerNumbersInUse:
self.processLabelPins((layerNumber, None))
def populateCoordinateMap(self):
def addToXyTree(startingStructureName = None,transformPath = None):
uVector = np.array([[1.0],[0.0],[0.0]]) #start with normal basis vectors
@ -281,8 +281,6 @@ class VlsiLayout:
self.newLayout(newRoot)
self.rootStructureName = newRoot
def addInstance(self,layoutToAdd,nameOfLayout=0,offsetInMicrons=(0,0),mirror=None,rotate=None):
"""
Method to insert one layout into another at a particular offset.
@ -406,7 +404,7 @@ class VlsiLayout:
#add the sref to the root structure
self.structures[self.rootStructureName].paths.append(pathToAdd)
def addText(self, text, layerNumber=0, purposeNumber=0, offsetInMicrons=(0,0), magnification=0.1, rotate = None):
def addText(self, text, layerNumber=0, purposeNumber=0, offsetInMicrons=(0,0), magnification=None, rotate=None):
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
textToAdd = GdsText()
textToAdd.drawingLayer = layerNumber
@ -415,7 +413,8 @@ class VlsiLayout:
textToAdd.transFlags = [0,0,0]
textToAdd.textString = self.padText(text)
#textToAdd.transFlags[1] = 1
textToAdd.magFactor = magnification
if magnification:
textToAdd.magFactor = magnification
if rotate:
#textToAdd.transFlags[2] = 1
textToAdd.rotateAngle = rotate

View File

@ -19,7 +19,7 @@ import re
import copy
import importlib
VERSION = "1.1.7"
VERSION = "1.1.9"
NAME = "OpenRAM v{}".format(VERSION)
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
@ -295,6 +295,14 @@ def read_config(config_file, is_unit_test=True):
dir_name = os.path.dirname(config_file)
module_name = os.path.basename(config_file)
# Check that the module name adheres to Python's module naming conventions.
# This will assist the user in interpreting subsequent errors in loading
# the module. Valid Python module naming is described here:
# https://docs.python.org/3/reference/simple_stmts.html#the-import-statement
if not module_name.isidentifier():
debug.error("Configuration file name is not a valid Python module name: "
"{0}. It should be a valid identifier.".format(module_name))
# Prepend the path to avoid if we are using the example config
sys.path.insert(0, dir_name)
# Import the configuration file of which modules to use
@ -574,9 +582,8 @@ def report_status():
debug.print_raw("Technology: {0}".format(OPTS.tech_name))
total_size = OPTS.word_size*OPTS.num_words*OPTS.num_banks
debug.print_raw("Total size: {} bits".format(total_size))
if total_size >= 2**14:
debug.warning("Requesting such a large memory size ({0}) will have a large run-time. ".format(total_size) +
"Consider using multiple smaller banks.")
if total_size >= 2**14 and not OPTS.analytical_delay:
debug.warning("Characterizing large memories ({0}) will have a large run-time. ".format(total_size))
debug.print_raw("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
OPTS.num_words,
OPTS.num_banks))

View File

@ -391,19 +391,17 @@ class replica_bitcell_array(bitcell_base_array):
for bit in range(self.rbl[1]):
dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul()
self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset,
mirror="MX" if bit % 2 else "R0")
mirror="MX" if (self.row_size + bit) % 2 else "R0")
def add_end_caps(self):
""" Add dummy cells or end caps around the array """
# FIXME: These depend on the array size itself
# Far top dummy row (first row above array is NOT flipped)
flip_dummy = self.rbl[1] % 2
# Far top dummy row (first row above array is NOT flipped if even number of rows)
flip_dummy = (self.row_size + self.rbl[1]) % 2
dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul()
self.dummy_row_insts[1].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# FIXME: These depend on the array size itself
# Far bottom dummy row (first row below array IS flipped)
flip_dummy = (self.rbl[0] + 1) % 2
dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset
@ -422,9 +420,8 @@ class replica_bitcell_array(bitcell_base_array):
def add_layout_pins(self):
""" Add the layout pins """
#All wordlines
#Main array wl and bl/br
# All wordlines
# Main array wl and bl/br
for pin_name in self.all_wordline_names:
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:

View File

@ -180,7 +180,7 @@ class replica_column(bitcell_base_array):
for port in self.all_ports:
for row in range(row_range_min, row_range_max):
wl_pin = self.cell_inst[row].get_pin(self.cell.get_wl_name(port))
self.add_layout_pin(text="wl_{0}_{1}".format(port, row),
self.add_layout_pin(text="wl_{0}_{1}".format(port, row - row_range_min),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,

View File

@ -11,7 +11,7 @@ import math
from sram_factory import factory
from vector import vector
from globals import OPTS
from tech import layer
class write_mask_and_array(design.design):
"""
@ -95,9 +95,9 @@ class write_mask_and_array(design.design):
if not self.offsets:
self.offsets = []
for i in range(self.columns):
self.offsets.append(i * self.driver_spacing)
self.offsets.append((i + self.write_size - 1) * self.driver_spacing)
self.width = self.offsets[-1] + self.driver_spacing
self.width = self.offsets[-1] + self.bitcell.width
self.height = self.and2.height
write_bits = self.columns / self.num_wmasks
@ -138,12 +138,13 @@ class write_mask_and_array(design.design):
self.add_via_stack_center(from_layer=en_pin.layer,
to_layer="m3",
offset=en_pos)
for supply in ["gnd", "vdd"]:
supply_pin=self.and2_insts[i].get_pin(supply)
self.add_power_pin(supply, supply_pin.center(), start_layer=supply_pin.layer)
for supply in ["gnd", "vdd"]:
supply_pin_left = self.and2_insts[0].get_pin(supply)
supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply)
self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()])
supply_pin = self.and2_insts[0].get_pin(supply)
supply_pin_yoffset = supply_pin.cy()
left_loc = vector(0, supply_pin_yoffset)
right_loc = vector(self.width, supply_pin_yoffset)
self.add_path(supply_pin.layer, [left_loc, right_loc])
self.add_power_pin(supply, left_loc, start_layer=supply_pin.layer)
self.add_power_pin(supply, right_loc, start_layer=supply_pin.layer)

View File

@ -94,7 +94,7 @@ class options(optparse.Values):
# When enabled, layout is not generated (and no DRC or LVS are performed)
netlist_only = False
# Whether we should do the final power routing
route_supplies = False
route_supplies = "tree"
# This determines whether LVS and DRC is checked at all.
check_lvsdrc = False
# This determines whether LVS and DRC is checked for every submodule.
@ -145,7 +145,7 @@ class options(optparse.Values):
# run (doesn't purge on errors, anyhow)
# Route the input/output pins to the perimeter
perimeter_pins = False
perimeter_pins = True
keep_temp = False

View File

@ -158,9 +158,9 @@ class ptx(design.design):
# TEMP FIX: Use old device names if using Calibre.
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult=1".format("nshort" if self.tx_type == "nmos" else "pshort",
self.mults,
self.tx_width,
drc("minwidth_poly"))
self.mults,
self.tx_width,
drc("minwidth_poly"))
elif cell_props.ptx.model_is_subckt:
# sky130 requires mult parameter too
self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2}u l={3}u".format(spice[self.tx_type],

25
compiler/processGDS.py Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env python3
import sys
from gdsMill import gdsMill
if len(sys.argv) < 2:
print("Usage: {0} in.gds out.gds".format(sys.argv[0]))
sys.exit(1)
in_gds_file = sys.argv[1]
out_gds_file = sys.argv[2]
layout = gdsMill.VlsiLayout()
reader = gdsMill.Gds2reader(layout)
reader.loadFromFile(in_gds_file)
struct = layout.structures[layout.rootStructureName]
# Do something to the structure
for text in struct.texts:
print(text.textString)
text.magFactor=""
writer = gdsMill.Gds2writer(layout)
writer.writeToFile(out_gds_file)

View File

@ -1,7 +0,0 @@
[globals/init_openram]: Initializing OpenRAM...
[globals/setup_paths]: Setting up paths...
[globals/setup_paths]: Temporary files saved in /home/jesse/output/
[globals/read_config]: Configuration file is /home/jesse/skywater-tech/riscv_1k_s8.py
[globals/read_config]: Output saved in /home/jesse/openram/compiler/riscv/
[globals/import_tech]: Importing technology: s8
[globals/import_tech]: Adding technology path: /home/jesse/openram/technology

File diff suppressed because it is too large Load Diff

View File

@ -1,61 +0,0 @@
[globals/init_openram]: Initializing OpenRAM...
[globals/setup_paths]: Setting up paths...
[globals/setup_paths]: Temporary files saved in /home/jesse/output/
[globals/read_config]: Configuration file is /home/jesse/skywater-tech/riscv_1k_s8.py
[globals/read_config]: Output saved in /home/jesse/openram/compiler/riscv/
[globals/import_tech]: Importing technology: sky130
[globals/import_tech]: Adding technology path: /home/jesse/openram/technology
[globals/init_paths]: Creating temp directory: /home/jesse/output/
[verify/<module>]: Initializing verify...
[verify/<module>]: LVS/DRC/PEX disabled.
[characterizer/<module>]: Initializing characterizer...
[characterizer/<module>]: Analytical model enabled.
[globals/setup_bitcell]: Using bitcell: bitcell_1rw_1r
|==============================================================================|
|========= OpenRAM v1.1.5 =========|
|========= =========|
|========= VLSI Design and Automation Lab =========|
|========= Computer Science and Engineering Department =========|
|========= University of California Santa Cruz =========|
|========= =========|
|========= Usage help: openram-user-group@ucsc.edu =========|
|========= Development help: openram-dev-group@ucsc.edu =========|
|========= Temp dir: /home/jesse/output/ =========|
|========= See LICENSE for license info =========|
|==============================================================================|
** Start: 06/25/2020 07:53:43
Technology: sky130
Total size: 8192 bits
Word size: 32
Words: 256
Banks: 1
Write size: 8
RW ports: 1
R-only ports: 1
W-only ports: 0
DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to do inline checking).
DRC/LVS/PEX is disabled (check_lvsdrc=True to enable).
Characterization is disabled (using analytical delay models) (analytical_delay=False to simulate).
[bitcell_1rw_1r/__init__]: Create bitcell with 1RW and 1R Port
[sram_config/recompute_sizes]: Recomputing with words per row: 2
[sram_config/recompute_sizes]: Rows: 128 Cols: 64
[sram_config/recompute_sizes]: Row addr size: 7 Col addr size: 1 Bank addr size: 8
Words per row: 2
Output files are:
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.sp
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.v
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.lib
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.py
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.html
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.log
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.lef
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.gds
[sram/__init__]: create sram of size 32 with 256 num of words 1 banks
[dff_array/__init__]: Creating row_addr_dff rows=7 cols=1
[dff_array/__init__]: Creating col_addr_dff rows=1 cols=1
[dff_array/__init__]: Creating data_dff rows=1 cols=32
[dff_array/__init__]: Creating wmask_dff rows=1 cols=4
[bank/__init__]: create sram of size 32 with 256 words
[port_data/__init__]: create data port of size 32 with 2 words per row
[precharge/__init__]: creating precharge cell precharge
[pgate/bin_width]: binning pmos tx, target: 0.44999999999999996, found 0.55 x 1 = 0.55

View File

@ -5,12 +5,11 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import numpy as np
import string
import debug
from vector3d import vector3d
from grid_cell import grid_cell
class grid:
"""
A two layer routing map. Each cell can be blocked in the vertical
@ -23,7 +22,6 @@ class grid:
NONPREFERRED_COST = 4
PREFERRED_COST = 1
def __init__(self, ll, ur, track_width):
""" Initialize the map and define the costs. """
@ -33,12 +31,12 @@ class grid:
self.track_width = track_width
self.track_widths = [self.track_width, self.track_width, 1.0]
self.track_factor = [1/self.track_width, 1/self.track_width, 1.0]
self.track_factor = [1 / self.track_width, 1 / self.track_width, 1.0]
# The bounds are in grids for this
# This is really lower left bottom layer and upper right top layer in 3D.
self.ll = vector3d(ll.x,ll.y,0).scale(self.track_factor).round()
self.ur = vector3d(ur.x,ur.y,1).scale(self.track_factor).round()
self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round()
self.ur = vector3d(ur.x, ur.y, 1).scale(self.track_factor).round()
# let's leave the map sparse, cells are created on demand to reduce memory
self.map={}
@ -46,18 +44,18 @@ class grid:
def add_all_grids(self):
for x in range(self.ll.x, self.ur.x, 1):
for y in range(self.ll.y, self.ur.y, 1):
self.add_map(vector3d(x,y,0))
self.add_map(vector3d(x,y,1))
self.add_map(vector3d(x, y, 0))
self.add_map(vector3d(x, y, 1))
def set_blocked(self,n,value=True):
def set_blocked(self, n, value=True):
if not isinstance(n, vector3d):
for item in n:
self.set_blocked(item,value)
self.set_blocked(item, value)
else:
self.add_map(n)
self.map[n].blocked=value
def is_blocked(self,n):
def is_blocked(self, n):
if not isinstance(n, vector3d):
for item in n:
if self.is_blocked(item):
@ -68,11 +66,10 @@ class grid:
self.add_map(n)
return self.map[n].blocked
def set_path(self,n,value=True):
if isinstance(n, (list,tuple,set,frozenset)):
def set_path(self, n, value=True):
if isinstance(n, (list, tuple, set, frozenset)):
for item in n:
self.set_path(item,value)
self.set_path(item, value)
else:
self.add_map(n)
self.map[n].path=value
@ -81,47 +78,89 @@ class grid:
for k in self.map:
self.map[k].blocked=False
def set_source(self,n,value=True):
def set_source(self, n, value=True):
if not isinstance(n, vector3d):
for item in n:
self.set_source(item,value)
self.set_source(item, value)
else:
self.add_map(n)
self.map[n].source=value
self.source.add(n)
def set_target(self,n,value=True):
def set_target(self, n, value=True):
if not isinstance(n, vector3d):
for item in n:
self.set_target(item,value)
self.set_target(item, value)
else:
self.add_map(n)
self.map[n].target=value
self.target.add(n)
def add_source(self,track_list,value=True):
debug.info(3,"Adding source list={0}".format(str(track_list)))
def add_source(self, track_list, value=True):
debug.info(3, "Adding source list={0}".format(str(track_list)))
for n in track_list:
debug.info(4,"Adding source ={0}".format(str(n)))
self.set_source(n,value)
self.set_blocked(n,False)
debug.info(4, "Adding source ={0}".format(str(n)))
self.set_source(n, value)
self.set_blocked(n, False)
def add_target(self,track_list,value=True):
debug.info(3,"Adding target list={0}".format(str(track_list)))
def add_target(self, track_list, value=True):
debug.info(3, "Adding target list={0}".format(str(track_list)))
for n in track_list:
debug.info(4,"Adding target ={0}".format(str(n)))
self.set_target(n,value)
self.set_blocked(n,False)
debug.info(4, "Adding target ={0}".format(str(n)))
self.set_target(n, value)
self.set_blocked(n, False)
def is_target(self,point):
def add_perimeter_target(self, side="all", value=True):
debug.info(3, "Adding perimeter target")
# Add the left/right columns
if side=="all" or side=="left":
x = self.ll.x
for y in range(self.ll.y, self.ur.y, 1):
n = vector3d(x, y, 0)
self.set_target(n, value)
self.set_blocked(n, False)
n = vector3d(x, y, 1)
self.set_target(n, value)
self.set_blocked(n, False)
if side=="all" or side=="right":
x = self.ur.x
for y in range(self.ll.y, self.ur.y, 1):
n = vector3d(x, y, 0)
self.set_target(n, value)
self.set_blocked(n, False)
n = vector3d(x, y, 1)
self.set_target(n, value)
self.set_blocked(n, False)
if side=="all" or side=="bottom":
y = self.ll.y
for x in range(self.ll.x, self.ur.x, 1):
n = vector3d(x, y, 0)
self.set_target(n, value)
self.set_blocked(n, False)
n = vector3d(x, y, 1)
self.set_target(n, value)
self.set_blocked(n, False)
if side=="all" or side=="top":
y = self.ur.y
for x in range(self.ll.x, self.ur.x, 1):
n = vector3d(x, y, 0)
self.set_target(n, value)
self.set_blocked(n, False)
n = vector3d(x, y, 1)
self.set_target(n, value)
self.set_blocked(n, False)
def is_target(self, point):
"""
Point is in the target set, so we are done.
"""
return point in self.target
def add_map(self,n):
def add_map(self, n):
"""
Add a point to the map if it doesn't exist.
"""
@ -132,8 +171,7 @@ class grid:
if n not in self.map:
self.map[n]=grid_cell()
def block_path(self,path):
def block_path(self, path):
"""
Mark the path in the routing grid as blocked.
Also unsets the path flag.

View File

@ -5,13 +5,12 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
from vector3d import vector3d
from itertools import tee
from grid import grid
from grid_cell import grid_cell
from direction import direction
class grid_path:
"""
A grid path is a list of lists of grid cells.
@ -29,8 +28,7 @@ class grid_path:
self.pathlist = []
def __str__(self):
#import pprint
p = str(self.pathlist) #pprint.pformat(self.pathlist)
p = str(self.pathlist)
if self.name != "":
return (str(self.name) + " : " + p)
return p
@ -188,12 +186,10 @@ class grid_path:
return newwave
def set_layer(self, zindex):
new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave]
self.pathlist = new_pathlist
def overlap(self, other):
"""
Return the overlap waves ignoring different layers

View File

@ -9,7 +9,6 @@
Some utility functions for sets of grid cells.
"""
import debug
import math
from direction import direction
from vector3d import vector3d
@ -44,6 +43,7 @@ def get_upper_right(curset):
ur = p
return ur
def get_lower_left(curset):
ll = None
for p in curset:
@ -51,7 +51,8 @@ def get_lower_left(curset):
ll = p
return ll
def get_border( curset, direct):
def get_border(curset, direct):
"""
Return the furthest cell(s) in a given direction.
"""
@ -86,6 +87,7 @@ def get_border( curset, direct):
newset = set(maxc)
return newset
def expand_border(curset, direct):
"""
Expand the current set of sells in a given direction.
@ -95,6 +97,7 @@ def expand_border(curset, direct):
next_border_set = increment_set(border_set, direct)
return next_border_set
def expand_borders(curset):
"""
Return the expansions in planar directions.
@ -106,6 +109,7 @@ def expand_borders(curset):
return(north_set, east_set, south_set, west_set)
def inflate_cell(cell, distance):
"""
Expand the current cell in all directions and return the set.
@ -122,6 +126,7 @@ def inflate_cell(cell, distance):
return newset
def inflate_set(curset, distance):
"""
Expand the set in all directions by the given number of grids.
@ -136,6 +141,7 @@ def inflate_set(curset, distance):
# Recurse with less depth
return inflate_set(newset,distance-1)
def flatten_set(curset):
"""
Flatten until we have a set of vector3d objects.
@ -149,7 +155,6 @@ def flatten_set(curset):
return newset
def distance_set(coord, curset):
"""
Return the distance from a coordinate to any item in the set

View File

@ -493,7 +493,6 @@ class pin_group:
debug.error("Could not find a connector for {} with {}".format(self.pins,
self.enclosures))
self.router.write_debug_gds("no_connector.gds")
import pdb; pdb.set_trace()
self.enclosures.append(connector)
# At this point, the pins are overlapping,

View File

@ -27,13 +27,14 @@ class router(router_tech):
route on a given layer. This is limited to two layer routes.
It populates blockages on a grid class.
"""
def __init__(self, layers, design, gds_filename=None, rail_track_width=1):
def __init__(self, layers, design, gds_filename=None, route_track_width=1):
"""
This will instantiate a copy of the gds file or the module at (0,0) and
route on top of this. The blockages from the gds/module will be
considered.
"""
router_tech.__init__(self, layers, rail_track_width)
router_tech.__init__(self, layers, route_track_width)
self.cell = design
@ -285,7 +286,7 @@ class router(router_tech):
If so, reduce the pin group grid to not include the adjacent grid.
Try to do this intelligently to keep th pins enclosed.
"""
debug.info(1,
debug.info(2,
"Comparing {0} and {1} adjacency".format(pin_name1,
pin_name2))
removed_grids = 0
@ -301,7 +302,7 @@ class router(router_tech):
adj_grids))
self.remove_adjacent_grid(pg1, pg2, adj_grids)
debug.info(1, "Removed {} adjacent grids.".format(removed_grids))
debug.info(2, "Removed {} adjacent grids.".format(removed_grids))
def remove_adjacent_grid(self, pg1, pg2, adj_grids):
"""
@ -347,6 +348,10 @@ class router(router_tech):
smaller))
smaller.grids.remove(adj)
def set_supply_rail_blocked(self, value):
# This is just a virtual function
pass
def prepare_blockages(self, pin_name):
"""
Reset and add all of the blockages in the design.
@ -362,7 +367,11 @@ class router(router_tech):
# Block all of the supply rails
# (some will be unblocked if they're a target)
self.set_supply_rail_blocked(True)
try:
self.set_supply_rail_blocked(True)
except AttributeError:
# If function doesn't exist, it isn't a supply router
pass
# Block all of the pin components
# (some will be unblocked if they're a source/target)
@ -797,7 +806,7 @@ class router(router_tech):
"""
Convert the pin groups into pin tracks and blockage tracks.
"""
debug.info(1, "Converting pins for {}.".format(pin_name))
debug.info(2, "Converting pins for {}.".format(pin_name))
for pg in self.pin_groups[pin_name]:
pg.convert_pin()
@ -808,7 +817,7 @@ class router(router_tech):
that are blocked by other shapes.
"""
for pin_name in self.pin_groups:
debug.info(1, "Enclosing pins for {}".format(pin_name))
debug.info(2, "Enclosing pins for {}".format(pin_name))
for pg in self.pin_groups[pin_name]:
pg.enclose_pin()
pg.add_enclosure(self.cell)
@ -829,6 +838,12 @@ class router(router_tech):
for i in range(self.num_pin_components(pin_name)):
self.add_pin_component_target(pin_name, i)
def add_perimeter_target(self, side="all"):
"""
This will mark all the cells on the perimeter of the original layout as a target.
"""
self.rg.add_perimeter_target(side=side)
def num_pin_components(self, pin_name):
"""
This returns how many disconnected pin components there are.
@ -865,7 +880,7 @@ class router(router_tech):
debug.check(index<self.num_pin_components(pin_name),"Pin component index too large.")
pin_in_tracks = self.pin_groups[pin_name][index].grids
debug.info(2, "Set target: " + str(pin_name) + " " + str(pin_in_tracks))
debug.info(3, "Set target: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_target(pin_in_tracks)
def add_pin_component_target_except(self, pin_name, index):
@ -901,7 +916,7 @@ class router(router_tech):
# self.write_debug_gds()
# First, simplify the path for
# debug.info(1, str(self.path))
# debug.info(3, str(self.path))
contracted_path = self.contract_path(path)
debug.info(3, "Contracted path: " + str(contracted_path))
@ -1000,25 +1015,20 @@ class router(router_tech):
# Double check source and taget are not same node, if so, we are done!
for k, v in self.rg.map.items():
if v.source and v.target:
debug.error("Grid cell is source and target! {}".format(k))
return False
return True
# returns the path in tracks
(path, cost) = self.rg.route(detour_scale)
if path:
debug.info(2, "Found path: cost={0} {1}".format(cost, str(path)))
self.paths.append(path)
self.paths.append(grid_utils.flatten_set(path))
self.add_route(path)
path_set = grid_utils.flatten_set(path)
self.path_blockages.append(path_set)
self.path_blockages.append(self.paths[-1])
return True
else:
self.write_debug_gds("failed_route.gds")
# clean up so we can try a reroute
self.rg.reinit()
return False
return True
def annotate_pin_and_tracks(self, pin, tracks):
""""
@ -1122,7 +1132,7 @@ class router(router_tech):
show_all_grids = True
if show_all_grids:
self.rg.add_all_grids()
# self.rg.add_all_grids()
for g in self.rg.map:
self.annotate_grid(g)
@ -1156,7 +1166,36 @@ class router(router_tech):
width=pin.width(),
height=pin.height())
def get_perimeter_pin(self):
""" Return the shape of the last routed path that was on the perimeter """
for v in self.paths[-1]:
if self.rg.is_target(v):
return self.convert_track_to_pin(v)
return None
def get_pin(self, pin_name):
""" Return the lowest, leftest pin group """
keep_pin = None
for index,pg in enumerate(self.pin_groups[pin_name]):
for pin in pg.enclosures:
if not keep_pin:
keep_pin = pin
else:
if pin.lx() <= keep_pin.lx() and pin.by() <= keep_pin.by():
keep_pin = pin
return keep_pin
def check_all_routed(self, pin_name):
"""
Check that all pin groups are routed.
"""
for pg in self.pin_groups[pin_name]:
if not pg.is_routed():
return False
# FIXME: This should be replaced with vector.snap_to_grid at some point
def snap_to_grid(offset):
"""

View File

@ -16,20 +16,20 @@ class router_tech:
"""
This is a class to hold the router tech constants.
"""
def __init__(self, layers, rail_track_width):
def __init__(self, layers, route_track_width):
"""
Allows us to change the layers that we are routing on.
This uses the preferreed directions.
"""
self.layers = layers
self.rail_track_width = rail_track_width
self.route_track_width = route_track_width
if len(self.layers) == 1:
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
@ -55,13 +55,13 @@ class router_tech:
"preferred_directions '{}' and '{}'.")
via_connect = contact(self.layers, (1, 1))
max_via_size = max(via_connect.width,via_connect.height)
max_via_size = max(via_connect.width, via_connect.height)
self.horiz_lpp = layer[self.horiz_layer_name]
self.vert_lpp = layer[self.vert_layer_name]
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
# For supplies, we will make the wire wider than the vias
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
@ -71,16 +71,16 @@ class router_tech:
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
# We'll keep horizontal and vertical tracks the same for simplicity.
self.track_width = max(self.horiz_track_width,self.vert_track_width)
debug.info(1,"Track width: {:.3f}".format(self.track_width))
self.track_space = max(self.horiz_layer_spacing,self.vert_layer_spacing)
debug.info(1,"Track space: {:.3f}".format(self.track_space))
self.track_width = max(self.horiz_track_width, self.vert_track_width)
debug.info(1, "Minimum track width: {:.3f}".format(self.track_width))
self.track_space = max(self.horiz_layer_spacing, self.vert_layer_spacing)
debug.info(1, "Minimum track space: {:.3f}".format(self.track_space))
self.track_wire = self.track_width - self.track_space
debug.info(1,"Track wire width: {:.3f}".format(self.track_wire))
debug.info(1, "Minimum track wire width: {:.3f}".format(self.track_wire))
self.track_widths = vector([self.track_width] * 2)
self.track_factor = vector([1/self.track_width] * 2)
debug.info(2,"Track factor: {}".format(self.track_factor))
debug.info(2, "Track factor: {}".format(self.track_factor))
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
self.layer_widths = [self.track_wire, 1, self.track_wire]
@ -107,9 +107,9 @@ class router_tech:
elif zindex==0:
return self.horiz_layer_name
else:
debug.error("Invalid zindex {}".format(zindex),-1)
debug.error("Invalid zindex {}".format(zindex), -1)
def get_supply_layer_width_space(self, zindex):
def get_layer_width_space(self, zindex):
"""
These are the width and spacing of a supply layer given a supply rail
of the given number of min wire widths.
@ -123,9 +123,9 @@ class router_tech:
min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf)
min_width = drc("minwidth_{0}".format(layer_name), self.rail_track_width*min_wire_width, math.inf)
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.rail_track_width*min_wire_width, math.inf)
min_width = drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf)
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * min_wire_width, math.inf)
return (min_width,min_spacing)
return (min_width, min_spacing)

View File

@ -0,0 +1,92 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California 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 globals import print_time
from router import router
from datetime import datetime
from supply_grid import supply_grid
class signal_escape_router(router):
"""
A router that routes signals to perimeter and makes pins.
"""
def __init__(self, layers, design, gds_filename=None):
"""
This will route on layers in design. It will get the blockages from
either the gds file name or the design itself (by saving to a gds file).
"""
router.__init__(self, layers, design, gds_filename, 1)
def create_routing_grid(self):
"""
Create a sprase routing grid with A* expansion functions.
"""
size = self.ur - self.ll
debug.info(1,"Size: {0} x {1}".format(size.x, size.y))
self.rg = supply_grid(self.ll, self.ur, self.track_width)
def escape_route(self, pin_list):
"""
Takes a list of tuples (name, side) and routes them. After routing,
it removes the old pin and places a new one on the perimeter.
"""
pin_names = [x[0] for x in pin_list]
# Clear the pins if we have previously routed
if (hasattr(self,'rg')):
self.clear_pins()
else:
self.create_routing_grid()
# Get the pin shapes
start_time = datetime.now()
self.find_pins_and_blockages(pin_names)
print_time("Finding pins and blockages",datetime.now(), start_time, 3)
# Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter
start_time = datetime.now()
for pin_name, side in pin_list:
self.route_signal(pin_name, side)
print_time("Maze routing pins",datetime.now(), start_time, 3)
# self.write_debug_gds("final_escape_router.gds",False)
return True
def route_signal(self, pin_name, side):
for detour_scale in [5 * pow(2, x) for x in range(5)]:
debug.info(1, "Exit routing {0} with scale {1}".format(pin_name, detour_scale))
# Clear everything in the routing grid.
self.rg.reinit()
# This is inefficient since it is non-incremental, but it was
# easier to debug.
self.prepare_blockages(pin_name)
# Add the single component of the pin as the source
# which unmarks it as a blockage too
self.add_source(pin_name)
# Marks the grid cells all along the perimeter as a target
self.add_perimeter_target(side)
# Actually run the A* router
if self.run_router(detour_scale=detour_scale):
new_pin = self.get_perimeter_pin()
self.cell.replace_layout_pin(pin_name, new_pin)
return
self.write_debug_gds("debug_route.gds", True)

View File

@ -5,7 +5,6 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from itertools import tee
import debug
from heapq import heappush,heappop
from copy import deepcopy
@ -14,6 +13,7 @@ from grid import grid
from grid_path import grid_path
from vector3d import vector3d
class signal_grid(grid):
"""
Expand the two layer grid to include A* search functions for a source and target.
@ -34,11 +34,11 @@ class signal_grid(grid):
p.reset()
# clear source and target pins
self.source=[]
self.target=[]
self.source = set()
self.target = set()
# Clear the queue
while len(self.q)>0:
while len(self.q) > 0:
heappop(self.q)
self.counter = 0
@ -49,19 +49,16 @@ class signal_grid(grid):
We will use an A* search, so this cost must be pessimistic.
Cost so far will be the length of the path.
"""
#debug.info(3,"Initializing queue.")
# Counter is used to not require data comparison in Python 3.x
# Items will be returned in order they are added during cost ties
self.counter = 0
for s in self.source:
cost = self.cost_to_target(s)
debug.info(3,"Init: cost=" + str(cost) + " " + str([s]))
heappush(self.q,(cost,self.counter,grid_path([vector3d(s)])))
debug.info(3, "Init: cost=" + str(cost) + " " + str([s]))
heappush(self.q, (cost, self.counter, grid_path([vector3d(s)])))
self.counter+=1
def route(self,detour_scale):
def route(self, detour_scale):
"""
This does the A* maze routing with preferred direction routing.
This only works for 1 track wide routes!
@ -70,7 +67,7 @@ class signal_grid(grid):
# We set a cost bound of the HPWL for run-time. This can be
# over-ridden if the route fails due to pruning a feasible solution.
any_source_element = next(iter(self.source))
cost_bound = detour_scale*self.cost_to_target(any_source_element)*grid.PREFERRED_COST
cost_bound = detour_scale * self.cost_to_target(any_source_element) * grid.PREFERRED_COST
# Check if something in the queue is already a source and a target!
for s in self.source:
@ -83,20 +80,17 @@ class signal_grid(grid):
# Put the source items into the queue
self.init_queue()
cheapest_path = None
cheapest_cost = None
# Keep expanding and adding to the priority queue until we are done
while len(self.q)>0:
# should we keep the path in the queue as well or just the final node?
(cost,count,curpath) = heappop(self.q)
debug.info(3,"Queue size: size=" + str(len(self.q)) + " " + str(cost))
debug.info(4,"Expanding: cost=" + str(cost) + " " + str(curpath))
(cost, count, curpath) = heappop(self.q)
debug.info(3, "Queue size: size=" + str(len(self.q)) + " " + str(cost))
debug.info(4, "Expanding: cost=" + str(cost) + " " + str(curpath))
# expand the last element
neighbors = self.expand_dirs(curpath)
debug.info(4,"Neighbors: " + str(neighbors))
debug.info(4, "Neighbors: " + str(neighbors))
for n in neighbors:
# make a new copy of the path to not update the old ones
@ -105,7 +99,7 @@ class signal_grid(grid):
newpath.append(n)
# check if we hit the target and are done
if self.is_target(n[0]): # This uses the [0] item because we are assuming 1-track wide
return (newpath,newpath.cost())
return (newpath, newpath.cost())
else:
# current path cost + predicted cost
current_cost = newpath.cost()
@ -116,19 +110,18 @@ class signal_grid(grid):
if (self.map[n[0]].min_cost==-1 or predicted_cost<self.map[n[0]].min_cost):
self.map[n[0]].min_path = newpath
self.map[n[0]].min_cost = predicted_cost
debug.info(4,"Enqueuing: cost=" + str(current_cost) + "+" + str(target_cost) + " " + str(newpath))
debug.info(4, "Enqueuing: cost=" + str(current_cost) + "+" + str(target_cost) + " " + str(newpath))
# add the cost to get to this point if we haven't reached it yet
heappush(self.q,(predicted_cost,self.counter,newpath))
heappush(self.q, (predicted_cost, self.counter, newpath))
self.counter += 1
#else:
# print("Better previous cost.")
#else:
# print("Cost bounded")
debug.warning("Unable to route path. Expand the detour_scale to allow detours.")
return (None,None)
return (None, None)
def expand_dirs(self,curpath):
def expand_dirs(self, curpath):
"""
Expand each of the four cardinal directions plus up or down
but not expanding to blocked cells. Expands in all directions
@ -143,33 +136,31 @@ class signal_grid(grid):
return unblocked_neighbors
def hpwl(self, src, dest):
"""
Return half perimeter wire length from point to another.
Either point can have positive or negative coordinates.
Include the via penalty if there is one.
"""
hpwl = abs(src.x-dest.x)
hpwl += abs(src.y-dest.y)
hpwl = abs(src.x - dest.x)
hpwl += abs(src.y - dest.y)
if src.x!=dest.x and src.y!=dest.y:
hpwl += grid.VIA_COST
return hpwl
def cost_to_target(self,source):
def cost_to_target(self, source):
"""
Find the cheapest HPWL distance to any target point ignoring
blockages for A* search.
"""
any_target_element = next(iter(self.target))
cost = self.hpwl(source,any_target_element)
cost = self.hpwl(source, any_target_element)
for t in self.target:
cost = min(self.hpwl(source,t),cost)
cost = min(self.hpwl(source, t), cost)
return cost
def get_inertia(self,p0,p1):
def get_inertia(self, p0, p1):
"""
Sets the direction based on the previous direction we came from.
"""

View File

@ -5,15 +5,10 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import gdsMill
import tech
from contact import contact
import math
import debug
from pin_layout import pin_layout
from globals import OPTS
from router import router
class signal_router(router):
"""
A router class to read an obstruction map from a gds and plan a
@ -27,7 +22,6 @@ class signal_router(router):
"""
router.__init__(self, layers, design, gds_filename)
def create_routing_grid(self):
"""
Create a sprase routing grid with A* expansion functions.
@ -35,11 +29,10 @@ class signal_router(router):
# We will add a halo around the boundary
# of this many tracks
size = self.ur - self.ll
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
debug.info(1, "Size: {0} x {1}".format(size.x, size.y))
import signal_grid
self.rg = signal_grid.signal_grid(self.ll, self.ur, self.track_width)
self.rg = signal_grid.signal_grid(self.ll, self.ur, self.route_track_width)
def route(self, src, dest, detour_scale=5):
"""
@ -47,13 +40,13 @@ class signal_router(router):
the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route.
This is used to speed up the routing when there is not much detouring needed.
"""
debug.info(1,"Running signal router from {0} to {1}...".format(src,dest))
debug.info(1, "Running signal router from {0} to {1}...".format(src, dest))
self.pins[src] = []
self.pins[dest] = []
# Clear the pins if we have previously routed
if (hasattr(self,'rg')):
if (hasattr(self, 'rg')):
self.clear_pins()
else:
# Creat a routing grid over the entire area
@ -67,8 +60,8 @@ class signal_router(router):
# Block everything
self.prepare_blockages()
# Clear the pins we are routing
self.set_blockages(self.pin_components[src],False)
self.set_blockages(self.pin_components[dest],False)
self.set_blockages(self.pin_components[src], False)
self.set_blockages(self.pin_components[dest], False)
# Now add the src/tgt if they are not blocked by other shapes
self.add_source(src)

View File

@ -5,12 +5,8 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
from vector3d import vector3d
from grid import grid
from signal_grid import signal_grid
from grid_path import grid_path
from direction import direction
class supply_grid(signal_grid):
@ -27,13 +23,14 @@ class supply_grid(signal_grid):
def reinit(self):
""" Reinitialize everything for a new route. """
self.source = set()
self.target = set()
# Reset all the cells in the map
for p in self.map.values():
p.reset()
def find_start_wave(self, wave, direct):
"""
Finds the first loc starting at loc and up that is open.
@ -46,8 +43,8 @@ class supply_grid(signal_grid):
return None
while wave and self.is_wave_blocked(wave):
wf=grid_path(wave)
wave=wf.neighbor(direct)
wf = grid_path(wave)
wave = wf.neighbor(direct)
# Bail out if we couldn't increment futher
if wave[0].x > self.ur.x or wave[-1].y > self.ur.y:
return None
@ -57,7 +54,6 @@ class supply_grid(signal_grid):
return wave
def is_wave_blocked(self, wave):
"""
Checks if any of the locations are blocked
@ -68,7 +64,6 @@ class supply_grid(signal_grid):
else:
return False
def probe(self, wave, direct):
"""
Expand the wave until there is a blockage and return

View File

@ -28,9 +28,9 @@ class supply_grid_router(router):
start_time = datetime.now()
# Power rail width in minimum wire widths
self.rail_track_width = 3
self.route_track_width = 2
router.__init__(self, layers, design, gds_filename, self.rail_track_width)
router.__init__(self, layers, design, gds_filename, self.route_track_width)
# The list of supply rails (grid sets) that may be routed
self.supply_rails = {}
@ -47,7 +47,7 @@ class supply_grid_router(router):
debug.info(1, "Size: {0} x {1}".format(size.x, size.y))
import supply_grid
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.route_track_width)
def route(self, vdd_name="vdd", gnd_name="gnd"):
"""
@ -74,6 +74,7 @@ class supply_grid_router(router):
start_time = datetime.now()
# Block everything
self.prepare_blockages(self.gnd_name)
# Determine the rail locations
self.route_supply_rails(self.gnd_name, 0)
@ -104,14 +105,6 @@ class supply_grid_router(router):
return True
def check_all_routed(self, pin_name):
"""
Check that all pin groups are routed.
"""
for pg in self.pin_groups[pin_name]:
if not pg.is_routed():
return False
def route_simple_overlaps(self, pin_name):
"""
This checks for simple cases where a pin component already overlaps a supply rail.
@ -317,7 +310,7 @@ class supply_grid_router(router):
data structure. Return whether it was added or not.
"""
# We must have at least 2 tracks to drop plus 2 tracks for a via
if len(wave_path) >= 4 * self.rail_track_width:
if len(wave_path) >= 4 * self.route_track_width:
grid_set = wave_path.get_grids()
self.supply_rails[name].append(grid_set)
return True

View File

@ -1,24 +1,19 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
# Copyright (c) 2016-2019 Regents of the University of California 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 math
import debug
from globals import OPTS,print_time
from contact import contact
from pin_group import pin_group
from pin_layout import pin_layout
from vector3d import vector3d
from globals import print_time
from router import router
from direction import direction
from datetime import datetime
import grid
import grid_utils
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import minimum_spanning_tree
from signal_grid import signal_grid
class supply_tree_router(router):
"""
@ -32,10 +27,9 @@ class supply_tree_router(router):
either the gds file name or the design itself (by saving to a gds file).
"""
# Power rail width in minimum wire widths
self.rail_track_width = 3
router.__init__(self, layers, design, gds_filename, self.rail_track_width)
self.route_track_width = 2
router.__init__(self, layers, design, gds_filename, self.route_track_width)
def create_routing_grid(self):
"""
@ -43,9 +37,7 @@ class supply_tree_router(router):
"""
size = self.ur - self.ll
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
import supply_grid
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
self.rg = signal_grid(self.ll, self.ur, self.route_track_width)
def route(self, vdd_name="vdd", gnd_name="gnd"):
"""
@ -82,7 +74,7 @@ class supply_tree_router(router):
self.route_pins(gnd_name)
print_time("Maze routing supplies",datetime.now(), start_time, 3)
#self.write_debug_gds("final.gds",False)
# self.write_debug_gds("final_tree_router.gds",False)
# Did we route everything??
if not self.check_all_routed(vdd_name):
@ -92,48 +84,6 @@ class supply_tree_router(router):
return True
def check_all_routed(self, pin_name):
"""
Check that all pin groups are routed.
"""
for pg in self.pin_groups[pin_name]:
if not pg.is_routed():
return False
def prepare_blockages(self, pin_name):
"""
Reset and add all of the blockages in the design.
Names is a list of pins to add as a blockage.
"""
debug.info(3,"Preparing blockages.")
# Start fresh. Not the best for run-time, but simpler.
self.clear_blockages()
# This adds the initial blockges of the design
#print("BLOCKING:",self.blocked_grids)
self.set_blockages(self.blocked_grids,True)
# Block all of the pin components (some will be unblocked if they're a source/target)
# Also block the previous routes
for name in self.pin_groups:
blockage_grids = {y for x in self.pin_groups[name] for y in x.grids}
self.set_blockages(blockage_grids,True)
blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages}
self.set_blockages(blockage_grids,True)
# FIXME: These duplicate a bit of work
# These are the paths that have already been routed.
self.set_blockages(self.path_blockages)
# Don't mark the other components as targets since we want to route
# directly to a rail, but unblock all the source components so we can
# route over them
blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids}
self.set_blockages(blockage_grids,False)
def route_pins(self, pin_name):
"""
This will route each of the remaining pin components to the other pins.
@ -141,15 +91,46 @@ class supply_tree_router(router):
"""
remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name])
debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name,
remaining_components))
debug.info(1,"Routing {0} with {1} pin components to connect.".format(pin_name,
remaining_components))
for index,pg in enumerate(self.pin_groups[pin_name]):
if pg.is_routed():
continue
# Create full graph
debug.info(2,"Creating adjacency matrix")
pin_size = len(self.pin_groups[pin_name])
adj_matrix = [[0] * pin_size for i in range(pin_size)]
debug.info(1,"Routing component {0} {1}".format(pin_name, index))
for index1,pg1 in enumerate(self.pin_groups[pin_name]):
for index2,pg2 in enumerate(self.pin_groups[pin_name]):
if index1>=index2:
continue
dist = int(grid_utils.distance_set(list(pg1.grids)[0], pg2.grids))
adj_matrix[index1][index2] = dist
# Find MST
debug.info(2,"Finding MinimumSpanning Tree")
X = csr_matrix(adj_matrix)
Tcsr = minimum_spanning_tree(X)
mst = Tcsr.toarray().astype(int)
connections = []
for x in range(pin_size):
for y in range(pin_size):
if x >= y:
continue
if mst[x][y]>0:
connections.append((x, y))
# Route MST components
for (src, dest) in connections:
self.route_signal(pin_name, src, dest)
#self.write_debug_gds("final.gds", True)
#return
def route_signal(self, pin_name, src_idx, dest_idx):
for detour_scale in [5 * pow(2, x) for x in range(5)]:
debug.info(2, "Routing {0} to {1} with scale {2}".format(src_idx, dest_idx, detour_scale))
# Clear everything in the routing grid.
self.rg.reinit()
@ -159,35 +140,27 @@ class supply_tree_router(router):
# Add the single component of the pin as the source
# which unmarks it as a blockage too
self.add_pin_component_source(pin_name,index)
self.add_pin_component_source(pin_name, src_idx)
# Marks all pin components except index as target
self.add_pin_component_target_except(pin_name,index)
# Add the prevous paths as a target too
self.add_path_target(self.paths)
print("SOURCE: ")
for k,v in self.rg.map.items():
if v.source:
print(k)
print("TARGET: ")
for k,v in self.rg.map.items():
if v.target:
print(k)
import pdb; pdb.set_trace()
if index==1:
self.write_debug_gds("debug{}.gds".format(pin_name),False)
self.add_pin_component_target(pin_name, dest_idx)
# Actually run the A* router
if not self.run_router(detour_scale=5):
self.write_debug_gds("debug_route.gds",True)
if self.run_router(detour_scale=detour_scale):
return
#if index==3 and pin_name=="vdd":
# self.write_debug_gds("route.gds",False)
self.write_debug_gds("debug_route.gds", True)
def add_io_pin(self, instance, pin_name, new_name=""):
"""
Add a signle input or output pin up to metal 3.
"""
pin = instance.get_pins(pin_name)
if new_name == "":
new_name = pin_name
# Just use the power pin function for now to save code
self.add_power_pin(name=new_name, loc=pin.center(), start_layer=pin.layer)

View File

@ -1,62 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],"../.."))
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
OPTS = globals.OPTS
class no_blockages_test(openram_test):
"""
Simplest two pin route test with no blockages.
"""
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from gds_cell import gds_cell
from design import design
from signal_router import signal_router as router
class routing(design, openram_test):
"""
A generic GDS design that we can route on.
"""
def __init__(self, name):
design.__init__(self, "top")
# Instantiate a GDS cell with the design
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
cell = gds_cell(name, gds_file)
self.add_inst(name=name,
mod=cell,
offset=[0,0])
self.connect_inst([])
layer_stack =("metal1","via1","metal2")
r=router(layer_stack,self,gds_file)
self.assertTrue(r.route(src="A",dest="B"))
r=routing("01_no_blockages_test_{0}".format(OPTS.tech_name))
self.local_drc_check(r)
# fails if there are any DRC errors on any cells
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,3 +0,0 @@
.SUBCKT cell
.ENDS cell

View File

@ -1,66 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],"../.."))
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
OPTS = globals.OPTS
class blockages_test(openram_test):
"""
Simple two pin route test with multilayer blockages.
"""
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from gds_cell import gds_cell
from design import design
from signal_router import signal_router as router
class routing(design, openram_test):
"""
A generic GDS design that we can route on.
"""
def __init__(self, name):
design.__init__(self, "top")
# Instantiate a GDS cell with the design
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
cell = gds_cell(name, gds_file)
self.add_inst(name=name,
mod=cell,
offset=[0,0])
self.connect_inst([])
layer_stack =("metal1","via1","metal2")
r=router(layer_stack,self,gds_file)
self.assertTrue(r.route(src="A",dest="B"))
r=routing("02_blockages_test_{0}".format(OPTS.tech_name))
self.local_drc_check(r)
# fails if there are any DRC errors on any cells
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,3 +0,0 @@
.SUBCKT cell
.ENDS cell

View File

@ -1,61 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],"../.."))
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
OPTS = globals.OPTS
class same_layer_pins_test(openram_test):
"""
Checks two pins on the same layer with positive and negative coordinates.
"""
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from gds_cell import gds_cell
from design import design
from signal_router import signal_router as router
class routing(design, openram_test):
"""
A generic GDS design that we can route on.
"""
def __init__(self, name):
design.__init__(self, "top")
# Instantiate a GDS cell with the design
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
cell = gds_cell(name, gds_file)
self.add_inst(name=name,
mod=cell,
offset=[0,0])
self.connect_inst([])
layer_stack =("metal1","via1","metal2")
r=router(layer_stack,self,gds_file)
self.assertTrue(r.route(src="A",dest="B"))
r = routing("03_same_layer_pins_test_{0}".format(OPTS.tech_name))
self.local_drc_check(r)
# fails if there are any DRC errors on any cells
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,3 +0,0 @@
.SUBCKT cell
.ENDS cell

View File

@ -1,63 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],"../.."))
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
OPTS = globals.OPTS
class diff_layer_pins_test(openram_test):
"""
Two pin route test with pins on different layers and blockages.
Pins are smaller than grid size.
"""
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from gds_cell import gds_cell
from design import design
from signal_router import signal_router as router
class routing(design, openram_test):
"""
A generic GDS design that we can route on.
"""
def __init__(self, name):
design.__init__(self, "top")
# Instantiate a GDS cell with the design
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
cell = gds_cell(name, gds_file)
self.add_inst(name=name,
mod=cell,
offset=[0,0])
self.connect_inst([])
layer_stack =("metal1","via1","metal2")
r=router(layer_stack,self,gds_file)
self.assertTrue(r.route(src="A",dest="B"))
r = routing("04_diff_layer_pins_test_{0}".format(OPTS.tech_name))
self.local_drc_check(r)
# fails if there are any DRC errors on any cells
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,3 +0,0 @@
.SUBCKT cell
.ENDS cell

View File

@ -1,65 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],"../.."))
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
OPTS = globals.OPTS
class two_nets_test(openram_test):
"""
Route two nets in the same GDS file. The routes will interact,
so they must block eachother.
"""
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from gds_cell import gds_cell
from design import design
from signal_router import signal_router as router
class routing(design, openram_test):
"""
A generic GDS design that we can route on.
"""
def __init__(self, name):
design.__init__(self, "top")
# Instantiate a GDS cell with the design
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
cell = gds_cell(name, gds_file)
self.add_inst(name=name,
mod=cell,
offset=[0,0])
self.connect_inst([])
layer_stack =("metal1","via1","metal2")
r=router(layer_stack,self,gds_file)
self.assertTrue(r.route(src="A",dest="B"))
self.assertTrue(r.route(src="C",dest="D"))
r = routing("05_two_nets_test_{0}".format(OPTS.tech_name))
self.local_drc_check(r)
# fails if there are any DRC errors on any cells
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,3 +0,0 @@
.SUBCKT cell
.ENDS cell

View File

@ -1,72 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],"../.."))
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
OPTS = globals.OPTS
class pin_location_test(openram_test):
"""
Simplest two pin route test with no blockages using the pin locations instead of labels.
"""
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from gds_cell import gds_cell
from design import design
from signal_router import signal_router as router
class routing(design, openram_test):
"""
A generic GDS design that we can route on.
"""
def __init__(self, name):
design.__init__(self, "top")
# Instantiate a GDS cell with the design
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
cell = gds_cell(name, gds_file)
self.add_inst(name=name,
mod=cell,
offset=[0,0])
self.connect_inst([])
layer_stack =("metal1","via1","metal2")
r=router(layer_stack,self,gds_file)
# these are user coordinates and layers
src_pin = [[0.52, 4.099],11]
tgt_pin = [[3.533, 1.087],11]
#r.route(layer_stack,src="A",dest="B")
self.assertTrue(r.route(src=src_pin,dest=tgt_pin))
# This only works for freepdk45 since the coordinates are hard coded
if OPTS.tech_name == "freepdk45":
r = routing("06_pin_location_test_{0}".format(OPTS.tech_name))
self.local_drc_check(r)
else:
debug.warning("This test does not support technology {0}".format(OPTS.tech_name))
# fails if there are any DRC errors on any cells
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,90 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],"../.."))
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
OPTS = globals.OPTS
class big_test(openram_test):
"""
Simplest two pin route test with no blockages using the pin locations instead of labels.
"""
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from gds_cell import gds_cell
from design import design
from signal_router import signal_router as router
class routing(design, openram_test):
"""
A generic GDS design that we can route on.
"""
def __init__(self, name):
design.__init__(self, "top")
# Instantiate a GDS cell with the design
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
cell = gds_cell(name, gds_file)
self.add_inst(name=name,
mod=cell,
offset=[0,0])
self.connect_inst([])
layer_stack =("metal1","via1","metal2")
r=router(layer_stack,self,gds_file)
connections=[('out_0_2', 'a_0_0'),
('out_0_3', 'b_0_0'),
('out_0_0', 'a_0_1'),
('out_1_2', 'a_1_0'),
('out_1_3', 'b_1_0'),
('out_1_0', 'a_1_1'),
('out_2_1', 'a_2_0'),
('out_2_2', 'b_2_0'),
('out_3_1', 'a_3_0'),
('out_3_2', 'b_3_0'),
('out_4_6', 'a_4_0'),
('out_4_7', 'b_4_0'),
('out_4_8', 'a_4_2'),
('out_4_9', 'b_4_2'),
('out_4_10', 'a_4_4'),
('out_4_11', 'b_4_4'),
('out_4_0', 'a_4_1'),
('out_4_2', 'b_4_1'),
('out_4_4', 'a_4_5'),
('out_4_1', 'a_4_3'),
('out_4_5', 'b_4_3')]
for (src,tgt) in connections:
self.assertTrue(r.route(src=src,dest=tgt))
# This test only runs on scn3me_subm tech
if OPTS.tech_name=="scn3me_subm":
r = routing("07_big_test_{0}".format(OPTS.tech_name))
self.local_drc_check(r)
else:
debug.warning("This test does not support technology {0}".format(OPTS.tech_name))
# fails if there are any DRC errors on any cells
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,66 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],"../.."))
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
OPTS = globals.OPTS
class expand_region_test(openram_test):
"""
Test an infeasible route followed by a feasible route with an expanded region.
"""
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from gds_cell import gds_cell
from design import design
from signal_router import signal_router as router
class routing(design, openram_test):
"""
A generic GDS design that we can route on.
"""
def __init__(self, name):
design.__init__(self, "top")
# Instantiate a GDS cell with the design
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
cell = gds_cell(name, gds_file)
self.add_inst(name=name,
mod=cell,
offset=[0,0])
self.connect_inst([])
layer_stack =("metal1","via1","metal2")
r=router(layer_stack,self,gds_file)
# This should be infeasible because it is blocked without a detour.
self.assertFalse(r.route(src="A",dest="B",detour_scale=1))
# This should be feasible because we allow it to detour
self.assertTrue(r.route(src="A",dest="B",detour_scale=3))
r = routing("08_expand_region_test_{0}".format(OPTS.tech_name))
self.local_drc_check(r)
# fails if there are any DRC errors on any cells
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,56 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
"Run a regresion test the library cells for DRC"
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
OPTS = globals.OPTS
class no_blockages_test(openram_test):
"""
Simplest two pin route test with no blockages.
"""
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from supply_router import supply_router as router
if False:
from control_logic import control_logic
cell = control_logic(16)
layer_stack =("metal3","via3","metal4")
rtr=router(layer_stack, cell)
self.assertTrue(rtr.route())
else:
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.words_per_row=1
sram = sram(c, "sram1")
cell = sram.s
self.local_check(cell,True)
# fails if there are any DRC errors on any cells
globals.end_openram()
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -1,16 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
word_size = 1
num_words = 16
tech_name = "freepdk45"
process_corners = ["TT"]
supply_voltages = [1.0]
temperatures = [25]

View File

@ -1,15 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
word_size = 1
num_words = 16
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]

View File

@ -1,23 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from design import design
class gds_cell(design):
"""
A generic GDS design.
"""
def __init__(self, name, gds_file):
self.name = name
self.gds_file = gds_file
self.sp_file = None
design.__init__(self, name)
# The dimensions will not be defined, so do this...
self.width=0
self.height=0

View File

@ -1,46 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
#!/usr/bin/env python3
import re
import unittest
import sys,os
sys.path.append(os.path.join(sys.path[0],"../../compiler"))
import globals
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
from testutils import header,openram_test
header(__file__, OPTS.tech_name)
# get a list of all files in the tests directory
files = os.listdir(sys.path[0])
# assume any file that ends in "test.py" in it is a regression test
nametest = re.compile("test\.py$", re.IGNORECASE)
tests = list(filter(nametest.search, files))
tests.sort()
# import all of the modules
filenameToModuleName = lambda f: os.path.splitext(f)[0]
moduleNames = map(filenameToModuleName, tests)
modules = map(__import__, moduleNames)
suite = unittest.TestSuite()
load = unittest.defaultTestLoader.loadTestsFromModule
suite.addTests(map(load, modules))
test_runner = unittest.TextTestRunner(verbosity=2,stream=sys.stderr)
test_result = test_runner.run(suite)
import verify
verify.print_drc_stats()
verify.print_lvs_stats()
verify.print_pex_stats()
sys.exit(not test_result.wasSuccessful())

View File

@ -1,265 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest,warnings
import sys,os,glob,copy
sys.path.append(os.path.join(sys.path[0],"../.."))
from globals import OPTS
import debug
class openram_test(unittest.TestCase):
""" Base unit test that we have some shared classes in. """
def local_drc_check(self, w):
self.reset()
tempgds = OPTS.openram_temp + "temp.gds"
w.gds_write(tempgds)
import verify
result=verify.run_drc(w.name, tempgds)
if result != 0:
self.fail("DRC failed: {}".format(w.name))
if not OPTS.keep_temp:
self.cleanup()
def local_check(self, a, final_verification=False):
self.reset()
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"
a.sp_write(tempspice)
a.gds_write(tempgds)
import verify
result=verify.run_drc(a.name, tempgds)
if result != 0:
self.fail("DRC failed: {}".format(a.name))
result=verify.run_lvs(a.name, tempgds, tempspice, final_verification)
if result != 0:
self.fail("LVS mismatch: {}".format(a.name))
if not OPTS.keep_temp:
self.cleanup()
def cleanup(self):
""" Reset the duplicate checker and cleanup files. """
files = glob.glob(OPTS.openram_temp + '*')
for f in files:
# Only remove the files
if os.path.isfile(f):
os.remove(f)
def reset(self):
"""
Reset everything after each test.
"""
# Reset the static duplicate name checker for unit tests.
import hierarchy_design
hierarchy_design.hierarchy_design.name_map=[]
def check_golden_data(self, data, golden_data, error_tolerance=1e-2):
"""
This function goes through two dictionaries, key by key and compares
each item. It uses relative comparisons for the items and returns false
if there is a mismatch.
"""
# Check each result
data_matches = True
for k in data.keys():
if type(data[k])==list:
for i in range(len(data[k])):
if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance):
data_matches = False
else:
self.isclose(k,data[k],golden_data[k],error_tolerance)
if not data_matches:
import pprint
data_string=pprint.pformat(data)
debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance*100)+data_string)
return data_matches
def isclose(self,key,value,actual_value,error_tolerance=1e-2):
""" This is used to compare relative values. """
import debug
relative_diff = self.relative_diff(value,actual_value)
check = relative_diff <= error_tolerance
if check:
debug.info(2,"CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100))
return True
else:
debug.error("NOT CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100))
return False
def relative_diff(self, value1, value2):
""" Compute the relative difference of two values and normalize to the largest.
If largest value is 0, just return the difference."""
# Edge case to avoid divide by zero
if value1==0 and value2==0:
return 0.0
# Don't need relative, exact compare
if value1==value2:
return 0.0
# Get normalization value
norm_value = abs(max(value1, value2))
# Edge case where greater is a zero
if norm_value == 0:
min_value = abs(min(value1, value2))
return abs(value1 - value2) / norm_value
def relative_compare(self, value,actual_value,error_tolerance):
""" This is used to compare relative values. """
if (value==actual_value): # if we don't need a relative comparison!
return True
return (abs(value - actual_value) / max(value,actual_value) <= error_tolerance)
def isapproxdiff(self, filename1, filename2, error_tolerance=0.001):
"""Compare two files.
Arguments:
filename1 -- First file name
filename2 -- Second file name
Return value:
True if the files are the same, False otherwise.
"""
import re
import debug
numeric_const_pattern = r"""
[-+]? # optional sign
(?:
(?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc
|
(?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc
)
# followed by optional exponent part if desired
(?: [Ee] [+-]? \d+ ) ?
"""
rx = re.compile(numeric_const_pattern, re.VERBOSE)
fp1 = open(filename1, 'rb')
fp2 = open(filename2, 'rb')
mismatches=0
line_num=0
while True:
line_num+=1
line1 = fp1.readline().decode('utf-8')
line2 = fp2.readline().decode('utf-8')
#print("line1:",line1)
#print("line2:",line2)
# 1. Find all of the floats using a regex
line1_floats=rx.findall(line1)
line2_floats=rx.findall(line2)
debug.info(3,"line1_floats: "+str(line1_floats))
debug.info(3,"line2_floats: "+str(line2_floats))
# 2. Remove the floats from the string
for f in line1_floats:
line1=line1.replace(f,"",1)
for f in line2_floats:
line2=line2.replace(f,"",1)
#print("line1:",line1)
#print("line2:",line2)
# 3. Convert to floats rather than strings
line1_floats = [float(x) for x in line1_floats]
line2_floats = [float(x) for x in line1_floats]
# 4. Check if remaining string matches
if line1 != line2:
if mismatches==0:
debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2))
mismatches += 1
debug.error("MISMATCH Line ({0}):\n{1}\n!=\n{2}".format(line_num,line1.rstrip('\n'),line2.rstrip('\n')))
# 5. Now compare that the floats match
elif len(line1_floats)!=len(line2_floats):
if mismatches==0:
debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2))
mismatches += 1
debug.error("MISMATCH Line ({0}) Length {1} != {2}".format(line_num,len(line1_floats),len(line2_floats)))
else:
for (float1,float2) in zip(line1_floats,line2_floats):
relative_diff = self.relative_diff(float1,float2)
check = relative_diff <= error_tolerance
if not check:
if mismatches==0:
debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2))
mismatches += 1
debug.error("MISMATCH Line ({0}) Float {1} != {2} diff: {3:.1f}%".format(line_num,float1,float2,relative_diff*100))
# Only show the first 10 mismatch lines
if not line1 and not line2 or mismatches>10:
fp1.close()
fp2.close()
return mismatches==0
# Never reached
return False
def isdiff(self,filename1,filename2):
""" This is used to compare two files and display the diff if they are different.. """
import debug
import filecmp
import difflib
check = filecmp.cmp(filename1,filename2)
if not check:
debug.error("MISMATCH file1={0} file2={1}".format(filename1,filename2))
f1 = open(filename1,"r")
s1 = f1.readlines().decode('utf-8')
f1.close()
f2 = open(filename2,"r").decode('utf-8')
s2 = f2.readlines()
f2.close()
mismatches=0
for line in difflib.unified_diff(s1, s2):
mismatches += 1
self.error("DIFF LINES:",line)
if mismatches>10:
return False
return False
else:
debug.info(2,"MATCH {0} {1}".format(filename1,filename2))
return True
def header(filename, technology):
# Skip the header for gitlab regression
import getpass
if getpass.getuser() == "gitlab-runner":
return
tst = "Running Test for:"
print("\n")
print(" ______________________________________________________________________________ ")
print("|==============================================================================|")
print("|=========" + tst.center(60) + "=========|")
print("|=========" + technology.center(60) + "=========|")
print("|=========" + filename.center(60) + "=========|")
from globals import OPTS
print("|=========" + OPTS.openram_temp.center(60) + "=========|")
print("|==============================================================================|")

View File

@ -87,26 +87,6 @@ class sram():
def save(self):
""" Save all the output files while reporting time to do it as well. """
if not OPTS.netlist_only:
# Write the layout
start_time = datetime.datetime.now()
gdsname = OPTS.output_path + self.s.name + ".gds"
debug.print_raw("GDS: Writing to {0}".format(gdsname))
self.gds_write(gdsname)
verify.write_drc_script(cell_name=self.s.name,
gds_name=os.path.basename(gdsname),
extract=True,
final_verification=True,
output_path=OPTS.output_path)
print_time("GDS", datetime.datetime.now(), start_time)
# Create a LEF physical model
start_time = datetime.datetime.now()
lefname = OPTS.output_path + self.s.name + ".lef"
debug.print_raw("LEF: Writing to {0}".format(lefname))
self.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time)
# Save the spice file
start_time = datetime.datetime.now()
spname = OPTS.output_path + self.s.name + ".sp"
@ -118,12 +98,33 @@ class sram():
output_path=OPTS.output_path)
print_time("Spice writing", datetime.datetime.now(), start_time)
if not OPTS.netlist_only:
# Write the layout
start_time = datetime.datetime.now()
gdsname = OPTS.output_path + self.s.name + ".gds"
debug.print_raw("GDS: Writing to {0}".format(gdsname))
self.gds_write(gdsname)
if OPTS.check_lvsdrc:
verify.write_drc_script(cell_name=self.s.name,
gds_name=os.path.basename(gdsname),
extract=True,
final_verification=True,
output_path=OPTS.output_path)
print_time("GDS", datetime.datetime.now(), start_time)
# Create a LEF physical model
start_time = datetime.datetime.now()
lefname = OPTS.output_path + self.s.name + ".lef"
debug.print_raw("LEF: Writing to {0}".format(lefname))
self.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time)
# Save the LVS file
start_time = datetime.datetime.now()
lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
debug.print_raw("LVS: Writing to {0}".format(lvsname))
self.lvs_write(lvsname)
if not OPTS.netlist_only:
if not OPTS.netlist_only and OPTS.check_lvsdrc:
verify.write_lvs_script(cell_name=self.s.name,
gds_name=os.path.basename(gdsname),
sp_name=os.path.basename(lvsname),

View File

@ -5,12 +5,12 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
from vector import vector
from sram_base import sram_base
from contact import m2_via
from channel_route import channel_route
from signal_escape_router import signal_escape_router as router
from globals import OPTS
import channel_route
class sram_1bank(sram_base):
@ -102,12 +102,17 @@ class sram_1bank(sram_base):
# Place with an initial wide channel (from above)
self.place_dffs()
# Route the channel and set to the new data bus size
# We need to temporarily add some pins for the x offsets
# but we'll remove them so that they have the right y
# offsets after the DFF placement.
self.add_layout_pins(add_vias=False)
self.route_dffs(add_routes=False)
self.remove_layout_pins()
# Re-place with the new channel size
self.place_dffs()
# Now route the channel
self.route_dffs()
def place_row_addr_dffs(self):
"""
@ -241,14 +246,13 @@ class sram_1bank(sram_base):
self.data_pos[port] = vector(x_offset, y_offset)
self.spare_wen_pos[port] = vector(x_offset, y_offset)
def add_layout_pins(self):
def route_escape_pins(self):
"""
Add the top-level pins for a single bank SRAM with control.
"""
highest_coord = self.find_highest_coords()
lowest_coord = self.find_lowest_coords()
bbox = [lowest_coord, highest_coord]
# List of pin to new pin name
pins_to_route = []
for port in self.all_ports:
# Depending on the port, use the bottom/top or left/right sides
# Port 0 is left/bottom
@ -258,132 +262,127 @@ class sram_1bank(sram_base):
# Connect the control pins as inputs
for signal in self.control_logic_inputs[port]:
if signal == "clk":
continue
if signal.startswith("rbl"):
continue
if OPTS.perimeter_pins:
self.add_perimeter_pin(name=signal + "{}".format(port),
pin=self.control_logic_insts[port].get_pin(signal),
side=left_or_right,
bbox=bbox)
if signal=="clk":
pins_to_route.append(("{0}{1}".format(signal, port), bottom_or_top))
else:
self.copy_layout_pin(self.control_logic_insts[port],
signal,
signal + "{}".format(port))
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="clk{}".format(port),
pin=self.control_logic_insts[port].get_pin("clk"),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.control_logic_insts[port],
"clk",
"clk{}".format(port))
# Data input pins go to BOTTOM/TOP
din_ports = []
pins_to_route.append(("{0}{1}".format(signal, port), left_or_right))
if port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
if OPTS.perimeter_pins:
p = self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit),
pin=self.data_dff_insts[port].get_pin("din_{0}".format(bit)),
side=bottom_or_top,
bbox=bbox)
din_ports.append(p)
else:
self.copy_layout_pin(self.data_dff_insts[port],
"din_{}".format(bit),
"din{0}[{1}]".format(port, bit))
pins_to_route.append(("din{0}[{1}]".format(port, bit), bottom_or_top))
# Data output pins go to BOTTOM/TOP
if port in self.readwrite_ports and OPTS.perimeter_pins:
if port in self.readwrite_ports or port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
# This should be routed next to the din pin
p = din_ports[bit]
self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(port, bit),
layer=p.layer,
offset=p.center() + vector(self.m3_pitch, 0),
width=p.width(),
height=p.height())
elif port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
if OPTS.perimeter_pins:
# This should have a clear route to the perimeter if there are no din routes
self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit),
pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.bank_inst,
"dout{0}_{1}".format(port, bit),
"dout{0}[{1}]".format(port, bit))
pins_to_route.append(("dout{0}[{1}]".format(port, bit), bottom_or_top))
# Lower address bits go to BOTTOM/TOP
for bit in range(self.col_addr_size):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit),
pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.col_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit))
pins_to_route.append(("addr{0}[{1}]".format(port, bit), bottom_or_top))
# Upper address bits go to LEFT/RIGHT
for bit in range(self.row_addr_size):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size),
pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)),
side=left_or_right,
bbox=bbox)
else:
self.copy_layout_pin(self.row_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit + self.col_addr_size))
pins_to_route.append(("addr{0}[{1}]".format(port, bit + self.col_addr_size), left_or_right))
# Write mask pins go to BOTTOM/TOP
if port in self.write_ports:
if self.write_size:
for bit in range(self.num_wmasks):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit),
pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.wmask_dff_insts[port],
"din_{}".format(bit),
"wmask{0}[{1}]".format(port, bit))
pins_to_route.append(("wmask{0}[{1}]".format(port, bit), bottom_or_top))
# Spare wen pins go to BOTTOM/TOP
if port in self.write_ports:
for bit in range(self.num_spare_cols):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit),
pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)),
side=left_or_right,
bbox=bbox)
else:
self.copy_layout_pin(self.spare_wen_dff_insts[port],
"din_{}".format(bit),
"spare_wen{0}[{1}]".format(port, bit))
pins_to_route.append(("spare_wen{0}[{1}]".format(port, bit), bottom_or_top))
rtr=router(self.m3_stack, self)
rtr.escape_route(pins_to_route)
def add_layout_pins(self, add_vias=True):
"""
Add the top-level pins for a single bank SRAM with control.
"""
for port in self.all_ports:
# Hack: If we are escape routing, set the pin layer to
# None so that we will start from the pin layer
# Otherwise, set it as the pin layer so that no vias are added.
# Otherwise, when we remove pins to move the dff array dynamically,
# we will leave some remaining vias when the pin locations change.
if add_vias:
pin_layer = None
else:
pin_layer = self.pwr_grid_layer
# Connect the control pins as inputs
for signal in self.control_logic_inputs[port]:
if signal.startswith("rbl"):
continue
self.add_io_pin(self.control_logic_insts[port],
signal,
signal + "{}".format(port),
start_layer=pin_layer)
if port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
self.add_io_pin(self.data_dff_insts[port],
"din_{}".format(bit),
"din{0}[{1}]".format(port, bit),
start_layer=pin_layer)
if port in self.readwrite_ports or port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
self.add_io_pin(self.bank_inst,
"dout{0}_{1}".format(port, bit),
"dout{0}[{1}]".format(port, bit),
start_layer=pin_layer)
for bit in range(self.col_addr_size):
self.add_io_pin(self.col_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit),
start_layer=pin_layer)
for bit in range(self.row_addr_size):
self.add_io_pin(self.row_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit + self.col_addr_size),
start_layer=pin_layer)
if port in self.write_ports:
if self.write_size:
for bit in range(self.num_wmasks):
self.add_io_pin(self.wmask_dff_insts[port],
"din_{}".format(bit),
"wmask{0}[{1}]".format(port, bit),
start_layer=pin_layer)
if port in self.write_ports:
for bit in range(self.num_spare_cols):
self.add_io_pin(self.spare_wen_dff_insts[port],
"din_{}".format(bit),
"spare_wen{0}[{1}]".format(port, bit),
start_layer=pin_layer)
def route_layout(self):
""" Route a single bank SRAM """
self.add_layout_pins()
self.route_clk()
self.route_control_logic()
self.route_row_addr_dff()
self.route_dffs()
# We add the vias to M3 before routing supplies because
# they might create some blockages
self.add_layout_pins()
# Route the supplies first since the MST is not blockage aware
# and signals can route to anywhere on sides (it is flexible)
self.route_supplies()
# Route the pins to the perimeter
if OPTS.perimeter_pins:
self.route_escape_pins()
def route_dffs(self, add_routes=True):
for port in self.all_ports:
@ -402,25 +401,6 @@ class sram_1bank(sram_base):
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, dff_pins)))
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m1_pitch)
else:
offset = vector(0,
self.bank.height + 2 * self.m1_space)
cr = channel_route.channel_route(netlist=route_map,
offset=offset,
layer_stack=self.m1_stack,
parent=self)
if add_routes:
self.add_inst(cr.name, cr)
self.connect_inst([])
else:
self.col_addr_bus_size[port] = cr.height
route_map = []
# wmask dff
if self.num_wmasks > 0 and port in self.write_ports:
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
@ -437,16 +417,6 @@ class sram_1bank(sram_base):
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, dff_pins)))
if port in self.readwrite_ports and OPTS.perimeter_pins:
# outputs from sense amp
# These are the output pins which had their pin placed on the perimeter, so route from the
# sense amp which should not align with write driver input
sram_names = ["dout{0}[{1}]".format(port, x) for x in range(self.word_size + self.num_spare_cols)]
sram_pins = [self.get_pin(x) for x in sram_names]
bank_names = ["dout{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, sram_pins)))
# spare wen dff
if self.num_spare_cols > 0 and port in self.write_ports:
dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)]
@ -464,26 +434,32 @@ class sram_1bank(sram_base):
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m1_pitch)
cr = channel_route.channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
- self.data_bus_size[port] + 2 * self.m3_pitch)
cr = channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
if add_routes:
self.add_inst(cr.name, cr)
self.connect_inst([])
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
# with no active devices.
# self.add_inst(cr.name, cr)
# self.connect_inst([])
self.add_flat_inst(cr.name, cr)
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
else:
offset = vector(0,
self.bank.height + 2 * self.m1_space)
cr = channel_route.channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
self.bank.height + self.m3_pitch)
cr = channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
if add_routes:
self.add_inst(cr.name, cr)
self.connect_inst([])
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
# with no active devices.
# self.add_inst(cr.name, cr)
# self.connect_inst([])
self.add_flat_inst(cr.name, cr)
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap

View File

@ -190,13 +190,13 @@ class sram_base(design, verilog, lef):
start_time = datetime.datetime.now()
self.route_layout()
self.route_supplies()
if not OPTS.is_unit_test:
print_time("Routing", datetime.datetime.now(), start_time)
self.add_lvs_correspondence_points()
self.offset_all_coordinates()
#self.offset_all_coordinates()
highest_coord = self.find_highest_coords()
self.width = highest_coord[0]
@ -207,9 +207,10 @@ class sram_base(design, verilog, lef):
ur=vector(self.width, self.height))
start_time = datetime.datetime.now()
# We only enable final verification if we have routed the design
self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc)
if not OPTS.is_unit_test:
# We only enable final verification if we have routed the design
# Only run this if not a unit test, because unit test will also verify it.
self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc)
print_time("Verification", datetime.datetime.now(), start_time)
def create_modules(self):
@ -220,31 +221,49 @@ class sram_base(design, verilog, lef):
# Copy the pins to the top level
# This will either be used to route or left unconnected.
for inst in self.insts:
self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst, "gnd")
for pin_name in ["vdd", "gnd"]:
for inst in self.insts:
self.copy_power_pins(inst, pin_name)
if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins)
return
grid_stack = set()
try:
from tech import power_grid
grid_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
import tech
if "m4" in tech.layer:
# Route a M3/M4 grid
grid_stack = self.m3_stack
elif "m3" in tech.layer:
grid_stack =("m3",)
# Route a M3/M4 grid
grid_stack = self.m3_stack
from supply_grid_router import supply_grid_router as router
if OPTS.route_supplies == "grid":
from supply_grid_router import supply_grid_router as router
elif OPTS.route_supplies:
from supply_tree_router import supply_tree_router as router
rtr=router(grid_stack, self)
rtr.route()
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pins
self.remove_layout_pin(pin_name)
pin = rtr.get_pin(pin_name)
self.add_layout_pin(pin_name,
pin.layer,
pin.ll(),
pin.width(),
pin.height())
def compute_bus_sizes(self):
""" Compute the independent bus widths shared between two and four bank SRAMs """

View File

@ -27,8 +27,8 @@ class bitcell_array_1rw_1r_test(openram_test):
OPTS.num_w_ports = 0
globals.setup_bitcell()
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
a = factory.create(module_type="bitcell_array", cols=4, rows=4)
debug.info(2, "Testing 2x2 array for cell_2port")
a = factory.create(module_type="bitcell_array", cols=2, rows=2)
self.local_check(a)
globals.end_openram()

View File

@ -25,7 +25,7 @@ class replica_bitcell_array_test(openram_test):
factory.reset()
debug.info(2, "Testing 4x4 array for bitcell")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 0])
a = factory.create(module_type="replica_bitcell_array", cols=7, rows=5, rbl=[1, 0])
self.local_check(a)
globals.end_openram()

View File

@ -23,6 +23,7 @@ class sram_1bank_2mux_1rw_1r_test(openram_test):
globals.init_openram(config_file)
from sram_config import sram_config
OPTS.route_supplies = False
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0

View File

@ -66,7 +66,9 @@ class model_delay_test(openram_test):
# Only compare the delays
spice_delays = {key:value for key, value in spice_data.items() if 'delay' in key}
spice_delays['min_period'] = spice_data['min_period']
model_delays = {key:value for key, value in model_data.items() if 'delay' in key}
model_delays['min_period'] = model_data['min_period']
debug.info(1,"Spice Delays={}".format(spice_delays))
debug.info(1,"Model Delays={}".format(model_delays))

View File

@ -17,6 +17,7 @@ from globals import OPTS
import debug
@unittest.skip("SKIPPING 26_hspice_pex_pinv_test")
class hspice_pex_pinv_test(openram_test):
def runTest(self):
@ -39,10 +40,10 @@ class hspice_pex_pinv_test(openram_test):
OPTS.keep_temp = True
debug.info(2, "Checking 1x size inverter")
tx = pinv.pinv(name="pinv", size=1)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
tx.gds_write(tempgds)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name)
tx.sp_write(tempsp)
tempgds = "{}.gds".format(tx.name)
tx.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
tempsp = "{}.sp".format(tx.name)
tx.sp_write("{0}{1}".format(OPTS.openram_temp, tempsp))
# make sure that the library simulation is successful
sp_delay = self.simulate_delay(test_module=tempsp,
@ -74,7 +75,7 @@ class hspice_pex_pinv_test(openram_test):
def simulate_delay(self, test_module, top_level_name):
from charutils import parse_spice_list
# setup simulation
sim_file = OPTS.openram_temp + "stim.sp"
sim_file = "stim.sp"
log_file_name = "timing"
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
test_sim.run_sim("stim.sp")
@ -86,7 +87,7 @@ class hspice_pex_pinv_test(openram_test):
import tech
from characterizer import measurements, stimuli
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
sim_file = open(sim_file, "w")
sim_file = open(OPTS.openram_temp + sim_file, "w")
simulation = stimuli(sim_file, corner)
# library files

View File

@ -16,7 +16,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 26_ngspice_pex_pinv_test")
class ngspice_pex_pinv_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
@ -37,10 +37,10 @@ class ngspice_pex_pinv_test(openram_test):
OPTS.keep_temp = True # force set keep to true to save the sp file
debug.info(2, "Checking 1x size inverter")
tx = pinv.pinv(name="pinv", size=1)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
tx.gds_write(tempgds)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name)
tx.sp_write(tempsp)
tempgds = "{}.gds".format(tx.name)
tx.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
tempsp = "{}.sp".format(tx.name)
tx.sp_write("{0}{1}".format(OPTS.openram_temp, tempsp))
# make sure that the library simulation is successful
sp_delay = self.simulate_delay(test_module=tempsp,
@ -72,12 +72,17 @@ class ngspice_pex_pinv_test(openram_test):
def simulate_delay(self, test_module, top_level_name):
from charutils import parse_spice_list
cwd = os.getcwd()
os.chdir(OPTS.openram_temp)
# setup simulation
sim_file = OPTS.openram_temp + "stim.sp"
sim_file = "stim.sp"
log_file_name = "timing"
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
test_sim.run_sim("stim.sp")
delay = parse_spice_list(log_file_name, "pinv_delay")
os.chdir(cwd)
return delay
def write_simulation(self, sim_file, cir_file, top_module_name):
@ -89,6 +94,7 @@ class ngspice_pex_pinv_test(openram_test):
simulation = stimuli(sim_file, corner)
# library files
import pdb; pdb.set_trace()
simulation.write_include(cir_file)
# supply voltages

View File

@ -53,21 +53,27 @@ class openram_back_end_test(openram_test):
else:
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME)
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file,
out_path,
options,
config_name,
out_path)
cmd = "{0} -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file,
out_path,
options,
config_name,
out_path)
debug.info(1, cmd)
os.system(cmd)
# assert an error until we actually check a resul
for extension in ["gds", "v", "lef", "sp"]:
filename = "{0}/{1}.{2}".format(out_path, out_file, extension)
for extension in ["gds", "v", "lef", "sp", "lvs.sp"]:
filename = "{0}{1}.{2}".format(out_path, out_file, extension)
debug.info(1, "Checking for file: " + filename)
self.assertEqual(os.path.exists(filename), True)
# check if the auxiliary scripts were created
for out_file in ["run_drc.sh", "run_lvs.sh"]:
filename = "{0}{1}".format(out_path, out_file)
debug.info(1, "Checking for file: " + filename)
self.assertEqual(os.path.exists(filename), True)
# Make sure there is any .lib file
import glob
files = glob.glob('{0}/*.lib'.format(out_path))
@ -79,7 +85,7 @@ class openram_back_end_test(openram_test):
self.assertTrue(len(datasheets)>0)
# grep any errors from the output
output_log = open("{0}/output.log".format(out_path), "r")
output_log = open("{0}output.log".format(out_path), "r")
output = output_log.read()
output_log.close()
self.assertEqual(len(re.findall('ERROR', output)), 0)

View File

@ -12,6 +12,5 @@ num_words = 16
tech_name = OPTS.tech_name
nominal_corner_only = True
route_supplies = True
check_lvsdrc = True

View File

@ -12,9 +12,7 @@ num_words = 16
tech_name = OPTS.tech_name
nominal_corner_only = True
route_supplies = True
check_lvsdrc = True
inline_lvsdrc = True
analytical_delay = False
spice_name = "ngspice"

View File

@ -3,7 +3,6 @@
04_precharge_pbitcell_test.py
04_replica_pbitcell_test.py
04_column_mux_pbitcell_test.py
05_bitcell_1rw_1r_array_test.py
05_bitcell_array_test.py
05_dummy_array_test.py
05_pbitcell_array_test.py

View File

@ -21,8 +21,8 @@ class openram_test(unittest.TestCase):
self.reset()
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, w.name)
w.gds_write(tempgds)
tempgds = "{}.gds".format(w.name)
w.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
import verify
result=verify.run_drc(w.name, tempgds, None)
@ -36,13 +36,13 @@ class openram_test(unittest.TestCase):
self.reset()
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name)
tempspice = "{}.sp".format(a.name)
tempgds = "{}.gds".format(a.name)
a.lvs_write(tempspice)
a.lvs_write("{0}{1}".format(OPTS.openram_temp, tempspice))
# cannot write gds in netlist_only mode
if not OPTS.netlist_only:
a.gds_write(tempgds)
a.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
import verify
# Run both DRC and LVS even if DRC might fail
@ -80,15 +80,13 @@ class openram_test(unittest.TestCase):
self.cleanup()
def run_pex(self, a, output=None):
if output == None:
output = OPTS.openram_temp + a.name + ".pex.netlist"
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name)
tempspice = "{}.sp".format(a.name)
tempgds = "{}.gds".format(a.name)
a.gds_write(tempgds)
a.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
import verify
result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False)
result=verify.run_pex(a.name, tempgds, tempspice, final_verification=False)
if result != 0:
self.fail("PEX ERROR: {}".format(a.name))
return output

View File

@ -23,7 +23,6 @@ from tech import lvs_name
from tech import pex_name
debug.info(1, "Initializing verify...")
if not OPTS.check_lvsdrc:
debug.info(1, "LVS/DRC/PEX disabled.")
OPTS.drc_exe = None

View File

@ -63,8 +63,8 @@ def write_drc_script(cell_name, gds_name, extract, final_verification=False, out
run_file = output_path + "run_drc.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
cmd = "{0} -gui -drc {1}drc_runset -batch".format(OPTS.drc_exe[1],
output_path)
cmd = "{0} -gui -drc drc_runset -batch".format(OPTS.drc_exe[1])
f.write(cmd)
f.write("\n")
f.close()
@ -125,8 +125,8 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out
run_file = output_path + "run_lvs.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
cmd = "{0} -gui -lvs {1}lvs_runset -batch".format(OPTS.lvs_exe[1],
output_path)
cmd = "{0} -gui -lvs lvs_runset -batch".format(OPTS.lvs_exe[1])
f.write(cmd)
f.write("\n")
f.close()
@ -178,8 +178,8 @@ def write_pex_script(cell_name, extract, output, final_verification=False, outpu
run_file = output_path + "run_pex.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
cmd = "{0} -gui -pex {1}pex_runset -batch".format(OPTS.pex_exe[1],
OPTS.openram_temp)
cmd = "{0} -gui -pex pex_runset -batch".format(OPTS.pex_exe[1])
f.write(cmd)
f.write("\n")
f.close()
@ -195,22 +195,8 @@ def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=Fals
global num_drc_runs
num_drc_runs += 1
# Filter the layouts through magic as a GDS filter for nsdm/psdm/nwell merging
# Disabled for now
if False and OPTS.tech_name == "sky130":
shutil.copy(gds_name, OPTS.openram_temp + "temp.gds")
from magic import filter_gds
filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds")
else:
# Copy file to local dir if it isn't already
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp)
drc_runset = write_drc_script(cell_name, gds_name, extract, final_verification, OPTS.openram_temp)
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp + os.path.basename(gds_name))
(outfile, errfile, resultsfile) = run_script(cell_name, "drc")
# check the result for these lines in the summary:
@ -251,12 +237,6 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
lvs_runset = write_lvs_script(cell_name, gds_name, sp_name, final_verification, OPTS.openram_temp)
# Copy file to local dir if it isn't already
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp)
if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)):
shutil.copy(sp_name, OPTS.openram_temp)
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
# check the result for these lines in the summary:
@ -338,12 +318,6 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False)
write_pex_script(cell_name, True, output, final_verification, OPTS.openram_temp)
# Copy file to local dir if it isn't already
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp)
if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)):
shutil.copy(sp_name, OPTS.openram_temp)
(outfile, errfile, resultsfile) = run_script(cell_name, "pex")

View File

@ -68,7 +68,6 @@ num_pex_runs = 0
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path, sp_name=None):
""" Write a magic script to perform DRC and optionally extraction. """
global OPTS
# Copy .magicrc file into the output directory
@ -78,33 +77,28 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
else:
debug.warning("Could not locate .magicrc file: {}".format(magic_file))
run_file = output_path + "run_drc.sh"
run_file = output_path + "run_ext.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
f.write('export OPENRAM_TECH="{}"\n'.format(os.environ['OPENRAM_TECH']))
f.write('echo "$(date): Starting DRC using Magic {}"\n'.format(OPTS.drc_exe[1]))
f.write('echo "$(date): Starting GDS to MAG using Magic {}"\n'.format(OPTS.drc_exe[1]))
f.write('\n')
f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1]))
# Do not run DRC for extraction/conversion
f.write("drc off\n")
f.write("gds polygon subcell true\n")
f.write("gds warning default\n")
f.write("gds flatten true\n")
f.write("gds readonly true\n")
f.write("gds ordering true\n")
f.write("#gds ordering true\n")
f.write("gds read {}\n".format(gds_name))
f.write('puts "Finished reading gds {}"\n'.format(gds_name))
f.write("load {}\n".format(cell_name))
f.write('puts "Finished loading cell {}"\n'.format(cell_name))
f.write("cellname delete \\(UNNAMED\\)\n")
f.write("writeall force\n")
f.write("select top cell\n")
f.write("expand\n")
f.write('puts "Finished expanding"\n')
f.write("drc check\n")
f.write('puts "Finished drc check"\n')
f.write("drc catchup\n")
f.write('puts "Finished drc catchup"\n')
f.write("drc count total\n")
f.write("drc count\n")
# Extract
if not sp_name:
f.write("port makeall\n")
else:
@ -139,6 +133,45 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
f.write(pre + "ext2spice format ngspice\n")
f.write(pre + "ext2spice {}\n".format(cell_name))
f.write('puts "Finished ext2spice"\n')
f.write("quit -noprompt\n")
f.write("EOF\n")
f.write("magic_retcode=$?\n")
f.write('echo "$(date): Finished ($magic_retcode) GDS to MAG using Magic {}"\n'.format(OPTS.drc_exe[1]))
f.write("exit $magic_retcode\n")
f.close()
os.system("chmod u+x {}".format(run_file))
run_file = output_path + "run_drc.sh"
f = open(run_file, "w")
f.write("#!/bin/sh\n")
f.write('export OPENRAM_TECH="{}"\n'.format(os.environ['OPENRAM_TECH']))
# Copy the bitcell mag files if they exist
try:
from tech import blackbox_cells
except ImportError:
blackbox_cells = []
for blackbox_cell_name in blackbox_cells:
mag_file = OPTS.openram_tech + "maglef_lib/" + blackbox_cell_name + ".mag"
debug.check(os.path.isfile(mag_file), "Could not find blackbox cell {}".format(mag_file))
f.write('cp {0} .\n'.format(mag_file))
f.write('echo "$(date): Starting DRC using Magic {}"\n'.format(OPTS.drc_exe[1]))
f.write('\n')
f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1]))
f.write("load {} -dereference\n".format(cell_name))
f.write('puts "Finished loading cell {}"\n'.format(cell_name))
f.write("cellname delete \\(UNNAMED\\)\n")
f.write("select top cell\n")
f.write("expand\n")
f.write('puts "Finished expanding"\n')
f.write("drc euclidean on\n")
f.write("drc check\n")
f.write('puts "Finished drc check"\n')
f.write("drc catchup\n")
f.write('puts "Finished drc catchup"\n')
f.write("drc count total\n")
f.write("quit -noprompt\n")
f.write("EOF\n")
f.write("magic_retcode=$?\n")
@ -147,7 +180,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
f.close()
os.system("chmod u+x {}".format(run_file))
def run_drc(cell_name, gds_name, sp_name=None, extract=True, final_verification=False):
"""Run DRC check on a cell which is implemented in gds_name."""
@ -155,12 +188,10 @@ def run_drc(cell_name, gds_name, sp_name=None, extract=True, final_verification=
global num_drc_runs
num_drc_runs += 1
# Copy file to local dir if it isn't already
if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'):
shutil.copy(gds_name, OPTS.openram_temp)
write_drc_script(cell_name, gds_name, extract, final_verification, OPTS.openram_temp, sp_name=sp_name)
(outfile, errfile, resultsfile) = run_script(cell_name, "ext")
(outfile, errfile, resultsfile) = run_script(cell_name, "drc")
# Check the result for these lines in the summary:
@ -244,12 +275,6 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False, output_path=
if not output_path:
output_path = OPTS.openram_temp
# Copy file to local dir if it isn't already
if os.path.dirname(gds_name) != output_path.rstrip('/'):
shutil.copy(gds_name, output_path)
if os.path.dirname(sp_name) != output_path.rstrip('/'):
shutil.copy(sp_name, output_path)
write_lvs_script(cell_name, gds_name, sp_name, final_verification)
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")

View File

@ -17,8 +17,8 @@ lvs_warned = False
pex_warned = False
def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None):
pass
def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None, sp_name=None):
debug.error("Cannot write DRC script for unknown tool", -1)
def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=False, output_path=None):