Ver código fonte

SCSI-1: Add delay before changing to DATA phase for compatibility with Akai S1000 (#71)

It seems that changing SCSI control signals too quickly after command is received
confuses some SCSI hosts. This commit adds a 400 us delay which has been determined
to be enough for Akai S1000, where the problem was observed.

To avoid performance degradation, the extra delay is performed in parallel with
SD card access. The SD card typically has a 1 millisecond latency, so the extra
wait does not increase the total latency.

The delay is only active in SCSI-1 mode. This is activated either explicitly
by setting EnableSCSI2 = 0, or automatically if the host does not send the
SCSI-2 Identify message.
Petteri Aimonen 3 anos atrás
pai
commit
0080cecab1

+ 12 - 4
lib/ZuluSCSI_platform_GD32F205/scsiPhy.cpp

@@ -212,12 +212,20 @@ extern "C" void scsiEnterPhase(int phase)
 // Change state and return nanosecond delay to wait
 extern "C" uint32_t scsiEnterPhaseImmediate(int phase)
 {
-    // ANSI INCITS 362-2002 SPI-3 10.7.1:
-    // Phase changes are not allowed while REQ or ACK is asserted.
-    while (likely(!scsiDev.resetFlag) && SCSI_IN(ACK)) {}
-
     if (phase != g_scsi_phase)
     {
+        // ANSI INCITS 362-2002 SPI-3 10.7.1:
+        // Phase changes are not allowed while REQ or ACK is asserted.
+        while (likely(!scsiDev.resetFlag) && SCSI_IN(ACK)) {}
+
+        if (scsiDev.compatMode < COMPAT_SCSI2 && (phase == DATA_IN || phase == DATA_OUT))
+        {
+            // Akai S1000/S3000 seems to need extra delay before changing to data phase
+            // after a command. The code in ZuluSCSI_disk.cpp tries to do this while waiting
+            // for SD card, to avoid any extra latency.
+            s2s_delay_ns(400000);
+        }
+
         int oldphase = g_scsi_phase;
         g_scsi_phase = (SCSI_PHASE)phase;
         scsiLogPhaseChange(phase);

+ 12 - 4
lib/ZuluSCSI_platform_RP2040/scsiPhy.cpp

@@ -168,12 +168,20 @@ extern "C" void scsiEnterPhase(int phase)
 // Change state and return nanosecond delay to wait
 extern "C" uint32_t scsiEnterPhaseImmediate(int phase)
 {
-    // ANSI INCITS 362-2002 SPI-3 10.7.1:
-    // Phase changes are not allowed while REQ or ACK is asserted.
-    while (likely(!scsiDev.resetFlag) && SCSI_IN(ACK)) {}
-
     if (phase != g_scsi_phase)
     {
+        // ANSI INCITS 362-2002 SPI-3 10.7.1:
+        // Phase changes are not allowed while REQ or ACK is asserted.
+        while (likely(!scsiDev.resetFlag) && SCSI_IN(ACK)) {}
+
+        if (scsiDev.compatMode < COMPAT_SCSI2 && (phase == DATA_IN || phase == DATA_OUT))
+        {
+            // Akai S1000/S3000 seems to need extra delay before changing to data phase
+            // after a command. The code in ZuluSCSI_disk.cpp tries to do this while waiting
+            // for SD card, to avoid any extra latency.
+            s2s_delay_ns(400000);
+        }
+
         int oldphase = g_scsi_phase;
         g_scsi_phase = (SCSI_PHASE)phase;
         scsiLogPhaseChange(phase);

+ 12 - 4
lib/ZuluSCSI_platform_template/scsiPhy.cpp

@@ -133,12 +133,20 @@ extern "C" void scsiEnterPhase(int phase)
 // Change state and return nanosecond delay to wait
 extern "C" uint32_t scsiEnterPhaseImmediate(int phase)
 {
-    // ANSI INCITS 362-2002 SPI-3 10.7.1:
-    // Phase changes are not allowed while REQ or ACK is asserted.
-    while (likely(!scsiDev.resetFlag) && SCSI_IN(ACK)) {}
-
     if (phase != g_scsi_phase)
     {
+        // ANSI INCITS 362-2002 SPI-3 10.7.1:
+        // Phase changes are not allowed while REQ or ACK is asserted.
+        while (likely(!scsiDev.resetFlag) && SCSI_IN(ACK)) {}
+
+        if (scsiDev.compatMode < COMPAT_SCSI2 && (phase == DATA_IN || phase == DATA_OUT))
+        {
+            // Akai S1000/S3000 seems to need extra delay before changing to data phase
+            // after a command. The code in ZuluSCSI_disk.cpp tries to do this while waiting
+            // for SD card, to avoid any extra latency.
+            s2s_delay_ns(400000);
+        }
+
         int oldphase = g_scsi_phase;
         g_scsi_phase = (SCSI_PHASE)phase;
         scsiLogPhaseChange(phase);

+ 4 - 2
src/ZuluSCSI_disk.cpp

@@ -1266,6 +1266,10 @@ static void doRead(uint32_t lba, uint32_t blocks)
 
 void diskDataIn_callback(uint32_t bytes_complete)
 {
+    // On SCSI-1 devices the phase change has some extra delays.
+    // Doing it here lets the SD card transfer proceed in background.
+    scsiEnterPhase(DATA_IN);
+
     // For best performance, do writes in blocks of 4 or more bytes
     if (bytes_complete < g_disk_transfer.bytes_sd)
     {
@@ -1335,8 +1339,6 @@ static void start_dataInTransfer(uint8_t *buffer, uint32_t count)
 
 static void diskDataIn()
 {
-    scsiEnterPhase(DATA_IN);
-
     // Figure out how many blocks we can fit in buffer
     uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
     uint32_t maxblocks = sizeof(scsiDev.data) / bytesPerSector;