AVRPROG compatible boot-loader Version 85 Dec 2008 Compiler avr-gcc av

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
/*****************************************************************************
*
* AVRPROG compatible boot-loader
* Version : 0.85 (Dec. 2008)
* Compiler : avr-gcc 4.1.2 / avr-libc 1.4.6
* size : depends on features and startup ( minmal features < 512 words)
* by : Martin Thomas, Kaiserslautern, Germany
* eversmith@heizung-thomas.de
* Additional code and improvements contributed by:
* - Uwe Bonnes
* - Bjoern Riemer
* - Olaf Rempel
*
* License : Copyright (c) 2006-2008 M. Thomas, U. Bonnes, O. Rempel
* Free to use. You have to mention the copyright
* owners in source-code and documentation of derived
* work. No warranty! (Yes, you can insert the BSD
* license here)
*
* Tested with ATmega8, ATmega16, ATmega162, ATmega32, ATmega324P,
* ATmega644, ATmega644P, ATmega128, AT90CAN128
*
* - Initial versions have been based on the Butterfly bootloader-code
* by Atmel Corporation (Authors: BBrandal, PKastnes, ARodland, LHM)
*
****************************************************************************
*
* See the makefile and readme.txt for information on how to adapt
* the linker-settings to the selected Boot Size (BOOTSIZE=xxxx) and
* the MCU-type. Other configurations futher down in this file.
*
* With BOOT_SIMPLE, minimal features and discarded int-vectors
* this bootloader has should fit into a a 512 word (1024, 0x400 bytes)
* bootloader-section.
*
****************************************************************************/
/*
TODOs:
- check lock-bits set
- __bad_interrupt still linked even with modified
linker-scripts which needs a default-handler,
"wasted": 3 words for AVR5 (>8kB), 2 words for AVR4
- Check watchdog-disable-function in avr-libc.
*/
// tabsize: 4
/* MCU frequency */
#ifndef F_CPU
// #define F_CPU 7372800
#define F_CPU (7372800/2)
#endif
/* UART Baudrate */
// #define BAUDRATE 9600
// #define BAUDRATE 19200
#define BAUDRATE 115200
/* use "Double Speed Operation" */
//#define UART_DOUBLESPEED
/* use second UART on mega128 / can128 / mega162 / mega324p / mega644p */
//#define UART_USE_SECOND
/* Device-Type:
For AVRProg the BOOT-option is prefered
which is the "correct" value for a bootloader.
avrdude may only detect the part-code for ISP */
#define DEVTYPE DEVTYPE_BOOT
// #define DEVTYPE DEVTYPE_ISP
/*
* Pin "STARTPIN" on port "STARTPORT" in this port has to grounded
* (active low) to start the bootloader
*/
#define BLPORT PORTC
#define BLDDR DDRC
#define BLPIN PINC
#define BLPNUM PINC7
/*
* Define if Watchdog-Timer should be disable at startup
*/
#define DISABLE_WDT_AT_STARTUP
/*
* Watchdog-reset is issued at exit
* define the timeout-value here (see avr-libc manual)
*/
#define EXIT_WDT_TIME WDTO_250MS
/*
* Select startup-mode
* SIMPLE-Mode - Jump to bootloader main BL-loop if key is
* pressed (Pin grounded) "during" reset or jump to the
* application if the pin is not grounded. The internal
* pull-up resistor is enabled during the startup and
* gets disabled before the application is started.
* POWERSAVE-Mode - Startup is separated in two loops
* which makes power-saving a little easier if no firmware
* is on the chip. Needs more memory
* BOOTICE-Mode - to flash the JTAGICE upgrade.ebn file.
* No startup-sequence in this mode. Jump directly to the
* parser-loop on reset
* F_CPU in BOOTICEMODE must be 7372800 Hz to be compatible
* with the org. JTAGICE-Firmware
* WAIT-mode waits 1 sec for the defined character if nothing
* is recived then the user prog is started.
*/
#define START_SIMPLE
//#define START_WAIT
//#define START_POWERSAVE
//#define START_BOOTICE
/* character to start the bootloader in mode START_WAIT */
#define START_WAIT_UARTCHAR 'S'
/* wait-time for START_WAIT mode ( t = WAIT_TIME * 10ms ) */
#define WAIT_VALUE 100 /* here: 100*10ms = 1000ms = 1sec */
/*
* enable/disable readout of fuse and lock-bits
* (AVRPROG has to detect the AVR correctly by device-code
* to show the correct information).
*/
//#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)
* Keep this undefined!
*/
//#define WRITELOCKBITS
/*
* define the following if the bootloader should not output
* itself at flash read (will fake an empty boot-section)
*/
#define READ_PROTECT_BOOTLOADER
#define VERSION_HIGH '0'
#define VERSION_LOW '8'
#define GET_LOCK_BITS 0x0001
#define GET_LOW_FUSE_BITS 0x0000
#define GET_HIGH_FUSE_BITS 0x0003
#define GET_EXTENDED_FUSE_BITS 0x0002
#ifdef UART_DOUBLESPEED
// #define UART_CALC_BAUDRATE(baudRate) (((F_CPU*10UL) / ((baudRate) *8UL) +5)/10 -1)
#define UART_CALC_BAUDRATE(baudRate) ((uint32_t)((F_CPU) + ((uint32_t)baudRate * 4UL)) / ((uint32_t)(baudRate) * 8UL) - 1)
#else
// #define UART_CALC_BAUDRATE(baudRate) (((F_CPU*10UL) / ((baudRate)*16UL) +5)/10 -1)
#define UART_CALC_BAUDRATE(baudRate) ((uint32_t)((F_CPU) + ((uint32_t)baudRate * 8UL)) / ((uint32_t)(baudRate) * 16UL) - 1)
#endif
#include <stdint.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/boot.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "chipdef.h"
uint8_t gBuffer[SPM_PAGESIZE];
#if defined(BOOTLOADERHASNOVECTORS)
#warning "This Bootloader does not link interrupt vectors - see makefile"
/* make the linker happy - it wants to see __vector_default */
// void __vector_default(void) { ; }
void __vector_default(void) { ; }
#endif
static void sendchar(uint8_t data)
{
while (!(UART_STATUS & (1<<UART_TXREADY)));
UART_DATA = data;
}
static uint8_t recvchar(void)
{
while (!(UART_STATUS & (1<<UART_RXREADY)));
return UART_DATA;
}
static inline void eraseFlash(void)
{
// erase only main section (bootloader protection)
uint32_t addr = 0;
while (APP_END > addr) {
boot_page_erase(addr); // Perform page erase
boot_spm_busy_wait(); // Wait until the memory is erased.
addr += SPM_PAGESIZE;
}
boot_rww_enable();
}
static inline void recvBuffer(pagebuf_t size)
{
pagebuf_t cnt;
uint8_t *tmp = gBuffer;
for (cnt = 0; cnt < sizeof(gBuffer); cnt++) {
*tmp++ = (cnt < size) ? recvchar() : 0xFF;
}
}
static inline uint16_t writeFlashPage(uint16_t waddr, pagebuf_t size)
{
uint32_t pagestart = (uint32_t)waddr<<1;
uint32_t baddr = pagestart;
uint16_t data;
uint8_t *tmp = gBuffer;
do {
data = *tmp++;
data |= *tmp++ << 8;
boot_page_fill(baddr, data); // call asm routine.
baddr += 2; // Select next word in memory
size -= 2; // Reduce number of bytes to write by two
} while (size); // Loop until all bytes written
boot_page_write(pagestart);
boot_spm_busy_wait();
boot_rww_enable(); // Re-enable the RWW section
return baddr>>1;
}
static inline uint16_t writeEEpromPage(uint16_t address, pagebuf_t size)
{
uint8_t *tmp = gBuffer;
do {
eeprom_write_byte( (uint8_t*)address, *tmp++ );
address++; // Select next byte
size--; // Decreas number of bytes to write
} while (size); // Loop until all bytes written
// eeprom_busy_wait();
return address;
}
static inline uint16_t readFlashPage(uint16_t waddr, pagebuf_t size)
{
uint32_t baddr = (uint32_t)waddr<<1;
uint16_t data;
do {
#ifndef READ_PROTECT_BOOTLOADER
#warning "Bootloader not read-protected"
#if defined(RAMPZ)
data = pgm_read_word_far(baddr);
#else
data = pgm_read_word_near(baddr);
#endif
#else
// don't read bootloader
if ( baddr < APP_END ) {
#if defined(RAMPZ)
data = pgm_read_word_far(baddr);
#else
data = pgm_read_word_near(baddr);
#endif
}
else {
data = 0xFFFF; // fake empty
}
#endif
sendchar(data); // send LSB
sendchar((data >> 8)); // send MSB
baddr += 2; // Select next word in memory
size -= 2; // Subtract two bytes from number of bytes to read
} while (size); // Repeat until block has been read
return baddr>>1;
}
static inline uint16_t readEEpromPage(uint16_t address, pagebuf_t size)
{
do {
sendchar( eeprom_read_byte( (uint8_t*)address ) );
address++;
size--; // Decrease number of bytes to read
} while (size); // Repeat until block has been read
return address;
}
#if defined(ENABLEREADFUSELOCK)
static uint8_t read_fuse_lock(uint16_t addr)
{
uint8_t mode = (1<<BLBSET) | (1<<SPMEN);
uint8_t retval;
asm volatile
(
"movw r30, %3\n\t" /* Z to addr */ \
"sts %0, %2\n\t" /* set mode in SPM_REG */ \
"lpm\n\t" /* load fuse/lock value into r0 */ \
"mov %1,r0\n\t" /* save return value */ \
: "=m" (SPM_REG),
"=r" (retval)
: "r" (mode),
"r" (addr)
: "r30", "r31", "r0"
);
return retval;
}
#endif
static void send_boot(void)
{
sendchar('A');
sendchar('V');
sendchar('R');
sendchar('B');
sendchar('O');
sendchar('O');
sendchar('T');
}
static void (*jump_to_app)(void) = 0x0000;
int main(void)
{
uint16_t address = 0;
uint8_t device = 0, val;
#ifdef DISABLE_WDT_AT_STARTUP
#ifdef WDT_OFF_SPECIAL
#warning "using target specific watchdog_off"
bootloader_wdt_off();
#else
cli();
wdt_reset();
wdt_disable();
#endif
#endif
#ifdef START_POWERSAVE
uint8_t OK = 1;
#endif
BLDDR &= ~(1<<BLPNUM); // set as Input
BLPORT |= (1<<BLPNUM); // Enable pullup
// Set baud rate
UART_BAUD_HIGH = (UART_CALC_BAUDRATE(BAUDRATE)>>8) & 0xFF;
UART_BAUD_LOW = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF);
#ifdef UART_DOUBLESPEED
UART_STATUS = ( 1<<UART_DOUBLE );
#endif
UART_CTRL = UART_CTRL_DATA;
UART_CTRL2 = UART_CTRL2_DATA;
#if defined(START_POWERSAVE)
/*
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.).
*/
for(;OK;) {
if ((BLPIN & (1<<BLPNUM))) {
// jump to main app if pin is not grounded
BLPORT &= ~(1<<BLPNUM); // set to default
#ifdef UART_DOUBLESPEED
UART_STATUS &= ~( 1<<UART_DOUBLE );
#endif
jump_to_app(); // Jump to application sector
} else {
val = recvchar();
/* ESC */
if (val == 0x1B) {
// AVRPROG connection
// Wait for signon
while (val != 'S')
val = recvchar();
send_boot(); // Report signon
OK = 0;
} else {
sendchar('?');
}
}
// Power-Save code here
}
#elif defined(START_SIMPLE)
if ((BLPIN & (1<<BLPNUM))) {
// jump to main app if pin is not grounded
BLPORT &= ~(1<<BLPNUM); // set to default
#ifdef UART_DOUBLESPEED
UART_STATUS &= ~( 1<<UART_DOUBLE );
#endif
jump_to_app(); // Jump to application sector
}
#elif defined(START_WAIT)
uint16_t cnt = 0;
while (1) {
if (UART_STATUS & (1<<UART_RXREADY))
if (UART_DATA == START_WAIT_UARTCHAR)
break;
if (cnt++ >= WAIT_VALUE) {
BLPORT &= ~(1<<BLPNUM); // set to default
jump_to_app(); // Jump to application sector
}
_delay_ms(10);
}
send_boot();
#elif defined(START_BOOTICE)
#warning "BOOTICE mode - no startup-condition"
#else
#error "Select START_ condition for bootloader in main.c"
#endif
for(;;) {
val = recvchar();
// Autoincrement?
if (val == 'a') {
sendchar('Y'); // Autoincrement is quicker
//write address
} else if (val == 'A') {
address = recvchar(); //read address 8 MSB
address = (address<<8) | recvchar();
sendchar('\r');
// Buffer load support
} else if (val == 'b') {
sendchar('Y'); // Report buffer load supported
sendchar((sizeof(gBuffer) >> 8) & 0xFF); // Report buffer size in bytes
sendchar(sizeof(gBuffer) & 0xFF);
// Start buffer load
} else if (val == 'B') {
pagebuf_t size;
size = recvchar() << 8; // Load high byte of buffersize
size |= recvchar(); // Load low byte of buffersize
val = recvchar(); // Load memory type ('E' or 'F')
recvBuffer(size);
if (device == DEVTYPE) {
if (val == 'F') {
address = writeFlashPage(address, size);
} else if (val == 'E') {
address = writeEEpromPage(address, size);
}
sendchar('\r');
} else {
sendchar(0);
}
// Block read
} else if (val == 'g') {
pagebuf_t size;
size = recvchar() << 8; // Load high byte of buffersize
size |= recvchar(); // Load low byte of buffersize
val = recvchar(); // Get memtype
if (val == 'F') {
address = readFlashPage(address, size);
} else if (val == 'E') {
address = readEEpromPage(address, size);
}
// Chip erase
} else if (val == 'e') {
if (device == DEVTYPE) {
eraseFlash();
}
sendchar('\r');
// Exit upgrade
} else if (val == 'E') {
wdt_enable(EXIT_WDT_TIME); // Enable Watchdog Timer to give reset
sendchar('\r');
#ifdef WRITELOCKBITS
#warning "Extension 'WriteLockBits' enabled"
// TODO: does not work reliably
// write lockbits
} else if (val == 'l') {
if (device == DEVTYPE) {
// write_lock_bits(recvchar());
boot_lock_bits_set(recvchar()); // boot.h takes care of mask
boot_spm_busy_wait();
}
sendchar('\r');
#endif
// Enter programming mode
} else if (val == 'P') {
sendchar('\r');
// Leave programming mode
} else if (val == 'L') {
sendchar('\r');
// return programmer type
} else if (val == 'p') {
sendchar('S'); // always serial programmer
#ifdef ENABLEREADFUSELOCK
#warning "Extension 'ReadFuseLock' enabled"
// read "low" fuse bits
} else if (val == 'F') {
sendchar(read_fuse_lock(GET_LOW_FUSE_BITS));
// read lock bits
} else if (val == 'r') {
sendchar(read_fuse_lock(GET_LOCK_BITS));
// read high fuse bits
} else if (val == 'N') {
sendchar(read_fuse_lock(GET_HIGH_FUSE_BITS));
// read extended fuse bits
} else if (val == 'Q') {
sendchar(read_fuse_lock(GET_EXTENDED_FUSE_BITS));
#endif
// Return device type
} else if (val == 't') {
sendchar(DEVTYPE);
sendchar(0);
// clear and set LED ignored
} else if ((val == 'x') || (val == 'y')) {
recvchar();
sendchar('\r');
// set device
} else if (val == 'T') {
device = recvchar();
sendchar('\r');
// Return software identifier
} else if (val == 'S') {
send_boot();
// Return Software Version
} else if (val == 'V') {
sendchar(VERSION_HIGH);
sendchar(VERSION_LOW);
// Return Signature Bytes (it seems that
// AVRProg expects the "Atmel-byte" 0x1E last
// but shows it first in the dialog-window)
} else if (val == 's') {
sendchar(SIG_BYTE3);
sendchar(SIG_BYTE2);
sendchar(SIG_BYTE1);
/* ESC */
} else if(val != 0x1b) {
sendchar('?');
}
}
return 0;
}