AVR Bootloader (avrboot cleanup)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

568 lines
14 KiB

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