Skip to content

Commit

Permalink
feat(sio): implement Integer Divider (#37)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Turro75 authored May 24, 2021
1 parent 9387184 commit 36f2e7c
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 2 deletions.
37 changes: 37 additions & 0 deletions src/peripherals/reset.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
3 changes: 2 additions & 1 deletion src/rp2040.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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'),
Expand Down
127 changes: 127 additions & 0 deletions src/sio.spec.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
});
70 changes: 70 additions & 0 deletions src/sio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
}
4 changes: 4 additions & 0 deletions src/utils/test-driver-gdb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
6 changes: 5 additions & 1 deletion src/utils/test-driver-rp2040.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
1 change: 1 addition & 0 deletions src/utils/test-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ export interface ICortexTestDriver {
readUint8(address: number): Promise<number>;
readUint16(address: number): Promise<number>;
readUint32(address: number): Promise<number>;
readInt32(address: number): Promise<number>;
tearDown(): Promise<void>;
}

0 comments on commit 36f2e7c

Please sign in to comment.