How to jump to a specific flash address from a C function

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

Hello folks,

 

I am trying to learn how to develop an I2C bootloader for attiny85. What I have so far is a small interrupt-free "bootloader wannabe" that communicates with the master correctly. It is loaded in the upper part of the flash (--section-start = .text = 1B40). In the lower part of the memory, I have a test program that I'm flashing together with the bootloader by combining both .hex in one.

 

I've modified by hand the test program reset vector inside the .hex  to jump to the bootloader after POR, and it works well. What I can not do is to jump from a bootloader C function to the start of the test program (0x1e). I do not know either how can I know the address of the rjpm instruction itself from C, to calculate later the jump to 0x1e from there. This is the original test program, without the reset mod.

Disassembly of section .sec1:
00000000 <.sec1>:
   0:	0e c0       	rjmp	.+28     	;  0x1e
   2:	28 c0       	rjmp	.+80     	;  0x54
   4:	27 c0       	rjmp	.+78     	;  0x54
   6:	26 c0       	rjmp	.+76     	;  0x54
   8:	25 c0       	rjmp	.+74     	;  0x54
   a:	24 c0       	rjmp	.+72     	;  0x54
   c:	23 c0       	rjmp	.+70     	;  0x54
   e:	22 c0       	rjmp	.+68     	;  0x54
  10:	21 c0       	rjmp	.+66     	;  0x54
  12:	20 c0       	rjmp	.+64     	;  0x54
  14:	1f c0       	rjmp	.+62     	;  0x54
  16:	1e c0       	rjmp	.+60     	;  0x54
  18:	1d c0       	rjmp	.+58     	;  0x54
  1a:	1c c0       	rjmp	.+56     	;  0x54
  1c:	1b c0       	rjmp	.+54     	;  0x54
  1e:	11 24       	eor	r1, r1

Can someone please help me with the C code to jump to the testing program? I have tried several things with no success, without knowing exactly what I'm doing:

asm volatile ("rjmp __ctors_end"); // --> Doesn't work!

((void (*)())0x1E)(); // --> Doesn't work!

 

This topic has a solution.
Last Edited: Sat. Jul 7, 2018 - 04:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

asm("rjmp 0x1e"); ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define foo ((void (*)(void))0x1E)

int main() {
    foo();
  38:	ee e1       	ldi	r30, 0x1E	; 30
  3a:	f0 e0       	ldi	r31, 0x00	; 0
  3c:	09 95       	icall
}

 

 

asm("rjmp 0x1e"); ?

I like that better, but it does not seem to do the right thing.  I'm not sure what it DID do...
"jmp 0x1e" works OK, if you have a chip that has the jmp instruction.

 

    asm("rjmp 0x1e");
  86:	0b c0       	rjmp	.+22     	; 0x9e <_etext+0x8>
    asm("jmp 0x1e");
  88:	0c 94 0f 00 	jmp	0x1e	; 0x1e <__FUSE_REGION_LENGTH__+0x1b>

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define foo ((void (*)(void))0x1E)

Personally I don't like the use of #define for this. I would go with a C solution:

typedef void (*fptr_t)(void);

fptr_t foo = (fptr_t) 0x001E;

int main(void) {
    foo();
}

this yields:

int main(void) {
    foo();
  46:   e0 91 60 00     lds     r30, 0x0060     ; 0x800060 <__data_start>
  4a:   f0 91 61 00     lds     r31, 0x0061     ; 0x800061 <__data_start+0x1>
  4e:   09 95           icall
  50:   08 95           ret

 

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

asm("rjmp 0x1e"); ?

This was one of my first attempts, it didn't work.

 

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

Thank you clawson, for some reason, it ends on the bootloader again ...

 

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

I ran this test:

typedef void (*fptr_t)(void);

int bar(int n) {
    return n+1;
}

fptr_t foo = (fptr_t) bar;

int main(void) {
    foo();
}

(and yes, I know the fn interface doesn't agree!) and what I find is:

00000046 <bar>:
  46:   01 96           adiw    r24, 0x01       ; 1
  48:   08 95           ret

so the function is at BYTE address 0x0046 but:

Contents of section .data:
 800060 2300                                 #.

so what is actually loaded for the ICALL is the WORD address 0x0023 (which is 0x0046 / 2). Therefore I have a feeling:

fptr_t foo = (fptr_t) 0x001E;

above should have been:

fptr_t foo = (fptr_t) 0x001E / 2; // /2 for byte to word conversion

 

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

casanovg wrote:
Thank you clawson, for some reason, it ends on the bootloader again ...
He made a small mistake. ;-)

With that code you need to use 0x0f (word address) instead of 0x1e (byte address).

 

Edit: Too late :-)

Stefan Ernst

Last Edited: Fri. Jul 6, 2018 - 11:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah ha then my analysis in #7 was right. cheeky

 

(pesky word/byte conversions again !!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define foo ((void (*)(void))0x1E)

int main() {
    foo();
  38:	ee e1       	ldi	r30, 0x1E	; 30
  3a:	f0 e0       	ldi	r31, 0x00	; 0
  3c:	09 95       	icall
}

Thanks westfw, this code simply freezes the attiny ...

 

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

Actually now that I think of it WHY would you want to jump to 0x001E (or 0x000F) anyway? Why not simply:

fptr_t foo = (fptr_t) 0;

because 0x0000 has an "(R)JMP 0x001E" (or 0x000F if you prefer!) anyway.

 

Almost all bootloaders simply JMP/CALL 0 to get into the app.

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

clawson wrote:
Actually now that I think of it WHY would you want to jump to 0x001E (or 0x000F) anyway?
He is writing a bootloader for a device without a dedicated bootloader section and without the fuse to redirect the reset. So in a later step at address 0 will be a jump to the bootloader.

Stefan Ernst

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

I'm out of luck today ... I have to leave for my job now, I'm sure I'm doing something else wrong ... thanks!

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

casanovg wrote:
(Reply to #7)

I'm out of luck today ... I have to leave for my job now, I'm sure I'm doing something else wrong ... thanks!

Did you use Cliff's code from that post?

fptr_t foo = (fptr_t) 0x001E / 2;

There is a pair of parentheses missing. I have no idea what the compiler makes out of "function pointer divided by two". Does that even compile?

Stefan Ernst

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

casanovg wrote:
What I can not do is to jump from a bootloader C function to the start of the test program (0x1e).

From the code snippet, 0x1e is where the reset vector goes.  Doesn't every other bootloader in AVR-land construct a jump to zero?

 

If the code will really try to simulate a "cold" reset, I guess there isn't much harm in the CALL approach.  If, however, the stack pointer isn't reset then at best there is a waste of some SRAM; at worst some kind of cascading situation could really mess the stack up.

 

I thought GCC had a goto extension, accepting a hard address.

 

Labels as Values

You can get the address of a label defined in the current function (or a containing function) with the unary operator &&. The value has type void *. This value is a constant and can be used wherever a constant of that type is valid. For example:

     void *ptr;
     ...
     ptr = &&foo;
     

To use these values, you need to be able to jump to one. This is done with the computed goto statement1, goto *exp;. For example,

     goto *ptr;
     

Any expression of type void * is allowed.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Fri. Jul 6, 2018 - 12:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Doesn't every other bootloader in AVR-land construct a jump to zero?
sternst wrote:
So in a later step at address 0 will be a jump to the bootloader.

Stefan is right - this is all about Tiny's and the fact they have no BOOTSZ/BOOTRST so the bootloader "owns" the first opcode in the app section.

 

As for

theusch wrote:
If, however, the stack pointer isn't reset
But it is - he's jumping to the start of the CRT where almost the first action after R1=0; SREG=R1 is SP=RAMEND. So no problem making a CALL/ICALL to this.

Last Edited: Fri. Jul 6, 2018 - 12:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

void target(void);

-Wl,--defsym,target=0xdeadbeef

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

So in a later step at address 0 will be a jump to the bootloader.

Yes, that's what I'm doing ... 

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

There is a pair of parentheses missing. I have no idea what the compiler makes out of "function pointer divided by two". Does that even compile?

You're right, with parentheses in the division it compiles ok ...

fptr_t foo = (fptr_t)(0x001E / 2);

Otherwise, you get this ...

main.c:242:29: error: invalid operands to binary / (have 'void (*)(void)' and 'int')
 fptr_t foo = (fptr_t)0x001E / 2;  // 2 for byte to word conversion
                             ^
make: *** [Makefile:47: main.o] Error 1

Thank you!

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

above should have been: 

fptr_t foo = (fptr_t) 0x001E / 2; // /2 for byte to word conversion

Yes Sir, this works (adding parentheses) Thank you very much!

 

Now I have to learn what is this byte/word address issue, I'm using this to "watch" the hex file: 

avr-objdump -m avr -D file.hex
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Labels as Values

You can get the address of a label defined in the current function (or a containing function) with the unary operator &&.

Thank you, is good to know this, It could be very useful ... 

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

void target(void);

-Wl,--defsym,target=0xdeadbeef

This is the opposite to find out the address of a function in memory right?

This way you're forcing the function to be located at the "deadbeef" flash position?

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

casanovg wrote:

void target(void);

-Wl,--defsym,target=0xdeadbeef

This is the opposite to find out the address of a function in memory right?

This way you're forcing the function to be located at the "deadbeef" flash position?

Yes.  OP would likely want 0x0.

To be clear: This would not force the location of an actual C function.

Doing this to an actual C function would result in a multiple definition error.

Use sections for that.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

Is good to know it, thanks