avrboot/main.c

544 lines
16 KiB
C
Raw Normal View History

2006-05-01 19:10:39 +02:00
/*****************************************************************************
*
* AVRPROG compatible boot-loader
2006-05-01 19:12:13 +02:00
* Version : 0.4 (6. Apr. 2004)
2006-05-01 19:10:39 +02:00
* Compiler : avr-gcc 3.3.1 / avr-libc 1.0
2006-05-01 19:12:13 +02:00
* size : ( larger than 512 words :-( )
2006-05-01 19:10:39 +02:00
* by : Martin Thomas, Kaiserslautern, Germany
* eversmith@heizung-thomas.de
*
2006-05-01 19:12:13 +02:00
* License : none. free code and free to use and modify
* BUT: Please send me bug-reports if you find out that
* something has been done wrong. You may mention where
* you've got the source in the documention of your
* project if you're using this bootloader.
*
* - based on the Butterfly Bootloader-Code
* Copyright (C) 1996-1998 Atmel Corporation
* Author(s) : BBrandal, PKastnes, ARodland, LHM
* The orignal code has been made available by ATMEL together with the
* Butterfly application code. Since ATMEL.NO had no problem with
* the application gcc-port they hopefully will not have any concerns about
* publishing this port. A lot of things have been change but the ATMEL
* "skeleton" is still in this code. Make sure to keep the copyright notice
* in derived work to avoid trouble.
*
* - based on boot.h from the avr-libc (c) Eric Weddington
2006-05-01 19:10:39 +02:00
*
****************************************************************************
*
* Many functions used by "AVRPROG" (fuses) have been disabled by ATMEL in
* the original source code of the Butterfly Boot-loader not by me.
2006-05-01 19:12:13 +02:00
* I will try to make all of them available in later versions but i.e the
* ATmega169 is not completly supported by AVRPROG 1.37.
2006-05-01 19:10:39 +02:00
*
* The boot interrupt vector is included (this bootloader is completly in
* ".text" section). If you need this space for further functions you have to
* add a separate section for the bootloader-functions and add an attribute
* for this section to _all_ function prototypes of functions in the loader.
* With this the interrupt vector will be placed at .0000 and the bootloader
* code (without interrupt vector) at the adress you define in the linker
2006-05-01 19:12:13 +02:00
* options for the newly created section. See the avr-libc FAQ, the avr-
* libc's avr/boot.h documentation and the makefile for further details.
2006-05-01 19:10:39 +02:00
*
2006-05-01 19:12:13 +02:00
* Sorry, so far efforts to shrink to 512 words failed.
2006-05-01 19:10:39 +02:00
* See the makefile for information how to adopt the linker-settings to
* the selected Boot Size (_Bxxx below)
*
****************************************************************************/
2006-05-01 19:12:13 +02:00
/*
Does not work so far:
- fuse high byte read (or parse in AVRPROG?)
- lock bits set
*/
2006-05-01 19:10:39 +02:00
2006-05-01 19:12:13 +02:00
// programmers-notepad tabsize 4
2006-05-01 19:10:39 +02:00
2006-05-01 19:12:13 +02:00
#include <inttypes.h>
2006-05-01 19:10:39 +02:00
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
2006-05-01 19:12:13 +02:00
/* BOOTICE-Mode - to flash the JTAGICE upgrade.ebn file.
No startup-sequence in this mode. Jump directly to the
parser-loop on reset
XTAL in BOOTICEMODE must be 7372800 Hz to be compatible
with the org. JTAGICE-Firmware */
// #define BOOTICEMODE
2006-05-01 19:10:39 +02:00
2006-05-01 19:12:13 +02:00
// UART handling - some definitions from P. Fleury's Library - thanks
2006-05-01 19:10:39 +02:00
#define BAUDRATE 19200
2006-05-01 19:12:13 +02:00
#ifndef BOOTICEMODE
2006-05-01 19:10:39 +02:00
#define XTAL 3686400
2006-05-01 19:12:13 +02:00
#else
#warning "BOOTICE mode - External Crystal/Oszillator must be 7,3728 MHz"
#define XTAL 7372800
#endif
#include "uart.h"
/* enable/disable readout of fuse and lock-bits
(will not work for Mega169 since not supported by AVRPROG 1.37 */
#define ENABLEREADFUSELOCK
/* enable/disable write of lock-bits
WARNING: lock-bits can not be reseted by bootloader (as far as I know)
Only protection no unprotection, "chip erase" from bootloader only
clears the flash but does no real "chip erase" (this is not possible
with a bootloader as far as I know)
*/
// #define WRITELOCKBITS
2006-05-01 19:10:39 +02:00
/* Select Boot Size (select one, comment out the others)
select at least _B1024 */
// NO! #define _B128
// NO! #define _B256
// NO! #define _B512
#define _B1024
//#define _B2048
#include "chipdef.h"
2006-05-01 19:12:13 +02:00
// this uses the latest version from avr-libc CVS not the one that comes
// with WINAVR Sep./03 (6.Apr.04: it's like the boot.h from the Apr/04
// WINAVR but the lock-bit-mask has been changed. Found out during the
// development of a STK500-plugin-compatible-bootloader)
#include "myboot.h"
// functions not found in boot.h
2006-05-01 19:10:39 +02:00
#include "lowlevel.h"
2006-05-01 19:12:13 +02:00
#define UART_RX_BUFFER_SIZE SPM_PAGESIZE
unsigned char gBuffer[UART_RX_BUFFER_SIZE];
#define eeprom_is_ready() bit_is_clear(EECR, EEWE)
#define my_eeprom_busy_wait() do{}while(!eeprom_is_ready())
2006-05-01 19:10:39 +02:00
unsigned char BufferLoad(unsigned int , unsigned char ) ;
void BlockRead(unsigned int , unsigned char ) ;
2006-05-01 19:12:13 +02:00
unsigned short address;
2006-05-01 19:10:39 +02:00
unsigned char device;
void send_boot(void)
{
sendchar('A');
sendchar('V');
sendchar('R');
sendchar('B');
sendchar('O');
sendchar('O');
sendchar('T');
}
int main(void)
{
void (*funcptr)( void ) = 0x0000; // Set up function pointer
2006-05-01 19:12:13 +02:00
unsigned short tempi;
2006-05-01 19:10:39 +02:00
char val;
char OK = 1;
2006-05-01 19:12:13 +02:00
MCUCR = (1<<IVCE);
2006-05-01 19:10:39 +02:00
MCUCR = (1<<IVSEL); //move interruptvectors to the Boot sector
2006-05-01 19:12:13 +02:00
USART_Init(UART_BAUD_SELECT(BAUDRATE,XTAL),UARTSINGLE); // single speed
// USART_Init(UART_BAUD_SELECT(BAUDRATE/2,XTAL),UARTDOUBLE); // double speed
/*
This is an adoption of the Butterfly Bootloader startup-sequence.
It may look a little strange but separating the login-loop from
the main parser-loop gives a lot a possibilities (timeout, sleep-modes
etc.).
2006-05-01 19:10:39 +02:00
*/
2006-05-01 19:12:13 +02:00
#ifndef BOOTICEMODE
PORTA = 0xFF; // Enable pullups on Port A
2006-05-01 19:10:39 +02:00
for(;OK;)
{
if((PINA & (1<<PINA7)))
{
2006-05-01 19:12:13 +02:00
// jump to main app if PIN A7 is not grounded
PORTA = 0x00; // set to default
2006-05-01 19:10:39 +02:00
MCUCR = (1<<IVCE);
MCUCR = (0<<IVSEL); //move interruptvectors to the Application sector
funcptr(); // Jump to application sector
}
else
{
val = recchar();
if( val == 0x1B)
{ // AVRPROG connection
while (val != 'S') // Wait for signon
{
val = recchar();
}
send_boot(); // Report signon
OK = 0;
}
else
sendchar('?');
}
2006-05-01 19:12:13 +02:00
}
#else
#warning "BOOTICE mode - no startup-condition"
#endif
2006-05-01 19:10:39 +02:00
for(;;)
{
val=recchar();
if(val=='a') //Autoincrement?
{
sendchar('Y'); //Autoincrement is quicker
}
else if(val=='A') //write address
{
address=recchar(); //read address 8 MSB
address=(address<<8)|recchar();
address=address<<1; //convert from word address to byte address
sendchar('\r');
}
else if(val=='b')
{ // Buffer load support
sendchar('Y'); // Report buffer load supported
sendchar((UART_RX_BUFFER_SIZE >> 8) & 0xFF);
// Report buffer size in bytes
sendchar(UART_RX_BUFFER_SIZE & 0xFF);
}
else if(val=='B') // Start buffer load
{
tempi = recchar() << 8; // Load high byte of buffersize
tempi |= recchar(); // Load low byte of buffersize
val = recchar(); // Load memory type ('E' or 'F')
sendchar (BufferLoad(tempi,val));
// Start downloading of buffer
}
else if(val == 'g') // Block read
{
tempi = (recchar() << 8) | recchar();
val = recchar(); // Get memtype
BlockRead(tempi,val); // Perform the block read
}
/*
else if(val=='c') //Write program memory, low byte
{
ldata=recchar();
sendchar('\r');
}
else if(val== 'C') //Write program memory, high byte
{
data=ldata|(recchar()<<8);
if (device == devtype)
{
fill_temp_buffer(data,(address)); //call asm routine.
}
address=address+2;
sendchar('\r');
}
*/
else if(val=='e') //Chip erase
{
if (device == devtype)
{
2006-05-01 19:12:13 +02:00
// erase only main section (bootloader protection)
for(address=0;address < APP_END;address += SPM_PAGESIZE) //Application section = 60 pages
2006-05-01 19:10:39 +02:00
{
2006-05-01 19:12:13 +02:00
//write_page(address,(1<<PGERS) + (1<<SPMEN)); //Perform page erase
boot_page_erase(address);
boot_spm_busy_wait(); // Wait until the memory is erased.
2006-05-01 19:10:39 +02:00
}
}
2006-05-01 19:12:13 +02:00
// write_page(address,(1<<RWWSRE) + (1<<SPMEN)); //Re-enable the RWW section
boot_rww_enable();
2006-05-01 19:10:39 +02:00
sendchar('\r');
}
else if(val=='E') //Exit upgrade
{
// WDTCR = (1<<WDTCE) | (1<<WDE); //Enable Watchdog Timer to give reset
wdt_enable(WDTO_15MS);
sendchar('\r');
}
2006-05-01 19:12:13 +02:00
#ifdef WRITELOCKBITS
#warning "Extension 'WriteLockBits' enabled"
// TODO: does not work
2006-05-01 19:10:39 +02:00
else if(val=='l') // write lockbits
{
if (device == devtype)
{
2006-05-01 19:12:13 +02:00
// write_lock_bits(recchar());
boot_lock_bits_set(recchar()); // boot.h takes care of mask
boot_spm_busy_wait();
2006-05-01 19:10:39 +02:00
}
sendchar('\r');
}
2006-05-01 19:12:13 +02:00
#endif
/*
2006-05-01 19:10:39 +02:00
else if(val== 'm') // write page
{
if (device == devtype)
{
write_page(address,(1<<PGERS) + (1<<SPMEN)); //Perform page erase
write_page((address),0x05);
write_page(address,(1<<RWWSRE) + (1<<SPMEN)); //Re-enable the RWW section
}
sendchar('\r');
}
*/
else if(val=='P') // Enter programming mode
{
sendchar('\r');
}
else if(val=='L') // Leave programming mode
{
sendchar('\r');
}
else if (val=='p') // mt: return programmer type
{
2006-05-01 19:12:13 +02:00
sendchar('S'); // always serial programmer
2006-05-01 19:10:39 +02:00
}
/*
else if(val=='R') //Read program memory
{
write_page(0,(1<<RWWSRE) + (1<<SPMEN)); //Re-enable the RWW section
// SPMCSR = (1<<RWWSRE) | (1<<SPMEN);
// __store_program_memory();
// while((SPMCSR & 0x01));
intval=read_program_memory(address,0x00);
sendchar((char)(intval>>8)); //send MSB
sendchar((char)intval); //send LSB
address=address+2;
}
2006-05-01 19:12:13 +02:00
*/
/*
2006-05-01 19:10:39 +02:00
else if (val == 'D') // write EEPROM
{
if (device == devtype)
{
EEARL = address;
EEARH = (address >> 8);
address++;
EEDR = recchar();
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
while (EECR & (1<<EEWE))
;
}
sendchar('\r');
}
2006-05-01 19:12:13 +02:00
*/
/*
2006-05-01 19:10:39 +02:00
else if (val == 'd') // read eeprom
{
EEARL = address;
EEARH = (address >> 8);
address++;
EECR |= (1<<EERE);
sendchar(EEDR);
}
*/
#ifdef ENABLEREADFUSELOCK
#warning "Extension 'ReadFuseLock' enabled"
else if(val=='F') // read fuse bits
{
2006-05-01 19:12:13 +02:00
sendchar((unsigned char) read_program_memory(0x0000,_BV(BLBSET)|_BV(SPMEN))); // 0x09 for (1<<BLBSET)|(1<<SPMEN)
2006-05-01 19:10:39 +02:00
}
else if(val=='r') // read lock bits
{
2006-05-01 19:12:13 +02:00
sendchar((unsigned char) read_program_memory(0x0001,_BV(BLBSET)|_BV(SPMEN)));
2006-05-01 19:10:39 +02:00
}
else if(val=='N') // read high fuse bits
2006-05-01 19:12:13 +02:00
// TODO: does not work
2006-05-01 19:10:39 +02:00
{
2006-05-01 19:12:13 +02:00
sendchar((unsigned char) read_program_memory(0x0003,_BV(BLBSET)|_BV(SPMEN)));
2006-05-01 19:10:39 +02:00
}
else if(val=='Q') // read extended fuse bits
{
2006-05-01 19:12:13 +02:00
sendchar('?'); // TODO see ATmega128 datasheet
2006-05-01 19:10:39 +02:00
}
#endif
// end of ENABLEREADFUSELOCK section
else if(val=='t') // Return programmer type
{
sendchar(devtype);
sendchar(0);
}
else if ((val=='x')||(val=='y')) // clear and set LED ignored
{
recchar();
sendchar('\r');
}
else if (val=='T') // set device/programmer type in bootloader (?)
{
device = recchar();
sendchar('\r');
}
else if (val=='S') // Return software identifier
{
send_boot();
}
else if (val=='V') // Return Software Version
{
2006-05-01 19:12:13 +02:00
sendchar('0');
sendchar('4');
2006-05-01 19:10:39 +02:00
}
else if (val=='s') // Return Signature Byte
{
sendchar(sig_byte1);
sendchar(sig_byte2);
sendchar(sig_byte3);
}
else if(val!=0x1b) // if not esc
{
sendchar('?');
}
2006-05-01 19:12:13 +02:00
} // end of "parser" for-loop
2006-05-01 19:10:39 +02:00
return 0;
}
unsigned char BufferLoad(unsigned int size, unsigned char mem)
{
2006-05-01 19:12:13 +02:00
unsigned int data, tempaddress, cnt;
unsigned char sreg;
2006-05-01 19:10:39 +02:00
2006-05-01 19:12:13 +02:00
for (cnt=0; cnt<UART_RX_BUFFER_SIZE; cnt++) {
if (cnt<size) gBuffer[cnt]=recchar();
else gBuffer[cnt]=0xFF;
}
cnt=0;
2006-05-01 19:10:39 +02:00
tempaddress = address; // Store address in page
2006-05-01 19:12:13 +02:00
my_eeprom_busy_wait();
2006-05-01 19:10:39 +02:00
if (device == devtype)
{
2006-05-01 19:12:13 +02:00
// Disable interrupts.
sreg = SREG;
cli();
if (mem == 'F') // Flash
2006-05-01 19:10:39 +02:00
{
do {
2006-05-01 19:12:13 +02:00
// data = recchar();
// data |= (recchar() << 8);
// fill_temp_buffer(data,(address));
data=gBuffer[cnt++];
data|=(gBuffer[cnt++]<<8);
boot_page_fill(address,data);
2006-05-01 19:10:39 +02:00
//call asm routine.
address=address+2; // Select next word in memory
size -= 2; // Reduce number of bytes to write by two
} while(size); // Loop until all bytes written
tempaddress &= 0xFF80; // Ensure the address points to the first byte in the page
2006-05-01 19:12:13 +02:00
//write_page((tempaddress),0x05); // Program page contents
boot_page_write(tempaddress);
boot_spm_busy_wait();
// write_page(tempaddress,(1<<RWWSRE) + (1<<SPMEN));
boot_rww_enable(); //Re-enable the RWW section
2006-05-01 19:10:39 +02:00
if (address != (address & 0xFF80))
{ // Ensure that the address points to the beginning of the next page
address &= 0xFF80;
2006-05-01 19:12:13 +02:00
address += SPM_PAGESIZE;
2006-05-01 19:10:39 +02:00
}
} // End FLASH
if (mem == 'E') // Start EEPROM
{
do {
EEARL = address; // Setup EEPROM address
EEARH = (address >> 8);
2006-05-01 19:12:13 +02:00
address++; // Select next byte
// EEDR = recchar(); // Load data to write
EEDR=gBuffer[cnt++];
2006-05-01 19:10:39 +02:00
EECR |= (1<<EEMWE); // Write data into EEPROM
EECR |= (1<<EEWE);
2006-05-01 19:12:13 +02:00
while (EECR & (1<<EEWE)); // Wait for EEPROM write to finish
2006-05-01 19:10:39 +02:00
size--; // Decreas number of bytes to write
} while(size); // Loop until all bytes written
2006-05-01 19:12:13 +02:00
2006-05-01 19:10:39 +02:00
}
2006-05-01 19:12:13 +02:00
// Re-enable interrupts (if they were ever enabled).
SREG = sreg;
return '\r'; // Report programming OK
2006-05-01 19:10:39 +02:00
}
2006-05-01 19:12:13 +02:00
2006-05-01 19:10:39 +02:00
return 0; // Report programming failed
}
void BlockRead(unsigned int size, unsigned char mem)
{
unsigned int data;
2006-05-01 19:12:13 +02:00
unsigned char sreg;
// Disable interrupts.
sreg = SREG;
cli();
my_eeprom_busy_wait();
2006-05-01 19:10:39 +02:00
if (mem == 'E') // Read EEPROM
{
do {
EEARL = address; // Setup EEPROM address
EEARH = (address >> 8);
address++; // Select next EEPROM byte
EECR |= (1<<EERE); // Read EEPROM
sendchar(EEDR); // Transmit EEPROM data to PC
size--; // Decrease number of bytes to read
} while (size); // Repeat until all block has been read
}
else // Read Flash
{
do {
data = read_program_memory(address,0x00);
sendchar((char)data); //send LSB
sendchar((char)(data >> 8)); //send MSB
address += 2; // Select next word in memory
size -= 2; // Subtract two bytes from number of bytes to read
} while (size); // Repeat until all block has been read
}
2006-05-01 19:12:13 +02:00
// Re-enable interrupts (if they were ever enabled).
SREG=sreg;
2006-05-01 19:10:39 +02:00
}