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.

FRAM vs. RAM: how to program

Other Parts Discussed in Thread: MSP430FR4133

I am programming a system, where I want to position into RAM

- the program parts that are urgent (ISR)

- the program parts which perform the biggest part of runtime

- intermediate values and constants

So I have to copy these values from FRAM to RAM on each power out.

In assembly language I would use absolute addresses and relative jumping etc., a relative complex handling.

Does anyone have any info or examples, especially in C with info how to handle that with IDE.

  • MSP430FR4133 has only 2 KB of RAM and the max MCLK is 16 MHz.
    Why do you want to run your code from RAM of that chip? It is small and slow.
    You talked about assembly language. Why do you ask info or examples in C?
  • Very easy: 2KB of RAM are plenty room for time-critical actions and complicated mathematics, the code of some FR processors runs faster in RAM (up to 24 MHz at FR573x with no wait states) consuming a lot less energy, the number of read and write accesses is unlimited, just to mention the most important reasons - I use FRAM for data (=results) logging and - of course - for code, constants, variables, partially copying it into RAM.
    I would like to get support/sample code/solutions from third parties in coding it in C to give others a chance to understand the problems - possibly there are usable routines in libraries, e.g. for wake-up from power-out. Of course, I can easily code my assembly program in C with hard-addressed assembly thinking - but no C programmer will understand it.
  • You can do this by creating one or more RAM sections in the Linker Command File;

    UNION RUN = RAM1
    	{
    	.RAM1_functA	: LOAD = FLASH2 | FLASH1B1,
    					  LOAD_START(_RAM1_functALoadStart),
    					  LOAD_END(_RAM1_functALoadEnd),
    					  LOAD_SIZE(_RAM1_functALoadSize),
    					  RUN_START(_RAM1_functARunStart)
    
    	.RAM1_functB	: LOAD = FLASH2 | FLASH1B1,
    					  LOAD_START(_RAM1_functBLoadStart),
    					  LOAD_END(_RAM1_functBLoadEnd),
    					  LOAD_SIZE(_RAM1_functBLoadSize),
    					  RUN_START(_RAM1_functBRunStart)
    	}
    

    And the following example program speaks for itself;

    extern void	_RAM1_functALoadStart, _RAM1_functARunStart, _RAM1_functALoadSize;
    
    #pragma CODE_SECTION(InhibitTimer, ".RAM1_functA")
    void InhibitTimer (void)
    {
    	TA0CCTL0 = 0;
        __delay_cycles(1000000);
    	TA0CCTL0 = CCIE;
    }
    
    void main (void)
    {
    	WDTCTL = WDTPW + WDTHOLD;		// Stop WDT
    
    	memcpy (&_RAM1_functARunStart, &_RAM1_functALoadStart, _symval(&_RAM1_functALoadSize));
    
    	// Flash LED by Timer
    	P1DIR |= 0x01;                            // P1.0 output
    	TA0CCTL0 = CCIE;                          // CCR0 interrupt enabled
    	TA0CCR0 = 50000;
    	TA0CTL = TASSEL_2 + MC_1 + TACLR;         // SMCLK, upmode, clear TAR
    
    	__bis_SR_register(GIE);
    
    	while (1)
    	{
    	    __delay_cycles(1000000);
    	    InhibitTimer();
    	}
    }
    
    // Timer0 A0 interrupt service routine
    #pragma CODE_SECTION(TIMER0_A0_ISR, ".RAM1_functA")
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void TIMER0_A0_ISR(void)
    {
      P1OUT ^= 0x01;		// Toggle P1.0
    }
    

  • Leo is correct and shows one way of moving code from one memory location to another (e.g. FRAM to RAM).


    The linker also supports a "Copy Table" feature that simplifies the code a little bit. As with the previous example, this requires editing the linker command file. Here, the syntax is slightly different in that you specify a "table" to hold the address & length information:

        UNION
        {
            .f1     : {} load = FRAM, table ( func1_copy_table )
            .f2     : {} load = FRAM, table ( func2_copy_table )
        } run = RAM
    

    This generic example shows two functions (each in their own code section ".f1" and ".f2") being linked into FRAM, but to be run from RAM. Notice that the linker table() function defines a symbol (e.g. func1_copy_table) that points to a structure including the load address, run address, and length of the output section.

    The compiler runtime support library includes a simple function call copy_in() for accessing the copy addresses, as well as invoking memcpy() to actually move the contents pointed to by the table.

    The main code (shown below) for this generic example includes a reference to the:

    • cpy_tbl.h header file
    • extern declarations for the copy table symbols
    • Creates a new code section for each function we want to link/run independently
    • Calls the copy_in() function before each function is executed
    #include "driverlib.h"
    #include <cpy_tbl.h>
    #include "hal.h"
    
    
    void func1();
    void func2();
    extern COPY_TABLE func1_copy_table;
    extern COPY_TABLE func2_copy_table;
    
    
    void main()
    {
        // Stop watchdog timer
        WDT_A_hold( WDT_A_BASE );
    
        // Initialize GPIO
        initGPIO();
    
        copy_in( &func1_copy_table );
        func1();
    
        copy_in( &func2_copy_table );
        func2();
    
    }
    
    #pragma CODE_SECTION (func1, ".f1")
    void func1(void) {
        GPIO_setOutputHighOnPin( LED1_PORT, LED1_PIN );
    }
    
    #pragma CODE_SECTION (func2, ".f2")
    void func2(void) {
        GPIO_setOutputHighOnPin( LED2_PORT, LED2_PIN );
    }
    

    In the end, this code is quite similar to that provided by Leo; but simplifies the creation and access of the run/load addresses using the linker's Copy Table feature. You can find more info on this feature in the following two documents:

    Take Care,
    Scott

**Attention** This is a public forum