Do not rely on Clockstretching for writes

This commit is contained in:
Olaf Rempel 2020-02-02 00:03:09 +01:00
parent 5bdbb430e7
commit 964c933bf3
2 changed files with 81 additions and 13 deletions

View File

@ -10,10 +10,10 @@ Currently the following AVR MCUs are supported:
AVR MCU | Flash bytes used (.text + .data) | Bootloader region size
--- | --- | ---
atmega8 | 718 (0x2CE) | 512 words
atmega88 | 740 (0x2E4) | 512 words
atmega168 | 740 (0x2E4) | 512 words
atmega328p | 740 (0x2E4) | 512 words
atmega8 | 802 (0x322) | 512 words
atmega88 | 826 (0x33A) | 512 words
atmega168 | 826 (0x33A) | 512 words
atmega328p | 826 (0x33A) | 512 words
(Compiled on Ubuntu 18.04 LTS (gcc 5.4.0 / avr-libc 2.0.0) with EEPROM and LED support)
@ -74,7 +74,7 @@ Read chip info | **SLA+W**, 0x02, 0x00, 0x00, 0x00, **SLA+R**, {8 bytes}, **STO*
Read 1+ flash bytes | **SLA+W**, 0x02, 0x01, addrh, addrl, **SLA+R**, {* bytes}, **STO** |
Read 1+ eeprom bytes | **SLA+W**, 0x02, 0x02, addrh, addrl, **SLA+R**, {* bytes}, **STO** |
Write one flash page | **SLA+W**, 0x02, 0x01, addrh, addrl, {* bytes}, **STO** | page size as indicated in chip info
Write 1+ eeprom bytes | **SLA+W**, 0x02, 0x02, addrh, addrl, {* bytes}, **STO** |
Write 1+ eeprom bytes | **SLA+W**, 0x02, 0x02, addrh, addrl, {* bytes}, **STO** | write 0 < n < page size bytes at once
**SLA+R** means Start Condition, Slave Address, Read Access
@ -82,16 +82,28 @@ Write 1+ eeprom bytes | **SLA+W**, 0x02, 0x02, addrh, addrl, {* bytes}, **STO**
**STO** means Stop Condition
A flash page / eeprom write is only triggered after the Stop Condition.
During the write process twiboot will NOT acknowledge its slave address.
The multiboot_tool repository contains a simple linux application that uses
this protocol to access the bootloader over linux i2c device.
The ispprog programming adapter can also be used as a avr910/butterfly to twiboot protocol bridge.
## TWI/I2C Clockstretching ##
While a write is in progress twiboot will not respond on the TWI/I2C bus and the
TWI/I2C master needs to retry/poll the slave address until the write has completed.
As a compile time option (USE_CLOCKSTRETCH) the previous behavior of twiboot can be restored:
TWI/I2C Clockstretching is then used to inform the master of the duration of the write.
Please note that there are some TWI/I2C masters that do not support clockstretching.
## Development ##
Issue reports, feature requests, patches or simply success stories are much appreciated.
## Roadmap ##
Some ideas that I want to investigate / implement in twiboot:
- find a way to not rely on TWI clock stretching during write access
- support AVR TINYs (USI peripheral, no bootloader fuse, no Read-While-Write flash)

70
main.c
View File

@ -24,6 +24,7 @@
#define VERSION_STRING "TWIBOOT v2.1"
#define EEPROM_SUPPORT 1
#define LED_SUPPORT 1
#define USE_CLOCKSTRETCH 0
#define F_CPU 8000000ULL
#define TIMER_DIVISOR 1024
@ -63,6 +64,8 @@
#define CMD_ACCESS_CHIPINFO (0x10 | CMD_ACCESS_MEMORY)
#define CMD_ACCESS_FLASH (0x20 | CMD_ACCESS_MEMORY)
#define CMD_ACCESS_EEPROM (0x30 | CMD_ACCESS_MEMORY)
#define CMD_WRITE_FLASH_PAGE (0x40 | CMD_ACCESS_MEMORY)
#define CMD_WRITE_EEPROM_PAGE (0x50 | CMD_ACCESS_MEMORY)
/* SLA+W */
#define CMD_SWITCH_APPLICATION CMD_READ_VERSION
@ -198,6 +201,22 @@ static void write_eeprom_byte(uint8_t val)
eeprom_busy_wait();
} /* write_eeprom_byte */
#if (USE_CLOCKSTRETCH == 0)
/* *************************************************************************
* write_eeprom_buffer
* ************************************************************************* */
static void write_eeprom_buffer(uint8_t size)
{
uint8_t *p = buf;
while (size--)
{
write_eeprom_byte(*p++);
}
} /* write_eeprom_buffer */
#endif /* (USE_CLOCKSTRETCH == 0) */
#endif /* EEPROM_SUPPORT */
@ -279,6 +298,19 @@ static uint8_t TWI_data_write(uint8_t bcnt, uint8_t data)
default:
switch (cmd)
{
#if (EEPROM_SUPPORT)
#if (USE_CLOCKSTRETCH)
case CMD_ACCESS_EEPROM:
write_eeprom_byte(data);
break;
#else
case CMD_ACCESS_EEPROM:
cmd = CMD_WRITE_EEPROM_PAGE;
/* fall through */
case CMD_WRITE_EEPROM_PAGE:
#endif /* (USE_CLOCKSTRETCH) */
#endif /* (EEPROM_SUPPORT) */
case CMD_ACCESS_FLASH:
{
uint8_t pos = bcnt -4;
@ -289,19 +321,19 @@ static uint8_t TWI_data_write(uint8_t bcnt, uint8_t data)
ack = 0x00;
}
if (pos >= (sizeof(buf) -1))
if ((cmd == CMD_ACCESS_FLASH) &&
(pos >= (sizeof(buf) -1))
)
{
#if (USE_CLOCKSTRETCH)
write_flash_page();
#else
cmd = CMD_WRITE_FLASH_PAGE;
#endif
}
break;
}
#if (EEPROM_SUPPORT)
case CMD_ACCESS_EEPROM:
write_eeprom_byte(data);
break;
#endif /* (EEPROM_SUPPORT) */
default:
ack = 0x00;
break;
@ -392,6 +424,30 @@ static void TWI_vect(void)
/* STOP or repeated START -> IDLE */
case 0xA0:
#if (USE_CLOCKSTRETCH == 0)
if ((cmd == CMD_WRITE_FLASH_PAGE)
#if (EEPROM_SUPPORT)
|| (cmd == CMD_WRITE_EEPROM_PAGE)
#endif
)
{
/* disable ACK for now, re-enable after page write */
control &= ~(1<<TWEA);
TWCR = (1<<TWINT) | control;
#if (EEPROM_SUPPORT)
if (cmd == CMD_WRITE_EEPROM_PAGE)
{
write_eeprom_buffer(bcnt -4);
}
else
#endif /* (EEPROM_SUPPORT) */
{
write_flash_page();
}
}
#endif /* (USE_CLOCKSTRETCH) */
bcnt = 0;
/* fall through */