From 6a5d4e17f4afe29f84308fe5ca78782233d7b611 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 16 Sep 2018 00:53:28 +0200 Subject: [PATCH] rework iso14443b device functions including FPGA I/Q signal transfer (#669) * 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 | 29 +++-- armsrc/fpgaloader.h | 2 +- armsrc/hfsnoop.c | 2 +- armsrc/iclass.c | 10 +- armsrc/iso14443a.c | 2 +- armsrc/iso14443b.c | 270 ++++++++++++++++------------------------ armsrc/iso15693.c | 151 ++++++++-------------- armsrc/legicrf.c | 39 +++--- armsrc/legicrfsim.c | 2 +- armsrc/lfops.c | 2 +- armsrc/lfsampling.c | 2 +- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 6 +- fpga/hi_read_tx.v | 4 +- 14 files changed, 206 insertions(+), 315 deletions(-) diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 77223bd0..a132b762 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -112,10 +112,10 @@ 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. +// Set up the synchronous serial port with the set of options that fits +// the FPGA mode. Both RX and TX are always enabled. //----------------------------------------------------------------------------- -void FpgaSetupSsc(void) +void FpgaSetupSsc(uint8_t FPGA_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 ((FPGA_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); @@ -154,16 +158,15 @@ void FpgaSetupSsc(void) // ourselves, not to another buffer). The stuff to manipulate those buffers // is in apps.h, because it should be inlined, for speed. //----------------------------------------------------------------------------- -bool FpgaSetupSscDma(uint8_t *buf, int len) +bool FpgaSetupSscDma(uint8_t *buf, uint16_t sample_count) { if (buf == NULL) return false; - 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_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_PTCR = AT91C_PDC_RXTEN; // go! + 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 = sample_count; // transfer this many samples + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address + AT91C_BASE_PDC_SSC->PDC_RNCR = sample_count; // ... with same number of samples 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; - } + int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & (ISO14443B_DMA_BUFFER_SIZE-1); + if(behindBy > maxBehindBy) { + maxBehindBy = behindBy; + } - samples += 2; + if(behindBy < 1) continue; - if(Handle14443bSamplesDemod(ci, cq)) { - gotFrame = true; - break; - } + 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 || gotFrame) { + 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; } + // Stop bit + ToSendStuffBit(1); } + // Send EOF - ToSendStuffBit(1); for(i = 0; i < 10; i++) { ToSendStuffBit(0); } - for(i = 0; i < 8; i++) { - ToSendStuffBit(1); - } + 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++) { + // 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; - 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"); + 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(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 c8a4829f..d3fd35d1 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -63,11 +63,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; } @@ -88,33 +88,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 demodulated should be alligned to the bit period by the caller. This is // done in rx_bit and rx_ack. static inline bool rx_bit() { - 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 = (int8_t)(iq >> 8); + int8_t cq = (int8_t)(iq & 0xff); + 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); @@ -131,12 +130,12 @@ static inline bool rx_bit() { static inline void tx_bit(bool bit) { // insert pause - LOW(GPIO_SSC_DOUT); + 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 carrier on, 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) { }; } @@ -166,10 +165,10 @@ static void tx_frame(uint32_t frame, uint8_t len) { }; // add pause to mark end of the frame - LOW(GPIO_SSC_DOUT); + 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(uint8_t len) { @@ -265,12 +264,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); diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c index 34633f36..a149e0f9 100644 --- a/armsrc/legicrfsim.c +++ b/armsrc/legicrfsim.c @@ -284,7 +284,7 @@ static void init_tag() { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // configure SSC with defaults - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // first pull output to low to prevent glitches then re-claim GPIO_SSC_DOUT LOW(GPIO_SSC_DOUT); 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 179e87eed22bc326f37e33f6925116916701c6bf..bd296174f0978d3214bae003d25a7063bff60021 100644 GIT binary patch literal 42175 zcmeIb4RBmnl`gvaoR)Ij-BKUPaz>EE9JQ3tF)gVr2gf){Yg=}LNIYXr2uy~e=5~UI z@lb(L80ucbo5yUm{}Dy*q`99Q_=`LL z%bHKN58Qp{C;ohO$7epVn(m@l*IzHn{?~gJX0!AOig#rfF6qpEpmSjtt)@8(-@kO> zlBL%zravdzK5zk_Z-4D0pUjes5Yc7XNS^;SvJopnvZp;u4*xfKKcAB&JfHdRNR|Zk zQ`;w~(d5U!QIZ$>QYqehC# zhz;R#ib%@S6;$g)NXo!Hk~(Bq+KGsIkJIo7 z?JsU~@SgGhjfYah##Sw3ZX}K0Q;p{NJv8qWopZWY$IJRD?U>jp>$k?YMZZsFCt>Ro zQT3imtKIp>rZ95_k_Cqnu2X^ieuT zT~5;0&8Wp~#%eOOi#i+w<1&Ao?~q1GJ7w%A^l-b@g$;yHrDmnvMRyB>$Fz(Aw@rl|zf7eJ2=7+j4u6T!b zOm<;ht!X-@VO%5n0(ZUo8Xlm>sl_p>S~m?-i!=tbCB}V`7AIcO8r^wz2z@TL+=Z^r zr~72O@_KJ>vtk>24lM0#qp5~gUda|HtrcI4DILG>9 z{MJ+MS{Tu&7&NUFv4&Vw>}aU(PdCMCXi!-FMw50}nGxDkcPBZbY}OC!EmRdbN=Q9L z4(+6j)L+-tx^KPe)xz6$&ziay^f%?(_KkNY@6dluZ_6ImrdMkRRDb4STxM1zw&-mF z1ME+j`qgx#i3h0Hy6d&XJRytrHY>d$meMwj*y(gpTc8yOI~Jsm#_HX%8#T|_>#UYr zjN7y(f*_3Bz6>GK{vqSGg68U1^SohNC-_I)hVsJbhwvq8XQ9?z3p=`9^XB;)I-+&u zHcd9${;Q1poR$e=HC`xqp3_E7ds^n7t%wP)m` zc4Dpm1w7ehXQtu{G;Tu1RU`BQOaUywB$Q~WOqTT#dW)Zw^~0`yiJCXDf{XHX$MrX1 z7-0tQ&==DW2Y20>9Mj2NuaSllJxbT}tUgD(>3z5K^%t6qb!Ll*3vGcr&qQ130JV@I zvbLMu827e_3V+b zkV}kbXb}r(Iu9XQjnfL%W-z*zz6>pm=e1_qOxMf!YM8i#)GA>~EMf9{WDRtXI>fCd ztqn#$CE~Ks2FsrkF{xSZS{P_INEs35tyQEpw24JAK#SXGu6~iR8oMP&NjcZz4GBTF zXbSq(Vd~`Sm;J7)c7}Q=>CCOte<%Tv#82ub^vT)*?vKJRu}Di)^&4qi_EhR8_5Vsg zk&7ycsTurwo%Ri+o;CJp37R82DiE$A#xDpye$|Wv3+^(EY-3`Wv#Dma`xm z#;<*5>M-ykgK+^k?uXG+?e{lH6z%Q&$7{3ZVBiMD* z#LL7^v|vul`e*29pHe*_nwE3$ zPTwqMY~u#)&uCb-+kmZ?=<&h!O7mK+N*4Er@oU$Sj@?GKHeuKka9N_-DDBcRPCASC zs6Eg4^&0ddW9w_^Xr$-wl9PoOEVc>oH{No;OW{{Er(=r(ztH*Q@X*WHt2q--Lih!Z z6N&rd+anNGB>z!oL{oO&$1k|Kd!ajqsjrSe7>NJ9b`si@>CKd|a7w?LYuflnVogcg z9%Uh3s^vELtx^cTXotx38)ob#Y+5Lqi8h1k$qatwu@7@rqNr~h*~2DIUN(uvR6rKs z*Gc$Vodd}&`m3<>STEj(XJvQ3z7*hB%}M-dr$|ib*T}aqoMZ{(I@^1r0A4)h<5$Jp zLn$((20KR`6o*?#d+ZK=^0Yh8M$M?Q0EVdnb5);v`AJ5kP4rpma`U|H%7^S@+wwJs z$By~IBse^FcP(t->Dm4+ktxO7&{gzjtNyAyOYp2p`lN?nk?rU+V9P{Tf5c|+z|iYx zqdM>7mud3~Qbxut8Z&Z6a(KB~Qv%Q*b@eMGlu(zbTg z)r&OrYd>Wif`tg0rZa=!@j6GbNb8G5xdj3=AXeq2uG%1M~#7a(}L*$IKSUEe`;MX=l?dJa0(b!VWa_70I@C)0Ic?!BJjdbA_8p0^%WD6-*zZ|RuM#}u_ z6brKWAU5_!Yk`9SX5F=5{K6<4%={_y9IMrceit2+`<%Kmc$3mo#lKv%;sN%Uos&b= z-V1WElRO>5FTCfA>OE`^`;rfW`20#+;$VJESHGBlh5JMCNA%A)`>`|Y%CQ;##ls24 z)gRy9SQFhvh6DYY#lPAsfUTDzk4uGLAEn1-OJ&Yl&2ZP$m$^2^BL%oTQJZH>aru`!KRi)=cuyL;w`&YD7RE22ReV5uQK@-+K)=I$ zy#HHA@g#&_=&I^7x{5xpDS(mLEvMoi*TVP}8hbWwXe+J#SW~QE2)`Ju(2Yrg@K|zE zUz(n>yCjq-j9+Xe6n=R^6I;L8_~lth7+Br_OvI+BOC(Ei&D~?nzg$Ri&GLVZUs?DD zo-AfVZ3Xx>tBttgD@?=)j$%ox_4SLr!HkHT8X!Q+h%2^e>u86y{bP9I`GlwfO<8k}3}V*K*^>~|iVv2V^r_!WXJg(j9)GisXpJ9+@k%Ij>s3 zqgJnNgx|X+>+>%czuf%{{Q}KdqkY^Oq6f!}w|xGk@C*AnxmJIPek~WKun%+e1A9?* zz$f;O<@_ zd!tz8?arLCuV0K`?pi1;fWAweq8FmvpSaJz6n=Sq{s|j=2b9XQkRkk&JEri0@oQf{v#prwyp7=Ac5KLNocJvMwIgE*!)Q#D*x=uqy}3v`(Rr+n zA3p?s?KD$P8ezi*Oq75b91-e0&>l9H0v40FaGtDFtEzbPed)AXT~r1hXY2b7vnkvQe1{% zgrZgJXY(&3vDy5~=ynnm z$5I;I5X!@hVW}jbiJI|IrMt;7PRacG~&}(Ne;u4fC&3&x6KV zeLhrgVI{dne}ND`thCSKUvF7C*;&5g7ee_+wmznGXY(xng$S?>w$;N?DGdldQ=;$M$a`>@fVb;t0d!wAZmG)(PzbUwttc3O8! zqZ_tmJ++O+Ee#Pmc1~vTFPJYYabRP&0s3Eof@CyKh(HMQFPY2!b4h<4!+{%L#y-Sv zahni+AynlgCx@mH?_K01E6b;R+Y;hmd#sLZ{2(x8y+j(tbvzW~2=TA|R)-zm*@%3K zOgXUOisKW6imzWVl`t3aZ11=>1Xl#ZQLzF%@%4-G3#Pz952xAfagr9giYEe3Li`J~ zi!O6oUj+X`=PST32mCTT+B4B{JEDa%L@c^* zr1cFvv3x8Tr!r<9CXV@qf~|4dX<4Iql8R*6CNTeE|2gLJuX<-A3p+7DV7JG=R)BxK z1?bOsOTYuro|APPXM>%0C*dH^#z2^Vy%pv88Sk#PVG`-0KoP}S|L6O{}dk)MV z06oU_Y+b;=##H{RT|k?zWd7wS;W4r%jL!jg1dm}WGP zekjnNlIqWVq2~EC`jZLx*P6(7Sif#j=eA)WL*pqr$U>NZNyVTocYfT7;zZcR@k6}= zmV^N^eoZn`BmdPH!Y||$op=|v)*@v$8pq9D(sI(X@C(~`EPl7QpEq(~;R*5b*lhed zBX6uG*TfM>lsCc+VkEz3%_;?sGi>@eoyTKAZy4<4`^Zh;vrizwuOzS(yy_o$$LZP zzXU9R-O7FL;66VB1G0n2_Anm8ueaz$xvLHMH9(_sk${_#Q=ot+WuJeIS)80RsIU#V zoU#D59UM`-DF0P3tYUN5f1f_0Ae**mKTQ))|aqXTM z?2F7u^qChWC7@pc|GJ`P*lGEk@rUp3rD1uk9Uo9Y){Dah|TnxYTBQ!hta1`p+XZWArn&esa!xtq*ZAu>_X`Q?p`i?&D!jswjYe?A6$nFBl z8wRWicBpvl6-+|FzlxwIT{83V+y#wK$?Ml8?d2Ru?6QGqZ@KHG@?Sk{^VBJ zc_hN_hMf=a>kR!!t{C4`)=)*qMah$A8SJyQmXe>BmprAE_QqTt7$v^ zNUE&jHS#n&3@%7@<*RNCc8|mDdWa&F6i)XEU{n%RDk-hEiL*t|FdZo>W;5LJn z%H@`v5=+fw0g15Vc!K%4#d}Y5j@%%DR@=1?7zZK5xA!uTwc2&eztjiy8`_XaRo>U# zXwa_m8YiBuUCDWK8+OCXe_0XGoMOx2H1n^==}re*^yAovEr;g}Xk)HVl1BdP>A^*I z=hP6#Kt6y#;?SSbzsZGp;MXmh`Yz6Yv5VFD_i?V@fX#duSO7s_fPVePyz^jNiz@YF zx6U`mg}6MjoQeZ{dR=J-H{u^*2rpJZg-2-sGas6QC*q+?@CM`6Agn{u=8 z3&V*ZoBXhT(o(##xB~t)rH#v7{h6wcBt>7rfk~OBzje7xz`xegrbsIpqZ_U< zb~pej&#@OU_yPY~DK=Vd6GkcZ9dkb|aN;{$AnWHg)Xx)bM1d64CQ-S^j$+!0o0DuYgz3FW5TQ9x8u6;9t|? z855{Crgsq>4@qZ6He;5^+eFlp>sD244`L0KY`T8G1(Snn-+UZo75%cy1?#6O1c} zAMVl4L{OJfg;F7G$lFh?1rIQ&Ka{{hxWwi7XXsodS4A7OR~-lwu6~Wxw2KY>9a~^7 zJ|XtV4h#M1hJLC31o*X*3L3KAS#2d$Ci5(WO`JX9aRvCLEnimx|N2nxZM1v1-MWGc zIXoif=~tg&5MYav5q%|_0*SEUCg2x*q!51X)|9_-k6tw}uKxE-=xC{eovevQk2bBS1hU-tuQTP&kb zYlP5lDg5G^lQ4cEl0(WHOj<+vOhxU(e{{zl;Ma(Cv=(KMmj1rsqas&OIfbW>GZCA~ zf5E>HPner|AA5?$ zTVQ|zetp6oo9tr#wJbU%dQ1coPk?C5jvu~SIJ^DenpHq6dDcd(`#Jp;dRwZ_2l(}> zePjgaFri>y;s{Q8ZXMjHpCV9xj8?MdAS+VN9742?ScBXNBrd z5I=;be7XBXXQ>r%Y#kJx_cJcr;FTf%B_(IOIPJwkBVeoIAzGM!akeYITKj~NC4%}>JdL*SP^5nrO!X+w9|6L1Sv0W<4YP`{C&d#MH6xS+zJ{RaDo|16b17Q_$V zEu7NAZL8MCmqwnJi@0p#Y5IXgATbm_{I0fEbg=4GxMqN$GNBYBEm37C*){RO~|{ znD0Oc_}9}k=(HJzm1-edWK0ZsJo?p9Wn0F4{MtrG#~!OD(t4-d@8qg+^ttH=p1!+2 ziKHj3x9(apk^F}Kik!;l?m%z`HZz-Z-V?&Fs&o*2!amH=Gj2#{9Ks@JRkbz1ufL!o zZJRJ2&|1auiH;_ilD3|TEwNl)3%ogtNQ%w)yCrR;?Hf|qy3CFY=Qf|6&b|+!?W@0 zbzrVVyaZE?ZdVoNa-ReI`X5HI>0!FHtW6plnrHBx$L{ejG4Cll`e2v6xjgr*^@6ig zBp(1avA==M7U0)z3s5_etj;~IoiC#P@JGB4u_$vw`1J@)iLS$d^^@W?kvp+!cdII- zci&_2JR|>Q?o>0MdW{Oh3bv-P78m2!PR%wqX0>D1HdC34KZ1Wa8^7j@1G43l@wJUF zhI z>29_FzfRFntvd}j>~3uAcjpt$@f_A&%};<|515Fw3#j?WwE}2YUqA5F)13gnz8)Da zwj;t>`%&b--d8auxsBWBAb$A62&$o~@CN_ND)d>S=_S1h1hm!I1N_=cPf_k$30q%l z9ZfB@6R%-I0$69^SEVpzaN3J1wNvaU20^({{o$+hBfIOws#5E#=FiO>3Uu|8a<6OW z1N>T1SYJ?R^;3|Djz9JAOW9{1zankoYqYJ8e`J4)I`ZK*0e-as0`iA8nzc`A&@YAn z<}w}@@YYcIujAAO+SPi3UK>=U{)}HRdZGB?b71SJ(<|x!7Uhz{yF}jUm>oqt3Wos`BucE`puNqWHG9;m^mY4qu_1VR*hKIHF zpk45dPZ}t>?DLXpF{X{R_6Nt8B1_KdFQE_V|0Up73w^N9`>Ka^e|aVCB!cgai?u=ScY zRLC4+1Hz6X+l>If%%;a{K26xqFIvC?h5$Ek2O)%Czn0xrV(r|Qv{8xrL&Og)4kU*0 zOI76&!xo3HnV$SYdbTCNuV?9}g&W#dPPM*3<69P0E}LQ?le6*bD|BCT%O>>a-^IP! z`zO#1CWh$PJu~^QviOEbi{bzxM3O@vRliLLznttK+0D?eaVk{WDj0IYAA5jb4O zl%Vgwq_H_&CzAW(8Q&fT@xzx5td7}P)?dX;vngmrWvpux0{%5E-!33KiIQgRZt59H zREJ)a9*_n2^%-b^oksNOZ>czp0$s*0&mQ{tRb$cKmXu|b8(*i9B0_NCHZB)7qC|T^ z5wNB!8@FL8d4Rs%2=MD}>?lN=8MY=Ps+=;g&pv)#H64AMdLj&4a9Dc8<^f=V=g$ZD zRh6R_#^u02RBp!4^j}4RXik`aouKs#QhC%>K1@I6Ld2c$=QpHVSqSRqpCnnf;7p(v zLYcS}+6*H970(uY{A$>y6-l|5BlO<^6B~K#>xIJZ0KZ<5M@n7J%5UizOPOU`JJov> z@!n+l69ImeIev&Z_M-E`9nL4tgRsnZFw%g3t%Ej|CDuKo%zM4PJj0BPdH00RzbryD z-Y(h&gMN(u=v>&p9&s)aiXX0|{e5;mZ6XH2<3eUVqoIDD7bW0d?*`jyxvTgwoElTG zwa3GP+4-+GtSli~NQCoWqxF_v2gZSlju8LaLtl*C7)?IAe8RW@H-i^WylwV8heTL_ zU(h%!`z-vHgc=uUj@r*JLKzph{s!WQVpk(a+7aLQrmOEtzs8_wjjqqe_4AL(78^ah zp6(lNuiUz(_UrVhZ2uX^V;PPJKB|76tjTmX7LgCJNvR^xp}1#RyWjN>xqkjFksV2# zjNjnH)_0_r2;1j=S0BOhTL&pg8+{WO%UcH&7HGGT$G_k);uPD;Qaq}8bp>t8X09sf zn)_W`f4IZhCeo-6H@PGp*wn^E9)fNK{EPNpyG`_c1wZoITu0w?8`t{!6{Q^}V*!q( zC@i>Ln-_Uh0Kc02`lvm1ciB^uI~OE245jQb>Okl571PM1ucG)&{bBT+T`!XTC?EPf z!`9aLt8`8R(YERj*SeSj-1!ftU0J=2YtaSp0>EJ+F1lrge^sy#7X!aysNcYMp{DBu zNxP#78W-Rfqm{J`HbMEcBHpNtO76VDIXjILH z=LCGMovi_WarPhYc{@JeSjDh4J=8;}KeQ9mL!tV^$He=O7ylZN6zJDC4UBXkTz|M5 zeHNcXNXK&>uY{o5kZEy$tfn7Qxvz~WXM&V|?bh1mcZ4MY3WmGqk^d6sMAz+!1HQhW z!9k~IR&+@q(E^`;okC_Gum##>d9Za#{Fu5WY{CqFAyl=i55M&{9{(EZre7NBJ+wmU zAlJ|DJY-eZWDy@@*t(SukZuNFHmX7WVGTlO5;lCNb~ldhb~snC&5U!KS@nlyvL!5} ziv?KI1dJ!fmG$+@t>0k$ntQjV?EFCMmGsjHG7$s8`7hKTo}u^1%>!|Ut@kK<$oM5; z7=8STJWu0<^*XIr?H|hP31Qx82Z^u>+GKtH^*X1$ZcSIUmy5_--GV^k+l0t=1qLlt zKM(#jW-QX4w${_bI0y#k7STcn`!MC|7wR`+8!f~)${NZ<;YdR`$?xGHYSvu~F>jp4 zZvoFOw^r!L@bx2E4)0J z{ybcqd%?&M$;tZoHJ090%2cp|^|Vu1m6&oA4+F>+&YWNV@A9qNayDRefjlo4-H-lE zYG7nCIdbv&<)n-wZqt?1a8Tvtrl~$<^n&`0SLm42B@!m;MbAdEqQ0cpz~4YbaZ4D# z1_idw7OgF^Bho6;*JuG^VHfbUJS}zd0D8;UB=G0GYOKlsJ5v*;?O#M9bFLheE z2Ji==RJ*QmkkYg8D^Fu!4>-P&0=RTp9a7go2oNzV&fwP>?X2j@Cm+y$%8uedvV{71 zY)Dc6Vwis&f#px2-Vd~^>r?F|{d)N%+9Y3`IlnxqV5^TbBe91TEH$M53F!ja%=u;T zFCeU80>Aj8fNM({&Se9?6z2N)rL9Msw25=0d&L6wpI6BBmIC@(SHJlD*AYkc=e=}x zxJ%Ssj*cA>3Y*fdex)PdrE^wJY%c4kif6?^jB6<1Xkq*k98Dc+(Z*;g)qzvXu|5@1 zjJf)iu89GyQgY5VZGkMXiL(H;S9_m|N8Zu|9TVn)l)%W zD!{K2-52@9sByb?oFQoxxveUn|8nC0qJPSrXQHw-MAsfJ=CyJ0e>Gq2823O20c4}` zehttClK}iWLPLiUpXWM+|vJAv%`7!GU-2OZl&b$0stoak0bq zi_rJ-%=zVtbuN!5yY)lSpBuTu5XRW|V+$GJ*Bf-9@oyc(Mc-2HuqgpKJ{q4dI`*J+7F{tI~rDq4%j=BPqM z^pIno?mUmxpxThqFxNop5z9J^C2$B8i57$tQmq&8%iI^;7Db%o8XA$;NTknHK_l1U zS6$s9)E`b+OY>k!mr;JWOTs2VcQn|;lJ3ml*BLsP$LgG+f5&j@DnqIkRA5{o{CbPB zq9dhWeRxb_y-J@rDb9kZZZ3hxss6b2^RS1HU|e(LVkDYVuR-IW@3t1of5Gn^j9nSi z$YF6An5n z%kgicp0;ZclK|$zdo>%6xq89%hwHFg;0+p7L+^KREx-irMi73EfCaGlJzRgdOMbV4 z`uVR@qS#VV`7bYL=jj*oFJ#gk<612#zDigxUQ>>1&5<$hJ&J!(?kg*8eP8Tpf%UTI zPO>)$L0{_XGS_cRYTepjIdfmeRRPF6^=p3Ajd_8e1@-odN%96?ihk zzao!P%L=0(c4NP2>3?@Wru$I{ut-`Bzs9YfA6dLK@!HS{o6}zPtY|DG9DRm=v6bNZ z!@r^bkSOF(dldrws1xp9-CBba&9sz9aUGTt_iv~+TxT_**Sc*~{d^64S{o>gfIu3G zUxl!Z(lS<0S?&+lA9C4-hhN+J(D~=^gjuB4==z6l{UIugO10O}UNhBO1@2 z_2)-aeQ%4CUZ(h0=NJ_2-<8J*!Q(_GJwUs-#zSaz#(i45Gt%$HJkiF}uQkvwR6{w& z66RkbGf`YpK*fJCGnrnpq1neT?kb=2-Pj&I*4Q(FJ0E@_FXVfUCq4`QHNcZtqEoOf zIcL?w`Y-4l?tIw1n*|;z<5!;BNORh_KuevKAJe-9m-*FCC_#Y&t*}%Z@UQ)m1&54s zZwKv~Xyy71)aki)-V$mKWlaW8wlT(Kvik*~%`7|;v~m3n_xu;*axaHNZRgf94l;Xd z3inHWmpcE2c#Di@Ob)zYYjK^P+aFDRm(AzD5QYN(3QGN%h^aPStefxOI4@CM^CLY+ zs2^Ls=|{Ta%c~PpxV1ygk2?P)JB+&fwTx99?&#|)BVD`R!rf5i0+Kr1A3nc~yFawP z#@eWc;uFSP9BMGE4;fvTzv1c^@GFAZu;y_5r&!F4rT1W5>&dQSHiGyeSIZ-g{ndCP ze^f5++w{Ot`(4XuXC+fw9>TBlr5@UhR2{&#C%=ko{$b}2B}4H;Rck~r7a4i9(B*8h zhd6%NHGwKqGiq_48NZgo8Y8;WK}DfCb>9ZzMFs&2S6EDf+qm`f8MKN0VQ(P#QZ(UMY>#i5yAGKSyTgDyQpY?8XTCF)#vHA23`CuhJrG3$T57!@-5u-+b?iPnN z;8#|UMt>H`qM|;28+J54?)@7*)OlJz(2Ii)!^wNFaQ|-h3?~QlQf;2w@cHF8Bv`hI zicq8&m=G|2NiBX{O|#ew@5?4uHycv6;K&2TBU7D2J(&^V`_}3`LZi`r>7PXiv8WV-4(l?A20U*2I)i3}4jRlt@#J&EmHF-ex z?eZH0T)s`acLu*s_jUExmw;b~#6FQ|LVte7__akJaqS`Ehm6ajZma%Nn8C#&`85F9 zk4g%^KIZOc#;-~2=M2Jz7RL`W!@#ex$WC+R=*8!k$=1%MIDQC#UO*ct#X`(`5Acga zXD}C&Q~(M>s7I*7s`TOd23NlV{Nk|TeQ??V3RhYnJ`*$dM=^f=RQ8-md~xVJA%18h zkhnBDT39se{wM`o)d*`!^(Di_>0DISVTpKL3UIVIEG` zEc{Zp*<|THE{PY7*P&xA0*<1GUj+Q(j2JX-D{ZFMzc)@7Oeo{kTKqWtD2w-)dww}H zhVx&WsF1fM9D7~J(lsVHbr`=y&&0~HuXOlQ4yoAOhiy-KhtIO*C4NPyms=bA) z|2k!}qb)%x&_jqH`u?GdUy8Y$r1fc)|3Z92yGx++mb<1der24ES*?OV2ig#V`8bm< z(y~kAk>jKA>qZBb4RJQmlXMySg$ffw2EoEiA)MJxBJkfcD@xB#rPU!S~8VUttC zh_+~yU1HO#Kg4<`7$=melvAm&N* zP{ig9>F2-Z+5H?(Kw9~f1vh>;UcRNRu^3J1X?fVgFPe7&oDtV^l|UN%|wlYll<@ZyL0NIeb9 z2d#LY_ADWVl)rVY_U;wC2NkI?+%@epB6JA%;K5fPm#9Bv*T}HI$TTEY8r?+&exa_j zN3N=1IOokIB~Qnv%`^1GC=NADxcW6xQ^7S&If*dc32fusK<2S{Cdvi`mNvkzO+YKe zn&9%8!_oGF@!3Lil!Nenq5K!u9|Cjxw0~tG6+5fFdye-ch#!vA5Zpq*)(_>->ddpT zY%k*2mCPYjnTGHS*L)3wfBigqRODp6ts8XM>6T#L0srFqLtY)NYVh`p$oT16T=2;2 z74WZ7`WMRL{wV#`=Ch;f^1W*$?EFRewf-*9t}Tr?w(NBD$FC~9w*Y?wjKkM2=3oD$ zx2|C7A-;!Va)g4fqc71zD-5<9g7w0PqKvJnWwr6AH$N{P-O|1qp~d&pI?>v1yy(a0 zIsbM1wXUVe?;Jw?;f-8>2#<%dG9g0vrJ?L_4X(O+byx`>NB--qL}UP$GSw>m+FJ9f z^)uO(2ZO#Y%JqjyXL%O=;f!-@?F@bu`%_ID*#fXy-D;##NPVTOVo|GiZ42KY^-#nT z8_mYy1}@|P7q;MQEg;wpEx<2CX%H!ep)?1LRPU{&7$*uBFq((rhm2Oq(%kLZ5z)oP zC(*(o+9Oc@7Q_#?Yv)P_Pj0?{<7(pi!_A~uth2Nj{>D%cKa^Coz(y6mi!L)!Sl)v9 z;fP{5|E17sQhtEGe0%FAV?34xemz9-vBFIKhBma!ei=E|P0m;Q?P|KaH>pA6s_{_$ z2G<{2cVRzop}(V+yz$?(EA9JQT8@H$O}cB!_;t9n!%9rf-KV`I7uku=>pk`}g@sk{ zFF*fPr+iuy<~>h8i{zHMXKDt%cz6cCM9q2al*Ii}s%)dwa}s3$Ir`wJvWG$Z@Bs^M z;bG&R!j*FOlJ>;0&oZ7GEzhU_qcFq2#CB6Y)>***Ui@UN#yPFN$x zuy!Yvc>%JGn;V7%DgbxKg8bJ%)6Yd}VABEpGxD61n?hH=OV5QYV5#<6#QL`UmPto%-UgW zNQAh{u?(*%V}TaoCM1DhKceg8N^AKDr#~bL+Y;(@_Xj5Eid6 zz|Vhyf2r!4YP_4CUe{wHJd&li%|(@P{f2vO6!5EqHd-ANld-!92XJQ9&odWBCOc9hQYB0eDMxkG8 zYrhUq_;f{|b^nG}Eg$VYW+3mt_4BBxcd&5JYTO+!e)t|IS&g0P$3?y?uwH*7__8tx z0e-o@7G~pBgo|^$+#T2en4dji{srIfP<%ieg+lL1m%Wrlw- zM~k5i|LAt`6x45+s?AfrekuO7EZ)#tYwZx%a@ep5@k4+@5TEz&kE$;YrQo6?U`*f_ z(-2^vU)TG2z1;hwxT-vkFt5xFpjH+a-a6nZrjK9EHSV~QTZeG}hIo%il=LS0AtT6y z9@NiQNL3qtxd8ksbXaMeu~a}7s-Fjbc@>hVIk7UgfLB6bAmctOtn|(=Gh~B%S{;CO zOoBqRQ2mD2pOSu!#&t5F?QIZt@;rMn8J~Z#uVv#pM}aLm3TN^lw+ZkI^&4shnRYRd z6?6!f;Fnizi2Dy^Yq`!w%9s%?UeRXo%d;DrMnl5dX z_c|@woPk2Kh6;8ko)6>K&= z>yvbrPc+*X^DhNkZ!!)}CY$k|9=s<$gI|hU#jB`~dX!pCBVXGr*Kz(Ue=+}p4qhn^ zaxkP{YZj`IBd-avuzh^~1>2$}u(1IXNPyCM2UgUY$$xo$t|HztRZ-VC;J$9of7yPn zO;TVJPGKVf3z}LdUHr0jn-qR2{^fF+9E~SHyGG&|mw~#t%1r+2Zr46nwS@6Vp|u?Q znwEx-1p92y*23cpZ4?+kS}J|l00m4ZTm zOBswsu79ZRKTM~V!%Eaxuw!N8ds?U1IDsnNM>X5^57qsLmKBYs3nR`DM-$48ZP9NL z`+k&i^^5PH-xURqvoxDfyCTyP^Yd*p#_@!*8{cs6KkRxInzBP2<30<$kxt4Fu-|LC z@r_Ev!>@_>7kGdYsm$}@C&Sl^X%!8n5h7^UQ1z3B}a5fQrf~SU4&nm?fN-+l*eUb zO%K!35zPD--0xE7mqo4x-aDs|09y`-^E67gYC`-=*;TeJC@_F+$&~bOXg`zN*x(o3 zJ?7nisA}V(RtOTlON(mjOjwY`a#&f=ZsPIeVmL|VEbV38YwGu`B+cYVcT8XjN{}IA3mw){l<{f2+ zTUuYC(_(QyfNWO$P}NJkiE;IODzSCwn$(Yq*}D>3^$`DJ`~s8uxj@;5X+$xcqSi`j zJIucrzc5m_em;FiY%#}s^D(ZO4B^+^PMXW2Fm?xHXYSc5VrH0sp{so>9~gQ?Q~1U4 z!+#Mw`5fd7|DtnI*p{6`9@z0+%A5$RE5$FyuVLud7$ML))|w?;Ohk#{cu>EA)5kwC$q!;Vz`T;@9<_BGez|(^(kvp@=n#&_pPHh_ZPBEcl<;&p0Mr zX#`2Y{NRS_w7aIvzuuHL+N;(emW%jdK5<99k(@yU0H^f@8gC7*Kjh5p$C8f?y#gQu zE=$xxoQoW!VU@x!|lv+h4cHhIO68_#SXPT!-M0*q|9 zcn<=kdG%$8AEM2mp4Xn|K1*N|{;rCdSp#MC_!q|y%XD`?Jp5A{>zC22)z_ka!#27% z1pJHftD}@$r@vlM{^|j}&ic8NyFYPe$m4nnzXZ0QiDiP(n@B#SSLE7kst?)})NgQ{ zt*3xETQ^_=H3KMk0C4P*E5UbGD2!9_LknSz61ENQi<>~7nOj+RzygH$7yLts`!_fw z*zx2X8*RYRh&h_O7T{kvVh`G7B0Yq0@rG3X81p!fe=&YxA4)7i6Y;p}r0)7ZiK7-K)-;n%!PO&bdxQTzxX zE)?xLMH@t##}&$d!Ccsw`Apwb|57$+r(oxm;}P($M>OOeDuAtEIHxq!eet+v_?I?j z;a(%WhmP7yoy}!^oW{iL`uT39<_M}Xy zpgYe7_$BY1=sXoe{h{>_QqiuLi+)5PlsY zH7?G7<(91~Eq4Jr5jwwIAr2dYZCRQsJCqm>3qZ6`{rq}TU1i#Z_&oO+8Yh5gGyF?~ zx6HEkB_9-H38`y-WM4gn;v3$VK){Wy0r|#aWAw zeGOHN7npXjZJ83e7a#=qbpdGAQ&s%y?b4!3@(lpu1xIDX99O?a8V(?y*)HSN)Rh1! z71nss&vw1$>KEs~&}V_Jn)E%olaGd>M#yM)&}YZhFRSSkNs%$)Swz3fTUA&C%z@O{ z$6WmynRh@NGZC_=>TQHSET#G!Jtupd4D{XoE{}ipgC=vylQ%9@5pd%We!sP1o|U)VNPw$HG=?pna;Z7PTOmo}|=VZ$>~4nwsn zA_gJAFYqt?y_*5B<3C_Gqk*dwwo4 zVPMVM!dk9OQ09q-U1rx0N z1g<}P0uW$K#=aN<|Ek9SMtdJYcmo$@PrL6?{EM!0ko`~4|IJ&A`4PCT!@;qQ6oU!5nA|C%4Y524QZ|DnCajLb%#D>L}DOLkQ7mze6^v1he0DogB4 z)J)Fc*Bj{J%1_0cEfvRR{CZ2E&y|_`H(=RJb#1SA1emb3rkb%cXWw61AfXb_XOrg#fBy?Md>h)JhFrDTipJlQoq1$kL->Un z`~Ecl9We!DrgpiB+g3#wzw*#8gvCfF3_6E1S?~|1I2zCRrM?S)|BKGbcCOYq&(0)D z{pT`IfiR)JdRdP8C~sfd=9}Y2opj5VZ$(NX3H4bT&c!>t6O`CB;Z7de`$Q%Jm&e6 z=$PN1V(vCfxW~Uxe@LA6>O@yH45!zvpD!ul1(#oN{f4T>l=?zUR1YDY=iQdzg!5mV z?=~#3k{75$_9^^=fc+D~FWh!mK@DCEI>;O!<{h?<;?x`9S2H~>RJFzu>mR7I0tire z!4dKRzvj~gxeMhM2pj%DcH8*#h0iNS7LFf|xN2qVW3Ipyw@cHWJ$ z!!8E03Kkdj*rgs`Wz)y6t@NnbzQPb%Rsf_V)C&D+ZZ8}Q;)fuDbU}-N^h)*?9>exRGPBc7VT&>kluOi$)Vut^9{9d&U#+ zHw^E*xrbkf9~w{eKlmKj*0|gwJ6|;3=DPPn$Hn=t7Hez-^u$6}*+m~GFMAU3uSbAZ zDjRsp*vD-c-@S}jh=1{4nFO0q^&9MFxW;@{d3nIU4!E!d7lO{aepEojLinX_7vx`u zp5i?-=hj2`rEcBiZYbIXBIfre#J{{7H9@;X#wjXXM(5!%hVx&AmXm#;U1FVVuNa@# zkP(A`jM&%=ew{@QcUwgaa318tFXHF22840d$yu(K-CJ z6g59btlU^~pNsFj?g9ULfFAE_c|oZ;j-@;oza9on-477vU-Ydck6+h%Z1f~Vq zrGS6ssVciH_z4a>iWVw&f<6cQ3rOU+Y>Q!Q07umVYzpx&uR1;WQJaf#v-lU=1lX|( zj04OUh!_Om*DU_!Rl)~d#V80YGR(h%vHK<^!1t$IzeUy0(@uAu*>tahpWyc=;9u&t zLl0`1c9}Ed3h^%wzuakIw^57|@> z3Tu|l9CI;6{rxXKZBfQ?tq%#5Tln^n_1*VHgHeD^_I0x<2%d!a*EzWQU|X=6mM3_& z#aWSMEO6@&6_Wz(s(6BDpM(21Tnnj8H&kzy4X?Pij_Wtr@~d$%?NWk=1)hF!{1AW1 zOO1=BaJDNvu1oS?=rjIor811ZJ@oL4o?NC6h!@T4qjiS@p=My2dg3hPVG%vKqUN5+ z$HkwR^IYLJbKYppJ*1vE3l&z9)FPJbvl6yf(=B=~*qi>4xm>_sBM7i83OpGd#W zEj7J^{r2tYc`;wmYQ$iF`S#2_^2e2ke3(As%*)oSbcF#3e?r1($VNplE)sD6qZA=u zFwiml)l;HnmI(oKm`5tCKo)zWfsl@D;MDFi@4KE9HTlS&s`*jEN5wpO+3H9ieU#<} zeHMBi2W}Qqo%Dp7VkED-PyBK9MzeznYq?><@&|(4#83P=ltfv@E|3Zfgf4dsjz#K& zHO-$ZHd{9v4TTxuy1_Zt&3z3)n|#e3k&n_xpq$1cx!Rf&H6p^#r*Znbx$eb4ZQDFwrY(?K?!QZdQYU^=iaC_XyC$Av!DdEaNd*P zyLwrhtlN!wO0BXVNzBtC!nxgQGHS>lmmcYNZqLq3)GQAP@^)*UQ6nP4?KDmjk*U;| z1zJH7g9J766V@^Q+|;X)l$K=}8Vk0SW?@i0@xSXP)~`FDO*JyY_J`H#GWW?me_T%r z-PbR)Ij)ou>5s7pYaG7E{Y0~o1T7~Mx}$`C2T)?TPy9BlR(Ty1!Pl<|YC{|K#A{O% zStwTa&uxl683;8EB*a`Z>iDl|tyy#EV*?-EH18o6vJmjZQG)c_5O%FW8o|OskeWp$ zYQr3$oj^C!Wx$h}+;qazAJt1*tt0|nu?C_=*zX|nRSasoFFPypX_N7A5 z=JydYKaMZ{{x8bA?q=ce55q$6>x;TqJ~Q`MqvNNQuqZ49ZEpDDAN}O3kDvU-Sr+cU z{^Xa=-mtdssku8&p89;yCZY|92h8_I=NuwmaGx~Iar{P6AD2fX9zX+^U8D}$Xfx)* z1L%VPMEdU`x&%huC(S@u?_)$6jz4HMj9nr)ew*L=9tywbVqA6sTI5aXzh^=8mcd4Q z#Yn@Yg4e`w67*qea%!?LT^9?&gl`H{!Aj(2i>l1=`4ATq^Xs-_-|i zZJ9Ll(dNjdg8Lfk1IEq5vQC0 z?H>6+{=Lm}o`SH&7t|B4O@A1dJy;Kb{;op6P1AvC$e;3wk7A^+>4Z&}NeJBP^YX$ze*Chh^IRI!D{O^j0K$|WVy!ZSr{gVE7<)cd?^|V}?l}iL~ z4*Ln3FA}C6Me_b)!SUWhHMCG9`cawjWGn_yol_gm}+o zSu7{N(EliRPaOtizpuDR#lavTAm)93>kjllkDD2;+c@(cH=gFc2m97*GkY&D3hnNo zO>9_ukVeD;^HL#bbMd%lsF*)4pz)>C;y>}Af;PWHmxDIHzb=PWI8f@}pv%4~2;S^{S6G)rD(hn|2IokgttkSdO!5rTfOs>cV^(78F*&~-kE`SX5gI}cxMLw z^Ui?sm*6nLUlRX$XW^ZZzcT~x%)mP{@XidpGXw9;z<<^mKsXeN`ge$LL8-09W?{VO2V{m3jNrYe`;UJltan1&3H5(#c^F&k9HYX zap|8rl=b(%eJ*eTNN!(et$FLcwQ6RqX#3*u;{5IX?QidW4rx|++WQ|-U>!Alwf{e? z`#(2)wXOe-bzlC<`u5-Z@_M?RYC8X5QT&hYTo{kjmnqm8U%0fx`FMx3h}P4r&V|bs zEm_vtNnat_*MA8=fAMFZ`)Zt2fQYV&2a^1+6%RN8Qax>Pa{0fh@cFDb;d$x5196hn zN3CC`dYd2rGe4%%AN(uY1jIk{3h%M^o_|f7`hV#?ruUwIO`C>)@jd#(i*~@F1l3cO zg0PmVA5Qyx=`@}U`HI?&H;l>G)h zs@n6xoOq4WPWza3Kt$79odtR8u*jPZw@Fux(?N>4!MJe60L6}4o%PnhaN2RLxHygF zz>hoUetMMJ-C(Xh- z#&X7lsr!=;JVuwO#|`IheFs?U^X?gypp3$p#I2h?D#x+r$?QZzcU=$P8$>VyR^!z01 zcGE-D<_7zFPdO)P9)$WPb5o!hf_UDWXP3UuedZp%KtrBz4Q+OHpQp*;?@Bb5)>LNl z82iE+E3s8&dF;=ddQGS1jL>`3t-@JM?}==;S~Fr?Bc7F={Nz3FyAqVA8S196GG3t> za|x|c2A){@$@5TQ{8;vSI!oO$ocS^juyaKyXSCAsY^U6jF^+oQrOfs8Fty8ICNk+f zEF*o^PBD*;yAeDQd9wLEO;xR4n{SCpwO{5xW7+r46I(6a<|%J2EYqa@6qCV5F-rTT z)0b$hpGO1IkwIG=)n4iN!|3Dh+I8Ke?sIV0XCb7z z`#W|)iGDS}qcC2p?)Un$&l}e%V=;uz4WZ1JkJ-n?B5w1Pw_Z!_Jhopu1&4$~hqHUT zqG@qDwOvIhsKm2{`7zN|ug~awdR?z|YwD!h6f5-E`>r|r3iDIb>g=axX-z~eD^uS}+X`(8`b8mLN*CHRPWNHc>NY8U5-;i(S$7o2HAyd;-4s%WBaVB0 zc0K*dURUVzgfUGQsfT{UHKxsiouBgbtAJl)cz2h4PW9}mJ#N&?OSy%-Uekf3?z6_P zj+2QWh~;WnwP&pq6zvITZ$8NQrN>^xuY@B)?f?&mgsBeNdLLefUrl>O%pSJd-I{dG zeCJUo;sO*3_$3SY6{40H59g(@Wz45@-g;Hw7qDRZ$uoLUYF#V}P#8}x$FEr0h>54! zFz#A8sh*@|E(~Kieg!%vtew?eG%RDu;4U%g3t=5^RpV!=Tviy8mh%l#UPC^ zE?45$=~)|UKJ0ACv}LbNh>NsEeI;*QfnT+UL!u(Xqx3W_bz>#`y3WI|Pfs?S zmzQLZ3cH5C>nibU4Av&5#`2vqtgek&Pdo2}U(W>NHFM==YF0rhy6C=|b*x{N_;n`u zjp{GCTU2v0_>#EZ+H$y=5v>Bhu#}6@=fMdl9$YGF>Av^JuRX?w`V;P=!{JF|sXIpR zgI{*ogaw#secalify_fRAHOEq&i987gj?0qHrDIj!TG(X2m>tP7u;ONFY6xI-mPx? ztx%Am^j2C@iC+P<>9sO|wykJ7BaGb*0>mLy;MW{I&pfM>bUfd^D*S!y z!{bzeUx5OC?Q9sWKj(Iz4rc}@#hV#Dt_uA6Ce^Bzycm<1pP`k_66;=$Usa4>FoRR# zJvMP_c*^)XgqADuYh>2-;`p5^5wn9KFqV2?LN}=3ot5|%TNO>t zo-3cQTl%c&R1tVaS0qr6+f?G03Qgbg2|AzZQ9Gs@;`EQS2v3Yk{5sHdzlhj} zhuSm2R`ll=v_Qcg`uL@N{$?wk!s6DquL|xJ5$7-AlW-evEi6;xm$XP&0J1w=xGO~8 z#<-#-{HoRXC8Mxo3}g~(;n3g1Zh)s$;unP_@aqzIijg!X*x)PJ`AYm^w0a8=0NS1nw*Fi{GS4Tg>x;hfUr2zG24j3&MyJ(1ntZC*EEmI z0&Kl`%PDb=KH*}$F5_Rbb4ClDvzJweceU_)4!EJXVWWTe@%h&Q(QH3TFc*j5DDI$$ zTRSZ}y|pOvubQPbCLL7KzM#F$!XAq-el0Efhnjz}<^Ol|xLW4~XT&wm7HIaYjaWg+ z-&N#av*QN*XSsB3n8)=b#nGQ&J(d?gvVdRVe&Zea4tCnu^Yiq8YD)%BiEGrBOfzF{1%7F- z$)y~{2#aeGY)ii8{rJ}};Uht1vcaXy2eTpYuPTSfRmQ*a4im98ntxHZ8CFImez9FW z176TQy=GUintn;k_{mXky-NHGX8Gai_TF3Oi1}8*K3~qiGU8Q_jO-Mxat|$FjH|>i z2?+2(!ORXg&MK_LW&8_0%om78L-Wxov4FyxF6Uq2Ov5By2tWt>(6O^iv0mRQ@vj12 zOc*DfdvF%wx?ee__ZInTw@C}GC=dCWeefy zSCh}b1c1J-T_!TZ1ja3Z11zdN{mPc(7v_2GY!ESVoEl{)6O0!4R}sH3E(=ZtoTYyc z!x-pZk4;;FUkvM4<%#iqO*-g6=wwH_##^r_@atZWf4%4YgknR$9jAU9Em2lh)OdQ~ z^RK0Jlv0V9^Jj4}8+Ieg`c>oU7vtA=3Gsy6#LM(UYO}GQyVMrDW8C^$ebjr8hhNLV zw!{nOQv=KQ)owJdcc06CJRe(z01j_MjbFcD51;oK+svzS!DmwE?Xm{%v2!QNI)O-}3ufVUE zd#znPPJ0uvRM9w5o!C&B}-~^yt_Ec}tUs0lzfAaEkm(2=&1F&ny%yx*mmB0hzejMtRj>h^$a+9=_dy-hhPdKklb z1o#Eo)efc!8=foS*R$;DDPS%Koi?~ZON7k*fh6`}O$onbx+xN{?n&``7Py82slj`W z;ypfoy+J2rJh!?lc!u6m%P=mi*SSpRL~I(kqx+2bp$$4|?wG;YFVe@{Fa^(>m*@$Q z>tGpv!BnPeGsE`-ws>0b9t3bE!nt7|ziR2gQ2SDA=gr+?+v1TU!2|W&p92yA{$`7Vrk8Ga#b*lc}0bv@m!=H+kMAm+-4l;$#J`1K}r zHGYa}wga{nyJ2{m@@;$ZSU5gdhF|BIcI6w+(>n~u{=&UkiXT2h+TA^HUprs|TU22Z zpp4Ila>fGB9wL4yhp24;J-kkBrnVzN+4~YKz&vI3i+azFx%43IRBcXh102^as*Qo{ zWkN*pGy*tra(Hhf8lRzW%h)?XXIn^U`o9>9dk@WbuwM11-t$v){(@d~dvb93qV%i) ze&vlRY_{%9EE`-_<-LdUN2fm2Ju5s=!~#N4ZX;b^A65-Me!W3YXF8MEy{+o3t6lw8 zUN1LvuOSNfMK{ubiu4a()9@8~*llM>YPEhzk+s%t1O#mEGvbF5`|vjCSF7`Ji8i~y zzaFK%62Zo5U%!F_rUO@`=0?&%x%ver{g|9pmYouwesTP8EYL~8bWm7~xnb=9-{w#( z9c=gX%ZndAkqqxdtos68FM}ry0GWf%?;7;^*E>9l&x9tx8K0x>70|C%`X^c>LoZh1 zm*YfLM8v^UXcq9xIy~Y~%}`A_ewmK6nlvPVGujaL)9uo#E8*7<0NGh-?WzGkrFJtznD18pJ$*s`JJW9HAn9{lGcb&^-y8IZkJpcGY8C0J0>W zl<-SpT5Jyc(AcU1Kb@gYE0ikX*D`t}-98juFQVq%KWk^&6}2Csk7liW3BM9BaR?j! zJ8_kxRLnKlhCfQJ)7IiL{IU;G+fhr3Ikcq)K=y|SBtAj&j#_~ zS4V!T0Ax!MNc^?>Xg)Spj$bp$?#vFF^($5x*aR4AEo95_>%6;sI+QS$z%U-nU$v3# zp}oif5hG8(5I>aY&r{az!lo^An@59p8ed%X->8MG6=H#>UmQO?WOumeb0cjh4Q*hN z8`o9i33>YEG>%eE#pE3;|uhf<*#P z%JAzE)nNwHVwo5Y>}42@VV)zcShzC$Lacitn69Z(!&1W*Bij5F8auvz@tV3psy972 zjxS(F)~bS3Q_wHQFRfN;rK9aL`V2cy@FmEgB7VI?=MgqU52r2o^W0~~uSZ$%`PVd! zclPAN37{21aFgM;G=AX;@GIb9oc$|zYkzxwI1`B~oxplQw3`s}!D--^!~18f%8e@S zvx2cX7{I1km?}g;8NalxbL-Lq;SrZ1+tKgIV1BevhF`A^9pBmQ4qL%yskhdfdR*9S zKP%^7ItrS{GU-IvEa(*7^W$O#o8{B-Zg+NWkmm_H#DqI9?q{n?+2 zRsTY^yH=(K{c-T*o|*!FO;z0}H3fDAS%YrOt;y8zxbARa{R;S%3tTUCF6mTFz5K3f z%UfR;U}OWon-BgL2#e(TN#NhNWS1Lu3{yTy-N$O`3@;tP989u5D@K>U#X zjk7#18^X^^`1SYdX@M=8#Xfvm9dN_<%qon_$FH%v?TA!B&6D_cxO{-xl#al-h;P)J zx<3jy^rz@5kos!i7oc`6_nDtm;#V?;Ul+CVK7xMbL}Ba;OZYV+UX*~XshVZhi9o&D zkqt)SP~pi)aG9se;9qJ=!o+O~g3CZyqe%@vDdS(-lE^jp4z@Z-we*GWg`GbuU_5sM zzx4i5^cCx2hNPWA@C&DaO*$jiScYGYeqyufh7D(o^Mo?|GFR-kV=`D5StTL#AwUXp zE%BrgSisAL{MVPW?@ghH`;3jP8Hun9gt+KSMs*;`-=+DN;AmkD@?XHO;5H2BZ~#xzMf^%s zJq5Ii@^ChLn0vLxFT@YI&w75Cf9tun-R4?N#L%XMUwb*6g=sN>UkdXhAj~K{DZ?+0A4XL0wDCLO zAnhtR1-NFqt$vX zs0tBa)ssi~@o{=qElNVTo}Nj;FqYxhcL@092EUq`tAI^1xbDE*-c2?V&3)zg^?*d1 z341#shVLSVKUcu3bcg#z3BMHFCzprpq{umffIx^8Vpd4L`1ti>)+RUn=i+`kE`VPn z!IFuGzF4PKkETCg(z-3eG4#y93lucVB+*R8lY)Fp_ zTaGC4ufD@>mZJwT=xM}3f+ofQJ&Gp<{zY?MW4)LNIXoP-XaoAwE{ARS;n^S+`PU&H z*GR`J2547i3HSNDxTKzNJMu;RlJ-6tvzHwW?Zr~QXLt93p2R#NmIaSV>&h%+*jgXt0Rh{6pckNKrBi+j?NFVe(f#iUz^pQBqDDw)1iU3 zyNcBqy-o|NK;*0&Rm8;y!_0(MVWaq+AeR8Agi zTHsAnbJ9o$AJ`ene|w6arJkYL=mu?5og=Y)S^Y-MRG_;tynUrx&G>~)o0l^-3nl(F zDUN5mM`20kt0TK$-fhM&cm`}+ygdf|Qd?8)cMdliU!|@21${xT-}vQ z6WYYI3q;Io1Jq;TDU!VIsNXP=Li*V)wpc|20mOu_7FD!I0>~Q6lOvrN{c0nqv@K6NN4v(qhm$<;`rfUAhsfC!>8R!%?a!CqEUUvYDOVP ziGS^*trk)e3|m{NRa$+bG4LH2SO`V_h5C)-vRkfc3bqP#e(7{4uofpg?hv*260(z4XvoMjeieuqA%5uk{L5^zwuwkOF_oIK zH)q>f@uRd2h^F}$F9mV@@Y{jx&n$t}8Bic%T)**c-eXZwz%M5-*J5q5kq|s^j+CJVXZIfR}}o~f2jDrSfb%saZKs@jb+%{2eYx6U=0uhKh|~I=}i-($Sy*R z+JuXhMXca;g5c-BqQcFRq~Y32I4YEz(1 z&ogbJ771?U^RLI~uBPUp;1O{fK58R;6sPUhJYlUZ;#blf=L}{9DZkYaVh(~=0v4&2 z5EE|l)`IIdo>rZSaA5cjferZ@>_)ruw0bZfb4vW{jCrp4aTlK6K03R-+wEZfb&i&~ z7@&_|@6g}UB4>>oY)5Pd@k7U0O7NCdXgcWgubZ5K0Q{eH3b2)KQP<|hQtNSQQ&t&% z{T_XfBFAn?_r}y=#m*8o8#{^|^rk*N&t}NnN{+gPszbD&90^6^Sb=T>P18SCEB%~0 z6-+@4_!UPPUjzJ~KsO;_L_)tOd))*g6;A}hu7_U)@SOk=10$O-rm70~HJv@<=(N{0 zgnFB7cntYUB*M&4e+j={6rsE{j73FnnxXcv)g?y3VBsj{O88Z64sC*dZS1|5wz!dO zdSmJZ`=NAOGWZRV@ZOWKOy`7izy&U+>B(xxMH+4tG3u@i3W|LGb;!hyascC<5s4K{1P0+c!nTZpd&cru zIKTez$B6eH$c7deOPdhSRG|@L%6gr82L4-l{h@g1eSEB48hZf?Nr;XM=g>Uw8tc#?uSN zuVMC)mi9j592|>H20to7R)LX~@vosk3oTgHJ3)u6HbSg>Ec3>eQ^D`kKcW6`O`?o{)y%*G6wG_~tuXIrK*W0TVfCA3{Oj4B3$yKO zS6(B=RZsH6GsZ;;p2Ez|j~`~}t~k7cul)Dhw0;Hu=<1i~9s76P;JOli?G~Fwi>k?s z#oUI1t8RUX7tRF{^Z6G_wWTAYbL$g;Ef5w*JckY0ezKf@W$ActT+WWcokJa30BiA# zdyc>dvt{^2Z&CLU@N3HBGT$-I1c1vfHm#3e)6O$vi)8J@;4(P>AC?%-dGidc4g5?W zzrL~lZq+hSlNCP(EeQuWRN`NR7+S=f1+-F!>C+1Q>x?xFMwS?X4(hew z{MR7ub!&2BI~}ZoskFs7)+pv$W${BvV;~8&OeJq@BpLj3 z@dh21IuVxl{DgGX7!AyFu$_S2gLbQgi5r)>c+`b(+S4z0&ImHj4$X3UHO-N?WH3LE zSPQ^COZA7pq_F|$SI#i$$-t6V!uNBZs-<%c#IIn zy!UYZ2EvA_mWkY9+!__7zG`0+jBMZ z>B+>BeCW{Nc~~1gE+4;k)7{kKM*9rtm(%Q8{gIbp>uzv^74?VTMGtG%&i`g~OQz*^ zCSvHk?sJKMA?unw63tbwX6(*Kmtr42>gqN1@hdG}qyyiYoo(4L`-IsE{2H91i`fGT z#-Xf!9y?Qn$2opTkJ#a%K?s-C&reGDV^}Zf=~;EJ3?DL3vyCTESf77=)!ORr?T_BO zay{)=k+nh3K5Hk?=U=UX@3_tVKRz`ZZeer*{KFJuoNnXuud2YN-sV2*w(3=wmZW@I zV*xA)6zyn$%^~84l1?H2RV!N@Yep^OK-p=UQ3qhCOZ6MCiL*nGC&RlMfM0UCyy_6J z;DW0SzR$n@iH^(8&&=M5ab2JXWq87PjV>rXF28>Ms7Ph^I?=J45jHdt-%#QedSqG) z&wBa={-riK&2mAXFwy5$?&>jfzg6gS0l&7>CIP#8O0-H0nTc3w>|U&i>o?FJIUDnA zze!go*X%R^Qtkns=VJYQ0QgnYEjuznd_<4Cj9-DXjvkkfUwdgx9uRHHfZJ?!27q4< zKjHZ)*3Vbnz-%j9ldWOc+MCk&wM|7-Mg6=Ol+MJgTn%80q8z>s!MSx|DZIT>Ogvpb7cL^D>KK;>G-0wk<4=CMd7e)|2PUD!^WoALS#cHhSdKYVo_`I9t zv5$G{J~C&7d5Rm_-okc%$?5PTh##IyAK*T}UW^~+=^tSEXLC68ZMkd$DWo&>bxV)S z$FCV0=W4@~2pgWwF3N_c2N6Fcod|PR#koHNw9(qGz#>Nn&(Op9rDss57o%S>(a4E> zF@6|YvDI)&m@yWM z=bWCg9d8*+#dFb~G2|M_cDNmr7@&_|<8;7r_Wed2 z#{p22%w3IY0l!tmue$r`U@DT0=J1~H(QKX;CSpiDmEjkdZvx?(oYCeyCv;br(sKd$ z31#^87~`_UC;+usxE)+}c*z7Vm*Lm_n3is?#*9k%EzEbK);YUt3~!z=9iE@Ds+jq3 zvR%PXsF8n1A91be$cK_oWICqMF(1EH)4_puO6O|8(VW-|(T}ET=%9>EV1Pb;U2i|+ z{=QSQQ7obFs(Iit;u_??+AhPdx9lzp)rJ;;4DrKG!(#qbfnSe-(J!a)o5o2V;7JT{ zhVkq2^Iuk%3gz(Y7#9!cb%lcSJf4*B3)z7-H#&{n@sA{2zCRYH~pJ|z-Hv<*;RXG0zT7mdjK%f75 z)qGe+z{2i1J5U35IB*elgDPw7950e5!olAG$+`+W#XVfJ@yGC(9pbLmr;$K;v%0zr)e+q_?Lhedj^gkN+B+YfG76Sg11hN$0g zu0s4UQGs84{wtPPd9Qd%EltLnP``Ra<(ybW{rrrBBa;d|1wKE!So$EC3nePUuS;%E zwsyw&QuVW{0q4k!_vl&GlP$+D)Mm)Gp~RatbD>{Yfo7m$*L!E|YwYji(m}SR}*K<@NK2WqWc1 z)BkOH3U<5!PW`0Us!jBt!cpem|PZz{oz0>JQ;qE!!`=|h3F;Fb6Rv!I=y5L%tg$G7lf}jroKc&gY&Y{jW|nnNFk!Qfr|W#bhXCB z>dxS5=p_tM?m!{Eo}UtaakOyLEngEaIfrNuC6Z9Ihq%5M`7dwJmz`gJSw5(mog0>lFM0f{ zJpV-pMSC8*4zx>USIt@`LiFentQYcMo?R`RUw%xRf{WI|?5EyXxq;qe{#Bm;^3aMJ zTze+j?aUs6TFuP59KYaixS>B=2|G_q)*}qw%KMq^YAJpQCIxKDaDCmrC7(>}Hc*Hd zSki|Wnjb#|?LwO-gyjQU)emLjTVftgglVv=F6tLkm>7;W3j`Zi%$H4ZfkH&t%3vn~ zJk5pk%k54qEs+1BB@{}xI5e#eP)r%c_~D0`o^($mZu2TaRevMHTz|;b5#fnok$(kr z8;LT%`PpL>=dwc{pdMF6{X9YvnK^N;ALLPBT$T7mICh#wR1IbGfe;h1X*RH>{8ts%Z?t=9ub0*0Y+`Egci0{(o&SPHX(kPFXg`q=fConjD!N;mGk=G*niP^=!njL`SC-pHf+m> zEbyH&~g@%b09i4axmk3g*?g1Y?(5^kc8OT-4t@e4jAK0+I68O7l>8w>{rnb(Rk{DRGN zfz+T~%1K6>1hb$h!QhwU7Z?Y;!5E#(cQ%K1q1@v{6ZG9F$1m7~QH0>eF&r!|$C}=w zWiGU`62IK=xY0$=Sc{I{@d$kWi<}56%YWUW?O~82G1}t7uiNM0mjZs3)o-xPgf|#7 zht=MlRxUM3IldnN5yhf@VK^8U_9_PwvnvvgK&2&RxlKX87{3q&busfX;FmVb?9-zD zC*#Kti|3d7VFrIqJu7PSfFy13Cra@{&A;-Yw+szr`Pqo~(nYmA$?-#}{W_P=FIz|t zJ%+I1qeJca^ezaGC|I_`M%J@qV^!mH&_Nt~A#B}246ufc9<*Iz6sMKLdzH_B9c0R> z(@bb%Wm1SAk|tu)UhM(t^IvfnY(nobH9KegO2MhfW2COvCLdt@dVl`n25P>Dljggg#!P|2MU#YfGt2kM;HDR?D9-(-z^Ii4r`>LJEi!cH6wtzDxhf>N_Gdm zgJ5tx%J>)C%ozLp95#eGg&?lrU$XtPFw0(8%)&(+p@(TNbE|UxwSqAYZT91%=!8K2 zi>~BfF&SmIkUzrwoP_W`{0rws2R*fdede4U^b7IBtNM5jb^Ux6c3yWCAp$MvBEyVv zx?1bbt|}f*PjOsba&=*VE4EJfouCk6hMsA7hQCEz|kKpisC zk3K)aj=e3OFXLZqGs<1x|!b8*-JyYoW|L=wa2qXV|mPu@#|g zZ!6XdB&!0y4o87s1sk59m5cCMD*dEXzrlOe#VB}9+XJ)GFmO3_hl3~ZsPuOMzYgmj zHk`59KG)_rE(!sCmfm{t`7iWY75dzRFDc^JH1?_JV@(y>?c!H1sw znwf?Q{uO)HIw;PlhgBq#{!u*&IW&HOH^sR>h4ahI{>NboT8AK*Vi0y7eSXW+ufqA| ziS!S|8G1AoNvB6ZoH>4o*{I}Sv^@>`d^Y`%8=47zgbX!q5bD-_7Z`dHd6W}@)pTG57_}+!~r}S$gw{Ug%Caelh>DK26Z3 z8AsDD4P=Z3mH6fEc*JNmfnO_P_gp()9#3m4TFieH?~mGz81=(qN#9@WG;ZX5csYK7 ze*w{2nRZQV$fa7vRuq@wNqPK`Vtv;x?ycgz+PAH-{th0O400@vr@6qtVh)$hqiloY zhx&Z3?tJn74d!2g?*6NGH@t>xZJy{4pGR$mW-b-+Lw*wCGtNioV1H=s;9~GEJ+9XR zN&c?l`DKk?2(Lxccf*x^5xWllYDN6e!!H}fU284-FIs6p=srIm&^lN+znqL_QlmK5 zy;pAFBFsPYW}`fQsQZJs&2e{8Zz!|UbPD|`iyv~IcjVZGkX;*o-%Et)KELSML+}2m zE-q`qQp!#-7yL_(xq5yo@T*;+)_MwdtYzzU`TCDbzKDbI%a%NLkAFd%J_fsSFaX+h ze=0}?yHOTD1YV2<`>S69WIJP@>`yh-Z=v7i{alv+dPpsw3U2^@{aP)YNo)vSLr($G zD)V2kkQ3qTAeVAIegq~3_irS%?o{%xw?Mm|a8S=Si9jM(h9Dj_V@#Ce7x-6FblF?X z8&)T7OnnGvlXZT%l7CHDePTE5pynfJyp-udH znC`uH%>deL)AjSP8+uK-enaz0M{E~JWzsb^mPr?=RKyRNf59Gp!^E``ba%F43PjB7 zv(d+Uqj-N*i)(xXhH(znx>y1bG4AtJwTEBuH_n@j2v083|I9362c}ydQwv8!(<|fN zdhz`m4<{lo1Rr4jMUfNM-c(F&jkb(o6plA`jbCwRD`p=1kU+%ZH6gVJo2_PPss8YO zd=%kp2G3{#z!9rPlEPhZxKuw6*mB_IdOi{pu|SCaL3X;cxo%MR8T`w-Af8x{HZboP zPDg&_CHgzyS8YE?agxVg;9u5kkAHQJgLcJS+*9Gka;5rttc7gvyXCCrUy&o$yxw+m zFwi_9b7l4OluO1`O&4tVLCQ}@7uMWGs?T`==dzcQp66uXdODP9%MW|}i^GQE`!u8= zRNTJ-wxwprheOuez#Z_f?c_3$mDL|UMi+Z~+#P@|$@is%^Trq1134Nlt3TwE9o_Y7 za{Aa8cz4cNO-HHoRNI4t3%vE>`uRhFHVIzIv5RIG@s_#fX3CB?b(YRAf1e&yZBFp( z^O=b4$=A$?FNr@@%~R-eybQl2xM!|a=~Z7z)783?!>tPH=-amIO<(TY1CIGojjxRy8| zx2v-Fq4jXGJ%O?f;1_{60i+Ob(ZXf%!&`8E8BSp93d_EEZru)YK8ePq_@RYkU#o%( zacp@%?mvv)Rx?LzV!Q18buW`Vwf5&*Se%|9>$bU_68`;?x+lj*Ut@Z&KJ6x(i%!dvKiu_AO91B59C#~*V z07RSZ&9lybDd1OJjGFstolM*+cG9N6e@s|*web8y55G{6=yA_MX-$X+5ZC%oGM!14 z#}8E{&m97uE(QN8p8w+d!*fnI+e0Rg3|lS#0m2gb*Waqhf2nQ@{vo>}oc}W7n4g1~ zs9=rhunyw>4GZ3TCXtUY?Q#_Mp;tej$cnQ1L)f}J#>Kw+{b*1l5n$X$(VV#1(=X;< zU@n<-zj%oS7o-OM7|r8-xWTiT%)fq}?%Id2A@i@E=FphYN#p8cSv>h2!QJuhk8~LozvRWtqGa&rIR1N4Elh^S8m=}`KktNQ2L1ZO-SWZD zH1`E{Si?af#~&JNQ=9EQ+_3`xvT3(@upURll99Qg2gQ$NRMs>Ge!x{9mHbO0s%Eo) zNKs`S62IZ@RS4X5r7UmV8Nc8jus4V>3oTQjL*fQ{JpltUU93Oc30;Cs*oihgt|c<8 zf|K^o9q8A7C|aDy#r*3+v?mpwYUr{!Y-mjnF2#YD?qqD*uRjFq#kBz63i5Y7NXQY{>SB475H^&DOTsGaXydx z4^N;Faf!eI3idaqOZfFfGV+S`U2#DHj;EuCQT$fmUj^*;_*aC|lp5DAI09ZxYXUscL~HJuCeI6Ay` ziMre!`kXI)1i0K#fnPe>a|W=!%tf3H=cF)EK)n+GvT?fH0^2%o4^szVwEkIiYytd@ zo66&dB#rSnu{E#A}%)bh4 zB9pyao34l-u0pi1QEUSId87uy74bs_yJ59Nkl*QcP)MJLaDc*yAA0(wozhOUDm&6cn^8(EU5xGvH{?=r(2XL&Q=d%(f87pmsZr_@Uqv zVVCnS-9zNR(C26z0a6xV{f>J2<(*&N%TIXhzsLBFB)jssXLx;<)Hu5!$s9 z^E{@x)n)t(Cl^Lh0@@|R2aMuD<_i1*nqxK`o*%##)|3T2so-A`u&v$&ifNa%uYT;F zyVD;3^7O*vUs{`HL#?pKBB05G2-IWOmGdtLSiog|TewXUF=5`=mHg}N?EZZCJ)Qs3 zfy5U`U&K*Szj5x?$4{WNm0=6#q#iUzVLaKkROG+hMf*UddkFS0AA3p1qZY9}EX#jA zLC9pmKNRT3Xe}4a{7?Jx`a^adhdro;4z3c5#_-LTrfjykmBrE+`Y7|)F)fP%y<6xEMUkZ%OYcuN}UC80J@z%7! zzZ{H0AGdLNuVQiYJg!1~gY#b~y`vq|E$`^H2(hzNu+MG@zd%nY%xx}lEnmO!l658j z8g~|vb;@85sKX7pEnR55$lYFsU*ojZjL7t1f$|}Rw9YQz&J$4R%KF2BI_E~1_aUei z2qpo)*lEX;B7W7~FA(~WfUOY+2QA%b28OY4{;LeXo=$W=4_2~=#uVoHC&8FHN2qgP z+$r)e9aa15v&QXkmSFJTGR|bKh#&H791R~fs_1xM_vo5P!u=hR@H^(#_}ABrKh$6+j> z%?KoVKsL}eU5Q^~6sK^!!Aw1m{S)T=*OLzFA>-xs8|UQ{CYA}jlG9!jEbs-7)8+LW zCz74p09zae?>rT4MFR)08{kGzmrD4wN*(m7%+?G4$CUs%&J{5r9!GZz~{nbt)Z{LnuH z8vVmuKtB@C)lT9ol6)l{``t@5g@57vpSY__bB;O-6Ao zZHnV#xYzns1j@91_VZt+(C5F{bmRQ8i~{4FLYt?G`en*NKL2GyzW`e<1h<3*tfq(` zuAs50_%g(4PiMzQ5I@{Dcv|3~E?aCLzg|me{xtzexbm?q)w&NV)geiOD86JEkE zfwFlS&J2Q1MqwE5HC|Jf*Kg$PJ0zIXk(Co1BB1S~4V?N05v#y2ghQ2eznGGbBx0N4 z({lY`OBTY3B7WV0h?(9|&!BWXHjS+{8_tsRJOm%VY#O3w$C3i)zX)f69Z~Nna%}8p zKYrLvZ>U8{-23!A;)h*MXhX0D__aR|{`I=hYieDWWj8~?0%*eWd`Hfh)Eg2{%HoGQ zTFA#>aY)AjY`x1s#(6ZKe`Vx$+CxC1MqJAV7`;7piiY|aPiqSNiwNhxY+&v*+8i?R zQL=i1G1oyGB8NNVG`h4k>11$T*HQ;%jDlUTErmFn&%bc~OLp$Wsnz-H^0_!8eV@Z2yJ})nx=7eFaVm2ab*DtV}nufWo=vBRhpv> znBTY^HyH5D&zFy8+ud6bf3}Au{HUDB7x9Z|tBp0~Y7GPuX}BKFQd)U!iux5o2_Me2 zp{#^-aD_xEWJ2*MT_=U20#eqcTU#dAD+<@Pb+$LdhwS3MD7wR`$z}6W$B6IGVEN+eECSLtU z6YeU16Ze>LTokZ%UOdS4^RJZf3)l97e}!{{j=~nrhxlHsCz*d`4L^SP9eI$NcLtG_ z;6yXS7CWxG{?Nbw@Lu&$7X0h$qF(Akj*ZpVQ=!l0@k4SZc$xZOssLLxF>CO&u0Je~ zAL16tF!EpQ$|_uV5;dQ7!N2ll@k7r_DB~5Uh+o3He*@M=?;p!Xc#RH7Tnjm0>X=o| zK>Uz8x!^%~drZMUMA*;;x9WhPuhhWbJ?TZEU0MC%JZ{s+{L9g8>}~UCtGuHAP=d=e z3d9eQ2!l|CHqPbshYI|i%MNwTKi~xXqLX6e*B@SoShr_Vbet{8@xz)6DFhNv`SpiY zXK)KKR)_2Tcw9Iy+)AIM&OQib^@m(-2sLNeYL7;7YlTQFoCr&l>JKMjF*W}R+2}K0 zyV1CBQxvC_EAHRWY}6)>uYy-TU(-mNZr26ge*Ew$^ydKZG&oP`{vcqh=Nb4F_4Etz zL;aD)T)h_T8zU|XL@V(x|Fp8B*OZBv$@ML(-Lm+hLbYK!n2kgcKWyi;*8&Kzep99R z;in1umB${t3qs7r-Ky_je~8`tHRO_bKL@#f9)*7sDC&!P`ZZ!*<6Khvv*Gi>?@JWY zXK{Y{8aPy_Kip_6@bnAy8|vJ~?i1k$2Y;7sf`ZEE`gsK_QHEdo8uJ<0LlkD`@izqu z_YsYi@M|ZBN)u9p9P%0-LspD*3N@gEh~#4eb@W^}AWS-5|L9_LX^m*R)5 z_$VULCISJR5sHR@He&c?^@q4lpR$DOl~{n?u(hHkWwfmRFo|wJzowyI-1%B2@_Z2o z_8~YQ+r#X2xSpMsqYWZ~+~=?xoVE%;=HnNa*&u$1Yf^|00LD%keC`j4FdnJK3)en_ zu7Wq&B1GE|P(pm(fuk4+U)MHes3Nh#bk;i zPo=X0xd7hJx$3xwUtaxT=UW&>3}qV!rtx=|TIop@SGa$kium;y56A5|3G=QmtjG+{ z2rw7fF+ErjKkPoL@g3KpPr#9O@Iu(1oY7dsFU^Hr?hkE~&Z*i5(fO^XfXnC0@?VPc zm6|H194BuzMM5;-Vx*6WKOsHO#ri{QEg;}-)%?yZK3TpQh!)+L`UXi}3yvQiB+$vY z5fVqO_$VI)gCEr|SI2|53T^`Yx}TSlF_+`mj~~PKo5bb$UHdw6My5K+ZFK#H>U8z| zaOea4%cLU&;&W)Qz`wlu!)Dj&gYIy?pr#K8i#F39Y5h^Le#5IjY|f)F@Job5SRN*& zQ*2l395AvxIs9E-{RXbih>K54)JLtlc`**K4+fkG%vc%!dXE;~1Mgx3>Y8vJ4PaEA zp!g7;lka{0h)NgPV0mijY+xhLg2PduLfdhCF^zmywC>6qn z93KNVC2KXw64+xIY)G5?%=d3>4YbQ>rdr$Zev9i5Hzjcx>RV;`FFAnoUwC5j{fDb& z^^5;OI1l?7;9wy>f4FuXDcNP?{8zI%!vf-CVvH6BaKVV5|6=~tvmyN3#=GhQ3mM}L zw)4pj+`mz+_dMsnfWm#V7bEO*TrQF!kTrNf5L{!vr(a9$)7dm^Jg2>AyMj7^h~dU; z=vTU!|4LMWe??nl4Tq{WdCE9S;2;u#j(ShOn&u3_OTnM&;JWTD)zCqnADthx{rflS zCdF~WeGYtW)N!is2jP7c9RnZq^Iu$l2>!J`ygN9VI%__j?BFxbs32XBL&|0K8-?5E z9UE!EIynE17+b*yI?C{iEr7B{us3j^UJd6(v~l}2c;c4hm%foq|Jg4U9Shb)V(``X zy13}3#?!BC;6t=UHfOBq)c--7CCVf=M&>H^$E>fF@T(hE>makOZt<*o7-bv5505@~ zbWuLmRl0v;M4QSTo_;N%9dY9saW0CAjt2(|_$8~}r3Rj z9k#b#BXt>iz{ZstKf_vVw{b}n4yvF(IC1IcznW+#%0!WazIF5BBvRoj*x&n-#cg0S z3;ZkDm}mFN4zhX2Qnmz_rr;ZcnM32s>JQ(;pY+->f>At42iArkH_kgx%jFZWhXwwX z!38nz$?nveea0{8oCN(xfK;4wwIwO=FV27AdN&syK918OngUSha-K`~91Sgm;}K`w zQRYUvTXv}6dhsFJAtUUx$IM|D!L(dazf=|27W7N|S}@(o;A60v!%1$Fay(mSnRplc zJbj-h2i$H!ErjM@QPdv>_^Swia|YfZJR+o(u^|!A&KYyTFP0y{xEvq9gtii{F@g97 z_!plix>Na8iL<=t*Eu_89Am`HV z!l5eWziu2?9KK!(dSVXJ9WKVs*<3hO73aT(@uVq-kA~^@Jihan-h0a9hisO4k3Fsb z78?ZZRAfu>!`&LcumlDEwe6J_a3Xj?EXV$NnLJR9K&G&{0oJMER@}U z_?Tc^p3St2h0*}?W%nN{3}=kzhXohw&b$%=7ai&KO4R*%7S|rW2u*nj0{m47^ivkf z&wnX&m8&&gmW4jQL|WJ&{QTD$RFDF@;{rl)>{_Te|E1Gj?8>r$ZWM%MC4M2A0SI9H z@`R}?;)jf1F?RI}<#6!%d54$ruk}LX7tFF2K(bz?hb0p++Av^ z&9fGCp9}r*`PUF;zKe%5T3iBuE&Tg8vU*w=){FDQi`z-S3y@T*-#xqiM-cF2Vs zz^{A>zdZed#V*$`1xL}p{}A;Xj1FVGpIPwsv*us8+1|66Bi8JrKUH04fM3r-Sk7gK zFM9lIri5SjyRanVtTHUH?z}($Dwsjv9%@9p?EF`u8-9N@T3vDe3#zA&Epwkw5eGl7 zJpZLlNRZ5%R{@xyX6IBoEa96FF#;9qoXh0!lxvOi+h9?`J^*Fa&6jDV&Il2iz zIgq5abTw5eDhgFBd>&7ly*9@ZRbLByQGLOgGgc{l&Z#SY*D;A-ZzB8|X@1XTLb1;Y z6aK5}b?kIeP^RNOQN=blSM?6`Wp9hlsVNE~AP4$#jj=gY99JmtS^AtiCtkJM6Z#>1 zfmXS7aZ~!^A_?_#8e@k3IS8YLLf!W%34wQu4A$) z8Tc(d8~AhM5ab+n)%pO>&m6zc(n#Xw)Fo7_3PM%de^MM*uNfZ*IV&yus1{ae0e1)E zNpTJp5x6=TL~cod_0dlp(mzZsN%SKi{lnVg$bbefSw6|T)k#;pvk$(!lf7mA!RNenB}ZY)cI|aRciyEr_ZT5 zB_Yt{J(0!rst&ABeaXhAsw)L5jSJ)o=@aZ#PiPbYQ>}39#HwPOV>cO}Q74lhZK^F7 z2K1BScOAQ_>dt{LCcn^B_f$cc;|p$KEpEKYxKq89{8&@%Q@()4r;>l%be&u0&GU`L z_wcW8O04@r)10Ro7pf56-00SM`bDCsO}uxsqNM`;Lo8G&?=?i#fpuza^6I9l&1J$s zBKi5IImI?N%Budr7wHQ$$JN4TsS!feYW<|}T{repQZ+f%yggaKh7}Mjexizf?lm2( zV6SDTw19<6H9A;9JSnxQf;Ks~S#tyn@y~_k^j1mtHe3Z>Ma6L$fj;-P_?%GH$}&OS z=FG9GWN};;^^&dCmxX}oU`O{-6F*@c`7?T)ih59 zlEPD~tN01)S7BVoq*0+?S3}VP{Q*A0UaRmu-j5j%glHw%E(-xIB+aybQf#92OP1@` z1htaoXIL~LPmd=eo|;;t-8IgN!`?3mu8_#3&}Z6Ll8>uxv{VAHuH0VI$03r z$Q6AyKba$o-^G`3_F<{@ZJd3nIi7GEKPfh$nt)I;Ye!8j>_J_x|HN+-GCoTu-H*m= zS8HJ&goe*n?c^t=?YbOjUdjT?wP5Cxu3q`8D{Fzv(t>RU>s*3WX+?8zODG;(^sy2vSoSMm8$PWhw$RnKA&-;~&fQ)rO!gT9H`Lid1T2 zCId}$6V)jQyNZJM#Cwk&kRnODXgo4<*`NiOc>IehtPX z2=Qpgu;}O&f~%Y8@A6^vazPcoE8ydDstC}&puG3=1$-<1ed(+XuL&8GJeysx4JFzzy3_5s=~w2{Em_rYZksbv9E0ymj=4$MvdCUE%* z!S!Aw12>5~)$Zg+nr1z9g-~eISAokK=u7y1v^a<2pVKePRE6)NYFek7lGROt%~uG8 zHgsds+!(l?epA)uuMmoR1M5|R%RWFeE*HKF>s5iv8lcHT>S7!0Lj<5v00dv_a)Aot z0#5b0jqxf*x61`t_%5JXIWGGEt#P@~<}zIN0h)#4cLij*e))>#35ECkOZugX-<33% z24>SrW#_LD3Uk;;(0m!P4Nh8IF1UsFP!%ndp+2LLj$I)fEq>QZGkK&bep9G!@Crc{ z_l9IRE}0Wiv@2TBFOq=-!!e_<#^nk@6~7C6=;NARV{quX-;Z5$f zsTmS`XhbfsuN3??#c`D}Fn@r>_q@-v6ep_q9-FR;2gd0SXs+uEdgk%usD~IBVDYzD z7%xsZ3v-Wp-$fM~I#ASEEtIXPDt@4N;k@6`g5Rd(!0?Ot%5VL{LdZ28@4x<^|G%s2 zUuY8!{|g`fzx7eirAwDwe)*Mx|CLvMcJ>9YS_6*^csKgBlhr^`j9et%tl z-}hANa>b~8TZh@c>;rVde^OYBN?k54P~qPXbou>E!T-1Ve%TVe&wrvUGQis{PQ9Oe zpN~Iy^uY{#FasaVzy~w%!3=yb10T%5zxf%^{t_G}_)GHN{A7GE0Uyl32Q%=&416#H yAI!i9Gw^S229V`PB1ioyP742$5hpq8Uo#1X_EmAp^6zm)0scKwN=p@9@_z%Y%!GOX diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index f637abf2..8233960f 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -197,9 +197,9 @@ begin end 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) + // 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; 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; -- 2.39.2