aboutsummaryrefslogtreecommitdiff
path: root/src/peripherals/watchdog.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/peripherals/watchdog.spec.ts')
-rw-r--r--src/peripherals/watchdog.spec.ts210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/peripherals/watchdog.spec.ts b/src/peripherals/watchdog.spec.ts
new file mode 100644
index 0000000..6fea3b3
--- /dev/null
+++ b/src/peripherals/watchdog.spec.ts
@@ -0,0 +1,210 @@
+/**
+ * AVR8 Watchdog Timer Test Suite
+ * Part of AVR8js
+ *
+ * Copyright (C) 2021 Uri Shaked
+ */
+
+import { AVRClock, clockConfig } from '..';
+import { CPU } from '../cpu/cpu';
+import { asmProgram, TestProgramRunner } from '../utils/test-utils';
+import { AVRWatchdog, watchdogConfig } from './watchdog';
+
+const R20 = 20;
+
+const MCUSR = 0x54;
+const WDRF = 1 << 3;
+
+const WDTCSR = 0x60;
+const WDP0 = 1 << 0;
+const WDP1 = 1 << 1;
+const WDP2 = 1 << 2;
+const WDE = 1 << 3;
+const WDCE = 1 << 4;
+const WDP3 = 1 << 5;
+const WDIE = 1 << 6;
+
+const INT_WDT = 0xc;
+
+describe('Watchdog', () => {
+ it('should correctly calculate the prescaler from WDTCSR', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const clock = new AVRClock(cpu, 16e6, clockConfig);
+ const watchdog = new AVRWatchdog(cpu, watchdogConfig, clock);
+ cpu.writeData(WDTCSR, WDCE | WDE);
+ cpu.writeData(WDTCSR, 0);
+ expect(watchdog.prescaler).toEqual(2048);
+ cpu.writeData(WDTCSR, WDP2 | WDP1 | WDP0);
+ expect(watchdog.prescaler).toEqual(256 * 1024);
+ cpu.writeData(WDTCSR, WDP3 | WDP0);
+ expect(watchdog.prescaler).toEqual(1024 * 1024);
+ });
+
+ it('should not change the prescaler unless WDCE is set', () => {
+ const cpu = new CPU(new Uint16Array(1024));
+ const clock = new AVRClock(cpu, 16e6, clockConfig);
+ const watchdog = new AVRWatchdog(cpu, watchdogConfig, clock);
+ cpu.writeData(WDTCSR, 0);
+ expect(watchdog.prescaler).toEqual(2048);
+ cpu.writeData(WDTCSR, WDP2 | WDP1 | WDP0);
+ expect(watchdog.prescaler).toEqual(2048);
+
+ cpu.writeData(WDTCSR, WDCE | WDE);
+ cpu.cycles += 5; // WDCE should expire after 4 cycles
+ cpu.writeData(WDTCSR, WDP2 | WDP1 | WDP0);
+ expect(watchdog.prescaler).toEqual(2048);
+ });
+
+ it('should reset the CPU when the timer expires', () => {
+ const { program } = asmProgram(`
+ ; register addresses
+ _REPLACE WDTCSR, ${WDTCSR}
+
+ ; Setup watchdog
+ ldi r16, ${WDE | WDCE}
+ sts WDTCSR, r16
+ ldi r16, ${WDE}
+ sts WDTCSR, r16
+
+ nop
+
+ break
+ `);
+ const cpu = new CPU(program);
+ const clock = new AVRClock(cpu, 16e6, clockConfig);
+ const watchdog = new AVRWatchdog(cpu, watchdogConfig, clock);
+ const runner = new TestProgramRunner(cpu);
+
+ // Setup: enable watchdog timer
+ runner.runInstructions(4);
+ expect(watchdog.enabled).toBe(true);
+
+ // Now we skip 8ms. Watchdog shouldn't fire, yet
+ cpu.cycles += 16000 * 8;
+ runner.runInstructions(1);
+
+ // Now we skip an extra 8ms. Watchdog should fire and reset!
+ cpu.cycles += 16000 * 8;
+ cpu.tick();
+ expect(cpu.pc).toEqual(0);
+ expect(cpu.readData(MCUSR)).toEqual(WDRF);
+ });
+
+ it('should extend the watchdog timeout when executing a WDR instruction', () => {
+ const { program } = asmProgram(`
+ ; register addresses
+ _REPLACE WDTCSR, ${WDTCSR}
+
+ ; Setup watchdog
+ ldi r16, ${WDE | WDCE}
+ sts WDTCSR, r16
+ ldi r16, ${WDE}
+ sts WDTCSR, r16
+
+ wdr
+ nop
+
+ break
+ `);
+ const cpu = new CPU(program);
+ const clock = new AVRClock(cpu, 16e6, clockConfig);
+ const watchdog = new AVRWatchdog(cpu, watchdogConfig, clock);
+ const runner = new TestProgramRunner(cpu);
+
+ // Setup: enable watchdog timer
+ runner.runInstructions(4);
+ expect(watchdog.enabled).toBe(true);
+
+ // Now we skip 8ms. Watchdog shouldn't fire, yet
+ cpu.cycles += 16000 * 8;
+ runner.runInstructions(1);
+
+ // Now we skip an extra 8ms. We extended the timeout with WDR, so watchdog won't fire yet
+ cpu.cycles += 16000 * 8;
+ runner.runInstructions(1);
+
+ // Finally, another 8ms bring us to 16ms since last WDR, and watchdog should fire
+ cpu.cycles += 16000 * 8;
+ cpu.tick();
+ expect(cpu.pc).toEqual(0);
+ });
+
+ it('should fire an interrupt when the watchdog expires and WDIE is set', () => {
+ const { program } = asmProgram(`
+ ; register addresses
+ _REPLACE WDTCSR, ${WDTCSR}
+
+ ; Setup watchdog
+ ldi r16, ${WDE | WDCE}
+ sts WDTCSR, r16
+ ldi r16, ${WDE | WDIE}
+ sts WDTCSR, r16
+
+ nop
+ sei
+
+ break
+ `);
+ const cpu = new CPU(program);
+ const clock = new AVRClock(cpu, 16e6, clockConfig);
+ const watchdog = new AVRWatchdog(cpu, watchdogConfig, clock);
+ const runner = new TestProgramRunner(cpu);
+
+ // Setup: enable watchdog timer
+ runner.runInstructions(4);
+ expect(watchdog.enabled).toBe(true);
+
+ // Now we skip 8ms. Watchdog shouldn't fire, yet
+ cpu.cycles += 16000 * 8;
+ runner.runInstructions(1);
+
+ // Now we skip an extra 8ms. Watchdog should fire and jump to the interrupt handler
+ cpu.cycles += 16000 * 8;
+ runner.runInstructions(1);
+
+ expect(cpu.pc).toEqual(INT_WDT);
+ // The watchdog timer should also clean the WDIE bit, so next timeout will reset the MCU.
+ expect(cpu.readData(WDTCSR) & WDIE).toEqual(0);
+ });
+
+ it('should not reset the CPU if the watchdog has been disabled', () => {
+ const { program } = asmProgram(`
+ ; register addresses
+ _REPLACE WDTCSR, ${WDTCSR}
+
+ ; Setup watchdog
+ ldi r16, ${WDE | WDCE}
+ sts WDTCSR, r16
+ ldi r16, ${WDE}
+ sts WDTCSR, r16
+
+ ; disable watchdog
+ ldi r16, ${WDE | WDCE}
+ sts WDTCSR, r16
+ ldi r16, 0
+ sts WDTCSR, r16
+
+ ldi r20, 55
+
+ break
+ `);
+ const cpu = new CPU(program);
+ const clock = new AVRClock(cpu, 16e6, clockConfig);
+ const watchdog = new AVRWatchdog(cpu, watchdogConfig, clock);
+ const runner = new TestProgramRunner(cpu);
+
+ // Setup: enable watchdog timer
+ runner.runInstructions(4);
+ expect(watchdog.enabled).toBe(true);
+
+ // Now we skip 8ms. Watchdog shouldn't fire, yet. We disable it.
+ cpu.cycles += 16000 * 8;
+ runner.runInstructions(4);
+
+ // Now we skip an extra 20ms. Watchdog shouldn't reset!
+ cpu.cycles += 16000 * 20;
+ runner.runInstructions(1);
+ expect(cpu.pc).not.toEqual(0);
+ expect(cpu.data[R20]).toEqual(55); // assert that `ldi r20, 55` ran
+ });
+});