Embedded RTOS class project. This project allows a Python/Tk program running on a PC host to monitor/control a test-CPU programmed into an altera development board.

Dependencies:   C12832_lcd USBDevice mbed-rtos mbed mmSPI-2 watchdog

Fork of USB_device_project by Mike Moore

Revision:
13:7e1688393abc
Parent:
9:81726c95be74
--- a/main.cpp	Sun Sep 01 20:36:16 2013 +0000
+++ b/main.cpp	Tue Sep 17 19:07:00 2013 +0000
@@ -1,14 +1,34 @@
 /*----------------------------------------------//------------------------------
     student   : m-moore
     email     : gated.clock@gmail.com
-    class     : usb device drivers
+    class     : embedded RTOS
     directory : USB_device_project
     file      : main.cpp
-    date      : september 3, 2013.
+    date      : september 19, 2013.
 ----copyright-----------------------------------//------------------------------   
     licensed for personal and academic use.
-    commercial use must be approved by the account-holder of
+    commercial use of original code must be approved by the account-holder of
     gated.clock@gmail.com
+----revision------------------------------------//------------------------------
+    this is the embedded RTOS class revision.
+    changes made since the USB device class release:
+    
+    1. a 'read all registers' feature has been added, which speeds up
+       CPU execution, since the UI obtains all of the register states after
+       each CPU clock.  originally, each register was individually read.
+       now, all registers are read at once, if so requested by the python code.
+       
+    2. some 'if' statements were changed to 'switch' statements (neatening).
+    
+    3. added watchdog timers for the three threads.  this via a meta-watchdog thread.
+    
+    4. added #defined-based option to either boot on error detection
+       (such as malloc fail) or use error(); function.
+       
+    5. the LCD is updated only if a display value is changed - reduced power
+       usage a little & reduces potential 'blinkieness'.
+       
+    6. BOOT notification on LCD.
 ----description---------------------------------//------------------------------
     overview:
     program to provide round-trip communication between a python test-control
@@ -23,14 +43,15 @@
     4. main thread provides USBSerial communication to/from host.
     5. SPI processing thread provides SPI communication to/from DUT.
     6. mmSPI library generates non-overlapping SPI and CPU clocks.
-    7. background diagnostic thread provides LCD & LED updates.
+    7. background diagnostic thread provides LCD & error updates.
+    8. meta watchdog thread monitors itself & the other threads.
 
     indicators: (led<3:0> = LED<1:4>)
     1. LCD  provides running counts for SPI and CPU clock cycles.
-    2. led0 indicates main thread processing.
-    3. led1 indicates SPI  thread processing.
-    4. led2 indicates LCD  thread processing.
-    5. led3 indicates reply-to-host underflow (should never turn on).
+    2. led0 indicates main     thread processing.
+    3. led1 indicates SPI      thread processing.
+    4. led2 indicates LCD      thread processing.
+    5. led3 indicates watchdog thread processing.
     
     implementation:
     1. main.processIncomingSerial(): accept incoming serial data from host,
@@ -56,6 +77,8 @@
        connection.
     5. no particular power sequence is needed for this system to work.
     
+    timing critical path:  serial processing.  the python code needs
+    a delay between serial access of 40mS conservatively.
     
     testing:
     the python UI provides the main testing mechanism.
@@ -113,18 +136,22 @@
     #include "C12832_lcd.h"                     // LCD display.
     #include "rtos.h"                           // RTOS.
     #include "mmSPI.h"                          // SPI.
+    #include "watchdog.h"                       // watchdog.
 //---defines------------------------------------//------------------------------
     #define LCD1 lcd.locate(0, 0);              // LCD line 1.
     #define LCD2 lcd.locate(0,11);              // LCD line 2.
     #define LCD3 lcd.locate(0,22);              // LCD line 3.
     #define LCD3 lcd.locate(0,22);              // LCD line 3.  
+    #define WATCHDOG_S     10                   // watchdog timeout, in seconds.
+    #define ERROR_BOOT      1                   // 1 means boot rather than error().
     #define SPI_BYTES       8                   // number of SPI bytes. 
     #define SPI_HZ     100000                   // SPI frequency in Hz.
-    #define SER_BYTES       8                   // serial in/out # of bytes.
+    #define SER_BYTES      18                   // serial in/out # of bytes.
     #define SER_ALIGN       7                   // '$' location in shift-register.
     #define THREAD_0_WAIT   8                   // multitasking wait mS.
     #define THREAD_1_WAIT   2                   // multitasking wait mS. 
     #define THREAD_2_WAIT 128                   // multitasking wait mS. 
+    #define THREAD_3_WAIT 128                   // multitasking wait mS.
     #define HB_MODULO      64                   // heartbeat slowdown factor.
     #define POOL_LEN       16                   // memory pool dimension.
     #define HCMD_SETREG     1                   // host command 'set register'.
@@ -133,6 +160,7 @@
     #define HCMD_GETMM      4                   // host command 'get main-memory.'
     #define HCMD_STEP       5                   // host command 'step-CPU'.
     #define HCMD_SETIR      6                   // host command 'set-IR'.
+    #define HCMD_GETALLREG  7                   // host command 'get-all-registers'.
     #define CPU_REG_0       0                   // CPU register 0.
     #define CPU_REG_1       1                   // CPU register 1.
     #define CPU_REG_2       2                   // CPU register 2.
@@ -140,6 +168,8 @@
     #define CPU_REG_PC      4                   // CPU Program Counter.
     #define CPU_IR_H        5                   // CPU IR high-byte.
     #define CPU_IR_L        6                   // CPU IR low-byte.
+//--externals-----------------------------------//------------------------------    
+    extern "C" void mbed_reset();               // processor reset.
 //--global_definitions--------------------------//------------------------------
     struct tFromHost                            // command from host.
     {
@@ -164,10 +194,21 @@
       char cRegisterValue;                      // data from CPU register.
       char cMMaddress;                          // which MM address read.
       char cMMdataH;                            // MM content high byte.
-      char cMMdataL;                            // MM content low  byte.      
+      char cMMdataL;                            // MM content low  byte.   
+      char cReg0;                               // data from R0.
+      char cReg1;                               // data from R1.
+      char cReg2;                               // data from R2.
+      char cReg3;                               // data from R3.
+      char cPC;                                 // data from program counter.
+      char cIRH;                                // high byte from instruction register.
+      char cIRL;                                // low  byte from instruction register.   
     };
     MemoryPool<tToHost, POOL_LEN> mPoolToHost;
     Queue     <tToHost, POOL_LEN> qToHost;
+    
+    Queue<int, POOL_LEN> queueWatchdogThread_0; // main thread watchdog notice.
+    Queue<int, POOL_LEN> queueWatchdogThread_1; // thread 1    watchdog notice.
+    Queue<int, POOL_LEN> queueWatchdogThread_2; // thread 2    watchdog notice.
 //--global_variables----------------------------//------------------------------ 
     char          gpcSerialFromHost[SER_BYTES]; // incoming serial buffer.
     char          gpcSerialToHost  [SER_BYTES]; // outgoing serial buffer.
@@ -189,7 +230,8 @@
     void processIncomingSerial();               // process incoming host data.    
     void processOutgoingSerial();               // process outgoing data to host.   
     void SPIprocessingThread(void const *args); // SPI-side processing.   
-    void diagnosticThread   (void const *args); // LCD and LED notifications.   
+    void diagnosticThread   (void const *args); // LCD and LED notifications.  
+    void watchdogThread     (void const *args); // overall watchdog. 
     char ascii_nibble_to_binary(char cAscii);   // ascii nibble -> binary.
     char binary_to_ascii_nibble(char cBinary);  // binary -> ascii nibble.                
     void clear_tFromHost(tFromHost *ptFromHost);// initialize structure.
@@ -205,8 +247,15 @@
       gdRoundTrip    = 1024;                    // initialize global.
       gulSPIclkCount =    0;                    // initialize global.
       gulCPUclkCount =    0;                    // initialize global.
+      led0           =    0;                    // initialize global.
+      led1           =    0;                    // initialize global.
+      led2           =    0;                    // initialize global.
+      led3           =    0;                    // initialize global.
       dHeartbeat     =    0;                    // initialize local.
       dLoop          =    0;                    // initialize local.
+      
+                                                // BOOT notification.
+      lcd.cls(); LCD2; lcd.printf("           BOOT"); wait(1.0);
 
                                                 // initialize serial-in shift-register.
       for (dLoop = 0; dLoop < SER_BYTES; dLoop++) gpcSerialFromHost[dLoop] = 0;
@@ -215,8 +264,11 @@
       Thread thread_1(SPIprocessingThread,NULL,osPriorityHigh,DEFAULT_STACK_SIZE,NULL);
       
                                                 // thread-out diagnostics.
-      Thread thread_2(diagnosticThread,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL);
- 
+      Thread thread_2(diagnosticThread,   NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL);
+      
+                                                // thread-out universal watchdog.
+      Thread thread_3(watchdogThread,     NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL);
+      
       while(1)                                  // main loop.
       {   
         processIncomingSerial();                // process data in from host.
@@ -224,7 +276,8 @@
                         
         dHeartbeat++;                           // thread heartbeat.
         if (!(dHeartbeat % HB_MODULO)) led0 = !led0;
-        Thread::wait(THREAD_0_WAIT);            // multitasking.   
+        queueWatchdogThread_0.put((int *) 0,1); // adds 1mS to wait.
+        Thread::wait(THREAD_0_WAIT - 1);        // multitasking.   
       }                                         // main loop.
     }                                           // main.
 /*----------------------------------------------//----------------------------*/
