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.

MSP430F5437A - Running in RAM

Other Parts Discussed in Thread: MSP430F5437A

Hi there,

There any many forum posts on the topic of running functions from RAM rather than FLASH - however I was unable to convert this wealth of information into a solution. 

I am using the MSP430F5437A and CCS4.0. My approach was to use the pragma and modified linker file.  

#pragma CODE_SECTION(xxx,".FLASHCODE")

and a modified linker file 

.FLASHCODE : load = FLASH_MEM, run = RAM_MEM 

...

RAM_MEM : origin = 0x1C00, length = 0x0200
RAM : origin = 0x1F00, length = 0x3000

FLASH_MEM :origin = 0x5C00, length = 0x200
FLASH : origin = 0x5F00, length = 0xA000

When I debug the code line-by-line I can see the code correctly jump to the function in RAM. However, it immediately jumps to FLASH and crashes (to clarify the next instruction is not a jump to flash). 

I have attempted to fix this by placing multiple NOPs prior to the ram function call and to remove compiler optimizations.

The code is from the MSP430F5x sample codes - "MSP430F543xA Demo - Single-Byte Flash In-System Programming, Copy SegC to SegD) with the modifications outlined above. 

Any thoughts or suggestions? 

  • James,

    do you copy the code portion manually from flash to RAM? You need to do this. I remember we have a similiar example code for MSP430F54xx (non-A) version. You can download it from here: http://www.ti.com/litv/zip/slac166s.

    After extracting the ZIP file, you can find a ZIP file called: MSP430F541x_MSP430F543x_Code_Examples\C\msp430x54x_flashwrite_04.zip.

    You shall use this example as reference to what you are trying to do.

  • Hi Leo Hendrawan - thanks for all your support on e2e, you have same excellent posts. Indeed - I had forgotten to copy the function. I had seen your advice to use this example - but didn't managed to get it to work in the past. This time - it all seems fine.

    Just to confirm - is it not possible to step through code which is executed in RAM? The code just jumped from the write_block_init() to while(1) but the flash location 0x10000 is fully updated. 

    I am actually interested in moving all of my application to RAM - its pretty small but does use interrupts: it just uses the DMA to move adc data to RAM, and then once the buffer is full, copies to external flash. I noticed the 5xx Workaround to disable interrupts in the RAM function. Is it okay to use interrupts if I move the interrupt table as mentioned in http://processors.wiki.ti.com/index.php/MSP430_FAQ#Is_there_a_way_to_re-allocate_the_interrupt_vector_on_MSP430.3F? Does this example simply copy the entire vector table or only the timer in this example? 

    Best wishes

  • James,

    thanks for the kind words.

    James Hillman said:

    Just to confirm - is it not possible to step through code which is executed in RAM? The code just jumped from the write_block_init() to while(1) but the flash location 0x10000 is fully updated. 

    I tested the code long time ago, and i remember it wasn't easy also to do step-by-step debugging. So, i think this could be one limitation from the IDE/tools.


    James Hillman said:

    I am actually interested in moving all of my application to RAM - its pretty small but does use interrupts: it just uses the DMA to move adc data to RAM, and then once the buffer is full, copies to external flash. I noticed the 5xx Workaround to disable interrupts in the RAM function. Is it okay to use interrupts if I move the interrupt table as mentioned in http://processors.wiki.ti.com/index.php/MSP430_FAQ#Is_there_a_way_to_re-allocate_the_interrupt_vector_on_MSP430.3F? Does this example simply copy the entire vector table or only the timer in this example? 

    Well, basically i wrote and tested the code in the wiki. It seems to work fine, as the timer interrupt can be served by the ISR. Looking into the code again (i already forget it actually :-) ), it seems what i did is to define the interrupt vector table defined as a global variable in RAM. Thus this variable will be initialized by the startup code prior the code running the C main() function.

  • Hi Leo,

    Thanks for your comments. Could I have your advice on how I wish to implement this - my aim is to get the lowest power code possible - I assume I can't move the whole of main to RAM?

    I modified your code from the wiki so the ISR wakes up the main code. My intention then is to place the processing which main normally runs in FLASH in an additional RAM function. How does this sound?

    I just did a quick test - but it seemed to go into the weeds - maybe I had overlapped the RAM ISR vector and the copy of flash... as the debugger fails with RAM related stuff its hard to debug. 

    Thanks, James 

  • James,

    sorry for the late answer, I was away for sometime.

    Anyway, basically it is true that the device will consume less power if you execute the code from RAM. But the big question is whether it is worth to move all code to RAM, since you will end up buying the device with bigger RAM while you are not using the flash for it (but you still need to pay for it :-) ).

    Anyway, i tried to change the code to wake the CPU from ISR residing in RAM, and it still works ok for me:

    /******************************************************************************
    * Copyright (c) 2012, Leo Hendrawan
    * All rights reserved.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions are met:
    *    * Redistributions of source code must retain the above copyright
    *      notice, this list of conditions and the following disclaimer.
    *    * Redistributions in binary form must reproduce the above copyright
    *      notice, this list of conditions and the following disclaimer in the
    *      documentation and/or other materials provided with the distribution.
    *
    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
    * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE MSS PROJECT OR ITS
    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
    * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
    * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
    * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
    * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    ******************************************************************************/
    
    /**************************************************************************//**
    *
    * @file     ram_int_vect_ex.c
    *
    * @brief    example code for moving and using the interrupt vector table to
    *           RAM on MSP430F5438A for Code Composer Studio (CCS) IDE v5.x
    *
    * @author   Leo Hendrawan
    *
    * @remark   this example code has been tested with CCS v5.2.1 and Code
    *           Generation Tools v4.1. It shall be compiled with small code
    *           memory model compile option (--code_model==small)
    *
    ******************************************************************************/
    
    #include "msp430f5438A.h"
    
    
    // comment/uncomment the following COPY_AT_RUN_TIME macro
    // to try different methods assigning the interrupt vector
    // table into RAM
    // - if COPY_AT_RUN_TIME is defined, the program needs to copy
    //   the address of the ISR explicitly during run-time
    // - if COPY_AT_RUN_TIME is not defined, the interrupt vector table
    //   is defined as constant variable and will be initialized by the
    //   C startup function before main()
    #define COPY_AT_RUN_TIME
    
    // sanity check - TI compiler
    #ifndef __TI_COMPILER_VERSION__
    #error "This example code only works with TI compiler with small code model!"
    #endif
    
    #ifndef __MSP430F5438A__
    #error "This example code only works for MSP430F5438A"
    #endif
    
    // last address of RAM memory
    #define TOP_OF_RAM           (0x5BFF)
    
    // start address of interrupt vector rable in RAM
    #define INT_VECT_START_ADDR  (TOP_OF_RAM + 1 - sizeof(int_vect_t))
    
    // declaration of TA1 CCR0 ISR
    __interrupt void TIMER1_A0_ISR(void);
    
    // ISR function pointer variable type
    typedef void (*isr_type_t)(void);
    
    // interrupt vector table data type
    typedef struct {
      isr_type_t reserved[41];
      isr_type_t rtc_a;
      isr_type_t io_p2;
      isr_type_t usci_b3_rx_tx;
      isr_type_t usci_a3_rx_tx;
      isr_type_t usci_b1_rx_tx;
      isr_type_t usci_a1_rx_tx;
      isr_type_t io_p1;
      isr_type_t ta1_ta1iv;
      isr_type_t ta1_ccr0;
      isr_type_t dma;
      isr_type_t usci_b2_rx_tx;
      isr_type_t usci_a2_rx_tx;
      isr_type_t ta0_ta0iv;
      isr_type_t ta0_ccr0;
      isr_type_t adc12_a;
      isr_type_t usci_b0_rx_tx;
      isr_type_t usci_a0_rx_tx;
      isr_type_t wdt;
      isr_type_t tb_tbiv;
      isr_type_t tb_ccr0;
      isr_type_t unmi;
      isr_type_t snmi;
      isr_type_t reset;
    }int_vect_t;
    
    #ifdef COPY_AT_RUN_TIME
    // pointer to the start address of the interrupt vector table in RAM
    int_vect_t * const ram_int_vect_p = (int_vect_t*) INT_VECT_START_ADDR;
    
    #else /* COPY_AT_RUN_TIME */
    // empty ISR
    #define EMPTY_ISR   ((isr_type_t) 0x0000)
    
    // interrupt vector table defined as variable
    #pragma RETAIN(ram_int_vect)
    #pragma location=INT_VECT_START_ADDR
    int_vect_t const ram_int_vect = {
      {
        EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR,
        EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR,
        EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR,
        EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR,
        EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR, EMPTY_ISR,
        EMPTY_ISR
      }, // reserved[41]
      EMPTY_ISR, // rtc_a;
      EMPTY_ISR, // io_p2;
      EMPTY_ISR, // usci_b3_rx_tx;
      EMPTY_ISR, // usci_a3_rx_tx;
      EMPTY_ISR, // usci_b1_rx_tx;
      EMPTY_ISR, // usci_a1_rx_tx;
      EMPTY_ISR, // io_p1;
      EMPTY_ISR, // ta1_ta1iv;
      (isr_type_t) &TIMER1_A0_ISR, // ta1_ccr0;
      EMPTY_ISR, // dma;
      EMPTY_ISR, // usci_b2_rx_tx;
      EMPTY_ISR, // usci_a2_rx_tx;
      EMPTY_ISR, // ta0_ta0iv;
      EMPTY_ISR, // ta0_ccr0;
      EMPTY_ISR, // adc12_a;
      EMPTY_ISR, // usci_b0_rx_tx;
      EMPTY_ISR, // usci_a0_rx_tx;
      EMPTY_ISR, // wdt;
      EMPTY_ISR, // tb_tbiv;
      EMPTY_ISR, // tb_ccr0;
      EMPTY_ISR, // unmi;
      EMPTY_ISR, // snmi;
      EMPTY_ISR, // reset;
    };
    #endif /* COPY_AT_RUN_TIME */
    
    // main function
    void main(void)
    {
      // set P1.0 as output
      P1DIR |= BIT0;
    
      // setup TA1 to generate interrupt every ~50 ms
      TA1CCTL0 = CCIE;
      TA1CCR0 = 50000;
      TA1CTL = TASSEL__SMCLK + MC__UP + TACLR;
    
    #ifdef COPY_AT_RUN_TIME
      // copy ISR address in the interrupt vector table
      ram_int_vect_p->ta1_ccr0 = (isr_type_t) &TIMER1_A0_ISR;
    #endif
      // move interrupt vector table to RAM
      SYSCTL |= SYSRIVECT;
    
      while(1)
      {
        // Enter LPM0, enable interrupts
        __bis_SR_register(LPM0_bits + GIE);
    
        // nops
        __no_operation();
        __no_operation();
    
        // Toggle P1.0
        P1OUT ^= BIT0;
      }
    }
    
    // Timer A0 interrupt service routine
    #pragma CODE_SECTION(TIMER1_A0_ISR, ".text:_isr")
    __interrupt void TIMER1_A0_ISR(void)
    {
      // exit LPM
      __bic_SR_register_on_exit(LPM0_bits);
    }
    
    // C startup pre init function
    int _system_pre_init(void)
    {
      // Stop WDT
      WDTCTL = WDTPW + WDTHOLD;
    
      // initialize variables
      return 1;
    }
    

    Problem in debugging can be caused by EEM bugs. For example i remember that i had headache debugging 5xx/6xx devices with LPM, and it turns out it is due to EEM13 bug.

  • James Hillman said:
    Just to confirm - is it not possible to step through code which is executed in RAM? The code just jumped from the write_block_init() to while(1) but the flash location 0x10000 is fully updated. 

    Single-stepping is possible whether the CPU executes from ram or flash.
    However, you must not step through code that is writing to flash. While a flash write is active, the debugger access to the CPU will not only retrieve wrong memory information from any flash area, it may also interfere with the flash write with unpredictable results.
    Also, during a flash write, interrupts must be disabled.

    Flash write code is the main reason for executiong code in ram, because the much faster block write mode cannot be used if the code runs from flash.

  • Hi Leo and Jens,

    Thank you very much for your comments. I have had great success with running large parts of my code in RAM. I just want to revisit this topic of 'stepping through' RAM based code. Indeed, sometimes when I press pause it gives me a memory location in RAM for the opcode - which is great. However, sometimes it seems to be running in Flash. For example, if I put a break point in an interrupt handler it displays a memory location in Flash. Can I assume, that if SYSRIVECT is set, and the code works as expected, then the interrupt handler is indeed running in RAM?

    Thanks,

    James 

  • James Hillman said:
    Can I assume, that if SYSRIVECT is set, and the code works as expected, then the interrupt handler is indeed running in RAM?

    SYSRIVECT won't put your handler into ram. It will tell the processor to fetch the handler address from the end of ram instead of the end of flash (of course, the address of the program start point is taken from 0xffff in any case, as after a reset, SYSRIVECT is reset too)

    James Hillman said:
    if I put a break point in an interrupt handler it displays a memory location in Flash.

    If you put a breakpoint on an interrupt handler and the system breaks in flash, then the interrupt handler is obviously located and executed in flash. The linker-generated MAP file will tell you the location of all linked resources in your program.

  • Hi Jens,

    I hope you are well - thank you for your time. I understand your points. You have seen the posts with Leo (TI) discussing the RAM variable method to load the interrupts into RAM.

    Below is the .map. (part of) I can see that my dedicated sections FLASH_MEM and RAM_MEM are the same size so the copy to RAM has been done - I look through the memory with the debugger to confirm. The bit below shows that the FLSAHCODE is run from RAM. 

    I'm still not clear why the debugger doesn't always show the RAM address - but I'll take confidence from everything else that its working correctly. 

    *
    .FLASHCODE 
    *          0    00005c00    00000210     RUN ADDR = 00001c00
                      00005c00    0000013a     main.obj (.FLASHCODE:main)
                      00005d3a    00000062     main.obj (.FLASHCODE:UartInit)
                      00005d9c    0000004a     parallel_flash.obj (.FLASHCODE:setup_parallel_flash)
                      00005de6    0000001e     main.obj (.FLASHCODE:UartSendString)
                      00005e04    0000000c     main.obj (.FLASHCODE:UartSendChar)
    
    
     
  • The map file shows that the main code (main, init, send/receive) runs in ram (RUN-ADDR=0x1c00). But what about your ISRs? They are not listed in this table.

    Again, if you put a breakpoint onto an ISR and the debuggers breaks in flash, then the breakpoint was put in flash and therefore the the ISR is still running in flash, not ram.

    It is possible, that the attributes required to tell the compiler that a function is an ISR overrides the pragma that tries to locate the ISR in your FLASHCODE segment.
    Usually, the compiler/linker will handle ISR placing by itself, ensuring that ISRs are placed in lower 64k (interrupt vectors are 16 bit only, so they can only point into lower 64k address range).

    If this is the case, then you must manage it by your own: copy the ISRs from flash to ram, copy the vector table form flash to ram, change the vectors to point to the RAM location, then set the bit for using the RAM vectors.

    I've never placed an ISR into ram so far (and if I ever do, it will likely be with MSPGCC.)

  • Hi Leo,

    I am still struggling to get confidence in setting up interrupts to run in RAM. As per the forum post, I have reported that the FET programmer always reports a flash address. I opened your project from http://processors.wiki.ti.com/index.php/File:MSP430F5438A_RAM_INT_VECT_CCS.zip in CCS5 with the latest updates. The code runs but if I place a breakpoint in the ISR it shows an address of 0x5C00 – which is flash. Please help.

  • James,

    i think there is a slight misunderstanding here. The RAM_INT_VECT_CCS example is an example code for moving only the interrupt vector table to RAM. However i believe what you want is to move the ISR to RAM. In this case, i would refer to code example for MSP430F54xx(non-A) version: http://www.ti.com/litv/zip/slac166s.

    Have you tried this?

  • Hi Leo,

    Thanks for the fast reply - that was a massive misunderstanding on my part. I will test this later today - but I have many applications now which use functions based in RAM - I didn't release I could use the same approach for ISR as well. 

    Are there any limitations to what code can be ran in RAM? I tried to move a function from C, and all the functions which it depends upon to RAM also, and it crashed the MSP430. The routine included DMA and LPM. Maybe, I will investigate this further.

    Thanks again, James. 

  • Hi everyone,

    As a final note I wanted to list pitfalls - at the very least for my future record! 

    1. Remember to use something like 

    memcpy((void*)0x1C00,(const void*)0x5C00,0x0700);

     to copy the functions from Flash to RAM at startup. I placed it just after the watchdog hold in main(). Some example codes use their own flash to ram copy function (either is fine). 

    2. Remember to use the correct length for memcpy or bespoke function.

    The length parameter  (0x700) must match the definition in the .cmd file. As you increase the number of functions in RAM you are likely to increase the segment sizes in the .cmd file. The number of times I forgot to update my copy function is untrue. You get some real weird symptoms if you only copy say the example code length which is 0x500. 

    3. To place a function or ISR into RAM (using Code Composer Studio - V4) use 

    #pragma CODE_SECTION(DMA_ISR,".run_from_ram")

    where DMA_ISR is replaced with the name of the function. Place the #prgama above the function definition. Make sure the .cmd file has run_from_ram defined and that the bespoke flash and ram segments are the same size. Something like the below in MEMORY, 

    RAM_EXECUTE : origin = 0x1C00, length = 0x0700
    RAM : origin = 0x2300, length = 0x3900
    
    FLASH_EXECUTE : origin = 0x5C00, length = 0x700
    FLASH : origin = 0x6300, length = 0x9C80

    Notice all the lengths are the same (0x700). Just update the existing RAM and FLASH with a new origin and the small length.

    Remember to add a SECTIONS statement:

    .run_from_ram: load = FLASH_EXECUTE, run = RAM_EXECUTE

    4. The RAM interrupt table is NOT the ISR!

    I am not sure why I thought this was required. As Leo and Michael have exampled - only ever needed for custom bootloader. I was purely trying to save power by running different parts of code in RAM. 

     Hooray for MSP430s! 

  • James,

    thanks for the summary. I made also some guide which can be found here:

    http://processors.wiki.ti.com/index.php/MSP430_FAQ#Running_Code_from_SRAM

**Attention** This is a public forum