RT1050 LPSPI last bit not completing in continuous mode

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

RT1050 LPSPI last bit not completing in continuous mode

Jump to solution
8,789 Views
dmarks_ls
Senior Contributor I

TL;DR - On the RT1050 LPSPI peripheral, when continuous mode is active (TCR.CONT == 1), the last SCK edge of a transfer does not complete, which means the last byte does not arrive in the RX FIFO, which means that when implementing a SPI driver API, nothing can continue after a read transfer, because the read request cannot be closed out without sending at least one more byte into the FIFO, and clearing CONT would de-assert PCSx and terminate the transfer.

So, I've been writing my own C++ LPSPI driver, using the FSL driver and other code as examples.  The base class for my SPI driver consists of two methods:

  • Begin(cs) -- Establishes what chip select is being driven and sets up the transfer.
  • Xfer(txdata, rxdata, length, is_final) -- Transmits and/or receives X number of bytes, marking whether this is the final transfer before PCSx is deasserted.

Xfer() can be called multiple times, for instance when doing a write-then-read as is typical of most SPI devices.  E.g.

/* Select PCS0. */
lpspi.Begin(0);
 /* Send command buffer, keep PCS0 active. */
lpspi.Xfer(cmd_buffer, nullptr, cmd_length, false);
/* Read data buffer, deassert PCS0 when done. */
lpspi.Xfer(nullptr, data_buffer, data_length, true);
‍‍‍‍‍‍

Now, while it's not typical, there should be no reason why one could not do two or more consecutive reads, perhaps because of a limited buffer size or other considerations.  Like this:

lpspi.Begin(0);
 lpspi.Xfer(cmd_buffer, nullptr, cmd_length, false);