@@ -316,43 +369,92 @@
       if (gcNewCommand)                         // execute once per new command.
       {
         pFromHost = mPoolFromHost.alloc();      // allocate next pool entry.
-        if (!pFromHost) error("\n\r processIncomingSerial : FATAL malloc error for pFromHost. \n\r"); 
+        if (!pFromHost)                         // failure detection.
+        {
+          if (ERROR_BOOT) mbed_reset(); else
+          error("\n\r processIncomingSerial : FATAL malloc error for pFromHost. \n\r"); 
+        }
+        
         clear_tFromHost(pFromHost);             // initialize structure.
         
                                                 // copy-in host message.
         pFromHost->cCommand = ascii_nibble_to_binary(gpcSerialFromHost[0]);
-                    
-                                                // host requests register access.
-        if (pFromHost->cCommand == HCMD_SETREG || pFromHost->cCommand == HCMD_GETREG)
-        {
-          pFromHost->cRegisterID    =   ascii_nibble_to_binary(gpcSerialFromHost[1]);
-          pFromHost->cRegisterValue = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) + 
-                                        ascii_nibble_to_binary(gpcSerialFromHost[3]);                
-        }
         
-                                                
-        if (pFromHost->cCommand == HCMD_SETIR)  // host requests IR write.
+//----
+
+        switch(pFromHost->cCommand)             // command dependency.
         {
-          pFromHost->cIRValueH = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) + 
-                                   ascii_nibble_to_binary(gpcSerialFromHost[3]);     
-          pFromHost->cIRValueL = ((ascii_nibble_to_binary(gpcSerialFromHost[4])) << 4) + 
-                                   ascii_nibble_to_binary(gpcSerialFromHost[5]);         
-        }
-        
+          case HCMD_SETREG :                    // host command 'set register'.
+          {
+            pFromHost->cRegisterID    =   ascii_nibble_to_binary(gpcSerialFromHost[1]);
+            pFromHost->cRegisterValue = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) + 
+                                          ascii_nibble_to_binary(gpcSerialFromHost[3]);          
+            break;
+          }                                     // host command 'set register'.
         
-                                                // host requests main-memory access.
-        if (pFromHost->cCommand == HCMD_SETMM || pFromHost->cCommand == HCMD_GETMM)
-        {
-          pFromHost->cMMaddress = ((ascii_nibble_to_binary(gpcSerialFromHost[1])) << 4) + 
-                                    ascii_nibble_to_binary(gpcSerialFromHost[2]);  
-          pFromHost->cMMdataH   = ((ascii_nibble_to_binary(gpcSerialFromHost[3])) << 4) + 
-                                    ascii_nibble_to_binary(gpcSerialFromHost[4]);         
-          pFromHost->cMMdataL   = ((ascii_nibble_to_binary(gpcSerialFromHost[5])) << 4) + 
-                                    ascii_nibble_to_binary(gpcSerialFromHost[6]);        
-        }                                       // host requests main-memory access.
+          case HCMD_GETREG :                    // host command 'get register'.
+          {
+            pFromHost->cRegisterID    =   ascii_nibble_to_binary(gpcSerialFromHost[1]);
+            pFromHost->cRegisterValue = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) + 
+                                          ascii_nibble_to_binary(gpcSerialFromHost[3]);   
+            gdRoundTrip++;                      // expected reply to host is pending.       
+            break;
+          }                                     // host command 'get register'.
+                 
+          case HCMD_SETMM :                     // host command 'set main-memory.'
+          {
+            pFromHost->cMMaddress = ((ascii_nibble_to_binary(gpcSerialFromHost[1])) << 4) + 
+                                      ascii_nibble_to_binary(gpcSerialFromHost[2]);  
+            pFromHost->cMMdataH   = ((ascii_nibble_to_binary(gpcSerialFromHost[3])) << 4) + 
+                                      ascii_nibble_to_binary(gpcSerialFromHost[4]);         
+            pFromHost->cMMdataL   = ((ascii_nibble_to_binary(gpcSerialFromHost[5])) << 4) + 
+                                      ascii_nibble_to_binary(gpcSerialFromHost[6]);           
+            break;
+          }                                     // host command 'set main-memory.'
+               
+          case HCMD_GETMM :                     // host command 'get main-memory.'
+          {
+            pFromHost->cMMaddress = ((ascii_nibble_to_binary(gpcSerialFromHost[1])) << 4) + 
+                                      ascii_nibble_to_binary(gpcSerialFromHost[2]);  
+            pFromHost->cMMdataH   = ((ascii_nibble_to_binary(gpcSerialFromHost[3])) << 4) + 
+                                      ascii_nibble_to_binary(gpcSerialFromHost[4]);         
+            pFromHost->cMMdataL   = ((ascii_nibble_to_binary(gpcSerialFromHost[5])) << 4) + 
+                                      ascii_nibble_to_binary(gpcSerialFromHost[6]);    
+                                      
+            gdRoundTrip++;                      // expected reply to host is pending.        
+            break;
+          }                                     // host command 'get main-memory.'
+                
+          case HCMD_STEP :                      // host command 'step-CPU'.
+          {
+            break;
+          }                                     // host command 'step-CPU'.
+            
+          case HCMD_SETIR :                     // host command 'set-IR'.
+          {
+            pFromHost->cIRValueH = ((ascii_nibble_to_binary(gpcSerialFromHost[2])) << 4) + 
+                                     ascii_nibble_to_binary(gpcSerialFromHost[3]);     
+            pFromHost->cIRValueL = ((ascii_nibble_to_binary(gpcSerialFromHost[4])) << 4) + 
+                                     ascii_nibble_to_binary(gpcSerialFromHost[5]);            
+            break;
+          }                                     // host command 'set-IR'.
+            
+          case HCMD_GETALLREG :                 // host command 'get-all-registers'.
+          {
+            gdRoundTrip++;                      // expected reply to host is pending.
+            break;
+          }                                     // host command 'get-all-registers'.
         
-        if (pFromHost->cCommand == HCMD_GETREG || pFromHost->cCommand == HCMD_GETMM)
-        gdRoundTrip++;                          // expected reply to host is pending.           
+          default :                             // default.
+          {
+            if (ERROR_BOOT) mbed_reset(); else
+            error("\n\r processIncomingSerial : ERROR unrecognised command from host. \n\r");
+            break;
+          }                                     // default.
+        }                                       // command dependency.
+        
+//----
+                   
         qFromHost.put(pFromHost);               // send out for processing.    
         gcNewCommand = 0;                       // don't execute until next new command.
       }                                         // execute once per new command.
@@ -370,43 +472,101 @@
       {
                                                 // bring it in from the queue.
         gpToHost = (tToHost *) gqToHostEvent.value.p;
-        if (!gpToHost) error("\n\r processOutgoingSerial : FATAL NULL gpToHost pointer. \n\r"); 
+        if (!gpToHost)                         // failure detection.
+        {
+          if (ERROR_BOOT) mbed_reset(); else
+          error("\n\r processOutgoingSerial : FATAL NULL gpToHost pointer. \n\r"); 
+        }
           
                                                 // clear outgoing buffer.
         for (dLoop = 0; dLoop < SER_BYTES; dLoop++) gpcSerialToHost[dLoop] = 0;
-                                                           
-                                                // the commands from the host were
-                                                // looped-back into the to-host struct,
-                                                // make use of them here.
-                                           
-        if (gpToHost->cCommand == HCMD_GETREG)  // reading from a register.                                             
+                                                                                                           
+        switch(gpToHost->cCommand)              // the from-host command was looped to here.
         {
-          gpcSerialToHost[0] = binary_to_ascii_nibble(  gpToHost->cCommand);
-          gpcSerialToHost[1] = binary_to_ascii_nibble(  gpToHost->cRegisterID);
-          gpcSerialToHost[2] = binary_to_ascii_nibble(((gpToHost->cRegisterValue) >> 4) & 0x0F);
-          gpcSerialToHost[3] = binary_to_ascii_nibble(((gpToHost->cRegisterValue) >> 0) & 0x0F);
-          gpcSerialToHost[4] = '\n';            // signals end of transfer.
+          case HCMD_SETREG :                    // host command 'set register'.
+          {
+            break;
+          }                                     // host command 'set register'.
+        
+          case HCMD_GETREG :                    // host command 'get register'.
+          {
+            gpcSerialToHost[0] = binary_to_ascii_nibble(  gpToHost->cCommand);
+            gpcSerialToHost[1] = binary_to_ascii_nibble(  gpToHost->cRegisterID);
+            gpcSerialToHost[2] = binary_to_ascii_nibble(((gpToHost->cRegisterValue) >> 4) & 0x0F);
+            gpcSerialToHost[3] = binary_to_ascii_nibble(((gpToHost->cRegisterValue) >> 0) & 0x0F);
+            gpcSerialToHost[4] = '\n';          // signals end of transfer.
+             
+                                                // transmit to the host.
+            serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES);        
+            gdRoundTrip--;                      // expected reply sent to host.       
+            break;
+          }                                     // host command 'get register'.
+                 
+          case HCMD_SETMM :                     // host command 'set main-memory.'
+          {
+            break;
+          }                                     // host command 'set main-memory.'
+               
+          case HCMD_GETMM :                     // host command 'get main-memory.'
+          {
+            gpcSerialToHost[0] = binary_to_ascii_nibble(  gpToHost->cCommand);
+            gpcSerialToHost[1] = binary_to_ascii_nibble(((gpToHost->cMMaddress) >> 4) & 0x0F);
+            gpcSerialToHost[2] = binary_to_ascii_nibble(((gpToHost->cMMaddress) >> 0) & 0x0F);
+            gpcSerialToHost[3] = binary_to_ascii_nibble(((gpToHost->cMMdataH  ) >> 4) & 0x0F);
+            gpcSerialToHost[4] = binary_to_ascii_nibble(((gpToHost->cMMdataH  ) >> 0) & 0x0F);
+            gpcSerialToHost[5] = binary_to_ascii_nibble(((gpToHost->cMMdataL  ) >> 4) & 0x0F);
+            gpcSerialToHost[6] = binary_to_ascii_nibble(((gpToHost->cMMdataL  ) >> 0) & 0x0F);   
+            gpcSerialToHost[7] = '\n';          // signals end of transfer.    
              
                                                 // transmit to the host.
-          serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES);        
-          gdRoundTrip--;                        // expected reply sent to host.                           
-        }                                  
-                                                         
-        if (gpToHost->cCommand == HCMD_GETMM)  // reading from main-memory.                                              
-        {
-          gpcSerialToHost[0] = binary_to_ascii_nibble(  gpToHost->cCommand);
-          gpcSerialToHost[1] = binary_to_ascii_nibble(((gpToHost->cMMaddress) >> 4) & 0x0F);
-          gpcSerialToHost[2] = binary_to_ascii_nibble(((gpToHost->cMMaddress) >> 0) & 0x0F);
-          gpcSerialToHost[3] = binary_to_ascii_nibble(((gpToHost->cMMdataH  ) >> 4) & 0x0F);
-          gpcSerialToHost[4] = binary_to_ascii_nibble(((gpToHost->cMMdataH  ) >> 0) & 0x0F);
-          gpcSerialToHost[5] = binary_to_ascii_nibble(((gpToHost->cMMdataL  ) >> 4) & 0x0F);
-          gpcSerialToHost[6] = binary_to_ascii_nibble(((gpToHost->cMMdataL  ) >> 0) & 0x0F);   
-          gpcSerialToHost[7] = '\n';            // signals end of transfer.    
-             
+            serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES);    
+            gdRoundTrip--;                      // expected reply sent to host.        
+            break;
+          }                                     // host command 'get main-memory.'
+                
+          case HCMD_STEP :                      // host command 'step-CPU'.
+          {
+            break;
+          }                                     // host command 'step-CPU'.
+            
+          case HCMD_SETIR :                     // host command 'set-IR'.
+          {
+            break;
+          }                                     // host command 'set-IR'.
+            
+          case HCMD_GETALLREG :                 // host command 'get-all-registers'.
+          {
+            gpcSerialToHost[ 0] = binary_to_ascii_nibble(  gpToHost->cCommand);
+            gpcSerialToHost[ 1] = binary_to_ascii_nibble(((gpToHost->cReg0) >> 4) & 0x0F);
+            gpcSerialToHost[ 2] = binary_to_ascii_nibble(((gpToHost->cReg0) >> 0) & 0x0F);
+            gpcSerialToHost[ 3] = binary_to_ascii_nibble(((gpToHost->cReg1) >> 4) & 0x0F);
+            gpcSerialToHost[ 4] = binary_to_ascii_nibble(((gpToHost->cReg1) >> 0) & 0x0F);
+            gpcSerialToHost[ 5] = binary_to_ascii_nibble(((gpToHost->cReg2) >> 4) & 0x0F);
+            gpcSerialToHost[ 6] = binary_to_ascii_nibble(((gpToHost->cReg2) >> 0) & 0x0F);
+            gpcSerialToHost[ 7] = binary_to_ascii_nibble(((gpToHost->cReg3) >> 4) & 0x0F);
+            gpcSerialToHost[ 8] = binary_to_ascii_nibble(((gpToHost->cReg3) >> 0) & 0x0F);
+            gpcSerialToHost[ 9] = binary_to_ascii_nibble(((gpToHost->cPC)   >> 4) & 0x0F);
+            gpcSerialToHost[10] = binary_to_ascii_nibble(((gpToHost->cPC)   >> 0) & 0x0F);
+            gpcSerialToHost[11] = binary_to_ascii_nibble(((gpToHost->cIRH)  >> 4) & 0x0F);
+            gpcSerialToHost[12] = binary_to_ascii_nibble(((gpToHost->cIRH)  >> 0) & 0x0F);          
+            gpcSerialToHost[13] = binary_to_ascii_nibble(((gpToHost->cIRL)  >> 4) & 0x0F);
+            gpcSerialToHost[14] = binary_to_ascii_nibble(((gpToHost->cIRL)  >> 0) & 0x0F);                 
+            gpcSerialToHost[15] = '\n';         // signals end of transfer.         
+          
                                                 // transmit to the host.
-          serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES);    
-          gdRoundTrip--;                        // expected reply sent to host.
-        }           
+            serial.writeBlock((uint8_t *) gpcSerialToHost, (uint16_t) SER_BYTES);        
+            gdRoundTrip--;                      // expected reply sent to host.  
+            break;
+          }                                     // host command 'get-all-registers'.
+        
+          default :
+          {
+            if (ERROR_BOOT) mbed_reset(); else
+            error("\n\r processOutgoingSerial : ERROR unrecognised command from host. \n\r");
+            break;
+          }                                     // default.
+        }                                       // switch(gpToHost->cCommand)                                         
+                                                
         mPoolToHost.free(gpToHost);             // done with this queue entry.
         gpToHost = NULL;                        // clear pointer.
       }                                         // if new reply to host.     
