lpddr4 module#

LPDDR4#

Port bundles and a signal-integrity constraint for a point-to-point LPDDR4 link between a controller and one memory device.

Source and scope#

The defaults in LPDDR4ConstraintParams are Table 2, “LPDDR4 without ECC Skew Constraints”, in AMD UG583, UltraScale Architecture PCB Design User Guide, section “LPDDR4 without ECC Length and Skew Constraints”, https://docs.amd.com/r/en-US/ug583-ultrascale-pcb-design/LPDDR4-without-ECC-Length-and-Skew-Constraints. That table is written for the Zynq UltraScale+ MPSoC processing system (PS) driving one LPDDR4 device without ECC; for any other controller, check its own layout guide.

  • CK_t to CK_c: 2 ps (applied as +/-2 ps)

  • DQS_t to DQS_c: 2 ps (applied as +/-2 ps)

  • Address/command/control to CK: +/-8 ps (applied to CKE, CS and CA0 to CA5)

  • DQ and DM to DQS: +/-5 ps (applied to DQ and DMI)

  • CK to DQS: -500 ps to +2500 ps, as CK delay minus DQS delay. Table 2’s note sends skew to items 3 to 8 of UG583’s General Memory Routing Guidelines, whose CK-to-DQS example subtracts the DQS delay from the clock delay. On jitx 4.6, ConstrainReferenceDifference(guide, target) measures guide delay minus target delay (checked on a code-routed pair), so CK is the guide.

  • DQ and DM, slowest to fastest: 5 ps. The constraint holds each DQ within +/-2.5 ps of DMI, which keeps any two of them within 5 ps; it is stricter than the rule for DMI against a DQ, since UG583 allows DMI anywhere inside the 5 ps spread.

Table 2’s note also says: “FPGA package flight times must be included in both total length constraints and skew constraints … Memory device package flight times do not need to be factored in.” The design-issue report counts a package’s delay only where its pins carry a TerminatingPinModel, so give the controller’s pins one from its package delay data (UG583 asks for the midrange where a minimum and maximum are given), and leave the memory’s pins without one. The controller in lpddr4_example is fictional and has none.

UG583 also sets maximum lengths (its Table 1), which this module does not apply, and excludes reset_n from the skew rules. It gives no insertion-loss limit and no impedance target, so the module sets neither by default: pass loss in the parameters to add a loss limit, and pass the routing structures from the board’s substrate.

Use#

Net the controller’s bundle to the memory’s bundle with an ordinary net and apply the constraint to the same two bundles:

self.link = self.controller.lpddr4 + self.memory.lpddr4
self.si = LPDDR4Constraint(diff_structure=..., se_structure=...)
self.si.constrain(self.controller.lpddr4, self.memory.lpddr4)

A controller that drives one CKE per channel for both ranks of an x32 dual-rank memory (the dual-rank VMK180 and VPK120 boards map their LPDDR4 interfaces this way) offers LPDDR4SharedCKE instead. Net its one CKE to both of the memory channel’s CKE balls and pass it as the controller side: LPDDR4Constraint.constrain() holds each of those balls against CK. Set spread_dq to None for a layout guide with no DQ spread rule.

The issue report measures a constraint only once the path between its two ends is routed. lpddr4_example is not routed.

class LPDDR4Width(*values)[source]#

Bases: Enum

LPDDR4 interface width: the number of x16 channels times 16.

x16 = 16#

One x16 channel

x32 = 32#

Two x16 channels

x64 = 64#

Four x16 channels

num_x16_channels(width)[source]#

Return the number of x16 channels in an interface of this width.

Return type:

int

Parameters:

width (LPDDR4Width)

class LPDDR4Rank(*values)[source]#

Bases: Enum

LPDDR4 rank count: the number of CKE and CS signals per channel.

Rank1 = 1#

Single rank

Rank2 = 2#

Dual rank

class LPDDR4Lane[source]#

Bases: Port

One LPDDR4 byte lane: eight DQ, one DQS pair and one DMI.

dq = [Port(), Port(), Port(), Port(), Port(), Port(), Port(), Port()]#

Data bits

dqs = DiffPair()#

Data strobe (p is DQS_t, n is DQS_c)

dmi = Port()#

Data mask and inversion

class LPDDR4Channel(rank)[source]#

Bases: Port

One LPDDR4 x16 channel: two byte lanes, the clock, and command/address.

Parameters:

rank (LPDDR4Rank) – Rank count, which sets the number of CKE and CS signals.

ck = DiffPair()#

Clock (p is CK_t, n is CK_c)

ca = [Port(), Port(), Port(), Port(), Port(), Port()]#

Command/address bits CA0 to CA5

d: Sequence[LPDDR4Lane]#

Byte lanes 0 and 1

cke: Sequence[Port]#

Clock enable, one per rank

cs: Sequence[Port]#

Chip select, one per rank

class LPDDR4(width, rank)[source]#

