From 5bec7a314e1188258646ead07188696c4106937e Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 11 Sep 2018 07:19:10 +0200 Subject: [PATCH] rework iso14443b device functions * hf_read_rx_xcorr.v: transfer i/q pair in one 16bit frame * hi_read_tx.v: invert ssp_dout. When nothing is transferred (ssp_dout=0), this results in no modulation (carrier on) * adjust arm sources accordingly * iso14443b.c: switch off carrier after hf 14b sri512read and hf 14b srix4kread * iso14443b.c: fix DMA circular buffer handling --- armsrc/fpgaloader.c | 16 ++- armsrc/fpgaloader.h | 2 +- armsrc/hfsnoop.c | 2 +- armsrc/iclass.c | 10 +- armsrc/iso14443a.c | 2 +- armsrc/iso14443b.c | 282 ++++++++++++++++------------------------ armsrc/iso15693.c | 151 +++++++-------------- armsrc/legicrf.c | 45 ++++--- armsrc/lfops.c | 2 +- armsrc/lfsampling.c | 2 +- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 4 +- fpga/hi_read_tx.v | 4 +- 13 files changed, 207 insertions(+), 315 deletions(-) diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 77223bd0..3023bcdd 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -115,7 +115,7 @@ void SetupSpi(int mode) // Set up the synchronous serial port, with the one set of options that we // always use when we are talking to the FPGA. Both RX and TX are enabled. //----------------------------------------------------------------------------- -void FpgaSetupSsc(void) +void FpgaSetupSsc(uint8_t mode) { // First configure the GPIOs, and get ourselves a clock. AT91C_BASE_PIOA->PIO_ASR = @@ -134,11 +134,15 @@ void FpgaSetupSsc(void) // on RX clock rising edge, sampled on falling edge AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); - // 8 bits per transfer, no loopback, MSB first, 1 transfer per sync + // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync // pulse, no output sync - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + if ((mode & 0xe0) == FPGA_MAJOR_MODE_HF_READER_RX_XCORR) { + 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); + } - // clock comes from TK pin, no clock output, outputs change on falling + // 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); @@ -160,9 +164,9 @@ bool FpgaSetupSscDma(uint8_t *buf, int len) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // transfer to this memory address - AT91C_BASE_PDC_SSC->PDC_RCR = len; // transfer this many bytes + AT91C_BASE_PDC_SSC->PDC_RCR = len; // transfer this many frames AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address - AT91C_BASE_PDC_SSC->PDC_RNCR = len; // ... with same number of bytes + AT91C_BASE_PDC_SSC->PDC_RNCR = len; // ... with same number of frames AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // go! return true; diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index fa16771d..f75dfc81 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -19,7 +19,7 @@ void FpgaSendCommand(uint16_t cmd, uint16_t v); void FpgaWriteConfWord(uint8_t v); void FpgaDownloadAndGo(int bitstream_version); -void FpgaSetupSsc(void); +void FpgaSetupSsc(uint8_t mode); void SetupSpi(int mode); bool FpgaSetupSscDma(uint8_t *buf, int len); void Fpga_print_status(); diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index d06af443..e492c474 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -37,7 +37,7 @@ void HfSnoop(int samplesToSkip, int triggersToSkip) // Select correct configs FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SNOOP); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SNOOP); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index c587f8ea..1591a062 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -676,7 +676,7 @@ void RAMFUNC SnoopIClass(void) Demod.state = DEMOD_UNSYNCD; // Setup for the DMA. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); upTo = dmaBuf; lastRxCounter = DMA_BUFFER_SIZE; FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); @@ -1163,7 +1163,7 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) StartCountSspClk(); // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // To control where we are in the protocol int cmdsRecvd = 0; @@ -1360,7 +1360,7 @@ static int SendIClassAnswer(uint8_t *resp, int respLen, int delay) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR|FPGA_HF_SIMULATOR_MODULATE_424K_8BIT); AT91C_BASE_SSC->SSC_THR = 0x00; - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); while(!BUTTON_PRESS()) { if((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)){ b = AT91C_BASE_SSC->SSC_RHR; (void) b; @@ -1398,7 +1398,7 @@ static void TransmitIClassCommand(const uint8_t *cmd, int len, int *samples, int int c; FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); AT91C_BASE_SSC->SSC_THR = 0x00; - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); if (wait) { @@ -1586,7 +1586,7 @@ void setupIclassReader() clear_trace(); // Setup SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // Start from off (no field generated) // Signal field is off with the appropriate LED LED_D_OFF(); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index a8273e5e..0cacaed9 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1889,7 +1889,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(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 75769859..cb8567fa 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Jonathan Westhues, split Nov 2006 +// piwi 2018 // // 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 @@ -16,8 +17,8 @@ #include "iso14443crc.h" -#define RECEIVE_SAMPLES_TIMEOUT 2000 -#define ISO14443B_DMA_BUFFER_SIZE 256 +#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 ISO14443B_DMA_BUFFER_SIZE 128 // PCB Block number for APDUs static uint8_t pcb_blocknum = 0; @@ -374,7 +375,7 @@ void SimulateIso14443bTag(void) // We need to listen to the high-frequency, peak-detected path. SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); cmdsRecvd = 0; @@ -440,7 +441,7 @@ void SimulateIso14443bTag(void) LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK); AT91C_BASE_SSC->SSC_THR = 0xff; - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // Transmit the response. uint16_t i = 0; @@ -535,60 +536,11 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) #define SUBCARRIER_DETECT_THRESHOLD 8 -// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by abs(ci) + abs(cq) -/* #define CHECK_FOR_SUBCARRIER() { \ - v = ci; \ - if(v < 0) v = -v; \ - if(cq > 0) { \ - v += cq; \ - } else { \ - v -= cq; \ - } \ - } - */ // Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) - - //note: couldn't we just use MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2) from common.h - marshmellow -#define CHECK_FOR_SUBCARRIER() { \ - v = MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2); \ - } - /* - if(ci < 0) { \ - if(cq < 0) { \ // ci < 0, cq < 0 - if (cq < ci) { \ - v = -cq - (ci >> 1); \ - } else { \ - v = -ci - (cq >> 1); \ - } \ - } else { \ // ci < 0, cq >= 0 - if (cq < -ci) { \ - v = -ci + (cq >> 1); \ - } else { \ - v = cq - (ci >> 1); \ - } \ - } \ - } else { \ - if(cq < 0) { \ // ci >= 0, cq < 0 - if (-cq < ci) { \ - v = ci - (cq >> 1); \ - } else { \ - v = -cq + (ci >> 1); \ - } \ - } else { \ // ci >= 0, cq >= 0 - if (cq < ci) { \ - v = ci + (cq >> 1); \ - } else { \ - v = cq + (ci >> 1); \ - } \ - } \ - } \ - } - */ - +#define AMPLITUDE(ci,cq) (MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2)) switch(Demod.state) { case DEMOD_UNSYNCD: - CHECK_FOR_SUBCARRIER(); - if(v > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected + if(AMPLITUDE(ci,cq) > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected Demod.state = DEMOD_PHASE_REF_TRAINING; Demod.sumI = ci; Demod.sumQ = cq; @@ -598,8 +550,7 @@ static RAMFUNC int Handle14443bSamplesDemod(int ci, int cq) case DEMOD_PHASE_REF_TRAINING: if(Demod.posCount < 8) { - CHECK_FOR_SUBCARRIER(); - if (v > SUBCARRIER_DETECT_THRESHOLD) { + if (AMPLITUDE(ci,cq) > SUBCARRIER_DETECT_THRESHOLD) { // set the reference phase (will code a logic '1') by averaging over 32 1/fs. // note: synchronization time > 80 1/fs Demod.sumI += ci; @@ -743,10 +694,11 @@ static void DemodInit(uint8_t *data) */ static void GetSamplesFor14443bDemod(int n, bool quiet) { - int max = 0; + int maxBehindBy = 0; bool gotFrame = false; - int lastRxCounter, ci, cq, samples = 0; - + int lastRxCounter, samples = 0; + int8_t ci, cq; + // Allocate memory from BigBuf for some buffers // free all previous allocations first BigBuf_free(); @@ -755,15 +707,19 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) uint8_t *receivedResponse = BigBuf_malloc(MAX_FRAME_SIZE); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); // Set up the demodulator for tag -> reader responses. DemodInit(receivedResponse); + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) + // Setup and start DMA. + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); - int8_t *upTo = dmaBuf; + uint16_t *upTo = dmaBuf; lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; // Signal field is ON with the appropriate LED: @@ -772,39 +728,40 @@ static void GetSamplesFor14443bDemod(int n, bool quiet) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); for(;;) { - int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; - if(behindBy > max) max = behindBy; - - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1)) > 2) { - ci = upTo[0]; - cq = upTo[1]; - upTo += 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - upTo = dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; - } - lastRxCounter -= 2; - if(lastRxCounter <= 0) { - lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - } - - samples += 2; - - if(Handle14443bSamplesDemod(ci, cq)) { - gotFrame = true; - break; - } + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; } - if(samples > n || gotFrame) { + if(behindBy < 1) continue; + + ci = *upTo >> 8; + cq = *upTo; + upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning + lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers + } + samples++; + + if(Handle14443bSamplesDemod(ci, cq)) { + gotFrame = true; + break; + } + + if(samples > n) { break; } } - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; + FpgaDisableSscDma(); - if (!quiet) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", max, samples, gotFrame, Demod.len, Demod.sumI, Demod.sumQ); + if (!quiet) Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", maxBehindBy, samples, gotFrame, Demod.len, Demod.sumI, Demod.sumQ); //Tracing if (tracing && Demod.len > 0) { uint8_t parity[MAX_PARITY_SIZE]; @@ -820,11 +777,7 @@ static void TransmitFor14443b(void) { int c; - FpgaSetupSsc(); - - while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - } + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); // Signal field is ON with the appropriate Red LED LED_D_ON(); @@ -832,31 +785,15 @@ static void TransmitFor14443b(void) LED_B_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - for(c = 0; c < 10;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - c++; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); - } - c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; + AT91C_BASE_SSC->SSC_THR = ~ToSend[c]; c++; if(c >= ToSendMax) { break; } } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } WDT_HIT(); } LED_B_OFF(); // Finished sending @@ -874,19 +811,14 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) ToSendReset(); - // Establish initial reference level - for(i = 0; i < 40; i++) { - ToSendStuffBit(1); - } // Send SOF for(i = 0; i < 10; i++) { ToSendStuffBit(0); } + ToSendStuffBit(1); + ToSendStuffBit(1); for(i = 0; i < len; i++) { - // Stop bits/EGT - ToSendStuffBit(1); - ToSendStuffBit(1); // Start bit ToSendStuffBit(0); // Data bits @@ -899,19 +831,18 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) } b >>= 1; } - } - // Send EOF - ToSendStuffBit(1); - for(i = 0; i < 10; i++) { - ToSendStuffBit(0); - } - for(i = 0; i < 8; i++) { + // Stop bit ToSendStuffBit(1); } - // And then a little more, to make sure that the last character makes - // it out before we switch to rx mode. - for(i = 0; i < 24; i++) { + // Send EOF + for(i = 0; i < 10; i++) { + ToSendStuffBit(0); + } + ToSendStuffBit(1); + + // ensure that last byte is filled up + for(i = 0; i < 8; i++) { ToSendStuffBit(1); } @@ -951,7 +882,7 @@ int iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *respo // send CodeAndTransmit14443bAsReader(message_frame, message_length + 4); // get response - GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT*100, true); + GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if(Demod.len < 3) { return 0; @@ -1011,7 +942,7 @@ int iso14443b_select_card() void iso14443b_setup() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1019,9 +950,6 @@ void iso14443b_setup() { LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - // Start the timer - StartCountSspClk(); - DemodReset(); UartReset(); } @@ -1047,7 +975,7 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) SpinDelay(200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Now give it time to spin up. // Signal field is on with the appropriate LED @@ -1065,6 +993,8 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) if (Demod.len == 0) { DbpString("No response from tag"); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } else { Dbprintf("Randomly generated Chip ID (+ 2 byte CRC): %02x %02x %02x", @@ -1080,17 +1010,23 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 3) { Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // Check the CRC of the answer: ComputeCrc14443(CRC_14443_B, Demod.output, 1 , &cmd1[2], &cmd1[3]); if(cmd1[2] != Demod.output[1] || cmd1[3] != Demod.output[2]) { DbpString("CRC Error reading select response."); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // Check response from the tag: should be the same UID as the command we just sent: if (cmd1[1] != Demod.output[0]) { Dbprintf("Bad response to SELECT from Tag, aborting: %02x %02x", cmd1[1], Demod.output[0]); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } @@ -1102,6 +1038,8 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 10) { Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // The check the CRC of the answer (use cmd1 as temporary variable): @@ -1131,6 +1069,8 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) GetSamplesFor14443bDemod(RECEIVE_SAMPLES_TIMEOUT, true); if (Demod.len != 6) { // Check if we got an answer from the tag DbpString("Expected 6 bytes from tag, got less..."); + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return; } // The check the CRC of the answer (use cmd1 as temporary variable): @@ -1149,6 +1089,9 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) } i++; } + + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); } @@ -1171,11 +1114,6 @@ void ReadSTMemoryIso14443b(uint32_t dwLast) */ void RAMFUNC SnoopIso14443b(void) { - // We won't start recording the frames that we acquire until we trigger; - // a good trigger condition to get started is probably when we see a - // response from the tag. - int triggered = true; // TODO: set and evaluate trigger condition - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BigBuf_free(); @@ -1183,10 +1121,10 @@ void RAMFUNC SnoopIso14443b(void) set_tracing(true); // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + uint16_t *dmaBuf = (uint16_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE * sizeof(uint16_t)); int lastRxCounter; - int8_t *upTo; - int ci, cq; + uint16_t *upTo; + int8_t ci, cq; int maxBehindBy = 0; // Count of samples received so far, so that we can include timing @@ -1211,7 +1149,7 @@ void RAMFUNC SnoopIso14443b(void) SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup for the DMA. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); upTo = dmaBuf; lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE); @@ -1219,46 +1157,48 @@ void RAMFUNC SnoopIso14443b(void) bool TagIsActive = false; bool ReaderIsActive = false; + // We won't start recording the frames that we acquire until we trigger. + // A good trigger condition to get started is probably when we see a + // reader command + bool triggered = false; // And now we loop, receiving samples. for(;;) { - int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (ISO14443B_DMA_BUFFER_SIZE-1); + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); if(behindBy > maxBehindBy) { maxBehindBy = behindBy; } - if(behindBy < 2) continue; + if(behindBy < 1) continue; - ci = upTo[0]; - cq = upTo[1]; - upTo += 2; - lastRxCounter -= 2; - if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - upTo = dmaBuf; + ci = *upTo>>8; + cq = *upTo; + upTo++; + lastRxCounter--; + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dmaBuf; // start reading the circular buffer from the beginning again lastRxCounter += ISO14443B_DMA_BUFFER_SIZE; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); + break; + } + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; // DMA Next Counter registers WDT_HIT(); - if(behindBy > (9*ISO14443B_DMA_BUFFER_SIZE/10)) { // TODO: understand whether we can increase/decrease as we want or not? - Dbprintf("blew circular buffer! behindBy=%d", behindBy); - break; - } - if(!tracing) { - DbpString("Reached trace limit"); - break; - } if(BUTTON_PRESS()) { DbpString("cancelled"); break; } } - samples += 2; + samples++; if (!TagIsActive) { // no need to try decoding reader data if the tag is sending if(Handle14443bUartBit(ci & 0x01)) { - if(triggered && tracing) { + triggered = true; + if(tracing) { LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, true); } /* And ready to receive another command. */ @@ -1268,7 +1208,8 @@ void RAMFUNC SnoopIso14443b(void) DemodReset(); } if(Handle14443bUartBit(cq & 0x01)) { - if(triggered && tracing) { + triggered = true; + if(tracing) { LogTrace(Uart.output, Uart.byteCnt, samples, samples, parity, true); } /* And ready to receive another command. */ @@ -1280,8 +1221,8 @@ void RAMFUNC SnoopIso14443b(void) ReaderIsActive = (Uart.state > STATE_GOT_FALLING_EDGE_OF_SOF); } - if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time - if(Handle14443bSamplesDemod(ci | 0x01, cq | 0x01)) { + if(!ReaderIsActive && triggered) { // no need to try decoding tag data if the reader is sending or not yet triggered + if(Handle14443bSamplesDemod(ci/2, cq/2)) { //Use samples as a time measurement if(tracing) @@ -1289,8 +1230,6 @@ void RAMFUNC SnoopIso14443b(void) uint8_t parity[MAX_PARITY_SIZE]; LogTrace(Demod.output, Demod.len, samples, samples, parity, false); } - triggered = true; - // And ready to receive another response. DemodReset(); } @@ -1301,7 +1240,6 @@ void RAMFUNC SnoopIso14443b(void) FpgaDisableSscDma(); LEDsoff(); - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; DbpString("Snoop statistics:"); Dbprintf(" Max behind by: %i", maxBehindBy); Dbprintf(" Uart State: %x", Uart.state); @@ -1327,7 +1265,11 @@ void SendRawCommand14443B(uint32_t datalen, uint32_t recv, uint8_t powerfield, u { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + + // switch field on and give tag some time to power up + LED_D_ON(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); + SpinDelay(10); if (datalen){ set_tracing(true); diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index ad6f5cfc..c092b383 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -227,26 +227,14 @@ static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *w { int c; -// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); if(*wait < 10) { *wait = 10; } -// for(c = 0; c < *wait;) { -// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { -// AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! -// c++; -// } -// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { -// volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; -// (void)r; -// } -// WDT_HIT(); -// } - c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; + AT91C_BASE_SSC->SSC_THR = ~cmd[c]; c++; if(c >= len) { break; @@ -300,36 +288,25 @@ static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int * { int c = 0; uint8_t *dest = BigBuf_get_addr(); - int getNext = 0; - - int8_t prev = 0; // NOW READ RESPONSE + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); c = 0; - getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = AMPLITUDE(b, prev); + // tone that the tag AM-modulates. We just want power. + int8_t i = iq >> 8; + int8_t q = iq; + uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; - - if(c >= 4000) { - break; - } - } else { - prev = b; + dest[c++] = r; + + if(c >= 4000) { + break; } - - getNext = !getNext; } } @@ -441,36 +418,26 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int { int c = 0; uint8_t *dest = BigBuf_get_addr(); - int getNext = 0; - - int8_t prev = 0; // NOW READ RESPONSE FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads c = 0; - getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = AMPLITUDE(b, prev); + // tone that the tag AM-modulates. We just want power, + // so abs(I) + abs(Q) is close to what we want. + int8_t i = iq >> 8; + int8_t q = iq; + uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; + dest[c++] = r; - if(c >= BIGBUF_SIZE) { - break; - } - } else { - prev = b; + if(c >= BIGBUF_SIZE) { + break; } - - getNext = !getNext; } } @@ -585,8 +552,6 @@ void AcquireRawAdcSamplesIso15693(void) uint8_t *dest = BigBuf_get_addr(); int c = 0; - int getNext = 0; - int8_t prev = 0; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BuildIdentifyRequest(); @@ -598,13 +563,13 @@ void AcquireRawAdcSamplesIso15693(void) SpinDelay(100); // Now send the command - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_TX); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); c = 0; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; + AT91C_BASE_SSC->SSC_THR = ~ToSend[c]; c++; if(c == ToSendMax+3) { break; @@ -613,32 +578,25 @@ void AcquireRawAdcSamplesIso15693(void) WDT_HIT(); } + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); c = 0; - getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = AMPLITUDE(b, prev); + // tone that the tag AM-modulates. We just want power, + // so abs(I) + abs(Q) is close to what we want. + int8_t i = iq >> 8; + int8_t q = iq; + uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; + dest[c++] = r; - if(c >= 4000) { - break; - } - } else { - prev = b; + if(c >= 4000) { + break; } - - getNext = !getNext; } } } @@ -649,16 +607,14 @@ void RecordRawAdcSamplesIso15693(void) uint8_t *dest = BigBuf_get_addr(); int c = 0; - int getNext = 0; - int8_t prev = 0; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Setup SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -667,33 +623,24 @@ void RecordRawAdcSamplesIso15693(void) FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); c = 0; - getNext = false; for(;;) { if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - + uint16_t iq = AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if(getNext) { - uint8_t r = AMPLITUDE(b, prev); + // tone that the tag AM-modulates. We just want power, + // so abs(I) + abs(Q) is close to what we want. + int8_t i = iq >> 8; + int8_t q = iq; + uint8_t r = AMPLITUDE(i, q); - dest[c++] = r; + dest[c++] = r; - if(c >= 14000) { - break; - } - } else { - prev = b; + if(c >= 14000) { + break; } - - getNext = !getNext; - WDT_HIT(); } } - Dbprintf("fin record"); + Dbprintf("finished recording"); } @@ -714,7 +661,7 @@ void Iso15693InitReader() { SpinDelay(10); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Give the tags time to energize FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); @@ -973,7 +920,7 @@ void ReaderIso15693(uint32_t parameter) SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Setup SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -1075,7 +1022,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) memset(buf, 0x00, 100); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 2a236b6f..499024cd 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -92,11 +92,11 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ // I/O interface abstraction (FPGA -> ARM) //----------------------------------------------------------------------------- -static inline uint8_t rx_byte_from_fpga() { +static inline uint16_t rx_frame_from_fpga() { for(;;) { WDT_HIT(); - // wait for byte be become available in rx holding register + // wait for frame be become available in rx holding register if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { return AT91C_BASE_SSC->SSC_RHR; } @@ -117,33 +117,32 @@ static inline uint8_t rx_byte_from_fpga() { // To reduce CPU time the amplitude is approximated by using linear functions: // am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq)) // -// Note: The SSC receiver is never synchronized the calculation my be performed -// on a I/Q pair from two subsequent correlations, but does not matter. -// // The bit time is 99.1us (21 I/Q pairs). The receiver skips the first 5 samples // and averages the next (most stable) 8 samples. The final 8 samples are dropped // also. // -// The demedulated should be alligned to the bit periode by the caller. This is +// The demedulated should be alligned to the bit period by the caller. This is // done in rx_bit_as_reader and rx_ack_as_reader. static inline bool rx_bit_as_reader() { - int32_t cq = 0; - int32_t ci = 0; + int32_t sum_cq = 0; + int32_t sum_ci = 0; // skip first 5 I/Q pairs for(size_t i = 0; i<5; ++i) { - (int8_t)rx_byte_from_fpga(); - (int8_t)rx_byte_from_fpga(); + (void)rx_frame_from_fpga(); } // sample next 8 I/Q pairs for(size_t i = 0; i<8; ++i) { - cq += (int8_t)rx_byte_from_fpga(); - ci += (int8_t)rx_byte_from_fpga(); + uint16_t iq = rx_frame_from_fpga(); + int8_t ci = iq >> 8; + int8_t cq = iq; + sum_ci += ci; + sum_cq += cq; } // calculate power - int32_t power = (MAX(ABS(ci), ABS(cq)) + (MIN(ABS(ci), ABS(cq)) >> 1)); + int32_t power = (MAX(ABS(sum_ci), ABS(sum_cq)) + MIN(ABS(sum_ci), ABS(sum_cq))/2); // compare average (power / 8) to threshold return ((power >> 3) > INPUT_THRESHOLD); @@ -159,13 +158,13 @@ static inline bool rx_bit_as_reader() { //----------------------------------------------------------------------------- static inline void tx_bit_as_reader(bool bit) { - // insert pause - LOW(GPIO_SSC_DOUT); + // insert pause (modulate carrier, ssp_dout=1) + HIGH(GPIO_SSC_DOUT); last_frame_end += RWD_TIME_PAUSE; while(GET_TICKS < last_frame_end) { }; - HIGH(GPIO_SSC_DOUT); - // return to high, wait for bit periode to end + // return to high (unmodulated carrier, ssp_dout = 0), wait for bit periode to end + LOW(GPIO_SSC_DOUT); last_frame_end += (bit ? RWD_TIME_1 : RWD_TIME_0) - RWD_TIME_PAUSE; while(GET_TICKS < last_frame_end) { }; } @@ -194,11 +193,11 @@ static void tx_frame_as_reader(uint32_t frame, uint8_t len) { legic_prng_forward(1); }; - // add pause to mark end of the frame - LOW(GPIO_SSC_DOUT); + // add pause (modulate carrier, ssp_dout = 1) to mark end of the frame + HIGH(GPIO_SSC_DOUT); last_frame_end += RWD_TIME_PAUSE; while(GET_TICKS < last_frame_end) { }; - HIGH(GPIO_SSC_DOUT); + LOW(GPIO_SSC_DOUT); } static uint32_t rx_frame_as_reader(uint8_t len) { @@ -294,12 +293,12 @@ static void init_reader(bool clear_mem) { LED_D_ON(); // configure SSC with defaults - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // re-claim GPIO_SSC_DOUT as GPIO and enable output AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - HIGH(GPIO_SSC_DOUT); + LOW(GPIO_SSC_DOUT); // init crc calculator crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0); @@ -772,7 +771,7 @@ void LegicRfSimulate(int phase, int frame, int reqresp) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); /* Bitbang the receiver */ diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 911ba8da..2fe49a80 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -1818,7 +1818,7 @@ void Cotag(uint32_t arg0) { SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_ADC); // start clock - 1.5ticks is 1us StartTicks(); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 3b076265..ffbc0da8 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -101,7 +101,7 @@ void LFSetupFPGAForADC(int divisor, bool lf_field) // Give it a bit of time for the resonant antenna to settle. SpinDelay(50); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_ADC); } /** diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 939ba93a54ce00e9425a1156d3900f1af58c4c8e..286e7b984a9537c0b0722b3bd9ad749d573d9199 100644 GIT binary patch literal 42175 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 EKX+8sTmS$7 literal 42175 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 diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index f637abf2..68f78355 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -198,8 +198,8 @@ begin end // set ssp_frame signal for corr_i_cnt = 0..3 and corr_i_cnt = 32..35 - // (send two frames with 8 Bits each) - if(corr_i_cnt[5:2] == 4'b0000 || corr_i_cnt[5:2] == 4'b1000) + // (send one frame with 16 Bits) + if(corr_i_cnt[5:2] == 4'b0000) ssp_frame = 1'b1; else ssp_frame = 1'b0; diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index 756683cd..819f1697 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -42,11 +42,11 @@ begin pwr_hi <= ck_1356megb; pwr_oe1 <= 1'b0; pwr_oe3 <= 1'b0; - pwr_oe4 <= ~ssp_dout; + pwr_oe4 <= ssp_dout; end else begin - pwr_hi <= ck_1356megb & ssp_dout; + pwr_hi <= ck_1356megb & ~ssp_dout; pwr_oe1 <= 1'b0; pwr_oe3 <= 1'b0; pwr_oe4 <= 1'b0;