ソースを参照

Reclocking for RP2040 works as a test case

There are two clock speed that can be reclocked to currently
that seem to work 125MHz at 10MB/s sync and 250MHz at 20MB/s.
Forcing slower sync speeds at 250MHz hasn't been tested.
The Pico and Pico 2 haven't been setup either.

The settings are to test it out are
```
[SCSI]
ReclockInKHz = 250000 # set to 250MHz using KHz
MaxSyncSpeed = 20 # to enable ultra Synchronous SCSI mode
```
Morio 1 年間 前
コミット
c9fcfa1ad3

+ 1 - 1
lib/SCSI2SD/src/firmware/scsi.c

@@ -1106,7 +1106,7 @@ static void process_MessageOut()
 				//The speeds above correspond to syncPeriod values of 25, 18, 15 and 12 (maybe, the last 3 are truncated)
 				//We will set the syncPeriod and syncOffset to the fastest we 
 				//can support if the initiator requests a faster speed
-				if (scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_SYNC_20 || S2S_CFG_SPEED_NoLimit)
+				if (scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_SYNC_20 || scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_NoLimit)
 				{
 					if (transferPeriod <= g_max_sync_20_period)
 						scsiDev.target->syncPeriod = g_max_sync_20_period;

+ 12 - 0
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h

@@ -73,6 +73,8 @@ extern bool g_moved_select_in;
 #define PLATFORM_VDD_WARNING_LIMIT_mV 2800
 #endif
 
+#define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 10
+
 // Debug logging functions
 void platform_log(const char *s);
 
@@ -126,6 +128,16 @@ void platform_poll();
 // This function should return without significantly delay.
 uint8_t platform_get_buttons();
 
+// Attempt to reclock the MCU - unsupported
+typedef enum
+{
+    ZULUSCSI_RECLOCK_SUCCESS,
+    ZULUSCSI_RECLOCK_NOT_SUPPORTED,
+    ZULUSCSI_RECLOCK_FAILED
+} zuluscsi_reclock_status_t;
+zuluscsi_reclock_status_t platform_reclock(uint32_t clk_in_khz){return ZULUSCSI_RECLOCK_NOT_SUPPORTED;}
+
+
 // Reinitialize SD card connection and save log from interrupt context.
 // This can be used in crash handlers.
 void platform_emergency_log_save();

+ 11 - 0
lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform.h

@@ -53,6 +53,7 @@ extern const char *g_platform_name;
 #define PLATFORM_VDD_WARNING_LIMIT_mV 2800
 #endif
 
+#define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 10
 // Debug logging functions
 void platform_log(const char *s);
 
@@ -110,6 +111,16 @@ void platform_poll();
 // This function should return without significantly delay.
 uint8_t platform_get_buttons();
 
+// Attempt to reclock the MCU - unsupported
+typedef enum
+{
+    ZULUSCSI_RECLOCK_SUCCESS,
+    ZULUSCSI_RECLOCK_NOT_SUPPORTED,
+    ZULUSCSI_RECLOCK_FAILED
+} zuluscsi_reclock_status_t;
+zuluscsi_reclock_status_t platform_reclock(uint32_t clk_in_khz){return ZULUSCSI_RECLOCK_NOT_SUPPORTED;}
+
+
 // Reinitialize SD card connection and save log from interrupt context.
 // This can be used in crash handlers.
 void platform_emergency_log_save();

+ 41 - 9
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.cpp

@@ -58,7 +58,7 @@ extern "C" {
 extern bool g_rawdrive_active;
 
 extern "C" {
-
+#include "timings.h"
 const char *g_platform_name = PLATFORM_NAME;
 static bool g_scsi_initiator = false;
 static uint32_t g_flash_chip_size = 0;
@@ -82,11 +82,29 @@ static void gpio_conf(uint gpio, gpio_function_t fn, bool pullup, bool pulldown,
     }
 }
 
+// \todo setup up timing for audio
 #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() {
+    // reset PLL for 135.428571MHz
+    pll_init(pll_sys, 1, 948000000, 7, 1);
+    // 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);
+    clock_configure(clk_peri,
+            0,
+            CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
+            135428571,
+            135428571);
+
+#endif
+
+
+static void reclock() {
     // ensure UART is fully drained before we mess up its clock
     uart_tx_wait_blocking(uart0);
     // switch clk_sys and clk_peri to pll_usb
@@ -101,23 +119,37 @@ 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_zuluscsi_timings.pll.refdiv,
+        g_zuluscsi_timings.pll.vco_freq,
+        g_zuluscsi_timings.pll.post_div1,
+        g_zuluscsi_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_zuluscsi_timings.clk_hz,
+            g_zuluscsi_timings.clk_hz);
     clock_configure(clk_peri,
             0,
             CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
-            135428571,
-            135428571);
+            g_zuluscsi_timings.clk_hz,
+            g_zuluscsi_timings.clk_hz);
     // reset UART for the new clock speed
     uart_init(uart0, 1000000);
 }
-#endif  // ENABLE_AUDIO_OUT
+
+zuluscsi_reclock_status_t platform_reclock(uint32_t clock_in_khz)
+{
+    if (set_timings(clock_in_khz))
+    {
+        reclock();
+        return ZULUSCSI_RECLOCK_SUCCESS;
+    }
+    return ZULUSCSI_RECLOCK_FAILED;
+}
 
 #ifdef HAS_DIP_SWITCHES
 enum pin_setup_state_t  {SETUP_FALSE, SETUP_TRUE, SETUP_UNDETERMINED};

