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] 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 zcma&P3w&JFbvL?ZpCkEbW~4cmC8}TpMknlE+Re96{G;PC@I5GQd6w>>sWq7dJ7`~YP0IyM4iV?d!ab*MiwCJI%ELj|}* ze&7GvXI?Vt_p6eS#nIN8{aWk4{%fthhpHlz{(p!9x6>70==~qJ|HZm5{KbMBT6)*t z{<+VuZT<4+*3ummZ2RkliLc(dAd#TYQK&7^);8b4f0xi&s#>sM>B2=z7k-RBPc+zj z2|tf~_tRfUkO~mdj6@*G|8FG%PJmQLOM*Q9cgp|1DnWQ&`u9MBB=u19=cv}^$N$BT zsq`=Y8*Lo%U%bM9?7!!~)28((V}X2tQuMdvsnc?2a7nUrMiJI&zUI<074>g~`aIk)U2~qwIHR*oiI*Z58wE z%}#4BG%QZh!9X+@8Z#U-!EMqN9%U_OMaUA4)lYG6OF|r@ta7|yLfjG1@A2wgxkp5M zL+P7iwApI)LRq}$35t55j7XSCeh<~3qA9sI8L^D()Qi;aMY6^zxG06i^K@YagoH6g`aRO#NfWYNh959as&i}bo;}7Xb%vID@yr(6 zboir2D;#>Fr!{G97l)iDsPz!~^ILU*qB(0=v|$6`Cs!fKrMR?A@iJu($GzYZ@d_QJ zSk6j_CD>y4aROJ9mxzGmjhJ0Gx{$}@@l;yvt+p3l)X_<`AG)CmR zv}^=jJ!ZX6OT2KxsP#9Fyb9x@j;_eNE2rcIo5wZlJpGQAk3@cMj8!K2vxX{8(YsEY zjHp#{cPf^jpChy^hmp?n*Q93 z;)n@)UM^PQq0mzQv$`oKr*3_kw)Gh~nzGs{qGsj96wQTTyb2Y@ zk7bWL=cui7TgHgn=LRvrhm2-ALQ7?2havpWQs!DZK)b#Ys!Lr<2fU~Z?GUEiMA2lZ zK{#addm1X5X&=p#t1_aQ4hXc#1e?VsbKaWJ{i4!e3(GWVj~zP{vV&oBpY6zyEy8#Y zgqcEUGeX1FNepM{yvXK<9oiuGa#tH^bYdw*?%UF8=yl(5)oD7j^G3O?#~8QH47STi z#&|w+A=V)yuN!Yw_WJ!9r!%x%hHVVze-he^8K>nLcYzF_Fe?4^T4LvDm|DqlF^XYY zN+DJICJoEQ)SVVJ{@AUG5!wT_vg{~2FJddu=Ml9>Mm?(zefB@Aeh@=$m1#Y8IS*qO z26;W&xGd6GZ{>8#y}6N63KWb81wZjMn6cOqh)I&OO1Ez zGgiLMyWIIq=%}w>G3eJ6wL7a-$my9vpD{l<8kJX5SQtTHzhY(#nlfKuT-Vdn$*2lF zf>nQ=pG*XU{#wK;R?}W9e#n|DB2JcK{vLw?*6~cywq>6k zchC5?Wt`5jkTDMX`XwtavKibNnVQvZJ+Bt}_VAMG$c2v^BXi10e5_9NZ z=>{)!r*Q&`w%oIRnwsa&GtpDDG1%g5Sz9@Wwvq1hYtYJ;5vvEiakxLpx|an;+7ntL zBJ^Eq=CKdZzNEG-mcQ;p^;^}bim#9C|MC2_FFw=;dkC9(Nwto~hC}(!QUsgX*)F&3 zhp9a4kL#2_t}GTM!Jied&(lk>4sUhVSOz1r(2JZmPP4V~BE1H6={9}ldMmA3hpolY zpR7Hw31&Gf&M6qisnD2kq|NV973*n})oONUMVy{+benfz!&`3;Wm1m678b=x;eS>l zxYgQ6j=!e+s9A<6nAb0|*3%KJMK~LZS?2<6SQP9-tOfR%LB4*uSDgS7br8(^2|xh4 zItKH8iT;-t$r%%sde3`UFJ2uDTUwYjE~tMWxRIYs29mnZz^^A%Yi`T1cv}mhOQOTu zqJUq+BInO@x&pQi`h_L%e4%aEG1ccU%A0T}@Z;6*=8<}#{bJk~coMb%zl^My^w(Vi zzx0j*Y+2__Y%K`lSz64(SQ&ota3;lh`UMMF;MW`aNeREOSFx1W3>uFA!UPugMc|iy zkHh^Lt-um+f3m@5Uzjw$Mz1TqDDU}ek%N9=<~@un9N0(eSvW=gP_!)xC}R)5hw)3p z)){eS;Bob(TM(3qROL5%wE>9)f3KIUi3f{#`N-lt{Ws>FD>G>`ej4 z7{BgHRjVJcqVmUoR5u-P37%SH}>wgyTi)|g%sFgXJg>7WyOcti$7wr7kB~~X6 zL2u(hIuckaZ^_I$?0=SzU#)DGVW?>?gjs8;&ti}gIa})&x$251uy z4Q-zH*FxhLd=fkv2<&0Cs@>e>YW%Xr;SzpL0aMUJM|VCl!`OLv)Hl@c6!43&33fw6 z0Q#e`;DX=zY51jw(_9!=GyRkn^Ittb9ls1^B!OS8z^|Y~LllJ|UiQbH$1m=)Y3~Wd zaPXk zSKe-fA^3_0>o>%TcGTA|H;~`PlfVnTpYMclpAQNX{(FWPztnPuEf#Ko`3iNUMlu@6 z-WIH0K7PR_RI@2KM>SsLvHst>XQehWdHf>Z9*##gh^yr*N*l(n(=i{&yb^x-YGr)Z z{8!&Ue26}SfR@&=0JoZt4 z-G^W^wZ1PhTBP>p*+z{HGLRYh&r-Brw}AzK&BOrlt7BjRTFCi+9pjfqtI7UlmKG9F zwBIt2O&VU8w$H#Xn8CTQhtW8;77JoG^?8fgvCkFot6(!>x}OlO-fY-#@rbQ$X3k#= z%jBhmxyZs?%<|WCE7n~vZeG8r+V69OA>i)=a4IH^W%L`;$iK#2C7sn42}OOjqc%Ar?DgBtV^!Hyfq%^$5^35cEd~4YBNZK}8WQV) zU-J$Hv!d5ucUk`%<&CyAhxdn4#wJ!;)ekX8*+N|(S;~3tv)T_xFwy$5K71e5Uwzb#ViaGpwdXXFa zJ^*nWW~@4E+~u!DuHuuVy1tA)lZYvDl+_?s1HT^ef@S=xRt{wTYOS?F)F!uQK00`1 zH@Hl{x;9reCA$6hbX#%h+2CK<&8CHky62&~S?ld*X^B@;#=nMdyI^01QOr7z?Y4Yi zhG(4roWJYJ@e5m9UcJOH1;(Yq*;(=Qi%Z%@(B~rm+D<8Il%u_!b+j>XovKb6pY{Hg zp7N}uc)*`$#;?28M{~icU5ja>YRp>4MGf`4?nKq`RFQw3^7<3ar@NQb)Tk}BF_?Hg z_Lv8L`hc%r6ZHqg%ig1zPmUW))yryu+x)f>6KtcZ$3=;M(PUt0?O&<>lQg9?Z25>5 zNtN+0IwIR-_d~96#3Uhh3&WQy4Q`<;y>LRWT*$7KS1p_ zM|w*9>oi8OP=-Of=s6brabcumW&Eo)O;P4w6RaS#S*?`7uU!O9DdS&#DvGXlj!+*t zax>!>3&DmG{~F*S%UDLdMVoq}UT}NuTH1*xH#ZddSN*T(h}z5Z{1!Ua)>Sh{&vUJ; z$%G303;6ZE+rAcvHbU>x(=xJOi~!M=$(D>!!mq%T({6?b`g7K()kfg~_#~rrb)ZrO z3jC{LsX8cI9<_4fEZsM{bEtZ^n1CH?RY4crO7cVlzZ}@QoVBd-B}aMCuQqWE`h`G8 zqQJj2eg)@yR_{Hd&Hy%c)oq<8)J7*hV!aP!)bAOxYU%M!%aidn#_Qq*wI>+ zA-8;@=5I^<%cXP8%P5iwOu*pJSrHj9PNmMVolg|_7uCN<7u52T;i;AHiC5G@Cw$Ck zr&l}>u?;2ub-%M;w)9wC;<)&}eVw$D#tAsmH;hDjMUj7H0JZaFRgZX$Hd2$cZWmz) zz__o7JpXd)o~6AMPgGTjBWkCL$=DS@xPHlD0Y*u&u>|DyL+e6R_u-|JsSuh7D^;h)Yr0pST) zn-c%}oH;C_2`gMHoPJu|6B(*KML!N`Y%20EhYr}$Mr%i9l(v{r4{CLaZMX-_bqe}L zlqH86l(3+B&9Z8VSPiDBaXGJFg!slm9zFQG`6BI=Ta|G>@CxiR_a~2EADIOInorkz z#+djQ`j`Y&^ilYl$B-qwGpMPaVD?OtC zWFO{VSz*yWlYMop*FO3(%}$6negCk)zgA_j>wMzFAWmOQ0lz-Pze0I_!G!l*pndR` z?8?jd*S0hH;b6#LW29Eqlb7?aNH(-v=6#Y=!1wlSgn699x>Mv|i9&xS=_2-6xDWou zH2$TBvy~N%eUewahaGtDALd_EB2UCNMBku|s>MNcZW{ku22Bawi><{1e57B(CS1^^>7v&1qa4>nO5c62;-X0zr0Y9e|=S)pnlm%zmIAlO*d$}zML^RF-mclotOr{}Sl4#;*V(_LuXo z6&x~kd=iSl0wdb*;a{1M-yd{?{e->TpUe5zcSHHk+s3#+{Ll-P<5z)p?R4@~n)w&@ zp)xMxUm-_aA7dg0+I3~@zU=SkU%_oK7jdsDAq1rkIb{4k{#B;~3!ygAz#f{ye*Q53 z`Uk0r*x&VEArN78fIFA*uSjBLPH83D69P}6_FCx9bpFL?)eijX$VH|C@0l;F4ktWi zT+YAl6gJF7%Lux0T>gMs2CQC@=R^hm6$1a_HXho%Mg!C$(B{MZD*(KRdr+$pa?QAn z+{ESlYe^_hKL5g>AH{}@m+`N>P1s}Pv~jp4Yy6sk%L?Plk>*w<{slO`WODGz7^h>@ zu{S(9oqx4@tFpCDGH;*XrnDD#V_ETg`Iin}iC@}j57BS5Dd1Oue*vkr&BSm(Yg+r# zi9&p%#J@sui-Wr8&nfJmb~xn5H2&4$-7;w$H!smO+-8c-i{%PJ0lxyscBK|NTaO!i z0mmJ7wP*CwO9X#oqQJjA&VQ+>W9^9kfPSo6?+vTYC~Z>natLVW`PXO#vR&r7fR(6h z4m@nHm$RKp$^M}ia#GPEegzJ6&661EZuiF+`_1kHjtVz92kE=#^_Wv!uO-fyx3#|j1OU50JAHLTR-yXL+ zz2^66zl^jQCSh~&xN5P^_~H2BH|oE1=pTQIQCzY*yczEyS8|>ce7SP|#UA4=!xW=) z1GQ^lcH-F2dVccwHH!HBqjt;3Lp`+(_T%Qx!!U7S0e$yEN#Ks;&r%#eOe5`eyz)wV z+-*#THXwI#k80#TPjUEsRRyb8#!p_Jk^y$rw7+uYE{)LHPsh#Q)h zdfDBp(C6v+#Y+kP#aKX4tMk+zn2ulPYTDms{&mL@5Bqt0$fQGT=XVtG3u*ELWAUVw zX?j6z0^Ec$SF(L><3!jr{Hl>at8x4a!Qt&@@h zTKuqMg^@vB3oT;eBO@c1#}8L2SmQLc`aqVZc@#<&;)mTj|FvGOvMWcp&w2a; z|KdLT`i1zRd4Rq=61uxh($`*e9ElMuRx!s5BCMCRB!vIkH0e{1@}DXVkS|F135C0nsvU zB~uNw(F7=*E%7ge4F?c5T-X1G7}?&Di%%IrdO;zg_;q8RuV0-1df!^gk@jPB21--` zUrQVO$fBk){JPM0&DzKr<30M?K-0u6qX85K0LX4RZ%{FQDEFH!&jXUi%t!6U!=c+T zu8lVOGg;zah#xkzsM)>uoJc-KEu$gHZ7dCBWIH^Ej(seE+@HH8Azo_8V#r&F$L?Y5 z31OKUcXa%4qk_letJb&B|P z(do!tbJjR7rxZ+z6KY^0<{{8ZUAm1z{BT&c=MWP75BB}MRX?f4XVG;26{iETrh8R3 zm7{|W^eZi5Cg*@c3vMm)uP`Ajh*5NUa0}OaAw4c~d0fKRFUBwA9b_mA*n;3iR4681 zWII;kUsrJ$>Lz}|L<~UY8OP~AAe7-(h0cFD)&^s-+^XPabQ^7SOtmCA|5e7n2C%iV z0$Yq)9V-oClWI*~p8wJ)Yz`1pfa)5iBJ;)9m|p^Z?I@oL5Z?WUJ<|8g~-kSDfY_+)sc)$_D|Umx)6Dr^7RYx zLw--)>itcPFewO_WQ}#KXcmMqQ|}+2f5j(Z>l(3M(c3V<7;w4OhA^Ffy@_0;LwgDF z4H8`Zhr0~AL3u(MIj68l~7?ay5wP6rdp=wzXHg1 zoiwK8MOv=rd&X;w%Y!wO)9?$+JtJ$xkoIcJg%c~|{s#Cx{EJlvuodeS`NYA8@?Tq~ zf>%16_OhnLHRybc`+NA;)37A{(iugNr-l(tVNIv;uMV~CozN0`-EPZ-oBQW+e*k2c z;TL=6C(~2(v>i!>ax^99^OMW?*DV=iF8!L#`-7Sw>|FsY_%Q#f0Db|Ic8F%$lZ*C% zUrfWV*4yx;S?)BOWau@qh@MhSoyS z4Gpyl6-pCW`CAY_M9yv@_xT;Tc=*|I6|f-Ck-TNi?4QvYwG6Pe@+}3<$ULQhUx7S@ z$uuphIAgZ0h>RKI4KHCsLMX4_fXd|gSCoYuaYPY_Ru(_hR52sWx&vlRVhI??G=7O9 zezDmBtj}v!2j~$9Vg&f5`B$-iV-V`lA#brUy?baW`V7p~_+=x0=wjHl=%kc}AL`5b; zi2&+1k~2;j=gpUt_Km$R{aJqgYyM6w!AA*YbfJ<1)W4yTDl}?d=p?(7yRWFjA)oecw+KxSc zo<;-Mb?$WjwVcsPn?VX8p8_CT?$zXsi5Xf4xqjnB6k*;`BcV`sND+-aj=&jt=Q(h)mB;WGScRl^j&t2-SXm*_?vh7%=Z8LV>r`jUcc z^s2Q)^jUkQ>%}Zl11{rw*G-EbJ_;^lb;GJZ1}tc(3SR9nm#LbDUno2D!ioMgy$9MA zrv5NJD|)@^1=H#`0?R$X)_DXn0b~#)cHMZ*H2gZF_ToLWz`x+GU|g+qm=Z3=H7$M! zZIZPOP$I{(dPSJ-30$Wjl+|zSQH^eFD)-H88i)MHa6-emLR`rEd`TDy}0! zSOac0#T0}x{JJ>0K!tP0LFcS&Q}Lf5|AqKrCSHbLpk3YVbrIEwnMziF~EK`C9vciuPI!LBtP}s6Q;nFLl6aW&YJ7 z57;1@kAr{mXO5~76))E;3&)s|SDHlpT~ zy2;V_)x&za&9aB zlDjY&9y2bh-v}uK_E;{qg9E`v+ANU~`*8h+Eh_E46^?AE6BV3j?!hQZ{EO!YBkf!Z z?%6DZ0CC{gE}o6)^&81ZegCzZ-tOiJ=gcE!z7#*S&N=Ov`IRFIuq7ilFb=y{YWzy_ z*ulT#XcyEy%biVBL7<2xmh*cW96b#@@e(3vU1|6xGx=5Na7UpU$;=d z<#@3Lsh6-Q&Er)|Q0UvAYtD5iz|r(PyMZgI&uen9djS*jTCb`%be}kTd5Z_SZUoM@AErXp8oU+f-QuSqa{H*Fes7O=*va1K<+{T;T8_eUEQ#V3cC$20 zqt60yi3tE%5x-8d$0#E^Rt^p@U#{VCosn=n%J`SAHq3={P2+SyE%Y!^?by8-*JLSv z_&Ig(w$@45`8R1#&n{IxjQq|4g?Lo9gkN(3Tdf~Q4*D(UAURHYZ!m767dKlN?052C2;v2*8h~f%yk}2R9%vYQR?sFl2SUqV1tpG^@ z&A%wbiguc{sT4nC{sq|D<~4m_BYv2xe$Qx=ub9^~&M3n##1Bcs)`*F?UoLn7{A-J9 zZG>l)GZWk$u0LF;Vhmg3O#dYUi7|Q@0>HNnzlNPh6yWbI!h4dz?A$?n3$?B=b{6m} z3I277mcEKnJdggg=TIzUbR9L@tr^49@QW7rfsviY_S1Co>p>X&bY!#~zrdhn82-Hd z8)xroI49R}+&BY#?=cw~vXh77wAray0AS7YFAwp@46Y;qr zQ~lCL*f3$N5YNtBR)t{Nt%AGb*B{Dj4q0 z7?;PdIP`0uT)!c7W%OFQzcZekjiQL2K8ht#KcCmH;V^or+N&a|2OJu85Ek3qe-d#3 z$_0#oZde z&WQG23@{%*w3vS-Ia-LL5SSl^l7<&%e0-@Ity{5`XoE zcxiO`T8&>%W@fo~QkMThNbokG)foMdTD&0oV?SzN=dJ2Zz3p=%BF)hhLW}1CTl1;A zxAFw-w;NOVE6>+2U4NLw*qg;k)-M&L%2#0UL9W}&^IvD(_7$L1zZJ-Tb-|!r!j5W_ zuoC&tV*UlSnz$ulFdV~uLS2d+mJrJDOIJQjMt<7AWD(L{6nWKn(mB`Hu>$WY<6qBo z&!2>T#nx`?ZjFYXOr4@9(ye!dCQJAQM6+87Xw^(niRustvtBF9h@%{=5x=$6m;P z>GNL;oNXvy6bOep)+pkK=T!$GfLx3pzC}+AL>*1LP>7fVJ=x7DJdJ;Oz&KDvcn(${ zMZIntIdL!-f}$1hYvxbzSJ68{s6y!XPZ<*3bl9plfIb4uYxkvctu=iu`Mkr^OEM?^i5{@NoazbS{Vddl~!|e%ekN%1dtcWsNmGCQoJth&m zSY+Qz^QAE+Ps^w5c^=x7_*dWnC1t25c$@eZwaCz)iJ!?wGq4-XWiZPeKU_ys0if09 zZ$@{GqHH5#8i&l&^58u5xmdq(nx4Yy7#Wmpe2i)|Y^gKujojxV{~EVBIv0L0^aI4h zVRo2^y;KRIyne$(v@j66JGfJ(<@Fp(aY^UD%J|o5aWA2atw(GZcY`XfN>mPDap828 z_}9!qb+1I5G4UwfOV>-Iu5wTV8IRq!t8V>U3T&%0oC$rOj%3O zJuUOJt9{)JtcB1-tcYJa|0VSb!V!c69NB6#I$Xa2=cHJ_VZj>5Ier+U*WIW>{BQ}* zx!lP7>!|P7#VRCayVqMd8AO)q&fQv-2!0t3u&aUzw8`@?+u2WFBAnN=DMPofnB5~j zP2Zws*D9;uxPnfr<<6?^pr8@8Ac^$--vziJ_Uf|wjY0PL3AxA|ErF>7;f43bRy14U zUuWqh1$NA4@D{beK^W?yyrjM{4Zn8CyBiv@77xsn8><=xY776xdo+m?VP*9jPmx{= zi9Uaau5CtF$IQn9EtzusdRecab!AY9Zyp|Ovj)Xsd9X9CpkJe=<^@Cbed@567z*z; z!p?EEcryI-EF|=E?L(~Z{+cSv6-IH&$sxfPdB7M~Q;m9DInwV5*r(}bd*QBX*JwxC z#$V?!Kg)Qe3f88GUu$V|AK38%(L(7;q_omaU!wFF77jRAh#$tlIHDt1!T5?TbjLvG z0kYsjavSKNWZkj4ID?5;9Mi6T%Jl)iqyd7-<0|s6^>DwV-c}d13xc{OgF-~q5vg+g zI>D!3J@k+TBq?A(*dB79hbnvdv%HEa3}>NN1N<_e<3a%IY`#_1bQH zoGwT#Q=;iDdC_feghN%tudVcIvaLHDMY{7SEwWn&LtE)L_L3c~b%pv3&A((cV?9J4 zy%}jQz*aC${|qBjoh{XGbi;FqWxE?_hTNHnL(Nd@>^QO8s>7l$U&DlB z%QAv06nRl~j7ENn_4=1ENNLv!OW` z>=3v5>wc>KM_7x!s(R44j^2=M95%epI_m8o3J;d?FJ}sH9FfMPa~^>nS0@)ITXmw-*Dus>2+nq;`FtFN6{|QuxXHmJ zl;Ia=yEF~l4*@Xxfbj+Mh+Nzgf52Gh$LE1x@EpKO5~0#x&cCSfsI_2MgS(NOT#(Oy-A&hd!Co=szsEoSmDmuzIaDKG zPz$`8rCXj>&-Nv9t%-8}b=G|P^~f&c*9^zrw*9kC9X@awesTV5VJ>{$SnP1wM);i3 zAzyxAq0=&Dlhw3MdH<$_Nc-#V=fB#9!%rIX>EAlr zC*o&~<>Ez|*z?lx%6a~0>HHTh+kueIOY|GL3=3zhx8CIhA6F#i@e4ORco@zkaQOu` z_#Cf$h1v#?fS@kjhVx&;G=IdJ5=+Gim~t`xD?)3zl*#DbwL6BEUmO{BS+?SgRL?zY6<|V@&`c-be~3 zn#tEM7yOIMHh@;=>1#N09GPr-l1}*eRmQ(gD3ooC;>6Q=b)!=~Cccgp>>!MEQu?@K zzs30FSVJOeYhgzaOiHy<_i!P8I12sB&|0#nauh{&i=65mhGv?{IQ#^^O)l^x$|-N8 z`4h%h<^N`0Ii`6fPOMDhUsJVkS2Cc|Z&SOuZ9mrQoYkHQm+`MK?H!1}Hv0WkE2Y-7 z!gTkqrzgDjv^6Q-^VcF*(JkRade`A3gW9Fy$AdQ3Yu^~auE@V~fj^-AUQ4bD@xuV{ z%V}66{+w_q*wy^Y=CPyx(0gCTRkb4q6fWZMYHR}$Mf#cygUet?;|KMJJr__NTGMN6 zH~$wLkD4AV+$FlH3*#D_k>oZ*6kl8NowB<} zG>8{yC&hIAhH$*@uZUND-QoJfXVmpM^ziHSosp)T6|3ap|3-e|`E7LkkYZyo>_h5T zj)x)h&UAASUNs%=&qVzW`XGJN4l8H;o%4o=MA#RMSlwB(X&69O@60NbQ8#R zH{(}-4%ScS`b+p_fu5}6w3j;Lwyz6+m+Lod&cYP=m-%^X(@7^eG-uvNtpn-Sp6-l+ zibTxT_T23_}g!AOeZ+%;@2<@2Eaa|7kwsMaG(5V@FZWA2T>l)UghIezQ{R z&W?&>fC+tCxfuk!O`2l}{37ol!#j+!{8t)2322ui($qvT*Wm0xluG%p?g~Cebgc|A z9BU#5M+kj}zg;I@E#jAbulQ&(bUe7oMVmV?F3b-(Jkqy(Tj$pQyK~4{EF&4zt{k!3 zdJs0mJfmar4C03lhDE49yp>~~7{y!883iIX2jkL=EMvUx?~Rz9C7c_rt-%RFIwlu7 zh*|0RallVY{L4g`#kHE@#ig!nbTtustYN-_aFjoZ>o-0r`Xyj%fvA@K7*2@y*d7nr zf979Wciccj3@3k^EDxU5)HQ4gfH3N}8M3Y<NRR4OZUeHkuS@CWxxSGv1 zikvYlR*6F;CQQ=sl)rLV`t7f_ohu>qFI6_4T(`ABsi+Wr1no``s zxDK1#gp{uDU0TqurG(8E#~#z1(eu~Ky^6~=H2;Db1b%IUoo7U&ZIgvqH|jSe!iI!; z(Vx*T{AF5<^;+JywHMC6KR@;mFYtl?`e>nf85{`*q zjX2-5aQu+<3z2OFTlZc-zsH-oTfiGU#P0#+Z1qCR1TMZvG4a8BlKdWt{MTrATN<-^ zjQ$PFRI?-0EYGP96`L%>FPc=a8_#J|>)Vm}Hst_r-;gNZXiFz4~O^7Zq$ ze*>V@I2qg^J^%!?LT+>CyWJat%SiILaDJJdr7WY>F$nOH;COUCE0?6;{}lPx zlNd$b-?*gjj|!cZziaML^MPNLNq!HX|3Wy_iw}q591q8PVCQi^i2{D*0fM zJBaOU_F_EtV*GH3UUoa&uxrE|_z5bqzJDIQoO@Jlbwvrk#H6NO(541fG~id~RMldd zKVTH`3vn%2(J+UqaIuWUejY?TK|eX{?=fY50jtBbON=O8AHF?!Ep3uQ#=ea{+dMxF z6(+aQ78?R!YrFWqZUY-$#4l6!*=|p8j(CpxB+SMA!tCs`cXe5IDj(m#`DHOWk#JBZ zn&S!QwAW5CDsiH@EPnV_=7QVN8`**FhdLrT~~xAxGxx*Am;KH0lqx0KX{h!d$2z1KGo7Xuv4vUu@Y}t@^AO3w0KgN962^ z_%$Flbz;c2-o`8h>o(urgCfj~xTk<$;9uw}Zi1@x`?CXjp#z(YUs_K!|8jI!-|B=( zk+`e__{D#f1%4%Yo;iNV(ZaaW^MgPu1O0JWl5ncTzvj_qYEo8COhG`1$qn+8>s5LE z#wRK78sRKCIUWp14SkdMVO<&jayEKE>R#lMzM~qw5Kze3D6h*}4Ip%q`!i%+A)Zxx zawto9UHk$9$_}pt676&2Yf9&rzwZLAJd8pg1@$6G_9A+KO*>GEAAUfHs%hBr{IZSE zS1mkoaNpOMzg|N#_rry_!Go7Or_;Y1%J_=BJ8%PqZ!4Yu!uQiFG*Dt5;6t-Wphp-QqMPclYkfc;|z@pCA{XGxvH_ zS+C7&DT^PXT7#BluuK|`^<8-|XfU4pv2fZxbNuk7A)Kb1Fc#uHD4WmqZ=j173OOcm z@Lad)1AakeDy_bhDYnrr?hj5t&?6oS+w%IQDw+kGZdmN~=yQ~9ruLCKtzBp%u@t~B zlVB>9Wupyz5^iGxb7OJ|@GHPgz`wo*YmE8?*GNKW^N?;x!aszCbd8+RL-=9-1$#&l zkIMkqFy;VBT)%P46;=Iz0lAp{7)|vz;&Dm!s32+l^nd{ppG9>BlNd zAX*lAG>7MBw4#OjcecJ}WmBiTlokJfFwdvhr&Z`rk$=HIba0|DD2`BSAa1W()XBNX zLBas@_4CO9D)$EFJE%WA8{Fu%IKZzr0A$xP7K|3~E9?NaWVp$Ajt<+{Y>Ab}=v`+& z0l$j;>mhoE<7_7lI3A`B^YRMbq%goT{Q9LzxLaXu*#3AmJU{0s>tI~P{MSP!ROV2u zM;xag;^Me1z2XGke7hIx1@0ty>>fQ$KU5Fr(8G&#H;O0yK6`K!%kp2xY;3>7Rw8xG zeptm1Z{aoFC!MM3`1Llb4a4KcS$bJ6&s^=IV;9xF2e1!|_=U_qwb#SMos>xNvyh{y zs;AlL74d6a{2a6kSnvgvCt~dL@0GxU5`LvO9d7Ln?TEfWeW_?~dWRT?UQEk>RhaU- zT`=AEUjb#T;|Lwuu6 znYzytZPW5!&-{y{Wyl%2ezjDK0c!MkaG64#`#Pogh&K%aT++-I#9e1Fs?{L1=i(qA37 z#>9BwfZWCSRm^_@zjzJqdbRnmtEpY7#XZ75Dha{Bf%YL|9~v!G(wb zvhw^F)+>)+8pu#TPv@r9Z^(+X^v*yB|M^|`^M!6?bjC%Af<5M6#rlmMv{AOahP3ik za=&cs3iTpE^Av+rS^jHCmv#VZKTFS8JG~%u(0Wug9){qN!(&gP{tzdO><|f}?5IN_ z>IDB1C=yGE(E@%A;W$bASi~_9-1D$ZZY6F&byH(CW%xD9D4akX8+W}*Y}yRQ<&Shm zK7Od|DRIp1xF<4JwTv#gH+e8Ah$za1haq@fCH(SGiLo1H*b~%Rhy37f>nRv>7{)^W zD}b^n6Z*9)-he|I4>n*fp$*v`lS#*JOUhqFR@!g zEsF8Da_=sF;uY{~5)O;TuU?d>Xn`$1Gnb|^{KCnp!{Otg$$9>z>qWirR8wz(f4xHo zfDNdbY~`?FPTTn{fvApo`t!{A1?KC)@`vF^#Sh^?$1wnOvy5e@;g{KZ$l5F77!LCt zIwrPs{(!7UMKS+{nK#=#=vQ3xFYPS7m|46ZOsWjOE>PHujA02%goSSE1EOsx@vk!t z?d-RlN*0|i#EtFM`%CzRZg}%2V^50T=KF)S0JoxGv&!Swc*SZxuGq}f%SzL--qI_{FPJ;9n)- z8ww*W!>@O#?Jz6}{u~npe@xe%c82%&m+%Yu9fTtow*J-Ecieyqr>mOJFDF@dn14Z= zfM2OMh=~~ROW)|5#FJwE#xZO^92r=GA!AK>?1&$F&he@x<@}3}Ew6T0PQcDXcRmp`&3V>?ks~ z`W1wEJ-C6n$iJF_XuAMghsT|K8<4C(bZp-CGX8}&2#4ANNhKyVb%KdltOq`S5xt&S78bAedfkbbF!@L{4(2?FsIb@ z;xfLECgE^;CH{r_!yfqI8n&pFVe4($EMYft;8v3NA?Lrc6erYBX6ayH7wF>7s!e_ajOLOJdl<7XT;Wd3!QUQys*QzibTO(kKA=8r8y5I7O0 zU}Vbc57h$r^RwEleAw^a~sZo z={C2EF~a3nv}#Q>k6$|fMJ}O!o&#>sF9+K=O>T2&P6@wuqrMlj8Z)fCok#uri)!y! zWUy4f0h_5{3V2P6whlKiACBzAqG)X5`#0d~d&ui`fPZN&GsQ$qg(v%q__ZF}2Jy^v zZIsKxJ@z;39}{9tCH$&@%|!hn#}8rNbO52SQj1w1R zQ9-{r|HWN}eU4i?j^v`N9{e9zfFgdKXSPM0H}?%A^Dm8PW2O4TH|Uj=&zG^>H@fo= z&H75$33Fc(_*IM_zD-D%=QMudnp0&xAZ}NiJVa^A_}3}E4yz9%&6CGkG437U%jFU! z{2CH)cNHQz*TQaKS~9PW^P469su({Urx%^X2+m1`=|u@e+b;f{CRLkk&IXG3m9^pS zYWxxx=tfz+9f`2>qGKGsM3H|D;?EZ=%+FlF1PtSTqXmG|Hj2GEW;(1pqZNd1M7cjm znn(;M+Zm=|XA#aabQby7YWNb(lJ}UEvUVvaA*!)DP7jQE5x_bBe~MIg}y zg}IvcWv)#E6iWPy4m*n|?7-JLN?8SfuBP{8LITK&{A(ENwHzKa?)!R@I$YFmoR>f} ziE$P2>n%E}63MW}3$snm$zs#;xe1K4h+i2b`5R?S-g?{|pz9=DzSp22(55o}^%&i! z8XapLvTy&O8t*_Lk-@hqSI)mY&bm?++m;>3q6HJ;nA(?gaqrM% z6Dgd;lhL8D+f?LVIXOj7Qw`pZP<@9TQh}2+$p@Ls`4^!=n=?n_;)sJdzun(VXJoC! zelGH_wa~9G_rk9OYt+rGBTnpxQ;_Yexs%lH@Lm%hIjCPnVagzxKb!u?SP35Zs}uaAtF%t~ft z1nvoB^7#5kAM3kB|5IWV#rPqHLwhiaRdpa@`(&K);-x?bkE_JLP%R$~@R5ICAPywL zSuEwhtO{rfV{Qib_i_+5WY?MjRL{!TW&8{GL-LSyn@q~=Ltzq6LK*);8V=Lj)NCCh zY`=$sb5pn|fctY9|C*0c^e}#{ZZqDoAaE|S48H(deE!SvaIC-y-(&2gDcSD8X)p3G ziw;st#&V+{q5GUB6mp1pbkLpemh-RGY`TxEL&XlYNs@w#+x=Nt#1a2R}N!nc`)`+E^TyqaDBU5fwh%74I(900O1{*`z26QNV$J%;1Q zj9)u>gI|VUfUVpAcxvTE`{KaDo#DR^EtdH&<|xCj$7@@1rRJ?splZbNs!YCA~HBQzcars)sfU(~Bjg7-c{8vT%VoP$(r13tS zuD_=6t^F~&lm?yWo zGd)>O5maLMu;tB|4E%@q24NIKmA%{_KEI4=c_%Wu@;xjQ;-X_K+il*A!9DMutyI^WAG&^w-Q%AU=-!?Lxt;cI8662&GSM-sonHF zITu8%gkPHZ61_)e8L>|y>r4v$^eAHqh3zJ)N<7HaZVx)i66!KuJ~_$BT&z zd?Crc+GJ-5q_~J*xIap@0atg;BuJL9ZGQh}mBCLd8r`T^H%*HW{3&Y_8R6Q@n(YW@5d>a}pA6zfiHiGP7Ao}3N-<$MR7$F)%yg$H4S06g>jaQz1R#`sR0 zHv`|pkV8WvOncONUegCmtrrt~{)?6=?bE)_N_1*VjLwchaE*ihXYu{>*kgnc(g~TLl;k3iw5gU$Dm8oA~Y{xIO$GD+g#P$Hxlz z6=)7TMSqpFrc&p%4R6>k{J%n*QvOThm(_#n`9A4%<5FA`q(*-!$u$1e3bhiGSVLU% zGb6-^#+@(X7vH}D^Uijjeyu>n{&^)oSt^m-Ea4ZPzyrx(uYz_Z!(+H`fd48oR*qjw zcpobGh;U^osBqZ}Gcs(pm;HJ6&ws^l$3A4v==21+5D}O2NL<=b7C+Rqi}4hWnVeT& zIasV+DdS(%oP zKpgcFw!Tp+IUT=nYtRPcZ0Z#ciLlYopM!|q03x>XZNb~wzki;0)PV78buQWN%~!@L zdY_iE5>3M|u&oouIp-CxgW)ekl(+8?5O`W@9Xv-@dwO3C>x_iIUd8(l(=cB@R#6X^d&jLU0)i_N{ydK&euy3_`08Ee<^YJ8E8s%2$LNT* zQvPdrX0!`P9h28VztWl7cqJz;^?Q=Mrkww}gl&WVd{G^86YH!+pNpF>Zg2Y)JbW@W zesTWmGzdyWHJzr5ZpUE63*mc4=o#;p-0U*^!pV9SC*vpjT7J}7ow8mGgQvLV_49Bu z!2Y69xVuq;qX-ts;~K18?5{;m$2T;K+zrfiytEDQ-C_=U$X70<9`1530JfzXUF5%N zX}5x7yN)i3ZP{yc)&x98$!+-l!%3ONKaI3FNr|rTA^^~{&SEd@^w(lX<45xM6A^YD z+l^Og*#Jx>c4jm5YiDSzGRbYYegjrQ!b@q^@w#x-_%d-JB9JU@hcjYcC@I=}JI3{g`_)dzve4%N)#6yAsF?)#z6l_6eEkA`0jaS%iQ3kg{nUEg zdJ4I9WZRPnbkzFm?&DXRio3I7nZwk6Kf;DzR7c&tDv~Sv{x3fc!1#6E>Y({ve@;x9 z8Vjby4~J#kHkzT$ZE`E~6t+~L$ z=`q}qs8R1S{6eXEkGMi21lMJKN|6Y6m7z!7wQHO?>7A%z{4DZ~#^e~4K3lXCtX95}{x zBE9o7EJUB~hEP_&!Dv-8Fsl*AH`-~nTIoOlkWG&tBDM_eUOI3ewL<9R`i-cvO7X)0 zTWr^xmk36pDZ*u>O9V2`ea^|tks+5`0Jz;Mbu3a6Q|)L$G5`!`tlkkdcJGL&zA8q3vqEe#46}|9Zpp9bo1u z1mAC>Qtx@qzi1)vI_MWW$!CmrU7vs9uXG#z{gp9jQ<~#!cegGI?d?2CoCsSroqsv7 znKpC~IR%CMm!}wW;j0(;*Ua7UDtGn8GC@ZmH<1DD$}#u6oPV)4xqpJVsKRmntZ_=^ zt$Hzj_@sFO*B#vA&N^=b>?8)5$K|s4;ciqiCp=`AEaoW+@A+-%Jr1Uj`In7c$%)ug z!Rv2*u@~Q8xd+(9{i!MEUtp1rZH2`Qx?f^zCBz|E^)QdCjDPKYz-jLOqhMw7Zq?Ep z+EDp3dc3bChl<2${41e$Bd|>O+siy`KTqW^qA$h|pOhEjS7LSEf9%fsv3i@n zu3!^N@k4VmfGZj(G=@2A7hhIZ(#dm~5`G~)lt6fG2B%|SQXT|$=Y9SH|I+xy_Bq&$ z{qtps`rdJ16Z0>__4TVk<5!c6F@EVpb4Is;S2-QOB<2UX38C{}GsTDm|B_%gDPO|h6HbdZYm{2&;r#M1-QUB%Jd_4)U->JudenWM7C(gD7zyFJ zRNxnUtz2PEi}6F`zsOba^oGnws8xl&FW$Dn$oejiA39_a?1mD0YrRs~W99Kf8}<+- z=;DV2nv)bKoP6S`EPi-tXn_;A3_icSJeRNEfN)v-@M(`t3L8%kcZU4d&D)mg{L4X| z-eLlN=V9T{rni7! z%)g!hcEe^?IejL^PL)_)#2ZE7{Cx{L5Jcf1_EX0Ax^^36;0c)A(0(3^i}9 zpdM)BriwN^SB773f%TfE75JCa>FJmi>_!>?`W@S6bp3L+XXspEE3plFVskO`$1X&RD6XodFkfK16*3cDHp|ns3p&}X3PrQ7a=ejHI z3Vc?5#;G4I7gqC=!e>1vDTzYtN(!hC3xz&+n|?;YP8S4aIswix*s9Rx=8Dd~9{0A` z%peOp>tgjHAk}Tz+v4?97*{y(C-iBrK2fpS7kVLlhNL$$VPcK+k0O#VpC$#g5P)Fe z3BL7|XoZ9S$Io*~fkQ9^gm+lL9I;Tr-vVI;@}Kpb#J7z98U3k0Ef8eAnz1&}jVJZR zK1(Bs%DBZ;t+a3jRj2V+{*%JEI?Y61*jZ`Wu>24@wV?Eq!W=511mdCwLZT(1&Ger* zq<^rc3ANgpWtoSyfW>{bqRrD!yhgvxbDb5*zBSIDTJ@=d&|eT#VO*WXahHUIn!__u z99O5A>}zm7Va-gHghhv{oKJMmEVfBjtO@)neOlEg&4LhU2z-K{$ijM623Dw^WL-nW zG~qUWQd}<)AZ$_FGPTgy!-6@m!m+!HYx>+Lj8Cc8k~cI|`@)S4)z2HB>f6pw3ZM1d zCo1mj`)u+v4HeJlh5F}b_)oA`{rUO$CyYDQiR6t9RnJ3s2tqYCK9T&JhO4}pg*L@M zuekH>&))u-hI+J7>Es_a)O$01{UTAWU#wPLyotJbb(H@aqRPPSYR&CeG*sL(6GD;& zJn2sUX+wRX&BtX$Z(t66hUz^CpQS$`8v>#X{3QQbAMd23YH%v8KuQaG=O(dX@kAB+ z+-W*}7H_ST7LY_t)j3!}JSnxQkbOPQZC1TtA@S*OeP@OAZgc9a3M!1t2=sWjCF;W! zE6W6Rn^SL9$ilc#PfoU0Ulv%=Sifwlbf97W&&N9jDdliLfd$tW`t*~+XJPDlYXkcu z)@Zew!B1Gf^5c3=8q@TP6)n&kzzqQq{Exyv%|swfD=~E?AxUX_rHP{u#UJRGEZ46w zYKBeMPx5Ul0t@8o-dS~B&#}-mv(Ef@#RBT?nN?>N-_umF?$H00_|vWR4{PBQTW9(L zo~YtSVP{)X5G*VNB^=7|XDR(8-^l8^V`hW;MB~gf3+5*pKb7uE&TJUKlhS)W-ZS$y z^V5NP*;No$^w|EB!e?2HorveuId}U)eL<*mo+~t=U_dD8+A*^r=qJQaifzKipU`XG z4TPr!#yq7VbvjXoF^L#SK|Ze${7o)XP%5@w!4|G zj5`XQ=)T(hwfj%^ZW(yagMg=gf8@vyx4!=NO$Hl3=Yc=`(D9dk+4J|6Ykz5QC=Wd6 z*o9YaeE!|dTXz_I_T0^n?;Ja~aCOf&H~)OsbAs?K`DMIMjI8It3@r7L%#lPP0dh${&50thzFIg2sS3MhUVgsii$k?zlqJCYr78yYFxI~#x8`9Zg zr(HUcdK}RS8Z-~BwxHGn#!no`u!EaKrf5W!z>=A|m)&`_in{CtnswQ7&=2t+2op$lm;;;Et1FP@wDJ~P*ZuNKSq@@&G}uYAD&8ys8^bms^Ceb;g4RnQvwmop z4#;YIs7^nYz=o@9R&>@I&G~BW{Sr_%hJmEQa8ev#v?CcTupF}rYh5k?Zx zXk)R8flp|Q=cIIiCv7pM(KuB*l{%%#c%&_klb8icVIZ_)q@&n5UtYX;8hlM}9vG5_ z%6COPLCobXfGrU{gz9FpHW?(n8uKXIVMB{mMiSr zbDmEtYjjEOk3UKeu0>yUo{ryBQ^w*sk*JuF{+u`xR8>2Pmn)AMi>`_5BEH#~qoLBn z2uml7~GKwb93{{0g9HUZ7mU+1$EyoV9Ayrp(u(Y{> zprQ|uNDM1>-6VG7aqJx!#_{8rz$(J}hESOWZ3}koG9=2@xzSY(qt+&*%LtRuaxZ!Xy6B*%F=?1Mt_e zwxHi3BhZ&jQgf$8PvqN_u_SI85^{ObX6~}8B+<&m1X{n-_*rtCD42qGT<9;T5)r;U z!-}8t02Vmpg4|9gTrNKj(Arq>H1-avfxuA4b6ye2A1@QoBQSH+yd75Y?7CK$ABi$Oth+QBhF4_M~CJVn#^ zAizJL%Ovp|ith>@7&~z;E2Rm&r0!TtK9~360HpAc3^KfNVN1SXD6_vRTqQ0z4gFqt zF{WvXd{c@fpOsH^`;*%H@thvGcc1*a;dAg_=6x{e9sZx{a%Au8LHu~~wxCIgzQ9dt zy|685@WCJgg9r>FFo?h)0)q$)BJdDLK+l&jBgTA5KEz=docbUFg9r>FFo?h)0)q%V 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