DocsTech
/
ICARUS VERILOG
/

~ cd 13. использование vpi

Icarus Verilog реализует часть API PLI 2.0 в Verilog. Это позволяет программистам писать код на языке C, взаимодействующий с симуляторами Verilog, для выполнения задач, которые иначе невозможно решить с помощью прямого Verilog. Многие разработчики Verilog, особенно те, кто использует Verilog только как инструмент синтеза, могут смело игнорировать весь вопрос PLI (и эту главу), но разработчики, которые хотят взаимодействовать с внешним миром при моделировании, не могут обойтись без VPI.

Остальная часть этой статьи предполагает наличие некоторых знаний о программировании на языке Си, ПЛИС Verilog и компиляторе, установленном в вашей системе. В большинстве случаев Icarus Verilog предполагает, что используемый вами компилятор — GNU Compilation System, поэтому советы и инструкции, приведенные ниже, отражают это. Если вы не программист на Си или не планируете использовать модули VPI, можете пропустить всю эту статью. Внизу есть ссылки для получения информации по более общим темам.

13.1. Как это работает

Модули VPI — это скомпилированный загружаемый объектный код, который время выполнения загружает по запросу пользователя. Пользователь дает команду vvp на поиск и загрузку модулей с помощью ключа «-m». Например, чтобы загрузить модуль «sample.vpi»:
...
Копировать
% vvp -m sample foo.vvp

Время выполнения vvp загружает модули сначала, до выполнения симуляции или даже до компиляции кода vvp. Часть загрузки включает вызов процедур инициализации. Эти процедуры регистрируют во времени выполнения все системные задачи и функции, которые реализует модуль. Как только это сделано, загрузчик времени выполнения может сопоставить имена вызываемых системных задач проекта с их реализациями в модулях VPI.

(Существует специальный модуль system.vpi, который всегда загружается для обеспечения основных системных задач).

Время выполнения симулятора (программа «vvp») получает информацию о свежезагруженном модуле, ища в нем символ «vlog_startup_routines». Эта таблица, предоставленная автором модуля и скомпилированная в модуль, представляет собой таблицу указателей функций с нулевым окончанием. Симулятор вызывает каждую из функций в таблице по порядку. Следующее простое определение на языке C определяет пример таблицы:
...
Копировать
void (*vlog_startup_routines[])() = {
   hello_register,
   0
};

Обратите внимание, что таблица «vlog_startup_routines» представляет собой массив указателей функций, последний из которых равен 0, чтобы отметить конец. При желании программист может организовать модуль таким образом, чтобы включить в эту таблицу множество стартовых функций.

Задача функций запуска, собранных в таблице запуска, — объявить системные задачи и функции, которые предоставляет модуль. Модуль может реализовать столько задач/функций, сколько пожелает, поэтому модуль можно с полным правом назвать библиотекой системных задач и функций.

13.2. Компиляция модулей VPI

Чтобы скомпилировать и связать VPI-модуль для использования с помощью Icarus Verilog, вы должны скомпилировать все исходные файлы модуля, как если бы вы компилировали DLL или разделяемый объект. В gcc под Linux это означает компиляцию с флагом «-fpic». Затем модуль компонуется вместе с библиотекой vpi следующим образом:
...
Копировать
% gcc -c -fpic hello.c
% gcc -shared -o hello.vpi hello.o -lvpi
Это предполагает, что заголовочный файл «vpi_user.h и библиотечный файл libvpi.a установлены в вашей системе, чтобы gcc мог их найти. Обычно так и происходит в системах Linux и UNIX. Более простой и предпочтительный метод, который работает во всех поддерживаемых системах, заключается в использовании одной команды:
...
Копировать
% iverilog-vpi hello.c

Команда «iverilog-vpi» принимает в качестве аргументов исходные файлы вашего модуля VPI, компилирует их с использованием соответствующих флагов компилятора и компонует их в модуль vpi с любыми библиотеками и флагами компоновщика, необходимыми для конкретной системы. Эта простая команда создает модуль «hello.vpi» с минимальными затратами.

