Parcourir la source

Synch transfer fix

Michael McMaster il y a 8 ans
Parent
commit
357164f74c

+ 5 - 0
lib/SCSI2SD/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 \

+ 4 - 0
lib/SCSI2SD/include/hidpacket.h

@@ -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);

BIN
lib/SCSI2SD/rtl/fpga_bitmap.o


+ 103 - 1
lib/SCSI2SD/src/firmware/config.c

@@ -25,6 +25,7 @@
 #include "trace.h"
 #include "bootloader.h"
 #include "bsp.h"
+#include "spinlock.h"
 
 #include "../../include/scsi2sd.h"
 #include "../../include/hidpacket.h"
@@ -64,6 +65,25 @@ enum USB_STATE
 
 static int usbInEpState;
 
+static void s2s_debugTimer();
+
+// Debug timer to log via USB.
+// Timer 6 & 7 is a simple counter with no external IO supported.
+static s2s_lock_t usbDevLock = s2s_lock_init;
+TIM_HandleTypeDef htim7;
+static int debugTimerStarted = 0;
+void TIM7_IRQHandler()
+{
+	HAL_TIM_IRQHandler(&htim7);
+}
+void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
+{
+	if (s2s_spin_trylock(&usbDevLock)) {
+		s2s_debugTimer();
+		s2s_spin_unlock(&usbDevLock);
+	}
+}
+
 void s2s_configInit(S2S_BoardCfg* config)
 {
 
@@ -108,7 +128,25 @@ void s2s_configInit(S2S_BoardCfg* config)
 			config->flags6 = S2S_CFG_ENABLE_TERMINATOR;
 		}
 	}
+}
 
+static void debugInit(void)
+{
+	if (debugTimerStarted == 1) return;
+
+	debugTimerStarted = 1;
+	// 10ms debug timer to capture logs over USB
+	__TIM7_CLK_ENABLE();
+	htim7.Instance = TIM7;
+	htim7.Init.Prescaler = 10800 - 1; // 16bit. 108MHz down to 10KHz
+	htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
+	htim7.Init.Period = 100 - 1; // 16bit. 10KHz down to 10ms.
+	htim7.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
+	HAL_TIM_Base_Init(&htim7);
+	HAL_TIM_Base_Start_IT(&htim7);
+
+	HAL_NVIC_SetPriority(TIM7_IRQn, 10, 0);
+	HAL_NVIC_EnableIRQ(TIM7_IRQn);
 }
 
 
@@ -262,6 +300,9 @@ processCommand(const uint8_t* cmd, size_t cmdSize)
 		break;
 
 	case S2S_CMD_DEBUG:
+		if (debugTimerStarted == 0) {
+			debugInit();
+		}
 		debugCommand();
 		break;
 
@@ -273,10 +314,12 @@ processCommand(const uint8_t* cmd, size_t cmdSize)
 
 void s2s_configPoll()
 {
+	s2s_spin_lock(&usbDevLock);
+
 	if (!USBD_Composite_IsConfigured(&hUsbDeviceFS))
 	{
 		usbInEpState = USB_IDLE;
-		return;
+		goto out;
 	}
 
 	if (USBD_HID_IsReportReady(&hUsbDeviceFS))
@@ -322,6 +365,65 @@ void s2s_configPoll()
 		break;
 	}
 
+out:
+	s2s_spin_unlock(&usbDevLock);
+}
+
+void s2s_debugTimer()
+{
+	if (!USBD_Composite_IsConfigured(&hUsbDeviceFS))
+	{
+		usbInEpState = USB_IDLE;
+		return;
+	}
+
+	if (USBD_HID_IsReportReady(&hUsbDeviceFS))
+	{
+		uint8_t hidBuffer[USBHID_LEN];
+		int byteCount = USBD_HID_GetReport(&hUsbDeviceFS, hidBuffer, sizeof(hidBuffer));
+		hidPacket_recv(hidBuffer, byteCount);
+
+		size_t cmdSize;
+		const uint8_t* cmd = hidPacket_peekPacket(&cmdSize);
+		// This is called from an ISR, only process simple commands.
+		if (cmd && (cmdSize > 0))
+		{
+			if (cmd[0] == S2S_CMD_DEBUG)
+			{
+				hidPacket_getPacket(&cmdSize);
+				debugCommand();
+			}
+			else if (cmd[0] == S2S_CMD_PING)
+			{
+				hidPacket_getPacket(&cmdSize);
+				pingCommand();
+			}
+		}
+	}
+
+	switch (usbInEpState)
+	{
+		case USB_IDLE:
+		{
+			uint8_t hidBuffer[USBHID_LEN];
+			const uint8_t* nextChunk = hidPacket_getHIDBytes(hidBuffer);
+
+			if (nextChunk)
+			{
+				USBD_HID_SendReport (&hUsbDeviceFS, nextChunk, sizeof(hidBuffer));
+				usbInEpState = USB_DATA_SENT;
+			}
+		}
+		break;
+
+		case USB_DATA_SENT:
+			if (!USBD_HID_IsBusy(&hUsbDeviceFS))
+			{
+				// Data accepted.
+				usbInEpState = USB_IDLE;
+			}
+			break;
+	}
 }
 
 

+ 15 - 0
lib/SCSI2SD/src/firmware/hidpacket.c

@@ -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))

+ 2 - 0
lib/SCSI2SD/src/firmware/main.c

@@ -117,6 +117,7 @@ void mainLoop()
 			// run if the SD card is present at startup.
 			// Don't use VBUS monitoring because that just tells us about
 			// power, which could be from a charger
