Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sio): fix implemented Hardware Divider #37

Merged
merged 15 commits into from
May 24, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
64 changes: 64 additions & 0 deletions src/sio.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { RP2040, SIO_START_ADDRESS } from './rp2040';

//HARDWARE DIVIDER
const _DIV_UDIVIDEND = SIO_START_ADDRESS + 0x060; // Divider unsigned dividend
const _DIV_UDIVISOR = SIO_START_ADDRESS + 0x064; // Divider unsigned divisor
const _DIV_SDIVIDEND = SIO_START_ADDRESS + 0x068; // Divider signed dividend
const _DIV_SDIVISOR = SIO_START_ADDRESS + 0x06c; // Divider signed divisor
const _DIV_QUOTIENT = SIO_START_ADDRESS + 0x070; // Divider result quotient
const _DIV_REMAINDER = SIO_START_ADDRESS + 0x074; //Divider result remainder
const _DIV_CSR = SIO_START_ADDRESS + 0x078;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please remove the _ (underscore) in front of the constant? The idea is to keep this code consistent with the rest of the code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I'm moving test to be gdbclient ready, I'll do it in the next commit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh great. I was going to suggest that, but you are faster!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok done,
I had to add several "| 0"which are needed when running on silicone and the read value is signed, maybe a readSInt32 implementation could be better? or maybe not as it matters only during test?

copied the silicone behavior when divisor=0 -> quot=-1, rem=dividend
I wasn't able to find any word related to div/0 exceptions in the datasheets, is this possible?

_DIV_XXX has been renamed to SIO_DIV_XXX, I choose this way as SIO_DIV_XXX = SIO_ADDRESS_BASE + DIV_XXX
Even if declared in different files, I wouldn't have the same name to both offset and full address.

Copy link
Contributor

@urish urish May 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

copied the silicone behavior when divisor=0 -> quot=-1, rem=dividend

Good idea to test what happens when dividing by zero.

I wasn't able to find any word related to div/0 exceptions in the datasheets, is this possible?

No idea! Did you see it getting any exception on the silicone?

I had to add several "| 0"which are needed when running on silicone and the read value is signed, maybe a readSInt32 implementation could be better? or maybe not as it matters only during test?

I think this is a good idea, it will make the tests easier to read / understand. I suggest calling it readInt32() (without the S), similar to how they call the signed int array in JavaScript (and also C++ type int32_t vs uint32_t).

_DIV_XXX has been renamed to SIO_DIV_XXX, I choose this way as SIO_DIV_XXX = SIO_ADDRESS_BASE + DIV_XXX
Even if declared in different files, I wouldn't have the same name to both offset and full address.

👍


describe('RPSIO', () => {
describe('Hardware Divider', () => {
it('should set perform a signed hardware divider 123456 / -321 = -384 R192', () => {
const rp2040 = new RP2040();
expect(rp2040.readUint32(_DIV_CSR)).toEqual(0);
rp2040.writeUint32(_DIV_SDIVIDEND, 123456);
rp2040.writeUint32(_DIV_SDIVISOR, -321);
expect(rp2040.readUint32(_DIV_CSR)).toEqual(1);
expect(rp2040.readUint32(_DIV_REMAINDER)).toEqual(192);
expect(rp2040.readUint32(_DIV_QUOTIENT)).toEqual(-384);
expect(rp2040.readUint32(_DIV_CSR)).toEqual(0);
});

it('should set perform an unsigned hardware divider 123456 / 321 = 384 R192', () => {
const rp2040 = new RP2040();
expect(rp2040.readUint32(_DIV_CSR)).toEqual(0);
rp2040.writeUint32(_DIV_UDIVIDEND, 123456);
const cycles = rp2040.cycles;
rp2040.writeUint32(_DIV_UDIVISOR, 321);
expect(rp2040.cycles - cycles).toEqual(8);
expect(rp2040.readUint32(_DIV_CSR)).toEqual(1);
expect(rp2040.readUint32(_DIV_REMAINDER)).toEqual(192);
expect(rp2040.readUint32(_DIV_QUOTIENT)).toEqual(384);
expect(rp2040.readUint32(_DIV_CSR)).toEqual(0);
});

it('should perform a division, store the result, do another division then restore the previously stored result ', () => {
const rp2040 = new RP2040();
rp2040.writeUint32(_DIV_SDIVIDEND, 123456);
rp2040.writeUint32(_DIV_SDIVISOR, -321);
const remainder = rp2040.readUint32(_DIV_REMAINDER);
const quotient = rp2040.readUint32(_DIV_QUOTIENT);
expect(remainder).toEqual(192);
expect(quotient).toEqual(-384);
rp2040.writeUint32(_DIV_UDIVIDEND, 123);
rp2040.writeUint32(_DIV_UDIVISOR, 7);
expect(rp2040.readUint32(_DIV_REMAINDER)).toEqual(4);
expect(rp2040.readUint32(_DIV_QUOTIENT)).toEqual(17);
rp2040.writeUint32(_DIV_REMAINDER, remainder);
rp2040.writeUint32(_DIV_QUOTIENT, quotient);
expect(rp2040.readUint32(_DIV_REMAINDER)).toEqual(192);
expect(rp2040.readUint32(_DIV_QUOTIENT)).toEqual(-384);
});

it('should set perform an unsigned division by zero 123456 / 0 = Infinity RNaN', () => {
const rp2040 = new RP2040();
rp2040.writeUint32(_DIV_UDIVIDEND, 123456);
rp2040.writeUint32(_DIV_UDIVISOR, 0);
expect(rp2040.readUint32(_DIV_REMAINDER)).toEqual(NaN);
expect(rp2040.readUint32(_DIV_QUOTIENT)).toEqual(Infinity);
});
});
});
49 changes: 49 additions & 0 deletions src/sio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,25 @@ 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) {}

Expand Down Expand Up @@ -62,6 +76,19 @@ export class RPSIO {
case CPUID:
// Returns the current CPU core id (always 0 for now)
return 0;
case DIV_UDIVIDEND:
case DIV_SDIVIDEND:
return this.divDividend;
case DIV_UDIVISOR:
case DIV_SDIVISOR:
return this.divDivisor;
case DIV_QUOTIENT:
this.divCSR = 0;
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 +144,28 @@ export class RPSIO {
case GPIO_HI_OE_XOR:
this.qspiGpioOutputEnable ^= value & GPIO_MASK;
break;
case DIV_UDIVIDEND:
case DIV_SDIVIDEND:
this.divDividend = value | 0;
this.divCSR = 1;
this.divQuotient = Math.trunc(this.divDividend / this.divDivisor);
this.divRemainder = Math.trunc(this.divDividend % this.divDivisor);
this.rp2040.cycles += 8;
break;
case DIV_UDIVISOR:
case DIV_SDIVISOR:
this.divDivisor = value | 0;
this.divCSR = 1;
this.divQuotient = Math.trunc(this.divDividend / this.divDivisor);
this.divRemainder = Math.trunc(this.divDividend % this.divDivisor);
this.rp2040.cycles += 8;
break;
case DIV_QUOTIENT:
this.divQuotient = value | 0;
break;
case DIV_REMAINDER:
this.divRemainder = value | 0;
break;
}
}
}