From 1f7bce5580d627b258680cf0d461a7a0ab908b35 Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Tue, 8 Feb 2022 14:47:06 +0100 Subject: [PATCH 1/8] iso15 sniffing: add support for dual subcarriers tag answers --- armsrc/Makefile | 2 +- armsrc/fpgaloader.c | 4 +- armsrc/fpgaloader.h | 10 + armsrc/iso15693.c | 380 +++++++++++++++++++++++++++++++++++-- fpga-xc2s30/Makefile | 9 +- fpga-xc2s30/fpga_hf_15.bit | Bin 0 -> 42178 bytes fpga-xc2s30/fpga_hf_15.v | 260 +++++++++++++++++++++++++ fpga-xc2s30/hi_read_fsk.v | 152 +++++++++++++++ fpga-xc2s30/xst_hf_15.scr | 1 + 9 files changed, 792 insertions(+), 26 deletions(-) create mode 100644 fpga-xc2s30/fpga_hf_15.bit create mode 100644 fpga-xc2s30/fpga_hf_15.v create mode 100644 fpga-xc2s30/hi_read_fsk.v create mode 100644 fpga-xc2s30/xst_hf_15.scr diff --git a/armsrc/Makefile b/armsrc/Makefile index 86ac7b39f..f55f9bc8e 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -103,7 +103,7 @@ endif include Standalone/Makefile.inc #the FPGA bitstream files. Note: order matters! -FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit fpga_felica.bit +FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit fpga_felica.bit fpga_hf_15.bit #the lz4 source files required for decompressing the fpga config at run time SRC_LZ4 = lz4.c diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 1b6360126..ab8d57f7d 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -162,7 +162,7 @@ void FpgaSetupSsc(uint16_t fpga_mode) { // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync // pulse, no output sync - if ((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER && FpgaGetCurrent() == FPGA_BITSTREAM_HF) { + if ((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER && (FpgaGetCurrent() == FPGA_BITSTREAM_HF || FpgaGetCurrent() == FPGA_BITSTREAM_HF_15)) { AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); } else { AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); @@ -612,7 +612,7 @@ void switch_off(void) { Dbprintf("switch_off"); } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - if (downloaded_bitstream == FPGA_BITSTREAM_HF) { + if (downloaded_bitstream == FPGA_BITSTREAM_HF || downloaded_bitstream == FPGA_BITSTREAM_HF_15) { FpgaDisableSscDma(); } set_tracing(false); diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index cff84c3e2..897031042 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -29,6 +29,7 @@ #define FPGA_BITSTREAM_LF 1 #define FPGA_BITSTREAM_HF 2 #define FPGA_BITSTREAM_HF_FELICA 3 +#define FPGA_BITSTREAM_HF_15 4 /* Communication between ARM / FPGA is done inside armsrc/fpgaloader.c (function FpgaSendCommand) @@ -77,6 +78,7 @@ thres| x x x x x x x x #define FPGA_MAJOR_MODE_HF_SNIFF (3<<6) // D #define FPGA_MAJOR_MODE_HF_ISO18092 (4<<6) // D #define FPGA_MAJOR_MODE_HF_GET_TRACE (5<<6) // D +#define FPGA_MAJOR_MODE_HF_FSK_READER (6<<6) // D // BOTH HF / LF #define FPGA_MAJOR_MODE_OFF (7<<6) // D @@ -105,6 +107,14 @@ thres| x x x x x x x x #define FPGA_HF_READER_SUBCARRIER_424_KHZ (1<<4) #define FPGA_HF_READER_SUBCARRIER_212_KHZ (2<<4) +#define FPGA_HF_FSK_READER_OUTPUT_1695_KHZ (0<<0) +#define FPGA_HF_FSK_READER_OUTPUT_848_KHZ (1<<0) +#define FPGA_HF_FSK_READER_OUTPUT_424_KHZ (2<<0) +#define FPGA_HF_FSK_READER_OUTPUT_212_KHZ (3<<0) + +#define FPGA_HF_FSK_READER_NOPOWER (0<<4) +#define FPGA_HF_FSK_READER_WITHPOWER (1<<4) + // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION 0x0 // 0000 #define FPGA_HF_SIMULATOR_MODULATE_BPSK 0x1 // 0001 diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index c08d6130f..c41760e24 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1204,7 +1204,7 @@ void AcquireRawAdcSamplesIso15693(void) { LEDsoff(); DbpString("Starting to acquire data..."); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); BigBuf_free(); clear_trace(); @@ -1259,12 +1259,274 @@ void AcquireRawAdcSamplesIso15693(void) { LEDsoff(); } +//============================================================================= +// An ISO 15693 decoder for tag responses in FSK (two subcarriers) mode. +// Subcarriers frequencies are 424kHz and 484kHz (fc/32 and fc/28), +// 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 +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= +#define DEBUG 0 +#define FREQ_IS_484(f) (f >= 26 && f <= 30) +#define FREQ_IS_424(f) (f >= 30 && f <= 34) +#define FREQ_IS_0(f) (f <= 24 || f >= 36) +#define SEOF_COUNT(c, s) ((s) ? (c >= 11 && c <= 13) : (c >= 44 && c <= 52)) +#define LOGIC_COUNT(c, s) ((s) ? (c >= 3 && c <= 6) : (c >= 13 && c <= 21)) +#define MAX_COUNT(c, s) ((s) ? (c >= 13) : (c >= 52)) +#define MIN_COUNT(c, s) ((s) ? (c <= 2) : (c <= 4)) + +typedef struct DecodeTagFSK { + enum { + STATE_FSK_BEFORE_SOF, + STATE_FSK_SOF_484, + STATE_FSK_SOF_424, + STATE_FSK_SOF_END, + STATE_FSK_RECEIVING_DATA_484, + STATE_FSK_RECEIVING_DATA_424, + STATE_FSK_EOF, + STATE_FSK_ERROR + } state; + enum { + LOGIC0_PART1, + LOGIC1_PART1, + LOGIC0_PART2, + LOGIC1_PART2, + SOF + } lastBit; + uint8_t count; + uint8_t bitCount; + uint8_t shiftReg; + uint16_t len; + uint16_t max_len; + uint8_t *output; +} DecodeTagFSK_t; + +static void DecodeTagFSKReset(DecodeTagFSK_t *DecodeTag) { + DecodeTag->state = STATE_FSK_BEFORE_SOF; + DecodeTag->bitCount = 0; + DecodeTag->len = 0; + DecodeTag->shiftReg = 0; + DbpString("FSK tag reset"); +} + +static void DecodeTagFSKInit(DecodeTagFSK_t *DecodeTag, uint8_t *data, uint16_t max_len) { + DecodeTag->output = data; + DecodeTag->max_len = max_len; + DecodeTagFSKReset(DecodeTag); +} + +// Performances of this function are crutial for stability +// as it is called in real time for every samples +static int inline __attribute__((always_inline)) Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *DecodeTag, bool recv_speed) +{ + switch(DecodeTag->state) { + case STATE_FSK_BEFORE_SOF: + if (FREQ_IS_484(freq)) + { // possible SOF starting + DecodeTag->state = STATE_FSK_SOF_484; + DecodeTag->lastBit = LOGIC0_PART1; + DecodeTag->count = 1; + } + break; + + case STATE_FSK_SOF_484: + //DbpString("STATE_FSK_SOF_484"); + + if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 484 + { + DecodeTag->count++; + } + else if (FREQ_IS_424(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) + { // SOF part1 continue at 424 + DecodeTag->state = STATE_FSK_SOF_424; + DecodeTag->count = 1; + } + else // SOF failed, roll back + { + DecodeTag->state = STATE_FSK_BEFORE_SOF; + } + break; + + case STATE_FSK_SOF_424: + //DbpString("STATE_FSK_SOF_424"); + + if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 424 + DecodeTag->count++; + else if (FREQ_IS_484(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) + { // SOF part 1 finished + DecodeTag->state = STATE_FSK_SOF_END; + DecodeTag->count = 1; + } + else // SOF failed, roll back + { + if (DEBUG) + Dbprintf("SOF_424 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + DecodeTag->state = STATE_FSK_BEFORE_SOF; + } + break; + + case STATE_FSK_SOF_END: + if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END (484) + DecodeTag->count++; + else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) + { // SOF END finished or SOF END 1st part finished + DecodeTag->count = 0; + if (DecodeTag->lastBit == SOF) + { // SOF finished at 424 + if (DEBUG) + DbpString("Receiving data !"); + DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; + LED_C_ON(); + } + DecodeTag->lastBit = SOF; + } + else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END (424) + DecodeTag->count++; + else if (DecodeTag->lastBit == SOF && FREQ_IS_484(freq) && + LOGIC_COUNT(DecodeTag->count, recv_speed)) + { // SOF finished at 484 + DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; + DecodeTag->count = 1; + LED_C_ON(); + } + else // SOF failed, roll back + { + if (DEBUG) + Dbprintf("SOF_END failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + DecodeTag->state = STATE_FSK_BEFORE_SOF; + } + break; + + + case STATE_FSK_RECEIVING_DATA_424: + if (DecodeTag->lastBit == LOGIC1_PART1 && + LOGIC_COUNT(DecodeTag->count, recv_speed)) + { // logic 1 finished + DecodeTag->lastBit = LOGIC1_PART2; + DecodeTag->count = 0; + + DecodeTag->shiftReg >>= 1; + DecodeTag->shiftReg |= 0x80; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 424 + DecodeTag->count++; + else if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed) && + DecodeTag->lastBit >= LOGIC0_PART2) + { // end of LOGIC0_PART1 + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; + DecodeTag->lastBit = LOGIC0_PART1; + } + else if (FREQ_IS_484(freq) && MIN_COUNT(DecodeTag->count, recv_speed)) + { // it was just the end of the previous block + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; + } + else if (FREQ_IS_484(freq) && DecodeTag->lastBit == LOGIC0_PART2 && + SEOF_COUNT(DecodeTag->count, recv_speed)) + { // EOF has started + if (DEBUG) + Dbprintf("RECEIVING_DATA_424 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_EOF; + LED_C_OFF(); + } + else // error + { + if (DEBUG) + Dbprintf("RECEIVING_DATA_424 error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + DecodeTag->state = STATE_FSK_ERROR; + LED_C_OFF(); + return true; + } + break; + + case STATE_FSK_RECEIVING_DATA_484: + if (DecodeTag->lastBit == LOGIC0_PART1 && + LOGIC_COUNT(DecodeTag->count, recv_speed)) + { // logic 0 finished + DecodeTag->lastBit = LOGIC0_PART2; + DecodeTag->count = 0; + + DecodeTag->shiftReg >>= 1; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 484 + DecodeTag->count++; + else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed) && + DecodeTag->lastBit >= LOGIC0_PART2) + { // end of LOGIC1_PART1 + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; + DecodeTag->lastBit = LOGIC1_PART1; + } + else if (FREQ_IS_424(freq) && MIN_COUNT(DecodeTag->count, recv_speed)) + { // it was just the end of the previous block + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; + } + else // error + { + if (DEBUG) + Dbprintf("RECEIVING_DATA_484 error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + LED_C_OFF(); + DecodeTag->state = STATE_FSK_ERROR; + return true; + } + break; + + case STATE_FSK_EOF: + if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 484 + { + DecodeTag->count++; + if (SEOF_COUNT(DecodeTag->count, recv_speed)) + return true; // end of the transmission + } + else // error + { + if (DEBUG) + Dbprintf("EOF error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + DecodeTag->state = STATE_FSK_ERROR; + return true; + } + break; + case STATE_FSK_ERROR: + LED_C_OFF(); + return true; // error + break; + } + return false; +} void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { LEDsoff(); LED_A_ON(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); DbpString("Starting to sniff. Press PM3 Button to stop."); @@ -1276,6 +1538,10 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { uint8_t response[ISO15693_MAX_RESPONSE_LENGTH] = {0}; DecodeTagInit(&dtag, response, sizeof(response)); + DecodeTagFSK_t dtagfsk = {0}; + uint8_t response2[ISO15693_MAX_RESPONSE_LENGTH] = {0}; + DecodeTagFSKInit(&dtagfsk, response2, sizeof(response2)); + DecodeReader_t dreader = {0}; uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH] = {0}; DecodeReaderInit(&dreader, cmd, sizeof(cmd), jam_search_len, jam_search_string); @@ -1301,6 +1567,8 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { bool tag_is_active = false; bool reader_is_active = false; bool expect_tag_answer = false; + bool expect_fsk_answer = false; + bool expect_fast_answer = false; int dma_start_time = 0; // Count of samples received so far, so that we can include timing @@ -1361,13 +1629,20 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { - 32 * 16 // time for SOF transfer - 16 * 16; // time for EOF transfer LogTrace_ISO15693(dreader.output, dreader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); + + expect_fsk_answer = dreader.output[0] & ISO15_REQ_SUBCARRIER_TWO; + expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; } // And ready to receive another command. DecodeReaderReset(&dreader); DecodeTagReset(&dtag); + DecodeTagFSKReset(&dtagfsk); reader_is_active = false; expect_tag_answer = true; - + if (expect_fsk_answer) + { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_FSK_READER | FPGA_HF_FSK_READER_OUTPUT_212_KHZ | FPGA_HF_FSK_READER_NOPOWER); + } } else if (Handle15693SampleFromReader(sniffdata & 0x01, &dreader)) { uint32_t eof_time = dma_start_time + (samples * 16) + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF @@ -1377,13 +1652,21 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { - 32 * 16 // time for SOF transfer - 16 * 16; // time for EOF transfer LogTrace_ISO15693(dreader.output, dreader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); + + expect_fsk_answer = dreader.output[0] & ISO15_REQ_SUBCARRIER_TWO; + expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; } // And ready to receive another command DecodeReaderReset(&dreader); DecodeTagReset(&dtag); + DecodeTagFSKReset(&dtagfsk); reader_is_active = false; expect_tag_answer = true; + if (expect_fsk_answer) + { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_FSK_READER | FPGA_HF_FSK_READER_OUTPUT_212_KHZ | FPGA_HF_FSK_READER_NOPOWER); + } } else { reader_is_active = (dreader.state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); } @@ -1391,25 +1674,77 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { if (reader_is_active == false && expect_tag_answer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet - if (Handle15693SamplesFromTag(sniffdata >> 2, &dtag)) { + if (!expect_fsk_answer) + { + if (Handle15693SamplesFromTag(sniffdata >> 2, &dtag)) { - uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF - if (dtag.lastBit == SOF_PART2) { - eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF + if (dtag.lastBit == SOF_PART2) { + eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + } + uint32_t sof_time = eof_time + - dtag.len * 8 * 8 * 16 // time for byte transfers + - (32 * 16) // time for SOF transfer + - (dtag.lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer + + LogTrace_ISO15693(dtag.output, dtag.len, (sof_time * 4), (eof_time * 4), NULL, false); + // And ready to receive another response. + DecodeTagReset(&dtag); + DecodeTagFSKReset(&dtagfsk); + DecodeReaderReset(&dreader); + expect_tag_answer = false; + tag_is_active = false; + } else { + tag_is_active = (dtag.state >= STATE_TAG_RECEIVING_DATA); } - uint32_t sof_time = eof_time - - dtag.len * 8 * 8 * 16 // time for byte transfers - - (32 * 16) // time for SOF transfer - - (dtag.lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer + } + else + { + if (Handle15693FSKSamplesFromTag(sniffdata >> 8, &dtagfsk, expect_fast_answer)) { + uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF + if (dtagfsk.lastBit == SOF) { + eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + } + uint32_t sof_time = eof_time + - dtagfsk.len * 8 * 8 * 16 // time for byte transfers + - (32 * 16) // time for SOF transfer + - (dtagfsk.lastBit != SOF ? (32 * 16) : 0); // time for EOF transfer - LogTrace_ISO15693(dtag.output, dtag.len, (sof_time * 4), (eof_time * 4), NULL, false); - // And ready to receive another response. - DecodeTagReset(&dtag); - DecodeReaderReset(&dreader); - expect_tag_answer = false; - tag_is_active = false; - } else { - tag_is_active = (dtag.state >= STATE_TAG_RECEIVING_DATA); + LogTrace_ISO15693(dtagfsk.output, dtagfsk.len, (sof_time * 4), (eof_time * 4), NULL, false); + // And ready to receive another response. + DecodeTagFSKReset(&dtagfsk); + DecodeTagReset(&dtag); + DecodeReaderReset(&dreader); + expect_tag_answer = false; + tag_is_active = false; + expect_fsk_answer = false; + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); + } + else if (Handle15693FSKSamplesFromTag(sniffdata & 0xFF, &dtagfsk, expect_fast_answer)) { + + uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF + if (dtagfsk.lastBit == SOF) { + eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + } + uint32_t sof_time = eof_time + - dtagfsk.len * 8 * 8 * 16 // time for byte transfers + - (32 * 16) // time for SOF transfer + - (dtagfsk.lastBit != SOF ? (32 * 16) : 0); // time for EOF transfer + + LogTrace_ISO15693(dtagfsk.output, dtagfsk.len, (sof_time * 4), (eof_time * 4), NULL, false); + // And ready to receive another response. + DecodeTagFSKReset(&dtagfsk); + DecodeTagReset(&dtag); + DecodeReaderReset(&dreader); + expect_tag_answer = false; + tag_is_active = false; + expect_fsk_answer = false; + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); + } else { + tag_is_active = (dtagfsk.state >= STATE_FSK_RECEIVING_DATA_484); + } } } @@ -1424,6 +1759,9 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { Dbprintf(" DecodeTag State........%d", dtag.state); Dbprintf(" DecodeTag byteCnt......%d", dtag.len); Dbprintf(" DecodeTag posCount.....%d", dtag.posCount); + Dbprintf(" DecodeTagFSK State........%d", dtagfsk.state); + Dbprintf(" DecodeTagFSK byteCnt......%d", dtagfsk.len); + Dbprintf(" DecodeTagFSK count.....%d", dtagfsk.count); Dbprintf(" DecodeReader State.....%d", dreader.state); Dbprintf(" DecodeReader byteCnt...%d", dreader.byteCount); Dbprintf(" DecodeReader posCount..%d", dreader.posCount); @@ -1436,7 +1774,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { void Iso15693InitReader(void) { LEDsoff(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -1679,7 +2017,7 @@ void ReaderIso15693(uint32_t parameter, iso15_card_select_t *p_card) { // When SIM: initialize the Proxmark3 as ISO15693 tag void Iso15693InitTag(void) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); diff --git a/fpga-xc2s30/Makefile b/fpga-xc2s30/Makefile index 9b1f7a5ca..59eaf8b79 100644 --- a/fpga-xc2s30/Makefile +++ b/fpga-xc2s30/Makefile @@ -5,9 +5,9 @@ RMDIR = rm -rf # rmdir only if dir is empty, tolerate failure RMDIR_SOFT = -rmdir # -all: fpga_lf.bit fpga_hf.bit fpga_felica.bit +all: fpga_lf.bit fpga_hf.bit fpga_felica.bit fpga_hf_15.bit clean: - $(Q)$(RM) *.bgn *.drc *.ncd *.ngd *_par.xrpt *-placed.* *-placed_pad.* *_usage.xml xst_hf.srp xst_lf.srp xst_felica.srp + $(Q)$(RM) *.bgn *.drc *.ncd *.ngd *_par.xrpt *-placed.* *-placed_pad.* *_usage.xml xst_hf.srp xst_lf.srp xst_felica.srp xst_hf_15.srp $(Q)$(RM) *.map *.ngc *.xrpt *.pcf *.rbt *.bld *.mrp *.ngm *.unroutes *_summary.xml netlist.lst $(Q)$(RMDIR) *_auto_* xst @@ -22,6 +22,11 @@ fpga_felica.ngc: fpga_felica.v fpga.ucf xst_felica.scr util.v hi_simulate.v hi_r $(info [-] XST $@) $(Q)$(XILINX_TOOLS_PREFIX)xst -ifn xst_felica.scr +fpga_hf_15.ngc: fpga_hf_15.v fpga.ucf xst_hf.scr util.v hi_simulate.v hi_reader.v hi_iso14443a.v hi_sniffer.v hi_get_trace.v hi_read_fsk.v + $(Q)$(RM) $@ + $(info [-] XST $@) + $(Q)$(XILINX_TOOLS_PREFIX)xst -ifn xst_hf_15.scr + fpga_lf.ngc: fpga_lf.v fpga.ucf xst_lf.scr util.v clk_divider.v lo_edge_detect.v lo_read.v lo_passthru.v lp20khz_1MSa_iir_filter.v min_max_tracker.v lf_edge_detect.v $(Q)$(RM) $@ $(info [-] XST $@) diff --git a/fpga-xc2s30/fpga_hf_15.bit b/fpga-xc2s30/fpga_hf_15.bit new file mode 100644 index 0000000000000000000000000000000000000000..916d843407f2a40f6ee2bb01a94b9ce0827359a0 GIT binary patch literal 42178 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 oq!DQ9jlb95X_Dd%z@a9|kp~Hd;N0a>-!*8l(j literal 0 HcmV?d00001 diff --git a/fpga-xc2s30/fpga_hf_15.v b/fpga-xc2s30/fpga_hf_15.v new file mode 100644 index 000000000..59118ef33 --- /dev/null +++ b/fpga-xc2s30/fpga_hf_15.v @@ -0,0 +1,260 @@ +//----------------------------------------------------------------------------- +// The FPGA is responsible for interfacing between the A/D, the coil drivers, +// and the ARM. In the low-frequency modes it passes the data straight +// through, so that the ARM gets raw A/D samples over the SSP. In the high- +// frequency modes, the FPGA might perform some demodulation first, to +// reduce the amount of data that we must send to the ARM. +// +// I am not really an FPGA/ASIC designer, so I am sure that a lot of this +// could be improved. +// +// Jonathan Westhues, March 2006 +// Added ISO14443-A support by Gerhard de Koning Gans, April 2008 +// iZsh , June 2014 +// Piwi, Feb 2019 +//----------------------------------------------------------------------------- + + +// Defining commands, modes and options. This must be aligned to the definitions in fpgaloader.h +// Note: the definitions here are without shifts + +// Commands: +`define FPGA_CMD_SET_CONFREG 1 +`define FPGA_CMD_TRACE_ENABLE 2 + +// Major modes: +`define FPGA_MAJOR_MODE_HF_READER 0 +`define FPGA_MAJOR_MODE_HF_SIMULATOR 1 +`define FPGA_MAJOR_MODE_HF_ISO14443A 2 +`define FPGA_MAJOR_MODE_HF_SNIFF 3 +`define FPGA_MAJOR_MODE_HF_ISO18092 4 +`define FPGA_MAJOR_MODE_HF_GET_TRACE 5 +`define FPGA_MAJOR_MODE_HF_FSK_READER 6 +`define FPGA_MAJOR_MODE_OFF 7 + +// Options for the generic HF reader +`define FPGA_HF_READER_MODE_RECEIVE_IQ 0 +`define FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE 1 +`define FPGA_HF_READER_MODE_RECEIVE_PHASE 2 +`define FPGA_HF_READER_MODE_SEND_FULL_MOD 3 +`define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD 4 +`define FPGA_HF_READER_MODE_SNIFF_IQ 5 +`define FPGA_HF_READER_MODE_SNIFF_AMPLITUDE 6 +`define FPGA_HF_READER_MODE_SNIFF_PHASE 7 +`define FPGA_HF_READER_MODE_SEND_JAM 8 + +`define FPGA_HF_READER_SUBCARRIER_848_KHZ 0 +`define FPGA_HF_READER_SUBCARRIER_424_KHZ 1 +`define FPGA_HF_READER_SUBCARRIER_212_KHZ 2 + +`define FPGA_HF_FSK_READER_OUTPUT_1695_KHZ 0 +`define FPGA_HF_FSK_READER_OUTPUT_848_KHZ 1 +`define FPGA_HF_FSK_READER_OUTPUT_424_KHZ 2 +`define FPGA_HF_FSK_READER_OUTPUT_212_KHZ 3 + +`define FPGA_HF_FSK_READER_NOPOWER 0 +`define FPGA_HF_FSK_READER_WITHPOWER 1 + +// Options for the HF simulated tag, how to modulate +`define FPGA_HF_SIMULATOR_NO_MODULATION 0 +`define FPGA_HF_SIMULATOR_MODULATE_BPSK 1 +`define FPGA_HF_SIMULATOR_MODULATE_212K 2 +`define FPGA_HF_SIMULATOR_MODULATE_424K 4 +`define FPGA_HF_SIMULATOR_MODULATE_424K_8BIT 5 + +// Options for ISO14443A +`define FPGA_HF_ISO14443A_SNIFFER 0 +`define FPGA_HF_ISO14443A_TAGSIM_LISTEN 1 +`define FPGA_HF_ISO14443A_TAGSIM_MOD 2 +`define FPGA_HF_ISO14443A_READER_LISTEN 3 +`define FPGA_HF_ISO14443A_READER_MOD 4 + +//options for ISO18092 / Felica +`define FPGA_HF_ISO18092_FLAG_NOMOD 1 // 0001 disable modulation module +`define FPGA_HF_ISO18092_FLAG_424K 2 // 0010 should enable 414k mode (untested). No autodetect +`define FPGA_HF_ISO18092_FLAG_READER 4 // 0100 enables antenna power, to act as a reader instead of tag + +`include "hi_reader.v" +`include "hi_simulate.v" +//`include "hi_iso14443a.v" +`include "hi_sniffer.v" +`include "util.v" +// `include "hi_flite.v" +`include "hi_get_trace.v" +`include "hi_read_fsk.v" + +module fpga_hf_15( + input spck, output miso, input mosi, input ncs, + input pck0, input ck_1356meg, input ck_1356megb, + output pwr_lo, output pwr_hi, + output pwr_oe1, output pwr_oe2, output pwr_oe3, output pwr_oe4, + input [7:0] adc_d, output adc_clk, output adc_noe, + output ssp_frame, output ssp_din, input ssp_dout, output ssp_clk, + input cross_hi, input cross_lo, + output dbg +); + +//----------------------------------------------------------------------------- +// 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 +// drivers (i.e., which section gets it). Also assign some symbolic names +// to the configuration bits, for use below. +//----------------------------------------------------------------------------- + +/* + Attempt to write up how its hooked up. + / Iceman, 2020 + + Communication between ARM / FPGA is done inside armsrc/fpgaloader.c see: function FpgaSendCommand() + Send 16 bit command / data pair to FPGA + The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 + where + C is 4bit command + D is 12bit data + + shift_reg receive this 16bit frame + + +-----+--------- frame layout -------------------- +bit | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +-----+------------------------------------------- +cmd | x x x x +major| x x x +opt | x x x x +sub | x x +divi | x x x x x x x x +thres| x x x x x x x x +-----+------------------------------------------- +*/ + +reg [15:0] shift_reg; +reg [8:0] conf_word; +reg trace_enable; + +// We switch modes between transmitting to the 13.56 MHz tag and receiving +// from it, which means that we must make sure that we can do so without +// glitching, or else we will glitch the transmitted carrier. +always @(posedge ncs) +begin + case(shift_reg[15:12]) + `FPGA_CMD_SET_CONFREG: conf_word <= shift_reg[8:0]; + `FPGA_CMD_TRACE_ENABLE: trace_enable <= shift_reg[0]; + endcase +end + +always @(posedge spck) +begin + if(~ncs) + begin + shift_reg[15:1] <= shift_reg[14:0]; + shift_reg[0] <= mosi; + end +end + +// select module (outputs) based on major mode +wire [2:0] major_mode = conf_word[8:6]; + +// configuring the HF reader +wire [1:0] subcarrier_frequency = conf_word[5:4]; +wire [3:0] minor_mode = conf_word[3:0]; + +//----------------------------------------------------------------------------- +// And then we instantiate the modules corresponding to each of the FPGA's +// major modes, and use muxes to connect the outputs of the active mode to +// the output pins. +//----------------------------------------------------------------------------- + +// 000 - HF reader +hi_reader hr( + ck_1356megb, + hr_pwr_lo, hr_pwr_hi, hr_pwr_oe1, hr_pwr_oe2, hr_pwr_oe3, hr_pwr_oe4, + adc_d, hr_adc_clk, + hr_ssp_frame, hr_ssp_din, ssp_dout, hr_ssp_clk, + hr_dbg, + subcarrier_frequency, minor_mode +); + +// 001 - HF simulated tag +hi_simulate hs( + ck_1356meg, + hs_pwr_lo, hs_pwr_hi, hs_pwr_oe1, hs_pwr_oe2, hs_pwr_oe3, hs_pwr_oe4, + adc_d, hs_adc_clk, + hs_ssp_frame, hs_ssp_din, ssp_dout, hs_ssp_clk, + hs_dbg, + minor_mode +); + +/*// 010 - HF ISO14443-A +hi_iso14443a hisn( + ck_1356meg, + hisn_pwr_lo, hisn_pwr_hi, hisn_pwr_oe1, hisn_pwr_oe2, hisn_pwr_oe3, hisn_pwr_oe4, + adc_d, hisn_adc_clk, + hisn_ssp_frame, hisn_ssp_din, ssp_dout, hisn_ssp_clk, + hisn_dbg, + minor_mode +);*/ + +// 011 - HF sniff +hi_sniffer he( + ck_1356megb, + he_pwr_lo, he_pwr_hi, he_pwr_oe1, he_pwr_oe2, he_pwr_oe3, he_pwr_oe4, + adc_d, he_adc_clk, + he_ssp_frame, he_ssp_din, he_ssp_clk +); + +// 100 - HF ISO18092 FeliCa +/* +hi_flite hfl( + ck_1356megb, + hfl_pwr_lo, hfl_pwr_hi, hfl_pwr_oe1, hfl_pwr_oe2, hfl_pwr_oe3, hfl_pwr_oe4, + adc_d, hfl_adc_clk, + hfl_ssp_frame, hfl_ssp_din, ssp_dout, hfl_ssp_clk, + hfl_dbg, + minor_mode +); +*/ + +// 101 - HF get trace +hi_get_trace gt( + ck_1356megb, + adc_d, trace_enable, major_mode, + gt_ssp_frame, gt_ssp_din, gt_ssp_clk +); + +// 110 - HF Read FSK +hi_read_fsk hrf( + ck_1356meg, + hrf_pwr_lo, hrf_pwr_hi, hrf_pwr_oe1, hrf_pwr_oe2, hrf_pwr_oe3, hrf_pwr_oe4, + adc_d, hrf_adc_clk, + hrf_ssp_frame, hrf_ssp_din, hrf_ssp_clk, + subcarrier_frequency, minor_mode +); + +// Major modes: +// 000 -- HF reader; subcarrier frequency and modulation depth selectable +// 001 -- HF simulated tag +// 010 -- HF ISO14443-A +// 011 -- HF sniff +// 100 -- HF ISO18092 FeliCa +// 101 -- HF get trace +// 110 -- HF Read FSK +// 111 -- FPGA_MAJOR_MODE_OFF + +// 000 001 010 011 100 101 110 111 + +mux8 mux_ssp_clk (major_mode, ssp_clk, hr_ssp_clk, hs_ssp_clk, 1'b0, he_ssp_clk, hfl_ssp_clk, gt_ssp_clk, hrf_ssp_clk, 1'b0); +mux8 mux_ssp_din (major_mode, ssp_din, hr_ssp_din, hs_ssp_din, 1'b0, he_ssp_din, hfl_ssp_din, gt_ssp_din, hrf_ssp_din, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, hr_ssp_frame, hs_ssp_frame, 1'b0, he_ssp_frame, hfl_ssp_frame, gt_ssp_frame, hrf_ssp_frame, 1'b0); +mux8 mux_pwr_oe1 (major_mode, pwr_oe1, hr_pwr_oe1, hs_pwr_oe1, 1'b0, he_pwr_oe1, hfl_pwr_oe1, 1'b0, hrf_pwr_oe1, 1'b0); +mux8 mux_pwr_oe2 (major_mode, pwr_oe2, hr_pwr_oe2, hs_pwr_oe2, 1'b0, he_pwr_oe2, hfl_pwr_oe2, 1'b0, hrf_pwr_oe2, 1'b0); +mux8 mux_pwr_oe3 (major_mode, pwr_oe3, hr_pwr_oe3, hs_pwr_oe3, 1'b0, he_pwr_oe3, hfl_pwr_oe3, 1'b0, hrf_pwr_oe3, 1'b0); +mux8 mux_pwr_oe4 (major_mode, pwr_oe4, hr_pwr_oe4, hs_pwr_oe4, 1'b0, he_pwr_oe4, hfl_pwr_oe4, 1'b0, hrf_pwr_oe4, 1'b0); +mux8 mux_pwr_lo (major_mode, pwr_lo, hr_pwr_lo, hs_pwr_lo, 1'b0, he_pwr_lo, hfl_pwr_lo, 1'b0, hrf_pwr_lo, 1'b0); +mux8 mux_pwr_hi (major_mode, pwr_hi, hr_pwr_hi, hs_pwr_hi, 1'b0, he_pwr_hi, hfl_pwr_hi, 1'b0, hrf_pwr_hi, 1'b0); +mux8 mux_adc_clk (major_mode, adc_clk, hr_adc_clk, hs_adc_clk, 1'b0, he_adc_clk, hfl_adc_clk, 1'b0, hrf_adc_clk, 1'b0); +mux8 mux_dbg (major_mode, dbg, hr_dbg, hs_dbg, 1'b0, he_dbg, hfl_dbg, 1'b0, 1'b0, 1'b0); + +// In all modes, let the ADC's outputs be enabled. +assign adc_noe = 1'b0; + +endmodule diff --git a/fpga-xc2s30/hi_read_fsk.v b/fpga-xc2s30/hi_read_fsk.v new file mode 100644 index 000000000..e45162111 --- /dev/null +++ b/fpga-xc2s30/hi_read_fsk.v @@ -0,0 +1,152 @@ +// lnv42, Jan 2020 +// reworked && integrated to RRG in Fev 2022 +// HF FSK reader (used for iso15 sniffing/reading) + +// output is the frequence divider from 13,56 MHz + +// (eg. for iso 15 two subcarriers mode (423,75 khz && 484,28 khz): it return 32 or 28) +// (423,75k = 13.56M / 32 and 484.28k = 13,56M / 28) + +module hi_read_fsk( + ck_1356meg, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_clk, + subcarrier_frequency, minor_mode +); + + input ck_1356meg; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + output ssp_frame, ssp_din, ssp_clk; + input [1:0]subcarrier_frequency; + input [3:0] minor_mode; + +assign adc_clk = ck_1356meg; // input sample frequency is 13,56 MHz + +assign power = subcarrier_frequency[0]; + +// Carrier is on if power is on, else is 0 +reg pwr_hi; +always @(ck_1356meg) +begin + if (power == `FPGA_HF_FSK_READER_WITHPOWER) + pwr_hi <= ck_1356meg; + else + pwr_hi <= 'b0; +end + +reg [7:0] adc_cnt = 8'd0; +reg [7:0] out1 = 8'd0; +reg [7:0] old = 8'd0; +reg [7:0] edge_id = 8'd0; +reg edge_started = 1'd0; +// Count clock edge between two signal edges +always @(negedge adc_clk) +begin + adc_cnt <= adc_cnt + 1'd1; + + if (& adc_d[7:5] && !(& old[7:5])) // up + begin + if (edge_started == 1'd0) // new edge starting + begin + if (edge_id <= adc_cnt) + out1 <= adc_cnt - edge_id; + else + out1 <= adc_cnt + 9'h100 - edge_id; + edge_id <= adc_cnt; + edge_started = 1'd1; + end + end + else + begin + edge_started = 1'd0; + if (edge_id <= adc_cnt) + begin + if (adc_cnt - edge_id > 8'd40) + begin + out1 <= 8'd0; + end + end + else + begin + if (adc_cnt + 9'h100 - edge_id > 8'd40) + begin + out1 <= 8'd0; + end + end + end + + old <= adc_d; +end + +// agregate out values (depending on selected output frequency) +reg [10:0] out_tmp = 11'd0; +reg [7:0] out = 8'd0; +always @(negedge adc_clk) +begin + out_tmp <= out_tmp + out1; + if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_848_KHZ && adc_cnt[0] == 1'd0) + begin // average on 2 values + out <= out_tmp[8:1]; + out_tmp <= 12'd0; + end + else if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_424_KHZ && adc_cnt[1:0] == 2'd0) + begin // average on 4 values + out <= out_tmp[9:2]; + out_tmp <= 12'd0; + end + else if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_212_KHZ && adc_cnt[2:0] == 3'd0) + begin // average on 8 values + out <= out_tmp[10:3]; + out_tmp <= 12'd0; + end + else // 1695_KHZ + out <= out1; +end + +// Set output (ssp) clock +(* clock_signal = "yes" *) reg ssp_clk; +always @(ck_1356meg) +begin + if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_1695_KHZ) + ssp_clk <= ~ck_1356meg; + else if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_848_KHZ) + ssp_clk <= ~adc_cnt[0]; + else if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_424_KHZ) + ssp_clk <= ~adc_cnt[1]; + else // 212 KHz + ssp_clk <= ~adc_cnt[2]; +end + +// Transmit output +reg ssp_frame; +reg [7:0] ssp_out = 8'd0; +reg [2:0] ssp_cnt = 4'd0; +always @(posedge ssp_clk) +begin + ssp_cnt <= ssp_cnt + 1'd1; + if(ssp_cnt == 3'd15) + begin + ssp_out <= out; + ssp_frame <= 1'b1; + end + else + begin + ssp_out <= {ssp_out[6:0], 1'b0}; + ssp_frame <= 1'b0; + end +end + +assign ssp_din = ssp_out[7]; + +// Unused. +assign pwr_oe4 = 1'b0; +assign pwr_oe1 = 1'b0; +assign pwr_oe3 = 1'b0; +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; + +endmodule + diff --git a/fpga-xc2s30/xst_hf_15.scr b/fpga-xc2s30/xst_hf_15.scr new file mode 100644 index 000000000..d8bb5ae7c --- /dev/null +++ b/fpga-xc2s30/xst_hf_15.scr @@ -0,0 +1 @@ +run -ifn fpga_hf_15.v -ifmt Verilog -ofn fpga_hf_15.ngc -ofmt NGC -p xc2s30-5-vq100 -top fpga_hf_15 -opt_mode area -opt_level 2 -resource_sharing yes -fsm_style bram -fsm_encoding compact From 38d49097f981240a0664319a1ff9a9c104bca86b Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Fri, 4 Mar 2022 09:45:12 +0100 Subject: [PATCH 2/8] iso15 sniffing: intragrate 2SC sniffing in same FPGA mode switching Fpgamode while sniffing with FpgaWriteConfWord() was sometimes too long so the tag answer start was lost. Now, (only with FPGA_BITSTREAM_HF_15) with "FPGA_HF_READER_MODE_SNIFF_AMPLITUDE | FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ": the amplitude is shrank from its 2 LSB bits and those 2 bits are now used to return the current frequency. From my tests, this 2 bits reduction does not affect quality of 1SC sniffing, but it may have slightly reduced the receiving range. FPGA FSK decoding code is also improved. --- armsrc/fpgaloader.c | 4 +- armsrc/fpgaloader.h | 9 +- armsrc/iso15693.c | 27 +-- fpga-xc2s30/Makefile | 2 +- fpga-xc2s30/fpga_hf_15.bit | Bin 42178 -> 42178 bytes fpga-xc2s30/fpga_hf_15.v | 36 ++- fpga-xc2s30/hi_reader_15.v | 443 +++++++++++++++++++++++++++++++++++++ 7 files changed, 469 insertions(+), 52 deletions(-) create mode 100644 fpga-xc2s30/hi_reader_15.v diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index ab8d57f7d..80d793b82 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -162,7 +162,9 @@ void FpgaSetupSsc(uint16_t fpga_mode) { // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync // pulse, no output sync - if ((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER && (FpgaGetCurrent() == FPGA_BITSTREAM_HF || FpgaGetCurrent() == FPGA_BITSTREAM_HF_15)) { + if (((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER || + (fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_FSK_READER) && + (FpgaGetCurrent() == FPGA_BITSTREAM_HF || FpgaGetCurrent() == FPGA_BITSTREAM_HF_15)) { AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); } else { AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 897031042..6069d3257 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -106,14 +106,7 @@ thres| x x x x x x x x #define FPGA_HF_READER_SUBCARRIER_848_KHZ (0<<4) #define FPGA_HF_READER_SUBCARRIER_424_KHZ (1<<4) #define FPGA_HF_READER_SUBCARRIER_212_KHZ (2<<4) - -#define FPGA_HF_FSK_READER_OUTPUT_1695_KHZ (0<<0) -#define FPGA_HF_FSK_READER_OUTPUT_848_KHZ (1<<0) -#define FPGA_HF_FSK_READER_OUTPUT_424_KHZ (2<<0) -#define FPGA_HF_FSK_READER_OUTPUT_212_KHZ (3<<0) - -#define FPGA_HF_FSK_READER_NOPOWER (0<<4) -#define FPGA_HF_FSK_READER_WITHPOWER (1<<4) +#define FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ (3<<4) // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION 0x0 // 0000 diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index c41760e24..919040d0e 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1269,10 +1269,10 @@ void AcquireRawAdcSamplesIso15693(void) { // Returns: true if we received a EOF // false if we are still waiting for some more //============================================================================= -#define DEBUG 0 -#define FREQ_IS_484(f) (f >= 26 && f <= 30) -#define FREQ_IS_424(f) (f >= 30 && f <= 34) -#define FREQ_IS_0(f) (f <= 24 || f >= 36) +//#define DEBUG 1 +#define FREQ_IS_484(f) ((f & 1) == 1) //(f >= 26 && f <= 30) +#define FREQ_IS_424(f) ((f & 2) == 2) //(f >= 30 && f <= 34) +#define FREQ_IS_0(f) ((f & 3) == 0) // (f <= 24 || f >= 36) #define SEOF_COUNT(c, s) ((s) ? (c >= 11 && c <= 13) : (c >= 44 && c <= 52)) #define LOGIC_COUNT(c, s) ((s) ? (c >= 3 && c <= 6) : (c >= 13 && c <= 21)) #define MAX_COUNT(c, s) ((s) ? (c >= 13) : (c >= 52)) @@ -1320,7 +1320,7 @@ static void DecodeTagFSKInit(DecodeTagFSK_t *DecodeTag, uint8_t *data, uint16_t // Performances of this function are crutial for stability // as it is called in real time for every samples -static int inline __attribute__((always_inline)) Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *DecodeTag, bool recv_speed) +static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *DecodeTag, bool recv_speed, int samples) { switch(DecodeTag->state) { case STATE_FSK_BEFORE_SOF: @@ -1546,7 +1546,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH] = {0}; DecodeReaderInit(&dreader, cmd, sizeof(cmd), jam_search_len, jam_search_string); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE | FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ); LED_D_OFF(); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1639,10 +1639,6 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { DecodeTagFSKReset(&dtagfsk); reader_is_active = false; expect_tag_answer = true; - if (expect_fsk_answer) - { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_FSK_READER | FPGA_HF_FSK_READER_OUTPUT_212_KHZ | FPGA_HF_FSK_READER_NOPOWER); - } } else if (Handle15693SampleFromReader(sniffdata & 0x01, &dreader)) { uint32_t eof_time = dma_start_time + (samples * 16) + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF @@ -1662,11 +1658,6 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { DecodeTagFSKReset(&dtagfsk); reader_is_active = false; expect_tag_answer = true; - if (expect_fsk_answer) - { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_FSK_READER | FPGA_HF_FSK_READER_OUTPUT_212_KHZ | FPGA_HF_FSK_READER_NOPOWER); - - } } else { reader_is_active = (dreader.state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); } @@ -1676,7 +1667,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { if (!expect_fsk_answer) { - if (Handle15693SamplesFromTag(sniffdata >> 2, &dtag)) { + if (Handle15693SamplesFromTag((sniffdata >> 4) << 2, &dtag)) { uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF if (dtag.lastBit == SOF_PART2) { @@ -1700,7 +1691,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } else { - if (Handle15693FSKSamplesFromTag(sniffdata >> 8, &dtagfsk, expect_fast_answer)) { + if (Handle15693FSKSamplesFromTag((sniffdata >> 2) & 0x3, &dtagfsk, expect_fast_answer, samples-fsksamples)) { uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF if (dtagfsk.lastBit == SOF) { eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) @@ -1719,7 +1710,6 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { tag_is_active = false; expect_fsk_answer = false; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); } else if (Handle15693FSKSamplesFromTag(sniffdata & 0xFF, &dtagfsk, expect_fast_answer)) { @@ -1741,7 +1731,6 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { tag_is_active = false; expect_fsk_answer = false; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); } else { tag_is_active = (dtagfsk.state >= STATE_FSK_RECEIVING_DATA_484); } diff --git a/fpga-xc2s30/Makefile b/fpga-xc2s30/Makefile index 59eaf8b79..d6f05c430 100644 --- a/fpga-xc2s30/Makefile +++ b/fpga-xc2s30/Makefile @@ -22,7 +22,7 @@ fpga_felica.ngc: fpga_felica.v fpga.ucf xst_felica.scr util.v hi_simulate.v hi_r $(info [-] XST $@) $(Q)$(XILINX_TOOLS_PREFIX)xst -ifn xst_felica.scr -fpga_hf_15.ngc: fpga_hf_15.v fpga.ucf xst_hf.scr util.v hi_simulate.v hi_reader.v hi_iso14443a.v hi_sniffer.v hi_get_trace.v hi_read_fsk.v +fpga_hf_15.ngc: fpga_hf_15.v fpga.ucf xst_hf.scr util.v hi_simulate.v hi_reader_15.v hi_iso14443a.v hi_sniffer.v hi_get_trace.v $(Q)$(RM) $@ $(info [-] XST $@) $(Q)$(XILINX_TOOLS_PREFIX)xst -ifn xst_hf_15.scr diff --git a/fpga-xc2s30/fpga_hf_15.bit b/fpga-xc2s30/fpga_hf_15.bit index 916d843407f2a40f6ee2bb01a94b9ce0827359a0..678e746c48393129982605bf251edeb50880c306 100644 GIT binary patch literal 42178 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�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 literal 42178 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 oq!DQ9jlb95X_Dd%z@a9|kp~Hd;N0a>-!*8l(j diff --git a/fpga-xc2s30/fpga_hf_15.v b/fpga-xc2s30/fpga_hf_15.v index 59118ef33..d5c2138d6 100644 --- a/fpga-xc2s30/fpga_hf_15.v +++ b/fpga-xc2s30/fpga_hf_15.v @@ -46,6 +46,7 @@ `define FPGA_HF_READER_SUBCARRIER_848_KHZ 0 `define FPGA_HF_READER_SUBCARRIER_424_KHZ 1 `define FPGA_HF_READER_SUBCARRIER_212_KHZ 2 +`define FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ 3 `define FPGA_HF_FSK_READER_OUTPUT_1695_KHZ 0 `define FPGA_HF_FSK_READER_OUTPUT_848_KHZ 1 @@ -74,14 +75,13 @@ `define FPGA_HF_ISO18092_FLAG_424K 2 // 0010 should enable 414k mode (untested). No autodetect `define FPGA_HF_ISO18092_FLAG_READER 4 // 0100 enables antenna power, to act as a reader instead of tag -`include "hi_reader.v" +`include "hi_reader_15.v" `include "hi_simulate.v" //`include "hi_iso14443a.v" `include "hi_sniffer.v" `include "util.v" // `include "hi_flite.v" `include "hi_get_trace.v" -`include "hi_read_fsk.v" module fpga_hf_15( input spck, output miso, input mosi, input ncs, @@ -221,15 +221,6 @@ hi_get_trace gt( gt_ssp_frame, gt_ssp_din, gt_ssp_clk ); -// 110 - HF Read FSK -hi_read_fsk hrf( - ck_1356meg, - hrf_pwr_lo, hrf_pwr_hi, hrf_pwr_oe1, hrf_pwr_oe2, hrf_pwr_oe3, hrf_pwr_oe4, - adc_d, hrf_adc_clk, - hrf_ssp_frame, hrf_ssp_din, hrf_ssp_clk, - subcarrier_frequency, minor_mode -); - // Major modes: // 000 -- HF reader; subcarrier frequency and modulation depth selectable // 001 -- HF simulated tag @@ -237,21 +228,20 @@ hi_read_fsk hrf( // 011 -- HF sniff // 100 -- HF ISO18092 FeliCa // 101 -- HF get trace -// 110 -- HF Read FSK +// 110 -- unused // 111 -- FPGA_MAJOR_MODE_OFF // 000 001 010 011 100 101 110 111 - -mux8 mux_ssp_clk (major_mode, ssp_clk, hr_ssp_clk, hs_ssp_clk, 1'b0, he_ssp_clk, hfl_ssp_clk, gt_ssp_clk, hrf_ssp_clk, 1'b0); -mux8 mux_ssp_din (major_mode, ssp_din, hr_ssp_din, hs_ssp_din, 1'b0, he_ssp_din, hfl_ssp_din, gt_ssp_din, hrf_ssp_din, 1'b0); -mux8 mux_ssp_frame (major_mode, ssp_frame, hr_ssp_frame, hs_ssp_frame, 1'b0, he_ssp_frame, hfl_ssp_frame, gt_ssp_frame, hrf_ssp_frame, 1'b0); -mux8 mux_pwr_oe1 (major_mode, pwr_oe1, hr_pwr_oe1, hs_pwr_oe1, 1'b0, he_pwr_oe1, hfl_pwr_oe1, 1'b0, hrf_pwr_oe1, 1'b0); -mux8 mux_pwr_oe2 (major_mode, pwr_oe2, hr_pwr_oe2, hs_pwr_oe2, 1'b0, he_pwr_oe2, hfl_pwr_oe2, 1'b0, hrf_pwr_oe2, 1'b0); -mux8 mux_pwr_oe3 (major_mode, pwr_oe3, hr_pwr_oe3, hs_pwr_oe3, 1'b0, he_pwr_oe3, hfl_pwr_oe3, 1'b0, hrf_pwr_oe3, 1'b0); -mux8 mux_pwr_oe4 (major_mode, pwr_oe4, hr_pwr_oe4, hs_pwr_oe4, 1'b0, he_pwr_oe4, hfl_pwr_oe4, 1'b0, hrf_pwr_oe4, 1'b0); -mux8 mux_pwr_lo (major_mode, pwr_lo, hr_pwr_lo, hs_pwr_lo, 1'b0, he_pwr_lo, hfl_pwr_lo, 1'b0, hrf_pwr_lo, 1'b0); -mux8 mux_pwr_hi (major_mode, pwr_hi, hr_pwr_hi, hs_pwr_hi, 1'b0, he_pwr_hi, hfl_pwr_hi, 1'b0, hrf_pwr_hi, 1'b0); -mux8 mux_adc_clk (major_mode, adc_clk, hr_adc_clk, hs_adc_clk, 1'b0, he_adc_clk, hfl_adc_clk, 1'b0, hrf_adc_clk, 1'b0); +mux8 mux_ssp_clk (major_mode, ssp_clk, hr_ssp_clk, hs_ssp_clk, 1'b0, he_ssp_clk, hfl_ssp_clk, gt_ssp_clk, 1'b0, 1'b0); +mux8 mux_ssp_din (major_mode, ssp_din, hr_ssp_din, hs_ssp_din, 1'b0, he_ssp_din, hfl_ssp_din, gt_ssp_din, 1'b0, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, hr_ssp_frame, hs_ssp_frame, 1'b0, he_ssp_frame, hfl_ssp_frame, gt_ssp_frame, 1'b0, 1'b0); +mux8 mux_pwr_oe1 (major_mode, pwr_oe1, hr_pwr_oe1, hs_pwr_oe1, 1'b0, he_pwr_oe1, hfl_pwr_oe1, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe2 (major_mode, pwr_oe2, hr_pwr_oe2, hs_pwr_oe2, 1'b0, he_pwr_oe2, hfl_pwr_oe2, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe3 (major_mode, pwr_oe3, hr_pwr_oe3, hs_pwr_oe3, 1'b0, he_pwr_oe3, hfl_pwr_oe3, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe4 (major_mode, pwr_oe4, hr_pwr_oe4, hs_pwr_oe4, 1'b0, he_pwr_oe4, hfl_pwr_oe4, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_lo (major_mode, pwr_lo, hr_pwr_lo, hs_pwr_lo, 1'b0, he_pwr_lo, hfl_pwr_lo, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_hi (major_mode, pwr_hi, hr_pwr_hi, hs_pwr_hi, 1'b0, he_pwr_hi, hfl_pwr_hi, 1'b0, 1'b0, 1'b0); +mux8 mux_adc_clk (major_mode, adc_clk, hr_adc_clk, hs_adc_clk, 1'b0, he_adc_clk, hfl_adc_clk, 1'b0, 1'b0, 1'b0); mux8 mux_dbg (major_mode, dbg, hr_dbg, hs_dbg, 1'b0, he_dbg, hfl_dbg, 1'b0, 1'b0, 1'b0); // In all modes, let the ADC's outputs be enabled. diff --git a/fpga-xc2s30/hi_reader_15.v b/fpga-xc2s30/hi_reader_15.v new file mode 100644 index 000000000..28b8a7cae --- /dev/null +++ b/fpga-xc2s30/hi_reader_15.v @@ -0,0 +1,443 @@ +//----------------------------------------------------------------------------- +// +// copied from hi_reader.v by Jonathan Westhues, April 2006 +// modified to add support for iso15 2sc mode by lnv42, Feb 2022 +//----------------------------------------------------------------------------- + +module hi_reader( + ck_1356meg, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + dbg, + subcarrier_frequency, minor_mode +); + input ck_1356meg; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + output dbg; + input [1:0] subcarrier_frequency; + input [3:0] minor_mode; + +assign adc_clk = ck_1356meg; // sample frequency is 13,56 MHz + +// 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, after_hysteresis_prev_prev; +reg [11:0] has_been_low_for; +always @(negedge adc_clk) +begin + if (& adc_d[7:0]) after_hysteresis <= 1'b1; + else if (~(| adc_d[7:0])) after_hysteresis <= 1'b0; + + if (after_hysteresis) + begin + has_been_low_for <= 12'd0; + end + else + begin + if (has_been_low_for == 12'd4095) + begin + has_been_low_for <= 12'd0; + after_hysteresis <= 1'b1; + end + else + has_been_low_for <= has_been_low_for + 1; + end +end + + +// Let us report a correlation every 64 samples. I.e. +// one Q/I pair after 4 subcarrier cycles for the 848kHz subcarrier, +// one Q/I pair after 2 subcarrier cycles for the 424kHz subcarriers, +// one Q/I pair for each subcarrier cyle for the 212kHz subcarrier. +// We need a 6-bit counter for the timing. +reg [5:0] corr_i_cnt; +always @(negedge adc_clk) +begin + corr_i_cnt <= corr_i_cnt + 1; +end + + +reg [1:0] fskout = 2'd0; +reg last0 = 1'b0; + +reg [7:0] avg = 8'd0; +reg [127:0] avg128 = 128'd0; +reg [7:0] diff16 = 8'd0; +reg [7:0] diff28 = 8'd0; +reg [7:0] diff32 = 8'd0; + +reg [11:0] match16 = 12'd0; +reg [11:0] match32 = 12'd0; +reg [11:0] match28 = 12'd0; + +always @(negedge adc_clk) +begin + if (corr_i_cnt[0] == 1'b0) // every 2 clock + begin + avg = adc_d[7:1]; + end + else + begin + avg = avg + adc_d[7:1]; + if (corr_i_cnt[0] == 1'b1) // every 2 clock + begin + if (avg > avg128[63:56]) + diff16 = avg - avg128[63:56]; + else + diff16 = avg128[63:56] - avg; + + if (avg > avg128[111:104]) + diff28 = avg - avg128[111:104]; + else + diff28 = avg128[111:104] - avg; + + if (avg > avg128[127:120]) + diff32 = avg - avg128[127:120]; + else + diff32 = avg128[127:120] - avg; + + avg128[127:8] = avg128[119:0]; + avg128[7:0] = avg; + + + if (corr_i_cnt[4:1] == 4'b0000) // every 32 clock (8*4) + begin + match16 = diff16; + match28 = diff28; + match32 = diff32; + end + else + begin + match16 = match16 + diff16; + match28 = match28 + diff28; + match32 = match32 + diff32; + + if (corr_i_cnt[4:1] == 4'b1111) // every 32 clock (8*4) + begin + last0 = (fskout == 2'b0); + if (match16 < 12'd64 && last0) + fskout = 2'b00; // not yet started + else if ((match16 | match28 | match32) == 12'b0) + fskout = 2'b00; // signal likely ended + else if (((match16 <= match28 + 12'd16) && (match16 <= match32+ 12'd16)) || + (match28 <= 12'd16 && match32 <= 12'd16)) + begin + if (!last0) + fskout = 2'b11; // 16 match better than 28 or 32 but already started + end + else + begin + if (match28 < match32) + begin + diff28 = match32 - match28; + diff16 = match16 - match28; + if (diff28*2 > diff16) + fskout = 2'b01; + else if (!last0) + begin + fskout = 2'b01; + end + end + else //if (match32 <= match28) + begin + diff32 = match28 - match32; + diff16 = match16 - match32; + if (diff32*2 > diff16) + fskout = 2'b10; + else if (!last0) + begin + fskout = 2'b10; + end + end + end + end + end + end + end +end + + +// A couple of registers in which to accumulate the correlations. From the 64 samples +// we would add at most 32 times the difference between unmodulated and modulated signal. It should +// be safe to assume that a tag will not be able to modulate the carrier signal by more than 25%. +// 32 * 255 * 0,25 = 2040, which can be held in 11 bits. Add 1 bit for sign. +// Temporary we might need more bits. For the 212kHz subcarrier we could possible add 32 times the +// maximum signal value before a first subtraction would occur. 32 * 255 = 8160 can be held in 13 bits. +// Add one bit for sign -> need 14 bit registers but final result will fit into 12 bits. +reg signed [13:0] corr_i_accum; +reg signed [13:0] corr_q_accum; +// we will report maximum 8 significant bits +reg signed [7:0] corr_i_out; +reg signed [7:0] corr_q_out; + + +// the amplitude of the subcarrier is sqrt(ci^2 + cq^2). +// approximate by amplitude = max(|ci|,|cq|) + 1/2*min(|ci|,|cq|) +reg [13:0] corr_amplitude, abs_ci, abs_cq, max_ci_cq; +reg [12:0] min_ci_cq_2; // min_ci_cq / 2 + +always @(*) +begin + if (corr_i_accum[13] == 1'b0) + abs_ci <= corr_i_accum; + else + abs_ci <= -corr_i_accum; + + if (corr_q_accum[13] == 1'b0) + abs_cq <= corr_q_accum; + else + abs_cq <= -corr_q_accum; + + if (abs_ci > abs_cq) + begin + max_ci_cq <= abs_ci; + min_ci_cq_2 <= abs_cq / 2; + end + else + begin + max_ci_cq <= abs_cq; + min_ci_cq_2 <= abs_ci / 2; + end + + corr_amplitude <= max_ci_cq + min_ci_cq_2; + +end + + +// The subcarrier reference signals +reg subcarrier_I; +reg subcarrier_Q; + +always @(*) +begin + if (subcarrier_frequency == `FPGA_HF_READER_SUBCARRIER_848_KHZ) + begin + subcarrier_I = ~corr_i_cnt[3]; + subcarrier_Q = ~(corr_i_cnt[3] ^ corr_i_cnt[2]); + end + else if (subcarrier_frequency == `FPGA_HF_READER_SUBCARRIER_212_KHZ) + begin + subcarrier_I = ~corr_i_cnt[5]; + subcarrier_Q = ~(corr_i_cnt[5] ^ corr_i_cnt[4]); + end + else + begin // 424 kHz + subcarrier_I = ~corr_i_cnt[4]; + subcarrier_Q = ~(corr_i_cnt[4] ^ corr_i_cnt[3]); + end +end + + +// ADC data appears on the rising edge, so sample it on the falling edge +always @(negedge adc_clk) +begin + // These are the correlators: we correlate against in-phase and quadrature + // versions of our reference signal, and keep the (signed) results or the + // resulting amplitude to send out later over the SSP. + if (corr_i_cnt == 6'd0) + begin + if (minor_mode == `FPGA_HF_READER_MODE_SNIFF_AMPLITUDE) + begin + if (subcarrier_frequency == `FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ) + begin + // send amplitude + 2 bits fsk (2sc) signal + 2 bits reader signal + corr_i_out <= corr_amplitude[13:6]; + corr_q_out <= {corr_amplitude[5:2], fskout, after_hysteresis_prev_prev, after_hysteresis_prev}; + end + else + begin + // send amplitude plus 2 bits reader signal + corr_i_out <= corr_amplitude[13:6]; + corr_q_out <= {corr_amplitude[5:0], after_hysteresis_prev_prev, after_hysteresis_prev}; + end + end + else if (minor_mode == `FPGA_HF_READER_MODE_SNIFF_IQ) + begin + // Send 7 most significant bits of in phase tag signal (signed), plus 1 bit reader signal + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= {corr_i_accum[11:5], after_hysteresis_prev_prev}; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= {7'b0111111, after_hysteresis_prev_prev}; + else + corr_i_out <= {7'b1000000, after_hysteresis_prev_prev}; + + // Send 7 most significant bits of quadrature phase tag signal (signed), plus 1 bit reader signal + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= {corr_q_accum[11:5], after_hysteresis_prev}; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= {7'b0111111, after_hysteresis_prev}; + else + corr_q_out <= {7'b1000000, after_hysteresis_prev}; + end + else if (minor_mode == `FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE) + begin + // send amplitude + corr_i_out <= {2'b00, corr_amplitude[13:8]}; + corr_q_out <= corr_amplitude[7:0]; + end + else if (minor_mode == `FPGA_HF_READER_MODE_RECEIVE_IQ) + begin + // Send 8 bits of in phase tag signal + if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + corr_i_out <= corr_i_accum[11:4]; + else // truncate to maximum value + if (corr_i_accum[13] == 1'b0) + corr_i_out <= 8'b01111111; + else + corr_i_out <= 8'b10000000; + + // Send 8 bits of quadrature phase tag signal + if (corr_q_accum[13:11] == 3'b000 || corr_q_accum[13:11] == 3'b111) + corr_q_out <= corr_q_accum[11:4]; + else // truncate to maximum value + if (corr_q_accum[13] == 1'b0) + corr_q_out <= 8'b01111111; + else + corr_q_out <= 8'b10000000; + end + + // for each Q/I pair report two reader signal samples when sniffing. Store the 1st. + after_hysteresis_prev_prev <= after_hysteresis; + + // Initialize next correlation. + // Both I and Q reference signals are high when corr_i_nct == 0. Therefore need to accumulate. + corr_i_accum <= $signed({1'b0, adc_d}); + corr_q_accum <= $signed({1'b0, adc_d}); + end + else + begin + if (subcarrier_I) + corr_i_accum <= corr_i_accum + $signed({1'b0, adc_d}); + else + corr_i_accum <= corr_i_accum - $signed({1'b0, adc_d}); + + if (subcarrier_Q) + corr_q_accum <= corr_q_accum + $signed({1'b0, adc_d}); + else + corr_q_accum <= corr_q_accum - $signed({1'b0, adc_d}); + end + + // for each Q/I pair report two reader signal samples when sniffing. Store the 2nd. + if (corr_i_cnt == 6'd32) + after_hysteresis_prev <= after_hysteresis; + + // Then the result from last time is serialized and send out to the ARM. + // We get one report each cycle, and each report is 16 bits, so the + // ssp_clk should be the adc_clk divided by 64/16 = 4. + // ssp_clk frequency = 13,56MHz / 4 = 3.39MHz + + if (corr_i_cnt[1:0] == 2'b00) + begin + // Don't shift if we just loaded new data, obviously. + if (corr_i_cnt != 6'd0) + begin + corr_i_out[7:0] <= {corr_i_out[6:0], corr_q_out[7]}; + corr_q_out[7:1] <= corr_q_out[6:0]; + end + end + +end + + +// ssp clock and frame signal for communication to and from ARM +// _____ _____ _____ _ +// ssp_clk | |_____| |_____| |_____| +// _____ +// ssp_frame ___| |____________________________ +// ___________ ___________ ___________ _ +// ssp_d_in X___________X___________X___________X_ +// +// corr_i_cnt 0 1 2 3 4 5 6 7 8 9 10 11 12 ... +// + +reg ssp_clk; +reg ssp_frame; + +always @(negedge adc_clk) +begin + if (corr_i_cnt[1:0] == 2'b00) + ssp_clk <= 1'b1; + + if (corr_i_cnt[1:0] == 2'b10) + ssp_clk <= 1'b0; + + // set ssp_frame signal for corr_i_cnt = 1..3 + // (send one frame with 16 Bits) + if (corr_i_cnt == 6'd1) + ssp_frame <= 1'b1; + + if (corr_i_cnt == 6'd3) + ssp_frame <= 1'b0; +end + + +assign ssp_din = corr_i_out[7]; + + +// a jamming signal +reg jam_signal; +reg [3:0] jam_counter; + +always @(negedge adc_clk) +begin + if (corr_i_cnt == 6'd0) + begin + jam_counter <= jam_counter + 1; + jam_signal <= jam_counter[1] ^ jam_counter[3]; + end +end + +// Antenna drivers +reg pwr_hi, pwr_oe4; + +always @(*) +begin + if (minor_mode == `FPGA_HF_READER_MODE_SEND_SHALLOW_MOD) + begin + pwr_hi = ck_1356meg; + pwr_oe4 = ssp_dout; + end + else if (minor_mode == `FPGA_HF_READER_MODE_SEND_FULL_MOD) + begin + pwr_hi = ck_1356meg & ~ssp_dout; + pwr_oe4 = 1'b0; + end + else if (minor_mode == `FPGA_HF_READER_MODE_SEND_JAM) + begin + pwr_hi = ck_1356meg & jam_signal; + pwr_oe4 = 1'b0; + end + else if (minor_mode == `FPGA_HF_READER_MODE_SNIFF_IQ + || minor_mode == `FPGA_HF_READER_MODE_SNIFF_AMPLITUDE + || minor_mode == `FPGA_HF_READER_MODE_SNIFF_PHASE) + begin // all off + pwr_hi = 1'b0; + pwr_oe4 = 1'b0; + end + else // receiving from tag + begin + pwr_hi = ck_1356meg; + pwr_oe4 = 1'b0; + end +end + +// always on +assign pwr_oe1 = 1'b0; +assign pwr_oe3 = 1'b0; + +// Unused. +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; + +// Debug Output +assign dbg = corr_i_cnt[3]; + +endmodule From 07013a5aafebc778ba2ce6fd2e6398e94ea1b8c4 Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Fri, 4 Mar 2022 12:06:41 +0100 Subject: [PATCH 3/8] iso15 sniff: commit some unused FPGA FSK code for history --- fpga-xc2s30/hi_read_fsk.v | 246 +++++++++++++++++++++++++++++++++++++- 1 file changed, 240 insertions(+), 6 deletions(-) diff --git a/fpga-xc2s30/hi_read_fsk.v b/fpga-xc2s30/hi_read_fsk.v index e45162111..a8c2003ff 100644 --- a/fpga-xc2s30/hi_read_fsk.v +++ b/fpga-xc2s30/hi_read_fsk.v @@ -37,6 +37,130 @@ begin pwr_hi <= 'b0; end +reg [4:0] adc_cnt = 5'd0; +always @(negedge adc_clk) +begin + adc_cnt <= adc_cnt + 1'd1; +end + +reg [7:0] out1 = 8'd0; +reg [7:0] out2 = 8'd0; + +reg [7:0] avg = 8'd0; +reg [7:0] avg1 = 8'd0; +reg [7:0] avg2 = 8'd0; +reg [7:0] avg3 = 8'd0; +reg [7:0] avg4 = 8'd0; +reg [7:0] avg5 = 8'd0; +reg [7:0] avg6 = 8'd0; +reg [7:0] avg7 = 8'd0; +reg [7:0] avg8 = 8'd0; +reg [7:0] avg9 = 8'd0; +reg [7:0] avg10 = 8'd0; +reg [7:0] avg11 = 8'd0; +reg [7:0] avg12 = 8'd0; +reg [7:0] avg13 = 8'd0; +reg [7:0] avg14 = 8'd0; +reg [7:0] avg15 = 8'd0; +reg [7:0] avg16 = 8'd0; + +reg [7:0] diff28 = 8'd0; +reg [7:0] diff32 = 8'd0; + +reg [11:0] match32 = 12'd0; +reg [11:0] match28 = 12'd0; + +always @(negedge adc_clk) +begin + if (adc_cnt[0] == 1'b0) // every 4 clock + begin + avg = adc_d[7:1]; + end + else + begin + avg = avg + adc_d[7:1]; + if (adc_cnt[0] == 1'b1) // every 4 clock + begin + if (avg > avg14) + diff28 = avg - avg14; + else + diff28 = avg14 - avg; + + if (avg > avg16) + diff32 = avg - avg16; + else + diff32 = avg16 - avg; + + avg16 = avg15; + avg15 = avg14; + avg14 = avg13; + avg13 = avg12; + avg12 = avg11; + avg11 = avg10; + avg10 = avg9; + avg9 = avg8; + avg8 = avg7; + avg7 = avg6; + avg6 = avg5; + avg5 = avg4; + avg4 = avg3; + avg3 = avg2; + avg2 = avg1; + avg1 = avg; + + if (adc_cnt[4:1] == 4'b0000) // every 32 clock (8*4) + begin + match28 = diff28; + match32 = diff32; + end + else + begin + match28 = match28 + diff28; + match32 = match32 + diff32; + + if (adc_cnt[4:1] == 4'b1111) // every 32 clock (8*4) + begin + if (match28[11:3] > 10'b0 || match32[11:3] > 10'b0) // if not only noise + begin + if (match28 < match32) + begin + //if (match32 - match28 < 12'd24) + //out1 = out1; // out1 stay at is old value + //else + if (match32 - match28 > 12'd32) + out1 = 8'd28; + else if (match32 - match28 < 12'd16) + out1 = 8'd0; + end + else //if (match32 <= match28) + begin + //if (match28 - match32 < 12'd24) + //out1 = 8'd30; // out1 stay at is old value + //else + if (match28 - match32 > 12'd32) + out1 = 8'd32; + else if (match28 - match32 < 12'd16) + out1 = 8'd0; + end + end + else + begin + out1 = 8'd0; + //out2 = match32[8:1]; + end + //out1 = match28[7:0]; + //out2 = match32[7:0]; + //out2 = 8'hFF; + + //out2 = out1; + + end + end + end + end +end + +/* reg [7:0] adc_cnt = 8'd0; reg [7:0] out1 = 8'd0; reg [7:0] old = 8'd0; @@ -80,9 +204,107 @@ begin old <= adc_d; end +*/ +// Other version of FSK reader, probably better but not working yet... +/*reg [7:0] out1 = 8'd0; +//reg [7:0] old = 8'd0; +reg [5:0] old1 = 4'd0; +reg [5:0] old2 = 4'd0; +//reg [7:0] edge_id = 8'd0; +//reg edge_started = 1'd0; +reg [5:0] edge_cnt = 6'd0; +reg [3:0] last_values = 4'd0; + +// Count clock edge between two signal edges +always @(negedge adc_clk) +begin + edge_cnt <= edge_cnt + 1'd1; + + last_values[3:1] <= last_values[2:0]; +// last_values[0] <= (& adc_d[7:5]); // adc_d >= 192 + + last_values[0] <= (adc_d[7:2] > old1 && old1 > old2); + + + //out1[7:4] <= out1[3:0]; + //out1[3:0] <= last_values; + //out1 <= 8'd28; + //out1 <= out1+1; + + if (edge_cnt > 6'd22 || out1 == 8'd0) + begin + if ((last_values[3:2] == 2'b0) && (last_values[1:0] == 2'b11)) // edge start detected + begin // 2 not high (low or mid) values followed by 2 high values + out1 <= edge_cnt; + edge_cnt <= 6'd0; + end + else if (edge_cnt > 6'd44) // average(32, 2*28) == 44 : ideal value for iso15 FSK + begin // /!\ MIN FREQ SUPPORTED = 13MHz/44 ~= 308kHz /!\ + out1 <= 8'd0; + edge_cnt <= 6'd0; + end + end + + old2 <= old1; + old1 <= adc_d[7:2]; + + /*if (last_values[0]) + out1 <= 8'h7F; + else if (last_values[1]) + out1 <= 8'hFF; + else + out1 <= 8'h0; + + /* + + if (& adc_d[7:5] && !(& old[7:5])) // up + begin + if (edge_started == 1'd0) // new edge starting + begin + //if (edge_id <= edge_cnt) + // out1 <= edge_cnt - edge_id; + //else + // out1 <= edge_cnt + (9'h100 - edge_id); + out1 <= edge_cnt; + //edge_id <= edge_cnt; + edge_started = 1'd1; + edge_cnt <= 8'd0; + end + end + else + begin + edge_started = 1'd0; + if (edge_cnt > 8'd80) + begin + out1 <= 8'd0; + //edge_id <= 8'd0; + edge_cnt <= 8'd0; + end +*//* if (edge_id <= edge_cnt) // NO EDGE + begin + if (edge_cnt - edge_id > 8'd40) + begin + out1 <= 8'd0; + edge_id <= 8'd0; + edge_cnt <= 8'd0; + end + end + else + begin + if (edge_cnt + (9'h100 - edge_id) > 8'd40) // NO EDGE + begin + out1 <= 8'd0; + edge_id <= 8'd0; + edge_cnt <= 8'd0; + end + end*//* + //end + + //old <= adc_d; +end*/ // agregate out values (depending on selected output frequency) -reg [10:0] out_tmp = 11'd0; +/*reg [10:0] out_tmp = 11'd0; reg [7:0] out = 8'd0; always @(negedge adc_clk) begin @@ -104,8 +326,14 @@ begin end else // 1695_KHZ out <= out1; -end +// if (adc_cnt > 8'd192 && edge_id < 8'd64) // WUT ? +// begin +// out <= 8'd0; +// out_tmp <= 11'd0; +// end +end +*/ // Set output (ssp) clock (* clock_signal = "yes" *) reg ssp_clk; always @(ck_1356meg) @@ -121,20 +349,26 @@ begin end // Transmit output +reg [379:0] megatmpout = 380'd0; + reg ssp_frame; reg [7:0] ssp_out = 8'd0; -reg [2:0] ssp_cnt = 4'd0; +reg [3:0] ssp_cnt = 4'd0; always @(posedge ssp_clk) begin ssp_cnt <= ssp_cnt + 1'd1; - if(ssp_cnt == 3'd15) + if (ssp_cnt[2:0] == 3'd7) begin - ssp_out <= out; + ssp_out = {2'b0, megatmpout[379:378], megatmpout[378], megatmpout[378], 2'b0}; + megatmpout[379:2] = megatmpout[377:0]; + megatmpout[1:0] = out2[5:4]; + out2 = out1; + ssp_frame <= 1'b1; end else begin - ssp_out <= {ssp_out[6:0], 1'b0}; + ssp_out = {ssp_out[6:0], 1'b0}; ssp_frame <= 1'b0; end end From b2c5a52fd799b03e320c22be5980eefdd7d9ac6e Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Fri, 4 Mar 2022 12:09:01 +0100 Subject: [PATCH 4/8] iso15 sniff: remove unused FPGA hi_read_fsk.v --- fpga-xc2s30/hi_read_fsk.v | 386 -------------------------------------- 1 file changed, 386 deletions(-) delete mode 100644 fpga-xc2s30/hi_read_fsk.v diff --git a/fpga-xc2s30/hi_read_fsk.v b/fpga-xc2s30/hi_read_fsk.v deleted file mode 100644 index a8c2003ff..000000000 --- a/fpga-xc2s30/hi_read_fsk.v +++ /dev/null @@ -1,386 +0,0 @@ -// lnv42, Jan 2020 -// reworked && integrated to RRG in Fev 2022 -// HF FSK reader (used for iso15 sniffing/reading) - -// output is the frequence divider from 13,56 MHz - -// (eg. for iso 15 two subcarriers mode (423,75 khz && 484,28 khz): it return 32 or 28) -// (423,75k = 13.56M / 32 and 484.28k = 13,56M / 28) - -module hi_read_fsk( - ck_1356meg, - pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, - adc_d, adc_clk, - ssp_frame, ssp_din, ssp_clk, - subcarrier_frequency, minor_mode -); - - input ck_1356meg; - output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; - input [7:0] adc_d; - output adc_clk; - output ssp_frame, ssp_din, ssp_clk; - input [1:0]subcarrier_frequency; - input [3:0] minor_mode; - -assign adc_clk = ck_1356meg; // input sample frequency is 13,56 MHz - -assign power = subcarrier_frequency[0]; - -// Carrier is on if power is on, else is 0 -reg pwr_hi; -always @(ck_1356meg) -begin - if (power == `FPGA_HF_FSK_READER_WITHPOWER) - pwr_hi <= ck_1356meg; - else - pwr_hi <= 'b0; -end - -reg [4:0] adc_cnt = 5'd0; -always @(negedge adc_clk) -begin - adc_cnt <= adc_cnt + 1'd1; -end - -reg [7:0] out1 = 8'd0; -reg [7:0] out2 = 8'd0; - -reg [7:0] avg = 8'd0; -reg [7:0] avg1 = 8'd0; -reg [7:0] avg2 = 8'd0; -reg [7:0] avg3 = 8'd0; -reg [7:0] avg4 = 8'd0; -reg [7:0] avg5 = 8'd0; -reg [7:0] avg6 = 8'd0; -reg [7:0] avg7 = 8'd0; -reg [7:0] avg8 = 8'd0; -reg [7:0] avg9 = 8'd0; -reg [7:0] avg10 = 8'd0; -reg [7:0] avg11 = 8'd0; -reg [7:0] avg12 = 8'd0; -reg [7:0] avg13 = 8'd0; -reg [7:0] avg14 = 8'd0; -reg [7:0] avg15 = 8'd0; -reg [7:0] avg16 = 8'd0; - -reg [7:0] diff28 = 8'd0; -reg [7:0] diff32 = 8'd0; - -reg [11:0] match32 = 12'd0; -reg [11:0] match28 = 12'd0; - -always @(negedge adc_clk) -begin - if (adc_cnt[0] == 1'b0) // every 4 clock - begin - avg = adc_d[7:1]; - end - else - begin - avg = avg + adc_d[7:1]; - if (adc_cnt[0] == 1'b1) // every 4 clock - begin - if (avg > avg14) - diff28 = avg - avg14; - else - diff28 = avg14 - avg; - - if (avg > avg16) - diff32 = avg - avg16; - else - diff32 = avg16 - avg; - - avg16 = avg15; - avg15 = avg14; - avg14 = avg13; - avg13 = avg12; - avg12 = avg11; - avg11 = avg10; - avg10 = avg9; - avg9 = avg8; - avg8 = avg7; - avg7 = avg6; - avg6 = avg5; - avg5 = avg4; - avg4 = avg3; - avg3 = avg2; - avg2 = avg1; - avg1 = avg; - - if (adc_cnt[4:1] == 4'b0000) // every 32 clock (8*4) - begin - match28 = diff28; - match32 = diff32; - end - else - begin - match28 = match28 + diff28; - match32 = match32 + diff32; - - if (adc_cnt[4:1] == 4'b1111) // every 32 clock (8*4) - begin - if (match28[11:3] > 10'b0 || match32[11:3] > 10'b0) // if not only noise - begin - if (match28 < match32) - begin - //if (match32 - match28 < 12'd24) - //out1 = out1; // out1 stay at is old value - //else - if (match32 - match28 > 12'd32) - out1 = 8'd28; - else if (match32 - match28 < 12'd16) - out1 = 8'd0; - end - else //if (match32 <= match28) - begin - //if (match28 - match32 < 12'd24) - //out1 = 8'd30; // out1 stay at is old value - //else - if (match28 - match32 > 12'd32) - out1 = 8'd32; - else if (match28 - match32 < 12'd16) - out1 = 8'd0; - end - end - else - begin - out1 = 8'd0; - //out2 = match32[8:1]; - end - //out1 = match28[7:0]; - //out2 = match32[7:0]; - //out2 = 8'hFF; - - //out2 = out1; - - end - end - end - end -end - -/* -reg [7:0] adc_cnt = 8'd0; -reg [7:0] out1 = 8'd0; -reg [7:0] old = 8'd0; -reg [7:0] edge_id = 8'd0; -reg edge_started = 1'd0; -// Count clock edge between two signal edges -always @(negedge adc_clk) -begin - adc_cnt <= adc_cnt + 1'd1; - - if (& adc_d[7:5] && !(& old[7:5])) // up - begin - if (edge_started == 1'd0) // new edge starting - begin - if (edge_id <= adc_cnt) - out1 <= adc_cnt - edge_id; - else - out1 <= adc_cnt + 9'h100 - edge_id; - edge_id <= adc_cnt; - edge_started = 1'd1; - end - end - else - begin - edge_started = 1'd0; - if (edge_id <= adc_cnt) - begin - if (adc_cnt - edge_id > 8'd40) - begin - out1 <= 8'd0; - end - end - else - begin - if (adc_cnt + 9'h100 - edge_id > 8'd40) - begin - out1 <= 8'd0; - end - end - end - - old <= adc_d; -end -*/ -// Other version of FSK reader, probably better but not working yet... -/*reg [7:0] out1 = 8'd0; -//reg [7:0] old = 8'd0; -reg [5:0] old1 = 4'd0; -reg [5:0] old2 = 4'd0; -//reg [7:0] edge_id = 8'd0; -//reg edge_started = 1'd0; -reg [5:0] edge_cnt = 6'd0; -reg [3:0] last_values = 4'd0; - -// Count clock edge between two signal edges -always @(negedge adc_clk) -begin - edge_cnt <= edge_cnt + 1'd1; - - last_values[3:1] <= last_values[2:0]; -// last_values[0] <= (& adc_d[7:5]); // adc_d >= 192 - - last_values[0] <= (adc_d[7:2] > old1 && old1 > old2); - - - //out1[7:4] <= out1[3:0]; - //out1[3:0] <= last_values; - //out1 <= 8'd28; - //out1 <= out1+1; - - if (edge_cnt > 6'd22 || out1 == 8'd0) - begin - if ((last_values[3:2] == 2'b0) && (last_values[1:0] == 2'b11)) // edge start detected - begin // 2 not high (low or mid) values followed by 2 high values - out1 <= edge_cnt; - edge_cnt <= 6'd0; - end - else if (edge_cnt > 6'd44) // average(32, 2*28) == 44 : ideal value for iso15 FSK - begin // /!\ MIN FREQ SUPPORTED = 13MHz/44 ~= 308kHz /!\ - out1 <= 8'd0; - edge_cnt <= 6'd0; - end - end - - old2 <= old1; - old1 <= adc_d[7:2]; - - /*if (last_values[0]) - out1 <= 8'h7F; - else if (last_values[1]) - out1 <= 8'hFF; - else - out1 <= 8'h0; - - /* - - if (& adc_d[7:5] && !(& old[7:5])) // up - begin - if (edge_started == 1'd0) // new edge starting - begin - //if (edge_id <= edge_cnt) - // out1 <= edge_cnt - edge_id; - //else - // out1 <= edge_cnt + (9'h100 - edge_id); - out1 <= edge_cnt; - //edge_id <= edge_cnt; - edge_started = 1'd1; - edge_cnt <= 8'd0; - end - end - else - begin - edge_started = 1'd0; - if (edge_cnt > 8'd80) - begin - out1 <= 8'd0; - //edge_id <= 8'd0; - edge_cnt <= 8'd0; - end -*//* if (edge_id <= edge_cnt) // NO EDGE - begin - if (edge_cnt - edge_id > 8'd40) - begin - out1 <= 8'd0; - edge_id <= 8'd0; - edge_cnt <= 8'd0; - end - end - else - begin - if (edge_cnt + (9'h100 - edge_id) > 8'd40) // NO EDGE - begin - out1 <= 8'd0; - edge_id <= 8'd0; - edge_cnt <= 8'd0; - end - end*//* - //end - - //old <= adc_d; -end*/ - -// agregate out values (depending on selected output frequency) -/*reg [10:0] out_tmp = 11'd0; -reg [7:0] out = 8'd0; -always @(negedge adc_clk) -begin - out_tmp <= out_tmp + out1; - if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_848_KHZ && adc_cnt[0] == 1'd0) - begin // average on 2 values - out <= out_tmp[8:1]; - out_tmp <= 12'd0; - end - else if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_424_KHZ && adc_cnt[1:0] == 2'd0) - begin // average on 4 values - out <= out_tmp[9:2]; - out_tmp <= 12'd0; - end - else if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_212_KHZ && adc_cnt[2:0] == 3'd0) - begin // average on 8 values - out <= out_tmp[10:3]; - out_tmp <= 12'd0; - end - else // 1695_KHZ - out <= out1; - -// if (adc_cnt > 8'd192 && edge_id < 8'd64) // WUT ? -// begin -// out <= 8'd0; -// out_tmp <= 11'd0; -// end -end -*/ -// Set output (ssp) clock -(* clock_signal = "yes" *) reg ssp_clk; -always @(ck_1356meg) -begin - if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_1695_KHZ) - ssp_clk <= ~ck_1356meg; - else if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_848_KHZ) - ssp_clk <= ~adc_cnt[0]; - else if (minor_mode == `FPGA_HF_FSK_READER_OUTPUT_424_KHZ) - ssp_clk <= ~adc_cnt[1]; - else // 212 KHz - ssp_clk <= ~adc_cnt[2]; -end - -// Transmit output -reg [379:0] megatmpout = 380'd0; - -reg ssp_frame; -reg [7:0] ssp_out = 8'd0; -reg [3:0] ssp_cnt = 4'd0; -always @(posedge ssp_clk) -begin - ssp_cnt <= ssp_cnt + 1'd1; - if (ssp_cnt[2:0] == 3'd7) - begin - ssp_out = {2'b0, megatmpout[379:378], megatmpout[378], megatmpout[378], 2'b0}; - megatmpout[379:2] = megatmpout[377:0]; - megatmpout[1:0] = out2[5:4]; - out2 = out1; - - ssp_frame <= 1'b1; - end - else - begin - ssp_out = {ssp_out[6:0], 1'b0}; - ssp_frame <= 1'b0; - end -end - -assign ssp_din = ssp_out[7]; - -// Unused. -assign pwr_oe4 = 1'b0; -assign pwr_oe1 = 1'b0; -assign pwr_oe3 = 1'b0; -assign pwr_lo = 1'b0; -assign pwr_oe2 = 1'b0; - -endmodule - From 3e784557fc4457ee6b47d3ba264f9f67787d802d Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Fri, 4 Mar 2022 12:32:08 +0100 Subject: [PATCH 5/8] iso15 sniff: improve iso15 FSK decoding (ARM part) --- armsrc/iso15693.c | 331 ++++++++++++++++++++++++++-------------------- 1 file changed, 187 insertions(+), 144 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 919040d0e..eb0df9ba0 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1280,21 +1280,22 @@ void AcquireRawAdcSamplesIso15693(void) { typedef struct DecodeTagFSK { enum { + STATE_FSK_ERROR, STATE_FSK_BEFORE_SOF, STATE_FSK_SOF_484, STATE_FSK_SOF_424, - STATE_FSK_SOF_END, + STATE_FSK_SOF_END_484, + STATE_FSK_SOF_END_424, STATE_FSK_RECEIVING_DATA_484, STATE_FSK_RECEIVING_DATA_424, - STATE_FSK_EOF, - STATE_FSK_ERROR + STATE_FSK_EOF } state; enum { LOGIC0_PART1, LOGIC1_PART1, LOGIC0_PART2, LOGIC1_PART2, - SOF + SOF } lastBit; uint8_t count; uint8_t bitCount; @@ -1309,7 +1310,6 @@ static void DecodeTagFSKReset(DecodeTagFSK_t *DecodeTag) { DecodeTag->bitCount = 0; DecodeTag->len = 0; DecodeTag->shiftReg = 0; - DbpString("FSK tag reset"); } static void DecodeTagFSKInit(DecodeTagFSK_t *DecodeTag, uint8_t *data, uint16_t max_len) { @@ -1320,7 +1320,7 @@ static void DecodeTagFSKInit(DecodeTagFSK_t *DecodeTag, uint8_t *data, uint16_t // Performances of this function are crutial for stability // as it is called in real time for every samples -static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *DecodeTag, bool recv_speed, int samples) +static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *DecodeTag, bool recv_speed) { switch(DecodeTag->state) { case STATE_FSK_BEFORE_SOF: @@ -1334,16 +1334,15 @@ static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *De case STATE_FSK_SOF_484: //DbpString("STATE_FSK_SOF_484"); - - if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 484 - { - DecodeTag->count++; - } - else if (FREQ_IS_424(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) + if (FREQ_IS_424(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) { // SOF part1 continue at 424 DecodeTag->state = STATE_FSK_SOF_424; DecodeTag->count = 1; } + else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 484 + { + DecodeTag->count++; + } else // SOF failed, roll back { DecodeTag->state = STATE_FSK_BEFORE_SOF; @@ -1352,103 +1351,136 @@ static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *De case STATE_FSK_SOF_424: //DbpString("STATE_FSK_SOF_424"); - - if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 424 - DecodeTag->count++; - else if (FREQ_IS_484(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) + if (FREQ_IS_484(freq) && SEOF_COUNT(DecodeTag->count, recv_speed)) { // SOF part 1 finished - DecodeTag->state = STATE_FSK_SOF_END; + DecodeTag->state = STATE_FSK_SOF_END_484; DecodeTag->count = 1; } + else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF at 424 + DecodeTag->count++; else // SOF failed, roll back { +#ifdef DEBUG if (DEBUG) Dbprintf("SOF_424 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif DecodeTag->state = STATE_FSK_BEFORE_SOF; } break; - case STATE_FSK_SOF_END: - if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END (484) - DecodeTag->count++; - else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) - { // SOF END finished or SOF END 1st part finished - DecodeTag->count = 0; - if (DecodeTag->lastBit == SOF) - { // SOF finished at 424 - if (DEBUG) - DbpString("Receiving data !"); - DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; - LED_C_ON(); - } - DecodeTag->lastBit = SOF; - } - else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END (424) - DecodeTag->count++; - else if (DecodeTag->lastBit == SOF && FREQ_IS_484(freq) && - LOGIC_COUNT(DecodeTag->count, recv_speed)) - { // SOF finished at 484 - DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; + case STATE_FSK_SOF_END_484: + if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) + { + DecodeTag->state = STATE_FSK_SOF_END_424; DecodeTag->count = 1; - LED_C_ON(); } - else // SOF failed, roll back + else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END_484 + DecodeTag->count++; + else // SOF failed, roll back { +#ifdef DEBUG if (DEBUG) - Dbprintf("SOF_END failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + Dbprintf("SOF_END_484 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif DecodeTag->state = STATE_FSK_BEFORE_SOF; } break; + case STATE_FSK_SOF_END_424: + if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) + { // SOF finished at 484 + DecodeTag->count = 1; + DecodeTag->lastBit = SOF; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; + LED_C_ON(); + } + else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count-2, recv_speed)) + { // SOF finished at 424 (wait count+2 to be sure that next freq is 424) + DecodeTag->count = 2; + DecodeTag->lastBit = SOF; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; + LED_C_ON(); + } + else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still in SOF_END_424 + DecodeTag->count++; + else // SOF failed, roll back + { +#ifdef DEBUG + if (DEBUG) + Dbprintf("SOF_END_424 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif + DecodeTag->state = STATE_FSK_BEFORE_SOF; + } + break; case STATE_FSK_RECEIVING_DATA_424: - if (DecodeTag->lastBit == LOGIC1_PART1 && - LOGIC_COUNT(DecodeTag->count, recv_speed)) - { // logic 1 finished - DecodeTag->lastBit = LOGIC1_PART2; - DecodeTag->count = 0; + if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) + { + if (DecodeTag->lastBit == LOGIC1_PART1) + { // logic 1 finished, goto 484 + DecodeTag->lastBit = LOGIC1_PART2; - DecodeTag->shiftReg >>= 1; - DecodeTag->shiftReg |= 0x80; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; - if (DecodeTag->len > DecodeTag->max_len) { - // buffer overflow, give up - LED_C_OFF(); - return true; - } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; - } - } - else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 424 + DecodeTag->shiftReg >>= 1; + DecodeTag->shiftReg |= 0x80; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + else + { // end of LOGIC0_PART1 + DecodeTag->lastBit = LOGIC0_PART1; + } + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; + } + else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count-2, recv_speed) && + DecodeTag->lastBit == LOGIC1_PART1) + { // logic 1 finished, stay in 484 + DecodeTag->lastBit = LOGIC1_PART2; + + DecodeTag->shiftReg >>= 1; + DecodeTag->shiftReg |= 0x80; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + DecodeTag->count = 2; + } + else if (FREQ_IS_424(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 424 DecodeTag->count++; - else if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed) && - DecodeTag->lastBit >= LOGIC0_PART2) - { // end of LOGIC0_PART1 - DecodeTag->count = 1; - DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; - DecodeTag->lastBit = LOGIC0_PART1; - } - else if (FREQ_IS_484(freq) && MIN_COUNT(DecodeTag->count, recv_speed)) - { // it was just the end of the previous block - DecodeTag->count = 1; - DecodeTag->state = STATE_FSK_RECEIVING_DATA_484; - } + else if (FREQ_IS_484(freq) && DecodeTag->lastBit == LOGIC0_PART2 && SEOF_COUNT(DecodeTag->count, recv_speed)) { // EOF has started +#ifdef DEBUG if (DEBUG) - Dbprintf("RECEIVING_DATA_424 failed: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + Dbprintf("RECEIVING_DATA_424->EOF: freq=%d, count=%d, recv_speed=%d, lastbit=%d, state=%d", freq, DecodeTag->count, recv_speed, DecodeTag->lastBit, DecodeTag->state); +#endif DecodeTag->count = 1; DecodeTag->state = STATE_FSK_EOF; LED_C_OFF(); } else // error { +#ifdef DEBUG if (DEBUG) - Dbprintf("RECEIVING_DATA_424 error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + Dbprintf("RECEIVING_DATA_424 error: freq=%d, count=%d, recv_speed=%d, lastbit=%d, state=%d", freq, DecodeTag->count, recv_speed, DecodeTag->lastBit, DecodeTag->state); +#endif DecodeTag->state = STATE_FSK_ERROR; LED_C_OFF(); return true; @@ -1456,43 +1488,59 @@ static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *De break; case STATE_FSK_RECEIVING_DATA_484: - if (DecodeTag->lastBit == LOGIC0_PART1 && - LOGIC_COUNT(DecodeTag->count, recv_speed)) - { // logic 0 finished - DecodeTag->lastBit = LOGIC0_PART2; - DecodeTag->count = 0; + if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed)) + { + if (DecodeTag->lastBit == LOGIC0_PART1) + { // logic 0 finished, goto 424 + DecodeTag->lastBit = LOGIC0_PART2; - DecodeTag->shiftReg >>= 1; - DecodeTag->bitCount++; - if (DecodeTag->bitCount == 8) { - DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; - if (DecodeTag->len > DecodeTag->max_len) { - // buffer overflow, give up - LED_C_OFF(); - return true; - } - DecodeTag->bitCount = 0; - DecodeTag->shiftReg = 0; - } - } + DecodeTag->shiftReg >>= 1; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + } + else + { // end of LOGIC1_PART1 + DecodeTag->lastBit = LOGIC1_PART1; + } + DecodeTag->count = 1; + DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; + } + else if (FREQ_IS_484(freq) && LOGIC_COUNT(DecodeTag->count-2, recv_speed) && + DecodeTag->lastBit == LOGIC0_PART1) + { // logic 0 finished, stay in 424 + DecodeTag->lastBit = LOGIC0_PART2; + + DecodeTag->shiftReg >>= 1; + DecodeTag->bitCount++; + if (DecodeTag->bitCount == 8) { + DecodeTag->output[DecodeTag->len++] = DecodeTag->shiftReg; + if (DecodeTag->len > DecodeTag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + DecodeTag->bitCount = 0; + DecodeTag->shiftReg = 0; + } + DecodeTag->count = 2; + } else if (FREQ_IS_484(freq) && !MAX_COUNT(DecodeTag->count, recv_speed)) // still at 484 DecodeTag->count++; - else if (FREQ_IS_424(freq) && LOGIC_COUNT(DecodeTag->count, recv_speed) && - DecodeTag->lastBit >= LOGIC0_PART2) - { // end of LOGIC1_PART1 - DecodeTag->count = 1; - DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; - DecodeTag->lastBit = LOGIC1_PART1; - } - else if (FREQ_IS_424(freq) && MIN_COUNT(DecodeTag->count, recv_speed)) - { // it was just the end of the previous block - DecodeTag->count = 1; - DecodeTag->state = STATE_FSK_RECEIVING_DATA_424; - } - else // error + else // error { +#ifdef DEBUG if (DEBUG) - Dbprintf("RECEIVING_DATA_484 error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); + Dbprintf("RECEIVING_DATA_484 error: freq=%d, count=%d, recv_speed=%d, lastbit=%d, state=%d", freq, DecodeTag->count, recv_speed, DecodeTag->lastBit, DecodeTag->state); +#endif LED_C_OFF(); DecodeTag->state = STATE_FSK_ERROR; return true; @@ -1508,14 +1556,20 @@ static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *De } else // error { +#ifdef DEBUG if (DEBUG) Dbprintf("EOF error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif DecodeTag->state = STATE_FSK_ERROR; return true; } break; case STATE_FSK_ERROR: LED_C_OFF(); +#ifdef DEBUG + if (DEBUG) + Dbprintf("FSK error: freq=%d, count=%d, recv_speed=%d", freq, DecodeTag->count, recv_speed); +#endif return true; // error break; } @@ -1587,7 +1641,9 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { dma_start_time = GetCountSspClk() & 0xfffffff0; } - volatile uint16_t sniffdata = *upTo++; + volatile uint16_t sniffdata; + volatile uint16_t sniffdata_prev = sniffdata; + sniffdata = *upTo++; // we have read all of the DMA buffer content if (upTo >= dma->buf + DMA_BUFFER_SIZE) { @@ -1618,7 +1674,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } // no need to try decoding reader data if the tag is sending - if (tag_is_active == false) { + if (!tag_is_active) { if (Handle15693SampleFromReader((sniffdata & 0x02) >> 1, &dreader)) { @@ -1634,7 +1690,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; } // And ready to receive another command. - DecodeReaderReset(&dreader); + //DecodeReaderReset(&dreader); // already reseted DecodeTagReset(&dtag); DecodeTagFSKReset(&dtagfsk); reader_is_active = false; @@ -1653,7 +1709,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; } // And ready to receive another command - DecodeReaderReset(&dreader); + //DecodeReaderReset(&dreader); // already reseted DecodeTagReset(&dtag); DecodeTagFSKReset(&dtagfsk); reader_is_active = false; @@ -1663,7 +1719,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } } - if (reader_is_active == false && expect_tag_answer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet + if (!reader_is_active && expect_tag_answer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet if (!expect_fsk_answer) { @@ -1691,48 +1747,35 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } else { - if (Handle15693FSKSamplesFromTag((sniffdata >> 2) & 0x3, &dtagfsk, expect_fast_answer, samples-fsksamples)) { - uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF - if (dtagfsk.lastBit == SOF) { - eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) - } - uint32_t sof_time = eof_time - - dtagfsk.len * 8 * 8 * 16 // time for byte transfers - - (32 * 16) // time for SOF transfer - - (dtagfsk.lastBit != SOF ? (32 * 16) : 0); // time for EOF transfer + if (FREQ_IS_0((sniffdata >> 2) & 0x3)) // tolerate 1 00 + sniffdata = sniffdata_prev; - LogTrace_ISO15693(dtagfsk.output, dtagfsk.len, (sof_time * 4), (eof_time * 4), NULL, false); - // And ready to receive another response. - DecodeTagFSKReset(&dtagfsk); - DecodeTagReset(&dtag); - DecodeReaderReset(&dreader); - expect_tag_answer = false; - tag_is_active = false; + if (Handle15693FSKSamplesFromTag((sniffdata >> 2) & 0x3, &dtagfsk, expect_fast_answer)) { expect_fsk_answer = false; - } - else if (Handle15693FSKSamplesFromTag(sniffdata & 0xFF, &dtagfsk, expect_fast_answer)) { + else { + tag_is_active = (dtagfsk.state >= STATE_FSK_RECEIVING_DATA_484); + } + if (!expect_fsk_answer) + { // FSK answer no more expected: switch back to ASK + if (dtagfsk.len > 0) + { + uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF + if (dtagfsk.lastBit == SOF) { + eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + } + uint32_t sof_time = eof_time + - dtagfsk.len * 8 * 8 * 16 // time for byte transfers + - (32 * 16) // time for SOF transfer + - (dtagfsk.lastBit != SOF ? (32 * 16) : 0); // time for EOF transfer - uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF - if (dtagfsk.lastBit == SOF) { - eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + LogTrace_ISO15693(dtagfsk.output, dtagfsk.len, (sof_time * 4), (eof_time * 4), NULL, false); } - uint32_t sof_time = eof_time - - dtagfsk.len * 8 * 8 * 16 // time for byte transfers - - (32 * 16) // time for SOF transfer - - (dtagfsk.lastBit != SOF ? (32 * 16) : 0); // time for EOF transfer - LogTrace_ISO15693(dtagfsk.output, dtagfsk.len, (sof_time * 4), (eof_time * 4), NULL, false); - // And ready to receive another response. DecodeTagFSKReset(&dtagfsk); - DecodeTagReset(&dtag); DecodeReaderReset(&dreader); expect_tag_answer = false; tag_is_active = false; - expect_fsk_answer = false; - - } else { - tag_is_active = (dtagfsk.state >= STATE_FSK_RECEIVING_DATA_484); } } } From 6d37410d337bcd5e248e3d7ca86881194abee4fa Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Tue, 8 Mar 2022 17:29:34 +0100 Subject: [PATCH 6/8] iso15 sniff: fix alignment --- armsrc/iso15693.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index eb0df9ba0..c864d6368 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1791,8 +1791,8 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { Dbprintf(" DecodeTag State........%d", dtag.state); Dbprintf(" DecodeTag byteCnt......%d", dtag.len); Dbprintf(" DecodeTag posCount.....%d", dtag.posCount); - Dbprintf(" DecodeTagFSK State........%d", dtagfsk.state); - Dbprintf(" DecodeTagFSK byteCnt......%d", dtagfsk.len); + Dbprintf(" DecodeTagFSK State.....%d", dtagfsk.state); + Dbprintf(" DecodeTagFSK byteCnt...%d", dtagfsk.len); Dbprintf(" DecodeTagFSK count.....%d", dtagfsk.count); Dbprintf(" DecodeReader State.....%d", dreader.state); Dbprintf(" DecodeReader byteCnt...%d", dreader.byteCount); From c5f216558c5ec60618124e261bfd3ba3f3cc991d Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Wed, 16 Mar 2022 11:26:07 +0100 Subject: [PATCH 7/8] iso15sniff: add an "iclass" bool parameter to SniffIso15693() This is required to disable ISO15 flags parsing when sniffing iClass because those flags don't exist in iClass coms (iClass iso15 based communications are always fast and using only one subcarrier). --- armsrc/Standalone/hf_15sniff.c | 2 +- armsrc/appmain.c | 2 +- armsrc/iclass.c | 2 +- armsrc/iso15693.c | 21 ++++++++++++++------- armsrc/iso15693.h | 2 +- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/armsrc/Standalone/hf_15sniff.c b/armsrc/Standalone/hf_15sniff.c index 45d837859..069266580 100644 --- a/armsrc/Standalone/hf_15sniff.c +++ b/armsrc/Standalone/hf_15sniff.c @@ -100,7 +100,7 @@ void RunMod(void) { Dbprintf(_YELLOW_("HF 15693 SNIFF started")); rdv40_spiffs_lazy_mount(); - SniffIso15693(0, NULL); + SniffIso15693(0, NULL, false); Dbprintf("Stopped sniffing"); SpinDelay(200); diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 07b44d8a4..01ba06a60 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1232,7 +1232,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ISO15693_SNIFF: { - SniffIso15693(0, NULL); + SniffIso15693(0, NULL, false); reply_ng(CMD_HF_ISO15693_SNIFF, PM3_SUCCESS, NULL, 0); break; } diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 134b633a8..e261efc59 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -91,7 +91,7 @@ static uint8_t get_pagemap(const picopass_hdr_t *hdr) { // Both sides of communication! //============================================================================= void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string) { - SniffIso15693(jam_search_len, jam_search_string); + SniffIso15693(jam_search_len, jam_search_string, true); } static void rotateCSN(const uint8_t *original_csn, uint8_t *rotated_csn) { diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index c864d6368..c064d7733 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1575,7 +1575,8 @@ static int RAMFUNC Handle15693FSKSamplesFromTag(uint8_t freq, DecodeTagFSK_t *De } return false; } -void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { + +void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool iclass) { LEDsoff(); LED_A_ON(); @@ -1601,6 +1602,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { DecodeReaderInit(&dreader, cmd, sizeof(cmd), jam_search_len, jam_search_string); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE | FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ); + LED_D_OFF(); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1622,7 +1624,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { bool reader_is_active = false; bool expect_tag_answer = false; bool expect_fsk_answer = false; - bool expect_fast_answer = false; + bool expect_fast_answer = true; // default to true is required for iClass int dma_start_time = 0; // Count of samples received so far, so that we can include timing @@ -1686,8 +1688,11 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { - 16 * 16; // time for EOF transfer LogTrace_ISO15693(dreader.output, dreader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); - expect_fsk_answer = dreader.output[0] & ISO15_REQ_SUBCARRIER_TWO; - expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; + if (!iclass) // Those flags don't exist in iClass + { + expect_fsk_answer = dreader.output[0] & ISO15_REQ_SUBCARRIER_TWO; + expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; + } } // And ready to receive another command. //DecodeReaderReset(&dreader); // already reseted @@ -1704,9 +1709,11 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { - 32 * 16 // time for SOF transfer - 16 * 16; // time for EOF transfer LogTrace_ISO15693(dreader.output, dreader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); - - expect_fsk_answer = dreader.output[0] & ISO15_REQ_SUBCARRIER_TWO; - expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; + if (!iclass) // Those flags don't exist in iClass + { + expect_fsk_answer = dreader.output[0] & ISO15_REQ_SUBCARRIER_TWO; + expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; + } } // And ready to receive another command //DecodeReaderReset(&dreader); // already reseted diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 29d266709..31ca6e6c1 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -50,7 +50,7 @@ void SimTagIso15693(uint8_t *uid); // simulate an ISO15693 tag - greg void BruteforceIso15693Afi(uint32_t speed); // find an AFI of a tag - atrox void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data); // send arbitrary commands from CLI - atrox -void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string); +void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool iclass); int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time); From 352850d158dd799d039b95b1368ce39822e7610d Mon Sep 17 00:00:00 2001 From: Yann GASCUEL <34003959+lnv42@users.noreply.github.com> Date: Wed, 16 Mar 2022 15:33:44 +0100 Subject: [PATCH 8/8] iso15: update TODO / BUGS / ISSUES with support of iso15 2SC sniff --- armsrc/iso15693.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index c064d7733..ac5c0fdaa 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -54,7 +54,7 @@ // *) signal decoding is unable to detect collisions. // *) add anti-collision support for inventory-commands // *) read security status of a block -// *) sniffing and simulation do not support two subcarrier modes. +// *) simulation do not support two subcarrier modes. // *) remove or refactor code under "deprecated" // *) document all the functions