How to tell WinAVR GCC where to place bootloader code

Go To Last Post
22 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

I'm using WinAVR-20050214 and try to write a simple bootloader for an ATmega8.

In avr-libc 1.2.3 I found many good information about how to do it. But not all
In chapter "Modules -> Bootloader support utilities" I read this:

#define BOOTLOADER_SECTION __attribute__ ((section (".bootloader")))
Used to declare a function or variable to be placed into a new section called .bootloader. This section and its contents can then be relocated to any address (such as the bootloader NRWW area) at link-time.

Can someone tell me how to realocate the section to NRWW area??????

Declaring a function like this won't put it into the Bootloader section.
So I think there is something more to be done:

BOOTLOADER_SECTION int main (void)
{
	init();

	// Endless Loop
	for (;;)
	{
	}
	
	return 0;
}

Thanks for your support!
Sebastian

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

You're telling it at the linker level, by using -ssection-start.

Keep in mind that all GNU utilities count addresses as
byte addresses.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Sebastian,

You may find the second post by Colin O'Flynn in this thread: https://www.avrfreaks.net/index.p... useful as it expands on what Jörg advises.

Cliff

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

Thanks, that was a very helpful link!
But now I have another Problem:
I CAN'T JUMP TO THE THE BOOTLOADER SECTION!!!

First I tried this:

typedef void (*funcPtr_t) (void);

void main (void)
{
    funcPtr_t bootloader = (void *) 0x0F80;

    bootloader():

    for (;;)
    {
    }
}

In this case the Compiler didn't load the Z-Register with the address
and used RJMP to reach the flash address.
But RJMP can't jump more than 2kwords.
So the program ended in space.

Then I tried this:

// ### LDI ### Load Immediate Register With Constant (reg = const)
#define LDI(reg, const) asm volatile ("ldi %0, %1" : "=d" (reg) : "M" (const))

// ### ICALL ### Indirect Call To Z-Register (PC = Z)
#define ICALL() asm volatile ("icall" :: )

static __inline__ void Bootloader (void)
{
	register uint8_t zPtr_H asm("r31");
	register uint8_t zPtr_L asm("r30");
	
	LDI(zPtr_H, 0x0F);
	LDI(zPtr_L, 0x80);
	ICALL();
}

In this case the Z-Register was loaded correctly
with 0x0F80. The ICALL was executed but still
the program won't jump to the selected address but
into space.

I tried different addresses. I divided the given addresses in ATmega8 datasheet by 2
or multiplicated them by 2. But The program never reached the bootloader start address.

I checked this behavior in AVR Studio and on Hardware.

I put a simple program into the bootloader section to check if program reaches
the section on Hardware:

void main (void)
{
	DDRB = 0xFF;
	PORTB = 0x00;
	
	for (;;)
	{
		PORTB = ~PORTB;
	}
}

If I enable bootloader reset the bootloader application works fine.
THANKS FOR YOUR SUPPORT!!!

Regards
Sebastian

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

> funcPtr_t bootloader = (void *) 0x0F80;

> In this case the Compiler didn't load the Z-Register with the
> address and used RJMP to reach the flash address. But RJMP can't
> jump more than 2kwords.

0xf80 is less than 2 Kwords.

You probably meant to jump to 2 * 0xf80 -- addresses are *byte*
addresses.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

I've just been doing something very similar (on a Mega16) but my boot code is at word address 1C00. So my Makefile specified:

BOOTLOAD = 0x3800

LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
LDFLAGS += -Wl,--section-start=.bootloader=$(BOOTLOAD) 
LDFLAGS += $(EXTMEMOPTS)
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)

which positions the BOOTLOADER_SECTION code to byte address 3800, word address 1C00.

I then just added your code to my main() with:

typedef void (* fn_ptr_t) (void);
...
	fn_ptr_t my_ptr = (void *)0x1C00;
...
	case 'u':
		my_ptr();
		break;

