Автор: Cyberflex (по мотивам реальной разработки «MixFighter»)
Как мы сделали мост между Icarus Verilog и NGSpice: две разных реализации архитектуры, их недостатки и почему идеальное решения пока не достигнуто
Что такое событийная и непрерывная симуляция?
В мире симуляции электронных систем существуют два принципиально разных подхода.
Событийная симуляция (event‑driven) используется для цифровых схем. Логические элементы меняют своё состояние только в моменты, когда на их входах происходят изменения – фронты тактового сигнала, переключения триггеров, задержки вентилей. Между этими событиями ничего не происходит, симулятор «спит» и не тратит ресурсы. Именно так работают Icarus Verilog, Modelsim, VCS и другие цифровые симуляторы. Это быстро и эффективно, потому что цифровые сигналы имеют лишь два (или несколько дискретных) состояния.
Непрерывная симуляция (или псевдонепрерывная) используется для аналоговых схем. Ток через транзистор, напряжение на конденсаторе, фаза генератора – всё это меняется плавно по законам дифференциальных уравнений. Симулятор (SPICE, NGSpice, Spectre) разбивает время на очень маленькие шаги, решает системы уравнений на каждом шаге и выдаёт результаты в виде кривых. Точность требует мельчайших шагов, особенно при резких фронтах или обратных связях. Это медленно, но другого способа получить «почти как в жизни» аналоговое поведение пока нет.
Проблема смешанного моделирования
Порой возникает задача совместной симуляции (ко‑симуляции) взаимодействующих аналоговой и цифровой систем. Представьте, что вы разрабатываете SAR ADC. Аналоговая часть – компаратор, ЦАП на конденсаторах, входной буфер – требует SPICE‑моделирования с точностью до микровольт и пикосекунд. Цифровая часть – управляющая логика, калибровка, интерфейс – прекрасно считается в Icarus Verilog. Как заставить их работать вместе?
Можно использовать готовые коммерческие решатели (Cadence, Synopsys), но они стоят дорого и не всегда доступны. А можно написать свой мост – VPI‑модуль, который свяжет Icarus и NGSpice через общую память и семафоры. Именно этим я и занялся.
Эту задачу можно решить, запрягши два разных симулятора в одну упряжку, управляя ими попеременно и обмениваясь данными между ними. Вот мы взяли пару таких стандартных симуляторов – Icarus Verilog и NGSpice – и сделали управляемый мост.
В процессе родилось две реализации:
-
«Классическая» – простая, синхронная, где Verilog явно запрашивает аналоговые данные («digital oversampling»).
-
«Альтернативная» – с упреждающим анализом (lookahead) и инжекцией событий из SPICE в Verilog.
Казалось бы, вторая – эволюция первой. Но на деле у каждой оказались фундаментальные архитектурные недостатки, которые невозможно исправить косметическими патчами.
В этой статье я разберу обе реализации, их компромиссы и покажу, почему идеального open‑source решения для смешанного моделирования пока не существует.
Спойлер: несмотря на все недостатки, инструмент уже сейчас вполне пригоден для реальных задач – от формальной верификации цифровых блоков в обвязке с аналоговыми IP до подсчёта импульсов сигма‑дельта ADC.
Рис. 1 — GTKWave (цифровые + аналоговые) и встроенный gnuplot NGSpice. Совмещённые временные шкалы, те же сигналы.
Как устроен мост: общая архитектура
Обе реализации базируются на одном и том же наборе технологий: VPI (Verilog Procedural Interface) Icarus, libngspice.so, пайповые семафоры, отдельный поток NGSpice. Схема работы (упрощённо):
Icarus (главный поток) NGSpice (поток) | | $spice_sync() (или $get_...) | | | state.target = t_now | | | wait(analog_done) <----------------+ post(analog_done) | | обновить real-переменные | применить alter (DAC) | | | post(digital_done) -------------> wait(digital_done) | | вернуть управление | | | (продолжает моделирование)
Проблема в том, что SPICE выдаёт результаты только в моменты своего внутреннего временного шага. А Verilog живёт событиями, которые могут быть в любые моменты времени.
Важное упрощение для тестбенча: $spice_sync()
В обеих реализациях присутствует системная задача $spice_sync(). Её вызов (например, по каждому фронту быстрого тактового сигнала) выполняет полный цикл обмена: передаёт текущие цифровые значения в SPICE (через alter), запрашивает свежие аналоговые напряжения, обновляет real-переменные в Verilog. При этом не нужно вызывать $get_analog_voltage() для каждого сигнала вручную.
always @(posedge clk_fast) begin $spice_sync(); // синхронизация и обновление всех expose-переменных if (tb.Vcmp > vref_th) begin // digital reaction on event ... endend
Благодаря expose_analog_N, real-переменные автоматически обновляются и видны в GTKWave на одной временной оси с цифровыми сигналами.
Моделирование задержек и фронтов
Часто в смешанных схемах важны не только статические уровни, но и время установления цифрового сигнала и скорость нарастания аналогового фронта. В обеих реализациях это легко моделируется с помощью «обёрточных» объектов на стороне Verilog и SPICE.
Классическая реализация: синхронный опрос
Каждый раз, когда Verilog нужны свежие аналоговые данные, мост узнаёт текущее время Icarus, блокируется на семафоре, SPICE считает до этого времени и возвращает значения.
Плюсы: простота, детерминизм, предсказуемая производительность.
Минусы (главный недостаток): аналоговые события, произошедшие между тактами Verilog, полностью игнорируются. Пример: компаратор переключается в момент 5.2 нс, а следующий положительный фронт тактового сигнала — в 10.0 нс. Verilog узнает об этом только в 10 нс, что ломает логику обратной связи в SAR ADC, DC-DC преобразователях. Приходится оверсэмплить обмен данными между двумя симуляторами повшая частоту срабатывания системной задачи clk_fast.
Альтернативная реализация: упреждающий анализ (lookahead)
Идея: разрешить SPICE бежать вперёд на небольшой интервал, следить за аналоговыми триггерами (пороги, окна, длительности, пики) и, если что-то произошло, остановиться и вызвать Verilog раньше. В конфиг‑файле mixed_bridge.cfg появляются строки вида:
analog_event_0 = threshold outp rising 1.2 | time_var=tb.analog_event_time id_var=tb.analog_event_idanalog_event_1 = duration outp above 0.8 for 10ns energy 1e-12
Плюсы: SPICE может инициировать событие, точность в идеальных условиях, гибкость.
Недостатки (фундаментальные):
-
Sentinel-ы проверяются только на шагах SPICE — возможен пропуск события между шагами.
-
Эвристика длины окна может провалиться (слишком мало — опоздание, слишком много — падение производительности).
-
Нарушение причинности для ЦАП: alter-команда выполнится только в следующей фазе A, после того как SPICE уже проскочил момент события.
-
Только одно событие за окно.
-
Недетерминизм из-за эвристик.
Рис. 2 — gwave: просмотр аналоговых сигналов Sigma Delta модулятора (те же vbuck, vbucki, Qbar, что и в GTKWave).
Сравнительная таблица
|
Характеристика |
Классическая |
Альтернативная |
|---|---|---|
|
Обнаружение аналоговых событий между тактами |
❌ Нет |
⚠️ Частично (с пропусками) |
|
Инжекция событий из SPICE в Verilog |
❌ Невозможно |
✅ Да (через cbAfterDelay) |
|
Производительность |
✅ Предсказуемая |
⚠️ Может взлететь (но maxstep помогает) |
|
Детерминизм |
✅ Полный |
⚠️ Зависит от эвристик |
|
Причинность DAC→аналог |
✅ Корректна |
❌ Нарушается во время фазы B |
|
Поддержка множественных событий |
❌ Нет |
❌ Только одно за окно |
|
Сложность кода |
~2000 строк |
~3500 строк + грамматика конфига |
Рис. 3 — JavaElectric EDA: принципиальная схема смешанного устройства (Sigma Delta modulator ADC + цифровой счётчик) и фрагмент SPICE нетлиста с описанием аналоговых событий.
Несмотря на всё: это уже работает и приносит пользу
Пример: сигма-дельта модулятор с цифровым счётчиком импульсов. Аналоговая часть выдаёт короткие импульсы с переменным (в следствии изименения входного сигналя) шагом. Цифровой счётчик должен считать каждый импульс, даже если он приходит в произвольный момент между фронтами тактового сигнала. Классическая реализация с вызовом $spice_sync() по каждому фронту быстрого такта (например, 500 МГц) вполне способна задетектировать импульс, если его длительность больше периода такта. Альтернативная реализация позволяет ещё точнее фиксировать момент прихода импульса и может работать с более редкими вызовами $spice_sync(), экономя ресурсы.
В обоих случаях разработчик получает возможность наблюдать аналоговые сигналы в GTKWave на одной временной оси с цифровыми сигналами, моделировать задержки и фронты через обёрточные объекты, проводить формальную верификацию цифровой логики в связке с реальными аналоговыми IP.
module DSMCounter( input i_clk, input i_rst, input i_Qbar, output reg[31:0] o_cntQbar, output reg o_ready);parameter CNT_CLK = 22;reg [31:0] cnt;reg [31:0] cntQbar;reg [31:0] cntTotal;always @(posedge i_clk) begin if(i_rst) begin cnt <= 0; cntQbar <= 0; o_cntQbar <= 0; o_ready <= 0; cntTotal <= 0; end else begin o_ready <= 0; cntTotal <= cntTotal + 1; if(i_Qbar == 1) begin cntQbar <= cntQbar + 1; end if(cnt == CNT_CLK-1) begin o_cntQbar <= cntQbar; o_ready <= 1; cnt <= 0; cntQbar <= 0; end else begin cnt <= cnt + 1; end endendendmodule
Рис. 4 — цифровая часть: счётчик импульсов на Verilog (модуль DSMCounter), интегрируемый в смешанную симуляцию через VPI мост.
Что дальше? Перспективы улучшения
Я планирую добавить диагностику недостоверных результатов, реализовать очередь событий вместо одного флага, исследовать cbNextSimTime в Icarus, интегрировать ограничение maxstep прямо в конфиг моста. Возможно, когда-нибудь NGSpice обзаведётся полноценным событийным API, и тогда мост можно будет переписать «правильно».
P.S. Где взять код? Обе реализации лежат в открытом доступе: классическая — IcarusNGSpice/, альтернативная — IcarusNGSpice.alternative/. Конфигурация через mixed_bridge.cfg. Для компиляции нужна shared-библиотека NGSpice (./configure —with-ngshared). Используйте на свой страх и риск, но, как показала практика, для многих задач его уже достаточно.
Если у вас есть идеи, как обойти описанные ограничения при текущих возможностях API симуляторов — Welcome to «issues». Успешных симуляций!
© CyberFlex, 2025 — открытый мост для смешанного моделирования Icarus Verilog ⇄ NGSpice
ссылка на оригинал статьи https://habr.com/ru/articles/1023270/