Merge branch 'dev' of github.com:VLSIDA/PrivateRAM into dev

This commit is contained in:
Jesse Cirimelli-Low 2022-05-19 21:51:13 -07:00
commit 825ada8293
101 changed files with 1775 additions and 861 deletions

View File

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

View File

@ -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])
+ <a rel="me" href="https://fosstodon.org/@mrg">@mrg@fostodon.org</a>
# 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = []

View File

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

View File

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

View File

@ -46,6 +46,8 @@ class bitcell_array(bitcell_base_array):
self.add_layout_pins()
self.route_supplies()
self.add_boundary()
self.DRC_LVS()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = []

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<self.num_pin_components(pin_name),
"Pin component index too large.")
self.source_components.append(index)
pin_in_tracks = self.pin_groups[pin_name][index].grids
debug.info(3,"Set source: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_source(pin_in_tracks)
@ -1020,6 +1089,15 @@ class router(router_tech):
self.rg.set_target(p)
self.rg.set_blocked(p, False)
def set_pin_component_target(self, pin_name, index):
"""
Add the pin component but also set it as the exclusive one.
Used by supply routing with multiple components.
"""
self.target_name = pin_name
self.target_components = []
self.add_pin_component_target(pin_name, index)
def add_pin_component_target(self, pin_name, index):
"""
This will mark only the pin tracks
@ -1028,6 +1106,7 @@ class router(router_tech):
"""
debug.check(index<self.num_pin_components(pin_name),"Pin component index too large.")
self.target_components.append(index)
pin_in_tracks = self.pin_groups[pin_name][index].grids
debug.info(3, "Set target: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_target(pin_in_tracks)
@ -1075,25 +1154,85 @@ class router(router_tech):
"""
Add the current wire route to the given design instance.
"""
path = self.prepare_path(path)
prepared_path = self.prepare_path(path)
debug.info(4, "Adding route: {}".format(str(path)))
# If it is only a square, add an enclosure to the track
if len(path) == 1:
self.add_single_enclosure(path[0][0])
debug.info(4, "Adding route: {}".format(str(prepared_path)))
# 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 prepared_path]
if len(self.layers) > 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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"]:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,3 +14,6 @@ tech_name = OPTS.tech_name
nominal_corner_only = True
check_lvsdrc = True
#route_supplies = False
output_name = "sram"

View File

@ -15,4 +15,5 @@ nominal_corner_only = True
check_lvsdrc = True
spice_name = "ngspice"
output_name = "sram"

View File

@ -11,3 +11,5 @@ num_words = 16
tech_name = OPTS.tech_name
output_name = "sram"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
num_banks=2
word_size = 32
num_words = 8192
write_size = 8

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More