1 /* Name: spminterface.h 
   2  * Project: USBaspLoader 
   3  * Author: Stephan Baerwolf 
   4  * Creation Date: 2012-08-01 
   5  * Copyright: (c) 2012 by Stephan Baerwolf 
   6  * License: GNU GPL v2 (see License.txt) 
  10 #ifndef SPMINTERFACE_H_f70ba6adf7624275947e859bdbff0599 
  11 #define SPMINTERFACE_H_f70ba6adf7624275947e859bdbff0599 
  14  * spminterface.h offers a lightweight interface by inserting 
  15  * an small machine-subroutine into the bootsection. (right  
  16  * after the interrupt-vector-table) 
  17  * This subroutine can be called by normal code in order to  
  18  * enable it to program the flash (and depending on BLB11-lockbit 
  19  * also the bootsection itself). Since SPM-calls from RWW-sections 
  20  * will fail to work. The routine will be called "bootloader__do_spm". 
  21  * Its principle assembler-code is depicted below (real code is a 
  22  * little machinedependend). 
  23  * Interfaces will be the 8-bit registers r10..r13, for details  
  24  * also see below. As also the pageaddress-registes (Z and rampZ) 
  25  * are interfaced via different registers, it is possible to call 
  26  * this routine via indirect call (icall). 
  27  * Traditionally it is also possible to rcall, therefore you can 
  28  * define "bootloader__do_spm" for your normal code via defsym at  
  30  * Example for an atmega8: "-Wl,--defsym=transfer_point=0x1826" 
  31  * (since BOOTLOADER_ADDRESS is 0x1800 and there are  
  32  * 2x19 = 38 = 0x26 byte for interrupts) 
  36 ;disable interrupts (if enabled) before calling! 
  37 ;you may also want to disable wdt, since this routine may busy-loop 
  38 ;================================================================== 
  40 ;spmcr (spmcrval determines SPM action) will be register:       r18 
  41 ;MCU dependend RA(MPZ should be transfered within register:     r11 
  42 ;lo8(Z) should be transfered within register:                   r12 
  43 ;hi8(Z) should be transfered within register:                   r13 
  44 ;( as definition of SPM low8bit of dataword are stored within   r0 ) 
  45 ;( as definition of SPM hi8bit  of dataword are stored within   r1 ) 
  48 ;temp0 will be register:                                        r11 
  49 ;temp1 will be register:                                        r12 
  50 ;temp2 will be register:                                        r13 
  51 ;spmcrval (r18) may also be changed due to rww reenable-phase   r18 
  52 ;Z (r31:r30) wil be changed during operation 
  55 ;================================================================== 
  56 ; TODO: waitA and waitB could be merged to subroutine saving 2 opc 
  57 ;================================================================== 
  59 ;load pageaddress (Z) from (r11:)r13:12 since it may was used for icall 
  64 waitA:                  ;check for pending SPM complete 
  69 out     SPMCR, spmcrval ;SPM timed sequence 
  72 waitB:                  ;check for previous SPM complete 
  77 ;avoid crash of userapplication 
  78 ldi     spmcrval, ((1<<RWWSRE) | (1<<SPMEN))  
  93  * This MACRO commands the linker to place correspondig 
  94  * data on the top of the firmware. (Right after the  
  95  * interrupt-vector-table) 
  96  * This is necessary to always locate the 
  97  * "bootloader__do_spm" for example at 0x1826, even if 
  98  * there are existing PROGMEM within the firmware... 
 100 #define BOOTLIBLINK __attribute__ ((section (".vectors") )) 
 103 #ifndef funcaddr___bootloader__do_spm 
 104   #if (defined(BOOTLOADER_ADDRESS)) && (!(defined(NEW_BOOTLOADER_ADDRESS))) 
 105     #if HAVE_SPMINTEREFACE 
 106       #define  funcaddr___bootloader__do_spm (&bootloader__do_spm) 
 109     #if defined (__AVR_ATmega8__) || defined (__AVR_ATmega8HVA__) 
 110       #define  funcaddr___bootloader__do_spm 0x1826 
 113       #error "unknown MCU - where is bootloader__do_spm located?" 
 120  * Call the "bootloader__do_spm"-function, located within the BLS via comfortable C-interface 
 121  * During operation code will block - disable or reset watchdog before call. 
 123  * ATTANTION:   Since the underlying "bootloader__do_spm" will automatically reenable the 
 124  *              rww-section, only one way to program the flash will work. 
 125  *              (First erase the page, then fill temp. buffer, finally program...) 
 126  *              Since unblocking rww-section erases the temp. pagebuffer (which happens 
 127  *              after a page-erase), first programming this buffer does not help !! 
 129  * REMEMBER: interrupts have to be disabled! (otherwise code may crash non-deterministic) 
 132 #define __do_spm_Ex(flash_wordaddress, spmcrval, dataword, ___bootloader__do_spm__ptr)          \ 
 138     "mov r13, %B[flashaddress]\n\t"                                                             \ 
 139     "mov r12, %A[flashaddress]\n\t"                                                             \ 
 140     "mov r11, %C[flashaddress]\n\t"                                                             \ 
 142     /* also load the spmcrval */                                                                \ 
 143     "mov r18, %[spmcrval]\n\t"                                                                  \ 
 146     "mov r1, %B[data]\n\t"                                                                      \ 
 147     "mov r0, %A[data]\n\t"                                                                      \ 
 149     /* finally call the bootloader-function */                                                  \ 
 156     : [flashaddress] "r" (flash_wordaddress),                                                   \ 
 157       [spmfunctionaddress] "z" (___bootloader__do_spm__ptr),                                    \ 
 158       [spmcrval] "r" (spmcrval),                                                                \ 
 159       [data] "r" (dataword)                                                                     \ 
 160     : "r0","r1","r11","r12","r13","r18"                                                         \ 
 165 #if (!(defined(BOOTLOADER_ADDRESS))) || (defined(NEW_BOOTLOADER_ADDRESS)) 
 166 void do_spm(const uint32_t flash_byteaddress
, const uint8_t spmcrval
, const uint16_t dataword
) { 
 167     __do_spm_Ex(flash_byteaddress
, spmcrval
, dataword
, funcaddr___bootloader__do_spm 
>> 1); 
 172 #include "bootloaderconfig.h" 
 174 #if (HAVE_SPMINTEREFACE) && (defined(BOOTLOADER_ADDRESS)) && (!(defined(NEW_BOOTLOADER_ADDRESS))) 
 177  * insert architecture dependend "bootloader_do_spm"-code 
 179  * try to make this array as big as possible 
 180  * (so bootloader always uses 2kbytes flash) 
 182 #if defined (__AVR_ATmega8__) || defined (__AVR_ATmega8HVA__) 
 183 //assume  SPMCR==0x37, SPMEN==0x0, RWWSRE=0x4, RWWSB=0x6 
 184 const uint16_t bootloader__do_spm
[17] BOOTLIBLINK 
= {0x0000, 0x2dec, 0x2dfd, 0xb6b7, 0xfcb0, 0xcffd, 0xbf27, 0x95e8, 0xb6b7, 
 185                                                      0xfcb0, 0xcffd, 0xe121, 0xb6b7, 0xfcb6, 0xcff4, 0x9508, 0xFFFF}; 
 187 00001826 <bootloader__do_spm>: 
 189     1828:       ec 2d           mov     r30, r12 
 190     182a:       fd 2d           mov     r31, r13 
 193     182c:       b7 b6           in      r11, 0x37       ; 55 
 194     182e:       b0 fc           sbrc    r11, 0 
 195     1830:       fd cf           rjmp    .-6             ; 0x182c <waitA> 
 196     1832:       27 bf           out     0x37, r18       ; 55 
 200     1836:       b7 b6           in      r11, 0x37       ; 55 
 201     1838:       b0 fc           sbrc    r11, 0 
 202     183a:       fd cf           rjmp    .-6             ; 0x1836 <waitB> 
 203     183c:       21 e1           ldi     r18, 0x11       ; 17 
 204     183e:       b7 b6           in      r11, 0x37       ; 55 
 205     1840:       b6 fc           sbrc    r11, 6 
 206     1842:       f4 cf           rjmp    .-24            ; 0x182c <waitA> 
 214 #elif defined (__AVR_ATmega48__) || defined (__AVR_ATmega48P__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega88P__) || defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__) 
 215 //assume  SPMCR:=SPMCSR==0x37, SPMEN:=SELFPRGEN==0x0, RWWSRE=0x4, RWWSB=0x6 
 216 const uint16_t bootloader__do_spm
[17] BOOTLIBLINK 
= {0x0000, 0x2dec, 0x2dfd, 0xb6b7, 0xfcb0, 0xcffd, 0xbf27, 0x95e8, 0xb6b7, 
 217                                                      0xfcb0, 0xcffd, 0xe121, 0xb6b7, 0xfcb6, 0xcff4, 0x9508, 0xFFFF}; 
 219 00001826 <bootloader__do_spm>: 
 221     1828:       ec 2d           mov     r30, r12 
 222     182a:       fd 2d           mov     r31, r13 
 225     182c:       b7 b6           in      r11, 0x37       ; 55 
 226     182e:       b0 fc           sbrc    r11, 0 
 227     1830:       fd cf           rjmp    .-6             ; 0x182c <waitA> 
 228     1832:       27 bf           out     0x37, r18       ; 55 
 232     1836:       b7 b6           in      r11, 0x37       ; 55 
 233     1838:       b0 fc           sbrc    r11, 0 
 234     183a:       fd cf           rjmp    .-6             ; 0x1836 <waitB> 
 235     183c:       21 e1           ldi     r18, 0x11       ; 17 
 236     183e:       b7 b6           in      r11, 0x37       ; 55 
 237     1840:       b6 fc           sbrc    r11, 6 
 238     1842:       f4 cf           rjmp    .-24            ; 0x182c <waitA> 
 246 #elif defined (__AVR_ATmega48A__) || defined (__AVR_ATmega48PA__) || defined (__AVR_ATmega88A__) || defined (__AVR_ATmega88PA__) || defined (__AVR_ATmega168A__) || defined (__AVR_ATmega168PA__) || defined (__AVR_ATmega328__) || defined (__AVR_ATmega328P__) 
 247 //assume  SPMCR:=SPMCSR==0x37, SPMEN:=SELFPRGEN==0x0, RWWSRE=0x4, RWWSB=0x6 
 248 const uint16_t bootloader__do_spm
[17] BOOTLIBLINK 
= {0x0000, 0x2dec, 0x2dfd, 0xb6b7, 0xfcb0, 0xcffd, 0xbf27, 0x95e8, 0xb6b7, 
 249                                                      0xfcb0, 0xcffd, 0xe121, 0xb6b7, 0xfcb6, 0xcff4, 0x9508, 0xFFFF}; 
 251 00001826 <bootloader__do_spm>: 
 253     1828:       ec 2d           mov     r30, r12 
 254     182a:       fd 2d           mov     r31, r13 
 257     182c:       b7 b6           in      r11, 0x37       ; 55 
 258     182e:       b0 fc           sbrc    r11, 0 
 259     1830:       fd cf           rjmp    .-6             ; 0x182c <waitA> 
 260     1832:       27 bf           out     0x37, r18       ; 55 
 264     1836:       b7 b6           in      r11, 0x37       ; 55 
 265     1838:       b0 fc           sbrc    r11, 0 
 266     183a:       fd cf           rjmp    .-6             ; 0x1836 <waitB> 
 267     183c:       21 e1           ldi     r18, 0x11       ; 17 
 268     183e:       b7 b6           in      r11, 0x37       ; 55 
 269     1840:       b6 fc           sbrc    r11, 6 
 270     1842:       f4 cf           rjmp    .-24            ; 0x182c <waitA> 
 278 #elif defined (__AVR_ATmega164A__) || defined (__AVR_ATmega164PA__) || defined (__AVR_ATmega324A__) || defined (__AVR_ATmega324PA__) || defined (__AVR_ATmega644A__) || defined (__AVR_ATmega644PA__) || defined (__AVR_ATmega1284__) || defined (__AVR_ATmega1284P__) 
 279 //assume  SPMCR:=SPCSR==0x37, SPMEN==0x0, RWWSRE=0x4, RWWSB=0x6 and rampZ=0x3b 
 280 const uint16_t bootloader__do_spm
[17] BOOTLIBLINK 
= {0xbebb, 0x2dec, 0x2dfd, 0xb6b7, 0xfcb0, 0xcffd, 0xbf27, 0x95e8, 0xb6b7, 
 281                                                      0xfcb0, 0xcffd, 0xe121, 0xb6b7, 0xfcb6, 0xcff4, 0x9508, 0xFFFF}; 
 283 00001826 <bootloader__do_spm>: 
 284     1826:       bb be           out     0x3b,r11        ; rampZ=r11; (rampZ is at IO 0x3b) 
 285     1828:       ec 2d           mov     r30, r12 
 286     182a:       fd 2d           mov     r31, r13 
 289     182c:       b7 b6           in      r11, 0x37       ; 55 
 290     182e:       b0 fc           sbrc    r11, 0 
 291     1830:       fd cf           rjmp    .-6             ; 0x182c <waitA> 
 292     1832:       27 bf           out     0x37, r18       ; 55 
 296     1836:       b7 b6           in      r11, 0x37       ; 55 
 297     1838:       b0 fc           sbrc    r11, 0 
 298     183a:       fd cf           rjmp    .-6             ; 0x1836 <waitB> 
 299     183c:       21 e1           ldi     r18, 0x11       ; 17 
 300     183e:       b7 b6           in      r11, 0x37       ; 55 
 301     1840:       b6 fc           sbrc    r11, 6 
 302     1842:       f4 cf           rjmp    .-24            ; 0x182c <waitA> 
 311   #error "bootloader__do_spm has to be adapted, since there is no architecture code, yet"