understanding opticode.c bootloader

I am trying to understand the optiboot code for the arduino located here:

arduino/hardware/arduino/avr/bootloaders/optiboot/optiboot.c

I have been able to figure out most of it (this was helpful) but am having problems with the following. The most helpful would be source documents so I can answer these questions for myself in the future. Can you help me with the following?

asm("  .section .version\n"
    "optiboot_version:  .word " MAKEVER(OPTIBOOT_MAJVER, OPTIBOOT_MINVER) "\n"
    "  .section .text\n");

Where is the ".section" command documented? Where is ".version" located? I expected the data sheet to answer the first question and this to answer the 2nd but no dice.

#define rstVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+4))
#define wdtVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+6))

where are SPM_PAGESIZE, VIRTUAL_BOOT_PARTITION, and SOFT_UART defined? Why do these lines of code use an offset?

void appStart() {
    watchdogConfig(WATCHDOG_OFF);
    __asm__ __volatile__ (
#ifdef VIRTUAL_BOOT_PARTITION
        // Jump to WDT vector
        "ldi r30,4\n"
        "clr r31\n"
#else
        // Jump to RST vector
        "clr r30\n"
        "clr r31\n"
#endif
        "ijmp\n"
    );

I understand that this is loading the Z-register and jumping to the address stored in it. I further understand jumping to the RST vector. However, what is a VIRTUAL_BOOT_PARTITION? Where is it defined?

UBRR0L = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 );

From the equation listed in table 20-1 here, I would have thought the equation should be:

UBRR0L = (uint8_t)( (F_CPU) / (BAUD_RATE * 8L) - 1 );

Why is there an extra BAUD_RATE * 4L there? Why is it using 4L and 8L rather than just 4 and 8?

if(ch == STK_GET_PARAMETER) {
    unsigned char which = getch();
    verifySpace();
    if (which == 0x82) {
        // Send optiboot version as "minor SW version"
        putch(OPTIBOOT_MINVER);
    } else if (which == 0x81) {
        //0x81 - major version
        putch(OPTIBOOT_MAJVER);
    } else {
        /*
         * GET PARAMETER returns a generic 0x03 reply for
         * other parameters - enough to keep Avrdude happy
         */
        putch(0x03);
    }
}

Where is 0x03 defined? The comments indicate it is a "do nothing" response, but how would I know that? I am looking here but perhaps there is another source.

if(ch == STK_LOAD_ADDRESS) {
    // LOAD ADDRESS
    uint16_t newAddress;
    newAddress = getch();
    newAddress = (newAddress & 0xff) | (getch() << 8);
#ifdef RAMPZ
    // Transfer top bit to RAMPZ
    // note: RAMPZ is only defined for devices with > 64K of memory
    RAMPZ = (newAddress & 0x8000) ? 1 : 0;
#endif
    newAddress += newAddress; // Convert from word address to byte address
    address = newAddress;
    verifySpace();
}

what is RAMPZ? what is being done in the "newAddress += newAddress" line? The comment does not give me much understanding.

// Copy buffer into programming buffer
bufPtr = buff;
addrPtr = (uint16_t)(void*)address;
ch = SPM_PAGESIZE / 2;

do {

    uint16_t a;
    a = *bufPtr++;
    a |= (*bufPtr++) << 8;
    __boot_page_fill_short((uint16_t)(void*)addrPtr,a);
    addrPtr += 2;

} while (--ch);

// Write from programming buffer
__boot_page_write_short((uint16_t)(void*)address);
boot_spm_busy_wait();

It looks like this code is writing pages twice. What is the difference between "__boot_page_fill_short" and "__boot_page_write_short"? I took a look here but didn't see the distinction.

Thanks!

esquire70:
I am trying to understand the optiboot code for the arduino located here:

arduino/hardware/arduino/avr/bootloaders/optiboot/optiboot.c

You need to provide a URL, or a version number, we can't tell what you have installed on your PC!

I have been able to figure out most of it (this was helpful) but am having problems with the following. The most helpful would be source documents so I can answer these questions for myself in the future. Can you help me with the following?

asm("  .section .version\n"

"optiboot_version:  .word " MAKEVER(OPTIBOOT_MAJVER, OPTIBOOT_MINVER) "\n"
    "  .section .text\n");



