rp2040_sdio.pio 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. ; RP2040 PIO program for implementing SD card access in SDIO mode
  2. ; Run "pioasm rp2040_sdio.pio rp2040_sdio.pio.h" to regenerate the C header from this.
  3. ;
  4. ; Copyright (c) 2022 Rabbit Hole Computing™
  5. ; Copyright (c) 2011-2024 Bill Greiman
  6. ; Copyright (c) 2024 Tech by Androda, LLC
  7. ; This file is part of the SdFat library for SD memory cards.
  8. ; Portions from Bill Greiman use the MIT License:
  9. ; MIT License
  10. ;
  11. ; Permission is hereby granted, free of charge, to any person obtaining a
  12. ; copy of this software and associated documentation files (the "Software"),
  13. ; to deal in the Software without restriction, including without limitation
  14. ; the rights to use, copy, modify, merge, publish, distribute, sublicense,
  15. ; and/or sell copies of the Software, and to permit persons to whom the
  16. ; Software is furnished to do so, subject to the following conditions:
  17. ;
  18. ; The above copyright notice and this permission notice shall be included
  19. ; in all copies or substantial portions of the Software.
  20. ;
  21. ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  22. ; OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  26. ; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  27. ; DEALINGS IN THE SOFTWARE.
  28. ; The RP2040 official work-in-progress code at
  29. ; https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card
  30. ; may be useful reference, but this is independent implementation.
  31. ;
  32. ; For official SDIO specifications, refer to:
  33. ; https://www.sdcard.org/downloads/pls/
  34. ; "SDIO Physical Layer Simplified Specification Version 8.00"
  35. ; Clock settings
  36. ; For 3.3V communication the available speeds are:
  37. ; - Default speed: max. 25 MHz clock
  38. ; - High speed: max. 50 MHz clock
  39. ;
  40. ; From the default RP2040 clock speed of 125 MHz, the closest dividers
  41. ; are 3 for 41.7 MHz and 5 for 25 MHz. The CPU can apply further divider
  42. ; through state machine registers for the initial handshake.
  43. ;
  44. ; Because data is written on the falling edge and read on the rising
  45. ; edge, it is preferrable to have a long 0 state and short 1 state.
  46. ;.define CLKDIV 3
  47. ;.define CLKDIV 5
  48. ;.define D0 (((CLKDIV + 1) /2) - 1) ; Used to be 2 clocks
  49. ;.define D1 ((CLKDIV/2) - 1) ; Used to be 1 clock
  50. ;.define SDIO_CLK_GPIO 10
  51. ;.define public SDIO_IRQ 7
  52. ; State Machine 0 is for the Command / Response
  53. ; This State Machine will stall with clock low after sending a command and receiving the response
  54. ; Note that the FIFOs are set to 8 bit mode here, because 8 bits evenly divides all command and response sizes
  55. ; NOTE: Any changes to cmd_rsp will require updates in rp2040_sdio to set delay values appropriately for speed config
  56. .program cmd_rsp
  57. .side_set 1 opt
  58. .wrap_target
  59. cmd_begin:
  60. send_cmd:
  61. out pins, 1 side 0 [0] ; When TX FIFO is empty, this command will stall with clock low
  62. jmp X-- send_cmd side 1 [0]
  63. jmp !Y cmd_begin side 0 [0] ; If no response, go back to the beginning and stall
  64. set pindirs, 0 side 1 [0]
  65. wait_resp:
  66. nop side 0 [0]
  67. nop side 1 [0]
  68. jmp PIN wait_resp ; Run the SD clock until CMD pin goes low (First bit of response)
  69. read_resp:
  70. in pins, 1
  71. push iffull block side 0 [0] ; Read command response
  72. jmp Y-- read_resp side 1 [0]
  73. .wrap
  74. % c-sdk {
  75. 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) {
  76. pio_sm_config c = cmd_rsp_program_get_default_config(offset);
  77. sm_config_set_sideset_pins(&c, clk_pin);
  78. sm_config_set_out_pins(&c, cmd_pin, 1);
  79. sm_config_set_in_pins(&c, cmd_pin);
  80. sm_config_set_set_pins(&c, cmd_pin, 1);
  81. sm_config_set_jmp_pin(&c, cmd_pin);
  82. sm_config_set_in_shift(&c, false, false, 8);
  83. sm_config_set_out_shift(&c, false, true, 8);
  84. sm_config_set_clkdiv_int_frac(&c, div_int, div_frac);
  85. return c;
  86. }
  87. %}
  88. ; Program which reads data and provides its own clock signal
  89. ; Use direct-execute PIO instructions to place the number of 4-bit nibbles to receive
  90. ; into the X register before enabling the state machine
  91. ; NOTE: Any changes to rd_data_w_clock will require updates in rp2040_sdio to set delay values appropriately for speed config
  92. .program rd_data_w_clock
  93. .side_set 1
  94. mov X, Y side 0 ; Reinitialize number of nibbles to receive
  95. wait_d0:
  96. nop side 0 [0] ; Run the clock...
  97. jmp PIN wait_d0 side 1 [0] ; Until the first response nibble (all zeroes)
  98. nop side 0 [0] ; Clock transition low to make the SD card write out the first actual data nibble
  99. nop side 1 [0] ; Transition clock high to stick data value
  100. read_loop:
  101. in pins, 4 side 0 [0] ; Read in the nibble and transition the clock low
  102. push iffull block side 1 ; Transition the clock high and block execution if rx fifo is full
  103. jmp X--, read_loop side 1 ; No delays here or previous instruction, because instr [1] = two instr execution time
  104. % c-sdk {
  105. static inline pio_sm_config pio_rd_data_w_clock_program_config(uint offset, uint d0_pin, uint clk_pin, float clk_div) {
  106. pio_sm_config c = rd_data_w_clock_program_get_default_config(offset);
  107. sm_config_set_sideset_pins(&c, clk_pin);
  108. sm_config_set_in_pins(&c, d0_pin);
  109. sm_config_set_jmp_pin(&c, d0_pin);
  110. sm_config_set_in_shift(&c, false, false, 32);
  111. sm_config_set_out_shift(&c, false, true, 32);
  112. sm_config_set_clkdiv(&c, clk_div);
  113. return c;
  114. }
  115. %}
  116. ; Data transmission program
  117. ;
  118. ; Before running this program, pindirs should be set as output
  119. ; and register X should be initialized with the number of nibbles
  120. ; to send minus 1 (typically 8 + 1024 + 16 + 1 - 1 = 1048)
  121. ;
  122. ; Register Y must be set to the number of CRC bits to receive (8/32)
  123. ;
  124. ; Words written to TX FIFO must be:
  125. ; - Word 0: start token 0xFFFFFFF0
  126. ; - Word 1-128: transmitted data (512 bytes)
  127. ; - Word 129-130: CRC checksum
  128. ; - Word 131: end token 0xFFFFFFFF
  129. ; NOTE: Any changes to sdio_tx_w_clock will require updates in rp2040_sdio to set delay values appropriately for speed config
  130. .program tx_data_w_clock
  131. .side_set 1 opt
  132. tx_loop:
  133. out PINS, 4 side 0 [0] ; Write nibble value and transition clock low
  134. jmp X-- tx_loop side 1 [0] ; Transition clock high, and check if more data needs to be sent
  135. set pindirs, 0 side 1 [0] ; Set input mode to receive CRC token, without changing clock phase
  136. crc_get:
  137. in pins, 1 side 1 [0] ; Input the first bit of CRC response
  138. jmp Y-- crc_get side 0 [0] ; Read the CRC bits
  139. bsy_wait:
  140. jmp PIN done side 1 [0]
  141. jmp bsy_wait side 0 [0] ; Clock until no longer BSY
  142. done:
  143. .wrap_target
  144. push iffull noblock side 0 ; Unconditional, just push the response token
  145. .wrap
  146. % c-sdk {
  147. static inline pio_sm_config pio_tx_w_clock_program_config(uint offset, uint data_pin, uint clk_pin, int clk_div) {
  148. pio_sm_config c = tx_data_w_clock_program_get_default_config(offset);
  149. sm_config_set_sideset_pins(&c, clk_pin);
  150. sm_config_set_out_pins(&c, data_pin, 4);
  151. sm_config_set_in_pins(&c, data_pin);
  152. sm_config_set_set_pins(&c, data_pin, 4);
  153. sm_config_set_in_shift(&c, false, false, 8);
  154. sm_config_set_out_shift(&c, false, true, 32);
  155. sm_config_set_jmp_pin(&c, data_pin);
  156. sm_config_set_clkdiv_int_frac(&c, clk_div, 0);
  157. return c;
  158. }
  159. %}