From 67638ca55e73dc9e8795743788205cdaca49f3f5 Mon Sep 17 00:00:00 2001 From: Uri Shaked Date: Tue, 22 Mar 2022 23:23:28 +0200 Subject: fix(timer): Phase Correct mode overruns #119 --- src/peripherals/timer.spec.ts | 52 +++++++++++++++++++++++++++++++++++++++++++ src/peripherals/timer.ts | 17 ++++++++++++-- 2 files changed, 67 insertions(+), 2 deletions(-) (limited to 'src/peripherals') diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index 9330168..609908d 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -908,6 +908,58 @@ describe('timer', () => { expect(cpu.readData(R17)).toEqual(0x4); expect(cpu.readData(R18)).toEqual(0x1); }); + + it('should update OCR0A when TCNT0=TOP and TOP=0 in PWM Phase Correct mode (issue #119)', () => { + const { program, instructionCount } = asmProgram(` + ; Set waveform generation mode (WGM) to PWM, Phase Correct + LDI r16, 0x01 ; TCCR0A = (1 << WGM00); + OUT 0x24, r16 + LDI r16, 0x09 ; TCCR0B = (1 << WGM02) | (1 << CS00); + OUT 0x25, r16 + LDI r16, 0x0 ; TCNT0 = 0x0; + OUT 0x26, r16 + + IN r17, 0x26 ; R17 = TCNT; // TCNT0 should read 0x0 + IN r18, 0x26 ; R18 = TCNT; // TCNT0 should read 0x0 + LDI r16, 0x2 ; OCR0A = 0x2; // TCNT0 should read 0x0 + OUT 0x27, r16 ; // TCNT0 should read 0x1 + NOP ; // TCNT0 should read 0x2 + IN r19, 0x26 ; R19 = TCNT; // TCNT0 should read 0x1 + `); + + const cpu = new CPU(program); + new AVRTimer(cpu, timer0Config); + + const runner = new TestProgramRunner(cpu); + runner.runInstructions(instructionCount); + + expect(cpu.readData(R17)).toEqual(0); + expect(cpu.readData(R18)).toEqual(0); + expect(cpu.readData(R19)).toEqual(0x1); + }); + + it('should not overrun when TOP < current value in Phase Correct mode (issue #119)', () => { + const { program, instructionCount } = asmProgram(` + ; Set waveform generation mode (WGM) to PWM, Phase Correct + LDI r16, 0x01 ; TCCR0A = (1 << WGM00); + OUT 0x24, r16 + LDI r16, 0x09 ; TCCR0B = (1 << WGM02) | (1 << CS00); + OUT 0x25, r16 + LDI r16, 0xff ; TCNT0 = 0xff; + OUT 0x26, r16 + + IN r17, 0x26 ; R17 = TCNT; // TCNT0 should read 255 + `); + + const cpu = new CPU(program); + const timer = new AVRTimer(cpu, timer0Config); + + const runner = new TestProgramRunner(cpu); + runner.runInstructions(instructionCount); + + expect(cpu.readData(R17)).toEqual(255); + expect(timer.debugTCNT).toEqual(0); // TCNT should wrap + }); }); describe('16 bit timers', () => { diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts index 6167394..93b9d19 100644 --- a/src/peripherals/timer.ts +++ b/src/peripherals/timer.ts @@ -496,6 +496,11 @@ export class AVRTimer { } } + /** Expose the raw value of TCNT, for use by the unit tests */ + get debugTCNT() { + return this.tcnt; + } + private updateWGMConfig() { const { config, WGM } = this; const wgmModes = config.bits === 16 ? wgmModes16Bit : wgmModes8Bit; @@ -637,7 +642,15 @@ export class AVRTimer { }; private phasePwmCount(value: u16, delta: u8) { - const { ocrA, ocrB, ocrC, hasOCRC, TOP, tcntUpdated } = this; + const { ocrA, ocrB, ocrC, hasOCRC, TOP, MAX, tcntUpdated } = this; + if (!value && !TOP) { + delta = 0; + if (this.ocrUpdateMode === OCRUpdateMode.Top) { + this.ocrA = this.nextOcrA; + this.ocrB = this.nextOcrB; + this.ocrC = this.nextOcrC; + } + } while (delta > 0) { if (this.countingUp) { value++; @@ -683,7 +696,7 @@ export class AVRTimer { } delta--; } - return value; + return value & MAX; } private timerUpdated(value: number, prevValue: number) { -- cgit v1.2.3