Understanding the bootloader (Arduino)

Hi all, I'd like to better understand the Arduino bootloader, so I've read the source code (ATmegaBOOT_168.c).
First of all, I've understood that the bootloader is an optional piece of code that make possible uploading a sketch without the need of an external programmer.
Now, in the source code, I see a function pointer "void (*app_start)(void) = 0x0000;" that made me wonder if is our code (sketch) really written at 0x0000 in the MCU? and if yes, at which address is the bootloader written ?

Many thanks

Look at the datasheet, page 265 for a pictoral view of the addresses, and page 275 for the actual addresses for the various chips (ATmega48A/PA/88A/PA/168A/PA/328/P).

Bootloader basically starts at the end of memory less the bootloader size, while the sketch starts at the beginning of memory.

is our code (sketch) really written at 0x0000 in the MCU?

Yep. Actually, the "vector table" starts at 0, with the first vector being the "reset vector." But vectors are actually AVR instructions, so... you do wind up with actual code at 0.

Chips with "bootloader support" have options that can be set to have reset cause the execution to start at an alternate address (near the end of flash.)

Luca-91:
Hi all, I'd like to better understand the Arduino bootloader, so I've read the source code (ATmegaBOOT_168.c).
First of all, I've understood that the bootloader is an optional piece of code that make possible uploading a sketch without the need of an external programmer.
Now, in the source code, I see a function pointer "void (*app_start)(void) = 0x0000;" that made me wonder if is our code (sketch) really written at 0x0000 in the MCU? and if yes, at which address is the bootloader written ?

Many thanks

To get a better understanding of the bootloader code, read Section 27 of the Atmel DataSheet (Atmel 8271).

the function pointer that is initialized to 0x0000 points to the nominal Reset vector. That memory location contains an JMP instruction to the start of your application code. Normally this jump instruction at address 0x0000 is executed whenever one of these events happen: Reset, Power-on, Brown-out or Watchdog.
But, this standard operation is overridden by the BootFlag fuse. If the BootFlag fuse is set, the Reset vector is moved to the start of the BootPage. So any hardware reset is directed to this address. The standard UNO bootloader uses a 1k bootpage, since the UNO has 32k of program space, this means the effective Reset vector is at 0x7C00. Which is were the 'BootLoader' is actually located in memory.

calling app_start(resetReason); jumps to 0x0000 and starts executing code from that location.
0x0000 contains a JMP main instruction.

When you upload your Arduino sketch code, it fills flash with the HEX image of your compiled sketch. 0x0000 is actually filled with a jump to the main(); function of your sketch.

Chuck.

BTW, these days "Optiboot" is more common than "ATmegaboot"
But the principles are the same, and ATmegaboot might be easier reading.

chucktodd:
ToThe standard UNO bootloader uses a 1k bootpage, since the UNO has 32k of program space, this means the effective Reset vector is at 0x7C00. Which is were the 'BootLoader' is actually located in memory.

I think it's 512 bytes for the Uno.

From boards.txt: "uno.upload.maximum_size=32256"

A tempting plum to pluck if you're running out of space.

jboyton:
I think it's 512 bytes for the Uno.

From boards.txt: "uno.upload.maximum_size=32256"

A tempting plum to pluck if you're running out of space.

That sounds good, I thought they were still using the 1k bootcode, and that you had to manually replace it with Optiboot if you wanted to reduce the memory usage?

Good to know.

Chuck.

Strangely Nano and Pro Mini are still using 2KB bootloaders. The little board with the big bootloader...

