Also fixes bug in FPGA interface that caused issues writing large files.
2016XXXX 6.0.7
+ - Synchronous transfers supported ! 5MB/s and 10MB/s supported.
- Fix for accessing data via USB with more than 2 devices configured.
20160815 6.0.6
CFLAGS=-mcpu=cortex-m3 -mthumb -mslow-flash-data \
-std=gnu11 \
-specs=nosys.specs \
- -Os -g \
+ -g \
+# -Os -g \
LDFLAGS= \
"-Tsrc/firmware/link.ld" \
// For the STM32F205, DMA bursts may not cross 1KB address boundaries.
// The maximum burst is 16 bytes.
-#define S2S_DMA_ALIGN __attribute__((aligned(16)))
+#define S2S_DMA_ALIGN __attribute__((aligned(1024)))
#endif
\r
#include <string.h>\r
\r
-static const uint16_t FIRMWARE_VERSION = 0x0606;\r
+static const uint16_t FIRMWARE_VERSION = 0x0607;\r
\r
// 1 flash row\r
static const uint8_t DEFAULT_CONFIG[128] =\r
(allocLength > MAX_SECTOR_SIZE) ? MAX_SECTOR_SIZE : allocLength;\r
scsiDev.phase = DATA_IN;\r
}\r
+ else if (mode == 0x2 && (scsiDev.cdb[2] == 0))\r
+ {\r
+ // TODO support BUFFER OFFSET fields in CDB\r
+ scsiDev.dataLen =\r
+ (allocLength > MAX_SECTOR_SIZE) ? MAX_SECTOR_SIZE : allocLength;\r
+ scsiDev.phase = DATA_IN;\r
+ }\r
+ else if (mode == 0x3)\r
+ {\r
+ uint32_t maxSize = MAX_SECTOR_SIZE - 4;\r
+ // 4 byte header\r
+ scsiDev.data[0] = 0;\r
+ scsiDev.data[1] = (maxSize >> 16) & 0xff;\r
+ scsiDev.data[2] = (maxSize >> 8) & 0xff;\r
+ scsiDev.data[3] = maxSize & 0xff;\r
+\r
+ scsiDev.dataLen =\r
+ (allocLength > 4) ? 4: allocLength;\r
+ scsiDev.phase = DATA_IN;\r
+ }\r
else\r
{\r
// error.\r
(((uint32_t) scsiDev.cdb[7]) << 8) +\r
scsiDev.cdb[8];\r
\r
- if (mode == 0 && allocLength <= sizeof(scsiDev.data))\r
+ if ((mode == 0 || mode == 2) && allocLength <= sizeof(scsiDev.data))\r
{\r
scsiDev.dataLen = allocLength;\r
scsiDev.phase = DATA_OUT;\r
0x01, // Response format is compatible with the old CCS format.\r
0x1f, // standard length.\r
0, 0, // Reserved\r
-0x08 // Enable linked commands\r
+0x18 // Enable sync and linked commands\r
};\r
// Vendor set by config 'c','o','d','e','s','r','c',' ',\r
// prodId set by config'S','C','S','I','2','S','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',\r
scsiDev.target->sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
}\r
scsiDev.target = NULL;\r
+\r
+ for (int i = 0; i < S2S_MAX_TARGETS; ++i)\r
+ {\r
+ scsiDev.target[i].syncOffset = 0;\r
+ scsiDev.target[i].syncPeriod = 0;\r
+ }\r
+\r
scsiDiskReset();\r
\r
scsiDev.postDataOutHook = NULL;\r
transfer.currentBlock = 0;\r
\r
scsiDev.postDataOutHook = NULL;\r
+\r
+ scsiDev.needSyncNegotiationAck = 0;\r
}\r
\r
static void process_SelectionPhase()\r
// ANY initiator can reset the reservation state via this message.\r
scsiDev.target->reservedId = -1;\r
scsiDev.target->reserverId = -1;\r
+\r
+ // Cancel any sync negotiation\r
+ scsiDev.target->syncOffset = 0;\r
+ scsiDev.target->syncPeriod = 0;\r
+\r
enter_BusFree();\r
}\r
else if (scsiDev.msgOut == 0x05)\r
{\r
// Message Reject\r
// Oh well.\r
- scsiDev.resetFlag = 1;\r
+\r
+ if (scsiDev.needSyncNegotiationAck)\r
+ {\r
+ scsiDev.target->syncOffset = 0;\r
+ scsiDev.target->syncPeriod = 0;\r
+ scsiDev.needSyncNegotiationAck = 0;\r
+ }\r
}\r
else if (scsiDev.msgOut == 0x08)\r
{\r
}\r
else if (extmsg[0] == 1 && msgLen == 3) // Synchronous data request\r
{\r
- // Negotiate back to async\r
+ int transferPeriod = extmsg[3];\r
+ int offset = extmsg[4];\r
+\r
+ if (transferPeriod > 50) // 200ns, 5MB/s\r
+ {\r
+ scsiDev.target->syncOffset = 0;\r
+ scsiDev.target->syncPeriod = 0;\r
+ } else {\r
+ scsiDev.target->syncOffset = offset < 15 ? offset : 15;\r
+ if (transferPeriod <= 25)\r
+ {\r
+ scsiDev.target->syncPeriod = 25; // 10MB/s\r
+ } else {\r
+ scsiDev.target->syncPeriod = 50; // 5MB/s\r
+ }\r
+ }\r
+\r
scsiEnterPhase(MESSAGE_IN);\r
- static const uint8_t SDTR[] = {0x01, 0x03, 0x01, 0x00, 0x00};\r
+ uint8_t SDTR[] = {0x01, 0x03, 0x01, scsiDev.target->syncPeriod, scsiDev.target->syncOffset};\r
scsiWrite(SDTR, sizeof(SDTR));\r
+ scsiDev.needSyncNegotiationAck = 1; // Check if this message is rejected.\r
}\r
else\r
{\r
\r
// Re-check the ATN flag in case it stays asserted.\r
scsiDev.atnFlag |= scsiStatusATN();\r
+\r
+ if (!scsiDev.atnFlag)\r
+ {\r
+ // Message wasn't rejected!\r
+ scsiDev.needSyncNegotiationAck = 0;\r
+ }\r
}\r
\r
void scsiPoll(void)\r
}\r
scsiDev.targets[i].sense.code = NO_SENSE;\r
scsiDev.targets[i].sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
+\r
+ scsiDev.targets[i].syncOffset = 0;\r
+ scsiDev.targets[i].syncPeriod = 0;\r
}\r
firstInit = 0;\r
}\r
// A 3rd party may be sending the RESERVE/RELEASE commands
int reservedId; // 0 -> 7 if reserved. -1 if not reserved.
int reserverId; // 0 -> 7 if reserved. -1 if not reserved.
+
+ uint8_t syncOffset;
+ uint8_t syncPeriod;
} TargetState;
typedef struct
uint8_t lastStatus;
uint8_t lastSense;
uint16_t lastSenseASC;
+
+ int needSyncNegotiationAck;
} ScsiDevice;
extern ScsiDevice scsiDev;
\r
#include <string.h>\r
\r
+// Assumes a 60MHz fpga clock.\r
+// 7:6 Hold count, 45ns\r
+// 5:3 Assertion count, 90ns\r
+// 2:0 Deskew count, 55ns\r
+#define SCSI_DEFAULT_TIMING ((0x3 << 6) | (0x6 << 3) | 0x4)\r
+\r
+// 7:6 Hold count, 10ns\r
+// 5:3 Assertion count, 30ns\r
+// 2:0 Deskew count, 25ns\r
+#define SCSI_FAST_TIMING ((0x1 << 6) | (0x2 << 3) | 0x2)\r
+\r
// Private DMA variables.\r
static int dmaInProgress = 0;\r
\r
};\r
}\r
\r
-#if FIFODEBUG\r
- if (!scsiPhyFifoEmpty()) {\r
+\r
+ i += chunk;\r
+ chunk = nextChunk;\r
+ }\r
+#if 1\r
+ if (!scsiPhyFifoEmpty() || !scsiPhyFifoAltEmpty()) {\r
int j = 0;\r
while (!scsiPhyFifoEmpty()) { scsiPhyRx(); ++j; }\r
+ scsiPhyFifoFlip();\r
+ int k = 0;\r
+ while (!scsiPhyFifoEmpty()) { scsiPhyRx(); ++k; }\r
// Force a lock-up.\r
assertFail();\r
}\r
#endif\r
- i += chunk;\r
- chunk = nextChunk;\r
- }\r
}\r
\r
void\r
int newPhase = phase > 0 ? phase : 0;\r
int oldPhase = *SCSI_CTRL_PHASE;\r
\r
- if (!scsiPhyFifoEmpty() || !scsiPhyFifoAltEmpty()) {\r
+ if (!scsiDev.resetFlag && (!scsiPhyFifoEmpty() || !scsiPhyFifoAltEmpty())) {\r
// Force a lock-up.\r
assertFail();\r
}\r
if (newPhase != oldPhase)\r
{\r
+ if ((newPhase == DATA_IN || newPhase == DATA_OUT) &&\r
+ scsiDev.target->syncOffset)\r
+ {\r
+ if (scsiDev.target->syncPeriod == 25)\r
+ {\r
+ // SCSI2 FAST Timing. 10MB/s.\r
+ *SCSI_CTRL_TIMING = SCSI_FAST_TIMING;\r
+ } else {\r
+ // 5MB/s Timing\r
+ *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING;\r
+ }\r
+ *SCSI_CTRL_SYNC_OFFSET = scsiDev.target->syncOffset;\r
+ } else {\r
+ *SCSI_CTRL_SYNC_OFFSET = 0;\r
+ *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING;\r
+ }\r
+\r
*SCSI_CTRL_PHASE = newPhase;\r
busSettleDelay();\r
\r
*SCSI_FIFO_SEL = 0;\r
*SCSI_CTRL_DBX = 0;\r
\r
+ *SCSI_CTRL_SYNC_OFFSET = 0;\r
+ *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING;\r
+\r
// DMA Benchmark code\r
// Currently 10MB/s. Assume 20MB/s is achievable with 16 bits.\r
#ifdef DMA_BENCHMARK\r
*SCSI_FIFO_SEL = 0;\r
*SCSI_CTRL_DBX = 0;\r
\r
+ *SCSI_CTRL_SYNC_OFFSET = 0;\r
+ *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING;\r
+\r
}\r
\r
void scsiPhyConfig()\r
#define SCSI_DATA_CNT_LO ((volatile uint8_t*)0x60000005)
#define SCSI_DATA_CNT_SET ((volatile uint8_t*)0x60000006)
#define SCSI_CTRL_DBX ((volatile uint8_t*)0x60000007)
+#define SCSI_CTRL_SYNC_OFFSET ((volatile uint8_t*)0x60000008)
+#define SCSI_CTRL_TIMING ((volatile uint8_t*)0x60000009)
#define SCSI_STS_FIFO ((volatile uint8_t*)0x60000010)
#define SCSI_STS_ALTFIFO ((volatile uint8_t*)0x60000011)