From f8b316a34063a037293748a2934221f93102c241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Hamal=20Dvo=C5=99=C3=A1k?= Date: Wed, 21 Feb 2024 23:41:29 +0100 Subject: [PATCH] Use DMA-based full speed mixing --- src/CMakeLists.txt | 5 +- src/main.c | 735 ++++++++++++++++++++++++++++++--------------- util/bridge.py | 58 ++++ 3 files changed, 560 insertions(+), 238 deletions(-) create mode 100755 util/bridge.py diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 75dd38f..a3feab5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,7 @@ target_link_libraries( hardware_divider hardware_dma hardware_pio + hardware_pwm hardware_timer hardware_vreg hardware_interp @@ -29,8 +30,8 @@ pico_add_extra_outputs(pico_sdr) set_property(TARGET pico_sdr PROPERTY C_STANDARD 23) target_compile_options(pico_sdr PRIVATE -Wall -Wextra -Wnull-dereference) target_compile_definitions(pico_sdr PUBLIC PICO_MAX_SHARED_IRQ_HANDLERS=8u) -target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_ENABLE_CRLF_SUPPORT=1) -target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_DEFAULT_CRLF=1) +target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_ENABLE_CRLF_SUPPORT=0) +target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_DEFAULT_CRLF=0) target_include_directories(pico_sdr PRIVATE include) diff --git a/src/main.c b/src/main.c index f26096f..76cc5ee 100644 --- a/src/main.c +++ b/src/main.c @@ -26,9 +26,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -36,29 +38,47 @@ #include #define CLK_SYS_HZ (250 * MHZ) -#define CLKDIV_RF 2 -#define CLK_RF_HZ ((unsigned)(CLK_SYS_HZ / CLKDIV_RF)) +#define BANDWIDTH 8000 #define EXTRA_BITS 8 #define NUM_SAMPLES 128 #define LPF_SAMPLES 0 /* 4 */ +#define IQ_BLOCK_LEN 32 + +#define XOR_ADDR 0x1000 +#define LO_COS_ACCUMULATOR (&pio1->sm[2].pinctrl) +#define LO_SIN_ACCUMULATOR (&pio1->sm[3].pinctrl) + #define LO_BITS_DEPTH 13 #define LO_WORDS (1 << LO_BITS_DEPTH) static uint32_t lo_cos[LO_WORDS] __attribute__((__aligned__(LO_WORDS * 4))); static uint32_t lo_sin[LO_WORDS] __attribute__((__aligned__(LO_WORDS * 4))); -static uint32_t rx_buf[LO_WORDS] __attribute__((__aligned__(LO_WORDS * 4))); -#define IQ_BLOCK_LEN 60 -static queue_t rx_queue; +#define RX_BITS_DEPTH 10 +#define RX_WORDS (1 << RX_BITS_DEPTH) +static uint32_t rx_cos[RX_WORDS] __attribute__((__aligned__(RX_WORDS * 4))); +static uint32_t rx_sin[RX_WORDS] __attribute__((__aligned__(RX_WORDS * 4))); -static int tx_dma = -1; -static int rx_dma = -1; +/* rx -> cp -> cos -> sin -> pio_cos -> pio_sin -> rx ... */ +static int dma_ch_rx = -1; +static int dma_ch_cp = -1; +static int dma_ch_cos = -1; +static int dma_ch_sin = -1; +static int dma_ch_pio_cos = -1; +static int dma_ch_pio_sin = -1; -static volatile struct status { - unsigned sample_rate; - int gap; -} status; +static int dma_ch_samp_trig = -1; +static int dma_ch_samp_cos = -1; +static int dma_ch_samp_sin = -1; + +static int dma_t_samp = -1; + +static int dma_ch_in_cos = -1; +static int dma_ch_in_sin = -1; + +static queue_t iq_queue; +static int gap = 0; static void bias_init(int in_pin, int out_pin) { @@ -71,29 +91,30 @@ static void bias_init(int in_pin, int out_pin) gpio_set_input_hysteresis_enabled(out_pin, false); gpio_set_drive_strength(out_pin, GPIO_DRIVE_STRENGTH_2MA); - const uint16_t lm_insn[] = { + const uint16_t insn[] = { pio_encode_mov_not(pio_pins, pio_pins), }; - pio_program_t lm_prog = { - .instructions = lm_insn, + pio_program_t prog = { + .instructions = insn, .length = 1, - .origin = 0, + .origin = 31, }; pio_sm_set_enabled(pio1, 0, false); pio_sm_restart(pio1, 0); + pio_sm_clear_fifos(pio1, 0); - if (pio_can_add_program(pio1, &lm_prog)) - pio_add_program(pio1, &lm_prog); + if (pio_can_add_program(pio1, &prog)) + pio_add_program(pio1, &prog); pio_sm_config pc = pio_get_default_sm_config(); sm_config_set_in_pins(&pc, in_pin); sm_config_set_out_pins(&pc, out_pin, 1); sm_config_set_set_pins(&pc, out_pin, 1); - sm_config_set_wrap(&pc, 0, 0); - sm_config_set_clkdiv_int_frac(&pc, 3, 127); - pio_sm_init(pio1, 0, 0, &pc); + sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1); + sm_config_set_clkdiv_int_frac(&pc, 7, 127); + pio_sm_init(pio1, 0, prog.origin, &pc); pio_sm_set_consecutive_pindirs(pio1, 0, out_pin, 1, GPIO_OUT); @@ -113,22 +134,23 @@ static void watch_init(int in_pin) pio_program_t prog = { .instructions = insn, .length = 1, - .origin = 1, + .origin = 30, }; pio_sm_set_enabled(pio1, 1, false); pio_sm_restart(pio1, 1); + pio_sm_clear_fifos(pio1, 1); if (pio_can_add_program(pio1, &prog)) pio_add_program(pio1, &prog); pio_sm_config pc = pio_get_default_sm_config(); sm_config_set_in_pins(&pc, in_pin); - sm_config_set_wrap(&pc, 1, 1); - sm_config_set_clkdiv_int_frac(&pc, CLKDIV_RF, 0); + sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1); + sm_config_set_clkdiv_int_frac(&pc, 1, 0); sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_RX); sm_config_set_in_shift(&pc, false, true, 32); - pio_sm_init(pio1, 1, 1, &pc); + pio_sm_init(pio1, 1, prog.origin, &pc); pio_sm_set_enabled(pio1, 1, true); } @@ -147,11 +169,12 @@ static void send_init(int out_pin) pio_program_t prog = { .instructions = insn, .length = 1, - .origin = 2, + .origin = 29, }; - pio_sm_set_enabled(pio1, 2, false); - pio_sm_restart(pio1, 2); + pio_sm_set_enabled(pio1, 1, false); + pio_sm_restart(pio1, 1); + pio_sm_clear_fifos(pio1, 1); if (pio_can_add_program(pio1, &prog)) pio_add_program(pio1, &prog); @@ -159,44 +182,87 @@ static void send_init(int out_pin) pio_sm_config pc = pio_get_default_sm_config(); sm_config_set_out_pins(&pc, out_pin, 1); sm_config_set_set_pins(&pc, out_pin, 1); - sm_config_set_wrap(&pc, 2, 2); - sm_config_set_clkdiv_int_frac(&pc, CLKDIV_RF, 0); + sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1); + sm_config_set_clkdiv_int_frac(&pc, 1, 0); sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_TX); sm_config_set_out_shift(&pc, false, true, 32); - pio_sm_init(pio1, 2, 2, &pc); + pio_sm_init(pio1, 1, prog.origin, &pc); - pio_sm_set_consecutive_pindirs(pio1, 2, out_pin, 1, GPIO_OUT); + pio_sm_set_consecutive_pindirs(pio1, 1, out_pin, 1, GPIO_OUT); - pio_sm_set_enabled(pio1, 2, true); + pio_sm_set_enabled(pio1, 1, true); } -static float lo_freq_init(float req_freq) +static void adder_init() { - const float step_hz = (float)CLK_RF_HZ / (LO_WORDS * 32); - float freq = roundf(req_freq / step_hz) * step_hz; +#if 1 + const uint16_t insn[] = { + pio_encode_jmp_y_dec(1), + pio_encode_out(pio_pc, 2), + pio_encode_out(pio_pc, 2), + pio_encode_jmp_x_dec(2), - unsigned step = (float)UINT_MAX / (float)CLK_RF_HZ * freq; + /* Avoid Y-- on wrap. */ + pio_encode_out(pio_pc, 2), + }; +#else + /* For debugging. Consumes at full speed and counts at half cycles. */ + const uint16_t insn[] = { + pio_encode_out(pio_null, 2), + pio_encode_jmp_x_dec(0), + }; +#endif - interp0->ctrl[0] = (31 << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) | - (0 << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) | - (0 << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB); - interp0->ctrl[1] = interp0->ctrl[0]; + pio_program_t prog = { + .instructions = insn, + .length = sizeof(insn) / sizeof(*insn), + .origin = 0, + }; - interp0->base[0] = step; - interp0->base[1] = step; - interp0->accum[1] = UINT_MAX / 4; + pio_sm_set_enabled(pio1, 2, false); + pio_sm_set_enabled(pio1, 3, false); + + pio_sm_restart(pio1, 2); + pio_sm_restart(pio1, 3); + + pio_sm_clear_fifos(pio1, 2); + pio_sm_clear_fifos(pio1, 3); + + if (pio_can_add_program(pio1, &prog)) + pio_add_program(pio1, &prog); + + pio_sm_config pc = pio_get_default_sm_config(); + sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1); + sm_config_set_clkdiv_int_frac(&pc, 1, 0); + sm_config_set_in_shift(&pc, false, true, 32); + sm_config_set_out_shift(&pc, false, true, 32); + pio_sm_init(pio1, 2, prog.origin + prog.length - 1, &pc); + pio_sm_init(pio1, 3, prog.origin + prog.length - 1, &pc); + + pio_sm_set_enabled(pio1, 2, true); + pio_sm_set_enabled(pio1, 3, true); +} + +static float lo_freq_init(double req_freq) +{ + const double step_hz = (double)CLK_SYS_HZ / (LO_WORDS * 32); + double freq = round(req_freq / step_hz) * step_hz; + + unsigned step = ((double)UINT_MAX + 1.0) / (double)CLK_SYS_HZ * freq; + unsigned asin = 0; + unsigned acos = UINT_MAX / 4; for (int i = 0; i < LO_WORDS; i++) { unsigned bsin = 0, bcos = 0; for (int j = 0; j < 32; j++) { - int sin_bit = interp0->peek[0] >> 31; - bsin |= sin_bit; + bsin |= asin >> 31; bsin <<= 1; + asin += step; - int cos_bit = interp0->pop[1] >> 31; - bcos |= cos_bit; + bcos |= acos >> 31; bcos <<= 1; + acos += step; } lo_sin[i] = bsin; @@ -244,125 +310,282 @@ inline static __unused int cheap_angle_diff(int angle1, int angle2) return diff; } -inline static unsigned popcount(unsigned v) +static const uint32_t samp_insn[] __attribute__((__aligned__(16))) = { + 0x4020, /* IN X, 32 */ + 0x4040, /* IN Y, 32 */ + 0xe020, /* SET X, 0 */ + 0xe040, /* SET Y, 0 */ +}; + +static uint32_t null, one = 1; + +static float rf_rx_start(int rx_pin, int bias_pin, float freq, int frac_num, int frac_denom) { - v = v - ((v >> 1) & 0x55555555); - v = (v & 0x33333333) + ((v >> 2) & 0x33333333); - return (((v + (v >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24; + dma_ch_rx = dma_claim_unused_channel(true); + dma_ch_cp = dma_claim_unused_channel(true); + dma_ch_cos = dma_claim_unused_channel(true); + dma_ch_sin = dma_claim_unused_channel(true); + dma_ch_pio_cos = dma_claim_unused_channel(true); + dma_ch_pio_sin = dma_claim_unused_channel(true); + + dma_ch_samp_cos = dma_claim_unused_channel(true); + dma_ch_samp_sin = dma_claim_unused_channel(true); + dma_ch_samp_trig = dma_claim_unused_channel(true); + + dma_t_samp = dma_claim_unused_timer(true); + + dma_channel_config dma_conf; + + /* Read received word into accumulator I. */ + dma_conf = dma_channel_get_default_config(dma_ch_rx); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, false); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 1, false)); + channel_config_set_chain_to(&dma_conf, dma_ch_cp); + dma_channel_configure(dma_ch_rx, &dma_conf, LO_COS_ACCUMULATOR, &pio1->rxf[1], 1, false); + + /* Copy accumulator I to accumulator Q. */ + dma_conf = dma_channel_get_default_config(dma_ch_cp); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, false); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_chain_to(&dma_conf, dma_ch_cos); + dma_channel_configure(dma_ch_cp, &dma_conf, LO_SIN_ACCUMULATOR, LO_COS_ACCUMULATOR, 1, + false); + + /* Read lo_cos into accumulator I with XOR. */ + dma_conf = dma_channel_get_default_config(dma_ch_cos); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, true); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2); + channel_config_set_chain_to(&dma_conf, dma_ch_sin); + dma_channel_configure(dma_ch_cos, &dma_conf, LO_COS_ACCUMULATOR + XOR_ADDR / 4, lo_cos, 1, + false); + + /* Read lo_sin into accumulator Q with XOR. */ + dma_conf = dma_channel_get_default_config(dma_ch_sin); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, true); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2); + channel_config_set_chain_to(&dma_conf, dma_ch_pio_cos); + dma_channel_configure(dma_ch_sin, &dma_conf, LO_SIN_ACCUMULATOR + XOR_ADDR / 4, lo_sin, 1, + false); + + /* Copy mixed I accumulator to PIO adder I. */ + dma_conf = dma_channel_get_default_config(dma_ch_pio_cos); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, false); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, true)); + channel_config_set_chain_to(&dma_conf, dma_ch_pio_sin); + dma_channel_configure(dma_ch_pio_cos, &dma_conf, &pio1->txf[2], LO_COS_ACCUMULATOR, 1, + false); + + /* Copy mixed Q accumulator to PIO adder Q. */ + dma_conf = dma_channel_get_default_config(dma_ch_pio_sin); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, false); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 3, true)); + channel_config_set_chain_to(&dma_conf, dma_ch_rx); + dma_channel_configure(dma_ch_pio_sin, &dma_conf, &pio1->txf[3], LO_SIN_ACCUMULATOR, 1, + false); + + /* Pacing timer for the sampling script trigger channel. */ + dma_timer_set_fraction(dma_t_samp, frac_num, frac_denom); + + /* Sampling trigger channel. */ + dma_conf = dma_channel_get_default_config(dma_ch_samp_trig); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, false); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_dreq(&dma_conf, dma_get_timer_dreq(dma_t_samp)); + channel_config_set_chain_to(&dma_conf, dma_ch_samp_cos); + dma_channel_configure(dma_ch_samp_trig, &dma_conf, &null, &one, 1, false); + + /* Trigger I accumulator values push. */ + dma_conf = dma_channel_get_default_config(dma_ch_samp_cos); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, true); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_ring(&dma_conf, false, 4); + channel_config_set_chain_to(&dma_conf, dma_ch_samp_sin); + dma_channel_configure(dma_ch_samp_cos, &dma_conf, &pio1->sm[2].instr, samp_insn, 4, false); + + /* Trigger Q accumulator values push. */ + dma_conf = dma_channel_get_default_config(dma_ch_samp_sin); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, true); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_ring(&dma_conf, false, 4); + channel_config_set_chain_to(&dma_conf, dma_ch_samp_trig); + dma_channel_configure(dma_ch_samp_sin, &dma_conf, &pio1->sm[3].instr, samp_insn, 4, false); + + bias_init(rx_pin, bias_pin); + adder_init(); + + float actual = lo_freq_init(freq); + + dma_channel_start(dma_ch_rx); + dma_channel_start(dma_ch_samp_trig); + watch_init(rx_pin); + + return actual; } -__noinline static int mix(uint32_t *buf, uint32_t *lo, int len) +static void rf_rx_stop(void) { - int x = 0; + pio_sm_set_enabled(pio1, 0, false); + pio_sm_set_enabled(pio1, 1, false); + pio_sm_set_enabled(pio1, 2, false); + pio_sm_set_enabled(pio1, 3, false); - for (int k = 0; k < len; k++) - x += popcount(buf[k] ^ lo[k]); + pio_sm_restart(pio1, 0); + pio_sm_restart(pio1, 1); + pio_sm_restart(pio1, 2); + pio_sm_restart(pio1, 3); - return x; + pio_sm_clear_fifos(pio1, 0); + pio_sm_clear_fifos(pio1, 1); + pio_sm_clear_fifos(pio1, 2); + pio_sm_clear_fifos(pio1, 3); + + sleep_us(10); + + dma_channel_abort(dma_ch_rx); + dma_channel_abort(dma_ch_cp); + dma_channel_abort(dma_ch_cos); + dma_channel_abort(dma_ch_sin); + dma_channel_abort(dma_ch_pio_cos); + dma_channel_abort(dma_ch_pio_sin); + dma_channel_abort(dma_ch_samp_cos); + dma_channel_abort(dma_ch_samp_sin); + dma_channel_abort(dma_ch_samp_trig); + + dma_channel_cleanup(dma_ch_rx); + dma_channel_cleanup(dma_ch_cp); + dma_channel_cleanup(dma_ch_cos); + dma_channel_cleanup(dma_ch_sin); + dma_channel_cleanup(dma_ch_pio_cos); + dma_channel_cleanup(dma_ch_pio_sin); + dma_channel_cleanup(dma_ch_samp_cos); + dma_channel_cleanup(dma_ch_samp_sin); + dma_channel_cleanup(dma_ch_samp_trig); + + dma_channel_unclaim(dma_ch_rx); + dma_channel_unclaim(dma_ch_cp); + dma_channel_unclaim(dma_ch_cos); + dma_channel_unclaim(dma_ch_sin); + dma_channel_unclaim(dma_ch_pio_cos); + dma_channel_unclaim(dma_ch_pio_sin); + dma_channel_unclaim(dma_ch_samp_cos); + dma_channel_unclaim(dma_ch_samp_sin); + dma_channel_unclaim(dma_ch_samp_trig); + + dma_timer_unclaim(dma_t_samp); + + dma_ch_rx = -1; + dma_ch_cp = -1; + dma_ch_cos = -1; + dma_ch_sin = -1; + dma_ch_pio_cos = -1; + dma_ch_pio_sin = -1; + dma_ch_samp_cos = -1; + dma_ch_samp_sin = -1; + dma_ch_samp_trig = -1; + + dma_t_samp = -1; +} + +static void rf_tx_start(int tx_pin) +{ + send_init(tx_pin); + + dma_ch_cos = dma_claim_unused_channel(true); + + dma_channel_config dma_conf = dma_channel_get_default_config(dma_ch_cos); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, true); + channel_config_set_write_increment(&dma_conf, false); + channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2); + channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 1, true)); + dma_channel_configure(dma_ch_cos, &dma_conf, &pio1->txf[1], lo_cos, UINT_MAX, true); +} + +static void rf_tx_stop() +{ + puts("Stopping TX..."); + pio_sm_set_enabled(pio1, 1, false); + pio_sm_restart(pio1, 1); + pio_sm_clear_fifos(pio1, 1); + + puts("Stopping DMA..."); + dma_channel_abort(dma_ch_cos); + dma_channel_cleanup(dma_ch_cos); + dma_channel_unclaim(dma_ch_cos); + dma_ch_cos = -1; } static void rf_rx(void) { -#if LPF_SAMPLES - static int lpIh1[LPF_SAMPLES], lpQh1[LPF_SAMPLES]; - int lpIavg1 = 0, lpQavg1 = 0; - - static int lpIh2[LPF_SAMPLES], lpQh2[LPF_SAMPLES]; - int lpIavg2 = 0, lpQavg2 = 0; - - static int lpIh3[LPF_SAMPLES], lpQh3[LPF_SAMPLES]; - int lpIavg3 = 0, lpQavg3 = 0; - - int lpIidx = 0, lpQidx = 0; - - for (int i = 0; i < LPF_SAMPLES; i++) { - lpIh1[i] = lpIh2[i] = lpIh3[i] = 0; - lpQh1[i] = lpQh2[i] = lpQh3[i] = 0; - } -#endif - - unsigned prev_transfers = 0; - static int16_t block[IQ_BLOCK_LEN]; - int block_ptr = 0; + uint32_t prev_transfers = dma_hw->ch[dma_ch_in_cos].transfer_count; + unsigned pos = 0; while (true) { - int delta = ~dma_hw->ch[rx_dma].transfer_count - prev_transfers; - int gap = NUM_SAMPLES - delta; + if (multicore_fifo_rvalid()) { + multicore_fifo_pop_blocking(); + multicore_fifo_push_blocking(0); + return; + } - while (delta < NUM_SAMPLES) - delta = ~dma_hw->ch[rx_dma].transfer_count - prev_transfers; + int delta = prev_transfers - dma_hw->ch[dma_ch_in_cos].transfer_count; + gap = IQ_BLOCK_LEN - delta; - if (gap < 0) - prev_transfers += NUM_SAMPLES; + while (delta < IQ_BLOCK_LEN) { + delta = prev_transfers - dma_hw->ch[dma_ch_in_cos].transfer_count; + sleep_us(100); + } - unsigned pos = prev_transfers & (LO_WORDS - 1); - prev_transfers += NUM_SAMPLES; + prev_transfers -= IQ_BLOCK_LEN; - int I = mix(rx_buf + pos, lo_cos + pos, NUM_SAMPLES); - int Q = mix(rx_buf + pos, lo_sin + pos, NUM_SAMPLES); + uint32_t *cos_ptr = rx_cos + pos; + uint32_t *sin_ptr = rx_sin + pos; - /* Remove the large DC bias. */ - I -= 16 * NUM_SAMPLES; - Q -= 16 * NUM_SAMPLES; + pos = (pos + IQ_BLOCK_LEN) & (RX_WORDS - 1); - /* Not sure why, but this removes a small DC bias. */ - I -= NUM_SAMPLES / 16; - Q -= NUM_SAMPLES / 16; + int16_t *blockptr = block; - /* Normalize to given number of bits. */ - I = (I * ((1 << (7 + EXTRA_BITS)) - 1)) / 16; - Q = (Q * ((1 << (7 + EXTRA_BITS)) - 1)) / 16; + for (int i = 0; i < IQ_BLOCK_LEN / 2; i++) { + uint32_t cos_pos = *cos_ptr++; + uint32_t cos_neg = *cos_ptr++; + uint32_t sin_pos = *sin_ptr++; + uint32_t sin_neg = *sin_ptr++; - I /= NUM_SAMPLES; - Q /= NUM_SAMPLES; + int I = cos_neg - cos_pos; + int Q = sin_neg - sin_pos; -#if LPF_SAMPLES - lpIavg1 += I - lpIh1[lpIidx]; - lpIh1[lpIidx] = I; + *blockptr++ = I; + *blockptr++ = Q; + } - lpIavg2 += lpIavg1 - lpIh2[lpIidx]; - lpIh2[lpIidx] = lpIavg1; - - lpIavg3 += lpIavg2 - lpIh3[lpIidx]; - lpIh3[lpIidx++] = lpIavg2; - - if (lpIidx >= LPF_SAMPLES) - lpIidx = 0; - - I = lpIavg3 / (LPF_SAMPLES * LPF_SAMPLES * LPF_SAMPLES); - - lpQavg1 += Q - lpQh1[lpQidx]; - lpQh1[lpQidx] = Q; - - lpQavg2 += lpQavg1 - lpQh2[lpQidx]; - lpQh2[lpQidx] = lpQavg1; - - lpQavg3 += lpQavg2 - lpQh3[lpQidx]; - lpQh3[lpQidx++] = lpQavg2; - - if (lpQidx >= LPF_SAMPLES) - lpQidx = 0; - - Q = lpQavg3 / (LPF_SAMPLES * LPF_SAMPLES * LPF_SAMPLES); -#endif - - status.sample_rate = CLK_RF_HZ / (NUM_SAMPLES * 32); - status.gap = gap; - - block[block_ptr++] = I; - block[block_ptr++] = Q; - - if (block_ptr >= IQ_BLOCK_LEN) { - queue_try_add(&rx_queue, block); - block_ptr = 0; + if (!queue_try_add(&iq_queue, block)) { + puts("queue overflow"); } } } +inline static int icopysign(int x, int s) +{ + return s >= 0 ? abs(x) : -abs(x); +} + static void __unused plot_IQ(int I, int Q) { - int mag = I ? copysign(log2f(abs(I)), I) : 0; + int mag = I ? icopysign(32 - __builtin_clz(abs(I)), I) : 0; if (mag < 0) { for (int l = -mag; l < 16; l++) @@ -382,7 +605,7 @@ static void __unused plot_IQ(int I, int Q) putchar(' '); } - mag = Q ? copysign(log2f(abs(Q)), Q) : 0; + mag = Q ? icopysign(32 - __builtin_clz(abs(Q)), Q) : 0; if (mag < 0) { for (int l = -mag; l < 16; l++) @@ -403,6 +626,106 @@ static void __unused plot_IQ(int I, int Q) } } +__unused inline static uint32_t ring_next(uint32_t **ptr, int dma_ch, int us) +{ + const unsigned ring_bits = (dma_hw->ch[dma_ch].al1_ctrl >> 6) & 15; + const uint32_t ring_mask = (1 << ring_bits) - 1; + uint32_t uptr = (uint32_t)*ptr; + + while (uptr == dma_hw->ch[dma_ch].write_addr) + sleep_us(us); + + uptr = (uptr & ~ring_mask) | ((uptr + sizeof(uint32_t)) & ring_mask); + *ptr = (uint32_t *)uptr; + return *(uint32_t *)uptr; +} + +static void do_rx(int rx_pin, int bias_pin, float freq, char mode) +{ + float actual = rf_rx_start(rx_pin, bias_pin, freq, 1, CLK_SYS_HZ / BANDWIDTH); + sleep_us(100); + + dma_ch_in_cos = dma_claim_unused_channel(true); + dma_ch_in_sin = dma_claim_unused_channel(true); + + dma_channel_config dma_conf; + + dma_conf = dma_channel_get_default_config(dma_ch_in_cos); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, false); + channel_config_set_write_increment(&dma_conf, true); + channel_config_set_ring(&dma_conf, true, RX_BITS_DEPTH + 2); + channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, false)); + dma_channel_configure(dma_ch_in_cos, &dma_conf, rx_cos, &pio1->rxf[2], UINT_MAX, false); + + dma_conf = dma_channel_get_default_config(dma_ch_in_sin); + channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); + channel_config_set_read_increment(&dma_conf, false); + channel_config_set_write_increment(&dma_conf, true); + channel_config_set_ring(&dma_conf, true, RX_BITS_DEPTH + 2); + channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 3, false)); + dma_channel_configure(dma_ch_in_sin, &dma_conf, rx_sin, &pio1->rxf[3], UINT_MAX, false); + + dma_start_channel_mask((1 << dma_ch_in_sin) | (1 << dma_ch_in_cos)); + + multicore_launch_core1(rf_rx); + + printf("Frequency: %.0f\n", actual); + + static int16_t block[IQ_BLOCK_LEN]; + + while (queue_try_remove(&iq_queue, block)) + /* Flush the queue */; + + if ('b' == mode) { + setvbuf(stdout, NULL, _IONBF, 0); + putchar('$'); + } + + while (true) { + int c = getchar_timeout_us(0); + if ('\r' == c) + break; + + if (queue_try_remove(&iq_queue, block)) { + if ('b' == mode) { + fwrite(block, sizeof block, 1, stdout); + fflush(stdout); + } else { + for (int i = 0; i < IQ_BLOCK_LEN / 2; i += 8) { + int I = block[i * 2]; + int Q = block[i * 2 + 1]; + printf("%2i %12i %12i ", gap, I, Q); + plot_IQ(I, Q); + putchar('\n'); + } + } + } + } + + putchar('\n'); + puts("Stopping core1..."); + multicore_fifo_push_blocking(0); + multicore_fifo_pop_blocking(); + sleep_us(10); + multicore_reset_core1(); + + puts("Stopping RX..."); + rf_rx_stop(); + + puts("Stopping readout DMAs..."); + dma_channel_abort(dma_ch_in_cos); + dma_channel_abort(dma_ch_in_sin); + dma_channel_cleanup(dma_ch_in_cos); + dma_channel_cleanup(dma_ch_in_sin); + dma_channel_unclaim(dma_ch_in_cos); + dma_channel_unclaim(dma_ch_in_sin); + dma_ch_in_cos = -1; + dma_ch_in_sin = -1; + + puts("Done."); +} + static void command(const char *cmd) { static char tmp[83]; @@ -414,6 +737,7 @@ static void command(const char *cmd) puts("drive N X - set GPIO pin drive strength"); puts("bias I O - output negated I to O"); puts("rx N FREQ - receive on pin N"); + puts("brx N FREQ - receive on pin N, binary output"); puts("tx N FREQ - transmit on pin N"); puts("sweep N F G S - sweep from F to G with given step"); puts("noise N - transmit random noise"); @@ -437,86 +761,38 @@ static void command(const char *cmd) return; } - if (3 == sscanf(cmd, " rx %i %f %[\a]", &n, &f, tmp)) { - watch_init(n); - float actual = lo_freq_init(f); - printf("actual frequency = %10.6f Hz\n", actual / MHZ); - - dma_channel_config dma_conf = dma_channel_get_default_config(rx_dma); - channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); - channel_config_set_read_increment(&dma_conf, false); - channel_config_set_write_increment(&dma_conf, true); - channel_config_set_ring(&dma_conf, true, LO_BITS_DEPTH + 2); - channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 1, false)); - dma_channel_configure(rx_dma, &dma_conf, rx_buf, &pio1->rxf[1], UINT_MAX, true); - - multicore_launch_core1(rf_rx); - sleep_us(100); - - while (true) { - int c = getchar_timeout_us(0); - - if (13 == c) { - multicore_reset_core1(); - dma_channel_abort(rx_dma); - dma_channel_cleanup(rx_dma); - puts("stopped"); - break; - } - - static int16_t block[IQ_BLOCK_LEN]; - - while (queue_try_remove(&rx_queue, block)) { - putchar('!'); - - for (int i = 0; i < IQ_BLOCK_LEN; i++) - printf("%04hx", block[i]); - - putchar('\n'); - } - - printf("gap=%i, sr=%i\n", status.gap, status.sample_rate); - } + if (4 == sscanf(cmd, " rx %i %i %f %[\a]", &n, &x, &f, tmp)) { + do_rx(n, x, f, 'a'); + return; + } + if (4 == sscanf(cmd, " brx %i %i %f %[\a]", &n, &x, &f, tmp)) { + do_rx(n, x, f, 'b'); return; } if (3 == sscanf(cmd, " tx %i %f %[\a]", &n, &f, tmp)) { - dma_channel_abort(tx_dma); + float actual = lo_freq_init(f); + printf("Frequency: %.0f\n", actual); - if (!f) { - gpio_init(n); - puts("stopped"); - return; + rf_tx_start(n); + puts("Transmitting, press ENTER to stop."); + + while (true) { + int c = getchar_timeout_us(1000); + + if ('\r' == c) { + break; + } } - send_init(n); - float actual = lo_freq_init(f); - printf("actual frequency = %10.6f MHz\n", actual / MHZ); - - dma_channel_config dma_conf = dma_channel_get_default_config(tx_dma); - channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); - channel_config_set_read_increment(&dma_conf, true); - channel_config_set_write_increment(&dma_conf, false); - channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2); - channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, true)); - dma_channel_configure(tx_dma, &dma_conf, &pio1->txf[2], lo_cos, UINT_MAX, true); - puts("started"); + rf_tx_stop(); + puts("Done."); return; } if (5 == sscanf(cmd, " sweep %i %f %f %i %[\a]", &n, &f, &g, &x, tmp)) { - dma_channel_abort(tx_dma); - - if (!f || !g) { - gpio_init(n); - puts("stopped"); - return; - } - - send_init(n); - - const float step_hz = (float)CLK_RF_HZ / (LO_WORDS * 32); + const float step_hz = (float)CLK_SYS_HZ / (LO_WORDS * 32); const float start = roundf(f / step_hz) * step_hz; const float stop = roundf(g / step_hz) * step_hz; @@ -525,53 +801,41 @@ static void command(const char *cmd) for (int i = 0; i < LO_WORDS; i++) lo_cos[i] = 0; - dma_channel_config dma_conf = dma_channel_get_default_config(tx_dma); - channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); - channel_config_set_read_increment(&dma_conf, true); - channel_config_set_write_increment(&dma_conf, false); - channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2); - channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, true)); - dma_channel_configure(tx_dma, &dma_conf, &pio1->txf[2], lo_cos, UINT_MAX, true); + rf_tx_start(n); for (int i = 0; i < steps; i += x) { - int c = getchar_timeout_us(0); - if ('\n' == c || '\r' == c) + int c = getchar_timeout_us(100); + if ('\r' == c) break; float actual = lo_freq_init(start + i * step_hz); - printf("frequency = %10.6f MHz\n", actual / MHZ); + printf("Frequency: %.0f\n", actual); } - dma_channel_abort(tx_dma); + rf_tx_stop(); + puts("Done."); return; } if (2 == sscanf(cmd, " noise %i %[\a]", &n, tmp)) { - send_init(n); - for (int i = 0; i < LO_WORDS; i++) lo_cos[i] = rand(); - dma_channel_config dma_conf = dma_channel_get_default_config(tx_dma); - channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); - channel_config_set_read_increment(&dma_conf, true); - channel_config_set_write_increment(&dma_conf, false); - channel_config_set_ring(&dma_conf, false, LO_BITS_DEPTH + 2); - channel_config_set_dreq(&dma_conf, pio_get_dreq(pio1, 2, true)); - dma_channel_configure(tx_dma, &dma_conf, &pio1->txf[2], lo_cos, UINT_MAX, true); + rf_tx_start(n); puts("Transmitting noise, press ENTER to stop."); while (true) { - int c = getchar_timeout_us(0); - if ('\n' == c || '\r' == c) + int c = getchar_timeout_us(100); + if ('\r' == c) break; for (int i = 0; i < LO_WORDS; i++) lo_cos[i] = rand(); } - dma_channel_abort(tx_dma); + rf_tx_stop(); + puts("Done."); return; } @@ -585,6 +849,8 @@ int main() clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, CLK_SYS_HZ, CLK_SYS_HZ); + bus_ctrl_hw->priority |= BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS; + stdio_usb_init(); for (int i = 0; i < 30; i++) { @@ -594,14 +860,11 @@ int main() sleep_ms(100); } - tx_dma = dma_claim_unused_channel(true); - rx_dma = dma_claim_unused_channel(true); - - queue_init(&rx_queue, IQ_BLOCK_LEN * 2, 64); - printf("\nPuppet Online!\n"); printf("clk_sys = %10.6f MHz\n", (float)clock_get_hz(clk_sys) / MHZ); + queue_init(&iq_queue, IQ_BLOCK_LEN * 2, 256); + static char cmd[83]; int cmdlen = 0; @@ -611,7 +874,7 @@ int main() int c; while ((c = getchar_timeout_us(10000)) >= 0) { - if (13 == c) { + if ('\r' == c) { /* Enter */ } else if ((8 == c) && (cmdlen > 0)) { cmd[--cmdlen] = 0; @@ -626,7 +889,7 @@ int main() putchar(c); } - if ((13 == c) || cmdlen == 80) { + if (('\r' == c) || cmdlen == 80) { printf("\n"); if (cmdlen > 0) { cmd[cmdlen] = '\a'; diff --git a/util/bridge.py b/util/bridge.py new file mode 100755 index 0000000..ee74f2f --- /dev/null +++ b/util/bridge.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +import time +from codecs import encode +from socket import AF_INET, SO_SNDBUF, SOCK_STREAM, SOL_SOCKET, socket + +import click +import serial + + +@click.command() +@click.option( + "-f", "--frequency", default=40680000, type=int, help="Frequency to tune to" +) +def bridge(frequency): + sock = socket(AF_INET, SOCK_STREAM) + sock.setsockopt(SOL_SOCKET, SO_SNDBUF, 1024 * 100) + + with serial.Serial("/dev/ttyACM0", baudrate=10_000_000) as fp: + print("Resetting...") + fp.write(b"\r\n") + + time.sleep(0.1) + + while fp.in_waiting: + fp.read(fp.in_waiting) + time.sleep(0.1) + + print("Connecting to localhost:1234...") + sock.connect(("localhost", 1234)) + + print(f"Starting RX 10/11 at {frequency}...") + fp.write(f"brx 10 11 {frequency}\r\n".encode("ascii")) + fp.read_until(b"$") + + try: + while True: + bstr = fp.read(64) + assert len(bstr) == 64 + + try: + assert len(bstr) == sock.send(bstr) + except ConnectionRefusedError: + pass + + except ConnectionError: + pass + + except KeyboardInterrupt: + pass + + finally: + fp.write(b"\r\n") + print("Bye.") + + +if __name__ == "__main__": + bridge()