aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUri Shaked2020-08-01 15:47:35 +0300
committerUri Shaked2020-08-01 15:47:35 +0300
commitb0bfe885f7f53d6ae0ff03977b4cd9f2b3eb856b (patch)
tree00c2dce9476c7c412b2d81445249d681ef368994
parentchore(deps): npm audit fix (diff)
downloadavr8js-b0bfe885f7f53d6ae0ff03977b4cd9f2b3eb856b.tar.gz
avr8js-b0bfe885f7f53d6ae0ff03977b4cd9f2b3eb856b.tar.bz2
avr8js-b0bfe885f7f53d6ae0ff03977b4cd9f2b3eb856b.zip
test(timer): use TestProgramRunner
Diffstat (limited to '')
-rw-r--r--src/peripherals/eeprom.spec.ts8
-rw-r--r--src/peripherals/timer.spec.ts253
-rw-r--r--src/utils/test-utils.ts2
3 files changed, 128 insertions, 135 deletions
diff --git a/src/peripherals/eeprom.spec.ts b/src/peripherals/eeprom.spec.ts
index 75cd5ea..487ce41 100644
--- a/src/peripherals/eeprom.spec.ts
+++ b/src/peripherals/eeprom.spec.ts
@@ -63,7 +63,7 @@ describe('EEPROM', () => {
it('should not erase the memory when writing if EEPM1 is high', () => {
// We subtract 0x20 to translate from RAM address space to I/O register space
- const { program } = asmProgram(`
+ const { program, instructionCount } = asmProgram(`
; register addresses
_REPLACE TWSR, ${EECR - 0x20}
_REPLACE EEARL, ${EEARL - 0x20}
@@ -85,7 +85,7 @@ describe('EEPROM', () => {
eepromBackend.memory[9] = 0x0f; // high four bits are cleared
const runner = new TestProgramRunner(cpu, eeprom);
- runner.runInstructions(program.length);
+ runner.runInstructions(instructionCount);
// EEPROM was 0x0f, and our program wrote 0x55.
// Since write (without erase) only clears bits, we expect 0x05 now.
@@ -197,7 +197,7 @@ describe('EEPROM', () => {
describe('EEPROM erase', () => {
it('should only erase the memory when EEPM0 is high', () => {
// We subtract 0x20 to translate from RAM address space to I/O register space
- const { program } = asmProgram(`
+ const { program, instructionCount } = asmProgram(`
; register addresses
_REPLACE TWSR, ${EECR - 0x20}
_REPLACE EEARL, ${EEARL - 0x20}
@@ -219,7 +219,7 @@ describe('EEPROM', () => {
eepromBackend.memory[9] = 0x22;
const runner = new TestProgramRunner(cpu, eeprom);
- runner.runInstructions(program.length);
+ runner.runInstructions(instructionCount);
expect(eepromBackend.memory[9]).toEqual(0xff);
});
diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts
index 8e1949d..3a7d1d9 100644
--- a/src/peripherals/timer.spec.ts
+++ b/src/peripherals/timer.spec.ts
@@ -1,8 +1,7 @@
import { CPU } from '../cpu/cpu';
-import { avrInstruction } from '../cpu/instruction';
-import { assemble } from '../utils/assembler';
-import { AVRTimer, timer0Config, timer1Config, timer2Config } from './timer';
+import { asmProgram, TestProgramRunner } from '../utils/test-utils';
import { PinOverrideMode } from './gpio';
+import { AVRTimer, timer0Config, timer1Config, timer2Config } from './timer';
// CPU registers
const R1 = 1;
@@ -56,22 +55,12 @@ const CS10 = 1;
const CS21 = 2;
const CS22 = 4;
-describe('timer', () => {
- let cpu: CPU;
-
- beforeEach(() => {
- cpu = new CPU(new Uint16Array(0x1000));
- });
-
- function loadProgram(...instructions: string[]) {
- const { bytes, errors } = assemble(instructions.join('\n'));
- if (errors.length) {
- throw new Error('Assembly failed: ' + errors);
- }
- cpu.progBytes.set(bytes, 0);
- }
+// opcodes
+const nopOpCode = '0000';
+describe('timer', () => {
it('should update timer every tick when prescaler is 1', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.data[TCCR0B] = CS00; // Set prescaler to 1
cpu.cycles = 1;
@@ -81,6 +70,7 @@ describe('timer', () => {
});
it('should update timer every 64 ticks when prescaler is 3', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.data[TCCR0B] = CS01 | CS00; // Set prescaler to 64
cpu.cycles = 64;
@@ -90,6 +80,7 @@ describe('timer', () => {
});
it('should not update timer if it has been disabled', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.data[TCCR0B] = 0; // No prescaler (timer disabled)
cpu.cycles = 100000;
@@ -99,6 +90,7 @@ describe('timer', () => {
});
it('should set TOV if timer overflows', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
timer.tick();
@@ -111,6 +103,7 @@ describe('timer', () => {
});
it('should set TOV if timer overflows in FAST PWM mode', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
timer.tick();
@@ -125,6 +118,7 @@ describe('timer', () => {
});
it('should generate an overflow interrupt if timer overflows and interrupts enabled', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
timer.tick();
@@ -141,6 +135,7 @@ describe('timer', () => {
});
it('should not generate an overflow interrupt when global interrupts disabled', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
timer.tick();
@@ -155,6 +150,7 @@ describe('timer', () => {
});
it('should not generate an overflow interrupt when TOIE0 is clear', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0xff);
timer.tick();
@@ -169,6 +165,7 @@ describe('timer', () => {
});
it('should set OCF0A flag when timer equals OCRA', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x10);
timer.tick();
@@ -183,6 +180,7 @@ describe('timer', () => {
});
it('should clear the timer in CTC mode if it equals to OCRA', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x10);
timer.tick();
@@ -198,6 +196,7 @@ describe('timer', () => {
});
it('should set OCF0B flag when timer equals OCRB', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x10);
timer.tick();
@@ -212,6 +211,7 @@ describe('timer', () => {
});
it('should generate Timer Compare A interrupt when TCNT0 == TCNTA', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x20);
timer.tick();
@@ -229,6 +229,7 @@ describe('timer', () => {
});
it('should not generate Timer Compare A interrupt when OCIEA is disabled', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x20);
timer.tick();
@@ -245,6 +246,7 @@ describe('timer', () => {
});
it('should generate Timer Compare B interrupt when TCNT0 == TCNTB', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(TCNT0, 0x20);
timer.tick();
@@ -264,24 +266,23 @@ describe('timer', () => {
it('should not increment TCNT on the same cycle of TCNT write (issue #36)', () => {
// At the end of this short program, R17 should contain 0x31. Verified against
// a physical ATmega328p.
- const program = [
- 'LDI r16, 0x1', // TCCR0B = 1 << CS00;
- 'OUT 0x25, r16',
- 'LDI r16, 0x30', // TCNT <- 0x30
- 'OUT 0x26, r16',
- 'NOP',
- 'IN r17, 0x26', // r17 <- TCNT
- ];
- loadProgram(...program);
+ const { program, instructionCount } = asmProgram(`
+ LDI r16, 0x1 ; TCCR0B = 1 << CS00;
+ OUT 0x25, r16
+ LDI r16, 0x30 ; TCNT <- 0x30
+ OUT 0x26, r16
+ NOP
+ IN r17, 0x26 ; r17 <- TCNT
+ `);
+ const cpu = new CPU(program);
const timer = new AVRTimer(cpu, timer0Config);
- for (let i = 0; i < program.length; i++) {
- avrInstruction(cpu);
- timer.tick();
- }
+ const runner = new TestProgramRunner(cpu, timer);
+ runner.runInstructions(instructionCount);
expect(cpu.data[R17]).toEqual(0x31);
});
it('timer2 should count every 256 ticks when prescaler is 6 (issue #5)', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer2Config);
cpu.data[TCCR2B] = CS22 | CS21; // Set prescaler to 256
@@ -295,120 +296,107 @@ describe('timer', () => {
});
it('should update TCNT as it is being read by a 2-cycle instruction (issue #40)', () => {
- const program = [
- 'LDI r16, 0x1', // TCCR0B = 1 << CS00;
- 'OUT 0x25, r16',
- 'LDI r16, 0x0', // TCNT0 <- 0x30
- 'OUT 0x26, r16',
- 'NOP',
- `LDS r1, 0x46`, // r1 <- TCNT0 (2 cycles)
- ];
- loadProgram(...program);
+ const { program, instructionCount } = asmProgram(`
+ LDI r16, 0x1 ; TCCR0B = 1 << CS00
+ OUT 0x25, r16
+ LDI r16, 0x0 ; TCNT0 <- 0x30
+ OUT 0x26, r16
+ NOP
+ LDS r1, 0x46 ; r1 <- TCNT0 (2 cycles)
+ `);
+ const cpu = new CPU(program);
const timer = new AVRTimer(cpu, timer0Config);
- for (let i = 0; i < program.length; i++) {
- avrInstruction(cpu);
- timer.tick();
- }
+ const runner = new TestProgramRunner(cpu, timer);
+ runner.runInstructions(instructionCount);
expect(cpu.data[R1]).toEqual(2);
});
describe('Phase-correct PWM mode', () => {
it('should count up to TOP, down to 0, and then set TOV flag', () => {
- const program = [
- // Set waveform generation mode (WGM) to PWM, Phase Correct, top OCR0A
- 'LDI r16, 0x1', // TCCR0A = 1 << WGM00;
- 'OUT 0x24, r16',
- 'LDI r16, 0x9', // TCCR0B = (1 << WGM02) | (1 << CS00);
- 'OUT 0x25, r16',
- 'LDI r16, 0x3', // OCR0A = 0x3;
- 'OUT 0x27, r16',
- 'LDI r16, 0x2', // TCNT0 = 0x2;
- 'OUT 0x26, r16',
- ];
- const nops = [
- 'NOP', // TCNT0 will be 3
- 'NOP', // TCNT0 will be 2
- 'NOP', // TCNT0 will be 1
- 'NOP', // TCNT0 will be 0
- 'NOP', // TCNT0 will be 1 (end of test)
- ];
- loadProgram(...program, ...nops);
+ const { program, lines, instructionCount } = asmProgram(`
+ ; Set waveform generation mode (WGM) to PWM, Phase Correct, top OCR0A
+ LDI r16, 0x1 ; TCCR0A = 1 << WGM00;
+ OUT 0x24, r16
+ LDI r16, 0x9 ; TCCR0B = (1 << WGM02) | (1 << CS00);
+ OUT 0x25, r16
+ LDI r16, 0x3 ; OCR0A = 0x3;
+ OUT 0x27, r16
+ LDI r16, 0x2 ; TCNT0 = 0x2;
+ OUT 0x26, r16
+
+ NOP ; TCNT0 will be 3
+ NOP ; TCNT0 will be 2
+ NOP ; TCNT0 will be 1
+ NOP ; TCNT0 will be 0
+ NOP ; TCNT0 will be 1 (end of test)
+ `);
+ const nopCount = lines.filter((line) => line.bytes == nopOpCode).length;
+ const cpu = new CPU(program);
const timer = new AVRTimer(cpu, timer0Config);
-
- for (let i = 0; i < program.length; i++) {
- avrInstruction(cpu);
- timer.tick();
- }
+ const runner = new TestProgramRunner(cpu, timer);
+ runner.runInstructions(instructionCount - nopCount);
expect(cpu.readData(TCNT0)).toEqual(2);
- avrInstruction(cpu);
- timer.tick();
+ runner.runInstructions(1);
expect(cpu.readData(TCNT0)).toEqual(3);
- avrInstruction(cpu);
- timer.tick();
+ runner.runInstructions(1);
expect(cpu.readData(TCNT0)).toEqual(2);
- avrInstruction(cpu);
- timer.tick();
+ runner.runInstructions(1);
expect(cpu.readData(TCNT0)).toEqual(1);
expect(cpu.data[TIFR0] & TOV0).toEqual(0);
- avrInstruction(cpu);
- timer.tick();
+ runner.runInstructions(1);
expect(cpu.readData(TCNT0)).toEqual(0);
expect(cpu.data[TIFR0] & TOV0).toEqual(TOV0);
- avrInstruction(cpu);
- timer.tick();
+ runner.runInstructions(1);
expect(cpu.readData(TCNT0)).toEqual(1);
});
it('should clear OC0A when TCNT0=OCR0A and counting up', () => {
- const program = [
- // Set waveform generation mode (WGM) to PWM, Phase Correct
- 'LDI r16, 0x81', // TCCR0A = (1 << COM0A1) || (1 << WGM01);
- 'OUT 0x24, r16',
- 'LDI r16, 0x1', // TCCR0B = (1 << CS00);
- 'OUT 0x25, r16',
- 'LDI r16, 0xfe', // OCR0A = 0xfe;
- 'OUT 0x27, r16',
- 'LDI r16, 0xfd', // TCNT0 = 0xfd;
- 'OUT 0x26, r16',
- ];
- const nops = [
- 'NOP', // TCNT0 will be 0xfe
- 'NOP', // TCNT0 will be 0xff
- 'NOP', // TCNT0 will be 0xfe again (end of test)
- ];
- loadProgram(...program, ...nops);
+ const { program, lines, instructionCount } = asmProgram(`
+ ; Set waveform generation mode (WGM) to PWM, Phase Correct
+ LDI r16, 0x81 ; TCCR0A = (1 << COM0A1) || (1 << WGM01);
+ OUT 0x24, r16
+ LDI r16, 0x1 ; TCCR0B = (1 << CS00);
+ OUT 0x25, r16
+ LDI r16, 0xfe ; OCR0A = 0xfe;
+ OUT 0x27, r16
+ LDI r16, 0xfd ; TCNT0 = 0xfd;
+ OUT 0x26, r16
+
+ NOP ; TCNT0 will be 0xfe
+ NOP ; TCNT0 will be 0xff
+ NOP ; TCNT0 will be 0xfe again (end of test)
+ `);
+
+ const cpu = new CPU(program);
const timer = new AVRTimer(cpu, timer0Config);
// Listen to Port D's internal callback
const gpioCallback = jest.fn();
cpu.gpioTimerHooks[PORTD] = gpioCallback;
- for (let i = 0; i < program.length; i++) {
- avrInstruction(cpu);
- timer.tick();
- }
+ const nopCount = lines.filter((line) => line.bytes == nopOpCode).length;
+ const runner = new TestProgramRunner(cpu, timer);
+ runner.runInstructions(instructionCount - nopCount);
+
expect(cpu.readData(TCNT0)).toEqual(0xfd);
expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b);
gpioCallback.mockClear();
- avrInstruction(cpu);
- timer.tick();
+ runner.runInstructions(1);
expect(cpu.readData(TCNT0)).toEqual(0xfe);
expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Clear, 0x2b);
gpioCallback.mockClear();
- avrInstruction(cpu);
- timer.tick();
+ runner.runInstructions(1);
expect(cpu.readData(TCNT0)).toEqual(0xff);
expect(gpioCallback).not.toHaveBeenCalled();
- avrInstruction(cpu);
- timer.tick();
+ runner.runInstructions(1);
expect(cpu.readData(TCNT0)).toEqual(0xfe);
expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set, 0x2b);
});
@@ -416,6 +404,7 @@ describe('timer', () => {
describe('16 bit timers', () => {
it('should increment 16-bit TCNT by 1', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1H, 0x22); // TCNT1 <- 0x2233
cpu.writeData(TCNT1, 0x33); // ...
@@ -432,6 +421,7 @@ describe('timer', () => {
});
it('should set OCF0A flag when timer equals OCRA (16 bit mode)', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1H, 0x10); // TCNT1 <- 0x10ee
cpu.writeData(TCNT1, 0xee); // ...
@@ -448,6 +438,7 @@ describe('timer', () => {
});
it('should generate an overflow interrupt if timer overflows and interrupts enabled', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1H, 0x3); // TCNT1 <- 0x3ff
cpu.writeData(TCNT1, 0xff); // ...
@@ -466,6 +457,7 @@ describe('timer', () => {
});
it('should reset the timer once it reaches ICR value in mode 12', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1H, 0x50); // TCNT1 <- 0x500f
cpu.writeData(TCNT1, 0x0f); // ...
@@ -482,6 +474,7 @@ describe('timer', () => {
});
it('should not update the high byte of TCNT if written after the low byte (issue #37)', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1, 0x22);
cpu.writeData(TCNT1H, 0x55);
@@ -492,6 +485,7 @@ describe('timer', () => {
});
it('reading from TCNT1H before TCNT1L should return old value (issue #37)', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
const timer = new AVRTimer(cpu, timer1Config);
cpu.writeData(TCNT1H, 0xff);
cpu.writeData(TCNT1, 0xff);
@@ -507,42 +501,41 @@ describe('timer', () => {
});
it('should toggle OC1B on Compare Match', () => {
- const program = [
- // Set waveform generation mode (WGM) to Normal, top 0xFFFF
- 'LDI r16, 0x10', // TCCR1A = (1 << COM1B0);
- 'STS 0x80, r16',
- 'LDI r16, 0x1', // TCCR1B = (1 << CS00);
- 'STS 0x81, r16',
- 'LDI r16, 0x0', // OCR1BH = 0x0;
- 'STS 0x8B, r16',
- 'LDI r16, 0x4a', // OCR1BL = 0x4a;
- 'STS 0x8A, r16',
- 'LDI r16, 0x0', // TCNT1H = 0x0;
- 'STS 0x85, r16',
- 'LDI r16, 0x49', // TCNT1L = 0x49;
- 'STS 0x84, r16',
- ];
- const nops = [
- 'NOP', // TCNT1 will be 0x49
- 'NOP', // TCNT1 will be 0x4a
- ];
- loadProgram(...program, ...nops);
+ const { program, lines, instructionCount } = asmProgram(`
+ ; Set waveform generation mode (WGM) to Normal, top 0xFFFF
+ LDI r16, 0x10 ; TCCR1A = (1 << COM1B0);
+ STS 0x80, r16
+ LDI r16, 0x1 ; TCCR1B = (1 << CS00);
+ STS 0x81, r16
+ LDI r16, 0x0 ; OCR1BH = 0x0;
+ STS 0x8B, r16
+ LDI r16, 0x4a ; OCR1BL = 0x4a;
+ STS 0x8A, r16
+ LDI r16, 0x0 ; TCNT1H = 0x0;
+ STS 0x85, r16
+ LDI r16, 0x49 ; TCNT1L = 0x49;
+ STS 0x84, r16
+
+ NOP ; TCNT1 will be 0x49
+ NOP ; TCNT1 will be 0x4a
+ `);
+
+ const cpu = new CPU(program);
const timer = new AVRTimer(cpu, timer1Config);
// Listen to Port B's internal callback
const gpioCallback = jest.fn();
cpu.gpioTimerHooks[PORTB] = gpioCallback;
- for (let i = 0; i < program.length; i++) {
- avrInstruction(cpu);
- timer.tick();
- }
+ const nopCount = lines.filter((line) => line.bytes == nopOpCode).length;
+ const runner = new TestProgramRunner(cpu, timer);
+ runner.runInstructions(instructionCount - nopCount);
+
expect(cpu.readData(TCNT1)).toEqual(0x49);
expect(gpioCallback).toHaveBeenCalledWith(2, PinOverrideMode.Enable, 0x25);
gpioCallback.mockClear();
- avrInstruction(cpu);
- timer.tick();
+ runner.runInstructions(1);
expect(cpu.readData(TCNT1)).toEqual(0x4a);
expect(gpioCallback).toHaveBeenCalledWith(2, PinOverrideMode.Toggle, 0x25);
});
diff --git a/src/utils/test-utils.ts b/src/utils/test-utils.ts
index dfb6b32..ca483f3 100644
--- a/src/utils/test-utils.ts
+++ b/src/utils/test-utils.ts
@@ -7,7 +7,7 @@ export function asmProgram(source: string) {
if (errors.length) {
throw new Error('Assembly failed: ' + errors);
}
- return { program: new Uint16Array(bytes.buffer), lines };
+ return { program: new Uint16Array(bytes.buffer), lines, instructionCount: lines.length };
}
export class TestProgramRunner {