scsi_accel_rp2040.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /* Data flow in SCSI acceleration:
  2. *
  3. * 1. Application provides a buffer of bytes to send.
  4. * 2. Code in this module adds parity bit to the bytes and packs two bytes into 32 bit words.
  5. * 3. DMA controller copies the words to PIO peripheral FIFO.
  6. * 4. PIO peripheral handles low-level SCSI handshake and writes bytes and parity to GPIO.
  7. */
  8. #include "ZuluSCSI_platform.h"
  9. #include "ZuluSCSI_log.h"
  10. #include "scsi_accel_rp2040.h"
  11. #include "scsi_accel.pio.h"
  12. #include <hardware/pio.h>
  13. #include <hardware/dma.h>
  14. #include <hardware/irq.h>
  15. #include <hardware/structs/iobank0.h>
  16. #define SCSI_DMA_PIO pio0
  17. #define SCSI_DMA_SM 0
  18. #define SCSI_DMA_CH 0
  19. enum scsidma_buf_sel_t { SCSIBUF_NONE = 0, SCSIBUF_A = 1, SCSIBUF_B = 2 };
  20. #define DMA_BUF_SIZE 128
  21. static struct {
  22. uint8_t *app_buf; // Buffer provided by application
  23. uint32_t app_bytes; // Bytes available in application buffer
  24. uint32_t dma_bytes; // Bytes that have been converted to DMA buffer so far
  25. uint8_t *next_app_buf; // Next buffer from application after current one finishes
  26. uint32_t next_app_bytes; // Bytes in next buffer
  27. // PIO configurations
  28. uint32_t pio_offset_async_write;
  29. uint32_t pio_offset_async_read;
  30. pio_sm_config pio_cfg_async_write;
  31. pio_sm_config pio_cfg_async_read;
  32. // DMA configurations
  33. dma_channel_config dma_write_config;
  34. // We use two DMA buffers alternatively
  35. // The buffer contains the data bytes with parity added.
  36. scsidma_buf_sel_t dma_current_buf;
  37. uint32_t dma_countA;
  38. uint32_t dma_countB;
  39. uint32_t dma_bufA[DMA_BUF_SIZE];
  40. uint32_t dma_bufB[DMA_BUF_SIZE];
  41. } g_scsi_dma;
  42. enum scsidma_state_t { SCSIDMA_IDLE = 0,
  43. SCSIDMA_WRITE, SCSIDMA_WRITE_DONE,
  44. SCSIDMA_READ };
  45. static volatile scsidma_state_t g_scsi_dma_state;
  46. static bool g_channels_claimed = false;
  47. // Fill DMA buffer and return number of words ready to be transferred
  48. static uint32_t refill_dmabuf(uint32_t *buf)
  49. {
  50. if (g_scsi_dma.app_bytes == 0 && g_scsi_dma.next_app_bytes > 0)
  51. {
  52. g_scsi_dma.dma_bytes = 0;
  53. g_scsi_dma.app_buf = g_scsi_dma.next_app_buf;
  54. g_scsi_dma.app_bytes = g_scsi_dma.next_app_bytes;
  55. g_scsi_dma.next_app_buf = 0;
  56. g_scsi_dma.next_app_bytes = 0;
  57. }
  58. uint32_t count = (g_scsi_dma.app_bytes - g_scsi_dma.dma_bytes) / 2;
  59. if (count > DMA_BUF_SIZE) count = DMA_BUF_SIZE;
  60. uint16_t *src = (uint16_t*)&g_scsi_dma.app_buf[g_scsi_dma.dma_bytes];
  61. uint16_t *end = src + count;
  62. uint32_t *dst = buf;
  63. while (src < end)
  64. {
  65. uint16_t input = *src++;
  66. *dst++ = (g_scsi_parity_lookup[input & 0xFF])
  67. | ((g_scsi_parity_lookup[input >> 8]) << 16);
  68. }
  69. g_scsi_dma.dma_bytes += count * 2;
  70. // Check if this buffer has been fully processed
  71. if (g_scsi_dma.dma_bytes >= g_scsi_dma.app_bytes)
  72. {
  73. assert(g_scsi_dma.dma_bytes == g_scsi_dma.app_bytes);
  74. g_scsi_dma.dma_bytes = 0;
  75. g_scsi_dma.app_buf = g_scsi_dma.next_app_buf;
  76. g_scsi_dma.app_bytes = g_scsi_dma.next_app_bytes;
  77. g_scsi_dma.next_app_buf = 0;
  78. g_scsi_dma.next_app_bytes = 0;
  79. }
  80. return count;
  81. }
  82. // Select GPIO from PIO peripheral or from software controlled SIO
  83. static void scsidma_config_gpio()
  84. {
  85. if (g_scsi_dma_state == SCSIDMA_IDLE)
  86. {
  87. iobank0_hw->io[SCSI_IO_DB0].ctrl = GPIO_FUNC_SIO;
  88. iobank0_hw->io[SCSI_IO_DB1].ctrl = GPIO_FUNC_SIO;
  89. iobank0_hw->io[SCSI_IO_DB2].ctrl = GPIO_FUNC_SIO;
  90. iobank0_hw->io[SCSI_IO_DB3].ctrl = GPIO_FUNC_SIO;
  91. iobank0_hw->io[SCSI_IO_DB4].ctrl = GPIO_FUNC_SIO;
  92. iobank0_hw->io[SCSI_IO_DB5].ctrl = GPIO_FUNC_SIO;
  93. iobank0_hw->io[SCSI_IO_DB6].ctrl = GPIO_FUNC_SIO;
  94. iobank0_hw->io[SCSI_IO_DB7].ctrl = GPIO_FUNC_SIO;
  95. iobank0_hw->io[SCSI_IO_DBP].ctrl = GPIO_FUNC_SIO;
  96. iobank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_SIO;
  97. }
  98. else if (g_scsi_dma_state == SCSIDMA_WRITE)
  99. {
  100. // Make sure the initial state of all pins is high and output
  101. pio_sm_set_pins(SCSI_DMA_PIO, SCSI_DMA_SM, 0x3FF);
  102. pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DMA_SM, 0, 10, true);
  103. iobank0_hw->io[SCSI_IO_DB0].ctrl = GPIO_FUNC_PIO0;
  104. iobank0_hw->io[SCSI_IO_DB1].ctrl = GPIO_FUNC_PIO0;
  105. iobank0_hw->io[SCSI_IO_DB2].ctrl = GPIO_FUNC_PIO0;
  106. iobank0_hw->io[SCSI_IO_DB3].ctrl = GPIO_FUNC_PIO0;
  107. iobank0_hw->io[SCSI_IO_DB4].ctrl = GPIO_FUNC_PIO0;
  108. iobank0_hw->io[SCSI_IO_DB5].ctrl = GPIO_FUNC_PIO0;
  109. iobank0_hw->io[SCSI_IO_DB6].ctrl = GPIO_FUNC_PIO0;
  110. iobank0_hw->io[SCSI_IO_DB7].ctrl = GPIO_FUNC_PIO0;
  111. iobank0_hw->io[SCSI_IO_DBP].ctrl = GPIO_FUNC_PIO0;
  112. iobank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_PIO0;
  113. }
  114. else if (g_scsi_dma_state == SCSIDMA_READ)
  115. {
  116. // Data bus as input, REQ pin as output
  117. pio_sm_set_pins(SCSI_DMA_PIO, SCSI_DMA_SM, 0x3FF);
  118. pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DMA_SM, 0, 9, false);
  119. pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DMA_SM, 9, 1, true);
  120. iobank0_hw->io[SCSI_IO_DB0].ctrl = GPIO_FUNC_SIO;
  121. iobank0_hw->io[SCSI_IO_DB1].ctrl = GPIO_FUNC_SIO;
  122. iobank0_hw->io[SCSI_IO_DB2].ctrl = GPIO_FUNC_SIO;
  123. iobank0_hw->io[SCSI_IO_DB3].ctrl = GPIO_FUNC_SIO;
  124. iobank0_hw->io[SCSI_IO_DB4].ctrl = GPIO_FUNC_SIO;
  125. iobank0_hw->io[SCSI_IO_DB5].ctrl = GPIO_FUNC_SIO;
  126. iobank0_hw->io[SCSI_IO_DB6].ctrl = GPIO_FUNC_SIO;
  127. iobank0_hw->io[SCSI_IO_DB7].ctrl = GPIO_FUNC_SIO;
  128. iobank0_hw->io[SCSI_IO_DBP].ctrl = GPIO_FUNC_SIO;
  129. iobank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_PIO0;
  130. }
  131. }
  132. static void start_dma_write()
  133. {
  134. // Prefill both DMA buffers
  135. g_scsi_dma.dma_countA = refill_dmabuf(g_scsi_dma.dma_bufA);
  136. g_scsi_dma.dma_countB = refill_dmabuf(g_scsi_dma.dma_bufB);
  137. // Start DMA from buffer A
  138. g_scsi_dma.dma_current_buf = SCSIBUF_A;
  139. dma_channel_configure(SCSI_DMA_CH,
  140. &g_scsi_dma.dma_write_config,
  141. &SCSI_DMA_PIO->txf[SCSI_DMA_SM],
  142. g_scsi_dma.dma_bufA,
  143. g_scsi_dma.dma_countA,
  144. true
  145. );
  146. }
  147. static void scsi_dma_write_irq()
  148. {
  149. dma_hw->ints0 = 1 << SCSI_DMA_CH;
  150. if (g_scsi_dma.dma_current_buf == SCSIBUF_A)
  151. {
  152. // Transfer from buffer A finished
  153. g_scsi_dma.dma_countA = 0;
  154. g_scsi_dma.dma_current_buf = SCSIBUF_NONE;
  155. if (g_scsi_dma.dma_countB != 0)
  156. {
  157. // Start transferring buffer B immediately
  158. dma_channel_set_trans_count(SCSI_DMA_CH, g_scsi_dma.dma_countB, false);
  159. dma_channel_set_read_addr(SCSI_DMA_CH, g_scsi_dma.dma_bufB, true);
  160. g_scsi_dma.dma_current_buf = SCSIBUF_B;
  161. // Refill buffer A for next time
  162. g_scsi_dma.dma_countA = refill_dmabuf(g_scsi_dma.dma_bufA);
  163. }
  164. }
  165. else
  166. {
  167. // Transfer from buffer B finished
  168. g_scsi_dma.dma_countB = 0;
  169. g_scsi_dma.dma_current_buf = SCSIBUF_NONE;
  170. if (g_scsi_dma.dma_countA != 0)
  171. {
  172. // Start transferring buffer A immediately
  173. dma_channel_set_trans_count(SCSI_DMA_CH, g_scsi_dma.dma_countA, false);
  174. dma_channel_set_read_addr(SCSI_DMA_CH, g_scsi_dma.dma_bufA, true);
  175. g_scsi_dma.dma_current_buf = SCSIBUF_A;
  176. // Refill buffer B for next time
  177. g_scsi_dma.dma_countB = refill_dmabuf(g_scsi_dma.dma_bufB);
  178. }
  179. }
  180. if (g_scsi_dma.dma_current_buf == SCSIBUF_NONE)
  181. {
  182. // Both buffers are empty, check if we have more data
  183. g_scsi_dma.dma_countA = refill_dmabuf(g_scsi_dma.dma_bufA);
  184. if (g_scsi_dma.dma_countA == 0)
  185. {
  186. // End of data for DMA, but PIO may still have bytes in its buffer
  187. g_scsi_dma_state = SCSIDMA_WRITE_DONE;
  188. }
  189. else
  190. {
  191. // Start transfer from buffer A
  192. dma_channel_set_trans_count(SCSI_DMA_CH, g_scsi_dma.dma_countA, false);
  193. dma_channel_set_read_addr(SCSI_DMA_CH, g_scsi_dma.dma_bufA, true);
  194. g_scsi_dma.dma_current_buf = SCSIBUF_A;
  195. // Refill B for the next interrupt
  196. g_scsi_dma.dma_countB = refill_dmabuf(g_scsi_dma.dma_bufB);
  197. }
  198. }
  199. }
  200. void scsi_accel_rp2040_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag)
  201. {
  202. // Number of bytes should always be divisible by 2.
  203. assert((count & 1) == 0);
  204. __disable_irq();
  205. if (g_scsi_dma_state == SCSIDMA_WRITE)
  206. {
  207. if (!g_scsi_dma.next_app_buf && data == g_scsi_dma.app_buf + g_scsi_dma.app_bytes)
  208. {
  209. // Combine with currently running request
  210. g_scsi_dma.app_bytes += count;
  211. count = 0;
  212. }
  213. else if (data == g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  214. {
  215. // Combine with queued request
  216. g_scsi_dma.next_app_bytes += count;
  217. count = 0;
  218. }
  219. else if (!g_scsi_dma.next_app_buf)
  220. {
  221. // Add as queued request
  222. g_scsi_dma.next_app_buf = (uint8_t*)data;
  223. g_scsi_dma.next_app_bytes = count;
  224. count = 0;
  225. }
  226. }
  227. __enable_irq();
  228. // Check if the request was combined
  229. if (count == 0) return;
  230. if (g_scsi_dma_state != SCSIDMA_IDLE && g_scsi_dma_state != SCSIDMA_WRITE_DONE)
  231. {
  232. // Wait for previous request to finish
  233. scsi_accel_rp2040_finishWrite(resetFlag);
  234. if (*resetFlag)
  235. {
  236. return;
  237. }
  238. }
  239. bool must_reconfig_gpio = (g_scsi_dma_state == SCSIDMA_IDLE);
  240. g_scsi_dma_state = SCSIDMA_WRITE;
  241. g_scsi_dma.app_buf = (uint8_t*)data;
  242. g_scsi_dma.app_bytes = count;
  243. g_scsi_dma.dma_bytes = 0;
  244. g_scsi_dma.next_app_buf = 0;
  245. g_scsi_dma.next_app_bytes = 0;
  246. g_scsi_dma.dma_current_buf = SCSIBUF_NONE;
  247. if (must_reconfig_gpio)
  248. {
  249. SCSI_ENABLE_DATA_OUT();
  250. pio_sm_init(SCSI_DMA_PIO, SCSI_DMA_SM, g_scsi_dma.pio_offset_async_write, &g_scsi_dma.pio_cfg_async_write);
  251. scsidma_config_gpio();
  252. pio_sm_set_enabled(SCSI_DMA_PIO, SCSI_DMA_SM, true);
  253. dma_channel_set_irq0_enabled(SCSI_DMA_CH, true);
  254. irq_set_exclusive_handler(DMA_IRQ_0, scsi_dma_write_irq);
  255. irq_set_enabled(DMA_IRQ_0, true);
  256. }
  257. start_dma_write();
  258. }
  259. bool scsi_accel_rp2040_isWriteFinished(const uint8_t* data)
  260. {
  261. // Check if everything has completed
  262. if (g_scsi_dma_state == SCSIDMA_IDLE || g_scsi_dma_state == SCSIDMA_WRITE_DONE)
  263. {
  264. return true;
  265. }
  266. if (!data)
  267. return false;
  268. // Check if this data item is still in queue.
  269. __disable_irq();
  270. bool finished = true;
  271. if (data >= g_scsi_dma.app_buf + g_scsi_dma.dma_bytes &&
  272. data < g_scsi_dma.app_buf + g_scsi_dma.app_bytes)
  273. {
  274. finished = false; // In current transfer
  275. }
  276. else if (data >= g_scsi_dma.next_app_buf &&
  277. data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  278. {
  279. finished = false; // In queued transfer
  280. }
  281. __enable_irq();
  282. return finished;
  283. }
  284. void scsi_accel_rp2040_stopWrite(volatile int *resetFlag)
  285. {
  286. // Wait for TX fifo to be empty and ACK to go high
  287. uint32_t start = millis();
  288. while ((!pio_sm_is_tx_fifo_empty(SCSI_DMA_PIO, SCSI_DMA_SM) || SCSI_IN(ACK)) && !*resetFlag)
  289. {
  290. if ((uint32_t)(millis() - start) > 5000)
  291. {
  292. azlog("scsi_accel_rp2040_stopWrite() timeout");
  293. *resetFlag = 1;
  294. break;
  295. }
  296. }
  297. dma_channel_abort(SCSI_DMA_CH);
  298. dma_channel_set_irq0_enabled(SCSI_DMA_CH, false);
  299. g_scsi_dma_state = SCSIDMA_IDLE;
  300. SCSI_RELEASE_DATA_REQ();
  301. scsidma_config_gpio();
  302. pio_sm_set_enabled(SCSI_DMA_PIO, SCSI_DMA_SM, false);
  303. }
  304. void scsi_accel_rp2040_finishWrite(volatile int *resetFlag)
  305. {
  306. uint32_t start = millis();
  307. while (g_scsi_dma_state != SCSIDMA_IDLE && !*resetFlag)
  308. {
  309. if ((uint32_t)(millis() - start) > 5000)
  310. {
  311. azlog("scsi_accel_rp2040_finishWrite() timeout");
  312. *resetFlag = 1;
  313. break;
  314. }
  315. if (g_scsi_dma_state == SCSIDMA_WRITE_DONE)
  316. {
  317. // DMA done, wait for PIO to finish also and reconfig GPIO.
  318. scsi_accel_rp2040_stopWrite(resetFlag);
  319. }
  320. }
  321. }
  322. void scsi_accel_rp2040_read(uint8_t *buf, uint32_t count, int *parityError, volatile int *resetFlag)
  323. {
  324. // The hardware would support DMA for reading from SCSI bus also, but currently
  325. // the rest of the software architecture does not. There is not much benefit
  326. // because there isn't much else to do before we get the data from the SCSI bus.
  327. //
  328. // Currently this method just reads from the PIO RX fifo directly in software loop.
  329. g_scsi_dma_state = SCSIDMA_READ;
  330. pio_sm_init(SCSI_DMA_PIO, SCSI_DMA_SM, g_scsi_dma.pio_offset_async_read, &g_scsi_dma.pio_cfg_async_read);
  331. scsidma_config_gpio();
  332. pio_sm_set_enabled(SCSI_DMA_PIO, SCSI_DMA_SM, true);
  333. // Set the number of bytes to read, must be divisible by 2.
  334. assert((count & 1) == 0);
  335. pio_sm_put(SCSI_DMA_PIO, SCSI_DMA_SM, count - 1);
  336. // Read results from PIO RX FIFO
  337. uint8_t *dst = buf;
  338. uint8_t *end = buf + count;
  339. uint32_t paritycheck = 0;
  340. while (dst < end)
  341. {
  342. if (*resetFlag)
  343. {
  344. break;
  345. }
  346. uint32_t available = pio_sm_get_rx_fifo_level(SCSI_DMA_PIO, SCSI_DMA_SM);
  347. while (available > 0)
  348. {
  349. available--;
  350. uint32_t word = pio_sm_get(SCSI_DMA_PIO, SCSI_DMA_SM);
  351. paritycheck ^= word;
  352. word = ~word;
  353. *dst++ = word & 0xFF;
  354. *dst++ = word >> 16;
  355. }
  356. }
  357. // Check parity errors in whole block
  358. // This doesn't detect if there is even number of parity errors in block.
  359. uint8_t byte0 = ~(paritycheck & 0xFF);
  360. uint8_t byte1 = ~(paritycheck >> 16);
  361. if (paritycheck != ((g_scsi_parity_lookup[byte1] << 16) | g_scsi_parity_lookup[byte0]))
  362. {
  363. azlog("Parity error in scsi_accel_rp2040_read(): ", paritycheck);
  364. *parityError = 1;
  365. }
  366. g_scsi_dma_state = SCSIDMA_IDLE;
  367. SCSI_RELEASE_DATA_REQ();
  368. scsidma_config_gpio();
  369. pio_sm_set_enabled(SCSI_DMA_PIO, SCSI_DMA_SM, false);
  370. }
  371. void scsi_accel_rp2040_init()
  372. {
  373. g_scsi_dma_state = SCSIDMA_IDLE;
  374. scsidma_config_gpio();
  375. // Mark channels as being in use, unless it has been done already
  376. if (!g_channels_claimed)
  377. {
  378. pio_sm_claim(SCSI_DMA_PIO, SCSI_DMA_SM);
  379. dma_channel_claim(SCSI_DMA_CH);
  380. g_channels_claimed = true;
  381. }
  382. // Load PIO programs
  383. pio_clear_instruction_memory(SCSI_DMA_PIO);
  384. // Asynchronous SCSI write
  385. g_scsi_dma.pio_offset_async_write = pio_add_program(SCSI_DMA_PIO, &scsi_accel_async_write_program);
  386. g_scsi_dma.pio_cfg_async_write = scsi_accel_async_write_program_get_default_config(g_scsi_dma.pio_offset_async_write);
  387. sm_config_set_out_pins(&g_scsi_dma.pio_cfg_async_write, SCSI_IO_DB0, 9);
  388. sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_async_write, SCSI_OUT_REQ);
  389. sm_config_set_fifo_join(&g_scsi_dma.pio_cfg_async_write, PIO_FIFO_JOIN_TX);
  390. sm_config_set_out_shift(&g_scsi_dma.pio_cfg_async_write, true, false, 32);
  391. // Asynchronous SCSI read
  392. g_scsi_dma.pio_offset_async_read = pio_add_program(SCSI_DMA_PIO, &scsi_accel_async_read_program);
  393. g_scsi_dma.pio_cfg_async_read = scsi_accel_async_read_program_get_default_config(g_scsi_dma.pio_offset_async_read);
  394. sm_config_set_in_pins(&g_scsi_dma.pio_cfg_async_read, SCSI_IO_DB0);
  395. sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_async_read, SCSI_OUT_REQ);
  396. sm_config_set_out_shift(&g_scsi_dma.pio_cfg_async_write, true, false, 32);
  397. sm_config_set_in_shift(&g_scsi_dma.pio_cfg_async_read, true, true, 32);
  398. // Create DMA channel configuration so it can be applied quickly later
  399. dma_channel_config cfg = dma_channel_get_default_config(SCSI_DMA_CH);
  400. channel_config_set_transfer_data_size(&cfg, DMA_SIZE_32);
  401. channel_config_set_read_increment(&cfg, true);
  402. channel_config_set_write_increment(&cfg, false);
  403. channel_config_set_dreq(&cfg, pio_get_dreq(SCSI_DMA_PIO, SCSI_DMA_SM, true));
  404. g_scsi_dma.dma_write_config = cfg;
  405. }