EmbeddedRelated.com
Forums

Programming flash from ram code

Started by tagrace_99 July 8, 2003
I have a situation where I need to erase and program flash from code 
placed into ram.

I can not use the pushed on stack code method shown in various app 
notes.

I have writen a routine that works perfectly if placed in flash but 
will not run out of ram.

I am testing the busy just like in the app note. I can not even get 
the flash to erase reliably (It did erase for me a few times)

Here is the initial code up to the erase:

        DINT                 ;DISABLE INTERUPTS
        CLR.B     &0         ;CLEAR INTERUPTS
        MOV       #5A80,&288 ;SHUT DOWN WATCHDOG
        MOV       #0,R13     ;SETUP LOOP COUNTER TO ERASE 10 SEGMENTS
ERSLP:  CMP       #10,R13
        JGE       WRTBYT     ;JUMP TO WRITE BYTE ROUTINE WHEN DONE

WAIT1:  BIT       #1,&12CH   ;TEST THE BUSY FLAG IN FCTL3
        JNZ       WAIT1      ;WAIT TILL NOT BUSY

        MOV       R13,R12    ;CALCULATE SEGMENT ADDRESS OFFSET
        ADD.B     R12,R12    ;BY MULTIPLYING 512 TIMES COUNTER
        SWPB      R12        ;THEN ADDING IT T0 THE BASE ADD LATER

        MOV       #0A500H,&12CH  ;REMOVE THE LOCK
        MOV       #0A502,&128H   ;SET ERASE
        CLR.B     #0D800H(R12)   ;DUMMY WRITE AT BASE + OFFSET

WAIT2:  BIT       #1,&12CH   ;TEST THE BUSY FLAG IN FCTL3
        JNZ       WAIT2

        MOV       #0A510H,&12CH  ;SET THE LOCK
        ADD       #1,R13     ;INCREMENT THE LOOP COUNTER
        JMP       ERSLP      ;GO BACK TO DO NEXT SEGMENT

WRTBYT: ; CODE NOW WRITES DATA TO FLASH IF I COULD GET THIS FAR. 

Like I said, this code works fine if run out of flash.

Is it a speed problem? I have tried setting up a number of timing 
options to no avail.

Does anyone know of a DCO clocked or a 32768 clocked senario that 
would work in the msp430f149?

You help in this would be greatly appreciated.

Ted Gregorius
TAG Systems



Beginning Microcontrollers with the MSP430

I haven't looked at your code in detail but the first question to ask
when
running code from RAM is how did the code get there? That might seem a peculiar
question, but generally to do this you would place the code to do the
erase/write in flash along with the rest of your code and copy it to RAM when
it becomes time to do some erasing/writing. Therein lies the (or at least a)
problem. If the code is linked with other code at a flash address it won't
work
when re-located by a simple copy operation, eg if you just copy it from some
flash address to some (other) RAM address unless it only uses relative
addressing. Depending on the processor type you use, an instruction such as JGE
uses relative addressing and therefore may be re-located from flash to RAM or
another flash segment without problems (JGE to an address is ok when that
address moves along with the re-located code). However, an instruction such as
JMP generally uses absolute addressing which will fail, since the absolute
address you're jumping to stays where it was linked, ie back in the flash
area
where you copied the code from and not in the new RAM area. In other words at
the first absolute JMP your RAM program jumps straight back into the flash area
you copied it from and it can't get back.

To avoid this problem only use relative addressing in re-locatable code or
modify your target jumps in absolute addressing (eg JMP ERSLP+OFFSET where
offset is the distance between flash address and RAM address). I'd post
some
code as an example but I haven't done this on the MSP430 yet; however I
seem to
recall some past posts addressed this issue. Relative addressing is easy to do,
however, particularly when you're working in assembler. Replace all
instructions with an absolute target address with instructions using a relative
address. A JMP can be replaced with a JGE if you fiddle with the flags to make
a GE condition prior to the JGE. Look at the assembler description of all the
instructions which modify the program counter to see whether they are relative
(ie ok )or absolute (need fixing).

Otherwise on the MSP430 RAM is just as good as flash for running code, there
are no penalties.

Regards, Hugh

>I have a situation where I need to erase and
program flash from code 
placed into ram.

I can not use the pushed on stack code method shown in various app 
notes.

I have writen a routine that works perfectly if placed in flash but 
will not run out of ram.

I am testing the busy just like in the app note. I can not even get 
the flash to erase reliably (It did erase for me a few times)

Here is the initial code up to the erase:

        DINT                 ;DISABLE INTERUPTS
        CLR.B     &0         ;CLEAR INTERUPTS
        MOV       #5A80,&288 ;SHUT DOWN WATCHDOG
        MOV       #0,R13     ;SETUP LOOP COUNTER TO ERASE 10 SEGMENTS
