Переглянути джерело

Merge pull request #103 from ZuluSCSI/rp2040_rom_drive

RP2040: Initial support for ROM drive in microcontroller flash space
Alex Perez 3 роки тому
батько
коміт
9eadee1d8a

+ 61 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.cpp

@@ -457,6 +457,67 @@ const void * btldr_vectors[2] = {&__StackTop, (void*)&btldr_reset_handler};
 
 #endif
 
+/************************************/
+/* ROM drive in extra flash space   */
+/************************************/
+
+#ifdef PLATFORM_HAS_ROM_DRIVE
+
+// Reserve up to 512 kB for firmware.
+#define ROMDRIVE_OFFSET (512 * 1024)
+
+// TODO: Actually read the flash chip size instead of assuming 2 MB.
+static uint32_t g_romdrive_max_size = 2048 * 1024 - ROMDRIVE_OFFSET;
+
+uint32_t azplatform_get_romdrive_maxsize()
+{
+    return g_romdrive_max_size;
+}
+
+bool azplatform_read_romdrive(uint8_t *dest, uint32_t start, uint32_t count)
+{
+    xip_ctrl_hw->stream_ctr = 0;
+
+    while (!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY))
+    {
+        (void) xip_ctrl_hw->stream_fifo;
+    }
+
+    xip_ctrl_hw->stream_addr = start + ROMDRIVE_OFFSET;
+    xip_ctrl_hw->stream_ctr = count / 4;
+
+    // Transfer happens in multiples of 4 bytes
+    assert(start < g_romdrive_max_size);
+    assert((count & 3) == 0);
+    assert((((uint32_t)dest) & 3) == 0);
+
+    uint32_t *dest32 = (uint32_t*)dest;
+    uint32_t words_remain = count / 4;
+    while (words_remain > 0)
+    {
+        if (!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY))
+        {
+            *dest32++ = xip_ctrl_hw->stream_fifo;
+            words_remain--;
+        }
+    }
+
+    return true;
+}
+
+bool azplatform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count)
+{
+    assert(start < g_romdrive_max_size);
+    assert((count % AZPLATFORM_ROMDRIVE_PAGE_SIZE) == 0);
+    __disable_irq();
+    flash_range_erase(start + ROMDRIVE_OFFSET, count);
+    flash_range_program(start + ROMDRIVE_OFFSET, data, count);
+    __enable_irq();
+    return true;
+}
+
+#endif
+
 /**********************************************/
 /* Mapping from data bytes to GPIO BOP values */
 /**********************************************/

+ 14 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.h

@@ -74,6 +74,20 @@ bool azplatform_rewrite_flash_page(uint32_t offset, uint8_t buffer[AZPLATFORM_FL
 void azplatform_boot_to_main_firmware();
 #endif
 
+// ROM drive in the unused external flash area
+#ifndef RP2040_DISABLE_ROMDRIVE
+#define PLATFORM_HAS_ROM_DRIVE 1
+// Check maximum available space for ROM drive in bytes
+uint32_t azplatform_get_romdrive_maxsize();
+
+// Read ROM drive area
+bool azplatform_read_romdrive(uint8_t *dest, uint32_t start, uint32_t count);
+
+// Reprogram ROM drive area
+#define AZPLATFORM_ROMDRIVE_PAGE_SIZE 4096
+bool azplatform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count);
+#endif
+
 // Below are GPIO access definitions that are used from scsiPhy.cpp.
 
 // Write a single SCSI pin.

+ 1 - 1
lib/ZuluSCSI_platform_RP2040/rp2040.ld

@@ -1,6 +1,6 @@
 MEMORY
 {
-    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
+    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 512k
     RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 240k  /* Leave space for pico-debug */
     SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
     SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k

+ 34 - 10
src/ZuluSCSI.cpp

@@ -248,6 +248,18 @@ bool findHDDImages()
           continue;
         }
 
+        // Check if the image should be loaded to microcontroller flash ROM drive
+        bool is_romdrive = false;
+        if (extension && strcasecmp(extension, ".rom") == 0)
+        {
+          is_romdrive = true;
+        }
+        else if (extension && strcasecmp(extension, ".rom_loaded") == 0)
+        {
+          // Already loaded ROM drive, ignore the image
+          continue;
+        }
+
         // Defaults for Hard Disks
         int id  = 1; // 0 and 3 are common in Macs for physical HD and CD, so avoid them.
         int lun = 0;
@@ -309,19 +321,29 @@ bool findHDDImages()
           continue;
         }
 
+        // Type mapping based on filename.
+        // If type is FIXED, the type can still be overridden in .ini file.
+        S2S_CFG_TYPE type = S2S_CFG_FIXED;
+        if (is_cd) type = S2S_CFG_OPTICAL;
+        if (is_fd) type = S2S_CFG_FLOPPY_14MB;
+        if (is_mo) type = S2S_CFG_MO;
+        if (is_re) type = S2S_CFG_REMOVEABLE;
+        if (is_tp) type = S2S_CFG_SEQUENTIAL;
+
         // Open the image file
-        if(id < NUM_SCSIID && lun < NUM_SCSILUN) {
+        if (id < NUM_SCSIID && is_romdrive)
+        {
+          azlog("-- Loading ROM drive from ", fullname, " for id:", id);
+          imageReady = scsiDiskProgramRomDrive(fullname, id, blk, type);
+          
+          if (imageReady)
+          {
+            foundImage = true;
+          }
+        }
+        else if(id < NUM_SCSIID && lun < NUM_SCSILUN) {
           azlog("-- Opening ", fullname, " for id:", id, " lun:", lun);
 
-          // Type mapping based on filename.
-          // If type is FIXED, the type can still be overridden in .ini file.
-          S2S_CFG_TYPE type = S2S_CFG_FIXED;
-          if (is_cd) type = S2S_CFG_OPTICAL;
-          if (is_fd) type = S2S_CFG_FLOPPY_14MB;
-          if (is_mo) type = S2S_CFG_MO;
-          if (is_re) type = S2S_CFG_REMOVEABLE;
-          if (is_tp) type = S2S_CFG_SEQUENTIAL;
-
           imageReady = scsiDiskOpenHDDImage(id, fullname, id, lun, blk, type);
           if(imageReady)
           {
@@ -343,6 +365,8 @@ bool findHDDImages()
   }
   root.close();
 
+  scsiDiskActivateRomDrive();
+
   // Print SCSI drive map
   for (int i = 0; i < NUM_SCSIID; i++)
   {

+ 211 - 4
src/ZuluSCSI_disk.cpp

@@ -43,9 +43,148 @@ extern "C" {
 #define PLATFORM_OPTIMAL_LAST_SD_WRITE_SIZE 512
 #endif
 
+#ifndef PLATFORM_HAS_ROM_DRIVE
+// Dummy defines for platforms without ROM drive support
+#define AZPLATFORM_ROMDRIVE_PAGE_SIZE 1024
+uint32_t azplatform_get_romdrive_maxsize() { return 0; }
+bool azplatform_read_romdrive(uint8_t *dest, uint32_t start, uint32_t count) { return false; }
+bool azplatform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count) { return false; }
+#endif
+
 // SD card sector size is always 512 bytes
 #define SD_SECTOR_SIZE 512
 
+/************************************************/
+/* ROM drive support (in microcontroller flash) */
+/************************************************/
+
+struct romdrive_hdr_t {
+    char magic[8]; // "ROMDRIVE"
+    int scsi_id;
+    uint32_t imagesize;
+    uint32_t blocksize;
+    S2S_CFG_TYPE drivetype;
+    uint32_t reserved[32];
+};
+
+// Check if the romdrive is present
+static bool check_romdrive(romdrive_hdr_t *hdr)
+{
+    if (!azplatform_read_romdrive((uint8_t*)hdr, 0, sizeof(romdrive_hdr_t)))
+    {
+        return false;
+    }
+
+    if (memcmp(hdr->magic, "ROMDRIVE", 8) != 0)
+    {
+        return false;
+    }
+
+    if (hdr->imagesize <= 0 || hdr->scsi_id < 0 || hdr->scsi_id > 8)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+// Load an image file to romdrive
+bool scsiDiskProgramRomDrive(const char *filename, int scsi_id, int blocksize, S2S_CFG_TYPE type)
+{
+    FsFile file = SD.open(filename, O_RDONLY);
+    if (!file.isOpen())
+    {
+        azlog("---- Failed to open: ", filename);
+        return false;
+    }
+
+    uint64_t filesize = file.size();
+    uint32_t maxsize = azplatform_get_romdrive_maxsize() - AZPLATFORM_ROMDRIVE_PAGE_SIZE;
+
+    azlog("---- SCSI ID: ", scsi_id, " blocksize ", blocksize, " type ", (int)type);
+    azlog("---- ROM drive maximum size is ", (int)azplatform_get_romdrive_maxsize(),
+          " bytes, image file is ", (int)file.size(), " bytes");
+    
+    if (filesize > maxsize)
+    {
+        azlog("---- Image size exceeds ROM space, not loading");
+        file.close();
+        return false;
+    }
+
+    romdrive_hdr_t hdr = {};
+    memcpy(hdr.magic, "ROMDRIVE", 8);
+    hdr.scsi_id = scsi_id;
+    hdr.imagesize = filesize;
+    hdr.blocksize = blocksize;
+    hdr.drivetype = type;
+
+    // Program the drive metadata header
+    if (!azplatform_write_romdrive((const uint8_t*)&hdr, 0, AZPLATFORM_ROMDRIVE_PAGE_SIZE))
+    {
+        azlog("---- Failed to program ROM drive header");
+        file.close();
+        return false;
+    }
+    
+    // Program the drive contents
+    uint32_t pages = (filesize + AZPLATFORM_ROMDRIVE_PAGE_SIZE - 1) / AZPLATFORM_ROMDRIVE_PAGE_SIZE;
+    for (uint32_t i = 0; i < pages; i++)
+    {
+        if (!file.read(scsiDev.data, AZPLATFORM_ROMDRIVE_PAGE_SIZE) ||
+            !azplatform_write_romdrive(scsiDev.data, (i + 1) * AZPLATFORM_ROMDRIVE_PAGE_SIZE, AZPLATFORM_ROMDRIVE_PAGE_SIZE))
+        {
+            azlog("---- Failed to program ROM drive page ", (int)i);
+            file.close();
+            return false;
+        }
+    }
+
+    file.close();
+
+    char newname[MAX_FILE_PATH * 2] = "";
+    strlcat(newname, filename, sizeof(newname));
+    strlcat(newname, "_loaded", sizeof(newname));
+    SD.rename(filename, newname);
+    azlog("---- ROM drive programming successful, image file renamed to ", newname);
+
+    return true;
+}
+
+// Check if rom drive exists and activate it
+bool scsiDiskActivateRomDrive()
+{
+    uint32_t maxsize = azplatform_get_romdrive_maxsize() - AZPLATFORM_ROMDRIVE_PAGE_SIZE;
+    azlog("-- Platform supports ROM drive up to ", (int)(maxsize / 1024), " kB");
+
+    romdrive_hdr_t hdr = {};
+    if (!check_romdrive(&hdr))
+    {
+        azlog("---- ROM drive image not detected");
+        return false;
+    }
+
+    if (s2s_getConfigById(hdr.scsi_id))
+    {
+        azlog("---- ROM drive SCSI id ", (int)hdr.scsi_id, " is already in use, not enabling");
+        return false;
+    }
+
+    azlog("---- Activating ROM drive, SCSI id ", (int)hdr.scsi_id, " size ", (int)(hdr.imagesize / 1024), " kB");
+    bool status = scsiDiskOpenHDDImage(hdr.scsi_id, "ROM:", hdr.scsi_id, 0, hdr.blocksize, hdr.drivetype);
+
+    if (!status)
+    {
+        azlog("---- ROM drive activation failed");
+        return false;
+    }
+    else
+    {
+        return true;
+    }
+}
+
+
 /***********************/
 /* Backing image files */
 /***********************/
@@ -58,12 +197,16 @@ SdDevice sdDev = {2, 256 * 1024 * 1024 * 2}; /* For SCSI2SD */
 //
 // Raw access is activated by using filename like "RAW:0:12345"
 // where the numbers are the first and last sector.
+//
+// If the platform supports a ROM drive, it is activated by using
+// filename "ROM:".
 class ImageBackingStore
 {
 public:
     ImageBackingStore()
     {
         m_israw = false;
+        m_isrom = false;
         m_blockdev = nullptr;
         m_bgnsector = m_endsector = m_cursector = 0;
     }
@@ -98,6 +241,17 @@ public:
                 m_endsector = sectorCount - 1;
             }
         }
+        else if (strncasecmp(filename, "ROM:", 4) == 0)
+        {
+            if (!check_romdrive(&m_romhdr))
+            {
+                m_romhdr.imagesize = 0;
+            }
+            else
+            {
+                m_isrom = true;
+            }
+        }
         else
         {
             m_fsfile = SD.open(filename, O_RDWR);
@@ -133,7 +287,16 @@ public:
         }
     }
 
-    bool isOpen() { return m_israw ? !!m_blockdev : m_fsfile.isOpen(); }
+    bool isOpen()
+    {
+        if (m_israw)
+            return (m_blockdev != NULL);
+        else if (m_isrom)
+            return (m_romhdr.imagesize > 0);
+        else
+            return m_fsfile.isOpen();
+    }
+
     bool close()
     {
         if (m_israw)
@@ -141,6 +304,11 @@ public:
             m_blockdev = nullptr;
             return true;
         }
+        else if (m_isrom)
+        {
+            m_romhdr.imagesize = 0;
+            return true;
+        }
         else
         {
             return m_fsfile.close();
@@ -153,6 +321,10 @@ public:
         {
             return (uint64_t)(m_endsector - m_bgnsector + 1) * SD_SECTOR_SIZE;
         }
+        else if (m_isrom)
+        {
+            return m_romhdr.imagesize;
+        }
         else
         {
             return m_fsfile.size();
@@ -167,6 +339,12 @@ public:
             *endSector = m_endsector;
             return true;
         }
+        else if (m_isrom)
+        {
+            *bgnSector = 0;
+            *endSector = 0;
+            return true;
+        }
         else
         {
             return m_fsfile.contiguousRange(bgnSector, endSector);
@@ -175,13 +353,20 @@ public:
 
     bool seek(uint64_t pos)
     {
-        if (m_israw && m_blockdev)
+        if (m_israw)
         {
             uint32_t sectornum = pos / SD_SECTOR_SIZE;
             assert((uint64_t)sectornum * SD_SECTOR_SIZE == pos);
             m_cursector = m_bgnsector + sectornum;
             return (m_cursector <= m_endsector);
         }
+        else if (m_isrom)
+        {
+            uint32_t sectornum = pos / SD_SECTOR_SIZE;
+            assert((uint64_t)sectornum * SD_SECTOR_SIZE == pos);
+            m_cursector = sectornum;
+            return m_cursector * SD_SECTOR_SIZE < m_romhdr.imagesize;
+        }
         else
         {
             return m_fsfile.seek(pos);
@@ -204,6 +389,21 @@ public:
                 return -1;
             }
         }
+        else if (m_isrom)
+        {
+            uint32_t sectorcount = count / SD_SECTOR_SIZE;
+            assert((uint64_t)sectorcount * SD_SECTOR_SIZE == count);
+            uint32_t start = m_cursector * SD_SECTOR_SIZE + AZPLATFORM_ROMDRIVE_PAGE_SIZE;
+            if (azplatform_read_romdrive((uint8_t*)buf, start, count))
+            {
+                m_cursector += sectorcount;
+                return count;
+            }
+            else
+            {
+                return -1;
+            }
+        }
         else
         {
             return m_fsfile.read(buf, count);
@@ -226,6 +426,11 @@ public:
                 return 0;
             }
         }
+        else if (m_isrom)
+        {
+            azlog("ERROR: attempted to write to ROM drive");
+            return 0;
+        }
         else
         {
             return m_fsfile.write(buf, count);
@@ -234,7 +439,7 @@ public:
 
     void flush()
     {
-        if (!m_israw)
+        if (!m_israw && !m_isrom)
         {
             m_fsfile.flush();
         }
@@ -242,6 +447,8 @@ public:
 
 private:
     bool m_israw;
+    bool m_isrom;
+    romdrive_hdr_t m_romhdr;
     FsFile m_fsfile;
     SdCard *m_blockdev;
     uint32_t m_bgnsector;
@@ -445,7 +652,7 @@ bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_id, int
         }
 
         uint32_t sector_begin = 0, sector_end = 0;
-        if (img.file.contiguousRange(&sector_begin, &sector_end))
+        if (img.file.contiguousRange(&sector_begin, &sector_end) && sector_end != 0)
         {
             azlog("---- Image file is contiguous, SD card sectors ", (int)sector_begin, " to ", (int)sector_end);
         }

+ 6 - 0
src/ZuluSCSI_disk.h

@@ -17,5 +17,11 @@ void scsiDiskResetImages();
 bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_id, int scsi_lun, int blocksize, S2S_CFG_TYPE type = S2S_CFG_FIXED);
 void scsiDiskLoadConfig(int target_idx);
 
+// Program ROM drive and rename image file
+bool scsiDiskProgramRomDrive(const char *filename, int scsi_id, int blocksize, S2S_CFG_TYPE type);
+
+// Check if there is ROM drive configured in microcontroller flash
+bool scsiDiskActivateRomDrive();
+
 // Returns true if there is at least one image active
 bool scsiDiskCheckAnyImagesConfigured();