From 6534c041b10accfffc45799d4020e6bbf0c6ac6c Mon Sep 17 00:00:00 2001 From: Michael McMaster Date: Sat, 10 Sep 2016 19:22:36 +1000 Subject: [PATCH] Stability fixes and SD UHS-I Speed class 3 fix. --- CHANGELOG | 4 + .../Src/stm32f2xx_hal_sd.c | 75 ++++++++++------- STM32CubeMX/SCSI2SD-V6/SCSI2SD-V6.ioc | 43 +++++----- STM32CubeMX/SCSI2SD-V6/Src/fsmc.c | 20 +++-- STM32CubeMX/SCSI2SD-V6/Src/main.c | 7 +- STM32CubeMX/SCSI2SD-V6/Src/sdio.c | 14 +++- rtl/fpga_bitmap.o | Bin 32724 -> 32724 bytes src/firmware/config.c | 2 +- src/firmware/disk.c | 60 ++++++++++---- src/firmware/scsiPhy.c | 77 ++++++++---------- src/firmware/scsiPhy.h | 6 ++ src/firmware/sd.c | 25 ++++-- src/firmware/sd.h | 2 +- src/scsi2sd-util6/SCSI2SD_HID.cc | 2 +- src/scsi2sd-util6/scsi2sd-util.cc | 2 +- 15 files changed, 201 insertions(+), 138 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b11680d5..6165e3c4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +201609XX 6.0.10 + - Fixed write issue with UHS-I Speed Class 3 SD cards. + - More stability bug fixes + 20160827 6.0.8 - Fixed "protocol error" issues when saving configuration to SD cards. - Synchronous transfers supported ! 5MB/s and 10MB/s supported. diff --git a/STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sd.c b/STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sd.c index 8511aa7b..9d2062dc 100755 --- a/STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sd.c +++ b/STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sd.c @@ -892,7 +892,7 @@ HAL_SD_ErrorTypedef HAL_SD_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint32_t *pRead SDIO_IT_STBITERR)); /* Enable SDIO DMA transfer */ - __HAL_SD_SDIO_DMA_ENABLE(); + // MM disabled, as this fails on fast cards. __HAL_SD_SDIO_DMA_ENABLE(); /* Configure DMA user callbacks */ hsd->hdmarx->XferCpltCallback = SD_DMA_RxCplt; @@ -901,26 +901,29 @@ HAL_SD_ErrorTypedef HAL_SD_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint32_t *pRead /* Enable the DMA Stream */ HAL_DMA_Start_IT(hsd->hdmarx, (uint32_t)&hsd->Instance->FIFO, (uint32_t)pReadBuffer, (uint32_t)(BlockSize * NumberOfBlocks)/4); + sdio_cmdinitstructure.Response = SDIO_RESPONSE_SHORT; + sdio_cmdinitstructure.WaitForInterrupt = SDIO_WAIT_NO; + sdio_cmdinitstructure.CPSM = SDIO_CPSM_ENABLE; + if (hsd->CardType == HIGH_CAPACITY_SD_CARD) { BlockSize = 512; ReadAddr /= 512; - } + } else { - /* Set Block Size for Card */ - sdio_cmdinitstructure.Argument = (uint32_t)BlockSize; - sdio_cmdinitstructure.CmdIndex = SD_CMD_SET_BLOCKLEN; - sdio_cmdinitstructure.Response = SDIO_RESPONSE_SHORT; - sdio_cmdinitstructure.WaitForInterrupt = SDIO_WAIT_NO; - sdio_cmdinitstructure.CPSM = SDIO_CPSM_ENABLE; - SDIO_SendCommand(hsd->Instance, &sdio_cmdinitstructure); + /* Set Block Size for Card */ + sdio_cmdinitstructure.Argument = (uint32_t)BlockSize; + sdio_cmdinitstructure.CmdIndex = SD_CMD_SET_BLOCKLEN; + + SDIO_SendCommand(hsd->Instance, &sdio_cmdinitstructure); - /* Check for error conditions */ - errorstate = SD_CmdResp1Error(hsd, SD_CMD_SET_BLOCKLEN); + /* Check for error conditions */ + errorstate = SD_CmdResp1Error(hsd, SD_CMD_SET_BLOCKLEN); - if (errorstate != SD_OK) - { - return errorstate; + if (errorstate != SD_OK) + { + return errorstate; + } } /* Configure the SD DPSM (Data Path State Machine) */ @@ -930,6 +933,11 @@ HAL_SD_ErrorTypedef HAL_SD_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint32_t *pRead sdio_datainitstructure.TransferDir = SDIO_TRANSFER_DIR_TO_SDIO; sdio_datainitstructure.TransferMode = SDIO_TRANSFER_MODE_BLOCK; sdio_datainitstructure.DPSM = SDIO_DPSM_ENABLE; + + // We cannot enable DMA too early on UHS-I class 3 SD cards, or else the + // data is just discarded before the dpsm is started. + __HAL_SD_SDIO_DMA_ENABLE(); + SDIO_DataConfig(hsd->Instance, &sdio_datainitstructure); /* Check number of blocks command */ @@ -1016,28 +1024,30 @@ HAL_SD_ErrorTypedef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint32_t *pWri HAL_DMA_Start_IT(hsd->hdmatx, (uint32_t)pWriteBuffer, (uint32_t)&hsd->Instance->FIFO, (uint32_t)(BlockSize * NumberOfBlocks)/4); /* Enable SDIO DMA transfer */ - __HAL_SD_SDIO_DMA_ENABLE(); + // MM disabled, as this fails on fast cards. __HAL_SD_SDIO_DMA_ENABLE(); + + sdio_cmdinitstructure.Response = SDIO_RESPONSE_SHORT; + sdio_cmdinitstructure.WaitForInterrupt = SDIO_WAIT_NO; + sdio_cmdinitstructure.CPSM = SDIO_CPSM_ENABLE; if (hsd->CardType == HIGH_CAPACITY_SD_CARD) { BlockSize = 512; WriteAddr /= 512; - } + } else { + /* Set Block Size for Card */ + sdio_cmdinitstructure.Argument = (uint32_t)BlockSize; + sdio_cmdinitstructure.CmdIndex = SD_CMD_SET_BLOCKLEN; - /* Set Block Size for Card */ - sdio_cmdinitstructure.Argument = (uint32_t)BlockSize; - sdio_cmdinitstructure.CmdIndex = SD_CMD_SET_BLOCKLEN; - sdio_cmdinitstructure.Response = SDIO_RESPONSE_SHORT; - sdio_cmdinitstructure.WaitForInterrupt = SDIO_WAIT_NO; - sdio_cmdinitstructure.CPSM = SDIO_CPSM_ENABLE; - SDIO_SendCommand(hsd->Instance, &sdio_cmdinitstructure); + SDIO_SendCommand(hsd->Instance, &sdio_cmdinitstructure); - /* Check for error conditions */ - errorstate = SD_CmdResp1Error(hsd, SD_CMD_SET_BLOCKLEN); + /* Check for error conditions */ + errorstate = SD_CmdResp1Error(hsd, SD_CMD_SET_BLOCKLEN); - if (errorstate != SD_OK) - { - return errorstate; + if (errorstate != SD_OK) + { + return errorstate; + } } /* Check number of blocks command */ @@ -1049,7 +1059,6 @@ HAL_SD_ErrorTypedef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint32_t *pWri else { /* MM: Prepare for write */ - /* Set Block Size for Card */ sdio_cmdinitstructure.Argument = (uint32_t)(hsd->RCA << 16); sdio_cmdinitstructure.CmdIndex = SD_CMD_APP_CMD; SDIO_SendCommand(hsd->Instance, &sdio_cmdinitstructure); @@ -1072,10 +1081,11 @@ HAL_SD_ErrorTypedef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint32_t *pWri /* Send CMD25 WRITE_MULT_BLOCK with argument data address */ sdio_cmdinitstructure.CmdIndex = SD_CMD_WRITE_MULT_BLOCK; } - + sdio_cmdinitstructure.Argument = (uint32_t)WriteAddr; SDIO_SendCommand(hsd->Instance, &sdio_cmdinitstructure); + /* Check for error conditions */ if(NumberOfBlocks > 1) { @@ -1098,6 +1108,11 @@ HAL_SD_ErrorTypedef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint32_t *pWri sdio_datainitstructure.TransferDir = SDIO_TRANSFER_DIR_TO_CARD; sdio_datainitstructure.TransferMode = SDIO_TRANSFER_MODE_BLOCK; sdio_datainitstructure.DPSM = SDIO_DPSM_ENABLE; + + // We cannot enable DMA too early on UHS-I class 3 SD cards, or else the + // data is just discarded before the dpsm is started. + __HAL_SD_SDIO_DMA_ENABLE(); + SDIO_DataConfig(hsd->Instance, &sdio_datainitstructure); hsd->SdTransferErr = errorstate; diff --git a/STM32CubeMX/SCSI2SD-V6/SCSI2SD-V6.ioc b/STM32CubeMX/SCSI2SD-V6/SCSI2SD-V6.ioc index ed61a351..056fddb8 100755 --- a/STM32CubeMX/SCSI2SD-V6/SCSI2SD-V6.ioc +++ b/STM32CubeMX/SCSI2SD-V6/SCSI2SD-V6.ioc @@ -455,38 +455,37 @@ ProjectManager.ProjectName=SCSI2SD-V6 ProjectManager.TargetToolchain=TrueSTUDIO ProjectManager.ToolChainLocation= RCC.48MHZClocksFreq_Value=48000000 -RCC.AHBFreq_Value=120000000 +RCC.AHBFreq_Value=96000000 RCC.APB1CLKDivider=RCC_HCLK_DIV4 -RCC.APB1Freq_Value=30000000 -RCC.APB1TimFreq_Value=60000000 +RCC.APB1Freq_Value=24000000 +RCC.APB1TimFreq_Value=48000000 RCC.APB2CLKDivider=RCC_HCLK_DIV2 -RCC.APB2Freq_Value=60000000 -RCC.APB2TimFreq_Value=120000000 -RCC.CortexFreq_Value=120000000 -RCC.EthernetFreq_Value=120000000 -RCC.FCLKCortexFreq_Value=120000000 +RCC.APB2Freq_Value=48000000 +RCC.APB2TimFreq_Value=96000000 +RCC.CortexFreq_Value=96000000 +RCC.EthernetFreq_Value=96000000 +RCC.FCLKCortexFreq_Value=96000000 RCC.FamilyName=M -RCC.HCLKFreq_Value=120000000 +RCC.HCLKFreq_Value=96000000 RCC.HSE_VALUE=20000000 RCC.HSI_VALUE=16000000 -RCC.I2SClocksFreq_Value=120000000 -RCC.IPParameters=FamilyName,LSE_VALUE,HSI_VALUE,SYSCLKFreq_VALUE,AHBFreq_Value,CortexFreq_Value,APB1Freq_Value,APB2Freq_Value,HSE_VALUE,RTCHSEDivFreq_Value,LSI_VALUE,RTCFreq_Value,VCOInputFreq_Value,VCOOutputFreq_Value,PLLCLKFreq_Value,48MHZClocksFreq_Value,VCOI2SOutputFreq_Value,VcooutputI2S,I2SClocksFreq_Value,RCC_MCO1Source,SYSCLKSource,HCLKFreq_Value,FCLKCortexFreq_Value,APB1TimFreq_Value,APB2TimFreq_Value,EthernetFreq_Value,MCO2PinFreq_Value,APB1CLKDivider,APB2CLKDivider,MCO1PinFreq_Value,PLLQ,RCC_MCODiv1 +RCC.I2SClocksFreq_Value=96000000 +RCC.IPParameters=FamilyName,LSE_VALUE,HSI_VALUE,SYSCLKFreq_VALUE,AHBFreq_Value,CortexFreq_Value,APB1Freq_Value,APB2Freq_Value,HSE_VALUE,RTCHSEDivFreq_Value,LSI_VALUE,RTCFreq_Value,VCOInputFreq_Value,VCOOutputFreq_Value,PLLCLKFreq_Value,48MHZClocksFreq_Value,VCOI2SOutputFreq_Value,VcooutputI2S,I2SClocksFreq_Value,RCC_MCO1Source,HCLKFreq_Value,FCLKCortexFreq_Value,APB1TimFreq_Value,APB2TimFreq_Value,EthernetFreq_Value,MCO2PinFreq_Value,APB1CLKDivider,APB2CLKDivider,MCO1PinFreq_Value,SYSCLKSource,PLLM RCC.LSE_VALUE=32768 RCC.LSI_VALUE=32000 -RCC.MCO1PinFreq_Value=60000000 -RCC.MCO2PinFreq_Value=120000000 -RCC.PLLCLKFreq_Value=120000000 -RCC.PLLQ=5 +RCC.MCO1PinFreq_Value=96000000 +RCC.MCO2PinFreq_Value=96000000 +RCC.PLLCLKFreq_Value=96000000 +RCC.PLLM=20 RCC.RCC_MCO1Source=RCC_MCO1SOURCE_PLLCLK -RCC.RCC_MCODiv1=RCC_MCODIV_2 RCC.RTCFreq_Value=32000 RCC.RTCHSEDivFreq_Value=10000000 -RCC.SYSCLKFreq_VALUE=120000000 +RCC.SYSCLKFreq_VALUE=96000000 RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK -RCC.VCOI2SOutputFreq_Value=240000000 -RCC.VCOInputFreq_Value=1250000 -RCC.VCOOutputFreq_Value=240000000 -RCC.VcooutputI2S=120000000 +RCC.VCOI2SOutputFreq_Value=192000000 +RCC.VCOInputFreq_Value=1000000 +RCC.VCOOutputFreq_Value=192000000 +RCC.VcooutputI2S=96000000 SDIO.BusWide=SDIO_BUS_WIDE_1B SDIO.IPParameters=BusWide,WideMode SDIO.WideMode=SDIO_BUS_WIDE_4B @@ -499,7 +498,7 @@ SH.S_TIM4_CH1.ConfNb=1 SPI1.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16 SPI1.CLKPhase=SPI_PHASE_2EDGE SPI1.CLKPolarity=SPI_POLARITY_HIGH -SPI1.CalculateBaudRate=3.75 MBits/s +SPI1.CalculateBaudRate=3.0 MBits/s SPI1.IPParameters=Mode,CalculateBaudRate,BaudRatePrescaler,CLKPolarity,CLKPhase SPI1.Mode=SPI_MODE_MASTER USB_DEVICE.CLASS_NAME-HID_FS=HID diff --git a/STM32CubeMX/SCSI2SD-V6/Src/fsmc.c b/STM32CubeMX/SCSI2SD-V6/Src/fsmc.c index de40c343..d01e9c33 100755 --- a/STM32CubeMX/SCSI2SD-V6/Src/fsmc.c +++ b/STM32CubeMX/SCSI2SD-V6/Src/fsmc.c @@ -67,19 +67,21 @@ void MX_FSMC_Init(void) hsram1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE; /* Timing */ -/* + // 1 clock to read the address, but since the signals aren't stable + // at 96MHz we wait two more clocks Timing.AddressSetupTime = 3; - Timing.AddressHoldTime = 2; + Timing.AddressHoldTime = 1; + Timing.DataSetupTime = 5; -*/ + // +1 clock hold time, +2 clock to let the bus settle (unstable at 96MHz) + // +1 clock to process read, 1 clock to output - Timing.AddressSetupTime = 2; - Timing.AddressHoldTime = 1; - Timing.DataSetupTime = 4; + // Allow a clock for us to release signals, or else we'll read + // our own output data as an address. + Timing.BusTurnAroundDuration = 1; - Timing.BusTurnAroundDuration = 0; - Timing.CLKDivision = 16; - Timing.DataLatency = 17; + Timing.CLKDivision = 16; // Ignored for async + Timing.DataLatency = 17; // Ignored for async Timing.AccessMode = FSMC_ACCESS_MODE_A; /* ExtTiming */ diff --git a/STM32CubeMX/SCSI2SD-V6/Src/main.c b/STM32CubeMX/SCSI2SD-V6/Src/main.c index bf352ace..45f98d9c 100755 --- a/STM32CubeMX/SCSI2SD-V6/Src/main.c +++ b/STM32CubeMX/SCSI2SD-V6/Src/main.c @@ -128,10 +128,11 @@ void SystemClock_Config(void) RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; - RCC_OscInitStruct.PLL.PLLM = 16; + + RCC_OscInitStruct.PLL.PLLM = 20; RCC_OscInitStruct.PLL.PLLN = 192; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; - RCC_OscInitStruct.PLL.PLLQ = 5; + RCC_OscInitStruct.PLL.PLLQ = 4; HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1 @@ -142,7 +143,7 @@ void SystemClock_Config(void) RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3); - HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_PLLCLK, RCC_MCODIV_2); + HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_PLLCLK, RCC_MCODIV_1); HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); diff --git a/STM32CubeMX/SCSI2SD-V6/Src/sdio.c b/STM32CubeMX/SCSI2SD-V6/Src/sdio.c index d1bb9dca..678a9e7a 100755 --- a/STM32CubeMX/SCSI2SD-V6/Src/sdio.c +++ b/STM32CubeMX/SCSI2SD-V6/Src/sdio.c @@ -81,18 +81,23 @@ void HAL_SD_MspInit(SD_HandleTypeDef* hsd) PC12 ------> SDIO_CK PD2 ------> SDIO_CMD */ - GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 - |GPIO_PIN_12; + GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - //GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Pull = GPIO_PULLUP; // MM GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = GPIO_AF12_SDIO; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + // No pullup on CMD + GPIO_InitStruct.Pin = GPIO_PIN_12; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF12_SDIO; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + GPIO_InitStruct.Pin = GPIO_PIN_2; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - //GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Pull = GPIO_PULLUP; // MM GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = GPIO_AF12_SDIO; @@ -135,6 +140,7 @@ void HAL_SD_MspInit(SD_HandleTypeDef* hsd) __HAL_LINKDMA(hsd,hdmarx,hdma_sdio_rx); /* Peripheral interrupt init*/ + HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); HAL_NVIC_SetPriority(SDIO_IRQn, 0, 0); HAL_NVIC_EnableIRQ(SDIO_IRQn); /* USER CODE BEGIN SDIO_MspInit 1 */ diff --git a/rtl/fpga_bitmap.o b/rtl/fpga_bitmap.o index 56551b363f91bcb0323785fb79e5b6eab51ae5ad..5d95ad379864f3ded3819c81f19c54de08a936d5 100644 GIT binary patch literal 32724 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 E01@%)_W%F@ diff --git a/src/firmware/config.c b/src/firmware/config.c index 7acb0831..6430d573 100755 --- a/src/firmware/config.c +++ b/src/firmware/config.c @@ -37,7 +37,7 @@ #include -static const uint16_t FIRMWARE_VERSION = 0x0608; +static const uint16_t FIRMWARE_VERSION = 0x060A; // 1 flash row static const uint8_t DEFAULT_CONFIG[128] = diff --git a/src/firmware/disk.c b/src/firmware/disk.c index 2e69d481..be912166 100755 --- a/src/firmware/disk.c +++ b/src/firmware/disk.c @@ -544,19 +544,25 @@ void scsiDiskPoll() transfer.lba); const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector); - int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE; + const int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE; int prep = 0; int i = 0; - int scsiActive = 0; + int scsiActive __attribute__((unused)) = 0; // unused if DMA disabled int sdActive = 0; while ((i < totalSDSectors) && likely(scsiDev.phase == DATA_IN) && likely(!scsiDev.resetFlag)) { - if (sdActive && sdReadDMAPoll()) + int completedDmaSectors; + if (sdActive && (completedDmaSectors = sdReadDMAPoll(sdActive))) { - prep += sdActive; - sdActive = 0; + prep += completedDmaSectors; + sdActive -= completedDmaSectors; + } else if (sdActive > 1) { + if (scsiDev.data[SD_SECTOR_SIZE * (prep % buffers) + 511] != 0x33) { + prep += 1; + sdActive -= 1; + } } if (!sdActive && @@ -566,25 +572,27 @@ void scsiDiskPoll() // Start an SD transfer if we have space. uint32_t startBuffer = prep % buffers; uint32_t sectors = totalSDSectors - prep; -#if 0 - if (!scsiActive && prep == i) - { - sectors = 1; // We need to get some data to send ASAP ! - } - else -#endif + + uint32_t freeBuffers = buffers - (prep - i); + + uint32_t contiguousBuffers = buffers - startBuffer; + freeBuffers = freeBuffers < contiguousBuffers + ? freeBuffers : contiguousBuffers; + sectors = sectors < freeBuffers ? sectors : freeBuffers; + + if (sectors > 128) sectors = 128; // 65536 DMA limit !! + + for (int dodgy = 0; dodgy < sectors; dodgy++) { - uint32_t freeBuffers = buffers - (prep - i); - uint32_t contiguousBuffers = buffers - startBuffer; - freeBuffers = freeBuffers < contiguousBuffers - ? freeBuffers : contiguousBuffers; - sectors = sectors < freeBuffers ? sectors : freeBuffers; + scsiDev.data[SD_SECTOR_SIZE * (startBuffer + dodgy) + 511] = 0x33; } + sdReadDMA(sdLBA + prep, sectors, &scsiDev.data[SD_SECTOR_SIZE * startBuffer]); sdActive = sectors; } +#ifdef SCSI_FSMC_DMA if (scsiActive && scsiPhyComplete() && scsiWriteDMAPoll()) { scsiActive = 0; @@ -602,6 +610,24 @@ void scsiDiskPoll() scsiWriteDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)], dmaBytes); scsiActive = 1; } +#else + if ((prep - i) > 0) + { + int dmaBytes = SD_SECTOR_SIZE; + if ((i % sdPerScsi) == (sdPerScsi - 1)) + { + dmaBytes = bytesPerSector % SD_SECTOR_SIZE; + if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE; + } + for (int k = 0; k < dmaBytes; ++k) + { + scsiPhyTx(scsiDev.data[SD_SECTOR_SIZE * (i % buffers) + k]); + } + i++; + while (!scsiPhyComplete() && !scsiDev.resetFlag) {} + scsiPhyFifoFlip(); + } +#endif } // We've finished transferring the data to the FPGA, now wait until it's diff --git a/src/firmware/scsiPhy.c b/src/firmware/scsiPhy.c index f4241573..ba71f659 100755 --- a/src/firmware/scsiPhy.c +++ b/src/firmware/scsiPhy.c @@ -29,23 +29,26 @@ #include // 5MB/s -// Assumes a 60MHz fpga clock. -// 7:6 Hold count, 45ns -// 5:3 Assertion count, 90ns +// Assumes a 96MHz fpga clock. // 2:0 Deskew count, 55ns -#define SCSI_DEFAULT_TIMING ((0x3 << 6) | (0x6 << 3) | 0x4) +// 6:4 Hold count, 53ns +// 3:0 Assertion count, 80ns +#define SCSI_DEFAULT_DESKEW 0x6 +#define SCSI_DEFAULT_TIMING ((0x5 << 4) | 0x8) // 10MB/s -// 7:6 Hold count, 17ns -// 5:3 Assertion count, 30ns // 2:0 Deskew count, 25ns -#define SCSI_FAST10_TIMING ((0x1 << 6) | (0x2 << 3) | 0x2) +// 6:4 Hold count, 33ns +// 3:0 Assertion count, 30ns +#define SCSI_FAST10_DESKEW 3 +#define SCSI_FAST10_TIMING ((0x3 << 4) | 0x3) // 20MB/s -// 7:6 Hold count, 17ns -// 5:3 Assertion count, 17ns -// 2:0 Deskew count, 17ns -#define SCSI_FAST20_TIMING ((0x1 << 6) | (0x1 << 3) | 0x1) +// 2:0 Deskew count, 12ns +// 6:4 Hold count, 17ns +// 3:0 Assertion count, 15ns +#define SCSI_FAST20_DESKEW 2 +#define SCSI_FAST20_TIMING ((0x2 << 4) | 0x2) // Private DMA variables. static int dmaInProgress = 0; @@ -199,11 +202,13 @@ scsiRead(uint8_t* data, uint32_t count) uint32_t chunk = ((count - i) > SCSI_FIFO_DEPTH) ? SCSI_FIFO_DEPTH : (count - i); +#ifdef SCSI_FSMC_DMA if (chunk >= 16) { // DMA is doing 32bit transfers. chunk = chunk & 0xFFFFFFF8; } +#endif startScsiRx(chunk); while (i < count && likely(!scsiDev.resetFlag)) @@ -213,19 +218,24 @@ scsiRead(uint8_t* data, uint32_t count) uint32_t nextChunk = ((count - i - chunk) > SCSI_FIFO_DEPTH) ? SCSI_FIFO_DEPTH : (count - i - chunk); +#ifdef SCSI_FSMC_DMA if (nextChunk >= 16) { nextChunk = nextChunk & 0xFFFFFFF8; } +#endif if (nextChunk > 0) { startScsiRx(nextChunk); } +#ifdef SCSI_FSMC_DMA if (chunk < 16) +#endif { scsiReadPIO(data + i, chunk); } +#ifdef SCSI_FSMC_DMA else { scsiReadDMA(data + i, chunk); @@ -236,12 +246,13 @@ scsiRead(uint8_t* data, uint32_t count) { }; } +#endif i += chunk; chunk = nextChunk; } -#if 1 +#if FIFODEBUG if (!scsiPhyFifoEmpty() || !scsiPhyFifoAltEmpty()) { int j = 0; while (!scsiPhyFifoEmpty()) { scsiPhyRx(); ++j; } @@ -339,10 +350,13 @@ scsiWrite(const uint8_t* data, uint32_t count) } #endif +#ifdef SCSI_FSMC_DMA if (chunk < 16) +#endif { scsiWritePIO(data + i, chunk); } +#ifdef SCSI_FSMC_DMA else { // DMA is doing 32bit transfers. @@ -355,6 +369,7 @@ scsiWrite(const uint8_t* data, uint32_t count) { } } +#endif while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) { @@ -417,15 +432,18 @@ void scsiEnterPhase(int phase) if (scsiDev.target->syncPeriod == 12) { // SCSI2 FAST-20 Timing. 20MB/s. - *SCSI_CTRL_TIMING = SCSI_FAST20_TIMING; + *SCSI_CTRL_TIMING = SCSI_FAST20_DESKEW; + *SCSI_CTRL_TIMING2 = SCSI_FAST20_TIMING; } else if (scsiDev.target->syncPeriod == 25) { // SCSI2 FAST Timing. 10MB/s. - *SCSI_CTRL_TIMING = SCSI_FAST10_TIMING; + *SCSI_CTRL_TIMING = SCSI_FAST10_DESKEW; + *SCSI_CTRL_TIMING2 = SCSI_FAST10_TIMING; } else { // 5MB/s Timing - *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING; + *SCSI_CTRL_TIMING = SCSI_DEFAULT_DESKEW; + *SCSI_CTRL_TIMING2 = SCSI_DEFAULT_TIMING; } *SCSI_CTRL_SYNC_OFFSET = scsiDev.target->syncOffset; } else { @@ -455,33 +473,6 @@ void scsiPhyReset() dmaInProgress = 0; } -#if 0 - - // Set the Clear bits for both SCSI device FIFOs - scsiTarget_AUX_CTL = scsiTarget_AUX_CTL | 0x03; - - // Trigger RST outselves. It is connected to the datapath and will - // ensure it returns to the idle state. The datapath runs at the BUS clk - // speed (ie. same as the CPU), so we can be sure it is active for a sufficient - // duration. - SCSI_RST_ISR_Disable(); - SCSI_SetPin(SCSI_Out_RST); - - SCSI_CTL_PHASE_Write(0); - SCSI_ClearPin(SCSI_Out_ATN); - SCSI_ClearPin(SCSI_Out_BSY); - SCSI_ClearPin(SCSI_Out_ACK); - SCSI_ClearPin(SCSI_Out_RST); - SCSI_ClearPin(SCSI_Out_SEL); - SCSI_ClearPin(SCSI_Out_REQ); - - // Allow the FIFOs to fill up again. - SCSI_ClearPin(SCSI_Out_RST); - SCSI_RST_ISR_Enable(); - scsiTarget_AUX_CTL = scsiTarget_AUX_CTL & ~(0x03); - - SCSI_Parity_Error_Read(); // clear sticky bits -#endif *SCSI_CTRL_PHASE = 0x00; *SCSI_CTRL_BSY = 0x00; @@ -495,7 +486,7 @@ void scsiPhyReset() *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING; // DMA Benchmark code - // Currently 10MB/s. Assume 20MB/s is achievable with 16 bits. + // Currently 6.6MB/s. Assume 13MB/s is achievable with 16 bits #ifdef DMA_BENCHMARK while(1) { diff --git a/src/firmware/scsiPhy.h b/src/firmware/scsiPhy.h index b70ff7bc..fa7e1684 100755 --- a/src/firmware/scsiPhy.h +++ b/src/firmware/scsiPhy.h @@ -27,6 +27,7 @@ #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_CTRL_TIMING2 ((volatile uint8_t*)0x6000000A) #define SCSI_STS_FIFO ((volatile uint8_t*)0x60000010) #define SCSI_STS_ALTFIFO ((volatile uint8_t*)0x60000011) @@ -59,6 +60,11 @@ #define scsiStatusSEL() ((*SCSI_STS_SCSI & 0x08) == 0x08) #define scsiStatusACK() ((*SCSI_STS_SCSI & 0x10) == 0x10) +// Disable DMA due to errate with the STM32F205 DMA2 controller when +// concurrently transferring FSMC (with FIFO) and APB (ie. sdio) +// peripherals. +#undef SCSI_FSMC_DMA + extern uint8_t scsiPhyFifoSel; void scsiPhyInit(void); diff --git a/src/firmware/sd.c b/src/firmware/sd.c index c9832d0c..204936db 100755 --- a/src/firmware/sd.c +++ b/src/firmware/sd.c @@ -38,21 +38,31 @@ SdDevice sdDev; static int sdCmdActive = 0; int -sdReadDMAPoll() +sdReadDMAPoll(uint32_t remainingSectors) { + // TODO DMA byte counting disabled for now as it's not + // working. + // We can ask the SDIO controller how many bytes have been + // processed (SDIO_GetDataCounter()) but I'm not sure if that + // means the data has been transfered via dma to memory yet. +// uint32_t dmaBytesRemaining = __HAL_DMA_GET_COUNTER(hsd.hdmarx) * 4; + if (hsd.DmaTransferCplt || hsd.SdTransferCplt || + +// if (dmaBytesRemaining == 0 || (HAL_SD_ErrorTypedef)hsd.SdTransferErr != SD_OK) { HAL_SD_CheckReadOperation(&hsd, (uint32_t)SD_DATATIMEOUT); // DMA transfer is complete sdCmdActive = 0; - return 1; + return remainingSectors; } - else +/* else { - return 0; - } + return remainingSectors - ((dmaBytesRemaining + (SD_SECTOR_SIZE - 1)) / SD_SECTOR_SIZE); + }*/ + return 0; } void sdReadDMA(uint32_t lba, uint32_t sectors, uint8_t* outputBuffer) @@ -244,9 +254,12 @@ static void sdInitDMA() init = 1; //TODO MM SEE STUPID SD_DMA_RxCplt that require the SD IRQs to preempt - // Configured with 4 bits preemption, NO sub priority. + // Ie. priority must be geater than the SDIO_IRQn priority. + // 4 bits preemption, NO sub priority. + HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 8, 0); HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); + HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 8, 0); HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); } diff --git a/src/firmware/sd.h b/src/firmware/sd.h index 1ca10801..74f89a6a 100755 --- a/src/firmware/sd.h +++ b/src/firmware/sd.h @@ -40,7 +40,7 @@ void sdWriteMultiSectorDMA(uint8_t* outputBuffer); int sdWriteSectorDMAPoll(); void sdReadDMA(uint32_t lba, uint32_t sectors, uint8_t* outputBuffer); -int sdReadDMAPoll(); +int sdReadDMAPoll(uint32_t remainingSectors); void sdCompleteTransfer(); void sdPoll(); diff --git a/src/scsi2sd-util6/SCSI2SD_HID.cc b/src/scsi2sd-util6/SCSI2SD_HID.cc index 9b6cb518..94db8b51 100644 --- a/src/scsi2sd-util6/SCSI2SD_HID.cc +++ b/src/scsi2sd-util6/SCSI2SD_HID.cc @@ -196,7 +196,7 @@ std::string HID::getFirmwareVersionStr() const { std::stringstream ver; - ver << std::hex << + ver << (myFirmwareVersion >> 8) << '.' << ((myFirmwareVersion & 0xF0) >> 4); diff --git a/src/scsi2sd-util6/scsi2sd-util.cc b/src/scsi2sd-util6/scsi2sd-util.cc index 4f1209f8..82094764 100644 --- a/src/scsi2sd-util6/scsi2sd-util.cc +++ b/src/scsi2sd-util6/scsi2sd-util.cc @@ -888,7 +888,7 @@ private: for (size_t i = 0; i < 2; ++i) { std::stringstream ss; - ss << "Programming sector flash array " << sector; + ss << "Writing SD sector " << sector; mmLogStatus(ss.str()); currentProgress += 1; -- 2.38.5