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.

msp430fr6989 write in FRAM

Other Parts Discussed in Thread: MSP430FR6989, MSP430FR5739

Hi,

we tried to executed the example below in order to write in FRAM. We use the CCS. At the end we read in the variable *FRAM_write_ptr the value =  0xFFFFFFFF, so it does't work

Our final goal is to store data coming from ADCs in FRAM (data logger).

Where is the mistake?

Is it the right way to proceed?

#include <msp430.h>

void FRAMWrite(void);

//unsigned char count = 0;
unsigned long *FRAM_write_ptr;
unsigned long data;

#define FRAM_TEST_START 0xD000

int main(void)
{
  WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT

  // Configure GPIO
  P1OUT &= ~BIT0;                           // Clear P1.0 output latch for a defined power-on state
  P1DIR |= BIT0;                            // Set P1.0 to output direction

  // Disable the GPIO power-on default high-impedance mode to activate
  // previously configured port settings
  PM5CTL0 &= ~LOCKLPM5;

  // Initialize dummy data
  data = 0x00010001;

  while(1)
  {
//    data += 0x00010001;
    FRAM_write_ptr = (unsigned long *)FRAM_TEST_START;
    FRAMWrite();                            // Endless loop
//    count++;
//    if (count > 100)
//    {
//      P1OUT ^= 0x01;                        // Toggle LED to show 512K bytes
//      count = 0;                            // ..have been written
//      data = 0x00010001;
//    }
  }
}

