scsi_accel_dma.cpp 29 KB

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