The TI E2E™ design support forums will undergo maintenance from Sept. 28 to Oct. 2. If you need design support during this time, contact your TI representative or open a new support request with our customer support center.

This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/MSP430FR58471: Methods for executing functions from RAM with MSP430-GCC

Part Number: MSP430FR58471

Tool/software: TI C/C++ Compiler

Hi,

I'm attempting to implement a workaround that calls for executing a function from RAM (specifically PMM32 from the msp430fr58471 errata) and am having a little bit of trouble. I've searched across this forum and see plenty of examples for how to do this on IAR and CCS but I'm using MSP430-GCC which appears to not support the methods I've seen so far. 

For reference, the contents of the function I'm attempting to call from RAM:

FRCTL0 = FRCTLPW;
GCCTL0 &= ~(FRPWR|FRLPMPWR);
__bis_SR_register(LPM3_bits | GIE);

Things that I've tried:

  1. Using __attribute((section(".data"))) which should store my code in flash but execute it from RAM. 
  2. Creating my own section in the linker file using a 32 byte range of RAM that I declared solely for this purpose with the "> RAM AT>FLASH" syntax and again using the section attribute.
  3. Using in-line assembly to jump to a const char array that contains the compiled binary data equivalent of the above commands.

All of these methods produce inconsistent results. Any help would be appreciated.

  • Hi Robert,

    Please allow me to take some time look into this. I'll get back to you ASAP.
  • Hi Robert,

    Really sorry for this late reply. I consulted internally and found that there's no better method to do with this as you are using GCC. In IAR/CCS, with TI compiler, the tools handle this automatically with the predefined attribute, but MSP430 GCC can't do it well.

    The suggested way for you is to copy these code manually to RAM firstly when executing, and then set your PC to the RAM location to execute the RAM code.
    Or, you may want to try to use CCS as it's a free licensed tool and supports almost all TI embedded processor products.

    Let me know if you got update.

    Regards,
    Harry
  • Hello Harry,

    Thanks for looking into this for me. Fortunately I've learned a number of things since this post and was able to successfully implement a function running from RAM. When I get a little more spare time I'll come back and explain in detail what I've learned in the hope that others will benefit from my struggle.

    For now suffice to say I was able to implement the function via

    /* lpm_from_ram - Disable FRAM from RAM before going into LPMx
     *
     * Workaround described by errata PMM32 in order to prevent unintended
     * code execution. This function must be placed in section ".data" in
     * order to be stored in flash but executed from RAM.
     */
    void __attribute((section(".data"))) lpm_from_ram(unsigned short sr_reg_bits) {
      FRCTL0 = FRCTLPW + NWAITS_1;        // Unlock fram registers
      GCCTL0 &= ~(FRPWR|FRLPMPWR);        // Power down the fram controller
      __bis_SR_register(sr_reg_bits);     // Enter SR register bits to set
    }

    I had stumbled into some traps of my own making while attempting this originally which caused me to post the question.

  • That is some very simple code so what you want to do can be done entirely in assembly language. Inline if you have to.

    The basic flow is to allocate space on the stack (which will be in RAM) for your code, copy it, call it as a subroutine, when it returns, clean up the stack and exit. Something like:

    	;; Note that registers r12-r15 can be used at will without saving
    	;; their previous contents.
    #include <msp430.h>
    
    	.equ	length,ramend-ramstart
    	.text
    	.func ramexe
    ramexe:
    	sub	#length,r1		; reserve stack space
    	mov	r1,r13			; setup to copy
    	mov	#ramstart,r14
    	mov	#length,r15
    loop:	mov	@r14+,@r13	; copy code
    	add	#2,r13
    	sub	#2,r15
    	jnz	loop
    	call	r1		; call function on stack
    	add	#length,r1 ; restore stack
    	ret
    	
    ramstart:	
    	mov	#FRCTLPW,&FRCTL0
    	and	#~(FRPWR|FRLPMPWR),&GCCTL0
    	bis	#(LPM3_bits|GIE),r2
    	ret
    ramend:	
    	.endfunc
    	.end
    

  • slaa103.pdf (Flash self programming technique) describes this method in more details. It seems to have vanished from the TI web site but can still be found.

  • Here's a quick breakdown of why my initial attempts at this failed. Hopefully this helps someone else. I'll go through the list of things I tried in a chronological order.

    • Creating my own section in the linker file using a 32 byte range of RAM that I declared solely for this purpose with the "> RAM AT>FLASH" syntax and again using the section attribute.

    This failed because even though I had my linker file entry defined correctly, I did not realize that by default the ".data" section is the only one being recognized by the C startup code as needing to be copied over. I verified this by closely examining the assembly output of the msp430 gcc. This was either due to some hard coded default (doubtful) or because I was unaware of some additional step I needed to do to trigger the copy from flash to RAM. 

    • Using __attribute((section(".data"))) which should store my code in flash but execute it from RAM. 

    This step should have worked, but I ran into two problems. The first was that I had failed to remove the special RAM section I defined in the linker file from the previous step and some special code I was running that resets my stack pointer was assigning it to a location in that section I failed to remove. The other reason was due to blindly using the FRCTL0 password assignment line from the device errata workaround description. I failed to realize that this was causing me access time faults because my fram controller wait state settings were getting removed and my clock speed was too high for having no wait states. It may be interesting to some that having the msp device at a higher temperature made these access time faults happen much quicker.

    • Using in-line assembly to jump to (and return from) a const char array that contains the compiled binary data equivalent of the above commands.

    Plenty of pitfalls on this one:

    1. I shouldn't have been using a const char array. This apparently assigns the array to flash instead of RAM. 
    2. I didn't account for variable length assembly instructions so the PC value I was storing for later was jumping to the wrong instruction.
    3. I did not ensure that some of the registers I was using were not already in use by other code at that time.

    I did eventually get this method to work before moving back to my simpler C only code posted above. I'll include the relevant code in case anyone is curious.

    /* Ram function existing as an array. It is the binary equivalent of the following instructions:
     *
     * FRCTL0 = FRCTLPW + NWAITS_1;
     * GCCTL0 &= ~(FRPWR|FRLPMPWR);
     * __bis_SR_register(LPM3_bits | GIE);
     * asm("bra r6");
     */
    char lpm_from_ram_binary[20] = {
      0xb2, 0x40, 0x10, 0xa5, 0x40, 0x01, 0xb2, 0xf0, 0xf9, 0xff,
      0x44, 0x01, 0x32, 0xd0, 0xd8, 0x00, 0x03, 0x43, 0xc0, 0x06
    };
    
    ...
    main() {
     ...
     lpm_from_ram();
     ...
    }
    
    /* PMM32 errata workaround; call lpm mode after disabling fram controller while excecuting in RAM.
     * BE VERY CAREFUL ABOUT WHICH REGISTERS YOU USE HERE! Overwriting ones currently in use has unpredictable results.
     * This function jumps to r14 after being set with the address of the array containing our code.
     * It sets up r6 as the return address to be used by the last byte code branch instruction.
     * A register to register add was used in order to keep the byte offset distance constant (beware variable asm instruction lengths)
     */
    void lpm_from_ram(void) {
      asm("mov #lpm_from_ram_binary, r14");
      asm("mov #4, r7");
      asm("mov pc, r6");
      asm("add r7, r6");
      asm("bra r14");
    }

      

      

    Once I had realized my initial mistakes just using the section attribute for ".data" made the most sense and was the least complex. It was a fun learning process nonetheless.

  • Robert,

    Thanks a lot for sharing this!

    Regards,
    Harry

**Attention** This is a public forum