mirror of
				https://github.com/gnss-sdr/gnss-sdr
				synced 2025-10-31 15:23:04 +00:00 
			
		
		
		
	Add the MAX2771_EVKIT FPGA signal source and the ENABLE_FPGA_MAX2771_EVKIT flag to enable it.
This commit is contained in:
		| @@ -41,6 +41,14 @@ if(ENABLE_AD9361) | ||||
|     list(APPEND OPT_DRIVER_HEADERS ad9361_fpga_signal_source.h) | ||||
| endif() | ||||
|  | ||||
| if(ENABLE_FPGA_MAX2771_EVKIT) | ||||
|     ############################################### | ||||
|     # MAX2771 EVKIT DIRECT TO FPGA Hardware | ||||
|     ############################################### | ||||
|     list(APPEND OPT_DRIVER_SOURCES max2771_evkit_fpga_signal_source.cc) | ||||
|     list(APPEND OPT_DRIVER_HEADERS max2771_evkit_fpga_signal_source.h) | ||||
| endif() | ||||
|  | ||||
| if(ENABLE_FPGA) | ||||
|     ############################################### | ||||
|     # FPGA DMA source | ||||
| @@ -159,7 +167,7 @@ target_include_directories(signal_source_adapters | ||||
|         ${GNSSSDR_SOURCE_DIR}/src/core/interfaces | ||||
| ) | ||||
|  | ||||
| if(ENABLE_FPGA OR ENABLE_AD9361) | ||||
| if(ENABLE_FPGA OR ENABLE_AD9361 OR ENABLE_FPGA_MAX2771_EVKIT) | ||||
|     target_link_libraries(signal_source_adapters | ||||
|         PUBLIC | ||||
|             signal_source_libs | ||||
|   | ||||
| @@ -0,0 +1,468 @@ | ||||
| /*! | ||||
|  * \file max2771_evkit_fpga_signal_source.cc | ||||
|  * \brief Signal source for the MAX2771EVKIT evaluation board connected directly | ||||
|  * to FPGA accelerators. | ||||
|  * This source implements only the MAX2771 control. It is NOT compatible with | ||||
|  * conventional SDR acquisition and tracking blocks. | ||||
|  * | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * | ||||
|  * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. | ||||
|  * This file is part of GNSS-SDR. | ||||
|  * | ||||
|  * Copyright (C) 2010-2024  (see AUTHORS file for a list of contributors) | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
|  | ||||
| #include "max2771_evkit_fpga_signal_source.h" | ||||
| #include "GPS_L1_CA.h" | ||||
| #include "GPS_L2C.h" | ||||
| #include "GPS_L5.h" | ||||
| #include "configuration_interface.h" | ||||
| #include "gnss_sdr_flags.h" | ||||
| #include "gnss_sdr_string_literals.h" | ||||
| #include <algorithm>  // for std::max | ||||
| #include <chrono>     // for std::chrono | ||||
| #include <cmath>      // for std::floor | ||||
| #include <exception>  // for std::exception | ||||
| #include <iostream>   // for std::cout | ||||
|  | ||||
| #if USE_GLOG_AND_GFLAGS | ||||
| #include <glog/logging.h> | ||||
| #else | ||||
| #include <absl/log/check.h> | ||||
| #include <absl/log/log.h> | ||||
| #endif | ||||
|  | ||||
| using namespace std::string_literals; | ||||
|  | ||||
| MAX2771EVKITFpgaSignalSource::MAX2771EVKITFpgaSignalSource(const ConfigurationInterface *configuration, | ||||
|     const std::string &role, unsigned int in_stream, unsigned int out_stream, | ||||
|     Concurrent_Queue<pmt::pmt_t> *queue __attribute__((unused))) | ||||
|     : SignalSourceBase(configuration, role, "Ad9361_Fpga_Signal_Source"s), | ||||
|       freq_(configuration->property(role + ".freq", static_cast<uint64_t>(GPS_L1_FREQ_HZ))), | ||||
|       sample_rate_(configuration->property(role + ".sampling_frequency", default_sampling_rate)), | ||||
|       in_stream_(in_stream), | ||||
|       out_stream_(out_stream), | ||||
|       bandwidth_(configuration->property(role + ".bandwidth", default_bandwidth)), | ||||
|       filter_order_(configuration->property(role + ".filter_order", default_filter_order)), | ||||
|       gain_in_(configuration->property(role + ".PGA_gain", default_PGA_gain_value)), | ||||
|       item_size_(sizeof(int8_t)), | ||||
|       chipen_(true), | ||||
|       if_filter_gain_(configuration->property(role + ".enable_IF_filter_gain", true)), | ||||
|       LNA_active_(configuration->property(role + ".LNA_active", false)), | ||||
|       enable_agc_(configuration->property(role + ".enable_AGC", true)), | ||||
|       enable_ovf_check_buffer_monitor_active_(true), | ||||
|       dump_(configuration->property(role + ".dump", false)), | ||||
| #if USE_GLOG_AND_GFLAGS | ||||
|       rf_shutdown_(configuration->property(role + ".rf_shutdown", FLAGS_rf_shutdown)) | ||||
| #else | ||||
|       rf_shutdown_(configuration->property(role + ".rf_shutdown", absl::GetFlag(FLAGS_rf_shutdown))) | ||||
| #endif | ||||
| { | ||||
|     // some basic checks | ||||
|     if (freq_ != GPS_L1_FREQ_HZ and freq_ != GPS_L2_FREQ_HZ and freq_ != GPS_L5_FREQ_HZ) | ||||
|         { | ||||
|             std::cout << "Configuration parameter freq should take values " << GPS_L1_FREQ_HZ << ", " << GPS_L2_FREQ_HZ << ", or " << GPS_L5_FREQ_HZ << "\n"; | ||||
|             std::cout << "Error: provided value freq = " << freq_ << " is not among valid values\n"; | ||||
|             std::cout << " This parameter has been set to its default value freq = " << GPS_L1_FREQ_HZ << '\n'; | ||||
|             LOG(WARNING) << "Invalid configuration value for freq parameter. Set to freq = " << GPS_L1_FREQ_HZ; | ||||
|             freq_ = GPS_L1_FREQ_HZ; | ||||
|         } | ||||
|     if (sample_rate_ != 4092000 and sample_rate_ != 8184000 and sample_rate_ != 16368000 and sample_rate_ != 32736000) | ||||
|         { | ||||
|             std::cout << "Configuration parameter sampling_frequency should take values 4092000, 8184000, 16368000, or 32736000\n"; | ||||
|             std::cout << "Error: provided value sampling_frequency = " << sample_rate_ << " is not among valid values\n"; | ||||
|             std::cout << " This parameter has been set to its default value sampling_frequency = " << default_sampling_rate << '\n'; | ||||
|             LOG(WARNING) << "Invalid configuration value for sampling_frequency parameter. Set to sampling_frequency = " << default_sampling_rate; | ||||
|             sample_rate_ = default_sampling_rate; | ||||
|         } | ||||
|     if (bandwidth_ != 2500000 and bandwidth_ != 4200000 and bandwidth_ != 8700000 and bandwidth_ != 16400000 and bandwidth_ != 23400000 and bandwidth_ != 36000000) | ||||
|         { | ||||
|             std::cout << "Configuration parameter bandwidth can only take the following values: 2500000, 4200000, 8700000, 16400000, 23400000, and 36000000 Hz\n"; | ||||
|             std::cout << "Error: provided value bandwidth = " << bandwidth_ << " is not among valid values\n"; | ||||
|             std::cout << " This parameter has been set to its default value bandwidth = " << default_bandwidth << '\n'; | ||||
|             LOG(WARNING) << "Invalid configuration value for bandwidth parameter. Set to bandwidth = " << default_bandwidth; | ||||
|             bandwidth_ = default_bandwidth; | ||||
|         } | ||||
|     if (filter_order_ != 3 and filter_order_ != 5) | ||||
|         { | ||||
|             std::cout << "Configuration parameter filter_order should take values 3 or 5\n"; | ||||
|             std::cout << "Error: provided value filter_order = " << filter_order_ << " is not among valid values\n"; | ||||
|             std::cout << " This parameter has been set to its default value filter_order = " << default_filter_order << '\n'; | ||||
|             LOG(WARNING) << "Invalid configuration value for filter_order parameter. Set to filter_order = " << default_filter_order; | ||||
|             filter_order_ = default_filter_order; | ||||
|         } | ||||
|     if (gain_in_ > max_PGA_gain_value) | ||||
|         { | ||||
|             std::cout << "Configuration parameter PGA_gain should be up to " << max_PGA_gain_value << "\n"; | ||||
|             std::cout << "Error: provided value PGA_gain = " << gain_in_ << " is not among valid values\n"; | ||||
|             std::cout << " This parameter has been set to its default value PGA_gain = " << default_PGA_gain_value << '\n'; | ||||
|             LOG(WARNING) << "Invalid configuration value for PGA_gain parameter. Set to PGA_gain = " << default_PGA_gain_value; | ||||
|             gain_in_ = default_PGA_gain_value; | ||||
|         } | ||||
|  | ||||
|     std::vector<uint32_t> register_values = setup_regs(); | ||||
|  | ||||
|     spidev_fpga = std::make_shared<Fpga_spidev>(); | ||||
|  | ||||
|     if (spidev_fpga->SPI_open()) | ||||
|         { | ||||
|             std::cerr << "Cannot open SPI device\n"; | ||||
|             // stop the receiver | ||||
|             queue->push(pmt::make_any(command_event_make(200, 0))); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|     if (configure(register_values)) | ||||
|         { | ||||
|             std::cerr << "Error configuring the MAX2771 device " << '\n'; | ||||
|         } | ||||
|  | ||||
|     if (spidev_fpga->SPI_close()) | ||||
|         { | ||||
|             std::cerr << "Error closing SPI device " << '\n'; | ||||
|         } | ||||
|  | ||||
|     std::string dump_filename = configuration->property(role + ".dump_filename", default_dump_filename); | ||||
|  | ||||
|     buffer_monitor_fpga = std::make_shared<Fpga_buffer_monitor>(NUM_FREQ_BANDS, dump_, dump_filename); | ||||
|     thread_buffer_monitor = std::thread([&] { run_buffer_monitor_process(); }); | ||||
|  | ||||
|     if (in_stream_ > 0) | ||||
|         { | ||||
|             LOG(ERROR) << "A signal source does not have an input stream"; | ||||
|         } | ||||
|     if (out_stream_ > 1) | ||||
|         { | ||||
|             LOG(ERROR) << "This implementation only supports one output stream"; | ||||
|         } | ||||
| } | ||||
|  | ||||
| std::vector<uint32_t> MAX2771EVKITFpgaSignalSource::setup_regs(void) | ||||
| { | ||||
|     std::vector<uint32_t> register_values = std::vector<uint32_t>(MAX2771_NUM_REGS); | ||||
|  | ||||
|  | ||||
|     uint32_t LNA_mode = (LNA_active_) ? 0x0 : 0x2; | ||||
|  | ||||
|     uint32_t Filter_Bandwidth; | ||||
|  | ||||
|     switch (bandwidth_) | ||||
|         { | ||||
|         case 2500000: | ||||
|             Filter_Bandwidth = 0x0; | ||||
|             break; | ||||
|         case 4200000: | ||||
|             Filter_Bandwidth = 0x2; | ||||
|             break; | ||||
|         case 8700000: | ||||
|             Filter_Bandwidth = 0x1; | ||||
|             break; | ||||
|         case 16400000: | ||||
|             Filter_Bandwidth = 0x7; | ||||
|             break; | ||||
|         case 23400000: | ||||
|             Filter_Bandwidth = 0x3; | ||||
|             break; | ||||
|         case 36000000: | ||||
|             Filter_Bandwidth = 0x4; | ||||
|             break; | ||||
|         default: | ||||
|             Filter_Bandwidth = 0x0;  // default bandwidth | ||||
|         } | ||||
|  | ||||
|     uint32_t chipen_select = (chipen_) ? 0x1 : 0x0; | ||||
|     uint32_t Filter_order_sel = (filter_order_ == 5) ? 0x0 : 0x1; | ||||
|     uint32_t IF_filter_gain_sel = (if_filter_gain_) ? 0x1 : 0x0; | ||||
|  | ||||
|     register_values[0] =  // configuration 1 register | ||||
|         (chipen_select << 31) + | ||||
|         (IDLE << 30) + | ||||
|         (0x8 << 26) +  // reserved | ||||
|         (0x8 << 22) +  // reserved | ||||
|         (0x2 << 20) +  // reserved | ||||
|         (0x1 << 18) +  // reserved | ||||
|         (MIXPOLE << 17) + | ||||
|         (LNA_mode << 15) + | ||||
|         (MIXERMODE << 13) + | ||||
|         (FCEN << 6) + | ||||
|         (Filter_Bandwidth << 3) + | ||||
|         (Filter_order_sel << 2) + | ||||
|         (FCENX << 1) + | ||||
|         IF_filter_gain_sel; | ||||
|  | ||||
|     uint32_t AGC_mode = (enable_agc_) ? 0x0 : 0x2; | ||||
|  | ||||
|     register_values[1] =  // configuration 2 register | ||||
|         (0x0 << 31) +     // reserved | ||||
|         (0x1 << 29) +     // reserved | ||||
|         (ANAIMON << 28) + | ||||
|         (IQEN << 27) + | ||||
|         (GAINREF << 15) + | ||||
|         (SPI_SDIO_CONFIG << 13) + | ||||
|         (AGC_mode << 11) + | ||||
|         (FORMAT << 9) + | ||||
|         (BITS << 6) + | ||||
|         (DRVCFG << 4) + | ||||
|         (0x1 << 3) +  // reserved | ||||
|         (0x0 << 2) +  // reserved | ||||
|         DIEID; | ||||
|  | ||||
|     register_values[2] =  // configuration 3 register | ||||
|         (0x0 << 28) +     //reserved | ||||
|         (gain_in_ << 22) + | ||||
|         (0x1 << 21) +  // reserved | ||||
|         (HILOADEN << 20) + | ||||
|         (0x1 << 19) +  // reserved | ||||
|         (0x1 << 18) +  // reserved | ||||
|         (0x1 << 17) +  // reserved | ||||
|         (0x1 << 16) +  // reserved | ||||
|         (FHIPEN << 15) + | ||||
|         (0x0 << 14) +  // reserved | ||||
|         (PGAIEN << 13) + | ||||
|         (PGAQEN << 12) + | ||||
|         (STRMEN << 11) + | ||||
|         (STRMSTART << 10) + | ||||
|         (STRMSTOP << 9) + | ||||
|         (0x7 << 6) +  // reserved | ||||
|         (STRMBITS << 4) + | ||||
|         (STAMPEN << 3) + | ||||
|         (TIMESYNCEN << 2) + | ||||
|         (DATASYNCEN << 1) + | ||||
|         STRMRST; | ||||
|  | ||||
|     uint32_t clock_out_div_ratio; | ||||
|  | ||||
|     switch (sample_rate_) | ||||
|         { | ||||
|         case 4092000: | ||||
|             clock_out_div_ratio = 0x1;  // XTAL frequency /4 | ||||
|             break; | ||||
|         case 8184000: | ||||
|             clock_out_div_ratio = 0x2;  // XTAL frequency /2 | ||||
|             break; | ||||
|         case 16368000: | ||||
|             clock_out_div_ratio = 0x3;  // XTAL frequency | ||||
|             break; | ||||
|         case 32736000: | ||||
|             clock_out_div_ratio = 0x0;  // XTAL Frequency x2 | ||||
|             break; | ||||
|         default: | ||||
|             clock_out_div_ratio = 0x1;  // default XTAL frequency | ||||
|         } | ||||
|  | ||||
|     register_values[3] =  // PLL configuration register | ||||
|         (clock_out_div_ratio << 29) + | ||||
|         (LOBAND << 28) + | ||||
|         (0x1 << 27) +  // reserved | ||||
|         (0x0 << 26) +  // reserved | ||||
|         (0x0 << 25) +  // reserved | ||||
|         (REFOUTEN << 24) + | ||||
|         (0x1 << 23) +  // reserved | ||||
|         (0x0 << 21) +  // reserved | ||||
|         (IXTAL << 19) + | ||||
|         (0x10 << 14) +  // reserved | ||||
|         (0x0 << 13) +   // reserved | ||||
|         (0x0 << 10) +   //reserved | ||||
|         (ICP << 9) + | ||||
|         (0x0 << 8) +  // reserved | ||||
|         (0x0 << 7) +  // reserved | ||||
|         (0x0 << 4) +  // reserved | ||||
|         (INT_PLL << 3) + | ||||
|         (PWRSAV << 2) + | ||||
|         (0x0 << 1) +  // reserved | ||||
|         0x0;          // reserved | ||||
|  | ||||
|     uint32_t freq_sel; | ||||
|     switch (freq_) | ||||
|         { | ||||
|         case static_cast<uint64_t>(GPS_L1_FREQ_HZ): | ||||
|             freq_sel = 0x604; | ||||
|             break; | ||||
|         case static_cast<uint64_t>(GPS_L2_FREQ_HZ): | ||||
|             freq_sel = 0x4B0; | ||||
|             break; | ||||
|         case static_cast<uint64_t>(GPS_L5_FREQ_HZ): | ||||
|             freq_sel = 0x47E; | ||||
|             break; | ||||
|         default: | ||||
|             freq_sel = 0x604; | ||||
|         } | ||||
|     //uint32_t freq_sel = (freq_ == GPS_L1_FREQ_HZ) ? 0x604 : | ||||
|     register_values[4] =  // PLL integer division register | ||||
|         (0x0 << 28) +     //reserved | ||||
|         (freq_sel << 13) + | ||||
|         (RDIV << 3) + | ||||
|         0x0;  // reserved | ||||
|  | ||||
|     register_values[5] =  // PLL fractional division register | ||||
|         (0x0 << 28) +     // reserved | ||||
|         (FDIV << 8) + | ||||
|         (0x7 << 4) +  // reserved | ||||
|         (0x0 << 3) +  // reserved | ||||
|         (0x0 << 2) +  // reserved | ||||
|         (0x0 << 1) +  // reserved | ||||
|         0x0;          // reserved | ||||
|  | ||||
|     register_values[6] =  // DSP interface register | ||||
|         (0x0 << 28) +     // reserved | ||||
|         0x8000000;        // reserved | ||||
|  | ||||
|     register_values[7] =  // clock configuration 1 register | ||||
|         (0x0 << 29) +     // reserved | ||||
|         (EXTADCCLK << 28) + | ||||
|         (REFCLK_L_CNT << 16) + | ||||
|         (REFCLK_M_CNT << 4) + | ||||
|         (FCLKIN << 3) + | ||||
|         (ADCCLK << 2) + | ||||
|         (0x1 << 1) +  //reserved | ||||
|         MODE; | ||||
|  | ||||
|     register_values[8] = TEST_MODE_1_REG_VAL;  // test mode 1 register | ||||
|  | ||||
|     register_values[9] = TEST_MODE_2_REG_VAL;  // test mode 2 register | ||||
|  | ||||
|     register_values[10] =  // clock configuration 2 register | ||||
|         (0x0 << 29) +      // reserved | ||||
|         (0x0 << 28) +      // reserved | ||||
|         (ADCCLK_L_CNT << 16) + | ||||
|         (ADCCLK_M_CNT << 4) + | ||||
|         (PRE_FRACDIV_SEL << 3) + | ||||
|         (CLKOUT_SEL << 2) + | ||||
|         0x0;  // reserved | ||||
|  | ||||
|     return register_values; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool MAX2771EVKITFpgaSignalSource::configure(std::vector<uint32_t> register_values) | ||||
| { | ||||
|     // write the registers | ||||
|     std::cerr << "Configuring MAX2771 registers" << std::endl; | ||||
|     uint32_t status = 0; | ||||
|     for (uint32_t k = 0; k < register_values.size(); k++) | ||||
|         { | ||||
|             status = spidev_fpga->write_reg32(k, register_values[k]); | ||||
|             if (status) | ||||
|                 { | ||||
|                     std::cerr << "Error writing the MAX2771 registers" << std::endl; | ||||
|                     break; | ||||
|                 } | ||||
|         } | ||||
|  | ||||
|     // Read the registers and verify that the values are correctly written | ||||
|     std::vector<uint32_t> reg_read = std::vector<uint32_t>(register_values.size()); | ||||
|  | ||||
|     for (uint8_t n = 0; n < register_values.size(); ++n) | ||||
|         { | ||||
|             status = spidev_fpga->read_reg32(n, ®_read[n]); | ||||
|             if (status) | ||||
|                 { | ||||
|                     std::cerr << "Error reading the MAX2771 registers" << std::endl; | ||||
|                     return status; | ||||
|                 } | ||||
|             else | ||||
|                 { | ||||
|                     if (reg_read[n] != register_values[n]) | ||||
|                         { | ||||
|                             std::cerr << "Error: Failed to verify the MAX2771 registers " << std::endl; | ||||
|                             return -1; | ||||
|                         } | ||||
|                 } | ||||
|         } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| MAX2771EVKITFpgaSignalSource::~MAX2771EVKITFpgaSignalSource() | ||||
| { | ||||
|     /* cleanup and exit */ | ||||
|  | ||||
|     if (rf_shutdown_) | ||||
|         { | ||||
|             chipen_ = false; | ||||
|             std::cout << "* MAX2771 Disabling RX streaming channels\n"; | ||||
|             std::vector<uint32_t> register_values = setup_regs(); | ||||
|  | ||||
|             if (spidev_fpga->SPI_open()) | ||||
|                 { | ||||
|                     std::cerr << "Cannot open SPI device\n"; | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             if (configure(register_values)) | ||||
|                 { | ||||
|                     std::cerr << "Error disabling the MAX2771 device " << '\n'; | ||||
|                 } | ||||
|  | ||||
|             if (spidev_fpga->SPI_close()) | ||||
|                 { | ||||
|                     std::cerr << "Error closing SPI device " << '\n'; | ||||
|                 } | ||||
|         } | ||||
|  | ||||
|     // disable buffer overflow checking and buffer monitoring | ||||
|     std::unique_lock<std::mutex> lock_buffer_monitor(buffer_monitor_mutex); | ||||
|     enable_ovf_check_buffer_monitor_active_ = false; | ||||
|     lock_buffer_monitor.unlock(); | ||||
|  | ||||
|     if (thread_buffer_monitor.joinable()) | ||||
|         { | ||||
|             thread_buffer_monitor.join(); | ||||
|         } | ||||
| } | ||||
|  | ||||
|  | ||||
| void MAX2771EVKITFpgaSignalSource::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<std::mutex> lock(buffer_monitor_mutex); | ||||
|             if (enable_ovf_check_buffer_monitor_active_ == false) | ||||
|                 { | ||||
|                     enable_ovf_check_buffer_monitor_active = false; | ||||
|                 } | ||||
|             lock.unlock(); | ||||
|         } | ||||
| } | ||||
|  | ||||
|  | ||||
| void MAX2771EVKITFpgaSignalSource::connect(gr::top_block_sptr top_block) | ||||
| { | ||||
|     if (top_block) | ||||
|         { /* top_block is not null */ | ||||
|         }; | ||||
|     DLOG(INFO) << "AD9361 FPGA source nothing to connect"; | ||||
| } | ||||
|  | ||||
|  | ||||
| void MAX2771EVKITFpgaSignalSource::disconnect(gr::top_block_sptr top_block) | ||||
| { | ||||
|     if (top_block) | ||||
|         { /* top_block is not null */ | ||||
|         }; | ||||
|     DLOG(INFO) << "AD9361 FPGA source nothing to disconnect"; | ||||
| } | ||||
|  | ||||
|  | ||||
| gr::basic_block_sptr MAX2771EVKITFpgaSignalSource::get_left_block() | ||||
| { | ||||
|     LOG(WARNING) << "Trying to get signal source left block."; | ||||
|     return {}; | ||||
| } | ||||
|  | ||||
|  | ||||
| gr::basic_block_sptr MAX2771EVKITFpgaSignalSource::get_right_block() | ||||
| { | ||||
|     return {}; | ||||
| } | ||||
| @@ -0,0 +1,163 @@ | ||||
| /*! | ||||
|  * \file max2771_evkit_fpga_signal_source.h | ||||
|  * \brief Signal source for the MAX2771EVKIT evaluation board connected directly | ||||
|  * to FPGA accelerators. | ||||
|  * This source implements only the MAX2771 control. It is NOT compatible with | ||||
|  * conventional SDR acquisition and tracking blocks. | ||||
|  * | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * | ||||
|  * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. | ||||
|  * This file is part of GNSS-SDR. | ||||
|  * | ||||
|  * Copyright (C) 2010-2024  (see AUTHORS file for a list of contributors) | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
|  | ||||
| #ifndef GNSS_SDR_MAX2771_EVKIT_FPGA_SIGNAL_SOURCE_H | ||||
| #define GNSS_SDR_MAX2771_EVKIT_FPGA_SIGNAL_SOURCE_H | ||||
|  | ||||
| #include "command_event.h" | ||||
| #include "concurrent_queue.h" | ||||
| #include "fpga_buffer_monitor.h" | ||||
| #include "fpga_spidev.h" | ||||
| #include "gnss_block_interface.h" | ||||
| #include "signal_source_base.h" | ||||
| #include <pmt/pmt.h>  // for pmt::pmt_t | ||||
| #include <cstdint>    // for fixed-width integer types | ||||
| #include <memory>     // for smart pointers | ||||
| #include <mutex>      // for mutex | ||||
| #include <string>     // for strings | ||||
| #include <thread>     // for threads | ||||
|  | ||||
|  | ||||
| /** \addtogroup Signal_Source | ||||
|  * \{ */ | ||||
| /** \addtogroup Signal_Source_adapters | ||||
|  * \{ */ | ||||
|  | ||||
|  | ||||
| class ConfigurationInterface; | ||||
|  | ||||
| class MAX2771EVKITFpgaSignalSource : public SignalSourceBase | ||||
| { | ||||
| public: | ||||
|     MAX2771EVKITFpgaSignalSource(const ConfigurationInterface *configuration, | ||||
|         const std::string &role, unsigned int in_stream, | ||||
|         unsigned int out_stream, Concurrent_Queue<pmt::pmt_t> *queue); | ||||
|  | ||||
|     ~MAX2771EVKITFpgaSignalSource(); | ||||
|  | ||||
|     std::vector<uint32_t> setup_regs(void); | ||||
|  | ||||
|  | ||||
|     inline size_t item_size() override | ||||
|     { | ||||
|         return item_size_; | ||||
|     } | ||||
|  | ||||
|     void connect(gr::top_block_sptr top_block) override; | ||||
|     void disconnect(gr::top_block_sptr top_block) override; | ||||
|     gr::basic_block_sptr get_left_block() override; | ||||
|     gr::basic_block_sptr get_right_block() override; | ||||
|  | ||||
| private: | ||||
|     const std::string default_dump_filename = std::string("FPGA_buffer_monitor_dump.dat"); | ||||
|     const uint64_t default_bandwidth = 2500000; | ||||
|     const uint32_t default_filter_order = 5; | ||||
|     const uint64_t default_sampling_rate = 4092000; | ||||
|     const uint32_t default_PGA_gain_value = 0x3A;  // default PGA gain when AGC is off | ||||
|     // max PGA gain value | ||||
|     const uint32_t max_PGA_gain_value = 0x3F; | ||||
|     // check buffer overflow and perform buffer monitoring every 1s by default | ||||
|     const uint32_t buffer_monitor_period_ms = 1000; | ||||
|     // buffer overflow and buffer monitoring initial delay | ||||
|     const uint32_t buffer_monitoring_initial_delay_ms = 2000; | ||||
|     // MAX2771 number of configuration registers | ||||
|     const uint32_t MAX2771_NUM_REGS = 11; | ||||
|     // MAX2771 configuration register fields | ||||
|     const uint32_t NUM_FREQ_BANDS = 1; | ||||
|     const uint32_t IDLE = 0x0;             // Idle mode disabled | ||||
|     const uint32_t MIXPOLE = 0x0;          // set the passive filter pole at mixer output at 13 MHz. | ||||
|     const uint32_t MIXERMODE = 0x0;        // L1 band enabled | ||||
|     const uint32_t FCEN = 0x58;            // Center frequency not used when in low-pass filter mode. Set to default value. | ||||
|     const uint32_t FCENX = 0x0;            // POlyphase filter selection set to Lowpass filter | ||||
|     const uint32_t ANAIMON = 0x0;          // analog monitor disabled | ||||
|     const uint32_t IQEN = 0x1;             // I and Q channels enable | ||||
|     const uint32_t GAINREF = 0xAA;         // AGC Gain ref | ||||
|     const uint32_t SPI_SDIO_CONFIG = 0x0;  // SPI SDIO config when tri-stated: nothing applied | ||||
|     const uint32_t FORMAT = 0x1;           // sign and magnitude | ||||
|     const uint32_t BITS = 0x2;             // number of bits in the ADC = 2 | ||||
|     const uint32_t DRVCFG = 0x0;           // output driver configuration = CMOS Logic | ||||
|     const uint32_t DIEID = 0x0;            // identifies version of IC | ||||
|     const uint32_t HILOADEN = 0x0;         // disable output driver for high loads | ||||
|     const uint32_t FHIPEN = 0x1;           // enable highpass coupling between filter and PGA. | ||||
|     const uint32_t PGAIEN = 0x1;           // I-Channel PGA Enable | ||||
|     const uint32_t PGAQEN = 0x1;           // Q-Channel PGA Enable | ||||
|     const uint32_t STRMEN = 0x0;           // disable DSP interface for serial streaming of data | ||||
|     const uint32_t STRMSTART = 0x0;        // the rising edge of this bit enables data streaming to the output, clock, data, sync and frame sync outputs. | ||||
|     const uint32_t STRMSTOP = 0x0;         // the rising edge of this bit disables data streaming to the output, clock,  data sync and frame sync outputs. | ||||
|     const uint32_t STRMBITS = 0x1;         // number of bits to be streamed: I MSB, I LSB | ||||
|     const uint32_t STAMPEN = 0x1;          // enable frame number insertion | ||||
|     const uint32_t TIMESYNCEN = 0x1;       // enable the output of the time sync pulses at all times when streaming is enabled. | ||||
|     const uint32_t DATASYNCEN = 0x0;       // disable the sync pulses at the DATASYNC output | ||||
|     const uint32_t STRMRST = 0x0;          // counter reset not active | ||||
|     const uint32_t LOBAND = 0x0;           // L1 band | ||||
|     const uint32_t REFOUTEN = 0x1;         // Output clock buffer enable | ||||
|     const uint32_t IXTAL = 0x1;            // XTAL osscillator/buffer set to normal current | ||||
|     const uint32_t ICP = 0x0;              // charge pump current selection set to 0.5 mA | ||||
|     const uint32_t INT_PLL = 0x1;          // PLL mode set to integer-N PLL | ||||
|     const uint32_t PWRSAV = 0x0;           // PLL power save mode disabled | ||||
|     const uint32_t RDIV = 0x10;            // Set the PLL reference division ratio such that the L1 band is tuned to 1575.42 Mhz | ||||
|     const uint32_t FDIV = 0x80000;         // PLL fractional division ratio not used. Set to default value | ||||
|     const uint32_t EXTADCCLK = 0x0;        // use internally generated clock | ||||
|     const uint32_t REFCLK_L_CNT = 0x100;   // set the L counter of the reference clock configuration to its default value | ||||
|     const uint32_t REFCLK_M_CNT = 0x61B;   // set the M counter of the reference clock configuration to its default value | ||||
|     const uint32_t FCLKIN = 0x0;           // fractional clock divider set to default value | ||||
|     const uint32_t ADCCLK = 0x0;           // ADC clock selection set to reference clock divider/multiplier | ||||
|     const uint32_t MODE = 0x0;             // DSP interface mode selection | ||||
|     const uint32_t ADCCLK_L_CNT = 0x100;   // set the L counter of the ADC clock configuration to its default value | ||||
|     const uint32_t ADCCLK_M_CNT = 0x61B;   // set the M counter of the ADC clock configuration to its default value | ||||
|     const uint32_t PRE_FRACDIV_SEL = 0x0;  // bypass fractional clock divider | ||||
|     const uint32_t CLKOUT_SEL = 0x1;       // CLKOUT selection set to ADC clock | ||||
|     // MAX2771 configuration register registers | ||||
|     const uint32_t TEST_MODE_1_REG_VAL = 0x01E0F401;  // reserved | ||||
|     const uint32_t TEST_MODE_2_REG_VAL = 0x00000002; | ||||
|  | ||||
|     bool configure(std::vector<uint32_t> register_values); | ||||
|     void run_buffer_monitor_process(); | ||||
|  | ||||
|  | ||||
|     std::thread thread_buffer_monitor; | ||||
|  | ||||
|     std::shared_ptr<Fpga_buffer_monitor> buffer_monitor_fpga; | ||||
|     std::shared_ptr<Fpga_spidev> spidev_fpga; | ||||
|  | ||||
|     std::mutex buffer_monitor_mutex; | ||||
|  | ||||
|     uint64_t freq_;  // frequency of local oscillator | ||||
|     uint64_t sample_rate_; | ||||
|  | ||||
|     uint32_t in_stream_; | ||||
|     uint32_t out_stream_; | ||||
|     uint32_t bandwidth_;     // 2500000, 4200000, 8700000, 16400000, 23400000, 36000000 | ||||
|     uint32_t filter_order_;  //3, 5 | ||||
|     uint32_t gain_in_;       // 0 to 0x3F | ||||
|  | ||||
|     size_t item_size_;  // 1 | ||||
|  | ||||
|     bool chipen_;          // chip enable | ||||
|     bool if_filter_gain_;  // true, false | ||||
|     bool LNA_active_;      // true, false | ||||
|     bool enable_agc_;      // true, false | ||||
|     bool enable_ovf_check_buffer_monitor_active_; | ||||
|     bool dump_; | ||||
|     bool rf_shutdown_; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** \} */ | ||||
| /** \} */ | ||||
| #endif  // GNSS_SDR_MAX2771_EVKIT_FPGA_SIGNAL_SOURCE_H | ||||
| @@ -12,17 +12,24 @@ if(ENABLE_FMCOMMS2 OR ENABLE_AD9361) | ||||
|     set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad9361_manager.h) | ||||
| endif() | ||||
|  | ||||
| if(ENABLE_FPGA OR ENABLE_AD9361) | ||||
| if(ENABLE_FPGA_MAX2771_EVKIT) | ||||
|     set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} fpga_spidev.cc) | ||||
|     set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} fpga_spidev.h) | ||||
| endif() | ||||
|  | ||||
| if(ENABLE_FPGA) | ||||
|     set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} fpga_switch.cc) | ||||
|     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) | ||||
|     set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} fpga_dma-proxy.cc) | ||||
|     set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_HEADERS} fpga_dma-proxy.h) | ||||
| endif() | ||||
|  | ||||
| if(ENABLE_AD9361 OR ENABLE_FPGA_MAX2771_EVKIT) | ||||
|     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() | ||||
|  | ||||
| if(ENABLE_PLUTOSDR) | ||||
|     set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad936x_iio_samples.cc) | ||||
|   | ||||
							
								
								
									
										131
									
								
								src/algorithms/signal_source/libs/fpga_spidev.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/algorithms/signal_source/libs/fpga_spidev.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | ||||
| /*! | ||||
|  * \file fpga_spidev.cc | ||||
|  * \brief FPGA SPI control. | ||||
|  * | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * | ||||
|  * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. | ||||
|  * This file is part of GNSS-SDR. | ||||
|  * | ||||
|  * Copyright (C) 2010-2024  (see AUTHORS file for a list of contributors) | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
|  | ||||
| #include "fpga_spidev.h" | ||||
| #include <cstring>             // for memset() | ||||
| #include <fcntl.h>             // for open(), O_RDWR | ||||
| #include <iostream>            // for std::cerr | ||||
| #include <linux/spi/spidev.h>  // spidev driver | ||||
| #include <sys/ioctl.h>         // for ioctl() | ||||
| #include <unistd.h>            // for close() | ||||
|  | ||||
|  | ||||
| int Fpga_spidev::SPI_open() | ||||
| { | ||||
|     if ((d_fd = open(SPI_DEVICE_NAME.c_str(), O_RDWR)) < 0) | ||||
|         { | ||||
|             std::cerr << "Failed to open the " << SPI_DEVICE_NAME << " device file \n"; | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|     int ret; | ||||
|     int32_t mode = 0; | ||||
|  | ||||
|     ret = ioctl(d_fd, SPI_IOC_WR_MODE32, &mode); | ||||
|     if (ret == -1) | ||||
|         { | ||||
|             std::cerr << "can't set spi mode\n"; | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|     ret = ioctl(d_fd, SPI_IOC_RD_MODE32, &mode);  // le digo al spi "algo" | ||||
|     if (ret == -1) | ||||
|         { | ||||
|             std::cerr << "can't set spi mode\n"; | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int Fpga_spidev::write_reg32(char addr, uint32_t data) | ||||
| { | ||||
|     uint8_t data_buffer[2]; | ||||
|     uint8_t recv_buffer[4]; | ||||
|     int res = 0; | ||||
|     struct spi_ioc_transfer xfer[2]; | ||||
|     memset(xfer, 0, sizeof(xfer)); | ||||
|     xfer[0].bits_per_word = 8; | ||||
|     xfer[0].speed_hz = SPI_SPEED; | ||||
|     xfer[1].bits_per_word = 8; | ||||
|     xfer[1].speed_hz = SPI_SPEED; | ||||
|  | ||||
|     memset(&recv_buffer, 0, sizeof(recv_buffer)); | ||||
|     memset(&data_buffer, 0, sizeof(data_buffer)); | ||||
|  | ||||
|     data_buffer[1] = addr << 4 | 0 << 3; | ||||
|     xfer[0].tx_buf = (unsigned long)data_buffer; | ||||
|     xfer[0].len = 2; | ||||
|  | ||||
|     // Would use memcpy but 'data' is in little endian | ||||
|     ((char*)recv_buffer)[0] = ((char*)&data)[3]; | ||||
|     ((char*)recv_buffer)[1] = ((char*)&data)[2]; | ||||
|     ((char*)recv_buffer)[2] = ((char*)&data)[1]; | ||||
|     ((char*)recv_buffer)[3] = ((char*)&data)[0]; | ||||
|  | ||||
|     xfer[1].tx_buf = (unsigned long)recv_buffer; | ||||
|     xfer[1].len = 4; | ||||
|     res = ioctl(d_fd, SPI_IOC_MESSAGE(2), xfer); | ||||
|     if (res < 0) | ||||
|         { | ||||
|             std::cout << "Error sending SPI message\n"; | ||||
|             return res; | ||||
|         } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int Fpga_spidev::read_reg32(uint8_t addr, uint32_t* copy_to) | ||||
| { | ||||
|     uint8_t data_buffer[2]; | ||||
|     uint8_t recv_buffer[4]; | ||||
|     int res; | ||||
|     struct spi_ioc_transfer xfer[2]; | ||||
|     memset(xfer, 0, sizeof(xfer)); | ||||
|     xfer[0].bits_per_word = 8; | ||||
|     xfer[0].speed_hz = SPI_SPEED; | ||||
|     xfer[1].bits_per_word = 8; | ||||
|     xfer[1].speed_hz = SPI_SPEED; | ||||
|  | ||||
|     memset(&recv_buffer, 0, sizeof(recv_buffer)); | ||||
|     memset(&data_buffer, 0, sizeof(data_buffer)); | ||||
|  | ||||
|     data_buffer[1] = addr << 4 | 1 << 3; | ||||
|     xfer[0].tx_buf = (unsigned long)data_buffer; | ||||
|     xfer[0].len = 2; | ||||
|  | ||||
|     xfer[1].rx_buf = (unsigned long)recv_buffer; | ||||
|     xfer[1].len = 4; | ||||
|     res = ioctl(d_fd, SPI_IOC_MESSAGE(2), xfer); | ||||
|     if (res < 0) | ||||
|         { | ||||
|             std::cout << "Error sending SPI message\n"; | ||||
|             return res; | ||||
|         } | ||||
|  | ||||
|     // the register data is received in the reverse order | ||||
|     uint32_t tmp_result = 0; | ||||
|     for (uint32_t k = 0; k < 4; ++k) | ||||
|         { | ||||
|             tmp_result = tmp_result + ((recv_buffer[3 - k]) << 8 * k); | ||||
|         } | ||||
|     *copy_to = tmp_result; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int Fpga_spidev::SPI_close() const | ||||
| { | ||||
|     return close(d_fd); | ||||
| } | ||||
							
								
								
									
										61
									
								
								src/algorithms/signal_source/libs/fpga_spidev.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/algorithms/signal_source/libs/fpga_spidev.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| /*! | ||||
|  * \file fpga_spidev.h | ||||
|  * \brief FPGA SPI control. | ||||
|  * | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  * | ||||
|  * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. | ||||
|  * This file is part of GNSS-SDR. | ||||
|  * | ||||
|  * Copyright (C) 2010-2024  (see AUTHORS file for a list of contributors) | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  * ----------------------------------------------------------------------------- | ||||
|  */ | ||||
|  | ||||
| #ifndef GNSS_SDR_FPGA_SPIDEV_H | ||||
| #define GNSS_SDR_FPGA_SPIDEV_H | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| class Fpga_spidev | ||||
| { | ||||
| public: | ||||
|     /*! | ||||
|      * \brief Default constructor. | ||||
|      */ | ||||
|     Fpga_spidev() = default; | ||||
|  | ||||
|     /*! | ||||
|      * \brief Default destructor. | ||||
|      */ | ||||
|     ~Fpga_spidev() = default; | ||||
|  | ||||
|     /*! | ||||
|      * \brief write a register through the SPI. | ||||
|      */ | ||||
|     int write_reg32(char addr, uint32_t data); | ||||
|  | ||||
|     /*! | ||||
|      * \brief read a register through the SPI. | ||||
|      */ | ||||
|     int read_reg32(uint8_t addr, uint32_t* copy_to); | ||||
|     /*! | ||||
|      * \brief Open the SPI device driver. | ||||
|      */ | ||||
|     int SPI_open(void); | ||||
|  | ||||
|     /*! | ||||
|      * \brief Close the SPI device driver | ||||
|      */ | ||||
|     int SPI_close(void) const; | ||||
|  | ||||
| private: | ||||
|     static const uint32_t SPI_SPEED = 250000; | ||||
|     const std::string SPI_DEVICE_NAME = std::string("/dev/spidev2.0");  // Switch UIO device name | ||||
|  | ||||
|     int d_fd; | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif  // GNSS_SDR_FPGA_SPIDEV_H | ||||
		Reference in New Issue
	
	Block a user
	 Marc Majoral
					Marc Majoral