void FRAMWrite(void)
{
//  unsigned int i=0;

//  for ( i= 0; i<128; i++)
//  {
    *FRAM_write_ptr = data;
    __no_operation();
//  }
}

  • Hi Alessandro,

    In CCS, by default the MPU (Memory Protection Unit) is enabled with partitions set by the compiler and linker cmd file. This memory protection unit restricts access to the FRAM and by default with this code you have posted, it is going to set all of the FRAM to read + execute access only - no write access. The MPU is a good thing - because FRAM is so easy to write to, you need something to help protect your code from being corrupted if for example you write off the end of an array or something similar. So this is why we have the MPU.

    The problem in your case, is that the compiler doesn't really know that you are writing to FRAM with this code and doesn't know to set the MPU accordingly. Also, the way your code is written could be a problem if your program was larger and so it happened to have code at 0xD000, because you would be overwriting your code - the linker handles the code placement and doesn't know that you are writing to 0xD000.

    The recommended way to place variables in FRAM, is to use the PERSISTENT pragma in CCS. See the FRAM best practices guide www.ti.com/lit/pdf/slaa628 section 3.4.1 and the compiler guide www.ti.com/lit/pdf/slau132 . There are also some other forum posts on a similar topic.

    Basically, what your code should instead do, is to declare a variable for the data you want to store and use the persistent pragma, like this:

    #pragma PERSISTENT(FRAM_data)
    unsigned long FRAM_data = 0;

    Then instead of using that FRAM_write_ptr to do your writes, simply use this like any other variable:

    	FRAM_data = data;

    If you want to store a whole bunch of data, like in a logging application, simply declare an array instead, like this:

    #pragma PERSISTENT(FRAM_arr)
    unsigned long FRAM_arr[1000] = 0;

    And then use it like any other array:

    FRAM_arr[i]=data;

    or could still use a pointer here now just like you could with any array.

    In CCS, when you debug, you can view FRAM_data in the expressions window, and you'll see where the compiler chose to place it (likely 0x4400), and you'll be able to see that writes are now successful, because the compiler and linker knew to adjust the MPU settings to allow for you to write to this part of FRAM.

    The advantages of this method are a few:

    1. The linker knows how much space to allocate for your FRAM data that you are going to want to write, so it will leave enough space and not put any code there that could accidentally get corrupted otherwise

    2. The compiler and linker will automatically adjust the MPU settings to allow write access to your variable while still protecting your code, without you having to do anything custom yourself with the MPU settings

    3. You can use the variable just like any other variable that was in RAM without even having to think about the fact that it's in FRAM - it doesn't need extra special treatment like flash where you'd have to have some write function. The only thing to remember is that the variable will retain its value through a power cycle/reset because it is in FRAM, only being re-init to the default value when you load code. If you want to truly have it reset every time like a RAM variable, you can init it in your main code somewhere before you use it, but otherwise you will have access to the persistent data you've logged there through power cycles.

    I hope that this helps address your needs. This doesn't put the FRAM variables at a user-defined address, but that usually isn't needed since you can just access these like regular variables - if there is some reason you need to define the specific address for the placement, please let us know why and we can help you work towards a solution for that. But this method with the simple pragma PERSISTENT is the way we recommend for most people and seems to be working fine for most use-cases that I've encountered.

    Regards,

    Katie

  • Hi,

    very clear explanation. We'll try to implement your suggestions.


    Thank you.

    Best regards.

  • Hi,

    I have some doubts.

    There is another feature to be implemented: The upgrade in field of the firmware. The bus used for this is the SPI, Maybe it will be the topic of another post.

    My question is: How to download the new program (knowing that the SPI send 8 bit at a time)  keeping the MPU active, without overwriting the current firmware or stored data? After reset the Program counter has to point to the new program.

    Are some manual memory partitions  necessary?

    Thank you very much for your support.

  • Hi Alessandro,

    The MSP430FR6989 has a built-in bootloader in ROM for this purpose. However this loader only supports UART (or I2C). If you really need SPI, then you'll have to implement a main-memory bootloader. We have an application note MSPBoot www.ti.com/.../slaa600 just for this purpose. In that case, you are going to be modifying and controlling your linker file, and in this case yes you may want to control placement yourself then by further customizing your linker file. You would set aside a portion of your FRAM in your linker file (e.g. in the linker file, set the Memory FRAM to start at a later address and subtract from its length, then add a line to make an area like MY_FRAM defined to actually start at 0x4400 the beginning of FRAM and take up the space you just carved out of FRAM). This would be to leave a space for your logging data in your application project. Then in the SECTIONS part of the linker file, have the .persistent section of the linker file be placed in this area that you've set aside, and also in that case manually control your MPU settings so that they are set correctly for your custom setup.

    Currently, MSPBoot doesn't have an example for MSP430FR6989, rather it has an example for one of the older FRAM parts MSP430FR5739. It also does not yet support 20-bit address space, but I do know others have modified it for this before if you search the forum (and we can also try to help here on e2e if you have questions as you work on it). Please know that we actually are currently working on an update for MSPBoot that will address this gap, including an MSP430FR6989 example with 20-bit support, but I do not know any release date for this yet - it could be some time.

    Regards,
    Katie
  • Hi Alessandro,

    Did this meet your needs for FRAM programming for now - can we close the thread or is there more we can do to help?

    Regards,
    Katie
  • Hi,

    the only problem is that the FRAM is segmented, some addresses in the middle are dedicated to interrupt vectors. So if the array is as big as the whole FRAM, it is not instantiated by compiler. So now we can write only in the low side of the FRAM, till the address of the interrupt vector.

    Thank you

    Best regards.

  • Hi Alessandro,

    Unfortunately, this is because older MSP devices did not have any addresses going above 16-bit addressing, and the interrupt vector table (IVT) was placed at the end of the address space. To not break compatibility, when we started making larger memory devices with 20-bit addressing, there is now IVT still in the same place but you may have things placed at above 0x10000. The good news is that there are ways around it, and it has been done before.

    To get around this, my recommendation would be to actually specify 2 arrays in your code - one to take up your space below the IVT, and one to take up the space above 0x10000. If possible, if there is enough space above 0x10000 for what you need to store you could also use just that - the compiler & linker should be able to place your array up there if it won't fit in lower memory. I suspect your issue might be that you have to make sure that you have set your project to use large data model (under Project > Properties > Build > MSP430 Compiler > Processor Options select "Large" for the drop-down for specify the data memory model). Otherwise, the compiler isn't going to place any data in upper memory.

    If you need to use space both above and below the IVT, my recommendation would be to have 2 arrays in your code, but if you want your code to really act like it's one contiguous address space you can basically have virtual addresses. E.g. your code could act like you have some giant contiguous block and address it that way, but every time you go to actually access the space you would want to call a function you've written to calculate which array and which address in the array that particular data is actually stored in. Essentially make some helper function to calculate actual address from virtual address.

    Another note: when accessing reading/writing into the 20-bit address space, you typically need to tell the compiler that it needs to use the special 20-bit versions of the assembly instructions, so that the address isn't truncated - you can do this with some pragmas that are described in www.ti.com/.../slau132 Table 6-5 MSP430 Intrinsics you'll see __data20_read_short(), __data20_write_char(), etc for accessing 20-bit address space correctly.

    Regards,
    Katie

  • Hi Katie,

    Could you be a little specific of how to do this? How to assign two arrays in the .cmd file.  I am currently use FR6989 to store four DATA arrays . each one is 30Kb, it is little chanllenge because it almost take up the whole 128kb space of the chip.
    While declaring with the code:
    #pragma PERSISTENT(MEM_DATA0)
    unsigned int MEM_DATA0[30][512] = { 0 };

    I am getting the error error #10099-D: program will not fit into available
    memory. placement with alignment fails for section ".text" size 0x2b12 .
    Available memory ranges:
    FLASH size: 0x1000 unused: 0xe56 max hole: 0xe56

  • Katie probable will not be able to answer your question for quite some time. Can someone else help?

    Your problem has nothing to do with FRAM. The situation is the same with FLASH device with a large amount of FLASH.

    With FRAM, write access is faster than a flash, thus it makes no sense to "FLASH" it. And there is no need to "Erase" it either. But we are stuck with those funny terms.
  • Hi old_cow_yellow,

    Thank you for the reply, I did some digging and guess I found the detail solution on e2e.ti.com/.../461905
    I have some more questions regarding to the solution:
    1) After divided the memory space into two parts, is there any way we could treat them as one again?

    2) For program convince, is it possible for me to redefine and split the memory space into small block like
    DATA0[30], DATA1[30], .etc

    Sorry for the questions, i am new to FRAM structure, any suggestion will be highly appreciated, thanks ahead.

**Attention** This is a public forum