From 03e8f97d2e8a1d84a21c17866dca3559112ee8ad Mon Sep 17 00:00:00 2001 From: Marc Majoral Date: Sat, 13 Feb 2021 17:10:43 +0100 Subject: [PATCH] real-time FPGA receiver buffer monitoring --- .../adapters/ad9361_fpga_signal_source.cc | 68 +++++++- .../adapters/ad9361_fpga_signal_source.h | 16 +- .../signal_source/libs/CMakeLists.txt | 2 + .../signal_source/libs/fpga_buffer_monitor.cc | 147 ++++++++++++++++++ .../signal_source/libs/fpga_buffer_monitor.h | 94 +++++++++++ 5 files changed, 317 insertions(+), 10 deletions(-) create mode 100644 src/algorithms/signal_source/libs/fpga_buffer_monitor.cc create mode 100644 src/algorithms/signal_source/libs/fpga_buffer_monitor.h diff --git a/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc index 613e981b8..13dc549d8 100644 --- a/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc +++ b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc @@ -98,22 +98,24 @@ Ad9361FpgaSignalSource::Ad9361FpgaSignalSource(const ConfigurationInterface *con throw std::exception(); } - switch_position = configuration->property(role + ".switch_position", 0); - if (switch_position != 0 && switch_position != 2) + switch_position_ = configuration->property(role + ".switch_position", 0); + if (switch_position_ != 0 && switch_position_ != 2) { std::cout << "SignalSource.switch_position configuration parameter must be either 0: read from file(s) via DMA, or 2: read from AD9361\n"; std::cout << "SignalSource.switch_position configuration parameter set to its default value switch_position=0 - read from file(s)\n"; - switch_position = 0; + switch_position_ = 0; } switch_fpga = std::make_shared(device_io_name); - switch_fpga->set_switch_position(switch_position); + switch_fpga->set_switch_position(switch_position_); item_size_ = sizeof(gr_complex); std::cout << "Sample rate: " << sample_rate_ << " Sps\n"; - if (switch_position == 0) // Inject file(s) via DMA + enable_ovf_check_buffer_monitor_ = 0; // check buffer overflow and buffer monitor disabled by default + + if (switch_position_ == 0) // Inject file(s) via DMA { enable_DMA_ = true; const std::string empty_string; @@ -154,7 +156,7 @@ Ad9361FpgaSignalSource::Ad9361FpgaSignalSource(const ConfigurationInterface *con freq_band = "L1L2"; } } - if (switch_position == 2) // Real-time via AD9361 + if (switch_position_ == 2) // Real-time via AD9361 { // some basic checks if ((rf_port_select_ != "A_BALANCED") and (rf_port_select_ != "B_BALANCED") and (rf_port_select_ != "A_N") and (rf_port_select_ != "B_N") and (rf_port_select_ != "B_P") and (rf_port_select_ != "C_N") and (rf_port_select_ != "C_P") and (rf_port_select_ != "TX_MONITOR1") and (rf_port_select_ != "TX_MONITOR2") and (rf_port_select_ != "TX_MONITOR1_2")) @@ -292,6 +294,23 @@ Ad9361FpgaSignalSource::Ad9361FpgaSignalSource(const ConfigurationInterface *con std::cout << "Exception cached when configuring the TX carrier: " << e.what() << '\n'; } } + + // when the receiver is working in real-time mode via AD9361 perform buffer overflow checking and buffer monitoring + enable_ovf_check_buffer_monitor_ = 1; + enable_buffer_monitor_ = configuration->property(role + ".enable_buffer_monitor", false); // only buffer overflow checking by default + + std::string device_io_name_buffer_monitor; + + // find the uio device file corresponding to the buffer monitor + if (find_uio_dev_file_name(device_io_name_buffer_monitor, buffer_monitor_device_name, 0) < 0) + { + std::cout << "Cannot find the FPGA uio device file corresponding to device name " << buffer_monitor_device_name << std::endl; + throw std::exception(); + } + + uint32_t num_freq_bands = (freq_band.compare("L1L2")) ? 1 : 2; + buffer_monitor_fpga = std::make_shared(device_io_name_buffer_monitor, num_freq_bands); + thread_buffer_monitor = std::thread([&] { run_buffer_monitor_process(); }); } // dynamic bits selection @@ -331,7 +350,7 @@ Ad9361FpgaSignalSource::Ad9361FpgaSignalSource(const ConfigurationInterface *con Ad9361FpgaSignalSource::~Ad9361FpgaSignalSource() { /* cleanup and exit */ - if (switch_position == 0) // read samples from a file via DMA + if (switch_position_ == 0) // read samples from a file via DMA { std::unique_lock lock(dma_mutex); enable_DMA_ = false; // disable the DMA @@ -342,7 +361,7 @@ Ad9361FpgaSignalSource::~Ad9361FpgaSignalSource() } } - if (switch_position == 2) // Real-time via AD9361 + if (switch_position_ == 2) // Real-time via AD9361 { if (rf_shutdown_) { @@ -363,6 +382,16 @@ Ad9361FpgaSignalSource::~Ad9361FpgaSignalSource() } } } + + // disable buffer overflow checking and buffer monitoring + std::unique_lock lock(buffer_monitor_mutex); + enable_ovf_check_buffer_monitor_ = false; + lock.unlock(); + + if (thread_buffer_monitor.joinable()) + { + thread_buffer_monitor.join(); + } } std::unique_lock lock(dynamic_bit_selection_mutex); @@ -646,6 +675,29 @@ void Ad9361FpgaSignalSource::run_dynamic_bit_selection_process() } } +void Ad9361FpgaSignalSource::run_buffer_monitor_process() +{ + bool enable_ovf_check_buffer_monitor_active = true; + + std::this_thread::sleep_for(std::chrono::milliseconds(buffer_monitoring_initial_delay_ms)); + + while (enable_ovf_check_buffer_monitor_active) + { + buffer_monitor_fpga->check_buffer_overflow(); + if (enable_buffer_monitor_) + { + buffer_monitor_fpga->monitor_buffer_status(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(buffer_monitor_period_ms)); + std::unique_lock lock(buffer_monitor_mutex); + if (enable_ovf_check_buffer_monitor_ == false) + { + enable_ovf_check_buffer_monitor_active = false; + } + lock.unlock(); + } +} + void Ad9361FpgaSignalSource::connect(gr::top_block_sptr top_block) { diff --git a/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.h b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.h index 2e1c951a3..92beee3f0 100644 --- a/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.h +++ b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.h @@ -19,6 +19,7 @@ #define GNSS_SDR_AD9361_FPGA_SIGNAL_SOURCE_H #include "concurrent_queue.h" +#include "fpga_buffer_monitor.h" #include "fpga_dynamic_bit_selection.h" #include "fpga_switch.h" #include "gnss_block_interface.h" @@ -74,22 +75,30 @@ public: private: const std::string switch_device_name = "AXIS_Switch_v1_0_0"; // Switch UIO device name - const std::string dyn_bit_sel_device_name = "dynamic_bits_selector"; // Switch UIO device name + const std::string dyn_bit_sel_device_name = "dynamic_bits_selector"; // Switch dhnamic bit selector device name + const std::string buffer_monitor_device_name = "buffer_monitor"; // buffer monitor device name // perform dynamic bit selection every 500 ms by default static const uint32_t Gain_control_period_ms = 500; + // check buffer overflow and perform buffer monitoring every 1s by default + static const uint32_t buffer_monitor_period_ms = 1000; + // buffer overflow and buffer monitoring initial delay + static const uint32_t buffer_monitoring_initial_delay_ms = 2000; void run_DMA_process(const std::string &FreqBand, const std::string &Filename1, const std::string &Filename2); void run_dynamic_bit_selection_process(); + void run_buffer_monitor_process(); std::thread thread_file_to_dma; std::thread thread_dynamic_bit_selection; + std::thread thread_buffer_monitor; std::shared_ptr switch_fpga; std::shared_ptr dynamic_bit_selection_fpga; + std::shared_ptr buffer_monitor_fpga; std::string role_; @@ -106,6 +115,7 @@ private: std::mutex dma_mutex; std::mutex dynamic_bit_selection_mutex; + std::mutex buffer_monitor_mutex; double rf_gain_rx1_; double rf_gain_rx2_; @@ -125,7 +135,7 @@ private: size_t item_size_; uint32_t in_stream_; uint32_t out_stream_; - int32_t switch_position; + int32_t switch_position_; bool enable_dds_lo_; bool filter_auto_; @@ -136,6 +146,8 @@ private: bool rx2_enable_; bool enable_DMA_; bool enable_dynamic_bit_selection_; + bool enable_buffer_monitor_; + bool enable_ovf_check_buffer_monitor_; bool rf_shutdown_; }; diff --git a/src/algorithms/signal_source/libs/CMakeLists.txt b/src/algorithms/signal_source/libs/CMakeLists.txt index dc65b1476..1dfbeea44 100644 --- a/src/algorithms/signal_source/libs/CMakeLists.txt +++ b/src/algorithms/signal_source/libs/CMakeLists.txt @@ -17,6 +17,8 @@ if(ENABLE_FPGA OR ENABLE_AD9361) set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_HEADERS} fpga_switch.h) set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} fpga_dynamic_bit_selection.cc) set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_HEADERS} fpga_dynamic_bit_selection.h) + set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} fpga_buffer_monitor.cc) + set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_HEADERS} fpga_buffer_monitor.h) endif() set(SIGNAL_SOURCE_LIB_SOURCES diff --git a/src/algorithms/signal_source/libs/fpga_buffer_monitor.cc b/src/algorithms/signal_source/libs/fpga_buffer_monitor.cc new file mode 100644 index 000000000..40cbad008 --- /dev/null +++ b/src/algorithms/signal_source/libs/fpga_buffer_monitor.cc @@ -0,0 +1,147 @@ +/*! + * \file fpga_buffer_monitor.cc + * \brief Check receiver buffer overflow and monitor the status of the receiver buffers. + * \authors
    + *
  • Marc Majoral, 2020. mmajoral(at)cttc.es + *
