added librevna firmware reproduce kit

Полное дерево LibreVNA v1.6.5 с доработками master/slave, эталонные битстримы
и прошивки STM32, упаковщик .vnafw. Проверено: build.sh пересобирает обе
прошивки побайтово идентично лежащим рядом .vnafw.

Мастер-битстрим (top_master.bin / extracted/master_fpga.bin) больше нигде не
существует — place&route в ISE недетерминирован, из исходников он не
восстанавливается.

Положено в multi_libreVNA_firmware/reproduce, а не в device_firmware/reproduce
как сказано в README комплекта: SHA256SUMS и build.sh ссылаются на
../combined.vnafw, а .vnafw лежат здесь.

Корневые правила .gitignore build*/build/ выбрасывали 482 файла, включая сам
build.sh и эталонные прошивки STM32 из SHA256SUMS — дописано разыгноривание.
Оставшиеся 7 файлов перебивал вложенный апстримный .gitignore (более глубокий
имеет приоритет), они добавлены force-add'ом. Промежуточные .o/.d/.su из
build-каталогов убраны, .bin/.elf/.hex/.map оставлены.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ayzen
2026-09-09 21:08:02 +03:00
co-authored by Claude Opus 5
parent 7997abe2d9
commit d2338ff10c
1241 changed files with 645541 additions and 1 deletions
@@ -0,0 +1,118 @@
#!/usr/bin/env python3
import argparse
import binascii
from pathlib import Path
DEFAULT_FPGA_BITSTREAM = "FPGA/VNA/top.bin"
DEFAULT_OUTPUT = "combined.vnafw"
DEFAULT_MCU_CANDIDATES = [
"Software/VNA_embedded/Debug/VNA_embedded.bin",
"Software/VNA_embedded/Release/VNA_embedded.bin",
"Software/VNA_embedded/build/VNA_embedded.bin",
]
HEADER_SIZE = 24
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Assemble a combined LibreVNA firmware image.",
)
parser.add_argument(
"--bitstream",
default=DEFAULT_FPGA_BITSTREAM,
help=f"FPGA bitstream to package (default: {DEFAULT_FPGA_BITSTREAM})",
)
parser.add_argument(
"--output",
default=DEFAULT_OUTPUT,
help=f"Output firmware image path (default: {DEFAULT_OUTPUT})",
)
parser.add_argument(
"--mcu",
help="Explicit MCU firmware .bin path. If omitted, the newest known build is used.",
)
return parser.parse_args()
def select_mcu_firmware(explicit_path: str | None) -> Path:
if explicit_path is not None:
path = Path(explicit_path)
if not path.is_file():
raise FileNotFoundError(f"MCU firmware not found: {path}")
return path
latest_modification = 0.0
newest_mcu: Path | None = None
for candidate in DEFAULT_MCU_CANDIDATES:
path = Path(candidate)
if not path.is_file():
continue
modified_time = path.stat().st_mtime
if modified_time >= latest_modification:
latest_modification = modified_time
newest_mcu = path
if newest_mcu is None:
raise FileNotFoundError("Couldn't find MCU firmware file")
return newest_mcu
def crc32_from_file(filename: Path, initial_crc: int) -> int:
buffer = filename.read_bytes()
return binascii.crc32(buffer, initial_crc) & 0xFFFFFFFF
def main() -> int:
args = parse_args()
bitstream_path = Path(args.bitstream)
if not bitstream_path.is_file():
raise FileNotFoundError(f"FPGA bitstream not found: {bitstream_path}")
mcu_firmware_path = select_mcu_firmware(args.mcu)
output_path = Path(args.output)
print(f"Using {mcu_firmware_path} as MCU firmware")
size_fpga = bitstream_path.stat().st_size
size_mcu = mcu_firmware_path.stat().st_size
print(f"Got FPGA bitstream of size {size_fpga}")
print(f"Got MCU firmware of size {size_mcu}")
crc = crc32_from_file(bitstream_path, 0xFFFFFFFF)
crc = crc32_from_file(mcu_firmware_path, crc)
print(f"Calculating CRC...:{hex(crc)}")
output_path.parent.mkdir(parents=True, exist_ok=True)
with output_path.open("wb") as firmware_image:
firmware_image.write(b"VNA!")
firmware_image.write((HEADER_SIZE).to_bytes(4, byteorder="little"))
firmware_image.write(size_fpga.to_bytes(4, byteorder="little"))
firmware_image.write((HEADER_SIZE + size_fpga).to_bytes(4, byteorder="little"))
firmware_image.write(size_mcu.to_bytes(4, byteorder="little"))
firmware_image.write(crc.to_bytes(4, byteorder="little"))
if firmware_image.tell() != HEADER_SIZE:
raise RuntimeError(
"Incorrect header size "
f"(defined as {HEADER_SIZE} but actual header is {firmware_image.tell()})"
)
firmware_image.write(bitstream_path.read_bytes())
firmware_image.write(mcu_firmware_path.read_bytes())
if firmware_image.tell() % 256 != 0:
padding = 256 - firmware_image.tell() % 256
firmware_image.write(bytearray(padding))
final_size = firmware_image.tell()
print(f"Created combined firmware file at {output_path} with size {final_size}")
return 0
if __name__ == "__main__":
raise SystemExit(main())