# Transmission Lines from Nets

The Signal Integrity (SI) problem generally requires the construction of transmission lines. The net statement, typically used for constructing electrical net lists, lacks some nuance in this regard. With net connections, there is no ordering of the connection between pads. By the time we get to the board view tool, the user is on their own to get the transmission line right. Ordering of components, trace widths, and clearances become details to manage. Consistency is a goal that is difficult to achieve. Maintaining this kind of consistency is a challenge in many EDA tools.

JITX's solution to this problem revolves around defining additional constraints that layer on top of the `net` statement. These constraints typically come in two forms:

1. Medium Independent Constraints - ordering of pads (topologies), widths of traces, etc.
2. Medium Dependent Constraints - timing delays, insertion loss limits, etc.

The JITX's current implementation of SI constraints is limited but will expand over time.

## What is a Topology?

In JITX, a "Route Topology" (usually shorted to topology) is an ordered set of connections between component pads. You can think of the topology as a tree where the edges are the "Topology Segments" and the nodes are physical component pads. Each node can attach to 1 or more segments.

Note that we said ordered, but not directed. There is no connotation of input or output, transmitter or receiver, applied to the endpoints or nodes of the topology tree.

The "Route Topology" tree is acyclic by definition. If we have nodes A, B, C, and D, then we can construct a topology `A => B => D` or we can construct `A => C => D`. But we can't construct a topology that routes `A => D` by two parallel `B` and `C` routes in the same topology.

For example:

In other words, there can only be one path from `A` to `D`.

## Valid Topology

A topology can be consider valid or invalid. Sometimes it is not trivial to make this determination due to Pin Assignment.

Requirements

1. Each segment of a topology must have both:
1. A net statement.
2. A topology-segment statement.
2. Each node of a topology must associate with a physical pad.
1. This means a component port - not a module port.
3. Pass through components (like resistors or blocking capacitors) need a pin-model statement.

### Invalid Segments

A "Invalid Segment" refers to any segment of a topology that does not have both a `topology-segment` and `net` statement associated with it.

In this example, notice that the segment from `U1.TX` to `U2.RX` is a proper segment with a `topology-segment` and `net` statement. In short hand, we typically call this a `topo-net`.

The segment from `U1.TX` to `U3.RX` only has a `net` statement. This "appendage" net causes this topology to contain an invalid segment. When the routing engine encounters this topology, it can't properly define the constraints and labels it as invalid.

# Circuit Topology

In most real-world signal integrity circuits, we typically don't have just copper traces. There are typically various components like capacitors, ESD protection diodes, common-mode chokes, among others that are part of the complete signal topology.

In the diagram above, the small circles are the nodes of the topologies, and the blue dotted arrows represent the `topo-nets`.

Here we see some common component examples - termination resistors and AC blocking caps. The insertion of these components causes our topology to become disjointed. Let's say we wanted to apply a timing-difference constraint between the signals of the differential pair between `MCU` and `Display`. These disjointed topologies present a bit of a problem. For example, we might need a much more complicated constraint, like constraining the sum of the lengths of two pairs of topologies. But this would mean that compensating skew dynamically becomes basically impossible.

This is where the pin-model comes in. The `pin-model` creates the link between disjointed routing topologies that utilize "pass-through" components.

By adding a `pin-model` statement to the AC blocking capacitors, we collapse the 4 disjoint routing topologies into 2 circuit topologies. In this case, we've used the "Pass-Through" form of the pin model. This form provides a mechanism for defining how the signal flows from one node of a topology to another node of a different topology.

Notice that we don't have to add pin models on every component. As shown, the termination resistor doesn't have a `pin-model`. This often can be useful when we don't want to introduce cycles or multiple ambiguous paths in a topology.

With the models in place, there is a contiguous path from one endpoint `MCU.LVDS.CLK.P` to `Display.LVDS.CLK.P` (and similarly for the `N` side of the differential pair). Now we can add constraints between the two endpoints of these signals without issue.

## Endpoint Pin Models

In addition to the "Pass-Through" pin models for components like resistors and capacitors, each endpoint may provide a "Single-Ended" pin model. These models characterize the delay and loss from the silicon through the wire-bonding and out the package via its pins. In complex chips, this delay and loss may be different from one pin to another.

These models are not strictly necessary. If they are not provided, then the routing engine will just assume all pads have zero delay and loss.

To complete the circuit topology, we can include these pin models in our design:

These "Single-Ended" models are added with the pin-model statement. We add one model for each of the physical component pads. The routing engine can then use this information to correctly compute delay and loss constraints for the entire topology.

## Circuit Topology Realization

