Hardware (CPLD-based) synchronisation

=======================================

This commit allows to synchronise multiple HackRFs with a synchronisation error **below 1 sampling period**

> WARNING: Use this at your own risk. If you don't know what you are doing you may damage your HackRF.
> The author takes no responsability for potential damages

Usage example: synchronise two HackRFs
======================================
1. Chose the master HackRF which will send the synchronisation pulse (HackRF0). HackRF1 will represent the slave hackrf.
2. Retreive the serial number of both HackRFs using `hackrf_info`
3. Use a wire to connect `SYNC_CMD` of HackRF0 to `SYNC_IN` of HackRF0 and HackRF1
4. Run `hackrf_transfer` with the argument `-H 1` to enable hardware synchronisation:
    ```
    $ hackrf_tranfer ... -r rec1.bin -d HackRF1_serial -H 1 | hackrf_transfer ... -r rec0.bin -d HackRF0_serial -H 1
    ```
rec0.bin and rec1.bin will have a time offset below 1 sampling period.
The 1PPS output of GNSS receivers can be used to synchronise HackRFs even if they are far from each other.
>DON'T APPLY INCOMPATIBLE VOLTAGE LEVELS TO THE CPLD PINS

Signal | Header |Pin | Description
-------|--------|----|------------
`SYNC_IN` | P28 | 16 | Synchronisation pulse input
`SYNC_CMD` | P28 | 15 | Synchronisation pulse output

Note:
=====
I had to remove CPLD-based decimation to use a GPIO for enabling hardware.

