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.

Protocol for writing to flash memory from CPU

Other Parts Discussed in Thread: CC2530, CC2510, CC2531, REMOTI, CC2533

I am attempting to write to the CODE memory amidst my program, and the writes seem to be having no effect on the data stored in the flash, here is my code for writing:

 

uint8 writePairID(uint16 pairID)
{
   uint8 datas[2];
  
   //Break input down into bytes for fast transfer to FWDATA
   datas[0] = (uint8)(pairID & 0xFF);
   datas[1] = (uint8)((pairID >> 8) & 0xFF);
 
   //Disable interrupts
   EA = 0;
  
   //Erase Block 14 (starts @ 0x7000)
   FADDRH = (uint8)(14 << 1);
   FCTL |= ERASE;
   while(FCTL & BUSY);
  
   //Load address to write first byte of word to (0x7000)
   FADDRH = 0x70;
   FADDRL = 0x00;
  
   //Perform write of pair ID
   FCTL |= WRITE;
   FWDATA = datas[0];
   FWDATA = datas[1];
   FWDATA = 0x00;
   FWDATA = 0x00;
  
   //Wait for operation to finish
   while(FCTL & FULL);
   while(FCTL & WRITE);
  
   //Re-enable interrupts
   EA = 1;
  
   //Return 0 if operation fail
   if(FCTL & ABORT)
     return 0;
  
   else
     return 1;
}

 

I know the block is erased properly and when I attempt to read out the value I am attempting to store I read 0xFF, which is the "reset value"

 

