Synch transfer fix
authorMichael McMaster <michael@codesrc.com>
Sun, 22 Jan 2017 07:40:13 +0000 (17:40 +1000)
committerMichael McMaster <michael@codesrc.com>
Sun, 22 Jan 2017 07:40:13 +0000 (17:40 +1000)
Makefile
include/hidpacket.h
rtl/fpga_bitmap.o
src/firmware/config.c
src/firmware/hidpacket.c
src/firmware/main.c
src/firmware/scsiPhy.c
src/firmware/spinlock.c [new file with mode: 0755]
src/firmware/spinlock.h [new file with mode: 0755]

index b3fa3f6..b90b51a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -51,6 +51,8 @@ build/stm32cubemx/stm32f2xx_hal_rcc.o: STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_
 build/stm32cubemx/stm32f2xx_hal_sd.o: STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sd.c
 build/stm32cubemx/stm32f2xx_hal_spi.o: STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_spi.c
 build/stm32cubemx/stm32f2xx_hal_sram.o: STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sram.c
+build/stm32cubemx/stm32f2xx_hal_tim.o: STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_tim.c
+build/stm32cubemx/stm32f2xx_hal_tim_ex.o: STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_tim_ex.c
 build/stm32cubemx/stm32f2xx_hal_uart.o: STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_uart.c
 build/stm32cubemx/stm32f2xx_ll_fsmc.o: STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_ll_fsmc.c
 build/stm32cubemx/stm32f2xx_ll_sdmmc.o: STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_ll_sdmmc.c
@@ -92,6 +94,8 @@ STM32OBJS = \
        build/stm32cubemx/stm32f2xx_hal_sd.o \
        build/stm32cubemx/stm32f2xx_hal_spi.o \
        build/stm32cubemx/stm32f2xx_hal_sram.o \
+       build/stm32cubemx/stm32f2xx_hal_tim.o \
+       build/stm32cubemx/stm32f2xx_hal_tim_ex.o \
        build/stm32cubemx/stm32f2xx_hal_uart.o \
        build/stm32cubemx/stm32f2xx_ll_fsmc.o \
        build/stm32cubemx/stm32f2xx_ll_sdmmc.o \
@@ -140,6 +144,7 @@ SRC = \
        src/firmware/scsiPhy.c \
        src/firmware/scsi.c \
        src/firmware/sd.c \
+       src/firmware/spinlock.c \
        src/firmware/tape.c \
        src/firmware/time.c \
        src/firmware/trace.c \
index 9beb002..384956f 100644 (file)
@@ -42,6 +42,10 @@ void hidPacket_recv(const uint8_t* bytes, size_t len);
 // available.
 const uint8_t* hidPacket_getPacket(size_t* len);
 
+// Returns the received packet contents, or NULL if a complete packet isn't
+// available.
+const uint8_t* hidPacket_peekPacket(size_t* len);
+
 // Call this with packet data to send. len <= USBHID_LEN
 // Overwrites any packet currently being sent.
 void hidPacket_send(const uint8_t* bytes, size_t len);
index 77e590a..c4ec7a4 100644 (file)
Binary files a/rtl/fpga_bitmap.o and b/rtl/fpga_bitmap.o differ
index 127ca1b..9bb8b11 100755 (executable)
@@ -25,6 +25,7 @@
 #include "trace.h"\r
 #include "bootloader.h"\r
 #include "bsp.h"\r
+#include "spinlock.h"\r
 \r
 #include "../../include/scsi2sd.h"\r
 #include "../../include/hidpacket.h"\r
@@ -64,6 +65,25 @@ enum USB_STATE
 \r
 static int usbInEpState;\r
 \r
+static void s2s_debugTimer();\r
+\r
+// Debug timer to log via USB.\r
+// Timer 6 & 7 is a simple counter with no external IO supported.\r
+static s2s_lock_t usbDevLock = s2s_lock_init;\r
+TIM_HandleTypeDef htim7;\r
+static int debugTimerStarted = 0;\r
+void TIM7_IRQHandler()\r
+{\r
+       HAL_TIM_IRQHandler(&htim7);\r
+}\r
+void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)\r
+{\r
+       if (s2s_spin_trylock(&usbDevLock)) {\r
+               s2s_debugTimer();\r
+               s2s_spin_unlock(&usbDevLock);\r
+       }\r
+}\r
+\r
 void s2s_configInit(S2S_BoardCfg* config)\r
 {\r
 \r
@@ -108,7 +128,25 @@ void s2s_configInit(S2S_BoardCfg* config)
                        config->flags6 = S2S_CFG_ENABLE_TERMINATOR;\r
                }\r
        }\r
+}\r
 \r
