aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUri Shaked2020-01-31 15:08:53 +0200
committerUri Shaked2020-01-31 15:08:53 +0200
commitf95a33c4bd6310f69bc24ee2f4c05157c532e25d (patch)
treec25add972dc4e9c4f92de6a6309bc72e440ed0fb
parentfix(assembler): BRBC/BRBS forward labels fail (diff)
downloadavr8js-f95a33c4bd6310f69bc24ee2f4c05157c532e25d.tar.gz
avr8js-f95a33c4bd6310f69bc24ee2f4c05157c532e25d.tar.bz2
avr8js-f95a33c4bd6310f69bc24ee2f4c05157c532e25d.zip
test(twi): assembly code to test master transmit #10
-rw-r--r--src/twi.spec.ts188
-rw-r--r--src/twi.ts14
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);
+ });
+ });
});
diff --git a/src/twi.ts b/src/twi.ts
index e2cfe24..027061f 100644
--- a/src/twi.ts
+++ b/src/twi.ts
@@ -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: