From 36f2e7c5158a4ab2a4d954077e9f1d77632c60d5 Mon Sep 17 00:00:00 2001 From: Valerio Turrini Date: Mon, 24 May 2021 09:19:21 +0200 Subject: [PATCH] feat(sio): implement Integer Divider (#37) Implemented according to section 2.3.1.5 of the RP2040 datasheet and verified against the silicone. All adds the RESET_BASE peripheral (RP2040 datasheet section 2.14) to reduce the amount of "unimplemented peripheral" read/write warnings. --- src/peripherals/reset.ts | 37 ++++++++++ src/rp2040.ts | 3 +- src/sio.spec.ts | 127 ++++++++++++++++++++++++++++++++ src/sio.ts | 70 ++++++++++++++++++ src/utils/test-driver-gdb.ts | 4 + src/utils/test-driver-rp2040.ts | 6 +- src/utils/test-driver.ts | 1 + 7 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 src/peripherals/reset.ts create mode 100644 src/sio.spec.ts diff --git a/src/peripherals/reset.ts b/src/peripherals/reset.ts new file mode 100644 index 0000000..9e60d1c --- /dev/null +++ b/src/peripherals/reset.ts @@ -0,0 +1,37 @@ +import { BasePeripheral, Peripheral } from './peripheral'; + +const RESET = 0x0; //Reset control. +const WDSEL = 0x4; //Watchdog select. +const RESET_DONE = 0x8; //Reset Done + +export class RPReset extends BasePeripheral implements Peripheral { + private reset: number = 0; + private wdsel: number = 0; + private reset_done: number = 0x1ffffff; + + readUint32(offset: number) { + switch (offset) { + case RESET: + return this.reset; + case WDSEL: + return this.wdsel; + case RESET_DONE: + return this.reset_done; + } + return super.readUint32(offset); + } + + writeUint32(offset: number, value: number) { + switch (offset) { + case RESET: + this.reset = value & 0x1ffffff; + break; + case WDSEL: + this.wdsel = value & 0x1ffffff; + break; + default: + super.writeUint32(offset, value); + break; + } + } +} diff --git a/src/rp2040.ts b/src/rp2040.ts index 61ecb39..4031c8a 100644 --- a/src/rp2040.ts +++ b/src/rp2040.ts @@ -7,6 +7,7 @@ import { RP2040SysCfg } from './peripherals/syscfg'; import { RPTimer } from './peripherals/timer'; import { RPUART } from './peripherals/uart'; import { RPSIO } from './sio'; +import { RPReset } from './peripherals/reset'; export const FLASH_START_ADDRESS = 0x10000000; export const FLASH_END_ADDRESS = 0x14000000; @@ -191,7 +192,7 @@ export class RP2040 { 0x40000: new UnimplementedPeripheral(this, 'SYSINFO_BASE'), 0x40004: new RP2040SysCfg(this, 'SYSCFG'), 0x40008: new UnimplementedPeripheral(this, 'CLOCKS_BASE'), - 0x4000c: new UnimplementedPeripheral(this, 'RESETS_BASE'), + 0x4000c: new RPReset(this, 'RESETS_BASE'), 0x40010: new UnimplementedPeripheral(this, 'PSM_BASE'), 0x40014: new UnimplementedPeripheral(this, 'IO_BANK0_BASE'), 0x40018: new UnimplementedPeripheral(this, 'IO_QSPI_BASE'), diff --git a/src/sio.spec.ts b/src/sio.spec.ts new file mode 100644 index 0000000..84f5640 --- /dev/null +++ b/src/sio.spec.ts @@ -0,0 +1,127 @@ +import { RP2040, SIO_START_ADDRESS } from './rp2040'; +import { GDBClient } from './utils/gdbclient'; +import { ICortexTestDriver } from './utils/test-driver'; +import { GDBTestDriver } from './utils/test-driver-gdb'; +import { RP2040TestDriver } from './utils/test-driver-rp2040'; + +//Hardware Divider registers absolute address +const SIO_DIV_UDIVIDEND = SIO_START_ADDRESS + 0x060; // Divider unsigned dividend +const SIO_DIV_UDIVISOR = SIO_START_ADDRESS + 0x064; // Divider unsigned divisor +const SIO_DIV_SDIVIDEND = SIO_START_ADDRESS + 0x068; // Divider signed dividend +const SIO_DIV_SDIVISOR = SIO_START_ADDRESS + 0x06c; // Divider signed divisor +const SIO_DIV_QUOTIENT = SIO_START_ADDRESS + 0x070; // Divider result quotient +const SIO_DIV_REMAINDER = SIO_START_ADDRESS + 0x074; //Divider result remainder +const SIO_DIV_CSR = SIO_START_ADDRESS + 0x078; + +describe('RPSIO', () => { + let cpu: ICortexTestDriver; + + beforeEach(async () => { + // if (process.env.TEST_GDB_SERVER) { + if (false) { + const client = new GDBClient(); + await client.connect('127.0.0.1'); + cpu = new GDBTestDriver(client); + await cpu.init(); + } else { + cpu = new RP2040TestDriver(new RP2040()); + } + }); + + afterEach(async () => { + await cpu.tearDown(); + }); + + describe('Hardware Divider', () => { + it('should perform a signed hardware divider 123456 / -321 = -384 REM 192', async () => { + await cpu.writeUint32(SIO_DIV_SDIVIDEND, 123456); + expect(await cpu.readInt32(SIO_DIV_SDIVIDEND)).toEqual(123456); + await cpu.writeUint32(SIO_DIV_SDIVISOR, -321); + expect(await cpu.readUint32(SIO_DIV_CSR)).toEqual(3); + expect(await cpu.readInt32(SIO_DIV_SDIVISOR)).toEqual(-321); + expect(await cpu.readInt32(SIO_DIV_REMAINDER)).toEqual(192); + expect(await cpu.readInt32(SIO_DIV_QUOTIENT)).toEqual(-384); + expect(await cpu.readUint32(SIO_DIV_CSR)).toEqual(1); + }); + + it('should perform a signed hardware divider -3000 / 2 = -1500 REM 0', async () => { + await cpu.writeUint32(SIO_DIV_SDIVIDEND, -3000); + expect(await cpu.readInt32(SIO_DIV_SDIVIDEND)).toEqual(-3000); + await cpu.writeUint32(SIO_DIV_SDIVISOR, 2); + expect(await cpu.readUint32(SIO_DIV_CSR)).toEqual(3); + expect(await cpu.readInt32(SIO_DIV_SDIVISOR)).toEqual(2); + expect(await cpu.readInt32(SIO_DIV_REMAINDER)).toEqual(0); + expect(await cpu.readInt32(SIO_DIV_QUOTIENT)).toEqual(-1500); + expect(await cpu.readUint32(SIO_DIV_CSR)).toEqual(1); + }); + + it('should perform an unsigned hardware divider 123456 / 321 = 384 REM 192', async () => { + await cpu.writeUint32(SIO_DIV_UDIVIDEND, 123456); + await cpu.writeUint32(SIO_DIV_UDIVISOR, 321); + expect(await cpu.readUint32(SIO_DIV_CSR)).toEqual(3); + expect(await cpu.readUint32(SIO_DIV_REMAINDER)).toEqual(192); + expect(await cpu.readUint32(SIO_DIV_QUOTIENT)).toEqual(384); + expect(await cpu.readUint32(SIO_DIV_CSR)).toEqual(1); + }); + + it('should perform a division, store the result, do another division then restore the previously stored result ', async () => { + await cpu.writeUint32(SIO_DIV_SDIVIDEND, 123456); + await cpu.writeUint32(SIO_DIV_SDIVISOR, -321); + const remainder = await cpu.readInt32(SIO_DIV_REMAINDER); + const quotient = await cpu.readInt32(SIO_DIV_QUOTIENT); + expect(remainder).toEqual(192); + expect(quotient).toEqual(-384); + await cpu.writeUint32(SIO_DIV_UDIVIDEND, 123); + await cpu.writeUint32(SIO_DIV_UDIVISOR, 7); + expect(await cpu.readUint32(SIO_DIV_REMAINDER)).toEqual(4); + expect(await cpu.readUint32(SIO_DIV_QUOTIENT)).toEqual(17); + await cpu.writeUint32(SIO_DIV_REMAINDER, remainder); + await cpu.writeUint32(SIO_DIV_QUOTIENT, quotient); + expect(await cpu.readUint32(SIO_DIV_CSR)).toEqual(3); + expect(await cpu.readInt32(SIO_DIV_REMAINDER)).toEqual(192); + expect(await cpu.readInt32(SIO_DIV_QUOTIENT)).toEqual(-384); + }); + + it('should perform an unsigned division by zero 123456 / 0 = 0xffffffff REM 123456', async () => { + await cpu.writeUint32(SIO_DIV_UDIVIDEND, 123456); + await cpu.writeUint32(SIO_DIV_UDIVISOR, 0); + expect(await cpu.readUint32(SIO_DIV_REMAINDER)).toEqual(123456); + expect(await cpu.readUint32(SIO_DIV_QUOTIENT)).toEqual(0xffffffff); + }); + + it('should perform an unsigned division by zero 0x80000000 / 0 = 0xffffffff REM 0x80000000', async () => { + await cpu.writeUint32(SIO_DIV_UDIVIDEND, 0x80000000); + await cpu.writeUint32(SIO_DIV_UDIVISOR, 0); + expect(await cpu.readUint32(SIO_DIV_REMAINDER)).toEqual(0x80000000); + expect(await cpu.readUint32(SIO_DIV_QUOTIENT)).toEqual(0xffffffff); + }); + + it('should perform a signed division by zero 3000 / 0 = -1 REM 3000', async () => { + await cpu.writeUint32(SIO_DIV_SDIVIDEND, 3000); + await cpu.writeUint32(SIO_DIV_SDIVISOR, 0); + expect(await cpu.readInt32(SIO_DIV_REMAINDER)).toEqual(3000); + expect(await cpu.readInt32(SIO_DIV_QUOTIENT)).toEqual(-1); + }); + + it('should perform a signed division by zero -3000 / 0 = 1 REM -3000', async () => { + await cpu.writeUint32(SIO_DIV_SDIVIDEND, -3000); + await cpu.writeUint32(SIO_DIV_SDIVISOR, 0); + expect(await cpu.readInt32(SIO_DIV_REMAINDER)).toEqual(-3000); + expect(await cpu.readInt32(SIO_DIV_QUOTIENT)).toEqual(1); + }); + + it('should perform a signed division 0x80000000 / 2 = 0xc0000000 REM 0', async () => { + await cpu.writeUint32(SIO_DIV_SDIVIDEND, 0x80000000); + await cpu.writeUint32(SIO_DIV_SDIVISOR, 2); + expect(await cpu.readUint32(SIO_DIV_REMAINDER)).toEqual(0); + expect(await cpu.readUint32(SIO_DIV_QUOTIENT)).toEqual(0xc0000000); + }); + + it('should perform an unsigned division 0x80000000 / 2 = 0x40000000 REM 0', async () => { + await cpu.writeUint32(SIO_DIV_UDIVIDEND, 0x80000000); + await cpu.writeUint32(SIO_DIV_UDIVISOR, 2); + expect(await cpu.readUint32(SIO_DIV_REMAINDER)).toEqual(0); + expect(await cpu.readUint32(SIO_DIV_QUOTIENT)).toEqual(0x40000000); + }); + }); +}); diff --git a/src/sio.ts b/src/sio.ts index 92e529b..85b0004 100644 --- a/src/sio.ts +++ b/src/sio.ts @@ -24,14 +24,45 @@ 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; + export class RPSIO { gpioValue = 0; gpioOutputEnable = 0; qspiGpioValue = 0; qspiGpioOutputEnable = 0; + divDividend = 0; + divDivisor = 1; + divQuotient = 0; + divRemainder = 0; + divCSR = 0; constructor(private readonly rp2040: RP2040) {} + updateHardwareDivider(signed: boolean) { + if (this.divDivisor == 0) { + this.divQuotient = this.divDividend > 0 ? -1 : 1; + this.divRemainder = this.divDividend; + } else { + if (signed) { + this.divQuotient = (this.divDividend | 0) / (this.divDivisor | 0); + this.divRemainder = (this.divDividend | 0) % (this.divDivisor | 0); + } else { + this.divQuotient = (this.divDividend >>> 0) / (this.divDivisor >>> 0); + this.divRemainder = (this.divDividend >>> 0) % (this.divDivisor >>> 0); + } + } + this.divCSR = 0b11; + this.rp2040.cycles += 8; + } + readUint32(offset: number) { switch (offset) { case GPIO_IN: @@ -62,6 +93,21 @@ export class RPSIO { case CPUID: // Returns the current CPU core id (always 0 for now) return 0; + 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; } console.warn(`Read from invalid SIO address: ${offset.toString(16)}`); return 0xffffffff; @@ -117,6 +163,30 @@ export class RPSIO { case GPIO_HI_OE_XOR: this.qspiGpioOutputEnable ^= value & GPIO_MASK; break; + case DIV_UDIVIDEND: + this.divDividend = value; + this.updateHardwareDivider(false); + break; + case DIV_SDIVIDEND: + this.divDividend = value; + this.updateHardwareDivider(true); + break; + case DIV_UDIVISOR: + this.divDivisor = value; + this.updateHardwareDivider(false); + break; + case DIV_SDIVISOR: + this.divDivisor = value; + this.updateHardwareDivider(true); + break; + case DIV_QUOTIENT: + this.divQuotient = value; + this.divCSR = 0b11; + break; + case DIV_REMAINDER: + this.divRemainder = value; + this.divCSR = 0b11; + break; } } } diff --git a/src/utils/test-driver-gdb.ts b/src/utils/test-driver-gdb.ts index cdb75a5..773c3ec 100644 --- a/src/utils/test-driver-gdb.ts +++ b/src/utils/test-driver-gdb.ts @@ -148,4 +148,8 @@ export class GDBTestDriver implements ICortexTestDriver { const result = await this.gdbClient.readMemory(address, 4); return new Uint32Array(result.buffer)[0]; } + + async readInt32(address: number) { + return (await this.readUint32(address)) | 0; + } } diff --git a/src/utils/test-driver-rp2040.ts b/src/utils/test-driver-rp2040.ts index 9ad6da6..73b1c1a 100644 --- a/src/utils/test-driver-rp2040.ts +++ b/src/utils/test-driver-rp2040.ts @@ -158,6 +158,10 @@ export class RP2040TestDriver implements ICortexTestDriver { } async readUint32(address: number) { - return this.rp2040.readUint32(address); + return this.rp2040.readUint32(address) >>> 0; + } + + async readInt32(address: number) { + return this.rp2040.readUint32(address) | 0; } } diff --git a/src/utils/test-driver.ts b/src/utils/test-driver.ts index 273d77f..8e997bb 100644 --- a/src/utils/test-driver.ts +++ b/src/utils/test-driver.ts @@ -41,5 +41,6 @@ export interface ICortexTestDriver { readUint8(address: number): Promise; readUint16(address: number): Promise; readUint32(address: number): Promise; + readInt32(address: number): Promise; tearDown(): Promise; }