From 190cba8932913cacbc788256edeb3b8b0e5c4d0e Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 28 Nov 2022 11:51:58 +0000 Subject: [PATCH 01/25] Implement TBMAN --- src/peripherals/tbman.ts | 14 ++++++++++++++ src/rp2040.ts | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/peripherals/tbman.ts diff --git a/src/peripherals/tbman.ts b/src/peripherals/tbman.ts new file mode 100644 index 0000000..fdef394 --- /dev/null +++ b/src/peripherals/tbman.ts @@ -0,0 +1,14 @@ +import { BasePeripheral, Peripheral } from './peripheral'; +const PLATFORM = 0; +const ASIC = 1; + +export class RPTBMAN extends BasePeripheral implements Peripheral { + readUint32(offset: number) { + switch (offset) { + case PLATFORM: + return ASIC; + default: + return super.readUint32(offset); + } + } +} \ No newline at end of file diff --git a/src/rp2040.ts b/src/rp2040.ts index 2fd4681..527e115 100644 --- a/src/rp2040.ts +++ b/src/rp2040.ts @@ -24,6 +24,7 @@ import { RPUART } from './peripherals/uart'; import { RPUSBController } from './peripherals/usb'; import { RPSIO } from './sio'; import { ConsoleLogger, Logger, LogLevel } from './utils/logging'; +import { RPTBMAN } from './peripherals/tbman'; export const FLASH_START_ADDRESS = 0x10000000; export const FLASH_END_ADDRESS = 0x14000000; @@ -145,7 +146,7 @@ export class RP2040 { 0x4005c: new RP2040RTC(this, 'RTC_BASE'), 0x40060: new UnimplementedPeripheral(this, 'ROSC_BASE'), 0x40064: new UnimplementedPeripheral(this, 'VREG_AND_CHIP_RESET_BASE'), - 0x4006c: new UnimplementedPeripheral(this, 'TBMAN_BASE'), + 0x4006c: new RPTBMAN(this, 'TBMAN_BASE'), 0x50000: this.dma, 0x50110: this.usbCtrl, 0x50200: this.pio[0], From de3601f592ec40f7681f02769b5d2be187236c3b Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 28 Nov 2022 11:52:57 +0000 Subject: [PATCH 02/25] Implement REF/SYS CTRL & SELECT for clock --- src/peripherals/clocks.ts | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/peripherals/clocks.ts b/src/peripherals/clocks.ts index f0da761..554531a 100644 --- a/src/peripherals/clocks.ts +++ b/src/peripherals/clocks.ts @@ -1,22 +1,43 @@ import { RP2040 } from '../rp2040'; import { BasePeripheral, Peripheral } from './peripheral'; +const CLK_REF_CTRL = 0x30; const CLK_REF_SELECTED = 0x38; +const CLK_SYS_CTRL = 0x3c; const CLK_SYS_SELECTED = 0x44; export class RPClocks extends BasePeripheral implements Peripheral { + refCtrl = 0; + sysCtrl = 0; constructor(rp2040: RP2040, name: string) { super(rp2040, name); } readUint32(offset: number) { switch (offset) { + case CLK_REF_CTRL: + return this.refCtrl; case CLK_REF_SELECTED: - return 1; - + return 1 << (this.refCtrl & 0x03); + case CLK_SYS_CTRL: + return this.sysCtrl; case CLK_SYS_SELECTED: - return 1; + return 1 << (this.sysCtrl & 0x01); } return super.readUint32(offset); } + + writeUint32(offset: number, value: number): void { + switch (offset) { + case CLK_REF_CTRL: + this.refCtrl = value; + break; + case CLK_SYS_CTRL: + this.sysCtrl = value; + break; + default: + super.writeUint32(offset, value); + break; + } + } } From e7a222cb898979bd2a52921b91790743d137a7a2 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 01:55:49 +0000 Subject: [PATCH 03/25] Define enum for cores --- src/core.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/core.ts diff --git a/src/core.ts b/src/core.ts new file mode 100644 index 0000000..fadfc21 --- /dev/null +++ b/src/core.ts @@ -0,0 +1,4 @@ +export enum Core { + Core0, + Core1, +} \ No newline at end of file From dfbeaf0830187bd9656dcd1b3bf5f9d8a63dda41 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 01:58:09 +0000 Subject: [PATCH 04/25] Add API for peripheral to read from core --- src/peripherals/peripheral.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/peripherals/peripheral.ts b/src/peripherals/peripheral.ts index fac3eb9..b859a6b 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 = From aca3a9cebc393b9f024a6afbf37066bb5cfd3813 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:01:10 +0000 Subject: [PATCH 05/25] Define onSEV event to wakeup another core --- src/cortex-m0-core.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cortex-m0-core.ts b/src/cortex-m0-core.ts index 15007c5..fb20ecc 100644 --- a/src/cortex-m0-core.ts +++ b/src/cortex-m0-core.ts @@ -88,6 +88,7 @@ export class CortexM0Core { SHPR2 = 0; SHPR3 = 0; + public onSEV?: () => void; constructor(readonly rp2040: RP2040) { this.SP = 0xfffffffc; this.bankedSP = 0xfffffffc; @@ -1137,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) { From 1b660e3e16141a5a5e20ec9e2bff3d40ecffd293 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:01:40 +0000 Subject: [PATCH 06/25] Define onBreak event for GDB --- src/cortex-m0-core.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/cortex-m0-core.ts b/src/cortex-m0-core.ts index fb20ecc..972ebd7 100644 --- a/src/cortex-m0-core.ts +++ b/src/cortex-m0-core.ts @@ -89,6 +89,10 @@ export class CortexM0Core { SHPR3 = 0; public onSEV?: () => void; + public onBreak?: (code: number) => void; + + stopped = true; + constructor(readonly rp2040: RP2040) { this.SP = 0xfffffffc; this.bankedSP = 0xfffffffc; @@ -719,7 +723,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) { @@ -1287,14 +1292,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 From c64c1412d52d9ba3fc26140f649eaabf604a6ba5 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:03:38 +0000 Subject: [PATCH 07/25] Implement read/write via core for ppb --- src/peripherals/ppb.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/peripherals/ppb.ts b/src/peripherals/ppb.ts index 17a9acd..2c9957e 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, _core: Core) { const { rp2040 } = this; - const { core } = rp2040; + const core = _core == 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, _core); } - writeUint32(offset: number, value: number) { + writeUint32ViaCore(offset: number, value: number, _core: Core) { const { rp2040 } = this; - const { core } = rp2040; + const core = _core == 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, _core); } } } From d5a1074293754460ea7d87a46a7e818a43caa979 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:05:57 +0000 Subject: [PATCH 08/25] Implement FIFO registers in SIO --- src/sio.ts | 153 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 11 deletions(-) diff --git a/src/sio.ts b/src/sio.ts index 1c167c7..d2da80c 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -1,7 +1,13 @@ import { RP2040 } from './rp2040'; import { Interpolator } from './interpolator'; +import { FIFO } from './utils/fifo'; +import { Core } from './core'; +import { IRQ } from './irq'; const CPUID = 0x000; +const FIFO_ST = 0x50; +const FIFO_WR = 0x54; +const FIFO_RD = 0x58; // GPIO const GPIO_IN = 0x004; // Input value for GPIO pins @@ -73,6 +79,11 @@ const SPINLOCK_ST = 0x5c; const SPINLOCK0 = 0x100; const SPINLOCK31 = 0x17c; +const FIFO_ST_VLD_BITS = 0x01; +const FIFO_ST_RDY_BITS = 0x02; +const FIFO_ST_WOF_BITS = 0x04; +const FIFO_ST_ROE_BITS = 0x08; + export class RPSIO { gpioValue = 0; gpioOutputEnable = 0; @@ -86,10 +97,22 @@ export class RPSIO { spinLock = 0; interp0 = new Interpolator(0); interp1 = new Interpolator(1); + // The meaning of FIFO is for core0 + readonly core0TxFIFO = new FIFO(8); + readonly core0RxFIFO = new FIFO(8); + readonly core1TxFIFO; + readonly core1RxFIFO; + core0ROE = false; + core0WOF = false; + core1ROE = false; + core1WOF = false; - constructor(private readonly rp2040: RP2040) {} + constructor(private readonly rp2040: RP2040) { + this.core1TxFIFO = this.core0RxFIFO; + this.core1RxFIFO = this.core0TxFIFO; + } - updateHardwareDivider(signed: boolean) { + updateHardwareDivider(signed: boolean, core: Core) { if (this.divDivisor == 0) { this.divQuotient = this.divDividend > 0 ? -1 : 1; this.divRemainder = this.divDividend; @@ -103,10 +126,18 @@ export class RPSIO { } } this.divCSR = 0b11; - this.rp2040.core.cycles += 8; + switch (core) + { + case Core.Core0: + this.rp2040.core0.cycles += 8; + break; + case Core.Core1: + this.rp2040.core1.cycles += 8; + break; + } } - readUint32(offset: number) { + readUint32(offset: number, core: Core) { if (offset >= SPINLOCK0 && offset <= SPINLOCK31) { const bitIndexMask = 1 << ((offset - SPINLOCK0) / 4); if (this.spinLock & bitIndexMask) { @@ -151,8 +182,60 @@ 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; + } + case FIFO_ST: + let value = 0; + switch (core) { + case Core.Core0: + if (!this.core0RxFIFO.empty) { + value |= FIFO_ST_VLD_BITS; + } + if (!this.core0TxFIFO.full) { + value |= FIFO_ST_RDY_BITS; + } + if (this.core0WOF) { + value |= FIFO_ST_WOF_BITS; + } + if (this.core0ROE) { + value |= FIFO_ST_ROE_BITS; + } + break; + case Core.Core1: + if (!this.core0TxFIFO.empty) { + value |= FIFO_ST_VLD_BITS; + } + if (!this.core0RxFIFO.full) { + value |= FIFO_ST_RDY_BITS; + } + if (this.core1WOF) { + value |= FIFO_ST_WOF_BITS; + } + if (this.core1ROE) { + value |= FIFO_ST_ROE_BITS; + } + break; + } + return value; + case FIFO_RD: + switch (core) { + case Core.Core0: + if (this.core0RxFIFO.empty) { + this.core0ROE = true; + this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); + return 0; + } + return this.core0RxFIFO.pull(); + case Core.Core1: + if (this.core1RxFIFO.empty) { + this.core1ROE = true; + this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); + return 0; + } + return this.core1RxFIFO.pull(); + } case SPINLOCK_ST: return this.spinLock; case DIV_UDIVIDEND: @@ -253,7 +336,7 @@ export class RPSIO { return 0xffffffff; } - 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; @@ -312,19 +395,19 @@ export class RPSIO { break; case DIV_UDIVIDEND: this.divDividend = value; - this.updateHardwareDivider(false); + this.updateHardwareDivider(false, core); break; case DIV_SDIVIDEND: this.divDividend = value; - this.updateHardwareDivider(true); + this.updateHardwareDivider(true, core); break; case DIV_UDIVISOR: this.divDivisor = value; - this.updateHardwareDivider(false); + this.updateHardwareDivider(false, core); break; case DIV_SDIVISOR: this.divDivisor = value; - this.updateHardwareDivider(true); + this.updateHardwareDivider(true, core); break; case DIV_QUOTIENT: this.divQuotient = value; @@ -412,6 +495,54 @@ export class RPSIO { case INTERP1_BASE_1AND0: this.interp1.setBase01(value); break; + case FIFO_ST: + switch (core) { + case Core.Core0: + if (value | FIFO_ST_WOF_BITS) { + this.core0WOF = false; + } + if (value | FIFO_ST_ROE_BITS) { + this.core0ROE = false; + } + if (!this.core0WOF && !this.core0ROE && this.core0RxFIFO.empty) { + this.rp2040.setInterruptCore(IRQ.SIO_PROC0, false, Core.Core0); + } + break; + case Core.Core1: + if (value | FIFO_ST_WOF_BITS) { + this.core1WOF = false; + } + if (value | FIFO_ST_ROE_BITS) { + this.core1ROE = false; + } + if (!this.core1WOF && !this.core1ROE && this.core1RxFIFO.empty) { + this.rp2040.setInterruptCore(IRQ.SIO_PROC1, false, Core.Core1); + } + break; + } + break; + case FIFO_WR: + switch (core) { + case Core.Core0: + if (this.core0TxFIFO.full) { + this.core0WOF = true; + this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); + } else { + this.core0TxFIFO.push(value); + this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); + } + break; + case Core.Core1: + if (this.core1TxFIFO.full) { + this.core1WOF = true; + this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); + } else { + this.core1TxFIFO.push(value); + 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)}` From a31e8fca16365abb3ad5f5955f7639690cbce585 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:08:03 +0000 Subject: [PATCH 09/25] Define 2 cores in rp2040 --- src/rp2040.ts | 88 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/src/rp2040.ts b/src/rp2040.ts index 527e115..8fada36 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,19 @@ 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(); } + 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 +180,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 (address >= FLASH_START_ADDRESS && address < FLASH_END_ADDRESS) { @@ -198,9 +193,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); @@ -242,6 +237,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; @@ -261,9 +257,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)}`); } @@ -330,7 +326,20 @@ 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() { @@ -344,23 +353,47 @@ export class RP2040 { } step() { - this.core.executeInstruction(); + this.core0.executeInstruction(); + this.core1.executeInstruction(); } execute() { + this.core0.stopped = false; + this.core1.stopped = false; + setTimeout(() => this.execute0(), 0); + } + + private execute0() { this.clock.resume(); this.executeTimer = null; - this.stopped = false; - for (let i = 0; i < 100000 && !this.stopped && !this.core.waiting; i++) { - this.core.executeInstruction(); + this.isCore0Running = true; + for (let i = 0; i < 1000 && !this.core0.stopped && !this.core0.waiting; i++) { + this.core0.executeInstruction(); } - if (!this.stopped) { - this.executeTimer = setTimeout(() => this.execute(), 0); + this.isCore0Running = false; + if (this.core1.stopped || this.core1.waiting) { + this.executeTimer = setTimeout(() => this.execute0(), 0); + } else { + this.executeTimer = setTimeout(() => this.execute1(), 0); + } + } + + private execute1() { + this.clock.resume(); + this.executeTimer = null; + for (let i = 0; i < 1000 && !this.core1.stopped && !this.core1.waiting; i++) { + this.core1.executeInstruction(); + } + if (this.core0.stopped || this.core0.waiting) { + this.executeTimer = setTimeout(() => this.execute1(), 0); + } else { + this.executeTimer = setTimeout(() => this.execute0(), 0); } } stop() { - this.stopped = true; + this.core0.stopped = true; + this.core1.stopped = true; if (this.executeTimer != null) { clearTimeout(this.executeTimer); this.executeTimer = null; @@ -368,7 +401,10 @@ 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; + } } } From dc29d0c3c37893ab56ab51202e4683e849aa1f5f Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:08:31 +0000 Subject: [PATCH 10/25] Register onSEV event for each core --- src/rp2040.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/rp2040.ts b/src/rp2040.ts index 8fada36..3f12d3b 100644 --- a/src/rp2040.ts +++ b/src/rp2040.ts @@ -155,6 +155,20 @@ export class RP2040 { 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; From efb78205ab965b003af9f7ec57ea22330bf8f691 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:09:30 +0000 Subject: [PATCH 11/25] Implement NMI_MASK for syscfg --- src/peripherals/syscfg.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/peripherals/syscfg.ts b/src/peripherals/syscfg.ts index 88ec9ca..9f8a9e0 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,8 +18,11 @@ 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: super.writeUint32(offset, value); From dcc32ab9950e149dc5ba68c9cce5f0fe1dc7e677 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:10:04 +0000 Subject: [PATCH 12/25] Modify debugger to debug only on core0 --- src/gdb/gdb-server.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gdb/gdb-server.ts b/src/gdb/gdb-server.ts index a9294fc..a005353 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(); } From 26fb63f306cb7dfbbe5eecf2c048b0bcb1ac0784 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:11:27 +0000 Subject: [PATCH 13/25] Update unit test --- src/instructions.spec.ts | 16 +++--- src/peripherals/timer.spec.ts | 8 +-- src/rp2040.spec.ts | 92 +++++++++++++++++------------------ 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/instructions.spec.ts b/src/instructions.spec.ts index cf3d73f..48b368d 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/timer.spec.ts b/src/peripherals/timer.spec.ts index 56709cf..0ddde43 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 c19d3ac..c713974 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); }); From 9b54d9d34c19d49e26a6959293d4c87b019c7435 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:11:41 +0000 Subject: [PATCH 14/25] Update test-utils --- test-utils/test-driver-rp2040.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test-utils/test-driver-rp2040.ts b/test-utils/test-driver-rp2040.ts index acdeacf..39b9234 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], From b1b4560f6fc0fc51de75bcce4ea9b504cd028896 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 12 Dec 2022 02:12:25 +0000 Subject: [PATCH 15/25] Update demo code --- demo/emulator-run.ts | 3 ++- demo/micropython-run.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/demo/emulator-run.ts b/demo/emulator-run.ts index e18cc9f..481b8cb 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 2f15cff..727f3cc 100644 --- a/demo/micropython-run.ts +++ b/demo/micropython-run.ts @@ -41,5 +41,6 @@ process.stdin.on('data', (chunk) => { } }); -mcu.core.PC = 0x10000000; +mcu.core0.PC = 0x10000000; +mcu.core1.PC = 0x10000000; mcu.execute(); From 4a0cf99bb3b65ec8ff53e573fcf47e4ba2e0d15f Mon Sep 17 00:00:00 2001 From: mingpepe Date: Tue, 13 Dec 2022 07:37:24 +0000 Subject: [PATCH 16/25] Let each core has its divider --- src/sio-core.ts | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sio.ts | 102 +++++++++-------------------------------------- 2 files changed, 123 insertions(+), 83 deletions(-) create mode 100644 src/sio-core.ts diff --git a/src/sio-core.ts b/src/sio-core.ts new file mode 100644 index 0000000..28cf423 --- /dev/null +++ b/src/sio-core.ts @@ -0,0 +1,104 @@ +import { RP2040 } from './rp2040'; +import { Core } from './core'; + +//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; + +export class RPSIOCore { + divDividend = 0; + divDivisor = 1; + divQuotient = 0; + divRemainder = 0; + divCSR = 0; + + constructor(private readonly rp2040: RP2040) { + + } + + 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; + default: + console.warn(`Read from invalid SIO address: ${offset.toString(16)}`); + return 0xffffffff; + } + } + + writeUint32(offset: number, value: number, core: Core) { + switch (offset) { + case DIV_UDIVIDEND: + this.divDividend = value; + this.updateHardwareDivider(false, core); + break; + case DIV_SDIVIDEND: + this.divDividend = value; + this.updateHardwareDivider(true, core); + break; + case DIV_UDIVISOR: + this.divDivisor = value; + this.updateHardwareDivider(false, core); + break; + case DIV_SDIVISOR: + this.divDivisor = value; + this.updateHardwareDivider(true, core); + break; + case DIV_QUOTIENT: + this.divQuotient = value; + this.divCSR = 0b11; + break; + case DIV_REMAINDER: + this.divRemainder = value; + this.divCSR = 0b11; + break; + default: + console.warn( + `Write to invalid SIO address: ${offset.toString(16)}, value=${value.toString(16)}` + ); + break; + } + } + + private updateHardwareDivider(signed: boolean, core: Core) { + 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 (core) { + case Core.Core0: + this.rp2040.core0.cycles += 8; + break; + case Core.Core1: + this.rp2040.core1.cycles += 8; + break; + } + } +} \ No newline at end of file diff --git a/src/sio.ts b/src/sio.ts index d2da80c..7cb715d 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -3,6 +3,7 @@ import { Interpolator } from './interpolator'; import { FIFO } from './utils/fifo'; import { Core } from './core'; import { IRQ } from './irq'; +import { RPSIOCore } from './sio-core'; const CPUID = 0x000; const FIFO_ST = 0x50; @@ -31,15 +32,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 @@ -89,11 +81,6 @@ 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); @@ -106,35 +93,14 @@ export class RPSIO { core0WOF = false; core1ROE = false; core1WOF = false; + readonly core0; + readonly core1; constructor(private readonly rp2040: RP2040) { this.core1TxFIFO = this.core0RxFIFO; this.core1RxFIFO = this.core0TxFIFO; - } - - updateHardwareDivider(signed: boolean, core: Core) { - 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 (core) - { - case Core.Core0: - this.rp2040.core0.cycles += 8; - break; - case Core.Core1: - this.rp2040.core1.cycles += 8; - break; - } + this.core0 = new RPSIOCore(rp2040); + this.core1 = new RPSIOCore(rp2040); } readUint32(offset: number, core: Core) { @@ -238,21 +204,6 @@ export class RPSIO { } 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: @@ -332,8 +283,12 @@ export class RPSIO { 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, core: Core) { @@ -393,30 +348,6 @@ export class RPSIO { case GPIO_HI_OE_XOR: this.qspiGpioOutputEnable ^= value & GPIO_MASK; break; - case DIV_UDIVIDEND: - this.divDividend = value; - this.updateHardwareDivider(false, core); - break; - case DIV_SDIVIDEND: - this.divDividend = value; - this.updateHardwareDivider(true, core); - break; - case DIV_UDIVISOR: - this.divDivisor = value; - this.updateHardwareDivider(false, core); - break; - case DIV_SDIVISOR: - this.divDivisor = value; - this.updateHardwareDivider(true, core); - 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(); @@ -544,9 +475,14 @@ export class RPSIO { } 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, core) + break; + case Core.Core1: + this.core1.writeUint32(offset, value, core); + break; + } } const pinsToUpdate = (this.gpioValue ^ prevGpioValue) | (this.gpioOutputEnable ^ prevGpioOutputEnable); From 10ec3e6b6d18f3fb3850ca9be580e58b8844238e Mon Sep 17 00:00:00 2001 From: mingpepe Date: Tue, 13 Dec 2022 08:19:10 +0000 Subject: [PATCH 17/25] Let each core has its interpolator --- src/sio-core.ts | 194 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sio.ts | 193 ----------------------------------------------- 2 files changed, 194 insertions(+), 193 deletions(-) diff --git a/src/sio-core.ts b/src/sio-core.ts index 28cf423..aaabb3f 100644 --- a/src/sio-core.ts +++ b/src/sio-core.ts @@ -1,5 +1,6 @@ import { RP2040 } from './rp2040'; import { Core } from './core'; +import { Interpolator } from './interpolator'; //HARDWARE DIVIDER const DIV_UDIVIDEND = 0x060; // Divider unsigned dividend @@ -10,6 +11,40 @@ 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 + export class RPSIOCore { divDividend = 0; divDivisor = 1; @@ -17,6 +52,9 @@ export class RPSIOCore { divRemainder = 0; divCSR = 0; + interp0 = new Interpolator(0); + interp1 = new Interpolator(1); + constructor(private readonly rp2040: RP2040) { } @@ -38,6 +76,84 @@ export class RPSIOCore { 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; default: console.warn(`Read from invalid SIO address: ${offset.toString(16)}`); return 0xffffffff; @@ -70,6 +186,84 @@ export class RPSIOCore { 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)}` diff --git a/src/sio.ts b/src/sio.ts index 7cb715d..bcd9019 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -1,5 +1,4 @@ import { RP2040 } from './rp2040'; -import { Interpolator } from './interpolator'; import { FIFO } from './utils/fifo'; import { Core } from './core'; import { IRQ } from './irq'; @@ -32,40 +31,6 @@ const GPIO_HI_OE_XOR = 0x04c; // QSPI output enable XOR const GPIO_MASK = 0x3fffffff; -//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; @@ -82,8 +47,6 @@ export class RPSIO { qspiGpioValue = 0; qspiGpioOutputEnable = 0; spinLock = 0; - interp0 = new Interpolator(0); - interp1 = new Interpolator(1); // The meaning of FIFO is for core0 readonly core0TxFIFO = new FIFO(8); readonly core0RxFIFO = new FIFO(8); @@ -204,84 +167,6 @@ export class RPSIO { } case SPINLOCK_ST: return this.spinLock; - 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; } switch (core) { case Core.Core0: @@ -348,84 +233,6 @@ export class RPSIO { case GPIO_HI_OE_XOR: this.qspiGpioOutputEnable ^= value & GPIO_MASK; 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: switch (core) { case Core.Core0: From fd563cd040487ec165ff5324041dfc5153c5eb02 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Tue, 13 Dec 2022 08:41:00 +0000 Subject: [PATCH 18/25] Move fifo related logic to each core --- src/sio-core.ts | 96 +++++++++++++++++++++++++++++++++++- src/sio.ts | 128 ++---------------------------------------------- 2 files changed, 99 insertions(+), 125 deletions(-) diff --git a/src/sio-core.ts b/src/sio-core.ts index aaabb3f..8b6b070 100644 --- a/src/sio-core.ts +++ b/src/sio-core.ts @@ -1,6 +1,8 @@ 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 @@ -45,6 +47,16 @@ const INTERP1_ACCUM0_ADD = 0x0f4; // Values written here are atomically added to 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; @@ -55,11 +67,22 @@ export class RPSIOCore { interp0 = new Interpolator(0); interp1 = new Interpolator(1); - constructor(private readonly rp2040: RP2040) { + ROE = false; + WOF = false; + static create2Cores(rp2040: RP2040) { + let rxFIFO = new FIFO(8); + let txFIFO = new FIFO(8); + let core0 = new RPSIOCore(rp2040, rxFIFO, txFIFO); + let core1 = new RPSIOCore(rp2040, txFIFO, rxFIFO); + return [core0, core1]; } - readUint32(offset: number) { + private constructor(private readonly rp2040: RP2040, + private readonly rxFIFO: FIFO, + private readonly txFIFO: FIFO) { } + + readUint32(offset: number, core: Core) { switch (offset) { case DIV_UDIVIDEND: return this.divDividend; @@ -154,6 +177,34 @@ export class RPSIOCore { 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 (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)}`); return 0xffffffff; @@ -264,6 +315,47 @@ export class RPSIOCore { 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 (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 (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 (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)}` diff --git a/src/sio.ts b/src/sio.ts index bcd9019..5c49f01 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -1,13 +1,8 @@ import { RP2040 } from './rp2040'; -import { FIFO } from './utils/fifo'; import { Core } from './core'; -import { IRQ } from './irq'; import { RPSIOCore } from './sio-core'; const CPUID = 0x000; -const FIFO_ST = 0x50; -const FIFO_WR = 0x54; -const FIFO_RD = 0x58; // GPIO const GPIO_IN = 0x004; // Input value for GPIO pins @@ -36,34 +31,19 @@ const SPINLOCK_ST = 0x5c; const SPINLOCK0 = 0x100; const SPINLOCK31 = 0x17c; -const FIFO_ST_VLD_BITS = 0x01; -const FIFO_ST_RDY_BITS = 0x02; -const FIFO_ST_WOF_BITS = 0x04; -const FIFO_ST_ROE_BITS = 0x08; - export class RPSIO { gpioValue = 0; gpioOutputEnable = 0; qspiGpioValue = 0; qspiGpioOutputEnable = 0; spinLock = 0; - // The meaning of FIFO is for core0 - readonly core0TxFIFO = new FIFO(8); - readonly core0RxFIFO = new FIFO(8); - readonly core1TxFIFO; - readonly core1RxFIFO; - core0ROE = false; - core0WOF = false; - core1ROE = false; - core1WOF = false; readonly core0; readonly core1; constructor(private readonly rp2040: RP2040) { - this.core1TxFIFO = this.core0RxFIFO; - this.core1RxFIFO = this.core0TxFIFO; - this.core0 = new RPSIOCore(rp2040); - this.core1 = new RPSIOCore(rp2040); + let cores = RPSIOCore.create2Cores(rp2040); + this.core0 = cores[0]; + this.core1 = cores[1]; } readUint32(offset: number, core: Core) { @@ -115,64 +95,14 @@ export class RPSIO { case Core.Core0: return 0; case Core.Core1: return 1; } - case FIFO_ST: - let value = 0; - switch (core) { - case Core.Core0: - if (!this.core0RxFIFO.empty) { - value |= FIFO_ST_VLD_BITS; - } - if (!this.core0TxFIFO.full) { - value |= FIFO_ST_RDY_BITS; - } - if (this.core0WOF) { - value |= FIFO_ST_WOF_BITS; - } - if (this.core0ROE) { - value |= FIFO_ST_ROE_BITS; - } - break; - case Core.Core1: - if (!this.core0TxFIFO.empty) { - value |= FIFO_ST_VLD_BITS; - } - if (!this.core0RxFIFO.full) { - value |= FIFO_ST_RDY_BITS; - } - if (this.core1WOF) { - value |= FIFO_ST_WOF_BITS; - } - if (this.core1ROE) { - value |= FIFO_ST_ROE_BITS; - } - break; - } - return value; - case FIFO_RD: - switch (core) { - case Core.Core0: - if (this.core0RxFIFO.empty) { - this.core0ROE = true; - this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); - return 0; - } - return this.core0RxFIFO.pull(); - case Core.Core1: - if (this.core1RxFIFO.empty) { - this.core1ROE = true; - this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); - return 0; - } - return this.core1RxFIFO.pull(); - } case SPINLOCK_ST: return this.spinLock; } switch (core) { case Core.Core0: - return this.core0.readUint32(offset); + return this.core0.readUint32(offset, core); case Core.Core1: - return this.core1.readUint32(offset); + return this.core1.readUint32(offset, core); } } @@ -233,54 +163,6 @@ export class RPSIO { case GPIO_HI_OE_XOR: this.qspiGpioOutputEnable ^= value & GPIO_MASK; break; - case FIFO_ST: - switch (core) { - case Core.Core0: - if (value | FIFO_ST_WOF_BITS) { - this.core0WOF = false; - } - if (value | FIFO_ST_ROE_BITS) { - this.core0ROE = false; - } - if (!this.core0WOF && !this.core0ROE && this.core0RxFIFO.empty) { - this.rp2040.setInterruptCore(IRQ.SIO_PROC0, false, Core.Core0); - } - break; - case Core.Core1: - if (value | FIFO_ST_WOF_BITS) { - this.core1WOF = false; - } - if (value | FIFO_ST_ROE_BITS) { - this.core1ROE = false; - } - if (!this.core1WOF && !this.core1ROE && this.core1RxFIFO.empty) { - this.rp2040.setInterruptCore(IRQ.SIO_PROC1, false, Core.Core1); - } - break; - } - break; - case FIFO_WR: - switch (core) { - case Core.Core0: - if (this.core0TxFIFO.full) { - this.core0WOF = true; - this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); - } else { - this.core0TxFIFO.push(value); - this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); - } - break; - case Core.Core1: - if (this.core1TxFIFO.full) { - this.core1WOF = true; - this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); - } else { - this.core1TxFIFO.push(value); - this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); - } - break; - } - break; default: switch (core) { case Core.Core0: From c9ab5ead2c3886f87c2b30280f5e7aced6c73b06 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Tue, 13 Dec 2022 08:59:40 +0000 Subject: [PATCH 19/25] Do no need to pass core info to each core --- src/sio-core.ts | 31 ++++++++++++++++--------------- src/sio.ts | 8 ++++---- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/sio-core.ts b/src/sio-core.ts index 8b6b070..cc85861 100644 --- a/src/sio-core.ts +++ b/src/sio-core.ts @@ -73,16 +73,17 @@ export class RPSIOCore { static create2Cores(rp2040: RP2040) { let rxFIFO = new FIFO(8); let txFIFO = new FIFO(8); - let core0 = new RPSIOCore(rp2040, rxFIFO, txFIFO); - let core1 = new RPSIOCore(rp2040, txFIFO, rxFIFO); + let core0 = new RPSIOCore(rp2040, rxFIFO, txFIFO, Core.Core0); + let 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 txFIFO: FIFO, + private readonly core: Core) { } - readUint32(offset: number, core: Core) { + readUint32(offset: number) { switch (offset) { case DIV_UDIVIDEND: return this.divDividend; @@ -195,7 +196,7 @@ export class RPSIOCore { case FIFO_RD: if (this.rxFIFO.empty) { this.ROE = true; - switch (core) { + switch (this.core) { case Core.Core0: this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); break; @@ -211,23 +212,23 @@ export class RPSIOCore { } } - writeUint32(offset: number, value: number, core: Core) { + writeUint32(offset: number, value: number) { switch (offset) { case DIV_UDIVIDEND: this.divDividend = value; - this.updateHardwareDivider(false, core); + this.updateHardwareDivider(false); break; case DIV_SDIVIDEND: this.divDividend = value; - this.updateHardwareDivider(true, core); + this.updateHardwareDivider(true); break; case DIV_UDIVISOR: this.divDivisor = value; - this.updateHardwareDivider(false, core); + this.updateHardwareDivider(false); break; case DIV_SDIVISOR: this.divDivisor = value; - this.updateHardwareDivider(true, core); + this.updateHardwareDivider(true); break; case DIV_QUOTIENT: this.divQuotient = value; @@ -323,7 +324,7 @@ export class RPSIOCore { this.ROE = false; } if (!this.WOF && !this.ROE && this.rxFIFO.empty) { - switch (core) { + switch (this.core) { case Core.Core0: this.rp2040.setInterruptCore(IRQ.SIO_PROC0, false, Core.Core0); break; @@ -336,7 +337,7 @@ export class RPSIOCore { case FIFO_WR: if (this.txFIFO.full) { this.WOF = true; - switch (core) { + switch (this.core) { case Core.Core0: this.rp2040.setInterruptCore(IRQ.SIO_PROC0, true, Core.Core0); break; @@ -346,7 +347,7 @@ export class RPSIOCore { } } else { this.txFIFO.push(value); - switch (core) { + switch (this.core) { case Core.Core0: this.rp2040.setInterruptCore(IRQ.SIO_PROC1, true, Core.Core1); break; @@ -364,7 +365,7 @@ export class RPSIOCore { } } - private updateHardwareDivider(signed: boolean, core: Core) { + private updateHardwareDivider(signed: boolean) { if (this.divDivisor == 0) { this.divQuotient = this.divDividend > 0 ? -1 : 1; this.divRemainder = this.divDividend; @@ -378,7 +379,7 @@ export class RPSIOCore { } } this.divCSR = 0b11; - switch (core) { + switch (this.core) { case Core.Core0: this.rp2040.core0.cycles += 8; break; diff --git a/src/sio.ts b/src/sio.ts index 5c49f01..ee587f3 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -100,9 +100,9 @@ export class RPSIO { } switch (core) { case Core.Core0: - return this.core0.readUint32(offset, core); + return this.core0.readUint32(offset); case Core.Core1: - return this.core1.readUint32(offset, core); + return this.core1.readUint32(offset); } } @@ -166,10 +166,10 @@ export class RPSIO { default: switch (core) { case Core.Core0: - this.core0.writeUint32(offset, value, core) + this.core0.writeUint32(offset, value) break; case Core.Core1: - this.core1.writeUint32(offset, value, core); + this.core1.writeUint32(offset, value); break; } } From 6f2da85585de712d4e94f5a47a23051df6be2843 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Tue, 13 Dec 2022 09:01:38 +0000 Subject: [PATCH 20/25] Add core info to warning message --- src/sio-core.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sio-core.ts b/src/sio-core.ts index cc85861..00d6406 100644 --- a/src/sio-core.ts +++ b/src/sio-core.ts @@ -207,7 +207,7 @@ export class RPSIOCore { } return this.rxFIFO.pull(); default: - console.warn(`Read from invalid SIO address: ${offset.toString(16)}`); + console.warn(`Read from invalid SIO address: ${offset.toString(16)} (${this.core})`); return 0xffffffff; } } @@ -359,7 +359,7 @@ export class RPSIOCore { break; default: console.warn( - `Write to invalid SIO address: ${offset.toString(16)}, value=${value.toString(16)}` + `Write to invalid SIO address: ${offset.toString(16)}, value=${value.toString(16)} (${this.core})` ); break; } From 278bda75f7b43631d12042963d358891db2f4186 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 30 Jan 2023 11:27:24 +0000 Subject: [PATCH 21/25] Execute core0 and core1 interleaving --- src/rp2040.ts | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/rp2040.ts b/src/rp2040.ts index 3f12d3b..8eef008 100644 --- a/src/rp2040.ts +++ b/src/rp2040.ts @@ -374,35 +374,27 @@ export class RP2040 { execute() { this.core0.stopped = false; this.core1.stopped = false; - setTimeout(() => this.execute0(), 0); + setTimeout(() => this.executeInternal(), 0); } - private execute0() { + private executeInternal() { this.clock.resume(); this.executeTimer = null; - this.isCore0Running = true; - for (let i = 0; i < 1000 && !this.core0.stopped && !this.core0.waiting; i++) { - this.core0.executeInstruction(); - } - this.isCore0Running = false; - if (this.core1.stopped || this.core1.waiting) { - this.executeTimer = setTimeout(() => this.execute0(), 0); - } else { - this.executeTimer = setTimeout(() => this.execute1(), 0); - } - } - - private execute1() { - this.clock.resume(); - this.executeTimer = null; - for (let i = 0; i < 1000 && !this.core1.stopped && !this.core1.waiting; i++) { - this.core1.executeInstruction(); - } - if (this.core0.stopped || this.core0.waiting) { - this.executeTimer = setTimeout(() => this.execute1(), 0); - } else { - this.executeTimer = setTimeout(() => this.execute0(), 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() { From 1290137e8193e635ed2377e1c1a80e4d33c96830 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 6 Feb 2023 02:44:39 +0000 Subject: [PATCH 22/25] Fix wrong merge --- src/sio.ts | 93 ------------------------------------------------------ 1 file changed, 93 deletions(-) diff --git a/src/sio.ts b/src/sio.ts index 2a9e34a..ee587f3 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -97,99 +97,6 @@ export class RPSIO { } 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; } switch (core) { case Core.Core0: From 9b55ad57a571de4f397803cfd78f10b668792f56 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 6 Feb 2023 02:55:58 +0000 Subject: [PATCH 23/25] Run lint --- src/core.ts | 6 +- src/peripherals/ppb.ts | 2 +- src/peripherals/syscfg.ts | 6 +- src/rp2040.ts | 15 +- src/sio-core.ts | 638 +++++++++++++++++++------------------- src/sio.ts | 8 +- 6 files changed, 341 insertions(+), 334 deletions(-) diff --git a/src/core.ts b/src/core.ts index fadfc21..de2ccd9 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,4 +1,4 @@ export enum Core { - Core0, - Core1, -} \ No newline at end of file + Core0, + Core1, +} diff --git a/src/peripherals/ppb.ts b/src/peripherals/ppb.ts index 2c9957e..85898e9 100644 --- a/src/peripherals/ppb.ts +++ b/src/peripherals/ppb.ts @@ -1,7 +1,7 @@ import { IClockTimer } from '../clock/clock'; import { MAX_HARDWARE_IRQ } from '../irq'; import { BasePeripheral, Peripheral } from './peripheral'; -import { Core } from '../core' +import { Core } from '../core'; export const CPUID = 0xd00; export const ICSR = 0xd04; diff --git a/src/peripherals/syscfg.ts b/src/peripherals/syscfg.ts index 9f8a9e0..544d339 100644 --- a/src/peripherals/syscfg.ts +++ b/src/peripherals/syscfg.ts @@ -20,9 +20,9 @@ export class RP2040SysCfg extends BasePeripheral implements Peripheral { case PROC0_NMI_MASK: this.rp2040.core0.interruptNMIMask = value; break; - case PROC1_NMI_MASK: - this.rp2040.core1.interruptNMIMask = value; - break; + case PROC1_NMI_MASK: + this.rp2040.core1.interruptNMIMask = value; + break; default: super.writeUint32(offset, value); diff --git a/src/rp2040.ts b/src/rp2040.ts index c20f292..f8caf54 100644 --- a/src/rp2040.ts +++ b/src/rp2040.ts @@ -161,14 +161,14 @@ export class RP2040 { } else { this.core1.eventRegistered = true; } - } + }; this.core1.onSEV = () => { if (this.core0.waiting) { this.core0.waiting = false; } else { this.core0.eventRegistered = true; } - } + }; } isCore0Running = true; @@ -351,8 +351,7 @@ export class RP2040 { } setInterruptCore(irq: number, value: boolean, core: Core) { - switch (core) - { + switch (core) { case Core.Core0: this.core0.setInterrupt(irq, value); break; @@ -413,10 +412,12 @@ export class RP2040 { this.clock.pause(); } - executing(core: Core): boolean{ + executing(core: Core): boolean { switch (core) { - case Core.Core0: return this.core0.stopped; - case Core.Core1: return this.core1.stopped; + 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 index 00d6406..8954e91 100644 --- a/src/sio-core.ts +++ b/src/sio-core.ts @@ -58,334 +58,338 @@ const FIFO_WR = 0x54; const FIFO_RD = 0x58; export class RPSIOCore { - divDividend = 0; - divDivisor = 1; - divQuotient = 0; - divRemainder = 0; - divCSR = 0; + divDividend = 0; + divDivisor = 1; + divQuotient = 0; + divRemainder = 0; + divCSR = 0; - interp0 = new Interpolator(0); - interp1 = new Interpolator(1); + interp0 = new Interpolator(0); + interp1 = new Interpolator(1); - ROE = false; - WOF = false; + ROE = false; + WOF = false; - static create2Cores(rp2040: RP2040) { - let rxFIFO = new FIFO(8); - let txFIFO = new FIFO(8); - let core0 = new RPSIOCore(rp2040, rxFIFO, txFIFO, Core.Core0); - let core1 = new RPSIOCore(rp2040, txFIFO, rxFIFO, Core.Core1); - return [core0, core1]; - } + static create2Cores(rp2040: RP2040) { + let rxFIFO = new FIFO(8); + let txFIFO = new FIFO(8); + let core0 = new RPSIOCore(rp2040, rxFIFO, txFIFO, Core.Core0); + let 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) { } + 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; + 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; } - } - - 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; + 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; } + } - 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); - } - } + 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; - switch (this.core) { + 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.core0.cycles += 8; - break; + this.rp2040.setInterruptCore(IRQ.SIO_PROC0, false, Core.Core0); + break; case Core.Core1: - this.rp2040.core1.cycles += 8; - break; + 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; } -} \ No newline at end of file + } +} diff --git a/src/sio.ts b/src/sio.ts index ee587f3..97f7974 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -92,8 +92,10 @@ export class RPSIO { return 0; // TODO verify with silicone case CPUID: switch (core) { - case Core.Core0: return 0; - case Core.Core1: return 1; + case Core.Core0: + return 0; + case Core.Core1: + return 1; } case SPINLOCK_ST: return this.spinLock; @@ -166,7 +168,7 @@ export class RPSIO { default: switch (core) { case Core.Core0: - this.core0.writeUint32(offset, value) + this.core0.writeUint32(offset, value); break; case Core.Core1: this.core1.writeUint32(offset, value); From 9bb7e7ac29aa20a2ec15b098f3a23104aa583c9a Mon Sep 17 00:00:00 2001 From: mingpepe Date: Mon, 6 Feb 2023 03:15:15 +0000 Subject: [PATCH 24/25] Fix error by lint --- src/sio-core.ts | 11 ++++++----- src/sio.ts | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sio-core.ts b/src/sio-core.ts index 8954e91..515b7d1 100644 --- a/src/sio-core.ts +++ b/src/sio-core.ts @@ -71,10 +71,10 @@ export class RPSIOCore { WOF = false; static create2Cores(rp2040: RP2040) { - let rxFIFO = new FIFO(8); - let txFIFO = new FIFO(8); - let core0 = new RPSIOCore(rp2040, rxFIFO, txFIFO, Core.Core0); - let core1 = new RPSIOCore(rp2040, txFIFO, rxFIFO, Core.Core1); + 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]; } @@ -180,7 +180,7 @@ export class RPSIOCore { return this.interp1.smresult0; case INTERP1_ACCUM1_ADD: return this.interp1.smresult1; - case FIFO_ST: + case FIFO_ST: { let value = 0; if (!this.rxFIFO.empty) { value |= FIFO_ST_VLD_BITS; @@ -195,6 +195,7 @@ export class RPSIOCore { value |= FIFO_ST_ROE_BITS; } return value; + } case FIFO_RD: if (this.rxFIFO.empty) { this.ROE = true; diff --git a/src/sio.ts b/src/sio.ts index 97f7974..acd3972 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -41,7 +41,7 @@ export class RPSIO { readonly core1; constructor(private readonly rp2040: RP2040) { - let cores = RPSIOCore.create2Cores(rp2040); + const cores = RPSIOCore.create2Cores(rp2040); this.core0 = cores[0]; this.core1 = cores[1]; } @@ -97,6 +97,7 @@ export class RPSIO { case Core.Core1: return 1; } + break; case SPINLOCK_ST: return this.spinLock; } From 23bf9d337b9dbbc69fa22b0689f3736553c902b3 Mon Sep 17 00:00:00 2001 From: mingpepe Date: Tue, 21 Mar 2023 10:20:09 +0000 Subject: [PATCH 25/25] Rename variable --- src/peripherals/ppb.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/peripherals/ppb.ts b/src/peripherals/ppb.ts index 85898e9..98f3256 100644 --- a/src/peripherals/ppb.ts +++ b/src/peripherals/ppb.ts @@ -54,9 +54,9 @@ export class RPPPB extends BasePeripheral implements Peripheral { systickReload = 0; systickTimer: IClockTimer | null = null; - readUint32ViaCore(offset: number, _core: Core) { + readUint32ViaCore(offset: number, coreIndex: Core) { const { rp2040 } = this; - const core = _core == Core.Core0 ? rp2040.core0 : rp2040.core1; + const core = coreIndex == Core.Core0 ? rp2040.core0 : rp2040.core1; switch (offset) { case CPUID: @@ -133,12 +133,12 @@ export class RPPPB extends BasePeripheral implements Peripheral { case SYST_CALIB: return 0x0000270f; } - return super.readUint32ViaCore(offset, _core); + return super.readUint32ViaCore(offset, coreIndex); } - writeUint32ViaCore(offset: number, value: number, _core: Core) { + writeUint32ViaCore(offset: number, value: number, coreIndex: Core) { const { rp2040 } = this; - const core = _core == Core.Core0 ? rp2040.core0 : rp2040.core1; + const core = coreIndex == Core.Core0 ? rp2040.core0 : rp2040.core1; const hardwareInterruptMask = (1 << MAX_HARDWARE_IRQ) - 1; @@ -247,7 +247,7 @@ export class RPPPB extends BasePeripheral implements Peripheral { return; default: - super.writeUint32ViaCore(offset, value, _core); + super.writeUint32ViaCore(offset, value, coreIndex); } } }