rp2040js 1.1.0 → 1.2.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.
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ ![](../../actions/workflows/ci-test.yml/badge.svg) ![](../../actions/workflows/ci-micropython.yml/badge.svg) ![](../../actions/workflows/ci-pico-sdk.yml/badge.svg)
2
+
1
3
  # rp2040js
2
4
 
3
5
  Raspberry Pi Pico Emulator for the [Wokwi Simulation Platform](https://wokwi.com). It blinks, runs Arduino code, and even the MicroPython REPL!
@@ -58,7 +60,7 @@ A GDB server on port 3333 can be enabled by specifying the `--gdb` flag:
58
60
  npm run start:micropython -- --gdb
59
61
  ```
60
62
 
61
- For using the MicroPython demo code in tests, the `--expect-text` can come handy: it will look for the given text in the serial output and exit with code 0 if found, or 1 if not found. You can find an example in [the MicroPython CI test](./github/workflows/ci-micropython.yml).
63
+ For using the MicroPython demo code in tests, the `--expect-text` can come handy: it will look for the given text in the serial output and exit with code 0 if found, or 1 if not found. You can find an example in [the MicroPython CI test](./.github/workflows/ci-micropython.yml).
62
64
 
63
65
  #### Filesystem support
64
66
 
@@ -92,6 +92,8 @@ export declare class RPPIO extends BasePeripheral implements Peripheral {
92
92
  readonly machines: StateMachine[];
93
93
  stopped: boolean;
94
94
  fdebug: number;
95
+ txStall: number;
96
+ rxStall: number;
95
97
  inputSyncBypass: number;
96
98
  irq: number;
97
99
  pinValues: number;
@@ -169,6 +169,7 @@ class StateMachine {
169
169
  return;
170
170
  }
171
171
  this.txFIFO.push(value);
172
+ this.pio.txStall &= ~(FDEBUG_TXSTALL << this.index);
172
173
  this.updateDMATx();
173
174
  this.checkWait();
174
175
  if (this.txFIFO.full) {
@@ -181,6 +182,7 @@ class StateMachine {
181
182
  return 0;
182
183
  }
183
184
  const result = this.rxFIFO.pull();
185
+ this.pio.rxStall &= ~(FDEBUG_RXSTALL << this.index);
184
186
  this.updateDMARx();
185
187
  this.checkWait();
186
188
  if (this.rxFIFO.empty) {
@@ -440,7 +442,8 @@ class StateMachine {
440
442
  this.pio.checkInterrupts();
441
443
  }
442
444
  else {
443
- this.pio.fdebug |= FDEBUG_RXSTALL << this.index;
445
+ this.pio.rxStall |= FDEBUG_RXSTALL << this.index;
446
+ this.pio.fdebug |= this.pio.rxStall;
444
447
  this.wait(WaitType.rxFIFO, false, this.inputShiftReg);
445
448
  }
446
449
  this.inputShiftCount = 0;
@@ -458,7 +461,8 @@ class StateMachine {
458
461
  this.pio.checkInterrupts();
459
462
  }
460
463
  else {
461
- this.pio.fdebug |= FDEBUG_TXSTALL << this.index;
464
+ this.pio.txStall |= FDEBUG_TXSTALL << this.index;
465
+ this.pio.fdebug |= this.pio.txStall;
462
466
  this.wait(WaitType.Out, false, arg);
463
467
  }
464
468
  }
@@ -488,7 +492,8 @@ class StateMachine {
488
492
  this.pio.checkInterrupts();
489
493
  }
490
494
  else {
491
- this.pio.fdebug |= FDEBUG_TXSTALL << this.index;
495
+ this.pio.txStall |= FDEBUG_TXSTALL << this.index;
496
+ this.pio.fdebug |= this.pio.txStall;
492
497
  if (block) {
493
498
  this.wait(WaitType.txFIFO, false, 0);
494
499
  }
@@ -511,7 +516,8 @@ class StateMachine {
511
516
  this.pio.checkInterrupts();
512
517
  }
513
518
  else {
514
- this.pio.fdebug |= FDEBUG_RXSTALL << this.index;
519
+ this.pio.rxStall |= FDEBUG_RXSTALL << this.index;
520
+ this.pio.fdebug |= this.pio.rxStall;
515
521
  if (block) {
516
522
  this.wait(WaitType.rxFIFO, false, this.inputShiftReg);
517
523
  }
@@ -831,6 +837,8 @@ class RPPIO extends peripheral_js_1.BasePeripheral {
831
837
  ];
832
838
  this.stopped = true;
833
839
  this.fdebug = 0;
840
+ this.txStall = 0;
841
+ this.rxStall = 0;
834
842
  this.inputSyncBypass = 0;
835
843
  this.irq = 0;
836
844
  this.pinValues = 0;
@@ -977,6 +985,7 @@ class RPPIO extends peripheral_js_1.BasePeripheral {
977
985
  }
978
986
  case FDEBUG:
979
987
  this.fdebug &= ~this.rawWriteValue;
988
+ this.fdebug |= this.txStall | this.rxStall;
980
989
  break;
981
990
  case TXF0:
982
991
  this.machines[0].writeFIFO(value);
@@ -0,0 +1,13 @@
1
+ import { BasePeripheral, Peripheral } from './peripheral.js';
2
+ export declare class RPXOSC extends BasePeripheral implements Peripheral {
3
+ private ctrl;
4
+ private status;
5
+ private dormant;
6
+ private startup;
7
+ private count;
8
+ private enabled;
9
+ private stable;
10
+ private isDormant;
11
+ readUint32(offset: number): number;
12
+ writeUint32(offset: number, value: number): void;
13
+ }
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RPXOSC = void 0;
4
+ const peripheral_js_1 = require("./peripheral.js");
5
+ // XOSC register offsets
6
+ const XOSC_CTRL = 0x00;
7
+ const XOSC_STATUS = 0x04;
8
+ const XOSC_DORMANT = 0x08;
9
+ const XOSC_STARTUP = 0x0c;
10
+ const XOSC_COUNT = 0x1c;
11
+ // CTRL register bits
12
+ const CTRL_ENABLE_LSB = 12;
13
+ const CTRL_ENABLE_BITS = 0x00fff000;
14
+ const CTRL_FREQ_RANGE_BITS = 0x00000fff;
15
+ // CTRL ENABLE values
16
+ const CTRL_ENABLE_DISABLE = 0xd1e;
17
+ const CTRL_ENABLE_ENABLE = 0xfab;
18
+ // STATUS register bits
19
+ const STATUS_STABLE = 0x80000000; // bit 31
20
+ const STATUS_BADWRITE = 0x01000000; // bit 24
21
+ const STATUS_ENABLED = 0x00001000; // bit 12
22
+ const STATUS_FREQ_RANGE_BITS = 0x00000003;
23
+ // DORMANT register values
24
+ const DORMANT_VALUE = 0x636f6d61; // "coma" in ASCII
25
+ const WAKE_VALUE = 0x77616b65; // "wake" in ASCII
26
+ // STARTUP register bits
27
+ const STARTUP_X4 = 0x00100000; // bit 20
28
+ const STARTUP_DELAY_BITS = 0x00003fff;
29
+ class RPXOSC extends peripheral_js_1.BasePeripheral {
30
+ constructor() {
31
+ super(...arguments);
32
+ this.ctrl = 0;
33
+ this.status = 0;
34
+ this.dormant = 0;
35
+ this.startup = 0;
36
+ this.count = 0;
37
+ this.enabled = false;
38
+ this.stable = false;
39
+ this.isDormant = false;
40
+ }
41
+ readUint32(offset) {
42
+ switch (offset) {
43
+ case XOSC_CTRL:
44
+ return this.ctrl;
45
+ case XOSC_STATUS: {
46
+ let status = this.status;
47
+ if (this.stable) {
48
+ status |= STATUS_STABLE;
49
+ }
50
+ if (this.enabled) {
51
+ status |= STATUS_ENABLED;
52
+ }
53
+ return status;
54
+ }
55
+ case XOSC_DORMANT:
56
+ return this.dormant;
57
+ case XOSC_STARTUP:
58
+ return this.startup;
59
+ case XOSC_COUNT:
60
+ return this.count;
61
+ }
62
+ return super.readUint32(offset);
63
+ }
64
+ writeUint32(offset, value) {
65
+ switch (offset) {
66
+ case XOSC_CTRL: {
67
+ this.ctrl = value;
68
+ const enableValue = (value & CTRL_ENABLE_BITS) >>> CTRL_ENABLE_LSB;
69
+ const freqRange = value & CTRL_FREQ_RANGE_BITS;
70
+ void freqRange; // Currently unused, but could be logged or validated
71
+ if (enableValue === CTRL_ENABLE_ENABLE) {
72
+ if (!this.isDormant) {
73
+ this.enabled = true;
74
+ // For simplicity, become stable immediately
75
+ // In real hardware, this would take time based on STARTUP register
76
+ this.stable = true;
77
+ }
78
+ }
79
+ else if (enableValue === CTRL_ENABLE_DISABLE) {
80
+ this.enabled = false;
81
+ this.stable = false;
82
+ }
83
+ else if (enableValue !== 0) {
84
+ // Invalid write to ENABLE field
85
+ this.status |= STATUS_BADWRITE;
86
+ this.warn(`Invalid ENABLE value written: 0x${enableValue.toString(16)}`);
87
+ }
88
+ break;
89
+ }
90
+ case XOSC_STATUS:
91
+ // Clear BADWRITE bit if written as 1 (write-1-to-clear)
92
+ if (value & STATUS_BADWRITE) {
93
+ this.status &= ~STATUS_BADWRITE;
94
+ }
95
+ break;
96
+ case XOSC_DORMANT:
97
+ if (value === DORMANT_VALUE) {
98
+ this.isDormant = true;
99
+ this.stable = false;
100
+ }
101
+ else if (value === WAKE_VALUE) {
102
+ this.isDormant = false;
103
+ if (this.enabled) {
104
+ this.stable = true;
105
+ }
106
+ }
107
+ this.dormant = value;
108
+ break;
109
+ case XOSC_STARTUP:
110
+ this.startup = value & (STARTUP_X4 | STARTUP_DELAY_BITS);
111
+ break;
112
+ case XOSC_COUNT:
113
+ // Writing to COUNT starts the countdown
114
+ this.count = value & 0xff;
115
+ // For simplicity, we don't actually implement the countdown
116
+ // In real hardware, this would decrement at the XOSC frequency
117
+ break;
118
+ default:
119
+ super.writeUint32(offset, value);
120
+ }
121
+ }
122
+ }
123
+ exports.RPXOSC = RPXOSC;
@@ -27,6 +27,7 @@ const timer_js_1 = require("./peripherals/timer.js");
27
27
  const uart_js_1 = require("./peripherals/uart.js");
28
28
  const usb_js_1 = require("./peripherals/usb.js");
29
29
  const watchdog_js_1 = require("./peripherals/watchdog.js");
30
+ const xosc_js_1 = require("./peripherals/xosc.js");
30
31
  const sio_js_1 = require("./sio.js");
31
32
  const logging_js_1 = require("./utils/logging.js");
32
33
  exports.FLASH_START_ADDRESS = 0x10000000;
@@ -137,7 +138,7 @@ class RP2040 {
137
138
  0x40018: new peripheral_js_1.UnimplementedPeripheral(this, 'IO_QSPI_BASE'),
138
139
  0x4001c: new pads_js_1.RPPADS(this, 'PADS_BANK0_BASE', 'bank0'),
139
140
  0x40020: new pads_js_1.RPPADS(this, 'PADS_QSPI_BASE', 'qspi'),
140
- 0x40024: new peripheral_js_1.UnimplementedPeripheral(this, 'XOSC_BASE'),
141
+ 0x40024: new xosc_js_1.RPXOSC(this, 'XOSC_BASE'),
141
142
  0x40028: new peripheral_js_1.UnimplementedPeripheral(this, 'PLL_SYS_BASE'),
142
143
  0x4002c: new peripheral_js_1.UnimplementedPeripheral(this, 'PLL_USB_BASE'),
143
144
  0x40030: new busctrl_js_1.RPBUSCTRL(this, 'BUSCTRL_BASE'),
@@ -92,6 +92,8 @@ export declare class RPPIO extends BasePeripheral implements Peripheral {
92
92
  readonly machines: StateMachine[];
93
93
  stopped: boolean;
94
94
  fdebug: number;
95
+ txStall: number;
96
+ rxStall: number;
95
97
  inputSyncBypass: number;
96
98
  irq: number;
97
99
  pinValues: number;
@@ -166,6 +166,7 @@ export class StateMachine {
166
166
  return;
167
167
  }
168
168
  this.txFIFO.push(value);
169
+ this.pio.txStall &= ~(FDEBUG_TXSTALL << this.index);
169
170
  this.updateDMATx();
170
171
  this.checkWait();
171
172
  if (this.txFIFO.full) {
@@ -178,6 +179,7 @@ export class StateMachine {
178
179
  return 0;
179
180
  }
180
181
  const result = this.rxFIFO.pull();
182
+ this.pio.rxStall &= ~(FDEBUG_RXSTALL << this.index);
181
183
  this.updateDMARx();
182
184
  this.checkWait();
183
185
  if (this.rxFIFO.empty) {
@@ -437,7 +439,8 @@ export class StateMachine {
437
439
  this.pio.checkInterrupts();
438
440
  }
439
441
  else {
440
- this.pio.fdebug |= FDEBUG_RXSTALL << this.index;
442
+ this.pio.rxStall |= FDEBUG_RXSTALL << this.index;
443
+ this.pio.fdebug |= this.pio.rxStall;
441
444
  this.wait(WaitType.rxFIFO, false, this.inputShiftReg);
442
445
  }
443
446
  this.inputShiftCount = 0;
@@ -455,7 +458,8 @@ export class StateMachine {
455
458
  this.pio.checkInterrupts();
456
459
  }
457
460
  else {
458
- this.pio.fdebug |= FDEBUG_TXSTALL << this.index;
461
+ this.pio.txStall |= FDEBUG_TXSTALL << this.index;
462
+ this.pio.fdebug |= this.pio.txStall;
459
463
  this.wait(WaitType.Out, false, arg);
460
464
  }
461
465
  }
@@ -485,7 +489,8 @@ export class StateMachine {
485
489
  this.pio.checkInterrupts();
486
490
  }
487
491
  else {
488
- this.pio.fdebug |= FDEBUG_TXSTALL << this.index;
492
+ this.pio.txStall |= FDEBUG_TXSTALL << this.index;
493
+ this.pio.fdebug |= this.pio.txStall;
489
494
  if (block) {
490
495
  this.wait(WaitType.txFIFO, false, 0);
491
496
  }
@@ -508,7 +513,8 @@ export class StateMachine {
508
513
  this.pio.checkInterrupts();
509
514
  }
510
515
  else {
511
- this.pio.fdebug |= FDEBUG_RXSTALL << this.index;
516
+ this.pio.rxStall |= FDEBUG_RXSTALL << this.index;
517
+ this.pio.fdebug |= this.pio.rxStall;
512
518
  if (block) {
513
519
  this.wait(WaitType.rxFIFO, false, this.inputShiftReg);
514
520
  }
@@ -827,6 +833,8 @@ export class RPPIO extends BasePeripheral {
827
833
  ];
828
834
  this.stopped = true;
829
835
  this.fdebug = 0;
836
+ this.txStall = 0;
837
+ this.rxStall = 0;
830
838
  this.inputSyncBypass = 0;
831
839
  this.irq = 0;
832
840
  this.pinValues = 0;
@@ -973,6 +981,7 @@ export class RPPIO extends BasePeripheral {
973
981
  }
974
982
  case FDEBUG:
975
983
  this.fdebug &= ~this.rawWriteValue;
984
+ this.fdebug |= this.txStall | this.rxStall;
976
985
  break;
977
986
  case TXF0:
978
987
  this.machines[0].writeFIFO(value);
@@ -0,0 +1,13 @@
1
+ import { BasePeripheral, Peripheral } from './peripheral.js';
2
+ export declare class RPXOSC extends BasePeripheral implements Peripheral {
3
+ private ctrl;
4
+ private status;
5
+ private dormant;
6
+ private startup;
7
+ private count;
8
+ private enabled;
9
+ private stable;
10
+ private isDormant;
11
+ readUint32(offset: number): number;
12
+ writeUint32(offset: number, value: number): void;
13
+ }
@@ -0,0 +1,119 @@
1
+ import { BasePeripheral } from './peripheral.js';
2
+ // XOSC register offsets
3
+ const XOSC_CTRL = 0x00;
4
+ const XOSC_STATUS = 0x04;
5
+ const XOSC_DORMANT = 0x08;
6
+ const XOSC_STARTUP = 0x0c;
7
+ const XOSC_COUNT = 0x1c;
8
+ // CTRL register bits
9
+ const CTRL_ENABLE_LSB = 12;
10
+ const CTRL_ENABLE_BITS = 0x00fff000;
11
+ const CTRL_FREQ_RANGE_BITS = 0x00000fff;
12
+ // CTRL ENABLE values
13
+ const CTRL_ENABLE_DISABLE = 0xd1e;
14
+ const CTRL_ENABLE_ENABLE = 0xfab;
15
+ // STATUS register bits
16
+ const STATUS_STABLE = 0x80000000; // bit 31
17
+ const STATUS_BADWRITE = 0x01000000; // bit 24
18
+ const STATUS_ENABLED = 0x00001000; // bit 12
19
+ const STATUS_FREQ_RANGE_BITS = 0x00000003;
20
+ // DORMANT register values
21
+ const DORMANT_VALUE = 0x636f6d61; // "coma" in ASCII
22
+ const WAKE_VALUE = 0x77616b65; // "wake" in ASCII
23
+ // STARTUP register bits
24
+ const STARTUP_X4 = 0x00100000; // bit 20
25
+ const STARTUP_DELAY_BITS = 0x00003fff;
26
+ export class RPXOSC extends BasePeripheral {
27
+ constructor() {
28
+ super(...arguments);
29
+ this.ctrl = 0;
30
+ this.status = 0;
31
+ this.dormant = 0;
32
+ this.startup = 0;
33
+ this.count = 0;
34
+ this.enabled = false;
35
+ this.stable = false;
36
+ this.isDormant = false;
37
+ }
38
+ readUint32(offset) {
39
+ switch (offset) {
40
+ case XOSC_CTRL:
41
+ return this.ctrl;
42
+ case XOSC_STATUS: {
43
+ let status = this.status;
44
+ if (this.stable) {
45
+ status |= STATUS_STABLE;
46
+ }
47
+ if (this.enabled) {
48
+ status |= STATUS_ENABLED;
49
+ }
50
+ return status;
51
+ }
52
+ case XOSC_DORMANT:
53
+ return this.dormant;
54
+ case XOSC_STARTUP:
55
+ return this.startup;
56
+ case XOSC_COUNT:
57
+ return this.count;
58
+ }
59
+ return super.readUint32(offset);
60
+ }
61
+ writeUint32(offset, value) {
62
+ switch (offset) {
63
+ case XOSC_CTRL: {
64
+ this.ctrl = value;
65
+ const enableValue = (value & CTRL_ENABLE_BITS) >>> CTRL_ENABLE_LSB;
66
+ const freqRange = value & CTRL_FREQ_RANGE_BITS;
67
+ void freqRange; // Currently unused, but could be logged or validated
68
+ if (enableValue === CTRL_ENABLE_ENABLE) {
69
+ if (!this.isDormant) {
70
+ this.enabled = true;
71
+ // For simplicity, become stable immediately
72
+ // In real hardware, this would take time based on STARTUP register
73
+ this.stable = true;
74
+ }
75
+ }
76
+ else if (enableValue === CTRL_ENABLE_DISABLE) {
77
+ this.enabled = false;
78
+ this.stable = false;
79
+ }
80
+ else if (enableValue !== 0) {
81
+ // Invalid write to ENABLE field
82
+ this.status |= STATUS_BADWRITE;
83
+ this.warn(`Invalid ENABLE value written: 0x${enableValue.toString(16)}`);
84
+ }
85
+ break;
86
+ }
87
+ case XOSC_STATUS:
88
+ // Clear BADWRITE bit if written as 1 (write-1-to-clear)
89
+ if (value & STATUS_BADWRITE) {
90
+ this.status &= ~STATUS_BADWRITE;
91
+ }
92
+ break;
93
+ case XOSC_DORMANT:
94
+ if (value === DORMANT_VALUE) {
95
+ this.isDormant = true;
96
+ this.stable = false;
97
+ }
98
+ else if (value === WAKE_VALUE) {
99
+ this.isDormant = false;
100
+ if (this.enabled) {
101
+ this.stable = true;
102
+ }
103
+ }
104
+ this.dormant = value;
105
+ break;
106
+ case XOSC_STARTUP:
107
+ this.startup = value & (STARTUP_X4 | STARTUP_DELAY_BITS);
108
+ break;
109
+ case XOSC_COUNT:
110
+ // Writing to COUNT starts the countdown
111
+ this.count = value & 0xff;
112
+ // For simplicity, we don't actually implement the countdown
113
+ // In real hardware, this would decrement at the XOSC frequency
114
+ break;
115
+ default:
116
+ super.writeUint32(offset, value);
117
+ }
118
+ }
119
+ }
@@ -24,6 +24,7 @@ import { RPTimer } from './peripherals/timer.js';
24
24
  import { RPUART } from './peripherals/uart.js';
25
25
  import { RPUSBController } from './peripherals/usb.js';
26
26
  import { RPWatchdog } from './peripherals/watchdog.js';
27
+ import { RPXOSC } from './peripherals/xosc.js';
27
28
  import { RPSIO } from './sio.js';
28
29
  import { ConsoleLogger, LogLevel } from './utils/logging.js';
29
30
  export const FLASH_START_ADDRESS = 0x10000000;
@@ -134,7 +135,7 @@ export class RP2040 {
134
135
  0x40018: new UnimplementedPeripheral(this, 'IO_QSPI_BASE'),
135
136
  0x4001c: new RPPADS(this, 'PADS_BANK0_BASE', 'bank0'),
136
137
  0x40020: new RPPADS(this, 'PADS_QSPI_BASE', 'qspi'),
137
- 0x40024: new UnimplementedPeripheral(this, 'XOSC_BASE'),
138
+ 0x40024: new RPXOSC(this, 'XOSC_BASE'),
138
139
  0x40028: new UnimplementedPeripheral(this, 'PLL_SYS_BASE'),
139
140
  0x4002c: new UnimplementedPeripheral(this, 'PLL_USB_BASE'),
140
141
  0x40030: new RPBUSCTRL(this, 'BUSCTRL_BASE'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rp2040js",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Raspberry Pi Pico (RP2040) Emulator",
5
5
  "repository": "https://github.com/wokwi/rp2040js",
6
6
  "keywords": [