Skip to content

Commit 02e5b21

Browse files
committed
fix(timer): Overflow interrupt fires twice #80
fix #80
1 parent 066928d commit 02e5b21

File tree

2 files changed

+45
-7
lines changed

2 files changed

+45
-7
lines changed

src/peripherals/timer.spec.ts

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,28 @@ describe('timer', () => {
101101
expect(tcnt).toEqual(0); // TCNT should stay 0
102102
});
103103

104-
it('should set TOV if timer overflows', () => {
104+
it('should set the TOV flag when timer reaches the TOP value', () => {
105105
const cpu = new CPU(new Uint16Array(0x1000));
106106
new AVRTimer(cpu, timer0Config);
107107
cpu.writeData(TCNT0, 0xff);
108108
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
109109
cpu.cycles = 1;
110110
cpu.tick();
111-
cpu.cycles = 2;
111+
expect(cpu.readData(TCNT0)).toEqual(0xff);
112+
expect(cpu.data[TIFR0] & TOV0).toEqual(TOV0);
113+
});
114+
115+
it('should set the TOV if timer overflows past TOP without reaching TOP', () => {
116+
const cpu = new CPU(new Uint16Array(0x1000));
117+
new AVRTimer(cpu, timer0Config);
118+
cpu.writeData(TCNT0, 0xfe);
119+
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
120+
cpu.cycles = 1;
112121
cpu.tick();
113-
const tcnt = cpu.readData(TCNT0);
114-
expect(tcnt).toEqual(0);
122+
expect(cpu.readData(TCNT0)).toEqual(0xfe);
123+
cpu.cycles += 4;
124+
cpu.tick();
125+
expect(cpu.readData(TCNT0)).toEqual(0x2);
115126
expect(cpu.data[TIFR0] & TOV0).toEqual(TOV0);
116127
});
117128

@@ -152,7 +163,7 @@ describe('timer', () => {
152163
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
153164
cpu.cycles = 1;
154165
cpu.tick();
155-
cpu.data[TIMSK0] = TOIE0;
166+
cpu.writeData(TIMSK0, TOIE0);
156167
cpu.data[SREG] = 0x80; // SREG: I-------
157168
cpu.cycles = 2;
158169
cpu.tick();
@@ -180,7 +191,7 @@ describe('timer', () => {
180191
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
181192
cpu.cycles = 1;
182193
cpu.tick();
183-
cpu.data[TIMSK0] = 2;
194+
cpu.writeData(TIMSK0, 2);
184195
cpu.data[SREG] = 0x80; // SREG: I-------
185196
cpu.cycles = 2;
186197
cpu.tick();
@@ -305,6 +316,29 @@ describe('timer', () => {
305316
expect(cpu.data[TIFR0]).toEqual(OCF0A | TOV0);
306317
});
307318

319+
it('should not set the TOV bit twice on overflow (issue #80)', () => {
320+
const cpu = new CPU(new Uint16Array(0x1000));
321+
new AVRTimer(cpu, timer0Config);
322+
cpu.writeData(TCNT0, 0xfe);
323+
cpu.writeData(OCR0A, 0xff);
324+
cpu.writeData(TCCR0A, WGM01); // WGM: CTC
325+
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
326+
327+
cpu.cycles = 1;
328+
cpu.tick();
329+
330+
cpu.cycles = 2;
331+
cpu.tick();
332+
expect(cpu.readData(TCNT0)).toEqual(0xff);
333+
expect(cpu.data[TIFR0] & TOV0).toEqual(TOV0);
334+
cpu.data[TIFR0] &= ~TOV0; // Clear the TOV0 bit
335+
336+
cpu.cycles = 3;
337+
cpu.tick();
338+
expect(cpu.readData(TCNT0)).toEqual(0);
339+
expect(cpu.data[TIFR0] & TOV0).toEqual(0);
340+
});
341+
308342
it('should set OCF0B flag when timer equals OCRB', () => {
309343
const cpu = new CPU(new Uint16Array(0x1000));
310344
new AVRTimer(cpu, timer0Config);

src/peripherals/timer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,15 +445,19 @@ export class AVRTimer {
445445
// OCRUpdateMode.Bottom only occurs in Phase Correct modes, handled by phasePwmCount().
446446
// Thus we only handle TOVUpdateMode.Top or TOVUpdateMode.Max here.
447447
if (
448-
(newVal === TOP || overflow) &&
448+
(newVal === TOP || (overflow && val < TOP)) &&
449449
(this.tovUpdateMode == TOVUpdateMode.Top || TOP === this.MAX)
450450
) {
451451
cpu.setInterruptFlag(this.OVF);
452452
}
453453
}
454454
}
455455
if (this.tcntUpdated) {
456+
const { TOP } = this;
456457
this.tcnt = this.tcntNext;
458+
if (this.tcnt === TOP && (this.tovUpdateMode == TOVUpdateMode.Top || TOP === this.MAX)) {
459+
cpu.setInterruptFlag(this.OVF);
460+
}
457461
this.tcntUpdated = false;
458462
}
459463
if (this.updateDivider) {

0 commit comments

Comments
 (0)