scsi_accel_dma.cpp 24 KB

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