Jump to content
43oh

Saving flash space, by making use of infomem


Recommended Posts

All modern MSP chips to my knowledge feature a small area of "information memory". To my knowledge this is the equivalent to internal EEPROM on a PIC or AVR.

 

Using mspgcc we can easily store an array of constant data into infomem. all that is required is a single attribute tag. For example in my current project I have an array of font data. by moving it to infomem I saved 192 bytes of flash. (Strictly speaking infomem is flash :think: So don't be worried about wearing it out anymore than flash :) ) Anyway here is the code

/*
"Compressed" font data, in form 0b ABCD EFGH IJKL MNOP
 {A-P} represent segment bits in each character

	 __J_ _A__ 
	|\   |   /| 
	| K  H  B |
	L  \ | /  C
	|   \|/   |
	>-M--*--D-<
	|   /|\   |
	N  / | \  E
	| O  I  F |
	|/   |   \|
	 ~~P~ ~G~~		
*/		
const unsigned int font_data[] 
__attribute__((section(".infomem"))) = { 
0x0000,0xC101,0x0110,0x3B89,0x9BD9,0x5BDA,0x0F6D,0x4000,
0x4400,0x0022,0x55AA,0x1188,0x0002,0x1008,0x0001,0x4002,
0xEA57,0x6800,0xB24D,0xDA41,0x3818,0x8659,0x9A5D,0xA840,
0xBA5D,0xBA59,0x0041,0x0042,0x4203,0x1209,0x0621,0xB0C0,
0xAACD,0xB85C,0xBBC1,0x8255,0xABC1,0x825D,0x805C,0x9A55,
0x381C,0x83C1,0x81C5,0x441C,0x0215,0x6834,0x2C34,0xAA55,
0xB05C,0xAE55,0xB45C,0x9A59,0x81C0,0x2A15,0x4016,0x2C16,
0x4422,0x40A0,0xC243,0x0055,0x0420,0xAA00,0x0402,0x0201,
0x0020,0x028D,0x009D,0x000D,0x018D,0x000F,0x9188,0x1E01,
0x009C,0x0080,0x0081,0x1580,0x03C0,0x188C,0x008C,0x008D,
0x015C,0x03D8,0x000C,0x1081,0x1388,0x0085,0x0006,0x0A85,
0x4182,0x0E01,0x000B,0x8388,0x0180,0x11C1,0x3150,0x0000,
};

 

This one line of code is what does all the magic. This tells the linker to move this block of data into the infomem region.

__attribute__((section(".infomem")))

 

 

Be warned the infomem region often contains calibration data for the DCO, it would be wise to make a note of what these values are. In case you accidentally store data over them.

This can easily be done using this command

mspdebug rf2500 'md 0x10FE 0x2'

*The address 0x10FE applicable for the the value line devices. This command will open mspdebug, connect to the device and dump 2 bytes from address 0x10FE, on the value line devices these 2 bytes are the DCO calibration values.

 

An interesting side note about the attribute command. is that it could be used to position a function into infomem. However Some MSP430 devices are unable to execute commands from infomem. (or maybe it's just the MSP430f5438 :lol: )

Link to post
Share on other sites

In IAR workbench use this way:

#pragma constseg = INFOB
LUT_TABLE Lut_adc[2] = {
 -67, 10730,  0,  0,  0,               // Input from U ADC
 0L, 19476L, 0L, 0L, 0L,               // Real measured U

 -59, 30680,  0,  0,  0,               // Input from I ADC
 0L, 39080L, 0L, 0L, 0L                // Real measured I
};
#pragma constseg = default

#pragma constseg = INFOC
const int Lut_pwm[2][LUT_PWM_SIZE + 1] = {
 0, 907, 1814, 2721, 3628, 4535, 5442, 6349, 7256, 8163, 9070,  9977, 10884, 11791, 12698, 13605,    // U to PWM
 0, 986, 1972, 2958, 3944, 4930, 5917, 6903, 7889, 8875, 9861, 10848, 11834, 12820, 13806, 14792     // I to PWM
};
#pragma constseg = default

#pragma constseg = INFOD
const int Factory_max_pwm_U =   8000;
const int Factory_max_pwm_I =   3000;
const int User_min_pwm_U =      0;
const int User_max_pwm_U =      8000;
const int User_min_pwm_I =      0;
const int User_max_pwm_I =      3000;
const int User_stored_pwm_U =   0;
const int User_stored_pwm_I =   1;
#pragma constseg = default

You be able to select concrete segment of info memory.

Link to post
Share on other sites
  • 3 weeks later...

This one line of code is what does all the magic. This tells the linker to move this block of data into the infomem region.

__attribute__((section(".infomem")))

 

You can avoid overwriting the infoa segment, which is where most of the precalibrated values are stored by using:

__attribute__((section(".infob")))

__attribute__((section(".infoc")))

__attribute__((section(".infod")))

 

each .infox section is 64 bytes. If you avoid .infoa you don't have to worry about what might be stored there.

 

[EDIT]

 

-rick

Link to post
Share on other sites

Not all information segments are 64 bytes; some are 128, and one odd line has 96 byte segments. Not all chips have four segments, either; some only have A and B.

 

Accessing sections individually was not supported in the GNU toolchain until late August, and has not yet been incorporated into a versioned release (you or your packager would need to be running off the git branches). With the next release, not only will you be able to place things in specific segments using the section attribute, you will also be able to reference the address of the specific segment and the size of any segment using linker symbols like __infoa and __info_segment_size. This makes using these sections a lot nicer. See https://sourceforge.net/tracker/?func=d ... tid=432701 for details; one day this might make it into a manual.

 

You can't mix .infomem and specific .info segments in the same program. .infomem starts with the lowest addressed segment, which is normally info D; info A is the highest, so you're generally safe from overwriting info A if you don't fill up the other ones.

Link to post
Share on other sites

You're welcome.

 

I have been avoiding all the older gcc releases as I'm only using the newly added value line chips and the FRAM chips. Are there any other nice features on the table for the next release of msp430-gcc?

 

Improved information segment management is one. I've added a couple more intrinsics to match CCS/IAR, and increased the span of legal values for __delay_cycles. Improved stack code for chips with the CPUX extensions (pushm/popm). I'll try to create a human readable summary before the next release, but in general the best option is to look at the tickets on SF for what's been done and what's currently pending. Generally nothing goes in that doesn't have a ticket backing it up, since I use the ticket numbers in the internal test suite.

Link to post
Share on other sites
  • 3 years later...

While reading this thread, I noticed that the answers did not comment on a solution for CCS. 

 

So here are my observations and code samples to make that work in CCS.

 

First, I trimmed down the font sample code from @@greeeg just to see if it will work.

#pragma DATA_SECTION(font_data, ".infoB")
const unsigned int font_data[] =
{
    0x0000,0xC101,0x0110,0x3B89,0x9BD9,0x5BDA,0x0F6D,0x4000,
    0x4400,0x0022,0x55AA,0x1188,0x0002,0x1008,0x0001,0x4002,
    0xEA57,0x6800,0xB24D,0xDA41,0x3818,0x8659,0x9A5D,0xA840,
    0xBA5D,0xBA59,0x0041,0x0042,0x4203,0x1209,0x0621,0xB0C0,
};

Good! This is just the right size to fill up 64 bytes - the size of INFOB. Any bigger and the compiler fails out with an error message about memory size is over filled or something.

 

Next, figure out what special names you can use in the DATA_SECTION declaration.

 

Open up the linker file for your project. In my case, it's called lnk_msp430g2553.cmd. 

 

You will see this inside of it:

    .infoA     : {} > INFOA              /* MSP430 INFO FLASH MEMORY SEGMENTS */
    .infoB     : {} > INFOB
    .infoC     : {} > INFOC
    .infoD     : {} > INFOD

so, use the lowercase name in your declaration.

 

 

And here is my test program that I used to test drive the flash on an MSP43G2553:

#include <msp430.h>

/*
 "Compressed" font data, in form 0b ABCD EFGH IJKL MNOP
  {A-P} represent segment bits in each character

     __J_ _A__
    |\   |   /|
    | K  H  B |
    L  \ | /  C
    |   \|/   |
    >-M--*--D-<
    |   /|\   |
    N  / | \  E
    | O  I  F |
    |/   |   \|
     ~~P~ ~G~~
*/

#pragma DATA_SECTION(font_data, ".infoB")
const unsigned int font_data[] =
{
    0x0000,0xC101,0x0110,0x3B89,0x9BD9,0x5BDA,0x0F6D,0x4000,
    0x4400,0x0022,0x55AA,0x1188,0x0002,0x1008,0x0001,0x4002,
    0xEA57,0x6800,0xB24D,0xDA41,0x3818,0x8659,0x9A5D,0xA840,
    0xBA5D,0xBA59,0x0041,0x0042,0x4203,0x1209,0x0621,0xB0C0,
};

//const unsigned int font_data[]
//__attribute__((section(".infob"))) =
//{
//0x0000,0xC101,0x0110,0x3B89,0x9BD9,0x5BDA,0x0F6D,0x4000,
//0x4400,0x0022,0x55AA,0x1188,0x0002,0x1008,0x0001,0x4002,
//0xEA57,0x6800,0xB24D,0xDA41,0x3818,0x8659,0x9A5D,0xA840,
//0xBA5D,0xBA59,0x0041,0x0042,0x4203,0x1209,0x0621,0xB0C0,
//0xAACD,0xB85C,0xBBC1,0x8255,0xABC1,0x825D,0x805C,0x9A55,
//0x381C,0x83C1,0x81C5,0x441C,0x0215,0x6834,0x2C34,0xAA55,
//0xB05C,0xAE55,0xB45C,0x9A59,0x81C0,0x2A15,0x4016,0x2C16,
//0x4422,0x40A0,0xC243,0x0055,0x0420,0xAA00,0x0402,0x0201,
//0x0020,0x028D,0x009D,0x000D,0x018D,0x000F,0x9188,0x1E01,
//0x009C,0x0080,0x0081,0x1580,0x03C0,0x188C,0x008C,0x008D,
//0x015C,0x03D8,0x000C,0x1081,0x1388,0x0085,0x0006,0x0A85,
//0x4182,0x0E01,0x000B,0x8388,0x0180,0x11C1,0x3150,0x0000,
//};


char  value;                                // 8-bit value to write to segment A

// Function prototypes
void write_SegC (char value);
void copy_C2D (void);

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
  if (CALBC1_1MHZ==0xFF)					// If calibration constant erased
  {											
    while(1);                               // do not load, trap CPU!!	
  }
  DCOCTL = 0;                               // Select lowest DCOx and MODx settings

  BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
  DCOCTL = CALDCO_1MHZ;

  FCTL2 = FWKEY + FSSEL0 + FN1;             // MCLK/3 for Flash Timing Generator
  value = 0;                                // initialize value

  while(1)                                  // Repeat forever
  {
    write_SegC(value++);                    // Write segment C, increment value
    copy_C2D();                             // Copy segment C to D
    __no_operation();                       // SET BREAKPOINT HERE
  }
}

void write_SegC (char value)
{
  char *Flash_ptr;                          // Flash pointer
  unsigned int i;

  Flash_ptr = (char *) 0x1040;              // Initialize Flash pointer
  FCTL1 = FWKEY + ERASE;                    // Set Erase bit
  FCTL3 = FWKEY;                            // Clear Lock bit
  *Flash_ptr = 0;                           // Dummy write to erase Flash segment

  FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation

  for (i=0; i<64; i++)
  {
    *Flash_ptr++ = value;                   // Write value to flash
  }

  FCTL1 = FWKEY;                            // Clear WRT bit
  FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
}

void copy_C2D (void)
{
  char *Flash_ptrC;                         // Segment C pointer
  char *Flash_ptrD;                         // Segment D pointer
  unsigned int i;

  Flash_ptrC = (char *) 0x1040;             // Initialize Flash segment C pointer
  Flash_ptrD = (char *) 0x1000;             // Initialize Flash segment D pointer
  FCTL1 = FWKEY + ERASE;                    // Set Erase bit
  FCTL3 = FWKEY;                            // Clear Lock bit
  *Flash_ptrD = 0;                          // Dummy write to erase Flash segment D
  FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation

  for (i=0; i<64; i++)
  {
    *Flash_ptrD++ = *Flash_ptrC++;          // copy value segment C to segment D
  }

  FCTL1 = FWKEY;                            // Clear WRT bit
  FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
}

Here is a screen shot of CCS as I inspected the information memory section.  

 

The way I have the screen stretched out causes each memory section to be all on one line ie:

  • INFOD (0x1000),
  • INFOC (0x1040),
  • INFOB (0x1080) and
  • INFOA (0x10C0).

 

post-112-0-45252600-1420674944_thumb.png

 

 

Footnote: For reference, here's TI's take on this topic: 

Link to post
Share on other sites
  • 4 weeks later...

I know that I'm going to forget how I did this so here's my mind dump on figuring out the flash timing generator settings:

/*******************************************************************************
 * Divide Flash clock by 1 to 64 using FN0 to FN5 according to:
 *    32*FN5 + 16*FN4 + 8*FN3 + 4*FN2 + 2*FN1 + FN0 + 1
 *
 * #define FN0  (0x0001)
 * #define FN1  (0x0002)
 * #define FN2  (0x0004)
 * #define FN3  (0x0008)
 * #define FN4  (0x0010)
 * #define FN5  (0x0020)
 *
 * Select the clock source for the Flash Timing Generator
 *
 * #define FSSEL0                 (0x0040)       // Flash clock select 0
 * #define FSSEL1                 (0x0080)       // Flash clock select 1
 *
 * #define FSSEL_0                (0x0000)       // Flash clock select: 0 - ACLK
 * #define FSSEL_1                (0x0040)       // Flash clock select: 1 - MCLK
 * #define FSSEL_2                (0x0080)       // Flash clock select: 2 - SMCLK
 * #define FSSEL_3                (0x00C0)       // Flash clock select: 3 - SMCLK
 *
 * What is the frequency of these various clock sources?
 * ACLK = 32768 kHz if you're using an external crystal
 * MCLK =
 * SMCLK=
 *
 * The target value for the FTG frequency is 514 kHz < SMCLK < 952 kHz (this is device specific).
 *
 * Calculate if you can set the FTG up in this range:
 * (952 - 514) + 514 = 733 <-- Suggested target value to achieve with above formula
 *
 * 32768 kHz / 733 = 44.704
 *
 * What FNn value will produce a result close to 733?
 * 733 / 44.703 = 16.396
 *
 * Let's round that to an even 16.
 *
 * What FNn value is equal to 16?
 * FN4 = 0x0010 or 0d16
 *
 * but that's not what we want. We want a result of 0d16. Choosing FN2, putting
 * it into the formula gives us:
 * 4*0x0004 + 1 = 17
 *
 * Let's recompute the resultant FTG frequency with that value:
 * 32768 / 733 = 44.704
 * 733 / 44.704 = 16.396
 *
 * Now compare by refactoring:
 * 733 / 17 = 43.118
 *
 * 43.118 * 17 = 733 Hz
 *
 * Now we can give this a try.
 *
 * Setup FCTL2 with the following values:
 * FCTL2 = FWKEY + FSSEL0 + FN2;
 *
 * Observe the Memory Rendering of the CPU to observe and verify that reliable
 * values are programmed into INFOD, INFOC and, INFOB information memory segments.
 *
 * When the MSP430 is configured to operate at 16 MHz, all 128 memory cells
 * appear to program properly. There are no stuck or inverted bits as the program
 * flows.
 *
 ******************************************************************************/

//  FCTL2 = FWKEY + FSSEL0 + FN1;             // MCLK/3 for Flash Timing Generator

  FCTL2 = FWKEY + FSSEL0 + FN2;

Note: These calculations depend upon this:

  // Configure MSP430 for 16MHz operation
  BCSCTL1 = CALBC1_16MHZ;
  DCOCTL  = CALDCO_16MHZ;
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...