ordec.core.arrange — Arrangement groups

Arrangement groups arrange schematic elements (instances, ports, nested groups) relative to each other without explicit coordinates. A group records its children in declaration order and emits constraints for the view’s Solver during postprocessing, so children of different sizes are placed without user intervention.

Row and Col place their children geometrically. Series and Parallel additionally connect them electrically, so circuit structures can be described by nesting alone. Groups are context managers:

with Col(gap=2):
    Pmos pu
    Nmos pd

with Col(gap=2) as stack: names the group, e.g. for use in constraints. Group blocks are naming-transparent: children declared inside the body are added to the view root as usual and only additionally recorded in the group.

Example (ORD):

viewgen schematic -> Schematic:
    port a : .align=East
    port y : .align=West
    net vss  # forward declaration, port statement follows in the stack

    with Series(gap=4):
        port vdd: .align=South
        Pmos pu:
            .g -- a
            .b -- vdd
            .d -- y
        Nmos pd:
            .g -- a
            .b -- vss
        port vss: .align=North

Choosing a group

Row and Col are purely geometric: they place their children and leave all wiring to explicit -- connections. Series and Parallel additionally connect their children electrically, modeling a current path:

Group

Placement

Electrical connection

Row

side by side, left to right

none

Col

stacked, top to bottom

none

Series

along the current path

each pair of neighbors, pin to pin

Parallel

across the current path

all children between two rail nets

Use Series/Parallel when the placement follows the circuit topology and let the connections fall out of the structure. Fall back to Col/Row with explicit wiring when the structure is irregular (heterogeneous pin selections, connections that do not follow the stack).

Attributes

All groups:

gap

Distance between adjacent children along the main axis. The default (2) leaves room for auto_wire() routing. Tighter gaps can exceed the router’s capacity.

align

Cross-axis placement policy for children of different sizes:

  • 'center' (default for Row, Col and Parallel) centers the children’s bounding boxes, snapped down to the unit grid so that on-grid pins stay routable.

  • 'start' / 'end' align the bottom/left or top/right edges.

  • 'pins' (Series only, its default) shifts each child so that each pair’s facing pins line up, giving straight junction wires. Where a child has no single junction point (a nested Parallel rail), its center is used.

With children of equal size, all policies coincide.

anchor

Position of the group’s southwest corner. The default 'auto' anchors a top-level group at (0, 0). Several auto-anchored groups in one view line up side by side. A group with a member that appears in a user constraint or has a directly assigned position follows that member instead. Pass an (x, y) tuple to anchor explicitly, or None to never emit an anchor.

Series and Parallel only:

horizontal

Direction of the current path through each child: vertical (False, the default) connects through downward-/upward-facing pins, horizontal (True) through right-/left-facing pins. The placement axis follows from it: Series places along the path, Parallel across it, so a vertical Series stacks like Col while a vertical Parallel places side by side like Row. The group never rotates children. Orient instances so that their current-carrying pins face along the path.

top, bottom (vertical), left, right (horizontal)

Pin name overrides for pin detection: when an instance child has several pins on a facing side, these select the connecting pin, uniformly for all instance children.

Connectivity

Series connects the pin of each child facing the next child to the next child’s pin facing back. Port children connect through their net. Parallel ties all pins facing one way to one rail net and all pins facing the other way to a second rail net. It does not accept port children (wire the rail net to the port explicitly).

A junction or rail net is adopted from whichever involved pin is already connected, so wiring one pin explicitly (.d -- y) names the net. Without any, an anonymous net is created. Pins that are already on two different nets are a connection conflict and raise an error.

Since a port’s statement position inside a group determines its place in the stack, a port that earlier statements reference can be forward-declared with net (see the example above): the later port statement attaches the symbol pin to the declared net.

Nesting

Any group nests in any group geometrically. The nested group is placed as a rigid block. Series and Parallel additionally connect nested Series/Parallel children through their boundary: a nested Series exposes the outward-facing pins of its first and last child, a nested Parallel exposes its two rails. Series/parallel circuit structures can therefore be described by nesting alone, e.g. a NAND as a Series of vdd, a Parallel pull-up pair, the pull-down transistors and vss. Tutorial section 4.9 Arrangement groups shows this NAND as a complete example with its rendered schematic.

Two restrictions apply: a Series/Parallel cannot electrically connect a nested Col/Row child (nest Series/Parallel instead, or wire explicitly), and a nested connecting group must share the enclosing group’s orientation (a horizontal Parallel has no upward/downward rails to connect in a vertical Series).

Placement precedence

Positions of schematic elements are determined in this order:

  1. Directly assigned positions (.pos = (x, y)) and explicit constraints (! ...) always win. A port or instance referenced in any constraint is not placed automatically.

  2. Members of an arrangement group are placed by the group. Top-level groups anchor at (0, 0). Several auto-anchored groups line up side by side. A group with a constrained or directly positioned member follows that member instead of being anchored.

  3. Remaining ports are auto-placed on the edge of the content bounding box based on their align (see schem_place_ports() in ordec.schematic.helpers): the align is the direction the port arrow points, into the drawing. A port with align=East is placed on the left edge, West on the right, North on the bottom and South on the top edge. Along the edge, the port lines up with the connected pin nearest to its edge, so that the wire towards it can run straight.

  4. Instances that end up with no position at all raise a ordec.core.constraints.SolverError.

Classes

Groups are configured entirely through their constructor arguments, described in the class docstrings and under Attributes above. Their methods are internal machinery (see below).

class ordec.core.arrange.ArrangementGroup(gap: int = 2, align: str | None = None, anchor='auto')

Base class of all arrangement groups, see module docstring. Use Row or Col (or Series/Parallel for electrical connection).