int cycle_count = 4;
for (int i = 0; i < cycle_count; ++i) {
 lpspi.Xfer(nullptr, data_buffer, data_length, (i == (cycle_count - 1)));
 /* Process the data buffer. */
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

However, THIS IS NOT POSSIBLE with the NXP LPSPI peripheral, based on my own testing and one other forum post on this exact topic involving a Kinetis KL28 LPSPI.  Here's a screencap of a 20-byte transfer, with CONT == 0:

lpspi_cont_is_0.png

CONT == 0 obviously won't work for a SPI device access, with PCSx de-asserting between every byte.  But here's the same transfer with CONT == 1 (the only change made in the code):

lpspi_cont_is_1.png

You can see that the last half-clock doesn't appear on SCK; the clock stays high, and PCSx stays asserted because I haven't written to TCR yet.  This results in the last (zero) byte not arriving in the RX FIFO, which causes my driver to stall because the expected 20 bytes are never fully received.  If I write TCR with both CONT and CONTC set to 1, this doesn't change (I tried it, SCK remains stuck in the middle of the final bit).  And if I write TCR with CONTC set to 0, then yes, the lask SCK edge appears and the last byte arrives in the RX FIFO, but this also de-asserts PCSx and terminates the transfer.

I consider this to be a bug/erratum in the LPSPI peripheral, because it unnecessarily constrains the behavior of the software using it.  My SPI driver cannot perform another transfer following a read while keeping PCSx asserted; performing a read requires that it be the last transfer before de-asserting PCSx.  I've worked around this in my driver by doing two things:

  • Rather than always trying to receive N bytes from the RX FIFO after sending N bytes, I keep a "garbage byte" counter that goes up each time a byte is written with no read expected; the counter is decremented each time a byte is read from the RX FIFO, but the counter is not required to go to zero if the write is not a final transfer; the garbage count carries over to the subsequent transfer.
  • If the application requests a read (i.e. rxdata != nullptr) and is_final is false, the LPSPI driver returns an error because it cannot complete the request.

It's a crummy workaround, but it at least allows me to maintain a consistent API for my drivers.  Unless NXP has a proper workaround that allows me to get that last byte into the RX FIFO without terminating the transfer or shoving an extra byte into the TX FIFO, then I guess this isn't really a question post, more of an "engineer beware" post.  Thanks for reading.

Labels (1)
1 Solution
7,688 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi David,

Sorry for the delay reply.

I got the confirmation from product team: LPSPI IP designer confirm that issue and will fix it at next version IP.

As a workaround so far, customer can clear LPSPI Transmit Command Register (TCR) [CONT] bit after required bytes had been transmitted.

Sorry for that may bring any inconvenience to you.

best regards,

Mike

View solution in original post

17 Replies
6,510 Views
ZohaibAli
Contributor III

@dmarks_ls I am facing a similar issue, I am new to development so I thought you could help me out here. I am trying to implement a write-then-read pattern in my lpspi driver for IMX-RT1060 EVK. As you can see in the attached image, I have no issue with the MOSI data. But, when I write the first byte (0x01 in this case) and then try to read from RDR after that, I do not get anything. After reading 16 times when I close the transaction by clearing CONT and CONTC bit, I can still see one frame/byte left unread in the RDR.

In other words, I am reading the first byte in the second iteration, the second byte in the third, and so on. Till the last byte which can only be read 17th time.

I am setting CONT bit and clearing CONTC bit before the first transfer, and then before subsequent transfers, I set the CONTC bit. Finally, after all bytes are written I clear both CONT and CONTC bits.

0 Kudos
6,462 Views
dmarks_ls
Senior Contributor I

You don't provide any code, so it's difficult to determine where your issue is occurring.  But it sounds like you're running into the fundamental problem with the LPSPI peripheral in continuous mode (TCR.CONT == 1) where if you write N bytes to LPSPI, then you cannot read more than N-1 bytes from RDR without first closing the transaction (or writing another byte).  So with the LPSPI peripheral in continuous mode, the activity you describe -- write one byte to TDR, then read one byte from RDR -- cannot be performed, because the LPSPI peripheral will always hold onto that last byte until you terminate the transfer (or write 0 to TCR.CONT, which also terminates the transfer).

Sorry, but I think the answer is that you need to find a different way to do what you're doing.  Now, it sounds like you're also describing that after you close the transaction, you're still only getting N-1 bytes out of RDR.  That doesn't match my experience; once the transaction is complete, if you've written, say, 10 bytes, you should be able to read 10 bytes from RDR.  If you're losing a byte, then double-check your driver.

David R.

0 Kudos
6,447 Views
ZohaibAli
Contributor III

Hi David,

Here is the gist of what I am doing in my driver to write-read.

 

void lpspi_write_read()
{
    /* Disable SPI module.
    *  Flush Rx and Tx FIFOs.
    *  Clear NOSTALL bit in CFGR1.
    *  Enable module.
    *  Clear CONT, CONTC, TXMSK, RXMSK, PCS
    *  bits in TCR.
    *  Select PCS0 in TCR. */
    
    /* Set CONT bit to keep SS asserted. */
    LPSPI_SET_BITS(TCR, CONT_MASK);

    /* Wait for TX FIFO to be empty. */
    while(read_mem(LPSPI_FSR) & FSR_TXCOUNT);

    while(DATA_FRAMES > 0)
    {
        /* Get next TX frame. */
        tx_frame = get_next_tx_frame();

        /* Clear Status Register. */
        LPSPI_CLR_BITS(SR, all);

        /* Write data frame on TDR. */
        write_mem(LPSPI_TDR, tx_frame);

        /* Wait for the Word to be sampled. */
        while((read_mem(LPSPI_SR) & SR_WCF) == 0);

        /* Clear Word Complete Flag in SR. */
        LPSPI_CLR_BITS(SR, SR_WCF);

        /* Read RDR to get received data frame. */
        rx_frame = read_mem(LPSPI_RDR);
    }

    /* Clear CONT and CONTC bits. */
    LPSPI_CLR_BITS(TCR, (CONT_MASK | CONTC_MASK));
}

 

Say, I am trying to write 5 bytes of data. When I write the first byte and then try to read from RDR, I don't get any frame. When I do the same for the following bytes though, I can read expected data from RDR.

After I have written all five bytes, I am clearing the CONT bit to end the transaction. At this point, there is still one byte left in the RDR.

For example: If I write (0x1, 0x2, 0x3, 0x4, 0x5) and the expected Slave response is (0x1), then I get received data like this: (0x0, 0x1, 0x1, 0x1, 0x1) and one shifted frame (0x1) is in RDR. So in a sense, I am also not loosing a byte.

Please let me know if you think of something that I am missing or doing wrong.

This issue is exactly the same as mine: https://community.nxp.com/t5/S32K/LPSPI-Rx-FIFO-in-Continuous-Mode/m-p/784592/highlight/false#M2515

Thanks,

Zohaib Ali.

Tags (1)
0 Kudos
6,431 Views
dmarks_ls
Senior Contributor I

You can't use LPSPI the way you're trying to use it.  The LPSPI peripheral, as currently shipping in the RT1050 (unless they've recently shipped an erratum-corrected chip version), does not allow you to read the last byte out of the RX FIFO until you clear CONT and/or CONTC.  Period.  You can't do it.  I'm not sure what you're trying to do with the Word Complete Flag (WCF) there, but the bit you're supposed to be paying attention to is the Receive Data Flag (RDF), which tells you that there's a new word to read in the Receive Data queue.  The first time you read RDR, I'm fairly certain that RDF is 0 (clear), which is why you're not getting actual data out of the RX FIFO, and why you have one byte left in the FIFO after stopping the transaction.

Once more: Because of this erratum, you CANNOT read RDR to get the most recent MISO word until you clear CONT and/or CONTC.  Your loop as written above (write a word, read a word, repeat N times) will never work.  Following is what your loop should be doing when performing a write-then-read with LPSPI (I'm assuming you're doing this in a non-interrupt-based manner):

  1. Start to write X bytes/words to TDR from your write buffer.  Don't overflow the TX FIFO (make sure SR.TDF is set before writing).
  2. When your write buffer is exhausted, begin writing zero-value words to TDR for as many bytes as you want to read from the SPI device.
  3. When you've written as many zero-value words as you need for the amount of data you need to read, stop writing to TDR and set TCR.CONTC to 0.
  4. Whenever SR.RDF is set, read RDR and fill your receive buffer.  You can decide if you want to keep the data you read when outputting your write buffer.
  5. You should eventually read exactly the number of words from RDR that you wrote to TDR in steps 1 and 2.
  6. When your receive buffer is full, exit your loop.  CONTC is already 0, so nothing further needs to be done with LPSPI.

Note that the above is the contents of a loop... obviously you can't complete steps 1, 2, and 3 without reading at least some data out of the RX FIFO else it will overflow.  Hope this helps.

David R.

0 Kudos
7,688 Views
dmarks_ls
Senior Contributor I

Alternately, I realize I could set the TCR.RXMSK bit instead of counting "garbage" bytes when doing a pure write; that would only require that I write TCR before each new block of data, with CONTC set.  But that still doesn't solve the core issue of getting every last byte/word into the RX FIFO when actually doing a read, so that it could be followed by another continuous operation.

0 Kudos
7,688 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi David,

I did a test with MCUXpresso SDK V2.5.1 lpspi driver demo [\interrupt_b2b\master] with IMXRT1050-EVKB board.

When I set Transmit Command Register (TCR) [CONT] bit to enable continuous transfer, I could regenerate your mentioned issue:

pastedImage_1.png

pastedImage_2.png

Then I add clear Transmit Command Register (TCR) [CONT] bit code at IRQHandler function, which could fix the issue:

pastedImage_6.png

pastedImage_4.png

From RT1050 reference manual, the LPSPI command word in master mode [CONT] bit can be modified during transfer.

If clear LPSPI Transmit Command Register (TCR) [CONT] bit during LPSPI communication processing, Then the chip select will exit continuous mode during transfer.

pastedImage_7.png

So far, customer can clear LPSPI Transmit Command Register (TCR) [CONT] bit after required bytes had been transmitted.

I need to check with i.MXRT product team about LPSPI module behavior with CONT bit set.

I will update this thread when there with any feedback.

Thanks for the patience.


Have a great day,
Mike

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos
7,688 Views
dmarks_ls
Senior Contributor I

Hi Mike,

I appreciate your taking the time to reproduce my issue.  You've also confirmed my diagnosis, that to get the last clock to appear and get the last byte to appear in the RX FIFO, you have to write TCR with either CONT or CONTC set to 0, which terminates the transfer.  That's the core issue; I can't write a driver for LPSPI that allows multiple reads within the same transfer, because the previous read can't be completed without pushing more data into the TX FIFO or terminating the transfer.  The following hypothetical code snippet is an example of what I can't do with LPSPI, that I can do with most any other SPI peripheral (e.g. Kinetis DSPI):

/* Last parameter:
 * false = continue transfer
 * true = end transfer */
lpspi.Begin(0);  // use PCS0
lpspi.Send(cmd_buffer, cmd_length, false);
lpspi.Read(data_buffer_1, data_length_1, false);
lpspi.Read(data_buffer_2, data_length_2, false);
lpspi.Read(data_buffer_3, data_length_3, false);
lpspi.Read(data_buffer_4, data_length_4, true);‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The first call to lpspi.Read() can't complete because the last byte will not enter the RX FIFO without 1) writing the next dummy byte to the TX FIFO or 2) writing TCR with CONT or CONTC set to 0, which also terminates the transfer.  The driver's hands are completely tied; writing an extra dummy byte is out of the question (there's no way to know what's supposed to come next), and terminating the transfer is also not an option.  My only workaround is to restrict the driver and generate an error if lpspi.Read() is called with "terminate transfer" set to false.

This really looks like a defect in the LPSPI peripheral; there's no good reason for SCK to freeze in the active state on the last bit of a transfer and withhold the last byte from the RX FIFO until either the next byte is written or the transfer is terminated.  I do now have a working C++ driver for LPSPI, with the aforementioned restriction that any read operation must also terminate the transfer, which is probably fine for most SPI device drivers.  It's just frustrating that NXP hasn't noticed or addressed this before now.  As I mentioned, someone had this exact same issue two years ago with a KL28 LPSPI, and you gave the same advice, which was to write TCR with CONT and CONTC clear to terminate the transfer and get the last byte into the RX FIFO.  But if you don't want to terminate the transfer at that point, you have no other option.

7,688 Views
brucemckenney
Contributor III

This also happens on the RT1011. This isn't too surprising (probably the same Verilog), just a data point.

My workaround was to configure PCS0 as a GPIO and wiggle it myself (the way we did it in the Ancient Days).

7,688 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi Bruce

Thanks for the info.

I don't know the process for develop/update the new LPSPI IP and be applied with new product.

RT1011 product project start define quite early. There exists the gap. 

I hope the new IP could be applied with new RT product soon.

best regards,

Mike

0 Kudos
7,689 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi David,

Sorry for the delay reply.

I got the confirmation from product team: LPSPI IP designer confirm that issue and will fix it at next version IP.

As a workaround so far, customer can clear LPSPI Transmit Command Register (TCR) [CONT] bit after required bytes had been transmitted.

Sorry for that may bring any inconvenience to you.

best regards,

Mike

7,688 Views
dmarks_ls
Senior Contributor I

I appreciate you looking into this issue and determining that there is in fact a silicon bug present.  It's a shame that this wasn't identified two years ago so that it might have been fixed prior to broad release of the i.MX RT series.  Thank you for your attention to this.

David R.

7,688 Views
liangliangma
Contributor III

I should have argued more so they would have not simply ignored this.

7,688 Views
dmarks_ls
Senior Contributor I

I do think it's NXP's fault for ignoring what looked to me like an obvious erratum.

0 Kudos
7,688 Views
TomE
Specialist II

I can't find mention of this problem in either of the latest  IMXRT1050 or IMXRT1060 Errata documents.

Could you please consider adding this to both the IMXRT1050 and IMXRT1060 (if applicable) Errata Documents?

Tom

7,688 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi David,

Just let you know, I am still checking with product team about this issue.

There has no feedback so far.

Thanks for the patience.

best regards,

Mike

7,688 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi David,

Thanks for the info.

I am checking with product team and wish it could draw a conclusion this time.

I will update this thread when there with any feedback.

best regards,

Mike

1,029 Views
TomE
Specialist II

 

> ‎04-19-2019 12:17 AM
> I will update this thread when there with any feedback.

Anything yet?

I've checked the following, and can't find any obvious mention of this problem.

Chip Errata for i.MX RT1060_A Rev 1.3 Dec 17, 2021
Chip Errata for i.MX RT1060_B Rev 1.1 Dec 17, 2021
Chip Errata for the i.MX RT1050 Rev 3Dec 15, 2021

Do you know which chips have "new IP" which has this fixed? Has it been fixed?

Tom