Shop OBEX P1 Docs P2 Docs Learn Events
MMC SD card reader (with an assembly problem) — Parallax Forums

MMC SD card reader (with an assembly problem)

David BDavid B Posts: 592
edited 2006-07-02 02:34 in Propeller 1
Here is an SD/MMC card reader.

Its funny - I just saw Rokiki's thread as I was about to post my project, and see that his project is very similar to mine. My spin code is a lot wordier, and my read speed via spin is a little slower - 1k per second, but this will read both MMC and SD cards.

I wired an MMC/SD card socket to my homemade Propeller board. (The board also has a Seetron 2-line 20-char LCD display and a 7-segment LED display used in this program.)

The program to access the cards is still in a primitive state, but already does quite a bit. It reads both MMC and SD cards, figuring out the card type. It locates the filesystem, figuring out whether the card uses partitions with an MBR or not. It finds a whole bunch of card and filesystem parameters - total sectors, filesystem sectors, whether FAT12 or FAT16 is used, sizes and locations of the FAT and the root directory, etc.

It's got the beginnings of a routine that finds a specific file in the root directory, then lists its contents, tracking its allocated sectors via the FAT allocation table. I used Perl to create a "demo" text file "SPINFILE.TXT" 5120 bytes (10 sectors) in size, where each block of 512 bytes begins with text like "This is sector five", "This is sector six",... and used a commercial card writer to copy the file to all four of my test cards. It helped immeasurably in debugging the code.

With purely spin card IO pin access, running at 64 mHz, it takes about half a second to read a 512 byte sector from the card. So I made an assembly routine to run in another cog for doing all the bitwise operations on the card, and let Spin functions do all byte-sized and up manipulations. That sped up access to about 6 milliseconds to read 512 bytes from a sector (fast enough for MP3 access!).

BUT - there is a problem. The assembly routine works perfectly sometimes but doesn't work properly other times, and I can't figure out what's wrong.

For a given version of the program, if the assembly works, it is reliable and will work every time. But if I make a change to the code, even one that should have no effect on the operation, then it may cause the assembly to fail. And when it fails, it's consistant; a version that fails will fail every time. As I developed this program, it seemed like, for every change I made to the code, it was random chance whether the assembly was going to work or not. First it would work fine, then I'd add another display line and it would fail. Then I'd add a subroutine and it would work again. And so on,...

When the assembly fails, the indication is that the card seems to return all $FFs. The assembly cog starts, runs, and responds fine, but there is no card response.

This got so aggravating that I made a "FAST" flag at the top of the code. When "FAST" is true then Spin will use the fast assembly routine. When "FAST" is false, Spin will use the slow Spin card access routines. When not running in "FAST" mode, the program is extremely reliable, although a bit sluggish. But when set to use FAST mode, the program is almost unuseable until this bug is figured out.

It happens that the version of the code included here DOES work in FAST mode. To demonstrate the strangeness of the problem, there is a simple delay call in the main routine. If that delay is uncommented, the assembly cog IO fails. Comment it, and the cog IO works. That's just one simple example of how touchy the problem I'm seeing is.

Can anyone see what is wrong? Am I missing something simple?

My best guess at this point is that something is blocking the IO pins from sending or receiving under some circumstances, even though I think I properly released the cog 0 control of the IO lines. Maybe this weekend I'll connect a digital pulse detector to the card socket and test out that theory.

David

