Files
Ayzen 62c0266a6b per-point switching: hardware bring-up, profiles, protocol doc
Verified on the bench that the modified master bitstream excites in every stage
and the RF switch routes: 16 traces per sweep, 62 dB between the two switch
positions of one master port, 9.9k measurements/s. Adds the built master image,
two run profiles, a verification script and librevna_protocol.md, and drops the
stale prototype driver copy that the firmware tree still carried.
2026-09-15 14:41:54 +03:00

18 KiB
Raw Permalink Blame History

Воспроизведение прошивок LibreVNA

Здесь всё, что нужно, чтобы пересобрать прошивки из device_firmware/ с нуля. Проект лежит целиком — не патч, а полное дерево с уже внесёнными доработками.

Исходники опережают бинарники. В дерево внесено переключение стимула внутри частотной точки (см. ../../per_point_switching.md). Битстрим и готовый образ с доработкой уже собраны: FPGA/VNA/top_master_per_point_switching.bin и ../combined_per_point_switching.vnafw. На железе они ещё не проверены. А combined.vnafw, extracted/ и build/ — это ПРЕДЫДУЩАЯ прошивка, собранная до доработки и сейчас залитая в приборы, то есть путь отката. Поэтому:

  • ./build.sh без аргументов по-прежнему даёт побайтовое совпадение — он только переупаковывает готовые .bin, не трогая исходники;
  • ./build.sh --stm32 и любая сборка FPGA дадут другие бинарники, уже с новой логикой — это ожидаемо, а не поломка;
  • исходники, из которых собраны текущие .vnafw, лежат в git-коммите d2338ff.

После сборки и проверки на железе обновите .vnafw, extracted/, build*/ и SHA256SUMS, и этот блок можно будет убрать.

Что где

reproduce/
├── LibreVNA/            полный проект: апстрим v1.6.5 + доработки master/slave
│   ├── Software/VNA_embedded/       прошивка STM32G431CBUx (CubeIDE + Makefile)
│   │   ├── build/                   собранный мастер (VNA_embedded.bin/.elf/.map)
│   │   └── build_external_lo_slave/ собранный slave
│   ├── FPGA/VNA/                    VHDL-проект под Xilinx ISE 14.7
│   │   ├── top_master.bin           битстрим мастера  ← из combined.vnafw
│   │   ├── top_external_lo_slave.bin битстрим slave
│   │   └── top.bin                  СТОКОВЫЙ апстримный, не наш (см. ниже)
│   └── AssembleFirmware.py          упаковщик .vnafw
├── extracted/           секции, вынутые из готовых .vnafw
├── build.sh             пересборка + сверка с эталоном
└── SHA256SUMS           эталонные хеши (sha256sum -c SHA256SUMS)

Из LibreVNA/ исключены только .git (снапшот с одним коммитом init, истории правок в нём нет) и .venv.

Железо и версии

MCU STM32G431CBUx, Cortex-M4, 128 КБ флеш / 32 КБ RAM
Линкер STM32G431CBUX_FLASH.ld, образ с 0x08000000
FPGA Xilinx, битстрим 341 712 Б, проект под ISE 14.7
Версия ПО FW 1.6.5, HW_REVISION='B' (задано в Makefile)
Тулчейн STM32 STM32CubeIDE 1.17.0, gcc arm-none-eabi 12.3.1
Апстрим github.com/jankae/LibreVNA, тег v1.6.5

Две сборки

Различие мастер/slave — один флаг, отдельных веток кода нет.

STM32 FPGA
мастер make (EXTERNAL_MASTER_LO_SLAVE=0) проект VNA.xise → BuildConfig.vhd (false)
slave make EXTERNAL_MASTER_LO_SLAVE=1 VNA_external_lo_slave.xise → BuildConfig_external_lo_slave.vhd (true)

Два .xise отличаются ровно одной строкой — каким из двух BuildConfig подключаться.

Формат .vnafw

Заголовок 24 байта, little-endian (см. AssembleFirmware.py):

Смещение Поле
0x00 магия VNA!
0x04 смещение FPGA-битстрима (всегда 24)
0x08 размер FPGA-битстрима
0x0C смещение прошивки MCU (= 24 + размер FPGA)
0x10 размер прошивки MCU
0x14 CRC32 по (битстрим + прошивка MCU), начальное значение 0xFFFFFFFF

Дальше подряд битстрим и прошивка MCU, хвост добит нулями до кратности 256.

Разложить готовый файл на части:

tail -c +25     combined.vnafw | head -c 341712 > top.bin            # FPGA
tail -c +341737 combined.vnafw | head -c 85004  > VNA_embedded.bin   # STM32

Отличия от апстрима v1.6.5

Аппаратный триггер и external-LO slave — 13 файлов (не 12: в списке не значился PC_Application/LibreVNA-GUI/Traces/trace.cpp, правка в GUI, к прошивкам отношения не имеющая). Переключение стимула внутри точки добавило ещё 7 изменённых файлов и 2 новых (git diff --stat d2338ff HEAD -- reproduce/LibreVNA/): FPGA/VNA/{Sweep,SPIConfig,top}.vhd, FPGA/VNA/VNA.xise, Application/Drivers/FPGA/FPGA.{hpp,cpp}, Application/VNA.cpp; новые — FPGA/VNA/Test_Sweep.vhd и FPGA/VNA/top_master_per_point_switching.bin.

Ещё из дерева удалён каталог Python/ — прототип драйвера multi-device, который переехал в python_app/hardware_full/librevna_multi_device_driver/. К сборке прошивок он отношения не имел; разбор протокола из него сохранён как ../../librevna_protocol.md.

Всё остальное дерево совпадает с апстримом байт в байт.

STM32 — поддержка аппаратного триггера (это и есть съём без USB)

Application/Trigger.cpp (+45/−3) — главное изменение, работает в обеих сборках. В апстриме Trigger::Init() только вешал EXTI на FPGA_TRIGGER_OUT, а FPGA_TRIGGER_IN всегда оставался выходом STM (триггер эмулировался по USB). Добавлены ConfigureInputPin() / ConfigureOutputMonitorPin(), которые в режиме Mode::Trigger (syncMode = 3) убирают STM с обеих линий:

  • FPGA_TRIGGER_IN → вход без подтяжки: линию гонит внешняя триггер-петля, а не STM;
  • FPGA_TRIGGER_OUT → вход без подтяжки, EXTI снимается: FPGA драйвит линию напрямую на второе устройство.

В остальных режимах поведение прежнее. Конфигурация пинов теперь вызывается и из Trigger::SetMode(), а не только при инициализации.

Application/Hardware.cpp (+29/−2) — в HW::SetIdle() вместо Trigger::SetInput(false) теперь Trigger::SetMode(Trigger::Mode::Off) (безусловно, обе сборки). Остальное под #if EXTERNAL_MASTER_LO_SLAVE: гасится Si5351-канал LO1, LO1 не инициализируется как PLL, статус LO1 всегда «locked», температура LO1 = 0.

Application/VNA.cpp (+33/−6) — вынесены UpdateLO1ReferenceClock() / DisableLO1ReferenceClock(); в slave-сборке VNA::Setup() отклоняет свип, если syncMode != Trigger или syncMaster != 0. Плюс безусловный багфикс: клампы points и dwell_time применялись к старому settings вместо нового s.

Application/App.cpp (+28/−1) — только под #if EXTERNAL_MASTER_LO_SLAVE: slave отвечает Nack на ManualControl, Generator, SpectrumAnalyzerSettings и на свипы, не проходящие ExternalLOSlaveSweepSupported(). Также SweepSettings теперь отвечает Nack, если VNA::Setup() вернул false.

Application/Hardware.hpp (+3) — дефолт EXTERNAL_MASTER_LO_SLAVE 0. Makefile (+2) — EXTERNAL_MASTER_LO_SLAVE ?= 0 и проброс в -D.

FPGA — глушение LO1 в slave

FPGA/VNA/top.vhd (+14/−8) — подключён use work.BuildConfig.all, заведены сигналы SPI_LO1_CE / SPI_LO1_RF_EN, и под EXTERNAL_MASTER_LO_SLAVE глушатся LO1_RF_EN, LO1_CE, LO1_CLK, LO1_MOSI, LO1_LE; plls_locked перестаёт ждать захвата LO, lo_unlocked фиксируется в '0'.

Все правки — под этим флагом. При false логика полностью совпадает с апстримом.

Новые файлы: BuildConfig.vhd, BuildConfig_external_lo_slave.vhd, VNA_external_lo_slave.xise; в VNA.xise добавлена ссылка на BuildConfig.vhd.

Переключение стимула внутри частотной точки

Внесено позже остального. В репозитории лежит и битстрим (FPGA/VNA/top_master_per_point_switching.bin), и готовый образ ../combined_per_point_switching.vnafw — оба с хешами в SHA256SUMS.

Образ собран локально на Linux тулчейном arm-none-eabi-gcc 14.2.1, а не CubeIDE 1.17.0 / gcc 12.3.1, которым собран текущий рабочий combined.vnafw. Логика та же (исходники те же), но байты MCU-секции отличаются: 88 660 против 85 044. Пересобрать этим же способом:

cd LibreVNA/Software/VNA_embedded && make BUILD_DIR=build_per_point_switching
cd .. && python3 AssembleFirmware.py \
    --bitstream FPGA/VNA/top_master_per_point_switching.bin \
    --mcu Software/VNA_embedded/build_per_point_switching/VNA_embedded.bin \
    --output ../combined_per_point_switching.vnafw

BUILD_DIR обязательно отдельный: в build/ лежит эталонный VNA_embedded.bin, извлечённый из рабочей прошивки, его перезаписывать нельзя.

Полное описание — ../../per_point_switching.md, базовый протокол — ../../librevna_protocol.md.

FPGA/VNA/Sweep.vhd — порт излучает не в одной стадии, а в БЛОКЕ подряд идущих: младшие SUBSTAGE_BITS бит счётчика стадий стали положением выходного ключа, старшие выбирают порт. Вынесено в функцию port_excites. Значение порт-стадии выше последней никогда не совпадает — так хост глушит излучение слейвов, посылая им 7.

FPGA/VNA/SPIConfig.vhd, FPGA/VNA/top.vhd — новый вход SUBSTAGE_BITS из свободных битов 8:6 регистра SweepSetup (адрес 6) и его проброс в Sweep.

Application/Drivers/FPGA/FPGA.{hpp,cpp} — у SetupSweep добавлен параметр substage_bits со значением по умолчанию 0, чтобы вызовы из ручного режима и анализатора спектра не менялись.

Application/VNA.cpp — ширина под-стадии берётся из port3Stage: у этого железа нет порта 3, поле свободно, и его переиспользование не меняет формат пакета.

FPGA/VNA/VNA.xise — одна строка: Starting Placer Cost Table (1-100) Map spartan6 переведена с 1 на 2. Не сбрасывать. Ограничение MCU_MOSI OFFSET hold маргинально уже в стоке (запас +0.458 нс), поэтому любое изменение нетлиста может его опрокинуть; с cost table 2 получается Timing Score 0 и запас +0.642 нс, то есть больше стокового. Подробности и цифры ресурсов — в ../../per_point_switching.md.

При SUBSTAGE_BITS = 0 логика бит в бит совпадает с апстримом — это проверено симуляцией.