+static void debugInit(void)\r
+{\r
+       if (debugTimerStarted == 1) return;\r
+\r
+       debugTimerStarted = 1;\r
+       // 10ms debug timer to capture logs over USB\r
+       __TIM7_CLK_ENABLE();\r
+       htim7.Instance = TIM7;\r
+       htim7.Init.Prescaler = 10800 - 1; // 16bit. 108MHz down to 10KHz\r
+       htim7.Init.CounterMode = TIM_COUNTERMODE_UP;\r
+       htim7.Init.Period = 100 - 1; // 16bit. 10KHz down to 10ms.\r
+       htim7.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;\r
+       HAL_TIM_Base_Init(&htim7);\r
+       HAL_TIM_Base_Start_IT(&htim7);\r
+\r
+       HAL_NVIC_SetPriority(TIM7_IRQn, 10, 0);\r
+       HAL_NVIC_EnableIRQ(TIM7_IRQn);\r
 }\r
 \r
 \r
@@ -262,6 +300,9 @@ processCommand(const uint8_t* cmd, size_t cmdSize)
                break;\r
 \r
        case S2S_CMD_DEBUG:\r
+               if (debugTimerStarted == 0) {\r
+                       debugInit();\r
+               }\r
                debugCommand();\r
                break;\r
 \r
@@ -273,10 +314,12 @@ processCommand(const uint8_t* cmd, size_t cmdSize)
 \r
 void s2s_configPoll()\r
 {\r
+       s2s_spin_lock(&usbDevLock);\r
+\r
        if (!USBD_Composite_IsConfigured(&hUsbDeviceFS))\r
        {\r
                usbInEpState = USB_IDLE;\r
-               return;\r
+               goto out;\r
        }\r
 \r
        if (USBD_HID_IsReportReady(&hUsbDeviceFS))\r
@@ -322,6 +365,65 @@ void s2s_configPoll()
                break;\r
        }\r
 \r
+out:\r
+       s2s_spin_unlock(&usbDevLock);\r
+}\r
+\r
+void s2s_debugTimer()\r
+{\r
+       if (!USBD_Composite_IsConfigured(&hUsbDeviceFS))\r
+       {\r
+               usbInEpState = USB_IDLE;\r
+               return;\r
+       }\r
+\r
+       if (USBD_HID_IsReportReady(&hUsbDeviceFS))\r
+       {\r
+               uint8_t hidBuffer[USBHID_LEN];\r
+               int byteCount = USBD_HID_GetReport(&hUsbDeviceFS, hidBuffer, sizeof(hidBuffer));\r
+               hidPacket_recv(hidBuffer, byteCount);\r
+\r
+               size_t cmdSize;\r
+               const uint8_t* cmd = hidPacket_peekPacket(&cmdSize);\r
+               // This is called from an ISR, only process simple commands.\r
+               if (cmd && (cmdSize > 0))\r
+               {\r
+                       if (cmd[0] == S2S_CMD_DEBUG)\r
+                       {\r
+                               hidPacket_getPacket(&cmdSize);\r
+                               debugCommand();\r
+                       }\r
+                       else if (cmd[0] == S2S_CMD_PING)\r
+                       {\r
+                               hidPacket_getPacket(&cmdSize);\r
+                               pingCommand();\r
+                       }\r
+               }\r
+       }\r
+\r
+       switch (usbInEpState)\r
+       {\r
+               case USB_IDLE:\r
+               {\r
+                       uint8_t hidBuffer[USBHID_LEN];\r
+                       const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);\r
+\r
+                       if (nextChunk)\r
+                       {\r
+                               USBD_HID_SendReport (&hUsbDeviceFS, nextChunk, sizeof(hidBuffer));\r
+                               usbInEpState = USB_DATA_SENT;\r
+                       }\r
+               }\r
+               break;\r
+\r
+               case USB_DATA_SENT:\r
+                       if (!USBD_HID_IsBusy(&hUsbDeviceFS))\r
+                       {\r
+                               // Data accepted.\r
+                               usbInEpState = USB_IDLE;\r
+                       }\r
+                       break;\r
+       }\r
 }\r
 \r
 \r
index b35d5a8..85c4222 100644 (file)
@@ -111,6 +111,21 @@ hidPacket_getPacket(size_t* len)
        }
 }
 
+const uint8_t*
+hidPacket_peekPacket(size_t* len)
+{
+       if (rx.state == COMPLETE)
+       {
+               *len = rx.offset;
+               return rx.buffer;
+       }
+       else
+       {
+               *len = 0;
+               return NULL;
+       }
+}
+
 void hidPacket_send(const uint8_t* bytes, size_t len)
 {
        if (len <= sizeof(tx.buffer))
index eb776da..2bb7d78 100755 (executable)
@@ -117,6 +117,7 @@ void mainLoop()
                        // run if the SD card is present at startup.\r
                        // Don't use VBUS monitoring because that just tells us about\r
                        // power, which could be from a charger\r
+#if 0\r
                        if ((blockDev.state & DISK_PRESENT) &&\r
                                isUsbStarted &&\r
                                (scsiDev.cmdCount > 0) && // no need for speed without scsi\r
@@ -129,6 +130,7 @@ void mainLoop()
                                        isUsbStarted = 0;\r
                                }\r
                        }\r
+#endif\r
 \r
                        else if (!(blockDev.state & DISK_PRESENT) && !isUsbStarted)\r
                        {\r
index ad6e2f7..fe92d24 100755 (executable)
@@ -476,7 +476,17 @@ void scsiEnterPhase(int phase)
                                *SCSI_CTRL_TIMING = SCSI_SYNC_TIMING(scsiDev.target->syncPeriod);\r
                        }\r
 \r
-                       *SCSI_CTRL_SYNC_OFFSET = scsiDev.target->syncOffset;\r
+                       // See note 26 in SCSI 2 standard: SCSI 1 implementations may assume\r
+                       // "leading edge of the first REQ pulse beyond the REQ/ACK offset\r
+                       // agreement would not occur until after the trailing edge of the\r
+                       // last ACK pulse within the agreement."\r
+                       // We simply subtract 1 from the offset to meet this requirement.\r
+                       if (scsiDev.target->syncOffset >= 2)\r
+                       {\r
+                               *SCSI_CTRL_SYNC_OFFSET = scsiDev.target->syncOffset - 1;\r
+                       } else {\r
+                               *SCSI_CTRL_SYNC_OFFSET = scsiDev.target->syncOffset;\r
+                       }\r
                } else {\r
                        *SCSI_CTRL_SYNC_OFFSET = 0;\r
 \r
@@ -548,70 +558,6 @@ void scsiPhyReset()
        }\r
        #endif\r
 \r
-       // FPGA comms test code\r
-       #ifdef FPGA_TEST\r
-       while(1)\r
-       {\r
-               for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)\r
-               {\r
-                       scsiDev.data[j] = j;\r
-               }\r
-\r
-               if (!scsiPhyFifoEmpty())\r
-               {\r
-                       assertFail();\r
-               }\r
-\r
-               *SCSI_CTRL_PHASE = DATA_IN;\r
-               HAL_DMA_Start(\r
-                       &memToFSMC,\r
-                       (uint32_t) &scsiDev.data[0],\r
-                       (uint32_t) SCSI_FIFO_DATA,\r
-                       SCSI_FIFO_DEPTH / 4);\r
-\r
-               HAL_DMA_PollForTransfer(\r
-                       &memToFSMC,\r
-                       HAL_DMA_FULL_TRANSFER,\r
-                       0xffffffff);\r
-\r
-               if (!scsiPhyFifoFull())\r
-               {\r
-                       assertFail();\r
-               }\r
-\r
-               memset(&scsiDev.data[0], 0, SCSI_FIFO_DEPTH);\r
-\r
-               *SCSI_CTRL_PHASE = DATA_OUT;\r
-               HAL_DMA_Start(\r
-                       &fsmcToMem,\r
-                       (uint32_t) SCSI_FIFO_DATA,\r
-                       (uint32_t) &scsiDev.data[0],\r
-                       SCSI_FIFO_DEPTH / 2);\r
-\r
-               HAL_DMA_PollForTransfer(\r
-                       &fsmcToMem,\r
-                       HAL_DMA_FULL_TRANSFER,\r
-                       0xffffffff);\r
-\r
-               if (!scsiPhyFifoEmpty())\r
-               {\r
-                       assertFail();\r
-               }\r
-\r
-\r
-               for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)\r
-               {\r
-                       if (scsiDev.data[j] != (uint8_t) j)\r
-                       {\r
-                               assertFail();\r
-                       }\r
-               }\r
-\r
-               s2s_fpgaReset();\r
-\r
-       }\r
-       #endif\r
-\r
        #ifdef SCSI_FREQ_TEST\r
        while(1)\r
        {\r
@@ -740,6 +686,7 @@ void scsiPhyConfig()
 // 8 = CD error\r
 // 16 = IO error\r
 // 32 = other error\r
+// 64 = fpga comms error\r
 int scsiSelfTest()\r
 {\r
        if (scsiDev.phase != BUS_FREE)\r
@@ -841,6 +788,69 @@ int scsiSelfTest()
        }\r
        */\r
 \r
+\r
+       // FPGA comms test code\r
+       for(i = 0; i < 10000; ++i)\r
+       {\r
+               for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)\r
+               {\r
+                       scsiDev.data[j] = j;\r
+               }\r
+\r
+               if (!scsiPhyFifoEmpty())\r
+               {\r
+                       assertFail();\r
+               }\r
+\r
+               *SCSI_CTRL_PHASE = DATA_IN;\r
+               HAL_DMA_Start(\r
+                       &memToFSMC,\r
+                       (uint32_t) &scsiDev.data[0],\r
+                       (uint32_t) SCSI_FIFO_DATA,\r
+                       SCSI_FIFO_DEPTH / 4);\r
+\r
+               HAL_DMA_PollForTransfer(\r
+                       &memToFSMC,\r
+                       HAL_DMA_FULL_TRANSFER,\r
+                       0xffffffff);\r
+\r
+               if (!scsiPhyFifoFull())\r
+               {\r
+                       assertFail();\r
+               }\r
+\r
+               memset(&scsiDev.data[0], 0, SCSI_FIFO_DEPTH);\r
+\r
+               *SCSI_CTRL_PHASE = DATA_OUT;\r
+               HAL_DMA_Start(\r
+                       &fsmcToMem,\r
+                       (uint32_t) SCSI_FIFO_DATA,\r
+                       (uint32_t) &scsiDev.data[0],\r
+                       SCSI_FIFO_DEPTH / 2);\r
+\r
+               HAL_DMA_PollForTransfer(\r
+                       &fsmcToMem,\r
+                       HAL_DMA_FULL_TRANSFER,\r
+                       0xffffffff);\r
+\r
+               if (!scsiPhyFifoEmpty())\r
+               {\r
+                       assertFail();\r
+               }\r
+\r
+\r
+               for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)\r
+               {\r
+                       if (scsiDev.data[j] != (uint8_t) j)\r
+                       {\r
+                               result |= 64;\r
+                       }\r
+               }\r
+\r
+               s2s_fpgaReset();\r
+\r
+       }\r
+\r
        *SCSI_CTRL_BSY = 0;\r
        return result;\r
 }\r
