MSP430FR6007: crc_table function

Part Number: MSP430FR6007


Tool/software:

Hi,

In my linke command file I do this:

.text             : {} crc_table (crc_table_text, algorithm=CRC32_PRIME)>> FRAM2 | FRAM
so text is in FRAM and FRAM2. in the crc_table_text is just the start address
structure looks like this:
typedef struct crc_table
{
   uint16_t         rec_size;    /* 8-bit addressable units */
   uint16_t         num_recs;
   CRC_RECORD       recs[1];
} CRC_TABLE
is this number in the recs array related to the number of Sections I have? in this case FRAM and FRAM2?
Unfortunately I use the dma for calculation and in this case no debugger is possible to use. My routine to calculate generalized over and amount of bytes is working so far. this is tested. currently it is not working when I do this in my code.
CRC_TABLE const *p_crc_table_text = &crc_table_text;
CRC_TABLE const *p_crc_table_isr_text = &crc_table_isr_text;

const uint32_t calculatedCrcText = crc_calculation_is3309((uint8_t *)p_crc_table_text->recs[0].addr,
                                                            p_crc_table_text->recs[0].size, 0x00000000, false);
const uint32_t calculatedCrcIsrText = crc_calculation_is3309((uint8_t *)p_crc_table_isr_text->recs[0].addr,
                                                               p_crc_table_isr_text->recs[0].size, 0x00000000, false);
