File: sgpio.c

package info (click to toggle)
hackrf 2024.02.1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 36,692 kB
  • sloc: ansic: 56,310; xml: 3,424; perl: 2,730; python: 1,427; makefile: 598; asm: 514; vhdl: 319; sh: 179; awk: 20
file content (341 lines) | stat: -rw-r--r-- 13,084 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/*
 * Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
 * Copyright 2012 Jared Boone <jared@sharebrained.com>
 * Copyright 2013 Benjamin Vernoux <titanmkd@gmail.com>
 * Copyright 2017 Schuyler St. Leger <schuyler.st.leger@gmail.com>
 *
 * This file is part of HackRF.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#include <libopencm3/lpc43xx/scu.h>
#include <libopencm3/lpc43xx/sgpio.h>

#include "hackrf_core.h"
#include "platform_detect.h"

#include "sgpio.h"

static void update_q_invert(sgpio_config_t* const config);

void sgpio_configure_pin_functions(sgpio_config_t* const config)
{
	scu_pinmux(SCU_PINMUX_SGPIO0, SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
	scu_pinmux(SCU_PINMUX_SGPIO1, SCU_GPIO_FAST | SCU_CONF_FUNCTION3);
	scu_pinmux(SCU_PINMUX_SGPIO2, SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
	scu_pinmux(SCU_PINMUX_SGPIO3, SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
	scu_pinmux(SCU_PINMUX_SGPIO4, SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
	scu_pinmux(SCU_PINMUX_SGPIO5, SCU_GPIO_FAST | SCU_CONF_FUNCTION2);
	scu_pinmux(SCU_PINMUX_SGPIO6, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
	scu_pinmux(SCU_PINMUX_SGPIO7, SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
	scu_pinmux(SCU_PINMUX_SGPIO8, SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
	scu_pinmux(SCU_PINMUX_SGPIO9, SCU_GPIO_FAST | SCU_CONF_FUNCTION7);
	scu_pinmux(SCU_PINMUX_SGPIO10, SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
	scu_pinmux(SCU_PINMUX_SGPIO11, SCU_GPIO_FAST | SCU_CONF_FUNCTION6);
	scu_pinmux(SCU_PINMUX_SGPIO12, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); /* GPIO0[13] */
	scu_pinmux(SCU_PINMUX_SGPIO14, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[13] */
	scu_pinmux(SCU_PINMUX_SGPIO15, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[14] */

	if (detected_platform() == BOARD_ID_HACKRF1_R9) {
		scu_pinmux(
			SCU_H1R9_HW_SYNC_EN,
			SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[5] */
	} else {
		scu_pinmux(
			SCU_HW_SYNC_EN,
			SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[12] */
	}

	sgpio_cpld_set_mixer_invert(config, 0);
	hw_sync_enable(0);

	gpio_output(config->gpio_q_invert);
	gpio_output(config->gpio_hw_sync_enable);
}

void sgpio_set_slice_mode(sgpio_config_t* const config, const bool multi_slice)
{
	config->slice_mode_multislice = multi_slice;
}

/*
 SGPIO 0 to 7 = DAC/ADC data bits 0 to 7
 (Note: DAC is 10 bits but only bit 9 to bit 2 are used, bits 1 & 0 are forced to 0 by CPLD)

 ADC => CLK x 2 = CLKx2 with
   CLKx2(0) rising = D0Q,
   CLKx2(1) rising = D1I
 Corresponds to:
   CLK(0) falling + tD0Q => D0Q,
   CLK(1) rising  + tDOI => D1I,
   CLK(1) falling + tD0Q => D1Q,
   CLK(1) rising  + tDOI => D2I ...)
 tDOI(CLK Rise to I-ADC Channel-I Output Data Valid) = 7.4 to 9ns
 tD0Q(CLK Fall to Q-ADC Channel-Q Output Data Valid) = 6.9 to 9ns

 DAC=> CLK x 2 = CLKx2 with:
   CLKx2(0) rising = Q:N-2,
   CLKx2(1) rising = I:N-1
 Corresponds to:
   CLK(0) rising  => Q:N-2,
   CLK(0) falling => I:N-1,
   CLK(1) rising  => Q:N-1,
   CLK(1) falling => I:N ...
 tDSI(I-DAC Data to CLK Fall Setup Time) = min 10ns
 tDSQ(Q-DAC Data to CLK Rise Setup Time) = min 10ns

 SGPIO8 Clock Input (External Clock)
 SGPIO9 Capture Input (Capture/ChipSelect, 1=Enable Capture, 0=Disable capture)
 SGPIO10 Disable Output (1/High=Disable codec data stream, 0/Low=Enable codec data stream)
 SGPIO11 Direction Output (1/High=TX mode LPC43xx=>CPLD=>DAC, 0/Low=RX mode LPC43xx<=CPLD<=ADC)
*/
void sgpio_configure(sgpio_config_t* const config, const sgpio_direction_t direction)
{
	// Disable all counters during configuration
	SGPIO_CTRL_ENABLE = 0;

	// Set SGPIO output values.
	const uint_fast8_t cpld_direction = (direction == SGPIO_DIRECTION_TX) ? 1 : 0;

	// clang-format off
	SGPIO_GPIO_OUTREG =
		  (cpld_direction << 11) // 1 = Output SGPIO11 High (TX mode)
		                         // 0 = Output SGPIO11 Low  (RX mode)
		| (1L << 10) // disable codec data stream during configuration
		             // (Output SGPIO10 High)
		;
	// clang-format on

	/* The data direction might have changed. Check if we need to
	 * adjust the q inversion. */
	update_q_invert(config);

	// Enable SGPIO pin outputs.
	const uint_fast16_t sgpio_gpio_data_direction =
		(direction == SGPIO_DIRECTION_TX) ? (0xFF << 0) : (0x00 << 0);

	// clang-format off
	SGPIO_GPIO_OENREG =
		  (1L << 14) // GPDMA burst request SGPIO14 active
		| (1L << 11) // direction output SGPIO11 active
		| (1L << 10) // disable output SGPIO10 active
		| (0L <<  9) // capture input SGPIO9 (output i is tri-stated)
		| (0L <<  8) // clock input SGPIO8 (output i is tri-stated)
		| sgpio_gpio_data_direction // 0xFF = Output all SGPIO High (TX mode)
		;                           // 0x00 = Output all SPGIO Low  (RX mode)

	SGPIO_OUT_MUX_CFG( 8) = // SGPIO8:
		  SGPIO_OUT_MUX_CFG_P_OE_CFG(0)  // gpio_oe (state set by GPIO_OEREG)
		| SGPIO_OUT_MUX_CFG_P_OUT_CFG(0) // dout_doutm1 (1-bit mode)
		;
	SGPIO_OUT_MUX_CFG( 9) = // SGPIO9: Input: qualifier
		  SGPIO_OUT_MUX_CFG_P_OE_CFG(0)  // gpio_oe (state set by GPIO_OEREG)
		| SGPIO_OUT_MUX_CFG_P_OUT_CFG(0) // dout_doutm1 (1-bit mode)
		;
    SGPIO_OUT_MUX_CFG(10) = // GPIO10: Output: disable
		  SGPIO_OUT_MUX_CFG_P_OE_CFG(0)  // gpio_oe (state set by GPIO_OEREG)
		| SGPIO_OUT_MUX_CFG_P_OUT_CFG(4) // gpio_out (level set by GPIO_OUTREG)
		;
    SGPIO_OUT_MUX_CFG(11) = // GPIO11: Output: direction
		  SGPIO_OUT_MUX_CFG_P_OE_CFG(0)  // gpio_oe (state set by GPIO_OEREG)
		| SGPIO_OUT_MUX_CFG_P_OUT_CFG(4) // gpio_out (level set by GPIO_OUTREG)
		;
	SGPIO_OUT_MUX_CFG(14) = // SGPIO14: Output: internal GPDMA burst request
		  SGPIO_OUT_MUX_CFG_P_OE_CFG(0)  // dout_oem1 (1-bit mode)
		| SGPIO_OUT_MUX_CFG_P_OUT_CFG(0) // dout_doutm1 (1-bit mode)
		;
	// clang-format on

	const uint_fast8_t output_multiplexing_mode =
		config->slice_mode_multislice ? 11 : 9;
	/* SGPIO0 to SGPIO7 */
	for (uint_fast8_t i = 0; i < 8; i++) {
		// SGPIO pin 0 outputs slice A bit "i".
		SGPIO_OUT_MUX_CFG(i) = SGPIO_OUT_MUX_CFG_P_OE_CFG(0)
			// 11 = dout_doutm8c (8-bit mode 8c) (multislice L0/7, N0/7)
			// 9  = dout_doutm8a (8-bit mode 8a) (A0/7, B0/7)
			| SGPIO_OUT_MUX_CFG_P_OUT_CFG(output_multiplexing_mode);
	}

	const uint_fast8_t slice_indices[] = {
		SGPIO_SLICE_A,
		SGPIO_SLICE_I,
		SGPIO_SLICE_E,
		SGPIO_SLICE_J,
		SGPIO_SLICE_C,
		SGPIO_SLICE_K,
		SGPIO_SLICE_F,
		SGPIO_SLICE_L,
	};
	const uint_fast8_t slice_gpdma = SGPIO_SLICE_H;

	const uint_fast8_t pos = config->slice_mode_multislice ? 0x1f : 0x03;
	const bool single_slice = !config->slice_mode_multislice;
	const uint_fast8_t slice_count = config->slice_mode_multislice ? 8 : 1;

	// Also enable slice D for clkout to the SCTimer
	uint32_t slice_enable_mask = BIT3;

	/* Configure Slice A, I, E, J, C, K, F, L (sgpio_slice_mode_multislice mode) */
	for (uint_fast8_t i = 0; i < slice_count; i++) {
		const uint_fast8_t slice_index = slice_indices[i];
		/* Only for slice0/A and RX mode set input_slice to 1 */
		const bool input_slice = (i == 0) && (direction != SGPIO_DIRECTION_TX);
		/* 0 = Self-loop (slice0/A RX mode), 3 = 8 slices */
		const uint_fast8_t concat_order = (input_slice || single_slice) ? 0 : 3;
		/* 0 = External data pin (slice0/A RX mode), 1 = Concatenate data */
		const uint_fast8_t concat_enable = (input_slice || single_slice) ? 0 : 1;

		// clang-format off
		SGPIO_MUX_CFG(slice_index) =
			  SGPIO_MUX_CFG_CONCAT_ORDER(concat_order)
			| SGPIO_MUX_CFG_CONCAT_ENABLE(concat_enable)
			| SGPIO_MUX_CFG_QUALIFIER_SLICE_MODE(0)  // Select qualifier slice A
			| SGPIO_MUX_CFG_QUALIFIER_PIN_MODE(1)    // Select qualifier pin SGPIO9
			| SGPIO_MUX_CFG_QUALIFIER_MODE(3)        // External SGPIO
			| SGPIO_MUX_CFG_CLK_SOURCE_SLICE_MODE(0) // Select clock source slice D
			| SGPIO_MUX_CFG_CLK_SOURCE_PIN_MODE(0)   // Source clock pin = SGPIO8
			| SGPIO_MUX_CFG_EXT_CLK_ENABLE(1)        // External clock signal selected
			;
		SGPIO_SLICE_MUX_CFG(slice_index) =
			  SGPIO_SLICE_MUX_CFG_INV_QUALIFIER(0)     // Use normal qualifier
			| SGPIO_SLICE_MUX_CFG_PARALLEL_MODE(3)     // Shift 1 byte (8 bits) per clock
			| SGPIO_SLICE_MUX_CFG_DATA_CAPTURE_MODE(0) // Detect rising edge
			| SGPIO_SLICE_MUX_CFG_INV_OUT_CLK(0)       // Normal clock
			| SGPIO_SLICE_MUX_CFG_CLKGEN_MODE(1)       // Use external clock from a pin or other slice
			| SGPIO_SLICE_MUX_CFG_CLK_CAPTURE_MODE(0)  // Use rising clock edge
			| SGPIO_SLICE_MUX_CFG_MATCH_MODE(0)        // Do not match data
			;
		// clang-format on

		SGPIO_PRESET(slice_index) = 0; // External clock, don't care
		SGPIO_COUNT(slice_index) = 0;  // External clock, don't care
		SGPIO_POS(slice_index) = SGPIO_POS_POS_RESET(pos) | SGPIO_POS_POS(pos);
		SGPIO_REG(slice_index) = 0x00000000;    // Primary output data register
		SGPIO_REG_SS(slice_index) = 0x00000000; // Shadow output data register
		// clang-format on

		slice_enable_mask |= (1 << slice_index);
	}

	if (config->slice_mode_multislice == false) {
		// clang-format off
		SGPIO_MUX_CFG(slice_gpdma) =
			  SGPIO_MUX_CFG_CONCAT_ORDER(0)          // Self-loop
			| SGPIO_MUX_CFG_CONCAT_ENABLE(1)         // Concatenate data
			| SGPIO_MUX_CFG_QUALIFIER_SLICE_MODE(0)  // Select qualifier slice A
			| SGPIO_MUX_CFG_QUALIFIER_PIN_MODE(1)    // Select qualifier pin SGPIO9
			| SGPIO_MUX_CFG_QUALIFIER_MODE(3)        // External SGPIO
			| SGPIO_MUX_CFG_CLK_SOURCE_SLICE_MODE(0) // Select clock source slice D
			| SGPIO_MUX_CFG_CLK_SOURCE_PIN_MODE(0)   // Source clock pin = SGPIO8
			| SGPIO_MUX_CFG_EXT_CLK_ENABLE(1)        // External clock signal selected
			;
		SGPIO_SLICE_MUX_CFG(slice_gpdma) =
			  SGPIO_SLICE_MUX_CFG_INV_QUALIFIER(0)     // Use normal qualifier
			| SGPIO_SLICE_MUX_CFG_PARALLEL_MODE(0)     // Shift 1 bit per clock
			| SGPIO_SLICE_MUX_CFG_DATA_CAPTURE_MODE(0) // Detect rising edge
			| SGPIO_SLICE_MUX_CFG_INV_OUT_CLK(0)       // Normal clock
			| SGPIO_SLICE_MUX_CFG_CLKGEN_MODE(1)       // Use external clock from a pin or other slice
			| SGPIO_SLICE_MUX_CFG_CLK_CAPTURE_MODE(0)  // Use rising clock edge
			| SGPIO_SLICE_MUX_CFG_MATCH_MODE(0)        // Do not match data
			;
		// clang-format on

		SGPIO_PRESET(slice_gpdma) = 0; // External clock, don't care
		SGPIO_COUNT(slice_gpdma) = 0;  // External clock, don't care
		SGPIO_POS(slice_gpdma) = SGPIO_POS_POS_RESET(0x1f) | SGPIO_POS_POS(0x1f);
		SGPIO_REG(slice_gpdma) =
			0x11111111; // Primary output data register, LSB -> out
		SGPIO_REG_SS(slice_gpdma) =
			0x11111111; // Shadow output data register, LSB -> out1

		slice_enable_mask |= (1 << slice_gpdma);
	}

	// Start SGPIO operation by enabling slice clocks.
	SGPIO_CTRL_ENABLE = slice_enable_mask;
}

void sgpio_cpld_stream_enable(sgpio_config_t* const config)
{
	(void) config;
	// Enable codec data stream.
	SGPIO_GPIO_OUTREG &= ~(1L << 10); /* SGPIO10 */
}

void sgpio_cpld_stream_disable(sgpio_config_t* const config)
{
	(void) config;
	// Disable codec data stream.
	SGPIO_GPIO_OUTREG |= (1L << 10); /* SGPIO10 */
}

bool sgpio_cpld_stream_is_enabled(sgpio_config_t* const config)
{
	(void) config;
	return (SGPIO_GPIO_OUTREG & (1L << 10)) == 0; /* SGPIO10 */
}

/*
 * The spectrum can be inverted by the analog section of the hardware in two
 * different ways:
 *
 * - The front-end mixer can introduce an inversion depending on the frequency
 *   tuning configuration.
 *
 * - Routing of the analog baseband signals can introduce an inversion
 *   depending on the design of the hardware platform and whether we are in RX
 *   or TX mode.
 *
 * When one but not both of the above effects inverts the spectrum, we instruct
 * the CPLD to correct the inversion by inverting the Q sample value.
 */
static bool mixer_invert = false;

/* Called when TX/RX changes or sgpio_cpld_set_mixer_invert() gets called. */
static void update_q_invert(sgpio_config_t* const config)
{
	/* 1=Output SGPIO11 High(TX mode), 0=Output SGPIO11 Low(RX mode) */
	bool tx_mode = (SGPIO_GPIO_OUTREG & (1 << 11)) > 0;

	/*
	 * This switch will need to change if we modify the CPLD to handle
	 * inversion the same way for RX and TX.
	 */
	bool baseband_invert = false;
	switch (detected_platform()) {
	case BOARD_ID_RAD1O:
	case BOARD_ID_HACKRF1_R9:
		baseband_invert = (tx_mode) ? false : true;
		break;
	default:
		baseband_invert = false;
	}

	gpio_write(config->gpio_q_invert, mixer_invert ^ baseband_invert);
}

void sgpio_cpld_set_mixer_invert(sgpio_config_t* const config, const uint_fast8_t invert)
{
	if (invert) {
		mixer_invert = true;
	} else {
		mixer_invert = false;
	}

	update_q_invert(config);
}