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

Optimize handling of reset interrupt.

Glenn Anderson 3 роки тому
батько
коміт
bc65933625
1 змінених файлів з 80 додано та 37 видалено
  1. 80 37
      src/BlueSCSI.cpp

+ 80 - 37
src/BlueSCSI.cpp

@@ -37,6 +37,7 @@
 
 #include <Arduino.h> // For Platform.IO
 #include <SdFat.h>
+#include <setjmp.h>
 
 #ifdef USE_STM32_DMA
 #warning "warning USE_STM32_DMA"
@@ -187,6 +188,8 @@ HDDIMG  img[NUM_SCSIID][NUM_SCSILUN]; // Maximum number
 uint8_t       m_senseKey = 0;         // Sense key
 unsigned      m_addition_sense = 0;   // Additional sense information
 volatile bool m_isBusReset = false;   // Bus reset
+volatile bool m_resetJmp = false;     // Call longjmp on reset
+jmp_buf       m_resetJmpBuf;
 
 byte          scsi_id_mask;           // Mask list of responding SCSI IDs
 byte          m_id;                   // Currently responding SCSI-ID
@@ -416,7 +419,7 @@ void setup()
   SCSI_TARGET_INACTIVE()
 
   //Occurs when the RST pin state changes from HIGH to LOW
-  //attachInterrupt(PIN_MAP[RST].gpio_bit, onBusReset, FALLING);
+  //attachInterrupt(RST, onBusReset, FALLING);
 
   LED_ON();
 
@@ -519,7 +522,7 @@ void setup()
   finalizeFileLog();
   LED_OFF();
   //Occurs when the RST pin state changes from HIGH to LOW
-  attachInterrupt(PIN_MAP[RST].gpio_bit, onBusReset, FALLING);
+  attachInterrupt(RST, onBusReset, FALLING);
 }
 
 /*
@@ -612,6 +615,36 @@ void noSDCardFound(void)
   }
 }
 
+/*
+ * Return from exception and call longjmp
+ */
+void __attribute__ ((noinline)) longjmpFromInterrupt(jmp_buf jmpb, int retval) __attribute__ ((noreturn));
+void longjmpFromInterrupt(jmp_buf jmpb, int retval) {
+  // Address of longjmp with the thumb bit cleared
+  const uint32_t longjmpaddr = ((uint32_t)longjmp) & 0xfffffffe;
+  const uint32_t zero = 0;
+  // Default PSR value, function calls don't require any particular value
+  const uint32_t PSR = 0x01000000;
+  // For documentation on what this is doing, see:
+  // https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-processor/exception-model/exception-entry-and-return
+  // Stack frame needs to have R0-R3, R12, LR, PC, PSR (from bottom to top)
+  // This is being set up to have R0 and R1 contain the parameters passed to longjmp, and PC is the address of the longjmp function.
+  // This is using existing stack space, rather than allocating more, as longjmp is just going to unroll the stack even further.
+  // 0xfffffff9 is the EXC_RETURN value to return to thread mode.
+  asm (
+      "str %0, [sp];\
+      str %1, [sp, #4];\
+      str %2, [sp, #8];\
+      str %2, [sp, #12];\
+      str %2, [sp, #16];\
+      str %2, [sp, #20];\
+      str %3, [sp, #24];\
+      str %4, [sp, #28];\
+      ldr lr, =0xfffffff9"
+       :: "r"(jmpb),"r"(retval),"r"(zero), "r"(longjmpaddr), "r"(PSR)
+  );
+}
+
 /*
  * Bus reset interrupt.
  */
@@ -635,10 +668,28 @@ void onBusReset(void)
       SCSI_DB_INPUT()
 
       LOGN("BusReset!");
-      m_isBusReset = true;
+      if (m_resetJmp) {
+        m_resetJmp = false;
+        // Jumping out of the interrupt handler, so need to clear the interupt source.
+        uint8 exti = PIN_MAP[RST].gpio_bit;
+        EXTI_BASE->PR = (1U << exti);
+        longjmpFromInterrupt(m_resetJmpBuf, 1);
+      } else {
+        m_isBusReset = true;
+      }
     }
   }
 }
+    
+/*
+ * Enable the reset longjmp, and check if reset fired while it was disabled.
+ */
+void enableResetJmp(void) {
+  m_resetJmp = true;
+  if (m_isBusReset) {
+    longjmp(m_resetJmpBuf, 1);
+  }
+}
 
 /*
  * Read by handshake.
@@ -647,10 +698,10 @@ inline byte readHandshake(void)
 {
   SCSI_OUT(vREQ,active)
   //SCSI_DB_INPUT()
-  while( ! SCSI_IN(vACK)) { if(m_isBusReset) return 0; }
+  while( ! SCSI_IN(vACK));
   byte r = readIO();
   SCSI_OUT(vREQ,inactive)
-  while( SCSI_IN(vACK)) { if(m_isBusReset) return 0; }
+  while( SCSI_IN(vACK));
   return r;  
 }
 
@@ -667,12 +718,12 @@ inline void writeHandshake(byte d)
   SCSI_OUT(vREQ,inactive) // setup wait (30ns)
   SCSI_OUT(vREQ,active)   // (30ns)
   //while(!SCSI_IN(vACK)) { if(m_isBusReset){ SCSI_DB_INPUT() return; }}
-  while(!m_isBusReset && !SCSI_IN(vACK));
+  while(!SCSI_IN(vACK));
   // ACK.Fall to REQ.Raise delay 500ns(typ.) (DTC-510B)
   GPIOB->regs->BSRR = DBP(0xff);  // DB=0xFF , SCSI_OUT(vREQ,inactive)
   // REQ.Raise to DB hold time 0ns
   SCSI_DB_INPUT() // (150ns)
-  while( SCSI_IN(vACK)) { if(m_isBusReset) return; }
+  while( SCSI_IN(vACK));
 }
 
 /*
@@ -686,9 +737,6 @@ void writeDataPhase(int len, const byte* p)
   SCSI_OUT(vCD ,inactive) //  gpio_write(CD, low);
   SCSI_OUT(vIO ,  active) //  gpio_write(IO, high);
   for (int i = 0; i < len; i++) {
-    if(m_isBusReset) {
-      return;
-    }
     writeHandshake(p[i]);
   }
 }
@@ -709,7 +757,9 @@ void writeDataPhaseSD(uint32_t adds, uint32_t len)
 
   for(uint32_t i = 0; i < len; i++) {
       // Asynchronous reads will make it faster ...
+    m_resetJmp = false;
     m_img->m_file.read(m_buf, m_img->m_blocksize);
+    enableResetJmp();
 
 #if READ_SPEED_OPTIMIZE
 
@@ -718,8 +768,8 @@ void writeDataPhaseSD(uint32_t adds, uint32_t len)
 #define FETCH_SRC()   (src_byte = *srcptr++)
 #define FETCH_BSRR_DB() (bsrr_val = bsrr_tbl[src_byte])
 #define REQ_OFF_DB_SET(BSRR_VAL) *db_dst = BSRR_VAL
-#define WAIT_ACK_ACTIVE()   while(!m_isBusReset && !SCSI_IN(vACK))
-#define WAIT_ACK_INACTIVE() do{ if(m_isBusReset) return; }while(SCSI_IN(vACK)) 
+#define WAIT_ACK_ACTIVE()   while(!SCSI_IN(vACK))
+#define WAIT_ACK_INACTIVE() while(SCSI_IN(vACK)) 
 
     SCSI_DB_OUTPUT()
     register byte *srcptr= m_buf;                 // Source buffer
@@ -799,9 +849,6 @@ void writeDataPhaseSD(uint32_t adds, uint32_t len)
     SCSI_DB_INPUT()
 #else
     for(int j = 0; j < m_img->m_blocksize; j++) {
-      if(m_isBusReset) {
-        return;
-      }
       writeHandshake(m_buf[j]);
     }
 #endif
@@ -835,6 +882,7 @@ void readDataPhaseSD(uint32_t adds, uint32_t len)
   SCSI_OUT(vCD ,inactive) //  gpio_write(CD, low);
   SCSI_OUT(vIO ,inactive) //  gpio_write(IO, low);
   for(uint32_t i = 0; i < len; i++) {
+    m_resetJmp = true;
 #if WRITE_SPEED_OPTIMIZE
   register byte *dstptr= m_buf;
 	register byte *endptr= m_buf + m_img->m_blocksize;
@@ -848,21 +896,21 @@ void readDataPhaseSD(uint32_t adds, uint32_t len)
       dstptr[5] = readHandshake();
       dstptr[6] = readHandshake();
       dstptr[7] = readHandshake();
-      if(m_isBusReset) {
-        return;
-      }
     }
 #else
     for(int j = 0; j <  m_img->m_blocksize; j++) {
-      if(m_isBusReset) {
-        return;
-      }
       m_buf[j] = readHandshake();
     }
 #endif
+    m_resetJmp = false;
     m_img->m_file.write(m_buf, m_img->m_blocksize);
+    // If a reset happened while writing, break and let the flush happen before it is handled.
+    if (m_isBusReset) {
+      break;
+    }
   }
   m_img->m_file.flush();
+  enableResetJmp();
 }
 
 /*
@@ -1235,6 +1283,11 @@ void loop()
   }
   LOGN("Selection");
   m_isBusReset = false;
+  if (setjmp(m_resetJmpBuf) == 1) {
+    LOGN("Reset, going to BusFree");
+    goto BusFree;
+  }
+  enableResetJmp();
   // Set BSY to-when selected
   SCSI_BSY_ACTIVE();     // Turn only BSY output ON, ACTIVE
 
@@ -1243,9 +1296,6 @@ void loop()
 
   // Wait until SEL becomes inactive
   while(isHigh(gpio_read(SEL)) && isLow(gpio_read(BSY))) {
-    if(m_isBusReset) {
-      goto BusFree;
-    }
   }
   SCSI_TARGET_ACTIVE()  // (BSY), REQ, MSG, CD, IO output turned on
   //  
@@ -1308,22 +1358,21 @@ void loop()
   
   int len;
   byte cmd[12];
-  cmd[0] = readHandshake(); if(m_isBusReset) goto BusFree;
+  cmd[0] = readHandshake();
   LOGHEX(cmd[0]);
   // Command length selection, reception
   static const int cmd_class_len[8]={6,10,10,6,6,12,6,6};
   len = cmd_class_len[cmd[0] >> 5];
-  cmd[1] = readHandshake(); LOG(":");LOGHEX(cmd[1]); if(m_isBusReset) goto BusFree;
-  cmd[2] = readHandshake(); LOG(":");LOGHEX(cmd[2]); if(m_isBusReset) goto BusFree;
-  cmd[3] = readHandshake(); LOG(":");LOGHEX(cmd[3]); if(m_isBusReset) goto BusFree;
-  cmd[4] = readHandshake(); LOG(":");LOGHEX(cmd[4]); if(m_isBusReset) goto BusFree;
-  cmd[5] = readHandshake(); LOG(":");LOGHEX(cmd[5]); if(m_isBusReset) goto BusFree;
+  cmd[1] = readHandshake(); LOG(":");LOGHEX(cmd[1]);
+  cmd[2] = readHandshake(); LOG(":");LOGHEX(cmd[2]);
+  cmd[3] = readHandshake(); LOG(":");LOGHEX(cmd[3]);
+  cmd[4] = readHandshake(); LOG(":");LOGHEX(cmd[4]);
+  cmd[5] = readHandshake(); LOG(":");LOGHEX(cmd[5]);
   // Receive the remaining commands
   for(int i = 6; i < len; i++ ) {
     cmd[i] = readHandshake();
     LOG(":");
     LOGHEX(cmd[i]);
-    if(m_isBusReset) goto BusFree;
   }
   // LUN confirmation
   m_sts = cmd[1]&0xe0;      // Preset LUN in status byte
@@ -1422,18 +1471,12 @@ void loop()
     m_addition_sense = 0x2000; // Invalid Command Operation Code
     break;
   }
-  if(m_isBusReset) {
-     goto BusFree;
-  }
 
   LOGN("Sts");
   SCSI_OUT(vMSG,inactive) // gpio_write(MSG, low);
   SCSI_OUT(vCD ,  active) // gpio_write(CD, high);
   SCSI_OUT(vIO ,  active) // gpio_write(IO, high);
   writeHandshake(m_sts);
-  if(m_isBusReset) {
-     goto BusFree;
-  }
 
   LOGN("MsgIn");
   SCSI_OUT(vMSG,  active) // gpio_write(MSG, high);