Shop OBEX P1 Docs P2 Docs Learn Events
P2 uSD Card Driver — FAT32 Filesystem (spin2/pasm2) - Page 5 — Parallax Forums

P2 uSD Card Driver — FAT32 Filesystem (spin2/pasm2)

1235

Comments

  • evanhevanh Posts: 17,235

    So you're also storing the programs using the Prop2? It had sounded like you were doing that from the desktop after compiling.

  • ke4pjwke4pjw Posts: 1,324

    @evanh Yes, the P2 downloads the new image to itself from the Internet and then reboots. This is an older video, but this is what the process looks like.

  • evanhevanh Posts: 17,235

    A tad late.

  • RaymanRayman Posts: 16,397
    edited 2026-04-05 15:43

    Tried this out and works great for usual P2 uSD pins. But, doesn't seem to work on other pins...

    Tried this:

        SD_BASE = 0
        SD_SCK  = SD_BASE + 5              ' - Serial Clock
        SD_CS   = SD_BASE + 3              ' - Chip Select
        SD_MOSI = SD_BASE + 4              '  - Master Out, Slave In
        SD_MISO = SD_BASE + 0              '  - Master In, Slave Out
    

    with no luck...
    A bit puzzling because not seeing anything in the source code that is pin# dependent...

  • evanhevanh Posts: 17,235
    edited 2026-04-05 21:57

    @Rayman said:
    A bit puzzling because not seeing anything in the source code that is pin# dependent...

    I'm not at home at the moment but from memory it uses smartpin serial hardware in some parts and he's not bothered to calculate the clock pin offset pin when setting the smartpin mode.

    I wanted to exchange the block level SD driver with something faster and smaller but that hasn't eventuated either. It's quite the tangle in there.

  • RaymanRayman Posts: 16,397

    Clock pin offset, guess that would do it…

  • evanhevanh Posts: 17,235
    edited 2026-04-06 07:29

    Oh, ouch, offset 5 between SD_MISO and SD_SCK. Not impossible but also not easy to fix the sources with that. That's Roger's 4-bit add-on pinout I presume?

  • RossHRossH Posts: 5,757

    I have not kept up with recent Spin developments, but I can't help wondering who thought mixing SPIN and PASM in this way was a good idea. Surely it makes maintaining, modifying or porting drivers like this far more complicated than it needs to be?

  • RaymanRayman Posts: 16,397

    @evanh yes, that is one of them that didn’t work….

  • evanhevanh Posts: 17,235

    @RossH said:
    I have not kept up with recent Spin developments, but I can't help wondering who thought mixing SPIN and PASM in this way was a good idea. Surely it makes maintaining, modifying or porting drivers like this far more complicated than it needs to be?

    Stephen may have got a little excessive in misc and redundant routines using ORG/END but I will say, however, it's rather handy to have high-level locals (in registers) directly available to assembly. So I do like the inline Pasm2 ability in Spin2.

  • RaymanRayman Posts: 16,397

    Inline assembly is awesome and using for uSD drivers makes a lot of sense... You get close to the performance of pure assembly with a lot more ease...

  • @evanh @Rayman - will add pin offset computation to the next update after v1.5.0. Good catch.

  • Stephen MoracoStephen Moraco Posts: 476
    edited 2026-04-08 18:45

    NEWS

    I released v1.5.0 a couple of days ago.

    Here's what's new/fixed:

    v1.5.0

    Defragmentation support, next-fit allocation, contiguous file creation, and Stale directory cluster fix.

    ### Added
    - Next-fit allocator: Sequential writes produce more contiguous files, reducing fragmentation
    - Defragmentation API (SD_INCLUDE_DEFRAG):
    - fileFragments(): Count non-contiguous fragments in a file's cluster chain
    - isFileContiguous(): Check if file clusters are stored contiguously
    - createFileContiguous(): Create a file with pre-allocated contiguous cluster chain
    - compactFile(): Relocate a fragmented file's clusters into a contiguous chain
    - FSCK fragmentation reporting: Audit and FSCK summaries now report fragmented file count and total fragments
    - New error codes: E_NO_CONTIGUOUS_SPACE, E_FILE_OPEN_FOR_COMPACT, E_VERIFY_FAILED

    ### Changed
    - Volume label detection improved for cards with metadata before the label entry
    - Regression suite expanded to 25 suites, 465 tests, all passing on hardware

    ### Fixed
    - Fixed stale directory cluster when navigating or deleting in large subdirectories

    Visit the P2 microSD FAT32 Filesystem repository for full documentation, including a driver tutorial, theory of operations, and card catalog.

    The release package can be downloaded from the Releases page. Download the sd-card-driver-{version}.zip
    file.

    If you find issues, please file them at the Issues page.

    What's next? Quickly address any reported issues. Likely conditional compile for LFN support.

    Enjoy!

    Stephen

  • evanhevanh Posts: 17,235
    edited 2026-04-08 01:20

    @"Stephen Moraco" said:
    - will add pin offset computation to the next update after v1.5.0.

    MOSI can't do it but, for MISO it gets complicated if wanting to deal with offsets 4,5,6. By using an intermediate smartpin one can then stretch the two input pin numbers up to both a +3 and -3 at once to effect a total offset of up to 6.

    Some of the complexity comes from the possibility of having to dodge pin numbers that are assigned another smartpin job. When the offset is 4 or 5 then choosing from more than one smartpin is an option.
    And that still doesn't deal with the possibility of trampling on a smartpin that should have been allocated to another driver. But, since there is no smartpin allocation system, there's little that can be done other than don't accommodate beyond +-3 offsets.

    The last part is keeping track of the extra pin number used for the MISO smartpin. It's a shuffle and looks messy.

  • RaymanRayman Posts: 16,397
    edited 2026-04-08 01:28

    Maybe not a problem for 4- bit setup because free to use non- 4 bit pins?

    Other more condensed pin groupings ok?

  • evanhevanh Posts: 17,235

    @Rayman said:
    Maybe not a problem for 4- bit setup because free to use non- 4 bit pins?

    The sdsd.cc 4-bit SD mode driver doesn't use any serial smartpin modes. It only uses a single smartpin for SD clock gen. All CMD and DAT data is transferred with streamer ops.

    Other more condensed pin groupings ok?

    Yes, of course. It only matters when a smartpin requires a smartB input.

  • evanhevanh Posts: 17,235

    So another option for Stephen is to stop using the serial smartpins and switch to using the streamer for everything.

  • RaymanRayman Posts: 16,397

    @evanh meant that for the 4bit setup in 1bit mode there are free pins that can be used…. Maybe that helps…

  • evanhevanh Posts: 17,235
    edited 2026-04-08 08:28

    @Rayman said:
    @evanh meant that for the 4bit setup in 1bit mode there are free pins that can be used…. Maybe that helps…

    Yes, fine for that add-on. And it's unlikely there would ever be a design that interleaves pins between uses but you never know for sure.

  • RaymanRayman Posts: 16,397
    edited 2026-04-08 18:46

    There are SPI display modules with built in uSD, that may or may not be another case…

  • maccamacca Posts: 1,054
    edited 2026-05-03 06:47

    I'm trying to use the driver with a pretty old 1GB uSD card, it works with the default 350MHz system clock, but if I set it to 250MHz it always fails to mount, seems the minimum is around 290MHz.

    This is the output of SD_FAT32_audit at 250 MHz

    Cog0  ==============================================
    Cog0    FAT32 Audit (via isp_fsck_utility)
    Cog0  ==============================================
    Cog0   
    Cog0  === FAT32 Filesystem Audit (read-only) ===
    Cog0  Card: 968 MB
    Cog0   
    Cog0    ERROR: Invalid MBR signature
    Cog0  ERROR: Cannot read filesystem geometry
    Cog0   
    Cog0  END_SESSION
    

    Is there something I can do to make it work ?

    I have another card, more recent, 32GB, that works well at 250MHz.

  • evanhevanh Posts: 17,235

    Add a debug mask to get some info card handling. eg: At line 114 change DEBUG_MASK = 0 to DEBUG_MASK = (1 << CH_INIT) | (1 << CH_MOUNT)

  • @macca said:
    I'm trying to use the driver with a pretty old 1GB uSD card, it works with the default 350MHz system clock, but if I set it to 250MHz it always fails to mount, seems the minimum is around 290MHz.

    Is there something I can do to make it work ?

    I have another card, more recent, 32GB, that works well at 250MHz.

    The clock that works at is maybe partially telling... I found that some cards and the technologies they implement (the various ratings) can be sensitive to clock speeds. It would appear that the 350 is some multiple of some value that 250 is not in the same group. Once you find the multiple, then lower clock speeds of that multiple will likely work. Yes, I know this sounds odd. ;-) There is a frequency-sweep test that I used to try to gain insight into issues like this. Can you run it (make sure you have a copy of the contents before you mess around, just in case) to see where your device has issues with clock speed? @evanh's suggestion is also good, so we can tell which calls are failing. The mix of these findings should help us identify. If we find something we can make the code more robust against, I'll be glad to!

  • maccamacca Posts: 1,054

    @"Stephen Moraco" said:
    The clock that works at is maybe partially telling... I found that some cards and the technologies they implement (the various ratings) can be sensitive to clock speeds. It would appear that the 350 is some multiple of some value that 250 is not in the same group. Once you find the multiple, then lower clock speeds of that multiple will likely work. Yes, I know this sounds odd. ;-) There is a frequency-sweep test that I used to try to gain insight into issues like this. Can you run it (make sure you have a copy of the contents before you mess around, just in case) to see where your device has issues with clock speed? @evanh's suggestion is also good, so we can tell which calls are failing. The mix of these findings should help us identify. If we find something we can make the code more robust against, I'll be glad to!

    Well... I managed to wipe the card anyway while testing various options so that's not a problem...

    Running with the debug options enabled shows that it can do initCard without (apparent) problems but fails to readSector:

    Cog0  INIT $0000_0000 $0000_0000 load
    Cog0  INIT $0000_0F64 $0000_662C jump
    Cog0  === SD Read/Write Example ===
    Cog1  INIT $0000_0F64 $0000_1AC2 jump
    Cog1    [do_mount] Calling initCard()...
    Cog1      [initCard] Starting card init...
    Cog1      [initCard] Reference: SPI_SD_Implementation_Reference.md
    Cog1      [initCard] Step 1: Power-on delay (100ms)...
    Cog1      [initCard] Step 2: SPI config, bit_delay=bit_delay = 2_500 (~50kHz)
    Cog1      [initCard] Pins: CS=1 MOSI=1 MISO=1 SCK=0
    Cog1      [initCard] Step 3: Recovery flush (4096 clocks to clear stuck transfer)...
    Cog1      [initCard] MISO after recovery flush: 1 (should be 1)
    Cog1      [initCard] Step 3.5: Initializing smart pins (ManAtWork pattern)...
    Cog1      [initSPIPins] Setting up smart pins (ManAtWork pattern)...
    Cog1      [initSPIPins] Mode values:
    Cog1      [initSPIPins]   spi_clk_mode=$spi_clk_mode = $0000_004A (P_TRANSITION | P_OE, idle LOW)
    Cog1      [initSPIPins]   spi_tx_mode=$spi_tx_mode = $0200_0078 (P_SYNC_TX | P_OE | P_PLUS2_B)
    Cog1      [initSPIPins]   spi_rx_mode=$spi_rx_mode = $0300_003A (P_SYNC_RX | P_PLUS3_B)
    Cog1      [initSPIPins]   SCK=P61 MOSI=P59 MISO=P58
    Cog1      [initSPIPins]   SCK configured: P_TRANSITION | P_OE (awaiting applySPISpeed)
    Cog1      [initSPIPins]   MOSI configured: P_SYNC_TX | P_PLUS2_B (enabled)
    Cog1      [initSPIPins]   MISO configured: P_SYNC_RX | P_PLUS3_B (enabled)
    Cog1      [initSPIPins]   Event SE1 configured for SCK pin 61
    Cog1      [initSPIPins] Smart pins configured and enabled
    Cog1      [applySPISpeed] Target=400 kHz, Actual=399 kHz
    Cog1      [applySPISpeed] Half-period=313 clocks
    Cog1      [sp_transfer_8] pins: miso=58 sck=61 raw=$FF00_0000 final=$FF
    Cog1      [sp_transfer_8] pins: miso=58 sck=61 raw=$FFFF_0000 final=$FF
    Cog1      [sp_transfer_8] pins: miso=58 sck=61 raw=$FFFF_FF00 final=$FF
    Cog1      [sp_transfer_8] pins: miso=58 sck=61 raw=$FFFF_FFFF final=$FF
    Cog1      [sp_transfer_8] pins: miso=58 sck=61 raw=$FFFF_FFFF final=$FF
    Cog1      [initCard] Dummy clocks sent via smart pins
    Cog1      [initCard] Step 4: CMD0 (GO_IDLE_STATE)...
    Cog1        [cmd] CS before assert: 1
    Cog1        [cmd] CS after pinl: 0 MISO=0
    Cog1        [cmd] After CMD0 sent, MISO=0
    Cog1      [initCard] CMD0 response: $$1
    Cog1      [initCard] CMD0 OK - card in idle state
    Cog1      [initCard] Step 5: CMD8 (SEND_IF_COND, VHS=1, pattern=$AA)...
    Cog1      [initCard] CMD8 response (32-bit): $resp = $0000_01AA
    Cog1      [initCard] CMD8 echo valid ($1AA) -> Ver 2.0+ SD card
    Cog1      [initCard] Step 6: ACMD41 init loop (arg=$acmd41_arg = $4000_0000)...
    Cog1      [initCard] ACMD41 complete - card ready!
    Cog1      [initCard] Step 7: CMD58 (READ_OCR)...
    Cog1      [initCard] OCR: $resp = $80FF_8000
    Cog1      [initCard]   Bit 31 (ready): (resp >> 31) & 1 = 1
    Cog1      [initCard]   Bit 30 (CCS):   (resp >> OCR_CCS_SHIFT) & 1 = 0
    Cog1      [initCard] Card type: SDSC (byte addressing)
    Cog1      [initCard] Step 8: Card identification and speed selection...
    Cog1      [applySPISpeed] Target=25_000 kHz, Actual=25_000 kHz
    Cog1      [applySPISpeed] Half-period=5 clocks
    Cog1      [initCard] Dummy clocks sent, CS HIGH
    Cog1      [probeCmd13] pre=[$FF $FF $FF $FF $FF $FF]
    Cog1      [probeCmd13] cap=[$FF $00 $00 $FF $FF $FF $FF $FF] NCR=2
    Cog1      [probeCmd13] R1=$$00 STATUS=$$00
    Cog1      [probeCmd13] CMD13 OK (R1=$$00 STATUS=$$00)
    Cog1      [probeCmd23] CMD23 not advertised (CMD_SUPPORT=$$00)
    Cog1      [initCard] === INIT SUCCESS ===
    Cog1    [do_mount] Reading MBR sector 0...
    Cog1    [readSector] TIMEOUT waiting for start token
    Cog1    [checkCardStatus] readSector: pre=[$FF $FF $FF $FF $FF $FF] NCR=8 R1=$$00 ST=$$08
    Cog1    [checkCardStatus] readSector: STATUS error=$$08
    Cog1      -> CC_ERROR
    Cog1    [do_mount] FAIL: MBR read error
    Cog1    [updateFSInfo] No valid FSInfo to update
    Cog0  Mount FAILED, error: sd.error() = -7
    Cog0  === Done ===
    

    If I comment the "Card identification and speed selection" step, it goes a little further:

    Cog1    [do_mount] Reading MBR sector 0...
    Cog1    [checkCardStatus] readSector: pre=[$FF $FF $FF $FF $FF $FF] NCR=2 R1=$$00 ST=$$00
    Cog1    [do_mount] MBR type code: $C
    Cog1    [do_mount] FAT32 detected, VBR at sector vbr_sec = 8_192
    Cog1    [checkCardStatus] readSector: pre=[$FF $FF $FF $FF $FF $FF] NCR=2 R1=$$00 ST=$$00
    Cog1    [do_mount] Bytes/sector: vbrBytesPerSec(pVBR) = 512
    Cog1    [do_mount] Sectors/cluster: sec_per_clus = 8
    Cog1    [do_mount] Reserved sectors: reserved = 32
    Cog1    [do_mount] Number of FATs: vbrNumFats(pVBR) = 2
    Cog1    [do_mount] VBR volume label: @vol_label = "P2-XFER    "
    Cog1    [checkCardStatus] readSector: pre=[$FF $FF $FF $FF $FF $FF] NCR=2 R1=$$00 ST=$$00
    Cog1    [do_mount] Root dir volume label: @vol_label = "P2-XFER    "
    Cog1    [checkCardStatus] readSector: pre=[$FF $FF $FF $FF $FF $FF] NCR=2 R1=$$00 ST=$$00
    Cog1    [do_mount] FSInfo: free_count=fsi_free_count = 246_488 nxt_free=fsi_nxt_free = 3
    Cog1    [do_mount] SUCCESS
    Cog1    [do_mount] SUCCESS, mode=FILESYSTEM
    Cog0  Card mounted, volume: sd.volumeLabel() = "P2-XFER    "
    Cog1    [checkCardStatus] writeSector: pre=[$FF $FF $FF $FF $FF $FF] NCR=3 R1=$$00 ST=$$01
    Cog1    [checkCardStatus] writeSector: STATUS error=$$01
    Cog1      -> CARD_IS_LOCKED
    Cog1    [writeSector] CMD24 warning: R1=$$01 (stale bits, proceeding)
    Cog1    [writeSector] Data REJECTED: Unknown status (response=$$07)
    Cog1    [updateFSInfo] Updated primary+backup: free_count=fsi_free_count = 246_488
    Cog1    [do_unmount] Card unmounted cleanly, mode=NONE
    Cog0  === Done ===
    Cog1    [readSector] ERROR: got $$FD instead of $FF or $FE
    Cog1    [checkCardStatus] readSector: pre=[$05 $04 $00 $00 $00 $00] NCR=1 R1=$$00 ST=$$00
    Cog1    [updateFSInfo] FSInfo read FAILED
    

    Don't know why it reports CARD_IS_LOCKED.

    The frequency-sweep test shows no errors:

    Cog0  INIT $0000_0000 $0000_0000 load
    Cog0  INIT $0000_0F64 $0000_A078 jump
    Cog0   
    Cog0  ============================================================
    Cog0    SPI Max Speed Characterization - External Header
    Cog0  ============================================================
    Cog0    Range: 15-25 MHz in 1 MHz steps
    Cog0    Per speed: 50 single + 10x8 multi + 5x8 write-verify
    Cog0    SYSCLK: 350 MHz
    Cog0   
    Cog0  Mounting SD card...
    Cog1  INIT $0000_0F64 $0000_50D2 jump
    Cog0  Card mounted successfully
    Cog0    Volume: P2-XFER    
    Cog0    Initial SPI: 25_000 kHz
    Cog0   
    Cog0  ============================================================
    Cog0    FREQUENCY SWEEP: 15 MHz -> 25 MHz
    Cog0  ============================================================
    Cog0   
    Cog0  --- 15 MHz target (hp=12, actual=14_583 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 16 MHz target (hp=11, actual=15_909 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 17 MHz target (hp=11, actual=15_909 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 18 MHz target (hp=10, actual=17_500 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 19 MHz target (hp=10, actual=17_500 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 20 MHz target (hp=9, actual=19_444 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 21 MHz target (hp=9, actual=19_444 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 22 MHz target (hp=8, actual=21_875 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 23 MHz target (hp=8, actual=21_875 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 24 MHz target (hp=8, actual=21_875 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  --- 25 MHz target (hp=7, actual=25_000 kHz) ---
    Cog0    P1-Single: OK=50 crc_err=0 rd_err=0
    Cog0    P2-Multi: OK=10 errors=0
    Cog0    P3-WrVerify: 5 iters, 0 errors
    Cog0    => ALL PASS
    Cog0   
    Cog0  ============================================================
    Cog0    MAX SPEED CHARACTERIZATION RESULTS
    Cog0  ============================================================
    Cog0   
    Cog0    Target  hp  Actual_kHz  Single  Multi  WrVerify  Overall
    Cog0    ------  --  ----------  ------  -----  --------  -------
    Cog0    15 MHz 12   14_583      PASS    PASS   PASS      PASS
    Cog0    16 MHz 11   15_909      PASS    PASS   PASS      PASS
    Cog0    17 MHz 11   15_909      PASS    PASS   PASS      PASS
    Cog0    18 MHz 10   17_500      PASS    PASS   PASS      PASS
    Cog0    19 MHz 10   17_500      PASS    PASS   PASS      PASS
    Cog0    20 MHz 9   19_444      PASS    PASS   PASS      PASS
    Cog0    21 MHz 9   19_444      PASS    PASS   PASS      PASS
    Cog0    22 MHz 8   21_875      PASS    PASS   PASS      PASS
    Cog0    23 MHz 8   21_875      PASS    PASS   PASS      PASS
    Cog0    24 MHz 8   21_875      PASS    PASS   PASS      PASS
    Cog0    25 MHz 7   25_000      PASS    PASS   PASS      PASS
    Cog0   
    Cog0    ALL PASS: 11 speeds, FAIL: 0 speeds
    Cog0   
    Cog0  END_SESSION
    

    Seems the initial SPI settings are working to some extent, but then fails for some other reasons.

  • Stephen MoracoStephen Moraco Posts: 476
    edited 2026-05-04 07:02

    @macca, I led you to the wrong test. Let's try again.

    1. Build and run diagnostic-tests/SD_frequency_characterize.spin2 against the 1GB card.
    2. Post the full output (the test prints a results table at the end).

    3. Build and run src/UTILS/SD_card_identify.spin2 — dumps CID + CSD + SCR + SD Status.

    4. Post the full output for the failing 1GB card

    This should give us some understanding. The results may lead to a custom test for you to run. This feels very much like we don't have enough margin for our data sampling for this card. Understanding which clock values fail in this new test tells us which clock division boundaries work for this card and which do not. That helps me dial in the sampling. We have tested a rich sample set of cards, and they all worked well with this current margin. You found a case where it's too close at some clock speeds. What I've seen in the past is that all clock speeds that result in the divider that works will work for this card. (Not that this is useful.) I'm hoping we'll be able to learn more and widen the operational range so your device can be added to the list of ones that work.

    Also, it looks like the CARD IS LOCKED is the driver trying to interpret misaligned data. Which is why it doesn't make sense.

  • maccamacca Posts: 1,054

    @"Stephen Moraco" said:
    1. Build and run diagnostic-tests/SD_frequency_characterize.spin2 against the 1GB card.
    2. Post the full output (the test prints a results table at the end).

    That source seems to have a "typo", _CLKFREQ is set to 270MHz and I believe it should be set at 320MHz, with the default the initial mount fails.

    Cog0  INIT $0000_0000 $0000_0000 load
    Cog0  INIT $0000_0F64 $0000_9408 jump
    Cog0   
    Cog0  ============================================================
    Cog0    SD Frequency Sweep Test (V2 Driver)
    Cog0  ============================================================
    Cog0    Purpose: Find sysclk frequencies where streamer timing fails
    Cog0    Test: writeSectorsRaw(8) + readSectorsRaw(8) + verify
    Cog0   
    Cog0  Initial mount at 320 MHz...
    Cog1  INIT $0000_0F64 $0000_1EC2 jump
    Cog0  ERROR: Initial mount failed!
    Cog0   
    Cog0  END_SESSION
    

    If I set the _CLKFREQ to 320MHz seems it stucks at clkset inside changeFrequency...

    Cog0  INIT $0000_0000 $0000_0000 load
    Cog0  INIT $0000_0F64 $0000_9408 jump
    Cog0   
    Cog0  ============================================================
    Cog0    SD Frequency Sweep Test (V2 Driver)
    Cog0  ============================================================
    Cog0    Purpose: Find sysclk frequencies where streamer timing fails
    Cog0    Test: writeSectorsRaw(8) + readSectorsRaw(8) + verify
    Cog0   
    Cog0  Initial mount at 320 MHz...
    Cog1  INIT $0000_0F64 $0000_1EC2 jump
    Cog0  Card mounted successfully
    Cog0   
    Cog0  --- BASELINE TEST @ 320 MHz ---
    Cog0    -> PASS (baseline verified)
    Cog0   
    Cog0  ============================================================
    Cog0    FREQUENCY SWEEP
    Cog0  ============================================================
    Cog0   
    Cog0  --- 310 MHz (hp=7) ---
    

    I tried with both my Spin Tools and pnut_ts just to be sure it isn't something in the compiler.

    1. Build and run src/UTILS/SD_card_identify.spin2 — dumps CID + CSD + SCR + SD Status.
    2. Post the full output for the failing 1GB card
    Cog0  INIT $0000_0000 $0000_0000 load
    Cog0  INIT $0000_0F64 $0000_6E90 jump
    Cog1  INIT $0000_0F64 $0000_2176 jump
    Cog0  L1: SanDisk SU01G SDSC 968MB [FAT32] SD 2.0 rev8.0 SN:$006C_D5B2 2_007/6
    Cog0  L2: Class 2, U0, V0, SPI 25 MHz  [P2FMTER]
    Cog0  END_SESSION
    
  • evanhevanh Posts: 17,235
    edited 2026-05-04 11:55

    Stephen, your streamer code is a tad unstable:

          ' STREAMER BULK TRANSFER: 512 bytes from MISO to hub via DMA
          ORG
                  DIRL      _sck                            ' Reset SCK: counter stops, output LOW, Y=0
                  DRVL      _sck                            ' Re-enable: DIR=1 restarts base period counter fresh
                  SETXFRQ   xfrq                            ' Set streamer NCO rate
                  WRFAST    #0, p_buf                       ' Setup WRFAST to hub buffer
                  WYPIN     clk_count, _sck                 ' Start clock transitions
                  WAITX     align_delay                     ' Wait one half-period to align with first rising edge
                  XINIT     stream_mode, init_phase         ' Start streamer with phase offset
                  WAITXFI                                   ' Wait for streamer to complete
          END
    

    The WRFAST, in particular, needs to be ahead of the rising DIR smartpin control. This is because WRFAST can be erratic in how many sysclock ticks it takes to execute. And consistency of ticks is critical between the rising DIR and XINIT to retain clock-data phase alignment.

    Secondly, a change in clock divider value, when small, will upset the timing when high number of ticks exist between rising DIR and WYPIN. This is due to the way the smartpin cycles internally - Cycling begins as soon as DIR rises. A new pulse series, from a WYPIN, begins only on a whole cycle boundary, independent of WYPIN execution time - Which is always 2 ticks. Consistency is attained by placing the WYPIN immediately following the rising DIR.

    Here's my recommendation:

          ' STREAMER BULK TRANSFER: 512 bytes from MISO to hub via DMA
          ORG
                  WRFAST    #0, p_buf                       ' Setup WRFAST to hub buffer
                  SETXFRQ   xfrq                            ' Set streamer NCO rate
                  FLTL      _sck                            ' Reset SCK: counter stops, output LOW, Y=0
                  DIRH      _sck                            ' Re-enable: DIR=1 restarts base period counter fresh
                  WYPIN     clk_count, _sck                 ' Start clock transitions
                  WAITX     align_delay                     ' Wait one half-period to align with first rising edge
                  XINIT     stream_mode, init_phase         ' Start streamer with phase offset
                  WAITXFI                                   ' Wait for streamer to complete
          END
    

    Lastly, there is the internal long route staging registers inside the Prop2 I/O paths, and other latencies in the outer pad-ring that adds further ticks at higher sysclock frequencies. For pin reads, these can add up to quite a number of ticks from the outputting of an SD clock edge to the streamer sampling of the SD response. Having extra compensation is a good idea. Your default align_delay calculation of half SD clock cycle is probably borderline, but I'll leave this detail for later.

  • @evanh said:
    The WRFAST, in particular, needs to be ahead of the rising DIR smartpin control. This is because WRFAST can be erratic in how many sysclock ticks it takes to execute. And consistency of ticks is critical between the rising DIR and XINIT to retain clock-data phase alignment.

    WRFAST only takes extra cycles if there's still data buffered from a previous write. (Unlike RDFAST). Though moving it to the top is probably a good idea, anyways.

  • evanhevanh Posts: 17,235
    edited 2026-05-04 16:22

    True, there is RDFAST in another part ... but that needs a far more in-depth discussion - After Macca get results on resolving the first SD block read.

    PS: Stephen has opted for a variable divider - to better fit the SD standard. Compared to Roger's work with PSRAM drivers, this has opened up a can of worms that needs dealt with. Again, I had wanted to just plug-in the already proven SD drivers from Flexspin efforts.

  • @evanh I'm studying what you've provided. Thank you for providing this. I'm also studying the configuration math for the read/write streamer pipelines. I believe we have a fixed value in there instead of a calculated one, which then puts us in the marginal behavior areas of the signal. More soon.

Sign In or Register to comment.