mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' of github.com:VLSIDA/PrivateRAM into dev
This commit is contained in:
commit
825ada8293
|
|
@ -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
|
||||
|
|
|
|||
11
README.md
11
README.md
|
|
@ -146,16 +146,18 @@ To run a specific test:
|
|||
```
|
||||
ce openram/compiler/tests
|
||||
make 05_bitcell_array_test
|
||||
|
||||
```
|
||||
To run a specific technology:
|
||||
|
||||
```
|
||||
cd openram/compiler/tests
|
||||
TECHS=scn4m_subm make 05_bitcell_array_test
|
||||
```
|
||||
|
||||
To increase the verbosity of the test, add one (or more) -v options and
|
||||
pass it as an argument to OpenRAM:
|
||||
```
|
||||
ARGS="-v" make 05_bitcell_array_test
|
||||
```
|
||||
|
||||
# Get Involved
|
||||
|
||||
|
|
@ -173,6 +175,7 @@ ARGS="-v" make 05_bitcell_array_test
|
|||
+ [OpenRAM Slack Workspace][Slack]
|
||||
+ [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe])
|
||||
+ [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe])
|
||||
+ <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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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=""):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ class bitcell_array(bitcell_base_array):
|
|||
|
||||
self.add_layout_pins()
|
||||
|
||||
self.route_supplies()
|
||||
|
||||
self.add_boundary()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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 """
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]:
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -14,3 +14,6 @@ tech_name = OPTS.tech_name
|
|||
nominal_corner_only = True
|
||||
check_lvsdrc = True
|
||||
|
||||
#route_supplies = False
|
||||
|
||||
output_name = "sram"
|
||||
|
|
|
|||
|
|
@ -15,4 +15,5 @@ nominal_corner_only = True
|
|||
check_lvsdrc = True
|
||||
spice_name = "ngspice"
|
||||
|
||||
output_name = "sram"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,3 +11,5 @@ num_words = 16
|
|||
|
||||
tech_name = OPTS.tech_name
|
||||
|
||||
output_name = "sram"
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ###
|
||||
|
|
|
|||
|
|
@ -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 $*
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
num_banks=2
|
||||
word_size = 32
|
||||
num_words = 8192
|
||||
write_size = 8
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
Loading…
Reference in New Issue