; RP2040 PIO program for implementing SD card access in SDIO mode ; Run "pioasm rp2040_sdio.pio rp2040_sdio.pio.h" to regenerate the C header from this. ; ; Copyright (c) 2022 Rabbit Hole Computing™ ; Copyright (c) 2011-2024 Bill Greiman ; Copyright (c) 2024 Tech by Androda, LLC ; This file is part of the SdFat library for SD memory cards. ; Portions from Bill Greiman use the MIT License: ; MIT License ; ; Permission is hereby granted, free of charge, to any person obtaining a ; copy of this software and associated documentation files (the "Software"), ; to deal in the Software without restriction, including without limitation ; the rights to use, copy, modify, merge, publish, distribute, sublicense, ; and/or sell copies of the Software, and to permit persons to whom the ; Software is furnished to do so, subject to the following conditions: ; ; The above copyright notice and this permission notice shall be included ; in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ; OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ; DEALINGS IN THE SOFTWARE. ; The RP2040 official work-in-progress code at ; https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card ; may be useful reference, but this is independent implementation. ; ; For official SDIO specifications, refer to: ; https://www.sdcard.org/downloads/pls/ ; "SDIO Physical Layer Simplified Specification Version 8.00" ; Clock settings ; For 3.3V communication the available speeds are: ; - Default speed: max. 25 MHz clock ; - High speed: max. 50 MHz clock ; ; From the default RP2040 clock speed of 125 MHz, the closest dividers ; are 3 for 41.7 MHz and 5 for 25 MHz. The CPU can apply further divider ; through state machine registers for the initial handshake. ; ; Because data is written on the falling edge and read on the rising ; edge, it is preferrable to have a long 0 state and short 1 state. ;.define CLKDIV 3 .define CLKDIV 5 .define D0 (((CLKDIV + 1) /2) - 1) .define D1 ((CLKDIV/2) - 1) .define SDIO_CLK_GPIO 10 .define public SDIO_IRQ 7 ; State Machine 0 is for the Command / Response ; This State Machine will stall with clock low after sending a command and receiving the response ; Note that the FIFOs are set to 8 bit mode here, because 8 bits evenly divides all command and response sizes .program cmd_rsp .side_set 1 opt .wrap_target cmd_begin: send_cmd: out pins, 1 side 0 [1] ; When TX FIFO is empty, this command will stall with clock low jmp X-- send_cmd side 1 [1] jmp !Y cmd_begin side 0 [1] ; If no response, go back to the beginning and stall set pindirs, 0 side 1 [3] wait_resp: nop side 0 [3] nop side 1 [2] jmp PIN wait_resp ; Run the SD clock until CMD pin goes low (First bit of response) read_resp: in pins, 1 push iffull block side 0 [2] ; Read command response jmp Y-- read_resp side 1 [1] .wrap % c-sdk { static inline pio_sm_config pio_cmd_rsp_program_config(uint offset, uint cmd_pin, uint clk_pin, uint16_t div_int, uint8_t div_frac) { pio_sm_config c = cmd_rsp_program_get_default_config(offset); sm_config_set_sideset_pins(&c, clk_pin); sm_config_set_out_pins(&c, cmd_pin, 1); sm_config_set_in_pins(&c, cmd_pin); sm_config_set_set_pins(&c, cmd_pin, 1); sm_config_set_jmp_pin(&c, cmd_pin); sm_config_set_in_shift(&c, false, false, 8); sm_config_set_out_shift(&c, false, true, 8); sm_config_set_clkdiv_int_frac(&c, div_int, div_frac); return c; } %} ; Program which reads data and provides its own clock signal ; Use direct-execute PIO instructions to place the number of 4-bit nibbles to receive ; into the X register before enabling the state machine .program rd_data_w_clock .side_set 1 mov X, Y side 0 ; Reinitialize number of nibbles to receive wait_d0: nop side 0 [3] ; Run the clock... jmp PIN wait_d0 side 1 [3] ; Until the first response nibble (all zeroes) nop side 0 [2] ; Clock transition low to make the SD card write out the first actual data nibble nop side 1 [1] ; Transition clock high to stick data value read_loop: in pins, 4 side 0 [2] ; Read in the nibble and transition the clock low push iffull block side 1 ; Transition the clock high and block execution if rx fifo is full jmp X--, read_loop side 1 ; No delays here or previous instruction, because instr [1] = two instr execution time % c-sdk { static inline pio_sm_config pio_rd_data_w_clock_program_config(uint offset, uint d0_pin, uint clk_pin, float clk_div) { pio_sm_config c = rd_data_w_clock_program_get_default_config(offset); sm_config_set_sideset_pins(&c, clk_pin); sm_config_set_in_pins(&c, d0_pin); sm_config_set_jmp_pin(&c, d0_pin); sm_config_set_in_shift(&c, false, false, 32); sm_config_set_out_shift(&c, false, true, 32); sm_config_set_clkdiv(&c, clk_div); return c; } %} ; Data transmission program ; ; Before running this program, pindirs should be set as output ; and register X should be initialized with the number of nibbles ; to send minus 1 (typically 8 + 1024 + 16 + 1 - 1 = 1048) ; ; Register Y must be set to the number of CRC bits to receive (8/32) ; ; Words written to TX FIFO must be: ; - Word 0: start token 0xFFFFFFF0 ; - Word 1-128: transmitted data (512 bytes) ; - Word 129-130: CRC checksum ; - Word 131: end token 0xFFFFFFFF .program sdio_tx_w_clock .side_set 1 opt tx_loop: out PINS, 4 side 0 [2] ; Write nibble value and transition clock low jmp X-- tx_loop side 1 [1] ; Transition clock high, and check if more data needs to be sent set pindirs, 0 side 1 [2] ; Set input mode to receive CRC token, without changing clock phase crc_get: in pins, 1 side 1 [4] ; Input the first bit of CRC response jmp Y-- crc_get side 0 [4] ; Read the CRC bits bsy_wait: jmp PIN done side 1 [4] jmp bsy_wait side 0 [4] ; Clock until no longer BSY done: .wrap_target push iffull noblock side 0 ; Unconditional, just push the response token .wrap % c-sdk { static inline pio_sm_config pio_sdio_tx_w_clock_program_config(uint offset, uint data_pin, uint clk_pin, int clk_div) { pio_sm_config c = sdio_tx_w_clock_program_get_default_config(offset); sm_config_set_sideset_pins(&c, clk_pin); sm_config_set_out_pins(&c, data_pin, 4); sm_config_set_in_pins(&c, data_pin); sm_config_set_set_pins(&c, data_pin, 4); sm_config_set_in_shift(&c, false, false, 8); sm_config_set_out_shift(&c, false, true, 32); sm_config_set_jmp_pin(&c, data_pin); sm_config_set_clkdiv_int_frac(&c, clk_div, 0); return c; } %}