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.

How do you use DMA to REPEATEDLY transfer from RAM to FLASH?

Following on from my previous post (http://e2e.ti.com/forums/t/8586.aspx) I have now modified my example to repeatedly transfer an array of data to consecutive sections of FLASH, except it doesn't work.

The code is:

#include "msp430xG46x.h"

#define MEM_START  0x10000UL
#define MEM_END    (MEM_START + 10 * 0x20)

void main(void)
{
  unsigned short inData[16];
  volatile unsigned int i, j;
  unsigned long dataPos = MEM_START;
  unsigned long FarPtr  = MEM_START;

  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT

  FLL_CTL0 |= XCAP14PF;                     // Configure xtal load capacitance

  // LFXT1 startup delay
  do
  {
    IFG1 &= ~OFIFG;                         // Clear OSCFault flag
    for (i = 0x47FF; i > 0; i--);           // Time for flag to set
  }
  while (IFG1 & OFIFG);                     // OSCFault flag still set?
  __disable_interrupt();                    // Disable interrupts

  // create some dummy data
  for (i = 0; i < 16; i++)
  {
     inData[i] = i + 1;
  }

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

  do
  {
    FCTL1 = FWKEY + ERASE;
    __data20_write_char(FarPtr, 0x00); // Dummy write to erase segment
    FarPtr += 0x0200;                  // Point to next segment
  } while (FarPtr < MEM_END);

  // Setup Timer_B for DMA to FLASH
  TBCTL = TBSSEL_2;                     // Use SMCLK as Timer_B source
  TBR = 0;
  TBCCR0 = 100;                         // Initialize TBCCR0
  TBCCR1 = 50;                          // Initialize TBCCR1
  TBCCTL1 = OUTMOD_7;                   // Reset OUT1 on EQU1, set on EQU0

  for (i = 0; i < 10; i++)
  { 
     // set up DMA
     __data16_write_addr((unsigned short)&DMA0SA, (unsigned int)inData);
                                           // Start block address
     __data16_write_addr((unsigned short)&DMA0DA, dataPos);
                                           // Destination block address
     DMA0SZ = 0x0010;                      // Block size
     DMA0CTL = DMADT_0 + DMASRCINCR_3 + DMADSTINCR_3 + DMAEN + DMAIE;
                                           // single xfer, inc s & d,
                                           // enable DMA & interrupt
     DMACTL0 = DMA0TSEL_8;                 // TBCCR0 triggers DMA0  
     
     FCTL1 = FWKEY + WRT;                  // Enable Flash write for copying
     TBCTL |= MC0;                         // Start Timer_B in UP mode
                                           // (counts up to TBCL0)

     // Activate LPM during DMA transfer, wake-up when finished
     __bis_SR_register(LPM0_bits + GIE);   // Enable interrupts, enter LPM0
     __disable_interrupt();                // Disable interrupts

     TBCTL &= ~MC0;                        // Disable Timer_B
     FCTL1 = FWKEY;                        // Disable Flash write

     dataPos += 0x20;

     for (j = 0; j < 16; j++)
     {
        inData[j] += 16;
     }
  }

  // Deactivate Flash memory write access
  FCTL3 = FWKEY + LOCK;                 // Lock Flash memory
}


//------------------------------------------------------------------------------
// DMA interrupt handler
//------------------------------------------------------------------------------
#pragma vector=DMA_VECTOR
__interrupt void DMA_ISR(void)
{
  DMA0CTL &= ~DMAIFG;                       // Clear DMA0 interrupt flag
  __bic_SR_register_on_exit(LPM0_bits);     // Exit LPM0 on reti
}
//------------------------------------------------------------------------------

 

In this example I've added code to repeat the transfer, moving the destination memory location by 32 (0x20) bytes each time. Again, the RAM to RAM transfer using Timer B works fine, but the RAM to FLASH version only transfers one data set then stops in low power mode as if an interrupt is never fired (the RAM to RAM version fires interrupts without any problem).

Any ideas anyone? Obviously something to do with FLASH memory, but I don't know what.

 

Thanks!

Derek

  • I do not think you (or anyone) can do it.

    The Flash controller needs to be set up for write operation. Once set up for byte/word write,  you can only write one byte/word. After that, you need to set it up again before next write.

    There is a also a block-write mode for 64 bytes. But you need to check and wait for the BUSY bit between consecutive writes and the DMA cannot do that.

  • I'm not sure that much of this is true... In my previous post I have an example of DMA transferring 16 short integers from RAM to Flash using a single DMA transfer controlled by a timer, which works. I have now modified that code to see whether I can repeatedly copy an array of data into consecutive and contiguous sections of Flash memory, but the Flash controller won't play ball.

    BTW, setting up the Flash controller every time doesn't work either. I've just tried it.

    There is a practical application for this example. If a process takes a buffer of data, processes it and puts the results in a second buffer for future processing, it is conceivable that this second buffer is stored in Flash memory. I can't believe that no-one has done this and that TI doesn't have a working example somewhere.

    I have looked at the Experimenter Board Voice Recorder example and that is repeatable, but only after leaving the record function and returning to main, and then waiting for the record button to be pressed, which fires an interrupt that re-runs the record routine. Does this mean I need a delay before I start using the Flash memory again?

    Has anyone mentioned that Flash memory is really slow... [:)]

  • Well, the obvious work around is to manually copy the data rather than use DMA.

    Example code follows.

    #include "msp430xG46x.h"

    #define MEM_START  0x10000UL
    #define MEM_END    (MEM_START + 10 * 0x20)

    void main(void)
    {
      unsigned short inData[16];
      volatile unsigned int i, j;
      unsigned long dataPos = MEM_START;
      unsigned long FarPtr  = MEM_START;

      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT

      FLL_CTL0 |= XCAP14PF;                     // Configure xtal load capacitance

      // LFXT1 startup delay
      do
      {
        IFG1 &= ~OFIFG;                         // Clear OSCFault flag
        for (i = 0x47FF; i > 0; i--);           // Time for flag to set
      }
      while (IFG1 & OFIFG);                     // OSCFault flag still set?
      __disable_interrupt();                    // Disable interrupts

      // create some dummy data
      for (i = 0; i < 16; i++)
      {
         inData[i] = i + 1;
      }

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

      // erase result memory
      FCTL1 = FWKEY + ERASE;
      do
      {
        __data20_write_char(FarPtr, 0x00); // Dummy write to erase segment
        FarPtr += 0x0200;                  // Point to next segment
      } while (FarPtr < MEM_END);

      FCTL1 = FWKEY + WRT;                 // Enable Flash write for copying

      for (i = 0; i < 10; i++)
      { 
         for (j = 0; j < 16; j++, dataPos += 2)
         {
            __data20_write_short(dataPos, inData[j]);
         }   

         for (j = 0; j < 16; j++)
         {
            inData[j] += 16;
         }    
      }
     
      // Deactivate Flash memory write access
      FCTL1 = FWKEY;                        // Disable Flash write
      FCTL3 = FWKEY + LOCK;                 // Lock Flash memory
    }

    This is a simple solution, however the User Guide gives an impression that using DMA with Flash is the simplest way to write to Flash. My experience is completely the opposite.

    The MSP430x4xx Family User's Guide (SLAU056H) states this (Section 10.2.14):

    MSP430 devices with an integrated DMA controller can automatically move
    data to the Flash memory. DMA transfers are done without CPU intervention
    and independent of any low-power modes. The DMA controller performs the
    move of the data word/byte to the Flash. The write timing control is done by
    the Flash controller.Write transfers to the Flash memory succeed if the Flash
    controller set--up is prior to the DMA transfer and if the Flash is not busy.

    The fact that the DMA controller can automatically move data into Flash memory should mean it's easy to set up and the DMA and Flash controllers work together to manage the transfers, but this does not seem to be the case. Unfortunately the guide does not include any examples and I can find none on this site.

    Are there any DMA/Flash gurus out there who can share their wisdom with us mere mortals who are struggling to use the controllers to transfer data "automatically", or has TI left a blatant bug in its silicon?

  • It says: "Write transfers to the Flash memory succeed if the Flash controller set--up is prior to the DMA transfer and if the Flash is not busy."

    Maybe "if the Flash is not busy" is the key. So, instead of using CPU to check and wait, you can time the DMA cycles so that it FCTL is not busy.

  • Yes, I think you're right. This is why I can transfer the first set of data but not the second; I think the DMA controller is permanently waiting for the Flash memory to be free (ie, to not be busy).

    Unfortunately I do not know of a way to test the Flash memory to check whether it is busy or free, or to force it into a "free" state. I've tried locking the Flash memory and setting up the controller again, but this did nothing. I've tried adding a delay, to no avail. I cannot find any documentation, application note or example that covers this hence why I posted this on the forum.

    I would like the forum (or the wiki) to include examples of typical uses of the peripherals so that if one person has solved a problem the solution is available for all (this is why I try to post full solutions to my examples rather than "Thanks, it work!"). These helpful code snippets will save time, become a valuable resource and prevent others re-inventing the wheel every time they face the same problem.

    Derek

  • The data-sheet says the byte/work write time for Flash is 30 cycles of fFTG (which is set up in FCTL2). I presume this means the Flash controller will be not-busy after that. You may want to pace the DMA transfer at a rate slower than 30 cycles of fFTG.

  • By the way (you probably already know this), if your code is in RAM (or a different bank of Flash), you can poll the FCTL-BUSY bit with CPU. If you code is in Flash (in the same bank), you cannot fatch the next instruction from Flash.  But TI has a trick -- FCTL will generate 0x3FFF which will fool the CPU to wait.

**Attention** This is a public forum