Realization is the process by which the abstract information of the circuit is formed into physical copper geometry in the board. This process has to take into account various clearance, width, and other requirements. It is where the rubber meets the road. The routing engine can either construct a circuit at this point - or it can't and fails.

In the context of the SI topology, realization is affected by the choice of nodes and segments. One way that it affects realization is by the means of "Stubs."

Consider this circuit - an excerpt of which is shown below:

``````pcb-module top-level :

inst A : resistor-model
inst B : resistor-model
inst C : resistor-model

net (A.p[1], B.p[1])
topology-segment(A.p[1], B.p[1])
structure(A.p[1] => B.p[1]) = single-ended

net (B.p[1], C.p[1])
topology-segment(B.p[1], C.p[1])
structure(B.p[1] => C.p[1]) = single-ended
``````

This is a simple topology consisting of two segments and three nodes.

Here is a reasonable expectation for how this might realize:

In this rendering:

• `A` => `U1`
• `B` => `U2`
• `C` => `U3`

I've placed `B` on the bottom layer and hence it requires a via.

The choice of how to place the via is where things get tricky. The order of operations matters.

### Order Matters

When we drag out the via for the connection, we can do so by dragging the via from `A.p[1]` or from `B.p[1]` (we could also do `C` but in this case, it is symmetric with `A`). In the following video, I drag the via from `B.p[1]`

In this case, the "Stub" is the copper from the via to `B.p[1]`. Because the via was dragged out from a pad on `B`, the via is associated with `B`. If we were to move the `B` component, the via would move with it. The routing engine considers the via an extension of the `B` component.

That is what allows us to select `A.p[1]`, the via, and `C.p[1]` and successfully route the signal topology. The via is effectively `B.p[1]`.

Now - what if dragged the via out from `A.p[1]` instead of `B.p[1]`:

On the surface, it would seem like these are exactly the same. But there is an important difference. Because the via was dragged out from `A.p[1]`, the via is now associated with the `A` component. If we moved `A`, the via would move with it. For the connection between `A.p[1]` and `B.p[1]`, this association has no effect. We can still connect `B.p[1]` to the via. But the "Stub" has moved - the stub is now `A.p[1]` to the via.

This presents problems for connecting to `C.p[1]`. Because the via is a stub for `A.p[1]`, we can't connect the via directly to `C.p[1]`. This would violate the constraints of the topology. The runtime prevents this connection because it is technically a circuit inconsistent with what is described in code. Hence the blue "There are no valid connections..." banner.

This phenomenon will apply equally to control points as well.

# Module Port Forwarding

In JITX we can use pcb-module definitions to create sub-circuits and reusable designs. We use the same `port` terminology for both `pcb-module` and `pcb-component` to describe the inputs and outputs to a circuit or component. But the ports of a `pcb-module` are different from the ports of a `pcb-component`.

The ports in a component map to a physical pad in a land pattern. This means that there is a physical manifestation of a component port in the PCB. The ports of a module don't necessarily have a `1:1` mapping from port to pad. This causes a bit of an issue when attempting to define a topology. If the module port maps to 3 physical pads, then should the topology create a branching tree? Should the topology connect to them serially? What do we do about amibiguities?

## Simple Topology

We're going to start off with a base case for demonstrating how topologies and modules work together. In this example we have two sub-circuits that we instantiate and connect together:

``````
pcb-routing-structure se-50:
...

pcb-module A:
port p : pin

inst R : resistor-model

net (p, R.p[1])
topology-segment(p, R.p[1])

pcb-module parent:

inst A1 : A
inst A2 : A

net (A1.p, A2.p)
topology-segment(A1.p, A2.p)

structure(A1.p, A2.p) = se-50
``````

Here is a visual representation of this topology:

There are technically 3 `topology-segment` statements in this design. But because `pcb-module` ports are different - we really only have one `topology-segment`. When a module port connects to one and only one component port, the module port "forwards" to the component port. In this example, the port `p` on module `A` forwards to `R.p[1]`.

This "forwarding" concept is is important because this allows us to use the `structure` statement at the top level to apply the single-ended 50 ohm trace to the entirety of the topology. If we did not have this forwarding mechanism, then we would have to pass the routing structure in to the sub-circuit like this:

``````; More complicated arguments to the module
pcb-module A (rte:RoutingStructure):
port p : pin

inst R : resistor-model

net (p, R.p[1])
topology-segment(p, R.p[1])
; Explicit structure statement inside
;  each module.
structure(p, R.p[1]) = rte

``````

This is not conducive to making re-usuable subcircuits. Having to pass in the routing structure means that it must propagate to any sub-circuits in the same way. The forwarding mechanism allows us to avoid this complexity.

## Bundles & More Components

Let's consider a more complex module example.

For this example, I'm going to transition to using JSL's SI helper routines for a more concise representation.

