rp2040js 1.3.4 → 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.
@@ -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
  }
@@ -34,6 +34,44 @@ const CLK_RTC_DIV = 0x70;
34
34
  const CLK_RTC_SELECTED = 0x74;
35
35
  const CLK_SYS_RESUS_CTRL = 0x78;
36
36
  const CLK_SYS_RESUS_STATUS = 0x7c;
37
+ // CLK_REF_CTRL
38
+ const CLK_REF_CTRL_SRC_MASK = 0x3;
39
+ const CLK_REF_CTRL_SRC_ROSC = 0x0;
40
+ const CLK_REF_CTRL_SRC_AUX = 0x1;
41
+ const CLK_REF_CTRL_SRC_XOSC = 0x2;
42
+ const CLK_REF_CTRL_AUXSRC_SHIFT = 5;
43
+ const CLK_REF_CTRL_AUXSRC_MASK = 0x3;
44
+ const CLK_REF_CTRL_AUXSRC_PLL_USB = 0x0;
45
+ // CLK_REF_DIV has no fractional part, only INT (bits 9:8)
46
+ const CLK_REF_DIV_INT_BITS = 0x300;
47
+ // CLK_SYS_CTRL
48
+ const CLK_SYS_CTRL_SRC_MASK = 0x1;
49
+ const CLK_SYS_CTRL_SRC_REF = 0x0;
50
+ const CLK_SYS_CTRL_AUXSRC_SHIFT = 5;
51
+ const CLK_SYS_CTRL_AUXSRC_MASK = 0x7;
52
+ const CLK_SYS_CTRL_AUXSRC_PLL_SYS = 0x0;
53
+ const CLK_SYS_CTRL_AUXSRC_PLL_USB = 0x1;
54
+ const CLK_SYS_CTRL_AUXSRC_ROSC = 0x2;
55
+ const CLK_SYS_CTRL_AUXSRC_XOSC = 0x3;
56
+ // CLK_PERI_CTRL. clk_peri has no SRC mux and no divider: it is driven straight from AUXSRC,
57
+ // which uses its own encoding (not the CLK_SYS one).
58
+ const CLK_PERI_CTRL_ENABLE = 1 << 11;
59
+ const CLK_PERI_CTRL_KILL = 1 << 10;
60
+ const CLK_PERI_CTRL_AUXSRC_SHIFT = 5;
61
+ const CLK_PERI_CTRL_AUXSRC_MASK = 0x7;
62
+ const CLK_PERI_CTRL_AUXSRC_CLK_SYS = 0x0;
63
+ const CLK_PERI_CTRL_AUXSRC_PLL_SYS = 0x1;
64
+ const CLK_PERI_CTRL_AUXSRC_PLL_USB = 0x2;
65
+ const CLK_PERI_CTRL_AUXSRC_ROSC = 0x3;
66
+ const CLK_PERI_CTRL_AUXSRC_XOSC = 0x4;
67
+ // CLK_x_DIV: 24.8 fixed point (INT bits 31:8, FRAC bits 7:0)
68
+ const CLK_DIV_INT_SHIFT = 8;
69
+ const CLK_DIV_FRAC_MASK = 0xff;
70
+ /** Decodes a CLK_x_DIV register value. INT = 0 means divide by 2^16. */
71
+ function clockDivisor(div) {
72
+ const int = div >>> CLK_DIV_INT_SHIFT;
73
+ return int ? int + (div & CLK_DIV_FRAC_MASK) / 256 : 0x10000;
74
+ }
37
75
  class RPClocks extends peripheral_js_1.BasePeripheral {
38
76
  constructor(rp2040, name) {
39
77
  super(rp2040, name);
@@ -58,6 +96,71 @@ class RPClocks extends peripheral_js_1.BasePeripheral {
58
96
  this.rtcCtrl = 0;
59
97
  this.rtcDiv = 0x100;
60
98
  }
99
+ /** clk_ref frequency, in Hz. GPIN0/GPIN1 are not modelled and yield 0. */
100
+ get refFreq() {
101
+ return this.refSourceFreq / clockDivisor(this.refDiv & CLK_REF_DIV_INT_BITS);
102
+ }
103
+ /** clk_sys frequency, in Hz. GPIN0/GPIN1 are not modelled and yield 0. */
104
+ get sysFreq() {
105
+ return this.sysSourceFreq / clockDivisor(this.sysDiv);
106
+ }
107
+ /**
108
+ * clk_peri frequency, in Hz. Feeds the UART and SPI baud rate generators. Returns 0 while
109
+ * the clock generator is stopped, and for the GPIN0/GPIN1 sources we do not model.
110
+ */
111
+ get periFreq() {
112
+ const { rp2040 } = this;
113
+ if (!(this.periCtrl & CLK_PERI_CTRL_ENABLE) || this.periCtrl & CLK_PERI_CTRL_KILL) {
114
+ return 0;
115
+ }
116
+ switch ((this.periCtrl >>> CLK_PERI_CTRL_AUXSRC_SHIFT) & CLK_PERI_CTRL_AUXSRC_MASK) {
117
+ case CLK_PERI_CTRL_AUXSRC_CLK_SYS:
118
+ return this.sysFreq;
119
+ case CLK_PERI_CTRL_AUXSRC_PLL_SYS:
120
+ return rp2040.pllSys.frequency;
121
+ case CLK_PERI_CTRL_AUXSRC_PLL_USB:
122
+ return rp2040.pllUsb.frequency;
123
+ case CLK_PERI_CTRL_AUXSRC_ROSC:
124
+ return rp2040.roscFreq;
125
+ case CLK_PERI_CTRL_AUXSRC_XOSC:
126
+ return rp2040.xoscFreq;
127
+ default:
128
+ return 0;
129
+ }
130
+ }
131
+ get refSourceFreq() {
132
+ const { rp2040 } = this;
133
+ switch (this.refCtrl & CLK_REF_CTRL_SRC_MASK) {
134
+ case CLK_REF_CTRL_SRC_ROSC:
135
+ return rp2040.roscFreq;
136
+ case CLK_REF_CTRL_SRC_XOSC:
137
+ return rp2040.xoscFreq;
138
+ case CLK_REF_CTRL_SRC_AUX: {
139
+ const auxsrc = (this.refCtrl >>> CLK_REF_CTRL_AUXSRC_SHIFT) & CLK_REF_CTRL_AUXSRC_MASK;
140
+ return auxsrc === CLK_REF_CTRL_AUXSRC_PLL_USB ? rp2040.pllUsb.frequency : 0;
141
+ }
142
+ default:
143
+ return 0;
144
+ }
145
+ }
146
+ get sysSourceFreq() {
147
+ const { rp2040 } = this;
148
+ if ((this.sysCtrl & CLK_SYS_CTRL_SRC_MASK) === CLK_SYS_CTRL_SRC_REF) {
149
+ return this.refFreq;
150
+ }
151
+ switch ((this.sysCtrl >>> CLK_SYS_CTRL_AUXSRC_SHIFT) & CLK_SYS_CTRL_AUXSRC_MASK) {
152
+ case CLK_SYS_CTRL_AUXSRC_PLL_SYS:
153
+ return rp2040.pllSys.frequency;
154
+ case CLK_SYS_CTRL_AUXSRC_PLL_USB:
155
+ return rp2040.pllUsb.frequency;
156
+ case CLK_SYS_CTRL_AUXSRC_ROSC:
157
+ return rp2040.roscFreq;
158
+ case CLK_SYS_CTRL_AUXSRC_XOSC:
159
+ return rp2040.xoscFreq;
160
+ default:
161
+ return 0;
162
+ }
163
+ }
61
164
  readUint32(offset) {
62
165
  switch (offset) {
63
166
  case CLK_GPOUT0_CTRL:
@@ -155,18 +258,23 @@ class RPClocks extends peripheral_js_1.BasePeripheral {
155
258
  break;
156
259
  case CLK_REF_CTRL:
157
260
  this.refCtrl = value;
261
+ this.rp2040.updateClocks();
158
262
  break;
159
263
  case CLK_REF_DIV:
160
264
  this.refDiv = value;
265
+ this.rp2040.updateClocks();
161
266
  break;
162
267
  case CLK_SYS_CTRL:
163
268
  this.sysCtrl = value;
269
+ this.rp2040.updateClocks();
164
270
  break;
165
271
  case CLK_SYS_DIV:
166
272
  this.sysDiv = value;
273
+ this.rp2040.updateClocks();
167
274
  break;
168
275
  case CLK_PERI_CTRL:
169
276
  this.periCtrl = value;
277
+ this.rp2040.updateClocks();
170
278
  break;
171
279
  case CLK_PERI_DIV:
172
280
  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,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;
@@ -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
  }
@@ -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
+ }
@@ -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;
@@ -77,6 +77,13 @@ export class RPUART extends BasePeripheral {
77
77
  get baudRate() {
78
78
  return Math.round(this.rp2040.clkPeri / (this.baudDivider * 16));
79
79
  }
80
+ /** Re-reports the baud rate, unless the firmware has not set the divider yet. */
81
+ clkPeriChanged() {
82
+ var _a;
83
+ if (this.baudDivider) {
84
+ (_a = this.onBaudRateChange) === null || _a === void 0 ? void 0 : _a.call(this, this.baudRate);
85
+ }
86
+ }
80
87
  get flags() {
81
88
  return (this.rxFIFO.full ? RXFF : 0) | (this.rxFIFO.empty ? RXFE : 0) | TXFE;
82
89
  }
@@ -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;
@@ -11,6 +11,7 @@ import { RPIO } from './peripherals/io.js';
11
11
  import { RPPADS } from './peripherals/pads.js';
12
12
  import { UnimplementedPeripheral } from './peripherals/peripheral.js';
13
13
  import { RPPIO } from './peripherals/pio.js';
14
+ import { RPPLL } from './peripherals/pll.js';
14
15
  import { RPPPB } from './peripherals/ppb.js';
15
16
  import { RPPSM } from './peripherals/psm.js';
16
17
  import { RPPWM } from './peripherals/pwm.js';
@@ -53,6 +54,13 @@ export class RP2040 {
53
54
  /* Clocks */
54
55
  this.clkSys = 125 * MHz;
55
56
  this.clkPeri = 125 * MHz;
57
+ /** Crystal oscillator frequency. 12 MHz on the Raspberry Pi Pico and most other boards. */
58
+ this.xoscFreq = 12 * MHz;
59
+ /** Ring oscillator frequency. Varies with voltage/temperature on real silicon. */
60
+ this.roscFreq = 6.5 * MHz;
61
+ this.pllSys = new RPPLL(this, 'PLL_SYS_BASE');
62
+ this.pllUsb = new RPPLL(this, 'PLL_USB_BASE');
63
+ this.clocks = new RPClocks(this, 'CLOCKS_BASE');
56
64
  this.ppb = new RPPPB(this, 'PPB');
57
65
  this.sio = new RPSIO(this);
58
66
  this.uart = [
@@ -125,11 +133,12 @@ export class RP2040 {
125
133
  }),
126
134
  ];
127
135
  this.logger = new ConsoleLogger(LogLevel.Debug, true);
136
+ this.clockListeners = new Set();
128
137
  this.peripherals = {
129
138
  0x18000: new RPSSI(this, 'SSI'),
130
139
  0x40000: new RP2040SysInfo(this, 'SYSINFO_BASE'),
131
140
  0x40004: new RP2040SysCfg(this, 'SYSCFG'),
132
- 0x40008: new RPClocks(this, 'CLOCKS_BASE'),
141
+ 0x40008: this.clocks,
133
142
  0x4000c: new RPReset(this, 'RESETS_BASE'),
134
143
  0x40010: new RPPSM(this, 'PSM_BASE'),
135
144
  0x40014: new RPIO(this, 'IO_BANK0_BASE'),
@@ -137,8 +146,8 @@ export class RP2040 {
137
146
  0x4001c: new RPPADS(this, 'PADS_BANK0_BASE', 'bank0'),
138
147
  0x40020: new RPPADS(this, 'PADS_QSPI_BASE', 'qspi'),
139
148
  0x40024: new RPXOSC(this, 'XOSC_BASE'),
140
- 0x40028: new UnimplementedPeripheral(this, 'PLL_SYS_BASE'),
141
- 0x4002c: new UnimplementedPeripheral(this, 'PLL_USB_BASE'),
149
+ 0x40028: this.pllSys,
150
+ 0x4002c: this.pllUsb,
142
151
  0x40030: new RPBUSCTRL(this, 'BUSCTRL_BASE'),
143
152
  0x40034: this.uart[0],
144
153
  0x40038: this.uart[1],
@@ -214,6 +223,46 @@ export class RP2040 {
214
223
  this.logger.warn(LOG_NAME, `Read from invalid memory address: ${address.toString(16)}`);
215
224
  return 0xffffffff;
216
225
  }
226
+ /**
227
+ * Recomputes `clkSys` and `clkPeri` from the PLL and CLOCKS registers and updates the
228
+ * peripherals derived from them. Until firmware configures the clock tree, each keeps its default.
229
+ */
230
+ updateClocks() {
231
+ this.updateClkSys();
232
+ this.updateClkPeri();
233
+ }
234
+ updateClkSys() {
235
+ const clkSys = this.clocks.sysFreq;
236
+ // 0 means clk_sys is fed from an unmodelled or unconfigured source: keep the last value
237
+ if (!clkSys || clkSys === this.clkSys) {
238
+ return;
239
+ }
240
+ const oldClkSys = this.clkSys;
241
+ this.clkSys = clkSys;
242
+ this.ppb.systickTimer.frequency = clkSys;
243
+ for (const channel of this.pwm.channels) {
244
+ channel.timer.frequency = this.pwm.clockFreq;
245
+ }
246
+ for (const listener of this.clockListeners) {
247
+ listener(clkSys, oldClkSys);
248
+ }
249
+ }
250
+ updateClkPeri() {
251
+ const clkPeri = this.clocks.periFreq;
252
+ // 0 means the clock generator is stopped, or the source is one we do not model
253
+ if (!clkPeri || clkPeri === this.clkPeri) {
254
+ return;
255
+ }
256
+ this.clkPeri = clkPeri;
257
+ for (const uart of this.uart) {
258
+ uart.clkPeriChanged();
259
+ }
260
+ }
261
+ /** Registers a listener to be notified whenever clk_sys changes. Returns an unsubscribe function. */
262
+ addClockListener(listener) {
263
+ this.clockListeners.add(listener);
264
+ return () => this.clockListeners.delete(listener);
265
+ }
217
266
  findPeripheral(address) {
218
267
  return this.peripherals[(address >>> 14) << 2];
219
268
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rp2040js",
3
- "version": "1.3.4",
3
+ "version": "1.4.0",
4
4
  "description": "Raspberry Pi Pico (RP2040) Emulator",
5
5
  "repository": {
6
6
  "type": "git",