pixalynx/usb3-hub-10g
A USB 3.2 Gen 2 six-port hub board with USB-C PD power input, regulated 5 V/3.3 V/1.15 V rails, one upstream and four protected downstream USB-A/USB-C ports, SuperSpeed muxes, ESD protection, and controlled port power.
- Version
- 0.0.1
- License
- unset
- Stars
- 0
blocks/TypeC.tsx
import { pcb } from "../lib/place"
import { Fragment } from "react"
import { UsbC24, USBC24_GND, USBC24_VBUS } from "../parts/Connectors"
import { Hd3ss3220 } from "../parts/Hd3ss3220"
import { Pusb3fr4, Usblc6, Tpd1e10b06 } from "../parts/Protection"
import { PortSwitch, Nmos } from "../parts/Power"
import { R, C, Led } from "../lib/passives"
import { J, S, onSheet } from "../lib/layout"
import { Note, Frame } from "../lib/sch"
/** Schematic positions relative to the block origin. UFP (upstream, J1 on the left): J1 at the origin, ESD, then the
* HD3SS3220 facing the hub. DFP (downstream): the HD3SS3220 at the origin facing the hub on its left, ESD, then the
* receptacle on the right; the port-power switch and its enable transistor below. */
const UFP = {
J: [0, 0], U: [11.8, 0.2], D1: [5.6, 2.4], D2: [5.6, -0.4], D3: [5.6, -2.7], CC1: [7.8, -3.6], CC2: [10.0, -3.6],
row: -4.0, rowX: [11.4, 12.7, 14.0, 15.3, 16.6], cap: [0.6, -5.4], frame: [-1.2, -6.9, 17.8, 4.0],
} as const
const DFP = {
J: [14.4, 0], U: [0, 0], D1: [5.0, 1.3], D2: [5.0, -1.5], D3: [9.4, 1.4], CC1: [7.3, -3.2], CC2: [9.5, -3.2],
row: -4.2, rowX: [-3.9, -2.6, -1.3, 0, 1.3, 2.6, 3.9, 5.2], sw: [10.0, -5.8], q: [5.5, -6.0], row2: -8.8, row2X: [5.4, 6.7, 8.0, 9.3, 10.6],
frame: [-5.2, -11.2, 16.8, 3.9],
} as const
/** One USB-C receptacle with its HD3SS3220 (CC logic + 10 Gbps orientation mux) and ESD.
*
* - `ufp` (the host port, J1/U2): PORT low, internal dead-battery Rd keeps the host's VBUS on while the hub is
* unpowered or ENn_CC is high (SLLSES1E 6.4.3/6.4.4), VBUS_DET through 887k from VBUS_HOST.
* - `dfp` (downstream C ports): PORT high, CURRENT_MODE 499k to VDD5 = 1.5 A Rp. VBUS comes from an AP22652
* (active-low EN) whose EN_N is pulled low through a 2N7002 only when the hub enables the port (PRT_CTLn high,
* gate) AND the HD3SS3220 reports an attach (ID low, source). ID only asserts once VBUS is at vSafe0V (6.3.1
* note), and the switch discharges VBUS when disabled. FAULT_N goes straight to PRT_CTLn so the hub still sees
* over-current (DS00003850F 8.2.1); the gate load on PRT_CTLn is DC-free, so an idle port never reads as a fault.
*
* Nets: connector side P_TX1P...; hub side <hub>_TXP/TXN (after the AC caps in blocks/Hub.tsx) and <hub>_RXP/RXN. */
type Props = { role: "ufp" | "dfp"; n: 0 | 3 | 4 }
export const TypeCPort = ({ role, n }: Props) => {
const P = role === "ufp" ? "UPC" : `C${n - 2}` // net prefix (UPC, C1, C2)
const hub = role === "ufp" ? "UP" : `DN${n}` // hub port prefix
const jn = role === "ufp" ? "J1" : `J${n + 2}` // J1, J5, J6
const un = role === "ufp" ? "U2" : `U${n}` // U2, U3, U4
const k = role === "ufp" ? 200 : 300 + (n - 3) * 50 // designator base
const jp = role === "ufp" ? J.HOST : n === 3 ? J.C1 : J.C2
const [sx, sy] = role === "ufp" ? S.UP : n === 3 ? S.C1 : S.C2
const sec = role === "ufp" ? "UP" : `C${n - 2}`
const vbus = role === "ufp" ? "VBUS_HOST" : `VBUS_${P}`
const f = jp.rot === 180 ? -1 : 1 // parts step away from the edge into the board
const row = (i: number) => s(L.rowX[i], L.row)
const row2 = (i: number) => s(DFP.row2X[i], DFP.row2)
const at = (dx: number, dy: number) => ({ x: jp.x + dx, y: jp.y + f * dy })
const s = (dx: number, dy: number) => ({ sx: sx + dx, sy: sy + dy, sec })
const sa = (pt: readonly [number, number]) => s(pt[0], pt[1])
const xy = (pt: readonly [number, number]) => ({ schX: sx + pt[0], schY: sy + pt[1], schSectionName: sec, ...onSheet(sec) })
const L = role === "ufp" ? UFP : DFP
const note = (pt: readonly [number, number], lines: string[]) => <Note x={sx + pt[0]} y={sy + pt[1]} lines={lines} />
return (
<>
<UsbC24 name={jn} side={role === "ufp" ? "right" : "left"} {...pcb(jn, { x: jp.x, y: jp.y, rot: jp.rot })} {...xy(L.J)} />
{[...USBC24_GND].map((p) => <trace key={p} from={`${jn}.${p}`} to="net.GND" />)}
{[...USBC24_VBUS].map((p) => <trace key={p} from={`${jn}.${p}`} to={`net.${vbus}`} />)}
<trace from={`${jn}.CC1`} to={`net.${P}_CC1`} /><trace from={`${jn}.CC2`} to={`net.${P}_CC2`} />
<trace from={`${jn}.DP_A6`} to={`net.${hub}_DP`} /><trace from={`${jn}.DP_B6`} to={`net.${hub}_DP`} />
<trace from={`${jn}.DN_A7`} to={`net.${hub}_DM`} /><trace from={`${jn}.DN_B7`} to={`net.${hub}_DM`} />
{/* Receptacle-side lanes TX1 and RX1 are P/N-inverted as well: on every receptacle those two lanes arrive mirrored
from the mux (scripts/pairs_links.py). Polarity inversion is corrected by the USB 3.x receiver. */}
{(["TX1P", "TX1N", "RX1P", "RX1N", "TX2P", "TX2N", "RX2P", "RX2N"] as const).map((p) => {
const inverted = p.startsWith("TX1") || p.startsWith("RX1")
const net = inverted ? (p.endsWith("P") ? p.slice(0, -1) + "N" : p.slice(0, -1) + "P") : p
return <trace key={p} from={`${jn}.${p}`} to={`net.${P}_${net}`} />
})}
<Hd3ss3220 name={un} connector={role === "ufp" ? "left" : "right"} {...pcb(un, { x: jp.x, y: jp.y + f * 12, rot: jp.rot })} {...xy(L.U)} />
<trace from={`${un}.CC1`} to={`net.${P}_CC1`} /><trace from={`${un}.CC2`} to={`net.${P}_CC2`} />
{["TX1P", "TX1N", "RX1P", "RX1N", "TX2P", "TX2N", "RX2P", "RX2N"].map((p) => <trace key={p} from={`${un}.${p}`} to={`net.${P}_${p}`} />)}
{/* System-side lane polarity is inverted on purpose (P<->N on both pairs): the hub edge and the mux edge face each
other with counter-clockwise pin numbering, so the pairs arrive mirrored. USB 3.x receivers detect and correct
lane polarity inversion during link training (mandatory), which avoids a crossover via per conductor. */}
<trace from={`${un}.TXP`} to={`net.${hub}_TXN`} /><trace from={`${un}.TXN`} to={`net.${hub}_TXP`} />
<trace from={`${un}.RXP`} to={`net.${hub}_RXN`} /><trace from={`${un}.RXN`} to={`net.${hub}_RXP`} />
<trace from={`${un}.VDD5`} to="net.VSYS5" /><trace from={`${un}.VCC33`} to="net.V3V3" />
{["GND13", "GND28", "EP", "ENN_MUX"].map((p) => <trace key={p} from={`${un}.${p}`} to="net.GND" />)}
<trace from={`${un}.ENN_CC`} to="net.EN_CC_N" />
<trace from={`${un}.DIR`} to={`net.${P}_DIR`} />
<trace from={`${un}.VBUS_DET`} to={`net.${P}_VBUS_DET`} />
<R name={`R${k + 1}`} v="200k" a="V3V3" b={`${P}_DIR`} {...at(-3, 9)} {...row(role === "ufp" ? 3 : 3)} />
<R name={`R${k + 2}`} v="887k" a={vbus} b={`${P}_VBUS_DET`} {...at(-3, 10)} {...row(role === "ufp" ? 4 : 0)} />
<C name={`C${k + 1}`} v="100nF" a="VSYS5" b="GND" {...at(2.5, 9)} {...row(role === "ufp" ? 0 : 4)} />
<C name={`C${k + 2}`} v={role === "dfp" ? "10uF" : "1uF"} fp={role === "dfp" ? "0805" : "0402"} a="VSYS5" b="GND" {...at(4, 9)} {...row(role === "ufp" ? 1 : 5)} />
<C name={`C${k + 3}`} v="100nF" a="V3V3" b="GND" {...at(2.5, 15)} {...row(role === "ufp" ? 2 : 7)} />
{role === "ufp" ? (
<trace from={`${un}.PORT`} to="net.GND" />
) : (
<>
<trace from={`${un}.PORT`} to={`net.${P}_PORT`} /><trace from={`${un}.CURRENT_MODE`} to={`net.${P}_CUR_MODE`} />
<R name={`R${k + 3}`} v="4.7k" a="VSYS5" b={`${P}_PORT`} {...at(-3, 11)} {...row(2)} />
<R name={`R${k + 4}`} v="499k" a="VSYS5" b={`${P}_CUR_MODE`} {...at(-3, 12)} {...row(1)} />
<trace from={`${un}.ID`} to={`net.${P}_ID`} />
</>
)}
{/* ESD at the receptacle: two 4-line SuperSpeed arrays (flow-through), USB 2.0 pair, CC lines */}
<Pusb3fr4 name={`D${k + 1}`} {...pcb(`D${k + 1}`, { x: jp.x - 2.5, y: jp.y + f * 6.5, rot: jp.rot })} {...xy(L.D1)} />
<Pusb3fr4 name={`D${k + 2}`} {...pcb(`D${k + 2}`, { x: jp.x + 2.5, y: jp.y + f * 6.5, rot: jp.rot })} {...xy(L.D2)} />
{(
// Channel order follows the routing (scripts/pairs_links.py): on every receptacle the west array takes the lane
// that leaves the mux further west. Which lane a flow-through channel protects makes no electrical difference.
(role === "ufp"
? [
[`D${k + 1}`, "TX1P", "TX1N", "RX1P", "RX1N"],
[`D${k + 2}`, "TX2P", "TX2N", "RX2P", "RX2N"],
]
: [
[`D${k + 1}`, "RX1N", "RX1P", "TX1N", "TX1P"],
[`D${k + 2}`, "RX2N", "RX2P", "TX2N", "TX2P"],
]) as readonly (readonly [string, string, string, string, string])[]
).flatMap(([d, a, b, c, e]) => [
<trace key={`${d}1`} from={`${d}.CH1`} to={`net.${P}_${a}`} />, <trace key={`${d}10`} from={`${d}.FT1`} to={`net.${P}_${a}`} />,
<trace key={`${d}2`} from={`${d}.CH2`} to={`net.${P}_${b}`} />, <trace key={`${d}9`} from={`${d}.FT2`} to={`net.${P}_${b}`} />,
<trace key={`${d}4`} from={`${d}.CH3`} to={`net.${P}_${c}`} />, <trace key={`${d}7`} from={`${d}.FT3`} to={`net.${P}_${c}`} />,
<trace key={`${d}5`} from={`${d}.CH4`} to={`net.${P}_${e}`} />, <trace key={`${d}6`} from={`${d}.FT4`} to={`net.${P}_${e}`} />,
<trace key={`${d}g3`} from={`${d}.GND3`} to="net.GND" />, <trace key={`${d}g8`} from={`${d}.GND8`} to="net.GND" />,
])}
<Usblc6 name={`D${k + 3}`} {...pcb(`D${k + 3}`, { x: jp.x - 6, y: jp.y + f * 7, rot: jp.rot })} {...xy(L.D3)} />
<trace from={`D${k + 3}.IO1`} to={`net.${hub}_DP`} /><trace from={`D${k + 3}.IO1B`} to={`net.${hub}_DP`} />
<trace from={`D${k + 3}.IO2`} to={`net.${hub}_DM`} /><trace from={`D${k + 3}.IO2B`} to={`net.${hub}_DM`} />
<trace from={`D${k + 3}.VBUS`} to="net.VSYS5" /><trace from={`D${k + 3}.GND`} to="net.GND" />
{(["CC1", "CC2"] as const).map((cc, i) => (
<Fragment key={cc}>
<Tpd1e10b06 name={`D${k + 4 + i}`} {...pcb(`D${k + 4 + i}`, { x: jp.x + 5.5 + i * 1.2, y: jp.y + f * 7, rot: jp.rot + 90 })} {...xy(i === 0 ? L.CC1 : L.CC2)} />
<trace from={`D${k + 4 + i}.IO`} to={`net.${P}_${cc}`} /><trace from={`D${k + 4 + i}.GND`} to="net.GND" />
</Fragment>
))}
{role === "ufp" ? (
<C name={`C${k + 4}`} v="4.7uF" a={vbus} b="GND" {...at(-6, 5)} {...sa(UFP.cap)} />
) : (
<>
{/* Port power: hub enable AND Type-C attach */}
<PortSwitch activeLow name={`U${k / 10}`} {...pcb(`U${k / 10}`, { x: jp.x - 7, y: jp.y + f * 12, rot: jp.rot })} {...xy(DFP.sw)} />
<trace from={`U${k / 10}.IN`} to="net.VSYS5" /><trace from={`U${k / 10}.GND`} to="net.GND" />
<trace from={`U${k / 10}.OUT`} to={`net.${vbus}`} />
<trace from={`U${k / 10}.FAULT_N`} to={`net.PRT_CTL${n}`} />
<trace from={`U${k / 10}.EN`} to={`net.${P}_EN_N`} />
<trace from={`U${k / 10}.ILIM`} to={`net.${P}_ILIM`} />
<R name={`R${k + 5}`} v="15k" a={`${P}_ILIM`} b="GND" {...at(-9, 13)} {...row2(1)} />
<R name={`R${k + 6}`} v="100k" a="V3V3" b={`${P}_EN_N`} {...at(-9, 15)} {...row2(0)} />
<Nmos name={`Q${k / 50}`} {...pcb(`Q${k / 50}`, { x: jp.x - 7, y: jp.y + f * 16, rot: jp.rot })} {...xy(DFP.q)} />
<trace from={`Q${k / 50}.G`} to={`net.PRT_CTL${n}`} />
<trace from={`Q${k / 50}.D`} to={`net.${P}_EN_N`} />
<trace from={`Q${k / 50}.S`} to={`net.${P}_ID`} />
{/* the switch's input cap is drawn in the VSYS5 bank with the HD3SS3220's caps (one bank per rail per sheet) */}
<C name={`C${k + 4}`} v="1uF" a="VSYS5" b="GND" {...at(-10, 11)} {...row(6)} />
<C name={`C${k + 5}`} v="10uF" fp="0805" a={vbus} b="GND" {...at(-5, 9)} {...row2(2)} />
<C name={`C${k + 6}`} v="100nF" a={vbus} b="GND" {...at(-4, 7)} {...row2(3)} />
<Led name={`D${k + 6}`} rname={`R${k + 7}`} rail={vbus} x={jp.x - 4.5} y={jp.y + f * 2.5} rx={jp.x - 4.5} ry={jp.y + f * 4} {...row2(4)} />
</>
)}
{notes(role, n, sx, sy)}
<Frame x0={sx + L.frame[0]} y0={sy + L.frame[1]} x1={sx + L.frame[2]} y1={sy + L.frame[3]}
title={role === "ufp" ? "UPSTREAM PORT: USB-C TO HOST (UFP)" : `DOWNSTREAM USB-C PORT ${n - 2} (HUB PORT ${n})`} />
</>
)
}
/** Chip notes (function and ratings; research/*.txt, docs/decisions.md), placed under each part. */
function notes(role: "ufp" | "dfp", n: number, sx: number, sy: number) {
const N = (dx: number, dy: number, lines: string[]) => <Note x={sx + dx} y={sy + dy} lines={lines} />
const pusb = ["PUSB3FR4 SuperSpeed", "ESD, 4 ch, 0.29 pF,", "+/-15 kV (IEC 61000-4-2)"]
const lc6 = ["USBLC6-2SC6 USB 2.0 ESD", "+ VBUS rail clamp"]
const cc = ["TPD1E10B06 CC-line ESD,", "5.5 V working, 1 line each"]
if (role === "ufp")
return (
<>
{N(-0.7, -3.32, ["J1 USB-C receptacle to the host,", "USB 3.2 Gen 2 (Amphenol 12401610E4#2A)", "VBUS_HOST in: bus-power fallback"])}
{N(4.9, 1.38, pusb)}
{N(4.9, -1.42, pusb)}
{N(5.0, -3.52, lc6)}
{N(7.4, -5.25, cc)}
{N(10.6, -1.82, ["U2 HD3SS3220 Type-C port controller +", "10 Gbps 2:1 SuperSpeed mux, UFP (PORT=GND)", "VDD5 4.5-5.5 V, VCC33 3.0-3.6 V"])}
</>
)
const sw = `U${n === 3 ? 30 : 35}`
return (
<>
{N(-1.2, -2.02, [`U${n} HD3SS3220 Type-C port controller +`, "10 Gbps 2:1 SuperSpeed mux, DFP (PORT high)", "Rp 1.5 A (CURRENT_MODE 499 k), VDD5 4.5-5.5 V"])}
{N(4.3, 0.28, pusb)}
{N(4.3, -2.52, pusb)}
{N(8.8, 0.58, lc6)}
{N(6.3, -4.85, cc)}
{N(13.7, -3.32, [`J${n + 2} USB-C receptacle, 10 Gbps`, "5 V / 1.5 A (Rp advertised)"])}
{N(9.2, -6.62, [`${sw} AP22652 power switch (EN active low)`, "3-5.5 V, 2.1 A max, limit 1.61-1.86 A (15 k)", "65 mOhm, output discharge when off"])}
{N(1.0, -6.6, [`Q${n === 3 ? 6 : 7} 2N7002: turns ${sw} on only when the`, `hub enables port ${n} (PRT_CTL${n}) AND U${n}`, "reports an attached sink (ID low)"])}
</>
)
}