ソースを参照

Merge pull request #467 from ZuluSCSI/feature/merge-rp2xxx-libs

Merge the RP2350 and the RP2040 base MCU platform libraries into a common library
Alex Perez 1 年間 前
コミット
5c620ac726
75 ファイル変更3022 行追加205 行削除
  1. 16 16
      .github/workflows/firmware_build.yml
  2. 55 0
      boards/rpipico.json
  3. 57 0
      boards/rpipico2.json
  4. 55 0
      boards/rpipicow.json
  5. 6 6
      boards/zuluscsi_rp2040.json
  6. 55 0
      boards/zuluscsi_rp2350A.json
  7. 2 2
      lib/SCSI2SD/src/firmware/log.h
  8. 1 1
      lib/SCSI2SD/src/firmware/network.c
  9. 15 7
      lib/SCSI2SD/src/firmware/scsi.c
  10. 13 3
      lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform_msc.cpp
  11. 13 3
      lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform_msc.cpp
  12. 22 10
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.cpp
  13. 25 1
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.h
  14. 0 0
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_BS2.h
  15. 0 0
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Pico.h
  16. 172 0
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Pico_2.h
  17. 0 0
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_RP2040.h
  18. 170 0
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_RP2350A.h
  19. 22 6
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_msc.cpp
  20. 0 0
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_msc.h
  21. 0 0
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_network.cpp
  22. 0 0
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_network.h
  23. 0 0
      lib/ZuluSCSI_platform_RP2MCU/audio.cpp
  24. 0 0
      lib/ZuluSCSI_platform_RP2MCU/audio.h
  25. 0 0
      lib/ZuluSCSI_platform_RP2MCU/bsp.h
  26. 25 8
      lib/ZuluSCSI_platform_RP2MCU/program_flash.cpp
  27. 0 1
      lib/ZuluSCSI_platform_RP2MCU/rp2040-template.ld
  28. 0 0
      lib/ZuluSCSI_platform_RP2MCU/rp2040_btldr.ld
  29. 352 0
      lib/ZuluSCSI_platform_RP2MCU/rp23xx-template.ld
  30. 307 0
      lib/ZuluSCSI_platform_RP2MCU/rp23xx_btldr.ld
  31. 6 3
      lib/ZuluSCSI_platform_RP2MCU/run_pioasm.sh
  32. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi2sd_time.h
  33. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.cpp
  34. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.h
  35. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsiPhy.cpp
  36. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsiPhy.h
  37. 10 6
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.cpp
  38. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.h
  39. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_Pico.pio
  40. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_Pico.pio.h
  41. 46 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_Pico_2.pio
  42. 43 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_Pico_2.pio.h
  43. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2040.pio
  44. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2040.pio.h
  45. 46 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2350A.pio
  46. 44 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2350A.pio.h
  47. 55 23
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target.cpp
  48. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target.h
  49. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_Pico.pio
  50. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_Pico.pio.h
  51. 124 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_Pico_2.pio
  52. 224 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_Pico_2.pio.h
  53. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2040.pio
  54. 0 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2040.pio.h
  55. 125 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2350A.pio
  56. 224 0
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2350A.pio.h
  57. 2 3
      lib/ZuluSCSI_platform_RP2MCU/sd_card_sdio.cpp
  58. 0 0
      lib/ZuluSCSI_platform_RP2MCU/sd_card_spi.cpp
  59. 12 7
      lib/ZuluSCSI_platform_RP2MCU/sdio.cpp
  60. 0 0
      lib/ZuluSCSI_platform_RP2MCU/sdio.h
  61. 0 0
      lib/ZuluSCSI_platform_RP2MCU/sdio_Pico.pio
  62. 0 0
      lib/ZuluSCSI_platform_RP2MCU/sdio_Pico.pio.h
  63. 164 0
      lib/ZuluSCSI_platform_RP2MCU/sdio_Pico_2.pio
  64. 121 0
      lib/ZuluSCSI_platform_RP2MCU/sdio_Pico_2.pio.h
  65. 0 0
      lib/ZuluSCSI_platform_RP2MCU/sdio_RP2040.pio
  66. 0 0
      lib/ZuluSCSI_platform_RP2MCU/sdio_RP2040.pio.h
  67. 164 0
      lib/ZuluSCSI_platform_RP2MCU/sdio_RP2350A.pio
  68. 121 0
      lib/ZuluSCSI_platform_RP2MCU/sdio_RP2350A.pio.h
  69. 95 92
      platformio.ini
  70. 3 2
      src/ZuluSCSI_config.h
  71. 0 3
      src/ZuluSCSI_msc.h
  72. 2 0
      src/ZuluSCSI_settings.cpp
  73. 1 0
      src/ZuluSCSI_settings.h
  74. 2 2
      src/process-linker-script.py
  75. 5 0
      zuluscsi.ini

+ 16 - 16
.github/workflows/firmware_build.yml

@@ -7,10 +7,10 @@ on:
 
 jobs:
   build_firmware:
-    name: RHC-internal-Z4
-    runs-on: self-hosted
-#    name: Build firmware on Ubuntu 20.04
-#    runs-on: ubuntu-20.04
+#    name: RHC-internal-Z4
+#    runs-on: self-hosted
+    name: Build firmware on GitHub using latest Ubuntu
+    runs-on: ubuntu-latest
     
     steps:
       - name: Check out code from GitHub
@@ -39,18 +39,18 @@ jobs:
           path: ZuluSCSI/distrib/*
           name: ZuluSCSI binaries
       
-      - name: Upload to latest release
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        if: github.ref == 'refs/heads/main'
-        run: |
-          cd ZuluSCSI
-          git tag -d latest
-          git tag latest
-          git push origin --force latest
-          cd distrib
-          gh api repos/${GITHUB_REPOSITORY}/releases/tags/latest | jq -r '.assets[] | [.url] | @tsv' | xargs -n 1 gh api -X DELETE || true
-          gh release upload --repo ${GITHUB_REPOSITORY} --clobber latest *
+      # - name: Upload to latest release
+      #   env:
+      #     GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      #   if: github.ref == 'refs/heads/main'
+      #   run: |
+      #     cd ZuluSCSI
+      #     git tag -d latest
+      #     git tag latest
+      #     git push origin --force latest
+      #     cd distrib
+      #     gh api repos/${GITHUB_REPOSITORY}/releases/tags/latest | jq -r '.assets[] | [.url] | @tsv' | xargs -n 1 gh api -X DELETE || true
+      #     gh release upload --repo ${GITHUB_REPOSITORY} --clobber latest *
 
       - name: Upload to newly created release
         env:

+ 55 - 0
boards/rpipico.json

@@ -0,0 +1,55 @@
+{
+    "build": {
+        "arduino": {
+            "earlephilhower": {
+                "boot2_source": "boot2_w25q080_2_padded_checksum.S",
+                "usb_vid": "0x2E8A",
+                "usb_pid": "0x000A"
+            }
+        },
+        "core": "earlephilhower",
+        "cpu": "cortex-m0plus",
+        "extra_flags": "-DARDUINO_RASPBERRY_PI_PICO -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250 ",
+        "f_cpu": "133000000L",
+        "hwids": [
+            [
+                "0x2E8A",
+                "0x00C0"
+            ],
+            [
+                "0x2E8A",
+                "0x000A"
+            ]
+        ],
+        "mcu": "rp2040",
+        "variant": "rpipico"
+    },
+    "debug": {
+        "jlink_device": "RP2040_M0_0",
+        "openocd_target": "rp2040.cfg",
+        "svd_path": "rp2040.svd"
+    },
+    "frameworks": [
+        "arduino"
+    ],
+    "name": "ZuluSCSI Pico",
+    "upload": {
+        "maximum_ram_size": 262144,
+        "maximum_size": 2097152,
+        "require_upload_port": true,
+        "native_usb": true,
+        "use_1200bps_touch": true,
+        "wait_for_upload_port": false,
+        "protocol": "cmsis-dap",
+        "protocols": [
+            "blackmagic",
+            "cmsis-dap",
+            "jlink",
+            "raspberrypi-swd",
+            "picotool",
+            "picoprobe"
+        ]
+    },
+    "url": "http://zuluscsi.com",
+    "vendor": "Rabbit Hole Computing LLC"
+}

+ 57 - 0
boards/rpipico2.json

@@ -0,0 +1,57 @@
+{
+    "build": {
+        "arduino": {
+            "earlephilhower": {
+                "boot2_source": "none.S",
+                "usb_vid": "0x2E8A",
+                "usb_pid": "0x000F"
+            }
+        },
+        "core": "earlephilhower",
+        "cpu": "cortex-m33",
+        "extra_flags": "-DARDUINO_RASPBERRY_PI_PICO_2 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250 ",
+        "f_cpu": "150000000L",
+        "hwids": [
+            [
+                "0x2E8A",
+                "0x00C0"
+            ],
+            [
+                "0x2E8A",
+                "0x000F"
+            ]
+        ],
+        "mcu": "rp2350",
+        "variant": "rpipico2"
+    },
+    "debug": {
+        "jlink_device": "RP2350_0",
+        "openocd_target": "rp2350.cfg",
+        "svd_path": "rp2350.svd"
+    },
+    "frameworks": [
+        "arduino"
+    ],
+    "name": "ZuluSCSI Pico 2",
+    "upload": {
+        "psram_length": 0,
+        "maximum_ram_size": 524288,
+        "maximum_size": 4194304,
+        "require_upload_port": true,
+        "native_usb": true,
+        "use_1200bps_touch": true,
+        "wait_for_upload_port": false,
+        "protocol": "cmsis-dap",
+        "protocols": [
+            "blackmagic",
+            "cmsis-dap",
+            "jlink",
+            "raspberrypi-swd",
+            "picotool",
+            "picoprobe",
+            "pico-debug"
+        ]
+    },
+    "url": "http://zuluscsi.com",
+    "vendor": "Rabbit Hole Computing LLC"
+}

+ 55 - 0
boards/rpipicow.json

@@ -0,0 +1,55 @@
+{
+    "build": {
+        "arduino": {
+            "earlephilhower": {
+                "boot2_source": "boot2_w25q080_2_padded_checksum.S",
+                "usb_vid": "0x2E8A",
+                "usb_pid": "0xF00A"
+            }
+        },
+        "core": "earlephilhower",
+        "cpu": "cortex-m0plus",
+        "extra_flags": "-DARDUINO_RASPBERRY_PI_PICO_W -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=250 ",
+        "f_cpu": "133000000L",
+        "hwids": [
+            [
+                "0x2E8A",
+                "0x00C0"
+            ],
+            [
+                "0x2E8A",
+                "0xF00A"
+            ]
+        ],
+        "mcu": "rp2040",
+        "variant": "rpipicow"
+    },
+    "debug": {
+        "jlink_device": "RP2040_M0_0",
+        "openocd_target": "rp2040.cfg",
+        "svd_path": "rp2040.svd"
+    },
+    "frameworks": [
+        "arduino"
+    ],
+    "name": "ZuluSCSI Pico DaynaPORT",
+    "upload": {
+        "maximum_ram_size": 262144,
+        "maximum_size": 2097152,
+        "require_upload_port": true,
+        "native_usb": true,
+        "use_1200bps_touch": true,
+        "wait_for_upload_port": false,
+        "protocol": "cmsis-dap",
+        "protocols": [
+            "blackmagic",
+            "cmsis-dap",
+            "jlink",
+            "raspberrypi-swd",
+            "picotool",
+            "picoprobe"
+        ]
+    },
+    "url": "http://zuluscsi.com",
+    "vendor": "Rabbit Hole Computing LLC"
+}

+ 6 - 6
boards/zuluscsi_rp2040.json

@@ -9,7 +9,7 @@
       },
       "core": "earlephilhower",
       "cpu": "cortex-m0plus",
-      "extra_flags": "-D ARDUINO_RHC_ZULUSCSI_RP2040 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500",
+      "extra_flags": "-DARDUINO_GENERIC_RP2040 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500 ",
       "f_cpu": "125000000L",
       "hwids": [
         [
@@ -22,7 +22,7 @@
         ]
       ],
       "mcu": "rp2040",
-      "variant": "zuluscsi_rp2040"
+      "variant": "generic"
     },
     "debug": {
       "jlink_device": "RP2040_M0_0",
@@ -32,7 +32,7 @@
     "frameworks": [
       "arduino"
     ],
-    "name": "ZuluSCSI",
+    "name": "ZuluSCSI RP2040",
     "upload": {
       "maximum_ram_size": 270336,
       "maximum_size": 2097152,
@@ -40,7 +40,7 @@
       "native_usb": true,
       "use_1200bps_touch": true,
       "wait_for_upload_port": false,
-      "protocol": "picotool",
+      "protocol": "cmsis-dap",
       "protocols": [
         "blackmagic",
         "cmsis-dap",
@@ -51,7 +51,7 @@
         "pico-debug"
       ]
     },
-    "url": "https://www.raspberrypi.org/products/raspberry-pi-pico/",
-    "vendor": "Rabbit Hole Computing"
+    "url": "http://zuluscsi.com",
+    "vendor": "Rabbit Hole Computing LLC"
   }
   

+ 55 - 0
boards/zuluscsi_rp2350A.json

@@ -0,0 +1,55 @@
+{
+    "build": {
+        "arduino": {
+            "earlephilhower": {
+                "boot2_source": "none.S",
+                "usb_vid": "0x2E8A",
+                "usb_pid": "0xF00F"
+            }
+        },
+        "core": "earlephilhower",
+        "cpu": "cortex-m33",
+        "extra_flags": "-DARDUINO_GENERIC_RP2350 -DARDUINO_ARCH_RP2040 -DUSBD_MAX_POWER_MA=500 ",
+        "f_cpu": "150000000L",
+        "hwids": [
+            [
+                "0x2E8A",
+                "0x00C0"
+            ],
+            [
+                "0x2E8A",
+                "0xF00F"
+            ]
+        ],
+        "mcu": "rp2350",
+        "variant": "generic_rp2350"
+    },
+    "debug": {
+        "jlink_device": "RP2350_0",
+        "openocd_target": "rp2350.cfg",
+        "svd_path": "rp2350.svd"
+    },
+    "frameworks": [
+        "arduino"
+    ],
+    "name": "ZuluSCSI RP2350A",
+    "upload": {
+        "maximum_ram_size": 524288,
+        "maximum_size": 16777216,
+        "require_upload_port": true,
+        "native_usb": true,
+        "use_1200bps_touch": true,
+        "wait_for_upload_port": false,
+        "protocol": "cmsis-dap",
+        "protocols": [
+            "blackmagic",
+            "cmsis-dap",
+            "jlink",
+            "raspberrypi-swd",
+            "picotool",
+            "picoprobe"
+        ]
+    },
+    "url": "http://zuluscsi.com",
+    "vendor": "Rabbit Hole Computing LLC"
+}

+ 2 - 2
lib/SCSI2SD/src/firmware/log.h

@@ -33,9 +33,9 @@ extern void dbgmsg_buf(const unsigned char *buf, unsigned long size);
 extern void dbgmsg_f(const char *format, ...);
 
 #define DBGMSG_BUF(buf, size) dbgmsg_buf(buf, size)
-#define DBGMSG_F(format, ...) dbgmsg_f(format, __VA_ARGS__);
+#define DBGMSG_F(format, ...) dbgmsg_f(format, __VA_ARGS__)
 #define LOGMSG_BUF(buf, size) logmsg_buf(buf, size)
-#define LOGMSG_F(format, ...) logmsg_f(format, __VA_ARGS__);
+#define LOGMSG_F(format, ...) logmsg_f(format, __VA_ARGS__)
 
 #else
 

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

@@ -375,7 +375,7 @@ int scsiNetworkCommand()
 				int size = sizeof(struct wifi_network_entry) * nets;
 				if (size + 2 > sizeof(scsiDev.data))
 				{
-					LOGMSG_F("WARNING: wifi_network_list is bigger than scsiDev.data, truncating");
+					LOGMSG_F("WARNING: wifi_network_list is bigger than scsiDev.data, truncating", 0);
 					size = sizeof(scsiDev.data) - 2;
 					size -= (size % (sizeof(struct wifi_network_entry)));
 				}

+ 15 - 7
lib/SCSI2SD/src/firmware/scsi.c

@@ -976,7 +976,7 @@ static void process_MessageOut()
 	}
 	else if (scsiDev.msgOut == 0x05)
 	{
-		// Initiate Detected Error
+		// Initiator Detected Error
 		// Ignore for now
 	}
 	else if (scsiDev.msgOut == 0x0F)
@@ -992,7 +992,7 @@ static void process_MessageOut()
 	}
 	else if (scsiDev.msgOut == MSG_REJECT)
 	{
-		// Message Reject
+		// Message Rejected
 		// Oh well.
 
 		if (wasNeedSyncNegotiationAck)
@@ -1046,7 +1046,7 @@ static void process_MessageOut()
 	{
 		int i;
 
-		// Extended message.
+		// Extended message. These include speed negotiation
 		int msgLen = scsiReadByte();
 		if (msgLen == 0) msgLen = 256;
 		uint8_t extmsg[256];
@@ -1072,8 +1072,8 @@ static void process_MessageOut()
 			int oldPeriod = scsiDev.target->syncPeriod;
 			int oldOffset = scsiDev.target->syncOffset;
 
-			int transferPeriod = extmsg[1];
-			int offset = extmsg[2];
+			int transferPeriod = extmsg[1];	//This is actually 1/4 of the duration of transfer speed in ns
+			int offset = extmsg[2];	//Req/Ack offset
 
 			if ((
 					(transferPeriod > 0) &&
@@ -1100,10 +1100,15 @@ 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)
+				//****TODO Try to get higher speeds
+				//The Adaptec AHA2940-UW seems to support 10, 13.4, 16 and 20 MHz
+				//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_TURBO) &&
-					(transferPeriod <= 16))
+					(transferPeriod <= 18))
 				{
-					scsiDev.target->syncPeriod = 16; // 15.6MB/s
+					scsiDev.target->syncPeriod = 18; // 20 corresponds to 12.5 MB/s
 				}
 				else if (scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_TURBO)
 				{
@@ -1128,6 +1133,9 @@ static void process_MessageOut()
 				}
 			}
 
+			//Reply back with negotiation speed (if different from previous one)
+			//Reply should be the same as request (if we support it) or slower if we don't
+			//Slower in this context means a higher syncPeriod or lower syncOffset 
 			if (transferPeriod != oldPeriod ||
 				scsiDev.target->syncPeriod != oldPeriod ||
 				offset != oldOffset ||

+ 13 - 3
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform_msc.cpp

@@ -25,6 +25,7 @@
 #include "ZuluSCSI_platform.h"
 #include "ZuluSCSI_log.h"
 #include "ZuluSCSI_msc.h"
+#include "ZuluSCSI_config.h"
 #include "ZuluSCSI_settings.h"
 #include "usb_serial.h"
 
@@ -93,12 +94,20 @@ bool platform_sense_msc() {
 
   logmsg("Waiting for USB enumeration to expose SD card as a mass storage device");
 
-  // wait for up to a second to be begin to be enumerated
+  // wait to be begin to be enumerated
   uint32_t start = millis();
-  while ((uint32_t)(millis() - start) < CR_ENUM_TIMEOUT)
+  uint16_t usb_timeout =  g_scsi_settings.getSystem()->usbMassStorageWaitPeriod;
+  while ((uint32_t)(millis() - start) < usb_timeout)
+  {
     if (cdc_acm.dev.cur_status >= USBD_ADDRESSED)
+    {
+      dbgmsg("USB enumeration took ", (int)((uint32_t)(millis() - start)), "ms");
       return true;
+    }
+  }
 
+  logmsg("Waiting for USB enumeration timed out after ", usb_timeout, "ms.");
+  logmsg("-- Try increasing 'USBMassStorageWaitPeriod' in the ", CONFIGFILE);
   //if not, disconnect MSC class...
   usbd_disconnect (&cdc_acm);
 
@@ -114,7 +123,8 @@ void platform_enter_msc() {
 
   // give the host a moment to finish enumerate and "load" media
   uint32_t start = millis();
-  while ((USBD_CONFIGURED != cdc_acm.dev.cur_status) && ((uint32_t)(millis() - start) < CR_ENUM_TIMEOUT) ) 
+  uint16_t usb_timeout =  g_scsi_settings.getSystem()->usbMassStorageWaitPeriod;
+  while ((USBD_CONFIGURED != cdc_acm.dev.cur_status) && ((uint32_t)(millis() - start) < usb_timeout ) ) 
     delay(100);
 }
 

+ 13 - 3
lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform_msc.cpp

@@ -25,6 +25,7 @@
 #include "ZuluSCSI_platform.h"
 #include "ZuluSCSI_log.h"
 #include "ZuluSCSI_msc.h"
+#include "ZuluSCSI_config.h"
 #include "ZuluSCSI_settings.h"
 #include "usb_serial.h"
 
@@ -93,12 +94,20 @@ bool platform_sense_msc() {
 
   logmsg("Waiting for USB enumeration to expose SD card as a mass storage device");
 
-  // wait for up to a second to be begin to be enumerated
+  // wait to be begin to be enumerated
   uint32_t start = millis();
-  while ((uint32_t)(millis() - start) < CR_ENUM_TIMEOUT)
+  uint16_t usb_timeout =  g_scsi_settings.getSystem()->usbMassStorageWaitPeriod;
+  while ((uint32_t)(millis() - start) < usb_timeout)
+  {
     if (cdc_acm.dev.cur_status >= USBD_ADDRESSED)
+    {
+      dbgmsg("USB enumeration took ", (int)((uint32_t)(millis() - start)), "ms");
       return true;
+    }
+  }
 
+  logmsg("Waiting for USB enumeration timed out after ", usb_timeout, "ms.");
+  logmsg("-- Try increasing 'USBMassStorageWaitPeriod' in the ", CONFIGFILE);
   //if not, disconnect MSC class...
   usbd_disconnect (&cdc_acm);
 
@@ -114,7 +123,8 @@ void platform_enter_msc() {
 
   // give the host a moment to finish enumerate and "load" media
   uint32_t start = millis();
-  while ((USBD_CONFIGURED != cdc_acm.dev.cur_status) && ((uint32_t)(millis() - start) < CR_ENUM_TIMEOUT) ) 
+  uint16_t usb_timeout =  g_scsi_settings.getSystem()->usbMassStorageWaitPeriod;
+  while ((USBD_CONFIGURED != cdc_acm.dev.cur_status) && ((uint32_t)(millis() - start) < usb_timeout ) ) 
     delay(100);
 }
 

+ 22 - 10
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.cpp → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.cpp

@@ -35,6 +35,7 @@
 #include <hardware/flash.h>
 #include <hardware/structs/xip_ctrl.h>
 #include <hardware/structs/usb.h>
+#include <hardware/sync.h>
 #include "scsi_accel_target.h"
 
 #ifndef PIO_FRAMEWORK_ARDUINO_NO_USB
@@ -68,7 +69,7 @@ static bool g_uart_initialized = false;
 /***************/
 
 // Helper function to configure whole GPIO in one line
