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:
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:
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):
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:
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.
已解决! 转到解答。
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
@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.
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.
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.
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):
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.
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.
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:
Then I add clear Transmit Command Register (TCR) [CONT] bit code at IRQHandler function, which could fix the issue:
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.
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.
-------------------------------------------------------------------------------
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.
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).
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
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
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.
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
> 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