Автор: 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
...
end
end
Благодаря 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_id
analog_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
end
end
endmodule
Рис. 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
Автор: cyberflex
