code wiki / _hdl_build / nx_sched_emit.nx
nx_sched_emit.nx source
↩ module page · 136 lines · 8648 B
1// nx_sched_emit.nx -- PREEMPTIVE SCHEDULER emitter (2-task round-robin), the kernel-up rung that
2// flips census GEN-K-preemptive-smp-scheduler from BEHIND toward PRESENT. Builds on the proven
3// timer-interrupt mechanism (nx_timer_irq_emit).
4//
5// AUTHOR=ORGAN: table-computes a bare-metal rv64 qemu-virt image (zero hand-written machine code).
6// Two tasks loop-emit their own marker ('A' / 'B'). The CLINT timer preempts every TICK; the trap
7// handler is the SCHEDULER -- it ROUND-ROBINS the two tasks with the one-instruction trick
8// csrrw t6, mepc, t6 ; atomically swap the interrupted PC <-> the saved task PC
9// (no task-control-block memory, no load needed: two tasks fit in mepc + one register), re-arms the
10// timer (deadline += TICK -> MTIMECMP), counts ticks, and after K ticks clean-halts via the SiFive
11// finisher. The transcript is interleaved RUNS of A and B -- PROOF the scheduler preempted and
12// switched contexts repeatedly. (Interleaving run-length is timing-determined, so the gate asserts
13// the STRUCTURAL property -- >=N A<->B switches -- not a byte-exact golden; honest for a preemptive
14// phenomenon.) Reuses the proven rv64 mini-encoder. Sovereign, no gcc/.sh. license_tier: ORIGINAL
15import "nx_syscalls.nx"
16const SC_MAGIC_4096: i64 = 4096
17
18const SC_OUT: *u8 = "runtime/_hdl_build/_sched_virt.bin"
19const SC_LOG: *u8 = "knowledge/status/sched.log"
20
21const SC_UART: i64 = 0x10000000
22const SC_FIN: i64 = 0x100000
23const SC_PASS: i64 = 0x5555
24const SC_MTIMECMP: i64 = 0x02004000
25const SC_TICK: i64 = 0x40
26const SC_KTICKS: i64 = 12 // bounded run: 12 preemptions then halt
27const SC_MSTATUS: i64 = 0x300
28const SC_MIE: i64 = 0x304
29const SC_MTVEC: i64 = 0x305
30const SC_MEPC: i64 = 0x341
31const SC_MIE_MTIE: i64 = 0x80
32const SC_MSTATUS_MIE: i64 = 0x08
33const SC_CH_S: i64 = 83 // 'S' boot
34const SC_CH_A: i64 = 65 // 'A' task A
35const SC_CH_B: i64 = 66 // 'B' task B
36// section byte offsets (table-computed layout; see comments)
37const SC_HANDLER: i64 = 68
38const SC_HALT: i64 = 100
39const SC_TASKA: i64 = 124
40const SC_TASKB: i64 = 136
41// rv64 registers
42const RV_X0: i64 = 0
43const RV_T0: i64 = 5
44const RV_T1: i64 = 6
45const RV_T2: i64 = 7
46const RV_T3: i64 = 28
47const RV_T4: i64 = 29
48const RV_T5: i64 = 30
49const RV_T6: i64 = 31
50
51func sc_lui(rd: i64, imm20: i64) -> i64 { return ((imm20 & 0xFFFFF) << 12) | (rd << 7) | 0x37 }
52func sc_auipc(rd: i64, imm20: i64) -> i64 { return ((imm20 & 0xFFFFF) << 12) | (rd << 7) | 0x17 }
53func sc_addi(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm & 0xFFF) << 20) | (rs1 << 15) | (rd << 7) | 0x13 }
54func sc_store(rs2: i64, rs1: i64, f3: i64, imm: i64) -> i64 {
55 let hi: i64 = ((imm >> 5) & 0x7f) << 25
56 let lo: i64 = (imm & 0x1f) << 7
57 return hi | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | lo | 0x23
58}
59func sc_branch(rs1: i64, rs2: i64, f3: i64, imm: i64) -> i64 {
60 let b12: i64 = ((imm >> 12) & 0x1) << 31
61 let b11: i64 = ((imm >> 11) & 0x1) << 7
62 let b10_5: i64 = ((imm >> 5) & 0x3f) << 25
63 let b4_1: i64 = ((imm >> 1) & 0xf) << 8
64 return b12 | b10_5 | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | b4_1 | b11 | 0x63
65}
66func sc_jal(rd: i64, imm: i64) -> i64 {
67 let b20: i64 = ((imm >> 20) & 0x1) << 31
68 let b19_12: i64 = ((imm >> 12) & 0xff) << 12
69 let b11: i64 = ((imm >> 11) & 0x1) << 20
70 let b10_1: i64 = ((imm >> 1) & 0x3ff) << 21
71 return b20 | b10_1 | b11 | b19_12 | (rd << 7) | 0x6f
72}
73func sc_csrrw(rd: i64, csr: i64, rs1: i64) -> i64 { return ((csr & 0xFFF) << 20) | (rs1 << 15) | (1 << 12) | (rd << 7) | 0x73 }
74func sc_csrrs(rd: i64, csr: i64, rs1: i64) -> i64 { return ((csr & 0xFFF) << 20) | (rs1 << 15) | (2 << 12) | (rd << 7) | 0x73 }
75func sc_w32(buf: *u8, off: i64, w: i64) -> i64 {
76 buf[off]=(w & 0xff) as u8; buf[off+1]=((w>>8)&0xff) as u8; buf[off+2]=((w>>16)&0xff) as u8; buf[off+3]=((w>>24)&0xff) as u8
77 return off + 4
78}
79func sc_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
80func sc_fp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
81func sc_fn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 }
82
83func main() -> i64 {
84 let buf: *u8 = sys_mmap(SC_MAGIC_4096)
85 var o: i64 = 0
86 // BOOT (bytes 0..64): mtvec, UART, 'S', timer arm, counter=0, t6=TASK_B, enable, jump TASK_A
87 o = sc_w32(buf, o, sc_auipc(RV_T3, 0)) // 0 auipc t3,0
88 o = sc_w32(buf, o, sc_addi(RV_T3, RV_T3, SC_HANDLER)) // 4 addi t3,t3,68
89 o = sc_w32(buf, o, sc_csrrw(RV_X0, SC_MTVEC, RV_T3)) // 8 csrrw x0,mtvec,t3
90 o = sc_w32(buf, o, sc_lui(RV_T0, SC_UART >> 12)) // 12 lui t0,UART
91 o = sc_w32(buf, o, sc_addi(RV_T1, RV_X0, SC_CH_S)) // 16 addi t1,'S'
92 o = sc_w32(buf, o, sc_store(RV_T1, RV_T0, 0, 0)) // 20 sb t1,0(t0)
93 o = sc_w32(buf, o, sc_addi(RV_T4, RV_X0, SC_TICK)) // 24 addi t4,TICK (deadline)
94 o = sc_w32(buf, o, sc_lui(RV_T2, SC_MTIMECMP >> 12)) // 28 lui t2,mtimecmp
95 o = sc_w32(buf, o, sc_store(RV_T4, RV_T2, 3, 0)) // 32 sd t4,0(t2) MTIMECMP=TICK
96 o = sc_w32(buf, o, sc_addi(RV_T5, RV_X0, 0)) // 36 addi t5,0 (tick counter)
97 o = sc_w32(buf, o, sc_auipc(RV_T6, 0)) // 40 auipc t6,0 -> t6=40
98 o = sc_w32(buf, o, sc_addi(RV_T6, RV_T6, SC_TASKB - 40)) // 44 addi t6,t6,(136-40) -> t6=TASK_B
99 o = sc_w32(buf, o, sc_addi(RV_T1, RV_X0, SC_MIE_MTIE)) // 48 addi t1,0x80
100 o = sc_w32(buf, o, sc_csrrs(RV_X0, SC_MIE, RV_T1)) // 52 csrrs mie,t1
101 o = sc_w32(buf, o, sc_addi(RV_T1, RV_X0, SC_MSTATUS_MIE)) // 56 addi t1,0x08
102 o = sc_w32(buf, o, sc_csrrs(RV_X0, SC_MSTATUS, RV_T1)) // 60 csrrs mstatus,t1
103 o = sc_w32(buf, o, sc_jal(RV_X0, SC_TASKA - 64)) // 64 jal TASK_A
104 // HANDLER = SCHEDULER (bytes 68..88)
105 o = sc_w32(buf, o, sc_csrrw(RV_T6, SC_MEPC, RV_T6)) // 68 csrrw t6,mepc,t6 (swap mepc<->t6)
106 o = sc_w32(buf, o, sc_addi(RV_T4, RV_T4, SC_TICK)) // 72 addi t4,t4,TICK
107 o = sc_w32(buf, o, sc_lui(RV_T2, SC_MTIMECMP >> 12)) // 76 lui t2,mtimecmp
108 o = sc_w32(buf, o, sc_store(RV_T4, RV_T2, 3, 0)) // 80 sd t4,0(t2) re-arm
109 o = sc_w32(buf, o, sc_addi(RV_T5, RV_T5, 1)) // 84 addi t5,t5,1 counter++
110 o = sc_w32(buf, o, sc_addi(RV_T1, RV_X0, SC_KTICKS)) // 88 addi t1,K
111 o = sc_w32(buf, o, sc_branch(RV_T5, RV_T1, 0, SC_HALT - 92)) // 92 beq t5,t1,HALT
112 o = sc_w32(buf, o, 0x30200073) // 96 mret
113 // HALT (bytes 100..116): SiFive finisher PASS
114 o = sc_w32(buf, o, sc_lui(RV_T1, SC_PASS >> 12)) // 100 lui t1,0x5
115 o = sc_w32(buf, o, sc_addi(RV_T1, RV_T1, SC_PASS & 0xFFF)) // 104 addi t1,t1,0x555
116 o = sc_w32(buf, o, sc_lui(RV_T2, SC_FIN >> 12)) // 108 lui t2,0x100
117 o = sc_w32(buf, o, sc_store(RV_T1, RV_T2, 2, 0)) // 112 sw t1,0(t2) finisher
118 o = sc_w32(buf, o, sc_jal(RV_X0, 0)) // 116 jal x0,0 guard
119 // TASK_A (bytes 124..132): loop emit 'A'
120 o = sc_w32(buf, o, sc_addi(RV_T1, RV_X0, SC_CH_A)) // 124 addi t1,'A'
121 o = sc_w32(buf, o, sc_store(RV_T1, RV_T0, 0, 0)) // 128 sb t1,0(t0)
122 o = sc_w32(buf, o, sc_jal(RV_X0, SC_TASKA - 132)) // 132 jal TASK_A (-8)
123 // TASK_B (bytes 136..144): loop emit 'B'
124 o = sc_w32(buf, o, sc_addi(RV_T1, RV_X0, SC_CH_B)) // 136 addi t1,'B'
125 o = sc_w32(buf, o, sc_store(RV_T1, RV_T0, 0, 0)) // 140 sb t1,0(t0)
126 o = sc_w32(buf, o, sc_jal(RV_X0, SC_TASKB - 144)) // 144 jal TASK_B (-8)
127
128 let fd: i64 = sys_openat_wr(SC_OUT, 420)
129 if fd < 0 { sc_p("SCHEDEMIT verdict=RED reason=out-unwritable\n" as *u8); return 1 }
130 sys_write(fd, buf, o)
131 sys_close(fd)
132 sc_p("SCHEDEMIT name=" as *u8); sc_p(SC_OUT); sc_p(" machine=virt bytes=" as *u8); sc_fn(1, o); sc_p(" tasks=2 round-robin=csrrw-mepc-swap kticks=12\n" as *u8)
133 let lf: i64 = sys_openat_append(SC_LOG, 420)
134 if lf >= 0 { sc_fp(lf, "SCHEDEMIT name=" as *u8); sc_fp(lf, SC_OUT); sc_fp(lf, " machine=virt bytes=" as *u8); sc_fn(lf, o); sc_fp(lf, " tasks=2 epoch=" as *u8); sc_fn(lf, sys_now_realtime_sec()); sc_fp(lf, "\n" as *u8); sys_close(lf) }
135 return 0
136}