Новый файл FPGA/VNA/Test_Sweep.vhd — самопроверяющийся тестбенч на четыре сценария (сток, два положения ключа, приёмник без излучения, четыре положения). В проект ISE не добавлен, на сборку не влияет. Запуск любым VHDL-симулятором:

ghdl -a --std=93 Sweep.vhd Test_Sweep.vhd && ghdl -r --std=93 Test_Sweep

Прочее

AssembleFirmware.py — переписан: добавлены --bitstream, --mcu, --output (в апстриме пути зашиты жёстко). Формат выходного файла не менялся.

Пересборка

Только упаковка .vnafw — самое быстрое

cd device_firmware/reproduce
./build.sh

Берёт готовые .bin из LibreVNA/ и собирает обе прошивки в out/, затем сверяет с эталонами. Даёт побайтовое совпадение с device_firmware/*.vnafw — проверено.

STM32 из исходников

./build.sh --stm32

или вручную:

cd LibreVNA/Software/VNA_embedded
BIN='C:/ST/STM32CubeIDE_1.17.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.12.3.rel1.win32_1.1.0.202410251130/tools/bin'
make BINPATH="$BIN" -j4                                                    # мастер
make BINPATH="$BIN" -j4 EXTERNAL_MASTER_LO_SLAVE=1 BUILD_DIR=build_external_lo_slave

BINPATH обязателен: по умолчанию в Makefile стоит /usr/bin, под Windows это не сработает. make.exe берите из CubeIDE (...externaltools.make.win32.../tools/bin).

FPGA — только вручную, в Xilinx ISE 14.7

ISE стоит в виртуалке VirtualBox ISE_14.7_VIRTUAL_MACHINE; её общая папка — хостовая C:\shared, внутри гостя это /home/ise/shared.

  1. Скопировать LibreVNA/FPGA в C:\shared\LibreVNA\FPGA.
  2. В ISE открыть нужный проект:
    • мастер — /home/ise/shared/LibreVNA/FPGA/VNA/VNA.xise
    • slave — .../VNA_external_lo_slave.xise
  3. Process → Generate Programming File.
  4. Результат — top.bit и top.bin в каталоге проекта.

Оба проекта пишут в один и тот же top.bin, поэтому последняя сборка затирает предыдущую — забирайте файл сразу и переименовывайте (top_master.bin / top_external_lo_slave.bin). Именно так и потерялся исходный мастер-битстрим: slave-сборка от 20.04 перезаписала его.

Что воспроизводится побайтово

.vnafw из готовых .bin да — проверено, обе прошивки совпали с device_firmware/
STM32 из исходников да — проверено, обе сборки (мастер ba729999…, slave 56654a03…) совпали побайтово на тулчейне CubeIDE 1.17.0 / gcc 12.3.1
FPGA-битстрим из ISE нет — place & route недетерминирован, плюс timestamp внутри битстрима. Логика идентична, файл будет другой

Поэтому мастер-битстрим сохранён отдельно: extracted/master_fpga.bin и LibreVNA/FPGA/VNA/top_master.bin. Больше нигде на машине его нет — он существовал только внутри combined.vnafw.

Внимание: LibreVNA/FPGA/VNA/top.bin — это стоковый апстримный битстрим (тот же, что в официальном релизе v1.6.5), а не наш мастер. Не перепутайте при упаковке.

Откуда что собрано

.vnafw FPGA-битстрим STM32
combined.vnafw top_master.bin (только из этого файла) build/VNA_embedded.bin
combined_external_lo_slave.vnafw top_external_lo_slave.bin build_external_lo_slave/VNA_embedded.bin

Прошивка устройства

Штатным LibreVNA-GUI: Device → Firmware Update, скармливать .vnafw целиком — GUI сам разложит битстрим и прошивку MCU. Отдельный master_stm32.bin нужен, только если шить STM напрямую через ST-Link/DFU на 0x08000000.

Проверка целостности

cd device_firmware/reproduce
sha256sum -c SHA256SUMS