Implement a virtual bootloader section

- patch reset vector while programming flash page0
  Let the reset vector always point to the twiboot start.
- Use another (must be unused!) ISR vector to store the original
  reset vector as jump to the application.
- Cache the values for the verification read
- currently works only for devices < 8kB flash (2 byte vector entries)
- using a attiny85 as target

This change is heavily based on the optiboot bootloader.
This commit is contained in:
Olaf Rempel 2020-10-26 18:54:00 +01:00
parent 9f3781a3eb
commit f4952aaa63
2 changed files with 91 additions and 3 deletions

View File

@ -67,7 +67,7 @@ AVRDUDE_MCU=t85
AVRDUDE_FUSES=lfuse:w:0xe2:m hfuse:w:0xdd:m efuse:w:0xfe:m
BOOTLOADER_START=0x1C00
CFLAGS_TARGET=-DUSE_CLOCKSTRETCH=1
CFLAGS_TARGET=-DUSE_CLOCKSTRETCH=1 -DVIRTUAL_BOOT_SECTION=1
endif
# ---------------------------------------------------------------------------

92
main.c
View File

@ -29,6 +29,10 @@
#define USE_CLOCKSTRETCH 0
#endif
#ifndef VIRTUAL_BOOT_SECTION
#define VIRTUAL_BOOT_SECTION 0
#endif
#ifndef TWI_ADDRESS
#define TWI_ADDRESS 0x29
#endif
@ -86,6 +90,23 @@
#define USI_ENABLE_SCL_HOLD 0x40 /* Hold SCL low after clock overflow */
#endif /* !defined(TWCR) && defined(USICR) */
#if (VIRTUAL_BOOT_SECTION)
/* unused vector to store application start address */
#define APPVECT_NUM EE_RDY_vect_num
/* each vector table entry is a 2byte RJMP opcode */
#define RSTVECT_ADDR 0x0000
#define APPVECT_ADDR (APPVECT_NUM * 2)
#define RSTVECT_PAGE_OFFSET (RSTVECT_ADDR % SPM_PAGESIZE)
#define APPVECT_PAGE_OFFSET (APPVECT_ADDR % SPM_PAGESIZE)
/* create RJMP opcode for the vector table */
#define OPCODE_RJMP(addr) (((addr) & 0x0FFF) | 0xC000)
#elif (!defined(ASRE) && !defined (RWWSRE))
#error "Device without bootloader section requires VIRTUAL_BOOT_SECTION"
#endif
/* SLA+R */
#define CMD_WAIT 0x00
#define CMD_READ_VERSION 0x01
@ -165,6 +186,12 @@ static uint8_t cmd = CMD_WAIT;
static uint8_t buf[SPM_PAGESIZE];
static uint16_t addr;
#if (VIRTUAL_BOOT_SECTION)
/* reset/application vectors received from host, needed for verify read */
static uint8_t rstvect_save[2];
static uint8_t appvect_save[2];
#endif /* (VIRTUAL_BOOT_SECTION) */
/* *************************************************************************
* write_flash_page
* ************************************************************************* */
@ -174,6 +201,29 @@ static void write_flash_page(void)
uint8_t size = SPM_PAGESIZE;
uint8_t *p = buf;
#if (VIRTUAL_BOOT_SECTION)
if (pagestart == (RSTVECT_ADDR & ~(SPM_PAGESIZE -1)))
{
/* save original vectors for verify read */
rstvect_save[0] = buf[RSTVECT_PAGE_OFFSET];
rstvect_save[1] = buf[RSTVECT_PAGE_OFFSET + 1];
appvect_save[0] = buf[APPVECT_PAGE_OFFSET];
appvect_save[1] = buf[APPVECT_PAGE_OFFSET + 1];
/* replace reset vector with jump to bootloader address */
uint16_t rst_vector = OPCODE_RJMP(BOOTLOADER_START -1);
buf[RSTVECT_PAGE_OFFSET] = (rst_vector & 0xFF);
buf[RSTVECT_PAGE_OFFSET + 1] = (rst_vector >> 8) & 0xFF;
/* replace application vector with jump to original reset vector */
uint16_t app_vector = rstvect_save[0] | (rstvect_save[1] << 8);
app_vector = OPCODE_RJMP(app_vector - APPVECT_NUM);
buf[APPVECT_PAGE_OFFSET] = (app_vector & 0xFF);
buf[APPVECT_PAGE_OFFSET + 1] = (app_vector >> 8) & 0xFF;
}
#endif /* (VIRTUAL_BOOT_SECTION) */
if (pagestart < BOOTLOADER_START)
{
boot_page_erase(pagestart);
@ -399,7 +449,33 @@ static uint8_t TWI_data_read(uint8_t bcnt)
break;
case CMD_ACCESS_FLASH:
data = pgm_read_byte_near(addr++);
switch (addr)
{
/* return cached values for verify read */
#if (VIRTUAL_BOOT_SECTION)
case RSTVECT_ADDR:
data = rstvect_save[0];
break;
case (RSTVECT_ADDR + 1):
data = rstvect_save[1];
break;
case APPVECT_ADDR:
data = appvect_save[0];
break;
case (APPVECT_ADDR + 1):
data = appvect_save[1];
break;
#endif /* (VIRTUAL_BOOT_SECTION) */
default:
data = pgm_read_byte_near(addr);
break;
}
addr++;
break;
#if (EEPROM_SUPPORT)
@ -680,7 +756,11 @@ static void TIMER0_OVF_vect(void)
} /* TIMER0_OVF_vect */
static void (*jump_to_app)(void) __attribute__ ((noreturn)) = 0x0000;
#if (VIRTUAL_BOOT_SECTION)
static void (*jump_to_app)(void) __attribute__ ((noreturn)) = (void*)APPVECT_ADDR;
#else
static void (*jump_to_app)(void) __attribute__ ((noreturn)) = (void*)0x0000;
#endif
/* *************************************************************************
@ -731,6 +811,14 @@ int main(void)
LED_INIT();
LED_GN_ON();
#if (VIRTUAL_BOOT_SECTION)
/* load current values (for reading flash) */
rstvect_save[0] = pgm_read_byte_near(RSTVECT_ADDR);
rstvect_save[1] = pgm_read_byte_near(RSTVECT_ADDR + 1);
appvect_save[0] = pgm_read_byte_near(APPVECT_ADDR);
appvect_save[1] = pgm_read_byte_near(APPVECT_ADDR + 1);
#endif /* (VIRTUAL_BOOT_SECTION) */
/* timer0: running with F_CPU/1024 */
#if defined (TCCR0)
TCCR0 = (1<<CS02) | (1<<CS00);