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 | 440 +++++++++++++++++++++++--------------------- 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 | 251 +++++++++++++------------ fpga/xst.scr | 2 +- 9 files changed, 428 insertions(+), 388 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 63cc32aec..00dc622fd 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 (b >= 5 || b == 3) // majority decision: 2 or more bits are set + return true; + else + return false; + +} - if(!Demod.buff) { - Demod.buff = 1; - Demod.buffer = v; - return FALSE; - } - else { - bit = Demod.buffer; - Demod.buffer = v; - } +inline RAMFUNC bool IsModulationNibble1(byte_t b) +{ + return IsModulation((b & 0xE0) >> 5); +} - 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 IsModulationNibble2(byte_t b) +{ + return IsModulation((b & 0x0E) >> 1); +} - if(bit & 0x08) { - Demod.syncBit = 0x08; - } +static RAMFUNC int ManchesterDecoding(int bit, uint16_t offset) +{ + + switch (Demod.state) { - if(bit & 0x04) { - if(Demod.syncBit) { - bit <<= 4; - } - Demod.syncBit = 0x04; - } - - 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) { + 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(); - 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; + 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; + } + 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; - } - else { - Demod.sub = SUB_NONE; - } - } - 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_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; } - } - else if(modulation) { - Demod.sub = SUB_SECOND_HALF; - } - - 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; - } - else if(Demod.sub == SUB_SECOND_HALF) { - Demod.bitCount++; - Demod.shiftReg >>= 1; - Demod.state = DEMOD_MANCHESTER_E; - } - else { - Demod.state = DEMOD_MANCHESTER_F; - } - 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); - + } // 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; } + 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; - /*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; - }*/ - } + 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 + } + 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; + - } // end (state != UNSYNCED) + 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; + } + } // 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 { // 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; + } + Demod.state = DEMOD_UNSYNCD; // start from the beginning + return TRUE; // we are finished with decoding the raw data sequence + } + } + + } - 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 4c3c66740..2a2b3403b 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 f7e0ffa99..2c5fd1c02 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 8759c22ca..12aeaaaec 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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 diff --git a/fpga/fpga.ucf b/fpga/fpga.ucf index 35f38e736..f20e2da02 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 d2d84a32e..a083ae5cf 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 eb03fa239..1009c4365 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. -// This one is all LF, so doesn't matter -//assign pwr_oe2 = modulating_carrier; -assign pwr_oe2 = 1'b0; +// 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))); -// 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_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; +// 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; + +// 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 60d24c64b..406bbeee7 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