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.

Compiler/MSP430FR5969: unable to write to HIFRAM (upper memory)

Part Number: MSP430FR5969

Tool/software: TI C/C++ Compiler

I'm writing an application that will require nearly all of the resources available on the msp430fr5969.

I'm using the MSP430 GCC provided by Mitto Systems. When I compile with -mcode-region=none and -mdata-region=none, the application overflows the bss section and I get an error. When I compile with -mcode-region=either and -mdata-region=either, the application works fine. According to the GCC User Guide, this is because the compiler shifts the overflowing section to the upper memory region (HIFRAM) which starts at 0x10000.

However, my application consistently hangs during c startup (crt0_start or crt0_init_bss). During my investigation, I found that there are problems in writing to HIFRAM even though the linker sets the write attribute. From msp430fr5969.ld provided by the msp430-gcc package:

HIFRAM (rxw) : ORIGIN = 0x00010000, LENGTH = 0x00003FFF

To test the write functionality, I used the blink.c example (which has been working consistently) and added in some writes to HIFRAM. I found that certain memory locations do not seem to be writable and in some cases trying to write causes errors. Here are the writes I tried and the results of those writes:

*(volatile uint16_t *) (0x10000) = 0xFACE; // does not write
*(volatile uint16_t *) (0x10002) = 0xFACE; // does not write
//*(volatile uint16_t *) (0x11000) = 0xFACE; MSP430 Error :Could not single step device
*(volatile uint16_t *) (0x11F00) = 0xFACE; // writes normally
*(volatile uint16_t *) (0x11F02) = 0xFACE; // writes normally
*(volatile uint16_t *) (0x12B00) = 0xFACE; // does not write
*(volatile uint16_t *) (0x13FD2) = 0xFACE; // does not write

  • note: It occurred to me that a pointer to upper memory should require more than 16 bits because of the 20 bit addressing but replacing uint16_t with uint32_t changes nothing.

  • Hello,

    Could you check the MPU register setting to see if the HIFRAM is writable?

  • Hi,

    Have you added code to disable the watchdog before the CRT code initializes data and bss? Since you have a large program, the watchdog might fire before you reach main.

    See this thread for an example: https://e2e.ti.com/support/tools/ccs/f/81/p/496916/1797071#1797071

    (We're going to document this in the next version of the user guide since this a common problem)

    The problems writing to HIFRAM are probably a separate issue as Winter pointed out.

    Regards

  • I believe that the settings are correct. I tried two approaches and neither changed the results from my initial post. As the user guide states, the memory should be rwx accessible if the MPU is disabled:

    To test this I added it to the device setup provided in the blink.c example from msp430-gcc:

    int main(void) {
    WDTCTL = WDTPW | WDTHOLD;               // Stop watchdog timer
    
    P1DIR |= 0x01;                                        // Set P1.0 to output direction
    PM5CTL0 &= ~LOCKLPM5;                   // Disable the GPIO power-on default high-impedance mode
    
    
    MPUCTL0 = MPUPW | 0x0;
    
    .
    .
    .
    <HIFRAM write operations>
    .
    .
    .

    As expected, gdb shows that the value is 0x9600 meaning that the MPU is disabled and the full memory should therefore be rwx accessible. Unfortunately this did not change anything. I then tried to enable the MPU and set write enable for all memory segments:

    int main(void) {
    WDTCTL = WDTPW | WDTHOLD;               // Stop watchdog timer
    
    P1DIR |= 0x01;                                        // Set P1.0 to output direction
    PM5CTL0 &= ~LOCKLPM5;                   // Disable the GPIO power-on default high-impedance mode
    
    
    MPUCTL0 = MPUPW;
    MPUSAM = MPUSEG1WE | MPUSEG2WE | MPUSEG3WE;
    MPUCTL0 = MPUPW | MPUENA;
    
    .
    .
    .
    <HIFRAM write operations>
    .
    .
    .

    Similarly, this did not change anything. What am I missing?

  • The first thing the main function does is disable the watchdog so that shouldn't be a problem in this case. In my reply to Winter I posted a more complete version of the main function so it should hopefully be more clear now.

  • Change  IDE, and try agagin.  I think it 's more like a IDE setting issue.  

    Use IAR for MSP430 (Large Mode) or CCS.

  • EmbeddedMike said:

    The first thing the main function does is disable the watchdog so that shouldn't be a problem in this case. In my reply to Winter I posted a more complete version of the main function so it should hopefully be more clear now.

    But in your OP you say execution is "hanging" at startup (i.e. before main):
    EmbeddedMike said:
    However, my application consistently hangs during c startup (crt0_start or crt0_init_bss).
    What exact hanging behaviour do you observe for the original program? Does the program never reach main and the PC is always within a crt0 function whenever you pause execution? This is a smoking gun for the watchdog firing.
    The point is that initializing data and bss before main can take long enough that the watchdog will fire during this initialization. So having the watchdog disable code in main() is too late.
    I tried your FRAM code on an FR5969, with both MSP430-GCC, 7.3.2.154 and 8.2.0.52 it works without any additional MPU register modifications:
    #include <msp430.h>                                                                                     
    #include <stdint.h>                                                                                     
                                                                                                            
    int main(void) {                                                                                        
        WDTCTL = WDTPW | WDTHOLD;               // Stop watchdog timer                                      
        PM5CTL0 &= ~LOCKLPM5;                   // Disable the GPIO power-on default high-impedance mode    
                                                // to activate previously configured port settings          
        *(volatile uint16_t *) (0x10000) = 0xFACE;                                                          
        *(volatile uint16_t *) (0x10002) = 0xFACE;                                                          
        *(volatile uint16_t *) (0x11000) = 0xFACE;                                                          
        *(volatile uint16_t *) (0x11F00) = 0xFACE;                                                          
        *(volatile uint16_t *) (0x11F02) = 0xFACE;                                                          
        *(volatile uint16_t *) (0x12B00) = 0xFACE;                                                          
        *(volatile uint16_t *) (0x13FD2) = 0xFACE;                                                          
                                                                                                            
        P1DIR |= 0x01;                          // Set P1.0 to output direction                             
                                                                                                            
        for(;;) {                                                                                           
            volatile unsigned int i;            // volatile to prevent optimization                         
                                                                                                            
            P1OUT ^= 0x01;                      // Toggle P1.0 using exclusive-OR                           
                                                                                                            
            i = 10000;                          // SW Delay                                                 
            do i--;                                                                                         
            while(i != 0);                                                                                  
        }                                                                                                   
        return 0;                                                                                           
    }                                                                                                       

    (gdb) x 0x10000 
    0x10000:        0xfaceface 
    (gdb) x 0x11000 
    0x11000:        0xffffface 
    (gdb) x 0x11f00 
    0x11f00:        0xfaceface 
    (gdb) x 0x12b00 
    0x12b00:        0xffffface 
    (gdb) x 0x13fd2 
    0x13fd2:        0xffffface

    As someone else suggested, you could try starting a debug session in another IDE, this might fix any potentially problematic settings. I would recommend creating an MSP430-GCC project in CCS, building and running the example code, then trying again with your original application.
  • EmbeddedMike said:

    note: It occurred to me that a pointer to upper memory should require more than 16 bits because of the 20 bit addressing but replacing uint16_t with uint32_t changes nothing.

    Note that the way your FRAM write code behaves is actually like this:

    *((volatile uint16_t *) (0x10000))= 0xFACE;

    i.e. 0x10000 is the value of a pointer to a 16-bit volatile integer. No matter what you change uint16_t to, "(volatile uint16_t *)" is always a pointer so will be a 20-bit integer with -mlarge, and a 16-bit integer with -msmall.

    But it is essential that uint16_t matches the size of the value on the RHS of the assignment, or at least matches what you expect to write.

    For example (note we write to 0x10002 first):

    *((volatile uint16_t *) (0x10002))= 0xFACE;
    *((volatile uint16_t *) (0x10000))= 0xFACE;
    (gdb) x 0x10000
    0x10000:        0xfaceface

    But if you change the type of the value the pointer points to, then the resulting writes to memory will be affected:

    *((volatile uint32_t *) (0x10002))= 0xFACE;
    *((volatile uint32_t *) (0x10000))= 0xFACE;
    (gdb) x 0x10000
    0x10000:        0x0000face

    The second write actually told the compiler you want to write 0xFACE0000 to address 0x10000. So 0x10000 will contain "0xFACE", and 0x10002 will contain "0x0000".

  • Thanks for the feedback but if the settings are a problem then I'll have to fix my compiler flags. I transitioned my project away from CCS, so I now have a makefile that builds my project and then I use gdb to load, execute, and debug. I did some digging it seems that the compiler flag equivalent of the settings you highlighted are addressed here:

    My build currently uses the "-mlarge" flag so based on my understanding, I should have access to the full memory range using 20-bit pointers.

  • I'm not familiar with GCC. But you can take a look at the disassembly, you'll find it's not a difficult work to locate the cause.

     *((volatile uint16_t *) (0x10000) ) = 0xFACE;  

    MOVX.W     #0xFACE  ,&0x10000

    ___right    :)

    if the disassembly looks like:

    MOV.W   #0xFACE, &0x10000  (not extended instrcution,write /read address out of 64KB need extended instruction.)

    it must  be    :(     failed. and it will lead you to see if it's a complier issue.

  • Update:

    blink.c example:

    I am able to write to HIFRAM in the example application now. I was using the makefile that msp430-gcc includes in that example and overlooked the fact that it didn't include -mlarge by default. The note in your other comment about pointer vs data size is totally correct, I'm not sure what I was thinking there.

    my application:

    The issue is resolved, though I'm not totally sure why. There was a mismatch in my makefile where the compile flags used "-mcode-region=either" and "-mdata-region=either" while the linker flags used "-mcode-region=none" and "-mdata-region=none". To verify that fixing those flags was the solution, I changed them back to being mismatched... program still reaches main and runs fine. The only other change that I made on the iteration that finally worked was running the debug session using "layout asm" and then "stepi"-ing through until it reached main. I don't see how that could be the solution because I now have no issue running through gdb using assembly or source. I suspect that the problem was in the makefile but I haven't been able to reproduce the original error.

    I'm going to leave this unresolved for the time being because I can't conclusively say what fixed the issue. I really appreciate your help with investigating this issue.

  • Stepping through the assembly instructions in gdb has been very helpful, thanks for the suggestion!

  • Glad to hear that. And thanks for sharing your findings. 

**Attention** This is a public forum