Comments

  • rokickirokicki Posts: 1,000
    edited 2006-06-28 22:26
    I'll try this tonight when I get home.

    Is it possible the variable sendGetByteArg is not being initialized?
    This sort of thing sounds like an uninitialized data problem.

    All those nops in the assembly are worriesome; is MMC really that
    slow?

    Cool setup, though. I'd love to see the underside of that board.
  • rokickirokicki Posts: 1,000
    edited 2006-06-29 05:27
    Hmm, worked great for me (after removing all the LCD lines and changing
    them to TV_Terminal lines) with or without the delay, at 80MHz
    (crystal), and even with only a single NOP in each of the three delay
    sections at the end, for both MMC and SD cards.

    At this point, I think it's time to get out the scope and look at the
    signal lines. It's possible it's something as simple as ground bounce
    affecting the integrity of the signals (and this can be affected by
    changing code a bit, possibly).

    Essentially, I can't get it to fail.

    What are your connections? Are they wirewrap? Do you have
    bypass caps all over the place? Especially the power
    connections are of concern.

    I tested it on the same setup in my thread on the SD card.
    My MMC card may be more recent than yours and possibly
    capable of higher speeds, but the program itself is, or seems
    to be, working just fine.

    Maybe give us all a picture of the underside of your setup
    and we can all speculate. Also, try different speeds (you are
    currently using 16x; try 8x, 4x, 2x, and see if they work).

    Post Edited (rokicki) : 6/29/2006 5:36:11 AM GMT
  • David BDavid B Posts: 592
    edited 2006-06-29 15:10
    Thanks for testing it out.

    Heres a shot of the board bottom. Most connections are wire-wrap, but I do have a big ground bus that all the parts tie to, and smaller +3.3 and +5 busses with plenty of tantalum and ceramic bypass caps.

    I wouldn't think the problem is hardware since, for a given version of the program, it is completely reliable; it either works perfectly, every time, or it fails every time, with only a small software change being the difference. But then there's always something new to learn in these projects - maybe·some sort of signal bounce or echo·is the problem.

    Last night I tried initializing sendGetByteArg before starting the cog; no difference.

    For those nops, I just took a working instance of the code and one by one, commented out nops until it failed, then added nops until it succeeded, then added a few more for a safety margin. First I trimmed nops for an SD card, then found the MMC required quite a few more nops. I'm curious how the resulting data transfer rate compares to the card specs, but haven't worked out any numbers in that regard.

    I'll try changing the clock rate and see what happens.

    David
    2816 x 2112 - 745K
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-06-29 16:11
    My propeller assembly is rather weak, fledgling to be concise, but given what your saying, I would like to offer the following:
    In previous assembly languages, the org statement needed (was not optional) an address. From looking at your code, it appears this is not the case.
    Maybe something compiles differently each time, causing the address to move?
    Something else comes to mind, cognew needs a stack, your sending it the pointer to your prams, maybe it needs more space?
    ...

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Just tossing my two bits worth into the bit bucket


    KK
    ·
  • David BDavid B Posts: 592
    edited 2006-06-29 22:11
    This Propeller assembly is new to me, too, and there's definately a learning curve involved, with things like having to build masks by shifting bits, and having to specifically indicate that certain flags are to be set by a command, not to mention the new hardware with the remote hub data, and the counters.

    Other people's assembly examples used "org" with no arguments, so I did, too. But I just tested both with and without "0", and my code still shows that same behavior of working, then failing if I make minor tweaks to the code.

    I checked the Spin manual - when starting an assembly cog, there is no stack argument; just the assembly routine and the pointer to an argument.

    But thanks for the tips - I'll get this thing working eventually...

    David
  • Kaos KiddKaos Kidd Posts: 614
    edited 2006-06-30 13:27
    David..
    I tried!
    I'm having Assembly "issues" as well, but like you said, "I'll get this thing working eventually..."

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Just tossing my two bits worth into the bit bucket


    KK
    ·
  • SSteveSSteve Posts: 808
    edited 2006-06-30 16:47
    This is unlikely affect your problem, but instead of
    waitcnt(cnt + us * 64)
    

    you should use
    waitcnt(us * 64 + cnt)
    

    The reason being that you want to add the delay time to the current value of cnt, not whatever value it had before you started the calculations. I forget if I read that in a thread or in a doc somewhere.

    I'm guessing your problem has something to do with data alignment, but that's about the best I can come up with. Have you read through the Tricks & Traps document?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    OS-X: because making Unix user-friendly was easier than debugging Windows

    links:
    My band's website
    Our album on the iTunes Music Store
  • David BDavid B Posts: 592
    edited 2006-06-30 22:20
    I did read tricks and traps a while ago, but I read them once again in case some trap would suddenly ring a bell in my head.

    Tried rearranging the delay args as you suggested; no improvement. Tried lowering the ckock speed from 16X to 8x, then after lowering the LCD baud rate to 2400, lowered the clock to 4X and to 2X, but again, no change - the same problem appears, and I can still toggle between a working version and a failing version.

    Since Rokicki successfully ran this code without the LCD routines, I ripped them all out and substituted simple procedures to send single digits to the on-board 7-segment LED for a status display. But again, no difference - the code sometimes works and sometimes fails.
  • David BDavid B Posts: 592
    edited 2006-07-01 22:05
    I may have solved the problem!

    In the assembly code, when I wanted to set a bit low, I had used

    and outa, !variable

    but that command does NOT behave as it seems like it should. As I was experimenting with it, I saw both non-clearing of the expected bit as well as clearing of unexpected bits.

    But this does work:

    andn outa, variable

    Does the "!variable" perform the bitwise NOT operation only at compile time?

    Here is a small test program that demonstrates this behavior.

    David
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-01 23:02
    Yes, any operator used in the assembly language only works at compile time. Only the actual instructions do stuff at execution time.
    I assume that !variable compiled the 2's complement of the address of variable into the instruction.
  • rokickirokicki Posts: 1,000
    edited 2006-07-02 02:34
    Heh! And it was working for me, only because the particular address that the
    negation of the address pointed to, happened to contain sufficiently "safe"
    stuff that it continued to work.

    Wow.
Sign In or Register to comment.