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.

Custom protocol with MIBSPI and DMA

Other Parts Discussed in Thread: HALCOGEN, RM48L952

Hello, 

I created a protocol to exchange messages with a slave. The thing is that the slave sends varying size packages (from 3 bytes to 1kB), which doesn't leave me with much of a choice for using DMA with it. The only choice I thought I had was to receive the protocol's overhead until the package's size, and then reconfigure the DMA for such transfer. 

Thinking now (I'm not at work right now, just thinking about it), especially about chaining DMA requests. Here's my line of thought. 

 - Every package has its own type (first byte indicates type of message being received, so I can allocate the correct buffer)

- Maybe I could associate DMA with 1 byte transfer and associate the correct buffer to the request line;

- If it's an error, change the DMA size to receive to 2 more bytes (CRC);

- After it's done, modify the DMA size to 1 again, so it can receive the next message;

 (All of this would have to be done by software)

- If it's not an error, I would manually change the destination buffer and size to receive 2 more bytes (size);

- After the size is received it would trigger another DMA request, which would write the size to the first request line's size so it can start receiving the data + crc;

- After this happens I would return everything to its original state. 

It would llok something like this

MIBSPI -> DMA1 1 byte

DMA1 change size to 2 bytes to receive to its correct buffer

DMA1 triggers DMA2 to change DMA1 block size

MIBSPI -> DMA1 transfer data

After transfer is finished, restore to initial state

In my mind I can kinda see it working properly (maybe I'm too tired). What do you think? Maybe I'm overthinking it and the way I first implemented it is better? It's up to discussion here. I'd really like to hear your opinions (members and TI employees). 

  • Another possibility is that I could create a 1kB buffer and always receive the maximum size, then when transfer is complete it would read the content and relocate it with another DMA request line to the correct buffer. 

    I think this one could also be viable, especially if the DMA requests fromt he master don't depend on interrupts to start/stop, but it could cause a problem with the slave, it could generate unwanted interrupts, which would be understood as incorrect message types and then message errors would end up being sent. 

  • Thanks for using the Hercules Forum! One of our engineers has been notified of this thread. 

  • Sorry to keep bothering you. I just have some ideas that I'd like to bounce off (hopefully I'm using this expression correctly) one of the engineers in the forum.

    When DMA chaining:

     - I would trigger the SPI DMA to receive a 3 bytes (containing the message header and 2 bytes of file size);

     - When this transfer ends, it will trigger another DMA transfer. Only this time it will transfer the 2 bytes containing the size of the message and write it to the number of elements/frames in the SPI DMA (the one that received the first 3 bytes)

     - This in turn would trigger a new DMA transfer with the first DMA channel which would receive the rest of the message.

    Is this possible? Would it be better if I triggered the transfers manually? I'm thinking that maybe this would loop indefinitely (certainly there must be a hardware restriction that I don't know of) and wouldn't work as expected. So maybe I could do this:

     - I would trigger the SPI DMA to receive a 3 bytes (containing the message header and 2 bytes of file size);

     - When this transfer ends, it will trigger another DMA transfer. Only this time it will transfer the 2 bytes containing the size of the message and write it to the number of elements/frames in the SPI DMA (the one that received the first 3 bytes)

     - Here I'd wait for the second DMA channel to finish the transfer and I'd do a new transfer with the first DAM channel.

    Sorry to keep bothering you, but I really have a lot of questions

  • Pablo,

    What you describe sounds like a good fit for the MibSPI sequencer,  which would be perhaps simpler than using DMA.  Or you could simplify the DMA programming by using the Mib unit.

    Have you thought about using the Mib unit for this task / do you have one available?

  • Hello, and thank you for your reply.


    I can't really remember hy I decide to use SPI instead of MIBSPI (I think that maybe it was because I thought that the MIBSPI couldn't interface with SPI devices).

    Is there any example code available for this? What's the biggest transfer I can make in "one go"?

  • Pablo,

    The MibSPI and SPI share the same 'SPI' logic.  It's just that the MibSPI has an extra 'mutlibuffer unit' (which is what Mib is supposed to be short for).  The multibuffer unit allows you to program sequences of SPI transfers by writing each transfer to an entry in RAM.  There is a transmit location that contains both the data to transmit as well as some 'control' bits and a receive location that gets the receive data and status flags after the transfer completes.   There are also 'transfer groups' where you link multiple entries together;  since sometimes you might have to send a few bytes of command and then read a few bytes of data back in order to complete a higher level SPI transaction.   These transfer groups can be triggered in various ways - by software, by an internal time base counter (for periodic polling of a SPI peripheral) or by cross triggering from a perhipheral like RTI or HET.

    There is an example that's probably on the simple end of what MibSPI can do in HalCoGen's example folder, under RM48x, and 'example_mibspiDma.c'.   You might try that out.

    The biggest transfer depends on how much memory the MibSPI has and you can find this on the front page of the datasheet, but on the TMS570SL31x for example the MibSPI has 128 words of memory (meaning 128 transfers of 16 bit data can be sequenced) and it's parity protected.

    As an aside, the low end Hercules (RM42x or TMS570LS03/04x) doesn't have DMA but it still has a MibSPI so it could be worth learning how to use if you plan to scale.

  • Ok, almost done. I have a few more questions.

    I read in the datasheet that there are 128kB of MIBSPI RAM, the same as the uC you mentioned in your answer. This means that one transfer group can send 128 16-bit words or 256 8-bit words, am I right in assuming this?

    Anyway, this means that the transfer group can receive/send up to 256 bytes per group, and considering tha there are 16 transfer groups in my device (RM48L952) this means that I can transfer up to 4kB of data in one go (assuming that the received data might vary from 3 bytes to 1kB)?

    Is MIBSPI that flexible?

  • Pablo,

    It's 128 words, not kB.

    Each word is good for one SPI transfer.  So if you need 2 x 8 bit transfers and you have to do something like toggle chip select between the two ... then you have to use 2 words.  If you can combine 2 x 8 bit transfers into a single 16-bit transfer from the perspective of the MCU - then you only need 1 word.

    The transfer groups share the 128 word RAM.  They get a start and end pointer into this common pool of commands so you can divide the command memoru up as needed.

     

  • Ok, So this means that the maximum amount of bytes that I can send/receive in one go is 256 bytes (16-bit transfers). So, this means that If I want to send/receive 1kB I would have to activate this transfer 4 times with4 CS activations?

    It would look something like this in the oscilloscope?

    |CS=0 | 256 bytes | CS_HOLD=1 | CS_HOLD=0 | 256bytes | CS_HOLD=1 | ....

  • Pablo,

    If you have a sequence where there's some header, plus a whole lot of bulk data, it's possible to use fewer mailbox entries for the bulk data and use the DMA to refresh these entries.  There are counters for the # of DMA requests to generate and you can specify which word in the buffer causes the DMA request to be generated.

    Where this can be useful especially is if you're not changing header or footer ... you can leave this in the MibSPI and use the DMA to help accelerate the 'payload'.

  • Sorry, I should also say that if you don't have a DMA you could apply a similar scheme with the CPU doing the data movement.   Even though you have less than 1K buffers in the MibSPI itself, it can be really advantageous to move the data in bursts of maybe 8 or 16 words compared to every 1 word,  if you're using the CPU.   Less time spent in interrupt overhead overall because it's amortorized over more words.

    I think if you play with the example that comes w. HalCoGen it'll give you a place to start to build from.

  • So ..

    Use group transfer.

    Group transfer 1 receives header

    Receiving last byte of header DMA writes the size to the next group transfer

    Next group transfer initiates.

    Cascade transfer groups until all transfers are done.

    Check if entire packet was received. If it wasn't restart the transfer and keep cascading until it's done??

    As you suggested I'll play around with the example in HALcogen, but I wasn't able to understand this answer, sorry.

  • I think I was able to understand this.

    Use 1 transfer group set to send/receive bytes (use 128 buffers maybe)

    Then set DMA to fill mibspiram when transfer ends

    restart transfer group

    do this as many times as needed

    Just to let you know, this must all be controlled by software, which means that it cannot keep sending/receiving as it pleases.

    This is really breaking my spirit.

  • Pablo,
    There is an ICOUNT register (DMAxCOUNT) that can be used to make the same set of buffers in the MibSPI generate multiple DMA requests until the total count is done.

    So lets say 1K transfers is your goal.  And you use 8 buffers.  1024/8 is 128 - so you can use this as the ICOUNT.

  • Thank you so much for your help so far. The thing is that I really don't know how many bytes I'll be receiving from the slave. Its response may vary from 3 bytes to 1024 and there lies my problem. I am trying to do exactly what you've suggested. I am trying to use the ICOUNT.

    The thing is that it still requires some manual work. I receive a frame, modify the ICOUNT, source and destination and make a new transfer. I can also do this manually, receiving byte by byte. But I'd like to automate this entire process. Sending a packt automatically is really easy, but my problem lies in the receive part, because the master doesn't know the answer size for the slave.

    This protocol is a little bit like modbus. The master sends a request, while it only reads busy from the slave. Then the master waits T useconds and the slave responds with another packet while the master only sends 0, just to receive the packet.

    I'll keep on going here.

  • Hi Pablo,

    Yes, the SPI doesn't have the intelligence to handle variable packet lengths, unfortunately.   You'll need some help from the CPU as you point out.

    The N2HET does have the intelligence for this, and it does have a very basic SPI emulation capability, as well as an integrated DMA (the HTU).   This combination *is* available on the RM42 and TMS570LS04/03 - so it's a solution that actually would scale.  But - it's up to you as to whether you want to get into this type of use case with HET.  And it definitely will not run as fast (bit rate) as the true SPI can run.

    Best Regards,

    Anthony 

     

  • I thought of a way, but I'm not sure if it'd work or not. The moment the MIBSPI receives the size it would automatically update the transfer group size, tricking it, by modifying the number of bytes the transfer group is supposed to receive.


    I think this is really prone to errors (don't know, haven't tried it yet, leaving it as a last resort), but I'll try this and if I'm successful I'll report it back to the community.

    Thank you so much for your help.