+#if 0
 			if ((blockDev.state & DISK_PRESENT) &&
 				isUsbStarted &&
 				(scsiDev.cmdCount > 0) && // no need for speed without scsi
@@ -129,6 +130,7 @@ void mainLoop()
 					isUsbStarted = 0;
 				}
 			}
+#endif
 
 			else if (!(blockDev.state & DISK_PRESENT) && !isUsbStarted)
 			{

+ 75 - 65
lib/SCSI2SD/src/firmware/scsiPhy.c

@@ -476,7 +476,17 @@ void scsiEnterPhase(int phase)
 				*SCSI_CTRL_TIMING = SCSI_SYNC_TIMING(scsiDev.target->syncPeriod);
 			}
 
-			*SCSI_CTRL_SYNC_OFFSET = scsiDev.target->syncOffset;
+			// See note 26 in SCSI 2 standard: SCSI 1 implementations may assume
+			// "leading edge of the first REQ pulse beyond the REQ/ACK offset
+			// agreement would not occur until after the trailing edge of the
+			// last ACK pulse within the agreement."
+			// We simply subtract 1 from the offset to meet this requirement.
+			if (scsiDev.target->syncOffset >= 2)
+			{
+				*SCSI_CTRL_SYNC_OFFSET = scsiDev.target->syncOffset - 1;
+			} else {
+				*SCSI_CTRL_SYNC_OFFSET = scsiDev.target->syncOffset;
+			}
 		} else {
 			*SCSI_CTRL_SYNC_OFFSET = 0;
 
@@ -548,70 +558,6 @@ void scsiPhyReset()
 	}
 	#endif
 
-	// FPGA comms test code
-	#ifdef FPGA_TEST
-	while(1)
-	{
-		for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)
-		{
-			scsiDev.data[j] = j;
-		}
-
-		if (!scsiPhyFifoEmpty())
-		{
-			assertFail();
-		}
-
-		*SCSI_CTRL_PHASE = DATA_IN;
-		HAL_DMA_Start(
-			&memToFSMC,
-			(uint32_t) &scsiDev.data[0],
-			(uint32_t) SCSI_FIFO_DATA,
-			SCSI_FIFO_DEPTH / 4);
-
-		HAL_DMA_PollForTransfer(
-			&memToFSMC,
-			HAL_DMA_FULL_TRANSFER,
-			0xffffffff);
-
-		if (!scsiPhyFifoFull())
-		{
-			assertFail();
-		}
-
-		memset(&scsiDev.data[0], 0, SCSI_FIFO_DEPTH);
-
-		*SCSI_CTRL_PHASE = DATA_OUT;
-		HAL_DMA_Start(
-			&fsmcToMem,
-			(uint32_t) SCSI_FIFO_DATA,
-			(uint32_t) &scsiDev.data[0],
-			SCSI_FIFO_DEPTH / 2);
-
-		HAL_DMA_PollForTransfer(
-			&fsmcToMem,
-			HAL_DMA_FULL_TRANSFER,
-			0xffffffff);
-
-		if (!scsiPhyFifoEmpty())
-		{
-			assertFail();
-		}
-
-
-		for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)
-		{
-			if (scsiDev.data[j] != (uint8_t) j)
-			{
-				assertFail();
-			}
-		}
-
-		s2s_fpgaReset();
-
-	}
-	#endif
-
 	#ifdef SCSI_FREQ_TEST
 	while(1)
 	{
@@ -740,6 +686,7 @@ void scsiPhyConfig()
 // 8 = CD error
 // 16 = IO error
 // 32 = other error
+// 64 = fpga comms error
 int scsiSelfTest()
 {
 	if (scsiDev.phase != BUS_FREE)
@@ -841,6 +788,69 @@ int scsiSelfTest()
 	}
 	*/
 
+
+	// FPGA comms test code
+	for(i = 0; i < 10000; ++i)
+	{
+		for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)
+		{
+			scsiDev.data[j] = j;
+		}
+
+		if (!scsiPhyFifoEmpty())
+		{
+			assertFail();
+		}
+
+		*SCSI_CTRL_PHASE = DATA_IN;
+		HAL_DMA_Start(
+			&memToFSMC,
+			(uint32_t) &scsiDev.data[0],
+			(uint32_t) SCSI_FIFO_DATA,
+			SCSI_FIFO_DEPTH / 4);
+
+		HAL_DMA_PollForTransfer(
+			&memToFSMC,
+			HAL_DMA_FULL_TRANSFER,
+			0xffffffff);
+
+		if (!scsiPhyFifoFull())
+		{
+			assertFail();
+		}
+
+		memset(&scsiDev.data[0], 0, SCSI_FIFO_DEPTH);
+
+		*SCSI_CTRL_PHASE = DATA_OUT;
+		HAL_DMA_Start(
+			&fsmcToMem,
+			(uint32_t) SCSI_FIFO_DATA,
+			(uint32_t) &scsiDev.data[0],
+			SCSI_FIFO_DEPTH / 2);
+
+		HAL_DMA_PollForTransfer(
+			&fsmcToMem,
+			HAL_DMA_FULL_TRANSFER,
+			0xffffffff);
+
+		if (!scsiPhyFifoEmpty())
+		{
+			assertFail();
+		}
+
+
+		for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)
+		{
+			if (scsiDev.data[j] != (uint8_t) j)
+			{
+				result |= 64;
+			}
+		}
+
+		s2s_fpgaReset();
+
+	}
+
 	*SCSI_CTRL_BSY = 0;
 	return result;
 }

+ 61 - 0
lib/SCSI2SD/src/firmware/spinlock.c

@@ -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;
+}

+ 38 - 0
lib/SCSI2SD/src/firmware/spinlock.h

@@ -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