#include <string.h>
#include "sim_twi.h"
+static void twi_bus_master_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+ switch (irq->irq) {
+ case TWI_MASTER_STOP:
+ bus->peer = NULL;
+ break;
+ case TWI_MASTER_START:
+ bus->peer = NULL;
+ bus->ack = 0;
+ break;
+ case TWI_MASTER_MISO:
+ bus->ack = 0;
+ break;
+ case TWI_MASTER_MOSI:
+ bus->ack = 0;
+ break;
+ case TWI_MASTER_ACK:
+ if (!peer) {
+ }
+ break;
+ }
+}
+
+static void twi_bus_slave_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+ twi_slave_t * slave = (twi_slave_t*)param;
+ twi_bus_t * bus = slave->bus;
+ switch (irq->irq) {
+ case TWI_SLAVE_MISO:
+ bus->latch = value;
+ break;
+ case TWI_SLAVE_ACK:
+ if (!bus->peer) {
+ bus->peer = slave;
+ printf("twi bus: slave %x selected\n", slave->address);
+ }
+ bus->ack = 0x80 | (value & 1);
+ break;
+ }
+}
+
+static void twi_slave_master_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+ twi_slave_t * slave = (twi_slave_t*)param;
+ switch (irq->irq) {
+ case TWI_MASTER_STOP:
+ if (slave->match) {
+ // we were target
+ }
+ slave->match = 0;
+ break;
+ case TWI_MASTER_START:
+ if ((value & 0xfe) == slave->address & 0xfe) {
+ if (slave->match) {
+ // restart
+ }
+ slave->match = 1;
+ avr_raise_irq(slave->irq + TWI_SLAVE_ACK, 1);
+ }
+ break;
+ case TWI_MASTER_MISO:
+ break;
+ case TWI_MASTER_MOSI:
+ break;
+ case TWI_MASTER_ACK:
+ break;
+ }
+}
+
void twi_bus_init(twi_bus_t * bus)
{
memset(bus, 0, sizeof(twi_bus_t));
+ avr_init_irq(bus->irq, 0, TWI_STATE_COUNT);
+ for (int i = 0; i < TWI_MASTER_STATE_COUNT; i++)
+ avr_irq_register_notify(bus->irq + i, twi_bus_master_irq_notify, bus);
}
void twi_bus_attach(twi_bus_t * bus, twi_slave_t * slave)
slave->bus = bus;
slave->next = bus->slave;
bus->slave = slave;
+
+ for (int i = 0; i < TWI_SLAVE_STATE_COUNT; i++)
+ avr_irq_register_notify(slave->irq + i, twi_bus_slave_irq_notify, slave);
+ for (int i = 0; i < TWI_MASTER_STATE_COUNT; i++)
+ avr_irq_register_notify(bus->irq + i, twi_slave_irq_notify, slave);
}
int twi_bus_start(twi_bus_t * bus, uint8_t address)
{
- // if we already have a peer, check to see if it's
- // still matching, if so, skip the lookup
- if (bus->peer) {
- if (twi_slave_match(bus->peer, address))
- return bus->peer->event(bus->peer, address, TWI_START);
- twi_bus_stop(bus);
- }
-
- bus->peer = NULL;
- twi_slave_t *s = bus->slave;
- while (s) {
- if (twi_slave_match(s, address)) {
- if (s->event(s, address, TWI_START)) {
- bus->peer = s;
- s->byte_index = 0;
- return 1;
- }
- }
- s = s->next;
- }
- return 0;
+ avr_raise_irq(bus->irq + TWI_MASTER_START, address);
+ return bus->peer != NULL ? 1 : 0;
}
void twi_bus_stop(twi_bus_t * bus)
{
- if (bus->peer) {
- bus->peer->event(bus->peer, 0, TWI_STOP);
- bus->peer->byte_index = 0;
- }
- bus->peer = NULL;
+ avr_raise_irq(bus->irq + TWI_MASTER_STOP, 0);
}
int twi_bus_write(twi_bus_t * bus, uint8_t data)
memset(slave, 0, sizeof(twi_slave_t));
slave->address = address;
slave->param = param;
+
}
void twi_slave_detach(twi_slave_t * slave)
#define SIM_TWI_H_
#include <stdint.h>
+#include "sim_irq.h"
-enum twi_event {
- TWI_START,
- TWI_STOP,
- // return non-zero if this slave address is handled by this slave
- // if NULL, the "address" field is used instead. If this function
- // is present, 'address' field is not used.
- TWI_PROBE,
- TWI_NACK /* Masker NACKed a receive byte. */
+/*
+ * The TWI system is designed to be representing the same state as
+ * a TWI/i2c bus itself. So each "state" of the bus is an IRQ sent
+ * by the master to the slave, with a couple sent from the
+ * slave to the master.
+ * This is designed to decorelate the operations on the "bus" so
+ * the firmware has time to "run" before acknowledging a byte, for
+ * example.
+ *
+ * IRQ Timeline goes as follow with an example transaction that
+ * does write addres, write registrer, read a byte after a i2c restart
+ * then stops the transaction.
+ *
+ * Master: START MOSI START MISO ACK STOP
+ * Slave: ACK ACK ACK MISO
+ */
+enum twi_state_e {
+ TWI_MASTER_STOP = 0,
+ TWI_MASTER_START, // master does a start with address
+ TWI_MASTER_MOSI, // master i2c write
+ TWI_MASTER_MISO, // master i2c read
+ TWI_MASTER_ACK, // master i2c ACK after a i2c read
+ TWI_MASTER_STATE_COUNT,
+
+ TWI_SLAVE_MISO = 0, // slave i2c read.
+ TWI_SLAVE_ACK, // slave acknowledges TWI_MASTER_MOSI
+ TWI_SLAVE_STATE_COUNT,
};
#define TWI_ADDRESS_READ_MASK 0x01
typedef struct twi_slave_t {
+ avr_irq_t irq[TWI_SLAVE_STATE_COUNT];
+
struct twi_bus_t * bus; // bus we are attached to
struct twi_slave_t * next; // daisy chain on the bus
- void * param; // module private parameter
- uint8_t address; // slave address (lowest bit is not used, it's for the W bit)
- int byte_index; // byte index in the transaction (since last start, restart)
-
- // handle start conditionto address+w, restart means "stop" wasn't called
- int (*event)(struct twi_slave_t* p, uint8_t address, enum twi_event event);
-
- // handle a data write, after a (re)start
- int (*write)(struct twi_slave_t* p, uint8_t v);
-
- // handle a data read, after a (re)start
- uint8_t (*read)(struct twi_slave_t* p);
+ uint32_t address; // can specify up to 4 matching addresses here
+ int match; // we are selected on the bus
+ int index; // byte index in the transaction
+
+ uint8_t latch; // last received byte
} twi_slave_t;
typedef struct twi_bus_t {
+ avr_irq_t irq[TWI_MASTER_STATE_COUNT];
+
struct twi_slave_t * slave; // daisy chain on the bus
-
struct twi_slave_t * peer; // during a transaction, this is the selected slave
+
+ uint8_t latch; // last received byte
+ uint8_t ack; // last received ack
} twi_bus_t;
void twi_bus_init(twi_bus_t * bus);
void twi_slave_init(twi_slave_t * slave, uint8_t address, void * param);
void twi_slave_detach(twi_slave_t * slave);
-int twi_slave_match(twi_slave_t * slave, uint8_t address);
#endif /* SIM_TWI_H_ */