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.

CC1312R: PING PONG mode in SPICC26X2DMA.C

Part Number: CC1312R
Other Parts Discussed in Thread: CC1310, SYSCONFIG

Hi,

I have written my own spi driver largely based on SPICC26X2DMA.C. I've recently changed it to use Ping Pong mode rather than Basic mode. My understanding is that ping pong mode should be suitable for all transfers but it fails when the transfer size is a single byte. I have inserted an 'if' statement that will use Basic mode unless the data size is larger than the maximum 1024. This works.

So my questions:

1) Why won't ping pong mode work for a transfer size of one single byte? It may well do, but it fails for me where Basic mode works.

2) How does arbitration come into play? I'm only using dma for the spi and nothing else and so there will not be any channel switching based on priorities. I thought I could set UDMA_ARB_1024 but it doesn't work in any mode. I set it to UDMA_ARB_4 and it does, even for single byte transfers (basic mode). What am I missing?

Thanks,

Andy

  • Hi Andrew,

    I suspect it could be a question of you having time to re-arm the channel when you set it to 1 byte (as you always need to re-arm the "old" channel once finished). Arbitration is the maximum number of bytes it will arbitrate for. Basically you can view it as a "maximum amount of data I will be allowed to move before another channel could interrupt me" setting.

    4-8 bytes is a good number from the arbitration point of view as you could not fill the FIFO with 1024 bytes. 

  • Hi,

    Thanks for your response. It could be time to rearm but I am the slave and the master gives me time. I'm only transferring one byte, a CRC, and so only the primary structure is needed. The alternate structure is empty. It works fine on BASIC but fails on PING-PONG. The first 6 bytes do work on PING-PONG, it's the following single byte that fails which is a completely new transaction so only the primary structure is used. I'm suspecting that using PING-PONG twice in a row is somehow causing and issue, whereas Basic resets something. 

    I've had to put this code in but I don't understand why PING-PONG doesn't ping or pong where Basic works. If my data is > 1024 it works really well.

        if(transaction->rxBuf)
        {
            if(transaction->count <= MAX_DMA_TRANSFER_AMOUNT)
            {
               rxDmaTableEntry->ui32Control = (UDMA_MODE_BASIC | UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4); 
            }
            else
            {
                rxDmaTableEntry->ui32Control = (UDMA_MODE_PINGPONG | UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4); 
            }         
            rxDmaTableEntry->pvDstEndAddr = (void *) (transaction->rxBuf + framesQueued + transferAmt - 1); 
        }    
        else
        {
            if(transaction->count <= MAX_DMA_TRANSFER_AMOUNT)
            {
                rxDmaTableEntry->ui32Control = (UDMA_MODE_BASIC | UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_4); 
            }
            else
            {
                rxDmaTableEntry->ui32Control = (UDMA_MODE_PINGPONG | UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_4); 
            } 
            
            rxDmaTableEntry->pvDstEndAddr = (void *) &object->rxScratchBuf;
        }

    The question is whether PING-PONG works when data count is 1025, so one byte in the alternative structure, or 2049, one byte in the primary structure afetr its second load.

    Thanks,

    Andy

  • Hi Andy, 

    How do you set them up in this case? If you are doing "ping-pong" you would need to initialize both tables, at least they should make sense even if both are not armed. Basically, I would only really see the need to use ping-pong in a scenario where you need to receive/send more than 1024 bytes or when you need to switch out buffers more frequently (like move between two 1-10 byte buffers).

    This is however not the typical use-case as setting up the DMA in the first place for such a small amount of bytes is typically a waste of time due to all the DMA configuring you need to do (this is why the SPI driver has a threshold on this). 

    Also, as I forgot to ask you, could you elaborate on what you mean by fail and why you feel the need to write your own driver?

  • Hi,

    I have initialised all tables just in case it was that. Even for SSI1 which I don't use.

    ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi0TxControlTableEntry, UDMA_CHAN_SSI0_TX);
    ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi0RxControlTableEntry, UDMA_CHAN_SSI0_RX);
    ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi1TxControlTableEntry, UDMA_CHAN_SSI1_TX);
    ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi1RxControlTableEntry, UDMA_CHAN_SSI1_RX);
    ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi0TxAltControlTableEntry,
                                 (UDMA_CHAN_SSI0_TX | UDMA_ALT_SELECT));
    ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi0RxAltControlTableEntry,
                                 (UDMA_CHAN_SSI0_RX | UDMA_ALT_SELECT));
    ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi1TxAltControlTableEntry,
                                 (UDMA_CHAN_SSI1_TX | UDMA_ALT_SELECT));
    ALLOCATE_CONTROL_TABLE_ENTRY(dmaSpi1RxAltControlTableEntry,
                                 (UDMA_CHAN_SSI1_RX | UDMA_ALT_SELECT));

    The reason for DMA is that I might be transferring 1500 bytes backwards and forwards, but most of the time the messages are about 30 bytes going from slave to master. The reason for a single byte is that I do no know the length of message coming in and read a header to get the length. Then I set up a transaction for the remaining data which is usually just a CRC but can be data and a crc too. The baud is about 400kbs but the messages are only once per second. It's worked great on basic for some time. Now that I need to transfer >1K of data I need to use ping-pong. 

    I haven't investigated too much so far if it fails because it's a single byte, or if it fails after one ping-pong for some reason. I don't have control of the master side at the moment so it's difficult to debug this. By failure I mean that I receive no more bytes after the header, So I set up for one byte, but then nothing.

    I suppose it would be helpful to know that ping-pong should work with a single byte and arbitration set to 4. Or that my solution of switching between basic and ping-pong is viable. Still, it'd be nice to know what's going on for confidence of my software.

    I have had a lot of problems with SPI on the CC1312R. I have to support the master side and the CC1312R SSI doesn't support SPI in the way I need it to. It's a long time since I wrote the original driver so I'm a little rusty, but the only SPI mode I 'can' use on the CC1312R required a toggle of the CS line I think, between each byte. This is somewhat unusual and I've never seen it on any other processor (apart from CC1310). I believe it is the original Philips standard but most SPI's don't have this requirement. I have had to write my own driver to overcome this and have taken control of the CS myself with my own ISR's. If I remember, I'm having to use a different SPI mode than the master to get it to work. I've never put so many hours into trying to get SPI to work as I have on the CC1312/10. Another requirement was that the CS line going high from the master resets everything if there's an error so that any SPI issues will not last more than a single message- a fail safe strategy. Having said that, my driver is very similar to TI's but I've taken out any transaction queues and removed any need for SPI handles and the like. I originally developed on the CC1310 and the entire hardware set up was a horrible spaghetti mess which I decided to simplify a lot to save memory. I'm glad to see in the later stack releases this is much tidier and sysconfig seems sorted now which is great work.

    Many thanks,

    ANdy

  • Hi Andy,

    You could for sure do 1 byte transfers with any given arbitration size, it is the maximum number of bytes it will be allowed to send without interruption, not the required number. 

    For PIN-PONG to work you have to keep track of both buffers at all time, it easy to miss out on this in terms of timing (for example, what happens in the case where you fail to handle the first swap before the second happen?). This should not lock the hardware as such but there cold be SW logic depending on an interrupt that never happens due to the miss-alignment.Your approach is viable, it leaves out the complexity of swapping buffers when not needed.

    As for the CS toggle, the TI supplied driver always had the option for you to not use the HW CS signal and instead define your own SW based CS. In that case you could just pend on falling/rising edge to start or cancel the transaction. Note that using a SW CS means the SPI hardware remain active at all time so the HW could be receiving trash data if the clock signal is toggled while the driver is "idle". 

    It is hard for me to give any more feedback on the DMA side,logic traces of the failing communication and SPI register dumps would be helpful.