diff options
| author | Uri Shaked | 2020-12-26 00:24:54 +0200 |
|---|---|---|
| committer | Uri Shaked | 2020-12-26 00:24:54 +0200 |
| commit | 02e5b211c6e31ca8f9ec19ff6ec52d4f5bbef568 (patch) | |
| tree | 67a624b2baf932d2dfe7ef709ac32ead8e423ff8 /src | |
| parent | 0.14.6 (diff) | |
| download | avr8js-02e5b211c6e31ca8f9ec19ff6ec52d4f5bbef568.tar.gz avr8js-02e5b211c6e31ca8f9ec19ff6ec52d4f5bbef568.tar.bz2 avr8js-02e5b211c6e31ca8f9ec19ff6ec52d4f5bbef568.zip | |
fix(timer): Overflow interrupt fires twice #80
fix #80
Diffstat (limited to '')
| -rw-r--r-- | src/peripherals/timer.spec.ts | 46 | ||||
| -rw-r--r-- | src/peripherals/timer.ts | 6 |
2 files changed, 45 insertions, 7 deletions
diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index cdb2c4c..3b5026c 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -101,17 +101,28 @@ describe('timer', () => { expect(tcnt).toEqual(0); // TCNT should stay 0 }); - it('should set TOV if timer overflows', () => { + it('should set the TOV flag when timer reaches the TOP value', () => { const cpu = new CPU(new Uint16Array(0x1000)); new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; cpu.tick(); - cpu.cycles = 2; + expect(cpu.readData(TCNT0)).toEqual(0xff); + expect(cpu.data[TIFR0] & TOV0).toEqual(TOV0); + }); + + it('should set the TOV if timer overflows past TOP without reaching TOP', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + new AVRTimer(cpu, timer0Config); + cpu.writeData(TCNT0, 0xfe); + cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 + cpu.cycles = 1; cpu.tick(); - const tcnt = cpu.readData(TCNT0); - expect(tcnt).toEqual(0); + expect(cpu.readData(TCNT0)).toEqual(0xfe); + cpu.cycles += 4; + cpu.tick(); + expect(cpu.readData(TCNT0)).toEqual(0x2); expect(cpu.data[TIFR0] & TOV0).toEqual(TOV0); }); @@ -152,7 +163,7 @@ describe('timer', () => { cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; cpu.tick(); - cpu.data[TIMSK0] = TOIE0; + cpu.writeData(TIMSK0, TOIE0); cpu.data[SREG] = 0x80; // SREG: I------- cpu.cycles = 2; cpu.tick(); @@ -180,7 +191,7 @@ describe('timer', () => { cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; cpu.tick(); - cpu.data[TIMSK0] = 2; + cpu.writeData(TIMSK0, 2); cpu.data[SREG] = 0x80; // SREG: I------- cpu.cycles = 2; cpu.tick(); @@ -305,6 +316,29 @@ describe('timer', () => { expect(cpu.data[TIFR0]).toEqual(OCF0A | TOV0); }); + it('should not set the TOV bit twice on overflow (issue #80)', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + new AVRTimer(cpu, timer0Config); + cpu.writeData(TCNT0, 0xfe); + cpu.writeData(OCR0A, 0xff); + cpu.writeData(TCCR0A, WGM01); // WGM: CTC + cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 + + cpu.cycles = 1; + cpu.tick(); + + cpu.cycles = 2; + cpu.tick(); + expect(cpu.readData(TCNT0)).toEqual(0xff); + expect(cpu.data[TIFR0] & TOV0).toEqual(TOV0); + cpu.data[TIFR0] &= ~TOV0; // Clear the TOV0 bit + + cpu.cycles = 3; + cpu.tick(); + expect(cpu.readData(TCNT0)).toEqual(0); + expect(cpu.data[TIFR0] & TOV0).toEqual(0); + }); + it('should set OCF0B flag when timer equals OCRB', () => { const cpu = new CPU(new Uint16Array(0x1000)); new AVRTimer(cpu, timer0Config); diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts index 957aa78..6ea29e3 100644 --- a/src/peripherals/timer.ts +++ b/src/peripherals/timer.ts @@ -445,7 +445,7 @@ export class AVRTimer { // OCRUpdateMode.Bottom only occurs in Phase Correct modes, handled by phasePwmCount(). // Thus we only handle TOVUpdateMode.Top or TOVUpdateMode.Max here. if ( - (newVal === TOP || overflow) && + (newVal === TOP || (overflow && val < TOP)) && (this.tovUpdateMode == TOVUpdateMode.Top || TOP === this.MAX) ) { cpu.setInterruptFlag(this.OVF); @@ -453,7 +453,11 @@ export class AVRTimer { } } if (this.tcntUpdated) { + const { TOP } = this; this.tcnt = this.tcntNext; + if (this.tcnt === TOP && (this.tovUpdateMode == TOVUpdateMode.Top || TOP === this.MAX)) { + cpu.setInterruptFlag(this.OVF); + } this.tcntUpdated = false; } if (this.updateDivider) { |