ERSLP:  CMP       #10,R13
        JGE       WRTBYT     ;JUMP TO WRITE BYTE ROUTINE WHEN DONE

WAIT1:  BIT       #1,&12CH   ;TEST THE BUSY FLAG IN FCTL3
        JNZ       WAIT1      ;WAIT TILL NOT BUSY

        MOV       R13,R12    ;CALCULATE SEGMENT ADDRESS OFFSET
        ADD.B     R12,R12    ;BY MULTIPLYING 512 TIMES COUNTER
        SWPB      R12        ;THEN ADDING IT T0 THE BASE ADD LATER

        MOV       #0A500H,&12CH  ;REMOVE THE LOCK
        MOV       #0A502,&128H   ;SET ERASE
        CLR.B     #0D800H(R12)   ;DUMMY WRITE AT BASE + OFFSET

WAIT2:  BIT       #1,&12CH   ;TEST THE BUSY FLAG IN FCTL3
        JNZ       WAIT2

        MOV       #0A510H,&12CH  ;SET THE LOCK
        ADD       #1,R13     ;INCREMENT THE LOOP COUNTER
        JMP       ERSLP      ;GO BACK TO DO NEXT SEGMENT

WRTBYT: ; CODE NOW WRITES DATA TO FLASH IF I COULD GET THIS FAR. 

Like I said, this code works fine if run out of flash.

Is it a speed problem? I have tried setting up a number of timing 
options to no avail.

Does anyone know of a DCO clocked or a 32768 clocked senario that 
would work in the msp430f149?

You help in this would be greatly appreciated.

Ted Gregorius
TAG Systems


__________________________________


I ran into these relocation issues when writing some code to allow for
in-field firmware updates. On the MSP430 all of the "jump" type
instructions
(JMP, JGE etc.) are relative, while BRANCH & CALL are absolute.
Unfortunately the MSP430 doesn't appear to have a relative equivalent to
CALL, so everything has to be put in one function if it's to be copied to
ram :-(

If it's of any help, here's some early code (with bugs included free
of
charge ;-). Be aware that there is no DCO stabilisation, error detection
etc. in this yet. But it did work in early tests.

I'm a relative newbie to C programming, so if anyone notices any glaring
evils please let me know!

Regards,
Kevin Brewster
Australia


void fieldinit(void)          // initialise field programming
{
 register unsigned int i;       // loop counter
 register unsigned char *fn_flash, *fn_ram; // function copying pointers
 register void (*fieldinit_in_ram)(void);  // function pointer

 BCSCTL1 = (BCSCTL1&~(RSEL2+RSEL1+RSEL0))+RSEL1;   // RSEL = 2
 DCOCTL  = (DCOCTL&~(DCO2+DCO1+DCO0))+DCO1+DCO0;   // DCO  =
3
 BCSCTL2 &= ~(SELS+DIVS_3);          // SCLK = DCO/1 ~280kHz
 FCTL2 = FWKEY + FSSEL_2;          // FCLK = SCLK/1

 UTCTL0 = SSEL0;         // UCLK = ACLK
 URCTL0 = 0x00 ;         // receieve all chars, not just address chars
 UCTL0  = CHAR;          // enable UART0, 8-bit chars
 UBR10  = 0x00;
 UBR00  = 0x0D;          // 32768/2400 = 13.65
 UMCTL0 = 0x6B;          // Modulation (compensate for 0.65)

 P3OUT &= ~LIN_SEL;        // put lin chip to sleep
 P3SEL &= ~LIN_TXD;        // P3.4 port function
 P3DIR |= LIN_TXD;         // P3.4 output direction
 P3OUT |= LIN_TXD;         // P3.4 high (signal normal slope mode)
 P3OUT |= LIN_SEL;         // select lin chip

 ME1 |= UTXE0 + URXE0;       // enable USART0 TX/RX modules
 IE1 &= ~(UTXIE0+URXIE0);      // disable USART0 TX/RX interrupts
 P3SEL |= 0x30;          // P3.4,5 = module function (USART0 TXD/RXD)

 i = (unsigned int) &fieldprog_sizer - (unsigned int) &fieldprog; //
length
of fieldprog() function
 fn_flash = (void *) &fieldprog; // address of fieldprog() function in flash
 fn_ram = (void *) 0x200;   // ram address to copy fieldprog() into

 for (;i>0;--i)
  *fn_ram++ = *fn_flash++;     // copy code from flash to ram

 fieldinit_in_ram = (void*) 0x200;   // point at new function location
 (*fieldinit_in_ram)();       // call the function in ram
}

// fieldprog() must be position independent to allow it to be copied to and
run from RAM
// To this end all function calls have been unrolled to form a single
function, since
// the MSP430 does not support pc-relative subroutine calls (like BSR in the
HC11).
// Also, all variables used must be of type register or auto (allocated on
stack frame).

// INTEL HEX RECORD FORMAT
// :10E000007B78300030380020007D7B78310030385C
// || |   | |                               |
// || |   | data                     checksum
// || |   record type (00a 01=eof)
// || start address
// |number of data bytes in this record
// intel hex record identifier

void fieldprog(void)
{
 auto char value[24] = {0,1,2,3,4,5,6,7,8,9, 0,0,0,0,0,0,0,
10,11,12,13,14,15,0};
 auto char inchar, *rxptr, rxbuf[100]; // maximum supported record length
including end-of-line characters
 auto char *address, data[50];
 auto char i, numdata, rectype, checksum;

 while(1) {

  while ((IFG1 & URXIFG0) == 0);     // wait for new character
  inchar=RXBUF0;           // get new char (clears flag)
  if (rxptr >= rxbuf + 99)       // prevent buffer overrun
   rxptr = rxbuf + 98;

  if (inchar == ':') {         // start of new ihex record
   rxptr = rxbuf;          // ..reset pointer
   continue;           // ..and get next character
  }

  if (inchar>='0' && inchar<='9' ||
inchar>='A' && inchar<='F') { // part of
ihex record
   *rxptr++ = inchar;               // ..add to buffer
   continue;                  // ..and get next character
  }

  if (inchar == 0x0d) {        // end of ihex record -> program it into
flash

   numdata = (value[rxbuf[0]-'0']<<4) +
value[rxbuf[1]-'0']; // number of
data bytes in this record :NN....
   address = (char*)((value[rxbuf[2]-'0']<<12) +
(value[rxbuf[3]-'0']<<8) +
(value[rxbuf[4]-'0']<<4) + value[rxbuf[5]-'0']);
   rectype = (value[rxbuf[6]-'0']<<4) +
value[rxbuf[7]-'0']; // record type
:nnaaaaRR....

   checksum = numdata;      // checksum calculation
   for (i=0; i<numdata+2+1+1; i++)  // (+2+1+1 =
+addr,rectyp,chksum)
    numdata += (value[rxbuf[8+2*i]-'0']<<4) +
value[rxbuf[9+2*i]-'0'];
   if (!checksum) {       // if bad checksum
    while ((IFG1 & UTXIFG0) == 0); //  wait transmit buffer empty
    TXBUF0 = 'X';       //  handshake 'X' = checksum error
    continue;        //  get next character (new record)
   }

   for (i=0; i<numdata; i++)      // copy ascii hex values into data[] as
binary
    data[i] = (value[rxbuf[8+2*i]-'0']<<4) +
value[rxbuf[9+2*i]-'0'];

   if (rectype == 0x00) {       // 0x00 = data record
    for (i=0; i<numdata; i++) {

     if (((unsigned int)address & 0x1ff) == 0) { // if start of new flash
segment -> erase this segment
      while (FCTL3 & BUSY);       //  wait for flash memory not busy (not
needed with this code in same flash module)
      FCTL1 = FWKEY + ERASE;       //  set erase bit
      FCTL3 = FWKEY;          //  clear lock bit
      *address = 0;          //  dummy write to erase flash segment
      FCTL1 = FWKEY;          //  clear erase bit
      FCTL3 = FWKEY + LOCK;       //  lock flash from accidental writes
     }

     while (FCTL3 & BUSY);        // wait for flash memory not busy (not
needed with this code in same flash module)
     FCTL1 = FWKEY + WRT;         // set WRT bit for write operation
     FCTL3 = FWKEY;           // clear lock bit
     *address++ = data[i];        // write data to address
     FCTL1 = FWKEY;           // clear WRT bit
     FCTL3 = FWKEY + LOCK;        // lock flash from accidental writes

    }
   }

   else if (rectype == 0x01)   // 0x01 = end of data record
    WDTCTL = ~WDTPW;     // kick watchdog with bad password -> reset

   while ((IFG1 & UTXIFG0) == 0); // wait transmit buffer empty
   TXBUF0 = '#';       // handshake '#' = packet programmed
okay
  }
 }
}
void fieldprog_sizer(void){} // address to obtain size of fieldprog()
function for teleport to ram




Thanks for the replys...

The code put in RAM is written in Assembler and is fully relocatable. 
In fact I can load it into any FLASH space without changes and it 
will run perfectly. It just won't run out of RAM.

How do I get the code into the RAM was asked.

This project has a USB port. The USB code has a 200 byte buffer which 
is used to transfer data back and forth to the outside world (a PC). 
The RAM code is around 140 bytes (this includes other functions 
besides the erase and program of flash). The RAM code is loaded into 
the BUFFER via a USB transfer (I have proven it is there intact and 
in it's entirity). A command is then sent to the unit via the USB 
which causes a BR to the start address of the BUFFER.

I basically see two different results (debug tools are limited on the 
live product).

1. The unit will reboot which I belive is being caused by a PUC. This 
is probably being generated by an ACCESS VIOLATION of the flash.

2. The unit is in an endless loop as if it was waiting for the BUSY 
to reset.

If I load the code into FLASH space, I can initiate the code using 
the USB BRANCH command so I know that is working. (I also use the USB 
branch command for other functions in this unit of which thousands 
have been shipped)

Here is somthing interesting...

If I don't wait for the BUSY to be clear, I will get at least the 
first segment of FLASH erased. The code crashes at some point but it 
must be a clue.

Yesterday I added a hook that allowed me to measure the MCLK 
frequency on Port5.4. The freq was 714kHz. In the FCTL2 I set the 
source as MCLK and divide it by 2 which should be in the correct 
ballpark for proper operation or is it? I've seen various freq specs 
for flash programming.


Today I plan on adding some hooks to look at the ACCVIFG and KEYV 
bits.

I can't sleep over this one. I'm thinking that there must be some 
flash activitiy still happening, like an interupt service, that is 
causing an access violation even though I have a DINT in the top of 
the code and I hold the Watch Dog.

Sure am willing to hear anyones ideas on this.

Thanks
Ted

--- In msp430@msp4..., "tagrace_99" <ted@t...> wrote:
> I have a situation where I need to erase and
program flash from 
code 
> placed into ram.
> 
> I can not use the pushed on stack code method shown in various app 
> notes.
> 
> I have writen a routine that works perfectly if placed in flash but 
> will not run out of ram.
> 
> I am testing the busy just like in the app note. I can not even get 
> the flash to erase reliably (It did erase for me a few times)
> 
> Here is the initial code up to the erase:
> 
>         DINT                 ;DISABLE INTERUPTS
>         CLR.B     &0         ;CLEAR INTERUPTS
>         MOV       #5A80,&288 ;SHUT DOWN WATCHDOG
>         MOV       #0,R13     ;SETUP LOOP COUNTER TO ERASE 10 
SEGMENTS
> ERSLP:  CMP       #10,R13
>         JGE       WRTBYT     ;JUMP TO WRITE BYTE ROUTINE WHEN DONE
> 
> WAIT1:  BIT       #1,&12CH   ;TEST THE BUSY FLAG IN FCTL3
>         JNZ       WAIT1      ;WAIT TILL NOT BUSY
> 
>         MOV       R13,R12    ;CALCULATE SEGMENT ADDRESS OFFSET
>         ADD.B     R12,R12    ;BY MULTIPLYING 512 TIMES COUNTER
>         SWPB      R12        ;THEN ADDING IT T0 THE BASE ADD LATER
> 
>         MOV       #0A500H,&12CH  ;REMOVE THE LOCK
>         MOV       #0A502,&128H   ;SET ERASE
>         CLR.B     #0D800H(R12)   ;DUMMY WRITE AT BASE + OFFSET
> 
> WAIT2:  BIT       #1,&12CH   ;TEST THE BUSY FLAG IN FCTL3
>         JNZ       WAIT2
> 
>         MOV       #0A510H,&12CH  ;SET THE LOCK
>         ADD       #1,R13     ;INCREMENT THE LOOP COUNTER
>         JMP       ERSLP      ;GO BACK TO DO NEXT SEGMENT
> 
> WRTBYT: ; CODE NOW WRITES DATA TO FLASH IF I COULD GET THIS FAR. 
> 
> Like I said, this code works fine if run out of flash.
> 
> Is it a speed problem? I have tried setting up a number of timing 
> options to no avail.
> 
> Does anyone know of a DCO clocked or a 32768 clocked senario that 
> would work in the msp430f149?
> 
> You help in this would be greatly appreciated.
> 
> Ted Gregorius
> TAG Systems


Hi kevin,
 
Thanks for the code snippet, this will help me on the way, i already had
written bootloader code for other devices but indead they are able of
handling jumps and calls etc, and this explaines why this code didn't
run on the msp, thanks.
 
One question though, 
I've compiled the code and downloaded it to the F149, I could see that
the code is been fetched and stored at $200, but am I correct if I say
that after download and call upon $200 you're not able to set
breakpoints anymore??
 
Further:
"If it's of any help, here's some early code (with bugs included
free of
charge ;-). Be aware that there is no DCO stabilisation, error detection
etc. in this yet. But it did work in early tests."
Which bugs?? Do you have a bug report?
 
 
thanks,
Martijn