And when I pressed 'u' in my command interface it jumped to the code at word address 1C00, byte address 3800 as expected. The call to my_ptr() became:

89:       	fn_ptr_t my_ptr = (void *)0x1C00;
+000004F4:   2CE1        MOV     R14,R1           Copy register
+000004F5:   E12C        LDI     R18,0x1C         Load immediate
+000004F6:   2EF2        MOV     R15,R18          Copy register

293:      					my_ptr();
+000005E4:   01F7        MOVW    R30,R14          Copy register pair
+000005E5:   9509        ICALL                    Indirect call to (Z)

So what you are trying to do WILL work. I can only think that the reason the compiler tried to use an inappropriate RJMP, if your device is so large as to have addresses out of range of relative calls/jumps, is because you built for the wrong MCU= ?

Cliff

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

PS Just a moment though - you said that an RJMP/RCALL was out of range but you also said your boot code was at word address 0xF80. That is byte address 0x1F00. So this is an 8K AVR. Well in an 8K AVR a relative jump/call can jump forward or back 2K words. That is forward of back 4K bytes. So all the program memory in an 8K AVR *IS* reachable with RJMP/RCALL. Remember that if you are down near 0x000 and you want to get to 0xF80 then maybe a forward jump is out of the question as it's more than 2K words/4K bytes away but, because the addresses wrap, the compile will just have coded a -negative offset and jumped backwards from near 000 to F80

Cliff

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

Thank you very much for your support!
You helped me a lot!!!!

I could solve my problem now but it was very tricky:

1. I have updated WinAVR from 20050214 to 20060125
Now the compiler generates correct jumps. I don't know if there was
a compiler bug or if my installation was corrupted.

2. I use Ponyproc2000 to program my AVR devices (ATmega8 in
this case) because avrdude don't work on my WinXP system
(This is a new topic which I will trigger soon).

3. Ponyproc always starts programming from address 0x0000
even if the hex file starts from an other section.

4. To force Ponyproc to put the application into the correct section
I programmed a tool which fills up the pages before the start section
with 0xFF.
Exampel:

ORIGINAL

:1000F00012C02BC02AC029C028C027C026C025C0D6
:1001000024C023C022C021C020C01FC01EC01DC0EB
:100110001CC01BC01AC011241FBECFE5D4E0DEBF37
:10012000CDBF10E0A0E6B0E0ECE4F1E002C0059045
:100130000D92A036B107D9F710E0A0E6B0E001C0FB
:0C0140001D92A036B107E1F705C0D2CF38
:10014C008FEF87BB18BA0895CFE5D4E0DEBFCDBFE3
:0A015C00F7DF88B3809588BBFCCF65
:04000003000000F009
:00000001FF

FILLED UP

:10000000000000F0FFFFFFFFFFFFFFFFFFFFFFFF0C
:10001000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0
:10002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
:10003000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0
:10004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
:10005000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
:10006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
:10007000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90
:10008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
:10009000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70
:1000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
:1000B000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50
:1000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
:1000D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF30
:1000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
:1000F00012C02BC02AC029C028C027C026C025C0D6
:1001000024C023C022C021C020C01FC01EC01DC0EB
:100110001CC01BC01AC011241FBECFE5D4E0DEBF37
:10012000CDBF10E0A0E6B0E0ECE4F1E002C0059045
:100130000D92A036B107D9F710E0A0E6B0E001C0FB
:0C0140001D92A036B107E1F705C0D2CF38
:10014C008FEF87BB18BA0895CFE5D4E0DEBFCDBFE3
:0A015C00F7DF88B3809588BBFCCF65

When I put this way a program into the bootloader section and
programed the BOOTRST fuse the application were executed.
With BOOTRST unprogrammed the application were executed, too.
Because of the jump call in the first words starting from address 0x0000
(see FILLED UP hex file).

5. In the next step I put an other application into the device
and stored it from address 0x0000 upwards (only write - no erase).
This application should only call the program in the bootloader section.
But the hardware did nothing