13.3. Пример

Давайте попробуем рассмотреть полный, рабочий пример. Поместите следующий код на языке C в файл hello.c:
...
Копировать
# include  <vpi_user.h>

static int hello_compiletf(char*user_data)
{
      return 0;
}

static int hello_calltf(char*user_data)
{
      vpi_printf("Hello, World!\n");
      return 0;
}

void hello_register()
{
      s_vpi_systf_data tf_data;

      tf_data.type      = vpiSysTask;
      tf_data.tfname    = "$hello";
      tf_data.calltf    = hello_calltf;
      tf_data.compiletf = hello_compiletf;
      tf_data.sizetf    = 0;
      tf_data.user_data = 0;
      vpi_register_systf(&tf_data);
}

void (*vlog_startup_routines[])() = {
    hello_register,
    0
};
и поместите следующий Verilog-код в файл hello.v:
...
Копировать
module main;
  initial $hello;
endmodule
Далее скомпилируйте и выполните код, выполнив следующие действия:
...
Копировать
% iverilog-vpi hello.c
% iverilog -o hello.vvp hello.v
% vvp -M. -mhello hello.vvp
Hello, World!

Компиляция и компоновка в этом примере удобно объединены в команду «iverilog-vpi». Затем команда «iverilog» компилирует исходный файл Verilog «hello.v» в программу «hello.vvp». Далее команда «vvp» демонстрирует использование флагов «-M» и «-m» для указания каталога поиска модуля vpi и имени модуля vpi. В частности, они указывают команде «vvp», где найти модуль, который мы только что скомпилировали.

Команда «vvp», выполненная как указано выше, загружает модуль «hello.vpi», который она находит в текущем рабочем каталоге. Когда модуль загружен, происходит сканирование таблицы vlog_startup_routines и выполняется функция «hello_register». Функция «hello_register», в свою очередь, сообщает «vvp» о системных задачах, которые включены в данный модуль.

После загрузки всех модулей загружается файл проектирования «hello.vvp», и его вызов системной задачи «$hello» сопоставляется с версией, объявленной модулем. Пока «vvp» компилирует исходный файл «hello.vvp», все обращения к «$hello» передаются функции «compiletf». Эта функция вызывается во время компиляции и может быть использована для проверки параметров системных задач или функций. Ее можно оставить пустой, как сейчас, или не указывать совсем. Функция «compiletf» может помочь производительности, собирая проверки параметров во время компиляции, так что их не нужно делать каждый раз при запуске системной задачи, что потенциально экономит время выполнения в целом.

Когда время выполнения выполняет вызов системной задачи hello, в загруженном модуле вызывается функция «hello_calltf», и таким образом генерируется вывод. Функция «calltf» вызывается во время выполнения, когда код Verilog фактически выполняет системную задачу. Именно здесь находится активный код задачи.

13.4. Типы возврата системных функций

Icarus Verilog поддерживает системные функции так же, как и системные задачи, но есть одно осложнение. Обратите внимание, что модуль, который вы компилируете, загружается только программой «vvp». В основном это не является проблемой, но при разработке выражений необходимо следить за типами, поэтому основному компилятору необходимо знать возвращаемый тип функций.

Начиная с Icarus Verilog v11, решение довольно простое. Имена и расположение пользовательских VPI-модулей могут быть переданы компилятору через флаги «iverilog» -m и -L и переменную окружения IVERILOG_VPI_MODULE_PATH. Компилятор загрузит и проанализирует указанные модули, чтобы автоматически определить все возвращаемые типы функций. Компилятор также автоматически передаст программе «vvp» имена и расположение указанных модулей, так что их не нужно будет указывать снова в командной строке «vvp».