diff --git a/src/firmware/spinlock.c b/src/firmware/spinlock.c
new file mode 100755 (executable)
index 0000000..a5d8414
--- /dev/null
@@ -0,0 +1,61 @@
+//     Copyright (C) 2016 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of SCSI2SD.
+//
+//     SCSI2SD is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+//
+//     SCSI2SD is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License
+//     along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "spinlock.h"
+
+int s2s_spin_trylock(s2s_lock_t* lock)
+{
+       if (__LDREXW(lock) == 0)
+       {
+               // Try to set lock
+               int status = __STREXW(1, lock);
+               if (status == 0)
+               {
+                       // got lock
+                       // Do not start any other memory access
+                       // until memory barrier is completed
+                       __DMB();
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+void s2s_spin_lock(s2s_lock_t* lock)
+{
+       int status = 0;
+       do
+       {
+               // Wait until lock is free
+               while (__LDREXW(lock) != 0);
+
+               // Try to set lock
+               status = __STREXW(1, lock);
+       } while (status!=0); //retry until lock successfully
+
+       // Do not start any other memory access
+       // until memory barrier is completed
+       __DMB();
+}
+
+void s2s_spin_unlock(s2s_lock_t* lock)
+{
+       // Ensure memory operations completed before releasing
+       __DMB();
+       *lock = 0;
+}
diff --git a/src/firmware/spinlock.h b/src/firmware/spinlock.h
new file mode 100755 (executable)
index 0000000..11c4dc2
--- /dev/null
@@ -0,0 +1,38 @@
+//     Copyright (C) 2016 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of SCSI2SD.
+//
+//     SCSI2SD is free software: you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation, either version 3 of the License, or
+//     (at your option) any later version.
+//
+//     SCSI2SD is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License
+//     along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.
+#ifndef S2S_SPINLOCK_H
+#define S2S_SPINLOCK_H
+
+#include "stm32f2xx.h"
+
+
+#define s2s_lock_t volatile uint32_t
+#define s2s_lock_init 0
+
+// Spinlock functions for Cortex-M3, based on ARM Application Note 321,
+// ARM Cortex-M Programming Guide to Memory Barrier Instructions, 4.19
+// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEJCHB.html
+//
+// s2s_spin_lock must NOT be used when mixing the main loop with a ISR, since
+// the main code will never get a chance to unlock while the ISR is active.
+// Use trylock in the ISR instead.
+
+int s2s_spin_trylock(s2s_lock_t* lock);
+void s2s_spin_lock(s2s_lock_t* lock);
+void s2s_spin_unlock(s2s_lock_t* lock);
+
+#endif