diff --git a/demo/emulator-run.ts b/demo/emulator-run.ts index e18cc9f0db..481b8cb308 100644 --- a/demo/emulator-run.ts +++ b/demo/emulator-run.ts @@ -18,5 +18,6 @@ mcu.uart[0].onByte = (value) => { process.stdout.write(new Uint8Array([value])); }; -mcu.core.PC = 0x10000000; +mcu.core0.PC = 0x10000000; +mcu.core1.PC = 0x10000000; mcu.execute(); diff --git a/demo/micropython-run.ts b/demo/micropython-run.ts index 5f49c1d47a..695899a65d 100644 --- a/demo/micropython-run.ts +++ b/demo/micropython-run.ts @@ -74,5 +74,6 @@ process.stdin.on('data', (chunk) => { } }); -mcu.core.PC = 0x10000000; +mcu.core0.PC = 0x10000000; +mcu.core1.PC = 0x10000000; mcu.execute(); diff --git a/src/core.ts b/src/core.ts new file mode 100644 index 0000000000..de2ccd9f47 --- /dev/null +++ b/src/core.ts @@ -0,0 +1,4 @@ +export enum Core { + Core0, + Core1, +} diff --git a/src/cortex-m0-core.ts b/src/cortex-m0-core.ts index 6107093ab4..bbcaa05eb0 100644 --- a/src/cortex-m0-core.ts +++ b/src/cortex-m0-core.ts @@ -88,6 +88,11 @@ export class CortexM0Core { SHPR2 = 0; SHPR3 = 0; + public onSEV?: () => void; + public onBreak?: (code: number) => void; + + stopped = true; + constructor(readonly rp2040: RP2040) { this.SP = 0xfffffffc; this.bankedSP = 0xfffffffc; @@ -724,7 +729,8 @@ export class CortexM0Core { else if (opcode >> 8 === 0b10111110) { const imm8 = opcode & 0xff; this.breakRewind = 2; - this.rp2040.onBreak(imm8); + this.stopped = true; + this.onBreak?.(imm8); } // BL else if (opcode >> 11 === 0b11110 && opcode2 >> 14 === 0b11 && ((opcode2 >> 12) & 0x1) == 1) { @@ -1132,6 +1138,7 @@ export class CortexM0Core { // SEV else if (opcode === 0b1011111101000000) { this.logger.info(LOG_NAME, 'SEV'); + this.onSEV?.(); } // STMIA else if (opcode >> 11 === 0b11000) { @@ -1266,14 +1273,16 @@ export class CortexM0Core { else if (opcode >> 8 == 0b11011110) { const imm8 = opcode & 0xff; this.breakRewind = 2; - this.rp2040.onBreak(imm8); + this.stopped = true; + this.onBreak?.(imm8); } // UDF (Encoding T2) else if (opcode >> 4 === 0b111101111111 && opcode2 >> 12 === 0b1010) { const imm4 = opcode & 0xf; const imm12 = opcode2 & 0xfff; this.breakRewind = 4; - this.rp2040.onBreak((imm4 << 12) | imm12); + this.stopped = true; + this.onBreak?.((imm4 << 12) | imm12); this.PC += 2; } // UXTB diff --git a/src/gdb/gdb-server.ts b/src/gdb/gdb-server.ts index a9294fc30d..a0053530c4 100644 --- a/src/gdb/gdb-server.ts +++ b/src/gdb/gdb-server.ts @@ -8,6 +8,7 @@ import { SYSM_CONTROL, SYSM_MSP, SYSM_PRIMASK, SYSM_PSP } from '../cortex-m0-cor import { RP2040 } from '../rp2040'; import { ConsoleLogger, Logger, LogLevel } from '../utils/logging'; import { GDBConnection } from './gdb-connection'; +import { Core } from '../core'; import { decodeHexBuf, encodeHexBuf, @@ -86,7 +87,7 @@ export class GDBServer { processGDBMessage(cmd: string) { const { rp2040 } = this; - const { core } = rp2040; + const { core0: core } = rp2040; if (cmd === 'Hg0') { return gdbMessage('OK'); } @@ -128,7 +129,7 @@ export class GDBServer { return gdbMessage('vCont;c;C;s;S'); } if (cmd.startsWith('vCont;c')) { - if (!rp2040.executing) { + if (!rp2040.executing(Core.Core0)) { rp2040.execute(); } return; @@ -145,7 +146,7 @@ export class GDBServer { break; case 'c': - if (!rp2040.executing) { + if (!rp2040.executing(Core.Core0)) { rp2040.execute(); } return gdbMessage('OK'); @@ -260,9 +261,9 @@ export class GDBServer { addConnection(connection: GDBConnection) { this.connections.add(connection); - this.rp2040.onBreak = () => { + this.rp2040.core0.onBreak = () => { this.rp2040.stop(); - this.rp2040.core.PC -= this.rp2040.core.breakRewind; + this.rp2040.core0.PC -= this.rp2040.core0.breakRewind; for (const connection of this.connections) { connection.onBreakpoint(); } diff --git a/src/instructions.spec.ts b/src/instructions.spec.ts index cf3d73f22f..48b368d65a 100644 --- a/src/instructions.spec.ts +++ b/src/instructions.spec.ts @@ -978,22 +978,22 @@ describe('Cortex-M0+ Instruction Set', () => { it('should execute a `udf 1` instruction', () => { const breakMock = jest.fn(); const rp2040 = new RP2040(); - rp2040.core.PC = 0x20000000; + rp2040.core0.PC = 0x20000000; rp2040.writeUint16(0x20000000, opcodeUDF(0x1)); - rp2040.onBreak = breakMock; + rp2040.core0.onBreak = breakMock; rp2040.step(); - expect(rp2040.core.PC).toEqual(0x20000002); + expect(rp2040.core0.PC).toEqual(0x20000002); expect(breakMock).toHaveBeenCalledWith(1); }); it('should execute a `udf.w #0` (T2 encoding) instruction', () => { const breakMock = jest.fn(); const rp2040 = new RP2040(); - rp2040.core.PC = 0x20000000; + rp2040.core0.PC = 0x20000000; rp2040.writeUint32(0x20000000, opcodeUDF2(0)); - rp2040.onBreak = breakMock; + rp2040.core0.onBreak = breakMock; rp2040.step(); - expect(rp2040.core.PC).toEqual(0x20000004); + expect(rp2040.core0.PC).toEqual(0x20000004); expect(breakMock).toHaveBeenCalledWith(0); }); @@ -1431,13 +1431,13 @@ describe('Cortex-M0+ Instruction Set', () => { await cpu.singleStep(); if (cpu instanceof RP2040TestDriver) { - expect(cpu.rp2040.core.pendingSVCall).toEqual(true); + expect(cpu.rp2040.core0.pendingSVCall).toEqual(true); } await cpu.singleStep(); // SVCall handler should run here const registers2 = await cpu.readRegisters(); if (cpu instanceof RP2040TestDriver) { - expect(cpu.rp2040.core.pendingSVCall).toEqual(false); + expect(cpu.rp2040.core0.pendingSVCall).toEqual(false); } expect(registers2.pc).toEqual(SVCALL_HANDLER + 2); expect(registers2.r0).toEqual(0x55); diff --git a/src/peripherals/peripheral.ts b/src/peripherals/peripheral.ts index fac3eb9744..b859a6b472 100644 --- a/src/peripherals/peripheral.ts +++ b/src/peripherals/peripheral.ts @@ -1,4 +1,5 @@ import { RP2040 } from '../rp2040'; +import { Core } from '../core'; const ATOMIC_NORMAL = 0; const ATOMIC_XOR = 1; @@ -21,7 +22,9 @@ export function atomicUpdate(currentValue: number, atomicType: number, newValue: export interface Peripheral { readUint32(offset: number): number; + readUint32ViaCore(offset: number, core: Core): number; writeUint32(offset: number, value: number): void; + writeUint32ViaCore(offset: number, value: number, core: Core): void; writeUint32Atomic(offset: number, value: number, atomicType: number): void; } @@ -38,10 +41,19 @@ export class BasePeripheral implements Peripheral { return 0xffffffff; } + readUint32ViaCore(offset: number, core: Core) { + this.warn(`Unimplemented peripheral readvia ${core} from ${offset.toString(16)}`); + return 0xffffffff; + } + writeUint32(offset: number, value: number) { this.warn(`Unimplemented peripheral write to ${offset.toString(16)}: ${value}`); } + writeUint32ViaCore(offset: number, value: number, core: Core) { + this.warn(`Unimplemented peripheral write via ${core} to ${offset.toString(16)}: ${value}`); + } + writeUint32Atomic(offset: number, value: number, atomicType: number) { this.rawWriteValue = value; const newValue = diff --git a/src/peripherals/ppb.ts b/src/peripherals/ppb.ts index 17a9acd57b..98f325630f 100644 --- a/src/peripherals/ppb.ts +++ b/src/peripherals/ppb.ts @@ -1,6 +1,7 @@ import { IClockTimer } from '../clock/clock'; import { MAX_HARDWARE_IRQ } from '../irq'; import { BasePeripheral, Peripheral } from './peripheral'; +import { Core } from '../core'; export const CPUID = 0xd00; export const ICSR = 0xd04; @@ -53,9 +54,9 @@ export class RPPPB extends BasePeripheral implements Peripheral { systickReload = 0; systickTimer: IClockTimer | null = null; - readUint32(offset: number) { + readUint32ViaCore(offset: number, coreIndex: Core) { const { rp2040 } = this; - const { core } = rp2040; + const core = coreIndex == Core.Core0 ? rp2040.core0 : rp2040.core1; switch (offset) { case CPUID: @@ -132,12 +133,12 @@ export class RPPPB extends BasePeripheral implements Peripheral { case SYST_CALIB: return 0x0000270f; } - return super.readUint32(offset); + return super.readUint32ViaCore(offset, coreIndex); } - writeUint32(offset: number, value: number) { + writeUint32ViaCore(offset: number, value: number, coreIndex: Core) { const { rp2040 } = this; - const { core } = rp2040; + const core = coreIndex == Core.Core0 ? rp2040.core0 : rp2040.core1; const hardwareInterruptMask = (1 << MAX_HARDWARE_IRQ) - 1; @@ -246,7 +247,7 @@ export class RPPPB extends BasePeripheral implements Peripheral { return; default: - super.writeUint32(offset, value); + super.writeUint32ViaCore(offset, value, coreIndex); } } } diff --git a/src/peripherals/syscfg.ts b/src/peripherals/syscfg.ts index 88ec9caca0..544d339d0f 100644 --- a/src/peripherals/syscfg.ts +++ b/src/peripherals/syscfg.ts @@ -8,7 +8,9 @@ export class RP2040SysCfg extends BasePeripheral implements Peripheral { readUint32(offset: number) { switch (offset) { case PROC0_NMI_MASK: - return this.rp2040.core.interruptNMIMask; + return this.rp2040.core0.interruptNMIMask; + case PROC1_NMI_MASK: + return this.rp2040.core1.interruptNMIMask; } return super.readUint32(offset); } @@ -16,7 +18,10 @@ export class RP2040SysCfg extends BasePeripheral implements Peripheral { writeUint32(offset: number, value: number) { switch (offset) { case PROC0_NMI_MASK: - this.rp2040.core.interruptNMIMask = value; + this.rp2040.core0.interruptNMIMask = value; + break; + case PROC1_NMI_MASK: + this.rp2040.core1.interruptNMIMask = value; break; default: diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index 726f1716a9..141bdccbeb 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -39,16 +39,16 @@ describe('RPTimer', () => { expect(rp2040.readUint32(ARMED)).toEqual(0); expect(rp2040.readUint32(INTR)).toEqual(0x8); expect(rp2040.readUint32(INTS)).toEqual(0); - expect(rp2040.core.pendingInterrupts).toBe(0); + expect(rp2040.core0.pendingInterrupts).toBe(0); // Enable the interrupts for all alarms rp2040.writeUint32(INTE, 0xff); expect(rp2040.readUint32(INTS)).toEqual(0x8); - expect(rp2040.core.pendingInterrupts).toBe(0x8); - expect(rp2040.core.interruptsUpdated).toEqual(true); + expect(rp2040.core0.pendingInterrupts).toBe(0x8); + expect(rp2040.core0.interruptsUpdated).toEqual(true); // Clear the alarm's interrupt rp2040.writeUint32(INTR_CLEAR, 0x8); expect(rp2040.readUint32(INTS)).toEqual(0); - expect(rp2040.core.pendingInterrupts).toBe(0); + expect(rp2040.core0.pendingInterrupts).toBe(0); }); it('should generate an interrupt if INTF is 1 even when the INTE bit is 0', () => { diff --git a/src/rp2040.spec.ts b/src/rp2040.spec.ts index c19d3acab3..c713974c74 100644 --- a/src/rp2040.spec.ts +++ b/src/rp2040.spec.ts @@ -16,8 +16,8 @@ describe('RP2040', () => { it(`should initialize PC and SP according to bootrom's vector table`, () => { const rp2040 = new RP2040(); rp2040.loadBootrom(new Uint32Array([0x20041f00, 0xee])); - expect(rp2040.core.SP).toEqual(0x20041f00); - expect(rp2040.core.PC).toEqual(0xee); + expect(rp2040.core0.SP).toEqual(0x20041f00); + expect(rp2040.core0.PC).toEqual(0xee); }); describe('IO Register Writes', () => { @@ -56,26 +56,26 @@ describe('RP2040', () => { const INT1_HANDLER = 0x10000100; const EXC_INT1 = 16 + 1; const rp2040 = new RP2040(); - rp2040.core.SP = 0x20004000; - rp2040.core.PC = 0x10004001; - rp2040.core.registers[r0] = 0x44; - rp2040.core.pendingInterrupts = INT1; - rp2040.core.enabledInterrupts = INT1; - rp2040.core.interruptsUpdated = true; + rp2040.core0.SP = 0x20004000; + rp2040.core0.PC = 0x10004001; + rp2040.core0.registers[r0] = 0x44; + rp2040.core0.pendingInterrupts = INT1; + rp2040.core0.enabledInterrupts = INT1; + rp2040.core0.interruptsUpdated = true; rp2040.writeUint32(VTOR, 0x10000000); rp2040.writeUint32(0x10000000 + EXC_INT1 * 4, INT1_HANDLER); rp2040.writeUint16(INT1_HANDLER, opcodeMOVS(r0, 0x55)); rp2040.writeUint16(INT1_HANDLER + 2, opcodeBX(lr)); // Exception handler should start at this point. rp2040.step(); // MOVS r0, 0x55 - expect(rp2040.core.IPSR).toEqual(EXC_INT1); - expect(rp2040.core.PC).toEqual(INT1_HANDLER + 2); - expect(rp2040.core.registers[r0]).toEqual(0x55); + expect(rp2040.core0.IPSR).toEqual(EXC_INT1); + expect(rp2040.core0.PC).toEqual(INT1_HANDLER + 2); + expect(rp2040.core0.registers[r0]).toEqual(0x55); rp2040.step(); // BX lr // Exception handler should return at this point. - expect(rp2040.core.PC).toEqual(0x10004000); - expect(rp2040.core.registers[r0]).toEqual(0x44); - expect(rp2040.core.IPSR).toEqual(0); + expect(rp2040.core0.PC).toEqual(0x10004000); + expect(rp2040.core0.registers[r0]).toEqual(0x44); + expect(rp2040.core0.IPSR).toEqual(0); }); it('should return correctly from exception with POP {lr}', () => { @@ -83,12 +83,12 @@ describe('RP2040', () => { const INT1_HANDLER = 0x10000100; const EXC_INT1 = 16 + 1; const rp2040 = new RP2040(); - rp2040.core.SP = 0x20004000; - rp2040.core.PC = 0x10004001; - rp2040.core.registers[r4] = 105; - rp2040.core.pendingInterrupts = INT1; - rp2040.core.enabledInterrupts = INT1; - rp2040.core.interruptsUpdated = true; + rp2040.core0.SP = 0x20004000; + rp2040.core0.PC = 0x10004001; + rp2040.core0.registers[r4] = 105; + rp2040.core0.pendingInterrupts = INT1; + rp2040.core0.enabledInterrupts = INT1; + rp2040.core0.interruptsUpdated = true; rp2040.writeUint32(VTOR, 0x10000000); rp2040.writeUint32(0x10000000 + EXC_INT1 * 4, INT1_HANDLER); rp2040.writeUint16(INT1_HANDLER, opcodePUSH(true, 0b01110000)); @@ -96,15 +96,15 @@ describe('RP2040', () => { rp2040.writeUint16(INT1_HANDLER + 4, opcodePOP(true, 0b01110000)); // Exception handler should start at this point. rp2040.step(); // push {r4, r5, r6, lr} - expect(rp2040.core.IPSR).toEqual(EXC_INT1); - expect(rp2040.core.PC).toEqual(INT1_HANDLER + 2); + expect(rp2040.core0.IPSR).toEqual(EXC_INT1); + expect(rp2040.core0.PC).toEqual(INT1_HANDLER + 2); rp2040.step(); // mov r4, 42 - expect(rp2040.core.registers[r4]).toEqual(42); + expect(rp2040.core0.registers[r4]).toEqual(42); rp2040.step(); // pop {r4, r5, r6, pc} // Exception handler should return at this point. - expect(rp2040.core.PC).toEqual(0x10004000); - expect(rp2040.core.registers[r4]).toEqual(105); - expect(rp2040.core.IPSR).toEqual(0); + expect(rp2040.core0.PC).toEqual(0x10004000); + expect(rp2040.core0.registers[r4]).toEqual(105); + expect(rp2040.core0.IPSR).toEqual(0); }); it('should clear the pending interrupt flag in exceptionEntry() for user IRQs (> 25)', () => { @@ -112,18 +112,18 @@ describe('RP2040', () => { const INT31_HANDLER = 0x10003100; const EXC_INT31 = 16 + 31; const rp2040 = new RP2040(); - rp2040.core.SP = 0x20004000; - rp2040.core.PC = 0x10004001; + rp2040.core0.SP = 0x20004000; + rp2040.core0.PC = 0x10004001; rp2040.writeUint32(NVIC_ISPR, INT31); // Set IRQ31 to pending - rp2040.core.enabledInterrupts = INT31; - rp2040.core.interruptsUpdated = true; + rp2040.core0.enabledInterrupts = INT31; + rp2040.core0.interruptsUpdated = true; rp2040.writeUint32(VTOR, 0x10000000); rp2040.writeUint32(0x10000000 + EXC_INT31 * 4, INT31_HANDLER); rp2040.writeUint16(INT31_HANDLER, opcodeNOP()); - expect(rp2040.core.pendingInterrupts).toEqual(INT31); + expect(rp2040.core0.pendingInterrupts).toEqual(INT31); // Exception handler should start at this point. rp2040.step(); // nop - expect(rp2040.core.pendingInterrupts).toEqual(0); // interrupt flag has been cleared + expect(rp2040.core0.pendingInterrupts).toEqual(0); // interrupt flag has been cleared expect(rp2040.readUint32(NVIC_ISPR)).toEqual(0); }); }); @@ -131,14 +131,14 @@ describe('RP2040', () => { describe('NVIC registers', () => { it('writing to NVIC_ISPR should set the corresponding pending interrupt bits', () => { const rp2040 = new RP2040(); - rp2040.core.pendingInterrupts = 0x1; + rp2040.core0.pendingInterrupts = 0x1; rp2040.writeUint32(NVIC_ISPR, 0x10); - expect(rp2040.core.pendingInterrupts).toBe(0x11); + expect(rp2040.core0.pendingInterrupts).toBe(0x11); }); it('writing to NVIC_ICPR should clear corresponding pending interrupt bits', () => { const rp2040 = new RP2040(); - rp2040.core.pendingInterrupts = 0xff00000f; + rp2040.core0.pendingInterrupts = 0xff00000f; rp2040.writeUint32(NVIC_ICPR, 0x1000000f); // Only the high 6 bits are actually cleared (see commit 5bc96994 for details) expect(rp2040.readUint32(NVIC_ISPR)).toBe(0xef00000f); @@ -146,28 +146,28 @@ describe('RP2040', () => { it('writing to NVIC_ISER should set the corresponding enabled interrupt bits', () => { const rp2040 = new RP2040(); - rp2040.core.enabledInterrupts = 0x1; + rp2040.core0.enabledInterrupts = 0x1; rp2040.writeUint32(NVIC_ISER, 0x10); - expect(rp2040.core.enabledInterrupts).toBe(0x11); + expect(rp2040.core0.enabledInterrupts).toBe(0x11); }); it('writing to NVIC_ICER should clear corresponding enabled interrupt bits', () => { const rp2040 = new RP2040(); - rp2040.core.enabledInterrupts = 0xff; + rp2040.core0.enabledInterrupts = 0xff; rp2040.writeUint32(NVIC_ICER, 0x10); - expect(rp2040.core.enabledInterrupts).toBe(0xef); + expect(rp2040.core0.enabledInterrupts).toBe(0xef); }); it('reading from NVIC_ISER/NVIC_ICER should return the current enabled interrupt bits', () => { const rp2040 = new RP2040(); - rp2040.core.enabledInterrupts = 0x1; + rp2040.core0.enabledInterrupts = 0x1; expect(rp2040.readUint32(NVIC_ISER)).toEqual(0x1); expect(rp2040.readUint32(NVIC_ICER)).toEqual(0x1); }); it('reading from NVIC_ISPR/NVIC_ICPR should return the current enabled interrupt bits', () => { const rp2040 = new RP2040(); - rp2040.core.pendingInterrupts = 0x2; + rp2040.core0.pendingInterrupts = 0x2; expect(rp2040.readUint32(NVIC_ISPR)).toEqual(0x2); expect(rp2040.readUint32(NVIC_ICPR)).toEqual(0x2); }); @@ -176,7 +176,7 @@ describe('RP2040', () => { const rp2040 = new RP2040(); // Set the priority of interrupt number 14 to 2 rp2040.writeUint32(0xe000e40c, 0x00800000); - const { interruptPriorities } = rp2040.core; + const { interruptPriorities } = rp2040.core0; expect(interruptPriorities[0] | 0).toEqual(~(1 << 14)); expect(interruptPriorities[1]).toEqual(0); expect(interruptPriorities[2]).toEqual(1 << 14); @@ -186,10 +186,10 @@ describe('RP2040', () => { it('should return the correct interrupt priorities when reading from NVIC_IPR5', () => { const rp2040 = new RP2040(); - rp2040.core.interruptPriorities[0] = 0; - rp2040.core.interruptPriorities[1] = 0x001fffff; // interrupts 0 ... 20 - rp2040.core.interruptPriorities[2] = 0x00200000; // interrupt 21 - rp2040.core.interruptPriorities[3] = 0xffc00000; // interrupt 22 ... 31 + rp2040.core0.interruptPriorities[0] = 0; + rp2040.core0.interruptPriorities[1] = 0x001fffff; // interrupts 0 ... 20 + rp2040.core0.interruptPriorities[2] = 0x00200000; // interrupt 21 + rp2040.core0.interruptPriorities[3] = 0xffc00000; // interrupt 22 ... 31 // Set the priority of interrupt number 14 to 2 expect(rp2040.readUint32(0xe000e414)).toEqual(0xc0c08040 | 0); }); diff --git a/src/rp2040.ts b/src/rp2040.ts index 1dbf24de85..f8caf5425c 100644 --- a/src/rp2040.ts +++ b/src/rp2040.ts @@ -23,6 +23,7 @@ import { RPTimer } from './peripherals/timer'; import { RPUART } from './peripherals/uart'; import { RPUSBController } from './peripherals/usb'; import { RPSIO } from './sio'; +import { Core } from './core'; import { ConsoleLogger, Logger, LogLevel } from './utils/logging'; import { RPTBMAN } from './peripherals/tbman'; @@ -48,7 +49,8 @@ export class RP2040 { readonly usbDPRAM = new Uint8Array(4 * KB); readonly usbDPRAMView = new DataView(this.usbDPRAM.buffer); - readonly core = new CortexM0Core(this); + readonly core0 = new CortexM0Core(this); + readonly core1 = new CortexM0Core(this); /* Clocks */ clkSys = 125 * MHz; @@ -112,8 +114,6 @@ export class RP2040 { ]; readonly usbCtrl = new RPUSBController(this, 'USB'); - private stopped = true; - public logger: Logger = new ConsoleLogger(LogLevel.Debug, true); private executeTimer: NodeJS.Timeout | null = null; @@ -153,25 +153,33 @@ export class RP2040 { 0x50300: this.pio[1], }; - // Debugging - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public onBreak = (code: number) => { - // TODO: raise HardFault exception - // console.error('Breakpoint!', code); - this.stopped = true; - }; - constructor(readonly clock: IClock = new RealtimeClock()) { this.reset(); + this.core0.onSEV = () => { + if (this.core1.waiting) { + this.core1.waiting = false; + } else { + this.core1.eventRegistered = true; + } + }; + this.core1.onSEV = () => { + if (this.core0.waiting) { + this.core0.waiting = false; + } else { + this.core0.eventRegistered = true; + } + }; } + isCore0Running = true; loadBootrom(bootromData: Uint32Array) { this.bootrom.set(bootromData); this.reset(); } reset() { - this.core.reset(); + this.core0.reset(); + this.core1.reset(); this.pwm.reset(); this.flash.fill(0xff); } @@ -186,6 +194,7 @@ export class RP2040 { } const { bootrom } = this; + const core = this.isCore0Running ? Core.Core0 : Core.Core1; if (address < bootrom.length * 4) { return bootrom[address / 4]; } else if ( @@ -201,9 +210,9 @@ export class RP2040 { ) { return this.usbDPRAMView.getUint32(address - DPRAM_START_ADDRESS, true); } else if (address >>> 12 === 0xe000e) { - return this.ppb.readUint32(address & 0xfff); + return this.ppb.readUint32ViaCore(address & 0xfff, core); } else if (address >= SIO_START_ADDRESS && address < SIO_START_ADDRESS + 0x10000000) { - return this.sio.readUint32(address - SIO_START_ADDRESS); + return this.sio.readUint32(address - SIO_START_ADDRESS, core); } const peripheral = this.findPeripheral(address); @@ -245,6 +254,7 @@ export class RP2040 { writeUint32(address: number, value: number) { address = address >>> 0; const { bootrom } = this; + const core = this.isCore0Running ? Core.Core0 : Core.Core1; const peripheral = this.findPeripheral(address); if (peripheral) { const atomicType = (address & 0x3000) >> 12; @@ -267,9 +277,9 @@ export class RP2040 { this.usbDPRAMView.setUint32(offset, value, true); this.usbCtrl.DPRAMUpdated(offset, value); } else if (address >= SIO_START_ADDRESS && address < SIO_START_ADDRESS + 0x10000000) { - this.sio.writeUint32(address - SIO_START_ADDRESS, value); + this.sio.writeUint32(address - SIO_START_ADDRESS, value, core); } else if (address >>> 12 === 0xe000e) { - this.ppb.writeUint32(address & 0xfff, value); + this.ppb.writeUint32ViaCore(address & 0xfff, value, core); } else { this.logger.warn(LOG_NAME, `Write to undefined address: ${address.toString(16)}`); } @@ -336,7 +346,19 @@ export class RP2040 { } setInterrupt(irq: number, value: boolean) { - this.core.setInterrupt(irq, value); + this.core0.setInterrupt(irq, value); + this.core1.setInterrupt(irq, value); + } + + setInterruptCore(irq: number, value: boolean, core: Core) { + switch (core) { + case Core.Core0: + this.core0.setInterrupt(irq, value); + break; + case Core.Core1: + this.core1.setInterrupt(irq, value); + break; + } } updateIOInterrupt() { @@ -350,23 +372,39 @@ export class RP2040 { } step() { - this.core.executeInstruction(); + this.core0.executeInstruction(); + this.core1.executeInstruction(); } execute() { + this.core0.stopped = false; + this.core1.stopped = false; + setTimeout(() => this.executeInternal(), 0); + } + + private executeInternal() { this.clock.resume(); this.executeTimer = null; - this.stopped = false; - for (let i = 0; i < 100000 && !this.stopped && !this.core.waiting; i++) { - this.core.executeInstruction(); - } - if (!this.stopped) { - this.executeTimer = setTimeout(() => this.execute(), 0); + let idle = false; + for (let i = 0; i < 1000 && !idle; i++) { + idle = true; + if (!this.core0.stopped && !this.core0.waiting) { + idle = false; + this.isCore0Running = true; + this.core0.executeInstruction(); + } + if (!this.core1.stopped && !this.core1.waiting) { + idle = false; + this.isCore0Running = false; + this.core1.executeInstruction(); + } } + this.executeTimer = setTimeout(() => this.executeInternal(), 0); } stop() { - this.stopped = true; + this.core0.stopped = true; + this.core1.stopped = true; if (this.executeTimer != null) { clearTimeout(this.executeTimer); this.executeTimer = null; @@ -374,7 +412,12 @@ export class RP2040 { this.clock.pause(); } - get executing() { - return !this.stopped; + executing(core: Core): boolean { + switch (core) { + case Core.Core0: + return this.core0.stopped; + case Core.Core1: + return this.core1.stopped; + } } } diff --git a/src/sio-core.ts b/src/sio-core.ts new file mode 100644 index 0000000000..515b7d1c25 --- /dev/null +++ b/src/sio-core.ts @@ -0,0 +1,396 @@ +import { RP2040 } from './rp2040'; +import { Core } from './core'; +import { Interpolator } from './interpolator'; +import { FIFO } from './utils/fifo'; +import { IRQ } from './irq'; + +//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 + +// FIFO +const FIFO_ST_VLD_BITS = 0x01; +const FIFO_ST_RDY_BITS = 0x02; +const FIFO_ST_WOF_BITS = 0x04; +const FIFO_ST_ROE_BITS = 0x08; + +const FIFO_ST = 0x50; +const FIFO_WR = 0x54; +const FIFO_RD = 0x58; + +export class RPSIOCore { + divDividend = 0; + divDivisor = 1; + divQuotient = 0; + divRemainder = 0; + divCSR = 0; + + interp0 = new Interpolator(0); + interp1 = new Interpolator(1); + + ROE = false; + WOF = false; + + static create2Cores(rp2040: RP2040) { + const rxFIFO = new FIFO(8); + const txFIFO = new FIFO(8); + const core0 = new RPSIOCore(rp2040, rxFIFO, txFIFO, Core.Core0); + const core1 = new RPSIOCore(rp2040, txFIFO, rxFIFO, Core.Core1); + return [core0, core1]; + } + + private constructor( + private readonly rp2040: RP2040, + private readonly rxFIFO: FIFO, + private readonly txFIFO: FIFO, + private readonly core: Core + ) {} + + readUint32(offset: number) { + switch (offset) { + 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; + case FIFO_ST: { + let value = 0; + if (!this.rxFIFO.empty) { + value |= FIFO_ST_VLD_BITS; + } + if (!this.txFIFO.full) { + value |= FIFO_ST_RDY_BITS; + } + if (this.WOF) { + value |= FIFO_ST_WOF_BITS; + } + if (this.ROE) { + value |= FIFO_ST_ROE_BITS; + } + return value; + } + case FIFO_RD: + if (this.rxFIFO.empty) { + this.ROE = true; + switch (this.core) { + case Core.Core0: + this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); + break; + case Core.Core1: + this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); + } + return 0; + } + return this.rxFIFO.pull(); + default: + console.warn(`Read from invalid SIO address: ${offset.toString(16)} (${this.core})`); + return 0xffffffff; + } + } + + writeUint32(offset: number, value: number) { + switch (offset) { + 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; + case FIFO_ST: + if (value | FIFO_ST_WOF_BITS) { + this.WOF = false; + } + if (value | FIFO_ST_ROE_BITS) { + this.ROE = false; + } + if (!this.WOF && !this.ROE && this.rxFIFO.empty) { + switch (this.core) { + case Core.Core0: + this.rp2040.setInterruptCore(IRQ.SIO_PROC0, false, Core.Core0); + break; + case Core.Core1: + this.rp2040.setInterruptCore(IRQ.SIO_PROC1, false, Core.Core1); + break; + } + } + break; + case FIFO_WR: + if (this.txFIFO.full) { + this.WOF = true; + switch (this.core) { + case Core.Core0: + this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); + break; + case Core.Core1: + this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); + break; + } + } else { + this.txFIFO.push(value); + switch (this.core) { + case Core.Core0: + this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); + break; + case Core.Core1: + this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); + break; + } + } + break; + default: + console.warn( + `Write to invalid SIO address: ${offset.toString(16)}, value=${value.toString(16)} (${ + this.core + })` + ); + break; + } + } + + private updateHardwareDivider(signed: boolean) { + 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; + switch (this.core) { + case Core.Core0: + this.rp2040.core0.cycles += 8; + break; + case Core.Core1: + this.rp2040.core1.cycles += 8; + break; + } + } +} diff --git a/src/sio.ts b/src/sio.ts index 103aa5a488..acd3972ed4 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -1,5 +1,6 @@ import { RP2040 } from './rp2040'; -import { Interpolator } from './interpolator'; +import { Core } from './core'; +import { RPSIOCore } from './sio-core'; const CPUID = 0x000; @@ -25,49 +26,6 @@ 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; @@ -78,35 +36,17 @@ export class RPSIO { gpioOutputEnable = 0; qspiGpioValue = 0; qspiGpioOutputEnable = 0; - divDividend = 0; - divDivisor = 1; - divQuotient = 0; - divRemainder = 0; - divCSR = 0; spinLock = 0; - interp0 = new Interpolator(0); - interp1 = new Interpolator(1); - - constructor(private readonly rp2040: RP2040) {} + readonly core0; + readonly core1; - updateHardwareDivider(signed: boolean) { - 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; + constructor(private readonly rp2040: RP2040) { + const cores = RPSIOCore.create2Cores(rp2040); + this.core0 = cores[0]; + this.core1 = cores[1]; } - readUint32(offset: number) { + readUint32(offset: number, core: Core) { if (offset >= SPINLOCK0 && offset <= SPINLOCK31) { const bitIndexMask = 1 << ((offset - SPINLOCK0) / 4); if (this.spinLock & bitIndexMask) { @@ -151,109 +91,25 @@ export class RPSIO { 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; + switch (core) { + case Core.Core0: + return 0; + case Core.Core1: + return 1; + } + break; 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; + switch (core) { + case Core.Core0: + return this.core0.readUint32(offset); + case Core.Core1: + return this.core1.readUint32(offset); + } } - writeUint32(offset: number, value: number) { + writeUint32(offset: number, value: number, core: Core) { if (offset >= SPINLOCK0 && offset <= SPINLOCK31) { const bitIndexMask = ~(1 << ((offset - SPINLOCK0) / 4)); this.spinLock &= bitIndexMask; @@ -310,112 +166,15 @@ export class RPSIO { 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)}` - ); + switch (core) { + case Core.Core0: + this.core0.writeUint32(offset, value); + break; + case Core.Core1: + this.core1.writeUint32(offset, value); + break; + } } const pinsToUpdate = (this.gpioValue ^ prevGpioValue) | (this.gpioOutputEnable ^ prevGpioOutputEnable); diff --git a/test-utils/test-driver-rp2040.ts b/test-utils/test-driver-rp2040.ts index acdeacffd4..39b9234614 100644 --- a/test-utils/test-driver-rp2040.ts +++ b/test-utils/test-driver-rp2040.ts @@ -15,7 +15,7 @@ export class RP2040TestDriver implements ICortexTestDriver { } async setPC(pcValue: number) { - this.rp2040.core.PC = pcValue; + this.rp2040.core0.PC = pcValue; } async writeUint8(address: number, value: number) { @@ -32,7 +32,7 @@ export class RP2040TestDriver implements ICortexTestDriver { async setRegisters(registers: Partial) { const { rp2040 } = this; - const { core } = rp2040; + const core = rp2040.core0; for (const key of Object.keys(registers) as ICortexRegisterName[]) { const value = registers[key] as number; const boolValue = registers[key] as boolean; @@ -121,7 +121,7 @@ export class RP2040TestDriver implements ICortexTestDriver { } async readRegisters(): Promise { - const { core } = this.rp2040; + const core = this.rp2040.core0; const { registers, xPSR } = core; return { r0: registers[0],