From 2ba53be02febfdc02de14d58c1c84bebf5df0380 Mon Sep 17 00:00:00 2001 From: Michael McMaster Date: Sun, 22 Jan 2017 17:40:13 +1000 Subject: [PATCH] Synch transfer fix --- Makefile | 5 ++ include/hidpacket.h | 4 ++ rtl/fpga_bitmap.o | Bin 32724 -> 32724 bytes src/firmware/config.c | 104 ++++++++++++++++++++++++++++- src/firmware/hidpacket.c | 15 +++++ src/firmware/main.c | 2 + src/firmware/scsiPhy.c | 140 +++++++++++++++++++++------------------ src/firmware/spinlock.c | 61 +++++++++++++++++ src/firmware/spinlock.h | 38 +++++++++++ 9 files changed, 303 insertions(+), 66 deletions(-) create mode 100755 src/firmware/spinlock.c create mode 100755 src/firmware/spinlock.h diff --git a/Makefile b/Makefile index b3fa3f64..b90b51a3 100644 --- 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 \ diff --git a/include/hidpacket.h b/include/hidpacket.h index 9beb0022..384956f8 100644 --- a/include/hidpacket.h +++ b/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); diff --git a/rtl/fpga_bitmap.o b/rtl/fpga_bitmap.o index 77e590a862a093f08770d5c62bcc2ed6da5057d2..c4ec7a4843862cb50b727468363ddda3c6401f05 100644 GIT binary patch literal 32724 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 literal 32724 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 diff --git a/src/firmware/config.c b/src/firmware/config.c index 127ca1b5..9bb8b113 100755 --- a/src/firmware/config.c +++ b/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; + } } diff --git a/src/firmware/hidpacket.c b/src/firmware/hidpacket.c index b35d5a8f..85c42220 100644 --- a/src/firmware/hidpacket.c +++ b/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)) diff --git a/src/firmware/main.c b/src/firmware/main.c index eb776dab..2bb7d78f 100755 --- a/src/firmware/main.c +++ b/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) { diff --git a/src/firmware/scsiPhy.c b/src/firmware/scsiPhy.c index ad6e2f79..fe92d247 100755 --- a/src/firmware/scsiPhy.c +++ b/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; } diff --git a/src/firmware/spinlock.c b/src/firmware/spinlock.c new file mode 100755 index 00000000..a5d84145 --- /dev/null +++ b/src/firmware/spinlock.c @@ -0,0 +1,61 @@ +// Copyright (C) 2016 Michael McMaster +// +// 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 . + +#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 index 00000000..11c4dc21 --- /dev/null +++ b/src/firmware/spinlock.h @@ -0,0 +1,38 @@ +// Copyright (C) 2016 Michael McMaster +// +// 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 . +#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 -- 2.38.5