mirror of https://github.com/VLSIDA/OpenRAM.git
Merge remote-tracking branch 'private/dev' into dev
This commit is contained in:
commit
80c0bccd70
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = True
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
perimeter_pins = True
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
|
output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ num_w_ports = 0
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corner_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = "tree"
|
||||||
check_lvsdrc = False
|
check_lvsdrc = True
|
||||||
perimeter_pins = False
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
[globals/init_openram]: Initializing OpenRAM...
|
|
||||||
[globals/setup_paths]: Setting up paths...
|
|
||||||
[globals/setup_paths]: Temporary files saved in /home/jesse/output/
|
|
||||||
[globals/read_config]: Configuration file is /home/jesse/skywater-tech/riscv_1k_s8.py
|
|
||||||
[globals/read_config]: Output saved in /home/jesse/openram/compiler/riscv/
|
|
||||||
[globals/import_tech]: Importing technology: s8
|
|
||||||
[globals/import_tech]: Adding technology path: /home/jesse/openram/technology
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,61 +0,0 @@
|
||||||
[globals/init_openram]: Initializing OpenRAM...
|
|
||||||
[globals/setup_paths]: Setting up paths...
|
|
||||||
[globals/setup_paths]: Temporary files saved in /home/jesse/output/
|
|
||||||
[globals/read_config]: Configuration file is /home/jesse/skywater-tech/riscv_1k_s8.py
|
|
||||||
[globals/read_config]: Output saved in /home/jesse/openram/compiler/riscv/
|
|
||||||
[globals/import_tech]: Importing technology: sky130
|
|
||||||
[globals/import_tech]: Adding technology path: /home/jesse/openram/technology
|
|
||||||
[globals/init_paths]: Creating temp directory: /home/jesse/output/
|
|
||||||
[verify/<module>]: Initializing verify...
|
|
||||||
[verify/<module>]: LVS/DRC/PEX disabled.
|
|
||||||
[characterizer/<module>]: Initializing characterizer...
|
|
||||||
[characterizer/<module>]: Analytical model enabled.
|
|
||||||
[globals/setup_bitcell]: Using bitcell: bitcell_1rw_1r
|
|
||||||
|==============================================================================|
|
|
||||||
|========= OpenRAM v1.1.5 =========|
|
|
||||||
|========= =========|
|
|
||||||
|========= VLSI Design and Automation Lab =========|
|
|
||||||
|========= Computer Science and Engineering Department =========|
|
|
||||||
|========= University of California Santa Cruz =========|
|
|
||||||
|========= =========|
|
|
||||||
|========= Usage help: openram-user-group@ucsc.edu =========|
|
|
||||||
|========= Development help: openram-dev-group@ucsc.edu =========|
|
|
||||||
|========= Temp dir: /home/jesse/output/ =========|
|
|
||||||
|========= See LICENSE for license info =========|
|
|
||||||
|==============================================================================|
|
|
||||||
** Start: 06/25/2020 07:53:43
|
|
||||||
Technology: sky130
|
|
||||||
Total size: 8192 bits
|
|
||||||
Word size: 32
|
|
||||||
Words: 256
|
|
||||||
Banks: 1
|
|
||||||
Write size: 8
|
|
||||||
RW ports: 1
|
|
||||||
R-only ports: 1
|
|
||||||
W-only ports: 0
|
|
||||||
DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to do inline checking).
|
|
||||||
DRC/LVS/PEX is disabled (check_lvsdrc=True to enable).
|
|
||||||
Characterization is disabled (using analytical delay models) (analytical_delay=False to simulate).
|
|
||||||
[bitcell_1rw_1r/__init__]: Create bitcell with 1RW and 1R Port
|
|
||||||
[sram_config/recompute_sizes]: Recomputing with words per row: 2
|
|
||||||
[sram_config/recompute_sizes]: Rows: 128 Cols: 64
|
|
||||||
[sram_config/recompute_sizes]: Row addr size: 7 Col addr size: 1 Bank addr size: 8
|
|
||||||
Words per row: 2
|
|
||||||
Output files are:
|
|
||||||
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.sp
|
|
||||||
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.v
|
|
||||||
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.lib
|
|
||||||
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.py
|
|
||||||
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.html
|
|
||||||
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.log
|
|
||||||
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.lef
|
|
||||||
/home/jesse/openram/compiler/riscv/sram_1rw1r_32_256_8_sky130.gds
|
|
||||||
[sram/__init__]: create sram of size 32 with 256 num of words 1 banks
|
|
||||||
[dff_array/__init__]: Creating row_addr_dff rows=7 cols=1
|
|
||||||
[dff_array/__init__]: Creating col_addr_dff rows=1 cols=1
|
|
||||||
[dff_array/__init__]: Creating data_dff rows=1 cols=32
|
|
||||||
[dff_array/__init__]: Creating wmask_dff rows=1 cols=4
|
|
||||||
[bank/__init__]: create sram of size 32 with 256 words
|
|
||||||
[port_data/__init__]: create data port of size 32 with 2 words per row
|
|
||||||
[precharge/__init__]: creating precharge cell precharge
|
|
||||||
[pgate/bin_width]: binning pmos tx, target: 0.44999999999999996, found 0.55 x 1 = 0.55
|
|
||||||
|
|
@ -5,12 +5,11 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import numpy as np
|
|
||||||
import string
|
|
||||||
import debug
|
import debug
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
from grid_cell import grid_cell
|
from grid_cell import grid_cell
|
||||||
|
|
||||||
|
|
||||||
class grid:
|
class grid:
|
||||||
"""
|
"""
|
||||||
A two layer routing map. Each cell can be blocked in the vertical
|
A two layer routing map. Each cell can be blocked in the vertical
|
||||||
|
|
@ -23,7 +22,6 @@ class grid:
|
||||||
NONPREFERRED_COST = 4
|
NONPREFERRED_COST = 4
|
||||||
PREFERRED_COST = 1
|
PREFERRED_COST = 1
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, ll, ur, track_width):
|
def __init__(self, ll, ur, track_width):
|
||||||
""" Initialize the map and define the costs. """
|
""" Initialize the map and define the costs. """
|
||||||
|
|
||||||
|
|
@ -33,12 +31,12 @@ class grid:
|
||||||
|
|
||||||
self.track_width = track_width
|
self.track_width = track_width
|
||||||
self.track_widths = [self.track_width, self.track_width, 1.0]
|
self.track_widths = [self.track_width, self.track_width, 1.0]
|
||||||
self.track_factor = [1/self.track_width, 1/self.track_width, 1.0]
|
self.track_factor = [1 / self.track_width, 1 / self.track_width, 1.0]
|
||||||
|
|
||||||
# The bounds are in grids for this
|
# The bounds are in grids for this
|
||||||
# This is really lower left bottom layer and upper right top layer in 3D.
|
# This is really lower left bottom layer and upper right top layer in 3D.
|
||||||
self.ll = vector3d(ll.x,ll.y,0).scale(self.track_factor).round()
|
self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round()
|
||||||
self.ur = vector3d(ur.x,ur.y,1).scale(self.track_factor).round()
|
self.ur = vector3d(ur.x, ur.y, 1).scale(self.track_factor).round()
|
||||||
|
|
||||||
# let's leave the map sparse, cells are created on demand to reduce memory
|
# let's leave the map sparse, cells are created on demand to reduce memory
|
||||||
self.map={}
|
self.map={}
|
||||||
|
|
@ -46,18 +44,18 @@ class grid:
|
||||||
def add_all_grids(self):
|
def add_all_grids(self):
|
||||||
for x in range(self.ll.x, self.ur.x, 1):
|
for x in range(self.ll.x, self.ur.x, 1):
|
||||||
for y in range(self.ll.y, self.ur.y, 1):
|
for y in range(self.ll.y, self.ur.y, 1):
|
||||||
self.add_map(vector3d(x,y,0))
|
self.add_map(vector3d(x, y, 0))
|
||||||
self.add_map(vector3d(x,y,1))
|
self.add_map(vector3d(x, y, 1))
|
||||||
|
|
||||||
def set_blocked(self,n,value=True):
|
def set_blocked(self, n, value=True):
|
||||||
if not isinstance(n, vector3d):
|
if not isinstance(n, vector3d):
|
||||||
for item in n:
|
for item in n:
|
||||||
self.set_blocked(item,value)
|
self.set_blocked(item, value)
|
||||||
else:
|
else:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
self.map[n].blocked=value
|
self.map[n].blocked=value
|
||||||
|
|
||||||
def is_blocked(self,n):
|
def is_blocked(self, n):
|
||||||
if not isinstance(n, vector3d):
|
if not isinstance(n, vector3d):
|
||||||
for item in n:
|
for item in n:
|
||||||
if self.is_blocked(item):
|
if self.is_blocked(item):
|
||||||
|
|
@ -68,11 +66,10 @@ class grid:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
return self.map[n].blocked
|
return self.map[n].blocked
|
||||||
|
|
||||||
|
def set_path(self, n, value=True):
|
||||||
def set_path(self,n,value=True):
|
if isinstance(n, (list, tuple, set, frozenset)):
|
||||||
if isinstance(n, (list,tuple,set,frozenset)):
|
|
||||||
for item in n:
|
for item in n:
|
||||||
self.set_path(item,value)
|
self.set_path(item, value)
|
||||||
else:
|
else:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
self.map[n].path=value
|
self.map[n].path=value
|
||||||
|
|
@ -81,47 +78,89 @@ class grid:
|
||||||
for k in self.map:
|
for k in self.map:
|
||||||
self.map[k].blocked=False
|
self.map[k].blocked=False
|
||||||
|
|
||||||
def set_source(self,n,value=True):
|
def set_source(self, n, value=True):
|
||||||
if not isinstance(n, vector3d):
|
if not isinstance(n, vector3d):
|
||||||
for item in n:
|
for item in n:
|
||||||
self.set_source(item,value)
|
self.set_source(item, value)
|
||||||
else:
|
else:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
self.map[n].source=value
|
self.map[n].source=value
|
||||||
self.source.add(n)
|
self.source.add(n)
|
||||||
|
|
||||||
def set_target(self,n,value=True):
|
def set_target(self, n, value=True):
|
||||||
if not isinstance(n, vector3d):
|
if not isinstance(n, vector3d):
|
||||||
for item in n:
|
for item in n:
|
||||||
self.set_target(item,value)
|
self.set_target(item, value)
|
||||||
else:
|
else:
|
||||||
self.add_map(n)
|
self.add_map(n)
|
||||||
self.map[n].target=value
|
self.map[n].target=value
|
||||||
self.target.add(n)
|
self.target.add(n)
|
||||||
|
|
||||||
|
def add_source(self, track_list, value=True):
|
||||||
def add_source(self,track_list,value=True):
|
debug.info(3, "Adding source list={0}".format(str(track_list)))
|
||||||
debug.info(3,"Adding source list={0}".format(str(track_list)))
|
|
||||||
for n in track_list:
|
for n in track_list:
|
||||||
debug.info(4,"Adding source ={0}".format(str(n)))
|
debug.info(4, "Adding source ={0}".format(str(n)))
|
||||||
self.set_source(n,value)
|
self.set_source(n, value)
|
||||||
self.set_blocked(n,False)
|
self.set_blocked(n, False)
|
||||||
|
|
||||||
|
def add_target(self, track_list, value=True):
|
||||||
def add_target(self,track_list,value=True):
|
debug.info(3, "Adding target list={0}".format(str(track_list)))
|
||||||
debug.info(3,"Adding target list={0}".format(str(track_list)))
|
|
||||||
for n in track_list:
|
for n in track_list:
|
||||||
debug.info(4,"Adding target ={0}".format(str(n)))
|
debug.info(4, "Adding target ={0}".format(str(n)))
|
||||||
self.set_target(n,value)
|
self.set_target(n, value)
|
||||||
self.set_blocked(n,False)
|
self.set_blocked(n, False)
|
||||||
|
|
||||||
def is_target(self,point):
|
def add_perimeter_target(self, side="all", value=True):
|
||||||
|
debug.info(3, "Adding perimeter target")
|
||||||
|
|
||||||
|
# Add the left/right columns
|
||||||
|
if side=="all" or side=="left":
|
||||||
|
x = self.ll.x
|
||||||
|
for y in range(self.ll.y, self.ur.y, 1):
|
||||||
|
n = vector3d(x, y, 0)
|
||||||
|
self.set_target(n, value)
|
||||||
|
self.set_blocked(n, False)
|
||||||
|
n = vector3d(x, y, 1)
|
||||||
|
self.set_target(n, value)
|
||||||
|
self.set_blocked(n, False)
|
||||||
|
|
||||||
|
if side=="all" or side=="right":
|
||||||
|
x = self.ur.x
|
||||||
|
for y in range(self.ll.y, self.ur.y, 1):
|
||||||
|
n = vector3d(x, y, 0)
|
||||||
|
self.set_target(n, value)
|
||||||
|
self.set_blocked(n, False)
|
||||||
|
n = vector3d(x, y, 1)
|
||||||
|
self.set_target(n, value)
|
||||||
|
self.set_blocked(n, False)
|
||||||
|
|
||||||
|
if side=="all" or side=="bottom":
|
||||||
|
y = self.ll.y
|
||||||
|
for x in range(self.ll.x, self.ur.x, 1):
|
||||||
|
n = vector3d(x, y, 0)
|
||||||
|
self.set_target(n, value)
|
||||||
|
self.set_blocked(n, False)
|
||||||
|
n = vector3d(x, y, 1)
|
||||||
|
self.set_target(n, value)
|
||||||
|
self.set_blocked(n, False)
|
||||||
|
|
||||||
|
if side=="all" or side=="top":
|
||||||
|
y = self.ur.y
|
||||||
|
for x in range(self.ll.x, self.ur.x, 1):
|
||||||
|
n = vector3d(x, y, 0)
|
||||||
|
self.set_target(n, value)
|
||||||
|
self.set_blocked(n, False)
|
||||||
|
n = vector3d(x, y, 1)
|
||||||
|
self.set_target(n, value)
|
||||||
|
self.set_blocked(n, False)
|
||||||
|
|
||||||
|
def is_target(self, point):
|
||||||
"""
|
"""
|
||||||
Point is in the target set, so we are done.
|
Point is in the target set, so we are done.
|
||||||
"""
|
"""
|
||||||
return point in self.target
|
return point in self.target
|
||||||
|
|
||||||
def add_map(self,n):
|
def add_map(self, n):
|
||||||
"""
|
"""
|
||||||
Add a point to the map if it doesn't exist.
|
Add a point to the map if it doesn't exist.
|
||||||
"""
|
"""
|
||||||
|
|
@ -132,8 +171,7 @@ class grid:
|
||||||
if n not in self.map:
|
if n not in self.map:
|
||||||
self.map[n]=grid_cell()
|
self.map[n]=grid_cell()
|
||||||
|
|
||||||
|
def block_path(self, path):
|
||||||
def block_path(self,path):
|
|
||||||
"""
|
"""
|
||||||
Mark the path in the routing grid as blocked.
|
Mark the path in the routing grid as blocked.
|
||||||
Also unsets the path flag.
|
Also unsets the path flag.
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,12 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
from itertools import tee
|
from itertools import tee
|
||||||
from grid import grid
|
from grid import grid
|
||||||
from grid_cell import grid_cell
|
|
||||||
from direction import direction
|
from direction import direction
|
||||||
|
|
||||||
|
|
||||||
class grid_path:
|
class grid_path:
|
||||||
"""
|
"""
|
||||||
A grid path is a list of lists of grid cells.
|
A grid path is a list of lists of grid cells.
|
||||||
|
|
@ -29,8 +28,7 @@ class grid_path:
|
||||||
self.pathlist = []
|
self.pathlist = []
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
#import pprint
|
p = str(self.pathlist)
|
||||||
p = str(self.pathlist) #pprint.pformat(self.pathlist)
|
|
||||||
if self.name != "":
|
if self.name != "":
|
||||||
return (str(self.name) + " : " + p)
|
return (str(self.name) + " : " + p)
|
||||||
return p
|
return p
|
||||||
|
|
@ -188,12 +186,10 @@ class grid_path:
|
||||||
|
|
||||||
return newwave
|
return newwave
|
||||||
|
|
||||||
|
|
||||||
def set_layer(self, zindex):
|
def set_layer(self, zindex):
|
||||||
new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave]
|
new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave]
|
||||||
self.pathlist = new_pathlist
|
self.pathlist = new_pathlist
|
||||||
|
|
||||||
|
|
||||||
def overlap(self, other):
|
def overlap(self, other):
|
||||||
"""
|
"""
|
||||||
Return the overlap waves ignoring different layers
|
Return the overlap waves ignoring different layers
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
Some utility functions for sets of grid cells.
|
Some utility functions for sets of grid cells.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import debug
|
|
||||||
import math
|
import math
|
||||||
from direction import direction
|
from direction import direction
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
|
|
@ -44,6 +43,7 @@ def get_upper_right(curset):
|
||||||
ur = p
|
ur = p
|
||||||
return ur
|
return ur
|
||||||
|
|
||||||
|
|
||||||
def get_lower_left(curset):
|
def get_lower_left(curset):
|
||||||
ll = None
|
ll = None
|
||||||
for p in curset:
|
for p in curset:
|
||||||
|
|
@ -51,7 +51,8 @@ def get_lower_left(curset):
|
||||||
ll = p
|
ll = p
|
||||||
return ll
|
return ll
|
||||||
|
|
||||||
def get_border( curset, direct):
|
|
||||||
|
def get_border(curset, direct):
|
||||||
"""
|
"""
|
||||||
Return the furthest cell(s) in a given direction.
|
Return the furthest cell(s) in a given direction.
|
||||||
"""
|
"""
|
||||||
|
|
@ -86,6 +87,7 @@ def get_border( curset, direct):
|
||||||
newset = set(maxc)
|
newset = set(maxc)
|
||||||
return newset
|
return newset
|
||||||
|
|
||||||
|
|
||||||
def expand_border(curset, direct):
|
def expand_border(curset, direct):
|
||||||
"""
|
"""
|
||||||
Expand the current set of sells in a given direction.
|
Expand the current set of sells in a given direction.
|
||||||
|
|
@ -95,6 +97,7 @@ def expand_border(curset, direct):
|
||||||
next_border_set = increment_set(border_set, direct)
|
next_border_set = increment_set(border_set, direct)
|
||||||
return next_border_set
|
return next_border_set
|
||||||
|
|
||||||
|
|
||||||
def expand_borders(curset):
|
def expand_borders(curset):
|
||||||
"""
|
"""
|
||||||
Return the expansions in planar directions.
|
Return the expansions in planar directions.
|
||||||
|
|
@ -106,6 +109,7 @@ def expand_borders(curset):
|
||||||
|
|
||||||
return(north_set, east_set, south_set, west_set)
|
return(north_set, east_set, south_set, west_set)
|
||||||
|
|
||||||
|
|
||||||
def inflate_cell(cell, distance):
|
def inflate_cell(cell, distance):
|
||||||
"""
|
"""
|
||||||
Expand the current cell in all directions and return the set.
|
Expand the current cell in all directions and return the set.
|
||||||
|
|
@ -122,6 +126,7 @@ def inflate_cell(cell, distance):
|
||||||
|
|
||||||
return newset
|
return newset
|
||||||
|
|
||||||
|
|
||||||
def inflate_set(curset, distance):
|
def inflate_set(curset, distance):
|
||||||
"""
|
"""
|
||||||
Expand the set in all directions by the given number of grids.
|
Expand the set in all directions by the given number of grids.
|
||||||
|
|
@ -136,6 +141,7 @@ def inflate_set(curset, distance):
|
||||||
# Recurse with less depth
|
# Recurse with less depth
|
||||||
return inflate_set(newset,distance-1)
|
return inflate_set(newset,distance-1)
|
||||||
|
|
||||||
|
|
||||||
def flatten_set(curset):
|
def flatten_set(curset):
|
||||||
"""
|
"""
|
||||||
Flatten until we have a set of vector3d objects.
|
Flatten until we have a set of vector3d objects.
|
||||||
|
|
@ -149,7 +155,6 @@ def flatten_set(curset):
|
||||||
return newset
|
return newset
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def distance_set(coord, curset):
|
def distance_set(coord, curset):
|
||||||
"""
|
"""
|
||||||
Return the distance from a coordinate to any item in the set
|
Return the distance from a coordinate to any item in the set
|
||||||
|
|
|
||||||
|
|
@ -493,7 +493,6 @@ class pin_group:
|
||||||
debug.error("Could not find a connector for {} with {}".format(self.pins,
|
debug.error("Could not find a connector for {} with {}".format(self.pins,
|
||||||
self.enclosures))
|
self.enclosures))
|
||||||
self.router.write_debug_gds("no_connector.gds")
|
self.router.write_debug_gds("no_connector.gds")
|
||||||
import pdb; pdb.set_trace()
|
|
||||||
self.enclosures.append(connector)
|
self.enclosures.append(connector)
|
||||||
|
|
||||||
# At this point, the pins are overlapping,
|
# At this point, the pins are overlapping,
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,14 @@ class router(router_tech):
|
||||||
route on a given layer. This is limited to two layer routes.
|
route on a given layer. This is limited to two layer routes.
|
||||||
It populates blockages on a grid class.
|
It populates blockages on a grid class.
|
||||||
"""
|
"""
|
||||||
def __init__(self, layers, design, gds_filename=None, rail_track_width=1):
|
def __init__(self, layers, design, gds_filename=None, route_track_width=1):
|
||||||
"""
|
"""
|
||||||
This will instantiate a copy of the gds file or the module at (0,0) and
|
This will instantiate a copy of the gds file or the module at (0,0) and
|
||||||
route on top of this. The blockages from the gds/module will be
|
route on top of this. The blockages from the gds/module will be
|
||||||
considered.
|
considered.
|
||||||
"""
|
"""
|
||||||
router_tech.__init__(self, layers, rail_track_width)
|
|
||||||
|
router_tech.__init__(self, layers, route_track_width)
|
||||||
|
|
||||||
self.cell = design
|
self.cell = design
|
||||||
|
|
||||||
|
|
@ -285,7 +286,7 @@ class router(router_tech):
|
||||||
If so, reduce the pin group grid to not include the adjacent grid.
|
If so, reduce the pin group grid to not include the adjacent grid.
|
||||||
Try to do this intelligently to keep th pins enclosed.
|
Try to do this intelligently to keep th pins enclosed.
|
||||||
"""
|
"""
|
||||||
debug.info(1,
|
debug.info(2,
|
||||||
"Comparing {0} and {1} adjacency".format(pin_name1,
|
"Comparing {0} and {1} adjacency".format(pin_name1,
|
||||||
pin_name2))
|
pin_name2))
|
||||||
removed_grids = 0
|
removed_grids = 0
|
||||||
|
|
@ -301,7 +302,7 @@ class router(router_tech):
|
||||||
adj_grids))
|
adj_grids))
|
||||||
self.remove_adjacent_grid(pg1, pg2, adj_grids)
|
self.remove_adjacent_grid(pg1, pg2, adj_grids)
|
||||||
|
|
||||||
debug.info(1, "Removed {} adjacent grids.".format(removed_grids))
|
debug.info(2, "Removed {} adjacent grids.".format(removed_grids))
|
||||||
|
|
||||||
def remove_adjacent_grid(self, pg1, pg2, adj_grids):
|
def remove_adjacent_grid(self, pg1, pg2, adj_grids):
|
||||||
"""
|
"""
|
||||||
|
|
@ -347,6 +348,10 @@ class router(router_tech):
|
||||||
smaller))
|
smaller))
|
||||||
smaller.grids.remove(adj)
|
smaller.grids.remove(adj)
|
||||||
|
|
||||||
|
def set_supply_rail_blocked(self, value):
|
||||||
|
# This is just a virtual function
|
||||||
|
pass
|
||||||
|
|
||||||
def prepare_blockages(self, pin_name):
|
def prepare_blockages(self, pin_name):
|
||||||
"""
|
"""
|
||||||
Reset and add all of the blockages in the design.
|
Reset and add all of the blockages in the design.
|
||||||
|
|
@ -362,7 +367,11 @@ class router(router_tech):
|
||||||
|
|
||||||
# Block all of the supply rails
|
# Block all of the supply rails
|
||||||
# (some will be unblocked if they're a target)
|
# (some will be unblocked if they're a target)
|
||||||
self.set_supply_rail_blocked(True)
|
try:
|
||||||
|
self.set_supply_rail_blocked(True)
|
||||||
|
except AttributeError:
|
||||||
|
# If function doesn't exist, it isn't a supply router
|
||||||
|
pass
|
||||||
|
|
||||||
# Block all of the pin components
|
# Block all of the pin components
|
||||||
# (some will be unblocked if they're a source/target)
|
# (some will be unblocked if they're a source/target)
|
||||||
|
|
@ -797,7 +806,7 @@ class router(router_tech):
|
||||||
"""
|
"""
|
||||||
Convert the pin groups into pin tracks and blockage tracks.
|
Convert the pin groups into pin tracks and blockage tracks.
|
||||||
"""
|
"""
|
||||||
debug.info(1, "Converting pins for {}.".format(pin_name))
|
debug.info(2, "Converting pins for {}.".format(pin_name))
|
||||||
for pg in self.pin_groups[pin_name]:
|
for pg in self.pin_groups[pin_name]:
|
||||||
pg.convert_pin()
|
pg.convert_pin()
|
||||||
|
|
||||||
|
|
@ -808,7 +817,7 @@ class router(router_tech):
|
||||||
that are blocked by other shapes.
|
that are blocked by other shapes.
|
||||||
"""
|
"""
|
||||||
for pin_name in self.pin_groups:
|
for pin_name in self.pin_groups:
|
||||||
debug.info(1, "Enclosing pins for {}".format(pin_name))
|
debug.info(2, "Enclosing pins for {}".format(pin_name))
|
||||||
for pg in self.pin_groups[pin_name]:
|
for pg in self.pin_groups[pin_name]:
|
||||||
pg.enclose_pin()
|
pg.enclose_pin()
|
||||||
pg.add_enclosure(self.cell)
|
pg.add_enclosure(self.cell)
|
||||||
|
|
@ -829,6 +838,12 @@ class router(router_tech):
|
||||||
for i in range(self.num_pin_components(pin_name)):
|
for i in range(self.num_pin_components(pin_name)):
|
||||||
self.add_pin_component_target(pin_name, i)
|
self.add_pin_component_target(pin_name, i)
|
||||||
|
|
||||||
|
def add_perimeter_target(self, side="all"):
|
||||||
|
"""
|
||||||
|
This will mark all the cells on the perimeter of the original layout as a target.
|
||||||
|
"""
|
||||||
|
self.rg.add_perimeter_target(side=side)
|
||||||
|
|
||||||
def num_pin_components(self, pin_name):
|
def num_pin_components(self, pin_name):
|
||||||
"""
|
"""
|
||||||
This returns how many disconnected pin components there are.
|
This returns how many disconnected pin components there are.
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -16,20 +16,20 @@ class router_tech:
|
||||||
"""
|
"""
|
||||||
This is a class to hold the router tech constants.
|
This is a class to hold the router tech constants.
|
||||||
"""
|
"""
|
||||||
def __init__(self, layers, rail_track_width):
|
def __init__(self, layers, route_track_width):
|
||||||
"""
|
"""
|
||||||
Allows us to change the layers that we are routing on.
|
Allows us to change the layers that we are routing on.
|
||||||
This uses the preferreed directions.
|
This uses the preferreed directions.
|
||||||
"""
|
"""
|
||||||
self.layers = layers
|
self.layers = layers
|
||||||
self.rail_track_width = rail_track_width
|
self.route_track_width = route_track_width
|
||||||
|
|
||||||
if len(self.layers) == 1:
|
if len(self.layers) == 1:
|
||||||
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
|
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
|
||||||
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
|
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
|
||||||
|
|
||||||
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
|
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
|
||||||
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
|
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
|
||||||
|
|
||||||
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
|
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
|
||||||
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
|
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
|
||||||
|
|
@ -55,13 +55,13 @@ class router_tech:
|
||||||
"preferred_directions '{}' and '{}'.")
|
"preferred_directions '{}' and '{}'.")
|
||||||
|
|
||||||
via_connect = contact(self.layers, (1, 1))
|
via_connect = contact(self.layers, (1, 1))
|
||||||
max_via_size = max(via_connect.width,via_connect.height)
|
max_via_size = max(via_connect.width, via_connect.height)
|
||||||
|
|
||||||
self.horiz_lpp = layer[self.horiz_layer_name]
|
self.horiz_lpp = layer[self.horiz_layer_name]
|
||||||
self.vert_lpp = layer[self.vert_layer_name]
|
self.vert_lpp = layer[self.vert_layer_name]
|
||||||
|
|
||||||
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
|
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
|
||||||
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
|
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
|
||||||
|
|
||||||
# For supplies, we will make the wire wider than the vias
|
# For supplies, we will make the wire wider than the vias
|
||||||
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
|
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
|
||||||
|
|
@ -71,16 +71,16 @@ class router_tech:
|
||||||
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
|
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
|
||||||
|
|
||||||
# We'll keep horizontal and vertical tracks the same for simplicity.
|
# We'll keep horizontal and vertical tracks the same for simplicity.
|
||||||
self.track_width = max(self.horiz_track_width,self.vert_track_width)
|
self.track_width = max(self.horiz_track_width, self.vert_track_width)
|
||||||
debug.info(1,"Track width: {:.3f}".format(self.track_width))
|
debug.info(1, "Minimum track width: {:.3f}".format(self.track_width))
|
||||||
self.track_space = max(self.horiz_layer_spacing,self.vert_layer_spacing)
|
self.track_space = max(self.horiz_layer_spacing, self.vert_layer_spacing)
|
||||||
debug.info(1,"Track space: {:.3f}".format(self.track_space))
|
debug.info(1, "Minimum track space: {:.3f}".format(self.track_space))
|
||||||
self.track_wire = self.track_width - self.track_space
|
self.track_wire = self.track_width - self.track_space
|
||||||
debug.info(1,"Track wire width: {:.3f}".format(self.track_wire))
|
debug.info(1, "Minimum track wire width: {:.3f}".format(self.track_wire))
|
||||||
|
|
||||||
self.track_widths = vector([self.track_width] * 2)
|
self.track_widths = vector([self.track_width] * 2)
|
||||||
self.track_factor = vector([1/self.track_width] * 2)
|
self.track_factor = vector([1/self.track_width] * 2)
|
||||||
debug.info(2,"Track factor: {}".format(self.track_factor))
|
debug.info(2, "Track factor: {}".format(self.track_factor))
|
||||||
|
|
||||||
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
|
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
|
||||||
self.layer_widths = [self.track_wire, 1, self.track_wire]
|
self.layer_widths = [self.track_wire, 1, self.track_wire]
|
||||||
|
|
@ -107,9 +107,9 @@ class router_tech:
|
||||||
elif zindex==0:
|
elif zindex==0:
|
||||||
return self.horiz_layer_name
|
return self.horiz_layer_name
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid zindex {}".format(zindex),-1)
|
debug.error("Invalid zindex {}".format(zindex), -1)
|
||||||
|
|
||||||
def get_supply_layer_width_space(self, zindex):
|
def get_layer_width_space(self, zindex):
|
||||||
"""
|
"""
|
||||||
These are the width and spacing of a supply layer given a supply rail
|
These are the width and spacing of a supply layer given a supply rail
|
||||||
of the given number of min wire widths.
|
of the given number of min wire widths.
|
||||||
|
|
@ -123,9 +123,9 @@ class router_tech:
|
||||||
|
|
||||||
min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf)
|
min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf)
|
||||||
|
|
||||||
min_width = drc("minwidth_{0}".format(layer_name), self.rail_track_width*min_wire_width, math.inf)
|
min_width = drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf)
|
||||||
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.rail_track_width*min_wire_width, math.inf)
|
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * min_wire_width, math.inf)
|
||||||
|
|
||||||
return (min_width,min_spacing)
|
return (min_width, min_spacing)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
||||||
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||||
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
import debug
|
||||||
|
from globals import print_time
|
||||||
|
from router import router
|
||||||
|
from datetime import datetime
|
||||||
|
from supply_grid import supply_grid
|
||||||
|
|
||||||
|
|
||||||
|
class signal_escape_router(router):
|
||||||
|
"""
|
||||||
|
A router that routes signals to perimeter and makes pins.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, layers, design, gds_filename=None):
|
||||||
|
"""
|
||||||
|
This will route on layers in design. It will get the blockages from
|
||||||
|
either the gds file name or the design itself (by saving to a gds file).
|
||||||
|
"""
|
||||||
|
router.__init__(self, layers, design, gds_filename, 1)
|
||||||
|
|
||||||
|
def create_routing_grid(self):
|
||||||
|
"""
|
||||||
|
Create a sprase routing grid with A* expansion functions.
|
||||||
|
"""
|
||||||
|
size = self.ur - self.ll
|
||||||
|
debug.info(1,"Size: {0} x {1}".format(size.x, size.y))
|
||||||
|
self.rg = supply_grid(self.ll, self.ur, self.track_width)
|
||||||
|
|
||||||
|
def escape_route(self, pin_list):
|
||||||
|
"""
|
||||||
|
Takes a list of tuples (name, side) and routes them. After routing,
|
||||||
|
it removes the old pin and places a new one on the perimeter.
|
||||||
|
"""
|
||||||
|
pin_names = [x[0] for x in pin_list]
|
||||||
|
|
||||||
|
# Clear the pins if we have previously routed
|
||||||
|
if (hasattr(self,'rg')):
|
||||||
|
self.clear_pins()
|
||||||
|
else:
|
||||||
|
self.create_routing_grid()
|
||||||
|
|
||||||
|
# Get the pin shapes
|
||||||
|
start_time = datetime.now()
|
||||||
|
self.find_pins_and_blockages(pin_names)
|
||||||
|
print_time("Finding pins and blockages",datetime.now(), start_time, 3)
|
||||||
|
|
||||||
|
# Route the supply pins to the supply rails
|
||||||
|
# Route vdd first since we want it to be shorter
|
||||||
|
start_time = datetime.now()
|
||||||
|
for pin_name, side in pin_list:
|
||||||
|
self.route_signal(pin_name, side)
|
||||||
|
|
||||||
|
print_time("Maze routing pins",datetime.now(), start_time, 3)
|
||||||
|
|
||||||
|
# self.write_debug_gds("final_escape_router.gds",False)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def route_signal(self, pin_name, side):
|
||||||
|
|
||||||
|
for detour_scale in [5 * pow(2, x) for x in range(5)]:
|
||||||
|
debug.info(1, "Exit routing {0} with scale {1}".format(pin_name, detour_scale))
|
||||||
|
|
||||||
|
# Clear everything in the routing grid.
|
||||||
|
self.rg.reinit()
|
||||||
|
|
||||||
|
# This is inefficient since it is non-incremental, but it was
|
||||||
|
# easier to debug.
|
||||||
|
self.prepare_blockages(pin_name)
|
||||||
|
|
||||||
|
# Add the single component of the pin as the source
|
||||||
|
# which unmarks it as a blockage too
|
||||||
|
self.add_source(pin_name)
|
||||||
|
|
||||||
|
# Marks the grid cells all along the perimeter as a target
|
||||||
|
self.add_perimeter_target(side)
|
||||||
|
|
||||||
|
# Actually run the A* router
|
||||||
|
if self.run_router(detour_scale=detour_scale):
|
||||||
|
new_pin = self.get_perimeter_pin()
|
||||||
|
self.cell.replace_layout_pin(pin_name, new_pin)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.write_debug_gds("debug_route.gds", True)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from itertools import tee
|
|
||||||
import debug
|
import debug
|
||||||
from heapq import heappush,heappop
|
from heapq import heappush,heappop
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
@ -14,6 +13,7 @@ from grid import grid
|
||||||
from grid_path import grid_path
|
from grid_path import grid_path
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
|
|
||||||
|
|
||||||
class signal_grid(grid):
|
class signal_grid(grid):
|
||||||
"""
|
"""
|
||||||
Expand the two layer grid to include A* search functions for a source and target.
|
Expand the two layer grid to include A* search functions for a source and target.
|
||||||
|
|
@ -34,11 +34,11 @@ class signal_grid(grid):
|
||||||
p.reset()
|
p.reset()
|
||||||
|
|
||||||
# clear source and target pins
|
# clear source and target pins
|
||||||
self.source=[]
|
self.source = set()
|
||||||
self.target=[]
|
self.target = set()
|
||||||
|
|
||||||
# Clear the queue
|
# Clear the queue
|
||||||
while len(self.q)>0:
|
while len(self.q) > 0:
|
||||||
heappop(self.q)
|
heappop(self.q)
|
||||||
self.counter = 0
|
self.counter = 0
|
||||||
|
|
||||||
|
|
@ -49,19 +49,16 @@ class signal_grid(grid):
|
||||||
We will use an A* search, so this cost must be pessimistic.
|
We will use an A* search, so this cost must be pessimistic.
|
||||||
Cost so far will be the length of the path.
|
Cost so far will be the length of the path.
|
||||||
"""
|
"""
|
||||||
#debug.info(3,"Initializing queue.")
|
|
||||||
|
|
||||||
# Counter is used to not require data comparison in Python 3.x
|
# Counter is used to not require data comparison in Python 3.x
|
||||||
# Items will be returned in order they are added during cost ties
|
# Items will be returned in order they are added during cost ties
|
||||||
self.counter = 0
|
self.counter = 0
|
||||||
for s in self.source:
|
for s in self.source:
|
||||||
cost = self.cost_to_target(s)
|
cost = self.cost_to_target(s)
|
||||||
debug.info(3,"Init: cost=" + str(cost) + " " + str([s]))
|
debug.info(3, "Init: cost=" + str(cost) + " " + str([s]))
|
||||||
heappush(self.q,(cost,self.counter,grid_path([vector3d(s)])))
|
heappush(self.q, (cost, self.counter, grid_path([vector3d(s)])))
|
||||||
self.counter+=1
|
self.counter+=1
|
||||||
|
|
||||||
|
def route(self, detour_scale):
|
||||||
def route(self,detour_scale):
|
|
||||||
"""
|
"""
|
||||||
This does the A* maze routing with preferred direction routing.
|
This does the A* maze routing with preferred direction routing.
|
||||||
This only works for 1 track wide routes!
|
This only works for 1 track wide routes!
|
||||||
|
|
@ -70,7 +67,7 @@ class signal_grid(grid):
|
||||||
# We set a cost bound of the HPWL for run-time. This can be
|
# We set a cost bound of the HPWL for run-time. This can be
|
||||||
# over-ridden if the route fails due to pruning a feasible solution.
|
# over-ridden if the route fails due to pruning a feasible solution.
|
||||||
any_source_element = next(iter(self.source))
|
any_source_element = next(iter(self.source))
|
||||||
cost_bound = detour_scale*self.cost_to_target(any_source_element)*grid.PREFERRED_COST
|
cost_bound = detour_scale * self.cost_to_target(any_source_element) * grid.PREFERRED_COST
|
||||||
|
|
||||||
# Check if something in the queue is already a source and a target!
|
# Check if something in the queue is already a source and a target!
|
||||||
for s in self.source:
|
for s in self.source:
|
||||||
|
|
@ -83,20 +80,17 @@ class signal_grid(grid):
|
||||||
|
|
||||||
# Put the source items into the queue
|
# Put the source items into the queue
|
||||||
self.init_queue()
|
self.init_queue()
|
||||||
cheapest_path = None
|
|
||||||
cheapest_cost = None
|
|
||||||
|
|
||||||
|
|
||||||
# Keep expanding and adding to the priority queue until we are done
|
# Keep expanding and adding to the priority queue until we are done
|
||||||
while len(self.q)>0:
|
while len(self.q)>0:
|
||||||
# should we keep the path in the queue as well or just the final node?
|
# should we keep the path in the queue as well or just the final node?
|
||||||
(cost,count,curpath) = heappop(self.q)
|
(cost, count, curpath) = heappop(self.q)
|
||||||
debug.info(3,"Queue size: size=" + str(len(self.q)) + " " + str(cost))
|
debug.info(3, "Queue size: size=" + str(len(self.q)) + " " + str(cost))
|
||||||
debug.info(4,"Expanding: cost=" + str(cost) + " " + str(curpath))
|
debug.info(4, "Expanding: cost=" + str(cost) + " " + str(curpath))
|
||||||
|
|
||||||
# expand the last element
|
# expand the last element
|
||||||
neighbors = self.expand_dirs(curpath)
|
neighbors = self.expand_dirs(curpath)
|
||||||
debug.info(4,"Neighbors: " + str(neighbors))
|
debug.info(4, "Neighbors: " + str(neighbors))
|
||||||
|
|
||||||
for n in neighbors:
|
for n in neighbors:
|
||||||
# make a new copy of the path to not update the old ones
|
# make a new copy of the path to not update the old ones
|
||||||
|
|
@ -105,7 +99,7 @@ class signal_grid(grid):
|
||||||
newpath.append(n)
|
newpath.append(n)
|
||||||
# check if we hit the target and are done
|
# check if we hit the target and are done
|
||||||
if self.is_target(n[0]): # This uses the [0] item because we are assuming 1-track wide
|
if self.is_target(n[0]): # This uses the [0] item because we are assuming 1-track wide
|
||||||
return (newpath,newpath.cost())
|
return (newpath, newpath.cost())
|
||||||
else:
|
else:
|
||||||
# current path cost + predicted cost
|
# current path cost + predicted cost
|
||||||
current_cost = newpath.cost()
|
current_cost = newpath.cost()
|
||||||
|
|
@ -116,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.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,10 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import gdsMill
|
|
||||||
import tech
|
|
||||||
from contact import contact
|
|
||||||
import math
|
|
||||||
import debug
|
import debug
|
||||||
from pin_layout import pin_layout
|
|
||||||
from globals import OPTS
|
|
||||||
from router import router
|
from router import router
|
||||||
|
|
||||||
|
|
||||||
class signal_router(router):
|
class signal_router(router):
|
||||||
"""
|
"""
|
||||||
A router class to read an obstruction map from a gds and plan a
|
A router class to read an obstruction map from a gds and plan a
|
||||||
|
|
@ -27,7 +22,6 @@ class signal_router(router):
|
||||||
"""
|
"""
|
||||||
router.__init__(self, layers, design, gds_filename)
|
router.__init__(self, layers, design, gds_filename)
|
||||||
|
|
||||||
|
|
||||||
def create_routing_grid(self):
|
def create_routing_grid(self):
|
||||||
"""
|
"""
|
||||||
Create a sprase routing grid with A* expansion functions.
|
Create a sprase routing grid with A* expansion functions.
|
||||||
|
|
@ -35,11 +29,10 @@ class signal_router(router):
|
||||||
# We will add a halo around the boundary
|
# We will add a halo around the boundary
|
||||||
# of this many tracks
|
# of this many tracks
|
||||||
size = self.ur - self.ll
|
size = self.ur - self.ll
|
||||||
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
|
debug.info(1, "Size: {0} x {1}".format(size.x, size.y))
|
||||||
|
|
||||||
import signal_grid
|
import signal_grid
|
||||||
self.rg = signal_grid.signal_grid(self.ll, self.ur, self.track_width)
|
self.rg = signal_grid.signal_grid(self.ll, self.ur, self.route_track_width)
|
||||||
|
|
||||||
|
|
||||||
def route(self, src, dest, detour_scale=5):
|
def route(self, src, dest, detour_scale=5):
|
||||||
"""
|
"""
|
||||||
|
|
@ -47,13 +40,13 @@ class signal_router(router):
|
||||||
the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route.
|
the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route.
|
||||||
This is used to speed up the routing when there is not much detouring needed.
|
This is used to speed up the routing when there is not much detouring needed.
|
||||||
"""
|
"""
|
||||||
debug.info(1,"Running signal router from {0} to {1}...".format(src,dest))
|
debug.info(1, "Running signal router from {0} to {1}...".format(src, dest))
|
||||||
|
|
||||||
self.pins[src] = []
|
self.pins[src] = []
|
||||||
self.pins[dest] = []
|
self.pins[dest] = []
|
||||||
|
|
||||||
# Clear the pins if we have previously routed
|
# Clear the pins if we have previously routed
|
||||||
if (hasattr(self,'rg')):
|
if (hasattr(self, 'rg')):
|
||||||
self.clear_pins()
|
self.clear_pins()
|
||||||
else:
|
else:
|
||||||
# Creat a routing grid over the entire area
|
# Creat a routing grid over the entire area
|
||||||
|
|
@ -67,8 +60,8 @@ class signal_router(router):
|
||||||
# Block everything
|
# Block everything
|
||||||
self.prepare_blockages()
|
self.prepare_blockages()
|
||||||
# Clear the pins we are routing
|
# Clear the pins we are routing
|
||||||
self.set_blockages(self.pin_components[src],False)
|
self.set_blockages(self.pin_components[src], False)
|
||||||
self.set_blockages(self.pin_components[dest],False)
|
self.set_blockages(self.pin_components[dest], False)
|
||||||
|
|
||||||
# Now add the src/tgt if they are not blocked by other shapes
|
# Now add the src/tgt if they are not blocked by other shapes
|
||||||
self.add_source(src)
|
self.add_source(src)
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,8 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
|
||||||
from vector3d import vector3d
|
|
||||||
from grid import grid
|
|
||||||
from signal_grid import signal_grid
|
from signal_grid import signal_grid
|
||||||
from grid_path import grid_path
|
from grid_path import grid_path
|
||||||
from direction import direction
|
|
||||||
|
|
||||||
|
|
||||||
class supply_grid(signal_grid):
|
class supply_grid(signal_grid):
|
||||||
|
|
@ -27,13 +23,14 @@ class supply_grid(signal_grid):
|
||||||
|
|
||||||
def reinit(self):
|
def reinit(self):
|
||||||
""" Reinitialize everything for a new route. """
|
""" Reinitialize everything for a new route. """
|
||||||
|
|
||||||
self.source = set()
|
self.source = set()
|
||||||
self.target = set()
|
self.target = set()
|
||||||
|
|
||||||
# Reset all the cells in the map
|
# Reset all the cells in the map
|
||||||
for p in self.map.values():
|
for p in self.map.values():
|
||||||
p.reset()
|
p.reset()
|
||||||
|
|
||||||
|
|
||||||
def find_start_wave(self, wave, direct):
|
def find_start_wave(self, wave, direct):
|
||||||
"""
|
"""
|
||||||
Finds the first loc starting at loc and up that is open.
|
Finds the first loc starting at loc and up that is open.
|
||||||
|
|
@ -46,8 +43,8 @@ class supply_grid(signal_grid):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
while wave and self.is_wave_blocked(wave):
|
while wave and self.is_wave_blocked(wave):
|
||||||
wf=grid_path(wave)
|
wf = grid_path(wave)
|
||||||
wave=wf.neighbor(direct)
|
wave = wf.neighbor(direct)
|
||||||
# Bail out if we couldn't increment futher
|
# Bail out if we couldn't increment futher
|
||||||
if wave[0].x > self.ur.x or wave[-1].y > self.ur.y:
|
if wave[0].x > self.ur.x or wave[-1].y > self.ur.y:
|
||||||
return None
|
return None
|
||||||
|
|
@ -57,7 +54,6 @@ class supply_grid(signal_grid):
|
||||||
|
|
||||||
return wave
|
return wave
|
||||||
|
|
||||||
|
|
||||||
def is_wave_blocked(self, wave):
|
def is_wave_blocked(self, wave):
|
||||||
"""
|
"""
|
||||||
Checks if any of the locations are blocked
|
Checks if any of the locations are blocked
|
||||||
|
|
@ -68,7 +64,6 @@ class supply_grid(signal_grid):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def probe(self, wave, direct):
|
def probe(self, wave, direct):
|
||||||
"""
|
"""
|
||||||
Expand the wave until there is a blockage and return
|
Expand the wave until there is a blockage and return
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@ class supply_grid_router(router):
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
|
|
||||||
# Power rail width in minimum wire widths
|
# Power rail width in minimum wire widths
|
||||||
self.rail_track_width = 3
|
self.route_track_width = 2
|
||||||
|
|
||||||
router.__init__(self, layers, design, gds_filename, self.rail_track_width)
|
router.__init__(self, layers, design, gds_filename, self.route_track_width)
|
||||||
|
|
||||||
# The list of supply rails (grid sets) that may be routed
|
# The list of supply rails (grid sets) that may be routed
|
||||||
self.supply_rails = {}
|
self.supply_rails = {}
|
||||||
|
|
@ -47,7 +47,7 @@ class supply_grid_router(router):
|
||||||
debug.info(1, "Size: {0} x {1}".format(size.x, size.y))
|
debug.info(1, "Size: {0} x {1}".format(size.x, size.y))
|
||||||
|
|
||||||
import supply_grid
|
import supply_grid
|
||||||
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
|
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.route_track_width)
|
||||||
|
|
||||||
def route(self, vdd_name="vdd", gnd_name="gnd"):
|
def route(self, vdd_name="vdd", gnd_name="gnd"):
|
||||||
"""
|
"""
|
||||||
|
|
@ -74,6 +74,7 @@ class supply_grid_router(router):
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
# Block everything
|
# Block everything
|
||||||
self.prepare_blockages(self.gnd_name)
|
self.prepare_blockages(self.gnd_name)
|
||||||
|
|
||||||
# Determine the rail locations
|
# Determine the rail locations
|
||||||
self.route_supply_rails(self.gnd_name, 0)
|
self.route_supply_rails(self.gnd_name, 0)
|
||||||
|
|
||||||
|
|
@ -104,14 +105,6 @@ class supply_grid_router(router):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check_all_routed(self, pin_name):
|
|
||||||
"""
|
|
||||||
Check that all pin groups are routed.
|
|
||||||
"""
|
|
||||||
for pg in self.pin_groups[pin_name]:
|
|
||||||
if not pg.is_routed():
|
|
||||||
return False
|
|
||||||
|
|
||||||
def route_simple_overlaps(self, pin_name):
|
def route_simple_overlaps(self, pin_name):
|
||||||
"""
|
"""
|
||||||
This checks for simple cases where a pin component already overlaps a supply rail.
|
This checks for simple cases where a pin component already overlaps a supply rail.
|
||||||
|
|
@ -317,7 +310,7 @@ class supply_grid_router(router):
|
||||||
data structure. Return whether it was added or not.
|
data structure. Return whether it was added or not.
|
||||||
"""
|
"""
|
||||||
# We must have at least 2 tracks to drop plus 2 tracks for a via
|
# We must have at least 2 tracks to drop plus 2 tracks for a via
|
||||||
if len(wave_path) >= 4 * self.rail_track_width:
|
if len(wave_path) >= 4 * self.route_track_width:
|
||||||
grid_set = wave_path.get_grids()
|
grid_set = wave_path.get_grids()
|
||||||
self.supply_rails[name].append(grid_set)
|
self.supply_rails[name].append(grid_set)
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
|
|
@ -1,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)
|
||||||
|
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
"Run a regresion test the library cells for DRC"
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../.."))
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
import debug
|
|
||||||
|
|
||||||
OPTS = globals.OPTS
|
|
||||||
|
|
||||||
class no_blockages_test(openram_test):
|
|
||||||
"""
|
|
||||||
Simplest two pin route test with no blockages.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
|
||||||
from gds_cell import gds_cell
|
|
||||||
from design import design
|
|
||||||
from signal_router import signal_router as router
|
|
||||||
|
|
||||||
class routing(design, openram_test):
|
|
||||||
"""
|
|
||||||
A generic GDS design that we can route on.
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
design.__init__(self, "top")
|
|
||||||
|
|
||||||
# Instantiate a GDS cell with the design
|
|
||||||
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
|
|
||||||
cell = gds_cell(name, gds_file)
|
|
||||||
self.add_inst(name=name,
|
|
||||||
mod=cell,
|
|
||||||
offset=[0,0])
|
|
||||||
self.connect_inst([])
|
|
||||||
|
|
||||||
layer_stack =("metal1","via1","metal2")
|
|
||||||
r=router(layer_stack,self,gds_file)
|
|
||||||
self.assertTrue(r.route(src="A",dest="B"))
|
|
||||||
|
|
||||||
r=routing("01_no_blockages_test_{0}".format(OPTS.tech_name))
|
|
||||||
self.local_drc_check(r)
|
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
# instantiate a copy of the class to actually run the test
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,66 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
"Run a regresion test the library cells for DRC"
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../.."))
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
import debug
|
|
||||||
|
|
||||||
OPTS = globals.OPTS
|
|
||||||
|
|
||||||
class blockages_test(openram_test):
|
|
||||||
"""
|
|
||||||
Simple two pin route test with multilayer blockages.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
|
||||||
from gds_cell import gds_cell
|
|
||||||
from design import design
|
|
||||||
from signal_router import signal_router as router
|
|
||||||
|
|
||||||
class routing(design, openram_test):
|
|
||||||
"""
|
|
||||||
A generic GDS design that we can route on.
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
design.__init__(self, "top")
|
|
||||||
|
|
||||||
# Instantiate a GDS cell with the design
|
|
||||||
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
|
|
||||||
cell = gds_cell(name, gds_file)
|
|
||||||
self.add_inst(name=name,
|
|
||||||
mod=cell,
|
|
||||||
offset=[0,0])
|
|
||||||
self.connect_inst([])
|
|
||||||
|
|
||||||
layer_stack =("metal1","via1","metal2")
|
|
||||||
r=router(layer_stack,self,gds_file)
|
|
||||||
self.assertTrue(r.route(src="A",dest="B"))
|
|
||||||
|
|
||||||
r=routing("02_blockages_test_{0}".format(OPTS.tech_name))
|
|
||||||
self.local_drc_check(r)
|
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# instantiate a copy of the class to actually run the test
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,61 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
"Run a regresion test the library cells for DRC"
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../.."))
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
import debug
|
|
||||||
|
|
||||||
OPTS = globals.OPTS
|
|
||||||
|
|
||||||
class same_layer_pins_test(openram_test):
|
|
||||||
"""
|
|
||||||
Checks two pins on the same layer with positive and negative coordinates.
|
|
||||||
"""
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
|
||||||
from gds_cell import gds_cell
|
|
||||||
from design import design
|
|
||||||
from signal_router import signal_router as router
|
|
||||||
|
|
||||||
class routing(design, openram_test):
|
|
||||||
"""
|
|
||||||
A generic GDS design that we can route on.
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
design.__init__(self, "top")
|
|
||||||
|
|
||||||
# Instantiate a GDS cell with the design
|
|
||||||
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
|
|
||||||
cell = gds_cell(name, gds_file)
|
|
||||||
self.add_inst(name=name,
|
|
||||||
mod=cell,
|
|
||||||
offset=[0,0])
|
|
||||||
self.connect_inst([])
|
|
||||||
|
|
||||||
layer_stack =("metal1","via1","metal2")
|
|
||||||
r=router(layer_stack,self,gds_file)
|
|
||||||
self.assertTrue(r.route(src="A",dest="B"))
|
|
||||||
|
|
||||||
r = routing("03_same_layer_pins_test_{0}".format(OPTS.tech_name))
|
|
||||||
self.local_drc_check(r)
|
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
# instantiate a copy of the class to actually run the test
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,63 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
"Run a regresion test the library cells for DRC"
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../.."))
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
import debug
|
|
||||||
|
|
||||||
OPTS = globals.OPTS
|
|
||||||
|
|
||||||
class diff_layer_pins_test(openram_test):
|
|
||||||
"""
|
|
||||||
Two pin route test with pins on different layers and blockages.
|
|
||||||
Pins are smaller than grid size.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
|
||||||
from gds_cell import gds_cell
|
|
||||||
from design import design
|
|
||||||
from signal_router import signal_router as router
|
|
||||||
|
|
||||||
class routing(design, openram_test):
|
|
||||||
"""
|
|
||||||
A generic GDS design that we can route on.
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
design.__init__(self, "top")
|
|
||||||
|
|
||||||
# Instantiate a GDS cell with the design
|
|
||||||
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
|
|
||||||
cell = gds_cell(name, gds_file)
|
|
||||||
self.add_inst(name=name,
|
|
||||||
mod=cell,
|
|
||||||
offset=[0,0])
|
|
||||||
self.connect_inst([])
|
|
||||||
|
|
||||||
layer_stack =("metal1","via1","metal2")
|
|
||||||
r=router(layer_stack,self,gds_file)
|
|
||||||
self.assertTrue(r.route(src="A",dest="B"))
|
|
||||||
|
|
||||||
r = routing("04_diff_layer_pins_test_{0}".format(OPTS.tech_name))
|
|
||||||
self.local_drc_check(r)
|
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
# instantiate a copy of the class to actually run the test
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,65 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
"Run a regresion test the library cells for DRC"
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../.."))
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
import debug
|
|
||||||
|
|
||||||
OPTS = globals.OPTS
|
|
||||||
|
|
||||||
class two_nets_test(openram_test):
|
|
||||||
"""
|
|
||||||
Route two nets in the same GDS file. The routes will interact,
|
|
||||||
so they must block eachother.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
|
||||||
from gds_cell import gds_cell
|
|
||||||
from design import design
|
|
||||||
from signal_router import signal_router as router
|
|
||||||
|
|
||||||
class routing(design, openram_test):
|
|
||||||
"""
|
|
||||||
A generic GDS design that we can route on.
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
design.__init__(self, "top")
|
|
||||||
|
|
||||||
# Instantiate a GDS cell with the design
|
|
||||||
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
|
|
||||||
cell = gds_cell(name, gds_file)
|
|
||||||
self.add_inst(name=name,
|
|
||||||
mod=cell,
|
|
||||||
offset=[0,0])
|
|
||||||
self.connect_inst([])
|
|
||||||
|
|
||||||
layer_stack =("metal1","via1","metal2")
|
|
||||||
r=router(layer_stack,self,gds_file)
|
|
||||||
self.assertTrue(r.route(src="A",dest="B"))
|
|
||||||
self.assertTrue(r.route(src="C",dest="D"))
|
|
||||||
|
|
||||||
r = routing("05_two_nets_test_{0}".format(OPTS.tech_name))
|
|
||||||
self.local_drc_check(r)
|
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
|
|
||||||
# instantiate a copy of the class to actually run the test
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
.SUBCKT cell
|
|
||||||
.ENDS cell
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,72 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
"Run a regresion test the library cells for DRC"
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../.."))
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
import debug
|
|
||||||
|
|
||||||
OPTS = globals.OPTS
|
|
||||||
|
|
||||||
class pin_location_test(openram_test):
|
|
||||||
"""
|
|
||||||
Simplest two pin route test with no blockages using the pin locations instead of labels.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
|
||||||
from gds_cell import gds_cell
|
|
||||||
from design import design
|
|
||||||
from signal_router import signal_router as router
|
|
||||||
|
|
||||||
class routing(design, openram_test):
|
|
||||||
"""
|
|
||||||
A generic GDS design that we can route on.
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
design.__init__(self, "top")
|
|
||||||
|
|
||||||
# Instantiate a GDS cell with the design
|
|
||||||
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
|
|
||||||
cell = gds_cell(name, gds_file)
|
|
||||||
self.add_inst(name=name,
|
|
||||||
mod=cell,
|
|
||||||
offset=[0,0])
|
|
||||||
self.connect_inst([])
|
|
||||||
|
|
||||||
layer_stack =("metal1","via1","metal2")
|
|
||||||
r=router(layer_stack,self,gds_file)
|
|
||||||
# these are user coordinates and layers
|
|
||||||
src_pin = [[0.52, 4.099],11]
|
|
||||||
tgt_pin = [[3.533, 1.087],11]
|
|
||||||
#r.route(layer_stack,src="A",dest="B")
|
|
||||||
self.assertTrue(r.route(src=src_pin,dest=tgt_pin))
|
|
||||||
|
|
||||||
# This only works for freepdk45 since the coordinates are hard coded
|
|
||||||
if OPTS.tech_name == "freepdk45":
|
|
||||||
r = routing("06_pin_location_test_{0}".format(OPTS.tech_name))
|
|
||||||
self.local_drc_check(r)
|
|
||||||
else:
|
|
||||||
debug.warning("This test does not support technology {0}".format(OPTS.tech_name))
|
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# instantiate a copy of the class to actually run the test
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,90 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
"Run a regresion test the library cells for DRC"
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../.."))
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
import debug
|
|
||||||
|
|
||||||
OPTS = globals.OPTS
|
|
||||||
|
|
||||||
class big_test(openram_test):
|
|
||||||
"""
|
|
||||||
Simplest two pin route test with no blockages using the pin locations instead of labels.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
|
||||||
from gds_cell import gds_cell
|
|
||||||
from design import design
|
|
||||||
from signal_router import signal_router as router
|
|
||||||
|
|
||||||
class routing(design, openram_test):
|
|
||||||
"""
|
|
||||||
A generic GDS design that we can route on.
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
design.__init__(self, "top")
|
|
||||||
|
|
||||||
# Instantiate a GDS cell with the design
|
|
||||||
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
|
|
||||||
cell = gds_cell(name, gds_file)
|
|
||||||
self.add_inst(name=name,
|
|
||||||
mod=cell,
|
|
||||||
offset=[0,0])
|
|
||||||
self.connect_inst([])
|
|
||||||
|
|
||||||
layer_stack =("metal1","via1","metal2")
|
|
||||||
r=router(layer_stack,self,gds_file)
|
|
||||||
connections=[('out_0_2', 'a_0_0'),
|
|
||||||
('out_0_3', 'b_0_0'),
|
|
||||||
('out_0_0', 'a_0_1'),
|
|
||||||
('out_1_2', 'a_1_0'),
|
|
||||||
('out_1_3', 'b_1_0'),
|
|
||||||
('out_1_0', 'a_1_1'),
|
|
||||||
('out_2_1', 'a_2_0'),
|
|
||||||
('out_2_2', 'b_2_0'),
|
|
||||||
('out_3_1', 'a_3_0'),
|
|
||||||
('out_3_2', 'b_3_0'),
|
|
||||||
('out_4_6', 'a_4_0'),
|
|
||||||
('out_4_7', 'b_4_0'),
|
|
||||||
('out_4_8', 'a_4_2'),
|
|
||||||
('out_4_9', 'b_4_2'),
|
|
||||||
('out_4_10', 'a_4_4'),
|
|
||||||
('out_4_11', 'b_4_4'),
|
|
||||||
('out_4_0', 'a_4_1'),
|
|
||||||
('out_4_2', 'b_4_1'),
|
|
||||||
('out_4_4', 'a_4_5'),
|
|
||||||
('out_4_1', 'a_4_3'),
|
|
||||||
('out_4_5', 'b_4_3')]
|
|
||||||
for (src,tgt) in connections:
|
|
||||||
self.assertTrue(r.route(src=src,dest=tgt))
|
|
||||||
|
|
||||||
# This test only runs on scn3me_subm tech
|
|
||||||
if OPTS.tech_name=="scn3me_subm":
|
|
||||||
r = routing("07_big_test_{0}".format(OPTS.tech_name))
|
|
||||||
self.local_drc_check(r)
|
|
||||||
else:
|
|
||||||
debug.warning("This test does not support technology {0}".format(OPTS.tech_name))
|
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# instantiate a copy of the class to actually run the test
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
Binary file not shown.
|
|
@ -1,66 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
"Run a regresion test the library cells for DRC"
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../.."))
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
import debug
|
|
||||||
|
|
||||||
OPTS = globals.OPTS
|
|
||||||
|
|
||||||
class expand_region_test(openram_test):
|
|
||||||
"""
|
|
||||||
Test an infeasible route followed by a feasible route with an expanded region.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
|
||||||
from gds_cell import gds_cell
|
|
||||||
from design import design
|
|
||||||
from signal_router import signal_router as router
|
|
||||||
|
|
||||||
class routing(design, openram_test):
|
|
||||||
"""
|
|
||||||
A generic GDS design that we can route on.
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
design.__init__(self, "top")
|
|
||||||
|
|
||||||
# Instantiate a GDS cell with the design
|
|
||||||
gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name)
|
|
||||||
cell = gds_cell(name, gds_file)
|
|
||||||
self.add_inst(name=name,
|
|
||||||
mod=cell,
|
|
||||||
offset=[0,0])
|
|
||||||
self.connect_inst([])
|
|
||||||
|
|
||||||
layer_stack =("metal1","via1","metal2")
|
|
||||||
r=router(layer_stack,self,gds_file)
|
|
||||||
# This should be infeasible because it is blocked without a detour.
|
|
||||||
self.assertFalse(r.route(src="A",dest="B",detour_scale=1))
|
|
||||||
# This should be feasible because we allow it to detour
|
|
||||||
self.assertTrue(r.route(src="A",dest="B",detour_scale=3))
|
|
||||||
|
|
||||||
r = routing("08_expand_region_test_{0}".format(OPTS.tech_name))
|
|
||||||
self.local_drc_check(r)
|
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
|
|
||||||
# instantiate a copy of the class to actually run the test
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,56 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
"Run a regresion test the library cells for DRC"
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
import debug
|
|
||||||
|
|
||||||
OPTS = globals.OPTS
|
|
||||||
|
|
||||||
class no_blockages_test(openram_test):
|
|
||||||
"""
|
|
||||||
Simplest two pin route test with no blockages.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
|
||||||
from supply_router import supply_router as router
|
|
||||||
|
|
||||||
if False:
|
|
||||||
from control_logic import control_logic
|
|
||||||
cell = control_logic(16)
|
|
||||||
layer_stack =("metal3","via3","metal4")
|
|
||||||
rtr=router(layer_stack, cell)
|
|
||||||
self.assertTrue(rtr.route())
|
|
||||||
else:
|
|
||||||
from sram import sram
|
|
||||||
from sram_config import sram_config
|
|
||||||
c = sram_config(word_size=4,
|
|
||||||
num_words=32,
|
|
||||||
num_banks=1)
|
|
||||||
|
|
||||||
c.words_per_row=1
|
|
||||||
sram = sram(c, "sram1")
|
|
||||||
cell = sram.s
|
|
||||||
|
|
||||||
self.local_check(cell,True)
|
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
# instantiate a copy of the class to actually run the test
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
word_size = 1
|
|
||||||
num_words = 16
|
|
||||||
|
|
||||||
tech_name = "freepdk45"
|
|
||||||
process_corners = ["TT"]
|
|
||||||
supply_voltages = [1.0]
|
|
||||||
temperatures = [25]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
word_size = 1
|
|
||||||
num_words = 16
|
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
|
||||||
process_corners = ["TT"]
|
|
||||||
supply_voltages = [5.0]
|
|
||||||
temperatures = [25]
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
from design import design
|
|
||||||
class gds_cell(design):
|
|
||||||
"""
|
|
||||||
A generic GDS design.
|
|
||||||
"""
|
|
||||||
def __init__(self, name, gds_file):
|
|
||||||
self.name = name
|
|
||||||
self.gds_file = gds_file
|
|
||||||
self.sp_file = None
|
|
||||||
|
|
||||||
design.__init__(self, name)
|
|
||||||
|
|
||||||
# The dimensions will not be defined, so do this...
|
|
||||||
self.width=0
|
|
||||||
self.height=0
|
|
||||||
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import re
|
|
||||||
import unittest
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../../compiler"))
|
|
||||||
import globals
|
|
||||||
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
|
|
||||||
from testutils import header,openram_test
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
|
|
||||||
# get a list of all files in the tests directory
|
|
||||||
files = os.listdir(sys.path[0])
|
|
||||||
|
|
||||||
# assume any file that ends in "test.py" in it is a regression test
|
|
||||||
nametest = re.compile("test\.py$", re.IGNORECASE)
|
|
||||||
tests = list(filter(nametest.search, files))
|
|
||||||
tests.sort()
|
|
||||||
|
|
||||||
# import all of the modules
|
|
||||||
filenameToModuleName = lambda f: os.path.splitext(f)[0]
|
|
||||||
moduleNames = map(filenameToModuleName, tests)
|
|
||||||
modules = map(__import__, moduleNames)
|
|
||||||
suite = unittest.TestSuite()
|
|
||||||
load = unittest.defaultTestLoader.loadTestsFromModule
|
|
||||||
suite.addTests(map(load, modules))
|
|
||||||
|
|
||||||
test_runner = unittest.TextTestRunner(verbosity=2,stream=sys.stderr)
|
|
||||||
test_result = test_runner.run(suite)
|
|
||||||
|
|
||||||
import verify
|
|
||||||
verify.print_drc_stats()
|
|
||||||
verify.print_lvs_stats()
|
|
||||||
verify.print_pex_stats()
|
|
||||||
|
|
||||||
sys.exit(not test_result.wasSuccessful())
|
|
||||||
|
|
@ -1,265 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
import unittest,warnings
|
|
||||||
import sys,os,glob,copy
|
|
||||||
sys.path.append(os.path.join(sys.path[0],"../.."))
|
|
||||||
from globals import OPTS
|
|
||||||
import debug
|
|
||||||
|
|
||||||
class openram_test(unittest.TestCase):
|
|
||||||
""" Base unit test that we have some shared classes in. """
|
|
||||||
|
|
||||||
def local_drc_check(self, w):
|
|
||||||
|
|
||||||
self.reset()
|
|
||||||
|
|
||||||
tempgds = OPTS.openram_temp + "temp.gds"
|
|
||||||
w.gds_write(tempgds)
|
|
||||||
import verify
|
|
||||||
|
|
||||||
result=verify.run_drc(w.name, tempgds)
|
|
||||||
if result != 0:
|
|
||||||
self.fail("DRC failed: {}".format(w.name))
|
|
||||||
|
|
||||||
if not OPTS.keep_temp:
|
|
||||||
self.cleanup()
|
|
||||||
|
|
||||||
def local_check(self, a, final_verification=False):
|
|
||||||
|
|
||||||
self.reset()
|
|
||||||
|
|
||||||
tempspice = OPTS.openram_temp + "temp.sp"
|
|
||||||
tempgds = OPTS.openram_temp + "temp.gds"
|
|
||||||
|
|
||||||
a.sp_write(tempspice)
|
|
||||||
a.gds_write(tempgds)
|
|
||||||
|
|
||||||
import verify
|
|
||||||
result=verify.run_drc(a.name, tempgds)
|
|
||||||
if result != 0:
|
|
||||||
self.fail("DRC failed: {}".format(a.name))
|
|
||||||
|
|
||||||
|
|
||||||
result=verify.run_lvs(a.name, tempgds, tempspice, final_verification)
|
|
||||||
if result != 0:
|
|
||||||
self.fail("LVS mismatch: {}".format(a.name))
|
|
||||||
|
|
||||||
if not OPTS.keep_temp:
|
|
||||||
self.cleanup()
|
|
||||||
|
|
||||||
def cleanup(self):
|
|
||||||
""" Reset the duplicate checker and cleanup files. """
|
|
||||||
files = glob.glob(OPTS.openram_temp + '*')
|
|
||||||
for f in files:
|
|
||||||
# Only remove the files
|
|
||||||
if os.path.isfile(f):
|
|
||||||
os.remove(f)
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
"""
|
|
||||||
Reset everything after each test.
|
|
||||||
"""
|
|
||||||
# Reset the static duplicate name checker for unit tests.
|
|
||||||
import hierarchy_design
|
|
||||||
hierarchy_design.hierarchy_design.name_map=[]
|
|
||||||
|
|
||||||
def check_golden_data(self, data, golden_data, error_tolerance=1e-2):
|
|
||||||
"""
|
|
||||||
This function goes through two dictionaries, key by key and compares
|
|
||||||
each item. It uses relative comparisons for the items and returns false
|
|
||||||
if there is a mismatch.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Check each result
|
|
||||||
data_matches = True
|
|
||||||
for k in data.keys():
|
|
||||||
if type(data[k])==list:
|
|
||||||
for i in range(len(data[k])):
|
|
||||||
if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance):
|
|
||||||
data_matches = False
|
|
||||||
else:
|
|
||||||
self.isclose(k,data[k],golden_data[k],error_tolerance)
|
|
||||||
if not data_matches:
|
|
||||||
import pprint
|
|
||||||
data_string=pprint.pformat(data)
|
|
||||||
debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance*100)+data_string)
|
|
||||||
return data_matches
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def isclose(self,key,value,actual_value,error_tolerance=1e-2):
|
|
||||||
""" This is used to compare relative values. """
|
|
||||||
import debug
|
|
||||||
relative_diff = self.relative_diff(value,actual_value)
|
|
||||||
check = relative_diff <= error_tolerance
|
|
||||||
if check:
|
|
||||||
debug.info(2,"CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100))
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
debug.error("NOT CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100))
|
|
||||||
return False
|
|
||||||
|
|
||||||
def relative_diff(self, value1, value2):
|
|
||||||
""" Compute the relative difference of two values and normalize to the largest.
|
|
||||||
If largest value is 0, just return the difference."""
|
|
||||||
|
|
||||||
# Edge case to avoid divide by zero
|
|
||||||
if value1==0 and value2==0:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
# Don't need relative, exact compare
|
|
||||||
if value1==value2:
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
# Get normalization value
|
|
||||||
norm_value = abs(max(value1, value2))
|
|
||||||
# Edge case where greater is a zero
|
|
||||||
if norm_value == 0:
|
|
||||||
min_value = abs(min(value1, value2))
|
|
||||||
|
|
||||||
return abs(value1 - value2) / norm_value
|
|
||||||
|
|
||||||
|
|
||||||
def relative_compare(self, value,actual_value,error_tolerance):
|
|
||||||
""" This is used to compare relative values. """
|
|
||||||
if (value==actual_value): # if we don't need a relative comparison!
|
|
||||||
return True
|
|
||||||
return (abs(value - actual_value) / max(value,actual_value) <= error_tolerance)
|
|
||||||
|
|
||||||
def isapproxdiff(self, filename1, filename2, error_tolerance=0.001):
|
|
||||||
"""Compare two files.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
|
|
||||||
filename1 -- First file name
|
|
||||||
|
|
||||||
filename2 -- Second file name
|
|
||||||
|
|
||||||
Return value:
|
|
||||||
|
|
||||||
True if the files are the same, False otherwise.
|
|
||||||
|
|
||||||
"""
|
|
||||||
import re
|
|
||||||
import debug
|
|
||||||
|
|
||||||
numeric_const_pattern = r"""
|
|
||||||
[-+]? # optional sign
|
|
||||||
(?:
|
|
||||||
(?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc
|
|
||||||
|
|
|
||||||
(?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc
|
|
||||||
)
|
|
||||||
# followed by optional exponent part if desired
|
|
||||||
(?: [Ee] [+-]? \d+ ) ?
|
|
||||||
"""
|
|
||||||
rx = re.compile(numeric_const_pattern, re.VERBOSE)
|
|
||||||
fp1 = open(filename1, 'rb')
|
|
||||||
fp2 = open(filename2, 'rb')
|
|
||||||
mismatches=0
|
|
||||||
line_num=0
|
|
||||||
while True:
|
|
||||||
line_num+=1
|
|
||||||
line1 = fp1.readline().decode('utf-8')
|
|
||||||
line2 = fp2.readline().decode('utf-8')
|
|
||||||
#print("line1:",line1)
|
|
||||||
#print("line2:",line2)
|
|
||||||
|
|
||||||
# 1. Find all of the floats using a regex
|
|
||||||
line1_floats=rx.findall(line1)
|
|
||||||
line2_floats=rx.findall(line2)
|
|
||||||
debug.info(3,"line1_floats: "+str(line1_floats))
|
|
||||||
debug.info(3,"line2_floats: "+str(line2_floats))
|
|
||||||
|
|
||||||
|
|
||||||
# 2. Remove the floats from the string
|
|
||||||
for f in line1_floats:
|
|
||||||
line1=line1.replace(f,"",1)
|
|
||||||
for f in line2_floats:
|
|
||||||
line2=line2.replace(f,"",1)
|
|
||||||
#print("line1:",line1)
|
|
||||||
#print("line2:",line2)
|
|
||||||
|
|
||||||
# 3. Convert to floats rather than strings
|
|
||||||
line1_floats = [float(x) for x in line1_floats]
|
|
||||||
line2_floats = [float(x) for x in line1_floats]
|
|
||||||
|
|
||||||
# 4. Check if remaining string matches
|
|
||||||
if line1 != line2:
|
|
||||||
if mismatches==0:
|
|
||||||
debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2))
|
|
||||||
mismatches += 1
|
|
||||||
debug.error("MISMATCH Line ({0}):\n{1}\n!=\n{2}".format(line_num,line1.rstrip('\n'),line2.rstrip('\n')))
|
|
||||||
|
|
||||||
# 5. Now compare that the floats match
|
|
||||||
elif len(line1_floats)!=len(line2_floats):
|
|
||||||
if mismatches==0:
|
|
||||||
debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2))
|
|
||||||
mismatches += 1
|
|
||||||
debug.error("MISMATCH Line ({0}) Length {1} != {2}".format(line_num,len(line1_floats),len(line2_floats)))
|
|
||||||
else:
|
|
||||||
for (float1,float2) in zip(line1_floats,line2_floats):
|
|
||||||
relative_diff = self.relative_diff(float1,float2)
|
|
||||||
check = relative_diff <= error_tolerance
|
|
||||||
if not check:
|
|
||||||
if mismatches==0:
|
|
||||||
debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2))
|
|
||||||
mismatches += 1
|
|
||||||
debug.error("MISMATCH Line ({0}) Float {1} != {2} diff: {3:.1f}%".format(line_num,float1,float2,relative_diff*100))
|
|
||||||
|
|
||||||
# Only show the first 10 mismatch lines
|
|
||||||
if not line1 and not line2 or mismatches>10:
|
|
||||||
fp1.close()
|
|
||||||
fp2.close()
|
|
||||||
return mismatches==0
|
|
||||||
|
|
||||||
# Never reached
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def isdiff(self,filename1,filename2):
|
|
||||||
""" This is used to compare two files and display the diff if they are different.. """
|
|
||||||
import debug
|
|
||||||
import filecmp
|
|
||||||
import difflib
|
|
||||||
check = filecmp.cmp(filename1,filename2)
|
|
||||||
if not check:
|
|
||||||
debug.error("MISMATCH file1={0} file2={1}".format(filename1,filename2))
|
|
||||||
f1 = open(filename1,"r")
|
|
||||||
s1 = f1.readlines().decode('utf-8')
|
|
||||||
f1.close()
|
|
||||||
f2 = open(filename2,"r").decode('utf-8')
|
|
||||||
s2 = f2.readlines()
|
|
||||||
f2.close()
|
|
||||||
mismatches=0
|
|
||||||
for line in difflib.unified_diff(s1, s2):
|
|
||||||
mismatches += 1
|
|
||||||
self.error("DIFF LINES:",line)
|
|
||||||
if mismatches>10:
|
|
||||||
return False
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
debug.info(2,"MATCH {0} {1}".format(filename1,filename2))
|
|
||||||
return True
|
|
||||||
|
|
||||||
def header(filename, technology):
|
|
||||||
# Skip the header for gitlab regression
|
|
||||||
import getpass
|
|
||||||
if getpass.getuser() == "gitlab-runner":
|
|
||||||
return
|
|
||||||
|
|
||||||
tst = "Running Test for:"
|
|
||||||
print("\n")
|
|
||||||
print(" ______________________________________________________________________________ ")
|
|
||||||
print("|==============================================================================|")
|
|
||||||
print("|=========" + tst.center(60) + "=========|")
|
|
||||||
print("|=========" + technology.center(60) + "=========|")
|
|
||||||
print("|=========" + filename.center(60) + "=========|")
|
|
||||||
from globals import OPTS
|
|
||||||
print("|=========" + OPTS.openram_temp.center(60) + "=========|")
|
|
||||||
print("|==============================================================================|")
|
|
||||||
|
|
@ -87,26 +87,6 @@ class sram():
|
||||||
def save(self):
|
def save(self):
|
||||||
""" Save all the output files while reporting time to do it as well. """
|
""" Save all the output files while reporting time to do it as well. """
|
||||||
|
|
||||||
if not OPTS.netlist_only:
|
|
||||||
# Write the layout
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
gdsname = OPTS.output_path + self.s.name + ".gds"
|
|
||||||
debug.print_raw("GDS: Writing to {0}".format(gdsname))
|
|
||||||
self.gds_write(gdsname)
|
|
||||||
verify.write_drc_script(cell_name=self.s.name,
|
|
||||||
gds_name=os.path.basename(gdsname),
|
|
||||||
extract=True,
|
|
||||||
final_verification=True,
|
|
||||||
output_path=OPTS.output_path)
|
|
||||||
print_time("GDS", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Create a LEF physical model
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
lefname = OPTS.output_path + self.s.name + ".lef"
|
|
||||||
debug.print_raw("LEF: Writing to {0}".format(lefname))
|
|
||||||
self.lef_write(lefname)
|
|
||||||
print_time("LEF", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Save the spice file
|
# Save the spice file
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
spname = OPTS.output_path + self.s.name + ".sp"
|
spname = OPTS.output_path + self.s.name + ".sp"
|
||||||
|
|
@ -118,12 +98,33 @@ class sram():
|
||||||
output_path=OPTS.output_path)
|
output_path=OPTS.output_path)
|
||||||
print_time("Spice writing", datetime.datetime.now(), start_time)
|
print_time("Spice writing", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
|
if not OPTS.netlist_only:
|
||||||
|
# Write the layout
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
gdsname = OPTS.output_path + self.s.name + ".gds"
|
||||||
|
debug.print_raw("GDS: Writing to {0}".format(gdsname))
|
||||||
|
self.gds_write(gdsname)
|
||||||
|
if OPTS.check_lvsdrc:
|
||||||
|
verify.write_drc_script(cell_name=self.s.name,
|
||||||
|
gds_name=os.path.basename(gdsname),
|
||||||
|
extract=True,
|
||||||
|
final_verification=True,
|
||||||
|
output_path=OPTS.output_path)
|
||||||
|
print_time("GDS", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
|
# Create a LEF physical model
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
lefname = OPTS.output_path + self.s.name + ".lef"
|
||||||
|
debug.print_raw("LEF: Writing to {0}".format(lefname))
|
||||||
|
self.lef_write(lefname)
|
||||||
|
print_time("LEF", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
# Save the LVS file
|
# Save the LVS file
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
|
lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
|
||||||
debug.print_raw("LVS: Writing to {0}".format(lvsname))
|
debug.print_raw("LVS: Writing to {0}".format(lvsname))
|
||||||
self.lvs_write(lvsname)
|
self.lvs_write(lvsname)
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only and OPTS.check_lvsdrc:
|
||||||
verify.write_lvs_script(cell_name=self.s.name,
|
verify.write_lvs_script(cell_name=self.s.name,
|
||||||
gds_name=os.path.basename(gdsname),
|
gds_name=os.path.basename(gdsname),
|
||||||
sp_name=os.path.basename(lvsname),
|
sp_name=os.path.basename(lvsname),
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_base import sram_base
|
from sram_base import sram_base
|
||||||
from contact import m2_via
|
from contact import m2_via
|
||||||
|
from channel_route import channel_route
|
||||||
|
from signal_escape_router import signal_escape_router as router
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import channel_route
|
|
||||||
|
|
||||||
|
|
||||||
class sram_1bank(sram_base):
|
class sram_1bank(sram_base):
|
||||||
|
|
@ -102,12 +102,17 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# Place with an initial wide channel (from above)
|
# Place with an initial wide channel (from above)
|
||||||
self.place_dffs()
|
self.place_dffs()
|
||||||
|
|
||||||
# Route the channel and set to the new data bus size
|
# Route the channel and set to the new data bus size
|
||||||
|
# We need to temporarily add some pins for the x offsets
|
||||||
|
# but we'll remove them so that they have the right y
|
||||||
|
# offsets after the DFF placement.
|
||||||
|
self.add_layout_pins(add_vias=False)
|
||||||
self.route_dffs(add_routes=False)
|
self.route_dffs(add_routes=False)
|
||||||
|
self.remove_layout_pins()
|
||||||
|
|
||||||
# Re-place with the new channel size
|
# Re-place with the new channel size
|
||||||
self.place_dffs()
|
self.place_dffs()
|
||||||
# Now route the channel
|
|
||||||
self.route_dffs()
|
|
||||||
|
|
||||||
def place_row_addr_dffs(self):
|
def place_row_addr_dffs(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -241,14 +246,13 @@ class sram_1bank(sram_base):
|
||||||
self.data_pos[port] = vector(x_offset, y_offset)
|
self.data_pos[port] = vector(x_offset, y_offset)
|
||||||
self.spare_wen_pos[port] = vector(x_offset, y_offset)
|
self.spare_wen_pos[port] = vector(x_offset, y_offset)
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def route_escape_pins(self):
|
||||||
"""
|
"""
|
||||||
Add the top-level pins for a single bank SRAM with control.
|
Add the top-level pins for a single bank SRAM with control.
|
||||||
"""
|
"""
|
||||||
highest_coord = self.find_highest_coords()
|
|
||||||
lowest_coord = self.find_lowest_coords()
|
|
||||||
bbox = [lowest_coord, highest_coord]
|
|
||||||
|
|
||||||
|
# List of pin to new pin name
|
||||||
|
pins_to_route = []
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
# Depending on the port, use the bottom/top or left/right sides
|
# Depending on the port, use the bottom/top or left/right sides
|
||||||
# Port 0 is left/bottom
|
# Port 0 is left/bottom
|
||||||
|
|
@ -258,132 +262,127 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# Connect the control pins as inputs
|
# Connect the control pins as inputs
|
||||||
for signal in self.control_logic_inputs[port]:
|
for signal in self.control_logic_inputs[port]:
|
||||||
if signal == "clk":
|
|
||||||
continue
|
|
||||||
if signal.startswith("rbl"):
|
if signal.startswith("rbl"):
|
||||||
continue
|
continue
|
||||||
if OPTS.perimeter_pins:
|
if signal=="clk":
|
||||||
self.add_perimeter_pin(name=signal + "{}".format(port),
|
pins_to_route.append(("{0}{1}".format(signal, port), bottom_or_top))
|
||||||
pin=self.control_logic_insts[port].get_pin(signal),
|
|
||||||
side=left_or_right,
|
|
||||||
bbox=bbox)
|
|
||||||
else:
|
else:
|
||||||
self.copy_layout_pin(self.control_logic_insts[port],
|
pins_to_route.append(("{0}{1}".format(signal, port), left_or_right))
|
||||||
signal,
|
|
||||||
signal + "{}".format(port))
|
|
||||||
|
|
||||||
if OPTS.perimeter_pins:
|
|
||||||
self.add_perimeter_pin(name="clk{}".format(port),
|
|
||||||
pin=self.control_logic_insts[port].get_pin("clk"),
|
|
||||||
side=bottom_or_top,
|
|
||||||
bbox=bbox)
|
|
||||||
else:
|
|
||||||
self.copy_layout_pin(self.control_logic_insts[port],
|
|
||||||
"clk",
|
|
||||||
"clk{}".format(port))
|
|
||||||
|
|
||||||
# Data input pins go to BOTTOM/TOP
|
|
||||||
din_ports = []
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
for bit in range(self.word_size + self.num_spare_cols):
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("din{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
p = self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit),
|
|
||||||
pin=self.data_dff_insts[port].get_pin("din_{0}".format(bit)),
|
|
||||||
side=bottom_or_top,
|
|
||||||
bbox=bbox)
|
|
||||||
din_ports.append(p)
|
|
||||||
else:
|
|
||||||
self.copy_layout_pin(self.data_dff_insts[port],
|
|
||||||
"din_{}".format(bit),
|
|
||||||
"din{0}[{1}]".format(port, bit))
|
|
||||||
|
|
||||||
# Data output pins go to BOTTOM/TOP
|
if port in self.readwrite_ports or port in self.read_ports:
|
||||||
if port in self.readwrite_ports and OPTS.perimeter_pins:
|
|
||||||
for bit in range(self.word_size + self.num_spare_cols):
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
# This should be routed next to the din pin
|
pins_to_route.append(("dout{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
p = din_ports[bit]
|
|
||||||
self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(port, bit),
|
|
||||||
layer=p.layer,
|
|
||||||
offset=p.center() + vector(self.m3_pitch, 0),
|
|
||||||
width=p.width(),
|
|
||||||
height=p.height())
|
|
||||||
elif port in self.read_ports:
|
|
||||||
for bit in range(self.word_size + self.num_spare_cols):
|
|
||||||
if OPTS.perimeter_pins:
|
|
||||||
# This should have a clear route to the perimeter if there are no din routes
|
|
||||||
self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit),
|
|
||||||
pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)),
|
|
||||||
side=bottom_or_top,
|
|
||||||
bbox=bbox)
|
|
||||||
else:
|
|
||||||
self.copy_layout_pin(self.bank_inst,
|
|
||||||
"dout{0}_{1}".format(port, bit),
|
|
||||||
"dout{0}[{1}]".format(port, bit))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Lower address bits go to BOTTOM/TOP
|
|
||||||
for bit in range(self.col_addr_size):
|
for bit in range(self.col_addr_size):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("addr{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit),
|
|
||||||
pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)),
|
|
||||||
side=bottom_or_top,
|
|
||||||
bbox=bbox)
|
|
||||||
else:
|
|
||||||
self.copy_layout_pin(self.col_addr_dff_insts[port],
|
|
||||||
"din_{}".format(bit),
|
|
||||||
"addr{0}[{1}]".format(port, bit))
|
|
||||||
|
|
||||||
# Upper address bits go to LEFT/RIGHT
|
|
||||||
for bit in range(self.row_addr_size):
|
for bit in range(self.row_addr_size):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("addr{0}[{1}]".format(port, bit + self.col_addr_size), left_or_right))
|
||||||
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size),
|
|
||||||
pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)),
|
|
||||||
side=left_or_right,
|
|
||||||
bbox=bbox)
|
|
||||||
else:
|
|
||||||
self.copy_layout_pin(self.row_addr_dff_insts[port],
|
|
||||||
"din_{}".format(bit),
|
|
||||||
"addr{0}[{1}]".format(port, bit + self.col_addr_size))
|
|
||||||
|
|
||||||
# Write mask pins go to BOTTOM/TOP
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("wmask{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit),
|
|
||||||
pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)),
|
|
||||||
side=bottom_or_top,
|
|
||||||
bbox=bbox)
|
|
||||||
else:
|
|
||||||
self.copy_layout_pin(self.wmask_dff_insts[port],
|
|
||||||
"din_{}".format(bit),
|
|
||||||
"wmask{0}[{1}]".format(port, bit))
|
|
||||||
|
|
||||||
# Spare wen pins go to BOTTOM/TOP
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
for bit in range(self.num_spare_cols):
|
for bit in range(self.num_spare_cols):
|
||||||
if OPTS.perimeter_pins:
|
pins_to_route.append(("spare_wen{0}[{1}]".format(port, bit), bottom_or_top))
|
||||||
self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit),
|
|
||||||
pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)),
|
|
||||||
side=left_or_right,
|
|
||||||
bbox=bbox)
|
|
||||||
else:
|
|
||||||
self.copy_layout_pin(self.spare_wen_dff_insts[port],
|
|
||||||
"din_{}".format(bit),
|
|
||||||
"spare_wen{0}[{1}]".format(port, bit))
|
|
||||||
|
|
||||||
|
rtr=router(self.m3_stack, self)
|
||||||
|
rtr.escape_route(pins_to_route)
|
||||||
|
|
||||||
|
def add_layout_pins(self, add_vias=True):
|
||||||
|
"""
|
||||||
|
Add the top-level pins for a single bank SRAM with control.
|
||||||
|
"""
|
||||||
|
for port in self.all_ports:
|
||||||
|
# Hack: If we are escape routing, set the pin layer to
|
||||||
|
# None so that we will start from the pin layer
|
||||||
|
# Otherwise, set it as the pin layer so that no vias are added.
|
||||||
|
# Otherwise, when we remove pins to move the dff array dynamically,
|
||||||
|
# we will leave some remaining vias when the pin locations change.
|
||||||
|
if add_vias:
|
||||||
|
pin_layer = None
|
||||||
|
else:
|
||||||
|
pin_layer = self.pwr_grid_layer
|
||||||
|
|
||||||
|
# Connect the control pins as inputs
|
||||||
|
for signal in self.control_logic_inputs[port]:
|
||||||
|
if signal.startswith("rbl"):
|
||||||
|
continue
|
||||||
|
self.add_io_pin(self.control_logic_insts[port],
|
||||||
|
signal,
|
||||||
|
signal + "{}".format(port),
|
||||||
|
start_layer=pin_layer)
|
||||||
|
|
||||||
|
if port in self.write_ports:
|
||||||
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
|
self.add_io_pin(self.data_dff_insts[port],
|
||||||
|
"din_{}".format(bit),
|
||||||
|
"din{0}[{1}]".format(port, bit),
|
||||||
|
start_layer=pin_layer)
|
||||||
|
|
||||||
|
if port in self.readwrite_ports or port in self.read_ports:
|
||||||
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
|
self.add_io_pin(self.bank_inst,
|
||||||
|
"dout{0}_{1}".format(port, bit),
|
||||||
|
"dout{0}[{1}]".format(port, bit),
|
||||||
|
start_layer=pin_layer)
|
||||||
|
|
||||||
|
for bit in range(self.col_addr_size):
|
||||||
|
self.add_io_pin(self.col_addr_dff_insts[port],
|
||||||
|
"din_{}".format(bit),
|
||||||
|
"addr{0}[{1}]".format(port, bit),
|
||||||
|
start_layer=pin_layer)
|
||||||
|
|
||||||
|
for bit in range(self.row_addr_size):
|
||||||
|
self.add_io_pin(self.row_addr_dff_insts[port],
|
||||||
|
"din_{}".format(bit),
|
||||||
|
"addr{0}[{1}]".format(port, bit + self.col_addr_size),
|
||||||
|
start_layer=pin_layer)
|
||||||
|
|
||||||
|
if port in self.write_ports:
|
||||||
|
if self.write_size:
|
||||||
|
for bit in range(self.num_wmasks):
|
||||||
|
self.add_io_pin(self.wmask_dff_insts[port],
|
||||||
|
"din_{}".format(bit),
|
||||||
|
"wmask{0}[{1}]".format(port, bit),
|
||||||
|
start_layer=pin_layer)
|
||||||
|
|
||||||
|
if port in self.write_ports:
|
||||||
|
for bit in range(self.num_spare_cols):
|
||||||
|
self.add_io_pin(self.spare_wen_dff_insts[port],
|
||||||
|
"din_{}".format(bit),
|
||||||
|
"spare_wen{0}[{1}]".format(port, bit),
|
||||||
|
start_layer=pin_layer)
|
||||||
|
|
||||||
def route_layout(self):
|
def route_layout(self):
|
||||||
""" Route a single bank SRAM """
|
""" Route a single bank SRAM """
|
||||||
|
|
||||||
self.add_layout_pins()
|
|
||||||
|
|
||||||
self.route_clk()
|
self.route_clk()
|
||||||
|
|
||||||
self.route_control_logic()
|
self.route_control_logic()
|
||||||
|
|
||||||
self.route_row_addr_dff()
|
self.route_row_addr_dff()
|
||||||
|
|
||||||
|
self.route_dffs()
|
||||||
|
|
||||||
|
# We add the vias to M3 before routing supplies because
|
||||||
|
# they might create some blockages
|
||||||
|
self.add_layout_pins()
|
||||||
|
|
||||||
|
# Route the supplies first since the MST is not blockage aware
|
||||||
|
# and signals can route to anywhere on sides (it is flexible)
|
||||||
|
self.route_supplies()
|
||||||
|
|
||||||
|
# Route the pins to the perimeter
|
||||||
|
if OPTS.perimeter_pins:
|
||||||
|
self.route_escape_pins()
|
||||||
|
|
||||||
def route_dffs(self, add_routes=True):
|
def route_dffs(self, add_routes=True):
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -53,21 +53,27 @@ class openram_back_end_test(openram_test):
|
||||||
else:
|
else:
|
||||||
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
|
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
|
||||||
config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME)
|
config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME)
|
||||||
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
|
cmd = "{0} -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
|
||||||
out_file,
|
out_file,
|
||||||
out_path,
|
out_path,
|
||||||
options,
|
options,
|
||||||
config_name,
|
config_name,
|
||||||
out_path)
|
out_path)
|
||||||
debug.info(1, cmd)
|
debug.info(1, cmd)
|
||||||
os.system(cmd)
|
os.system(cmd)
|
||||||
|
|
||||||
# assert an error until we actually check a resul
|
# assert an error until we actually check a resul
|
||||||
for extension in ["gds", "v", "lef", "sp"]:
|
for extension in ["gds", "v", "lef", "sp", "lvs.sp"]:
|
||||||
filename = "{0}/{1}.{2}".format(out_path, out_file, extension)
|
filename = "{0}{1}.{2}".format(out_path, out_file, extension)
|
||||||
debug.info(1, "Checking for file: " + filename)
|
debug.info(1, "Checking for file: " + filename)
|
||||||
self.assertEqual(os.path.exists(filename), True)
|
self.assertEqual(os.path.exists(filename), True)
|
||||||
|
|
||||||
|
# check if the auxiliary scripts were created
|
||||||
|
for out_file in ["run_drc.sh", "run_lvs.sh"]:
|
||||||
|
filename = "{0}{1}".format(out_path, out_file)
|
||||||
|
debug.info(1, "Checking for file: " + filename)
|
||||||
|
self.assertEqual(os.path.exists(filename), True)
|
||||||
|
|
||||||
# Make sure there is any .lib file
|
# Make sure there is any .lib file
|
||||||
import glob
|
import glob
|
||||||
files = glob.glob('{0}/*.lib'.format(out_path))
|
files = glob.glob('{0}/*.lib'.format(out_path))
|
||||||
|
|
@ -79,7 +85,7 @@ class openram_back_end_test(openram_test):
|
||||||
self.assertTrue(len(datasheets)>0)
|
self.assertTrue(len(datasheets)>0)
|
||||||
|
|
||||||
# grep any errors from the output
|
# grep any errors from the output
|
||||||
output_log = open("{0}/output.log".format(out_path), "r")
|
output_log = open("{0}output.log".format(out_path), "r")
|
||||||
output = output_log.read()
|
output = output_log.read()
|
||||||
output_log.close()
|
output_log.close()
|
||||||
self.assertEqual(len(re.findall('ERROR', output)), 0)
|
self.assertEqual(len(re.findall('ERROR', output)), 0)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,5 @@ num_words = 16
|
||||||
tech_name = OPTS.tech_name
|
tech_name = OPTS.tech_name
|
||||||
|
|
||||||
nominal_corner_only = True
|
nominal_corner_only = True
|
||||||
route_supplies = "tree"
|
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,7 @@ num_words = 16
|
||||||
tech_name = OPTS.tech_name
|
tech_name = OPTS.tech_name
|
||||||
|
|
||||||
nominal_corner_only = True
|
nominal_corner_only = True
|
||||||
route_supplies = True
|
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
inline_lvsdrc = True
|
spice_name = "ngspice"
|
||||||
analytical_delay = False
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ lvs_warned = False
|
||||||
pex_warned = False
|
pex_warned = False
|
||||||
|
|
||||||
|
|
||||||
def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None):
|
def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None, sp_name=None):
|
||||||
pass
|
debug.error("Cannot write DRC script for unknown tool", -1)
|
||||||
|
|
||||||
|
|
||||||
def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=False, output_path=None):
|
def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=False, output_path=None):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue