System c что это
Перейти к содержимому

System c что это

  • автор:

Разработка цифровой аппаратуры на C++/SystemC глазами SystemVerilog программиста

SystemC это библиотека для C++ позволяющая моделировать всевозможные аппаратные системы на различном уровне абстракции. Поддерживается как традиционное дискретно-событийное моделирование, привычное программистам на Verilog и VHDL, так и аналоговое моделирование в духе SPICE/Verilog AMS. В комплект также входит библиотека и методология для виртуального прототипирования, библиотеки для написания тестовых окружений и верификации с использованием рандомизированных тестов.

В этой я расскажу о синтезируемом подмножестве SystemC, сравнивая его с синтезируемым SystemVerilog. Сам я пользуюсь SystemC уже где-то 3 года, а до этого несколько лет писал на Verilog/SystemVerilog. Попытаюсь охватить предмет с разных сторон: начиная с философских рассуждений о причинах возникновения SystemC, краткого обзора экосистемы и инструментария и заканчивая практическими примерами синтаксиса и семантики.

Подразумевается, что читатели знакомы с Verilog и C++.

Размышления о причинах возникновения SystemC

За свою длинную историю индустрия разработки электроники нашла применение множеству языков программирования и породила огромное количество DSLей (Domain-specific languages). Если представить себе гипотетического full-stack аппаратчика (по аналогии с full-stack веб-программистом), который в одиночку может спроектировать современную микросхему, от алгоритма до реализации в кремнии, то ему помимо знания матчасти (арихитектура эвм, электроника, алгоритмы из прикладной области и др.) придется владеть целой кучей разнообразных языков: Matlab для разработки алгоритмов, Verilog или VHDL для описания RTL модели, SystemVerilog/E/Vera для написания тестов и тестового окружения, TCL для написания скриптов управляющих САПР пакетами, SPICE/Verilog-AMS для моделирования аналоговых подсистем, SKILL или Python для генерации топологий, Си/Asm для написания всевозможного firmware. При желании список можно продолжать и дальше.

Конечно в природе такие универсальные инженеры практически не встречаются и проект делают несколько команд, каждая из которых хорошо разбирается в своей достаточно узкой области. Однако, очень часто приходится совмещать работу на нескольких этапах разработки. К примеру, легко представить, что человек написавший RTL модель IP блока напишет для него и набор тестов для верификации. Это в свою очередь создает запрос на создание универсальных языков, пригодных для решения смежных задач.

В мире цифровой микроэлектроники таким универсальным языком стал SystemVerilog, который помимо классического Verilog (с небольшими расширениями), содержит в себе объектно-ориентированный язык для написания тестовых окружений, язык утверждений (assertions) для формальной верификации а так же специальные конструкции для рандомизации и анализа тестового покрытия. В каком-то смысле SystemVerilog это не совсем новый язык, а скорее конгломерат языков, склеенный общим синтаксисом.

Но что если мы хотим большего? Язык на котором помимо всего вышеперечисленного можно разрабатывать алгоритмы, писать встроенное ПО, создавать виртуальные прототипы. Не пора ли добавить к SystemVerilog еще пару DSL?

We need to go deeper

Существует однако и другой подход: вместо придумывания всё новых DSLей, можно создавать программные библиотеки, предназначенные для решения специального класса задач. Таким путём пошли создатели SystemC — библиотеки для C++, позволяющей моделировать цифровую аппаратуру. Хотя в каком-то смысле SystemC является DLS’ем, созданным средствами метапрограммирования на C++, сам C++ при этом не расширяется новыми синтаксическими конструкциями. Метапрограммирование широко применяется и в других C++ библиотеках.

У такого подхода существуют свои плюсы и минусы. Основной плюс C++ в его универсальности: сегодня ты можешь писать хардвер на SystemC, а завтра GUI на Qt. (Хотя придется потратить достаточно много времени на изучение каждой из этих библиотек). Основной минус в синтаксисе: код на чистом DSL будет намного красивей, особенно если нужно сделать что-то простое (для простых модулей код на Verilog будет компактней и проще, чем аналогичный код на SystemC).

Помимо недостаточной универсальности у Verilog есть и другая проблема: он очень низкоуровневый. В каком-то смысле синтезируемый Verilog это макроассемблер для аппаратуры ( если ассемблер для аппаратуры это логическая схема). Новые конструкции, появившиеся в синтезируемом SystemVerilog не решают эту проблему низкоуровневости. Очень часто приходится прибегать к использованию всевозможных генераторов кода на Verilog, например скриптов на Python. Среди моих коллег популярной была идея вставлять код на Perl внутрь модулей на Verilog. Полученный таким путём гибрид назвали перлилогом. Думаю многие знакомы с Verilog-mode для emacs, который умеет генерировать Verilog код для соединения модулей.

По сравнению с SystemVerilog, синтезируемый SystemC позволяет гораздо больше. Да, вы можете писать синтезируемый код с классами! При решении сложных задач средства абстракции C++ позволяют писать более элегантный (простой и компактный) код.

Экосистема SystemC

Рассмотрим основные программные инструменты, с которыми приходится иметь дело разработчикам на SystemVerilog и SystemC.

Среда разработки

SystemVerilog:
Большинство программистов на Verilog для написания кода используют текстовый редактор: поддержка Verilog есть в Vim, Emacs, Sublime Text, Notepad++, Slickedit и других популярных редакторах. Прикладным программистам написание кода в тестовом редакторе может показаться архаизмом: большинство из них используют умные IDE с авто-подсказками, автоматизированными рефакторингами, удобной навигацией. Однако в мире синтезируемого Verilog огромной пользы от использования IDE нет: это объясняется тем что вся функциональность разбивается на совершенно независимые друг от друга модули. Весь контекст с которым работает разработчик отдельного модуля обычно умещается в один файл. Совсем другое дело с написанием тестбенчей на SystemVerilog, здесь вполне может пригодится IDE, такая как DVT.

SystemC:
При написании синтезируемого C++/SystemC простым текстовым редактором уже не обойтись. К счастью, существует множество C++ IDE (в том числе и бесплатных), которые в состоянии справиться с кодом на SystemC. Например, можно использовать привычную многим MS Visual Studio. Я долгое время пользовался Eclipse CDT и Netbeans для написания кода на C++/SystemC. Последнее время пробую Clion от Jetbrains.
Написание SystemC кода в Clion

Симуляция и отладка

SystemVerilog:
Для симуляции и отладки кода на Verilog используется HDL симулятор. Существуют как бесплатные (IcarusVerilog), так и платные симуляторы. По сравнению с бесплатным симулятором коммерческие решения обеспечивают большую скорость симуляции и предоставляют удобные графические среды для отладки.

SystemC:
C SystemC ситуация в целом похожа: можно использовать референсный симулятор и GDB для отладки, но когда нужно отлаживать какой-то более-менее сложный сигнальный протокол приходится пользоваться одним из коммерческих симуляторов.

Отладка SystemC в симуляторе

Синтез

SystemVerilog:
Синтез SystemVerilog поддерживается основными FPGA и ASIC вендорами. В том числе существуют и бесплатные версии пакетов для FPGA, чем пользуются многие российские вузы для обучения студентов азам цифровой схемотехники.

SystemC:
Для синтеза SystemC используются специальные пакеты высокоуровневого синтеза (англ. High-level Synthesis, HLS). Что в них такого высокоуровневого спросите вы? Всё дело в том, что HLS пакеты, помимо традиционного RTL кода написанного на SystemC умеют синтезировать и чисто поведенческий (“untimed”) код, автоматически вставляя регистры, там где это необходимо.

Большинство HLS пакетов могут синтезировать и чистый C/C++, SystemC используется только в тех случаях, когда нужно добавить модульность и сигнальные интерфейсы. В каком-то смысле синтез с C/C++ является технологией для разработки акселераторов, конкурирующей с синтезом с OpenCL. Хотя при использовании SystemC мы не ограничены только разработкой акселераторов, а можем разрабатывать совершенно любые цифровые схемы. Чуть позже я расскажу про HLS немного подробнее.

На выходе HLS пакета мы обычно имеем привычные RTL модули на Verilog, которые затем синтезируются с помощью Verilog синтезатора.

К сожалению, все существующие HLS с поддержкой SystemC исключительно коммерческие и стоят много денег. Бесплатных версий нет, хотя университетам всё продают с большой скидкой.
Лучшими средствами синтеза SystemC на рынке являются Stratus от Cadence и Catapult C от Calypto/Mentor Graphics.

Другие EDA пакеты для SystemC

Помимо написания синтезируемого кода, SystemC достаточно широко используется для виртуального прототипирования. Создание виртуальных прототипов (эмуляторов) на C++/SystemC используется в пакетах Synopsys Virtualizer, Mentor Graphics Vista, Cadence Virtual System Platform. При этом нельзя сказать что SystemC на этом рынке является доминирующим решением: существуют и продукты SystemC не использующие, например WindRiver Simics.

На этом обзорная часть статьи завершается. Пришло время погрузиться в код.

Погружение в код

Синтезируемый SystemC. Базовые строительные блоки

Не буду здесь полностью описывать весь стандарт SystemC, пройдусь только по самому необходимому. Все примеры будут построены на сравнении SystemVerilog и SystemC.

Типы данных

SystemVerilog:
Основным типом используемым в синтезируемом SystemVerilog является тип logic. Переменная типа logic может принимать 4 значения: 1, 0, x, z. x означает неизвестное значение. z означает высокоимпедансное состояние. Можно создавать вектора типа logic различной длины, например:

logic [1:0] data; // 2-х битный вектор initial begin data = 7; $display(data); end 

Выведет в консоль:3

SystemC:
В SystemC тоже есть типы с 4-мя состояниями. Однако на практике в основном используются типы с 2-мя состояниями 1 и 0. Основная причина — типы с 2-мя состояниями симулируются быстрее.

После синтеза все типы с 2-мя состояниями превращаются в logic. Это может привести к различиям в результатах симуляции SystemC (до синтеза) и Verilog (после синтеза). В SystemC не сброшенный регистр будет иметь значение 0, в Verilog — x. К счастью, синтезатор выдает предупреждение каждый раз когда видит регистр без сброса, поэтому на практике после чтения лога синтезатора проблем с расхождением результатов симуляции можно избежать.

Очень часто в коде на SystemC используются встроенные типы C++, такие как int или char. Если же нам требуется число с заданным количеством бит, можно использовать тип sc_uint:

sc_uint data; // 2-х битная переменная data = 7; cout  

Выведет в консоль:3

Как реализован sc_uint? Это просто шаблонный класс в котором перегружены все основные операторы.

Модули

Рассмотрим пример пустого модуля на SystemVerilog и SystemC
SystemVerilog:

module top ( input clk, rstn, input [7:0] din, output logic [7:0] dout ) // тело модуля endmodule 

SystemC:

struct top: public sc_module < sc_inclk, rstn; sc_in > din; sc_out > dout; top(const char* name) : sc_module(name) , clk("clk") , rstn("rstn") , din("din"), dout("dout") < >>; 

Разберем интересные строки подробнее:

struct top: public sc_module  

модули в SystemC это производные классы от класса sc_module

 sc_in clk, rstn; sc_in > din; sc_out > dout; 

Для создания портов в SystemC используются специальные классы sc_in и sc_out.

 top(const char* name) : sc_module(name) , clk("clk") , rstn("rstn") , din("din"), dout("dout") 

Конструкторам модуля и портов передаются строки содержащие их имя. Это нужно для того чтобы симуляционное ядро могло выдавать удобные для чтения логи, например:
Error: (E109) complete binding failed: port not bound: port 'top.dout' (sc_out)
Ошибка: порт dout модуля top никуда не подключен.
( Возможно, когда в C++ появится нормальная поддержка интроспекции объекты в SystemC смогут узнавать свои имена самостоятельно )
Для удобства создания модулей в SystemC определено несколько макросов. С их использованием аналогичный модуль выглядит следующим образом:

SC_MODULE(top) < sc_inclk, rstn; sc_in > din; sc_out > dout; SC_CTOR(top) , clk("clk") , rstn("rstn") , din("din"), dout("dout") <> >; 
Переменные и присваивания

SystemVerilog:
Можно утверждать что все переменные в синтезируемом SystemVerilog- статические: они существуют с начала и до конца симуляции. И имеют глобальную область видимости (хотя доступ к сигналам “через крышу” по иерархическому имени не допускается в синтезируемом коде). Еще одной особенностью SystemVerilog является наличие нескольких операторов присваивания: блокирующего и неблокирующего присваивания в процедурных блоках, а так же непрерывного присваивания.
Блокирующее присваивание происходит либо сразу, либо блокирует исполнение текущего процесса до момента когда присваивание совершится.
Пример:

logic a; initial begin a = #42 1; $display($time); end 

Выведет в консоль:42
т.к. вызов функции $display произойдет лишь в момент времени 42, когда присваивание произойдет.
Неблокирующее присваивание откладывает присваивание на какой-то момент симуляционного времени в будущем и не блокирует исполнение процесса. Если время не указано явно, присваивание происходит на следующем дельта-цикле.

initial begin a  

Выведет в консоль: 0

SystemC:
Переменные в C++ ничего не знают про симуляционное ядро SystemC и поэтому ведут себя привычным для C++ программиста образом. Для того чтобы промоделировать неблокирующее присваивание в SystemC используется специальный тип sc_signal, переменные этого типа далее называются сигналами:

sc_signal < sc_uint> data; // сигнал типа sc_uint

Любое присваивание значения data будет неблокирующим.
Синтезируемый SystemC требует, чтобы взаимодействие между несколькими процессами происходило через сигналы. Аналогично, в Verilog хорошим стилем является использование исключительно неблокирующих присваиваний в always_ff процедурных блоках. В противном случае рискуем получить неопределенное поведение (состояние гонки), когда результат симуляции будет зависеть от порядка вызова процессов в одном дельта цикле.
Аналога блокирующего присваивания в SystemC нет.

Процессы (Процедурные блоки)

SystemVerilog :
Синтезируемый SystemVerilog поддерживает два основных типа процедурных блоков always_comb и always_ff. Помимо них есть еще always_latch, но использовать регистры-защелки на практике приходится довольно редко.
always_comb используется для описания комбинаторной логики

always_comb begin a = b + c; end 

Процесс будет исполняться каждый раз, когда изменяется значение b или c. То же самое можно было бы написать более явно, как в классическом Verilog:

always@(b or c) begin a = b + c; end 

Помимо процедурного блока always_comb для описания комбинационных схем можно использовать оператор непрерывного присваивания:

assign a = b + c; 

Процедурный блок always_ff используется для описания последовательностной логики, т.е. схем с памятью.

always_ff @(posedge clk or negedge arst_n) begin if(~arst_n) begin a  

Этот пример описывает двоичный счетчик с асинхронным сбросом.

SystemC:

Процессы в SystemC создаются в конструкторе модуля. Тело процессов описывается в функциях-членах модуля. Тип процесса похожего на always блок из Verilog в SystemC называется SC_METHOD.
Рассмотрим примеры процессов, аналогичные приведенным ранее процедурным блокам на SystemVerilog:
Комбинаторная логика:

SC_CTOR(top) < SC_METHOD(comb_method); // макрос для создания процесса типа SC_METHOD sensitive void comb_method() < a = b + c; >// тело процесса описывается в функции-члене 

Последовательностная логика:

SC_CTOR(top) < SC_METHOD(seq_method); // макрос для создания процесса типа SC_METHOD sensitive void seq_method() < // тело процесса описывается в функции-члене if (!arst_n) a = 0; else a = a + 1; >

Аналога непрерывного присваивания в SystemC нет. Так же как и нет возможности указать wildcard в списке чувствительности ( always@* в Verilog). Даже мощная шаблонная магия C++ не позволяет реализовать это средствами метапрограммирования.

Параметризация

Модули на SystemVerilog можно параметризовать. К примеру, можно написать параметризуемое FIFO, ширина и глубина которого будут указываться при создании экземпляра.

В SystemC для создания параметризуемых модулей используются шаблонные классы. С использованием шаблонов и наследования возможности по параметризации в SystemC становятся почти безграничными.

Промежуточные итоги

SystemC позволяет описывать аппаратуру на уровне RTL в стиле очень близком к простому Verilog. Код на Verilog будет изящней и компактней, но в целом всю функциональность можно повторить. Рассмотрим полноценный пример: реализуем на Verilog и SystemC сдвиговый регистр с последовательным входом и выходом (serial-in/serial-out) и асинхронным сбросом:
Код на Verilog:

module shifreg ( input clk, sin, reset, output sout ); reg [7:0] tmp; always @(posedge clk or posedge reset) begin if (reset) tmp ; end assign sout = tmp[7]; endmodule 

Код на SystemC

// Для сигналов и портов используется инициализация в стиле C++11 SC_MODULE(shift_reg) < sc_inclk, sin, reset; sc_out sout; SC_CTOR(shift_reg) < SC_METHOD(shift_method); sensitive private: sc_signal > tmp ; void shift_method() < // для чтения и записи сигналов используются методы read и write // метод write - аналог неблокирующего присваивания в verilog if ( reset.read() ) < tmp.write(0); >else < // перегруженный оператор "," (запятая) используется для конкатенации tmp.write((tmp.read().range(6,0) , sin.read())); >> void sout_method() < sout = tmp.read()[7]; >>; 
Хороший SystemC. Возможности синтезируемого SystemC, которых нет в SystemVerilog
Пользовательские типы данных

Синтезируемый SystemC полностью поддерживает объектно-ориентированное программирование на C++. Это позволяет создавать удобные типы данных для работы в своей предметной области. Например, если вы занимаетесь 3D графикой, то вам постоянно приходится иметь дело с 3-х мерными вещественными векторами. Для их аппаратной реализации потребуется решить несколько задач.
Во первых, операции с плавающей точкой как правило не поддерживаются синтезатором. Поэтому вам придется реализовать их самостоятельно, или использовать стороннюю библиотеку, например DesignWare floating point. И в том и в другом случае вы можете создать удобный класс для работы с плавающей точкой:

class my_float < public: my_float operator+( const my_float &rval) const; my_float operator-( const my_float &rval ) const; my_float operator*( const my_float &rval ) const; // и другие операции . private: sc_uintraw_data; // внутри себя float это простой 32-битный вектор > 

С использованием my_float можно реализовать класс для работы с векторами:

class vector_3d < public: vector_3d operator*( const vector_3d &rval ) const; // vector product vector_3d dot_product (const vector_3d &other) const; // dot product // и другие операции . private: my_float x, y, z; >; 

После чего эти пользовательские типы можно использовать в синтезируемом SystemC.

vector_3d a,b,c; c = a + b; 

Синтезаторы SystemVerilog не поддерживают синтез классов, но умеют синтезировать структуры. Поэтому программирование на SystemVerilog чем-то напоминает программирование на языке Си. На SystemVerilog эта задача с векторами обычно решается следующим образом: вы создаете отдельный пакет, а в нём определяете структуры и функции для работы с ними:

package Vector3DPkg; typedef struct < logic [31:0] x, y, x; >vector_3d; function vector_3d add(vector_3d a, b); add.x = float_add (a.x, b.x); add.y = float_add (a.y, b.y); //. endfunction function vector_3dmul(vector_3d a, b); //. endpackage : Vector3DPkg 
SC_CTHREADS (clocked threads). Процессы с неявным состоянием

Синтезируемые процессы в Verilog не могут использовать выражения для управления временем и ожидания событий. Т.е. запущенный процесс должен исполниться до конца и только потом передать управление другому процессу. К примеру, данный процесс не синтезируется:

always @(posedge clk) begin out  

В Verilog мы должны явно специфицировать регистр состояния, который будет определять поведение процесса на каждом такте. Синтезируемым аналогом предыдущего примера кода будет следующий процесс:

logic [1:0] state; always @(posedge clk or negedge reset_n) begin if ( ~ reset_n) state  

В SystemC синтезируемые процессы описывающие последовательностную логику (цифровой автомат) могут останавливаться на ожидании события от тактового сигнала. Это позволяет описывать автомат без явной спецификации регистра состояния. Процессы такого типа создаются с помощью макроса SC_CTHREAD. Остановка процесса до следующего тактового сигнала осуществляется путём вызова функции wait(); Пример:

SC_CTOR ( top ) < // процесс создается в конструкторе // clk.pos() означает тактирование по переднему фронту сигнала clk SC_CTHREAD(test_cthread, clk.pos() ); async_reset_signal_is(reset_n, 0); // асинхронный сброс по уровню 0 >void test_cthread () < // код до первого вызова wait() называется reset-секцией, выполняется при запуске процесса или при активном сигнале сброса. out > 

На первый взгляд польза от наличия таких процессов не очевидна. В конце концов не так уж и сложно явно закодировать переменную для состояния цифрового автомата (переменная state в примере на Verilog).
Истинная мощь SC_CTHREAD процессов заключается в возможности вызова функций, которые могут заблокировать процесс, т.е. вызывать функцию wait(). Такая функция может исполняться несколько тактов! Аналогом из мира Verilog являются task’и, они однако не синтезируются и используются только в тестах.
Например:

while (1) < res = calculate_something(); // несколько тактов занимаемся какими-то вычислениями spi_send(res); // отправляем результат по SPI, тоже за несколько тактов >

Ещё больше пользы от функций, исполнение которых иногда занимает несколько тактов, а иногда происходит мгновенно, без вызова wait().
Для примера рассмотрим процесс, который читает данные из FIFO, обрабатывает их, после чего отправляет результат в память по системной шине (например, AMBA AXI). Пускай данными будет 3-х мерный вектор рассмотренный раннее, а обработка будет заключаться в нормализации этого вектора. С использованием SC_CTHREAD и готовых классов для работы с FIFO и AXI написать такой процесс очень просто:

fifo data_fifo; // экземпляр FIFO amba_axi bus_master; // реализация мастера шины AMBA AXI void computational_thread() < wait(); while (1) < vector_3d vec = data_fifo.pop(); // читаем данные из FIFO vec.normalize(); // обрабатываем данные bus_master.write( 0xDEADBEEF, vec); // отправляем результат в память по адресу 0xDEADBEEF >

Предположим что нормализация вектора реализована в виде комбинационной схемы. Тогда, в зависимости от готовности FIFO и шины, исполнение одного цикла такого процесса может занимать от одного такта и более. Если в FIFO есть данные и шина не занята, то нормализация одного вектора будет происходить за такт. Если FIFO пустое, то процесс заблокируется на функции чтения из FIFO data_fifo.pop до момента поступления новых данных. Если шина занята, то процесс заблокируется на функции bus_master.write до момента когда шина освободится.

У опытного разработчика наверняка возник вопрос, как мы делаем нормализацию вектора за такт? На какой частоте работает наш модуль? Действительно, цепочка из умножения, двух сложений, квадратного корня и деления это слишком много для одной комбинационной схемы. Тем более что речь идет об операциях с плавающей точкой. В случае синхронной схемотехники эта комбинационная цепочка наверняка станет узким местом, ограничивающем максимальную тактовую частоту работы всей схемы.

В зависимости от требований к пропускной способности нашего нормализатора проблема может быть решена несколькими способами:

  • Если мы никуда не торопимся, то можно сэкономить на ресурсах и реализовать нормализацию в виде FSMD с одним умножителем, сумматором, делителем и модулем извлечения квадратного корня. В этом случае мы потратим 6 тактов на вычисление длины вектора и еще 3 такта для вычисления значения каждого из элементов результата, в сумме — 9 тактов на один вектор.
  • Если мы сильно торопимся, а ресурсов не жалко, оригинальную комбинационную схему можно превратить в конвейер. В этом случае в пике (когда в FIFO постоянно есть данные) мы получим тот же 1 такт на вектор, но уже на большей тактовой частоте.
  • Так же возможны любые промежуточные между первым и вторым варианты. К примеру, если логика дорогая а регистры дешевые, то в первом рассмотренном варианте микроархитектуры можно начинать обработку следующего вектора не дожидаясь завершения выполнения предыдущего, по мере освобождения ресурсов. После вычисления трех квадратов элементов первого вектора, умножитель освобождается и можно начинать обработку следующего вектора. Такая реализация называется конвейером с интервалом инициализации в 3 такта. Т.е. каждые три такта конвейер будет забирать из FIFO новый вектор.

К сожалению, реализация любого из предложенных решений вручную потребует много времени и значительно усложнит наш 3-х строчный исходник. Например, в случае конвейерной реализации придется создать по процессу на каждую из стадий конвейера. К счастью, при использовании SystemC нам ничего не нужно делать руками — ведь можно просто воспользоваться высокоуровневым синтезом!

Высокоуровневый синтез.
  • Исходный код. Иногда его называют untimed code, т.к. он не содержит конструкций для остановки процесса, таких как функция wait
  • Timing constraints. Временные ограничения. Задают список тактовых сигналов и их период, а так же задержки на внешних портах
  • Спецификация микроархитектуры. В качестве микроархитектуры мы можем выбрать любой из рассмотренных ранее вариантов
void vector_3d::normalize() < my_float magnitude = sqrt( x*x + y*y + z*z ); x = x / magnitude; y = y / magnitude; z = z / magnitude; >

В качестве микроархитектуры можно например выбрать конвейер с интервалом инициализации в 1 такт и задержкой (latency) в 4 такта, а тактовую частоту установить в 500 МГц. Используя технологическую библиотеку HLS пакет определит задержку распространения сигнала через каждый арифметический элемент и оптимально расставит их по стадиям конвейера. При необходимости, выполнение одной операции может быть разбито на несколько стадий: например деление это достаточно сложная операция, выполнение которой может и не влезть в один тактовый период. Поэтому вполне возможно, что синтезетор разобьет делитель между 3ей и 4ой стадией конвейера.

Анализ проекта в HLS пакете от Cadence

  • Не требуется руками специфицировать регистры, логику остановки конвейера.
  • HLS позволяет переключаться между несколькими микроархитектурами не меняя исходный код.
uint32_t write_address; // 32-битный адрес vector_3d memory[1024]; // память 1024x96 , каждый вектор - 96 бит . while (1) < vector_3d vec = data_fifo.pop(); // читаем данные из FIFO vec.normalize(); // обрабатываем данные memory [write_address] = vec; // записываем результат в память write_address ++; >

Хочется также отметить что не все HLS средства поддерживают синтез с SystemC. Использование SystemC требуется лишь там, где необходимо описывать сигнальные интерфейсы (например AMBA или UART). На FPGA платформах шинные интерфейсы как правило стандартизированы, поэтому их использование в HLS коде может быть неявным. К примеру, Vivado HLS от Xilinx ориентирован прежде всего на синтез с чистого C/C++. В рамках SoC платформы Xilinx стандартом является интерфейс AMBA AXI, поэтому предполагается что отправлять и получать данные ваши функции будут по AXI, либо с помощью простого handshake протокола. Всё что от вас требуется — описать алгоритмический код. Конечно у такого подхода есть и свои недостатки: при создании сложных проектов вы вполне можете прийти к склеиванию множества HLS модулей в коде на Verilog или графическом редакторе схем. Для этих целей у Xilinx есть еще один продукт — Vivado IP Integrator.

Соединение HLS блока с ARM процессором через AMBA AXI в Vivado IP Integrator

Заключение

В качестве заключения хочу попробовать ответить на вопрос который часто задают RTL разработчики увидев новый тул: А что с качеством результата? Как будут отличаться тайминги, площадь, энергопотребление схем описанных на SystemC и синтезированных с помощью HLS в сравнении с RTL описанным на SystemVerilog?

