Skip to main content

Lithography mask library

Project description

Masque README

Masque is a Python module for designing lithography masks.

The general idea is to implement something resembling the GDSII file-format, but with some vectorized element types (eg. circles, not just polygons) and the ability to output to multiple formats.

Installation

Requirements:

  • python >= 3.11
  • numpy
  • klamath (used for GDSII i/o)

Optional requirements:

  • ezdxf (DXF i/o): ezdxf
  • oasis (OASIS i/o): fatamorgana
  • svg (SVG output): svgwrite
  • visualization (shape plotting): matplotlib
  • text (Text shape): matplotlib, freetype

Install with pip:

pip install 'masque[oasis,dxf,svg,visualization,text]'

Overview

A layout consists of a hierarchy of Patterns stored in a single Library. Each Pattern can contain Refs pointing at other patterns, Shapes, Labels, and Ports.

masque departs from several "classic" GDSII paradigms:

  • A Pattern object does not store its own name. A name is only assigned when the pattern is placed into a Library, which is effectively a name->Pattern mapping.
  • Layer info for Shapess and Labels is not stored in the individual shape and label objects. Instead, the layer is determined by the key for the container dict (e.g. pattern.shapes[layer]).
    • This simplifies many common tasks: filtering Shapes by layer, remapping layers, and checking if a layer is empty.
    • Technically, this allows reusing the same shape or label object across multiple layers. This isn't part of the standard workflow since a mixture of single-use and multi-use shapes could be confusing.
    • This is similar to the approach used in KLayout
  • Ref target names are also determined in the key of the container dict (e.g. pattern.refs[target_name]).
    • This similarly simplifies filtering Refs by target name, updating to a new target, and checking if a given Pattern is referenced.
  • Pattern names are set by their containing Library and are not stored in the Pattern objects.
    • This guarantees that there are no duplicate pattern names within any given Library.
    • Likewise, enumerating all the names (and all the Patterns) in a Library is straightforward.
  • Each Ref, Shape, or Label can be repeated multiple times by attaching a repetition object to it.
    • This is similar to how OASIS reptitions are handled, and provides extra flexibility over the GDSII approach of only allowing arrays through AREF (Ref + repetition).
  • Labels do not have an orientation or presentation
    • This is in line with how they are used in practice, and how they are represented in OASIS.
  • Non-polygonal Shapes are allowed. For example, elliptical arcs are a basic shape type.
    • This enables compatibility with OASIS (e.g. circles) and other formats.
    • Shapes provide a .to_polygons() method for GDSII compatibility.
  • Most coordinate values are stored as 64-bit floats internally.
    • 1 earth radii in nanometers (6e15) is still represented without approximation (53 bit mantissa -> 2^53 > 9e15)
    • Operations that would otherwise clip/round on are still represented approximately.
    • Memory usage is usually dominated by other Python overhead.
  • Pattern objects also contain Port information, which can be used to "snap" together multiple sub-components by matching up the requested port offsets and rotations.
    • Port rotations are defined as counter-clockwise angles from the +x axis.
    • Ports point into the interior of their associated device.
    • Port rotations may be None in the case of non-oriented ports.
    • Ports have a ptype string which is compared in order to catch mismatched connections at build time.
    • Ports can be exported into/imported from Labels stored directly in the layout, editable from standard tools (e.g. KLayout). A default format is provided.

In one important way, masque stays very orthodox: References are accomplished by listing the target's name, not its Pattern object.

  • The main downside of this is that any operations that traverse the hierarchy require both the Pattern and the Library which is contains its reference targets.
  • This guarantees that names within a Library remain unique at all times.
    • Since this can be tedious in cases where you don't actually care about the name of a pattern, patterns whose names start with SINGLE_USE_PREFIX (default: an underscore) may be silently renamed in order to maintain uniqueness. See masque.library.SINGLE_USE_PREFIX, masque.library._rename_patterns(), and ILibrary.add() for more details.
  • Having all patterns accessible through the Library avoids having to perform a tree traversal for every operation which needs to touch all Pattern objects (e.g. deleting a layer everywhere or scaling all patterns).
  • Since Pattern doesn't know its own name, you can't create a reference by passing in a Pattern object -- you need to know its name.
  • You can reference a Pattern before it is created, so long as you have already decided on its name.
  • Functions like Pattern.place() and Pattern.plug() need to receive a pattern's name in order to create a reference, but they also need to access the pattern's ports.
    • One way to provide this data is through an Abstract, generated via Library.abstract() or through a Library.abstract_view().
    • Another way is use Builder.place() or Builder.plug(), which automatically creates an Abstract from its internally-referenced Library.

