diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 57facdb8..0d4852d8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,6 +22,7 @@ jobs:
run: |
export OPENRAM_HOME="${{ github.workspace }}/compiler"
export OPENRAM_TECH="${{ github.workspace }}/technology"
+ export FREEPDK45="~/FreePDK45"
#cd $OPENRAM_HOME/.. && make pdk && make install
#export OPENRAM_TMP="${{ github.workspace }}/scn4me_subm_temp"
#python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 12 -t scn4m_subm
diff --git a/README.md b/README.md
index 688f22da..4a3ec69a 100644
--- a/README.md
+++ b/README.md
@@ -146,16 +146,18 @@ To run a specific test:
```
ce openram/compiler/tests
make 05_bitcell_array_test
-
+```
To run a specific technology:
-
+```
cd openram/compiler/tests
TECHS=scn4m_subm make 05_bitcell_array_test
+```
To increase the verbosity of the test, add one (or more) -v options and
pass it as an argument to OpenRAM:
```
ARGS="-v" make 05_bitcell_array_test
+```
# Get Involved
@@ -173,6 +175,7 @@ ARGS="-v" make 05_bitcell_array_test
+ [OpenRAM Slack Workspace][Slack]
+ [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe])
+ [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe])
++ @mrg@fostodon.org
# License
@@ -215,4 +218,6 @@ If I forgot to add you, please let me know!
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf
[Sky130]: https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git
-[Slack]: https://join.slack.com/t/openram/shared_invite/enQtNDgxMjc3NzU5NTI1LWZiYWMwNjNkZThmYTdkODc3NDE1NDhjNzUxNDhmMDQ4ZTM3NDgwNWFlNjM5NWFiZDkyMzBlNzc1NTg3ZjllNTY
+[Slack]: https://join.slack.com/t/openram/shared_invite/zt-onim74ue-zlttW5XI30xvdBlJGJF6JA
+
+
diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py
index b2697472..0102bf24 100644
--- a/compiler/base/custom_cell_properties.py
+++ b/compiler/base/custom_cell_properties.py
@@ -156,6 +156,16 @@ class bitcell(cell):
self.storage_nets = storage_nets
+ self.wl_layer = "m1"
+ self.wl_dir = "H"
+ self.bl_layer = "m2"
+ self.bl_dir = "V"
+
+ self.vdd_layer = "m1"
+ self.vdd_dir = "H"
+ self.gnd_layer = "m1"
+ self.gnd_dir = "H"
+
class cell_properties():
"""
diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py
index fe295273..5e9bec3f 100644
--- a/compiler/base/hierarchy_design.py
+++ b/compiler/base/hierarchy_design.py
@@ -8,6 +8,7 @@
import hierarchy_layout
import hierarchy_spice
import debug
+import os
from globals import OPTS
@@ -22,6 +23,15 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.drc_errors = "skipped"
self.lvs_errors = "skipped"
+ # Flag for library cells which is recomputed in hierachy_layout
+ gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds"
+ is_library_cell = os.path.isfile(gds_file)
+ # Uniquify names to address the flat GDS namespace
+ # except for the top/output name
+ if not is_library_cell and name != OPTS.output_name and not name.startswith(OPTS.output_name):
+ name = OPTS.output_name + "_" + name
+ cell_name = name
+
hierarchy_spice.spice.__init__(self, name, cell_name)
hierarchy_layout.layout.__init__(self, name, cell_name)
self.init_graph_params()
diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py
index b3eff27d..68476a00 100644
--- a/compiler/base/hierarchy_layout.py
+++ b/compiler/base/hierarchy_layout.py
@@ -42,6 +42,7 @@ class layout():
self.cell_name = cell_name
self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds"
+ self.is_library_cell = os.path.isfile(self.gds_file)
self.width = None
self.height = None
@@ -60,8 +61,6 @@ class layout():
self.pin_map = {}
# List of modules we have already visited
self.visited = []
- # Flag for library cells
- self.is_library_cell = False
self.gds_read()
@@ -408,8 +407,8 @@ class layout():
"""
pins = instance.get_pins(pin_name)
- debug.check(len(pins) > 0,
- "Could not find pin {}".format(pin_name))
+ if len(pins) == 0:
+ debug.warning("Could not find pin {0} on {1}".format(pin_name, instance.mod.name))
for pin in pins:
if new_name == "":
@@ -420,19 +419,200 @@ class layout():
pin.width(),
pin.height())
- def copy_layout_pins(self, instance, prefix=""):
- """
- Create a copied version of the layout pin at the current level.
- You can optionally rename the pin to a new name.
- """
- for pin_name in self.pin_map.keys():
- self.copy_layout_pin(instance, pin_name, prefix + pin_name)
- def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy"):
+ def connect_row_locs(self, from_layer, to_layer, locs, name=None, full=False):
+ """
+ Connects left/right rows that are aligned on the given layer.
+ """
+ bins = {}
+ for loc in locs:
+ y = pin.y
+ try:
+ bins[y].append(loc)
+ except KeyError:
+ bins[y] = [loc]
+
+ for y, v in bins.items():
+ # Not enough to route a pin, so just copy them
+ if len(v) < 2:
+ continue
+
+ if full:
+ left_x = 0
+ right_x = self.width
+ else:
+ left_x = min([loc.x for loc in v])
+ right_x = max([loc.x for loc in v])
+
+ left_pos = vector(left_x, y)
+ right_pos = vector(right_x, y)
+
+ # Make sure to add vias to the new route
+ for loc in v:
+ self.add_via_stack_center(from_layer=from_layer,
+ to_layer=to_layer,
+ offset=loc,
+ min_area=True)
+
+ if name:
+ self.add_layout_pin_segment_center(text=name,
+ layer=to_layer,
+ start=left_pos,
+ end=right_pos)
+ else:
+ self.add_segment_center(layer=to_layer,
+ start=left_pos,
+ end=right_pos)
+
+ def connect_row_pins(self, layer, pins, name=None, full=False):
+ """
+ Connects left/right rows that are aligned.
+ """
+ bins = {}
+ for pin in pins:
+ y = pin.cy()
+ try:
+ bins[y].append(pin)
+ except KeyError:
+ bins[y] = [pin]
+
+ for y, v in bins.items():
+ # Not enough to route a pin, so just copy them
+ if len(v) < 2:
+ continue
+
+ if full:
+ left_x = 0
+ right_x = self.width
+ else:
+ left_x = min([pin.lx() for pin in v])
+ right_x = max([pin.rx() for pin in v])
+
+ left_pos = vector(left_x, y)
+ right_pos = vector(right_x, y)
+
+ # Make sure to add vias to the new route
+ for pin in v:
+ self.add_via_stack_center(from_layer=pin.layer,
+ to_layer=layer,
+ offset=pin.center(),
+ min_area=True)
+
+ if name:
+ self.add_layout_pin_segment_center(text=name,
+ layer=layer,
+ start=left_pos,
+ end=right_pos)
+ else:
+ self.add_segment_center(layer=layer,
+ start=left_pos,
+ end=right_pos)
+
+ def connect_col_locs(self, from_layer, to_layer, locs, name=None, full=False):
+ """
+ Connects top/bot columns that are aligned.
+ """
+ bins = {}
+ for loc in locs:
+ x = loc.x
+ try:
+ bins[x].append(loc)
+ except KeyError:
+ bins[x] = [loc]
+
+ for x, v in bins.items():
+ # Not enough to route a pin, so just copy them
+ if len(v) < 2:
+ continue
+
+ if full:
+ bot_y = 0
+ top_y = self.height
+ else:
+ bot_y = min([loc.y for loc in v])
+ top_y = max([loc.y for loc in v])
+
+ top_pos = vector(x, top_y)
+ bot_pos = vector(x, bot_y)
+
+ # Make sure to add vias to the new route
+ for loc in v:
+ self.add_via_stack_center(from_layer=from_layer,
+ to_layer=to_layer,
+ offset=loc,
+ min_area=True)
+
+ if name:
+ self.add_layout_pin_segment_center(text=name,
+ layer=to_layer,
+ start=top_pos,
+ end=bot_pos)
+ else:
+ self.add_segment_center(layer=to_layer,
+ start=top_pos,
+ end=bot_pos)
+
+
+ def connect_col_pins(self, layer, pins, name=None, full=False):
+ """
+ Connects top/bot columns that are aligned.
+ """
+ bins = {}
+ for pin in pins:
+ x = pin.cx()
+ try:
+ bins[x].append(pin)
+ except KeyError:
+ bins[x] = [pin]
+
+ for x, v in bins.items():
+ # Not enough to route a pin, so just copy them
+ if len(v) < 2:
+ continue
+
+ if full:
+ bot_y = 0
+ top_y = self.height
+ else:
+ bot_y = min([pin.by() for pin in v])
+ top_y = max([pin.uy() for pin in v])
+
+ top_pos = vector(x, top_y)
+ bot_pos = vector(x, bot_y)
+
+ # Make sure to add vias to the new route
+ for pin in v:
+ self.add_via_stack_center(from_layer=pin.layer,
+ to_layer=layer,
+ offset=pin.center(),
+ min_area=True)
+
+ if name:
+ self.add_layout_pin_segment_center(text=name,
+ layer=layer,
+ start=top_pos,
+ end=bot_pos)
+ else:
+ self.add_segment_center(layer=layer,
+ start=top_pos,
+ end=bot_pos)
+
+ def get_metal_layers(self, from_layer, to_layer):
+
+ from_id = layer_indices[from_layer]
+ to_id = layer_indices[to_layer]
+
+ layer_list = [x for x in layer_indices.keys() if layer_indices[x] >= from_id and layer_indices[x] < to_id]
+
+ return layer_list
+
+
+ def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True):
"""
Route together all of the pins of a given name that vertically align.
Uses local_insts if insts not specified.
Uses center of pin by default, or right or left if specified.
+ TODO: Add equally spaced option for IR drop min, right now just 2
"""
@@ -450,14 +630,19 @@ class layout():
bins[x] = [(inst,pin)]
for x, v in bins.items():
- # Not enough to route a pin
+ # Not enough to route a pin, so just copy them
if len(v) < 2:
+ debug.warning("Pins don't align well so copying pins instead of connecting with pin.")
+ for inst,pin in v:
+ self.add_layout_pin(pin.name,
+ pin.layer,
+ pin.ll(),
+ pin.width(),
+ pin.height())
continue
- bot_y = min([inst.by() for (inst,pin) in v])
- top_y = max([inst.uy() for (inst,pin) in v])
-
last_via = None
+ pin_layer = None
for inst,pin in v:
if layer:
pin_layer = layer
@@ -468,34 +653,66 @@ class layout():
last_via = self.add_via_stack_center(from_layer=pin.layer,
to_layer=pin_layer,
- offset=vector(x, y),
- min_area=True)
+ offset=vector(x, y))
if last_via:
via_width=last_via.mod.second_layer_width
+ via_height=last_via.mod.second_layer_height
else:
via_width=None
+ via_height=0
- top_pos = vector(x, top_y)
- bot_pos = vector(x, bot_y)
- rect = self.add_layout_pin_rect_center(text=name,
- layer=pin_layer,
- offset=top_pos)
-# self.add_layout_pin_rect_center(text=name,
-# layer=pin_layer,
-# offset=bot_pos)
- self.add_segment_center(layer=pin_layer,
- start=vector(rect.cx(), bot_pos.y),
- end=rect.bc(),
- width=via_width)
+ bot_y = min([pin.by() for (inst,pin) in v])
+ top_y = max([pin.uy() for (inst,pin) in v])
+
+ if full_width:
+ bot_y = min(0, bot_y)
+ top_y = max(self.height, top_y)
+
+ top_pos = vector(x, top_y + 0.5 * via_height)
+ bot_pos = vector(x, bot_y - 0.5 * via_height)
+
+# self.add_layout_pin_rect_ends(name=name,
+# layer=pin_layer,
+# start=top_pos,
+# end=bot_pos,
+# width=via_width)
+ self.add_layout_pin_segment_center(text=name,
+ layer=pin_layer,
+ start=top_pos,
+ end=bot_pos,
+ width=via_width)
+ def add_layout_pin_rect_ends(self, name, layer, start, end, width=None):
- def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy"):
+ # This adds pins on the end connected by a segment
+ top_rect = self.add_layout_pin_rect_center(text=name,
+ layer=layer,
+ offset=start)
+ bot_rect = self.add_layout_pin_rect_center(text=name,
+ layer=layer,
+ offset=end)
+ # This is made to not overlap with the pin above
+ # so that the power router will only select a small pin.
+ # Otherwise it adds big blockages over the rails.
+ if start.y != end.y:
+ self.add_segment_center(layer=layer,
+ start=bot_rect.uc(),
+ end=top_rect.bc())
+ else:
+ self.add_segment_center(layer=layer,
+ start=bot_rect.rc(),
+ end=top_rect.lc())
+
+ return (bot_rect, top_rect)
+
+ def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True):
"""
Route together all of the pins of a given name that horizontally align.
Uses local_insts if insts not specified.
Uses center of pin by default, or top or botom if specified.
+ TODO: Add equally spaced option for IR drop min, right now just 2
"""
@@ -515,14 +732,18 @@ class layout():
# Filter the small bins
for y, v in bins.items():
- # Not enough to route a pin
if len(v) < 2:
+ debug.warning("Pins don't align well so copying pins instead of connecting with pin.")
+ for inst,pin in v:
+ self.add_layout_pin(pin.name,
+ pin.layer,
+ pin.ll(),
+ pin.width(),
+ pin.height())
continue
- left_x = min([inst.lx() for (inst,pin) in v])
- right_x = max([inst.rx() for (inst,pin) in v])
-
last_via = None
+ pin_layer = None
for inst,pin in v:
if layer:
pin_layer = layer
@@ -533,31 +754,63 @@ class layout():
last_via = self.add_via_stack_center(from_layer=pin.layer,
to_layer=pin_layer,
- offset=vector(pin.cx(), y),
+ offset=vector(x, y),
min_area=True)
if last_via:
via_height=last_via.mod.second_layer_height
+ via_width=last_via.mod.second_layer_width
else:
via_height=None
+ via_width=0
- left_pos = vector(left_x, y)
- right_pos = vector(right_x, y)
+ left_x = min([pin.lx() for (inst,pin) in v])
+ right_x = max([pin.rx() for (inst,pin) in v])
- rect = self.add_layout_pin_rect_center(text=name,
- layer=pin_layer,
- offset=left_pos)
-# self.add_layout_pin_rect_center(text=name,
-# layer=pin_layer,
-# offset=right_pos)
- # This is made to not overlap with the pin above
- # so that the power router will only select a small pin.
- # Otherwise it adds big blockages over the rails.
- self.add_segment_center(layer=pin_layer,
- start=rect.rc(),
- end=vector(right_pos.x, rect.cy()),
- width=via_height)
+ if full_width:
+ left_x = min(0, left_x)
+ right_x = max(self.width, right_x)
+ left_pos = vector(left_x + 0.5 * via_width, y)
+ right_pos = vector(right_x + 0.5 * via_width, y)
+
+# self.add_layout_pin_rect_ends(name=name,
+# layer=pin_layer,
+# start=left_pos,
+# end=right_pos,
+# width=via_height)
+ self.add_layout_pin_segment_center(text=name,
+ layer=pin_layer,
+ start=left_pos,
+ end=right_pos,
+ width=via_height)
+
+ def add_layout_end_pin_segment_center(self, text, layer, start, end):
+ """
+ Creates a path with two pins on the end that don't overlap.
+ """
+
+ start_pin = self.add_layout_pin_rect_center(text=text,
+ layer=layer,
+ offset=start)
+ end_pin = self.add_layout_pin_rect_center(text=text,
+ layer=layer,
+ offset=end)
+
+ if start.x != end.x and start.y != end.y:
+ file_name = "non_rectilinear.gds"
+ self.gds_write(file_name)
+ debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1)
+ elif start.x != end.x:
+ self.add_segment_center(layer=layer,
+ start=start_pin.rc(),
+ end=end_pin.lc())
+ elif start.y != end.y:
+ self.add_segment_center(layer=layer,
+ start=start_pin.uc(),
+ end=end_pin.bc())
+ else:
+ debug.error("Cannot have a point pin.", -1)
def add_layout_pin_segment_center(self, text, layer, start, end, width=None):
"""
@@ -828,6 +1081,8 @@ class layout():
offset=offset)
return None
+ intermediate_layers = self.get_metal_layers(from_layer, to_layer)
+
via = None
cur_layer = from_layer
while cur_layer != to_layer:
@@ -850,7 +1105,9 @@ class layout():
implant_type=implant_type,
well_type=well_type)
- if cur_layer != from_layer or min_area:
+ # Only add the enclosure if we are in an intermediate layer
+ # or we are forced to
+ if min_area or cur_layer in intermediate_layers:
self.add_min_area_rect_center(cur_layer,
offset,
via.mod.first_layer_width,
@@ -869,7 +1126,6 @@ class layout():
Add a minimum area retcangle at the given point.
Either width or height should be fixed.
"""
-
min_area = drc("minarea_{}".format(layer))
if min_area == 0:
return
@@ -877,14 +1133,18 @@ class layout():
min_width = drc("minwidth_{}".format(layer))
if preferred_directions[layer] == "V":
- height = max(min_area / width, min_width)
+ new_height = max(min_area / width, min_width)
+ new_width = width
else:
- width = max(min_area / height, min_width)
+ new_width = max(min_area / height, min_width)
+ new_height = height
+
+ debug.check(min_area <= round_to_grid(new_height*new_width), "Min area violated.")
self.add_rect_center(layer=layer,
offset=offset,
- width=width,
- height=height)
+ width=new_width,
+ height=new_height)
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
"""Adds a ptx module to the design."""
@@ -903,10 +1163,6 @@ class layout():
"""Reads a GDSII file in the library and checks if it exists
Otherwise, start a new layout for dynamic generation."""
- # This must be done for netlist only mode too
- if os.path.isfile(self.gds_file):
- self.is_library_cell = True
-
if OPTS.netlist_only:
self.gds = None
return
@@ -1303,7 +1559,7 @@ class layout():
self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()]
- def get_bbox(self, side="all", big_margin=0, little_margin=0):
+ def get_bbox(self, side="all", margin=0):
"""
Get the bounding box from the GDS
"""
@@ -1330,27 +1586,18 @@ class layout():
ll_offset = vector(0, 0)
ur_offset = vector(0, 0)
if side in ["ring", "top", "all"]:
- ur_offset += vector(0, big_margin)
- else:
- ur_offset += vector(0, little_margin)
+ ur_offset += vector(0, margin)
if side in ["ring", "bottom", "all"]:
- ll_offset += vector(0, big_margin)
- else:
- ll_offset += vector(0, little_margin)
+ ll_offset += vector(0, margin)
if side in ["ring", "left", "all"]:
- ll_offset += vector(big_margin, 0)
- else:
- ll_offset += vector(little_margin, 0)
+ ll_offset += vector(margin, 0)
if side in ["ring", "right", "all"]:
- ur_offset += vector(big_margin, 0)
- else:
- ur_offset += vector(little_margin, 0)
+ ur_offset += vector(margin, 0)
bbox = (ll - ll_offset, ur + ur_offset)
size = ur - ll
- debug.info(1, "Size: {0} x {1} with perimeter big margin {2} little margin {3}".format(size.x,
+ debug.info(1, "Size: {0} x {1} with perimeter margin {2}".format(size.x,
size.y,
- big_margin,
- little_margin))
+ margin))
return bbox
@@ -1437,12 +1684,13 @@ class layout():
width = None
height = None
+ pin = None
if start_layer in self.pwr_grid_layers:
- self.add_layout_pin_rect_center(text=name,
- layer=start_layer,
- offset=loc,
- width=width,
- height=height)
+ pin = self.add_layout_pin_rect_center(text=name,
+ layer=start_layer,
+ offset=loc,
+ width=width,
+ height=height)
else:
via = self.add_via_stack_center(from_layer=start_layer,
to_layer=self.pwr_grid_layers[0],
@@ -1453,11 +1701,13 @@ class layout():
width = via.width
if not height:
height = via.height
- self.add_layout_pin_rect_center(text=name,
- layer=self.pwr_grid_layers[0],
- offset=loc,
- width=width,
- height=height)
+ pin = self.add_layout_pin_rect_center(text=name,
+ layer=self.pwr_grid_layers[0],
+ offset=loc,
+ width=width,
+ height=height)
+
+ return pin
def copy_power_pin(self, pin, loc=None, directions=None, new_name=""):
"""
diff --git a/compiler/base/lef.py b/compiler/base/lef.py
index 1d86a63e..8a54253f 100644
--- a/compiler/base/lef.py
+++ b/compiler/base/lef.py
@@ -119,14 +119,13 @@ class lef:
old_blockages = list(self.blockages[pin.layer])
for blockage in old_blockages:
if blockage.overlaps(inflated_pin):
- intersection_shape = blockage.intersection(inflated_pin)
+ intersection_pin = blockage.intersection(inflated_pin)
# If it is zero area, don't split the blockage
- if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]:
+ if not intersection_pin or intersection_pin.area() == 0:
continue
# Remove the old blockage and add the new ones
self.blockages[pin.layer].remove(blockage)
- intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer)
new_blockages = blockage.cut(intersection_pin)
self.blockages[pin.layer].extend(new_blockages)
# We split something so make another pass
diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py
index cc13e049..82590ee6 100644
--- a/compiler/base/pin_layout.py
+++ b/compiler/base/pin_layout.py
@@ -174,6 +174,10 @@ class pin_layout:
def intersection(self, other):
""" Check if a shape overlaps with a rectangle """
+
+ if not self.overlaps(other):
+ return None
+
(ll, ur) = self.rect
(oll, our) = other.rect
@@ -182,7 +186,10 @@ class pin_layout:
min_y = max(ll.y, oll.y)
max_y = min(ur.y, our.y)
- return [vector(min_x, min_y), vector(max_x, max_y)]
+ if max_x - min_x == 0 or max_y - min_y == 0:
+ return None
+
+ return pin_layout("", [vector(min_x, min_y), vector(max_x, max_y)], self.layer)
def xoverlaps(self, other):
""" Check if shape has x overlap """
@@ -504,12 +511,19 @@ class pin_layout:
elif other.contains(self):
return math.inf
else:
- intersections = self.compute_overlap_segment(other)
+ intersections = set(self.compute_overlap_segment(other))
# This is the common case where two pairs of edges overlap
# at two points, so just find the distance between those two points
if len(intersections) == 2:
(p1, p2) = intersections
return math.sqrt(pow(p1[0]-p2[0], 2) + pow(p1[1]-p2[1], 2))
+ # If we have a rectangular overlap region
+ elif len(intersections) == 4:
+ points = intersections
+ ll = vector(min(p.x for p in points), min(p.y for p in points))
+ ur = vector(max(p.x for p in points), max(p.y for p in points))
+ new_shape = pin_layout("", [ll, ur], self.lpp)
+ return max(new_shape.height(), new_shape.width())
else:
# This is where we had a corner intersection or none
return 0
diff --git a/compiler/base/vector.py b/compiler/base/vector.py
index 6c4fabe2..5d011a09 100644
--- a/compiler/base/vector.py
+++ b/compiler/base/vector.py
@@ -51,6 +51,7 @@ class vector():
else:
self.x=float(value[0])
self.y=float(value[1])
+ self._hash = hash((self.x,self.y))
def __getitem__(self, index):
"""
@@ -104,6 +105,7 @@ class vector():
def snap_to_grid(self):
self.x = self.snap_offset_to_grid(self.x)
self.y = self.snap_offset_to_grid(self.y)
+ self._hash = hash((self.x,self.y))
return self
def snap_offset_to_grid(self, offset):
diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py
index fc131d0f..d1e7927b 100644
--- a/compiler/bitcells/pbitcell.py
+++ b/compiler/bitcells/pbitcell.py
@@ -33,6 +33,18 @@ class pbitcell(bitcell_base.bitcell_base):
self.mirror = props.bitcell_1port.mirror
self.end_caps = props.bitcell_1port.end_caps
+ self.wl_layer = "m1"
+ self.wl_dir = "H"
+
+ self.bl_layer = "m2"
+ self.bl_dir = "V"
+
+ self.vdd_layer = "m1"
+ self.vdd_dir = "H"
+
+ self.gnd_layer = "m1"
+ self.gnd_dir = "H"
+
bitcell_base.bitcell_base.__init__(self, name)
fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
info_string = fmt_str.format(self.num_rw_ports,
@@ -87,7 +99,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.route_wordlines()
self.route_bitlines()
- self.route_supply()
+ self.route_supplies()
if self.replica_bitcell:
self.route_rbc_short()
@@ -412,13 +424,14 @@ class pbitcell(bitcell_base.bitcell_base):
def route_rails(self):
""" Adds gnd and vdd rails and connects them to the inverters """
+
# Add rails for vdd and gnd
gnd_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch
self.gnd_position = vector(0, gnd_ypos)
- self.add_rect_center(layer="m1",
- offset=self.gnd_position,
- width=self.width)
- self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H"))
+ self.add_layout_pin_rect_center(text="gnd",
+ layer="m1",
+ offset=self.gnd_position,
+ width=self.width)
vdd_ypos = self.inverter_nmos_ypos \
+ self.inverter_nmos.active_height \
@@ -426,10 +439,10 @@ class pbitcell(bitcell_base.bitcell_base):
+ self.inverter_pmos.active_height \
+ self.vdd_offset
self.vdd_position = vector(0, vdd_ypos)
- self.add_rect_center(layer="m1",
- offset=self.vdd_position,
- width=self.width)
- self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H"))
+ self.add_layout_pin_rect_center(text="vdd",
+ layer="m1",
+ offset=self.vdd_position,
+ width=self.width)
def create_readwrite_ports(self):
"""
@@ -898,7 +911,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.add_path("m2",
[port_contact_offest, br_offset], width=contact.m1_via.height)
- def route_supply(self):
+ def route_supplies(self):
""" Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """
# route inverter nmos and read-access nmos to gnd
nmos_contact_positions = []
diff --git a/compiler/globals.py b/compiler/globals.py
index 15bb4fe9..d0c0d673 100644
--- a/compiler/globals.py
+++ b/compiler/globals.py
@@ -377,7 +377,6 @@ def read_config(config_file, is_unit_test=True):
ports,
OPTS.tech_name)
-
def end_openram():
""" Clean up openram for a proper exit """
cleanup_paths()
diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py
index bd3fc0a4..1f807d63 100644
--- a/compiler/modules/bank.py
+++ b/compiler/modules/bank.py
@@ -229,7 +229,7 @@ class bank(design.design):
# UPPER LEFT QUADRANT
# To the left of the bitcell array above the predecoders and control logic
- x_offset = self.m2_gap + self.port_address[port].width
+ x_offset = self.decoder_gap + self.port_address[port].width
self.port_address_offsets[port] = vector(-x_offset,
self.main_bitcell_array_bottom)
@@ -272,7 +272,7 @@ class bank(design.design):
# LOWER RIGHT QUADRANT
# To the right of the bitcell array
- x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap
+ x_offset = self.bitcell_array_right + self.port_address[port].width + self.decoder_gap
self.port_address_offsets[port] = vector(x_offset,
self.main_bitcell_array_bottom)
@@ -364,9 +364,9 @@ class bank(design.design):
self.num_col_addr_lines = 0
self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines
- # A space for wells or jogging m2
- self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
- 3 * self.m2_pitch,
+ # Gap between decoder and array
+ self.decoder_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
+ 2 * self.m2_pitch,
drc("nwell_to_nwell"))
def add_modules(self):
@@ -400,11 +400,10 @@ class bank(design.design):
self.port_data = []
self.bit_offsets = self.get_column_offsets()
for port in self.all_ports:
- temp_pre = factory.create(module_type="port_data",
- sram_config=self.sram_config,
- port=port,
- bit_offsets=self.bit_offsets)
- self.port_data.append(temp_pre)
+ self.port_data.append(factory.create(module_type="port_data",
+ sram_config=self.sram_config,
+ port=port,
+ bit_offsets=self.bit_offsets))
if(self.num_banks > 1):
self.bank_select = factory.create(module_type="bank_select")
@@ -524,25 +523,9 @@ class bank(design.design):
if self.col_addr_size == 0:
return
- elif self.col_addr_size == 1:
- self.column_decoder = factory.create(module_type="pinvbuf",
- height=self.dff.height)
- elif self.col_addr_size == 2:
- self.column_decoder = factory.create(module_type="hierarchical_predecode2x4",
- column_decoder=True,
- height=self.dff.height)
-
- elif self.col_addr_size == 3:
- self.column_decoder = factory.create(module_type="hierarchical_predecode3x8",
- column_decoder=True,
- height=self.dff.height)
- elif self.col_addr_size == 4:
- self.column_decoder = factory.create(module_type="hierarchical_predecode4x16",
- column_decoder=True,
- height=self.dff.height)
else:
- # No error checking before?
- debug.error("Invalid column decoder?", -1)
+ self.column_decoder = factory.create(module_type="column_decoder",
+ col_addr_size=self.col_addr_size)
self.column_decoder_inst = [None] * len(self.all_ports)
for port in self.all_ports:
@@ -606,15 +589,22 @@ class bank(design.design):
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
+
# Copy only the power pins already on the power layer
# (this won't add vias to internal bitcell pins, for example)
- for inst in self.insts:
- self.copy_power_pins(inst, "vdd", add_vias=False)
- self.copy_power_pins(inst, "gnd", add_vias=False)
+
+ # This avoids getting copy errors on vias and other instances
+ all_insts = [self.bitcell_array_inst] + self.port_address_inst + self.port_data_inst
+ if hasattr(self, "column_decoder_inst"):
+ all_insts += self.column_decoder_inst
+
+ for inst in all_insts:
+ self.copy_layout_pin(inst, "vdd")
+ self.copy_layout_pin(inst, "gnd")
if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins:
for pin_name, supply_name in zip(['vnb','vpb'],['gnd','vdd']):
- self.copy_power_pins(self.bitcell_array_inst, pin_name, new_name=supply_name)
+ self.copy_layout_pin(self.bitcell_array_inst, pin_name, new_name=supply_name)
# If we use the pinvbuf as the decoder, we need to add power pins.
# Other decoders already have them.
@@ -921,23 +911,14 @@ class bank(design.design):
stack = getattr(self, layer_props.bank.stack)
pitch = getattr(self, layer_props.bank.pitch)
- if self.col_addr_size == 1:
+ decode_names = []
+ for i in range(self.num_col_addr_lines):
+ decode_names.append("out_{}".format(i))
- # Connect to sel[0] and sel[1]
- decode_names = ["Zb", "Z"]
-
- # The Address LSB
- self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
-
- elif self.col_addr_size > 1:
- decode_names = []
- for i in range(self.num_col_addr_lines):
- decode_names.append("out_{}".format(i))
-
- for i in range(self.col_addr_size):
- decoder_name = "in_{}".format(i)
- addr_name = "addr{0}_{1}".format(port, i)
- self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
+ for i in range(self.col_addr_size):
+ decoder_name = "in_{}".format(i)
+ addr_name = "addr{0}_{1}".format(port, i)
+ self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
if port % 2:
offset = self.column_decoder_inst[port].ll() - vector((self.num_col_addr_lines + 1) * pitch, 0)
diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py
index d08e3f48..0432dec2 100644
--- a/compiler/modules/bitcell_array.py
+++ b/compiler/modules/bitcell_array.py
@@ -46,6 +46,8 @@ class bitcell_array(bitcell_base_array):
self.add_layout_pins()
+ self.route_supplies()
+
self.add_boundary()
self.DRC_LVS()
diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py
index 5bff2448..45285267 100644
--- a/compiler/modules/bitcell_base_array.py
+++ b/compiler/modules/bitcell_base_array.py
@@ -156,18 +156,15 @@ class bitcell_base_array(design.design):
width=self.width,
height=wl_pin.height())
- def add_supply_pins(self):
- for row in range(self.row_size):
- for col in range(self.column_size):
- inst = self.cell_inst[row, col]
- for pin_name in ["vdd", "gnd"]:
- self.copy_layout_pin(inst, pin_name)
+ def route_supplies(self):
+ for inst in self.cell_inst.values():
+ for pin_name in ["vdd", "gnd"]:
+ self.copy_layout_pin(inst, pin_name)
def add_layout_pins(self):
""" Add the layout pins """
self.add_bitline_pins()
self.add_wl_pins()
- self.add_supply_pins()
def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset
diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py
index 438fd34a..27194a89 100644
--- a/compiler/modules/col_cap_array.py
+++ b/compiler/modules/col_cap_array.py
@@ -99,4 +99,4 @@ class col_cap_array(bitcell_base_array):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
- self.copy_power_pin(pin)
+ self.copy_layout_pin(inst, pin_name)
diff --git a/compiler/modules/column_decoder.py b/compiler/modules/column_decoder.py
new file mode 100644
index 00000000..f94786a2
--- /dev/null
+++ b/compiler/modules/column_decoder.py
@@ -0,0 +1,119 @@
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2022 Regents of the University of California
+# All rights reserved.
+#
+from tech import drc
+import debug
+import design
+import math
+from sram_factory import factory
+from vector import vector
+from globals import OPTS
+from tech import cell_properties
+from tech import layer_properties as layer_props
+
+
+class column_decoder(design.design):
+ """
+ Create the column mux decoder.
+ """
+
+ def __init__(self, name, col_addr_size):
+ super().__init__(name)
+
+ self.col_addr_size = col_addr_size
+ self.num_inputs = col_addr_size
+ self.num_outputs = pow(2, col_addr_size)
+ debug.info(2,
+ "create column decoder of {0} inputs and {1} outputs".format(self.num_inputs,
+ self.num_outputs))
+
+ self.create_netlist()
+ if not OPTS.netlist_only:
+ self.create_layout()
+ self.DRC_LVS()
+
+ def create_netlist(self):
+ self.add_pins()
+ self.add_modules()
+ self.create_instances()
+
+ def create_instances(self):
+ self.column_decoder_inst = self.add_inst(name="column_decoder",
+ mod=self.column_decoder)
+ self.connect_inst(self.pins)
+
+ def create_layout(self):
+ self.column_decoder_inst.place(vector(0,0))
+
+ self.width = self.column_decoder_inst.width
+ self.height = self.column_decoder_inst.height
+
+ self.route_layout()
+
+
+ def add_pins(self):
+ """ Add the module pins """
+
+ for i in range(self.num_inputs):
+ self.add_pin("in_{0}".format(i), "INPUT")
+
+ for j in range(self.num_outputs):
+ self.add_pin("out_{0}".format(j), "OUTPUT")
+
+ self.add_pin("vdd", "POWER")
+ self.add_pin("gnd", "GROUND")
+
+ def route_layout_pins(self):
+ """ Add the pins. """
+
+ if self.col_addr_size == 1:
+ self.copy_layout_pin(self.column_decoder_inst, "A", "in_0")
+ self.copy_layout_pin(self.column_decoder_inst, "Zb", "out_0")
+ self.copy_layout_pin(self.column_decoder_inst, "Z", "out_1")
+ elif self.col_addr_size > 1:
+ for i in range(self.num_inputs):
+ self.copy_layout_pin(self.column_decoder_inst, "in_{0}".format(i))
+
+ for i in range(self.num_outputs):
+ self.copy_layout_pin(self.column_decoder_inst, "out_{0}".format(i))
+
+ def route_layout(self):
+ """ Create routing among the modules """
+ self.route_layout_pins()
+ self.route_supplies()
+
+ def route_supplies(self):
+ """ Propagate all vdd/gnd pins up to this level for all modules """
+ if self.col_addr_size == 1:
+ self.copy_layout_pin(self.column_decoder_inst, "vdd")
+ self.copy_layout_pin(self.column_decoder_inst, "gnd")
+ else:
+ self.route_vertical_pins("vdd", self.insts, xside="rx",)
+ self.route_vertical_pins("gnd", self.insts, xside="lx",)
+
+ def add_modules(self):
+
+ self.dff =factory.create(module_type="dff")
+
+ if self.col_addr_size == 1:
+ self.column_decoder = factory.create(module_type="pinvbuf",
+ height=self.dff.height)
+ elif self.col_addr_size == 2:
+ self.column_decoder = factory.create(module_type="hierarchical_predecode2x4",
+ column_decoder=True,
+ height=self.dff.height)
+
+ elif self.col_addr_size == 3:
+ self.column_decoder = factory.create(module_type="hierarchical_predecode3x8",
+ column_decoder=True,
+ height=self.dff.height)
+ elif self.col_addr_size == 4:
+ self.column_decoder = factory.create(module_type="hierarchical_predecode4x16",
+ column_decoder=True,
+ height=self.dff.height)
+ else:
+ # No error checking before?
+ debug.error("Invalid column decoder?", -1)
+
diff --git a/compiler/modules/column_mux_array.py b/compiler/modules/column_mux_array.py
index 0fa35af0..36d4080e 100644
--- a/compiler/modules/column_mux_array.py
+++ b/compiler/modules/column_mux_array.py
@@ -147,13 +147,14 @@ class column_mux_array(design.design):
offset=offset,
height=self.height - offset.y)
- for inst in self.mux_inst:
- self.copy_layout_pin(inst, "gnd")
+ def route_supplies(self):
+ self.route_horizontal_pins("gnd", self.insts)
def add_routing(self):
self.add_horizontal_input_rail()
self.add_vertical_poly_rail()
self.route_bitlines()
+ self.route_supplies()
def add_horizontal_input_rail(self):
""" Create address input rails below the mux transistors """
diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py
index 01810323..79ce3e30 100644
--- a/compiler/modules/control_logic.py
+++ b/compiler/modules/control_logic.py
@@ -280,7 +280,8 @@ class control_logic(design.design):
def route_rails(self):
""" Add the input signal inverted tracks """
height = self.control_logic_center.y - self.m2_pitch
- offset = vector(self.ctrl_dff_array.width, 0)
+ # DFF spacing plus the power routing
+ offset = vector(self.ctrl_dff_array.width + self.m4_pitch, 0)
self.input_bus = self.create_vertical_bus("m2",
offset,
@@ -312,7 +313,8 @@ class control_logic(design.design):
self.place_dffs()
# All of the control logic is placed to the right of the DFFs and bus
- self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width
+ # as well as the power supply stripe
+ self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + self.m4_pitch
row = 0
# Add the logic on the right of the bus
@@ -368,7 +370,7 @@ class control_logic(design.design):
self.route_clk_buf()
self.route_gated_clk_bar()
self.route_gated_clk_buf()
- self.route_supply()
+ self.route_supplies()
def create_delay(self):
""" Create the replica bitline """
@@ -707,28 +709,74 @@ class control_logic(design.design):
start=out_pos,
end=right_pos)
- def route_supply(self):
+ def route_supplies(self):
""" Add vdd and gnd to the instance cells """
- supply_layer = self.dff.get_pin("vdd").layer
+ pin_layer = self.dff.get_pin("vdd").layer
+ supply_layer = self.supply_stack[2]
+
+ # FIXME: We should be able to replace this with route_vertical_pins instead
+ # but we may have to make the logic gates a separate module so that they
+ # have row pins of the same width
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
+ min_row_x_loc = self.control_x_offset
+
+ vdd_pin_locs = []
+ gnd_pin_locs = []
+
+ last_via = None
for inst in self.row_end_inst:
pins = inst.get_pins("vdd")
for pin in pins:
- if pin.layer == supply_layer:
+ if pin.layer == pin_layer:
row_loc = pin.rc()
pin_loc = vector(max_row_x_loc, pin.rc().y)
- self.add_power_pin("vdd", pin_loc, start_layer=pin.layer)
- self.add_path(supply_layer, [row_loc, pin_loc])
+ vdd_pin_locs.append(pin_loc)
+ last_via = self.add_via_stack_center(from_layer=pin_layer,
+ to_layer=supply_layer,
+ offset=pin_loc,
+ min_area=True)
+ self.add_path(pin_layer, [row_loc, pin_loc])
pins = inst.get_pins("gnd")
for pin in pins:
- if pin.layer == supply_layer:
+ if pin.layer == pin_layer:
row_loc = pin.rc()
- pin_loc = vector(max_row_x_loc, pin.rc().y)
- self.add_power_pin("gnd", pin_loc, start_layer=pin.layer)
- self.add_path(supply_layer, [row_loc, pin_loc])
+ pin_loc = vector(min_row_x_loc, pin.rc().y)
+ gnd_pin_locs.append(pin_loc)
+ last_via = self.add_via_stack_center(from_layer=pin_layer,
+ to_layer=supply_layer,
+ offset=pin_loc,
+ min_area=True)
+ self.add_path(pin_layer, [row_loc, pin_loc])
+
+ if last_via:
+ via_height=last_via.mod.second_layer_height
+ via_width=last_via.mod.second_layer_width
+ else:
+ via_height=None
+ via_width=0
+
+ min_y = min([x.y for x in vdd_pin_locs])
+ max_y = max([x.y for x in vdd_pin_locs])
+ bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height)
+ top_pos = vector(max_row_x_loc, max_y + 0.5 * via_height)
+ self.add_layout_pin_segment_center(text="vdd",
+ layer=supply_layer,
+ start=bot_pos,
+ end=top_pos,
+ width=via_width)
+
+ min_y = min([x.y for x in gnd_pin_locs])
+ max_y = max([x.y for x in gnd_pin_locs])
+ bot_pos = vector(min_row_x_loc, min_y - 0.5 * via_height)
+ top_pos = vector(min_row_x_loc, max_y + 0.5 * via_height)
+ self.add_layout_pin_segment_center(text="gnd",
+ layer=supply_layer,
+ start=bot_pos,
+ end=top_pos,
+ width=via_width)
self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst, "vdd")
@@ -781,7 +829,7 @@ class control_logic(design.design):
# Connect this at the bottom of the buffer
out_pin = inst.get_pin("Z")
out_pos = out_pin.center()
- mid1 = vector(out_pos.x, out_pos.y - 0.4 * inst.mod.height)
+ mid1 = vector(out_pos.x, out_pos.y - 0.3 * inst.mod.height)
mid2 = vector(self.input_bus[name].cx(), mid1.y)
bus_pos = self.input_bus[name].center()
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py
index e044aaa3..f90191ef 100644
--- a/compiler/modules/delay_chain.py
+++ b/compiler/modules/delay_chain.py
@@ -172,20 +172,11 @@ class delay_chain(design.design):
self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
def route_supplies(self):
- # Add power and ground to all the cells except:
- # the fanout driver, the right-most load
- # The routing to connect the loads is over the first and last cells
- # We have an even number of drivers and must only do every other
- # supply rail
-
- for inst in self.driver_inst_list:
- load_list = self.load_inst_map[inst]
- for pin_name in ["vdd", "gnd"]:
- pin = load_list[0].get_pin(pin_name)
- self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
-
- pin = load_list[-2].get_pin(pin_name)
- self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
+ # These pins get routed in one cell from the left/right
+ # because the input signal gets routed on M3 and can interfere with the delay input.
+ self.route_vertical_pins("vdd", self.driver_inst_list, xside="rx")
+ right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list]
+ self.route_vertical_pins("gnd", right_load_insts, xside="lx")
def add_layout_pins(self):
diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py
index 27f6f5bf..5cca38b6 100644
--- a/compiler/modules/dff_array.py
+++ b/compiler/modules/dff_array.py
@@ -42,6 +42,7 @@ class dff_array(design.design):
self.height = self.rows * self.dff.height
self.place_dff_array()
+ self.route_supplies()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
@@ -106,17 +107,26 @@ class dff_array(design.design):
return dout_name
+ def route_supplies(self):
+ if self.rows > 1:
+ # Vertical straps on ends if multiple rows
+ left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)]
+ right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)]
+ self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy")
+ self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy")
+ else:
+
+ # Add connections every 4 cells
+ for col in range(0, self.columns, 4):
+ vdd_pin=self.dff_insts[0, col].get_pin("vdd")
+ self.add_power_pin("vdd", vdd_pin.lc())
+
+ # Add connections every 4 cells
+ for col in range(0, self.columns, 4):
+ gnd_pin=self.dff_insts[0, col].get_pin("gnd")
+ self.add_power_pin("gnd", gnd_pin.rc())
+
def add_layout_pins(self):
- for row in range(self.rows):
- for col in range(self.columns):
- # Continous vdd rail along with label.
- vdd_pin=self.dff_insts[row, col].get_pin("vdd")
- self.copy_power_pin(vdd_pin)
-
- # Continous gnd rail along with label.
- gnd_pin=self.dff_insts[row, col].get_pin("gnd")
- self.copy_power_pin(gnd_pin)
-
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row, col].get_pin("D")
diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py
index 6f8b54aa..70209767 100644
--- a/compiler/modules/dff_buf_array.py
+++ b/compiler/modules/dff_buf_array.py
@@ -154,15 +154,23 @@ class dff_buf_array(design.design):
gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd")
self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height())
- for row in range(self.rows):
- for col in range(self.columns):
- # Continous vdd rail along with label.
- vdd_pin=self.dff_insts[row, col].get_pin("vdd")
- self.copy_power_pin(vdd_pin, loc=vdd_pin.lc())
+ if OPTS.experimental_power and self.rows > 1:
+ # Vertical straps on ends if multiple rows
+ left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)]
+ right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)]
+ self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy")
+ self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy")
+ else:
+ for row in range(self.rows):
+ for col in range(self.columns):
+ # Continous vdd rail along with label.
+ vdd_pin=self.dff_insts[row, col].get_pin("vdd")
+ self.copy_power_pin(vdd_pin)
+
+ # Continous gnd rail along with label.
+ gnd_pin=self.dff_insts[row, col].get_pin("gnd")
+ self.copy_power_pin(gnd_pin)
- # Continous gnd rail along with label.
- gnd_pin=self.dff_insts[row, col].get_pin("gnd")
- self.copy_power_pin(gnd_pin, loc=gnd_pin.lc())
def add_layout_pins(self):
diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py
index 0786142f..83449a70 100644
--- a/compiler/modules/dummy_array.py
+++ b/compiler/modules/dummy_array.py
@@ -36,6 +36,8 @@ class dummy_array(bitcell_base_array):
self.add_layout_pins()
+ self.route_supplies()
+
self.add_boundary()
self.DRC_LVS()
@@ -98,6 +100,8 @@ class dummy_array(bitcell_base_array):
width=self.width,
height=wl_pin.height())
+ def route_supplies(self):
+
# Copy a vdd/gnd layout pin from every cell
for row in range(self.row_size):
for col in range(self.column_size):
diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py
index 0db5acbb..40c2a93a 100644
--- a/compiler/modules/hierarchical_decoder.py
+++ b/compiler/modules/hierarchical_decoder.py
@@ -28,7 +28,6 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst = []
self.pre3x8_inst = []
self.pre4x16_inst = []
- self.local_insts = []
b = factory.create(module_type=OPTS.bitcell)
self.cell_height = b.height
@@ -591,65 +590,18 @@ class hierarchical_decoder(design.design):
def route_supplies(self):
"""
- Add a pin for each row of vdd/gnd which are
- must-connects next level up.
"""
- # This is an experiment with power rails
- if OPTS.experimental_power:
- if layer_props.hierarchical_decoder.vertical_supply:
- pre_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst
- self.route_vertical_pins("vdd", insts=pre_insts, yside="by")
- self.route_vertical_pins("gnd", insts=pre_insts, yside="by")
- self.route_vertical_pins("vdd", insts=self.and_inst, yside="by")
- self.route_vertical_pins("gnd", insts=self.and_inst, yside="by")
- else:
- pre_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst
- self.route_vertical_pins("vdd", insts=pre_insts)
- self.route_vertical_pins("gnd", insts=pre_insts)
- self.route_vertical_pins("vdd", insts=self.and_inst, xside="rx")
- self.route_vertical_pins("gnd", insts=self.and_inst, xside="lx")
+ # Leave these to route in the port_address
+ all_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst
+ for inst in all_insts:
+ self.copy_layout_pin(inst, "vdd")
+ self.copy_layout_pin(inst, "gnd")
+
+ self.route_vertical_pins("vdd", self.and_inst, xside="rx",)
+ self.route_vertical_pins("gnd", self.and_inst, xside="lx",)
- # Widen the rails to cover any gap
- for inst in self.and_inst:
- for name in ["vdd", "gnd"]:
- supply_pin = inst.get_pin(name)
- self.add_segment_center(layer=supply_pin.layer,
- start=vector(0, supply_pin.cy()),
- end=vector(self.width, supply_pin.cy()))
- else:
- if layer_props.hierarchical_decoder.vertical_supply:
- for n in ["vdd", "gnd"]:
- pins = self.and_inst[0].get_pins(n)
- for pin in pins:
- self.add_rect(layer=pin.layer,
- offset=pin.ll() + vector(0, self.bus_space),
- width=pin.width(),
- height=self.height - 2 * self.bus_space)
- # This adds power vias at the top of each cell
- # (except the last to keep them inside the boundary)
- for i in self.and_inst[:-1]:
- pins = i.get_pins(n)
- for pin in pins:
- self.copy_power_pin(pin, loc=pin.uc())
-
- for i in self.pre2x4_inst + self.pre3x8_inst:
- self.copy_layout_pin(i, n)
- else:
- # The vias will be placed at the right of the cells.
- xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space
- for row in range(0, self.num_outputs):
- for pin_name in ["vdd", "gnd"]:
- # The nand and inv are the same height rows...
- supply_pin = self.and_inst[row].get_pin(pin_name)
- pin_pos = vector(xoffset, supply_pin.cy())
- self.copy_power_pin(supply_pin, loc=pin_pos)
-
- # Copy the pins from the predecoders
- for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst:
- for pin_name in ["vdd", "gnd"]:
- self.copy_layout_pin(pre, pin_name)
def route_predecode_bus_outputs(self, rail_name, pin, row):
"""
diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py
index 706fc58e..8ae3a08d 100644
--- a/compiler/modules/hierarchical_predecode.py
+++ b/compiler/modules/hierarchical_predecode.py
@@ -381,7 +381,7 @@ class hierarchical_predecode(design.design):
def route_supplies(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
- # We may ahve vertical power supply rails
+ # We may have vertical power supply rails
if layer_props.hierarchical_predecode.vertical_supply and not self.column_decoder:
for n in ["vdd", "gnd"]:
# This makes a wire from top to bottom for both inv and and gates
@@ -395,7 +395,7 @@ class hierarchical_predecode(design.design):
height=top_pin.uy() - self.bus_pitch)
# This adds power vias at the top of each cell
# (except the last to keep them inside the boundary)
- for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]:
+ for i in [self.inv_inst[0], self.inv_inst[-2], self.and_inst[0], self.and_inst[-2]]:
pins = i.get_pins(n)
for pin in pins:
self.copy_power_pin(pin, loc=pin.uc())
@@ -403,8 +403,6 @@ class hierarchical_predecode(design.design):
# In other techs, we are using standard cell decoder cells with horizontal power
else:
for num in range(0, self.number_of_outputs):
-
- # Route both supplies
for n in ["vdd", "gnd"]:
and_pins = self.and_inst[num].get_pins(n)
for and_pin in and_pins:
@@ -418,4 +416,4 @@ class hierarchical_predecode(design.design):
else:
xoffset = self.inv_inst[0].lx() - self.bus_space
pin_pos = vector(xoffset, and_pin.cy())
- self.copy_power_pin(and_pin, loc=pin_pos)
+ self.add_power_pin(n, pin_pos)
diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py
index 49e8ed76..18b33c0f 100644
--- a/compiler/modules/local_bitcell_array.py
+++ b/compiler/modules/local_bitcell_array.py
@@ -159,17 +159,20 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
def place(self):
""" Place the bitcelll array to the right of the wl driver. """
- # FIXME: Replace this with a tech specific paramter
+
+ # FIXME: Replace this with a tech specific parameter
driver_to_array_spacing = 3 * self.m3_pitch
- self.wl_insts[0].place(vector(0, self.cell.height))
+ wl_offset = vector(0, self.bitcell_array.get_replica_bottom())
+ self.wl_insts[0].place(wl_offset)
- self.bitcell_array_inst.place(vector(self.wl_insts[0].rx() + driver_to_array_spacing,
- 0))
+ bitcell_array_offset = vector(self.wl_insts[0].rx() + driver_to_array_spacing, 0)
+ self.bitcell_array_inst.place(bitcell_array_offset)
if len(self.all_ports) > 1:
- self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
- 2 * self.cell.height + self.wl_array.height),
+ wl_offset = vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
+ self.bitcell_array.get_replica_bottom() + self.wl_array.height + self.cell.height)
+ self.wl_insts[1].place(wl_offset,
mirror="XY")
self.height = self.bitcell_array.height
diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py
index 7c3af51c..a96a556e 100644
--- a/compiler/modules/port_address.py
+++ b/compiler/modules/port_address.py
@@ -76,16 +76,19 @@ class port_address(design.design):
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
- for inst in [self.wordline_driver_array_inst, self.row_decoder_inst]:
- self.copy_layout_pin(inst, "vdd")
- self.copy_layout_pin(inst, "gnd")
+ if layer_props.wordline_driver.vertical_supply:
+ self.copy_layout_pin(self.rbl_driver_inst, "vdd")
+ else:
+ rbl_pos = self.rbl_driver_inst.get_pin("vdd").rc()
+ self.add_power_pin("vdd", rbl_pos)
+ self.add_path("m4", [rbl_pos, self.wordline_driver_array_inst.get_pins("vdd")[0].rc()])
- for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"):
- if layer_props.port_address.supply_offset:
- self.copy_power_pin(rbl_vdd_pin)
- else:
- self.copy_power_pin(rbl_vdd_pin, loc=rbl_vdd_pin.lc())
+ self.copy_layout_pin(self.wordline_driver_array_inst, "vdd")
+ self.copy_layout_pin(self.wordline_driver_array_inst, "gnd")
+ self.copy_layout_pin(self.row_decoder_inst, "vdd")
+ self.copy_layout_pin(self.row_decoder_inst, "gnd")
+
# Also connect the B input of the RBL and_dec to vdd
if OPTS.local_array_size == 0:
rbl_b_pin = self.rbl_driver_inst.get_pin("B")
diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py
index 0215d76a..372a5629 100644
--- a/compiler/modules/precharge_array.py
+++ b/compiler/modules/precharge_array.py
@@ -95,11 +95,7 @@ class precharge_array(design.design):
self.copy_layout_pin(inst, "br", "br_{0}".format(i))
def route_supplies(self):
- if OPTS.experimental_power:
- self.route_horizontal_pins("vdd")
- else:
- for inst in self.local_insts:
- self.copy_layout_pin(inst, "vdd")
+ self.route_horizontal_pins("vdd")
def create_insts(self):
"""Creates a precharge array by horizontally tiling the precharge cell"""
diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py
index 57a58d79..92a0358b 100644
--- a/compiler/modules/replica_bitcell_array.py
+++ b/compiler/modules/replica_bitcell_array.py
@@ -6,7 +6,10 @@
import debug
from bitcell_base_array import bitcell_base_array
-from tech import drc, spice, cell_properties
+from pbitcell import pbitcell
+from contact import contact
+from tech import drc, spice, preferred_directions
+from tech import cell_properties as props
from vector import vector
from globals import OPTS
from sram_factory import factory
@@ -305,19 +308,18 @@ class replica_bitcell_array(bitcell_base_array):
def create_layout(self):
- # We will need unused wordlines grounded, so we need to know their layer
- # and create a space on the left and right for the vias to connect to ground
- pin = self.cell.get_pin(self.cell.get_all_wl_names()[0])
- pin_layer = pin.layer
- self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
- self.unused_offset = vector(self.unused_pitch, 0)
+ # This creates space for the unused wordline connections as well as the
+ # row-based or column based power and ground lines.
+ self.vertical_pitch = getattr(self, "{}_pitch".format(self.supply_stack[0]))
+ self.horizontal_pitch = getattr(self, "{}_pitch".format(self.supply_stack[2]))
+ self.unused_offset = vector(0.25, 0.25)
# This is a bitcell x bitcell offset to scale
self.bitcell_offset = vector(self.cell.width, self.cell.height)
self.col_end_offset = vector(self.cell.width, self.cell.height)
self.row_end_offset = vector(self.cell.width, self.cell.height)
- # Everything is computed with the main array at (self.unused_pitch, 0) to start
+ # Everything is computed with the main array
self.bitcell_array_inst.place(offset=self.unused_offset)
self.add_replica_columns()
@@ -331,29 +333,58 @@ class replica_bitcell_array(bitcell_base_array):
self.translate_all(array_offset.scale(-1, -1))
# Add extra width on the left and right for the unused WLs
+
self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x
self.height = self.dummy_row_insts[1].uy()
self.add_layout_pins()
+ self.route_supplies()
+
self.route_unused_wordlines()
+ lower_left = self.find_lowest_coords()
+ upper_right = self.find_highest_coords()
+ self.width = upper_right.x - lower_left.x
+ self.height = upper_right.y - lower_left.y
+ self.translate_all(lower_left)
+
self.add_boundary()
self.DRC_LVS()
def get_main_array_top(self):
+ """ Return the top of the main bitcell array. """
return self.bitcell_array_inst.uy()
def get_main_array_bottom(self):
+ """ Return the bottom of the main bitcell array. """
return self.bitcell_array_inst.by()
def get_main_array_left(self):
+ """ Return the left of the main bitcell array. """
return self.bitcell_array_inst.lx()
def get_main_array_right(self):
+ """ Return the right of the main bitcell array. """
return self.bitcell_array_inst.rx()
+ def get_replica_top(self):
+ """ Return the top of all replica columns. """
+ return self.dummy_row_insts[0].by()
+
+ def get_replica_bottom(self):
+ """ Return the bottom of all replica columns. """
+ return self.dummy_row_insts[0].uy()
+
+ def get_replica_left(self):
+ """ Return the left of all replica columns. """
+ return self.dummy_col_insts[0].rx()
+
+ def get_replica_right(self):
+ """ Return the right of all replica columns. """
+ return self.dummy_col_insts[1].rx()
+
def get_column_offsets(self):
"""
Return an array of the x offsets of all the regular bits
@@ -458,37 +489,205 @@ class replica_bitcell_array(bitcell_base_array):
width=pin.width(),
height=self.height)
+ def route_supplies(self):
+
+ if OPTS.bitcell == "pbitcell":
+ bitcell = factory.create(module_type="pbitcell")
+ else:
+ bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports))
+
+ wl_layer = bitcell.wl_layer
+ wl_dir = bitcell.wl_dir
+
+ bl_layer = bitcell.bl_layer
+ bl_dir = bitcell.bl_dir
+
+ vdd_layer = bitcell.vdd_layer
+ vdd_dir = bitcell.vdd_dir
+
+ gnd_layer = bitcell.gnd_layer
+ gnd_dir = bitcell.gnd_dir
+
# vdd/gnd are only connected in the perimeter cells
- # replica column should only have a vdd/gnd in the dummy cell on top/bottom
supply_insts = self.dummy_col_insts + self.dummy_row_insts
+ # For the wordlines
+ top_bot_mult = 1
+ left_right_mult = 1
- if OPTS.experimental_power:
- for pin_name in self.supplies:
- #self.route_vertical_pins(name=pin_name, insts=supply_insts)
- self.route_horizontal_pins(name=pin_name, insts=supply_insts)
+ vdd_locs = []
+ gnd_locs = []
+ # There are always vertical pins for the WLs on the left/right if we have unused wordlines
+ self.left_gnd_locs = self.route_side_pin("gnd", "left", left_right_mult)
+ self.right_gnd_locs = self.route_side_pin("gnd","right", left_right_mult)
+ # This needs to be big enough so that they aren't in the same supply routing grid
+ left_right_mult = 4
- #self.route_vertical_pins(name=pin_name, insts=self.replica_col_insts)
- #self.route_horizontal_pins(name=pin_name, insts=self.replica_col_insts)
- for inst in supply_insts:
- pin_list = inst.get_pins(pin_name)
- for pin in pin_list:
- self.copy_power_pin(pin)
+ if gnd_dir == "V":
+ self.top_gnd_locs = self.route_side_pin("gnd", "top", top_bot_mult)
+ self.bot_gnd_locs = self.route_side_pin("gnd", "bot", top_bot_mult)
+ # This needs to be big enough so that they aren't in the same supply routing grid
+ top_bot_mult = 4
- for inst in self.replica_col_insts:
- if inst:
- self.copy_layout_pin(inst, pin_name)
+ if vdd_dir == "V":
+ self.top_vdd_locs = self.route_side_pin("vdd", "top", top_bot_mult)
+ self.bot_vdd_locs = self.route_side_pin("vdd", "bot", top_bot_mult)
+ elif vdd_dir == "H":
+ self.left_vdd_locs = self.route_side_pin("vdd", "left", left_right_mult)
+ self.right_vdd_locs = self.route_side_pin("vdd", "right", left_right_mult)
else:
- for pin_name in self.supplies:
- for inst in supply_insts:
- pin_list = inst.get_pins(pin_name)
- for pin in pin_list:
- self.copy_power_pin(pin)
+ debug.error("Invalid vdd direction {}".format(vdd_dir), -1)
- for inst in self.replica_col_insts:
- if inst:
- self.copy_layout_pin(inst, pin_name)
+ for inst in supply_insts:
+ for pin in inst.get_pins("vdd"):
+ if vdd_dir == "V":
+ self.connect_side_pin(pin, "top", self.top_vdd_locs[0].y)
+ self.connect_side_pin(pin, "bot", self.bot_vdd_locs[0].y)
+ elif vdd_dir == "H":
+ self.connect_side_pin(pin, "left", self.left_vdd_locs[0].x)
+ self.connect_side_pin(pin, "right", self.right_vdd_locs[0].x)
+
+
+ for inst in supply_insts:
+ for pin in inst.get_pins("gnd"):
+ if gnd_dir == "V":
+ self.connect_side_pin(pin, "top", self.top_gnd_locs[0].y)
+ self.connect_side_pin(pin, "bot", self.bot_gnd_locs[0].y)
+ elif gnd_dir == "H":
+ self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
+ self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
+
+
+ def route_unused_wordlines(self):
+ """ Connect the unused RBL and dummy wordlines to gnd """
+ # This grounds all the dummy row word lines
+ for inst in self.dummy_row_insts:
+ for wl_name in self.col_cap_top.get_wordline_names():
+ pin = inst.get_pin(wl_name)
+ self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
+ self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
+
+ # Ground the unused replica wordlines
+ for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
+ for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
+ if wl_name in self.gnd_wordline_names:
+ pin = inst.get_pin(pin_name)
+ self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
+ self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
+
+ def route_side_pin(self, name, side, offset_multiple=1):
+ """
+ Routes a vertical or horizontal pin on the side of the bbox.
+ The multiple specifies how many track offsets to be away from the side assuming
+ (0,0) (self.width, self.height)
+ """
+ if side in ["left", "right"]:
+ return self.route_vertical_side_pin(name, side, offset_multiple)
+ elif side in ["top", "bottom", "bot"]:
+ return self.route_horizontal_side_pin(name, side, offset_multiple)
+ else:
+ debug.error("Invalid side {}".format(side), -1)
+
+ def route_vertical_side_pin(self, name, side, offset_multiple=1):
+ """
+ Routes a vertical pin on the side of the bbox.
+ """
+ if side == "left":
+ bot_loc = vector(-offset_multiple * self.vertical_pitch, 0)
+ top_loc = vector(-offset_multiple * self.vertical_pitch, self.height)
+ elif side == "right":
+ bot_loc = vector(self.width + offset_multiple * self.vertical_pitch, 0)
+ top_loc = vector(self.width + offset_multiple * self.vertical_pitch, self.height)
+
+ layer = self.supply_stack[2]
+ top_via = contact(layer_stack=self.supply_stack,
+ directions=("H", "H"))
+
+
+# self.add_layout_pin_rect_ends(text=name,
+# layer=layer,
+# start=bot_loc,
+# end=top_loc)
+ self.add_layout_pin_segment_center(text=name,
+ layer=layer,
+ start=bot_loc,
+ end=top_loc,
+ width=top_via.second_layer_width)
+
+ return (bot_loc, top_loc)
+
+ def route_horizontal_side_pin(self, name, side, offset_multiple=1):
+ """
+ Routes a horizontal pin on the side of the bbox.
+ """
+ if side in ["bottom", "bot"]:
+ left_loc = vector(0, -offset_multiple * self.horizontal_pitch)
+ right_loc = vector(self.width, -offset_multiple * self.horizontal_pitch)
+ elif side == "top":
+ left_loc = vector(0, self.height + offset_multiple * self.horizontal_pitch)
+ right_loc = vector(self.width, self.height + offset_multiple * self.horizontal_pitch)
+
+ layer = self.supply_stack[0]
+ side_via = contact(layer_stack=self.supply_stack,
+ directions=("V", "V"))
+
+# self.add_layout_pin_rect_ends(text=name,
+# layer=layer,
+# start=left_loc,
+# end=right_loc)
+ self.add_layout_pin_segment_center(text=name,
+ layer=layer,
+ start=left_loc,
+ end=right_loc,
+ width=side_via.first_layer_height)
+
+ return (left_loc, right_loc)
+
+ def connect_side_pin(self, pin, side, offset):
+ """
+ Used to connect horizontal layers of pins to the left/right straps
+ locs provides the offsets of the pin strip end points.
+ """
+ if side in ["left", "right"]:
+ self.connect_vertical_side_pin(pin, side, offset)
+ elif side in ["top", "bottom", "bot"]:
+ self.connect_horizontal_side_pin(pin, side, offset)
+ else:
+ debug.error("Invalid side {}".format(side), -1)
+
+ def connect_horizontal_side_pin(self, pin, side, yoffset):
+ """
+ Used to connect vertical layers of pins to the top/bottom horizontal straps
+ """
+ cell_loc = pin.center()
+ pin_loc = vector(cell_loc.x, yoffset)
+
+ # Place the pins a track outside of the array
+ self.add_via_stack_center(offset=pin_loc,
+ from_layer=pin.layer,
+ to_layer=self.supply_stack[0],
+ directions=("V", "V"))
+
+ # Add a path to connect to the array
+ self.add_path(pin.layer, [cell_loc, pin_loc])
+
+
+ def connect_vertical_side_pin(self, pin, side, xoffset):
+ """
+ Used to connect vertical layers of pins to the top/bottom vertical straps
+ """
+ cell_loc = pin.center()
+ pin_loc = vector(xoffset, cell_loc.y)
+
+ # Place the pins a track outside of the array
+ self.add_via_stack_center(offset=pin_loc,
+ from_layer=pin.layer,
+ to_layer=self.supply_stack[2],
+ directions=("H", "H"))
+
+ # Add a path to connect to the array
+ self.add_path(pin.layer, [cell_loc, pin_loc])
def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW."""
@@ -507,34 +706,6 @@ class replica_bitcell_array(bitcell_base_array):
cell_power.leakage * self.column_size * self.row_size)
return total_power
- def route_unused_wordlines(self):
- """ Connect the unused RBL and dummy wordlines to gnd """
- # This grounds all the dummy row word lines
- for inst in self.dummy_row_insts:
- for wl_name in self.col_cap_top.get_wordline_names():
- self.ground_pin(inst, wl_name)
-
- # Ground the unused replica wordlines
- for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
- for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
- if wl_name in self.gnd_wordline_names:
- self.ground_pin(inst, pin_name)
-
- def ground_pin(self, inst, name):
- pin = inst.get_pin(name)
- pin_layer = pin.layer
-
- left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy())
- right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy())
-
- # Place the pins a track outside of the array
- left_loc = left_pin_loc - vector(self.unused_pitch, 0)
- right_loc = right_pin_loc + vector(self.unused_pitch, 0)
- self.add_power_pin("gnd", left_loc, directions=("H", "H"))
- self.add_power_pin("gnd", right_loc, directions=("H", "H"))
-
- # Add a path to connect to the array
- self.add_path(pin_layer, [left_loc, right_loc], width=pin.height())
def gen_bl_wire(self):
if OPTS.netlist_only:
diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py
index a3de3a38..43a3cda3 100644
--- a/compiler/modules/replica_column.py
+++ b/compiler/modules/replica_column.py
@@ -69,6 +69,8 @@ class replica_column(bitcell_base_array):
self.add_layout_pins()
+ self.route_supplies()
+
self.add_boundary()
self.DRC_LVS()
@@ -185,15 +187,11 @@ class replica_column(bitcell_base_array):
width=self.width,
height=wl_pin.height())
- # Supplies are only connected in the ends
- for (index, inst) in enumerate(self.cell_inst):
+ def route_supplies(self):
+
+ for inst in self.cell_inst:
for pin_name in ["vdd", "gnd"]:
- if inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]:
- #for pin in inst.get_pins(pin_name):
- # self.copy_power_pin(pin)
- self.copy_power_pins(inst, pin_name)
- else:
- self.copy_layout_pin(inst, pin_name)
+ self.copy_layout_pin(inst, pin_name)
def get_bitline_names(self, port=None):
if port == None:
diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py
index e7dc9816..8f092dc4 100644
--- a/compiler/modules/row_cap_array.py
+++ b/compiler/modules/row_cap_array.py
@@ -106,10 +106,13 @@ class row_cap_array(bitcell_base_array):
width=self.width,
height=wl_pin.height())
- # Add vdd/gnd via stacks
for row in range(1, self.row_size - 1):
for col in range(self.column_size):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
- self.copy_power_pin(pin)
+ self.add_layout_pin(text=pin_name,
+ layer=pin.layer,
+ offset=pin.ll(),
+ width=pin.width(),
+ height=pin.height())
diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py
index a481679f..daabf8df 100644
--- a/compiler/modules/sense_amp_array.py
+++ b/compiler/modules/sense_amp_array.py
@@ -174,18 +174,8 @@ class sense_amp_array(design.design):
height=dout_pin.height())
def route_supplies(self):
- if OPTS.experimental_power:
- self.route_horizontal_pins("vdd")
- self.route_horizontal_pins("gnd")
- else:
- for i in range(len(self.local_insts)):
- inst = self.local_insts[i]
-
- for gnd_pin in inst.get_pins("gnd"):
- self.copy_power_pin(gnd_pin)
-
- for vdd_pin in inst.get_pins("vdd"):
- self.copy_power_pin(vdd_pin)
+ self.route_horizontal_pins("vdd")
+ self.route_horizontal_pins("gnd")
def route_rails(self):
# Add enable across the array
diff --git a/compiler/modules/wordline_buffer_array.py b/compiler/modules/wordline_buffer_array.py
index 51fe4030..9c74b79d 100644
--- a/compiler/modules/wordline_buffer_array.py
+++ b/compiler/modules/wordline_buffer_array.py
@@ -44,7 +44,10 @@ class wordline_buffer_array(design.design):
self.place_drivers()
self.route_layout()
self.route_supplies()
- self.offset_all_coordinates()
+
+ # Don't offset these because some cells use standard cell style drivers
+ #self.offset_all_coordinates()
+
self.add_boundary()
self.DRC_LVS()
@@ -71,31 +74,11 @@ class wordline_buffer_array(design.design):
are must-connects next level up.
"""
if layer_props.wordline_driver.vertical_supply:
- for name in ["vdd", "gnd"]:
- supply_pins = self.wld_inst[0].get_pins(name)
- for pin in supply_pins:
- self.add_layout_pin_segment_center(text=name,
- layer=pin.layer,
- start=pin.bc(),
- end=vector(pin.cx(), self.height))
+ self.route_vertical_pins("vdd", self.wld_inst)
+ self.route_vertical_pins("gnd", self.wld_inst)
else:
- # Find the x offsets for where the vias/pins should be placed
- xoffset_list = [self.wld_inst[0].rx()]
- for num in range(self.rows):
- # this will result in duplicate polygons for rails, but who cares
-
- # use the inverter offset even though it will be the and's too
- (gate_offset, y_dir) = self.get_gate_offset(0,
- self.wl_driver.height,
- num)
- # Route both supplies
- for name in ["vdd", "gnd"]:
- supply_pin = self.wld_inst[num].get_pin(name)
-
- # Add pins in two locations
- for xoffset in xoffset_list:
- pin_pos = vector(xoffset, supply_pin.cy())
- self.copy_power_pin(supply_pin, loc=pin_pos)
+ self.route_vertical_pins("vdd", self.wld_inst, xside="rx",)
+ self.route_vertical_pins("gnd", self.wld_inst, xside="lx",)
def create_drivers(self):
self.wld_inst = []
diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py
index a13f4bcd..63c39a7c 100644
--- a/compiler/modules/wordline_driver_array.py
+++ b/compiler/modules/wordline_driver_array.py
@@ -53,7 +53,7 @@ class wordline_driver_array(design.design):
self.height = self.wld_inst[-1].uy()
self.add_boundary()
- self.route_vdd_gnd()
+ self.route_supplies()
self.DRC_LVS()
def add_pins(self):
@@ -72,54 +72,17 @@ class wordline_driver_array(design.design):
self.wl_driver = factory.create(module_type="wordline_driver",
cols=self.cols)
- def route_vdd_gnd(self):
+ def route_supplies(self):
"""
Add vertical power rails.
"""
- # Experiment with power straps
- if OPTS.experimental_power:
- if layer_props.wordline_driver.vertical_supply:
- self.route_vertical_pins("vdd", insts=self.wld_inst)
- self.route_vertical_pins("gnd", insts=self.wld_inst)
- else:
- self.route_vertical_pins("vdd", insts=self.wld_inst, xside="lx")
- self.route_vertical_pins("gnd", insts=self.wld_inst, xside="rx")
-
- # Widen the rails to cover any gap
- for num in range(self.rows):
- for name in ["vdd", "gnd"]:
- supply_pin = self.wld_inst[num].get_pin(name)
- self.add_segment_center(layer=supply_pin.layer,
- start=vector(0, supply_pin.cy()),
- end=vector(self.width, supply_pin.cy()))
+ if layer_props.wordline_driver.vertical_supply:
+ self.route_vertical_pins("vdd", self.wld_inst)
+ self.route_vertical_pins("gnd", self.wld_inst)
else:
- if layer_props.wordline_driver.vertical_supply:
- for name in ["vdd", "gnd"]:
- supply_pins = self.wld_inst[0].get_pins(name)
- for pin in supply_pins:
- self.add_layout_pin_segment_center(text=name,
- layer=pin.layer,
- start=pin.bc(),
- end=vector(pin.cx(), self.height))
- else:
- # Find the x offsets for where the vias/pins should be placed
- xoffset_list = [self.wld_inst[0].rx()]
- for num in range(self.rows):
- # this will result in duplicate polygons for rails, but who cares
-
- # use the inverter offset even though it will be the and's too
- (gate_offset, y_dir) = self.get_gate_offset(0,
- self.wl_driver.height,
- num)
- # Route both supplies
- for name in ["vdd", "gnd"]:
- supply_pin = self.wld_inst[num].get_pin(name)
-
- # Add pins in two locations
- for xoffset in xoffset_list:
- pin_pos = vector(xoffset, supply_pin.cy())
- self.copy_power_pin(supply_pin, loc=pin_pos)
+ self.route_vertical_pins("vdd", self.wld_inst, xside="rx",)
+ self.route_vertical_pins("gnd", self.wld_inst, xside="lx",)
def create_drivers(self):
diff --git a/compiler/openram.py b/compiler/openram.py
index 089c0f3a..8ce6ae1e 100755
--- a/compiler/openram.py
+++ b/compiler/openram.py
@@ -74,8 +74,8 @@ for path in output_files:
from sram import sram
-s = sram(sram_config=c,
- name=OPTS.output_name)
+s = sram(name=OPTS.output_name,
+ sram_config=c)
# Output the files for the resulting SRAM
s.save()
diff --git a/compiler/options.py b/compiler/options.py
index d81a8c7e..26bcb099 100644
--- a/compiler/options.py
+++ b/compiler/options.py
@@ -166,15 +166,12 @@ class options(optparse.Values):
keep_temp = False
- # Add a prefix of the root cell before every structure in the GDS
- # after outputting the GDS2
- uniquify = False
-
# These are the default modules that can be over-riden
bank_select = "bank_select"
bitcell_array = "bitcell_array"
bitcell = "bitcell"
buf_dec = "pbuf"
+ column_decoder = "column_decoder"
column_mux_array = "column_mux_array"
control_logic = "control_logic"
decoder = "hierarchical_decoder"
@@ -199,4 +196,4 @@ class options(optparse.Values):
write_mask_and_array = "write_mask_and_array"
# Non-public options
- experimental_power = False
+ experimental_power = True
diff --git a/compiler/pgates/column_mux.py b/compiler/pgates/column_mux.py
index a4a83dcc..bf489e87 100644
--- a/compiler/pgates/column_mux.py
+++ b/compiler/pgates/column_mux.py
@@ -240,10 +240,9 @@ class column_mux(pgate.pgate):
self.add_via_center(layers=self.col_mux_stack,
offset=active_pos)
- # Add the M1->..->power_grid_layer stack
- self.add_power_pin(name="gnd",
- loc=active_pos,
- start_layer="m1")
+ self.add_layout_pin_rect_center(text="gnd",
+ layer="m1",
+ offset=active_pos)
# Add well enclosure over all the tx and contact
if "pwell" in layer:
diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py
index 8eff8c8f..1538e0a5 100644
--- a/compiler/pgates/precharge.py
+++ b/compiler/pgates/precharge.py
@@ -96,46 +96,13 @@ class precharge(design.design):
Adds a vdd rail at the top of the cell
"""
- if OPTS.experimental_power:
- pmos_pin = self.upper_pmos2_inst.get_pin("S")
- pmos_pos = pmos_pin.center()
- self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos])
+ pmos_pin = self.upper_pmos2_inst.get_pin("S")
+ pmos_pos = pmos_pin.center()
+ self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos])
- self.add_via_stack_center(from_layer=pmos_pin.layer,
- to_layer=self.supply_stack[0],
- offset=self.well_contact_pos,
- directions=("V", "V"))
-
- self.add_min_area_rect_center(layer=self.en_layer,
- offset=self.well_contact_pos,
- width=self.well_contact.mod.second_layer_width)
-
- self.add_layout_pin_rect_center(text="vdd",
- layer=self.supply_stack[0],
- offset=self.well_contact_pos)
- else:
- # Adds the rail across the width of the cell
- vdd_position = vector(0.5 * self.width, self.height)
- layer_width = drc("minwidth_" + self.en_layer)
- self.add_rect_center(layer=self.en_layer,
- offset=vdd_position,
- width=self.width,
- height=layer_width)
-
- pmos_pin = self.upper_pmos2_inst.get_pin("S")
-
- # center of vdd rail
- pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
- self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos])
-
- self.add_power_pin("vdd",
- self.well_contact_pos,
- directions=("V", "V"))
-
- self.add_via_stack_center(from_layer=pmos_pin.layer,
- to_layer=self.en_layer,
- offset=pmos_pin.center(),
- directions=("V", "V"))
+ self.add_layout_pin_rect_center(text="vdd",
+ layer=pmos_pin.layer,
+ offset=self.well_contact_pos)
def create_ptx(self):
"""
diff --git a/compiler/router/grid.py b/compiler/router/grid.py
index 843fb3ed..a366ebe0 100644
--- a/compiler/router/grid.py
+++ b/compiler/router/grid.py
@@ -68,6 +68,16 @@ class grid:
self.add_map(n)
return self.map[n].blocked
+ def is_inside(self, n):
+ if not isinstance(n, vector3d):
+ for item in n:
+ if self.is_inside(item):
+ return True
+ else:
+ return False
+ else:
+ return n.x >= self.ll.x and n.x <= self.ur.x and n.y >= self.ll.y and n.y <= self.ur.y
+
def set_path(self, n, value=True):
if isinstance(n, (list, tuple, set, frozenset)):
for item in n:
@@ -128,7 +138,7 @@ class grid:
"""
Side specifies which side.
Layer specifies horizontal (0) or vertical (1)
- Width specifies how wide the perimter "stripe" should be.
+ Width specifies how wide the perimeter "stripe" should be.
Works from the inside out from the bbox (ll, ur)
"""
if "ring" in side:
diff --git a/compiler/router/grid_cell.py b/compiler/router/grid_cell.py
index 42be2761..f201a094 100644
--- a/compiler/router/grid_cell.py
+++ b/compiler/router/grid_cell.py
@@ -20,8 +20,7 @@ class grid_cell:
def reset(self):
"""
- Reset the dynamic info about routing. The pins/blockages are not reset so
- that they can be reused.
+ Reset the dynamic info about routing.
"""
self.min_cost=-1
self.min_path=None
diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py
index 5e6d6f89..d67200d5 100644
--- a/compiler/router/pin_group.py
+++ b/compiler/router/pin_group.py
@@ -158,7 +158,7 @@ class pin_group:
# Now add the right name
for pin in new_pin_list:
pin.name = self.name
-
+
debug.check(len(new_pin_list) > 0,
"Did not find any enclosures.")
@@ -424,7 +424,7 @@ class pin_group:
debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.")
common_blockages = self.router.get_blocked_grids() & self.grids
-
+
# Start with the ll and make the widest row
row = [ll]
# Move in dir1 while we can
@@ -641,7 +641,7 @@ class pin_group:
# way than blockages.
blockages = sufficient | insufficient | blockage_in_tracks
self.blockages.update(blockages)
-
+
# If we have a blockage, we must remove the grids
# Remember, this excludes the pin blockages already
blocked_grids = self.router.get_blocked_grids()
diff --git a/compiler/router/router.py b/compiler/router/router.py
index f82a6128..2e5236ea 100644
--- a/compiler/router/router.py
+++ b/compiler/router/router.py
@@ -92,12 +92,19 @@ class router(router_tech):
def get_bbox(self):
return self.bbox
- def create_routing_grid(self, router_type):
+ def create_routing_grid(self, router_type=None):
"""
- Create a sprase routing grid with A* expansion functions.
+ Create (or recreate) a sprase routing grid with A* expansion functions.
"""
+ debug.check(router_type or hasattr(self, "router_type"), "Must specify a routing grid type.")
+
self.init_bbox(self.bbox, self.margin)
- self.rg = router_type(self.ll, self.ur, self.track_width)
+
+ if router_type:
+ self.router_type = router_type
+ self.rg = router_type(self.ll, self.ur, self.track_width)
+ else:
+ self.rg = self.router_type(self.ll, self.ur, self.track_width)
def clear_pins(self):
"""
@@ -222,9 +229,9 @@ class router(router_tech):
# Enclose the continguous grid units in a metal
# rectangle to fix some DRCs
- start_time = datetime.now()
- self.enclose_pins()
- print_time("Enclosing pins", datetime.now(), start_time, 4)
+ #start_time = datetime.now()
+ #self.enclose_pins()
+ #print_time("Enclosing pins", datetime.now(), start_time, 4)
# MRG: Removing this code for now. The later compute enclosure code
# assumes that all pins are touching and this may produce sets of pins
@@ -368,9 +375,10 @@ class router(router_tech):
# This is just a virtual function
pass
- def prepare_blockages(self):
+ def prepare_blockages(self, src=None, dest=None):
"""
Reset and add all of the blockages in the design.
+ Skip adding blockages from src and dest component if specified as a tuple of name,component.
"""
debug.info(3, "Preparing blockages.")
@@ -393,8 +401,14 @@ class router(router_tech):
# Now go and block all of the blockages due to pin shapes.
# Some of these will get unblocked later if they are the source/target.
for name in self.pin_groups:
- # This should be a superset of the grids...
- blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages}
+ blockage_grids = []
+ for component_idx, component in enumerate(self.pin_groups[name]):
+ # Skip adding source or dest blockages
+ if src and src[0] == name and src[1] == component_idx:
+ continue
+ if dest and dest[0] == name and dest[1] == component_idx:
+ continue
+ blockage_grids.extend(component.blockages)
self.set_blockages(blockage_grids, True)
# If we have paths that were recently routed, add them as blockages as well.
@@ -560,20 +574,18 @@ class router(router_tech):
debug.info(3, "Converting pin [ {0} , {1} ]".format(ll, ur))
# scale the size bigger to include neaby tracks
- ll = ll.scale(self.track_factor).floor()
- ur = ur.scale(self.track_factor).ceil()
+ ll_scaled = ll.scale(self.track_factor).floor()
+ ur_scaled = ur.scale(self.track_factor).ceil()
# Keep tabs on tracks with sufficient and insufficient overlap
sufficient_list = set()
insufficient_list = set()
zindex = self.get_zindex(pin.lpp)
- 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))
+ for x in range(int(ll_scaled[0]) - expansion, int(ur_scaled[0]) + 1 + expansion):
+ for y in range(int(ll_scaled[1] - expansion), int(ur_scaled[1]) + 1 + expansion):
+ cur_grid = vector3d(x, y, zindex)
+ (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, cur_grid)
if full_overlap:
sufficient_list.update([full_overlap])
if partial_overlap:
@@ -656,6 +668,43 @@ class router(router_tech):
return set([best_coord])
+ def break_on_grids(self, tracks, xvals, yvals):
+ track_list = []
+ for x in xvals:
+ for y in yvals:
+ track_list.append(vector3d(x, y, 0))
+ track_list.append(vector3d(x, y, 1))
+
+ for current in tracks:
+ if current in track_list:
+ breakpoint()
+
+ def divide_pin_to_tracks(self, pin, tracks):
+ """
+ Return a list of pin shape parts that are in the tracks.
+ """
+ # If pin is smaller than a track, just return it.
+ track_pin = self.convert_track_to_shape_pin(list(tracks)[0])
+ if pin.width() < track_pin.width() and pin.height() < track_pin.height():
+ return [pin]
+
+ overlap_pins = []
+ for track in tracks:
+ track_pin = self.convert_track_to_shape_pin(track)
+ overlap_pin = track_pin.intersection(pin)
+
+ # If pin is smaller than minwidth, in one dimension, skip it.
+ min_pin_width = drc("minwidth_{0}". format(pin.layer))
+ if not overlap_pin or (overlap_pin.width() < min_pin_width and overlap_pin.height() < min_pin_width):
+ continue
+ else:
+ overlap_pins.append(overlap_pin)
+
+ debug.check(len(overlap_pins) > 0, "No pins overlapped the tracks.")
+
+ return overlap_pins
+
+
def convert_pin_coord_to_tracks(self, pin, coord):
"""
Return all tracks that an inflated pin overlaps
@@ -879,6 +928,8 @@ class router(router_tech):
This will mark the grids for all pin components as a source.
Marking as source or target also clears blockage status.
"""
+ self.source_name = pin_name
+ self.source_components = []
for i in range(self.num_pin_components(pin_name)):
self.add_pin_component_source(pin_name, i)
@@ -890,6 +941,8 @@ class router(router_tech):
This will mark the grids for all pin components as a target.
Marking as source or target also clears blockage status.
"""
+ self.target_name = pin_name
+ self.target_components = []
for i in range(self.num_pin_components(pin_name)):
self.add_pin_component_target(pin_name, i)
@@ -925,42 +978,36 @@ class router(router_tech):
self.new_pins[name] = pg.pins
- def add_ring_supply_pin(self, name, width=3, space=2):
+ def add_ring_supply_pin(self, name, width=3, space=3):
"""
- Adds a ring supply pin that goes inside the given bbox.
+ Adds a ring supply pin that goes outside the given bbox.
"""
pg = pin_group(name, [], self)
- # Offset two spaces inside and one between the rings
- # Units are in routing grids
- if name == "gnd":
- offset = width + 2 * space
- else:
- offset = space
# LEFT
left_grids = set(self.rg.get_perimeter_list(side="left_ring",
width=width,
margin=self.margin,
- offset=offset,
+ offset=space,
layers=[1]))
# RIGHT
right_grids = set(self.rg.get_perimeter_list(side="right_ring",
width=width,
margin=self.margin,
- offset=offset,
+ offset=space,
layers=[1]))
# TOP
top_grids = set(self.rg.get_perimeter_list(side="top_ring",
width=width,
margin=self.margin,
- offset=offset,
+ offset=space,
layers=[0]))
# BOTTOM
bottom_grids = set(self.rg.get_perimeter_list(side="bottom_ring",
width=width,
margin=self.margin,
- offset=offset,
+ offset=space,
layers=[0]))
horizontal_layer_grids = left_grids | right_grids
@@ -972,6 +1019,7 @@ class router(router_tech):
# Add vias in the overlap points
horizontal_corner_grids = vertical_layer_grids & horizontal_layer_grids
+ corners = []
for g in horizontal_corner_grids:
self.add_via(g)
@@ -984,6 +1032,15 @@ class router(router_tech):
self.pin_groups[name].append(pg)
self.new_pins[name] = pg.pins
+ # Update the bbox so that it now includes the new pins
+ for p in pg.pins:
+ if p.lx() < self.ll.x or p.by() < self.ll.y:
+ self.ll = p.ll()
+ if p.rx() > self.ur.x or p.uy() > self.ur.y:
+ self.ur = p.ur()
+ self.bbox = (self.ll, self.ur)
+ self.create_routing_grid()
+
def get_new_pins(self, name):
return self.new_pins[name]
@@ -991,6 +1048,8 @@ class router(router_tech):
"""
This will mark all the cells on the perimeter of the original layout as a target.
"""
+ self.target_name = ""
+ self.target_components = []
self.rg.add_perimeter_target(side=side)
def num_pin_components(self, pin_name):
@@ -999,6 +1058,15 @@ class router(router_tech):
"""
return len(self.pin_groups[pin_name])
+ def set_pin_component_source(self, pin_name, index):
+ """
+ Add the pin component but also set it as the exclusive one.
+ Used by supply routing with multiple components.
+ """
+ self.source_name = pin_name
+ self.source_components = []
+ self.add_pin_component_source(pin_name, index)
+
def add_pin_component_source(self, pin_name, index):
"""
This will mark only the pin tracks
@@ -1008,6 +1076,7 @@ class router(router_tech):
debug.check(index 1:
+ self.cell.add_route(layers=self.layers,
+ coordinates=abs_path,
+ layer_widths=self.layer_widths)
else:
- # convert the path back to absolute units from tracks
- # This assumes 1-track wide again
- abs_path = [self.convert_point_to_units(x[0]) for x in path]
- # Otherwise, add the route which includes enclosures
- if len(self.layers) > 1:
- self.cell.add_route(layers=self.layers,
- coordinates=abs_path,
- layer_widths=self.layer_widths)
- else:
- self.cell.add_path(layer=self.layers[0],
- coordinates=abs_path,
- width=self.layer_widths[0])
+ self.cell.add_path(layer=self.layers[0],
+ coordinates=abs_path,
+ width=self.layer_widths[0])
+
+ def create_route_connector(self, path_tracks, pin_name, components):
+ """
+ Find a rectangle to connect the track and the off-grid pin of a component.
+ """
+
+ if len(path_tracks) == 0 or len(components) == 0:
+ return
+
+ # Find the track pin
+ track_pins = [self.convert_tracks_to_pin(x) for x in path_tracks]
+
+ # Convert the off-grid pin into parts in each routing grid
+ offgrid_pin_parts = []
+ for component in components:
+ pg = self.pin_groups[pin_name][component]
+ for pin in pg.pins:
+ # Layer min with
+ min_width = drc("minwidth_{}".format(pin.layer))
+
+ # If we intersect, by a min_width, we are done!
+ for track_pin in track_pins:
+ intersection = pin.intersection(track_pin)
+ if intersection and intersection.width() > min_width and intersection.height() > min_width:
+ return
+
+ #self.break_on_grids(pg.grids, xvals=[68], yvals=range(93,100))
+ partial_pin_parts = self.divide_pin_to_tracks(pin, pg.grids)
+ offgrid_pin_parts.extend(partial_pin_parts)
+
+ debug.check(len(offgrid_pin_parts) > 0, "No offgrid pin parts found.")
+
+ # Find closest part
+ closest_track_pin, closest_part_pin = self.find_closest_pin(track_pins, offgrid_pin_parts)
+
+ debug.check(closest_track_pin and closest_part_pin, "Found no closest pins.")
+
+ # Find the bbox of the on-grid track and the off-grid pin part
+ closest_track_pin.bbox([closest_part_pin])
+
+ # Connect to off grid pin to track pin with closest shape
+ self.cell.add_rect(layer=closest_track_pin.layer,
+ offset=closest_track_pin.ll(),
+ width=closest_track_pin.width(),
+ height=closest_track_pin.height())
+
+ def find_closest_pin(self, first_list, second_list):
+ """
+ Find the closest pin in the lists. Does a stupid O(n^2).
+ """
+ min_dist = None
+ min_item = (None, None)
+ for pin1 in first_list:
+ for pin2 in second_list:
+ if pin1.layer != pin2.layer:
+ continue
+ new_dist = pin1.distance(pin2)
+ if min_dist == None or new_dist < min_dist:
+ min_item = (pin1, pin2)
+ min_dist = new_dist
+
+ return min_item
+
def add_single_enclosure(self, track):
"""
@@ -1174,7 +1313,15 @@ class router(router_tech):
self.paths.append(grid_utils.flatten_set(path))
self.add_route(path)
+ self.create_route_connector(path,
+ self.source_name,
+ self.source_components)
+ self.create_route_connector(path,
+ self.target_name,
+ self.target_components)
self.path_blockages.append(self.paths[-1])
+ #self.write_debug_gds("debug_route.gds", False)
+ #breakpoint()
return True
else:
return False
@@ -1274,11 +1421,18 @@ class router(router_tech):
"""
debug.info(2, "Adding router info")
+ show_bbox = False
show_blockages = False
show_blockage_grids = False
show_enclosures = False
show_all_grids = True
+ if show_bbox:
+ self.cell.add_rect(layer="text",
+ offset=vector(self.ll.x, self.ll.y),
+ width=self.ur.x - self.ll.x,
+ height=self.ur.y - self.ll.y)
+
if show_all_grids:
for g in self.rg.map:
self.annotate_grid(g)
diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py
index 7cc41d97..d727e900 100644
--- a/compiler/router/signal_escape_router.py
+++ b/compiler/router/signal_escape_router.py
@@ -63,7 +63,7 @@ class signal_escape_router(router):
print_time("Maze routing pins",datetime.now(), start_time, 3)
- # self.write_debug_gds("final_escape_router.gds",False)
+ #self.write_debug_gds("final_escape_router.gds",False)
return True
diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py
index 340db093..3d8c69eb 100644
--- a/compiler/router/signal_grid.py
+++ b/compiler/router/signal_grid.py
@@ -119,10 +119,11 @@ class signal_grid(grid):
# Expand all directions.
neighbors = curpath.expand_dirs()
+ # Filter the out of region ones
# Filter the blocked ones
- unblocked_neighbors = [x for x in neighbors if not self.is_blocked(x)]
+ valid_neighbors = [x for x in neighbors if self.is_inside(x) and not self.is_blocked(x)]
- return unblocked_neighbors
+ return valid_neighbors
def hpwl(self, src, dest):
"""
diff --git a/compiler/router/signal_router.py b/compiler/router/signal_router.py
index 9fbf871f..11e40a91 100644
--- a/compiler/router/signal_router.py
+++ b/compiler/router/signal_router.py
@@ -59,7 +59,7 @@ class signal_router(router):
self.write_debug_gds(stop_program=False)
return False
- self.write_debug_gds(stop_program=False)
+ #self.write_debug_gds(stop_program=False)
return True
diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py
index 88d02f2e..a018a129 100644
--- a/compiler/router/supply_tree_router.py
+++ b/compiler/router/supply_tree_router.py
@@ -43,6 +43,7 @@ class supply_tree_router(router):
bbox=bbox,
route_track_width=self.route_track_width)
+
def route(self, vdd_name="vdd", gnd_name="gnd"):
"""
Route the two nets in a single layer.
@@ -75,6 +76,9 @@ class supply_tree_router(router):
self.add_ring_supply_pin(self.vdd_name)
self.add_ring_supply_pin(self.gnd_name)
+ #self.write_debug_gds("initial_tree_router.gds",False)
+ #breakpoint()
+
# Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter
start_time = datetime.now()
@@ -82,8 +86,6 @@ class supply_tree_router(router):
self.route_pins(gnd_name)
print_time("Maze routing supplies", datetime.now(), start_time, 3)
- # self.write_debug_gds("final_tree_router.gds",False)
-
# Did we route everything??
if not self.check_all_routed(vdd_name):
return False
@@ -144,15 +146,15 @@ class supply_tree_router(router):
# Route MST components
for index, (src, dest) in enumerate(connections):
- if not (index % 100):
+ if not (index % 25):
debug.info(1, "{0} supply segments routed, {1} remaining.".format(index, len(connections) - index))
self.route_signal(pin_name, src, dest)
- # if pin_name == "gnd":
- # print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages))
- # print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages))
- # self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False)
+ if False and pin_name == "gnd":
+ print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages))
+ print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages))
+ self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False)
- #self.write_debug_gds("final.gds", True)
+ #self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False)
#return
def route_signal(self, pin_name, src_idx, dest_idx):
@@ -161,7 +163,7 @@ class supply_tree_router(router):
# Second pass, clear prior pin blockages so that you can route over other metal
# of the same supply. Otherwise, this can create a lot of circular routes due to accidental overlaps.
for unblock_routes in [False, True]:
- for detour_scale in [5 * pow(2, x) for x in range(5)]:
+ for detour_scale in [2 * pow(2, x) for x in range(5)]:
debug.info(2, "Routing {0} to {1} with scale {2}".format(src_idx, dest_idx, detour_scale))
# Clear everything in the routing grid.
@@ -169,7 +171,7 @@ class supply_tree_router(router):
# This is inefficient since it is non-incremental, but it was
# easier to debug.
- self.prepare_blockages()
+ self.prepare_blockages(src=(pin_name, src_idx), dest=(pin_name, dest_idx))
if unblock_routes:
msg = "Unblocking supply self blockages to improve access (may cause DRC errors):\n{0}\n{1})"
debug.warning(msg.format(pin_name,
@@ -178,15 +180,17 @@ class supply_tree_router(router):
# Add the single component of the pin as the source
# which unmarks it as a blockage too
- self.add_pin_component_source(pin_name, src_idx)
+ self.set_pin_component_source(pin_name, src_idx)
# Marks all pin components except index as target
# which unmarks it as a blockage too
- self.add_pin_component_target(pin_name, dest_idx)
+ self.set_pin_component_target(pin_name, dest_idx)
# Actually run the A* router
if self.run_router(detour_scale=detour_scale):
return
+ #if detour_scale > 2:
+ # self.write_debug_gds("route_{0}_{1}_d{2}.gds".format(src_idx, dest_idx, detour_scale), False)
self.write_debug_gds("debug_route.gds", True)
diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py
index 90855d8e..b656f547 100644
--- a/compiler/sram/sram.py
+++ b/compiler/sram/sram.py
@@ -61,26 +61,6 @@ class sram():
def gds_write(self, name):
self.s.gds_write(name)
- # This addresses problems with flat GDS namespaces when we
- # want to merge this SRAM with other SRAMs.
- if OPTS.uniquify:
- import gdsMill
- gds = gdsMill.VlsiLayout()
- reader = gdsMill.Gds2reader(gds)
- reader.loadFromFile(name)
-
- # Uniquify but skip the library cells since they are hard coded
- try:
- from tech import library_prefix_name
- except ImportError:
- library_prefix_name = None
- gds.uniquify(library_prefix_name)
-
- writer = gdsMill.Gds2writer(gds)
- unique_name = name.replace(".gds", "_unique.gds")
- writer.writeToFile(unique_name)
- shutil.move(unique_name, name)
-
def verilog_write(self, name):
self.s.verilog_write(name)
diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py
index ce10eb4f..a410e04a 100644
--- a/compiler/sram/sram_1bank.py
+++ b/compiler/sram/sram_1bank.py
@@ -336,31 +336,25 @@ class sram_1bank(sram_base):
# Some technologies have an isolation
self.add_dnwell(inflate=2.5)
+ # Route the supplies together and/or to the ring/stripes.
+ # This is done with the original bbox since the escape routes need to
+ # be outside of the ring for OpenLane
+ rt = router_tech(self.supply_stack, 1)
+ init_bbox = self.get_bbox(side="ring",
+ margin=rt.track_width)
+
# We need the initial bbox for the supply rings later
# because the perimeter pins will change the bbox
# Route the pins to the perimeter
- pre_bbox = None
if OPTS.perimeter_pins:
- rt = router_tech(self.supply_stack, 1)
-
- if OPTS.supply_pin_type in ["ring", "left", "right", "top", "bottom"]:
- big_margin = 12 * rt.track_width
- little_margin = 2 * rt.track_width
- else:
- big_margin = 6 * rt.track_width
- little_margin = 0
-
- pre_bbox = self.get_bbox(side="ring",
- big_margin=rt.track_width)
-
- bbox = self.get_bbox(side=OPTS.supply_pin_type,
- big_margin=big_margin,
- little_margin=little_margin)
+ # We now route the escape routes far enough out so that they will
+ # reach past the power ring or stripes on the sides
+ bbox = self.get_bbox(side="ring",
+ margin=11*rt.track_width)
self.route_escape_pins(bbox)
- # 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(pre_bbox)
+ self.route_supplies(init_bbox)
+
def route_dffs(self, add_routes=True):
diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py
index ec690610..4bcc1cb3 100644
--- a/compiler/sram/sram_base.py
+++ b/compiler/sram/sram_base.py
@@ -213,7 +213,7 @@ class sram_base(design, verilog, lef):
self.add_lvs_correspondence_points()
- self.offset_all_coordinates()
+ #self.offset_all_coordinates()
highest_coord = self.find_highest_coords()
self.width = highest_coord[0]
@@ -243,6 +243,7 @@ class sram_base(design, verilog, lef):
for inst in self.insts:
self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name])
+ # Pick the router type
if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins)
return
@@ -250,6 +251,7 @@ class sram_base(design, verilog, lef):
from supply_grid_router import supply_grid_router as router
else:
from supply_tree_router import supply_tree_router as router
+
rtr=router(layers=self.supply_stack,
design=self,
bbox=bbox,
@@ -257,6 +259,8 @@ class sram_base(design, verilog, lef):
rtr.route()
+ # This removes the original pre-supply routing pins and replaces them
+ # with the ring or peripheral power pins
if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]:
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
diff --git a/compiler/tests/06_column_decoder_16row_test.py b/compiler/tests/06_column_decoder_16row_test.py
new file mode 100755
index 00000000..2823b647
--- /dev/null
+++ b/compiler/tests/06_column_decoder_16row_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 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
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class column_decoder_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 0
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ # Checks 2x4 and 2-input NAND decoder
+ debug.info(1, "Testing 16 row sample for column_decoder")
+ a = factory.create(module_type="column_decoder", col_addr_size=4)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py
new file mode 100755
index 00000000..3c4ee7ee
--- /dev/null
+++ b/compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 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
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class hierarchical_decoder_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two
+ debug.info(1, "Testing 132 row sample for hierarchical_decoder")
+ a = factory.create(module_type="hierarchical_decoder", num_outputs=132)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py
new file mode 100755
index 00000000..ea50b36c
--- /dev/null
+++ b/compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 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
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class hierarchical_decoder_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ # Checks 2x4 and 2-input NAND decoder
+ debug.info(1, "Testing 16 row sample for hierarchical_decoder")
+ a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py
new file mode 100755
index 00000000..549ce54f
--- /dev/null
+++ b/compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 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
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class hierarchical_decoder_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ # Checks 2x4 and 2-input NAND decoder with non-power-of-two
+ debug.info(1, "Testing 17 row sample for hierarchical_decoder")
+ a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py
deleted file mode 100755
index 867cdaff..00000000
--- a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env python3
-# See LICENSE for licensing information.
-#
-# Copyright (c) 2016-2021 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
-from testutils import *
-import sys, os
-sys.path.append(os.getenv("OPENRAM_HOME"))
-import globals
-from globals import OPTS
-from sram_factory import factory
-import debug
-
-
-class hierarchical_decoder_1rw_1r_test(openram_test):
-
- def runTest(self):
- config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
- globals.init_openram(config_file)
-
- # Use the 2 port cell since it is usually bigger/easier
- OPTS.num_rw_ports = 1
- OPTS.num_r_ports = 1
- OPTS.num_w_ports = 0
- globals.setup_bitcell()
-
- # Checks 2x4 and 2-input NAND decoder
- debug.info(1, "Testing 16 row sample for hierarchical_decoder")
- a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
- self.local_check(a)
-
- # Checks 2x4 and 2-input NAND decoder with non-power-of-two
- debug.info(1, "Testing 17 row sample for hierarchical_decoder")
- a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
- self.local_check(a)
-
- # Checks 2x4 with 3x8 and 2-input NAND decoder
- debug.info(1, "Testing 32 row sample for hierarchical_decoder")
- a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
- self.local_check(a)
-
- # Checks 3 x 2x4 and 3-input NAND decoder
- debug.info(1, "Testing 64 row sample for hierarchical_decoder")
- a = factory.create(module_type="hierarchical_decoder", num_outputs=64)
- self.local_check(a)
-
- # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two
- debug.info(1, "Testing 132 row sample for hierarchical_decoder")
- a = factory.create(module_type="hierarchical_decoder", num_outputs=132)
- self.local_check(a)
-
- # Checks 3 x 3x8 and 3-input NAND decoder
- debug.info(1, "Testing 512 row sample for hierarchical_decoder")
- a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
- self.local_check(a)
-
- globals.end_openram()
-
-# run the test from the command line
-if __name__ == "__main__":
- (OPTS, args) = globals.parse_args()
- del sys.argv[1:]
- header(__file__, OPTS.tech_name)
- unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py
new file mode 100755
index 00000000..6c2f6bd6
--- /dev/null
+++ b/compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 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
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class hierarchical_decoder_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ # Checks 2x4 with 3x8 and 2-input NAND decoder
+ debug.info(1, "Testing 32 row sample for hierarchical_decoder")
+ a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py
new file mode 100755
index 00000000..201fc399
--- /dev/null
+++ b/compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 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
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class hierarchical_decoder_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ # Checks 2x4 and 2-input NAND decoder
+ debug.info(1, "Testing 4096 row sample for hierarchical_decoder")
+ a = factory.create(module_type="hierarchical_decoder", num_outputs=4096)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py
new file mode 100755
index 00000000..f53f4838
--- /dev/null
+++ b/compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 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
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class hierarchical_decoder_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ # Checks 3 x 3x8 and 3-input NAND decoder
+ debug.info(1, "Testing 512 row sample for hierarchical_decoder")
+ a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py
new file mode 100755
index 00000000..af9fb4f4
--- /dev/null
+++ b/compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 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
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class hierarchical_decoder_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ # Checks 3 x 2x4 and 3-input NAND decoder
+ debug.info(1, "Testing 64 row sample for hierarchical_decoder")
+ a = factory.create(module_type="hierarchical_decoder", num_outputs=64)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_bothrbl_1rw_1r_test.py
similarity index 55%
rename from compiler/tests/14_replica_bitcell_array_1rw_1r_test.py
rename to compiler/tests/14_replica_bitcell_array_bothrbl_1rw_1r_test.py
index 0523b471..8dad9f9b 100755
--- a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py
+++ b/compiler/tests/14_replica_bitcell_array_bothrbl_1rw_1r_test.py
@@ -25,21 +25,6 @@ class replica_bitcell_array_1rw_1r_test(openram_test):
OPTS.num_w_ports = 0
globals.setup_bitcell()
- debug.info(2, "Testing 4x4 non-replica array for dp cell")
- a = factory.create(module_type="replica_bitcell_array",
- cols=4,
- rows=4,
- rbl=[1, 1])
- self.local_check(a)
-
- debug.info(2, "Testing 4x4 left replica array for dp cell")
- a = factory.create(module_type="replica_bitcell_array",
- cols=4,
- rows=4,
- rbl=[1, 1],
- left_rbl=[0])
- self.local_check(a)
-
debug.info(2, "Testing 4x4 array left and right replica for dp cell")
a = factory.create(module_type="replica_bitcell_array",
cols=4,
@@ -49,17 +34,6 @@ class replica_bitcell_array_1rw_1r_test(openram_test):
right_rbl=[1])
self.local_check(a)
-
- # Sky 130 has restrictions on the symmetries
- if OPTS.tech_name != "sky130":
- debug.info(2, "Testing 4x4 array right only replica for dp cell")
- a = factory.create(module_type="replica_bitcell_array",
- cols=4,
- rows=4,
- rbl=[1, 1],
- right_rbl=[1])
- self.local_check(a)
-
globals.end_openram()
# run the test from the command line
diff --git a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py
new file mode 100755
index 00000000..3137802b
--- /dev/null
+++ b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 Regents of the University of California
+# All rights reserved.
+#
+import unittest
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class replica_bitcell_array_1rw_1r_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ debug.info(2, "Testing 4x4 left replica array for dp cell")
+ a = factory.create(module_type="replica_bitcell_array",
+ cols=4,
+ rows=4,
+ rbl=[1, 1],
+ left_rbl=[0])
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py
new file mode 100755
index 00000000..7f7ee74c
--- /dev/null
+++ b/compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 Regents of the University of California
+# All rights reserved.
+#
+import unittest
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class replica_bitcell_array_1rw_1r_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ debug.info(2, "Testing 4x4 non-replica array for dp cell")
+ a = factory.create(module_type="replica_bitcell_array",
+ cols=4,
+ rows=4,
+ rbl=[1, 1])
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/18_port_address_16rows_1rw_1r_test.py b/compiler/tests/18_port_address_16rows_1rw_1r_test.py
new file mode 100755
index 00000000..ff39eeed
--- /dev/null
+++ b/compiler/tests/18_port_address_16rows_1rw_1r_test.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 Regents of the University of California
+# All rights reserved.
+#
+import unittest
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class port_address_1rw_1r_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ # Use the 2 port cell since it is usually bigger/easier
+ OPTS.num_rw_ports = 1
+ OPTS.num_r_ports = 1
+ OPTS.num_w_ports = 0
+ globals.setup_bitcell()
+
+ debug.info(1, "Port address 16 rows")
+ a = factory.create("port_address", cols=16, rows=16, port=0)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/18_port_address_16rows_test.py b/compiler/tests/18_port_address_16rows_test.py
new file mode 100755
index 00000000..a60508de
--- /dev/null
+++ b/compiler/tests/18_port_address_16rows_test.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 Regents of the University of California
+# All rights reserved.
+#
+import unittest
+from testutils import *
+import sys, os
+sys.path.append(os.getenv("OPENRAM_HOME"))
+import globals
+from globals import OPTS
+from sram_factory import factory
+import debug
+
+
+class port_address_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ globals.init_openram(config_file)
+
+ debug.info(1, "Port address 16 rows")
+ a = factory.create("port_address", cols=16, rows=16, port=0)
+ self.local_check(a)
+
+ globals.end_openram()
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = globals.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_256rows_1rw_1r_test.py
similarity index 88%
rename from compiler/tests/18_port_address_1rw_1r_test.py
rename to compiler/tests/18_port_address_256rows_1rw_1r_test.py
index f81196e8..e9c11bd7 100755
--- a/compiler/tests/18_port_address_1rw_1r_test.py
+++ b/compiler/tests/18_port_address_256rows_1rw_1r_test.py
@@ -26,10 +26,6 @@ class port_address_1rw_1r_test(openram_test):
OPTS.num_w_ports = 0
globals.setup_bitcell()
- debug.info(1, "Port address 16 rows")
- a = factory.create("port_address", cols=16, rows=16, port=0)
- self.local_check(a)
-
debug.info(1, "Port address 256 rows")
a = factory.create("port_address", cols=256, rows=256, port=1)
self.local_check(a)
diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_512rows_test.py
similarity index 86%
rename from compiler/tests/18_port_address_test.py
rename to compiler/tests/18_port_address_512rows_test.py
index 7ecf3288..120ec9be 100755
--- a/compiler/tests/18_port_address_test.py
+++ b/compiler/tests/18_port_address_512rows_test.py
@@ -20,10 +20,6 @@ class port_address_test(openram_test):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
- debug.info(1, "Port address 16 rows")
- a = factory.create("port_address", cols=16, rows=16, port=0)
- self.local_check(a)
-
debug.info(1, "Port address 512 rows")
a = factory.create("port_address", cols=256, rows=512, port=0)
self.local_check(a)
diff --git a/compiler/tests/Makefile b/compiler/tests/Makefile
index c5d508c4..2cd5c17d 100644
--- a/compiler/tests/Makefile
+++ b/compiler/tests/Makefile
@@ -120,8 +120,8 @@ $(TEST_BASES):
@mkdir -p results/$*/tmp
@docker run \
-v $(TOP_DIR):/openram \
- -v $(FREEPDK45):/freepdk45\
- -e FREEPDK45=/freepdk45\
+ -v $(FREEPDK45):/freepdk45 \
+ -e FREEPDK45=/freepdk45 \
-v $(PDK_ROOT):/pdk \
-e PDK_ROOT=/pdk \
-e PDKPATH=/pdk/sky130A \
diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py
index dc3739ab..44c3e774 100644
--- a/compiler/tests/configs/config.py
+++ b/compiler/tests/configs/config.py
@@ -14,3 +14,6 @@ tech_name = OPTS.tech_name
nominal_corner_only = True
check_lvsdrc = True
+#route_supplies = False
+
+output_name = "sram"
diff --git a/compiler/tests/configs/config_back_end.py b/compiler/tests/configs/config_back_end.py
index 3294e979..4bf0aa8b 100644
--- a/compiler/tests/configs/config_back_end.py
+++ b/compiler/tests/configs/config_back_end.py
@@ -15,4 +15,5 @@ nominal_corner_only = True
check_lvsdrc = True
spice_name = "ngspice"
+output_name = "sram"
diff --git a/compiler/tests/configs/config_front_end.py b/compiler/tests/configs/config_front_end.py
index 4486b077..2b42a914 100644
--- a/compiler/tests/configs/config_front_end.py
+++ b/compiler/tests/configs/config_front_end.py
@@ -11,3 +11,5 @@ num_words = 16
tech_name = OPTS.tech_name
+output_name = "sram"
+
diff --git a/docker/Dockerfile b/docker/Dockerfile
index e1045107..3922477c 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -24,13 +24,14 @@ RUN apt-get install --no-install-recommends -y libx11-dev libcairo2-dev
RUN apt-get install --no-install-recommends -y qt5-default qtcreator ruby-full ruby-dev python3-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 libqt5svg5-dev libqt5designer5 libqt5designercomponents5 libqt5xmlpatterns5-dev qttools5-dev
### Klayout ###
-ARG KLAYOUT_COMMIT=v0.27.8
+#ARG KLAYOUT_COMMIT=v0.27.8
+ARG KLAYOUT_COMMIT=ea1bf40a1ee1c1c934e47a0020417503ab3d7e7e
WORKDIR /root
RUN git clone https://github.com/KLayout/klayout
WORKDIR /root/klayout
RUN git checkout ${KLAYOUT_COMMIT}
-RUN ./build.sh -qt5 -j 8 \
- && cp -r bin-release /usr/local/klayout
+RUN ./build.sh -qt5 -debug -j 8 \
+ && cp -r bin-debug /usr/local/klayout
RUN rm -rf /root/klayout
### Trilinos ###
diff --git a/macros/Makefile b/macros/Makefile
index 31943ad9..ca7bcdc7 100644
--- a/macros/Makefile
+++ b/macros/Makefile
@@ -33,15 +33,15 @@ configs:
.PHONY: configs
-BROKEN := \
- sky130_sram_1kbyte_1r1w_8x1024_8 \
- sky130_sram_1kbyte_1rw_32x256_8 \
- sky130_sram_2kbyte_1rw_32x512_8 \
- sky130_sram_4kbyte_1rw_32x1024_8 \
+BROKEN :=
WORKING_STAMPS=$(filter-out $(addsuffix .ok, $(BROKEN)), $(STAMPS))
+EXAMPLE_STAMPS=$(filter example%, $(WORKING_STAMPS))
+SKY130_STAMPS=$(filter sky130%, $(WORKING_STAMPS))
+FREEPDK45_STAMPS=$(filter freepdk45%, $(WORKING_STAMPS))
+SCN4M_SUBM_STAMPS=$(filter scn4m_subm%, $(WORKING_STAMPS))
-all: | configs
+all: | configs
@echo
@echo "Building following working configs"
@for S in $(WORKING_STAMPS); do echo " - $$S"; done
@@ -49,6 +49,18 @@ all: | configs
$(MAKE) $(WORKING_STAMPS)
@echo "Built all macros."
+example: $(EXAMPLE_STAMPS)
+.PHONY: example
+
+sky130: $(SKY130_STAMPS)
+.PHONY: sky130
+
+freepdk45: $(FREEPDK45_STAMPS)
+.PHONY: freepdk45
+
+scn4m_subm: $(SCN4M_SUBM_STAMPS)
+.PHONY: scn4m_subm
+
%.ok: configs/%.py
@echo "Building $*"
@mkdir -p $*
diff --git a/macros/configs/big_config_scn4m_subm.py b/macros/configs/example_config_big_scn4m_subm.py
similarity index 100%
rename from macros/configs/big_config_scn4m_subm.py
rename to macros/configs/example_config_big_scn4m_subm.py
diff --git a/macros/configs/giant_config_scn4m_subm.py b/macros/configs/example_config_giant_scn4m_subm.py
similarity index 100%
rename from macros/configs/giant_config_scn4m_subm.py
rename to macros/configs/example_config_giant_scn4m_subm.py
diff --git a/macros/configs/medium_config_scn4m_subm.py b/macros/configs/example_config_medium_scn4m_subm.py
similarity index 100%
rename from macros/configs/medium_config_scn4m_subm.py
rename to macros/configs/example_config_medium_scn4m_subm.py
diff --git a/macros/configs/riscv_freepdk45_8kbyte.py b/macros/configs/freepdk45_sram_1rw1r_32x2048_8.py
similarity index 100%
rename from macros/configs/riscv_freepdk45_8kbyte.py
rename to macros/configs/freepdk45_sram_1rw1r_32x2048_8.py
diff --git a/macros/configs/riscv_sky130_1kbyte_1rw.py b/macros/configs/riscv_sky130_1kbyte_1rw.py
deleted file mode 100644
index 9bdf47ed..00000000
--- a/macros/configs/riscv_sky130_1kbyte_1rw.py
+++ /dev/null
@@ -1,25 +0,0 @@
-word_size = 32
-num_words = 256
-write_size = 8
-
-local_array_size = 16
-
-num_rw_ports = 1
-num_r_ports = 0
-num_w_ports = 0
-
-tech_name = "sky130"
-nominal_corner_only = True
-
-route_supplies = False
-check_lvsdrc = True
-perimeter_pins = False
-#netlist_only = True
-#analytical_delay = False
-output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
- num_r_ports,
- num_w_ports,
- word_size,
- num_words,
- tech_name)
-output_path = "macro/{}".format(output_name)
diff --git a/macros/configs/riscv_sky130_1kbyte_1rw1r.py b/macros/configs/riscv_sky130_1kbyte_1rw1r.py
deleted file mode 100644
index d0b47857..00000000
--- a/macros/configs/riscv_sky130_1kbyte_1rw1r.py
+++ /dev/null
@@ -1,25 +0,0 @@
-word_size = 32
-num_words = 256
-write_size = 8
-
-#local_array_size = 16
-
-num_rw_ports = 1
-num_r_ports = 1
-num_w_ports = 0
-
-tech_name = "sky130"
-nominal_corner_only = True
-
-#route_supplies = False
-check_lvsdrc = True
-#perimeter_pins = False
-#netlist_only = True
-#analytical_delay = False
-output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
- num_r_ports,
- num_w_ports,
- word_size,
- num_words,
- tech_name)
-output_path = "macro/{}".format(output_name)
diff --git a/macros/configs/riscv_sky130_2kbyte_1rw.py b/macros/configs/riscv_sky130_2kbyte_1rw.py
deleted file mode 100644
index b85df3f9..00000000
--- a/macros/configs/riscv_sky130_2kbyte_1rw.py
+++ /dev/null
@@ -1,25 +0,0 @@
-word_size = 32
-num_words = 512
-write_size = 8
-
-local_array_size = 16
-
-num_rw_ports = 1
-num_r_ports = 0
-num_w_ports = 0
-
-tech_name = "sky130"
-nominal_corner_only = True
-
-route_supplies = False
-check_lvsdrc = True
-perimeter_pins = False
-#netlist_only = True
-#analytical_delay = False
-output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
- num_r_ports,
- num_w_ports,
- word_size,
- num_words,
- tech_name)
-output_path = "macro/{}".format(output_name)
diff --git a/macros/configs/riscv_sky130_2kbyte_1rw1r.py b/macros/configs/riscv_sky130_2kbyte_1rw1r.py
deleted file mode 100644
index e94882e9..00000000
--- a/macros/configs/riscv_sky130_2kbyte_1rw1r.py
+++ /dev/null
@@ -1,25 +0,0 @@
-word_size = 32
-num_words = 512
-write_size = 8
-
-local_array_size = 16
-
-num_rw_ports = 1
-num_r_ports = 1
-num_w_ports = 0
-
-tech_name = "sky130"
-nominal_corner_only = True
-
-route_supplies = False
-check_lvsdrc = True
-perimeter_pins = False
-#netlist_only = True
-#analytical_delay = False
-output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
- num_r_ports,
- num_w_ports,
- word_size,
- num_words,
- tech_name)
-output_path = "macro/{}".format(output_name)
diff --git a/macros/configs/riscv_sky130_4kbyte_1rw.py b/macros/configs/riscv_sky130_4kbyte_1rw.py
deleted file mode 100644
index 1b6cdc07..00000000
--- a/macros/configs/riscv_sky130_4kbyte_1rw.py
+++ /dev/null
@@ -1,25 +0,0 @@
-word_size = 32
-num_words = 1024
-write_size = 8
-
-local_array_size = 16
-
-num_rw_ports = 1
-num_r_ports = 0
-num_w_ports = 0
-
-tech_name = "sky130"
-nominal_corner_only = True
-
-route_supplies = False
-check_lvsdrc = True
-perimeter_pins = False
-#netlist_only = True
-#analytical_delay = False
-output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
- num_r_ports,
- num_w_ports,
- word_size,
- num_words,
- tech_name)
-output_path = "macro/{}".format(output_name)
diff --git a/macros/configs/riscv_sky130_4kbyte_1rw1r.py b/macros/configs/riscv_sky130_4kbyte_1rw1r.py
deleted file mode 100644
index 2d53df31..00000000
--- a/macros/configs/riscv_sky130_4kbyte_1rw1r.py
+++ /dev/null
@@ -1,25 +0,0 @@
-word_size = 32
-num_words = 1024
-write_size = 8
-
-local_array_size = 16
-
-num_rw_ports = 1
-num_r_ports = 1
-num_w_ports = 0
-
-tech_name = "sky130"
-nominal_corner_only = True
-
-route_supplies = False
-check_lvsdrc = True
-perimeter_pins = False
-#netlist_only = True
-#analytical_delay = False
-output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports,
- num_r_ports,
- num_w_ports,
- word_size,
- num_words,
- tech_name)
-output_path = "macro/{}".format(output_name)
diff --git a/macros/configs/riscv_scn4m_subm_16kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py
similarity index 100%
rename from macros/configs/riscv_scn4m_subm_16kbyte_1rw1r.py
rename to macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py
diff --git a/macros/configs/riscv_scn4m_subm_1kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py
similarity index 100%
rename from macros/configs/riscv_scn4m_subm_1kbyte_1rw1r.py
rename to macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py
diff --git a/macros/configs/riscv_scn4m_subm_2kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py
similarity index 100%
rename from macros/configs/riscv_scn4m_subm_2kbyte_1rw1r.py
rename to macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py
diff --git a/macros/configs/riscv_scn4m_subm_32kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py
similarity index 98%
rename from macros/configs/riscv_scn4m_subm_32kbyte_1rw1r.py
rename to macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py
index 87ddb5eb..285b1dbf 100644
--- a/macros/configs/riscv_scn4m_subm_32kbyte_1rw1r.py
+++ b/macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py
@@ -1,3 +1,4 @@
+num_banks=2
word_size = 32
num_words = 8192
write_size = 8
diff --git a/macros/configs/riscv_scn4m_subm_4kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py
similarity index 100%
rename from macros/configs/riscv_scn4m_subm_4kbyte_1rw1r.py
rename to macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py
diff --git a/macros/configs/riscv_scn4m_subm_8kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py
similarity index 100%
rename from macros/configs/riscv_scn4m_subm_8kbyte_1rw1r.py
rename to macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py
diff --git a/technology/freepdk45/gds_lib/cell_1rw.gds b/technology/freepdk45/gds_lib/cell_1rw.gds
index 3a67e40b..e8116b9e 100644
Binary files a/technology/freepdk45/gds_lib/cell_1rw.gds and b/technology/freepdk45/gds_lib/cell_1rw.gds differ
diff --git a/technology/freepdk45/gds_lib/cell_2rw.gds b/technology/freepdk45/gds_lib/cell_2rw.gds
index 3ceea617..728b20a7 100644
Binary files a/technology/freepdk45/gds_lib/cell_2rw.gds and b/technology/freepdk45/gds_lib/cell_2rw.gds differ
diff --git a/technology/freepdk45/gds_lib/dummy_cell_1rw.gds b/technology/freepdk45/gds_lib/dummy_cell_1rw.gds
index cbfcb064..92d2b130 100644
Binary files a/technology/freepdk45/gds_lib/dummy_cell_1rw.gds and b/technology/freepdk45/gds_lib/dummy_cell_1rw.gds differ
diff --git a/technology/freepdk45/gds_lib/dummy_cell_2rw.gds b/technology/freepdk45/gds_lib/dummy_cell_2rw.gds
index f5a4600f..1c08cb4f 100644
Binary files a/technology/freepdk45/gds_lib/dummy_cell_2rw.gds and b/technology/freepdk45/gds_lib/dummy_cell_2rw.gds differ
diff --git a/technology/freepdk45/gds_lib/replica_cell_1rw.gds b/technology/freepdk45/gds_lib/replica_cell_1rw.gds
index cef0722d..076a8049 100644
Binary files a/technology/freepdk45/gds_lib/replica_cell_1rw.gds and b/technology/freepdk45/gds_lib/replica_cell_1rw.gds differ
diff --git a/technology/freepdk45/gds_lib/replica_cell_2rw.gds b/technology/freepdk45/gds_lib/replica_cell_2rw.gds
index 1b2564a4..f7bec774 100644
Binary files a/technology/freepdk45/gds_lib/replica_cell_2rw.gds and b/technology/freepdk45/gds_lib/replica_cell_2rw.gds differ
diff --git a/technology/freepdk45/tech/freepdk45.lylvs b/technology/freepdk45/tech/freepdk45.lylvs
index 934a981c..5500d9e3 100644
--- a/technology/freepdk45/tech/freepdk45.lylvs
+++ b/technology/freepdk45/tech/freepdk45.lylvs
@@ -214,7 +214,7 @@ connect_global(pwell, "PWELL")
connect_global(nwell, "NWELL")
connect_global(bulk, "BULK")
-for pat in %w(pinv* pnor* pnand* and?_dec* write_driver* port_address* replica_bitcell_array*)
+for pat in %w(*pinv* *pnor* *pnand* *and?_dec* *write_driver* *port_address* *replica_bitcell_array*)
connect_explicit(pat, [ "NWELL", "vdd" ])
connect_explicit(pat, [ "BULK", "PWELL", "gnd" ])
end
diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py
index d351ae36..7c1ac84b 100644
--- a/technology/freepdk45/tech/tech.py
+++ b/technology/freepdk45/tech/tech.py
@@ -31,7 +31,6 @@ tech_modules = module_type()
# Custom cell properties
###################################################
cell_properties = cell_properties()
-cell_properties.bitcell_power_pin_directions = ("V", "V")
###################################################
# Custom cell properties
diff --git a/technology/scn4m_subm/gds_lib/cell_1rw.gds b/technology/scn4m_subm/gds_lib/cell_1rw.gds
index 92523dc1..6f4d7b26 100644
Binary files a/technology/scn4m_subm/gds_lib/cell_1rw.gds and b/technology/scn4m_subm/gds_lib/cell_1rw.gds differ
diff --git a/technology/scn4m_subm/gds_lib/cell_2rw.gds b/technology/scn4m_subm/gds_lib/cell_2rw.gds
index 0a0e0f8d..cc0c0873 100644
Binary files a/technology/scn4m_subm/gds_lib/cell_2rw.gds and b/technology/scn4m_subm/gds_lib/cell_2rw.gds differ
diff --git a/technology/scn4m_subm/gds_lib/dummy_cell_1rw.gds b/technology/scn4m_subm/gds_lib/dummy_cell_1rw.gds
index b27e5e02..3f678558 100644
Binary files a/technology/scn4m_subm/gds_lib/dummy_cell_1rw.gds and b/technology/scn4m_subm/gds_lib/dummy_cell_1rw.gds differ
diff --git a/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds b/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds
index 00bba7f7..f8fd6760 100644
Binary files a/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds and b/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds differ
diff --git a/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds b/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds
index 53525453..a5b5e3d2 100644
Binary files a/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds and b/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds differ
diff --git a/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds b/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds
index c91147fd..df0f9e28 100644
Binary files a/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds and b/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds differ
diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py
index a8c44996..525736a8 100644
--- a/technology/scn4m_subm/tech/tech.py
+++ b/technology/scn4m_subm/tech/tech.py
@@ -30,6 +30,8 @@ tech_modules = module_type()
# Custom cell properties
###################################################
cell_properties = cell_properties()
+cell_properties.bitcell_1port.gnd_layer = "m2"
+cell_properties.bitcell_1port.gnd_dir = "V"
###################################################
# Custom cell properties
diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py
index e81fe476..72250afc 100644
--- a/technology/sky130/tech/tech.py
+++ b/technology/sky130/tech/tech.py
@@ -86,6 +86,13 @@ cell_properties.bitcell_1port.port_map = {'bl': 'BL',
'vpb': 'VPB',
'gnd': 'VGND'}
+cell_properties.bitcell_1port.wl_layer = "m2"
+cell_properties.bitcell_1port.bl_layer = "m1"
+cell_properties.bitcell_1port.vdd_layer = "m1"
+cell_properties.bitcell_1port.vdd_dir = "V"
+cell_properties.bitcell_1port.gnd_layer = "m2"
+cell_properties.bitcell_1port.gnd_dir = "H"
+
cell_properties.bitcell_2port.mirror.x = True
cell_properties.bitcell_2port.mirror.y = True
cell_properties.bitcell_2port.end_caps = True
@@ -98,6 +105,16 @@ cell_properties.bitcell_2port.port_map = {'bl0': 'BL0',
'wl1': 'WL1',
'vdd': 'VDD',
'gnd': 'GND'}
+cell_properties.bitcell_1port.wl_layer = "m2"
+cell_properties.bitcell_1port.vdd_layer = "m2"
+cell_properties.bitcell_1port.vdd_dir = "H"
+cell_properties.bitcell_1port.gnd_layer = "m2"
+cell_properties.bitcell_1port.gnd_dir = "H"
+cell_properties.bitcell_2port.wl_layer = "m2"
+cell_properties.bitcell_2port.vdd_layer = "m1"
+cell_properties.bitcell_2port.vdd_dir = "H"
+cell_properties.bitcell_2port.gnd_layer = "m2"
+cell_properties.bitcell_2port.gnd_dir = "H"
cell_properties.col_cap_1port_bitcell = cell(['br', 'vdd', 'gnd', 'bl', 'gate'],
['INPUT', 'POWER', 'GROUND', 'INPUT', 'INPUT'],