На самом деле никак. Всё в ваших руках: SystemC и HLS не лишают вас возможности затюнить всё с точностью до гейта там где это требуется. И в то же время HLS не освобождает вас от необходимости понимать основы цифровой схемотехники. HLS это не магическое средство, превращающее C++ программиста в аппаратчика, это средство позволяющее автоматизировать рутинную работу, облегчающее процесс написания и поддержки синтезируемого кода.

В этой статье я никак не коснулся вопроса верификации. Верификация всегда занимает большую часть времени разработки и SystemC есть что предложить на этом поприще. Хорошо написанный SystemC стимулируется быстрее чем RTL, т.к. часть кода написана в “untimed стиле”, а сигнальные интерфейсы можно заменить на вызовы функций (Transaction-level modeling). Библиотека SCV (SystemC Verification Library) позволяет рандомизировать тестовые вектора, так же на подходе SystemC версия UVM. А т.к. SystemC это C++, то части исходного кода можно переиспользовать между синтезируемым кодом, референсной моделью, виртуальным прототипом и драйвером операционной системы. Но рассказ обо всём этом достоин отдельной статьи.

  • схемотехника
  • systemc
  • verilog
  • fpga/asic
  • hardware
  • hardware acceleration
  • аппаратное ускорение
  • аппаратная реализация
  • Высокая производительность
  • C++
  • Системное программирование
  • FPGA

System c что это

Язык SystemC - это библиотека специальных классов для языка C++, которая позволяет создавать эффективные и точные модели программных алгоритмов, аппаратных архитектур, интерфейсов и схем на системном уровне, т.е. практически всех компонентов встроенных систем.

На данном сайте представлен базовый материал, посвященный языку SystemC:

Обзор современных языков описания аппаратуры; особенности построения моделей на языке SystemC; описание этапов создания моделей с использованием среды разработки ЦА, поддерживающей язык SystemC; основы SystemC; примеры разработки моделей ЦА на языке SystemC; материал, включающий различные статьи, посвященные языку SystemC; ссылки на литературу.

28.07.2008 - Добавлен новый материал "Знакомство с SystemC".

System c что это

Модули и
Иерархия

Рисунок 2.1 – Главные компоненты языка SystemC

Имитатор SystemC имеет 3 главные фазы работы: разработка, выполнение и постобработка [1]. Выполнение всех операторов до конструкции sc_start() есть фаза разработки. На этом этапе происходит инициализация структур данных и подготовка к следующей фазе выполнения. Фаза выполнения передает управление ядру моделирования SystemC, которое управляет работой всех процессов и создает иллюзию параллельности их выполнения. Постобработка связана с удалением всех созданных структур данных, освобождением памяти и завершением этапа моделирования.

Принцип работы ядра моделирования SystemC схож с языками VHDL и Verilog. Если обратиться к Verilog и VHDL, то может показаться, что каждый процесс начинается сразу. В действительности проходит некоторое время между инициализацией кода и началом моделирования. В SystemC, также как и в C/C++, есть строго определенная точка входа в программу. В случае SystemC – sc_main().

В библиотеке SystemC существует тип данных sc_time для того чтобы измерять время в процессе моделирования. У времени есть две составляющие: числовое значение и размерность. Языком поддерживается измерение времени в секундах, миллисекундах, микросекундах, наносекундах, пикосекундах, фемтосекундах.

Соответствующие спецификаторы времени следующие:

  • SC_SEC // секунды
  • SC_MS // миллисекунды
  • SC_US // микросекунды
  • SC_NS // наносекунды
  • SC_PS // пикосекунды
  • SC_FS // фемтосекунды

Объявление переменных времени выглядит следующим образом:

sc_time имя_переменной (числовой значение, спецификатор);
sc_time t_Period(10, SC_NS);

Над переменными типа sc_time SystemC позволяет производить операции сложения, вычитания, масштабирования и др. В библиотеке определена константа SC_ZERO_TIME, которая соответствует времени 0.

Метод sc_start() запускает фазу моделирования, которая состоит из инициализации и выполнения. Метод может принимать параметр типа sc_time, который является ограничением максимального времени моделирования. Без параметра sc_start() запускает фазу моделирования, которая будет протекать бесконечно, пока в программе не встретится метод sc_stop(), который принудительно завершает моделирование.

Метод wait() используется в SystemC для того чтобы моделировать задержки реальных действий (например: механических воздействий, химических реакций или распространения сигнала). С помощью метода можно приостановить выполнение процесса SC_THREAD на промежуток времени или до появления какого-либо события:

sc_time t_delay(25, SC_FS);
wait(t_delay);

Данный метод позволяет определить в момент вызова, сколько времени прошло с начала моделирования, например:

sc_time t_delay1(10, SC_NS);
sc_time t_delay2(25, SC_NS);
cout wait(t_dalay1);
cout wait(t_delay2);
cout В результате исполнения данного проекта, мы получем на экране следующие сообщения:
Текущее время моделирования: 0 s
Текущее время моделирования: 10 ns
Текущее время моделирования: 35 ns

В «таблице 2.1» представлены типы данных, поддерживаемые SystemC. В «таблице 2.2» представлены операции над данными, поддерживаемые SystemC [1].

Таблица 2.1 – Основные типы данных, поддерживаемые SystemC

Одиночный бит, принимающий значение true или false. Использовать данных тип не рекомендуется, более предпочтительно применение типа bool

Вектор, содержащий n бит. Рекомендуется использовать sc_uint , где это возможно

Одиночный бит, который принимает значения 0, 1, X, Z

Вектор, содержащий n бит, типа sc_logic

Вектор, содержащий n целых чисел, размером 64 бит

Вектор, содержащий n целых чисел, размером более 64 бит

К поддерживаемым типам данных также относятся другие типы данных С++, такие как bool, int, unsigned int, long, unsigned long, char, unsigned char, short, unsigned short, struct, enum.

Таблица 2. Операции над данными, поддерживаемые SystemC

&(and), |(or), ^(xor), and ~(not)

Инкремент и декремент

to_uint( ) и to_int( )

Модуль в SystemC – это базовый элемент, включающий в себя процессы (processes) и другие модули. SC_MODULE – макроc C++. На «рисунке 2.2» изображен модуль, который включает в себя несколько процессов.

Рисунок 2.1 – Модуль, содержащий несколько процессов

Описание каждого модуля на SystemC содержится в заголовочном файле module_name.h и содержит:

  • Объявление портов;
  • Объявление внутренних сигналов;
  • Объявление внутренних переменных;
  • Объявление экземпляров подмодулей;
  • Конструктор;
  • Деструктор;
  • Процессы;
  • Функции процессов;
  • Вспомогательные функции.

Пример описания заголовочного файла модуля SystemC:

#include "systemc.h"
SC_MODULE (имя модуля)
SC_CTOR (module_name) // Конструктор
//Объявление процессов
//Объявление чувствительного списка
>
>;

