From 51d4f6f1146b083f12381419bbfb6addd550e6a3 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 12 Jun 2015 07:43:00 +0200 Subject: [PATCH] fixing iso14443b (issue #103): - fix: IQ demodulator (FPGA) - fix: approximately align reader signal delay to tag response delay (FPGA) - fix: remove deprecated RSSI calculation to improve decoder speed (iso14443b.c) - fix: better approximation of signal amplitude to avoid false carrier detection (iso14443b.c) - fix: remove initial power off in iso14443b raw command (iso14443b.c) - add: enable tracing for iso14443b raw command (iso14443b.c) - fix: client crashed when checking CRC for incomplete responses (iso14433b.c) - speeding up snoop to avoid circular buffer overflow - added some comments for better documentation - rename functions (iso14443 -> iso14443b) - remove unused code in hi_read_rx_xcorr.v --- armsrc/appmain.c | 12 +- armsrc/apps.h | 8 +- armsrc/iso14443b.c | 476 ++++++++++++++++++++-------------------- client/cmdhf14b.c | 14 +- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/fpga_hf.v | 11 +- fpga/hi_read_rx_xcorr.v | 78 ++----- 7 files changed, 279 insertions(+), 320 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index c226c726..9bfa5ea7 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -263,7 +263,7 @@ void SimulateTagHfListen(void) // We're using this mode just so that I can test it out; the simulated // tag mode would work just as well and be simpler. FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_SNOOP); // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -783,19 +783,19 @@ void UsbPacketReceived(uint8_t *packet, int len) #ifdef WITH_ISO14443b case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443: - AcquireRawAdcSamplesIso14443(c->arg[0]); + AcquireRawAdcSamplesIso14443b(c->arg[0]); break; case CMD_READ_SRI512_TAG: - ReadSTMemoryIso14443(0x0F); + ReadSTMemoryIso14443b(0x0F); break; case CMD_READ_SRIX4K_TAG: - ReadSTMemoryIso14443(0x7F); + ReadSTMemoryIso14443b(0x7F); break; case CMD_SNOOP_ISO_14443: - SnoopIso14443(); + SnoopIso14443b(); break; case CMD_SIMULATE_TAG_ISO_14443: - SimulateIso14443Tag(); + SimulateIso14443bTag(); break; case CMD_ISO_14443B_COMMAND: SendRawCommand14443B(c->arg[0],c->arg[1],c->arg[2],c->d.asBytes); diff --git a/armsrc/apps.h b/armsrc/apps.h index 6360b664..542f3a65 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -141,10 +141,10 @@ void EM4xReadWord(uint8_t Address, uint32_t Pwd, uint8_t PwdMode); void EM4xWriteWord(uint32_t Data, uint8_t Address, uint32_t Pwd, uint8_t PwdMode); /// iso14443.h -void SimulateIso14443Tag(void); -void AcquireRawAdcSamplesIso14443(uint32_t parameter); -void ReadSTMemoryIso14443(uint32_t); -void RAMFUNC SnoopIso14443(void); +void SimulateIso14443bTag(void); +void AcquireRawAdcSamplesIso14443b(uint32_t parameter); +void ReadSTMemoryIso14443b(uint32_t); +void RAMFUNC SnoopIso14443b(void); void SendRawCommand14443B(uint32_t, uint32_t, uint8_t, uint8_t[]); /// iso14443a.h diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 4e40bb68..f598df3c 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -5,9 +5,8 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Routines to support ISO 14443. This includes both the reader software and -// the `fake tag' modes. At the moment only the Type B modulation is -// supported. +// Routines to support ISO 14443B. This includes both the reader software and +// the `fake tag' modes. //----------------------------------------------------------------------------- #include "proxmark3.h" @@ -17,15 +16,8 @@ #include "iso14443crc.h" -//static void GetSamplesFor14443(int weTx, int n); - -/*#define DEMOD_TRACE_SIZE 4096 -#define READER_TAG_BUFFER_SIZE 2048 -#define TAG_READER_BUFFER_SIZE 2048 -#define DEMOD_DMA_BUFFER_SIZE 1024 -*/ - #define RECEIVE_SAMPLES_TIMEOUT 2000 +#define ISO14443B_DMA_BUFFER_SIZE 512 //============================================================================= // An ISO 14443 Type B tag. We listen for commands from the reader, using @@ -104,14 +96,14 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) ToSendStuffBit(1); } - // Send SOF. + // Send EOF. for(i = 0; i < 10; i++) { ToSendStuffBit(0); ToSendStuffBit(0); ToSendStuffBit(0); ToSendStuffBit(0); } - for(i = 0; i < 10; i++) { + for(i = 0; i < 2; i++) { ToSendStuffBit(1); ToSendStuffBit(1); ToSendStuffBit(1); @@ -120,9 +112,6 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) // Convert from last byte pos to length ToSendMax++; - - // Add a few more for slop - ToSendMax += 2; } //----------------------------------------------------------------------------- @@ -146,6 +135,9 @@ static struct { } Uart; /* Receive & handle a bit coming from the reader. + * + * This function is called 4 times per bit (every 2 subcarrier cycles). + * Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 2,36us * * LED handling: * LED A -> ON once we have received the SOF and are expecting the rest. @@ -154,7 +146,7 @@ static struct { * Returns: true if we received a EOF * false if we are still waiting for some more */ -static int Handle14443UartBit(int bit) +static int Handle14443bUartBit(int bit) { switch(Uart.state) { case STATE_UNSYNCD: @@ -169,9 +161,9 @@ static int Handle14443UartBit(int bit) case STATE_GOT_FALLING_EDGE_OF_SOF: Uart.posCnt++; - if(Uart.posCnt == 2) { + if(Uart.posCnt == 2) { // sample every 4 1/fs in the middle of a bit if(bit) { - if(Uart.bitCnt >= 10) { + if(Uart.bitCnt > 9) { // we've seen enough consecutive // zeros that it's a valid SOF Uart.posCnt = 0; @@ -189,7 +181,7 @@ static int Handle14443UartBit(int bit) Uart.bitCnt++; } if(Uart.posCnt >= 4) Uart.posCnt = 0; - if(Uart.bitCnt > 14) { + if(Uart.bitCnt > 12) { // Give up if we see too many zeros without // a one, too. Uart.state = STATE_ERROR_WAIT; @@ -199,7 +191,7 @@ static int Handle14443UartBit(int bit) case STATE_AWAITING_START_BIT: Uart.posCnt++; if(bit) { - if(Uart.posCnt > 25) { + if(Uart.posCnt > 50/2) { // max 57us between characters = 49 1/fs, max 3 etus after low phase of SOF = 24 1/fs // stayed high for too long between // characters, error Uart.state = STATE_ERROR_WAIT; @@ -283,12 +275,12 @@ static int Handle14443UartBit(int bit) // Assume that we're called with the SSC (to the FPGA) and ADC path set // correctly. //----------------------------------------------------------------------------- -static int GetIso14443CommandFromReader(uint8_t *received, int *len, int maxLen) +static int GetIso14443bCommandFromReader(uint8_t *received, int *len, int maxLen) { uint8_t mask; int i, bit; - // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen + // Set FPGA mode to "simulated ISO 14443B tag", no modulation (listen // only, since we are receiving, not transmitting). // Signal field is off with the appropriate LED LED_D_OFF(); @@ -314,7 +306,7 @@ static int GetIso14443CommandFromReader(uint8_t *received, int *len, int maxLen) mask = 0x80; for(i = 0; i < 8; i++, mask >>= 1) { bit = (b & mask); - if(Handle14443UartBit(bit)) { + if(Handle14443bUartBit(bit)) { *len = Uart.byteCnt; return TRUE; } @@ -327,9 +319,13 @@ static int GetIso14443CommandFromReader(uint8_t *received, int *len, int maxLen) // Main loop of simulated tag: receive commands from reader, decide what // response to send, and send it. //----------------------------------------------------------------------------- -void SimulateIso14443Tag(void) +void SimulateIso14443bTag(void) { + // the only command we understand is REQB, AFI=0, Select All, N=0: static const uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; + // ... and we respond with ATQB, PUPI = 820de174, Application Data = 0x20381922, + // supports only 106kBit/s in both directions, max frame size = 32Bytes, + // supports ISO14443-4, FWI=8 (77ms), NAD supported, CID not supported: static const uint8_t response1[] = { 0x50, 0x82, 0x0d, 0xe1, 0x74, 0x20, 0x38, 0x19, 0x22, 0x00, 0x21, 0x85, 0x5e, 0xd7 @@ -338,10 +334,9 @@ void SimulateIso14443Tag(void) uint8_t *resp; int respLen; - uint8_t *resp1 = BigBuf_get_addr() + 800; - int resp1Len; - - uint8_t *receivedCmd = BigBuf_get_addr(); + // allocate command receive buffer + BigBuf_free(); + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); int len; int i; @@ -349,10 +344,12 @@ void SimulateIso14443Tag(void) int cmdsRecvd = 0; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - memset(receivedCmd, 0x44, 400); + // prepare the (only one) tag answer: CodeIso14443bAsTag(response1, sizeof(response1)); - memcpy(resp1, ToSend, ToSendMax); resp1Len = ToSendMax; + uint8_t *resp1 = BigBuf_malloc(ToSendMax); + memcpy(resp1, ToSend, ToSendMax); + uint16_t resp1Len = ToSendMax; // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -363,14 +360,14 @@ void SimulateIso14443Tag(void) for(;;) { uint8_t b1, b2; - if(!GetIso14443CommandFromReader(receivedCmd, &len, 100)) { - Dbprintf("button pressed, received %d commands", cmdsRecvd); - break; - } + if(!GetIso14443bCommandFromReader(receivedCmd, &len, 100)) { + Dbprintf("button pressed, received %d commands", cmdsRecvd); + break; + } // Good, look at the command now. - if(len == sizeof(cmd1) && memcmp(receivedCmd, cmd1, len)==0) { + if(len == sizeof(cmd1) && memcmp(receivedCmd, cmd1, len) == 0) { resp = resp1; respLen = resp1Len; } else { Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsRecvd); @@ -385,8 +382,6 @@ void SimulateIso14443Tag(void) break; } - memset(receivedCmd, 0x44, 32); - cmdsRecvd++; if(cmdsRecvd > 0x30) { @@ -444,8 +439,10 @@ static struct { int bitCount; int posCount; int thisBit; +/* this had been used to add RSSI (Received Signal Strength Indication) to traces. Currently not implemented. int metric; int metricN; +*/ uint16_t shiftReg; uint8_t *output; int len; @@ -456,6 +453,9 @@ static struct { /* * Handles reception of a bit from the tag * + * This function is called 2 times per bit (every 4 subcarrier cycles). + * Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 4,72us + * * LED handling: * LED C -> ON once we have received the SOF and are expecting the rest. * LED C -> OFF once we have received EOF or are unsynced @@ -464,12 +464,12 @@ static struct { * false if we are still waiting for some more * */ -static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) +static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) { int v; - // The soft decision on the bit uses an estimate of just the - // quadrant of the reference angle, not the exact angle. +// The soft decision on the bit uses an estimate of just the +// quadrant of the reference angle, not the exact angle. #define MAKE_SOFT_DECISION() { \ if(Demod.sumI > 0) { \ v = ci; \ @@ -483,47 +483,87 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) } \ } +#define SUBCARRIER_DETECT_THRESHOLD 8 + +// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by abs(ci) + abs(cq) +/* #define CHECK_FOR_SUBCARRIER() { \ + v = ci; \ + if(v < 0) v = -v; \ + if(cq > 0) { \ + v += cq; \ + } else { \ + v -= cq; \ + } \ + } + */ +// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) +#define CHECK_FOR_SUBCARRIER() { \ + if(ci < 0) { \ + if(cq < 0) { /* ci < 0, cq < 0 */ \ + if (cq < ci) { \ + v = -cq - (ci >> 1); \ + } else { \ + v = -ci - (cq >> 1); \ + } \ + } else { /* ci < 0, cq >= 0 */ \ + if (cq < -ci) { \ + v = -ci + (cq >> 1); \ + } else { \ + v = cq - (ci >> 1); \ + } \ + } \ + } else { \ + if(cq < 0) { /* ci >= 0, cq < 0 */ \ + if (-cq < ci) { \ + v = ci - (cq >> 1); \ + } else { \ + v = -cq + (ci >> 1); \ + } \ + } else { /* ci >= 0, cq >= 0 */ \ + if (cq < ci) { \ + v = ci + (cq >> 1); \ + } else { \ + v = cq + (ci >> 1); \ + } \ + } \ + } \ + } + switch(Demod.state) { case DEMOD_UNSYNCD: - v = ci; - if(v < 0) v = -v; - if(cq > 0) { - v += cq; - } else { - v -= cq; - } - if(v > 40) { - Demod.posCount = 0; + CHECK_FOR_SUBCARRIER(); + if(v > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected Demod.state = DEMOD_PHASE_REF_TRAINING; - Demod.sumI = 0; - Demod.sumQ = 0; - } + Demod.sumI = ci; + Demod.sumQ = cq; + Demod.posCount = 1; + } break; case DEMOD_PHASE_REF_TRAINING: if(Demod.posCount < 8) { - Demod.sumI += ci; - Demod.sumQ += cq; - } else if(Demod.posCount > 100) { - // error, waited too long - Demod.state = DEMOD_UNSYNCD; - } else { - MAKE_SOFT_DECISION(); - if(v < 0) { - Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; - Demod.posCount = 0; + CHECK_FOR_SUBCARRIER(); + if (v > SUBCARRIER_DETECT_THRESHOLD) { + // set the reference phase (will code a logic '1') by averaging over 32 1/fs. + // note: synchronization time > 80 1/fs + Demod.sumI += ci; + Demod.sumQ += cq; + Demod.posCount++; + } else { // subcarrier lost + Demod.state = DEMOD_UNSYNCD; } + } else { + Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; } - Demod.posCount++; break; case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: MAKE_SOFT_DECISION(); - if(v < 0) { + if(v < 0) { // logic '0' detected Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; - Demod.posCount = 0; + Demod.posCount = 0; // start of SOF sequence } else { - if(Demod.posCount > 100) { + if(Demod.posCount > 200/4) { // maximum length of TR1 = 200 1/fs Demod.state = DEMOD_UNSYNCD; } } @@ -531,37 +571,40 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) break; case DEMOD_GOT_FALLING_EDGE_OF_SOF: + Demod.posCount++; MAKE_SOFT_DECISION(); if(v > 0) { - if(Demod.posCount < 12) { + if(Demod.posCount < 9*2) { // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges Demod.state = DEMOD_UNSYNCD; } else { LED_C_ON(); // Got SOF Demod.state = DEMOD_AWAITING_START_BIT; Demod.posCount = 0; Demod.len = 0; +/* this had been used to add RSSI (Received Signal Strength Indication) to traces. Currently not implemented. Demod.metricN = 0; Demod.metric = 0; +*/ } } else { - if(Demod.posCount > 100) { + if(Demod.posCount > 12*2) { // low phase of SOF too long (> 12 etu) Demod.state = DEMOD_UNSYNCD; LED_C_OFF(); } } - Demod.posCount++; break; case DEMOD_AWAITING_START_BIT: + Demod.posCount++; MAKE_SOFT_DECISION(); if(v > 0) { - if(Demod.posCount > 10) { + if(Demod.posCount > 3*2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs Demod.state = DEMOD_UNSYNCD; LED_C_OFF(); } - } else { + } else { // start bit detected Demod.bitCount = 0; - Demod.posCount = 1; + Demod.posCount = 1; // this was the first half Demod.thisBit = v; Demod.shiftReg = 0; Demod.state = DEMOD_RECEIVING_DATA; @@ -570,28 +613,30 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) case DEMOD_RECEIVING_DATA: MAKE_SOFT_DECISION(); - if(Demod.posCount == 0) { + if(Demod.posCount == 0) { // first half of bit Demod.thisBit = v; Demod.posCount = 1; - } else { + } else { // second half of bit Demod.thisBit += v; +/* this had been used to add RSSI (Received Signal Strength Indication) to traces. Currently not implemented. if(Demod.thisBit > 0) { Demod.metric += Demod.thisBit; } else { Demod.metric -= Demod.thisBit; } (Demod.metricN)++; +*/ Demod.shiftReg >>= 1; - if(Demod.thisBit > 0) { + if(Demod.thisBit > 0) { // logic '1' Demod.shiftReg |= 0x200; } Demod.bitCount++; if(Demod.bitCount == 10) { uint16_t s = Demod.shiftReg; - if((s & 0x200) && !(s & 0x001)) { + if((s & 0x200) && !(s & 0x001)) { // stop bit == '1', start bit == '0' uint8_t b = (s >> 1); Demod.output[Demod.len] = b; Demod.len++; @@ -600,7 +645,7 @@ static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) Demod.state = DEMOD_UNSYNCD; LED_C_OFF(); if(s == 0x000) { - // This is EOF + // This is EOF (start, stop and all data bits == '0' return TRUE; } } @@ -624,6 +669,7 @@ static void DemodReset() // Clear out the state of the "UART" that receives from the tag. Demod.len = 0; Demod.state = DEMOD_UNSYNCD; + Demod.posCount = 0; memset(Demod.output, 0x00, MAX_FRAME_SIZE); } @@ -653,14 +699,12 @@ static void UartInit(uint8_t *data) /* * Demodulate the samples we received from the tag, also log to tracebuffer - * weTx: set to 'TRUE' if we behave like a reader - * set to 'FALSE' if we behave like a snooper * quiet: set to 'TRUE' to disable debug output */ -static void GetSamplesFor14443Demod(int weTx, int n, int quiet) +static void GetSamplesFor14443bDemod(int n, bool quiet) { int max = 0; - int gotFrame = FALSE; + bool gotFrame = FALSE; int lastRxCounter, ci, cq, samples = 0; // Allocate memory from BigBuf for some buffers @@ -671,57 +715,56 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); // Set up the demodulator for tag -> reader responses. DemodInit(receivedResponse); // Setup and start DMA. - FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); int8_t *upTo = dmaBuf; - lastRxCounter = DMA_BUFFER_SIZE; + lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; // Signal field is ON with the appropriate LED: - if (weTx) LED_D_ON(); else LED_D_OFF(); + LED_D_ON(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | - (weTx ? 0 : FPGA_HF_READER_RX_XCORR_SNOOP)); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); for(;;) { int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; if(behindBy > max) max = behindBy; - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (DMA_BUFFER_SIZE-1)) - > 2) - { + while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1)) > 2) { ci = upTo[0]; cq = upTo[1]; upTo += 2; - if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { upTo = dmaBuf; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; } lastRxCounter -= 2; if(lastRxCounter <= 0) { - lastRxCounter += DMA_BUFFER_SIZE; + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; } samples += 2; - if(Handle14443SamplesDemod(ci, cq)) { - gotFrame = 1; + if(Handle14443bSamplesDemod(ci, cq)) { + gotFrame = TRUE; + break; } } - if(samples > n) { + if(samples > n || gotFrame) { break; } } + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; - if (!quiet) Dbprintf("%x %x %x", max, gotFrame, Demod.len); + + if (!quiet) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", max, samples, gotFrame, Demod.len, Demod.sumI, Demod.sumQ); //Tracing if (tracing && Demod.len > 0) { uint8_t parity[MAX_PARITY_SIZE]; @@ -731,43 +774,10 @@ static void GetSamplesFor14443Demod(int weTx, int n, int quiet) } -//----------------------------------------------------------------------------- -// Read the tag's response. We just receive a stream of slightly-processed -// samples from the FPGA, which we will later do some signal processing on, -// to get the bits. -//----------------------------------------------------------------------------- -/*static void GetSamplesFor14443(int weTx, int n) -{ - uint8_t *dest = (uint8_t *)BigBuf; - int c; - - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | - (weTx ? 0 : FPGA_HF_READER_RX_XCORR_SNOOP)); - - c = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - - dest[c++] = (uint8_t)b; - - if(c >= n) { - break; - } - } - } -}*/ - - //----------------------------------------------------------------------------- // Transmit the command (to the tag) that was placed in ToSend[]. //----------------------------------------------------------------------------- -static void TransmitFor14443(void) +static void TransmitFor14443b(void) { int c; @@ -781,8 +791,7 @@ static void TransmitFor14443(void) LED_D_ON(); // Signal we are transmitting with the Green LED LED_B_ON(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); for(c = 0; c < 10;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { @@ -817,7 +826,7 @@ static void TransmitFor14443(void) //----------------------------------------------------------------------------- // Code a layer 2 command (string of octets, including CRC) into ToSend[], -// so that it is ready to transmit to the tag using TransmitFor14443(). +// so that it is ready to transmit to the tag using TransmitFor14443b(). //----------------------------------------------------------------------------- static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { @@ -873,16 +882,16 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) //----------------------------------------------------------------------------- -// Read an ISO 14443 tag. We send it some set of commands, and record the +// Read an ISO 14443B tag. We send it some set of commands, and record the // responses. // The command name is misleading, it actually decodes the reponse in HEX // into the output buffer (read the result using hexsamples, not hisamples) // // obsolete function only for test //----------------------------------------------------------------------------- -void AcquireRawAdcSamplesIso14443(uint32_t parameter) +void AcquireRawAdcSamplesIso14443b(uint32_t parameter) { - uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; + uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; // REQB with AFI=0, Request All, N=0 SendRawCommand14443B(sizeof(cmd1),1,1,cmd1); } @@ -894,7 +903,7 @@ void AcquireRawAdcSamplesIso14443(uint32_t parameter) static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) { CodeIso14443bAsReader(cmd, len); - TransmitFor14443(); + TransmitFor14443b(); if (tracing) { uint8_t parity[MAX_PARITY_SIZE]; GetParity(cmd, len, parity); @@ -904,7 +913,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) //----------------------------------------------------------------------------- -// Read a SRI512 ISO 14443 tag. +// Read a SRI512 ISO 14443B tag. // // SRI512 tags are just simple memory tags, here we're looking at making a dump // of the contents of the memory. No anticollision algorithm is done, we assume @@ -912,7 +921,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) // // I tried to be systematic and check every answer of the tag, every CRC, etc... //----------------------------------------------------------------------------- -void ReadSTMemoryIso14443(uint32_t dwLast) +void ReadSTMemoryIso14443b(uint32_t dwLast) { clear_trace(); set_tracing(TRUE); @@ -933,15 +942,15 @@ void ReadSTMemoryIso14443(uint32_t dwLast) // Signal field is on with the appropriate LED LED_D_ON(); FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(200); // First command: wake up the tag using the INITIATE command - uint8_t cmd1[] = { 0x06, 0x00, 0x97, 0x5b}; + uint8_t cmd1[] = {0x06, 0x00, 0x97, 0x5b}; CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); // LED_A_ON(); - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len == 0) { @@ -949,7 +958,7 @@ void ReadSTMemoryIso14443(uint32_t dwLast) return; } else { Dbprintf("Randomly generated UID from tag (+ 2 byte CRC): %x %x %x", - Demod.output[0], Demod.output[1],Demod.output[2]); + Demod.output[0], Demod.output[1], Demod.output[2]); } // There is a response, SELECT the uid DbpString("Now SELECT tag:"); @@ -959,22 +968,22 @@ void ReadSTMemoryIso14443(uint32_t dwLast) CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); // LED_A_ON(); - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len != 3) { - Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); - return; + Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); + return; } // Check the CRC of the answer: ComputeCrc14443(CRC_14443_B, Demod.output, 1 , &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[1] || cmd1[3] != Demod.output[2]) { - DbpString("CRC Error reading select response."); - return; + DbpString("CRC Error reading select response."); + return; } // Check response from the tag: should be the same UID as the command we just sent: if (cmd1[1] != Demod.output[0]) { - Dbprintf("Bad response to SELECT from Tag, aborting: %x %x", cmd1[1], Demod.output[0]); - return; + Dbprintf("Bad response to SELECT from Tag, aborting: %x %x", cmd1[1], Demod.output[0]); + return; } // Tag is now selected, // First get the tag's UID: @@ -983,22 +992,22 @@ void ReadSTMemoryIso14443(uint32_t dwLast) CodeAndTransmit14443bAsReader(cmd1, 3); // Only first three bytes for this one // LED_A_ON(); - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len != 10) { - Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); - return; + Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); + return; } // The check the CRC of the answer (use cmd1 as temporary variable): ComputeCrc14443(CRC_14443_B, Demod.output, 8, &cmd1[2], &cmd1[3]); - if(cmd1[2] != Demod.output[8] || cmd1[3] != Demod.output[9]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); - // Do not return;, let's go on... (we should retry, maybe ?) + if(cmd1[2] != Demod.output[8] || cmd1[3] != Demod.output[9]) { + Dbprintf("CRC Error reading block! - Below: expected, got %x %x", + (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); + // Do not return;, let's go on... (we should retry, maybe ?) } Dbprintf("Tag UID (64 bits): %08x %08x", - (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); + (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); // Now loop to read all 16 blocks, address from 0 to last block Dbprintf("Tag memory dump, block 0 to %d",dwLast); @@ -1006,7 +1015,7 @@ void ReadSTMemoryIso14443(uint32_t dwLast) i = 0x00; dwLast++; for (;;) { - if (i == dwLast) { + if (i == dwLast) { DbpString("System area block (0xff):"); i = 0xff; } @@ -1015,25 +1024,25 @@ void ReadSTMemoryIso14443(uint32_t dwLast) CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); // LED_A_ON(); - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); // LED_A_OFF(); if (Demod.len != 6) { // Check if we got an answer from the tag - DbpString("Expected 6 bytes from tag, got less..."); - return; + DbpString("Expected 6 bytes from tag, got less..."); + return; } // The check the CRC of the answer (use cmd1 as temporary variable): ComputeCrc14443(CRC_14443_B, Demod.output, 4, &cmd1[2], &cmd1[3]); - if(cmd1[2] != Demod.output[4] || cmd1[3] != Demod.output[5]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); - // Do not return;, let's go on... (we should retry, maybe ?) + if(cmd1[2] != Demod.output[4] || cmd1[3] != Demod.output[5]) { + Dbprintf("CRC Error reading block! - Below: expected, got %x %x", + (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); + // Do not return;, let's go on... (we should retry, maybe ?) } // Now print out the memory location: Dbprintf("Address=%x, Contents=%x, CRC=%x", i, - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], - (Demod.output[4]<<8)+Demod.output[5]); + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], + (Demod.output[4]<<8)+Demod.output[5]); if (i == 0xff) { - break; + break; } i++; } @@ -1054,10 +1063,10 @@ void ReadSTMemoryIso14443(uint32_t dwLast) * Memory usage for this function, (within BigBuf) * Last Received command (reader->tag) - MAX_FRAME_SIZE * Last Received command (tag->reader) - MAX_FRAME_SIZE - * DMA Buffer, 1024 bytes (samples) - DMA_BUFFER_SIZE + * DMA Buffer - ISO14443B_DMA_BUFFER_SIZE * Demodulated samples received - all the rest */ -void RAMFUNC SnoopIso14443(void) +void RAMFUNC SnoopIso14443b(void) { // We won't start recording the frames that we acquire until we trigger; // a good trigger condition to get started is probably when we see a @@ -1071,7 +1080,7 @@ void RAMFUNC SnoopIso14443(void) set_tracing(TRUE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(DMA_BUFFER_SIZE); + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); int lastRxCounter; int8_t *upTo; int ci, cq; @@ -1089,24 +1098,21 @@ void RAMFUNC SnoopIso14443(void) Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); Dbprintf(" Reader -> tag: %i bytes", MAX_FRAME_SIZE); Dbprintf(" tag -> Reader: %i bytes", MAX_FRAME_SIZE); - Dbprintf(" DMA: %i bytes", DMA_BUFFER_SIZE); + Dbprintf(" DMA: %i bytes", ISO14443B_DMA_BUFFER_SIZE); - // Signal field is off with the appropriate LED - LED_D_OFF(); + // Signal field is off, no reader signal, no tag signal + LEDsoff(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | - FPGA_HF_READER_RX_XCORR_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_SNOOP); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. FpgaSetupSsc(); upTo = dmaBuf; - lastRxCounter = DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE); + lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); uint8_t parity[MAX_PARITY_SIZE]; - LED_A_ON(); bool TagIsActive = FALSE; bool ReaderIsActive = FALSE; @@ -1114,50 +1120,56 @@ void RAMFUNC SnoopIso14443(void) // And now we loop, receiving samples. for(;;) { int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (DMA_BUFFER_SIZE-1); + (ISO14443B_DMA_BUFFER_SIZE-1); if(behindBy > maxBehindBy) { maxBehindBy = behindBy; - if(behindBy > (9*DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? - Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); - break; - } } + if(behindBy < 2) continue; ci = upTo[0]; cq = upTo[1]; upTo += 2; lastRxCounter -= 2; - if(upTo >= dmaBuf + DMA_BUFFER_SIZE) { + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { upTo = dmaBuf; - lastRxCounter += DMA_BUFFER_SIZE; + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + WDT_HIT(); + if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? + Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); + break; + } + if(!tracing) { + DbpString("Reached trace limit"); + break; + } + if(BUTTON_PRESS()) { + DbpString("cancelled"); + break; + } } samples += 2; if (!TagIsActive) { // no need to try decoding reader data if the tag is sending - if(Handle14443UartBit(ci & 0x01)) { + if(Handle14443bUartBit(ci & 0x01)) { if(triggered && tracing) { GetParity(Uart.output, Uart.byteCnt, parity); - LogTrace(Uart.output,Uart.byteCnt,samples, samples,parity,TRUE); + LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); } - if(Uart.byteCnt==0) Dbprintf("[1] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); - /* And ready to receive another command. */ UartReset(); /* And also reset the demod code, which might have been */ /* false-triggered by the commands from the reader. */ DemodReset(); } - if(Handle14443UartBit(cq & 0x01)) { + if(Handle14443bUartBit(cq & 0x01)) { if(triggered && tracing) { GetParity(Uart.output, Uart.byteCnt, parity); - LogTrace(Uart.output,Uart.byteCnt,samples, samples, parity, TRUE); + LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, TRUE); } - if(Uart.byteCnt==0) Dbprintf("[2] Error, Uart.byteCnt==0, Uart.bitCnt=%d", Uart.bitCnt); - /* And ready to receive another command. */ UartReset(); /* And also reset the demod code, which might have been */ @@ -1168,7 +1180,7 @@ void RAMFUNC SnoopIso14443(void) } if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time - if(Handle14443SamplesDemod(ci & 0xFE, cq & 0xFE)) { + if(Handle14443bSamplesDemod(ci & 0xFE, cq & 0xFE)) { //Use samples as a time measurement if(tracing) @@ -1178,31 +1190,17 @@ void RAMFUNC SnoopIso14443(void) LogTrace(Demod.output, Demod.len, samples, samples, parity, FALSE); } triggered = TRUE; - LED_A_OFF(); - LED_B_ON(); // And ready to receive another response. DemodReset(); } - TagIsActive = (Demod.state != DEMOD_UNSYNCD); - } - - WDT_HIT(); - - if(!tracing) { - DbpString("Reached trace limit"); - break; + TagIsActive = (Demod.state > DEMOD_PHASE_REF_TRAINING); } - if(BUTTON_PRESS()) { - DbpString("cancelled"); - break; - } } + FpgaDisableSscDma(); - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); + LEDsoff(); AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; DbpString("Snoop statistics:"); Dbprintf(" Max behind by: %i", maxBehindBy); @@ -1228,38 +1226,36 @@ void RAMFUNC SnoopIso14443(void) void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, uint8_t data[]) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - if(!powerfield) - { + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(); + + set_tracing(TRUE); + +/* if(!powerfield) { // Make sure that we start from off, since the tags are stateful; // confusing things will happen if we don't reset them between reads. FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(200); } + */ - if(!GETBIT(GPIO_LED_D)) - { - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - - // Now give it time to spin up. - // Signal field is on with the appropriate LED - LED_D_ON(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); - SpinDelay(200); - } + // if(!GETBIT(GPIO_LED_D)) { // if field is off + // FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + // // Signal field is on with the appropriate LED + // LED_D_ON(); + // SpinDelay(200); + // } CodeAndTransmit14443bAsReader(data, datalen); - if(recv) - { - GetSamplesFor14443Demod(TRUE, RECEIVE_SAMPLES_TIMEOUT, TRUE); - uint16_t iLen = MIN(Demod.len,USB_CMD_DATA_SIZE); - cmd_send(CMD_ACK,iLen,0,0,Demod.output,iLen); + if(recv) { + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, TRUE); + uint16_t iLen = MIN(Demod.len, USB_CMD_DATA_SIZE); + cmd_send(CMD_ACK, iLen, 0, 0, Demod.output, iLen); } - if(!powerfield) - { + + if(!powerfield) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); } diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index 525ffcc6..21a4e179 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -288,7 +288,7 @@ int CmdHF14BCmdRaw (const char *cmd) { if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { recv = resp.d.asBytes; PrintAndLog("received %i octets",resp.arg[0]); - if(!resp.arg[0]) + if(resp.arg[0] == 0) return 0; hexout = (char *)malloc(resp.arg[0] * 3 + 1); if (hexout != NULL) { @@ -298,11 +298,13 @@ int CmdHF14BCmdRaw (const char *cmd) { } PrintAndLog("%s", hexout); free(hexout); - ComputeCrc14443(CRC_14443_B, recv, resp.arg[0]-2, &first, &second); - if(recv[resp.arg[0]-2]==first && recv[resp.arg[0]-1]==second) { - PrintAndLog("CRC OK"); - } else { - PrintAndLog("CRC failed"); + if (resp.arg[0] > 2) { + ComputeCrc14443(CRC_14443_B, recv, resp.arg[0]-2, &first, &second); + if(recv[resp.arg[0]-2]==first && recv[resp.arg[0]-1]==second) { + PrintAndLog("CRC OK"); + } else { + PrintAndLog("CRC failed"); + } } } else { PrintAndLog("malloc failed your client has low memory?"); diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 53078a782422c09596f006d95c85ae45da20072d..717dad13eb67e5ef2cfeb39da3c4a0f6e9e04fd3 100644 GIT binary patch literal 42175 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 literal 42175 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 diff --git a/fpga/fpga_hf.v b/fpga/fpga_hf.v index a2100df6..e84081b3 100644 --- a/fpga/fpga_hf.v +++ b/fpga/fpga_hf.v @@ -67,15 +67,10 @@ assign major_mode = conf_word[7:5]; // some fraction of the buffers) wire hi_read_tx_shallow_modulation = conf_word[0]; -// For the high-frequency receive correlator: frequency against which to -// correlate. -wire hi_read_rx_xcorr_848 = conf_word[0]; -// and whether to drive the coil (reader) or just short it (snooper) +// For the high-frequency receive correlator: +// whether to drive the coil (reader) or just short it (snooper) wire hi_read_rx_xcorr_snoop = conf_word[1]; -// Divide the expected subcarrier frequency for hi_read_rx_xcorr by 4 -wire hi_read_rx_xcorr_quarter = conf_word[2]; - // For the high-frequency simulated tag: what kind of modulation to use. wire [2:0] hi_simulate_mod_type = conf_word[2:0]; @@ -102,7 +97,7 @@ hi_read_rx_xcorr hrxc( hrxc_ssp_frame, hrxc_ssp_din, ssp_dout, hrxc_ssp_clk, cross_hi, cross_lo, hrxc_dbg, - hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter + hi_read_rx_xcorr_snoop ); hi_simulate hs( diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index 06142637..a6a99cd5 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -10,7 +10,7 @@ module hi_read_rx_xcorr( ssp_frame, ssp_din, ssp_dout, ssp_clk, cross_hi, cross_lo, dbg, - xcorr_is_848, snoop, xcorr_quarter_freq + snoop ); input pck0, ck_1356meg, ck_1356megb; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; @@ -20,58 +20,20 @@ module hi_read_rx_xcorr( output ssp_frame, ssp_din, ssp_clk; input cross_hi, cross_lo; output dbg; - input xcorr_is_848, snoop, xcorr_quarter_freq; + input snoop; // Carrier is steady on through this, unless we're snooping. assign pwr_hi = ck_1356megb & (~snoop); assign pwr_oe1 = 1'b0; -assign pwr_oe2 = 1'b0; assign pwr_oe3 = 1'b0; assign pwr_oe4 = 1'b0; -reg ssp_clk; -reg ssp_frame; - -reg fc_div_2; -always @(posedge ck_1356meg) - fc_div_2 = ~fc_div_2; - -reg fc_div_4; -always @(posedge fc_div_2) - fc_div_4 = ~fc_div_4; - -reg fc_div_8; -always @(posedge fc_div_4) - fc_div_8 = ~fc_div_8; - -reg adc_clk; - -always @(xcorr_is_848 or xcorr_quarter_freq or ck_1356meg) - if(~xcorr_quarter_freq) - begin - if(xcorr_is_848) - // The subcarrier frequency is fc/16; we will sample at fc, so that - // means the subcarrier is 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 ... - adc_clk <= ck_1356meg; - else - // The subcarrier frequency is fc/32; we will sample at fc/2, and - // the subcarrier will look identical. - adc_clk <= fc_div_2; - end - else - begin - if(xcorr_is_848) - // The subcarrier frequency is fc/64 - adc_clk <= fc_div_4; - else - // The subcarrier frequency is fc/128 - adc_clk <= fc_div_8; - end +wire adc_clk = ck_1356megb; // When we're a reader, we just need to do the BPSK demod; but when we're an // eavesdropper, we also need to pick out the commands sent by the reader, // using AM. Do this the same way that we do it for the simulated tag. -reg after_hysteresis, after_hysteresis_prev; +reg after_hysteresis, after_hysteresis_prev, after_hysteresis_prev_prev; reg [11:0] has_been_low_for; always @(negedge adc_clk) begin @@ -97,7 +59,6 @@ end // Let us report a correlation every 4 subcarrier cycles, or 4*16 samples, // so we need a 6-bit counter. reg [5:0] corr_i_cnt; -reg [5:0] corr_q_cnt; // And a couple of registers in which to accumulate the correlations. // we would add at most 32 times adc_d, the result can be held in 13 bits. // Need one additional bit because it can be negative as well @@ -105,32 +66,38 @@ reg signed [13:0] corr_i_accum; reg signed [13:0] corr_q_accum; reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; +// clock and frame signal for communication to ARM +reg ssp_clk; +reg ssp_frame; + + // ADC data appears on the rising edge, so sample it on the falling edge always @(negedge adc_clk) begin + corr_i_cnt <= corr_i_cnt + 1; + // These are the correlators: we correlate against in-phase and quadrature // versions of our reference signal, and keep the (signed) result to // send out later over the SSP. - if(corr_i_cnt == 7'd63) + if(corr_i_cnt == 7'd0) begin if(snoop) begin - // highest 7 significant bits of tag signal (signed), 1 bit reader signal: - corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev}; - corr_q_out <= {corr_q_accum[13:7], after_hysteresis}; + // 7 most significant bits of tag signal (signed), 1 bit reader signal: + corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev_prev}; + corr_q_out <= {corr_q_accum[13:7], after_hysteresis_prev}; + after_hysteresis_prev_prev <= after_hysteresis; end else begin - // highest 8 significant bits of tag signal + // 8 most significant bits of tag signal corr_i_out <= corr_i_accum[13:6]; corr_q_out <= corr_q_accum[13:6]; end corr_i_accum <= adc_d; corr_q_accum <= adc_d; - corr_q_cnt <= 4; - corr_i_cnt <= 0; end else begin @@ -139,13 +106,11 @@ begin else corr_i_accum <= corr_i_accum + adc_d; - if(corr_q_cnt[3]) - corr_q_accum <= corr_q_accum - adc_d; - else + if(corr_i_cnt[3] == corr_i_cnt[2]) // phase shifted by pi/2 corr_q_accum <= corr_q_accum + adc_d; + else + corr_q_accum <= corr_q_accum - adc_d; - corr_i_cnt <= corr_i_cnt + 1; - corr_q_cnt <= corr_q_cnt + 1; end // The logic in hi_simulate.v reports 4 samples per bit. We report two @@ -172,7 +137,7 @@ begin end // set ssp_frame signal for corr_i_cnt = 0..3 and corr_i_cnt = 32..35 - // (two frames with 8 Bits each) + // (send two frames with 8 Bits each) if(corr_i_cnt[5:2] == 4'b0000 || corr_i_cnt[5:2] == 4'b1000) ssp_frame = 1'b1; else @@ -186,5 +151,6 @@ assign dbg = corr_i_cnt[3]; // Unused. assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; endmodule -- 2.39.2