Skip to content

Commit

Permalink
feat(sio): start working on GPIO implementation #20
Browse files Browse the repository at this point in the history
  • Loading branch information
urish committed May 18, 2021
1 parent 68d33d5 commit 9387184
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 20 deletions.
26 changes: 26 additions & 0 deletions src/gpio-pin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { RP2040 } from './rp2040';

export enum GPIOPinState {
Low,
High,
Input,
InputPullUp,
InputPullDown,
}

export class GPIOPin {
constructor(readonly rp2040: RP2040, readonly index: number) {}

get value() {
const { index, rp2040 } = this;
const bitmask = 1 << index;
if (rp2040.sio.gpioOutputEnable & bitmask) {
return rp2040.sio.gpioValue & bitmask ? GPIOPinState.High : GPIOPinState.Low;
} else {
// TODO account for pullup/pulldown
return GPIOPinState.Input;
}
}

// TODO add a way to listen for value changes
}
63 changes: 43 additions & 20 deletions src/rp2040.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { IClockTimer, IClock } from './clock/clock';
import { IClock, IClockTimer } from './clock/clock';
import { RealtimeClock } from './clock/realtime-clock';
import { GPIOPin } from './gpio-pin';
import { Peripheral, UnimplementedPeripheral } from './peripherals/peripheral';
import { RP2040RTC } from './peripherals/rtc';
import { RP2040SysCfg } from './peripherals/syscfg';
import { RPTimer } from './peripherals/timer';
import { RPUART } from './peripherals/uart';
import { RPSIO } from './sio';

export const FLASH_START_ADDRESS = 0x10000000;
export const FLASH_END_ADDRESS = 0x14000000;
Expand All @@ -13,8 +15,6 @@ export const SIO_START_ADDRESS = 0xd0000000;

/* eslint-disable @typescript-eslint/no-unused-vars */

const SIO_CPUID_OFFSET = 0;