Where is the ".section" command documented? Where is ".version" located? I expected the [data sheet](http://www.atmel.com/images/doc2545.pdf) to answer the first question and [this](http://www.nongnu.org/avr-libc/user-manual/mem_sections.html) to answer the 2nd but no dice.

For anything in asm() you need to consult gcc assembler manual, and possibly the linker manual. ".section" specifies a section for the code/data to go in, which will be located by the linker. ".version" is a custom section name. ".text" is a standard section name for code.

The purpose is probably to put optiboot_version at a specific address, you would need to look at the linker script to see where.

I suggest you look here: Google Code Archive - Long-term storage for Google Code Project Hosting.
It's the latest version, and there is "some" additional documentation (probably not enough to answer your questions.)

Where is the ".section" command documented? Where is ".version" located?

.section is a gnu assembler directive. The GNU Assembler (one source; generic.)
.version is an arbitrary name.
These are essentially commands that transfer information from the source code to the linker, which will then put together the various bits of information in various places in memory, based on the "linker map" (file provided by the infrastructure) and commands given to it explicitly ("-Wl,--section-start=.version=0x3ffe" in the Makefile.) The newer code does this in C with attribute instead of using inline assembler, but it does the same thing.

where are SPM_PAGESIZE, VIRTUAL_BOOT_PARTITION, and SOFT_UART defined?

SPM_PAGESIZE comes from the atmel-provided include files via <avr/io.h>; it's a chip-specific value.
VIRTUAL_BOOT_PARTITION and SOFT_UART are user "preprocessor" variables provided by the makefile (or not.) They control options of the program, implemented manually via "#ifdef" and similar.

Why do these lines of code use an offset?

These are "manually defined" variables. Normally you'd say "char buff[SPM_PAGESIZE]; int rstvector, wdtvector;" But C would want to include code to initialize those variables, which would be "too much" extra code for optiboot to fit in 512 bytes. So instead we define buff as being where we know the first byte of RAM is and rstvetcor as the first byte after buff, and so on.

VIRTUAL_BOOT_PARTITION is a hack for implementing a bootloader on parts that don't have "start at the bootloader address after reset" support. So the start vector always has to be at 0, where it will be overwritten by the application's vectors. Except the bootloader "cleverly substitutes" its own start address for the start of the application during "upload", causing the bootloader to always run first. I'm not sure that this feature is currently working - it was designed for chips like the tiny85, and I've never tried running it on that chip during my tenure as optiboot maintainer. :frowning:

Why is there an extra BAUD_RATE * 4L there? Why is it using 4L and 8L rather than just 4 and 8?

It's doing rounding. int(calculated_divisor + 0.5), except since it's all integers we use algebra and multiply the 0.5 by something big enough to make it an integer. (Hmm. This is probably silly; I should just let it use compile-time floating point. like __delay_ms())

Where is 0x03 defined? The comments indicate it is a "do nothing" response

It's not defined. It's a value for various parameters. See the "parameter definitions" section of the document. Optiboot says "yeah, hardware version 3. Status LED value 3. Target voltage 3. No topcard detected (3)." (gross, eh?)

what is RAMPZ? what is being done in the "newAddress += newAddress" line?

RAMPZ is the "extra byte" of flash addressing for parts with more than 64k words.
The addition (multiply by 2) converts from (16bit) word addresses that Atmel likes to use in various place, to byte addresses used by the hardware and avr-libc in most places.

What is the difference between "__boot_page_fill_short" and "__boot_page_write_short"?

Flash is written a full page at a time, rather than a byte at a time. To write a page of flash, you fill a RAM buffer in the flash controller peripheral (boot_page_fill), and then give a "boot_page_write" to actually write that buffer to the flash memory. All together, there are three copies (buff, which is filled from the serial comm, the flash page buffer filled by page_fill, and the flash itself.)

Wow. Thank you so much for the detailed reply. I am learning a lot and you have opened up a lot of new documentation for review. I will be reviewing it tonight and may post some questions soon.

One follow-up question on the appropriate address locations for ".text" and ".version". I searched the Atmel 168 data sheet for values "0x3e00" and "0x3ffe" (from the Makefile) and didnt find anything above "0x1000". How would I come up with those values? Thanks.

I searched the Atmel 168 data sheet for values "0x3e00" and "0x3ffe" (from the Makefile) and didnt find anything above "0x1000".

Those are addresses in flash memory. 0x3e00 is "16k" - 512, which is where you'd want the bootloader to start (near the end of memory) on an ATmega168. 0x3ffe = 16k - 2; the version number is stored in the last two bytes. The datasheet has the first of these numbers in the tables labeled "boot size configuration" in the boot loader support chapter, except in the datasheet they use word address that have to be multiplied by two to get the numbers you see in the code/makefile.

Great. I think I understand it all except one final piece. When "ch == STK_READ_SIGN", the code responds with

putch(SIGNATURE_0);
putch(SIGNATURE_1);
putch(SIGNATURE_2);

I haven't been able to find where SIGNATURE_* is defined. Where are those values coming from?

The signature bytes are also from the <avr/io.h> include chain (eventually something like avr/iom328p.h)

(Note that this means that the signature value returned is COMPILED IN, and not read from the chip itself. This is what permits the arduino hack of burning a 328P bootloader into a 328, and thereafter having the IDE think it's a normal 328P)

Makes sence. I must have been grepping the wrong directory. Thank you so much for your thorough response. This has been super helpful.