Mike Moore / mmSPI_RTOS

Dependents:   RTOS_project RTOS_project_fork_01 RTOS_project_fork_02

Files at this revision

API Documentation at this revision

Comitter:
gatedClock
Date:
Tue Sep 17 20:40:03 2013 +0000
Commit message:
update.

Changed in this revision

mmSPI_RTOS.cpp Show annotated file Show diff for this revision Revisions of this file
mmSPI_RTOS.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 7b8e6b90c874 mmSPI_RTOS.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmSPI_RTOS.cpp	Tue Sep 17 20:40:03 2013 +0000
@@ -0,0 +1,442 @@
+/*----------------------------------------------//------------------------------
+    student   : m-moore
+    email     : gated.clock@gmail.com
+    class     : embedded RTOS
+    directory : URTOS_project/mmSPI_RTOS
+    file      : mmSPI_RTOS.cpp
+    date      : september 19, 2013.
+----copyright-----------------------------------//------------------------------   
+    licensed for personal and academic use.
+    commercial use of original code must be approved by the account-holder of
+    gated.clock@gmail.com
+----description---------------------------------//------------------------------
+    this library provides the low-level SPI data and clock signaling 
+    for communication with the altera development board, via four i/o
+    pins available on the mbed development board's zigbee header.
+    
+    this library also provides higher-level calls to send/receive register
+    and main-memory data to/from the CPU implemented in the altera.
+    
+    the SPI clock and the CPU clock are both built by this library.
+    
+    the SPI clock (*pSCLK) is generated with quarter-phase resolution,
+    although at the moment, only half-phase resolution is being used.
+    
+    within the FPGA, the SPI scan registers are called 'shadow registers'
+    because they parallel load-to/store-from the CPU registers.  
+    
+    character vectors pcSend and pcReceive are used to scan-in and scan-out
+    to/from the altera shadow registers.  note that the prefix 'pc' means
+    'pointer-of-type-character' and not 'personal-computer'.
+    
+    the shadow registers in the altera are arranged as follows:
+    
+ <-- MISO (altera-out to mbed-in)
+     ^
+     |
+   scan_08  U14_spi_control = pcSend/pcReceive[7]
+   scan_08  U08_shadowR0    = pcSend/pcReceive[6]
+   scan_08  U09_shadowR1    = pcSend/pcReceive[5]
+   scan_08  U10_shadowR2    = pcSend/pcReceive[4]
+   scan_08  U11_shadowR3    = pcSend/pcReceive[3]
+   scan_08  U12_shadowPC    = pcSend/pcReceive[2]
+   scan_16  U13_shadowIR    = pcSend/pcReceive[1:0]
+     ^
+     |
+ --> MOSI (altera-in from mbed-out)
+ 
+    when U14_spi_control<1> is high, the mbed takes over CPU operation.
+    this means that the CPU instruction decoder decodes U13_shadowIR
+    rather than the CPU's actual IR.
+    
+    when U14_spi_control<2> is high, the mbed writes into the IR.
+    this means that the CPU instruction decoder load-enable outputs
+    are gated-off so that the mbed may write into the instrucition
+    register without immediate consequence in the CPU.  writing to
+    the IR this way is more for the ability to show that it can be done,
+    rather than any likely useful purpose.
+    
+    debug/test:
+    this library was debugged by using the altera FPGA logic analyzer
+    known as 'signal tap'.  the main program uses all of the memory and
+    register access calls, which were used as an informal unit test.
+-----includes-----------------------------------//----------------------------*/
+    #include "mmSPI_RTOS.h"
+//==============================================//==============================
+    mmSPI_RTOS::mmSPI_RTOS()                    // constructor.
+    {
+      allocations();                            // object allocations.
+      
+      *pSCLK        = 0;                        // initialize.
+      *pCPUclk      = 0;                        // initialize.
+      ulSPIclkCount = 0;                        // initialize.
+      ulCPUclkCount = 0;                        // initialize.
+    }                                           // constructor.
+//----------------------------------------------//------------------------------    
+    mmSPI_RTOS::~mmSPI_RTOS()                   // destructor.
+    {
+                                                // deallocations.
+      if (pMOSI)   {delete pMOSI;   pMOSI   = NULL;}
+      if (pMISO)   {delete pMISO;   pMISO   = NULL;}
+      if (pSCLK)   {delete pSCLK;   pSCLK   = NULL;}
+      if (pCPUclk) {delete pCPUclk; pCPUclk = NULL;}
+    }                                           // destructor.
+//----------------------------------------------//------------------------------
+    void mmSPI_RTOS::allocations(void)          // object allocations.
+    {
+      pMOSI   = new DigitalOut(mmSPI_RTOS_MOSI);// SPI MOSI pin object.
+      if (!pMOSI) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pMOSI. \n\r"); 
+      
+      pMISO   = new DigitalOut(mmSPI_RTOS_MISO);// SPI MISO pin object.
+      if (!pMISO) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pMISO. \n\r"); 
+    
+      pSCLK   = new DigitalOut(mmSPI_RTOS_SCLK);// SPI SCLK pin object.
+      if (!pSCLK) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pSCLK. \n\r"); 
+      
+      pCPUclk = new DigitalOut(mmCPU_CLK);      // SPI SCLK pin object.
+      if (!pCPUclk) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pCPUclk. \n\r");
+    }                                           // allocations.
+//----------------------------------------------//------------------------------    
+                                                // set SPI clock frequency.
+    void mmSPI_RTOS::setSPIfrequency(float fFreq)    
+    {
+      fSPIfreq = fFreq;                         // promote to object scope.
+      if (fSPIfreq < .05)                       // don't get near divide-by-zero.
+      error("\n\r mmSPI_RTOS::setSPIfrequency : FATAL SPI frequency set too low. \n\r"); 
+      fSPIquarterP = (1 / fSPIfreq) / 4;        // figure quarter-cycle period.
+    }
+//----------------------------------------------//------------------------------  
+                                                // obtain SPI send buffer pointer.  
+    void mmSPI_RTOS::setSendBuffer(char * pcSendBuffer)
+    {
+      pcSend = pcSendBuffer;                    // promote to object scope.
+    }                                           // setSendBuffer.
+//----------------------------------------------//------------------------------    
+                                                // obtain SPI receive buffer pointer.  
+    void mmSPI_RTOS::setReceiveBuffer(char * pcReceiveBuffer)
+    {
+      pcReceive = pcReceiveBuffer;              // promote to object scope.
+    }                                           // setReceiveBuffer.
+//----------------------------------------------//------------------------------
+                                                // obtain number of SPI bytes.
+    void mmSPI_RTOS::setNumberOfBytes(int dNumberOfBytes)
+    {
+      dNumBytes = dNumberOfBytes;               // promote to object scope.
+    }                                           // setNumberOfBytes.
+//----------------------------------------------//------------------------------
+/*
+    this method has four 'components' which may be switched on/off by the caller:
+    1. cPreCPU  = 1 means cycle the altera CPU clock once.
+    2. cPreSPI  = 1 means cycle the        SPI clock once.
+    3. cScan    = 1 means run a full SPI scan-in/scan-out cycle.
+    4. cPostCPU = 1 means cycle the alter CPU clock once, then the SPI clock once.
+    
+    in the altera, upon the falling edge of any CPU clock, a load-enable is
+    asserted into the scan registers, such that at the next rising edge of
+    the SPI clock, the scan registers perform a parallel-load of the CPU
+    registers, so that that data can subsequently be scanned back into the mbed
+    via a full SPI scan-in cycle.  (this load-enable is turned-off upon the first
+    falling edge of the subsequent SPI clock.)
+    therefore, the regular input switches to this method are (1,1,1,0).  
+    this means that the altera CPU clock will cycle,
+    and the result of that CPU operation will be parallel-loaded into the
+    shadow registers for scan into the mbed on the subsequent full-scan cycle.
+    
+    a finer level of sequencing these 'components' is needed by the 'step' method,
+    which is why they are under four-input-parameter control.                                                
+ */
+    void mmSPI_RTOS::transceive_vector(char cPreCPU, char cPreSPI, char cScan, char cPostCPU)
+    {      
+      int  dClear;                              // clear-loop index.
+      int  dIndex;                              // total scan index.
+      int  dMosiByteIndex;                      // SPI-MOSI byte index.
+      int  dMosiBitIndex;                       // SPI-MOSI bit  index.
+      int  dMisoByteIndex;                      // SPI-MISO byte index.
+      int  dMisoBitIndex;                       // SPI-MISO bit  index.
+      
+                                                // initializations.  these are needed.
+      dIndex         = (dNumBytes * 8) - 1;     // calculate scan count.
+      dMosiByteIndex =  dIndex / 8;             // calculate current byte index.
+      dMosiBitIndex  =  dIndex % 8;             // calculate current bit  iindex.
+      
+                                                // clear the SPI receive vector.
+      for (dClear = 0; dClear < dNumBytes; dClear++) pcReceive[dClear] = 0;
+//---
+      if (cPreCPU)                              // if pre-CPU clock.
+      {
+        *pCPUclk = 1;                           // pulse the CPU clock.
+        wait(fSPIquarterP); 
+        wait(fSPIquarterP);     
+        *pCPUclk = 0;  
+        wait(fSPIquarterP); 
+        wait(fSPIquarterP); 
+        ulCPUclkCount++;
+      }                                         // if pre-CPU clock.
+ //---     
+      if (cPreSPI)                              // if pre-SPI pulse.
+      {
+        *pSCLK = 1;                             // pulse the SPI clock for parallel load.              
+        wait(fSPIquarterP); 
+        wait(fSPIquarterP); 
+        *pSCLK = 0;
+        ulSPIclkCount++;
+      }                                         // if pre-SPI pulse.
+ //---     
+      if (cScan)                                // if cScan.
+      {
+                                                // pre-assert MOSI.
+        *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1;
+        wait(fSPIquarterP); 
+        wait(fSPIquarterP); 
+      
+      
+                                                // main SPI scan loop.
+        for (dIndex = (dNumBytes * 8) - 1; dIndex >= 0; dIndex--)
+        {
+          dMisoByteIndex = dIndex / 8;
+          dMisoBitIndex  = dIndex % 8;
+          pcReceive[dMisoByteIndex] = pcReceive[dMisoByteIndex] | (*pMISO << dMisoBitIndex);      
+          *pSCLK = 1;             
+          wait(fSPIquarterP); 
+          wait(fSPIquarterP); 
+          *pSCLK = 0;    
+          if (dIndex < 0) dIndex = 0;
+          dMosiByteIndex = (dIndex - 1) / 8;
+          dMosiBitIndex  = (dIndex - 1) % 8;
+          *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1;
+          wait(fSPIquarterP); 
+          wait(fSPIquarterP);
+          ulSPIclkCount++;
+        }                                       // main SPI scan loop.
+      }                                         // if cScan.
+ //---     
+      if (cPostCPU)                             // if post-CPU pulse.
+      {
+        *pCPUclk = 1;                           // pulse the CPU clock.
+        wait(fSPIquarterP); 
+        wait(fSPIquarterP);     
+        *pCPUclk = 0;  
+        wait(fSPIquarterP); 
+        wait(fSPIquarterP);   
+        ulCPUclkCount++;    
+        
+        *pSCLK = 1;                             // clear-out the SPI parallel-load enable.        
+        wait(fSPIquarterP); 
+        wait(fSPIquarterP); 
+        *pSCLK = 0;        
+        ulSPIclkCount++;
+      }                                         // if post-CPU pulse.
+    }                                           // transceive_vector.
+//----------------------------------------------//------------------------------
+/*
+   cRegister  ->  CPU_register  
+      0            R0
+      1            R1
+      2            R2
+      3            R3
+      4            PC
+      5         <use write_IR instead>
+      6         <nothing>
+      7         <nothing>
+      
+    in order to write to a CPU register, pcSend[7] is set to 0x02,
+    which informs the CPU instruction decoder to decode what we are
+    here scanning into the shadow instruction-register, rather than
+    the CPU instruction decoder decoding the regular instruction-register.
+    here, we set-up pcSend to scan the instruction 
+    'load Rx with immediate data yy', and this is executed on the
+    subsequent CPU clock.  
+    
+    said another way, this method writes a value into a CPU register
+    by effectively taking over the CPU and doing a write-register-immediate.
+*/
+
+                                                // write to a CPU register.
+    void mmSPI_RTOS::write_register(char cRegister, char cValue)
+    {     
+      clear_transmit_vector();                  // clear transmit vector.
+      
+      pcSend[7] = 0x02;                         // mbed sends a command.
+      
+                                                // align into instruction word.
+      pcSend[1] = ((cRegister & 0x07) << 2) | 0xA0;
+      pcSend[0] = cValue & 0xFF;                // immediate value to i.w.
+ 
+      transceive_vector(1,1,1,0);               // transmit command.
+ 
+      clear_transmit_vector();                  // clear transmit vector.
+    }                                           // write_register.
+//----------------------------------------------//------------------------------  
+/*  
+    for writing to the instruction-register, this method is used.
+    for reading from the instruction-register, the 'regular' 
+    read_register method is used, once for each of the two IR data bytes.
+    
+    unlike method 'write_register', this method works by gating-off the
+    outputs of the CPU instruction decoder, and having the instruction-register
+    content loaded in from the shadow instruction-register.
+*/
+                                                // write instruction register.
+    void mmSPI_RTOS::write_IR(char cValueH, char cValueL)
+    {
+      clear_transmit_vector();                  // clear transmit vector.
+      
+      pcSend[7] = 0x06;                         // mbed sends a command.
+      
+      pcSend[1] = (cValueH & 0xFF);             // load IR shadow with new IR content.
+      pcSend[0] = (cValueL & 0xFF);
+ 
+      transceive_vector(1,1,1,0);               // transmit command.
+ 
+      clear_transmit_vector();                  // clear transmit vector.    
+    }                                           // write instruction register.
+//----------------------------------------------//------------------------------
+/*
+  cRegister  ->  CPU_register   pcReceive
+      0      ->    R0             [6]
+      1      ->    R1             [5]
+      2      ->    R2             [4]
+      3      ->    R3             [3]
+      4      ->    PC             [2]
+      5      ->    IR-H           [1]
+      6      ->    IR-L           [0]
+      7      ->  <never-use>
+      
+    this method works by taking over the instruction decoder as described above,
+    but not issuing any instruction, but the scan registers will parallel-load
+    all of the CPU register data and scan them into pcReceive, where they are
+    picked up below.
+*/
+                                                // return content of a CPU register.
+    char mmSPI_RTOS::read_register(char cRegister)   
+    { 
+      clear_transmit_vector();                  // clear transmit vector.
+      
+      pcSend[7] = 0x02;                         // suppress cpu operation.
+
+      transceive_vector(1,1,1,0);               // snap & scan-out reg contents.
+      
+      return (pcReceive[6 - cRegister]);        // return the particular reg value.
+    }                                           // read_register.
+//----------------------------------------------//------------------------------
+//----------------------------------------------//------------------------------
+/*
+  since the Tk UI often wants to obtain the content of all of the CPU registers,
+  this method may be used to obtain all of them at once, speading up the UI 
+  read cycle.
+      
+  pRegisters[6] <- pcReceive[6] = R0    
+  pRegisters[5] <- pcReceive[5] = R1
+  pRegisters[4] <- pcReceive[4] = R2
+  pRegisters[3] <- pcReceive[3] = R3
+  pRegisters[2] <- pcReceive[2] = PC
+  pRegisters[1] <- pcReceive[1] = IR-H
+  pRegisters[0] <- pcReceive[0] = IR-L  
+*/
+
+    void mmSPI_RTOS::read_all_registers(char * pcRegisters) 
+    { 
+      int dLoop;                                // loop index.
+      
+      clear_transmit_vector();                  // clear transmit vector.
+      
+      pcSend[7] = 0x02;                         // suppress cpu operation.
+
+      transceive_vector(1,1,1,0);               // snap & scan-out reg contents.
+      
+                                                // propagate register data.
+      for (dLoop = 0; dLoop < 7; dLoop++) pcRegisters[dLoop] = pcReceive[dLoop];
+      
+    }                                           // read_all_registers.
+//----------------------------------------------//------------------------------
+/*
+    this method works by taking over the instruction decoder, and issuing
+    the CPU commands which load the MM address/data into {R3,R2,R1}
+    followed by issuing a write-pulse.
+*/
+                                                // write a word to main-memory.
+    void mmSPI_RTOS::write_memory(char cHData, char cLdata, char cAddress)
+    {
+      clear_transmit_vector();                  // clear transmit vector.
+                    
+      write_register(0x03,cAddress);            // R3 <- address.
+      write_register(0x02,cHData);              // R2 <- high-data.
+      write_register(0x01,cLdata);              // R1 <- low-data.
+  
+      pcSend[7] = 0x02;                         // mbed sends command.
+      pcSend[1] = 0x02;                         // write-enable high.
+      pcSend[0] = 0x00;                         // remainder of instruction.
+      transceive_vector(1,1,1,0);
+      
+      pcSend[7] = 0x02;                         // mbed sends command.
+      pcSend[1] = 0x00;                         // write-enable low.
+      pcSend[0] = 0x00;                         // remainder of instruction.
+      transceive_vector(1,1,1,0);   
+                           
+      clear_transmit_vector();                  // clear transmit vector. 
+    }                                           // write_memory.
+//----------------------------------------------//------------------------------
+/*
+    this command works by taking over the instruction decoder, loading
+    R3 with the MM address, then issuing load-from-MM commands so that
+    R2,R1 are loaded with MM[R3], and then reading the result from
+    R1,R2.
+*/
+                                                // fetch a word from main memory.
+    unsigned int mmSPI_RTOS::read_memory(char cAddress)
+    { 
+      unsigned int udMemoryContent;             // return variable.
+      char         cHData;                      // returned data-high.
+      char         cLData;                      // returned data-low.
+                               
+      clear_transmit_vector();                  // clear transmit vector.
+            
+      write_register(0x03,cAddress);            // R3 <= address.
+      
+      pcSend[7] = 0x02;                         // mbed sends command.
+      pcSend[1] = 0xC8;                         // R2 <- MM[R3]
+      pcSend[0] = 0x00;
+      transceive_vector(1,1,1,0);               // send command. 
+      
+      pcSend[7] = 0x02;                         // mbed sends command.
+      pcSend[1] = 0xC4;                         // R1 <- MM[R3]
+      pcSend[0] = 0x00;
+      transceive_vector(1,1,1,0);               // send command.      
+           
+      cHData = read_register(0x02);             // obtain MM high-data-byte.
+      cLData = read_register(0x01);             // obtain MM low-data-byte.
+                
+      udMemoryContent = (cHData << 8) + cLData; // build the memory word.
+                         
+      clear_transmit_vector();                  // clear transmit vector.
+    
+      return udMemoryContent;                   // return the memory word.
+    }                                           // read_memory.
+//----------------------------------------------//------------------------------
+    void mmSPI_RTOS::step(void)                      // step the CPU.
+    {      
+      clear_transmit_vector();                  // clear transmit vector.
+      transceive_vector(0,0,1,0);               // enable CPU mode.
+      transceive_vector(0,0,0,1);               // advance CPU, clear shadow-load.
+      pcSend[7] = 0x02;                         // ready to disable CPU mode.
+      transceive_vector(0,0,1,0);               // disable CPU mode.
+    }                                           // step.
+//----------------------------------------------//------------------------------        
+    void mmSPI_RTOS::clear_transmit_vector(void)// fill transmit buffer with 0.     
+    {
+      int dLoop;
+      for (dLoop = 0; dLoop < dNumBytes; dLoop++) pcSend[dLoop] = 0x00;
+    }                                           // clear_transmit_vector.
+//----------------------------------------------//------------------------------
+                                                // getters.
+    unsigned long mmSPI_RTOS::SPIClockCount() {return ulSPIclkCount;}
+    unsigned long mmSPI_RTOS::CPUClockCount() {return ulCPUclkCount;}
+//----------------------------------------------//------------------------------
+
+
+
+
+
+
+
+
diff -r 000000000000 -r 7b8e6b90c874 mmSPI_RTOS.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmSPI_RTOS.h	Tue Sep 17 20:40:03 2013 +0000
@@ -0,0 +1,76 @@
+#ifndef mmSPI_RTOS_H                            // include guard.
+#define mmSPI_RTOS_H                            // include guard.
+/*----------------------------------------------//------------------------------
+    student   : m-moore
+    email     : gated.clock@gmail.com
+    class     : embedded RTOS
+    directory : RTOS_project/mmSPI_RTOS
+    file      : mmSPI_RTOS.h
+    date      : september 19, 2013.
+----copyright-----------------------------------//------------------------------   
+    licensed for personal and academic use.
+    commercial use of original code must be approved by the account-holder of
+    gated.clock@gmail.com
+----description---------------------------------//------------------------------  
+    this library provides the low-level SPI data and clock signaling 
+    for communication with the altera development board, via four i/o
+    pins available on the mbed development board's zigbee header.  
+----notes---------------------------------------//------------------------------ 
+------------------------------------------------//----------------------------*/
+    #include "mbed.h"                           // standard mbed.org class.
+//---defines------------------------------------//------------------------------
+    #define mmSPI_RTOS_MOSI p29                 // SPI interface pin.
+    #define mmSPI_RTOS_MISO p30                 // SPI interface pin.
+    #define mmSPI_RTOS_SCLK p9                  // SPI interface pin.
+    #define mmCPU_CLK  p10                      // soft CPU system clock.
+//==============================================//==============================
+    class mmSPI_RTOS
+    {
+      public:
+                      mmSPI_RTOS();             // constructor.
+                     ~mmSPI_RTOS();             // destructor.
+        void          allocations();            // object allocations.       
+        
+        void          setSPIfrequency (float);  // initializations.
+        void          setSendBuffer   (char * pcSendBuffer);
+        void          setReceiveBuffer(char * pcReceiveBuffer);
+        void          setNumberOfBytes(int    dNumberOfBytes);
+
+                                                // SPI transceive loop.
+        void          transceive_vector(char cPreCPU, char cPreSPI, char cScan, char cPostCPU);       
+        
+                                                // write/read CPU registers.     
+        void          write_register    (char   cRegister, char cValue);
+        void          write_IR          (char   cValueH,   char cValueL);
+        char          read_register     (char   cRegister);
+        void          read_all_registers(char * pcRegisters);
+        
+                                                // write/read CPU main-memory.
+        void          write_memory(char cHData, char cLdata, char cAddress);
+        unsigned int  read_memory (char cAddress);
+        
+        void          step();                   // step the CPU.
+        
+        void          clear_transmit_vector();  // fill with 0.
+        
+        unsigned long SPIClockCount();          // return SPI clock count.
+        unsigned long CPUClockCount();          // return CPU clock count.      
+        
+      private:
+      
+      DigitalOut  * pMOSI;                      // SPI pin.
+      DigitalOut  * pMISO;                      // SPI pin.
+      DigitalOut  * pSCLK;                      // SPI pin.
+      DigitalOut  * pCPUclk;                    // soft cpu clock.
+      char        * pcSend;                     // SPI transmit vector.
+      char        * pcReceive;                  // SPI receive  vector.
+      float         fSPIfreq;                   // SPI clock   frequency.
+      float         fSPIquarterP;               // SPI quarter period.
+      int           dNumBytes;                  // number of SPI bytes.
+      int           dLoop01;                    // loop index.
+      int           dLoop02;                    // loop index.
+      unsigned long ulSPIclkCount;              // SPI clock count.
+      unsigned long ulCPUclkCount;              // CPU clock count.
+    };
+//----------------------------------------------//------------------------------
+#endif                                          // include guard.
\ No newline at end of file