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
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 \
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 \
// 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);
#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
\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
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
break;\r
\r
case S2S_CMD_DEBUG:\r
+ if (debugTimerStarted == 0) {\r
+ debugInit();\r
+ }\r
debugCommand();\r
break;\r
\r
\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
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
}
}
+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))
// 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
isUsbStarted = 0;\r
}\r
}\r
+#endif\r
\r
else if (!(blockDev.state & DISK_PRESENT) && !isUsbStarted)\r
{\r
*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
}\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
// 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
}\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
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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