Skip to content

Commit f0e8791

Browse files
committed
feat(watchdog): implement Watchdog peripheral
1 parent 4b6d803 commit f0e8791

File tree

2 files changed

+151
-2
lines changed

2 files changed

+151
-2
lines changed

src/peripherals/watchdog.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { RP2040 } from '../rp2040.js';
2+
import { Timer32, Timer32PeriodicAlarm, TimerMode } from '../utils/timer32.js';
3+
import { BasePeripheral, Peripheral } from './peripheral.js';
4+
5+
const CTRL = 0x00; // Control register
6+
const LOAD = 0x04; // Load the watchdog timer.
7+
const REASON = 0x08; // Logs the reason for the last reset.
8+
const SCRATCH0 = 0x0c; // Scratch register
9+
const SCRATCH1 = 0x10; // Scratch register
10+
const SCRATCH2 = 0x14; // Scratch register
11+
const SCRATCH3 = 0x18; // Scratch register
12+
const SCRATCH4 = 0x1c; // Scratch register
13+
const SCRATCH5 = 0x20; // Scratch register
14+
const SCRATCH6 = 0x24; // Scratch register
15+
const SCRATCH7 = 0x28; // Scratch register
16+
const TICK = 0x2c; // Controls the tick generator
17+
18+
// CTRL bits:
19+
const TRIGGER = 1 << 31;
20+
const ENABLE = 1 << 30;
21+
const PAUSE_DBG1 = 1 << 26;
22+
const PAUSE_DBG0 = 1 << 25;
23+
const PAUSE_JTAG = 1 << 24;
24+
const TIME_MASK = 0xffffff;
25+
const TIME_SHIFT = 0;
26+
27+
// LOAD bits
28+
const LOAD_MASK = 0xffffff;
29+
const LOAD_SHIFT = 0;
30+
31+
// REASON bits:
32+
const FORCE = 1 << 1;
33+
const TIMER = 1 << 0;
34+
35+
// TICK bits:
36+
const COUNT_MASK = 0x1ff;
37+
const COUNT_SHIFT = 11;
38+
const RUNNING = 1 << 10;
39+
const TICK_ENABLE = 1 << 9;
40+
const CYCLES_MASK = 0x1ff;
41+
const CYCLES_SHIFT = 0;
42+
43+
const TICK_FREQUENCY = 2_000_000; // Actually 1 MHz, but due to errata RP2040-E1, the timer is decremented twice per tick
44+
45+
export class RPWatchdog extends BasePeripheral implements Peripheral {
46+
readonly timer;
47+
readonly alarm;
48+
readonly scratchData = new Uint32Array(8);
49+
50+
private enable = false;
51+
private tickEnable = true;
52+
private reason = 0;
53+
private pauseDbg0 = true;
54+
private pauseDbg1 = true;
55+
private pauseJtag = true;
56+
57+
/** Called when the watchdog triggers - override with your own soft reset implementation */
58+
onWatchdogTrigger = () => {
59+
this.rp2040.logger.warn(this.name, 'Watchdog triggered, but no reset handler provided');
60+
};
61+
62+
// User provided
63+
constructor(rp2040: RP2040, name: string) {
64+
super(rp2040, name);
65+
this.timer = new Timer32(rp2040.clock, TICK_FREQUENCY);
66+
this.timer.mode = TimerMode.Decrement;
67+
this.timer.enable = false;
68+
this.alarm = new Timer32PeriodicAlarm(this.timer, () => {
69+
this.reason = TIMER;
70+
this.onWatchdogTrigger?.();
71+
});
72+
this.alarm.target = 0;
73+
this.alarm.enable = false;
74+
}
75+
76+
readUint32(offset: number) {
77+
switch (offset) {
78+
case CTRL:
79+
return (
80+
(this.timer.enable ? ENABLE : 0) |
81+
(this.pauseDbg0 ? PAUSE_DBG0 : 0) |
82+
(this.pauseDbg1 ? PAUSE_DBG1 : 0) |
83+
(this.pauseJtag ? PAUSE_JTAG : 0) |
84+
((this.timer.counter & TIME_MASK) << TIME_SHIFT)
85+
);
86+
87+
case REASON:
88+
return this.reason;
89+
90+
case SCRATCH0:
91+
case SCRATCH1:
92+
case SCRATCH2:
93+
case SCRATCH3:
94+
case SCRATCH4:
95+
case SCRATCH5:
96+
case SCRATCH6:
97+
case SCRATCH7:
98+
return this.scratchData[(offset - SCRATCH0) >> 2];
99+
100+
case TICK:
101+
// TODO COUNT bits
102+
return this.tickEnable ? RUNNING | TICK_ENABLE : 0;
103+
}
104+
return super.readUint32(offset);
105+
}
106+
107+
writeUint32(offset: number, value: number) {
108+
switch (offset) {
109+
case CTRL:
110+
if (value & TRIGGER) {
111+
this.reason = FORCE;
112+
this.onWatchdogTrigger?.();
113+
}
114+
this.enable = !!(value & ENABLE);
115+
this.timer.enable = this.enable && this.tickEnable;
116+
this.alarm.enable = this.enable && this.tickEnable;
117+
this.pauseDbg0 = !!(value & PAUSE_DBG0);
118+
this.pauseDbg1 = !!(value & PAUSE_DBG1);
119+
this.pauseJtag = !!(value & PAUSE_JTAG);
120+
break;
121+
122+
case LOAD:
123+
this.timer.set((value >>> LOAD_SHIFT) & LOAD_MASK);
124+
break;
125+
126+
case SCRATCH0:
127+
case SCRATCH1:
128+
case SCRATCH2:
129+
case SCRATCH3:
130+
case SCRATCH4:
131+
case SCRATCH5:
132+
case SCRATCH6:
133+
case SCRATCH7:
134+
this.scratchData[(offset - SCRATCH0) >> 2] = value;
135+
break;
136+
137+
case TICK:
138+
this.tickEnable = !!(value & TICK_ENABLE);
139+
this.timer.enable = this.enable && this.tickEnable;
140+
this.alarm.enable = this.enable && this.tickEnable;
141+
// TODO - handle CYCLES (tick also affectes timer)
142+
break;
143+
144+
default:
145+
super.writeUint32(offset, value);
146+
}
147+
}
148+
}

src/rp2040.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ import { RPTBMAN } from './peripherals/tbman.js';
2424
import { RPTimer } from './peripherals/timer.js';
2525
import { RPUART } from './peripherals/uart.js';
2626
import { RPUSBController } from './peripherals/usb.js';
27+
import { RPWatchdog } from './peripherals/watchdog.js';
2728
import { RPSIO } from './sio.js';
28-
import { ConsoleLogger, Logger, LogLevel } from './utils/logging.js';
29+
import { ConsoleLogger, LogLevel, Logger } from './utils/logging.js';
2930

3031
export const FLASH_START_ADDRESS = 0x10000000;
3132
export const RAM_START_ADDRESS = 0x20000000;
@@ -161,7 +162,7 @@ export class RP2040 {
161162
0x4004c: this.adc,
162163
0x40050: this.pwm,
163164
0x40054: new RPTimer(this, 'TIMER_BASE'),
164-
0x40058: new UnimplementedPeripheral(this, 'WATCHDOG_BASE'),
165+
0x40058: new RPWatchdog(this, 'WATCHDOG_BASE'),
165166
0x4005c: new RP2040RTC(this, 'RTC_BASE'),
166167
0x40060: new UnimplementedPeripheral(this, 'ROSC_BASE'),
167168
0x40064: new UnimplementedPeripheral(this, 'VREG_AND_CHIP_RESET_BASE'),

0 commit comments

Comments
 (0)