scsi_accel_dma.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  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_dma.h"
  22. #include <ZuluSCSI_log.h>
  23. #include <ZuluSCSI_disk.h>
  24. #include <scsi2sd.h>
  25. #include <gd32f20x_timer.h>
  26. #include <gd32f20x_rcu.h>
  27. #include <assert.h>
  28. #include <string.h>
  29. #ifndef SCSI_ACCEL_DMA_AVAILABLE
  30. void scsi_accel_timer_dma_init() {}
  31. void scsi_accel_greenpak_dma_init() {}
  32. void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag) {}
  33. void scsi_accel_dma_stopWrite() {}
  34. void scsi_accel_dma_finishWrite(volatile int *resetFlag) {}
  35. bool scsi_accel_dma_isWriteFinished(const uint8_t* data) { return true; }
  36. #else
  37. static void greenpak_refill_dmabuf();
  38. static void greenpak_start_dma();
  39. static void greenpak_stop_dma();
  40. enum greenpak_state_t { GREENPAK_IO1_LOW = 0, GREENPAK_IO1_HIGH, GREENPAK_STOP};
  41. #define DMA_BUF_SIZE 256
  42. #define DMA_BUF_MASK (DMA_BUF_SIZE - 1)
  43. static struct {
  44. uint8_t *app_buf; // Buffer provided by application
  45. uint32_t dma_buf[DMA_BUF_SIZE]; // Buffer of data formatted for GPIO BOP register
  46. uint32_t dma_idx; // Write index to DMA buffer
  47. uint32_t dma_fillto; // Point up to which DMA buffer is available for refilling
  48. uint32_t timer_buf; // Control value for timer SWEVG register
  49. uint32_t bytes_app; // Bytes available in application buffer
  50. uint32_t bytes_dma; // Bytes (words) written so far to DMA buffer
  51. uint32_t scheduled_dma; // Bytes (words) that DMA data count was last set to
  52. greenpak_state_t greenpak_state; // Toggle signal state for greenpak
  53. uint8_t *next_app_buf; // Next buffer from application after current one finishes
  54. uint32_t next_app_bytes; // Bytes in next buffer
  55. } g_scsi_dma;
  56. enum scsidma_state_t { SCSIDMA_IDLE = 0, SCSIDMA_WRITE };
  57. static volatile scsidma_state_t g_scsi_dma_state;
  58. static bool g_scsi_dma_use_greenpak;
  59. void scsi_accel_timer_dma_init()
  60. {
  61. // TODO: find root cause of of DMA timeout
  62. // Temporary fix, setting prefetch prefetch buffer to 4k seems to fix a timing issue
  63. // in Timer DMA SCSI phy mode only
  64. image_config_t* img;
  65. for (uint8_t i = 0; i < S2S_MAX_TARGETS; i++)
  66. {
  67. img = &scsiDiskGetImageConfig(i);
  68. if (img->prefetchbytes > 4096)
  69. {
  70. img->prefetchbytes = 4096;
  71. }
  72. }
  73. g_scsi_dma_state = SCSIDMA_IDLE;
  74. g_scsi_dma_use_greenpak = false;
  75. rcu_periph_clock_enable(SCSI_TIMER_RCU);
  76. rcu_periph_clock_enable(SCSI_TIMER_DMA_RCU);
  77. // DMA Channel A: data copy
  78. // GPIO DMA copies data from memory buffer to GPIO BOP register.
  79. // The memory buffer is filled by interrupt routine.
  80. dma_parameter_struct gpio_dma_config =
  81. {
  82. .periph_addr = (uint32_t)&GPIO_BOP(SCSI_OUT_PORT),
  83. .periph_width = DMA_PERIPHERAL_WIDTH_32BIT,
  84. .memory_addr = 0, // Filled before transfer
  85. .memory_width = DMA_MEMORY_WIDTH_32BIT,
  86. .number = DMA_BUF_SIZE,
  87. .priority = DMA_PRIORITY_ULTRA_HIGH,
  88. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  89. .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
  90. .direction = DMA_MEMORY_TO_PERIPHERAL
  91. };
  92. dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA, &gpio_dma_config);
  93. dma_circulation_enable(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA);
  94. NVIC_SetPriority(SCSI_TIMER_DMACHA_IRQn, 1);
  95. NVIC_EnableIRQ(SCSI_TIMER_DMACHA_IRQn);
  96. // DMA Channel B: timer update
  97. // Timer DMA causes update event to restart timer after
  98. // GPIO DMA operation is done.
  99. dma_parameter_struct timer_dma_config =
  100. {
  101. .periph_addr = (uint32_t)&TIMER_SWEVG(SCSI_TIMER),
  102. .periph_width = DMA_PERIPHERAL_WIDTH_32BIT,
  103. .memory_addr = (uint32_t)&g_scsi_dma.timer_buf,
  104. .memory_width = DMA_MEMORY_WIDTH_32BIT,
  105. .number = DMA_BUF_SIZE,
  106. .priority = DMA_PRIORITY_HIGH,
  107. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  108. .memory_inc = DMA_PERIPH_INCREASE_DISABLE,
  109. .direction = DMA_MEMORY_TO_PERIPHERAL
  110. };
  111. dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB, &timer_dma_config);
  112. NVIC_SetPriority(SCSI_TIMER_DMACHB_IRQn, 2);
  113. NVIC_EnableIRQ(SCSI_TIMER_DMACHB_IRQn);
  114. g_scsi_dma.timer_buf = TIMER_SWEVG_UPG;
  115. // Timer is used to toggle the request signal based on external trigger input.
  116. // OUT_REQ is driven by timer output.
  117. // 1. On timer update event, REQ is set low.
  118. // 2. When ACK goes low, timer counts and OUT_REQ is set high.
  119. // Simultaneously a DMA request is triggered to write next data to GPIO.
  120. // 3. When ACK goes high, a DMA request is triggered to cause timer update event.
  121. // The DMA request priority is set so that 2. always completes before it.
  122. TIMER_CTL0(SCSI_TIMER) = 0;
  123. TIMER_SMCFG(SCSI_TIMER) = TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_CI0F_ED;
  124. TIMER_CAR(SCSI_TIMER) = 65535;
  125. TIMER_PSC(SCSI_TIMER) = 0;
  126. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  127. TIMER_CHCTL0(SCSI_TIMER) = 0x6001; // CH0 as input, CH1 as DMA trigger
  128. TIMER_CHCTL1(SCSI_TIMER) = 0x6074; // CH2 as fast PWM output, CH3 as DMA trigger
  129. TIMER_CHCTL2(SCSI_TIMER) = TIMER_CHCTL2_CH2NEN;
  130. TIMER_CCHP(SCSI_TIMER) = TIMER_CCHP_POEN;
  131. TIMER_CH1CV(SCSI_TIMER) = 1; // Copy data when ACK goes low
  132. TIMER_CH2CV(SCSI_TIMER) = 1; // REQ is low until ACK goes low
  133. TIMER_CH3CV(SCSI_TIMER) = 2; // Reset timer after ACK goes high & previous DMA is complete
  134. gpio_init(SCSI_TIMER_IN_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_TIMER_IN_PIN);
  135. scsi_accel_dma_stopWrite();
  136. }
  137. // Select whether OUT_REQ is connected to timer or GPIO port
  138. static void scsi_dma_gpio_config(bool enable)
  139. {
  140. if (enable)
  141. {
  142. gpio_init(SCSI_OUT_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ);
  143. if (g_scsi_dma_use_greenpak)
  144. {
  145. GPIO_BC(SCSI_OUT_PORT) = GREENPAK_PLD_IO1;
  146. GPIO_BOP(SCSI_OUT_PORT) = GREENPAK_PLD_IO2;
  147. }
  148. else
  149. {
  150. gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN);
  151. }
  152. }
  153. else
  154. {
  155. GPIO_BC(SCSI_OUT_PORT) = GREENPAK_PLD_IO2;
  156. gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN);
  157. gpio_init(SCSI_OUT_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ);
  158. }
  159. }
  160. // Convert input bytes into BOP values in the DMA buffer
  161. static void refill_dmabuf()
  162. {
  163. if (g_scsi_dma_use_greenpak)
  164. {
  165. greenpak_refill_dmabuf();
  166. return;
  167. }
  168. // Check how many bytes we have available from the application
  169. uint32_t count = g_scsi_dma.bytes_app - g_scsi_dma.bytes_dma;
  170. // Check amount of free space in DMA buffer
  171. uint32_t max = g_scsi_dma.dma_fillto - g_scsi_dma.dma_idx;
  172. if (count > max) count = max;
  173. if (count == 0) return;
  174. uint8_t *src = g_scsi_dma.app_buf + g_scsi_dma.bytes_dma;
  175. uint32_t *dst = g_scsi_dma.dma_buf;
  176. uint32_t pos = g_scsi_dma.dma_idx;
  177. uint32_t end = pos + count;
  178. g_scsi_dma.dma_idx = end;
  179. g_scsi_dma.bytes_dma += count;
  180. while (pos + 4 <= end)
  181. {
  182. uint32_t input = *(uint32_t*)src;
  183. src += 4;
  184. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 0) & 0xFF];
  185. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 8) & 0xFF];
  186. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 16) & 0xFF];
  187. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 24) & 0xFF];
  188. }
  189. while (pos < end)
  190. {
  191. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[*src++];
  192. }
  193. if (end < g_scsi_dma.dma_fillto)
  194. {
  195. // Partial buffer fill, this will get refilled from interrupt if we
  196. // get more data. Set next byte to an invalid parity value so that
  197. // any race conditions will get caught as parity error.
  198. dst[pos & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[0] ^ SCSI_OUT_DBP;
  199. }
  200. }
  201. // Start DMA transfer
  202. static void start_dma()
  203. {
  204. if (g_scsi_dma_use_greenpak)
  205. {
  206. greenpak_start_dma();
  207. return;
  208. }
  209. // Disable channels while configuring
  210. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  211. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  212. TIMER_CTL0(SCSI_TIMER) = 0;
  213. // Set new buffer address and size
  214. // CHA / Data channel is in circular mode and always has DMA_BUF_SIZE buffer size.
  215. // CHB / Update channel limits the number of data.
  216. DMA_CHMADDR(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = (uint32_t)g_scsi_dma.dma_buf;
  217. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = DMA_BUF_SIZE;
  218. uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma;
  219. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule;
  220. g_scsi_dma.scheduled_dma += dma_to_schedule;
  221. // Clear pending DMA events
  222. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  223. TIMER_DMAINTEN(SCSI_TIMER) = TIMER_DMAINTEN_CH1DEN | TIMER_DMAINTEN_CH3DEN;
  224. // Clear and enable interrupt
  225. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHA);
  226. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHB);
  227. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE;
  228. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_FTFIE;
  229. // Enable channels
  230. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_CHEN;
  231. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN;
  232. // Make sure REQ is initially high
  233. TIMER_CNT(SCSI_TIMER) = 16;
  234. TIMER_CHCTL1(SCSI_TIMER) = 0x6050;
  235. TIMER_CHCTL1(SCSI_TIMER) = 0x6074;
  236. // Enable timer
  237. TIMER_CTL0(SCSI_TIMER) |= TIMER_CTL0_CEN;
  238. // Generate first events
  239. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH1G;
  240. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH3G;
  241. }
  242. // Stop DMA transfer
  243. static void stop_dma()
  244. {
  245. greenpak_stop_dma();
  246. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  247. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  248. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~(DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE);
  249. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_FTFIE;
  250. TIMER_CTL0(SCSI_TIMER) &= ~TIMER_CTL0_CEN;
  251. g_scsi_dma_state = SCSIDMA_IDLE;
  252. SCSI_RELEASE_DATA_REQ();
  253. }
  254. static void check_dma_next_buffer()
  255. {
  256. // Check if we are at the end of the application buffer
  257. if (g_scsi_dma.next_app_buf && g_scsi_dma.bytes_dma == g_scsi_dma.bytes_app)
  258. {
  259. // Switch to next buffer
  260. assert(g_scsi_dma.scheduled_dma == g_scsi_dma.bytes_app);
  261. g_scsi_dma.app_buf = g_scsi_dma.next_app_buf;
  262. g_scsi_dma.bytes_app = g_scsi_dma.next_app_bytes;
  263. g_scsi_dma.bytes_dma = 0;
  264. g_scsi_dma.scheduled_dma = 0;
  265. g_scsi_dma.next_app_buf = 0;
  266. g_scsi_dma.next_app_bytes = 0;
  267. refill_dmabuf();
  268. }
  269. }
  270. // Convert new data from application buffer to DMA buffer
  271. extern "C" void SCSI_TIMER_DMACHA_IRQ()
  272. {
  273. // dbgmsg("DMA irq A, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  274. // DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  275. // TIMER_CNT(SCSI_TIMER));
  276. uint32_t intf = DMA_INTF(SCSI_TIMER_DMA);
  277. const uint32_t half_flag = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA);
  278. const uint32_t full_flag = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA);
  279. if (intf & half_flag)
  280. {
  281. if (intf & full_flag)
  282. {
  283. logmsg("ERROR: SCSI DMA overrun: ", intf,
  284. " bytes_app: ", g_scsi_dma.bytes_app,
  285. " bytes_dma: ", g_scsi_dma.bytes_dma,
  286. " dma_idx: ", g_scsi_dma.dma_idx,
  287. " sched_dma: ", g_scsi_dma.scheduled_dma);
  288. stop_dma();
  289. return;
  290. }
  291. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA);
  292. g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2;
  293. }
  294. else if (intf & full_flag)
  295. {
  296. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA);
  297. g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2;
  298. }
  299. // Fill DMA buffer with data from current application buffer
  300. refill_dmabuf();
  301. check_dma_next_buffer();
  302. }
  303. // Check if enough data is available to continue DMA transfer
  304. extern "C" void SCSI_TIMER_DMACHB_IRQ()
  305. {
  306. // dbgmsg("DMA irq B, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  307. // DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  308. // TIMER_CNT(SCSI_TIMER));
  309. uint32_t intf = DMA_INTF(SCSI_TIMER_DMA);
  310. if (intf & DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB))
  311. {
  312. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB);
  313. if (g_scsi_dma.bytes_app > g_scsi_dma.scheduled_dma)
  314. {
  315. if (g_scsi_dma.dma_idx < g_scsi_dma.dma_fillto)
  316. {
  317. // Previous request didn't have a complete buffer worth of data.
  318. // Refill the buffer and ensure that the first byte of the new data gets
  319. // written to outputs.
  320. __disable_irq();
  321. refill_dmabuf();
  322. __enable_irq();
  323. }
  324. // Verify the first byte of the new data has been written to outputs
  325. // It may have been updated after the DMA write occurred.
  326. __disable_irq();
  327. uint32_t first_data_idx = g_scsi_dma.dma_idx - (g_scsi_dma.bytes_dma - g_scsi_dma.scheduled_dma);
  328. uint32_t first_data = g_scsi_dma.dma_buf[first_data_idx & DMA_BUF_MASK];
  329. GPIO_BOP(SCSI_OUT_PORT) = first_data;
  330. __enable_irq();
  331. // Update the total number of bytes available for DMA
  332. uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma;
  333. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  334. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule;
  335. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN;
  336. g_scsi_dma.scheduled_dma += dma_to_schedule;
  337. }
  338. else
  339. {
  340. // Wait for final ACK to go low, shouldn't take long.
  341. uint32_t start = millis();
  342. while (TIMER_CNT(SCSI_TIMER) < 1)
  343. {
  344. if ((uint32_t)(millis() - start) > 500)
  345. {
  346. logmsg("SCSI_TIMER_DMACHB_IRQ: timeout waiting for final ACK");
  347. break;
  348. }
  349. }
  350. // No more data available
  351. stop_dma();
  352. }
  353. }
  354. }
  355. void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag)
  356. {
  357. __disable_irq();
  358. if (g_scsi_dma_state == SCSIDMA_WRITE)
  359. {
  360. if (!g_scsi_dma.next_app_buf && data == g_scsi_dma.app_buf + g_scsi_dma.bytes_app)
  361. {
  362. // Combine with currently running request
  363. g_scsi_dma.bytes_app += count;
  364. count = 0;
  365. }
  366. else if (data == g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  367. {
  368. // Combine with queued request
  369. g_scsi_dma.next_app_bytes += count;
  370. count = 0;
  371. }
  372. else if (!g_scsi_dma.next_app_buf)
  373. {
  374. // Add as queued request
  375. g_scsi_dma.next_app_buf = (uint8_t*)data;
  376. g_scsi_dma.next_app_bytes = count;
  377. count = 0;
  378. }
  379. }
  380. __enable_irq();
  381. // Check if the request was combined
  382. if (count == 0) return;
  383. if (g_scsi_dma_state != SCSIDMA_IDLE)
  384. {
  385. // Wait for previous request to finish
  386. scsi_accel_dma_finishWrite(resetFlag);
  387. if (*resetFlag)
  388. {
  389. return;
  390. }
  391. }
  392. // dbgmsg("Starting DMA write of ", (int)count, " bytes");
  393. scsi_dma_gpio_config(true);
  394. g_scsi_dma_state = SCSIDMA_WRITE;
  395. g_scsi_dma.app_buf = (uint8_t*)data;
  396. g_scsi_dma.dma_idx = 0;
  397. g_scsi_dma.dma_fillto = DMA_BUF_SIZE;
  398. g_scsi_dma.bytes_app = count;
  399. g_scsi_dma.bytes_dma = 0;
  400. g_scsi_dma.scheduled_dma = 0;
  401. g_scsi_dma.next_app_buf = NULL;
  402. g_scsi_dma.next_app_bytes = 0;
  403. g_scsi_dma.greenpak_state = GREENPAK_IO1_LOW;
  404. refill_dmabuf();
  405. start_dma();
  406. }
  407. bool scsi_accel_dma_isWriteFinished(const uint8_t* data)
  408. {
  409. // Check if everything has completed
  410. if (g_scsi_dma_state == SCSIDMA_IDLE)
  411. {
  412. return true;
  413. }
  414. if (!data)
  415. return false;
  416. // Check if this data item is still in queue.
  417. __disable_irq();
  418. bool finished = true;
  419. if (data >= g_scsi_dma.app_buf + g_scsi_dma.bytes_dma &&
  420. data < g_scsi_dma.app_buf + g_scsi_dma.bytes_app)
  421. {
  422. finished = false; // In current transfer
  423. }
  424. else if (data >= g_scsi_dma.next_app_buf &&
  425. data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  426. {
  427. finished = false; // In queued transfer
  428. }
  429. __enable_irq();
  430. return finished;
  431. }
  432. void scsi_accel_dma_stopWrite()
  433. {
  434. stop_dma();
  435. scsi_dma_gpio_config(false);
  436. }
  437. void scsi_accel_dma_finishWrite(volatile int *resetFlag)
  438. {
  439. uint32_t start = millis();
  440. while (g_scsi_dma_state != SCSIDMA_IDLE && !*resetFlag)
  441. {
  442. if ((uint32_t)(millis() - start) > 5000)
  443. {
  444. logmsg("scsi_accel_dma_finishWrite() timeout, DMA counts ",
  445. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  446. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  447. TIMER_CNT(SCSI_TIMER));
  448. *resetFlag = 1;
  449. break;
  450. }
  451. }
  452. scsi_accel_dma_stopWrite();
  453. }
  454. /************************************************/
  455. /* Functions using external GreenPAK logic chip */
  456. /************************************************/
  457. void scsi_accel_greenpak_dma_init()
  458. {
  459. g_scsi_dma_state = SCSIDMA_IDLE;
  460. g_scsi_dma_use_greenpak = true;
  461. rcu_periph_clock_enable(SCSI_TIMER_RCU);
  462. rcu_periph_clock_enable(SCSI_TIMER_DMA_RCU);
  463. // DMA Channel A: data copy
  464. // GPIO DMA copies data from memory buffer to GPIO BOP register.
  465. // The memory buffer is filled by interrupt routine.
  466. dma_parameter_struct gpio_dma_config =
  467. {
  468. .periph_addr = (uint32_t)&GPIO_BOP(SCSI_OUT_PORT),
  469. .periph_width = DMA_PERIPHERAL_WIDTH_32BIT,
  470. .memory_addr = (uint32_t)g_scsi_dma.dma_buf,
  471. .memory_width = DMA_MEMORY_WIDTH_32BIT,
  472. .number = DMA_BUF_SIZE,
  473. .priority = DMA_PRIORITY_ULTRA_HIGH,
  474. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  475. .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
  476. .direction = DMA_MEMORY_TO_PERIPHERAL
  477. };
  478. dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA, &gpio_dma_config);
  479. dma_circulation_enable(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA);
  480. NVIC_SetPriority(SCSI_TIMER_DMACHA_IRQn, 2);
  481. NVIC_EnableIRQ(SCSI_TIMER_DMACHA_IRQn);
  482. NVIC_DisableIRQ(SCSI_TIMER_DMACHB_IRQn);
  483. // EXTI channel is used to trigger when we reach end of the transfer.
  484. // Because the main DMA is circular and transfer size may not be even
  485. // multiple of it, we cannot trigger the end at the DMA interrupt.
  486. gpio_exti_source_select(GREENPAK_PLD_IO2_EXTI_SOURCE_PORT, GREENPAK_PLD_IO2_EXTI_SOURCE_PIN);
  487. exti_init(GREENPAK_PLD_IO2_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
  488. exti_interrupt_flag_clear(GREENPAK_PLD_IO2_EXTI);
  489. exti_interrupt_disable(GREENPAK_PLD_IO2_EXTI);
  490. NVIC_SetPriority(GREENPAK_IRQn, 1);
  491. NVIC_EnableIRQ(GREENPAK_IRQn);
  492. // Timer is used to trigger DMA requests
  493. // OUT_REQ is driven by timer output.
  494. // 1. On timer update event, REQ is set low.
  495. // 2. When ACK goes low, timer counts and OUT_REQ is set high.
  496. // Simultaneously a DMA request is triggered to write next data to GPIO.
  497. // 3. When ACK goes high, a DMA request is triggered to cause timer update event.
  498. // The DMA request priority is set so that 2. always completes before it.
  499. TIMER_CTL0(SCSI_TIMER) = 0;
  500. TIMER_SMCFG(SCSI_TIMER) = TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_CI0F_ED;
  501. TIMER_CAR(SCSI_TIMER) = 1;
  502. TIMER_PSC(SCSI_TIMER) = 0;
  503. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  504. TIMER_CHCTL0(SCSI_TIMER) = 0x6001; // CH0 as input, CH1 as DMA trigger
  505. TIMER_CHCTL1(SCSI_TIMER) = 0;
  506. TIMER_CHCTL2(SCSI_TIMER) = 0;
  507. TIMER_CCHP(SCSI_TIMER) = 0;
  508. TIMER_CH1CV(SCSI_TIMER) = 1; // Copy data when ACK goes low
  509. gpio_init(SCSI_TIMER_IN_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_TIMER_IN_PIN);
  510. }
  511. extern const uint32_t g_scsi_out_byte_to_bop_pld1hi[256];
  512. extern const uint32_t g_scsi_out_byte_to_bop_pld1lo[256];
  513. static void greenpak_refill_dmabuf()
  514. {
  515. if (g_scsi_dma.greenpak_state == GREENPAK_STOP)
  516. {
  517. // Wait for previous DMA block to end first
  518. return;
  519. }
  520. // Check how many bytes we have available from the application
  521. uint32_t count = g_scsi_dma.bytes_app - g_scsi_dma.bytes_dma;
  522. // Check amount of free space in DMA buffer
  523. uint32_t max = g_scsi_dma.dma_fillto - g_scsi_dma.dma_idx;
  524. if (count > max) count = max;
  525. uint8_t *src = g_scsi_dma.app_buf + g_scsi_dma.bytes_dma;
  526. uint32_t *dst = g_scsi_dma.dma_buf;
  527. uint32_t pos = g_scsi_dma.dma_idx;
  528. uint32_t end = pos + count;
  529. g_scsi_dma.dma_idx = end;
  530. g_scsi_dma.bytes_dma += count;
  531. g_scsi_dma.scheduled_dma = g_scsi_dma.bytes_dma;
  532. if (pos < end && g_scsi_dma.greenpak_state == GREENPAK_IO1_HIGH)
  533. {
  534. // Fix alignment so that main loop begins with PLD1HI
  535. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1lo[*src++];
  536. g_scsi_dma.greenpak_state = GREENPAK_IO1_LOW;
  537. }
  538. while (pos + 4 <= end)
  539. {
  540. uint32_t input = *(uint32_t*)src;
  541. src += 4;
  542. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1hi[(input >> 0) & 0xFF];
  543. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1lo[(input >> 8) & 0xFF];
  544. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1hi[(input >> 16) & 0xFF];
  545. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1lo[(input >> 24) & 0xFF];
  546. }
  547. while (pos < end)
  548. {
  549. if (g_scsi_dma.greenpak_state == GREENPAK_IO1_HIGH)
  550. {
  551. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1lo[*src++];
  552. g_scsi_dma.greenpak_state = GREENPAK_IO1_LOW;
  553. }
  554. else
  555. {
  556. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1hi[*src++];
  557. g_scsi_dma.greenpak_state = GREENPAK_IO1_HIGH;
  558. }
  559. }
  560. uint32_t remain = g_scsi_dma.dma_fillto - g_scsi_dma.dma_idx;
  561. if (!g_scsi_dma.next_app_buf && remain > 0)
  562. {
  563. // Mark the end of transfer by turning PD2 off
  564. dst[(pos++) & DMA_BUF_MASK] = (GREENPAK_PLD_IO2 << 16) | (GREENPAK_PLD_IO1 << 16);
  565. g_scsi_dma.dma_idx = pos;
  566. g_scsi_dma.greenpak_state = GREENPAK_STOP;
  567. }
  568. }
  569. extern "C" void GREENPAK_IRQ()
  570. {
  571. if (EXTI_PD & GREENPAK_PLD_IO2_EXTI)
  572. {
  573. EXTI_PD = GREENPAK_PLD_IO2_EXTI;
  574. if (g_scsi_dma.bytes_app > g_scsi_dma.bytes_dma || g_scsi_dma.next_app_buf)
  575. {
  576. assert(g_scsi_dma.greenpak_state == GREENPAK_STOP);
  577. g_scsi_dma.greenpak_state = GREENPAK_IO1_LOW;
  578. // More data is available
  579. check_dma_next_buffer();
  580. refill_dmabuf();
  581. // Continue transferring
  582. GPIO_BOP(SCSI_OUT_PORT) = GREENPAK_PLD_IO2;
  583. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH1G;
  584. }
  585. else
  586. {
  587. stop_dma();
  588. }
  589. }
  590. }
  591. static void greenpak_start_dma()
  592. {
  593. // Disable channels while configuring
  594. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  595. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  596. TIMER_CTL0(SCSI_TIMER) = 0;
  597. // Set buffer address and size
  598. DMA_CHMADDR(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = (uint32_t)g_scsi_dma.dma_buf;
  599. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = DMA_BUF_SIZE;
  600. // Clear pending DMA events
  601. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  602. TIMER_DMAINTEN(SCSI_TIMER) = TIMER_DMAINTEN_CH1DEN | TIMER_DMAINTEN_CH3DEN;
  603. // Clear and enable interrupt
  604. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHA);
  605. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE;
  606. exti_interrupt_flag_clear(GREENPAK_PLD_IO2_EXTI);
  607. exti_interrupt_enable(GREENPAK_PLD_IO2_EXTI);
  608. // Enable channels
  609. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_CHEN;
  610. // Enable timer
  611. TIMER_CNT(SCSI_TIMER) = 0;
  612. TIMER_CTL0(SCSI_TIMER) |= TIMER_CTL0_CEN;
  613. // Generate first event
  614. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH1G;
  615. }
  616. static void greenpak_stop_dma()
  617. {
  618. exti_interrupt_disable(GREENPAK_PLD_IO2_EXTI);
  619. }
  620. #endif