rp2040js 1.3.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RPPLL = void 0;
4
+ const peripheral_js_1 = require("./peripheral.js");
5
+ // PLL register offsets
6
+ const PLL_CS = 0x00; // Control and Status
7
+ const PLL_PWR = 0x04; // Power control
8
+ const PLL_FBDIV_INT = 0x08; // Feedback divisor
9
+ const PLL_PRIM = 0x0c; // Primary post dividers
10
+ // PLL_CS bits
11
+ const PLL_CS_LOCK = 1 << 31;
12
+ const PLL_CS_BYPASS = 1 << 8;
13
+ const PLL_CS_REFDIV_MASK = 0x3f;
14
+ // PLL_FBDIV_INT bits
15
+ const PLL_FBDIV_INT_MASK = 0xfff;
16
+ // PLL_PRIM bits
17
+ const PLL_PRIM_POSTDIV1_SHIFT = 16;
18
+ const PLL_PRIM_POSTDIV2_SHIFT = 12;
19
+ const PLL_PRIM_POSTDIV_MASK = 0x7;
20
+ /**
21
+ * RP2040 PLL peripheral.
22
+ *
23
+ * Always reports locked (we have no startup delay to model), and computes its output
24
+ * frequency from the divider registers:
25
+ *
26
+ * f_out = (f_ref / REFDIV * FBDIV) / (POSTDIV1 * POSTDIV2)
27
+ *
28
+ * The frequency is what makes `RP2040.clkSys` follow `set_sys_clock_khz()` and friends.
29
+ */
30
+ class RPPLL extends peripheral_js_1.BasePeripheral {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.cs = 0x1; // REFDIV = 1
34
+ this.pwr = 0x2d; // VCO and post dividers powered down
35
+ this.fbdivInt = 0;
36
+ this.prim = 0x77000; // POSTDIV1 = 7, POSTDIV2 = 7
37
+ }
38
+ get refdiv() {
39
+ return this.cs & PLL_CS_REFDIV_MASK;
40
+ }
41
+ get fbdiv() {
42
+ return this.fbdivInt & PLL_FBDIV_INT_MASK;
43
+ }
44
+ get postdiv1() {
45
+ return (this.prim >>> PLL_PRIM_POSTDIV1_SHIFT) & PLL_PRIM_POSTDIV_MASK;
46
+ }
47
+ get postdiv2() {
48
+ return (this.prim >>> PLL_PRIM_POSTDIV2_SHIFT) & PLL_PRIM_POSTDIV_MASK;
49
+ }
50
+ /** PLL output frequency, in Hz, derived from the current register values. */
51
+ get frequency() {
52
+ const refFreq = this.rp2040.xoscFreq / (this.refdiv || 1);
53
+ if (this.cs & PLL_CS_BYPASS) {
54
+ return refFreq;
55
+ }
56
+ const postdiv = (this.postdiv1 || 1) * (this.postdiv2 || 1);
57
+ return (refFreq * this.fbdiv) / postdiv;
58
+ }
59
+ readUint32(offset) {
60
+ switch (offset) {
61
+ case PLL_CS:
62
+ return this.cs | PLL_CS_LOCK;
63
+ case PLL_PWR:
64
+ return this.pwr;
65
+ case PLL_FBDIV_INT:
66
+ return this.fbdivInt;
67
+ case PLL_PRIM:
68
+ return this.prim;
69
+ default:
70
+ return super.readUint32(offset);
71
+ }
72
+ }
73
+ writeUint32(offset, value) {
74
+ switch (offset) {
75
+ case PLL_CS:
76
+ this.cs = value;
77
+ break;
78
+ case PLL_PWR:
79
+ this.pwr = value;
80
+ break;
81
+ case PLL_FBDIV_INT:
82
+ this.fbdivInt = value;
83
+ break;
84
+ case PLL_PRIM:
85
+ this.prim = value;
86
+ break;
87
+ default:
88
+ super.writeUint32(offset, value);
89
+ return;
90
+ }
91
+ this.rp2040.updateClocks();
92
+ }
93
+ }
94
+ exports.RPPLL = RPPLL;
@@ -106,12 +106,15 @@ class PWMChannel {
106
106
  if (value & CSR_EN && !(this.csr & CSR_EN)) {
107
107
  this.updateDoubleBuffered();
108
108
  }
109
+ // PH_ADV and PH_RET are self-clearing, so they never appear in the stored csr value
109
110
  this.csr = value & ~(CSR_PH_ADV | CSR_PH_RET);
110
- if (this.csr & CSR_PH_ADV) {
111
- this.timer.advance(1);
112
- }
113
- if (this.csr & CSR_PH_RET) {
114
- this.timer.advance(-1);
111
+ if (value & CSR_EN) {
112
+ if (value & CSR_PH_ADV) {
113
+ this.timer.advance(1);
114
+ }
115
+ if (value & CSR_PH_RET) {
116
+ this.timer.advance(-1);
117
+ }
115
118
  }
116
119
  this.divMode = (this.csr >> CSR_DIVMODE_SHIFT) & CSR_DIVMODE_MASK;
117
120
  this.setBDirection(this.divMode === PWMDivMode.FreeRunning);
@@ -28,6 +28,8 @@ export declare class RPUART extends BasePeripheral implements Peripheral {
28
28
  get wordLength(): 5 | 6 | 7 | 8 | undefined;
29
29
  get baudDivider(): number;
30
30
  get baudRate(): number;
31
+ /** Re-reports the baud rate, unless the firmware has not set the divider yet. */
32
+ clkPeriChanged(): void;
31
33
  get flags(): number;
32
34
  checkInterrupts(): void;
33
35
  feedByte(value: number): void;
@@ -80,6 +80,13 @@ class RPUART extends peripheral_js_1.BasePeripheral {
80
80
  get baudRate() {
81
81
  return Math.round(this.rp2040.clkPeri / (this.baudDivider * 16));
82
82
  }
83
+ /** Re-reports the baud rate, unless the firmware has not set the divider yet. */
84
+ clkPeriChanged() {
85
+ var _a;
86
+ if (this.baudDivider) {
87
+ (_a = this.onBaudRateChange) === null || _a === void 0 ? void 0 : _a.call(this, this.baudRate);
88
+ }
89
+ }
83
90
  get flags() {
84
91
  return (this.rxFIFO.full ? RXFF : 0) | (this.rxFIFO.empty ? RXFE : 0) | TXFE;
85
92
  }
@@ -2,10 +2,12 @@ import { IClock } from './clock/clock.js';
2
2
  import { CortexM0Core } from './cortex-m0-core.js';
3
3
  import { GPIOPin } from './gpio-pin.js';
4
4
  import { RPADC } from './peripherals/adc.js';
5
+ import { RPClocks } from './peripherals/clocks.js';
5
6
  import { RPDMA } from './peripherals/dma.js';
6
7
  import { RPI2C } from './peripherals/i2c.js';
7
8
  import { Peripheral } from './peripherals/peripheral.js';
8
9
  import { RPPIO } from './peripherals/pio.js';
10
+ import { RPPLL } from './peripherals/pll.js';
9
11
  import { RPPPB } from './peripherals/ppb.js';
10
12
  import { RPPWM } from './peripherals/pwm.js';
11
13
  import { RPSPI } from './peripherals/spi.js';
@@ -19,6 +21,11 @@ export declare const RAM_START_ADDRESS = 536870912;
19
21
  export declare const APB_START_ADDRESS = 1073741824;
20
22
  export declare const DPRAM_START_ADDRESS = 1343225856;
21
23
  export declare const SIO_START_ADDRESS = 3489660928;
24
+ /**
25
+ * Notified after clk_sys changes. Listeners that cache a frequency derived from clk_sys
26
+ * (PIO clock dividers, for instance) use this to re-derive it.
27
+ */
28
+ export type ClockListener = (clkSys: number, oldClkSys: number) => void;
22
29
  export declare class RP2040 {
23
30
  readonly clock: IClock;
24
31
  readonly bootrom: Uint32Array<ArrayBuffer>;
@@ -32,6 +39,13 @@ export declare class RP2040 {
32
39
  readonly core: CortexM0Core;
33
40
  clkSys: number;
34
41
  clkPeri: number;
42
+ /** Crystal oscillator frequency. 12 MHz on the Raspberry Pi Pico and most other boards. */
43
+ xoscFreq: number;
44
+ /** Ring oscillator frequency. Varies with voltage/temperature on real silicon. */
45
+ roscFreq: number;
46
+ readonly pllSys: RPPLL;
47
+ readonly pllUsb: RPPLL;
48
+ readonly clocks: RPClocks;
35
49
  readonly ppb: RPPPB;
36
50
  readonly sio: RPSIO;
37
51
  readonly uart: RPUART[];
@@ -45,6 +59,7 @@ export declare class RP2040 {
45
59
  readonly usbCtrl: RPUSBController;
46
60
  readonly spi: RPSPI[];
47
61
  logger: Logger;
62
+ private readonly clockListeners;
48
63
  readonly peripherals: {
49
64
  [index: number]: Peripheral;
50
65
  };
@@ -53,6 +68,15 @@ export declare class RP2040 {
53
68
  loadBootrom(bootromData: Uint32Array): void;
54
69
  reset(): void;
55
70
  readUint32(address: number): number;
71
+ /**
72
+ * Recomputes `clkSys` and `clkPeri` from the PLL and CLOCKS registers and updates the
73
+ * peripherals derived from them. Until firmware configures the clock tree, each keeps its default.
74
+ */
75
+ updateClocks(): void;
76
+ private updateClkSys;
77
+ private updateClkPeri;
78
+ /** Registers a listener to be notified whenever clk_sys changes. Returns an unsubscribe function. */
79
+ addClockListener(listener: ClockListener): () => boolean;
56
80
  findPeripheral(address: number): Peripheral;
57
81
  /** We assume the address is 16-bit aligned */
58
82
  readUint16(address: number): number;
@@ -14,6 +14,7 @@ const io_js_1 = require("./peripherals/io.js");
14
14
  const pads_js_1 = require("./peripherals/pads.js");
15
15
  const peripheral_js_1 = require("./peripherals/peripheral.js");
16
16
  const pio_js_1 = require("./peripherals/pio.js");
17
+ const pll_js_1 = require("./peripherals/pll.js");
17
18
  const ppb_js_1 = require("./peripherals/ppb.js");
18
19
  const psm_js_1 = require("./peripherals/psm.js");
19
20
  const pwm_js_1 = require("./peripherals/pwm.js");
@@ -56,6 +57,13 @@ class RP2040 {
56
57
  /* Clocks */
57
58
  this.clkSys = 125 * MHz;
58
59
  this.clkPeri = 125 * MHz;
60
+ /** Crystal oscillator frequency. 12 MHz on the Raspberry Pi Pico and most other boards. */
61
+ this.xoscFreq = 12 * MHz;
62
+ /** Ring oscillator frequency. Varies with voltage/temperature on real silicon. */
63
+ this.roscFreq = 6.5 * MHz;
64
+ this.pllSys = new pll_js_1.RPPLL(this, 'PLL_SYS_BASE');
65
+ this.pllUsb = new pll_js_1.RPPLL(this, 'PLL_USB_BASE');
66
+ this.clocks = new clocks_js_1.RPClocks(this, 'CLOCKS_BASE');
59
67
  this.ppb = new ppb_js_1.RPPPB(this, 'PPB');
60
68
  this.sio = new sio_js_1.RPSIO(this);
61
69
  this.uart = [
@@ -128,11 +136,12 @@ class RP2040 {
128
136
  }),
129
137
  ];
130
138
  this.logger = new logging_js_1.ConsoleLogger(logging_js_1.LogLevel.Debug, true);
139
+ this.clockListeners = new Set();
131
140
  this.peripherals = {
132
141
  0x18000: new ssi_js_1.RPSSI(this, 'SSI'),
133
142
  0x40000: new sysinfo_js_1.RP2040SysInfo(this, 'SYSINFO_BASE'),
134
143
  0x40004: new syscfg_js_1.RP2040SysCfg(this, 'SYSCFG'),
135
- 0x40008: new clocks_js_1.RPClocks(this, 'CLOCKS_BASE'),
144
+ 0x40008: this.clocks,
136
145
  0x4000c: new reset_js_1.RPReset(this, 'RESETS_BASE'),
137
146
  0x40010: new psm_js_1.RPPSM(this, 'PSM_BASE'),
138
147
  0x40014: new io_js_1.RPIO(this, 'IO_BANK0_BASE'),
@@ -140,8 +149,8 @@ class RP2040 {
140
149
  0x4001c: new pads_js_1.RPPADS(this, 'PADS_BANK0_BASE', 'bank0'),
141
150
  0x40020: new pads_js_1.RPPADS(this, 'PADS_QSPI_BASE', 'qspi'),
142
151
  0x40024: new xosc_js_1.RPXOSC(this, 'XOSC_BASE'),
143
- 0x40028: new peripheral_js_1.UnimplementedPeripheral(this, 'PLL_SYS_BASE'),
144
- 0x4002c: new peripheral_js_1.UnimplementedPeripheral(this, 'PLL_USB_BASE'),
152
+ 0x40028: this.pllSys,
153
+ 0x4002c: this.pllUsb,
145
154
  0x40030: new busctrl_js_1.RPBUSCTRL(this, 'BUSCTRL_BASE'),
146
155
  0x40034: this.uart[0],
147
156
  0x40038: this.uart[1],
@@ -217,6 +226,46 @@ class RP2040 {
217
226
  this.logger.warn(LOG_NAME, `Read from invalid memory address: ${address.toString(16)}`);
218
227
  return 0xffffffff;
219
228
  }
229
+ /**
230
+ * Recomputes `clkSys` and `clkPeri` from the PLL and CLOCKS registers and updates the
231
+ * peripherals derived from them. Until firmware configures the clock tree, each keeps its default.
232
+ */
233
+ updateClocks() {
234
+ this.updateClkSys();
235
+ this.updateClkPeri();
236
+ }
237
+ updateClkSys() {
238
+ const clkSys = this.clocks.sysFreq;
239
+ // 0 means clk_sys is fed from an unmodelled or unconfigured source: keep the last value
240
+ if (!clkSys || clkSys === this.clkSys) {
241
+ return;
242
+ }
243
+ const oldClkSys = this.clkSys;
244
+ this.clkSys = clkSys;
245
+ this.ppb.systickTimer.frequency = clkSys;
246
+ for (const channel of this.pwm.channels) {
247
+ channel.timer.frequency = this.pwm.clockFreq;
248
+ }
249
+ for (const listener of this.clockListeners) {
250
+ listener(clkSys, oldClkSys);
251
+ }
252
+ }
253
+ updateClkPeri() {
254
+ const clkPeri = this.clocks.periFreq;
255
+ // 0 means the clock generator is stopped, or the source is one we do not model
256
+ if (!clkPeri || clkPeri === this.clkPeri) {
257
+ return;
258
+ }
259
+ this.clkPeri = clkPeri;
260
+ for (const uart of this.uart) {
261
+ uart.clkPeriChanged();
262
+ }
263
+ }
264
+ /** Registers a listener to be notified whenever clk_sys changes. Returns an unsubscribe function. */
265
+ addClockListener(listener) {
266
+ this.clockListeners.add(listener);
267
+ return () => this.clockListeners.delete(listener);
268
+ }
220
269
  findPeripheral(address) {
221
270
  return this.peripherals[(address >>> 14) << 2];
222
271
  }
@@ -37,6 +37,12 @@ class Timer32 {
37
37
  */
38
38
  advance(delta) {
39
39
  this.baseValue += delta;
40
+ if (this.topValue !== 0xffffffff) {
41
+ // Keep the base value in range, so that retarding past 0 wraps around to the top
42
+ const topModulo = this.timerMode === TimerMode.ZigZag ? this.topValue * 2 : this.topValue + 1;
43
+ this.baseValue = ((this.baseValue % topModulo) + topModulo) % topModulo;
44
+ }
45
+ this.updated();
40
46
  }
41
47
  get rawCounter() {
42
48
  const { baseFreq, prescalerValue, baseNanos, baseValue, enabled, timerMode } = this;
@@ -29,37 +29,37 @@ const registers = [
29
29
  `name:pc;bitsize:32;offset:60;encoding:int;format:hex;set:General Purpose Registers;generic:pc;alt-name:r15;gcc:15;dwarf:15;`,
30
30
  `name:cpsr;bitsize:32;offset:64;encoding:int;format:hex;set:General Purpose Registers;generic:flags;alt-name:psr;gcc:16;dwarf:16;`,
31
31
  ];
32
- const targetXML = `<?xml version="1.0"?>
33
- <!DOCTYPE target SYSTEM "gdb-target.dtd">
34
- <target version="1.0">
35
- <architecture>arm</architecture>
36
- <feature name="org.gnu.gdb.arm.m-profile">
37
- <reg name="r0" bitsize="32" regnum="0" save-restore="yes" type="int" group="general"/>
38
- <reg name="r1" bitsize="32" regnum="1" save-restore="yes" type="int" group="general"/>
39
- <reg name="r2" bitsize="32" regnum="2" save-restore="yes" type="int" group="general"/>
40
- <reg name="r3" bitsize="32" regnum="3" save-restore="yes" type="int" group="general"/>
41
- <reg name="r4" bitsize="32" regnum="4" save-restore="yes" type="int" group="general"/>
42
- <reg name="r5" bitsize="32" regnum="5" save-restore="yes" type="int" group="general"/>
43
- <reg name="r6" bitsize="32" regnum="6" save-restore="yes" type="int" group="general"/>
44
- <reg name="r7" bitsize="32" regnum="7" save-restore="yes" type="int" group="general"/>
45
- <reg name="r8" bitsize="32" regnum="8" save-restore="yes" type="int" group="general"/>
46
- <reg name="r9" bitsize="32" regnum="9" save-restore="yes" type="int" group="general"/>
47
- <reg name="r10" bitsize="32" regnum="10" save-restore="yes" type="int" group="general"/>
48
- <reg name="r11" bitsize="32" regnum="11" save-restore="yes" type="int" group="general"/>
49
- <reg name="r12" bitsize="32" regnum="12" save-restore="yes" type="int" group="general"/>
50
- <reg name="sp" bitsize="32" regnum="13" save-restore="yes" type="data_ptr" group="general"/>
51
- <reg name="lr" bitsize="32" regnum="14" save-restore="yes" type="int" group="general"/>
52
- <reg name="pc" bitsize="32" regnum="15" save-restore="yes" type="code_ptr" group="general"/>
53
- <reg name="xPSR" bitsize="32" regnum="16" save-restore="yes" type="int" group="general"/>
54
- </feature>
55
- <feature name="org.gnu.gdb.arm.m-system">
56
- <reg name="msp" bitsize="32" regnum="17" save-restore="yes" type="data_ptr" group="system"/>
57
- <reg name="psp" bitsize="32" regnum="18" save-restore="yes" type="data_ptr" group="system"/>
58
- <reg name="primask" bitsize="1" regnum="19" save-restore="yes" type="int8" group="system"/>
59
- <reg name="basepri" bitsize="8" regnum="20" save-restore="yes" type="int8" group="system"/>
60
- <reg name="faultmask" bitsize="1" regnum="21" save-restore="yes" type="int8" group="system"/>
61
- <reg name="control" bitsize="2" regnum="22" save-restore="yes" type="int8" group="system"/>
62
- </feature>
32
+ const targetXML = `<?xml version="1.0"?>
33
+ <!DOCTYPE target SYSTEM "gdb-target.dtd">
34
+ <target version="1.0">
35
+ <architecture>arm</architecture>
36
+ <feature name="org.gnu.gdb.arm.m-profile">
37
+ <reg name="r0" bitsize="32" regnum="0" save-restore="yes" type="int" group="general"/>
38
+ <reg name="r1" bitsize="32" regnum="1" save-restore="yes" type="int" group="general"/>
39
+ <reg name="r2" bitsize="32" regnum="2" save-restore="yes" type="int" group="general"/>
40
+ <reg name="r3" bitsize="32" regnum="3" save-restore="yes" type="int" group="general"/>
41
+ <reg name="r4" bitsize="32" regnum="4" save-restore="yes" type="int" group="general"/>
42
+ <reg name="r5" bitsize="32" regnum="5" save-restore="yes" type="int" group="general"/>
43
+ <reg name="r6" bitsize="32" regnum="6" save-restore="yes" type="int" group="general"/>
44
+ <reg name="r7" bitsize="32" regnum="7" save-restore="yes" type="int" group="general"/>
45
+ <reg name="r8" bitsize="32" regnum="8" save-restore="yes" type="int" group="general"/>
46
+ <reg name="r9" bitsize="32" regnum="9" save-restore="yes" type="int" group="general"/>
47
+ <reg name="r10" bitsize="32" regnum="10" save-restore="yes" type="int" group="general"/>
48
+ <reg name="r11" bitsize="32" regnum="11" save-restore="yes" type="int" group="general"/>
49
+ <reg name="r12" bitsize="32" regnum="12" save-restore="yes" type="int" group="general"/>
50
+ <reg name="sp" bitsize="32" regnum="13" save-restore="yes" type="data_ptr" group="general"/>
51
+ <reg name="lr" bitsize="32" regnum="14" save-restore="yes" type="int" group="general"/>
52
+ <reg name="pc" bitsize="32" regnum="15" save-restore="yes" type="code_ptr" group="general"/>
53
+ <reg name="xPSR" bitsize="32" regnum="16" save-restore="yes" type="int" group="general"/>
54
+ </feature>
55
+ <feature name="org.gnu.gdb.arm.m-system">
56
+ <reg name="msp" bitsize="32" regnum="17" save-restore="yes" type="data_ptr" group="system"/>
57
+ <reg name="psp" bitsize="32" regnum="18" save-restore="yes" type="data_ptr" group="system"/>
58
+ <reg name="primask" bitsize="1" regnum="19" save-restore="yes" type="int8" group="system"/>
59
+ <reg name="basepri" bitsize="8" regnum="20" save-restore="yes" type="int8" group="system"/>
60
+ <reg name="faultmask" bitsize="1" regnum="21" save-restore="yes" type="int8" group="system"/>
61
+ <reg name="control" bitsize="2" regnum="22" save-restore="yes" type="int8" group="system"/>
62
+ </feature>
63
63
  </target>`;
64
64
  const LOG_NAME = 'GDBServer';
65
65
  export class GDBServer {
@@ -5,7 +5,7 @@ export { I2CMode, I2CSpeed, RPI2C } from './peripherals/i2c.js';
5
5
  export { BasePeripheral, type Peripheral } from './peripherals/peripheral.js';
6
6
  export { RPPIO, StateMachine } from './peripherals/pio.js';
7
7
  export { RPUSBController } from './peripherals/usb.js';
8
- export { RP2040 } from './rp2040.js';
8
+ export { RP2040, type ClockListener } from './rp2040.js';
9
9
  export { Simulator } from './simulator.js';
10
10
  export { USBCDC } from './usb/cdc.js';
11
11
  export { DataDirection, DescriptorType, SetupRecipient, SetupRequest, SetupType, type ISetupPacketParams, } from './usb/interfaces.js';
@@ -22,6 +22,17 @@ export declare class RPClocks extends BasePeripheral implements Peripheral {
22
22
  rtcCtrl: number;
23
23
  rtcDiv: number;
24
24
  constructor(rp2040: RP2040, name: string);
25
+ /** clk_ref frequency, in Hz. GPIN0/GPIN1 are not modelled and yield 0. */
26
+ get refFreq(): number;
27
+ /** clk_sys frequency, in Hz. GPIN0/GPIN1 are not modelled and yield 0. */
28
+ get sysFreq(): number;
29
+ /**
30
+ * clk_peri frequency, in Hz. Feeds the UART and SPI baud rate generators. Returns 0 while
31
+ * the clock generator is stopped, and for the GPIN0/GPIN1 sources we do not model.
32
+ */
33
+ get periFreq(): number;
34
+ private get refSourceFreq();
35
+ private get sysSourceFreq();
25
36
  readUint32(offset: number): number;
26
37
  writeUint32(offset: number, value: number): void;
27
38
  }
@@ -31,6 +31,44 @@ const CLK_RTC_DIV = 0x70;
31
31
  const CLK_RTC_SELECTED = 0x74;
32
32
  const CLK_SYS_RESUS_CTRL = 0x78;
33
33
  const CLK_SYS_RESUS_STATUS = 0x7c;
34
+ // CLK_REF_CTRL
35
+ const CLK_REF_CTRL_SRC_MASK = 0x3;
36
+ const CLK_REF_CTRL_SRC_ROSC = 0x0;
37
+ const CLK_REF_CTRL_SRC_AUX = 0x1;
38
+ const CLK_REF_CTRL_SRC_XOSC = 0x2;
39
+ const CLK_REF_CTRL_AUXSRC_SHIFT = 5;
40
+ const CLK_REF_CTRL_AUXSRC_MASK = 0x3;
41
+ const CLK_REF_CTRL_AUXSRC_PLL_USB = 0x0;
42
+ // CLK_REF_DIV has no fractional part, only INT (bits 9:8)
43
+ const CLK_REF_DIV_INT_BITS = 0x300;
44
+ // CLK_SYS_CTRL
45
+ const CLK_SYS_CTRL_SRC_MASK = 0x1;
46
+ const CLK_SYS_CTRL_SRC_REF = 0x0;
47
+ const CLK_SYS_CTRL_AUXSRC_SHIFT = 5;
48
+ const CLK_SYS_CTRL_AUXSRC_MASK = 0x7;
49
+ const CLK_SYS_CTRL_AUXSRC_PLL_SYS = 0x0;
50
+ const CLK_SYS_CTRL_AUXSRC_PLL_USB = 0x1;
51
+ const CLK_SYS_CTRL_AUXSRC_ROSC = 0x2;
52
+ const CLK_SYS_CTRL_AUXSRC_XOSC = 0x3;
53
+ // CLK_PERI_CTRL. clk_peri has no SRC mux and no divider: it is driven straight from AUXSRC,
54
+ // which uses its own encoding (not the CLK_SYS one).
55
+ const CLK_PERI_CTRL_ENABLE = 1 << 11;
56
+ const CLK_PERI_CTRL_KILL = 1 << 10;
57
+ const CLK_PERI_CTRL_AUXSRC_SHIFT = 5;
58
+ const CLK_PERI_CTRL_AUXSRC_MASK = 0x7;
59
+ const CLK_PERI_CTRL_AUXSRC_CLK_SYS = 0x0;
60
+ const CLK_PERI_CTRL_AUXSRC_PLL_SYS = 0x1;
61
+ const CLK_PERI_CTRL_AUXSRC_PLL_USB = 0x2;
62
+ const CLK_PERI_CTRL_AUXSRC_ROSC = 0x3;
63
+ const CLK_PERI_CTRL_AUXSRC_XOSC = 0x4;
64
+ // CLK_x_DIV: 24.8 fixed point (INT bits 31:8, FRAC bits 7:0)
65
+ const CLK_DIV_INT_SHIFT = 8;
66
+ const CLK_DIV_FRAC_MASK = 0xff;
67
+ /** Decodes a CLK_x_DIV register value. INT = 0 means divide by 2^16. */
68
+ function clockDivisor(div) {
69
+ const int = div >>> CLK_DIV_INT_SHIFT;
70
+ return int ? int + (div & CLK_DIV_FRAC_MASK) / 256 : 0x10000;
71
+ }
34
72
  export class RPClocks extends BasePeripheral {
35
73
  constructor(rp2040, name) {
36
74
  super(rp2040, name);
@@ -55,6 +93,71 @@ export class RPClocks extends BasePeripheral {
55
93
  this.rtcCtrl = 0;
56
94
  this.rtcDiv = 0x100;
57
95
  }
96
+ /** clk_ref frequency, in Hz. GPIN0/GPIN1 are not modelled and yield 0. */
97
+ get refFreq() {
98
+ return this.refSourceFreq / clockDivisor(this.refDiv & CLK_REF_DIV_INT_BITS);
99
+ }
100
+ /** clk_sys frequency, in Hz. GPIN0/GPIN1 are not modelled and yield 0. */
101
+ get sysFreq() {
102
+ return this.sysSourceFreq / clockDivisor(this.sysDiv);
103
+ }
104
+ /**
105
+ * clk_peri frequency, in Hz. Feeds the UART and SPI baud rate generators. Returns 0 while
106
+ * the clock generator is stopped, and for the GPIN0/GPIN1 sources we do not model.
107
+ */
108
+ get periFreq() {
109
+ const { rp2040 } = this;
110
+ if (!(this.periCtrl & CLK_PERI_CTRL_ENABLE) || this.periCtrl & CLK_PERI_CTRL_KILL) {
111
+ return 0;
112
+ }
113
+ switch ((this.periCtrl >>> CLK_PERI_CTRL_AUXSRC_SHIFT) & CLK_PERI_CTRL_AUXSRC_MASK) {
114
+ case CLK_PERI_CTRL_AUXSRC_CLK_SYS:
115
+ return this.sysFreq;
116
+ case CLK_PERI_CTRL_AUXSRC_PLL_SYS:
117
+ return rp2040.pllSys.frequency;
118
+ case CLK_PERI_CTRL_AUXSRC_PLL_USB:
119
+ return rp2040.pllUsb.frequency;
120
+ case CLK_PERI_CTRL_AUXSRC_ROSC:
121
+ return rp2040.roscFreq;
122
+ case CLK_PERI_CTRL_AUXSRC_XOSC:
123
+ return rp2040.xoscFreq;
124
+ default:
125
+ return 0;
126
+ }
127
+ }
128
+ get refSourceFreq() {
129
+ const { rp2040 } = this;
130
+ switch (this.refCtrl & CLK_REF_CTRL_SRC_MASK) {
131
+ case CLK_REF_CTRL_SRC_ROSC:
132
+ return rp2040.roscFreq;
133
+ case CLK_REF_CTRL_SRC_XOSC:
134
+ return rp2040.xoscFreq;
135
+ case CLK_REF_CTRL_SRC_AUX: {
136
+ const auxsrc = (this.refCtrl >>> CLK_REF_CTRL_AUXSRC_SHIFT) & CLK_REF_CTRL_AUXSRC_MASK;
137
+ return auxsrc === CLK_REF_CTRL_AUXSRC_PLL_USB ? rp2040.pllUsb.frequency : 0;
138
+ }
139
+ default:
140
+ return 0;
141
+ }
142
+ }
143
+ get sysSourceFreq() {
144
+ const { rp2040 } = this;
145
+ if ((this.sysCtrl & CLK_SYS_CTRL_SRC_MASK) === CLK_SYS_CTRL_SRC_REF) {
146
+ return this.refFreq;
147
+ }
148
+ switch ((this.sysCtrl >>> CLK_SYS_CTRL_AUXSRC_SHIFT) & CLK_SYS_CTRL_AUXSRC_MASK) {
149
+ case CLK_SYS_CTRL_AUXSRC_PLL_SYS:
150
+ return rp2040.pllSys.frequency;
151
+ case CLK_SYS_CTRL_AUXSRC_PLL_USB:
152
+ return rp2040.pllUsb.frequency;
153
+ case CLK_SYS_CTRL_AUXSRC_ROSC:
154
+ return rp2040.roscFreq;
155
+ case CLK_SYS_CTRL_AUXSRC_XOSC:
156
+ return rp2040.xoscFreq;
157
+ default:
158
+ return 0;
159
+ }
160
+ }
58
161
  readUint32(offset) {
59
162
  switch (offset) {
60
163
  case CLK_GPOUT0_CTRL:
@@ -152,18 +255,23 @@ export class RPClocks extends BasePeripheral {
152
255
  break;
153
256
  case CLK_REF_CTRL:
154
257
  this.refCtrl = value;
258
+ this.rp2040.updateClocks();
155
259
  break;
156
260
  case CLK_REF_DIV:
157
261
  this.refDiv = value;
262
+ this.rp2040.updateClocks();
158
263
  break;
159
264
  case CLK_SYS_CTRL:
160
265
  this.sysCtrl = value;
266
+ this.rp2040.updateClocks();
161
267
  break;
162
268
  case CLK_SYS_DIV:
163
269
  this.sysDiv = value;
270
+ this.rp2040.updateClocks();
164
271
  break;
165
272
  case CLK_PERI_CTRL:
166
273
  this.periCtrl = value;
274
+ this.rp2040.updateClocks();
167
275
  break;
168
276
  case CLK_PERI_DIV:
169
277
  this.periDiv = value;
@@ -0,0 +1,25 @@
1
+ import { BasePeripheral, Peripheral } from './peripheral.js';
2
+ /**
3
+ * RP2040 PLL peripheral.
4
+ *
5
+ * Always reports locked (we have no startup delay to model), and computes its output
6
+ * frequency from the divider registers:
7
+ *
8
+ * f_out = (f_ref / REFDIV * FBDIV) / (POSTDIV1 * POSTDIV2)
9
+ *
10
+ * The frequency is what makes `RP2040.clkSys` follow `set_sys_clock_khz()` and friends.
11
+ */
12
+ export declare class RPPLL extends BasePeripheral implements Peripheral {
13
+ private cs;
14
+ private pwr;
15
+ private fbdivInt;
16
+ private prim;
17
+ get refdiv(): number;
18
+ get fbdiv(): number;
19
+ get postdiv1(): number;
20
+ get postdiv2(): number;
21
+ /** PLL output frequency, in Hz, derived from the current register values. */
22
+ get frequency(): number;
23
+ readUint32(offset: number): number;
24
+ writeUint32(offset: number, value: number): void;
25
+ }
@@ -0,0 +1,90 @@
1
+ import { BasePeripheral } from './peripheral.js';
2
+ // PLL register offsets
3
+ const PLL_CS = 0x00; // Control and Status
4
+ const PLL_PWR = 0x04; // Power control
5
+ const PLL_FBDIV_INT = 0x08; // Feedback divisor
6
+ const PLL_PRIM = 0x0c; // Primary post dividers
7
+ // PLL_CS bits
8
+ const PLL_CS_LOCK = 1 << 31;
9
+ const PLL_CS_BYPASS = 1 << 8;
10
+ const PLL_CS_REFDIV_MASK = 0x3f;
11
+ // PLL_FBDIV_INT bits
12
+ const PLL_FBDIV_INT_MASK = 0xfff;
13
+ // PLL_PRIM bits
14
+ const PLL_PRIM_POSTDIV1_SHIFT = 16;
15
+ const PLL_PRIM_POSTDIV2_SHIFT = 12;
16
+ const PLL_PRIM_POSTDIV_MASK = 0x7;
17
+ /**
18
+ * RP2040 PLL peripheral.
19
+ *
20
+ * Always reports locked (we have no startup delay to model), and computes its output
21
+ * frequency from the divider registers:
22
+ *
23
+ * f_out = (f_ref / REFDIV * FBDIV) / (POSTDIV1 * POSTDIV2)
24
+ *
25
+ * The frequency is what makes `RP2040.clkSys` follow `set_sys_clock_khz()` and friends.
26
+ */
27
+ export class RPPLL extends BasePeripheral {
28
+ constructor() {
29
+ super(...arguments);
30
+ this.cs = 0x1; // REFDIV = 1
31
+ this.pwr = 0x2d; // VCO and post dividers powered down
32
+ this.fbdivInt = 0;
33
+ this.prim = 0x77000; // POSTDIV1 = 7, POSTDIV2 = 7
34
+ }
35
+ get refdiv() {
36
+ return this.cs & PLL_CS_REFDIV_MASK;
37
+ }
38
+ get fbdiv() {
39
+ return this.fbdivInt & PLL_FBDIV_INT_MASK;
40
+ }
41
+ get postdiv1() {
42
+ return (this.prim >>> PLL_PRIM_POSTDIV1_SHIFT) & PLL_PRIM_POSTDIV_MASK;
43
+ }
44
+ get postdiv2() {
45
+ return (this.prim >>> PLL_PRIM_POSTDIV2_SHIFT) & PLL_PRIM_POSTDIV_MASK;
46
+ }
47
+ /** PLL output frequency, in Hz, derived from the current register values. */
48
+ get frequency() {
49
+ const refFreq = this.rp2040.xoscFreq / (this.refdiv || 1);
50
+ if (this.cs & PLL_CS_BYPASS) {
51
+ return refFreq;
52
+ }
53
+ const postdiv = (this.postdiv1 || 1) * (this.postdiv2 || 1);
54
+ return (refFreq * this.fbdiv) / postdiv;
55
+ }
56
+ readUint32(offset) {
57
+ switch (offset) {
58
+ case PLL_CS:
59
+ return this.cs | PLL_CS_LOCK;
60
+ case PLL_PWR:
61
+ return this.pwr;
62
+ case PLL_FBDIV_INT:
63
+ return this.fbdivInt;
64
+ case PLL_PRIM:
65
+ return this.prim;
66
+ default:
67
+ return super.readUint32(offset);
68
+ }
69
+ }
70
+ writeUint32(offset, value) {
71
+ switch (offset) {
72
+ case PLL_CS:
73
+ this.cs = value;
74
+ break;
75
+ case PLL_PWR:
76
+ this.pwr = value;
77
+ break;
78
+ case PLL_FBDIV_INT:
79
+ this.fbdivInt = value;
80
+ break;
81
+ case PLL_PRIM:
82
+ this.prim = value;
83
+ break;
84
+ default:
85
+ super.writeUint32(offset, value);
86
+ return;
87
+ }
88
+ this.rp2040.updateClocks();
89
+ }
90
+ }