More info:
==========
[M. Bartolucci, J. A. Del Peral-Rosado, R. Estatuet-Castillo, J. A. Garcia-Molina, M. Crisci and G. E. Corazza, "Synchronisation of low-cost open source SDRs for navigation applications," 2016 8th ESA Workshop on Satellite Navigation Technologies and European Workshop on GNSS Signals and Signal Processing (NAVITEC), Noordwijk, 2016, pp. 1-7.](http://ieeexplore.ieee.org/document/7849328/)

[Alternative link](http://spcomnav.uab.es/docs/conferences/Bartolucci_NAVITEC_2016.pdf)
This commit is contained in:
Marco Bartolucci
2017-05-16 11:39:44 +02:00
parent 747d8e2278
commit 533f9ee332
12 changed files with 48 additions and 87 deletions

View File

@ -132,11 +132,12 @@ static struct gpio_t gpio_cpld_tms = GPIO(3, 1);
static struct gpio_t gpio_cpld_tdi = GPIO(3, 4); static struct gpio_t gpio_cpld_tdi = GPIO(3, 4);
#endif #endif
static struct gpio_t gpio_rx_decimation[3] = { //static struct gpio_t gpio_rx_decimation[3] = {
GPIO(5, 12), // GPIO(5, 12),
GPIO(5, 13), // GPIO(5, 13),
GPIO(5, 14), // GPIO(5, 14),
}; //};
static struct gpio_t gpio_hw_sync_enable = GPIO(5,12);
static struct gpio_t gpio_rx_q_invert = GPIO(0, 13); static struct gpio_t gpio_rx_q_invert = GPIO(0, 13);
i2c_bus_t i2c0 = { i2c_bus_t i2c0 = {
@ -242,11 +243,7 @@ w25q80bv_driver_t spi_flash = {
sgpio_config_t sgpio_config = { sgpio_config_t sgpio_config = {
.gpio_rx_q_invert = &gpio_rx_q_invert, .gpio_rx_q_invert = &gpio_rx_q_invert,
.gpio_rx_decimation = { .gpio_hw_sync_enable = &gpio_hw_sync_enable,
&gpio_rx_decimation[0],
&gpio_rx_decimation[1],
&gpio_rx_decimation[2],
},
.slice_mode_multislice = true, .slice_mode_multislice = true,
}; };
@ -824,11 +821,6 @@ void pin_setup(void) {
scu_pinmux(SCU_PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); scu_pinmux(SCU_PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(SCU_PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); scu_pinmux(SCU_PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
//gpio_input(&gpio_sync_in_a);
//gpio_input(&gpio_sync_in_b);
//gpio_output(&gpio_sync_out_a);
//gpio_output(&gpio_sync_out_b);
#endif #endif
#ifdef RAD1O #ifdef RAD1O
@ -841,11 +833,6 @@ void pin_setup(void) {
scu_pinmux(SCU_PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); scu_pinmux(SCU_PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(SCU_PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); scu_pinmux(SCU_PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
//gpio_input(&gpio_sync_in_a);
//gpio_input(&gpio_sync_in_b);
//gpio_output(&gpio_sync_out_a);
//gpio_output(&gpio_sync_out_b);
#endif #endif
/* enable input on SCL and SDA pins */ /* enable input on SCL and SDA pins */
@ -902,3 +889,7 @@ void led_off(const led_t led) {
void led_toggle(const led_t led) { void led_toggle(const led_t led) {
gpio_toggle(&gpio_led[led]); gpio_toggle(&gpio_led[led]);
} }
void hw_sync_enable(const hw_sync_mode_t hw_sync_mode){
gpio_write(&gpio_hw_sync_enable, hw_sync_mode==1);
}

View File

@ -293,11 +293,7 @@ void led_on(const led_t led);
void led_off(const led_t led); void led_off(const led_t led);
void led_toggle(const led_t led); void led_toggle(const led_t led);
void hw_sync_syn(); void hw_sync_enable(const hw_sync_mode_t hw_sync_mode);
void hw_sync_stop();
void hw_sync_ack();
bool hw_sync_ready();
void hw_sync_copy_state();
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -49,13 +49,11 @@ void sgpio_configure_pin_functions(sgpio_config_t* const config) {
scu_pinmux(SCU_PINMUX_SGPIO14, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[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] */ scu_pinmux(SCU_PINMUX_SGPIO15, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); /* GPIO5[14] */
sgpio_cpld_stream_rx_set_decimation(config, 1);
sgpio_cpld_stream_rx_set_q_invert(config, 0); sgpio_cpld_stream_rx_set_q_invert(config, 0);
hw_sync_enable(0);
gpio_output(config->gpio_rx_q_invert); gpio_output(config->gpio_rx_q_invert);
gpio_output(config->gpio_rx_decimation[0]); gpio_output(config->gpio_hw_sync_enable);
gpio_output(config->gpio_rx_decimation[1]);
gpio_output(config->gpio_rx_decimation[2]);
} }
void sgpio_set_slice_mode( void sgpio_set_slice_mode(
@ -258,21 +256,6 @@ bool sgpio_cpld_stream_is_enabled(sgpio_config_t* const config) {
return (SGPIO_GPIO_OUTREG & (1L << 10)) == 0; /* SGPIO10 */ return (SGPIO_GPIO_OUTREG & (1L << 10)) == 0; /* SGPIO10 */
} }
bool sgpio_cpld_stream_rx_set_decimation(sgpio_config_t* const config, const uint_fast8_t n) {
/* CPLD interface is three bits, SGPIO[15:13]:
* 111: decimate by 1 (skip_n=0, skip no samples)
* 110: decimate by 2 (skip_n=1, skip every other sample)
* 101: decimate by 3 (skip_n=2, skip two of three samples)
* ...
* 000: decimate by 8 (skip_n=7, skip seven of eight samples)
*/
const uint_fast8_t skip_n = n - 1;
gpio_write(config->gpio_rx_decimation[0], (skip_n & 1) == 0);
gpio_write(config->gpio_rx_decimation[1], (skip_n & 2) == 0);
gpio_write(config->gpio_rx_decimation[2], (skip_n & 4) == 0);
return (skip_n < 8);
}
#ifdef RAD1O #ifdef RAD1O
/* The rad1o hardware has a bug which makes it /* The rad1o hardware has a bug which makes it

View File

@ -36,7 +36,7 @@ typedef enum {
typedef struct sgpio_config_t { typedef struct sgpio_config_t {
gpio_t gpio_rx_q_invert; gpio_t gpio_rx_q_invert;
gpio_t gpio_rx_decimation[3]; gpio_t gpio_hw_sync_enable;
bool slice_mode_multislice; bool slice_mode_multislice;
} sgpio_config_t; } sgpio_config_t;

View File

@ -4,18 +4,6 @@ RF codec.
CPLD-based triggered capture CPLD-based triggered capture
============================ ============================
Code related to the paper:
[M. Bartolucci, J. A. del Peral-Rosado, R. Estatuet-Castillo, J. A. Garcia-Molina, M. Crisci, G. E. Corazza, "Synchronisation of low-cost open source SDRs for navigation applications", Proc. 8th ESA Workshop on Satellite Navigation User Equipment Technologies (NAVITEC), Dec 16 2016](http://spcomnav.uab.es/docs/conferences/Bartolucci_NAVITEC_2016.pdf)
Please read the paper for hardware correction and additional details.
* If you don't want to build use `default_sync.xsvf` to flash the CPLD
* This is still a very rough implementation. Synchronization can't be disabled!
Requirements
============
To build this VHDL project and produce an SVF file for flashing the CPLD: To build this VHDL project and produce an SVF file for flashing the CPLD:
* Xilinx WebPACK 13.4 for Windows or Linux. * Xilinx WebPACK 13.4 for Windows or Linux.

BIN
firmware/cpld/sgpio_if/default.xsvf Normal file → Executable file

Binary file not shown.

View File

@ -1,5 +1,5 @@
Programmer Jedec Bit Map Programmer Jedec Bit Map
Date Extracted: Mon May 15 12:52:29 2017 Date Extracted: Mon May 15 14:19:25 2017
QF25812* QF25812*
QP100* QP100*
@ -368,7 +368,7 @@ L012869 000101111001111101000000011*
Note Block 2 * Note Block 2 *
Note Block 2 ZIA * Note Block 2 ZIA *
L012896 1111111111111111* L012896 1111111011100111*
L012912 1111111111111111* L012912 1111111111111111*
L012928 1111111011110011* L012928 1111111011110011*
L012944 1111111111111111* L012944 1111111111111111*
@ -410,17 +410,17 @@ L013504 1111111111111111*
L013520 1111111111111111* L013520 1111111111111111*
Note Block 2 PLA AND array * Note Block 2 PLA AND array *
L013536 11110111110111011111111111111111111111111111111111111111111111111011111111111111* L013536 10110111110111111111111111111111111111111111111111111111111111111011111111111111*
L013616 11111011111011011111111111111111111111111111111111111111111111111011111111111111* L013616 10111011111011111111111111111111111111111111111111111111111111111011111111111111*
L013696 11110111111011111111111111110111111111111111111111111111111111111111111111111111* L013696 11110111110111011111111111111111111111111111111111111111111111111011111111111111*
L013776 11111011110111111111111111110111111111111111111111111111111111111111111111111111* L013776 11111011111011011111111111111111111111111111111111111111111111111011111111111111*
L013856 11111011111111111111111111111111111110111111111111111111111111111111111111111111* L013856 11110111111011111111111111110111111111111111111111111111111111111111111111111111*
L013936 11111011111111111111111101111111111110111111111111111111111111111111111111111111* L013936 11111011110111111111111111110111111111111111111111111111111111111111111111111111*
L014016 11111111111111111111111110111111111101111111111111111111111111111111111111111111* L014016 11111011111111111111111111111111111110111111111111111111111111111111111111111111*
L014096 11111111111011111111111111111111111111111111111111111111111111111111111111111111* L014096 11111111111011111111111111111111111111111111111111111111111111111111111111111111*
L014176 11110111111111111111111110111111111111111111111111111111111111111111111111111111* L014176 11111011111111111111111101111111111110111111111111111111111111111111111111111111*
L014256 11111111111111111111111111111111111111111111111111111111111111111111111111111111* L014256 11111111111111111111111110111111111101111111111111111111111111111111111111111111*
L014336 11111111111111111111111111111111111111111111111111111111111111111111111111111111* L014336 11110111111111111111111110111111111111111111111111111111111111111111111111111111*
L014416 11111111111111111111111111111111111111111111111111111111111111111111111111111111* L014416 11111111111111111111111111111111111111111111111111111111111111111111111111111111*
L014496 11111111111111111111111111111111111111111111111111111111111111111111111111111111* L014496 11111111111111111111111111111111111111111111111111111111111111111111111111111111*
L014576 11111111111111111111111111111111111111111111111111111111111111111111111111111111* L014576 11111111111111111111111111111111111111111111111111111111111111111111111111111111*
@ -472,13 +472,13 @@ L018016 0111111111111111*
L018032 0111111111111111* L018032 0111111111111111*
L018048 0111111111111111* L018048 0111111111111111*
L018064 0111111111111111* L018064 0111111111111111*
L018080 1101011100111000* L018080 0111111111111111*
L018096 1111110111111111* L018096 0111111111111111*
L018112 1111110111111111* L018112 1101011100111000*
L018128 1111111111111111* L018128 1111111111111111*
L018144 1111110111111111* L018144 1111110111111111*
L018160 1111111111111111* L018160 1111110111111111*
L018176 1111111111111111* L018176 1111110111111111*
L018192 1111111111111111* L018192 1111111111111111*
L018208 1111111111111111* L018208 1111111111111111*
L018224 1111111111111111* L018224 1111111111111111*
@ -528,7 +528,7 @@ L018896 1111111111111111*
Note Block 2 I/O Macrocell Configuration 27 bits * Note Block 2 I/O Macrocell Configuration 27 bits *
N Aclk ClkOp Clk:2 ClkFreq R:2 P:2 RegMod:2 INz:2 FB:2 InReg St XorIn:2 RegCom Oe:4 Tm Slw Pu* N Aclk ClkOp Clk:2 ClkFreq R:2 P:2 RegMod:2 INz:2 FB:2 InReg St XorIn:2 RegCom Oe:4 Tm Slw Pu*
L018912 000101111001110100000000011* L018912 000101111001110100000000011*
L018939 000001111001111110011111100* L018939 000001111000011100011111100*
L018966 000101111000011101001000111* L018966 000101111000011101001000111*
L018993 000001111001111110011111100* L018993 000001111001111110011111100*
L019020 000101111000011101001000111* L019020 000101111000011101001000111*
@ -753,5 +753,5 @@ L025810 0*
Note I/O Bank 1 Vcco * Note I/O Bank 1 Vcco *
L025811 0* L025811 0*
C0C4E* C0AA8*
AACE AABC

View File

@ -61,6 +61,7 @@ NET "HOST_DATA<7>" LOC = "P77" | IOSTANDARD = LVCMOS33 | SLEW = SLOW ;
NET "HOST_DIRECTION" LOC = "P71" | IOSTANDARD = LVCMOS33 ; NET "HOST_DIRECTION" LOC = "P71" | IOSTANDARD = LVCMOS33 ;
NET "HOST_DISABLE" LOC = "P76" | IOSTANDARD = LVCMOS33 ; NET "HOST_DISABLE" LOC = "P76" | IOSTANDARD = LVCMOS33 ;
NET "HOST_Q_INVERT" LOC = "P70" | IOSTANDARD = LVCMOS33 ; NET "HOST_Q_INVERT" LOC = "P70" | IOSTANDARD = LVCMOS33 ;
NET "HOST_SYNC_EN" LOC = "P90" | IOSTANDARD = LVCMOS33 ;
NET "HOST_SYNC" LOC = "P55" | IOSTANDARD = LVCMOS33; NET "HOST_SYNC" LOC = "P55" | IOSTANDARD = LVCMOS33;
NET "HOST_SYNC_CMD" LOC = "P56" | IOSTANDARD = LVCMOS33 | SLEW = SLOW ; NET "HOST_SYNC_CMD" LOC = "P56" | IOSTANDARD = LVCMOS33 | SLEW = SLOW ;

View File

@ -30,6 +30,7 @@ entity top is
Port( Port(
HOST_DATA : inout std_logic_vector(7 downto 0); HOST_DATA : inout std_logic_vector(7 downto 0);
HOST_CAPTURE : out std_logic; HOST_CAPTURE : out std_logic;
HOST_SYNC_EN : in std_logic;
HOST_SYNC_CMD : out std_logic; HOST_SYNC_CMD : out std_logic;
HOST_SYNC : in std_logic; HOST_SYNC : in std_logic;
HOST_DISABLE : in std_logic; HOST_DISABLE : in std_logic;
@ -57,6 +58,7 @@ architecture Behavioral of top is
signal host_data_enable_i : std_logic; signal host_data_enable_i : std_logic;
signal host_data_capture_o : std_logic; signal host_data_capture_o : std_logic;
signal host_sync_enable : std_logic := '0';
signal host_sync_o : std_logic := '0'; signal host_sync_o : std_logic := '0';
signal host_sync_i : std_logic := '0'; signal host_sync_i : std_logic := '0';
signal host_sync_latched : std_logic := '0'; signal host_sync_latched : std_logic := '0';
@ -95,6 +97,7 @@ begin
data_from_host_i <= HOST_DATA; data_from_host_i <= HOST_DATA;
HOST_CAPTURE <= host_data_capture_o; HOST_CAPTURE <= host_data_capture_o;
host_sync_enable <= HOST_SYNC_EN;
host_sync_i <= HOST_SYNC; host_sync_i <= HOST_SYNC;
HOST_SYNC_CMD <= host_sync_o; HOST_SYNC_CMD <= host_sync_o;
@ -153,11 +156,11 @@ begin
if rising_edge(host_clk_i) then if rising_edge(host_clk_i) then
if transfer_direction_i = to_dac then if transfer_direction_i = to_dac then
if codec_clk_i = '1' then if codec_clk_i = '1' then
host_data_capture_o <= host_data_enable_i and host_sync_latched; host_data_capture_o <= host_data_enable_i and (host_sync_latched or not host_sync_enable);
end if; end if;
else else
if codec_clk_i = '0' then if codec_clk_i = '0' then
host_data_capture_o <= host_data_enable_i and host_sync_latched; host_data_capture_o <= host_data_enable_i and (host_sync_latched or not host_sync_enable);
end if; end if;
end if; end if;
end if; end if;

View File

@ -275,9 +275,8 @@ void set_transceiver_mode(const transceiver_mode_t new_transceiver_mode) {
if( _transceiver_mode != TRANSCEIVER_MODE_OFF ) { if( _transceiver_mode != TRANSCEIVER_MODE_OFF ) {
si5351c_activate_best_clock_source(&clock_gen); si5351c_activate_best_clock_source(&clock_gen);
if( _hw_sync_mode != HW_SYNC_MODE_OFF) { hw_sync_enable(_hw_sync_mode);
hw_sync_enable();
}
baseband_streaming_enable(&sgpio_config); baseband_streaming_enable(&sgpio_config);
} }
} }

View File

@ -964,7 +964,7 @@ int main(int argc, char** argv) {
} }
if(hw_sync) { if(hw_sync) {
fprintf(stderr, "call hackrf_set_hw_sync_mode(%d)\n", hw_sync); fprintf(stderr, "call hackrf_set_hw_sync_mode(%d)\n", hw_sync_enable);
result = hackrf_set_hw_sync_mode(device, hw_sync_enable ? HW_SYNC_MODE_ON : HW_SYNC_MODE_OFF); result = hackrf_set_hw_sync_mode(device, hw_sync_enable ? HW_SYNC_MODE_ON : HW_SYNC_MODE_OFF);
if( result != HACKRF_SUCCESS ) { if( result != HACKRF_SUCCESS ) {
fprintf(stderr, "hackrf_set_hw_sync_mode() failed: %s (%d)\n", hackrf_error_name(result), result); fprintf(stderr, "hackrf_set_hw_sync_mode() failed: %s (%d)\n", hackrf_error_name(result), result);