STIMA  3
boot.h
Go to the documentation of this file.
1 /* Modified to use out for SPM access
2 ** Peter Knight, Optiboot project http://optiboot.googlecode.com
3 **
4 ** Todo: Tidy up
5 **
6 ** "_short" routines execute 1 cycle faster and use 1 less word of flash
7 ** by using "out" instruction instead of "sts".
8 **
9 ** Additional elpm variants that trust the value of RAMPZ
10 */
11 
12 /* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Eric B. Weddington
13  All rights reserved.
14 
15  Redistribution and use in source and binary forms, with or without
16  modification, are permitted provided that the following conditions are met:
17 
18  * Redistributions of source code must retain the above copyright
19  notice, this list of conditions and the following disclaimer.
20  * Redistributions in binary form must reproduce the above copyright
21  notice, this list of conditions and the following disclaimer in
22  the documentation and/or other materials provided with the
23  distribution.
24  * Neither the name of the copyright holders nor the names of
25  contributors may be used to endorse or promote products derived
26  from this software without specific prior written permission.
27 
28  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
29  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
32  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  POSSIBILITY OF SUCH DAMAGE. */
39 
40 /* $Id: boot.h,v 1.27.2.3 2008/09/30 13:58:48 arcanum Exp $ */
41 
42 #ifndef _AVR_BOOT_H_
43 #define _AVR_BOOT_H_ 1
44 
115 #include <avr/eeprom.h>
116 #include <avr/io.h>
117 #include <inttypes.h>
118 #include <limits.h>
119 
120 /* Check for SPM Control Register in processor. */
121 #if defined (SPMCSR)
122 # define __SPM_REG SPMCSR
123 #elif defined (SPMCR)
124 # define __SPM_REG SPMCR
125 #else
126 # error AVR processor does not provide bootloader support!
127 #endif
128 
129 
130 /* Check for SPM Enable bit. */
131 #if defined(SPMEN)
132 # define __SPM_ENABLE SPMEN
133 #elif defined(SELFPRGEN)
134 # define __SPM_ENABLE SELFPRGEN
135 #else
136 # error Cannot find SPM Enable bit definition!
137 #endif
138 
147 #define BOOTLOADER_SECTION __attribute__ ((section (".bootloader")))
148 
149 /* Create common bit definitions. */
150 #ifdef ASB
151 #define __COMMON_ASB ASB
152 #else
153 #define __COMMON_ASB RWWSB
154 #endif
155 
156 #ifdef ASRE
157 #define __COMMON_ASRE ASRE
158 #else
159 #define __COMMON_ASRE RWWSRE
160 #endif
161 
162 /* Define the bit positions of the Boot Lock Bits. */
163 
164 #define BLB12 5
165 #define BLB11 4
166 #define BLB02 3
167 #define BLB01 2
168 
173 #define boot_spm_interrupt_enable() (__SPM_REG |= (uint8_t)_BV(SPMIE))
174 
179 #define boot_spm_interrupt_disable() (__SPM_REG &= (uint8_t)~_BV(SPMIE))
180 
185 #define boot_is_spm_interrupt() (__SPM_REG & (uint8_t)_BV(SPMIE))
186 
191 #define boot_rww_busy() (__SPM_REG & (uint8_t)_BV(__COMMON_ASB))
192 
197 #define boot_spm_busy() (__SPM_REG & (uint8_t)_BV(__SPM_ENABLE))
198 
203 #define boot_spm_busy_wait() do{}while(boot_spm_busy())
204 
205 #define __BOOT_PAGE_ERASE (_BV(__SPM_ENABLE) | _BV(PGERS))
206 #define __BOOT_PAGE_WRITE (_BV(__SPM_ENABLE) | _BV(PGWRT))
207 #define __BOOT_PAGE_FILL _BV(__SPM_ENABLE)
208 #define __BOOT_RWW_ENABLE (_BV(__SPM_ENABLE) | _BV(__COMMON_ASRE))
209 #define __BOOT_LOCK_BITS_SET (_BV(__SPM_ENABLE) | _BV(BLBSET))
210 
211 #define __boot_page_fill_short(address, data) \
212 (__extension__({ \
213  __asm__ __volatile__ \
214  ( \
215  "movw r0, %3\n\t" \
216  "out %0, %1\n\t" \
217  "spm\n\t" \
218  "clr r1\n\t" \
219  : \
220  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
221  "r" ((uint8_t)__BOOT_PAGE_FILL), \
222  "z" ((uint16_t)address), \
223  "r" ((uint16_t)data) \
224  : "r0" \
225  ); \
226 }))
227 
228 #define __boot_page_fill_normal(address, data) \
229 (__extension__({ \
230  __asm__ __volatile__ \
231  ( \
232  "movw r0, %3\n\t" \
233  "sts %0, %1\n\t" \
234  "spm\n\t" \
235  "clr r1\n\t" \
236  : \
237  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
238  "r" ((uint8_t)__BOOT_PAGE_FILL), \
239  "z" ((uint16_t)address), \
240  "r" ((uint16_t)data) \
241  : "r0" \
242  ); \
243 }))
244 
245 #define __boot_page_fill_alternate(address, data)\
246 (__extension__({ \
247  __asm__ __volatile__ \
248  ( \
249  "movw r0, %3\n\t" \
250  "sts %0, %1\n\t" \
251  "spm\n\t" \
252  ".word 0xffff\n\t" \
253  "nop\n\t" \
254  "clr r1\n\t" \
255  : \
256  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
257  "r" ((uint8_t)__BOOT_PAGE_FILL), \
258  "z" ((uint16_t)address), \
259  "r" ((uint16_t)data) \
260  : "r0" \
261  ); \
262 }))
263 
264 #define __boot_page_fill_extended(address, data) \
265 (__extension__({ \
266  __asm__ __volatile__ \
267  ( \
268  "movw r0, %4\n\t" \
269  "movw r30, %A3\n\t" \
270  "sts %1, %C3\n\t" \
271  "sts %0, %2\n\t" \
272  "spm\n\t" \
273  "clr r1\n\t" \
274  : \
275  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
276  "i" (_SFR_MEM_ADDR(RAMPZ)), \
277  "r" ((uint8_t)__BOOT_PAGE_FILL), \
278  "r" ((uint32_t)address), \
279  "r" ((uint16_t)data) \
280  : "r0", "r30", "r31" \
281  ); \
282 }))
283 
284 #define __boot_page_fill_extended_short(address, data) \
285 (__extension__({ \
286  __asm__ __volatile__ \
287  ( \
288  "movw r0, %4\n\t" \
289  "movw r30, %A3\n\t" \
290  "out %1, %C3\n\t" \
291  "out %0, %2\n\t" \
292  "spm\n\t" \
293  "clr r1\n\t" \
294  : \
295  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
296  "i" (_SFR_IO_ADDR(RAMPZ)), \
297  "r" ((uint8_t)__BOOT_PAGE_FILL), \
298  "r" ((uint32_t)address), \
299  "r" ((uint16_t)data) \
300  : "r0", "r30", "r31" \
301  ); \
302 }))
303 
304 #define __boot_page_erase_short(address) \
305 (__extension__({ \
306  __asm__ __volatile__ \
307  ( \
308  "out %0, %1\n\t" \
309  "spm\n\t" \
310  : \
311  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
312  "r" ((uint8_t)__BOOT_PAGE_ERASE), \
313  "z" ((uint16_t)address) \
314  ); \
315 }))
316 
317 
318 #define __boot_page_erase_normal(address) \
319 (__extension__({ \
320  __asm__ __volatile__ \
321  ( \
322  "sts %0, %1\n\t" \
323  "spm\n\t" \
324  : \
325  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
326  "r" ((uint8_t)__BOOT_PAGE_ERASE), \
327  "z" ((uint16_t)address) \
328  ); \
329 }))
330 
331 #define __boot_page_erase_alternate(address) \
332 (__extension__({ \
333  __asm__ __volatile__ \
334  ( \
335  "sts %0, %1\n\t" \
336  "spm\n\t" \
337  ".word 0xffff\n\t" \
338  "nop\n\t" \
339  : \
340  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
341  "r" ((uint8_t)__BOOT_PAGE_ERASE), \
342  "z" ((uint16_t)address) \
343  ); \
344 }))
345 
346 #define __boot_page_erase_extended(address) \
347 (__extension__({ \
348  __asm__ __volatile__ \
349  ( \
350  "movw r30, %A3\n\t" \
351  "sts %1, %C3\n\t" \
352  "sts %0, %2\n\t" \
353  "spm\n\t" \
354  : \
355  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
356  "i" (_SFR_MEM_ADDR(RAMPZ)), \
357  "r" ((uint8_t)__BOOT_PAGE_ERASE), \
358  "r" ((uint32_t)address) \
359  : "r30", "r31" \
360  ); \
361 }))
362 #define __boot_page_erase_extended_short(address) \
363 (__extension__({ \
364  __asm__ __volatile__ \
365  ( \
366  "movw r30, %A3\n\t" \
367  "out %1, %C3\n\t" \
368  "out %0, %2\n\t" \
369  "spm\n\t" \
370  : \
371  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
372  "i" (_SFR_IO_ADDR(RAMPZ)), \
373  "r" ((uint8_t)__BOOT_PAGE_ERASE), \
374  "r" ((uint32_t)address) \
375  : "r30", "r31" \
376  ); \
377 }))
378 
379 #define __boot_page_write_short(address) \
380 (__extension__({ \
381  __asm__ __volatile__ \
382  ( \
383  "out %0, %1\n\t" \
384  "spm\n\t" \
385  : \
386  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
387  "r" ((uint8_t)__BOOT_PAGE_WRITE), \
388  "z" ((uint16_t)address) \
389  ); \
390 }))
391 
392 #define __boot_page_write_normal(address) \
393 (__extension__({ \
394  __asm__ __volatile__ \
395  ( \
396  "sts %0, %1\n\t" \
397  "spm\n\t" \
398  : \
399  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
400  "r" ((uint8_t)__BOOT_PAGE_WRITE), \
401  "z" ((uint16_t)address) \
402  ); \
403 }))
404 
405 #define __boot_page_write_alternate(address) \
406 (__extension__({ \
407  __asm__ __volatile__ \
408  ( \
409  "sts %0, %1\n\t" \
410  "spm\n\t" \
411  ".word 0xffff\n\t" \
412  "nop\n\t" \
413  : \
414  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
415  "r" ((uint8_t)__BOOT_PAGE_WRITE), \
416  "z" ((uint16_t)address) \
417  ); \
418 }))
419 
420 #define __boot_page_write_extended(address) \
421 (__extension__({ \
422  __asm__ __volatile__ \
423  ( \
424  "movw r30, %A3\n\t" \
425  "sts %1, %C3\n\t" \
426  "sts %0, %2\n\t" \
427  "spm\n\t" \
428  : \
429  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
430  "i" (_SFR_MEM_ADDR(RAMPZ)), \
431  "r" ((uint8_t)__BOOT_PAGE_WRITE), \
432  "r" ((uint32_t)address) \
433  : "r30", "r31" \
434  ); \
435 }))
436 #define __boot_page_write_extended_short(address) \
437 (__extension__({ \
438  __asm__ __volatile__ \
439  ( \
440  "movw r30, %A3\n\t" \
441  "out %1, %C3\n\t" \
442  "out %0, %2\n\t" \
443  "spm\n\t" \
444  : \
445  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
446  "i" (_SFR_IO_ADDR(RAMPZ)), \
447  "r" ((uint8_t)__BOOT_PAGE_WRITE), \
448  "r" ((uint32_t)address) \
449  : "r30", "r31" \
450  ); \
451 }))
452 
453 #define __boot_rww_enable_short() \
454 (__extension__({ \
455  __asm__ __volatile__ \
456  ( \
457  "out %0, %1\n\t" \
458  "spm\n\t" \
459  : \
460  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
461  "r" ((uint8_t)__BOOT_RWW_ENABLE) \
462  ); \
463 }))
464 
465 #define __boot_rww_enable() \
466 (__extension__({ \
467  __asm__ __volatile__ \
468  ( \
469  "sts %0, %1\n\t" \
470  "spm\n\t" \
471  : \
472  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
473  "r" ((uint8_t)__BOOT_RWW_ENABLE) \
474  ); \
475 }))
476 
477 #define __boot_rww_enable_alternate() \
478 (__extension__({ \
479  __asm__ __volatile__ \
480  ( \
481  "sts %0, %1\n\t" \
482  "spm\n\t" \
483  ".word 0xffff\n\t" \
484  "nop\n\t" \
485  : \
486  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
487  "r" ((uint8_t)__BOOT_RWW_ENABLE) \
488  ); \
489 }))
490 
491 /* From the mega16/mega128 data sheets (maybe others):
492 
493  Bits by SPM To set the Boot Loader Lock bits, write the desired data to
494  R0, write "X0001001" to SPMCR and execute SPM within four clock cycles
495  after writing SPMCR. The only accessible Lock bits are the Boot Lock bits
496  that may prevent the Application and Boot Loader section from any
497  software update by the MCU.
498 
499  If bits 5..2 in R0 are cleared (zero), the corresponding Boot Lock bit
500  will be programmed if an SPM instruction is executed within four cycles
501  after BLBSET and SPMEN (or SELFPRGEN) are set in SPMCR. The Z-pointer is
502  don't care during this operation, but for future compatibility it is
503  recommended to load the Z-pointer with $0001 (same as used for reading the
504  Lock bits). For future compatibility It is also recommended to set bits 7,
505  6, 1, and 0 in R0 to 1 when writing the Lock bits. When programming the
506  Lock bits the entire Flash can be read during the operation. */
507 
508 #define __boot_lock_bits_set_short(lock_bits) \
509 (__extension__({ \
510  uint8_t value = (uint8_t)(~(lock_bits)); \
511  __asm__ __volatile__ \
512  ( \
513  "ldi r30, 1\n\t" \
514  "ldi r31, 0\n\t" \
515  "mov r0, %2\n\t" \
516  "out %0, %1\n\t" \
517  "spm\n\t" \
518  : \
519  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
520  "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
521  "r" (value) \
522  : "r0", "r30", "r31" \
523  ); \
524 }))
525 
526 #define __boot_lock_bits_set(lock_bits) \
527 (__extension__({ \
528  uint8_t value = (uint8_t)(~(lock_bits)); \
529  __asm__ __volatile__ \
530  ( \
531  "ldi r30, 1\n\t" \
532  "ldi r31, 0\n\t" \
533  "mov r0, %2\n\t" \
534  "sts %0, %1\n\t" \
535  "spm\n\t" \
536  : \
537  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
538  "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
539  "r" (value) \
540  : "r0", "r30", "r31" \
541  ); \
542 }))
543 
544 #define __boot_lock_bits_set_alternate(lock_bits) \
545 (__extension__({ \
546  uint8_t value = (uint8_t)(~(lock_bits)); \
547  __asm__ __volatile__ \
548  ( \
549  "ldi r30, 1\n\t" \
550  "ldi r31, 0\n\t" \
551  "mov r0, %2\n\t" \
552  "sts %0, %1\n\t" \
553  "spm\n\t" \
554  ".word 0xffff\n\t" \
555  "nop\n\t" \
556  : \
557  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
558  "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
559  "r" (value) \
560  : "r0", "r30", "r31" \
561  ); \
562 }))
563 
564 /*
565  Reading lock and fuse bits:
566 
567  Similarly to writing the lock bits above, set BLBSET and SPMEN (or
568  SELFPRGEN) bits in __SPMREG, and then (within four clock cycles) issue an
569  LPM instruction.
570 
571  Z address: contents:
572  0x0000 low fuse bits
573  0x0001 lock bits
574  0x0002 extended fuse bits
575  0x0003 high fuse bits
576 
577  Sounds confusing, doesn't it?
578 
579  Unlike the macros in pgmspace.h, no need to care for non-enhanced
580  cores here as these old cores do not provide SPM support anyway.
581  */
582 
587 #define GET_LOW_FUSE_BITS (0x0000)
592 #define GET_LOCK_BITS (0x0001)
597 #define GET_EXTENDED_FUSE_BITS (0x0002)
602 #define GET_HIGH_FUSE_BITS (0x0003)
603 
616 #define boot_lock_fuse_bits_get_short(address) \
617 (__extension__({ \
618  uint8_t __result; \
619  __asm__ __volatile__ \
620  ( \
621  "ldi r30, %3\n\t" \
622  "ldi r31, 0\n\t" \
623  "out %1, %2\n\t" \
624  "lpm %0, Z\n\t" \
625  : "=r" (__result) \
626  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
627  "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
628  "M" (address) \
629  : "r0", "r30", "r31" \
630  ); \
631  __result; \
632 }))
633 
634 #define boot_lock_fuse_bits_get(address) \
635 (__extension__({ \
636  uint8_t __result; \
637  __asm__ __volatile__ \
638  ( \
639  "ldi r30, %3\n\t" \
640  "ldi r31, 0\n\t" \
641  "sts %1, %2\n\t" \
642  "lpm %0, Z\n\t" \
643  : "=r" (__result) \
644  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
645  "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \
646  "M" (address) \
647  : "r0", "r30", "r31" \
648  ); \
649  __result; \
650 }))
651 
663 #define __BOOT_SIGROW_READ (_BV(__SPM_ENABLE) | _BV(SIGRD))
664 
665 #define boot_signature_byte_get_short(addr) \
666 (__extension__({ \
667  uint16_t __addr16 = (uint16_t)(addr); \
668  uint8_t __result; \
669  __asm__ __volatile__ \
670  ( \
671  "out %1, %2\n\t" \
672  "lpm %0, Z" "\n\t" \
673  : "=r" (__result) \
674  : "i" (_SFR_IO_ADDR(__SPM_REG)), \
675  "r" ((uint8_t) __BOOT_SIGROW_READ), \
676  "z" (__addr16) \
677  ); \
678  __result; \
679 }))
680 
681 #define boot_signature_byte_get(addr) \
682 (__extension__({ \
683  uint16_t __addr16 = (uint16_t)(addr); \
684  uint8_t __result; \
685  __asm__ __volatile__ \
686  ( \
687  "sts %1, %2\n\t" \
688  "lpm %0, Z" "\n\t" \
689  : "=r" (__result) \
690  : "i" (_SFR_MEM_ADDR(__SPM_REG)), \
691  "r" ((uint8_t) __BOOT_SIGROW_READ), \
692  "z" (__addr16) \
693  ); \
694  __result; \
695 }))
696 
750 /* Normal versions of the macros use 16-bit addresses.
751  Extended versions of the macros use 32-bit addresses.
752  Alternate versions of the macros use 16-bit addresses and require special
753  instruction sequences after LPM.
754 
755  FLASHEND is defined in the ioXXXX.h file.
756  USHRT_MAX is defined in <limits.h>. */
757 
758 #if defined(__AVR_ATmega161__) || defined(__AVR_ATmega163__) \
759  || defined(__AVR_ATmega323__)
760 
761 /* Alternate: ATmega161/163/323 and 16 bit address */
762 #define boot_page_fill(address, data) __boot_page_fill_alternate(address, data)
763 #define boot_page_erase(address) __boot_page_erase_alternate(address)
764 #define boot_page_write(address) __boot_page_write_alternate(address)
765 #define boot_rww_enable() __boot_rww_enable_alternate()
766 #define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_alternate(lock_bits)
767 
768 #elif (FLASHEND > USHRT_MAX)
769 
770 /* Extended: >16 bit address */
771 #define boot_page_fill(address, data) __boot_page_fill_extended_short(address, data)
772 #define boot_page_erase(address) __boot_page_erase_extended_short(address)
773 #define boot_page_write(address) __boot_page_write_extended_short(address)
774 #define boot_rww_enable() __boot_rww_enable_short()
775 #define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_short(lock_bits)
776 
777 #else
778 
779 /* Normal: 16 bit address */
780 #define boot_page_fill(address, data) __boot_page_fill_short(address, data)
781 #define boot_page_erase(address) __boot_page_erase_short(address)
782 #define boot_page_write(address) __boot_page_write_short(address)
783 #define boot_rww_enable() __boot_rww_enable_short()
784 #define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_short(lock_bits)
785 
786 #endif
787 
793 #define boot_page_fill_safe(address, data) \
794 do { \
795  boot_spm_busy_wait(); \
796  eeprom_busy_wait(); \
797  boot_page_fill(address, data); \
798 } while (0)
799 
805 #define boot_page_erase_safe(address) \
806 do { \
807  boot_spm_busy_wait(); \
808  eeprom_busy_wait(); \
809  boot_page_erase (address); \
810 } while (0)
811 
817 #define boot_page_write_safe(address) \
818 do { \
819  boot_spm_busy_wait(); \
820  eeprom_busy_wait(); \
821  boot_page_write (address); \
822 } while (0)
823 
829 #define boot_rww_enable_safe() \
830 do { \
831  boot_spm_busy_wait(); \
832  eeprom_busy_wait(); \
833  boot_rww_enable(); \
834 } while (0)
835 
841 #define boot_lock_bits_set_safe(lock_bits) \
842 do { \
843  boot_spm_busy_wait(); \
844  eeprom_busy_wait(); \
845  boot_lock_bits_set (lock_bits); \
846 } while (0)
847 
848 #endif /* _AVR_BOOT_H_ */