+ * + * Class that checks the receiver buffer overflow flags and monitors the status of the receiver buffers. + * + * + * ----------------------------------------------------------------------------- + * + * 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_buffer_monitor.h" +#include +#include // for open, O_RDWR, O_SYNC +#include // for cout +#include // for mmap + +Fpga_buffer_monitor::Fpga_buffer_monitor(const std::string &device_name, uint32_t num_freq_bands) +{ + d_num_freq_bands = num_freq_bands; + + // open device descriptor + if ((d_device_descriptor = open(device_name.c_str(), O_RDWR | O_SYNC)) == -1) + { + LOG(WARNING) << "Cannot open deviceio" << device_name; + } + + // device memory map + d_map_base = reinterpret_cast(mmap(nullptr, FPGA_PAGE_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, d_device_descriptor, 0)); + + if (d_map_base == reinterpret_cast(-1)) + { + LOG(WARNING) << "Cannot map the FPGA buffer monitor module"; + std::cout << "Could not map the FPGA buffer monitor \n"; + } + + // sanity check: check test register + if (buffer_monitor_test_register() < 0) + { + LOG(WARNING) << "FPGA buffer monitor test register sanity check failed"; + std::cout << "FPGA buffer monitor test register sanity check failed\n"; + } + else + { + LOG(INFO) << "FPGA buffer monitor test register sanity check success !"; + } + + DLOG(INFO) << "FPGA buffer monitor class created"; + + // initialize maximum buffer occupancy in case buffer monitoring is enabled + max_buff_occ_freq_band_0 = 0; + max_buff_occ_freq_band_1 = 0; +} + + +Fpga_buffer_monitor::~Fpga_buffer_monitor() +{ + close_device(); +} + + +void Fpga_buffer_monitor::check_buffer_overflow() +{ + // check buffer overflow flags + uint32_t buffer_overflow_status = d_map_base[overflow_flags_reg_addr]; + + if ((buffer_overflow_status & overflow_freq_band_0_bit_pos) != 0) + { + std::cout << "Buffer overflow in frequency band 0" << std::endl; + throw std::exception(); + } + + if (d_num_freq_bands > 1) + { + if ((buffer_overflow_status & overflow_freq_band_1_bit_pos) != 0) + { + std::cout << "Buffer overflow in frequency band 1" << std::endl; + throw std::exception(); + } + } +} + + +void Fpga_buffer_monitor::monitor_buffer_status(void) +{ + uint32_t current_buff_occ_freq_band_0 = d_map_base[current_buff_occ_freq_band_0_reg_addr] * num_sapmples_per_buffer_element; + uint32_t temp_max_buff_occ_freq_band_0 = d_map_base[max_buff_occ_freq_band_0_reg_addr] * num_sapmples_per_buffer_element; + if (temp_max_buff_occ_freq_band_0 > max_buff_occ_freq_band_0) + { + max_buff_occ_freq_band_0 = temp_max_buff_occ_freq_band_0; + } + std::cout << "current buffer occupancy frequency band 0 = " << current_buff_occ_freq_band_0 << " samples " << std::endl; + std::cout << "temporary maximum buffer occupancy frequency band 0 = " << temp_max_buff_occ_freq_band_0 << " samples " << std::endl; + std::cout << "maximum buffer occupancy frequency band 0 = " << max_buff_occ_freq_band_0 << " samples " << std::endl; + + if (d_num_freq_bands > 1) + { + uint32_t current_buff_occ_freq_band_1 = d_map_base[current_buff_occ_freq_band_1_reg_addr] * num_sapmples_per_buffer_element; + uint32_t temp_max_buff_occ_freq_band_1 = d_map_base[max_buff_occ_freq_band_1_reg_addr] * num_sapmples_per_buffer_element; + if (temp_max_buff_occ_freq_band_1 > max_buff_occ_freq_band_1) + { + max_buff_occ_freq_band_1 = temp_max_buff_occ_freq_band_1; + } + std::cout << "current buffer occupancy frequency band 1 = " << current_buff_occ_freq_band_1 << " samples " << std::endl; + std::cout << "temporary maximum buffer occupancy frequency band 1 = " << temp_max_buff_occ_freq_band_1 << " samples " << std::endl; + std::cout << "maximum buffer occupancy frequency band 1 = " << max_buff_occ_freq_band_1 << " samples " << std::endl; + } +} + + +int32_t Fpga_buffer_monitor::buffer_monitor_test_register(void) +{ + // write value to test register + d_map_base[test_reg_addr] = test_register_writeval; + // read value from test register + uint32_t readval = d_map_base[test_reg_addr]; + + if (test_register_writeval != readval) + { + return -1; + } + + return 0; +} + + +void Fpga_buffer_monitor::close_device() +{ + auto *aux = const_cast(d_map_base); + if (munmap(static_cast(aux), FPGA_PAGE_SIZE) == -1) + { + std::cout << "Failed to unmap memory uio\n"; + } + + close(d_device_descriptor); +} diff --git a/src/algorithms/signal_source/libs/fpga_buffer_monitor.h b/src/algorithms/signal_source/libs/fpga_buffer_monitor.h new file mode 100644 index 000000000..f07a16357 --- /dev/null +++ b/src/algorithms/signal_source/libs/fpga_buffer_monitor.h @@ -0,0 +1,94 @@ +/*! + * \file fpga_buffer_monitor.h + * \brief Check receiver buffer overflow and monitor the status of the receiver buffers. + * \authors
    + *
  • Marc Majoral, 2021. mmajoral(at)cttc.es + *
