Compare commits

..

No commits in common. "main" and "portapack-detection" have entirely different histories.

21 changed files with 212 additions and 465 deletions

View file

@ -17,127 +17,70 @@ jobs:
host:
strategy:
matrix:
os: ['macos', 'ubuntu', 'windows']
cmake: ['3.10.0', '3.21.7', '4.2.6', 'latest']
compiler: ['gcc', 'msvc']
sys:
- {os: 'macos', shell: 'bash'}
- {os: 'ubuntu', shell: 'bash'}
- {os: 'windows', shell: 'pwsh'}
- {os: 'windows', shell: 'msys2'}
cmake: ['3.10.0', '3.16.0', '3.21.0', '4.0.0', 'latest']
exclude:
# MSVC only runs on Windows.
- os: 'macos'
compiler: 'msvc'
- os: 'ubuntu'
compiler: 'msvc'
# We can't use the oldest CMake versions on Windows.
- os: 'windows'
# GitHub runners use Visual Studio 2022. Support added in CMake 3.21.
- sys: {os: 'windows', shell: 'pwsh'}
cmake: '3.10.0'
# The in-between CMake versions aren't so intersting with GCC.
- compiler: 'gcc'
cmake: '3.21.7'
- compiler: 'gcc'
cmake: '4.2.6'
- sys: {os: 'windows', shell: 'pwsh'}
cmake: '3.16.0'
# MSYS2 always supplies the latest cmake.
- sys: {os: 'windows', shell: 'msys2'}
cmake: '3.10.0'
- sys: {os: 'windows', shell: 'msys2'}
cmake: '3.16.0'
- sys: {os: 'windows', shell: 'msys2'}
cmake: '3.21.0'
- sys: {os: 'windows', shell: 'msys2'}
cmake: '4.0.0'
include:
# Shell selection
- os: 'macos'
shell: 'bash'
- os: 'ubuntu'
shell: 'bash'
- os: 'windows'
compiler: 'msvc'
shell: 'pwsh'
- os: 'windows'
compiler: 'gcc'
shell: 'msys2'
# MSVC version selection
- cmake: '3.21.7'
msvc: '17.14'
- cmake: '4.2.6'
msvc: 'latest'
- cmake: 'latest'
msvc: 'latest'
# CMake generator selection
- os: 'macos'
generator: 'Unix Makefiles'
- os: 'ubuntu'
generator: 'Unix Makefiles'
- compiler: 'msvc'
cmake: '3.21.7'
generator: 'Visual Studio 17 2022'
- compiler: 'msvc'
cmake: '4.2.6'
generator: 'Visual Studio 18 2026'
- compiler: 'msvc'
cmake: 'latest'
generator: 'Visual Studio 18 2026'
- os: 'windows'
compiler: 'gcc'
generator: 'MSYS Makefiles'
# Additional CMake arguments
- os: 'windows'
compiler: 'msvc'
- sys: {os: 'windows', shell: 'pwsh'}
cmake_args: >-
-DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe
-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
--install-prefix=$env:GITHUB_WORKSPACE/install
- os: 'windows'
compiler: 'gcc'
- sys: {os: 'windows', shell: 'msys2'}
cmake_args: >-
--install-prefix=/usr/local
# CMake 3.10 uses --build with --target install
- cmake: '3.10.0'
install_cmd: --build
install_args: --target install
# Later versions just use --install
- cmake: '3.21.7'
install_cmd: --install
- cmake: '4.2.6'
install_cmd: --install
- cmake: 'latest'
install_cmd: --install
# We need sudo on MacOS and Ubuntu only
- os: macos
sudo: sudo
- os: ubuntu
sudo: sudo
# Don't cancel all builds when one fails
fail-fast: false
runs-on: ${{ matrix.os }}-latest
runs-on: ${{ matrix.sys.os }}-latest
defaults:
run:
shell: '${{ matrix.shell }} {0}'
shell: '${{ matrix.sys.shell }} {0}'
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Setup cmake
uses: lukka/get-cmake@latest
with:
cmakeVersion: ${{ matrix.cmake }}
if: matrix.shell != 'msys2'
- name: Install MSVC (Windows)
uses: k3DW/setup-msvc@v1
with:
vs-version: ${{ matrix.msvc }}
if: matrix.compiler == 'msvc' && matrix.msvc != 'latest'
if: matrix.sys.shell != 'msys2'
- name: Install dependencies (macOS)
run: brew install fftw
if: matrix.os == 'macos'
if: matrix.sys.os == 'macos'
- name: Install dependencies (Ubuntu)
run: |
sudo apt update
sudo apt install libfftw3-dev libusb-1.0-0-dev
if: matrix.os == 'ubuntu'
if: matrix.sys.os == 'ubuntu'
- name: Install dependencies (Windows)
run: vcpkg install --triplet=x64-windows libusb fftw3 pthreads pkgconf
if: matrix.os == 'windows' && matrix.shell != 'msys2'
if: matrix.sys.os == 'windows' && matrix.sys.shell != 'msys2'
- name: Setup MSYS (Windows)
if: matrix.os == 'windows' && matrix.shell == 'msys2'
if: matrix.sys.os == 'windows' && matrix.sys.shell == 'msys2'
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
@ -155,7 +98,7 @@ jobs:
run: |
cmake -E make_directory host/build
cd host/build
cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}}
cmake .. -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}}
cmake --build . --config Release
# Build libhackrf ONLY
@ -164,12 +107,22 @@ jobs:
run: |
cmake -E make_directory host/libhackrf/build
cd host/libhackrf/build
cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake --build . --config Release
- name: Install (libhackrf)
run: |
${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/libhackrf/build ${{ matrix.install_args }} --config Release
sudo cmake --install host/libhackrf/build --config Release
if: matrix.sys.os != 'windows' && matrix.cmake != '3.10.0'
- name: Install (libhackrf, CMake 3.10)
run: |
sudo cmake --build host/libhackrf/build --target install --config Release
if: matrix.sys.os != 'windows' && matrix.cmake == '3.10.0'
- name: Install (libhackrf, Windows)
run: cmake --install host/libhackrf/build --config Release
if: matrix.sys.os == 'windows'
# Build hackrf-tools ONLY
@ -177,12 +130,22 @@ jobs:
run: |
cmake -E make_directory host/hackrf-tools/build
cd host/hackrf-tools/build
cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }}
cmake --build . --config Release
- name: Install (hackrf-tools)
run: |
${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/hackrf-tools/build ${{ matrix.install_args }}
sudo cmake --install host/hackrf-tools/build --config Release
if: matrix.sys.os != 'windows' && matrix.cmake != '3.10.0'
- name: Install (hackrf-tools, CMake 3.10)
run: |
sudo cmake --build host/hackrf-tools/build --target install --config Release
if: matrix.sys.os != 'windows' && matrix.cmake == '3.10.0'
- name: Install (hackrf-tools, Windows)
run: cmake --install host/hackrf-tools/build --config Release
if: matrix.sys.os == 'windows'
# Publish the contents of install/bin (which should be the combination libhackrf and host-tools) for Windows
- name: Publish Artifacts (Windows)
@ -190,7 +153,7 @@ jobs:
with:
name: hackrf-tools-windows
path: ${{github.workspace}}/install/bin
if: matrix.os == 'windows' && matrix.cmake == 'latest' && matrix.shell == 'pwsh'
if: matrix.sys.os == 'windows' && matrix.cmake == 'latest' && matrix.sys.shell == 'pwsh'
firmware:
strategy:
@ -216,7 +179,7 @@ jobs:
runs-on: ${{ matrix.os }}-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
submodules: true

View file

@ -16,7 +16,7 @@ jobs:
- check: 'firmware/hackrf_usb'
exclude: ''
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Run clang-format-action
uses: jidicula/clang-format-action@v4.6.2
with:

135
Jenkinsfile vendored
View file

@ -39,15 +39,31 @@ pipeline {
timeout(time: 20, unit: 'MINUTES')
}
steps {
runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES')
runCommand("Build HackRF One Firmware", './ci-scripts/build_firmware.sh HACKRF_ONE', 3, 1, 'MINUTES')
lock('HIL_hubs') {
script {
allOff()
runTest("Check Host", 'h1_eut', './ci-scripts/test_host.sh')
runTest("HackRF One HIL Test", 'h1_tester h1_eut', h1_test)
runTest("SGPIO Debug Test", 'h1_eut', 'python3 ci-scripts/test_sgpio_debug.py')
}
sh './ci-scripts/install_host.sh'
sh './ci-scripts/build_firmware.sh HACKRF_ONE'
script {
allOff()
reset('h1_eut')
}
sh 'sleep 1s'
retry(3) {
sh './ci-scripts/test_host.sh'
}
script {
reset('h1_tester h1_eut')
}
sh 'sleep 1s'
script {
// Allow up to 3 retries 5 minutes each for the HIL test
runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test)
}
script {
allOff()
reset('h1_eut')
}
sh 'sleep 1s'
retry(3) {
sh 'python3 ci-scripts/test_sgpio_debug.py'
}
}
}
@ -63,17 +79,32 @@ pipeline {
timeout(time: 20, unit: 'MINUTES')
}
steps {
runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES')
runCommand("Build Universal Firmware", './ci-scripts/build_firmware.sh UNIVERSAL', 3, 1, 'MINUTES')
lock('HIL_hubs') {
script {
allOff()
runTest("Check Host", 'h1_eut', './ci-scripts/test_host.sh')
runTest("HackRF One HIL Test", 'h1_tester h1_eut', h1_test)
runTest("SGPIO Debug Test", 'h1_eut', 'python3 ci-scripts/test_sgpio_debug.py')
}
sh './ci-scripts/install_host.sh'
sh './ci-scripts/build_firmware.sh UNIVERSAL'
script {
allOff()
reset('h1_eut')
}
sh 'sleep 1s'
retry(3) {
sh './ci-scripts/test_host.sh'
}
script {
reset('h1_tester h1_eut')
}
sh 'sleep 1s'
script {
// Allow up to 3 retries 5 minutes each for the HIL test
runCommand(3, 5, 'MINUTES', "HackRF One Test", h1_test)
}
script {
allOff()
reset('h1_eut')
}
sh 'sleep 1s'
retry(3) {
sh 'python3 ci-scripts/test_sgpio_debug.py'
}
}
}
stage('Test HackRF Pro with BOARD=PRALINE') {
@ -88,14 +119,23 @@ pipeline {
timeout(time: 20, unit: 'MINUTES')
}
steps {
runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES')
runCommand("Build Praline Firmware", './ci-scripts/build_firmware.sh PRALINE', 3, 1, 'MINUTES')
lock('HIL_hubs') {
script {
allOff()
runTest("Check Host", 'hpro_eut', './ci-scripts/test_host.sh')
runTest("HackRF Pro HIL Test", 'hpro_tester hpro_eut', hpro_test)
}
sh './ci-scripts/install_host.sh'
sh './ci-scripts/build_firmware.sh PRALINE'
script {
allOff()
reset('hpro_eut')
}
sh 'sleep 1s'
retry(3) {
sh './ci-scripts/test_host.sh'
}
script {
reset('hpro_tester hpro_eut')
}
sh 'sleep 1s'
script {
// Allow up to 3 retries 5 minutes each for the HIL test
runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test)
}
}
}
@ -111,14 +151,23 @@ pipeline {
timeout(time: 20, unit: 'MINUTES')
}
steps {
runCommand("Install Host Tools", './ci-scripts/install_host.sh', 3, 1, 'MINUTES')
runCommand("Build Universal Firmware", './ci-scripts/build_firmware.sh UNIVERSAL', 3, 1, 'MINUTES')
lock('HIL_hubs') {
script {
allOff()
runTest("Check Host", 'hpro_eut', './ci-scripts/test_host.sh')
runTest("HackRF Pro HIL Test", 'hpro_tester hpro_eut', hpro_test)
}
sh './ci-scripts/install_host.sh'
sh './ci-scripts/build_firmware.sh UNIVERSAL'
script {
allOff()
reset('hpro_eut')
}
sh 'sleep 1s'
retry(3) {
sh './ci-scripts/test_host.sh'
}
script {
reset('hpro_tester hpro_eut')
}
sh 'sleep 1s'
script {
// Allow up to 3 retries 5 minutes each for the HIL test
runCommand(3, 5, 'MINUTES', "HackRF Pro Test", hpro_test)
}
}
}
@ -135,15 +184,15 @@ pipeline {
def allOff() {
// Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately
runCommand('USB hub port power server command', "hubs all off", 3, 20, 'SECONDS')
runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs all off")
}
def reset(devices) {
// Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately
runCommand('USB hub port power server command', "hubs ${devices} reset", 3, 20, 'SECONDS')
runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs ${devices} reset")
}
def runCommand(title, cmd, retries, time, unit) {
def runCommand(retries, time, unit, title, cmd) {
retry(retries) {
try {
timeout(time: time, unit: unit) {
@ -165,13 +214,3 @@ def runCommand(title, cmd, retries, time, unit) {
}
}
}
def runTest(title, devices, cmd) {
retry(3) {
// reset() retains it's own internal retries
reset(devices)
sh 'sleep 1s'
// run the test with 0 internal retries and 3 external retries to ensure resets between runs
runCommand(title, cmd, 0, 5, 'MINUTES')
}
}

View file

@ -24,6 +24,8 @@
#include <stdint.h>
#include "hackrf_ui.h"
#include "i2c_bus.h"
#include "i2c_lpc.h"
#include "platform_detect.h"
#include "sgpio.h"
#include "si5351c.h"
@ -34,6 +36,8 @@
void clock_gen_init(void)
{
i2c_bus_start(si5351c.bus, &i2c_config_fast_clock);
si5351c_init(&si5351c);
si5351c_disable_all_outputs(&si5351c);
si5351c_disable_oeb_pin_control(&si5351c);
@ -116,6 +120,7 @@ void clock_gen_init(void)
void clock_gen_shutdown(void)
{
i2c_bus_start(si5351c.bus, &i2c_config_fast_clock);
si5351c_disable_all_outputs(&si5351c);
si5351c_disable_oeb_pin_control(&si5351c);
si5351c_power_down_all_clocks(&si5351c);

View file

@ -54,20 +54,3 @@ typedef uint64_t fp_28_36_t;
#define SR_FP_MHZ(mhz) (mhz##ULL * SR_FP_ONE_MHZ)
#define SR_FP_KHZ(khz) (khz##ULL * SR_FP_ONE_KHZ)
#define SR_FP_HZ(hz) (hz##ULL * SR_FP_ONE_HZ)
/* 1.63 fixed point */
typedef uint64_t fp_1_63_t;
#define FRAC_ONE (1ULL << 63)
/* one hundredth in 1.63 fixed point */
#define FRAC_ONE_PERCENT (FRAC_ONE / (100ULL))
/* one thousandth in 1.63 fixed point */
#define FRAC_ONE_PERMILLE (FRAC_ONE / (1000ULL))
/* one millionth in 1.63 fixed point */
#define FRAC_ONE_PPM (FRAC_ONE / (1000000ULL))
/* one billionth in 1.63 fixed point */
#define FRAC_ONE_PPB (FRAC_ONE / (1000000000ULL))

View file

@ -25,8 +25,6 @@
#include <libopencm3/lpc43xx/i2c.h>
#include <libopencm3/lpc43xx/memorymap.h>
#include "cpu_clock.h"
/* Driver instances. */
i2c_bus_t i2c0 = {
.obj = (void*) I2C0_BASE,
@ -43,7 +41,7 @@ i2c_bus_t i2c1 = {
};
const i2c_lpc_config_t i2c_config_fast_clock = {
.clock_khz = 400,
.duty_cycle_count = 255,
};
/* FIXME return i2c0 status from each function */
@ -53,9 +51,7 @@ void i2c_lpc_start(i2c_bus_t* const bus, const void* const _config)
const i2c_lpc_config_t* const config = _config;
const uint32_t port = (uint32_t) bus->obj;
const uint16_t duty_cycle_count =
(cpu_clock_mhz * (2000 / config->clock_khz)) / 4;
i2c_init(port, duty_cycle_count);
i2c_init(port, config->duty_cycle_count);
}
void i2c_lpc_stop(i2c_bus_t* const bus)

View file

@ -29,7 +29,7 @@
#include "i2c_bus.h"
typedef struct {
const uint16_t clock_khz;
const uint16_t duty_cycle_count;
} i2c_lpc_config_t;
void i2c_lpc_start(i2c_bus_t* const bus, const void* const config);

View file

@ -34,7 +34,6 @@
#include "rf_path.h"
#include "transceiver_mode.h"
#include "tuning.h"
#include "u128.h"
#ifdef IS_PRALINE
#include "fpga.h"
#include "tune_config.h"
@ -54,7 +53,7 @@ void radio_init(radio_t* const radio)
}
}
radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
radio->config[RADIO_BANK_REQUESTED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
radio->config[RADIO_BANK_IDLE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF;
radio->config[RADIO_BANK_RX][RADIO_OPMODE] = TRANSCEIVER_MODE_RX;
radio->config[RADIO_BANK_TX][RADIO_OPMODE] = TRANSCEIVER_MODE_TX;
@ -78,7 +77,7 @@ radio_error_t radio_reg_write(
}
switch (bank) {
case RADIO_BANK_REQUESTED:
case RADIO_BANK_ACTIVE:
mark_dirty(radio, reg);
/* fall through */
case RADIO_BANK_IDLE:
@ -133,75 +132,6 @@ static uint32_t radio_update_direction(radio_t* const radio, uint64_t* bank)
return (1 << RADIO_OPMODE);
}
/* Apply correction to a true frequency to get an adjusted frequency. */
static uint64_t adjust(uint64_t freq, uint64_t correction)
{
/* skip if we can */
if (correction == FRAC_ONE) {
return freq;
}
u128 product = u128_multiply(freq, correction);
/* product >> 63 */
return (product.hi << 1) | (product.lo >> 63);
}
/* Given an adjusted frequency and correction, return the true frequency. */
static uint64_t unadjust(uint64_t freq, uint64_t correction)
{
/* skip if we can */
if (correction == FRAC_ONE) {
return freq;
}
/* (1 << (63 + 63)) */
u128 numerator = (u128){
.hi = 1ULL << 62,
.lo = 0,
};
u128 denominator = (u128){
.hi = 0,
.lo = correction,
};
u128 reciprocal = u128_divide(numerator, denominator);
return adjust(freq, reciprocal.lo);
}
static uint64_t restrict_correction(uint64_t requested_correction)
{
/* Correction should be in the range 0.99 to 1.01 */
const uint64_t min_correction = FRAC_ONE - FRAC_ONE_PERCENT;
const uint64_t max_correction = FRAC_ONE + FRAC_ONE_PERCENT;
if (requested_correction >= min_correction &&
requested_correction <= max_correction) {
return requested_correction;
} else {
return FRAC_ONE;
}
}
static uint64_t set_lo(uint64_t freq_lo, uint64_t correction, bool apply)
{
fp_40_24_t corrected_lo = adjust(freq_lo, correction);
fp_40_24_t achieved_lo = mixer_set_frequency(&mixer, corrected_lo, apply);
return unadjust(achieved_lo, correction);
}
static uint64_t set_if(uint64_t freq_if, uint64_t correction, bool apply)
{
fp_40_24_t corrected_if = adjust(freq_if, correction);
fp_40_24_t achieved_if = max283x_set_frequency(&max283x, corrected_if, apply);
return unadjust(achieved_if, correction);
}
static uint64_t set_afe_rate(uint64_t afe_rate, uint64_t correction, bool apply)
{
fp_28_36_t corrected_rate = adjust(afe_rate, correction);
fp_28_36_t achieved_rate = sample_rate_set(corrected_rate, apply);
return unadjust(achieved_rate, correction);
}
#define ABSOLUTE_MIN_AFE_RATE SR_FP_KHZ(200)
#define ABSOLUTE_MAX_AFE_RATE SR_FP_KHZ(43600)
#define MAX_SUPPORTED_AFE_RATE SR_FP_KHZ(40000)
@ -253,9 +183,6 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
bool new_n = false;
const uint64_t requested_rate = bank[RADIO_SAMPLE_RATE];
const uint64_t requested_correction = bank[RADIO_CLOCK_CORRECTION];
uint64_t correction = restrict_correction(requested_correction);
if (requested_rate != RADIO_UNSET) {
rate = MIN(requested_rate, MAX_MCU_RATE);
@ -309,7 +236,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
new_n = (n != previous_n);
afe_rate = rate << n;
afe_rate = set_afe_rate(afe_rate, correction, false);
afe_rate = sample_rate_set(afe_rate, false);
previous_rate = radio->config[RADIO_BANK_APPLIED][RADIO_SAMPLE_RATE];
if ((previous_n == RADIO_UNSET) || previous_rate == RADIO_UNSET) {
previous_afe_rate = RADIO_UNSET;
@ -318,7 +245,7 @@ static uint32_t radio_update_sample_rate(radio_t* const radio, uint64_t* bank)
}
new_afe_rate = (afe_rate != previous_afe_rate);
if (new_afe_rate) {
afe_rate = set_afe_rate(afe_rate, correction, true);
afe_rate = sample_rate_set(afe_rate, true);
}
rate = afe_rate >> n;
@ -446,7 +373,6 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
#ifdef IS_PRALINE
const uint64_t requested_rotation = bank[RADIO_ROTATION];
#endif
const uint64_t requested_correction = bank[RADIO_CLOCK_CORRECTION];
const uint64_t applied_rf = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF];
const uint64_t applied_if = radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF];
@ -455,8 +381,6 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
radio->config[RADIO_BANK_APPLIED][RADIO_IMAGE_REJECT];
const uint64_t applied_rotation =
radio->config[RADIO_BANK_APPLIED][RADIO_ROTATION];
const uint64_t applied_correction =
radio->config[RADIO_BANK_APPLIED][RADIO_CLOCK_CORRECTION];
uint64_t freq_rf = applied_rf;
uint64_t analog_rf = applied_rf;
@ -464,7 +388,6 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
uint64_t freq_lo = applied_lo;
uint64_t img_reject = applied_img_reject;
uint64_t rotation = applied_rotation;
uint64_t correction = applied_correction;
uint64_t opmode = bank[RADIO_OPMODE];
if (opmode == RADIO_UNSET) {
@ -482,12 +405,11 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
img_reject = RF_PATH_FILTER_BYPASS;
}
}
correction = restrict_correction(requested_correction);
if (requested_if != RADIO_UNSET) {
freq_if = set_if(requested_if, correction, false);
freq_if = max283x_set_frequency(&max283x, requested_if, false);
}
if (requested_lo != RADIO_UNSET) {
freq_lo = set_lo(requested_lo, correction, false);
freq_lo = mixer_set_frequency(&mixer, freq_lo, false);
}
#ifdef IS_PRALINE
if (IS_PRALINE) {
@ -569,7 +491,7 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
freq_lo = RADIO_UNSET;
}
if (freq_lo != RADIO_UNSET) {
freq_lo = set_lo(freq_lo, correction, false);
freq_lo = mixer_set_frequency(&mixer, freq_lo, false);
}
}
@ -591,12 +513,12 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
/* Apply settings. */
if ((freq_if != applied_if) && (freq_if != RADIO_UNSET)) {
freq_if = set_if(freq_if, correction, true);
freq_if = max283x_set_frequency(&max283x, freq_if, true);
radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_IF] = freq_if;
changed |= (1 << RADIO_FREQUENCY_IF);
}
if ((freq_lo != applied_lo) && (freq_lo != RADIO_UNSET)) {
freq_lo = set_lo(freq_lo, correction, true);
freq_lo = mixer_set_frequency(&mixer, freq_lo, true);
radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = freq_lo;
changed |= (1 << RADIO_FREQUENCY_LO);
}
@ -639,10 +561,6 @@ static uint32_t radio_update_frequency(radio_t* const radio, uint64_t* bank)
radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_RF] = freq_rf;
changed |= (1 << RADIO_FREQUENCY_RF);
}
if ((correction != applied_correction) && (correction != RADIO_UNSET)) {
radio->config[RADIO_BANK_APPLIED][RADIO_CLOCK_CORRECTION] = correction;
changed |= (1 << RADIO_CLOCK_CORRECTION);
}
return changed;
}
@ -937,7 +855,7 @@ bool radio_update(radio_t* const radio)
return false;
}
radio->regs_dirty = 0;
memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_REQUESTED][0]), sizeof(tmp_bank));
memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_ACTIVE][0]), sizeof(tmp_bank));
nvic_enable_irq(NVIC_USB0_IRQ);
if ((dirty & RADIO_REG_GROUP_RATE) ||
@ -998,14 +916,14 @@ void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode)
nvic_disable_irq(NVIC_USB0_IRQ);
for (uint8_t reg = 0; reg < RADIO_NUM_REGS; reg++) {
value = radio->config[source_bank][reg];
previous = radio->config[RADIO_BANK_REQUESTED][reg];
previous = radio->config[RADIO_BANK_ACTIVE][reg];
if ((value != RADIO_UNSET) && (value != previous)) {
radio->config[RADIO_BANK_REQUESTED][reg] = value;
radio->config[RADIO_BANK_ACTIVE][reg] = value;
mark_dirty(radio, reg);
}
}
radio->config[RADIO_BANK_REQUESTED][RADIO_OPMODE] = mode;
radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = mode;
mark_dirty(radio, RADIO_OPMODE);
nvic_enable_irq(NVIC_USB0_IRQ);
radio_update(radio);