+ 19 - 5
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.h

@@ -64,31 +64,36 @@ extern const char *g_platform_name;
 # define PLATFORM_REVISION "2.0"
 # define PLATFORM_HAS_INITIATOR_MODE 1
 # define DISABLE_SWO
-#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_10
+# define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
+# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 10
 #elif defined(ZULUSCSI_PICO_2)
 # define PLATFORM_NAME "ZuluSCSI Pico 2"
 # define PLATFORM_PID "Pico 2"
 # define PLATFORM_REVISION "2.0"
 # define PLATFORM_HAS_INITIATOR_MODE 1
 # define DISABLE_SWO
-#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
+# define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
+# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 20
 #elif defined(ZULUSCSI_RP2350A)
 # define PLATFORM_NAME "ZuluSCSI RP2350A"
 # define PLATFORM_PID "RP2350A"
 # define PLATFORM_REVISION "2.0"
 # define PLATFORM_HAS_INITIATOR_MODE 1
-#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
+# define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
+# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 20
 #elif defined(ZULUSCSI_BS2)
 # define PLATFORM_NAME "ZuluSCSI BS2"
 # define PLATFORM_PID "BS2"
 # define PLATFORM_REVISION "1.0"
-#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_10
+# define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
+# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 10
 #else
 # define PLATFORM_NAME "ZuluSCSI RP2040"
 # define PLATFORM_PID "RP2040"
 # define PLATFORM_REVISION "2.0"
 # define PLATFORM_HAS_INITIATOR_MODE 1
-#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_10
+# define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
+# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 10
 #endif
 
 #define PLATFORM_OPTIMAL_MIN_SD_WRITE_SIZE 32768
@@ -157,6 +162,15 @@ void platform_poll();
 // This function should return without significantly delay.
 uint8_t platform_get_buttons();
 
+// Attempt to reclock the MCU
+typedef enum
+{
+    ZULUSCSI_RECLOCK_SUCCESS,
+    ZULUSCSI_RECLOCK_NOT_SUPPORTED,
+    ZULUSCSI_RECLOCK_FAILED
+} zuluscsi_reclock_status_t;
+zuluscsi_reclock_status_t platform_reclock(uint32_t clk_in_khz);
+
 // Set callback that will be called during data transfer to/from SD card.
 // This can be used to implement simultaneous transfer to SCSI bus.
 typedef void (*sd_callback_t)(uint32_t bytes_complete);

+ 4 - 0
lib/ZuluSCSI_platform_RP2MCU/scsi2sd_timings.h

@@ -21,6 +21,10 @@
 #ifndef ZULUSCSI_PLATFORM_TIMINGS_H
 #define ZULUSCSI_PLATFORM_TIMINGS_H
 #include <stdint.h>
+
+// This is actually a minimum number
+// 1/4 of the lowest sync period
+// that keeps the scsi interface stable
 extern uint8_t g_max_sync_20_period;
 extern uint8_t g_max_sync_10_period;
 extern uint8_t g_max_sync_5_period;

+ 218 - 87
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target.cpp

@@ -1,38 +1,39 @@
-/** 
+/**
  * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
- * 
+ *
  * This work incorporates work from the following
  *  Copyright (c) 2023 joshua stein <jcs@jcs.org>
- * 
- * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
- * 
+ *
+ * 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. 
- * 
+ * (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. 
- * 
+ * 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/>.
 **/
 
 /* Data flow in SCSI acceleration:
- *
- * 1. Application provides a buffer of bytes to send.
- * 2. Code in this module adds parity bit to the bytes and packs two bytes into 32 bit words.
- * 3. DMA controller copies the words to PIO peripheral FIFO.
- * 4. PIO peripheral handles low-level SCSI handshake and writes bytes and parity to GPIO.
- */
+*
+* 1. Application provides a buffer of bytes to send.
+* 2. Code in this module adds parity bit to the bytes and packs two bytes into 32 bit words.
+* 3. DMA controller copies the words to PIO peripheral FIFO.
+* 4. PIO peripheral handles low-level SCSI handshake and writes bytes and parity to GPIO.
+*/
 
 #include "ZuluSCSI_platform.h"
 #include "ZuluSCSI_log.h"
 #include "scsi_accel_target.h"
+#include "timings.h"
 #include <hardware/pio.h>
 #include <hardware/dma.h>
 #include <hardware/irq.h>
