[add_bundle_from_corners][doroutes.add_bundle_from_corners] is the bundle (multi-wire) counterpart of
add_route_from_corners and add_route_from_steps. It performs a
fan-in at each end so the N wires converge onto a single bundle
line, then routes that bundle through user-specified corners or
steps.
Reach for it when:
- the floorplan dictates an exact route shape (a routing channel, fence regions, hand-drawn paths),
- the same routing recipe needs to apply to many similarly-shaped
layouts (use
steps=for placement-independent recipes), or - A* routing is overkill for a route you already know the shape of.
This is optical routing: bends are bend_euler at the PDK
minimum radius (5 µm for cspdk.si220.cband).
Imports
A 5-Port Test Frame
dr.pcells.fanout_frame2(transition="ew") puts 5 east-facing inputs
on the left edge and 5 west-facing outputs on the right edge of a
100 × 200 µm frame — the same layout used in the A* bundle routing
tutorial, so you can compare the algorithmic and corner-based
approaches side by side.
c = gf.Component()
ref = c << dr.pcells.fanout_frame2(transition="ew", add_frame=True)
c.add_ports(ref)
dr.util.show_cell(c)
Bundle Through Explicit Corners
Pass corners=[(x1, y1), (x2, y2), ...] to route the bundle through a
sequence of absolute coordinate points (in dbu — typically 1 nm). Two
corners give a Z-shaped detour: head east, turn south, head west, turn
back to the destination.
Each corner contributes one 90° turn in the bundle's path; consecutive segments must be axis-aligned (manhattan), so adjacent corners share either an x or y coordinate.
c = gf.Component()
ref = c << dr.pcells.fanout_frame2(transition="ew", add_frame=True)
c.add_ports(ref)
inv_dbu = dr.util.get_inv_dbu(c.kcl)
dr.add_bundle_from_corners(
component=c,
ports1=[p for p in c.ports if str(p.name).startswith("in")],
ports2=[p for p in c.ports if str(p.name).startswith("out")],
corners=[
(50 * inv_dbu, 30 * inv_dbu), # turn south at (50, 30) µm
(
80 * inv_dbu,
30 * inv_dbu,
), # turn east at (80, 30) µm — bundle now heads east
],
spacing=1.0,
bend={"component": "bend_euler", "settings": {"radius": 5}},
straight="straight",
)
dr.util.show_cell(c)
Same Route via Relative Steps
The same routing function accepts steps=[...] instead of corners=,
where each step describes a one-axis move from the previous corner.
The four supported step forms are:
| Key | Meaning |
|---|---|
dx |
move by Δx from the previous corner |
dy |
move by Δy from the previous corner |
x |
absolute x in dbu (or "inst,port" to anchor to a port) |
y |
absolute y in dbu (or "inst,port" to anchor to a port) |
The advantage of steps over corners: the recipe is independent of
absolute placement, so the same step list works no matter where the
parent component is instantiated.
c = gf.Component()
ref = c << dr.pcells.fanout_frame2(transition="ew", add_frame=True)
c.add_ports(ref)
inv_dbu = dr.util.get_inv_dbu(c.kcl)
# Same Z-shape detour, expressed as relative moves from the input centroid.
dr.add_bundle_from_corners(
component=c,
ports1=[p for p in c.ports if str(p.name).startswith("in")],
ports2=[p for p in c.ports if str(p.name).startswith("out")],
steps=[
{
"dx": 50 * inv_dbu,
"dy": -70 * inv_dbu,
}, # diagonal first step sets the fan-in anchor
{"dx": 30 * inv_dbu}, # head east 30 µm
],
spacing=1.0,
bend={"component": "bend_euler", "settings": {"radius": 5}},
straight="straight",
)
dr.util.show_cell(c)
Choosing Between corners and steps
Both modes call the same router under the hood; pick whichever feels natural for what you're trying to express:
corners— best when the route is anchored to a fixed floorplan (a routing channel, fence region, or specific coordinate constraint). You know the absolute (x, y) points the bundle has to pass through.steps— best when the route shape is what matters and you want it to survive re-placement (the same recipe applies to many similar layouts).
Internally the bundle routes through a manhattan fan-in by default
(add_fan_in(strategy="manhattan")). If you need a smoother fan-in
(s-bend or l-bend), use add_bundle_astar with a fanin_strategy=
kwarg, or compose add_fan_in + add_route_from_corners directly.
Next steps
- Don't want to spell the path out yourself? See A* Bundle Routing for the algorithmic alternative.
- For the fan-in stage in isolation (and how to choose between manhattan, s-bend, and l-bend), see Fan-In.
- For routing between arrayed instances (pad grids, probe cards), see Array Routing — it uses these same corner / step recipes on a real-world layout.