Merge remote-tracking branch 'private/dev' into dev

This commit is contained in:
mrg 2020-12-23 11:59:38 -08:00
commit 80c0bccd70
64 changed files with 502 additions and 16720 deletions

View File

@ -467,6 +467,23 @@ class layout():
"""
self.pin_map[text] = set()
def remove_layout_pins(self):
"""
Delete all the layout pins
"""
self.pin_map = {}
def replace_layout_pin(self, text, pin):
"""
Remove the old pin and replace with a new one
"""
self.remove_layout_pin(text)
self.add_layout_pin(text=text,
layer=pin.layer,
offset=pin.ll(),
width=pin.width(),
height=pin.height())
def add_layout_pin(self, text, layer, offset, width=None, height=None):
"""
Create a labeled pin
@ -1191,6 +1208,21 @@ class layout():
elif add_vias:
self.add_power_pin(name, pin.center(), start_layer=pin.layer)
def add_io_pin(self, instance, pin_name, new_name="", start_layer=None):
"""
Add a signle input or output pin up to metal 3.
"""
pin = instance.get_pin(pin_name)
if new_name == "":
new_name = pin_name
if not start_layer:
start_layer = pin.layer
# Just use the power pin function for now to save code
self.add_power_pin(name=new_name, loc=pin.center(), start_layer=start_layer)
def add_power_pin(self, name, loc, size=[1, 1], directions=None, start_layer="m1"):
"""
Add a single power pin from the lowest power_grid layer down to M1 (or li) at

View File

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

View File

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

View File

@ -582,9 +582,8 @@ def report_status():
debug.print_raw("Technology: {0}".format(OPTS.tech_name))
total_size = OPTS.word_size*OPTS.num_words*OPTS.num_banks
debug.print_raw("Total size: {} bits".format(total_size))
if total_size >= 2**14:
debug.warning("Requesting such a large memory size ({0}) will have a large run-time. ".format(total_size) +
"Consider using multiple smaller banks.")
if total_size >= 2**14 and not OPTS.analytical_delay:
debug.warning("Characterizing large memories ({0}) will have a large run-time. ".format(total_size))
debug.print_raw("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
OPTS.num_words,
OPTS.num_banks))

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
# See LICENSE for licensing information.
#
#Copyright (c) 2016-2019 Regents of the University of California and The Board
#of Regents for the Oklahoma Agricultural and Mechanical College
#(acting for and on behalf of Oklahoma State University)
#All rights reserved.
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
from globals import print_time
@ -12,6 +12,7 @@ from datetime import datetime
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):
@ -26,9 +27,9 @@ class supply_tree_router(router):
either the gds file name or the design itself (by saving to a gds file).
"""
# Power rail width in minimum wire widths
self.rail_track_width = 3
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):
"""
@ -36,9 +37,7 @@ class supply_tree_router(router):
"""
size = self.ur - self.ll
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
import supply_grid
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
self.rg = signal_grid(self.ll, self.ur, self.route_track_width)
def route(self, vdd_name="vdd", gnd_name="gnd"):
"""
@ -75,7 +74,7 @@ class supply_tree_router(router):
self.route_pins(gnd_name)
print_time("Maze routing supplies",datetime.now(), start_time, 3)
#self.write_debug_gds("final.gds",False)
# self.write_debug_gds("final_tree_router.gds",False)
# Did we route everything??
if not self.check_all_routed(vdd_name):
@ -85,47 +84,6 @@ class supply_tree_router(router):
return True
def check_all_routed(self, pin_name):
"""
Check that all pin groups are routed.
"""
for pg in self.pin_groups[pin_name]:
if not pg.is_routed():
return False
def prepare_blockages(self, pin_name):
"""
Reset and add all of the blockages in the design.
Names is a list of pins to add as a blockage.
"""
debug.info(3,"Preparing blockages.")
# Start fresh. Not the best for run-time, but simpler.
self.clear_blockages()
# This adds the initial blockges of the design
#print("BLOCKING:",self.blocked_grids)
self.set_blockages(self.blocked_grids,True)
# Block all of the pin components (some will be unblocked if they're a source/target)
# Also block the previous routes
for name in self.pin_groups:
blockage_grids = {y for x in self.pin_groups[name] for y in x.grids}
self.set_blockages(blockage_grids,True)
blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages}
self.set_blockages(blockage_grids,True)
# FIXME: These duplicate a bit of work
# These are the paths that have already been routed.
self.set_blockages(self.path_blockages)
# Don't mark the other components as targets since we want to route
# directly to a rail, but unblock all the source components so we can
# route over them
blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids}
self.set_blockages(blockage_grids,False)
def route_pins(self, pin_name):
"""
This will route each of the remaining pin components to the other pins.
@ -137,6 +95,7 @@ class supply_tree_router(router):
remaining_components))
# Create full graph
debug.info(2,"Creating adjacency matrix")
pin_size = len(self.pin_groups[pin_name])
adj_matrix = [[0] * pin_size for i in range(pin_size)]
@ -148,6 +107,7 @@ class supply_tree_router(router):
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)
@ -159,8 +119,6 @@ class supply_tree_router(router):
if mst[x][y]>0:
connections.append((x, y))
debug.info(1,"MST has {0} segments.".format(len(connections)))
# Route MST components
for (src, dest) in connections:
self.route_signal(pin_name, src, dest)
@ -195,3 +153,14 @@ class supply_tree_router(router):
def add_io_pin(self, instance, pin_name, new_name=""):
"""
Add a signle input or output pin up to metal 3.
"""
pin = instance.get_pins(pin_name)
if new_name == "":
new_name = pin_name
# Just use the power pin function for now to save code
self.add_power_pin(name=new_name, loc=pin.center(), start_layer=pin.layer)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -190,13 +190,13 @@ class sram_base(design, verilog, lef):
start_time = datetime.datetime.now()
self.route_layout()
self.route_supplies()
if not OPTS.is_unit_test:
print_time("Routing", datetime.datetime.now(), start_time)
self.add_lvs_correspondence_points()
self.offset_all_coordinates()
#self.offset_all_coordinates()
highest_coord = self.find_highest_coords()
self.width = highest_coord[0]
@ -245,10 +245,19 @@ class sram_base(design, verilog, lef):
rtr=router(grid_stack, self)
rtr.route()
vdd_pin = rtr.get_pin("vdd")
gnd_pin = rtr.get_pin("gnd")
for pin_name, pin in [("vdd", vdd_pin), ("gnd", gnd_pin)]:
# 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(),

View File

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

View File

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

View File

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

View File

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

View File

@ -68,7 +68,6 @@ num_pex_runs = 0
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path, sp_name=None):
""" Write a magic script to perform DRC and optionally extraction. """
global OPTS
# Copy .magicrc file into the output directory

View File

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