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