MSP430FR5964: Writing 20-bit address into DMA register fails <1% of the time

Part Number: MSP430FR5964
Other Parts Discussed in Thread: MSP430FR5969, MSP430F6779

Tool/software:

I'm trying to assign the full 20-bits of the DMAxSA register. The user manual specifies, "Reading or writing bits 19-16 requires the use of extended instructions".
This is already confusing because the MSP430FR5XX_6XX DMA code example uses the __data20_write_long() intrinsic, which is implemented using two standard MOV.W instructions which are not part of the extended instructions (CPU430X). When I attempt to assign a 20-bit value to DMA0SA using __data20_write_long(&DMA0SA, 0x10000) I get a register value of 0x0.

After reading the MSP documentation further and this form discussion, I learned that i need to use __data16_write_addr() instead.
When compiled, this intrinsic actually uses the MOVX.A or POPX.A instructions which are extended address assignment instructions and I can now see that it successfully assigns the full 20-bit registers.
However, after keeping my application running for a few days, it still breaks because of a failed DMA address assignment operation. Once every few ten thousand DMA address assignments, the bits 19-16 are wrongfully cleared.

I have verified that both parameters to __data16_write_addr() are correct, and the DMA channel is disabled, so the issue is not with the application software itself. Furthermore, I have disabled interrupts as advised by the Compiler User Guide.

Replacing __data16_write_addr with my own assembly code does not change the situation.

    __asm(  "PUSHM.W    #2, R15\n"
            "POPX.A     0(R12)");

The only way that I can fix this issue is by reading back the DMA register value and reassign it in a loop.

#pragma optimize=none
void Dma_WriteAddress(uint32_t destination, uint32_t source)
{
    __disable_interrupt();

    do
    {
        __data16_write_addr(static_cast<uint16_t>(destination), source);
    }
    while(__data16_read_addr(static_cast<uint16_t>(destination)) != source);

    __enable_interrupt();
}

When I add logging to this function, I can see that the assignment fails only once every 5.000 to 50.000 times that this function is called.

Of course this solution is not optimal and should not be necessary. Could this be a hardware bug (it is not in the errata)? Am i missing something? Does anyone else have this problem?

This was tested on three MSP430FR5964 ICs, hardware revision C
Compiler: IAR MSP430 7.21.1
Data/Code model: Small

  • That is definitely very strange. It might be a hardware bug but the question is where? The intrinsic does several things and the one hazard that I see (in the GCC version at least) is that the operand is placed on the stack. Not normally a problem but what if an interrupt occurs and that messes up the stack somehow? It shouldn't of course.

      __data16_write_addr(DMA0SA, 0x10000);
        440c:       81 43 00 00     mov     #0,     0(r1)   ;r3 As==00
        4410:       91 43 02 00     mov     #1,     2(r1)   ;r3 As==01
    
    00004414 <.Loc.34.1>:
        4414:       1c 42 12 05     mov     &0x0512,r12     ;0x0512
        4418:       1d 42 14 05     mov     &0x0514,r13     ;0x0514
        441c:       00 18 ec 41     movx.a  @r1,    0(r12)  ;
        4420:       00 00 
    
    

    A better way to code that, for an assembly language programmer anyway, is:

    movx.a #0x10000,&0x512

  • I have changed the function so that the MOVX.A instruction copies 20 bits from the stack to the register. Then I check the most significant 4 bits without modifying the stack using a MOV.B instruction and stop the debugger if it does not match. Interrupts are disabled during these instructions.

    With the debugger I can clearly see that the most significant 4 bits are zero while all the bytes on the stack that were used by MOVX.A are correct(not zero), the DMA register value is wrong.

    It might be a hardware bug in the MOV/POPX.A instructions however, I can't imagine that this problem would not have been noticed since the 2021 release of this MCU.

    I'm worried that it is not a hardware bug and that there is another factor in play causing instability. Do you have any further suggestions?

  • I put together a simple test to run on a fr5969 that I have handy.

            push r6
            clr  r6
            mova #0x10000,r15
            mova #0x512,r14
    loop:   clrx.a @r14
            mova r15,0(r14)
            incx.a r6
            jz exit
            cmpx.a @r14,r15
            jz loop
    exit:  

    If r6 is zero on exit then it ran a million times, toggling the DMA register, without error.

    I have yet to see an error so it does not appear to be a problem with these (unique) device registers.

    There tends to be a lot of commonality across devices as designs get reused (which is why the same errata will appear in multiple devices) so this problem is unlikely to be unique to a single device.

  • I think I should add that I would like to see a complete example of the section of code that exhibits this problem. Not the C level code since I don't have your compiler, but the assembly code it outputs. Then I can see if I can replicate the problem on a msp430fr5969.

  • I have fixed the problem!
    I noticed that this issue never occurs when I disable all other DMA channels. With this information, I did some further research and found the following DMA4 Errata from the MSP430F6779 (released in April 2024).



    From the three possible workarounds, option 2 seems best for my situation. After setting DMARMWDIS in my MSP430FR5964, which probably uses the same DMA hardware, the problem is fixed.

    Thank you David Schultz for your feedback and interest in my post.

    To any TI employees watching, it might be helpful to retrospectively update the Errata's to include this information.

**Attention** This is a public forum