mirror of https://github.com/VLSIDA/OpenRAM.git
Merged with dev
This commit is contained in:
commit
448e8f4cfd
14
.coveragerc
14
.coveragerc
|
|
@ -4,6 +4,10 @@ omit =
|
|||
*/.local/*
|
||||
# omit everything in /usr
|
||||
/usr/*
|
||||
# ignore the unit tests themselves
|
||||
*/tests/*
|
||||
# ignore the debug utilities
|
||||
debug.py
|
||||
[paths]
|
||||
source =
|
||||
/home/gitlab-runner/builds/2fd64746/0
|
||||
|
|
@ -12,3 +16,13 @@ source =
|
|||
/home/gitlab-runner/builds/2fd64746/3
|
||||
/home/gitlab-runner/builds/2fd64746/4
|
||||
/home/gitlab-runner/builds/2fd64746/5
|
||||
[report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
def __repr__
|
||||
except Exception
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
if 0:
|
||||
if __name__ == "__main__":
|
||||
if not OPTS.is_unit_test
|
||||
|
|
@ -1,93 +1,45 @@
|
|||
TECH = scn4m_subm
|
||||
CUR_DIR = $(shell pwd)
|
||||
TEST_DIR = ${CUR_DIR}/tests
|
||||
|
||||
MAKEFLAGS += -j 2
|
||||
MAKEFLAGS += -j 1
|
||||
|
||||
# Library test
|
||||
LIBRARY_TESTS = \
|
||||
01_library_drc_test.py \
|
||||
02_library_lvs_test.py
|
||||
LIBRARY_TESTS = $(shell find ${TEST_DIR} -name 0[1-2]*_test.py)
|
||||
|
||||
# Technology and DRC tests (along with ptx)
|
||||
TECH_TESTS = \
|
||||
03_contact_test.py \
|
||||
03_ptx_1finger_pmos_test.py \
|
||||
03_ptx_4finger_nmos_test.py \
|
||||
03_path_test.py \
|
||||
03_ptx_3finger_nmos_test.py \
|
||||
03_ptx_4finger_pmos_test.py \
|
||||
03_ptx_1finger_nmos_test.py \
|
||||
03_ptx_3finger_pmos_test.py \
|
||||
03_wire_test.py
|
||||
TECH_TESTS = $(shell find ${TEST_DIR} -name 03*_test.py)
|
||||
|
||||
# Parameterized cells
|
||||
PCELLS_TESTS = \
|
||||
04_pinv_1x_test.py \
|
||||
04_pinv_1x_beta_test.py \
|
||||
04_pinv_2x_test.py \
|
||||
04_pinv_10x_test.py \
|
||||
04_pnand2_test.py \
|
||||
04_pnor2_test.py \
|
||||
04_pnand3_test.py\
|
||||
04_wordline_driver_test.py \
|
||||
04_precharge_test.py
|
||||
CELL_TESTS = $(shell find ${TEST_DIR} -name 04*_test.py)
|
||||
|
||||
# Dynamically generated modules and arrays
|
||||
MODULE_TESTS = \
|
||||
05_bitcell_array_test.py \
|
||||
06_hierarchical_decoder_test.py \
|
||||
06_hierarchical_predecode2x4_test.py \
|
||||
06_hierarchical_predecode3x8_test.py \
|
||||
07_single_level_column_mux_array_test.py \
|
||||
08_precharge_array_test.py \
|
||||
09_sense_amp_array_test.py \
|
||||
10_write_driver_array_test.py \
|
||||
11_ms_flop_array_test.py \
|
||||
12_tri_gate_array_test.py \
|
||||
13_delay_chain_test.py \
|
||||
14_replica_bitline_test.py \
|
||||
16_control_logic_test.py
|
||||
MODULE_TESTS = $(shell find ${TEST_DIR} -name 0[5-9]*_test.py)\
|
||||
$(shell find ${TEST_DIR} -name 1*_test.py)
|
||||
|
||||
# Top-level SRAM configurations
|
||||
TOP_TESTS = \
|
||||
19_multi_bank_test.py \
|
||||
19_single_bank_test.py \
|
||||
20_sram_1bank_test.py \
|
||||
20_sram_2bank_test.py \
|
||||
20_sram_4bank_test.py
|
||||
TOP_TESTS = $(shell find ${TEST_DIR} -name 20*_test.py)
|
||||
|
||||
# All simulation tests.
|
||||
CHAR_TESTS = \
|
||||
21_hspice_delay_test.py \
|
||||
21_ngspice_delay_test.py \
|
||||
21_ngspice_setuphold_test.py \
|
||||
21_hspice_setuphold_test.py \
|
||||
22_sram_func_test.py \
|
||||
22_pex_func_test_with_pinv.py \
|
||||
23_lib_sram_prune_test.py \
|
||||
23_lib_sram_test.py
|
||||
CHAR_TESTS = $(shell find ${TEST_DIR} -name 2[1-2]*_test.py)
|
||||
|
||||
# Keep the model lib test here since it is fast
|
||||
# and doesn't need simulation.
|
||||
USAGE_TESTS = \
|
||||
23_lib_sram_model_test.py \
|
||||
24_lef_sram_test.py \
|
||||
25_verilog_sram_test.py
|
||||
USAGE_TESTS = $(shell find ${TEST_DIR} -name 2[3-9]*_test.py)\
|
||||
$(shell find ${TEST_DIR} -name 30*_test.py)
|
||||
|
||||
ALL_FILES = \
|
||||
ALL_TESTS = \
|
||||
${LIBRARY_TESTS} \
|
||||
${TECH_TESTS} \
|
||||
${PCELLS_TESTS} \
|
||||
${MODULES_TESTS} \
|
||||
${CELL_TESTS} \
|
||||
${MODULE_TESTS} \
|
||||
${TOP_TESTS} \
|
||||
${CHAR_TESTS} \
|
||||
${USAGE_TESTS}
|
||||
|
||||
default all:
|
||||
.PHONY: ${ALL_TESTS}
|
||||
|
||||
$(ALL_FILES):
|
||||
python ${TEST_DIR}/$@ -t freepdk45
|
||||
python ${TEST_DIR}/$@ -t scn3me_subm
|
||||
all: ${ALL_TESTS}
|
||||
|
||||
# Library tests
|
||||
lib: ${LIBRARY_TESTS}
|
||||
|
|
@ -96,10 +48,10 @@ lib: ${LIBRARY_TESTS}
|
|||
tech: ${TECH_TESTS}
|
||||
|
||||
# Dynamically generated cells
|
||||
pcells: ${PCELLS_TESTS}
|
||||
cell: ${CELL_TESTS}
|
||||
|
||||
# Dynamically generated modules
|
||||
modules: ${MODULES_TESTS}
|
||||
module: ${MODULE_TESTS}
|
||||
|
||||
# Top level SRAM tests
|
||||
top: ${TOP_TESTS}
|
||||
|
|
@ -110,6 +62,9 @@ char: ${CHAR_TESTS}
|
|||
# Usage and file generation
|
||||
usage: ${USAGE_TESTS}
|
||||
|
||||
$(ALL_TESTS):
|
||||
python3 $@ -t ${TECH}
|
||||
|
||||
clean:
|
||||
find . -name \*.pyc -exec rm {} \;
|
||||
find . -name \*~ -exec rm {} \;
|
||||
|
|
|
|||
|
|
@ -441,10 +441,10 @@ class pin_layout:
|
|||
"""
|
||||
Given three co-linear points, determine if q lies on segment pr
|
||||
"""
|
||||
if q[0] <= max(p[0], r[0]) and \
|
||||
q[0] >= min(p[0], r[0]) and \
|
||||
q[1] <= max(p[1], r[1]) and \
|
||||
q[1] >= min(p[1], r[1]):
|
||||
if q.x <= max(p.x, r.x) and \
|
||||
q.x >= min(p.x, r.x) and \
|
||||
q.y <= max(p.y, r.y) and \
|
||||
q.y >= min(p.y, r.y):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
@ -473,8 +473,8 @@ class pin_layout:
|
|||
x = (b2*c1 - b1*c2)/determinant
|
||||
y = (a1*c2 - a2*c1)/determinant
|
||||
|
||||
r = [x,y]
|
||||
r = vector(x,y).snap_to_grid()
|
||||
if self.on_segment(a, r, b) and self.on_segment(c, r, d):
|
||||
return [x, y]
|
||||
return r
|
||||
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ def parse_args():
|
|||
# This may be overridden when we read a config file though...
|
||||
if OPTS.tech_name == "":
|
||||
OPTS.tech_name = "scmos"
|
||||
# Alias SCMOS to AMI 0.5um
|
||||
# Alias SCMOS to 180nm
|
||||
if OPTS.tech_name == "scmos":
|
||||
OPTS.tech_name = "scn4m_subm"
|
||||
|
||||
|
|
@ -89,6 +89,7 @@ def print_banner():
|
|||
print("|=========" + dev_info.center(60) + "=========|")
|
||||
temp_info = "Temp dir: {}".format(OPTS.openram_temp)
|
||||
print("|=========" + temp_info.center(60) + "=========|")
|
||||
print("|=========" + "See LICENSE for license info".center(60) + "=========|")
|
||||
print("|==============================================================================|")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -172,7 +172,6 @@ class pin_group:
|
|||
ymax = max(plc.y,elc.y)
|
||||
ll = vector(plc.x, ymin)
|
||||
ur = vector(prc.x, ymax)
|
||||
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
||||
elif pin.yoverlaps(enclosure):
|
||||
# Is it horizontal overlap, extend pin shape to enclosure
|
||||
pbc = pin.bc()
|
||||
|
|
@ -183,7 +182,6 @@ class pin_group:
|
|||
xmax = max(pbc.x,ebc.x)
|
||||
ll = vector(xmin, pbc.y)
|
||||
ur = vector(xmax, puc.y)
|
||||
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
||||
else:
|
||||
# Neither, so we must do a corner-to corner
|
||||
pc = pin.center()
|
||||
|
|
@ -194,8 +192,10 @@ class pin_group:
|
|||
ymax = max(pc.y, ec.y)
|
||||
ll = vector(xmin, ymin)
|
||||
ur = vector(xmax, ymax)
|
||||
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
||||
|
||||
if ll.x==ur.x or ll.y==ur.y:
|
||||
return None
|
||||
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
||||
return p
|
||||
|
||||
def find_above_connector(self, pin, enclosures):
|
||||
|
|
@ -226,7 +226,7 @@ class pin_group:
|
|||
# If it already overlaps, no connector needed
|
||||
if above_item.overlaps(pin):
|
||||
return None
|
||||
|
||||
|
||||
# Otherwise, make a connector to the item
|
||||
p = self.compute_connector(pin, above_item)
|
||||
return p
|
||||
|
|
@ -485,7 +485,9 @@ class pin_group:
|
|||
for pin_list in self.pins:
|
||||
if not self.overlap_any_shape(pin_list, self.enclosures):
|
||||
connector = self.find_smallest_connector(pin_list, self.enclosures)
|
||||
debug.check(connector!=None, "Could not find a connector for {} with {}".format(pin_list, self.enclosures))
|
||||
if connector==None:
|
||||
debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures))
|
||||
self.router.write_debug_gds("no_connector.gds")
|
||||
self.enclosures.append(connector)
|
||||
|
||||
|
||||
|
|
@ -559,15 +561,13 @@ class pin_group:
|
|||
of any grid in the other set.
|
||||
"""
|
||||
# We could optimize this to just check the boundaries
|
||||
g1_grids = set()
|
||||
g2_grids = set()
|
||||
adj_grids = set()
|
||||
for g1 in self.grids:
|
||||
for g2 in other.grids:
|
||||
if g1.distance(g2) <= separation:
|
||||
g1_grids.add(g1)
|
||||
g2_grids.add(g2)
|
||||
adj_grids.add(g1)
|
||||
|
||||
return g1_grids,g2_grids
|
||||
return adj_grids
|
||||
|
||||
def convert_pin(self):
|
||||
"""
|
||||
|
|
@ -576,14 +576,16 @@ class pin_group:
|
|||
should be either blocked or part of the pin.
|
||||
"""
|
||||
pin_set = set()
|
||||
partial_set = set()
|
||||
blockage_set = set()
|
||||
|
||||
for pin_list in self.pins:
|
||||
for pin in pin_list:
|
||||
debug.info(2," Converting {0}".format(pin))
|
||||
# Determine which tracks the pin overlaps
|
||||
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin)
|
||||
pin_set.update(pin_in_tracks)
|
||||
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin)
|
||||
pin_set.update(sufficient)
|
||||
partial_set.update(insufficient)
|
||||
|
||||
# Blockages will be a super-set of pins since it uses the inflated pin shape.
|
||||
blockage_in_tracks = self.router.convert_blockage(pin)
|
||||
|
|
@ -595,89 +597,94 @@ class pin_group:
|
|||
if len(shared_set)>0:
|
||||
debug.info(2,"Removing pins {}".format(shared_set))
|
||||
pin_set.difference_update(shared_set)
|
||||
shared_set = partial_set & self.router.blocked_grids
|
||||
if len(shared_set)>0:
|
||||
debug.info(2,"Removing pins {}".format(shared_set))
|
||||
partial_set.difference_update(shared_set)
|
||||
shared_set = blockage_set & self.router.blocked_grids
|
||||
if len(shared_set)>0:
|
||||
debug.info(2,"Removing blocks {}".format(shared_set))
|
||||
blockage_set.difference_update(shared_set)
|
||||
|
||||
# At least one of the groups must have some valid tracks
|
||||
if (len(pin_set)==0 and len(blockage_set)==0):
|
||||
if (len(pin_set)==0 and len(partial_set)==0 and len(blockage_set)==0):
|
||||
#debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
|
||||
|
||||
for pin_list in self.pins:
|
||||
for pin in pin_list:
|
||||
debug.info(2," Converting {0}".format(pin))
|
||||
debug.warning(" Expanding conversion {0}".format(pin))
|
||||
# Determine which tracks the pin overlaps
|
||||
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
|
||||
pin_set.update(pin_in_tracks)
|
||||
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
|
||||
pin_set.update(sufficient)
|
||||
partial_set.update(insufficient)
|
||||
|
||||
if len(pin_set)==0:
|
||||
if len(pin_set)==0 and len(partial_set)==0:
|
||||
debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins))
|
||||
self.router.write_debug_gds("blocked_pin.gds")
|
||||
|
||||
# We need to route each of the components, so don't combine the groups
|
||||
self.grids = pin_set | blockage_set
|
||||
# Remember the secondary grids for removing adjacent pins in wide metal spacing
|
||||
self.secondary_grids = blockage_set - pin_set
|
||||
# Consider all the grids that would be blocked
|
||||
self.grids = pin_set | partial_set
|
||||
# Remember the secondary grids for removing adjacent pins
|
||||
self.secondary_grids = partial_set
|
||||
|
||||
debug.info(2," pins {}".format(self.grids))
|
||||
debug.info(2," secondary {}".format(self.secondary_grids))
|
||||
|
||||
def recurse_simple_overlap_enclosure(self, start_set, direct):
|
||||
"""
|
||||
Recursive function to return set of tracks that connects to
|
||||
the actual supply rail wire in a given direction (or terminating
|
||||
when any track is no longer in the supply rail.
|
||||
"""
|
||||
next_set = grid_utils.expand_border(start_set, direct)
|
||||
# def recurse_simple_overlap_enclosure(self, start_set, direct):
|
||||
# """
|
||||
# Recursive function to return set of tracks that connects to
|
||||
# the actual supply rail wire in a given direction (or terminating
|
||||
# when any track is no longer in the supply rail.
|
||||
# """
|
||||
# next_set = grid_utils.expand_border(start_set, direct)
|
||||
|
||||
supply_tracks = self.router.supply_rail_tracks[self.name]
|
||||
supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
|
||||
# supply_tracks = self.router.supply_rail_tracks[self.name]
|
||||
# supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
|
||||
|
||||
supply_overlap = next_set & supply_tracks
|
||||
wire_overlap = next_set & supply_wire_tracks
|
||||
# supply_overlap = next_set & supply_tracks
|
||||
# wire_overlap = next_set & supply_wire_tracks
|
||||
|
||||
# If the rail overlap is the same, we are done, since we connected to the actual wire
|
||||
if len(wire_overlap)==len(start_set):
|
||||
new_set = start_set | wire_overlap
|
||||
# If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region
|
||||
elif len(supply_overlap)==len(start_set):
|
||||
recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
|
||||
new_set = start_set | supply_overlap | recurse_set
|
||||
else:
|
||||
# If we got no next set, we are done, can't expand!
|
||||
new_set = set()
|
||||
# # If the rail overlap is the same, we are done, since we connected to the actual wire
|
||||
# if len(wire_overlap)==len(start_set):
|
||||
# new_set = start_set | wire_overlap
|
||||
# # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region
|
||||
# elif len(supply_overlap)==len(start_set):
|
||||
# recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
|
||||
# new_set = start_set | supply_overlap | recurse_set
|
||||
# else:
|
||||
# # If we got no next set, we are done, can't expand!
|
||||
# new_set = set()
|
||||
|
||||
return new_set
|
||||
# return new_set
|
||||
|
||||
def create_simple_overlap_enclosure(self, start_set):
|
||||
"""
|
||||
This takes a set of tracks that overlap a supply rail and creates an enclosure
|
||||
that is ensured to overlap the supply rail wire.
|
||||
It then adds rectangle(s) for the enclosure.
|
||||
"""
|
||||
additional_set = set()
|
||||
# Check the layer of any element in the pin to determine which direction to route it
|
||||
e = next(iter(start_set))
|
||||
new_set = start_set.copy()
|
||||
if e.z==0:
|
||||
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
|
||||
if not new_set:
|
||||
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
|
||||
else:
|
||||
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
|
||||
if not new_set:
|
||||
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST)
|
||||
# def create_simple_overlap_enclosure(self, start_set):
|
||||
# """
|
||||
# This takes a set of tracks that overlap a supply rail and creates an enclosure
|
||||
# that is ensured to overlap the supply rail wire.
|
||||
# It then adds rectangle(s) for the enclosure.
|
||||
# """
|
||||
# additional_set = set()
|
||||
# # Check the layer of any element in the pin to determine which direction to route it
|
||||
# e = next(iter(start_set))
|
||||
# new_set = start_set.copy()
|
||||
# if e.z==0:
|
||||
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
|
||||
# if not new_set:
|
||||
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
|
||||
# else:
|
||||
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
|
||||
# if not new_set:
|
||||
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST)
|
||||
|
||||
# Expand the pin grid set to include some extra grids that connect the supply rail
|
||||
self.grids.update(new_set)
|
||||
# # Expand the pin grid set to include some extra grids that connect the supply rail
|
||||
# self.grids.update(new_set)
|
||||
|
||||
# Add the inflated set so we don't get wide metal spacing issues (if it exists)
|
||||
self.blockages.update(grid_utils.inflate_set(new_set,self.router.supply_rail_space_width))
|
||||
# # Block the grids
|
||||
# self.blockages.update(new_set)
|
||||
|
||||
# Add the polygon enclosures and set this pin group as routed
|
||||
self.set_routed()
|
||||
self.enclosures = self.compute_enclosures()
|
||||
# # Add the polygon enclosures and set this pin group as routed
|
||||
# self.set_routed()
|
||||
# self.enclosures = self.compute_enclosures()
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,12 +21,12 @@ class router(router_tech):
|
|||
It populates blockages on a grid class.
|
||||
"""
|
||||
|
||||
def __init__(self, layers, design, gds_filename=None):
|
||||
def __init__(self, layers, design, gds_filename=None, rail_track_width=1):
|
||||
"""
|
||||
This will instantiate a copy of the gds file or the module at (0,0) and
|
||||
route on top of this. The blockages from the gds/module will be considered.
|
||||
"""
|
||||
router_tech.__init__(self, layers)
|
||||
router_tech.__init__(self, layers, rail_track_width)
|
||||
|
||||
self.cell = design
|
||||
|
||||
|
|
@ -70,8 +70,7 @@ class router(router_tech):
|
|||
self.boundary = self.layout.measureBoundary(self.top_name)
|
||||
# These must be un-indexed to get rid of the matrix type
|
||||
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
|
||||
# Pad the UR by a few tracks to add an extra rail possibly
|
||||
self.ur = vector(self.boundary[1][0], self.boundary[1][1]) + self.track_widths.scale(5,5)
|
||||
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
|
||||
|
||||
def clear_pins(self):
|
||||
"""
|
||||
|
|
@ -130,12 +129,8 @@ class router(router_tech):
|
|||
Pin can either be a label or a location,layer pair: [[x,y],layer].
|
||||
"""
|
||||
debug.info(1,"Finding pins for {}.".format(pin_name))
|
||||
#start_time = datetime.now()
|
||||
self.retrieve_pins(pin_name)
|
||||
#print_time("Retrieved pins",datetime.now(), start_time)
|
||||
#start_time = datetime.now()
|
||||
self.analyze_pins(pin_name)
|
||||
#print_time("Analyzed pins",datetime.now(), start_time)
|
||||
|
||||
def find_blockages(self):
|
||||
"""
|
||||
|
|
@ -160,48 +155,32 @@ class router(router_tech):
|
|||
|
||||
# This will get all shapes as blockages and convert to grid units
|
||||
# This ignores shapes that were pins
|
||||
#start_time = datetime.now()
|
||||
self.find_blockages()
|
||||
#print_time("Find blockags",datetime.now(), start_time)
|
||||
|
||||
# Convert the blockages to grid units
|
||||
#start_time = datetime.now()
|
||||
self.convert_blockages()
|
||||
#print_time("Find blockags",datetime.now(), start_time)
|
||||
|
||||
# This will convert the pins to grid units
|
||||
# It must be done after blockages to ensure no DRCs between expanded pins and blocked grids
|
||||
#start_time = datetime.now()
|
||||
for pin in pin_list:
|
||||
self.convert_pins(pin)
|
||||
#print_time("Convert pins",datetime.now(), start_time)
|
||||
|
||||
#start_time = datetime.now()
|
||||
for pin in pin_list:
|
||||
self.combine_adjacent_pins(pin)
|
||||
#print_time("Combine pins",datetime.now(), start_time)
|
||||
#self.write_debug_gds("debug_combine_pins.gds",stop_program=True)
|
||||
#for pin in pin_list:
|
||||
# self.combine_adjacent_pins(pin)
|
||||
|
||||
# Separate any adjacent grids of differing net names to prevent wide metal DRC violations
|
||||
# Separate any adjacent grids of differing net names that overlap
|
||||
# Must be done before enclosing pins
|
||||
#start_time = datetime.now()
|
||||
self.separate_adjacent_pins(self.supply_rail_space_width)
|
||||
#print_time("Separate pins",datetime.now(), start_time)
|
||||
# For debug
|
||||
#self.separate_adjacent_pins(1)
|
||||
self.separate_adjacent_pins(0)
|
||||
|
||||
# Enclose the continguous grid units in a metal rectangle to fix some DRCs
|
||||
#start_time = datetime.now()
|
||||
self.enclose_pins()
|
||||
#print_time("Enclose pins",datetime.now(), start_time)
|
||||
#self.write_debug_gds("debug_enclose_pins.gds",stop_program=True)
|
||||
|
||||
|
||||
def combine_adjacent_pins(self, pin_name):
|
||||
"""
|
||||
Find pins that have adjacent routing tracks and merge them into a
|
||||
single pin_group. The pins themselves may not be touching, but
|
||||
enclose_pis in the next step will ensure they are touching.
|
||||
enclose_pins in the next step will ensure they are touching.
|
||||
"""
|
||||
debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
|
||||
# Find all adjacencies
|
||||
|
|
@ -279,48 +258,50 @@ class router(router_tech):
|
|||
debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2))
|
||||
for index1,pg1 in enumerate(self.pin_groups[pin_name1]):
|
||||
for index2,pg2 in enumerate(self.pin_groups[pin_name2]):
|
||||
# FIgXME: Use separation distance and edge grids only
|
||||
grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation)
|
||||
adj_grids = pg1.adjacent_grids(pg2, separation)
|
||||
# These should have the same length, so...
|
||||
if len(grids_g1)>0:
|
||||
debug.info(3,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2))
|
||||
self.remove_adjacent_grid(pg1, grids_g1, pg2, grids_g2)
|
||||
if len(adj_grids)>0:
|
||||
debug.info(3,"Adjacent grids {0} {1} adj={2}".format(index1,index2,adj_grids))
|
||||
self.remove_adjacent_grid(pg1, pg2, adj_grids)
|
||||
|
||||
def remove_adjacent_grid(self, pg1, grids1, pg2, grids2):
|
||||
def remove_adjacent_grid(self, pg1, pg2, adj_grids):
|
||||
"""
|
||||
Remove one of the adjacent grids in a heuristic manner.
|
||||
This will try to keep the groups similar sized by removing from the bigger group.
|
||||
"""
|
||||
# Determine the bigger and smaller group
|
||||
|
||||
if pg1.size()>pg2.size():
|
||||
bigger = pg1
|
||||
bigger_grids = grids1
|
||||
smaller = pg2
|
||||
smaller_grids = grids2
|
||||
else:
|
||||
bigger = pg2
|
||||
bigger_grids = grids2
|
||||
smaller = pg1
|
||||
smaller_grids = grids1
|
||||
|
||||
# First, see if we can remove grids that are in the secondary grids
|
||||
# i.e. they aren't necessary to the pin grids
|
||||
if bigger_grids.issubset(bigger.secondary_grids):
|
||||
debug.info(3,"Removing {} from bigger {}".format(str(bigger_grids), bigger))
|
||||
bigger.grids.difference_update(bigger_grids)
|
||||
self.blocked_grids.update(bigger_grids)
|
||||
return
|
||||
elif smaller_grids.issubset(smaller.secondary_grids):
|
||||
debug.info(3,"Removing {} from smaller {}".format(str(smaller_grids), smaller))
|
||||
smaller.grids.difference_update(smaller_grids)
|
||||
self.blocked_grids.update(smaller_grids)
|
||||
return
|
||||
for adj in adj_grids:
|
||||
|
||||
# If that fails, just randomly remove from the bigger one and give a warning.
|
||||
# This might fail later.
|
||||
debug.warning("Removing arbitrary grids from a pin group {} {}".format(bigger, bigger_grids))
|
||||
debug.check(len(bigger.grids)>len(bigger_grids),"Zero size pin group after adjacency removal {} {}".format(bigger, bigger_grids))
|
||||
bigger.grids.difference_update(bigger_grids)
|
||||
self.blocked_grids.update(bigger_grids)
|
||||
|
||||
# If the adjacent grids are a subset of the secondary grids (i.e. not necessary)
|
||||
# remove them from each
|
||||
if adj in bigger.secondary_grids:
|
||||
debug.info(3,"Removing {} from bigger secondary {}".format(adj, bigger))
|
||||
bigger.grids.remove(adj)
|
||||
bigger.secondary_grids.remove(adj)
|
||||
self.blocked_grids.add(adj)
|
||||
elif adj in smaller.secondary_grids:
|
||||
debug.info(3,"Removing {} from smaller secondary {}".format(adj, smaller))
|
||||
smaller.grids.remove(adj)
|
||||
smaller.secondary_grids.remove(adj)
|
||||
self.blocked_grids.add(adj)
|
||||
else:
|
||||
# If we couldn't remove from a secondary grid, we must remove from the primary
|
||||
# grid of at least one pin
|
||||
if adj in bigger.grids:
|
||||
debug.info(3,"Removing {} from bigger primary {}".format(adj, bigger))
|
||||
bigger.grids.remove(adj)
|
||||
elif adj in smaller.grids:
|
||||
debug.info(3,"Removing {} from smaller primary {}".format(adj, smaller))
|
||||
smaller.grids.remove(adj)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -359,17 +340,6 @@ class router(router_tech):
|
|||
self.set_blockages(blockage_grids,False)
|
||||
|
||||
|
||||
# def translate_coordinates(self, coord, mirr, angle, xyShift):
|
||||
# """
|
||||
# Calculate coordinates after flip, rotate, and shift
|
||||
# """
|
||||
# coordinate = []
|
||||
# for item in coord:
|
||||
# x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0])
|
||||
# y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1])
|
||||
# coordinate += [(x, y)]
|
||||
# return coordinate
|
||||
|
||||
def convert_shape_to_units(self, shape):
|
||||
"""
|
||||
Scale a shape (two vector list) to user units
|
||||
|
|
@ -496,11 +466,6 @@ class router(router_tech):
|
|||
# and the track points are at the center
|
||||
ll = ll.round()
|
||||
ur = ur.round()
|
||||
# if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5:
|
||||
# debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur))
|
||||
# debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur))
|
||||
# pin=self.convert_track_to_shape(ll)
|
||||
# debug.info(0,"Pin {}".format(pin))
|
||||
return [ll,ur]
|
||||
|
||||
def convert_pin_to_tracks(self, pin_name, pin, expansion=0):
|
||||
|
|
@ -515,36 +480,24 @@ class router(router_tech):
|
|||
# scale the size bigger to include neaby tracks
|
||||
ll=ll.scale(self.track_factor).floor()
|
||||
ur=ur.scale(self.track_factor).ceil()
|
||||
#print(pin)
|
||||
|
||||
# Keep tabs on tracks with sufficient and insufficient overlap
|
||||
sufficient_list = set()
|
||||
insufficient_list = set()
|
||||
|
||||
|
||||
zindex=self.get_zindex(pin.layer_num)
|
||||
for x in range(int(ll[0])+expansion,int(ur[0])+1+expansion):
|
||||
for y in range(int(ll[1]+expansion),int(ur[1])+1+expansion):
|
||||
(full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex))
|
||||
(full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex))
|
||||
if full_overlap:
|
||||
sufficient_list.update([full_overlap])
|
||||
if partial_overlap:
|
||||
insufficient_list.update([partial_overlap])
|
||||
debug.info(4,"Converting [ {0} , {1} ] full={2} partial={3}".format(x,y, full_overlap, partial_overlap))
|
||||
debug.info(2,"Converting [ {0} , {1} ] full={2}".format(x,y, full_overlap))
|
||||
|
||||
# Remove the blocked grids
|
||||
sufficient_list.difference_update(self.blocked_grids)
|
||||
insufficient_list.difference_update(self.blocked_grids)
|
||||
|
||||
if len(sufficient_list)>0:
|
||||
return sufficient_list
|
||||
elif expansion==0 and len(insufficient_list)>0:
|
||||
best_pin = self.get_all_offgrid_pin(pin, insufficient_list)
|
||||
#print(best_pin)
|
||||
return best_pin
|
||||
elif expansion>0:
|
||||
nearest_pin = self.get_furthest_offgrid_pin(pin, insufficient_list)
|
||||
return nearest_pin
|
||||
else:
|
||||
return set()
|
||||
# Return all grids with any potential overlap (sufficient or not)
|
||||
return (sufficient_list,insufficient_list)
|
||||
|
||||
|
||||
def get_all_offgrid_pin(self, pin, insufficient_list):
|
||||
"""
|
||||
|
|
@ -568,7 +521,6 @@ class router(router_tech):
|
|||
"""
|
||||
Find a list of the single pin with the most overlap.
|
||||
"""
|
||||
#print("INSUFFICIENT LIST",insufficient_list)
|
||||
# Find the coordinate with the most overlap
|
||||
best_coord = None
|
||||
best_overlap = -math.inf
|
||||
|
|
@ -589,7 +541,6 @@ class router(router_tech):
|
|||
Get a grid cell that is the furthest from the blocked grids.
|
||||
"""
|
||||
|
||||
#print("INSUFFICIENT LIST",insufficient_list)
|
||||
# Find the coordinate with the most overlap
|
||||
best_coord = None
|
||||
best_dist = math.inf
|
||||
|
|
@ -606,7 +557,6 @@ class router(router_tech):
|
|||
Given a pin and a list of grid cells (probably non-overlapping),
|
||||
return the nearest grid cell (center to center).
|
||||
"""
|
||||
#print("INSUFFICIENT LIST",insufficient_list)
|
||||
# Find the coordinate with the most overlap
|
||||
best_coord = None
|
||||
best_dist = math.inf
|
||||
|
|
@ -622,30 +572,32 @@ class router(router_tech):
|
|||
|
||||
def convert_pin_coord_to_tracks(self, pin, coord):
|
||||
"""
|
||||
Given a pin and a track coordinate, determine if the pin overlaps enough.
|
||||
If it does, add additional metal to make the pin "on grid".
|
||||
If it doesn't, add it to the blocked grid list.
|
||||
Return all tracks that an inflated pin overlaps
|
||||
"""
|
||||
|
||||
(width, spacing) = self.get_layer_width_space(coord.z)
|
||||
|
||||
# This is the rectangle if we put a pin in the center of the track
|
||||
track_pin = self.convert_track_to_pin(coord)
|
||||
# This is using the full track shape rather than a single track pin shape
|
||||
# because we will later patch a connector if there isn't overlap.
|
||||
track_pin = self.convert_track_to_shape_pin(coord)
|
||||
|
||||
# This is the normal pin inflated by a minimum design rule
|
||||
inflated_pin = pin_layout(pin.name, pin.inflate(0.5*self.track_space), pin.layer)
|
||||
|
||||
overlap_length = pin.overlap_length(track_pin)
|
||||
|
||||
debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length))
|
||||
# If it overlaps by more than the min width DRC, we can just use the track
|
||||
if overlap_length==math.inf or snap_val_to_grid(overlap_length) >= snap_val_to_grid(width):
|
||||
debug.info(3," Overlap: {0} >? {1}".format(overlap_length,spacing))
|
||||
return (coord, None)
|
||||
# Otherwise, keep track of the partial overlap grids in case we need to patch it later.
|
||||
debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length))
|
||||
inflated_overlap_length = inflated_pin.overlap_length(track_pin)
|
||||
debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, inflated_pin.rect, track_pin, inflated_overlap_length))
|
||||
|
||||
# If it overlaps with the pin, it is sufficient
|
||||
if overlap_length==math.inf or overlap_length > 0:
|
||||
debug.info(2," Overlap: {0} >? {1}".format(overlap_length,0))
|
||||
return (coord,None)
|
||||
# If it overlaps with the inflated pin, it is partial
|
||||
elif inflated_overlap_length==math.inf or inflated_overlap_length > 0:
|
||||
debug.info(2," Partial overlap: {0} >? {1}".format(inflated_overlap_length,0))
|
||||
return (None,coord)
|
||||
else:
|
||||
debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,spacing))
|
||||
return (None, coord)
|
||||
|
||||
|
||||
|
||||
|
||||
debug.info(2," No overlap: {0} {1}".format(overlap_length,0))
|
||||
return (None,None)
|
||||
|
||||
|
||||
def convert_track_to_pin(self, track):
|
||||
|
|
@ -653,25 +605,34 @@ class router(router_tech):
|
|||
Convert a grid point into a rectangle shape that is centered
|
||||
track in the track and leaves half a DRC space in each direction.
|
||||
"""
|
||||
# space depends on which layer it is
|
||||
if self.get_layer(track[2])==self.horiz_layer_name:
|
||||
space = 0.5*self.horiz_layer_spacing
|
||||
else:
|
||||
space = 0.5*self.vert_layer_spacing
|
||||
|
||||
# calculate lower left
|
||||
x = track.x*self.track_width - 0.5*self.track_width + space
|
||||
y = track.y*self.track_width - 0.5*self.track_width + space
|
||||
x = track.x*self.track_width - 0.5*self.track_width + 0.5*self.track_space
|
||||
y = track.y*self.track_width - 0.5*self.track_width + 0.5*self.track_space
|
||||
ll = snap_to_grid(vector(x,y))
|
||||
|
||||
# calculate upper right
|
||||
x = track.x*self.track_width + 0.5*self.track_width - space
|
||||
y = track.y*self.track_width + 0.5*self.track_width - space
|
||||
x = track.x*self.track_width + 0.5*self.track_width - 0.5*self.track_space
|
||||
y = track.y*self.track_width + 0.5*self.track_width - 0.5*self.track_space
|
||||
ur = snap_to_grid(vector(x,y))
|
||||
|
||||
p = pin_layout("", [ll, ur], self.get_layer(track[2]))
|
||||
return p
|
||||
|
||||
def convert_track_to_shape_pin(self, track):
|
||||
"""
|
||||
Convert a grid point into a rectangle shape that occupies the entire centered
|
||||
track.
|
||||
"""
|
||||
# to scale coordinates to tracks
|
||||
x = track[0]*self.track_width - 0.5*self.track_width
|
||||
y = track[1]*self.track_width - 0.5*self.track_width
|
||||
# offset lowest corner object to to (-track halo,-track halo)
|
||||
ll = snap_to_grid(vector(x,y))
|
||||
ur = snap_to_grid(ll + vector(self.track_width,self.track_width))
|
||||
|
||||
p = pin_layout("", [ll, ur], self.get_layer(track[2]))
|
||||
return p
|
||||
|
||||
def convert_track_to_shape(self, track):
|
||||
"""
|
||||
Convert a grid point into a rectangle shape that occupies the entire centered
|
||||
|
|
@ -686,6 +647,23 @@ class router(router_tech):
|
|||
|
||||
return [ll,ur]
|
||||
|
||||
def convert_track_to_inflated_pin(self, track):
|
||||
"""
|
||||
Convert a grid point into a rectangle shape that is inflated by a half DRC space.
|
||||
"""
|
||||
# calculate lower left
|
||||
x = track.x*self.track_width - 0.5*self.track_width - 0.5*self.track_space
|
||||
y = track.y*self.track_width - 0.5*self.track_width - 0.5*self.track_space
|
||||
ll = snap_to_grid(vector(x,y))
|
||||
|
||||
# calculate upper right
|
||||
x = track.x*self.track_width + 0.5*self.track_width + 0.5*self.track_space
|
||||
y = track.y*self.track_width + 0.5*self.track_width + 0.5*self.track_space
|
||||
ur = snap_to_grid(vector(x,y))
|
||||
|
||||
p = pin_layout("", [ll, ur], self.get_layer(track[2]))
|
||||
return p
|
||||
|
||||
def analyze_pins(self, pin_name):
|
||||
"""
|
||||
Analyze the shapes of a pin and combine them into groups which are connected.
|
||||
|
|
@ -742,8 +720,6 @@ class router(router_tech):
|
|||
pg.enclose_pin()
|
||||
pg.add_enclosure(self.cell)
|
||||
|
||||
#self.write_debug_gds("pin_debug.gds", False)
|
||||
|
||||
def add_source(self, pin_name):
|
||||
"""
|
||||
This will mark the grids for all pin components as a source.
|
||||
|
|
@ -813,9 +789,6 @@ class router(router_tech):
|
|||
"""
|
||||
debug.info(4,"Set path: " + str(path))
|
||||
|
||||
# Keep track of path for future blockages
|
||||
#path.set_blocked()
|
||||
|
||||
# This is marked for debug
|
||||
path.set_path()
|
||||
|
||||
|
|
@ -877,8 +850,6 @@ class router(router_tech):
|
|||
Enclose the tracks from ll to ur in a single rectangle that meets
|
||||
the track DRC rules.
|
||||
"""
|
||||
# Get the layer information
|
||||
(width, space) = self.get_layer_width_space(zindex)
|
||||
layer = self.get_layer(zindex)
|
||||
|
||||
# This finds the pin shape enclosed by the track with DRC spacing on the sides
|
||||
|
|
@ -886,44 +857,10 @@ class router(router_tech):
|
|||
(abs_ll,unused) = pin.rect
|
||||
pin = self.convert_track_to_pin(ur)
|
||||
(unused,abs_ur) = pin.rect
|
||||
#print("enclose ll={0} ur={1}".format(ll,ur))
|
||||
#print("enclose ll={0} ur={1}".format(abs_ll,abs_ur))
|
||||
|
||||
pin = pin_layout(name, [abs_ll, abs_ur], layer)
|
||||
|
||||
return pin
|
||||
|
||||
|
||||
def compute_wide_enclosure(self, ll, ur, zindex, name=""):
|
||||
"""
|
||||
Enclose the tracks from ll to ur in a single rectangle that meets the track DRC rules.
|
||||
"""
|
||||
|
||||
# Find the pin enclosure of the whole track shape (ignoring DRCs)
|
||||
(abs_ll,unused) = self.convert_track_to_shape(ll)
|
||||
(unused,abs_ur) = self.convert_track_to_shape(ur)
|
||||
|
||||
# Get the layer information
|
||||
x_distance = abs(abs_ll.x-abs_ur.x)
|
||||
y_distance = abs(abs_ll.y-abs_ur.y)
|
||||
shape_width = min(x_distance, y_distance)
|
||||
shape_length = max(x_distance, y_distance)
|
||||
|
||||
# Get the DRC rule for the grid dimensions
|
||||
(width, space) = self.get_layer_width_space(zindex, shape_width, shape_length)
|
||||
layer = self.get_layer(zindex)
|
||||
|
||||
if zindex==0:
|
||||
spacing = vector(0.5*self.track_width, 0.5*space)
|
||||
else:
|
||||
spacing = vector(0.5*space, 0.5*self.track_width)
|
||||
# Compute the shape offsets with correct spacing
|
||||
new_ll = abs_ll + spacing
|
||||
new_ur = abs_ur - spacing
|
||||
pin = pin_layout(name, [new_ll, new_ur], layer)
|
||||
|
||||
return pin
|
||||
|
||||
|
||||
def contract_path(self,path):
|
||||
"""
|
||||
|
|
@ -963,8 +900,7 @@ class router(router_tech):
|
|||
self.add_route(path)
|
||||
|
||||
path_set = grid_utils.flatten_set(path)
|
||||
inflated_path = grid_utils.inflate_set(path_set,self.supply_rail_space_width)
|
||||
self.path_blockages.append(inflated_path)
|
||||
self.path_blockages.append(path_set)
|
||||
else:
|
||||
self.write_debug_gds("failed_route.gds")
|
||||
# clean up so we can try a reroute
|
||||
|
|
|
|||
|
|
@ -3,48 +3,62 @@ from contact import contact
|
|||
from pin_group import pin_group
|
||||
from vector import vector
|
||||
import debug
|
||||
import math
|
||||
|
||||
class router_tech:
|
||||
"""
|
||||
This is a class to hold the router tech constants.
|
||||
"""
|
||||
def __init__(self, layers):
|
||||
def __init__(self, layers, rail_track_width):
|
||||
"""
|
||||
Allows us to change the layers that we are routing on. First layer
|
||||
is always horizontal, middle is via, and last is always
|
||||
vertical.
|
||||
"""
|
||||
self.layers = layers
|
||||
self.rail_track_width = rail_track_width
|
||||
|
||||
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
|
||||
|
||||
# This is the minimum routed track spacing
|
||||
via_connect = contact(self.layers, (1, 1))
|
||||
self.max_via_size = max(via_connect.width,via_connect.height)
|
||||
|
||||
self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name))
|
||||
self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name))
|
||||
max_via_size = max(via_connect.width,via_connect.height)
|
||||
|
||||
self.horiz_layer_number = layer[self.horiz_layer_name]
|
||||
self.vert_layer_number = layer[self.vert_layer_name]
|
||||
|
||||
self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name))
|
||||
self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name))
|
||||
self.horiz_layer_number = layer[self.horiz_layer_name]
|
||||
|
||||
self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing
|
||||
self.vert_track_width = self.max_via_size + self.vert_layer_spacing
|
||||
if self.rail_track_width>1:
|
||||
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
|
||||
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
|
||||
|
||||
# For supplies, we will make the wire wider than the vias
|
||||
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
|
||||
self.horiz_layer_minwidth = max(self.horiz_layer_minwidth, max_via_size)
|
||||
|
||||
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
|
||||
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
|
||||
|
||||
else:
|
||||
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
|
||||
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
|
||||
|
||||
self.horiz_track_width = max_via_size + self.horiz_layer_spacing
|
||||
self.vert_track_width = max_via_size + self.vert_layer_spacing
|
||||
|
||||
# We'll keep horizontal and vertical tracks the same for simplicity.
|
||||
self.track_width = max(self.horiz_track_width,self.vert_track_width)
|
||||
debug.info(1,"Track width: "+str(self.track_width))
|
||||
|
||||
debug.info(1,"Track width: {:.3f}".format(self.track_width))
|
||||
self.track_space = max(self.horiz_layer_spacing,self.vert_layer_spacing)
|
||||
debug.info(1,"Track space: {:.3f}".format(self.track_space))
|
||||
self.track_wire = self.track_width - self.track_space
|
||||
debug.info(1,"Track wire width: {:.3f}".format(self.track_wire))
|
||||
|
||||
self.track_widths = vector([self.track_width] * 2)
|
||||
self.track_factor = vector([1/self.track_width] * 2)
|
||||
debug.info(2,"Track factor: {0}".format(self.track_factor))
|
||||
|
||||
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
|
||||
self.layer_widths = [self.track_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing]
|
||||
|
||||
|
||||
debug.info(2,"Track factor: {}".format(self.track_factor))
|
||||
|
||||
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
|
||||
self.layer_widths = [self.track_wire, 1, self.track_wire]
|
||||
|
||||
def get_zindex(self,layer_num):
|
||||
if layer_num==self.horiz_layer_number:
|
||||
return 0
|
||||
|
|
@ -76,4 +90,24 @@ class router_tech:
|
|||
|
||||
return (min_width,min_spacing)
|
||||
|
||||
|
||||
def get_supply_layer_width_space(self, zindex):
|
||||
"""
|
||||
These are the width and spacing of a supply layer given a supply rail
|
||||
of the given number of min wire widths.
|
||||
"""
|
||||
if zindex==1:
|
||||
layer_name = self.vert_layer_name
|
||||
elif zindex==0:
|
||||
layer_name = self.horiz_layer_name
|
||||
else:
|
||||
debug.error("Invalid zindex for track", -1)
|
||||
|
||||
min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf)
|
||||
|
||||
min_width = drc("minwidth_{0}".format(layer_name), self.rail_track_width*min_wire_width, math.inf)
|
||||
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.rail_track_width*min_wire_width, math.inf)
|
||||
|
||||
return (min_width,min_spacing)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,17 +24,16 @@ class supply_router(router):
|
|||
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)
|
||||
# Power rail width in minimum wire widths
|
||||
self.rail_track_width = 3
|
||||
|
||||
router.__init__(self, layers, design, gds_filename, self.rail_track_width)
|
||||
|
||||
# The list of supply rails (grid sets) that may be routed
|
||||
self.supply_rails = {}
|
||||
self.supply_rail_wires = {}
|
||||
# This is the same as above but as a sigle set for the all the rails
|
||||
self.supply_rail_tracks = {}
|
||||
self.supply_rail_wire_tracks = {}
|
||||
|
||||
# Power rail width in grid units.
|
||||
self.rail_track_width = 2
|
||||
|
||||
|
||||
|
||||
|
|
@ -65,17 +64,10 @@ class supply_router(router):
|
|||
# but this is simplest for now.
|
||||
self.create_routing_grid()
|
||||
|
||||
# Compute the grid dimensions
|
||||
self.compute_supply_rail_dimensions()
|
||||
|
||||
# Get the pin shapes
|
||||
#start_time = datetime.now()
|
||||
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
|
||||
#print_time("Pins and blockages",datetime.now(), start_time)
|
||||
#self.write_debug_gds("pin_enclosures.gds",stop_program=True)
|
||||
|
||||
# Add the supply rails in a mesh network and connect H/V with vias
|
||||
#start_time = datetime.now()
|
||||
# Block everything
|
||||
self.prepare_blockages(self.gnd_name)
|
||||
# Determine the rail locations
|
||||
|
|
@ -85,23 +77,15 @@ class supply_router(router):
|
|||
self.prepare_blockages(self.vdd_name)
|
||||
# Determine the rail locations
|
||||
self.route_supply_rails(self.vdd_name,1)
|
||||
#self.write_debug_gds("debug_rails.gds",stop_program=True)
|
||||
#print_time("Supply rails",datetime.now(), start_time)
|
||||
|
||||
#start_time = datetime.now()
|
||||
self.route_simple_overlaps(vdd_name)
|
||||
self.route_simple_overlaps(gnd_name)
|
||||
#print_time("Simple overlaps",datetime.now(), start_time)
|
||||
#self.write_debug_gds("debug_simple_route.gds",stop_program=False)
|
||||
|
||||
# Route the supply pins to the supply rails
|
||||
# Route vdd first since we want it to be shorter
|
||||
#start_time = datetime.now()
|
||||
self.route_pins_to_rails(vdd_name)
|
||||
self.route_pins_to_rails(gnd_name)
|
||||
#print_time("Routing",datetime.now(), start_time)
|
||||
#self.write_debug_gds("debug_pin_routes.gds",stop_program=True)
|
||||
|
||||
|
||||
#self.write_debug_gds("final.gds",False)
|
||||
|
||||
return True
|
||||
|
|
@ -116,9 +100,7 @@ class supply_router(router):
|
|||
debug.info(1,"Routing simple overlap pins for {0}".format(pin_name))
|
||||
|
||||
# These are the wire tracks
|
||||
wire_tracks = self.supply_rail_wire_tracks[pin_name]
|
||||
# These are the wire and space tracks
|
||||
supply_tracks = self.supply_rail_tracks[pin_name]
|
||||
wire_tracks = self.supply_rail_tracks[pin_name]
|
||||
|
||||
for pg in self.pin_groups[pin_name]:
|
||||
if pg.is_routed():
|
||||
|
|
@ -129,13 +111,10 @@ class supply_router(router):
|
|||
if len(overlap_grids)>0:
|
||||
pg.set_routed()
|
||||
continue
|
||||
|
||||
# Else, if we overlap some of the space track, we can patch it with an enclosure
|
||||
common_set = supply_tracks & pg.grids
|
||||
if len(common_set)>0:
|
||||
pg.create_simple_overlap_enclosure(common_set)
|
||||
pg.add_enclosure(self.cell)
|
||||
|
||||
# Else, if we overlap some of the space track, we can patch it with an enclosure
|
||||
#pg.create_simple_overlap_enclosure(pg.grids)
|
||||
#pg.add_enclosure(self.cell)
|
||||
|
||||
|
||||
|
||||
|
|
@ -146,7 +125,7 @@ class supply_router(router):
|
|||
NOTE: It is still possible though unlikely that there are disconnected groups of rails.
|
||||
"""
|
||||
|
||||
all_rails = self.supply_rail_wires[name]
|
||||
all_rails = self.supply_rails[name]
|
||||
|
||||
connections = set()
|
||||
via_areas = []
|
||||
|
|
@ -186,8 +165,8 @@ class supply_router(router):
|
|||
# the indices to determine a rail is connected to another
|
||||
# the overlap area for placement of a via
|
||||
overlap = new_r1 & new_r2
|
||||
if len(overlap) >= self.supply_rail_wire_width**2:
|
||||
debug.info(3,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap))
|
||||
if len(overlap) >= 1:
|
||||
debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap))
|
||||
connections.update([i1,i2])
|
||||
via_areas.append(overlap)
|
||||
|
||||
|
|
@ -196,7 +175,7 @@ class supply_router(router):
|
|||
ll = grid_utils.get_lower_left(area)
|
||||
ur = grid_utils.get_upper_right(area)
|
||||
center = (ll + ur).scale(0.5,0.5,0)
|
||||
self.add_via(center,self.rail_track_width)
|
||||
self.add_via(center,1)
|
||||
|
||||
# Determien which indices were not connected to anything above
|
||||
missing_indices = set([x for x in range(len(self.supply_rails[name]))])
|
||||
|
|
@ -209,7 +188,6 @@ class supply_router(router):
|
|||
ur = grid_utils.get_upper_right(all_rails[rail_index])
|
||||
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur))
|
||||
self.supply_rails[name].pop(rail_index)
|
||||
self.supply_rail_wires[name].pop(rail_index)
|
||||
|
||||
# Make the supply rails into a big giant set of grids for easy blockages.
|
||||
# Must be done after we determine which ones are connected.
|
||||
|
|
@ -226,7 +204,7 @@ class supply_router(router):
|
|||
ll = grid_utils.get_lower_left(rail)
|
||||
ur = grid_utils.get_upper_right(rail)
|
||||
z = ll.z
|
||||
pin = self.compute_wide_enclosure(ll, ur, z, name)
|
||||
pin = self.compute_pin_enclosure(ll, ur, z, name)
|
||||
debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin))
|
||||
self.cell.add_layout_pin(text=name,
|
||||
layer=pin.layer,
|
||||
|
|
@ -234,43 +212,6 @@ class supply_router(router):
|
|||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
def compute_supply_rail_dimensions(self):
|
||||
"""
|
||||
Compute the supply rail dimensions including wide metal spacing rules.
|
||||
"""
|
||||
|
||||
self.max_yoffset = self.rg.ur.y
|
||||
self.max_xoffset = self.rg.ur.x
|
||||
|
||||
# Longest length is conservative
|
||||
rail_length = max(self.max_yoffset,self.max_xoffset)
|
||||
# Convert the number of tracks to dimensions to get the design rule spacing
|
||||
rail_width = self.track_width*self.rail_track_width
|
||||
|
||||
# Get the conservative width and spacing of the top rails
|
||||
(horizontal_width, horizontal_space) = self.get_layer_width_space(0, rail_width, rail_length)
|
||||
(vertical_width, vertical_space) = self.get_layer_width_space(1, rail_width, rail_length)
|
||||
width = max(horizontal_width, vertical_width)
|
||||
space = max(horizontal_space, vertical_space)
|
||||
|
||||
# This is the supply rail pitch in terms of routing grids
|
||||
# i.e. a rail of self.rail_track_width needs this many tracks including
|
||||
# space
|
||||
track_pitch = self.rail_track_width*width + space
|
||||
|
||||
# Determine the pitch (in tracks) of the rail wire + spacing
|
||||
self.supply_rail_width = math.ceil(track_pitch/self.track_width)
|
||||
debug.info(1,"Rail step: {}".format(self.supply_rail_width))
|
||||
|
||||
# Conservatively determine the number of tracks that the rail actually occupies
|
||||
space_tracks = math.ceil(space/self.track_width)
|
||||
self.supply_rail_wire_width = self.supply_rail_width - space_tracks
|
||||
debug.info(1,"Rail wire tracks: {}".format(self.supply_rail_wire_width))
|
||||
total_space = self.supply_rail_width - self.supply_rail_wire_width
|
||||
self.supply_rail_space_width = math.floor(0.5*total_space)
|
||||
debug.info(1,"Rail space tracks: {} (on both sides)".format(self.supply_rail_space_width))
|
||||
|
||||
|
||||
def compute_supply_rails(self, name, supply_number):
|
||||
"""
|
||||
Compute the unblocked locations for the horizontal and vertical supply rails.
|
||||
|
|
@ -279,16 +220,20 @@ class supply_router(router):
|
|||
"""
|
||||
|
||||
self.supply_rails[name]=[]
|
||||
self.supply_rail_wires[name]=[]
|
||||
|
||||
start_offset = supply_number*self.supply_rail_width
|
||||
max_yoffset = self.rg.ur.y
|
||||
max_xoffset = self.rg.ur.x
|
||||
min_yoffset = self.rg.ll.y
|
||||
min_xoffset = self.rg.ll.x
|
||||
|
||||
start_offset = min_yoffset + supply_number
|
||||
|
||||
# Horizontal supply rails
|
||||
for offset in range(start_offset, self.max_yoffset, 2*self.supply_rail_width):
|
||||
for offset in range(start_offset, max_yoffset, 2):
|
||||
# Seed the function at the location with the given width
|
||||
wave = [vector3d(0,offset+i,0) for i in range(self.supply_rail_width)]
|
||||
wave = [vector3d(min_xoffset,offset,0)]
|
||||
# While we can keep expanding east in this horizontal track
|
||||
while wave and wave[0].x < self.max_xoffset:
|
||||
while wave and wave[0].x < max_xoffset:
|
||||
added_rail = self.find_supply_rail(name, wave, direction.EAST)
|
||||
if not added_rail:
|
||||
# Just seed with the next one
|
||||
|
|
@ -299,11 +244,11 @@ class supply_router(router):
|
|||
|
||||
# Vertical supply rails
|
||||
max_offset = self.rg.ur.x
|
||||
for offset in range(start_offset, self.max_xoffset, 2*self.supply_rail_width):
|
||||
for offset in range(start_offset, max_xoffset, 2):
|
||||
# Seed the function at the location with the given width
|
||||
wave = [vector3d(offset+i,0,1) for i in range(self.supply_rail_width)]
|
||||
wave = [vector3d(offset,min_yoffset,1)]
|
||||
# While we can keep expanding north in this vertical track
|
||||
while wave and wave[0].y < self.max_yoffset:
|
||||
while wave and wave[0].y < max_yoffset:
|
||||
added_rail = self.find_supply_rail(name, wave, direction.NORTH)
|
||||
if not added_rail:
|
||||
# Just seed with the next one
|
||||
|
|
@ -378,11 +323,6 @@ class supply_router(router):
|
|||
if len(wave_path)>=4*self.rail_track_width:
|
||||
grid_set = wave_path.get_grids()
|
||||
self.supply_rails[name].append(grid_set)
|
||||
|
||||
start_wire_index = self.supply_rail_space_width
|
||||
end_wire_index = self.supply_rail_width - self.supply_rail_space_width
|
||||
wire_set = wave_path.get_wire_grids(start_wire_index,end_wire_index)
|
||||
self.supply_rail_wires[name].append(wire_set)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
@ -417,10 +357,6 @@ class supply_router(router):
|
|||
rail_set.update(rail)
|
||||
self.supply_rail_tracks[pin_name] = rail_set
|
||||
|
||||
wire_set = set()
|
||||
for rail in self.supply_rail_wires[pin_name]:
|
||||
wire_set.update(rail)
|
||||
self.supply_rail_wire_tracks[pin_name] = wire_set
|
||||
|
||||
|
||||
def route_pins_to_rails(self, pin_name):
|
||||
|
|
@ -465,7 +401,7 @@ class supply_router(router):
|
|||
"""
|
||||
debug.info(4,"Add supply rail target {}".format(pin_name))
|
||||
# Add the wire itself as the target
|
||||
self.rg.set_target(self.supply_rail_wire_tracks[pin_name])
|
||||
self.rg.set_target(self.supply_rail_tracks[pin_name])
|
||||
# But unblock all the rail tracks including the space
|
||||
self.rg.set_blocked(self.supply_rail_tracks[pin_name],False)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ class no_blockages_test(openram_test):
|
|||
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
|
||||
|
|
@ -33,9 +36,6 @@ class no_blockages_test(openram_test):
|
|||
sram = sram(c, "sram1")
|
||||
cell = sram.s
|
||||
|
||||
layer_stack =("metal3","via3","metal4")
|
||||
rtr=router(layer_stack, cell)
|
||||
self.assertTrue(rtr.route())
|
||||
self.local_check(cell,True)
|
||||
|
||||
# fails if there are any DRC errors on any cells
|
||||
|
|
|
|||
|
|
@ -21,11 +21,6 @@ class sram_1bank(sram_base):
|
|||
def __init__(self, name, sram_config):
|
||||
sram_base.__init__(self, name, sram_config)
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
sram_base.create_netlist(self)
|
||||
self.create_modules()
|
||||
|
||||
def create_modules(self):
|
||||
"""
|
||||
This adds the modules for a single bank SRAM with control
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import sys
|
|||
import datetime
|
||||
import getpass
|
||||
import debug
|
||||
from datetime import datetime
|
||||
from importlib import reload
|
||||
from vector import vector
|
||||
from globals import OPTS, print_time
|
||||
|
|
@ -65,36 +66,52 @@ class sram_base(design):
|
|||
|
||||
def create_netlist(self):
|
||||
""" Netlist creation """
|
||||
|
||||
|
||||
start_time = datetime.now()
|
||||
|
||||
# Must create the control logic before pins to get the pins
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
|
||||
# This is for the lib file if we don't create layout
|
||||
self.width=0
|
||||
self.height=0
|
||||
|
||||
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Netlisting",datetime.now(), start_time)
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
""" Layout creation """
|
||||
start_time = datetime.now()
|
||||
self.place_instances()
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Placement",datetime.now(), start_time)
|
||||
|
||||
start_time = datetime.now()
|
||||
self.route_layout()
|
||||
self.route_supplies()
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Routing",datetime.now(), start_time)
|
||||
|
||||
self.add_lvs_correspondence_points()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
|
||||
# Must be done after offsetting lower-left
|
||||
self.route_supplies()
|
||||
|
||||
highest_coord = self.find_highest_coords()
|
||||
self.width = highest_coord[0]
|
||||
self.height = highest_coord[1]
|
||||
|
||||
|
||||
start_time = datetime.now()
|
||||
self.DRC_LVS(final_verification=True)
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Verification",datetime.now(), start_time)
|
||||
|
||||
|
||||
def create_modules(self):
|
||||
debug.error("Must override pure virtual function.",-1)
|
||||
|
||||
def route_supplies(self):
|
||||
""" Route the supply grid and connect the pins to them. """
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import globals
|
|||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error")
|
||||
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error")
|
||||
class psram_1bank_2mux_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ class sram_1bank_2mux_1rw_1r_test(openram_test):
|
|||
num_banks=1)
|
||||
|
||||
c.words_per_row=2
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
a = sram(c, "sram")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ class sram_1bank_2mux_test(openram_test):
|
|||
num_banks=1)
|
||||
|
||||
c.words_per_row=2
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
a = sram(c, "sram")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ class sram_1bank_4mux_test(openram_test):
|
|||
num_banks=1)
|
||||
|
||||
c.words_per_row=4
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
a = sram(c, "sram")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ class sram_1bank_8mux_1rw_1r_test(openram_test):
|
|||
num_banks=1)
|
||||
|
||||
c.words_per_row=8
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
a = sram(c, "sram")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ class sram_1bank_8mux_test(openram_test):
|
|||
num_banks=1)
|
||||
|
||||
c.words_per_row=8
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
a = sram(c, "sram")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ class sram_1bank_nomux_1rw_1r_test(openram_test):
|
|||
num_banks=1)
|
||||
|
||||
c.words_per_row=1
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
a = sram(c, "sram")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ class sram_1bank_nomux_test(openram_test):
|
|||
num_banks=1)
|
||||
|
||||
c.words_per_row=1
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
a = sram(c, "sram")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -42,13 +42,14 @@ class openram_test(openram_test):
|
|||
|
||||
|
||||
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
|
||||
|
||||
cmd = "python3 {0}/openram.py -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(OPENRAM_HOME,
|
||||
out_file,
|
||||
out_path,
|
||||
verbosity,
|
||||
OPTS.tech_name,
|
||||
out_path)
|
||||
# Always perform code coverage
|
||||
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
|
||||
cmd = "{0} -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(exe_name,
|
||||
out_file,
|
||||
out_path,
|
||||
verbosity,
|
||||
OPTS.tech_name,
|
||||
out_path)
|
||||
debug.info(1, cmd)
|
||||
os.system(cmd)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue