A Julia package for CAD of electronic devices, in particular superconducting devices operating at microwave frequencies.
Julia 0.7 and above
Activate package mode by pressing
] at the REPL in a Julia console.
When Clipper.jl is added, it will be built to compile shared library / DLL files. A compiler will be downloaded for you on Windows.
Let's mock up a transmission line with two launchers and some bridges across the transmission line. We begin by making a cell with a rectangle in it:
using Devices, Devices.PreferMicrons, FileIO cr = Cell("rect", nm) r = centered(Rectangle(20μm, 40μm)) render!(cr, r, Rectangles.Plain(), GDSMeta(1,0))
Note that when you use
Devices.PreferMicrons, this will also enable the unqualified use of the following units:
rad. (By unqualified we mean that the symbols are imported into the calling namespace and do not need to be prefixed with a module name.) When adding length units together, if the units don't agree, the result will be in microns. You can instead do
Devices.PreferNanometers if you want the result to default to nanometers. (These are your two choices at the moment, though there's nothing fundamentally limiting other possibilities: see
src/units.jl for how to do this for other units.)
When you specify the units for a
Cell, you are specifying a database unit. Anything rendered into this cell will be discretized into integer multiples of the database unit. This means that nothing smaller than 1 nm can be represented accurately. Nonetheless, this is typically a satisfactory choice for superconducting devices.
A rectangle made with a width and height parameter will default to having its lower-left corner at the origin.
centered will return a rectangle that is centered about the origin instead.
The rectangle is then rendered into the cell.
Rectangles.Plain() specifies a rendering style. Other examples include
Rectangles.Rounded (where the corners are rounded off) or
Rectangles.Undercut. You can omit the style, in which case
Rectangles.Plain() will be assumed.
GDSMeta(1) indicates the target GDS-II layer. You can also specify the GDS-II datatype as a second argument, e.g.
In another cell, we make the transmission line with some launchers on both ends:
p = Path(μm) sty = launch!(p) straight!(p, 500μm, sty) turn!(p, π/2, 150μm) straight!(p, 500μm) launch!(p) cp = Cell("pathonly", nm) render!(cp, p, GDSMeta(0))
Finally, let's put bridges across the feedline:
turnidx = Int((length(p)+1)/2) - 1 # the first straight segment of the path simplify!(p, turnidx .+ (0:2)) attach!(p, CellReference(cr, Point(0.0μm, 0.0μm)), (40μm):(40μm):((pathlength(p[turnidx]))-40μm), i=turnidx) c = Cell("decoratedpath", nm) render!(c, p, GDSMeta(0))
How easy was that?
You can save a GDS file for e-beam lithography, or an SVG for vector graphics by using
save with an appropriate extension:
save("/path/to/myoutput.gds", c) save("/path/to/myoutput.svg", c)
Note that SVG support is experimental at the moment, and is not completely optimized. It is however used in generating the graphics you see in this documentation. If you use Juno for Atom, rendered cells are automatically previewed in the plot pane provided you enter
Devices.@junographics at the start of your session. If you use Jupyter/IJulia, rendered cells are automatically returned as a result
Example without using units
For compatibility and laziness reasons it is possible to use Devices.jl without units at all. If you do not provide units, all values are presumed to be in microns. The syntax is otherwise the same:
using Devices, FileIO cr = Cell("rect") r = centered(Rectangle(20,40)) render!(cr, r, GDSMeta(1)) p = Path() sty = launch!(p) straight!(p,500,sty) turn!(p,π/2,150) straight!(p,500) launch!(p) cp = Cell("pathonly") render!(cp, p, GDSMeta(0)) turnidx = Int((length(p)+1)/2) - 1 # the first straight segment of the path simplify!(p, turnidx+(0:2)) attach!(p, CellReference(cr, Point(0.0,0.0)), 40:40:((pathlength(p[turnidx]))-40), i=turnidx) c = Cell("decoratedpath") render!(c, p, GDSMeta(0))
- You cannot mix and match unitful and unitless numbers (the latter are not presumed to be in microns in this case).
- It is somewhat annoying to maintain this behavior alongside unit support, so eventually I may drop support for this and require the use of units.
Since Julia has a just-in-time compiler, the first time code is executed may take much longer than any other times. This means that a lot of time will be wasted repeating compilations if you run Devices.jl in a script like you would in other languages. For readability, it is best to split up your CAD code into functions that have clearly named inputs and perform a well-defined task. At present, for performance reasons, it is also best to avoid writing functions with keyword arguments (though this will be addressed in Julia 1.0).
It is also best to avoid writing statements in global scope. In other words, put most of your code in a function. Your CAD script should ideally look like the following:
using Devices, Devices.PreferMicrons, FileIO using CoordinateTransformations using Clipper function subroutine1() # render some thing end function subroutine2() # render some other thing end function main() # my cad code goes here: do all of the things subroutine1() subroutine2() save("/path/to/out.gds", ...) end main() # execute main() at end of script.
You can then
include this file from Julia to generate your pattern. Provided you write your script this way, subsequent runs should be several times faster than the first if you
include the file again from the same Julia session.
- If you cannot save the GDS file, try deleting any file that happens to be at the target path. A corrupted file at the target path may prevent saving.
- Decorated styles should not become part of compound styles, for now. Avoid this by decorating / attaching cell references at the end.