diff options
Diffstat (limited to 'src/peripherals/eeprom.spec.ts')
| -rw-r--r-- | src/peripherals/eeprom.spec.ts | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/src/peripherals/eeprom.spec.ts b/src/peripherals/eeprom.spec.ts new file mode 100644 index 0000000..ba66100 --- /dev/null +++ b/src/peripherals/eeprom.spec.ts @@ -0,0 +1,195 @@ +import { CPU } from '../cpu/cpu'; +import { AVREEPROM, EEPROMMemoryBackend } from './eeprom'; +import { asmProgram, TestProgramRunner } from '../utils/test-utils'; + +// EEPROM Registers +const EECR = 0x3f; +const EEDR = 0x40; +const EEARL = 0x41; +const EEARH = 0x42; +const SREG = 95; + +// Register bit names +/* eslint-disable @typescript-eslint/no-unused-vars */ +const EERE = 1; +const EEPE = 2; +const EEMPE = 4; +const EERIE = 8; +const EEPM0 = 16; +const EEPM1 = 32; +/* eslint-enable @typescript-eslint/no-unused-vars */ + +describe('EEPROM', () => { + describe('Reading the EEPROM', () => { + it('should return 0xff when reading from an empty location', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + const eeprom = new AVREEPROM(cpu, new EEPROMMemoryBackend(1024)); + cpu.writeData(EEARL, 0); + cpu.writeData(EEARH, 0); + cpu.writeData(EECR, EERE); + eeprom.tick(); + expect(cpu.cycles).toEqual(4); + expect(cpu.data[EEDR]).toEqual(0xff); + }); + + it('should return the value stored at the given EEPROM address', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + const eepromBackend = new EEPROMMemoryBackend(1024); + const eeprom = new AVREEPROM(cpu, eepromBackend); + eepromBackend.memory[0x250] = 0x42; + cpu.writeData(EEARL, 0x50); + cpu.writeData(EEARH, 0x2); + cpu.writeData(EECR, EERE); + eeprom.tick(); + expect(cpu.data[EEDR]).toEqual(0x42); + }); + }); + + describe('Writing to the EEPROM', () => { + it('should write a byte to the given EEPROM address', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + const eepromBackend = new EEPROMMemoryBackend(1024); + const eeprom = new AVREEPROM(cpu, eepromBackend); + cpu.writeData(EEDR, 0x55); + cpu.writeData(EEARL, 15); + cpu.writeData(EEARH, 0); + cpu.writeData(EECR, EEMPE); + cpu.writeData(EECR, EEPE); + eeprom.tick(); + expect(cpu.cycles).toEqual(2); + expect(eepromBackend.memory[15]).toEqual(0x55); + expect(cpu.data[EECR] & EEPE).toEqual(EEPE); + }); + + 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(` + ; register addresses + _REPLACE TWSR, ${EECR - 0x20} + _REPLACE EEARL, ${EEARL - 0x20} + _REPLACE EEDR, ${EEDR - 0x20} + _REPLACE EECR, ${EECR - 0x20} + + LDI r16, 0x55 + OUT EEDR, r16 + LDI r16, 9 + OUT EEARL, r16 + SBI EECR, 5 ; EECR |= EEPM1 + SBI EECR, 2 ; EECR |= EEMPE + SBI EECR, 1 ; EECR |= EEPE + `); + + const cpu = new CPU(program); + const eepromBackend = new EEPROMMemoryBackend(1024); + const eeprom = new AVREEPROM(cpu, eepromBackend); + eepromBackend.memory[9] = 0x0f; // high four bits are cleared + + const runner = new TestProgramRunner(cpu, eeprom); + runner.runInstructions(program.length); + + // EEPROM was 0x0f, and our program wrote 0x55. + // Since write (without erase) only clears bits, we expect 0x05 now. + expect(eepromBackend.memory[9]).toEqual(0x05); + }); + + it('should clear the EEPE bit and fire an interrupt when write has been completed', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + const eepromBackend = new EEPROMMemoryBackend(1024); + const eeprom = new AVREEPROM(cpu, eepromBackend); + cpu.writeData(EEDR, 0x55); + cpu.writeData(EEARL, 15); + cpu.writeData(EEARH, 0); + cpu.writeData(EECR, EEMPE | EERIE); + cpu.data[SREG] = 0x80; // SREG: I------- + cpu.writeData(EECR, EEPE); + cpu.cycles += 1000; + eeprom.tick(); + // At this point, write shouldn't be complete yet + expect(cpu.data[EECR] & EEPE).toEqual(EEPE); + expect(cpu.pc).toEqual(0); + cpu.cycles += 10000000; + // And now, 10 million cycles later, it should. + eeprom.tick(); + expect(eepromBackend.memory[15]).toEqual(0x55); + expect(cpu.data[EECR] & EEPE).toEqual(0); + expect(cpu.pc).toEqual(0x2c); // EEPROM Ready interrupt + }); + + it('should skip the write if EEMPE is clear', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + const eepromBackend = new EEPROMMemoryBackend(1024); + const eeprom = new AVREEPROM(cpu, eepromBackend); + cpu.writeData(EEDR, 0x55); + cpu.writeData(EEARL, 15); + cpu.writeData(EEARH, 0); + cpu.writeData(EECR, EEMPE); + cpu.cycles = 8; // waiting for more than 4 cycles should clear EEMPE + eeprom.tick(); + cpu.writeData(EECR, EEPE); + eeprom.tick(); + // Ensure that nothing was written, and EEPE bit is clear + expect(cpu.cycles).toEqual(8); + expect(eepromBackend.memory[15]).toEqual(0xff); + expect(cpu.data[EECR] & EEPE).toEqual(0); + }); + + it('should skip the write if another write is already in progress', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + const eepromBackend = new EEPROMMemoryBackend(1024); + const eeprom = new AVREEPROM(cpu, eepromBackend); + + // Write 0x55 to address 15 + cpu.writeData(EEDR, 0x55); + cpu.writeData(EEARL, 15); + cpu.writeData(EEARH, 0); + cpu.writeData(EECR, EEMPE); + cpu.writeData(EECR, EEPE); + eeprom.tick(); + expect(cpu.cycles).toEqual(2); + + // Write 0x66 to address 16 (first write is still in progress) + cpu.writeData(EEDR, 0x66); + cpu.writeData(EEARL, 16); + cpu.writeData(EEARH, 0); + cpu.writeData(EECR, EEMPE); + cpu.writeData(EECR, EEPE); + eeprom.tick(); + + // Ensure that second write didn't happen + expect(cpu.cycles).toEqual(2); + expect(eepromBackend.memory[15]).toEqual(0x55); + expect(eepromBackend.memory[16]).toEqual(0xff); + }); + }); + + 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(` + ; register addresses + _REPLACE TWSR, ${EECR - 0x20} + _REPLACE EEARL, ${EEARL - 0x20} + _REPLACE EEDR, ${EEDR - 0x20} + _REPLACE EECR, ${EECR - 0x20} + + LDI r16, 0x55 + OUT EEDR, r16 + LDI r16, 9 + OUT EEARL, r16 + SBI EECR, 4 ; EECR |= EEPM0 + SBI EECR, 2 ; EECR |= EEMPE + SBI EECR, 1 ; EECR |= EEPE + `); + + const cpu = new CPU(program); + const eepromBackend = new EEPROMMemoryBackend(1024); + const eeprom = new AVREEPROM(cpu, eepromBackend); + eepromBackend.memory[9] = 0x22; + + const runner = new TestProgramRunner(cpu, eeprom); + runner.runInstructions(program.length); + + expect(eepromBackend.memory[9]).toEqual(0xff); + }); + }); +}); |
