Mike Moore / mmSPI_RTOS

Dependents:   RTOS_project RTOS_project_fork_01 RTOS_project_fork_02

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers mmSPI_RTOS.cpp Source File

mmSPI_RTOS.cpp

00001 /*----------------------------------------------//------------------------------
00002     student   : m-moore
00003     email     : gated.clock@gmail.com
00004     class     : embedded RTOS
00005     directory : URTOS_project/mmSPI_RTOS
00006     file      : mmSPI_RTOS.cpp
00007     date      : september 19, 2013.
00008 ----copyright-----------------------------------//------------------------------   
00009     licensed for personal and academic use.
00010     commercial use of original code 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_RTOS.h"
00065 //==============================================//==============================
00066     mmSPI_RTOS::mmSPI_RTOS()                    // 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_RTOS::~mmSPI_RTOS()                   // 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_RTOS::allocations(void)          // object allocations.
00086     {
00087       pMOSI   = new DigitalOut(mmSPI_RTOS_MOSI);// SPI MOSI pin object.
00088       if (!pMOSI) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pMOSI. \n\r"); 
00089       
00090       pMISO   = new DigitalOut(mmSPI_RTOS_MISO);// SPI MISO pin object.
00091       if (!pMISO) error("\n\r mmSPI_RTOS::allocations : FATAL malloc error for pMISO. \n\r"); 
00092     
00093       pSCLK   = new DigitalOut(mmSPI_RTOS_SCLK);// SPI SCLK pin object.
00094       if (!pSCLK) error("\n\r mmSPI_RTOS::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_RTOS::allocations : FATAL malloc error for pCPUclk. \n\r");
00098     }                                           // allocations.
00099 //----------------------------------------------//------------------------------    
00100                                                 // set SPI clock frequency.
00101     void mmSPI_RTOS::setSPIfrequency(float fFreq)    
00102     {
00103       fSPIfreq = fFreq;                         // promote to object scope.
00104       if (fSPIfreq < .05)                       // don't get near divide-by-zero.
00105       error("\n\r mmSPI_RTOS::setSPIfrequency : FATAL SPI frequency set too low. \n\r"); 
00106       fSPIquarterP = (1 / fSPIfreq) / 4;        // figure quarter-cycle period.
00107     }
00108 //----------------------------------------------//------------------------------  
00109                                                 // obtain SPI send buffer pointer.  
00110     void mmSPI_RTOS::setSendBuffer(char * pcSendBuffer)
00111     {
00112       pcSend = pcSendBuffer;                    // promote to object scope.
00113     }                                           // setSendBuffer.
00114 //----------------------------------------------//------------------------------    
00115                                                 // obtain SPI receive buffer pointer.  
00116     void mmSPI_RTOS::setReceiveBuffer(char * pcReceiveBuffer)
00117     {
00118       pcReceive = pcReceiveBuffer;              // promote to object scope.
00119     }                                           // setReceiveBuffer.
00120 //----------------------------------------------//------------------------------
00121                                                 // obtain number of SPI bytes.
00122     void mmSPI_RTOS::setNumberOfBytes(int dNumberOfBytes)
00123     {
00124       dNumBytes = dNumberOfBytes;               // promote to object scope.
00125     }                                           // setNumberOfBytes.
00126 //----------------------------------------------//------------------------------
00127 /*
00128     this method has four 'components' which may be switched on/off by the caller:
00129     1. cPreCPU  = 1 means cycle the altera CPU clock once.
00130     2. cPreSPI  = 1 means cycle the        SPI clock once.
00131     3. cScan    = 1 means run a full SPI scan-in/scan-out cycle.
00132     4. cPostCPU = 1 means cycle the alter CPU clock once, then the SPI clock once.
00133     
00134     in the altera, upon the falling edge of any CPU clock, a load-enable is
00135     asserted into the scan registers, such that at the next rising edge of
00136     the SPI clock, the scan registers perform a parallel-load of the CPU
00137     registers, so that that data can subsequently be scanned back into the mbed
00138     via a full SPI scan-in cycle.  (this load-enable is turned-off upon the first
00139     falling edge of the subsequent SPI clock.)
00140     therefore, the regular input switches to this method are (1,1,1,0).  
00141     this means that the altera CPU clock will cycle,
00142     and the result of that CPU operation will be parallel-loaded into the
00143     shadow registers for scan into the mbed on the subsequent full-scan cycle.
00144     
00145     a finer level of sequencing these 'components' is needed by the 'step' method,
00146     which is why they are under four-input-parameter control.                                                
00147  */
00148     void mmSPI_RTOS::transceive_vector(char cPreCPU, char cPreSPI, char cScan, char cPostCPU)
00149     {      
00150       int  dClear;                              // clear-loop index.
00151       int  dIndex;                              // total scan index.
00152       int  dMosiByteIndex;                      // SPI-MOSI byte index.
00153       int  dMosiBitIndex;                       // SPI-MOSI bit  index.
00154       int  dMisoByteIndex;                      // SPI-MISO byte index.
00155       int  dMisoBitIndex;                       // SPI-MISO bit  index.
00156       
00157                                                 // initializations.  these are needed.
00158       dIndex         = (dNumBytes * 8) - 1;     // calculate scan count.
00159       dMosiByteIndex =  dIndex / 8;             // calculate current byte index.
00160       dMosiBitIndex  =  dIndex % 8;             // calculate current bit  iindex.
00161       
00162                                                 // clear the SPI receive vector.
00163       for (dClear = 0; dClear < dNumBytes; dClear++) pcReceive[dClear] = 0;
00164 //---
00165       if (cPreCPU)                              // if pre-CPU clock.
00166       {
00167         *pCPUclk = 1;                           // pulse the CPU clock.
00168         wait(fSPIquarterP); 
00169         wait(fSPIquarterP);     
00170         *pCPUclk = 0;  
00171         wait(fSPIquarterP); 
00172         wait(fSPIquarterP); 
00173         ulCPUclkCount++;
00174       }                                         // if pre-CPU clock.
00175  //---     
00176       if (cPreSPI)                              // if pre-SPI pulse.
00177       {
00178         *pSCLK = 1;                             // pulse the SPI clock for parallel load.              
00179         wait(fSPIquarterP); 
00180         wait(fSPIquarterP); 
00181         *pSCLK = 0;
00182         ulSPIclkCount++;
00183       }                                         // if pre-SPI pulse.
00184  //---     
00185       if (cScan)                                // if cScan.
00186       {
00187                                                 // pre-assert MOSI.
00188         *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1;
00189         wait(fSPIquarterP); 
00190         wait(fSPIquarterP); 
00191       
00192       
00193                                                 // main SPI scan loop.
00194         for (dIndex = (dNumBytes * 8) - 1; dIndex >= 0; dIndex--)
00195         {
00196           dMisoByteIndex = dIndex / 8;
00197           dMisoBitIndex  = dIndex % 8;
00198           pcReceive[dMisoByteIndex] = pcReceive[dMisoByteIndex] | (*pMISO << dMisoBitIndex);      
00199           *pSCLK = 1;             
00200           wait(fSPIquarterP); 
00201           wait(fSPIquarterP); 
00202           *pSCLK = 0;    
00203           if (dIndex < 0) dIndex = 0;
00204           dMosiByteIndex = (dIndex - 1) / 8;
00205           dMosiBitIndex  = (dIndex - 1) % 8;
00206           *pMOSI = ((pcSend[dMosiByteIndex]) >> dMosiBitIndex) & 1;
00207           wait(fSPIquarterP); 
00208           wait(fSPIquarterP);
00209           ulSPIclkCount++;
00210         }                                       // main SPI scan loop.
00211       }                                         // if cScan.
00212  //---     
00213       if (cPostCPU)                             // if post-CPU pulse.
00214       {
00215         *pCPUclk = 1;                           // pulse the CPU clock.
00216         wait(fSPIquarterP); 
00217         wait(fSPIquarterP);     
00218         *pCPUclk = 0;  
00219         wait(fSPIquarterP); 
00220         wait(fSPIquarterP);   
00221         ulCPUclkCount++;    
00222         
00223         *pSCLK = 1;                             // clear-out the SPI parallel-load enable.        
00224         wait(fSPIquarterP); 
00225         wait(fSPIquarterP); 
00226         *pSCLK = 0;        
00227         ulSPIclkCount++;
00228       }                                         // if post-CPU pulse.
00229     }                                           // transceive_vector.
00230 //----------------------------------------------//------------------------------
00231 /*
00232    cRegister  ->  CPU_register  
00233       0            R0
00234       1            R1
00235       2            R2
00236       3            R3
00237       4            PC
00238       5         <use write_IR instead>
00239       6         <nothing>
00240       7         <nothing>
00241       
00242     in order to write to a CPU register, pcSend[7] is set to 0x02,
00243     which informs the CPU instruction decoder to decode what we are
00244     here scanning into the shadow instruction-register, rather than
00245     the CPU instruction decoder decoding the regular instruction-register.
00246     here, we set-up pcSend to scan the instruction 
00247     'load Rx with immediate data yy', and this is executed on the
00248     subsequent CPU clock.  
00249     
00250     said another way, this method writes a value into a CPU register
00251     by effectively taking over the CPU and doing a write-register-immediate.
00252 */
00253 
00254                                                 // write to a CPU register.
00255     void mmSPI_RTOS::write_register(char cRegister, char cValue)
00256     {     
00257       clear_transmit_vector();                  // clear transmit vector.
00258       
00259       pcSend[7] = 0x02;                         // mbed sends a command.
00260       
00261                                                 // align into instruction word.
00262       pcSend[1] = ((cRegister & 0x07) << 2) | 0xA0;
00263       pcSend[0] = cValue & 0xFF;                // immediate value to i.w.
00264  
00265       transceive_vector(1,1,1,0);               // transmit command.
00266  
00267       clear_transmit_vector();                  // clear transmit vector.
00268     }                                           // write_register.
00269 //----------------------------------------------//------------------------------  
00270 /*  
00271     for writing to the instruction-register, this method is used.
00272     for reading from the instruction-register, the 'regular' 
00273     read_register method is used, once for each of the two IR data bytes.
00274     
00275     unlike method 'write_register', this method works by gating-off the
00276     outputs of the CPU instruction decoder, and having the instruction-register
00277     content loaded in from the shadow instruction-register.
00278 */
00279                                                 // write instruction register.
00280     void mmSPI_RTOS::write_IR(char cValueH, char cValueL)
00281     {
00282       clear_transmit_vector();                  // clear transmit vector.
00283       
00284       pcSend[7] = 0x06;                         // mbed sends a command.
00285       
00286       pcSend[1] = (cValueH & 0xFF);             // load IR shadow with new IR content.
00287       pcSend[0] = (cValueL & 0xFF);
00288  
00289       transceive_vector(1,1,1,0);               // transmit command.
00290  
00291       clear_transmit_vector();                  // clear transmit vector.    
00292     }                                           // write instruction register.
00293 //----------------------------------------------//------------------------------
00294 /*
00295   cRegister  ->  CPU_register   pcReceive
00296       0      ->    R0             [6]
00297       1      ->    R1             [5]
00298       2      ->    R2             [4]
00299       3      ->    R3             [3]
00300       4      ->    PC             [2]
00301       5      ->    IR-H           [1]
00302       6      ->    IR-L           [0]
00303       7      ->  <never-use>
00304       
00305     this method works by taking over the instruction decoder as described above,
00306     but not issuing any instruction, but the scan registers will parallel-load
00307     all of the CPU register data and scan them into pcReceive, where they are
00308     picked up below.
00309 */
00310                                                 // return content of a CPU register.
00311     char mmSPI_RTOS::read_register(char cRegister)   
00312     { 
00313       clear_transmit_vector();                  // clear transmit vector.
00314       
00315       pcSend[7] = 0x02;                         // suppress cpu operation.
00316 
00317       transceive_vector(1,1,1,0);               // snap & scan-out reg contents.
00318       
00319       return (pcReceive[6 - cRegister]);        // return the particular reg value.
00320     }                                           // read_register.
00321 //----------------------------------------------//------------------------------
00322 //----------------------------------------------//------------------------------
00323 /*
00324   since the Tk UI often wants to obtain the content of all of the CPU registers,
00325   this method may be used to obtain all of them at once, speading up the UI 
00326   read cycle.
00327       
00328   pRegisters[6] <- pcReceive[6] = R0    
00329   pRegisters[5] <- pcReceive[5] = R1
00330   pRegisters[4] <- pcReceive[4] = R2
00331   pRegisters[3] <- pcReceive[3] = R3
00332   pRegisters[2] <- pcReceive[2] = PC
00333   pRegisters[1] <- pcReceive[1] = IR-H
00334   pRegisters[0] <- pcReceive[0] = IR-L  
00335 */
00336 
00337     void mmSPI_RTOS::read_all_registers(char * pcRegisters) 
00338     { 
00339       int dLoop;                                // loop index.
00340       
00341       clear_transmit_vector();                  // clear transmit vector.
00342       
00343       pcSend[7] = 0x02;                         // suppress cpu operation.
00344 
00345       transceive_vector(1,1,1,0);               // snap & scan-out reg contents.
00346       
00347                                                 // propagate register data.
00348       for (dLoop = 0; dLoop < 7; dLoop++) pcRegisters[dLoop] = pcReceive[dLoop];
00349       
00350     }                                           // read_all_registers.
00351 //----------------------------------------------//------------------------------
00352 /*
00353     this method works by taking over the instruction decoder, and issuing
00354     the CPU commands which load the MM address/data into {R3,R2,R1}
00355     followed by issuing a write-pulse.
00356 */
00357                                                 // write a word to main-memory.
00358     void mmSPI_RTOS::write_memory(char cHData, char cLdata, char cAddress)
00359     {
00360       clear_transmit_vector();                  // clear transmit vector.
00361                     
00362       write_register(0x03,cAddress);            // R3 <- address.
00363       write_register(0x02,cHData);              // R2 <- high-data.
00364       write_register(0x01,cLdata);              // R1 <- low-data.
00365   
00366       pcSend[7] = 0x02;                         // mbed sends command.
00367       pcSend[1] = 0x02;                         // write-enable high.
00368       pcSend[0] = 0x00;                         // remainder of instruction.
00369       transceive_vector(1,1,1,0);
00370       
00371       pcSend[7] = 0x02;                         // mbed sends command.
00372       pcSend[1] = 0x00;                         // write-enable low.
00373       pcSend[0] = 0x00;                         // remainder of instruction.
00374       transceive_vector(1,1,1,0);   
00375                            
00376       clear_transmit_vector();                  // clear transmit vector. 
00377     }                                           // write_memory.
00378 //----------------------------------------------//------------------------------
00379 /*
00380     this command works by taking over the instruction decoder, loading
00381     R3 with the MM address, then issuing load-from-MM commands so that
00382     R2,R1 are loaded with MM[R3], and then reading the result from
00383     R1,R2.
00384 */
00385                                                 // fetch a word from main memory.
00386     unsigned int mmSPI_RTOS::read_memory(char cAddress)
00387     { 
00388       unsigned int udMemoryContent;             // return variable.
00389       char         cHData;                      // returned data-high.
00390       char         cLData;                      // returned data-low.
00391                                
00392       clear_transmit_vector();                  // clear transmit vector.
00393             
00394       write_register(0x03,cAddress);            // R3 <= address.
00395       
00396       pcSend[7] = 0x02;                         // mbed sends command.
00397       pcSend[1] = 0xC8;                         // R2 <- MM[R3]
00398       pcSend[0] = 0x00;
00399       transceive_vector(1,1,1,0);               // send command. 
00400       
00401       pcSend[7] = 0x02;                         // mbed sends command.
00402       pcSend[1] = 0xC4;                         // R1 <- MM[R3]
00403       pcSend[0] = 0x00;
00404       transceive_vector(1,1,1,0);               // send command.      
00405            
00406       cHData = read_register(0x02);             // obtain MM high-data-byte.
00407       cLData = read_register(0x01);             // obtain MM low-data-byte.
00408                 
00409       udMemoryContent = (cHData << 8) + cLData; // build the memory word.
00410                          
00411       clear_transmit_vector();                  // clear transmit vector.
00412     
00413       return udMemoryContent;                   // return the memory word.
00414     }                                           // read_memory.
00415 //----------------------------------------------//------------------------------
00416     void mmSPI_RTOS::step(void)                      // step the CPU.
00417     {      
00418       clear_transmit_vector();                  // clear transmit vector.
00419       transceive_vector(0,0,1,0);               // enable CPU mode.
00420       transceive_vector(0,0,0,1);               // advance CPU, clear shadow-load.
00421       pcSend[7] = 0x02;                         // ready to disable CPU mode.
00422       transceive_vector(0,0,1,0);               // disable CPU mode.
00423     }                                           // step.
00424 //----------------------------------------------//------------------------------        
00425     void mmSPI_RTOS::clear_transmit_vector(void)// fill transmit buffer with 0.     
00426     {
00427       int dLoop;
00428       for (dLoop = 0; dLoop < dNumBytes; dLoop++) pcSend[dLoop] = 0x00;
00429     }                                           // clear_transmit_vector.
00430 //----------------------------------------------//------------------------------
00431                                                 // getters.
00432     unsigned long mmSPI_RTOS::SPIClockCount() {return ulSPIclkCount;}
00433     unsigned long mmSPI_RTOS::CPUClockCount() {return ulCPUclkCount;}
00434 //----------------------------------------------//------------------------------
00435 
00436 
00437 
00438 
00439 
00440 
00441 
00442