scsi_accel_asm.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /**
  2. * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  3. *
  4. * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  5. *
  6. * https://www.gnu.org/licenses/gpl-3.0.html
  7. * ----
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version. 
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. * GNU General Public License for more details. 
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  20. **/
  21. #include "scsi_accel_asm.h"
  22. #include "ZuluSCSI_platform.h"
  23. // Optimized ASM blocks for the SCSI communication subroutine
  24. // Take 8 bits from d and format them for writing
  25. // d is name of data operand, b is bit offset, x is unique label
  26. #define ASM_LOAD_DATA(d, b, x) \
  27. " load_data1_" x "_%=: \n" \
  28. " ubfx %[tmp1], %[" d "], #" b ", #8 \n" \
  29. " ldr %[tmp1], [%[byte_lookup], %[tmp1], lsl #2] \n"
  30. // Write data to SCSI port and set REQ high
  31. #define ASM_SEND_DATA(x) \
  32. " send_data" x "_%=: \n" \
  33. " str %[tmp1], [%[out_port_bop]] \n"
  34. // Read data from SCSI port, set REQ high, store data to d at bit offset b
  35. #define ASM_RECV_DATA(d, b, x) \
  36. " recv_data" x "_%=: \n" \
  37. " ldr %[tmp1], [%[in_port_istat]] \n" \
  38. " mov %[tmp2], %[req_high_bop] \n" \
  39. " str %[tmp2], [%[out_port_bop]] \n" \
  40. " ubfx %[tmp1], %[tmp1], %[data_in_shift], #8 \n" \
  41. " bfi %[" d "], %[tmp1], #" b ", #8 \n"
  42. // Wait for ACK to be high, set REQ low, wait ACK low
  43. #define ASM_HANDSHAKE(x) \
  44. " ldr %[tmp2], [%[ack_pin_bb]] \n" \
  45. " str %[tmp2], [%[req_pin_bb]] \n" \
  46. " cbnz %[tmp2], req_is_low_now" x "_%= \n" \
  47. " ldr %[tmp2], [%[ack_pin_bb]] \n" \
  48. " str %[tmp2], [%[req_pin_bb]] \n" \
  49. " cbnz %[tmp2], req_is_low_now" x "_%= \n" \
  50. " ldr %[tmp2], [%[ack_pin_bb]] \n" \
  51. " str %[tmp2], [%[req_pin_bb]] \n" \
  52. " cbnz %[tmp2], req_is_low_now" x "_%= \n" \
  53. " wait_ack_inactive" x "_%=: \n" \
  54. " ldr %[tmp2], [%[ack_pin_bb]] \n" \
  55. " str %[tmp2], [%[req_pin_bb]] \n" \
  56. " cbnz %[tmp2], req_is_low_now" x "_%= \n" \
  57. " ldr %[tmp2], [%[reset_flag]] \n" \
  58. " cbnz %[tmp2], req_is_low_now" x "_%= \n" \
  59. " b.n wait_ack_inactive" x "_%= \n" \
  60. " req_is_low_now" x "_%=: \n" \
  61. " ldr %[tmp2], [%[ack_pin_bb]] \n" \
  62. " cbz %[tmp2], over_ack_active" x "_%= \n" \
  63. " ldr %[tmp2], [%[ack_pin_bb]] \n" \
  64. " cbz %[tmp2], over_ack_active" x "_%= \n" \
  65. " ldr %[tmp2], [%[ack_pin_bb]] \n" \
  66. " cbz %[tmp2], over_ack_active" x "_%= \n" \
  67. " ldr %[tmp2], [%[ack_pin_bb]] \n" \
  68. " cbz %[tmp2], over_ack_active" x "_%= \n" \
  69. " wait_ack_active" x "_%=: \n" \
  70. " ldr %[tmp2], [%[ack_pin_bb]] \n" \
  71. " cbz %[tmp2], over_ack_active" x "_%= \n" \
  72. " ldr %[tmp2], [%[reset_flag]] \n" \
  73. " cbnz %[tmp2], over_ack_active" x "_%= \n" \
  74. " b.n wait_ack_active" x "_%= \n" \
  75. " over_ack_active" x "_%=: \n" \
  76. // Send bytes to SCSI bus using the asynchronous handshake mechanism
  77. // Takes 4 bytes at a time for sending from buf.
  78. void scsi_accel_asm_send(const uint32_t *buf, uint32_t num_words, volatile int *resetFlag)
  79. {
  80. volatile uint32_t *out_port_bop = (volatile uint32_t*)&GPIO_BOP(SCSI_OUT_PORT);
  81. const uint32_t *byte_lookup = g_scsi_out_byte_to_bop;
  82. uint32_t ack_pin_bb = PERIPH_BB_BASE + (((uint32_t)&GPIO_ISTAT(SCSI_ACK_PORT)) - APB1_BUS_BASE) * 32 + SCSI_IN_ACK_IDX * 4;
  83. uint32_t req_pin_bb = PERIPH_BB_BASE + (((uint32_t)out_port_bop) - APB1_BUS_BASE) * 32 + (SCSI_OUT_REQ_IDX + 16) * 4;
  84. register uint32_t tmp1 = 0;
  85. register uint32_t tmp2 = 0;
  86. register uint32_t data = 0;
  87. asm volatile (
  88. " ldr %[data], [%[buf]], #4 \n" \
  89. ASM_LOAD_DATA("data", "0", "first")
  90. "inner_loop_%=: \n" \
  91. ASM_SEND_DATA("0")
  92. ASM_LOAD_DATA("data", "8", "8")
  93. ASM_HANDSHAKE("0")
  94. ASM_SEND_DATA("8")
  95. ASM_LOAD_DATA("data", "16", "16")
  96. ASM_HANDSHAKE("8")
  97. ASM_SEND_DATA("16")
  98. ASM_LOAD_DATA("data", "24", "24")
  99. ASM_HANDSHAKE("16")
  100. ASM_SEND_DATA("24")
  101. " ldr %[data], [%[buf]], #4 \n" \
  102. ASM_LOAD_DATA("data", "0", "0")
  103. ASM_HANDSHAKE("24")
  104. " subs %[num_words], %[num_words], #1 \n" \
  105. " bne inner_loop_%= \n"
  106. : /* Output */ [tmp1] "+l" (tmp1), [tmp2] "+l" (tmp2), [data] "+r" (data),
  107. [buf] "+r" (buf), [num_words] "+r" (num_words)
  108. : /* Input */ [ack_pin_bb] "r" (ack_pin_bb),
  109. [req_pin_bb] "r" (req_pin_bb),
  110. [out_port_bop] "r"(out_port_bop),
  111. [byte_lookup] "r" (byte_lookup),
  112. [reset_flag] "r" (resetFlag)
  113. : /* Clobber */ );
  114. SCSI_RELEASE_DATA_REQ();
  115. }
  116. // Read bytes from SCSI bus using asynchronous handshake mechanism
  117. // Takes 4 bytes at a time.
  118. void scsi_accel_asm_recv(uint32_t *buf, uint32_t num_words, volatile int *resetFlag)
  119. {
  120. volatile uint32_t *out_port_bop = (volatile uint32_t*)&GPIO_BOP(SCSI_OUT_PORT);
  121. volatile uint32_t *in_port_istat = (volatile uint32_t*)&GPIO_ISTAT(SCSI_IN_PORT);
  122. uint32_t ack_pin_bb = PERIPH_BB_BASE + (((uint32_t)&GPIO_ISTAT(SCSI_ACK_PORT)) - APB1_BUS_BASE) * 32 + SCSI_IN_ACK_IDX * 4;
  123. uint32_t req_pin_bb = PERIPH_BB_BASE + (((uint32_t)out_port_bop) - APB1_BUS_BASE) * 32 + (SCSI_OUT_REQ_IDX + 16) * 4;
  124. register uint32_t tmp1 = 0;
  125. register uint32_t tmp2 = 0;
  126. register uint32_t data = 0;
  127. asm volatile (
  128. "inner_loop_%=: \n" \
  129. ASM_HANDSHAKE("0")
  130. ASM_RECV_DATA("data", "0", "0")
  131. ASM_HANDSHAKE("8")
  132. ASM_RECV_DATA("data", "8", "8")
  133. ASM_HANDSHAKE("16")
  134. ASM_RECV_DATA("data", "16", "16")
  135. ASM_HANDSHAKE("24")
  136. ASM_RECV_DATA("data", "24", "24")
  137. " mvn %[data], %[data] \n" \
  138. " str %[data], [%[buf]], #4 \n" \
  139. " subs %[num_words], %[num_words], #1 \n" \
  140. " bne inner_loop_%= \n"
  141. : /* Output */ [tmp1] "+l" (tmp1), [tmp2] "+l" (tmp2), [data] "+r" (data),
  142. [buf] "+r" (buf), [num_words] "+r" (num_words)
  143. : /* Input */ [ack_pin_bb] "r" (ack_pin_bb),
  144. [req_pin_bb] "r" (req_pin_bb),
  145. [out_port_bop] "r"(out_port_bop),
  146. [in_port_istat] "r" (in_port_istat),
  147. [reset_flag] "r" (resetFlag),
  148. [data_in_shift] "I" (SCSI_IN_SHIFT),
  149. [req_high_bop] "I" (SCSI_OUT_REQ)
  150. : /* Clobber */ );
  151. SCSI_RELEASE_DATA_REQ();
  152. }