#include "proto.h" #include #define I2C_START 2 #define I2C_STOP 3 #define I2C_IDLE 4 #define I2C_ACKTRANSFER 5 #define I2C_BYTETRANSFER 8 #define I2C_WAITFORSTART 9 #define IC2_DATA_ACKOK 13 #define IC2_DATA_ACKERR 15 #define IC2_DATA_ABORTED 16 #define IC2_DATA_ACKNONE 17 #define IC2_SCLERR 18 #define WRFLAG_D 0x1000 /**************************************************************************/ typedef struct { int searchdata; int glitchfilter; } I2Cparameters; typedef struct { protoTime time; int scl; int sda; } I2CTraceEntryRaw; typedef struct { protoTime time; int state; int data; int index; } I2CTraceEntry; /************************************************************************** I2C Protocol Level 1 - parse i2c anti glitch filter **************************************************************************/ /* The Stage one processing filters out glitches which are < 500ns. This is important, because I2C has VERY bad rising edges. Basically a signal transition is only valid if the signal stays constant for 500ns after the transition; otherwise the transition is ignored.*/ static protoSizeT PROTOAPI PROTO_Process(protoContext context, protoPtr arrayOut, protoSizeT arrayOutSize, protoPtr arrayIn, protoSizeT arrayInSize, protoPtr localdata) { I2Cparameters *parameters; int i,j,h; I2CTraceEntryRaw *stageOneArray; /* Pointer to array which will contain the output records */ protoTime traceTime; /* timestamp of raw trace data record */ int traceChannel[2]; /* Bit values of SCL and SDA */ int sdaOld,sclOld,lastSclEntry,lastSdaEntry,sclEdge,sdaEdge; protoTime lastSclTime,lastSdaTime,delta,tolerance; stageOneArray=(I2CTraceEntryRaw *)arrayOut; /* type cast to the used output record type */ parameters = (I2Cparameters *) localdata; PROTO_ReadTrace(context, -1, &traceTime, traceChannel); tolerance = PROTO_TIME_DIVINT(PROTO_TIME_MULTINT(PROTO_TIME_ONEMICROSECOND, parameters->glitchfilter),1000); sclOld=traceChannel[0]; sdaOld=traceChannel[1]; lastSclTime=traceTime; lastSdaTime=traceTime; stageOneArray[0].time=traceTime; stageOneArray[0].scl=sclOld; stageOneArray[0].sda=sdaOld; lastSclEntry=0; lastSdaEntry=0; sclEdge=0; sdaEdge=0; h=1; for (i=1;i= arrayOutSize) return PROTO_PROCESS_OUTOFMEMORY; if ((i & 0xfff) == 0) if (PROTO_Cancel(context)) return PROTO_PROCESS_CANCEL; PROTO_ReadTrace(context, -1, &traceTime, traceChannel); stageOneArray[h].time=traceTime; stageOneArray[h].scl=traceChannel[0]; stageOneArray[h].sda=traceChannel[1]; if (traceChannel[0]!=sclOld && !sclEdge) { /* this case only happens at the first edge, or after a glitch was suppressed. */ lastSclTime=traceTime; lastSclEntry=h; sclEdge=1; } if (traceChannel[0]==sclOld && sclEdge) { /* This means we scanned over something like _____-____ or -----_---- Now we have to check if the signal was stable long enough */ delta=PROTO_TIME_SUB(traceTime,lastSclTime); if (PROTO_TIME_ISSMALLER(delta,tolerance)) { /* If the signal wasn't stable long enough we supress the glitch */ sclEdge=0; for (j=lastSclEntry;jscl); PROTO_Control(context, PROTO_ATTRIBUTE_LIGHT); PROTO_Puts(context, "SDA : "); PROTO_Control(context, PROTO_ATTRIBUTE_NORM); PROTO_Printf(context, "%d", entry->sda); } /************************************************************************** I2C Protocol Level 2 - parse i2c data **************************************************************************/ #define SCL() (stageOneArray[indexin].scl) /* sclock */ #define SDA() (stageOneArray[indexin].sda) /* sdata */ static protoSizeT PROTOAPI PROTO_Process2(protoContext context, protoPtr arrayOut, protoSizeT arrayOutSize, protoPtr arrayIn, protoSizeT arrayInSize, protoPtr localdata) { I2CTraceEntry *I2Ctracebuffer; I2CTraceEntryRaw *stageOneArray; int bitCounter, theByte, ndata; int I2Cstate,I2CWrFlag; int indexin, indexout; protoTime time; int sclref, sdaref; I2Ctracebuffer = (I2CTraceEntry *) arrayOut; stageOneArray = (I2CTraceEntryRaw *) arrayIn; /* Data from Stage One */ if (arrayInSize<2) return 0; I2Cstate = I2C_WAITFORSTART; I2CWrFlag = 0; indexout = 0; ndata = 0; bitCounter = 0x80; theByte = 0; sclref = stageOneArray[0].scl; sdaref = stageOneArray[0].sda; arrayOutSize-=2; for (indexin = 1; indexin < arrayInSize; indexin++) { if (indexout > arrayOutSize) return PROTO_PROCESS_OUTOFMEMORY; if ((indexin & 0xfff) == 0) if (PROTO_Cancel(context)) return PROTO_PROCESS_CANCEL; time=stageOneArray[indexin].time; switch (I2Cstate) { case I2C_IDLE: // wait for start and check for unexpected SCL transitions if (sclref!=0 && SCL()==0) { // unexpected falling edge on SCL I2Cstate=I2C_WAITFORSTART; I2Ctracebuffer[indexout].time = time; I2Ctracebuffer[indexout].state = IC2_SCLERR; indexout++; break; } // fall through to I2C_WAITFORSTART case I2C_WAITFORSTART: // wait for start condition, ignore transitions on SCL if (sclref!=0 && SCL()!=0 && sdaref!=0 && SDA()==0) { I2Cstate=I2C_START; I2Ctracebuffer[indexout].time = time; I2Ctracebuffer[indexout].state = I2C_START; indexout++; } break; case I2C_START: // wait for falling edge after start condition // here: sclref==1, sdaref==0 if (SCL()==0) { // falling edge after start, now wait for data I2Cstate=I2C_BYTETRANSFER; ndata = 0; I2CWrFlag = 0; bitCounter = 0x80; break; } // here: sclref==1, SCL==1, sdaref==0 if (SDA()!=0) { // Unexpected STOP after START. Wait for next START I2Cstate=I2C_IDLE; I2Ctracebuffer[indexout].time = time; I2Ctracebuffer[indexout].state = I2C_STOP; indexout++; } break; case I2C_BYTETRANSFER: // sample bits for one byte (8 bits), ACK is extra if (sclref==0) { // SCL was LOW: if (SCL()!=0) { // rising edge on SCL if (bitCounter==0x80) { // first bit of byte: remember time I2Ctracebuffer[indexout].time = time; I2Ctracebuffer[indexout].index = ndata; theByte=0; ndata++; } // remember bit if (SDA()!=0) theByte|=bitCounter; } break; } // here sclref==1 if (SCL()==0) { // falling edge on SCL bitCounter>>=1; if (bitCounter==0) { // Last bit of data received, now check for ACK I2Ctracebuffer[indexout].state = IC2_DATA_ABORTED|I2CWrFlag; I2Ctracebuffer[indexout].data = theByte; if (ndata==1 && (theByte&0x1)==0 ) I2CWrFlag|=WRFLAG_D; I2Cstate=I2C_ACKTRANSFER; } break; } // here sclref==1 && SCL==1 if (sdaref!=0 && SDA()==0) { // START detected if (bitCounter<0x80) { // Unexpected start... I2Ctracebuffer[indexout].state = IC2_DATA_ABORTED|I2CWrFlag; I2Ctracebuffer[indexout].data = theByte; indexout++; } I2Cstate=I2C_START; I2Ctracebuffer[indexout].state = I2C_START; I2Ctracebuffer[indexout].time = time; indexout++; break; } if (sdaref==0 && SDA()!=0) { // STOP detected if (bitCounter<0x80) { // Unexpected stop... I2Ctracebuffer[indexout].state = IC2_DATA_ABORTED|I2CWrFlag; I2Ctracebuffer[indexout].data = theByte; indexout++; } else if ((I2CWrFlag&WRFLAG_D)==0 && ndata>2 && indexout>0 && (I2Ctracebuffer[indexout-1].state&0xFFF)==IC2_DATA_ACKERR) { // Special case: We read data from slave device ( Write Flag not set, ndata > 2), // and it is the last byte transferred from slave device (so we are at a stop here) // => Do not flag missing ACK as an error. I2Ctracebuffer[indexout-1].state = IC2_DATA_ACKNONE|I2CWrFlag; } I2Cstate=I2C_IDLE; I2Ctracebuffer[indexout].state = I2C_STOP; I2Ctracebuffer[indexout].time = time; indexout++; break; } /* Should not reach here because of edge filter: * ( sclref == 1 && SCL() == 1) * && ( sdaref == 0 && SDA() == 0 * || sdaref == 1 && SDA() == 1) */ break; case I2C_ACKTRANSFER: if (sclref==0) { if (SCL()!=0) { // rising edge on SCL , check ACK bit if (SDA()==0) { I2Ctracebuffer[indexout].state = IC2_DATA_ACKOK|I2CWrFlag; } else { I2Ctracebuffer[indexout].state = IC2_DATA_ACKERR|I2CWrFlag; } } break; } // here sclref==1 if (SCL()==0) { // falling edge, ack parsed, commit current byte, now try next byte: indexout++; I2Cstate=I2C_BYTETRANSFER; bitCounter = 0x80; break; } // here sclref==1 && SCL()==1 if (sdaref!=0 && SDA()==0) { // unexpected START, output aborted_d I2Ctracebuffer[indexout].state = IC2_DATA_ABORTED|I2CWrFlag; indexout++; I2Cstate=I2C_START; I2Ctracebuffer[indexout].state = I2C_START; I2Ctracebuffer[indexout].time = time; indexout++; break; } if (sdaref==0 && SDA()!=0) { // unexpected STOP, output aborted_d I2Ctracebuffer[indexout].state = IC2_DATA_ABORTED|I2CWrFlag; indexout++; I2Cstate=I2C_IDLE; I2Ctracebuffer[indexout].state = I2C_STOP; I2Ctracebuffer[indexout].time = time; indexout++; break; } /* Should not reach here because of edge filter: * ( sclref == 1 && SCL() == 1) * && ( sdaref == 0 && SDA() == 0 * || sdaref == 1 && SDA() == 1) */ break; default: I2Cstate=I2C_IDLE; } sclref=SCL(); sdaref=SDA(); } return indexout; } /***********/ static void OutputData(protoContext context, protoPtr pdata) { I2CTraceEntry *data = (I2CTraceEntry *) pdata; PROTO_Control(context, PROTO_ATTRIBUTE_NORM); PROTO_Printf(context, "%02x", data->data); if (data->state&WRFLAG_D) PROTO_Control(context, PROTO_ATTRIBUTE_COLOR0+2); else PROTO_Control(context, PROTO_ATTRIBUTE_COLOR0+4); if (data->index>0) { if (data->state&WRFLAG_D) PROTO_Puts(context, " Data Wr #"); else PROTO_Puts(context, " Data Rd #"); PROTO_Printf(context, "%4d", data->index-1); } else { if (data->state&WRFLAG_D) PROTO_Puts(context, " SlaveAddr Wr "); else PROTO_Puts(context, " SlaveAddr Rd "); } switch (data->state&0xFFF) { case IC2_DATA_ACKOK: PROTO_Control(context, PROTO_ATTRIBUTE_LIGHT); PROTO_Printf(context, " \005 ACK "); break; case IC2_DATA_ACKNONE: PROTO_Control(context, PROTO_ATTRIBUTE_LIGHT); PROTO_Printf(context, " \005 NAK "); break; case IC2_DATA_ACKERR: PROTO_Control(context, PROTO_ATTRIBUTE_ERROR); if (data->index>0) PROTO_Puts(context, " \005\005\005 Error: NAK "); else PROTO_Puts(context, " \005 NAK "); break; case IC2_DATA_ABORTED: PROTO_Control(context, PROTO_ATTRIBUTE_ERROR); PROTO_Puts(context, " \005 Error: Byte aborted prematurely "); break; default: ; } } static void PROTOAPI PROTO_Display2(protoContext context, protoPtr pdata, protoPtr localdata) { I2CTraceEntry *data = (I2CTraceEntry *) pdata; switch (data->state&0xFFF) { case I2C_START: PROTO_Control(context, PROTO_ATTRIBUTE_LEVEL1); PROTO_Puts(context, " \005\005\005 Start Condition "); PROTO_Control(context, PROTO_CONTROL_LINETILLEND); break; case I2C_STOP: PROTO_Control(context, PROTO_ATTRIBUTE_LEVEL1); PROTO_Puts(context, " \005\005\005 Stop Condition "); PROTO_Control(context, PROTO_CONTROL_LINETILLEND); break; case IC2_SCLERR: PROTO_Control(context, PROTO_ATTRIBUTE_ERROR); PROTO_Puts(context, "Error: Unexpected falling edge on SCL"); break; case IC2_DATA_ACKOK: case IC2_DATA_ACKERR: case IC2_DATA_ACKNONE: case IC2_DATA_ABORTED: OutputData(context, data); break; } PROTO_Control(context, PROTO_ATTRIBUTE_NORM); } /***********/ static void OutputDataExport(protoContext context, protoPtr pdata) { I2CTraceEntry *data = (I2CTraceEntry *) pdata; char myline[128],*ptr; ptr=myline; ptr+=sprintf(ptr, "%02x", data->data); if (data->index>0) { if (data->state&WRFLAG_D) ptr+=sprintf(ptr, " Data Wr #"); else ptr+=sprintf(ptr, " Data Rd #"); ptr+=sprintf(ptr, "%4d", data->index-1); } else { if (data->state&WRFLAG_D) ptr+=sprintf(ptr, " SlaveAddr Wr "); else ptr+=sprintf(ptr, " SlaveAddr Rd "); } switch (data->state&0xFFF) { case IC2_DATA_ACKOK: ptr+=sprintf(ptr, " - ACK "); break; case IC2_DATA_ACKNONE: ptr+=sprintf(ptr, " - NAK "); break; case IC2_DATA_ACKERR: if (data->index>0) ptr+=sprintf(ptr, " - Error: NAK "); else ptr+=sprintf(ptr, " - NAK"); break; case IC2_DATA_ABORTED: ptr+=sprintf(ptr, " - Error: Byte aborted prematurely"); break; default: ; } PROTO_Printf(context,"%s",myline); } static void PROTOAPI PROTO_Export(protoContext context, protoPtr pdata, protoPtr localdata) { I2CTraceEntry *data = (I2CTraceEntry *) pdata; switch (data->state&0xFFF) { case I2C_START: PROTO_Printf(context, "-- Start Condition ----"); break; case I2C_STOP: PROTO_Printf(context, "-- Stop Condition -----"); break; case IC2_SCLERR: PROTO_Printf(context, "Error: Unexpected falling edge on SCL"); break; case IC2_DATA_ACKOK: case IC2_DATA_ACKERR: case IC2_DATA_ACKNONE: case IC2_DATA_ABORTED: OutputDataExport(context,data); break; default: ; } PROTO_Control(context, PROTO_CONTROL_LINEFEED); } /***********/ static int PROTOAPI PROTO_Find(protoContext context, protoPtr pdata, protoPtr localdata) { I2Cparameters* parameters; I2CTraceEntry* data; int state; parameters = (I2Cparameters*) localdata; data = (I2CTraceEntry*) pdata; state = data->state&0xFFF; if( state == IC2_DATA_ACKOK || state == IC2_DATA_ACKERR || state == IC2_DATA_ACKNONE) return ( (data->data & 0xFF) == (parameters->searchdata & 0xFF) ); return 0; } static int PROTOAPI PROTO_Share(protoContext context, protoPtr sharedata, protoPtr localdata) { return 1; } /************************************************************************** I2C Protocol Parser **************************************************************************/ int PROTOAPI PROTO_Init(protoContext context, int command) { I2Cparameters* parameters; int searchbyte; int glitchfilter; if (PROTO_RequestVersion(context, 2) != PROTO_OK) { PROTO_Puts(context, "Requires newer trace32 version"); return PROTO_FAIL; } parameters = (I2Cparameters*) PROTO_Alloc(context, sizeof(I2Cparameters) ); PROTO_Parse(context, (protoPtr) 0, "", PROTO_PARSE_CHANNEL); PROTO_Parse(context, (protoPtr) 0, "", PROTO_PARSE_CHANNEL); if (command == PROTO_COMMAND_FIND) { PROTO_Parse(context, (protoPtr) &searchbyte, "", PROTO_PARSE_INTEGER); parameters->searchdata = searchbyte; } glitchfilter = 500; // default 500ns PROTO_Parse(context, (protoPtr) &glitchfilter, "", PROTO_PARSE_INTEGER | PROTO_PARSE_OPTIONAL); parameters->glitchfilter = glitchfilter; PROTO_RegisterProcessCallback(context, PROTO_Process, (protoPtr) parameters, sizeof(I2CTraceEntryRaw), 1); PROTO_RegisterDisplayCallback(context, PROTO_Display, (protoPtr) NULL, 1); PROTO_RegisterProcessCallback(context, PROTO_Process2, (protoPtr) NULL, sizeof(I2CTraceEntry), 2); PROTO_RegisterDisplayCallback(context, PROTO_Display2, (protoPtr) NULL, 2); PROTO_RegisterExportCallback (context, PROTO_Export, (protoPtr) NULL, 2); PROTO_RegisterFindCallback (context, PROTO_Find, (protoPtr) parameters, 2); PROTO_RegisterShareCallback(context, PROTO_Share, (protoPtr) NULL, 0); PROTO_SetDefaultLevel(context, 2); return PROTO_OK; }