### Naive Approach

We're going to start with a straight forward approach where we just connect topologies in a logical way:

``````import jsl/si/helpers
import jsl/bundles

pcb-differential-routing-structure diff-100:
...

pcb-module B:
port D : diff-pair

; Termination Resistor
inst R : resistor-model

inst MCU : some-MCU-with-LVDS

topo-net(D.P => R.p[1] => MCU.LVDS.P)
topo-net(D.N => R.p[2] => MCU.LVDS.N)

pcb-module parent:

inst B1 : B
inst B2 : B

topo-net(B1.D => B2.D)
diff-structure(diff-100, B1.D => B2.D)

``````

Here is a visual representation of this topology:

First, notice that our topology inside the sub-module `B` consists of a chain of connections from the port `D` to the port `MCU.LVDS`. We've effectively added a termination resistors between `D` and `MCU.LVDS`. By adding this node in the topology, we still have a single `1:1` module port to component port mapping.

If we look at how this design realizes in our circuit - we'll see something unexpected:

Notice how the `diff-100` differential structure gets applied on the trace to the left of the resistor `R` but does not get applied to the traces from the `R` to the `MCU`. The issue is that the module port forwarding only extends to `R`. This means that when `diff-structure` is called, the port `B1.D.P` forwards to `R.p[1]` only and that becomes the endpoint of the constraint application.

### Constraining the entire topology

Let's try to address these issues with a slight refactor:

``````import jsl/si/helpers
import jsl/bundles

pcb-differential-routing-structure diff-100:
...

pcb-module B:
port D : diff-pair

; Termination Resistor
inst R : resistor-model

public inst MCU : some-MCU-with-LVDS

topo-net(D.P => R.p[1] => MCU.LVDS.P)
topo-net(D.N => R.p[2] => MCU.LVDS.N)

pcb-module parent:

inst B1 : B
inst B2 : B

topo-net(B1.D => B2.D)
diff-structure(diff-100, B1.MCU.LVDS => B2.MCU.LVDS)

``````

The topology representation hasn't changed, but we've modified the constraint application. In order to apply the differential routing structure `diff-100` to the segment from `R` to `MCU.LVDS`, I need to make the `MCU` instance `public`. Then from the `parent` context I can reference into `B1` and `B2` to set the true endpoints of the topology.

Now when we route the design we see:

This isn't optimal either. It requires the user of this module to understand the inner workings of the `B` module. But it serves to demonstrate one of the limitations of the module port forwarding mechanism. For a more robust solution to this problem, see Signal Ends.

## An Invalid Topology with a Module

In the previous example, we added components to the design while preserving the `1:1` mapping of the module port to a component port. Now let's see what happens when we violate this requirement:

``````pcb-module C:
pin p

inst R1 : resistor-model
inst R2 : resistor-model

; This creates a Y structure in the
;   topology graph.
topo-net(p => R1.p[1])
topo-net(p => R2.p[1])

pcb-module parent:

inst C1 : C
inst C2 : C

topo-net(C1.p => C2.p)
``````

Here is a visual representation of this invalid topology:

Notice how in this example, the port module `p` connects to one pin on each of `R1` and `R2`. This causes a bit of ambiguity with respect to how the port `p` maps to a physical component `pad`. In this simple example, it isn't as obvious to see why a Y junction can't be created in this case. But this does violate the `1:1` module port to component port assumption. That assumption makes the logic and realization of these topologies easier as the circuits become more complex.

## Fixing the Ambiguity

``````pcb-component test-point:
port tp : pin
...

pcb-module D:
port p : pin

inst TP : test-point

topo-net(p => TP.tp)

inst R1 : resistor-model
inst R2 : resistor-model

topo-net(TP.tp => R1.p[1])
topo-net(TP.tp => R2.p[1])

pcb-module parent:

inst D1 : D
inst D2 : D

topo-net(D1.p => D2.p)
``````

Here is a visual representation of this topology:

By inserting the test point component, we can control the `1:1` relationship and still achieve the `Y` structure for the topology.

Notice that this circuit has 7 `topology-segment()` statements but it has only 1 topology. That topology has 5 total segments:

Notice that the module ports don't exist in this topology. They effectively dissolve away as the topology is realized in the PCB.

# Mental Model Recap

The mental model for the SI topology system consists of two trees:

1. The Circuit Topology Tree
1. This is a tree consisting of many disjoint, ordered sub-graphs (Route Topologies)
2. The nodes of this graph are the component ports - not the module ports.
3. The edges of this graph are the `topo-nets`
2. The Realization Tree
1. This is the tree of traces, vias, and component pads that realizes one of the Route Topologies.
2. The nodes of this tree are: