diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02239f82..21593d8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,70 +17,127 @@ jobs: host: strategy: matrix: - 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'] + os: ['macos', 'ubuntu', 'windows'] + cmake: ['3.10.0', '3.21.7', '4.2.6', 'latest'] + compiler: ['gcc', 'msvc'] exclude: - # GitHub runners use Visual Studio 2022. Support added in CMake 3.21. - - sys: {os: 'windows', shell: 'pwsh'} + # 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' cmake: '3.10.0' - - 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' + # The in-between CMake versions aren't so intersting with GCC. + - compiler: 'gcc' + cmake: '3.21.7' + - compiler: 'gcc' + cmake: '4.2.6' include: - - sys: {os: 'windows', shell: 'pwsh'} + # 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' 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 - - sys: {os: 'windows', shell: 'msys2'} + - os: 'windows' + compiler: 'gcc' 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.sys.os }}-latest + runs-on: ${{ matrix.os }}-latest defaults: run: - shell: '${{ matrix.sys.shell }} {0}' + shell: '${{ matrix.shell }} {0}' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup cmake uses: lukka/get-cmake@latest with: cmakeVersion: ${{ matrix.cmake }} - if: matrix.sys.shell != 'msys2' + 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' - name: Install dependencies (macOS) run: brew install fftw - if: matrix.sys.os == 'macos' + if: matrix.os == 'macos' - name: Install dependencies (Ubuntu) run: | sudo apt update sudo apt install libfftw3-dev libusb-1.0-0-dev - if: matrix.sys.os == 'ubuntu' + if: matrix.os == 'ubuntu' - name: Install dependencies (Windows) run: vcpkg install --triplet=x64-windows libusb fftw3 pthreads pkgconf - if: matrix.sys.os == 'windows' && matrix.sys.shell != 'msys2' + if: matrix.os == 'windows' && matrix.shell != 'msys2' - name: Setup MSYS (Windows) - if: matrix.sys.os == 'windows' && matrix.sys.shell == 'msys2' + if: matrix.os == 'windows' && matrix.shell == 'msys2' uses: msys2/setup-msys2@v2 with: msystem: UCRT64 @@ -98,7 +155,7 @@ jobs: run: | cmake -E make_directory host/build cd host/build - cmake .. -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}} + cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{matrix.cmake_args}} cmake --build . --config Release # Build libhackrf ONLY @@ -107,22 +164,12 @@ jobs: run: | cmake -E make_directory host/libhackrf/build cd host/libhackrf/build - cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }} + cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }} cmake --build . --config Release - name: Install (libhackrf) run: | - 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' + ${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/libhackrf/build ${{ matrix.install_args }} --config Release # Build hackrf-tools ONLY @@ -130,22 +177,12 @@ jobs: run: | cmake -E make_directory host/hackrf-tools/build cd host/hackrf-tools/build - cmake .. -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }} + cmake .. -G "${{ matrix.generator }}" -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_args }} cmake --build . --config Release - name: Install (hackrf-tools) run: | - 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' + ${{ matrix.sudo }} cmake ${{ matrix.install_cmd }} host/hackrf-tools/build ${{ matrix.install_args }} # Publish the contents of install/bin (which should be the combination libhackrf and host-tools) for Windows - name: Publish Artifacts (Windows) @@ -153,7 +190,7 @@ jobs: with: name: hackrf-tools-windows path: ${{github.workspace}}/install/bin - if: matrix.sys.os == 'windows' && matrix.cmake == 'latest' && matrix.sys.shell == 'pwsh' + if: matrix.os == 'windows' && matrix.cmake == 'latest' && matrix.shell == 'pwsh' firmware: strategy: @@ -179,7 +216,7 @@ jobs: runs-on: ${{ matrix.os }}-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 0bf33429..d498d609 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -16,7 +16,7 @@ jobs: - check: 'firmware/hackrf_usb' exclude: '' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Run clang-format-action uses: jidicula/clang-format-action@v4.6.2 with: diff --git a/Jenkinsfile b/Jenkinsfile index b41d6714..dcfd9ad8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -39,31 +39,15 @@ pipeline { timeout(time: 20, unit: 'MINUTES') } steps { - 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' + 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') + } } } } @@ -79,32 +63,17 @@ pipeline { timeout(time: 20, unit: 'MINUTES') } steps { - 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' + 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') + } } + } } stage('Test HackRF Pro with BOARD=PRALINE') { @@ -119,23 +88,14 @@ pipeline { timeout(time: 20, unit: 'MINUTES') } steps { - 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) + 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) + } } } } @@ -151,23 +111,14 @@ pipeline { timeout(time: 20, unit: 'MINUTES') } steps { - 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) + 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) + } } } } @@ -184,15 +135,15 @@ pipeline { def allOff() { // Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately - runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs all off") + runCommand('USB hub port power server command', "hubs all off", 3, 20, 'SECONDS') } def reset(devices) { // Allow up to 3 retries, 20 seconds each, for the USB hub port power server to respond appropriately - runCommand(3, 20, 'SECONDS', 'USB hub port power server command', "hubs ${devices} reset") + runCommand('USB hub port power server command', "hubs ${devices} reset", 3, 20, 'SECONDS') } -def runCommand(retries, time, unit, title, cmd) { +def runCommand(title, cmd, retries, time, unit) { retry(retries) { try { timeout(time: time, unit: unit) { @@ -214,3 +165,13 @@ def runCommand(retries, time, unit, title, cmd) { } } } + +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') + } +} diff --git a/firmware/common/clock_gen.c b/firmware/common/clock_gen.c index 0cf21ab2..a709960b 100644 --- a/firmware/common/clock_gen.c +++ b/firmware/common/clock_gen.c @@ -24,8 +24,6 @@ #include #include "hackrf_ui.h" -#include "i2c_bus.h" -#include "i2c_lpc.h" #include "platform_detect.h" #include "sgpio.h" #include "si5351c.h" @@ -36,8 +34,6 @@ 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); @@ -120,7 +116,6 @@ 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); diff --git a/firmware/common/fixed_point.h b/firmware/common/fixed_point.h index b6908797..974e34d2 100644 --- a/firmware/common/fixed_point.h +++ b/firmware/common/fixed_point.h @@ -54,3 +54,20 @@ 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)) diff --git a/firmware/common/i2c_lpc.c b/firmware/common/i2c_lpc.c index 9d6499fa..2c0fc5e2 100644 --- a/firmware/common/i2c_lpc.c +++ b/firmware/common/i2c_lpc.c @@ -25,6 +25,8 @@ #include #include +#include "cpu_clock.h" + /* Driver instances. */ i2c_bus_t i2c0 = { .obj = (void*) I2C0_BASE, @@ -41,7 +43,7 @@ i2c_bus_t i2c1 = { }; const i2c_lpc_config_t i2c_config_fast_clock = { - .duty_cycle_count = 255, + .clock_khz = 400, }; /* FIXME return i2c0 status from each function */ @@ -51,7 +53,9 @@ 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; - i2c_init(port, config->duty_cycle_count); + const uint16_t duty_cycle_count = + (cpu_clock_mhz * (2000 / config->clock_khz)) / 4; + i2c_init(port, duty_cycle_count); } void i2c_lpc_stop(i2c_bus_t* const bus) diff --git a/firmware/common/i2c_lpc.h b/firmware/common/i2c_lpc.h index e71cb362..2a2562ee 100644 --- a/firmware/common/i2c_lpc.h +++ b/firmware/common/i2c_lpc.h @@ -29,7 +29,7 @@ #include "i2c_bus.h" typedef struct { - const uint16_t duty_cycle_count; + const uint16_t clock_khz; } i2c_lpc_config_t; void i2c_lpc_start(i2c_bus_t* const bus, const void* const config); diff --git a/firmware/common/radio.c b/firmware/common/radio.c index f8b2e514..36f65cad 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -34,6 +34,7 @@ #include "rf_path.h" #include "transceiver_mode.h" #include "tuning.h" +#include "u128.h" #ifdef IS_PRALINE #include "fpga.h" #include "tune_config.h" @@ -53,7 +54,7 @@ void radio_init(radio_t* const radio) } } radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; - radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; + radio->config[RADIO_BANK_REQUESTED][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; @@ -77,7 +78,7 @@ radio_error_t radio_reg_write( } switch (bank) { - case RADIO_BANK_ACTIVE: + case RADIO_BANK_REQUESTED: mark_dirty(radio, reg); /* fall through */ case RADIO_BANK_IDLE: @@ -132,6 +133,75 @@ 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) @@ -183,6 +253,9 @@ 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); @@ -236,7 +309,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 = sample_rate_set(afe_rate, false); + afe_rate = set_afe_rate(afe_rate, correction, 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; @@ -245,7 +318,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 = sample_rate_set(afe_rate, true); + afe_rate = set_afe_rate(afe_rate, correction, true); } rate = afe_rate >> n; @@ -373,6 +446,7 @@ 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]; @@ -381,6 +455,8 @@ 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; @@ -388,6 +464,7 @@ 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) { @@ -405,11 +482,12 @@ 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 = max283x_set_frequency(&max283x, requested_if, false); + freq_if = set_if(requested_if, correction, false); } if (requested_lo != RADIO_UNSET) { - freq_lo = mixer_set_frequency(&mixer, freq_lo, false); + freq_lo = set_lo(requested_lo, correction, false); } #ifdef IS_PRALINE if (IS_PRALINE) { @@ -491,7 +569,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 = mixer_set_frequency(&mixer, freq_lo, false); + freq_lo = set_lo(freq_lo, correction, false); } } @@ -513,12 +591,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 = max283x_set_frequency(&max283x, freq_if, true); + freq_if = set_if(freq_if, correction, 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 = mixer_set_frequency(&mixer, freq_lo, true); + freq_lo = set_lo(freq_lo, correction, true); radio->config[RADIO_BANK_APPLIED][RADIO_FREQUENCY_LO] = freq_lo; changed |= (1 << RADIO_FREQUENCY_LO); } @@ -561,6 +639,10 @@ 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; } @@ -855,7 +937,7 @@ bool radio_update(radio_t* const radio) return false; } radio->regs_dirty = 0; - memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_ACTIVE][0]), sizeof(tmp_bank)); + memcpy(&tmp_bank[0], &(radio->config[RADIO_BANK_REQUESTED][0]), sizeof(tmp_bank)); nvic_enable_irq(NVIC_USB0_IRQ); if ((dirty & RADIO_REG_GROUP_RATE) || @@ -916,14 +998,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_ACTIVE][reg]; + previous = radio->config[RADIO_BANK_REQUESTED][reg]; if ((value != RADIO_UNSET) && (value != previous)) { - radio->config[RADIO_BANK_ACTIVE][reg] = value; + radio->config[RADIO_BANK_REQUESTED][reg] = value; mark_dirty(radio, reg); } } - radio->config[RADIO_BANK_ACTIVE][RADIO_OPMODE] = mode; + radio->config[RADIO_BANK_REQUESTED][RADIO_OPMODE] = mode; mark_dirty(radio, RADIO_OPMODE); nvic_enable_irq(NVIC_USB0_IRQ); radio_update(radio); diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 821c7fea..8e12471a 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -165,17 +165,23 @@ 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 (23) +#define RADIO_NUM_REGS (24) #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)) -#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_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_BW \ ((1 << RADIO_BB_BANDWIDTH_TX) | (1 << RADIO_BB_BANDWIDTH_RX) | \ (1 << RADIO_XCVR_TX_LPF) | (1 << RADIO_XCVR_RX_LPF) | \ @@ -185,7 +191,7 @@ typedef enum { (1 << RADIO_GAIN_RX_IF) | (1 << RADIO_GAIN_RX_BB)) /** - * Register bank RADIO_BANK_ACTIVE stores the active configuration. Active + * Register bank RADIO_BANK_REQUESTED 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 @@ -196,7 +202,7 @@ typedef enum { */ typedef enum { RADIO_BANK_APPLIED = 0, - RADIO_BANK_ACTIVE = 1, + RADIO_BANK_REQUESTED = 1, RADIO_BANK_IDLE = 2, RADIO_BANK_RX = 3, RADIO_BANK_TX = 4, @@ -231,7 +237,7 @@ typedef struct { void radio_init(radio_t* const radio); /** - * Write to one or more registers. Writes to RADIO_BANK_ACTIVE are applied at + * Write to one or more registers. Writes to RADIO_BANK_REQUESTED are applied at * the next radio_update(). Writes to RADIO_BANK_APPLIED are not supported. */ radio_error_t radio_reg_write( @@ -249,7 +255,7 @@ uint64_t radio_reg_read( const radio_register_t reg); /** - * Apply changes requested in RADIO_BANK_ACTIVE. + * Apply changes requested in RADIO_BANK_REQUESTED. * Return true if any changes were applied. */ bool radio_update(radio_t* const radio); diff --git a/firmware/common/si5351c.c b/firmware/common/si5351c.c index 259c062a..a455a23e 100644 --- a/firmware/common/si5351c.c +++ b/firmware/common/si5351c.c @@ -274,17 +274,6 @@ 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( @@ -339,7 +328,7 @@ bool si5351c_clkin_signal_valid(si5351c_driver_t* const drv) } } -void si5351c_clkout_enable(si5351c_driver_t* const drv, bool enable) +static void si5351c_clkout_ms_enable(si5351c_driver_t* const drv, bool enable) { drv->clk[drv->clkout_id].output_enable = enable; drv->clk[drv->clkout_id].power_down = !enable; @@ -351,12 +340,42 @@ void si5351c_clkout_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) { - /* MCU clock is shared with CLKOUT. */ - si5351c_clkout_enable(drv, enable); + 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); } #endif #ifdef IS_NOT_H1_R9 diff --git a/firmware/common/u128.c b/firmware/common/u128.c new file mode 100644 index 00000000..3543f332 --- /dev/null +++ b/firmware/common/u128.c @@ -0,0 +1,88 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * 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= divisor + rem.lo = tmp.lo; + rem.hi = tmp.hi; + quot.lo |= 1; + } + } + + return quot; +} diff --git a/firmware/common/u128.h b/firmware/common/u128.h new file mode 100644 index 00000000..c51dc5f5 --- /dev/null +++ b/firmware/common/u128.h @@ -0,0 +1,32 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * 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 + +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); diff --git a/firmware/fpga/build/praline_fpga.bin b/firmware/fpga/build/praline_fpga.bin index a6b02485..64dd9b4b 100644 Binary files a/firmware/fpga/build/praline_fpga.bin and b/firmware/fpga/build/praline_fpga.bin differ diff --git a/firmware/fpga/dsp/cic.py b/firmware/fpga/dsp/cic.py index fa0f3fa0..99f5376a 100644 --- a/firmware/fpga/dsp/cic.py +++ b/firmware/fpga/dsp/cic.py @@ -240,6 +240,7 @@ 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() @@ -255,7 +256,9 @@ 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) diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index dfc67d9e..8f6698da 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -203,6 +203,7 @@ 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") diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index ce24d879..813a9453 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include @@ -426,6 +428,8 @@ 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(); @@ -487,6 +491,9 @@ 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__); diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index 0da2cf6e..1bea5dd2 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -132,7 +132,7 @@ usb_request_status_t usb_vendor_request_init_sweep( radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, 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_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_RF, (sweep_freq + offset) * FP_ONE_HZ); nvic_enable_irq(NVIC_USB0_IRQ); diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index dbbbf661..e72a408b 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -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_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_BB_BANDWIDTH_TX, bandwidth); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, 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_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_RF, freq * FP_ONE_HZ); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_IF, RADIO_UNSET); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_LO, RADIO_UNSET); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, 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_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_IF, explicit_params.if_freq_hz * FP_ONE_HZ); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_FREQUENCY_LO, explicit_params.lo_freq_hz * FP_ONE_HZ); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, 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_ACTIVE, RADIO_SAMPLE_RATE, value); + radio_reg_write(&radio, RADIO_BANK_REQUESTED, 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_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_GAIN_TX_RF, endpoint->setup.value); radio_reg_write( &radio, - RADIO_BANK_ACTIVE, + RADIO_BANK_REQUESTED, 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_ACTIVE, RADIO_GAIN_RX_IF, gain); + radio_reg_write(&radio, RADIO_BANK_REQUESTED, 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_ACTIVE, RADIO_GAIN_RX_BB, gain); + radio_reg_write(&radio, RADIO_BANK_REQUESTED, 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_ACTIVE, RADIO_GAIN_TX_IF, gain); + radio_reg_write(&radio, RADIO_BANK_REQUESTED, 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_ACTIVE, + RADIO_BANK_REQUESTED, 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_ACTIVE, + RADIO_BANK_REQUESTED, RADIO_TRIGGER, endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index 005babb3..dde22e6e 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -28,7 +28,7 @@ #define USB_VENDOR_ID (0x1D50) -#define USB_API_VERSION (0x0112) +#define USB_API_VERSION (0x0113) #define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF) diff --git a/host/hackrf-tools/src/hackrf_debug.c b/host/hackrf-tools/src/hackrf_debug.c index 52ede9cb..5f8785b5 100644 --- a/host/hackrf-tools/src/hackrf_debug.c +++ b/host/hackrf-tools/src/hackrf_debug.c @@ -501,7 +501,7 @@ int radio_read_register( return result; } -#define RADIO_NUM_REGS (23) +#define RADIO_NUM_REGS (24) int radio_read_registers(hackrf_device* device, const uint8_t bank) { diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 7bafd5f3..4018ac63 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -57,6 +57,7 @@ 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. @@ -3496,7 +3497,7 @@ int ADDCALL hackrf_set_fpga_bitstream(hackrf_device* device, const uint8_t index 0, NULL, 0, - DEFAULT_REQUEST_TIMEOUT); + FPGA_BITSTREAM_TIMEOUT); if (result != 0) { last_libusb_error = result;