SPI library used to communicate with an altera development board attached to four zigbee-header pins.

Files at this revision

API Documentation at this revision

Comitter:
gatedClock
Date:
Sun Sep 01 02:29:08 2013 +0000
Parent:
34:d5553509f31a
Commit message:
add project code.

Changed in this revision

mmSPI.cpp Show annotated file Show diff for this revision Revisions of this file
mmSPI.h Show annotated file Show diff for this revision Revisions of this file
--- a/mmSPI.cpp	Sat Aug 31 02:19:42 2013 +0000
+++ b/mmSPI.cpp	Sun Sep 01 02:29:08 2013 +0000
@@ -2,13 +2,66 @@
     student   : m-moore
     email     : gated.clock@gmail.com
     class     : usb device drivers
-    directory : mmSPI
+    directory : USB_device_project/mmSPI
     file      : mmSPI.cpp
     date      : september 3, 2013.
-------------------------------------------------//----------------------------*/
+----copyright-----------------------------------//------------------------------   
+    licensed for personal and academic use.
+    commercial use 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.h"
-/*----------------------------------------------//------------------------------
-------------------------------------------------//----------------------------*/
 //==============================================//==============================
     mmSPI::mmSPI()                              // constructor.
     {
@@ -70,25 +123,44 @@
       dNumBytes = dNumberOfBytes;               // promote to object scope.
     }                                           // setNumberOfBytes.
 //----------------------------------------------//------------------------------
-                                                // transceive a character array.
-                                                // MSB out/in first.
-                                                // normal  inputs: [1,1,1,0]
-                                                // CPUstep input : [0,0,1,1]
+/*
+    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::transceive_vector(char cPreCPU, char cPreSPI, char cScan, char cPostCPU)
     {      
-      int  dClear;
-      int  dIndex;       
-      int  dMosiByteIndex;
-      int  dMosiBitIndex;
-      int  dMisoByteIndex;
-      int  dMisoBitIndex;
+      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.
       
-      dIndex         = (dNumBytes * 8) - 1;
-      dMosiByteIndex =  dIndex / 8;
-      dMosiBitIndex  =  dIndex % 8;
+                                                // 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.
@@ -99,7 +171,7 @@
         wait(fSPIquarterP); 
         ulCPUclkCount++;
       }                                         // if pre-CPU clock.
-      
+ //---     
       if (cPreSPI)                              // if pre-SPI pulse.
       {
         *pSCLK = 1;                             // pulse the SPI clock for parallel load.              
@@ -108,7 +180,7 @@
         *pSCLK = 0;
         ulSPIclkCount++;
       }                                         // if pre-SPI pulse.
-      
+ //---     
       if (cScan)                                // if cScan.
       {
                                                 // pre-assert MOSI.
@@ -136,7 +208,7 @@
           ulSPIclkCount++;
         }                                       // main SPI scan loop.
       }                                         // if cScan.
-      
+ //---     
       if (cPostCPU)                             // if post-CPU pulse.
       {
         *pCPUclk = 1;                           // pulse the CPU clock.
@@ -155,16 +227,30 @@
       }                                         // if post-CPU pulse.
     }                                           // transceive_vector.
 //----------------------------------------------//------------------------------
-//  cRegister  ->  CPU_register
-//      0            R0
-//      1            R1
-//      2            R2
-//      3            R3
-//      4            PC
-//      5         <meta, don't do>
-//      6         <nothing>
-//      7         <nothing>
+/*
+   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::write_register(char cRegister, char cValue)
     {     
       clear_transmit_vector();                  // clear transmit vector.
@@ -179,7 +265,16 @@
  
       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::write_IR(char cValueH, char cValueL)
     {
@@ -195,18 +290,24 @@
       clear_transmit_vector();                  // clear transmit vector.    
     }                                           // write instruction register.
 //----------------------------------------------//------------------------------
-//  cRegister  ->  CPU_register
-//      0      ->    R0
-//      1      ->    R1
-//      2      ->    R2
-//      3      ->    R3
-//      4      ->    PC
-//      5      ->    IR-H
-//      6      ->    IR-L
-//      7      ->  <never-use>
-                                                // returns the content of
-                                                // a CPU register.
-    char mmSPI::read_register(char cRegister)
+/*
+  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.
+*/
+
+    char mmSPI::read_register(char cRegister)   // return content of a CPU register.
     { 
       clear_transmit_vector();                  // clear transmit vector.
       
@@ -217,6 +318,12 @@
       return (pcReceive[6 - cRegister]);        // return the particular reg value.
     }                                           // read_register.
 //----------------------------------------------//------------------------------
+/*
+    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::write_memory(char cHData, char cLdata, char cAddress)
     {
       clear_transmit_vector();                  // clear transmit vector.
@@ -238,6 +345,12 @@
       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::read_memory(char cAddress)
     { 
--- a/mmSPI.h	Sat Aug 31 02:19:42 2013 +0000
+++ b/mmSPI.h	Sun Sep 01 02:29:08 2013 +0000
@@ -4,12 +4,18 @@
     student   : m-moore
     email     : gated.clock@gmail.com
     class     : usb device drivers
-    directory : mmSPI
+    directory : USB_device_project/mmSPI
     file      : mmSPI.h
     date      : september 3, 2013.
-----description---------------------------------//------------------------------    
+----copyright-----------------------------------//------------------------------   
+    licensed for personal and academic use.
+    commercial use 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---------------------------------------//------------------------------ 
-    1. the SPI interface pins are routed to the zigbee header.
 ------------------------------------------------//----------------------------*/
     #include "mbed.h"                           // standard mbed.org class.
 //---defines------------------------------------//------------------------------
@@ -17,9 +23,6 @@
     #define mmSPI_MISO p30                      // SPI interface pin.
     #define mmSPI_SCLK p9                       // SPI interface pin.
     #define mmCPU_CLK  p10                      // soft CPU system clock.
-/*----------------------------------------------//------------------------------  
-------------------------------------------------//----------------------------*/
-
 //==============================================//==============================
     class mmSPI
     {
@@ -38,7 +41,7 @@
         
                                                 // write/read CPU registers.     
         void          write_register(char cRegister, char cValue);
-        void          write_IR(char cValueH, char cValueL);
+        void          write_IR      (char cValueH, char cValueL);
         char          read_register (char cRegister);
         
                                                 // write/read CPU main-memory.