+ * + * Class that checks the receiver buffer overflow flags and monitors the status of the receiver buffers. + * + * + * ----------------------------------------------------------------------------- + * + * 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 + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_FPGA_BUFFER_MONITOR_H +#define GNSS_SDR_FPGA_BUFFER_MONITOR_H + +#include +#include +#include + +/** \addtogroup Signal_Source + * \{ */ +/** \addtogroup Signal_Source_libs + * \{ */ + + +/*! + * \brief Class that checks the receiver buffer overflow flags and monitors the status of the receiver buffers. + */ +class Fpga_buffer_monitor +{ +public: + /*! + * \brief Constructor + */ + explicit Fpga_buffer_monitor(const std::string& device_name, uint32_t num_freq_bands); + + /*! + * \brief Destructor + */ + ~Fpga_buffer_monitor(); + + /*! + * \brief This function checks the status of the buffer overflow flags in the FPGA + */ + void check_buffer_overflow(void); + + /*! + * \brief This function monitors the FPGA buffer status + */ + void monitor_buffer_status(void); + +private: + static const size_t FPGA_PAGE_SIZE = 0x10000; + static const uint32_t test_register_writeval = 0x55AA; + static const uint32_t num_sapmples_per_buffer_element = 2; + // write addresses + static const uint32_t reset_overflow_flags_and_max_buff_size_reg_addr = 0; + // read-write addresses + static const uint32_t test_reg_addr = 7; + // read addresses + static const uint32_t current_buff_occ_freq_band_0_reg_addr = 0; + static const uint32_t current_buff_occ_freq_band_1_reg_addr = 1; + static const uint32_t max_buff_occ_freq_band_0_reg_addr = 2; + static const uint32_t max_buff_occ_freq_band_1_reg_addr = 3; + static const uint32_t overflow_flags_reg_addr = 4; + // FPGA-related constants + static const uint32_t overflow_freq_band_0_bit_pos = 1; + static const uint32_t overflow_freq_band_1_bit_pos = 2; + + int32_t buffer_monitor_test_register(void); + void close_device(void); + + volatile unsigned* d_map_base; // driver memory map corresponding to the FPGA buffer monitor + int d_device_descriptor; // driver descriptor corresponding to the FPGA buffer monitor + + uint32_t d_num_freq_bands; + + uint32_t max_buff_occ_freq_band_0; + uint32_t max_buff_occ_freq_band_1; +}; + + +/** \} */ +/** \} */ +#endif // GNSS_SDR_FPGA_BUFFER_MONITOR_H