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.

CC2530 DMA problem in HalFlashWrite

We rapidly use DMA controllers, channels 3 and 4. Sometimes transfer in HalFlashWrite is stopped and we can detect that HAL_DMA_CHECK_IRQ and then we repeat procedure.

But sometimes HAL_DMA_CHECK_IRQ return 1 despite the facts that last 4 byte are not written. So we change this function:

void HalFlashWrite(uint16 addr, uint8 *buf, uint16 cnt)
{

#if (defined HAL_DMA) && (HAL_DMA == TRUE)
   halDMADesc_t *ch = HAL_NV_DMA_GET_DESC();
   uint8 numTry = 0;
   uint8 i = 0;
   
   for ( numTry = 0; numTry < HAL_FLASH_WRITE_TRY; numTry++ )
   {
      // Clear DMA trigger event
      HAL_DMA_SET_SOURCE(ch, &dummySrc);
      HAL_DMA_SET_DEST(ch, &dummyDst);
      HAL_DMA_SET_VLEN(ch, HAL_DMA_VLEN_USE_LEN);
      HAL_DMA_SET_LEN(ch, (1));
      HAL_DMA_SET_WORD_SIZE(ch, HAL_DMA_WORDSIZE_BYTE);
      HAL_DMA_SET_TRIG_MODE(ch, HAL_DMA_TMODE_BLOCK);
      HAL_DMA_SET_TRIG_SRC(ch, HAL_DMA_TRIG_NONE);
      HAL_DMA_SET_SRC_INC(ch, HAL_DMA_SRCINC_0);
      HAL_DMA_SET_DST_INC(ch, HAL_DMA_DSTINC_0);
      // The DMA is to be polled and shall not issue an IRQ upon completion.
      HAL_DMA_SET_IRQ(ch, HAL_DMA_IRQMASK_DISABLE);
      HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS);
      HAL_DMA_SET_PRIORITY(ch, HAL_DMA_PRI_HIGH);
      HAL_DMA_CLEAR_IRQ(HAL_NV_DMA_CH);
      
      HAL_DMA_ARM_CH(HAL_NV_DMA_CH);
      
      for ( i = 0; i < 15; i++)
      {
         asm("NOP");
      }
      
      HAL_DMA_MAN_TRIGGER( HAL_NV_DMA_CH );
      
      while ( !HAL_DMA_CHECK_IRQ( HAL_NV_DMA_CH ));
      
      // Setting DMA transfer
      HAL_DMA_SET_SOURCE(ch, buf);
      HAL_DMA_SET_DEST(ch, &FWDATA);
      HAL_DMA_SET_VLEN(ch, HAL_DMA_VLEN_USE_LEN);
      HAL_DMA_SET_LEN(ch, (cnt * HAL_FLASH_WORD_SIZE));
      HAL_DMA_SET_WORD_SIZE(ch, HAL_DMA_WORDSIZE_BYTE);
      HAL_DMA_SET_TRIG_MODE(ch, HAL_DMA_TMODE_SINGLE);
      HAL_DMA_SET_TRIG_SRC(ch, HAL_DMA_TRIG_FLASH);
      HAL_DMA_SET_SRC_INC(ch, HAL_DMA_SRCINC_1);
      HAL_DMA_SET_DST_INC(ch, HAL_DMA_DSTINC_0);
      // The DMA is to be polled and shall not issue an IRQ upon completion.
      HAL_DMA_SET_IRQ(ch, HAL_DMA_IRQMASK_DISABLE);
      HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS);
      HAL_DMA_SET_PRIORITY(ch, HAL_DMA_PRI_HIGH);
      HAL_DMA_CLEAR_IRQ(HAL_NV_DMA_CH);
      
      FADDRL = (uint8)addr;
      FADDRH = (uint8)(addr >> 8);
      
      HAL_DMA_ARM_CH(HAL_NV_DMA_CH);
      
      while( !(HalAdcCheckVdd(VDD_MIN_NV)) );
#ifdef WATCHDOG
      WatchDogClear();
