Comfused with AVR bootloader flash section

Go To Last Post
64 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


 

I am porting an open-source bootloader to a commercial project I am working on. Unfortunately, the memory footprint size of this bootloader is larger than the 8kb of the maximum flash bootloader section of AT90CAN128 chip (not supported in the simulator) and I have to split the bootloader within the memory into two sections so that the flash manipulation functions to be positioned in the bootloader section.

 

I have tried positioning my flash write function to the address 0x1E000, which is the starting address of the bootloader section, by forcing its address as:

blt_bool __attribute__((at(0x1E000))) FlashWrite(blt_addr addr, blt_int32u len, blt_int8u *data);

The application crashes, or runs for a few seconds and crashes. It looks like the AVR is not very happy about the position-independent code.

 

Maybe I need to transfer all the functions that are called from this function etc, hence the whole section .booloader; but I am having difficulties doing that. When I set flags for the Linker via the IDE such as -Tbootloader,  or  LDSECTION = --section-start=.bootloader=0x1E000 etc give me build errors (-Ttext=0x1E000 works but it is not what I need in this case):

blt_bool __attribute__ ((section (".bootloader"))) FlashWrite(blt_addr addr, blt_int32u len, blt_int8u *data);

 

 

How do I solve this problem of a bootloader not fitting in the bootloader section? (Note: I have not tried the size optimization because I do not have a license for the XC8 Pro Compiler).

 

In the worst case that having a large bootloader is a dead end, I am thinking of having three applications in memory: the ported bootloader to manage the file transfer, etc, the main application of course, and another bootloader at the end of the flash dedicated to copy data to flash from another source (maybe an external flash).

 

P.S.

I have tried this simple nicely written example that I found on this website and I am convinced that it worked for me too by observing the flash contents after doing a read of flash, also commenting the flash write function and do another read of flash, and comparing the differences:

https://www.pocketmagic.net/simp...

 

This topic has a solution.
Last Edited: Thu. Jun 3, 2021 - 10:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AmVRosia wrote:
How do I solve this problem of a bootloader not fitting in the bootloader section?

  • Try different AVR GCC
  • Split bootloader then try IAR EWAVR Kickstart (4KB)

FSF AVR GCC's efficiency varies by subversion; likewise with Microchip AVR GCC.

IAR EWAVR is the first AVR C compiler; IIRC, Atmel and IAR teamed.

AmVRosia wrote:
(Note: I have not tried the size optimization because I do not have a license for the XC8 Pro Compiler).
MPLAB XC8 PRO has a trial duration; afterwards, subscription seems reasonable as bootloader development is aperiodic whereas application development is periodic (typically two to six weeks per cycle though quarters are popular)

AmVRosia wrote:
... to copy data to flash from another source (maybe an external flash).
akin to what SerialBootloader does; network stack is in the application, application writes to external flash, bootloader reads from USB/UART/TWI/etc or external flash.

 

edit :

  • refactor

Try to move functionality from bootloader into the application; bootloader's requirements should be minimally essential (tree - apple, pear, peach, etc)

Spin the design again (prune the tree)

 


Setting Toolchain Flavors | Microchip Studio

 

AN3419 - Getting Started with IAR Embedded Workbench for AVR

IAR Embedded Workbench for AVR | IAR Systems (bottom)

 

MPLAB® XC Compilers | Microchip Technology

[approx 1/3 page]

The MPLAB XC C Compiler contains a free, 60-day trial of a PRO license for evaluation when activated. 

MPLAB® XC Compiler Licenses - YouTube (5m2s)

 

AN_8390 AVR2054: SerialBootloader User Guide

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Thu. May 13, 2021 - 01:21 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Remember that when you start doing the LPMs the entire app space will read back as 0xFFFF so it's not just the SPM routines themselves that may need to be forced above the BLS line but perhaps the data access stuff too.

 

<avr/boot.h> has a macro called BOOTLOADER_SECTION (which is really just an __attribute__((section(".bootloader"))) )

 

https://www.nongnu.org/avr-libc/...

 

that you can pepper routines with so:

BOOTLOADER_SECTION void do_SPM(char * buf, int len) {
    // stuff
}

BOOTLOADER_SECTION uint8_t geNextByte() {
    // stuff
}

BOOTLOADER_SECTION void bootloaderLoop() {
    while (data) {
        for (int i = 0; i < SPM_PAGE_SIZE; i++) {
            buff[i] = getNextByte()
        }
        do(_SPM(buff, SPM_PAGE_SIZE));
    }
}

void a_function() {
    // this is not tagged with BOOTLOADER_SECTION so will be placed at the "start"
}

void main() {
    // this is not BOOTLOADER_SECTION so will be down near the start

    a_function(); // call nearby function

    bootloaderLoop(); // call up to the functions grouped at 0x1E000+
}

