code wiki / (root) / nx_sched_qbv.nx

nx_sched_qbv.nx source

↩ module page · 68 lines · 2944 B

1// nx_sched_qbv.nx -- the BOUNDED-LATENCY transmit scheduler (IEEE 802.1Qbv 2// time-aware shaper, re-implemented from spec). A repeating cycle with a 3// RESERVED critical time-slice + a GUARD BAND so best-effort traffic can 4// never overrun into it. This gives critical traffic a PROVABLE worst- 5// case transmit latency (<= one cycle minus the slice) -- the determinism 6// that separates critical-grade (TSN/aerospace/medical) from best-effort. 7// 8// Pure/deterministic, integer-only (microseconds; Q16.16-ready), sovereign 9// (raw syscalls only). Composes the HDR quantiles in nx_linkqual to GATE 10// the measured worst case against this analytic bound. 11// 12// license_tier: ORIGINAL 13 14import "nx_syscalls.nx" 15 16// A repeating gate-control cycle: critical slice = [crit_start, crit_start 17// + crit_len); guard band = max_frame_us before crit_start (no best-effort 18// frame may be in flight across crit_start). All in microseconds. 19struct QbvSchedule { 20 cycle_us: i64, 21 crit_start: i64, 22 crit_len: i64, 23 max_frame_us: i64, 24} 25const QBV_SCHED_BYTES: i64 = 32 26 27func qbv_init(s: *QbvSchedule, cycle_us: i64, crit_start: i64, crit_len: i64, max_frame_us: i64) -> i64 { 28 s.cycle_us = cycle_us 29 s.crit_start = crit_start 30 s.crit_len = crit_len 31 s.max_frame_us = max_frame_us 32 return 0 33} 34func qbv_crit_end(s: *QbvSchedule) -> i64 { return s.crit_start + s.crit_len } 35 36// Is the critical gate open at cycle-relative time t? 37func qbv_crit_open(s: *QbvSchedule, t: i64) -> i64 { 38 if t < s.crit_start { return 0 } 39 if t >= (s.crit_start + s.crit_len) { return 0 } 40 return 1 41} 42 43// May a best-effort frame of `tx` us START at cycle-relative time t? It 44// must complete fully BEFORE the critical slice, OR start at/after the 45// slice and complete within the cycle. This IS the guard band: a frame 46// that would cross crit_start is refused (it waits until after crit_end). 47func qbv_be_can_start(s: *QbvSchedule, t: i64, tx: i64) -> i64 { 48 if (t + tx) <= s.crit_start { return 1 } // fits before critical 49 let ce: i64 = s.crit_start + s.crit_len 50 if t >= ce { 51 if (t + tx) <= s.cycle_us { return 1 } // after critical, within cycle 52 } 53 return 0 // would overrun the critical slice / cycle 54} 55 56// Worst-case wait for a CRITICAL frame arriving at cycle-relative time t 57// (0 if it can send now during the slice). 58func qbv_crit_wait(s: *QbvSchedule, t: i64) -> i64 { 59 if t < s.crit_start { return s.crit_start - t } 60 if t < (s.crit_start + s.crit_len) { return 0 } // inside the slice -> send now 61 return (s.cycle_us - t) + s.crit_start // wait for next cycle's slice 62} 63 64// The analytic worst-case bound for any critical arrival: at most one cycle 65// minus the reserved slice. (Gate the MEASURED p99 against this.) 66func qbv_crit_worst_bound(s: *QbvSchedule) -> i64 { 67 return s.cycle_us - s.crit_len 68}