@@ -98,7 +99,7 @@ static struct {
     uint8_t *app_buf; // Buffer provided by application
     uint32_t app_bytes; // Bytes available in application buffer
     uint32_t dma_bytes; // Bytes that have been scheduled for DMA so far
-    
+
     uint8_t *next_app_buf; // Next buffer from application after current one finishes
     uint32_t next_app_bytes; // Bytes in next buffer
 
@@ -123,7 +124,14 @@ static struct {
     pio_sm_config pio_cfg_read;
     pio_sm_config pio_cfg_read_parity;
     pio_sm_config pio_cfg_sync_read_pacer;
-    
+    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
     dma_channel_config dmacfg_write_chB; // Addresses from scsi_parity PIO to lookup DMA
@@ -138,8 +146,8 @@ static struct {
 } g_scsi_dma;
 
 enum scsidma_state_t { SCSIDMA_IDLE = 0,
-                       SCSIDMA_WRITE, SCSIDMA_WRITE_DONE,
-                       SCSIDMA_READ, SCSIDMA_READ_DONE };
+                    SCSIDMA_WRITE, SCSIDMA_WRITE_DONE,
+                    SCSIDMA_READ, SCSIDMA_READ_DONE };
 static const char* scsidma_states[5] = {"IDLE", "WRITE", "WRITE_DONE", "READ", "READ_DONE"};
 static volatile scsidma_state_t g_scsi_dma_state;
 static bool g_channels_claimed = false;
@@ -195,7 +203,7 @@ static void config_parity_sm_for_write()
     pio_sm_put(SCSI_DMA_PIO, SCSI_PARITY_SM, addrbase >> 9);
     pio_sm_exec(SCSI_DMA_PIO, SCSI_PARITY_SM, pio_encode_pull(false, false));
     pio_sm_exec(SCSI_DMA_PIO, SCSI_PARITY_SM, pio_encode_mov(pio_x, pio_osr));
-    
+
     // DMA channel B will copy addresses from parity PIO to DMA channel C read address register.
     // It is triggered by the parity SM RX FIFO request
     dma_channel_configure(SCSI_DMA_CH_B,
@@ -203,7 +211,7 @@ static void config_parity_sm_for_write()
         &dma_hw->ch[SCSI_DMA_CH_C].al3_read_addr_trig,
         &SCSI_DMA_PIO->rxf[SCSI_PARITY_SM],
         1, true);
-    
+
     // DMA channel C will read g_scsi_parity_lookup to copy data + parity to SCSI write state machine.
     // It is triggered by SCSI write machine TX FIFO request and chains to re-enable channel B.
     dma_channel_configure(SCSI_DMA_CH_C,
@@ -237,7 +245,7 @@ static void start_dma_write()
 
     uint8_t *src_buf = &g_scsi_dma.app_buf[g_scsi_dma.dma_bytes];
     g_scsi_dma.dma_bytes += bytes_to_send;
-    
+
     // Start DMA from current buffer to parity generator
     dma_channel_configure(SCSI_DMA_CH_A,
         &g_scsi_dma.dmacfg_write_chA,
@@ -298,7 +306,7 @@ void scsi_accel_rp2040_startWrite(const uint8_t* data, uint32_t count, volatile
     g_scsi_dma.dma_bytes = 0;
     g_scsi_dma.next_app_buf = 0;
     g_scsi_dma.next_app_bytes = 0;
-    
+
     if (must_reconfig_gpio)
     {
         SCSI_ENABLE_DATA_OUT();
@@ -355,7 +363,7 @@ void scsi_accel_rp2040_startWrite(const uint8_t* data, uint32_t count, volatile
             pio_sm_set_enabled(SCSI_DMA_PIO, SCSI_DATA_SM, true);
             pio_sm_set_enabled(SCSI_DMA_PIO, SCSI_PARITY_SM, true);
         }
-        
+
         dma_channel_set_irq0_enabled(SCSI_DMA_CH_A, true);
     }
 
@@ -372,7 +380,7 @@ bool scsi_accel_rp2040_isWriteFinished(const uint8_t* data)
 
     if (!data)
         return false;
-    
+
     // Check if this data item is still in queue.
     bool finished = true;
     uint32_t saved_irq = save_and_disable_interrupts();
@@ -383,7 +391,7 @@ bool scsi_accel_rp2040_isWriteFinished(const uint8_t* data)
         finished = false; // In current transfer
     }
     else if (data >= g_scsi_dma.next_app_buf &&
-             data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
+            data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
     {
         finished = false; // In queued transfer
     }
@@ -491,7 +499,7 @@ static void config_parity_sm_for_read()
     pio_sm_put(SCSI_DMA_PIO, SCSI_DATA_SM, addrbase >> 10);
     pio_sm_exec(SCSI_DMA_PIO, SCSI_DATA_SM, pio_encode_pull(false, false) | pio_encode_sideset(1, 1));
     pio_sm_exec(SCSI_DMA_PIO, SCSI_DATA_SM, pio_encode_mov(pio_y, pio_osr) | pio_encode_sideset(1, 1));
-    
+
     // For synchronous mode, the REQ pin is driven by SCSI_SYNC_SM, so disable it in SCSI_DATA_SM
     if (g_scsi_dma.syncOffset > 0)
     {
@@ -504,7 +512,7 @@ static void config_parity_sm_for_read()
         &SCSI_DMA_PIO->txf[SCSI_PARITY_SM],
         NULL,
         1, false);
-    
+
     // DMA channel C will copy addresses from data PIO to DMA channel B read address register.
     // It is triggered by the data SM RX FIFO request.
     // This triggers channel B by writing to READ_ADDR_TRIG
@@ -549,7 +557,7 @@ static void start_dma_read()
     pio_sm_set_enabled(SCSI_DMA_PIO, SCSI_DATA_SM, false);
     pio_sm_clear_fifos(SCSI_DMA_PIO, SCSI_PARITY_SM);
     pio_sm_clear_fifos(SCSI_DMA_PIO, SCSI_DATA_SM);
-    
+
     if (g_scsi_dma.app_bytes <= g_scsi_dma.dma_bytes)
     {
         // Buffer has been fully processed, swap it
@@ -559,7 +567,7 @@ static void start_dma_read()
         g_scsi_dma.next_app_buf = 0;
         g_scsi_dma.next_app_bytes = 0;
     }
-    
+
     // Check if we are all done.
     // From SCSIDMA_READ_DONE state we can either go to IDLE in stopRead()
     // or back to READ in startWrite().
@@ -584,10 +592,10 @@ static void start_dma_read()
         pio_sm_exec(SCSI_DMA_PIO, SCSI_SYNC_SM, pio_encode_pull(false, false) | pio_encode_sideset(1, 1));
         pio_sm_exec(SCSI_DMA_PIO, SCSI_SYNC_SM, pio_encode_mov(pio_x, pio_osr) | pio_encode_sideset(1, 1));
         hw_set_bits(&SCSI_DMA_PIO->sm[SCSI_SYNC_SM].shiftctrl, PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS);
-        
+
         // Prefill FIFOs to get correct syncOffset
         int prefill = 12 - g_scsi_dma.syncOffset;
-        
+
         // Always at least 1 word to avoid race condition between REQ and ACK pulses
         if (prefill < 1) prefill = 1;
 
@@ -604,7 +612,7 @@ static void start_dma_read()
             pio_sm_exec(SCSI_DMA_PIO, SCSI_SYNC_SM, pio_encode_push(false, false) | pio_encode_sideset(1, 1));
             prefill--;
         }
-        
+
         pio_sm_exec(SCSI_DMA_PIO, SCSI_SYNC_SM, pio_encode_jmp(g_scsi_dma.pio_offset_sync_read_pacer) | pio_encode_sideset(1, 1));
 
         // Start transfers
@@ -715,7 +723,7 @@ bool scsi_accel_rp2040_isReadFinished(const uint8_t* data)
         finished = false; // In current transfer
     }
     else if (data >= g_scsi_dma.next_app_buf &&
-             data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
+            data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
     {
         finished = false; // In queued transfer
     }
@@ -753,13 +761,13 @@ void scsi_accel_rp2040_finishRead(const uint8_t *data, uint32_t count, int *pari
             break;
         }
     }
-    
+
     if (g_scsi_dma_state == SCSIDMA_READ_DONE || *resetFlag)
     {
         // This was last buffer, release bus
         scsi_accel_rp2040_stopRead();
     }
-    
+
     // Check if any parity errors have been detected during the transfer so far
     if (parityError != NULL && (SCSI_DMA_PIO->irq & 1))
     {
@@ -768,6 +776,125 @@ void scsi_accel_rp2040_finishRead(const uint8_t *data, uint32_t count, int *pari
     }
 }
 
+/*******************************************************/
+/* Write SCSI PIO program timings and ACK pin          */
+/*******************************************************/
+static void zulu_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()
+{
+    zulu_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_zuluscsi_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()
+{
+    zulu_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()
+{
+    zulu_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       */
 /*******************************************************/
@@ -868,7 +995,20 @@ 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);
@@ -876,13 +1016,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);
+        zulu_pio_remove_program(SCSI_DMA_PIO, &scsi_parity_program, g_scsi_dma.pio_offset_parity, g_scsi_dma.pio_removed_parity);
+        zulu_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);
+        zulu_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);
+        zulu_pio_remove_program(SCSI_DMA_PIO, &scsi_accel_read_program, g_scsi_dma.pio_offset_read, g_scsi_dma.pio_removed_read);
+        zulu_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);
+        zulu_pio_remove_program(SCSI_DMA_PIO, &scsi_read_parity_program, g_scsi_dma.pio_offset_read_parity, g_scsi_dma.pio_removed_read_parity);
+        zulu_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);
@@ -893,7 +1033,7 @@ void scsi_accel_rp2040_init()
         // Set flag to re-initialize SCSI PIO system
         g_channels_claimed = false;
     }
