File size: 7,232 Bytes
d8afd6f 8d577f5 d8afd6f 8d577f5 d8afd6f 8d577f5 d8afd6f 8d577f5 d8afd6f 8d577f5 d8afd6f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | // ESP32 ULP coprocessor (FSM) interpreter + RTC_IO / SENS register blocks
const RTC_PAD_TO_GPIO = [36, 37, 38, 39, 34, 35, 25, 26, 33, 32, 4, 0, 2, 15, 13, 12, 14, 27];
const GPIO_TO_RTC = {}; RTC_PAD_TO_GPIO.forEach((g, i) => { GPIO_TO_RTC[g] = i; });
export class RtcIo {
constructor(soc) { this.soc = soc; this.reset(); }
reset() { this.out = 0; this.en = 0; this.r = new Int32Array(0x100); this.status = 0; }
drives(pad) { const i = GPIO_TO_RTC[pad]; return i !== undefined && ((this.en >>> (14 + i)) & 1) === 1; }
outLevel(pad) { return (this.out >>> (14 + GPIO_TO_RTC[pad])) & 1; }
inReg() { let v = 0; for (let i = 0; i < 18; i++) if (this.soc.gpio.level(RTC_PAD_TO_GPIO[i])) v |= (1 << (14 + i)); return v | 0; }
read(off) {
switch (off) {
case 0x00: return this.out; case 0x0c: return this.en; case 0x18: return this.status; case 0x24: return this.inReg();
}
return this.r[(off >> 2) & 0xFF];
}
write(off, v) {
const old = this.soc.gpio.snapshot();
switch (off) {
case 0x00: this.out = v; break; case 0x04: this.out |= v; break; case 0x08: this.out &= ~v; break;
case 0x0c: this.en = v; break; case 0x10: this.en |= v; break; case 0x14: this.en &= ~v; break;
case 0x1c: this.status |= v; break; case 0x20: this.status &= ~v; break;
default: this.r[(off >> 2) & 0xFF] = v;
}
this.soc.gpio.padsChanged(old);
}
}
export class Sens {
constructor(soc) { this.soc = soc; this.r = new Int32Array(0x100); }
reset() { this.r.fill(0); }
read(off) { return this.r[(off >> 2) & 0xFF]; }
write(off, v) {
this.r[(off >> 2) & 0xFF] = v;
if (off === 0x2c) {
if ((v >> 8) & 1 && (v >> 9) & 1) this.soc.ulp.start((v >> 11) & 0x7FF); // FORCE_START_TOP + START_TOP
}
}
}
export class Ulp {
constructor(soc) { this.soc = soc; this.reset(); }
reset() { this.running = false; this.pc = 0; this.reg = [0, 0, 0, 0]; this.stage = 0; this.zero = false; this.ovf = false; this.tNs = 0; this.cycNs = 125; }
mem() { return this.soc.rtcSlow32; }
start(pc) {
this.pc = pc; this.running = true; this.reg = [0, 0, 0, 0]; this.stage = 0; this.tNs = this.soc.timeNs;
}
advance(tNs) {
if (!this.running) return;
let guard = 0; const soc = this.soc;
while (this.tNs < tNs && this.running && guard++ < 100000) { soc.ulpNow = this.tNs; this.step(); }
soc.ulpNow = null;
if (this.tNs < tNs && !this.running) this.tNs = tNs;
}
periphRead(sel, addr) {
const soc = this.soc;
const bases = [0x3ff48000, 0x3ff48400, 0x3ff48800, 0x3ff48c00];
return soc.busRead(soc.cpus[0], bases[sel] + addr * 4, 4);
}
periphWrite(sel, addr, v) {
const soc = this.soc;
const bases = [0x3ff48000, 0x3ff48400, 0x3ff48800, 0x3ff48c00];
soc.busWrite(soc.cpus[0], bases[sel] + addr * 4, 4, v);
}
step() {
const m = this.mem();
const ins = m[this.pc & 0x7FF] >>> 0;
const op = ins >>> 28, R = this.reg;
let cyc = 5, npc = this.pc + 1;
switch (op) {
case 1: { // WR_REG
const addr = ins & 0xFF, sel = (ins >> 8) & 3, data = (ins >> 10) & 0xFF, low = (ins >> 18) & 31, high = (ins >> 23) & 31;
const width = high - low + 1;
// read-modify-write for partial; for full-word registers (W1TS style) write shifted value
const mask = (width >= 32 ? 0xFFFFFFFF : ((1 << width) - 1)) << low;
const full = (this.periphRead(sel, addr) & ~mask) | ((data << low) & mask);
this.periphWrite(sel, addr, this.isW1(sel, addr) ? ((data << low) & mask) : full);
cyc = 10; break;
}
case 2: { // RD_REG
const addr = ins & 0xFF, sel = (ins >> 8) & 3, low = (ins >> 18) & 31, high = (ins >> 23) & 31;
const v = this.periphRead(sel, addr) >>> 0;
R[0] = ((v >>> low) & ((1 << (high - low + 1)) - 1)) & 0xFFFF; cyc = 8; break;
}
case 4: cyc = (ins & 0xFFFF) + 1; break; // DELAY
case 6: { // ST
const d = ins & 3, s = (ins >> 2) & 3, off = (ins >> 10) & 0x7FF;
m[(R[s] + off) & 0x7FF] = ((this.pc & 0xFFFF) << 21) | (R[d] & 0xFFFF);
cyc = 4; break;
}
case 13: { // LD
const d = ins & 3, s = (ins >> 2) & 3, off = (ins >> 10) & 0x7FF;
R[d] = m[(R[s] + off) & 0x7FF] & 0xFFFF; cyc = 4; break;
}
case 7: { // ALU
const sub = (ins >> 21) & 7; // bits 25:21? ulp_insn: sub_opcode[27:25], sel[24:21]
const sub3 = (ins >> 25) & 7, sel = (ins >> 21) & 15;
if (sub3 === 0) { // reg
const d = ins & 3, s = (ins >> 2) & 3, t = (ins >> 4) & 3; this.alu(sel, d, R[s], R[t], false);
} else if (sub3 === 1) { // imm
const d = ins & 3, s = (ins >> 2) & 3, imm = (ins >> 4) & 0xFFFF; this.alu(sel, d, R[s], imm, true);
} else { // stage counter
const imm = (ins >> 4) & 0xFF;
if (sel === 0) this.stage = (this.stage + imm) & 0xFF; else if (sel === 1) this.stage = (this.stage - imm) & 0xFF; else this.stage = 0;
}
cyc = 6; break;
}
case 8: { // BRANCH
const sub = (ins >> 25) & 7;
if (sub === 0) { // BX
const type = (ins >> 28 - 0) & 0; const tp = (ins >> 22) & 7;
const target = ((ins >> 21) & 1) ? R[ins & 3] : ((ins >> 2) & 0x7FF);
let take = false;
if (tp === 0) take = true; else if (tp === 1) take = this.zero; else if (tp === 2) take = this.ovf;
if (take) npc = target;
} else if (sub === 1) { // B: relative, cond on R0
const imm = ins & 0xFFFF, cmp = (ins >> 16) & 1, off = (ins >> 17) & 0x7F, sign = (ins >> 24) & 1;
const take = cmp === 0 ? R[0] < imm : R[0] >= imm;
if (take) npc = this.pc + (sign ? -off : off);
} else if (sub === 2) { // BS
const imm = ins & 0xFF, cmp = (ins >> 15) & 3, off = (ins >> 17) & 0x7F, sign = (ins >> 24) & 1;
const take = cmp === 0 ? this.stage < imm : cmp === 1 ? this.stage >= imm : this.stage <= imm;
if (take) npc = this.pc + (sign ? -off : off);
}
cyc = 5; break;
}
case 9: { // END
const sub = (ins >> 25) & 7;
if (sub === 0 && (ins & 1)) this.soc.rtc.ulpWake();
cyc = 5; break;
}
case 11: this.running = false; cyc = 5; break; // HALT
case 5: R[ins & 3] = 0x800; cyc = 20; break; // ADC (stub)
case 10: R[ins & 3] = 0x80; cyc = 20; break; // TSENS (stub)
case 3: cyc = 20; break; // I2C (stub)
default: this.running = false; break;
}
this.pc = npc & 0x7FF;
this.tNs += cyc * this.cycNs;
}
isW1(sel, addr) { return false; }
alu(sel, d, a, b, isImm) {
const R = this.reg; let r;
switch (sel) {
case 0: r = a + b; this.ovf = r > 0xFFFF; break;
case 1: r = a - b; this.ovf = r < 0; break;
case 2: r = a & b; this.ovf = false; break;
case 3: r = a | b; this.ovf = false; break;
case 4: r = isImm ? b : a; this.ovf = false; break; // MOV: reg form copies sreg, imm form loads imm
case 5: r = a << b; this.ovf = r > 0xFFFF; break;
case 6: r = a >> b; this.ovf = false; break;
default: r = 0;
}
r &= 0xFFFF; R[d] = r; this.zero = r === 0;
}
}
|