Allocating functions at absolute address

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

Hello,

i'm trying to realize my self-updatable bootloader.

 

The working theory is the same as described in other threads: i have the main bootloader allocated at the boot-start address,

then a small function to update the bootloader, allocated at the last bootloader page.

When bootloader updating is needed, the main bootloader will load its new version into RAM, then will call the bootloader update routine

which will erase and update the main bootloader.

Obviously no interrupts will be used in boot update routine and also all SP functions used to write to boot area are to be allocated

after boot update routine.

In my case i have the main bootloader start at 0x40000 and i would put the bootloader update routine at 0x41E00 which is the

address of the last page in bootloader area.

 

It seems simple, but i didn't realize how to allocate boot-uploader and sp routines at that specific address.

 

First thing i tried, in the same bootloader AS6 project where i moved the .text section to 0x20000 (0x40000 in words), 

i added a second named section .lastbootpage=0x20F00.

Then i declared a boot-update routine like:

 

__attribute__((section(".lastbootpage"))) void boot_update (void )

It works, the routine is then allocated at 0x41E00, but then i wanted to put some routines of sp_driver.s also there and i did:

.section .lastbootpage		
.global SP_EraseWriteBootPage

SP_EraseWriteBootPage:
	in	r19, RAMPZ   
	...
	...

No compilation errors are given by avr-gcc, but the routines are then not allocated at that section-specific address any more.

It seems like section allocation for lastbootpage works when only one routine is declared to be allocated there.

 

My second trial was then to start a new project only containing the boot-update routine and its related sp_driver.s routines.

So i did, declaring section .text=0x20E00 and writing down my routine.

Looking the generated assembler seems now ok, but the interrupt vector table and clear bss and some other stuff is

added before the update routine.

 

I've looked around the forum and i've tried to use the -nostartfiles flag to get rid of that code i'll not use: no bss clear, no

interrupt table is needed, just the boot-update routine.

But the weird is now that, using the -nostartfiles flag, only the bss clear code is generated (16 bytes), no other code.

Here it is:

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00000010  00041e00  00041e00  00000074  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000000  00802000  00041e10  00000084  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 .comment      0000002f  00000000  00000000  00000084  2**0
                  CONTENTS, READONLY
  3 .debug_aranges 00000068  00000000  00000000  000000b8  2**3
                  CONTENTS, READONLY, DEBUGGING
  4 .debug_info   000003e1  00000000  00000000  00000120  2**0
                  CONTENTS, READONLY, DEBUGGING
  5 .debug_abbrev 0000017b  00000000  00000000  00000501  2**0
                  CONTENTS, READONLY, DEBUGGING
  6 .debug_line   00000331  00000000  00000000  0000067c  2**0
                  CONTENTS, READONLY, DEBUGGING
  7 .debug_frame  00000024  00000000  00000000  000009b0  2**2
                  CONTENTS, READONLY, DEBUGGING
  8 .debug_str    00000197  00000000  00000000  000009d4  2**0
                  CONTENTS, READONLY, DEBUGGING
  9 .debug_ranges 00000030  00000000  00000000  00000b70  2**3
                  CONTENTS, READONLY, DEBUGGING

Disassembly of section .text:

00041e00 <__ctors_end>:
   41e00:	20 e2       	ldi	r18, 0x20	; 32
   41e02:	a0 e0       	ldi	r26, 0x00	; 0
   41e04:	b0 e2       	ldi	r27, 0x20	; 32
   41e06:	01 c0       	rjmp	.+2      	; 0x41e0a <.do_clear_bss_start>

00041e08 <.do_clear_bss_loop>:
   41e08:	1d 92       	st	X+, r1

00041e0a <.do_clear_bss_start>:
   41e0a:	a0 30       	cpi	r26, 0x00	; 0
   41e0c:	b2 07       	cpc	r27, r18
   41e0e:	e1 f7       	brne	.-8      	; 0x41e08 <.do_clear_bss_loop>

 

So, how can i allocate more than 1 piece of code at a specific address as i tried on my first attempt, or how can i correctly set the avr-gcc to generate only

the code related to the "main" routine ? no intialization, no vectors, nothing more than "int main (void)" compiled ?

 