Порты необходимы для взаимодействия модулей между собой. Каждый модуль может включать любое количество входных (inputs), выходных (outputs) и смешанных (inouts) портов.

Порты - это члены объекта SC_MODULE. Вы можете объявлять любое число портов типа sc_in, sc_out, sc_inout. Конструкция sc_inout позволяет читать значение из выходного порта модуля.
Пример описания портов на SystemC:

SC_MODULE (module_name)
sc_in < bool >port_name;
sc_out < char >port_name;
sc_inout < sc_logic >port_name;
SC_CTOR ( module_name)
// Объявление процессов
// Объявление списка чувствительных сигналов
>
>;

Порты используются для взаимодействия модуля с другими модулями. Внутри модуля необходимо использовать внутренние сигналы (Internal Signals) для передачи данных от одного процесса к другому.
Пример описания сигналов на SystemC:

SC_MODULE (module_name)
// Объявление портов
sc_in < port_type>port_name;
sc_out < port_type>port_name;
// Объявление сигналов
sc_signal < signal_type>signal_name;
sc_signal < signal_type>signal1, signal2;
SC_CTOR ( module_name)
// Объявление процессов
// Объявление списка чувствительных сигналов
>
>;

Внутри модуля вам предоставляется возможность создавать переменные любого типа, поддерживаемого SystemC и C++. Эти переменные могут участвовать во всех взаимодействиях внутри создаваемого модуля.
Пример описания переменных на SystemC:

SC_MODULE (module_name)
// Объявление портов
sc_in < port_type>port_name;
sc_out < port_type>port_name;
// Объявление внутренних сигналов
sc_signal < signal_type>signal_name;
// Объявление внутренних переменных
int count_val; //Internal counter
sc_int mem[1024]; // Массив sc_int
SC_CTOR ( module_name)
// Объявление процессов
// Объявление списка чувствительных сигналов
>
>;

Примечание. Нельзя использовать внутренние переменные для взаимодействия между процессами. Это может привести к ошибкам на этапе симуляции проекта.

Вы можете объявлять одну или несколько функций, которые не являются процессами. Этот тип функций не будет объявлен в конструкторе модуля. Данные функции могут быть вызваны внутри процесса для реализации дополнительных вычислений. На данный тип функций не накладываются ограничения на использование аргументов и тип возвращаемых значений.

С помощью методов чтения и записи можно считывать значения из портов или сигналов, а также записывать значения в порты или сигналы. Рекомендуется использовать функции read() и write() для чтения и записи значений:

address = into.read(); // чтение адреса
temp1 = address; // сохранение адреса
data_tmp = memory[address]; // чтение адреса из памяти
outof.write(data_tmp); // запись в выходной порт

Примечание. Для того чтобы считать или записать отдельный бит порта или сигнала, необходимо сначала записать значение этого порта или сигнала во внутреннюю переменную и произвести выборку отдельного бита из переменной:

sc_signal > a;
sc_int b;
bool c;
b = a.read();
c = b[0];
// c = a[0]; //Данная запись не корректна в SystemC

Изменение значения при присваивании сигналу или выходному порту не происходит до завершения процесса. В случае с внутренними переменными присваивание происходит мгновенно.

Чтобы создать иерархический модуль, необходимо:

  • Объявить в начале модуля объекты составных модулей;
  • В конструкторе модуля необходимо задать уникальное имя каждому отдельному составному элементу;
  • Записать значения во входные порты каждого отдельного модуля, а также указать переменную, сигнал или выходной порт, куда будет записан результат работы модуля.

Процессы в SystemC необходимы для того, чтобы в системе описать параллельное поведение физических элементов [3, c.77]. Это означает, что все процессы выполняются одновременно, а не последовательно как функции в C++.

Описание процесса схоже с описанием функции на C++. Процесс объявляется как функция класса SC_MODULE и регистрируется в его конструкторе. Процесс не принимает параметров и не возвращает значения.

Разработчику необходимо задать список чувствительных входов и сигналов модуля, чтобы определить изменения на каких входах должны спровоцировать начало работы процесса модуля.

Процесс способен читать и записывать значения входных и выходных портов, внутренних сигналов (Internal Signals) и внутренних переменных (Internal variables).

Самый простой тип процесса в SystemC это SC_THREAD. Процесс SC_THREAD в SystemC идентичен программному потоку. В C/C ++ программы, есть только один поток, работающий для всей программы. Ядро SystemC позволяет многим потокам выполняться параллельно. Процесс SC_THREAD запускается в процессе моделирования только один раз, однако его выполнение можно приостановить методом wait(). При завершении процесса, запустить его повторно нельзя, поэтому, данный тип процесса, как правило, содержит бесконечный цикл, имеющий по крайней мере один вызов функции wait().

В отличие от процесса SC_THREAD, процесс SC_METHOD никогда не приостанавливается (вызов функции wait() внутри процесса SC_METHOD приведет к ошибке), однако может быть вызван несколько раз в процессе моделирования, что делает его в некоторых случаях более предпочтительным. Данный тип процесса подобен процессам языков VHDL и Verilog. SC_METHOD поддерживает конструкцию next_trigger(), которая определяет динамическую чувствительность процесса (последующего вызова).

Процесс SC_CTHREAD является небольшой модификацией процесса SC_THREAD с добавлением возможности динамической чувствительности, речь о котором в данной работе не пойдет.

Процесс в SystemC объявляется в теле модуля и регистрируется как процесс внутри конструктора. Необходимо объявлять процесс как функцию void, не содержащую аргументов. При регистрации функции как SC_METHOD процесс, необходимо использовать конструкцию SC_METHOD, которая имеет один аргумент – имя процесса.

Пример описания процесса на SystemC:

SC_MODULE(my_module)
sc_in a;
sc_in b;
sc_out x;
sc_out y;
sc_signalc;
sc_signal d;
void my_method_proc();
SC_CTOR(my_module)
SC_METHOD(my_method_proc);
// Объявление списка чувствительных сигналов
>
>;

Процессы реагируют на изменение сигналов, которые находятся в списке чувствительных входов. Возможно использование функций sensitive(), sensitive_pos() или sensitive_neg() или потоков sensitive, sensitive_pos, sensitive_neg при описании списка чувствительности (sensitivity list).

Для комбинаторной логики, спискок чувствительных входов приравнивает все входные порты (input и inout ports) и сигналы (signals) к входным сигналам процесса. Для реализации level-sensitive входов необходимо использовать метод sensitive так, как показано в примере:

SC_MODULE(my_module)
sc_in a;
sc_in b;
sc_out y;
sc_signalc;
void my_method_proc();
SC_CTOR(my_module)
SC_METHOD(my_method_proc);
// Объявление списка чувствительных сигналов sensitive sensitive(b); //Функциональное описание
sensitive(e); //Функциональное описание
>
>;

