From 7cc204bff881ce1d1833d8e93469f6bbba80c70e Mon Sep 17 00:00:00 2001 From: iZsh Date: Fri, 20 Jun 2014 01:02:59 +0200 Subject: [PATCH] THIS REQUIRES A BOOTROM UPDATE!! To save FPGA area, split the LF and HF bitstreams and load them on-demand. --- .gitignore | 6 +- armsrc/Makefile | 9 +- armsrc/appmain.c | 9 +- armsrc/apps.h | 28 ++- armsrc/fpgaloader.c | 45 +++- armsrc/hitag2.c | 3 + armsrc/iclass.c | 6 + armsrc/iso14443.c | 4 + armsrc/iso14443a.c | 1 + armsrc/iso15693.c | 5 + armsrc/ldscript | 3 +- armsrc/legicrf.c | 2 + armsrc/lfops.c | 12 + armsrc/mifarecmd.c | 506 ++++++++++++++++++++--------------------- client/Makefile | 6 +- common/ldscript.common | 4 +- fpga/Makefile | 40 ++-- fpga/clk_divider.v | 25 ++ fpga/fpga.bit | Bin 42172 -> 0 bytes fpga/fpga.v | 220 ------------------ fpga/fpga_hf.bit | Bin 0 -> 42175 bytes fpga/fpga_hf.v | 150 ++++++++++++ fpga/fpga_lf.bit | Bin 0 -> 42175 bytes fpga/fpga_lf.v | 125 ++++++++++ fpga/go.bat | 52 ++++- fpga/lo_edge_detect.v | 53 +---- fpga/lo_passthru.v | 44 +--- fpga/lo_read.v | 67 ++---- fpga/xst.scr | 1 - fpga/xst_hf.scr | 1 + fpga/xst_lf.scr | 1 + include/proxmark3.h | 4 + 32 files changed, 771 insertions(+), 661 deletions(-) create mode 100644 fpga/clk_divider.v delete mode 100644 fpga/fpga.bit delete mode 100644 fpga/fpga.v create mode 100644 fpga/fpga_hf.bit create mode 100644 fpga/fpga_hf.v create mode 100644 fpga/fpga_lf.bit create mode 100644 fpga/fpga_lf.v delete mode 100644 fpga/xst.scr create mode 100644 fpga/xst_hf.scr create mode 100644 fpga/xst_lf.scr diff --git a/.gitignore b/.gitignore index 3f6347a0d..3b258b3be 100644 --- a/.gitignore +++ b/.gitignore @@ -19,11 +19,13 @@ lua luac fpga/* -!fpga/fpga.bit +!fpga/fpga_lf.bit +!fpga/fpga_hf.bit !fpga/*.v !fpga/Makefile !fpga/fpga.ucf -!fpga/xst.scr +!fpga/xst_lf.scr +!fpga/xst_hf.scr !fpga/go.bat !fpga/sim.tcl diff --git a/armsrc/Makefile b/armsrc/Makefile index 2e5350bb3..e10c10019 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -52,10 +52,13 @@ OBJS = $(OBJDIR)/osimage.s19 $(OBJDIR)/fpgaimage.s19 all: $(OBJS) -$(OBJDIR)/fpga.o: fpga.bit - $(OBJCOPY) -O elf32-littlearm -I binary -B arm --redefine-sym _binary____fpga_fpga_bit_start=_binary_fpga_bit_start --redefine-sym _binary____fpga_fpga_bit_end=_binary_fpga_bit_end --prefix-sections=fpga_bit $^ $@ +$(OBJDIR)/fpga_lf.o: fpga_lf.bit + $(OBJCOPY) -O elf32-littlearm -I binary -B arm --redefine-sym _binary____fpga_fpga_lf_bit_start=_binary_fpga_lf_bit_start --redefine-sym _binary____fpga_fpga_lf_bit_end=_binary_fpga_lf_bit_end --prefix-sections=fpga_lf_bit $^ $@ -$(OBJDIR)/fullimage.elf: $(VERSIONOBJ) $(OBJDIR)/fpga.o $(THUMBOBJ) $(ARMOBJ) +$(OBJDIR)/fpga_hf.o: fpga_hf.bit + $(OBJCOPY) -O elf32-littlearm -I binary -B arm --redefine-sym _binary____fpga_fpga_hf_bit_start=_binary_fpga_hf_bit_start --redefine-sym _binary____fpga_fpga_hf_bit_end=_binary_fpga_hf_bit_end --prefix-sections=fpga_hf_bit $^ $@ + +$(OBJDIR)/fullimage.elf: $(VERSIONOBJ) $(OBJDIR)/fpga_lf.o $(OBJDIR)/fpga_hf.o $(THUMBOBJ) $(ARMOBJ) $(CC) $(LDFLAGS) -Wl,-T,ldscript,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ $(LIBS) $(OBJDIR)/fpgaimage.elf: $(OBJDIR)/fullimage.elf diff --git a/armsrc/appmain.c b/armsrc/appmain.c index b6c322008..b7bc87e70 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -214,6 +214,7 @@ void MeasureAntennaTuning(void) * ( hopefully around 95 if it is tuned to 125kHz!) */ + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); for (i=255; i>19; i--) { WDT_HIT(); @@ -236,6 +237,7 @@ void MeasureAntennaTuning(void) LED_A_ON(); // Let the FPGA drive the high-frequency antenna around 13.56 MHz. + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(20); // Vref = 3300mV, and an 10:1 voltage divider on the input @@ -264,6 +266,7 @@ void MeasureAntennaTuningHf(void) for (;;) { // Let the FPGA drive the high-frequency antenna around 13.56 MHz. + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(20); // Vref = 3300mV, and an 10:1 voltage divider on the input @@ -286,6 +289,7 @@ void SimulateTagHfListen(void) // We're using this mode just so that I can test it out; the simulated // tag mode would work just as well and be simpler. + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); // We need to listen to the high-frequency, peak-detected path. @@ -365,6 +369,7 @@ void SendVersion(void) void SamyRun() { DbpString("Stand-alone mode! No PC necessary."); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); // 3 possible options? no just 2 for now #define OPTS 2 @@ -923,6 +928,7 @@ void UsbPacketReceived(uint8_t *packet, int len) break; case CMD_SET_LF_DIVISOR: + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, c->arg[0]); break; @@ -1017,7 +1023,8 @@ void __attribute__((noreturn)) AppMain(void) AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; // Load the FPGA image, which we have stored in our flash. - FpgaDownloadAndGo(); + // (the HF version by default) + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); StartTickCount(); diff --git a/armsrc/apps.h b/armsrc/apps.h index 92228b8db..76d1247a9 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -67,7 +67,8 @@ extern uint32_t BigBuf[]; /// fpga.h void FpgaSendCommand(uint16_t cmd, uint16_t v); void FpgaWriteConfWord(uint8_t v); -void FpgaDownloadAndGo(void); +void FpgaDownloadAndGo(int bitstream_version); +int FpgaGatherBitstreamVersion(); void FpgaGatherVersion(char *dst, int len); void FpgaSetupSsc(void); void SetupSpi(int mode); @@ -77,17 +78,20 @@ bool FpgaSetupSscDma(uint8_t *buf, int len); void SetAdcMuxFor(uint32_t whichGpio); // Definitions for the FPGA commands. -#define FPGA_CMD_SET_CONFREG (1<<12) -#define FPGA_CMD_SET_DIVISOR (2<<12) +#define FPGA_CMD_SET_CONFREG (1<<12) +#define FPGA_CMD_SET_DIVISOR (2<<12) // Definitions for the FPGA configuration word. -#define FPGA_MAJOR_MODE_LF_READER (0<<5) +// LF +#define FPGA_MAJOR_MODE_LF_READER (0<<5) #define FPGA_MAJOR_MODE_LF_EDGE_DETECT (1<<5) -#define FPGA_MAJOR_MODE_HF_READER_TX (2<<5) -#define FPGA_MAJOR_MODE_HF_READER_RX_XCORR (3<<5) -#define FPGA_MAJOR_MODE_HF_SIMULATOR (4<<5) -#define FPGA_MAJOR_MODE_HF_ISO14443A (5<<5) -#define FPGA_MAJOR_MODE_LF_PASSTHRU (6<<5) -#define FPGA_MAJOR_MODE_OFF (7<<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) +// BOTH +#define FPGA_MAJOR_MODE_OFF (7<<5) // Options for LF_EDGE_DETECT #define FPGA_LF_EDGE_DETECT_READER_FIELD (1<<0) // Options for the HF reader, tx to tag @@ -95,14 +99,14 @@ void SetAdcMuxFor(uint32_t whichGpio); // 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_QUARTER_FREQ (1<<2) // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION (0<<0) #define FPGA_HF_SIMULATOR_MODULATE_BPSK (1<<0) #define FPGA_HF_SIMULATOR_MODULATE_212K (2<<0) #define FPGA_HF_SIMULATOR_MODULATE_424K (4<<0) // Options for ISO14443A -#define FPGA_HF_ISO14443A_SNIFFER (0<<0) +#define FPGA_HF_ISO14443A_SNIFFER (0<<0) #define FPGA_HF_ISO14443A_TAGSIM_LISTEN (1<<0) #define FPGA_HF_ISO14443A_TAGSIM_MOD (2<<0) #define FPGA_HF_ISO14443A_READER_LISTEN (3<<0) diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index d63310a35..2f996bc57 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -252,7 +252,7 @@ static void DownloadFPGA(const char *FpgaImage, int FpgaImageLen, int byterevers static char *bitparse_headers_start; static char *bitparse_bitstream_end; -static int bitparse_initialized; +static int bitparse_initialized = 0; /* Simple Xilinx .bit parser. The file starts with the fixed opaque byte sequence * 00 09 0f f0 0f f0 0f f0 0f f0 00 00 01 * After that the format is 1 byte section type (ASCII character), 2 byte length @@ -322,12 +322,28 @@ int bitparse_find_section(char section_name, char **section_start, unsigned int // Find out which FPGA image format is stored in flash, then call DownloadFPGA // with the right parameters to download the image //----------------------------------------------------------------------------- -extern char _binary_fpga_bit_start, _binary_fpga_bit_end; -void FpgaDownloadAndGo(void) +extern char _binary_fpga_lf_bit_start, _binary_fpga_lf_bit_end; +extern char _binary_fpga_hf_bit_start, _binary_fpga_hf_bit_end; +void FpgaDownloadAndGo(int bitstream_version) { + void *bit_start; + void *bit_end; + + // check whether or not the bitstream is already loaded + if (FpgaGatherBitstreamVersion() == bitstream_version) + return; + + if (bitstream_version == FPGA_BITSTREAM_LF) { + bit_start = &_binary_fpga_lf_bit_start; + bit_end = &_binary_fpga_lf_bit_end; + } else if (bitstream_version == FPGA_BITSTREAM_HF) { + bit_start = &_binary_fpga_hf_bit_start; + bit_end = &_binary_fpga_hf_bit_end; + } else + return; /* Check for the new flash image format: Should have the .bit file at &_binary_fpga_bit_start */ - if(bitparse_init(&_binary_fpga_bit_start, &_binary_fpga_bit_end)) { + if(bitparse_init(bit_start, bit_end)) { /* Successfully initialized the .bit parser. Find the 'e' section and * send its contents to the FPGA. */ @@ -351,6 +367,17 @@ void FpgaDownloadAndGo(void) DownloadFPGA((char*)0x102000, 10524*4, 1); } +int FpgaGatherBitstreamVersion() +{ + char temp[256]; + FpgaGatherVersion(temp, sizeof (temp)); + if (!memcmp("LF", temp, 2)) + return FPGA_BITSTREAM_LF; + else if (!memcmp("HF", temp, 2)) + return FPGA_BITSTREAM_HF; + return FPGA_BITSTREAM_ERR; +} + void FpgaGatherVersion(char *dst, int len) { char *fpga_info; @@ -359,13 +386,15 @@ void FpgaGatherVersion(char *dst, int len) if(!bitparse_find_section('e', &fpga_info, &fpga_info_len)) { strncat(dst, "FPGA image: legacy image without version information", len-1); } else { - strncat(dst, "FPGA image built", len-1); /* USB packets only have 48 bytes data payload, so be terse */ -#if 0 if(bitparse_find_section('a', &fpga_info, &fpga_info_len) && fpga_info[fpga_info_len-1] == 0 ) { - strncat(dst, " from ", len-1); - strncat(dst, fpga_info, len-1); + if (!memcmp("fpga_lf", fpga_info, 7)) + strncat(dst, "LF ", len-1); + else if (!memcmp("fpga_hf", fpga_info, 7)) + strncat(dst, "HF ", len-1); } + strncat(dst, "FPGA image built", len-1); +#if 0 if(bitparse_find_section('b', &fpga_info, &fpga_info_len) && fpga_info[fpga_info_len-1] == 0 ) { strncat(dst, " for ", len-1); strncat(dst, fpga_info, len-1); diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 1a0e9b56a..9181a62ea 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -743,6 +743,7 @@ void SnoopHitag(uint32_t type) { // Set up eavesdropping mode, frequency divisor which will drive the FPGA // and analog mux selection. + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz SetAdcMuxFor(GPIO_MUXSEL_LOPKD); @@ -966,6 +967,7 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { // Set up simulator mode, frequency divisor which will drive the FPGA // and analog mux selection. + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz SetAdcMuxFor(GPIO_MUXSEL_LOPKD); @@ -1124,6 +1126,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { bool bStop; bool bQuitTraceFull = false; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); // Reset the return status bSuccessful = false; diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 8d65b523b..9c5e8b2be 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -689,6 +689,8 @@ void RAMFUNC SnoopIClass(void) // into trace, along with its length and other annotations. //uint8_t *trace = (uint8_t *)BigBuf; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // reset traceLen to 0 iso14a_set_tracing(TRUE); iso14a_clear_trace(); @@ -995,6 +997,8 @@ void SimulateIClass(uint8_t arg0, uint8_t *datain) { uint8_t simType = arg0; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // Enable and clear the trace tracing = TRUE; traceLen = 0; @@ -1426,6 +1430,8 @@ void ReaderIClass(uint8_t arg0) { uint8_t* resp = (((uint8_t *)BigBuf) + 3560); // was 3560 - tied to other size changes + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // Reset trace buffer memset(trace, 0x44, RECV_CMD_OFFSET); traceLen = 0; diff --git a/armsrc/iso14443.c b/armsrc/iso14443.c index 5e8eddd24..7a445bcb8 100644 --- a/armsrc/iso14443.c +++ b/armsrc/iso14443.c @@ -350,6 +350,7 @@ void SimulateIso14443Tag(void) int cmdsRecvd = 0; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); memset(receivedCmd, 0x44, 400); CodeIso14443bAsTag(response1, sizeof(response1)); @@ -867,6 +868,7 @@ void ReadSTMemoryIso14443(uint32_t dwLast) { uint8_t i = 0x00; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Make sure that we start from off, since the tags are stateful; // confusing things will happen if we don't reset them between reads. LED_D_OFF(); @@ -1011,6 +1013,7 @@ void RAMFUNC SnoopIso14443(void) // response from the tag. int triggered = TRUE; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // The command (reader -> tag) that we're working on receiving. uint8_t *receivedCmd = (uint8_t *)(BigBuf) + DEMOD_TRACE_SIZE; // The response (tag -> reader) that we're working on receiving. @@ -1196,6 +1199,7 @@ done: void SendRawCommand14443B(uint32_t datalen, uint32_t recv,uint8_t powerfield, uint8_t data[]) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); if(!powerfield) { // Make sure that we start from off, since the tags are stateful; diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 9afe0788c..099e1d68e 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1763,6 +1763,7 @@ int iso14443a_select_card(byte_t* uid_ptr, iso14a_card_select_t* p_hi14a_card, u } void iso14443a_setup(uint8_t fpga_minor_mode) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port FpgaSetupSsc(); // connect Demodulated Signal to ADC: diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 63e72c14a..ed7beb6fb 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -606,6 +606,7 @@ void AcquireRawAdcSamplesIso15693(void) int8_t prev = 0; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BuildIdentifyRequest(); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -687,6 +688,7 @@ void RecordRawAdcSamplesIso15693(void) int8_t prev = 0; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC FpgaSetupSsc(); @@ -753,6 +755,7 @@ void Iso15693InitReader() { LED_C_OFF(); LED_D_OFF(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC // FpgaSetupSsc(); @@ -1015,6 +1018,7 @@ void ReaderIso15693(uint32_t parameter) // Blank arrays memset(BigBuf + 3660, 0, 300); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC FpgaSetupSsc(); @@ -1165,6 +1169,7 @@ void SimTagIso15693(uint32_t parameter) // Blank arrays memset(answer1, 0, 100); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC FpgaSetupSsc(); diff --git a/armsrc/ldscript b/armsrc/ldscript index dcb04bf09..d0be3b6a0 100644 --- a/armsrc/ldscript +++ b/armsrc/ldscript @@ -21,7 +21,8 @@ ENTRY(Vector) SECTIONS { .fpgaimage : { - *(fpga_bit.data) + *(fpga_lf_bit.data) + *(fpga_hf_bit.data) } >fpgaimage :fpgaimage .start : { diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index f2eb680b4..3fbdf5cba 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -310,6 +310,7 @@ static uint32_t perform_setup_phase_rwd(int iv) } static void LegicCommonInit(void) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaSetupSsc(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); @@ -687,6 +688,7 @@ void LegicRfSimulate(int phase, int frame, int reqresp) legic_frame_drift = frame; legic_reqresp_drift = reqresp; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaSetupSsc(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 76c4b44e7..a0fa870b6 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -17,6 +17,7 @@ void AcquireRawAdcSamples125k(int divisor) { + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); if ( (divisor == 1) || (divisor < 0) || (divisor > 255) ) FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz else if (divisor == 0) @@ -69,6 +70,7 @@ void ModThenAcquireRawAdcSamples125k(int delay_off, int period_0, int period_1, int at134khz; /* Make sure the tag is reset */ + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(2500); @@ -158,6 +160,7 @@ void ReadTItag(void) uint32_t threshold = (sampleslo - sampleshi + 1)>>1; // TI tags charge at 134.2Khz + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz // Place FPGA in passthrough mode, in this mode the CROSS_LO line @@ -365,6 +368,7 @@ void AcquireTiType(void) // if not provided a valid crc will be computed from the data and written. void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) { + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); if(crc == 0) { crc = update_crc16(crc, (idlo)&0xff); crc = update_crc16(crc, (idlo>>8)&0xff); @@ -436,6 +440,7 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) int i; uint8_t *tab = (uint8_t *)BigBuf; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; @@ -602,6 +607,7 @@ void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol) int m=0, n=0, i=0, idx=0, found=0, lastval=0; uint32_t hi2=0, hi=0, lo=0; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); @@ -815,6 +821,7 @@ void CmdIOdemodFSK(int findone, int *high, int *low, int ledcontrol) uint32_t code=0, code2=0; //uint32_t hi2=0, hi=0, lo=0; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); @@ -1132,6 +1139,7 @@ void CmdIOdemodFSK(int findone, int *high, int *low, int ledcontrol) // Write one bit to card void T55xxWriteBit(int bit) { + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); if (bit == 0) @@ -1147,6 +1155,7 @@ void T55xxWriteBlock(uint32_t Data, uint32_t Block, uint32_t Pwd, uint8_t PwdMod { unsigned int i; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); @@ -1191,6 +1200,7 @@ void T55xxReadBlock(uint32_t Block, uint32_t Pwd, uint8_t PwdMode) uint8_t *dest = (uint8_t *)BigBuf; int m=0, i=0; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); m = sizeof(BigBuf); // Clear destination buffer before sending the command memset(dest, 128, m); @@ -1255,6 +1265,7 @@ void T55xxReadTrace(void){ uint8_t *dest = (uint8_t *)BigBuf; int m=0, i=0; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); m = sizeof(BigBuf); // Clear destination buffer before sending the command memset(dest, 128, m); @@ -1970,6 +1981,7 @@ void SendForward(uint8_t fwd_bit_count) { LED_D_ON(); //Field on + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index c934a2802..6a491b532 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -91,66 +91,66 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); // iso14a_set_tracing(TRUE); - -} - -void MifareUReadBlock(uint8_t arg0,uint8_t *datain) -{ - // params - uint8_t blockNo = arg0; - - // variables - byte_t isOK = 0; - byte_t dataoutbuf[16]; - uint8_t uid[10]; - uint32_t cuid; - - // clear trace - iso14a_clear_trace(); + +} + +void MifareUReadBlock(uint8_t arg0,uint8_t *datain) +{ + // params + uint8_t blockNo = arg0; + + // variables + byte_t isOK = 0; + byte_t dataoutbuf[16]; + uint8_t uid[10]; + uint32_t cuid; + + // clear trace + iso14a_clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - - if(mifare_ultra_readblock(cuid, blockNo, dataoutbuf)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Read block error"); - break; - }; - - if(mifare_ultra_halt(cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; - } - - if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED"); - - // add trace trailer - memset(uid, 0x44, 4); - LogTrace(uid, 4, 0, 0, TRUE); - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16); - LED_B_OFF(); - - - // Thats it... - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -} - -//----------------------------------------------------------------------------- -// Select, Authenticaate, Read an MIFARE tag. -// read sector (data = 4 x 16 bytes = 64 bytes) + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + while (true) { + if(!iso14443a_select_card(uid, NULL, &cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + break; + }; + + if(mifare_ultra_readblock(cuid, blockNo, dataoutbuf)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Read block error"); + break; + }; + + if(mifare_ultra_halt(cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + break; + }; + + isOK = 1; + break; + } + + if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED"); + + // add trace trailer + memset(uid, 0x44, 4); + LogTrace(uid, 4, 0, 0, TRUE); + LED_B_ON(); + cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16); + LED_B_OFF(); + + + // Thats it... + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +} + +//----------------------------------------------------------------------------- +// Select, Authenticaate, Read an MIFARE tag. +// read sector (data = 4 x 16 bytes = 64 bytes) //----------------------------------------------------------------------------- void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { @@ -242,72 +242,72 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); // iso14a_set_tracing(TRUE); - -} - -void MifareUReadCard(uint8_t arg0, uint8_t *datain) -{ - // params - uint8_t sectorNo = arg0; - - // variables - byte_t isOK = 0; - byte_t dataoutbuf[16 * 4]; - uint8_t uid[10]; - uint32_t cuid; - - // clear trace - iso14a_clear_trace(); -// iso14a_set_tracing(false); - + +} + +void MifareUReadCard(uint8_t arg0, uint8_t *datain) +{ + // params + uint8_t sectorNo = arg0; + + // variables + byte_t isOK = 0; + byte_t dataoutbuf[16 * 4]; + uint8_t uid[10]; + uint32_t cuid; + + // clear trace + iso14a_clear_trace(); +// iso14a_set_tracing(false); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - for(int sec=0;sec<16;sec++){ - if(mifare_ultra_readblock(cuid, sectorNo * 4 + sec, dataoutbuf + 4 * sec)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Read block %d error",sec); - break; - }; - } - if(mifare_ultra_halt(cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; - } - - if (MF_DBGLEVEL >= 2) DbpString("READ CARD FINISHED"); - - // add trace trailer - memset(uid, 0x44, 4); - LogTrace(uid, 4, 0, 0, TRUE); - - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,64); - //cmd_send(CMD_ACK,isOK,0,0,dataoutbuf+32, 32); - LED_B_OFF(); - - // Thats it... - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -// iso14a_set_tracing(TRUE); - -} - - -//----------------------------------------------------------------------------- -// Select, Authenticaate, Read an MIFARE tag. -// read block + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + while (true) { + if(!iso14443a_select_card(uid, NULL, &cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + break; + }; + for(int sec=0;sec<16;sec++){ + if(mifare_ultra_readblock(cuid, sectorNo * 4 + sec, dataoutbuf + 4 * sec)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Read block %d error",sec); + break; + }; + } + if(mifare_ultra_halt(cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + break; + }; + + isOK = 1; + break; + } + + if (MF_DBGLEVEL >= 2) DbpString("READ CARD FINISHED"); + + // add trace trailer + memset(uid, 0x44, 4); + LogTrace(uid, 4, 0, 0, TRUE); + + LED_B_ON(); + cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,64); + //cmd_send(CMD_ACK,isOK,0,0,dataoutbuf+32, 32); + LED_B_OFF(); + + // Thats it... + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +// iso14a_set_tracing(TRUE); + +} + + +//----------------------------------------------------------------------------- +// Select, Authenticaate, Read an MIFARE tag. +// read block //----------------------------------------------------------------------------- void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { @@ -384,137 +384,137 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); // iso14a_set_tracing(TRUE); - -} - -void MifareUWriteBlock(uint8_t arg0, uint8_t *datain) -{ - // params - uint8_t blockNo = arg0; - byte_t blockdata[16]; - - memset(blockdata,'\0',16); - memcpy(blockdata, datain,16); - - // variables - byte_t isOK = 0; - uint8_t uid[10]; - uint32_t cuid; - - // clear trace - iso14a_clear_trace(); - // iso14a_set_tracing(false); - + +} + +void MifareUWriteBlock(uint8_t arg0, uint8_t *datain) +{ + // params + uint8_t blockNo = arg0; + byte_t blockdata[16]; + + memset(blockdata,'\0',16); + memcpy(blockdata, datain,16); + + // variables + byte_t isOK = 0; + uint8_t uid[10]; + uint32_t cuid; + + // clear trace + iso14a_clear_trace(); + // iso14a_set_tracing(false); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - - if(mifare_ultra_writeblock(cuid, blockNo, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - break; - }; - - if(mifare_ultra_halt(cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; - } - - if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); - - // add trace trailer - memset(uid, 0x44, 4); - LogTrace(uid, 4, 0, 0, TRUE); - - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,0,0); -// UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand)); - LED_B_OFF(); - - - // Thats it... - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -// iso14a_set_tracing(TRUE); - -} - -void MifareUWriteBlock_Special(uint8_t arg0, uint8_t *datain) -{ - // params - uint8_t blockNo = arg0; - byte_t blockdata[4]; - - memcpy(blockdata, datain,4); - - // variables - byte_t isOK = 0; - uint8_t uid[10]; - uint32_t cuid; - - // clear trace - iso14a_clear_trace(); - // iso14a_set_tracing(false); - + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + while (true) { + if(!iso14443a_select_card(uid, NULL, &cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + break; + }; + + if(mifare_ultra_writeblock(cuid, blockNo, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + break; + }; + + if(mifare_ultra_halt(cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + break; + }; + + isOK = 1; + break; + } + + if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); + + // add trace trailer + memset(uid, 0x44, 4); + LogTrace(uid, 4, 0, 0, TRUE); + + LED_B_ON(); + cmd_send(CMD_ACK,isOK,0,0,0,0); +// UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand)); + LED_B_OFF(); + + + // Thats it... + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +// iso14a_set_tracing(TRUE); + +} + +void MifareUWriteBlock_Special(uint8_t arg0, uint8_t *datain) +{ + // params + uint8_t blockNo = arg0; + byte_t blockdata[4]; + + memcpy(blockdata, datain,4); + + // variables + byte_t isOK = 0; + uint8_t uid[10]; + uint32_t cuid; + + // clear trace + iso14a_clear_trace(); + // iso14a_set_tracing(false); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - - if(mifare_ultra_special_writeblock(cuid, blockNo, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - break; - }; - - if(mifare_ultra_halt(cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; - } - - if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); - - // add trace trailer - memset(uid, 0x44, 4); - LogTrace(uid, 4, 0, 0, TRUE); - - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,0,0); -// UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand)); - LED_B_OFF(); - - - // Thats it... - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -// iso14a_set_tracing(TRUE); - -} - -// Return 1 if the nonce is invalid else return 0 -int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, byte_t * parity) { - return ((oddparity((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \ + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + while (true) { + if(!iso14443a_select_card(uid, NULL, &cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + break; + }; + + if(mifare_ultra_special_writeblock(cuid, blockNo, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + break; + }; + + if(mifare_ultra_halt(cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + break; + }; + + isOK = 1; + break; + } + + if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); + + // add trace trailer + memset(uid, 0x44, 4); + LogTrace(uid, 4, 0, 0, TRUE); + + LED_B_ON(); + cmd_send(CMD_ACK,isOK,0,0,0,0); +// UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand)); + LED_B_OFF(); + + + // Thats it... + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +// iso14a_set_tracing(TRUE); + +} + +// Return 1 if the nonce is invalid else return 0 +int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, byte_t * parity) { + return ((oddparity((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \ (oddparity((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \ (oddparity((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0; } diff --git a/client/Makefile b/client/Makefile index 6d75b4bbb..e4a3580b8 100644 --- a/client/Makefile +++ b/client/Makefile @@ -24,8 +24,10 @@ QTLDLIBS = -L$(QTDIR)/lib -lQtCore4 -lQtGui4 MOC = $(QTDIR)/bin/moc LUAPLATFORM = mingw else ifeq ($(platform),Darwin) -CXXFLAGS = -I/Library/Frameworks/QtGui.framework/Versions/Current/Headers -I/Library/Frameworks/QtCore.framework/Versions/Current/Headers -QTLDLIBS = -framework QtGui -framework QtCore +#CXXFLAGS = -I/Library/Frameworks/QtGui.framework/Versions/Current/Headers -I/Library/Frameworks/QtCore.framework/Versions/Current/Headers +#QTLDLIBS = -framework QtGui -framework QtCore +CXXFLAGS = -I$(QTDIR)/include -I$(QTDIR)/include/QtCore -I$(QTDIR)/include/QtGui +QTLDLIBS = -F/opt/local/Library/Frameworks -framework QtGui -framework QtCore MOC = moc LUAPLATFORM = macosx else diff --git a/common/ldscript.common b/common/ldscript.common index 7cde5891d..f1b63550d 100644 --- a/common/ldscript.common +++ b/common/ldscript.common @@ -13,8 +13,8 @@ MEMORY { bootphase1 : ORIGIN = 0x00100000, LENGTH = 0x200 /* Phase 1 bootloader: Copies real bootloader to RAM */ bootphase2 : ORIGIN = 0x00100200, LENGTH = 0x2000 - 0x200 /* Main bootloader code, stored in Flash, executed from RAM */ - fpgaimage : ORIGIN = 0x00102000, LENGTH = 64k - 0x2000 /* Place where the FPGA image will end up */ - osimage : ORIGIN = 0x00110000, LENGTH = 256K - 64k /* Place where the main OS will end up */ + fpgaimage : ORIGIN = 0x00102000, LENGTH = 96k - 0x2000 /* Place where the FPGA image will end up */ + osimage : ORIGIN = 0x00118000, LENGTH = 256K - 96k /* Place where the main OS will end up */ ram : ORIGIN = 0x00200000, LENGTH = 64K - 0x20 /* RAM, minus small common area */ commonarea : ORIGIN = 0x00200000 + 64K - 0x20, LENGTH = 0x20 /* Communication between bootloader and main OS */ } diff --git a/fpga/Makefile b/fpga/Makefile index 12aeaaaec..1aaa9f760 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -1,31 +1,33 @@ include ../common/Makefile.common -all: fpga.ngc fpga.ngd fpga.ncd fpga-placed.ncd fpga.bit +all: fpga_lf.bit fpga_hf.bit clean: - $(DELETE) fpga.bgn fpga.drc fpga.ncd fpga.ngd fpga_par.xrpt fpga-placed.pad fpga-placed.par fpga-placed.xpi fpga_usage.xml xlnx_auto_0.ise xst.srp - $(DELETE) fpga.map fpga.ngc fpga_ngdbuild.xrpt fpga.pcf fpga-placed_pad.csv fpga-placed.ptwx fpga.rbt xlnx_auto_0_xdb - $(DELETE) fpga.bld fpga.mrp fpga.ngc_xst.xrpt fpga.ngm fpga-placed.ncd fpga-placed_pad.txt fpga-placed.unroutes fpga_summary.xml netlist.lst xst + $(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.ngc: fpga.v fpga.ucf xst.scr util.v lo_edge_detect.v lo_read.v lo_passthru.v hi_simulate.v hi_read_tx.v hi_read_rx_xcorr.v hi_iso14443a.v - $(DELETE) fpga.ngc - $(XILINX_TOOLS_PREFIX)xst -ifn xst.scr +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 + $(DELETE) $@ + $(XILINX_TOOLS_PREFIX)xst -ifn xst_hf.scr -fpga.ngd: fpga.ngc - $(DELETE) fpga.ngd - $(XILINX_TOOLS_PREFIX)ngdbuild -aul -p xc2s30-5-vq100 -nt timestamp -uc fpga.ucf fpga.ngc fpga.ngd +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 + $(DELETE) $@ + $(XILINX_TOOLS_PREFIX)xst -ifn xst_lf.scr -fpga.ncd: fpga.ngd - $(DELETE) fpga.ncd - $(XILINX_TOOLS_PREFIX)map -p xc2s30-5-vq100 fpga.ngd +%.ngd: %.ngc + $(DELETE) $@ + $(XILINX_TOOLS_PREFIX)ngdbuild -aul -p xc2s30-5-vq100 -nt timestamp -uc fpga.ucf $< $@ -fpga-placed.ncd: fpga.ncd - $(DELETE) fpga-placed.ncd - $(XILINX_TOOLS_PREFIX)par fpga.ncd fpga-placed.ncd +%.ncd: %.ngd + $(DELETE) $@ + $(XILINX_TOOLS_PREFIX)map -p xc2s30-5-vq100 $< -fpga.bit: fpga-placed.ncd - $(DELETE) fpga.bit fpga.drc fpga.rbt - $(XILINX_TOOLS_PREFIX)bitgen fpga-placed.ncd fpga.bit +%-placed.ncd: %.ncd + $(DELETE) $@ + $(XILINX_TOOLS_PREFIX)par $< $@ +%.bit: %-placed.ncd + $(DELETE) $@ $*.drc $*.rbt + $(XILINX_TOOLS_PREFIX)bitgen $< $@ .PHONY: all clean help help: diff --git a/fpga/clk_divider.v b/fpga/clk_divider.v new file mode 100644 index 000000000..882af5cc8 --- /dev/null +++ b/fpga/clk_divider.v @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2014 iZsh +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +module clk_divider(input clk, input [7:0] divisor, output [7:0] div_cnt, output div_clk); + + reg [7:0] div_cnt_ = 0; + reg div_clk_; + assign div_cnt = div_cnt_; + assign div_clk = div_clk_; + + always @(posedge clk) + begin + if(div_cnt == divisor) begin + div_cnt_ <= 8'd0; + div_clk_ = !div_clk_; + end else + div_cnt_ <= div_cnt_ + 1; + end + +endmodule + diff --git a/fpga/fpga.bit b/fpga/fpga.bit deleted file mode 100644 index e5ea4fcd33b41a65cf776e06d388ebfcad02bff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42172 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 literal 0 HcmV?d00001 diff --git a/fpga/fpga_lf.v b/fpga/fpga_lf.v new file mode 100644 index 000000000..8dea68d75 --- /dev/null +++ b/fpga/fpga_lf.v @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// 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 +//----------------------------------------------------------------------------- + +`include "lo_read.v" +`include "lo_passthru.v" +`include "lo_edge_detect.v" +`include "util.v" +`include "clk_divider.v" + +module fpga_lf( + 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. +//----------------------------------------------------------------------------- + +reg [15:0] shift_reg; +reg [7:0] divisor; +reg [7:0] conf_word; + +// 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]) + 4'b0001: conf_word <= shift_reg[7:0]; // FPGA_CMD_SET_CONFREG + 4'b0010: divisor <= shift_reg[7:0]; // FPGA_CMD_SET_DIVISOR + endcase +end + +always @(posedge spck) +begin + if(~ncs) + begin + shift_reg[15:1] <= shift_reg[14:0]; + shift_reg[0] <= mosi; + end +end + +wire [2:0] major_mode; +assign major_mode = conf_word[7:5]; + +// For the low-frequency configuration: +wire lf_field = conf_word[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. +//----------------------------------------------------------------------------- +wire [7:0] pck_cnt; +wire pck_divclk; +clk_divider div_clk(pck0, divisor, pck_cnt, pck_divclk); + +lo_read lr( + pck0, pck_cnt, pck_divclk, + lr_pwr_lo, lr_pwr_hi, lr_pwr_oe1, lr_pwr_oe2, lr_pwr_oe3, lr_pwr_oe4, + adc_d, lr_adc_clk, + lr_ssp_frame, lr_ssp_din, lr_ssp_clk, + lr_dbg +); + +lo_passthru lp( + pck_divclk, + lp_pwr_lo, lp_pwr_hi, lp_pwr_oe1, lp_pwr_oe2, lp_pwr_oe3, lp_pwr_oe4, + lp_adc_clk, + lp_ssp_din, ssp_dout, + cross_lo, + lp_dbg +); + +lo_edge_detect le( + pck0, pck_cnt, pck_divclk, + le_pwr_lo, le_pwr_hi, le_pwr_oe1, le_pwr_oe2, le_pwr_oe3, le_pwr_oe4, + adc_d, le_adc_clk, + le_ssp_frame, ssp_dout, le_ssp_clk, + cross_lo, + le_dbg, + lf_field +); + +// Major modes: +// 000 -- LF reader (generic) +// 001 -- LF edge detect (generic) +// 010 -- LF passthrough + +mux8 mux_ssp_clk (major_mode, ssp_clk, lr_ssp_clk, le_ssp_clk, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_ssp_din (major_mode, ssp_din, lr_ssp_din, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, lr_ssp_frame, le_ssp_frame, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe1 (major_mode, pwr_oe1, lr_pwr_oe1, le_pwr_oe1, lp_pwr_oe1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe2 (major_mode, pwr_oe2, lr_pwr_oe2, le_pwr_oe2, lp_pwr_oe2, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe3 (major_mode, pwr_oe3, lr_pwr_oe3, le_pwr_oe3, lp_pwr_oe3, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_oe4 (major_mode, pwr_oe4, lr_pwr_oe4, le_pwr_oe4, lp_pwr_oe4, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_lo (major_mode, pwr_lo, lr_pwr_lo, le_pwr_lo, lp_pwr_lo, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_pwr_hi (major_mode, pwr_hi, lr_pwr_hi, le_pwr_hi, lp_pwr_hi, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_adc_clk (major_mode, adc_clk, lr_adc_clk, le_adc_clk, lp_adc_clk, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); +mux8 mux_dbg (major_mode, dbg, lr_dbg, le_dbg, lp_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; + +endmodule diff --git a/fpga/go.bat b/fpga/go.bat index 8600d3cd9..d9704e08d 100644 --- a/fpga/go.bat +++ b/fpga/go.bat @@ -2,37 +2,67 @@ rmdir/s/q xst -del fpga.ngc -xst -ifn xst.scr +del fpga_lf.ngc +xst -ifn xst_lf.scr if errorlevel 0 goto ok1 goto done :ok1 -del fpga.ngd -ngdbuild -aul -p xc2s30-6vq100 -nt timestamp -uc fpga.ucf fpga.ngc fpga.ngd +del fpga_lf.ngd +ngdbuild -aul -p xc2s30-6vq100 -nt timestamp -uc fpga.ucf fpga_lf.ngc fpga_lf.ngd if errorlevel 0 goto ok2 goto done :ok2 -del fpga.ncd -map -p xc2s30-6vq100 fpga.ngd +del fpga_lf.ncd +map -p xc2s30-6vq100 fpga_lf.ngd if errorlevel 0 goto ok3 goto done :ok3 -del fpga-placed.ncd -par fpga.ncd fpga-placed.ncd +del fpga_lf-placed.ncd +par fpga_lf.ncd fpga_lf-placed.ncd if errorlevel 0 goto ok4 goto done :ok4 -del fpga.bit fpga.drc fpga.rbt -bitgen -b fpga-placed.ncd fpga.bit +del fpga_lf.bit fpga_lf.drc fpga_lf.rbt +bitgen -b fpga_lf-placed.ncd fpga_lf.bit if errorlevel 0 goto ok5 goto done :ok5 +del fpga_hf.ngc +xst -ifn xst_hf.scr +if errorlevel 0 goto ok6 +goto done +:ok6 + +del fpga_hf.ngd +ngdbuild -aul -p xc2s30-6vq100 -nt timestamp -uc fpga.ucf fpga_hf.ngc fpga_hf.ngd +if errorlevel 0 goto ok7 +goto done +:ok7 + +del fpga_hf.ncd +map -p xc2s30-6vq100 fpga_hf.ngd +if errorlevel 0 goto ok8 +goto done +:ok8 + +del fpga_hf-placed.ncd +par fpga_hf.ncd fpga_hf-placed.ncd +if errorlevel 0 goto ok9 +goto done +:ok9 + +del fpga_hf.bit fpga_hf.drc fpga_hf.rbt +bitgen -b fpga_hf-placed.ncd fpga_hf.bit +if errorlevel 0 goto ok10 +goto done +:ok10 + echo okay -perl ..\tools\rbt2c.pl fpga.rbt > ..\armsrc\fpgaimg.c +perl ..\tools\rbt2c.pl fpga_lf.rbt > ..\armsrc\fpgaimg.c :done diff --git a/fpga/lo_edge_detect.v b/fpga/lo_edge_detect.v index 8458ee692..af600b833 100644 --- a/fpga/lo_edge_detect.v +++ b/fpga/lo_edge_detect.v @@ -7,34 +7,18 @@ //----------------------------------------------------------------------------- module lo_edge_detect( - 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, - divisor, - lf_field + input pck0, input [7:0] pck_cnt, input pck_divclk, + 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 ssp_frame, input ssp_dout, output ssp_clk, + input cross_lo, + output dbg, + input lf_field ); - 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 [7:0] divisor; - input lf_field; -// Divide the clock to be used for the ADC -reg [7:0] pck_divider; -reg clk_state; - -wire tag_modulation; -assign tag_modulation = ssp_dout & !lf_field; -wire reader_modulation; -assign reader_modulation = !ssp_dout & lf_field & clk_state; +wire tag_modulation = ssp_dout & !lf_field; +wire reader_modulation = !ssp_dout & lf_field & pck_divclk; // No logic, straight through. assign pwr_oe1 = 1'b0; // not used in LF mode @@ -46,20 +30,7 @@ assign pwr_lo = reader_modulation; assign pwr_hi = 1'b0; assign dbg = ssp_frame; -always @(posedge pck0) -begin - if(pck_divider == divisor[7:0]) - begin - pck_divider <= 8'd0; - clk_state = !clk_state; - end - else - begin - pck_divider <= pck_divider + 1; - end -end - -assign adc_clk = ~clk_state; +assign adc_clk = ~pck_divclk; // Toggle the output with hysteresis // Set to high if the ADC value is above 200 @@ -70,7 +41,7 @@ reg output_state; always @(posedge pck0) begin - if((pck_divider == 8'd7) && !clk_state) begin + if((pck_cnt == 8'd7) && !pck_divclk) begin is_high = (adc_d >= 8'd190); is_low = (adc_d <= 8'd70); end diff --git a/fpga/lo_passthru.v b/fpga/lo_passthru.v index 5c59d11ca..933728eb0 100644 --- a/fpga/lo_passthru.v +++ b/fpga/lo_passthru.v @@ -4,42 +4,14 @@ //----------------------------------------------------------------------------- module lo_passthru( - 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, divisor + input pck_divclk, + output pwr_lo, output pwr_hi, + output pwr_oe1, output pwr_oe2, output pwr_oe3, output pwr_oe4, + output adc_clk, + output ssp_din, input ssp_dout, + input cross_lo, + output dbg ); - 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 [7:0] divisor; - -reg [7:0] pck_divider; -reg ant_lo; - -// this task runs on the rising egde of pck0 clock (24Mhz) and creates ant_lo -// which is high for (divisor+1) pck0 cycles and low for the same duration -// ant_lo is therefore a 50% duty cycle clock signal with a frequency of -// 12Mhz/(divisor+1) which drives the antenna as well as the ADC clock adc_clk -always @(posedge pck0) -begin - if(pck_divider == divisor[7:0]) - begin - pck_divider <= 8'd0; - ant_lo = !ant_lo; - end - else - begin - pck_divider <= pck_divider + 1; - end -end // the antenna is modulated when ssp_dout = 1, when 0 the // antenna drivers stop modulating and go into listen mode @@ -47,7 +19,7 @@ assign pwr_oe3 = 1'b0; assign pwr_oe1 = ssp_dout; assign pwr_oe2 = ssp_dout; assign pwr_oe4 = ssp_dout; -assign pwr_lo = ant_lo && ssp_dout; +assign pwr_lo = pck_divclk && ssp_dout; assign pwr_hi = 1'b0; assign adc_clk = 1'b0; assign ssp_din = cross_lo; diff --git a/fpga/lo_read.v b/fpga/lo_read.v index e6f245ca6..f2d79127c 100644 --- a/fpga/lo_read.v +++ b/fpga/lo_read.v @@ -7,65 +7,34 @@ //----------------------------------------------------------------------------- module lo_read( - 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, - lo_is_125khz, divisor + input pck0, input [7:0] pck_cnt, input pck_divclk, + 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 ssp_frame, output ssp_din, output ssp_clk, + output dbg ); - 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 lo_is_125khz; // redundant signal, no longer used anywhere - input [7:0] divisor; reg [7:0] to_arm_shiftreg; -reg [7:0] pck_divider; -reg ant_lo; - -// this task runs on the rising egde of pck0 clock (24Mhz) and creates ant_lo -// which is high for (divisor+1) pck0 cycles and low for the same duration -// ant_lo is therefore a 50% duty cycle clock signal with a frequency of -// 12Mhz/(divisor+1) which drives the antenna as well as the ADC clock adc_clk -always @(posedge pck0) -begin - if(pck_divider == divisor[7:0]) - begin - pck_divider <= 8'd0; - ant_lo = !ant_lo; - end - else - begin - pck_divider <= pck_divider + 1; - end -end // this task also runs at pck0 frequency (24Mhz) and is used to serialize // the ADC output which is then clocked into the ARM SSP. -// because ant_lo always transitions when pck_divider = 0 we use the -// pck_divider counter to sync our other signals off it -// we read the ADC value when pck_divider=7 and shift it out on counts 8..15 +// because pck_divclk always transitions when pck_cnt = 0 we use the +// pck_div counter to sync our other signals off it +// we read the ADC value when pck_cnt=7 and shift it out on counts 8..15 always @(posedge pck0) begin - if((pck_divider == 8'd7) && !ant_lo) - to_arm_shiftreg <= adc_d; - else - begin - to_arm_shiftreg[7:1] <= to_arm_shiftreg[6:0]; + if((pck_cnt == 8'd7) && !pck_divclk) + to_arm_shiftreg <= adc_d; + else begin + to_arm_shiftreg[7:1] <= to_arm_shiftreg[6:0]; // simulation showed a glitch occuring due to the LSB of the shifter // not being set as we shift bits out // this ensures the ssp_din remains low after a transfer and suppresses // the glitch that would occur when the last data shifted out ended in // a 1 bit and the next data shifted out started with a 0 bit - to_arm_shiftreg[0] <= 1'b0; + to_arm_shiftreg[0] <= 1'b0; end end @@ -83,11 +52,11 @@ end // ssp_clk |_| |_| |_| |_| |_| |_| |_| |_| |_| |_ // serialized SSP data is gated by ant_lo to suppress unwanted signal -assign ssp_din = to_arm_shiftreg[7] && !ant_lo; +assign ssp_din = to_arm_shiftreg[7] && !pck_divclk; // SSP clock always runs at 24Mhz assign ssp_clk = pck0; // SSP frame is gated by ant_lo and goes high when pck_divider=8..15 -assign ssp_frame = (pck_divider[7:3] == 5'd1) && !ant_lo; +assign ssp_frame = (pck_cnt[7:3] == 5'd1) && !pck_divclk; // unused signals tied low assign pwr_hi = 1'b0; assign pwr_oe1 = 1'b0; @@ -95,9 +64,9 @@ assign pwr_oe2 = 1'b0; assign pwr_oe3 = 1'b0; assign pwr_oe4 = 1'b0; // this is the antenna driver signal -assign pwr_lo = ant_lo; +assign pwr_lo = pck_divclk; // ADC clock out of phase with antenna driver -assign adc_clk = ~ant_lo; +assign adc_clk = ~pck_divclk; // ADC clock also routed to debug pin assign dbg = adc_clk; endmodule diff --git a/fpga/xst.scr b/fpga/xst.scr deleted file mode 100644 index 406bbeee7..000000000 --- a/fpga/xst.scr +++ /dev/null @@ -1 +0,0 @@ -run -ifn fpga.v -ifmt Verilog -ofn fpga.ngc -ofmt NGC -p xc2s30-5-vq100 -opt_mode Speed -opt_level 1 -ent fpga diff --git a/fpga/xst_hf.scr b/fpga/xst_hf.scr new file mode 100644 index 000000000..dd2fdc859 --- /dev/null +++ b/fpga/xst_hf.scr @@ -0,0 +1 @@ +run -ifn fpga_hf.v -ifmt Verilog -ofn fpga_hf.ngc -ofmt NGC -p xc2s30-5-vq100 -top fpga_hf -opt_mode area -opt_level 2 -resource_sharing yes -fsm_style bram -fsm_encoding compact diff --git a/fpga/xst_lf.scr b/fpga/xst_lf.scr new file mode 100644 index 000000000..2d6c7e951 --- /dev/null +++ b/fpga/xst_lf.scr @@ -0,0 +1 @@ +run -ifn fpga_lf.v -ifmt Verilog -ofn fpga_lf.ngc -ofmt NGC -p xc2s30-5-vq100 -top fpga_lf -opt_mode area -opt_level 2 -resource_sharing yes -fsm_style bram -fsm_encoding compact diff --git a/include/proxmark3.h b/include/proxmark3.h index ce263ca11..8c9417da5 100644 --- a/include/proxmark3.h +++ b/include/proxmark3.h @@ -60,6 +60,10 @@ #define SPI_FPGA_MODE 0 #define SPI_LCD_MODE 1 +#define FPGA_BITSTREAM_ERR 0 +#define FPGA_BITSTREAM_LF 1 +#define FPGA_BITSTREAM_HF 2 + #define TRUE 1 #define FALSE 0