6. Then I saw that my 2nd application were not programmed correct.
The already written words starting from 0x0000 were corrupted.
I think that bits in the flash only can be set by the erase command and
cleared by the program command.

7. I changed my fill up tool. Now I can select if the jump command
at address 0x0000 shall be cleared or not.
Now everything works fine!

I have uploaded my fill up tool. Maybe you're interested to look at it or
you even think it is useful.
It is a Microsoft Excel macro. I have programmed it with Excel 2003
but I guess it works even with Excel 2000.
YOU MUST ENABLE MACROS to execute this tool. This can be done
in menue Extras->Macro->Security

After opening the Excel sheet you see a tiny little button. Click on it
and a Open File Dialog appears. Select the *.hex file you want to fill
up and click OK. If the original file name was main.hex a new file is
created with the name fill_up_main.hex in the same folder than the
original file. The original file stays unchanged!

REGARDS
Sebastian

Attachment(s): 

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

> I think that bits in the flash only can be set by the erase command
> and cleared by the program command.

That's the way all current ROMs (thus also all flash ROMs, EEPROMs
etc.) do work. Bit memory cells default to `1', and can be programmed
to `0', but only erased back to `1'. (That's also the reason why you
set a fuse bit to 0 to `program' it.)

But: there's no need for your specific jump instruction at the
beginning. Just leave the empty ROM at 0xFFFF. Even though not
documented, this is effectively a NOP instruction, so the CPU will
quickly run once through the entire ROM, and then starts executing
your boot loader. Of course, if the boot loader wants to program
anything into a ROM page that has been written before, it first needs
to erase that page.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

0xFFFF means NOP!!!
Good to know.
Thank you Jörg

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

By the way, I've been playing about using BOOTLOADER_SECTION to locate some of my routines into the BLS and came across something a bit odd. I have a few routines I want to locate from word address 1C00 (byte 3800) onwards so I defined them as BOOTLOADER_SECTION then, as noted above, I changed my Makefile to include:

#BOOTLOAD = 0x3800

LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
#LDFLAGS += -Wl,--section-start=.bootloader=$(BOOTLOAD) 
LDFLAGS += $(EXTMEMOPTS)
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)

(but with the two #-ed lines active)

When I build this code and load it into AVR Studio and from there down a JTAGICE mkII into my Mega16 target the C pre-amble does not copy the .data initialisers from flash with LPM and position them at the start of the SRAM. Instead the block of source data for the LPM that is supposed to initialise .data is copying from a block of 0xFF in unused ROM space.

I'm sure it's just something I'm doing wrong by using/repositioning .bootloader in addition to .text and .data but as yet I'm not sure exactly what that wrong thing is?

Cliff

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

Some more details on this .data initialiser problem I've seen. When I do build to locate a routine in .bootloader and position it at 3800 I find:

OUTPUT(sounds.elf elf32-avr)

.bootloader     0x00003800       0x94
 .bootloader    0x00003800       0x94 dflash.o
                0x00003800                my_spm_routine

and also:

                0x00002584                _etext = .

.data           0x00800060       0xd2 load address 0x00002584

And, indeed, I can see the loop that copies the .data initial values starts to read from 2584. But the problem is that is not where the .data initial values are. When I build without trying to use BOOTLOADER_SECTION I get:

                0x00002618                _etext = .

.data           0x00800060       0xd2 load address 0x00002618

And in BOTH cases that address, 2618, IS where the initial values are in program memory. It's just that in the case where BOOTLOADER_SECTION is used the value of _etext and hence the place from which the initial values are copied is offset by the same 0094 bytes which is how much of .text has actually been located to .bootloader instead. That is 2618 - 2584 = 0094

Could the linker script that's use be in error for not subtracting .bootloader size from .text to position .data ? Or is this something I can command using LDFLAGS to the linker?

Cliff

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

Actually, I'm wrong in the above. Of course _etext is 0094 less in the .bootloader used case because those 0094 bytes are somewhere else. The fault actually seems to be in AVR Studio's loading of the .ELF file. If I look at the .hex using a hex editor then in the .bootloader case the initialisers ARE there at 2584 onwards but if I load the .ELF into Studio and look at word address 12C2 which is byte address 2584 the bytes there are all just 0xFF and yet the bytes right up to this location (at the end of .text) match the .HEX

So it's not a build problem at all it's a debugger problem and I have a sneaking suspicion it's one that's known about as I've read of Studio having problem with avr-gcc generated bootloader code.

Cliff

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

AVR Studio is known to not handle anything else but the
standard sections from an ELF file (.text, .data, .bss).

They should perhaps provide an option for users to load
their custom hex file, and connect it to an ELF file to
obtain the debugging information. That's the way VMlab
is working. But better discuss that in the AVR Studio
forum I'd say.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Jörg,

Now I realise it's a limitation of Studio I agree this is the wrong place to continue dicussion about it except to say that I finally found the option for JTAGICE mkII in Studio which says "don't download program code" so it'll be possible to use the JTAG programmer functions to program the .HEX and then use the JTAG debugger functions with "don't download" in order to then debug code that includes .bootloader but I think I'll leave the development of my SPM code until the end so I don't have to do this all the time.

For anyone else reading this who's affected, to prevent JTAG downloading to the device at the start of debug one just has to set Debug-Select Platform & Device..-select JTAGICE mkII and the target AVR and then TICK THE OPEN PLATFORM OPTIONS BOX. That's the magic to get access to the propery sheet in which the "debug" tab has the option for "don't reprogram device".

The curious thing though is that the actual BOOTLOADER_SECTION routine, for which the code is located in the .bootloader section at 1C00, is loaded from the .ELF by Studio? (it's just the existence of a .bootloader that seems to upset Studio's view of where .data is)

Cliff

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

>... I finally found the option for JTAGICE mkII in Studio
> which says "don't download program code"

Yeah, it's well hidden, isn't it? I really don't love AVR
Studio at all (and normally don't use it myself).

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

I found this in
AVRRAVEN_1284p_BOOTLOADER
AvrStudio ProjectOptions LinkerOptions

--gc-sections
-mrelax
-Wl,--section-start=.text=1E000

So far looking at the output map file it seems to work.
I've AVR studio 4.16
There is also a Memory Setting to define the bootloader flash, but not sure this actually makes a difference
:D

Thanks for everyones posts - its helped me track down that I wasn't the only one having problems.

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

Note: You've replied to a 3 year old thread.

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

But, I would have never noticed this without someone else blundering into it.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

I was looking for a more current thread too to ask another question.
Avr109 - self programming doesn't seem to deal with how to go through the 0x10000 boundary for the ATmega128 and ATmega256
This must have been solved but haven't seen any replies. I'm experimenting with the "MegaBootLoaderBLIPS" PC i/f but with a 'c' version of the bootcode that is based on AVR109 file.
Any threads out there discussing this or a good place to start a new thread.
I'm looking to combine the AVR109 approach and also build in an AVR2017-Raven load from a flash bank
:o

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

neilh wrote:
I was looking for a more current thread too to ask another question.

neilh wrote:

Any threads out there discussing this or a good place to start a new thread.

You don't need to find a related thread (no matter how old) to ask a question. In fact, asking an unrelated question that doesn't match a topic is called "hijacking a thread" and is usually considered impolite.

You can always start a new thread. See the "New Topic" button at the top. It's very easy to do. :wink:

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

Hi neilh,
not sure what you are talking about, but i'm trying to build a bootloader for the atmega2560 based on avr109 myself.

The command for big devices is not 'A' but 'H'. The APN describes two bytes for 'A'. With the 'H' it's 3 bytes, MSByte first.
Have a look at avrprog. This already follows the avr109 protocol

Please post a link if you made a new thread.
EDIT: Guess i found it https://www.avrfreaks.net/index.p...