Files
homeclaw/web-ui/vendor/rp2040js/sio.js
T
kim 209f3a3aef v3.0.6: RP2040 에뮬레이터 개선 — USB CDC, vendor화, 부팅 감지
- USB CDC 경로 복원: #define Serial Serial1 우회 제거
- rp2040js/avr8js/codemirror/xterm/monaco-editor vendor화
- /vendor 정적 라우팅 추가 (Express 5 catch-all 우회)
- 부팅 완료 타임아웃 감지 (Serial 출력 없어도 8초 후 RAF 전환)
- 리셋 시 Simulator 재생성으로 주변 장치 상태 초기화
- 시리얼 패널의 내부 메시지를 console.log로 이동
2026-06-19 15:49:36 +09:00

422 lines
17 KiB
JavaScript

import { Interpolator } from './interpolator.js';
const CPUID = 0x000;
// GPIO
const GPIO_IN = 0x004; // Input value for GPIO pins
const GPIO_HI_IN = 0x008; // Input value for QSPI pins
const GPIO_OUT = 0x010; // GPIO output value
const GPIO_OUT_SET = 0x014; // GPIO output value set
const GPIO_OUT_CLR = 0x018; // GPIO output value clear
const GPIO_OUT_XOR = 0x01c; // GPIO output value XOR
const GPIO_OE = 0x020; // GPIO output enable
const GPIO_OE_SET = 0x024; // GPIO output enable set
const GPIO_OE_CLR = 0x028; // GPIO output enable clear
const GPIO_OE_XOR = 0x02c; // GPIO output enable XOR
const GPIO_HI_OUT = 0x030; // QSPI output value
const GPIO_HI_OUT_SET = 0x034; // QSPI output value set
const GPIO_HI_OUT_CLR = 0x038; // QSPI output value clear
const GPIO_HI_OUT_XOR = 0x03c; // QSPI output value XOR
const GPIO_HI_OE = 0x040; // QSPI output enable
const GPIO_HI_OE_SET = 0x044; // QSPI output enable set
const GPIO_HI_OE_CLR = 0x048; // QSPI output enable clear
const GPIO_HI_OE_XOR = 0x04c; // QSPI output enable XOR
const GPIO_MASK = 0x3fffffff;
//HARDWARE DIVIDER
const DIV_UDIVIDEND = 0x060; // Divider unsigned dividend
const DIV_UDIVISOR = 0x064; // Divider unsigned divisor
const DIV_SDIVIDEND = 0x068; // Divider signed dividend
const DIV_SDIVISOR = 0x06c; // Divider signed divisor
const DIV_QUOTIENT = 0x070; // Divider result quotient
const DIV_REMAINDER = 0x074; //Divider result remainder
const DIV_CSR = 0x078;
//INTERPOLATOR
const INTERP0_ACCUM0 = 0x080; // Read/write access to accumulator 0
const INTERP0_ACCUM1 = 0x084; // Read/write access to accumulator 1
const INTERP0_BASE0 = 0x088; // Read/write access to BASE0 register
const INTERP0_BASE1 = 0x08c; // Read/write access to BASE1 register
const INTERP0_BASE2 = 0x090; // Read/write access to BASE2 register
const INTERP0_POP_LANE0 = 0x094; // Read LANE0 result, and simultaneously write lane results to both accumulators (POP)
const INTERP0_POP_LANE1 = 0x098; // Read LANE1 result, and simultaneously write lane results to both accumulators (POP)
const INTERP0_POP_FULL = 0x09c; // Read FULL result, and simultaneously write lane results to both accumulators (POP)
const INTERP0_PEEK_LANE0 = 0x0a0; // Read LANE0 result, without altering any internal state (PEEK)
const INTERP0_PEEK_LANE1 = 0x0a4; // Read LANE1 result, without altering any internal state (PEEK)
const INTERP0_PEEK_FULL = 0x0a8; // Read FULL result, without altering any internal state (PEEK)
const INTERP0_CTRL_LANE0 = 0x0ac; // Control register for lane 0
const INTERP0_CTRL_LANE1 = 0x0b0; // Control register for lane 1
const INTERP0_ACCUM0_ADD = 0x0b4; // Values written here are atomically added to ACCUM0
const INTERP0_ACCUM1_ADD = 0x0b8; // Values written here are atomically added to ACCUM1
const INTERP0_BASE_1AND0 = 0x0bc; // On write, the lower 16 bits go to BASE0, upper bits to BASE1 simultaneously
const INTERP1_ACCUM0 = 0x0c0; // Read/write access to accumulator 0
const INTERP1_ACCUM1 = 0x0c4; // Read/write access to accumulator 1
const INTERP1_BASE0 = 0x0c8; // Read/write access to BASE0 register
const INTERP1_BASE1 = 0x0cc; // Read/write access to BASE1 register
const INTERP1_BASE2 = 0x0d0; // Read/write access to BASE2 register
const INTERP1_POP_LANE0 = 0x0d4; // Read LANE0 result, and simultaneously write lane results to both accumulators (POP)
const INTERP1_POP_LANE1 = 0x0d8; // Read LANE1 result, and simultaneously write lane results to both accumulators (POP)
const INTERP1_POP_FULL = 0x0dc; // Read FULL result, and simultaneously write lane results to both accumulators (POP)
const INTERP1_PEEK_LANE0 = 0x0e0; // Read LANE0 result, without altering any internal state (PEEK)
const INTERP1_PEEK_LANE1 = 0x0e4; // Read LANE1 result, without altering any internal state (PEEK)
const INTERP1_PEEK_FULL = 0x0e8; // Read FULL result, without altering any internal state (PEEK)
const INTERP1_CTRL_LANE0 = 0x0ec; // Control register for lane 0
const INTERP1_CTRL_LANE1 = 0x0f0; // Control register for lane 1
const INTERP1_ACCUM0_ADD = 0x0f4; // Values written here are atomically added to ACCUM0
const INTERP1_ACCUM1_ADD = 0x0f8; // Values written here are atomically added to ACCUM1
const INTERP1_BASE_1AND0 = 0x0fc; // On write, the lower 16 bits go to BASE0, upper bits to BASE1 simultaneously
//SPINLOCK
const SPINLOCK_ST = 0x5c;
const SPINLOCK0 = 0x100;
const SPINLOCK31 = 0x17c;
export class RPSIO {
constructor(rp2040) {
this.rp2040 = rp2040;
this.gpioValue = 0;
this.gpioOutputEnable = 0;
this.qspiGpioValue = 0;
this.qspiGpioOutputEnable = 0;
this.divDividend = 0;
this.divDivisor = 1;
this.divQuotient = 0;
this.divRemainder = 0;
this.divCSR = 0;
this.spinLock = 0;
this.interp0 = new Interpolator(0);
this.interp1 = new Interpolator(1);
}
updateHardwareDivider(signed) {
if (this.divDivisor == 0) {
this.divQuotient = this.divDividend > 0 ? -1 : 1;
this.divRemainder = this.divDividend;
}
else {
if (signed) {
this.divQuotient = (this.divDividend | 0) / (this.divDivisor | 0);
this.divRemainder = (this.divDividend | 0) % (this.divDivisor | 0);
}
else {
this.divQuotient = (this.divDividend >>> 0) / (this.divDivisor >>> 0);
this.divRemainder = (this.divDividend >>> 0) % (this.divDivisor >>> 0);
}
}
this.divCSR = 0b11;
this.rp2040.core.cycles += 8;
}
readUint32(offset) {
if (offset >= SPINLOCK0 && offset <= SPINLOCK31) {
const bitIndexMask = 1 << ((offset - SPINLOCK0) / 4);
if (this.spinLock & bitIndexMask) {
return 0;
}
else {
this.spinLock |= bitIndexMask;
return bitIndexMask;
}
}
switch (offset) {
case GPIO_IN:
return this.rp2040.gpioValues;
case GPIO_HI_IN: {
const { qspi } = this.rp2040;
let result = 0;
for (let qspiIndex = 0; qspiIndex < qspi.length; qspiIndex++) {
if (qspi[qspiIndex].inputValue) {
result |= 1 << qspiIndex;
}
}
return result;
}
case GPIO_OUT:
return this.gpioValue;
case GPIO_OE:
return this.gpioOutputEnable;
case GPIO_HI_OUT:
return this.qspiGpioValue;
case GPIO_HI_OE:
return this.qspiGpioOutputEnable;
case GPIO_OUT_SET:
case GPIO_OUT_CLR:
case GPIO_OUT_XOR:
case GPIO_OE_SET:
case GPIO_OE_CLR:
case GPIO_OE_XOR:
case GPIO_HI_OUT_SET:
case GPIO_HI_OUT_CLR:
case GPIO_HI_OUT_XOR:
case GPIO_HI_OE_SET:
case GPIO_HI_OE_CLR:
case GPIO_HI_OE_XOR:
return 0; // TODO verify with silicone
case CPUID:
// Returns the current CPU core id (always 0 for now)
return 0;
case SPINLOCK_ST:
return this.spinLock;
case DIV_UDIVIDEND:
return this.divDividend;
case DIV_SDIVIDEND:
return this.divDividend;
case DIV_UDIVISOR:
return this.divDivisor;
case DIV_SDIVISOR:
return this.divDivisor;
case DIV_QUOTIENT:
this.divCSR &= ~0b10;
return this.divQuotient;
case DIV_REMAINDER:
return this.divRemainder;
case DIV_CSR:
return this.divCSR;
case INTERP0_ACCUM0:
return this.interp0.accum0;
case INTERP0_ACCUM1:
return this.interp0.accum1;
case INTERP0_BASE0:
return this.interp0.base0;
case INTERP0_BASE1:
return this.interp0.base1;
case INTERP0_BASE2:
return this.interp0.base2;
case INTERP0_CTRL_LANE0:
return this.interp0.ctrl0;
case INTERP0_CTRL_LANE1:
return this.interp0.ctrl1;
case INTERP0_PEEK_LANE0:
return this.interp0.result0;
case INTERP0_PEEK_LANE1:
return this.interp0.result1;
case INTERP0_PEEK_FULL:
return this.interp0.result2;
case INTERP0_POP_LANE0: {
const value = this.interp0.result0;
this.interp0.writeback();
return value;
}
case INTERP0_POP_LANE1: {
const value = this.interp0.result1;
this.interp0.writeback();
return value;
}
case INTERP0_POP_FULL: {
const value = this.interp0.result2;
this.interp0.writeback();
return value;
}
case INTERP0_ACCUM0_ADD:
return this.interp0.smresult0;
case INTERP0_ACCUM1_ADD:
return this.interp0.smresult1;
case INTERP1_ACCUM0:
return this.interp1.accum0;
case INTERP1_ACCUM1:
return this.interp1.accum1;
case INTERP1_BASE0:
return this.interp1.base0;
case INTERP1_BASE1:
return this.interp1.base1;
case INTERP1_BASE2:
return this.interp1.base2;
case INTERP1_CTRL_LANE0:
return this.interp1.ctrl0;
case INTERP1_CTRL_LANE1:
return this.interp1.ctrl1;
case INTERP1_PEEK_LANE0:
return this.interp1.result0;
case INTERP1_PEEK_LANE1:
return this.interp1.result1;
case INTERP1_PEEK_FULL:
return this.interp1.result2;
case INTERP1_POP_LANE0: {
const value = this.interp1.result0;
this.interp1.writeback();
return value;
}
case INTERP1_POP_LANE1: {
const value = this.interp1.result1;
this.interp1.writeback();
return value;
}
case INTERP1_POP_FULL: {
const value = this.interp1.result2;
this.interp1.writeback();
return value;
}
case INTERP1_ACCUM0_ADD:
return this.interp1.smresult0;
case INTERP1_ACCUM1_ADD:
return this.interp1.smresult1;
}
console.warn(`Read from invalid SIO address: ${offset.toString(16)}`);
return 0xffffffff;
}
writeUint32(offset, value) {
if (offset >= SPINLOCK0 && offset <= SPINLOCK31) {
const bitIndexMask = ~(1 << ((offset - SPINLOCK0) / 4));
this.spinLock &= bitIndexMask;
return;
}
const prevGpioValue = this.gpioValue;
const prevGpioOutputEnable = this.gpioOutputEnable;
switch (offset) {
case GPIO_OUT:
this.gpioValue = value & GPIO_MASK;
break;
case GPIO_OUT_SET:
this.gpioValue |= value & GPIO_MASK;
break;
case GPIO_OUT_CLR:
this.gpioValue &= ~value;
break;
case GPIO_OUT_XOR:
this.gpioValue ^= value & GPIO_MASK;
break;
case GPIO_OE:
this.gpioOutputEnable = value & GPIO_MASK;
break;
case GPIO_OE_SET:
this.gpioOutputEnable |= value & GPIO_MASK;
break;
case GPIO_OE_CLR:
this.gpioOutputEnable &= ~value;
break;
case GPIO_OE_XOR:
this.gpioOutputEnable ^= value & GPIO_MASK;
break;
case GPIO_HI_OUT:
this.qspiGpioValue = value & GPIO_MASK;
break;
case GPIO_HI_OUT_SET:
this.qspiGpioValue |= value & GPIO_MASK;
break;
case GPIO_HI_OUT_CLR:
this.qspiGpioValue &= ~value;
break;
case GPIO_HI_OUT_XOR:
this.qspiGpioValue ^= value & GPIO_MASK;
break;
case GPIO_HI_OE:
this.qspiGpioOutputEnable = value & GPIO_MASK;
break;
case GPIO_HI_OE_SET:
this.qspiGpioOutputEnable |= value & GPIO_MASK;
break;
case GPIO_HI_OE_CLR:
this.qspiGpioOutputEnable &= ~value;
break;
case GPIO_HI_OE_XOR:
this.qspiGpioOutputEnable ^= value & GPIO_MASK;
break;
case DIV_UDIVIDEND:
this.divDividend = value;
this.updateHardwareDivider(false);
break;
case DIV_SDIVIDEND:
this.divDividend = value;
this.updateHardwareDivider(true);
break;
case DIV_UDIVISOR:
this.divDivisor = value;
this.updateHardwareDivider(false);
break;
case DIV_SDIVISOR:
this.divDivisor = value;
this.updateHardwareDivider(true);
break;
case DIV_QUOTIENT:
this.divQuotient = value;
this.divCSR = 0b11;
break;
case DIV_REMAINDER:
this.divRemainder = value;
this.divCSR = 0b11;
break;
case INTERP0_ACCUM0:
this.interp0.accum0 = value;
this.interp0.update();
break;
case INTERP0_ACCUM1:
this.interp0.accum1 = value;
this.interp0.update();
break;
case INTERP0_BASE0:
this.interp0.base0 = value;
this.interp0.update();
break;
case INTERP0_BASE1:
this.interp0.base1 = value;
this.interp0.update();
break;
case INTERP0_BASE2:
this.interp0.base2 = value;
this.interp0.update();
break;
case INTERP0_CTRL_LANE0:
this.interp0.ctrl0 = value;
this.interp0.update();
break;
case INTERP0_CTRL_LANE1:
this.interp0.ctrl1 = value;
this.interp0.update();
break;
case INTERP0_ACCUM0_ADD:
this.interp0.accum0 += value;
this.interp0.update();
break;
case INTERP0_ACCUM1_ADD:
this.interp0.accum1 += value;
this.interp0.update();
break;
case INTERP0_BASE_1AND0:
this.interp0.setBase01(value);
break;
case INTERP1_ACCUM0:
this.interp1.accum0 = value;
this.interp1.update();
break;
case INTERP1_ACCUM1:
this.interp1.accum1 = value;
this.interp1.update();
break;
case INTERP1_BASE0:
this.interp1.base0 = value;
this.interp1.update();
break;
case INTERP1_BASE1:
this.interp1.base1 = value;
this.interp1.update();
break;
case INTERP1_BASE2:
this.interp1.base2 = value;
this.interp1.update();
break;
case INTERP1_CTRL_LANE0:
this.interp1.ctrl0 = value;
this.interp1.update();
break;
case INTERP1_CTRL_LANE1:
this.interp1.ctrl1 = value;
this.interp1.update();
break;
case INTERP1_ACCUM0_ADD:
this.interp1.accum0 += value;
this.interp1.update();
break;
case INTERP1_ACCUM1_ADD:
this.interp1.accum1 += value;
this.interp1.update();
break;
case INTERP1_BASE_1AND0:
this.interp1.setBase01(value);
break;
default:
console.warn(`Write to invalid SIO address: ${offset.toString(16)}, value=${value.toString(16)}`);
}
const pinsToUpdate = (this.gpioValue ^ prevGpioValue) | (this.gpioOutputEnable ^ prevGpioOutputEnable);
if (pinsToUpdate) {
const { gpio } = this.rp2040;
for (let gpioIndex = 0; gpioIndex < gpio.length; gpioIndex++) {
if (pinsToUpdate & (1 << gpioIndex)) {
gpio[gpioIndex].checkForUpdates();
}
}
}
}
}