SDK 2.7.0 - LPI2C_MasterReceive() hangs for >256 bytes

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

SDK 2.7.0 - LPI2C_MasterReceive() hangs for >256 bytes

Jump to solution
699 Views
dmarks_ls
Senior Contributor I

So I lost about a day to this bug...

Summary

The LPI2C peripheral (by design) cannot be instructed to receive more than 256 at a time.  However, the NXP (FSL) LPI2C driver will gladly accept the oversized transfer request and then promptly hang the system or thread.  This occurs in both blocking (polled) and non-blocking (interrupt/RTOS) modes.

Test Setup

I have an I2C FRAM device (FM24CL64B, 64 kbit / 8 kB) connected to an RT1050 MCU.  I have code that generates two 70-byte reads, and then two 326-byte reads (326 by coincidence is equal to 256 + 70).  Each of the 70-byte reads complete successfully; using a scope, I see START, [A0] (bus address - write), [01][00] (memory address), START (repeat), [A1] (bus address - read), then 70 bytes of memory, the last of which is NACKed, then STOP.

Observed Failure

When the 326-byte read is submitted, I see on the scope: START, write, address, START, read, 70 bytes of memory (last one NACKed), and then SCK stuck low.  If I run in non-blocking (polled) mode, execution is hung inside LPI2C_MasterReceive(), where rxSize is down to 255, but the RX FIFO is empty.  The blocking driver hangs effectively at the same point, with 70 bytes read and a stuck I2C bus.

Root Cause

The core issue is that the LPI2C peripheral itself is not capable of reading more than 256 bytes at a time, as I discovered when digging into this failure.  When writing MTDR, only the lower 8 bits (DATA[7:0]) is used to specify the number of bytes to read in, and the last byte is NACKed, meaning you can't (or shouldn't) perform two consecutive read commands, because the slave should not be NACKed until the transfer is complete.  Thus, you are limited to reading 256 bytes in a single transfer.  But that's not the actual bug.

Identified Defect

The bug I'm reporting is that the LPI2C driver is ill-behaved; the driver is being told to transfer 326 bytes, but it does not validate rxSize; it instead feeds (rxSize - 1) to the LPI2C_MTDR_DATA() macro, which masks the value down to 8 bits, i.e. (326 - 1) & 0xFF = (70 - 1) = 69.  Thus, the I2C peripheral is told "go read 70 bytes", which it does, but the main loop in LPI2C_MasterReceive() is waiting to receive a full 326 bytes, and hangs when the RX FIFO goes dry.

The LPI2C driver should know that the LPI2C peripheral cannot read more than 256 bytes in one transfer, and should generate an appropriate error when given an oversized read transfer.  It should NOT hang the system and leave the developer (i.e. me) scratching my head as to why the thread craps out when it tries to perform the large I2C read.

Proposed Solution

The driver should either return an error code or throw an assert() if a receive transfer is too large (more than 256 bytes).  The check can be performed in LPI2C_MasterReceive() for blocking transfers, and somewhere else for non-blocking transfers.  An error code or runtime assert is a much cleaner response than hanging the system.

As for my code, I must modify my FRAM driver to perform reads in 256-byte chunks, which I would have to do anyway, but I would have discovered this sooner if the driver had generated a meaningful error.  Please make sure that your driver software is robust and validates its inputs, thanks.

David R.

0 Kudos
1 Solution
585 Views
victorjimenez
NXP TechSupport
NXP TechSupport

Hello David, 

Thanks for your feedback! I already passed this to the SDK team. 

Best Regards, 

Victor 

View solution in original post

1 Reply
586 Views
victorjimenez
NXP TechSupport
NXP TechSupport

Hello David, 

Thanks for your feedback! I already passed this to the SDK team. 

Best Regards, 

Victor