Glossary

  • Library: A collection of named cells. OASIS or GDS "library" or file.
  • Tree: Any {name: pattern} mapping which has only one topcell.
  • Pattern: A collection of geometry, text labels, and reference to other patterns. OASIS or GDS "Cell", DXF "Block".
  • Ref: A reference to another pattern. GDS "AREF/SREF", OASIS "Placement".
  • Shape: Individual geometric entity. OASIS or GDS "Geometry element", DXF "LWPolyline" or "Polyline".
  • repetition: Repetition operation. OASIS "repetition". GDS "AREF" is a Ref combined with a Grid repetition.
  • Label: Text label. Not rendered into geometry. OASIS, GDS, DXF "Text".
  • annotation: Additional metadata. OASIS or GDS "property".

Syntax, shorthand, and design patterns

Most syntax and behavior should follow normal python conventions. There are a few exceptions, either meant to catch common mistakes or to provide a shorthand for common operations:

Library objects don't allow overwriting already-existing patterns

library['mycell'] = pattern0
library['mycell'] = pattern1   # Error! 'mycell' already exists and can't be overwritten
del library['mycell']          # We can explicitly delete it
library['mycell'] = pattern1   # And now it's ok to assign a new value
library.delete('mycell')       # This also deletes all refs pointing to 'mycell' by default

Insert a newly-made hierarchical pattern (with children) into a layout

# Let's say we have a function which returns a new library containing one topcell (and possibly children)
tree = make_tree(...)

# To reference this cell in our layout, we have to add all its children to our `library` first:
top_name = tree.top()              # get the name of the topcell
name_mapping = library.add(tree)   # add all patterns from `tree`, renaming elgible conflicting patterns
new_name = name_mapping.get(top_name, top_name)    # get the new name for the cell (in case it was auto-renamed)
my_pattern.ref(new_name, ...)       # instantiate the cell

# This can be accomplished as follows
new_name = library << tree       # Add `tree` into `library` and return the top cell's new name
my_pattern.ref(new_name, ...)       # instantiate the cell

# In practice, you may do lots of
my_pattern.ref(lib << make_tree(...), ...)

# With a `Builder` and `place()`/`plug()` the `lib <<` portion can be implicit:
my_builder = Builder(library=lib, ...)
...
my_builder.place(make_tree(...))

We can also use this shorthand to quickly add and reference a single flat (as yet un-named) pattern:

anonymous_pattern = Pattern(...)
my_pattern.ref(lib << {'_tentative_name': anonymous_pattern}, ...)

Place a hierarchical pattern into a layout, preserving its port info

# As above, we have a function that makes a new library containing one topcell (and possibly children)
tree = make_tree(...)

# We need to go get its port info to `place()` it into our existing layout,
new_name = library << tree          # Add the tree to the library and return its name (see `<<` above)
abstract = library.abstract(tree)   # An `Abstract` stores a pattern's name and its ports (but no geometry)
my_pattern.place(abstract, ...)

# With shorthand,
abstract = library <= tree
my_pattern.place(abstract, ...)

# or
my_pattern.place(library << make_tree(...), ...)


### Quickly add geometry, labels, or refs:
The long form for adding elements can be overly verbose:
```python3
my_pattern.shapes[layer].append(Polygon(vertices, ...))
my_pattern.labels[layer] += [Label('my text')]
my_pattern.refs[target_name].append(Ref(offset=..., ...))

There is shorthand for the most common elements:

my_pattern.polygon(layer=layer, vertices=vertices, ...)
my_pattern.rect(layer=layer, xctr=..., xmin=..., ymax=..., ly=...)  # rectangle; pick 4 of 6 constraints
my_pattern.rect(layer=layer, ymin=..., ymax=..., xctr=..., lx=...)
my_pattern.path(...)
my_pattern.label(layer, 'my_text')
my_pattern.ref(target_name, offset=..., ...)

Accessing ports

# Square brackets pull from the underlying `.ports` dict:
assert pattern['input'] is pattern.ports['input']

# And you can use them to read multiple ports at once:
assert pattern[('input', 'output')] == {
    'input': pattern.ports['input'],
    'output': pattern.ports['output'],
    }

# But you shouldn't use them for anything except reading
pattern['input'] = Port(...)   # Error!
has_input = ('input' in pattern)   # Error!

Building patterns

library = Library(...)
my_pattern_name, my_pattern = library.mkpat(some_name_generator())
...
def _make_my_subpattern() -> str:
    #   This function can draw from the outer scope (e.g. `library`) but will not pollute the outer scope
    # (e.g. the variable `subpattern` will not be accessible from outside the function; you must load it
    # from within `library`).
    subpattern_name, subpattern = library.mkpat(...)
    subpattern.rect(...)
    ...
    return subpattern_name
my_pattern.ref(_make_my_subpattern(), offset=..., ...)

TODO

  • Better interface for polygon operations (e.g. with pyclipper)
    • de-embedding
    • boolean ops
  • Tests tests tests
  • check renderpather
  • pather and renderpather examples

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

masque-3.1.tar.gz (144.8 kB view hashes)

Uploaded Source

Built Distribution

masque-3.1-py3-none-any.whl (171.9 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page