[ASKED AND ANSWERED] Double checking about bootloading larger addresses

I am thinking of expanding the possible program size for over the air updates and when I look at the boot loader it seems like it should be easy enough to do. I’m not sure I want to actually do it, and it could be way more in-depth than this but it looks like it is just expanding the data size, using the larger bootloader memory option, and using the extended macros. Might need to use the normal macros for addresses less than 2 bytes and extended for larger ones. I’m not sure yet.

I have 2 questions to get opinions on. What is with the circular casting they chose to do, and am I missing something obvious?

uint16_t i,b;
MACRO((uint16_t)(void*)i,b);

I don’t understand the casting they did. It doesn’t make any sense to me. That combined with when I half heartedly tried changing to a large data type uint32_t if gave me an error that it didn’t give before with the uint16_t.

Here are the errors it through
Error:
cast to pointer from integer of different size
and
cast from pointer to integer of different size

and here are the unmodified and modified code sections.

I’m not so much looking for actual answers as much as thoughts and opinions.

Unmodified Code Section.

uint16_t b, i, nextAddress=0;
    
    LED_PIN |= _BV(LED);
    for (i=0; i<imagesize; i+=2)
    {
      
      //read 2 bytes (16 bits) from flash image, transfer them to page buffer
      b = FLASH_readByte(i+12); // flash image starts at position 10 on the external flash memory: FLX:XX:FLASH_IMAGE_BYTES_HERE...... (XX = two size bytes)
      b |= FLASH_readByte(i+13) << 8; //bytes are stored big endian on external flash, need to flip the bytes to little endian for transfer to internal flash
      __boot_page_fill_short((uint16_t)(void*)i,b);

      //when 1 page is full (or we're on the last page), write it to the internal flash memory
      if ((i+2)%SPM_PAGESIZE==0 || (i+2==imagesize))
      {
        __boot_page_erase_short((uint16_t)(void*)nextAddress); //(i+2-SPM_PAGESIZE)
        boot_spm_busy_wait();
        // Write from programming buffer
        __boot_page_write_short((uint16_t)(void*)nextAddress ); //(i+2-SPM_PAGESIZE)
        boot_spm_busy_wait();
        nextAddress += SPM_PAGESIZE;
      }
    }

Modified Code Section.

uint32_t b, i, nextAddress=0;
    
    LED_PIN |= _BV(LED);
    for (i=0; i<imagesize; i+=2)
    {
      
      //read 2 bytes (16 bits) from flash image, transfer them to page buffer
      b = FLASH_readByte(i+12); // flash image starts at position 10 on the external flash memory: FLX:XX:FLASH_IMAGE_BYTES_HERE...... (XX = two size bytes)
      b |= FLASH_readByte(i+13) << 8; //bytes are stored big endian on external flash, need to flip the bytes to little endian for transfer to internal flash
      __boot_page_fill_extended_short((uint32_t)(void*)i,b);

      //when 1 page is full (or we're on the last page), write it to the internal flash memory
      if ((i+2)%SPM_PAGESIZE==0 || (i+2==imagesize))
      {
        __boot_page_erase_extended_short((uint32_t)(void*)nextAddress); //(i+2-SPM_PAGESIZE)
        boot_spm_busy_wait();
        // Write from programming buffer
        __boot_page_write_extended_short((uint32_t)(void*)nextAddress ); //(i+2-SPM_PAGESIZE)
        boot_spm_busy_wait();
        nextAddress += SPM_PAGESIZE;
      }
    }

With dozens of different Arduino boards, and who knows how many different boot loaders (a LEAST a dozen), don't you think it would be nice if you provided at least a CLUE as to WHAT board and bootloader you're even talking about?

Pretend nobody here can actually see what hardware and software you're using....

Regards,
Ray L.

Thanks for the rapid response. Sorry for not giving enough information.

I meant the question as more of an abstraction to try to figure out why the original author chose the castings that they chose, but sure I can give specifics.

in that exact example
that code is from the Dual Optiboot bootloader by Felix Ratsu at Low Power Labs.
the chip I was looking at is the atmega1284p
and the board would be a deriviative of the moteinomega.

Sorry I didn’t give specifics, its not so much a concrete problem as an over explained question of why would someone declare a variable uint16_t then cast it as a void* and then cast it at a uint16_t again.

And then a different question of why would it compile when done as a uint16_t but error out when the same operation is done as a uint32_t?

So that’s 2 questions that I erroneously thought would not be board, chip, or bootloader specific.

why would someone do this and why would it work?

uint16_t i;
SomeFunctionOrMacro((uint16_t)(void*)i);

and yet this doesn’t

uint32_t i;
SomeFunctionOrMacro((uint32_t)(void*)i);

wouldn’t this do the same thing without all the casting

uint32_t i;
SomeFunctionOrMacro(i)

optiboot.c:603: warning: cast to pointer from integer of different size
optiboot.c:603: warning: cast from pointer to integer of different size
optiboot.c:608: warning: cast to pointer from integer of different size
optiboot.c:608: warning: cast from pointer to integer of different size
optiboot.c:611: warning: cast to pointer from integer of different size
optiboot.c:611: warning: cast from pointer to integer of different size

the output response doesn't make any since to me

Because a pointer on a particular platform has a size, and even on a AVR device with over 64k flash/ram that size is still 16 bits.

You need to look at the AVR instruction set and the op-codes for addressing FLASH and you will see that...

1.1 RAMPX, RAMPY, and RAMPZ
Registers concatenated with the X-, Y-, and Z-registers enabling indirect addressing of the whole data space on MCUs with more than 64KB data space, and constant data fetch on MCUs with more than 64KB program space.

1.2. RAMPD
Register concatenated with the Z-register enabling direct addressing of the whole data space on MCUs with more than 64KB data space.

1.3. EIND
Register concatenated with the Z-register enabling indirect jump and call to the whole program space on MCUs with more than 64K words (128KB) program space.

These registers are essentially segment registers. So in that regard the addressing scheme is very much like a 16-bit 80x86 which is also able to address over 64k, but still only has 16-bit pointers.

I’m not familiar with your boot loader, but the NRF24 modified optiboot loader that I’ve played with (from what I recall) loads the 3rd byte of the supplied address into RAMPZ ready for the SPM instruction.

Edit: As for the casting to void* and back again, I’m not sure I see the point of that either. But you’ll need to look at the macro/function for writing to flash to see if they can handle 24-bit addresses. For the optiboot implementation I’m familiar with they can’t. They just use inline assembler to load the 16-bit Z register with the FLASH address and then emit a SPM instruction.

That of course relies on the RAMPZ register already having been set correctly. Which it is if you look at lines 610-613 here. Of course that is only done in builds for devices which have a RAMPZ (hence the #ifdef).

Thank you very much. Everything you said makes perfect sense to me, and I'm glad that I am not the only one that doesn't understand the casting and cast back. What you said about the flash address size does make total since especially thinking about the assembler instructions. Cool and thank you. I guess I am going to close the topic since I have my answer.

Thank you again.