Schematic-driven design

SchematicDrivenLayout provides an interface for schematic-driven design, intended to

  • Support a formal schematic-level description of a device
  • Make placing and connecting components and routing wires easier and more automated
  • Allow inspection/analysis/modification of a design at a higher level
  • Keep layers of abstraction separate, except for deliberate abstraction-breaking "escape hatches" to allow detailed, flexible control over layouts

The schematic-driven design flow goes like this:

Here's what that looks like as a diagram, for comparison with geometry-level flows:

Quick start

Defining a component

We'll define an arrow similar to SchematicDrivenLayout.ArrowAnnotation but without any text:

using DeviceLayout, .SchematicDrivenLayout
import DeviceLayout: mm, μm, nm
import DeviceLayout.SchematicDrivenLayout: AbstractComponent
import DeviceLayout.Graphics: inch
using FileIO

### Define the component type
"""
    struct ArrowComponent <: AbstractComponent{typeof(1.0nm)}
        name = "arrow"
        length = 0.05mm
        width = 0.005mm
    end

An arrow with a given length and width.

The arrow is drawn in the `SemanticMeta(:annotation)` layer.

# Hooks

  - `nock`: The base of the arrow, with inward direction along the arrow
  - `tip`: The tip of the arrow, with inward direction opposite the arrow
"""
@compdef struct ArrowComponent <: AbstractComponent{typeof(1.0nm)}
    name = "arrow"
    length = 0.05mm
    width = 0.005mm
end

# Draw an arrow from left to right
function SchematicDrivenLayout._geometry!(cs::CoordinateSystem, ac::ArrowComponent)
    (; length, width) = parameters(ac)
    rect = centered(Rectangle(length, width))
    zer = zero(width)
    tip_edge = [Point(zer, zer), Point(3 * width, 3 * width), Point(zer, 6 * width)]
    tip = Polygon(vcat(tip_edge, reverse(tip_edge) .- Point(width, zer)))
    tip = Align.flushright(tip, rect, offset=width, centered=true)
    arrow = union2d(rect, tip)

    return place!(cs, arrow, :annotation)
end

# Define a hook at the base of the arrow, pointing in the same direction
# Define a hook at the tip of the arrow, pointing in the opposite direction
function SchematicDrivenLayout.hooks(ac::ArrowComponent)
    nockhook = PointHook(Point(-ac.length / 2, zero(ac.length)), 0°)
    tip = PointHook(Point(ac.length / 2, zero(ac.length)), 180°)
    return (nock=nockhook, tip=tip)
end

### Demo a component instance
arrow = ArrowComponent()
arrow_cs = geometry(arrow)

# Render to Cell for SVG export
target = ArtworkTarget(ProcessTechnology((; annotation=GDSMeta()), (;)))
arrowcell = Cell("arrow", nm)
render!(arrowcell, arrow_cs, target)
save("arrow_geom.svg", arrowcell, width=4inch, height=2inch);

Defining a Schematic

Now we'll make a Schematic that attaches two arrows at the base, rotating the second so that it points in the opposite direction:

g = SchematicGraph("double_arrow")
a1_node = add_node!(g, arrow)
fuse!(g, a1_node => :nock, arrow => :nock)
sch = plan(g; log_dir=nothing)
check!(sch)

cell = Cell("double_arrow", nm)
render!(cell, sch, target)
save("double_arrow.svg", flatten(cell), width=4inch, height=1inch);