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.
I hadn't noticed it before, but some of the 5xx devices (such as the msp430f5438a) spec a different operating current when executing from ram vs flash. I don't really have a use case for this yet, but putting a few ISRs in RAM seems like something interesting that I'd love to play with :)
Maybe my google-fu is rusty but I haven't come across very many examples on that do this. Slaa103 (Flash self-programming guide) has some assembly that copies a chunk of flash code into ram and then jumps to it (Figure 3), but Id love to stay in my nice, civilized C environment because writing things in assembly makes it feel like work :(
Does anyone have a C example of how to do this, or should I set out to write my own?
Hi memoryleak,
we have created some sample code that demonstrates execution from RAM. Hopefully it could be a good starting point for you.
Regards,
Dung
One main reason for executing something in ram is that you cannot do burst flash writing when the code that does it is in flash itself.
Some MSPs allow moving the vector table into RAM, so if you are flashing data and need some ISR being executed while the flash is busy erasing/writing, it might be useful to move it into RAM (and the vector table too, as it is inaccessible in flash during erase/write).
I don't think that executing code from ram instead of flash will save that much energy, but well, in theory the whoel applicaiton can be copied into ram and executed there. If there is enough ram, that is.
There are several way for putting code into ram. One way is to move the function into the data segment. Then it is stored in the flash area along with the variable init values and copied into ram at startup.How to do this depends on the compiler (#pragma, function attributes etc.)
The ddrawback is that the function can be overwritten, and even if not, it consumes ram all the time. Moving just the required code to ram temporarily might be the better solution.
It should be possible to generate fully relocatable code too in C, as long as you don't use any class stuff. (but usually, you cannot rely on this if not explicitely supported by the compiler), so you can have the funciton in flash, copy it to RAM when needed. Fully relocatable means that inside the code chunk only relative jumps are used, and calls are only used to functions that will never mode or through function pointers
Dung,
Thanks for the examples! The procedure looks pretty straightforward, I can't wait to play with these later today :)
Jens,
As always, I appreciate your thoughts. I also wouldn't think that executing out of ram would save much power, but apparently it cuts CPU related power by a decent bit(at least in the 5438a). This is a purely academic pursuit for me right now, so I have no real power target other than "as low as I can possibly make it". Right now I spend most of my time in LPM3 (running on VLO), waking only to service and interrupt, do some math (running on DCO) and go back to sleep. If large portions (CPU time wise) of my code can fit in ram I might be able to reduce power significantly if I have the extra ram to devote to code storage.
I hadn't thought of moving the ISR vector table to ram or even modifying the ISR vectors, I had just planned to have a one-liner ISR that calls my volatile function. Once I get that working I think I'll look at moving the ISR vectors around :)
Thank you both for the help!
Executing the majority of my ISR out of ram reduced my CPU related power consumption by about ~20% (on my FE4272). My margin for error is pretty high (quickie experiment, hand dmm) but it does seem to be a viable way to squeeze few more electrons out of a design if you're feeling hardcore about it :)
Of course since I don't touch the CPU but every 16ms and only for a handful of cycles, the savings is somewhere around the difference between picking XCAP14PF vs XCAP0PF for my crystal. Still a fun optimization though :)
Indeed it is. I never used hte MSP because of its low power consumption. My predecessor seelcted the MSP because its hardware modules were superior to the PICs while still being cheap and easy to handle. Then I inherited all the PIC projects and some started MSP migrations and had to do all the new firmware. It was easier to writ ethe new ode than to understand the condensed and complicated (because of the hardware limitations) PIC code.memoryleak said:Still a fun optimization though
Anyway, many people pick the MSP because of its low power consumtion, and making it even lower is a nice thing.
For larger projects, it still needs to be calculated whether the reduced power consumption for RAM execution pays for the increased runtime required for the setup and copying. Especially if the code needs to be moved more than once (there's so much more flash than RAM and therefore larger projects might need to exchange the code in ram more or less often)
Hello
I have been using your example to program a small bootloader program that can update the flash via an RF link.
I run into problems now because I erase some functions I use from a library, and although the function itself is also copied to RAM first, at some point the code still jumps to Flash rather than to RAM.
How can I make sure that library functions are also called from RAM?
Is this an IAR problem or is there some linker option to force it to use the functions in memory?
I have not copied all my code into RAM, to save memory, the initialisation of my system can be run from flash first. Once initialised, I then jump to running from RAM.
Is this ok to do? Or should I copy all functions to RAM first?
thanks
Only move the code to ram that is needed when there is no more flash.
However, if you use a library, fucntions of the library may use other functions and might not kow htat these are moved to ram. If the library was precompiled, the linker will link these 'internal' calls to the flash location.
Also, if the library is available in source code, either the calls inside or the segment definitions need to be changes so they will assume the code in RAM rather than flash. (if you change the segment definitions, the linker will automatically adjust the calls and also copy the functions from flash to ram on startup, but that's not trivial)
This kind of code tweaking usually only works with code you have written yourself or at least adjusted yourself.
Bout the segment definitions, on mspgcc, you simply say in yur source code that the function belongs to the data segment. Then it is treated liek initialized variables (copied to ram on startup) and all references to it are pointing to ram.
I don't know how to do it with IAR. Maybe some sort of #pragma.
Hi All,
I want to run function form RAM in CC430F5137. I saw RAM example project.
I have once question, how do i decide how much memory to allocated to the function.
For ex: In linker file of the example I found this,
RAM_MEM : origin = 0x1C00, length = 0x0200 ????
FLASH_MEM : origin = 0x5C00, length = 0x0200 ???
How those numbers(red)are decided ??
Thanks,
Rahul
For obvious reasons, FLASH_MEM and RAM_MEM must be same size.
The size of 0x200 likely was chosen because 0x200 is the size of a main flash segment. In general, the size must be of course large enough to hold your code. Aligning the segment sizes to flash segment borders allows runtime-updates of this part of flash without affecting other code in flash. AFAIK not a requirement for the simple case of a monolithic compiled/linked application.
Thanks Michael,
I wanted to run piece code from ram. How do I decide the code size of that piece code. Because I need size to be mentioned in linker command file ! right? pls refer this source code,
//****************************************************************************** // MSP430F543xA Demo - ADC12_A, Sample A10 Temp and Convert to oC and oF // // Description: A single sample is made on A10 with reference to internal // 1.5V Vref. Software sets ADC12SC to start sample and conversion - ADC12SC // automatically cleared at EOC. ADC12 internal oscillator times sample // and conversion. In Mainloop MSP430 waits in LPM4 to save power until // ADC10 conversion complete, ADC12_ISR will force exit from any LPMx in // Mainloop on reti. // ACLK = n/a, MCLK = SMCLK = default DCO ~ 1.045MHz, ADC12CLK = ADC12OSC // // Uncalibrated temperature measured from device to devive will vary due to // slope and offset variance from device to device - please see datasheet. // // MSP430F5438A // ----------------- // /|\| XIN|- // | | | // --|RST XOUT|- // | | // |A10 | // // Andreas Dannenberg // Texas Instruments Inc. // November 2009 // Built with CCSv4 and IAR Embedded Workbench Version V4.21 //****************************************************************************** // Filename: // msp430x54xA_adc12_10_3.c // Description: // ULP Optimizations Step 4 // Modifications (compared to original code example from TI web): // - (ULP 4.1) Terminate unused GPIO pins // - (ULP 2.1) Use timer module for delays // - (ULP 7.1) Moved global variables as locals into main // - (ULP 5.1) Moved execution of divide operations into RAM // Current consumption: // 246.3uA - 247.3uA //****************************************************************************** #include "msp430x54xA.h" volatile long temp; //volatile long IntDegF; //volatile long IntDegC; #pragma CODE_SECTION(CalculateTempInCandF,".FLASHCODE") void CalculateTempInCandF(void) { volatile long IntDegF; volatile long IntDegC; IntDegC = ((temp - 1857) * 666) / 4096; IntDegF = ((temp - 1857) * 1199) / 4096 + 32; } void main(void) { // volatile long IntDegF; // volatile long IntDegC; WDTCTL = WDTPW + WDTHOLD; // Stop WDT // added per ULP Advisor info PAOUT = 0; PADIR = 0xFFFF; // P1 & P2 PASEL = 0; PBOUT = 0; PBDIR = 0xFFFF; // P3 & P4 PBSEL = 0; PCOUT = 0x4000; // AMP_SD with R to VCC PCDIR = 0xFFFF; // P5 & P6 PCSEL = 0; PDOUT = 0; PDDIR = 0xFFFF; // P7 & P8 PDSEL = 0; PEOUT = 0; PEDIR = 0xFFFF; // P9 & P10 PESEL = 0; // P11OUT = 0; // P11DIR = 0xFF; // P11 // P11SEL = 0; PFOUT = 0; PFDIR = 0xFFFF; // ULP Advisor knows only 'letter' Ports PFSEL = 0; PJOUT = 0; PJDIR = 0xFF; // PJ // copy processing intensive routines into RAM memcpy((void*)0x1C00,(const void*)0x5C00,0x0200); /* Initialize the shared reference module */ REFCTL0 |= REFMSTR + REFVSEL_0 + REFON; // Enable internal 1.5V reference /* Initialize ADC12_A */ ADC12CTL0 = ADC12SHT0_8 + ADC12ON; // Set sample time ADC12CTL1 = ADC12SHP; // Enable sample timer ADC12MCTL0 = ADC12SREF_1 + ADC12INCH_10; // ADC input ch A10 => temp sense ADC12IE = 0x001; // ADC_IFG upon conv result-ADCMEMO // __delay_cycles(75); // 35us delay to allow Ref to settle // based on default DCO frequency. // See Datasheet for typical settle // time. TA1CTL = TASSEL_1 + TACLR; // ACLK, clear TAR // TA1CCR0 = 32768-1; // 1 second TA1CCR0 = (32768*2)-1; // 2 seconds TA1CCTL0 = CCIE; // CCR0 interrupt enabled TA1CTL |= MC_1; // Start timer in up-mode __bis_SR_register(LPM4_bits + GIE); // LPM4 with interrupts enabled ADC12CTL0 |= ADC12ENC; while(1) { ADC12CTL0 |= ADC12SC; // Sampling and conversion start __bis_SR_register(LPM4_bits + GIE); // LPM4 with interrupts enabled __no_operation(); /* // Temperature in Celsius // ((A10/4096*1500mV) - 680mV)*(1/2.25mV) = (A10/4096*666) - 302 // = (A10 - 1857) * (666 / 4096) IntDegC = ((temp - 1857) * 666) / 4096; // Temperature in Fahrenheit // Tf = (9/5)*Tc + 32 IntDegF = ((temp - 1857) * 1199) / 4096 + 32; */ CalculateTempInCandF(); // __no_operation(); // SET BREAKPOINT HERE __bis_SR_register(LPM4_bits + GIE); // LPM4 with interrupts enabled } } #pragma vector=ADC12_VECTOR __interrupt void ADC12ISR (void) { switch(__even_in_range(ADC12IV,34)) { case 0: break; // Vector 0: No interrupt case 2: break; // Vector 2: ADC overflow case 4: break; // Vector 4: ADC timing overflow case 6: // Vector 6: ADC12IFG0 temp = ADC12MEM0; // Move results, IFG is cleared __bic_SR_register_on_exit(LPM4_bits); // Exit active CPU break; case 8: break; // Vector 8: ADC12IFG1 case 10: break; // Vector 10: ADC12IFG2 case 12: break; // Vector 12: ADC12IFG3 case 14: break; // Vector 14: ADC12IFG4 case 16: break; // Vector 16: ADC12IFG5 case 18: break; // Vector 18: ADC12IFG6 case 20: break; // Vector 20: ADC12IFG7 case 22: break; // Vector 22: ADC12IFG8 case 24: break; // Vector 24: ADC12IFG9 case 26: break; // Vector 26: ADC12IFG10 case 28: break; // Vector 28: ADC12IFG11 case 30: break; // Vector 30: ADC12IFG12 case 32: break; // Vector 32: ADC12IFG13 case 34: break; // Vector 34: ADC12IFG14 default: break; } } // Timer A0 interrupt service routine #pragma vector=TIMER1_A0_VECTOR __interrupt void TIMER1_A0_ISR(void) { __bic_SR_register_on_exit(LPM4_bits); // Exit any LPM }
In linker command file,
In sections,
.FLASHCODE : load = FLASH_MEM, run = RAM_MEM
/* CODE IN FLASH AND WILL BE COPIED
TO RAM AT EXECUTION HANDLED BY
USER */
In Memory,
RAM_MEM : origin = 0x1C00, length = 0x0200
RAM : origin = 0x1E00, length = 0x3E00
FLASH_MEM : origin = 0x5C00, length = 0x0200
FLASH : origin = 0x5E00, length = 0xA180
FLASH2 : origin = 0x10000,length = 0x25C00
Hows that 0x200 has been decided!?
Thanks,
Rahul
The size of the segments must be equal or larger than the size of the code you want to place in. That's obvious.
You can figure out the size of the functions by looking at the map file before placing them into the ram segment. Or you just try and when the code is larger, the linker will tell you so and also give you the required size. You can then increase the segment size.
**Attention** This is a public forum