Bases: Port

LPDDR4 interface: one or more x16 channels.

Parameters:
ch: Sequence[LPDDR4Channel]#

x16 channels

class LPDDR4SharedCKEChannel[source]#

Bases: Port

One x16 channel of a dual-rank controller that drives one CKE for both ranks.

d = (LPDDR4Lane(), LPDDR4Lane())#

Byte lanes 0 and 1

ck = DiffPair()#

Clock (p is CK_t, n is CK_c)

cke = Port()#

Clock enable, shared by both ranks

cs = (Port(), Port())#

Chip select, one per rank

ca = [Port(), Port(), Port(), Port(), Port(), Port()]#

Command/address bits CA0 to CA5

class LPDDR4SharedCKE[source]#

Bases: Port

An x32 dual-rank controller interface with one CKE per channel.

The controller side of a link to an x32 dual-rank LPDDR4 memory. A pin assignment option maps one controller pin to one port, so a shared CKE needs its own port rather than two ports of LPDDR4.

ch = (LPDDR4SharedCKEChannel(), LPDDR4SharedCKEChannel())#

Channels A and B

signal_ports(bus)[source]#

Return every single-pin port of bus in a fixed order.

Per channel: CK p and n, the CKE signals, the CS signals, CA0 to CA5, then per byte lane DQ0 to DQ7, DQS p and n, and DMI. Two bundles of the same width and rank give lists that line up signal for signal. The example controller assigns its balls in this order.

Return type:

list[Port]

Parameters:

bus (LPDDR4)

class LPDDR4ConstraintParams(skew_ck=Toleranced(0, 2e-12, 2e-12), skew_ck_cke=Toleranced(0, 8e-12, 8e-12), skew_ck_cs=Toleranced(0, 8e-12, 8e-12), skew_ck_ca=Toleranced(0, 8e-12, 8e-12), skew_ck_dqs=Toleranced(1e-09, 1.5e-09, 1.5e-09), skew_dqs=Toleranced(0, 2e-12, 2e-12), skew_dqs_dq=Toleranced(0, 5e-12, 5e-12), spread_dq=5e-12, loss=None)[source]#

Bases: object

LPDDR4 timing limits. Defaults are AMD UG583 Table 2.

To change one value, use dataclasses.replace() on a default instance.

Parameters:
skew_ck: Toleranced = Toleranced(0, 2e-12, 2e-12)#

CK intra-pair skew

skew_ck_cke: Toleranced = Toleranced(0, 8e-12, 8e-12)#

CKE to CK skew

skew_ck_cs: Toleranced = Toleranced(0, 8e-12, 8e-12)#

CS to CK skew

skew_ck_ca: Toleranced = Toleranced(0, 8e-12, 8e-12)#

CA to CK skew

skew_ck_dqs: Toleranced = Toleranced(1e-09, 1.5e-09, 1.5e-09)#

CK.p delay minus DQS.p delay

skew_dqs: Toleranced = Toleranced(0, 2e-12, 2e-12)#

DQS intra-pair skew

skew_dqs_dq: Toleranced = Toleranced(0, 5e-12, 5e-12)#

DQ and DMI to DQS skew

spread_dq: float | None = 5e-12#

Largest delay difference, slowest to fastest, among the DQ and DMI of a byte lane, in seconds, or None for no spread rule. Applied as +/- half of it around DMI.

loss: float | None = None#

Maximum insertion loss in dB for every signal, or None for no limit

class LPDDR4Constraint(diff_structure, se_structure, params=None)[source]#

Bases: SignalConstraint[LPDDR4]

Signal-integrity constraint for an LPDDR4 link.

For each channel, constrain() applies:

  • CK: intra-pair skew, on the differential structure.

  • CKE, CS and CA: skew against CK.p, on the single-ended structure. A LPDDR4SharedCKE controller’s one CKE is held against CK.p to each of the memory channel’s CKE balls.

  • Per byte lane, DQS: intra-pair skew, and the CK.p minus DQS.p window.

  • Per byte lane, DQ and DMI: skew against DQS.p, on the single-ended structure.

  • Per byte lane, unless spread_dq is None, DQ spread: each DQ within half of spread_dq of DMI, so any two of them differ by at most spread_dq. ConstrainReferenceDifference compares each signal with one guide, which is why a pairwise limit is expressed this way.

  • With params.loss set, that insertion-loss limit on every signal.

The guides are single pins (CK.p, DQS.p) because ConstrainReferenceDifference accepts only a single-pin guide. UG583 measures against a differential pair’s mid-point, so the p leg can sit off that reference by up to half the pair’s intra-pair skew.

Parameters:
constrain(src, dst)[source]#

Apply the LPDDR4 constraints between two netted bundles.

Parameters:
Raises:
  • TypeError – If dst is not an LPDDR4, such as the controller and memory sides passed in the wrong order.

  • ValueError – If the bundles differ in width or rank.

Return type:

None