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() 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
@ -1191,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

View File

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

View File

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

View File

@ -582,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))

View File

@ -92,7 +92,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.
@ -141,7 +141,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

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) # (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.

View File

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

View File

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

View File

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

View File

@ -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.
@ -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))
@ -1007,11 +1022,10 @@ class router(router_tech):
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 return True
else: else:
return False return False
@ -1118,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)
@ -1152,6 +1166,14 @@ 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): def get_pin(self, pin_name):
""" Return the lowest, leftest pin group """ """ Return the lowest, leftest pin group """
keep_pin = None keep_pin = None
@ -1165,6 +1187,14 @@ class router(router_tech):
return keep_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):

View File

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

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) # (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,18 +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")
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
@ -142,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.
""" """

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
# 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 debug import debug
from globals import print_time from globals import print_time
@ -12,6 +12,7 @@ from datetime import datetime
import grid_utils import grid_utils
from scipy.sparse import csr_matrix from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import minimum_spanning_tree from scipy.sparse.csgraph import minimum_spanning_tree
from signal_grid import signal_grid
class supply_tree_router(router): 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). 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):
""" """
@ -36,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"):
""" """
@ -75,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):
@ -85,47 +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.
@ -137,6 +95,7 @@ class supply_tree_router(router):
remaining_components)) remaining_components))
# Create full graph # Create full graph
debug.info(2,"Creating adjacency matrix")
pin_size = len(self.pin_groups[pin_name]) pin_size = len(self.pin_groups[pin_name])
adj_matrix = [[0] * pin_size for i in range(pin_size)] 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 adj_matrix[index1][index2] = dist
# Find MST # Find MST
debug.info(2,"Finding MinimumSpanning Tree")
X = csr_matrix(adj_matrix) X = csr_matrix(adj_matrix)
Tcsr = minimum_spanning_tree(X) Tcsr = minimum_spanning_tree(X)
mst = Tcsr.toarray().astype(int) mst = Tcsr.toarray().astype(int)
@ -159,8 +119,6 @@ class supply_tree_router(router):
if mst[x][y]>0: if mst[x][y]>0:
connections.append((x, y)) connections.append((x, y))
debug.info(1,"MST has {0} segments.".format(len(connections)))
# Route MST components # Route MST components
for (src, dest) in connections: for (src, dest) in connections:
self.route_signal(pin_name, src, dest) 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): 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),

View File

@ -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:
@ -418,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)]
@ -446,10 +435,10 @@ 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.m3_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:
# This causes problem in magic since it sometimes cannot extract connectivity of isntances # This causes problem in magic since it sometimes cannot extract connectivity of isntances
# with no active devices. # with no active devices.
@ -461,10 +450,10 @@ class sram_1bank(sram_base):
else: else:
offset = vector(0, offset = vector(0,
self.bank.height + self.m3_pitch) 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:
# This causes problem in magic since it sometimes cannot extract connectivity of isntances # This causes problem in magic since it sometimes cannot extract connectivity of isntances
# with no active devices. # with no active devices.

View File

@ -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]
@ -245,10 +245,19 @@ class sram_base(design, verilog, lef):
rtr=router(grid_stack, self) rtr=router(grid_stack, self)
rtr.route() rtr.route()
vdd_pin = rtr.get_pin("vdd") # Find the lowest leftest pin for vdd and gnd
gnd_pin = rtr.get_pin("gnd") for pin_name in ["vdd", "gnd"]:
for pin_name, pin in [("vdd", vdd_pin), ("gnd", gnd_pin)]: # 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) self.remove_layout_pin(pin_name)
pin = rtr.get_pin(pin_name)
self.add_layout_pin(pin_name, self.add_layout_pin(pin_name,
pin.layer, pin.layer,
pin.ll(), pin.ll(),

View File

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

View File

@ -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 = "tree"
check_lvsdrc = True check_lvsdrc = True

View File

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

View File

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

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): 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

View File

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