-static void gpio_conf(uint gpio, enum gpio_function fn, bool pullup, bool pulldown, bool output, bool initial_state, bool fast_slew)
+static void gpio_conf(uint gpio, gpio_function_t fn, bool pullup, bool pulldown, bool output, bool initial_state, bool fast_slew)
 {
     gpio_put(gpio, initial_state);
     gpio_set_dir(gpio, output);
@@ -77,7 +78,7 @@ static void gpio_conf(uint gpio, enum gpio_function fn, bool pullup, bool pulldo
 
     if (fast_slew)
     {
-        padsbank0_hw->io[gpio] |= PADS_BANK0_GPIO0_SLEWFAST_BITS;
+        pads_bank0_hw->io[gpio] |= PADS_BANK0_GPIO0_SLEWFAST_BITS;
     }
 }
 
@@ -187,7 +188,7 @@ void platform_init()
     bool working_dip = true;
     bool dbglog = false;
     bool termination = false;
-# ifdef ZULUSCSI_PICO
+# if defined(ZULUSCSI_PICO) || defined(ZULUSCSI_PICO_2)
     // Initiator dip setting works on all rev 2023b, 2023c, and newer rev Pico boards
     g_scsi_initiator = !gpio_get(DIP_INITIATOR);
     
@@ -253,9 +254,9 @@ void platform_init()
     // Get flash chip size
     uint8_t cmd_read_jedec_id[4] = {0x9f, 0, 0, 0};
     uint8_t response_jedec[4] = {0};
-    __disable_irq();
+    uint32_t saved_irq = save_and_disable_interrupts();
     flash_do_cmd(cmd_read_jedec_id, response_jedec, 4);
-    __enable_irq();
+    restore_interrupts(saved_irq);
     g_flash_chip_size = (1 << response_jedec[3]);
     logmsg("Flash chip size: ", (int)(g_flash_chip_size / 1024), " kB");
 
@@ -285,6 +286,11 @@ void platform_init()
     gpio_conf(GPIO_EXP_SPARE, GPIO_FUNC_SIO, true,false, false,  true, false);
     // configuration of corresponding SPI unit occurs in audio_setup()
 #endif  // ENABLE_AUDIO_OUTPUT
+
+#ifdef GPIO_USB_POWER
+    gpio_conf(GPIO_USB_POWER, GPIO_FUNC_SIO, false, false, false,  false, false);
+#endif
+
 }
 
 // late_init() only runs in main application, SCSI not needed in bootloader
@@ -623,7 +629,11 @@ static void watchdog_callback(unsigned alarm_num)
             logmsg("scsiDev.phase: ", (int)scsiDev.phase);
             scsi_accel_log_state();
 
