From d714d3effc50b7761dd308c6b0864deea14bd483 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 25 Mar 2014 21:38:24 +0100 Subject: [PATCH] Improvements/Fixes to 14443 sniffing/snooping - fixed a circular buffer rollover bug in iso14443a.c - fixed 7 Byte UID handling in hf mf sniff - fixed "cannot append" error in hf mf sniff d - fixed hint on mfkey32 in hf mf sim x - fixed hf mf sniff sometimes showing rogue data from previous calloc - improve snooping/sniffing by syncing modulation detector window with reader signal (hi_iso14443a.v) - code cleanup of hi_iso14443a.v --- armsrc/fpgaloader.c | 4 +- armsrc/iso14443a.c | 71 ++--- armsrc/mifaresniff.c | 3 +- armsrc/util.c | 2 - client/cmdhfmf.c | 10 +- fpga/fpga.bit | Bin 42172 -> 42172 bytes fpga/hi_iso14443a.v | 685 +++++++++++++++++++++++++------------------ 7 files changed, 442 insertions(+), 333 deletions(-) diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 1e728e11..d63310a3 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -115,11 +115,11 @@ void FpgaSetupSsc(void) AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); // 8 bits per transfer, no loopback, MSB first, 1 transfer per sync - // pulse, no output sync, start on positive-going edge of sync + // pulse, no output sync AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); // clock comes from TK pin, no clock output, outputs change on falling - // edge of TK, sample on rising edge of TK + // edge of TK, sample on rising edge of TK, start on positive-going edge of sync AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); // tx framing is the same as the rx framing diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index b105e792..9afe0788 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -42,15 +42,14 @@ static uint8_t iso14_pcb_blocknum = 0; // // Total delays including SSC-Transfers between ARM and FPGA. These are in carrier clock cycles (1/13,56MHz) // -// When the PM acts as reader and is receiving, it takes -// 3 ticks for the A/D conversion -// 10 ticks ( 16 on average) delay in the modulation detector. -// 6 ticks until the SSC samples the first data -// 7*16 ticks to complete the transfer from FPGA to ARM -// 8 ticks to the next ssp_clk rising edge +// When the PM acts as reader and is receiving tag data, it takes +// 3 ticks delay in the AD converter +// 16 ticks until the modulation detector completes and sets curbit +// 8 ticks until bit_to_arm is assigned from curbit +// 8*16 ticks for the transfer from FPGA to ARM // 4*16 ticks until we measure the time // - 8*16 ticks because we measure the time of the previous transfer -#define DELAY_AIR2ARM_AS_READER (3 + 10 + 6 + 7*16 + 8 + 4*16 - 8*16) +#define DELAY_AIR2ARM_AS_READER (3 + 16 + 8 + 8*16 + 4*16 - 8*16) // When the PM acts as a reader and is sending, it takes // 4*16 ticks until we can write data to the sending hold register @@ -61,15 +60,15 @@ static uint8_t iso14_pcb_blocknum = 0; #define DELAY_ARM2AIR_AS_READER (4*16 + 8*16 + 8 + 8 + 1) // When the PM acts as tag and is receiving it takes -// 12 ticks delay in the RF part, +// 2 ticks delay in the RF part (for the first falling edge), // 3 ticks for the A/D conversion, // 8 ticks on average until the start of the SSC transfer, // 8 ticks until the SSC samples the first data // 7*16 ticks to complete the transfer from FPGA to ARM // 8 ticks until the next ssp_clk rising edge -// 3*16 ticks until we measure the time +// 4*16 ticks until we measure the time // - 8*16 ticks because we measure the time of the previous transfer -#define DELAY_AIR2ARM_AS_TAG (12 + 3 + 8 + 8 + 7*16 + 8 + 3*16 - 8*16) +#define DELAY_AIR2ARM_AS_TAG (2 + 3 + 8 + 8 + 7*16 + 8 + 4*16 - 8*16) // The FPGA will report its internal sending delay in uint16_t FpgaSendQueueDelay; @@ -78,29 +77,30 @@ uint16_t FpgaSendQueueDelay; #define DELAY_FPGA_QUEUE (FpgaSendQueueDelay<<1) // When the PM acts as tag and is sending, it takes -// 5*16 ticks until we can write data to the sending hold register +// 4*16 ticks until we can write data to the sending hold register // 8*16 ticks until the SHR is transferred to the Sending Shift Register // 8 ticks until the first transfer starts // 8 ticks later the FPGA samples the data // + a varying number of ticks in the FPGA Delay Queue (mod_sig_buf) // + 1 tick to assign mod_sig_coil -#define DELAY_ARM2AIR_AS_TAG (5*16 + 8*16 + 8 + 8 + DELAY_FPGA_QUEUE + 1) +#define DELAY_ARM2AIR_AS_TAG (4*16 + 8*16 + 8 + 8 + DELAY_FPGA_QUEUE + 1) // When the PM acts as sniffer and is receiving tag data, it takes // 3 ticks A/D conversion -// 16 ticks delay in the modulation detector (on average). -// + 16 ticks until it's result is sampled. +// 14 ticks to complete the modulation detection +// 8 ticks (on average) until the result is stored in to_arm // + the delays in transferring data - which is the same for // sniffing reader and tag data and therefore not relevant -#define DELAY_TAG_AIR2ARM_AS_SNIFFER (3 + 16 + 16) +#define DELAY_TAG_AIR2ARM_AS_SNIFFER (3 + 14 + 8) -// When the PM acts as sniffer and is receiving tag data, it takes -// 12 ticks delay in analogue RF receiver +// When the PM acts as sniffer and is receiving reader data, it takes +// 2 ticks delay in analogue RF receiver (for the falling edge of the +// start bit, which marks the start of the communication) // 3 ticks A/D conversion -// 8 ticks on average until we sample the data. +// 8 ticks on average until the data is stored in to_arm. // + the delays in transferring data - which is the same for // sniffing reader and tag data and therefore not relevant -#define DELAY_READER_AIR2ARM_AS_SNIFFER (12 + 3 + 8) +#define DELAY_READER_AIR2ARM_AS_SNIFFER (2 + 3 + 8) //variables used for timing purposes: //these are in ssp_clk cycles: @@ -258,23 +258,7 @@ void UartReset() Uart.endTime = 0; } -/* inline RAMFUNC Modulation_t MillerModulation(uint8_t b) -{ - // switch (b & 0x88) { - // case 0x00: return MILLER_MOD_BOTH_HALVES; - // case 0x08: return MILLER_MOD_FIRST_HALF; - // case 0x80: return MILLER_MOD_SECOND_HALF; - // case 0x88: return MILLER_MOD_NOMOD; - // } - // test the second cycle for a pause. For whatever reason the startbit tends to appear earlier than the rest. - switch (b & 0x44) { - case 0x00: return MOD_BOTH_HALVES; - case 0x04: return MOD_FIRST_HALF; - case 0x40: return MOD_SECOND_HALF; - default: return MOD_NOMOD; - } -} - */ + // use parameter non_real_time to provide a timestamp. Set to 0 if the decoder should measure real time static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { @@ -398,10 +382,10 @@ static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) static tDemod Demod; // Lookup-Table to decide if 4 raw bits are a modulation. -// We accept three or four consecutive "1" in any position +// We accept three or four "1" in any position const bool Mod_Manchester_LUT[] = { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, - FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE + FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE }; #define IsManchesterModulationNibble1(b) (Mod_Manchester_LUT[(b & 0x00F0) >> 4]) @@ -646,7 +630,7 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { previous_data = *data; rsamples++; data++; - if(data > dmaBuf + DMA_BUFFER_SIZE) { + if(data == dmaBuf + DMA_BUFFER_SIZE) { data = dmaBuf; } } // main cycle @@ -1423,7 +1407,7 @@ static int EmSendCmd14443aRaw(uint8_t *resp, int respLen, bool correctionNeeded) i = 1; } - // clear receiving shift register and holding register + // clear receiving shift register and holding register while(!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); b = AT91C_BASE_SSC->SSC_RHR; (void) b; while(!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); @@ -2593,11 +2577,12 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * //May just aswell send the collected ar_nr in the response aswell cmd_send(CMD_ACK,CMD_SIMULATE_MIFARE_CARD,0,0,&ar_nr_responses,ar_nr_collected*4*4); } + if(flags & FLAG_NR_AR_ATTACK) { if(ar_nr_collected > 1) { Dbprintf("Collected two pairs of AR/NR which can be used to extract keys from reader:"); - Dbprintf("../tools/mfkey/mfkey32 %08x %08x %08x %08x", + Dbprintf("../tools/mfkey/mfkey32 %08x %08x %08x %08x %08x %08x", ar_nr_responses[0], // UID ar_nr_responses[1], //NT ar_nr_responses[2], //AR1 @@ -2608,7 +2593,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * } else { Dbprintf("Failed to obtain two AR/NR pairs!"); if(ar_nr_collected >0) { - Dbprintf("Only got these: UID=%08d, nonce=%08d, AR1=%08d, NR1=%08d", + Dbprintf("Only got these: UID=%08x, nonce=%08x, AR1=%08x, NR1=%08x", ar_nr_responses[0], // UID ar_nr_responses[1], //NT ar_nr_responses[2], //AR1 @@ -2762,7 +2747,7 @@ void RAMFUNC SniffMifare(uint8_t param) { previous_data = *data; sniffCounter++; data++; - if(data > dmaBuf + DMA_BUFFER_SIZE) { + if(data == dmaBuf + DMA_BUFFER_SIZE) { data = dmaBuf; } diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index bd9840e8..3e5570f9 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -93,7 +93,8 @@ bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint32_t parity, ui } case SNF_ANTICOL2:{ if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // CL2 UID - memcpy(sniffUID, data, 4); + memcpy(sniffUID, sniffUID+4, 3); + memcpy(sniffUID+3, data, 4); sniffUIDType = SNF_UID_7; sniffState = SNF_UID2; } diff --git a/armsrc/util.c b/armsrc/util.c index 6d34ae5e..905bad25 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -363,8 +363,6 @@ void StartCountSspClk() // while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low - // after the falling edge of ssp_frame, there is delay of 1/13,56MHz (73ns) until the next rising edge of ssp_clk. This are only a few - // processor cycles. We therefore may or may not be able to sync on this edge. Therefore better make sure that we miss it: while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high // note: up to now two ssp_clk rising edges have passed since the rising edge of ssp_frame // it is now safe to assert a sync signal. This sets all timers to 0 on next active clock edge diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index cc9b4c76..2dc1ab2b 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1847,8 +1847,9 @@ int CmdHF14AMfSniff(const char *Cmd){ printf("Press the key on pc keyboard to abort the client.\n"); printf("-------------------------------------------------------------------------\n"); - UsbCommand c = {CMD_MIFARE_SNIFFER, {0, 0, 0}}; - SendCommand(&c); + UsbCommand c = {CMD_MIFARE_SNIFFER, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); // wait cycle while (true) { @@ -1895,7 +1896,7 @@ int CmdHF14AMfSniff(const char *Cmd){ sak = bufPtr[11]; PrintAndLog("tag select uid:%s atqa:%02x %02x sak:0x%02x", sprint_hex(uid, 7), atqa[0], atqa[1], sak); - if (wantLogToFile) { + if (wantLogToFile || wantDecrypt) { FillFileNameByUID(logHexFileName, uid, ".log", 7); AddLogCurrentDT(logHexFileName); } @@ -1911,7 +1912,8 @@ int CmdHF14AMfSniff(const char *Cmd){ } } // resp not NILL } // while (true) - return 0; + + return 0; } static command_t CommandTable[] = diff --git a/fpga/fpga.bit b/fpga/fpga.bit index f494783311398ea66fd32529687f55b09e320fb9..e773ef9326d66be61c29bc6c169be07d9703a364 100644 GIT binary patch literal 42172 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 literal 42172 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 diff --git a/fpga/hi_iso14443a.v b/fpga/hi_iso14443a.v index ec5aa757..3f614fdd 100644 --- a/fpga/hi_iso14443a.v +++ b/fpga/hi_iso14443a.v @@ -29,45 +29,31 @@ module hi_iso14443a( output dbg; input [2:0] mod_type; -reg ssp_clk; -reg ssp_frame; -wire adc_clk; -assign adc_clk = ck_1356meg; +wire adc_clk = ck_1356meg; + -reg after_hysteresis, pre_after_hysteresis, after_hysteresis_prev1, after_hysteresis_prev2, after_hysteresis_prev3, after_hysteresis_prev4; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Reader -> PM3: +// detecting and shaping the reader's signal. Reader will modulate the carrier by 100% (signal is either on or off). Use a +// hysteresis (Schmitt Trigger) to avoid false triggers during slowly increasing or decreasing carrier amplitudes +reg after_hysteresis; reg [11:0] has_been_low_for; -reg [8:0] saw_deep_modulation; -reg [2:0] deep_counter; -reg deep_modulation; always @(negedge adc_clk) begin - if(& adc_d[7:6]) after_hysteresis <= 1'b1; // adc_d >= 196 (U >= 3,28V) -> after_hysteris = 1 - else if(~(| adc_d[7:4])) after_hysteresis <= 1'b0; // if adc_d <= 15 (U <= 1,13V) -> after_hysteresis = 0 - - pre_after_hysteresis <= after_hysteresis; + if(adc_d >= 16) after_hysteresis <= 1'b1; // U >= 1,14V -> after_hysteresis = 1 + else if(adc_d < 8) after_hysteresis <= 1'b0; // U < 1,04V -> after_hysteresis = 0 + // Note: was >= 3,53V and <= 1,19V. The new trigger values allow more reliable detection of the first bit + // (it might not reach 3,53V due to the high time constant of the high pass filter in the analogue RF part). + // In addition, the new values are more in line with ISO14443-2: "The PICC shall detect the ”End of Pause” after the field exceeds + // 5% of H_INITIAL and before it exceeds 60% of H_INITIAL." Depending on the signal strength, 60% might well be less than 3,53V. - if(~(| adc_d[7:0])) // if adc_d == 0 (U <= 0,94V) - begin - if(deep_counter == 3'd7) // adc_d == 0 for 7 adc_clk ticks -> deep_modulation (by reader) - begin - deep_modulation <= 1'b1; - saw_deep_modulation <= 8'd0; - end - else - deep_counter <= deep_counter + 1; - end - else - begin - deep_counter <= 3'd0; - if(saw_deep_modulation == 8'd255) // adc_d != 0 for 255 adc_clk ticks -> deep_modulation is over, now waiting for tag's response - deep_modulation <= 1'b0; - else - saw_deep_modulation <= saw_deep_modulation + 1; - end - if(after_hysteresis) + // detecting a loss of reader's field (adc_d < 192 for 4096 clock cycles). If this is the case, + // set the detected reader signal (after_hysteresis) to '1' (unmodulated) + if(adc_d >= 192) begin has_been_low_for <= 12'd0; end @@ -76,121 +62,182 @@ begin if(has_been_low_for == 12'd4095) begin has_been_low_for <= 12'd0; - after_hysteresis <= 1'b1; // reset after_hysteresis to 1 if it had been 0 for 4096 cycles (no field) + after_hysteresis <= 1'b1; end else begin has_been_low_for <= has_been_low_for + 1; end end + end -// Report every 4 subcarrier cycles -// 128 periods of carrier frequency => 7-bit counter [negedge_cnt] -reg [6:0] negedge_cnt; -reg bit1, bit2, bit3, bit4; -reg curbit; - -// storage for four 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; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Reader -> PM3 +// detect when a reader is active (modulating). We assume that the reader is active, if we see the carrier off for at least 8 +// carrier cycles. We assume that the reader is inactive, if the carrier stayed high for at least 256 carrier cycles. +reg deep_modulation; +reg [2:0] deep_counter; +reg [8:0] saw_deep_modulation; -// 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); +always @(negedge adc_clk) +begin + if(~(| adc_d[7:0])) // if adc_d == 0 (U <= 0,94V) + begin + if(deep_counter == 3'd7) // adc_d == 0 for 8 adc_clk ticks -> deep_modulation (by reader) + begin + deep_modulation <= 1'b1; + saw_deep_modulation <= 8'd0; + end + else + deep_counter <= deep_counter + 1; + end + else + begin + deep_counter <= 3'd0; + if(saw_deep_modulation == 8'd255) // adc_d != 0 for 256 adc_clk ticks -> deep_modulation is over, probably waiting for tag's response + deep_modulation <= 1'b0; + else + saw_deep_modulation <= saw_deep_modulation + 1; + end +end -// 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 8 Bit results to the arm -reg [7:0] to_arm; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tag -> PM3 +// filter the input for a tag's signal. The filter box needs the 4 previous input values and is a gaussian derivative filter +// for noise reduction and edge detection. +// store 4 previous samples: +reg [7:0] input_prev_4, input_prev_3, input_prev_2, input_prev_1; +// convert to signed signals (and multiply by two for samples at t-4 and t) +wire signed [10:0] input_prev_4_times_2 = {0, 0, input_prev_4, 0}; +wire signed [10:0] input_prev_3_times_1 = {0, 0, 0, input_prev_3}; +wire signed [10:0] input_prev_1_times_1 = {0, 0, 0, input_prev_1}; +wire signed [10:0] adc_d_times_2 = {0, 0, adc_d, 0}; -reg bit_to_arm; -reg fdt_indicator, fdt_elapsed; -reg [10:0] fdt_counter; -//reg [47:0] mod_sig_buf; -reg [31:0] mod_sig_buf; -//reg [5:0] mod_sig_ptr; -reg [4:0] mod_sig_ptr; -reg [3:0] mod_sig_flip; -reg mod_sig, mod_sig_coil; -reg temp_buffer_reset; -reg sendbit; -reg [3:0] sub_carrier_cnt; -reg[3:0] reader_falling_edge_time; +wire signed [10:0] tmp_1, tmp_2; +wire signed [10:0] adc_d_filtered; +integer i; -// ADC data appears on the rising edge, so sample it on the falling edge +assign tmp_1 = input_prev_4_times_2 + input_prev_3_times_1; +assign tmp_2 = input_prev_1_times_1 + adc_d_times_2; + always @(negedge adc_clk) begin - // ------------------------------------------------------------------------------------------------------------------------------------------------------------------ - // relevant for TAGSIM_MOD only. Timing of Tag's answer relative 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'd547) fdt_indicator <= 1'b1; // The ARM must not send earlier to prevent mod_sig_buf overflow. - // The mod_sig_buf can buffer 29 excess data bits, i.e. a maximum delay of 29 * 16 = 464 adc_clk ticks. fdt_indicator - // could appear at ssp_din after 1 tick, 16 ticks for the transfer, 128 ticks until response is sended. - // 1148 - 464 - 1 - 128 - 8 = 547 + // for (i = 3; i > 0; i = i - 1) + // begin + // input_shift[i] <= input_shift[i-1]; + // end + // input_shift[0] <= adc_d; + input_prev_4 <= input_prev_3; + input_prev_3 <= input_prev_2; + input_prev_2 <= input_prev_1; + input_prev_1 <= adc_d; +end + +// assign adc_d_filtered = (input_shift[3] << 1) + input_shift[2] - input_shift[0] - (adc_d << 1); +assign adc_d_filtered = tmp_1 - tmp_2; + - if ((mod_type == `TAGSIM_MOD) || (mod_type == `TAGSIM_LISTEN)) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// internal FPGA timing. Maximum required period is 128 carrier clock cycles for a full 8 Bit transfer to ARM. (i.e. we need a +// 7 bit counter). Adjust its frequency to external reader's clock when simulating a tag or sniffing. +reg pre_after_hysteresis; +reg [3:0] reader_falling_edge_time; +reg [6:0] negedge_cnt; + +always @(negedge adc_clk) +begin + // detect a reader signal's falling edge and remember its timing: + pre_after_hysteresis <= after_hysteresis; + if (pre_after_hysteresis && ~after_hysteresis) + begin + reader_falling_edge_time[3:0] <= negedge_cnt[3:0]; + end + + // adjust internal timer counter if necessary: + if (negedge_cnt[3:0] == 4'd13 && (mod_type == `SNIFFER || mod_type == `TAGSIM_LISTEN) && deep_modulation) begin - if(fdt_counter == 11'd1148) // the RF part delays the rising edge by approx 5 adc_clk_ticks, the ADC needs 3 clk_ticks for A/D conversion, - // 16 ticks delay by mod_sig_buf - // 1172 - 5 - 3 - 16 = 1148. + if (reader_falling_edge_time == 4'd1) // reader signal changes right after sampling. Better sample earlier next time. begin - if(fdt_elapsed) - begin - if(negedge_cnt[3:0] == mod_sig_flip) mod_sig_coil <= mod_sig; // start modulating (if mod_sig is already set) - sub_carrier_cnt[3:0] <= sub_carrier_cnt[3:0] + 1; - end - else - begin - mod_sig_flip <= negedge_cnt[3:0]; // start modulation at this time - sub_carrier_cnt[3:0] <= 0; // subcarrier phase in sync with start of modulation - mod_sig_coil <= mod_sig; // assign signal to coil - fdt_elapsed = 1'b1; - if(~(| mod_sig_ptr[4:0])) mod_sig_ptr <= 5'd9; // if mod_sig_ptr == 0 -> 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 + negedge_cnt <= negedge_cnt + 2; // time warp + end + else if (reader_falling_edge_time == 4'd0) // reader signal changes right before sampling. Better sample later next time. + begin + negedge_cnt <= negedge_cnt; // freeze time end else begin - fdt_counter <= fdt_counter + 1; // Count until 1155 + negedge_cnt <= negedge_cnt + 1; // Continue as usual end + reader_falling_edge_time[3:0] <= 4'd8; // adjust only once per detected edge end - else // other modes: don't use the delay line. + else if (negedge_cnt == 7'd127) // normal operation: count from 0 to 127 begin - mod_sig_coil <= ssp_dout; + negedge_cnt <= 0; end - - - //------------------------------------------------------------------------------------------------------------------------------------------- - // Relevant for READER_LISTEN only - // look for steepest falling and rising edges: + else + begin + negedge_cnt <= negedge_cnt + 1; + end +end - if(negedge_cnt[3:0] == 4'd1) // reset modulation detector. Save current edge. + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tag -> PM3: +// determine best possible time for starting/resetting the modulation detector. +reg [3:0] mod_detect_reset_time; + +always @(negedge adc_clk) +begin + if (mod_type == `READER_LISTEN) + // (our) reader signal changes at t=1, tag response expected n*16+4 ticks later, further delayed by + // 3 ticks ADC conversion. + // 1 + 4 + 3 = 8 begin - if (adc_d_filtered > 0) - begin - rx_mod_falling_edge_max <= adc_d_filtered; - rx_mod_rising_edge_max <= 0; - end - else + mod_detect_reset_time <= 4'd8; + end + else + if (mod_type == `SNIFFER) + begin + // detect a rising edge of reader's signal and sync modulation detector to the tag's answer: + if (~pre_after_hysteresis && after_hysteresis && deep_modulation) + // reader signal rising edge detected at negedge_cnt[3:0]. This signal had been delayed + // 9 ticks by the RF part + 3 ticks by the A/D converter + 1 tick to assign to after_hysteresis. + // The tag will respond n*16 + 4 ticks later + 3 ticks A/D converter delay. + // - 9 - 3 - 1 + 4 + 3 = -6 begin - rx_mod_falling_edge_max <= 0; - rx_mod_rising_edge_max <= -adc_d_filtered; + mod_detect_reset_time <= negedge_cnt[3:0] - 4'd4; end end - else // detect modulation +end + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tag -> PM3: +// modulation detector. Looks for the steepest falling and rising edges within a 16 clock period. If there is both a significant +// falling and rising edge (in any order), a modulation is detected. +reg signed [10:0] rx_mod_falling_edge_max; +reg signed [10:0] rx_mod_rising_edge_max; +reg curbit; + +always @(negedge adc_clk) +begin + if(negedge_cnt[3:0] == mod_detect_reset_time) + begin + // detect modulation signal: if modulating, there must have been a falling AND a rising edge + if (rx_mod_falling_edge_max > 5 && rx_mod_rising_edge_max > 5) + curbit <= 1'b1; // modulation + else + curbit <= 1'b0; // no modulation + // reset modulation detector + rx_mod_rising_edge_max <= 0; + rx_mod_falling_edge_max <= 0; + end + else // look for steepest edges (slopes) begin if (adc_d_filtered > 0) begin @@ -204,233 +251,309 @@ begin end end - // detect modulation signal: if modulating, there must be a falling and a rising edge - if (rx_mod_falling_edge_max > 6 && rx_mod_rising_edge_max > 6) - curbit <= 1'b1; // modulation - else - curbit <= 1'b0; // no modulation - - - // 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; +end + - // Relevant for TAGSIM_MOD only (timing the Tag's answer. See above) - // When we see end of a modulation and we are emulating a Tag, start fdt_counter. - // Reset fdt_counter when modulation is detected. - if(~after_hysteresis /* && mod_sig_buf_empty */ && mod_type == `TAGSIM_LISTEN) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tag+Reader -> PM3 +// sample 4 bits reader data and 4 bits tag data for sniffing +reg [3:0] reader_data; +reg [3:0] tag_data; + +always @(negedge adc_clk) +begin + if(negedge_cnt[3:0] == 4'd0) begin - fdt_counter <= 11'd0; - fdt_elapsed = 1'b0; - fdt_indicator <= 1'b0; - temp_buffer_reset = 1'b0; - mod_sig_ptr <= 5'b00000; - mod_sig = 1'b0; - end + reader_data[3:0] <= {reader_data[2:0], after_hysteresis}; + tag_data[3:0] <= {tag_data[2:0], curbit}; + end +end + - if(negedge_cnt[3:0] == 4'd1) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PM3 -> Tag: +// a delay line to ensure that we send the (emulated) tag's answer at the correct time according to ISO14443-3 +reg [31:0] mod_sig_buf; +reg [4:0] mod_sig_ptr; +reg mod_sig; + +always @(negedge adc_clk) +begin + if(negedge_cnt[3:0] == 4'd0) // sample data at rising edge of ssp_clk - ssp_dout changes at the falling edge. begin - // What do we communicate to the ARM - if(mod_type == `TAGSIM_LISTEN) - sendbit = after_hysteresis; - else if(mod_type == `TAGSIM_MOD) - /* if(fdt_counter > 11'd772) sendbit = mod_sig_coil; // huh? - else */ - sendbit = fdt_indicator; - else if (mod_type == `READER_LISTEN) - sendbit = curbit; + mod_sig_buf[31:2] <= mod_sig_buf[30:1]; // shift + if (~ssp_dout && ~mod_sig_buf[1]) + mod_sig_buf[1] <= 1'b0; // delete the correction bit (a single 1 preceded and succeeded by 0) else - sendbit = 1'b0; + mod_sig_buf[1] <= mod_sig_buf[0]; + mod_sig_buf[0] <= ssp_dout; // add new data to the delay line + + mod_sig = mod_sig_buf[mod_sig_ptr]; // the delayed signal. end +end - // check timing of a falling edge in reader signal - if (pre_after_hysteresis && ~after_hysteresis) - reader_falling_edge_time[3:0] <= negedge_cnt[3:0]; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PM3 -> Tag, internal timing: +// a timer for the 1172 cycles fdt (Frame Delay Time). Start the timer with a rising edge of the reader's signal. +// set fdt_elapsed when we no longer need to delay data. Set fdt_indicator when we can start sending data. +// Note: the FPGA only takes care for the 1172 delay. To achieve an additional 1236-1172=64 ticks delay, the ARM must send +// a 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 additional delay. +reg [10:0] fdt_counter; +reg fdt_indicator, fdt_elapsed; +reg [3:0] mod_sig_flip; +reg [3:0] sub_carrier_cnt; +// we want to achieve a delay of 1172. The RF part already has delayed the reader signals's rising edge +// by 9 ticks, the ADC took 3 ticks and there is always a delay of 32 ticks by the mod_sig_buf. Therefore need to +// count to 1172 - 9 - 3 - 32 = 1128 +`define FDT_COUNT 11'd1128 +// The ARM must not send too early, otherwise the mod_sig_buf will overflow, therefore signal that we are ready +// with fdt_indicator. The mod_sig_buf can buffer 29 excess data bits, i.e. a maximum delay of 29 * 16 = 464 adc_clk ticks. +// fdt_indicator could appear at ssp_din after 1 tick, the transfer needs 16 ticks, the ARM can send 128 ticks later. +// 1128 - 464 - 1 - 128 - 8 = 535 +`define FDT_INDICATOR_COUNT 11'd535 - // sync clock to external reader's clock: - if (negedge_cnt[3:0] == 4'd13 && (mod_type == `SNIFFER || mod_type == `TAGSIM_MOD || mod_type == `TAGSIM_LISTEN)) +// reset on a pause in listen mode. I.e. the counter starts when the pause is over: +assign fdt_reset = ~after_hysteresis && mod_type == `TAGSIM_LISTEN; + +always @(negedge adc_clk) +begin + if (fdt_reset) begin - // adjust clock if necessary: - if (reader_falling_edge_time < 4'd8 && reader_falling_edge_time > 4'd1) - begin - negedge_cnt <= negedge_cnt; // freeze time + fdt_counter <= 11'd0; + fdt_elapsed <= 1'b0; + fdt_indicator <= 1'b0; + end + else + begin + if(fdt_counter == `FDT_COUNT) + begin + if(~fdt_elapsed) // just reached fdt. + begin + mod_sig_flip <= negedge_cnt[3:0]; // start modulation at this time + sub_carrier_cnt <= 4'd0; // subcarrier phase in sync with start of modulation + fdt_elapsed <= 1'b1; + end + else + begin + sub_carrier_cnt <= sub_carrier_cnt + 1; + end end - else if (reader_falling_edge_time == 4'd8) - begin - negedge_cnt <= negedge_cnt + 1; // the desired state. Advance as usual; - end else begin - negedge_cnt[3:0] <= 4'd15; // time warp + fdt_counter <= fdt_counter + 1; end - reader_falling_edge_time <= 4'd8; // only once per detected rising edge end + if(fdt_counter == `FDT_INDICATOR_COUNT) fdt_indicator <= 1'b1; +end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PM3 -> Reader or Tag +// assign a modulation signal to the antenna. This signal is either a delayed signal (to achieve fdt when sending to a reader) +// or undelayed when sending to a tag +reg mod_sig_coil; - //------------------------------------------------------------------------------------------------------------------------------------------ - // Prepare 8 Bits to communicate to ARM - if (negedge_cnt == 7'd63) +always @(negedge adc_clk) +begin + if (mod_type == `TAGSIM_MOD) // need to take care of proper fdt timing begin - if (mod_type == `SNIFFER) + if(fdt_counter == `FDT_COUNT) begin - if(deep_modulation) // a reader is sending (or there's no field at all) + if(fdt_elapsed) begin - to_arm <= {after_hysteresis_prev1,after_hysteresis_prev2,after_hysteresis_prev3,after_hysteresis_prev4,1'b0,1'b0,1'b0,1'b0}; + if(negedge_cnt[3:0] == mod_sig_flip) mod_sig_coil <= mod_sig; end else begin - to_arm <= {after_hysteresis_prev1,after_hysteresis_prev2,after_hysteresis_prev3,after_hysteresis_prev4,bit1,bit2,bit3,bit4}; - end - negedge_cnt <= 0; + mod_sig_coil <= mod_sig; // just reached fdt. Immediately assign signal to coil + end end - else + end + else // other modes: don't delay + begin + mod_sig_coil <= ssp_dout; + end +end + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PM3 -> Reader +// determine the required delay in the mod_sig_buf (set mod_sig_ptr). +reg temp_buffer_reset; + +always @(negedge adc_clk) +begin + if(fdt_reset) + begin + mod_sig_ptr <= 5'd0; + temp_buffer_reset = 1'b0; + end + else + begin + if(fdt_counter == `FDT_COUNT && ~fdt_elapsed) // if we just reached fdt + if(~(| mod_sig_ptr[4:0])) + mod_sig_ptr <= 5'd8; // ... but didn't buffer a 1 yet, delay next 1 by n*128 ticks. + else + temp_buffer_reset = 1'b1; // else no need for further delays. + + if(negedge_cnt[3:0] == 4'd0) // at rising edge of ssp_clk - ssp_dout changes at the falling edge. begin - negedge_cnt <= negedge_cnt + 1; + if((ssp_dout || (| mod_sig_ptr[4:0])) && ~fdt_elapsed) // buffer a 1 (and all subsequent data) until fdt is reached. + if (mod_sig_ptr == 5'd31) + mod_sig_ptr <= 5'd0; // buffer overflow - data loss. + else + mod_sig_ptr <= mod_sig_ptr + 1; // increase buffer (= increase delay by 16 adc_clk ticks). mod_sig_ptr always points ahead of first 1. + else if(fdt_elapsed && ~temp_buffer_reset) + begin + // 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 (as defined in ISO14443-3) + if(ssp_dout) + temp_buffer_reset = 1'b1; + if(mod_sig_ptr == 5'd1) + mod_sig_ptr <= 5'd8; // still nothing received, need to go for the next interval + else + mod_sig_ptr <= mod_sig_ptr - 1; // decrease buffer. + end end - end - else if(negedge_cnt == 7'd127) + end +end + + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// FPGA -> ARM communication: +// buffer 8 bits data to be sent to ARM. Shift them out bit by bit. +reg [7:0] to_arm; + +always @(negedge adc_clk) +begin + if (negedge_cnt[5:0] == 6'd63) // fill the buffer begin - if (mod_type == `TAGSIM_MOD) + if (mod_type == `SNIFFER) begin - to_arm[7:0] <= {mod_sig_ptr[4:0], mod_sig_flip[3:1]}; - negedge_cnt <= 0; + if(deep_modulation) // a reader is sending (or there's no field at all) + begin + to_arm <= {reader_data[3:0], 4'b0000}; // don't send tag data + end + else + begin + to_arm <= {reader_data[3:0], tag_data[3:0]}; + end end else begin - to_arm[7:0] <= 8'd0; - negedge_cnt <= negedge_cnt + 1; + to_arm[7:0] <= {mod_sig_ptr[4:0], mod_sig_flip[3:1]}; // feedback timing information end - end - else - begin - negedge_cnt <= negedge_cnt + 1; - end + end - - if(negedge_cnt == 7'd1) - begin - after_hysteresis_prev1 <= after_hysteresis; - bit1 <= curbit; - end - if(negedge_cnt == 7'd17) + if(negedge_cnt[2:0] == 3'b000 && mod_type == `SNIFFER) // shift at double speed begin - after_hysteresis_prev2 <= after_hysteresis; - bit2 <= curbit; - end - if(negedge_cnt == 7'd33) - begin - after_hysteresis_prev3 <= after_hysteresis; - bit3 <= curbit; - end - if(negedge_cnt == 7'd49) - begin - after_hysteresis_prev4 <= after_hysteresis; - bit4 <= curbit; - end - - //-------------------------------------------------------------------------------------------------------------------------------------------------------------- - // Relevant in TAGSIM_MOD only. Delay-Line to buffer data and send it at the correct time - if(negedge_cnt[3:0] == 4'd0) // at rising edge of ssp_clk - ssp_dout changes at the falling edge. - begin - mod_sig_buf[31:0] <= {mod_sig_buf[30:1], ssp_dout, 1'b0}; // shift in new data starting at mod_sig_buf[1]. mod_sig_buf[0] = 0 always. - // asign the delayed signal to mod_sig, but 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((ssp_dout || (| mod_sig_ptr[4: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 - if (mod_sig_ptr == 5'd30) mod_sig_ptr <= 5'd0; - 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. + // Don't shift if we just loaded new data, obviously. + if(negedge_cnt[5:0] != 6'd0) begin - // 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 == 5'd2) mod_sig_ptr <= 5'd9; // still nothing received, need to go for the next interval - else mod_sig_ptr <= mod_sig_ptr - 1; // decrease buffer. + to_arm[7:1] <= to_arm[6:0]; end - else + end + + if(negedge_cnt[3:0] == 4'b0000 && mod_type != `SNIFFER) + begin + // Don't shift if we just loaded new data, obviously. + if(negedge_cnt[6:0] != 7'd0) begin - if(~mod_sig_buf[mod_sig_ptr-1] && ~mod_sig_buf[mod_sig_ptr+1]) mod_sig = 1'b0; - // finally, assign the delayed signal: - else mod_sig = mod_sig_buf[mod_sig_ptr]; + to_arm[7:1] <= to_arm[6:0]; end end - //----------------------------------------------------------------------------------------------------------------------------------------------------------------------- - // Communication to ARM (SSP Clock and data) - // SNIFFER mode (ssp_clk = adc_clk / 8, ssp_frame clock = adc_clk / 64)): +end + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// FPGA -> ARM communication: +// generate a ssp clock and ssp frame signal for the synchronous transfer from/to the ARM +reg ssp_clk; +reg ssp_frame; +reg [2:0] ssp_frame_counter; + +always @(negedge adc_clk) +begin if(mod_type == `SNIFFER) + // SNIFFER mode (ssp_clk = adc_clk / 8, ssp_frame clock = adc_clk / 64)): begin - if(negedge_cnt[2:0] == 3'b100) - ssp_clk <= 1'b0; - - if(negedge_cnt[2:0] == 3'b000) - begin + if(negedge_cnt[2:0] == 3'd0) ssp_clk <= 1'b1; - // Don't shift if we just loaded new data, obviously. - if(negedge_cnt[5:0] != 6'd0) - begin - to_arm[7:1] <= to_arm[6:0]; - end - end + if(negedge_cnt[2:0] == 3'd4) + ssp_clk <= 1'b0; - if(negedge_cnt[5:4] == 2'b00) - ssp_frame = 1'b1; - else - ssp_frame = 1'b0; - - bit_to_arm = to_arm[7]; + if(negedge_cnt[5:0] == 6'd0) // ssp_frame rising edge indicates start of frame + ssp_frame <= 1'b1; + if(negedge_cnt[5:0] == 6'd8) + ssp_frame <= 1'b0; 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; + if(negedge_cnt[3:0] == 4'd0) + ssp_clk <= 1'b1; + if(negedge_cnt[3:0] == 4'd8) + ssp_clk <= 1'b0; - if(negedge_cnt[3:0] == 4'b0111) - begin - // if(ssp_frame_counter == 3'd7) ssp_frame_counter <= 3'd0; - // else ssp_frame_counter <= ssp_frame_counter + 1; - if (negedge_cnt[6:4] == 3'b000) ssp_frame = 1'b1; - else ssp_frame = 1'b0; - end - // ssp_frame = (ssp_frame_counter == 3'd7); + if(negedge_cnt[6:0] == 7'd7) // ssp_frame rising edge indicates start of frame + ssp_frame <= 1'b1; + if(negedge_cnt[6:0] == 7'd23) + ssp_frame <= 1'b0; + end +end - if(negedge_cnt[3:0] == 4'b0000) - begin - ssp_clk <= 1'b1; - // Don't shift if we just loaded new data, obviously. - if(negedge_cnt[6:0] != 7'd0) - begin - to_arm[7:1] <= to_arm[6:0]; - end - end - - if (mod_type == `TAGSIM_MOD && fdt_elapsed && temp_buffer_reset) - // transmit timing information - bit_to_arm = to_arm[7]; + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// FPGA -> ARM communication: +// select the data to be sent to ARM +reg bit_to_arm; +reg sendbit; + +always @(negedge adc_clk) +begin + if(negedge_cnt[3:0] == 4'd0) + begin + // What do we communicate to the ARM + if(mod_type == `TAGSIM_LISTEN) + sendbit = after_hysteresis; + else if(mod_type == `TAGSIM_MOD) + /* if(fdt_counter > 11'd772) sendbit = mod_sig_coil; // huh? + else */ + sendbit = fdt_indicator; + else if (mod_type == `READER_LISTEN) + sendbit = curbit; else - // transmit data or fdt_indicator - bit_to_arm = sendbit; - end - -end //always @(negedge adc_clk) + sendbit = 1'b0; + end -assign ssp_din = bit_to_arm; + if(mod_type == `SNIFFER) + // send sampled reader and tag data: + bit_to_arm = to_arm[7]; + else if (mod_type == `TAGSIM_MOD && fdt_elapsed && temp_buffer_reset) + // send timing information: + bit_to_arm = to_arm[7]; + else + // send data or fdt_indicator + bit_to_arm = sendbit; +end + + + + +assign ssp_din = bit_to_arm; // Subcarrier (adc_clk/16, for TAGSIM_MOD only). wire sub_carrier; -- 2.39.5