-    
+
     if (!g_channels_claimed)
     {
         // Mark channels as being in use, unless it has been done already
@@ -906,15 +1046,15 @@ void scsi_accel_rp2040_init()
         dma_channel_claim(SCSI_DMA_CH_D);
         g_channels_claimed = true;
     }
-    
+
     // 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_OUT_REQ);
@@ -922,20 +1062,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_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_OUT_REQ);
-    sm_config_set_out_shift(&g_scsi_dma.pio_cfg_sync_write, true, true, 32);
-    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_OUT_REQ);
@@ -943,18 +1075,27 @@ 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_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);
 
+    // Synchronous SCSI data writer
+    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_OUT_REQ);
+    sm_config_set_out_shift(&g_scsi_dma.pio_cfg_sync_write, true, true, 32);
+    sm_config_set_in_shift(&g_scsi_dma.pio_cfg_sync_write, true, true, 1);
+
+
     // Create DMA channel configurations so they can be applied quickly later
-    
+
     // For write to SCSI BUS:
     // Channel A: Bytes from RAM to scsi_parity PIO
     dma_channel_config cfg = dma_channel_get_default_config(SCSI_DMA_CH_A);
@@ -1033,7 +1174,7 @@ void scsi_accel_rp2040_init()
     channel_config_set_write_increment(&cfg, false);
     channel_config_set_dreq(&cfg, pio_get_dreq(SCSI_DMA_PIO, SCSI_DATA_SM, true));
     g_scsi_dma.dmacfg_read_chD = cfg;
-    
+
     // Interrupts are used for data buffer swapping
     irq_set_exclusive_handler(DMA_IRQ_0, scsi_dma_irq);
     irq_set_enabled(DMA_IRQ_0, true);