Atmega8 has/had a 1k bootloader.
ATmega168 and early 328 boards had the 2k atmegaboot bootloader, because optiboot didn't exist when they first came out.
Uno, being a brand-new board type, made a clean switch to the 512 byte Optiboot. There's no reason that older boards can't use optiboot as well, other than a desire not to change boot process of an existing product.
(The whole point of OptiLoader was to be able to take it to a get-together and put optiboot on everyone's old boards, saving them 512 to 1.5k bytes of space.)

Thanks to everybody for the help!

Okay, just another question.. I've followed a tutorial on how to write a blink led in pure C with avr-libc, and i was able to produce an .hex file. Since flashing this .hex file would write it starting from 0x0000 I assume that the first istruction would be a jump to the main function (as stated by chucktodd), so I've run avr-objdump on the .hex and I've got:

led.hex:     file format ihex


Disassembly of section .sec1:

00000000 <.sec1>:
   0:   0c 94 56 00     jmp     0xac    ;  0xac
   4:   0c 94 60 00     jmp     0xc0    ;  0xc0
   8:   0c 94 60 00     jmp     0xc0    ;  0xc0
   c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  10:   0c 94 60 00     jmp     0xc0    ;  0xc0
  14:   0c 94 60 00     jmp     0xc0    ;  0xc0
  18:   0c 94 60 00     jmp     0xc0    ;  0xc0
  1c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  20:   0c 94 60 00     jmp     0xc0    ;  0xc0
  24:   0c 94 60 00     jmp     0xc0    ;  0xc0
  28:   0c 94 60 00     jmp     0xc0    ;  0xc0
  2c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  30:   0c 94 60 00     jmp     0xc0    ;  0xc0
  34:   0c 94 60 00     jmp     0xc0    ;  0xc0
  38:   0c 94 60 00     jmp     0xc0    ;  0xc0
  3c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  40:   0c 94 60 00     jmp     0xc0    ;  0xc0
  44:   0c 94 60 00     jmp     0xc0    ;  0xc0
  48:   0c 94 60 00     jmp     0xc0    ;  0xc0
  4c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  50:   0c 94 60 00     jmp     0xc0    ;  0xc0
  54:   0c 94 60 00     jmp     0xc0    ;  0xc0
  58:   0c 94 60 00     jmp     0xc0    ;  0xc0
  5c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  60:   0c 94 60 00     jmp     0xc0    ;  0xc0
  64:   0c 94 60 00     jmp     0xc0    ;  0xc0
  68:   0c 94 60 00     jmp     0xc0    ;  0xc0
  6c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  70:   0c 94 60 00     jmp     0xc0    ;  0xc0
  74:   0c 94 60 00     jmp     0xc0    ;  0xc0
  78:   0c 94 60 00     jmp     0xc0    ;  0xc0
  7c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  80:   0c 94 60 00     jmp     0xc0    ;  0xc0
  84:   0c 94 60 00     jmp     0xc0    ;  0xc0
  88:   0c 94 60 00     jmp     0xc0    ;  0xc0
  8c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  90:   0c 94 60 00     jmp     0xc0    ;  0xc0
  94:   0c 94 60 00     jmp     0xc0    ;  0xc0
  98:   0c 94 60 00     jmp     0xc0    ;  0xc0
  9c:   0c 94 60 00     jmp     0xc0    ;  0xc0
  a0:   0c 94 60 00     jmp     0xc0    ;  0xc0
  a4:   0c 94 60 00     jmp     0xc0    ;  0xc0
  a8:   0c 94 60 00     jmp     0xc0    ;  0xc0
  ac:   11 24           eor     r1, r1
  ae:   1f be           out     0x3f, r1        ; 63
  b0:   cf ef           ldi     r28, 0xFF       ; 255
  b2:   da e0           ldi     r29, 0x0A       ; 10
  b4:   de bf           out     0x3e, r29       ; 62
  b6:   cd bf           out     0x3d, r28       ; 61
  b8:   0e 94 62 00     call    0xc4    ;  0xc4
  bc:   0c 94 78 00     jmp     0xf0    ;  0xf0
  c0:   0c 94 00 00     jmp     0       ;  0x0
  c4:   25 9a           sbi     0x04, 5 ; 4
  c6:   2d 9a           sbi     0x05, 5 ; 5
  c8:   2f ef           ldi     r18, 0xFF       ; 255
  ca:   83 ed           ldi     r24, 0xD3       ; 211
  cc:   90 e3           ldi     r25, 0x30       ; 48
  ce:   21 50           subi    r18, 0x01       ; 1
  d0:   80 40           sbci    r24, 0x00       ; 0
  d2:   90 40           sbci    r25, 0x00       ; 0
  d4:   e1 f7           brne    .-8             ;  0xce
  d6:   00 c0           rjmp    .+0             ;  0xd8
  d8:   00 00           nop
  da:   2d 98           cbi     0x05, 5 ; 5
  dc:   2f ef           ldi     r18, 0xFF       ; 255
  de:   83 ed           ldi     r24, 0xD3       ; 211
  e0:   90 e3           ldi     r25, 0x30       ; 48
  e2:   21 50           subi    r18, 0x01       ; 1
  e4:   80 40           sbci    r24, 0x00       ; 0
  e6:   90 40           sbci    r25, 0x00       ; 0
  e8:   e1 f7           brne    .-8             ;  0xe2
  ea:   00 c0           rjmp    .+0             ;  0xec
  ec:   00 00           nop
  ee:   eb cf           rjmp    .-42            ;  0xc6
  f0:   f8 94           cli
  f2:   ff cf           rjmp    .-2             ;  0xf2

AH!!!! The first instruction is a jump at 0xac! and 0xAC is where I think the main() is! (Please correct me if I'm wrong...)

That make sense, but why there are all those jmp to 0xC0 (that jump again to 0x0) ???

Thanks!

The very first instruction is jump to program prolog which must be executed before the main function. It ensures setting the registers, initialize and clear global variables etc. In your case on address ACh is EOR R1,R1 instruction. It clears R1 register. It is a rule to have R1=0.

BTW: You can use avr-objdump with -S switch to intermix the source code with ASM. It could be interesting reading for you.

The first instruction is a jump at [to] 0xac! and 0xAC is where I think the main() is! (Please correct me if I'm wrong...)

Correct, except for the typo.

That make sense, but why there are all those jmp to 0xC0 (that jump again to 0x0)

Those are the other vectors in the "vector table" that I mentioned; they are jumps to the routines that handle the other interrupts that are possible on your chip. 0xC0 is the "unused interrupt" vector; if you don't define an ISR for a particular interrupt, the compiler will point it at the (rather useless) "reset the chip, sort of" code that you noticed.

You can use avr-objdump with switch to intermix the source code with ASM.

To get the mixed output (with "-S"), you'll have to use the .elf file rather than the .hex file as input.

Thank you both.

Where can I read about the other interrupts? and what kind of interrupts are there? I imagine that there is something like the PIT (programmable interval timer) on x86.

Start with the datasheet. They are all listed there, and do not copy paste well here I have found.

12.4 Interrupt Vectors in ATmega328 and ATmega328P
Table 12-6. Reset and Interrupt Vectors in ATmega328 and ATmega328P

All 26 are shown.

Thanks, I'll give it a read asap.

Where can I read about the other interrupts? and what kind of interrupts are there?

The datasheet: http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
Warning: ~650 pages. Interrupts in general are desribed in section 12, but you have to read the individual peripheral sections to understand the details of that particular peripheral.

/* External Interrupt Request 0 */
/* External Interrupt Request 1 */
/* Pin Change Interrupt Request 0 */
/* Pin Change Interrupt Request 0 */
/* Pin Change Interrupt Request 1 */
/* Watchdog Time-out Interrupt */
/* Timer/Counter2 Compare Match A */
/* Timer/Counter2 Compare Match A */
/* Timer/Counter2 Overflow */
/* Timer/Counter1 Capture Event */
/* Timer/Counter1 Compare Match A */
/* Timer/Counter1 Compare Match B */ 
/* Timer/Counter1 Overflow */
/* TimerCounter0 Compare Match A */
/* TimerCounter0 Compare Match B */
/* Timer/Couner0 Overflow */
/* SPI Serial Transfer Complete */
/* USART Rx Complete */
/* USART, Data Register Empty */
/* USART Tx Complete */
/* ADC Conversion Complete */
/* EEPROM Ready */
/* Analog Comparator */
/* Two-wire Serial Interface */
/* Store Program Memory Read */

Luca-91
If you are trying to understand to all "magic behind the curtain" before the main(), try to debug the program in AtmelStudio.
Just you have to put somewhere in your code the jump to zero address or null function call to reach the beginning and you can trace it per instruction.

westfw

You can use avr-objdump with switch to intermix the source code with ASM.

To get the mixed output (with "-S"), you'll have to use the .elf file rather than the .hex file as input.

Yes, that was exactly what I meant. Thanks.