// JS wrapper around the WebAssembly Xtensa core (wasm/xtensa.c). Exposes the same surface as XtensaCPU (xtensa.js). const I = { AR: 0, F: 64, PC: 80, PS: 81, WB: 82, WS: 83, WB4: 84, SAR: 85, BR: 86, LBEG: 87, LEND: 88, LCOUNT: 89, SCOMPARE1: 90, ACCLO: 91, ACCHI: 92, M: 93, DEPC: 97, EXCCAUSE: 98, EXCVADDR: 99, VECBASE: 100, INTENABLE: 101, INTSET: 102, INTPINS: 103, CPENABLE: 104, THREADPTR: 105, FCR: 106, FSR: 107, ATOMCTL: 108, MEMCTL: 109, CCOMPARE: 110, CCOUNTOFF: 113, MISC: 114, DDR: 118, ICOUNT: 119, DEBUGCAUSE: 120, EPC: 121, EPS: 129, EXCSAVE: 137, HALTED: 145, STALLED: 146, CHKINT: 147, WINLIM: 148, OWB: 149, ID: 150, SR: 160, CYC: 416, CYCBASE: 418, SLICELEN: 420, N: 421, RDP: 422, WRP: 423, HOOKPC: 424, HOOKS: 425, }; export const STATE_WORDS = 1024; export class XtensaCPUWasm { constructor(soc, id, buffer, stateOff, rdOff, wrOff, exports) { this.soc = soc; this.id = id; this.ex = exports; this.S = new Int32Array(buffer, stateOff, STATE_WORDS); this.D = new Float64Array(buffer, stateOff, STATE_WORDS / 2); this.ar = this.S.subarray(I.AR, I.AR + 64); this.f = new Float32Array(buffer, stateOff + I.F * 4, 16); this.epc = this.S.subarray(I.EPC, I.EPC + 8); this.eps = this.S.subarray(I.EPS, I.EPS + 8); this.excsave = this.S.subarray(I.EXCSAVE, I.EXCSAVE + 8); this.ccompare = this.S.subarray(I.CCOMPARE, I.CCOMPARE + 3); this.m = this.S.subarray(I.M, I.M + 4); this.misc = this.S.subarray(I.MISC, I.MISC + 4); this.rd = new Int32Array(buffer, rdOff, 1 << 20); this.wr = new Int32Array(buffer, wrOff, 1 << 20); this.rdOff = rdOff; this.wrOff = wrOff; this.stateOff = stateOff; exports.xt_set_state(id, stateOff); this.reset(); } reset() { const S = this.S; S.fill(0); S[I.PC] = 0x40000400; S[I.PS] = 0x1F; S[I.WS] = 1; S[I.VECBASE] = 0x40000000; S[I.CHKINT] = 1; S[I.WINLIM] = 3; S[I.ID] = this.id; S[I.RDP] = this.rdOff; S[I.WRP] = this.wrOff; S[I.HOOKS] = this.soc.hooks && this.soc.hooks.size ? 1 : 0; } run(budget) { this.S[I.HOOKS] = this.soc.hooks.size ? 1 : 0; this.ex.xt_run(this.id, budget); } curCycles() { return this.D[I.CYCBASE >> 1] + this.S[I.SLICELEN] - this.S[I.N]; } get ccount() { return (this.curCycles() + this.S[I.CCOUNTOFF]) >>> 0; } get interrupt() { return (this.S[I.INTPINS] | this.S[I.INTSET]) | 0; } pendingLevel() { return this.ex.xt_pending(this.id); } deliverNow() { this.ex.xt_deliver(this.id); } checkWake() { if (this.pendingLevel() > 0) this.S[I.HALTED] = 0; } raiseIntSet(mask) { this.S[I.INTSET] |= mask; this.S[I.CHKINT] = 1; if (this.S[I.HALTED]) this.checkWake(); } get cyc() { return this.D[I.CYC >> 1]; } set cyc(v) { this.D[I.CYC >> 1] = v; } get cycBase() { return this.D[I.CYCBASE >> 1]; } set cycBase(v) { this.D[I.CYCBASE >> 1] = v; } get halted() { return this.S[I.HALTED] !== 0; } set halted(v) { this.S[I.HALTED] = v ? 1 : 0; } get stalled() { return this.S[I.STALLED] !== 0; } set stalled(v) { this.S[I.STALLED] = v ? 1 : 0; } get chkInt() { return this.S[I.CHKINT] !== 0; } set chkInt(v) { this.S[I.CHKINT] = v ? 1 : 0; } } for (const [name, idx] of [['pc', I.PC], ['ps', I.PS], ['wb', I.WB], ['ws', I.WS], ['wb4', I.WB4], ['sar', I.SAR], ['br', I.BR], ['lbeg', I.LBEG], ['lend', I.LEND], ['lcount', I.LCOUNT], ['scompare1', I.SCOMPARE1], ['acclo', I.ACCLO], ['acchi', I.ACCHI], ['depc', I.DEPC], ['exccause', I.EXCCAUSE], ['excvaddr', I.EXCVADDR], ['vecbase', I.VECBASE], ['intenable', I.INTENABLE], ['intSet', I.INTSET], ['intPins', I.INTPINS], ['cpenable', I.CPENABLE], ['threadptr', I.THREADPTR], ['fcr', I.FCR], ['fsr', I.FSR], ['atomctl', I.ATOMCTL], ['memctl', I.MEMCTL], ['ccountOff', I.CCOUNTOFF], ['ddr', I.DDR], ['icount', I.ICOUNT], ['debugcause', I.DEBUGCAUSE], ['winLim', I.WINLIM], ['owb', I.OWB], ['sliceLen', I.SLICELEN], ['n', I.N], ['hookPc', I.HOOKPC]]) { Object.defineProperty(XtensaCPUWasm.prototype, name, { get() { return this.S[idx]; }, set(v) { this.S[idx] = v; } }); }