Mike Moore / mmSPI
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers mmSPI.cpp Source File

mmSPI.cpp

00001 /*----------------------------------------------//------------------------------
00002     student   : m-moore
00003     email     : gated.clock@gmail.com
00004     class     : usb device drivers
00005     directory : USB_device_project/mmSPI
00006     file      : mmSPI.cpp
00007     date      : september 3, 2013.
00008 ----copyright-----------------------------------//------------------------------   
00009     licensed for personal and academic use.
00010     commercial use must be approved by the account-holder of
00011     gated.clock@gmail.com
00012 ----description---------------------------------//------------------------------
00013     this library provides the low-level SPI data and clock signaling 
00014     for communication with the altera development board, via four i/o
00015     pins available on the mbed development board's zigbee header.
00016     
00017     this library also provides higher-level calls to send/receive register
00018     and main-memory data to/from the CPU implemented in the altera.
00019     
00020     the SPI clock and the CPU clock are both built by this library.
00021     
00022     the SPI clock (*pSCLK) is generated with quarter-phase resolution,
00023     although at the moment, only half-phase resolution is being used.
00024     
00025     within the FPGA, the SPI scan registers are called 'shadow registers'
00026     because they parallel load-to/store-from the CPU registers.  
00027     
00028     character vectors pcSend and pcReceive are used to scan-in and scan-out
00029     to/from the altera shadow registers.  note that the prefix 'pc' means
00030     'pointer-of-type-character' and not 'personal-computer'.
00031     
00032     the shadow registers in the altera are arranged as follows:
00033     
00034  <-- MISO (altera-out to mbed-in)
00035      ^
00036      |
00037    scan_08  U14_spi_control = pcSend/pcReceive[7]
00038    scan_08  U08_shadowR0    = pcSend/pcReceive[6]
00039    scan_08  U09_shadowR1    = pcSend/pcReceive[5]
00040    scan_08  U10_shadowR2    = pcSend/pcReceive[4]
00041    scan_08  U11_shadowR3    = pcSend/pcReceive[3]
00042    scan_08  U12_shadowPC    = pcSend/pcReceive[2]
00043    scan_16  U13_shadowIR    = pcSend/pcReceive[1:0]
00044      ^
00045      |
00046  --> MOSI (altera-in from mbed-out)
00047  
00048     when U14_spi_control<1> is high, the mbed takes over CPU operation.
00049     this means that the CPU instruction decoder decodes U13_shadowIR
00050     rather than the CPU's actual IR.
00051     
00052     when U14_spi_control<2> is high, the mbed writes into the IR.
00053     this means that the CPU instruction decoder load-enable outputs
00054     are gated-off so that the mbed may write into the instrucition
00055     register without immediate consequence in the CPU.  writing to
00056     the IR this way is more for the ability to show that it can be done,
00057     rather than any likely useful purpose.
00058     
00059     debug/test:
00060     this library was debugged by using the altera FPGA logic analyzer
00061     known as 'signal tap'.  the main program uses all of the memory and
00062     register access calls, which were used as an informal unit test.
00063 -----includes-----------------------------------//----------------------------*/
00064     #include "mmSPI.h"
00065 //==============================================//==============================
00066     mmSPI::mmSPI()                              // constructor.
00067     {
00068       allocations();                            // object allocations.
00069       
00070       *pSCLK        = 0;                        // initialize.
00071       *pCPUclk      = 0;                        // initialize.
00072       ulSPIclkCount = 0;                        // initialize.
00073       ulCPUclkCount = 0;                        // initialize.
00074     }                                           // constructor.
00075 //----------------------------------------------//------------------------------    
00076     mmSPI::~mmSPI()                             // destructor.
00077     {
00078                                                 // deallocations.
00079       if (pMOSI)   {delete pMOSI;   pMOSI   = NULL;}
00080       if (pMISO)   {delete pMISO;   pMISO   = NULL;}
00081       if (pSCLK)   {delete pSCLK;   pSCLK   = NULL;}
00082       if (pCPUclk) {delete pCPUclk; pCPUclk = NULL;}
00083     }                                           // destructor.
00084 //----------------------------------------------//------------------------------
00085     void mmSPI::allocations(void)               // object allocations.
00086     {
00087       pMOSI   = new DigitalOut(mmSPI_MOSI);     // SPI MOSI pin object.
00088       if (!pMOSI) error("\n\r mmSPI::allocations : FATAL malloc error for pMOSI. \n\r"); 
00089       
00090       pMISO   = new DigitalOut(mmSPI_MISO);     // SPI MISO pin object.
00091       if (!pMISO) error("\n\r mmSPI::allocations : FATAL malloc error for pMISO. \n\r"); 
00092     
00093       pSCLK   = new DigitalOut(mmSPI_SCLK);     // SPI SCLK pin object.
00094       if (!pSCLK) error("\n\r mmSPI::allocations : FATAL malloc error for pSCLK. \n\r"); 
00095       
00096       pCPUclk = new DigitalOut(mmCPU_CLK);      // SPI SCLK pin object.
00097       if (!pCPUclk) error("\n\r mmSPI::allocations : FATAL malloc error for pCPUclk. \n\r");
00098     }                                           // allocations.
00099 //----------------------------------------------//------------------------------    
00100     void mmSPI::setSPIfrequency(float fFreq)    // set SPI clock frequency.
00101     {
00102       fSPIfreq = fFreq;                         // promote to object scope.
00103       if (fSPIfreq < .05)                       // don't get near divide-by-zero.
00104       error("\n\r mmSPI::setSPIfrequency : FATAL SPI frequency set too low. \n\r"); 
00105       fSPIquarterP = (1 / fSPIfreq) / 4;        // figure quarter-cycle period.
00106     }
00107 //----------------------------------------------//------------------------------  
00108                                                 // obtain SPI send buffer pointer.  
00109     void mmSPI::setSendBuffer(char * pcSendBuffer)
00110     {
00111       pcSend = pcSendBuffer;                    // promote to object scope.
00112     }                                           // setSendBuffer.
00113 //----------------------------------------------//------------------------------    
00114                                                 // obtain SPI receive buffer pointer.  
00115     void mmSPI::setReceiveBuffer(char * pcReceiveBuffer)
00116     {
00117       pcReceive = pcReceiveBuffer;              // promote to object scope.
00118     }                                           // setReceiveBuffer.
00119 //----------------------------------------------//------------------------------
00120                                                 // obtain number of SPI bytes.
00121     void mmSPI::setNumberOfBytes(int dNumberOfBytes)
00122     {
00123       dNumBytes = dNumberOfBytes;               // promote to object scope.
00124     }                                           // setNumberOfBytes.
00125 //----------------------------------------------//------------------------------
00126 /*
00127     this method has four 'components' which may be switched on/off by the caller:
00128     1. cPreCPU  = 1 means cycle the altera CPU clock once.
00129     2. cPreSPI  = 1 means cycle the        SPI clock once.
00130     3. cScan    = 1 means run a full SPI scan-in/scan-out cycle.
00131     4. cPostCPU = 1 means cycle the alter CPU clock once, then the SPI clock once.
00132     
00133     in the altera, upon the falling edge of any CPU clock, a load-enable is
00134     asserted into the scan registers, such that at the next rising edge of
00135     the SPI clock, the scan registers perform a parallel-load of the CPU
00136     registers, so that that data can subsequently be scanned back into the mbed
00137     via a full SPI scan-in cycle.  (this load-enable is turned-off upon the first
00138     falling edge of the subsequent SPI clock.)
00139     therefore, the regular input switches to this method are (1,1,1,0).  
00140     this means that the altera CPU clock will cycle,
00141     and the result of that CPU operation will be parallel-loaded into the
00142     shadow registers for scan into the mbed on the subsequent full-scan cycle.
00143     
00144     a finer level of sequencing these 'components' is needed by the 'step' method,
00145     which is why they are under four-input-parameter control.                                                
00146  */
00147     void mmSPI::transceive_vector(char cPreCPU, char cPreSPI, char cScan, char cPostCPU)
00148     {      
00149       int  dClear;                              // clear-loop index.
00150       int  dIndex;                              // total scan index.
00151       int  dMosiByteIndex;                      // SPI-MOSI byte index.
00152       int  dMosiBitIndex;                       // SPI-MOSI bit  index.
00153       int  dMisoByteIndex;                      // SPI-MISO byte index.
00154       int  dMisoBitIndex;                       // SPI-MISO bit  index.
00155       
00156                                                 // initializations.  these are needed.
00157       dIndex         = (dNumBytes * 8) - 1;     // calculate scan count.
00158       dMosiByteIndex =  dIndex / 8;             // calculate current byte index.
00159       dMosiBitIndex  =  dIndex % 8;             // calculate current bit  iindex.
00160       
00161                                                 // clear the SPI receive vector.
00162       for (dClear = 0; dClear < dNumBytes; dClear++) pcReceive[dClear] = 0;
00163 //---
00164       if (cPreCPU)                              // if pre-CPU clock.
00165       {
00166         *pCPUclk = 1;                           // pulse the CPU clock.
00167         wait(fSPIquarterP); 
00168         wait(fSPIquarterP);     
00169         *pCPUclk = 0;  
00170         wait(fSPIquarterP); 
00171         wait(fSPIquarterP); 
00172         ulCPUclkCount++;
00173       }                                         // if pre-CPU clock.
00174  //---     
00175       if (cPreSPI)                              // if pre-SPI pulse.
00176       {
00177         *pSCLK = 1;                             // pulse the SPI clock for parallel load.              
00178         wait(fSPIquarterP); 
00179         wait(fSPIquarterP); 
00180         *pSCLK = 0;
00181         ulSPIclkCount++;
00182       }                                         // if pre-SPI pulse.
00183  //---     
00184       if (cScan)                                // if cScan.
00185       {
00186                                                 // pre-assert MOSI.
00187         *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1;
00188         wait(fSPIquarterP); 
00189         wait(fSPIquarterP); 
00190       
00191       
00192                                                 // main SPI scan loop.
00193         for (dIndex = (dNumBytes * 8) - 1; dIndex >= 0; dIndex--)
00194         {
00195           dMisoByteIndex = dIndex / 8;
00196           dMisoBitIndex  = dIndex % 8;
00197           pcReceive[dMisoByteIndex] = pcReceive[dMisoByteIndex] | (*pMISO << dMisoBitIndex);      
00198           *pSCLK = 1;             
00199           wait(fSPIquarterP); 
00200           wait(fSPIquarterP); 
00201           *pSCLK = 0;    
00202           if (dIndex < 0) dIndex = 0;
00203           dMosiByteIndex = (dIndex - 1) / 8;
00204           dMosiBitIndex  = (dIndex - 1) % 8;
00205           *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1;
00206           wait(fSPIquarterP); 
00207           wait(fSPIquarterP);
00208           ulSPIclkCount++;
00209         }                                       // main SPI scan loop.
00210       }                                         // if cScan.
00211  //---     
00212       if (cPostCPU)                             // if post-CPU pulse.
00213       {
00214         *pCPUclk = 1;                           // pulse the CPU clock.
00215         wait(fSPIquarterP); 
00216         wait(fSPIquarterP);     
00217         *pCPUclk = 0;  
00218         wait(fSPIquarterP); 
00219         wait(fSPIquarterP);   
00220         ulCPUclkCount++;    
00221         
00222         *pSCLK = 1;                             // clear-out the SPI parallel-load enable.        
00223         wait(fSPIquarterP); 
00224         wait(fSPIquarterP); 
00225         *pSCLK = 0;        
00226         ulSPIclkCount++;
00227       }                                         // if post-CPU pulse.
00228     }                                           // transceive_vector.
00229 //----------------------------------------------//------------------------------
00230 /*
00231    cRegister  ->  CPU_register  
00232       0            R0
00233       1            R1
00234       2            R2
00235       3            R3
00236       4            PC
00237       5         <use write_IR instead>
00238       6         <nothing>
00239       7         <nothing>
00240       
00241     in order to write to a CPU register, pcSend[7] is set to 0x02,
00242     which informs the CPU instruction decoder to decode what we are
00243     here scanning into the shadow instruction-register, rather than
00244     the CPU instruction decoder decoding the regular instruction-register.
00245     here, we set-up pcSend to scan the instruction 
00246     'load Rx with immediate data yy', and this is executed on the
00247     subsequent CPU clock.  
00248     
00249     said another way, this method writes a value into a CPU register
00250     by effectively taking over the CPU and doing a write-register-immediate.
00251 */
00252 
00253                                                 // write to a CPU register.
00254     void mmSPI::write_register(char cRegister, char cValue)
00255     {     
00256       clear_transmit_vector();                  // clear transmit vector.
00257       
00258       pcSend[7] = 0x02;                         // mbed sends a command.
00259       
00260                                                 // align into instruction word.
00261       pcSend[1] = ((cRegister & 0x07) << 2) | 0xA0;
00262       pcSend[0] = cValue & 0xFF;                // immediate value to i.w.
00263  
00264       transceive_vector(1,1,1,0);               // transmit command.
00265  
00266       clear_transmit_vector();                  // clear transmit vector.
00267     }                                           // write_register.
00268 //----------------------------------------------//------------------------------  
00269 /*  
00270     for writing to the instruction-register, this method is used.
00271     for reading from the instruction-register, the 'regular' 
00272     read_register method is used, once for each of the two IR data bytes.
00273     
00274     unlike method 'write_register', this method works by gating-off the
00275     outputs of the CPU instruction decoder, and having the instruction-register
00276     content loaded in from the shadow instruction-register.
00277 */
00278                                                 // write instruction register.
00279     void mmSPI::write_IR(char cValueH, char cValueL)
00280     {
00281       clear_transmit_vector();                  // clear transmit vector.
00282       
00283       pcSend[7] = 0x06;                         // mbed sends a command.
00284       
00285       pcSend[1] = (cValueH & 0xFF);             // load IR shadow with new IR content.
00286       pcSend[0] = (cValueL & 0xFF);
00287  
00288       transceive_vector(1,1,1,0);               // transmit command.
00289  
00290       clear_transmit_vector();                  // clear transmit vector.    
00291     }                                           // write instruction register.
00292 //----------------------------------------------//------------------------------
00293 /*
00294   cRegister  ->  CPU_register   pcReceive
00295       0      ->    R0             [6]
00296       1      ->    R1             [5]
00297       2      ->    R2             [4]
00298       3      ->    R3             [3]
00299       4      ->    PC             [2]
00300       5      ->    IR-H           [1]
00301       6      ->    IR-L           [0]
00302       7      ->  <never-use>
00303       
00304     this method works by taking over the instruction decoder as described above,
00305     but not issuing any instruction, but the scan registers will parallel-load
00306     all of the CPU register data and scan them into pcReceive, where they are
00307     picked up below.
00308 */
00309 
00310     char mmSPI::read_register(char cRegister)   // return content of a CPU register.
00311     { 
00312       clear_transmit_vector();                  // clear transmit vector.
00313       
00314       pcSend[7] = 0x02;                         // suppress cpu operation.
00315 
00316       transceive_vector(1,1,1,0);               // snap & scan-out reg contents.
00317       
00318       return (pcReceive[6 - cRegister]);        // return the particular reg value.
00319     }                                           // read_register.
00320 //----------------------------------------------//------------------------------
00321 /*
00322     this method works by taking over the instruction decoder, and issuing
00323     the CPU commands which load the MM address/data into {R3,R2,R1}
00324     followed by issuing a write-pulse.
00325 */
00326                                                 // write a word to main-memory.
00327     void mmSPI::write_memory(char cHData, char cLdata, char cAddress)
00328     {
00329       clear_transmit_vector();                  // clear transmit vector.
00330                     
00331       write_register(0x03,cAddress);            // R3 <- address.
00332       write_register(0x02,cHData);              // R2 <- high-data.
00333       write_register(0x01,cLdata);              // R1 <- low-data.
00334   
00335       pcSend[7] = 0x02;                         // mbed sends command.
00336       pcSend[1] = 0x02;                         // write-enable high.
00337       pcSend[0] = 0x00;                         // remainder of instruction.
00338       transceive_vector(1,1,1,0);
00339       
00340       pcSend[7] = 0x02;                         // mbed sends command.
00341       pcSend[1] = 0x00;                         // write-enable low.
00342       pcSend[0] = 0x00;                         // remainder of instruction.
00343       transceive_vector(1,1,1,0);   
00344                            
00345       clear_transmit_vector();                  // clear transmit vector. 
00346     }                                           // write_memory.
00347 //----------------------------------------------//------------------------------
00348 /*
00349     this command works by taking over the instruction decoder, loading
00350     R3 with the MM address, then issuing load-from-MM commands so that
00351     R2,R1 are loaded with MM[R3], and then reading the result from
00352     R1,R2.
00353 */
00354                                                 // fetch a word from main memory.
00355     unsigned int mmSPI::read_memory(char cAddress)
00356     { 
00357       unsigned int udMemoryContent;             // return variable.
00358       char         cHData;                      // returned data-high.
00359       char         cLData;                      // returned data-low.
00360                                
00361       clear_transmit_vector();                  // clear transmit vector.
00362             
00363       write_register(0x03,cAddress);            // R3 <= address.
00364       
00365       pcSend[7] = 0x02;                         // mbed sends command.
00366       pcSend[1] = 0xC8;                         // R2 <- MM[R3]
00367       pcSend[0] = 0x00;
00368       transceive_vector(1,1,1,0);               // send command. 
00369       
00370       pcSend[7] = 0x02;                         // mbed sends command.
00371       pcSend[1] = 0xC4;                         // R1 <- MM[R3]
00372       pcSend[0] = 0x00;
00373       transceive_vector(1,1,1,0);               // send command.      
00374            
00375       cHData = read_register(0x02);             // obtain MM high-data-byte.
00376       cLData = read_register(0x01);             // obtain MM low-data-byte.
00377                 
00378       udMemoryContent = (cHData << 8) + cLData; // build the memory word.
00379                          
00380       clear_transmit_vector();                  // clear transmit vector.
00381     
00382       return udMemoryContent;                   // return the memory word.
00383     }                                           // read_memory.
00384 //----------------------------------------------//------------------------------
00385     void mmSPI::step(void)                      // step the CPU.
00386     {      
00387       clear_transmit_vector();                  // clear transmit vector.
00388       transceive_vector(0,0,1,0);               // enable CPU mode.
00389       transceive_vector(0,0,0,1);               // advance CPU, clear shadow-load.
00390       pcSend[7] = 0x02;                         // ready to disable CPU mode.
00391       transceive_vector(0,0,1,0);               // disable CPU mode.
00392     }                                           // step.
00393 //----------------------------------------------//------------------------------
00394     void mmSPI::clear_transmit_vector(void)     // fill transmit buffer with 0.
00395     {
00396       int dLoop;
00397       for (dLoop = 0; dLoop < dNumBytes; dLoop++) pcSend[dLoop] = 0x00;
00398     }                                           // clear_transmit_vector.
00399 //----------------------------------------------//------------------------------
00400                                                 // getters.
00401     unsigned long mmSPI::SPIClockCount() {return ulSPIclkCount;}
00402     unsigned long mmSPI::CPUClockCount() {return ulCPUclkCount;}
00403 //----------------------------------------------//------------------------------
00404 
00405 
00406 
00407 
00408 
00409 
00410 
00411