def __init__(self, name, size, border_layers): if not (type(border_layers) == list): border_layers = [border_layers] Cell.__init__(self, name) self.size_x, self.size_y = size # Create the border of the cell for l in border_layers: self.border = Box((-self.size_x / 2., -self.size_y / 2.), (self.size_x / 2., self.size_y / 2.), 1, layer=l) self.add(self.border) # Add border to the frame self.align_markers = None
def make_theory_cell(wafer_orient='111'): ''' Makes the theory cell and returns ir as a cell''' # Pitch Dependence PitchDep = Cell('PitchDependence') arrayHeight = 5. arrayWidth = arrayHeight * 2. arraySpacing = 10. spacing = 0.5 length = [arrayWidth] widths = [0.050, 0.100, 0.150, 0.200, 0.250] wire_spacings = [0.100, 0.200, 0.400] for j, width in enumerate(widths): for i, wire_spacing in enumerate(wire_spacings): PitchDep.add(makeSlitArray2(wire_spacing + width, spacing, width, length, 0, arrayHeight, arrayWidth, arraySpacing, l_smBeam), origin=(i * 30, j * 20)) TopCell = Cell('GrowthTheoryTopCell') TopCell.add(PitchDep, origin=(0., 0.)) # # TODO: Add the branched growth shapes return TopCell
def add_chip_labels(self): wafer_lbl = PATTERN + "\n" + WAFER_ID text = Label(wafer_lbl, 40., layer=l_lgBeam) text.translate(tuple(np.array(-text.bounding_box.mean(0)))) # Center justify label chip_lbl_cell = Cell('chip_label') chip_lbl_cell.add(text) center_x, center_y = (5000, 5000) for block in self.blocks: block.add(chip_lbl_cell, origin=(center_x, center_y - 300)) block.add(chip_lbl_cell, origin=(center_x, center_y - 4500)) block.add(chip_lbl_cell, origin=(center_x + 4500, center_y), rotation= 90) block.add(chip_lbl_cell, origin=(center_x, center_y + 4500), rotation= 180) block.add(chip_lbl_cell, origin=(center_x - 4500, center_y), rotation= 270)
def add_subdicing_marks(self, length, width, layers): if type(layers) is not list: layers = [layers] for l in layers: mark_cell = Cell("SubdicingMark") line = Path([[0, 0], [0, length]], width=width, layer=l) mark_cell.add(line) for block in self.blocks: block.add(mark_cell, origin=(self.block_size[0] / 2., 0), rotation=0) block.add(mark_cell, origin=(0, self.block_size[1] / 2.), rotation=-90) block.add(mark_cell, origin=(self.block_size[0], self.block_size[1] / 2.), rotation=90) block.add(mark_cell, origin=(self.block_size[0] / 2., self.block_size[1]), rotation=180)
def make_many_shapes(self, array_size, shape_areas, pitch, shapes, skew, layer): offset_x = array_size * 1.25 offset_y = array_size * 1.25 cur_y = 0 many_shape_cell = Cell('ManyShapes') for area in shape_areas: cur_x = 0 for shape in shapes: write_top_labels = cur_y == 0 write_side_labels = cur_x == 0 s_array = self.make_shape_array(array_size, area, pitch, shape, layer, skew, toplabels=write_top_labels, sidelabels=write_side_labels) many_shape_cell.add(s_array, origin=(cur_x - array_size / 2., cur_y - array_size / 2.)) cur_x += offset_x cur_y -= offset_y self.add(many_shape_cell, origin=(-offset_x * (len(shapes) - 1) / 2., offset_y * (len(skews) - 1) / 2.))
def make_branch_device_array(self, spacing, _widths, array_height, array_width, array_spacing, len_inner, len_outer, n_membranes, layers): if not (type(layers) == list): layers = [layers] if not (type(_widths) == list): _widths = [_widths] for l in layers: i = -1 j = 0 manydevices = Cell("ManyDevices") for width in _widths: device = self.make_branch_device(width, spacing, len_inner, len_outer, n_membranes, l) [[x_min, y_min], [x_max, y_max]] = device.bounding_box x_size = abs(x_max - x_min) y_size = abs(y_max - y_min) i += 1 if i % 3 == 0: j += 1 # Move to array to next line i = 0 # Restart at left nx = int(array_width / (x_size + spacing)) ny = int(array_height / (y_size + spacing)) devices = CellArray(device, nx, ny, (x_size + spacing, y_size + spacing)) devices.translate((-(nx - 1) * (x_size + spacing) / 2., -(ny - 1) * (y_size + spacing) / 2.)) device_array = Cell("DeviceArray") device_array.add(devices) # Make the labels for each array of devices text = Label('w/s/l\n%i/%.1f/%i' % (width * 1000, spacing, len_outer), 5) lbl_vertical_offset = 1.40 if j % 2 == 0: text.translate( tuple(np.array(-text.bounding_box.mean(0)) + np.array(( 0, -array_height / lbl_vertical_offset)))) # Center justify label else: text.translate( tuple(np.array(-text.bounding_box.mean(0)) + np.array(( 0, array_height / lbl_vertical_offset)))) # Center justify label # TODO: Finish this below device_array.add(text) manydevices.add(device_array, origin=( (array_width + array_spacing) * i, (array_height + 2. * array_spacing) * j - array_spacing / 2.)) self.add(manydevices, origin=(-i * (array_width + array_spacing) / 2, -(j + 1.5) * (array_height + array_spacing) / 2))
def add_theory_cells(self): theory_cells = Cell('TheoryCells') theory_cells.add(make_theory_cell(wafer_orient='100'), origin=(-400, 0)) theory_cells.add(make_theory_cell_3br(), origin=(0, 0)) theory_cells.add(make_theory_cell_4br(), origin=(400, 0)) center_x, center_y = (self.block_size[0] / 2., self.block_size[1] / 2.) for block in self.blocks: block.add(theory_cells, origin=(center_x, center_y - 4000))
def make_many_shapes(array_size, shape_areas, pitch, shapes, layer): offset_x = array_size * 1.25 offset_y = array_size * 1.25 cur_y = 0 many_shape_cell = Cell('ManyShapes') for area in shape_areas: cur_x = 0 for shape in shapes: write_labels = cur_y == 0 s_array = make_shape_array(array_size, area, pitch, shape, layer, labels=write_labels) many_shape_cell.add(s_array, origin=(cur_x, cur_y)) cur_x += offset_x cur_y -= offset_y return many_shape_cell
def add_theory_cell(self): theory_cells = Cell('TheoryCells') theory_cells.add(make_theory_cell(), origin=(-400, 0)) theory_cells.add(make_theory_cell_3br(), origin=(0, 0)) theory_cells.add(make_theory_cell_4br(), origin=(400, 0)) # Add it in all the cells for (i, pt) in enumerate(self.block_pts): origin = (pt + np.array([0.5, 0.5])) * self.block_size origin += np.array([0, -2000]) self.add(theory_cells, origin=origin)
def add_theory_cell(self): theory_cell = make_theory_cell() theory_cells = Cell('TheoryCells') for x, y in self.upCenters: theory_cells.add(theory_cell, origin=(x, y - 1500)) for x, y in self.downCenters: if self.symmetric_chips: theory_cells.add(theory_cell, origin=(x, y + 1500), rotation=180) theory_cells.add(theory_cell, origin=(x, y + 1500)) self.add(theory_cells)
def make_tapered_cross(self, nw1, nw2, tw1, tw2, l1, l2, tl1, tl2, layer): cross_cell = Cell( 'TaperedCross_NW{:.0f}_TW{:.0f}_L{:.0f}_TL{:.0f}xNW{:.0f}_TW{:.0f}_L{:.0f}_TL{:.0f}' .format(nw1 * 1000, tw1 * 1000, l1, tl1, nw2 * 1000, tw2 * 1000, l2, tl2)) l1 += tw2 l2 += tw1 tl1 += tw2 tl2 += tl1 cross_pts = [(tw2 / 2., tw1 / 2.), (tl1, nw1 / 2.), (l1 / 2., nw1 / 2.), (l1 / 2., -nw1 / 2.), (tl1, -nw1 / 2.), (tw2 / 2., -tw1 / 2.), (nw2 / 2., -tl2 / 2.), (nw2 / 2., -l2 / 2.), (-nw2 / 2., -l2 / 2.), (-nw2 / 2., -tl2 / 2.), (-tw2 / 2., -tw1 / 2.)] cross_pts.extend(tuple(map(tuple, (-np.array(cross_pts)).tolist()))) membrane = Boundary(cross_pts, layer=layer) # Create gdsCAD shape cross_cell.add(membrane) return cross_cell
def add_tem_nanowires(self): size = 500 y_offset = 1000 shapes_big = make_shape_array(size, 0.02, 0.5, 'Tris_right', l_smBeam, labels=False) shapes_small = make_shape_array(size, 0.005, 0.5, 'Tris_right', l_smBeam, labels=False) tem_shapes = Cell('TEMShapes') # tem_shapes.add(shapes_big, origin=(2200 - size / 2., y_offset - size / 2.)) tem_shapes.add(shapes_small, origin=(-size / 2., -size / 2.)) self.block_up.add(tem_shapes, origin=(-2200, y_offset)) self.block_down.add(tem_shapes, origin=(2200, -y_offset))
def makeAlignMarkers(self, t, w, position, layers, cross=False): if not (type(layers) == list): layers = [layers] self.aMarkers = Cell("AlignMarkers") for l in layers: if not (cross): am1 = Rectangle((-w / 2., -w / 2.), (w / 2., w / 2.), layer=l) elif cross: h = w crosspts = [(-t / 2., t / 2.), (-t / 2., h / 2.), (t / 2., h / 2.), (t / 2., t / 2.), (w / 2., t / 2.), (w / 2., -t / 2.), (t / 2., -t / 2.), (t / 2., -h / 2.), (-t / 2., -h / 2.), (-t / 2., -t / 2.), (-w / 2., -t / 2.), (-w / 2., t / 2.)] am1 = Boundary(crosspts, layer=l) # Create gdsCAD shape # am1 = Polygon(crosspts) #Create shapely polygon for later calculation am1 = am1.translate(tuple(position)) # 850,850 am2 = am1.copy().scale((-1, 1)) # Reflect in x-axis am3 = am1.copy().scale((1, -1)) # Reflect in y-axis am4 = am1.copy().scale((-1, -1)) # Reflect in both x and y-axis self.aMarkers.add([am1, am2, am3, am4]) self.add(self.aMarkers)
def make_many_shapes(self, array_size, shape_areas, pitches, shape, skew, layer): """ :param array_size: :param shape_areas: :param pitches: :param shape: :param skew: :param layer: :return: """ if (type(shape) == list): shape = shape[0] offset_x = array_size * 1.25 offset_y = array_size * 1.25 cur_y = 0 many_shape_cell = Cell('ManyShapes') for area in shape_areas: cur_x = 0 for pitch in pitches: write_top_labels = cur_y == 0 write_side_labels = cur_x == 0 s_array = self.make_shape_array(array_size, area, pitch, shape, layer, skew, toplabels=write_top_labels, sidelabels=write_side_labels) many_shape_cell.add(s_array, origin=(cur_x - array_size / 2., cur_y - array_size / 2.)) cur_x += offset_x cur_y -= offset_y self.add(many_shape_cell, origin=(-offset_x * (len(pitches) - 1) / 2., offset_y * (len(shape_areas) - 1) / 2.))
def add_theory_cell(self): theory_cells = Cell('TheoryCells') theory_cells.add(make_theory_cell(), origin=(-200, 0)) theory_cells.add(make_theory_cell_br(), origin=(200, 0)) self.block_up.add(theory_cells, origin=(0, 1300)) self.block_down.add(theory_cells, origin=(0, -1300))
def make_align_markers(self, t, w, position, layers, cross=False, auto_marks=False): if not (type(layers) == list): layers = [layers] self.align_markers = Cell("AlignMarkers") self.align_marker = Cell("AlignMarker") for l in layers: if not cross: am0 = Rectangle((-w / 2., -w / 2.), (w / 2., w / 2.), layer=l) self.align_marker.add(am0) elif cross: crosspts = [(0, 0), (w / 2., 0), (w / 2., t), (t, t), (t, w / 2), (0, w / 2), (0, 0)] crosspts.extend( tuple(map(tuple, (-np.array(crosspts)).tolist()))) # crosspts = [(-t / 2., t / 2.), (-t / 2., h / 2.), (t / 2., h / 2.), # (t / 2., t / 2.), (w / 2., t / 2.), (w / 2., -t / 2.), # (t / 2., -t / 2.), (t / 2., -h / 2.), # (-t / 2., -h / 2.), (-t / 2., -t / 2.), # (-w / 2., -t / 2.), (-w / 2., t / 2.)] am0 = Boundary(crosspts, layer=l) # Create gdsCAD shape self.align_marker.add(am0) # am1 = Polygon(crosspts) #Create shapely polygon for later calculation if auto_marks: # automatic alignment marks for the e-beam tool auto_mark_rect = Rectangle((-10., -10.), (10., 10.), layer=l) auto_mark = Cell("AutoMark") auto_mark.add(auto_mark_rect) self.align_marker.add(auto_mark, origin=(100, 100)) self.align_marker.add(auto_mark, origin=(-100, 100)) self.align_marker.add(auto_mark, origin=(100, -100)) self.align_marker.add(auto_mark, origin=(-100, -100)) self.align_markers.add(self.align_marker, origin=tuple(np.array(position) * [1, 1])) self.align_markers.add(self.align_marker, origin=tuple(np.array(position) * [-1, 1])) self.align_markers.add(self.align_marker, origin=tuple(np.array(position) * [1, -1])) self.align_markers.add(self.align_marker, origin=tuple(np.array(position) * [-1, -1])) self.add(self.align_markers)
def add_block_labels(self, layers, quasi_unique_labels=False): if type(layers) is not list: layers = [layers] txtSize = 800 if quasi_unique_labels: unique_label_string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' possible_labels = [ "".join(x) for x in itertools.product(unique_label_string, repeat=2) ] blockids_set = set() while len(blockids_set) < len(self.blocks): blockids_set.add(random_choice(possible_labels)) blockids = list(blockids_set) for i, block in enumerate(self.blocks): blocklabel = Cell('LBL_B_' + blockids[i]) for l in layers: txt = Label(blockids[i], txtSize, layer=l) bbox = txt.bounding_box offset = (0, 0) txt.translate( -np.mean(bbox, 0)) # Center text around origin txt.translate(offset) # Translate it to bottom of wafer blocklabel.add(txt) block.add(blocklabel, origin=(self.block_size[0] / 2., self.block_size[1] / 2.)) else: for (i, pt) in enumerate(self.block_pts): origin = (pt + np.array([0.5, 0.5])) * self.block_size blk_lbl = self.blockcols[pt[0]] + self.blockrows[pt[1]] for l in layers: txt = Label(blk_lbl, txtSize, layer=l_lgBeam) bbox = txt.bounding_box offset = np.array(pt) txt.translate( -np.mean(bbox, 0)) # Center text around origin lbl_cell = Cell("lbl_" + blk_lbl) lbl_cell.add(txt) origin += np.array([0, 2000]) # Translate it up by 2mm self.add(lbl_cell, origin=origin)
def processCheck_Slits(self, position, arrayWidth, slitWidth, pitch, length, rotation, layers): if not (type(layers) == list): layers = [layers] Nx = int(arrayWidth / pitch) Ny = 1 for l in layers: # Define the slits slit = Cell("Slits") rect = Rectangle((-slitWidth / 2., -length / 2.), (slitWidth / 2., length / 2.), layer=l) slit.add(rect) slits = CellArray(slit, Nx, Ny, (pitch, 0)) slits.translate((-(Nx) * (pitch) / 2., 0.)) slits.translate(position) slitarray = Cell("ProcessCheckingSlits") slitarray.add(slits) self.add(slitarray)
def add_theory_cell(self): theory_cells = Cell('TheoryCells') theory_cells.add(make_theory_cell(), origin=(-200, 0)) theory_cells.add(make_theory_cell_br(), origin=(200, 0)) for x, y in self.upCenters: self.add(theory_cells, origin=(x, y + 1300)) for x, y in self.downCenters: self.add(theory_cells, origin=( x, y - 1300 )) # Don't rotate because of directionality of branched membranes
def add_theory_cells(self): theory_cells = Cell('TheoryCells') theory_cells.add(make_theory_cell(wafer_orient='100'), origin=(70, 0)) # theory_cells.add(make_theory_cell_3br(), origin=(0, 0)) # theory_cells.add(make_theory_cell_4br(), origin=(400, 0)) theory_cells.add(make_theory_cell(wafer_orient='100'), origin=(20, -400), rotation=45) # theory_cells.add(make_theory_cell_3br(), origin=(-50, -400), rotation=45) # theory_cells.add(make_theory_cell_4br(), origin=(370, -400), rotation=45) center_x, center_y = (5000, 5000) for block in self.blocks: block.add(theory_cells, origin=(center_x, center_y - 1700))
def add_dashed_dicing_marks(self, layers): if type(layers) is not list: layers = [layers] width = 10. / 2 dashlength = 2000 r = self.wafer_r rng = np.floor(self.wafer_r / self.block_size).astype(int) dmarks = Cell('DIC_MRKS') for l in layers: for x in np.arange(-rng[0], rng[0] + 1) * self.block_size[0]: y = np.sqrt(r**2 - x**2) vm = dashed_line([x, y], [x, -y], dashlength, width, layer=l) dmarks.add(vm) for y in np.arange(-rng[1], rng[1] + 1) * self.block_size[1]: x = np.sqrt(r**2 - y**2) hm = dashed_line([x, y], [-x, y], dashlength, width, layer=l) dmarks.add(hm) self.add(dmarks)
def add_cleave_xsection_nws(self): pitches = [0.5, 1., 2., 4.] widths = [10., 20., 40., 60., 100., 160., 240.] n_membranes = 10 length = 50 spacing = 10 cleave_xsection_cell = Cell("CleaveCrossSection") y_offset = 0 for pitch in pitches: for width in widths: nm_cell = Cell("P{:.0f}W{:.0f}".format(pitch, width)) slit = Path([(-length / 2., 0), (length / 2., 0)], width=width / 1000., layer=l_smBeam) nm_cell.add(slit) nm_cell_array = Cell("P{:.0f}W{:.0f}_Array".format( pitch, width)) tmp = CellArray(nm_cell, 1.0, n_membranes, [0, pitch]) nm_cell_array.add(tmp) cleave_xsection_cell.add(nm_cell_array, origin=(0, y_offset + pitch * n_membranes)) y_offset += pitch * n_membranes + spacing text = Label("P{:.1f}W{:.0f}".format(pitch, width), 1.0, layer=l_smBeam) text.translate(tuple(np.array( -text.bounding_box.mean(0)))) # Center justify label txt_cell = Cell("lbl_P{:.1f}W{:.0f}".format(pitch, width)) txt_cell.add(text) cleave_xsection_cell.add(txt_cell, origin=(length * 0.75, y_offset - 8.0)) cleave_xsection_cell.add(txt_cell, origin=(-length * 0.75, y_offset - 8.0)) y_offset += spacing * 3 center_x, center_y = (5000, 5000) for block in self.blocks: block.add(cleave_xsection_cell, origin=(center_x + 1150, center_y - 463)) block.add(cleave_xsection_cell, origin=(center_x - 350, center_y + 350), rotation=45.) block.add(cleave_xsection_cell, origin=(center_x + 463, center_y - 1150), rotation=90.)
def make_slit_array(self, _pitches, spacing, _widths, _lengths, rot_angle, array_height, array_width, array_spacing, layers): if not (type(layers) == list): layers = [layers] if not (type(_pitches) == list): _pitches = [_pitches] if not (type(_lengths) == list): _lengths = [_lengths] if not (type(_widths) == list): _widths = [_widths] manyslits = i = j = None for l in layers: i = -1 j = -1 manyslits = Cell("SlitArray") pitch = _pitches[0] for length in _lengths: j += 1 i = -1 for width in _widths: # for pitch in pitches: i += 1 if i % 3 == 0: j += 1 # Move to array to next line i = 0 # Restart at left nx = int(array_width / (length + spacing)) ny = int(array_height / pitch) # Define the slits slit = Cell("Slits") rect = Rectangle((-length / 2., -width / 2.), (length / 2., width / 2.), layer=l) slit.add(rect) slits = CellArray(slit, nx, ny, (length + spacing, pitch)) slits.translate((-(nx - 1) * (length + spacing) / 2., -(ny - 1) * pitch / 2.)) slit_array = Cell("SlitArray") slit_array.add(slits) text = Label('w/p/l\n%i/%i/%i' % (width * 1000, pitch, length), 5, layer=l) lbl_vertical_offset = 1.35 if j % 2 == 0: text.translate( tuple( np.array(-text.bounding_box.mean(0)) + np.array((0, -array_height / lbl_vertical_offset)) )) # Center justify label else: text.translate( tuple( np.array(-text.bounding_box.mean(0)) + np.array((0, array_height / lbl_vertical_offset )))) # Center justify label slit_array.add(text) manyslits.add( slit_array, origin=((array_width + array_spacing) * i, (array_height + 2. * array_spacing) * j - array_spacing / 2.)) # This is an ugly hack to center rotated slits, should fix this properly... hacky_offset_x = 200 if rot_angle == 45 else 0 # TODO: fix this ugly thing hacky_offset_y = -25 if rot_angle == 45 else 0 self.add(manyslits, origin=(-i * (array_width + array_spacing) / 2 + hacky_offset_x, -(j + 1.5) * (array_height + array_spacing) / 2 + hacky_offset_y), rotation=rot_angle)
def make_align_markers(self, t, w, position, layers, joy_markers=False, camps_markers=False): if not (type(layers) == list): layers = [layers] top_mk_cell = Cell('AlignmentMark') for l in layers: if not joy_markers: am0 = Rectangle((-w / 2., -w / 2.), (w / 2., w / 2.), layer=l) rect_mk_cell = Cell("RectMarker") rect_mk_cell.add(am0) top_mk_cell.add(rect_mk_cell) elif joy_markers: crosspts = [(0, 0), (w / 2., 0), (w / 2., t), (t, t), (t, w / 2), (0, w / 2), (0, 0)] crosspts.extend( tuple(map(tuple, (-np.array(crosspts)).tolist()))) am0 = Boundary(crosspts, layer=l) # Create gdsCAD shape joy_mk_cell = Cell("JOYMarker") joy_mk_cell.add(am0) top_mk_cell.add(joy_mk_cell) if camps_markers: emw = 20. # 20 um e-beam marker width camps_mk = Rectangle((-emw / 2., -emw / 2.), (emw / 2., emw / 2.), layer=l) camps_mk_cell = Cell("CAMPSMarker") camps_mk_cell.add(camps_mk) top_mk_cell.add(camps_mk_cell, origin=[100., 100.]) top_mk_cell.add(camps_mk_cell, origin=[100., -100.]) top_mk_cell.add(camps_mk_cell, origin=[-100., 100.]) top_mk_cell.add(camps_mk_cell, origin=[-100., -100.]) self.align_markers = Cell("AlignMarkers") self.align_markers.add(top_mk_cell, origin=np.array(position) * np.array([1, -1])) self.align_markers.add(top_mk_cell, origin=np.array(position) * np.array([-1, -1])) self.align_markers.add(top_mk_cell, origin=np.array(position) * np.array([1, 1])) self.align_markers.add(top_mk_cell, origin=np.array(position) * np.array([-1, 1])) self.add(self.align_markers)
class Frame(Cell): """ Make a frame for writing to with ebeam lithography Params: -name of the frame, just like when naming a cell -size: the size of the frame as an array [xsize,ysize] """ def __init__(self, name, size, border_layers): if not (type(border_layers) == list): border_layers = [border_layers] Cell.__init__(self, name) self.size_x, self.size_y = size # Create the border of the cell for l in border_layers: self.border = Box((-self.size_x / 2., -self.size_y / 2.), (self.size_x / 2., self.size_y / 2.), 1, layer=l) self.add(self.border) # Add border to the frame self.align_markers = None def make_align_markers(self, t, w, position, layers, joy_markers=False, camps_markers=False): if not (type(layers) == list): layers = [layers] top_mk_cell = Cell('AlignmentMark') for l in layers: if not joy_markers: am0 = Rectangle((-w / 2., -w / 2.), (w / 2., w / 2.), layer=l) rect_mk_cell = Cell("RectMarker") rect_mk_cell.add(am0) top_mk_cell.add(rect_mk_cell) elif joy_markers: crosspts = [(0, 0), (w / 2., 0), (w / 2., t), (t, t), (t, w / 2), (0, w / 2), (0, 0)] crosspts.extend( tuple(map(tuple, (-np.array(crosspts)).tolist()))) am0 = Boundary(crosspts, layer=l) # Create gdsCAD shape joy_mk_cell = Cell("JOYMarker") joy_mk_cell.add(am0) top_mk_cell.add(joy_mk_cell) if camps_markers: emw = 20. # 20 um e-beam marker width camps_mk = Rectangle((-emw / 2., -emw / 2.), (emw / 2., emw / 2.), layer=l) camps_mk_cell = Cell("CAMPSMarker") camps_mk_cell.add(camps_mk) top_mk_cell.add(camps_mk_cell, origin=[100., 100.]) top_mk_cell.add(camps_mk_cell, origin=[100., -100.]) top_mk_cell.add(camps_mk_cell, origin=[-100., 100.]) top_mk_cell.add(camps_mk_cell, origin=[-100., -100.]) self.align_markers = Cell("AlignMarkers") self.align_markers.add(top_mk_cell, origin=np.array(position) * np.array([1, -1])) self.align_markers.add(top_mk_cell, origin=np.array(position) * np.array([-1, -1])) self.align_markers.add(top_mk_cell, origin=np.array(position) * np.array([1, 1])) self.align_markers.add(top_mk_cell, origin=np.array(position) * np.array([-1, 1])) self.add(self.align_markers) def make_slit_array(self, _pitches, spacing, _widths, _lengths, rot_angle, array_height, array_width, array_spacing, layers): if not (type(layers) == list): layers = [layers] if not (type(_pitches) == list): _pitches = [_pitches] if not (type(_lengths) == list): _lengths = [_lengths] if not (type(_widths) == list): _widths = [_widths] manyslits = i = j = None for l in layers: i = -1 j = -1 manyslits = Cell("SlitArray") pitch = _pitches[0] for length in _lengths: j += 1 i = -1 for width in _widths: # for pitch in pitches: i += 1 if i % 3 == 0: j += 1 # Move to array to next line i = 0 # Restart at left nx = int(array_width / (length + spacing)) ny = int(array_height / pitch) # Define the slits slit = Cell("Slits") rect = Rectangle((-length / 2., -width / 2.), (length / 2., width / 2.), layer=l) slit.add(rect) slits = CellArray(slit, nx, ny, (length + spacing, pitch)) slits.translate((-(nx - 1) * (length + spacing) / 2., -(ny - 1) * pitch / 2.)) slit_array = Cell("SlitArray") slit_array.add(slits) text = Label('w/p/l\n%i/%i/%i' % (width * 1000, pitch, length), 5, layer=l) lbl_vertical_offset = 1.35 if j % 2 == 0: text.translate( tuple( np.array(-text.bounding_box.mean(0)) + np.array((0, -array_height / lbl_vertical_offset)) )) # Center justify label else: text.translate( tuple( np.array(-text.bounding_box.mean(0)) + np.array((0, array_height / lbl_vertical_offset )))) # Center justify label slit_array.add(text) manyslits.add( slit_array, origin=((array_width + array_spacing) * i, (array_height + 2. * array_spacing) * j - array_spacing / 2.)) # This is an ugly hack to center rotated slits, should fix this properly... hacky_offset_x = 200 if rot_angle == 45 else 0 # TODO: fix this ugly thing hacky_offset_y = -25 if rot_angle == 45 else 0 self.add(manyslits, origin=(-i * (array_width + array_spacing) / 2 + hacky_offset_x, -(j + 1.5) * (array_height + array_spacing) / 2 + hacky_offset_y), rotation=rot_angle)
def add_tem_membranes(self, widths, length, pitch, layer): tem_membranes = Cell('TEM_Membranes') n = 4 curr_y = 0 for width in widths: membrane = Path([(-length / 2., 0), (length / 2., 0)], width=width, layer=layer) membrane_cell = Cell('Membrane_w{:.0f}'.format(width * 1000)) membrane_cell.add(membrane) membrane_array = CellArray(membrane_cell, 1, n, (0, pitch)) membrane_array_cell = Cell('MembraneArray_w{:.0f}'.format(width * 1000)) membrane_array_cell.add(membrane_array) tem_membranes.add(membrane_array_cell, origin=(0, curr_y)) curr_y += n * pitch n2 = 3 tem_membranes2 = Cell('Many_TEM_Membranes') tem_membranes2.add( CellArray(tem_membranes, 1, n2, (0, n * len(widths) * pitch))) center_x, center_y = (5000, 5000) for block in self.blocks: block.add(tem_membranes2, origin=(center_x, center_y + 2000)) block.add(tem_membranes2, origin=(center_x, center_y + 1500), rotation=45)
def add_prealignment_markers(self, layers, mrkr_size=7): if mrkr_size % 2 == 0: # Number is even, but we need odd numbers mrkr_size += 1 if type(layers) is not list: layers = [layers] for l in layers: rect_size = 10. # 10 um large PAMM rectangles marker_rect = Rectangle([-rect_size / 2., -rect_size / 2.], [rect_size / 2., rect_size / 2.], layer=l) marker = Cell('10umMarker') marker.add(marker_rect) # Make one arm of the PAMM array marker_arm = Cell('PAMM_Arm') # Define the positions of the markers, they increase in spacing by 1 um each time: mrkr_positions = [ 75 * n + (n - 1) * n // 2 for n in range(1, (mrkr_size - 1) // 2 + 1) ] for pos in mrkr_positions: marker_arm.add(marker, origin=[pos, 0]) # Build the final PAMM Marker pamm_cell = Cell('PAMM_Marker') pamm_cell.add(marker) # Center marker pamm_cell.add(marker_arm) # Right arm pamm_cell.add(marker_arm, rotation=180) # Left arm pamm_cell.add(marker_arm, rotation=90) # Top arm pamm_cell.add(marker_arm, rotation=-90) # Bottom arm for pos in mrkr_positions: pamm_cell.add(marker_arm, origin=[pos, 0], rotation=90) # Top arms pamm_cell.add(marker_arm, origin=[-pos, 0], rotation=90) pamm_cell.add(marker_arm, origin=[pos, 0], rotation=-90) # Bottom arms pamm_cell.add(marker_arm, origin=[-pos, 0], rotation=-90) # Make the 4 tick marks that mark the center of the array h = 30. w = 100. tick_mrk = Rectangle([-w / 2., -h / 2.], [w / 2, h / 2.], layer=l) tick_mrk_cell = Cell("TickMark") tick_mrk_cell.add(tick_mrk) pos = mrkr_positions[-1] + 75 + w / 2. pamm_cell.add(tick_mrk_cell, origin=[pos, 0]) pamm_cell.add(tick_mrk_cell, origin=[-pos, 0]) pamm_cell.add(tick_mrk_cell, origin=[0, pos], rotation=90) pamm_cell.add(tick_mrk_cell, origin=[0, -pos], rotation=90) center_x, center_y = (5000, 5000) for block in self.blocks: block.add(pamm_cell, origin=(center_x + 2000, center_y)) block.add(pamm_cell, origin=(center_x - 2000, center_y))
centerLeftAlignField.make_align_markers(2., 20., (180., 180.), l_lgBeam, joy_markers=True) centerLeftAlignField.add(quantum_playground) centerRightAlignField = Frame("CenterRightAlignField", (smFrameSize, smFrameSize), []) centerRightAlignField.make_align_markers(2., 20., (180., 180.), l_lgBeam, joy_markers=True) centerRightAlignField.add(quantum_playground, rotation=45) # Add everything together to a top cell topCell = Cell("TopCell") topCell.add(lgField) smFrameSpacing = 400 # Spacing between the three small frames dx = smFrameSpacing + smFrameSize dy = smFrameSpacing + smFrameSize topCell.add(smField1, origin=(-dx / 2., dy / 2.)) topCell.add(smField2, origin=(dx / 2., dy / 2.)) topCell.add(smField3, origin=(-dx / 2., -dy / 2.)) topCell.add(smField4, origin=(dx / 2., -dy / 2.)) topCell.add(centerLeftAlignField, origin=(-dx / 2, 0.)) topCell.add(centerRightAlignField, origin=(dx / 2, 0.)) topCell.add(centerAlignField, origin=(0., 0.)) topCell.spacing = np.array([4000., 4000.]) # %%Create the layout and output GDS file layout = Layout('LIBRARY')
def add_block_labels(self, layers, unique_ids=False, save_ids=True, load_ids=True): if type(layers) is not list: layers = [layers] txtSize = 1000 blockids = [] if not unique_ids: for (i, pt) in enumerate(self.block_pts): blockids.append(self.blockcols[pt[0]] + self.blockrows[pt[1]]) else: existing_ids = {} existing_id_set = set() # Load the previously-used IDs from a JSON file if load_ids: master_db = '../../../ChipIDs_DB.json' if os.path.isfile(master_db): with open(master_db, 'r') as f: try: existing_ids = json.load(f) existing_id_set = set([ item for sublist in list(existing_ids.values()) for item in sublist ]) # Check if wafer is in the loaded database if load_ids and WAFER_ID in existing_ids: blockids = existing_ids[WAFER_ID] # If there is a reading error then proceed with a warning except json.decoder.JSONDecodeError: print( "Json Error: Couldn't load chip IDs from database!" ) existing_id_set = set() # If the IDs haven't already been set by loading them from the database if not blockids: # Generate some new IDs, but only use the ones that haven't previously been used unique_label_string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' possible_label_set = set([ "".join(x) for x in itertools.product(unique_label_string, repeat=2) ]) possible_label_set = possible_label_set - existing_id_set # Remove chip lbls from the set of possible lbls blockids_set = set() while len(blockids_set) < len(self.blocks): blockids_set.add(random_choice(list(possible_label_set))) blockids = list(blockids_set) # Save the labels to a file if save_ids: existing_ids.update({WAFER_ID: blockids}) json_string = json.dumps(existing_ids) json_string = json_string.replace( "], ", "],\n") # Make the file a bit easier to read in notepad with open(master_db, 'w') as f: f.write(json_string) # Write the labels to the cells for i, block in enumerate(self.blocks): blocklabel = Cell('LBL_B_' + blockids[i]) for l in layers: txt = Label(blockids[i], txtSize, layer=l) bbox = txt.bounding_box offset = (0, 0) txt.translate(-np.mean(bbox, 0)) # Center text around origin txt.translate(offset) # Translate it to bottom of wafer blocklabel.add(txt) block.add(blocklabel, origin=(self.block_size[0] / 2., self.block_size[1] / 2. - 400))
def make_slit_array(self, _pitches, spacing, _widths, _lengths, rot_angle, array_height, array_width, array_spacing, layers): if not (type(layers) == list): layers = [layers] if not (type(_pitches) == list): _pitches = [_pitches] if not (type(_lengths) == list): _lengths = [_lengths] if not (type(_widths) == list): _widths = [_widths] manyslits = i = j = None for l in layers: i = -1 j = -1 manyslits = Cell("SlitArray") pitch = _pitches[0] for length in _lengths: j += 1 i = -1 for width in _widths: # for pitch in pitches: i += 1 if i % 3 == 0: j += 1 # Move to array to next line i = 0 # Restart at left pitch_v = pitch / np.cos(np.deg2rad(rot_angle)) # widthV = width / np.cos(np.deg2rad(rotAngle)) nx = int(array_width / (length + spacing)) ny = int(array_height / pitch_v) # Define the slits slit = Cell("Slits") rect = Rectangle((-length / 2., -width / 2.), (length / 2., width / 2.), layer=l) rect = rect.copy().rotate(rot_angle) slit.add(rect) slits = CellArray(slit, nx, ny, (length + spacing, pitch_v)) slits.translate((-(nx - 1) * (length + spacing) / 2., -(ny - 1) * pitch_v / 2.)) slit_array = Cell("SlitArray") slit_array.add(slits) text = Label('w/p/l\n%i/%i/%i' % (width * 1000, pitch, length), 5, layer=l) lbl_vertical_offset = 1.35 if j % 2 == 0: text.translate( tuple(np.array(-text.bounding_box.mean(0)) + np.array(( 0, -array_height / lbl_vertical_offset)))) # Center justify label else: text.translate( tuple(np.array(-text.bounding_box.mean(0)) + np.array(( 0, array_height / lbl_vertical_offset)))) # Center justify label slit_array.add(text) manyslits.add(slit_array, origin=((array_width + array_spacing) * i, ( array_height + 2. * array_spacing) * j - array_spacing / 2.)) self.add(manyslits, origin=(-i * (array_width + array_spacing) / 2, -(j + 1.5) * ( array_height + array_spacing) / 2))