scsi_accel_sync.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /* Synchronous mode SCSI implementation.
  2. *
  3. * In synchronous mode, the handshake mechanism is not used. Instead
  4. * either end of the communication will just send a bunch of bytes
  5. * and only afterwards checks that the number of acknowledgement
  6. * pulses matches.
  7. *
  8. * The receiving end should latch in the data at the falling edge of
  9. * the request pulse (on either REQ or ACK pin). We use the GD32 EXMC
  10. * peripheral to implement this latching with the NWAIT pin when
  11. * reading data from the host. NOE is used to generate the REQ pulses.
  12. *
  13. * Writing data to the host is simpler, as we can just write it out
  14. * from the GPIO port at our own pace. A timer is used for generating
  15. * the output pulses on REQ pin.
  16. */
  17. #include "scsi_accel_sync.h"
  18. #include <AzulSCSI_log.h>
  19. #include <gd32f20x_exmc.h>
  20. #ifndef SCSI_SYNC_MODE_AVAILABLE
  21. void scsi_accel_sync_init() {}
  22. void scsi_accel_sync_read(uint8_t *data, uint32_t count, int* parityError, volatile int *resetFlag) {}
  23. void scsi_accel_sync_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag) {}
  24. void scsi_accel_sync_stopWrite() {}
  25. void scsi_accel_sync_finishWrite(volatile int *resetFlag) {}
  26. bool scsi_accel_sync_isWriteFinished(const uint8_t* data) { return true; }
  27. #else
  28. #define SYNC_DMA_BUFSIZE 512
  29. static uint32_t g_sync_dma_buf[SYNC_DMA_BUFSIZE];
  30. void scsi_accel_sync_init()
  31. {
  32. rcu_periph_clock_enable(RCU_EXMC);
  33. rcu_periph_clock_enable(SCSI_TIMER_RCU);
  34. rcu_periph_clock_enable(SCSI_EXMC_DMA_RCU);
  35. exmc_norsram_timing_parameter_struct timing_param = {
  36. .asyn_access_mode = EXMC_ACCESS_MODE_A,
  37. .syn_data_latency = EXMC_DATALAT_2_CLK,
  38. .syn_clk_division = EXMC_SYN_CLOCK_RATIO_2_CLK,
  39. .bus_latency = 1,
  40. .asyn_data_setuptime = 2,
  41. .asyn_address_holdtime = 2,
  42. .asyn_address_setuptime = 16
  43. };
  44. exmc_norsram_parameter_struct sram_param = {
  45. .norsram_region = EXMC_BANK0_NORSRAM_REGION0,
  46. .write_mode = EXMC_ASYN_WRITE,
  47. .extended_mode = DISABLE,
  48. .asyn_wait = ENABLE,
  49. .nwait_signal = ENABLE,
  50. .memory_write = DISABLE,
  51. .nwait_config = EXMC_NWAIT_CONFIG_DURING,
  52. .wrap_burst_mode = DISABLE,
  53. .nwait_polarity = EXMC_NWAIT_POLARITY_HIGH,
  54. .burst_mode = DISABLE,
  55. .databus_width = EXMC_NOR_DATABUS_WIDTH_16B,
  56. .memory_type = EXMC_MEMORY_TYPE_SRAM,
  57. .address_data_mux = DISABLE,
  58. .read_write_timing = &timing_param
  59. };
  60. EXMC_SNCTL(EXMC_BANK0_NORSRAM_REGION0) &= ~EXMC_SNCTL_NRBKEN;
  61. exmc_norsram_init(&sram_param);
  62. // DMA used to transfer data from EXMC to RAM
  63. // DMA is used so that if data transfer fails, we can at least abort by resetting CPU.
  64. // Accessing EXMC from the CPU directly hangs it totally if ACK pulses are not received.
  65. dma_parameter_struct exmc_dma_config =
  66. {
  67. .periph_addr = EXMC_NOR_PSRAM,
  68. .periph_width = DMA_PERIPHERAL_WIDTH_16BIT,
  69. .memory_addr = (uint32_t)g_sync_dma_buf,
  70. .memory_width = DMA_MEMORY_WIDTH_16BIT,
  71. .number = 0, // Filled before transfer
  72. .priority = DMA_PRIORITY_MEDIUM,
  73. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  74. .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
  75. .direction = DMA_PERIPHERAL_TO_MEMORY
  76. };
  77. dma_init(SCSI_EXMC_DMA, SCSI_EXMC_DMACH, &exmc_dma_config);
  78. dma_memory_to_memory_enable(SCSI_EXMC_DMA, SCSI_EXMC_DMACH);
  79. gpio_init(SCSI_IN_ACK_EXMC_NWAIT_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_IN_ACK_EXMC_NWAIT_PIN);
  80. }
  81. void scsi_accel_sync_read(uint8_t *data, uint32_t count, int* parityError, volatile int *resetFlag)
  82. {
  83. // Enable EXMC to drive REQ from EXMC_NOE pin
  84. EXMC_SNCTL(EXMC_BANK0_NORSRAM_REGION0) |= EXMC_SNCTL_NRBKEN;
  85. uint32_t oldmode = GPIO_CTL0(SCSI_OUT_REQ_EXMC_NOE_PORT);
  86. uint32_t newmode = oldmode & ~(0xF << (SCSI_OUT_REQ_EXMC_NOE_IDX * 4));
  87. newmode |= 0xB << (SCSI_OUT_REQ_EXMC_NOE_IDX * 4);
  88. GPIO_CTL0(SCSI_OUT_REQ_EXMC_NOE_PORT) = newmode;
  89. while (count > 0)
  90. {
  91. uint32_t blocksize = (count > SYNC_DMA_BUFSIZE * 2) ? (SYNC_DMA_BUFSIZE * 2) : count;
  92. count -= blocksize;
  93. DMA_CHCNT(SCSI_EXMC_DMA, SCSI_EXMC_DMACH) = blocksize;
  94. DMA_CHCTL(SCSI_EXMC_DMA, SCSI_EXMC_DMACH) |= DMA_CHXCTL_CHEN;
  95. uint16_t *src = (uint16_t*)g_sync_dma_buf;
  96. uint8_t *dst = data;
  97. uint8_t *end = data + blocksize;
  98. uint32_t start = millis();
  99. while (dst < end)
  100. {
  101. uint32_t remain = DMA_CHCNT(SCSI_EXMC_DMA, SCSI_EXMC_DMACH);
  102. while (dst < end - remain)
  103. {
  104. *dst++ = ~(*src++) >> SCSI_EXMC_DATA_SHIFT;
  105. }
  106. if ((uint32_t)(millis() - start) > 500 || *resetFlag)
  107. {
  108. // We are in a pinch here: without ACK pulses coming, the EXMC and DMA peripherals
  109. // are locked up. The only way out is a whole system reset.
  110. azlog("SCSI Synchronous read timeout: resetting system");
  111. NVIC_SystemReset();
  112. }
  113. }
  114. DMA_CHCTL(SCSI_EXMC_DMA, SCSI_EXMC_DMACH) &= ~DMA_CHXCTL_CHEN;
  115. }
  116. GPIO_CTL0(SCSI_OUT_REQ_EXMC_NOE_PORT) = oldmode;
  117. EXMC_SNCTL(EXMC_BANK0_NORSRAM_REGION0) &= ~EXMC_SNCTL_NRBKEN;
  118. }
  119. void scsi_accel_sync_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag)
  120. {
  121. for (int i = 0; i < count; i++)
  122. {
  123. SCSI_OUT_DATA(data[i]);
  124. delay_100ns();
  125. SCSI_OUT(REQ, 1);
  126. delay_ns(200);
  127. SCSI_OUT(REQ, 0);
  128. delay_ns(500);
  129. }
  130. SCSI_RELEASE_DATA_REQ();
  131. }
  132. void scsi_accel_sync_stopWrite()
  133. {
  134. }
  135. void scsi_accel_sync_finishWrite(volatile int *resetFlag)
  136. {
  137. }
  138. bool scsi_accel_sync_isWriteFinished(const uint8_t* data)
  139. {
  140. return true;
  141. }
  142. #endif