6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 10:22 pm

All times are UTC




Post new topic Reply to topic  [ 45 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: SD Card interfacing
PostPosted: Wed Sep 25, 2013 8:53 pm 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
SD Cards have mentioned in a number of topics recently. My personal interest is to interface an SD card to my CHOCHI board http://forum.6502.org/viewtopic.php?f=10&t=2644. Currently it has a serial loader, but it would be nice to be able to boot from an SD card if it's attached.

The last time I tried to interface an SD card I did in in hardware, using a shift register on an FPGA and a Picoblaze core to read data. I don't remember the details other than it was very touchy, worked with a small selection of cards I have on hand, and was an incredible pain in the butt (prone to not working for no reason, then working again). I somehow got it working well enough for the project, but had always meant to go back at figure it out. So here goes.

This time I decided to bitbang SPI (since I'm running at 45MHz I can do it pretty fast). SD Cards are pretty lax about operating speed - at least some of my cards initialize at 2Hz!. Also, clock jitter seems to not matter much as far as I can tell. With software I can monitor the signals and make adjustments easier. And I can share the results - this can be done with any 6502 that has 3 output lines and 1 input lines available.

There is a lot of conflicting information about connecting SD cards, and the flavor of SPI to be used. Some indicate mode 0, some mode 3. I finally got consistent results using mode 3 - clock is normally high, transitions happen on the falling edge, and are sampled on the rising edge.

First thing: send 74 clocks to the card:
Code:
;--------------------------------------------------------------
; Cold-start procedure - 74+ clocks             
sd_cold:
              jsr       sd_clk_up         ;normal=1
              jsr       sd_data_up        ;normal=1
              jsr       sd_sel_dn         ;assert SEL
sd_init:
              jsr       tick
              ldx       #74               ;output 74 clocks
sd_init_loop: jsr       sd_clk_dn
              jsr       tick
              jsr       sd_clk_up
              jsr       tick
              dex
              bne       sd_init_loop
              jsr       tick
              jsr       sd_sel_up
              ; For some reason, it really helps to send 8 more
sd_init1:     
              lda       #$FF
              jsr       sd_op
               rts

Now, to send/receive a byte, a shift register following SPI mode 3 rules (I think...)
Code:
;--------------------------------------------------------------
; read/write a single byte.             
sd_op:     
              sta       TEMP            ;preserve byte
              ldx       #8
sd_op1:
              jsr       sd_clk_dn
              ;---------------------------------
              ; output a bit and shift TEMP
              lda       TEMP            ;load rcv/transmit byte
              sta       SD_MOSI         ;store high bit to SPI
              clc
              rol       a               ;<< Free up low bit
              sta       TEMP            ;and save
               ;---------------------------------
              jsr       tick
              ;---------UP----------------------
              jsr       sd_clk_up
              ;---------------------------------
              ; input low bit
              lda       SD_MISO         ;read low bit from SPI
              ora       TEMP            ;or in current value
              sta       TEMP            ;and save back
              ;---------------------------------
              jsr       tick
              ;---------------------------------
              dex
              bne       sd_op1
              jsr       tick
              jsr       sd_data_up      ;data up is normal
              lda       TEMP            ;return value
              rts

In order to use the code above, some obvious routines matching your hardware must be built. I use bit 7 of the output port and bit 0 of the input port (I have an FPGA); adjust the code to match your hardware.

After sending a command, the response has the high bit 0, but first, one or more $FF may be sent. So I send $FF and check return values like this:
Code:
;--------------------------------------------------------------
; wait for an R0 response.  R0 has bit 7 clear...
sd_R0:        lda       #$FF            ;send $FF
              jsr       sd_op
              rol       a               ;high bit set?
              bcs       sd_R0         ;continue waiting
              ror       a
              rts


This should be enough to initialize the card like this:
Code:
init:         jsr       sd_cold                 ;intialize the SD card
              jsr       sd_sel_dn               ;select
              lda       #$40                    ;CMD0 - reset and idle
              jsr       sd_op
              lda       #$00
              jsr       sd_op
              lda       #$00
              jsr       sd_op
              lda       #$00
              jsr       sd_op
              lda       #$00
              jsr       sd_op
              lda       #$95
              jsr       sd_op

              ldy       #8
init1:        lda       #$ff
              jsr       sd_op
              jsr hex_out
              dey
              bne       init1
              jsr cr
              jsr cin
xxx: jmp init         

The code above resets the card and sends command 0 to put the card into idle state. It then outputs the next 8 bytes returned.

Here is where trouble starts. Out of 4 cards I have on hand, only one returns the expected
Code:
FF 01 FF FF FF FF FF FF
.
Another card returns
Code:
3F 01 FF FF FF FF FF FF
.
3F has a low high bit, so it would be confused with a proper R0 response byte... However, running init again returns
Code:
BF 01 FF FF FF FF FF FF
.
This is disturbing, but more acceptable (BF is meaningless and would be filtered).

This reminds me of the trouble I had the last time... I could just skip any bytes with high bit set, and re-initialize if the response is not 01. However it could be indicative of some kind of a problem with the shift registers getting out of sync. It might bite me later...

I will proceed as if it makes sense to re-initialize until a reasonable response is obtained.

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Wed Sep 25, 2013 10:06 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
That's what I do in my code. Just keep sending the GO_IDLE_STATE command, read a couple of bytes until something != FF is received, and then compare next byte with 01. If not equal, send another GO_IDLE_STATE.

This is what the Hitachi manual says:

1. drives CS high, making the card nonactive,

2. issues at least 76 dummy clocks for MMC initialization, and

3. drives CS low and transmits a CMD0 command (GO_IDLE_STATE).
Note: At this point, the card is still in MMC mode, and so a CRC is necessary. The command
0 format is “40, 00, 00, 00, 00, 95”, of which “95” is the CRC.
Also, according to the MMC specification, an MMC in the Idle state only accepts
CMD0 and CMD1. If the user wishes to turn the CRC function on or off with CMD59,
this must be done after issuing CMD1.

4. At this point the mode changes to SPI mode. The host waits for an R1 response from the
MMC.

5. If the R1 response is 01h, the sequence proceeds to step 8; if the R1 response is a value other
than 01h, an error is judged to have occurred and error handling is performed. Processing
might generally be to provide an error indication or to re-execute the sequence starting from
powering-on.


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Wed Sep 25, 2013 10:08 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
The SD card spec is very poor and confusing, and not all cards follow the spec exactly. For instance, there's a command to turn off CRC checking, but not every card supports it. It is better to leave CRC checking on, and just add a CRC to every command.


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Wed Sep 25, 2013 10:23 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
Before sending a command byte you should always send a FF dummy byte. In the spec this period is referred to as Ncs, specified with a minimum of 0 bytes with FF value, so theoretically you could skip it, but some cards don't work without it.


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Wed Sep 25, 2013 11:22 pm 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
Arlet wrote:
Before sending a command byte you should always send a FF dummy byte. In the spec this period is referred to as Ncs, specified with a minimum of 0 bytes with FF value, so theoretically you could skip it, but some cards don't work without it.

Should the dummy byte be sent before or after asserting (setting to 0) the CSEL line?

Experiencing much flakiness across different cards...

EDIT: one of the cards indeed required an dummy byte (before asserting CSEL low) in order to take CMD1. There is a ridiculous number of different ways to initialize, but all my cards respond to CMD1. Next I will attempt to read sectors.

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Thu Sep 26, 2013 4:18 am 
Offline
User avatar

Joined: Sun Dec 29, 2002 8:56 pm
Posts: 449
Location: Canada
I'm assuming that interrupts are disabled when the code is being run ?

As an alternate solution to bit-banging the spi, I'm just using a modified version of the spi_master core found on OPencores as an SD card controller. It seems to work okay. I'm not sure if i'd fit in the CHOCHI board, though.

Slice Logic Utilization:
Number of Slice Registers: 564 out of 54576 1%
Number of Slice LUTs: 876 out of 27288 3%
Number used as Logic: 873 out of 27288 3%
Number used as Memory: 3 out of 6408 0%
Number used as SRL: 3

_________________
http://www.finitron.ca


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Thu Sep 26, 2013 4:50 am 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
You've got to be kidding - it's just a shift register! What could possibly take up that much space?

I have an SPI core that fits in 15 XC3S slices. I chose not to use it because I wanted to get to the bottom of the problems I was having with some SD cards not working. It is mostly a software issue, and this way I can share it with non-fpga users. Later I can always insert the SPI core.

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Thu Sep 26, 2013 5:00 am 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
enso wrote:
Should the dummy byte be sent before or after asserting (setting to 0) the CSEL line?


After. But notice that the >= 74 clock cycles in the beginning should be sent with CS deasserted. After that, you can assert CS, and never touch it again. It's fine to leave it asserted the whole time.

Quote:
Next I will attempt to read sectors.


Not so quick. You need to find out the card type. There's MMC, SDv1, SDv2, and SDHC, each with their little differences.

Send CMD8 with arg = 00 00 01 AA. If you don't get an ILLEGAL_COM error, it's an SDv2 card.
Otherwise, send CMD58 to read OCR (4 bytes), and then send ACMD41 with arg = 0. If you get ILLEGAL COM error, it's an SDv1, otherwise it's MMC. To send ACMDxx you first need to send CMD55 with ARG=0 to announce application specific command.

After this, you need to wait until card is ready.

For MMC, send CMD1, arg 0.
For SDv1 send ACMD41, arg 0
For SDv2 send ACMD41, arg 40 00 00 00 (host supports v2)

Repeat sending this command until you get R1=00 response. For some cards, this can take a considerable amount of time.

When you're done with all of this, you still need to check to see if the card is SDHC type. The difference is that SDHC uses sector number to access the card, and the others use byte offsets. For SDHC, the byte offset is too big. You only need to do this check if the card is SDv2.

Send CMD58 (read OCR), and read 4 bytes. If first byte has bit 6 set, it's an SDHC card.

Then send CMD59 arg 0 to turn CRC off (but keep sending valid CRC7)
Send CMD16 arg=512 to set sector length to 512.

In my code I also read CID, CMD10, but I'm not sure if that's required, or if I was just experimenting.


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Thu Sep 26, 2013 5:12 am 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
enso wrote:
You've got to be kidding - it's just a shift register! What could possibly take up that much space?


Flexibility. You probably can use the module in 1000 different ways, so you first have to spend time to configure it in the one and only way you're going to use it. Of course, if you're only going to use it in one way, might as well use a simpler module. :)


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Thu Sep 26, 2013 6:39 am 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
Interesting. I've never seen the 'assert CS once' method.

So, I have 5 different brands of cards working with command 1 to initialize it.

I've been sending 74 clocks with CS asserted, and it works. At one point I tried it the other way, and it seems to work better with CS low. I can't remember why now.

I've been sending the dummy byte before each command, outside the CS bracket. That seems to work.

Curiously, sending command 1 to some cards returns 5 (invalid command), but trying it again returns 0...

I am not sure why they call this nonsense a standard - it's really all over the place.

Oh, as far as the giant SPI core goes - I really don't understand why someone would want to sacrifice that much hardware for the 'flexibility' of using it in different ways. I find it hard to envision the situation. It's pretty easy to configure a different bitstream for a different job - it's not a million dollar silicon mask.

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Sun Oct 13, 2013 7:02 pm 
Offline
User avatar

Joined: Sun Nov 28, 2004 3:07 pm
Posts: 28
Location: Budapest, Hungary
I'm also thinking on SD-card interfacing (and other SPI devices btw) but not with bit-banging which seems to be too slow for my taste. What I could think till now:

Option1: Let's use an MCU (like a low pin count number AVR) which supports SPI nicely. Problem: interfacing with the CPU, it's really hard even for a 20MHz clocked AVR to catch the CPU's read/write request even if it's clocked on much lower frequency. Of course I can use latch/buffer/whatever, but then the elegant "only a smaller DIP packaged AVR" solution will be lost. With MCUs having many pins it's even possible to have a DMA like solution (stopping CPU and allow the MCU to address RAM and store/read bytes), but I feel this a bit too complex solution.

Option2: Let's use a dedicated SPI interface IC. Unfortunately there is not so many ... Unlike UARTs, they're quite rate. To be precise I only know about 65SPI, but it would be harder/more expensive to get one from Hungary, also maybe it's too 65xx specific, I'd like something I can use in my Z80 projects as well. I don't know if 65SPI can be used with outer CPUs easily or not.

Option3: Build a light SPI interface on generic 74xx items. As far as I can understand, the situation is not so complex, you need a parallel-to-serial and serial-to-parallel shift register maybe latch/buffer and some clock source. I think it would be the nicest solution for my needs, especially because I would only use SPI mode 0, I don't need the full potential of 65SPI at all. Unfortunately I couldn't find any resource which is about building a simple SPI interface from simple logic ICs. I am also not sure if I now underestimate the complexity of this project (even with the constraint to support only a single mode, etc).

Please note, that though it's an SD-card topic, I can have the idea to also use with ENC28J60 ethernet controller or some other too.

I am interested in others' opinion on my post, and I would be quite happy if someone can suggest resources to build something mentioned at the third option. The Z80 is here since I have computers (not home built though) based on the Z80 too, and I'd like to create a solution which is more or less compatible with those too, well at least after some magic on the control bus signals to bridge the difference :)

