Created
April 17, 2023 07:57
-
-
Save s910324/693ba2d1a25b0d2309b3fe6f01edddbf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pya | |
import math | |
class Toolkit(object): | |
def __init__(self): | |
super(Toolkit, self).__init__() | |
def sin(deg): | |
return math.sin(Toolkit.deg_2_arc(deg)) | |
def cos(deg): | |
return math.cos(Toolkit.deg_2_arc(deg)) | |
def tan(deg): | |
return math.tan(Toolkit.deg_2_arc(deg)) | |
def deg_2_arc(deg): | |
return (deg / 360) * 2 * math.pi | |
def arc_2_deg(arc): | |
return (arc / 2 / math.pi) * 360 | |
def circle(x, y, r, p = 64, deg1 = 0, unit = 1, checker = None): | |
pts = [] | |
if isinstance(checker, (float, int)): | |
if ((x ** 2 + y ** 2) ** (0.5) > (checker - r)): | |
return pts | |
if isinstance(checker, (list, tuple)): | |
if ((x ** 2 + y ** 2)**(0.5) > (max(checker) - r)) or ((x ** 2 + y ** 2) ** (0.5) < (min(checker) - r)): | |
return pts | |
for i in range(p): | |
ccx = x + r * Toolkit.cos(deg1 + (360 / p) * i) | |
ccy = y + r * Toolkit.sin(deg1 + (360 / p) * i) | |
pts.append(pya.DPoint(ccx / unit, ccy / unit)) | |
return pts + [pts[0]] | |
def rect(x, y, w, h, unit = 1): | |
return [ | |
pya.DPoint((x + (w/2)) / unit, (y + (h/2)) / unit), | |
pya.DPoint((x - (w/2)) / unit, (y + (h/2)) / unit), | |
pya.DPoint((x - (w/2)) / unit, (y - (h/2)) / unit), | |
pya.DPoint((x + (w/2)) / unit, (y - (h/2)) / unit), | |
pya.DPoint((x + (w/2)) / unit, (y + (h/2)) / unit), | |
] | |
class Wafer(pya.PCellDeclarationHelper): | |
def __init__(self): | |
super(Wafer, self).__init__() | |
self.param("name", self.TypeString, "Name", default = "") | |
self.param("lg", self.TypeLayer, "Layer for guideline", default = pya.LayerInfo(1, 0)) | |
self.param("lw", self.TypeLayer, "Layer for wafer", default = pya.LayerInfo(1, 0)) | |
self.param("le", self.TypeLayer, "Layer for edge exclusion", default = pya.LayerInfo(1, 0)) | |
wafer_size_option = self.param( "wafer_size_option", self.TypeString, "Wafer Size", default=2) | |
wafer_size_option.add_choice(" 2 inch", 0) | |
wafer_size_option.add_choice(" 4 inch", 1) | |
wafer_size_option.add_choice(" 6 inch", 2) | |
wafer_size_option.add_choice(" 8 inch", 3) | |
wafer_size_option.add_choice("12 inch", 4) | |
self.param("edge_exclude", self.TypeDouble, "Edge exclusion", default = 3.0, unit = "mm") | |
self.param("circle_dots", self.TypeInt, "Points per circle", default = 128) | |
self.param("place_chip", self.TypeBoolean, "Place Chip", default = False) | |
self.param("lc", self.TypeLayer, "Layer for good chips", default = pya.LayerInfo(1, 0)) | |
self.param("lcp", self.TypeLayer, "Layer for partial chips", default = pya.LayerInfo(1, 0)) | |
self.param("chip_w", self.TypeDouble, "Chip width", default = 5000, unit = "um") | |
self.param("chip_h", self.TypeDouble, "Chip height", default = 5000, unit = "um") | |
self.param("scribe_w", self.TypeDouble, "Scribeline width", default = 250, unit = "um") | |
self.param("scribe_h", self.TypeDouble, "Scribeline height", default = 250, unit = "um") | |
self.param("chip_offset_x", self.TypeDouble, "Chip offset X", default = 0, unit = "um") | |
self.param("chip_offset_y", self.TypeDouble, "Chip offset Y", default = 0, unit = "um") | |
def display_text_impl(self): | |
class_name = self.__class__.__name__ | |
custom_name = (self.name + "--" if self.name else "") | |
wafer_size = {0:" 2 inch", 1:" 4 inch", 2:" 6 inch", 3:" 8 inch", 4:"12 inch"}[self.wafer_size_option] | |
return "%s%s(%s, EBR: %.2f mm)" % (custom_name, class_name, wafer_size, self.edge_exclude) | |
def coerce_parameters_impl(self): | |
unit = self.layout.dbu | |
mm = 1000 | |
self.wafer_diameter = 0 | |
self.primary_flat = 0 | |
self.primary_notch = 0 | |
if self.wafer_size_option == 0: #2 unch | |
self.wafer_diameter = 50.80 * mm | |
self.primary_flat = 15.88 * mm | |
if self.wafer_size_option == 1: #4 unch | |
self.wafer_diameter = 100.0 * mm | |
self.primary_flat = 32.50 * mm | |
if self.wafer_size_option == 2: #6 unch | |
self.wafer_diameter = 150.0 * mm | |
self.primary_flat = 57.50 * mm | |
if self.wafer_size_option == 3: #8 unch | |
self.wafer_diameter = 200.0 * mm | |
self.primary_notch = [3.0 * mm, 1.0 * mm, 0.5 * mm] # w&h, rounding, y-offset | |
if self.wafer_size_option == 4: #12 unch | |
self.wafer_diameter = 300.0 * mm | |
self.primary_notch = [3.0 * mm, 1.0 * mm, 0.5 * mm] # w&h, rounding, y-offset | |
self.circle_dots = 32 if self.circle_dots <= 32 else self.circle_dots | |
self.edge_exclude_mm = 0 if self.edge_exclude <= 0 else self.edge_exclude * mm / unit | |
def can_create_from_shape_impl(self): | |
return self.shape.is_box() or self.shape.is_polygon() or self.shape.is_path() | |
def parameters_from_shape_impl(self): | |
self.l = self.layout.get_info(self.layer) | |
def transformation_from_shape_impl(self): | |
return pya.Trans(self.shape.bbox().center()) | |
def produce_impl(self): | |
unit = self.layout.dbu | |
self.wafer_radius = self.wafer_diameter/2 | |
wafer_circle_dots = Toolkit.circle(0, 0, self.wafer_radius, p = self.circle_dots, unit = unit) | |
circle_poly_reg = pya.Region(pya.Polygon(wafer_circle_dots)) | |
circle_path_reg = pya.Region(pya.Path(wafer_circle_dots, 1)) | |
flat_notch_reg = pya.Region() | |
if self.primary_flat != 0: | |
h = 2*(self.wafer_radius - (self.wafer_radius ** 2 - (self.primary_flat/2) ** 2 ) ** (0.5)) | |
flat_notch_reg = pya.Region(pya.Polygon(Toolkit.rect(0, -self.wafer_radius, self.wafer_diameter, h, unit = unit))) | |
if self.primary_notch != 0: | |
width, rounding, offset = self.primary_notch | |
pts = Toolkit.rect(0, 0, width, width, self.layout.dbu) | |
rect_poly = pya.Polygon(pts).round_corners(rounding, rounding/unit, self.circle_dots) | |
rect_poly.transform(pya.DCplxTrans (1.0, 45, False, 0, (-self.wafer_radius-offset)/unit)) | |
flat_notch_reg = pya.Region(rect_poly) | |
wafer_reg = circle_poly_reg - flat_notch_reg | |
ebr_reg = wafer_reg.sized(-self.edge_exclude_mm) | |
self.cell.shapes(self.lg_layer).insert(circle_path_reg) | |
self.cell.shapes(self.lw_layer).insert(wafer_reg) | |
self.cell.shapes(self.le_layer).insert(ebr_reg) | |
if self.place_chip and (self.chip_w * self.chip_h > 0): | |
pitch_x = self.chip_w + self.scribe_w | |
pitch_y = self.chip_h + self.scribe_h | |
offset_x = self.chip_offset_x % pitch_x | |
offset_y = self.chip_offset_y % pitch_y | |
chip_column = (int(self.wafer_radius/pitch_x) + 2) * 2 | |
chip_row = (int(self.wafer_radius/pitch_y) + 2) * 2 | |
origin_x = -1 * (chip_column ) / 2 * pitch_x | |
origin_y = -1 * (chip_row ) / 2 * pitch_y | |
chip_array = [] | |
for c in range(chip_column): | |
for r in range(chip_row): | |
chip_x = origin_x + (c * pitch_x) + offset_x | |
chip_y = origin_y + (r * pitch_y) + offset_y | |
chip_array.append(pya.Polygon(Toolkit.rect(chip_x, chip_y, self.chip_w, self.chip_h, self.layout.dbu))) | |
chip_reg = pya.Region() | |
chip_part_reg = pya.Region() | |
chip_reg.merged_semantics = False | |
chip_part_reg.merged_semantics = False | |
ebr_reg.merged_semantics = False | |
chip_reg.insert(chip_array) | |
chip_good_reg = chip_reg.inside(ebr_reg) | |
for rect in chip_reg.not_inside(ebr_reg): | |
chip_part_reg.insert(ebr_reg & rect) | |
self.cell.shapes(self.lc_layer).insert(chip_good_reg) | |
self.cell.shapes(self.lcp_layer).insert(chip_part_reg) | |
chip_count_txt = "chip: %d" % (chip_good_reg.count()) | |
chip_count_trans = pya.DTrans (-self.wafer_radius/unit + 200/unit, -self.wafer_radius/unit + 200/unit) | |
self.cell.shapes(self.lc_layer).insert(pya.Text(chip_count_txt, chip_count_trans)) | |
class gdsToolkit(pya.Library): | |
def __init__(self): | |
self.description = "Toolkit" | |
self.layout().register_pcell("Wafer", Wafer()) | |
self.register("Toolkit") | |
gdsToolkit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment