Jump to content
spirilis

Energia and Wolverine - Tips

Recommended Posts

I wrote this up the other week but forgot to post it.  Just a little power-user help for folks interested in really exploring the "potential" behind FRAM memory in Energia.

 

Tips & Advanced Tricks for using Wolverine with Energia 12
 
I've brainstormed a few things folks might want to play with if they're interested in getting the most out of their Wolverine LaunchPad and its built-in FRAM memory.  The FRAM memory, being modifiable yet persistent, is quite different from Flash and our C compilers aren't necessarily laid out to make it super-simple to use.  That said, it's not really that hard to use.
 
There are a few "tricks" you can employ with Energia 12 to get more out of your Wolverine-
 
1. Declare global variables as being part of the ".text" linker section, so they live in FRAM and are not copied to/fro RAM on bootup. (this is the "quick hack" way to do it, the long & canonical way to do this is to define a new section in the Linker Script assigned to the ROM segment and declare your arrays or variables as being a part of that section instead).
 
2. Write/read to the "High" memory segment -- the Wolverine happens to have a 16KB segment of its FRAM living from 0x10000 to 0x13FFF (that's above address 65536 decimal, outside the realm of what a 16-bit pointer can reference) which is unknown to the MSPGCC compiler that ships with Energia 12, since it doesn't support large-memory model.
 
3. Move your RAM to FRAM, giving you more RAM (stack & heap) if it's what you want.  This isn't always necessary since you can declare large arrays as a global in the ".text" section per #1 above.
 
Without further adieu, let me show you:
 
1. In your sketch, up top you can do something like this:
 
#define PERSIST __attribute__((section(".text")))
 
and then declare global variables with this new "PERSIST" keyword like such:
 
uint8_t DisplayBuffer[LCD_MAXIMUM_Y][LCD_MAXIMUM_X] PERSIST;
 
Your "DisplayBuffer" array will live in FRAM and won't hog up any of your RAM.  It will co-exist with program code, so be VERY VERY SURE not to make any "buffer overflow" mistakes and write before or past the end of its memory boundaries or you will corrupt your sketch's binary application code.  Take this doubly serious when the application in question involves Internet connectivity of any kind; I can imagine the spectacular headline on the blogs "IoT application sports buffer-overflow exploit where attacker can write any binary MSP430 code they want!"
 
Alternately, you can use the InfoMem segments:
 
uint8_t DisplayBuffer[512] __attribute__((section(".infomem")));
 
which has 512 bytes (maximum) available for your use; it lives outside the main code FRAM area and if you try writing beyond it, you attempt to write to the calibration data (TLV) FRAM segment which should throw an internal violation (not sure what happens, whether the chip halts or resets or does nothing, but the calibration info will remain safe).  Unlike other MSP430 chips, Info segment "A" can be written on these as the calibration data is stored in another "special" FRAM segment at 0x1A00.
 
 
2. I wrote some functions employing assembly language; which the current stable release of MSPGCC does understand (the extended-memory 20-bit instructions) that gives you a way to copy data to or from the higher memory segments.
See here: HighMem.zip
 
The functions are called highmem_write() and highmem_read().  You can import the HighMem library into your sketch, and it should add: #include "highmem.h"
to your sketch.
 
The functions are:
 
void highmem_write(uint32_t dstaddr, void *srcaddr, uint16_t len)
You give it the FRAM destination address as a 32-bit integer (0x10000 or above), followed by a pointer to a buffer in your SRAM or lower FRAM, followed by the # of bytes you want to write.
 
The reciprocal of this is highmem_read():
void highmem_read(uint32_t srcaddr, void *dstaddr, uint16_t len)
 
Give it the FRAM source address as a 32-bit integer (0x10000 or above), followed by a pointer to a buffer in your SRAM or lower FRAM where you want the data to be copied into, followed by the # of bytes.  The # of bytes can be an odd or even number, as it uses byte-at-once transfers rather than word-at-once.  It uses DMA channel#2 to do the transfers.
 
For those using the FRAM to store a display buffer of some sort, this may be useful as a "bonus" loft or swap area for storing multiple versions of your display framebuffer.  The memory up there is not allocated by the linker, so you need to manage it yourself (keep track of which addresses you've used and how much data is stored there).  It's also not possible to reference the data directly by variables, because the MSPGCC compiler used in Energia 12 doesn't support the large-memory model (doesn't produce the 20-bit pointers and appropriate instructions for accessing it directly).  There is a later revision of MSPGCC (developmental) that does support this, but Energia isn't using it.  At some point in the future Energia will use the RedHat msp430-elf-gcc which does support large-memory model, but that'll be a while out I think.
 
Example sketch using these (writes a stock string to the highmem, then changes 2 bytes before reading it back into an SRAM buffer to be printed over the serial port)-
#include <highmem.h>
#include <string.h>

const char *teststr = "What is this???";

void setup()
{
  // put your setup code here, to run once:
  pinMode(P4_5, INPUT_PULLUP);
  delay(10);
  while (digitalRead(P4_5)) ;  // Wait for S1 to be pressed so the user has time to open serial monitor
  
  Serial.begin(115200);
  Serial.println("HighMem test:");
  highmem_write(0x10000, teststr, strlen(teststr)+1); // Write stock string to highmem
}

void loop()
{
  char buf[128];
  // put your main code here, to run repeatedly:
  buf[0] = 'a';
  buf[1] = 't';
  highmem_write(0x10000+10, buf, 2);  // Write 2-byte modification to highmem
  highmem_read(0x10000, buf, strlen(teststr)+1); // Pull entire string from highmem to 'buf'
  Serial.print("HighMem string says: ");
  Serial.println(buf);
  while(1)
    delay(1000);
}
It prints "What is that???" instead of "What is this???", thanks to the modification performed in loop().
 
 
3. Edit MSPGCC's linker script for the msp430fr5969 to move "ram" into the FRAM address space.  Note you'll have to move the "rom" linker segment up appropriately to give room for the RAM.
 
From your Energia install, e.g. C:\energia-0101E0012, the MSPGCC compiler is stored under "hardware\tools\msp430".  The linker scripts live in: C:\energia-0101E0012\hardware\tools\msp430\msp430\lib\ldscripts with a separate directory for each chip.
 
Go into C:\energia-0101E0012\hardware\tools\msp430\msp430\lib\ldscripts\msp430fr5969 and locate a file called "memory.x". Open this in a text editor, you should see the default contents which look like this:
 
MEMORY {
  sfr              : ORIGIN = 0x0000, LENGTH = 0x0010 /* END=0x0010, size 16 */
  peripheral_8bit  : ORIGIN = 0x0010, LENGTH = 0x00f0 /* END=0x0100, size 240 */
  peripheral_16bit : ORIGIN = 0x0100, LENGTH = 0x0100 /* END=0x0200, size 256 */
  bsl              : ORIGIN = 0x1000, LENGTH = 0x0800 /* END=0x1800, size 2K as 4 512-byte segments */
  infomem          : ORIGIN = 0x1800, LENGTH = 0x0200 /* END=0x1a00, size 512 as 4 128-byte segments */
  infod            : ORIGIN = 0x1800, LENGTH = 0x0080 /* END=0x1880, size 128 */
  infoc            : ORIGIN = 0x1880, LENGTH = 0x0080 /* END=0x1900, size 128 */
  infob            : ORIGIN = 0x1900, LENGTH = 0x0080 /* END=0x1980, size 128 */
  infoa            : ORIGIN = 0x1980, LENGTH = 0x0080 /* END=0x1a00, size 128 */
  ram (wx)         : ORIGIN = 0x1c00, LENGTH = 0x0800 /* END=0x2400, size 2K */
  rom (rx)         : ORIGIN = 0x4400, LENGTH = 0xbb80 /* END=0xff80, size 48000 */
  vectors          : ORIGIN = 0xff80, LENGTH = 0x0080 /* END=0x10000, size 128 as 64 2-byte segments */
  far_rom          : ORIGIN = 0x00010000, LENGTH = 0x00004000 /* END=0x00014000, size 16K */
  /* Remaining banks are absent */
  ram2 (wx)        : ORIGIN = 0x0000, LENGTH = 0x0000
  ram_mirror (wx)  : ORIGIN = 0x0000, LENGTH = 0x0000
  usbram (wx)      : ORIGIN = 0x0000, LENGTH = 0x0000                                                                                                                                                          }
REGION_ALIAS("REGION_TEXT", rom);
REGION_ALIAS("REGION_DATA", ram);
REGION_ALIAS("REGION_FAR_ROM", far_rom);
PROVIDE (__info_segment_size = 0x80);
PROVIDE (__infod = 0x1800);
PROVIDE (__infoc = 0x1880);
PROVIDE (__infob = 0x1900);
PROVIDE (__infoa = 0x1980);
What we're interested in here is the "ram (wx)" and "rom (rx)" definitions in the MEMORY { } declaration.
The default setup puts RAM where it should be; at the Wolverine's 2KB SRAM segment starting at 0x1C00.  The "rom" is
actually FRAM, but called "rom" since the main linker scripts are common to all the msp430 targets supported and expect
to see the main program memory called "rom".
 
So for fun, let's move our 2KB RAM segment into FRAM.
 
It'll look like this:
 
  ram (wx)         : ORIGIN = 0x4400, LENGTH = 2048 /* END=0x4C00, size 2K */
  rom (rx)         : ORIGIN = 0x4C00, LENGTH = 45952 /* END=0xff80, size 48000-2048 = 45952 */

Note I used decimal for the LENGTH part so it's easier to think through.  We're putting our "ram" segment at the start of
FRAM, and moving the start of the "rom" segment 2KB higher to compensate, but also telling the linker that its segment is
now 2048 bytes (2K) smaller.  This last piece is very important; you'll see errors in compiling if you get it wrong.
 
Now one of TI's big marketing push is you can partition your FRAM to give more space to RAM vs. code.  Let's say you have
a sketch that runs beautifully on the MSP430F5529 LaunchPad with its 8KB available SRAM, but bombs on the Wolverine.  You
want to give the Wolverine 8KB RAM too so it'll work right.
 
It'll look like this:
 
  ram (wx)         : ORIGIN = 0x4400, LENGTH = 8192 /* END=0x6400, size 8K */
  rom (rx)         : ORIGIN = 0x6400, LENGTH = 39808 /* END=0xff80, size 48000-8192 = 39808 */

We put "ram" at the start of FRAM, gave it 8192 bytes (8KB), moved the "rom" segment up by 8KB and shrunk its total
length by 8KB.
 
Note that unfortunately you can't just combine your existing SRAM and FRAM together since they are not contiguous in address
space.  SRAM starts at 0x1C00 and ends at 0x2400, where FRAM starts at 0x4400; there is a 0x2000 (8KB) gap between those two.
 
It's always a good idea to keep the original ram & rom specifiers in that memory.x file but commented out so you can quickly
go back to the original configuration.  Here's what it looks like:
  /* ram (wx)         : ORIGIN = 0x1c00, LENGTH = 0x0800 */ /* END=0x2400, size 2K */
  /* rom (rx)         : ORIGIN = 0x4400, LENGTH = 0xbb80 */ /* END=0xff80, size 48000 */
  ram (wx)         : ORIGIN = 0x4400, LENGTH = 8192 /* END=0x6400, size 8K */
  rom (rx)         : ORIGIN = 0x6400, LENGTH = 39808 /* END=0xff80, size 48000-8192 = 39808 */
 
You can then comment out the 2nd ram/rom definitions and uncomment the first one (notice the "*/" after "LENGTH = " in the
first 2 lines) to revert back to the default memory setup.
 
That said, unless you are just trying to run a sketch that works beautifully on the MSP430F5529 or Tiva-C due to its expansive
RAM and won't on the Wolverine, there's probably no reason to hack your linker script when you can just declare big buffers
as part of the ".text" section per #1 explained above.

Share this post


Link to post
Share on other sites

@@spirilis Is this writeup still good for Energia 17?

Yep as Energia 17 is still using @@pabigot 's old MSPGCC ... I am told the big switch to msp430-elf-gcc is supposed to happen soon, but for Energia 17 all of this thread should apply.

(Should still apply after the switch but the linker script stuff might be different.)

Share this post


Link to post
Share on other sites

Hi!

I try to implement solution 3) for the MSP430FR2433. Its exactly my use case... implementing a second (persistent) buffer of ~3kb which does otherwise not fit to the 4kb RAM anymore.

However, following the suggested directory structure I cannot find the folder MSP430FR2433. Looking at folder msp430fr2xx_4xxgeneric instead, there is only one file periph.x but not memory.x. Does the solution still work for this MCU & Energia?

Thanks in advance.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×