scsi_accel_dma.cpp 14 KB

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