Thanks!


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Sun Oct 13, 2013 7:06 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
You could use a CPLD to implement SPI.


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Sun Oct 13, 2013 7:32 pm 
Offline
User avatar

Joined: Sun Nov 28, 2004 3:07 pm
Posts: 28
Location: Budapest, Hungary
Arlet wrote:
You could use a CPLD to implement SPI.


Surely, I guess this is the 65SPI, that's a CPLD for real, programmed for this purpose. The only problem that I don't know anything about GALs/CPLDs/FPGAs and also I failed multiple times to learn VHDL/Verilog, simply it looks alien for me ... :( That's why I started to think about a discrete generic 74xx parts to implement an SPI interface with only a fixed mode. As far as I can estimate, it wouldn't be too complex not to worth to try. But I can be wrong, of course!


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Sun Oct 13, 2013 7:40 pm 
Offline
User avatar

Joined: Sat Sep 29, 2012 10:15 pm
Posts: 899
Bit-banging SPI is very simple, and SD cards have very lax timing requirements. I've driven SD card clocks by hand on one occasion!

This is the code I've been using on my 45MHz 6502 CHOCHI board. I am sure it could be improved:
Code:
;--------------------------------------------------------------
; read/write a single byte.             
;  uses TEMP to keep the byte.
sd_op:
              sta       TEMP            ;preserve byte
              ldx       #8
sd_op1:
              ;---------------------------------
              ; First, output a bit and shift TEMP
              lda       TEMP            ;load rcv/transmit byte
              sta       SD_MOSI         ;store high bit to SPI
              clc
              rol       a               ;<< Free up low bit
              sta       TEMP            ;and save
              ;---------------------------------
              jsr       sd_clk_dn
              ;---------------------------------
              jsr       tick
              ;---------------------------------
              jsr       sd_clk_up
              ;---------------------------------
              ;---------------------------------
              jsr       tick
              ;---------------------------------
              ; input low bit
              lda       SD_MISO         ;read low bit from SPI
              ora       TEMP            ;or in current value
              sta       TEMP            ;and save back

              dex
              bne       sd_op1

              jsr       tick
              jsr       sd_data_up      ;data up is normal
              lda       TEMP            ;return value
              rts

_________________
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut


Top
 Profile  
Reply with quote  
 Post subject: Re: SD Card interfacing
PostPosted: Sun Oct 13, 2013 7:43 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
Bit banging is not too difficult, but it isn't particularly fast either, especially for folks that don't have a 45 MHz CPU.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 45 posts ]  Go to page 1, 2, 3  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 29 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: