Use DMA-based full speed mixing
This commit is contained in:
		
							parent
							
								
									c7980567c0
								
							
						
					
					
						commit
						f8b316a340
					
				
					 3 changed files with 560 additions and 238 deletions
				
			
		|  | @ -19,6 +19,7 @@ target_link_libraries( | ||||||
|   hardware_divider |   hardware_divider | ||||||
|   hardware_dma |   hardware_dma | ||||||
|   hardware_pio |   hardware_pio | ||||||
|  |   hardware_pwm | ||||||
|   hardware_timer |   hardware_timer | ||||||
|   hardware_vreg |   hardware_vreg | ||||||
|   hardware_interp |   hardware_interp | ||||||
|  | @ -29,8 +30,8 @@ pico_add_extra_outputs(pico_sdr) | ||||||
| set_property(TARGET pico_sdr PROPERTY C_STANDARD 23) | set_property(TARGET pico_sdr PROPERTY C_STANDARD 23) | ||||||
| target_compile_options(pico_sdr PRIVATE -Wall -Wextra -Wnull-dereference) | 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_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_ENABLE_CRLF_SUPPORT=0) | ||||||
| target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_DEFAULT_CRLF=1) | target_compile_definitions(pico_sdr PUBLIC PICO_STDIO_DEFAULT_CRLF=0) | ||||||
| 
 | 
 | ||||||
| target_include_directories(pico_sdr PRIVATE include) | target_include_directories(pico_sdr PRIVATE include) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										735
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										735
									
								
								src/main.c
									
									
									
									
									
								
							|  | @ -26,9 +26,11 @@ | ||||||
| #include <hardware/vreg.h> | #include <hardware/vreg.h> | ||||||
| #include <hardware/sync.h> | #include <hardware/sync.h> | ||||||
| #include <hardware/pio.h> | #include <hardware/pio.h> | ||||||
|  | #include <hardware/pwm.h> | ||||||
| #include <hardware/interp.h> | #include <hardware/interp.h> | ||||||
| 
 | 
 | ||||||
| #include <hardware/regs/clocks.h> | #include <hardware/regs/clocks.h> | ||||||
|  | #include <hardware/structs/bus_ctrl.h> | ||||||
| 
 | 
 | ||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  | @ -36,29 +38,47 @@ | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| 
 | 
 | ||||||
| #define CLK_SYS_HZ (250 * MHZ) | #define CLK_SYS_HZ (250 * MHZ) | ||||||
| #define CLKDIV_RF 2 | #define BANDWIDTH 8000 | ||||||
| #define CLK_RF_HZ ((unsigned)(CLK_SYS_HZ / CLKDIV_RF)) |  | ||||||
| 
 | 
 | ||||||
| #define EXTRA_BITS 8 | #define EXTRA_BITS 8 | ||||||
| #define NUM_SAMPLES 128 | #define NUM_SAMPLES 128 | ||||||
| #define LPF_SAMPLES 0 /* 4 */ | #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_BITS_DEPTH 13 | ||||||
| #define LO_WORDS (1 << LO_BITS_DEPTH) | #define LO_WORDS (1 << LO_BITS_DEPTH) | ||||||
| static uint32_t lo_cos[LO_WORDS] __attribute__((__aligned__(LO_WORDS * 4))); | 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 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 | #define RX_BITS_DEPTH 10 | ||||||
| static queue_t rx_queue; | #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; | /* rx -> cp -> cos -> sin -> pio_cos -> pio_sin -> rx ... */ | ||||||
| static int rx_dma = -1; | 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 { | static int dma_ch_samp_trig = -1; | ||||||
| 	unsigned sample_rate; | static int dma_ch_samp_cos = -1; | ||||||
| 	int gap; | static int dma_ch_samp_sin = -1; | ||||||
| } status; | 
 | ||||||
|  | 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) | 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_input_hysteresis_enabled(out_pin, false); | ||||||
| 	gpio_set_drive_strength(out_pin, GPIO_DRIVE_STRENGTH_2MA); | 	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_encode_mov_not(pio_pins, pio_pins), | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	pio_program_t lm_prog = { | 	pio_program_t prog = { | ||||||
| 		.instructions = lm_insn, | 		.instructions = insn, | ||||||
| 		.length = 1, | 		.length = 1, | ||||||
| 		.origin = 0, | 		.origin = 31, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	pio_sm_set_enabled(pio1, 0, false); | 	pio_sm_set_enabled(pio1, 0, false); | ||||||
| 	pio_sm_restart(pio1, 0); | 	pio_sm_restart(pio1, 0); | ||||||
|  | 	pio_sm_clear_fifos(pio1, 0); | ||||||
| 
 | 
 | ||||||
| 	if (pio_can_add_program(pio1, &lm_prog)) | 	if (pio_can_add_program(pio1, &prog)) | ||||||
| 		pio_add_program(pio1, &lm_prog); | 		pio_add_program(pio1, &prog); | ||||||
| 
 | 
 | ||||||
| 	pio_sm_config pc = pio_get_default_sm_config(); | 	pio_sm_config pc = pio_get_default_sm_config(); | ||||||
| 	sm_config_set_in_pins(&pc, in_pin); | 	sm_config_set_in_pins(&pc, in_pin); | ||||||
| 	sm_config_set_out_pins(&pc, out_pin, 1); | 	sm_config_set_out_pins(&pc, out_pin, 1); | ||||||
| 	sm_config_set_set_pins(&pc, out_pin, 1); | 	sm_config_set_set_pins(&pc, out_pin, 1); | ||||||
| 	sm_config_set_wrap(&pc, 0, 0); | 	sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1); | ||||||
| 	sm_config_set_clkdiv_int_frac(&pc, 3, 127); | 	sm_config_set_clkdiv_int_frac(&pc, 7, 127); | ||||||
| 	pio_sm_init(pio1, 0, 0, &pc); | 	pio_sm_init(pio1, 0, prog.origin, &pc); | ||||||
| 
 | 
 | ||||||
| 	pio_sm_set_consecutive_pindirs(pio1, 0, out_pin, 1, GPIO_OUT); | 	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 = { | 	pio_program_t prog = { | ||||||
| 		.instructions = insn, | 		.instructions = insn, | ||||||
| 		.length = 1, | 		.length = 1, | ||||||
| 		.origin = 1, | 		.origin = 30, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	pio_sm_set_enabled(pio1, 1, false); | 	pio_sm_set_enabled(pio1, 1, false); | ||||||
| 	pio_sm_restart(pio1, 1); | 	pio_sm_restart(pio1, 1); | ||||||
|  | 	pio_sm_clear_fifos(pio1, 1); | ||||||
| 
 | 
 | ||||||
| 	if (pio_can_add_program(pio1, &prog)) | 	if (pio_can_add_program(pio1, &prog)) | ||||||
| 		pio_add_program(pio1, &prog); | 		pio_add_program(pio1, &prog); | ||||||
| 
 | 
 | ||||||
| 	pio_sm_config pc = pio_get_default_sm_config(); | 	pio_sm_config pc = pio_get_default_sm_config(); | ||||||
| 	sm_config_set_in_pins(&pc, in_pin); | 	sm_config_set_in_pins(&pc, in_pin); | ||||||
| 	sm_config_set_wrap(&pc, 1, 1); | 	sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1); | ||||||
| 	sm_config_set_clkdiv_int_frac(&pc, CLKDIV_RF, 0); | 	sm_config_set_clkdiv_int_frac(&pc, 1, 0); | ||||||
| 	sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_RX); | 	sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_RX); | ||||||
| 	sm_config_set_in_shift(&pc, false, true, 32); | 	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); | 	pio_sm_set_enabled(pio1, 1, true); | ||||||
| } | } | ||||||
|  | @ -147,11 +169,12 @@ static void send_init(int out_pin) | ||||||
| 	pio_program_t prog = { | 	pio_program_t prog = { | ||||||
| 		.instructions = insn, | 		.instructions = insn, | ||||||
| 		.length = 1, | 		.length = 1, | ||||||
| 		.origin = 2, | 		.origin = 29, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	pio_sm_set_enabled(pio1, 2, false); | 	pio_sm_set_enabled(pio1, 1, false); | ||||||
| 	pio_sm_restart(pio1, 2); | 	pio_sm_restart(pio1, 1); | ||||||
|  | 	pio_sm_clear_fifos(pio1, 1); | ||||||
| 
 | 
 | ||||||
| 	if (pio_can_add_program(pio1, &prog)) | 	if (pio_can_add_program(pio1, &prog)) | ||||||
| 		pio_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(); | 	pio_sm_config pc = pio_get_default_sm_config(); | ||||||
| 	sm_config_set_out_pins(&pc, out_pin, 1); | 	sm_config_set_out_pins(&pc, out_pin, 1); | ||||||
| 	sm_config_set_set_pins(&pc, out_pin, 1); | 	sm_config_set_set_pins(&pc, out_pin, 1); | ||||||
| 	sm_config_set_wrap(&pc, 2, 2); | 	sm_config_set_wrap(&pc, prog.origin, prog.origin + prog.length - 1); | ||||||
| 	sm_config_set_clkdiv_int_frac(&pc, CLKDIV_RF, 0); | 	sm_config_set_clkdiv_int_frac(&pc, 1, 0); | ||||||
| 	sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_TX); | 	sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_TX); | ||||||
| 	sm_config_set_out_shift(&pc, false, true, 32); | 	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); | #if 1 | ||||||
| 	float freq = roundf(req_freq / step_hz) * step_hz; | 	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) | | 	pio_program_t prog = { | ||||||
| 			   (0 << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) | | 		.instructions = insn, | ||||||
| 			   (0 << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB); | 		.length = sizeof(insn) / sizeof(*insn), | ||||||
| 	interp0->ctrl[1] = interp0->ctrl[0]; | 		.origin = 0, | ||||||
|  | 	}; | ||||||
| 
 | 
 | ||||||
