|
|
@@ -18,6 +18,7 @@
|
|
|
#include "scsi_accel_sync.h"
|
|
|
#include <AzulSCSI_log.h>
|
|
|
#include <gd32f20x_exmc.h>
|
|
|
+#include <scsi.h>
|
|
|
|
|
|
#ifndef SCSI_SYNC_MODE_AVAILABLE
|
|
|
|
|
|
@@ -38,8 +39,8 @@ static uint32_t g_sync_dma_buf[SYNC_DMA_BUFSIZE];
|
|
|
void scsi_accel_sync_init()
|
|
|
{
|
|
|
rcu_periph_clock_enable(RCU_EXMC);
|
|
|
- rcu_periph_clock_enable(SCSI_TIMER_RCU);
|
|
|
rcu_periph_clock_enable(SCSI_EXMC_DMA_RCU);
|
|
|
+ rcu_periph_clock_enable(SCSI_SYNC_TIMER_RCU);
|
|
|
|
|
|
exmc_norsram_timing_parameter_struct timing_param = {
|
|
|
.asyn_access_mode = EXMC_ACCESS_MODE_A,
|
|
|
@@ -90,6 +91,14 @@ void scsi_accel_sync_init()
|
|
|
dma_memory_to_memory_enable(SCSI_EXMC_DMA, SCSI_EXMC_DMACH);
|
|
|
|
|
|
gpio_init(SCSI_IN_ACK_EXMC_NWAIT_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_IN_ACK_EXMC_NWAIT_PIN);
|
|
|
+ gpio_init(SCSI_TIMER_IN_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_TIMER_IN_PIN);
|
|
|
+
|
|
|
+ // TIMER1 is used to count ACK pulses
|
|
|
+ TIMER_CTL0(SCSI_SYNC_TIMER) = 0;
|
|
|
+ TIMER_SMCFG(SCSI_SYNC_TIMER) = TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_CI0FE0;
|
|
|
+ TIMER_CAR(SCSI_SYNC_TIMER) = 65535;
|
|
|
+ TIMER_PSC(SCSI_SYNC_TIMER) = 0;
|
|
|
+ TIMER_CHCTL0(SCSI_SYNC_TIMER) = 0x0001; // CH0 as input
|
|
|
}
|
|
|
|
|
|
void scsi_accel_sync_recv(uint8_t *data, uint32_t count, int* parityError, volatile int *resetFlag)
|
|
|
@@ -132,6 +141,7 @@ void scsi_accel_sync_recv(uint8_t *data, uint32_t count, int* parityError, volat
|
|
|
}
|
|
|
|
|
|
DMA_CHCTL(SCSI_EXMC_DMA, SCSI_EXMC_DMACH) &= ~DMA_CHXCTL_CHEN;
|
|
|
+ data = end;
|
|
|
}
|
|
|
|
|
|
GPIO_CTL0(SCSI_OUT_REQ_EXMC_NOE_PORT) = oldmode;
|
|
|
@@ -142,19 +152,324 @@ void scsi_accel_sync_recv(uint8_t *data, uint32_t count, int* parityError, volat
|
|
|
/* Transfer from device to host */
|
|
|
/********************************/
|
|
|
|
|
|
+// Simple delay, about 10 ns.
|
|
|
+// This is less likely to get optimized away by CPU pipeline than nop
|
|
|
+#define ASM_DELAY() \
|
|
|
+" ldr %[tmp2], [%[reset_flag]] \n"
|
|
|
+
|
|
|
+// Take 8 bits from d and format them for writing
|
|
|
+// d is name of data operand, b is bit offset
|
|
|
+#define ASM_LOAD_DATA(b) \
|
|
|
+" ubfx %[tmp1], %[data], #" b ", #8 \n" \
|
|
|
+" ldr %[tmp1], [%[byte_lookup], %[tmp1], lsl #2] \n"
|
|
|
+
|
|
|
+// Write data to SCSI port and set REQ high
|
|
|
+#define ASM_SEND_DATA() \
|
|
|
+" str %[tmp1], [%[out_port_bop]] \n"
|
|
|
+
|
|
|
+// Set REQ low
|
|
|
+#define ASM_SET_REQ_LOW() \
|
|
|
+" mov %[tmp2], %[bop_req_low] \n" \
|
|
|
+" str %[tmp2], [%[out_port_bop]] \n"
|
|
|
+
|
|
|
+// Wait for ACK_TIMER - n to be less than num_bytes
|
|
|
+#define ASM_WAIT_ACK_TIMER(n) \
|
|
|
+ "wait_acks_" n "_%=: \n" \
|
|
|
+ " ldr %[tmp2], [%[ack_timer]] \n" \
|
|
|
+ " sub %[tmp2], # " n " \n" \
|
|
|
+ " cmp %[tmp2], %[num_bytes] \n" \
|
|
|
+ " ble got_acks_" n "_%= \n" \
|
|
|
+ " ldr %[tmp2], [%[reset_flag]] \n" \
|
|
|
+ " cmp %[tmp2], #0 \n" \
|
|
|
+ " bne all_done_%= \n" \
|
|
|
+ " b wait_acks_" n "_%= \n" \
|
|
|
+ "got_acks_" n "_%=: \n"
|
|
|
+
|
|
|
+// Send 4 bytes
|
|
|
+#define ASM_SEND_4BYTES() \
|
|
|
+ASM_LOAD_DATA("0") \
|
|
|
+ASM_SEND_DATA() \
|
|
|
+ASM_DELAY1() \
|
|
|
+ASM_SET_REQ_LOW() \
|
|
|
+ASM_DELAY2() \
|
|
|
+ASM_LOAD_DATA("8") \
|
|
|
+ASM_SEND_DATA() \
|
|
|
+ASM_DELAY1() \
|
|
|
+ASM_SET_REQ_LOW() \
|
|
|
+ASM_DELAY2() \
|
|
|
+ASM_LOAD_DATA("16") \
|
|
|
+ASM_SEND_DATA() \
|
|
|
+ASM_DELAY1() \
|
|
|
+ASM_SET_REQ_LOW() \
|
|
|
+ASM_DELAY2() \
|
|
|
+ASM_LOAD_DATA("24") \
|
|
|
+ASM_SEND_DATA() \
|
|
|
+ASM_DELAY1() \
|
|
|
+ASM_SET_REQ_LOW()
|
|
|
+
|
|
|
+// Send 1 byte, wait for ACK_TIMER to be less than num_bytes + n and send 3 bytes more
|
|
|
+// This interleaving minimizes the delay caused by WAIT_ACK_TIMER.
|
|
|
+#define ASM_SEND_4BYTES_WAIT(n) \
|
|
|
+ASM_LOAD_DATA("0") \
|
|
|
+ASM_SEND_DATA() \
|
|
|
+ASM_DELAY2() \
|
|
|
+ASM_LOAD_DATA("8") \
|
|
|
+ASM_SET_REQ_LOW() \
|
|
|
+ASM_DELAY2() \
|
|
|
+" ldr %[tmp2], [%[ack_timer]] \n" \
|
|
|
+" sub %[tmp2], # " n " \n" \
|
|
|
+ASM_SEND_DATA() \
|
|
|
+" cmp %[tmp2], %[num_bytes] \n" \
|
|
|
+" ble got_acks_" n "_%= \n" \
|
|
|
+ASM_WAIT_ACK_TIMER(n) \
|
|
|
+ASM_DELAY2() \
|
|
|
+ASM_SET_REQ_LOW() \
|
|
|
+ASM_DELAY2() \
|
|
|
+ASM_LOAD_DATA("16") \
|
|
|
+ASM_SEND_DATA() \
|
|
|
+ASM_DELAY1() \
|
|
|
+ASM_SET_REQ_LOW() \
|
|
|
+ASM_DELAY2() \
|
|
|
+ASM_LOAD_DATA("24") \
|
|
|
+ASM_SEND_DATA() \
|
|
|
+ASM_DELAY1() \
|
|
|
+ASM_SET_REQ_LOW() \
|
|
|
+
|
|
|
+// Specialized routine for settings:
|
|
|
+// <=100 ns period, >=15 outstanding REQs
|
|
|
+static void sync_send_100ns_15off(const uint8_t *buf, uint32_t num_bytes, volatile int *resetFlag)
|
|
|
+{
|
|
|
+ volatile uint32_t *out_port_bop = (volatile uint32_t*)&GPIO_BOP(SCSI_OUT_PORT);
|
|
|
+ volatile uint32_t *ack_timer = &TIMER_CNT(SCSI_SYNC_TIMER);
|
|
|
+ const uint32_t *byte_lookup = g_scsi_out_byte_to_bop;
|
|
|
+ register uint32_t tmp1 = 0;
|
|
|
+ register uint32_t tmp2 = 0;
|
|
|
+ register uint32_t data = 0;
|
|
|
+
|
|
|
+#define ASM_DELAY1()
|
|
|
+#define ASM_DELAY2() ASM_DELAY()
|
|
|
+
|
|
|
+ asm volatile (
|
|
|
+ "main_loop_%=: \n"
|
|
|
+ " subs %[num_bytes], %[num_bytes], #16 \n"
|
|
|
+ " bmi last_bytes_%= \n"
|
|
|
+
|
|
|
+ /* At each point make sure there is at most 15 bytes in flight */
|
|
|
+ " ldr %[data], [%[buf]], #4 \n"
|
|
|
+ ASM_SEND_4BYTES_WAIT("22")
|
|
|
+ ASM_DELAY2()
|
|
|
+ " ldr %[data], [%[buf]], #4 \n"
|
|
|
+ ASM_SEND_4BYTES()
|
|
|
+ ASM_DELAY2()
|
|
|
+ " ldr %[data], [%[buf]], #4 \n"
|
|
|
+ ASM_SEND_4BYTES_WAIT("14")
|
|
|
+ ASM_DELAY2()
|
|
|
+ " ldr %[data], [%[buf]], #4 \n"
|
|
|
+ ASM_SEND_4BYTES()
|
|
|
+
|
|
|
+ " cbz %[num_bytes], all_done_%= \n"
|
|
|
+ " b main_loop_%= \n"
|
|
|
+
|
|
|
+ "last_bytes_%=: \n"
|
|
|
+ " add %[num_bytes], %[num_bytes], #16 \n"
|
|
|
+ "last_bytes_loop_%=: \n"
|
|
|
+ " ldrb %[data], [%[buf]], #1 \n"
|
|
|
+ ASM_LOAD_DATA("0")
|
|
|
+
|
|
|
+ ASM_WAIT_ACK_TIMER("15")
|
|
|
+ ASM_SEND_DATA()
|
|
|
+ ASM_DELAY1()
|
|
|
+ ASM_SET_REQ_LOW()
|
|
|
+ ASM_DELAY2()
|
|
|
+
|
|
|
+ " subs %[num_bytes], %[num_bytes], #1 \n"
|
|
|
+ " bne last_bytes_loop_%= \n"
|
|
|
+ "all_done_%=: \n"
|
|
|
+ ASM_DELAY1()
|
|
|
+
|
|
|
+ : /* Output */ [tmp1] "+l" (tmp1), [tmp2] "+l" (tmp2), [data] "+r" (data),
|
|
|
+ [buf] "+r" (buf), [num_bytes] "+r" (num_bytes)
|
|
|
+ : /* Input */ [ack_timer] "r" (ack_timer),
|
|
|
+ [bop_req_low] "I" (SCSI_OUT_REQ << 16),
|
|
|
+ [out_port_bop] "r"(out_port_bop),
|
|
|
+ [byte_lookup] "r" (byte_lookup),
|
|
|
+ [reset_flag] "r" (resetFlag)
|
|
|
+ : /* Clobber */);
|
|
|
+
|
|
|
+#undef ASM_DELAY1
|
|
|
+#undef ASM_DELAY2
|
|
|
+
|
|
|
+ SCSI_RELEASE_DATA_REQ();
|
|
|
+}
|
|
|
+
|
|
|
+// Specialized routine for settings:
|
|
|
+// <=200 ns period, >=15 outstanding REQs
|
|
|
+static void sync_send_200ns_15off(const uint8_t *buf, uint32_t num_bytes, volatile int *resetFlag)
|
|
|
+{
|
|
|
+ volatile uint32_t *out_port_bop = (volatile uint32_t*)&GPIO_BOP(SCSI_OUT_PORT);
|
|
|
+ volatile uint32_t *ack_timer = &TIMER_CNT(SCSI_SYNC_TIMER);
|
|
|
+ const uint32_t *byte_lookup = g_scsi_out_byte_to_bop;
|
|
|
+ register uint32_t tmp1 = 0;
|
|
|
+ register uint32_t tmp2 = 0;
|
|
|
+ register uint32_t data = 0;
|
|
|
+
|
|
|
+#define ASM_DELAY1() ASM_DELAY() ASM_DELAY() ASM_DELAY()
|
|
|
+#define ASM_DELAY2() ASM_DELAY() ASM_DELAY() ASM_DELAY() ASM_DELAY()
|
|
|
+
|
|
|
+ asm volatile (
|
|
|
+ "main_loop_%=: \n"
|
|
|
+ " subs %[num_bytes], %[num_bytes], #16 \n"
|
|
|
+ " bmi last_bytes_%= \n"
|
|
|
+
|
|
|
+ /* At each point make sure there is at most 15 bytes in flight */
|
|
|
+ " ldr %[data], [%[buf]], #4 \n"
|
|
|
+ ASM_SEND_4BYTES_WAIT("22")
|
|
|
+ ASM_DELAY2()
|
|
|
+ " ldr %[data], [%[buf]], #4 \n"
|
|
|
+ ASM_SEND_4BYTES()
|
|
|
+ ASM_DELAY2()
|
|
|
+ " ldr %[data], [%[buf]], #4 \n"
|
|
|
+ ASM_SEND_4BYTES_WAIT("14")
|
|
|
+ ASM_DELAY2()
|
|
|
+ " ldr %[data], [%[buf]], #4 \n"
|
|
|
+ ASM_SEND_4BYTES()
|
|
|
+
|
|
|
+ " cbz %[num_bytes], all_done_%= \n"
|
|
|
+ " b main_loop_%= \n"
|
|
|
+
|
|
|
+ "last_bytes_%=: \n"
|
|
|
+ " add %[num_bytes], %[num_bytes], #16 \n"
|
|
|
+ "last_bytes_loop_%=: \n"
|
|
|
+ " ldrb %[data], [%[buf]], #1 \n"
|
|
|
+ ASM_LOAD_DATA("0")
|
|
|
+
|
|
|
+ ASM_WAIT_ACK_TIMER("15")
|
|
|
+ ASM_SEND_DATA()
|
|
|
+ ASM_DELAY1()
|
|
|
+ ASM_SET_REQ_LOW()
|
|
|
+ ASM_DELAY2()
|
|
|
+
|
|
|
+ " subs %[num_bytes], %[num_bytes], #1 \n"
|
|
|
+ " bne last_bytes_loop_%= \n"
|
|
|
+ "all_done_%=: \n"
|
|
|
+ ASM_DELAY1()
|
|
|
+
|
|
|
+ : /* Output */ [tmp1] "+l" (tmp1), [tmp2] "+l" (tmp2), [data] "+r" (data),
|
|
|
+ [buf] "+r" (buf), [num_bytes] "+r" (num_bytes)
|
|
|
+ : /* Input */ [ack_timer] "r" (ack_timer),
|
|
|
+ [bop_req_low] "I" (SCSI_OUT_REQ << 16),
|
|
|
+ [out_port_bop] "r"(out_port_bop),
|
|
|
+ [byte_lookup] "r" (byte_lookup),
|
|
|
+ [reset_flag] "r" (resetFlag)
|
|
|
+ : /* Clobber */);
|
|
|
+
|
|
|
+#undef ASM_DELAY1
|
|
|
+#undef ASM_DELAY2
|
|
|
+
|
|
|
+ SCSI_RELEASE_DATA_REQ();
|
|
|
+}
|
|
|
+
|
|
|
+// Specialized routine for settings:
|
|
|
+// <=260 ns period, >=7 outstanding REQs
|
|
|
+static void sync_send_260ns_7off(const uint8_t *buf, uint32_t num_bytes, volatile int *resetFlag)
|
|
|
+{
|
|
|
+ volatile uint32_t *out_port_bop = (volatile uint32_t*)&GPIO_BOP(SCSI_OUT_PORT);
|
|
|
+ volatile uint32_t *ack_timer = &TIMER_CNT(SCSI_SYNC_TIMER);
|
|
|
+ const uint32_t *byte_lookup = g_scsi_out_byte_to_bop;
|
|
|
+ register uint32_t tmp1 = 0;
|
|
|
+ register uint32_t tmp2 = 0;
|
|
|
+ register uint32_t data = 0;
|
|
|
+
|
|
|
+#define ASM_DELAY1() ASM_DELAY() ASM_DELAY() ASM_DELAY() ASM_DELAY() \
|
|
|
+ ASM_DELAY() ASM_DELAY()
|
|
|
+#define ASM_DELAY2() ASM_DELAY() ASM_DELAY() ASM_DELAY() ASM_DELAY() \
|
|
|
+ ASM_DELAY() ASM_DELAY() ASM_DELAY() ASM_DELAY()
|
|
|
+
|
|
|
+ asm volatile (
|
|
|
+ "main_loop_%=: \n"
|
|
|
+ " subs %[num_bytes], %[num_bytes], #4 \n"
|
|
|
+ " bmi last_bytes_%= \n"
|
|
|
+
|
|
|
+ /* At each point make sure there is at most 3 bytes in flight */
|
|
|
+ " ldr %[data], [%[buf]], #4 \n"
|
|
|
+ ASM_SEND_4BYTES_WAIT("7")
|
|
|
+
|
|
|
+ " cbz %[num_bytes], all_done_%= \n"
|
|
|
+ " b main_loop_%= \n"
|
|
|
+
|
|
|
+ "last_bytes_%=: \n"
|
|
|
+ " add %[num_bytes], %[num_bytes], #4 \n"
|
|
|
+ "last_bytes_loop_%=: \n"
|
|
|
+ " ldrb %[data], [%[buf]], #1 \n"
|
|
|
+ ASM_LOAD_DATA("0")
|
|
|
+
|
|
|
+ ASM_WAIT_ACK_TIMER("5")
|
|
|
+ ASM_SEND_DATA()
|
|
|
+ ASM_DELAY1()
|
|
|
+ ASM_SET_REQ_LOW()
|
|
|
+ ASM_DELAY2()
|
|
|
+
|
|
|
+ " subs %[num_bytes], %[num_bytes], #1 \n"
|
|
|
+ " bne last_bytes_loop_%= \n"
|
|
|
+ "all_done_%=: \n"
|
|
|
+ ASM_DELAY1()
|
|
|
+
|
|
|
+ : /* Output */ [tmp1] "+l" (tmp1), [tmp2] "+l" (tmp2), [data] "+r" (data),
|
|
|
+ [buf] "+r" (buf), [num_bytes] "+r" (num_bytes)
|
|
|
+ : /* Input */ [ack_timer] "r" (ack_timer),
|
|
|
+ [bop_req_low] "I" (SCSI_OUT_REQ << 16),
|
|
|
+ [out_port_bop] "r"(out_port_bop),
|
|
|
+ [byte_lookup] "r" (byte_lookup),
|
|
|
+ [reset_flag] "r" (resetFlag)
|
|
|
+ : /* Clobber */);
|
|
|
+
|
|
|
+#undef ASM_DELAY1
|
|
|
+#undef ASM_DELAY2
|
|
|
+
|
|
|
+ SCSI_RELEASE_DATA_REQ();
|
|
|
+}
|
|
|
+
|
|
|
void scsi_accel_sync_send(const uint8_t* data, uint32_t count, volatile int *resetFlag)
|
|
|
{
|
|
|
- for (int i = 0; i < count; i++)
|
|
|
+ // Timer counts down from the initial number of bytes.
|
|
|
+ TIMER_CNT(SCSI_SYNC_TIMER) = count;
|
|
|
+ TIMER_CTL0(SCSI_SYNC_TIMER) = TIMER_CTL0_CEN | TIMER_CTL0_DIR;
|
|
|
+
|
|
|
+ int syncOffset = scsiDev.target->syncOffset;
|
|
|
+ int syncPeriod = scsiDev.target->syncPeriod;
|
|
|
+
|
|
|
+ if (syncOffset >= 15 && syncPeriod <= 25)
|
|
|
{
|
|
|
- SCSI_OUT_DATA(data[i]);
|
|
|
- delay_100ns();
|
|
|
- SCSI_OUT(REQ, 1);
|
|
|
- delay_ns(200);
|
|
|
- SCSI_OUT(REQ, 0);
|
|
|
- delay_ns(500);
|
|
|
+ sync_send_100ns_15off(data, count, resetFlag);
|
|
|
}
|
|
|
- SCSI_RELEASE_DATA_REQ();
|
|
|
+ else if (syncOffset >= 15 && syncPeriod <= 50)
|
|
|
+ {
|
|
|
+ sync_send_200ns_15off(data, count, resetFlag);
|
|
|
+ }
|
|
|
+ else if (syncOffset >= 7 && syncPeriod <= 65)
|
|
|
+ {
|
|
|
+ sync_send_260ns_7off(data, count, resetFlag);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ azdbg("No optimized routine for syncOffset=", syncOffset, " syndPeriod=", syncPeriod, ", using fallback");
|
|
|
+ while (count-- > 0)
|
|
|
+ {
|
|
|
+ while (TIMER_CNT(SCSI_SYNC_TIMER) > count + syncOffset && !*resetFlag);
|
|
|
+
|
|
|
+ SCSI_OUT_DATA(*data++);
|
|
|
+ delay_ns(syncPeriod * 2);
|
|
|
+ SCSI_OUT(REQ, 0);
|
|
|
+ delay_ns(syncPeriod * 2);
|
|
|
+ }
|
|
|
+ delay_ns(syncPeriod * 2);
|
|
|
+ SCSI_RELEASE_DATA_REQ();
|
|
|
+ }
|
|
|
+
|
|
|
+ while (TIMER_CNT(SCSI_SYNC_TIMER) > 0 && !*resetFlag);
|
|
|
+
|
|
|
+ TIMER_CTL0(SCSI_SYNC_TIMER) = 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
-#endif
|
|
|
+#endif
|