Эх сурвалжийг харах

Fixes for timer + DMA acceleration and enable by default.

Gives about 2.9 MB/s, compared with 2.6 MB/s without DMA.
Petteri Aimonen 3 жил өмнө
parent
commit
6475f9923a

+ 3 - 1
lib/AzulSCSI_platform_GD32F205/AzulSCSI_platform.cpp

@@ -101,9 +101,11 @@ void azplatform_init()
     DBG_CTL |= DBG_CTL_TRACE_IOEN;
     CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
     TPI->ACPR = SystemCoreClock / 2000000 - 1; // 2 Mbps baudrate for SWO
+    // TPI->ACPR = SystemCoreClock / 30000000 - 1; // 30 Mbps baudrate for SWO
     TPI->SPPR = 2;
     TPI->FFCR = 0x100; // TPIU packet framing disabled
-    // DWT->CTRL = (1 << DWT_CTRL_CYCTAP_Pos)
+    // DWT->CTRL |= (1 << DWT_CTRL_EXCTRCENA_Pos);
+    // DWT->CTRL |= (1 << DWT_CTRL_CYCTAP_Pos)
     //             | (15 << DWT_CTRL_POSTPRESET_Pos)
     //             | (1 << DWT_CTRL_PCSAMPLENA_Pos)
     //             | (3 << DWT_CTRL_SYNCTAP_Pos)

+ 1 - 1
lib/AzulSCSI_platform_GD32F205/gd32_sdio_sdcard.c

@@ -2472,7 +2472,7 @@ static void dma_transfer_config(uint32_t *srcbuf, uint32_t bufsize)
     dma_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
     dma_struct.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
     dma_struct.memory_width = DMA_MEMORY_WIDTH_32BIT;
-    dma_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
+    dma_struct.priority = DMA_PRIORITY_MEDIUM;
     dma_init(DMA1, DMA_CH3, &dma_struct);
 
     dma_circulation_disable(DMA1, DMA_CH3);

+ 95 - 44
lib/AzulSCSI_platform_GD32F205/scsiPhy.cpp

@@ -8,6 +8,8 @@
 #include "scsi_accel_greenpak.h"
 #include "AzulSCSI_log.h"
 #include "AzulSCSI_log_trace.h"
+#include "AzulSCSI_config.h"
+#include <minIni.h>
 
 #include <scsi2sd.h>
 extern "C" {
@@ -15,6 +17,17 @@ extern "C" {
 #include <time.h>
 }
 
+// Acceleration mode in use
+static enum {
+    PHY_MODE_UNKNOWN = 0,
+    PHY_MODE_PIO = 1,
+    PHY_MODE_DMA_TIMER = 2,
+    PHY_MODE_GREENPAK_PIO = 3
+} g_scsi_phy_mode;
+static const char *g_scsi_phy_mode_names[4] = {
+    "Unknown", "PIO", "DMA_TIMER", "GREENPAK_PIO"
+};
+
 static void init_irqs();
 
 /***********************/
@@ -100,6 +113,34 @@ static void scsi_rst_assert_interrupt()
     }
 }
 
+static void selectPhyMode()
+{
+    int oldmode = g_scsi_phy_mode;
+
+    int wanted_mode = ini_getl("SCSI", "PhyMode", PHY_MODE_UNKNOWN, CONFIGFILE);
+    g_scsi_phy_mode = PHY_MODE_PIO;
+    
+#ifdef SCSI_ACCEL_DMA_AVAILABLE
+    if (wanted_mode == PHY_MODE_UNKNOWN || wanted_mode == PHY_MODE_DMA_TIMER)
+    {
+        g_scsi_phy_mode = PHY_MODE_DMA_TIMER;
+    }
+#endif
+
+    if (wanted_mode == PHY_MODE_UNKNOWN || wanted_mode == PHY_MODE_GREENPAK_PIO)
+    {
+        if (greenpak_is_ready())
+        {
+            g_scsi_phy_mode = PHY_MODE_GREENPAK_PIO;
+        }
+    }
+
+    if (g_scsi_phy_mode != oldmode)
+    {
+        azlog("SCSI PHY operating mode: ", g_scsi_phy_mode_names[g_scsi_phy_mode]);
+    }
+}
+
 extern "C" void scsiPhyReset(void)
 {
     SCSI_RELEASE_OUTPUTS();
@@ -107,9 +148,8 @@ extern "C" void scsiPhyReset(void)
     g_scsi_ctrl_bsy = 0;
     init_irqs();
 
-#ifdef SCSI_ACCEL_DMA_AVAILABLE
+    selectPhyMode();
     scsi_accel_dma_init();
-#endif
 }
 
 /************************/
@@ -233,33 +273,39 @@ extern "C" void scsiStartWrite(const uint8_t* data, uint32_t count)
 {
     scsiLogDataIn(data, count);
 
-#ifdef SCSI_ACCEL_DMA_AVAILABLE
-    if (gpio_input_bit_get(DIP_PORT, DIPSW1_PIN))
+    if (g_scsi_phy_mode == PHY_MODE_PIO || g_scsi_phy_mode == PHY_MODE_GREENPAK_PIO)
     {
+        // Software based bit-banging.
+        // Write requests are queued and then executed in isWriteFinished() callback.
+        // This allows better parallelism with SD card transfers.
+        
+        if (g_scsi_writereq.count)
+        {
+            if (data == g_scsi_writereq.data + g_scsi_writereq.count)
+            {
+                // Combine with previous one
+                g_scsi_writereq.count += count;
+                return;
+            }
+            else
+            {
+                // Actually execute previous request
+                scsiFinishWrite();
+            }
+        }
+
+        g_scsi_writereq.data = data;
+        g_scsi_writereq.count = count;
+    }
+    else if (g_scsi_phy_mode == PHY_MODE_DMA_TIMER)
+    {
+        // Accelerated writes using DMA and timers
         scsi_accel_dma_startWrite(data, count, &scsiDev.resetFlag);
-        return;
     }
-#endif
-
-    if (g_scsi_writereq.count)
+    else
     {
-        if (data == g_scsi_writereq.data + g_scsi_writereq.count)
-        {
-            // Combine with previous one
-            g_scsi_writereq.count += count;
-            return;
-        }
-        else
-        {
-            // Actually execute previous request
-            scsiFinishWrite();
-        }
+        azlog("Unknown SCSI PHY mode: ", (int)g_scsi_phy_mode);
     }
-
-    // Queue polling write requests.
-    // This allows better parallelism with SD card transfers.
-    g_scsi_writereq.data = data;
-    g_scsi_writereq.count = count;
 }
 
 static void processPollingWrite(uint32_t count)
@@ -272,7 +318,7 @@ static void processPollingWrite(uint32_t count)
     if (count_words * 4 == count)
     {
         // Use accelerated subroutine
-        if (greenpak_is_ready())
+        if (g_scsi_phy_mode == PHY_MODE_GREENPAK_PIO)
         {
             scsi_accel_greenpak_send((const uint32_t*)data, count_words, &scsiDev.resetFlag);
         }
@@ -320,33 +366,38 @@ static bool isPollingWriteFinished(const uint8_t *data)
 
 extern "C" bool scsiIsWriteFinished(const uint8_t *data)
 {
-#ifdef SCSI_ACCEL_DMA_AVAILABLE
-    if (!scsi_accel_dma_isWriteFinished(data))
-        return false;
-#endif
-
-    // Check if there is still a polling transfer in progress
-    if (!isPollingWriteFinished(data))
+    if (g_scsi_phy_mode == PHY_MODE_DMA_TIMER)
     {
-        // Process the transfer piece-by-piece while waiting
-        // for SD card to react.
-        processPollingWrite(256);
-        return isPollingWriteFinished(data);
+        return scsi_accel_dma_isWriteFinished(data);
     }
+    else
+    {
+        // Check if there is still a polling transfer in progress
+        if (!isPollingWriteFinished(data))
+        {
+            // Process the transfer piece-by-piece while waiting
+            // for SD card to react.
+            processPollingWrite(256);
+            return isPollingWriteFinished(data);
+        }
 
-    return true;
+        return true;
+    }
 }
 
 extern "C" void scsiFinishWrite()
 {
-#ifdef SCSI_ACCEL_DMA_AVAILABLE
-    scsi_accel_dma_finishWrite(&scsiDev.resetFlag);
-#endif
-
-    // Finish previously started polling write request.
-    if (g_scsi_writereq.count)
+    if (g_scsi_phy_mode == PHY_MODE_DMA_TIMER)
     {
-        processPollingWrite(g_scsi_writereq.count);
+        scsi_accel_dma_finishWrite(&scsiDev.resetFlag);
+    }
+    else
+    {
+        // Finish previously started polling write request.
+        if (g_scsi_writereq.count)
+        {
+            processPollingWrite(g_scsi_writereq.count);
+        }
     }
 }
 

+ 21 - 8
lib/AzulSCSI_platform_GD32F205/scsi_accel_dma.cpp

@@ -4,7 +4,15 @@
 #include <gd32f20x_rcu.h>
 #include <assert.h>
 
-#ifdef SCSI_ACCEL_DMA_AVAILABLE
+#ifndef SCSI_ACCEL_DMA_AVAILABLE
+
+void scsi_accel_dma_init() {}
+void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag) {}
+void scsi_accel_dma_stopWrite() {}
+void scsi_accel_dma_finishWrite(volatile int *resetFlag) {}
+bool scsi_accel_dma_isWriteFinished(const uint8_t* data) { return true; }
+
+#else
 
 #define DMA_BUF_SIZE 256
 #define DMA_BUF_MASK (DMA_BUF_SIZE - 1)