#endif
      while (FCTL & 0x80);  // Wait until flash controler is busy
      
      FCTL |= 0x02;         // Trigger the DMA writes.
      
      while (FCTL & 0x80);  // Wait until flash controler is busy
      
      // Check flash controller abort status bit. This bit is set when a write operation or page erase 
      // is aborted. An operation is aborted when the page accessed is locked. The abort bit is cleared
      // when a write or page erase is started.
      if( FCTL & 0x20 )
      {
         // Attempt to erase locked page - false state, restart device.
         SystemReset();
      }
      
      //if (!HAL_DMA_CH_ARMED( HAL_NV_DMA_CH ))
      //{
      
      if( HAL_DMA_CHECK_IRQ( HAL_NV_DMA_CH ))
      {
         uint8 tmp[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
         
         for ( numTry = 0; numTry < HAL_FLASH_WRITE_TRY; numTry++ )
         {
            HalFlashRead( ((uint32)addr * HAL_FLASH_WORD_SIZE) / HAL_FLASH_PAGE_SIZE, (((uint32)addr * HAL_FLASH_WORD_SIZE) % HAL_FLASH_PAGE_SIZE) + (cnt - 1) * HAL_FLASH_WORD_SIZE , tmp, HAL_FLASH_WORD_SIZE);
            
            uint8 j = 0;
            for (j = 0; j < HAL_FLASH_WORD_SIZE; j++ )
            {
               if( *(buf + (cnt - 1) * HAL_FLASH_WORD_SIZE + j ) != tmp[j] ) break;
            }
            
            if ( j == HAL_FLASH_WORD_SIZE )
            {
               break;
            }
            else
            {
               // Clear DMA trigger event
               HAL_DMA_SET_SOURCE(ch, &dummySrc);
               HAL_DMA_SET_DEST(ch, &dummyDst);
               HAL_DMA_SET_VLEN(ch, HAL_DMA_VLEN_USE_LEN);
               HAL_DMA_SET_LEN(ch, (1));
               HAL_DMA_SET_WORD_SIZE(ch, HAL_DMA_WORDSIZE_BYTE);
               HAL_DMA_SET_TRIG_MODE(ch, HAL_DMA_TMODE_BLOCK);
               HAL_DMA_SET_TRIG_SRC(ch, HAL_DMA_TRIG_NONE);
               HAL_DMA_SET_SRC_INC(ch, HAL_DMA_SRCINC_0);
               HAL_DMA_SET_DST_INC(ch, HAL_DMA_DSTINC_0);
               // The DMA is to be polled and shall not issue an IRQ upon completion.
               HAL_DMA_SET_IRQ(ch, HAL_DMA_IRQMASK_DISABLE);
               HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS);
               HAL_DMA_SET_PRIORITY(ch, HAL_DMA_PRI_HIGH);
               HAL_DMA_CLEAR_IRQ(HAL_NV_DMA_CH);
               
               HAL_DMA_ARM_CH(HAL_NV_DMA_CH);
               
               for ( i = 0; i < 15; i++)
               {
                  asm("NOP");
               }
               
               HAL_DMA_MAN_TRIGGER( HAL_NV_DMA_CH );
               
               while ( !HAL_DMA_CHECK_IRQ( HAL_NV_DMA_CH ));
               
               // Setting DMA transfer
               HAL_DMA_SET_SOURCE(ch, ( buf + (cnt - 1) * HAL_FLASH_WORD_SIZE ) );
               HAL_DMA_SET_DEST(ch, &FWDATA);
               HAL_DMA_SET_VLEN(ch, HAL_DMA_VLEN_USE_LEN);
               HAL_DMA_SET_LEN(ch, (1 * HAL_FLASH_WORD_SIZE));
               HAL_DMA_SET_WORD_SIZE(ch, HAL_DMA_WORDSIZE_BYTE);
               HAL_DMA_SET_TRIG_MODE(ch, HAL_DMA_TMODE_SINGLE);
               HAL_DMA_SET_TRIG_SRC(ch, HAL_DMA_TRIG_FLASH);
               HAL_DMA_SET_SRC_INC(ch, HAL_DMA_SRCINC_1);
               HAL_DMA_SET_DST_INC(ch, HAL_DMA_DSTINC_0);
               // The DMA is to be polled and shall not issue an IRQ upon completion.
               HAL_DMA_SET_IRQ(ch, HAL_DMA_IRQMASK_DISABLE);
               HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS);
               HAL_DMA_SET_PRIORITY(ch, HAL_DMA_PRI_HIGH);
               HAL_DMA_CLEAR_IRQ(HAL_NV_DMA_CH);
               
               FADDRL = (uint8)( addr + cnt -1 );
               FADDRH = (uint8)(( addr + cnt -1 ) >> 8);
               
               HAL_DMA_ARM_CH(HAL_NV_DMA_CH);
               
               while( !(HalAdcCheckVdd(VDD_MIN_NV)) );
#ifdef WATCHDOG
               WatchDogClear();
#endif
               while (FCTL & 0x80);  // Wait until flash controler is busy
               
               FCTL |= 0x02;         // Trigger the DMA writes.
               
               while (FCTL & 0x80);  // Wait until flash controler is busy
               
               // Check flash controller abort status bit. This bit is set when a write operation or page erase 
               // is aborted. An operation is aborted when the page accessed is locked. The abort bit is cleared
               // when a write or page erase is started.
               if( FCTL & 0x20 )
               {
                  // Attempt to erase locked page - false state, restart device.
                  SystemReset();
               }
            }
         }
         
         break;
      }
      
      //}
   }      