Any helpful hints?

  • Hi,

    maybe it will be of some help to you. Here's my code snippet to write into Flash on msp430 2274 :

     

     

     

     

     

     

     

     

    void WriteDataSegD(uint8_t *content, uint8_t len)

    {

    char *Flash_ptr;

    // Flash pointer

     

     

    unsigned int  i;

     

     

    FCTL2 = FWKEY + FSSEL0 + FN1;

    // MCLK/3 for Flash Timing Generator

     

     

    Flash_ptr = (char *)0x1000;

    // Initialize Flash pointer

     

     

    FCTL3 = FWKEY;

    // Clear Lock bit

     

     

    FCTL1 = FWKEY + ERASE;

    // Set Erase bit

     

     

    *Flash_ptr = 0;

    // Dummy write to erase Flash seg

     

     

    FCTL1 = FWKEY + WRT;

    // Set WRT bit for write operation

     

     

    for

    {

    (i = 0; i < len; i++)

    *Flash_ptr++ = content[i];

    // Write value to flash

     

    }

     

    FCTL1 = FWKEY;

    // Clear WRT bit

     

     

    FCTL3 = FWKEY + LOCK;

    // Set LOCK bit

     

    }

  • I should add that I am running the code out of SRAM at all times, and the clock speed is 32MHz.

  • And I have declared

     

    __no_init const uint8 __code pairIDAddr[2048] @ 0x7000;

     

    as a global variable

  • It seems that your addressing is wrong when you try to write to the flash. When you erase the page, you use the correct address for accessing the page that starts at address 0x7000 in code space, but you need to use the same address for the start of the page when writing. Flash addressing, both for erasing and writing, is based on a 32-bit word size, so it is not byte addressed as is the case when reading the flash. This means that you need to modify the statement

       //Load address to write first byte of word to (0x7000)
       FADDRH = 0x70;
       FADDRL = 0x00;

    into

       //Load address to write first byte of word to (0x7000)
       FADDRH = 0x1C;
       FADDRL = 0x00;

     

  • In the user's guide it says that you only input the page number when doing an erase (and only in FADDRH[7:1]), when doing a write you use the address of the first byte you want to write to, as long as it is on a 4-byte boundary.  I tried using the page number instead of the physical address and it still did not work, nor did it write to the 0xC100 memory location in code space.  Are there any settings that need to be in place/avoided for a flash write to work, because I put an if statement right after I set the WRITE bit in the FCTL register to check to make sure it was actually being set, and it seems from the result of that statement that my setting of the WRITE bit is being ignored by the FCTL register, would there be any reason for this?

  • You need to wait for the ERASE flag to be cleared after the erase operation, otherwise setting WRITE to 1 will be ignored. So you need to add the statement   

       while(FCTL & ERASE);

    after while(FCTL & BUSY);

    I tested your code with this change and the correct address (0x1C00), and it works.

    The addressing for writing is as I said, cf. Section 6.1 of the user guide, as well as Section 6.2.1, which says:

    1. Set FADDRH:FADDRL to the start address. (This is the 16 MSBs of the 18-bit byte address).

    For the addressing when erasing, setting FADDRH[7:1] to the page number is equivalent to setting FADDRH:FADDRL equal to the first word-address of the page.

     

     

  • I made the changes you said, and something is still not working, I read out the data that I mean to have just written into Flash and it comes out as 0xFFFF (indicated by the printed value of the halLcdWrite(...) command), instead of 0xABAB like I passed into the function.  Are there necessary settings of the Flash control registers, clock registers or anything else?  Here are the code snippets, just in case I missed something.

     

    __no_init const uint8 __code pairIDAddr[2048] @ 0x7000;

    pairAddr = (uint16)writePairID(0xABAB);
       
        pairAddr = readPairID();
        pText[3] = (char)(((pairAddr >> 12) & 0x0F) + 55);
        pText[2] = (char)(((pairAddr >> 8) & 0x0F) + 55);
        pText[1] = (char)(((pairAddr >> 4) & 0x0F) + 55);
        pText[0] = (char)(((pairAddr) & 0x0F) + 55);
        pText[4] = 0;
        halLcdWriteLine(2, pText);

    uint8 writePairID(uint16 pairID)
    {
       uint8 datas[2];
       uint8 status;
       uint16 pData;
      
       status = 1;
      
       //Break input down into bytes for fast transfer to FWDATA
       datas[0] = (uint8)(pairID & 0xFF);
       datas[1] = (uint8)((pairID >> 8) & 0xFF);
     
       //Disable interrupts
       EA = 0;
      
       //Erase Block 14 (starts @ 0x7000)
       FADDRH = (uint8)(14 << 1);
       FCTL |= ERASE;
       while(FCTL & BUSY);
       while(FCTL & ERASE);
      
       //Load address to write first byte of word to (0x7000)
       FADDRH = 0x1C;
       FADDRL = 0x00;
      
       //Perform write of pair ID
       FCTL |= WRITE;
       FWDATA = datas[0];
       FWDATA = datas[1];
       FWDATA = 0x00;
       FWDATA = 0x00;
      
       //Wait for operation to finish
       while(FCTL & FULL);
       while(FCTL & WRITE);
      
       //Return 0 if operation fail
       pData = readPairID();
       if(pData != pairID)
         status = 0;
      
       //Re-enable interrupts
       EA = 1;
      
       return status;
    }

    uint16 readPairID(void)
    {
      uint16 pID;
     
      pID = pairIDAddr[1];
      pID <<= 8;
      pID += pairIDAddr[0];
     
      return pID;
    }

  • There are no special settings needed. You must make sure that the flash page that you are trying to modify is not locked, but probably, it is not (if so, you would not have been able to erase it either). It is important that the write routine is executed from RAM, but if I understood you correctly, it is. You could still double-check that you actually are executing from RAM.

    You could also try changing the declaration of pairIDAddr from const to volatile, since the contents will actually change. I don't think the compiler gives you a problem in this case, but in theory, it could (as it is now, the compiler could choose to read from pairIDAddr before the flash has been written to, since it is led to believe that the contents won't change).

  • All that is needed to run from RAM is to set:

     

    MEMCTR |= BIT3;

     

    correct?

     

    If so, then I am definitely running from RAM.  I attempted to set pairIDAddr to volatile, but the compiler said that all variables declared in __code needed to be constant.

  • You also need to have the actual routine placed in RAM and the program jumping to the correct address. When you do MEMCTR |= BIT3, the code memory space from 0x8000 to 0x9FFF is overlaid with the system RAM, cf. Figure 2-3 on page 24 of the user guide. If you execute from addresses outside that range, you are not executing from RAM. If so, you get the behavior that you observe.

    Unfortunately, as far as I know, IAR has no straight-forward way of placing code in RAM, so you need to do a bit of "hacking". An alternative is to use DMA to write to flash.

    The best declaration for pairIDAddr is

    __no_init const volatile uint8 __code pairIDAddr[2048] @ 0x7000;

    But it would probably work without volatile as well. Your problem is probably that you were in fact not executing code from RAM.

  • I used the code found in the cc1110, cc2510 examples for writing to flash from the CPU and tailored it to work on the cc2530 and in my code, however when it goes to run from ram it just seems to get lost, here is what I have (note the comment I added with <-- in the for loop):

     

    void runFunctionFromRam(void (*func)(void), uint16 __xdata *ramAddr,
                            uint16 funcLength)
    {
        /* flashFuncAddr is a pointer to where in flash the function is. */
        uint16 __code *flashFuncAddr = (uint16 __code *)(uint16)func;

        /* f is a function pointer to the address in RAM where the function will be
         * placed.
         */
        VFPTR f = (VFPTR)(uint16)ramAddr;

        /* Copy the function from flash to RAM. */
        uint16 i;
        for (i = 0; i < funcLength; i++)
        {
            ramAddr[i] = flashFuncAddr[i];                                                             <--- If I set a breakpoint here, it says the XDATA stack is filled to 100% (256 out of 256 bytes)
        }

        /* Run function from RAM. */
        (*f)();

        return;
    }

     

    PS I appreciate all your help and I hope we can get this resolved quickly!

  • There is a significant difference between the CC251x/CC111x and the CC2530 in terms of the memory map. The CC251x/CC111x have the same memory map in XDATA and CODE, which make it easier, and the largest variant has 32 KB flash, which makes it possible to have all the flash mapped in at the same time. Since the CC2530 has more flash, things get a bit trickier. First, the XDATA and CODE spaces are different. That can be handled. What makes it more tricky, is that when you map in the RAM as code, it will mask out some of the flash. If you have a large program, the compiler/linker may have placed code in that area which is called by your function running from RAM. If so, the call will go wrong. Even if you don't have any function calls inside your function, IAR may insert subroutine calls to implement C operations, and those subroutine calls will fail if the code was placed in the wrong location. You must also make sure that no interrupt routines makes calls to the area that is masked out. You must also avoid absolute jumps to within your function. That will be OK if your function is not too big.

    If your code size is below 32 KB, you are OK. If your code size could be larger than that, you may have to handle this situation. This can be done either by modifying the linker files so that system code is not placed in the first 8 KB of code bank 1-7, or by making sure that your function does not contain any subroutine calls. The latter requires that you write your code in assembly or keep it very simple and inspect the generated assembly code to verify that no calls are made.

    To make the function work, I changed it as follows:

    void runFunctionFromRam(void (*func)(void), uint16 __xdata *ramAddr,
                            uint16 funcLength)
    {
        /* flashFuncAddr is a pointer to where in flash the function is. */
        uint16 __code *flashFuncAddr = (uint16 __code *)(uint16)func;

        /* f is a function pointer to the address in RAM where the function will be
         * placed.
         */
        VFPTR f = (VFPTR)((uint16)(ramAddr)+0x8000);

        /* Copy the function from flash to RAM. */
        uint16 i;
        for (i = 0; i < funcLength; i++)
        {
            ramAddr[i] = flashFuncAddr[i];
        }

        /* Run function from RAM. */
        MEMCTR |= 0x08;
        (*f)();
        MEMCTR &= ~0x08;

        return;
    }

    This will work as long as your total program is less than 32 KB. Note that you will not be able to single step or place breakpoints normally inside the function that runs from RAM. You may single step from inside the Disassembler window in IAR only.

    I did not see any problem with XDATA stack overflow. You need to make sure that you have allocated a buffer to hold the RAM code. I have enclosed an example file. It has some things hard coded (such as the data to write to the flash), but I am sure that you will be able to modify that.

    In total, running C code from RAM on CC2530 is a bit tricky, and you need to be aware that increased code size or new compiler versions may make functional code stop working. You may find it easier to use DMA to write to the flash; then you don't need to run code from RAM.

    Flash_Test.zip
  • The (ramAddr) + 0x8000 is what was doing me in, I got it working now though, and I can't applaud your persistence in helping me enough, this is the reason that my company tries as hard as we can to use TI parts, especially MCUs and DSPs.  So again, thank you.

  • Hi, I'm working with the CC2531 device and I've a problem writing in flash memory. I have been triying to use the code that you suggested, but I can't check in what direction we are writing. I check the XDATA, but I don't find the 2 caracters that  we just write. Might you tell me where in memory I must look?

    Thank you so much.

  • Hi Juan,

    You may want to look at some software examples, e.g. RemoTI. This is a simple stack that includes flash access software. There are other examples, but I'm most familiar with this one, the flash access software is the same though.

    Please install RemoTI, then find the flash access software in the folder any of the \Components\haltarget\ folders, e.g. C:\Texas Instruments\RemoTI-CC253xDK-1.3.1\Components\hal\target\CC2533EB_NPI

    hal_flash.c

    It can be a little tricky to look at flash with IAR, it may be easier to read out a hex file with Smart RF Flash Programmer. If you do want to look at the flash with IAR then you must make sure the correct bank is mapped to the upper XDATA area, then look at the upper XDATA area in Memory View. You map banks with the MEMCTR.XBANK[2:0] bits, please check out the register description on page 37 of CC253x/40/41 User's Guide.

  • Hi Torbjorn,

    I'm still having the same problem I told you yesterday. I'm using your code, but I don`t know if I'm using it properly.

    If I understand your code well, it must save 0xAB 0xAB  0x00 0x00 in the 0x1C00 memory position, But I can't see this change in the memory map (XDATA).

    I attached you an image where you can see what I'm talking about.

    you can also find the image in this link

    https://4ikim.4ikim.com/EnlaceDirecto.aspx?link=06e88306-637a-49db-83ee-703301678ff6

    Best regards

  • Hi Juan,

    Please take a minute to read about flash writing in www.ti.com/lit/swru191.

    FADDRH and FADDRL constitute a 16 bit word address in flash. A word is 4 bytes. So, address 0x1C00 is 0x7000 in byte address. To see this in XDATA memory view bank 0 must be mapped to upper 32K of XDATA, as I explained in the previous post. You see, XDATA mapping for the lower 32K is a bit mixed. First xKB are RAM, then SRAM. Last 1K is the Information Page. The information page is physically after the last available flash page, e.g. page 97 when there are 96 pages total.

    When Bank0 has been mapped to the upper 32K of XDATA you can see it in XDATA memory view by adding 0x8000 to the address within the Bank. So, 0x7000 in Bank0, mapped to upper 32K XDATA, can be seen at address 0xF000.

    In fact, an easier way to look at the FADDRH:FADDRL is that FADDRH designates the flash page (for CC2533 flash page size is ). Divide this by 32 and you get the Bank number. Or, get bank number from FADDRH[7:5].

  • Hi Torbjorn, 

    It was my problem, thank you so much for your help. I'm trying to verify your answer but I don't find where to do it.

    Thank you very much once again for your help

    Best regards

  • Hi Torbjon,

    I have an other question, I am writing in the flash memory, in the 0xF800 position, but when I want to rewrite this position of the flash memory I can not do it. I need to rewrite this position because it is important for my devide perfonmance.

    Thank you very much again for your help

    Best regards