From f45efe3db6b915e40c6ca537157bb417b8a6986c Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 21 Apr 2021 10:07:37 -0700 Subject: [PATCH] Abstracted LEF added. Params for array wordline layers. --- compiler/base/custom_layer_properties.py | 6 +- compiler/base/hierarchy_layout.py | 5 +- compiler/base/lef.py | 87 +++++++++++++++++++----- compiler/base/pin_layout.py | 40 +++++++++-- compiler/modules/local_bitcell_array.py | 32 ++++----- compiler/options.py | 3 + compiler/sram/sram_base.py | 7 +- 7 files changed, 139 insertions(+), 41 deletions(-) diff --git a/compiler/base/custom_layer_properties.py b/compiler/base/custom_layer_properties.py index a8c6509b..eff24f82 100644 --- a/compiler/base/custom_layer_properties.py +++ b/compiler/base/custom_layer_properties.py @@ -125,8 +125,10 @@ class _wordline_driver: class _bitcell_array: def __init__(self, - wordline_layer): + wordline_layer, + wordline_pitch_factor=2): self.wordline_layer = wordline_layer + self.wordline_pitch_factor = wordline_pitch_factor class layer_properties(): @@ -165,7 +167,7 @@ class layer_properties(): self._wordline_driver = _wordline_driver(vertical_supply=False) - self._local_bitcell_array = _bitcell_array(wordline_layer="m3") + self._local_bitcell_array = _bitcell_array(wordline_layer="m2") self._global_bitcell_array = _bitcell_array(wordline_layer="m3") diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 36a54937..1e2add8d 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -674,7 +674,8 @@ class layout(): directions=None, size=[1, 1], implant_type=None, - well_type=None): + well_type=None, + min_area=False): """ Punch a stack of vias from a start layer to a target layer by the center. """ @@ -708,7 +709,7 @@ class layout(): implant_type=implant_type, well_type=well_type) - if cur_layer != from_layer: + if cur_layer != from_layer or min_area: self.add_min_area_rect_center(cur_layer, offset, via.mod.first_layer_width, diff --git a/compiler/base/lef.py b/compiler/base/lef.py index a5c1910a..9ff02816 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -10,6 +10,8 @@ from tech import layer_names import os import shutil from globals import OPTS +from vector import vector +from pin_layout import pin_layout class lef: @@ -68,13 +70,63 @@ class lef: def lef_write(self, lef_name): """ Write the entire lef of the object to the file. """ - if OPTS.drc_exe and OPTS.drc_exe[0] == "magic": - self.magic_lef_write(lef_name) - return + if OPTS.detailed_lef: + debug.info(3, "Writing detailed LEF to {0}".format(lef_name)) + self.detailed_lef_write(lef_name) + else: + debug.info(3, "Writing abstract LEF to {0}".format(lef_name)) + # Can possibly use magic lef write to create the LEF + # if OPTS.drc_exe and OPTS.drc_exe[0] == "magic": + # self.magic_lef_write(lef_name) + # return + self.abstract_lef_write(lef_name) - debug.info(3, "Writing detailed LEF to {0}".format(lef_name)) + def abstract_lef_write(self, lef_name): + # To maintain the indent level easily + self.indent = "" - self.indent = "" # To maintain the indent level easily + self.lef = open(lef_name, "w") + self.lef_write_header() + + # Start with blockages on all layers the size of the block + # minus the pin escape margin (hard coded to 4 x m3 pitch) + # These are a pin_layout to use their geometric functions + perimeter_margin = self.m3_pitch + self.blockages = {} + for layer_name in self.lef_layers: + self.blockages[layer_name]=[] + for layer_name in self.lef_layers: + ll = vector(perimeter_margin, perimeter_margin) + ur = vector(self.width - perimeter_margin, self.height - perimeter_margin) + self.blockages[layer_name].append(pin_layout("", + [ll, ur], + layer_name)) + + # For each pin, remove the blockage and add the pin + for pin_name in self.pins: + pin = self.get_pin(pin_name) + inflated_pin = pin.inflated_pin(multiple=1) + for blockage in self.blockages[pin.layer]: + if blockage.overlaps(inflated_pin): + intersection_shape = blockage.intersection(inflated_pin) + # If it is zero area, don't add the pin + if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]: + 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) + + self.lef_write_pin(pin_name) + + self.lef_write_obstructions(abstracted=True) + self.lef_write_footer() + self.lef.close() + + def detailed_lef_write(self, lef_name): + # To maintain the indent level easily + self.indent = "" self.lef = open(lef_name, "w") self.lef_write_header() @@ -136,24 +188,29 @@ class lef: self.indent = self.indent[:-3] self.lef.write("{0}END {1}\n".format(self.indent, name)) - def lef_write_obstructions(self): + def lef_write_obstructions(self, abstracted=False): """ Write all the obstructions on each layer """ self.lef.write("{0}OBS\n".format(self.indent)) for layer in self.lef_layers: self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[layer])) self.indent += " " - blockages = self.get_blockages(layer, True) - for b in blockages: - self.lef_write_shape(b) + if abstracted: + blockages = self.blockages[layer] + for b in blockages: + self.lef_write_shape(b.rect) + else: + blockages = self.get_blockages(layer, True) + for b in blockages: + self.lef_write_shape(b) self.indent = self.indent[:-3] self.lef.write("{0}END\n".format(self.indent)) - def lef_write_shape(self, rect): - if len(rect) == 2: + def lef_write_shape(self, obj): + if len(obj) == 2: """ Write a LEF rectangle """ self.lef.write("{0}RECT ".format(self.indent)) - for item in rect: - # print(rect) + for item in obj: + # print(obj) self.lef.write(" {0} {1}".format(round(item[0], self.round_grid), round(item[1], @@ -162,12 +219,10 @@ class lef: else: """ Write a LEF polygon """ self.lef.write("{0}POLYGON ".format(self.indent)) - for item in rect: + for item in obj: self.lef.write(" {0} {1}".format(round(item[0], self.round_grid), round(item[1], self.round_grid))) - # for i in range(0,len(rect)): - # self.lef.write(" {0} {1}".format(round(rect[i][0],self.round_grid), round(rect[i][1],self.round_grid))) self.lef.write(" ;\n") diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index e8c6f0a5..e6baa4fc 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -139,13 +139,13 @@ class pin_layout: min_area = drc("{}_minarea".format(self.layer)) pass - def inflate(self, spacing=None): + def inflate(self, spacing=None, multiple=0.5): """ Inflate the rectangle by the spacing (or other rule) and return the new rectangle. """ if not spacing: - spacing = 0.5*drc("{0}_to_{0}".format(self.layer)) + spacing = multiple*drc("{0}_to_{0}".format(self.layer)) (ll, ur) = self.rect spacing = vector(spacing, spacing) @@ -154,15 +154,23 @@ class pin_layout: return (newll, newur) + def inflated_pin(self, spacing=None, multiple=0.5): + """ + Inflate the rectangle by the spacing (or other rule) + and return the new rectangle. + """ + inflated_area = self.inflate(spacing, multiple) + return pin_layout(self.name, inflated_area, self.layer) + def intersection(self, other): """ Check if a shape overlaps with a rectangle """ (ll, ur) = self.rect (oll, our) = other.rect min_x = max(ll.x, oll.x) - max_x = min(ll.x, oll.x) + max_x = min(ur.x, our.x) min_y = max(ll.y, oll.y) - max_y = min(ll.y, oll.y) + max_y = min(ur.y, our.y) return [vector(min_x, min_y), vector(max_x, max_y)] @@ -578,6 +586,30 @@ class pin_layout: return None + def cut(self, shape): + """ + Return a set of shapes that are this shape minus the argument shape. + """ + # Make the unique coordinates in X and Y directions + x_offsets = sorted([self.lx(), self.rx(), shape.lx(), shape.rx()]) + y_offsets = sorted([self.by(), self.uy(), shape.by(), shape.uy()]) + + new_shapes = [] + # Create all of the shapes + for x1, x2 in zip(x_offsets[0:], x_offsets[1:]): + if x1==x2: + continue + for y1, y2 in zip(y_offsets[0:], y_offsets[1:]): + if y1==y2: + continue + new_shape = pin_layout("", [vector(x1, y1), vector(x2, y2)], self.lpp) + # Don't add the existing shape in if it overlaps the pin shape + if new_shape.contains(shape): + continue + new_shapes.append(new_shape) + + return new_shapes + def same_lpp(self, lpp1, lpp2): """ Check if the layers and purposes are the same. diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index d8c81aea..68552d57 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -191,6 +191,11 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): def route(self): + global_wl_layer = layer_props.global_bitcell_array.wordline_layer + global_wl_pitch = getattr(self, "{}_pitch".format(global_wl_layer)) + global_wl_pitch_factor = layer_props.global_bitcell_array.wordline_pitch_factor + local_wl_layer = layer_props.local_bitcell_array.wordline_layer + # Route the global wordlines for port in self.all_ports: if port == 0: @@ -200,9 +205,6 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): wordline_pins = self.wl_array.get_inputs() - wl_layer = layer_props.global_bitcell_array.wordline_layer - wl_pitch = getattr(self, "{}_pitch".format(wl_layer)) - for (wl_name, in_pin_name) in zip(wordline_names, wordline_pins): # wl_pin = self.bitcell_array_inst.get_pin(wl_name) in_pin = self.wl_insts[port].get_pin(in_pin_name) @@ -210,23 +212,21 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): y_offset = in_pin.cy() if port == 0: - y_offset -= 2 * wl_pitch + y_offset -= global_wl_pitch_factor * global_wl_pitch else: - y_offset += 2 * wl_pitch - - self.add_layout_pin_segment_center(text=wl_name, - layer=wl_layer, - start=vector(self.wl_insts[port].lx(), y_offset), - end=vector(self.wl_insts[port].lx() + self.wl_array.width, y_offset)) - + y_offset += global_wl_pitch_factor * global_wl_pitch mid = vector(in_pin.cx(), y_offset) - self.add_path("m2", [in_pin.center(), mid]) + + self.add_layout_pin_rect_center(text=wl_name, + layer=global_wl_layer, + offset=mid) + + self.add_path(local_wl_layer, [in_pin.center(), mid]) self.add_via_stack_center(from_layer=in_pin.layer, - to_layer="m2", - offset=in_pin.center()) - self.add_via_center(self.m2_stack, - offset=mid) + to_layer=local_wl_layer, + offset=mid, + min_area=True) # Route the buffers for port in self.all_ports: diff --git a/compiler/options.py b/compiler/options.py index 67ac14d7..cd54b2c6 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -153,6 +153,9 @@ class options(optparse.Values): # Route the input/output pins to the perimeter perimeter_pins = True + # Detailed or abstract LEF view + detailed_lef = False + keep_temp = False diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 6dacdd90..7621f67b 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -263,13 +263,18 @@ class sram_base(design, verilog, lef): # Add it as an IO pin to the perimeter lowest_coord = self.find_lowest_coords() - pin_width = pin.rx() - lowest_coord.x + route_width = pin.rx() - lowest_coord.x + pin_width = 2 * getattr(self, "{}_width".format(pin.layer)) pin_offset = vector(lowest_coord.x, pin.by()) self.add_layout_pin(pin_name, pin.layer, pin_offset, pin_width, pin.height()) + self.add_rect(pin.layer, + pin_offset, + route_width, + pin.height()) def route_escape_pins(self): """