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.
@@ -103,12 +103,15 @@ class PWMChannel {
103
103
  if (value & CSR_EN && !(this.csr & CSR_EN)) {
104
104
  this.updateDoubleBuffered();
105
105
  }
106
+ // PH_ADV and PH_RET are self-clearing, so they never appear in the stored csr value
106
107
  this.csr = value & ~(CSR_PH_ADV | CSR_PH_RET);
107
- if (this.csr & CSR_PH_ADV) {
108
- this.timer.advance(1);
109
- }
110
- if (this.csr & CSR_PH_RET) {
111
- this.timer.advance(-1);
108
+ if (value & CSR_EN) {
109
+ if (value & CSR_PH_ADV) {
110
+ this.timer.advance(1);
111
+ }
112
+ if (value & CSR_PH_RET) {
113
+ this.timer.advance(-1);
114
+ }
112
115
  }
113
116
  this.divMode = (this.csr >> CSR_DIVMODE_SHIFT) & CSR_DIVMODE_MASK;
114
117
  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;
@@ -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
  }
@@ -34,6 +34,12 @@ export class Timer32 {
34
34
  */
35
35
  advance(delta) {
36
36
  this.baseValue += delta;
37
+ if (this.topValue !== 0xffffffff) {
38
+ // Keep the base value in range, so that retarding past 0 wraps around to the top
39
+ const topModulo = this.timerMode === TimerMode.ZigZag ? this.topValue * 2 : this.topValue + 1;
40
+ this.baseValue = ((this.baseValue % topModulo) + topModulo) % topModulo;
41
+ }
42
+ this.updated();
37
43
  }
38
44
  get rawCounter() {
39
45
  const { baseFreq, prescalerValue, baseNanos, baseValue, enabled, timerMode } = this;
package/package.json CHANGED
@@ -1,88 +1,88 @@
1
- {
2
- "name": "rp2040js",
3
- "version": "1.3.3",
4
- "description": "Raspberry Pi Pico (RP2040) Emulator",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/wokwi/rp2040js.git"
8
- },
9
- "keywords": [
10
- "rp2040",
11
- "raspberry pi pico",
12
- "emulator"
13
- ],
14
- "files": [
15
- "dist"
16
- ],
17
- "author": "Uri Shaked <uri@wokwi.com>",
18
- "license": "MIT",
19
- "type": "module",
20
- "main": "./dist/cjs/index.js",
21
- "module": "./dist/esm/index.js",
22
- "typings": "./dist/cjs/index.d.ts",
23
- "engines": {
24
- "node": ">=18.0.0"
25
- },
26
- "exports": {
27
- ".": {
28
- "import": {
29
- "types": "./dist/esm/index.d.ts",
30
- "default": "./dist/esm/index.js"
31
- },
32
- "require": {
33
- "types": "./dist/cjs/index.d.ts",
34
- "default": "./dist/cjs/index.js"
35
- }
36
- },
37
- "./gdb-tcp-server": {
38
- "import": {
39
- "types": "./dist/esm/gdb/gdb-tcp-server.d.ts",
40
- "default": "./dist/esm/gdb/gdb-tcp-server.js"
41
- },
42
- "require": {
43
- "types": "./dist/cjs/gdb/gdb-tcp-server.d.ts",
44
- "default": "./dist/cjs/gdb/gdb-tcp-server.js"
45
- }
46
- }
47
- },
48
- "scripts": {
49
- "build": "rimraf dist && tsc && tsc -p tsconfig.cjs.json && node build-scripts/dist-package-json",
50
- "prepublish": "npm run build",
51
- "prepare": "husky install",
52
- "format:check": "prettier --check **/*.{ts,js} !**/dist/** !**/node_modules/**",
53
- "lint": "eslint . --ext .ts",
54
- "start": "tsx demo/emulator-run.ts",
55
- "start:micropython": "tsx demo/micropython-run.ts",
56
- "start:circuitpython": "tsx demo/micropython-run.ts --circuitpython",
57
- "start:gdbdiff": "tsx debug/gdbdiff.ts",
58
- "test": "vitest run",
59
- "test:watch": "vitest",
60
- "test:micropython-spi": "tsx test/micropython-spi-test.ts"
61
- },
62
- "devDependencies": {
63
- "@types/minimist": "^1.2.2",
64
- "@types/node": "^20",
65
- "@typescript-eslint/eslint-plugin": "^6.7.3",
66
- "@typescript-eslint/parser": "^6.7.3",
67
- "eslint": "^8.50.0",
68
- "husky": "^8.0.3",
69
- "lint-staged": "^15.4.3",
70
- "minimist": "^1.2.7",
71
- "prettier": "^3.0.3",
72
- "prettier-plugin-organize-imports": "^4.3.0",
73
- "rimraf": "^5.0.5",
74
- "tsx": "^4.19.3",
75
- "typescript": "^5.7.3",
76
- "uf2": "^1.0.0",
77
- "vitest": "^4.1.9"
78
- },
79
- "lint-staged": {
80
- "**/*.ts": [
81
- "eslint --fix",
82
- "prettier --write"
83
- ],
84
- "**/*.js": [
85
- "prettier --write"
86
- ]
87
- }
88
- }
1
+ {
2
+ "name": "rp2040js",
3
+ "version": "1.4.0",
4
+ "description": "Raspberry Pi Pico (RP2040) Emulator",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/wokwi/rp2040js.git"
8
+ },
9
+ "keywords": [
10
+ "rp2040",
11
+ "raspberry pi pico",
12
+ "emulator"
13
+ ],
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "author": "Uri Shaked <uri@wokwi.com>",
18
+ "license": "MIT",
19
+ "type": "module",
20
+ "main": "./dist/cjs/index.js",
21
+ "module": "./dist/esm/index.js",
22
+ "typings": "./dist/cjs/index.d.ts",
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ },
26
+ "exports": {
27
+ ".": {
28
+ "import": {
29
+ "types": "./dist/esm/index.d.ts",
30
+ "default": "./dist/esm/index.js"
31
+ },
32
+ "require": {
33
+ "types": "./dist/cjs/index.d.ts",
34
+ "default": "./dist/cjs/index.js"
35
+ }
36
+ },
37
+ "./gdb-tcp-server": {
38
+ "import": {
39
+ "types": "./dist/esm/gdb/gdb-tcp-server.d.ts",
40
+ "default": "./dist/esm/gdb/gdb-tcp-server.js"
41
+ },
42
+ "require": {
43
+ "types": "./dist/cjs/gdb/gdb-tcp-server.d.ts",
44
+ "default": "./dist/cjs/gdb/gdb-tcp-server.js"
45
+ }
46
+ }
47
+ },
48
+ "scripts": {
49
+ "build": "rimraf dist && tsc && tsc -p tsconfig.cjs.json && node build-scripts/dist-package-json",
50
+ "prepublish": "npm run build",
51
+ "prepare": "husky install",
52
+ "format:check": "prettier --check **/*.{ts,js} !**/dist/** !**/node_modules/**",
53
+ "lint": "eslint . --ext .ts",
54
+ "start": "tsx demo/emulator-run.ts",
55
+ "start:micropython": "tsx demo/micropython-run.ts",
56
+ "start:circuitpython": "tsx demo/micropython-run.ts --circuitpython",
57
+ "start:gdbdiff": "tsx debug/gdbdiff.ts",
58
+ "test": "vitest run",
59
+ "test:watch": "vitest",
60
+ "test:micropython-spi": "tsx test/micropython-spi-test.ts"
61
+ },
62
+ "devDependencies": {
63
+ "@types/minimist": "^1.2.2",
64
+ "@types/node": "^20",
65
+ "@typescript-eslint/eslint-plugin": "^6.7.3",
66
+ "@typescript-eslint/parser": "^6.7.3",
67
+ "eslint": "^8.50.0",
68
+ "husky": "^8.0.3",
69
+ "lint-staged": "^15.4.3",
70
+ "minimist": "^1.2.7",
71
+ "prettier": "^3.0.3",
72
+ "prettier-plugin-organize-imports": "^4.3.0",
73
+ "rimraf": "^5.0.5",
74
+ "tsx": "^4.19.3",
75
+ "typescript": "^5.7.3",
76
+ "uf2": "^1.0.0",
77
+ "vitest": "^4.1.9"
78
+ },
79
+ "lint-staged": {
80
+ "**/*.ts": [
81
+ "eslint --fix",
82
+ "prettier --write"
83
+ ],
84
+ "**/*.js": [
85
+ "prettier --write"
86
+ ]
87
+ }
88
+ }