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.
18 KiB
Воспроизведение прошивок 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.
- Скопировать
LibreVNA/FPGAвC:\shared\LibreVNA\FPGA. - В ISE открыть нужный проект:
- мастер —
/home/ise/shared/LibreVNA/FPGA/VNA/VNA.xise - slave —
.../VNA_external_lo_slave.xise
- мастер —
- Process → Generate Programming File.
- Результат —
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