SD/MMC card chip drivers fail Chan FatFs provided misaligned memory address tests

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

SD/MMC card chip drivers fail Chan FatFs provided misaligned memory address tests

3,361 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by briching on Mon Oct 05 17:09:35 MST 2015
Hello,

I have an embedded artists 4357 OEM board dev kit, and I am trying to get the SD card working with a file system.  The provided example (periph_sdmmc) runs fine on it's own, but when incorporating the example code into our production code, we end up with a corrupted file system.  I believe this is because the example provided with lpcopen is not fully compatible with Chan FatFS.

As an isolation test, I have downloaded the code at http://elm-chan.org/fsw/ff/img/app4.c in the hopes that I would be able to test out the drivers directly to see the solution.  It is not obvious to me what to do, since I will most likely have to modify the sd/mmc code within lpc-open to get it to work.

The periph_sdmmc project passes the first two tests and fails "**** Single sector write test 2 ****\n".  The problem is that the buffer gets populated with data in the wrong place.  Digging down into the chip drivers for the sd/mmc card, I can see that this may be due to DMA being used which expects buffers to be aligned on 32-bit addresses.  You can't just pass a void pointer in there and expect it to work out.  Chan needs to be able to pass in a byte pointer from up above.

If chan's provided checker utility doesn't pass for the example, without even a single line of our production code in it, I suspect the culprit is in lpcopen.  Try it out yourself as a 5 minute experiment.  Make a call to test_diskio from the periph_sdmmc main function, and you probably will see that it doesn't pass.  Dig in and look at the contents of memory and you'll see your data written and read being off by two bytes as shown below:

Here's what was written in the misaligned test (bytes ordered sequentially left to right):

B6DB6E37 188C46A3 52291788 44A2512B 168B46A3
52291708 84C2E173 BADDED75 391F0C86 C3E271BB
DEEF743A 1D8D4521 934A2511 8BC663B2 59AFD4EA
F579BF5C AED768B4 5A2D9549 27904824 12090780
C0E0F078 BC5E2F14 0A058143 A2D1EBF6 FBFE7F3C
1E8FC4E2 F1FBFE7F BCDE6F34 1A8DC5E1 733A9D4D
A5D1EBF6 7BBEDF6C B65B2E97 C8E4F279 BFDCEE77
B85C2E97 48A452A9 57A8D46A 35190F04 8241A352
A9D7E8F4 FA7D3D9D 4DA5512B 160B0683 C2E1F3FA
FDFD7D3D 9D4D2511 8B462392 49279048 249249A7
50A8542A 15098740 2090C8E4 F279BFDC 6EB7D8EC
F6FB7EBF DC6E3718 8C46A3D2 E977381C 8E472090
48A4D269 3798CCE6 733A1D8D C561339A 4DA551AB
562B96CB 66B3DAED 75391F8C C663B2D9 6FB45AAD
D5E97738 1C8EC760 30180C86 C3E2713B 9ECFE472
B95F2C16 8BC6E372 399FCCE6 F37A3D9D CDE5713B
9ECFE4F2 F9FFFCFE 7F3C9E4F A4522917 88C46231
1B8EC760 B0582C16 0B060302 0183C2E1 F3FA7DBD
DD6D3599 CF643219 8F44A2D1 6B369B4E A7502894
CAE5F17B BEDF6C36 9B4E2790 C8E47239 1F0C86C3
E2F17BBE 5FACD6EB F67B3E9F 4CA6D3EA F5F97FBC
DEEFF47A 3D1D0D85 41231289 47A0D068 341A0D05
81432211 8B462392 49A7D0E8 F47A3D9D 4DA5D1EB
76BB5E2F 148AC561 B35AAD55 A9572814 8A45A1D3
EA75391F 8CC663B2 592F148A 452193CA 65311B0E
0780C060 30188C46 23924927


Here's what was read back: 
6FB4B6DB 6E37188C 46A35229 178844A2 512B168B
46A35229 170884C2 E173BADD ED75391F 0C86C3E2
71BBDEEF 743A1D8D 4521934A 25118BC6 63B259AF
D4EAF579 BF5CAED7 68B45A2D 95492790 48241209
0780C0E0 F078BC5E 2F140A05 8143A2D1 EBF6FBFE
7F3C1E8F C4E2F1FB FE7FBCDE 6F341A8D C5E1733A
9D4DA5D1 EBF67BBE DF6CB65B 2E97C8E4 F279BFDC
EE77B85C 2E9748A4 52A957A8 D46A3519 0F048241
A352A9D7 E8F4FA7D 3D9D4DA5 512B160B 0683C2E1
F3FAFDFD 7D3D9D4D 25118B46 23924927 90482492
49A750A8 542A1509 87402090 C8E4F279 BFDC6EB7
D8ECF6FB 7EBFDC6E 37188C46 A3D2E977 381C8E47
209048A4 D2693798 CCE6733A 1D8DC561 339A4DA5
51AB562B 96CB66B3 DAED7539 1F8CC663 B2D96FB4
5AADD5E9 77381C8E C7603018 0C86C3E2 713B9ECF
E472B95F 2C168BC6 E372399F CCE6F37A 3D9DCDE5
713B9ECF E4F2F9FF FCFE7F3C 9E4FA452 291788C4
62311B8E C760B058 2C160B06 03020183 C2E1F3FA
7DBDDD6D 3599CF64 32198F44 A2D16B36 9B4EA750
2894CAE5 F17BBEDF 6C369B4E 2790C8E4 72391F0C
86C3E2F1 7BBE5FAC D6EBF67B 3E9F4CA6 D3EAF5F9
7FBCDEEF F47A3D1D 0D854123 128947A0 D068341A
0D058143 22118B46 239249A7 D0E8F47A 3D9D4DA5
D1EB76BB 5E2F148A C561B35A AD55A957 28148A45
A1D3EA75 391F8CC6 63B2592F 148A4521 93CA6531
1B0E0780 C0603018 8C462300


I don't believe I'm responsible for aligning this memory since the place that has the problem appears to be inside the chip level drivers.  Am I missing something here?
Labels (1)
0 Kudos
13 Replies

1,838 Views
molnard
Contributor I

Hi,

There is no easy solution to this. I have found a walk-around which is slower but eats less RAM and user only when the read/write buffer is misaligned. On other case the standard multiblock read is used.

/**
* @brief Read Sector(s)
* @param drv : driver index
* @param buff : Pointer to the data buffer to store read data
* @param sector : Start sector number
* param count : Sector count (1..255)
* @retval DSTATUS : operation status
*/
DRESULT disk_read (
BYTE drv, /* Physical drive number (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
UINT count /* Sector count (1..255) */
)
{
uint32_t timeout = 100000;
SD_Error sdstatus = SD_OK;
DWORD scratch [BLOCK_SIZE / 4] ; // Alignment ensured, need enough stack

portENTER_CRITICAL();

if (SD_Detect() != SD_PRESENT)
{
portEXIT_CRITICAL();
return(RES_NOTRDY);
}

if ((DWORD)buff & 3) // DMA Alignment issue, do single up to aligned buffer
{
while (count--)
{
SD_ReadBlock((BYTE *)scratch, (uint32_t )((sector + count) * BLOCK_SIZE) ,BLOCK_SIZE);
/* Check if the Transfer is finished */
sdstatus = SD_WaitReadOperation();

while(SD_GetStatus() != SD_TRANSFER_OK)
{

if (timeout-- == 0)
{
portEXIT_CRITICAL();
return RES_ERROR;
}
}
memcpy (&buff[count * BLOCK_SIZE] ,scratch, BLOCK_SIZE);
}
}
else
{
SD_ReadMultiBlocks((BYTE *)buff, (uint32_t )(sector * BLOCK_SIZE), BLOCK_SIZE, count);

/* Check if the Transfer is finished */
sdstatus = SD_WaitReadOperation();

while(SD_GetStatus() != SD_TRANSFER_OK)
{

if (timeout-- == 0)
{
portEXIT_CRITICAL();
return RES_ERROR;
}
}
}
if (sdstatus == SD_OK)
{
portEXIT_CRITICAL();
return RES_OK;
}
portEXIT_CRITICAL();

return RES_NOTRDY;
}

/**
* @brief write Sector(s)
* @param drv : driver index
* @param buff : Pointer to the data to be written
* @param sector : Start sector number
* @param count : Sector count (1..255)
* @retval DSTATUS : operation status
*/

#if _READONLY == 0
DRESULT disk_write (
BYTE drv, /* Physical drive number (0) */
const BYTE *buff, /* Pointer to the data to be written */
DWORD sector, /* Start sector number (LBA) */
UINT count /* Sector count (1..255) */
)
{
SD_Error sdstatus = SD_OK;
uint32_t timeout = 1000000;
DWORD scratch [BLOCK_SIZE / 4] ; // Alignment ensured, need enough stack

portENTER_CRITICAL();

if (SD_Detect() != SD_PRESENT)
{
portEXIT_CRITICAL();
return(RES_NOTRDY);
}

if ((DWORD)buff & 3) // DMA Alignment issue, do single up to aligned buffer
{
while (count--)
{
memcpy (scratch, &buff[count * BLOCK_SIZE], BLOCK_SIZE);

SD_WriteBlock((BYTE *)scratch, (uint32_t )((sector + count) * BLOCK_SIZE), BLOCK_SIZE);

/* Check if the Transfer is finished */
sdstatus = SD_WaitWriteOperation();

while(SD_GetStatus() != SD_TRANSFER_OK)
{
if (timeout-- == 0)
{
portEXIT_CRITICAL();
return RES_ERROR;
}
}
}
}
else
{
SD_WriteMultiBlocks((BYTE *)buff, (uint32_t )(sector * BLOCK_SIZE), BLOCK_SIZE, count);
/* Check if the Transfer is finished */
sdstatus = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK)
{
if (timeout-- == 0)
{
portEXIT_CRITICAL();
return RES_ERROR;
}
}
}
if (sdstatus == SD_OK)
{
portEXIT_CRITICAL();
return RES_OK;
}
portEXIT_CRITICAL();

return RES_NOTRDY;
}
#endif /* _READONLY == 0 */

0 Kudos

1,838 Views
zzzmqp
Contributor III

Hello,

I'm wondering if NXP support ever got back to you on this?

It had been 10+ months with 1 or 2 new releases of LPCopen and FatFS too...

I ran in the same issue and timing IS critical (pretty much maxing out the SD Card write throughput), so memcpy to align buffers won't work.

Thank you!

0 Kudos

1,838 Views
zzzmqp
Contributor III

OK, looks like there was no LPCopen release in the meantime, but two FatFs updates (which I have applied).

I'm still curious if NXP has an SDMMC driver update in the pipeline...  are any new LPCopen releases planned that may address this issue?

Would be great to get an official NXP answer here...

Thanks!

0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by briching on Sat Mar 19 19:35:40 MST 2016
I had to malloc temporary buffers and do memcpy's to perform the alignment.  A bit of a performance hit for many people, but for us it works.  I have a support request in with NXP filed when this post was created, and I still haven't heard back from them yet.  It looks like we're on our own to solve this problem.

I said I would post code a long time ago, so here it is.  Here's my read and write functions.  You should be able to adapt them to your needs quickly.

bx_TDriveResult bx_FS_SdmmcDisk_ReadSectors(bx_TDriveData *pxDriveData, byte *pbBuffer, uint32 uiSector, uint32 uiNumSectors)
{
bx_TDriveResult result = bx_drInvalidParameter;
if(pxDriveData && pbBuffer && (uiSector < bx_Sdmmc_CardGetSectorCount()) && uiNumSectors)
{
result = bx_drNotReady;
if(pxDriveData->xDriveStatus == bx_dsbNoError)
{
uint32 num_bytes = uiNumSectors * bx_Sdmmc_CardGetSectorSize();
uint32* aligned_buffer = (uint32*)malloc(num_bytes);
result = bx_drReadWriteError;
if(bx_Sdmmc_CardReadSectors(aligned_buffer, uiSector, uiNumSectors))
{
memcpy(pbBuffer, aligned_buffer, num_bytes);
result = bx_drOkay;
}
free(aligned_buffer);
}
}
return result;
}

bx_TDriveResult bx_FS_SdmmcDisk_WriteSectors(bx_TDriveData *pxDriveData, const byte *pbBuffer, uint32 uiSector, uint32 uiNumSectors)
{
bx_TDriveResult result = bx_drInvalidParameter;
if(pxDriveData && pbBuffer && (uiSector < bx_Sdmmc_CardGetSectorCount()) && uiNumSectors)
{
result = bx_drNotReady;
if(pxDriveData->xDriveStatus == bx_dsbNoError)
{

uint32 num_bytes = uiNumSectors * bx_Sdmmc_CardGetSectorSize();
uint32* aligned_buffer = (uint32*)malloc(num_bytes);
memcpy(aligned_buffer, pbBuffer, num_bytes);
result = bx_Sdmmc_CardWriteSectors(aligned_buffer, uiSector, uiNumSectors) ? bx_drOkay : bx_drReadWriteError;
free(aligned_buffer);
}
}
return result;
}


Good luck
0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by danny_com on Thu Mar 17 04:55:23 MST 2016
Hi,

I am having the same problem with the files I am writing using fatfs. Did you find any solution to this problem in LPCOpen??

Regards
0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by briching on Thu Oct 15 17:38:23 MST 2015
Yes, that's all you have to do.  Just beware you'll lose the data/filesystem on the sdcard when you run it.

By the way, I finally took the time to trace through Chan FatFs code and found out exactly why it fails.  If you don't use uint32_t pointers that are recast as byte pointers (essentially 32-bit aligned byte pointers) for every call to Chan that needs a buffer handed in (i.e. a call to f_open), then it will fail 3 out of 4 times, assuming an even distribution of addresses when you declare your buffers dynamically.  We have so many places in our legacy code that byte pointers are used that they cannot all be re-declared as uint32 aligned in any reasonable amount of time.  Besides that, Chan is supposed to support byte pointers directly.  The problem is lpcopen's implementation of the SDMMC drivers which use DMA throws out bits [1:0] on the address of the buffer that you hand in.  This makes it not fully compatible with Chan.  And no mention of this anywhere in the lpcopen docs.  Extremely frustrating.

However, if you are already using uint32_t as the type of your buffers you pass to Chan (i.e. with a call to f_open) you will never see this problem in your app code.
0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by schisanoa on Thu Oct 15 05:36:59 MST 2015
I'll check for it the next week, should I call this function:

result = test_diskio(0, 10, buff, sizeof buff);

right?

Sorry but before next week I can't check it
0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by briching on Sat Oct 10 16:14:42 MST 2015
Here is a complete example showing how the SD/MMC drivers provided with lpcopen fail Chan FatFs disk-io layer check utility.  Take 5 minutes and run it on your hardware to see.
0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by briching on Thu Oct 08 21:52:12 MST 2015
If you haven't solved the USB/DMA issue, that is something I know a little about.  I found the USB was starving the LCD fifos and causing fifo underflow interrupts.  In your case it could be starving other data paths using DMA as well.  The fix involves writing an undocumented register to tell the USB DMA controller how it wants to handle DMA transactions.  See here

I would have a project to upload to test the periph_sdmmc problem, but I am in the hospital with mom and a new baby.

Hope that helps,
Brad
0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by schisanoa on Thu Oct 08 01:54:06 MST 2015
I will double check it in the next weeks, but I think everything should already be ok. I only experienced some problem on the USB mass storage layer interfaced with the SD, and only with the DMA enabled. However let me know if you find the exact problem and if you reach to fix it;

BR
Alessio
0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by briching on Wed Oct 07 12:26:05 MST 2015
Are you sure it works properly?  Before you release you might want to run Chan's disk-io test bench on your disk io layer.  Ours worked fine until we had to do more complicated things that stressed the file system.  The example by itself (periph_sdmmc) works just fine, but if you try incorporating it into production code or running the disk-io utility (again provided by Chan to make sure your drive API is up to snuff) you may find otherwise.  I didn't believe it until I ran that test myself.

Also, I have determined there is no easy way to fix this other than re-create the chip-layer for the sd-card, or edit the chip-layer directly (which is something I would like to avoid since it is supposed to be supported by NXP.)

When I get a chance I will upload the periph_sdmmc project with the driver tester hooked up so anyone can download it.  I'm hoping someone else has a EA lpc4357 OEM board to test it out on, that way they won't have to swap out the board layer.

Thanks for the reply by the way. 

Best,
Brad
0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by schisanoa on Wed Oct 07 01:22:55 MST 2015
I'm working with a LPC4088, with SD and elm Chan FatFS, and it works right. Both the example provided from nxp in LPC Open 2.xx are working on the Evaluation board, and on my own board I upgraded the FatFS with the last version from Elm Chan and everithing works good.

By the way, try to contact the NXP support on NXP website, it is not always very fast, but they usually are very helpful.
0 Kudos

1,838 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by briching on Tue Oct 06 13:03:33 MST 2015
I guess since no one else has had a problem with this, they are not using the SD card in production with Chan FatFs, or they have fixed the issue in their own disk_io layer.  In the meantime I will plan on doing the same, but people outta at least know that the example (periph_sdmmc) does not implement a compatible Chan FatFs disk_io layer.  The user will have to manage misaligned addresses on their own with a suitable temporary buffer and a healthy dose of memcpy's.  As soon as I fix it I will post for everyone to see.

If this is not slated to be fixed in a new release of LPCOpen, I suggest that at least the signature of the routines that accept void pointers be changed to uint32 pointers so that someone else doesn't spend an afternoon trying to figure out why it doesn't work as advertised.
0 Kudos