Skip to content

Commit f32994d

Browse files
committed
fix(timer): TOV flag does not update correctly #75
fix #75
1 parent 38dde78 commit f32994d

File tree

2 files changed

+56
-20
lines changed

2 files changed

+56
-20
lines changed

src/peripherals/timer.spec.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ describe('timer', () => {
239239
expect(cpu.cycles).toEqual(2);
240240
});
241241

242-
it('should clear the timer in CTC mode if it equals to OCRA', () => {
242+
it('should reset the counter in CTC mode if it equals to OCRA', () => {
243243
const cpu = new CPU(new Uint16Array(0x1000));
244244
new AVRTimer(cpu, timer0Config);
245245
cpu.writeData(TCNT0, 0x10);
@@ -248,12 +248,44 @@ describe('timer', () => {
248248
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
249249
cpu.cycles = 1;
250250
cpu.tick();
251-
cpu.cycles = 2;
251+
cpu.cycles = 3;
252252
cpu.tick();
253253
const tcnt = cpu.readData(TCNT0);
254254
expect(tcnt).toEqual(0);
255255
expect(cpu.pc).toEqual(0);
256-
expect(cpu.cycles).toEqual(2);
256+
expect(cpu.cycles).toEqual(3);
257+
});
258+
259+
it('should not set the TOV bit when TOP < MAX in CTC mode (issue #75)', () => {
260+
const cpu = new CPU(new Uint16Array(0x1000));
261+
new AVRTimer(cpu, timer0Config);
262+
cpu.writeData(TCNT0, 0x1e);
263+
cpu.writeData(OCR0A, 0x1f);
264+
cpu.writeData(TCCR0A, WGM01); // WGM: CTC
265+
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
266+
cpu.cycles = 1;
267+
cpu.tick();
268+
cpu.cycles = 2;
269+
cpu.tick();
270+
const tcnt = cpu.readData(TCNT0);
271+
expect(tcnt).toEqual(0x1f);
272+
expect(cpu.data[TIFR0]).toEqual(OCF0A); // TOV0 clear
273+
});
274+
275+
it('should set the TOV bit when TOP == MAX in CTC mode (issue #75)', () => {
276+
const cpu = new CPU(new Uint16Array(0x1000));
277+
new AVRTimer(cpu, timer0Config);
278+
cpu.writeData(TCNT0, 0xfe);
279+
cpu.writeData(OCR0A, 0xff);
280+
cpu.writeData(TCCR0A, WGM01); // WGM: CTC
281+
cpu.writeData(TCCR0B, CS00); // Set prescaler to 1
282+
cpu.cycles = 1;
283+
cpu.tick();
284+
cpu.cycles = 2;
285+
cpu.tick();
286+
const tcnt = cpu.readData(TCNT0);
287+
expect(tcnt).toEqual(0xff);
288+
expect(cpu.data[TIFR0]).toEqual(OCF0A | TOV0);
257289
});
258290

259291
it('should set OCF0B flag when timer equals OCRB', () => {

src/peripherals/timer.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,14 @@ function compToOverride(comp: CompBitsValue) {
238238
}
239239

240240
export class AVRTimer {
241+
private readonly MAX = this.config.bits === 16 ? 0xffff : 0xff;
241242
private lastCycle = 0;
242243
private ocrA: u16 = 0;
243244
private nextOcrA: u16 = 0;
244245
private ocrB: u16 = 0;
245246
private nextOcrB: u16 = 0;
246247
private ocrUpdateMode = OCRUpdateMode.Immediate;
248+
private tovUpdateMode = TOVUpdateMode.Max;
247249
private icr: u16 = 0; // only for 16-bit timers
248250
private timerMode: TimerMode;
249251
private topValue: TimerTopValue;
@@ -403,10 +405,11 @@ export class AVRTimer {
403405

404406
private updateWGMConfig() {
405407
const wgmModes = this.config.bits === 16 ? wgmModes16Bit : wgmModes8Bit;
406-
const [timerMode, topValue, ocrUpdateMode] = wgmModes[this.WGM];
408+
const [timerMode, topValue, ocrUpdateMode, tovUpdateMode] = wgmModes[this.WGM];
407409
this.timerMode = timerMode;
408410
this.topValue = topValue;
409411
this.ocrUpdateMode = ocrUpdateMode;
412+
this.tovUpdateMode = tovUpdateMode;
410413
}
411414

412415
count = (reschedule = true) => {
@@ -417,28 +420,34 @@ export class AVRTimer {
417420
const counterDelta = Math.floor(delta / divider);
418421
this.lastCycle += counterDelta * divider;
419422
const val = this.tcnt;
420-
const { timerMode } = this;
423+
const { timerMode, TOP } = this;
421424
const phasePwm =
422425
timerMode === TimerMode.PWMPhaseCorrect || timerMode === TimerMode.PWMPhaseFrequencyCorrect;
423-
const limit = this.TOP + 1;
424426
const newVal = phasePwm
425427
? this.phasePwmCount(val, counterDelta)
426-
: (val + counterDelta) % limit;
427-
const overflow = val + counterDelta >= limit;
428+
: (val + counterDelta) % (TOP + 1);
429+
const overflow = val + counterDelta > TOP;
428430
// A CPU write overrides (has priority over) all counter clear or count operations.
429431
if (!this.tcntUpdated) {
430432
this.tcnt = newVal;
431433
this.timerUpdated();
432434
}
433435

434-
if (!phasePwm && this.ocrUpdateMode == OCRUpdateMode.Bottom && overflow) {
435-
// OCRUpdateMode.Top only occurs in Phase Correct modes, handled by phasePwmCount()
436-
this.ocrA = this.nextOcrA;
437-
this.ocrB = this.nextOcrB;
438-
}
436+
if (!phasePwm) {
437+
if (this.ocrUpdateMode == OCRUpdateMode.Bottom && overflow) {
438+
// OCRUpdateMode.Top only occurs in Phase Correct modes, handled by phasePwmCount()
439+
this.ocrA = this.nextOcrA;
440+
this.ocrB = this.nextOcrB;
441+
}
439442

440-
if ((timerMode === TimerMode.Normal || timerMode === TimerMode.FastPWM) && val > newVal) {
441-
cpu.setInterruptFlag(this.OVF);
443+
// OCRUpdateMode.Bottom only occurs in Phase Correct modes, handled by phasePwmCount().
444+
// Thus we only handle TOVUpdateMode.Top or TOVUpdateMode.Max here.
445+
if (
446+
(newVal === TOP || overflow) &&
447+
(this.tovUpdateMode == TOVUpdateMode.Top || TOP === this.MAX)
448+
) {
449+
cpu.setInterruptFlag(this.OVF);
450+
}
442451
}
443452
}
444453
if (this.tcntUpdated) {
@@ -492,11 +501,6 @@ export class AVRTimer {
492501

493502
if (this.ocrA && value === this.ocrA) {
494503
this.cpu.setInterruptFlag(this.OCFA);
495-
if (this.timerMode === TimerMode.CTC) {
496-
// Clear Timer on Compare Match (CTC) Mode
497-
this.tcnt = 0;
498-
this.cpu.setInterruptFlag(this.OVF);
499-
}
500504
if (this.compA) {
501505
this.updateCompPin(this.compA, 'A');
502506
}

0 commit comments

Comments
 (0)