diff options
| author | Uri Shaked | 2020-12-20 16:28:06 +0200 |
|---|---|---|
| committer | Uri Shaked | 2020-12-20 16:30:23 +0200 |
| commit | f32994d68ce708cbc4e24f5de655c8cf0ffb52ef (patch) | |
| tree | 15d7abb5ea7806434b2ce662c38ee5e2167f5ff3 /src/peripherals | |
| parent | 0.14.3 (diff) | |
| download | avr8js-f32994d68ce708cbc4e24f5de655c8cf0ffb52ef.tar.gz avr8js-f32994d68ce708cbc4e24f5de655c8cf0ffb52ef.tar.bz2 avr8js-f32994d68ce708cbc4e24f5de655c8cf0ffb52ef.zip | |
fix(timer): TOV flag does not update correctly #75
fix #75
Diffstat (limited to '')
| -rw-r--r-- | src/peripherals/timer.spec.ts | 38 | ||||
| -rw-r--r-- | src/peripherals/timer.ts | 38 |
2 files changed, 56 insertions, 20 deletions
diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index 11c0264..583e759 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -239,7 +239,7 @@ describe('timer', () => { expect(cpu.cycles).toEqual(2); }); - it('should clear the timer in CTC mode if it equals to OCRA', () => { + it('should reset the counter in CTC mode if it equals to OCRA', () => { const cpu = new CPU(new Uint16Array(0x1000)); new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0x10); @@ -248,12 +248,44 @@ describe('timer', () => { cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; cpu.tick(); - cpu.cycles = 2; + cpu.cycles = 3; cpu.tick(); const tcnt = cpu.readData(TCNT0); expect(tcnt).toEqual(0); expect(cpu.pc).toEqual(0); - expect(cpu.cycles).toEqual(2); + expect(cpu.cycles).toEqual(3); + }); + + it('should not set the TOV bit when TOP < MAX in CTC mode (issue #75)', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + new AVRTimer(cpu, timer0Config); + cpu.writeData(TCNT0, 0x1e); + cpu.writeData(OCR0A, 0x1f); + cpu.writeData(TCCR0A, WGM01); // WGM: CTC + cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 + cpu.cycles = 1; + cpu.tick(); + cpu.cycles = 2; + cpu.tick(); + const tcnt = cpu.readData(TCNT0); + expect(tcnt).toEqual(0x1f); + expect(cpu.data[TIFR0]).toEqual(OCF0A); // TOV0 clear + }); + + it('should set the TOV bit when TOP == MAX in CTC mode (issue #75)', () => { + 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(); + const tcnt = cpu.readData(TCNT0); + expect(tcnt).toEqual(0xff); + expect(cpu.data[TIFR0]).toEqual(OCF0A | TOV0); }); it('should set OCF0B flag when timer equals OCRB', () => { diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts index 0f4d837..f10a49b 100644 --- a/src/peripherals/timer.ts +++ b/src/peripherals/timer.ts @@ -238,12 +238,14 @@ function compToOverride(comp: CompBitsValue) { } export class AVRTimer { + private readonly MAX = this.config.bits === 16 ? 0xffff : 0xff; private lastCycle = 0; private ocrA: u16 = 0; private nextOcrA: u16 = 0; private ocrB: u16 = 0; private nextOcrB: u16 = 0; private ocrUpdateMode = OCRUpdateMode.Immediate; + private tovUpdateMode = TOVUpdateMode.Max; private icr: u16 = 0; // only for 16-bit timers private timerMode: TimerMode; private topValue: TimerTopValue; @@ -403,10 +405,11 @@ export class AVRTimer { private updateWGMConfig() { const wgmModes = this.config.bits === 16 ? wgmModes16Bit : wgmModes8Bit; - const [timerMode, topValue, ocrUpdateMode] = wgmModes[this.WGM]; + const [timerMode, topValue, ocrUpdateMode, tovUpdateMode] = wgmModes[this.WGM]; this.timerMode = timerMode; this.topValue = topValue; this.ocrUpdateMode = ocrUpdateMode; + this.tovUpdateMode = tovUpdateMode; } count = (reschedule = true) => { @@ -417,28 +420,34 @@ export class AVRTimer { const counterDelta = Math.floor(delta / divider); this.lastCycle += counterDelta * divider; const val = this.tcnt; - const { timerMode } = this; + const { timerMode, TOP } = this; const phasePwm = timerMode === TimerMode.PWMPhaseCorrect || timerMode === TimerMode.PWMPhaseFrequencyCorrect; - const limit = this.TOP + 1; const newVal = phasePwm ? this.phasePwmCount(val, counterDelta) - : (val + counterDelta) % limit; - const overflow = val + counterDelta >= limit; + : (val + counterDelta) % (TOP + 1); + const overflow = val + counterDelta > TOP; // A CPU write overrides (has priority over) all counter clear or count operations. if (!this.tcntUpdated) { this.tcnt = newVal; this.timerUpdated(); } - if (!phasePwm && this.ocrUpdateMode == OCRUpdateMode.Bottom && overflow) { - // OCRUpdateMode.Top only occurs in Phase Correct modes, handled by phasePwmCount() - this.ocrA = this.nextOcrA; - this.ocrB = this.nextOcrB; - } + if (!phasePwm) { + if (this.ocrUpdateMode == OCRUpdateMode.Bottom && overflow) { + // OCRUpdateMode.Top only occurs in Phase Correct modes, handled by phasePwmCount() + this.ocrA = this.nextOcrA; + this.ocrB = this.nextOcrB; + } - if ((timerMode === TimerMode.Normal || timerMode === TimerMode.FastPWM) && val > newVal) { - cpu.setInterruptFlag(this.OVF); + // 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) && + (this.tovUpdateMode == TOVUpdateMode.Top || TOP === this.MAX) + ) { + cpu.setInterruptFlag(this.OVF); + } } } if (this.tcntUpdated) { @@ -492,11 +501,6 @@ export class AVRTimer { if (this.ocrA && value === this.ocrA) { this.cpu.setInterruptFlag(this.OCFA); - if (this.timerMode === TimerMode.CTC) { - // Clear Timer on Compare Match (CTC) Mode - this.tcnt = 0; - this.cpu.setInterruptFlag(this.OVF); - } if (this.compA) { this.updateCompPin(this.compA, 'A'); } |
