Мост для ко-симуляции в Icarus Verilog и NGSpice

от автора

Автор: 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.

GTKWave и gnuplot — аналоговые и цифровые сигналы на одной оси

GTKWave и NGSpice / gnuplot — аналоговые и цифровые сигналы на одной оси

 

Рис. 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 уже проскочил момент события.

  • Только одно событие за окно.

  • Недетерминизм из-за эвристик.

gwave — аналоговый вьювер

gwave — аналоговый вьювер

 

Рис. 2 — gwave: просмотр аналоговых сигналов Sigma Delta модулятора (те же vbuck, vbucki, Qbar, что и в GTKWave).

Сравнительная таблица

Характеристика

Классическая

Альтернативная

Обнаружение аналоговых событий между тактами

❌ Нет

⚠️ Частично (с пропусками)

Инжекция событий из SPICE в Verilog

❌ Невозможно

✅ Да (через cbAfterDelay)

Производительность

✅ Предсказуемая

⚠️ Может взлететь (но maxstep помогает)

Детерминизм

✅ Полный

⚠️ Зависит от эвристик

Причинность DAC→аналог

✅ Корректна

❌ Нарушается во время фазы B

Поддержка множественных событий

❌ Нет

❌ Только одно за окно

Сложность кода

~2000 строк

~3500 строк + грамматика конфига

JavaElectric EDA — смешанная схема и SPICE нетлист

JavaElectric EDA — схема Sigma Delta Modulator и задание для SPICE симуляции нетлиста

 

Рис. 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/