Many thanks for your help.

Tom.

 

 

 

 

This topic has a solution.
Last Edited: Wed. Jan 27, 2016 - 09:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you post a compilable example of what you are talking about as I don't really follow what your issue is but here are some general points:

 

1) Atmel Studio's "memories" section takes word not byte addresses so always eneter half what you want (in bytes) there

 

2) You can achieve the same as ".text=0x1234" in memories using either a separate -Wl,-section-start=0x2468" or the short form -Wl,-Ttext=0x2468

 

3) GCC always links with the CRT (C Run Time) unless you tell it not to using -nostartfiles. It used to be the case that the CRT consisted of:

 

a) JMP at reset

b) a full vector table

c) clear R1

d) SREG = R1

e) SP = RAMEND

f) _do_copy_data()

g) _do_clear_bss()

h) a CALL main that actually enters you C code

i) JMP to _exit

j) _exit provides CLI then a while(1)

 

but the technology that provide (f) and (g) are now broken out separately. So -nostartiles means you don't get a), b), c), d), e), h), i) and j) these days. Instead the first linked file is simply positioned at the base of .text (wherever you -section-start'd it to). Because SREG is not cleared (to diable I) and R1 is not loaded with 0 and SP is not set to RAMEND you may want to do all these things in the code you write (though modern AVRs set SP to RAMEND and power on anyway). This also lifts the requirement for the "first function" to be called main(). If you are writing partly in C the tendency is to keep calling it main() anyway as the user will look for that to understand the program flow but if you are only writing avr-as code you no longer need to call your entry "main:" but can call it "entry:" or "reset:" or whataver you choose.

 

4) To position anything in GCC to a fixed address is a two stage process. One is to arrange for the code or data to be in a named section (though sometimes you rely on the default of code going to ".text" and then move that). The other is to tell the linker where you want that section. In C this is:

__attribute__((section(".mysect"))) int some_fn(type var1, type var2...) {
    // stuff
}

then:

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

replacing 1234 with the absolute address in bytes you want to use. To know how to do this in Asm just see what the C compiler does:

$ cat avr.c
#include <avr/io.h>

__attribute__((section(".mysect"))) int some_fn(char var1, int var2) {
    // stuff
    PORTB = 0x55;
}

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

$ avr-gcc -mmcu=atmega16 -Os -save-temps -c avr.c -o avr.o
$ cat avr.s
	.file	"avr.c"
__SREG__ = 0x3f
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__CCP__ = 0x34
__tmp_reg__ = 0
__zero_reg__ = 1
	.section	.mysect,"ax",@progbits
.global	some_fn
	.type	some_fn, @function
some_fn:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	ldi r24,lo8(85)
	out 56-32,r24
/* epilogue start */
	ret
	.size	some_fn, .-some_fn
etc.

So, as you have above it is nothing more than using ".section"

 

To then place this section you use -Wl,-section-start like this:

$ avr-gcc -mmcu=atmega16 -Wl,-section-start=.mysect=0x1234 avr.c -o avr.elf
$ avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .mysect:

00001234 <some_fn>:
    1234:	df 93       	push	r29
    1236:	cf 93       	push	r28
    1238:	00 d0       	rcall	.+0      	; 0x123a <some_fn+0x6>
    123a:	0f 92       	push	r0
    123c:	cd b7       	in	r28, 0x3d	; 61
    123e:	de b7       	in	r29, 0x3e	; 62
    1240:	89 83       	std	Y+1, r24	; 0x01
    1242:	7b 83       	std	Y+3, r23	; 0x03
    1244:	6a 83       	std	Y+2, r22	; 0x02
    1246:	88 e3       	ldi	r24, 0x38	; 56
    1248:	90 e0       	ldi	r25, 0x00	; 0
    124a:	25 e5       	ldi	r18, 0x55	; 85
    124c:	fc 01       	movw	r30, r24
    124e:	20 83       	st	Z, r18
    1250:	0f 90       	pop	r0
    1252:	0f 90       	pop	r0
    1254:	0f 90       	pop	r0
    1256:	cf 91       	pop	r28
    1258:	df 91       	pop	r29
    125a:	08 95       	ret

Disassembly of section .text:

00000000 <__vectors>:
   0:	0c 94 2a 00 	jmp	0x54	; 0x54 <__ctors_end>
   4:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
   8:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
   c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  10:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  14:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  18:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  1c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  20:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  24:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  28:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  2c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  30:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  34:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  38:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  3c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  40:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  44:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  48:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  4c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  50:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>

00000054 <__ctors_end>:
  54:	11 24       	eor	r1, r1
  56:	1f be       	out	0x3f, r1	; 63
  58:	cf e5       	ldi	r28, 0x5F	; 95
  5a:	d4 e0       	ldi	r29, 0x04	; 4
  5c:	de bf       	out	0x3e, r29	; 62
  5e:	cd bf       	out	0x3d, r28	; 61
  60:	0e 94 36 00 	call	0x6c	; 0x6c <main>
  64:	0c 94 3b 00 	jmp	0x76	; 0x76 <_exit>

00000068 <__bad_interrupt>:
  68:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

0000006c <main>:
  6c:	df 93       	push	r29
  6e:	cf 93       	push	r28
  70:	cd b7       	in	r28, 0x3d	; 61
  72:	de b7       	in	r29, 0x3e	; 62
  74:	ff cf       	rjmp	.-2      	; 0x74 <main+0x8>

00000076 <_exit>:
  76:	f8 94       	cli

00000078 <__stop_program>:
  78:	ff cf       	rjmp	.-2      	; 0x78 <__stop_program>

As you can see this placed some_fn() at 0x1234 with everything else (including the CRT) left down at 0x0000. If I had multiple functions to be "lifted up" I would do this:

$ cat avr.c
#include <avr/io.h>

#define HIGH_FN __attribute__((section(".mysect")))

HIGH_FN int some_fn(char var1, int var2) {
    PORTB = 0x55;
}

HIGH_FN int another_fn(char var1, int var2) {
    PORTB = 0xAA;
}

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

$ avr-gcc -mmcu=atmega16 -Wl,-section-start=.mysect=0x1234 avr.c -o avr.elf
$ avr-objdump -S avr.elf

avr.elf:     file format elf32-avr


Disassembly of section .mysect:

00001234 <some_fn>:
    1234:	df 93       	push	r29
    1236:	cf 93       	push	r28
    1238:	00 d0       	rcall	.+0      	; 0x123a <some_fn+0x6>
    123a:	0f 92       	push	r0
    123c:	cd b7       	in	r28, 0x3d	; 61
    123e:	de b7       	in	r29, 0x3e	; 62
    1240:	89 83       	std	Y+1, r24	; 0x01
    1242:	7b 83       	std	Y+3, r23	; 0x03
    1244:	6a 83       	std	Y+2, r22	; 0x02
    1246:	88 e3       	ldi	r24, 0x38	; 56
    1248:	90 e0       	ldi	r25, 0x00	; 0
    124a:	25 e5       	ldi	r18, 0x55	; 85
    124c:	fc 01       	movw	r30, r24
    124e:	20 83       	st	Z, r18
    1250:	0f 90       	pop	r0
    1252:	0f 90       	pop	r0
    1254:	0f 90       	pop	r0
    1256:	cf 91       	pop	r28
    1258:	df 91       	pop	r29
    125a:	08 95       	ret

0000125c <another_fn>:
    125c:	df 93       	push	r29
    125e:	cf 93       	push	r28
    1260:	00 d0       	rcall	.+0      	; 0x1262 <another_fn+0x6>
    1262:	0f 92       	push	r0
    1264:	cd b7       	in	r28, 0x3d	; 61
    1266:	de b7       	in	r29, 0x3e	; 62
    1268:	89 83       	std	Y+1, r24	; 0x01
    126a:	7b 83       	std	Y+3, r23	; 0x03
    126c:	6a 83       	std	Y+2, r22	; 0x02
    126e:	88 e3       	ldi	r24, 0x38	; 56
    1270:	90 e0       	ldi	r25, 0x00	; 0
    1272:	2a ea       	ldi	r18, 0xAA	; 170
    1274:	fc 01       	movw	r30, r24
    1276:	20 83       	st	Z, r18
    1278:	0f 90       	pop	r0
    127a:	0f 90       	pop	r0
    127c:	0f 90       	pop	r0
    127e:	cf 91       	pop	r28
    1280:	df 91       	pop	r29
    1282:	08 95       	ret
etc.

As you can see both functions have been "lifted" to sit consecutively from 0x1234 onwards.

 

if this was Asm I would just need the one ".Section" before the two routines. I'd switch back to the "default" section with ".text"

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

Hello Clawson,

thanks for answering.

 

My problems are mainly two:

the first is that it seems that atmel studio will allocate something at a fixed address only if i use __attribute__ ((section(.mysection)) once.

In the project for the complete bootloader, i started putting that attribute near my bootloader update routine and it was correctly allocated where i wanted.

Then, when i added the same section to the assembly code into sp_driver.s, to have those routines also allocated where my

bootloader updating routine was, the generated code was no more allocated there, as no section was defined at all.

 

The second is that i then started a new project (again in AS6) intended for the bootloader updating routine only,

where i assigned the address where i wanted to allocate that code to the .text section.

Here everything was ok, all my code and sp_driver.s was put into the last bootloader page, but also the

CRT was linked.

Using the -nostartfiles flag (i set it under the AVR/GNU Linker, General page) i get no interrupt table in assembly output, but

also no other code than this (consider that 0x41e00 is the address where my routine should begin)

 

Disassembly of section .text:

00041e00 <__ctors_end>:
   41e00:	20 e2       	ldi	r18, 0x20	; 32
   41e02:	a0 e0       	ldi	r26, 0x00	; 0
   41e04:	b0 e2       	ldi	r27, 0x20	; 32
   41e06:	01 c0       	rjmp	.+2      	; 0x41e0a <.do_clear_bss_start>

00041e08 <.do_clear_bss_loop>:
   41e08:	1d 92       	st	X+, r1

00041e0a <.do_clear_bss_start>:
   41e0a:	a0 30       	cpi	r26, 0x00	; 0
   41e0c:	b2 07       	cpc	r27, r18
   41e0e:	e1 f7       	brne	.-8      	; 0x41e08 <.do_clear_bss_loop>

I'll read then your answer deeply, eventually posting the project.

 

Thanks for the moment.

Tom

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

Tomass wrote:
My problems are mainly two: the first is that it seems that atmel studio will allocate something at a fixed address only if i use __attribute__ ((section(.mysection)) once.

I don't believe you. This is nothing to do with Studio (at the end of the day it's just a text editor and a Makefile generator) it's to do with how you use the compiler and linker. As my example above showed I can easily assign two functions to one section and they are both placed (consecutively) in that section.

Tomass wrote:
Then, when i added the same section to the assembly code into sp_driver.s, to have those routines also allocated where my bootloader updating routine was, the generated code was no more allocated there, as no section was defined at all.
Show the smallest possible compilable test project that demonstrates that.
Tomass wrote:
but also the CRT was linked.
But that's what -nostartfiles is for.
Tomass wrote:
but also no other code than this
Oh right - I see exactly what is going on then. Have you heard of -ffunction-sections and -gc-sections? This a relatively recent addition to the compiler to remove anything in the build that is apparently not used. As the compiler builds the code with -ffunction-sections it actually puts each function into a separate named section (not just the default ".text"). Then when the code is linked with the -gc-sections ("garbage collect" sections) option the linker keeps track on the access count to each section of the code. If, at the end of the link, it finds any section with access=0 it does not write it to the output image because it knows nothing called it.

 

So now consider a "normal" C program. It all starts with the CRT (which cannot be discarded). That calls main(). main() calls foo(), foo() calls bar(). When the linker looks at the access to main.sect and foo.sect and bar.sect it sees access_count=1 as they all got called (there might have been some other function called barf() that never got called so barf.sect has access=0 and won't be in the final output). But in this the only reason main(), then foo() then bar() got accessed is because it all started with that very first "CALL main" in the CRT.

 

Now build with -nostartfiles and that "CALL main" goes away. So main() has an access=0, foo() has access=0, bar() has access=0 (and barf()). None of them are apparently accessed so -gc-sections discards them all.

 

Bottom line: if you use -nostartfiles then turn off -gc-sections too (there's a tick box for that in the linker options!)

Last Edited: Mon. Jan 25, 2016 - 01:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Great man!

I usually set gc flag on for obvious reasons, but i was not considering that no calls to main by CRT = no compiled main.

Anyway i'll look deeply into your considerations in using the section attributes correctly : in another project i've a lot of code

used to load some other ICs on the board and i wanted to put it at the end of flash, and i was getting the same 

strange behaviour, so when i used __attribute__ on a single part of code it was ok, but when i used twice it was not working.

Surely i was doing something wrong, now i'll learn how to do it correctly.

 

Anyway maaany thanks again!

Tom.

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

Hello Clawson,

 

here i attached the project.

The problem is this:

if i leave the text section alone and i define mysection=0x20f00 i get what expected,

so my main function is then allocated at 0x41E00, while all other sp_driver.s stuff is allocated

starting from address 0.

If now i try to put any of the sp_driver.s functions at mysection address, i get what i told before, so

everything starts at address 0, also the main routine which was previously set at mysection address.

 

If then i don't use "mysection" and i re-allocate .text at 0x20f00 i get everything starting at

0x41e00, but the problem is that i would like the main routine sitting there, instead i get all

sp_driver.s compiled at 0x41e00 and the main routine at address 0x41-and-something,

after all sp_driver routines.

 

So, is there now any special way to have the c source compiled before the sp_driver.s file ?

 

 

Attachment(s): 

Last Edited: Mon. Jan 25, 2016 - 04:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not near Studio right now so I can only read the files statically but I'm looking at both sp_driver.S and Debug/Makefile. The former has most of the routines in ".text" though a few (enough?) are in ".BOOT" but when I look at the linker rule in the Makefile I find this:

	$(QUOTE)C:\Program Files (x86)\Atmel\Atmel Toolchain\AVR8 GCC\Native\3.4.1056\avr8-gnu-toolchain\bin\avr-gcc.exe$(QUOTE) -o$(OUTPUT_FILE_PATH_AS_ARGS) $(OBJS_AS_ARGS) $(USER_OBJS) $(LIBS) -nostartfiles -Wl,-Map="stubloader.map" -Wl,--start-group -Wl,-lm  -Wl,--end-group -mrelax -Wl,-section-start=.text=0x41e00  -mmcu=atxmega256a3b -fno-jump-tables 

So, yeah that has a -section-start for .text (though 0x41E00 seems an "odd" address?) but where is the -section-start that tells the linker where to place the stuff in .BOOT?

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

So, yeah that has a -section-start for .text (though 0x41E00 seems an "odd" address?) but where is the -section-start that tells the linker where to place the stuff in .BOOT?

 right,  the .BOOT section was defined into an external makefile (i took sp_driver.s out of another bootloader where all compilation was driven by a makefile and .BOOT section was defined there).

I renamed .BOOT to .text, but it made no difference on the compiled output.

Why is 0x41E00 an odd address ? I would allocate the bootloader-updating function on the last bootloader page so, correct me if i'm wrong, the bootloader (for a 256K device) starts

at 0x40000 and is 0x2000 bytes long, each page is 0x200 bytes long so the last bootloader page is at 0x40000+0x2000-0x200=0x41E00, right?

There i would my "main" routine to be allocated, and the other sp_driver.s stuff after the main routine, but what i'm getting is 

 

Disassembly of section .text:

00041e00 <SP_ReadByte>:

......(sp_readbyte and sp_driver.s code)

....

....

00041f28 <main>:

 

...main function is at address 0x41f28 and i would have it at address 0x41e00 like this

 

00041e00 <main>:

(main code)

00041something <SP_ReadByte>:

(sp_driver.s code)

Last Edited: Mon. Jan 25, 2016 - 06:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tomass wrote:
I renamed .BOOT to .text, but it made no difference on the compiled output.

But there are still some functions being placed in .BOOT in that file?

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

Well,

everything should go to .BOOT section: i took that sp_driver.s out of an Xmega bootloader i'm trying to convert into a

self-updatable one.

The sp_driver has all routines assigned to be into .text section, while only that spcommon_something was assigned

to .BOOT section. Nonetheless the makefile which came with that bootloader genrated an executable for boot section

(nothing was into .text section).

 

At this moment, the problem is that i would have the "main" routine at address 0x41e00 and all other stuff coming

out sp_driver.s placed after my main routine, while what i get is exactly the contrary: everything else is placed

at address 0x41e000 while the main routine, the one i'll call to perform bootloader updating, is placed after.

 

 

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

Why does it matter? Normally you don't care where in a section or in what order the linker chooses to place functions.

 

The one exception I can think of is the reset JMP and IVT for an AVR. Those have to be placed first at a known/fixed address which is why they are all assigned to ".vectors" and the default linker scripts have:

  /* Internal text space or external memory.  */
  .text   :
  {
    *(.vectors)
    KEEP(*(.vectors))
    /* For data that needs to reside in the lower 64k of progmem.  */
    *(.progmem.gcc*)
    *(.progmem*)
    . = ALIGN(2);
     __trampolines_start = . ;
etc.

which ensures that .vectors gets paced right at the start. If there's really some reason you want main() before everything else then assign it to a different named section then arrange for that to be placed first (a "private .x file and -Wl,-T myscript.x" to the linker would do this).

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

Hello Clawson,

please let me summarize:

 

i have a bootloader, and i would like to make it self-updatable.

In order to do this, and as you know, i've to launch spm instructions from the bootloader memory area (no spm is allowed from application area).

So the main bootloader, which will be complete in every part (interrupt vectors table, initializers, everything else),

will reside at BOOT_SECTION_START, and a minimal erase-and-reprogam function (the one i'm asking help for addressing correctly) which will

reside starting on the last boot section page, in my case address 0x41e00.

 

When i want to change the main bootloader, i'll made it to load its new copy in ram then,

after turning off all interrupts (the IV table will be wiped out then), i'll jump to location

0x41e00 (so this is the reason why address does matter to me) where i'll erase the main bootloader and substitute

it with the copy i had in ram.

Once did this, i'll reset the MCU, so everything will be cleared and the reset vector will jump to the main (and updated)

bootloader. If then nothing else is to be done by the main bootloader, it will jump to application.

 

As i wrote, my last problem now is that everything is placed correctly starting from address 0x41e00, but i would like

that the bootloader updating function is placed there (at address 0x41e00), then all other stuff following.

 

As you said i could also don't care about my main routine being allocated where i want: it is important for me to

have all code allocated starting from 0x41e00, so then i could take the main function address eg: 0x41f80 and

jump there from the main bootloader.

This could work indeed, but i think it is not a solution, rather a workaround.

 

I honestly say that GCC has so many tweaks and switches and tricks and i got really drowned into its docs,

but it seems strange to me that it cannot do this: allocating something "there" and something "after", by

my choice, the starting address i give.

 

Bye

Tom.

 

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

But it doesn't matter the actual order/location of functions in the "top bootloader". I'd just put a fixed jump-table at the very end of flash (0x41FF0 ?) with the address of the 6 or 8 functions in "top" that you might want to share down to the lower bootloader or even the app. Something like (effectively):

.org 0x41FF0

JMP SPM_load_buffer
JMP SPM_erase_page
JMP SPM_write_page
JMP <whatever else>

The code that wants to call SPM_write_page() (say) does something like:

typedef int (*fptr_t)(char *, int n);

fptr_t SPM_write_page = (fptr_t)0x41FF8;

....
    SPM_write_page(buffer, byte_count);

Of course you can't really ".org 0x41FF0" like that - but you can use "section ".foo"" and then later -Wl,-section-start=.foo=0x41FF0.

 

This small jump table is built as part of a completely separate top.elf with those name routines within. The low bootloader is built as a second program and the app as a third.

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

Hello Clawson,

 

i don't care about order of functions in the top/main bootloader, since i cannot use them from the second bootloader, expecially during its update.

I could use a table as you suggest to perfom a "clean" jump to the main bootloader, once the second bootloader has updated it: indeed, rebuilding

the main bootloader will result in different addresses for the same functions, so a jump table will be useful to the second bootloader to

recall the same functions once updated.

 

But my problem is different: i don't need to recall the main bootloader functions from the second bootloader.

I only need to jump from the first to the second when the first is to be updated, and my problem is that

the first doesn't know where to jump exactly to enter the second bootloader main routine.

The second bootloader was addressed starting from 0x41e00, but this address was not

its "main" routine, instead some other.

 

I think i finally found a simple solution: i put the second bootloader main routine into .init9 .

 

Disassembly of section .text:

00041e00 <main>:

__attribute__ ((section(".init9"))) int main(void)
{
    while(1)
    {
        PORTE.DIR=0xff;
   41e00:    8f ef           ldi    r24, 0xFF    ; 255
   41e02:    80 93 80 06     sts    0x0680, r24
        PORTE.OUTTGL=0xff;
   41e06:    80 93 87 06     sts    0x0687, r24
   41e0a:    fb cf           rjmp    .-10         ; 0x41e02 <main+0x2>

00041e0c <SP_ReadByte>:

....

....

So now my second bootloader main routine sits at address 0x41e00 (and will always be there) so i can jump there directly from the first bootloader.

Is this correct ?

 

Bye

Tom.

 

Last Edited: Wed. Jan 27, 2016 - 04:18 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tomass wrote:
Is this correct ?

It'll work but in theory .init9 is a "fall through" section and should be "naked". But that only matters when the CRT is used. If building -nostartfiles there is no CRT to fall through to - so it doesn't matter in this case. So, yeah, this solves it. In fact if you really want main() at the very start (and -nostartfiles) then you could put it in ".vectors" which is the section that is guaranteed to be at the very start.

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

Hello Clawson,

 

many thanks for your suggestions, everything is working fine.

 

There's something still unclear regarding why assigning the same

section to one or more routines, sometimes results as no section was

assigned at all, but maybe i'll start a new thread for this.

 

Kind regards

Tom.

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

Tomass wrote:
There's something still unclear regarding why assigning the same section to one or more routines, sometimes results as no section was assigned at all, but maybe i'll start a new thread for this.

If you do then post the smallest compilable example that demonstrates that happening (personally I don't believe it ;-)

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

Hello Clawson,

you're right (again).

 

The problem happens if PROGMEM is  mixed up with __attribute__((section(".mysect))) in something like this:

 

 __attribute__ ((section(".mysect")))  const unsigned char  array_one[] PROGMEM ={1,2,3,4,5} ;

 

When both are present, allocation at "mysect" is not done.

 

Now i'm asking (forgive the dumb question),  how can i allocate an array to my section while specifying also the PROGMEM attribute?

Reading from http://www.nongnu.org/avr-libc/user-manual/pgmspace.html , it seems that if no PROGMEM keyword is used,

the const array goes also to ram (??).

 

Until now i did not use sections to allocate large PROGMEM arrays, but now i should put a lot of stuff (array tables) in the area above 128K.

For this reason i'm asking what is the right way to do this.

 

Thanks.

Tom.

 

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

Tomass wrote:
When both are present, allocation at "mysect" is not done.

You do know that PROGMEM is effectively just:

#define PROGMEM __attribute__((section(".progmem")))

don't you. So you are effectively asking for the data to be position in TWO sections at once. I'm not sure which one "wins" but from your example it looks like the first!

 

In reality the PROGMEM is really:

#define PROGMEM __attribute__((progmem))

but it's simply that "inside" the compiler the "progmem" attribute just means "assign this to section .progmem".

 

But stop and think about this for a moment. If I just:

 __attribute__ ((section(".mysect")))  const unsigned char  array_one[] ={1,2,3,4,5} ;

and then:

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

then that ALREADY puts that into flash (exactly the same as progmem data!). The only difference between ".mysect" and ".progmem" is that for the first you get to say where in flash it will be (in fact you HAVE to say where it will be). For .progmem it's left to the linker (actually the linker script) to say where it goes. The default linker scripts arrange for:

  .text   :
  {
    *(.vectors)
    KEEP(*(.vectors))
    /* For data that needs to reside in the lower 64k of progmem.  */
    *(.progmem.gcc*)
    *(.progmem*)
    . = ALIGN(2);
     __trampolines_start = . ;
    /* The jump trampolines for the 16-bit limited relocs will reside here.  */
    *(.trampolines)
    *(.trampolines*)
     __trampolines_end = . ;
etc.

which means anything you assign to PROGMEM will be put as close to 0x0000 as it possibly can be (just above ".vectors" which holds the reset jump and the fixed address vector table). It's done like this to try (if there is a lot of PROGMEM data) to get as much of it into the easier to access first 64K.

 

Coming back to you ".mysect" the fact is that the only thing that says "this is to go into the code flash" is the address of it. Because the AVR is "Harvard" but the GCC tools only know about von Neumann (well they do until you start to explore "__flash" and "__memx"!) then the developers have chosen to break one big linear address map into sections:

  text   (rx)   : ORIGIN = 0, LENGTH = 128K
  data   (rw!x) : ORIGIN = 0x800060, LENGTH = 0xffa0
  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K
  fuse      (rw!x) : ORIGIN = 0x820000, LENGTH = 1K
  lock      (rw!x) : ORIGIN = 0x830000, LENGTH = 1K
  signature (rw!x) : ORIGIN = 0x840000, LENGTH = 1K

So if you specify a section-start address from 0x000000 to 0x7FFFFF then that means "code" (aka "text"). If you use 0x800000..0x80FFFF then it is "RAM". If you use 0x810000..0x81FFFF it is "EEPROM" and so on for fuse, lock and signature too. So given:

 __attribute__ ((section(".mysect")))  const unsigned char  array_one[] ={1,2,3,4,5} ;

you might use:

-Wl,-section-start=.mysect=0x800200

and that puts it in RAM so you would then access it in the C with:

char * p = (char *)0x200;
PORTB = p[3];

But if you used:

-Wl,-section-start=.mysect=0x200

that puts it in flash. So then the C would be (ignoring __flash for the time being):

const char * p = (const char *)0x200;
PORTB = pgm_read_byte(p + 3);

Or you could even use:

-Wl,-section-start=.mysect=0x810200

which is EEPROM and then:

char * p = (char *)0x200;
PORTB = eeprom_read_byte(p + 3);

Bottom line: unless you want the linker to pick addresses for you down near 0 then forget PROGMEM and instead just use a named section and your own address then pgm_read_*() the data from there.

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

Hello Clawson

don't you. So you are effectively asking for the data to be position in TWO sections at once. I'm not sure which one "wins" but from your example it looks like the first!

 

In reality the PROGMEM is really:

 yes, i did know that and i was feeling that the problem was what you explained: of course i cannot assign something to two different sections.

Anyway, regarding this latter, a compiler error or warning message "cannot assign data to two sections", or something like, would have been really useful.

 

But my question arose from my misunderstanding of this (http://www.nongnu.org/avr-libc/user-manual/pgmspace.html)

A Note On const

Many users bring up the idea of using C's keyword const as a means of declaring data to be in Program Space. Doing this would be an abuse of the intended meaning of the const keyword.

constis used to tell the compiler that the data is to be "read-only". It is used to help make it easier for the compiler to make certain transformations, or to help the compiler check for incorrect usage of those variables.

For example, the const keyword is commonly used in many functions as a modifier on the parameter type. This tells the compiler that the function will only use the parameter as read-only and will not modify the contents of the parameter variable.

constwas intended for uses such as this, not as a means to identify where the data should be stored. If it were used as a means to define data storage, then it loses its correct meaning (changes its semantics) in other situations such as in the function parameter example.

So i was thinking, erroneously, that if no PROGMEM attribute was used, data would end up in ram.

 

But the truth is that, if someone declare a variable or array only as "const" it will end up in ram, while

if you use PROGMEM  OR  any other named section of your choice pointing into the flash addresses,

it will go there.

If you use PROGMEM, the linker will try to keep your datas near address 0, if you use your section, it will

put the datas where you want.

 

Much clearer now.

 

Kind regards.

Tom.

 

 

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

const just means "disallow any attempt to modify this". Certainly in AVR8 (well avr-gcc anyway) it has no other meaning. In fact for years people were putting non-const data in PROGMEM but from complier version 4.6 onwards it has been insisting that anything in either PROGMEM or now __flash must also be made "const" (which aids the optimisation decisions the compiler may make).

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

message suppressed because wrong thread. Sorry

AVR inside

Last Edited: Fri. Jul 24, 2020 - 03:54 PM