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
Diffstat (limited to '')
-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:
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.