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..4a6869a73 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_active_ = false; // 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,26 @@ 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 if dump is enabled perform buffer monitoring + enable_ovf_check_buffer_monitor_active_ = true; + + std::string device_io_name_buffer_monitor; + + dump_ = configuration->property(role + ".dump", false); + std::string dump_filename = configuration->property(role + ".dump_filename", default_dump_filename); + + // 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, dump_, dump_filename, queue); + thread_buffer_monitor = std::thread([&] { run_buffer_monitor_process(); }); } // dynamic bits selection @@ -331,7 +353,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 +364,7 @@ Ad9361FpgaSignalSource::~Ad9361FpgaSignalSource() } } - if (switch_position == 2) // Real-time via AD9361 + if (switch_position_ == 2) // Real-time via AD9361 { if (rf_shutdown_) { @@ -363,6 +385,16 @@ Ad9361FpgaSignalSource::~Ad9361FpgaSignalSource() } } } + + // disable buffer overflow checking and buffer monitoring + std::unique_lock lock(buffer_monitor_mutex); + enable_ovf_check_buffer_monitor_active_ = false; + lock.unlock(); + + if (thread_buffer_monitor.joinable()) + { + thread_buffer_monitor.join(); + } } std::unique_lock lock(dynamic_bit_selection_mutex); @@ -646,6 +678,25 @@ 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_and_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_active_ == 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..7ee3c9b2a 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 + const std::string default_dump_filename = std::string("FPGA_buffer_monitor_dump.dat"); // 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,9 @@ private: bool rx2_enable_; bool enable_DMA_; bool enable_dynamic_bit_selection_; + bool enable_ovf_check_buffer_monitor_active_; + bool enable_ovf_check_buffer_monitor_; + bool dump_; bool rf_shutdown_; }; diff --git a/src/algorithms/signal_source/libs/CMakeLists.txt b/src/algorithms/signal_source/libs/CMakeLists.txt index dc65b1476..30bb8f5d8 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 @@ -90,6 +92,13 @@ if(ENABLE_FMCOMMS2 OR ENABLE_AD9361) endif() endif() +if(ENABLE_FPGA OR ENABLE_AD9361) + target_link_libraries(signal_source_libs + PUBLIC + algorithms_libs + ) +endif() + if(ENABLE_CLANG_TIDY) if(CLANG_TIDY_EXE) set_target_properties(signal_source_libs 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..adb92d7fb --- /dev/null +++ b/src/algorithms/signal_source/libs/fpga_buffer_monitor.cc @@ -0,0 +1,260 @@ +/*! + * \file fpga_buffer_monitor.cc + * \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-2021 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#include "fpga_buffer_monitor.h" +#include "command_event.h" +#include "gnss_sdr_create_directory.h" +#include "gnss_sdr_filesystem.h" +#include +#include // for time, localtime +#include // for open, O_RDWR, O_SYNC +#include // for string, ofstream +#include // for cout +#include // for mmap + +Fpga_buffer_monitor::Fpga_buffer_monitor(const std::string &device_name, uint32_t num_freq_bands, bool dump, std::string dump_filename, Concurrent_Queue *queue) +{ + d_num_freq_bands = num_freq_bands; + d_dump = dump; + d_dump_filename = dump_filename; + d_queue = queue; + + // 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 + d_max_buff_occ_freq_band_0 = 0; + d_max_buff_occ_freq_band_1 = 0; + + if (d_dump) + { + std::string dump_path; + // Get path + if (d_dump_filename.find_last_of('/') != std::string::npos) + { + const std::string dump_filename_ = d_dump_filename.substr(d_dump_filename.find_last_of('/') + 1); + dump_path = d_dump_filename.substr(0, d_dump_filename.find_last_of('/')); + d_dump_filename = dump_filename_; + } + else + { + dump_path = std::string("."); + } + if (d_dump_filename.empty()) + { + d_dump_filename = "FPGA_buffer_monitor"; + } + // remove extension if any + if (d_dump_filename.substr(1).find_last_of('.') != std::string::npos) + { + d_dump_filename = d_dump_filename.substr(0, d_dump_filename.find_last_of('.')); + } + d_dump_filename = dump_path + fs::path::preferred_separator + d_dump_filename; + // create directory + if (!gnss_sdr_create_directory(dump_path)) + { + std::cerr << "GNSS-SDR cannot create dump file for the Buffer Monitor block. Wrong permissions?\n"; + d_dump = false; + } + + std::string dump_filename_ = d_dump_filename; + dump_filename_.append(".dat"); + + if (!d_dump_file.is_open()) + { + try + { + d_dump_file.exceptions(std::ifstream::failbit | std::ifstream::badbit); + d_dump_file.open(dump_filename_.c_str(), std::ios::out | std::ios::binary); + LOG(INFO) << "FPGA buffer monitor dump enabled. Log file: " << dump_filename_.c_str(); + } + catch (const std::ifstream::failure &e) + { + LOG(WARNING) << "Exception opening FPGA buffer monitor dump file " << e.what(); + } + } + } +} + + +Fpga_buffer_monitor::~Fpga_buffer_monitor() +{ + close_device(); + + if (d_dump) + { + if (d_dump_file.is_open()) + { + try + { + d_dump_file.close(); + } + catch (const std::exception &ex) + { + LOG(WARNING) << "Exception in FPGA buffer monitor destructor: " << ex.what(); + } + } + } +} + + +void Fpga_buffer_monitor::check_buffer_overflow_and_monitor_buffer_status(void) +{ + // check buffer overflow flags + bool overflow_detected = false; + uint32_t buffer_overflow_status = d_map_base[overflow_flags_reg_addr]; + + if ((buffer_overflow_status & overflow_freq_band_0_bit_pos) != 0) + { + overflow_detected = true; + std::cout << "Buffer overflow in frequency band 0" << std::endl; + } + + if (d_num_freq_bands > 1) + { + if ((buffer_overflow_status & overflow_freq_band_1_bit_pos) != 0) + { + overflow_detected = true; + std::cout << "Buffer overflow in frequency band 1" << std::endl; + } + } + + if (overflow_detected) + { + LOG(ERROR) << "Stopping receiver, FPGA buffer overflow detected."; + d_queue->push(pmt::make_any(command_event_make(200, 0))); + } + + // buffer monitor + if (d_dump == 1) + { + 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 > d_max_buff_occ_freq_band_0) + { + d_max_buff_occ_freq_band_0 = temp_max_buff_occ_freq_band_0; + } + + time_t rawtime; + struct tm *timeinfo; + char buff_time_ch[80]; + + time(&rawtime); + timeinfo = localtime(&rawtime); + + strftime(buff_time_ch, sizeof(buff_time_ch), "%d-%m-%Y %H:%M:%S", timeinfo); + std::string buffer_time(buff_time_ch); + d_dump_file << buffer_time << " "; + + std::string buffer_txt; + // current buffer occupancy frequency band 0 (number of samples) + buffer_txt = std::to_string(current_buff_occ_freq_band_0); + d_dump_file << buffer_txt << " "; + // temporary maximum buffer occupancy frequency band 0 (number of samples) + buffer_txt = std::to_string(temp_max_buff_occ_freq_band_0); + d_dump_file << buffer_txt << " "; + // maximum buffer occupancy frequency band 0 (number of samples) + buffer_txt = std::to_string(d_max_buff_occ_freq_band_0); + d_dump_file << buffer_txt; + + if (d_num_freq_bands > 1) + { + d_dump_file << " "; + 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 > d_max_buff_occ_freq_band_1) + { + d_max_buff_occ_freq_band_1 = temp_max_buff_occ_freq_band_1; + } + + // current buffer occupancy frequency band 1 (number of samples) + buffer_txt = std::to_string(current_buff_occ_freq_band_1); + d_dump_file << buffer_txt << " "; + // temporary maximum buffer occupancy frequency band 1 (number of samples) + buffer_txt = std::to_string(temp_max_buff_occ_freq_band_1); + d_dump_file << buffer_txt << " "; + // maximum buffer occupancy frequency band 1 (number of samples) + buffer_txt = std::to_string(d_max_buff_occ_freq_band_1); + d_dump_file << buffer_txt << std::endl; + } + else + { + d_dump_file << 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..035320569 --- /dev/null +++ b/src/algorithms/signal_source/libs/fpga_buffer_monitor.h @@ -0,0 +1,104 @@ +/*! + * \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-2021 (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 "concurrent_queue.h" +#include // pmt +#include // for int32_t +#include // for string, ofstream + +/** \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, + bool dump, + std::string dump_filename, + Concurrent_Queue* queue); + + /*! + * \brief Destructor + */ + ~Fpga_buffer_monitor(); + + /*! + * \brief This function checks buffer overflow and monitors the FPGA buffer status + */ + void check_buffer_overflow_and_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 d_max_buff_occ_freq_band_0; + uint32_t d_max_buff_occ_freq_band_1; + + bool d_dump; + std::string d_dump_filename; + std::ofstream d_dump_file; + + Concurrent_Queue* d_queue; +}; + + +/** \} */ +/** \} */ +#endif // GNSS_SDR_FPGA_BUFFER_MONITOR_H