scsi_accel_dma.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. #include "scsi_accel_dma.h"
  2. #include <AzulSCSI_log.h>
  3. #include <gd32f20x_timer.h>
  4. #include <gd32f20x_rcu.h>
  5. #include <assert.h>
  6. #ifndef SCSI_ACCEL_DMA_AVAILABLE
  7. void scsi_accel_dma_init() {}
  8. void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag) {}
  9. void scsi_accel_dma_stopWrite() {}
  10. void scsi_accel_dma_finishWrite(volatile int *resetFlag) {}
  11. bool scsi_accel_dma_isWriteFinished(const uint8_t* data) { return true; }
  12. #else
  13. #define DMA_BUF_SIZE 256
  14. #define DMA_BUF_MASK (DMA_BUF_SIZE - 1)
  15. static struct {
  16. uint8_t *app_buf; // Buffer provided by application
  17. uint32_t dma_buf[DMA_BUF_SIZE]; // Buffer of data formatted for GPIO BOP register
  18. uint32_t dma_idx; // Write index to DMA buffer
  19. uint32_t dma_fillto; // Point up to which DMA buffer is available for refilling
  20. uint32_t timer_buf; // Control value for timer SWEVG register
  21. uint32_t bytes_app; // Bytes available in application buffer
  22. uint32_t bytes_dma; // Bytes (words) written so far to DMA buffer
  23. uint32_t scheduled_dma; // Bytes (words) that DMA data count was last set to
  24. uint8_t *next_app_buf; // Next buffer from application after current one finishes
  25. uint32_t next_app_bytes; // Bytes in next buffer
  26. } g_scsi_dma;
  27. enum scsidma_state_t { SCSIDMA_IDLE = 0, SCSIDMA_WRITE, SCSIDMA_READ };
  28. static volatile scsidma_state_t g_scsi_dma_state;
  29. void scsi_accel_dma_init()
  30. {
  31. g_scsi_dma_state = SCSIDMA_IDLE;
  32. rcu_periph_clock_enable(SCSI_TIMER_RCU);
  33. rcu_periph_clock_enable(SCSI_TIMER_DMA_RCU);
  34. // DMA Channel A: data copy
  35. // GPIO DMA copies data from memory buffer to GPIO BOP register.
  36. // The memory buffer is filled by interrupt routine.
  37. dma_parameter_struct gpio_dma_config =
  38. {
  39. .periph_addr = (uint32_t)&GPIO_BOP(SCSI_OUT_PORT),
  40. .periph_width = DMA_PERIPHERAL_WIDTH_32BIT,
  41. .memory_addr = 0, // Filled before transfer
  42. .memory_width = DMA_MEMORY_WIDTH_32BIT,
  43. .number = DMA_BUF_SIZE,
  44. .priority = DMA_PRIORITY_ULTRA_HIGH,
  45. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  46. .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
  47. .direction = DMA_MEMORY_TO_PERIPHERAL
  48. };
  49. dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA, &gpio_dma_config);
  50. dma_circulation_enable(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA);
  51. NVIC_SetPriority(SCSI_TIMER_DMACHA_IRQn, 1);
  52. NVIC_EnableIRQ(SCSI_TIMER_DMACHA_IRQn);
  53. // DMA Channel B: timer update
  54. // Timer DMA causes update event to restart timer after
  55. // GPIO DMA operation is done.
  56. dma_parameter_struct timer_dma_config =
  57. {
  58. .periph_addr = (uint32_t)&TIMER_SWEVG(SCSI_TIMER),
  59. .periph_width = DMA_PERIPHERAL_WIDTH_32BIT,
  60. .memory_addr = (uint32_t)&g_scsi_dma.timer_buf,
  61. .memory_width = DMA_MEMORY_WIDTH_32BIT,
  62. .number = DMA_BUF_SIZE,
  63. .priority = DMA_PRIORITY_HIGH,
  64. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  65. .memory_inc = DMA_PERIPH_INCREASE_DISABLE,
  66. .direction = DMA_MEMORY_TO_PERIPHERAL
  67. };
  68. dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB, &timer_dma_config);
  69. NVIC_SetPriority(SCSI_TIMER_DMACHB_IRQn, 2);
  70. NVIC_EnableIRQ(SCSI_TIMER_DMACHB_IRQn);
  71. g_scsi_dma.timer_buf = TIMER_SWEVG_UPG;
  72. // Timer is used to toggle the request signal based on external trigger input.
  73. // OUT_REQ is driven by timer output.
  74. // 1. On timer update event, REQ is set low.
  75. // 2. When ACK goes low, timer counts and OUT_REQ is set high.
  76. // Simultaneously a DMA request is triggered to write next data to GPIO.
  77. // 3. When ACK goes high, a DMA request is triggered to cause timer update event.
  78. // The DMA request priority is set so that 2. always completes before it.
  79. TIMER_CTL0(SCSI_TIMER) = 0;
  80. TIMER_SMCFG(SCSI_TIMER) = TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_CI0F_ED;
  81. TIMER_CAR(SCSI_TIMER) = 65535;
  82. TIMER_PSC(SCSI_TIMER) = 0;
  83. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  84. TIMER_CHCTL0(SCSI_TIMER) = 0x6001; // CH0 as input, CH1 as DMA trigger
  85. TIMER_CHCTL1(SCSI_TIMER) = 0x6074; // CH2 as fast PWM output, CH3 as DMA trigger
  86. TIMER_CHCTL2(SCSI_TIMER) = TIMER_CHCTL2_CH2NEN;
  87. TIMER_CCHP(SCSI_TIMER) = TIMER_CCHP_POEN;
  88. TIMER_CH1CV(SCSI_TIMER) = 1; // Copy data when ACK goes low
  89. TIMER_CH2CV(SCSI_TIMER) = 1; // REQ is low until ACK goes low
  90. TIMER_CH3CV(SCSI_TIMER) = 2; // Reset timer after ACK goes high & previous DMA is complete
  91. gpio_init(SCSI_TIMER_IN_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_TIMER_IN_PIN);
  92. scsi_accel_dma_stopWrite();
  93. }
  94. // Select whether OUT_REQ is connected to timer or GPIO port
  95. static void scsi_dma_gpio_config(bool enable_timer)
  96. {
  97. if (enable_timer)
  98. {
  99. gpio_init(SCSI_OUT_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ);
  100. gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN);
  101. }
  102. else
  103. {
  104. gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN);
  105. gpio_init(SCSI_OUT_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ);
  106. }
  107. }
  108. // Convert input bytes into BOP values in the DMA buffer
  109. static void refill_dmabuf()
  110. {
  111. // Check how many bytes we have available from the application
  112. uint32_t count = g_scsi_dma.bytes_app - g_scsi_dma.bytes_dma;
  113. // Check amount of free space in DMA buffer
  114. uint32_t max = g_scsi_dma.dma_fillto - g_scsi_dma.dma_idx;
  115. if (count > max) count = max;
  116. if (count == 0) return;
  117. uint8_t *src = g_scsi_dma.app_buf + g_scsi_dma.bytes_dma;
  118. uint32_t *dst = g_scsi_dma.dma_buf;
  119. uint32_t pos = g_scsi_dma.dma_idx;
  120. uint32_t end = pos + count;
  121. g_scsi_dma.dma_idx = end;
  122. g_scsi_dma.bytes_dma += count;
  123. while (pos + 4 <= end)
  124. {
  125. uint32_t input = *(uint32_t*)src;
  126. src += 4;
  127. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 0) & 0xFF];
  128. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 8) & 0xFF];
  129. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 16) & 0xFF];
  130. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 24) & 0xFF];
  131. }
  132. while (pos + 4 <= end)
  133. {
  134. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[*src++];
  135. }
  136. }
  137. // Start DMA transfer
  138. static void start_dma()
  139. {
  140. // Disable channels while configuring
  141. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  142. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  143. TIMER_CTL0(SCSI_TIMER) = 0;
  144. // Set new buffer address and size
  145. // CHA / Data channel is in circular mode and always has DMA_BUF_SIZE buffer size.
  146. // CHB / Update channel limits the number of data.
  147. DMA_CHMADDR(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = (uint32_t)g_scsi_dma.dma_buf;
  148. uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma;
  149. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule;
  150. g_scsi_dma.scheduled_dma += dma_to_schedule;
  151. // Clear pending DMA events
  152. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  153. TIMER_DMAINTEN(SCSI_TIMER) = TIMER_DMAINTEN_CH1DEN | TIMER_DMAINTEN_CH3DEN;
  154. // Clear and enable interrupt
  155. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHA);
  156. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHB);
  157. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE;
  158. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_FTFIE;
  159. // Enable channels
  160. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_CHEN;
  161. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN;
  162. // Make sure REQ is initially high
  163. TIMER_CNT(SCSI_TIMER) = 16;
  164. TIMER_CHCTL1(SCSI_TIMER) = 0x6050;
  165. TIMER_CHCTL1(SCSI_TIMER) = 0x6074;
  166. // Enable timer
  167. TIMER_CTL0(SCSI_TIMER) |= TIMER_CTL0_CEN;
  168. // Generate first events
  169. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH1G;
  170. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH3G;
  171. }
  172. // Stop DMA transfer
  173. static void stop_dma()
  174. {
  175. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  176. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  177. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~(DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE);
  178. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_FTFIE;
  179. TIMER_CTL0(SCSI_TIMER) &= ~TIMER_CTL0_CEN;
  180. g_scsi_dma_state = SCSIDMA_IDLE;
  181. SCSI_RELEASE_DATA_REQ();
  182. }
  183. // Convert new data from application buffer to DMA buffer
  184. extern "C" void SCSI_TIMER_DMACHA_IRQ()
  185. {
  186. // azdbg("DMA irq A, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  187. // DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  188. // TIMER_CNT(SCSI_TIMER));
  189. uint32_t intf = DMA_INTF(SCSI_TIMER_DMA);
  190. const uint32_t half_flag = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA);
  191. const uint32_t full_flag = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA);
  192. if (intf & half_flag)
  193. {
  194. if (intf & full_flag)
  195. {
  196. azlog("ERROR: SCSI DMA overrun: ", intf,
  197. " bytes_app: ", g_scsi_dma.bytes_app,
  198. " bytes_dma: ", g_scsi_dma.bytes_dma,
  199. " dma_idx: ", g_scsi_dma.dma_idx,
  200. " sched_dma: ", g_scsi_dma.scheduled_dma);
  201. stop_dma();
  202. return;
  203. }
  204. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA);
  205. g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2;
  206. }
  207. else if (intf & full_flag)
  208. {
  209. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA);
  210. g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2;
  211. }
  212. // Fill DMA buffer with data from current application buffer
  213. refill_dmabuf();
  214. // Check if we are at the end of the application buffer
  215. if (g_scsi_dma.next_app_buf && g_scsi_dma.bytes_dma == g_scsi_dma.bytes_app)
  216. {
  217. // Switch to next buffer
  218. assert(g_scsi_dma.scheduled_dma == g_scsi_dma.bytes_app);
  219. g_scsi_dma.app_buf = g_scsi_dma.next_app_buf;
  220. g_scsi_dma.bytes_app = g_scsi_dma.next_app_bytes;
  221. g_scsi_dma.bytes_dma = 0;
  222. g_scsi_dma.scheduled_dma = 0;
  223. g_scsi_dma.next_app_buf = 0;
  224. g_scsi_dma.next_app_bytes = 0;
  225. refill_dmabuf();
  226. }
  227. }
  228. // Check if enough data is available to continue DMA transfer
  229. extern "C" void SCSI_TIMER_DMACHB_IRQ()
  230. {
  231. // azdbg("DMA irq B, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  232. // DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  233. // TIMER_CNT(SCSI_TIMER));
  234. uint32_t intf = DMA_INTF(SCSI_TIMER_DMA);
  235. if (intf & DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB))
  236. {
  237. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB);
  238. if (g_scsi_dma.bytes_app > g_scsi_dma.scheduled_dma)
  239. {
  240. if (g_scsi_dma.dma_idx < g_scsi_dma.dma_fillto)
  241. {
  242. // Previous request didn't have a complete buffer worth of data.
  243. // Refill the buffer and ensure that the first byte of the new data gets
  244. // written to outputs.
  245. __disable_irq();
  246. uint32_t *first_data = &g_scsi_dma.dma_buf[g_scsi_dma.dma_idx & DMA_BUF_MASK];
  247. refill_dmabuf();
  248. GPIO_BOP(SCSI_OUT_PORT) = *first_data;
  249. __enable_irq();
  250. }
  251. // Update the total number of bytes available for DMA
  252. uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma;
  253. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  254. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule;
  255. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN;
  256. g_scsi_dma.scheduled_dma += dma_to_schedule;
  257. }
  258. else
  259. {
  260. // No more data available
  261. stop_dma();
  262. }
  263. }
  264. }
  265. void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag)
  266. {
  267. __disable_irq();
  268. if (g_scsi_dma_state == SCSIDMA_WRITE)
  269. {
  270. if (!g_scsi_dma.next_app_buf && data == g_scsi_dma.app_buf + g_scsi_dma.bytes_app)
  271. {
  272. // Combine with currently running request
  273. g_scsi_dma.bytes_app += count;
  274. count = 0;
  275. }
  276. else if (data == g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  277. {
  278. // Combine with queued request
  279. g_scsi_dma.next_app_bytes += count;
  280. count = 0;
  281. }
  282. else if (!g_scsi_dma.next_app_buf)
  283. {
  284. // Add as queued request
  285. g_scsi_dma.next_app_buf = (uint8_t*)data;
  286. g_scsi_dma.next_app_bytes = count;
  287. count = 0;
  288. }
  289. }
  290. __enable_irq();
  291. // Check if the request was combined
  292. if (count == 0) return;
  293. if (g_scsi_dma_state != SCSIDMA_IDLE)
  294. {
  295. // Wait for previous request to finish
  296. scsi_accel_dma_finishWrite(resetFlag);
  297. if (*resetFlag)
  298. {
  299. return;
  300. }
  301. }
  302. // azdbg("Starting DMA write of ", (int)count, " bytes");
  303. scsi_dma_gpio_config(true);
  304. g_scsi_dma_state = SCSIDMA_WRITE;
  305. g_scsi_dma.app_buf = (uint8_t*)data;
  306. g_scsi_dma.dma_idx = 0;
  307. g_scsi_dma.dma_fillto = DMA_BUF_SIZE;
  308. g_scsi_dma.bytes_app = count;
  309. g_scsi_dma.bytes_dma = 0;
  310. g_scsi_dma.scheduled_dma = 0;
  311. g_scsi_dma.next_app_buf = NULL;
  312. g_scsi_dma.next_app_bytes = 0;
  313. refill_dmabuf();
  314. start_dma();
  315. }
  316. bool scsi_accel_dma_isWriteFinished(const uint8_t* data)
  317. {
  318. // Check if everything has completed
  319. if (g_scsi_dma_state == SCSIDMA_IDLE)
  320. {
  321. return true;
  322. }
  323. if (!data)
  324. return false;
  325. // Check if this data item is still in queue.
  326. __disable_irq();
  327. bool finished = true;
  328. if (data >= g_scsi_dma.app_buf + g_scsi_dma.bytes_dma &&
  329. data < g_scsi_dma.app_buf + g_scsi_dma.bytes_app)
  330. {
  331. finished = false; // In current transfer
  332. }
  333. else if (data >= g_scsi_dma.next_app_buf &&
  334. data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  335. {
  336. finished = false; // In queued transfer
  337. }
  338. __enable_irq();
  339. return finished;
  340. }
  341. void scsi_accel_dma_stopWrite()
  342. {
  343. stop_dma();
  344. scsi_dma_gpio_config(false);
  345. }
  346. void scsi_accel_dma_finishWrite(volatile int *resetFlag)
  347. {
  348. uint32_t start = millis();
  349. while (g_scsi_dma_state != SCSIDMA_IDLE && !*resetFlag)
  350. {
  351. if ((uint32_t)(millis() - start) > 5000)
  352. {
  353. azlog("scsi_accel_dma_finishWrite() timeout, DMA counts ",
  354. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  355. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  356. TIMER_CNT(SCSI_TIMER));
  357. *resetFlag = 1;
  358. break;
  359. }
  360. }
  361. scsi_accel_dma_stopWrite();
  362. }
  363. #endif