mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2025-12-03 07:08:08 +00:00
300 lines
12 KiB
C++
300 lines
12 KiB
C++
/*!
|
|
* \file fpga_acquisition.cc
|
|
* \brief Highly optimized FPGA vector correlator class
|
|
* \authors <ul>
|
|
* <li> Marc Majoral, 2019. mmajoral(at)cttc.cat
|
|
* </ul>
|
|
*
|
|
* Class that controls and executes a highly optimized acquisition HW
|
|
* accelerator in the FPGA
|
|
*
|
|
* -----------------------------------------------------------------------------
|
|
*
|
|
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
|
|
* This file is part of GNSS-SDR.
|
|
*
|
|
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* -----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "fpga_acquisition.h"
|
|
#include "MATH_CONSTANTS.h" // for TWO_PI
|
|
#include <cmath> // for log2
|
|
#include <fcntl.h> // libraries used by the GIPO
|
|
#include <iostream> // for operator<<
|
|
#include <sys/mman.h> // libraries used by the GIPO
|
|
#include <unistd.h> // for write, close, read, ssize_t
|
|
#include <utility>
|
|
|
|
#if USE_GLOG_AND_GFLAGS
|
|
#include <glog/logging.h>
|
|
#else
|
|
#include <absl/log/log.h>
|
|
#endif
|
|
|
|
#ifndef TEMP_FAILURE_RETRY
|
|
#define TEMP_FAILURE_RETRY(exp) \
|
|
({ \
|
|
decltype(exp) _rc; \
|
|
do \
|
|
{ \
|
|
_rc = (exp); \
|
|
} \
|
|
while (_rc == -1 && errno == EINTR); \
|
|
_rc; \
|
|
})
|
|
#endif
|
|
|
|
Fpga_Acquisition::Fpga_Acquisition(std::string device_name,
|
|
uint32_t select_queue,
|
|
uint32_t doppler_max,
|
|
std::vector<std::pair<uint32_t, uint32_t>> &downsampling_filter_specs,
|
|
uint32_t &max_FFT_size) : d_device_name(std::move(device_name)),
|
|
d_resampled_fs(0),
|
|
d_map_base(nullptr), // driver memory map
|
|
d_all_fft_codes(nullptr),
|
|
d_fd(0), // driver descriptor
|
|
d_fft_size(0),
|
|
d_excludelimit(0),
|
|
d_nsamples(0), // number of samples not including padding
|
|
d_filter_num(0),
|
|
d_downsampling_factor(1),
|
|
d_downsampling_filter_delay(0),
|
|
d_select_queue(select_queue),
|
|
d_doppler_max(doppler_max),
|
|
d_PRN(0),
|
|
d_IP_core_version(0)
|
|
{
|
|
Fpga_Acquisition::open_device();
|
|
Fpga_Acquisition::reset_acquisition();
|
|
Fpga_Acquisition::fpga_acquisition_test_register();
|
|
Fpga_Acquisition::read_ipcore_info(downsampling_filter_specs, max_FFT_size);
|
|
Fpga_Acquisition::close_device();
|
|
DLOG(INFO) << "Acquisition FPGA class created";
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::init(uint32_t nsamples, uint32_t fft_size,
|
|
int64_t resampled_fs, uint32_t downsampling_filter_num, uint32_t excludelimit, uint32_t *all_fft_codes)
|
|
{
|
|
d_resampled_fs = resampled_fs;
|
|
d_all_fft_codes = all_fft_codes;
|
|
d_fft_size = fft_size;
|
|
d_excludelimit = excludelimit;
|
|
d_nsamples = nsamples;
|
|
d_filter_num = downsampling_filter_num;
|
|
if (d_filter_num > 0)
|
|
{
|
|
d_downsampling_factor = d_downsampling_filter_specs[d_filter_num - 1].first;
|
|
d_downsampling_filter_delay = d_downsampling_filter_specs[d_filter_num - 1].second;
|
|
}
|
|
}
|
|
|
|
|
|
bool Fpga_Acquisition::set_local_code(uint32_t PRN)
|
|
{
|
|
// select the code with the chosen PRN
|
|
d_PRN = PRN;
|
|
return true;
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::write_local_code()
|
|
{
|
|
d_map_base[CLEAR_MEM_REG_ADDR] = LOCAL_CODE_CLEAR_MEM;
|
|
// write local code
|
|
for (uint32_t k = 0; k < d_fft_size; k++)
|
|
{
|
|
d_map_base[PROG_MEM_ADDR] = d_all_fft_codes[d_fft_size * (d_PRN - 1) + k];
|
|
}
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::open_device()
|
|
{
|
|
// open communication with HW accelerator
|
|
if ((d_fd = open(d_device_name.c_str(), O_RDWR | O_SYNC)) == -1)
|
|
{
|
|
LOG(WARNING) << "Cannot open deviceio" << d_device_name;
|
|
std::cout << "Acq: cannot open deviceio" << d_device_name << '\n';
|
|
}
|
|
d_map_base = reinterpret_cast<volatile uint32_t *>(mmap(nullptr, FPGA_PAGE_SIZE,
|
|
PROT_READ | PROT_WRITE, MAP_SHARED, d_fd, 0));
|
|
|
|
if (d_map_base == reinterpret_cast<void *>(-1))
|
|
{
|
|
LOG(WARNING) << "Cannot map the FPGA acquisition module into user memory";
|
|
std::cout << "Acq: cannot map deviceio" << d_device_name << '\n';
|
|
}
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::fpga_acquisition_test_register()
|
|
{
|
|
// write value to test register
|
|
d_map_base[TEST_REG_ADDR] = TEST_REG_SANITY_CHECK;
|
|
// read value from test register
|
|
const uint32_t readval = d_map_base[TEST_REG_ADDR];
|
|
|
|
if (readval != TEST_REG_SANITY_CHECK)
|
|
{
|
|
LOG(WARNING) << "Acquisition test register sanity check failed";
|
|
}
|
|
else
|
|
{
|
|
LOG(INFO) << "Acquisition test register sanity check success!";
|
|
}
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::read_ipcore_info(std::vector<std::pair<uint32_t, uint32_t>> &downsampling_filter_specs, uint32_t &max_FFT_size)
|
|
{
|
|
d_IP_core_version = d_map_base[FPGA_IP_CORE_VERSION_REG_ADDR];
|
|
if (d_IP_core_version == FPGA_ACQ_IP_VERSION_1)
|
|
{
|
|
// FPGA acquisition IP core version FPGA_ACQ_IP_VERSION_1
|
|
max_FFT_size = d_map_base[MAX_FFT_SIZE_REG_ADDR];
|
|
if (d_select_queue == ACQ_BUFF_0)
|
|
{
|
|
// Check if the requested downsampling filter is available on the FPGA and read its implemented latency.
|
|
uint32_t downsampling_filter_dec_factors = d_map_base[DOWNSAMPLING_FILTER_DEC_FACTORS_REG_ADDR];
|
|
uint32_t downsampling_filter_latencies = d_map_base[DOWNSAMPLING_FILTER_LATENCIES_REG_ADDR];
|
|
for (uint32_t filt_num = 1; filt_num <= MAX_FILTERS_AVAILABLE; filt_num++)
|
|
{
|
|
// The information about the instantiated downsampling filters in the FPGA is ordered such that the largest downsampling factor is stored in the least significant bits (LSBs), while the smallest downsampling factor is stored in the most significant bits (MSBs).
|
|
uint32_t dec_factor = downsampling_filter_dec_factors & BIT_MASK_4;
|
|
downsampling_filter_dec_factors = downsampling_filter_dec_factors >> RSHIFT_4_BITS;
|
|
|
|
uint32_t filter_latency = downsampling_filter_latencies & BIT_MASK_8;
|
|
downsampling_filter_latencies = downsampling_filter_latencies >> RSHIFT_8_BITS;
|
|
if (dec_factor != 0)
|
|
{
|
|
downsampling_filter_specs.emplace_back(dec_factor, filter_latency);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// FPGA Acquisition IP core versions earlier than FPGA_ACQ_IP_VERSION_1
|
|
max_FFT_size = DEFAULT_MAX_FFT_SIZE;
|
|
if (d_select_queue == ACQ_BUFF_0)
|
|
{
|
|
// An acquisition resampler with a downsampling factor of DEFAULT_DOWNSAMPLING_FACTOR is implemented in the L1/E1 frequency band
|
|
downsampling_filter_specs.emplace_back(DEFAULT_DOWNSAMPLING_FACTOR, DEFAULT_DOWNSAMPLING_FILTER_DELAY);
|
|
}
|
|
}
|
|
|
|
d_downsampling_filter_specs = downsampling_filter_specs;
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::run_acquisition()
|
|
{
|
|
// enable interrupts
|
|
int32_t reenable = 1;
|
|
// int32_t disable_int = 0;
|
|
const ssize_t nbytes = TEMP_FAILURE_RETRY(write(d_fd, reinterpret_cast<void *>(&reenable), sizeof(int32_t)));
|
|
if (nbytes != sizeof(int32_t))
|
|
{
|
|
std::cerr << "Error enabling run in the FPGA.\n";
|
|
}
|
|
|
|
// launch the acquisition process
|
|
d_map_base[ACQ_COMMAND_FLAGS_REG_ADDR] = LAUNCH_ACQUISITION; // writing a 1 to reg 8 launches the acquisition process
|
|
int32_t irq_count;
|
|
|
|
// wait for interrupt
|
|
const ssize_t nb = read(d_fd, &irq_count, sizeof(irq_count));
|
|
if (nb != sizeof(irq_count))
|
|
{
|
|
std::cout << "acquisition module Read failed to retrieve 4 bytes!\n";
|
|
std::cout << "acquisition module Interrupt number " << irq_count << '\n';
|
|
}
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::set_block_exp(uint32_t total_block_exp)
|
|
{
|
|
d_map_base[MAX_FFT_SCALING_FACTOR_REG_ADDR] = total_block_exp;
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::set_doppler_sweep(uint32_t num_sweeps, uint32_t doppler_step, int32_t doppler_min)
|
|
{
|
|
// The doppler step can never be outside the range -pi to +pi, otherwise there would be aliasing
|
|
// The FPGA expects phase_step_rad between -1 (-pi) to +1 (+pi)
|
|
float phase_step_rad_real = 2.0F * (doppler_min) / static_cast<float>(d_resampled_fs);
|
|
d_map_base[DOPPLER_MIN_REG_ADDR] = static_cast<int32_t>(phase_step_rad_real * (POW_2_31));
|
|
|
|
// repeat the calculation with the doppler step
|
|
phase_step_rad_real = 2.0F * (doppler_step) / static_cast<float>(d_resampled_fs);
|
|
d_map_base[DOPPLER_STEP_REG_ADDR] = static_cast<int32_t>(phase_step_rad_real * (POW_2_31)); // * 2^29 (in total it makes x2^31 in two steps to avoid the warnings
|
|
|
|
// write number of doppler sweeps
|
|
d_map_base[NUM_DOPPLER_SEARCH_STEPS_REG_ADDR] = num_sweeps;
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::configure_acquisition()
|
|
{
|
|
// Fpga_Acquisition::();
|
|
d_map_base[FREQ_BAND_DOWNSAMPLE_REG_ADDR] = d_select_queue | (d_filter_num << 4);
|
|
d_map_base[FFT_LENGTH_REG_ADDR] = d_fft_size;
|
|
d_map_base[CORR_NSAMPLES_REG_ADDR] = d_nsamples;
|
|
d_map_base[LOG2_FFT_LENGTH_REG_ADDR] = static_cast<int32_t>(std::log2(static_cast<float>(d_fft_size))); // log2 FFTlength
|
|
d_map_base[EXCL_LIM_REG_ADDR] = d_excludelimit;
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::read_acquisition_results(uint32_t *max_index,
|
|
float *firstpeak, float *secondpeak, uint64_t *initial_sample, float *power_sum, uint32_t *doppler_index, uint32_t *total_blk_exp)
|
|
{
|
|
uint64_t initial_sample_tmp = (static_cast<uint64_t>(d_map_base[SAMPLESTAMP_MSW_REG_ADDR]) << 32) + (static_cast<uint64_t>(d_map_base[SAMPLESTAMP_LSW_REG_ADDR]));
|
|
uint32_t max_index_tmp = d_map_base[ACQ_DELAY_SAMPLES_REG_ADDR]; // read max index position
|
|
if (d_downsampling_factor > 1)
|
|
{
|
|
initial_sample_tmp *= static_cast<uint64_t>(d_downsampling_factor); // take into account the downsampling factor for the sample stamp
|
|
initial_sample_tmp -= static_cast<uint64_t>(d_downsampling_filter_delay); // take into account the downsampling filter delay
|
|
max_index_tmp *= d_downsampling_factor; // take into account the downsampling factor for the acq. delay
|
|
}
|
|
*initial_sample = initial_sample_tmp;
|
|
*max_index = max_index_tmp;
|
|
*firstpeak = d_map_base[MAG_SQ_FIRST_PEAK_REG_ADDR]; // read first peak value
|
|
*secondpeak = d_map_base[MAG_SQ_SECOND_PEAK_REG_ADDR]; // read second peak value
|
|
*power_sum = 0; // The FPGA does not currently support power sum.
|
|
*doppler_index = d_map_base[DOPPLER_INDEX_REG_ADDR]; // read doppler index -- this read releases the interrupt line
|
|
*total_blk_exp = d_map_base[FFT_SCALING_FACTOR_REG_ADDR]; // read FFT block exponent
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::close_device()
|
|
{
|
|
auto *aux = const_cast<uint32_t *>(d_map_base);
|
|
if (munmap(static_cast<void *>(aux), FPGA_PAGE_SIZE) == -1)
|
|
{
|
|
std::cout << "Failed to unmap memory uio\n";
|
|
}
|
|
close(d_fd);
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::reset_acquisition()
|
|
{
|
|
d_map_base[ACQ_COMMAND_FLAGS_REG_ADDR] = RESET_ACQUISITION; // setting bit 2 resets the acquisition. This causes a reset of all
|
|
// the FPGA HW modules including the multicorrelators
|
|
}
|
|
|
|
|
|
void Fpga_Acquisition::stop_acquisition()
|
|
{
|
|
d_map_base[ACQ_COMMAND_FLAGS_REG_ADDR] = STOP_ACQUISITION; // setting bit 3 stops the acquisition module. This stops all
|
|
// the FPGA HW modules including the multicorrelators
|
|
}
|