rjmp offset calculation in C for bootloader VirtualReset

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

For picoboot I need to calculate the start of the application code from the address 0 rjmp instruction, and do the calculations to generate an rjmp to the start of the application from the virtual reset vector.

I know what I have to do, but I'm getting a headache trying to figure out the code to do it.

From avrdude's structures I know the size of the flash (always <= 8KB which is the addressing limit of rjmp). I know the size of the bootloader, and therefore the address of the virtual reset vector that comes just before the bootloader at the end of memory.

I save the original reset vector as follows:

    appstart = *((uint8_t *)m->buf) |
               *((uint8_t *)m->buf +1 ) << 8;

If I have a variable flashsize (bytes), a constant BOOTLOADER_SIZE (bytes), how do I create the opcode to go in the virtual reset vector? The rjmp opcode is 0xCXXX where XXX is a 12-bit signed value representing a forward or reverse word offset. I've started the calculation as follows:

Quote:
appstart = 0xc000 | (appstart & 0x0fff) ...

The way I code is I calculate/visualize things in my head, and then write the steps down in code. The rjmp address calculation is too complicated to do all in my head, which means I'm drawing a bit of a blank for the code.

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

First, why bother?
Why not a jump to 0?

Next, instruction adressing is done in words.
An RJMP can address 4K words.
The offset in an RJMP is from the next instruction.
0xC000 is a 2-cycle NOP.
waddr_target = (1 + waddr_rjmp + rjmp) & 0x0FFF
It works even if the flash space is less than 4K words.
In your case, waddr_rjmp wold seem to be 0.

Moderation in all things. -- ancient proverb

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

skeeve wrote:
First, why bother?
Why not a jump to 0?

That would create an infinite loop, since I redirect the reset vector to the bootloader. Here's more of the picoboot_paged_write avrdude function I'm writing:

  if ( addr == 0 ) {
    /* save and redirect reset vector */
    appstart = *((uint8_t *)m->buf) |
               *((uint8_t *)m->buf +1 ) << 8;
    *((uint8_t *)m->buf) = reset_vec_lo;
    *((uint8_t *)m->buf+1) = reset_vec_hi;

reset_vec_lo/reset_vec_hi contain the bytes for the rjmp to the start of the bootloader ( 0xde & 0xcf for a part with 8KB flash)

skeeve wrote:

The offset in an RJMP is from the next instruction.
0xC000 is a 2-cycle NOP.
waddr_target = (1 + waddr_rjmp + rjmp) & 0x0FFF
It works even if the flash space is less than 4K words.

That helps. Still trying to get all the numbers in my head but I think I can break it down into manageable chunks now.
The absolute word address of the application start could be:

appstart_addr = appstart & 0x0FFF +1

The address of the VirtualReset vector (where the new rjmp has to go) would be flashsize - BOOTLOADER_SIZE/2. Then with your waddr_target calculation I get the target offset, which I then and with 0xC000 to create the opcode.
Does this sound right?

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

Here's what I've come up with:

  if ( addr == 0 ) {
    /* save and redirect reset vector */
    appstart = *((uint8_t *)m->buf) |
               *((uint8_t *)m->buf +1 ) << 8;
    *((uint8_t *)m->buf) = reset_vec_lo;
    *((uint8_t *)m->buf+1) = reset_vec_hi;

    /* calculate new rjmp for appstart */
    vrst_vec_addr = m->size - BOOTLOADER_SIZE;
    appstart = 0xc000 |
               ( (vrst_vec_addr/2) + (appstart & 0x0FFF) );
    m->buf[vrst_vec_addr] = appstart & 0x00FF;
    m->buf[vrst_vec_addr+1] = appstart >> 8;
    m->tags[vrst_vec_addr] |= TAG_ALLOCATED;
    m->tags[vrst_vec_addr+1] |= TAG_ALLOCATED;
  }

This is the picoboot paged_write function that gets called from avr.c:avr_write in avrdude.

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

ralphd wrote:
skeeve wrote:
First, why bother?
Why not a jump to 0?

That would create an infinite loop, since I redirect the reset vector to the bootloader. ...
I thought AVRs had a fuse for that sort of thing?
Am I wrong? Are you allergic to it?
Quote:
skeeve wrote:

The offset in an RJMP is from the next instruction.
0xC000 is a 2-cycle NOP.
waddr_target = (1 + waddr_rjmp + rjmp) & 0x0FFF
It works even if the flash space is less than 4K words.

That helps. Still trying to get all the numbers in my head but I think I can break it down into manageable chunks now.
The absolute word address of the application start could be:

appstart_addr = appstart & 0x0FFF +1

If & has the higher priority, it will fail for one case.
You probably do not that case.
Quote:

The address of the VirtualReset vector (where the new rjmp has to go) would be flashsize - BOOTLOADER_SIZE/2. Then with your waddr_target calculation I get the target offset, which I then and with 0xC000 to create the opcode.
Does this sound right?
Possibly.
I'm still trying get my brain around why you need to do this.
I'm having trouble reading your code.
Too many casts
That said, I get the impression that you are working to hard to work with byte-size chunks.

In any case, relocating an RJMP is probably more easily done directly,
rather than though the target address intermediary.

rjmp2 = rjmp1 + waddr_rjmp1 - waddr_rjmp2;
rjmp2 += rjmp2 & 0x01000; // in case of borrow from opcode

Moderation in all things. -- ancient proverb

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

skeeve wrote:
ralphd wrote:

That would create an infinite loop, since I redirect the reset vector to the bootloader. ...
I thought AVRs had a fuse for that sort of thing?
Am I wrong? Are you allergic to it?

None of the ATtiny AVRs have bootloader support in hardware.

The address calculation works. Thanks for your help. I just finished testing it and got the first successful upload with my bootloader. It's called picoboot and you can find it on google code (I'd post a link but don't want to awaken the captcha beast).

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

I needed to add another "& 0x0FFF" to avoid overflowing the 12-bit address space which would make the opcode 0xd instead of 0xc:

    /* calculate new rjmp for appstart */
    appstart = 0xc000 |
               (((appstart & 0x0FFF) + BOOTLOADER_SIZE/2) & 0x0FFF);

I have no special talents.  I am only passionately curious. - Albert Einstein

 

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

You need a test to ensure the instruction at 0x0000 is an rjmp:

   if ( addr == 0 ) {
    /* save reset vector */
    appstart = *((uint8_t *)m->buf) |
               *((uint8_t *)m->buf +1 ) << 8;
    /* validate rjmp */
    if ((appstart & 0xC000) != 0xC000) {
      // emit error and abort
    }
    else {
      /* redirect reset vector */
      *((uint8_t *)m->buf) = reset_vec_lo;
      *((uint8_t *)m->buf+1) = reset_vec_hi;

      /* calculate new rjmp for appstart */
      vrst_vec_addr = m->size - BOOTLOADER_SIZE;
       /* calculate new rjmp for appstart */
      appstart = 0xc000 |
                 (((appstart & 0x0FFF) + BOOTLOADER_SIZE/2) & 0x0FFF); 
      m->buf[vrst_vec_addr] = appstart & 0x00FF;
      m->buf[vrst_vec_addr+1] = appstart >> 8;
      m->tags[vrst_vec_addr] |= TAG_ALLOCATED;
      m->tags[vrst_vec_addr+1] |= TAG_ALLOCATED;
    }

It would be best if this test were performed before any other flash pages were programmed, otherwise the previous app could be rendered non-functional before the error is discovered.

At a minimum, the behaviour should be documented so users of picoboot are aware of the limitation.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

joeymorin wrote:
You need a test to ensure the instruction at 0x0000 is an rjmp:

Although it's a rare case the code being loaded wouldn't have a reset vector, I'll probably check for it since it's just a few lines of code.

I have no special talents.  I am only passionately curious. - Albert Einstein