Примечание: Во избежание риска возникновения ошибок на этапе симуляции проекта, включайте все входные порты в список чувствительных портов при реализации комбинаторной логики. В примере показана ситуация с неполным перечнем входов, отображенном в списке чувствительных портов.

void comb_proc ()
out_x = in_a & in_b & in_c;
>
SC_CTOR( comb_logic_complete )
SC_METHOD( comb_proc);
sensitive >

Пример описания заголовочного файла модели элемента 3И на SystemC:
#include "systemc.h"

Конструкция Edge-Sensitive используется для реализации последовательностной логики, при моделировании триггеров. Для этого необходимо использовать такие потоки как sensitive_neg (срез), sensitive_pos (фронт). Входные порты должны иметь тип sc_in, ниже приведен пример использования конструкции edge-sensitive:

SC_MODULE(my_module)
sc_in a;
sc_in b;
sc_in clock;
sc_out y;
sc_in reset;
sc_signalc;
void my_method_proc();
SC_CTOR(my_module)
SC_METHOD(my_method_proc);
sensitive_pos (clock); // Функциональное описание
sensitive_neg >
>;

Ограничения при использовании Sensitivity Lists:

  • Нельзя совмещать конструкции Level-Sensitive и Edge-Sensitive в одном процессе;
  • Нельзя применять тип sc_logic для реализации синхросигнала (clock) или других Edge-Sensitive’s входов. Допустимым является только тип sc_in.

Пример описания заголовочного файла JK-триггера на SystemC:

Событие – это нечто, происходящее в определенное время. У события нет никакого значения и продолжительности, оно либо произошло, либо еще нет. В SystemC событие определяется с помощью класса sc_event. Процессы могут реагировать на события, но для этого в списке чувствительности процесса должно быть явно указано событие, к которому он будет чувствителен, например,
sc_event delay;

SC_METHOD(do_delay);
sensitive

Событие можно вызвать с помощью метода notify(), в качестве параметра может быть указан промежуток времени в формате sc_time, через которое событие должно произойти (в таком случае, событие будет запланированным), например,

// Событие произойдет немедленно
delay.notify();
// Событие произойдет через промежуток времени 0
delay.notify(SC_ZERO_TIME);
// Событие произойдет через промежуток времени 20 нс (запланированное событие)
delay.notify(20, SC_NS);
Отменить вызов запланированного события можно с помощью метода cancel():
delay.cancel();

Ниже приведен пример модели логического элемента НЕ7404, задержка срабатывания которого реализовано с помощью события:

В данном примере временная задержка реализована с использованием события. Событие delay, объявленное в теле модуля происходит тогда, когда, на входе элемента НЕ меняется входной сигнал (sc_in A;), метод do_delay чувствителен к изменению сигнала A. Команда delay.notify(22, SC_NS) активизирует событие delay спустя 22 ns, в свою очередь метод do_not, чувствительный к событию delay, реагирует и меняет значение на выходе елемента НЕ.

В SystemC модули связаны, используя или примитивные каналы или иерархические каналы. Оба типа каналов соединяются с модулями через порты.

Интерфейс в SystemC – это абстрактный класс, наследуемый от базового класса sc_interface, в котором объявлены виртуальные методы, которые используют каналы и порты [3, c.130].

Канал в SystemC – это класс, который осуществляет один или более интерфейсов SystemC, наследуется от sc_channel или от sc_prim_channel [3, c.130].

В случае использования интерфейсов, мы можем создавать модули, не зависимые от реализации каналов коммуникации, примером интерфейса в SystemC может служить sc_mutex_if. Примитивный канал, реализующий данный интерфейс – это sc_mutex. Данный канал используется при моделировании разделяемого ресурса:

sc_mutex NAME;
NAME.lock(); // Чтобы занять ресурс NAME
NAME.trylock(); // Чтобы занять ресурс NAME (true – ресурс не занят, false – иначе).
NAME.unlock() // Освободить ресурс

В SystemC существуют также другие примитивные каналы, такие как sc_fifo и sc_semaphore. Sc_signal, речь о котором шла в предыдущих главах данной работы, тоже является каналом SystemC.

Моделирование на языке SystemC в процессе разработки протоколов передачи данных Текст научной статьи по специальности «Компьютерные и информационные науки»

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Оленев Валентин Леонидович

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

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Оленев Валентин Леонидович

Разработка, анализ и проектирование транспортного протокола СТП-ИСС для бортовых космических сетей SpaceWire

Разработка транспортного протокола СТП-ИСС для бортовых сетей SpaceWire
Тестирование и верификация HDL-моделей компонентов SoC. I
Исследование стандарта SpaceFibre при применении технологии виртуального прототипирования
Обзор методов описания встраиваемой аппаратуры и построения инструментария кросс-разработки
i Не можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «Моделирование на языке SystemC в процессе разработки протоколов передачи данных»

МОДЕЛИРОВАНИЕ НА ЯЗЫКЕ SystemC В ПРОЦЕССЕ РАЗРАБОТКИ ПРОТОКОЛОВ ПЕРЕДАЧИ ДАННЫХ

Аннотация. Описываются место моделирования в процессе разработки коммуникационных протоколов, возможности моделирования и те цели, которые можно достичь, используя модели на различных этапах разработки. Рассмотрен язык SystemC как наиболее адаптированный для написания такого рода моделей, имеющий преимущества перед другими языками; доказывается его состоятельность для моделирования и верификации.

Ключевые слова: моделирование, протокол, SystemC, верификация.

Abstract. Modelling takes more important role in the development process as a solution to perform detailed check of the specification and project verification to a stage of physical realization. Modelling could be applied to the different stages of system design and a number of different languages and modeling platforms can be used. The models could have different structures and internal logic. This article describes a role of modeling inside the systems development course, discuss the advantages and tricky points that the developer can face while using modeling approach. Besides, the article describes SystemC modelling language, which is a good solution for making models of the embedded networks.

Keywords: modelling, protocol, SystemC, verification.

Протоколом передачи данных называют набор правил, которые определяют обмен данными между различными устройствами и программными средствами. Протокол определяет временные характеристики сигналов и структуру передаваемых данных. Сетевые протоколы определяют также и правила взаимодействия устройств в составе сети.

Процесс проектирования протоколов является универсальной и многоплановой задачей, которая стремительно развивается. В настоящее время проектирование протоколов сопряжено с рядом трудностей, связанных с увеличением сложности проектов, повышением требований к надежности и потребляемой мощности изделий, работающих по данному протоколу, а также необходимостью завершения проекта в кратчайшие сроки. Традиционный маршрут проектирования не позволяет удовлетворить всем этим требованиям.

1 Проектирование протоколов передачи данных

При проектировании современных протоколов передачи данных и реализующих их сложнофункциональных систем используется маршрут, показанный на рис. 1. Он включает в себя несколько основных этапов:

- концептуальное проектирование: выбор направления разработки, исследование и анализ существующих средств и протоколов, разработка драфтовой версии спецификации;

- спецификация: получение финальной спецификации протокола, а также его моделей на языках высокого уровня (обычно на SystemC/SDL);

