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;
  }
}