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.

Flash write to 20-bit address space

Other Parts Discussed in Thread: MSP430FG4618, MSP430F249, MSP430F5419A

Hi, i want to write my data in 20-bit flash memory address. Here's my code,

  unsigned int *Flash_ptr;                          // Flash pointer
  Flash_ptr = (unsigned int *) 0xfb00;              // Initialize Flash pointer

  FCTL2 = FWKEY + FSSEL1 + FN1;             // SMCLK/3 = ~333kHz
  FCTL3 = FWKEY;                            // Unlock Flash memory for write

  FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation
  *Flash_ptr = output;                      // Write value to flash

  FCTL1 = FWKEY;                            // Disable Flash write
  FCTL3 = FWKEY + LOCK;                     // Lock Flash memory

With this code i can write my output value into 0xfb00 memory address. If i change this address with 0x10000, an error occurs. How can i modify my code for writing data into 20-bit memory address?

Thanks.

Ferhat

  • Pointers on MSPs are usually 16 bit. This means the maximum address they can point to is 0xffff.

    Whether pointers with more than 16 bit are available, depends on your compiler.

    Personally, I use hand-written assembly code to load a processor register with a 32 bit (long int) value (my 'pointer'), and access memory indirectly though it:

    unsigned long int myPointer;
    asm("dint");
    asm("nop");
    asm("mova &myPointer, r15");
    asm("movx #0, @r15");
    asm("eint");

    This way I can keep using the smaller and faster 16 bit-only addressing and still access the upper flash area if I need to (e.g. for storing data)

    It is necessary to disable interrupts before using 20 bit register commands because if compiled in 16 bit mode, ISRs will only save 16 bit of the registers they use. (storing 20 bit on stack requires one more cycle and 2 more bytes each)

  • Ferhat,

    Check out this post for C examples on writing to extended Flash memory:

    http://e2e.ti.com/support/microcontrollers/msp43016-bit_ultra-low_power_mcus/f/166/p/18877/72969.aspx#72969 

    Regards,

    Priya

  • Priya Thanigai said:
    Check out this post for C examples on writing to extended Flash memory:


    The examples mentioned in this thread do nothing different than what he's doing anyway. So they won't help. The difference is that the examples are compiled in a project environment which has 20 bit data pointers support while he obviously has only 16 bit data pointers. If he does not need or want 20 bit data pointers (this can cause a lot of problems with existing code and libraries and doubles the space requirements for all pointers and also for any stack operation), the examples will give the same error he's gettign already.
    The only solution then is to write the few high memory accesses he needs in assembly language, either as a separate assembly file or as inline assembly where needed.

  • Jens-Michael Gross said:

    the examples will give the same error he's getting already.

    Gross, can you explain assembly code a little more?

  • ferhat said:
    Gross, can you explain assembly code a little more?

    oh, well, that's a wide field :)

    Compared to C, assembly code is much close to the processors inner workings. Every code line matches one instruction the processor is executing.
    Basically, the C compiler translates teh C code into a chain of assembly code which 'do the job'. There's some additional management information, so the linker knows how to link the compiled data, but that's another field.

    A typical assembly instruction is

    MOV #0, R15

    This instruction moves the 'immediate' (= taken as written here) value '0' into the processor register R15. R15 is one of 16 registers in which most of the work is done. R0 is the program counter (PC), R1 the stack pointer (SP), R2 the status register (SR), R3 has a special use and the rest are just 'variables'.

    Normally, these registers are 16 bit sized.

    Accessing memory is done either directly or indirectly. For example
    MOV #0, &myGlobalIntVar
    will write the value 0 into the memory location of myGlobalIntVar. The full name would be 'MOV.W', as you're accessing word values, but the W can be omitted). For writing a single byte to a char variable, the instruction is MOV.B instead. And indirect addressing involves one of the registers holding the destination address:
    MOV #myGlobalIntVar, R15
    MOV #0, @R15

    will first load the memory address of myGlobalIntVar into R15 and then write 0 into the memory addres which R15 is pointing to. This works also with an additional offset:
    MOV #0, 2(R15)
    will write 0 to two bytes above the address of myGlobalIntVar. This is required for arrays or for accessing the upper word of long int variables.

    Still, R15 is 16 bit only and the MOV instruction will also only handle byte or word values and 16 bit address pointers. So when the linker tries to replace myGlobalIntVar with its actual address, it can only fill a 16 bit value into the instruction. If myGlobalIntVar has an address above 64k, this will fail (17 or more significant bits in the address)

    To address locations above 64k, more than 16 bits are required. To maintain compatibility, you cannot just use 0xffff(R15) to address an area above 64k (as it is done on the PC with the HMA and the A20 gate). It will wrap around to 0 adn still access the lower 64k.
    Instead, TI has introduced an extension to the MSPs instruction set, the MSP430X instruction set extension.
    First, all registers except the SR (R2) are now 20 bit. The old instructions, however, will only use 16 bits of them and work as they always did.
    There are new instructions, marked with the 'a' and 'x' extension. 'a' stands for 'adress' and marks instructions which use address-space values (20 bit) instead of 16 bit. Most of them, except MOVA, only work with registers. You can add two 20 bit registers, compare them and such. MOVA, however, allows loading a 20 bit immediate value (such as an address) into a register, read from or write to a 20 bit address. Where a read or write from or to memory results in 2 16 bit accesses, one for the lower 16 bit and one for the upper 4 bit. So you can read a 20 bit value from a long variable into a register. You cannot, however, write a word or byte to a 20 bit address. The advantage of the 'a' instructions is that they are small. Only the MOVA instruction requires additional words for holding the upper 4 bits of source or destination.

    For all other jobs involving a 20 bit address, but a byte/word/20bit value, there are the 'x' instructions, such as MOVX. Their main drawback is that they always need an additional instruction word (prefix) which holds additional informations, such as the additional 4 bits for source or destination address etc. It is necessary since the number of possible instructions is limited (after all they are identified by a 16 bit value, the processor opcode, which also contains the information about the type of the parameters). This makes the X instructions execute at least 1 cycle slower than their non-X-pendant. Even if you don't do anything that couldn't be done with the original iinstruction set.

    All X instructions use full 20 bit of the registers when used as memory address (or if a direct memory address is used). Tehy can, however, move byte or word data from and to these addresses and not only 20 bit values. So MOVX.A #0x1ffff, R15 will move a 20 bit (address) value into R15, while MOVX.W #0xffff,R15 will only move a word value. If the destination is a memory location, two 16 bit writes will be done. However, only 20 siginificant bits are read/written. Finally, MOVX.W #0, @R15 will write a word value of 0 into the memory location pointed to by the 20 bit value in R15.

    To sum up:

    MOVA &longvar, R15 moves the lower 20 bits of the global variable longvar into R15, and MOVX #0, @r15 writes '0' into the memory location pointed to by R15. This effectively writes '0' into the address pointed to by the long variable longvar.

    One thing at last: if your compiler supports only the standard commandset, or is configured to not use 20 bit data structures, it will generate code for ISRs and function that saves only the lower 16 bit of the registers on stack. Since (from the compiler view) only 16 bit registers are used, and the standard commands are faster and require less stack space.
    If you , however, use 20 bit registers for temporary access to the extended address range, any ISR will probably clear the upper 4 bits, so you need to disable ISRs before workign with 20 bit instructions. And place a NOP behind the instruction clearing GIE, as an IRQ might still happen up to one instruction after clearing GIE.
    You'll need to block interrupts anyway if you're writing to flash.

  • Priya Thanigai said:

    Ferhat,

    Check out this post for C examples on writing to extended Flash memory:

    http://e2e.ti.com/support/microcontrollers/msp43016-bit_ultra-low_power_mcus/f/166/p/18877/72969.aspx#72969 

    Regards,

    Priya

     

    Hi Priya,

    I checked msp430x54x_flashwrite_04 C example in slac166n. I tried using flash pointer long type like flashwrite_04 example but it didn't work. Should i change iar compiler settings for recording data into 20-bit address space? Example code is similar with 16-bit flash record except long pointer type. How can i record data into 20-bit address space? (I'm using msp430fg4618 exp. board)

    Thanks.

    Ferhat

  • Jens-Michael Gross said:

    Pointers on MSPs are usually 16 bit. This means the maximum address they can point to is 0xffff.

    Whether pointers with more than 16 bit are available, depends on your compiler.

    Personally, I use hand-written assembly code to load a processor register with a 32 bit (long int) value (my 'pointer'), and access memory indirectly though it:

    unsigned long int myPointer;
    asm("dint");
    asm("nop");
    asm("mova &myPointer, r15");
    asm("movx #0, @r15");
    asm("eint");

    This way I can keep using the smaller and faster 16 bit-only addressing and still access the upper flash area if I need to (e.g. for storing data)

    It is necessary to disable interrupts before using 20 bit register commands because if compiled in 16 bit mode, ISRs will only save 16 bit of the registers they use. (storing 20 bit on stack requires one more cycle and 2 more bytes each)

     

    Would it help to push the value of r15 on the stack before modifying it? 

     

  • Alexandru Stefan said:
    Would it help to push the value of r15 on the stack before modifying it? 

    For what purpose? For the access itself, it is still necessary to load the 20 bit value into a register, and when at this point an ISR triggers...

  • Jens-Michael Gross said:

    For what purpose? For the access itself, it is still necessary to load the 20 bit value into a register, and when at this point an ISR triggers...

    I was thinking that a previous value stored in r15 (directly by the programmer or through the code generated by the compiler) is lost. 

  • Alexandru Stefan said:
    I was thinking that a previous value stored in r15 (directly by the programmer or through the code generated by the compiler) is lost. 


    You're right. If this code is directly inlined, the previous content of R15 will indeed be lost. As is the previous state of the GIE bit (so SR shoudl be saved and restored too)

    The code was meant as a example code snippet for adjusting it into your project.

    If you put it into a function, then (depending on the compiler), some registers are already considered clobbered. This usually includes R12..R15, which hold the passed function parameters (if any) and return the functions return value (if any). The compiler doesn't assume their content to survive a funciton call, nor does any function save and restore them (except for ISRs).
    On mspgcc, there is a way to tell the compiler that a certain register has been clobbered during an ASM statement. And a way to let the compiler pick a proper register rather than using a fixed one.

    It looks like
    asm ("mov #0, r15":::(r15));
    or
    asm ("mov #0, %0":"=r"(name):);

    Of course, the whole code only makes sense if you do something with the result. In the second line, the compiler ensures that a variable 'name' (which you have to define) will get the result from a register it picks as suitable for the instruction. The variable, if it is never used after this, will most likely be discarded totally, or only created as a temporary register variable for this single isntruction.
    I don't think there is a similar way to 'help' the compiler in CCS or IAR. Did I mention that I love mspgcc? :)

  • In my opinion Jens gave us the best implementation, but here is mine, I put the function write code  in the memory area where I attempt to write:

    void Save_log() @ "LOG_FUNC" ;

    void Save_log() @ "LOG_FUNC"
    {
      uint16_t data_size;
      uint16_t n;
      char *Flash_ptr;                          // Flash pointer
     
      data_size = FLASH_PAGE_SIZE;
      ptr_data = (char *) data_ram;
     
      Flash_ptr = (char *) (data_flash);       // Direccion inicial del segmento
      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(n = 0; n < data_size; n++)
      {
        *Flash_ptr++ = (uint8_t)n;
      }
        
      FCTL1 = FWKEY;   
      FCTL3 = FWKEY + LOCK; 
    }

    you will need define in linker file (for example):

    -Z(CONST)LOG_FUNC=10000-10200



  • Depending on data model (small, large), char * Flash_ptr will be a 16 b it pointer. So '*Flash_ptr = 0' or *'Flash_ptr++ = n' will nly write to the lower 64k.
    The main purpose of using MSP430X assembly commands was to create a 20 bit data pointer, so the write can cover the full 1MB addressing range of the MSP430X cores.

    However, in the meantime the compilers have added specific intrinsics for a 20 bit write or read (taking a long values as address parameter). This makes the assembly workaround obsolete. See the compiler documentation.


  • Here is my code that uses the intrinsics to read and write 20 bit address space.


    uint32_t ReadFlashU32(MemoryAddress_t address) {
    return __data20_read_long(address);
    }

    typedef void (*func_ptr)(void);

    int16_t FlashReadArray(MemoryAddress_t address, uint8_t* dst, uint32_t size) {
    int i;
    for(i=0;i<size;i++) {
    dst[i] = __data20_read_char(address);
    address++;
    }
    return ERRNO_SUCCESS;
    }

    int16_t FlashVerifyArray(MemoryAddress_t address, const uint8_t* src, uint32_t size) {
    int i;
    for(i=0;i<size;i++) {
    uint8_t b = __data20_read_char(address);
    if (b != src[i]) return ERRNO_FLASH_VERIFY_FAILED;
    address++;
    }
    return ERRNO_SUCCESS;
    }



    int16_t FlashWriteArray(MemoryAddress_t address, const uint8_t* src, uint32_t size) {
    if (address & 3 || size & 3) {
    return ERRNO_FLASH_BUFFER_ODD_SIZE;
    }

    int i;
    FCTL3 = FWKEY;
    FCTL1 = FWKEY+WRT;

    for(i=0;i<size;i++) {
    __data20_write_char(address, src[i]);
    address++;
    }
    FCTL1 = FWKEY; // Clear WRT bit
    FCTL3 = FWKEY+LOCK; // Set LOCK bit

    return 0;
    }

    int16_t FlashErasePage(MemoryAddress_t page, uint32_t password) {
    if (password != FLASH_PASSWORD) return -1;

    uint32_t* ptr = (uint32_t*)page;
    FCTL3 = FWKEY; // Clear Lock bit
    FCTL1 = FWKEY+ERASE; // Set Erase bit
    __data20_write_char(page,0); // Dummy write to erase Flash seg
    FCTL1 = FWKEY; // Clear WRT bit

    if ((uint32_t)ptr == 0x0000FE00) {
    uint8_t resetVector[2] = { 0x00, 0x5C};
    // vector table was just erased... write the reset vector back as soon as possible!
    FlashWriteArray(0x0000FFFE,resetVector,2);
    }
    return 0;
    }
  • Hi Jens-Michael,

    Thanks for the post. It was very helpful.

    I have a boot which stores the address of application bank in regvar R4. Code was for MSP430F249.

    After I upgraded micro-controller from MSP430F249 to MSP430F5419A. I used asm code mentioned in above post to move 20-bit address of application bank into R4.

    Above code works absolutely fine in Debug mode. But same doesn't work in Release mode.

    I cross verified map files and lst files, Both modes generate the same map files but after I flash the release hex, dis-assembly shows there is change in instruction which is mentioned in asm().

    Below are the details,

    IAR MSP430 Workbench.

    MSP430F5419A uC.

    Code snippet is,

     currentExecutionBankTemp = bootInterface.firstBank;

     asm("dint");

     asm("nop");

     asm("movx.a currentExecutionBankTemp, r5");

     //asm("movx #0, @r15");

     asm("eint");

    __regvar __no_init uint32 __data20 currentExecutionBank @ __R4;

    __no_init uint32 __data20 currentExecutionBankTemp; // Temp var to hold currentBank

    __no_init uint8 __data20 newFirmwareStatus @ "FLASH_END";

    Below are few snapshots of release and debug Mode.

    Release Mode,

    Debug Mode,

    Thanks

    Bhushan 

  • Both codes seem to be identical (the binary data is the same) even though disassembly interprets the addresses differently. Are you sure you updated the MSP after recompiling?
    What about ISRs? Do you use large data model for your code? If not, ISRs will only save the lower 16 bit of registers they use (and R4 is likely to be used). This would destroy (clear the upper 4 bit of) your manually loaded 20 bit value in R4 if an ISR is called before you perform the write. You should put the loading of R4 into the atomic block (with itnerrutps disabled). Best you load R4 right before the movx.a.

**Attention** This is a public forum