pin_lane_polarity module#

Lane choice and P/N inversion on a provider that is one end of the path.

Use this pattern when a part may drive a differential link from any of several lanes, either way round, and an SI constraint covers the link. The provider is the driving part itself, so the constrained path begins on the required port. Four pieces compose:

  1. The provider. SerializerLanes offers one DiffPair with @provide.one_of: lane 0 or lane 1, each straight or inverted (OFFERED). one_of because the receiver takes a single lane. Offer inversion only where both ends of the link tolerate it, as the fictional parts here do.

  2. The consumer. LanePolarityLink requires the pair, keeps it in a local variable (a required port stored on self fails the build), and nets it with plain + nets through a common-mode choke to the connector.

  3. The pin model. The choke is a pass-through part, so each winding carries a BridgingPinModel; without one the path stops at the choke.

  4. The constraint. ConstrainDiffPair(Topology(lane, conn.rx)) begins on the required pair. Until routes resolve the assignment the report says Unassigned Pin on Path. Once they do, the measured line names the serializer pin the routes chose, inverted lanes included.

The shipping design, LanePolarityDesign, has no placement and no routes, so the assignment stays open for whoever routes the board. The verification designs place the parts and close the path with code Routes that end on the serializer’s physical pins (a route cannot end on a required port):

  • LaneZeroProof: lane 0, straight. Offered, so it measures.

  • LaneOneInvertedProof: lane 1, inverted (tx[1].n to connector rx.p). Offered, so it measures from tx[1].n.

  • SplitLanesIllegal: rx.p from lane 0, rx.n from lane 1. No offered mapping splits a lane, so a route is dropped.

Every part is fictional, not a real product. The choke’s 2 ps and 0.02 dB per winding, the 1 ns delay budget and the 5 ps skew limit are illustrative values; take real ones from the part’s datasheet and the protocol’s specification.

To check it, save this as a script in the project root and run it with the project’s runtime running:

import jitx

from jitxexamples.patterns.pin_lane_polarity import LaneOneInvertedProof


def main() -> None:
    with jitx.runtime as rt:
        report = rt.submit(LaneOneInvertedProof).issues(satisfied=True, wait=30)
        if not report.complete:
            print("report incomplete: raise wait and run again")
        for group in report.groups():
            print("met" if group.satisfied else "NOT MET", group.text)
            if len(group.issues) > 1:
                for issue in group:
                    print("   ", issue.text)


if __name__ == "__main__":
    main()

The real output for each design, on jitx 4.6.0rc10 against runtime 4.6.0-rc.16 (the shipping design and the three verification designs):

LanePolarityDesign:

NOT MET 2 issues: Unassigned Pin on Path: TX_P.p
    Unassigned Pin on Path: TX_P.p
    Unconnected Topology on Path: choke.a_out, conn.rx.p
NOT MET 2 issues: Unassigned Pin on Path: TX_P.n
    Unassigned Pin on Path: TX_P.n
    Unconnected Topology on Path: choke.b_out, conn.rx.n
NOT MET 4 issues: Unassigned Pin on Path: TX_P.p
    Unassigned Pin on Path: TX_P.p
    Unconnected Topology on Path: choke.a_out, conn.rx.p
    Unassigned Pin on Path: TX_P.n
    Unconnected Topology on Path: choke.b_out, conn.rx.n
met Satisfied

LaneZeroProof:

met Time: 70.937ps - Target: 500ps ± 500ps from dut.ser.serializer.tx[0].p to dut.conn.rx.p
met Time: 70.937ps - Target: 500ps ± 500ps from dut.ser.serializer.tx[0].n to dut.conn.rx.n
met Skew: 0s - Target: ± 5ps (longer from dut.ser.serializer.tx[0].p to dut.conn.rx.p, shorter from dut.ser.serializer.tx[0].n to dut.conn.rx.n)
met Satisfied

LaneOneInvertedProof:

met Time: 70.937ps - Target: 500ps ± 500ps from dut.ser.serializer.tx[1].n to dut.conn.rx.p
met Time: 81.924ps - Target: 500ps ± 500ps from dut.ser.serializer.tx[1].p to dut.conn.rx.n
NOT MET Skew: 10.987ps - Target: ± 5ps (longer from dut.ser.serializer.tx[1].p to dut.conn.rx.n, shorter from dut.ser.serializer.tx[1].n to dut.conn.rx.p)
met Satisfied

SplitLanesIllegal:

NOT MET Unassigned Pin on Path: dut.TX_P.p
NOT MET Unassigned Pin on Path: dut.TX_P.p
met Time: 70.144ps - Target: 500ps ± 500ps from dut.ser.serializer.tx[1].n to dut.conn.rx.n
NOT MET Code route dropped: it conflicts with other connections

Lane 1 inverted measures from tx[1].n: the routes resolved the inverted mapping. Its skew misses the window because its two routes cross on one layer and come out unequal: the constraint bound, and this routing fails it. A measurement means the constraint applied; whether it is met is group.satisfied. In the illegal design the dropped route leaves rx.p with no assignment, and rx.n still measures from the lane its route fits.

CHOKE_DELAY = 2e-12#

2 ps through each choke winding.

Type:

Illustrative

CHOKE_LOSS = 0.02#

0.02 dB through each choke winding.

Type:

Illustrative

1 ns from serializer to connector.

Type:

Illustrative

5 ps intra-pair skew.

Type:

Illustrative

Mapping#

A serializer lane and whether its P/N are inverted.

alias of tuple[int, bool]

OFFERED: tuple[tuple[int, bool], ...] = ((0, False), (0, True), (1, False), (1, True))#

either lane, either way round.

Type:

Every option the provider offers

class Serializer[source]#

Bases: Component

Fictional two-lane serializer. Either lane may carry the link, and its link partner corrects P/N polarity. Not a real part.

tx = (DiffPair(), DiffPair())#

Transmit lanes 0 and 1.

gnd = Port()#
class CommonModeChoke[source]#

Bases: Component

Fictional common-mode choke: winding A from a_in to a_out, winding B from b_in to b_out. Not a real part; its pin-model values are illustrative.

a_in = Port()#
b_in = Port()#
a_out = Port()#
b_out = Port()#
class LinkConnector[source]#

Bases: Component

Fictional one-lane connector. Not a real part.

rx = DiffPair()#
gnd = Port()#
class SerializerLanes[source]#

Bases: Circuit

The provider: the serializer, offering one lane either way round.

gnd = Port()#
lane = PrePostInit#

Bases: Circuit

Serializer lane to connector through the choke, constrained end to end. Unplaced and route-free, so the assignment stays open.

class LanePolarityDesign(*args, **kwargs)[source]#

Bases: SampleDesign

The shipping design: no placement, no routes.

circuit: Circuit = LanePolarityLink()#
class LanePolarityProof(p_lane, n_lane, inverted)[source]#

Bases: Circuit

LanePolarityLink placed, with code routes from the serializer to the choke: rx.p’s winding from lane p_lane, rx.n’s from lane n_lane, each lane’s legs crossed when inverted.

Parameters:
class LaneZeroProof[source]#

Bases: SampleDesign

Lane 0, straight: offered, so the constraint measures.

class LaneOneInvertedProof[source]#

Bases: SampleDesign

Lane 1, inverted: offered, so the constraint measures from tx[1].n.

class SplitLanesIllegal[source]#

Bases: SampleDesign

rx.p from lane 0 and rx.n from lane 1: not offered, so a route is dropped.