diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index 78b2d758..750f2ba7 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -90,101 +90,122 @@ usb_request_status_t usb_vendor_request_init_sweep( void sweep_bulk_transfer_complete(void *user_data, unsigned int bytes_transferred) { (void) user_data; - m0_state.m4_count += bytes_transferred; + (void) bytes_transferred; + + // For each buffer transferred, we need to bump the count by three buffers + // worth of data, to allow for the discarded buffers. + m0_state.m4_count += 3 * 0x4000; } void sweep_mode(uint32_t seq) { - unsigned int blocks_queued = 0; - unsigned int phase = 1; + // Sweep mode is implemented using timed M0 operations, as follows: + // + // 0. M4 initially puts the M0 into RX mode, with an m0_count threshold + // of 16K and a next mode of WAIT. + // + // 1. M4 spins until the M0 switches to WAIT mode. + // + // 2. M0 captures one 16K block of samples, and switches to WAIT mode. + // + // 3. M4 sees the mode change, advances the m0_count target by 32K, and + // sets next mode to RX. + // + // 4. M4 adds the sweep metadata at the start of the block and + // schedules a bulk transfer for the block. + // + // 5. M4 retunes - this takes about 760us worst-case, so should be + // complete before the M0 goes back to RX. + // + // 6. M4 spins until the M0 mode changes to RX, then advances the + // m0_count limit by 16K and sets the next mode to WAIT. + // + // 7. Process repeats from step 1. + + unsigned int phase = 0; bool odd = true; uint16_t range = 0; uint8_t *buffer; - bool transfer = false; transceiver_startup(TRANSCEIVER_MODE_RX_SWEEP); + // Set M0 to RX first buffer, then wait. + m0_state.threshold = 0x4000; + m0_state.next_mode = M0_MODE_WAIT; + m0_state.mode = M0_MODE_RX; + baseband_streaming_enable(&sgpio_config); while (transceiver_request.seq == seq) { - uint32_t m0_offset = m0_state.m0_count & USB_BULK_BUFFER_MASK; - // Set up IN transfer of buffer 0. - if ( m0_offset >= 16384 && phase == 1) { - transfer = true; - buffer = &usb_bulk_buffer[0x0000]; - phase = 0; - blocks_queued++; - } - // Set up IN transfer of buffer 1. - if ( m0_offset < 16384 && phase == 0) { - transfer = true; - buffer = &usb_bulk_buffer[0x4000]; - phase = 1; - blocks_queued++; - } + // Wait for M0 to finish receiving a buffer. + while (m0_state.mode != M0_MODE_WAIT) + if (transceiver_request.seq != seq) + goto end; - if (transfer) { - *buffer = 0x7f; - *(buffer+1) = 0x7f; - *(buffer+2) = sweep_freq & 0xff; - *(buffer+3) = (sweep_freq >> 8) & 0xff; - *(buffer+4) = (sweep_freq >> 16) & 0xff; - *(buffer+5) = (sweep_freq >> 24) & 0xff; - *(buffer+6) = (sweep_freq >> 32) & 0xff; - *(buffer+7) = (sweep_freq >> 40) & 0xff; - *(buffer+8) = (sweep_freq >> 48) & 0xff; - *(buffer+9) = (sweep_freq >> 56) & 0xff; - if (blocks_queued > THROWAWAY_BUFFERS) { - usb_transfer_schedule_block( - &usb_endpoint_bulk_in, - buffer, - 0x4000, - sweep_bulk_transfer_complete, - NULL - ); - } - transfer = false; - } else { - // Account for having discarded a buffer. + // Set M0 to switch back to RX after two more buffers. + m0_state.threshold += 0x8000; + m0_state.next_mode = M0_MODE_RX; - // Disable USB IRQ whilst doing so, since this requires - // a read-modify-write, and sweep_bulk_transfer_complete() - // might be called from the USB ISR while we are changing - // this count. - nvic_disable_irq(NVIC_USB0_IRQ); - m0_state.m4_count += 0x4000; - nvic_enable_irq(NVIC_USB0_IRQ); - } + // Write metadata to buffer. + buffer = &usb_bulk_buffer[phase * 0x4000]; + *buffer = 0x7f; + *(buffer+1) = 0x7f; + *(buffer+2) = sweep_freq & 0xff; + *(buffer+3) = (sweep_freq >> 8) & 0xff; + *(buffer+4) = (sweep_freq >> 16) & 0xff; + *(buffer+5) = (sweep_freq >> 24) & 0xff; + *(buffer+6) = (sweep_freq >> 32) & 0xff; + *(buffer+7) = (sweep_freq >> 40) & 0xff; + *(buffer+8) = (sweep_freq >> 48) & 0xff; + *(buffer+9) = (sweep_freq >> 56) & 0xff; - if ((dwell_blocks + THROWAWAY_BUFFERS) <= blocks_queued) { - if(INTERLEAVED == style) { - if(!odd && ((sweep_freq + step_width) >= ((uint64_t)frequencies[1+range*2] * FREQ_GRANULARITY))) { - range = (range + 1) % num_ranges; - sweep_freq = (uint64_t)frequencies[range*2] * FREQ_GRANULARITY; - } else { - if(odd) { - sweep_freq += step_width/4; - } else { - sweep_freq += 3*step_width/4; - } - } - odd = !odd; + // Set up IN transfer of buffer. + usb_transfer_schedule_block( + &usb_endpoint_bulk_in, + buffer, + 0x4000, + sweep_bulk_transfer_complete, NULL + ); + + // Use other buffer next time. + phase = (phase + 1) % 2; + + // Calculate next sweep frequency. + if(INTERLEAVED == style) { + if(!odd && ((sweep_freq + step_width) >= ((uint64_t)frequencies[1+range*2] * FREQ_GRANULARITY))) { + range = (range + 1) % num_ranges; + sweep_freq = (uint64_t)frequencies[range*2] * FREQ_GRANULARITY; } else { - if((sweep_freq + step_width) >= ((uint64_t)frequencies[1+range*2] * FREQ_GRANULARITY)) { - range = (range + 1) % num_ranges; - sweep_freq = (uint64_t)frequencies[range*2] * FREQ_GRANULARITY; + if(odd) { + sweep_freq += step_width/4; } else { - sweep_freq += step_width; + sweep_freq += 3*step_width/4; } } - - nvic_disable_irq(NVIC_USB0_IRQ); - set_freq(sweep_freq + offset); - nvic_enable_irq(NVIC_USB0_IRQ); - blocks_queued = 0; + odd = !odd; + } else { + if((sweep_freq + step_width) >= ((uint64_t)frequencies[1+range*2] * FREQ_GRANULARITY)) { + range = (range + 1) % num_ranges; + sweep_freq = (uint64_t)frequencies[range*2] * FREQ_GRANULARITY; + } else { + sweep_freq += step_width; + } } - } + // Retune to new frequency. + nvic_disable_irq(NVIC_USB0_IRQ); + set_freq(sweep_freq + offset); + nvic_enable_irq(NVIC_USB0_IRQ); + // Wait for M0 to resume RX. + while (m0_state.mode != M0_MODE_RX) + if (transceiver_request.seq != seq) + goto end; + + // Set M0 to switch back to WAIT after filling next buffer. + m0_state.threshold += 0x4000; + m0_state.next_mode = M0_MODE_WAIT; + } +end: transceiver_shutdown(); }