If you have something like this and -Wl,--section-start=.bootloader=0x1E000 then all the functions tagged as "BOOTLOADER_SECTION" will be placed from 0x1E000 upwards but main()/a_function() will be down near the start (which is where you have set -Wl,--section-start=.text=0x???? where ???? is some address in high memory but below the BOOTSZ BLS boundary.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AmVRosia wrote:
I am porting an open-source bootloader to a commercial project

I may help to know what bootloader your talking about, can you provide a link, please!

Jim

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Thank you all for your time to reply.

 

  • Try different AVR GCC

I have installed the XC8_2.32 toolchain to replace the native toolchain and now I do not get errors for the Linker flags.

 

 

 

MPLAB XC8 PRO has a trial duration; afterwards, subscription seems reasonable as bootloader development is aperiodic whereas application development is periodic (typically two to six weeks per cycle though quarters are popular

I have tried the XC8 Pro trial licence, it saves 1/4 of the size, but still the whole bootloader does not fit in the 8kb section.

 

  Try to move functionality from bootloader into the application; bootloader's requirements should be minimally essential

Yes, that is an option too but needs management decision. If I cannot work around the splitting bootloader approach I will have to try that.

 

 try IAR EWAVR Kickstart (4KB)

I will give it a go if it is not a lot of hassle to port the project to IAR at a later stage, hoping that it will work with the Microchip Studio as it stands.

 

Last Edited: Thu. May 13, 2021 - 01:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0



Remember that when you start doing the LPMs the entire app space will read back as 0xFFFF so it's not just the SPM routines themselves that may need to be forced above the BLS line but perhaps the data access stuff too.

 

<avr/boot.h> has a macro called BOOTLOADER_SECTION (which is really just an __attribute__((section(".bootloader"))) )

 

https://www.nongnu.org/avr-libc/...

 

that you can pepper routines with so:

BOOTLOADER_SECTION void do_SPM(char * buf, int len) {
    // stuff
}

BOOTLOADER_SECTION uint8_t geNextByte() {
    // stuff
}

BOOTLOADER_SECTION void bootloaderLoop() {
    while (data) {
        for (int i = 0; i < SPM_PAGE_SIZE; i++) {
            buff[i] = getNextByte()
        }
        do(_SPM(buff, SPM_PAGE_SIZE));
    }
}

void a_function() {
    // this is not tagged with BOOTLOADER_SECTION so will be placed at the "start"
}

void main() {
    // this is not BOOTLOADER_SECTION so will be down near the start

    a_function(); // call nearby function

    bootloaderLoop(); // call up to the functions grouped at 0x1E000+

If you have something like this and -Wl,--section-start=.bootloader=0x1E000 then all the functions tagged as "BOOTLOADER_SECTION" will be placed from 0x1E000 upwards but main()/a_function() will be down near the start (which is where you have set -Wl,--section-start=.text=0x???? where ???? is some address in high memory but below the BOOTSZ BLS boundary.

I initially approached the problem as you describe, but 1) I had a problem with the linker flags (which seems resolved after changing toolchain), 2) I still do not see the section .bootloader moving further to the end in the flash memory.

 

I can see these in the map file:

line 973:

Address of section .bootloader set to 0x1e000

line 1567:

.bootloader     0x000000000001e000        0x0

line 1667:

.bootloader     0x0000000000000574      0x9b6

 .bootloader    0x0000000000000574      0x9b6 Bootloader/flash.o

                0x00000000000009e2                FlashReinit

                0x0000000000000a0a                FlashInit

                0x0000000000000a0e                FlashWrite

                0x0000000000000ade                FlashErase

                0x0000000000000d98                FlashWriteChecksum

                0x0000000000000ebe                FlashVerifyChecksum

                0x0000000000000ec2                FlashDone

                0x0000000000000f20                FlashGetUserProgBaseAddress

 

and the last addresses in the hex build output file look like:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I may help to know what bootloader your talking about, can you provide a link, please

 

https://www.feaser.com/openblt/d...

 

I have already ported it to my project and have the hardware drivers working. I have successful file transfers, but I cannot store the bytes in the program internal flash memory yet.

Last Edited: Thu. May 13, 2021 - 01:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That seems to suggest that there are TWO different ".bootloader" - one being repositioned but with nothing in it and one with 0x9B6 bytes but not moved.

 

Perhaps the IDE does this anyway for "linker flags" but I would ensure in the build output that it is prepending the "-Wl," onto that.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AmVRosia wrote:

https://www.feaser.com/openblt/d...

 

I have already ported it to my project and have the hardware drivers working. I have successful file transfers, but I cannot store the bytes in the program internal flash memory yet.

May I suggest, rather then trying to squeeze a generic bootloader in to an AVR, you use an AVR specific bootloader: https://github.com/huukit/Atmega...

This one will already fit in to the space provided for bootloaders!!!

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would ensure in the build output that it is prepending the "-Wl," onto that.

 

Contents in makefile:

 

# All Target
all: $(OUTPUT_FILE_PATH) $(ADDITIONAL_DEPENDENCIES)

$(OUTPUT_FILE_PATH): $(OBJS) $(USER_OBJS) $(OUTPUT_FILE_DEP) $(LIB_DEP) $(LINKER_SCRIPT_DEP)
    @echo Building target: $@
    @echo Invoking:  XC8 C Linker : 2.32
    $(QUOTE)C:\Program Files\Microchip\xc8\v2.32\bin\xc8-cc.exe$(QUOTE) -o$(OUTPUT_FILE_PATH_AS_ARGS) $(OBJS_AS_ARGS) $(USER_OBJS) $(LIBS) -mcpu=AT90CAN128  -mdfp="C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.6.364\xc8"  -Wl,-Map="BMS.map" -funsigned-char -funsigned-bitfields -Wl,--start-group -Wl,-lm  -Wl,--end-group -Wl,--gc-sections -Os -ffunction-sections -fdata-sections -fpack-struct -fshort-enums --memorysummary,memoryfile.xml -Wl,--section-start=.bootloader=0x1E000  
    @echo Finished building target: $@
    "C:\Program Files\Microchip\xc8\v2.32\bin\avr-objcopy.exe" -O ihex -R .eeprom -R .fuse -R .lock -R .signature -R .user_signatures  "BMS.elf" "BMS.hex"
    "C:\Program Files\Microchip\xc8\v2.32\bin\avr-objcopy.exe" -j .eeprom --set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0 --no-change-warnings -O ihex "BMS.elf" "BMS.eep" || exit 0
    "C:\Program Files\Microchip\xc8\v2.32\bin\avr-objdump.exe" -h -S "BMS.elf" > "BMS.lss"

 

 

How do those flags work? I never understood those and their syntax. I always find the solution of what needs to be used in a particular case online. I haven't come across a well explanatory tutorial or manual etc to understand those in-depth.
For example, there are linker manipulations using the PSEC:

https://microchipdeveloper.com/x...
Direct adjustment of linker using -L :
( 4.8.6 -L-: Adjust Linker Options Directly )

https://ww1.microchip.com/downlo...

The -WI (which expects comma-separated instructions)
The -Ttext=0x1E000 (which moves the whole application)

 

Last Edited: Thu. May 13, 2021 - 03:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

laugh on the bootloader's name!

homepage [OpenBLT Bootloader]

[mid-page]

  • Supports common available communication interfaces such as RS232CANTCP/IP and USB.

Now see the difficulty.

USB DFU can fit on USB megaAVR; USB CDC ACM and USB HID will fit.

TCP/IP will be a challenge; maybe UDP will fit.

 

P.S.

OpenBLT is on GPLv3 without a bootloader exception; not an issue on the Arm Cortex-M with an MPU (AVR don't have a MPU, AVR32 does)

openblt/Target/Source at master · feaser/openblt · GitHub (bottom of readme.dox)

Licensing | homepage [OpenBLT Bootloader]

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah now I haven't tried this but I wonder if -ffunction-sections "interferes" with an attempt to __attribute__((section(".bootloader"))) ?

 

When function-sections does (for the purpose of garbage collecting routines that are not called) is to put a function like foo() into a section called something like .text.foo then at link time the linker (run with -gc-sections) looks at the access count on each such section and discards anything that has a 0 access count. That in itself is not a problem but if you are asking for "__attribute__((section(".bootloader"))) void foo() {}" and -ffunction-sections is going on at the same time I wonder what happens?

 

Off to try an experiment in avr-gcc (I assume xc8 will be the same?)...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

OK so it doesn't look like -function-section/-gc-sections interfere but when I wrote:

#include <avr/io.h>
#include <avr/boot.h>

BOOTLOADER_SECTION void foo() {
    PORTB = 0x55;
}

int main(void)
{
    foo();
    while (1)
    {
    }
}

and used:

 

it did not initially work as intended. What the .map file said was:

Discarded input sections

 .data          0x00000000        0x0 C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATmega_DFP/1.6.364/gcc/dev/atmega328p/avr5/crtatmega328p.o
 .bss           0x00000000        0x0 C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATmega_DFP/1.6.364/gcc/dev/atmega328p/avr5/crtatmega328p.o
 .text          0x00000000        0x0 main.o
 .data          0x00000000        0x0 main.o
 .bss           0x00000000        0x0 main.o
 .bootloader    0x00000000        0x6 main.o

which is listing 6 bytes in ".bootloader" as being discarded. When one looks at the LSS it's not difficult to work out why:

0000007a <main>:
#include <avr/io.h>
#include <avr/boot.h>

BOOTLOADER_SECTION void foo() {
    PORTB = 0x55;
  7a:	85 e5       	ldi	r24, 0x55	; 85
  7c:	85 b9       	out	0x05, r24	; 5
  7e:	ff cf       	rjmp	.-2      	; 0x7e <main+0x4>

It will have initially created a copy of foo() that was destined to locate at 0x2468 but as it then spotted that it could be inlined, it did that. Then the garbage collector will have spotted that nothing then calls the copy at 0x2468 so that is discarded.

 

I "fixed" this with:

__attribute__((noinline)) BOOTLOADER_SECTION void foo() {
    PORTB = 0x55;
}

which means it must create the separate function and call it. That leads to:

.bootloader     0x00002468        0x6
 .bootloader    0x00002468        0x6 main.o
                0x00002468                foo

and in the LSS:

Disassembly of section .bootloader:

00002468 <foo>:
#include <avr/io.h>
#include <avr/boot.h>

__attribute__((noinline)) BOOTLOADER_SECTION void foo() {
    PORTB = 0x55;
    2468:	85 e5       	ldi	r24, 0x55	; 85
    246a:	85 b9       	out	0x05, r24	; 5
    246c:	08 95       	ret

and also:

0000007a <main>:
}

int main(void)
{
    foo();
  7a:	0e 94 34 12 	call	0x2468	; 0x2468 <foo>
  7e:	ff cf       	rjmp	.-2      	; 0x7e <main+0x4>

so you may want to check whether anything is being inlined and, if so, the separate callable will be discarded. If this is code that must go above the BLS you can't afford to have it inlined and it must be called.

 

(PS oh and if you wonder why my relocation address of 0x1234 became 0x2468 on the day - that is just Atmel going against the flow!)

Last Edited: Thu. May 13, 2021 - 03:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IIRC the usual mechanism for a bootloader that will fit

is to use the linker to move the .text output section to the start of the bootloader region.

If the bootloader almost fits, use an attribute to move some functions into an orphan section.

Use the linker to move the orphan section where you want it.

 

An orphan section is one not mentioned in the linker script.

.annie will probably work.

.init7 probably will not.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wonder if -ffunction-sections "interferes" with an attempt to __attribute__((section(".bootloader"))) ?

 

When function-sections does (for the purpose of garbage collecting routines that are not called) is to put a function like foo() into a section called something like .text.foo then at link time the linker (run with -gc-sections) looks at the access count on each such section and discards anything that has a 0 access count. That in itself is not a problem but if you are asking for "__attribute__((section(".bootloader"))) void foo() {}" and -ffunction-sections is going on at the same time I wonder what happens?

I am not very clear on what are you describing here. Are there any relevant keywords websites documents etc to read about it? 

 

I have tried the __attribute__((noinline)) and does not work for me. But I use the Microchip Studio IDE which does not have the memory settings option under the AVR/GNU linker where you specify the flash segment. I used the same linker flag as before -Wl,--section-start=.bootloader=0x1E000. Which IDE version are you using?

 

I am wondering what happens to the map file if your .text section is very large (if you try to put a massive array within main() so that it exceeds the bootloader section for testing).

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

IIRC the usual mechanism for a bootloader that will fit

is to use the linker to move the .text output section to the start of the bootloader region.

If the bootloader almost fits, use an attribute to move some functions into an orphan section.

Use the linker to move the orphan section where you want it.

 

An orphan section is one not mentioned in the linker script.

.annie will probably work.

.init7 probably will not

Thanks this sounds like an interesting approach. Could you please help me with the linker flags etc?

 

I used this syntax within the code for most (or all) of the non flash related functions:
__attribute__((section(".annie"))) static void Init(void)

 

And the following flags:

 

And I get the error:
Error        BMS.elf section `.annie%27' will not fit in region `text'
 

(Looks like the linker still tries to fit everything in the same area above 0x1E000. Same as the previous example, the linker does not position correctly the sections in flash.)

 

Last Edited: Fri. May 14, 2021 - 10:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AmVRosia wrote:
I have tried the __attribute__((noinline)) and does not work for me.
I don't know a whole lot about "xc8". It seems on the surface to be some kind of "rip-off" of GCC as a lot of the syntax it supports seems very GCC like. In fact the very fact that it supports __attribute__((section(".foo"))) would seem to show its heritage. So I am kind of surprised if it does not support ALL the GCC attributes.

 

At least with GCC there is an "open" manual so one can easily read about:

 

https://gcc.gnu.org/onlinedocs/g...

https://gcc.gnu.org/onlinedocs/g...

 

I have no idea where one would go to find the equivalent manual for XC8 though.

 

BTW attributes can be combined (with commas) so something like:

__attribute__((section(".foo")))
__attribute__((noinline))
void bar(void) {
    
}

can actually be written as:

__attribute__((section(".foo"), noinline))
void bar(void) {
    
}

and you could of course wrap that into a macro:

#define BOOT_CODE __attribute__((section(".foo"), noinline))

then use in the form:

BOOT_CODE void bar(void) {
    
}

to tag given functions as being located high and not inlinable.

 

But I have no immediate idea why XC8 would not support your attempt to use it separately. What was the error when you tried to apply the noinline attribute?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

What was the error when you tried to apply the noinline attribute?

 

It does not give me an error, but it does not move the sections in memory as in your case. Basically, I get the exact same output as without using the __attribute__((noinline))

 

Can I set up my IDE to use GCC instead? (Sorry if it was mentioned earlier as a suggestion, I did not understand that)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
I have no idea where one would go to find the equivalent manual for XC8 though.
Function Attributes | MPLAB® XC8 C Compiler User’s Guide for AVR® MCU

Attributes | MPLAB® XC8 C Compiler User’s Guide for AVR® MCU

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

gchapman wrote:

clawson wrote:
I have no idea where one would go to find the equivalent manual for XC8 though.
Function Attributes | MPLAB® XC8 C Compiler User’s Guide for AVR® MCU

Attributes | MPLAB® XC8 C Compiler User’s Guide for AVR® MCU

 

But while that mentions a section attribute on DATA ("variables") it does not admit to it on FUNCTIONS ?

 

HOWEVER, wandering around in that manual I came across:

 

https://onlinedocs.microchip.com...

 

So it seems that rather than assigning to a section and then telling the linker where to position that section you can simply do:

int __at(0x1C000) mach_status(int mode)
{
  /* function body */
}

That same page does link to:

 

https://onlinedocs.microchip.com...

 

so another syntax is:

int __section("myText") helper(int mode) { /* ... */ }

What a stange hotch-potch of utter nonsense. If they are going to pretend to be GCC and support *some* of the data/function __attributes__ then why on earth didn't they simply match the XC8 specific __section("name") to be __attribute__((section("name"))) ??

 

Anyway that seems to be the answer - to try __section() instead as a first step.

 

The __at() thing may look attractive on the surface but if you can't use sections you would have to give an absolute address to each and every function that is to be moved.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


clawson wrote:
If they are going to pretend to be GCC and ...
a wrench is the Common C Interface (CCI); MPLAB XC8 v2 for PIC is based on Clang (LLVM) and up to C99 inclusive.

Maybe Clang is the future as LLVM's license is Apache with exceptions.

clawson wrote:
The __at() thing may look attractive on the surface but ...
CCI's __at is in two issues.

 


Common C Interface Standard | MPLAB® XC8 C Compiler User’s Guide for AVR® MCU

https://releases.llvm.org/12.0.0/LICENSE.TXT

 

MPLAB® XC8 C Compiler Version 2.32 Release Notes for AVR® MCU

6. Known Issues

[middle of page 11]

 

 

 

 

edit : corrected render defect

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Fri. May 14, 2021 - 02:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

To summarise my tests:

The __attribute__((".bootloader")) as well as the __attribute__((".bootloader", noinline)) as well as the __section(".bootloader"), they all create a .bootloader sector with the correct flash related functions inside and I see no difference between those instructions:

 

The -Ttext=0x1E000 flag in linker positions the whole code to the location specified.

 

The -Wl,--section-start=.text=0x1E000 behaves differently, it creates another empty .text sector at the address specified

The -Wl,--section-start=.bootloader=0x1E000 behaves similarly, it creates another empty .bootloader sector at the address specified

 

The __at(0x1E000) instruction works, but I need to do one by one all the functions, it does not seem a good approach. I tried to improvise to use __at(".bootloader") in an attempt to move the whole sector but it does not seem to work either.

 

Hence it is some sort of sequencing problem, the sector .bootloader exists, but the linker instruction does not move the sector but creates a new one, maybe because it first tries to move it without being created and then creates it. I tried both approaches, move the whole text at the end of the flash and move the non-flash related functions at the top of the flash, and the opposite. Unless there is an instruction to move the whole sector, I cannot do much with this linker.

 

Seems that I will have to change the IDE, or the bootloader, or try to shrink the existing bootloader.

Last Edited: Fri. May 14, 2021 - 03:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AmVRosia wrote:
The -Wl,--section-start=.text=0x1E000 behaves differently, it creates another empty .text sector at the address specified

The -Wl,--section-start=.bootloader=0x1E000 behaves similarly, it creates another empty .bootloader sector at the address specified

possible instances of issue XC8-1755

AmVRosia wrote:
The __at(0x1E000) instruction works, but I need to do one by one all the functions, it does not seem a good approach.
yet is the CCI way; __at is probably reasonable for a few or several functions or variables.

AmVRosia wrote:
... I cannot do much with this linker.
then AVR GCC (Microchip Technology, or, FSF) in-lieu of MPLAB XC8 for AVR.

AmVRosia wrote:
Seems that I will have to change the IDE, ...
You might like IAR EWAVR as EWAVR has a long history.

AmVRosia wrote:
... or try to shrink the existing bootloader.
You might be "close"; maybe a trim instead of a refactor.

If switch to AVR GCC then there are a few versions that "may" squeeze enough.

AVR Clang is possible; can evaluate to see if that's worth your effort.

AVR LLVM released | AVR Freaks

 

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Downgrading from Microchip Studio and XC8 toolchain to AVR Studio 7 and AVR GCC toolchain solves the problem with the steps described above. The IDE looks almost identical so it is easy to familiarise with, plus it gives access to this nice memory window under the linker where I can set the section addresses (note: the hex address number is in words instead of bytes).

Stepping into the drivers, I managed to replace some code with low-level instructions and fit the bootloader within the 8k limit. I had to simplify the UART debugging function, which worked as the printf() equivalent. Positioning the whole bootloader at the end did not seem to work. For example, the LED that the bootloader loop controls, instead of blinking, stays ON (of course that could be anything). I think some sort of bad initialization of hardware must take place (I use CAN Bus, UART, GPIO, timers). I checked the fuses and they are correct as of the example I tested successfully (in reference to my first post).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello again,

 

Continuing on adding features to the bootloader, I came across another blocker:

 

I am trying to set at compiling time a fixed set of bytes in flash memory at my application's .hex and .srec files as a form of an ID, so that the bootloader will only accept an application with this byte pattern.

 

When I do this:

 

const uint8_t volatile application_id[] PROGMEM = {0xAA, 0x55, 0xAA, 0x55};

 

I kind of get the pattern somewhere at the beginning of the file, but I have noticed that the location (of PROGMEM) changes when I make changes to my application (looks like when the vector table increases, it moves the following sections lower). What is the best way to set a fixed position of this byte pattern in flash?

 

The second problem is that I have to use the application_id array (for example to fprint it) so that the compiler (using size optimization) will put it in flash memory. How do I make sure that it will always be there without the need to use it?

 

Thanks

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
__attribute__((section(".codeID"))) const uint8_t volatile application_id[] = {0xAA, 0x55, 0xAA, 0x55};
-Wl,-section-start=.codeID=0x1234

will put that data at flash address 0x1234. Change location as required. Don't bump into anything the linker is already placing (i put this well up above the code actually being generated).

 

Be wary of -fdata-sections and -gc-sections. If nothing in the code refers to this data it will be eaten by the linker when -gc-sections tells it to "garbage collect" the unreferenced data sections.

 

Another option is to have srec_cat "meddle" with the .hex after it has been built. It has "generators" for things like ID numbers.

AmVRosia wrote:

The second problem is that I have to use the application_id array (for example to fprint it) so that the compiler (using size optimization) will put it in flash memory. How do I make sure that it will always be there without the need to use it?

Not sure what this means - can you explain further?

 

fprint() is run time - not compile time - how could that write to flash (well actually it could but it's a bit of tortuous journey to get there!!).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


clawson wrote:

-Wl,-section-start=.codeID=0x1234

 

 

I had to type this syntax instead because ARV understands words (half the address in bytes):

 

In order to get it working:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Be wary of -fdata-sections and -gc-sections. If nothing in the code refers to this data it will be eaten by the linker when -gc-sections tells it to "garbage collect" the unreferenced data sections.

 

 

I think in this sentence you are describing my second problem.

clawson wrote:

The second problem is that I have to use the application_id array (for example to fprint it) so that the compiler (using size optimization) will put it in flash memory. How do I make sure that it will always be there without the need to use it?

 

By saying "I have to use the application_id array" probably I am referring to what you call "If nothing in the code refers to this data".

I will rephrase:
I have to refer to the array "application_id" (for example to use it within a print type function and spring the ID), otherwise the "garbage collect" of the linker "eats" the unreferenced "application_id" data section. How do I preserve my data without the need to refer to this "application_id" data? By turning off optimizations temporarily? By making it volatile?

 

#pragma GCC push_options
#pragma GCC optimize ("O0")
__attribute__((section(".codeID"))) const uint8_t volatile application_id[] = {0xAA, 0x55, 0xAA, 0x55};
#pragma GCC pop_options
 

int main(void)
{
    // some hardware initializations
    
    UART_Printf("__________________________\r\n%X", pgm_read_dword_near(application_id)); // this line is required as reference to application_id in order to appear in flash
    
    while(1)
    {

        // main code

    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


AmVRosia wrote:
I had to type this syntax instead because ARV understands words (half the address in bytes):
Well, yes, that is ONE way to do it but I hat eth fact that Atmel scew thing sup by confusingly talking about WORD addresses and then passing that as double the value for a BYTE address to the section start. I'm usually tempted simply to end it here:

 

 

As for not having it garbage collected. Just disabling -fdata-sections may be the very easiest solution:

 

Or a stronger approach is to switch off section discards all together at:

 

There is another way to preserve the specific data item by symbol name but, to be brutally honest, I've currently forgotten what it is even though I posted something about it in the last couple of months  - it's an age thing! I'll see if I can locate that thread..

 

BTW the __attribute__((used, section(".codeID"))) solution may look tempting but "used" does not mean what you think it may mean! ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In my custom bootloaders, I use something like:

/************************  Start of Version Section  ************************
 * For version information to be placed at very top of Flash Memory
 * In Project Properties / Tool Chain / Linker / Miscellaneous.
 * add the following after '-Wl,--section-start=.text=0x1FC00' :
 *     -Wl,--section-start=.version=0x1FFFC -Wl,--undefined=myVersion
 ****************************************************************************/

#define MYVERSION     'r', '2', 'h', 0x01       // Revision ID, Version, Revision, Features byte

__attribute__ ((used, section (".version")))
uint8_t myVersion[]  = { MYVERSION };          // Version Info to be placed in flash

/*************************  End of Version Section  *************************/

So it may be the "--undefined" that you are looking for...

David

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah ha! Yes, it was undefined that is the "magic" ! :-)

 

EDIT: that allowed me to locate: https://www.avrfreaks.net/commen... - it's the same thing but illustrates that -u is simply a short-form for that option.

Last Edited: Thu. Jun 3, 2021 - 12:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Thanks guys, good stuff! I hope we help people with bootloader designs in the future with this nicely concentrated information covering most parts of the flash memory software design.

 

I do not want to turn off the garbage collector for the rest of the application, it is useful to keep the file size small by removing any unnecessary dumped code. Hence I tried the undefined:

 

This syntax does the magic trick for my case:

 

where:

 

 

 

 

Another theory question I got is:

 

I was thinking, since I split the bootloader into 2 sections and fit about 0x2500 words in the first part of flash (starting at address 0x0) and a tiny bit of code of some flash functions at the end part of flash (starting at address 0xFF00), with a size of about 0x700 words, why not to transfer a few more functions at the end of flash until it fills up and save some space at the front of the flash for the application. After starting to place my other functions to the bootloader section of flash, I noticed that instead of getting the same size file with bytes shifted in flash, I was getting an even bigger-sized file. It looks like the linker keeps multiple copies of those functions to both the front and the end part of the flash. Any ideas? (I remember from a PIC microcontroller project that the linker was doing something similar copying a function twice in flash for functions were called within interrupts.)

 

 

Last Edited: Sat. Jun 5, 2021 - 01:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AmVRosia wrote:
was thinking, since I split the bootloader into 2 sections and fit about 0x2500 words in the first part of flash (starting at address 0x0) and a tiny bit of code of some flash functions at the end part of flash (starting at address 0xFF00), with a size of about 0x700 words, why not to transfer a few more functions at the end of flash until it fills up and save some space at the front of the flash for the application. After starting to place my other functions to the bootloader section of flash, I noticed that instead of getting the same size file with bytes shifted in flash, I was getting an even bigger-sized file. It looks like the linker keeps multiple copies of those functions to both the front and the end part of the flash. Any ideas?
You seem to be referring to two savings:

Moving more bootloader code from the application region to the bootloader region.

The application using bootloader code.

 

To make the former work, you need to put in a more useful (i.e. higher) section start address for the application region code.

Otherwise there will be a gap above the application region code,

but the low end of the bootloader will not change.

 

To make the latter work, you need to link one executable to make it use symbols in another executable.

You will likely soon be receiving a pointer to instructions on making a jump table.

Another way is to copy the desired symbols from the bootloader executable.

Convert the list into a file of symbol-defining optiond for the linker.

Pass that file to the linker as an @file.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am having problems with the interrupts, they do work in the bootloader (which starts at address 0x0) but do not work at the application (which starts at address 0x4000). I am missing the aliasing step. How is it done in the AVR Studio?

 

I cant get it to work:

https://www.nongnu.org/avr-libc/...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You need to read about "IVSEL".

 

The AVR has two possible positions for the interrupt vector table which one might call "low" and "high" and the IVSEL bit says which one to use. At power on (even with BOOTRST) the bit is 0 so the IVT at 0 is used. If a bootloader wants to use interrupts then probably the first thing it will wan to do is change IVSEL. this makes the active IVT the one at the BOOTSZ boundary. While the bootloader us any ISR()s it has programmed will hook vectors in the IVT at that high address. Obviously when the bootloader is finished one of its last actions before entering the app should then be to switch IVSEL back so that the app will get to use the "low" IVT

 

On the whole bootloaders shouldn't really use interrupts anyway. As soon as you add interrupts the testing of the code becomes about 10 times more complex (how to you test for interrupts interrupting ANY part of the non interrupt path?). When you ship a product the only part of the code that should be 100% error free and fully tested is the bootloader. Bugs in the application can be fixed later by a new application delivery but the bootloader cannot be changed (or only with massive difficulty!) so it needs to be rock solid and you don't help that cause by adding the complexity of interrupts. Given that a bootloader only has to do one simple job: receive a stream of data and SPM it, there's nothing norally that complex in it that mandates the use of interrupts.

 

(having said that, if your comms channel is a "complex peripheral" like CAN or USB it could be that interrupts cannot be avoided).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

You need to read about "IVSEL".

 

The AVR has two possible positions for the interrupt vector table which one might call "low" and "high" and the IVSEL bit says which one to use. At power on (even with BOOTRST) the bit is 0 so the IVT at 0 is used. If a bootloader wants to use interrupts then probably the first thing it will wan to do is change IVSEL. this makes the active IVT the one at the BOOTSZ boundary. While the bootloader us any ISR()s it has programmed will hook vectors in the IVT at that high address. Obviously when the bootloader is finished one of its last actions before entering the app should then be to switch IVSEL back so that the app will get to use the "low" IVT

 

On the whole bootloaders shouldn't really use interrupts anyway. As soon as you add interrupts the testing of the code becomes about 10 times more complex (how to you test for interrupts interrupting ANY part of the non interrupt path?). When you ship a product the only part of the code that should be 100% error free and fully tested is the bootloader. Bugs in the application can be fixed later by a new application delivery but the bootloader cannot be changed (or only with massive difficulty!) so it needs to be rock solid and you don't help that cause by adding the complexity of interrupts. Given that a bootloader only has to do one simple job: receive a stream of data and SPM it, there's nothing norally that complex in it that mandates the use of interrupts.

 

(having said that, if your comms channel is a "complex peripheral" like CAN or USB it could be that interrupts cannot be avoided).

 

It has CANBus communications and it cannot be avoided. I need the bootloader to be able to do file transfer over CANBus.

 

I came across the IVSEL you mentioned and I think this is not applicable in my case. Correct me if I am wrong. I have a customised bootloader which part of it is at the beginning of the flash and the flash erase / write functions are at the end of the AVR's flash special section. The interrupts work for this design of bootloader.
When the bootloader jumps into address 0x4000 which is the reset vector of my application (I think, since I just shifted everything in flash by an offset of 0x4000), the application runs but the CANBus Rx interrupt is not working because I am missing some form of aliasing. If IVSEL controls fixed addresses that the interrupts jump to, probably both of those low and high addresses are occupied by my bootloader at the moment. I need a custom area for aliased vector table.

 

Again from the PIC example, I worked on in the past: I had a complex design where the interrupt entries were jumping to an offset location and used to work without problems. Of course, it was super hard to debug. I guess there must be a simpler way to do it via the linker somehow.

Last Edited: Thu. Jul 15, 2021 - 04:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
... or USB it could be that interrupts cannot be avoided).
A USB megaAVR bootloader is possible without interrupts; USB HID can be low-speed with a bit rate similar to some AVR UART max baud.

https://github.com/rrevans/ubaboot/blob/master/ubaboot.S#L52

 

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

AmVRosia wrote:
I think this is not applicable in my case. Correct me if I am wrong. 
I'm hoping your memory architecture is something like:

 

 

When you use a bootloader you effectively have two complete (and mostly separate) programs in the AVR. Both have their own reset entry point, interrupt vector table, C Run Time setup, main() and ISR() handlers.

 

At power on (because of BOOTRST+BOOTSZ) execution will enter the AVR at 0x1E000 where the bootloader lives. Reset will jump to the CRT and it in turn will CALL to main(). If that program is going to enable some interrupts, sei() and then use it's own "ISR(SOME_vect) // (boot)" to handle those interrupts then you would want the IVT (boot) to be active. You can't leave the one down just above 0 ("IVT (app)") as the active one because it will contain relative jumps to ISRs in the app code. The vectors down in that low copy of the IVT will not "know" about ISR()s up in the bootloader. So before you enable any interrupt and sei() in the main() of the bootloader you do the IVSEL switch. now the active IVT becomes "IVT (boot)" and it has vector jumps to "ISR(SOME_vect) (boot)". That is the ISR()s that were built as part of the bootloader program (which is built and programmed completely separately - it cannot know or use anything in the app space).

 

In theory before the bootloader makes that final decision to start the app it should disable all interrupts (actually it's best for it to watchdog reset and reset all peripherals!) and when it is ready, almost the last thing it does before it jumps to 0 is to make sure IVSEL is 0 (low). Now, when the app starts, it should find itself in a "normal AVR environment" so the IVT that will become active when it configures interrupt sources and sei()s is the low one - the one that has the ISR vectors that it generated when it was built.

 

Now sometimes people decide (for things like UART and CAN) that they don't want to duplicate the same support code into both boot and app. So they use the copy in the bootloader for those things and "share it down" to the app (the app can know/share things in the boot because the boot is always there - the converse is NOT true, the bootloader cannot share things from the app because one day (during a failed upload attempt) the app may not be there).

 

So there's an outside possibility that if you are are sharing CAN support from the bootloader and it is using ISRs then you might want to leave IVSEL=1 so for all the time the app runs the active IVT (and iSR()) are the ones up in the bootloader. But what if the app wants to hook some other interrupts too? Its ISR()s are "down low" but the active IVT is still "up high". When the bootloader was built it cannot know which other ISRs the app might later want to use. So if it is to remain the active IVT (just to support the CAN) then all the "unused" vectors will actually have to be pointed to handlers that are ready to "call through" RAM based function pointers. Then when the app starts it will need to ensure that those RAM based function pointers are updated to point to its ISR() in the low position. But.... the boot and app were built with different RAM layouts! So you would have to have an agreed reserved area of RAM that both the app and the boot "know about" (probably above the stack) but this really starts to get complex !

 

The simple solution is to treat these two things as almost entirely two separate, standalone programs even if that might mean building the same interrupt drive CAN support into both (so, yes, there will be two copies of the same stuff occupying flash!).

 

EDIT: Sorry, just re-read your post. What on earth is the app doing at 0x4000 ? If you do that then the app can never use interrupts as it won't be able to position vectors over the expected IVT location ?? If you do go that way (because something "non program" has to overlay the IVT down near 0) then you MUST set IVSEL and keep the active IVT the high one. But if you do this you face the problem I just described and that is getting IVT entries "high" to vector to ISR()s in the low app. Why make a rod for your own back ?? I would seriously rethink this 0x4000 idea!

Last Edited: Fri. Jul 16, 2021 - 09:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

clawson wrote:

 

 

AmVRosia wrote:
I think this is not applicable in my case. Correct me if I am wrong. 
I'm hoping your memory architecture is something like:

 

 

When you use a bootloader you effectively have two complete (and mostly separate) programs in the AVR. Both have their own reset entry point, interrupt vector table, C Run Time setup, main() and ISR() handlers.

 

At power on (because of BOOTRST+BOOTSZ) execution will enter the AVR at 0x1E000 where the bootloader lives. Reset will jump to the CRT and it in turn will CALL to main(). If that program is going to enable some interrupts, sei() and then use it's own "ISR(SOME_vect) // (boot)" to handle those interrupts then you would want the IVT (boot) to be active. You can't leave the one down just above 0 ("IVT (app)") as the active one because it will contain relative jumps to ISRs in the app code. The vectors down in that low copy of the IVT will not "know" about ISR()s up in the bootloader. So before you enable any interrupt and sei() in the main() of the bootloader you do the IVSEL switch. now the active IVT becomes "IVT (boot)" and it has vector jumps to "ISR(SOME_vect) (boot)". That is the ISR()s that were built as part of the bootloader program (which is built and programmed completely separately - it cannot know or use anything in the app space).

 

In theory before the bootloader makes that final decision to start the app it should disable all interrupts (actually it's best for it to watchdog reset and reset all peripherals!) and when it is ready, almost the last thing it does before it jumps to 0 is to make sure IVSEL is 0 (low). Now, when the app starts, it should find itself in a "normal AVR environment" so the IVT that will become active when it configures interrupt sources and sei()s is the low one - the one that has the ISR vectors that it generated when it was built.

 

Now sometimes people decide (for things like UART and CAN) that they don't want to duplicate the same support code into both boot and app. So they use the copy in the bootloader for those things and "share it down" to the app (the app can know/share things in the boot because the boot is always there - the converse is NOT true, the bootloader cannot share things from the app because one day (during a failed upload attempt) the app may not be there).

 

So there's an outside possibility that if you are are sharing CAN support from the bootloader and it is using ISRs then you might want to leave IVSEL=1 so for all the time the app runs the active IVT (and iSR()) are the ones up in the bootloader. But what if the app wants to hook some other interrupts too? Its ISR()s are "down low" but the active IVT is still "up high". When the bootloader was built it cannot know which other ISRs the app might later want to use. So if it is to remain the active IVT (just to support the CAN) then all the "unused" vectors will actually have to be pointed to handlers that are ready to "call through" RAM based function pointers. Then when the app starts it will need to ensure that those RAM based function pointers are updated to point to its ISR() in the low position. But.... the boot and app were built with different RAM layouts! So you would have to have an agreed reserved area of RAM that both the app and the boot "know about" (probably above the stack) but this really starts to get complex !

 

The simple solution is to treat these two things as almost entirely two separate, standalone programs even if that might mean building the same interrupt drive CAN support into both (so, yes, there will be two copies of the same stuff occupying flash!).

 

EDIT: Sorry, just re-read your post. What on earth is the app doing at 0x4000 ? If you do that then the app can never use interrupts as it won't be able to position vectors over the expected IVT location ?? If you do go that way (because something "non program" has to overlay the IVT down near 0) then you MUST set IVSEL and keep the active IVT the high one. But if you do this you face the problem I just described and that is getting IVT entries "high" to vector to ISR()s in the low app. Why make a rod for your own back ?? I would seriously rethink this 0x4000 idea!

 

Thank you for the very explanatory and detailed post. It is also what I understood as to how AVR wants the flash to be structured by reading the datasheet.

No, unfortunately, my architecture is a bit unorthodox.

  • If you refer back to older conversations, I cannot fit the whole of the bootloader at the Boot Flash Section, hence I had to split it (my bootloader is about 0x3500 words and the Boot Flash Section has max size 0x2000 words).
  • I decided to put the main part of the bootloader at the beginning of the program memory flash starting at the address 0x0 and ending at the moment at about a bit over address 0x3000+. In that way, the bootloader has its interrupts working. 
  • I was planning to use shared interrupts; the application will reuse the bootloader's CANBus receive interrupt and interrupt handler function in this case. I dedicated those "sectors" up to 0x4000 for the bootloader first part. 
  • I have positioned the bootloader's flash manipulation functions at the Boot Flash Section at the end of the program flash memory in order to work starting at address 0xF000 (because the AVR does not give me any other alternative). Hence the bootloader is split at the beginning and very end of the flash, leaving a big free memory gap for the application.
  • The application "lives" within 0x4000 up to 0xF000
     

My bootloader and application are more interconnected and related to each other as opposed to two separate worlds. It is messy and complex but I cannot think of another better way. 

 

Last Edited: Fri. Jul 16, 2021 - 10:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Again from the PIC example, I worked on in the past: I had a complex design where the interrupt entries were jumping to an offset location and used to work without problems. Of course, it was super hard to debug. I guess there must be a simpler way to do it via the linker somehow.

 

I found this coding design I was talking about:

 

void __attribute__((picinterrupt(("")))) INTERRUPT_InterruptManager (void)
{
    if (PIE5bits.RXB0IE && PIR5bits.RXB0IF)
    {

        if (TRISB4)
        {
            __asm("goto 4008h");
        }
        else
        {
            ECAN_ISR_ECAN_RXBI();
        }
    }
}

 

This is what I have written in the bootloader of the PIC.

  1. When the PIC executes the interrupt it will jump to the INTERRUPT_InterruptManager().
  2. The INTERRUPT_InterruptManager() will check if it is a CANBus interrupt.
  3. The INTERRUPT_InterruptManager() will check if an unused pin of the microcontroller I chose has its direction register high or low (if it is a GPIO input or output). That was the best I could think to use as a mode indicator (bootloader or application).
  4. In the case of the bootloader (low) it will jump to the ECAN_ISR_ECAN_RXBI() CANBus interrupt service routine locally in the bootloader.
  5. In the case of the application (high) it will jump to the address 0x4008 which is the CANBus interrupt in the aliased vector table in the application section.
Last Edited: Fri. Jul 16, 2021 - 11:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AmVRosia wrote:
I decided to put the main part of the bootloader at the beginning of the program memory flash starting at the address 0x0 and ending at the moment at about a bit over address 0x3000+. In that way, the bootloader has its interrupts working. 
That is quite the worst way to do this. Put the "extra bit" of the bootloader BELOW it with the boot entry and IVT in the millde. Then have the bootloader set IVSEL when it starts (and clear it when it finishes).

AmVRosia wrote:
I was planning to use shared interrupts; the application will reuse the bootloader's CANBus receive interrupt and interrupt handler function in this case
But then you hit exactly the thing I said above. The bootloader cannot know which ISR()s the app might want to use or where to direct the jumps. So if its IVT remains active (IVSEL=1) then for the ones not involved in CAN you will need to have each go to a generic kind of handler, each of which maintains a RAM based vector. Then the app needs someway to set those vectors so it can have their ultimate destination be its own ISR()s (expect that you won't be using ISR any more - just "normal functions" but that have __attribute__((signal)) so they are generated with RETIs not RETs. It's a bit of a nightmare.

 

I really would rethink your design before you get too deeply committed into a single solution (that may raise all other kinds of issues).

 

If you do go for the "redirecting" kind of generic handlers with IVSEL=1 all the time then I guess you'll want something like:

typedef void (*fptr)(void);

fptr redirects[NUM_VECTORS];

ISR(__vector1) {
    if (redirects[0]) {
        redirects[0]();
    }
}

ISR(__vector2) {
    if (redirects[1]) {
        redirects[1]();
    }
}

ISR(__vector3) {
    if (redirects[2]) {
        redirects[2]();
    }
}

The the bootloader exposes to the app (maybe via a dispatch table in a fixed position?) functions like:

void hook_vector1(fptr pISR) {
    redirects[0] = pISR;
}

void hook_vector2(fptr pISR) {
    redirects[1] = pISR;
}
etc.

Then in the app code - having determined how to reach the hook functions you have something like:

__attribute((signal)) void vec1_handler(void) {
    // this is an ISR
}

extern void hook1(fptr); // this fn actually in bootloader

int main(void) {
    hook1(vec1_handler);
    // stuff
}

HOWEVER it's not quite this simple. When the bootloader is built it will just arbitrarily position the redirects[] array of function pointers. So you need to over-ride that and place it in a "protected" part of SRAM that neither bootloader nor app are likely to use otherwise. My choice would be to drop the top of the stack and squeeze this in above then use __attribute__((section(".vects"))) and -section-start=.vects=wherever to position this in that protected part of RAM.

 

All very messy!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

That is quite the worst way to do this. Put the "extra bit" of the bootloader BELOW it with the boot entry and IVT in the millde. Then have the bootloader set IVSEL when it starts (and clear it when it finishes).

I thought this idea too, yes this could work, especially if I redesign it without any interrupts at all in the bootloader (not sure how easy will be yet). 

 

clawson wrote:

But then you hit exactly the thing I said above. The bootloader cannot know which ISR()s the app might want to use or where to direct the jumps. So if its IVT remains active (IVSEL=1) then for the ones not involved in CAN you will need to have each go to a generic kind of handler, each of which maintains a RAM based vector. Then the app needs someway to set those vectors so it can have their ultimate destination be its own ISR()s (expect that you won't be using ISR any more - just "normal functions" but that have __attribute__((signal)) so they are generated with RETIs not RETs. It's a bit of a nightmare.


I think that was not the case in my PIC project because I used the aliased vector table and all I had to do is to jump to the aliased address. Then the execution of the interrupt service routine was managed by the PIC at this point (interrupt jump to the interrupt service routine and returning from it). So for instance if the CAN receive interrupt is in address 0x08 and the application is positioned at starting address 0x4000, then a jump to 0x4008 seems to do the correct actions to service my interrupt. Unless I understand something wrong and I was just lucky that it was working for the time I was involved in that project for over a year without problems.

But yes let's stick to the cleanest possible solution because otherwise, it might populate painful problems in the future. The correct approach is to change chip (but there is none with a bigger flash, or boot flash section), or bootloader (but we want the XPC protocol support over CANBus which seems to add most of the size).

Ok, thanks I will experiment with those suggestions the following days and come back to the forum for an update.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


BTW all these new fangled AVR-0/AVR-1/AVR-Dx etc (which are Xmega derivatives) have turned things on their head. Instead of having a separate "bootloader section" up top they have the bootloader at 0 (if one is used). Either way this means just one IVT in a fixed position. So you might want to explore these if considering something "newer"

 

(HOWEVER I think Atmel-Microchip somehow lost interest in CAN after the first few years of the 2000 decade - I'm not sure there's been anything in the last 15+ years that includes CAN so you might be stuck with the old architecture)

 

((off to check their parametric selection tool...))

 

EDIT: yup, as I thought...

 

 

Just six models have CAN:

 

AT90CAN128

AT90CAN64

AT90CAN32

ATMega64M1

ATMega32M1

ATMega16M1

 

and I don't think the M1s are really that much younger than the aged AT90CAN chips. It does seem like they abandoned chasing CAN customers - probably because the Tier 1 OEM in Automotive had their sources tied up and Atmel couldn't "break in".

Last Edited: Fri. Jul 16, 2021 - 02:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Either way this means just one IVT in a fixed position.
IVSEL is still present.

clawson wrote:
I'm not sure there's been anything in the last 15+ years that includes CAN so you might be stuck with the old architecture)
CAN PIC has been updated.

 


Interrupts | Migration from the megaAVR® to AVR® Dx Microcontroller Families (third paragraph)

 

Microchip Delivers First 8-bit MCU Family for CAN FD Networks | Microchip Technology

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0



IVSEL is still present.

Oh I see so in this (upside down??) picture:

 

 

these new devices have the potential of an IVT in both "BOOT" and "APPCODE" section - didn't realise that. I guess it's obvious when you think about it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

AmVRosia wrote:
I cannot fit the whole of the bootloader at the Boot Flash Section, hence I had to split it (my bootloader is about 0x3500 words and the Boot Flash Section has max size 0x2000 words).

...

... but I cannot think of another better way.

SerialBootloader though a possible no-go is the attached non-volatile storage; otherwise, consider EWAVR.

Some non-volatile storage have block write-protect such that a minimally essential known good application can be provisioned (akin to DBS set-top boxes that have a few applications in storage and must not be bricked by an update)

 


AN_8390 AVR2054: SerialBootloader User Guide

[PDF page 16]

 

IAR Embedded Workbench for AVR | IAR Systems

AN3419 - Getting Started with IAR Embedded Workbench for AVR

 

edit : fyi, EERAM is half the price of MRAM

Serial EERAM | Microchip Technology

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Mon. Jul 19, 2021 - 08:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are RWW/NRWW sections that you have to be aware of. If you need the bootloader code and interrupts to keep working while a flash erase/write is in progress (~5ms or so), then the code and interrupts will have to be in the NRWW section (upper flash) as there is no access to the RWW section while an erase/write is in progress. 

 

I would make the bootloader be the oddball in terms of the hoops you have to jump through to get it all positioned properly. The apps can then be built as if they are normal apps except for the new size restriction. Trying to make an app and bootloader aware of each other in some way is more work than benefit, so treat them separately.

 

If the bootloader is ok with being halted (no interrupts) during erase/write, then the bootloader section can be the minimal size just to get the interrupts and the flash writing code in the right place. The rest of the bootloader can then live below the bootloader in one contiguous section (just so it does not have to be split up due to the vectors in a fixed address)-

 

-------------------0x0000 (word addresses)
normal vectors (IVSEL=0)
normal app code

end app code
-------------------0xDFFF
-------------------0xE000 (16K remaining)
bootloader code 
(.text)
                   0xF000 NRWW section
-------------------0xFE00 (1024 bytes, bootloader section)
boot vectors (IVSEL=1)
flash write function
-------------------0xFFFF end

 

 

If the bootloader needs interrupts working while erase/write, then you would have to split up your code into 2 types (normal, section specified) so those that need to be used during erase/write are in the NRWW section (NRWW coincides with bootloader start since set to largest size now).

 

-------------------0x0000 (word addresses)
normal vectors (IVSEL=0)
normal app code

end app code
-------------------0xDFFF
-------------------0xE000 (16K remaining)
bootloader code 
not used during 
erase/write
-------------------0xF000 NRWW section (8k bootloader section)
boot vectors (IVSEL=1)
bootloader code
needed 
during erase/write
-------------------0xFFFF end

 

In either case, I would grab a copy of the linker script and put it in the project, then use that copy and make changes to it as needed. You can create memory regions and sections to accommodate the layout much easier than trying to struggle through trying to make the existing linker script work as-is. They are not etched in stone.

 

An example for the first simpler layout above where interrupts/code need not be running during flash/erase- add a bls region to hold the vectors and flash code, and change the text region start and size (note regions and sections are not the same thing, and text/.text are different, as is bls/.bls)-

  bls    (rx)   : ORIGIN = 0x1FC00, LENGTH = 1024
  text   (rx)   : ORIGIN = 0x1C000, LENGTH = 16k-1024

 

then comment out the .vectors in the .text section beginning, create a .bls section and add the .vectors here instead, along with a section you can specify for the flash writing code-

  .bls : {
    *(.vectors)
    KEEP(*(.vectors))
    *(.flashfuncs)
} >bls

 

now your vectors are at the proper bootloader vector address, the flash function can use a section attribute to place it in the .flashfuncs section, and all your normal code is in the .text section below the .bls section. Interrupts (IVSEL=1) have to be disabled when doing a flash erase/write since your code is inaccessible, the flash write function will be blocking, with a rww enable when done. You will also of course want to make your flash writing function avoid writing to your bootloader code which now starts at 0x1C000.

 

If interrupts needed while erase/write, then you need to change things. The text region starts at the bootloader start (set to max size now), and the .vector section remains in the .text section as it normally would be, but now you need to create a region below the bootloader section to contain code that will not (can not) be used during erase/write. Each of those functions will need a section attribute.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

You need to read about "IVSEL".

 

The AVR has two possible positions for the interrupt vector table which one might call "low" and "high" and the IVSEL bit says which one to use. At power on (even with BOOTRST) the bit is 0 so the IVT at 0 is used. If a bootloader wants to use interrupts then probably the first thing it will wan to do is change IVSEL. this makes the active IVT the one at the BOOTSZ boundary. While the bootloader us any ISR()s it has programmed will hook vectors in the IVT at that high address. Obviously when the bootloader is finished one of its last actions before entering the app should then be to switch IVSEL back so that the app will get to use the "low" IVT

 

On the whole bootloaders shouldn't really use interrupts anyway. As soon as you add interrupts the testing of the code becomes about 10 times more complex (how to you test for interrupts interrupting ANY part of the non interrupt path?). When you ship a product the only part of the code that should be 100% error free and fully tested is the bootloader. Bugs in the application can be fixed later by a new application delivery but the bootloader cannot be changed (or only with massive difficulty!) so it needs to be rock solid and you don't help that cause by adding the complexity of interrupts. Given that a bootloader only has to do one simple job: receive a stream of data and SPM it, there's nothing norally that complex in it that mandates the use of interrupts.

 

(having said that, if your comms channel is a "complex peripheral" like CAN or USB it could be that interrupts cannot be avoided).

 

I did some testing without interrupts and I managed to get the same functionality in bootloader without them. Hence I will try to redesign/relocate things in flash, having the bootloader starting at 0xC000 address without using interrupts, and move the application to the beginning of flash 0x0 address.

clawson wrote:

That is quite the worst way to do this. Put the "extra bit" of the bootloader BELOW it with the boot entry and IVT in the millde. Then have the bootloader set IVSEL when it starts (and clear it when it finishes).

 

How do I position the reset vector to the high end of the flash? Is it something like that? Is it meant to change at run time?

//---------------------------------------
// Map ISR vectors to bootloader section
//---------------------------------------
cli();               // disable interupts
MCUCR = (1<<IVCE);   // enable interruptvectors change
MCUCR = (1<<IVSEL);  // move interruptvectors to bootloader
//---------------------------------------

At the moment with the bootloader positioned at 0xD000 and the above code addition and the fuse for the high reset vector set seem that the bootloader works.

Last Edited: Mon. Jul 19, 2021 - 11:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AmVRosia wrote:
How do I position the reset vector to the high end of the flash? Is it something like that?
Old school mega AVRs only have 5 possible (fixed) places where the reset entry can occur. The default (no BOOTRST) is 0x0000. With BOOTRST it is then the two BOOTSZ fuses in combination that change the location of the "upper" reset point (and IVT). 

 

AFAIK you cannot change this - it has to be one of the 5.

Pages