#endif
}

  • This can cause problems with network parameters save and update. 

    Situation:

    Device join network. After device power reset device is not in network because network parameters are not saved.

    Best regards, Aleksandar Majdak.

  • This is better version of HalFlashWrite. We did rapid testing with this version. With previous version we have problem with multiple zero write.

    #define HAL_FLASH_WRITE_TRY           8
    
    void HalFlashWrite(uint16 addr, uint8 *buf, uint16 cnt)
    {
    
    #if (defined HAL_DMA) && (HAL_DMA == TRUE)
       halDMADesc_t *ch = HAL_NV_DMA_GET_DESC();
       uint8 numTry = 0, byte_count = 0;
       uint8 i = 0;
       uint16 word_count = 0;
       uint8 tmp_word[HAL_FLASH_WORD_SIZE];
       bool tmp_word_write_active = FALSE; //for repeated word write
       
       // the following two variables are used for reading from flash   
       uint8 target_pg = ((uint32)addr * HAL_FLASH_WORD_SIZE) / HAL_FLASH_PAGE_SIZE;
       //the address withing the page in bytes
       uint16 target_addr = ((uint32)addr * HAL_FLASH_WORD_SIZE) % HAL_FLASH_PAGE_SIZE;
    
       for ( numTry = 0; numTry < HAL_FLASH_WRITE_TRY; numTry++ )
       {
          // Clear DMA trigger event
          HAL_DMA_SET_SOURCE(ch, &dummySrc);
          HAL_DMA_SET_DEST(ch, &dummyDst);
          HAL_DMA_SET_VLEN(ch, HAL_DMA_VLEN_USE_LEN);
          HAL_DMA_SET_LEN(ch, (1));
          HAL_DMA_SET_WORD_SIZE(ch, HAL_DMA_WORDSIZE_BYTE);
          HAL_DMA_SET_TRIG_MODE(ch, HAL_DMA_TMODE_BLOCK);
          HAL_DMA_SET_TRIG_SRC(ch, HAL_DMA_TRIG_NONE);
          HAL_DMA_SET_SRC_INC(ch, HAL_DMA_SRCINC_0);
          HAL_DMA_SET_DST_INC(ch, HAL_DMA_DSTINC_0);
          // The DMA is to be polled and shall not issue an IRQ upon completion.
          HAL_DMA_SET_IRQ(ch, HAL_DMA_IRQMASK_DISABLE);
          HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS);
          HAL_DMA_SET_PRIORITY(ch, HAL_DMA_PRI_HIGH);
          HAL_DMA_CLEAR_IRQ(HAL_NV_DMA_CH);
    
          HAL_DMA_ARM_CH(HAL_NV_DMA_CH);
    
          for ( i = 0; i < 15; i++)
          {
             asm("NOP");
          }
    
          HAL_DMA_MAN_TRIGGER( HAL_NV_DMA_CH );
    
          while ( !HAL_DMA_CHECK_IRQ( HAL_NV_DMA_CH ));
    
          // Setting DMA transfer
          if (tmp_word_write_active == TRUE)
          {
            //repeated word write - used if the previous
            //write failed somewhere inside a word to be written
            HAL_DMA_SET_SOURCE(ch, tmp_word);
            HAL_DMA_SET_LEN(ch, HAL_FLASH_WORD_SIZE);
          }
          else
          {
            HAL_DMA_SET_SOURCE(ch, buf + word_count * HAL_FLASH_WORD_SIZE);
            HAL_DMA_SET_LEN(ch, ((cnt - word_count) * HAL_FLASH_WORD_SIZE));
          }
    
          tmp_word_write_active = FALSE;
          
          HAL_DMA_SET_DEST(ch, &FWDATA);
          HAL_DMA_SET_VLEN(ch, HAL_DMA_VLEN_USE_LEN);
          HAL_DMA_SET_WORD_SIZE(ch, HAL_DMA_WORDSIZE_BYTE);
          HAL_DMA_SET_TRIG_MODE(ch, HAL_DMA_TMODE_SINGLE);
          HAL_DMA_SET_TRIG_SRC(ch, HAL_DMA_TRIG_FLASH);
          HAL_DMA_SET_SRC_INC(ch, HAL_DMA_SRCINC_1);
          HAL_DMA_SET_DST_INC(ch, HAL_DMA_DSTINC_0);
          // The DMA is to be polled and shall not issue an IRQ upon completion.
          HAL_DMA_SET_IRQ(ch, HAL_DMA_IRQMASK_DISABLE);
          HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS);
          HAL_DMA_SET_PRIORITY(ch, HAL_DMA_PRI_HIGH);
          HAL_DMA_CLEAR_IRQ(HAL_NV_DMA_CH);
    
          FADDRL = (uint8)(addr + word_count);
          FADDRH = (uint8)((addr + word_count) >> 8);
    
          HAL_DMA_ARM_CH(HAL_NV_DMA_CH);
    
          while( !(HalAdcCheckVdd(VDD_MIN_NV)) );
    #ifdef WATCHDOG
          WatchDogClear();
    #endif
          while (FCTL & 0x80);  // Wait until flash controler is busy
    
          FCTL |= 0x02;         // Trigger the DMA writes.
    
          while (FCTL & 0x80);  // Wait until flash controler is busy
    
          // Check flash controller abort status bit. This bit is set when a write operation or page erase
          // is aborted. An operation is aborted when the page accessed is locked. The abort bit is cleared
          // when a write or page erase is started.
          if( FCTL & 0x20 )
          {
             // Attempt to erase locked page - false state, restart device.
             SystemReset();
          }
    
          //check the correctness of written words
          for (; word_count < cnt; word_count++)
          {
            HalFlashRead( target_pg, target_addr + word_count * HAL_FLASH_WORD_SIZE, tmp_word, HAL_FLASH_WORD_SIZE);
            for (byte_count = 0; byte_count < HAL_FLASH_WORD_SIZE; byte_count++ )
            {
               uint8 byte_val = *(buf + word_count * HAL_FLASH_WORD_SIZE + byte_count );
               if ((tmp_word[byte_count] & ~byte_val) != 0)
               {
                    break;
               }
            }
            //mismatch found, break the loop!
            if (byte_count != HAL_FLASH_WORD_SIZE)
              break;
          }
          if (word_count == cnt) //everything ok, flash write is done
            break;
    
          //  the mismatch was found...
          if (numTry < (HAL_FLASH_WRITE_TRY - 1))
          {
            if (byte_count != 0)
            {
              //The write operation failed somewhere inside a word.
              //(i.e. some bytes were correctly written, but some not).
              //The content of buffer tmp_word[] will be used for the next write iteration.
    
              tmp_word_write_active = TRUE;
              
              //for correctly written bytes...
              //according to CC253x User Guide, section "6.2.2 Writing Multiple Times to a Word":
              // - A 0 must not be written more than two times to a single bit
              for (i = 0; i < byte_count; i++)
                tmp_word[i] = 0xff;  //this value will not change the state of written bytes in flash
    
              //for incorrectly written bytes...
              for (i = byte_count; i < HAL_FLASH_WORD_SIZE; i++)
                tmp_word[i] = buf[word_count * HAL_FLASH_WORD_SIZE + i];
            }
          }
       }
    #endif
    }