| 	interp0->base[0] = step; | 	pio_sm_set_enabled(pio1, 2, false); | ||||||
| 	interp0->base[1] = step; | 	pio_sm_set_enabled(pio1, 3, false); | ||||||
| 	interp0->accum[1] = UINT_MAX / 4; | 
 | ||||||
|  | 	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++) { | 	for (int i = 0; i < LO_WORDS; i++) { | ||||||
| 		unsigned bsin = 0, bcos = 0; | 		unsigned bsin = 0, bcos = 0; | ||||||
| 
 | 
 | ||||||
| 		for (int j = 0; j < 32; j++) { | 		for (int j = 0; j < 32; j++) { | ||||||
| 			int sin_bit = interp0->peek[0] >> 31; | 			bsin |= asin >> 31; | ||||||
| 			bsin |= sin_bit; |  | ||||||
| 			bsin <<= 1; | 			bsin <<= 1; | ||||||
|  | 			asin += step; | ||||||
| 
 | 
 | ||||||
| 			int cos_bit = interp0->pop[1] >> 31; | 			bcos |= acos >> 31; | ||||||
| 			bcos |= cos_bit; |  | ||||||
| 			bcos <<= 1; | 			bcos <<= 1; | ||||||
|  | 			acos += step; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		lo_sin[i] = bsin; | 		lo_sin[i] = bsin; | ||||||
|  | @ -244,125 +310,282 @@ inline static __unused int cheap_angle_diff(int angle1, int angle2) | ||||||
| 	return diff; | 	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); | 	dma_ch_rx = dma_claim_unused_channel(true); | ||||||
| 	v = (v & 0x33333333) + ((v >> 2) & 0x33333333); | 	dma_ch_cp = dma_claim_unused_channel(true); | ||||||
| 	return (((v + (v >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24; | 	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++) | 	pio_sm_restart(pio1, 0); | ||||||
| 		x += popcount(buf[k] ^ lo[k]); | 	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) | 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]; | 	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) { | 	while (true) { | ||||||
| 		int delta = ~dma_hw->ch[rx_dma].transfer_count - prev_transfers; | 		if (multicore_fifo_rvalid()) { | ||||||
| 		int gap = NUM_SAMPLES - delta; | 			multicore_fifo_pop_blocking(); | ||||||
|  | 			multicore_fifo_push_blocking(0); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		while (delta < NUM_SAMPLES) | 		int delta = prev_transfers - dma_hw->ch[dma_ch_in_cos].transfer_count; | ||||||
| 			delta = ~dma_hw->ch[rx_dma].transfer_count - prev_transfers; | 		gap = IQ_BLOCK_LEN - delta; | ||||||
| 
 | 
 | ||||||
| 		if (gap < 0) | 		while (delta < IQ_BLOCK_LEN) { | ||||||
| 			prev_transfers += NUM_SAMPLES; | 			delta = prev_transfers - dma_hw->ch[dma_ch_in_cos].transfer_count; | ||||||
|  | 			sleep_us(100); | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		unsigned pos = prev_transfers & (LO_WORDS - 1); | 		prev_transfers -= IQ_BLOCK_LEN; | ||||||
| 		prev_transfers += NUM_SAMPLES; |  | ||||||
| 
 | 
 | ||||||
| 		int I = mix(rx_buf + pos, lo_cos + pos, NUM_SAMPLES); | 		uint32_t *cos_ptr = rx_cos + pos; | ||||||
| 		int Q = mix(rx_buf + pos, lo_sin + pos, NUM_SAMPLES); | 		uint32_t *sin_ptr = rx_sin + pos; | ||||||
| 
 | 
 | ||||||
| 		/* Remove the large DC bias. */ | 		pos = (pos + IQ_BLOCK_LEN) & (RX_WORDS - 1); | ||||||
| 		I -= 16 * NUM_SAMPLES; |  | ||||||
| 		Q -= 16 * NUM_SAMPLES; |  | ||||||
| 
 | 
 | ||||||
| 		/* Not sure why, but this removes a small DC bias. */ | 		int16_t *blockptr = block; | ||||||
| 		I -= NUM_SAMPLES / 16; |  | ||||||
| 		Q -= NUM_SAMPLES / 16; |  | ||||||
| 
 | 
 | ||||||
| 		/* Normalize to given number of bits. */ | 		for (int i = 0; i < IQ_BLOCK_LEN / 2; i++) { | ||||||
| 		I = (I * ((1 << (7 + EXTRA_BITS)) - 1)) / 16; | 			uint32_t cos_pos = *cos_ptr++; | ||||||
| 		Q = (Q * ((1 << (7 + EXTRA_BITS)) - 1)) / 16; | 			uint32_t cos_neg = *cos_ptr++; | ||||||
|  | 			uint32_t sin_pos = *sin_ptr++; | ||||||
|  | 			uint32_t sin_neg = *sin_ptr++; | ||||||
| 
 | 
 | ||||||
| 		I /= NUM_SAMPLES; | 			int I = cos_neg - cos_pos; | ||||||
| 		Q /= NUM_SAMPLES; | 			int Q = sin_neg - sin_pos; | ||||||
| 
 | 
 | ||||||
| #if LPF_SAMPLES | 			*blockptr++ = I; | ||||||
| 		lpIavg1 += I - lpIh1[lpIidx]; | 			*blockptr++ = Q; | ||||||
| 		lpIh1[lpIidx] = I; | 		} | ||||||
| 
 | 
 | ||||||
| 		lpIavg2 += lpIavg1 - lpIh2[lpIidx]; | 		if (!queue_try_add(&iq_queue, block)) { | ||||||
| 		lpIh2[lpIidx] = lpIavg1; | 			puts("queue overflow"); | ||||||
| 
 |  | ||||||
| 		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; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | inline static int icopysign(int x, int s) | ||||||
|  | { | ||||||
|  | 	return s >= 0 ? abs(x) : -abs(x); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void __unused plot_IQ(int I, int Q) | 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) { | 	if (mag < 0) { | ||||||
| 		for (int l = -mag; l < 16; l++) | 		for (int l = -mag; l < 16; l++) | ||||||
|  | @ -382,7 +605,7 @@ static void __unused plot_IQ(int I, int Q) | ||||||
| 			putchar(' '); | 			putchar(' '); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mag = Q ? copysign(log2f(abs(Q)), Q) : 0; | 	mag = Q ? icopysign(32 - __builtin_clz(abs(Q)), Q) : 0; | ||||||
| 
 | 
 | ||||||
| 	if (mag < 0) { | 	if (mag < 0) { | ||||||
| 		for (int l = -mag; l < 16; l++) | 		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 void command(const char *cmd) | ||||||
| { | { | ||||||
| 	static char tmp[83]; | 	static char tmp[83]; | ||||||
|  | @ -414,6 +737,7 @@ static void command(const char *cmd) | ||||||
| 		puts("drive N X        - set GPIO pin drive strength"); | 		puts("drive N X        - set GPIO pin drive strength"); | ||||||
| 		puts("bias I O         - output negated I to O"); | 		puts("bias I O         - output negated I to O"); | ||||||
| 		puts("rx N FREQ        - receive on pin N"); | 		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("tx N FREQ        - transmit on pin N"); | ||||||
| 		puts("sweep N F G S    - sweep from F to G with given step"); | 		puts("sweep N F G S    - sweep from F to G with given step"); | ||||||
| 		puts("noise N          - transmit random noise"); | 		puts("noise N          - transmit random noise"); | ||||||
|  | @ -437,86 +761,38 @@ static void command(const char *cmd) | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (3 == sscanf(cmd, " rx %i %f %[\a]", &n, &f, tmp)) { | 	if (4 == sscanf(cmd, " rx %i %i %f %[\a]", &n, &x, &f, tmp)) { | ||||||
| 		watch_init(n); | 		do_rx(n, x, f, 'a'); | ||||||
| 		float actual = lo_freq_init(f); | 		return; | ||||||
| 		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, " brx %i %i %f %[\a]", &n, &x, &f, tmp)) { | ||||||
|  | 		do_rx(n, x, f, 'b'); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (3 == sscanf(cmd, " tx %i %f %[\a]", &n, &f, tmp)) { | 	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) { | 		rf_tx_start(n); | ||||||
| 			gpio_init(n); | 		puts("Transmitting, press ENTER to stop."); | ||||||
| 			puts("stopped"); | 
 | ||||||
| 			return; | 		while (true) { | ||||||
|  | 			int c = getchar_timeout_us(1000); | ||||||
|  | 
 | ||||||
|  | 			if ('\r' == c) { | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		send_init(n); | 		rf_tx_stop(); | ||||||
| 		float actual = lo_freq_init(f); | 		puts("Done."); | ||||||
| 		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"); |  | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (5 == sscanf(cmd, " sweep %i %f %f %i %[\a]", &n, &f, &g, &x, tmp)) { | 	if (5 == sscanf(cmd, " sweep %i %f %f %i %[\a]", &n, &f, &g, &x, tmp)) { | ||||||
| 		dma_channel_abort(tx_dma); | 		const float step_hz = (float)CLK_SYS_HZ / (LO_WORDS * 32); | ||||||
| 
 |  | ||||||
| 		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 start = roundf(f / step_hz) * step_hz; | 		const float start = roundf(f / step_hz) * step_hz; | ||||||
| 		const float stop = roundf(g / 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++) | 		for (int i = 0; i < LO_WORDS; i++) | ||||||
| 			lo_cos[i] = 0; | 			lo_cos[i] = 0; | ||||||
| 
 | 
 | ||||||
| 		dma_channel_config dma_conf = dma_channel_get_default_config(tx_dma); | 		rf_tx_start(n); | ||||||
| 		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); |  | ||||||
| 
 | 
 | ||||||
| 		for (int i = 0; i < steps; i += x) { | 		for (int i = 0; i < steps; i += x) { | ||||||
| 			int c = getchar_timeout_us(0); | 			int c = getchar_timeout_us(100); | ||||||
| 			if ('\n' == c || '\r' == c) | 			if ('\r' == c) | ||||||
| 				break; | 				break; | ||||||
| 
 | 
 | ||||||
| 			float actual = lo_freq_init(start + i * step_hz); | 			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; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (2 == sscanf(cmd, " noise %i %[\a]", &n, tmp)) { | 	if (2 == sscanf(cmd, " noise %i %[\a]", &n, tmp)) { | ||||||
| 		send_init(n); |  | ||||||
| 
 |  | ||||||
| 		for (int i = 0; i < LO_WORDS; i++) | 		for (int i = 0; i < LO_WORDS; i++) | ||||||
| 			lo_cos[i] = rand(); | 			lo_cos[i] = rand(); | ||||||
| 
 | 
 | ||||||
| 		dma_channel_config dma_conf = dma_channel_get_default_config(tx_dma); | 		rf_tx_start(n); | ||||||
| 		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("Transmitting noise, press ENTER to stop."); | 		puts("Transmitting noise, press ENTER to stop."); | ||||||
| 
 | 
 | ||||||
| 		while (true) { | 		while (true) { | ||||||
| 			int c = getchar_timeout_us(0); | 			int c = getchar_timeout_us(100); | ||||||
| 			if ('\n' == c || '\r' == c) | 			if ('\r' == c) | ||||||
| 				break; | 				break; | ||||||
| 
 | 
 | ||||||
| 			for (int i = 0; i < LO_WORDS; i++) | 			for (int i = 0; i < LO_WORDS; i++) | ||||||
| 				lo_cos[i] = rand(); | 				lo_cos[i] = rand(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		dma_channel_abort(tx_dma); | 		rf_tx_stop(); | ||||||
|  | 		puts("Done."); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -585,6 +849,8 @@ int main() | ||||||
| 	clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, CLK_SYS_HZ, | 	clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, CLK_SYS_HZ, | ||||||
| 			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(); | 	stdio_usb_init(); | ||||||
| 
 | 
 | ||||||
| 	for (int i = 0; i < 30; i++) { | 	for (int i = 0; i < 30; i++) { | ||||||
|  | @ -594,14 +860,11 @@ int main() | ||||||
| 		sleep_ms(100); | 		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("\nPuppet Online!\n"); | ||||||
| 	printf("clk_sys = %10.6f MHz\n", (float)clock_get_hz(clk_sys) / MHZ); | 	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]; | 	static char cmd[83]; | ||||||
| 	int cmdlen = 0; | 	int cmdlen = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -611,7 +874,7 @@ int main() | ||||||
| 		int c; | 		int c; | ||||||
| 
 | 
 | ||||||
| 		while ((c = getchar_timeout_us(10000)) >= 0) { | 		while ((c = getchar_timeout_us(10000)) >= 0) { | ||||||
| 			if (13 == c) { | 			if ('\r' == c) { | ||||||
| 				/* Enter */ | 				/* Enter */ | ||||||
| 			} else if ((8 == c) && (cmdlen > 0)) { | 			} else if ((8 == c) && (cmdlen > 0)) { | ||||||
| 				cmd[--cmdlen] = 0; | 				cmd[--cmdlen] = 0; | ||||||
|  | @ -626,7 +889,7 @@ int main() | ||||||
| 				putchar(c); | 				putchar(c); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if ((13 == c) || cmdlen == 80) { | 			if (('\r' == c) || cmdlen == 80) { | ||||||
| 				printf("\n"); | 				printf("\n"); | ||||||
| 				if (cmdlen > 0) { | 				if (cmdlen > 0) { | ||||||
| 					cmd[cmdlen] = '\a'; | 					cmd[cmdlen] = '\a'; | ||||||
|  |  | ||||||
							
								
								
									
										58
									
								
								util/bridge.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										58
									
								
								util/bridge.py
									
									
									
									
									
										Executable file
									
								
							|  | @ -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() | ||||||
		Loading…
	
		Reference in a new issue