[examples] expand port_pather tutorial
This commit is contained in:
parent
395244ee83
commit
737d41d592
1 changed files with 123 additions and 38 deletions
|
|
@ -1,30 +1,16 @@
|
||||||
"""
|
"""
|
||||||
PortPather tutorial: Using .at() syntax for fluent port manipulation
|
PortPather tutorial: Using .at() syntax
|
||||||
"""
|
"""
|
||||||
from numpy import pi
|
from masque import RenderPather, Pattern, Port, R90
|
||||||
from masque import Pather, Library, Pattern, Port
|
|
||||||
from masque.builder.tools import AutoTool
|
|
||||||
from masque.file.gdsii import writefile
|
from masque.file.gdsii import writefile
|
||||||
|
|
||||||
from basic_shapes import GDS_OPTS
|
from basic_shapes import GDS_OPTS
|
||||||
# Reuse helper functions and constants from the basic pather tutorial
|
from pather import map_layer, prepare_tools
|
||||||
from pather import (
|
|
||||||
M1_WIDTH, V1_WIDTH, M2_WIDTH,
|
|
||||||
make_pad, make_via, make_bend, make_straight_wire, map_layer
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
# Reuse the same patterns (pads, bends, vias) and tools as in pather.py
|
# Reuse the same patterns (pads, bends, vias) and tools as in pather.py
|
||||||
library, M1_tool, M2_tool = prepare_tools()
|
library, M1_tool, M2_tool = prepare_tools()
|
||||||
library['pad'] = make_pad()
|
|
||||||
library['m1_bend'] = make_bend(layer='M1', ptype='m1wire', width=M1_WIDTH)
|
|
||||||
library['m2_bend'] = make_bend(layer='M2', ptype='m2wire', width=M2_WIDTH)
|
|
||||||
library['v1_via'] = make_via(
|
|
||||||
layer_top='M2', layer_via='V1', layer_bot='M1',
|
|
||||||
width_top=M2_WIDTH, width_via=V1_WIDTH, width_bot=M1_WIDTH,
|
|
||||||
ptype_bot='m1wire', ptype_top='m2wire',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create a RenderPather and place some initial pads (same as Pather tutorial)
|
# Create a RenderPather and place some initial pads (same as Pather tutorial)
|
||||||
rpather = RenderPather(library, tools=M2_tool)
|
rpather = RenderPather(library, tools=M2_tool)
|
||||||
|
|
@ -35,51 +21,150 @@ def main() -> None:
|
||||||
rpather.pattern.label(layer='M2', string='GND', offset=(18e3, 60e3))
|
rpather.pattern.label(layer='M2', string='GND', offset=(18e3, 60e3))
|
||||||
|
|
||||||
#
|
#
|
||||||
# Routing with .at()
|
# Routing with .at() chaining
|
||||||
#
|
#
|
||||||
# The .at(port_name) method returns a PortPather object which wraps the Pather
|
# The .at(port_name) method returns a PortPather object which wraps the Pather
|
||||||
# and remembers the selected port(s). This allows method chaining.
|
# and remembers the selected port(s). This allows method chaining.
|
||||||
|
|
||||||
# Then we can route just like in the other pather tutorials:
|
# Route VCC: 6um South, then West to x=0.
|
||||||
|
# (Note: since the port points North into the pad, path() moves South by default)
|
||||||
(rpather.at('VCC')
|
(rpather.at('VCC')
|
||||||
.path(ccw=False, length=6_000)
|
.path(ccw=False, length=6_000) # Move South, turn West (Clockwise)
|
||||||
.path_to(ccw=None, x=0)
|
.path_to(ccw=None, x=0) # Continue West to x=0
|
||||||
)
|
)
|
||||||
|
|
||||||
rpather.at('GND').path(0, 5_000).path_to(None, x=rpather['VCC'].x)
|
# Route GND: 5um South, then West to match VCC's x-coordinate.
|
||||||
|
rpather.at('GND').path(ccw=False, length=5_000).path_to(ccw=None, x=rpather['VCC'].x)
|
||||||
|
|
||||||
|
|
||||||
# We're using AutoTool so we could retool directly to M1_ptool like in the Pather
|
#
|
||||||
# tutorial, but let's manually plug to demonstrate what it looks like:
|
# Tool management and manual plugging
|
||||||
|
#
|
||||||
|
# We can use .retool() to change the tool for specific ports.
|
||||||
|
# We can also use .plug() directly on a PortPather.
|
||||||
|
|
||||||
|
# Manually add a via to GND and switch to M1_tool for subsequent segments
|
||||||
(rpather.at('GND')
|
(rpather.at('GND')
|
||||||
.plug('v1_via', 'top')
|
.plug('v1_via', 'top')
|
||||||
.retool(M1_tool) # this only retools the 'GND' port
|
.retool(M1_tool) # this only retools the 'GND' port
|
||||||
)
|
)
|
||||||
|
|
||||||
# We can also pass multiple ports to .at(), and then use .mpath() on them:
|
# We can also pass multiple ports to .at(), and then use .mpath() on them.
|
||||||
|
# Here we bundle them, turn South, and retool both to M1 (VCC gets an auto-via).
|
||||||
(rpather.at(['GND', 'VCC'])
|
(rpather.at(['GND', 'VCC'])
|
||||||
.mpath(ccw=True, xmax=-10_000, spacing=5_000)
|
.mpath(ccw=True, xmax=-10_000, spacing=5_000) # Move West to -10k, turn South
|
||||||
.retool(M1_tool) # this retools both ports
|
.retool(M1_tool) # Retools both GND and VCC
|
||||||
.mpath(ccw=True, emax=50_000, spacing=1_200) # ...causing an automatic via on VCC here
|
.mpath(ccw=True, emax=50_000, spacing=1_200) # Turn East, moves 50um extension
|
||||||
.mpath(ccw=False, emin=1_000, spacing=1_200)
|
.mpath(ccw=False, emin=1_000, spacing=1_200) # U-turn back South
|
||||||
.mpath(ccw=False, emin=2_000, spacing=4_500)
|
.mpath(ccw=False, emin=2_000, spacing=4_500) # U-turn back West
|
||||||
)
|
)
|
||||||
|
|
||||||
# Now we can finish up the equivalent to the other tutorials..
|
# Retool VCC back to M2 and move both to x=-28k
|
||||||
rpather.at('VCC').retool(M2_tool)
|
rpather.at('VCC').retool(M2_tool)
|
||||||
rpather.at(['GND', 'VCC']).mpath(None, xmin=-28_000)
|
rpather.at(['GND', 'VCC']).mpath(ccw=None, xmin=-28_000)
|
||||||
|
|
||||||
|
# Final segments to -50k
|
||||||
|
rpather.at('VCC').path_to(ccw=None, x=-50_000, out_ptype='m1wire')
|
||||||
|
with rpather.at('GND').toolctx(M2_tool):
|
||||||
|
rpather.at('GND').path_to(ccw=None, x=-40_000)
|
||||||
|
rpather.at('GND').path_to(ccw=None, x=-50_000)
|
||||||
|
|
||||||
rpather.at('VCC').path_to(None, x=-50_000, out_ptype='m1wire')
|
|
||||||
with pather.toolctx(M2_tool, keys=['GND']):
|
|
||||||
pather.at('GND').path_to(None, x=-40_000)
|
|
||||||
pather.at('GND').path_to(None, x=-50_000)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Save result
|
# Branching with save_copy and into_copy
|
||||||
#
|
#
|
||||||
library['PortPather_Tutorial'] = pather.pattern
|
# .save_copy(new_name) creates a port copy and keeps the original selected.
|
||||||
|
# .into_copy(new_name) creates a port copy and selects the new one.
|
||||||
|
|
||||||
|
# Create a tap on GND
|
||||||
|
(rpather.at('GND')
|
||||||
|
.path(ccw=None, length=5_000) # Move GND further West
|
||||||
|
.save_copy('GND_TAP') # Mark this location for a later branch
|
||||||
|
.pathS(length=10_000, jog=-10_000) # Continue GND with an S-bend
|
||||||
|
)
|
||||||
|
|
||||||
|
# Branch VCC and follow the new branch
|
||||||
|
(rpather.at('VCC')
|
||||||
|
.path(ccw=None, length=5_000)
|
||||||
|
.into_copy('VCC_BRANCH') # We are now manipulating 'VCC_BRANCH'
|
||||||
|
.path(ccw=True, length=5_000) # VCC_BRANCH turns South
|
||||||
|
)
|
||||||
|
# The original 'VCC' port remains at x=-55k, y=VCC.y
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Port set management: add, drop, rename, delete
|
||||||
|
#
|
||||||
|
|
||||||
|
# Route the GND_TAP we saved earlier.
|
||||||
|
(rpather.at('GND_TAP')
|
||||||
|
.retool(M1_tool)
|
||||||
|
.path(ccw=True, length=10_000) # Turn South
|
||||||
|
.rename_to('GND_FEED') # Give it a more descriptive name
|
||||||
|
.retool(M1_tool) # Re-apply tool to the new name
|
||||||
|
)
|
||||||
|
|
||||||
|
# We can manage the active set of ports in a PortPather
|
||||||
|
pp = rpather.at(['VCC_BRANCH', 'GND_FEED'])
|
||||||
|
pp.add_port('GND') # Now tracking 3 ports
|
||||||
|
pp.drop_port('VCC_BRANCH') # Now tracking 2 ports: GND_FEED, GND
|
||||||
|
pp.path_each(ccw=None, length=5_000) # Move both 5um forward (length > transition size)
|
||||||
|
|
||||||
|
# We can also delete ports from the pather entirely
|
||||||
|
rpather.at('VCC').delete() # VCC is gone (we have VCC_BRANCH instead)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Advanced Connections: path_into and path_from
|
||||||
|
#
|
||||||
|
|
||||||
|
# path_into routes FROM the selected port TO a target port.
|
||||||
|
# path_from routes TO the selected port FROM a source port.
|
||||||
|
|
||||||
|
# Create a destination component
|
||||||
|
dest_ports = {
|
||||||
|
'in_A': Port((0, 0), rotation=R90, ptype='m2wire'),
|
||||||
|
'in_B': Port((5_000, 0), rotation=R90, ptype='m2wire')
|
||||||
|
}
|
||||||
|
library['dest'] = Pattern(ports=dest_ports)
|
||||||
|
# Place dest so that its ports are to the West and South of our current wires.
|
||||||
|
# Rotating by pi/2 makes the ports face West (pointing East).
|
||||||
|
rpather.place('dest', offset=(-100_000, -100_000), rotation=R90, port_map={'in_A': 'DEST_A', 'in_B': 'DEST_B'})
|
||||||
|
|
||||||
|
# Connect GND_FEED to DEST_A
|
||||||
|
# Since GND_FEED is moving South and DEST_A faces West, a single bend will suffice.
|
||||||
|
rpather.at('GND_FEED').path_into('DEST_A')
|
||||||
|
|
||||||
|
# Connect VCC_BRANCH to DEST_B using path_from
|
||||||
|
rpather.at('DEST_B').path_from('VCC_BRANCH')
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Direct Port Transformations and Metadata
|
||||||
|
#
|
||||||
|
(rpather.at('GND')
|
||||||
|
.set_ptype('m1wire') # Change metadata
|
||||||
|
.translate((1000, 0)) # Shift the port 1um East
|
||||||
|
.rotate(R90 / 2) # Rotate it 45 degrees
|
||||||
|
.set_rotation(R90) # Force it to face West
|
||||||
|
)
|
||||||
|
|
||||||
|
# Demonstrate .plugged() to acknowledge a manual connection
|
||||||
|
# (Normally used when you place components so their ports perfectly overlap)
|
||||||
|
rpather.add_port_pair(offset=(0, 0), names=('TMP1', 'TMP2'))
|
||||||
|
rpather.at('TMP1').plugged('TMP2') # Removes both ports
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Rendering and Saving
|
||||||
|
#
|
||||||
|
# Since we used RenderPather, we must call .render() to generate the geometry.
|
||||||
|
rpather.render()
|
||||||
|
|
||||||
|
library['PortPather_Tutorial'] = rpather.pattern
|
||||||
library.map_layers(map_layer)
|
library.map_layers(map_layer)
|
||||||
writefile(library, 'port_pather.gds', **GDS_OPTS)
|
writefile(library, 'port_pather.gds', **GDS_OPTS)
|
||||||
|
print("Tutorial complete. Output written to port_pather.gds")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue