From: Michel Pollet Date: Sun, 27 Mar 2011 10:43:40 +0000 (+0100) Subject: parts: Adds generic i2c eeprom slave X-Git-Tag: v1.0a8~1 X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=0093da9c8ed852fd7f3394be68716fcd7d3a8c53;p=sx%2Fsimavr.git parts: Adds generic i2c eeprom slave This part simulates an i2c eeprom, it handles most modes, including "multiple addresses" ones, and ones with multiple byte offset bytes. Signed-off-by: Michel Pollet --- diff --git a/examples/parts/i2c_eeprom.c b/examples/parts/i2c_eeprom.c new file mode 100644 index 0000000..2ae7778 --- /dev/null +++ b/examples/parts/i2c_eeprom.c @@ -0,0 +1,153 @@ +/* + i2c_eeprom.c + + Copyright 2008, 2009 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + + +#include +#include +#include +#include "sim_avr.h" +#include "avr_twi.h" +#include "i2c_eeprom.h" + +/* + * called when a RESET signal is sent + */ +static void +i2c_eeprom_in_hook( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + i2c_eeprom_t * p = (i2c_eeprom_t*)param; + avr_twi_msg_irq_t v; + v.u.v = value; + + /* + * If we receive a STOP, check it was meant to us, and reset the transaction + */ + if (v.u.twi.msg & TWI_COND_STOP) { + if (p->selected) { + // it was us ! + if (p->verbose) + printf("eeprom received stop\n"); + } + p->selected = 0; + p->index = 0; + p->reg_addr = 0; + } + /* + * if we receive a start, reset status, check if the slave address is + * meant to be us, and if so reply with an ACK bit + */ + if (v.u.twi.msg & TWI_COND_START) { + p->selected = 0; + p->index = 0; + if ((p->addr_base & p->addr_mask) == (v.u.twi.addr & p->addr_mask)) { + // it's us ! + p->selected = v.u.twi.addr; + avr_raise_irq(p->irq + TWI_IRQ_MISO, + avr_twi_irq_msg(TWI_COND_ACK, p->selected, 1)); + } + } + /* + * If it's a data transaction, first check it is meant to be us (we + * received the correct address and are selected) + */ + if (p->selected) { + /* + * This is a write transaction, first receive as many address bytes + * as we need, then set the address register, then start + * writing data, + */ + if (v.u.twi.msg & TWI_COND_WRITE) { + // address size is how many bytes we use for address register + avr_raise_irq(p->irq + TWI_IRQ_MISO, + avr_twi_irq_msg(TWI_COND_ACK, p->selected, 1)); + int addr_size = p->size > 256 ? 2 : 1; + if (p->index < addr_size) { + p->reg_addr |= (v.u.twi.data << (p->index * 8)); + if (p->index == addr_size-1) { + // add the slave address, if relevant + p->reg_addr += ((p->selected & 1) - p->addr_base) << 7; + if (p->verbose) + printf("eeprom set address to 0x%04x\n", p->reg_addr); + } + } else { + if (p->verbose) + printf("eeprom WRITE data 0x%04x: %02x\n", p->reg_addr, v.u.twi.data); + p->ee[p->reg_addr++] = v.u.twi.data; + } + p->reg_addr &= (p->size -1); + p->index++; + } + /* + * It's a read transaction, just send the next byte back to the master + */ + if (v.u.twi.msg & TWI_COND_READ) { + if (p->verbose) + printf("eeprom READ data 0x%04x: %02x\n", p->reg_addr, p->ee[p->reg_addr]); + uint8_t data = p->ee[p->reg_addr++]; + avr_raise_irq(p->irq + TWI_IRQ_MISO, + avr_twi_irq_msg(TWI_COND_READ, p->selected, data)); + p->reg_addr &= (p->size -1); + p->index++; + } + } +} + +static const char * _ee_irq_names[2] = { + [TWI_IRQ_MISO] = "8>eeprom.out", + [TWI_IRQ_MOSI] = "32ee, 0xff, sizeof(p->ee)); + p->irq = avr_alloc_irq(&avr->irq_pool, 0, 2, _ee_irq_names); + avr_irq_register_notify(p->irq + TWI_IRQ_MOSI, i2c_eeprom_in_hook, p); + + p->size = size > sizeof(p->ee) ? sizeof(p->ee) : size; + if (data) + memcpy(p->ee, data, p->size); +} + +void +i2c_eeprom_attach( + struct avr_t * avr, + i2c_eeprom_t * p, + uint32_t i2c_irq_base ) +{ + // "connect" the IRQs of the eeprom to the TWI/i2c master of the AVR + avr_connect_irq( + p->irq + TWI_IRQ_MISO, + avr_io_getirq(avr, i2c_irq_base, TWI_IRQ_MISO)); + avr_connect_irq( + avr_io_getirq(avr, i2c_irq_base, TWI_IRQ_MOSI), + p->irq + TWI_IRQ_MOSI ); +} diff --git a/examples/parts/i2c_eeprom.h b/examples/parts/i2c_eeprom.h new file mode 100644 index 0000000..5fe2ba9 --- /dev/null +++ b/examples/parts/i2c_eeprom.h @@ -0,0 +1,91 @@ +/* + i2c_eeprom.h + + Copyright 2008, 2009 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + + +#ifndef __I2C_EEPROM_H___ +#define __I2C_EEPROM_H___ + +#include "sim_irq.h" + +/* + * This is a generic i2c eeprom; it can be up to 4096 bytes, and can work + * in two modes : + * 1) ONE slave address, and either one or two bytes sent on i2c to specify + * the byte to read/write. + * So a transaction looks like: + * [] [] + * 2) Multiple slave address to specify the high byte offset value, and one + * byte offset sent + * So a transaction looks like: + * [] + * + * these two modes seem to cover many eeproms + */ +typedef struct i2c_eeprom_t { + avr_irq_t * irq; // irq list + uint8_t addr_base; + uint8_t addr_mask; + int verbose; + + uint8_t selected; // selected address + int index; // byte index in current transaction + + uint16_t reg_addr; // read/write address register + int size; // also implies the address size, one or two byte + uint8_t ee[4096]; +} i2c_eeprom_t; + +/* + * Initializes an eeprom. + * + * The address is the TWI/i2c address base, for example 0xa0 -- the 7 MSB are + * relevant, the bit zero is always meant as the "read write" bit. + * The "mask" parameter specifies which bits should be matched as a slave; + * if you want to have a peripheral that handle read and write, use '1'; if you + * want to also match several addresses on the bus, specify these bits on the + * mask too. + * Example: + * Address 0xa1 mask 0x00 will match address 0xa0 in READ only + * Address 0xa0 mask 0x01 will match address 0xa0 in read AND write mode + * Address 0xa0 mask 0x03 will match 0xa0 0xa2 in read and write mode + * + * The "data" is optional, data is initialized as 0xff like a normal eeprom. + */ +void +i2c_eeprom_init( + struct avr_t * avr, + i2c_eeprom_t * p, + uint8_t addr, + uint8_t mask, + uint8_t * data, + size_t size); + +/* + * Attach the eeprom to the AVR's TWI master code, + * pass AVR_IOCTL_TWI_GETIRQ(0) for example as i2c_irq_base + */ +void +i2c_eeprom_attach( + struct avr_t * avr, + i2c_eeprom_t * p, + uint32_t i2c_irq_base ); + +#endif /* __I2C_EEPROM_H___ */