View file

@ -165,23 +165,17 @@ typedef enum {
* DC block enable of type bool.
*/
RADIO_DC_BLOCK = 22,
/**
* Correction factor for radio reference clock, of type fp_1_63_t.
*/
RADIO_CLOCK_CORRECTION = 23,
} radio_register_t;
#define RADIO_NUM_REGS (24)
#define RADIO_NUM_REGS (23)
#define RADIO_UNSET (0xffffffffffffffff)
/* register groups for bitfield convenience */
#define RADIO_REG_GROUP_RATE \
((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) | \
(1 << RADIO_RESAMPLE_RX) | (1 << RADIO_CLOCK_CORRECTION))
#define RADIO_REG_GROUP_FREQ \
((1 << RADIO_FREQUENCY_RF) | (1 << RADIO_FREQUENCY_IF) | \
(1 << RADIO_FREQUENCY_LO) | (1 << RADIO_IMAGE_REJECT) | (1 << RADIO_ROTATION) | \
(1 << RADIO_CLOCK_CORRECTION))
#define RADIO_REG_GROUP_RATE \
((1 << RADIO_SAMPLE_RATE) | (1 << RADIO_RESAMPLE_TX) | (1 << RADIO_RESAMPLE_RX))
#define RADIO_REG_GROUP_FREQ \
((1 << RADIO_FREQUENCY_RF) | (1 << RADIO_FREQUENCY_IF) | \
(1 << RADIO_FREQUENCY_LO) | (1 << RADIO_IMAGE_REJECT) | (1 << RADIO_ROTATION))
#define RADIO_REG_GROUP_BW \
((1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_BB_BANDWIDTH_RX) | \
(1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF) | \
@ -191,7 +185,7 @@ typedef enum {
(1 << RADIO_GAIN_RX_IF) | (1 << RADIO_GAIN_RX_BB))
/**
* Register bank RADIO_BANK_REQUESTED stores the active configuration. Active
* Register bank RADIO_BANK_ACTIVE stores the active configuration. Active
* register settings are copied to the applied register when applied.
*
* The other three banks store settings that will be applied when switching to
@ -202,7 +196,7 @@ typedef enum {
*/
typedef enum {
RADIO_BANK_APPLIED = 0,
RADIO_BANK_REQUESTED = 1,
RADIO_BANK_ACTIVE = 1,
RADIO_BANK_IDLE = 2,
RADIO_BANK_RX = 3,
RADIO_BANK_TX = 4,
@ -237,7 +231,7 @@ typedef struct {
void radio_init(radio_t* const radio);
/**
* Write to one or more registers. Writes to RADIO_BANK_REQUESTED are applied at
* Write to one or more registers. Writes to RADIO_BANK_ACTIVE are applied at
* the next radio_update(). Writes to RADIO_BANK_APPLIED are not supported.
*/
radio_error_t radio_reg_write(
@ -255,7 +249,7 @@ uint64_t radio_reg_read(
const radio_register_t reg);
/**
* Apply changes requested in RADIO_BANK_REQUESTED.
* Apply changes requested in RADIO_BANK_ACTIVE.
* Return true if any changes were applied.
*/
bool radio_update(radio_t* const radio);

View file

@ -274,6 +274,17 @@ void si5351c_enable_clock_outputs(si5351c_driver_t* const drv)
SI5351C_OUTPUT_DISABLE);
}
si5351c_regs_commit(drv);
#ifdef IS_H1_R9
if (IS_H1_R9) {
const platform_gpio_t* gpio = platform_gpio();
if (drv->clk[drv->clkout_id].output_enable) {
gpio_set(gpio->h1r9_clkout_en);
} else {
gpio_clear(gpio->h1r9_clkout_en);
}
}
#endif
}
void si5351c_set_int_mode(
@ -328,7 +339,7 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv)
}
}
static void si5351c_clkout_ms_enable(si5351c_driver_t* const drv, bool enable)
void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable)
{
drv->clk[drv->clkout_id].output_enable = enable;
drv->clk[drv->clkout_id].power_down = !enable;
@ -340,42 +351,12 @@ static void si5351c_clkout_ms_enable(si5351c_driver_t* const drv, bool enable)
si5351c_enable_clock_outputs(drv);
}
void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable)
{
#ifdef IS_H1_R9
if (IS_H1_R9) {
const platform_gpio_t* gpio = platform_gpio();
/* CLKOUT is shared with MCU_CLK, enable MS when either on. */
bool mcu_clkin_enabled = gpio_read(gpio->h1r9_mcu_clk_en);
bool ms_needed = enable | mcu_clkin_enabled;
si5351c_clkout_ms_enable(drv, ms_needed);
/* Set GPIO to gate CLKOUT output downstream of MS. */
gpio_write(gpio->h1r9_clkout_en, enable);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
/* We have a dedicated CLKOUT multisynth. */
si5351c_clkout_ms_enable(drv, enable);
}
#endif
}
void si5351c_mcu_clkin_enable(si5351c_driver_t* const drv, bool enable)
{
#ifdef IS_H1_R9
if (IS_H1_R9) {
const platform_gpio_t* gpio = platform_gpio();
/* MCU_CLK is shared with CLKOUT, enable MS when either on. */
bool clkout_enabled = gpio_read(gpio->h1r9_clkout_en);
bool ms_needed = enable | clkout_enabled;
si5351c_clkout_ms_enable(drv, ms_needed);
/* Set GPIO to gate MCU_CLK output downstream of MS. */
gpio_write(gpio->h1r9_mcu_clk_en, enable);
/* MCU clock is shared with CLKOUT. */
si5351c_clkout_enable(drv, enable);
}
#endif
#ifdef IS_NOT_H1_R9

View file

@ -1,88 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "u128.h"
/* From https://stackoverflow.com/a/58381061 */
u128 u128_multiply(uint64_t lhs, uint64_t rhs)
{
uint64_t lo_lo = (lhs & 0xFFFFFFFF) * (rhs & 0xFFFFFFFF);
uint64_t hi_lo = (lhs >> 32) * (rhs & 0xFFFFFFFF);
uint64_t lo_hi = (lhs & 0xFFFFFFFF) * (rhs >> 32);
uint64_t hi_hi = (lhs >> 32) * (rhs >> 32);
uint64_t cross = ((lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi);
return (u128){
.hi = ((hi_lo >> 32) + (cross >> 32) + hi_hi),
.lo = ((cross << 32) | (lo_lo & 0xFFFFFFFF)),
};
}
/* From https://stackoverflow.com/a/70052545 */
/* clang-format off */
#define SUBCcc(a,b,cy,t0,t1,t2) \
(t0=(b)+cy, t1=(a), cy=t0<cy, t2=t1<t0, cy=cy+t2, t1-t0)
#define SUBcc(a,b,cy,t0,t1) \
(t0=(b), t1=(a), cy=t1<t0, t1-t0)
#define SUBC(a,b,cy,t0,t1) \
(t0=(b)+cy, t1=(a), t1-t0)
#define ADDCcc(a,b,cy,t0,t1) \
(t0=(b)+cy, t1=(a), cy=t0<cy, t0=t0+t1, t1=t0<t1, cy=cy+t1, t0=t0)
#define ADDcc(a,b,cy,t0,t1) \
(t0=(b), t1=(a), t0=t0+t1, cy=t0<t1, t0=t0)
#define ADDC(a,b,cy,t0,t1) \
(t0=(b)+cy, t1=(a), t0+t1)
/* clang-format on */
u128 u128_divide(u128 dvnd, u128 dvsr)
{
u128 quot, rem, tmp;
uint64_t cy, t0, t1, t2;
quot.hi = dvnd.hi;
quot.lo = dvnd.lo;
rem.hi = 0;
rem.lo = 0;
for (int i = 0; i < 128; i++) {
quot.lo = ADDcc(quot.lo, quot.lo, cy, t0, t1);
quot.hi = ADDCcc(quot.hi, quot.hi, cy, t0, t1);
rem.lo = ADDCcc(rem.lo, rem.lo, cy, t0, t1);
rem.hi = ADDC(rem.hi, rem.hi, cy, t0, t1);
tmp.lo = SUBcc(rem.lo, dvsr.lo, cy, t0, t1);
tmp.hi = SUBCcc(rem.hi, dvsr.hi, cy, t0, t1, t2);
if (!cy) { // remainder >= divisor
rem.lo = tmp.lo;
rem.hi = tmp.hi;
quot.lo |= 1;
}
}
return quot;
}

View file

@ -1,32 +0,0 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <stdint.h>
typedef struct {
uint64_t hi;
uint64_t lo;
} u128;
u128 u128_multiply(uint64_t a, uint64_t b);
u128 u128_divide(u128 dvnd, u128 dvsr);

Binary file not shown.

View file

@ -240,7 +240,6 @@ class ProgrammableShift(wiring.Component):
# Implement the map itself (should it be done outside?)
max_shift = max(self.shift_map.values())
min_shift = min(self.shift_map.values())
value_scaled = [ Signal(signed(self.width_in + max_shift)) for _ in range(self.num_channels) ]
scaled_valid = Signal()
@ -256,9 +255,7 @@ class ProgrammableShift(wiring.Component):
for k, v in self.shift_map.items():
with m.Case(k):
m.d.sync += value_scaled[c].eq(self.input.payload[c] << (max_shift - v))
with m.Default():
m.d.sync += value_scaled[c].eq(self.input.payload[c] << (max_shift - min_shift))
with m.If(~self.output.valid | self.output.ready):
m.d.comb += scaled_ready.eq(1)
m.d.sync += self.output.valid.eq(scaled_valid)

View file

@ -203,7 +203,6 @@ macro(DeclareTargets)
${PATH_HACKRF_FIRMWARE_COMMON}/adc.c
${PATH_HACKRF_FIRMWARE_COMMON}/da7219.c
${PATH_HACKRF_FIRMWARE_COMMON}/max283x.c
${PATH_HACKRF_FIRMWARE_COMMON}/u128.c
)
if(BOARD STREQUAL "RAD1O")

View file

@ -36,8 +36,6 @@
#include <delay.h>
#include <fixed_point.h>
#include <hackrf_ui.h>
#include <i2c_bus.h>
#include <i2c_lpc.h>
#include <leds.h>
#include <operacake.h>
#include <mixer.h>
@ -428,8 +426,6 @@ int main(void)
detect_hardware_platform();
board_id_t board_id = detected_platform();
i2c_bus_start(&i2c0, &i2c_config_fast_clock);
pins_shutdown();
sgpio_pin_shutdown(&sgpio_config);
rf_path_pin_shutdown();
@ -491,9 +487,6 @@ int main(void)
#endif
cpu_clock_init();
/* Clock speed has changed, adjust I2C clock */
i2c_bus_start(&i2c0, &i2c_config_fast_clock);
/* Wake the M0 */
ipc_halt_m0();
ipc_start_m0((uint32_t) &__ram_m0_start__);

View file

@ -132,7 +132,7 @@ usb_request_status_t usb_vendor_request_init_sweep(
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_FREQUENCY_RF,
(sweep_freq + offset) * FP_ONE_HZ);
usb_transfer_schedule_ack(endpoint->in);
@ -263,7 +263,7 @@ void sweep_mode(uint32_t seq)
nvic_disable_irq(NVIC_USB0_IRQ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_FREQUENCY_RF,
(sweep_freq + offset) * FP_ONE_HZ);
nvic_enable_irq(NVIC_USB0_IRQ);

View file

@ -94,12 +94,12 @@ usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth(
(endpoint->setup.index << 16) | endpoint->setup.value;
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_BB_BANDWIDTH_TX,
bandwidth);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_BB_BANDWIDTH_RX,
bandwidth);
usb_transfer_schedule_ack(endpoint->in);
@ -123,22 +123,22 @@ usb_request_status_t usb_vendor_request_set_freq(
set_freq_params.freq_mhz * 1000000ULL + set_freq_params.freq_hz;
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_FREQUENCY_RF,
freq * FP_ONE_HZ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_FREQUENCY_IF,
RADIO_UNSET);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_FREQUENCY_LO,
RADIO_UNSET);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_IMAGE_REJECT,
RADIO_UNSET);
usb_transfer_schedule_ack(endpoint->in);
@ -160,17 +160,17 @@ usb_request_status_t usb_vendor_request_set_freq_explicit(
} else if (stage == USB_TRANSFER_STAGE_DATA) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_FREQUENCY_IF,
explicit_params.if_freq_hz * FP_ONE_HZ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_FREQUENCY_LO,
explicit_params.lo_freq_hz * FP_ONE_HZ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_IMAGE_REJECT,
explicit_params.path);
usb_transfer_schedule_ack(endpoint->in);
@ -215,7 +215,7 @@ usb_request_status_t usb_vendor_request_set_sample_rate_frac(
uint32_t numerator = set_sample_r_params.freq_hz;
uint32_t denominator = set_sample_r_params.divider;
uint64_t value = round_sample_rate(numerator, denominator);
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_SAMPLE_RATE, value);
radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_SAMPLE_RATE, value);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
@ -228,12 +228,12 @@ usb_request_status_t usb_vendor_request_set_amp_enable(
if (stage == USB_TRANSFER_STAGE_SETUP) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_GAIN_TX_RF,
endpoint->setup.value);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_GAIN_RX_RF,
endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
@ -247,7 +247,7 @@ usb_request_status_t usb_vendor_request_set_lna_gain(
{
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint8_t gain = endpoint->setup.index;
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_IF, gain);
radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_GAIN_RX_IF, gain);
endpoint->buffer[0] = RADIO_OK;
usb_transfer_schedule_block(
endpoint->in,
@ -266,7 +266,7 @@ usb_request_status_t usb_vendor_request_set_vga_gain(
{
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint8_t gain = endpoint->setup.index;
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_BB, gain);
radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_GAIN_RX_BB, gain);
endpoint->buffer[0] = RADIO_OK;
usb_transfer_schedule_block(
endpoint->in,
@ -285,7 +285,7 @@ usb_request_status_t usb_vendor_request_set_txvga_gain(
{
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint8_t gain = endpoint->setup.index;
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_TX_IF, gain);
radio_reg_write(&radio, RADIO_BANK_ACTIVE, RADIO_GAIN_TX_IF, gain);
endpoint->buffer[0] = RADIO_OK;
usb_transfer_schedule_block(
endpoint->in,
@ -315,7 +315,7 @@ usb_request_status_t usb_vendor_request_set_antenna_enable(
if (stage == USB_TRANSFER_STAGE_SETUP) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_BIAS_TEE,
endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
@ -427,7 +427,7 @@ usb_request_status_t usb_vendor_request_set_hw_sync_mode(
if (stage == USB_TRANSFER_STAGE_SETUP) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BANK_ACTIVE,
RADIO_TRIGGER,
endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);

View file

@ -28,7 +28,7 @@
#define USB_VENDOR_ID (0x1D50)
#define USB_API_VERSION (0x0113)
#define USB_API_VERSION (0x0112)
#define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF)

View file

@ -501,7 +501,7 @@ int radio_read_register(
return result;
}
#define RADIO_NUM_REGS (24)
#define RADIO_NUM_REGS (23)
int radio_read_registers(hackrf_device* device, const uint8_t bank)
{

View file

@ -57,7 +57,6 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI
#define DEFAULT_REQUEST_TIMEOUT 100
#define CPLD_WRITE_TIMEOUT 10000
#define SPIFLASH_WRITE_TIMEOUT 50000 // W25Q32JV max chip erase time
#define FPGA_BITSTREAM_TIMEOUT 500
// TODO: Factor this into a shared #include so that firmware can use
// the same values.
@ -3497,7 +3496,7 @@ int ADDCALL hackrf_set_fpga_bitstream(hackrf_device* device, const uint8_t index
0,
NULL,
0,
FPGA_BITSTREAM_TIMEOUT);
DEFAULT_REQUEST_TIMEOUT);
if (result != 0) {
last_libusb_error = result;