Ограничение MCU_MOSI OFFSET hold маргинально уже в стоке: запас +0.458 нс. Поэтому любое изменение нетлиста переставляет размещение и может его опрокинуть. A/B в ISE: сток - Timing Score 0, версия на маске при cost table 1 - 5480. Cost table 2 даёт Timing Score 0, причём запас по hold на MCU_MOSI больше стокового: +0.642 против +0.458 нс. Значение зафиксировано в VNA.xise одной строкой, иначе следующая сборка снова словит нарушение. Ресурсы финальной сборки: 2302 LUT против 2189 у стока (40% против 38% ёмкости xc6slx9). Промежуточные 2182, которые я приводил раньше, были из сборки с cost table 1 - её перебор потом переделал, и упаковка map изменилась. Битстрим положен как top_master_per_point_switching.bin, а не поверх top_master.bin: последний - одна из двух копий невосстановимого битстрима, из которого собран текущий combined.vnafw. Документ приведён в согласованное состояние: таблица контракта и описание Sweep.vhd отставали от версии на маске. Собрано и проверено на машине сборки: ISE 14.7 в виртуалке, xtclsh в батч-режиме. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Воспроизведение прошивок LibreVNA
Здесь всё, что нужно, чтобы пересобрать прошивки из device_firmware/ с нуля.
Проект лежит целиком — не патч, а полное дерево с уже внесёнными доработками.
Исходники опережают бинарники. В дерево внесено переключение стимула внутри частотной точки (см.
../../per_point_switching.md), а лежащие рядом.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, к прошивкам
отношения не имеющая). Переключение стимула внутри точки добавило ещё 5
изменённых файлов и один новый. Всё остальное дерево совпадает с апстримом байт
в байт.
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.
Переключение стимула внутри частотной точки
Внесено позже остального, бинарников рядом ещё нет (см. блок в начале файла).
Полное описание — ../../per_point_switching.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, поле свободно, и его переиспользование не меняет формат
пакета.
При 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