SI Usage Patterns¶
The statements discussed in Topologies and Constraints are often considered the "primitives" of the JITX Signal Integrity (SI) system. These primitives must be strung together in a particular way to achieve the desired results. To make this process easier, we've developed various higher level constructs in the JITX Standard Library (JSL). This document will provide an overview of these constructs. Feel free to review the source code to understand more of the details.
topo-net
¶
Readers of the Topologies treatment will immediately notice the duplicative nature of the net
and topology-segment
statements. To remedy this and improve the utility of these statements, JSL provides the topo-net function. This function provides a more flexible method defining combination net
and topology-segment
statements.
inst MCU : MCU-with-USB
inst conn : USB-Connector
topo-net( MCU.usb.bus => conn.usb.bus )
In this simple example, we just connect two endpoints together. The endpoints can be any type of port, SinglePin
, Bundle
, or PortArray
. The only requirement is that all listed endpoints are of the same type. These endpoints can be module ports, component ports, or require ports.
We can also string together multiple components in sequence:
inst MCU : MCU-with-USB
inst ESD : TPD4E05
inst conn : USB-Connector
topo-net( MCU.usb.bus => ESD.D[0].A => ESD.D[0].B => conn.usb.bus )
Here the TPD4E05 is a pass-through style ESD protector.
topo-pair
¶
The topo-net
statement works well for any type of port, but with differential pairs, we often want to support more complex operations. In particular it is common to use AC blocking caps in a topology sequence. If we were to naively attempt this, it would be quite verbose:
inst MCU : MCU-with-USB
inst conn : USB-Connector
inst bcap : ceramic-cap(470.0e-9)[2]
topo-net(MCU.usb.bus.P => bcap[0].p[1])
topo-net(bcap[0].p[2], conn.usb.bus.P)
topo-net(MCU.usb.bus.N => bcap[1].p[1])
topo-net(bcap[1].p[2], conn.usb.bus.N)
We can do better if we leverage the topo-pair construct and some coupler modules like dp-coupler
inst MCU : MCU-with-USB
inst conn : USB-Connector
val block-cap = ceramic-cap(470.0e-9)
inst tx-bcap : dp-coupler(block-cap)
topo-pair(MCU.bus.lane.TX => tx-bcap => conn.bus.lane.TX)
Notice that this method allows us to construct the topology as a natural sequence of elements. This topo-pair
function can accept either endpoint ports like topo-net
or it can accept components like dp-coupler
that provide a dual-pair supports statement.
Signal Ends¶
Often times when constructing a module sub-circuit, the actual endpoint of a topology is buried inside that sub-circuit (or recursively within a sub-sub-circuit). This makes it difficult to apply the routing structure to the terminal endpoints. This manifests in a section of a trace or differential pair that lacks a routing structure assignment. For example, consider the following:
pcb-differential-routing-structure diff-100:
...
pcb-module MCU-with-Blocking-Caps:
port data : diff-pair
inst ctl : MCU-with-USB
val block-cap = ceramic-cap(470.0e-9)
inst tx-bcap : dp-coupler(block-cap)
; Connects to the output data pair for the module
topo-pair(ctl.bus.lane.TX => tx-bcap => data)
pcb-module top-level:
inst MCU : MCU-with-Blocking-Caps
inst conn : USB-Connector
val rte = MCU.data => conn.usb.lane.TX
topo-net(MCU.data => conn.usb.lane.TX)
diff-structure(diff-100, rte)
In this case, the differential routing structure diff-100
is applied to the topology from one side of tx-bcap
to the conn
component. It does not get applies to the topology segment from ctl
to the other side of tx-bcap
. This is analogous to the problem described here.
One method of working around this is to use the signal-ends approach defined in JSL.
pcb-differential-routing-structure diff-100:
...
pcb-module MCU-with-Blocking-Caps:
port data : diff-pair
inst ctl : MCU-with-USB
val block-cap = ceramic-cap(470.0e-9)
inst tx-bcap : dp-coupler(block-cap)
; Connects to the output data pair for the module
topo-pair(ctl.bus.lane.TX => tx-bcap => data)
set-signal-end(data, ctl.bus.lane.TX)
pcb-module top-level:
inst MCU : MCU-with-Blocking-Caps
inst conn : USB-Connector
val rte = MCU.data => conn.usb.lane.TX
topo-net(MCU.data => conn.usb.lane.TX)
val ep-rte = find-signal-end(MCU.data) => conn.usb.lane.TX
diff-structure(diff-100, ep-rte)
The idea is that the module author recognizes that this hidden topology endpoint problem exists and uses the set-signal-end
function to expose the true endpoint. This set-signal-end
approach may be recursive in the face of multiple layers of hierarchical sub-modules. The find-signal-end
function traverses these recursive definitions to find the final endpoint for application of the routing structure in a complete way.
SI-Constraints
¶
Find an expressive and modular way to create topologies and constraints is a difficult problem. The SI-Constraint framework is intended to provide a mechanism that provides both:
- Reusable constructs for common applications like differential pairs, lanes (TX/RX), etc.
- The flexibility to add or remove components from the topology easily.
Here is a taste - a more complete example will be coming soon:
val usb3-constraint = USB-Constraint(proto = USB3, route-struct = diff-90)
val lane-constraint = LaneConstraint(usb3-constraint)
within [src, dst] = constrain-topology(
usb3-mux.usb-c.lane[i] => usb-c.conn.bus.lane[i],
lane-constraint
):
; Receive
require prot-rx : dual-pair from ss-esd
topo-pair(src.RX => prot-rx => dst.RX)
; Transmit
inst tx-bcap : dp-coupler(block-cap)
require prot-tx : dual-pair from ss-esd
topo-pair(src.TX => tx-bcap => prot-tx => dst.TX)
The conceptual idea is that the constraint definition for many protocols is typically the same in every application. In PCIe, USB, HDMI, etc there is typically a sequence of structure
, timing-difference
, and insertion-loss
statements that are the same in almost every application. This framework encapsulates that concept in an interface type SI-Constraint
.
In the example above, both USB-Constraint
and LaneConstraint
are SI-Constraint
implementations. The USB-Constraint
is more concrete and encodes the expected constraints of a USB superspeed bus. The LaneConstraint
implementation is more abstract. A lane is a bundle consisting of a Transmit signal and a Receive signal. Commonly, these signals are defined as diff-pair
bundles. The lane constraint will apply the passed USB-Constraint
to both the Tx and Rx signals. This demonstrates the composability of the SI-Constraints
framework.
A within
clause is used to provide the user the opportunity to modify the topology before the constraints are applied. In this example, we insert blocking capacitors and/or ESD protection between the mux and the connector. The RX
part of the lane gets only an ESD protector while the TX
part of the lane gets both ESD protection and a blocking capacitor configuration.