Parameters:
  • gap – Distance between adjacent children along the main axis. The default leaves room for auto_wire() routing.

  • align – Cross-axis alignment of the children: ‘center’ (snapped down to the unit grid), ‘start’ (bottom/left edge) or ‘end’ (top/right edge). None picks the group’s default_align.

  • anchor – Position of the group’s southwest corner: an (x, y) tuple, None to never emit an anchor, or ‘auto’ (default) to anchor a top-level group at (0, 0) unless a member appears in a user constraint or has a directly assigned position.

class ordec.core.arrange.Row(gap: int = 2, align: str | None = None, anchor='auto')

Bases: ArrangementGroup

Places children side by side, left to right along the x axis.

class ordec.core.arrange.Col(gap: int = 2, align: str | None = None, anchor='auto')

Bases: ArrangementGroup

Places children in a stack, top to bottom along the y axis.

class ordec.core.arrange.ConnectingGroup(gap: int = 2, align: str | None = None, anchor='auto', horizontal: bool = False, top: str | None = None, bottom: str | None = None, left: str | None = None, right: str | None = None)

Bases: ArrangementGroup

Common base of the electrically connecting groups Series and Parallel: pin detection by facing side and shared net handling.

Pin detection relies on the symbol convention that the pins carrying the current path face along its direction. It must be unambiguous: with several pins on a facing side, the top=/bottom= (vertical) or left=/right= (horizontal) pin names select the pin, uniformly for all instance children. horizontal chooses the orientation of the current path. The placement axis follows from it and flow_along_axis. gap, align and anchor are inherited from ArrangementGroup.

class ordec.core.arrange.Series(gap: int = 2, align: str | None = None, anchor='auto', horizontal: bool = False, top: str | None = None, bottom: str | None = None, left: str | None = None, right: str | None = None)

Bases: ConnectingGroup

Places children in a stack like Col (side by side like Row with horizontal=True) and connects them in series, modeling a current path: the pin of each child facing the next child is connected to the next child’s pin facing back. Port children connect through their net. A junction’s net is adopted from whichever facing pin is already connected, so it can be named by wiring one pin explicitly (e.g. .d -- y). Otherwise an anonymous net is created.

The default alignment ‘pins’ aligns each pair’s facing pins on one line, giving straight junction wires. Pass align=’center’ to center the children’s bounding boxes instead.

class ordec.core.arrange.Parallel(gap: int = 2, align: str | None = None, anchor='auto', horizontal: bool = False, top: str | None = None, bottom: str | None = None, left: str | None = None, right: str | None = None)

Bases: ConnectingGroup

Places children side by side like Row (stacked like Col with horizontal=True) and connects them in parallel: all upward-facing pins are tied to one rail net and all downward-facing pins to another (left-/right-facing with horizontal=True). To name a rail net, wire one child pin explicitly (e.g. .d -- y). Port children are not supported. Wire ports to the rail nets explicitly.

Internals

While the viewgen body runs, a group only records its children in declaration order. The view context emits all top-level groups during postprocessing: ArrangementGroup.emit() resolves connectivity (parent before child, since ORDB cannot merge nets), computes the rigid relative arrangement() of the children and constrains each child’s position to the first child by a constant offset, anchoring top-level groups. User code only calls these methods when using groups outside a viewgen.

class ordec.core.arrange.Endpoint(net: Net = None, attach: Callable = None)

Connectable boundary of an arrangement group child: an instance pin, a port’s net or a nested group’s boundary/rail.

net: Net = None

Net the endpoint is already connected to, or None.

attach: Callable = None

Callable that connects the endpoint to a given net, or None for endpoints that are themselves nets (ports).

connect(net: Net)

Connects the endpoint to net. Raises ValueError on a conflict.

class ordec.core.arrange.ArrangedRects(rects: list, offsets: list, width: float, height: float)

Rigid relative layout of a group’s children, see ArrangementGroup.arrangement().

ArrangementGroup.add(child)

Appends a child (an instance, a port/net or a nested group) and returns it. Raises TypeError if the group is sealed because its outline was already used (see rect()), and ValueError if the child was already added.

ArrangementGroup.child_rect(child) Rect4LinearTerm

Returns a child’s bounding box in view coordinates. A net is placed through its port. Ports are zero-size points at their position.

ArrangementGroup.arrangement() ArrangedRects

Computes the relative arrangement of the children. Since child sizes are constants, the arrangement is rigid. Center alignment is snapped down to the unit grid so that on-grid children stay on grid regardless of size differences.

ArrangementGroup.rect() Rect4LinearTerm

Returns the group’s bounding box, anchored on the first child. Reading it seals the group against further children (terms derived from it would not follow).

ArrangementGroup.resolve_connectivity()

Establishes the group’s electrical connections, a no-op for the purely geometric groups. emit() resolves a group’s connectivity before its children’s: a nested group receives its boundary nets from the enclosing group here and ties its internals to them when its own turn comes (ORDB cannot merge nets afterwards).

ArrangementGroup.emit(solver: Solver, toplevel: bool = True, auto_anchor: tuple = (0, 0)) bool

Resolves the group’s connectivity and emits its placement constraints into solver. Called by the view context during postprocessing. Call manually when using groups outside a viewgen. Nested groups are emitted with toplevel=False and never anchored. auto_anchor is the position used for anchor=’auto’. Returns True if the automatic anchor was applied.

ConnectingGroup.facing_pin(inst, side: D4) str

Returns the name of the single pin of an instance child that faces side, honoring the group’s pin name overrides. Raises ValueError if no unambiguous, plainly named pin is found.

ConnectingGroup.endpoint(child, side: D4) Endpoint

Returns the Endpoint of a direct child on the given side.

abstractmethod ConnectingGroup.side_endpoint(side: D4) Endpoint

Returns the boundary Endpoint offered to an enclosing connecting group.