SPI library used to communicate with an altera development board attached to four zigbee-header pins.
Revision 35:6152c9709697, committed 2013-09-01
- 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.