-            uint32_t *p =  (uint32_t*)__get_MSP();
+
+            uint32_t msp;
+            asm volatile ("MRS %0, msp" : "=r" (msp) );
+
+            uint32_t *p =  (uint32_t*)msp;
 
             for (int i = 0; i < 8; i++)
             {
@@ -647,7 +657,9 @@ static void watchdog_callback(unsigned alarm_num)
             logmsg("scsiDev.cdb: ", bytearray(scsiDev.cdb, 12));
             logmsg("scsiDev.phase: ", (int)scsiDev.phase);
 
-            uint32_t *p =  (uint32_t*)__get_MSP();
+            uint32_t msp;
+            asm volatile ("MRS %0, msp" : "=r" (msp) );
+            uint32_t *p =  (uint32_t*)msp;
 
             for (int i = 0; i < 8; i++)
             {
@@ -677,7 +689,7 @@ void platform_reset_watchdog()
     if (!g_watchdog_initialized)
     {
         int alarm_num = -1;
-        for (int i = 0; i < NUM_TIMERS; i++)
+        for (int i = 0; i < NUM_GENERIC_TIMERS; i++)
         {
             if (!hardware_alarm_is_claimed(i))
             {
@@ -805,10 +817,10 @@ bool platform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count
     assert(start < platform_get_romdrive_maxsize());
     assert((count % PLATFORM_ROMDRIVE_PAGE_SIZE) == 0);
 
-    __disable_irq();
+    uint32_t saved_irq = save_and_disable_interrupts();
     flash_range_erase(start + ROMDRIVE_OFFSET, count);
     flash_range_program(start + ROMDRIVE_OFFSET, data, count);
-    __enable_irq();
+    restore_interrupts(saved_irq);
     return true;
 }
 

+ 25 - 1
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.h → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.h

@@ -30,9 +30,15 @@
 #ifdef ZULUSCSI_PICO
 // ZuluSCSI Pico carrier board variant
 #include "ZuluSCSI_platform_gpio_Pico.h"
+#elif defined(ZULUSCSI_PICO_2)
+// ZuluSCSI Pico 2 carrier board variant
+#include "ZuluSCSI_platform_gpio_Pico_2.h"
 #elif defined(ZULUSCSI_BS2)
 // BS2 hardware variant, using Raspberry Pico board on a carrier PCB
 #include "ZuluSCSI_platform_gpio_BS2.h"
+#elif defined(ZULUSCSI_RP2350A)
+// RP2350A variant, using mcu chip directly
+#include "ZuluSCSI_platform_gpio_RP2350A.h"
 #else
 // Normal RP2040 variant, using RP2040 chip directly
 #include "ZuluSCSI_platform_gpio_RP2040.h"
@@ -54,19 +60,37 @@ extern const char *g_platform_name;
 # else
 #   define PLATFORM_NAME "ZuluSCSI Pico"
 # endif
+# define PLATFORM_PID "Pico"
 # define PLATFORM_REVISION "2.0"
 # define PLATFORM_HAS_INITIATOR_MODE 1
 # define DISABLE_SWO
+#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_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_TURBO
+#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_TURBO
 #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
 #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
 #endif
 
-#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

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform_gpio_BS2.h → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_BS2.h


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform_gpio_Pico.h → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Pico.h


+ 172 - 0
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Pico_2.h

@@ -0,0 +1,172 @@
+/** 
+ * ZuluSCSI™ - Copyright (c) 2022 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/>.
+**/
+
+// GPIO definitions for ZuluSCSI RP2040-based hardware
+
+#pragma once
+
+#include <hardware/gpio.h>
+
+// SCSI data input/output port.
+// The data bus uses external bidirectional buffer, with
+// direction controlled by DATA_DIR pin.
+#define SCSI_IO_DB0  0
+#define SCSI_IO_DB1  1
+#define SCSI_IO_DB2  2
+#define SCSI_IO_DB3  3
+#define SCSI_IO_DB4  4
+#define SCSI_IO_DB5  5
+#define SCSI_IO_DB6  6
+#define SCSI_IO_DB7  7
+#define SCSI_IO_DBP  8
+#define SCSI_IO_DATA_MASK 0x1FF
+#define SCSI_IO_SHIFT 0
+
+// Data direction control
+#define SCSI_DATA_DIR 9
+
+// SCSI output status lines
+#define SCSI_OUT_IO   22
+#define SCSI_OUT_CD   18
+#define SCSI_OUT_MSG  20
+#define SCSI_OUT_RST  21
+#define SCSI_OUT_BSY  27
+#define SCSI_OUT_REQ  17
+#define SCSI_OUT_SEL  19
+
+// SCSI input status signals
+#define SCSI_IN_SEL  18
+#define SCSI_IN_ACK  26
+#define SCSI_IN_ATN  28
+#define SCSI_IN_BSY  20
+#define SCSI_IN_RST  21
+
+// Status line outputs for initiator mode
+#define SCSI_OUT_ACK  26
+#define SCSI_OUT_ATN  28
+
+// Status line inputs for initiator mode
+#define SCSI_IN_IO    22
+#define SCSI_IN_CD    18
+#define SCSI_IN_MSG   20
+#define SCSI_IN_REQ   17
+
+// Status LED pins
+#define LED_PIN      16
+#define LED_ON()    sio_hw->gpio_set = 1 << LED_PIN
+#define LED_OFF()   sio_hw->gpio_clr = 1 << LED_PIN
+
+
+// SD card pins in SDIO mode
+#define SDIO_CLK 10
+#define SDIO_CMD 11
+#define SDIO_D0  12
+#define SDIO_D1  13
+#define SDIO_D2  14
+#define SDIO_D3  15
+
+// SD card pins in SPI mode
+#define SD_SPI       spi0
+#define SD_SPI_SCK   10
+#define SD_SPI_MOSI  11
+#define SD_SPI_MISO  12
+#define SD_SPI_CS    15
+
+#ifndef ENABLE_AUDIO_OUTPUT
+    // No spare pins for I2C
+    // IO expander I2C
+    // #define GPIO_I2C_SDA 14
+    // #define GPIO_I2C_SCL 15
+#else
+    // IO expander I2C pins being used as SPI for audio
+    #define AUDIO_SPI      spi1
+    #define GPIO_EXP_SPARE 14
+    #define GPIO_EXP_AUDIO 15
+#endif
+
+// DIP switch pins
+#define HAS_DIP_SWITCHES
+#define DIP_INITIATOR 28
+#define DIP_DBGLOG 17
+#define DIP_TERM 22
+
+// Other pins
+#define SWO_PIN 16
+#define GPIO_USB_POWER 24
+// Below are GPIO access definitions that are used from scsiPhy.cpp.
+
+// Write a single SCSI pin.
+// Example use: SCSI_OUT(ATN, 1) sets SCSI_ATN to low (active) state.
+#define SCSI_OUT(pin, state) \
+    *(state ? &sio_hw->gpio_clr : &sio_hw->gpio_set) = 1 << (SCSI_OUT_ ## pin)
+
+// Read a single SCSI pin.
+// Example use: SCSI_IN(ATN), returns 1 for active low state.
+#define SCSI_IN(pin) \
+    ((sio_hw->gpio_in & (1 << (SCSI_IN_ ## pin))) ? 0 : 1)
+
+// Set pin directions for initiator vs. target mode
+#define SCSI_ENABLE_INITIATOR() \
+    (sio_hw->gpio_oe_set = (1 << SCSI_OUT_ACK) | \
+                           (1 << SCSI_OUT_ATN)), \
+    (sio_hw->gpio_oe_clr = (1 << SCSI_IN_IO) | \
+                           (1 << SCSI_IN_CD) | \
+                           (1 << SCSI_IN_MSG) | \
+                           (1 << SCSI_IN_REQ))
+
+// Enable driving of shared control pins
+#define SCSI_ENABLE_CONTROL_OUT() \
+    (sio_hw->gpio_oe_set = (1 << SCSI_OUT_CD) | \
+                           (1 << SCSI_OUT_MSG))
+
+// Set SCSI data bus to output
+#define SCSI_ENABLE_DATA_OUT() \
+    (sio_hw->gpio_clr = (1 << SCSI_DATA_DIR), \
+     sio_hw->gpio_oe_set = SCSI_IO_DATA_MASK)
+
+// Write SCSI data bus, also sets REQ to inactive.
+#define SCSI_OUT_DATA(data) \
+    gpio_put_masked(SCSI_IO_DATA_MASK | (1 << SCSI_OUT_REQ), \
+                    g_scsi_parity_lookup[(uint8_t)(data)] | (1 << SCSI_OUT_REQ)), \
+    SCSI_ENABLE_DATA_OUT()
+
+// Release SCSI data bus and REQ signal
+#define SCSI_RELEASE_DATA_REQ() \
+    (sio_hw->gpio_oe_clr = SCSI_IO_DATA_MASK, \
+     sio_hw->gpio_set = (1 << SCSI_DATA_DIR) | (1 << SCSI_OUT_REQ))
+
+// Release all SCSI outputs
+#define SCSI_RELEASE_OUTPUTS() \
+    SCSI_RELEASE_DATA_REQ(), \
+    sio_hw->gpio_oe_clr = (1 << SCSI_OUT_CD) | \
+                          (1 << SCSI_OUT_MSG), \
+    sio_hw->gpio_set = (1 << SCSI_OUT_IO) | \
+                       (1 << SCSI_OUT_CD) | \
+                       (1 << SCSI_OUT_MSG) | \
+                       (1 << SCSI_OUT_RST) | \
+                       (1 << SCSI_OUT_BSY) | \
+                       (1 << SCSI_OUT_REQ) | \
+                       (1 << SCSI_OUT_SEL)
+
+// Read SCSI data bus
+#define SCSI_IN_DATA() \
+    (~sio_hw->gpio_in & SCSI_IO_DATA_MASK) >> SCSI_IO_SHIFT
+

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform_gpio_RP2040.h → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_RP2040.h


+ 170 - 0
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_RP2350A.h

@@ -0,0 +1,170 @@
+/** 
+ * ZuluSCSI™ - Copyright (c) 2022 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/>.
+**/
+
+// GPIO definitions for ZuluSCSI RP2040-based hardware
+
+#pragma once
+
+#include <hardware/gpio.h>
+
+// SCSI data input/output port.
+// The data bus uses external bidirectional buffer, with
+// direction controlled by DATA_DIR pin.
+#define SCSI_IO_DB0  0
+#define SCSI_IO_DB1  1
+#define SCSI_IO_DB2  2
+#define SCSI_IO_DB3  3
+#define SCSI_IO_DB4  4
+#define SCSI_IO_DB5  5
+#define SCSI_IO_DB6  6
+#define SCSI_IO_DB7  7
+#define SCSI_IO_DBP  8
+#define SCSI_IO_DATA_MASK 0x1FF
+#define SCSI_IO_SHIFT 0
+
+// Data direction control
+#define SCSI_DATA_DIR 17
+
+// SCSI output status lines
+#define SCSI_OUT_IO   12
+#define SCSI_OUT_CD   11
+#define SCSI_OUT_MSG  13
+#define SCSI_OUT_RST  28
+#define SCSI_OUT_BSY  26
+#define SCSI_OUT_REQ  9
+#define SCSI_OUT_SEL  24
+
+// SCSI input status signals
+#define SCSI_IN_SEL  11
+#define SCSI_IN_ACK  10
+#define SCSI_IN_ATN  29
+#define SCSI_IN_BSY  13
+#define SCSI_IN_RST  27
+
+// Status line outputs for initiator mode
+#define SCSI_OUT_ACK  10
+#define SCSI_OUT_ATN  29
+
+// Status line inputs for initiator mode
+#define SCSI_IN_IO    12
+#define SCSI_IN_CD    11
+#define SCSI_IN_MSG   13
+#define SCSI_IN_REQ   9
+
+// Status LED pins
+#define LED_PIN      25
+#define LED_ON()     sio_hw->gpio_set = 1 << LED_PIN
+#define LED_OFF()    sio_hw->gpio_clr = 1 << LED_PIN
+
+// SD card pins in SDIO mode
+#define SDIO_CLK 18
+#define SDIO_CMD 19
+#define SDIO_D0  20
+#define SDIO_D1  21
+#define SDIO_D2  22
+#define SDIO_D3  23
+
+// SD card pins in SPI mode
+#define SD_SPI       spi0
+#define SD_SPI_SCK   18
+#define SD_SPI_MOSI  19
+#define SD_SPI_MISO  20
+#define SD_SPI_CS    23
+
+#ifndef ENABLE_AUDIO_OUTPUT
+    // IO expander I2C
+    #define GPIO_I2C_SDA 14
+    #define GPIO_I2C_SCL 15
+#else
+    // IO expander I2C pins being used as SPI for audio
+    #define AUDIO_SPI      spi1
+    #define GPIO_EXP_SPARE 14
+    #define GPIO_EXP_AUDIO 15
+#endif
+
+// DIP switch pins
+#define HAS_DIP_SWITCHES
+#define DIP_INITIATOR 10
+#define DIP_DBGLOG 16
+#define DIP_TERM 9
+
+// Other pins
+#define SWO_PIN 16
+
+// Below are GPIO access definitions that are used from scsiPhy.cpp.
+
+// Write a single SCSI pin.
+// Example use: SCSI_OUT(ATN, 1) sets SCSI_ATN to low (active) state.
+#define SCSI_OUT(pin, state) \
+    *(state ? &sio_hw->gpio_clr : &sio_hw->gpio_set) = 1 << (SCSI_OUT_ ## pin)
+
+// Read a single SCSI pin.
+// Example use: SCSI_IN(ATN), returns 1 for active low state.
+#define SCSI_IN(pin) \
+    ((sio_hw->gpio_in & (1 << (SCSI_IN_ ## pin))) ? 0 : 1)
+
+// Set pin directions for initiator vs. target mode
+#define SCSI_ENABLE_INITIATOR() \
+    (sio_hw->gpio_oe_set = (1 << SCSI_OUT_ACK) | \
+                           (1 << SCSI_OUT_ATN)), \
+    (sio_hw->gpio_oe_clr = (1 << SCSI_IN_IO) | \
+                           (1 << SCSI_IN_CD) | \
+                           (1 << SCSI_IN_MSG) | \
+                           (1 << SCSI_IN_REQ))
+
+// Enable driving of shared control pins
+#define SCSI_ENABLE_CONTROL_OUT() \
+    (sio_hw->gpio_oe_set = (1 << SCSI_OUT_CD) | \
+                           (1 << SCSI_OUT_MSG))
+
+// Set SCSI data bus to output
+#define SCSI_ENABLE_DATA_OUT() \
+    (sio_hw->gpio_clr = (1 << SCSI_DATA_DIR), \
+     sio_hw->gpio_oe_set = SCSI_IO_DATA_MASK)
+
+// Write SCSI data bus, also sets REQ to inactive.
+#define SCSI_OUT_DATA(data) \
+    gpio_put_masked(SCSI_IO_DATA_MASK | (1 << SCSI_OUT_REQ), \
+                    g_scsi_parity_lookup[(uint8_t)(data)] | (1 << SCSI_OUT_REQ)), \
+    SCSI_ENABLE_DATA_OUT()
+
+// Release SCSI data bus and REQ signal
+#define SCSI_RELEASE_DATA_REQ() \
+    (sio_hw->gpio_oe_clr = SCSI_IO_DATA_MASK, \
+     sio_hw->gpio_set = (1 << SCSI_DATA_DIR) | (1 << SCSI_OUT_REQ))
+
+// Release all SCSI outputs
+#define SCSI_RELEASE_OUTPUTS() \
+    SCSI_RELEASE_DATA_REQ(), \
+    sio_hw->gpio_oe_clr = (1 << SCSI_OUT_CD) | \
+                          (1 << SCSI_OUT_MSG), \
+    sio_hw->gpio_set = (1 << SCSI_OUT_IO) | \
+                       (1 << SCSI_OUT_CD) | \
+                       (1 << SCSI_OUT_MSG) | \
+                       (1 << SCSI_OUT_RST) | \
+                       (1 << SCSI_OUT_BSY) | \
+                       (1 << SCSI_OUT_REQ) | \
+                       (1 << SCSI_OUT_SEL)
+
+// Read SCSI data bus
+#define SCSI_IN_DATA() \
+    (~sio_hw->gpio_in & SCSI_IO_DATA_MASK) >> SCSI_IO_SHIFT
+

+ 22 - 6
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform_msc.cpp → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_msc.cpp

@@ -28,10 +28,12 @@
 #include "ZuluSCSI_platform.h"
 #include "ZuluSCSI_log.h"
 #include "ZuluSCSI_msc.h"
-
+#include "ZuluSCSI_config.h"
+#include "ZuluSCSI_settings.h"
 #include <class/msc/msc.h>
 #include <class/msc/msc_device.h>
 
+
 #if CFG_TUD_MSC_EP_BUFSIZE < SD_SECTOR_SIZE
   #error "CFG_TUD_MSC_EP_BUFSIZE is too small! It needs to be at least 512 (SD_SECTOR_SIZE)"
 #endif
@@ -43,7 +45,10 @@ static bool unitReady = false;
 /* return true if USB presence detected / eligble to enter CR mode */
 bool platform_sense_msc() {
 
-#ifdef ZULUSCSI_PICO
+#ifdef ZULUSCSI_PICO_2
+  if (!gpio_get(GPIO_USB_POWER))
+    return false;
+#elif defined(ZULUSCSI_PICO)
   // check if we're USB powered, if not, exit immediately
   // pin on the wireless module, see https://github.com/earlephilhower/arduino-pico/discussions/835
   if (rp2040.isPicoW() && !digitalRead(34))
@@ -57,9 +62,21 @@ bool platform_sense_msc() {
 
   // wait for up to a second to be enumerated
   uint32_t start = millis();
-  while (!tud_connected() && ((uint32_t)(millis() - start) < CR_ENUM_TIMEOUT)) 
+  bool timed_out = false;
+  uint16_t usb_timeout =  g_scsi_settings.getSystem()->usbMassStorageWaitPeriod;
+  while (!tud_connected())
+  {
+    if ((uint32_t)(millis() - start) > usb_timeout)
+    {
+      logmsg("Waiting for USB enumeration timed out after ", usb_timeout, "ms.");
+      logmsg("-- Try increasing 'USBMassStorageWaitPeriod' in the ", CONFIGFILE);
+      timed_out = true;
+      break;
+    } 
     delay(100);
-
+  }
+  if (!timed_out)
+    dbgmsg("USB enumeration took ", (int)((uint32_t)(millis() - start)), "ms");
   // tud_connected returns True if just got out of Bus Reset and received the very first data from host
   // https://github.com/hathach/tinyusb/blob/master/src/device/usbd.h#L63
   return tud_connected();
@@ -93,9 +110,8 @@ void __USBInstallMassStorage() { }
 extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8],
                         uint8_t product_id[16], uint8_t product_rev[4]) {
 
-  // TODO: We could/should use strings from the platform, but they are too long
   const char vid[] = "ZuluSCSI";
-  const char pid[] = "Pico"; 
+  const char pid[] = PLATFORM_PID; 
   const char rev[] = "1.0";
 
   memcpy(vendor_id, vid, tu_min32(strlen(vid), 8));

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform_msc.h → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_msc.h


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform_network.cpp → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_network.cpp


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform_network.h → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_network.h


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/audio.cpp → lib/ZuluSCSI_platform_RP2MCU/audio.cpp


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/audio.h → lib/ZuluSCSI_platform_RP2MCU/audio.h


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/bsp.h → lib/ZuluSCSI_platform_RP2MCU/bsp.h


+ 25 - 8
lib/ZuluSCSI_platform_RP2040/program_flash.cpp → lib/ZuluSCSI_platform_RP2MCU/program_flash.cpp

@@ -29,6 +29,10 @@
 #include <hardware/flash.h>
 #include <hardware/structs/xip_ctrl.h>
 #include <hardware/structs/usb.h>
+#include <hardware/structs/nvic.h>
+#include <hardware/structs/scb.h>
+#include <hardware/sync.h>
+
 #ifndef PIO_FRAMEWORK_ARDUINO_NO_USB
 #include <SerialUSB.h>
 #include <class/cdc/cdc_device.h>
@@ -58,19 +62,29 @@ bool platform_rewrite_flash_page(uint32_t offset, uint8_t buffer[PLATFORM_FLASH_
     }
 
 
-    if (NVIC_GetEnableIRQ(USBCTRL_IRQ_IRQn))
+#ifdef ZULUSCSI_MCU_RP23XX
+
+    if (nvic_hw->iser[0] & 1 << 14)
     {
         logmsg("Disabling USB during firmware flashing");
-        NVIC_DisableIRQ(USBCTRL_IRQ_IRQn);
+        nvic_hw->icer[0] = 1 << 14;
         usb_hw->main_ctrl = 0;
     }
+#else
+    if (nvic_hw->iser & 1 << 14)
+    {
+        logmsg("Disabling USB during firmware flashing");
+        nvic_hw->icer = 1 << 14;
+        usb_hw->main_ctrl = 0;
+    }
+#endif
 
     dbgmsg("Writing flash at offset ", offset, " data ", bytearray(buffer, 4));
     assert(offset % PLATFORM_FLASH_PAGE_SIZE == 0);
     assert(offset >= PLATFORM_BOOTLOADER_SIZE);
 
     // Avoid any mbed timer interrupts triggering during the flashing.
-    __disable_irq();
+    uint32_t saved_irq = save_and_disable_interrupts();
 
     // For some reason any code executed after flashing crashes
     // unless we disable the XIP cache.
@@ -88,17 +102,20 @@ bool platform_rewrite_flash_page(uint32_t offset, uint8_t buffer[PLATFORM_FLASH_
     for (int i = 0; i < num_words; i++)
     {
         uint32_t expected = buf32[i];
+#ifdef ZULUSCSI_MCU_RP23XX
+        uint32_t actual = *(volatile uint32_t*)(XIP_NOCACHE_NOALLOC_BASE + offset + i * 4);
+#else
         uint32_t actual = *(volatile uint32_t*)(XIP_NOCACHE_BASE + offset + i * 4);
-
+#endif
         if (actual != expected)
         {
             logmsg("Flash verify failed at offset ", offset + i * 4, " got ", actual, " expected ", expected);
-            __enable_irq();
+            restore_interrupts(saved_irq);
             return false;
         }
     }
 
-    __enable_irq();
+    restore_interrupts(saved_irq);
 
     return true;
 }
@@ -109,7 +126,7 @@ void platform_boot_to_main_firmware()
     // To ensure that the system state is reset properly, we perform
     // a SYSRESETREQ and jump straight from the reset vector to main application.
     g_bootloader_exit_req = &g_bootloader_exit_req;
-    SCB->AIRCR = 0x05FA0004;
+    scb_hw->aircr = 0x05FA0004;
     while(1);
 }
 
@@ -122,7 +139,7 @@ void btldr_reset_handler()
         application_base = (uint32_t*)(XIP_BASE + PLATFORM_BOOTLOADER_SIZE);
     }
 
-    SCB->VTOR = (uint32_t)application_base;
+    scb_hw->vtor = (uint32_t)application_base;
     __asm__(
         "msr msp, %0\n\t"
         "bx %1" : : "r" (application_base[0]),

+ 0 - 1
lib/ZuluSCSI_platform_RP2040/rp2040-template.ld → lib/ZuluSCSI_platform_RP2MCU/rp2040-template.ld

@@ -77,7 +77,6 @@ SECTIONS
         .pio/build/$project_name/src/ZuluSCSI_log_trace.cpp.o(.text .text*)
         .pio/build/$project_name/src/ZuluSCSI_settings.cpp.o(.text .text*)
         .pio/build/$project_name/src/QuirksCheck.cpp.o(.text .text*)
-        *libZuluSCSI_platform_RP2040.a:ZuluSCSI_platform.cpp.o(.text .text*)
         *libm*:(.text .text*)
         *libc*:(.text .text*)
         *libgcc*:*df*(.text .text*)

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/rp2040_btldr.ld → lib/ZuluSCSI_platform_RP2MCU/rp2040_btldr.ld


+ 352 - 0
lib/ZuluSCSI_platform_RP2MCU/rp23xx-template.ld

@@ -0,0 +1,352 @@
+/* Based on GCC ARM embedded samples.
+   Defines the following symbols for use by code:
+    __exidx_start
+    __exidx_end
+    __etext
+    __data_start__
+    __preinit_array_start
+    __preinit_array_end
+    __init_array_start
+    __init_array_end
+    __fini_array_start
+    __fini_array_end
+    __data_end__
+    __bss_start__
+    __bss_end__
+    __end__
+    end
+    __HeapLimit
+    __StackLimit
+    __StackTop
+    __stack (== StackTop)
+*/
+
+MEMORY
+{
+    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = $program_size
+    PSRAM(rwx) : ORIGIN = 0x11000000, LENGTH = 0
+    RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k  /* Leave space for pico-debug */
+    SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k
+    SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k
+}
+
+PROVIDE ( _EEPROM_start = __EEPROM_START__ );
+PROVIDE ( _FS_start     = __FS_START__ );
+PROVIDE ( _FS_end       = __FS_END__ );
+
+ENTRY(_entry_point)
+
+SECTIONS
+{
+    .flash_begin : {
+        __flash_binary_start = .;
+    } > FLASH
+
+    /* The bootrom will enter the image at the point indicated in your
+       IMAGE_DEF, which is usually the reset handler of your vector table.
+
+       The debugger will use the ELF entry point, which is the _entry_point
+       symbol, and in our case is *different from the bootrom's entry point.*
+       This is used to go back through the bootrom on debugger launches only,
+       to perform the same initial flash setup that would be performed on a
+       cold boot.
+    */
+
+    /* If ZuluSCSI SD card bootloader is included, it goes in first 128 kB */
+    .text.bootloader : ALIGN(16) SUBALIGN(16)
+    {
+        KEEP(*(.text.btldr*))
+        . = ALIGN(131072);
+        CHECK_BOOTLOADER_SIZE = 1 / (. <= 131072);
+    } > FLASH
+
+    .text : {
+        __logical_binary_start = .;
+        KEEP (*(.vectors))
+        KEEP (*(.binary_info_header))
+        __binary_info_header_end = .;
+        KEEP (*(.embedded_block))
+        __embedded_block_end = .;
+        KEEP (*(.reset))
+        /* TODO revisit this now memset/memcpy/float in ROM */
+        /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
+         * FLASH ... we will include any thing excluded here in .data below by default */
+        *(.init)
+        *libgcc.a:cmse_nonsecure_call.o
+        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
+        *(.fini)
+        /* Pull all c'tors into .text */
+        *crtbegin.o(.ctors)
+        *crtbegin?.o(.ctors)
+        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
+        *(SORT(.ctors.*))
+        *(.ctors)
+        /* Followed by destructors */
+        *crtbegin.o(.dtors)
+        *crtbegin?.o(.dtors)
+        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
+        *(SORT(.dtors.*))
+        *(.dtors)
+
+        . = ALIGN(4);
+        /* preinit data */
+        PROVIDE_HIDDEN (__preinit_array_start = .);
+        KEEP(*(SORT(.preinit_array.*)))
+        KEEP(*(.preinit_array))
+        PROVIDE_HIDDEN (__preinit_array_end = .);
+
+        . = ALIGN(4);
+        /* init data */
+        PROVIDE_HIDDEN (__init_array_start = .);
+        KEEP(*(SORT(.init_array.*)))
+        KEEP(*(.init_array))
+        PROVIDE_HIDDEN (__init_array_end = .);
+
+        . = ALIGN(4);
+        /* finit data */
+        PROVIDE_HIDDEN (__fini_array_start = .);
+        *(SORT(.fini_array.*))
+        *(.fini_array)
+        PROVIDE_HIDDEN (__fini_array_end = .);
+
+        *(.eh_frame*)
+        . = ALIGN(4);
+
+               /* Put only non-timecritical code in flash
+         * This includes e.g. floating point math routines.
+         */
+        .pio/build/$project_name/src/ZuluSCSI_log.cpp.o(.text .text*)
+        .pio/build/$project_name/src/ZuluSCSI_log_trace.cpp.o(.text .text*)
+        .pio/build/$project_name/src/ZuluSCSI_settings.cpp.o(.text .text*)
+        .pio/build/$project_name/src/QuirksCheck.cpp.o(.text .text*)
+        *libZuluSCSI_platform_RP2350.a:ZuluSCSI_platform.cpp.o(.text .text*)
+        *libm*:(.text .text*)
+        *libc*:(.text .text*)
+        *libgcc*:*df*(.text .text*)
+        *USB*(.text .text*)
+        *SPI*(.text .text*)
+        *Spi*(.text .text*)
+        *spi*(.text .text*)
+        *stdc*:(.text .text*)
+        *supc*:(.text .text*)
+        *nosys*:(.text .text*)
+        *libc*:*printf*(.text .text*)
+        *libc*:*toa*(.text .text*)
+        *libminIni.a:(.text .text*)
+        *libCUEParser.a:(.text .text*)
+    } > FLASH
+
+    /* Note the boot2 section is optional, and should be discarded if there is
+       no reference to it *inside* the binary, as it is not called by the
+       bootrom. (The bootrom performs a simple best-effort XIP setup and
+       leaves it to the binary to do anything more sophisticated.) However
+       there is still a size limit of 256 bytes, to ensure the boot2 can be
+       stored in boot RAM.
+
+       Really this is a "XIP setup function" -- the name boot2 is historic and
+       refers to its dual-purpose on RP2040, where it also handled vectoring
+       from the bootrom into the user image.
+    */
+
+    .boot2 : {
+        __boot2_start__ = .;
+        *(.boot2)
+        __boot2_end__ = .;
+    } > FLASH
+
+    ASSERT(__boot2_end__ - __boot2_start__ <= 256,
+        "ERROR: Pico second stage bootloader must be no more than 256 bytes in size")
+
+    .rodata : {
+        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
+        *(.srodata*)
+        . = ALIGN(4);
+        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
+        . = ALIGN(4);
+    } > FLASH
+
+    .ARM.extab :
+    {
+        *(.ARM.extab* .gnu.linkonce.armextab.*)
+    } > FLASH
+
+    __exidx_start = .;
+    .ARM.exidx :
+    {
+        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+    } > FLASH
+    __exidx_end = .;
+
+    /* Machine inspectable binary information */
+    . = ALIGN(4);
+    __binary_info_start = .;
+    .binary_info :
+    {
+        KEEP(*(.binary_info.keep.*))
+        *(.binary_info.*)
+    } > FLASH
+    __binary_info_end = .;
+    . = ALIGN(4);
+
+    .ram_vector_table (NOLOAD): {
+        *(.ram_vector_table)
+    } > RAM
+
+    .uninitialized_data (NOLOAD): {
+        . = ALIGN(4);
+        *(.uninitialized_data*)
+    } > RAM
+
+    .data : {
+        __data_start__ = .;
+        *(vtable)
+
+        *(.time_critical*)
+
+        /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
+        *(.text)
+        *(.text*)
+        . = ALIGN(4);
+        *(.rodata*)
+        . = ALIGN(4);
+
+        *(.data*)
+        *(.sdata*)
+
+        . = ALIGN(4);
+        *(.after_data.*)
+        . = ALIGN(4);
+        /* preinit data */
+        PROVIDE_HIDDEN (__mutex_array_start = .);
+        KEEP(*(SORT(.mutex_array.*)))
+        KEEP(*(.mutex_array))
+        PROVIDE_HIDDEN (__mutex_array_end = .);
+
+        *(.jcr)
+        . = ALIGN(4);
+    } > RAM AT> FLASH
+
+    .tdata : {
+        . = ALIGN(4);
+		*(.tdata .tdata.* .gnu.linkonce.td.*)
+        /* All data end */
+        __tdata_end = .;
+    } > RAM AT> FLASH
+    PROVIDE(__data_end__ = .);
+
+    /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
+    __etext = LOADADDR(.data);
+
+    .tbss (NOLOAD) : {
+        . = ALIGN(4);
+        __bss_start__ = .;
+        __tls_base = .;
+        *(.tbss .tbss.* .gnu.linkonce.tb.*)
+        *(.tcommon)
+
+        __tls_end = .;
+    } > RAM
+
+    .bss (NOLOAD) : {
+        . = ALIGN(4);
+        __tbss_end = .;
+
+        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
+        *(COMMON)
+        /* Python template escaping dollar sign with two dollar signs */
+        PROVIDE(__global_pointer$$ = . + 2K);
+        *(.sbss*)
+        . = ALIGN(4);
+        __bss_end__ = .;
+    } > RAM
+
+    .heap (NOLOAD):
+    {
+        __end__ = .;
+        end = __end__;
+        KEEP(*(.heap*))
+        /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
+           to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
+        . = ORIGIN(RAM) + LENGTH(RAM);
+        __HeapLimit = .;
+    } > RAM
+
+    /* Start and end symbols must be word-aligned */
+    .scratch_x : {
+        __scratch_x_start__ = .;
+        *(.scratch_x.*)
+        . = ALIGN(4);
+        __scratch_x_end__ = .;
+    } > SCRATCH_X AT > FLASH
+    __scratch_x_source__ = LOADADDR(.scratch_x);
+
+    .scratch_y : {
+        __scratch_y_start__ = .;
+        *(.scratch_y.*)
+        . = ALIGN(4);
+        __scratch_y_end__ = .;
+    } > SCRATCH_Y AT > FLASH
+    __scratch_y_source__ = LOADADDR(.scratch_y);
+
+    /* .stack*_dummy section doesn't contains any symbols. It is only
+     * used for linker to calculate size of stack sections, and assign
+     * values to stack symbols later
+     *
+     * stack1 section may be empty/missing if platform_launch_core1 is not used */
+
+    /* by default we put core 0 stack at the end of scratch Y, so that if core 1
+     * stack is not used then all of SCRATCH_X is free.
+     */
+    .stack1_dummy (NOLOAD):
+    {
+        *(.stack1*)
+    } > SCRATCH_X
+    .stack_dummy (NOLOAD):
+    {
+        KEEP(*(.stack*))
+    } > SCRATCH_Y
+
+    .flash_end : {
+        KEEP(*(.embedded_end_block*))
+        PROVIDE(__flash_binary_end = .);
+    } > FLASH =0xaa
+
+    .psram (NOLOAD) : {
+        __psram_start__ = .;
+        *(.psram*)
+        . = ALIGN(4096);
+        __psram_heap_start__ = .;
+    } > PSRAM
+
+    /* stack limit is poorly named, but historically is maximum heap ptr */
+    __StackLimit = ORIGIN(RAM) + LENGTH(RAM);
+    __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
+    __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
+    __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
+    __StackBottom = __StackTop - SIZEOF(.stack_dummy);
+    PROVIDE(__stack = __StackTop);
+
+    /* picolibc and LLVM */
+    PROVIDE (__heap_start = __end__);
+    PROVIDE (__heap_end = __HeapLimit);
+    PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
+    PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
+    PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
+
+    /* TLSF */
+    PROVIDE (__psram_start = __psram_start__);
+    PROVIDE (__psram_heap_start = __psram_heap_start__);
+
+    /* llvm-libc */
+    PROVIDE (_end = __end__);
+    PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
+
+    /* Check if data + heap + stack exceeds RAM limit */
+    ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
+
+    ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary")
+    ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary")
+
+    /* todo assert on extra code */
+}
+

+ 307 - 0
lib/ZuluSCSI_platform_RP2MCU/rp23xx_btldr.ld

@@ -0,0 +1,307 @@
+/** 
+ * ZuluSCSI™ - Copyright (c) 2022 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/>.
+**/
+
+
+/*
+ *
+ * Customized linker script for building bootloader
+ *
+ */
+
+ MEMORY
+{
+    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 128k
+    PSRAM(rwx) : ORIGIN = 0x11000000, LENGTH = 0
+    RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k
+    SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k
+    SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k
+}
+PROVIDE ( _EEPROM_start = __EEPROM_START__ );
+PROVIDE ( _FS_start     = __FS_START__ );
+PROVIDE ( _FS_end       = __FS_END__ );
+
+ENTRY(_entry_point)
+SECTIONS
+{
+    .flash_begin : {
+        __flash_binary_start = .;
+    } > FLASH
+
+    .text : {
+        __logical_binary_start = .;
+        KEEP (*(.btldr_vectors))
+        KEEP (*(.binary_info_header))
+        __binary_info_header_end = .;
+        . = ALIGN(256);
+        __real_vectors_start = .;
+        KEEP (*(.vectors))
+
+        KEEP (*(.embedded_block))
+        __embedded_block_end = .;
+        KEEP (*(.reset))
+        /* TODO revisit this now memset/memcpy/float in ROM */
+        /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
+         * FLASH ... we will include any thing excluded here in .data below by default */
+        *(.init)
+        *libgcc.a:cmse_nonsecure_call.o
+        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
+        *(.fini)
+        /* Pull all c'tors into .text */
+        *crtbegin.o(.ctors)
+        *crtbegin?.o(.ctors)
+        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
+        *(SORT(.ctors.*))
+        *(.ctors)
+        /* Followed by destructors */
+        *crtbegin.o(.dtors)
+        *crtbegin?.o(.dtors)
+        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
+        *(SORT(.dtors.*))
+        *(.dtors)
+
+        . = ALIGN(4);
+        /* preinit data */
+        PROVIDE_HIDDEN (__preinit_array_start = .);
+        KEEP(*(SORT(.preinit_array.*)))
+        KEEP(*(.preinit_array))
+        PROVIDE_HIDDEN (__preinit_array_end = .);
+
+        . = ALIGN(4);
+        /* init data */
+        PROVIDE_HIDDEN (__init_array_start = .);
+        KEEP(*(SORT(.init_array.*)))
+        KEEP(*(.init_array))
+        PROVIDE_HIDDEN (__init_array_end = .);
+
+        . = ALIGN(4);
+        /* finit data */
+        PROVIDE_HIDDEN (__fini_array_start = .);
+        *(SORT(.fini_array.*))
+        *(.fini_array)
+        PROVIDE_HIDDEN (__fini_array_end = .);
+
+        *(.eh_frame*)
+        . = ALIGN(4);
+    } > FLASH
+
+    /* Note the boot2 section is optional, and should be discarded if there is
+       no reference to it *inside* the binary, as it is not called by the
+       bootrom. (The bootrom performs a simple best-effort XIP setup and
+       leaves it to the binary to do anything more sophisticated.) However
+       there is still a size limit of 256 bytes, to ensure the boot2 can be
+       stored in boot RAM.
+
+       Really this is a "XIP setup function" -- the name boot2 is historic and
+       refers to its dual-purpose on RP2040, where it also handled vectoring
+       from the bootrom into the user image.
+    */
+
+    .rodata : {
+        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
+        *(.srodata*)
+        . = ALIGN(4);
+        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
+        . = ALIGN(4);
+    } > FLASH
+
+    .ARM.extab :
+    {
+        *(.ARM.extab* .gnu.linkonce.armextab.*)
+    } > FLASH
+
+    __exidx_start = .;
+    .ARM.exidx :
+    {
+        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+    } > FLASH
+    __exidx_end = .;
+
+    /* Machine inspectable binary information */
+    . = ALIGN(4);
+    __binary_info_start = .;
+    .binary_info :
+    {
+        KEEP(*(.binary_info.keep.*))
+        *(.binary_info.*)
+    } > FLASH
+    __binary_info_end = .;
+    . = ALIGN(4);
+
+    .ram_vector_table (NOLOAD): {
+        *(.ram_vector_table)
+    } > RAM
+
+    .uninitialized_data (NOLOAD): {
+        . = ALIGN(4);
+        *(.uninitialized_data*)
+    } > RAM
+
+    .data : {
+        __data_start__ = .;
+        *(vtable)
+
+        *(.time_critical*)
+
+        /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
+        *(.text*)
+        . = ALIGN(4);
+        *(.rodata*)
+        . = ALIGN(4);
+
+        *(.data*)
+        *(.sdata*)
+
+        . = ALIGN(4);
+        *(.after_data.*)
+        . = ALIGN(4);
+        /* preinit data */
+        PROVIDE_HIDDEN (__mutex_array_start = .);
+        KEEP(*(SORT(.mutex_array.*)))
+        KEEP(*(.mutex_array))
+        PROVIDE_HIDDEN (__mutex_array_end = .);
+
+        *(.jcr)
+        . = ALIGN(4);
+    } > RAM AT> FLASH
+
+    .tdata : {
+        . = ALIGN(4);
+		*(.tdata .tdata.* .gnu.linkonce.td.*)
+        /* All data end */
+        __tdata_end = .;
+    } > RAM AT> FLASH
+    PROVIDE(__data_end__ = .);
+
+    /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */
+    __etext = LOADADDR(.data);
+
+    .tbss (NOLOAD) : {
+        . = ALIGN(4);
+        __bss_start__ = .;
+        __tls_base = .;
+        *(.tbss .tbss.* .gnu.linkonce.tb.*)
+        *(.tcommon)
+
+        __tls_end = .;
+    } > RAM
+
+    .bss (NOLOAD) : {
+        . = ALIGN(4);
+        __tbss_end = .;
+
+        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
+        *(COMMON)
+        PROVIDE(__global_pointer$ = . + 2K);
+        *(.sbss*)
+        . = ALIGN(4);
+        __bss_end__ = .;
+    } > RAM
+
+    .heap (NOLOAD):
+    {
+        __end__ = .;
+        end = __end__;
+        KEEP(*(.heap*))
+        /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however
+           to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */
+        . = ORIGIN(RAM) + LENGTH(RAM);
+        __HeapLimit = .;
+    } > RAM
+
+    /* Start and end symbols must be word-aligned */
+    .scratch_x : {
+        __scratch_x_start__ = .;
+        *(.scratch_x.*)
+        . = ALIGN(4);
+        __scratch_x_end__ = .;
+    } > SCRATCH_X AT > FLASH
+    __scratch_x_source__ = LOADADDR(.scratch_x);
+
+    .scratch_y : {
+        __scratch_y_start__ = .;
+        *(.scratch_y.*)
+        . = ALIGN(4);
+        __scratch_y_end__ = .;
+    } > SCRATCH_Y AT > FLASH
+    __scratch_y_source__ = LOADADDR(.scratch_y);
+
+    /* .stack*_dummy section doesn't contains any symbols. It is only
+     * used for linker to calculate size of stack sections, and assign
+     * values to stack symbols later
+     *
+     * stack1 section may be empty/missing if platform_launch_core1 is not used */
+
+    /* by default we put core 0 stack at the end of scratch Y, so that if core 1
+     * stack is not used then all of SCRATCH_X is free.
+     */
+    .stack1_dummy (NOLOAD):
+    {
+        *(.stack1*)
+    } > SCRATCH_X
+    .stack_dummy (NOLOAD):
+    {
+        KEEP(*(.stack*))
+    } > SCRATCH_Y
+
+    .flash_end : {
+        KEEP(*(.embedded_end_block*))
+        PROVIDE(__flash_binary_end = .);
+    } > FLASH =0xaa
+
+    .psram (NOLOAD) : {
+        __psram_start__ = .;
+        *(.psram*)
+        . = ALIGN(4096);
+        __psram_heap_start__ = .;
+    } > PSRAM
+
+    /* stack limit is poorly named, but historically is maximum heap ptr */
+    __StackLimit = ORIGIN(RAM) + LENGTH(RAM);
+    __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
+    __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
+    __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
+    __StackBottom = __StackTop - SIZEOF(.stack_dummy);
+    PROVIDE(__stack = __StackTop);
+
+    /* picolibc and LLVM */
+    PROVIDE (__heap_start = __end__);
+    PROVIDE (__heap_end = __HeapLimit);
+    PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) );
+    PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1));
+    PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) );
+
+    /* TLSF */
+    PROVIDE (__psram_start = __psram_start__);
+    PROVIDE (__psram_heap_start = __psram_heap_start__);
+
+    /* llvm-libc */
+    PROVIDE (_end = __end__);
+    PROVIDE (__llvm_libc_heap_limit = __HeapLimit);
+
+    /* Check if data + heap + stack exceeds RAM limit */
+    ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
+
+    ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary")
+    ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary")
+
+    /* todo assert on extra code */
+}
+

+ 6 - 3
lib/ZuluSCSI_platform_RP2040/run_pioasm.sh → lib/ZuluSCSI_platform_RP2MCU/run_pioasm.sh

@@ -1,14 +1,17 @@
 #!/bin/bash
 
 # This script regenerates the .pio.h files from .pio
-
 pioasm sdio_RP2040.pio sdio_RP2040.pio.h
 pioasm sdio_Pico.pio sdio_Pico.pio.h
-pioasm sdio_BS2.pio sdio_BS2.pio.h
+pioasm sdio.RP2350.pio sdio.RP2350.pio.h
+pioasm sdio.Pico_2.pio sdio.Pico_2.h
 
 pioasm scsi_accel_target_RP2040.pio scsi_accel_target_RP2040.pio.h
-pioasm scsi_accel_target_BS2.pio scsi_accel_target_BS2.pio.h
 pioasm scsi_accel_target_Pico.pio scsi_accel_target_Pico.pio.h
+pioasm scsi_accel_target_RP2350A.pio scsi_accel_target_RP2350A.pio.h
+pioasm scsi_accel_target_Pico_2.pio scsi_accel_target_Pico_2.pio.h
 
 pioasm scsi_accel_host_RP2040.pio scsi_accel_host_RP2040.pio.h
 pioasm scsi_accel_host_Pico.pio scsi_accel_host_Pico.pio.h
+pioasm scsi_accel_host_RP2350A.pio scsi_accel_host_RP2350A.pio.h
+pioasm scsi_accel_host_Pico_2.pio scsi_accel_host_Pico_2.pio.h

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi2sd_time.h → lib/ZuluSCSI_platform_RP2MCU/scsi2sd_time.h


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsiHostPhy.cpp → lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.cpp


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsiHostPhy.h → lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.h


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsiPhy.cpp → lib/ZuluSCSI_platform_RP2MCU/scsiPhy.cpp


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsiPhy.h → lib/ZuluSCSI_platform_RP2MCU/scsiPhy.h


+ 10 - 6
lib/ZuluSCSI_platform_RP2040/scsi_accel_host.cpp → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.cpp

@@ -31,11 +31,15 @@
 #include <hardware/sync.h>
 
 #ifdef PLATFORM_HAS_INITIATOR_MODE
-#ifdef ZULUSCSI_PICO
-#include "scsi_accel_host_Pico.pio.h"
-#else
-#include "scsi_accel_host_RP2040.pio.h"
-#endif
+# ifdef ZULUSCSI_PICO_2
+#  include "scsi_accel_host_Pico_2.pio.h"
+# elif defined(ZULUSCSI_PICO)
+#  include "scsi_accel_host_Pico.pio.h"
+# elif defined(ZULUSCSI_RP2350A)
+#  include "scsi_accel_host_RP2350A.pio.h"
+# else
+#  include "scsi_accel_host_RP2040.pio.h"
+# endif
 
 #define SCSI_PIO pio0
 #define SCSI_SM 0
@@ -170,4 +174,4 @@ void scsi_accel_host_init()
     sm_config_set_in_shift(&g_scsi_host.pio_cfg_async_read, true, true, 32);
 }
 
-#endif
+#endif // PLATFORM_HAS_INITIATOR_MODE

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_host.h → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.h


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_host_Pico.pio → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_Pico.pio


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_host_Pico.pio.h → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_Pico.pio.h


+ 46 - 0
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_Pico_2.pio

@@ -0,0 +1,46 @@
+; ZuluSCSI™ - Copyright (c) 2022 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/>.
+
+
+; RP2040 PIO program for accelerating SCSI initiator / host function
+; Run "pioasm scsi_accel_host.pio scsi_accel_host.pio.h" to regenerate the C header from this.
+; GPIO mapping:
+; - 0-7: DB0-DB7
+; -   8: DBP
+; Side set is ACK pin
+
+.define REQ 17
+.define ACK 26
+
+; Read from SCSI bus using asynchronous handshake.
+; Data is returned as 16-bit words that contain the 8 data bits + 1 parity bit.
+; Number of bytes to receive minus 1 should be written to TX fifo.
+; Number of bytes to receive must be divisible by 2.
+.program scsi_host_async_read
+    .side_set 1
+
+    pull block                  side 1  ; Get number of bytes to receive
+    mov x, osr                  side 1  ; Store to counter X
+
+start:
+    wait 0 gpio REQ             side 1  ; Wait for REQ low
+    in pins, 9                  side 0  ; Assert ACK, read GPIO
+    in null, 7                  side 0  ; Padding bits
+    wait 1 gpio REQ             side 0  ; Wait for REQ high
+    jmp x-- start               side 1  ; Deassert ACK, decrement byte count and jump to start

+ 43 - 0
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_Pico_2.pio.h

@@ -0,0 +1,43 @@
+// -------------------------------------------------- //
+// This file is autogenerated by pioasm; do not edit! //
+// -------------------------------------------------- //
+
+#pragma once
+
+#if !PICO_NO_HARDWARE
+#include "hardware/pio.h"
+#endif
+
+// -------------------- //
+// scsi_host_async_read //
+// -------------------- //
+
+#define scsi_host_async_read_wrap_target 0
+#define scsi_host_async_read_wrap 6
+
+static const uint16_t scsi_host_async_read_program_instructions[] = {
+            //     .wrap_target
+    0x90a0, //  0: pull   block           side 1     
+    0xb027, //  1: mov    x, osr          side 1     
+    0x3011, //  2: wait   0 gpio, 17      side 1     
+    0x4009, //  3: in     pins, 9         side 0     
+    0x4067, //  4: in     null, 7         side 0     
+    0x2091, //  5: wait   1 gpio, 17      side 0     
+    0x1042, //  6: jmp    x--, 2          side 1     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_host_async_read_program = {
+    .instructions = scsi_host_async_read_program_instructions,
+    .length = 7,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_host_async_read_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_host_async_read_wrap_target, offset + scsi_host_async_read_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#endif

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_host_RP2040.pio → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2040.pio


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_host_RP2040.pio.h → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2040.pio.h


+ 46 - 0
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2350A.pio

@@ -0,0 +1,46 @@
+; ZuluSCSI™ - Copyright (c) 2022 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/>.
+
+
+; RP2040 PIO program for accelerating SCSI initiator / host function
+; Run "pioasm scsi_accel_host.pio scsi_accel_host.pio.h" to regenerate the C header from this.
+; GPIO mapping:
+; - 0-7: DB0-DB7
+; -   8: DBP
+; Side set is ACK pin
+
+.define REQ 9
+.define ACK 10
+
+; Read from SCSI bus using asynchronous handshake.
+; Data is returned as 16-bit words that contain the 8 data bits + 1 parity bit.
+; Number of bytes to receive minus 1 should be written to TX fifo.
+; Number of bytes to receive must be divisible by 2.
+.program scsi_host_async_read
+    .side_set 1
+
+    pull block                  side 1  ; Get number of bytes to receive
+    mov x, osr                  side 1  ; Store to counter X
+
+start:
+    wait 0 gpio REQ             side 1  ; Wait for REQ low
+    in pins, 9                  side 0  ; Assert ACK, read GPIO
+    in null, 7                  side 0  ; Padding bits
+    wait 1 gpio REQ             side 0  ; Wait for REQ high
+    jmp x-- start               side 1  ; Deassert ACK, decrement byte count and jump to start

+ 44 - 0
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2350A.pio.h

@@ -0,0 +1,44 @@
+// -------------------------------------------------- //
+// This file is autogenerated by pioasm; do not edit! //
+// -------------------------------------------------- //
+
+#pragma once
+
+#if !PICO_NO_HARDWARE
+#include "hardware/pio.h"
+#endif
+
+// -------------------- //
+// scsi_host_async_read //
+// -------------------- //
+
+#define scsi_host_async_read_wrap_target 0
+#define scsi_host_async_read_wrap 6
+
+static const uint16_t scsi_host_async_read_program_instructions[] = {
+            //     .wrap_target
+    0x90a0, //  0: pull   block           side 1     
+    0xb027, //  1: mov    x, osr          side 1     
+    0x3009, //  2: wait   0 gpio, 9       side 1     
+    0x4009, //  3: in     pins, 9         side 0     
+    0x4067, //  4: in     null, 7         side 0     
+    0x2089, //  5: wait   1 gpio, 9       side 0     
+    0x1042, //  6: jmp    x--, 2          side 1     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_host_async_read_program = {
+    .instructions = scsi_host_async_read_program_instructions,
+    .length = 7,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_host_async_read_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_host_async_read_wrap_target, offset + scsi_host_async_read_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#endif
+

+ 55 - 23
lib/ZuluSCSI_platform_RP2040/scsi_accel_target.cpp → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target.cpp

@@ -45,10 +45,14 @@
 #endif // ENABLE_AUDIO_OUTPUT
 
 #if defined(ZULUSCSI_PICO) || defined(ZULUSCSI_BS2)
-#include "scsi_accel_target_Pico.pio.h"
+# include "scsi_accel_target_Pico.pio.h"
+#elif defined(ZULUSCSI_PICO_2)
+# include "scsi_accel_target_Pico_2.pio.h"
+#elif defined(ZULUSCSI_RP2350A)
+# include "scsi_accel_target_RP2350A.pio.h"
 #else
-#include "scsi_accel_target_RP2040.pio.h"
-#endif // ZULUSCSI_PICO
+# include "scsi_accel_target_RP2040.pio.h"
+#endif
 
 // SCSI bus write acceleration uses up to 3 PIO state machines:
 // SM0: Convert data bytes to lookup addresses to add parity
@@ -249,7 +253,7 @@ void scsi_accel_rp2040_startWrite(const uint8_t* data, uint32_t count, volatile
     // Any read requests should be matched with a stopRead()
     assert(g_scsi_dma_state != SCSIDMA_READ && g_scsi_dma_state != SCSIDMA_READ_DONE);
 
-    __disable_irq();
+    uint32_t saved_irq = save_and_disable_interrupts();
     if (g_scsi_dma_state == SCSIDMA_WRITE)
     {
         if (!g_scsi_dma.next_app_buf && data == g_scsi_dma.app_buf + g_scsi_dma.app_bytes)
@@ -272,7 +276,7 @@ void scsi_accel_rp2040_startWrite(const uint8_t* data, uint32_t count, volatile
             count = 0;
         }
     }
-    __enable_irq();
+    restore_interrupts(saved_irq);
 
     // Check if the request was combined
     if (count == 0) return;
@@ -371,7 +375,7 @@ bool scsi_accel_rp2040_isWriteFinished(const uint8_t* data)
     
     // Check if this data item is still in queue.
     bool finished = true;
-    __disable_irq();
+    uint32_t saved_irq = save_and_disable_interrupts();
     if (data >= g_scsi_dma.app_buf &&
         data < g_scsi_dma.app_buf + g_scsi_dma.app_bytes &&
         (uint32_t)data >= dma_hw->ch[SCSI_DMA_CH_A].al1_read_addr)
@@ -383,7 +387,7 @@ bool scsi_accel_rp2040_isWriteFinished(const uint8_t* data)
     {
         finished = false; // In queued transfer
     }
-    __enable_irq();
+    restore_interrupts(saved_irq);
 
     return finished;
 }
@@ -634,7 +638,7 @@ void scsi_accel_rp2040_startRead(uint8_t *data, uint32_t count, int *parityError
     // Any write requests should be matched with a stopWrite()
     assert(g_scsi_dma_state != SCSIDMA_WRITE && g_scsi_dma_state != SCSIDMA_WRITE_DONE);
 
-    __disable_irq();
+    uint32_t saved_irq = save_and_disable_interrupts();
     if (g_scsi_dma_state == SCSIDMA_READ)
     {
         if (!g_scsi_dma.next_app_buf && data == g_scsi_dma.app_buf + g_scsi_dma.app_bytes)
@@ -657,7 +661,7 @@ void scsi_accel_rp2040_startRead(uint8_t *data, uint32_t count, int *parityError
             count = 0;
         }
     }
-    __enable_irq();
+    restore_interrupts(saved_irq);
 
     // Check if the request was combined
     if (count == 0) return;
@@ -703,7 +707,7 @@ bool scsi_accel_rp2040_isReadFinished(const uint8_t* data)
 
     // Check if this data item is still in queue.
     bool finished = true;
-    __disable_irq();
+    uint32_t saved_irq = save_and_disable_interrupts();
     if (data >= g_scsi_dma.app_buf &&
         data < g_scsi_dma.app_buf + g_scsi_dma.app_bytes &&
         (uint32_t)data >= dma_hw->ch[SCSI_DMA_CH_A].write_addr)
@@ -715,7 +719,7 @@ bool scsi_accel_rp2040_isReadFinished(const uint8_t* data)
     {
         finished = false; // In queued transfer
     }
-    __enable_irq();
+    restore_interrupts(saved_irq);
 
     return finished;
 }
@@ -1087,29 +1091,57 @@ 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
-            int delay0, delay1, delay2;
+            // The delays are in clock cycles, each taking 6.66 ns. (@150 MHz)
+            // 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 (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;
-
-            if (syncPeriod <= 25)
+#endif            
+            if (syncPeriod < 25)
             {
-                // Fast SCSI timing: 30 ns assertion period, 25 ns skew delay
+                // 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.
-                delay0 = 3;
-                delay1 = 5;
+                // 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)
+                }
+                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;
                 delay2 = totalDelay - delay0 - delay1 - 3;
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;
             }
             else
             {
+                // \TODO set RP23XX timings here
+
                 // Slow SCSI timing: 90 ns assertion period, 55 ns skew delay
-                delay0 = 6;
-                delay1 = 12;
+                delay0 = 7;
+                delay1 = 14;
                 delay2 = totalDelay - delay0 - delay1 - 3;
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_target.h → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target.h


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_target_Pico.pio → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_Pico.pio


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_target_Pico.pio.h → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_Pico.pio.h


+ 124 - 0
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_Pico_2.pio

@@ -0,0 +1,124 @@
+; ZuluSCSI™ - Copyright (c) 2022 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/>.
+
+
+; RP2040 PIO program for accelerating SCSI communication
+; Run "pioasm scsi_accel.pio scsi_accel.pio.h" to regenerate the C header from this.
+; GPIO mapping:
+; - 0-7: DB0-DB7
+; -   8: DBP
+; Side set is REQ pin
+
+.define REQ 17
+.define ACK 26
+
+; Delay from data setup to REQ assertion.
+; deskew delay + cable skew delay = 55 ns minimum
+; One clock cycle is 6.67 ns (150MHz) => delay 8.25 clocks or 9 clocks
+.define REQ_DLY 9
+
+; Adds parity to data that is to be written to SCSI
+; This works by generating addresses for DMA to fetch data from.
+; Register X should be initialized to the base address of the lookup table.
+.program scsi_parity
+    pull block
+    in NULL, 1
+    in OSR, 8
+    in X, 23
+
+; Write to SCSI bus using asynchronous handshake.
+; Data is written as 32-bit words that contain the 8 data bits + 1 parity bit.
+; 23 bits in each word are discarded.
+; Number of bytes to send must be multiple of 2.
+.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
+
+; Read from SCSI bus using sync or async handshake.
+; Data is returned as 32-bit words:
+; - bit  0: always zero
+; - bits 1-8: data byte
+; - bit  9: parity bit
+; - bits 10-31: lookup table address
+; Lookup table address should be loaded into register Y.
+; One dummy word should be written to TX fifo for every byte to receive.
+.program scsi_accel_read
+    .side_set 1
+
+    pull block                  side 1  ; Pull from TX fifo for counting bytes and pacing sync mode
+    wait 1 gpio ACK             side 1  ; Wait for ACK high
+    in null, 1                  side 0  ; Zero bit because lookup table entries are 16-bit
+    wait 0 gpio ACK             side 0  ; Assert REQ, wait for ACK low
+    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.
+; The C code should set autopull threshold to match scsi_sync_write autopush threshold.
+; System DMA will then move words from scsi_sync_write RX fifo to scsi_sync_write_pacer TX fifo.
+.program scsi_sync_write_pacer
+    wait 1 gpio ACK
+    wait 0 gpio ACK   ; Wait for falling edge on ACK
+    out null, 1       ; Let scsi_sync_write send one more byte
+
+; Data pacing state machine for synchronous reads.
+; The delay times will be rewritten by C code to match the negotiated SCSI sync speed.
+; Number of bytes to receive minus one should be loaded into register X.
+; In synchronous mode this generates the REQ pulses and dummy words.
+; In asynchronous mode it just generates dummy words to feed to scsi_accel_read.
+.program scsi_sync_read_pacer
+    .side_set 1
+
+start:
+    push block      [0]      side 1  ; Send dummy word to scsi_accel_read, wait for transfer period
+    jmp x-- start   [0]      side 0  ; Assert REQ, wait for assert time
+
+finish:
+    jmp finish      [0]      side 1
+
+; Parity checker for reads from SCSI bus.
+; Receives 16-bit words from g_scsi_parity_check_lookup
+; Bottom 8 bits are the data byte, which is passed to output FIFO
+; The 9th bit is parity valid bit, which is 1 for valid and 0 for parity error.
+.program scsi_read_parity
+parity_valid:
+    out isr, 8                ; Take the 8 data bits for passing to RX fifo
+    push block                ; Push the data to RX fifo
+    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

+ 224 - 0
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_Pico_2.pio.h

@@ -0,0 +1,224 @@
+// -------------------------------------------------- //
+// This file is autogenerated by pioasm; do not edit! //
+// -------------------------------------------------- //
+
+#pragma once
+
+#if !PICO_NO_HARDWARE
+#include "hardware/pio.h"
+#endif
+
+// ----------- //
+// scsi_parity //
+// ----------- //
+
+#define scsi_parity_wrap_target 0
+#define scsi_parity_wrap 3
+
+static const uint16_t scsi_parity_program_instructions[] = {
+            //     .wrap_target
+    0x80a0, //  0: pull   block                      
+    0x4061, //  1: in     null, 1                    
+    0x40e8, //  2: in     osr, 8                     
+    0x4037, //  3: in     x, 23                      
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_parity_program = {
+    .instructions = scsi_parity_program_instructions,
+    .length = 4,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_parity_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_parity_wrap_target, offset + scsi_parity_wrap);
+    return c;
+}
+#endif
+
+// ---------------------- //
+// scsi_accel_async_write //
+// ---------------------- //
+
+#define scsi_accel_async_write_wrap_target 0
+#define scsi_accel_async_write_wrap 4
+
+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     
+    0x7777, //  2: out    null, 23        side 1 [7] 
+    0x309a, //  3: wait   1 gpio, 26      side 1     
+    0x201a, //  4: wait   0 gpio, 26      side 0     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_accel_async_write_program = {
+    .instructions = scsi_accel_async_write_program_instructions,
+    .length = 5,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_accel_async_write_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_accel_async_write_wrap_target, offset + scsi_accel_async_write_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#endif
+
+// --------------- //
+// scsi_accel_read //
+// --------------- //
+
+#define scsi_accel_read_wrap_target 0
+#define scsi_accel_read_wrap 5
+
+static const uint16_t scsi_accel_read_program_instructions[] = {
+            //     .wrap_target
+    0x90a0, //  0: pull   block           side 1     
+    0x309a, //  1: wait   1 gpio, 26      side 1     
+    0x4061, //  2: in     null, 1         side 0     
+    0x201a, //  3: wait   0 gpio, 26      side 0     
+    0x5009, //  4: in     pins, 9         side 1     
+    0x5056, //  5: in     y, 22           side 1     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_accel_read_program = {
+    .instructions = scsi_accel_read_program_instructions,
+    .length = 6,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_accel_read_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_accel_read_wrap_target, offset + scsi_accel_read_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#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 //
+// --------------------- //
+
+#define scsi_sync_write_pacer_wrap_target 0
+#define scsi_sync_write_pacer_wrap 2
+
+static const uint16_t scsi_sync_write_pacer_program_instructions[] = {
+            //     .wrap_target
+    0x209a, //  0: wait   1 gpio, 26                 
+    0x201a, //  1: wait   0 gpio, 26                 
+    0x6061, //  2: out    null, 1                    
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_sync_write_pacer_program = {
+    .instructions = scsi_sync_write_pacer_program_instructions,
+    .length = 3,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_sync_write_pacer_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_sync_write_pacer_wrap_target, offset + scsi_sync_write_pacer_wrap);
+    return c;
+}
+#endif
+
+// -------------------- //
+// scsi_sync_read_pacer //
+// -------------------- //
+
+#define scsi_sync_read_pacer_wrap_target 0
+#define scsi_sync_read_pacer_wrap 2
+
+static const uint16_t scsi_sync_read_pacer_program_instructions[] = {
+            //     .wrap_target
+    0x9020, //  0: push   block           side 1     
+    0x0040, //  1: jmp    x--, 0          side 0     
+    0x1002, //  2: jmp    2               side 1     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_sync_read_pacer_program = {
+    .instructions = scsi_sync_read_pacer_program_instructions,
+    .length = 3,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_sync_read_pacer_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_sync_read_pacer_wrap_target, offset + scsi_sync_read_pacer_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#endif
+
+// ---------------- //
+// scsi_read_parity //
+// ---------------- //
+
+#define scsi_read_parity_wrap_target 0
+#define scsi_read_parity_wrap 4
+
+static const uint16_t scsi_read_parity_program_instructions[] = {
+            //     .wrap_target
+    0x60c8, //  0: out    isr, 8                     
+    0x8020, //  1: push   block                      
+    0x6038, //  2: out    x, 24                      
+    0x0040, //  3: jmp    x--, 0                     
+    0xc000, //  4: irq    nowait 0                   
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_read_parity_program = {
+    .instructions = scsi_read_parity_program_instructions,
+    .length = 5,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_read_parity_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_read_parity_wrap_target, offset + scsi_read_parity_wrap);
+    return c;
+}
+#endif

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_target_RP2040.pio → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2040.pio


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/scsi_accel_target_RP2040.pio.h → lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2040.pio.h


+ 125 - 0
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2350A.pio

@@ -0,0 +1,125 @@
+; ZuluSCSI™ - Copyright (c) 2022 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/>.
+
+
+; RP2040 PIO program for accelerating SCSI communication
+; Run "pioasm scsi_accel.pio scsi_accel.pio.h" to regenerate the C header from this.
+; GPIO mapping:
+; - 0-7: DB0-DB7
+; -   8: DBP
+; Side set is REQ pin
+
+.define REQ 9
+.define ACK 10
+
+; Delay from data setup to REQ assertion.
+; deskew delay + cable skew delay = 55 ns minimum
+; One clock cycle is 6.67 ns (150MHz) => delay 8.25 clocks or 9 clocks
+.define REQ_DLY 9
+
+
+; Adds parity to data that is to be written to SCSI
+; This works by generating addresses for DMA to fetch data from.
+; Register X should be initialized to the base address of the lookup table.
+.program scsi_parity
+    pull block
+    in NULL, 1
+    in OSR, 8
+    in X, 23
+
+; Write to SCSI bus using asynchronous handshake.
+; Data is written as 32-bit words that contain the 8 data bits + 1 parity bit.
+; 23 bits in each word are discarded.
+; Number of bytes to send must be multiple of 2.
+.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
+
+; Read from SCSI bus using sync or async handshake.
+; Data is returned as 32-bit words:
+; - bit  0: always zero
+; - bits 1-8: data byte
+; - bit  9: parity bit
+; - bits 10-31: lookup table address
+; Lookup table address should be loaded into register Y.
+; One dummy word should be written to TX fifo for every byte to receive.
+.program scsi_accel_read
+    .side_set 1
+
+    pull block                  side 1  ; Pull from TX fifo for counting bytes and pacing sync mode
+    wait 1 gpio ACK             side 1  ; Wait for ACK high
+    in null, 1                  side 0  ; Zero bit because lookup table entries are 16-bit
+    wait 0 gpio ACK             side 0  ; Assert REQ, wait for ACK low
+    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.
+; The C code should set autopull threshold to match scsi_sync_write autopush threshold.
+; System DMA will then move words from scsi_sync_write RX fifo to scsi_sync_write_pacer TX fifo.
+.program scsi_sync_write_pacer
+    wait 1 gpio ACK
+    wait 0 gpio ACK   ; Wait for falling edge on ACK
+    out null, 1       ; Let scsi_sync_write send one more byte
+
+; Data pacing state machine for synchronous reads.
+; The delay times will be rewritten by C code to match the negotiated SCSI sync speed.
+; Number of bytes to receive minus one should be loaded into register X.
+; In synchronous mode this generates the REQ pulses and dummy words.
+; In asynchronous mode it just generates dummy words to feed to scsi_accel_read.
+.program scsi_sync_read_pacer
+    .side_set 1
+
+start:
+    push block      [0]      side 1  ; Send dummy word to scsi_accel_read, wait for transfer period
+    jmp x-- start   [0]      side 0  ; Assert REQ, wait for assert time
+
+finish:
+    jmp finish      [0]      side 1
+
+; Parity checker for reads from SCSI bus.
+; Receives 16-bit words from g_scsi_parity_check_lookup
+; Bottom 8 bits are the data byte, which is passed to output FIFO
+; The 9th bit is parity valid bit, which is 1 for valid and 0 for parity error.
+.program scsi_read_parity
+parity_valid:
+    out isr, 8                ; Take the 8 data bits for passing to RX fifo
+    push block                ; Push the data to RX fifo
+    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

+ 224 - 0
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target_RP2350A.pio.h

@@ -0,0 +1,224 @@
+// -------------------------------------------------- //
+// This file is autogenerated by pioasm; do not edit! //
+// -------------------------------------------------- //
+
+#pragma once
+
+#if !PICO_NO_HARDWARE
+#include "hardware/pio.h"
+#endif
+
+// ----------- //
+// scsi_parity //
+// ----------- //
+
+#define scsi_parity_wrap_target 0
+#define scsi_parity_wrap 3
+
+static const uint16_t scsi_parity_program_instructions[] = {
+            //     .wrap_target
+    0x80a0, //  0: pull   block                      
+    0x4061, //  1: in     null, 1                    
+    0x40e8, //  2: in     osr, 8                     
+    0x4037, //  3: in     x, 23                      
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_parity_program = {
+    .instructions = scsi_parity_program_instructions,
+    .length = 4,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_parity_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_parity_wrap_target, offset + scsi_parity_wrap);
+    return c;
+}
+#endif
+
+// ---------------------- //
+// scsi_accel_async_write //
+// ---------------------- //
+
+#define scsi_accel_async_write_wrap_target 0
+#define scsi_accel_async_write_wrap 4
+
+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     
+    0x7777, //  2: out    null, 23        side 1 [7] 
+    0x308a, //  3: wait   1 gpio, 10      side 1     
+    0x200a, //  4: wait   0 gpio, 10      side 0     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_accel_async_write_program = {
+    .instructions = scsi_accel_async_write_program_instructions,
+    .length = 5,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_accel_async_write_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_accel_async_write_wrap_target, offset + scsi_accel_async_write_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#endif
+
+// --------------- //
+// scsi_accel_read //
+// --------------- //
+
+#define scsi_accel_read_wrap_target 0
+#define scsi_accel_read_wrap 5
+
+static const uint16_t scsi_accel_read_program_instructions[] = {
+            //     .wrap_target
+    0x90a0, //  0: pull   block           side 1     
+    0x308a, //  1: wait   1 gpio, 10      side 1     
+    0x4061, //  2: in     null, 1         side 0     
+    0x200a, //  3: wait   0 gpio, 10      side 0     
+    0x5009, //  4: in     pins, 9         side 1     
+    0x5056, //  5: in     y, 22           side 1     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_accel_read_program = {
+    .instructions = scsi_accel_read_program_instructions,
+    .length = 6,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_accel_read_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_accel_read_wrap_target, offset + scsi_accel_read_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#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 //
+// --------------------- //
+
+#define scsi_sync_write_pacer_wrap_target 0
+#define scsi_sync_write_pacer_wrap 2
+
+static const uint16_t scsi_sync_write_pacer_program_instructions[] = {
+            //     .wrap_target
+    0x208a, //  0: wait   1 gpio, 10                 
+    0x200a, //  1: wait   0 gpio, 10                 
+    0x6061, //  2: out    null, 1                    
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_sync_write_pacer_program = {
+    .instructions = scsi_sync_write_pacer_program_instructions,
+    .length = 3,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_sync_write_pacer_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_sync_write_pacer_wrap_target, offset + scsi_sync_write_pacer_wrap);
+    return c;
+}
+#endif
+
+// -------------------- //
+// scsi_sync_read_pacer //
+// -------------------- //
+
+#define scsi_sync_read_pacer_wrap_target 0
+#define scsi_sync_read_pacer_wrap 2
+
+static const uint16_t scsi_sync_read_pacer_program_instructions[] = {
+            //     .wrap_target
+    0x9020, //  0: push   block           side 1     
+    0x0040, //  1: jmp    x--, 0          side 0     
+    0x1002, //  2: jmp    2               side 1     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_sync_read_pacer_program = {
+    .instructions = scsi_sync_read_pacer_program_instructions,
+    .length = 3,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_sync_read_pacer_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_sync_read_pacer_wrap_target, offset + scsi_sync_read_pacer_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#endif
+
+// ---------------- //
+// scsi_read_parity //
+// ---------------- //
+
+#define scsi_read_parity_wrap_target 0
+#define scsi_read_parity_wrap 4
+
+static const uint16_t scsi_read_parity_program_instructions[] = {
+            //     .wrap_target
+    0x60c8, //  0: out    isr, 8                     
+    0x8020, //  1: push   block                      
+    0x6038, //  2: out    x, 24                      
+    0x0040, //  3: jmp    x--, 0                     
+    0xc000, //  4: irq    nowait 0                   
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program scsi_read_parity_program = {
+    .instructions = scsi_read_parity_program_instructions,
+    .length = 5,
+    .origin = -1,
+};
+
+static inline pio_sm_config scsi_read_parity_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + scsi_read_parity_wrap_target, offset + scsi_read_parity_wrap);
+    return c;
+}
+#endif

+ 2 - 3
lib/ZuluSCSI_platform_RP2040/sd_card_sdio.cpp → lib/ZuluSCSI_platform_RP2MCU/sd_card_sdio.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022-2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
  * Copyright (c) 2024 Tech by Androda, LLC
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
@@ -20,7 +20,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 **/
 
-// Driver for accessing SD card in SDIO mode on RP2040.
+// Driver for accessing SD card in SDIO mode on RP2040 and RP23XX.
 
 #include "ZuluSCSI_platform.h"
 
@@ -42,7 +42,6 @@ static sdio_status_t g_sdio_error;
 static uint32_t g_sdio_dma_buf[128];
 static uint32_t g_sdio_sector_count;
 
-
 #define checkReturnOk(call) ((g_sdio_error = (call)) == SDIO_OK ? true : logSDError(__LINE__))
 static bool logSDError(int line)
 {

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/sd_card_spi.cpp → lib/ZuluSCSI_platform_RP2MCU/sd_card_spi.cpp


+ 12 - 7
lib/ZuluSCSI_platform_RP2040/sdio.cpp → lib/ZuluSCSI_platform_RP2MCU/sdio.cpp

@@ -1,7 +1,7 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022-2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
  * Copyright (c) 2024 Tech by Androda, LLC
- * 
+ *  
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
  * https://www.gnu.org/licenses/gpl-3.0.html
@@ -20,7 +20,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 **/
 
-// Implementation of SDIO communication for RP2040
+// Implementation of SDIO communication for RP2040 and RP23XX
 //
 // The RP2040 official work-in-progress code at
 // https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card
@@ -34,13 +34,18 @@
 #include <hardware/pio.h>
 #include <hardware/dma.h>
 #include <hardware/gpio.h>
+#include <hardware/structs/scb.h>
 #include <ZuluSCSI_platform.h>
 #include <ZuluSCSI_log.h>
 
 #if defined(ZULUSCSI_PICO) || defined(ZULUSCSI_BS2)
-#include "sdio_Pico.pio.h"
+# include "sdio_Pico.pio.h"
+#elif defined(ZULUSCSI_PICO_2)
+# include "sdio_Pico_2.pio.h"
+#elif defined(ZULUSCSI_RP2350A)
+# include "sdio_RP2350A.pio.h"
 #else
-#include "sdio_RP2040.pio.h"
+# include "sdio_RP2040.pio.h"
 #endif
 
 #define SDIO_PIO pio1
@@ -183,7 +188,6 @@ waitagain:
     return SDIO_OK;
 }
 
-
 /*******************************************************
  * Basic SDIO command execution
  *******************************************************/
@@ -742,7 +746,8 @@ static void rp2040_sdio_tx_irq()
 // Check if transmission is complete
 sdio_status_t rp2040_sdio_tx_poll(uint32_t *bytes_complete)
 {
-    if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
+    // SCB_ICSR_VECTACTIVE_Msk (0x1FFUL)
+    if (scb_hw->icsr & (0x1FFUL))
     {
         // Verify that IRQ handler gets called even if we are in hardfault handler
         rp2040_sdio_tx_irq();

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/sdio.h → lib/ZuluSCSI_platform_RP2MCU/sdio.h


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/sdio_Pico.pio → lib/ZuluSCSI_platform_RP2MCU/sdio_Pico.pio


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/sdio_Pico.pio.h → lib/ZuluSCSI_platform_RP2MCU/sdio_Pico.pio.h


+ 164 - 0
lib/ZuluSCSI_platform_RP2MCU/sdio_Pico_2.pio

@@ -0,0 +1,164 @@
+; ZuluSCSI™ - Copyright (c) 2022 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/>.
+
+; RP2040 PIO program for implementing SD card access in SDIO mode
+; Run "pioasm rp2040_sdio.pio rp2040_sdio.pio.h" to regenerate the C header from this.
+
+; The RP2040 official work-in-progress code at
+; https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card
+; may be useful reference, but this is independent implementation.
+;
+; For official SDIO specifications, refer to:
+; https://www.sdcard.org/downloads/pls/
+; "SDIO Physical Layer Simplified Specification Version 8.00"
+
+; Clock settings
+; For 3.3V communication the available speeds are:
+; - Default speed: max. 25 MHz clock
+; - High speed:    max. 50 MHz clock
+;
+; From the default RP2040 clock speed of 125 MHz, the closest dividers
+; are 3 for 41.7 MHz and 5 for 25 MHz. The CPU can apply further divider
+; through state machine registers for the initial handshake.
+;
+; 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
+
+; State machine 0 is used to:
+; - generate continuous clock on SDIO_CLK
+; - send CMD packets
+; - receive response packets
+;
+; Pin mapping for this state machine:
+; - Sideset    : CLK
+; - IN/OUT/SET : CMD
+; - JMP_PIN    : CMD
+;
+; The commands to send are put on TX fifo and must have two words:
+; Word 0 bits 31-24: Number of bits in command minus one (usually 47)
+; Word 0 bits 23-00: First 24 bits of the command packet, shifted out MSB first
+; Word 1 bits 31-08: Last 24 bits of the command packet, shifted out MSB first
+; Word 1 bits 07-00: Number of bits in response minus one (usually 47), or 0 if no response
+;
+; The response is put on RX fifo, starting with the MSB.
+; Partial last word will be padded with zero bits at the top.
+;
+; The state machine EXECCTRL should be set so that STATUS indicates TX FIFO < 2
+; and that AUTOPULL and AUTOPUSH are enabled.
+
+.program sdio_cmd_clk
+    .side_set 1
+
+    mov OSR, NULL       side 1 [D1]    ; Make sure OSR is full of zeros to prevent autopull
+
+wait_cmd:
+    mov Y, !STATUS      side 0 [D0]    ; Check if TX FIFO has data
+    jmp !Y wait_cmd     side 1 [D1]
+
+load_cmd:
+    out NULL, 32        side 0 [D0]    ; Load first word (trigger autopull)
+    out X, 8            side 1 [D1]    ; Number of bits to send
+    set pins, 1         side 0 [D0]    ; Initial state of CMD is high
+    set pindirs, 1      side 1 [D1]    ; Set SDIO_CMD as output
+
+send_cmd:
+    out pins, 1         side 0 [D0]    ; Write output on falling edge of CLK
+    jmp X-- send_cmd    side 1 [D1]
+
+prep_resp:
+    set pindirs, 0      side 0 [D0]    ; Set SDIO_CMD as input
+    out X, 8            side 1 [D1]    ; Get number of bits in response
+    nop                 side 0 [D0]    ; For clock alignment
+    jmp !X resp_done    side 1 [D1]    ; Check if we expect a response
+
+wait_resp:
+    nop                  side 0 [D0]
+    jmp PIN wait_resp    side 1 [D1]    ; Loop until SDIO_CMD = 0
+
+    ; Note: input bits are read at the same time as we write CLK=0.
+    ; Because the host controls the clock, the read happens before
+    ; the card sees the falling clock edge. This gives maximum time
+    ; for the data bit to settle.
+read_resp:
+    in PINS, 1          side 0 [D0]    ; Read input data bit
+    jmp X-- read_resp   side 1 [D1]    ; Loop to receive all data bits
+
+resp_done:
+    push                side 0 [D0]    ; Push the remaining part of response
+
+; State machine 1 is used to send and receive data blocks.
+; Pin mapping for this state machine:
+; - IN / OUT: SDIO_D0-D3
+; - GPIO defined at beginning of this file: SDIO_CLK
+
+; Data reception program
+; This program will wait for initial start of block token and then
+; receive a data block. The application must set number of nibbles
+; to receive minus 1 to Y register before running this program.
+.program sdio_data_rx
+
+wait_start:
+    mov X, Y                               ; Reinitialize number of nibbles to receive
+    wait 0 pin 0                           ; Wait for zero state on D0
+    wait 1 gpio SDIO_CLK_GPIO  [CLKDIV-1]  ; Wait for rising edge and then whole clock cycle
+
+rx_data:
+    in PINS, 4                 [CLKDIV-2]  ; Read nibble
+    jmp X--, rx_data
+
+; Data transmission program
+;
+; Before running this program, pindirs should be set as output
+; and register X should be initialized with the number of nibbles
+; to send minus 1 (typically 8 + 1024 + 16 + 1 - 1 = 1048)
+; and register Y with the number of response bits minus 1 (typically 31).
+;
+; Words written to TX FIFO must be:
+; - Word 0: start token 0xFFFFFFF0
+; - Word 1-128: transmitted data (512 bytes)
+; - Word 129-130: CRC checksum
+; - Word 131: end token 0xFFFFFFFF
+;
+; After the card reports idle status, RX FIFO will get a word that
+; contains the D0 line response from card.
+
+.program sdio_data_tx
+    wait 0 gpio SDIO_CLK_GPIO  
+    wait 1 gpio SDIO_CLK_GPIO  [CLKDIV + D1 - 1]; Synchronize so that write occurs on falling edge
+
+tx_loop:
+    out PINS, 4                [D0]    ; Write nibble and wait for whole clock cycle
+    jmp X-- tx_loop            [D1]
+
+    set pindirs, 0x00          [D0]    ; Set data bus as input
+
+.wrap_target
+response_loop:
+    in PINS, 1                 [D1]    ; Read D0 on rising edge
+    jmp Y--, response_loop     [D0]
+
+wait_idle:
+    wait 1 pin 0               [D1]    ; Wait for card to indicate idle condition
+    push                       [D0]    ; Push the response token
+.wrap

+ 121 - 0
lib/ZuluSCSI_platform_RP2MCU/sdio_Pico_2.pio.h

@@ -0,0 +1,121 @@
+// -------------------------------------------------- //
+// This file is autogenerated by pioasm; do not edit! //
+// -------------------------------------------------- //
+
+#pragma once
+
+#if !PICO_NO_HARDWARE
+#include "hardware/pio.h"
+#endif
+
+// ------------ //
+// sdio_cmd_clk //
+// ------------ //
+
+#define sdio_cmd_clk_wrap_target 0
+#define sdio_cmd_clk_wrap 17
+
+static const uint16_t sdio_cmd_clk_program_instructions[] = {
+            //     .wrap_target
+    0xb1e3, //  0: mov    osr, null       side 1 [1] 
+    0xa24d, //  1: mov    y, !status      side 0 [2] 
+    0x1161, //  2: jmp    !y, 1           side 1 [1] 
+    0x6260, //  3: out    null, 32        side 0 [2] 
+    0x7128, //  4: out    x, 8            side 1 [1] 
+    0xe201, //  5: set    pins, 1         side 0 [2] 
+    0xf181, //  6: set    pindirs, 1      side 1 [1] 
+    0x6201, //  7: out    pins, 1         side 0 [2] 
+    0x1147, //  8: jmp    x--, 7          side 1 [1] 
+    0xe280, //  9: set    pindirs, 0      side 0 [2] 
+    0x7128, // 10: out    x, 8            side 1 [1] 
+    0xa242, // 11: nop                    side 0 [2] 
+    0x1131, // 12: jmp    !x, 17          side 1 [1] 
+    0xa242, // 13: nop                    side 0 [2] 
+    0x11cd, // 14: jmp    pin, 13         side 1 [1] 
+    0x4201, // 15: in     pins, 1         side 0 [2] 
+    0x114f, // 16: jmp    x--, 15         side 1 [1] 
+    0x8220, // 17: push   block           side 0 [2] 
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program sdio_cmd_clk_program = {
+    .instructions = sdio_cmd_clk_program_instructions,
+    .length = 18,
+    .origin = -1,
+};
+
+static inline pio_sm_config sdio_cmd_clk_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + sdio_cmd_clk_wrap_target, offset + sdio_cmd_clk_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#endif
+
+// ------------ //
+// sdio_data_rx //
+// ------------ //
+
+#define sdio_data_rx_wrap_target 0
+#define sdio_data_rx_wrap 4
+
+static const uint16_t sdio_data_rx_program_instructions[] = {
+            //     .wrap_target
+    0xa022, //  0: mov    x, y                       
+    0x2020, //  1: wait   0 pin, 0                   
+    0x248A, //  2: wait   1 gpio, 10             [4] 
+    0x4304, //  3: in     pins, 4                [3] 
+    0x0043, //  4: jmp    x--, 3                     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program sdio_data_rx_program = {
+    .instructions = sdio_data_rx_program_instructions,
+    .length = 5,
+    .origin = -1,
+};
+
+static inline pio_sm_config sdio_data_rx_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + sdio_data_rx_wrap_target, offset + sdio_data_rx_wrap);
+    return c;
+}
+#endif
+
+// ------------ //
+// sdio_data_tx //
+// ------------ //
+
+#define sdio_data_tx_wrap_target 5
+#define sdio_data_tx_wrap 8
+
+static const uint16_t sdio_data_tx_program_instructions[] = {
+    0x200A, //  0: wait   0 gpio, 10                 
+    0x258A, //  1: wait   1 gpio, 10             [5] 
+    0x6204, //  2: out    pins, 4                [2] 
+    0x0142, //  3: jmp    x--, 2                 [1] 
+    0xe280, //  4: set    pindirs, 0             [2] 
+            //     .wrap_target
+    0x4101, //  5: in     pins, 1                [1] 
+    0x0285, //  6: jmp    y--, 5                 [2] 
+    0x21a0, //  7: wait   1 pin, 0               [1] 
+    0x8220, //  8: push   block                  [2] 
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program sdio_data_tx_program = {
+    .instructions = sdio_data_tx_program_instructions,
+    .length = 9,
+    .origin = -1,
+};
+
+static inline pio_sm_config sdio_data_tx_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + sdio_data_tx_wrap_target, offset + sdio_data_tx_wrap);
+    return c;
+}
+#endif
+

+ 0 - 0
lib/ZuluSCSI_platform_RP2040/sdio_RP2040.pio → lib/ZuluSCSI_platform_RP2MCU/sdio_RP2040.pio


+ 0 - 0
lib/ZuluSCSI_platform_RP2040/sdio_RP2040.pio.h → lib/ZuluSCSI_platform_RP2MCU/sdio_RP2040.pio.h


+ 164 - 0
lib/ZuluSCSI_platform_RP2MCU/sdio_RP2350A.pio

@@ -0,0 +1,164 @@
+; ZuluSCSI™ - Copyright (c) 2022 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/>.
+
+; RP2040 PIO program for implementing SD card access in SDIO mode
+; Run "pioasm rp2040_sdio.pio rp2040_sdio.pio.h" to regenerate the C header from this.
+
+; The RP2040 official work-in-progress code at
+; https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card
+; may be useful reference, but this is independent implementation.
+;
+; For official SDIO specifications, refer to:
+; https://www.sdcard.org/downloads/pls/
+; "SDIO Physical Layer Simplified Specification Version 8.00"
+
+; Clock settings
+; For 3.3V communication the available speeds are:
+; - Default speed: max. 25 MHz clock
+; - High speed:    max. 50 MHz clock
+;
+; From the default RP2040 clock speed of 125 MHz, the closest dividers
+; are 3 for 41.7 MHz and 5 for 25 MHz. The CPU can apply further divider
+; through state machine registers for the initial handshake.
+;
+; 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 18
+
+; State machine 0 is used to:
+; - generate continuous clock on SDIO_CLK
+; - send CMD packets
+; - receive response packets
+;
+; Pin mapping for this state machine:
+; - Sideset    : CLK
+; - IN/OUT/SET : CMD
+; - JMP_PIN    : CMD
+;
+; The commands to send are put on TX fifo and must have two words:
+; Word 0 bits 31-24: Number of bits in command minus one (usually 47)
+; Word 0 bits 23-00: First 24 bits of the command packet, shifted out MSB first
+; Word 1 bits 31-08: Last 24 bits of the command packet, shifted out MSB first
+; Word 1 bits 07-00: Number of bits in response minus one (usually 47), or 0 if no response
+;
+; The response is put on RX fifo, starting with the MSB.
+; Partial last word will be padded with zero bits at the top.
+;
+; The state machine EXECCTRL should be set so that STATUS indicates TX FIFO < 2
+; and that AUTOPULL and AUTOPUSH are enabled.
+
+.program sdio_cmd_clk
+    .side_set 1
+
+    mov OSR, NULL       side 1 [D1]    ; Make sure OSR is full of zeros to prevent autopull
+
+wait_cmd:
+    mov Y, !STATUS      side 0 [D0]    ; Check if TX FIFO has data
+    jmp !Y wait_cmd     side 1 [D1]
+
+load_cmd:
+    out NULL, 32        side 0 [D0]    ; Load first word (trigger autopull)
+    out X, 8            side 1 [D1]    ; Number of bits to send
+    set pins, 1         side 0 [D0]    ; Initial state of CMD is high
+    set pindirs, 1      side 1 [D1]    ; Set SDIO_CMD as output
+
+send_cmd:
+    out pins, 1         side 0 [D0]    ; Write output on falling edge of CLK
+    jmp X-- send_cmd    side 1 [D1]
+
+prep_resp:
+    set pindirs, 0      side 0 [D0]    ; Set SDIO_CMD as input
+    out X, 8            side 1 [D1]    ; Get number of bits in response
+    nop                 side 0 [D0]    ; For clock alignment
+    jmp !X resp_done    side 1 [D1]    ; Check if we expect a response
+
+wait_resp:
+    nop                  side 0 [D0]
+    jmp PIN wait_resp    side 1 [D1]    ; Loop until SDIO_CMD = 0
+
+    ; Note: input bits are read at the same time as we write CLK=0.
+    ; Because the host controls the clock, the read happens before
+    ; the card sees the falling clock edge. This gives maximum time
+    ; for the data bit to settle.
+read_resp:
+    in PINS, 1          side 0 [D0]    ; Read input data bit
+    jmp X-- read_resp   side 1 [D1]    ; Loop to receive all data bits
+
+resp_done:
+    push                side 0 [D0]    ; Push the remaining part of response
+
+; State machine 1 is used to send and receive data blocks.
+; Pin mapping for this state machine:
+; - IN / OUT: SDIO_D0-D3
+; - GPIO defined at beginning of this file: SDIO_CLK
+
+; Data reception program
+; This program will wait for initial start of block token and then
+; receive a data block. The application must set number of nibbles
+; to receive minus 1 to Y register before running this program.
+.program sdio_data_rx
+
+wait_start:
+    mov X, Y                               ; Reinitialize number of nibbles to receive
+    wait 0 pin 0                           ; Wait for zero state on D0
+    wait 1 gpio SDIO_CLK_GPIO  [CLKDIV-1]  ; Wait for rising edge and then whole clock cycle
+
+rx_data:
+    in PINS, 4                 [CLKDIV-2]  ; Read nibble
+    jmp X--, rx_data
+
+; Data transmission program
+;
+; Before running this program, pindirs should be set as output
+; and register X should be initialized with the number of nibbles
+; to send minus 1 (typically 8 + 1024 + 16 + 1 - 1 = 1048)
+; and register Y with the number of response bits minus 1 (typically 31).
+;
+; Words written to TX FIFO must be:
+; - Word 0: start token 0xFFFFFFF0
+; - Word 1-128: transmitted data (512 bytes)
+; - Word 129-130: CRC checksum
+; - Word 131: end token 0xFFFFFFFF
+;
+; After the card reports idle status, RX FIFO will get a word that
+; contains the D0 line response from card.
+
+.program sdio_data_tx
+    wait 0 gpio SDIO_CLK_GPIO  
+    wait 1 gpio SDIO_CLK_GPIO  [CLKDIV + D1 - 1]; Synchronize so that write occurs on falling edge
+
+tx_loop:
+    out PINS, 4                [D0]    ; Write nibble and wait for whole clock cycle
+    jmp X-- tx_loop            [D1]
+
+    set pindirs, 0x00          [D0]    ; Set data bus as input
+
+.wrap_target
+response_loop:
+    in PINS, 1                 [D1]    ; Read D0 on rising edge
+    jmp Y--, response_loop     [D0]
+
+wait_idle:
+    wait 1 pin 0               [D1]    ; Wait for card to indicate idle condition
+    push                       [D0]    ; Push the response token
+.wrap

+ 121 - 0
lib/ZuluSCSI_platform_RP2MCU/sdio_RP2350A.pio.h

@@ -0,0 +1,121 @@
+// -------------------------------------------------- //
+// This file is autogenerated by pioasm; do not edit! //
+// -------------------------------------------------- //
+
+#pragma once
+
+#if !PICO_NO_HARDWARE
+#include "hardware/pio.h"
+#endif
+
+// ------------ //
+// sdio_cmd_clk //
+// ------------ //
+
+#define sdio_cmd_clk_wrap_target 0
+#define sdio_cmd_clk_wrap 17
+
+static const uint16_t sdio_cmd_clk_program_instructions[] = {
+            //     .wrap_target
+    0xb1e3, //  0: mov    osr, null       side 1 [1] 
+    0xa24d, //  1: mov    y, !status      side 0 [2] 
+    0x1161, //  2: jmp    !y, 1           side 1 [1] 
+    0x6260, //  3: out    null, 32        side 0 [2] 
+    0x7128, //  4: out    x, 8            side 1 [1] 
+    0xe201, //  5: set    pins, 1         side 0 [2] 
+    0xf181, //  6: set    pindirs, 1      side 1 [1] 
+    0x6201, //  7: out    pins, 1         side 0 [2] 
+    0x1147, //  8: jmp    x--, 7          side 1 [1] 
+    0xe280, //  9: set    pindirs, 0      side 0 [2] 
+    0x7128, // 10: out    x, 8            side 1 [1] 
+    0xa242, // 11: nop                    side 0 [2] 
+    0x1131, // 12: jmp    !x, 17          side 1 [1] 
+    0xa242, // 13: nop                    side 0 [2] 
+    0x11cd, // 14: jmp    pin, 13         side 1 [1] 
+    0x4201, // 15: in     pins, 1         side 0 [2] 
+    0x114f, // 16: jmp    x--, 15         side 1 [1] 
+    0x8220, // 17: push   block           side 0 [2] 
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program sdio_cmd_clk_program = {
+    .instructions = sdio_cmd_clk_program_instructions,
+    .length = 18,
+    .origin = -1,
+};
+
+static inline pio_sm_config sdio_cmd_clk_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + sdio_cmd_clk_wrap_target, offset + sdio_cmd_clk_wrap);
+    sm_config_set_sideset(&c, 1, false, false);
+    return c;
+}
+#endif
+
+// ------------ //
+// sdio_data_rx //
+// ------------ //
+
+#define sdio_data_rx_wrap_target 0
+#define sdio_data_rx_wrap 4
+
+static const uint16_t sdio_data_rx_program_instructions[] = {
+            //     .wrap_target
+    0xa022, //  0: mov    x, y                       
+    0x2020, //  1: wait   0 pin, 0                   
+    0x2492, //  2: wait   1 gpio, 18             [4] 
+    0x4304, //  3: in     pins, 4                [3] 
+    0x0043, //  4: jmp    x--, 3                     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program sdio_data_rx_program = {
+    .instructions = sdio_data_rx_program_instructions,
+    .length = 5,
+    .origin = -1,
+};
+
+static inline pio_sm_config sdio_data_rx_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + sdio_data_rx_wrap_target, offset + sdio_data_rx_wrap);
+    return c;
+}
+#endif
+
+// ------------ //
+// sdio_data_tx //
+// ------------ //
+
+#define sdio_data_tx_wrap_target 5
+#define sdio_data_tx_wrap 8
+
+static const uint16_t sdio_data_tx_program_instructions[] = {
+    0x2012, //  0: wait   0 gpio, 18                 
+    0x2592, //  1: wait   1 gpio, 18             [5] 
+    0x6204, //  2: out    pins, 4                [2] 
+    0x0142, //  3: jmp    x--, 2                 [1] 
+    0xe280, //  4: set    pindirs, 0             [2] 
+            //     .wrap_target
+    0x4101, //  5: in     pins, 1                [1] 
+    0x0285, //  6: jmp    y--, 5                 [2] 
+    0x21a0, //  7: wait   1 pin, 0               [1] 
+    0x8220, //  8: push   block                  [2] 
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program sdio_data_tx_program = {
+    .instructions = sdio_data_tx_program_instructions,
+    .length = 9,
+    .origin = -1,
+};
+
+static inline pio_sm_config sdio_data_tx_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + sdio_data_tx_wrap_target, offset + sdio_data_tx_wrap);
+    return c;
+}
+#endif
+

+ 95 - 92
platformio.ini

@@ -1,7 +1,7 @@
 ; PlatformIO Project Configuration File https://docs.platformio.org/page/projectconf.html
 
 [platformio]
-default_envs = ZuluSCSIv1_0, ZuluSCSIv1_0_mini, ZuluSCSIv1_1_plus, ZuluSCSI_RP2040, ZuluSCSI_RP2040_Audio, ZuluSCSI_Pico, ZuluSCSI_Pico_DaynaPORT, ZuluSCSI_BS2
+default_envs = ZuluSCSIv1_0, ZuluSCSIv1_0_mini, ZuluSCSIv1_1_plus, ZuluSCSI_RP2040, ZuluSCSI_RP2040_Audio, ZuluSCSI_Pico, ZuluSCSI_Pico_DaynaPORT, ZuluSCSI_BS2, ZuluSCSI_Pico_2
 
 ; Example platform to serve as a base for porting efforts
 [env:template]
@@ -93,35 +93,27 @@ build_flags =
      -DSDFAT_NOARDUINO
      -DFILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC
 
-; ZuluSCSI RP2040 hardware platform, based on the Raspberry Pi foundation RP2040 microcontroller
-[env:ZuluSCSI_RP2040]
-platform = https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c
+; ZuluSCSI settings shared among Raspberry Pi microcontroller like the RP2040 and RP2350
+[env:ZuluSCSI_RP2MCU]
+platform = https://github.com/maxgerhardt/platform-raspberrypi.git#2d445020acf8b792768a5fa5ba1870ac9607c11c
 platform_packages =
-    framework-arduinopico@https://github.com/rabbitholecomputing/arduino-pico.git#v3.6.0-DaynaPORT
-board_build.core = earlephilhower
-board = zuluscsi_rp2040
-framework = arduino
-; How much flash in bytes the bootloader and main app will be allocated
-; It is used as the starting point for a ROM image saved in flash
-; Changing this will cause issues with boards that already have a ROM drive in flash
-program_flash_allocation = 360448
+    framework-arduinopico@https://github.com/rabbitholecomputing/arduino-pico.git#v4.1.1-DaynaPORT
 extra_scripts =
     src/build_bootloader.py
-    lib/ZuluSCSI_platform_RP2040/process-linker-script.py
-board_build.ldscript = ${BUILD_DIR}/rp2040.ld
-ldscript_bootloader = lib/ZuluSCSI_platform_RP2040/rp2040_btldr.ld
+    src/process-linker-script.py
+board_build.core = earlephilhower
+board_build.ldscript = ${BUILD_DIR}/rp_linker.ld ; created by src/process-linker-script.py
+framework = arduino
 lib_deps =
     SdFat=https://github.com/rabbitholecomputing/SdFat#2.2.3-gpt
     minIni
-    ZuluSCSI_platform_RP2040
     SCSI2SD
     CUEParser=https://github.com/rabbitholecomputing/CUEParser
+    ZuluSCSI_platform_RP2MCU
 upload_protocol = cmsis-dap
 debug_tool = cmsis-dap
 debug_build_flags =
     -O2 -ggdb -g3
-; The values can be adjusted down to get a debug build to fit in to SRAM
-    -DLOGBUFSIZE=4096
 build_flags =
     -O2 -Isrc -ggdb -g3
     -Wall -Wno-sign-compare -Wno-ignored-qualifiers
@@ -131,14 +123,34 @@ build_flags =
     -DHAS_SDIO_CLASS
     -DUSE_ARDUINO=1
     -DPICO_FLASH_SPI_CLKDIV=2
-    -DZULUSCSI_V2_0
-    -DROMDRIVE_OFFSET=${env:ZuluSCSI_RP2040.program_flash_allocation}
-; build flags mirroring the framework-arduinopico#v3.6.0-DaynaPORT static library build
+    -DPLATFORM_MASS_STORAGE
+    -DFILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC
+; build flags mirroring the framework-arduinopico#v4.1.1-DaynaPORT static library build
     -DPICO_CYW43_ARCH_POLL=1
 	-DCYW43_LWIP=0
 	-DCYW43_USE_OTP_MAC=0
-    -DPLATFORM_MASS_STORAGE
-    -DFILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC
+
+
+
+; ZuluSCSI RP2040 hardware platform, based on the Raspberry Pi foundation RP2040 microcontroller
+[env:ZuluSCSI_RP2040]
+extends = env:ZuluSCSI_RP2MCU
+board = zuluscsi_rp2040
+; How much flash in bytes the bootloader and main app will be allocated
+; It is used as the starting point for a ROM image saved in flash
+; Changing this will cause issues with boards that already have a ROM drive in flash
+program_flash_allocation = 360448
+linker_script_template = lib/ZuluSCSI_platform_RP2MCU/rp2040-template.ld
+ldscript_bootloader = lib/ZuluSCSI_platform_RP2MCU/rp2040_btldr.ld
+debug_build_flags =
+    ${env:ZuluSCSI_RP2MCU.debug_build_flags}
+; The values can be adjusted down to get a debug build to fit in to SRAM
+    -DLOGBUFSIZE=4096
+build_flags =
+    ${env:ZuluSCSI_RP2MCU.build_flags}
+    -DZULUSCSI_V2_0
+    -DZULUSCSI_MCU_RP20XX
+    -DROMDRIVE_OFFSET=${env:ZuluSCSI_RP2040.program_flash_allocation}
 
 ; ZuluSCSI RP2040 hardware platform, as above, but with audio output support enabled
 [env:ZuluSCSI_RP2040_Audio]
@@ -151,71 +163,42 @@ build_flags =
 ; Variant of RP2040 platform, based on Raspberry Pico board and a carrier PCB
 ; Part of the ZuluSCSI_RP2040 platform, but with different pins.
 [env:ZuluSCSI_Pico]
-extends = env:ZuluSCSI_RP2040
+extends = env:ZuluSCSI_RP2MCU
+board = rpipico
+program_flash_allocation = 360448
+linker_script_template = lib/ZuluSCSI_platform_RP2MCU/rp2040-template.ld
+ldscript_bootloader = lib/ZuluSCSI_platform_RP2MCU/rp2040_btldr.ld
 build_flags =
-    -O2 -Isrc -ggdb -g3
-    -Wall -Wno-sign-compare -Wno-ignored-qualifiers
-    -DSPI_DRIVER_SELECT=3
-    -DSD_CHIP_SELECT_MODE=2
-    -DENABLE_DEDICATED_SPI=1
-    -DHAS_SDIO_CLASS
-    -DUSE_ARDUINO=1
+    ${env:ZuluSCSI_RP2MCU.build_flags}
     -DZULUSCSI_PICO
-    -DROMDRIVE_OFFSET=${env:ZuluSCSI_RP2040.program_flash_allocation}
-; build flags mirroring the framework-arduinopico#v3.6.0-DaynaPORT static library build
-    -DPICO_CYW43_ARCH_POLL=1
-	-DCYW43_LWIP=0
-	-DCYW43_USE_OTP_MAC=0
-    -DPLATFORM_MASS_STORAGE
-    -DFILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC
+    -DZULUSCSI_MCU_RP20XX
+    -DROMDRIVE_OFFSET=${env:ZuluSCSI_Pico.program_flash_allocation}
 
 ; Build for the ZuluSCSI Pico carrier board with a Pico-W
 ; for SCSI DaynaPORT emulation
 [env:ZuluSCSI_Pico_DaynaPORT]
-platform = https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c
-platform_packages =
-    framework-arduinopico@https://github.com/rabbitholecomputing/arduino-pico.git#v3.6.0-DaynaPORT
-framework = arduino
+extends = env:ZuluSCSI_RP2MCU
 board = rpipicow
-board_build.core = earlephilhower
 ; How much flash in bytes the bootloader and main app will be allocated
 ; It is used as the starting point for a ROM image saved in flash
 program_flash_allocation = 589824
-extra_scripts =
-    src/build_bootloader.py
-    lib/ZuluSCSI_platform_RP2040/process-linker-script.py
-ldscript_bootloader = lib/ZuluSCSI_platform_RP2040/rp2040_btldr.ld
-board_build.ldscript = ${BUILD_DIR}/rp2040.ld
-debug_tool = cmsis-dap
+linker_script_template = lib/ZuluSCSI_platform_RP2MCU/rp2040-template.ld
+ldscript_bootloader = lib/ZuluSCSI_platform_RP2MCU/rp2040_btldr.ld
 debug_build_flags =
-    -O2 -ggdb -g3
+    ${env:ZuluSCSI_RP2MCU.debug_build_flags}
     -DLOGBUFSIZE=4096
     -DPREFETCH_BUFFER_SIZE=0
     -DSCSI2SD_BUFFER_SIZE=57344
 ; This controls the depth NETWORK_PACKET_MAX_SIZE (1520 bytes)
 ; For example a queue size of 10 would be 10 x 1520 = 30400 bytes
-    -DNETWORK_PACKET_QUEUE_SIZE=10
+    -DNETWORK_PACKET_QUEUE_SIZE=8
 ; This flag enables verbose logging of TCP/IP traffic and other information
 ; it also takes up a bit of SRAM so it should be disabled with production code
     -DNETWORK_DEBUG_LOGGING
-    -DPLATFORM_MASS_STORAGE
-    -DFILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC
-
-lib_deps =
-    SdFat=https://github.com/rabbitholecomputing/SdFat#2.2.3-gpt
-    minIni
-    ZuluSCSI_platform_RP2040
-    SCSI2SD
-    CUEParser=https://github.com/rabbitholecomputing/CUEParser
 build_flags =
-    -O2 -Isrc
-    -Wall -Wno-sign-compare -Wno-ignored-qualifiers
-    -DSPI_DRIVER_SELECT=3
-    -DSD_CHIP_SELECT_MODE=2
-    -DENABLE_DEDICATED_SPI=1
-    -DHAS_SDIO_CLASS
-    -DUSE_ARDUINO=1
+    ${env:ZuluSCSI_RP2MCU.build_flags}
     -DZULUSCSI_PICO
+    -DZULUSCSI_MCU_RP20XX
     -DZULUSCSI_NETWORK
     -DZULUSCSI_DAYNAPORT
     -DROMDRIVE_OFFSET=${env:ZuluSCSI_Pico_DaynaPORT.program_flash_allocation}
@@ -226,39 +209,42 @@ build_flags =
 ; This controls the depth of NETWORK_PACKET_MAX_SIZE (1520 bytes)
 ; For example a queue size of 10 would be 10 x 1520 = 15200 bytes
     -DNETWORK_PACKET_QUEUE_SIZE=14
-    
 ; This flag enables verbose logging of TCP/IP traffic and other information
 ; it also takes up a bit of SRAM so it should be disabled with production code
-    ;-DNETWORK_DEBUG_LOGGING
+    ; -DNETWORK_DEBUG_LOGGING
 
-; build flags mirroring the framework-arduinopico#v3.6.0-DaynaPORT static library build
-    -DPICO_CYW43_ARCH_POLL=1
-	-DCYW43_LWIP=0
-	-DCYW43_USE_OTP_MAC=0
- ;   -DPIO_FRAMEWORK_ARDUINO_NO_USB
-    -DPLATFORM_MASS_STORAGE
-    -DFILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC
 
 ; Variant of RP2040 platform, based on Raspberry Pico board and a carrier PCB
 ; Differs in pinout from ZuluSCSI_RP2040 platform, but shares most of the code.
 [env:ZuluSCSI_BS2]
-extends = env:ZuluSCSI_RP2040
+extends = env:ZuluSCSI_RP2MCU
+board = rpipico
+program_flash_allocation = 360448
+linker_script_template = lib/ZuluSCSI_platform_RP2MCU/rp2040-template.ld
+ldscript_bootloader = lib/ZuluSCSI_platform_RP2MCU/rp2040_btldr.ld
 build_flags =
-    -O2 -Isrc -ggdb -g3
-    -Wall -Wno-sign-compare -Wno-ignored-qualifiers
-    -DSPI_DRIVER_SELECT=3
-    -DSD_CHIP_SELECT_MODE=2
-    -DENABLE_DEDICATED_SPI=1
-    -DHAS_SDIO_CLASS
-    -DUSE_ARDUINO=1
+    ${env:ZuluSCSI_RP2MCU.build_flags}
     -DZULUSCSI_BS2
-    -DROMDRIVE_OFFSET=${env:ZuluSCSI_RP2040.program_flash_allocation}
-; build flags mirroring the framework-arduinopico#v3.6.0-DaynaPORT static library build
-    -DPICO_CYW43_ARCH_POLL=1
-	-DCYW43_LWIP=0
-	-DCYW43_USE_OTP_MAC=0
-    -DPLATFORM_MASS_STORAGE
-    -DFILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC
+    -DZULUSCSI_MCU_RP20XX
+    -DROMDRIVE_OFFSET=${env:ZuluSCSI_BS2.program_flash_allocation}
+
+; ZuluSCSI Pico2 hardware platform, based on the Raspberry Pi foundation RP2350A microcontroller
+[env:ZuluSCSI_Pico_2]
+extends = env:ZuluSCSI_RP2MCU
+board = rpipico2
+; How much flash in bytes the bootloader and main app will be allocated
+; It is used as the starting point for a ROM image saved in flash
+; Changing this will cause issues with boards that already have a ROM drive in flash
+program_flash_allocation = 360448
+linker_script_template = lib/ZuluSCSI_platform_RP2MCU/rp23xx-template.ld
+ldscript_bootloader = lib/ZuluSCSI_platform_RP2MCU/rp23xx_btldr.ld
+debug_build_flags =
+    ${env:ZuluSCSI_RP2MCU.debug_build_flags}
+build_flags =
+    ${env:ZuluSCSI_RP2MCU.build_flags}
+    -DZULUSCSI_PICO_2
+    -DZULUSCSI_MCU_RP23XX
+    -DROMDRIVE_OFFSET=${env:ZuluSCSI_Pico_2.program_flash_allocation}
 
 ; ZuluSCSI VF4 hardware platform with GD32F450ZET6 CPU.
 [env:ZULUSCSIv1_4]
@@ -292,7 +278,7 @@ build_flags =
      -D__SYSTEM_CLOCK_200M_PLL_IRC16M=200000000
      -DSPI_DRIVER_SELECT=3
      -DSD_CHIP_SELECT_MODE=2
-     -DENABLE_DEDICATED_SPI=1
+     -DENABLE_DEDICATED_SPI=1   
      -DHAS_SDIO_CLASS
      -DPIO_USBFS_DEVICE_CDC
      -DZULUSCSI_V1_4
@@ -300,3 +286,20 @@ build_flags =
      -DPLATFORM_MASS_STORAGE
      -DSDFAT_NOARDUINO
      -DFILE_COPY_CONSTRUCTOR_SELECT=FILE_COPY_CONSTRUCTOR_PUBLIC
+
+;========================================
+; ZuluSCSI RP2350 hardware platform, based on the Raspberry Pi foundation RP2350 microcontroller
+[env:ZuluSCSI_RP2350A]
+extends = env:ZuluSCSI_RP2MCU
+board = zuluscsi_RP2350A
+; How much flash in bytes the bootloader and main app will be allocated
+; It is used as the starting point for a ROM image saved in flash
+; Changing this will cause issues with boards that already have a ROM drive in flash
+program_flash_allocation = 360448
+linker_script_template = lib/ZuluSCSI_platform_RP2MCU/rp23xx-template.ld
+ldscript_bootloader = lib/ZuluSCSI_platform_RP2MCU/rp23xx_btldr.ld
+build_flags =
+    ${env:ZuluSCSI_RP2MCU.build_flags}
+    -DZULUSCSI_RP2350A
+    -DZULUSCSI_MCU_RP23XX
+    -DROMDRIVE_OFFSET=${env:ZuluSCSI_RP2350A.program_flash_allocation}

+ 3 - 2
src/ZuluSCSI_config.h

@@ -28,8 +28,9 @@
 #include <ZuluSCSI_platform.h>
 
 // Use variables for version number
-#define FW_VER_NUM      "24.11.1"
-#define FW_VER_SUFFIX   "release"
+#define FW_VER_NUM      "24.11.01"
+#define FW_VER_SUFFIX   "devel"
+
 #define ZULU_FW_VERSION FW_VER_NUM "-" FW_VER_SUFFIX
 #define INQUIRY_NAME  PLATFORM_NAME " v" ZULU_FW_VERSION
 #define TOOLBOX_API 0

+ 0 - 3
src/ZuluSCSI_msc.h

@@ -25,9 +25,6 @@
 // include platform-specific defines
 #include "ZuluSCSI_platform_msc.h"
 
-// wait up to this long during init sequence for USB enumeration to enter card reader
-#define CR_ENUM_TIMEOUT 1000
-
 enum  MSC_LEDState { LED_SOLIDON = 0, LED_BLINK_FAST, LED_BLINK_SLOW };
 extern volatile enum MSC_LEDState MSC_LEDMode;
 

+ 2 - 0
src/ZuluSCSI_settings.cpp

@@ -297,6 +297,7 @@ scsi_system_settings_t *ZuluSCSISettings::initSystem(const char *presetName)
     cfgSys.useFATAllocSize = false;
     cfgSys.enableCDAudio = false;
     cfgSys.enableUSBMassStorage = false;
+    cfgSys.usbMassStorageWaitPeriod = 1000;
     
     // setting set for all or specific devices
     cfgDev.deviceType = S2S_CFG_NOT_SET;
@@ -392,6 +393,7 @@ scsi_system_settings_t *ZuluSCSISettings::initSystem(const char *presetName)
     cfgSys.enableCDAudio = ini_getbool("SCSI", "EnableCDAudio", cfgSys.enableCDAudio, CONFIGFILE);
 
     cfgSys.enableUSBMassStorage = ini_getbool("SCSI", "EnableUSBMassStorage", cfgSys.enableUSBMassStorage, CONFIGFILE);
+    cfgSys.usbMassStorageWaitPeriod = ini_getl("SCSI", "USBMassStorageWaitPeriod", cfgSys.usbMassStorageWaitPeriod, CONFIGFILE);
     
     return &cfgSys;
 }

+ 1 - 0
src/ZuluSCSI_settings.h

@@ -66,6 +66,7 @@ typedef struct __attribute__((__packed__)) scsi_system_settings_t
     bool useFATAllocSize;
     bool enableCDAudio;
     bool enableUSBMassStorage;
+    uint16_t usbMassStorageWaitPeriod;
 } scsi_system_settings_t;
 
 // This struct should only have new setting added to the end

+ 2 - 2
lib/ZuluSCSI_platform_RP2040/process-linker-script.py → src/process-linker-script.py

@@ -19,8 +19,8 @@
 from string import Template 
 Import ("env")
 
-template_file = 'lib/ZuluSCSI_platform_RP2040/rp2040-template.ld'
-linker_file = env.subst('$BUILD_DIR') + '/rp2040.ld'
+template_file =  env.GetProjectOption('linker_script_template')
+linker_file = env.subst('$BUILD_DIR') + '/rp_linker.ld'
 
 def process_template(source, target, env):
     values = {

+ 5 - 0
zuluscsi.ini

@@ -66,6 +66,11 @@
 #DisableMacSanityCheck = 0 # Disable sanity warnings for Mac disk drives. Default is 0 - enable checks
 #BlockSize = 0 # Set the drive's blocksize, defaults to 2048 for CDs and 512 for all other drives
 
+# 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
+
 # SCSI DaynaPORT settings
 #WiFiSSID = "Wifi SSID string"
 #WiFiPassword = "WiFi Password"