nx_robot_signal_gate.nx source
↩ module page · 126 lines · 6044 B
1// nx_robot_signal_gate.nx -- R-ROBO-6 GATE: the hardware signal/register rung (STEP/DIR + PWM + GPIO),
2// built from the rung below (nx_robot_step pulse plan). VERIFIES:
3// (1) GPIO write/read + level-normalize (garbage level -> 1);
4// (2) NEVER-BRICK pin-mask: a write to a non-allowed pin is REJECTED and the pin is unchanged;
5// (3) STEP/DIR waveform from a pulse plan: rising-edge count EXACTLY equals the commanded steps
6// (round-trip pulses->waveform->steps), DIR level matches direction;
7// (4) full-move: command (20,8,-5); generate per-axis waveforms; rising edges == |delta| each,
8// negative axis sets DIR high;
9// (5) PWM: in-range duty -> exact high-count; over/under -> CLAMPED (never-brick);
10// (6) EXHAUSTIVE never-brick: sweep PWM duty -50..200 -> high-count always in [0,PERIOD];
11// (7) NEGATIVE CONTROL: a raw register write escapes the pin-mask (proving the mask is load-bearing).
12// 100% sovereign, integer-only, hardware-rung-up. license_tier: ORIGINAL expect_exit: 0
13import "nx_syscalls.nx"
14import "nx_robot_signal.nx"
15
16func sw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
17func sn(v: i64) -> i64 {
18 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
19 var m: i64 = v
20 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
21 let d: *u8 = sys_mmap(24); var k: i64 = 0
22 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
23 let o: *u8 = sys_mmap(24); var i: i64 = 0
24 while i < k { o[i] = d[k - 1 - i]; i = i + 1 }
25 sys_write(1, o, k); return 0
26}
27func check(name: *u8, cond: i64, tot: *i64) -> i64 {
28 if cond == 1 { sw(" ok " as *u8); tot[0] = tot[0] + 1 }
29 else { sw(" FAIL " as *u8); tot[1] = tot[1] + 1 }
30 sw(name); sw("\n" as *u8)
31 return 0
32}
33func abs_i(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
34
35func main() -> i64 {
36 let tot: *i64 = sys_mmap(16) as *i64
37 tot[0] = 0; tot[1] = 0
38 sw("=== nx_robot_signal_gate R-ROBO-6 -- hardware signal/register rung (STEP/DIR + PWM + GPIO) ===\n" as *u8)
39
40 let NB: i64 = 8
41 let pins: *i64 = gpio_new(NB)
42 let allowed: *i64 = gpio_new(NB)
43 allowed[0] = 1; allowed[1] = 1 // STEP + DIR pins writable; pins 2..7 are NOT
44
45 // (1) write/read + normalize
46 gpio_write(pins, allowed, 0, 1)
47 var ok1: i64 = 0
48 if gpio_read(pins, 0) == 1 {
49 gpio_write(pins, allowed, 1, 99) // garbage level -> normalized to 1
50 if gpio_read(pins, 1) == 1 { ok1 = 1 }
51 }
52 check("GPIO write/read + level-normalize (garbage->1)" as *u8, ok1, tot)
53
54 // (2) never-brick pin mask: write to a disallowed pin (5) rejected, pin stays 0
55 let rej: i64 = gpio_write(pins, allowed, 5, 1)
56 var ok2: i64 = 0
57 if rej == (0 - 1) { if gpio_read(pins, 5) == 0 { ok2 = 1 } }
58 check("NEVER-BRICK pin-mask: write to non-allowed pin REJECTED (pin unchanged)" as *u8, ok2, tot)
59
60 // build a pulse plan: 3 axes, delta (20, 8, -5)
61 let naxes: i64 = 3
62 let delta: *i64 = sys_mmap(64) as *i64
63 delta[0] = 20; delta[1] = 8; delta[2] = 0 - 5
64 let dir: *i64 = sys_mmap(64) as *i64
65 let maxt: i64 = 64
66 let puls: *i64 = sys_mmap(maxt * naxes * 8) as *i64
67 let nt: i64 = robot_step_plan(delta, naxes, dir, puls, maxt)
68
69 // (3) STEP/DIR waveform for axis 1 (8 steps, +dir)
70 let trace: *i64 = sys_mmap(4096) as *i64
71 let nw1: i64 = gen_step_dir(puls, naxes, nt, 1, dir[1], pins, allowed, trace)
72 let re1: i64 = count_rising(trace, nw1, SIG_STEP_BIT)
73 sw(" axis1: waveform writes=" as *u8); sn(nw1); sw(" STEP rising-edges=" as *u8); sn(re1); sw(" (expect 8)\n" as *u8)
74 var ok3: i64 = 0
75 if re1 == 8 { ok3 = 1 }
76 check("STEP/DIR waveform: rising-edge count == commanded steps (axis1=8)" as *u8, ok3, tot)
77
78 // (4) axis 2: 5 steps, dir = -1 -> DIR high
79 let pins2: *i64 = gpio_new(NB)
80 let trace2: *i64 = sys_mmap(4096) as *i64
81 let nw2: i64 = gen_step_dir(puls, naxes, nt, 2, dir[2], pins2, allowed, trace2)
82 let re2: i64 = count_rising(trace2, nw2, SIG_STEP_BIT)
83 sw(" axis2: STEP rising-edges=" as *u8); sn(re2); sw(" DIR pin=" as *u8); sn(gpio_read(pins2, SIG_DIR_BIT)); sw(" (expect 5, DIR=1)\n" as *u8)
84 var ok4: i64 = 0
85 if re2 == 5 { if gpio_read(pins2, SIG_DIR_BIT) == 1 { ok4 = 1 } }
86 check("full move: per-axis edges == |delta| and negative axis sets DIR high" as *u8, ok4, tot)
87
88 // (5) PWM in-range + clamp
89 let pat: *i64 = sys_mmap(SIG_PWM_PERIOD * 8) as *i64
90 let d1: i64 = gen_pwm(30, pat)
91 let h1: i64 = count_high(pat, SIG_PWM_PERIOD)
92 let d2: i64 = gen_pwm(150, pat) // clamp to 100
93 let h2: i64 = count_high(pat, SIG_PWM_PERIOD)
94 let d3: i64 = gen_pwm(0 - 10, pat) // clamp to 0
95 let h3: i64 = count_high(pat, SIG_PWM_PERIOD)
96 sw(" PWM duty 30->high " as *u8); sn(h1); sw(", 150->high " as *u8); sn(h2); sw(", -10->high " as *u8); sn(h3); sw("\n" as *u8)
97 var ok5: i64 = 0
98 if h1 == 30 { if h2 == 100 { if h3 == 0 { ok5 = 1 } } }
99 check("PWM: exact high-count + clamp to [0,PERIOD] (never-brick)" as *u8, ok5, tot)
100
101 // (6) exhaustive never-brick PWM sweep
102 var esc: i64 = 0
103 var du: i64 = 0 - 50
104 while du <= 200 {
105 let dd: i64 = gen_pwm(du, pat)
106 let hh: i64 = count_high(pat, SIG_PWM_PERIOD)
107 if hh < 0 { esc = 1 }
108 if hh > SIG_PWM_PERIOD { esc = 1 }
109 du = du + 1
110 }
111 var ok6: i64 = 0
112 if esc == 0 { ok6 = 1 }
113 check("EXHAUSTIVE never-brick: PWM high-count always in [0,PERIOD] over -50..200" as *u8, ok6, tot)
114
115 // (7) negative control: raw write escapes the pin mask
116 let pins3: *i64 = gpio_new(NB)
117 gpio_write_raw(pins3, 5, 1) // poke disallowed pin 5 directly
118 var ok7: i64 = 0
119 if gpio_read(pins3, 5) == 1 { ok7 = 1 }
120 check("NEG-CONTROL: raw write escapes pin-mask (mask earns its keep)" as *u8, ok7, tot)
121
122 sw("=== VERDICT pass=" as *u8); sn(tot[0]); sw(" fail=" as *u8); sn(tot[1]); sw(" ===\n" as *u8)
123 if tot[1] == 0 { sys_exit(0) }
124 sys_exit(1)
125 return 1
126}