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.

SPI hardware and CSL clarification

Guru 15580 points
Other Parts Discussed in Thread: ADS7951, OMAP-L138

I am trying to understand the operation of the SPI peripheral in the C5515. sprufo3 seems to imply that SPI reads and writes are two different processes. However, most SPI peripherals treat a SPI transaction as both a read *and* a write by clocking data into MISO as it receives data from the master on MOSI. The SPI chip I am interfacing to requires a two-way (read *and* write) transaction since it clocks out status bytes as the C5515 is clocking out commands.

Does the C5515 SPI peripheral indeed perform a bi-directional transfer during a transaction? If so, why does the CSL only support SPI_write and SPI_read functions? After sending a 16-bit transaction to SPIDAT2, will SPIDAT1 have the incoming 16-bit response from my SPI peripheral?

  • My experience is with the C5506 and multiple ADS7951 chips, one per serial port. The C5506 runs at 108 MHz and has a special SPI mode for the McBSP ports which shares the CLKR and CLKX signals. The ADS7951 uses a 16-bit command word and 16-bit result/status and I have the serial links clocked at 18 MHz.

    When my firmware calls MCBSP_write16(), it takes 16 serial clock cycles before anything appears in MCBSP_rrdy() or MCBSP_read16(). Because there are 6 CPU clock cycles per serial clock, that means I have to wait 96 CPU instructions before I can expect a result, even though output and input are literally simultaneous.

    In other words, keep in mind that a transmit event basically occurs at the start of a serial word, and the function does not block until the bits are all sent, while a receive event occurs after all serial bits have been received. The TMS320 is certainly plenty fast enough that you might end up executing read before the write has completed.

    The C5506 does have an interrupt mode that is triggered by the FSR (Frame Sync Receive) signal, but I have not experimented with whether this occurs at the beginning or end of a word, and in any case I assume it would not be very useful to read the serial receive data before all of the bits have arrived. Also, the C5506 can buffer several transmit words in hardware, which lengthens the amount of time you might have to wait for the "simultaneous" receive event to complete.

    Although the C5515 SPI probably differs from the C5506 McBSP substantially, I imagine that the same conditions are true: Transmit does not block, and Receive cannot occur until all bits have been transferred. Thus, even though the electrical signals are truly simultaneous, the serial hardware makes it possible for the CPU to operate as if transmit and receive were sort of separate.

  • Thanks Brian. I also suspect that the C5515 and C5505 SPI hardware is different. I'll have to wait until next week to test this since my PCB is in fab.

  • Hi Mike,

    You are right. According to SPRUFO3, C5515 cannot read and write SPI simultaneously (input bit and output bit on the same clock edge). If that SPI device is not avoidable, you can use a small external circuit (shift register) to delay the output of the device 1 frame.

    Cheers,

    Cong-Van

  • Cong,

    No offense, but I sure hope you are wrong. All standard SPI devices shift data in as they are shifting data out. For example, take a look at the timing diagram on page 13 of sprufo3.

    Notice that the slave is sending data in (MISO) as the master is sending data out (MOSI). This is exactly what my SPI peripheral device does and will not work otherwise. What is confusing me (I can't test this yet) is why there are two different data transaction commands...read and write. See Table 10, pg 29 (bits 1-0, CMD).

    Should't the SPIDAT1 contain the incoming data from the slave after the master issues a Write command? This is how the OMAP-L138 SPI peripheral works. Why do I need a read command? What purpose does it serve? How is it different from Write?

  • Hi Mike,

    You may be right. According to the following thread, C5505 does support full-duplex SPI, so does C5515. It is quite interesting that "there is no difference between the read and the write command".

    http://e2e.ti.com/support/dsp/tms320c5000_power-efficient_dsps/f/109/t/10974.aspx#42715

    Cheers,

    Cong-Van

  • MikeH said:
    What is confusing me (I can't test this yet) is why there are two different data transaction commands...read and write.

    The reason is that the hardware peripheral buffers the serial data in parallel registers, and the DSP only deals with the parallel data. While it is true that each serial output bit is indeed simultaneous with the corresponding serial input bit, it is not quite possible for the parallel output buffer and parallel input buffer register to be updated simultaneously. When the peripheral output register is written, it takes several cycles for the total serial word to be transmitted. Meanwhile, the peripheral input register does not reflect the live serial input on a bit-by-bit basis, but rather it waits until the entire serial word has been received, and after the last bit comes in the peripheral updates the parallel buffer register as a whole word.

    The SPI_write() function stores a word in the parallel buffer register. Depending upon the serial hardware, there may actually be several stages of buffering, or at the very least there might be a buffer for the word that is actively being clocked out over the serial lines plus a separate word register that can hold the next word.

    The SPI_read() function grabs the entire parallel word from the peripheral buffer register, but it can only do so after all bits have been collected. Thus, it technically happens after the write has finished.

    Should't the SPIDAT1 contain the incoming data from the slave after the master issues a Write command? This is how the OMAP-L138 SPI peripheral works. Why do I need a read command? What purpose does it serve? How is it different from Write?

    No, SPIDAT1 cannot contain the incoming data from the slave immediately after the master issues a write. As I described above, it may take hundreds of CPU cycles before the much-slower serial port completes the entire serial word transfer. Granted, you did technically say "after" without qualifying how long of a time frame you're talking about. In my specific example given earlier in this thread, the incoming data appears 96 clock cycles after the write command is issued.

    If you check the timing diagrams for the OMAP-L138 SPI peripheral, you will find that it is not possible for all of the incoming data to be known at the instant that the output data starts the sending process. If you could superimpose the instruction sequence of your DSP on top of the SPI timing diagram, you'd see hundreds of operations during the time span of a single serial word. Human perception may be that such a serial transfer is instantaneous, but the DSP sees the serial operation as moving incredibly slowly.

    Note that if you need a single command that performs both write and read as one operation, then you can write one yourself. However, you would have to block the processor while the write buffer is full, then block the processor again until the read buffer is not empty. You would also have to be very careful not to get out of sync, because the serial peripheral will gladly hold on to the incoming data from a previous command until you read it, so your code might mismatch the command/result pair.

    One thing I've noticed is that the CSL provides multiple raw subroutines: A couple of routines check whether the send buffer is empty or full, then there is SPI_write(), plus a routine should be available to test whether input data is ready and waiting, and finally an SPI_read() to grab the data once it is there. This mixture of subroutines allows you to write code that does not needlessly block your instruction flow just to wait for a serial event to complete. Since I keep pointing out that a single serial exchange over SPI can take hundreds of instruction clock cycles, the best coding approach is to use serial events to trigger either interrupts or DMA transfers.  Then, your code will never block while waiting for just a single transaction, but rather will be able to transfer a long sequence of words and then come along later to process the whole data set as an array.  SPI_read() would therefore be most useful in a serial interrupt handler routine, because there is no need to test whether the SPIDAT1 register has data when you're inside an interrupt that is triggered whenever the SPIDAT1 register is filled.

    To put it another way, if you DSP is not performing very many operations, and you don't really care about high performance, then you can certainly spin in a loop writing one master command at a time and reading the slave data, piece by piece. But as soon as you start processing high-speed streams of data, you need to separate the write stream from the read stream so that the DSP never blocks or otherwise wastes even a single cycle. Thus, while it may not make sense at first for write and read to be separate, they really must be separate in parallel terms and also they must be separate for the sake of efficient processing.

  • Cong Van Nguyen said:
    According to the following thread, C5505 does support full-duplex SPI, so does C5515. It is quite interesting that "there is no difference between the read and the write command".

    http://e2e.ti.com/support/dsp/tms320c5000_power-efficient_dsps/f/109/t/10974.aspx#42715

    Thanks for the link, Cong.

    So, I see that the difference between the C5505 discussed in this thread and the C5506 that I am using is that the C5506 has a McBSP peripheral with separate registers for write and read. It appears that the C5505 only has a single register for both write and read. However, despite those differences, the timing is still the same. Even with the shared write/read register of an SPI peripheral, there is still a delay of many DSP cycles between when the master command write starts and when the slave read data appears in the shared register. Thus, SPI_write() returns immediately so that you can perform other tasks with the DSP. Then, SPI_read() can be used later - in an interrupt service routine, via the DMA peripheral, or even by timed or polled access in your main code.

  • Biran,

    Wow! Thanks for the "uber-response"! You should write a book...:)

    Yes, I believe the C5515 may be significantly different that your experience since it does have a single read/write register. I agree with your general assessment that one should minimize wait loops when designing high-speed systems. However, my experience (with the OMAP-L38) shows that there are very few instruction cycles (10's) between the end of a SPI send instruction and the availability of data in the receive register. In fact, the OMAP BSL that I use (from Spectrum Digital) does exactly what you say: send the transaction (byte in my case), wait for TX EMPTY, then wait for RX FULL, COPY(i.e. read) the received byte from the RX register into my data buffer. This actually works very efficiently with very little wasted time waiting. Since my app has nothing better to do than wait for the incoming byte, this works for me.

    I am still concerned by what I see on the C5515. As expected, the SPI WRITE command starts an outbound data transfer of data in the SPIDAT2 register, while at the same time clocking data into the SPIDAT1 register. Once this dual transaction is complete (completion being tested by waiting for #SPISTATA1->BSY) I can *COPY* the data that appears in the RX register (SPIDAT1) into my receive data buffer as follows (dest_buffer[bufIndex] = (CSL_SPI_REGS->SPIDR1 & 0xFF);

    HOWEVER, if I now issue a SPI READ "command", ANOTHER DATA TRANSACTION APPEARS ON THE MOSI/MISO PINS! This causes another byte to be sent from the SPIDAT2 register, and another byte to appear on the SPIDAT1 register. In other words, the SPI READ command DUPLICATES what the SPI WRITE has just done.

    I may be totally wrong in this since I can't actually test with my SPI slave chip yet, but by using LOOPBACK, this is what I am observing on the C5515.

    Thanks again for your clarification.

  • Cong-Van,

    Cong Van Nguyen said:
    "there is no difference between the read and the write command".

    Good find! This actually answers my question and validates my finding! Thanks!

  • MikeH said:
    my experience (with the OMAP-L38) shows that there are very few instruction cycles (10's) between the end of a SPI send instruction and the availability of data in the receive register.

    It all depends upon the instruction rate of your CPU and the bit rate of the serial link (also, the word size, because 16-bit SPI obviously takes twice as long); in other words, you cannot rely on it always being around 10 cycles, especially not when you move to a new processor platform.

    Since my app has nothing better to do than wait for the incoming byte, this works for me.

    It is very rare for DSP firmware to have wasted cycles. If nothing else, you could process the previous buffer while a new buffer is being received. If you are processing data before sending it, then you could pre-process the next buffer while sending the current buffer. If all you're doing with the DSP is moving bytes around, then that sure seems like a degenerate example.

    I am still concerned by what I see on the C5515.

    [...]

    HOWEVER, if I now issue a SPI READ "command", ANOTHER DATA TRANSACTION APPEARS ON THE MOSI/MISO PINS! This causes another byte to be sent from the SPIDAT2 register, and another byte to appear on the SPIDAT1 register. In other words, the SPI READ command DUPLICATES what the SPI WRITE has just done.

    I am also concerned, since this seems to indicate that the CSL for the C55x5 has a different style of implementation than the CSL for the C5506. With the C5506 McBSP, the CSL does the minimal number of operations inside a subroutine. What you're finding is that the READ has a side-effect of initiating a WRITE.

    The good news is that the CSL is open source. You can either fix the READ or create your own SPI_write_read() subroutine.