2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 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.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
2019-10-03 01:26:02 +02:00
|
|
|
This provides a set of useful generic types for the gdsMill interface.
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
|
|
|
|
import debug
|
2016-11-11 02:28:06 +01:00
|
|
|
from vector import vector
|
2017-12-19 18:55:59 +01:00
|
|
|
import tech
|
2017-12-19 18:01:24 +01:00
|
|
|
import math
|
|
|
|
|
from globals import OPTS
|
2018-10-04 23:04:29 +02:00
|
|
|
from utils import round_to_grid
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2019-10-03 01:26:02 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
class geometry:
|
|
|
|
|
"""
|
|
|
|
|
A specific path, shape, or text geometry. Base class for shared
|
|
|
|
|
items.
|
|
|
|
|
"""
|
|
|
|
|
def __init__(self):
|
|
|
|
|
""" By default, everything has no size. """
|
|
|
|
|
self.width = 0
|
|
|
|
|
self.height = 0
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
""" override print function output """
|
2019-10-03 01:26:02 +02:00
|
|
|
debug.error("__str__ must be overridden by all geometry types.", 1)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
""" override print function output """
|
2019-10-03 01:26:02 +02:00
|
|
|
debug.error("__repr__ must be overridden by all geometry types.", 1)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-12-19 18:01:24 +01:00
|
|
|
# def translate_coords(self, coords, mirr, angle, xyShift):
|
|
|
|
|
# """Calculate coordinates after flip, rotate, and shift"""
|
|
|
|
|
# coordinate = []
|
|
|
|
|
# for item in coords:
|
|
|
|
|
# x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0])
|
|
|
|
|
# y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1])
|
|
|
|
|
# coordinate += [(x, y)]
|
|
|
|
|
# return coordinate
|
|
|
|
|
|
2018-09-05 18:28:43 +02:00
|
|
|
def transform_coords(self, coords, offset, mirr, angle):
|
|
|
|
|
"""Calculate coordinates after flip, rotate, and shift"""
|
|
|
|
|
coordinate = []
|
|
|
|
|
for item in coords:
|
2019-10-03 01:26:02 +02:00
|
|
|
x = item[0] * math.cos(angle) - item[1] * mirr * math.sin(angle) + offset[0]
|
|
|
|
|
y = item[0] * math.sin(angle) + item[1] * mirr * math.cos(angle) + offset[1]
|
2018-09-05 18:28:43 +02:00
|
|
|
coordinate += [[x, y]]
|
|
|
|
|
return coordinate
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def normalize(self):
|
|
|
|
|
""" Re-find the LL and UR points after a transform """
|
2019-10-03 01:26:02 +02:00
|
|
|
(first, second) = self.boundary
|
|
|
|
|
ll = vector(min(first[0], second[0]),
|
|
|
|
|
min(first[1], second[1])).snap_to_grid()
|
|
|
|
|
ur = vector(max(first[0], second[0]),
|
|
|
|
|
max(first[1], second[1])).snap_to_grid()
|
|
|
|
|
self.boundary = [ll, ur]
|
2018-08-27 19:42:40 +02:00
|
|
|
|
|
|
|
|
def update_boundary(self):
|
|
|
|
|
""" Update the boundary with a new placement. """
|
2019-10-03 01:26:02 +02:00
|
|
|
self.compute_boundary(self.offset, self.mirror, self.rotate)
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2019-10-03 01:26:02 +02:00
|
|
|
def compute_boundary(self, offset=vector(0, 0), mirror="", rotate=0):
|
|
|
|
|
""" Transform with offset, mirror and rotation to get the absolute pin location.
|
2017-08-24 00:02:15 +02:00
|
|
|
We must then re-find the ll and ur. The master is the cell instance. """
|
2018-08-28 01:42:48 +02:00
|
|
|
if OPTS.netlist_only:
|
2020-01-24 22:24:44 +01:00
|
|
|
self.boundary = [vector(0,0), vector(0,0)]
|
2018-08-28 01:42:48 +02:00
|
|
|
return
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2019-10-03 01:26:02 +02:00
|
|
|
(ll, ur) = [vector(0, 0), vector(self.width, self.height)]
|
|
|
|
|
|
|
|
|
|
if mirror == "MX":
|
|
|
|
|
ll = ll.scale(1, -1)
|
|
|
|
|
ur = ur.scale(1, -1)
|
|
|
|
|
elif mirror == "MY":
|
|
|
|
|
ll = ll.scale(-1, 1)
|
|
|
|
|
ur = ur.scale(-1, 1)
|
|
|
|
|
elif mirror == "XY":
|
|
|
|
|
ll = ll.scale(-1, -1)
|
|
|
|
|
ur = ur.scale(-1, -1)
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2019-10-03 01:26:02 +02:00
|
|
|
if rotate == 90:
|
|
|
|
|
ll = ll.rotate_scale(-1, 1)
|
|
|
|
|
ur = ur.rotate_scale(-1, 1)
|
|
|
|
|
elif rotate == 180:
|
|
|
|
|
ll = ll.scale(-1, -1)
|
|
|
|
|
ur = ur.scale(-1, -1)
|
|
|
|
|
elif rotate == 270:
|
|
|
|
|
ll = ll.rotate_scale(1, -1)
|
|
|
|
|
ur = ur.rotate_scale(1, -1)
|
|
|
|
|
|
|
|
|
|
self.boundary = [offset + ll, offset + ur]
|
2017-08-24 00:02:15 +02:00
|
|
|
self.normalize()
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def ll(self):
|
|
|
|
|
""" Return the lower left corner """
|
|
|
|
|
return self.boundary[0]
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def ur(self):
|
|
|
|
|
""" Return the upper right corner """
|
|
|
|
|
return self.boundary[1]
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def lr(self):
|
|
|
|
|
""" Return the lower right corner """
|
|
|
|
|
return vector(self.boundary[1].x, self.boundary[0].y)
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def ul(self):
|
|
|
|
|
""" Return the upper left corner """
|
|
|
|
|
return vector(self.boundary[0].x, self.boundary[1].y)
|
|
|
|
|
|
|
|
|
|
def uy(self):
|
|
|
|
|
""" Return the upper edge """
|
|
|
|
|
return self.boundary[1].y
|
|
|
|
|
|
|
|
|
|
def by(self):
|
|
|
|
|
""" Return the bottom edge """
|
|
|
|
|
return self.boundary[0].y
|
|
|
|
|
|
|
|
|
|
def lx(self):
|
|
|
|
|
""" Return the left edge """
|
|
|
|
|
return self.boundary[0].x
|
|
|
|
|
|
|
|
|
|
def rx(self):
|
|
|
|
|
""" Return the right edge """
|
|
|
|
|
return self.boundary[1].x
|
2017-12-19 18:01:24 +01:00
|
|
|
|
2018-03-21 21:20:48 +01:00
|
|
|
def cx(self):
|
|
|
|
|
""" Return the center x """
|
2019-10-03 01:26:02 +02:00
|
|
|
return 0.5 * (self.boundary[0].x + self.boundary[1].x)
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2018-03-21 21:20:48 +01:00
|
|
|
def cy(self):
|
|
|
|
|
""" Return the center y """
|
2019-10-03 01:26:02 +02:00
|
|
|
return 0.5 * (self.boundary[0].y + self.boundary[1].y)
|
2020-02-28 19:24:09 +01:00
|
|
|
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-12-19 18:01:24 +01:00
|
|
|
class instance(geometry):
|
|
|
|
|
"""
|
|
|
|
|
An instance of an instance/module with a specified location and
|
|
|
|
|
rotation
|
|
|
|
|
"""
|
2019-10-03 01:26:02 +02:00
|
|
|
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
|
2017-12-19 18:01:24 +01:00
|
|
|
"""Initializes an instance to represent a module"""
|
|
|
|
|
geometry.__init__(self)
|
2019-10-03 01:26:02 +02:00
|
|
|
debug.check(mirror not in ["R90", "R180", "R270"],
|
|
|
|
|
"Please use rotation and not mirroring during instantiation.")
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-12-19 18:01:24 +01:00
|
|
|
self.name = name
|
|
|
|
|
self.mod = mod
|
|
|
|
|
self.gds = mod.gds
|
|
|
|
|
self.rotate = rotate
|
|
|
|
|
self.offset = vector(offset).snap_to_grid()
|
|
|
|
|
self.mirror = mirror
|
2018-10-22 18:17:03 +02:00
|
|
|
if OPTS.netlist_only:
|
|
|
|
|
self.width = 0
|
|
|
|
|
self.height = 0
|
|
|
|
|
else:
|
2019-10-03 01:26:02 +02:00
|
|
|
if mirror in ["R90", "R270"] or rotate in [90, 270]:
|
2018-10-25 01:12:27 +02:00
|
|
|
self.width = round_to_grid(mod.height)
|
|
|
|
|
self.height = round_to_grid(mod.width)
|
|
|
|
|
else:
|
|
|
|
|
self.width = round_to_grid(mod.width)
|
|
|
|
|
self.height = round_to_grid(mod.height)
|
2019-10-03 01:26:02 +02:00
|
|
|
self.compute_boundary(offset, mirror, rotate)
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-12-19 18:01:24 +01:00
|
|
|
debug.info(4, "creating instance: " + self.name)
|
|
|
|
|
|
2019-11-15 19:47:59 +01:00
|
|
|
def get_blockages(self, lpp, top=False):
|
2019-06-25 18:20:00 +02:00
|
|
|
""" Retrieve blockages of all modules in this instance.
|
2018-09-05 18:28:43 +02:00
|
|
|
Apply the transform of the instance placement to give absolute blockages."""
|
|
|
|
|
angle = math.radians(float(self.rotate))
|
|
|
|
|
mirr = 1
|
2019-10-03 01:26:02 +02:00
|
|
|
if self.mirror == "R90":
|
2018-09-05 18:28:43 +02:00
|
|
|
angle += math.radians(90.0)
|
2019-10-03 01:26:02 +02:00
|
|
|
elif self.mirror == "R180":
|
2018-09-05 18:28:43 +02:00
|
|
|
angle += math.radians(180.0)
|
2019-10-03 01:26:02 +02:00
|
|
|
elif self.mirror == "R270":
|
2018-09-05 18:28:43 +02:00
|
|
|
angle += math.radians(270.0)
|
2019-10-03 01:26:02 +02:00
|
|
|
elif self.mirror == "MX":
|
2018-09-05 18:28:43 +02:00
|
|
|
mirr = -1
|
2019-10-03 01:26:02 +02:00
|
|
|
elif self.mirror == "MY":
|
2018-09-05 18:28:43 +02:00
|
|
|
mirr = -1
|
|
|
|
|
angle += math.radians(180.0)
|
2019-10-03 01:26:02 +02:00
|
|
|
elif self.mirror == "XY":
|
2018-09-05 18:28:43 +02:00
|
|
|
mirr = 1
|
|
|
|
|
angle += math.radians(180.0)
|
2019-06-24 17:59:58 +02:00
|
|
|
|
|
|
|
|
new_blockages = []
|
2018-09-05 18:28:43 +02:00
|
|
|
if self.mod.is_library_cell:
|
2019-06-25 18:20:00 +02:00
|
|
|
# Writes library cell blockages as shapes instead of a large metal blockage
|
2019-06-24 17:59:58 +02:00
|
|
|
blockages = []
|
2019-11-15 19:47:59 +01:00
|
|
|
blockages = self.mod.gds.getBlockages(lpp)
|
2019-06-24 17:59:58 +02:00
|
|
|
for b in blockages:
|
2019-12-06 06:33:13 +01:00
|
|
|
new_blockages.append(self.transform_coords(b,self.offset, mirr, angle))
|
2018-09-05 18:28:43 +02:00
|
|
|
else:
|
2019-11-15 19:47:59 +01:00
|
|
|
blockages = self.mod.get_blockages(lpp)
|
2018-09-05 18:28:43 +02:00
|
|
|
for b in blockages:
|
|
|
|
|
new_blockages.append(self.transform_coords(b,self.offset, mirr, angle))
|
2019-06-24 17:59:58 +02:00
|
|
|
return new_blockages
|
|
|
|
|
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-12-19 18:01:24 +01:00
|
|
|
def gds_write_file(self, new_layout):
|
|
|
|
|
"""Recursively writes all the sub-modules in this instance"""
|
|
|
|
|
debug.info(4, "writing instance: " + self.name)
|
2020-02-20 01:26:52 +01:00
|
|
|
# make sure to write out my module/structure
|
2017-12-19 18:01:24 +01:00
|
|
|
# (it will only be written the first time though)
|
|
|
|
|
self.mod.gds_write_file(self.gds)
|
|
|
|
|
# now write an instance of my module/structure
|
|
|
|
|
new_layout.addInstance(self.gds,
|
2018-11-16 20:48:41 +01:00
|
|
|
self.mod.name,
|
2018-10-04 23:04:29 +02:00
|
|
|
offsetInMicrons=self.offset,
|
|
|
|
|
mirror=self.mirror,
|
|
|
|
|
rotate=self.rotate)
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2018-08-28 02:25:39 +02:00
|
|
|
def place(self, offset, mirror="R0", rotate=0):
|
|
|
|
|
""" This updates the placement of an instance. """
|
|
|
|
|
# Update the placement of an already added instance
|
2018-10-04 23:04:29 +02:00
|
|
|
self.offset = vector(offset).snap_to_grid()
|
2018-08-28 02:25:39 +02:00
|
|
|
self.mirror = mirror
|
|
|
|
|
self.rotate = rotate
|
|
|
|
|
self.update_boundary()
|
2018-11-16 20:48:41 +01:00
|
|
|
debug.info(3, "placing instance {}".format(self))
|
2020-02-20 01:26:52 +01:00
|
|
|
|
|
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
def get_pin(self,name,index=-1):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Return an absolute pin that is offset and transformed based on
|
2017-12-12 23:53:19 +01:00
|
|
|
this instance location. Index will return one of several pins."""
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
import copy
|
2019-10-03 01:26:02 +02:00
|
|
|
if index == -1:
|
2017-12-12 23:53:19 +01:00
|
|
|
pin = copy.deepcopy(self.mod.get_pin(name))
|
|
|
|
|
pin.transform(self.offset,self.mirror,self.rotate)
|
|
|
|
|
return pin
|
|
|
|
|
else:
|
|
|
|
|
pins = copy.deepcopy(self.mod.get_pin(name))
|
|
|
|
|
pin.transform(self.offset,self.mirror,self.rotate)
|
|
|
|
|
return pin[index]
|
|
|
|
|
|
|
|
|
|
def get_num_pins(self, name):
|
|
|
|
|
""" Return the number of pins of a given name """
|
|
|
|
|
return len(self.mod.get_pins(name))
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def get_pins(self,name):
|
|
|
|
|
""" Return an absolute pin that is offset and transformed based on
|
|
|
|
|
this instance location. """
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
import copy
|
|
|
|
|
pin = copy.deepcopy(self.mod.get_pins(name))
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
new_pins = []
|
|
|
|
|
for p in pin:
|
2020-02-20 01:26:52 +01:00
|
|
|
p.transform(self.offset,self.mirror,self.rotate)
|
2017-08-24 00:02:15 +02:00
|
|
|
new_pins.append(p)
|
|
|
|
|
return new_pins
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def __str__(self):
|
|
|
|
|
""" override print function output """
|
2018-10-04 23:04:29 +02:00
|
|
|
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
""" override print function output """
|
|
|
|
|
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
|
|
|
|
|
|
|
|
|
|
class path(geometry):
|
|
|
|
|
"""Represents a Path"""
|
|
|
|
|
|
2019-10-25 19:03:25 +02:00
|
|
|
def __init__(self, lpp, coordinates, path_width):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Initializes a path for the specified layer"""
|
|
|
|
|
geometry.__init__(self)
|
|
|
|
|
self.name = "path"
|
2019-10-25 19:03:25 +02:00
|
|
|
self.layerNumber = lpp[0]
|
|
|
|
|
self.layerPurpose = lpp[1]
|
2016-11-08 18:57:35 +01:00
|
|
|
self.coordinates = map(lambda x: [x[0], x[1]], coordinates)
|
2016-11-20 20:06:53 +01:00
|
|
|
self.coordinates = vector(self.coordinates).snap_to_grid()
|
2016-11-08 18:57:35 +01:00
|
|
|
self.path_width = path_width
|
|
|
|
|
|
|
|
|
|
# FIXME figure out the width/height. This type of path is not
|
|
|
|
|
# supported right now. It might not work in gdsMill.
|
|
|
|
|
assert(0)
|
|
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
def gds_write_file(self, new_layout):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Writes the path to GDS"""
|
2017-09-30 01:22:13 +02:00
|
|
|
debug.info(4, "writing path (" + str(self.layerNumber) + "): " + self.coordinates)
|
2018-10-04 23:04:29 +02:00
|
|
|
new_layout.addPath(layerNumber=self.layerNumber,
|
2019-10-25 19:03:25 +02:00
|
|
|
purposeNumber=self.layerPurpose,
|
2018-10-04 23:04:29 +02:00
|
|
|
coordinates=self.coordinates,
|
|
|
|
|
width=self.path_width)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-12-19 18:01:24 +01:00
|
|
|
def get_blockages(self, layer):
|
|
|
|
|
""" Fail since we don't support paths yet. """
|
|
|
|
|
assert(0)
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def __str__(self):
|
|
|
|
|
""" override print function output """
|
|
|
|
|
return "path: layer=" + self.layerNumber + " w=" + self.width
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
""" override print function output """
|
|
|
|
|
return "( path: layer=" + self.layerNumber + " w=" + self.width + " coords=" + str(self.coordinates) + " )"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class label(geometry):
|
|
|
|
|
"""Represents a text label"""
|
|
|
|
|
|
2019-10-25 19:03:25 +02:00
|
|
|
def __init__(self, text, lpp, offset, zoom=-1):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Initializes a text label for specified layer"""
|
|
|
|
|
geometry.__init__(self)
|
|
|
|
|
self.name = "label"
|
|
|
|
|
self.text = text
|
2019-10-25 19:03:25 +02:00
|
|
|
self.layerNumber = lpp[0]
|
|
|
|
|
self.layerPurpose = lpp[1]
|
2016-11-20 20:06:53 +01:00
|
|
|
self.offset = vector(offset).snap_to_grid()
|
2017-12-19 18:01:24 +01:00
|
|
|
|
2017-05-24 01:18:11 +02:00
|
|
|
if zoom<0:
|
2017-12-19 18:55:59 +01:00
|
|
|
self.zoom = tech.GDS["zoom"]
|
2017-05-24 01:18:11 +02:00
|
|
|
else:
|
|
|
|
|
self.zoom = zoom
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.size = 0
|
|
|
|
|
|
2017-09-30 01:22:13 +02:00
|
|
|
debug.info(4,"creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset))
|
2016-11-18 23:10:30 +01:00
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
def gds_write_file(self, new_layout):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Writes the text label to GDS"""
|
2017-09-30 01:22:13 +02:00
|
|
|
debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text)
|
2018-10-04 23:04:29 +02:00
|
|
|
new_layout.addText(text=self.text,
|
|
|
|
|
layerNumber=self.layerNumber,
|
2020-02-21 03:35:54 +01:00
|
|
|
purposeNumber=self.layerPurpose,
|
2018-10-04 23:04:29 +02:00
|
|
|
offsetInMicrons=self.offset,
|
|
|
|
|
magnification=self.zoom,
|
|
|
|
|
rotate=None)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-12-19 18:01:24 +01:00
|
|
|
def get_blockages(self, layer):
|
|
|
|
|
""" Returns an empty list since text cannot be blockages. """
|
|
|
|
|
return []
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def __str__(self):
|
|
|
|
|
""" override print function output """
|
|
|
|
|
return "label: " + self.text + " layer=" + str(self.layerNumber)
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
""" override print function output """
|
2018-10-04 23:04:29 +02:00
|
|
|
return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )"
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
class rectangle(geometry):
|
|
|
|
|
"""Represents a rectangular shape"""
|
|
|
|
|
|
2019-10-25 19:03:25 +02:00
|
|
|
def __init__(self, lpp, offset, width, height):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Initializes a rectangular shape for specified layer"""
|
|
|
|
|
geometry.__init__(self)
|
|
|
|
|
self.name = "rect"
|
2019-10-25 19:03:25 +02:00
|
|
|
self.layerNumber = lpp[0]
|
|
|
|
|
self.layerPurpose = lpp[1]
|
2016-11-20 20:06:53 +01:00
|
|
|
self.offset = vector(offset).snap_to_grid()
|
|
|
|
|
self.size = vector(width, height).snap_to_grid()
|
2018-10-04 23:04:29 +02:00
|
|
|
self.width = round_to_grid(self.size.x)
|
|
|
|
|
self.height = round_to_grid(self.size.y)
|
2019-10-03 01:26:02 +02:00
|
|
|
self.compute_boundary(offset, "", 0)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2019-10-03 01:26:02 +02:00
|
|
|
debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): "
|
2016-11-18 23:10:30 +01:00
|
|
|
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
|
2020-02-20 01:26:52 +01:00
|
|
|
|
2017-12-19 18:01:24 +01:00
|
|
|
def get_blockages(self, layer):
|
|
|
|
|
""" Returns a list of one rectangle if it is on this layer"""
|
|
|
|
|
if self.layerNumber == layer:
|
2019-10-03 01:26:02 +02:00
|
|
|
return [[self.offset,
|
|
|
|
|
vector(self.offset.x + self.width,
|
|
|
|
|
self.offset.y + self.height)]]
|
2017-12-19 18:01:24 +01:00
|
|
|
else:
|
|
|
|
|
return []
|
2016-11-18 23:10:30 +01:00
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
def gds_write_file(self, new_layout):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Writes the rectangular shape to GDS"""
|
2019-10-03 01:26:02 +02:00
|
|
|
debug.info(4, "writing rectangle (" + str(self.layerNumber) + "):"
|
2016-11-18 23:10:30 +01:00
|
|
|
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
|
2018-10-04 23:04:29 +02:00
|
|
|
new_layout.addBox(layerNumber=self.layerNumber,
|
2019-10-25 19:03:25 +02:00
|
|
|
purposeNumber=self.layerPurpose,
|
2018-10-04 23:04:29 +02:00
|
|
|
offsetInMicrons=self.offset,
|
|
|
|
|
width=self.width,
|
|
|
|
|
height=self.height,
|
|
|
|
|
center=False)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
""" override print function output """
|
2019-04-17 22:41:17 +02:00
|
|
|
return self.__repr__()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
""" override print function output """
|
2019-04-17 22:41:17 +02:00
|
|
|
return "( rect: @" + str(self.offset) + " WxH=" + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " )"
|