@@ -94,9 +102,6 @@ void scsi_accel_dma_init()
     gpio_init(SCSI_TIMER_IN_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_TIMER_IN_PIN);
 
     scsi_accel_dma_stopWrite();
-
-    // For debug
-    gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);
 }
 
 // Select whether OUT_REQ is connected to timer or GPIO port
@@ -106,13 +111,11 @@ static void scsi_dma_gpio_config(bool enable_timer)
     {
         gpio_init(SCSI_OUT_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ);
         gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN);
-        GPIO_BOP(GPIOE) = GPIO_PIN_0;
     }
     else
     {
         gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN);
         gpio_init(SCSI_OUT_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ);
-        GPIO_BC(GPIOE) = GPIO_PIN_0;
     }
 }
 
@@ -182,11 +185,11 @@ static void start_dma()
     DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN;
 
     // Make sure REQ is initially high
+    TIMER_CNT(SCSI_TIMER) = 16;
     TIMER_CHCTL1(SCSI_TIMER) = 0x6050;
     TIMER_CHCTL1(SCSI_TIMER) = 0x6074;
 
     // Enable timer
-    TIMER_CNT(SCSI_TIMER) = 16;
     TIMER_CTL0(SCSI_TIMER) |= TIMER_CTL0_CEN;
 
     // Generate first events
@@ -199,8 +202,11 @@ static void stop_dma()
 {
     DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
     DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~(DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE);
+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_FTFIE;
     TIMER_CTL0(SCSI_TIMER) &= ~TIMER_CTL0_CEN;
     g_scsi_dma_state = SCSIDMA_IDLE;
+    SCSI_RELEASE_DATA_REQ();
 }
 
 // Convert new data from application buffer to DMA buffer
@@ -242,9 +248,11 @@ extern "C" void SCSI_TIMER_DMACHA_IRQ()
     if (g_scsi_dma.next_app_buf && g_scsi_dma.bytes_dma == g_scsi_dma.bytes_app)
     {
         // Switch to next buffer
+        assert(g_scsi_dma.scheduled_dma == g_scsi_dma.bytes_app);
         g_scsi_dma.app_buf = g_scsi_dma.next_app_buf;
         g_scsi_dma.bytes_app = g_scsi_dma.next_app_bytes;
         g_scsi_dma.bytes_dma = 0;
+        g_scsi_dma.scheduled_dma = 0;
         g_scsi_dma.next_app_buf = 0;
         g_scsi_dma.next_app_bytes = 0;
         refill_dmabuf();
@@ -266,8 +274,13 @@ extern "C" void SCSI_TIMER_DMACHB_IRQ()
         {
             if (g_scsi_dma.dma_idx < g_scsi_dma.dma_fillto)
             {
+                // Previous request didn't have a complete buffer worth of data.
+                // Refill the buffer and ensure that the first byte of the new data gets
+                // written to outputs.
                 __disable_irq();
+                uint32_t *first_data = &g_scsi_dma.dma_buf[g_scsi_dma.dma_idx & DMA_BUF_MASK];
                 refill_dmabuf();
+                GPIO_BOP(SCSI_OUT_PORT) = *first_data;
                 __enable_irq();
             }
 
@@ -326,7 +339,7 @@ void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int
         }
     }
 
-    azdbg("Starting DMA write of ", (int)count, " bytes");
+    // azdbg("Starting DMA write of ", (int)count, " bytes");
     scsi_dma_gpio_config(true);
     g_scsi_dma_state = SCSIDMA_WRITE;
     g_scsi_dma.app_buf = (uint8_t*)data;

+ 3 - 4
lib/AzulSCSI_platform_GD32F205/scsi_accel_dma.h

@@ -6,9 +6,8 @@
 #include "AzulSCSI_platform.h"
 
 #ifdef SCSI_TIMER
-// TODO: This works, but without external logic does not improve performance compared
-// to software bitbang.
-// #define SCSI_ACCEL_DMA_AVAILABLE 1
+#define SCSI_ACCEL_DMA_AVAILABLE 1
+#endif
 
 void scsi_accel_dma_init();
 void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag);
@@ -20,4 +19,4 @@ void scsi_accel_dma_finishWrite(volatile int *resetFlag);
 bool scsi_accel_dma_isWriteFinished(const uint8_t* data);
 
 
-#endif
+