@@ -421,6 +581,7 @@
     {
       char        pcSendBuffer   [SPI_BYTES];   // SPI send buffer.
       char        pcReceiveBuffer[SPI_BYTES];   // SPI receive buffer.
+      char        pcRegisters    [SPI_BYTES];   // fetch-all-registers vector.
       int         dHeartbeat;                   // heartbeat counter.
       int         dMemoryRead;                  // read main-memory into this.
       int         dLoop;                        // loop index.
@@ -439,8 +600,12 @@
       for (dLoop = 0; dLoop < SPI_BYTES; dLoop++) pcSendBuffer   [dLoop] = 0;
       for (dLoop = 0; dLoop < SPI_BYTES; dLoop++) pcReceiveBuffer[dLoop] = 0;
  
-      pSPI = new mmSPI;                         // SPI allocation.
-      if (!pSPI)  error("\n\r SPIprocessingThread : FATAL new error for pSPI. \n\r");
+      pSPI = new mmSPI;                         // SPI allocation.      
+      if (!pSPI)                                // failure detection.
+      {
+        if (ERROR_BOOT) mbed_reset(); else
+        error("\n\r SPIprocessingThread : FATAL new error for pSPI. \n\r"); 
+      }      
       
       pSPI->setSendBuffer   (pcSendBuffer);     // set SPI send buffer.
       pSPI->setReceiveBuffer(pcReceiveBuffer);  // set SPI receive buffer.
@@ -456,7 +621,11 @@
         {
                                                 // bring it in from the queue.
           pFromHost = (tFromHost *) qFromHostEvent.value.p;
-          if (!pFromHost) error("\n\r SPIprocessingThread : FATAL NULL pFromHost pointer. \n\r"); 
+          if (!pFromHost)                       // failure detection.
+          {
+            if (ERROR_BOOT) mbed_reset(); else
+            error("\n\r SPIprocessingThread : FATAL NULL pFromHost pointer. \n\r");  
+          }
           
           switch(pFromHost->cCommand)           // host command decode.
           {
@@ -483,7 +652,12 @@
             case HCMD_GETREG :                  // get CPU register.
             {
               gpToHost = mPoolToHost.alloc();   // allocate next pool entry.
-              if (!gpToHost) error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r"); 
+              if (!gpToHost)                    // failure detection.
+              {
+                if (ERROR_BOOT) mbed_reset(); else
+                error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r"); 
+              }              
+              
               clear_tToHost(gpToHost);          // initialize structure.
             
               switch(pFromHost->cRegisterID)    // which register to read from.
@@ -506,6 +680,35 @@
               break; 
             }                                   // get CPU register.     
             
+            case HCMD_GETALLREG :               // get all CPU registers.
+            {
+              gpToHost = mPoolToHost.alloc();   // allocate next pool entry.              
+              if (!gpToHost)                    // failure detection.
+              {
+                if (ERROR_BOOT) mbed_reset(); else
+                error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r");
+              }              
+              
+              clear_tToHost(gpToHost);          // initialize structure.
+              
+                                                // tell SPI to return all reg content.
+              pSPI->read_all_registers(pcRegisters);
+              
+              gpToHost->cReg0 = pcRegisters[6]; // transfer to outgoing structure.
+              gpToHost->cReg1 = pcRegisters[5];
+              gpToHost->cReg2 = pcRegisters[4];
+              gpToHost->cReg3 = pcRegisters[3];
+              gpToHost->cPC   = pcRegisters[2];
+              gpToHost->cIRH  = pcRegisters[1];
+              gpToHost->cIRL  = pcRegisters[0];
+              
+                                                // loop-back to host.
+              gpToHost->cCommand = pFromHost->cCommand;
+              qToHost.put(gpToHost);            // send up for processing. 
+              
+              break;
+            }                                   // get all CPU registers.
+            
             case HCMD_SETMM  :                  // do main-memory write.
             {
               pSPI->write_memory(pFromHost->cMMdataH, pFromHost->cMMdataL, pFromHost->cMMaddress);     
@@ -514,8 +717,13 @@
             
             case HCMD_GETMM  :                  // do main-memory read.
             {
-              gpToHost = mPoolToHost.alloc();   // allocate next pool entry.
-              if (!gpToHost) error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r"); 
+              gpToHost = mPoolToHost.alloc();   // allocate next pool entry.   
+              if (!gpToHost)                    // failure detection.
+              {
+                if (ERROR_BOOT) mbed_reset(); else
+                error("\n\r SPIprocessingThread : FATAL malloc error for gpToHost. \n\r"); 
+              }              
+              
               clear_tToHost(gpToHost);          // initialize structure.
             
                                                 // read from CPU memory.
@@ -547,39 +755,85 @@
  
                                                 // thread heartbeat.
         dHeartbeat++; if (!(dHeartbeat % HB_MODULO)) led1 = !led1;
-        Thread::wait(THREAD_1_WAIT);            // cooperative multitasking.      
+        queueWatchdogThread_1.put((int *) 0,1); // adds 1mS to wait.
+        Thread::wait(THREAD_1_WAIT - 1);        // cooperative multitasking.      
       }                                         // thread loop.
     }                                           // SPIprocessingThread.
 /*----------------------------------------------//----------------------------*/
-    void diagnosticThread(void const *args)     // LCD and LED notifications.
+    void diagnosticThread(void const *args)     // LCD notification & error check.
     {
-      int dHeartbeat;                           // heartbeat counter.
-    
+                                                // track previous values.
+      static unsigned long ulLastSPIclkCount = 1;      
+      static unsigned long ulLastCPUclkCount = 1;
+      int                  dHeartbeat;          // heartbeat counter.
+      
       dHeartbeat = 0;                           // initialize.
-      led3       = 0;                           // initialize.
-      
+            
       while(1)                                  // thread loop.
       {
                                                 // if message round trip 
                                                 // count not consistent.
-        if (gdRoundTrip > 1025 || gdRoundTrip < 1024) led3 = 1;
-        
-        lcd.cls();                              // clear LCD display.
-        LCD1;                                   // lcd line 1.
-        lcd.printf(" USB DEV CLASS PROJECT");
+        if (gdRoundTrip > 1025 || gdRoundTrip < 1024)
+        {
+          if (ERROR_BOOT) mbed_reset(); else
+          error("\n\r diagnosticThread : ERROR serial reply underflow. \n\r");         
+        }
         
-        LCD2;                                   // lcd line 2.
-        lcd.printf(" %11lu = SPI clocks",gulSPIclkCount);
+                                                // update LCD only if a display
+                                                // value has changed.  anti-blink,
+                                                // save a bit of power.
+        if (gulSPIclkCount != ulLastSPIclkCount ||
+            gulCPUclkCount != ulLastCPUclkCount   )
+        {
+          lcd.cls();                            // clear LCD display.
+          LCD1;                                 // lcd line 1.
+          lcd.printf(" RTOS CLASS PROJECT");
         
-        LCD3;                                   // lcd line 3.
-        lcd.printf(" %11lu = CPU clocks",gulCPUclkCount);
+          LCD2;                                 // lcd line 2.
+          lcd.printf(" %11lu = SPI clocks",gulSPIclkCount);
+        
+          LCD3;                                 // lcd line 3.
+          lcd.printf(" %11lu = CPU clocks",gulCPUclkCount);
+        }
+        
+        ulLastSPIclkCount = gulSPIclkCount;     // pipeline.
+        ulLastCPUclkCount = gulCPUclkCount;     // pipeline.
        
         dHeartbeat++;                           // thread heartbeat.
         if (!(dHeartbeat % HB_MODULO)) led2 = !led2;
-        Thread::wait(THREAD_2_WAIT);            // multitasking.
+        queueWatchdogThread_2.put((int *) 0,1); // adds 1mS to wait.
+        Thread::wait(THREAD_2_WAIT - 1);        // multitasking.
       }                                         // thread loop.
     }                                           // diagnosticThread.
 /*----------------------------------------------//----------------------------*/
+                                                // this is its own watchdog.
+
+    void watchdogThread(void const *args)       // overall watchdog.
+    {
+      int      dHeartbeat;                      // heartbeat counter.
+      osEvent  queueEvent;                      // queue event
+      Watchdog watchdog;                        // the one and only watchdog.
+      
+      dHeartbeat = 0;                           // initialize.
+
+      watchdog.kick(WATCHDOG_S);                // initialize watchdog.
+    
+      while (1)                                 // thread loop.
+      {
+                                                // all other threads report-in.
+                                                // blocking wait on all of them.
+        queueEvent = queueWatchdogThread_0.get(osWaitForever); 
+        queueEvent = queueWatchdogThread_1.get(osWaitForever);
+        queueEvent = queueWatchdogThread_2.get(osWaitForever);  
+
+        watchdog.kick();                        // reset watchdog timer.
+        
+        dHeartbeat++;                           // thread heartbeat.
+        if (!(dHeartbeat % HB_MODULO)) led3 = !led3;
+        Thread::wait(THREAD_3_WAIT);            // multitasking.
+      }                                         // thread loop.
+    }
+/*----------------------------------------------//----------------------------*/
     char ascii_nibble_to_binary(char cAscii)    // ascii nibble -> binary.
     {
       char cBinary;                             // converted value.