const XIP_SSI_BASE = 0x18000000;
const SSI_TXFLR_OFFSET = 0x00000020;
const SSI_RXFLR_OFFSET = 0x00000024;
Expand Down Expand Up @@ -109,8 +109,43 @@ export class RP2040 {
readonly writeHooks = new Map<number, CPUWriteCallback>();
readonly readHooks = new Map<number, CPUReadCallback>();

readonly sio = new RPSIO(this);

readonly uart = [new RPUART(this, 'UART0'), new RPUART(this, 'UART1')];

readonly gpio = [
new GPIOPin(this, 0),
new GPIOPin(this, 1),
new GPIOPin(this, 2),
new GPIOPin(this, 3),
new GPIOPin(this, 4),
new GPIOPin(this, 5),
new GPIOPin(this, 6),
new GPIOPin(this, 7),
new GPIOPin(this, 8),
new GPIOPin(this, 9),
new GPIOPin(this, 10),
new GPIOPin(this, 11),
new GPIOPin(this, 12),
new GPIOPin(this, 13),
new GPIOPin(this, 14),
new GPIOPin(this, 15),
new GPIOPin(this, 16),
new GPIOPin(this, 17),
new GPIOPin(this, 18),
new GPIOPin(this, 19),
new GPIOPin(this, 20),
new GPIOPin(this, 21),
new GPIOPin(this, 22),
new GPIOPin(this, 23),
new GPIOPin(this, 24),
new GPIOPin(this, 25),
new GPIOPin(this, 26),
new GPIOPin(this, 27),
new GPIOPin(this, 28),
new GPIOPin(this, 29),
];

private stopped = false;

// APSR fields
Expand Down Expand Up @@ -193,10 +228,6 @@ export class RP2040 {
constructor(readonly clock: IClock = new RealtimeClock()) {
this.SP = 0xfffffffc;
this.bankedSP = 0xfffffffc;
this.readHooks.set(SIO_START_ADDRESS + SIO_CPUID_OFFSET, () => {
// Returns the current CPU core id (always 0 for now)
return 0;
});
this.readHooks.set(XIP_SSI_BASE + SSI_TXFLR_OFFSET, () => 0);
this.readHooks.set(XIP_SSI_BASE + SSI_RXFLR_OFFSET, () => 0);
this.readHooks.set(XIP_SSI_BASE + SSI_SR_OFFSET, () => {
Expand Down Expand Up @@ -436,6 +467,10 @@ export class RP2040 {
const hook = this.readHooks.get(address);
if (hook) {
return hook(address);
} else {
if (address >= SIO_START_ADDRESS && address < SIO_START_ADDRESS + 0x10000000) {
return this.sio.readUint32(address - SIO_START_ADDRESS);
}
}
}
console.warn(`Read from invalid memory address: ${address.toString(16)}`);
Expand Down Expand Up @@ -471,19 +506,7 @@ export class RP2040 {
} else if (address >= RAM_START_ADDRESS && address < RAM_START_ADDRESS + this.sram.length) {
this.sramView.setUint32(address - RAM_START_ADDRESS, value, true);
} else if (address >= SIO_START_ADDRESS && address < SIO_START_ADDRESS + 0x10000000) {
const sioAddress = address - SIO_START_ADDRESS;
// SIO write
const pinList = [];
for (let i = 0; i < 32; i++) {
if (value & (1 << i)) {
pinList.push(i);
}
}
if (sioAddress === 20) {
console.log(`GPIO pins ${pinList} set to HIGH`);
} else if (sioAddress === 24) {
console.log(`GPIO pins ${pinList} set to LOW`);
}
this.sio.writeUint32(address - SIO_START_ADDRESS, value);
} else if (address >= USBCTRL_BASE && address < USBCTRL_BASE + 0x100000) {
// Ignore these USB writes for now
} else {
Expand Down
122 changes: 122 additions & 0 deletions src/sio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { RP2040 } from './rp2040';

const CPUID = 0x000;

// GPIO
const GPIO_IN = 0x004; // Input value for GPIO pins
const GPIO_HI_IN = 0x008; // Input value for QSPI pins
const GPIO_OUT = 0x010; // GPIO output value
const GPIO_OUT_SET = 0x014; // GPIO output value set
const GPIO_OUT_CLR = 0x018; // GPIO output value clear
const GPIO_OUT_XOR = 0x01c; // GPIO output value XOR
const GPIO_OE = 0x020; // GPIO output enable
const GPIO_OE_SET = 0x024; // GPIO output enable set
const GPIO_OE_CLR = 0x028; // GPIO output enable clear
const GPIO_OE_XOR = 0x02c; // GPIO output enable XOR
const GPIO_HI_OUT = 0x030; // QSPI output value
const GPIO_HI_OUT_SET = 0x034; // QSPI output value set
const GPIO_HI_OUT_CLR = 0x038; // QSPI output value clear
const GPIO_HI_OUT_XOR = 0x03c; // QSPI output value XOR
const GPIO_HI_OE = 0x040; // QSPI output enable
const GPIO_HI_OE_SET = 0x044; // QSPI output enable set
const GPIO_HI_OE_CLR = 0x048; // QSPI output enable clear
const GPIO_HI_OE_XOR = 0x04c; // QSPI output enable XOR

const GPIO_MASK = 0x3fffffff;

export class RPSIO {
gpioValue = 0;
gpioOutputEnable = 0;
qspiGpioValue = 0;
qspiGpioOutputEnable = 0;

constructor(private readonly rp2040: RP2040) {}

readUint32(offset: number) {
switch (offset) {
case GPIO_IN:
return 0; // TODO implement!
case GPIO_HI_IN:
return 0; // TODO implement!
case GPIO_OUT:
return this.gpioValue;
case GPIO_OE:
return this.gpioOutputEnable;
case GPIO_HI_OUT:
return this.qspiGpioValue;
case GPIO_HI_OE:
return this.qspiGpioOutputEnable;
case GPIO_OUT_SET:
case GPIO_OUT_CLR:
case GPIO_OUT_XOR:
case GPIO_OE_SET:
case GPIO_OE_CLR:
case GPIO_OE_XOR:
case GPIO_HI_OUT_SET:
case GPIO_HI_OUT_CLR:
case GPIO_HI_OUT_XOR:
case GPIO_HI_OE_SET:
case GPIO_HI_OE_CLR:
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;
}
console.warn(`Read from invalid SIO address: ${offset.toString(16)}`);
return 0xffffffff;
}

writeUint32(offset: number, value: number) {
switch (offset) {
case GPIO_OUT:
this.gpioValue = value & GPIO_MASK;
break;
case GPIO_OUT_SET:
this.gpioValue |= value & GPIO_MASK;
break;
case GPIO_OUT_CLR:
this.gpioValue &= ~value;
break;
case GPIO_OUT_XOR:
this.gpioValue ^= value & GPIO_MASK;
break;
case GPIO_OE:
this.gpioOutputEnable = value & GPIO_MASK;
break;
case GPIO_OE_SET:
this.gpioOutputEnable |= value & GPIO_MASK;
break;
case GPIO_OE_CLR:
this.gpioOutputEnable &= ~value;
break;
case GPIO_OE_XOR:
this.gpioOutputEnable ^= value & GPIO_MASK;
break;
case GPIO_HI_OUT:
this.qspiGpioValue = value & GPIO_MASK;
break;
case GPIO_HI_OUT_SET:
this.qspiGpioValue |= value & GPIO_MASK;
break;
case GPIO_HI_OUT_CLR:
this.qspiGpioValue &= ~value;
break;
case GPIO_HI_OUT_XOR:
this.qspiGpioValue ^= value & GPIO_MASK;
break;
case GPIO_HI_OE:
this.qspiGpioOutputEnable = value & GPIO_MASK;
break;
case GPIO_HI_OE_SET:
this.qspiGpioOutputEnable |= value & GPIO_MASK;
break;
case GPIO_HI_OE_CLR:
this.qspiGpioOutputEnable &= ~value;
break;
case GPIO_HI_OE_XOR:
this.qspiGpioOutputEnable ^= value & GPIO_MASK;
break;
}
}
}

0 comments on commit 9387184

Please sign in to comment.