diff options
Diffstat (limited to 'Radio/HW/BladeRF/src/board/bladerf2/common.c')
-rw-r--r-- | Radio/HW/BladeRF/src/board/bladerf2/common.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/board/bladerf2/common.c b/Radio/HW/BladeRF/src/board/bladerf2/common.c new file mode 100644 index 0000000..15202f8 --- /dev/null +++ b/Radio/HW/BladeRF/src/board/bladerf2/common.c @@ -0,0 +1,415 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2018 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> + +#include "board/board.h" +#include "capabilities.h" +#include "common.h" + + +/******************************************************************************/ +/* Constants */ +/******************************************************************************/ + +/* Board state to string map */ +char const *bladerf2_state_to_string[] = { + [STATE_UNINITIALIZED] = "Uninitialized", + [STATE_FIRMWARE_LOADED] = "Firmware Loaded", + [STATE_FPGA_LOADED] = "FPGA Loaded", + [STATE_INITIALIZED] = "Initialized", +}; + + +/******************************************************************************/ +/* Sample format control */ +/******************************************************************************/ + +static inline int requires_timestamps(bladerf_format format, bool *required) +{ + switch (format) { + case BLADERF_FORMAT_SC8_Q7_META: + case BLADERF_FORMAT_SC16_Q11_META: + case BLADERF_FORMAT_PACKET_META: + *required = true; + break; + + case BLADERF_FORMAT_SC8_Q7: + case BLADERF_FORMAT_SC16_Q11: + *required = false; + break; + + default: + return BLADERF_ERR_INVAL; + } + + return 0; +} + +int perform_format_config(struct bladerf *dev, + bladerf_direction dir, + bladerf_format format) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + + struct bladerf2_board_data *board_data = dev->board_data; + bool use_timestamps, other_using_timestamps; + bladerf_channel other; + uint32_t gpio_val; + int status; + + status = requires_timestamps(format, &use_timestamps); + if (status != 0) { + log_debug("%s: Invalid format: %d\n", __FUNCTION__, format); + return status; + } + + switch (dir) { + case BLADERF_RX: + other = BLADERF_TX; + break; + + case BLADERF_TX: + other = BLADERF_RX; + break; + + default: + log_debug("Invalid direction: %d\n", dir); + return BLADERF_ERR_INVAL; + } + + status = requires_timestamps(board_data->module_format[other], + &other_using_timestamps); + if ((status == 0) && (other_using_timestamps != use_timestamps)) { + log_debug("Format conflict detected: RX=%d, TX=%d\n"); + return BLADERF_ERR_INVAL; + } + + CHECK_STATUS(dev->backend->config_gpio_read(dev, &gpio_val)); + + if (use_timestamps) { + gpio_val |= BLADERF_GPIO_TIMESTAMP; + } else { + gpio_val &= ~BLADERF_GPIO_TIMESTAMP; + } + + if (format == BLADERF_FORMAT_PACKET_META) { + gpio_val |= BLADERF_GPIO_PACKET | BLADERF_GPIO_TIMESTAMP; + } else { + gpio_val &= ~BLADERF_GPIO_PACKET; + } + + if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) { + gpio_val |= BLADERF_GPIO_8BIT_MODE; + } else { + gpio_val &= ~BLADERF_GPIO_8BIT_MODE; + } + + CHECK_STATUS(dev->backend->config_gpio_write(dev, gpio_val)); + + board_data->module_format[dir] = format; + + return 0; +} + +int perform_format_deconfig(struct bladerf *dev, bladerf_direction dir) +{ + struct bladerf2_board_data *board_data = dev->board_data; + + switch (dir) { + case BLADERF_RX: + case BLADERF_TX: + /* We'll reconfigure the HW when we call perform_format_config, + * so we just need to update our stored information */ + board_data->module_format[dir] = -1; + break; + + default: + log_debug("%s: Invalid direction: %d\n", __FUNCTION__, dir); + return BLADERF_ERR_INVAL; + } + + return 0; +} + + +/******************************************************************************/ +/* Size checks */ +/******************************************************************************/ + +bool is_valid_fpga_size(struct bladerf *dev, bladerf_fpga_size fpga, size_t len) +{ + /* We do not build FPGAs with compression enabled. Therfore, they + * will always have a fixed file size. + */ + char const env_override[] = "BLADERF_SKIP_FPGA_SIZE_CHECK"; + + bool valid; + size_t expected; + int status; + + status = dev->board->get_fpga_bytes(dev, &expected); + if (status < 0) { + log_error( + "Error %d querying FPGA size.\n", + status); + return false; + } + + /* Provide a means to override this check. This is intended to allow + * folks who know what they're doing to work around this quickly without + * needing to make a code change. (e.g., someone building a custom FPGA + * image that enables compressoin) */ + if (getenv(env_override)) { + log_info("Overriding FPGA size check per %s\n", env_override); + valid = true; + } else if (expected > 0) { + valid = (len == expected); + } else { + log_debug("Unknown FPGA type (%d). Using relaxed size criteria.\n", + fpga); + + if (len < (1 * 1024 * 1024)) { + valid = false; + } else if (len > + (dev->flash_arch->tsize_bytes - BLADERF_FLASH_ADDR_FPGA)) { + valid = false; + } else { + valid = true; + } + } + + if (!valid) { + log_warning("Detected potentially incorrect FPGA file (length was %d, " + "expected %d).\n", + len, expected); + + log_debug("If you are certain this file is valid, you may define\n" + "BLADERF_SKIP_FPGA_SIZE_CHECK in your environment to skip " + "this check.\n\n"); + } + + return valid; +} + +bool is_valid_fw_size(size_t len) +{ + /* Simple FW applications generally are significantly larger than this + */ + if (len < (50 * 1024)) { + return false; + } else if (len > BLADERF_FLASH_BYTE_LEN_FIRMWARE) { + return false; + } else { + return true; + } +} + +bladerf_tuning_mode default_tuning_mode(struct bladerf *dev) +{ + struct bladerf2_board_data *board_data = dev->board_data; + bladerf_tuning_mode mode; + char const *env_var; + extern struct controller_fns const rfic_fpga_control; + + mode = BLADERF_TUNING_MODE_HOST; + + /* Detect TX FPGA bug and report warning */ + if (BLADERF_TUNING_MODE_FPGA == mode && rfic_fpga_control.is_present(dev) && + version_fields_less_than(&board_data->fpga_version, 0, 10, 2)) { + log_warning("FPGA v%u.%u.%u has errata related to FPGA-based tuning; " + "defaulting to host-based tuning. To use FPGA-based " + "tuning, update to FPGA v%u.%u.%u, or set the " + "BLADERF_DEFAULT_TUNING_MODE enviroment variable to " + "'fpga'.\n", + board_data->fpga_version.major, + board_data->fpga_version.minor, + board_data->fpga_version.patch, 0, 10, 2); + mode = BLADERF_TUNING_MODE_HOST; + } + + env_var = getenv("BLADERF_DEFAULT_TUNING_MODE"); + + if (env_var != NULL) { + if (!strcasecmp("host", env_var)) { + mode = BLADERF_TUNING_MODE_HOST; + } else if (!strcasecmp("fpga", env_var)) { + /* Capability check */ + if (!have_cap(board_data->capabilities, BLADERF_CAP_FPGA_TUNING)) { + log_error("The loaded FPGA version (%u.%u.%u) does not support " + "the tuning mode being used to override the default. " + "Ignoring BLADERF_DEFAULT_TUNING_MODE.\n", + board_data->fpga_version.major, + board_data->fpga_version.minor, + board_data->fpga_version.patch); + } else { + mode = BLADERF_TUNING_MODE_FPGA; + } + } else { + log_debug("Invalid tuning mode override: %s\n", env_var); + } + } + + /* Check if the FPGA actually has RFIC control built in */ + if (BLADERF_TUNING_MODE_FPGA == mode && + !rfic_fpga_control.is_present(dev)) { + log_debug("FPGA does not have RFIC tuning capabilities, " + "defaulting to host-based control.\n"); + mode = BLADERF_TUNING_MODE_HOST; + } + + switch (mode) { + case BLADERF_TUNING_MODE_HOST: + log_debug("Default tuning mode: Host\n"); + break; + case BLADERF_TUNING_MODE_FPGA: + log_debug("Default tuning mode: FPGA\n"); + break; + default: + assert(!"Bug encountered."); + mode = BLADERF_TUNING_MODE_HOST; + } + + return mode; +} + + +/******************************************************************************/ +/* Misc */ +/******************************************************************************/ + +bool check_total_sample_rate(struct bladerf *dev) +{ + int status; + uint32_t reg; + size_t i; + + bladerf_sample_rate rate_accum = 0; + size_t active_channels = 0; + + const bladerf_sample_rate MAX_SAMPLE_THROUGHPUT = 80000000; + + /* Read RFFE control register */ + status = dev->backend->rffe_control_read(dev, ®); + if (status < 0) { + return false; + } + + /* Accumulate sample rates for all channels */ + if (_rffe_dir_enabled(reg, BLADERF_RX)) { + bladerf_sample_rate rx_rate; + + status = + dev->board->get_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rx_rate); + if (status < 0) { + return false; + } + + for (i = 0; i < dev->board->get_channel_count(dev, BLADERF_RX); ++i) { + if (_rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(i))) { + rate_accum += rx_rate; + ++active_channels; + } + } + } + + if (_rffe_dir_enabled(reg, BLADERF_TX)) { + bladerf_sample_rate tx_rate; + + status = + dev->board->get_sample_rate(dev, BLADERF_CHANNEL_TX(0), &tx_rate); + if (status < 0) { + return false; + } + + for (i = 0; i < dev->board->get_channel_count(dev, BLADERF_TX); ++i) { + if (_rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(i))) { + rate_accum += tx_rate; + ++active_channels; + } + } + } + + log_verbose("%s: active_channels=%d, rate_accum=%d, maximum=%d\n", + __FUNCTION__, active_channels, rate_accum, + MAX_SAMPLE_THROUGHPUT); + + if (rate_accum > MAX_SAMPLE_THROUGHPUT) { + log_warning("The total sample throughput for the %d active channel%s, " + "%g Msps, is greater than the recommended maximum sample " + "throughput, %g Msps. You may experience dropped samples " + "unless the sample rate is reduced, or some channels are " + "deactivated.\n", + active_channels, (active_channels == 1 ? "" : "s"), + rate_accum / 1e6, MAX_SAMPLE_THROUGHPUT / 1e6); + return false; + } + + return true; +} + +bool does_rffe_dir_have_enabled_ch(uint32_t reg, bladerf_direction dir) +{ + switch (dir) { + case BLADERF_RX: + return _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(0)) || + _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(1)); + case BLADERF_TX: + return _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(0)) || + _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(1)); + } + + return false; +} + +int get_gain_offset(struct bladerf *dev, bladerf_channel ch, float *offset) +{ + CHECK_BOARD_STATE(STATE_INITIALIZED); + NULL_CHECK(offset); + + struct bladerf_gain_range const *ranges = NULL; + bladerf_frequency frequency = 0; + size_t i, ranges_len; + + if (BLADERF_CHANNEL_IS_TX(ch)) { + ranges = bladerf2_tx_gain_ranges; + ranges_len = ARRAY_SIZE(bladerf2_tx_gain_ranges); + } else { + ranges = bladerf2_rx_gain_ranges; + ranges_len = ARRAY_SIZE(bladerf2_rx_gain_ranges); + } + + CHECK_STATUS(dev->board->get_frequency(dev, ch, &frequency)); + + for (i = 0; i < ranges_len; ++i) { + struct bladerf_gain_range const *r = &(ranges[i]); + struct bladerf_range const *rfreq = &(r->frequency); + + // if the frequency range matches, and the range name is null, + // then we found our match + if (is_within_range(rfreq, frequency) && (NULL == r->name)) { + *offset = r->offset; + return 0; + } + } + + return BLADERF_ERR_INVAL; +} |