#include "proto.h" #include #include /******************************************************************************************* Protocol analyzer for Serial Peripheral Interface (SPI) 04.10.2005, HLG 23.10.2005, HLG, enhanced: variable frame length, bit order, inversion mask 11.10.2008, HLG, enhanced: maximum data size per packet increased from 32 to 128 bits (c) Lauterbach GmbH *******************************************************************************************/ #define MAXDWORDS 4 typedef struct { int cpol; // SPI clock polarity int cpha; // SPI clock phase int invert; // inversion mask (default: invert=0) int framesize; // bits per transfer (default: framesize=8) int msbf; // Most Significant Bit comes First (default: mbsf=1) } param; typedef struct { int sck; // SPI Clock int mosi; // SPI Data from master to slave int miso; // SPI Data to master from slave int cs; // SPI Chip select / data strobe } signals; typedef struct { protoTime timestamp; protoWord32 bitsMOSI[MAXDWORDS]; protoWord32 bitsMISO[MAXDWORDS]; int framesize; } data; static void inversebits(signals* sig, int invert) // invert signals coresponding to mask { sig->sck ^= !!(invert&0x08); sig->mosi ^= !!(invert&0x04); sig->miso ^= !!(invert&0x02); sig->cs ^= !!(invert&0x01); } static protoSizeT PROTOAPI process1(protoContext context, protoPtr arrayOut, protoSizeT arrayOutSize, protoPtr arrayIn, protoSizeT arrayInSize, protoPtr localdata) { int i; protoTime time; // incoming timestamp signals traceBits; // incoming signal lines signals traceBitsOld; // previous icoming signla lines (for edge detection) data *dataOut; // outgoing data structure which is generated by this function (process1) param *parameters; // global parameters int sample = 0; // number of received data samples protoSizeT index = 0; // number of detected data bytes int size = 0; // counts received bits (counting down) protoWord32 *bitsMOSI; protoWord32 *bitsMISO; dataOut = (data*) arrayOut; parameters= (param*) localdata; bitsMOSI = dataOut[index].bitsMOSI; bitsMISO = dataOut[index].bitsMISO; for( i=0; icpha == 0 ) { parameters->framesize = 0; do { if( (sample & 0xfff) == 0 ) // give GUI a chance to stop us if( PROTO_Cancel(context) ) return PROTO_PROCESS_CANCEL; PROTO_ReadTrace( context, sample, &time, (int*) &traceBits ); sample++; inversebits(&traceBits, parameters->invert); } while( traceBits.cs == 0 && sample < arrayInSize ); } else { PROTO_ReadTrace( context, sample, &time, (int*) &traceBits ); sample++; inversebits(&traceBits, parameters->invert); } /* Detect data bytes */ while( sample < arrayInSize ) { if( (sample & 0xfff) == 0 ) // give GUI a chance to stop us if (PROTO_Cancel(context)) return PROTO_PROCESS_CANCEL; traceBitsOld = traceBits; PROTO_ReadTrace( context, sample, &time, (int*) &traceBits ); sample++; inversebits(&traceBits, parameters->invert); // RECEIVE bits if( traceBits.cs == 0 ) { if( traceBitsOld.cs == 1 ) { dataOut[index].timestamp = time; } else // traceBitsOld.cs == 0 (cs must be active BEFORE the first clock event) { if( ( traceBitsOld.sck==0 && traceBits.sck==1 && parameters->cpol==parameters->cpha ) || // Shift in bits on rising edge of SPI clock ( traceBitsOld.sck==1 && traceBits.sck==0 && parameters->cpol!=parameters->cpha ) ) // Shift in bits on falling edge of SPI clock { if( dataOut[index].timestamp == 0 ) dataOut[index].timestamp = time; if( parameters->msbf ){ // most significant bit came first for( i=size/32+1; i>0; i-- ){ bitsMOSI[i] = bitsMOSI[i]<<1 | !!(bitsMOSI[i-1]&0x80000000); bitsMISO[i] = bitsMISO[i]<<1 | !!(bitsMISO[i-1]&0x80000000); } bitsMOSI[0] = bitsMOSI[0]<<1 | traceBitsOld.mosi; bitsMISO[0] = bitsMISO[0]<<1 | traceBitsOld.miso; } else { // least significant bit came first int minor,major; minor = size%32; major = size/32; bitsMOSI[major] |= traceBitsOld.mosi << minor; bitsMISO[major] |= traceBitsOld.miso << minor; } size++; } } } // STORE received byte if( size!=0 ) // ...if we received at least one bit... { if( ( traceBits.cs == 1) || // when we received the end of data transfer ( size==parameters->framesize && parameters->cpha==1 && traceBits.sck!=parameters->cpol ) || // clock phase mode 1 requires no deasserting CS (transfer ends also after parameters->framesize bits (mostly 8 bits)) ( size==32*MAXDWORDS ) || // maximum sample-count per data frame reached ( sample==arrayInSize ) ) // reached last recorded sample { dataOut[index].framesize = size; if(parameters->cpha==0 && size>parameters->framesize) parameters->framesize = size; // set maximum framesize /* init next frame : */ size=0; index++; if( index >= arrayOutSize ) // Check if we have space for a new data byte return PROTO_PROCESS_OUTOFMEMORY; bitsMOSI = dataOut[index].bitsMOSI; bitsMISO = dataOut[index].bitsMISO; for( i=0; i=0; i-- ) sprintf(string+strlen(string), "%08x", data[i]); PROTO_Printf( context, "%s", string ); } static void PROTOAPI display1(protoContext context, protoPtr pdata, protoPtr localdata) { data *spidata = (data*) pdata; param *parameters = (param*)localdata; const char *framename; char string[16]; switch(spidata->framesize) { case 8: framename = " Byte"; break; case 16: framename = " Word"; break; case 32: framename = " Long"; break; default: framename = string; sprintf(string, "%2ibit", spidata->framesize); } PROTO_Control( context, PROTO_ATTRIBUTE_LIGHT ); PROTO_Printf ( context, "%s", framename ); PROTO_Control( context, PROTO_ATTRIBUTE_NORM ); PROTO_Puts ( context, " TO " ); PROTO_Control( context, PROTO_ATTRIBUTE_LIGHT ); PROTO_Puts ( context, "Device : " ); PROTO_Control( context, PROTO_ATTRIBUTE_NORM ); PrintData1 ( context, spidata->bitsMOSI, parameters->framesize ); PROTO_Control( context, PROTO_ATTRIBUTE_LIGHT ); PROTO_Printf ( context, " %s", framename ); PROTO_Control( context, PROTO_ATTRIBUTE_NORM ); PROTO_Puts ( context, " FROM " ); PROTO_Control( context, PROTO_ATTRIBUTE_LIGHT ); PROTO_Puts ( context, "Device : " ); PROTO_Control( context, PROTO_ATTRIBUTE_NORM ); PrintData1 ( context, spidata->bitsMISO, parameters->framesize ); } static void PROTOAPI export(protoContext context, protoPtr pdata, protoPtr localdata) { data *spidata = (data*) pdata; param *parameters = (param*) localdata; PROTO_Printf ( context, "%03i,", spidata->framesize ); PrintData1 ( context, spidata->bitsMOSI, parameters->framesize ); PROTO_Printf ( context, "," ); PrintData1 ( context, spidata->bitsMISO, parameters->framesize ); PROTO_Printf ( context, "," ); PROTO_Control( context, PROTO_CONTROL_LINEFEED ); } int PROTOAPI PROTO_Init(protoContext context, int command) { param *parameters; parameters = (param*) PROTO_Alloc(context, sizeof(param) ); parameters->msbf = 1; parameters->invert = 0; PROTO_Parse(context, (protoPtr) 0, "", PROTO_PARSE_CHANNEL); // Clock PROTO_Parse(context, (protoPtr) 0, "", PROTO_PARSE_CHANNEL); // Data from master to slave PROTO_Parse(context, (protoPtr) 0, "", PROTO_PARSE_CHANNEL); // Data to master from slave PROTO_Parse(context, (protoPtr) 0, "", PROTO_PARSE_CHANNEL); // Chip select / data strobe PROTO_Parse(context, (protoPtr)(¶meters->cpol), "", PROTO_PARSE_INTEGER); // clock polarity PROTO_Parse(context, (protoPtr)(¶meters->cpha), "", PROTO_PARSE_INTEGER); // clock phase PROTO_Parse(context, (protoPtr)(¶meters->msbf), "LSBF,MSBF", PROTO_PARSE_SELECTION|PROTO_PARSE_OPTIONAL); // bit order: Most Significant Bit First PROTO_Parse(context, (protoPtr)(¶meters->invert), "", PROTO_PARSE_INTEGER |PROTO_PARSE_OPTIONAL); // inversion mask if( parameters->cpol != 0 ) parameters->cpol = 1; switch( parameters->cpha ) { case 0: parameters->framesize = 0; // if CPHA==0 framelength is determined by CS low period break; case 1: parameters->framesize = 8; // default framlegth for CPHA==1 is 8 bit (1 bit legth is unsupported) break; default: // other values define the framelength for CPHA==1 mode parameters->framesize = parameters->cpha; if(parameters->framesize>32*MAXDWORDS) parameters->framesize=32*MAXDWORDS; parameters->cpha = 1; break; } PROTO_RegisterProcessCallback(context, process1, (protoPtr) parameters, sizeof(data), 1); PROTO_RegisterDisplayCallback(context, display1, (protoPtr) parameters, 1); PROTO_RegisterExportCallback (context, export, (protoPtr) parameters, 1); PROTO_SetDefaultLevel(context, 1); return PROTO_OK; }