diff options
| author | Uri Shaked | 2020-12-25 01:45:03 +0200 |
|---|---|---|
| committer | Uri Shaked | 2020-12-25 01:45:03 +0200 |
| commit | cf6d4910d1cd320b93d26548fcd500a8b1b2864b (patch) | |
| tree | 981d9ff79688b2f68c58243a4699819729bb6137 | |
| parent | 0.14.5 (diff) | |
| download | avr8js-cf6d4910d1cd320b93d26548fcd500a8b1b2864b.tar.gz avr8js-cf6d4910d1cd320b93d26548fcd500a8b1b2864b.tar.bz2 avr8js-cf6d4910d1cd320b93d26548fcd500a8b1b2864b.zip | |
fix(timer): Output Compare sometimes misses Compare Match #79
fix #79
Diffstat (limited to '')
| -rw-r--r-- | src/peripherals/timer.spec.ts | 31 | ||||
| -rw-r--r-- | src/peripherals/timer.ts | 20 |
2 files changed, 45 insertions, 6 deletions
diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index 1b4d4d7..cdb2c4c 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -541,6 +541,37 @@ describe('timer', () => { expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set, 0x2b); }); + it('should not miss Compare Match when executing multi-cycle instruction (issue #79)', () => { + const { program, instructionCount } = asmProgram(` + LDI r16, 0x10 ; OCR0A = 0x10; // <- TOP value + OUT 0x27, r16 + ; Set waveform generation mode (WGM) to normal, enable OC0A (Set on match) + LDI r16, 0xc0 ; TCCR0A = (1 << COM0A1) | (1 << COM0A0); + OUT 0x24, r16 + LDI r16, 0x1 ; TCCR0B = (1 << CS00); + OUT 0x25, r16 + LDI r16, 0xf ; TCNT0 = 0xf; + OUT 0x26, r16 + RJMP 1 ; TCNT0 will be 0x11 (RJMP takes 2 cycles) + `); + + const cpu = new CPU(program); + new AVRTimer(cpu, timer0Config); + + // Listen to Port D's internal callback + const gpioCallback = jest.fn(); + cpu.gpioTimerHooks[PORTD] = gpioCallback; + + const runner = new TestProgramRunner(cpu); + runner.runInstructions(instructionCount); + + expect(cpu.readData(TCNT0)).toEqual(0x11); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b); + + // Verify that Compare Match has occured and set the OC0A pin (PD6 on ATmega328p) + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set, 0x2b); + }); + it('should only update OCR0A when TCNT0=TOP in PWM Phase Correct mode (issue #76)', () => { const { program, instructionCount } = asmProgram(` LDI r16, 0x4 ; OCR0A = 0x4; diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts index 71c1982..957aa78 100644 --- a/src/peripherals/timer.ts +++ b/src/peripherals/timer.ts @@ -300,7 +300,7 @@ export class AVRTimer { this.tcntUpdated = true; this.cpu.updateClockEvent(this.count, 0); if (this.divider) { - this.timerUpdated(); + this.timerUpdated(this.tcntNext, this.tcntNext); } }; this.cpu.writeHooks[config.OCRA] = (value: u8) => { @@ -432,7 +432,7 @@ export class AVRTimer { // A CPU write overrides (has priority over) all counter clear or count operations. if (!this.tcntUpdated) { this.tcnt = newVal; - this.timerUpdated(); + this.timerUpdated(newVal, val); } if (!phasePwm) { @@ -498,15 +498,23 @@ export class AVRTimer { return value; } - private timerUpdated() { - const value = this.tcnt; - if (value === this.ocrA) { + private timerUpdated(value: number, prevValue: number) { + const { ocrA, ocrB, countingUp } = this; + if ( + (countingUp && prevValue < ocrA && value >= ocrA) || + (countingUp && prevValue > value && ocrA >= value) || + (!countingUp && prevValue > ocrA && value <= ocrA) + ) { this.cpu.setInterruptFlag(this.OCFA); if (this.compA) { this.updateCompPin(this.compA, 'A'); } } - if (value === this.ocrB) { + if ( + (countingUp && prevValue < ocrB && value >= ocrB) || + (countingUp && prevValue > value && ocrB >= value) || + (!countingUp && prevValue > ocrB && value <= ocrB) + ) { this.cpu.setInterruptFlag(this.OCFB); if (this.compB) { this.updateCompPin(this.compB, 'B'); |
