diff --git a/device_firmware/multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/VNA.xise b/device_firmware/multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/VNA.xise index 31ee3f6..5ce09d4 100644 --- a/device_firmware/multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/VNA.xise +++ b/device_firmware/multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/VNA.xise @@ -433,7 +433,7 @@ - + diff --git a/device_firmware/multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/top_master_per_point_switching.bin b/device_firmware/multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/top_master_per_point_switching.bin new file mode 100644 index 0000000..fb0d90b Binary files /dev/null and b/device_firmware/multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/top_master_per_point_switching.bin differ diff --git a/device_firmware/multi_libreVNA_firmware/reproduce/SHA256SUMS b/device_firmware/multi_libreVNA_firmware/reproduce/SHA256SUMS index 2d2ee91..aafc9e8 100644 --- a/device_firmware/multi_libreVNA_firmware/reproduce/SHA256SUMS +++ b/device_firmware/multi_libreVNA_firmware/reproduce/SHA256SUMS @@ -10,6 +10,7 @@ ba729999be7849187704cf4ccbc4ae4a8877b0860eee1b44225c0585537bb091 *extracted/mast 56654a0344556d24ecca10f119e0226b8297a2bb89d1aa4217280854e9ffa7bd *extracted/slave_stm32.bin # битстримы и прошивки MCU в дереве проекта c0e69bbcadd9fac20de10acbe1875ceba39cc7ce35a7c64ed54f44150560ef75 *LibreVNA/FPGA/VNA/top_master.bin +e184eb92bef1c698849511845fa8d7543287ed1e696c2b461520533112913113 *LibreVNA/FPGA/VNA/top_master_per_point_switching.bin 71f12345b1bed31f6a8b78e2eaa30503f26556cedcbff4615cd5a998953210d5 *LibreVNA/FPGA/VNA/top_external_lo_slave.bin 9e063008fabc872c8d534793851116373e6730cfbeaec17de18c1ba8bf7e9ca2 *LibreVNA/FPGA/VNA/top.bin ba729999be7849187704cf4ccbc4ae4a8877b0860eee1b44225c0585537bb091 *LibreVNA/Software/VNA_embedded/build/VNA_embedded.bin diff --git a/device_firmware/per_point_switching.md b/device_firmware/per_point_switching.md index acd0f8f..ceaf32b 100644 --- a/device_firmware/per_point_switching.md +++ b/device_firmware/per_point_switching.md @@ -39,51 +39,57 @@ | Биты | Поле | Значение в этом режиме | |---|---|---| | 0..2 | `stages` | 3 (то есть 4 стадии) | -| 3..5 | `port1Stage` | 0 — **индекс порта**, а не номер стадии | -| 6..8 | `port2Stage` | 1 | +| 3..5 | `port1Stage` | 0 — **первая стадия блока** порта 1 | +| 6..8 | `port2Stage` | 2 — первая стадия блока порта 2 | | 9..11 | `port3Stage` → ширина под-стадии | `log2(положений ключа)` = 1 | | 12..14 | `port4Stage` | не используется | | 15 | `unused2` | не используется | +Порт-стадия — это **первая стадия блока** порта, а не его индекс. Без под-стадий +блок вырождается в одну стадию, и значение совпадает с индексом, то есть со +стоковой раскладкой. + Поле `port3Stage` переиспользовано под ширину блока под-стадий: двухпортовая прошивка его игнорирует, поэтому формат пакета не меняется. **Ноль там — стоковая раскладка**, так что хост с `stimulus_switch_positions = 1` остаётся полностью совместим с немодифицированным прибором. -Слейвам обе порт-стадии выставляются в **7**. Стоковый приём «одна за последней -стадией» здесь не работает: при 4 стадиях старшие биты счётчика принимают только -значения 0 и 1, свободного значения не остаётся, и слейв начал бы излучать. +Слейвам обе порт-стадии выставляются в **7** — зарезервированное значение +«порт не излучает». Стоковый приём «одна за последней стадией» здесь не работает: +при 4 стадиях старшие биты счётчика принимают только значения 0 и 1, свободного +значения не остаётся, и слейв начал бы излучать. ## FPGA мастера -Сделано в [`multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/`](multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/), -осталось собрать битстрим в ISE. +Сделано и собрано в +[`multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/`](multi_libreVNA_firmware/reproduce/LibreVNA/FPGA/VNA/). ### `Sweep.vhd` Порт включался по точному совпадению, поэтому излучал ровно в одной стадии, и во второй под-стадии зондирующего сигнала просто не было. Сравнение вынесено в функцию `port_excites`: младшие `SUBSTAGE_BITS` бит счётчика стадий — положение -ключа, старшие — номер порта. Сдвиг на переменное число бит синтезируется в -трёхбитный barrel shifter. +ключа, и они **маскируются** из сравнения, а старшие биты выбирают порт. ```vhdl -if unsigned(port_stage) > shift_right(unsigned(last_stage), to_integer(unsigned(substage_width))) then +if port_stage = PORT_STAGE_DISABLED then -- "111", порт не излучает return false; end if; -return shift_right(stage, to_integer(unsigned(substage_width))) = unsigned(port_stage); +return (std_logic_vector(stage) and compare_mask) = (port_stage and compare_mask); ``` -Совместимость со стоком **строгая, а не приблизительная**. При `SUBSTAGE_BITS = 0` -сдвиг нулевой и условие вырождается в `stage_cnt = port_stage` плюс проверка -`port_stage <= STAGES`. Эта проверка не может ничего изменить: счётчик стадий -пробегает только `0..STAGES`, поэтому при `port_stage > STAGES` совпадения не было -бы и так. То есть для всех существующих вызовов поведение идентично — включая -ручной режим и анализатор спектра, где `STAGES = 0`. +Маска декодируется из `SUBSTAGE_BITS` тремя вентилями, причём нераспознанная +ширина даёт все единицы — чтобы неконфигурированный прибор никогда не выродился +в «каждый порт излучает в каждой стадии». -Значение выше последней порт-стадии никогда не совпадает — так хост глушит -излучение слейвов, посылая им 7. Старый хост слал «одну за последней», это тоже -глушит. +Совместимость со стоком **строгая, а не приблизительная**. При `SUBSTAGE_BITS = 0` +маска все единицы, и выражение становится буквально стоковым +`stage_cnt = port_stage`. Значит для всех существующих вызовов поведение +идентично — включая ручной режим и анализатор спектра, где `STAGES = 0`. + +Значение 7 зарезервировано под «порт выключен»: так хост глушит излучение +слейвов. Стоковый хост слал «одну за последней стадией», и это тоже не совпадает +ни с одной реальной стадией, так что новый битстрим работает и со старым хостом. ### `SPIConfig.vhd` и `top.vhd` @@ -101,6 +107,38 @@ when 6 => STAGES <= spi_buf_out(15 downto 13); В `top.vhd` добавлен сигнал `sweep_substage_bits` и проброшен от `SPIConfig` к `Sweep`. +### Почему маска, а не сдвиг + +Первая версия сравнивала `shift_right(stage_cnt, substage_bits)` с номером порта. +Функционально верно, но переменный сдвиг синтезируется в barrel shifter, а +вызывался он четырежды (по два на порт): **+142 LUT** при всего +3 регистрах. +Разводка поехала, и контрольная сборка показала нарушение hold на входе +`MCU_MOSI` — на шине, по которой MCU пишет регистры FPGA. Сбой там означал бы +неверную конфигурацию развёртки, то есть крайне неприятный в поиске отказ. + +Маска обходится несколькими вентилями и такого эффекта не даёт. + +### Cost table размещателя — обязательно + +Ограничение `MCU_MOSI OFFSET = IN ... BEFORE MCU_SCK` маргинально **уже в стоке**: +запас по hold всего +0.458 нс. Поэтому любое изменение нетлиста переставляет +размещение и может его опрокинуть. Проверено прямым A/B: сток даёт Timing Score 0, +первая версия правки — 1880, версия на маске при cost table 1 — 5480. + +Решается штатно, подбором cost table. В `VNA.xise` зафиксировано: + +```xml + +``` + +С этим значением **Timing Score 0**, а запас по hold на `MCU_MOSI` даже больше +стокового: +0.642 нс против +0.458. Ресурсы: 2302 LUT против 2189 у стока (40% +против 38% ёмкости xc6slx9). + +**Не сбрасывайте это свойство в 1.** Если после правок в VHDL тайминг снова +поедет — перебирайте cost table дальше (3, 4, 5…) и проверяйте `Timing Score` в +`top.par`; сборка каждого варианта около шести минут, синтез при этом кэширован. + ### Проверка Рядом лежит самопроверяющийся тестбенч `Test_Sweep.vhd` на четыре сценария: сток, @@ -112,8 +150,10 @@ when 6 => STAGES <= spi_buf_out(15 downto 13); ghdl -a --std=93 Sweep.vhd Test_Sweep.vhd && ghdl -r --std=93 Test_Sweep ``` -Тест проверен мутацией: если вернуть стоковое точное сравнение, сценарии с -под-стадиями падают, а стоковый продолжает проходить. +Тест проверен мутацией: если выбросить маску из сравнения, сценарии с +под-стадиями падают (8 сработавших проверок), а стоковый продолжает проходить. + +VHDL держите в чистом ASCII: GHDL отвергает не-ASCII символы даже в комментариях. ## Прошивка STM32 LibreVNA @@ -136,8 +176,6 @@ FPGA::SetupSweep(s.stages, s.port1Stage, s.port2Stage, s.syncMode != 0, ``` Собирать так же, как описано в [`reproduce/README.md`](multi_libreVNA_firmware/reproduce/README.md). -Отдельно: битстрим мастера единственный в своём роде, `top.bin` перезаписывается -каждой сборкой ISE — переименовывать сразу. ## Контроллер ключа (отдельный STM32) @@ -188,13 +226,36 @@ STM32 прибора в Hi-Z на обеих линиях (`GPIO_MODE_INPUT`, `N ### 1. Битстрим FPGA мастера (Xilinx ISE 14.7) +**Уже собран.** Лежит в `FPGA/VNA/top_master_per_point_switching.bin`, +sha256 `e184eb92bef1c698849511845fa8d754…`, Timing Score 0. Пересобирать нужно +только после правок в VHDL. + +Имя отдельное намеренно: `top_master.bin` оставлен нетронутым — это одна из двух +копий невосстановимого битстрима, из которого собран текущий `combined.vnafw` +(вторая — `extracted/master_fpga.bin`). Заменить его новым стоит только после +проверки на железе. + +Как собрать заново — в GUI: + 1. Скопировать [`multi_libreVNA_firmware/reproduce/LibreVNA/FPGA`](multi_libreVNA_firmware/reproduce/LibreVNA/FPGA) в общую папку виртуалки (`C:\shared\LibreVNA\FPGA`). 2. Открыть **`VNA.xise`** — проект мастера. Не `VNA_external_lo_slave.xise`. 3. Process → **Generate Programming File**. -4. Забрать `top.bin` **сразу** и переименовать в `top_master.bin`: оба проекта +4. Забрать `top.bin` **сразу** и переименовать в + `top_master_per_point_switching.bin`: оба проекта пишут в один и тот же файл, и следующая сборка его затрёт. Именно так был потерян исходный мастер-битстрим. +5. Проверить `Timing Score` в `top.par` — должен быть 0 (см. про cost table выше). + +Или из командной строки в госте, без GUI: + +```bash +cd ~/shared/LibreVNA/FPGA/VNA # лучше скопировать на локальный диск: vboxsf медленная +source /opt/Xilinx/14.7/ISE_DS/settings64.sh +printf 'project open VNA.xise\nprocess run "Generate Programming File"\nproject close\n' > /tmp/bit.tcl +xtclsh /tmp/bit.tcl +grep "Timing Score" top.par +``` Битстрим невоспроизводим побайтово (place & route недетерминирован плюс timestamp внутри), так что новый файл будет отличаться от старого при любом @@ -215,7 +276,7 @@ make BINPATH="<путь к arm-none-eabi из CubeIDE>/tools/bin" -j4 ```bash cd multi_libreVNA_firmware/reproduce/LibreVNA python3 AssembleFirmware.py \ - --bitstream FPGA/VNA/top_master.bin \ + --bitstream FPGA/VNA/top_master_per_point_switching.bin \ --mcu Software/VNA_embedded/build/VNA_embedded.bin \ --output ../combined_per_point_switching.vnafw ```