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}