Skip to content

Commit cf6d491

Browse files
committed
fix(timer): Output Compare sometimes misses Compare Match #79
fix #79
1 parent 4c611c7 commit cf6d491

File tree

2 files changed

+45
-6
lines changed

2 files changed

+45
-6
lines changed

src/peripherals/timer.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,37 @@ describe('timer', () => {
541541
expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set, 0x2b);
542542
});
543543

544+
it('should not miss Compare Match when executing multi-cycle instruction (issue #79)', () => {
545+
const { program, instructionCount } = asmProgram(`
546+
LDI r16, 0x10 ; OCR0A = 0x10; // <- TOP value
547+
OUT 0x27, r16
548+
; Set waveform generation mode (WGM) to normal, enable OC0A (Set on match)
549+
LDI r16, 0xc0 ; TCCR0A = (1 << COM0A1) | (1 << COM0A0);
550+
OUT 0x24, r16
551+
LDI r16, 0x1 ; TCCR0B = (1 << CS00);
552+
OUT 0x25, r16
553+
LDI r16, 0xf ; TCNT0 = 0xf;
554+
OUT 0x26, r16
555+
RJMP 1 ; TCNT0 will be 0x11 (RJMP takes 2 cycles)
556+
`);
557+
558+
const cpu = new CPU(program);
559+
new AVRTimer(cpu, timer0Config);
560+
561+
// Listen to Port D's internal callback
562+
const gpioCallback = jest.fn();
563+
cpu.gpioTimerHooks[PORTD] = gpioCallback;
564+
565+
const runner = new TestProgramRunner(cpu);
566+
runner.runInstructions(instructionCount);
567+
568+
expect(cpu.readData(TCNT0)).toEqual(0x11);
569+
expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b);
570+
571+
// Verify that Compare Match has occured and set the OC0A pin (PD6 on ATmega328p)
572+
expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set, 0x2b);
573+
});
574+
544575
it('should only update OCR0A when TCNT0=TOP in PWM Phase Correct mode (issue #76)', () => {
545576
const { program, instructionCount } = asmProgram(`
546577
LDI r16, 0x4 ; OCR0A = 0x4;

src/peripherals/timer.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ export class AVRTimer {
300300
this.tcntUpdated = true;
301301
this.cpu.updateClockEvent(this.count, 0);
302302
if (this.divider) {
303-
this.timerUpdated();
303+
this.timerUpdated(this.tcntNext, this.tcntNext);
304304
}
305305
};
306306
this.cpu.writeHooks[config.OCRA] = (value: u8) => {
@@ -432,7 +432,7 @@ export class AVRTimer {
432432
// A CPU write overrides (has priority over) all counter clear or count operations.
433433
if (!this.tcntUpdated) {
434434
this.tcnt = newVal;
435-
this.timerUpdated();
435+
this.timerUpdated(newVal, val);
436436
}
437437

438438
if (!phasePwm) {
@@ -498,15 +498,23 @@ export class AVRTimer {
498498
return value;
499499
}
500500

501-
private timerUpdated() {
502-
const value = this.tcnt;
503-
if (value === this.ocrA) {
501+
private timerUpdated(value: number, prevValue: number) {
502+
const { ocrA, ocrB, countingUp } = this;
503+
if (
504+
(countingUp && prevValue < ocrA && value >= ocrA) ||
505+
(countingUp && prevValue > value && ocrA >= value) ||
506+
(!countingUp && prevValue > ocrA && value <= ocrA)
507+
) {
504508
this.cpu.setInterruptFlag(this.OCFA);
505509
if (this.compA) {
506510
this.updateCompPin(this.compA, 'A');
507511
}
508512
}
509-
if (value === this.ocrB) {
513+
if (
514+
(countingUp && prevValue < ocrB && value >= ocrB) ||
515+
(countingUp && prevValue > value && ocrB >= value) ||
516+
(!countingUp && prevValue > ocrB && value <= ocrB)
517+
) {
510518
this.cpu.setInterruptFlag(this.OCFB);
511519
if (this.compB) {
512520
this.updateCompPin(this.compB, 'B');

0 commit comments

Comments
 (0)