root / arduino-1.0 / hardware / arduino / bootloaders / optiboot / boot.h @ 58d82c77
History | View | Annotate | Download (33.2 KB)
1 | 58d82c77 | Tom Mullins | /* 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 | |||
45 | /** \file */
|
||
46 | /** \defgroup avr_boot <avr/boot.h>: Bootloader Support Utilities
|
||
47 | \code
|
||
48 | #include <avr/io.h>
|
||
49 | #include <avr/boot.h>
|
||
50 | \endcode
|
||
51 | |||
52 | The macros in this module provide a C language interface to the
|
||
53 | bootloader support functionality of certain AVR processors. These
|
||
54 | macros are designed to work with all sizes of flash memory.
|
||
55 | |||
56 | Global interrupts are not automatically disabled for these macros. It
|
||
57 | is left up to the programmer to do this. See the code example below.
|
||
58 | Also see the processor datasheet for caveats on having global interrupts
|
||
59 | enabled during writing of the Flash.
|
||
60 | |||
61 | \note Not all AVR processors provide bootloader support. See your
|
||
62 | processor datasheet to see if it provides bootloader support.
|
||
63 | |||
64 | \todo From email with Marek: On smaller devices (all except ATmega64/128),
|
||
65 | __SPM_REG is in the I/O space, accessible with the shorter "in" and "out"
|
||
66 | instructions - since the boot loader has a limited size, this could be an
|
||
67 | important optimization.
|
||
68 | |||
69 | \par API Usage Example
|
||
70 | The following code shows typical usage of the boot API.
|
||
71 | |||
72 | \code
|
||
73 | #include <inttypes.h>
|
||
74 | #include <avr/interrupt.h>
|
||
75 | #include <avr/pgmspace.h>
|
||
76 |
|
||
77 | void boot_program_page (uint32_t page, uint8_t *buf)
|
||
78 | {
|
||
79 | uint16_t i;
|
||
80 | uint8_t sreg;
|
||
81 | |||
82 | // Disable interrupts.
|
||
83 | |||
84 | sreg = SREG;
|
||
85 | cli();
|
||
86 |
|
||
87 | eeprom_busy_wait ();
|
||
88 | |||
89 | boot_page_erase (page);
|
||
90 | boot_spm_busy_wait (); // Wait until the memory is erased.
|
||
91 | |||
92 | for (i=0; i<SPM_PAGESIZE; i+=2)
|
||
93 | {
|
||
94 | // Set up little-endian word.
|
||
95 | |||
96 | uint16_t w = *buf++;
|
||
97 | w += (*buf++) << 8;
|
||
98 |
|
||
99 | boot_page_fill (page + i, w);
|
||
100 | }
|
||
101 | |||
102 | boot_page_write (page); // Store buffer in flash page.
|
||
103 | boot_spm_busy_wait(); // Wait until the memory is written.
|
||
104 | |||
105 | // Reenable RWW-section again. We need this if we want to jump back
|
||
106 | // to the application after bootloading.
|
||
107 | |||
108 | boot_rww_enable ();
|
||
109 | |||
110 | // Re-enable interrupts (if they were ever enabled).
|
||
111 | |||
112 | SREG = sreg;
|
||
113 | }\endcode */
|
||
114 | |||
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 | |||
139 | /** \ingroup avr_boot
|
||
140 | \def BOOTLOADER_SECTION
|
||
141 | |||
142 | Used to declare a function or variable to be placed into a
|
||
143 | new section called .bootloader. This section and its contents
|
||
144 | can then be relocated to any address (such as the bootloader
|
||
145 | NRWW area) at link-time. */
|
||
146 | |||
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 | |||
169 | /** \ingroup avr_boot
|
||
170 | \def boot_spm_interrupt_enable()
|
||
171 | Enable the SPM interrupt. */
|
||
172 | |||
173 | #define boot_spm_interrupt_enable() (__SPM_REG |= (uint8_t)_BV(SPMIE))
|
||
174 | |||
175 | /** \ingroup avr_boot
|
||
176 | \def boot_spm_interrupt_disable()
|
||
177 | Disable the SPM interrupt. */
|
||
178 | |||
179 | #define boot_spm_interrupt_disable() (__SPM_REG &= (uint8_t)~_BV(SPMIE))
|
||
180 | |||
181 | /** \ingroup avr_boot
|
||
182 | \def boot_is_spm_interrupt()
|
||
183 | Check if the SPM interrupt is enabled. */
|
||
184 | |||
185 | #define boot_is_spm_interrupt() (__SPM_REG & (uint8_t)_BV(SPMIE))
|
||
186 | |||
187 | /** \ingroup avr_boot
|
||
188 | \def boot_rww_busy()
|
||
189 | Check if the RWW section is busy. */
|
||
190 | |||
191 | #define boot_rww_busy() (__SPM_REG & (uint8_t)_BV(__COMMON_ASB))
|
||
192 | |||
193 | /** \ingroup avr_boot
|
||
194 | \def boot_spm_busy()
|
||
195 | Check if the SPM instruction is busy. */
|
||
196 | |||
197 | #define boot_spm_busy() (__SPM_REG & (uint8_t)_BV(__SPM_ENABLE))
|
||
198 | |||
199 | /** \ingroup avr_boot
|
||
200 | \def boot_spm_busy_wait()
|
||
201 | Wait while the SPM instruction is busy. */
|
||
202 | |||
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 | |||
583 | /** \ingroup avr_boot
|
||
584 | \def GET_LOW_FUSE_BITS
|
||
585 | address to read the low fuse bits, using boot_lock_fuse_bits_get
|
||
586 | */
|
||
587 | #define GET_LOW_FUSE_BITS (0x0000) |
||
588 | /** \ingroup avr_boot
|
||
589 | \def GET_LOCK_BITS
|
||
590 | address to read the lock bits, using boot_lock_fuse_bits_get
|
||
591 | */
|
||
592 | #define GET_LOCK_BITS (0x0001) |
||
593 | /** \ingroup avr_boot
|
||
594 | \def GET_EXTENDED_FUSE_BITS
|
||
595 | address to read the extended fuse bits, using boot_lock_fuse_bits_get
|
||
596 | */
|
||
597 | #define GET_EXTENDED_FUSE_BITS (0x0002) |
||
598 | /** \ingroup avr_boot
|
||
599 | \def GET_HIGH_FUSE_BITS
|
||
600 | address to read the high fuse bits, using boot_lock_fuse_bits_get
|
||
601 | */
|
||
602 | #define GET_HIGH_FUSE_BITS (0x0003) |
||
603 | |||
604 | /** \ingroup avr_boot
|
||
605 | \def boot_lock_fuse_bits_get(address)
|
||
606 | |||
607 | Read the lock or fuse bits at \c address.
|
||
608 | |||
609 | Parameter \c address can be any of GET_LOW_FUSE_BITS,
|
||
610 | GET_LOCK_BITS, GET_EXTENDED_FUSE_BITS, or GET_HIGH_FUSE_BITS.
|
||
611 | |||
612 | \note The lock and fuse bits returned are the physical values,
|
||
613 | i.e. a bit returned as 0 means the corresponding fuse or lock bit
|
||
614 | is programmed.
|
||
615 | */
|
||
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 | |||
652 | /** \ingroup avr_boot
|
||
653 | \def boot_signature_byte_get(address)
|
||
654 | |||
655 | Read the Signature Row byte at \c address. For some MCU types,
|
||
656 | this function can also retrieve the factory-stored oscillator
|
||
657 | calibration bytes.
|
||
658 | |||
659 | Parameter \c address can be 0-0x1f as documented by the datasheet.
|
||
660 | \note The values are MCU type dependent.
|
||
661 | */
|
||
662 | |||
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 | |||
697 | /** \ingroup avr_boot
|
||
698 | \def boot_page_fill(address, data)
|
||
699 | |||
700 | Fill the bootloader temporary page buffer for flash
|
||
701 | address with data word.
|
||
702 | |||
703 | \note The address is a byte address. The data is a word. The AVR
|
||
704 | writes data to the buffer a word at a time, but addresses the buffer
|
||
705 | per byte! So, increment your address by 2 between calls, and send 2
|
||
706 | data bytes in a word format! The LSB of the data is written to the lower
|
||
707 | address; the MSB of the data is written to the higher address.*/
|
||
708 | |||
709 | /** \ingroup avr_boot
|
||
710 | \def boot_page_erase(address)
|
||
711 | |||
712 | Erase the flash page that contains address.
|
||
713 | |||
714 | \note address is a byte address in flash, not a word address. */
|
||
715 | |||
716 | /** \ingroup avr_boot
|
||
717 | \def boot_page_write(address)
|
||
718 | |||
719 | Write the bootloader temporary page buffer
|
||
720 | to flash page that contains address.
|
||
721 |
|
||
722 | \note address is a byte address in flash, not a word address. */
|
||
723 | |||
724 | /** \ingroup avr_boot
|
||
725 | \def boot_rww_enable()
|
||
726 | |||
727 | Enable the Read-While-Write memory section. */
|
||
728 | |||
729 | /** \ingroup avr_boot
|
||
730 | \def boot_lock_bits_set(lock_bits)
|
||
731 | |||
732 | Set the bootloader lock bits.
|
||
733 | |||
734 | \param lock_bits A mask of which Boot Loader Lock Bits to set.
|
||
735 | |||
736 | \note In this context, a 'set bit' will be written to a zero value.
|
||
737 | Note also that only BLBxx bits can be programmed by this command.
|
||
738 | |||
739 | For example, to disallow the SPM instruction from writing to the Boot
|
||
740 | Loader memory section of flash, you would use this macro as such:
|
||
741 | |||
742 | \code
|
||
743 | boot_lock_bits_set (_BV (BLB11));
|
||
744 | \endcode
|
||
745 | |||
746 | \note Like any lock bits, the Boot Loader Lock Bits, once set,
|
||
747 | cannot be cleared again except by a chip erase which will in turn
|
||
748 | also erase the boot loader itself. */
|
||
749 | |||
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 | |||
788 | /** \ingroup avr_boot
|
||
789 | |||
790 | Same as boot_page_fill() except it waits for eeprom and spm operations to
|
||
791 | complete before filling the page. */
|
||
792 | |||
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 | |||
800 | /** \ingroup avr_boot
|
||
801 | |||
802 | Same as boot_page_erase() except it waits for eeprom and spm operations to
|
||
803 | complete before erasing the page. */
|
||
804 | |||
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 | |||
812 | /** \ingroup avr_boot
|
||
813 | |||
814 | Same as boot_page_write() except it waits for eeprom and spm operations to
|
||
815 | complete before writing the page. */
|
||
816 | |||
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 | |||
824 | /** \ingroup avr_boot
|
||
825 | |||
826 | Same as boot_rww_enable() except waits for eeprom and spm operations to
|
||
827 | complete before enabling the RWW mameory. */
|
||
828 | |||
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 | |||
836 | /** \ingroup avr_boot
|
||
837 | |||
838 | Same as boot_lock_bits_set() except waits for eeprom and spm operations to
|
||
839 | complete before setting the lock bits. */
|
||
840 | |||
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_ */ |