mirror of https://github.com/VLSIDA/OpenRAM.git
Merged and addressed conflict in delay.py
This commit is contained in:
commit
cd84cf1973
20
README.md
20
README.md
|
|
@ -79,25 +79,6 @@ You may get the entire [FreePDK45 PDK here][FreePDK45].
|
||||||
If you are using [SCMOS], you should install [Magic] and [Netgen].
|
If you are using [SCMOS], you should install [Magic] and [Netgen].
|
||||||
We have included the most recent SCN4M_SUBM design rules from [Qflow].
|
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
|
# Basic Usage
|
||||||
|
|
||||||
Once you have defined the environment, you can run OpenRAM from the command line
|
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
|
You can see all of the options for the configuration file in
|
||||||
$OPENRAM\_HOME/options.py
|
$OPENRAM\_HOME/options.py
|
||||||
|
|
||||||
|
|
||||||
# Unit Tests
|
# Unit Tests
|
||||||
|
|
||||||
Regression testing performs a number of tests for all modules in OpenRAM.
|
Regression testing performs a number of tests for all modules in OpenRAM.
|
||||||
|
|
|
||||||
|
|
@ -446,7 +446,7 @@ class path(geometry):
|
||||||
class label(geometry):
|
class label(geometry):
|
||||||
"""Represents a text label"""
|
"""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"""
|
"""Initializes a text label for specified layer"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.name = "label"
|
self.name = "label"
|
||||||
|
|
@ -455,8 +455,11 @@ class label(geometry):
|
||||||
self.layerPurpose = lpp[1]
|
self.layerPurpose = lpp[1]
|
||||||
self.offset = vector(offset).snap_to_grid()
|
self.offset = vector(offset).snap_to_grid()
|
||||||
|
|
||||||
if zoom<0:
|
if not zoom:
|
||||||
self.zoom = tech.GDS["zoom"]
|
try:
|
||||||
|
self.zoom = tech.GDS["zoom"]
|
||||||
|
except:
|
||||||
|
self.zoom = None
|
||||||
else:
|
else:
|
||||||
self.zoom = zoom
|
self.zoom = zoom
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
# Do not run if disabled in options.
|
# Do not run if disabled in options.
|
||||||
elif (OPTS.inline_lvsdrc or force_check or final_verification):
|
elif (OPTS.inline_lvsdrc or force_check or final_verification):
|
||||||
|
|
||||||
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name)
|
tempspice = "{}.sp".format(self.name)
|
||||||
self.lvs_write(tempspice)
|
self.lvs_write("{0}{1}".format(OPTS.openram_temp, tempspice))
|
||||||
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
|
tempgds = "{}.gds".format(self.name)
|
||||||
self.gds_write(tempgds)
|
self.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
|
||||||
# Final verification option does not allow nets to be connected by label.
|
# Final verification option does not allow nets to be connected by label.
|
||||||
self.drc_errors = verify.run_drc(self.cell_name, tempgds, tempspice, extract=True, final_verification=final_verification)
|
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)
|
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:
|
if OPTS.netlist_only:
|
||||||
return
|
return
|
||||||
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, self.name)
|
tempspice = "{}.sp".format(self.name)
|
||||||
self.lvs_write(tempspice)
|
self.lvs_write("{0}{1}".format(OPTS.openram_temp, tempspice))
|
||||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, self.cell_name)
|
tempgds = "{}.gds".format(self.cell_name)
|
||||||
self.gds_write(tempgds)
|
self.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
|
||||||
num_errors = verify.run_drc(self.cell_name, tempgds, tempspice, final_verification=final_verification)
|
num_errors = verify.run_drc(self.cell_name, tempgds, tempspice, final_verification=final_verification)
|
||||||
debug.check(num_errors == 0,
|
debug.check(num_errors == 0,
|
||||||
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
|
"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:
|
if OPTS.netlist_only:
|
||||||
return
|
return
|
||||||
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, self.cell_name)
|
tempspice = "{}.sp".format(self.cell_name)
|
||||||
self.lvs_write(tempspice)
|
self.lvs_write("{0}{1}".format(OPTS.openram_temp, tempspice))
|
||||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, self.name)
|
tempgds = "{}.gds".format(self.name)
|
||||||
self.gds_write(tempgds)
|
self.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
|
||||||
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
|
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
|
||||||
debug.check(num_errors == 0,
|
debug.check(num_errors == 0,
|
||||||
"LVS failed for {0} with {1} error(s)".format(self.cell_name,
|
"LVS failed for {0} with {1} error(s)".format(self.cell_name,
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ class layout():
|
||||||
# This is commented out for runtime reasons
|
# This is commented out for runtime reasons
|
||||||
# debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
|
# debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
|
||||||
return self.insts[-1]
|
return self.insts[-1]
|
||||||
|
|
||||||
def get_inst(self, name):
|
def get_inst(self, name):
|
||||||
""" Retrieve an instance by name """
|
""" Retrieve an instance by name """
|
||||||
for inst in self.insts:
|
for inst in self.insts:
|
||||||
|
|
@ -244,6 +244,19 @@ class layout():
|
||||||
return inst
|
return inst
|
||||||
return None
|
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):
|
def add_rect(self, layer, offset, width=None, height=None):
|
||||||
"""
|
"""
|
||||||
Adds a rectangle on a given layer,offset with width and height
|
Adds a rectangle on a given layer,offset with width and height
|
||||||
|
|
@ -454,6 +467,23 @@ class layout():
|
||||||
"""
|
"""
|
||||||
self.pin_map[text] = set()
|
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):
|
def add_layout_pin(self, text, layer, offset, width=None, height=None):
|
||||||
"""
|
"""
|
||||||
Create a labeled pin
|
Create a labeled pin
|
||||||
|
|
@ -498,7 +528,7 @@ class layout():
|
||||||
offset=offset + vector(0.5 * width,
|
offset=offset + vector(0.5 * width,
|
||||||
0.5 * height))
|
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"""
|
"""Adds a text label on the given layer,offset, and zoom level"""
|
||||||
debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset))
|
debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset))
|
||||||
lpp = techlayer[layer]
|
lpp = techlayer[layer]
|
||||||
|
|
@ -976,7 +1006,7 @@ class layout():
|
||||||
(horizontal_layer, via_layer, vertical_layer) = layer_stack
|
(horizontal_layer, via_layer, vertical_layer) = layer_stack
|
||||||
if horizontal:
|
if horizontal:
|
||||||
route_layer = vertical_layer
|
route_layer = vertical_layer
|
||||||
bys_layer = horizontal_layer
|
bus_layer = horizontal_layer
|
||||||
else:
|
else:
|
||||||
route_layer = horizontal_layer
|
route_layer = horizontal_layer
|
||||||
bus_layer = vertical_layer
|
bus_layer = vertical_layer
|
||||||
|
|
@ -1078,18 +1108,24 @@ class layout():
|
||||||
"""
|
"""
|
||||||
import channel_route
|
import channel_route
|
||||||
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self)
|
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self)
|
||||||
self.add_inst(cr.name, cr)
|
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
|
||||||
self.connect_inst([])
|
# 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):
|
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
|
||||||
"""
|
"""
|
||||||
Wrapper to create a horizontal channel route
|
Wrapper to create a horizontal channel route
|
||||||
"""
|
"""
|
||||||
import channel_route
|
import channel_route
|
||||||
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
|
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
|
||||||
self.add_inst(cr.name, cr)
|
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
|
||||||
self.connect_inst([])
|
# 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):
|
def add_boundary(self, ll=vector(0, 0), ur=None):
|
||||||
""" Add boundary for debugging dimensions """
|
""" Add boundary for debugging dimensions """
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
|
|
@ -1172,6 +1208,21 @@ class layout():
|
||||||
elif add_vias:
|
elif add_vias:
|
||||||
self.add_power_pin(name, pin.center(), start_layer=pin.layer)
|
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"):
|
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
|
Add a single power pin from the lowest power_grid layer down to M1 (or li) at
|
||||||
|
|
|
||||||
|
|
@ -380,20 +380,26 @@ class pin_layout:
|
||||||
label_purpose = purpose
|
label_purpose = purpose
|
||||||
|
|
||||||
newLayout.addBox(layerNumber=layer_num,
|
newLayout.addBox(layerNumber=layer_num,
|
||||||
purposeNumber=pin_purpose,
|
purposeNumber=purpose,
|
||||||
offsetInMicrons=self.ll(),
|
offsetInMicrons=self.ll(),
|
||||||
width=self.width(),
|
width=self.width(),
|
||||||
height=self.height(),
|
height=self.height(),
|
||||||
center=False)
|
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
|
# This fixes some pin label offsetting when GDS gets
|
||||||
# imported into Magic.
|
# imported into Magic.
|
||||||
newLayout.addText(text=self.name,
|
newLayout.addText(text=self.name,
|
||||||
layerNumber=layer_num,
|
layerNumber=layer_num,
|
||||||
purposeNumber=label_purpose,
|
purposeNumber=label_purpose,
|
||||||
offsetInMicrons=self.center(),
|
offsetInMicrons=self.center())
|
||||||
magnification=GDS["zoom"],
|
|
||||||
rotate=None)
|
|
||||||
|
|
||||||
def compute_overlap(self, other):
|
def compute_overlap(self, other):
|
||||||
""" Calculate the rectangular overlap of two rectangles. """
|
""" Calculate the rectangular overlap of two rectangles. """
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -11,7 +11,7 @@ nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = True
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
perimeter_pins = True
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
|
output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ num_w_ports = 0
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corner_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = "tree"
|
||||||
check_lvsdrc = False
|
check_lvsdrc = True
|
||||||
perimeter_pins = False
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import math
|
|
||||||
|
|
||||||
class GdsStructure:
|
class GdsStructure:
|
||||||
"""Class represent a GDS Structure Object"""
|
"""Class represent a GDS Structure Object"""
|
||||||
|
|
@ -15,6 +15,7 @@ class GdsStructure:
|
||||||
self.nodes=[]
|
self.nodes=[]
|
||||||
self.boxes=[]
|
self.boxes=[]
|
||||||
|
|
||||||
|
|
||||||
class GdsBoundary:
|
class GdsBoundary:
|
||||||
"""Class represent a GDS Boundary Object"""
|
"""Class represent a GDS Boundary Object"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -24,6 +25,7 @@ class GdsBoundary:
|
||||||
self.purposeLayer=0
|
self.purposeLayer=0
|
||||||
self.coordinates=""
|
self.coordinates=""
|
||||||
|
|
||||||
|
|
||||||
class GdsPath:
|
class GdsPath:
|
||||||
"""Class represent a GDS Path Object"""
|
"""Class represent a GDS Path Object"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -112,6 +114,7 @@ class GdsPath:
|
||||||
lastY = y
|
lastY = y
|
||||||
return boundaryEquivalent
|
return boundaryEquivalent
|
||||||
|
|
||||||
|
|
||||||
class GdsSref:
|
class GdsSref:
|
||||||
"""Class represent a GDS structure reference Object"""
|
"""Class represent a GDS structure reference Object"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -123,6 +126,7 @@ class GdsSref:
|
||||||
self.rotateAngle=""
|
self.rotateAngle=""
|
||||||
self.coordinates=""
|
self.coordinates=""
|
||||||
|
|
||||||
|
|
||||||
class GdsAref:
|
class GdsAref:
|
||||||
"""Class represent a GDS array reference Object"""
|
"""Class represent a GDS array reference Object"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -134,6 +138,7 @@ class GdsAref:
|
||||||
self.rotateAngle=""
|
self.rotateAngle=""
|
||||||
self.coordinates=""
|
self.coordinates=""
|
||||||
|
|
||||||
|
|
||||||
class GdsText:
|
class GdsText:
|
||||||
"""Class represent a GDS text Object"""
|
"""Class represent a GDS text Object"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -150,6 +155,7 @@ class GdsText:
|
||||||
self.coordinates=""
|
self.coordinates=""
|
||||||
self.textString = ""
|
self.textString = ""
|
||||||
|
|
||||||
|
|
||||||
class GdsNode:
|
class GdsNode:
|
||||||
"""Class represent a GDS Node Object"""
|
"""Class represent a GDS Node Object"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -159,6 +165,7 @@ class GdsNode:
|
||||||
self.nodeType=""
|
self.nodeType=""
|
||||||
self.coordinates=""
|
self.coordinates=""
|
||||||
|
|
||||||
|
|
||||||
class GdsBox:
|
class GdsBox:
|
||||||
"""Class represent a GDS Box Object"""
|
"""Class represent a GDS Box Object"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,25 @@
|
||||||
from .gdsPrimitives import *
|
from .gdsPrimitives import *
|
||||||
from datetime import *
|
from datetime import *
|
||||||
#from mpmath import matrix
|
|
||||||
#from numpy import matrix
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
#import gdsPrimitives
|
import math
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
|
||||||
class VlsiLayout:
|
class VlsiLayout:
|
||||||
"""Class represent a hierarchical layout"""
|
"""Class represent a hierarchical layout"""
|
||||||
|
|
||||||
def __init__(self, name=None, units=(0.001,1e-9), libraryName = "DEFAULT.DB", gdsVersion=5):
|
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
|
# keep a list of all the structures in this layout
|
||||||
self.units = units
|
self.units = units
|
||||||
#print(units)
|
# print(units)
|
||||||
modDate = datetime.now()
|
modDate = datetime.now()
|
||||||
self.structures=dict()
|
self.structures=dict()
|
||||||
self.layerNumbersInUse = []
|
self.layerNumbersInUse = []
|
||||||
self.debug = False
|
self.debug = False
|
||||||
if name:
|
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)
|
self.rootStructureName=self.padText(name)
|
||||||
#create the ROOT structure
|
# create the ROOT structure
|
||||||
self.structures[self.rootStructureName] = GdsStructure()
|
self.structures[self.rootStructureName] = GdsStructure()
|
||||||
self.structures[self.rootStructureName].name = name
|
self.structures[self.rootStructureName].name = name
|
||||||
self.structures[self.rootStructureName].createDate = (modDate.year,
|
self.structures[self.rootStructureName].createDate = (modDate.year,
|
||||||
|
|
@ -36,7 +35,7 @@ class VlsiLayout:
|
||||||
modDate.minute,
|
modDate.minute,
|
||||||
modDate.second)
|
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['units']=self.units
|
||||||
self.info['dates']=(modDate.year,
|
self.info['dates']=(modDate.year,
|
||||||
modDate.month,
|
modDate.month,
|
||||||
|
|
@ -53,12 +52,13 @@ class VlsiLayout:
|
||||||
self.info['libraryName']=libraryName
|
self.info['libraryName']=libraryName
|
||||||
self.info['gdsVersion']=gdsVersion
|
self.info['gdsVersion']=gdsVersion
|
||||||
|
|
||||||
self.xyTree = [] #This will contain a list of all structure names
|
# This will contain a list of all structure names
|
||||||
#expanded to include srefs / arefs separately.
|
# expanded to include srefs / arefs separately.
|
||||||
#each structure will have an X,Y,offset, and rotate associated
|
# each structure will have an X,Y,offset, and rotate associated
|
||||||
#with it. Populate via traverseTheHierarchy method.
|
# with it. Populate via traverseTheHierarchy method.
|
||||||
|
self.xyTree = []
|
||||||
|
|
||||||
#temp variables used in delegate functions
|
# temp variables used in delegate functions
|
||||||
self.tempCoordinates=None
|
self.tempCoordinates=None
|
||||||
self.tempPassFail = True
|
self.tempPassFail = True
|
||||||
|
|
||||||
|
|
@ -68,12 +68,12 @@ class VlsiLayout:
|
||||||
self.pins = {}
|
self.pins = {}
|
||||||
|
|
||||||
def rotatedCoordinates(self,coordinatesToRotate,rotateAngle):
|
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))
|
angle=math.radians(float(0))
|
||||||
if(rotateAngle):
|
if(rotateAngle):
|
||||||
angle = math.radians(float(rotateAngle))
|
angle = math.radians(float(rotateAngle))
|
||||||
|
|
||||||
coordinatesRotate = [] #this will hold the rotated values
|
coordinatesRotate = [] # this will hold the rotated values
|
||||||
for coordinate in coordinatesToRotate:
|
for coordinate in coordinatesToRotate:
|
||||||
# This is the CCW rotation matrix
|
# This is the CCW rotation matrix
|
||||||
newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle)
|
newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle)
|
||||||
|
|
@ -82,53 +82,51 @@ class VlsiLayout:
|
||||||
return coordinatesRotate
|
return coordinatesRotate
|
||||||
|
|
||||||
def rename(self,newName):
|
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] = self.structures[self.rootStructureName]
|
||||||
self.structures[newName].name = self.padText(newName)
|
self.structures[newName].name = self.padText(newName)
|
||||||
#and delete the old root
|
# and delete the old root
|
||||||
del self.structures[self.rootStructureName]
|
del self.structures[self.rootStructureName]
|
||||||
self.rootStructureName = newName
|
self.rootStructureName = newName
|
||||||
#repopulate the 2d map so drawing occurs correctly
|
# repopulate the 2d map so drawing occurs correctly
|
||||||
del self.xyTree[:]
|
del self.xyTree[:]
|
||||||
self.populateCoordinateMap()
|
self.populateCoordinateMap()
|
||||||
|
|
||||||
def newLayout(self,newName):
|
def newLayout(self,newName):
|
||||||
#if (newName == "" | newName == 0):
|
# if (newName == "" | newName == 0):
|
||||||
# print("ERROR: vlsiLayout.py:newLayout newName is null")
|
# print("ERROR: vlsiLayout.py:newLayout newName is null")
|
||||||
|
|
||||||
#make sure the newName is a multiple of 2 characters
|
# make sure the newName is a multiple of 2 characters
|
||||||
#if(len(newName)%2 == 1):
|
# if(len(newName)%2 == 1):
|
||||||
#pad with a zero
|
# pad with a zero
|
||||||
#newName = newName + '\x00'
|
# newName = newName + '\x00'
|
||||||
#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] = self.structures[self.rootStructureName]
|
||||||
|
|
||||||
modDate = datetime.now()
|
modDate = datetime.now()
|
||||||
|
|
||||||
self.structures[newName] = GdsStructure()
|
self.structures[newName] = GdsStructure()
|
||||||
self.structures[newName].name = newName
|
self.structures[newName].name = newName
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.rootStructureName = newName
|
self.rootStructureName = newName
|
||||||
|
|
||||||
self.rootStructureName=newName
|
self.rootStructureName=newName
|
||||||
|
|
||||||
#create the ROOT structure
|
# create the ROOT structure
|
||||||
self.structures[self.rootStructureName] = GdsStructure()
|
self.structures[self.rootStructureName] = GdsStructure()
|
||||||
#self.structures[self.rootStructureName].name = name
|
# self.structures[self.rootStructureName].name = name
|
||||||
self.structures[self.rootStructureName].createDate = (modDate.year,
|
self.structures[self.rootStructureName].createDate = (modDate.year,
|
||||||
modDate.month,
|
modDate.month,
|
||||||
modDate.day,
|
modDate.day,
|
||||||
modDate.hour,
|
modDate.hour,
|
||||||
modDate.minute,
|
modDate.minute,
|
||||||
modDate.second)
|
modDate.second)
|
||||||
self.structures[self.rootStructureName].modDate = (modDate.year,
|
self.structures[self.rootStructureName].modDate = (modDate.year,
|
||||||
modDate.month,
|
modDate.month,
|
||||||
modDate.day,
|
modDate.day,
|
||||||
modDate.hour,
|
modDate.hour,
|
||||||
modDate.minute,
|
modDate.minute,
|
||||||
modDate.second)
|
modDate.second)
|
||||||
|
|
||||||
|
|
||||||
#repopulate the 2d map so drawing occurs correctly
|
#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)))
|
debug.check(len(structureNames)==1,"Multiple possible root structures in the layout: {}".format(str(structureNames)))
|
||||||
self.rootStructureName = structureNames[0]
|
self.rootStructureName = structureNames[0]
|
||||||
|
|
||||||
|
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction=None,
|
||||||
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None,
|
transformPath=[], rotateAngle=0, transFlags=[0, 0, 0], coordinates=(0, 0)):
|
||||||
transformPath = [], rotateAngle = 0, transFlags = [0,0,0], coordinates = (0,0)):
|
# since this is a recursive function, must deal with the default
|
||||||
#since this is a recursive function, must deal with the default
|
# parameters explicitly
|
||||||
#parameters explicitly
|
|
||||||
if startingStructureName == None:
|
if startingStructureName == None:
|
||||||
startingStructureName = self.rootStructureName
|
startingStructureName = self.rootStructureName
|
||||||
|
|
||||||
#set up the rotation matrix
|
# set up the rotation matrix
|
||||||
if(rotateAngle == None or rotateAngle == ""):
|
if(rotateAngle == None or rotateAngle == ""):
|
||||||
angle = 0
|
angle = 0
|
||||||
else:
|
else:
|
||||||
# MRG: Added negative to make CCW rotate 8/29/18
|
# MRG: Added negative to make CCW rotate 8/29/18
|
||||||
angle = math.radians(float(rotateAngle))
|
angle = math.radians(float(rotateAngle))
|
||||||
mRotate = np.array([[math.cos(angle),-math.sin(angle),0.0],
|
mRotate = np.array([[math.cos(angle), -math.sin(angle), 0.0],
|
||||||
[math.sin(angle),math.cos(angle),0.0],
|
[math.sin(angle), math.cos(angle), 0.0],
|
||||||
[0.0,0.0,1.0]])
|
[0.0, 0.0, 1.0]])
|
||||||
#set up the translation matrix
|
# set up the translation matrix
|
||||||
translateX = float(coordinates[0])
|
translateX = float(coordinates[0])
|
||||||
translateY = float(coordinates[1])
|
translateY = float(coordinates[1])
|
||||||
mTranslate = np.array([[1.0,0.0,translateX],[0.0,1.0,translateY],[0.0,0.0,1.0]])
|
mTranslate = np.array([[1.0, 0.0, translateX],
|
||||||
#set up the scale matrix (handles mirror X)
|
[0.0, 1.0, translateY],
|
||||||
|
[0.0, 0.0, 1.0]])
|
||||||
|
# set up the scale matrix (handles mirror X)
|
||||||
scaleX = 1.0
|
scaleX = 1.0
|
||||||
if(transFlags[0]):
|
if (transFlags[0]):
|
||||||
scaleY = -1.0
|
scaleY = -1.0
|
||||||
else:
|
else:
|
||||||
scaleY = 1.0
|
scaleY = 1.0
|
||||||
mScale = np.array([[scaleX,0.0,0.0],[0.0,scaleY,0.0],[0.0,0.0,1.0]])
|
mScale = np.array([[scaleX, 0.0, 0.0],
|
||||||
#we need to keep track of all transforms in the hierarchy
|
[0.0, scaleY, 0.0],
|
||||||
#when we add an element to the xy tree, we apply all transforms from the bottom up
|
[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))
|
transformPath.append((mRotate,mScale,mTranslate))
|
||||||
if delegateFunction != None:
|
if delegateFunction != None:
|
||||||
delegateFunction(startingStructureName, transformPath)
|
delegateFunction(startingStructureName, transformPath)
|
||||||
#starting with a particular structure, we will recursively traverse the tree
|
# starting with a particular structure, we will recursively traverse the tree
|
||||||
#********might have to set the recursion level deeper for big layouts!
|
# ********might have to set the recursion level deeper for big layouts!
|
||||||
try:
|
try:
|
||||||
if(len(self.structures[startingStructureName].srefs)>0): #does this structure reference any others?
|
if(len(self.structures[startingStructureName].srefs)>0): #does this structure reference any others?
|
||||||
#if so, go through each and call this function again
|
# if so, go through each and call this function again
|
||||||
#if not, return back to the caller (caller can be this function)
|
# if not, return back to the caller (caller can be this function)
|
||||||
for sref in self.structures[startingStructureName].srefs:
|
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,
|
self.traverseTheHierarchy(startingStructureName = sref.sName,
|
||||||
delegateFunction = delegateFunction,
|
delegateFunction = delegateFunction,
|
||||||
transformPath = transformPath,
|
transformPath = transformPath,
|
||||||
|
|
@ -205,8 +206,8 @@ class VlsiLayout:
|
||||||
except KeyError:
|
except KeyError:
|
||||||
debug.error("Could not find structure {} in GDS file.".format(startingStructureName),-1)
|
debug.error("Could not find structure {} in GDS file.".format(startingStructureName),-1)
|
||||||
|
|
||||||
#MUST HANDLE AREFs HERE AS WELL
|
# MUST HANDLE AREFs HERE AS WELL
|
||||||
#when we return, drop the last transform from the transformPath
|
# when we return, drop the last transform from the transformPath
|
||||||
del transformPath[-1]
|
del transformPath[-1]
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -218,7 +219,6 @@ class VlsiLayout:
|
||||||
for layerNumber in self.layerNumbersInUse:
|
for layerNumber in self.layerNumbersInUse:
|
||||||
self.processLabelPins((layerNumber, None))
|
self.processLabelPins((layerNumber, None))
|
||||||
|
|
||||||
|
|
||||||
def populateCoordinateMap(self):
|
def populateCoordinateMap(self):
|
||||||
def addToXyTree(startingStructureName = None,transformPath = None):
|
def addToXyTree(startingStructureName = None,transformPath = None):
|
||||||
uVector = np.array([[1.0],[0.0],[0.0]]) #start with normal basis vectors
|
uVector = np.array([[1.0],[0.0],[0.0]]) #start with normal basis vectors
|
||||||
|
|
@ -281,8 +281,6 @@ class VlsiLayout:
|
||||||
self.newLayout(newRoot)
|
self.newLayout(newRoot)
|
||||||
self.rootStructureName = newRoot
|
self.rootStructureName = newRoot
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def addInstance(self,layoutToAdd,nameOfLayout=0,offsetInMicrons=(0,0),mirror=None,rotate=None):
|
def addInstance(self,layoutToAdd,nameOfLayout=0,offsetInMicrons=(0,0),mirror=None,rotate=None):
|
||||||
"""
|
"""
|
||||||
Method to insert one layout into another at a particular offset.
|
Method to insert one layout into another at a particular offset.
|
||||||
|
|
@ -406,7 +404,7 @@ class VlsiLayout:
|
||||||
#add the sref to the root structure
|
#add the sref to the root structure
|
||||||
self.structures[self.rootStructureName].paths.append(pathToAdd)
|
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]))
|
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
|
||||||
textToAdd = GdsText()
|
textToAdd = GdsText()
|
||||||
textToAdd.drawingLayer = layerNumber
|
textToAdd.drawingLayer = layerNumber
|
||||||
|
|
@ -415,7 +413,8 @@ class VlsiLayout:
|
||||||
textToAdd.transFlags = [0,0,0]
|
textToAdd.transFlags = [0,0,0]
|
||||||
textToAdd.textString = self.padText(text)
|
textToAdd.textString = self.padText(text)
|
||||||
#textToAdd.transFlags[1] = 1
|
#textToAdd.transFlags[1] = 1
|
||||||
textToAdd.magFactor = magnification
|
if magnification:
|
||||||
|
textToAdd.magFactor = magnification
|
||||||
if rotate:
|
if rotate:
|
||||||
#textToAdd.transFlags[2] = 1
|
#textToAdd.transFlags[2] = 1
|
||||||
textToAdd.rotateAngle = rotate
|
textToAdd.rotateAngle = rotate
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import re
|
||||||
import copy
|
import copy
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
VERSION = "1.1.7"
|
VERSION = "1.1.9"
|
||||||
NAME = "OpenRAM v{}".format(VERSION)
|
NAME = "OpenRAM v{}".format(VERSION)
|
||||||
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
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)
|
dir_name = os.path.dirname(config_file)
|
||||||
module_name = os.path.basename(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
|
# Prepend the path to avoid if we are using the example config
|
||||||
sys.path.insert(0, dir_name)
|
sys.path.insert(0, dir_name)
|
||||||
# Import the configuration file of which modules to use
|
# 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))
|
debug.print_raw("Technology: {0}".format(OPTS.tech_name))
|
||||||
total_size = OPTS.word_size*OPTS.num_words*OPTS.num_banks
|
total_size = OPTS.word_size*OPTS.num_words*OPTS.num_banks
|
||||||
debug.print_raw("Total size: {} bits".format(total_size))
|
debug.print_raw("Total size: {} bits".format(total_size))
|
||||||
if total_size >= 2**14:
|
if total_size >= 2**14 and not OPTS.analytical_delay:
|
||||||
debug.warning("Requesting such a large memory size ({0}) will have a large run-time. ".format(total_size) +
|
debug.warning("Characterizing large memories ({0}) will have a large run-time. ".format(total_size))
|
||||||
"Consider using multiple smaller banks.")
|
|
||||||
debug.print_raw("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
|
debug.print_raw("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
|
||||||
OPTS.num_words,
|
OPTS.num_words,
|
||||||
OPTS.num_banks))
|
OPTS.num_banks))
|
||||||
|
|
|
||||||
|
|
@ -391,19 +391,17 @@ class replica_bitcell_array(bitcell_base_array):
|
||||||
for bit in range(self.rbl[1]):
|
for bit in range(self.rbl[1]):
|
||||||
dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul()
|
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,
|
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):
|
def add_end_caps(self):
|
||||||
""" Add dummy cells or end caps around the array """
|
""" 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 if even number of rows)
|
||||||
# Far top dummy row (first row above array is NOT flipped)
|
flip_dummy = (self.row_size + self.rbl[1]) % 2
|
||||||
flip_dummy = self.rbl[1] % 2
|
|
||||||
dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul()
|
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,
|
self.dummy_row_insts[1].place(offset=dummy_row_offset,
|
||||||
mirror="MX" if flip_dummy else "R0")
|
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)
|
# Far bottom dummy row (first row below array IS flipped)
|
||||||
flip_dummy = (self.rbl[0] + 1) % 2
|
flip_dummy = (self.rbl[0] + 1) % 2
|
||||||
dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset
|
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):
|
def add_layout_pins(self):
|
||||||
""" Add the layout pins """
|
""" Add the layout pins """
|
||||||
|
|
||||||
#All wordlines
|
# All wordlines
|
||||||
#Main array wl and bl/br
|
# Main array wl and bl/br
|
||||||
|
|
||||||
for pin_name in self.all_wordline_names:
|
for pin_name in self.all_wordline_names:
|
||||||
pin_list = self.bitcell_array_inst.get_pins(pin_name)
|
pin_list = self.bitcell_array_inst.get_pins(pin_name)
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ class replica_column(bitcell_base_array):
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
for row in range(row_range_min, row_range_max):
|
for row in range(row_range_min, row_range_max):
|
||||||
wl_pin = self.cell_inst[row].get_pin(self.cell.get_wl_name(port))
|
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,
|
layer=wl_pin.layer,
|
||||||
offset=wl_pin.ll().scale(0, 1),
|
offset=wl_pin.ll().scale(0, 1),
|
||||||
width=self.width,
|
width=self.width,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import math
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import layer
|
|
||||||
|
|
||||||
class write_mask_and_array(design.design):
|
class write_mask_and_array(design.design):
|
||||||
"""
|
"""
|
||||||
|
|
@ -95,9 +95,9 @@ class write_mask_and_array(design.design):
|
||||||
if not self.offsets:
|
if not self.offsets:
|
||||||
self.offsets = []
|
self.offsets = []
|
||||||
for i in range(self.columns):
|
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
|
self.height = self.and2.height
|
||||||
|
|
||||||
write_bits = self.columns / self.num_wmasks
|
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,
|
self.add_via_stack_center(from_layer=en_pin.layer,
|
||||||
to_layer="m3",
|
to_layer="m3",
|
||||||
offset=en_pos)
|
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"]:
|
for supply in ["gnd", "vdd"]:
|
||||||
supply_pin_left = self.and2_insts[0].get_pin(supply)
|
supply_pin = self.and2_insts[0].get_pin(supply)
|
||||||
supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply)
|
supply_pin_yoffset = supply_pin.cy()
|
||||||
self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()])
|
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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ class options(optparse.Values):
|
||||||
# When enabled, layout is not generated (and no DRC or LVS are performed)
|
# When enabled, layout is not generated (and no DRC or LVS are performed)
|
||||||
netlist_only = False
|
netlist_only = False
|
||||||
# Whether we should do the final power routing
|
# Whether we should do the final power routing
|
||||||
route_supplies = False
|
route_supplies = "tree"
|
||||||
# This determines whether LVS and DRC is checked at all.
|
# This determines whether LVS and DRC is checked at all.
|
||||||
check_lvsdrc = False
|
check_lvsdrc = False
|
||||||
# This determines whether LVS and DRC is checked for every submodule.
|
# 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)
|
# run (doesn't purge on errors, anyhow)
|
||||||
|
|
||||||
# Route the input/output pins to the perimeter
|
# Route the input/output pins to the perimeter
|
||||||
perimeter_pins = False
|
perimeter_pins = True
|
||||||
|
|
||||||
keep_temp = False
|
keep_temp = False
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -158,9 +158,9 @@ class ptx(design.design):
|
||||||
# TEMP FIX: Use old device names if using Calibre.
|
# 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.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.mults,
|
||||||
self.tx_width,
|
self.tx_width,
|
||||||
drc("minwidth_poly"))
|
drc("minwidth_poly"))
|
||||||
elif cell_props.ptx.model_is_subckt:
|
elif cell_props.ptx.model_is_subckt:
|
||||||
# sky130 requires mult parameter too
|
# 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],
|
self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2}u l={3}u".format(spice[self.tx_type],
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
||||||
|
|
@ -5,12 +5,11 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import numpy as np
|
|
||||||
import string
|
|
||||||
import debug
|
import debug
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
from grid_cell import grid_cell
|
from grid_cell import grid_cell
|
||||||
|
|
||||||
|
|
||||||
class grid:
|
class grid:
|
||||||
"""
|
"""
|
||||||
A two layer routing map. Each cell can be blocked in the vertical
|
A two layer routing map. Each cell can be blocked in the vertical
|
||||||
|
|
@ -23,7 +22,6 @@ class grid:
|
||||||
NONPREFERRED_COST = 4
|
NONPREFERRED_COST = 4
|
||||||
PREFERRED_COST = 1
|
PREFERRED_COST = 1
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, ll, ur, track_width):
|
def __init__(self, ll, ur, track_width):
|
||||||
""" Initialize the map and define the costs. """
|
""" Initialize the map and define the costs. """
|
||||||
|
|
||||||
|
|
@ -33,12 +31,12 @@ class grid:
|
||||||
|
|
||||||
self.track_width = track_width
|
self.track_width = track_width
|
||||||
self.track_widths = [self.track_width, self.track_width, 1.0]
|
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
|
# The bounds are in grids for this
|
||||||
# This is really lower left bottom layer and upper right top layer in 3D.
|
# 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.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.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
|
# let's leave the map sparse, cells are created on demand to reduce memory
|
||||||
self.map={}
|
self.map={}
|
||||||
|
|
@ -46,18 +44,18 @@ class grid:
|
||||||
def add_all_grids(self):
|
def add_all_grids(self):
|
||||||
for x in range(self.ll.x, self.ur.x, 1):
|
for x in range(self.ll.x, self.ur.x, 1):
|
||||||
for y in range(self.ll.y, self.ur.y, 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, 0))
|
||||||
self.add_map(vector3d(x,y,1))
|
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):
|
if not isinstance(n, vector3d):
|
||||||
for item in n:
|
for item in n:
|
||||||
self.set_blocked(item,value)
|
self.set_blocked(item, value)
|
||||||
else:
|
else:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
self.map[n].blocked=value
|
self.map[n].blocked=value
|
||||||
|
|
||||||
def is_blocked(self,n):
|
def is_blocked(self, n):
|
||||||
if not isinstance(n, vector3d):
|
if not isinstance(n, vector3d):
|
||||||
for item in n:
|
for item in n:
|
||||||
if self.is_blocked(item):
|
if self.is_blocked(item):
|
||||||
|
|
@ -68,11 +66,10 @@ class grid:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
return self.map[n].blocked
|
return self.map[n].blocked
|
||||||
|
|
||||||
|
def set_path(self, n, value=True):
|
||||||
def set_path(self,n,value=True):
|
if isinstance(n, (list, tuple, set, frozenset)):
|
||||||
if isinstance(n, (list,tuple,set,frozenset)):
|
|
||||||
for item in n:
|
for item in n:
|
||||||
self.set_path(item,value)
|
self.set_path(item, value)
|
||||||
else:
|
else:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
self.map[n].path=value
|
self.map[n].path=value
|
||||||
|
|
@ -81,47 +78,89 @@ class grid:
|
||||||
for k in self.map:
|
for k in self.map:
|
||||||
self.map[k].blocked=False
|
self.map[k].blocked=False
|
||||||
|
|
||||||
def set_source(self,n,value=True):
|
def set_source(self, n, value=True):
|
||||||
if not isinstance(n, vector3d):
|
if not isinstance(n, vector3d):
|
||||||
for item in n:
|
for item in n:
|
||||||
self.set_source(item,value)
|
self.set_source(item, value)
|
||||||
else:
|
else:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
self.map[n].source=value
|
self.map[n].source=value
|
||||||
self.source.add(n)
|
self.source.add(n)
|
||||||
|
|
||||||
def set_target(self,n,value=True):
|
def set_target(self, n, value=True):
|
||||||
if not isinstance(n, vector3d):
|
if not isinstance(n, vector3d):
|
||||||
for item in n:
|
for item in n:
|
||||||
self.set_target(item,value)
|
self.set_target(item, value)
|
||||||
else:
|
else:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
self.map[n].target=value
|
self.map[n].target=value
|
||||||
self.target.add(n)
|
self.target.add(n)
|
||||||
|
|
||||||
|
def add_source(self, track_list, value=True):
|
||||||
def add_source(self,track_list,value=True):
|
debug.info(3, "Adding source list={0}".format(str(track_list)))
|
||||||
debug.info(3,"Adding source list={0}".format(str(track_list)))
|
|
||||||
for n in track_list:
|
for n in track_list:
|
||||||
debug.info(4,"Adding source ={0}".format(str(n)))
|
debug.info(4, "Adding source ={0}".format(str(n)))
|
||||||
self.set_source(n,value)
|
self.set_source(n, value)
|
||||||
self.set_blocked(n,False)
|
self.set_blocked(n, False)
|
||||||
|
|
||||||
|
def add_target(self, track_list, value=True):
|
||||||
def add_target(self,track_list,value=True):
|
debug.info(3, "Adding target list={0}".format(str(track_list)))
|
||||||
debug.info(3,"Adding target list={0}".format(str(track_list)))
|
|
||||||
for n in track_list:
|
for n in track_list:
|
||||||
debug.info(4,"Adding target ={0}".format(str(n)))
|
debug.info(4, "Adding target ={0}".format(str(n)))
|
||||||
self.set_target(n,value)
|
self.set_target(n, value)
|
||||||
self.set_blocked(n,False)
|
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.
|
Point is in the target set, so we are done.
|
||||||
"""
|
"""
|
||||||
return point in self.target
|
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.
|
Add a point to the map if it doesn't exist.
|
||||||
"""
|
"""
|
||||||
|
|
@ -132,8 +171,7 @@ class grid:
|
||||||
if n not in self.map:
|
if n not in self.map:
|
||||||
self.map[n]=grid_cell()
|
self.map[n]=grid_cell()
|
||||||
|
|
||||||
|
def block_path(self, path):
|
||||||
def block_path(self,path):
|
|
||||||
"""
|
"""
|
||||||
Mark the path in the routing grid as blocked.
|
Mark the path in the routing grid as blocked.
|
||||||
Also unsets the path flag.
|
Also unsets the path flag.
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,12 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
from itertools import tee
|
from itertools import tee
|
||||||
from grid import grid
|
from grid import grid
|
||||||
from grid_cell import grid_cell
|
|
||||||
from direction import direction
|
from direction import direction
|
||||||
|
|
||||||
|
|
||||||
class grid_path:
|
class grid_path:
|
||||||
"""
|
"""
|
||||||
A grid path is a list of lists of grid cells.
|
A grid path is a list of lists of grid cells.
|
||||||
|
|
@ -29,8 +28,7 @@ class grid_path:
|
||||||
self.pathlist = []
|
self.pathlist = []
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
#import pprint
|
p = str(self.pathlist)
|
||||||
p = str(self.pathlist) #pprint.pformat(self.pathlist)
|
|
||||||
if self.name != "":
|
if self.name != "":
|
||||||
return (str(self.name) + " : " + p)
|
return (str(self.name) + " : " + p)
|
||||||
return p
|
return p
|
||||||
|
|
@ -188,12 +186,10 @@ class grid_path:
|
||||||
|
|
||||||
return newwave
|
return newwave
|
||||||
|
|
||||||
|
|
||||||
def set_layer(self, zindex):
|
def set_layer(self, zindex):
|
||||||
new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave]
|
new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave]
|
||||||
self.pathlist = new_pathlist
|
self.pathlist = new_pathlist
|
||||||
|
|
||||||
|
|
||||||
def overlap(self, other):
|
def overlap(self, other):
|
||||||
"""
|
"""
|
||||||
Return the overlap waves ignoring different layers
|
Return the overlap waves ignoring different layers
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
Some utility functions for sets of grid cells.
|
Some utility functions for sets of grid cells.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import debug
|
|
||||||
import math
|
import math
|
||||||
from direction import direction
|
from direction import direction
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
|
|
@ -44,6 +43,7 @@ def get_upper_right(curset):
|
||||||
ur = p
|
ur = p
|
||||||
return ur
|
return ur
|
||||||
|
|
||||||
|
|
||||||
def get_lower_left(curset):
|
def get_lower_left(curset):
|
||||||
ll = None
|
ll = None
|
||||||
for p in curset:
|
for p in curset:
|
||||||
|
|
@ -51,7 +51,8 @@ def get_lower_left(curset):
|
||||||
ll = p
|
ll = p
|
||||||
return ll
|
return ll
|
||||||
|
|
||||||
def get_border( curset, direct):
|
|
||||||
|
def get_border(curset, direct):
|
||||||
"""
|
"""
|
||||||
Return the furthest cell(s) in a given direction.
|
Return the furthest cell(s) in a given direction.
|
||||||
"""
|
"""
|
||||||
|
|
@ -86,6 +87,7 @@ def get_border( curset, direct):
|
||||||
newset = set(maxc)
|
newset = set(maxc)
|
||||||
return newset
|
return newset
|
||||||
|
|
||||||
|
|
||||||
def expand_border(curset, direct):
|
def expand_border(curset, direct):
|
||||||
"""
|
"""
|
||||||
Expand the current set of sells in a given direction.
|
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)
|
next_border_set = increment_set(border_set, direct)
|
||||||
return next_border_set
|
return next_border_set
|
||||||
|
|
||||||
|
|
||||||
def expand_borders(curset):
|
def expand_borders(curset):
|
||||||
"""
|
"""
|
||||||
Return the expansions in planar directions.
|
Return the expansions in planar directions.
|
||||||
|
|
@ -106,6 +109,7 @@ def expand_borders(curset):
|
||||||
|
|
||||||
return(north_set, east_set, south_set, west_set)
|
return(north_set, east_set, south_set, west_set)
|
||||||
|
|
||||||
|
|
||||||
def inflate_cell(cell, distance):
|
def inflate_cell(cell, distance):
|
||||||
"""
|
"""
|
||||||
Expand the current cell in all directions and return the set.
|
Expand the current cell in all directions and return the set.
|
||||||
|
|
@ -122,6 +126,7 @@ def inflate_cell(cell, distance):
|
||||||
|
|
||||||
return newset
|
return newset
|
||||||
|
|
||||||
|
|
||||||
def inflate_set(curset, distance):
|
def inflate_set(curset, distance):
|
||||||
"""
|
"""
|
||||||
Expand the set in all directions by the given number of grids.
|
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
|
# Recurse with less depth
|
||||||
return inflate_set(newset,distance-1)
|
return inflate_set(newset,distance-1)
|
||||||
|
|
||||||
|
|
||||||
def flatten_set(curset):
|
def flatten_set(curset):
|
||||||
"""
|
"""
|
||||||
Flatten until we have a set of vector3d objects.
|
Flatten until we have a set of vector3d objects.
|
||||||
|
|
@ -149,7 +155,6 @@ def flatten_set(curset):
|
||||||
return newset
|
return newset
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def distance_set(coord, curset):
|
def distance_set(coord, curset):
|
||||||
"""
|
"""
|
||||||
Return the distance from a coordinate to any item in the set
|
Return the distance from a coordinate to any item in the set
|
||||||
|
|
|
||||||
|
|
@ -493,7 +493,6 @@ class pin_group:
|
||||||
debug.error("Could not find a connector for {} with {}".format(self.pins,
|
debug.error("Could not find a connector for {} with {}".format(self.pins,
|
||||||
self.enclosures))
|
self.enclosures))
|
||||||
self.router.write_debug_gds("no_connector.gds")
|
self.router.write_debug_gds("no_connector.gds")
|
||||||
import pdb; pdb.set_trace()
|
|
||||||
self.enclosures.append(connector)
|
self.enclosures.append(connector)
|
||||||
|
|
||||||
# At this point, the pins are overlapping,
|
# At this point, the pins are overlapping,
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,14 @@ class router(router_tech):
|
||||||
route on a given layer. This is limited to two layer routes.
|
route on a given layer. This is limited to two layer routes.
|
||||||
It populates blockages on a grid class.
|
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
|
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
|
route on top of this. The blockages from the gds/module will be
|
||||||
considered.
|
considered.
|
||||||
"""
|
"""
|
||||||
router_tech.__init__(self, layers, rail_track_width)
|
|
||||||
|
router_tech.__init__(self, layers, route_track_width)
|
||||||
|
|
||||||
self.cell = design
|
self.cell = design
|
||||||
|
|
||||||
|
|
@ -285,7 +286,7 @@ class router(router_tech):
|
||||||
If so, reduce the pin group grid to not include the adjacent grid.
|
If so, reduce the pin group grid to not include the adjacent grid.
|
||||||
Try to do this intelligently to keep th pins enclosed.
|
Try to do this intelligently to keep th pins enclosed.
|
||||||
"""
|
"""
|
||||||
debug.info(1,
|
debug.info(2,
|
||||||
"Comparing {0} and {1} adjacency".format(pin_name1,
|
"Comparing {0} and {1} adjacency".format(pin_name1,
|
||||||
pin_name2))
|
pin_name2))
|
||||||
removed_grids = 0
|
removed_grids = 0
|
||||||
|
|
@ -301,7 +302,7 @@ class router(router_tech):
|
||||||
adj_grids))
|
adj_grids))
|
||||||
self.remove_adjacent_grid(pg1, pg2, 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):
|
def remove_adjacent_grid(self, pg1, pg2, adj_grids):
|
||||||
"""
|
"""
|
||||||
|
|
@ -347,6 +348,10 @@ class router(router_tech):
|
||||||
smaller))
|
smaller))
|
||||||
smaller.grids.remove(adj)
|
smaller.grids.remove(adj)
|
||||||
|
|
||||||
|
def set_supply_rail_blocked(self, value):
|
||||||
|
# This is just a virtual function
|
||||||
|
pass
|
||||||
|
|
||||||
def prepare_blockages(self, pin_name):
|
def prepare_blockages(self, pin_name):
|
||||||
"""
|
"""
|
||||||
Reset and add all of the blockages in the design.
|
Reset and add all of the blockages in the design.
|
||||||
|
|
@ -362,7 +367,11 @@ class router(router_tech):
|
||||||
|
|
||||||
# Block all of the supply rails
|
# Block all of the supply rails
|
||||||
# (some will be unblocked if they're a target)
|
# (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
|
# Block all of the pin components
|
||||||
# (some will be unblocked if they're a source/target)
|
# (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.
|
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]:
|
for pg in self.pin_groups[pin_name]:
|
||||||
pg.convert_pin()
|
pg.convert_pin()
|
||||||
|
|
||||||
|
|
@ -808,7 +817,7 @@ class router(router_tech):
|
||||||
that are blocked by other shapes.
|
that are blocked by other shapes.
|
||||||
"""
|
"""
|
||||||
for pin_name in self.pin_groups:
|
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]:
|
for pg in self.pin_groups[pin_name]:
|
||||||
pg.enclose_pin()
|
pg.enclose_pin()
|
||||||
pg.add_enclosure(self.cell)
|
pg.add_enclosure(self.cell)
|
||||||
|
|
@ -829,6 +838,12 @@ class router(router_tech):
|
||||||
for i in range(self.num_pin_components(pin_name)):
|
for i in range(self.num_pin_components(pin_name)):
|
||||||
self.add_pin_component_target(pin_name, i)
|
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):
|
def num_pin_components(self, pin_name):
|
||||||
"""
|
"""
|
||||||
This returns how many disconnected pin components there are.
|
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.")
|
debug.check(index<self.num_pin_components(pin_name),"Pin component index too large.")
|
||||||
|
|
||||||
pin_in_tracks = self.pin_groups[pin_name][index].grids
|
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)
|
self.rg.add_target(pin_in_tracks)
|
||||||
|
|
||||||
def add_pin_component_target_except(self, pin_name, index):
|
def add_pin_component_target_except(self, pin_name, index):
|
||||||
|
|
@ -901,7 +916,7 @@ class router(router_tech):
|
||||||
# self.write_debug_gds()
|
# self.write_debug_gds()
|
||||||
|
|
||||||
# First, simplify the path for
|
# First, simplify the path for
|
||||||
# debug.info(1, str(self.path))
|
# debug.info(3, str(self.path))
|
||||||
contracted_path = self.contract_path(path)
|
contracted_path = self.contract_path(path)
|
||||||
debug.info(3, "Contracted path: " + str(contracted_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!
|
# Double check source and taget are not same node, if so, we are done!
|
||||||
for k, v in self.rg.map.items():
|
for k, v in self.rg.map.items():
|
||||||
if v.source and v.target:
|
if v.source and v.target:
|
||||||
debug.error("Grid cell is source and target! {}".format(k))
|
return True
|
||||||
return False
|
|
||||||
|
|
||||||
# returns the path in tracks
|
# returns the path in tracks
|
||||||
(path, cost) = self.rg.route(detour_scale)
|
(path, cost) = self.rg.route(detour_scale)
|
||||||
if path:
|
if path:
|
||||||
debug.info(2, "Found path: cost={0} {1}".format(cost, str(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)
|
self.add_route(path)
|
||||||
|
|
||||||
path_set = grid_utils.flatten_set(path)
|
self.path_blockages.append(self.paths[-1])
|
||||||
self.path_blockages.append(path_set)
|
return True
|
||||||
else:
|
else:
|
||||||
self.write_debug_gds("failed_route.gds")
|
|
||||||
# clean up so we can try a reroute
|
|
||||||
self.rg.reinit()
|
|
||||||
return False
|
return False
|
||||||
return True
|
|
||||||
|
|
||||||
def annotate_pin_and_tracks(self, pin, tracks):
|
def annotate_pin_and_tracks(self, pin, tracks):
|
||||||
""""
|
""""
|
||||||
|
|
@ -1122,7 +1132,7 @@ class router(router_tech):
|
||||||
show_all_grids = True
|
show_all_grids = True
|
||||||
|
|
||||||
if show_all_grids:
|
if show_all_grids:
|
||||||
self.rg.add_all_grids()
|
# self.rg.add_all_grids()
|
||||||
for g in self.rg.map:
|
for g in self.rg.map:
|
||||||
self.annotate_grid(g)
|
self.annotate_grid(g)
|
||||||
|
|
||||||
|
|
@ -1156,7 +1166,36 @@ class router(router_tech):
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=pin.height())
|
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
|
# FIXME: This should be replaced with vector.snap_to_grid at some point
|
||||||
def snap_to_grid(offset):
|
def snap_to_grid(offset):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -16,20 +16,20 @@ class router_tech:
|
||||||
"""
|
"""
|
||||||
This is a class to hold the router tech constants.
|
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.
|
Allows us to change the layers that we are routing on.
|
||||||
This uses the preferreed directions.
|
This uses the preferreed directions.
|
||||||
"""
|
"""
|
||||||
self.layers = layers
|
self.layers = layers
|
||||||
self.rail_track_width = rail_track_width
|
self.route_track_width = route_track_width
|
||||||
|
|
||||||
if len(self.layers) == 1:
|
if len(self.layers) == 1:
|
||||||
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
|
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
|
||||||
self.horiz_lpp = self.vert_lpp = layer[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.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
|
||||||
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
|
(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.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
|
||||||
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
|
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
|
||||||
|
|
@ -55,13 +55,13 @@ class router_tech:
|
||||||
"preferred_directions '{}' and '{}'.")
|
"preferred_directions '{}' and '{}'.")
|
||||||
|
|
||||||
via_connect = contact(self.layers, (1, 1))
|
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.horiz_lpp = layer[self.horiz_layer_name]
|
||||||
self.vert_lpp = layer[self.vert_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.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
|
||||||
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
|
(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
|
# For supplies, we will make the wire wider than the vias
|
||||||
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
|
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
|
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
|
||||||
|
|
||||||
# We'll keep horizontal and vertical tracks the same for simplicity.
|
# We'll keep horizontal and vertical tracks the same for simplicity.
|
||||||
self.track_width = max(self.horiz_track_width,self.vert_track_width)
|
self.track_width = max(self.horiz_track_width, self.vert_track_width)
|
||||||
debug.info(1,"Track width: {:.3f}".format(self.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)
|
self.track_space = max(self.horiz_layer_spacing, self.vert_layer_spacing)
|
||||||
debug.info(1,"Track space: {:.3f}".format(self.track_space))
|
debug.info(1, "Minimum track space: {:.3f}".format(self.track_space))
|
||||||
self.track_wire = self.track_width - 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_widths = vector([self.track_width] * 2)
|
||||||
self.track_factor = vector([1/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)
|
# 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]
|
self.layer_widths = [self.track_wire, 1, self.track_wire]
|
||||||
|
|
@ -107,9 +107,9 @@ class router_tech:
|
||||||
elif zindex==0:
|
elif zindex==0:
|
||||||
return self.horiz_layer_name
|
return self.horiz_layer_name
|
||||||
else:
|
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
|
These are the width and spacing of a supply layer given a supply rail
|
||||||
of the given number of min wire widths.
|
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_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_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.rail_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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from itertools import tee
|
|
||||||
import debug
|
import debug
|
||||||
from heapq import heappush,heappop
|
from heapq import heappush,heappop
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
@ -14,6 +13,7 @@ from grid import grid
|
||||||
from grid_path import grid_path
|
from grid_path import grid_path
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
|
|
||||||
|
|
||||||
class signal_grid(grid):
|
class signal_grid(grid):
|
||||||
"""
|
"""
|
||||||
Expand the two layer grid to include A* search functions for a source and target.
|
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()
|
p.reset()
|
||||||
|
|
||||||
# clear source and target pins
|
# clear source and target pins
|
||||||
self.source=[]
|
self.source = set()
|
||||||
self.target=[]
|
self.target = set()
|
||||||
|
|
||||||
# Clear the queue
|
# Clear the queue
|
||||||
while len(self.q)>0:
|
while len(self.q) > 0:
|
||||||
heappop(self.q)
|
heappop(self.q)
|
||||||
self.counter = 0
|
self.counter = 0
|
||||||
|
|
||||||
|
|
@ -49,19 +49,16 @@ class signal_grid(grid):
|
||||||
We will use an A* search, so this cost must be pessimistic.
|
We will use an A* search, so this cost must be pessimistic.
|
||||||
Cost so far will be the length of the path.
|
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
|
# Counter is used to not require data comparison in Python 3.x
|
||||||
# Items will be returned in order they are added during cost ties
|
# Items will be returned in order they are added during cost ties
|
||||||
self.counter = 0
|
self.counter = 0
|
||||||
for s in self.source:
|
for s in self.source:
|
||||||
cost = self.cost_to_target(s)
|
cost = self.cost_to_target(s)
|
||||||
debug.info(3,"Init: cost=" + str(cost) + " " + str([s]))
|
debug.info(3, "Init: cost=" + str(cost) + " " + str([s]))
|
||||||
heappush(self.q,(cost,self.counter,grid_path([vector3d(s)])))
|
heappush(self.q, (cost, self.counter, grid_path([vector3d(s)])))
|
||||||
self.counter+=1
|
self.counter+=1
|
||||||
|
|
||||||
|
def route(self, detour_scale):
|
||||||
def route(self,detour_scale):
|
|
||||||
"""
|
"""
|
||||||
This does the A* maze routing with preferred direction routing.
|
This does the A* maze routing with preferred direction routing.
|
||||||
This only works for 1 track wide routes!
|
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
|
# 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.
|
# over-ridden if the route fails due to pruning a feasible solution.
|
||||||
any_source_element = next(iter(self.source))
|
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!
|
# Check if something in the queue is already a source and a target!
|
||||||
for s in self.source:
|
for s in self.source:
|
||||||
|
|
@ -83,20 +80,17 @@ class signal_grid(grid):
|
||||||
|
|
||||||
# Put the source items into the queue
|
# Put the source items into the queue
|
||||||
self.init_queue()
|
self.init_queue()
|
||||||
cheapest_path = None
|
|
||||||
cheapest_cost = None
|
|
||||||
|
|
||||||
|
|
||||||
# Keep expanding and adding to the priority queue until we are done
|
# Keep expanding and adding to the priority queue until we are done
|
||||||
while len(self.q)>0:
|
while len(self.q)>0:
|
||||||
# should we keep the path in the queue as well or just the final node?
|
# should we keep the path in the queue as well or just the final node?
|
||||||
(cost,count,curpath) = heappop(self.q)
|
(cost, count, curpath) = heappop(self.q)
|
||||||
debug.info(3,"Queue size: size=" + str(len(self.q)) + " " + str(cost))
|
debug.info(3, "Queue size: size=" + str(len(self.q)) + " " + str(cost))
|
||||||
debug.info(4,"Expanding: cost=" + str(cost) + " " + str(curpath))
|
debug.info(4, "Expanding: cost=" + str(cost) + " " + str(curpath))
|
||||||
|
|
||||||
# expand the last element
|
# expand the last element
|
||||||
neighbors = self.expand_dirs(curpath)
|
neighbors = self.expand_dirs(curpath)
|
||||||
debug.info(4,"Neighbors: " + str(neighbors))
|
debug.info(4, "Neighbors: " + str(neighbors))
|
||||||
|
|
||||||
for n in neighbors:
|
for n in neighbors:
|
||||||
# make a new copy of the path to not update the old ones
|
# make a new copy of the path to not update the old ones
|
||||||
|
|
@ -105,7 +99,7 @@ class signal_grid(grid):
|
||||||
newpath.append(n)
|
newpath.append(n)
|
||||||
# check if we hit the target and are done
|
# 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
|
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:
|
else:
|
||||||
# current path cost + predicted cost
|
# current path cost + predicted cost
|
||||||
current_cost = newpath.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):
|
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_path = newpath
|
||||||
self.map[n[0]].min_cost = predicted_cost
|
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
|
# 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
|
self.counter += 1
|
||||||
#else:
|
#else:
|
||||||
# print("Better previous cost.")
|
# print("Better previous cost.")
|
||||||
#else:
|
#else:
|
||||||
# print("Cost bounded")
|
# 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
|
Expand each of the four cardinal directions plus up or down
|
||||||
but not expanding to blocked cells. Expands in all directions
|
but not expanding to blocked cells. Expands in all directions
|
||||||
|
|
@ -143,33 +136,31 @@ class signal_grid(grid):
|
||||||
|
|
||||||
return unblocked_neighbors
|
return unblocked_neighbors
|
||||||
|
|
||||||
|
|
||||||
def hpwl(self, src, dest):
|
def hpwl(self, src, dest):
|
||||||
"""
|
"""
|
||||||
Return half perimeter wire length from point to another.
|
Return half perimeter wire length from point to another.
|
||||||
Either point can have positive or negative coordinates.
|
Either point can have positive or negative coordinates.
|
||||||
Include the via penalty if there is one.
|
Include the via penalty if there is one.
|
||||||
"""
|
"""
|
||||||
hpwl = abs(src.x-dest.x)
|
hpwl = abs(src.x - dest.x)
|
||||||
hpwl += abs(src.y-dest.y)
|
hpwl += abs(src.y - dest.y)
|
||||||
if src.x!=dest.x and src.y!=dest.y:
|
if src.x!=dest.x and src.y!=dest.y:
|
||||||
hpwl += grid.VIA_COST
|
hpwl += grid.VIA_COST
|
||||||
return hpwl
|
return hpwl
|
||||||
|
|
||||||
def cost_to_target(self,source):
|
def cost_to_target(self, source):
|
||||||
"""
|
"""
|
||||||
Find the cheapest HPWL distance to any target point ignoring
|
Find the cheapest HPWL distance to any target point ignoring
|
||||||
blockages for A* search.
|
blockages for A* search.
|
||||||
"""
|
"""
|
||||||
any_target_element = next(iter(self.target))
|
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:
|
for t in self.target:
|
||||||
cost = min(self.hpwl(source,t),cost)
|
cost = min(self.hpwl(source, t), cost)
|
||||||
|
|
||||||
return 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.
|
Sets the direction based on the previous direction we came from.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,10 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import gdsMill
|
|
||||||
import tech
|
|
||||||
from contact import contact
|
|
||||||
import math
|
|
||||||
import debug
|
import debug
|
||||||
from pin_layout import pin_layout
|
|
||||||
from globals import OPTS
|
|
||||||
from router import router
|
from router import router
|
||||||
|
|
||||||
|
|
||||||
class signal_router(router):
|
class signal_router(router):
|
||||||
"""
|
"""
|
||||||
A router class to read an obstruction map from a gds and plan a
|
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)
|
router.__init__(self, layers, design, gds_filename)
|
||||||
|
|
||||||
|
|
||||||
def create_routing_grid(self):
|
def create_routing_grid(self):
|
||||||
"""
|
"""
|
||||||
Create a sprase routing grid with A* expansion functions.
|
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
|
# We will add a halo around the boundary
|
||||||
# of this many tracks
|
# of this many tracks
|
||||||
size = self.ur - self.ll
|
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
|
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):
|
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.
|
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.
|
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[src] = []
|
||||||
self.pins[dest] = []
|
self.pins[dest] = []
|
||||||
|
|
||||||
# Clear the pins if we have previously routed
|
# Clear the pins if we have previously routed
|
||||||
if (hasattr(self,'rg')):
|
if (hasattr(self, 'rg')):
|
||||||
self.clear_pins()
|
self.clear_pins()
|
||||||
else:
|
else:
|
||||||
# Creat a routing grid over the entire area
|
# Creat a routing grid over the entire area
|
||||||
|
|
@ -67,8 +60,8 @@ class signal_router(router):
|
||||||
# Block everything
|
# Block everything
|
||||||
self.prepare_blockages()
|
self.prepare_blockages()
|
||||||
# Clear the pins we are routing
|
# Clear the pins we are routing
|
||||||
self.set_blockages(self.pin_components[src],False)
|
self.set_blockages(self.pin_components[src], False)
|
||||||
self.set_blockages(self.pin_components[dest],False)
|
self.set_blockages(self.pin_components[dest], False)
|
||||||
|
|
||||||
# Now add the src/tgt if they are not blocked by other shapes
|
# Now add the src/tgt if they are not blocked by other shapes
|
||||||
self.add_source(src)
|
self.add_source(src)
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,8 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
|
||||||
from vector3d import vector3d
|
|
||||||
from grid import grid
|
|
||||||
from signal_grid import signal_grid
|
from signal_grid import signal_grid
|
||||||
from grid_path import grid_path
|
from grid_path import grid_path
|
||||||
from direction import direction
|
|
||||||
|
|
||||||
|
|
||||||
class supply_grid(signal_grid):
|
class supply_grid(signal_grid):
|
||||||
|
|
@ -27,13 +23,14 @@ class supply_grid(signal_grid):
|
||||||
|
|
||||||
def reinit(self):
|
def reinit(self):
|
||||||
""" Reinitialize everything for a new route. """
|
""" Reinitialize everything for a new route. """
|
||||||
|
|
||||||
self.source = set()
|
self.source = set()
|
||||||
self.target = set()
|
self.target = set()
|
||||||
|
|
||||||
# Reset all the cells in the map
|
# Reset all the cells in the map
|
||||||
for p in self.map.values():
|
for p in self.map.values():
|
||||||
p.reset()
|
p.reset()
|
||||||
|
|
||||||
|
|
||||||
def find_start_wave(self, wave, direct):
|
def find_start_wave(self, wave, direct):
|
||||||
"""
|
"""
|
||||||
Finds the first loc starting at loc and up that is open.
|
Finds the first loc starting at loc and up that is open.
|
||||||
|
|
@ -46,8 +43,8 @@ class supply_grid(signal_grid):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
while wave and self.is_wave_blocked(wave):
|
while wave and self.is_wave_blocked(wave):
|
||||||
wf=grid_path(wave)
|
wf = grid_path(wave)
|
||||||
wave=wf.neighbor(direct)
|
wave = wf.neighbor(direct)
|
||||||
# Bail out if we couldn't increment futher
|
# Bail out if we couldn't increment futher
|
||||||
if wave[0].x > self.ur.x or wave[-1].y > self.ur.y:
|
if wave[0].x > self.ur.x or wave[-1].y > self.ur.y:
|
||||||
return None
|
return None
|
||||||
|
|
@ -57,7 +54,6 @@ class supply_grid(signal_grid):
|
||||||
|
|
||||||
return wave
|
return wave
|
||||||
|
|
||||||
|
|
||||||
def is_wave_blocked(self, wave):
|
def is_wave_blocked(self, wave):
|
||||||
"""
|
"""
|
||||||
Checks if any of the locations are blocked
|
Checks if any of the locations are blocked
|
||||||
|
|
@ -68,7 +64,6 @@ class supply_grid(signal_grid):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def probe(self, wave, direct):
|
def probe(self, wave, direct):
|
||||||
"""
|
"""
|
||||||
Expand the wave until there is a blockage and return
|
Expand the wave until there is a blockage and return
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@ class supply_grid_router(router):
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
|
|
||||||
# Power rail width in minimum wire widths
|
# 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
|
# The list of supply rails (grid sets) that may be routed
|
||||||
self.supply_rails = {}
|
self.supply_rails = {}
|
||||||
|
|
@ -47,7 +47,7 @@ class supply_grid_router(router):
|
||||||
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 supply_grid
|
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"):
|
def route(self, vdd_name="vdd", gnd_name="gnd"):
|
||||||
"""
|
"""
|
||||||
|
|
@ -74,6 +74,7 @@ class supply_grid_router(router):
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
# Block everything
|
# Block everything
|
||||||
self.prepare_blockages(self.gnd_name)
|
self.prepare_blockages(self.gnd_name)
|
||||||
|
|
||||||
# Determine the rail locations
|
# Determine the rail locations
|
||||||
self.route_supply_rails(self.gnd_name, 0)
|
self.route_supply_rails(self.gnd_name, 0)
|
||||||
|
|
||||||
|
|
@ -104,14 +105,6 @@ class supply_grid_router(router):
|
||||||
|
|
||||||
return True
|
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):
|
def route_simple_overlaps(self, pin_name):
|
||||||
"""
|
"""
|
||||||
This checks for simple cases where a pin component already overlaps a supply rail.
|
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.
|
data structure. Return whether it was added or not.
|
||||||
"""
|
"""
|
||||||
# We must have at least 2 tracks to drop plus 2 tracks for a via
|
# 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()
|
grid_set = wave_path.get_grids()
|
||||||
self.supply_rails[name].append(grid_set)
|
self.supply_rails[name].append(grid_set)
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,19 @@
|
||||||
# See LICENSE for licensing information.
|
# See LICENSE for licensing information.
|
||||||
#
|
#
|
||||||
#Copyright (c) 2016-2019 Regents of the University of California and The Board
|
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
||||||
#of Regents for the Oklahoma Agricultural and Mechanical College
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||||
#(acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
#All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import gdsMill
|
|
||||||
import tech
|
|
||||||
import math
|
|
||||||
import debug
|
import debug
|
||||||
from globals import OPTS,print_time
|
from globals import print_time
|
||||||
from contact import contact
|
|
||||||
from pin_group import pin_group
|
|
||||||
from pin_layout import pin_layout
|
|
||||||
from vector3d import vector3d
|
|
||||||
from router import router
|
from router import router
|
||||||
from direction import direction
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import grid
|
|
||||||
import grid_utils
|
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):
|
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).
|
either the gds file name or the design itself (by saving to a gds file).
|
||||||
"""
|
"""
|
||||||
# Power rail width in minimum wire widths
|
# 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)
|
||||||
|
|
||||||
def create_routing_grid(self):
|
def create_routing_grid(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -43,9 +37,7 @@ class supply_tree_router(router):
|
||||||
"""
|
"""
|
||||||
size = self.ur - self.ll
|
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))
|
||||||
|
self.rg = signal_grid(self.ll, self.ur, self.route_track_width)
|
||||||
import supply_grid
|
|
||||||
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
|
|
||||||
|
|
||||||
def route(self, vdd_name="vdd", gnd_name="gnd"):
|
def route(self, vdd_name="vdd", gnd_name="gnd"):
|
||||||
"""
|
"""
|
||||||
|
|
@ -82,7 +74,7 @@ class supply_tree_router(router):
|
||||||
self.route_pins(gnd_name)
|
self.route_pins(gnd_name)
|
||||||
print_time("Maze routing supplies",datetime.now(), start_time, 3)
|
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??
|
# Did we route everything??
|
||||||
if not self.check_all_routed(vdd_name):
|
if not self.check_all_routed(vdd_name):
|
||||||
|
|
@ -92,48 +84,6 @@ class supply_tree_router(router):
|
||||||
|
|
||||||
return True
|
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):
|
def route_pins(self, pin_name):
|
||||||
"""
|
"""
|
||||||
This will route each of the remaining pin components to the other pins.
|
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])
|
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,
|
debug.info(1,"Routing {0} with {1} pin components to connect.".format(pin_name,
|
||||||
remaining_components))
|
remaining_components))
|
||||||
|
|
||||||
for index,pg in enumerate(self.pin_groups[pin_name]):
|
# Create full graph
|
||||||
if pg.is_routed():
|
debug.info(2,"Creating adjacency matrix")
|
||||||
continue
|
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.
|
# Clear everything in the routing grid.
|
||||||
self.rg.reinit()
|
self.rg.reinit()
|
||||||
|
|
||||||
|
|
@ -159,35 +140,27 @@ class supply_tree_router(router):
|
||||||
|
|
||||||
# Add the single component of the pin as the source
|
# Add the single component of the pin as the source
|
||||||
# which unmarks it as a blockage too
|
# 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
|
# Marks all pin components except index as target
|
||||||
self.add_pin_component_target_except(pin_name,index)
|
self.add_pin_component_target(pin_name, dest_idx)
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Actually run the A* router
|
# Actually run the A* router
|
||||||
if not self.run_router(detour_scale=5):
|
if self.run_router(detour_scale=detour_scale):
|
||||||
self.write_debug_gds("debug_route.gds",True)
|
return
|
||||||
|
|
||||||
#if index==3 and pin_name=="vdd":
|
self.write_debug_gds("debug_route.gds", True)
|
||||||
# self.write_debug_gds("route.gds",False)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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()
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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()
|
|
||||||
Binary file not shown.
|
|
@ -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()
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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()
|
|
||||||
|
|
@ -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]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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]
|
|
||||||
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -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())
|
|
||||||
|
|
@ -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("|==============================================================================|")
|
|
||||||
|
|
@ -87,26 +87,6 @@ class sram():
|
||||||
def save(self):
|
def save(self):
|
||||||
""" Save all the output files while reporting time to do it as well. """
|
""" 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
|
# Save the spice file
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
spname = OPTS.output_path + self.s.name + ".sp"
|
spname = OPTS.output_path + self.s.name + ".sp"
|
||||||
|
|
@ -118,12 +98,33 @@ class sram():
|
||||||
output_path=OPTS.output_path)
|
output_path=OPTS.output_path)
|
||||||
print_time("Spice writing", datetime.datetime.now(), start_time)
|
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
|
# Save the LVS file
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
|
lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
|
||||||
debug.print_raw("LVS: Writing to {0}".format(lvsname))
|
debug.print_raw("LVS: Writing to {0}".format(lvsname))
|
||||||
self.lvs_write(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,
|
verify.write_lvs_script(cell_name=self.s.name,
|
||||||
gds_name=os.path.basename(gdsname),
|
gds_name=os.path.basename(gdsname),
|
||||||
sp_name=os.path.basename(lvsname),
|
sp_name=os.path.basename(lvsname),
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_base import sram_base
|
from sram_base import sram_base
|
||||||
from contact import m2_via
|
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
|
from globals import OPTS
|
||||||
import channel_route
|
|
||||||
|
|
||||||
|
|
||||||
class sram_1bank(sram_base):
|
class sram_1bank(sram_base):
|
||||||
|
|
@ -102,12 +102,17 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# Place with an initial wide channel (from above)
|
# Place with an initial wide channel (from above)
|
||||||
self.place_dffs()
|
self.place_dffs()
|
||||||
|
|
||||||
# Route the channel and set to the new data bus size
|
# 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.route_dffs(add_routes=False)
|
||||||
|
self.remove_layout_pins()
|
||||||
|
|
||||||
# Re-place with the new channel size
|
# Re-place with the new channel size
|
||||||
self.place_dffs()
|
self.place_dffs()
|
||||||
# Now route the channel
|
|
||||||
self.route_dffs()
|
|
||||||
|
|
||||||
def place_row_addr_dffs(self):
|
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.data_pos[port] = vector(x_offset, y_offset)
|
||||||
self.spare_wen_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.
|
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:
|
for port in self.all_ports:
|
||||||
# Depending on the port, use the bottom/top or left/right sides
|
# Depending on the port, use the bottom/top or left/right sides
|
||||||
# Port 0 is left/bottom
|
# Port 0 is left/bottom
|
||||||
|
|
@ -258,132 +262,127 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# Connect the control pins as inputs
|
# Connect the control pins as inputs
|
||||||
for signal in self.control_logic_inputs[port]:
|
for signal in self.control_logic_inputs[port]:
|
||||||
if signal == "clk":
|
|
||||||
continue
|
|
||||||
if signal.startswith("rbl"):
|
if signal.startswith("rbl"):
|
||||||
continue
|
continue
|
||||||
if OPTS.perimeter_pins:
|
if signal=="clk":
|
||||||
self.add_perimeter_pin(name=signal + "{}".format(port),
|
pins_to_route.append(("{0}{1}".format(signal, port), bottom_or_top))
|
||||||
pin=self.control_logic_insts[port].get_pin(signal),
|
|
||||||
side=left_or_right,
|
|
||||||
bbox=bbox)
|
|
||||||
else:
|
else:
|
||||||
self.copy_layout_pin(self.control_logic_insts[port],
|
pins_to_route.append(("{0}{1}".format(signal, port), left_or_right))
|
||||||
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 = []
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
for bit in range(self.word_size + self.num_spare_cols):
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("din{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
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))
|
|
||||||
|
|
||||||
# Data output pins go to BOTTOM/TOP
|
if port in self.readwrite_ports or port in self.read_ports:
|
||||||
if port in self.readwrite_ports and OPTS.perimeter_pins:
|
|
||||||
for bit in range(self.word_size + self.num_spare_cols):
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
# This should be routed next to the din pin
|
pins_to_route.append(("dout{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Lower address bits go to BOTTOM/TOP
|
|
||||||
for bit in range(self.col_addr_size):
|
for bit in range(self.col_addr_size):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("addr{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
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))
|
|
||||||
|
|
||||||
# Upper address bits go to LEFT/RIGHT
|
|
||||||
for bit in range(self.row_addr_size):
|
for bit in range(self.row_addr_size):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("addr{0}[{1}]".format(port, bit + self.col_addr_size), left_or_right))
|
||||||
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))
|
|
||||||
|
|
||||||
# Write mask pins go to BOTTOM/TOP
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("wmask{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
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))
|
|
||||||
|
|
||||||
# Spare wen pins go to BOTTOM/TOP
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
for bit in range(self.num_spare_cols):
|
for bit in range(self.num_spare_cols):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("spare_wen{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
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))
|
|
||||||
|
|
||||||
|
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):
|
def route_layout(self):
|
||||||
""" Route a single bank SRAM """
|
""" Route a single bank SRAM """
|
||||||
|
|
||||||
self.add_layout_pins()
|
|
||||||
|
|
||||||
self.route_clk()
|
self.route_clk()
|
||||||
|
|
||||||
self.route_control_logic()
|
self.route_control_logic()
|
||||||
|
|
||||||
self.route_row_addr_dff()
|
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):
|
def route_dffs(self, add_routes=True):
|
||||||
|
|
||||||
for port in self.all_ports:
|
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]
|
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||||
route_map.extend(list(zip(bank_pins, dff_pins)))
|
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
|
# wmask dff
|
||||||
if self.num_wmasks > 0 and port in self.write_ports:
|
if self.num_wmasks > 0 and port in self.write_ports:
|
||||||
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
|
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]
|
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||||
route_map.extend(list(zip(bank_pins, dff_pins)))
|
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
|
# spare wen dff
|
||||||
if self.num_spare_cols > 0 and port in self.write_ports:
|
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)]
|
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:
|
if port == 0:
|
||||||
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
|
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
|
||||||
- self.data_bus_size[port] + 2 * self.m1_pitch)
|
- self.data_bus_size[port] + 2 * self.m3_pitch)
|
||||||
cr = channel_route.channel_route(netlist=route_map,
|
cr = channel_route(netlist=route_map,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layer_stack=layer_stack,
|
layer_stack=layer_stack,
|
||||||
parent=self)
|
parent=self)
|
||||||
if add_routes:
|
if add_routes:
|
||||||
self.add_inst(cr.name, cr)
|
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
|
||||||
self.connect_inst([])
|
# with no active devices.
|
||||||
|
# self.add_inst(cr.name, cr)
|
||||||
|
# self.connect_inst([])
|
||||||
|
self.add_flat_inst(cr.name, cr)
|
||||||
else:
|
else:
|
||||||
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
|
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
|
||||||
else:
|
else:
|
||||||
offset = vector(0,
|
offset = vector(0,
|
||||||
self.bank.height + 2 * self.m1_space)
|
self.bank.height + self.m3_pitch)
|
||||||
cr = channel_route.channel_route(netlist=route_map,
|
cr = channel_route(netlist=route_map,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layer_stack=layer_stack,
|
layer_stack=layer_stack,
|
||||||
parent=self)
|
parent=self)
|
||||||
if add_routes:
|
if add_routes:
|
||||||
self.add_inst(cr.name, cr)
|
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
|
||||||
self.connect_inst([])
|
# with no active devices.
|
||||||
|
# self.add_inst(cr.name, cr)
|
||||||
|
# self.connect_inst([])
|
||||||
|
self.add_flat_inst(cr.name, cr)
|
||||||
else:
|
else:
|
||||||
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
|
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -190,13 +190,13 @@ class sram_base(design, verilog, lef):
|
||||||
|
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
self.route_layout()
|
self.route_layout()
|
||||||
self.route_supplies()
|
|
||||||
if not OPTS.is_unit_test:
|
if not OPTS.is_unit_test:
|
||||||
print_time("Routing", datetime.datetime.now(), start_time)
|
print_time("Routing", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
self.add_lvs_correspondence_points()
|
self.add_lvs_correspondence_points()
|
||||||
|
|
||||||
self.offset_all_coordinates()
|
#self.offset_all_coordinates()
|
||||||
|
|
||||||
highest_coord = self.find_highest_coords()
|
highest_coord = self.find_highest_coords()
|
||||||
self.width = highest_coord[0]
|
self.width = highest_coord[0]
|
||||||
|
|
@ -207,9 +207,10 @@ class sram_base(design, verilog, lef):
|
||||||
ur=vector(self.width, self.height))
|
ur=vector(self.width, self.height))
|
||||||
|
|
||||||
start_time = datetime.datetime.now()
|
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:
|
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)
|
print_time("Verification", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
|
|
@ -220,31 +221,49 @@ class sram_base(design, verilog, lef):
|
||||||
|
|
||||||
# Copy the pins to the top level
|
# Copy the pins to the top level
|
||||||
# This will either be used to route or left unconnected.
|
# This will either be used to route or left unconnected.
|
||||||
for inst in self.insts:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
self.copy_power_pins(inst, "vdd")
|
for inst in self.insts:
|
||||||
self.copy_power_pins(inst, "gnd")
|
self.copy_power_pins(inst, pin_name)
|
||||||
|
|
||||||
if not OPTS.route_supplies:
|
if not OPTS.route_supplies:
|
||||||
# Do not route the power supply (leave as must-connect pins)
|
# Do not route the power supply (leave as must-connect pins)
|
||||||
return
|
return
|
||||||
|
|
||||||
grid_stack = set()
|
|
||||||
try:
|
try:
|
||||||
from tech import power_grid
|
from tech import power_grid
|
||||||
grid_stack = power_grid
|
grid_stack = power_grid
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# if no power_grid is specified by tech we use sensible defaults
|
# if no power_grid is specified by tech we use sensible defaults
|
||||||
import tech
|
# Route a M3/M4 grid
|
||||||
if "m4" in tech.layer:
|
grid_stack = self.m3_stack
|
||||||
# Route a M3/M4 grid
|
|
||||||
grid_stack = self.m3_stack
|
|
||||||
elif "m3" in tech.layer:
|
|
||||||
grid_stack =("m3",)
|
|
||||||
|
|
||||||
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=router(grid_stack, self)
|
||||||
rtr.route()
|
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):
|
def compute_bus_sizes(self):
|
||||||
""" Compute the independent bus widths shared between two and four bank SRAMs """
|
""" Compute the independent bus widths shared between two and four bank SRAMs """
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ class bitcell_array_1rw_1r_test(openram_test):
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
globals.setup_bitcell()
|
globals.setup_bitcell()
|
||||||
|
|
||||||
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
|
debug.info(2, "Testing 2x2 array for cell_2port")
|
||||||
a = factory.create(module_type="bitcell_array", cols=4, rows=4)
|
a = factory.create(module_type="bitcell_array", cols=2, rows=2)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class replica_bitcell_array_test(openram_test):
|
||||||
|
|
||||||
factory.reset()
|
factory.reset()
|
||||||
debug.info(2, "Testing 4x4 array for bitcell")
|
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)
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ class sram_1bank_2mux_1rw_1r_test(openram_test):
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
from sram_config import sram_config
|
from sram_config import sram_config
|
||||||
|
|
||||||
|
OPTS.route_supplies = False
|
||||||
OPTS.num_rw_ports = 1
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_r_ports = 1
|
OPTS.num_r_ports = 1
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,9 @@ class model_delay_test(openram_test):
|
||||||
|
|
||||||
# Only compare the delays
|
# Only compare the delays
|
||||||
spice_delays = {key:value for key, value in spice_data.items() if 'delay' in key}
|
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 = {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,"Spice Delays={}".format(spice_delays))
|
||||||
debug.info(1,"Model Delays={}".format(model_delays))
|
debug.info(1,"Model Delays={}".format(model_delays))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skip("SKIPPING 26_hspice_pex_pinv_test")
|
||||||
class hspice_pex_pinv_test(openram_test):
|
class hspice_pex_pinv_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
|
@ -39,10 +40,10 @@ class hspice_pex_pinv_test(openram_test):
|
||||||
OPTS.keep_temp = True
|
OPTS.keep_temp = True
|
||||||
debug.info(2, "Checking 1x size inverter")
|
debug.info(2, "Checking 1x size inverter")
|
||||||
tx = pinv.pinv(name="pinv", size=1)
|
tx = pinv.pinv(name="pinv", size=1)
|
||||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
|
tempgds = "{}.gds".format(tx.name)
|
||||||
tx.gds_write(tempgds)
|
tx.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
|
||||||
tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name)
|
tempsp = "{}.sp".format(tx.name)
|
||||||
tx.sp_write(tempsp)
|
tx.sp_write("{0}{1}".format(OPTS.openram_temp, tempsp))
|
||||||
|
|
||||||
# make sure that the library simulation is successful
|
# make sure that the library simulation is successful
|
||||||
sp_delay = self.simulate_delay(test_module=tempsp,
|
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):
|
def simulate_delay(self, test_module, top_level_name):
|
||||||
from charutils import parse_spice_list
|
from charutils import parse_spice_list
|
||||||
# setup simulation
|
# setup simulation
|
||||||
sim_file = OPTS.openram_temp + "stim.sp"
|
sim_file = "stim.sp"
|
||||||
log_file_name = "timing"
|
log_file_name = "timing"
|
||||||
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
|
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
|
||||||
test_sim.run_sim("stim.sp")
|
test_sim.run_sim("stim.sp")
|
||||||
|
|
@ -86,7 +87,7 @@ class hspice_pex_pinv_test(openram_test):
|
||||||
import tech
|
import tech
|
||||||
from characterizer import measurements, stimuli
|
from characterizer import measurements, stimuli
|
||||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
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)
|
simulation = stimuli(sim_file, corner)
|
||||||
|
|
||||||
# library files
|
# library files
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import globals
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
@unittest.skip("SKIPPING 26_ngspice_pex_pinv_test")
|
||||||
class ngspice_pex_pinv_test(openram_test):
|
class ngspice_pex_pinv_test(openram_test):
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
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
|
OPTS.keep_temp = True # force set keep to true to save the sp file
|
||||||
debug.info(2, "Checking 1x size inverter")
|
debug.info(2, "Checking 1x size inverter")
|
||||||
tx = pinv.pinv(name="pinv", size=1)
|
tx = pinv.pinv(name="pinv", size=1)
|
||||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name)
|
tempgds = "{}.gds".format(tx.name)
|
||||||
tx.gds_write(tempgds)
|
tx.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
|
||||||
tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name)
|
tempsp = "{}.sp".format(tx.name)
|
||||||
tx.sp_write(tempsp)
|
tx.sp_write("{0}{1}".format(OPTS.openram_temp, tempsp))
|
||||||
|
|
||||||
# make sure that the library simulation is successful
|
# make sure that the library simulation is successful
|
||||||
sp_delay = self.simulate_delay(test_module=tempsp,
|
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):
|
def simulate_delay(self, test_module, top_level_name):
|
||||||
from charutils import parse_spice_list
|
from charutils import parse_spice_list
|
||||||
|
cwd = os.getcwd()
|
||||||
|
os.chdir(OPTS.openram_temp)
|
||||||
|
|
||||||
# setup simulation
|
# setup simulation
|
||||||
sim_file = OPTS.openram_temp + "stim.sp"
|
sim_file = "stim.sp"
|
||||||
log_file_name = "timing"
|
log_file_name = "timing"
|
||||||
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
|
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
|
||||||
test_sim.run_sim("stim.sp")
|
test_sim.run_sim("stim.sp")
|
||||||
delay = parse_spice_list(log_file_name, "pinv_delay")
|
delay = parse_spice_list(log_file_name, "pinv_delay")
|
||||||
|
|
||||||
|
os.chdir(cwd)
|
||||||
return delay
|
return delay
|
||||||
|
|
||||||
def write_simulation(self, sim_file, cir_file, top_module_name):
|
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)
|
simulation = stimuli(sim_file, corner)
|
||||||
|
|
||||||
# library files
|
# library files
|
||||||
|
import pdb; pdb.set_trace()
|
||||||
simulation.write_include(cir_file)
|
simulation.write_include(cir_file)
|
||||||
|
|
||||||
# supply voltages
|
# supply voltages
|
||||||
|
|
|
||||||
|
|
@ -53,21 +53,27 @@ class openram_back_end_test(openram_test):
|
||||||
else:
|
else:
|
||||||
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
|
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
|
||||||
config_name = "{0}/tests/configs/config_back_end.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,
|
cmd = "{0} -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
|
||||||
out_file,
|
out_file,
|
||||||
out_path,
|
out_path,
|
||||||
options,
|
options,
|
||||||
config_name,
|
config_name,
|
||||||
out_path)
|
out_path)
|
||||||
debug.info(1, cmd)
|
debug.info(1, cmd)
|
||||||
os.system(cmd)
|
os.system(cmd)
|
||||||
|
|
||||||
# assert an error until we actually check a resul
|
# assert an error until we actually check a resul
|
||||||
for extension in ["gds", "v", "lef", "sp"]:
|
for extension in ["gds", "v", "lef", "sp", "lvs.sp"]:
|
||||||
filename = "{0}/{1}.{2}".format(out_path, out_file, extension)
|
filename = "{0}{1}.{2}".format(out_path, out_file, extension)
|
||||||
debug.info(1, "Checking for file: " + filename)
|
debug.info(1, "Checking for file: " + filename)
|
||||||
self.assertEqual(os.path.exists(filename), True)
|
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
|
# Make sure there is any .lib file
|
||||||
import glob
|
import glob
|
||||||
files = glob.glob('{0}/*.lib'.format(out_path))
|
files = glob.glob('{0}/*.lib'.format(out_path))
|
||||||
|
|
@ -79,7 +85,7 @@ class openram_back_end_test(openram_test):
|
||||||
self.assertTrue(len(datasheets)>0)
|
self.assertTrue(len(datasheets)>0)
|
||||||
|
|
||||||
# grep any errors from the output
|
# 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 = output_log.read()
|
||||||
output_log.close()
|
output_log.close()
|
||||||
self.assertEqual(len(re.findall('ERROR', output)), 0)
|
self.assertEqual(len(re.findall('ERROR', output)), 0)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,5 @@ num_words = 16
|
||||||
tech_name = OPTS.tech_name
|
tech_name = OPTS.tech_name
|
||||||
|
|
||||||
nominal_corner_only = True
|
nominal_corner_only = True
|
||||||
route_supplies = True
|
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,7 @@ num_words = 16
|
||||||
tech_name = OPTS.tech_name
|
tech_name = OPTS.tech_name
|
||||||
|
|
||||||
nominal_corner_only = True
|
nominal_corner_only = True
|
||||||
route_supplies = True
|
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
inline_lvsdrc = True
|
spice_name = "ngspice"
|
||||||
analytical_delay = False
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
04_precharge_pbitcell_test.py
|
04_precharge_pbitcell_test.py
|
||||||
04_replica_pbitcell_test.py
|
04_replica_pbitcell_test.py
|
||||||
04_column_mux_pbitcell_test.py
|
04_column_mux_pbitcell_test.py
|
||||||
05_bitcell_1rw_1r_array_test.py
|
|
||||||
05_bitcell_array_test.py
|
05_bitcell_array_test.py
|
||||||
05_dummy_array_test.py
|
05_dummy_array_test.py
|
||||||
05_pbitcell_array_test.py
|
05_pbitcell_array_test.py
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ class openram_test(unittest.TestCase):
|
||||||
|
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, w.name)
|
tempgds = "{}.gds".format(w.name)
|
||||||
w.gds_write(tempgds)
|
w.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
|
||||||
import verify
|
import verify
|
||||||
|
|
||||||
result=verify.run_drc(w.name, tempgds, None)
|
result=verify.run_drc(w.name, tempgds, None)
|
||||||
|
|
@ -36,13 +36,13 @@ class openram_test(unittest.TestCase):
|
||||||
|
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name)
|
tempspice = "{}.sp".format(a.name)
|
||||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, 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
|
# cannot write gds in netlist_only mode
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
a.gds_write(tempgds)
|
a.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
|
||||||
|
|
||||||
import verify
|
import verify
|
||||||
# Run both DRC and LVS even if DRC might fail
|
# Run both DRC and LVS even if DRC might fail
|
||||||
|
|
@ -80,15 +80,13 @@ class openram_test(unittest.TestCase):
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
|
||||||
def run_pex(self, a, output=None):
|
def run_pex(self, a, output=None):
|
||||||
if output == None:
|
tempspice = "{}.sp".format(a.name)
|
||||||
output = OPTS.openram_temp + a.name + ".pex.netlist"
|
tempgds = "{}.gds".format(a.name)
|
||||||
tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name)
|
|
||||||
tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name)
|
|
||||||
|
|
||||||
a.gds_write(tempgds)
|
a.gds_write("{0}{1}".format(OPTS.openram_temp, tempgds))
|
||||||
|
|
||||||
import verify
|
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:
|
if result != 0:
|
||||||
self.fail("PEX ERROR: {}".format(a.name))
|
self.fail("PEX ERROR: {}".format(a.name))
|
||||||
return output
|
return output
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ from tech import lvs_name
|
||||||
from tech import pex_name
|
from tech import pex_name
|
||||||
|
|
||||||
debug.info(1, "Initializing verify...")
|
debug.info(1, "Initializing verify...")
|
||||||
|
|
||||||
if not OPTS.check_lvsdrc:
|
if not OPTS.check_lvsdrc:
|
||||||
debug.info(1, "LVS/DRC/PEX disabled.")
|
debug.info(1, "LVS/DRC/PEX disabled.")
|
||||||
OPTS.drc_exe = None
|
OPTS.drc_exe = None
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,8 @@ def write_drc_script(cell_name, gds_name, extract, final_verification=False, out
|
||||||
run_file = output_path + "run_drc.sh"
|
run_file = output_path + "run_drc.sh"
|
||||||
f = open(run_file, "w")
|
f = open(run_file, "w")
|
||||||
f.write("#!/bin/sh\n")
|
f.write("#!/bin/sh\n")
|
||||||
cmd = "{0} -gui -drc {1}drc_runset -batch".format(OPTS.drc_exe[1],
|
cmd = "{0} -gui -drc drc_runset -batch".format(OPTS.drc_exe[1])
|
||||||
output_path)
|
|
||||||
f.write(cmd)
|
f.write(cmd)
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
f.close()
|
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"
|
run_file = output_path + "run_lvs.sh"
|
||||||
f = open(run_file, "w")
|
f = open(run_file, "w")
|
||||||
f.write("#!/bin/sh\n")
|
f.write("#!/bin/sh\n")
|
||||||
cmd = "{0} -gui -lvs {1}lvs_runset -batch".format(OPTS.lvs_exe[1],
|
cmd = "{0} -gui -lvs lvs_runset -batch".format(OPTS.lvs_exe[1])
|
||||||
output_path)
|
|
||||||
f.write(cmd)
|
f.write(cmd)
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
f.close()
|
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"
|
run_file = output_path + "run_pex.sh"
|
||||||
f = open(run_file, "w")
|
f = open(run_file, "w")
|
||||||
f.write("#!/bin/sh\n")
|
f.write("#!/bin/sh\n")
|
||||||
cmd = "{0} -gui -pex {1}pex_runset -batch".format(OPTS.pex_exe[1],
|
cmd = "{0} -gui -pex pex_runset -batch".format(OPTS.pex_exe[1])
|
||||||
OPTS.openram_temp)
|
|
||||||
f.write(cmd)
|
f.write(cmd)
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
f.close()
|
f.close()
|
||||||
|
|
@ -195,22 +195,8 @@ def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=Fals
|
||||||
global num_drc_runs
|
global num_drc_runs
|
||||||
num_drc_runs += 1
|
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)
|
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")
|
(outfile, errfile, resultsfile) = run_script(cell_name, "drc")
|
||||||
|
|
||||||
# check the result for these lines in the summary:
|
# 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)
|
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")
|
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
|
||||||
|
|
||||||
# check the result for these lines in the summary:
|
# 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)
|
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")
|
(outfile, errfile, resultsfile) = run_script(cell_name, "pex")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,6 @@ num_pex_runs = 0
|
||||||
|
|
||||||
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path, sp_name=None):
|
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. """
|
""" Write a magic script to perform DRC and optionally extraction. """
|
||||||
|
|
||||||
global OPTS
|
global OPTS
|
||||||
|
|
||||||
# Copy .magicrc file into the output directory
|
# 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:
|
else:
|
||||||
debug.warning("Could not locate .magicrc file: {}".format(magic_file))
|
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 = open(run_file, "w")
|
||||||
f.write("#!/bin/sh\n")
|
f.write("#!/bin/sh\n")
|
||||||
f.write('export OPENRAM_TECH="{}"\n'.format(os.environ['OPENRAM_TECH']))
|
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('\n')
|
||||||
f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1]))
|
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 polygon subcell true\n")
|
||||||
f.write("gds warning default\n")
|
f.write("gds warning default\n")
|
||||||
f.write("gds flatten true\n")
|
f.write("gds flatten true\n")
|
||||||
f.write("gds readonly 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("gds read {}\n".format(gds_name))
|
||||||
f.write('puts "Finished reading gds {}"\n'.format(gds_name))
|
f.write('puts "Finished reading gds {}"\n'.format(gds_name))
|
||||||
f.write("load {}\n".format(cell_name))
|
f.write("load {}\n".format(cell_name))
|
||||||
f.write('puts "Finished loading cell {}"\n'.format(cell_name))
|
f.write('puts "Finished loading cell {}"\n'.format(cell_name))
|
||||||
f.write("cellname delete \\(UNNAMED\\)\n")
|
f.write("cellname delete \\(UNNAMED\\)\n")
|
||||||
f.write("writeall force\n")
|
f.write("writeall force\n")
|
||||||
f.write("select top cell\n")
|
|
||||||
f.write("expand\n")
|
# Extract
|
||||||
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")
|
|
||||||
if not sp_name:
|
if not sp_name:
|
||||||
f.write("port makeall\n")
|
f.write("port makeall\n")
|
||||||
else:
|
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 format ngspice\n")
|
||||||
f.write(pre + "ext2spice {}\n".format(cell_name))
|
f.write(pre + "ext2spice {}\n".format(cell_name))
|
||||||
f.write('puts "Finished ext2spice"\n')
|
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("quit -noprompt\n")
|
||||||
f.write("EOF\n")
|
f.write("EOF\n")
|
||||||
f.write("magic_retcode=$?\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()
|
f.close()
|
||||||
os.system("chmod u+x {}".format(run_file))
|
os.system("chmod u+x {}".format(run_file))
|
||||||
|
|
||||||
|
|
||||||
def run_drc(cell_name, gds_name, sp_name=None, extract=True, final_verification=False):
|
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."""
|
"""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
|
global num_drc_runs
|
||||||
num_drc_runs += 1
|
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)
|
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")
|
(outfile, errfile, resultsfile) = run_script(cell_name, "drc")
|
||||||
|
|
||||||
# Check the result for these lines in the summary:
|
# 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:
|
if not output_path:
|
||||||
output_path = OPTS.openram_temp
|
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)
|
write_lvs_script(cell_name, gds_name, sp_name, final_verification)
|
||||||
|
|
||||||
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
|
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ lvs_warned = False
|
||||||
pex_warned = False
|
pex_warned = False
|
||||||
|
|
||||||
|
|
||||||
def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None):
|
def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None, sp_name=None):
|
||||||
pass
|
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):
|
def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=False, output_path=None):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue