timanu90
Posts: 58
Joined: Sat Dec 24, 2016 11:54 am

SD Card controller

Wed Aug 16, 2017 7:30 pm

Hi guys,

I want to have SD card access, I am trying to understand the command sequence. I tried to reverse engineer this driver:

https://github.com/yrakcaz/RasPiK/blob/ ... rts/emmc.c

I want to understand the command sequence and timings (seems that timings are important) but I cant any reliable source. I tried the code above but no luck.

Code: Select all

T_STATUS bcm2837_emmc_read_blocks(T_UINT32 block_num, T_UINT32 block_cnt, T_BYTE* buffer)
{
    T_STATUS ret = OK;
    T_UINT32 i;

    /* Set block size and number of blocks to transfer */
    emmc->BLKSIZECNT.bits.blksize   = 512;
    emmc->BLKSIZECNT.bits.blkcnt    = block_cnt;

    emmc->ARG1 = block_num;

    /* 17 is read single block command */
    T_EMMC_CMDTM_REG cmd;

    cmd.bits.TM_DAT_DIR = 1;
    cmd.bits.CMD_CRCCHK_EN = TRUE;
    cmd.bits.CMD_RSPNS_TYPE = 2;
    cmd.bits.CMD_ISDATA = TRUE;
    cmd.bits.CMD_INDEX = 17;

    emmc->CMDTM.all = cmd.all;

    kprint("emmc->INTERRUPT.bits.CMD_DONE: %d\n\r", emmc->INTERRUPT.bits.CMD_DONE);

    /* Wait cmd to be processed */
    while ( FALSE == emmc->INTERRUPT.bits.CMD_DONE );

    /* Clear the interrupt */
    emmc->INTERRUPT.bits.CMD_DONE = ONE;

    /* Print response for dbg */
    kprint("EMMC :: RESP0: 0x%x\n\r", emmc->RESP0);

    T_UINT32* ptr = (T_UINT32*)buffer;

    for(i=ZERO; i< 512; i+=4) {
        *ptr = emmc->DATA;
        ptr++;
    }

    return ret;
}
With this code I try to read the block 0, where I expect the partion table on the SDCard, but I got all 0s.

Does anyone know some resources that explain the steps needed by the SDCard to work so I can try figure out the sequence and timings of the commands I write on the controller.

Thanks in advance
Tiago

LdB
Posts: 568
Joined: Wed Dec 07, 2016 2:29 pm

Re: SD Card controller

Thu Aug 17, 2017 3:53 am

The first block isn't the partition table it is the disk master boot record and it is mainly zero's 446 of them and then 4 partition table descriptors.

Check the sig is 0xaa55 and then read the 4 partition block descriptors to find the partition tables.

Code: Select all

/*--------------------------------------------------------------------------}
{				     MASTER BOOT RECORD BLOCK STRUCTURE					    }
{--------------------------------------------------------------------------*/
// Structure to access Master Boot Record for getting info about partitions
struct __attribute__((packed)) MBRinfo_struct {
	uint8_t					nothing[446];	// Filler the gap in the structure
	struct partition_struct partitionData[4];// partition records (16x4)
	uint16_t				signature;		// 0xaa55
};
The partition block descriptors are each 16 bytes so there are 48 bytes and the sig bytes of interest in that whole first block everything else is pretty much zero. Oh and on the Pi2/3 446 bytes is unaligned so be careful just jamming a struct pointer to it as you will trigger an unaligned access fault.

Code: Select all

/*--------------------------------------------------------------------------}
{				    PARTITION DECRIPTION BLOCK STRUCTURE				    }
{--------------------------------------------------------------------------*/
//Structure to access info of a partition on the disk
struct __attribute__((packed)) partition_struct {
	uint8_t		status;						// 0x80 - active partition
	uint8_t		headStart;					// starting head
	uint16_t	cylSectStart;				// starting cylinder and sector
	uint8_t		type;						// partition type (01h = 12bit FAT, 04h = 16bit FAT, 05h = Ex MSDOS, 06h = 16bit FAT (>32Mb), 0Bh = 32bit FAT (<2048GB))
	uint8_t		headEnd;					// ending head of the partition
	uint16_t	cylSectEnd;					// ending cylinder and sector
	uint32_t	firstSector;				// total sectors between MBR & the first sector of the partition
	uint32_t	sectorsTotal;				// size of this partition in sectors
};
I found the Pi doesn't seem to care about the active partition it always uses partition 1 when booting. You can't seem to use multiple boot partitions on it. There have been a lot of firmware changes since and you can kick from USB now, so not sure if it's still true as I haven't tried in a while.

timanu90
Posts: 58
Joined: Sat Dec 24, 2016 11:54 am

Re: SD Card controller

Thu Aug 17, 2017 7:39 am

Yes I was expecting to find the value 0xaa55 on the last 2 bytes of the read block, but I got full 0s. Thats why I was asking if there are any timings that I must respect, for example between writing commands to the host controller.

LdB
Posts: 568
Joined: Wed Dec 07, 2016 2:29 pm

Re: SD Card controller

Thu Aug 17, 2017 8:21 am

There are no special timings for the Pi the only applicable timings are on the SD card as per the specs which you will already have no doubt from https://www.sdcard.org. The SD clock on the Pi is 41.66667Mhz it changed in a firmware a while ago and is still often quoted wrong. The Pi can also not do 1.8V drop down it is strictly 3.3v.

I have now debugged 2 sets of SD code for people as well as doing my own so I am not going to get involved in another rewrite.

moizumi99 still has his last code up on githib which is in nice linear C code in a single file and header, and I know works as I helped debug
https://github.com/moizumi99/RPiHaribot ... e/sdcard.c

You could start there as I can vouch it is correct or I have assembler code if you would prefer that. It doesn't have the interrupt connected but that is very trivial to add if you want it.

I can also give you FAT32 reading in a single linear C file or assembler if you need.

timanu90
Posts: 58
Joined: Sat Dec 24, 2016 11:54 am

Re: SD Card controller

Thu Aug 17, 2017 11:28 am

Thank you I will take a look on this driver and try put mine to work. About the FAT32 I have my own code, but I would like to get the generic file name working instead of that strange one. Your code work with any file name with any length?

LdB
Posts: 568
Joined: Wed Dec 07, 2016 2:29 pm

Re: SD Card controller

Thu Aug 17, 2017 2:03 pm

I assume you are talking about Long File Names (LFN) and yes it works with them but they are trivial to add if you have existing code. You just need the locale from the standards C library to convert the UTF16 characters to ascii8. I will however make you aware the FAT32 and LFN is still patented to Microsoft and if you are doing commercial stuff you need a license.

http://en.swpat.org/wiki/Microsoft_FAT_patents

https://www.google.com/patents/US5579517?dq=5,579,517
https://www.google.com/patents/US5758352?dq=5,758,352

TOMTOM the mapping embedded unit is the most famous to fall foul of it. Last cost I got was around $0.30 USD a unit, so it's not to bad but you just file a request from pricing with MS.

I know the issue had even been discussed with the Pi itself but not sure if they ever resolved it.
viewtopic.php?f=2&t=511

LdB
Posts: 568
Joined: Wed Dec 07, 2016 2:29 pm

Re: SD Card controller

Thu Aug 17, 2017 2:24 pm

This is all you should need to read the LFN
If you want 8 byte ascii to your locale you need these two C standard libraries

#include <wchar.h> // Needed for UTF for long file name support
#include <ctype.h> // toupper

SetLocale to the language you want back and read the up to 13 chars per FAT entry until LFN name ends.
https://www.tutorialspoint.com/c_standa ... locale.htm
wctob does all the conversion work
http://en.cppreference.com/w/c/string/multibyte/wctob

Code: Select all

/*-ReadLFNEntry--------------------------------------------------------------
 Transfers the up to 13 characters from the LFN directory entry to the buffer
 and returns the count of characters placed in the buffer. The first five
 characters in Name1 have alignment issues for the ARM 7/8, care needed. 
 18Feb17 LdB
 --------------------------------------------------------------------------*/
static uint8_t ReadLFNEntry (struct dir_LFN_Structure* LFdir, char LFNtext[14]) {
	uint8_t utf1, utf2;
	uint16_t utf, j;
	bool lfn_done = false;
	uint8_t LFN_blockcount = 0;
	if (LFdir) {													// Check the directory pointer valid
		for (j = 0; ((j < 5) && (lfn_done == false)); j++) {		// For each of the first 5 characters
			// Alignment problems on this 1st block .. please leave alone
			utf1 = LFdir->LDIR_Name1[j * 2];						// Load the low byte  ***unaligned access
			utf2 = LFdir->LDIR_Name1[j * 2 + 1];					// Load the high byte
			utf = (utf2 << 8) | utf1;								// Roll and join to make word
			if (utf == 0) lfn_done = true;							// If the character is zero we are done
				else LFNtext[LFN_blockcount++] = wctob(utf);		// Otherwise narrow to ascii and place in buffer
		}
		for (j = 0; ((j < 6) && (lfn_done == false)); j++) {		// For the next six characters
			utf = LFdir->LDIR_Name2[j];								// Fetch the character
			if (utf == 0) lfn_done = true;							// If the character is zero we are done
				else LFNtext[LFN_blockcount++] = wctob(utf);		// Otherwise narrow to ascii and place in buffer
		}
		for (j = 0; ((j < 2) && (lfn_done == false)); j++) {		// For the last two characters
			utf = LFdir->LDIR_Name3[j];								// Fetch the character
			if (utf == 0) lfn_done = true;							// If the character is zero we are done
				else LFNtext[LFN_blockcount++] = wctob(utf);		// Otherwise narrow to ascii and place in buffer
		}
	}
	LFNtext[LFN_blockcount] = '\0';									// Make ascizz incase it needs to be used as string									
	return LFN_blockcount;											// Return characters in buffer 0-13 is valid range
}

timanu90
Posts: 58
Joined: Sat Dec 24, 2016 11:54 am

Re: SD Card controller

Thu Aug 17, 2017 3:26 pm

Thank you very much. When I have my SDCard driver or wrapper working I will look to this.

LdB
Posts: 568
Joined: Wed Dec 07, 2016 2:29 pm

Re: SD Card controller

Fri Aug 18, 2017 4:56 am

No worries.

I have one major whinge at the code I wish people writing baremetal for the Pi would stop using int's to hit hardware registers. The moment you change to 64bit compiling it writes to the next register as well because an int is 8 bytes. I am going thru and changing them to uint32_t from the c standard and removing a lot of the external dependencies. I will toss up a link to my repo when done.

LdB
Posts: 568
Joined: Wed Dec 07, 2016 2:29 pm

Re: SD Card controller

Wed Aug 23, 2017 5:44 pm

Okay I fixed up the SD card software but that code has something strange it plays with the CSD something my old assembler code doesn't go near.

There is something weird I am missing with the CSD, I am getting the data clean and correct and I know about MSB and the CRC being chopped on the Pi.

So when I read a SANDISK 16M ultra I get the CSD of
400e00325b59000073a77f800a4000xx (xx is where CRC would be)

from here http://goughlui.com/other-pages/sdsdhcs ... -database/ they get
400e00325b590000edc87f800a4040c3 SANDISK ULTRA 32Gb UHS-I microSDHC

So pretty dam close to same.

However whenever I feed those into the specification I get a weird C-Size. I tried a couple of code blocks from linux drivers that extract the CSD and they get it wrong.

However this site http://goughlui.com/static/csdecode2.htm converts the code to a proper C-Size but what I notice is it gobbles all the bits directly as 0x0073a7 = [Decimal: 29607] but that is those middle 3 bytes 00 73 a7. What happened to all the current draw stuff in the spec.

Every variation of specification I can find doesn't say it works like that can anyone shed some light on it for me?

Return to “Bare metal”

Who is online

Users browsing this forum: No registered users and 7 guests