Is the dma usable also in FRAM2 for crc calculation?
  • In general, you can't assume that there is only one CRC_TABLE entry for .text:

    1) The linker is capable of generating alignment "holes" such that some bytes are not predictable once loaded. For this case the linker generates multiple entries.

    2) It's possible for .text to be split between FLASH and FLASH2. (This is improbable when giving preference to FLASH2, since it's so big, but it is possible.)

    You should just step through the entries num_recs times.

    -------

    DMASA/DMADA are 32 bits, so they can accommodate addresses in FLASH2. To make sure the compiler gets the pointer sizes right, you should use the intrinsic "__data20_write_long" as illustrated in example msp430fr60x7_dma_01.c here:

    https://dev.ti.com/tirex/explore/node?node=A__AJThw7eQLuzxGR4hTWjRfQ__msp430ware__IOGqZri__LATEST

  • I currently use the DMA_setSrcAddress from the TI Driver Lib in my Dma Feed policy. As far as I can see this implementation is than wrong and could be the trouble?

    void DMA_setSrcAddress(uint8_t channelSelect, uint32_t srcAddress, uint16_t directionSelect)
    {
      // Set the Source Address
      __data16_write_addr((unsigned short)(DMA_BASE + channelSelect + OFS_DMA0SA), srcAddress);
    
      // Reset bits before setting them
      HWREG16(DMA_BASE + channelSelect + OFS_DMA0CTL) &= ~(DMASRCINCR_3);
      HWREG16(DMA_BASE + channelSelect + OFS_DMA0CTL) |= directionSelect;
    }
    
    void DMA_setDstAddress(uint8_t channelSelect, uint32_t dstAddress, uint16_t directionSelect)
    {
      // Set the Destination Address
      __data16_write_addr((unsigned short)(DMA_BASE + channelSelect + OFS_DMA0DA), dstAddress);
    
      // Reset bits before setting them
      HWREG16(DMA_BASE + channelSelect + OFS_DMA0CTL) &= ~(DMADSTINCR_3);
      HWREG16(DMA_BASE + channelSelect + OFS_DMA0CTL) |= (directionSelect << 2);
    }

  • Based on C compiler User Guide (SLAU132Y) Table 6-5, the "16" vs "20" refers to the width of the target (register) address, not the size of the datum (32 bits in both cases). On the FR6007 [Ref datasheet (SLASEV3A) Table 9-64], these registers start at about 0x0500, i.e. 16-bit addressable.

    So while the data20 variant is perhaps a "good habit", I expect the data16 variants work fine.

  • Ok tried it. Unfortunately this was not the solution. Second problem I could not debug it because with dma debugger does not work. 

    Problem is still that my routine works so far. I added also a test with crc_table functionality in my component this also works. 

    The only difference I see is that in my product the FRAM is filled (more than 64KB) and in the stand alone component test not.

  • How do you tell that something is wrong? (Incorrect result? DMA stops in the middle?)

    I'm not familiar with the IS3309 CRC. Are you writing by bytes or words?

  • saved checksums from the linker script of ti are not the same to my calculated ones. Im writing byt by byte. you can ignore the type of crc it is a normal add functionality. What could be different 

      CRC_TABLE const *p_crc_table_text = &crc_table_text;
      CRC_TABLE const *p_crc_table_isr_text = &crc_table_isr_text;
    
      const uint32_t calculatedCrcText = crc_calc((uint8_t *)p_crc_table_text->recs[0].addr,
                                                                p_crc_table_text->recs[0].size, 0x00000000, false);
     
      if (calculatedCrcText != p_crc_table_text->recs[0].crc_value)
      {
        // Error
      }

    I did some test in isolated crc component

    some input

    in general, as mentioned, the calculation worked. Unfortunately when I create a huge array to simulate a filled FRAM it seems that the linker do not place it propperly

    I created an array:

    __attribute__((section(".const"), used)) const std::array<uint8_t, 0x33000> fill_array{0x01}

    in the linker command file I placed this:

        .const            : {} crc_table (crc_table_const, algorithm=CRC32_PRIME)>> FRAM | FRAM2  /* Constant data                     */

    In the map file:

    it is there and in the listing

    you can see that it is on the higher FRAM but on the target you see that it has not two entries for high and low part. I really do not get it and even do not know how to simulate it to get the error. The calculated checksums fit (you can see in the pictures below) so calculation is correct so far. Should have somethinig to do with the high and low memory area in my opinion.

  • Your screenshot of the crc_tables indicates num_recs=2 but (as the debugger sees it) each has only one element. This is presumably from the "CRC_RECORD       recs[1];" declaration in crc_tbl.h. You may have to resort to using the Memory Browser to see the other entry.

    ---------------

    I've used the DMA on high-FRAM on an FR5994 before (I needed a large input buffer), and it worked as expected. That said, there are sporadic reports of the DMAxSA/DA not getting all the bits (mixtures of memory models and Errata), and the usual recommendation (as Mr Lehman states here) is to always use data20_write_long. (I don't know why the DriverLib people didn't.) It might be useful to examine the DMA0 registers (CTL/SA/DA/SZ) before and after the DMA operation. 

  • so my assumption to see at lease 2 records for this linker command line is correct yes?

    .text             : {} crc_table (crc_table_text, algorithm=CRC32_PRIME)>> FRAM2 | FRAM

    I would expect one for FRAM and one for FRAM2

  • You would see that in the case where FRAM2 filled up, which is not unlikely in this case (since you filled most of FRAM2 with that array).

    Your screenshot says num_recs=2 (for both .text and .const), so apparently that's what happened.

    If you have any functions declared as ISRs (#pragma vector=) they're put in something called ".text:_isr" which is put in low-FRAM. That area is (I think) not included in the .text CRC.

  • so my crc dma feed method looks like this but this kills the system 

    I have a lot of questions on this module and im really frustrated. 

     static void feed(const uint8_t *src, size_t length, const uint8_t *dst)
      {
        // Add size validation (your original 0x5FF is fine)
        if (length > 0xFFFF || length == 0)
        {
          return; // Invalid size
        }
    
        DMA_initParam cfg = {
            DMA_CHANNEL_0,                // channelSelect
            DMA_TRANSFER_BLOCK, // transferModeSelect
            length,                       // transferSize
            DMA_TRIGGERSOURCE_0,          // triggerSourceSelect
            DMA_SIZE_SRCBYTE_DSTBYTE,     // transferUnitSelect
            DMA_TRIGGER_RISINGEDGE        // triggerTypeSelect
        };
    
        DMA_init(&cfg);
    
        // Set direction bits
        HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) &= ~(DMASRCINCR | DMADSTINCR);
        HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) |= DMASRCINCR_3;
    
        // Try without masking first
        __data20_write_long((uintptr_t)&DMA0SA, (uintptr_t)src);
        __data20_write_long((uintptr_t)&DMA0DA, (uintptr_t)dst);
    
        // Clear flag and enable
        DMA0CTL &= ~DMAIFG;
        DMA_enableTransfers(DMA_CHANNEL_0);
        DMA0CTL |= DMAREQ;
        
        // Add timeout to debug
        uint32_t timeout = 10000;
        while (!(DMA0CTL & DMAIFG) && --timeout)
        {
          __no_operation();
        }
    
        if (timeout == 0)
        {
          // Timeout occurred - add breakpoint here for debugging
          __no_operation();
        }
    
        DMA_disableTransfers(DMA_CHANNEL_0);
      }

    I want to calculate the crc over the code. Software is too slow so dma should be used.

    from crc driver i give a chunk of memory (because dma is limited to 16bit length) to the feed function. with my settings I transfer this block of memory byte by byte to the crc destination address in one block. imagine the dst is the crc target register. FRom settings point the dma should just increment the src address not the destination address. When I add the line  DMA0CTL |= DMAREQ the system crashes. I do not expect an interrupt. Is this line even needed? but when I remove this line I get a timeout from the memory movement. do you have a working example on using the dma with the crc module to calculate the crc in the 20bit memory area?

  • When I add the line  DMA0CTL |= DMAREQ the system crashes. I do not expect an interrupt. Is this line even needed?

    Of course it is required. You have to trigger the DMA or nothing happens.

    Hmmm. I was just looking at the GCC version of __data20_write_long() and something is very wrong with it.

    #define _data20_write_long(addr,src) \
    ({ \
            unsigned int __tmp; \
            unsigned long __addr = addr; \
            __asm__ __volatile__ ( \
            "movx.a %1, %0 \n\t" \
            "mov.w  %L2, 0(%0) \n\t" \
            "mov.w  %H2, 2(%0)" \
            : "=&r"((unsigned int) __tmp) \
            : "m"((unsigned long) __addr), "ri"((long) src) \
            ); \
    })
    
    

    After the expected movx.a instruction, there are two word writes. Which completely destroys the intent and zeros out the upper word when used with DMA address registers. In the past there was a __data20_write_addr() that didn't have this redundant and faulty write.

  • Oh, I suspect that the system crash is a power up clear (PUC) from accessing vacant peripheral space.

    Lots of ways to check this. Examining the generated code being the simplest.

  • test_dma.zip

    i made a tiny test with the driverlib. I can see that with --data-model large the controller hangs up in frequency setup. when my dma setup is included, when i exclude the dma setupt it does not stuck in the crystal setup. It seems to work with -data-model restricted. but this is just a gues, I did not try all combinations.

  • Additional question. currently I do a block transfer with the dma to the crc module. is this Ok? doest the crc is able to handle this? or is DMA_TRANSFER_SINGLE?

  • Single mode requires a trigger for each transfer. Which would make this slower than not using DMA.

    Burst block mode would be better if you needed to slip some CPU activity in while the transfer happens. Since you are blocking till it completes, that isn't the case.

    Oh, I looked at dma.obj in that pile of code you included. I assume this code is from DriverLib. What I see should work fine for setting the source and destination DMA addresses.

  • hangs up in frequency setup.

    Usually you clear LOCKLPM5 early. Not after configuring the LFXT pins and hoping that the general rule on GPIO configuration doesn't apply to the LFXT.

  • no its not from driverlib. there is no real example on using the dma in combination with crc module in the the higher address area. It is my example to show you that something strange happens when I run the example (even with  PM5CTL0 &= ~LOCKLPM5; moved to Top (below the PJ port init) the crystal was never initialized). If I remove my dma code, it runs to the end.

    Dont know whats happening here.  

  • I don't have a device with CRC32 handy but I did run this code on the fr5969 with no trouble:

    int main(void)
    {
      DMA0CTL = DMASBDB | DMASRCINCR_3 | DMADT_1;
      DMA0DA = &CRCDIRB_H;
      __data16_write_addr(&DMA0SA, 0x10000L);
      DMA0SZ = 0xffff;
      DMA0CTL |= DMAEN;
      CRCINIRES = 0xffff;
      DMA0CTL |= DMAREQ;
      
      while (1);
    }
    

  • with the 16 bit address range it also works on my side. the problem is with the 20bit address range

  • with the 16 bit address range it also works on my side. the problem is with the 20bit address range

    Break it up into smaller parts.

  • I have to jump to higher addresses (20bit) with the dma independent from the chunk size, and it looks like, that this is the problem.

  • It shouldn't be. At least with GCC I get reasonable code and the expected behaviour.

    $ /usr/ti/gcc/bin/msp430-elf-objdump -S a.out

      __data16_write_addr(&DMA0SA, 0x10000L);
        4468:       81 43 00 00     mov     #0,     0(r1)   ;r3 As==00
        446c:       91 43 02 00     mov     #1,     2(r1)   ;r3 As==01
    
    00004470 <.Loc.17.1>:
        4470:       8c 00 12 05     mova    #1298,  r12     ;0x00512
        4474:       00 18 ec 41     movx.a  @r1,    0(r12)  ;
        4478:       00 00 
    
    

  • unfortunately this does not help me. First I use CGT, second I use __data20_wirte. I also get correct assembly with __data20_write but the code stucks anyway when I add: DMA0CTL |= DMAREQ; (done after the clock init). when this line is uncommented. the crystal setup hangs in the while loop for whatever reason. 

    uint16_t volatile resetReason = 0;
    int main()
    {
      resetReason = SYSRSTIV;
      resetReason = 0;
    
      WDTCTL = WDTPW | WDTHOLD; // Stop the watchdog timer
    
      // To enable LFXT, the PSEL bits associated with the crystal pins must be set.   - RM 3.2
      // Setting the PSEL bit causes the LFXIN and LFXOUT ports to be configured for LFXT operation.
      // LFXIN = PJ.4
      PJSEL0 |= BIT4 | BIT5;    // Set bits 4 and 5 in PJSEL0 to enable primary function
      PJSEL1 &= ~(BIT4 | BIT5); // Clear bits 4 and 5 in PJSEL1 to select primary function
    
    
      PM5CTL0 &= ~LOCKLPM5; // Clear the LOCKLPM5 bit to enable GPIO functionality
      // Unlock CS registers
      CSCTL0 = 0xA500; // Password to unlock clock system registers
      // Set DCO to 8 MHz
      CSCTL1 = 0x000C;
      // Configure clock sources
      CSCTL2 = SELM__DCOCLK | SELS__DCOCLK | SELA__LFXTCLK;
      // MCLK = DCOCLK, SMCLK = DCOCLK, ACLK = LFXTCLK
      // Set dividers for MCLK, SMCLK, and ACLK
      CSCTL3 = DIVM__1 | DIVS__1 | DIVA__1;
      // MCLK = DCO / 1, SMCLK = DCO / 1, ACLK = LFXT / 1
      // Enable LFXT with drive strength 3
      CSCTL4 &= ~LFXTOFF;    // Enable LFXT
      CSCTL4 |= LFXTDRIVE_3; // Set LFXT drive strength to 3
      // Clear fault flags and wait for LFXT to stabilize
      do
      {
        CSCTL5 &= ~LFXTOFFG; // Clear LFXT fault flag
        SFRIFG1 &= ~OFIFG;   // Clear oscillator fault flag
      } while (SFRIFG1 & OFIFG);
      // Lock CS registers
      CSCTL0_H = 0x01; // Lock clock system registers
    
      uint16_t length = 0xFFFF;
      DMA_initParam cfg = {
          DMA_CHANNEL_0,            // channelSelect
          DMA_TRANSFER_BLOCK,       // transferModeSelect
          length,                   // transferSize
          DMA_TRIGGERSOURCE_0,      // triggerSourceSelect
          DMA_SIZE_SRCBYTE_DSTBYTE, // transferUnitSelect
          DMA_TRIGGER_RISINGEDGE    // triggerTypeSelect
      };
    
      uintptr_t src = (uintptr_t)(0x10000);
      uintptr_t dst = (uintptr_t)(&CRC32DIRBB0);
    
      DMA_init(&cfg);
    
      HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) &= ~(DMASRCINCR | DMADSTINCR);
      HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) |= DMASRCINCR_3;
    
      __data20_write_long((uintptr_t)&DMA0SA, src);
      __data20_write_long((uintptr_t)&DMA0DA, dst);
    
      DMA0CTL &= ~DMAIFG;
      DMA_enableTransfers(DMA_CHANNEL_0);
    
      //DMA0CTL |= DMAREQ;
    
      uint32_t timeout = 10000;
      while (!(DMA0CTL & DMAIFG) && --timeout)
      {
        __no_operation();
      }
      /**/
    
      while (1)
      {
      };
    }

  • So you are saying that when code is added to set DMAREQ, the clock initialization fails long before it gets executed.

    Which is impossible unless the compiler is misbehaving in a unique way.

  • When you step through is the device still hanging? Do you have a loop to wait for the crystal to start up?

  • Hi Luke, yes you see the loop in the example above, I wait for the crystal.

  • Can you output the low frequency clock? Your DMAREQ line is after the clock check and I don't see why the compiler would be adjusting the DMA code to be before the clock check

  • Hi Luke, 

    let me unlock the thread again and post the latest feedback from our customer:

    I just noticed somethin else: When using the DMA function in the lower addresses (16Bit) it works fine, but when using the higher FRAM area the device stops working. 

    I also found this note from the datasheet: 

    But let me show what I'm doing: 

    template <size_t VMaxChunkSize = 0xFFFF>
    struct CRC32DataFeedMockWithDma
    {
      static constexpr auto maxChunkSize()
      {
        return VMaxChunkSize;
      }
    
      static struct Internals m_internals;
    
      static void feed(const uint8_t *src, size_t length, const uint8_t *dst)
      {
        // Safety check: DMA transfer size is uint16_t, max 65535
        if (length > 0xFFFF || length == 0)
        {
          return; // or assert/abort
        }
        
        DMA_initParam cfg = {
            DMA_CHANNEL_0,                 // channelSelect
            DMA_TRANSFER_BLOCK,            // transferModeSelect
            static_cast<uint16_t>(length), // transferSize
            DMA_TRIGGERSOURCE_0,           // triggerSourceSelect
            DMA_SIZE_SRCBYTE_DSTBYTE,      // transferUnitSelect
            DMA_TRIGGER_RISINGEDGE         // triggerTypeSelect
        };
    
        DMA_init(&cfg);
    
        // Use proper 20-bit address handling
        // Convert pointers to 32-bit first to avoid truncation
        uint32_t volatile src_addr = reinterpret_cast<uint32_t>(src);
        uint32_t volatile dst_addr = reinterpret_cast<uint32_t>(dst);
    
        // Set the Source Address (20-bit)
        __data20_write_long((uint32_t)&DMA0SA, src_addr & 0xFFFFF);
    
        // Reset bits before setting them
        HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) &= ~(DMASRCINCR_3);
        HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) |= DMA_DIRECTION_INCREMENT;
    
        // Set the Destination Address (20-bit)
        __data20_write_long((uint32_t)&DMA0DA, dst_addr & 0xFFFFF);
    
        HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) &= ~(DMADSTINCR_3);
        HWREG16(DMA_BASE + DMA_CHANNEL_0 + OFS_DMA0CTL) |= (DMA_DIRECTION_UNCHANGED << 2);
    
        // Clear DMA interrupt flag and start transfer
        DMA0CTL &= ~DMAIFG;
        DMA_enableTransfers(DMA_CHANNEL_0);
        DMA_startTransfer(DMA_CHANNEL_0);
    
        // Poll for completion
        while (!(DMA0CTL & DMAIFG))
        {
          __no_operation();
        }
    
        DMA_disableTransfers(DMA_CHANNEL_0);
      }
    };
    
    

    Do you have any idea what could be wrong here? 

    Thank you! 

  •     // Set the Destination Address (20-bit)
        __data20_write_long((uint32_t)&DMA0DA, dst_addr & 0xFFFFF);

    This uses two 16 bit writes. See  SLAU132.

**Attention** This is a public forum