Explorar o código

Ultra SCSI (Fast20) Support.

Merged from: https://github.com/ZuluSCSI/ZuluSCSI-firmware/pull/468

Co-authored-by: mglazzari-qinmotion <mglazzari@qinmotion.com>
Co-authored-by: androda <androda@users.noreply.github.com>
Morio hai 1 ano
pai
achega
72e5a5160e

+ 98 - 16
lib/BlueSCSI_platform_RP2040/BlueSCSI_platform.cpp

@@ -15,6 +15,8 @@
 #include <hardware/spi.h>
 #include <hardware/adc.h>
 #include <hardware/flash.h>
+#include <hardware/sync.h>
+#include "custom_timings.h"
 #include <hardware/structs/xip_ctrl.h>
 #include <hardware/structs/usb.h>
 #ifdef ENABLE_AUDIO_OUTPUT
@@ -34,7 +36,7 @@
 #include "hardware/i2c.h"
 
 extern "C" {
-
+#include "timings_RP2MCU.h"
 const char *g_platform_name = PLATFORM_NAME;
 static bool g_scsi_initiator = false;
 static bool g_supports_initiator = false;
@@ -110,13 +112,10 @@ static void CheckPicoW() {
 }
 #endif
 
-#ifdef ENABLE_AUDIO_OUTPUT
-// Increases clk_sys and clk_peri to 135.428571MHz at runtime to support
-// division to audio output rates. Invoke before anything is using clk_peri
-// except for the logging UART, which is handled below.
-static void reclock_for_audio() {
+static void reclock() {
     // ensure UART is fully drained before we mess up its clock
-    uart_tx_wait_blocking(uart0);
+    if (uart_is_enabled(uart0))
+        uart_tx_wait_blocking(uart0);
     // switch clk_sys and clk_peri to pll_usb
     // see code in 2.15.6.1 of the datasheet for useful comments
     clock_configure(clk_sys,
@@ -129,24 +128,100 @@ static void reclock_for_audio() {
             CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
             48 * MHZ,
             48 * MHZ);
-    // reset PLL for 135.428571MHz
-    pll_init(pll_sys, 1, 948000000, 7, 1);
+    // reset PLL
+    pll_init(pll_sys,
+        g_bluescsi_timings->pll.refdiv,
+        g_bluescsi_timings->pll.vco_freq,
+        g_bluescsi_timings->pll.post_div1,
+        g_bluescsi_timings->pll.post_div2);
+
     // switch clocks back to pll_sys
     clock_configure(clk_sys,
             CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
             CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
-            135428571,
-            135428571);
+            g_bluescsi_timings->clk_hz,
+            g_bluescsi_timings->clk_hz);
     clock_configure(clk_peri,
             0,
             CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
-            135428571,
-            135428571);
+            g_bluescsi_timings->clk_hz,
+            g_bluescsi_timings->clk_hz);
     // reset UART for the new clock speed
-    uart_init(uart0, 1000000);
+    if (uart_is_enabled(uart0))
+        uart_init(uart0, 1000000);
 }
+
+uint32_t platform_sys_clock_in_hz()
+{
+    return clock_get_hz(clk_sys);
+}
+
+bluescsi_speed_grade_t platform_string_to_speed_grade(const char *speed_grade_str, size_t length)
+{
+    static const char sg_default[] = "Default";
+    bluescsi_speed_grade_t grade;
+
+#ifdef ENABLE_AUDIO_OUTPUT
+    log("Audio output enabled, reclocking isn't possible");
+    return SPEED_GRADE_DEFAULT;
 #endif
 
+    if (strcasecmp(speed_grade_str, sg_default) == 0)
+      grade = SPEED_GRADE_DEFAULT;
+    else if (strcasecmp(speed_grade_str, "TurboMax") == 0)
+      grade = SPEED_GRADE_MAX;
+    else if (strcasecmp(speed_grade_str, "TurboA") == 0)
+      grade = SPEED_GRADE_A;
+    else if (strcasecmp(speed_grade_str, "TurboB") == 0)
+      grade = SPEED_GRADE_B;
+    else if (strcasecmp(speed_grade_str, "TurboC") == 0)
+      grade = SPEED_GRADE_C;
+    else if (strcasecmp(speed_grade_str, "Custom") == 0)
+      grade = SPEED_GRADE_CUSTOM;
+    else
+    {
+      log("Setting \"", speed_grade_str, "\" does not match any know speed grade, using default");
+      grade = SPEED_GRADE_DEFAULT;
+    }
+    return grade;
+}
+
+bluescsi_reclock_status_t platform_reclock(bluescsi_speed_grade_t speed_grade)
+{
+    CustomTimings ct;
+    if (speed_grade == SPEED_GRADE_CUSTOM)
+    {
+        if (ct.use_custom_timings())
+        {
+            log("Custom timings found in \"", CUSTOM_TIMINGS_FILE, "\" overriding reclocking");
+            log("Initial Clock set to ", (int) platform_sys_clock_in_hz(), "Hz");
+            if (ct.set_timings_from_file())
+            {
+                reclock();
+                log("SDIO clock set to ", (int)((g_bluescsi_timings->clk_hz / g_bluescsi_timings->sdio.clk_div_pio + (5 * MHZ / 10)) / MHZ) , "MHz");
+                return BLUESCSI_RECLOCK_CUSTOM;
+            }
+            else
+                return BLUESCSI_RECLOCK_FAILED;
+        }
+        else
+        {
+            log("Custom timings file, \"", CUSTOM_TIMINGS_FILE, "\" not found or disabled");
+            return BLUESCSI_RECLOCK_FAILED;
+        }
+
+    }
+    else if (set_timings(speed_grade))
+    {
+        log("Initial Clock set to ", (int) platform_sys_clock_in_hz(), "Hz");
+        reclock();
+        log("SDIO clock set to ", (int)((g_bluescsi_timings->clk_hz / g_bluescsi_timings->sdio.clk_div_pio + (5 * MHZ / 10)) / MHZ) , "MHz");
+        return BLUESCSI_RECLOCK_SUCCESS;
+    }
+    return BLUESCSI_RECLOCK_FAILED;
+}
+
+
 void platform_init()
 {
     // Make sure second core is stopped
@@ -222,8 +297,14 @@ void platform_init()
 
 #ifdef ENABLE_AUDIO_OUTPUT
     log("SP/DIF audio to expansion header enabled");
-    log("-- Overclocking to 135.428571MHz");
-    reclock_for_audio();
+    if (platform_reclock(SPEED_GRADE_AUDIO) == BLUESCSI_RECLOCK_SUCCESS)
+    {
+        log("Reclocked for Audio Ouput at ", (int) platform_sys_clock_in_hz(), "Hz");
+    }
+    else
+    {
+        log("Audio Output timings not found");
+    }
 #endif
 
     // Get flash chip size
@@ -343,6 +424,7 @@ void platform_late_init()
         gpio_conf(scsi_pins.OUT_ACK,   GPIO_FUNC_SIO, true,false, true,  true, true);
         //gpio_conf(SCSI_OUT_ATN,   GPIO_FUNC_SIO, false,false, true,  true, true);  // ATN output is unused
     }
+    scsi_accel_rp2040_init();
 }
 
 void platform_enable_initiator_mode() {

+ 27 - 11
lib/BlueSCSI_platform_RP2040/BlueSCSI_platform.h

@@ -4,6 +4,8 @@
 
 #include <stdint.h>
 #include <Arduino.h>
+#include <BlueSCSI_config.h>
+
 #include "BlueSCSI_platform_gpio.h"
 #include "scsiHostPhy.h"
 
@@ -13,16 +15,14 @@ extern "C" {
 
 /* These are used in debug output and default SCSI strings */
 extern const char *g_platform_name;
-#define PLATFORM_NAME "BlueSCSI"
-#define PLATFORM_REVISION "2.0"
-#define PLATFORM_TOOLBOX_API 0
-#define PLATFORM_INQUIRY PLATFORM_NAME "v" FW_VER_NUM
-#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_10
-#define PLATFORM_OPTIMAL_MIN_SD_WRITE_SIZE 32768
-#define PLATFORM_OPTIMAL_MAX_SD_WRITE_SIZE 65536
-#define PLATFORM_OPTIMAL_LAST_SD_WRITE_SIZE 8192
-#define SD_USE_SDIO 1
-#define PLATFORM_HAS_INITIATOR_MODE 1
+
+
+#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
+#ifdef PICO_RP2040
+#define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 10
+#else
+#define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 20
+#endif
 
 #ifndef PLATFORM_VDD_WARNING_LIMIT_mV
 #define PLATFORM_VDD_WARNING_LIMIT_mV 2800
@@ -32,7 +32,7 @@ extern SCSI_PINS scsi_pins;
 
 // NOTE: The driver supports synchronous speeds higher than 10MB/s, but this
 // has not been tested due to lack of fast enough SCSI adapter.
-// #define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_TURBO
+// #define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
 
 // Debug logging function, can be used to print to e.g. serial port.
 // May get called from interrupt handlers.
@@ -86,6 +86,22 @@ void platform_poll();
 // This function should return without significantly delay.
 uint8_t platform_get_buttons();
 
+uint32_t platform_sys_clock_in_hz();
+    // Reclocking return status
+typedef enum
+{
+    BLUESCSI_RECLOCK_SUCCESS,
+    BLUESCSI_RECLOCK_CUSTOM,
+    BLUESCSI_RECLOCK_NOT_SUPPORTED,
+    BLUESCSI_RECLOCK_FAILED
+} bluescsi_reclock_status_t;
+
+// Attempt to reclock the MCU
+bluescsi_reclock_status_t platform_reclock(bluescsi_speed_grade_t speed_grade);
+
+// convert string to speed grade
+bluescsi_speed_grade_t platform_string_to_speed_grade(const char *speed_grade_str, size_t length);
+
 // Platform method to determine whether this is a certain hardware version
 bool is202309a();
 

+ 124 - 0
lib/BlueSCSI_platform_RP2040/custom_timings.cpp

@@ -0,0 +1,124 @@
+/**
+ * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ *
+ * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+**/
+
+#include "custom_timings.h"
+#include <SdFat.h>
+#include <minIni.h>
+#include <BlueSCSI_log.h>
+#include <BlueSCSI_platform.h>
+#include "timings_RP2MCU.h"
+extern SdFs SD;
+
+extern "C"
+{
+    #include <timings.h>
+}
+
+bool CustomTimings::use_custom_timings()
+{
+    return SD.exists(CUSTOM_TIMINGS_FILE) && !ini_getbool("settings", "disable", 0, CUSTOM_TIMINGS_FILE);
+}
+
+bool CustomTimings::set_timings_from_file()
+{
+    const char settings_section[] = "settings";
+    const char pll_section[] = "pll";
+    const char scsi_section[] = "scsi";
+    const char scsi_20_section[] = "scsi_20";
+    const char scsi_10_section[] = "scsi_10";
+    const char scsi_5_section[] = "scsi_5";
+    const char sdio_section[] = "sdio";
+
+
+    bluescsi_timings_t custom_timings;
+
+
+    // pll
+    int32_t vco = ini_getl(pll_section, "vco_freq_hz", g_bluescsi_timings->pll.vco_freq, CUSTOM_TIMINGS_FILE);
+    int32_t post_div1 = ini_getl(pll_section, "pd1", g_bluescsi_timings->pll.post_div1, CUSTOM_TIMINGS_FILE);
+    int32_t post_div2 = ini_getl(pll_section, "pd2", g_bluescsi_timings->pll.post_div2, CUSTOM_TIMINGS_FILE);
+
+    if (vco > 0 && post_div1 > 0 && post_div2 > 0)
+    {
+        if (vco / post_div1 / post_div2 > 250000000)
+        {
+            log("Reclocking over 250MHz with the PLL settings is not allowed using ", CUSTOM_TIMINGS_FILE);
+            return false;
+        }
+    }
+    else
+    {
+        log("Reclocking failed because 0 or negative PLL settings values");
+        return false;
+    }
+
+    g_bluescsi_timings->pll.vco_freq = vco;
+    g_bluescsi_timings->pll.post_div1 = post_div1;
+    g_bluescsi_timings->pll.post_div2 = post_div2;
+    g_bluescsi_timings->pll.refdiv =  ini_getl(pll_section, "refdiv", g_bluescsi_timings->pll.refdiv, CUSTOM_TIMINGS_FILE);
+
+    char speed_grade_str[10];
+    ini_gets(settings_section, "extends_speed_grade", "Default", speed_grade_str, sizeof(speed_grade_str), CUSTOM_TIMINGS_FILE);
+    bluescsi_speed_grade_t speed_grade =  platform_string_to_speed_grade(speed_grade_str, sizeof(speed_grade_str));
+    set_timings(speed_grade);
+
+    int32_t number_setting = ini_getl(settings_section, "boot_with_sync_value", 0, CUSTOM_TIMINGS_FILE);
+
+    if (number_setting > 0)
+    {
+        g_force_sync = number_setting;
+        number_setting = ini_getl(settings_section, "boot_with_offset_value", 15, CUSTOM_TIMINGS_FILE);
+        g_force_offset = number_setting > 15 ? 15 : number_setting;
+        log("Forcing sync of ", (int) g_force_sync, " and offset of ", (int) g_force_offset);
+    }
+    g_bluescsi_timings->clk_hz = ini_getl(settings_section, "clk_hz", g_bluescsi_timings->clk_hz, CUSTOM_TIMINGS_FILE);
+
+
+    // scsi
+    g_bluescsi_timings->scsi.clk_period_ps = ini_getl(scsi_section, "clk_period_ps", g_bluescsi_timings->scsi.clk_period_ps, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi.req_delay = ini_getl(scsi_section, "req_delay_cc", g_bluescsi_timings->scsi.req_delay, CUSTOM_TIMINGS_FILE);
+
+    // scsi 20
+    g_bluescsi_timings->scsi_20.delay0 = ini_getl(scsi_20_section, "delay0_cc", g_bluescsi_timings->scsi_20.delay0, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi_20.delay1 = ini_getl(scsi_20_section, "delay1_cc", g_bluescsi_timings->scsi_20.delay1, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi_20.total_delay_adjust = ini_getl(scsi_20_section, "total_delay_adjust_cc", g_bluescsi_timings->scsi_20.total_delay_adjust, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi_20.max_sync = ini_getl(scsi_20_section, "max_sync", g_bluescsi_timings->scsi_20.max_sync, CUSTOM_TIMINGS_FILE);
+
+    // scsi 10
+    g_bluescsi_timings->scsi_10.delay0 = ini_getl(scsi_10_section, "delay0_cc", g_bluescsi_timings->scsi_10.delay0, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi_10.delay1 = ini_getl(scsi_10_section, "delay1_cc", g_bluescsi_timings->scsi_10.delay1, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi_10.total_delay_adjust = ini_getl(scsi_10_section, "total_delay_adjust_cc", g_bluescsi_timings->scsi_10.total_delay_adjust, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi_10.max_sync = ini_getl(scsi_10_section, "max_sync", g_bluescsi_timings->scsi_10.max_sync, CUSTOM_TIMINGS_FILE);
+
+    // scsi 5
+    g_bluescsi_timings->scsi_5.delay0 = ini_getl(scsi_5_section, "delay0_cc", g_bluescsi_timings->scsi_5.delay0, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi_5.delay1 = ini_getl(scsi_5_section, "delay1_cc", g_bluescsi_timings->scsi_5.delay1, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi_5.total_delay_adjust = ini_getl(scsi_5_section, "total_delay_adjust_cc", g_bluescsi_timings->scsi_5.total_delay_adjust, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->scsi_5.max_sync = ini_getl(scsi_5_section, "max_sync", g_bluescsi_timings->scsi_5.max_sync, CUSTOM_TIMINGS_FILE);
+
+    // sdio
+    g_bluescsi_timings->sdio.clk_div_pio = ini_getl(sdio_section, "clk_div_pio", g_bluescsi_timings->sdio.clk_div_pio, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->sdio.clk_div_1mhz = ini_getl(sdio_section, "clk_div_1mhz", g_bluescsi_timings->sdio.clk_div_1mhz, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->sdio.delay0 = ini_getl(sdio_section, "delay0", g_bluescsi_timings->sdio.delay0, CUSTOM_TIMINGS_FILE);
+    g_bluescsi_timings->sdio.delay1 = ini_getl(sdio_section, "delay1", g_bluescsi_timings->sdio.delay1, CUSTOM_TIMINGS_FILE);
+
+    return true;
+}

+ 35 - 0
lib/BlueSCSI_platform_RP2040/custom_timings.h

@@ -0,0 +1,35 @@
+/**
+* ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ *
+ * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+**/
+#pragma once
+
+#define CUSTOM_TIMINGS_FILE "bluescsi_timings.ini"
+
+extern "C"
+{
+#include "timings_RP2MCU.h"
+}
+
+class CustomTimings
+{
+public:
+    bool use_custom_timings();
+    bool set_timings_from_file();
+};

+ 71 - 5
lib/BlueSCSI_platform_RP2040/rp2040_sdio.cpp

@@ -17,6 +17,7 @@
 //#include <hardware/gpio.h>
 #include <BlueSCSI_platform.h>
 #include <BlueSCSI_log.h>
+#include "timings_RP2MCU.h"
 
 #define SDIO_PIO pio1
 #define SDIO_CMD_SM 0
@@ -890,8 +891,35 @@ void __not_in_flash_func(rp2040_sdio_init)(int clock_divider)
     gpio_set_pulls(SDIO_D2, true, false);
     gpio_set_pulls(SDIO_D3, true, false);
 
+    //
     // Command state machine
-    g_sdio.pio_cmd_rsp_clk_offset = pio_add_program(SDIO_PIO, &cmd_rsp_program);
+    //
+    uint16_t rw_prg_instr[cmd_rsp_program.length];
+    memcpy(rw_prg_instr, cmd_rsp_program_instructions, sizeof(cmd_rsp_program_instructions));
+    // If the instruction is a side-set 0 use delay0
+    // If the instruction is a side-set 1 use delay1
+    // cmd_rsp does not follow a certain pattern, so bluntly set delay values here.
+    // NOTE: Any changes to cmd_rsp will require updates here as well
+    rw_prg_instr[0] = cmd_rsp_program_instructions[0] | pio_encode_delay(g_bluescsi_timings->sdio.delay0);
+    rw_prg_instr[1] = cmd_rsp_program_instructions[1] | pio_encode_delay(g_bluescsi_timings->sdio.delay1);
+
+    rw_prg_instr[2] = cmd_rsp_program_instructions[2] | pio_encode_delay(g_bluescsi_timings->sdio.delay0);
+    rw_prg_instr[3] = cmd_rsp_program_instructions[3] | pio_encode_delay(g_bluescsi_timings->sdio.delay1 + 1);
+
+    rw_prg_instr[4] = cmd_rsp_program_instructions[4] | pio_encode_delay(g_bluescsi_timings->sdio.delay0);
+    rw_prg_instr[5] = cmd_rsp_program_instructions[5] | pio_encode_delay(g_bluescsi_timings->sdio.delay1);
+
+    rw_prg_instr[8] = cmd_rsp_program_instructions[8] | pio_encode_delay(g_bluescsi_timings->sdio.delay0);
+    rw_prg_instr[9] = cmd_rsp_program_instructions[9] | pio_encode_delay(g_bluescsi_timings->sdio.delay1);
+
+    pio_program cmd_rsp_prg_w_delays = {
+        rw_prg_instr,
+        cmd_rsp_program.length,
+        cmd_rsp_program.origin,
+        cmd_rsp_program.pio_version
+    };
+
+    g_sdio.pio_cmd_rsp_clk_offset = pio_add_program(SDIO_PIO, &cmd_rsp_prg_w_delays);
     g_sdio.pio_cfg_cmd_rsp = pio_cmd_rsp_program_config(g_sdio.pio_cmd_rsp_clk_offset, SDIO_CMD, SDIO_CLK, clock_divider, 0);
 
     pio_sm_init(SDIO_PIO, SDIO_CMD_SM, g_sdio.pio_cmd_rsp_clk_offset, &g_sdio.pio_cfg_cmd_rsp);
@@ -900,13 +928,51 @@ void __not_in_flash_func(rp2040_sdio_init)(int clock_divider)
     pio_sm_set_consecutive_pindirs(SDIO_PIO, SDIO_CMD_SM, SDIO_CMD, 1, true);
     pio_sm_set_consecutive_pindirs(SDIO_PIO, SDIO_CMD_SM, SDIO_D0, 4, false);
 
+    //
     // Data reception program
-    g_sdio.pio_data_rx_offset = pio_add_program(SDIO_PIO, &rd_data_w_clock_program);
+    //
+    // Clear the temp memory array
+    memset(rw_prg_instr, cmd_rsp_program.length, sizeof(rw_prg_instr[0]));
+    memcpy(rw_prg_instr, rd_data_w_clock_program_instructions, sizeof(rd_data_w_clock_program_instructions));
+    rw_prg_instr[1] = rd_data_w_clock_program_instructions[1] | pio_encode_delay(g_bluescsi_timings->sdio.delay0 + 1);
+    rw_prg_instr[2] = rd_data_w_clock_program_instructions[2] | pio_encode_delay(g_bluescsi_timings->sdio.delay1 + 1);
+    rw_prg_instr[3] = rd_data_w_clock_program_instructions[3] | pio_encode_delay(g_bluescsi_timings->sdio.delay0);
+    rw_prg_instr[4] = rd_data_w_clock_program_instructions[4] | pio_encode_delay(g_bluescsi_timings->sdio.delay1);
+    rw_prg_instr[5] = rd_data_w_clock_program_instructions[5] | pio_encode_delay(g_bluescsi_timings->sdio.delay0);
+
+    pio_program rd_data_w_delays = {
+        rw_prg_instr,
+        rd_data_w_clock_program.length,
+        rd_data_w_clock_program.origin,
+        rd_data_w_clock_program.pio_version
+    };
+
+    g_sdio.pio_data_rx_offset = pio_add_program(SDIO_PIO, &rd_data_w_delays);
     g_sdio.pio_cfg_data_rx = pio_rd_data_w_clock_program_config(g_sdio.pio_data_rx_offset, SDIO_D0, SDIO_CLK, clock_divider);
 
+    //
     // Data transmission program
-    g_sdio.pio_data_tx_offset = pio_add_program(SDIO_PIO, &sdio_tx_w_clock_program);
-    g_sdio.pio_cfg_data_tx = pio_sdio_tx_w_clock_program_config(g_sdio.pio_data_tx_offset, SDIO_D0, SDIO_CLK, clock_divider);
+    //
+    // Clear the temp memory array
+    memset(rw_prg_instr, cmd_rsp_program.length, sizeof(rw_prg_instr[0]));
+    memcpy(rw_prg_instr, tx_data_w_clock_program_instructions, sizeof(tx_data_w_clock_program_instructions));
+    rw_prg_instr[0] = tx_data_w_clock_program_instructions[0] | pio_encode_delay(g_bluescsi_timings->sdio.delay0);
+    rw_prg_instr[1] = tx_data_w_clock_program_instructions[1] | pio_encode_delay(g_bluescsi_timings->sdio.delay1);
+    rw_prg_instr[2] = tx_data_w_clock_program_instructions[2] | pio_encode_delay(g_bluescsi_timings->sdio.delay1);
+    rw_prg_instr[3] = tx_data_w_clock_program_instructions[3] | pio_encode_delay(g_bluescsi_timings->sdio.delay1 + 1);
+    rw_prg_instr[4] = tx_data_w_clock_program_instructions[4] | pio_encode_delay(g_bluescsi_timings->sdio.delay0 + 1);
+    rw_prg_instr[5] = tx_data_w_clock_program_instructions[5] | pio_encode_delay(g_bluescsi_timings->sdio.delay1 + 1);
+    rw_prg_instr[6] = tx_data_w_clock_program_instructions[6] | pio_encode_delay(g_bluescsi_timings->sdio.delay0 + 1);
+
+    pio_program tx_data_w_delays = {
+        rw_prg_instr,
+        tx_data_w_clock_program.length,
+        tx_data_w_clock_program.origin,
+        tx_data_w_clock_program.pio_version
+    };
+
+    g_sdio.pio_data_tx_offset = pio_add_program(SDIO_PIO, &tx_data_w_delays);
+    g_sdio.pio_cfg_data_tx = pio_tx_w_clock_program_config(g_sdio.pio_data_tx_offset, SDIO_D0, SDIO_CLK, clock_divider);
 
     // Disable SDIO pins input synchronizer.
     // This reduces input delay.
@@ -970,5 +1036,5 @@ void __not_in_flash_func(rp2040_sdio_delay_increment)(uint16_t additional_delay)
     */
     rp2040_sdio_update_delays(cmd_rsp_program, g_sdio.pio_cmd_rsp_clk_offset, additional_delay);
     rp2040_sdio_update_delays(rd_data_w_clock_program, g_sdio.pio_data_rx_offset, additional_delay);
-    rp2040_sdio_update_delays(sdio_tx_w_clock_program, g_sdio.pio_data_tx_offset, additional_delay);
+    rp2040_sdio_update_delays(tx_data_w_clock_program, g_sdio.pio_data_tx_offset, additional_delay);
 }

+ 31 - 28
lib/BlueSCSI_platform_RP2040/rp2040_sdio.pio

@@ -47,34 +47,35 @@
 ; Because data is written on the falling edge and read on the rising
 ; edge, it is preferrable to have a long 0 state and short 1 state.
 ;.define CLKDIV 3
-.define CLKDIV 5
-.define D0 (((CLKDIV + 1) /2) - 1)
-.define D1 ((CLKDIV/2) - 1)
-.define SDIO_CLK_GPIO 10
-.define public SDIO_IRQ 7
+;.define CLKDIV 5
+;.define D0 (((CLKDIV + 1) /2) - 1)     ; Used to be 2 clocks
+;.define D1 ((CLKDIV/2) - 1)            ; Used to be 1 clock
+;.define SDIO_CLK_GPIO 10
+;.define public SDIO_IRQ 7
 
 ; State Machine 0 is for the Command / Response
 ; This State Machine will stall with clock low after sending a command and receiving the response
 ; Note that the FIFOs are set to 8 bit mode here, because 8 bits evenly divides all command and response sizes
+; NOTE: Any changes to cmd_rsp will require updates in rp2040_sdio to set delay values appropriately for speed config
 .program cmd_rsp
 .side_set 1 opt
 .wrap_target
 cmd_begin:
 send_cmd:
-    out pins, 1         side 0 [1]  ; When TX FIFO is empty, this command will stall with clock low
-    jmp X-- send_cmd    side 1 [1]
+    out pins, 1         side 0 [0]  ; When TX FIFO is empty, this command will stall with clock low
+    jmp X-- send_cmd    side 1 [0]
 
-    jmp !Y cmd_begin    side 0 [1]  ; If no response, go back to the beginning and stall
-    set pindirs, 0      side 1 [3]
+    jmp !Y cmd_begin    side 0 [0]  ; If no response, go back to the beginning and stall
+    set pindirs, 0      side 1 [0]
 wait_resp:
-    nop                 side 0 [3]
-    nop                 side 1 [2]
+    nop                 side 0 [0]
+    nop                 side 1 [0]
     jmp PIN wait_resp               ; Run the SD clock until CMD pin goes low (First bit of response)
     
 read_resp:
     in pins, 1          
-    push iffull block   side 0 [2]  ; Read command response
-    jmp Y-- read_resp   side 1 [1]
+    push iffull block   side 0 [0]  ; Read command response
+    jmp Y-- read_resp   side 1 [0]
 .wrap
 
 % c-sdk {
@@ -95,16 +96,17 @@ static inline pio_sm_config pio_cmd_rsp_program_config(uint offset, uint cmd_pin
 ; Program which reads data and provides its own clock signal
 ; Use direct-execute PIO instructions to place the number of 4-bit nibbles to receive
 ; into the X register before enabling the state machine
+; NOTE: Any changes to rd_data_w_clock will require updates in rp2040_sdio to set delay values appropriately for speed config
 .program rd_data_w_clock
 .side_set 1
 mov X, Y                side 0      ; Reinitialize number of nibbles to receive
 wait_d0:
-    nop                 side 0 [3]  ; Run the clock...
-    jmp PIN wait_d0     side 1 [3]  ; Until the first response nibble (all zeroes)
-    nop                 side 0 [2]  ; Clock transition low to make the SD card write out the first actual data nibble
-    nop                 side 1 [1]  ; Transition clock high to stick data value
+    nop                 side 0 [0]  ; Run the clock...
+    jmp PIN wait_d0     side 1 [0]  ; Until the first response nibble (all zeroes)
+    nop                 side 0 [0]  ; Clock transition low to make the SD card write out the first actual data nibble
+    nop                 side 1 [0]  ; Transition clock high to stick data value
 read_loop:
-    in pins, 4          side 0 [2]  ; Read in the nibble and transition the clock low
+    in pins, 4          side 0 [0]  ; Read in the nibble and transition the clock low
     push iffull block   side 1      ; Transition the clock high and block execution if rx fifo is full
     jmp X--, read_loop  side 1      ; No delays here or previous instruction, because instr [1] = two instr execution time
 
@@ -135,28 +137,29 @@ static inline pio_sm_config pio_rd_data_w_clock_program_config(uint offset, uint
 ; - Word 1-128: transmitted data (512 bytes)
 ; - Word 129-130: CRC checksum
 ; - Word 131: end token 0xFFFFFFFF
-.program sdio_tx_w_clock
+; NOTE: Any changes to sdio_tx_w_clock will require updates in rp2040_sdio to set delay values appropriately for speed config
+.program tx_data_w_clock
 .side_set 1 opt
 tx_loop:
-    out PINS, 4             side 0 [2]      ; Write nibble value and transition clock low
-    jmp X-- tx_loop         side 1 [1]      ; Transition clock high, and check if more data needs to be sent
+    out PINS, 4             side 0 [0]      ; Write nibble value and transition clock low
+    jmp X-- tx_loop         side 1 [0]      ; Transition clock high, and check if more data needs to be sent
 
-    set pindirs, 0          side 1 [2]      ; Set input mode to receive CRC token, without changing clock phase
+    set pindirs, 0          side 1 [0]      ; Set input mode to receive CRC token, without changing clock phase
 
 crc_get:
-    in pins, 1              side 1 [4]      ; Input the first bit of CRC response
-    jmp Y-- crc_get         side 0 [4]      ; Read the CRC bits
+    in pins, 1              side 1 [0]      ; Input the first bit of CRC response
+    jmp Y-- crc_get         side 0 [0]      ; Read the CRC bits
 bsy_wait:
-    jmp PIN done            side 1 [4]
-    jmp bsy_wait            side 0 [4]      ; Clock until no longer BSY
+    jmp PIN done            side 1 [0]
+    jmp bsy_wait            side 0 [0]      ; Clock until no longer BSY
 done:
 .wrap_target
     push iffull noblock     side 0         ; Unconditional, just push the response token
 .wrap
 
 % c-sdk {
-static inline pio_sm_config pio_sdio_tx_w_clock_program_config(uint offset, uint data_pin, uint clk_pin, int clk_div) {
-    pio_sm_config c = sdio_tx_w_clock_program_get_default_config(offset);
+static inline pio_sm_config pio_tx_w_clock_program_config(uint offset, uint data_pin, uint clk_pin, int clk_div) {
+    pio_sm_config c = tx_data_w_clock_program_get_default_config(offset);
     sm_config_set_sideset_pins(&c, clk_pin);
     sm_config_set_out_pins(&c, data_pin, 4);
     sm_config_set_in_pins(&c, data_pin);

+ 31 - 33
lib/BlueSCSI_platform_RP2040/rp2040_sdio.pio.h

@@ -8,8 +8,6 @@
 #include "hardware/pio.h"
 #endif
 
-#define SDIO_IRQ 7
-
 // ------- //
 // cmd_rsp //
 // ------- //
@@ -19,16 +17,16 @@
 
 static const uint16_t cmd_rsp_program_instructions[] = {
             //     .wrap_target
-    0x7101, //  0: out    pins, 1         side 0 [1] 
-    0x1940, //  1: jmp    x--, 0          side 1 [1] 
-    0x1160, //  2: jmp    !y, 0           side 0 [1] 
-    0xfb80, //  3: set    pindirs, 0      side 1 [3] 
-    0xb342, //  4: nop                    side 0 [3] 
-    0xba42, //  5: nop                    side 1 [2] 
+    0x7001, //  0: out    pins, 1         side 0     
+    0x1840, //  1: jmp    x--, 0          side 1     
+    0x1060, //  2: jmp    !y, 0           side 0     
+    0xf880, //  3: set    pindirs, 0      side 1     
+    0xb042, //  4: nop                    side 0     
+    0xb842, //  5: nop                    side 1     
     0x00c4, //  6: jmp    pin, 4                     
     0x4001, //  7: in     pins, 1                    
-    0x9260, //  8: push   iffull block    side 0 [2] 
-    0x1987, //  9: jmp    y--, 7          side 1 [1] 
+    0x9060, //  8: push   iffull block    side 0     
+    0x1887, //  9: jmp    y--, 7          side 1     
             //     .wrap
 };
 
@@ -71,11 +69,11 @@ static inline pio_sm_config pio_cmd_rsp_program_config(uint offset, uint cmd_pin
 static const uint16_t rd_data_w_clock_program_instructions[] = {
             //     .wrap_target
     0xa022, //  0: mov    x, y            side 0     
-    0xa342, //  1: nop                    side 0 [3] 
-    0x13c1, //  2: jmp    pin, 1          side 1 [3] 
-    0xa242, //  3: nop                    side 0 [2] 
-    0xb142, //  4: nop                    side 1 [1] 
-    0x4204, //  5: in     pins, 4         side 0 [2] 
+    0xa042, //  1: nop                    side 0     
+    0x10c1, //  2: jmp    pin, 1          side 1     
+    0xa042, //  3: nop                    side 0     
+    0xb042, //  4: nop                    side 1     
+    0x4004, //  5: in     pins, 4         side 0     
     0x9060, //  6: push   iffull block    side 1     
     0x1045, //  7: jmp    x--, 5          side 1     
             //     .wrap
@@ -109,41 +107,41 @@ static inline pio_sm_config pio_rd_data_w_clock_program_config(uint offset, uint
 #endif
 
 // --------------- //
-// sdio_tx_w_clock //
+// tx_data_w_clock //
 // --------------- //
 
-#define sdio_tx_w_clock_wrap_target 7
-#define sdio_tx_w_clock_wrap 7
-
-static const uint16_t sdio_tx_w_clock_program_instructions[] = {
-    0x7204, //  0: out    pins, 4         side 0 [2] 
-    0x1940, //  1: jmp    x--, 0          side 1 [1] 
-    0xfa80, //  2: set    pindirs, 0      side 1 [2] 
-    0x5c01, //  3: in     pins, 1         side 1 [4] 
-    0x1483, //  4: jmp    y--, 3          side 0 [4] 
-    0x1cc7, //  5: jmp    pin, 7          side 1 [4] 
-    0x1405, //  6: jmp    5               side 0 [4] 
+#define tx_data_w_clock_wrap_target 7
+#define tx_data_w_clock_wrap 7
+
+static const uint16_t tx_data_w_clock_program_instructions[] = {
+    0x7004, //  0: out    pins, 4         side 0     
+    0x1840, //  1: jmp    x--, 0          side 1     
+    0xf880, //  2: set    pindirs, 0      side 1     
+    0x5801, //  3: in     pins, 1         side 1     
+    0x1083, //  4: jmp    y--, 3          side 0     
+    0x18c7, //  5: jmp    pin, 7          side 1     
+    0x1005, //  6: jmp    5               side 0     
             //     .wrap_target
     0x9040, //  7: push   iffull noblock  side 0     
             //     .wrap
 };
 
 #if !PICO_NO_HARDWARE
-static const struct pio_program sdio_tx_w_clock_program = {
-    .instructions = sdio_tx_w_clock_program_instructions,
+static const struct pio_program tx_data_w_clock_program = {
+    .instructions = tx_data_w_clock_program_instructions,
     .length = 8,
     .origin = -1,
 };
 
-static inline pio_sm_config sdio_tx_w_clock_program_get_default_config(uint offset) {
+static inline pio_sm_config tx_data_w_clock_program_get_default_config(uint offset) {
     pio_sm_config c = pio_get_default_sm_config();
-    sm_config_set_wrap(&c, offset + sdio_tx_w_clock_wrap_target, offset + sdio_tx_w_clock_wrap);
+    sm_config_set_wrap(&c, offset + tx_data_w_clock_wrap_target, offset + tx_data_w_clock_wrap);
     sm_config_set_sideset(&c, 2, true, false);
     return c;
 }
 
-static inline pio_sm_config pio_sdio_tx_w_clock_program_config(uint offset, uint data_pin, uint clk_pin, int clk_div) {
-    pio_sm_config c = sdio_tx_w_clock_program_get_default_config(offset);
+static inline pio_sm_config pio_tx_w_clock_program_config(uint offset, uint data_pin, uint clk_pin, int clk_div) {
+    pio_sm_config c = tx_data_w_clock_program_get_default_config(offset);
     sm_config_set_sideset_pins(&c, clk_pin);
     sm_config_set_out_pins(&c, data_pin, 4);
     sm_config_set_in_pins(&c, data_pin);

+ 33 - 0
lib/BlueSCSI_platform_RP2040/scsi2sd_timings.c

@@ -0,0 +1,33 @@
+/**
+* ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ *
+ * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+**/
+#include "timings.h"
+#include "BlueSCSI_platform.h"
+#if defined(ARCH_RP2350)
+uint8_t g_max_sync_20_period = 18;
+uint8_t g_max_sync_10_period = 25;
+uint8_t g_max_sync_5_period  = 50;
+#elif defined(ARCH_RP2040)
+uint8_t g_max_sync_20_period = 25;
+uint8_t g_max_sync_10_period = 25;
+uint8_t g_max_sync_5_period  = 50;
+#endif
+uint8_t g_force_sync = 0;
+uint8_t g_force_offset = 15;

+ 3 - 2
lib/BlueSCSI_platform_RP2040/scsi_accel.pio

@@ -13,7 +13,8 @@
 
 ; Delay from data setup to REQ assertion.
 ; deskew delay + cable skew delay = 55 ns minimum
-; One clock cycle is 8 ns => delay 7 clocks
+; One clock cycle is x ns => delay (55 / x) clocks
+; REQ_DLY is a dummy value, will be rewritten
 .define REQ_DLY 7
 
 ; Adds parity to data that is to be written to SCSI
@@ -34,7 +35,7 @@
 
     pull ifempty block          side 1  ; Get data from TX FIFO
     out pins, 9                 side 1  ; Write data and parity bit
-    out null, 23 [REQ_DLY-2]    side 1  ; Discard unused bits, wait for data preset time
+    out null, 23    [0]         side 1  ; Discard unused bits, wait for data preset time
     wait 1 gpio ACK             side 1  ; Wait for ACK to be inactive
     wait 0 gpio ACK             side 0  ; Assert REQ, wait for ACK low
 

+ 1 - 1
lib/BlueSCSI_platform_RP2040/scsi_accel.pio.h

@@ -49,7 +49,7 @@ static const uint16_t scsi_accel_async_write_program_instructions[] = {
             //     .wrap_target
     0x90e0, //  0: pull   ifempty block   side 1     
     0x7009, //  1: out    pins, 9         side 1     
-    0x7577, //  2: out    null, 23        side 1 [5] 
+    0x7077, //  2: out    null, 23        side 1 
     0x309a, //  3: wait   1 gpio, 26      side 1     
     0x201a, //  4: wait   0 gpio, 26      side 0     
             //     .wrap

+ 186 - 26
lib/BlueSCSI_platform_RP2040/scsi_accel_rp2040.cpp

@@ -12,6 +12,7 @@
 #include "BlueSCSI_log.h"
 #include "scsi_accel_rp2040.h"
 #include "scsi_accel.pio.h"
+#include "timings_RP2MCU.h"
 #include <hardware/pio.h>
 #include <hardware/dma.h>
 #include <hardware/irq.h>
@@ -74,6 +75,15 @@ static struct {
     pio_sm_config pio_cfg_read;
     pio_sm_config pio_cfg_read_parity;
     pio_sm_config pio_cfg_sync_read_pacer;
+
+    // PIO Program States
+    bool pio_removed_parity;
+    bool pio_removed_async_write;
+    bool pio_removed_sync_write_pacer;
+    bool pio_removed_sync_write;
+    bool pio_removed_read;
+    bool pio_removed_read_parity;
+    bool pio_removed_sync_read_pacer;
     
     // DMA configurations for write
     dma_channel_config dmacfg_write_chA; // Data from RAM to scsi_parity PIO
@@ -719,6 +729,126 @@ void scsi_accel_rp2040_finishRead(const uint8_t *data, uint32_t count, int *pari
     }
 }
 
+/*******************************************************/
+/* Write SCSI PIO program timings and ACK pin          */
+/*******************************************************/
+static void platform_pio_remove_program(PIO pio, const pio_program_t *program, uint loaded_offset, bool &removed)
+{
+    if (!removed)
+    {
+        pio_remove_program(pio, program, loaded_offset);
+        removed = true;
+    }
+}
+
+static int pio_add_scsi_accel_async_write_program()
+{
+    platform_pio_remove_program(SCSI_DMA_PIO,
+        &scsi_accel_async_write_program,
+        g_scsi_dma.pio_offset_async_write,
+        g_scsi_dma.pio_removed_async_write);
+
+    uint16_t rewrote_instructions[sizeof(scsi_accel_async_write_program_instructions)/sizeof(scsi_accel_async_write_program_instructions[0])];
+    pio_program rewrote_program = {rewrote_instructions,
+        scsi_accel_async_write_program.length,
+        scsi_accel_async_write_program.origin,
+        scsi_accel_async_write_program.pio_version};
+
+    memcpy(rewrote_instructions,
+        scsi_accel_async_write_program_instructions,
+        sizeof(scsi_accel_async_write_program_instructions));
+
+    // out null, 23         side 1  [0] ;[REQ_DLY-2]      ; Discard unused bits, wait for data preset time
+    uint8_t delay = g_bluescsi_timings->scsi.req_delay - 2;
+    assert( delay <= 0xF);
+    rewrote_instructions[2] |= pio_encode_delay(delay);
+    // wait 1 gpio ACK      side 1      ; Wait for ACK to be inactive
+    rewrote_instructions[3] = pio_encode_wait_gpio(true, SCSI_IN_ACK) | pio_encode_sideset(1, 1);
+    // wait 0 gpio ACK      side 0      ; Assert REQ, wait for ACK low
+    rewrote_instructions[4] = pio_encode_wait_gpio(false, SCSI_IN_ACK) | pio_encode_sideset(1, 0);
+
+    g_scsi_dma.pio_removed_async_write = false;
+    return pio_add_program(SCSI_DMA_PIO, &rewrote_program);
+}
+
+static int pio_add_scsi_accel_read_program()
+{
+    platform_pio_remove_program(SCSI_DMA_PIO,
+        &scsi_accel_read_program,
+        g_scsi_dma.pio_offset_read,
+        g_scsi_dma.pio_removed_read);
+
+    uint16_t rewrote_instructions[sizeof(scsi_accel_read_program_instructions)/sizeof(scsi_accel_read_program_instructions[0])];
+
+    pio_program rewrote_program = {
+        rewrote_instructions,
+        scsi_accel_read_program.length,
+        scsi_accel_read_program.origin,
+        scsi_accel_read_program.pio_version};
+
+    memcpy(rewrote_instructions,
+        scsi_accel_read_program_instructions,
+        sizeof(scsi_accel_read_program_instructions));
+
+    // wait 1 gpio ACK             side 1  ; Wait for ACK high
+    rewrote_instructions[1] = pio_encode_wait_gpio(true, SCSI_IN_ACK) | pio_encode_sideset(1, 1);
+    // wait 0 gpio ACK             side 0  ; Assert REQ, wait for ACK low
+    rewrote_instructions[3] = pio_encode_wait_gpio(false, SCSI_IN_ACK) | pio_encode_sideset(1, 0);
+    g_scsi_dma.pio_removed_read = false;
+    return pio_add_program(SCSI_DMA_PIO, &rewrote_program);
+}
+
+static int pio_add_scsi_sync_write_pacer_program()
+{
+    platform_pio_remove_program(SCSI_DMA_PIO,
+        &scsi_sync_write_pacer_program,
+        g_scsi_dma.pio_offset_sync_write_pacer,
+        g_scsi_dma.pio_removed_sync_write_pacer);
+
+    uint16_t rewrote_instructions[sizeof(scsi_sync_write_pacer_program_instructions)/sizeof(scsi_sync_write_pacer_program_instructions[0])];
+
+    pio_program rewrote_program = {
+        rewrote_instructions,
+        scsi_sync_write_pacer_program.length,
+        scsi_sync_write_pacer_program.origin,
+        scsi_sync_write_pacer_program.pio_version};
+
+    memcpy(rewrote_instructions,
+        scsi_sync_write_pacer_program_instructions,
+        sizeof(scsi_sync_write_pacer_program_instructions));
+
+    // wait 1 gpio ACK
+    rewrote_instructions[0] = pio_encode_wait_gpio(true, SCSI_IN_ACK);
+    // wait 0 gpio ACK   ; Wait for falling edge on ACK
+    rewrote_instructions[1] = pio_encode_wait_gpio(false, SCSI_IN_ACK);
+    g_scsi_dma.pio_removed_sync_write_pacer = false;
+    return pio_add_program(SCSI_DMA_PIO, &rewrote_program);
+}
+
+static int pio_add_scsi_parity_program()
+{
+    g_scsi_dma.pio_removed_parity = false;
+    return pio_add_program(SCSI_DMA_PIO, &scsi_parity_program);
+}
+
+static int pio_add_scsi_sync_read_pacer_program()
+{
+    g_scsi_dma.pio_removed_sync_read_pacer = false;
+    return pio_add_program(SCSI_DMA_PIO, &scsi_sync_read_pacer_program);
+}
+
+static int pio_add_scsi_read_parity_program()
+{
+    g_scsi_dma.pio_removed_read_parity = false;
+    return pio_add_program(SCSI_DMA_PIO, &scsi_read_parity_program);
+}
+
+static int pio_add_scsi_sync_write_program()
+{
+    g_scsi_dma.pio_removed_sync_write = false;
+    return pio_add_program(SCSI_DMA_PIO, &scsi_sync_write_program);
+}
+
 /*******************************************************/
 /* Initialization functions common to read/write       */
 /*******************************************************/
@@ -826,6 +956,19 @@ void scsi_accel_rp2040_init()
     g_scsi_dma_state = SCSIDMA_IDLE;
     scsidma_config_gpio();
 
+    static bool first_init = true;
+    if (first_init)
+    {
+        g_scsi_dma.pio_removed_parity = true;
+        g_scsi_dma.pio_removed_async_write = true;
+        g_scsi_dma.pio_removed_sync_write_pacer = true;
+        g_scsi_dma.pio_removed_sync_write = true;
+        g_scsi_dma.pio_removed_read = true;
+        g_scsi_dma.pio_removed_read_parity = true;
+        g_scsi_dma.pio_removed_sync_read_pacer = true;
+        first_init = false;
+    }
+
     if (g_channels_claimed) {
         // Un-claim all SCSI state machines
         pio_sm_unclaim(SCSI_DMA_PIO, SCSI_PARITY_SM);
@@ -833,13 +976,13 @@ void scsi_accel_rp2040_init()
         pio_sm_unclaim(SCSI_DMA_PIO, SCSI_SYNC_SM);
 
         // Remove all SCSI programs
-        pio_remove_program(SCSI_DMA_PIO, &scsi_parity_program, g_scsi_dma.pio_offset_parity);
-        pio_remove_program(SCSI_DMA_PIO, &scsi_accel_async_write_program, g_scsi_dma.pio_offset_async_write);
-        pio_remove_program(SCSI_DMA_PIO, &scsi_sync_write_pacer_program, g_scsi_dma.pio_offset_sync_write_pacer);
-        pio_remove_program(SCSI_DMA_PIO, &scsi_sync_write_program, g_scsi_dma.pio_offset_sync_write);
-        pio_remove_program(SCSI_DMA_PIO, &scsi_accel_read_program, g_scsi_dma.pio_offset_read);
-        pio_remove_program(SCSI_DMA_PIO, &scsi_sync_read_pacer_program, g_scsi_dma.pio_offset_sync_read_pacer);
-        pio_remove_program(SCSI_DMA_PIO, &scsi_read_parity_program, g_scsi_dma.pio_offset_read_parity);
+        platform_pio_remove_program(SCSI_DMA_PIO, &scsi_parity_program, g_scsi_dma.pio_offset_parity, g_scsi_dma.pio_removed_parity);
+        platform_pio_remove_program(SCSI_DMA_PIO, &scsi_accel_async_write_program, g_scsi_dma.pio_offset_async_write, g_scsi_dma.pio_removed_async_write);
+        platform_pio_remove_program(SCSI_DMA_PIO, &scsi_sync_write_pacer_program, g_scsi_dma.pio_offset_sync_write_pacer, g_scsi_dma.pio_removed_sync_write_pacer);
+        platform_pio_remove_program(SCSI_DMA_PIO, &scsi_accel_read_program, g_scsi_dma.pio_offset_read, g_scsi_dma.pio_removed_read);
+        platform_pio_remove_program(SCSI_DMA_PIO, &scsi_sync_read_pacer_program, g_scsi_dma.pio_offset_sync_read_pacer, g_scsi_dma.pio_removed_sync_read_pacer);
+        platform_pio_remove_program(SCSI_DMA_PIO, &scsi_read_parity_program, g_scsi_dma.pio_offset_read_parity, g_scsi_dma.pio_removed_read_parity);
+        platform_pio_remove_program(SCSI_DMA_PIO, &scsi_sync_write_program, g_scsi_dma.pio_offset_sync_write, g_scsi_dma.pio_removed_sync_write);
 
         // Un-claim all SCSI DMA channels
         dma_channel_unclaim(SCSI_DMA_CH_A);
@@ -863,13 +1006,13 @@ void scsi_accel_rp2040_init()
     }
     
     // Parity lookup generator
-    g_scsi_dma.pio_offset_parity = pio_add_program(SCSI_DMA_PIO, &scsi_parity_program);
+    g_scsi_dma.pio_offset_parity = pio_add_scsi_parity_program();
     g_scsi_dma.pio_cfg_parity = scsi_parity_program_get_default_config(g_scsi_dma.pio_offset_parity);
     sm_config_set_out_shift(&g_scsi_dma.pio_cfg_parity, true, false, 32);
     sm_config_set_in_shift(&g_scsi_dma.pio_cfg_parity, true, true, 32);
 
     // Asynchronous SCSI write
-    g_scsi_dma.pio_offset_async_write = pio_add_program(SCSI_DMA_PIO, &scsi_accel_async_write_program);
+    g_scsi_dma.pio_offset_async_write = pio_add_scsi_accel_async_write_program();
     g_scsi_dma.pio_cfg_async_write = scsi_accel_async_write_program_get_default_config(g_scsi_dma.pio_offset_async_write);
     sm_config_set_out_pins(&g_scsi_dma.pio_cfg_async_write, SCSI_IO_DB0, 9);
     sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_async_write, scsi_pins.OUT_REQ);
@@ -877,12 +1020,12 @@ void scsi_accel_rp2040_init()
     sm_config_set_out_shift(&g_scsi_dma.pio_cfg_async_write, true, false, 32);
 
     // Synchronous SCSI write pacer / ACK handler
-    g_scsi_dma.pio_offset_sync_write_pacer = pio_add_program(SCSI_DMA_PIO, &scsi_sync_write_pacer_program);
+    g_scsi_dma.pio_offset_sync_write_pacer = pio_add_scsi_sync_write_pacer_program();
     g_scsi_dma.pio_cfg_sync_write_pacer = scsi_sync_write_pacer_program_get_default_config(g_scsi_dma.pio_offset_sync_write_pacer);
     sm_config_set_out_shift(&g_scsi_dma.pio_cfg_sync_write_pacer, true, true, 1);
 
     // Synchronous SCSI data writer
-    g_scsi_dma.pio_offset_sync_write = pio_add_program(SCSI_DMA_PIO, &scsi_sync_write_program);
+    g_scsi_dma.pio_offset_sync_write = pio_add_scsi_sync_write_program();
     g_scsi_dma.pio_cfg_sync_write = scsi_sync_write_program_get_default_config(g_scsi_dma.pio_offset_sync_write);
     sm_config_set_out_pins(&g_scsi_dma.pio_cfg_sync_write, SCSI_IO_DB0, 9);
     sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_sync_write, scsi_pins.OUT_REQ);
@@ -890,7 +1033,7 @@ void scsi_accel_rp2040_init()
     sm_config_set_in_shift(&g_scsi_dma.pio_cfg_sync_write, true, true, 1);
 
     // Asynchronous / synchronous SCSI read
-    g_scsi_dma.pio_offset_read = pio_add_program(SCSI_DMA_PIO, &scsi_accel_read_program);
+    g_scsi_dma.pio_offset_read = pio_add_scsi_accel_read_program();
     g_scsi_dma.pio_cfg_read = scsi_accel_read_program_get_default_config(g_scsi_dma.pio_offset_read);
     sm_config_set_in_pins(&g_scsi_dma.pio_cfg_read, SCSI_IO_DB0);
     sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_read, scsi_pins.OUT_REQ);
@@ -898,12 +1041,12 @@ void scsi_accel_rp2040_init()
     sm_config_set_in_shift(&g_scsi_dma.pio_cfg_read, true, true, 32);
 
     // Synchronous SCSI read pacer
-    g_scsi_dma.pio_offset_sync_read_pacer = pio_add_program(SCSI_DMA_PIO, &scsi_sync_read_pacer_program);
+    g_scsi_dma.pio_offset_sync_read_pacer = pio_add_scsi_sync_read_pacer_program();
     g_scsi_dma.pio_cfg_sync_read_pacer = scsi_sync_read_pacer_program_get_default_config(g_scsi_dma.pio_offset_sync_read_pacer);
     sm_config_set_sideset_pins(&g_scsi_dma.pio_cfg_sync_read_pacer, scsi_pins.OUT_REQ);
 
     // Read parity check
-    g_scsi_dma.pio_offset_read_parity = pio_add_program(SCSI_DMA_PIO, &scsi_read_parity_program);
+    g_scsi_dma.pio_offset_read_parity = pio_add_scsi_read_parity_program();
     g_scsi_dma.pio_cfg_read_parity = scsi_read_parity_program_get_default_config(g_scsi_dma.pio_offset_read_parity);
     sm_config_set_out_shift(&g_scsi_dma.pio_cfg_read_parity, true, true, 32);
     sm_config_set_in_shift(&g_scsi_dma.pio_cfg_read_parity, true, false, 32);
@@ -1046,20 +1189,36 @@ bool scsi_accel_rp2040_setSyncMode(int syncOffset, int syncPeriod)
 
             // Set up the timing parameters to PIO program
             // The scsi_sync_write PIO program consists of three instructions.
-            // The delays are in clock cycles, each taking 8 ns.
-            // delay0: Delay from data write to REQ assertion
-            // delay1: Delay from REQ assert to REQ deassert
-            // delay2: Delay from REQ deassert to data write
+            // The delays are in clock cycles, each taking 8 ns (@100MHz) or 6.66ns (@150MHz).
+            // delay0: Delay from data write to REQ assertion (data setup)
+            // delay1: Delay from REQ assert to REQ deassert (req pulse width)
+            // delay2: Delay from REQ deassert to data write (negation period)
+            // see timings.c for delay periods in clock cycles
             int delay0, delay1, delay2;
-            int totalDelay = syncPeriod * 4 / 8;
+            uint32_t up_rounder = g_bluescsi_timings->scsi.clk_period_ps / 2 + 1;
+            uint32_t delay_in_ps = (syncPeriod * 4) * 1000;
+            // This is the delay in clock cycles rounded up
+            int totalDelay = (delay_in_ps + up_rounder) / g_bluescsi_timings->scsi.clk_period_ps;
 
-            if (syncPeriod <= 25)
+            if (syncPeriod < 25)
+            {
+                // Fast-20 SCSI timing: 15 ns assertion period
+                // The hardware rise and fall time require some extra delay,
+                // These delays are in addition to the 1 cycle that the PIO takes to execute the instruction
+                totalDelay += g_bluescsi_timings->scsi_20.total_delay_adjust;
+                delay0 = g_bluescsi_timings->scsi_20.delay0; //Data setup time, should be min 11.5ns according to the spec for FAST-20
+                delay1 = g_bluescsi_timings->scsi_20.delay1; //pulse width, should be min 15ns according to the spec for FAST-20
+                delay2 = totalDelay - delay0 - delay1 - 3;  //Data hold time, should be min 16.5ns according to the spec for FAST-20
+                if (delay2 < 0) delay2 = 0;
+                if (delay2 > 15) delay2 = 15;
+            }
+            else if (syncPeriod < 50 )
             {
-                // Fast SCSI timing: 30 ns assertion period, 25 ns skew delay
+                // Fast-10 SCSI timing: 30 ns assertion period, 25 ns skew delay
                 // The hardware rise and fall time require some extra delay,
-                // the values below are tuned based on oscilloscope measurements.
-                delay0 = 3;
-                delay1 = 5;
+                totalDelay += g_bluescsi_timings->scsi_10.total_delay_adjust;
+                delay0 = g_bluescsi_timings->scsi_10.delay0; // 4;
+                delay1 = g_bluescsi_timings->scsi_10.delay1; // 6;
                 delay2 = totalDelay - delay0 - delay1 - 3;
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;
@@ -1067,8 +1226,9 @@ bool scsi_accel_rp2040_setSyncMode(int syncOffset, int syncPeriod)
             else
             {
                 // Slow SCSI timing: 90 ns assertion period, 55 ns skew delay
-                delay0 = 6;
-                delay1 = 12;
+                totalDelay += g_bluescsi_timings->scsi_5.total_delay_adjust;
+                delay0 = g_bluescsi_timings->scsi_5.delay0;
+                delay1 = g_bluescsi_timings->scsi_5.delay1;
                 delay2 = totalDelay - delay0 - delay1 - 3;
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;

+ 335 - 0
lib/BlueSCSI_platform_RP2040/timings_RP2MCU.c

@@ -0,0 +1,335 @@
+/**
+ * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ *
+ * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+**/
+#include "timings_RP2MCU.h"
+#include <string.h>
+#include "timings.h"
+
+
+static bluescsi_timings_t  predefined_timings[]  = {
+    {
+        .clk_hz = 125000000,
+
+        .pll =
+        {
+            .refdiv = 1,
+            .vco_freq = 1500000000,
+            .post_div1 = 6,
+            .post_div2 = 2
+        },
+
+        .scsi =
+        {
+            .req_delay = 7,
+            .clk_period_ps = 8000
+        },
+
+        .scsi_20 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+
+        },
+
+        .scsi_10 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+        },
+
+        .scsi_5 =
+        {
+            .delay0 = 10 - 1,
+            .delay1 = 15 - 1,
+            .total_delay_adjust = -1,
+            .max_sync = 50,
+        },
+
+        .sdio =
+        {
+            .clk_div_1mhz = 25, // = 125MHz clk / clk_div_pio
+            .clk_div_pio = 5,
+            .delay0 = 3 - 1, // subtract one for the instruction delay
+            .delay1 = 2 - 1  // clk_div_pio - delay0 and subtract one for the instruction delay
+        }
+    },
+    {
+        .clk_hz = 133000000,
+
+        .pll =
+        {
+            .refdiv = 1,
+            .vco_freq = 1596000000,
+            .post_div1 = 6,
+            .post_div2 = 2
+        },
+
+        .scsi =
+        {
+            .req_delay = 7,
+            .clk_period_ps = 7519
+        },
+
+        .scsi_20 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+
+        },
+
+        .scsi_10 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+        },
+
+        .scsi_5 =
+        {
+            .delay0 = 10 - 1,
+            .delay1 = 15 - 1,
+            .total_delay_adjust = -1,
+            .max_sync = 50,
+        },
+
+        .sdio =
+        {
+            .clk_div_1mhz = 25,
+            .clk_div_pio = 5,
+            .delay0 = 3 - 1, // subtract one for the instruction delay
+            .delay1 = 2 - 1  // clk_div_pio - delay0 and subtract one for the instruction delay
+        }
+    },
+    {
+        .clk_hz = 135428571,
+
+        .pll =
+        {
+            .refdiv = 1,
+            .vco_freq = 948000000,
+            .post_div1 = 7,
+            .post_div2 = 1
+        },
+
+        .scsi =
+        {
+            .req_delay = 7,
+            .clk_period_ps = 7384
+        },
+
+        .scsi_20 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+
+        },
+
+        .scsi_10 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+        },
+
+        .scsi_5 =
+        {
+            .delay0 = 10 - 1,
+            .delay1 = 15 - 1,
+            .total_delay_adjust = -1,
+            .max_sync = 50,
+        },
+
+        .sdio =
+        {
+            .clk_div_1mhz = 27 , // = 135MHz clk / clk_div_pio
+            .clk_div_pio = 5,
+            .delay0 = 3 - 1, // subtract one for the instruction delay
+            .delay1 = 2 - 1  // clk_div_pio - delay0 and subtract one for the instruction delay
+        }
+    },
+    {
+        .clk_hz = 150000000,
+
+        .pll =
+        {
+            .refdiv = 1,
+            .vco_freq = 1500000000,
+            .post_div1 = 5,
+            .post_div2 = 2
+        },
+
+        .scsi =
+        {
+            .req_delay = 9,
+            .clk_period_ps = 6667
+        },
+
+        .scsi_20 =
+        {
+            .delay0 = 3 - 1,
+            .delay1 = 4 - 1,
+            .total_delay_adjust = 0,
+            .max_sync = 18,
+
+        },
+
+        .scsi_10 =
+        {
+            .delay0 = 4 - 1,
+            .delay1 = 5 - 1,
+            .total_delay_adjust = 0,
+            .max_sync = 25,
+
+        },
+
+        .scsi_5 =
+        {
+            .delay0 = 10 - 1,
+            .delay1 = 15, // should be 18 - 1 but max currently is 15
+            .total_delay_adjust = 0,
+            .max_sync = 50,
+
+        },
+
+        .sdio =
+        {
+            .clk_div_1mhz = 30, // = 150MHz clk / clk_div_pio
+            .clk_div_pio = 5,
+            .delay0 = 3 - 1, // subtract one for the instruction delay
+            .delay1 = 2 - 1  // clk_div_pio - delay0 and subtract one for the instruction delay
+        }
+    },
+    {
+        .clk_hz = 250000000,
+
+        .pll =
+        {
+            .refdiv = 1,
+            .vco_freq = 1500000000,
+            .post_div1 = 6,
+            .post_div2 = 1
+        },
+
+        .scsi =
+        {
+            .req_delay = 14,
+            .clk_period_ps = 4000,
+        },
+
+        .scsi_20 =
+        {
+            .delay0 = 3 - 1,
+            .delay1 = 5 - 1,
+            .total_delay_adjust = 1,
+            .max_sync = 12,
+
+        },
+
+        .scsi_10 =
+        {
+            .delay0 = 6 - 1,
+            .delay1 = 9 - 1,
+            .total_delay_adjust = 1,
+            .max_sync = 25,
+        },
+
+        .scsi_5 =
+        {
+            .delay0 = 15, // maxed out should be 16
+            .delay1 = 15, // maxed out should be 30
+            .total_delay_adjust = 1,
+            .max_sync = 50,
+        },
+#ifndef RP2040
+        .sdio =
+        {
+            .clk_div_1mhz = 30, // set by trail and error
+            .clk_div_pio = 6, // SDIO at 41.7MHz
+            .delay0 = 4 - 1, // subtract one for the instruction delay
+            .delay1 = 2 - 1  // clk_div_pio - delay0 and subtract one for the instruction delay
+        }
+#else
+        .sdio =
+        {
+            .clk_div_1mhz = 50, // = 250MHz clk / clk_div_pio
+            .clk_div_pio = 5, // SDIO at 50MHz
+            .delay0 = 4 - 1, // subtract one for the instruction delay
+            .delay1 = 1 - 1  // clk_div_pio - delay0 and subtract one for the instruction delay
+        }
+#endif
+    },
+};
+    bluescsi_timings_t  current_timings;
+
+#ifdef ENABLE_AUDIO_OUTPUT
+    bluescsi_timings_t *g_bluescsi_timings = &predefined_timings[2];
+#elif defined(ARCH_RP2350)
+    bluescsi_timings_t *g_bluescsi_timings = &predefined_timings[3];
+#elif defined(ARCH_RP2040)
+    bluescsi_timings_t *g_bluescsi_timings = &predefined_timings[1];
+#else
+    bluescsi_timings_t *g_bluescsi_timings = &predefined_timings[0];
+#endif
+
+
+bool set_timings(bluescsi_speed_grade_t speed_grade)
+{
+    uint8_t timings_index;
+
+    switch (speed_grade)
+    {
+        case SPEED_GRADE_MAX:
+        case SPEED_GRADE_A:
+            timings_index = 4;
+            break;
+        case SPEED_GRADE_B:
+            timings_index = 3;
+            break;
+        case SPEED_GRADE_C:
+            timings_index  = 1;
+            break;
+        case SPEED_GRADE_AUDIO:
+            timings_index = 2;
+            break;
+        default:
+            timings_index = 0;
+            break;
+    }
+    if (speed_grade != SPEED_GRADE_DEFAULT && speed_grade != SPEED_GRADE_CUSTOM)
+    {
+        g_bluescsi_timings = &current_timings;
+        memcpy(g_bluescsi_timings, &predefined_timings[timings_index], sizeof(current_timings));
+        g_max_sync_10_period = g_bluescsi_timings->scsi_10.max_sync;
+        g_max_sync_20_period = g_bluescsi_timings->scsi_20.max_sync;
+        g_max_sync_5_period = g_bluescsi_timings->scsi_5.max_sync;
+        return true;
+    }
+    return false;
+}

+ 108 - 0
lib/BlueSCSI_platform_RP2040/timings_RP2MCU.h

@@ -0,0 +1,108 @@
+/**
+ * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ *
+ * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+**/
+#ifndef BLUESCSI_TIMINGS_RP2MCU_H
+#define BLUESCSI_TIMINGS_RP2MCU_H
+#include <stdint.h>
+#include <stdbool.h>
+#include <BlueSCSI_config.h>
+
+typedef struct
+{
+    uint32_t clk_hz;
+    struct
+    {
+        // These numbers are for pico-sdk's pll_init() function
+        // their values can be obtained using the script:
+        // "/src/rp2_common/hardware_clocks/scripts/vcocalc.py" 
+        uint8_t refdiv;
+        uint32_t vco_freq;
+        uint8_t post_div1;
+        uint8_t post_div2;
+    } pll;
+    struct
+    {
+        // Delay from data setup to REQ assertion.
+        // deskew delay + cable skew delay = 55 ns minimum
+        // One clock cycle is x ns => delay (55 / x) clocks
+        uint8_t req_delay;
+        // Period of the system clock in pico seconds
+        uint32_t clk_period_ps;
+    } scsi;
+
+
+    // delay0: Data Setup Time - Delay from data write to REQ assertion
+    // delay1  Transmit Assertion time from REQ assert to REQ deassert (req pulse) 
+    // delay2: Negation period - (total_delay - d0 - d1): total_delay spec is the sync value * 4 in ns width)
+    // both values are in clock cycles minus 1 for the pio instruction delay
+    // delay0 spec: Ultra(20):  11.5ns  Fast(10): 23ns  SCSI-1(5): 23ns
+    // delay1 spec: Ultra(20):  16.5ns  Fast(10): 33ns  SCSI-1(5): 53ns 
+    // delay2 spec: Ultra(20):  15ns    Fast(10): 30ns  SCSI-1(5): 80ns 
+    // total_delay_adjust is manual adjustment value, when checked with a scope
+    // Max sync - the minimum sync period ("max" clock rate) that is supported at this clock rate, the number is 1/4 the actual value in ns
+    struct
+    {
+        uint8_t delay0;
+        uint8_t delay1;
+        int16_t total_delay_adjust;
+        uint8_t max_sync;
+    } scsi_20;
+
+    struct
+    {
+        uint8_t delay0;
+        uint8_t delay1;
+        int16_t total_delay_adjust;
+        uint8_t max_sync;
+    } scsi_10;
+
+    struct
+    {
+        uint8_t delay0;
+        uint8_t delay1;
+        int16_t total_delay_adjust;
+        uint8_t max_sync;
+    } scsi_5;
+
+
+    struct
+    {
+        // System clock speed in MHz clk / clk_div_pio
+        uint8_t clk_div_1mhz;
+
+        // System clock speed / clk_div_pio <= 50MHz
+        // At 125Hz, the closest dividers 5 is used for 25 MHz for
+        // stability at that clock speed
+        // The CPU can apply further divider through state machine
+        // registers for the initial handshake.
+        uint8_t clk_div_pio;
+        // clk_div_pio = (delay0 + 1) + (delay1 + 1)
+        // delay1 should be shorter than delay0
+        uint8_t delay0; // subtract one for the instruction delay
+        uint8_t delay1; // clk_div_pio - delay0 and subtract one for the instruction delay
+    } sdio;
+
+} bluescsi_timings_t;
+
+extern  bluescsi_timings_t *g_bluescsi_timings;
+
+// Sets timings to the speed_grade, returns false on SPEED_GRADE_DEFAULT and SPEED_GRADE_CUSTOM
+bool set_timings(bluescsi_speed_grade_t speed_grade);
+#endif // BLUESCSI_TIMINGS_RP2MCU_H

+ 1 - 1
lib/SCSI2SD/include/scsi2sd.h

@@ -96,7 +96,7 @@ typedef enum
 	S2S_CFG_SPEED_ASYNC_50,
 	S2S_CFG_SPEED_SYNC_5,
 	S2S_CFG_SPEED_SYNC_10,
-	S2S_CFG_SPEED_TURBO
+	S2S_CFG_SPEED_SYNC_20
 } S2S_CFG_SPEED;
 
 typedef struct __attribute__((packed))

+ 36 - 25
lib/SCSI2SD/src/firmware/scsi.c

@@ -33,6 +33,7 @@
 #include "network.h"
 #include "tape.h"
 #include "vendor.h"
+#include "timings.h"
 
 #include <string.h>
 
@@ -759,8 +760,16 @@ static void scsiReset()
 
 	for (int i = 0; i < S2S_MAX_TARGETS; ++i)
 	{
-		scsiDev.targets[i].syncOffset = 0;
-		scsiDev.targets[i].syncPeriod = 0;
+		if (g_force_sync > 0)
+		{
+			scsiDev.targets[i].syncPeriod = g_force_sync;
+			scsiDev.targets[i].syncOffset = g_force_offset;
+		}
+		else
+		{
+			scsiDev.targets[i].syncOffset = 0;
+			scsiDev.targets[i].syncPeriod = 0;
+		}
 	}
 	scsiDev.minSyncPeriod = 0;
 
@@ -1101,31 +1110,25 @@ static void process_MessageOut()
 				// data corruption while reading data. We can count the
 				// ACK's correctly, but can't save the data to a register
 				// before it changes. (ie. transferPeriod == 12)
-				if ((scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_TURBO) &&
-					(transferPeriod <= 16))
+				if (scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_SYNC_20 || scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_NoLimit)
 				{
-					scsiDev.target->syncPeriod = 16; // 15.6MB/s
+					if (transferPeriod <= g_max_sync_20_period)
+						scsiDev.target->syncPeriod = g_max_sync_20_period;
+					else
+						scsiDev.target->syncPeriod = transferPeriod;
 				}
-				else if (scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_TURBO)
+				else if (scsiDev.boardCfg.scsiSpeed >= S2S_CFG_SPEED_SYNC_10)
 				{
-					scsiDev.target->syncPeriod = transferPeriod;
+					if (transferPeriod <= g_max_sync_10_period)
+						scsiDev.target->syncPeriod = g_max_sync_10_period;
+					else
+						scsiDev.target->syncPeriod = transferPeriod;
 				}
-				else if (transferPeriod <= 25 &&
-					((scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_NoLimit) ||
-						(scsiDev.boardCfg.scsiSpeed >= S2S_CFG_SPEED_SYNC_10)))
-				{
-					scsiDev.target->syncPeriod = 25; // 100ns, 10MB/s
-
-				} else if (transferPeriod < 50 &&
-					((scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_NoLimit) ||
-						(scsiDev.boardCfg.scsiSpeed >= S2S_CFG_SPEED_SYNC_10)))
-				{
-					scsiDev.target->syncPeriod = transferPeriod;
-				} else if (transferPeriod >= 50)
-				{
-					scsiDev.target->syncPeriod = transferPeriod;
-				} else {
-					scsiDev.target->syncPeriod = 50;
+				else if (scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_SYNC_5) {
+					if (transferPeriod <= g_max_sync_5_period)
+						scsiDev.target->syncPeriod = g_max_sync_5_period;
+					else
+						scsiDev.target->syncPeriod = transferPeriod;
 				}
 			}
 
@@ -1337,8 +1340,16 @@ void scsiInit()
 		scsiDev.targets[i].sense.code = NO_SENSE;
 		scsiDev.targets[i].sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;
 
-		scsiDev.targets[i].syncOffset = 0;
-		scsiDev.targets[i].syncPeriod = 0;
+		if (g_force_sync > 0)
+		{
+			scsiDev.targets[i].syncPeriod = g_force_sync;
+			scsiDev.targets[i].syncOffset = g_force_offset;
+		}
+		else
+		{
+			scsiDev.targets[i].syncOffset = 0;
+			scsiDev.targets[i].syncPeriod = 0;
+		}
 
 		// Always "start" the device. Many systems (eg. Apple System 7)
 		// won't respond properly to

+ 29 - 0
lib/SCSI2SD/src/firmware/timings.h

@@ -0,0 +1,29 @@
+/**
+ * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ *
+ * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+**/
+#ifndef BLUESCSI_SCSI2SD_TIMINGS_H
+#define BLUESCSI_SCSI2SD_TIMINGS_H
+#include <stdint.h>
+extern uint8_t g_max_sync_20_period;
+extern uint8_t g_max_sync_10_period;
+extern uint8_t g_max_sync_5_period;
+extern uint8_t g_force_sync;
+extern uint8_t g_force_offset;
+#endif // BLUESCSI_SCSI2SD_TIMINGS_H

+ 2 - 0
platformio.ini

@@ -51,6 +51,7 @@ build_flags =
     -DPICO_CYW43_ARCH_POLL=1
     -DCYW43_LWIP=0
     -DCYW43_USE_OTP_MAC=0
+    -DARCH_RP2040
 
 [env:BlueSCSI_Pico2]
 board = rpipico2w
@@ -66,6 +67,7 @@ build_flags =
     -DPICO_CYW43_ARCH_POLL=1
     -DCYW43_LWIP=0
     -DCYW43_USE_OTP_MAC=0
+    -DARCH_RP2350
 
 ; Experimental Audio build
 ; Requires separate hardware and overclock.

+ 25 - 0
src/BlueSCSI.cpp

@@ -632,6 +632,31 @@ extern "C" void bluescsi_setup(void)
         setInitiatorModeParityCheck(false);
       }
     }
+    char speed_grade_str[10];
+    static const char sg_default[] = "Default"; 
+    ini_gets("SCSI", "SpeedGrade", sg_default, speed_grade_str, sizeof(speed_grade_str), CONFIGFILE);
+    bluescsi_speed_grade_t grade = platform_string_to_speed_grade(speed_grade_str, sizeof(speed_grade_str));
+    if (grade != SPEED_GRADE_DEFAULT)
+    {
+      bluescsi_reclock_status_t status = platform_reclock(grade);
+      switch (status)
+      {
+        case BLUESCSI_RECLOCK_NOT_SUPPORTED:
+          log("Reclocking this board is not supported");
+          break;
+        case BLUESCSI_RECLOCK_FAILED:
+          log("Reclocking failed");
+          break;
+        case BLUESCSI_RECLOCK_SUCCESS:
+          log("Reclocking was successful");
+          break;
+        case BLUESCSI_RECLOCK_CUSTOM:
+          log("Custom reclocking timings used");
+          break;
+      }
+      g_sdcard_present = mountSDCard();
+      reinitSCSI();
+    }    
     if (SD.clusterCount() == 0)
     {
       log("SD card without filesystem!");

+ 1 - 0
src/BlueSCSI_cdrom.cpp

@@ -31,6 +31,7 @@
 #include "BlueSCSI_cdrom.h"
 #include "BlueSCSI_log.h"
 #include "BlueSCSI_config.h"
+#include "BlueSCSI_platform.h"
 #include "BlueSCSI_cdrom.h"
 #include <CUEParser.h>
 #include <assert.h>

+ 22 - 1
src/BlueSCSI_config.h

@@ -4,7 +4,17 @@
 #pragma once
 
 #include <string.h>
-#include <BlueSCSI_platform.h>
+// #include <BlueSCSI_platform.h>
+
+#define PLATFORM_NAME "BlueSCSI"
+#define PLATFORM_REVISION "2.0"
+#define PLATFORM_TOOLBOX_API 0
+#define PLATFORM_INQUIRY PLATFORM_NAME "v" FW_VER_NUM
+#define PLATFORM_OPTIMAL_MIN_SD_WRITE_SIZE 32768
+#define PLATFORM_OPTIMAL_MAX_SD_WRITE_SIZE 65536
+#define PLATFORM_OPTIMAL_LAST_SD_WRITE_SIZE 8192
+#define SD_USE_SDIO 1
+#define PLATFORM_HAS_INITIATOR_MODE 1
 
 // Use variables for version number
 #define FW_VER_NUM      "2024.12.09"
@@ -84,6 +94,17 @@
 #define PREFETCH_BUFFER_SIZE 8192
 #endif
 
+typedef enum
+{
+    SPEED_GRADE_DEFAULT,
+    SPEED_GRADE_MAX,
+    SPEED_GRADE_CUSTOM,
+    SPEED_GRADE_A,
+    SPEED_GRADE_B,
+    SPEED_GRADE_C,
+    SPEED_GRADE_AUDIO,
+} bluescsi_speed_grade_t;
+
 /**
  * @filename - name of the file to be evaluated for block size
  * @scsiId - ID of the device we're looking to get the block size for

+ 2 - 0
src/BlueSCSI_disk.cpp

@@ -1069,6 +1069,8 @@ void s2s_configInit(S2S_BoardCfg* config)
         config->scsiSpeed = S2S_CFG_SPEED_ASYNC_50;
     else if (maxSyncSpeed < 10 && config->scsiSpeed > S2S_CFG_SPEED_SYNC_5)
         config->scsiSpeed = S2S_CFG_SPEED_SYNC_5;
+    else if (maxSyncSpeed < 20 && config->scsiSpeed > S2S_CFG_SPEED_SYNC_10)
+        config->scsiSpeed = S2S_CFG_SPEED_SYNC_10;
 
     if ((int)config->selectionDelay == defaults.selectionDelay)
     {

+ 10 - 10
src/BlueSCSI_log_trace.cpp

@@ -206,17 +206,17 @@ void scsiLogPhaseChange(int new_phase)
         {
             debuglog("---- Total IN: ", g_InByteCount, " OUT: ", g_OutByteCount, " CHECKSUM: ", (int)g_DataChecksum);
         }
-	// log Xebec vendor command
+	    // log Xebec vendor command
         if (old_phase == DATA_OUT && scsiDev.cdb[0] == 0x0C && g_OutByteCount == 8)
-	{
-		int cylinders = ((uint16_t)scsiDev.data[0] << 8) + scsiDev.data[1];
-		int heads = scsiDev.data[2];
-		int reducedWrite = ((uint16_t)scsiDev.data[3] << 8) + scsiDev.data[4];
-		int writePrecomp = ((uint16_t)scsiDev.data[5] << 8) + scsiDev.data[6];
-		int eccBurst = scsiDev.data[7];
-		debuglog("---- Xebec Initialize Drive Characteristics: cylinders=", cylinders, " heads=", heads,
-				 " reducedWrite=", reducedWrite, " writePrecomp=", writePrecomp, " eccBurst=", eccBurst);
-	}
+	    {
+		    int cylinders = ((uint16_t)scsiDev.data[0] << 8) + scsiDev.data[1];
+		    int heads = scsiDev.data[2];
+		    int reducedWrite = ((uint16_t)scsiDev.data[3] << 8) + scsiDev.data[4];
+		    int writePrecomp = ((uint16_t)scsiDev.data[5] << 8) + scsiDev.data[6];
+		    int eccBurst = scsiDev.data[7];
+		    debuglog("---- Xebec Initialize Drive Characteristics: cylinders=", cylinders, " heads=", heads,
+				     " reducedWrite=", reducedWrite, " writePrecomp=", writePrecomp, " eccBurst=", eccBurst);
+	    }
         g_InByteCount = g_OutByteCount = 0;
         g_DataChecksum = 0;