@@ -1096,41 +1237,30 @@ bool scsi_accel_rp2040_setSyncMode(int syncOffset, int syncPeriod)
             // delay1: Delay from REQ assert to REQ deassert (req pulse width)
             // delay2: Delay from REQ deassert to data write (data hold)
             int delay0, delay1, delay2, initialDelay, remainderDelay;
-#ifdef ZULUSCSI_MCU_RP23XX
-            int totalDelay = (syncPeriod * 4 * 100 + 333) / 667 ;    //The +333 is equivalent to rounding to the nearest integer
-#else
-            int totalDelay = syncPeriod * 4 / 8;
-#endif            
+            uint32_t up_rounder = g_zuluscsi_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_zuluscsi_timings.scsi.clk_period_ps;
+
             if (syncPeriod < 25)
             {
                 // Fast-20 SCSI timing: 15 ns assertion period
                 // The hardware rise and fall time require some extra delay,
-                // the values below are tuned based on oscilloscope measurements.
                 // These delays are in addition to the 1 cycle that the PIO takes to execute the instruction
-                initialDelay = totalDelay / 3;
-                remainderDelay = totalDelay % 3;    //ReminderDelay will be 0, 1 or 2
-                delay0 = initialDelay - 1; //Data setup time, should be min 11.5ns according to the spec for FAST-20
-                delay2 = initialDelay - 1; //Data hold time, should be min 16.5ns according to the spec for FAST-20
-                delay1 = initialDelay - 1; //pulse width, should be min 15ns according to the spec for FAST-20
-                if (remainderDelay){
-                    delay2++;   //if we have "unassigned" delay time, give it to the one requiring the longest (hold time)
-                    remainderDelay--;
-                }
-                if (remainderDelay){
-                    delay1++;   //if we still have "unassigned" delay time, give it to the one requiring it the next (pulse width)
-                }
+                totalDelay += g_zuluscsi_timings.scsi_20.total_delay_adjust;
+                delay0 = g_zuluscsi_timings.scsi_20.delay0; //Data setup time, should be min 11.5ns according to the spec for FAST-20
+                delay1 = g_zuluscsi_timings.scsi_20.delay1; //Data hold time, should be min 16.5ns according to the spec for FAST-20
+                delay2 = totalDelay - delay0 - delay1 - 3; //pulse width, should be min 15ns according to the spec for FAST-20
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;
             }
             else if (syncPeriod < 50 )
             {
-                // \TODO set RP23XX timings here
-
                 // 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 = 4;
-                delay1 = 6;
+                totalDelay += g_zuluscsi_timings.scsi_10.total_delay_adjust;
+                delay0 = g_zuluscsi_timings.scsi_10.delay0; // 4;
+                delay1 = g_zuluscsi_timings.scsi_10.delay1; // 6;
                 delay2 = totalDelay - delay0 - delay1 - 3;
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;
@@ -1140,8 +1270,9 @@ bool scsi_accel_rp2040_setSyncMode(int syncOffset, int syncPeriod)
                 // \TODO set RP23XX timings here
 
                 // Slow SCSI timing: 90 ns assertion period, 55 ns skew delay
-                delay0 = 7;
-                delay1 = 14;
+                totalDelay += g_zuluscsi_timings.scsi_5.total_delay_adjust;
+                delay0 = g_zuluscsi_timings.scsi_5.delay0;
+                delay1 = g_zuluscsi_timings.scsi_5.delay1;
                 delay2 = totalDelay - delay0 - delay1 - 3;
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;

+ 22 - 21
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2040.pio

@@ -24,13 +24,13 @@
 ; - 0-7: DB0-DB7
 ; -   8: DBP
 ; Side set is REQ pin
-
-.define REQ 9
+; ACK is a dummy value, Will be rewritten on initialization
 .define ACK 10
 
 ; 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
@@ -49,11 +49,11 @@
 .program scsi_accel_async_write
     .side_set 1
 
-    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
-    wait 1 gpio ACK             side 1  ; Wait for ACK to be inactive
-    wait 0 gpio ACK             side 0  ; Assert REQ, wait for ACK low
+    pull ifempty block   side 1      ; Get data from TX FIFO
+    out pins, 9          side 1      ; Write data and parity bit
+    out null, 23         side 1  [0] ;[REQ_DLY-2]      ; 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
 
 ; Read from SCSI bus using sync or async handshake.
 ; Data is returned as 32-bit words:
@@ -73,19 +73,6 @@
     in pins, 9                  side 1  ; Deassert REQ, read GPIO
     in y, 22                    side 1  ; Copy parity lookup table address
 
-; Data state machine for synchronous writes.
-; Takes the lowest 9 bits of each 32 bit word and writes them to bus with REQ pulse.
-; The delay times will be rewritten by C code to match the negotiated SCSI sync speed.
-;
-; Shifts one bit to ISR per every byte transmitted. This is used to control the transfer
-; pace, the RX fifo acts as a counter to keep track of unacknowledged bytes. The C code
-; can set the syncOffset by changing autopush threshold, e.g. threshold 3 = 12 bytes offset.
-.program scsi_sync_write
-    .side_set 1
-
-    out pins, 9      [0]        side 1  ; Write data and parity bit, wait for deskew delay
-    out null, 23     [0]        side 0  ; Assert REQ, wait for assert time
-    in null, 1       [0]        side 1  ; Deassert REQ, wait for transfer period, wait for space in ACK buffer
 
 ; Data pacing state machine for synchronous writes.
 ; Takes one bit from ISR on every falling edge of ACK.
@@ -122,3 +109,17 @@ parity_valid:
     out x, 24                 ; Take the parity valid bit, and the rest of 32-bit word
     jmp x-- parity_valid      ; If parity valid bit is 1, repeat from start
     irq set 0                 ; Parity error, set interrupt flag
+
+; Data state machine for synchronous writes.
+; Takes the lowest 9 bits of each 32 bit word and writes them to bus with REQ pulse.
+; The delay times will be rewritten by C code to match the negotiated SCSI sync speed.
+;
+; Shifts one bit to ISR per every byte transmitted. This is used to control the transfer
+; pace, the RX fifo acts as a counter to keep track of unacknowledged bytes. The C code
+; can set the syncOffset by changing autopush threshold, e.g. threshold 3 = 12 bytes offset.
+.program scsi_sync_write
+    .side_set 1
+
+    out pins, 9      [0]        side 1  ; Write data and parity bit, wait for deskew delay
+    out null, 23     [0]        side 0  ; Assert REQ, wait for assert time
+    in null, 1       [0]        side 1  ; Deassert REQ, wait for transfer period, wait for space in ACK buffer

+ 30 - 31
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2040.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     
     0x308a, //  3: wait   1 gpio, 10      side 1     
     0x200a, //  4: wait   0 gpio, 10      side 0     
             //     .wrap
@@ -103,36 +103,6 @@ static inline pio_sm_config scsi_accel_read_program_get_default_config(uint offs
 }
 #endif
 
-// --------------- //
-// scsi_sync_write //
-// --------------- //
-
-#define scsi_sync_write_wrap_target 0
-#define scsi_sync_write_wrap 2
-
-static const uint16_t scsi_sync_write_program_instructions[] = {
-            //     .wrap_target
-    0x7009, //  0: out    pins, 9         side 1     
-    0x6077, //  1: out    null, 23        side 0     
-    0x5061, //  2: in     null, 1         side 1     
-            //     .wrap
-};
-
-#if !PICO_NO_HARDWARE
-static const struct pio_program scsi_sync_write_program = {
-    .instructions = scsi_sync_write_program_instructions,
-    .length = 3,
-    .origin = -1,
-};
-
-static inline pio_sm_config scsi_sync_write_program_get_default_config(uint offset) {
-    pio_sm_config c = pio_get_default_sm_config();
-    sm_config_set_wrap(&c, offset + scsi_sync_write_wrap_target, offset + scsi_sync_write_wrap);
-    sm_config_set_sideset(&c, 1, false, false);
-    return c;
-}
-#endif
-
 // --------------------- //
 // scsi_sync_write_pacer //
 // --------------------- //
@@ -223,3 +193,32 @@ static inline pio_sm_config scsi_read_parity_program_get_default_config(uint off
 }
 #endif
 
+// --------------- //
+// scsi_sync_write //
+// --------------- //
+
+#define scsi_sync_write_wrap_target 0
+#define scsi_sync_write_wrap 2
+
+static const uint16_t scsi_sync_write_program_instructions[] = {
+            //     .wrap_target
+    0x7009, //  0: out    pins, 9         side 1     
+    0x6077, //  1: out    null, 23        side 0     
+    0x5061, //  2: in     null, 1         side 1     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_sync_write_program = {
+    .instructions = scsi_sync_write_program_instructions,
+    .length = 3,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_sync_write_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_sync_write_wrap_target, offset + scsi_sync_write_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#endif

+ 233 - 76
lib/ZuluSCSI_platform_RP2MCU/timings.c

@@ -1,29 +1,32 @@
-/** 
+/**
  * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
- * 
- * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
- * 
+ *
+ * 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. 
- * 
+ * (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. 
- * 
+ * 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 <string.h>
+#include "scsi2sd_timings.h"
+
 #ifdef ZULUSCSI_MCU_RP23XX
-    zuluscsi_timings_t g_zuluscsi_timings = 
+    zuluscsi_timings_t g_zuluscsi_timings =
     {
         .clk_hz = 150000000,
-        .scsi = 
+        .scsi =
         {
             .delay0 = 0,
             .delay1 = 0,
@@ -31,7 +34,7 @@
             .gpio_ack = 0,
             .gpio_req = 0,
             .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE
-        
+
         },
         .sdio =
         {
@@ -42,87 +45,241 @@
         }
     };
 #else
-    zuluscsi_timings_t g_zuluscsi_timings = 
+    zuluscsi_timings_t g_zuluscsi_timings =
     {
         .clk_hz = 125000000,
-        .scsi = 
+
+        .pll =
         {
-            .delay0 = 0,
-            .delay1 = 0,
-            .req_delay = 0,
+            .refdiv = 1,
+            .vco_freq = 1500000000,
+            .post_div1 = 6,
+            .post_div2 = 2
+        },
+
+        .scsi =
+        {
+            .req_delay = 7,
+            .clk_period_ps = 5000
+        },
+
+        .scsi_20 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+
+        },
+
+        .scsi_10 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+
+        },
+
+        .scsi_5 =
+        {
+            .delay0 = 7,
+            .delay1 = 14,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = -1,
+            .max_sync = 50,
 
-            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE
-        
         },
+
         .sdio =
         {
-            .clk_div_1mhz = 25, // = 125MHz clk / clk_div_pio  
+            .clk_div_1mhz = 25, // = 125MHz clk / clk_div_pio
             .clk_div_pio = 5,
-            .delay0 = 3 - 1, // subtract one for the instruction delay 
+            .delay0 = 3 - 1, // subtract one for the instruction delay
             .delay1 = 2 - 1  // clk_div_pio - delay0 and subtract one for the instruction delay
         }
     };
 #endif
 
-zuluscsi_timings_t predefined_timings[] =
+static zuluscsi_timings_t predefined_timings[] = {
     {
+        .clk_hz = 125000000,
+
+        .pll =
         {
-            .clk_hz = 125000000,
-            .scsi = 
-            {
-                .delay0 = 0,
-                .delay1 = 0,
-                .req_delay = 0,
-                .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE
-            
-            },
-            .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 = 150000000,
-            .scsi = 
-            {
-                .delay0 = 0,
-                .delay1 = 0,
-                .req_delay = 0,
-                .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE
-            
-            },
-            .sdio =
-            {
-                .clk_div_1mhz = 0,
-                .clk_div_pio = 0,
-                .delay0 = 0,
-                .delay1 = 0
-            }
-        },
-        {
-            .clk_hz = 250000000,
-            .scsi = 
-            {
-                .delay0 = 0,
-                .delay1 = 0,
-                .req_delay = 0,
-                .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE
-            
-            },
-            .sdio =
-            {
-                .clk_div_1mhz = 0,
-                .clk_div_pio = 0,
-                .delay0 = 0,
-                .delay1 = 0
-            }
+            .refdiv = 1,
+            .vco_freq = 1500000000,
+            .post_div1 = 6,
+            .post_div2 = 2
+        },
+
+        .scsi =
+        {
+            .req_delay = 7,
+            .clk_period_ps = 5000
+        },
+
+        .scsi_20 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+
+        },
+
+        .scsi_10 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = -1,
+            .max_sync = 25,
+        },
+
+        .scsi_5 =
+        {
+            .delay0 = 7,
+            .delay1 = 14,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .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 = 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,
+            .delay1 = 4,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = 0,
+            .max_sync = 18,
+
+        },
+
+        .scsi_10 =
+        {
+            .delay0 = 4,
+            .delay1 = 6,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = 0,
+            .max_sync = 25,
+
+        },
+
+        .scsi_5 =
+        {
+            .delay0 = 7,
+            .delay1 = 14,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = 0,
+            .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 = 250000000,
 
-void set_timings(uint32_t system_clk)
+        .pll =
+        {
+            .refdiv = 1,
+            .vco_freq = 1500000000,
+            .post_div1 = 6,
+            .post_div2 = 1
+        },
+
+        .scsi =
+        {
+            .req_delay = 14,
+            .clk_period_ps = 4000,
+        },
+
+        .scsi_20 =
+        {
+            .delay0 = 2,
+            .delay1 = 4,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = 1,
+            .max_sync = 12,
+
+        },
+
+        .scsi_10 =
+        {
+            .delay0 = 8,
+            .delay1 = 10,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = 1,
+            .max_sync = 25,
+        },
+
+        .scsi_5 =
+        {
+            .delay0 = 14,
+            .delay1 = 15,
+            .mode = ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
+            .total_delay_adjust = 1,
+            .max_sync = 50,
+        },
+
+        .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
+        }
+    }
+};
+
+bool set_timings(uint32_t target_clk_in_khz)
 {
-;
+    uint32_t number_of_timings = sizeof(predefined_timings)/sizeof( predefined_timings[0]);
+    for (uint8_t i = 0; i < number_of_timings; i++)
+    {
+        if (target_clk_in_khz == predefined_timings[i].clk_hz / 1000)
+        {
+            memcpy(&g_zuluscsi_timings, &predefined_timings[i], sizeof(g_zuluscsi_timings));
+            g_max_sync_10_period = g_zuluscsi_timings.scsi_10.max_sync;
+            g_max_sync_20_period = g_zuluscsi_timings.scsi_20.max_sync;
+            g_max_sync_5_period = g_zuluscsi_timings.scsi_5.max_sync;
+            return true;
+        }
+    }
+    return false;
 }

+ 68 - 17
lib/ZuluSCSI_platform_RP2MCU/timings.h

@@ -1,33 +1,31 @@
-/** 
+/**
  * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
- * 
- * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
- * 
+ *
+ * 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. 
- * 
+ * (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. 
- * 
+ * 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 ZULUSCSI_RP2MCU_TIMINGS_H
 #define ZULUSCSI_RP2MCU_TIMINGS_H
 #include <stdint.h>
-
+#include <stdbool.h>
 typedef enum
 {
     ZULUSCSI_PIO_TARGET_MODE_SIMPLE,
-    ZULUSCSI_PIO_TARGET_MODE_EXTRA_DELAY1,
-    ZULUSCSI_PIO_TARGET_MODE_EXTRA_DELAY2,
-    ZULUSCSI_PIO_TARGET_MODE_EXTRA_DELAY_1AND2
+    ZULUSCSI_PIO_TARGET_MODE_EXTRA_DELAY,
 } zuluscsi_pio_target_mode_t;
 
 typedef struct
@@ -35,22 +33,75 @@ typedef struct
     uint32_t clk_hz;
     struct
     {
+        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: Delay from data write to REQ assertion (data setup)
+    // delay1: Delay from REQ assert to REQ deassert (req pulse width)
+    // both values are in clock cycles minus 1 for the pio instruction delay
+    // total_delay_adjust is manual adjustment value, when checked with a scope
+    // Max sync - the max sync period that is supported at this clock rate, the number is 1/4 the actual value in ns
+    struct
+    {
         zuluscsi_pio_target_mode_t mode;
         uint8_t delay0;
         uint8_t delay1;
-    } scsi;
+        int16_t total_delay_adjust;
+        uint8_t max_sync;
+    } scsi_20;
 
-    struct 
+    struct
     {
-        uint8_t clk_div_1mhz;
-        uint8_t clk_div_pio;
+        zuluscsi_pio_target_mode_t mode;
         uint8_t delay0;
         uint8_t delay1;
+        int16_t total_delay_adjust;
+        uint8_t max_sync;
+    } scsi_10;
+
+    struct
+    {
+        zuluscsi_pio_target_mode_t mode;
+        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;
 
 } zuluscsi_timings_t;
 
 extern  zuluscsi_timings_t g_zuluscsi_timings;
-void set_timings(uint32_t system_clk);
+
+bool set_timings(uint32_t target_clk_in_khz);
 #endif // ZULUSCSI_RP2MCU_TIMINGS_H

+ 19 - 0
src/ZuluSCSI.cpp

@@ -850,6 +850,25 @@ static void zuluscsi_setup_sd_card()
 
   if (g_sdcard_present)
   {
+    int32_t clock_khz = ini_getl("SCSI", "ReclockInKHz", 0, CONFIGFILE);
+    if ( clock_khz > 0)
+    {
+      zuluscsi_reclock_status_t status = platform_reclock(clock_khz);
+      switch (status)
+      {
+        case ZULUSCSI_RECLOCK_NOT_SUPPORTED:
+          logmsg("Reclocking this board is not supported");
+          break;
+        case ZULUSCSI_RECLOCK_FAILED:
+          logmsg("Reclocking at ", (int) clock_khz , " KHz is not supported");
+          break;
+        case ZULUSCSI_RECLOCK_SUCCESS:
+          logmsg("Reclocking at ", (int) clock_khz , " KHz was successful");
+      }
+      g_sdcard_present = mountSDCard();
+      reinitSCSI();
+    }
+
     if (SD.clusterCount() == 0)
     {
       logmsg("SD card without filesystem!");

+ 2 - 0
src/ZuluSCSI_disk.cpp

@@ -1071,6 +1071,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;
 
     logmsg("-- SelectionDelay = ", (int)config->selectionDelay);
 

+ 2 - 1
src/ZuluSCSI_settings.cpp

@@ -26,6 +26,7 @@
 #include "ZuluSCSI_log.h"
 #include "ZuluSCSI_config.h"
 #include "ZuluSCSI_settings.h"
+#include "ZuluSCSI_platform.h"
 #include <strings.h>
 #include <minIni.h>
 #include <minIni_cache.h>
@@ -284,7 +285,7 @@ scsi_system_settings_t *ZuluSCSISettings::initSystem(const char *presetName)
     // Default settings for host compatibility 
     cfgSys.quirks = img.quirks;
     cfgSys.selectionDelay = 255;
-    cfgSys.maxSyncSpeed = 10;
+    cfgSys.maxSyncSpeed = PLATFORM_DEFAULT_SCSI_SPEED_SETTING;
     cfgSys.initPreDelay = 0;
     cfgSys.initPostDelay = 0;
     cfgSys.phyMode = 0;

+ 8 - 2
zuluscsi.ini

@@ -1,4 +1,5 @@
 
+# See http://zuluscsi.com/manual for more information about setting up your ZuluSCSI
 [SCSI]
 # Settings that apply to all SCSI IDs
 
@@ -26,10 +27,15 @@
 #EnableSelLatch = 0 # For Philips P2000C and other devices that release SEL signal before BSY
 #EnableParity = 1 # Enable parity checks on platforms that support it (RP2040)
 #MapLunsToIDs = 0 # For Philips P2000C simulate multiple LUNs
-#MaxSyncSpeed = 10 # Set to 5 or 10 to enable synchronous SCSI mode, 0 to disable
 #InitPreDelay = 0  # How many milliseconds to delay before the SCSI interface is initialized
 #InitPostDelay = 0 # How many milliseconds to delay after the SCSI interface is initialized
 
+# Will attempt to reclock the board if the board supports both reclocking and the choosen speed
+# ReclockInKHz = 0 # Default is zero, ignore reclocking
+# When reclocking the board MaxSyncSpeed may need to be increased, see manual for details
+# MaxSyncSpeed = 10 # Set to 5, 10, or 20 to enable synchronous SCSI mode, 0 to disable
+
+
 # ROM settings
 #DisableROMDrive = 1 # Disable the ROM drive if it has been loaded to flash
 #ROMDriveSCSIID = 7 # Override ROM drive's SCSI ID
@@ -69,7 +75,7 @@
 # Mount SD card as a mass storage device before entering SCSI emulator
 # EnableUSBMassStorage = 1 # setting this to one will mount the SD card if the USB cord is plugged in
 # USBMassStorageWaitPeriod = 1000 # Number of milliseconds the device will wait for the host to mount SD card
-#   May need to be increased if coneneted through a USB hub
+#   May need to be increased if connected through a USB hub
 
 # SCSI DaynaPORT settings
 #WiFiSSID = "Wifi SSID string"