Для версий Icarus Verilog, предшествующих v11, решение требует, чтобы разработчик модуля включил таблицу в форме, которую компилятор может прочитать. Файл System Function Table содержит эту информацию. Простой пример выглядит следующим образом:
...
Копировать
# Example sft declarations of some common functions
$random      vpiSysFuncInt
$bitstoreal  vpiSysFuncReal
$realtobits  vpiSysFuncSized 64 unsigned

Это демонстрирует формат файла и типы поддержки. Каждая строка содержит комментарий (начинается с «#») или объявление типа для одной функции. Объявление начинается с имени системной функции (включая ведущий символ «$») и заканчивается типом. Поддерживаются следующие типы:

Любые функции, не имеющие явного объявления типа в файле SFT, неявно принимаются за «vpiSysFuncSized 32 unsigned».

Автор модуля предоставляет вместе с файлом «.vpi», который является модулем, файл «.sft», в котором объявлены все возвращаемые типы функций. Например, если файл имеет имя «example.sft», передайте его в командную строку «iverilog» или в командный файл точно так же, как если бы это был обычный исходный файл.

13.5. Модули Cadence PLI

С помощью модуля cadpli Icarus Verilog может загружать приложения PLI1, которые были скомпилированы и скомпонованы для динамической загрузки в Verilog-XL или NC-Verilog. Это позволяет пользователям Icarus Verilog запускать модули сторонних разработчиков, которые были скомпилированы для взаимодействия с XL или NC. Очевидно, что это работает только в той операционной системе, для запуска которой было скомпилировано PLI-приложение. Например, модуль для Linux может быть загружен и запущен только под Linux. Кроме того, 64-битная версия vvp может загружать только 64-битные PLI1-приложения и т. д.

Для связи миров в Icarus Verilog используется интерфейсный модуль «cadpli». Этот модуль устанавливается вместе с Icarus Verilog и вызывается обычным флагом -m в iverilog или vvp. Этот модуль, в свою очередь, сканирует расширенные аргументы в поисках аргументов -cadpli=. Последние указывают объект share и функцию bootstrap для запуска модуля. Например, для запуска модуля product.so, имеющего загрузочную функцию «my_boot»:
...
Копировать
% vvp -mcadpli a.out -cadpli=./product.so:my_boot

Аргумент «-mcadpli» заставляет vvp загрузить библиотечный модуль cadpli.vpl. Это активирует интерпретатор с аргументом -cadpli=. Аргумент -cadpli=<модуль>: заставляет vvp через модуль cadpli загрузить загружаемое PLI-приложение, вызвать функцию my_boot для получения таблицы veriusertfs и просканировать эту таблицу для регистрации системных задач и функций, экспортируемых этим объектом. Формат расширенного аргумента -cadpli= по сути такой же, как и аргумент +loadpli1= в Verilog-XL.

Интеграция с этого момента не вызывает затруднений. PLI-приложение практически не знает, что его вызывает Icarus Verilog, а не Verilog-XL, и работает так же, как и в других случаях.

13.6. Другие ссылки

Поскольку выше описано только то, как заставить PLI/VPI работать с Icarus Verilog, вот несколько ссылок на материалы, которые помогут разобраться с общими аспектами работы PLI/VPI.

Principles of Verilog PLI by Swapnajit Mittra. ISBN 0-7923-8477-6

The Verilog PLI Handbook by Stuart Sutherland. ISBN 0-7923-8489-X

Главная
Курсы
Вебинары
1. Руководство по установке Icarus Verilog
2. Начало работы с Icarus Verilog
3. Симуляция с помощью Icarus Verilog
4. Флаги командной строки iverilog
5. Формат командного файла
6. Атрибуты
7. IVLPP — препроцессор IVL
8. Флаги командной строки VVP
9. Интерактивный режим VVP
10. VVP как библиотека
11. Флаги командной строки vhdlpp
12. Icarus Verilog с GTKWave
13. Использование VPI
14. Расширения Icarus Verilog
Закрыть