diff options
| author | Uri Shaked | 2020-01-31 15:08:53 +0200 |
|---|---|---|
| committer | Uri Shaked | 2020-01-31 15:08:53 +0200 |
| commit | f95a33c4bd6310f69bc24ee2f4c05157c532e25d (patch) | |
| tree | c25add972dc4e9c4f92de6a6309bc72e440ed0fb /src | |
| parent | fix(assembler): BRBC/BRBS forward labels fail (diff) | |
| download | avr8js-f95a33c4bd6310f69bc24ee2f4c05157c532e25d.tar.gz avr8js-f95a33c4bd6310f69bc24ee2f4c05157c532e25d.tar.bz2 avr8js-f95a33c4bd6310f69bc24ee2f4c05157c532e25d.zip | |
test(twi): assembly code to test master transmit #10
Diffstat (limited to 'src')
| -rw-r--r-- | src/twi.spec.ts | 188 | ||||
| -rw-r--r-- | src/twi.ts | 14 |
2 files changed, 199 insertions, 3 deletions
diff --git a/src/twi.spec.ts b/src/twi.spec.ts index dacd958..871bc41 100644 --- a/src/twi.spec.ts +++ b/src/twi.spec.ts @@ -1,9 +1,35 @@ import { CPU } from './cpu'; import { AVRTWI, twiConfig } from './twi'; +import { assemble } from './utils/assembler'; +import { avrInstruction } from './instruction'; const FREQ_16MHZ = 16e6; +function asmProgram(source: string) { + const { bytes, errors, lines } = assemble(source); + if (errors.length) { + throw new Error('Assembly failed: ' + errors); + } + return { program: new Uint16Array(bytes.buffer), lines }; +} + +function runInstructions(cpu: CPU, twi: AVRTWI, count: number) { + for (let i = 0; i < count; i++) { + if (cpu.progMem[cpu.pc] === 0x9598) { + console.log(cpu.data[0xbc].toString(16)); + console.log(cpu.data[16]); + throw new Error('BREAK instruction encountered'); + } + avrInstruction(cpu); + twi.tick(); + } +} + describe('TWI', () => { + const TWINT = 7; + const TWSTA = 5; + const TWEN = 2; + it('should correctly calculate the sclFrequency from TWBR', () => { const cpu = new CPU(new Uint16Array(1024)); const twi = new AVRTWI(cpu, twiConfig, FREQ_16MHZ); @@ -19,4 +45,166 @@ describe('TWI', () => { cpu.writeData(0xb9, 0x01); // TWSR <- 1 (prescaler: 4) expect(twi.sclFrequency).toEqual(400000); }); + + describe('Master mode', () => { + it('should call the startEvent handler when TWSTA bit is written 1', () => { + const cpu = new CPU(new Uint16Array(1024)); + const twi = new AVRTWI(cpu, twiConfig, FREQ_16MHZ); + jest.spyOn(twi.eventHandler, 'start'); + cpu.writeData(0xbc, (1 << TWINT) | (1 << TWSTA) | (1 << TWEN)); + twi.tick(); + expect(twi.eventHandler.start).toHaveBeenCalledWith(false); + }); + + it('should successfully transmit a byte to a slave', () => { + // based on the example in page 225 of the datasheet: + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf + const { program } = asmProgram(` + ; register addresses + _REPLACE TWSR, 0xb9 + _REPLACE TWDR, 0xbb + _REPLACE TWCR, 0xbc + + ; TWCR bits + _REPLACE TWEN, 0x04 + _REPLACE TWSTO, 0x10 + _REPLACE TWSTA, 0x20 + _REPLACE TWINT, 0x80 + + ; TWSR states + _REPLACE START, 0x8 ; TWI start + _REPLACE MT_SLA_ACK, 0x18 ; Slave Adresss ACK has been received + _REPLACE MT_DATA_ACK, 0x28 ; Data ACK has been received + + ; Send start condition + ldi r16, TWEN + sbr r16, TWSTA + sbr r16, TWINT + sts TWCR, r16 + + ; Wait for TWINT Flag set. This indicates that the START condition has been transmitted + wait1: + lds r16, TWCR + andi r16, TWINT + breq wait1 + + ; Check value of TWI Status Register. Mask prescaler bits. If status different from START go to ERROR + lds r16, TWSR + andi r16, 0xf8 + cpi r16, START + brne error + + ; Load SLA_W into TWDR Register. Clear TWINT bit in TWCR to start transmission of address + ; 0x44 = Address 0x22, write mode (R/W bit clear) + _REPLACE SLA_W, 0x44 + ldi r16, SLA_W + sts TWDR, r16 + ldi r16, TWINT + sbr r16, TWEN + sts TWCR, r16 + + ; Wait for TWINT Flag set. This indicates that the SLA+W has been transmitted, and ACK/NACK has been received. + wait2: + lds r16, TWCR + andi r16, TWINT + breq wait2 + + ; Check value of TWI Status Register. Mask prescaler bits. If status different from MT_SLA_ACK go to ERROR + lds r16, TWSR + andi r16, 0xf8 + cpi r16, MT_SLA_ACK + brne error + + ; Load DATA into TWDR Register. Clear TWINT bit in TWCR to start transmission of data + _replace DATA, 0x55 + ldi r16, DATA + sts TWDR, r16 + ldi r16, TWINT + sbr r16, TWEN + sts TWCR, r16 + + ; Wait for TWINT Flag set. This indicates that the DATA has been transmitted, and ACK/NACK has been received + wait3: + lds r16, TWCR + andi r16, TWINT + breq wait3 + + ; Check value of TWI Status Register. Mask prescaler bits. If status different from MT_DATA_ACK go to ERROR + lds r16, TWSR + andi r16, 0xf8 + cpi r16, MT_DATA_ACK + brne error + + ; Transmit STOP condition + ldi r16, TWINT + sbr r16, TWEN + sbr r16, TWSTO + sts TWCR, r16 + + ; Wait for TWINT Flag set. This indicates that the STOP condition has been sent + wait4: + lds r16, TWCR + andi r16, TWINT + breq wait4 + + ; Check value of TWI Status Register. The masked value should be 0xf8 once done + lds r16, TWSR + andi r16, 0xf8 + cpi r16, 0xf8 + brne error + + ; Indicate success by loading 0x42 into r17 + ldi r17, 0x42 + + loop: + jmp loop + + ; In case of an error, toggle a breakpoint + error: + break + `); + const cpu = new CPU(program); + const twi = new AVRTWI(cpu, twiConfig, FREQ_16MHZ); + twi.eventHandler = { + start: jest.fn(), + stop: jest.fn(), + connectToSlave: jest.fn(), + writeByte: jest.fn(), + readByte: jest.fn() + }; + + // Step 1: wait for start condition + runInstructions(cpu, twi, 4); + expect(twi.eventHandler.start).toHaveBeenCalledWith(false); + + runInstructions(cpu, twi, 16); + twi.completeStart(); + + // Step 2: wait for slave connect in write mode + runInstructions(cpu, twi, 16); + expect(twi.eventHandler.connectToSlave).toHaveBeenCalledWith(0x22, true); + + runInstructions(cpu, twi, 16); + twi.completeConnect(true); + + // Step 3: wait for first data byte + runInstructions(cpu, twi, 16); + expect(twi.eventHandler.writeByte).toHaveBeenCalledWith(0x55); + + runInstructions(cpu, twi, 16); + twi.completeWrite(true); + + // Step 4: wait for stop condition + runInstructions(cpu, twi, 16); + console.log(cpu.data[16]); + expect(twi.eventHandler.stop).toHaveBeenCalled(); + + runInstructions(cpu, twi, 16); + twi.completeStop(); + + // Step 5: wait for the assembly code to indicate success by settings r17 to 0x42 + runInstructions(cpu, twi, 16); + expect(cpu.data[17]).toEqual(0x42); + }); + }); }); @@ -96,6 +96,8 @@ export class NoopTWIEventHandler implements TWIEventHandler { export class AVRTWI { public eventHandler: TWIEventHandler = new NoopTWIEventHandler(this); + private nextTick: (() => void) | null = null; + constructor(private cpu: CPU, private config: TWIConfig, private freqMHz: number) { this.updateStatus(STATUS_TWI_IDLE); this.cpu.writeHooks[config.TWCR] = (value) => { @@ -106,8 +108,7 @@ export class AVRTWI { const { status } = this; if (clearInt && value & TWCR_TWEN) { const twdrValue = this.cpu.data[this.config.TWDR]; - // TODO: this should be executed after the current instruction completes - setTimeout(() => { + this.nextTick = () => { if (value & TWCR_TWSTA) { this.eventHandler.start(status !== STATUS_TWI_IDLE); } else if (value & TWCR_TWSTO) { @@ -120,13 +121,20 @@ export class AVRTWI { const ack = !!(value & TWCR_TWEA); this.eventHandler.readByte(ack); } - }, 0); + }; this.cpu.data[config.TWCR] = value; return true; } }; } + tick() { + if (this.nextTick) { + this.nextTick(); + this.nextTick = null; + } + } + get prescaler() { switch (this.cpu.data[this.config.TWSR] & TWSR_TWPS_MASK) { case 0: |
