scsi_accel_sync.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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. /* Synchronous mode SCSI implementation.
  22. *
  23. * In synchronous mode, the handshake mechanism is not used. Instead
  24. * either end of the communication will just send a bunch of bytes
  25. * and only afterwards checks that the number of acknowledgement
  26. * pulses matches.
  27. *
  28. * The receiving end should latch in the data at the falling edge of
  29. * the request pulse (on either REQ or ACK pin). We use the GD32 EXMC
  30. * peripheral to implement this latching with the NWAIT pin when
  31. * reading data from the host. NOE is used to generate the REQ pulses.
  32. *
  33. * Writing data to the host is simpler, as we can just write it out
  34. * from the GPIO port at our own pace. A timer is used for generating
  35. * the output pulses on REQ pin.
  36. */
  37. #include "scsi_accel_sync.h"
  38. #include <ZuluSCSI_log.h>
  39. #include <gd32f20x_exmc.h>
  40. #include <scsi.h>
  41. #ifndef SCSI_SYNC_MODE_AVAILABLE
  42. void scsi_accel_sync_init() {}
  43. void scsi_accel_sync_recv(uint8_t *data, uint32_t count, int* parityError, volatile int *resetFlag) {}
  44. void scsi_accel_sync_send(const uint8_t* data, uint32_t count, volatile int *resetFlag) {}
  45. #else
  46. /********************************/
  47. /* Transfer from host to device */
  48. /********************************/
  49. #define SYNC_DMA_BUFSIZE 512
  50. static uint32_t g_sync_dma_buf[SYNC_DMA_BUFSIZE];
  51. void scsi_accel_sync_init()
  52. {
  53. rcu_periph_clock_enable(RCU_EXMC);
  54. rcu_periph_clock_enable(SCSI_EXMC_DMA_RCU);
  55. rcu_periph_clock_enable(SCSI_SYNC_TIMER_RCU);
  56. exmc_norsram_timing_parameter_struct timing_param = {
  57. .asyn_access_mode = EXMC_ACCESS_MODE_A,
  58. .syn_data_latency = EXMC_DATALAT_2_CLK,
  59. .syn_clk_division = EXMC_SYN_CLOCK_RATIO_2_CLK,
  60. .bus_latency = 1,
  61. .asyn_data_setuptime = 2,
  62. .asyn_address_holdtime = 2,
  63. .asyn_address_setuptime = 16
  64. };
  65. exmc_norsram_parameter_struct sram_param = {
  66. .norsram_region = EXMC_BANK0_NORSRAM_REGION0,
  67. .write_mode = EXMC_ASYN_WRITE,
  68. .extended_mode = DISABLE,
  69. .asyn_wait = ENABLE,
  70. .nwait_signal = ENABLE,
  71. .memory_write = DISABLE,
  72. .nwait_config = EXMC_NWAIT_CONFIG_DURING,
  73. .wrap_burst_mode = DISABLE,
  74. .nwait_polarity = EXMC_NWAIT_POLARITY_HIGH,
  75. .burst_mode = DISABLE,
  76. .databus_width = EXMC_NOR_DATABUS_WIDTH_16B,
  77. .memory_type = EXMC_MEMORY_TYPE_SRAM,
  78. .address_data_mux = DISABLE,
  79. .read_write_timing = &timing_param
  80. };
  81. EXMC_SNCTL(EXMC_BANK0_NORSRAM_REGION0) &= ~EXMC_SNCTL_NRBKEN;
  82. exmc_norsram_init(&sram_param);
  83. // DMA used to transfer data from EXMC to RAM
  84. // DMA is used so that if data transfer fails, we can at least abort by resetting CPU.
  85. // Accessing EXMC from the CPU directly hangs it totally if ACK pulses are not received.
  86. dma_parameter_struct exmc_dma_config =
  87. {
  88. .periph_addr = EXMC_NOR_PSRAM,
  89. .periph_width = DMA_PERIPHERAL_WIDTH_16BIT,
  90. .memory_addr = (uint32_t)g_sync_dma_buf,
  91. .memory_width = DMA_MEMORY_WIDTH_16BIT,
  92. .number = 0, // Filled before transfer
  93. .priority = DMA_PRIORITY_MEDIUM,
  94. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  95. .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
  96. .direction = DMA_PERIPHERAL_TO_MEMORY
  97. };
  98. dma_init(SCSI_EXMC_DMA, SCSI_EXMC_DMACH, &exmc_dma_config);
  99. dma_memory_to_memory_enable(SCSI_EXMC_DMA, SCSI_EXMC_DMACH);
  100. gpio_init(SCSI_IN_ACK_EXMC_NWAIT_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_IN_ACK_EXMC_NWAIT_PIN);
  101. gpio_init(SCSI_TIMER_IN_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_TIMER_IN_PIN);
  102. // TIMER1 is used to count ACK pulses
  103. TIMER_CTL0(SCSI_SYNC_TIMER) = 0;
  104. TIMER_SMCFG(SCSI_SYNC_TIMER) = TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_CI0FE0;
  105. TIMER_CAR(SCSI_SYNC_TIMER) = 65535;
  106. TIMER_PSC(SCSI_SYNC_TIMER) = 0;
  107. TIMER_CHCTL0(SCSI_SYNC_TIMER) = 0x0001; // CH0 as input
  108. }
  109. void scsi_accel_sync_recv(uint8_t *data, uint32_t count, int* parityError, volatile int *resetFlag)
  110. {
  111. // Enable EXMC to drive REQ from EXMC_NOE pin
  112. EXMC_SNCTL(EXMC_BANK0_NORSRAM_REGION0) |= EXMC_SNCTL_NRBKEN;
  113. uint32_t oldmode = GPIO_CTL0(SCSI_OUT_REQ_EXMC_NOE_PORT);
  114. uint32_t newmode = oldmode & ~(0xF << (SCSI_OUT_REQ_EXMC_NOE_IDX * 4));
  115. newmode |= 0xB << (SCSI_OUT_REQ_EXMC_NOE_IDX * 4);
  116. GPIO_CTL0(SCSI_OUT_REQ_EXMC_NOE_PORT) = newmode;
  117. while (count > 0)
  118. {
  119. uint32_t blocksize = (count > SYNC_DMA_BUFSIZE * 2) ? (SYNC_DMA_BUFSIZE * 2) : count;
  120. count -= blocksize;
  121. DMA_CHCNT(SCSI_EXMC_DMA, SCSI_EXMC_DMACH) = blocksize;
  122. DMA_CHCTL(SCSI_EXMC_DMA, SCSI_EXMC_DMACH) |= DMA_CHXCTL_CHEN;
  123. uint16_t *src = (uint16_t*)g_sync_dma_buf;
  124. uint8_t *dst = data;
  125. uint8_t *end = data + blocksize;
  126. uint32_t start = millis();
  127. while (dst < end)
  128. {
  129. uint32_t remain = DMA_CHCNT(SCSI_EXMC_DMA, SCSI_EXMC_DMACH);
  130. while (dst < end - remain)
  131. {
  132. *dst++ = ~(*src++) >> SCSI_EXMC_DATA_SHIFT;
  133. }
  134. if ((uint32_t)(millis() - start) > 500 || *resetFlag)
  135. {
  136. // We are in a pinch here: without ACK pulses coming, the EXMC and DMA peripherals
  137. // are locked up. The only way out is a whole system reset.
  138. logmsg("SCSI Synchronous read timeout: resetting system");
  139. NVIC_SystemReset();
  140. }
  141. }
  142. DMA_CHCTL(SCSI_EXMC_DMA, SCSI_EXMC_DMACH) &= ~DMA_CHXCTL_CHEN;
  143. data = end;
  144. }
  145. GPIO_CTL0(SCSI_OUT_REQ_EXMC_NOE_PORT) = oldmode;
  146. EXMC_SNCTL(EXMC_BANK0_NORSRAM_REGION0) &= ~EXMC_SNCTL_NRBKEN;
  147. }
  148. /********************************/
  149. /* Transfer from device to host */
  150. /********************************/
  151. // Simple delay, about 20 ns.
  152. // This is less likely to get optimized away by CPU pipeline than nop
  153. #define ASM_DELAY() \
  154. " ldr %[tmp2], [%[reset_flag]] \n"
  155. // Take 8 bits from d and format them for writing
  156. // d is name of data operand, b is bit offset
  157. #define ASM_LOAD_DATA(b) \
  158. " ubfx %[tmp1], %[data], #" b ", #8 \n" \
  159. " ldr %[tmp1], [%[byte_lookup], %[tmp1], lsl #2] \n"
  160. // Write data to SCSI port and set REQ high
  161. #define ASM_SEND_DATA() \
  162. " str %[tmp1], [%[out_port_bop]] \n"
  163. // Set REQ low
  164. #define ASM_SET_REQ_LOW() \
  165. " mov %[tmp2], %[bop_req_low] \n" \
  166. " str %[tmp2], [%[out_port_bop]] \n"
  167. // Wait for ACK_TIMER - n to be less than num_bytes
  168. #define ASM_WAIT_ACK_TIMER(n) \
  169. "wait_acks_" n "_%=: \n" \
  170. " ldr %[tmp2], [%[ack_timer]] \n" \
  171. " sub %[tmp2], # " n " \n" \
  172. " cmp %[tmp2], %[num_bytes] \n" \
  173. " ble got_acks_" n "_%= \n" \
  174. " ldr %[tmp2], [%[reset_flag]] \n" \
  175. " cmp %[tmp2], #0 \n" \
  176. " bne all_done_%= \n" \
  177. " b wait_acks_" n "_%= \n" \
  178. "got_acks_" n "_%=: \n"
  179. // Send 4 bytes
  180. #define ASM_SEND_4BYTES() \
  181. ASM_LOAD_DATA("0") \
  182. ASM_SEND_DATA() \
  183. ASM_DELAY1() \
  184. ASM_SET_REQ_LOW() \
  185. ASM_DELAY2() \
  186. ASM_LOAD_DATA("8") \
  187. ASM_SEND_DATA() \
  188. ASM_DELAY1() \
  189. ASM_SET_REQ_LOW() \
  190. ASM_DELAY2() \
  191. ASM_LOAD_DATA("16") \
  192. ASM_SEND_DATA() \
  193. ASM_DELAY1() \
  194. ASM_SET_REQ_LOW() \
  195. ASM_DELAY2() \
  196. ASM_LOAD_DATA("24") \
  197. ASM_SEND_DATA() \
  198. ASM_DELAY1() \
  199. ASM_SET_REQ_LOW()
  200. // Send 1 byte, wait for ACK_TIMER to be less than num_bytes + n and send 3 bytes more
  201. // This interleaving minimizes the delay caused by WAIT_ACK_TIMER.
  202. #define ASM_SEND_4BYTES_WAIT(n) \
  203. ASM_LOAD_DATA("0") \
  204. ASM_SEND_DATA() \
  205. ASM_DELAY2() \
  206. ASM_LOAD_DATA("8") \
  207. ASM_SET_REQ_LOW() \
  208. ASM_DELAY2() \
  209. " ldr %[tmp2], [%[ack_timer]] \n" \
  210. " sub %[tmp2], # " n " \n" \
  211. ASM_SEND_DATA() \
  212. " cmp %[tmp2], %[num_bytes] \n" \
  213. " ble got_acks_" n "_%= \n" \
  214. ASM_WAIT_ACK_TIMER(n) \
  215. ASM_DELAY2() \
  216. ASM_SET_REQ_LOW() \
  217. ASM_DELAY2() \
  218. ASM_LOAD_DATA("16") \
  219. ASM_SEND_DATA() \
  220. ASM_DELAY1() \
  221. ASM_SET_REQ_LOW() \
  222. ASM_DELAY2() \
  223. ASM_LOAD_DATA("24") \
  224. ASM_SEND_DATA() \
  225. ASM_DELAY1() \
  226. ASM_SET_REQ_LOW() \
  227. // Specialized routine for settings:
  228. // <=100 ns period, >=15 outstanding REQs
  229. static void sync_send_100ns_15off(const uint8_t *buf, uint32_t num_bytes, volatile int *resetFlag)
  230. {
  231. volatile uint32_t *out_port_bop = (volatile uint32_t*)&GPIO_BOP(SCSI_OUT_PORT);
  232. volatile uint32_t *ack_timer = &TIMER_CNT(SCSI_SYNC_TIMER);
  233. const uint32_t *byte_lookup = g_scsi_out_byte_to_bop;
  234. register uint32_t tmp1 = 0;
  235. register uint32_t tmp2 = 0;
  236. register uint32_t data = 0;
  237. // Delay 1 is typically longest and delay 2 shortest.
  238. // Tuning these is just trial and error.
  239. #define ASM_DELAY1() " nop\n nop\n nop\n"
  240. #define ASM_DELAY2() " nop\n nop\n"
  241. asm volatile (
  242. "main_loop_%=: \n"
  243. " subs %[num_bytes], %[num_bytes], #16 \n"
  244. " bmi last_bytes_%= \n"
  245. /* At each point make sure there is at most 15 bytes in flight */
  246. " ldr %[data], [%[buf]], #4 \n"
  247. ASM_SEND_4BYTES_WAIT("22")
  248. " ldr %[data], [%[buf]], #4 \n"
  249. ASM_SEND_4BYTES()
  250. " ldr %[data], [%[buf]], #4 \n"
  251. ASM_SEND_4BYTES_WAIT("14")
  252. " ldr %[data], [%[buf]], #4 \n"
  253. ASM_SEND_4BYTES()
  254. " cbz %[num_bytes], all_done_%= \n"
  255. " b main_loop_%= \n"
  256. "last_bytes_%=: \n"
  257. " add %[num_bytes], %[num_bytes], #16 \n"
  258. "last_bytes_loop_%=: \n"
  259. " ldrb %[data], [%[buf]], #1 \n"
  260. ASM_LOAD_DATA("0")
  261. ASM_WAIT_ACK_TIMER("15")
  262. ASM_SEND_DATA()
  263. ASM_DELAY1()
  264. ASM_SET_REQ_LOW()
  265. " subs %[num_bytes], %[num_bytes], #1 \n"
  266. " bne last_bytes_loop_%= \n"
  267. "all_done_%=: \n"
  268. ASM_DELAY()
  269. : /* Output */ [tmp1] "+l" (tmp1), [tmp2] "+l" (tmp2), [data] "+r" (data),
  270. [buf] "+r" (buf), [num_bytes] "+r" (num_bytes)
  271. : /* Input */ [ack_timer] "r" (ack_timer),
  272. [bop_req_low] "I" (SCSI_OUT_REQ << 16),
  273. [out_port_bop] "r"(out_port_bop),
  274. [byte_lookup] "r" (byte_lookup),
  275. [reset_flag] "r" (resetFlag)
  276. : /* Clobber */);
  277. #undef ASM_DELAY1
  278. #undef ASM_DELAY2
  279. SCSI_RELEASE_DATA_REQ();
  280. }
  281. // Specialized routine for settings:
  282. // <=200 ns period, >=15 outstanding REQs
  283. static void sync_send_200ns_15off(const uint8_t *buf, uint32_t num_bytes, volatile int *resetFlag)
  284. {
  285. volatile uint32_t *out_port_bop = (volatile uint32_t*)&GPIO_BOP(SCSI_OUT_PORT);
  286. volatile uint32_t *ack_timer = &TIMER_CNT(SCSI_SYNC_TIMER);
  287. const uint32_t *byte_lookup = g_scsi_out_byte_to_bop;
  288. register uint32_t tmp1 = 0;
  289. register uint32_t tmp2 = 0;
  290. register uint32_t data = 0;
  291. #define ASM_DELAY1() ASM_DELAY() ASM_DELAY() ASM_DELAY() ASM_DELAY()
  292. #define ASM_DELAY2() ASM_DELAY() ASM_DELAY() ASM_DELAY()
  293. asm volatile (
  294. "main_loop_%=: \n"
  295. " subs %[num_bytes], %[num_bytes], #16 \n"
  296. " bmi last_bytes_%= \n"
  297. /* At each point make sure there is at most 15 bytes in flight */
  298. " ldr %[data], [%[buf]], #4 \n"
  299. ASM_SEND_4BYTES_WAIT("22")
  300. ASM_DELAY2()
  301. " ldr %[data], [%[buf]], #4 \n"
  302. ASM_SEND_4BYTES()
  303. ASM_DELAY2()
  304. " ldr %[data], [%[buf]], #4 \n"
  305. ASM_SEND_4BYTES_WAIT("14")
  306. ASM_DELAY2()
  307. " ldr %[data], [%[buf]], #4 \n"
  308. ASM_SEND_4BYTES()
  309. " cbz %[num_bytes], all_done_%= \n"
  310. " b main_loop_%= \n"
  311. "last_bytes_%=: \n"
  312. " add %[num_bytes], %[num_bytes], #16 \n"
  313. "last_bytes_loop_%=: \n"
  314. " ldrb %[data], [%[buf]], #1 \n"
  315. ASM_LOAD_DATA("0")
  316. ASM_WAIT_ACK_TIMER("15")
  317. ASM_SEND_DATA()
  318. ASM_DELAY1()
  319. ASM_SET_REQ_LOW()
  320. ASM_DELAY2()
  321. " subs %[num_bytes], %[num_bytes], #1 \n"
  322. " bne last_bytes_loop_%= \n"
  323. "all_done_%=: \n"
  324. ASM_DELAY1()
  325. : /* Output */ [tmp1] "+l" (tmp1), [tmp2] "+l" (tmp2), [data] "+r" (data),
  326. [buf] "+r" (buf), [num_bytes] "+r" (num_bytes)
  327. : /* Input */ [ack_timer] "r" (ack_timer),
  328. [bop_req_low] "I" (SCSI_OUT_REQ << 16),
  329. [out_port_bop] "r"(out_port_bop),
  330. [byte_lookup] "r" (byte_lookup),
  331. [reset_flag] "r" (resetFlag)
  332. : /* Clobber */);
  333. #undef ASM_DELAY1
  334. #undef ASM_DELAY2
  335. SCSI_RELEASE_DATA_REQ();
  336. }
  337. // Specialized routine for settings:
  338. // <=260 ns period, >=7 outstanding REQs
  339. static void sync_send_260ns_7off(const uint8_t *buf, uint32_t num_bytes, volatile int *resetFlag)
  340. {
  341. volatile uint32_t *out_port_bop = (volatile uint32_t*)&GPIO_BOP(SCSI_OUT_PORT);
  342. volatile uint32_t *ack_timer = &TIMER_CNT(SCSI_SYNC_TIMER);
  343. const uint32_t *byte_lookup = g_scsi_out_byte_to_bop;
  344. register uint32_t tmp1 = 0;
  345. register uint32_t tmp2 = 0;
  346. register uint32_t data = 0;
  347. #define ASM_DELAY1() ASM_DELAY() ASM_DELAY() ASM_DELAY() ASM_DELAY() \
  348. ASM_DELAY() ASM_DELAY()
  349. #define ASM_DELAY2() ASM_DELAY() ASM_DELAY() ASM_DELAY() ASM_DELAY() \
  350. ASM_DELAY() ASM_DELAY() ASM_DELAY() ASM_DELAY()
  351. asm volatile (
  352. "main_loop_%=: \n"
  353. " subs %[num_bytes], %[num_bytes], #4 \n"
  354. " bmi last_bytes_%= \n"
  355. /* At each point make sure there is at most 3 bytes in flight */
  356. " ldr %[data], [%[buf]], #4 \n"
  357. ASM_SEND_4BYTES_WAIT("7")
  358. " cbz %[num_bytes], all_done_%= \n"
  359. " b main_loop_%= \n"
  360. "last_bytes_%=: \n"
  361. " add %[num_bytes], %[num_bytes], #4 \n"
  362. "last_bytes_loop_%=: \n"
  363. " ldrb %[data], [%[buf]], #1 \n"
  364. ASM_LOAD_DATA("0")
  365. ASM_WAIT_ACK_TIMER("5")
  366. ASM_SEND_DATA()
  367. ASM_DELAY1()
  368. ASM_SET_REQ_LOW()
  369. ASM_DELAY2()
  370. " subs %[num_bytes], %[num_bytes], #1 \n"
  371. " bne last_bytes_loop_%= \n"
  372. "all_done_%=: \n"
  373. ASM_DELAY1()
  374. : /* Output */ [tmp1] "+l" (tmp1), [tmp2] "+l" (tmp2), [data] "+r" (data),
  375. [buf] "+r" (buf), [num_bytes] "+r" (num_bytes)
  376. : /* Input */ [ack_timer] "r" (ack_timer),
  377. [bop_req_low] "I" (SCSI_OUT_REQ << 16),
  378. [out_port_bop] "r"(out_port_bop),
  379. [byte_lookup] "r" (byte_lookup),
  380. [reset_flag] "r" (resetFlag)
  381. : /* Clobber */);
  382. #undef ASM_DELAY1
  383. #undef ASM_DELAY2
  384. SCSI_RELEASE_DATA_REQ();
  385. }
  386. void scsi_accel_sync_send(const uint8_t* data, uint32_t count, volatile int *resetFlag)
  387. {
  388. // Timer counts down from the initial number of bytes.
  389. TIMER_CNT(SCSI_SYNC_TIMER) = count;
  390. TIMER_CTL0(SCSI_SYNC_TIMER) = TIMER_CTL0_CEN | TIMER_CTL0_DIR;
  391. int syncOffset = scsiDev.target->syncOffset;
  392. int syncPeriod = scsiDev.target->syncPeriod;
  393. if (syncOffset >= 15 && syncPeriod <= 25)
  394. {
  395. sync_send_100ns_15off(data, count, resetFlag);
  396. }
  397. else if (syncOffset >= 15 && syncPeriod <= 50)
  398. {
  399. sync_send_200ns_15off(data, count, resetFlag);
  400. }
  401. else if (syncOffset >= 7 && syncPeriod <= 65)
  402. {
  403. sync_send_260ns_7off(data, count, resetFlag);
  404. }
  405. else
  406. {
  407. dbgmsg("No optimized routine for syncOffset=", syncOffset, " syncPeriod=", syncPeriod, ", using fallback");
  408. while (count-- > 0)
  409. {
  410. while (TIMER_CNT(SCSI_SYNC_TIMER) > count + syncOffset && !*resetFlag);
  411. SCSI_OUT_DATA(*data++);
  412. delay_ns(syncPeriod * 2);
  413. SCSI_OUT(REQ, 0);
  414. delay_ns(syncPeriod * 2);
  415. }
  416. delay_ns(syncPeriod * 2);
  417. SCSI_RELEASE_DATA_REQ();
  418. }
  419. while (TIMER_CNT(SCSI_SYNC_TIMER) > 0 && !*resetFlag);
  420. if (*resetFlag)
  421. {
  422. dbgmsg("Bus reset during sync transfer, total ", (int)count,
  423. " bytes, remaining ACK count ", (int)TIMER_CNT(SCSI_SYNC_TIMER));
  424. }
  425. TIMER_CTL0(SCSI_SYNC_TIMER) = 0;
  426. }
  427. #endif