From 5ea2a24839c71ba6e58af937a392a6204b8b4696 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 24 Mar 2019 18:11:41 +0100 Subject: [PATCH] FPGA changes (#803) * merge hf_rx_xcorr and hf_tx modes into one module with common ssp_clk and ssp_frame * get rid of most of the warnings when compiling the HF verilog sources * refactoring the constants in Verilog sources --- armsrc/appmain.c | 5 +- armsrc/fpgaloader.c | 10 +- armsrc/fpgaloader.h | 30 +-- armsrc/iso14443b.c | 63 ++++--- armsrc/iso14443b.h | 18 +- armsrc/iso15693.c | 138 +++++++------- armsrc/legicrf.c | 16 +- fpga/Makefile | 4 +- fpga/fpga.ucf | 60 +++--- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/fpga_hf.v | 161 +++++++++-------- fpga/hi_get_trace.v | 17 +- fpga/hi_iso14443a.v | 55 +++--- fpga/hi_read_tx.v | 78 -------- fpga/{hi_read_rx_xcorr.v => hi_reader.v} | 221 +++++++++++++---------- fpga/hi_simulate.v | 33 ++-- fpga/hi_sniffer.v | 13 +- 17 files changed, 425 insertions(+), 497 deletions(-) delete mode 100644 fpga/hi_read_tx.v rename fpga/{hi_read_rx_xcorr.v => hi_reader.v} (58%) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 8824847e..926ac52e 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -24,6 +24,7 @@ #include "legicrfsim.h" #include "hitag2.h" #include "hitagS.h" +#include "iso14443b.h" #include "iso15693.h" #include "lfsampling.h" #include "BigBuf.h" @@ -243,7 +244,7 @@ void MeasureAntennaTuningHfOnly(int *vHf) // Let the FPGA drive the high-frequency antenna around 13.56 MHz. LED_A_ON(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); SpinDelay(20); *vHf = AvgAdc_Voltage_HF(); LED_A_OFF(); @@ -285,7 +286,7 @@ void MeasureAntennaTuningHf(void) // Let the FPGA drive the high-frequency antenna around 13.56 MHz. FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); for (;;) { SpinDelay(500); diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index d1d527c4..214f4843 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -130,21 +130,21 @@ void FpgaSetupSsc(uint8_t FPGA_mode) // Now set up the SSC proper, starting from a known state. AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; - // RX clock comes from TX clock, RX starts when TX starts, data changes - // on RX clock rising edge, sampled on falling edge + // RX clock comes from TX clock, RX starts on Transmit Start, + // data and frame signal is sampled on falling edge of RK AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync // pulse, no output sync - if ((FPGA_mode & 0xe0) == FPGA_MAJOR_MODE_HF_READER_RX_XCORR) { + if ((FPGA_mode & 0xe0) == FPGA_MAJOR_MODE_HF_READER) { 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); } // TX clock comes from TK pin, no clock output, outputs change on falling - // edge of TK, sample on rising edge of TK, start on positive-going edge of sync - AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); + // edge of TK, frame sync is sampled on rising edge of TK, start TX on rising edge of TF + AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); // tx framing is the same as the rx framing AT91C_BASE_SSC->SSC_TFMR = AT91C_BASE_SSC->SSC_RFMR; diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 006de8de..42f9ccc6 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -49,12 +49,11 @@ void SetAdcMuxFor(uint32_t whichGpio); #define FPGA_MAJOR_MODE_LF_EDGE_DETECT (1<<5) #define FPGA_MAJOR_MODE_LF_PASSTHRU (2<<5) // HF -#define FPGA_MAJOR_MODE_HF_READER_TX (0<<5) -#define FPGA_MAJOR_MODE_HF_READER_RX_XCORR (1<<5) -#define FPGA_MAJOR_MODE_HF_SIMULATOR (2<<5) -#define FPGA_MAJOR_MODE_HF_ISO14443A (3<<5) -#define FPGA_MAJOR_MODE_HF_SNOOP (4<<5) -#define FPGA_MAJOR_MODE_HF_GET_TRACE (5<<5) +#define FPGA_MAJOR_MODE_HF_READER (0<<5) +#define FPGA_MAJOR_MODE_HF_SIMULATOR (1<<5) +#define FPGA_MAJOR_MODE_HF_ISO14443A (2<<5) +#define FPGA_MAJOR_MODE_HF_SNOOP (3<<5) +#define FPGA_MAJOR_MODE_HF_GET_TRACE (4<<5) // BOTH #define FPGA_MAJOR_MODE_OFF (7<<5) @@ -66,14 +65,19 @@ void SetAdcMuxFor(uint32_t whichGpio); #define FPGA_LF_EDGE_DETECT_READER_FIELD (1<<0) #define FPGA_LF_EDGE_DETECT_TOGGLE_MODE (1<<1) -// Options for the HF reader, tx to tag -#define FPGA_HF_READER_TX_SHALLOW_MOD (1<<0) +// Options for the HF reader +#define FPGA_HF_READER_MODE_RECEIVE_IQ (0<<0) +#define FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE (1<<0) +#define FPGA_HF_READER_MODE_RECEIVE_PHASE (2<<0) +#define FPGA_HF_READER_MODE_SEND_FULL_MOD (3<<0) +#define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD (4<<0) +#define FPGA_HF_READER_MODE_SNOOP_IQ (5<<0) +#define FPGA_HF_READER_MODE_SNOOP_AMPLITUDE (6<<0) +#define FPGA_HF_READER_MODE_SNOOP_PHASE (7<<0) -// Options for the HF reader, correlating against rx from tag -#define FPGA_HF_READER_RX_XCORR_848_KHZ (1<<0) -#define FPGA_HF_READER_RX_XCORR_SNOOP (1<<1) -#define FPGA_HF_READER_RX_XCORR_QUARTER_FREQ (1<<2) -#define FPGA_HF_READER_RX_XCORR_AMPLITUDE (1<<3) +#define FPGA_HF_READER_SUBCARRIER_848_KHZ (0<<3) +#define FPGA_HF_READER_SUBCARRIER_424_KHZ (1<<3) +#define FPGA_HF_READER_SUBCARRIER_212_KHZ (2<<3) // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION (0<<0) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 5e770a77..f276158f 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -18,8 +18,9 @@ #include "string.h" #include "iso14443crc.h" #include "fpgaloader.h" +#include "BigBuf.h" -#define RECEIVE_SAMPLES_TIMEOUT 1000 // TR0 max is 256/fs = 256/(848kHz) = 302us or 64 samples from FPGA. 1000 seems to be much too high? +#define RECEIVE_SAMPLES_TIMEOUT 64 // TR0 max is 256/fs = 256/(848kHz) = 302us or 64 samples from FPGA #define ISO14443B_DMA_BUFFER_SIZE 128 // PCB Block number for APDUs @@ -692,7 +693,7 @@ static void DemodInit(uint8_t *data) * Demodulate the samples we received from the tag, also log to tracebuffer * quiet: set to 'true' to disable debug output */ -static void GetSamplesFor14443bDemod(int n, bool quiet) +static void GetSamplesFor14443bDemod(int timeout, bool quiet) { int maxBehindBy = 0; bool gotFrame = false; @@ -716,7 +717,7 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) // Setup and start DMA. - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); uint16_t *upTo = dmaBuf; @@ -725,7 +726,7 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) // Signal field is ON with the appropriate LED: LED_D_ON(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); for(;;) { int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); @@ -754,7 +755,8 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) break; } - if(samples > n) { + if(samples > timeout && Demod.state < DEMOD_PHASE_REF_TRAINING) { + LED_C_OFF(); break; } } @@ -774,28 +776,21 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) //----------------------------------------------------------------------------- static void TransmitFor14443b(void) { - int c; - - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); - - // Signal field is ON with the appropriate Red LED - LED_D_ON(); - // Signal we are transmitting with the Green LED + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); LED_B_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - - c = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ~ToSend[c]; - c++; - if(c >= ToSendMax) { - break; - } + for(int c = 0; c < ToSendMax; c++) { + uint8_t data = ToSend[c]; + for (int i = 0; i < 8; i++) { + uint16_t send_word = (data & 0x80) ? 0x0000 : 0xffff; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + data <<= 1; } WDT_HIT(); } - LED_B_OFF(); // Finished sending + LED_B_OFF(); } @@ -942,13 +937,13 @@ int iso14443b_select_card() void iso14443b_setup() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Signal field is on with the appropriate LED LED_D_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); DemodReset(); UartReset(); @@ -976,12 +971,12 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) SpinDelay(200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Now give it time to spin up. // Signal field is on with the appropriate LED LED_D_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); SpinDelay(200); clear_trace(); @@ -1143,15 +1138,15 @@ void RAMFUNC SnoopIso14443b(void) Dbprintf(" tag -> Reader: %i bytes", MAX_FRAME_SIZE); Dbprintf(" DMA: %i bytes", ISO14443B_DMA_BUFFER_SIZE); - // Signal field is off, no reader signal, no tag signal - LEDsoff(); + // Signal field is off + LED_D_OFF(); // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_SNOOP_IQ); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); upTo = dmaBuf; lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); @@ -1255,12 +1250,14 @@ void RAMFUNC SnoopIso14443b(void) */ void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, uint8_t data[]) { + LED_A_ON(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // switch field on and give tag some time to power up LED_D_ON(); - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); SpinDelay(10); if (datalen){ @@ -1282,5 +1279,7 @@ void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, u FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); } + + LED_A_OFF(); } diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index de6faa92..3326ab12 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -10,12 +10,18 @@ // Routines to support ISO 14443 type B. //----------------------------------------------------------------------------- -#ifndef __ISO14443B_H -#define __ISO14443B_H -#include "common.h" +#ifndef ISO14443B_H__ +#define ISO14443B_H__ -int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response); -void iso14443b_setup(); -int iso14443b_select_card(); +#include +#include + +extern int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response); +extern void iso14443b_setup(); +extern int iso14443b_select_card(); +extern void SimulateIso14443bTag(void); +extern void ReadSTMemoryIso14443b(uint32_t); +extern void SnoopIso14443b(void); +extern void SendRawCommand14443B(uint32_t, uint32_t, uint8_t, uint8_t[]); #endif /* __ISO14443B_H */ diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index d988e2b9..e3524375 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -80,11 +80,14 @@ static int DEBUG = 0; #define ISO15693_MAX_RESPONSE_LENGTH 36 // allows read single block with the maximum block size of 256bits. Read multiple blocks not supported yet #define ISO15693_MAX_COMMAND_LENGTH 45 // allows write single block with the maximum block size of 256bits. Write multiple blocks not supported yet -// timing. Delays in SSP_CLK ticks. -#define DELAY_READER_TO_ARM 8 -#define DELAY_ARM_TO_READER 1 -#define DELAY_ISO15693_VCD_TO_VICC 132 // 132/423.75kHz = 311.5us from end of EOF to start of tag response -#define DELAY_ISO15693_VICC_TO_VCD 1017 // 1017/3.39MHz = 300us between end of tag response and next reader command +// timing. Delays in SSP_CLK ticks. +// SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag +#define DELAY_READER_TO_ARM_SIM 8 +#define DELAY_ARM_TO_READER_SIM 1 +#define DELAY_ISO15693_VCD_TO_VICC_SIM 132 // 132/423.75kHz = 311.5us from end of command EOF to start of tag response +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader +#define DELAY_ISO15693_VCD_TO_VICC_READER 1056 // 1056/3,39MHz = 311.5us from end of command EOF to start of tag response +#define DELAY_ISO15693_VICC_TO_VCD_READER 1017 // 1017/3.39MHz = 300us between end of tag response and next reader command // --------------------------- // Signal Processing @@ -269,22 +272,28 @@ static void CodeIso15693AsTag(uint8_t *cmd, int n) // Transmit the command (to the tag) that was placed in cmd[]. static void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t start_time) { - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); - while (GetCountSspClk() < start_time); + while (GetCountSspClk() < start_time) ; LED_B_ON(); - for(int c = 0; c < len; ) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ~cmd[c]; - c++; - } - WDT_HIT(); - } + for(int c = 0; c < len; c++) { + uint8_t data = cmd[c]; + for (int i = 0; i < 8; i++) { + uint16_t send_word = (data & 0x80) ? 0x0000 : 0xffff; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + data <<= 1; + } + WDT_HIT(); + } LED_B_OFF(); } + //----------------------------------------------------------------------------- // Transmit the tag response (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- @@ -562,10 +571,10 @@ static int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, int tim while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_AMPLITUDE); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); // Setup and start DMA. - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); uint16_t *upTo = dmaBuf; @@ -927,7 +936,7 @@ static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint3 for (int i = 7; i >= 0; i--) { if (Handle15693SampleFromReader((b >> i) & 0x01, &DecodeReader)) { - *eof_time = bit_time + samples - DELAY_READER_TO_ARM; // end of EOF + *eof_time = bit_time + samples - DELAY_READER_TO_ARM_SIM; // end of EOF gotFrame = true; break; } @@ -954,7 +963,7 @@ static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint3 samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); if (DecodeReader.byteCount > 0) { - LogTrace(DecodeReader.output, DecodeReader.byteCount, 0, 0, NULL, true); + LogTrace(DecodeReader.output, DecodeReader.byteCount, 0, *eof_time, NULL, true); } return DecodeReader.byteCount; @@ -997,34 +1006,23 @@ void AcquireRawAdcSamplesIso15693(void) uint8_t *dest = BigBuf_get_addr(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - BuildIdentifyRequest(); - + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + BuildIdentifyRequest(); + // Give the tags time to energize LED_D_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(100); // Now send the command - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - - LED_B_ON(); - for(int c = 0; c < ToSendMax; ) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ~ToSend[c]; - c++; - } - WDT_HIT(); - } - LED_B_OFF(); + TransmitTo15693Tag(ToSend, ToSendMax, 0); // wait for last transfer to complete - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) ; - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_AMPLITUDE); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); for(int c = 0; c < 4000; ) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { @@ -1047,7 +1045,6 @@ void SnoopIso15693(void) clear_trace(); set_tracing(true); - // The DMA buffer, used to stream samples from the FPGA uint16_t* dmaBuf = (uint16_t*)BigBuf_malloc(ISO15693_DMA_BUFFER_SIZE*sizeof(uint16_t)); uint16_t *upTo; @@ -1072,13 +1069,13 @@ void SnoopIso15693(void) Dbprintf(" tag -> Reader: %i bytes", ISO15693_MAX_RESPONSE_LENGTH); Dbprintf(" DMA: %i bytes", ISO15693_DMA_BUFFER_SIZE * sizeof(uint16_t)); } - Dbprintf("Snoop started. Press button to stop."); + Dbprintf("Snoop started. Press PM3 Button to stop."); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_SNOOP | FPGA_HF_READER_RX_XCORR_AMPLITUDE); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNOOP_AMPLITUDE); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); upTo = dmaBuf; FpgaSetupSscDma((uint8_t*) dmaBuf, ISO15693_DMA_BUFFER_SIZE); @@ -1173,7 +1170,6 @@ void SnoopIso15693(void) // Initialize the proxmark as iso15k reader -// (this might produces glitches that confuse some tags static void Iso15693InitReader() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC @@ -1185,11 +1181,11 @@ static void Iso15693InitReader() { SpinDelay(10); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Give the tags time to energize LED_D_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); SpinDelay(250); } @@ -1284,7 +1280,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t *recv, // Now wait for a response if (recv != NULL) { - answerLen = GetIso15693AnswerFromTag(recv, max_recv_len, DELAY_ISO15693_VCD_TO_VICC * 2); + answerLen = GetIso15693AnswerFromTag(recv, max_recv_len, DELAY_ISO15693_VCD_TO_VICC_READER * 2); } LED_A_OFF(); @@ -1368,10 +1364,10 @@ void SetDebugIso15693(uint32_t debug) { } -//----------------------------------------------------------------------------- -// Simulate an ISO15693 reader, perform anti-collision and then attempt to read a sector +//--------------------------------------------------------------------------------------- +// Simulate an ISO15693 reader, perform anti-collision and then attempt to read a sector. // all demodulation performed in arm rather than host. - greg -//----------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------- void ReaderIso15693(uint32_t parameter) { LEDsoff(); @@ -1388,7 +1384,7 @@ void ReaderIso15693(uint32_t parameter) SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup SSC - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -1396,7 +1392,7 @@ void ReaderIso15693(uint32_t parameter) // Give the tags time to energize LED_D_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); SpinDelay(200); StartCountSspClk(); @@ -1407,10 +1403,10 @@ void ReaderIso15693(uint32_t parameter) // Now send the IDENTIFY command BuildIdentifyRequest(); TransmitTo15693Tag(ToSend, ToSendMax, 0); - + // Now wait for a response - answerLen = GetIso15693AnswerFromTag(answer, sizeof(answer), DELAY_ISO15693_VCD_TO_VICC * 2) ; - uint32_t start_time = GetCountSspClk() + DELAY_ISO15693_VICC_TO_VCD; + answerLen = GetIso15693AnswerFromTag(answer, sizeof(answer), DELAY_ISO15693_VCD_TO_VICC_READER * 2) ; + uint32_t start_time = GetCountSspClk() + DELAY_ISO15693_VICC_TO_VCD_READER; if (answerLen >=12) // we should do a better check than this { @@ -1446,22 +1442,17 @@ void ReaderIso15693(uint32_t parameter) // read all pages if (answerLen >= 12 && DEBUG) { - - // debugptr = BigBuf_get_addr(); - - int i = 0; - while (i < 32) { // sanity check, assume max 32 pages + for (int i = 0; i < 32; i++) { // sanity check, assume max 32 pages BuildReadBlockRequest(TagUID, i); TransmitTo15693Tag(ToSend, ToSendMax, start_time); - int answerLen = GetIso15693AnswerFromTag(answer, sizeof(answer), DELAY_ISO15693_VCD_TO_VICC * 2); - start_time = GetCountSspClk() + DELAY_ISO15693_VICC_TO_VCD; + int answerLen = GetIso15693AnswerFromTag(answer, sizeof(answer), DELAY_ISO15693_VCD_TO_VICC_READER * 2); + start_time = GetCountSspClk() + DELAY_ISO15693_VICC_TO_VCD_READER; if (answerLen > 0) { Dbprintf("READ SINGLE BLOCK %d returned %d octets:", i, answerLen); DbdecodeIso15693Answer(answerLen, answer); Dbhexdump(answerLen, answer, false); if ( *((uint32_t*) answer) == 0x07160101 ) break; // exit on NoPageErr } - i++; } } @@ -1501,7 +1492,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) if ((cmd_len >= 5) && (cmd[0] & ISO15693_REQ_INVENTORY) && (cmd[1] == ISO15693_INVENTORY)) { // TODO: check more flags bool slow = !(cmd[0] & ISO15693_REQ_DATARATE_HIGH); - start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC - DELAY_ARM_TO_READER; + start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM - DELAY_ARM_TO_READER_SIM; TransmitTo15693Reader(ToSend, ToSendMax, start_time, slow); } @@ -1509,6 +1500,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) Dbhexdump(cmd_len, cmd, false); } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); } @@ -1536,7 +1528,7 @@ void BruteforceIso15693Afi(uint32_t speed) data[2] = 0; // mask length datalen = AddCrc(data,3); recvlen = SendDataTag(data, datalen, false, speed, recv, sizeof(recv), 0); - uint32_t start_time = GetCountSspClk() + DELAY_ISO15693_VCD_TO_VICC; + uint32_t start_time = GetCountSspClk() + DELAY_ISO15693_VICC_TO_VCD_READER; WDT_HIT(); if (recvlen>=12) { Dbprintf("NoAFI UID=%s", sprintUID(NULL, &recv[2])); @@ -1553,7 +1545,7 @@ void BruteforceIso15693Afi(uint32_t speed) data[2] = i & 0xFF; datalen = AddCrc(data,4); recvlen = SendDataTag(data, datalen, false, speed, recv, sizeof(recv), start_time); - start_time = GetCountSspClk() + DELAY_ISO15693_VCD_TO_VICC; + start_time = GetCountSspClk() + DELAY_ISO15693_VICC_TO_VCD_READER; WDT_HIT(); if (recvlen >= 12) { Dbprintf("AFI=%i UID=%s", i, sprintUID(NULL, &recv[2])); @@ -1614,8 +1606,8 @@ static void __attribute__((unused)) BuildSysInfoRequest(uint8_t *uid) uint8_t cmd[12]; uint16_t crc; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data + // If we set the Option_Flag in this request, the VICC will respond with the security status of the block + // followed by the block data // one sub-carrier, inventory, 1 slot, fast rate cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit // System Information command code @@ -1645,8 +1637,8 @@ static void __attribute__((unused)) BuildReadMultiBlockRequest(uint8_t *uid) uint8_t cmd[14]; uint16_t crc; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data + // If we set the Option_Flag in this request, the VICC will respond with the security status of the block + // followed by the block data // one sub-carrier, inventory, 1 slot, fast rate cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit // READ Multi BLOCK command code @@ -1679,8 +1671,8 @@ static void __attribute__((unused)) BuildArbitraryRequest(uint8_t *uid,uint8_t C uint8_t cmd[14]; uint16_t crc; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data + // If we set the Option_Flag in this request, the VICC will respond with the security status of the block + // followed by the block data // one sub-carrier, inventory, 1 slot, fast rate cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit // READ BLOCK command code @@ -1714,8 +1706,8 @@ static void __attribute__((unused)) BuildArbitraryCustomRequest(uint8_t uid[], u uint8_t cmd[14]; uint16_t crc; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data + // If we set the Option_Flag in this request, the VICC will respond with the security status of the block + // followed by the block data // one sub-carrier, inventory, 1 slot, fast rate cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit // READ BLOCK command code @@ -1731,7 +1723,7 @@ static void __attribute__((unused)) BuildArbitraryCustomRequest(uint8_t uid[], u cmd[8] = 0x05; cmd[9]= 0xe0; // always e0 (not exactly unique) // Parameter - cmd[10] = 0x05; // for custom codes this must be manufcturer code + cmd[10] = 0x05; // for custom codes this must be manufacturer code cmd[11] = 0x00; // cmd[12] = 0x00; diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 947d6cd5..c848e647 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -152,7 +152,7 @@ static inline void tx_bit(bool bit) { //----------------------------------------------------------------------------- static void tx_frame(uint32_t frame, uint8_t len) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); // wait for next tx timeslot last_frame_end += RWD_FRAME_WAIT; @@ -173,9 +173,7 @@ static void tx_frame(uint32_t frame, uint8_t len) { } static uint32_t rx_frame(uint8_t len) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); // hold sampling until card is expected to respond last_frame_end += TAG_FRAME_WAIT; @@ -196,9 +194,7 @@ static uint32_t rx_frame(uint8_t len) { static bool rx_ack() { // change fpga into rx mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); // hold sampling until card is expected to respond last_frame_end += TAG_FRAME_WAIT; @@ -258,14 +254,12 @@ static int init_card(uint8_t cardtype, legic_card_select_t *p_card) { static void init_reader(bool clear_mem) { // configure FPGA FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); LED_D_ON(); // configure SSC with defaults - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // re-claim GPIO_SSC_DOUT as GPIO and enable output AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; diff --git a/fpga/Makefile b/fpga/Makefile index 70b0b5fe..01b848fb 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -1,11 +1,11 @@ -include ../common/Makefile.common +include ../common/Makefile.common # for $(DETECTED_OS) all: fpga_lf.bit fpga_hf.bit clean: $(DELETE) *.bgn *.drc *.ncd *.ngd *_par.xrpt *-placed.* *-placed_pad.* *_usage.xml xst_hf.srp xst_lf.srp $(DELETE) *.map *.ngc *.xrpt *.pcf *.rbt *_auto_* *.bld *.mrp *.ngm *.unroutes *_summary.xml netlist.lst xst -fpga_hf.ngc: fpga_hf.v fpga.ucf xst_hf.scr util.v hi_simulate.v hi_read_tx.v hi_read_rx_xcorr.v hi_iso14443a.v hi_sniffer.v hi_get_trace.v +fpga_hf.ngc: fpga_hf.v fpga.ucf xst_hf.scr util.v hi_simulate.v hi_reader.v hi_iso14443a.v hi_sniffer.v hi_get_trace.v $(DELETE) $@ $(XILINX_TOOLS_PREFIX)xst -ifn xst_hf.scr diff --git a/fpga/fpga.ucf b/fpga/fpga.ucf index f20e2da0..545ce5d0 100644 --- a/fpga/fpga.ucf +++ b/fpga/fpga.ucf @@ -1,38 +1,38 @@ # See the schematic for the pin assignment. -NET "adc_d<0>" LOC = "P62" ; -NET "adc_d<1>" LOC = "P60" ; -NET "adc_d<2>" LOC = "P58" ; -NET "adc_d<3>" LOC = "P57" ; -NET "adc_d<4>" LOC = "P56" ; -NET "adc_d<5>" LOC = "P55" ; -NET "adc_d<6>" LOC = "P54" ; -NET "adc_d<7>" LOC = "P53" ; -#NET "cross_hi" LOC = "P88" ; -#NET "miso" LOC = "P40" ; +NET "adc_d<0>" LOC = "P62" ; +NET "adc_d<1>" LOC = "P60" ; +NET "adc_d<2>" LOC = "P58" ; +NET "adc_d<3>" LOC = "P57" ; +NET "adc_d<4>" LOC = "P56" ; +NET "adc_d<5>" LOC = "P55" ; +NET "adc_d<6>" LOC = "P54" ; +NET "adc_d<7>" LOC = "P53" ; +NET "cross_hi" LOC = "P88" ; +#NET "miso" LOC = "P40" ; #PACE: Start of Constraints generated by PACE #PACE: Start of PACE I/O Pin Assignments -NET "adc_clk" LOC = "P46" ; -NET "adc_noe" LOC = "P47" ; -NET "ck_1356meg" LOC = "P91" ; -NET "ck_1356megb" LOC = "P93" ; -NET "cross_lo" LOC = "P87" ; -NET "dbg" LOC = "P22" ; -NET "mosi" LOC = "P43" ; -NET "ncs" LOC = "P44" ; -NET "pck0" LOC = "P36" ; -NET "pwr_hi" LOC = "P80" ; -NET "pwr_lo" LOC = "P81" ; -NET "pwr_oe1" LOC = "P82" ; -NET "pwr_oe2" LOC = "P83" ; -NET "pwr_oe3" LOC = "P84" ; -NET "pwr_oe4" LOC = "P86" ; -NET "spck" LOC = "P39" ; -NET "ssp_clk" LOC = "P71" ; -NET "ssp_din" LOC = "P32" ; -NET "ssp_dout" LOC = "P34" ; -NET "ssp_frame" LOC = "P31" ; +NET "adc_clk" LOC = "P46" ; +NET "adc_noe" LOC = "P47" ; +NET "ck_1356meg" LOC = "P91" ; +NET "ck_1356megb" LOC = "P93" ; +NET "cross_lo" LOC = "P87" ; +NET "dbg" LOC = "P22" ; +NET "mosi" LOC = "P43" ; +NET "ncs" LOC = "P44" ; +NET "pck0" LOC = "P36" ; +NET "pwr_hi" LOC = "P80" ; +NET "pwr_lo" LOC = "P81" ; +NET "pwr_oe1" LOC = "P82" ; +NET "pwr_oe2" LOC = "P83" ; +NET "pwr_oe3" LOC = "P84" ; +NET "pwr_oe4" LOC = "P86" ; +NET "spck" LOC = "P39" ; +NET "ssp_clk" LOC = "P71" ; +NET "ssp_din" LOC = "P32" ; +NET "ssp_dout" LOC = "P34" ; +NET "ssp_frame" LOC = "P31" ; #PACE: Start of PACE Area Constraints diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 2ea2c24e76d0e4b64f173d48cf67dc299283e53d..665f7bcbe9df8553e37ddf040bf7406f7b59798e 100644 GIT binary patch literal 42175 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 literal 42175 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 H^0)s7_0XSf diff --git a/fpga/fpga_hf.v b/fpga/fpga_hf.v index ef935260..01c6ebb2 100644 --- a/fpga/fpga_hf.v +++ b/fpga/fpga_hf.v @@ -13,8 +13,47 @@ // iZsh , June 2014 //----------------------------------------------------------------------------- -`include "hi_read_tx.v" -`include "hi_read_rx_xcorr.v" +// Defining modes and options. This must be aligned to the definitions in fpgaloader.h +// Note: the definitions here are without shifts +// Major modes: +`define FPGA_MAJOR_MODE_LF_ADC 0 +`define FPGA_MAJOR_MODE_LF_EDGE_DETECT 1 +`define FPGA_MAJOR_MODE_LF_PASSTHRU 2 +`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_SNOOP 3 +`define FPGA_MAJOR_MODE_HF_GET_TRACE 4 +`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_SUBCARRIER_848_KHZ 0 +`define FPGA_HF_READER_SUBCARRIER_424_KHZ 1 +`define FPGA_HF_READER_SUBCARRIER_212_KHZ 2 + +// 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 + +`include "hi_reader.v" `include "hi_simulate.v" `include "hi_iso14443a.v" `include "hi_sniffer.v" @@ -49,8 +88,8 @@ reg trace_enable; always @(posedge ncs) begin case(shift_reg[15:12]) - 4'b0001: conf_word <= shift_reg[7:0]; // FPGA_CMD_SET_CONFREG - 4'b0010: trace_enable <= shift_reg[0]; // FPGA_CMD_TRACE_ENABLE + 4'b0001: conf_word <= shift_reg[7:0]; // FPGA_CMD_SET_CONFREG + 4'b0010: trace_enable <= shift_reg[0]; // FPGA_CMD_TRACE_ENABLE endcase end @@ -63,26 +102,12 @@ begin end end -wire [2:0] major_mode; -assign major_mode = conf_word[7:5]; - -// For the high-frequency transmit configuration: modulation depth, either -// 100% (just quite driving antenna, steady LOW), or shallower (tri-state -// some fraction of the buffers) -wire hi_read_tx_shallow_modulation = conf_word[0]; - -// For the high-frequency receive correlator: frequency against which to -// correlate. -wire hi_read_rx_xcorr_848 = conf_word[0]; -// and whether to drive the coil (reader) or just short it (snooper) -wire hi_read_rx_xcorr_snoop = conf_word[1]; -// divide subcarrier frequency by 4 -wire hi_read_rx_xcorr_quarter = conf_word[2]; -// send amplitude only instead of ci/cq pair -wire hi_read_rx_xcorr_amplitude = conf_word[3]; +// select module (outputs) based on major mode +wire [2:0] major_mode = conf_word[7:5]; -// For the high-frequency simulated tag: what kind of modulation to use. -wire [2:0] hi_simulate_mod_type = conf_word[2:0]; +// configuring the HF reader +wire [1:0] subcarrier_frequency = conf_word[4:3]; +wire [2:0] minor_mode = conf_word[2:0]; //----------------------------------------------------------------------------- // And then we instantiate the modules corresponding to each of the FPGA's @@ -90,85 +115,71 @@ wire [2:0] hi_simulate_mod_type = conf_word[2:0]; // the output pins. //----------------------------------------------------------------------------- -hi_read_tx ht( - pck0, ck_1356meg, ck_1356megb, - ht_pwr_lo, ht_pwr_hi, ht_pwr_oe1, ht_pwr_oe2, ht_pwr_oe3, ht_pwr_oe4, - adc_d, ht_adc_clk, - ht_ssp_frame, ht_ssp_din, ssp_dout, ht_ssp_clk, - cross_hi, cross_lo, - ht_dbg, - hi_read_tx_shallow_modulation -); - -hi_read_rx_xcorr hrxc( - pck0, ck_1356meg, ck_1356megb, - hrxc_pwr_lo, hrxc_pwr_hi, hrxc_pwr_oe1, hrxc_pwr_oe2, hrxc_pwr_oe3, hrxc_pwr_oe4, - adc_d, hrxc_adc_clk, - hrxc_ssp_frame, hrxc_ssp_din, ssp_dout, hrxc_ssp_clk, - cross_hi, cross_lo, - hrxc_dbg, - hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter, hi_read_rx_xcorr_amplitude +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 ); hi_simulate hs( - pck0, ck_1356meg, ck_1356megb, + 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, - cross_hi, cross_lo, hs_dbg, - hi_simulate_mod_type + minor_mode ); hi_iso14443a hisn( - pck0, ck_1356meg, ck_1356megb, - hisn_pwr_lo, hisn_pwr_hi, hisn_pwr_oe1, hisn_pwr_oe2, hisn_pwr_oe3, hisn_pwr_oe4, + 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, - cross_hi, cross_lo, hisn_dbg, - hi_simulate_mod_type + minor_mode ); hi_sniffer he( - pck0, ck_1356meg, 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, ssp_dout, he_ssp_clk, - cross_hi, cross_lo, - he_dbg, - hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter + 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 ); hi_get_trace gt( - ck_1356megb, - adc_d, trace_enable, major_mode, - gt_ssp_frame, gt_ssp_din, gt_ssp_clk + ck_1356megb, + adc_d, trace_enable, major_mode, + gt_ssp_frame, gt_ssp_din, gt_ssp_clk ); // Major modes: -// 000 -- HF reader, transmitting to tag; modulation depth selectable -// 001 -- HF reader, receiving from tag, correlating as it goes; frequency selectable -// 010 -- HF simulated tag -// 011 -- HF ISO14443-A -// 100 -- HF Snoop -// 101 -- HF get trace +// 000 -- HF reader; subcarrier frequency and modulation depth selectable +// 001 -- HF simulated tag +// 010 -- HF ISO14443-A +// 011 -- HF Snoop +// 100 -- HF get trace // 111 -- everything off -mux8 mux_ssp_clk (major_mode, ssp_clk, ht_ssp_clk, hrxc_ssp_clk, hs_ssp_clk, hisn_ssp_clk, he_ssp_clk, gt_ssp_clk, 1'b0, 1'b0); -mux8 mux_ssp_din (major_mode, ssp_din, ht_ssp_din, hrxc_ssp_din, hs_ssp_din, hisn_ssp_din, he_ssp_din, gt_ssp_din, 1'b0, 1'b0); -mux8 mux_ssp_frame (major_mode, ssp_frame, ht_ssp_frame, hrxc_ssp_frame, hs_ssp_frame, hisn_ssp_frame, he_ssp_frame, gt_ssp_frame, 1'b0, 1'b0); -mux8 mux_pwr_oe1 (major_mode, pwr_oe1, ht_pwr_oe1, hrxc_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, he_pwr_oe1, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_oe2 (major_mode, pwr_oe2, ht_pwr_oe2, hrxc_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, he_pwr_oe2, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_oe3 (major_mode, pwr_oe3, ht_pwr_oe3, hrxc_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, he_pwr_oe3, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_oe4 (major_mode, pwr_oe4, ht_pwr_oe4, hrxc_pwr_oe4, hs_pwr_oe4, hisn_pwr_oe4, he_pwr_oe4, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_lo (major_mode, pwr_lo, ht_pwr_lo, hrxc_pwr_lo, hs_pwr_lo, hisn_pwr_lo, he_pwr_lo, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_hi (major_mode, pwr_hi, ht_pwr_hi, hrxc_pwr_hi, hs_pwr_hi, hisn_pwr_hi, he_pwr_hi, 1'b0, 1'b0, 1'b0); -mux8 mux_adc_clk (major_mode, adc_clk, ht_adc_clk, hrxc_adc_clk, hs_adc_clk, hisn_adc_clk, he_adc_clk, 1'b0, 1'b0, 1'b0); -mux8 mux_dbg (major_mode, dbg, ht_dbg, hrxc_dbg, hs_dbg, hisn_dbg, he_dbg, 1'b0, 1'b0, 1'b0); +mux8 mux_ssp_clk (major_mode, ssp_clk, hr_ssp_clk, hs_ssp_clk, hisn_ssp_clk, he_ssp_clk, gt_ssp_clk, 1'b0, 1'b0, 1'b0); +mux8 mux_ssp_din (major_mode, ssp_din, hr_ssp_din, hs_ssp_din, hisn_ssp_din, he_ssp_din, gt_ssp_din, 1'b0, 1'b0, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, hr_ssp_frame, hs_ssp_frame, hisn_ssp_frame, he_ssp_frame, gt_ssp_frame, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe1 (major_mode, pwr_oe1, hr_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, he_pwr_oe1, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe2 (major_mode, pwr_oe2, hr_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, he_pwr_oe2, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe3 (major_mode, pwr_oe3, hr_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, he_pwr_oe3, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe4 (major_mode, pwr_oe4, hr_pwr_oe4, hs_pwr_oe4, hisn_pwr_oe4, he_pwr_oe4, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_lo (major_mode, pwr_lo, hr_pwr_lo, hs_pwr_lo, hisn_pwr_lo, he_pwr_lo, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_hi (major_mode, pwr_hi, hr_pwr_hi, hs_pwr_hi, hisn_pwr_hi, he_pwr_hi, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_adc_clk (major_mode, adc_clk, hr_adc_clk, hs_adc_clk, hisn_adc_clk, he_adc_clk, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_dbg (major_mode, dbg, hr_dbg, hs_dbg, hisn_dbg, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); // In all modes, let the ADC's outputs be enabled. assign adc_noe = 1'b0; +// not used +assign miso = 1'b0; + endmodule diff --git a/fpga/hi_get_trace.v b/fpga/hi_get_trace.v index 7acecf80..3e7412f7 100644 --- a/fpga/hi_get_trace.v +++ b/fpga/hi_get_trace.v @@ -14,11 +14,6 @@ module hi_get_trace( input [2:0] major_mode; output ssp_frame, ssp_din, ssp_clk; -// constants for some major_modes: -`define OFF 3'b111 -`define GET_TRACE 3'b101 - - // clock divider reg [6:0] clock_cnt; always @(negedge ck_1356megb) @@ -30,7 +25,7 @@ end reg [2:0] sample_clock; always @(negedge ck_1356megb) begin - if (sample_clock == 3'd3) + if (sample_clock == 3'd7) sample_clock <= 3'd0; else sample_clock <= sample_clock + 1; @@ -45,11 +40,11 @@ reg write_enable2; always @(negedge ck_1356megb) begin previous_major_mode <= major_mode; - if (major_mode == `GET_TRACE) + if (major_mode == `FPGA_MAJOR_MODE_HF_GET_TRACE) begin write_enable1 <= 1'b0; write_enable2 <= 1'b0; - if (previous_major_mode != `GET_TRACE) // just switched into GET_TRACE mode + if (previous_major_mode != `FPGA_MAJOR_MODE_HF_GET_TRACE) // just switched into GET_TRACE mode addr <= start_addr; if (clock_cnt == 7'd0) begin @@ -59,7 +54,7 @@ begin addr <= addr + 1; end end - else if (major_mode != `OFF) + else if (major_mode != `FPGA_MAJOR_MODE_OFF) begin if (trace_enable) begin @@ -92,11 +87,11 @@ begin start_addr <= addr; end end - else // major_mode == `OFF + else // major_mode == `FPGA_MAJOR_MODE_OFF begin write_enable1 <= 1'b0; write_enable2 <= 1'b0; - if (previous_major_mode != `OFF && previous_major_mode != `GET_TRACE) // just switched off + if (previous_major_mode != `FPGA_MAJOR_MODE_OFF && previous_major_mode != `FPGA_MAJOR_MODE_HF_GET_TRACE) // just switched off start_addr <= addr; end end diff --git a/fpga/hi_iso14443a.v b/fpga/hi_iso14443a.v index b1b7b141..e460a2cc 100644 --- a/fpga/hi_iso14443a.v +++ b/fpga/hi_iso14443a.v @@ -3,29 +3,20 @@ // Gerhard de Koning Gans, April 2008 //----------------------------------------------------------------------------- -// constants for the different modes: -`define SNIFFER 3'b000 -`define TAGSIM_LISTEN 3'b001 -`define TAGSIM_MOD 3'b010 -`define READER_LISTEN 3'b011 -`define READER_MOD 3'b100 - module hi_iso14443a( - pck0, ck_1356meg, ck_1356megb, + 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, - cross_hi, cross_lo, dbg, mod_type ); - input pck0, ck_1356meg, ck_1356megb; + 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; - input cross_hi, cross_lo; output dbg; input [2:0] mod_type; @@ -151,7 +142,7 @@ begin end // adjust internal timer counter if necessary: - if (negedge_cnt[3:0] == 4'd13 && (mod_type == `SNIFFER || mod_type == `TAGSIM_LISTEN) && deep_modulation) + if (negedge_cnt[3:0] == 4'd13 && (mod_type == `FPGA_HF_ISO14443A_SNIFFER || mod_type == `FPGA_HF_ISO14443A_TAGSIM_LISTEN) && deep_modulation) begin if (reader_falling_edge_time == 4'd1) // reader signal changes right after sampling. Better sample earlier next time. begin @@ -185,7 +176,7 @@ reg [3:0] mod_detect_reset_time; always @(negedge adc_clk) begin - if (mod_type == `READER_LISTEN) + if (mod_type == `FPGA_HF_ISO14443A_READER_LISTEN) // (our) reader signal changes at negedge_cnt[3:0]=9, tag response expected to start n*16+4 ticks later, further delayed by // 3 ticks ADC conversion. The maximum filter output (edge detected) will be detected after subcarrier zero crossing (+7 ticks). // To allow some timing variances, we want to have the maximum filter outputs well within the detection window, i.e. @@ -195,7 +186,7 @@ begin mod_detect_reset_time <= 4'd4; end else - if (mod_type == `SNIFFER) + if (mod_type == `FPGA_HF_ISO14443A_SNIFFER) begin // detect a rising edge of reader's signal and sync modulation detector to the tag's answer: if (~pre_after_hysteresis && after_hysteresis && deep_modulation) @@ -320,7 +311,7 @@ reg [3:0] sub_carrier_cnt; // response window of 1128 - 774 = 354 ticks. // reset on a pause in listen mode. I.e. the counter starts when the pause is over: -assign fdt_reset = ~after_hysteresis && mod_type == `TAGSIM_LISTEN; +assign fdt_reset = ~after_hysteresis && mod_type == `FPGA_HF_ISO14443A_TAGSIM_LISTEN; always @(negedge adc_clk) begin @@ -363,7 +354,7 @@ reg mod_sig_coil; always @(negedge adc_clk) begin - if (mod_type == `TAGSIM_MOD) // need to take care of proper fdt timing + if (mod_type == `FPGA_HF_ISO14443A_TAGSIM_MOD) // need to take care of proper fdt timing begin if(fdt_counter == `FDT_COUNT) begin @@ -438,7 +429,7 @@ always @(negedge adc_clk) begin if (negedge_cnt[5:0] == 6'd63) // fill the buffer begin - if (mod_type == `SNIFFER) + if (mod_type == `FPGA_HF_ISO14443A_SNIFFER) begin if(deep_modulation) // a reader is sending (or there's no field at all) begin @@ -455,7 +446,7 @@ begin end end - if(negedge_cnt[2:0] == 3'b000 && mod_type == `SNIFFER) // shift at double speed + if(negedge_cnt[2:0] == 3'b000 && mod_type == `FPGA_HF_ISO14443A_SNIFFER) // shift at double speed begin // Don't shift if we just loaded new data, obviously. if(negedge_cnt[5:0] != 6'd0) @@ -464,7 +455,7 @@ begin end end - if(negedge_cnt[3:0] == 4'b0000 && mod_type != `SNIFFER) + if(negedge_cnt[3:0] == 4'b0000 && mod_type != `FPGA_HF_ISO14443A_SNIFFER) begin // Don't shift if we just loaded new data, obviously. if(negedge_cnt[6:0] != 7'd0) @@ -484,8 +475,8 @@ reg ssp_frame; always @(negedge adc_clk) begin - if(mod_type == `SNIFFER) - // SNIFFER mode (ssp_clk = adc_clk / 8, ssp_frame clock = adc_clk / 64)): + if(mod_type == `FPGA_HF_ISO14443A_SNIFFER) + // FPGA_HF_ISO14443A_SNIFFER mode (ssp_clk = adc_clk / 8, ssp_frame clock = adc_clk / 64)): begin if(negedge_cnt[2:0] == 3'd0) ssp_clk <= 1'b1; @@ -505,7 +496,7 @@ begin if(negedge_cnt[3:0] == 4'd8) ssp_clk <= 1'b0; - if(negedge_cnt[6:0] == 7'd7) // ssp_frame rising edge indicates start of frame + if(negedge_cnt[6:0] == 7'd7) // ssp_frame rising edge indicates start of frame, sampled on falling edge of ssp_clk ssp_frame <= 1'b1; if(negedge_cnt[6:0] == 7'd23) ssp_frame <= 1'b0; @@ -525,23 +516,23 @@ begin if(negedge_cnt[3:0] == 4'd0) begin // What do we communicate to the ARM - if(mod_type == `TAGSIM_LISTEN) + if(mod_type == `FPGA_HF_ISO14443A_TAGSIM_LISTEN) sendbit = after_hysteresis; - else if(mod_type == `TAGSIM_MOD) + else if(mod_type == `FPGA_HF_ISO14443A_TAGSIM_MOD) /* if(fdt_counter > 11'd772) sendbit = mod_sig_coil; // huh? else */ sendbit = fdt_indicator; - else if (mod_type == `READER_LISTEN) + else if (mod_type == `FPGA_HF_ISO14443A_READER_LISTEN) sendbit = curbit; else sendbit = 1'b0; end - if(mod_type == `SNIFFER) + if(mod_type == `FPGA_HF_ISO14443A_SNIFFER) // send sampled reader and tag data: bit_to_arm = to_arm[7]; - else if (mod_type == `TAGSIM_MOD && fdt_elapsed && temp_buffer_reset) + else if (mod_type == `FPGA_HF_ISO14443A_TAGSIM_MOD && fdt_elapsed && temp_buffer_reset) // send timing information: bit_to_arm = to_arm[7]; else @@ -554,22 +545,22 @@ end assign ssp_din = bit_to_arm; -// Subcarrier (adc_clk/16, for TAGSIM_MOD only). +// Subcarrier (adc_clk/16, for FPGA_HF_ISO14443A_TAGSIM_MOD only). wire sub_carrier; assign sub_carrier = ~sub_carrier_cnt[3]; -// in READER_MOD: drop carrier for mod_sig_coil==1 (pause); in READER_LISTEN: carrier always on; in other modes: carrier always off -assign pwr_hi = (ck_1356megb & (((mod_type == `READER_MOD) & ~mod_sig_coil) || (mod_type == `READER_LISTEN))); +// in FPGA_HF_ISO14443A_READER_MOD: drop carrier for mod_sig_coil==1 (pause); in FPGA_HF_ISO14443A_READER_LISTEN: carrier always on; in other modes: carrier always off +assign pwr_hi = (ck_1356meg & (((mod_type == `FPGA_HF_ISO14443A_READER_MOD) & ~mod_sig_coil) || (mod_type == `FPGA_HF_ISO14443A_READER_LISTEN))); // Enable HF antenna drivers: assign pwr_oe1 = 1'b0; assign pwr_oe3 = 1'b0; -// TAGSIM_MOD: short circuit antenna with different resistances (modulated by sub_carrier modulated by mod_sig_coil) +// FPGA_HF_ISO14443A_TAGSIM_MOD: short circuit antenna with different resistances (modulated by sub_carrier modulated by mod_sig_coil) // for pwr_oe4 = 1 (tristate): antenna load = 10k || 33 = 32,9 Ohms // for pwr_oe4 = 0 (active): antenna load = 10k || 33 || 33 = 16,5 Ohms -assign pwr_oe4 = mod_sig_coil & sub_carrier & (mod_type == `TAGSIM_MOD); +assign pwr_oe4 = mod_sig_coil & sub_carrier & (mod_type == `FPGA_HF_ISO14443A_TAGSIM_MOD); // This is all LF, so doesn't matter. assign pwr_oe2 = 1'b0; diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v deleted file mode 100644 index 819f1697..00000000 --- a/fpga/hi_read_tx.v +++ /dev/null @@ -1,78 +0,0 @@ -//----------------------------------------------------------------------------- -// The way that we connect things when transmitting a command to an ISO -// 15693 tag, using 100% modulation only for now. -// -// Jonathan Westhues, April 2006 -//----------------------------------------------------------------------------- - -module hi_read_tx( - pck0, ck_1356meg, ck_1356megb, - pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, - adc_d, adc_clk, - ssp_frame, ssp_din, ssp_dout, ssp_clk, - cross_hi, cross_lo, - dbg, - shallow_modulation -); - input pck0, ck_1356meg, ck_1356megb; - output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; - input [7:0] adc_d; - output adc_clk; - input ssp_dout; - output ssp_frame, ssp_din, ssp_clk; - input cross_hi, cross_lo; - output dbg; - input shallow_modulation; - -// low frequency outputs, not relevant -assign pwr_lo = 1'b0; -assign pwr_oe2 = 1'b0; - -// The high-frequency stuff. For now, for testing, just bring out the carrier, -// and allow the ARM to modulate it over the SSP. -reg pwr_hi; -reg pwr_oe1; -reg pwr_oe3; -reg pwr_oe4; - -always @(ck_1356megb or ssp_dout or shallow_modulation) -begin - if(shallow_modulation) - begin - pwr_hi <= ck_1356megb; - pwr_oe1 <= 1'b0; - pwr_oe3 <= 1'b0; - pwr_oe4 <= ssp_dout; - end - else - begin - pwr_hi <= ck_1356megb & ~ssp_dout; - pwr_oe1 <= 1'b0; - pwr_oe3 <= 1'b0; - pwr_oe4 <= 1'b0; - end -end - - -// Then just divide the 13.56 MHz clock down to produce appropriate clocks -// for the synchronous serial port. - -reg [6:0] hi_div_by_128; - -always @(posedge ck_1356meg) - hi_div_by_128 <= hi_div_by_128 + 1; - -assign ssp_clk = hi_div_by_128[6]; - -reg [2:0] hi_byte_div; - -always @(negedge ssp_clk) - hi_byte_div <= hi_byte_div + 1; - -assign ssp_frame = (hi_byte_div == 3'b000); - -assign ssp_din = 1'b0; - -assign dbg = ssp_frame; - -endmodule \ No newline at end of file diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_reader.v similarity index 58% rename from fpga/hi_read_rx_xcorr.v rename to fpga/hi_reader.v index 503c8d67..b0b21e30 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_reader.v @@ -3,35 +3,25 @@ // Jonathan Westhues, April 2006 //----------------------------------------------------------------------------- -module hi_read_rx_xcorr( - pck0, ck_1356meg, ck_1356megb, +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, - cross_hi, cross_lo, dbg, - xcorr_is_848, snoop, xcorr_quarter_freq, hi_read_rx_xcorr_amplitude + subcarrier_frequency, minor_mode ); - input pck0, ck_1356meg, ck_1356megb; + 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; - input cross_hi, cross_lo; output dbg; - input xcorr_is_848, snoop, xcorr_quarter_freq, hi_read_rx_xcorr_amplitude; + input [1:0] subcarrier_frequency; + input [2:0] minor_mode; -// Carrier is steady on through this, unless we're snooping. -assign pwr_hi = ck_1356megb & (~snoop); -assign pwr_oe1 = 1'b0; -assign pwr_oe3 = 1'b0; -assign pwr_oe4 = 1'b0; -// Unused. -assign pwr_lo = 1'b0; -assign pwr_oe2 = 1'b0; - -assign adc_clk = ck_1356megb; // sample frequency is 13,56 MHz +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, @@ -71,7 +61,8 @@ begin corr_i_cnt <= corr_i_cnt + 1; end -// And a couple of registers in which to accumulate the correlations. From the 64 samples + +// 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. @@ -84,18 +75,13 @@ reg signed [13:0] corr_q_accum; reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; -// clock and frame signal for communication to ARM -reg ssp_clk; -reg ssp_frame; - - // 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, 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 @(corr_i_accum or corr_q_accum) +always @(*) begin if (corr_i_accum[13] == 1'b0) abs_ci <= corr_i_accum; @@ -110,15 +96,15 @@ begin if (abs_ci > abs_cq) begin max_ci_cq <= abs_ci; - min_ci_cq <= abs_cq; + min_ci_cq_2 <= abs_cq / 2; end else begin max_ci_cq <= abs_cq; - min_ci_cq <= abs_ci; + min_ci_cq_2 <= abs_ci / 2; end - corr_amplitude <= max_ci_cq + min_ci_cq/2; + corr_amplitude <= max_ci_cq + min_ci_cq_2; end @@ -127,14 +113,14 @@ end reg subcarrier_I; reg subcarrier_Q; -always @(corr_i_cnt or xcorr_is_848 or xcorr_quarter_freq) +always @(*) begin - if (xcorr_is_848 & ~xcorr_quarter_freq) // 848 kHz + 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 (xcorr_is_848 & xcorr_quarter_freq) // 212 kHz + 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]); @@ -155,62 +141,56 @@ begin // resulting amplitude to send out later over the SSP. if(corr_i_cnt == 6'd0) begin - if(snoop) + if (minor_mode == `FPGA_HF_READER_MODE_SNIFF_AMPLITUDE) begin - if (hi_read_rx_xcorr_amplitude) - 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 - else - 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 - end - else + // 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 + 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 - if (hi_read_rx_xcorr_amplitude) - begin - // send amplitude - corr_i_out <= {2'b00, corr_amplitude[13:8]}; - corr_q_out <= corr_amplitude[7:0]; - end - else - 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 - end + // 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; @@ -241,12 +221,8 @@ begin // 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'b10) - ssp_clk <= 1'b0; - if(corr_i_cnt[1:0] == 2'b00) begin - ssp_clk <= 1'b1; // Don't shift if we just loaded new data, obviously. if(corr_i_cnt != 6'd0) begin @@ -255,17 +231,70 @@ begin end end - // set ssp_frame signal for corr_i_cnt = 0..3 - // (send one frame with 16 Bits) - if(corr_i_cnt[5:2] == 4'b0000) - ssp_frame = 1'b1; - else - ssp_frame = 1'b0; +end + + +// ssp clock and frame signal for communication to and from ARM +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'd2) + ssp_frame <= 1'b1; + if (corr_i_cnt == 6'd14) + ssp_frame <= 1'b0; end + assign ssp_din = corr_i_out[7]; + +// 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_SNIFF_IQ + || minor_mode == `FPGA_HF_READER_MODE_SNIFF_AMPLITUDE + || minor_mode == `FPGA_HF_READER_MODE_SNIFF_PHASE) + begin + 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 diff --git a/fpga/hi_simulate.v b/fpga/hi_simulate.v index 8d70bb1b..de58a74e 100644 --- a/fpga/hi_simulate.v +++ b/fpga/hi_simulate.v @@ -16,29 +16,20 @@ // Jonathan Westhues, October 2006 //----------------------------------------------------------------------------- -// possible mod_types: -`define NO_MODULATION 3'b000 -`define MODULATE_BPSK 3'b001 -`define MODULATE_212K 3'b010 -`define MODULATE_424K 3'b100 -`define MODULATE_424K_8BIT 3'b101 - module hi_simulate( - pck0, ck_1356meg, ck_1356megb, + 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, - cross_hi, cross_lo, dbg, mod_type ); - input pck0, ck_1356meg, ck_1356megb; + 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; - input cross_hi, cross_lo; output dbg; input [2:0] mod_type; @@ -49,8 +40,8 @@ assign adc_clk = ck_1356meg; always @(negedge adc_clk) begin - if(& adc_d[7:5]) after_hysteresis = 1'b1; - else if(~(| adc_d[7:5])) after_hysteresis = 1'b0; + if(& adc_d[7:5]) after_hysteresis = 1'b1; // if (adc_d >= 224) + else if(~(| adc_d[7:5])) after_hysteresis = 1'b0; // if (adc_d <= 31) end @@ -65,10 +56,10 @@ reg ssp_clk; always @(negedge adc_clk) begin - if(mod_type == `MODULATE_424K_8BIT) + if(mod_type == `FPGA_HF_SIMULATOR_MODULATE_424K_8BIT) // Get bit every at 53KHz (every 8th carrier bit of 424kHz) ssp_clk <= ssp_clk_divider[7]; - else if(mod_type == `MODULATE_212K) + else if(mod_type == `FPGA_HF_SIMULATOR_MODULATE_212K) // Get next bit at 212kHz ssp_clk <= ssp_clk_divider[5]; else @@ -92,7 +83,7 @@ always @(negedge ssp_clk) reg ssp_frame; always @(ssp_frame_divider_to_arm or ssp_frame_divider_from_arm or mod_type) - if(mod_type == `NO_MODULATION) // not modulating, so listening, to ARM + if(mod_type == `FPGA_HF_SIMULATOR_NO_MODULATION) // not modulating, so listening, to ARM ssp_frame = (ssp_frame_divider_to_arm == 3'b000); else ssp_frame = (ssp_frame_divider_from_arm == 3'b000); @@ -104,14 +95,14 @@ always @(posedge ssp_clk) // Modulating carrier frequency is fc/64 (212kHz) to fc/16 (848kHz). Reuse ssp_clk divider for that. reg modulating_carrier; -always @(mod_type or ssp_clk or ssp_dout) - if (mod_type == `NO_MODULATION) +always @(*) + if (mod_type == `FPGA_HF_SIMULATOR_NO_MODULATION) modulating_carrier <= 1'b0; // no modulation - else if (mod_type == `MODULATE_BPSK) + else if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_BPSK) modulating_carrier <= ssp_dout ^ ssp_clk_divider[3]; // XOR means BPSK - else if (mod_type == `MODULATE_212K) + else if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_212K) modulating_carrier <= ssp_dout & ssp_clk_divider[5]; // switch 212kHz subcarrier on/off - else if (mod_type == `MODULATE_424K || mod_type == `MODULATE_424K_8BIT) + else if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_424K || mod_type == `FPGA_HF_SIMULATOR_MODULATE_424K_8BIT) modulating_carrier <= ssp_dout & ssp_clk_divider[4]; // switch 424kHz modulation on/off else modulating_carrier <= 1'b0; // yet unused diff --git a/fpga/hi_sniffer.v b/fpga/hi_sniffer.v index 3a989ce6..c2dc844a 100644 --- a/fpga/hi_sniffer.v +++ b/fpga/hi_sniffer.v @@ -1,21 +1,14 @@ module hi_sniffer( - pck0, ck_1356meg, ck_1356megb, + 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, - cross_hi, cross_lo, - dbg, - xcorr_is_848, snoop, xcorr_quarter_freq // not used. + ssp_frame, ssp_din, ssp_clk ); - input pck0, ck_1356meg, ck_1356megb; + 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; - input cross_hi, cross_lo; - output dbg; - input xcorr_is_848, snoop, xcorr_quarter_freq; // not used. // We are only snooping, all off. assign pwr_hi = 1'b0; -- 2.39.5