code wiki / _hdl_build / nx_fpga_board_anim_gate.nx
nx_fpga_board_anim_gate.nx source
↩ module page · 106 lines · 8089 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_fpga_board_anim_gate.nx -- GATE for RUNG 31: a LIVE, SELF-RUNNING virtual board. R30 was a static filmstrip;
4// this makes the virtual ULX3S RUN by itself -- operator: "like i have the actual board just virtual." A chain of
5// single-board frames, each one a big clear board (8 LEDs at that clock tick) carrying an HTML
6// <meta http-equiv="refresh"> to the NEXT frame (looping) -- so opening one page auto-advances the board at ~1 fps,
7// the LEDs counting in binary, with NO JavaScript and NO server (pure sovereign HTML + the browser's own refresh).
8// Each frame's LEDs are the real DFF outputs of the clocked counter fabric. Writes web_assets/board_0.html .. _15.html.
9// T1 every frame's LEDs == the simulated counter (counts 0..15) AND the refresh chains t -> (t+1)%N (it loops).
10// T2 sovereign (0 JS) + all 16 frames written. T3 NEVER-BRICK. T4 LIAR-KILL (corrupt counter -> wrong LEDs).
11// expect_exit: 0 license_tier: ORIGINAL
12import "nx_fpga_verilog.nx" // vlg_wr_str / vlg_wr_int
13import "nx_fpga_seq.nx"
14import "nx_fpga_fabric.nx"
15import "nx_syscalls.nx"
16
17func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
18" as *u8); return ok }
19func has_sub(buf: *u8, len: i64, s: *u8) -> i64 { var sl: i64=0; while s[sl]!=(0 as u8){sl=sl+1} var p: i64=0; while p+sl<=len { var m: i64=1; var j: i64=0; while j<sl { if buf[p+j]!=s[j] {m=0} j=j+1 } if m==1 {return 1} p=p+1 } return 0 }
20
21// render ONE big board frame (8 LEDs at `val`, tick `t`), auto-refreshing to board_<next>.html. Returns length.
22func render_board(buf: *u8, t: i64, val: i64, next: i64, NFR: i64) -> i64 {
23 var o: i64=0
24 o=vlg_wr_str(buf,o,"<!doctype html><html><head><meta charset='utf-8'><title>Nishi Virtual ULX3S</title>\x00" as *u8)
25 o=vlg_wr_str(buf,o,"<meta http-equiv='refresh' content='1;url=board_\x00" as *u8); o=vlg_wr_int(buf,o,next); o=vlg_wr_str(buf,o,".html'>\x00" as *u8)
26 o=vlg_wr_str(buf,o,"<style>body{background:#0b0b0b;color:#cfc;font-family:monospace;margin:0;padding:20px;text-align:center}h1{color:#39ff14;font-size:20px}.n{color:#8b8;font-size:13px}b{color:#39ff14}</style></head><body>\x00" as *u8)
27 o=vlg_wr_str(buf,o,"<h1>Nishi Virtual ULX3S — running</h1>\x00" as *u8)
28 o=vlg_wr_str(buf,o,"<svg width='600' height='230' xmlns='http://www.w3.org/2000/svg'>\x00" as *u8)
29 o=vlg_wr_str(buf,o,"<rect x='2' y='2' width='596' height='226' rx='14' fill='#08240b' stroke='#1a5a1a' stroke-width='3'/>\x00" as *u8)
30 o=vlg_wr_str(buf,o,"<text x='24' y='34' fill='#5fae5f' font-size='15'>ULX3S · ECP5-85F · clk \xe2\x86\x91</text>\x00" as *u8)
31 // 8 big LEDs (LED7..LED0 left->right)
32 var b: i64=7
33 while b>=0 {
34 let cx: i64 = 78 + (7-b)*64
35 let lit: i64 = (val>>b)&1
36 if lit==1 { o=vlg_wr_str(buf,o,"<circle cx='\x00" as *u8); o=vlg_wr_int(buf,o,cx); o=vlg_wr_str(buf,o,"' cy='110' r='24' fill='#39ff14' stroke='#bfffb0' stroke-width='3'/>\x00" as *u8) }
37 else { o=vlg_wr_str(buf,o,"<circle cx='\x00" as *u8); o=vlg_wr_int(buf,o,cx); o=vlg_wr_str(buf,o,"' cy='110' r='20' fill='#0c2d0c' stroke='#1f4f1f' stroke-width='3'/>\x00" as *u8) }
38 o=vlg_wr_str(buf,o,"<text x='\x00" as *u8); o=vlg_wr_int(buf,o,cx-15); o=vlg_wr_str(buf,o,"' y='160' fill='#6a9' font-size='12'>LED\x00" as *u8); o=vlg_wr_int(buf,o,b); o=vlg_wr_str(buf,o,"</text>\x00" as *u8)
39 b=b-1
40 }
41 o=vlg_wr_str(buf,o,"<text x='24' y='205' fill='#cfc' font-size='17'>tick \x00" as *u8); o=vlg_wr_int(buf,o,t); o=vlg_wr_str(buf,o," · count = \x00" as *u8); o=vlg_wr_int(buf,o,val); o=vlg_wr_str(buf,o," · 0b\x00" as *u8)
42 b=7; while b>=0 { o=vlg_wr_int(buf,o,(val>>b)&1); b=b-1 }
43 o=vlg_wr_str(buf,o,"</text></svg>\x00" as *u8)
44 o=vlg_wr_str(buf,o,"<p class='n'>The 8 LEDs are the real DFF outputs of an 8-bit counter fabric, clocked once per frame. This page auto-advances every second (HTML meta-refresh, looping) — <b>sovereign: 0 JavaScript, 0 external loads, no server</b>. Open <b>board_0.html</b> and watch it run.</p>\x00" as *u8)
45 o=vlg_wr_str(buf,o,"</body></html>\n\x00" as *u8)
46 buf[o]=0 as u8
47 return o
48}
49
50func main() -> i64 {
51 gw("=== nx_fpga_board_anim_gate: RUNG 31 -- a LIVE self-running virtual ULX3S (auto-advancing, 0-JS) ===\n" as *u8)
52 var pass: i64 = 0; var total: i64 = 0
53 let W: i64 = 8; let NFR: i64 = 16
54 let kind: *i64=sys_mmap(8*64) as *i64; let init: *i64=sys_mmap(8*64) as *i64; let src: *i64=sys_mmap(8*256) as *i64; let po: *i64=sys_mmap(8*64) as *i64
55 let npi: i64 = seq_build_counter(W, kind, init, src, po)
56 let ncells: i64 = 3*W+1
57
58 let leds: *i64=sys_mmap(8*64) as *i64
59 let pi: *i64=sys_mmap(8*8) as *i64; let co: *i64=sys_mmap(8*64) as *i64; let q: *i64=sys_mmap(8*64) as *i64
60 var z: i64=0; while z<ncells { q[z]=0; z=z+1 } pi[0]=0
61 var t: i64=0
62 while t<NFR { seq_eval(ncells,npi,kind,init,src,pi,co,q); var v: i64=0; var i: i64=0; while i<W { v=v|((fab_po(po[i],npi,pi,co)&1)<<i); i=i+1 } leds[t]=v; seq_tick(ncells,npi,kind,init,src,pi,co,q); t=t+1 }
63
64 let buf: *u8=sys_mmap(16384); let fname: *u8=sys_mmap(64)
65 var wmis: i64=0; var sov: i64=1; var chainok: i64=1
66 t=0
67 while t<NFR {
68 let nxt: i64 = (t+1) % NFR
69 let len: i64 = render_board(buf, t, leds[t], nxt, NFR)
70 // sovereignty
71 if has_sub(buf, len, "<script\x00" as *u8)==1 { sov=0 }
72 // refresh chain: must reference board_<nxt>.html
73 var cf: i64=0; cf=vlg_wr_str(fname,0,"url=board_\x00" as *u8); cf=vlg_wr_int(fname,cf,nxt); cf=vlg_wr_str(fname,cf,".html\x00" as *u8); fname[cf]=0 as u8
74 if has_sub(buf, len, fname)==0 { chainok=0 }
75 // write the frame
76 var fo: i64=0; fo=vlg_wr_str(fname,0,"web_assets/board_\x00" as *u8); fo=vlg_wr_int(fname,fo,t); fo=vlg_wr_str(fname,fo,".html\x00" as *u8); fname[fo]=0 as u8
77 let fd: i64=sys_openat_wr(fname, 420)
78 if fd >= 0 { let wr: i64=sys_write(fd, buf, len); sys_close(fd); if wr != len { wmis=wmis+1 } } else { wmis=wmis+1 }
79 t=t+1
80 }
81
82 // T1: LEDs == counter + the refresh chain loops
83 var mis: i64=0; t=0; while t<NFR { if leds[t]!=t { mis=mis+1 } t=t+1 }
84 total=total+1; if mis==0 { if chainok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) }
85 gw("T1 every frame's 8 LEDs == the simulated counter (0..15) + meta-refresh chains t->(t+1)%16 (loops): led-mismatches=" as *u8); gn(mis); gw(" chain-ok=" as *u8); gn(chainok); gw("\n" as *u8)
86
87 // T2: sovereign + all frames written
88 total=total+1; if sov==1 { if wmis==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) }
89 gw("T2 sovereign (0 <script>) + all 16 frames written to web_assets/board_0..15.html, write-failures=" as *u8); gn(wmis); gw("\n" as *u8)
90
91 // T3: never-brick
92 total=total+1; pass=pass+1
93 gw(" [PASS] T3 never-brick (#26): renders text + writes files; zero hardware-state writes, the board is virtual\n" as *u8)
94
95 // T4: liar-kill
96 init[W] = init[W] ^ 0xffff
97 var z2: i64=0; while z2<ncells { q[z2]=0; z2=z2+1 }
98 seq_eval(ncells,npi,kind,init,src,pi,co,q); seq_tick(ncells,npi,kind,init,src,pi,co,q); seq_eval(ncells,npi,kind,init,src,pi,co,q)
99 var v2: i64=0; var i2: i64=0; while i2<W { v2=v2|((fab_po(po[i2],npi,pi,co)&1)<<i2); i2=i2+1 }
100 total=total+1; if v2 != 1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
101 gw("T4 liar-kill: corrupting the counter -> the running board's LEDs read \x00" as *u8); gn(v2); gw(" after 1 tick (!= 1)\n" as *u8)
102
103 gw("\n=== nx_fpga_board_anim_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
104 if pass == total { gw(" GREEN (a LIVE self-running virtual ULX3S: open web_assets/board_0.html in the Nishi browser and the board AUTO-COUNTS 1fps, 0-JS, looping -- like holding the board, virtual)\n" as *u8); sys_exit(0); return 0 }
105 gw(" RED\n" as *u8); sys_exit(1); return 1
106}