- логическое проектирование: трансформация исполняемой спецификации проекта на уровень регистровых передач (на языках Verilog/VHDL) и далее на вентильный уровень;

Рис. 1 Общий маршрут проектирования

- верификация: проверка протокола и различных решений на соответствие исходной спецификации и другим требованиям в процессе проектирования и детализации;

- физическое проектирование: начинается от выбора технологического и библиотечного базиса и заканчивается получением финального устройства, способного работать по данному протоколу [1].

Концептуальный уровень является критическим для оценки общих характеристик протокола и системы в целом. На этом уровне создается общая исполняемая спецификация, позволяющая исследовать и оценить различные варианты ее построения и выбрать оптимальное решение, которое будет реализовано в дальнейшем. Таким образом, имея исполняемую спецификацию, поведенческие модели и общую архитектуру, проектирование, верификация и топологическая реализация далее в какие-то периоды могут вестись параллельно.

2 Моделирование в процессе спецификации

Основной целью в процессе спецификации протокола является определение и спецификация основных его функций, а также создание исполняемой модели. Эта модель используется для верификации корректности работы протокола с функциональной точки зрения, а также для определения необходимых аппаратных ресурсов для его работы. Также ведется проверка самих механизмов и подходов, реализованных в спецификации, на корректность и совместимость [1]. Общий маршрут проектирования на данном этапе приведен на рис. 2.

Функциональное планирование протокола Определение сценариев работы Создание общих моделей протокола

Создание исполняемой модели Разработка алгоритмов Функциональное моделирование

Разделение проекта Анализ архитектуры Анализ требуемых ресурсов, производительности и т.п.

Проверка описаний протокола Описание интерфейсов Схемы арбитража

Рис. 2 Маршрут спецификации протокола

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

промоделировано поведение всей системы в целом или ее отдельных блоков. Моделируемые функции проверяются и оцениваются для получения оптимальных алгоритмов работы и функциональных решений с использованием различных видов оценок и критериев. Наконец, производится уточнение спецификации системы, при этом создается более детальное описание протокола и его реализации.

3 Моделирование в процессе верификации

Метод верификации моделей (или проверки на модели) применяется для верификации систем с конечным числом состояний. Обычно процедура верификации заключается в исчерпывающем обходе пространства состояний системы, для того чтобы выяснить, выполняется ли спецификация. При наличии достаточных ресурсов эта процедура всегда завершается ответом «да» или «нет». Более того, она может быть реализована достаточно эффективным алгоритмом, способным работать на вычислительных машинах невысокого класса. Метод верификации моделей применим ко многим важным классам вычислительных систем, к каким относятся и коммуникационные протоколы [2].

Функциональная верификация занимает все более важное место в общем маршруте проектирования. Если раньше верификация проводилась средствами логического моделирования, то сейчас верификация начинается на поведенческом уровне, на стадии разработки общей спецификации проекта. Основными требованиями, предъявляемыми к составу средств функционального проектирования и верификации, являются:

- анализ производительности и других системных параметров проектируемых протоколов;

- возможность совместной разработки и верификации аппаратуры и встроенного программного обеспечения;

- единая среда проектирования;

- наличие библиотек и высокоуровневых конструкций для функциональных блоков и коммуникационных каналов;

- средства управления данными и документирования проектов.

На высших уровнях представления используются языки C/C++/ SystemC. Для моделирования кода C/C++ используется встроенное ядро моделирования, которое осуществляет планирование и исполнение модели в соответствии со структурой и поведенческими функциями проекта вместе с программными объектами [1].

4 Моделирование в процессе тестирования устройств

Важным этапом в процессе разработки протоколов является тестирование реально воспроизведенных систем на платах при помощи точной компьютерной модели, описывающей стандарт в соответствии с его спецификацией. Такой метод в последнее время все чаще используется для окончательной проверки спецификации, тестирования алгоритмов и работы системы. Для этого выходы уже существующей модели посредством вычислительных машин соединяют с входами на реальной плате и наоборот. Таким образом, разработчики получают возможность программно задавать различные режимы работы модели и тестировать реакцию удаленного устройства и его работу в составе как сети point-to-point, так и сети полноценной.

В данный момент не существует стандарта или алгоритма, которым можно руководствоваться при написании подобных «тестеров». Поэтому существующие в данный момент решения разнообразны, но по сути своей в их основе лежит похожая логика. Тестер может контролировать все, что идет из вышестоящего уровня в нижестоящий, может самостоятельно отправлять данные напрямую в нижестоящий уровень, может просто вести лог событий и вообще быть в отключенном виде, просто пропуская через себя данные. На самом верхнем уровне располагается программное обеспечение, посредством которого имеется возможность генерировать тесты для нижележащей модели на выбранном языке программирования. Пройдя через всю модель, данные выходят из Тестера через среду взаимодействия. Этим термином обозначен любой объект моделирования, позволяющий Тестеру взаимодействовать с другими объектами. Это может быть порт, SAP, канал, FIFO и т.п. Данные из среды взаимодействия можно направить на еще один экземпляр такого Тестера для проверки взаимодействия двух моделей либо напрямую на плату для тестирования готового продукта или его прототипа.

Таким образом, подобный Тестер позволяет проверять модель на соответствие спецификации, может генерировать данные, посылая их напрямую в любой из слоев. Могут быть сгенерированы ошибочные данные и проверены схемы отработки ошибочных ситуаций. Разработчики получают возможность тестировать готовую плату или ее прототип и таким образом вести процесс моделирования и разработки устройства параллельно. В Тестер могут быть включены механизмы для выявления производственных дефектов на плате.

Примером подобного тестера служит программное окружение, разработанное для тестирования стандарта SpaceWire (рис. 3,а). Данный Тестер предназначен для генерации и внесения ошибок в информацию, проходящую через уровни модели. Таким образом, исследуется реакция удаленной стороны на различные ошибки и проверяются механизмы обработки ошибок [3].

Другим примером может служить Тестер, разрабатываемый для тестирования протоколов в Nokia (рис. 3,б). Он разработан для тестирования SystemC модели протокола и подсоединенного к нему устройства. Здесь, в отличие от предыдущего примера, введены промежуточные уровни, через которые ведется управление и контроль. Эти уровни имеют сервисные точки доступа как к самим уровням протокола, так и к тестеру [4]. За счет этих промежуточных уровней Тестер способен не только генерировать ошибки, но и управлять слоями, тестировать каждый слой отдельно и контролировать данные, проходящие между ними.

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

5 Язык SystemC и его преимущества

Моделирование с использованием SystemC в последнее время становится одним из наиболее эффективных методов для изучения, анализа или построения сложных систем, таких как стеки протоколов, сети с большим количеством узлов или системы на кристалле. SystemC - это C++ библиотека, которая используется для моделирования параллельных систем. Она дает возможность реализовать распределенное во времени моделирование за счет работы с событиями и сигналами времени.

Direct D, S forcing

Il и_____________44 II

Ошибка генерации бита________________

Генерация дополнительн ого символа

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *