EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

Setting absolute addresses on flash (ICC)

Started by Gabriel March 9, 2005
Hello everyone,

I'm using ICC430 and trying to compile a small program where I want
main() to stay on the first page of my MSP430F149, and all other
rotines and constants to start from the second page, so I can erase
the entire flash (page 2 to the end) and reprogram it (using a serial
link).
That would allow in-system reprogramming without using MSP
jtag/bootloader and also would never make the system unbootable.
How do I tell ICC where to place the code? I tried to use #pragma
abs_address:0x____, but the compiler still placing constants on the
first page.

Gabriel




Beginning Microcontrollers with the MSP430

--- In msp430@msp4..., "Gabriel" <gcalin@u...> wrote:
> I'm using ICC430 and trying to compile a
small program where I want
> main() to stay on the first page of my MSP430F149, and all other
> rotines and constants to start from the second page, so I can erase
> the entire flash (page 2 to the end) and reprogram it (using a serial
> link).
> That would allow in-system reprogramming without using MSP
> jtag/bootloader and also would never make the system unbootable.
> How do I tell ICC where to place the code? I tried to use #pragma
> abs_address:0x____, but the compiler still placing constants on the
> first page.

My approach was to build a separate image for the "loader" code.  You
can use the project options or the linker command line option -blit to
set the range of addresses each image uses.  I put my loader up at
FE00, with the interrupt vectors.  (Interrupts present an ineresting
challenge.)

I'd be interested to hear Al's thoughts on field updates of code.

Regards,

William





Hi William. I've written on this subject a few times. All but one of
my 
systems is field upgradable (and that will be after the current 
re-write). There was a very long discussion some time ago. On that 
occasion I wrote a fairly long  response on the methods that I employed. 
This included dealing with midstream failures, power downs, vector 
interrupt updates etc. The method you suggest, whereby you assume that 
vector interrupts are seldom changed, thus you share the update code 
with the vector table is one method. however it is very limited. I find 
that the thing I might want to change most is ISR's. Of course if you 
are really disciplined you can fix the address of each ISR, and leave 
gaps between them. But this still falls over when you might realise that 
one ISR needs to be larger than the space allowed. Then things get messy 
with subroutine calls from ISR's or a branch from the entry point.

As a very brief synopsis of how I handle it. I typically reserve one of 
the smaller data flash segments for tracking my progress, and a full 
segment for my downloader. more if necessary. In the 128 byte data 
segment I allocate 1 bit per segment of memory to be programmed, 
including the downloader for completeness. My RESET vector always points 
to the start of the downloader code, which, after turning off the WDT 
and setting SP, adds a variable called MODE, stored in the dataflash, to 
PC. This vectors the program to the relevant program entry point 
depending upon the programmed status of the device. 0 takes me to the 
normal program entry point, 4 takes me to the start a new upload point 
and 2 takes me to the continue update point. The difference between the 
latter two is simply that in the case of an update the software finds 
the last succesfully updated segemnt from the dataflash tracking table. 
Here a 0 bit means succesfully programmed and verified. The bit is 
written AFTER all the tests of the most recently programmed segment. The 
segments containing the uploader are set to 0 after skipping the program 
step.. The very last segment to be written ios the vector tables. This 
segment is erased and the RESET vector is immediately reprogrammed 
first. Thus the only risk point is the time between segment erasure and 
RESET programming completion. The remaining vectors are then programmed 
before completing programming of the remainder of that segment.

If I am using a UART or other peripheral to update the system (rather 
than bit banging). The ISR's needed are included in the uploader 
segment, and programmed immediately after RESET vector.

A slightly riskier version of this moves the uploader into RAM first, 
and reprograms the uploader segments first, using the same techniques.

I think that, provided you have enough space to cater for it, a field 
upgrade system like this should be included in every design. It can be 
made ass ecure as anything micro based can be, with a little care, and 
adds convenience for your customers. There is nothing to stop you from 
including encryption in your uploader, allowing your clients to download 
updates over the internet and upate their own systems.

Al

William J. Watson wrote:

>--- In msp430@msp4..., "Gabriel"
<gcalin@u...> wrote:
>  
>
>>I'm using ICC430 and trying to compile a small program where I want
>>main() to stay on the first page of my MSP430F149, and all other
>>rotines and constants to start from the second page, so I can erase
>>the entire flash (page 2 to the end) and reprogram it (using a serial
>>link).
>>That would allow in-system reprogramming without using MSP
>>jtag/bootloader and also would never make the system unbootable.
>>How do I tell ICC where to place the code? I tried to use #pragma
>>abs_address:0x____, but the compiler still placing constants on the
>>first page.
>>    
>>
>
>My approach was to build a separate image for the "loader" code. 
You
>can use the project options or the linker command line option -blit to
>set the range of addresses each image uses.  I put my loader up at
>FE00, with the interrupt vectors.  (Interrupts present an ineresting
>challenge.)
>
>I'd be interested to hear Al's thoughts on field updates of code.
>
>Regards,
>
>William
>
>
>
>
>
>
>.
>
> 
>Yahoo! Groups Links
>
>
>
> 
>
>
>
>
>  
>


Al-
Could you expand on this a bit? Especially the branch part?
It sounds simple and easy, so I must be overlooking something.

Jack

On Mar 9, 2005, at 7:16 PM, Onestone wrote:

> things get messy 
> with subroutine calls from ISR's or a branch from the entry point.

Jack




Jack wrote:

>Al-
>Could you expand on this a bit? Especially the branch part?
>It sounds simple and easy, so I must be overlooking something.
>
I've explained the issue regarding branching below, hopefully in 
adequate detail. The rest is fairly simple, this assumes (being lazy) 
that the uploader is in the first two main flash segments, so I am just 
linearly updating:-

   1. Receive instruction to upgrade.
   2. Confirm this is valid.
   3. Erase tracking block
   4. clear bit 1 of MODE   (MODE = NOT.MODE -2)
   5. Signal ready to host
   6. Receive next block
   7. If Block type (first 2 bytes)  = void GOTO 5
   8. confirm CRC16 of block
   9. if correct GOTO 12
  10. send repeat block to host
  11. GOTO 6
  12. Clear bit 2 in MODE to signal updating
  13. erase current segment
  14. if segment = VECTORS program in vector sequence then GOTO14
  15. program segment (I still prefer a word at a time)
  16. verify segment
  17. if OK CLEAR BIT IN TRACKING SEGMENT
  18. GOTO 5
  19. if third attempt GOTO 20
  20. GOTO 12
  21. if NOT complete GOTO 5
  22. END
  23. ABORT SESION

Obviously there needs to be a little more flesh to this, for example I 
usually move the erased segment to RAM before erasure, then have the 
option to reprogram it if I get persistent failures. The 'block type' 
word at the start of a data packet can be used to indicate if this 
segment is able to stand alone. This outlines the general principals of 
the update process. the only thing left is the start up:-

                         ORG      UPDATESEG0

RESET:
       MOV      #STACKTOP,SP
       MOV      #PUPPYCIDE,&WDT
       MOV      &MODE,R4
       INV         R4
       DECD      R4
       AND      #0x0006,R4                     ;RESTRICT RANGE OF VALUES
        ADD      R4,PC
       JMP         RUNMODE
       JMP         UPDATE
RECOVER:                                            ;scan dataflash to 
get address of next block to program
        ...
        ...
        ...      
UPDATE:                                              ;DO THE FLASH 
UPDATE PROCESS
         ...
       ...
       ...

             ORG         USERSEG0

RUNMODE:
       CALL      #INIT
       ETC
       ETC

>
>Jack
>
>On Mar 9, 2005, at 7:16 PM, Onestone wrote:
>
>  
>
>>things get messy 
>>with subroutine calls from ISR's or a branch from the entry point.
>>
The Branch issue goeas like this:-

My old isr setup for TimerA  looks like this:-

;TA0 PRODUCES A HEART BEAT

TA0_ISR:
       ADD      #TICK,&CCRA0
       BIC      #CCIFG,&CCTLA0
        RETI

TA_ISR:
       ADD   &TAIV,PC
       RETI                           ;CAN'T BE 0
       JMP      TA1_ISR
       JMP      TA2_ISR
       RETI                           ;DUMMY IN CASE OF TIMERA_5 STYLE
       RETI
       JMP      TA_OVF      ;TIMERA OVERFLOW ISR
       RETI                           ;JUST IN CASE

TA1_ISR:
       XOR      #BUZZER,&P3OUT      ;DO ANYTHING
       BIC         #CCIFG,&CCTLA1
       RETI

TA2_ISR:
       BIC      #CCIE+CCIFG,&CCTLA2      ;UNUSED INT IS HANDLED
       RETI

Now lets assume that instead of a simple pin toggle for a buzzer your 
TimerA_1 is now to be used to sequence an external positional servo 
motor controller. Which requires a sequenced pulse train to instruct it 
to move and how far. Physically the ISR requires more space than the 3 
words it currently uses. If you have not allowed for movable ISR vectors 
you must now somehow handle the ISR. Luckily I have used 3 words for 
ISR1, so I could use a call and RETI,  as follows:-

TAISR:
       ...
        ...
       JMP      TA_OVF      ;TIMERA OVERFLOW ISR
       RETI                           ;JUST IN CASE

TA1_ISR:
;       XOR      #BUZZER,&P3OUT      ;DO ANYTHING
;       BIC         #CCIFG,&CCTLA1
          CALL   #NEWTA1_ISR
       RETI

TA2_ISR:
       BIC      #CCIE+CCIFG,&CCTLA2      ;UNUSED INT IS HANDLED
       RETI

However, if it was TA2 that I wanted to coopt for a new task I would be 
stuck using the above method. Although I've (correctly) handled the 
unused ISR, it only uses 2 words of memory, not enough for a call and 
return, and using a JMP limits the range I can jump. Typically code 
would have been packed in the original, so that any space is likely to 
be out of range of a jmp. In this case use the BRanch instruction:-

TA1_ISR:
;       XOR      #BUZZER,&P3OUT      ;DO ANYTHING
;       BIC         #CCIFG,&CCTLA1
          CALL   #NEWTA1_ISR
       RETI

TA2_ISR:
;       BIC      #CCIE+CCIFG,&CCTLA2      ;UNUSED INT IS HANDLED
;       RETI
       BR         NEWTA1_ISR

TA_OVF:
       INC      &OVERFLOW
       RETI


Thanks! Very instructive.
For one thing, I hadn't considered "out of range of a jmp"
likelyhood.


--- In msp430@msp4..., Onestone <onestone@b...> wrote:

> However, if it was TA2 that I wanted to coopt for
a new task I would be 
> stuck using the above method. Although I've (correctly) handled the 
> unused ISR, it only uses 2 words of memory, not enough for a call and 
> return, and using a JMP limits the range I can jump. Typically code 
> would have been packed in the original, so that any space is likely to 
> be out of range of a jmp. In this case use the BRanch instruction:-






The 2024 Embedded Online Conference