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}