From e691fc45bcaf0ec02c0da0b472d06580046e918f Mon Sep 17 00:00:00 2001 From: "micki.held@gmx.de" Date: Tue, 19 Nov 2013 18:52:40 +0000 Subject: [PATCH] - improved reader sensitivity for 14443a cards (FPGA change!) - implemented ISO 14443A anticollision loop See http://www.proxmark.org/forum/viewtopic.php?id=1797 further details --- armsrc/iso14443a.c | 432 +++++++++++++++++++++++--------------------- armsrc/iso14443a.h | 54 +++--- client/cmdhf14a.c | 18 +- fpga/Makefile | 4 +- fpga/fpga.bit | Bin 42172 -> 42172 bytes fpga/fpga.ucf | 13 ++ fpga/fpga.v | 34 ++-- fpga/hi_iso14443a.v | 249 +++++++++++++------------ fpga/xst.scr | 2 +- 9 files changed, 423 insertions(+), 383 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 63cc32ae..00dc622f 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -96,9 +96,9 @@ uint32_t GetParity(const uint8_t * pbtCmd, int iLen) int i; uint32_t dwPar = 0; - // Generate the encrypted data + // Generate the parity bits for (i = 0; i < iLen; i++) { - // Save the encrypted parity bit + // and save them to a 32Bit word dwPar |= ((OddByteParity[pbtCmd[i]]) << i); } return dwPar; @@ -375,196 +375,176 @@ static RAMFUNC int MillerDecoding(int bit) } //============================================================================= -// ISO 14443 Type A - Manchester +// ISO 14443 Type A - Manchester decoder //============================================================================= +// Basics: +// The tag will modulate the reader field by asserting different loads to it. As a consequence, the voltage +// at the reader antenna will be modulated as well. The FPGA detects the modulation for us and would deliver e.g. the following: +// ........ 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ....... +// The Manchester decoder needs to identify the following sequences: +// 4 ticks modulated followed by 4 ticks unmodulated: Sequence D = 1 (also used as "start of communication") +// 4 ticks unmodulated followed by 4 ticks modulated: Sequence E = 0 +// 8 ticks unmodulated: Sequence F = end of communication +// 8 ticks modulated: A collision. Save the collision position and treat as Sequence D +// Note 1: the bitstream may start at any time (either in first or second nibble within the parameter bit). We therefore need to sync. +// Note 2: parameter offset is used to determine the position of the parity bits (required for the anticollision command only) static tDemod Demod; -static RAMFUNC int ManchesterDecoding(int v) +inline RAMFUNC bool IsModulation(byte_t b) { - int bit; - int modulation; - //int error = 0; - - if(!Demod.buff) { - Demod.buff = 1; - Demod.buffer = v; - return FALSE; - } - else { - bit = Demod.buffer; - Demod.buffer = v; - } + if (b >= 5 || b == 3) // majority decision: 2 or more bits are set + return true; + else + return false; + +} - if(Demod.state==DEMOD_UNSYNCD) { - Demod.output[Demod.len] = 0xfa; - Demod.syncBit = 0; - //Demod.samples = 0; - Demod.posCount = 1; // This is the first half bit period, so after syncing handle the second part +inline RAMFUNC bool IsModulationNibble1(byte_t b) +{ + return IsModulation((b & 0xE0) >> 5); +} - if(bit & 0x08) { - Demod.syncBit = 0x08; - } +inline RAMFUNC bool IsModulationNibble2(byte_t b) +{ + return IsModulation((b & 0x0E) >> 1); +} - if(bit & 0x04) { - if(Demod.syncBit) { - bit <<= 4; +static RAMFUNC int ManchesterDecoding(int bit, uint16_t offset) +{ + + switch (Demod.state) { + + case DEMOD_UNSYNCD: // not yet synced + Demod.len = 0; // initialize number of decoded data bytes + Demod.bitCount = offset; // initialize number of decoded data bits + Demod.shiftReg = 0; // initialize shiftreg to hold decoded data bits + Demod.parityBits = 0; // initialize parity bits + Demod.collisionPos = 0; // Position of collision bit + + if (IsModulationNibble1(bit) + && !IsModulationNibble2(bit)) { // this is the start bit + Demod.samples = 8; + if(trigger) LED_A_OFF(); + Demod.state = DEMOD_MANCHESTER_DATA; + } else if (!IsModulationNibble1(bit) && IsModulationNibble2(bit)) { // this may be the first half of the start bit + Demod.samples = 4; + Demod.state = DEMOD_HALF_SYNCD; } - Demod.syncBit = 0x04; - } + break; - if(bit & 0x02) { - if(Demod.syncBit) { - bit <<= 2; - } - Demod.syncBit = 0x02; - } - if(bit & 0x01 && Demod.syncBit) { - Demod.syncBit = 0x01; - } - - if(Demod.syncBit) { - Demod.len = 0; - Demod.state = DEMOD_START_OF_COMMUNICATION; - Demod.sub = SUB_FIRST_HALF; - Demod.bitCount = 0; - Demod.shiftReg = 0; - Demod.parityBits = 0; - Demod.samples = 0; - if(Demod.posCount) { - if(trigger) LED_A_OFF(); - switch(Demod.syncBit) { - case 0x08: Demod.samples = 3; break; - case 0x04: Demod.samples = 2; break; - case 0x02: Demod.samples = 1; break; - case 0x01: Demod.samples = 0; break; + case DEMOD_HALF_SYNCD: + Demod.samples += 8; + if (IsModulationNibble1(bit)) { // error: this was not a start bit. + Demod.state = DEMOD_UNSYNCD; + } else { + if (IsModulationNibble2(bit)) { // modulation in first half + Demod.state = DEMOD_MOD_FIRST_HALF; + } else { // no modulation in first half + Demod.state = DEMOD_NOMOD_FIRST_HALF; } } - //error = 0; - } - } - else { - //modulation = bit & Demod.syncBit; - modulation = ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; - - Demod.samples += 4; - - if(Demod.posCount==0) { - Demod.posCount = 1; - if(modulation) { - Demod.sub = SUB_FIRST_HALF; + break; + + + case DEMOD_MOD_FIRST_HALF: + Demod.samples += 8; + Demod.bitCount++; + if (IsModulationNibble1(bit)) { // modulation in both halfs - collision + if (!Demod.collisionPos) { + Demod.collisionPos = (Demod.len << 3) + Demod.bitCount; + } + } // modulation in first half only - Sequence D = 1 + Demod.shiftReg = (Demod.shiftReg >> 1) | 0x100; // add a 1 to the shiftreg + if(Demod.bitCount >= 9) { // if we decoded a full byte (including parity) + Demod.parityBits <<= 1; // make room for the parity bit + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.parityBits |= ((Demod.shiftReg >> 8) & 0x01); // store parity bit + Demod.bitCount = 0; + Demod.shiftReg = 0; } - else { - Demod.sub = SUB_NONE; + if (IsModulationNibble2(bit)) { // modulation in first half + Demod.state = DEMOD_MOD_FIRST_HALF; + } else { // no modulation in first half + Demod.state = DEMOD_NOMOD_FIRST_HALF; } - } - else { - Demod.posCount = 0; - if(modulation && (Demod.sub == SUB_FIRST_HALF)) { - if(Demod.state!=DEMOD_ERROR_WAIT) { - Demod.state = DEMOD_ERROR_WAIT; - Demod.output[Demod.len] = 0xaa; - //error = 0x01; + break; + + + case DEMOD_NOMOD_FIRST_HALF: + if (IsModulationNibble1(bit)) { // modulation in second half only - Sequence E = 0 + Demod.bitCount++; + Demod.samples += 8; + Demod.shiftReg = (Demod.shiftReg >> 1); // add a 0 to the shiftreg + if(Demod.bitCount >= 9) { // if we decoded a full byte (including parity) + Demod.parityBits <<= 1; // make room for the new parity bit + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.parityBits |= ((Demod.shiftReg >> 8) & 0x01); // store parity bit + Demod.bitCount = 0; + Demod.shiftReg = 0; } + } else { // no modulation in both halves - End of communication + Demod.samples += 4; + if(Demod.bitCount > 0) { // if we decoded bits + Demod.shiftReg >>= (9 - Demod.bitCount); // add the remaining decoded bits to the output + Demod.output[Demod.len++] = Demod.shiftReg & 0xff; + // No parity bit, so just shift a 0 + Demod.parityBits <<= 1; + } + Demod.state = DEMOD_UNSYNCD; // start from the beginning + return TRUE; // we are finished with decoding the raw data sequence } - else if(modulation) { - Demod.sub = SUB_SECOND_HALF; + if (IsModulationNibble2(bit)) { // modulation in first half + Demod.state = DEMOD_MOD_FIRST_HALF; + } else { // no modulation in first half + Demod.state = DEMOD_NOMOD_FIRST_HALF; } + break; + - switch(Demod.state) { - case DEMOD_START_OF_COMMUNICATION: - if(Demod.sub == SUB_FIRST_HALF) { - Demod.state = DEMOD_MANCHESTER_D; - } - else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_ERROR_WAIT; - //error = 0x02; - } - break; - - case DEMOD_MANCHESTER_D: - case DEMOD_MANCHESTER_E: - if(Demod.sub == SUB_FIRST_HALF) { - Demod.bitCount++; - Demod.shiftReg = (Demod.shiftReg >> 1) ^ 0x100; - Demod.state = DEMOD_MANCHESTER_D; + case DEMOD_MANCHESTER_DATA: + Demod.samples += 8; + if (IsModulationNibble1(bit)) { // modulation in first half + if (IsModulationNibble2(bit) & 0x0f) { // ... and in second half = collision + if (!Demod.collisionPos) { + Demod.collisionPos = (Demod.len << 3) + Demod.bitCount; } - else if(Demod.sub == SUB_SECOND_HALF) { - Demod.bitCount++; - Demod.shiftReg >>= 1; - Demod.state = DEMOD_MANCHESTER_E; + } // modulation in first half only - Sequence D = 1 + Demod.bitCount++; + Demod.shiftReg = (Demod.shiftReg >> 1) | 0x100; // in both cases, add a 1 to the shiftreg + if(Demod.bitCount >= 9) { // if we decoded a full byte (including parity) + Demod.parityBits <<= 1; // make room for the parity bit + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.parityBits |= ((Demod.shiftReg >> 8) & 0x01); // store parity bit + Demod.bitCount = 0; + Demod.shiftReg = 0; + } + } else { // no modulation in first half + if (IsModulationNibble2(bit)) { // and modulation in second half = Sequence E = 0 + Demod.bitCount++; + Demod.shiftReg = (Demod.shiftReg >> 1); // add a 0 to the shiftreg + if(Demod.bitCount >= 9) { // if we decoded a full byte (including parity) + Demod.parityBits <<= 1; // make room for the new parity bit + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.parityBits |= ((Demod.shiftReg >> 8) & 0x01); // store parity bit + Demod.bitCount = 0; + Demod.shiftReg = 0; } - else { - Demod.state = DEMOD_MANCHESTER_F; + } else { // no modulation in both halves - End of communication + if(Demod.bitCount > 0) { // if we decoded bits + Demod.shiftReg >>= (9 - Demod.bitCount); // add the remaining decoded bits to the output + Demod.output[Demod.len++] = Demod.shiftReg & 0xff; + // No parity bit, so just shift a 0 + Demod.parityBits <<= 1; } - break; - - case DEMOD_MANCHESTER_F: - // Tag response does not need to be a complete byte! - if(Demod.len > 0 || Demod.bitCount > 0) { - if(Demod.bitCount > 0) { - Demod.shiftReg >>= (9 - Demod.bitCount); - Demod.output[Demod.len] = Demod.shiftReg & 0xff; - Demod.len++; - // No parity bit, so just shift a 0 - Demod.parityBits <<= 1; - } - - Demod.state = DEMOD_UNSYNCD; - return TRUE; - } - else { - Demod.output[Demod.len] = 0xad; - Demod.state = DEMOD_ERROR_WAIT; - //error = 0x03; - } - break; - - case DEMOD_ERROR_WAIT: - Demod.state = DEMOD_UNSYNCD; - break; - - default: - Demod.output[Demod.len] = 0xdd; - Demod.state = DEMOD_UNSYNCD; - break; - } - - if(Demod.bitCount>=9) { - Demod.output[Demod.len] = Demod.shiftReg & 0xff; - Demod.len++; - - Demod.parityBits <<= 1; - Demod.parityBits ^= ((Demod.shiftReg >> 8) & 0x01); - - Demod.bitCount = 0; - Demod.shiftReg = 0; + Demod.state = DEMOD_UNSYNCD; // start from the beginning + return TRUE; // we are finished with decoding the raw data sequence + } } + + } - /*if(error) { - Demod.output[Demod.len] = 0xBB; - Demod.len++; - Demod.output[Demod.len] = error & 0xFF; - Demod.len++; - Demod.output[Demod.len] = 0xBB; - Demod.len++; - Demod.output[Demod.len] = bit & 0xFF; - Demod.len++; - Demod.output[Demod.len] = Demod.buffer & 0xFF; - Demod.len++; - Demod.output[Demod.len] = Demod.syncBit & 0xFF; - Demod.len++; - Demod.output[Demod.len] = 0xBB; - Demod.len++; - return TRUE; - }*/ - - } - - } // end (state != UNSYNCED) - - return FALSE; + return FALSE; // not finished yet, need more data } //============================================================================= @@ -691,7 +671,7 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { LED_B_OFF(); } - if(ManchesterDecoding(data[0] & 0x0F)) { + if(ManchesterDecoding(data[0], 0)) { LED_B_ON(); if (!LogTrace(receivedResponse, Demod.len, 0 - Demod.samples, Demod.parityBits, FALSE)) break; @@ -1296,7 +1276,7 @@ static void TransmitFor14443a(const uint8_t *cmd, int len, uint32_t *timing) while(GetCountMifare() < (*timing & 0xfffffff8)); // Delay transfer (multiple of 8 MF clock ticks) } - for(c = 0; c < 10;) { // standard delay for each transfer (allow tag to be ready after last transmission) + for(c = 0; c < 10;) { // standard delay for each transfer (allow tag to be ready after last transmission?) if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x00; c++; @@ -1558,13 +1538,12 @@ int EmSendCmdPar(uint8_t *resp, int respLen, uint32_t par){ //----------------------------------------------------------------------------- // Wait a certain time for tag response // If a response is captured return TRUE -// If it takes to long return FALSE +// If it takes too long return FALSE //----------------------------------------------------------------------------- -static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) //uint8_t *buffer +static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t offset, int maxLen, int *samples) { - // buffer needs to be 512 bytes int c; - + // Set FPGA mode to "reader listen mode", no modulation (listen // only, since we are receiving, not transmitting). // Signal field is on with the appropriate LED @@ -1577,7 +1556,6 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, int maxLen, int Demod.state = DEMOD_UNSYNCD; uint8_t b; - if (elapsed) *elapsed = 0; c = 0; for(;;) { @@ -1590,12 +1568,8 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, int maxLen, int if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { if(c < iso14a_timeout) { c++; } else { return FALSE; } b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - if(ManchesterDecoding((b>>4) & 0xf)) { - *samples = ((c - 1) << 3) + 4; - return TRUE; - } - if(ManchesterDecoding(b & 0x0f)) { - *samples = c << 3; + if(ManchesterDecoding(b, offset)) { + *samples = Demod.samples; return TRUE; } } @@ -1607,12 +1581,12 @@ void ReaderTransmitBitsPar(uint8_t* frame, int bits, uint32_t par, uint32_t *tim CodeIso14443aBitsAsReaderPar(frame,bits,par); - // Select the card + // Send command to tag TransmitFor14443a(ToSend, ToSendMax, timing); if(trigger) LED_A_ON(); - // Store reader command in buffer + // Log reader command in trace buffer if (tracing) LogTrace(frame,nbytes(bits),0,par,TRUE); } @@ -1621,38 +1595,49 @@ void ReaderTransmitPar(uint8_t* frame, int len, uint32_t par, uint32_t *timing) ReaderTransmitBitsPar(frame,len*8,par, timing); } +void ReaderTransmitBits(uint8_t* frame, int len, uint32_t *timing) +{ + // Generate parity and redirect + ReaderTransmitBitsPar(frame,len,GetParity(frame,len/8), timing); +} + void ReaderTransmit(uint8_t* frame, int len, uint32_t *timing) { // Generate parity and redirect ReaderTransmitBitsPar(frame,len*8,GetParity(frame,len), timing); } +int ReaderReceiveOffset(uint8_t* receivedAnswer, uint16_t offset) +{ + int samples = 0; + if (!GetIso14443aAnswerFromTag(receivedAnswer,offset,160,&samples)) return FALSE; + if (tracing) LogTrace(receivedAnswer,Demod.len,samples,Demod.parityBits,FALSE); + if(samples == 0) return FALSE; + return Demod.len; +} + int ReaderReceive(uint8_t* receivedAnswer) { - int samples = 0; - if (!GetIso14443aAnswerFromTag(receivedAnswer,160,&samples,0)) return FALSE; - if (tracing) LogTrace(receivedAnswer,Demod.len,samples,Demod.parityBits,FALSE); - if(samples == 0) return FALSE; - return Demod.len; + return ReaderReceiveOffset(receivedAnswer, 0); } -int ReaderReceivePar(uint8_t* receivedAnswer, uint32_t * parptr) +int ReaderReceivePar(uint8_t *receivedAnswer, uint32_t *parptr) { - int samples = 0; - if (!GetIso14443aAnswerFromTag(receivedAnswer,160,&samples,0)) return FALSE; - if (tracing) LogTrace(receivedAnswer,Demod.len,samples,Demod.parityBits,FALSE); + int samples = 0; + if (!GetIso14443aAnswerFromTag(receivedAnswer,0,160,&samples)) return FALSE; + if (tracing) LogTrace(receivedAnswer,Demod.len,samples,Demod.parityBits,FALSE); *parptr = Demod.parityBits; - if(samples == 0) return FALSE; - return Demod.len; + if(samples == 0) return FALSE; + return Demod.len; } -/* performs iso14443a anticolision procedure +/* performs iso14443a anticollision procedure * fills the uid pointer unless NULL * fills resp_data unless NULL */ int iso14443a_select_card(byte_t* uid_ptr, iso14a_card_select_t* p_hi14a_card, uint32_t* cuid_ptr) { uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP uint8_t sel_all[] = { 0x93,0x20 }; - uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t rats[] = { 0xE0,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0 uint8_t* resp = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); // was 3560 - tied to other size changes byte_t uid_resp[4]; @@ -1666,7 +1651,7 @@ int iso14443a_select_card(byte_t* uid_ptr, iso14a_card_select_t* p_hi14a_card, u ReaderTransmitBitsPar(wupa,7,0, NULL); // Receive the ATQA if(!ReaderReceive(resp)) return 0; -// Dbprintf("atqa: %02x %02x",resp[0],resp[1]); + // Dbprintf("atqa: %02x %02x",resp[0],resp[1]); if(p_hi14a_card) { memcpy(p_hi14a_card->atqa, resp, 2); @@ -1690,19 +1675,50 @@ int iso14443a_select_card(byte_t* uid_ptr, iso14a_card_select_t* p_hi14a_card, u ReaderTransmit(sel_all,sizeof(sel_all), NULL); if (!ReaderReceive(resp)) return 0; - // First backup the current uid - memcpy(uid_resp,resp,4); - uid_resp_len = 4; + if (Demod.collisionPos) { // we had a collision and need to construct the UID bit by bit + memset(uid_resp, 0, 4); + uint16_t uid_resp_bits = 0; + uint16_t collision_answer_offset = 0; + // anti-collision-loop: + while (Demod.collisionPos) { + Dbprintf("Multiple tags detected. Collision after Bit %d", Demod.collisionPos); + for (uint16_t i = collision_answer_offset; i < Demod.collisionPos; i++, uid_resp_bits++) { // add valid UID bits before collision point + uint16_t UIDbit = (resp[i/8] >> (i % 8)) & 0x01; + uid_resp[uid_resp_bits & 0xf8] |= UIDbit << (uid_resp_bits % 8); + } + uid_resp[uid_resp_bits/8] |= 1 << (uid_resp_bits % 8); // next time select the card(s) with a 1 in the collision position + uid_resp_bits++; + // construct anticollosion command: + sel_uid[1] = ((2 + uid_resp_bits/8) << 4) | (uid_resp_bits & 0x07); // length of data in bytes and bits + for (uint16_t i = 0; i <= uid_resp_bits/8; i++) { + sel_uid[2+i] = uid_resp[i]; + } + collision_answer_offset = uid_resp_bits%8; + ReaderTransmitBits(sel_uid, 16 + uid_resp_bits, NULL); + if (!ReaderReceiveOffset(resp, collision_answer_offset)) return 0; + } + // finally, add the last bits and BCC of the UID + for (uint16_t i = collision_answer_offset; i < (Demod.len-1)*8; i++, uid_resp_bits++) { + uint16_t UIDbit = (resp[i/8] >> (i%8)) & 0x01; + uid_resp[uid_resp_bits/8] |= UIDbit << (uid_resp_bits % 8); + } + + } else { // no collision, use the response to SELECT_ALL as current uid + memcpy(uid_resp,resp,4); + } + uid_resp_len = 4; // Dbprintf("uid: %02x %02x %02x %02x",uid_resp[0],uid_resp[1],uid_resp[2],uid_resp[3]); - // calculate crypto UID. Always use last 4 Bytes. + // calculate crypto UID. Always use last 4 Bytes. if(cuid_ptr) { *cuid_ptr = bytes_to_num(uid_resp, 4); } // Construct SELECT UID command - memcpy(sel_uid+2,resp,5); - AppendCrc14443a(sel_uid,7); + sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) + memcpy(sel_uid+2,uid_resp,4); // the UID + sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC + AppendCrc14443a(sel_uid,7); // calculate and add CRC ReaderTransmit(sel_uid,sizeof(sel_uid), NULL); // Receive the SAK @@ -1710,7 +1726,7 @@ int iso14443a_select_card(byte_t* uid_ptr, iso14a_card_select_t* p_hi14a_card, u sak = resp[0]; // Test if more parts of the uid are comming - if ((sak & 0x04) && uid_resp[0] == 0x88) { + if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) { // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of: // http://www.nxp.com/documents/application_note/AN10927.pdf memcpy(uid_resp, uid_resp + 1, 3); @@ -1769,6 +1785,7 @@ void iso14443a_setup() { FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); SpinDelay(7); // iso14443-3 specifies 5ms max. + Demod.state = DEMOD_UNSYNCD; iso14a_timeout = 2048; //default } @@ -1815,6 +1832,7 @@ void ReaderIso14443a(UsbCommand * c) if(param & ISO14A_CONNECT) { iso14a_clear_trace(); } + iso14a_set_tracing(true); if(param & ISO14A_REQUEST_TRIGGER) { @@ -1976,8 +1994,6 @@ void ReaderMifare(bool first_try) //keep the card active FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - // CodeIso14443aBitsAsReaderPar(mf_auth, sizeof(mf_auth)*8, GetParity(mf_auth, sizeof(mf_auth)*8)); - sync_time = (sync_time & 0xfffffff8) + sync_cycles + catch_up_cycles; catch_up_cycles = 0; @@ -2645,7 +2661,7 @@ void RAMFUNC SniffMifare(uint8_t param) { Demod.state = DEMOD_UNSYNCD; } - if(ManchesterDecoding(data[0] & 0x0F)) { + if(ManchesterDecoding(data[0], 0)) { LED_C_INV(); if (MfSniffLogic(receivedResponse, Demod.len, Demod.parityBits, Demod.bitCount, FALSE)) break; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 4c3c6674..2a2b3403 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -22,32 +22,22 @@ #define CARD_MEMORY 6000 #define CARD_MEMORY_LEN 4096 -typedef struct nestedVector { uint32_t nt, ks1; } nestedVector; - typedef struct { enum { DEMOD_UNSYNCD, - DEMOD_START_OF_COMMUNICATION, - DEMOD_MANCHESTER_D, - DEMOD_MANCHESTER_E, - DEMOD_MANCHESTER_F, - DEMOD_ERROR_WAIT - } state; - int bitCount; - int posCount; - int syncBit; - int parityBits; - uint16_t shiftReg; - int buffer; - int buff; - int samples; - int len; - enum { - SUB_NONE, - SUB_FIRST_HALF, - SUB_SECOND_HALF - } sub; - uint8_t *output; + DEMOD_HALF_SYNCD, + DEMOD_MOD_FIRST_HALF, + DEMOD_NOMOD_FIRST_HALF, + DEMOD_MANCHESTER_DATA + } state; + uint16_t bitCount; + uint16_t collisionPos; + uint16_t syncBit; + uint16_t parityBits; + uint16_t shiftReg; + uint16_t samples; + uint16_t len; + uint8_t *output; } tDemod; typedef struct { @@ -79,18 +69,18 @@ typedef struct { extern byte_t oddparity (const byte_t bt); -extern uint32_t GetParity(const uint8_t * pbtCmd, int iLen); -extern void AppendCrc14443a(uint8_t* data, int len); +extern uint32_t GetParity(const uint8_t *pbtCmd, int iLen); +extern void AppendCrc14443a(uint8_t *data, int len); -extern void ReaderTransmit(uint8_t* frame, int len, uint32_t *timing); -extern void ReaderTransmitBitsPar(uint8_t* frame, int bits, uint32_t par, uint32_t *timing); -extern void ReaderTransmitPar(uint8_t* frame, int len, uint32_t par, uint32_t *timing); -extern int ReaderReceive(uint8_t* receivedAnswer); -extern int ReaderReceivePar(uint8_t* receivedAnswer, uint32_t * parptr); +extern void ReaderTransmit(uint8_t *frame, int len, uint32_t *timing); +extern void ReaderTransmitBitsPar(uint8_t *frame, int bits, uint32_t par, uint32_t *timing); +extern void ReaderTransmitPar(uint8_t *frame, int len, uint32_t par, uint32_t *timing); +extern int ReaderReceive(uint8_t *receivedAnswer); +extern int ReaderReceivePar(uint8_t *receivedAnswer, uint32_t *parptr); extern void iso14443a_setup(); -extern int iso14_apdu(uint8_t * cmd, size_t cmd_len, void * data); -extern int iso14443a_select_card(uint8_t * uid_ptr, iso14a_card_select_t * resp_data, uint32_t * cuid_ptr); +extern int iso14_apdu(uint8_t *cmd, size_t cmd_len, void *data); +extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr); extern void iso14a_set_trigger(bool enable); extern void iso14a_set_timeout(uint32_t timeout); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index f7e0ffa9..2c5fd1c0 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -101,7 +101,7 @@ int CmdHF14AList(const char *Cmd) char *crc; crc = ""; if (len > 2) { - uint8_t b1, b2; + uint8_t b1, b2; for (j = 0; j < (len - 1); j++) { // gives problems... search for the reason.. /*if(frame[j] == 0xAA) { @@ -132,8 +132,8 @@ int CmdHF14AList(const char *Cmd) crc = (isResponse & (len < 6)) ? "" : " !crc"; } else { crc = ""; - } - } + } + } } else { crc = ""; // SHORT } @@ -148,7 +148,7 @@ int CmdHF14AList(const char *Cmd) PrintAndLog(" +%7d: %s: %s %s %s", (prev < 0 ? 0 : (timestamp - prev)), metricString, - (isResponse ? "TAG" : " "), line, crc); + (isResponse ? "TAG " : " "), line, crc); prev = timestamp; i += (len + 9); @@ -184,12 +184,12 @@ int CmdHF14AReader(const char *Cmd) case 0x00: PrintAndLog("TYPE : NXP MIFARE Ultralight | Ultralight C"); break; case 0x04: PrintAndLog("TYPE : NXP MIFARE (various !DESFire !DESFire EV1)"); break; - case 0x08: PrintAndLog("TYPE : NXP MIFARE CLASSIC 1k | Plus 2k"); break; + case 0x08: PrintAndLog("TYPE : NXP MIFARE CLASSIC 1k | Plus 2k SL1"); break; case 0x09: PrintAndLog("TYPE : NXP MIFARE Mini 0.3k"); break; - case 0x10: PrintAndLog("TYPE : NXP MIFARE Plus 2k"); break; - case 0x11: PrintAndLog("TYPE : NXP MIFARE Plus 4k"); break; - case 0x18: PrintAndLog("TYPE : NXP MIFARE Classic 4k | Plus 4k"); break; - case 0x20: PrintAndLog("TYPE : NXP MIFARE DESFire 4k | DESFire EV1 2k/4k/8k | Plus 2k/4k | JCOP 31/41"); break; + case 0x10: PrintAndLog("TYPE : NXP MIFARE Plus 2k SL2"); break; + case 0x11: PrintAndLog("TYPE : NXP MIFARE Plus 4k SL2"); break; + case 0x18: PrintAndLog("TYPE : NXP MIFARE Classic 4k | Plus 4k SL1"); break; + case 0x20: PrintAndLog("TYPE : NXP MIFARE DESFire 4k | DESFire EV1 2k/4k/8k | Plus 2k/4k SL3 | JCOP 31/41"); break; case 0x24: PrintAndLog("TYPE : NXP MIFARE DESFire | DESFire EV1"); break; case 0x28: PrintAndLog("TYPE : JCOP31 or JCOP41 v2.3.1"); break; case 0x38: PrintAndLog("TYPE : Nokia 6212 or 6131 MIFARE CLASSIC 4K"); break; diff --git a/fpga/Makefile b/fpga/Makefile index 8759c22c..12aeaaae 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -12,11 +12,11 @@ fpga.ngc: fpga.v fpga.ucf xst.scr util.v lo_edge_detect.v lo_read.v lo_passthru. fpga.ngd: fpga.ngc $(DELETE) fpga.ngd - $(XILINX_TOOLS_PREFIX)ngdbuild -aul -p xc2s30-6vq100 -nt timestamp -uc fpga.ucf fpga.ngc fpga.ngd + $(XILINX_TOOLS_PREFIX)ngdbuild -aul -p xc2s30-5-vq100 -nt timestamp -uc fpga.ucf fpga.ngc fpga.ngd fpga.ncd: fpga.ngd $(DELETE) fpga.ncd - $(XILINX_TOOLS_PREFIX)map -p xc2s30-6vq100 fpga.ngd + $(XILINX_TOOLS_PREFIX)map -p xc2s30-5-vq100 fpga.ngd fpga-placed.ncd: fpga.ncd $(DELETE) fpga-placed.ncd diff --git a/fpga/fpga.bit b/fpga/fpga.bit index 3ea1560ddb9e7ebc124ad9d5471441d3df5b09d5..e7d6270779a2e87db920d2fd16bbd18ddaca0b66 100644 GIT binary patch literal 42172 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 literal 42172 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 Cu5t7L diff --git a/fpga/fpga.ucf b/fpga/fpga.ucf index 35f38e73..f20e2da0 100644 --- a/fpga/fpga.ucf +++ b/fpga/fpga.ucf @@ -39,3 +39,16 @@ NET "ssp_frame" LOC = "P31" ; #PACE: Start of PACE Prohibit Constraints #PACE: End of Constraints generated by PACE + +# definition of Clock nets: +NET "ck_1356meg" TNM_NET = "clk_net_1356" ; +NET "ck_1356megb" TNM_NET = "clk_net_1356b" ; +NET "pck0" TNM_NET = "clk_net_pck0" ; +NET "spck" TNM_NET = "clk_net_spck" ; + +# Timing specs of clock nets: +TIMEGRP "clk_net_1356_all" = "clk_net_1356" "clk_net_1356b" ; +TIMESPEC "TS_1356MHz" = PERIOD "clk_net_1356_all" 74 ns HIGH 37 ns ; +TIMESPEC "TS_24MHz" = PERIOD "clk_net_pck0" 42 ns HIGH 21 ns ; +TIMESPEC "TS_4MHz" = PERIOD "clk_net_spck" 250 ns HIGH 125 ns ; + diff --git a/fpga/fpga.v b/fpga/fpga.v index d2d84a32..a083ae5c 100644 --- a/fpga/fpga.v +++ b/fpga/fpga.v @@ -22,17 +22,17 @@ `include "util.v" module fpga( - spcki, miso, mosi, ncs, - pck0i, ck_1356meg, ck_1356megb, + spck, miso, mosi, ncs, + pck0, ck_1356meg, ck_1356megb, pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, adc_d, adc_clk, adc_noe, ssp_frame, ssp_din, ssp_dout, ssp_clk, cross_hi, cross_lo, dbg ); - input spcki, mosi, ncs; + input spck, mosi, ncs; output miso; - input pck0i, ck_1356meg, ck_1356megb; + input pck0, ck_1356meg, ck_1356megb; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; input [7:0] adc_d; output adc_clk, adc_noe; @@ -42,15 +42,17 @@ module fpga( output dbg; //assign pck0 = pck0i; - IBUFG #(.IOSTANDARD("DEFAULT") ) pck0b( - .O(pck0), - .I(pck0i) - ); +// IBUFG #(.IOSTANDARD("DEFAULT") ) pck0b( +// .O(pck0), +// .I(pck0i) +// ); //assign spck = spcki; - IBUFG #(.IOSTANDARD("DEFAULT") ) spckb( - .O(spck), - .I(spcki) - ); +// IBUFG #(.IOSTANDARD("DEFAULT") ) spckb( + // .O(spck), + // .I(spcki) +// ); + + //----------------------------------------------------------------------------- // The SPI receiver. This sets up the configuration word, which the rest of // the logic looks at to determine how to connect the A/D and the coil @@ -68,8 +70,8 @@ reg [7:0] conf_word; always @(posedge ncs) begin case(shift_reg[15:12]) - 4'b0001: conf_word <= shift_reg[7:0]; - 4'b0010: divisor <= shift_reg[7:0]; + 4'b0001: conf_word <= shift_reg[7:0]; // FPGA_CMD_SET_CONFREG + 4'b0010: divisor <= shift_reg[7:0]; // FPGA_CMD_SET_DIVISOR endcase end @@ -202,7 +204,7 @@ hi_iso14443a hisn( mux8 mux_ssp_clk (major_mode, ssp_clk, lr_ssp_clk, ls_ssp_clk, ht_ssp_clk, hrxc_ssp_clk, hs_ssp_clk, hisn_ssp_clk, lp_ssp_clk, 1'b0); mux8 mux_ssp_din (major_mode, ssp_din, lr_ssp_din, ls_ssp_din, ht_ssp_din, hrxc_ssp_din, hs_ssp_din, hisn_ssp_din, lp_ssp_din, 1'b0); -mux8 mux_ssp_frame (major_mode, ssp_frame, lr_ssp_frame, ls_ssp_frame, ht_ssp_frame, hrxc_ssp_frame, hs_ssp_frame, hisn_ssp_frame, lp_ssp_frame, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, lr_ssp_frame, ls_ssp_frame, ht_ssp_frame, hrxc_ssp_frame, hs_ssp_frame, hisn_ssp_frame, lp_ssp_frame, 1'b0); mux8 mux_pwr_oe1 (major_mode, pwr_oe1, lr_pwr_oe1, ls_pwr_oe1, ht_pwr_oe1, hrxc_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, lp_pwr_oe1, 1'b0); mux8 mux_pwr_oe2 (major_mode, pwr_oe2, lr_pwr_oe2, ls_pwr_oe2, ht_pwr_oe2, hrxc_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, lp_pwr_oe2, 1'b0); mux8 mux_pwr_oe3 (major_mode, pwr_oe3, lr_pwr_oe3, ls_pwr_oe3, ht_pwr_oe3, hrxc_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, lp_pwr_oe3, 1'b0); @@ -210,7 +212,7 @@ mux8 mux_pwr_oe4 (major_mode, pwr_oe4, lr_pwr_oe4, ls_pwr_oe4, ht_pwr_oe4 mux8 mux_pwr_lo (major_mode, pwr_lo, lr_pwr_lo, ls_pwr_lo, ht_pwr_lo, hrxc_pwr_lo, hs_pwr_lo, hisn_pwr_lo, lp_pwr_lo, 1'b0); mux8 mux_pwr_hi (major_mode, pwr_hi, lr_pwr_hi, ls_pwr_hi, ht_pwr_hi, hrxc_pwr_hi, hs_pwr_hi, hisn_pwr_hi, lp_pwr_hi, 1'b0); mux8 mux_adc_clk (major_mode, adc_clk, lr_adc_clk, ls_adc_clk, ht_adc_clk, hrxc_adc_clk, hs_adc_clk, hisn_adc_clk, lp_adc_clk, 1'b0); -mux8 mux_dbg (major_mode, dbg, lr_dbg, ls_dbg, ht_dbg, hrxc_dbg, hs_dbg, hisn_dbg, lp_dbg, 1'b0); +mux8 mux_dbg (major_mode, dbg, lr_dbg, ls_dbg, ht_dbg, hrxc_dbg, hs_dbg, hisn_dbg, lp_dbg, 1'b0); // In all modes, let the ADC's outputs be enabled. assign adc_noe = 1'b0; diff --git a/fpga/hi_iso14443a.v b/fpga/hi_iso14443a.v index eb03fa23..1009c436 100644 --- a/fpga/hi_iso14443a.v +++ b/fpga/hi_iso14443a.v @@ -39,8 +39,8 @@ reg [2:0] deep_counter; reg deep_modulation; always @(negedge adc_clk) begin - if(& adc_d[7:6]) after_hysteresis <= 1'b1; - else if(~(| adc_d[7:4])) after_hysteresis <= 1'b0; + if(& adc_d[7:6]) after_hysteresis <= 1'b1; // if adc_d >= 196 + else if(~(| adc_d[7:4])) after_hysteresis <= 1'b0; // if adc_d <= 15 if(~(| adc_d[7:0])) begin @@ -83,20 +83,34 @@ reg [5:0] negedge_cnt; reg bit1, bit2, bit3; reg [3:0] count_ones; reg [3:0] count_zeros; -wire [7:0] avg; -reg [7:0] lavg; -reg signed [12:0] step1; -reg signed [12:0] step2; -reg [7:0] stepsize; +// wire [7:0] avg; +// reg [7:0] lavg; +// reg signed [12:0] step1; +// reg signed [12:0] step2; +// reg [7:0] stepsize; +reg [7:0] rx_mod_edge_threshold; reg curbit; -reg [12:0] average; -wire signed [9:0] dif; +// reg [12:0] average; +// wire signed [9:0] dif; + +// storage for two previous samples: +reg [7:0] adc_d_1; +reg [7:0] adc_d_2; +reg [7:0] adc_d_3; +reg [7:0] adc_d_4; + +// the filtered signal (filter performs noise reduction and edge detection) +// (gaussian derivative) +wire signed [10:0] adc_d_filtered; +assign adc_d_filtered = (adc_d_4 << 1) + adc_d_3 - adc_d_1 - (adc_d << 1); + +// Registers to store steepest edges detected: +reg [7:0] rx_mod_falling_edge_max; +reg [7:0] rx_mod_rising_edge_max; // A register to send the results to the arm reg signed [7:0] to_arm; -assign avg[7:0] = average[11:4]; -assign dif = lavg - avg; reg bit_to_arm; reg fdt_indicator, fdt_elapsed; @@ -115,36 +129,67 @@ reg [2:0] ssp_frame_counter; // ADC data appears on the rising edge, so sample it on the falling edge always @(negedge adc_clk) begin - - // last bit = 0 then fdt = 1172, in case of 0x26 (7-bit command, LSB first!) - // last bit = 1 then fdt = 1236, in case of 0x52 (7-bit command, LSB first!) - if(fdt_counter == 11'd740) fdt_indicator = 1'b1; + // ------------------------------------------------------------------------------------------------------------------------------------------------------------------ + // relevant for TAGSIM_MOD only. Timing of Tag's answer to a command received from a reader + // ISO14443-3 specifies: + // fdt = 1172, if last bit was 0. + // fdt = 1236, if last bit was 1. + // the FPGA takes care for the 1172 delay. To achieve the additional 1236-1172=64 ticks delay, the ARM must send an additional correction bit (before the start bit). + // The correction bit will be coded as 00010000, i.e. it adds 4 bits to the transmission stream, causing the required delay. + if(fdt_counter == 11'd740) fdt_indicator = 1'b1; // fdt_indicator is true for 740 <= fdt_counter <= 1148. Ready to buffer data. (?) + // Shouldn' this be 1236 - 720 = 516? (The mod_sig_buf can buffer 46 data bits, + // i.e. a maximum delay of 46 * 16 = 720 adc_clk ticks) - if(fdt_counter == 11'd1148) + if(fdt_counter == 11'd1148) // additional 16 (+ eventual n*128) adc_clk_ticks delay will be added by the mod_sig_buf below + // the remaining 8 ticks delay comes from the 8 ticks timing difference between reseting fdt_counter and the mod_sig_buf clock. begin if(fdt_elapsed) begin - if(negedge_cnt[3:0] == mod_sig_flip[3:0]) mod_sig_coil <= mod_sig; + if(negedge_cnt[3:0] == mod_sig_flip[3:0]) mod_sig_coil <= mod_sig; // start modulating (if mod_sig is already set) end else begin - mod_sig_flip[3:0] <= negedge_cnt[3:0]; - mod_sig_coil <= mod_sig; + mod_sig_flip[3:0] <= negedge_cnt[3:0]; // exact timing of modulation + mod_sig_coil <= mod_sig; // modulate (if mod_sig is already set) fdt_elapsed = 1'b1; fdt_indicator = 1'b0; - if(~(| mod_sig_ptr[5:0])) mod_sig_ptr <= 6'b001001; - else temp_buffer_reset = 1'b1; // fix position of the buffer pointer + if(~(| mod_sig_ptr[5:0])) mod_sig_ptr <= 6'b001001; // didn't receive a 1 yet. Delay next 1 by n*128 ticks. + else temp_buffer_reset = 1'b1; // else fix the buffer size at current position end end else begin - fdt_counter <= fdt_counter + 1; + fdt_counter <= fdt_counter + 1; // Count until 1148 end - if(& negedge_cnt[3:0]) + + //------------------------------------------------------------------------------------------------------------------------------------------- + // Relevant for READER_LISTEN only + // look for steepest falling and rising edges: + if (adc_d_filtered > 0) + begin + if (adc_d_filtered > rx_mod_falling_edge_max) + rx_mod_falling_edge_max <= adc_d_filtered; + end + else + begin + if (-adc_d_filtered > rx_mod_rising_edge_max) + rx_mod_rising_edge_max <= -adc_d_filtered; + end + + // store previous samples for filtering and edge detection: + adc_d_4 <= adc_d_3; + adc_d_3 <= adc_d_2; + adc_d_2 <= adc_d_1; + adc_d_1 <= adc_d; + + + + if(& negedge_cnt[3:0]) // == 0xf == 15 begin - // When there is a dip in the signal and not in reader mode + // Relevant for TAGSIM_MOD only (timing Tag's answer. See above) + // When there is a dip in the signal and not in (READER_MOD, READER_LISTEN, TAGSIM_MOD) if(~after_hysteresis && mod_sig_buf_empty && ~((mod_type == 3'b100) || (mod_type == 3'b011) || (mod_type == 3'b010))) // last condition to prevent reset begin fdt_counter <= 11'd0; @@ -154,74 +199,33 @@ begin mod_sig_ptr <= 6'b000000; end - lavg <= avg; - - if(stepsize<16) stepsize = 8'd16; - - if(dif>0) - begin - step1 = dif*3; - step2 = stepsize*2; // 3:2 - if(step1>step2) - begin - curbit = 1'b0; - stepsize = dif; - end - end - else - begin - step1 = dif*3; - step1 = -step1; - step2 = stepsize*2; - if(step1>step2) - begin - curbit = 1'b1; - stepsize = -dif; - end - end - - if(curbit) - begin - count_zeros <= 4'd0; - if(& count_ones[3:2]) - begin - curbit = 1'b0; // suppressed signal - stepsize = 8'd24; // just a fine number - end + // Relevant for READER_LISTEN only + // detect modulation signal: if modulating, there must be a falling and a rising edge ... and vice versa + if (rx_mod_falling_edge_max > 6 && rx_mod_rising_edge_max > 6) + curbit = 1'b1; // modulation else - begin - count_ones <= count_ones + 1; - end - end - else - begin - count_ones <= 4'd0; - if(& count_zeros[3:0]) - begin - stepsize = 8'd24; - end - else - begin - count_zeros <= count_zeros + 1; - end - end - + curbit = 1'b0; // no modulation + + // prepare next edge detection: + rx_mod_rising_edge_max <= 0; + rx_mod_falling_edge_max <= 0; + + // What do we communicate to the ARM - if(mod_type == 3'b001) sendbit = after_hysteresis; - else if(mod_type == 3'b010) + if(mod_type == 3'b001) sendbit = after_hysteresis; // TAGSIM_LISTEN + else if(mod_type == 3'b010) // TAGSIM_MOD begin if(fdt_counter > 11'd772) sendbit = mod_sig_coil; else sendbit = fdt_indicator; end - else if(mod_type == 3'b011) sendbit = curbit; - else sendbit = 1'b0; + else if(mod_type == 3'b011) sendbit = curbit; // READER_LISTEN + else sendbit = 1'b0; // READER_MOD, SNIFFER end - if(~(| negedge_cnt[3:0])) average <= adc_d; - else average <= average + adc_d; - - if(negedge_cnt == 7'd63) + //------------------------------------------------------------------------------------------------------------------------------------------ + // Relevant for SNIFFER mode only. Prepare communication to ARM. + if(negedge_cnt == 7'd63) begin if(deep_modulation) begin @@ -234,7 +238,7 @@ begin negedge_cnt <= 0; - end + end else begin negedge_cnt <= negedge_cnt + 1; @@ -256,35 +260,48 @@ begin bit3 <= curbit; end - - if(mod_type != 3'b000) + //-------------------------------------------------------------------------------------------------------------------------------------------------------------- + // Relevant in TAGSIM_MOD only. Delay-Line to buffer data and send it at the correct time + // Note: Data in READER_MOD is fed through this delay line as well. + if(mod_type != 3'b000) // != SNIFFER begin - if(negedge_cnt[3:0] == 4'b1000) + if(negedge_cnt[3:0] == 4'b1000) // == 0x8 begin - // The modulation signal of the tag - mod_sig_buf[47:0] <= {mod_sig_buf[46:1], ssp_dout, 1'b0}; - if((ssp_dout || (| mod_sig_ptr[5:0])) && ~fdt_elapsed) - if(mod_sig_ptr == 6'b101110) + // The modulation signal of the tag. The delay line is only relevant for TAGSIM_MOD, but used in other modes as well. + // Note: this means that even in READER_MOD, there will be an arbitrary delay depending on the time of a previous reset of fdt_counter and the time and + // content of the next bit to be transmitted. + mod_sig_buf[47:0] <= {mod_sig_buf[46:1], ssp_dout, 1'b0}; // shift in new data starting at mod_sig_buf[1]. mod_sig_buf[0] = 0 always. + if((ssp_dout || (| mod_sig_ptr[5:0])) && ~fdt_elapsed) // buffer a 1 (and all subsequent data) until fdt_counter = 1148 adc_clk ticks. + if(mod_sig_ptr == 6'b101110) // buffer overflow at 46 - this would mean data loss begin mod_sig_ptr <= 6'b000000; end - else mod_sig_ptr <= mod_sig_ptr + 1; - else if(fdt_elapsed && ~temp_buffer_reset) + else mod_sig_ptr <= mod_sig_ptr + 1; // increase buffer (= increase delay by 16 adc_clk ticks). ptr always points to first 1. + else if(fdt_elapsed && ~temp_buffer_reset) + // fdt_elapsed. If we didn't receive a 1 yet, ptr will be at 9 and not yet fixed. Otherwise temp_buffer_reset will be 1 already. begin - if(ssp_dout) temp_buffer_reset = 1'b1; - if(mod_sig_ptr == 6'b000010) mod_sig_ptr <= 6'b001001; - else mod_sig_ptr <= mod_sig_ptr - 1; + // wait for the next 1 after fdt_elapsed before fixing the delay and starting modulation. This ensures that the response can only happen + // at intervals of 8 * 16 = 128 adc_clk ticks intervals (as defined in ISO14443-3) + if(ssp_dout) temp_buffer_reset = 1'b1; + if(mod_sig_ptr == 6'b000010) mod_sig_ptr <= 6'b001001; // still nothing received, need to go for the next interval + else mod_sig_ptr <= mod_sig_ptr - 1; // decrease buffer. end else + // mod_sig_ptr and therefore the delay is now fixed until fdt_counter is reset (this can happen in SNIFFER and TAGSIM_LISTEN mode only. Note that SNIFFER + // mode (3'b000) is the default and is active in FPGA_MAJOR_MODE_OFF if no other minor mode is explicitly requested. begin - // side effect: when ptr = 1 it will cancel the first 1 of every block of ones + // don't modulate with the correction bit (which is sent as 00010000, all other bits will come with at least 2 consecutive 1s) + // side effect: when ptr = 1 it will cancel the first 1 of every block of ones. Note: this would only be the case if we received a 1 just before fdt_elapsed. if(~mod_sig_buf[mod_sig_ptr-1] && ~mod_sig_buf[mod_sig_ptr+1]) mod_sig = 1'b0; - else mod_sig = mod_sig_buf[mod_sig_ptr] & fdt_elapsed; // & fdt_elapsed was for direct relay to oe4 + // finally, do the modulation: + else mod_sig = mod_sig_buf[mod_sig_ptr] & fdt_elapsed; end end end - // SSP Clock and data + //----------------------------------------------------------------------------------------------------------------------------------------------------------------------- + // Communication to ARM (SSP Clock and data) + // SNIFFER mode (ssp_clk = adc_clk / 8, ssp_frame clock = adc_clk / 64)): if(mod_type == 3'b000) begin if(negedge_cnt[2:0] == 3'b100) @@ -308,6 +325,9 @@ begin bit_to_arm = to_arm[7]; end else + //----------------------------------------------------------------------------------------------------------------------------------------------------------------------- + // Communication to ARM (SSP Clock and data) + // all other modes (ssp_clk = adc_clk / 16, ssp_frame clock = adc_clk / 128): begin if(negedge_cnt[3:0] == 4'b1000) ssp_clk <= 1'b0; @@ -331,30 +351,29 @@ end assign ssp_din = bit_to_arm; -// Modulating carrier frequency is fc/16 + +// Modulating carrier (adc_clk/16, for TAGSIM_MOD only). Will be 0 for other modes. wire modulating_carrier; -assign modulating_carrier = (mod_sig_coil & negedge_cnt[3] & (mod_type == 3'b010)); -assign pwr_hi = (ck_1356megb & (((mod_type == 3'b100) & ~mod_sig_coil) || (mod_type == 3'b011))); +assign modulating_carrier = (mod_sig_coil & negedge_cnt[3] & (mod_type == 3'b010)); // in TAGSIM_MOD only. Otherwise always 0. + +// for READER_MOD only: drop carrier for mod_sig_coil==1 (pause), READER_LISTEN: carrier always on, others: carrier always off +assign pwr_hi = (ck_1356megb & (((mod_type == 3'b100) & ~mod_sig_coil) || (mod_type == 3'b011))); -// This one is all LF, so doesn't matter -//assign pwr_oe2 = modulating_carrier; -assign pwr_oe2 = 1'b0; -// Toggle only one of these, since we are already producing much deeper -// modulation than a real tag would. -//assign pwr_oe1 = modulating_carrier; +// Enable HF antenna drivers: assign pwr_oe1 = 1'b0; +assign pwr_oe3 = 1'b0; + +// TAGSIM_MOD: short circuit antenna with different resistances (modulated by modulating_carrier) +// for pwr_oe4 = 1 (tristate): antenna load = 10k || 33 = 32,9 Ohms +// for pwr_oe4 = 0 (active): antenna load = 10k || 33 || 33 = 16,5 Ohms assign pwr_oe4 = modulating_carrier; -//assign pwr_oe4 = 1'b0; -// This one is always on, so that we can watch the carrier. -//assign pwr_oe3 = modulating_carrier; -assign pwr_oe3 = 1'b0; +// This is all LF, so doesn't matter. +assign pwr_oe2 = 1'b0; +assign pwr_lo = 1'b0; assign dbg = negedge_cnt[3]; -// Unused. -assign pwr_lo = 1'b0; - endmodule diff --git a/fpga/xst.scr b/fpga/xst.scr index 60d24c64..406bbeee 100644 --- a/fpga/xst.scr +++ b/fpga/xst.scr @@ -1 +1 @@ -run -ifn fpga.v -ifmt Verilog -ofn fpga.ngc -ofmt NGC -p xc2s30-6vq100 -opt_mode Speed -opt_level 1 -ent fpga +run -ifn fpga.v -ifmt Verilog -ofn fpga.ngc -ofmt NGC -p xc2s30-5-vq100 -opt_mode Speed -opt_level 1 -ent fpga -- 2.39.2