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

  • Tutorial


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<2> data;  // 2-х битная переменная
data = 7;
cout << data;
Выведет в консоль: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_in<bool>         clk, rstn;
   sc_in<sc_uint<8> >  din;
   sc_out<sc_uint<8> > 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<bool>         clk, rstn;
   sc_in<sc_uint<8> >  din;
   sc_out<sc_uint<8> > 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_in<bool>         clk, rstn;
   sc_in<sc_uint<8> >  din;
   sc_out<sc_uint<8> > 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 <= #42 1;
	$display($time);
end
Выведет в консоль: 0

SystemC:
Переменные в C++ ничего не знают про симуляционное ядро SystemC и поэтому ведут себя привычным для C++ программиста образом. Для того чтобы промоделировать неблокирующее присваивание в SystemC используется специальный тип sc_signal, переменные этого типа далее называются сигналами:
sc_signal< sc_uint<2> > data;  // сигнал типа sc_uint<2> 
Любое присваивание значения 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 <= 0;
	end 
        else begin
    	    a <= a + 1;
	end
end
Этот пример описывает двоичный счетчик с асинхронным сбросом.

SystemC:
Процессы в SystemC создаются в конструкторе модуля. Тело процессов описывается в функциях-членах модуля. Тип процесса похожего на always блок из Verilog в SystemC называется SC_METHOD.
Рассмотрим примеры процессов, аналогичные приведенным ранее процедурным блокам на SystemVerilog:
Комбинаторная логика:
SC_CTOR(top) {
	SC_METHOD(comb_method);    // макрос для создания процесса типа SC_METHOD
	sensitive << b << c;   // список чуствительности  (аналог @(a or b) )
}

void comb_method() { a = b + c;  }        // тело процесса описывается в функции-члене 
Последовательностная логика:
SC_CTOR(top) {
	SC_METHOD(seq_method);  // макрос для создания процесса типа SC_METHOD
	sensitive << clk.pos() << arst_n.neg();  // список чуствительности ( @(posedge clk or negedge arst_n)  )
}

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 <= 0; 
    else 
        tmp <= {tmp[6:0], sin}; 
end

assign sout = tmp[7]; 

endmodule

Код на SystemC
// Для сигналов и портов используется инициализация в стиле C++11

SC_MODULE(shift_reg) {
    sc_in<bool> clk{"clk"}, sin{"sin"}, reset{"reset"};
    sc_out<bool> sout{"sout"};

    SC_CTOR(shift_reg) {
        SC_METHOD(shift_method);
        sensitive << clk.pos() << reset.pos();
        // т.к. непрерывного присваивания нет, приходится создавать процесс
        SC_METHOD(sout_method);         
        sensitive << tmp; 
     }

private:

    sc_signal <sc_uint<8> > tmp {"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_uint<32> raw_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 <= 1;    
    @(posedge clk); // ожидание события не синтезируется
    out <= 2;
    @(posedge clk); 
    out <= 42;
end
В Verilog мы должны явно специфицировать регистр состояния, который будет определять поведение процесса на каждом такте. Синтезируемым аналогом предыдущего примера кода будет следующий процесс:
logic [1:0] state;  

always @(posedge clk or negedge reset_n)
begin
    if ( ~ reset_n)
	state <= 0;
	out <= 1;
    else 
case (state)
    0: begin
    	state <= 1;
        out <= 1;
    end
   1: begin
	state <= 2;
        out <= 2;
    end
    2: begin
	state <= 0;
        out <= 42;
    end
end
В 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 <= 1;
      wait(); 

     // в отличии от SC_METHOD, SC_CTHREAD не должен завершаться никогда
     // поэтому в теле процесса всегда есть бесконечный цикл
     while (1) { 
           out.write(1);
           wait (); // ожидание переднего фронта на clk

           out.write(2);
           wait (); // ожидание переднего фронта на clk

           out.write(42);
      }
}
На первый взгляд польза от наличия таких процессов не очевидна. В конце концов не так уж и сложно явно закодировать переменную для состояния цифрового автомата (переменная 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 нам ничего не нужно делать руками — ведь можно просто воспользоваться высокоуровневым синтезом!

Высокоуровневый синтез.


Высокоуровневый синтез это процесс трансформации алгоритмического кода написанного на высокоуровневом языке программирования в цифровую аппаратуру его реализующую. На вход HLS пакета подаются:
  • Исходный код. Иногда его называют 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

Опытные пользователи средств логического синтеза знают что некоторые из них (например Deisgn Compiler) обладают похожей функцией, которая называется ретайминг (retiming). В сравнении с ретаймингом HLS обладает несколькими преимуществами:
  • Не требуется руками специфицировать регистры, логику остановки конвейера.
  • HLS позволяет переключаться между несколькими микроархитектурами не меняя исходный код.
Еще одной интересной особенностью HLS является работа с памятью. Абстракцией памяти в HLS является обычный массив. От нас лишь требуется указать синтезатору библиотеку доступных в техпроцессе памятей. Например, можно переделать наш пример таким образом, чтобы результат не отправлялся по шине AXI, а записывался напрямую в память:
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++, то части исходного кода можно переиспользовать между синтезируемым кодом, референсной моделью, виртуальным прототипом и драйвером операционной системы. Но рассказ обо всём этом достоин отдельной статьи.
Поделиться публикацией

Комментарии 28

    +1
    Хабр снова торт! Спасибо большое за статью.
    Сталкивался с SystemC во время учебы, и тогда он показался достаточно интересным, но проблемы с открытыми (или хотя бы дешевыми) утилитами для синтеза останавливали. Видно, что с тех пор прогресс ушел далеко вперед, но утилит таких по прежнему нет — печально.
      +1
      Ряд мыслей по поводу сравнения:
      • Всю семантику процессов/списков чувствительности/блокирующих-неблокирующих пришлось перенести, переписав другими словами
      • Пользовательские типы и операторы — это все есть в VHDL с самого рождения. То, что в веририлоге не осилили — печально
      • Последовательные автоматы. Чисто теоретически ничто (и никто) не запрещает синтезировать конструкцию вида

        always 
        begin
            @(posedge clk)
            out <= 1;    
            @(posedge clk); // ожидание события не синтезируется
            out <= 2;
            @(posedge clk); 
            out <= 42;
        end
        


        Жаль, что нынешние синтезаторы не умеют (а может какие-нибудь продвинутые умеют?), иногда это было бы удобно. Сами же возможности языков что verilog, что VHDL подобную возможность не ограничивают
        +1
        • Да, весь этот инструментарий используется в SystemC для написания различных signal-level интерфейсов.
        • К сожалению я видел своими глазами, как людей заставляли переписывать VHDL в SystemVerilog, чтобы соответсвовать корпоративным стандартам. VHDL сложнее чем Verilog, поэтому Verilog программисты плодятся быстрее и постепенно убивают VHDL. А разработчиков на SystemC можно получать путём тренировки хороших C++ программистов.
        • Да, сами языки не ограничивают возможость высокоуровневого синтеза с них. Один из первых HLS — behavioral compiler синтезировал Verilog и VHDL. Но не взлетел. Синтез с C/C++ завоевал себе небольшой рынок прежде всего потому, что алгоритмические модели часто пишутся на C/C++. Поэтому можно к примеру какой-нибудь VP9 кодек переписать из софта в хард не меняя языка разработки. Я читал что Google ровно так и делал, используя Catapult C для синтеза
        0
        А подскажите, какую литературу порекомендуете по SysC. в сети лишь примитивное описание, вроде «создадим вентиль или-не»
          0
          Лучше всего начать с изучения C++ и основ Verilog (Verilog всё равно вам пригодится). А потом просто заказать тренинг от SystemC вендора. После тренинга можно пользоваться стандартом SystemC как референсом.
            0
            Ну, мне как любителю пишущему на плюсах это немного затратно, кмк. Мне систС нужен как конструктор для симуляторов, с удобным интерфейсом на TLE2.0. Через скрипты и прочее. Для того, чтобы не изобретать свои примитивы. А использовать удобную библиотеку.
              +1
              Понятно, т.е. вас прежде всего интересует вопрос создания виртуальных прототипов как хобби. В таком случае SystemC вам возможно не нужен, можете всё писать на чистом C++.

              SystemC+TLM2.0 превращается в конструктор симуляторов только если у вас есть доступ к готовым блокам. А бесплатно эти блоки увы никто не раздает.

              Если в ваших проектах вы дойдете до уровня когда вам потребуется моделировать систему с несколькими активными сущностями (Несколько процессоров, процессор и видеокарта, процессор и DMA контроллер) тогда да, есть смысл взглянуть на SystemC. Возможно где-то в сети есть готовая opensource платформа на SystemC, тогда можно было бы разобраться на готовом примере. Не знаю правда есть ли что-то подобное в сети.

              В комплекте с коммерческими EDA пакетами такие примеры есть (например в Virtualizer), я разбирался в TLM2.0 на этих примерах.

              Про TLM-2.0 есть tutorial в открытом доступе. Его правда достаточно сложно понять. Он скорее подходит для того чтобы освежить всё в памяти, а не для обучения с нуля.
              www.doulos.com/knowhow/systemc/tlm2/tutorial__1
                +2
                Мне тоже всегда хотелось написать свою модель процессора на C++. Но, к сожалению, не хватает на всё времени.
                  +2
                  спасибо огромное за столь развернутое объяснение. Я лишь пишу модели для Proteus на Lua, и хотел прикрутить SystemC ради более выразительного описания процессоров, нежели разрозненные модули выдумывая каждый раз что-то новое.
                  Я обратил внимание на QBox, он имеет уже интерфейс к Qemu, TLM 2.0
                  И вот даже готовые модули, к примеру, Cortex-M3

                  У меня всё очень любительское, но эту тема мне очень интересна
                    0
                    Спасибо за ссылку на GreenSocs/QBox, не знал про них. Посмотрел на исходники Cortex-M3 платформы: там уже не чистый SystemC/TLM, а более хитрая библиотка работающая поверх SystemC.
                    Наверное разбираться во всём этом как хобби это уже слишком, надо хобби превращать в работу за деньги.
                      0
                      Говорят, you cannot teach an old dog new tricks, но чем чёрт ни шутит. В любом случае, спасибо за статью!
                        0
                        Есть русскоязычная версия этого выражения «поздно учить старую обезьяну новым фокусам».
            0
            Спасибо, интересная статья! Хотелось бы добавить, что в Vivado HLS от Xilinx большую роль играют директивы, с помощью которых можно получать те или иные результаты по пропускной способности, максимальной задержке и ресурсам. Сначала отлаживается алгоритм в чистом виде, без привязки к железу, а затем путем вставки директив в нужные места, идет борьба за ресурсы и скорость. Насчет handshake/axi вы правы, Xilinx может создать монстрообразную конструкцию с кучей лишних портов, которые потом придется на RTL уровне соединять. Но весь этот процесс можно обуздать. В любом случае, вендоры правильно поступают, привклекая Сишников в разработку на FPGA, особенно с учетом того, что современные кристаллы настолько огромные, что забить его чистым RTL-кодом не так-то и просто :)
              0
              В статье я называл эти директивы «спецификацией микроархитектуры». Они присутсвтуют во всех HLS.
              +1
              Толково написано, спасибо.
              При этом нельзя сказать, что SystemC на этом рынке является доминирующим решением: существуют и продукты, SystemC не использующие, например WindRiver Simics.

              Как один из разработчиков Simics хочу уточнить это утверждение.

              Действительно, функциональные (т.е. не потактовые) модели устройств в Simics пишутся на языке DML (device modeling language), который наиболее хорошо выражает типичные функциональные особенности (банки регистров, порты, карты памяти и т.п.). Однако существующую огромную IP-базу моделей, написанных на SystemC, нельзя игнорировать. Поэтому существует возможность интеграции такого кода в модели Simics. Самое интересное при этом — это сопряжение различий в парадигмах моделирования времени в SystemC и Simics.
                0
                Можно ли запускать Simics как slave в SystemC симуляции? В этом случае мы могли бы например использовать софт бегущий на simics для тестирования RTL. К примеру, использовать драйвер сетевой карты для тестирования RTL модели сетевой карты :)
                  0
                  Я не уверен насчёт деталей master-slave отношений, которые сейчас доступны в Simics. Текущие усилия по интеграции всё-таки были сконцентрированы на переиспользовании существующего кода, чтобы максимально быстро получить модель полной платформы. Хотя нужно понимать, что производительность при этом будет страдать. SystemC модели слишком точные и медленные по сравнению с «родными» функциональными.

                  > К примеру, использовать драйвер сетевой карты для тестирования RTL модели сетевой карты
                  Чаще бывает наоборот — драйвер к моменту наличия модели (SystemC/DML — неважно) ещё не готов или совсем ещё плох. Но в принципе, я думаю, и Ваш сценарий на практике кем-то жа был уже опробован.
                    0
                    По поводу слишком медленных и точных SystemC моделей: всё зависит от того как писать. По хорошему, Untimed модель должна быть на 99% на чистом C++ и минимально использовать фукнции SystemC вызывающие переключение контекста. В идеальном случае переключение контекста нужно только для того чтобы сгенерировать прерывание.

                    Чаще бывает наоборот — драйвер к моменту наличия модели (SystemC/DML — неважно) ещё не готов или совсем ещё плох. Но в принципе, я думаю, и Ваш сценарий на практике кем-то жа был уже опробован.

                    Ну идея в том чтобы писать железку и драйвер одновременно. Hardware/Software Codesign, о необходимости которого всё время говорили большевики.
                    Мы сейчас пытаемся вставить в RTL симуляцию C++ную модель процессора на которой бежит реальное firmware.
                      0
                      Хотел кстати спросить, многопроцессорные системы в Symics симулируются в одном треде операционной системы, или можно запустить каждый процессор в отдельном треде?
                        0
                        В последней версии Simics 5 некоторые модели архитектур ЦПУ x86, ARM, ppc могут исполняться параллельно даже при работе с общей памятью. Если общей памяти не шарится (например, моделируются несколько плат, соединённых по сети ), то такие части можно запускать в параллель, и это работает уже давно.
                          0
                          Гриша Atakua а зачем в одной конфигурации замешивать x86, arm или powerpc? Был реальный usage-case?
                            +1
                            Я имел в виду, что если в модели есть несколько ядер x86, то в пятой версии симулятора они могут быть помещены в домен с общей памятью и при этом симулироваться параллельно. Не для всех моделей разных архитектур эта возможность сейчас поддерживается, т.е. скажем, модель процессора от MMIX (гипотетическая многоядерная) в Симиксе из коробки будет работать только в квотированном режиме на одном хозяйском процессоре. Добавление возможности параллельной симуляции для нового процессора — непростая вещь, и, что немаловажно, далеко не все сценарии симуляции получат от этого выигрыш. Например, при работе системного кода (UEFI, firmware, ...) часты доступы к устройствам, код IO-bound, и от параллелизации толку нет.

                            > зачем в одной конфигурации замешивать x86, arm или powerpc? Был реальный usage-case?

                            В современных платформах очень часто вместе с центральным процессором от IA-32 идут контроллеры (security, сенсоры, управление питанием) от иных вендоров или не с архитектурой x86.
                  0
                  Так и не нашел для себя ответ на вопрос:
                  Вы описываете как аналогичные конструкции SV выглядят на SystemC, но при этом этом для синтеза все равно используется HLS-синтезатор.
                  Это выглядит как прослойка в виде SystemC, чтобы иметь возможность написать SV код в C++ для синтеза.
                  Почему от этого не отказываются в пользу чистого HLS-синтеза с директивами (или OpenCL), но без специальных классов SystemC, которые опускают программистов на низкий уровень абстракции SV?
                    0
                    Вы правы, в одном из юзкейсов SystemC можно рассматривать как прослойку, позволяющую написать SV код в C++ для синтеза. Это нужно для того чтобы склеивать HSL блоки с SV модулями. Альтернативный подход — использовать некоторый стандартный набор сигнальных интерфейсов в HLS, а в случае если нужен нестандартный интерфейс, конвертор можно написать в SV. Vivado HLS как раз поддерживает такой подход.

                    Но у синтезируемого SystemC есть еще одна важная область использования: спецификация параллелизма на уровне процессов. SystemC это как pthreads для моделирования аппаратуры.

                    Проще всего объяснить на примере: представьте что вам нужно написать out-of-order процессор. Тогда у вас есть процесс который накачивает данные, есть процесс-плнировщик, набор процессов для исполнения (integer и floating point), процесс с reorder buffer'ом и сохранением результатов. Исполняться они будут параллельно. SystemC позволяет явно специфицировать каждый процесс и соединить их с помощью каналов. Причем с нулевыми накладными расходами (всё как в SV).
                    +4
                    Напишу свой пост 2012 года с критикой SystemC. Глядя назад, пост слишком энтузиастичен по поводу UVM, но мое мнение про SystemC сейчас то же, что и в 2012 году:

                    panchul.livejournal.com/203346.html

                    Вот позавчера мне пришло письмо от джентлемена из одного российского университета, который занимается верификацией дизайнов на VHDL с помощью тестов, написанных на SystemC. Джентлемен спрашивает, имеет ли смысл транслировать тесты из SystemC в VHDL с помощью какого-нибудь third-party тула, наподобие тула от британской компании Celoxica, который использовали его коллеги. В качестве симулятора они, насколько я понял, используют ModelSim, который входит в состав Altera Quartus.

                    Мой ответ:

                    1. Disclaimer

                    Я обычно пишу такие ответы довольно уверенным тоном, но мой тон не означает, что я пытаюсь навязать свое мнение как истину в последней инстанции. Вашему научному руководителю может быть виднее, а для студентов полезно экспериментировать с разнообразными технологиями, даже если подобные эксперименты были не очень удачны в прошлом. Тем не менее, мое мнение может быть для вас полезным, так как я в свое время заседал на заседаниях, на которых присутствовали и маркетеры SystemC, которые потом перестали со мной здороваться. Кроме этого, я должен предупредить, что так как я пишу блогпост в Живом Журнале, а не статью для СМИ, я не буду перепроверять все свои утверждения, а также не буду делать расследование текущего состояния тех технологий, за которыми я перестал следить. Учитывая, что речь пойдет о развитии языковых средств за последние 15 лет, я могу запросто сказать что-нибудь не то.

                    2. Кратко

                    С моей точки зрения, SystemC всегда был и остается неудачной технологией, подерживаемой на плаву маркетингом различной степени недобросовестности. Несмотря на то, что некоторые группы в Европе в начале 2000-х годов стали использовать SystemC для системного моделирования, сейчас многие индустриальные команды стараются избавиться от кода, написанного на SystemC и перевести всю верификацию на SystemVerilog. Это связано с тем, что SystemC не ликвидировал неудобства, связанном с его использованием (см. ниже), а также не смог накопить критическую массу средств для functional-coverage based constraint random verification methodology — методологии, которая появилась в языках для верификации e/Specman, OpenVera и SystemVerilog, и которая стала в последние годы мейнстримом. Кроме этого, сейчас в индустрии приобретает все большую популярность Universal Verification Methodology (UVM), основанная на SystemVerilog. Так как эта методология еще год назад считалась нестабильной, существует множество мелких возможностей для создания разнообразных стредств автоматической верификации и тулов для верификационных инженеров на платформе UVM. Несмотря на то, что UVM критикуют за излишнюю сложность, эта все еще открытая ниша может эксплуатироваться как университетскими группами, так и небольшими компаниями.

                    <lj-cut text=«Дальше»>
                    3. История и проблемы с SystemC

                    3.1 Начало

                    В начале 1990-х годов произошло распостранение С++. После этого в середине 1990-х годов одновременно у нескольких людей возник зуд имплементировать HDL в виде библиотеки С++ классов, используя переопределение операций (overloading), чтобы обыкновенное присваивание вызывало скрытое обращение к event-driven simulation engine. Этот зуд возник у партнера (забыл имя) Джона Сангвинетти, двух немецких студентов, джентльменов из бельгийского исследовательского центра IMEC и лично меня.

                    Лично у меня идею идти этим путем обломал некий маркетер в Mentor Graphics, где я в то время работал, и я написал впоследствие свой тул для трансляции из С в Verilog, используя другую идею, а также сделал технологию CycleC, которая строилась на cycle-driven simulation, а не на event-driven simulation, как у SystemC (см. http://en.wikipedia.org/wiki/C_Level_Design).

                    Джон Сангвинетти и компания создали компанию C2DA / CynApps, которая впоследствие слилась с Forte. Сначала C2DA сделали библиотеку классов, которая напоминала SystemC, но была более элегантной. Впоследствие им пришлось под давлением от индустриальной политики отказаться от своей библиотеки и использовать SystemC в качестве входного языка.

                    Джентлемены из IMEC создали странную технологию, при виде которой у хардверных инженеров соловели клаза и они погружались в кому. Классы в C++ вообще вызывали у хардверных инженеров отторжение, но в случае с IMEC-ом они еще и как-то хитро генерировали HDL код во время симуляции, или что-то в таком духе, забыл идею.

                    Двое немецких студентов решили реализовать VHDL в C++. У маркетинга Синопсиса сработал условный рефлекс на слово «Си» и SystemC был рожден.

                    Кроме вышеперечисленных, в период 1996-1999 в мире появилось еще пара компаний, которые разными способами транслировали (или делали вид, что транслировали) С в хардвер — Handel-C/Embedded Solutions/Celoxica и Frontier Design. У всех претендентов были похожие слайды и похожий объем венчурного финансирования, хотя продавали все компании разное. Ни у одной из компаний не было уверенного успеха. У C Level лицензии приобрели Hitachi и Fujitsu, у CynApps и Frontier — еще кто-то, ESL/будущая Celoxica выпустила прес-релизы, что они «разослали свой С-подобный продукт в 50 университетов». Мнение индустрии (точнее, слухи, распускаемые Синопсисом) были «это все фигня, вот сейчас выйдет Синопсис и всех уроет».

                    3.2 Выход в свет и первые реакции

                    И вот в начале 1999 года Synopsys выкатил на обозрение то, чем они собирались всех урыть — первой версией библиотеки классов SystemC, которая реализовала классы, имплементирующие модули, порты и сигналы. Несмотря на то, что перед презентацией был огромный ажиотаж, показанное вызвало разочарование у многих инженеров.

                    Дело в том, что большинство хардвер-инженеров в 1999 году под словом «C» имело в виду тот ANSI C, который им когда-то преподали в коледже для писания простых алгоритмов, а не C++ с классами, темплейтами, виртуальными функциями и overloading. Все эти красоты хардверные инженеры видали в гробу, посколько это не входило в сферу их проблем — как организовать конвейер, оптимизировать тайминг и т.д. До презентации многие хардверные инженеры думали, что Synopsys покажет им тул, который магически траслирует untimed алгоритмы, написанные на бедненьком простеньком С в отпимизированных хардвер. В Synopsys верили — ведь за 10 лет до этих событий они организовали революцию в области логического синтеза.

                    «Прохладная встреча — ну и ладно», — подумали синопсоиды. «Мы сейчас как рррразвернем нашу маррркетинг-мышцу и заставим инженеров принять наш тул через их головы, впаривая его их менеджерам».

                    Это план не прошел. Инженеры реагировали так, как будто их пробует изнасиловать представитель несовместимой с ними сексуальной ориентации. «Ни за какие деньги!!!»

                    Причин у такой реакции было несколько:

                    1. Без хорошего понимания C++ пользователи SystemC постоянно налетали на ошибки компиляции, связанные с overloading в сложноустроенных templated классах для сигналов и портов разной ширины. Эти ошибки были так же далеки от их жизни, как general protection fault от феминистических статей Натальи Радуловой. Хардверные инженеры не собирались ради этой хрени учить нюансы C++ и тем более разбираться с кишками библиотеки SystemC. Чтобы бороться с таким явлением, всевозможные SystemC консультанты стали предлагать писать конструкции с аксессорами типа вместо «a = b + c» писать «a.write (b.read () + c.read ())». Это выглядело неубедительно, особенно для пользователей Verilog-а, хотя пользователи VHDL уже были в прошлом слегка изнасилованы конструкциями типа «a <= std_logic_vector (resize (signed (b), 9) + resize (signed , 9))», поэтому им было не впервой.

                    2. Еще более сильное неприятие происходило, когда пользователи (хардверные инженеры) получали GPF внутри SystemC библиотеки. Они потом бежали обратно в их родной Верилог и VHDL так, что только пятки сверкали.

                    3. Код на C++ очень медленно компилировался, особенно без precompiled headers, которые были в Microsoft DevStudio под Windows, но не были в GCC на Sun Solaris (тогдашней главной EDA платформе).

                    4. Интерфейс между SystemC и HDL не был продуман; даже много лет спустя он выключает кучу оптимизаций в Synopsys VCS и замедляет смешанное выполнение

                    5. Код на SystemC симулировался медленнее (а не многократно быстрее, как ожидалось и обещалось ранее), чем аналогичный код на HDL для компилирующего симулятора — Synopsys VCS или Cadence NC.

                    6. Особо долдонистые синопсоидные маркетеры придумали Гениальную Идею — они решили сделать компилятор с SystemC как прибамбасу поверх так называемого Behavioral Compiler — тула для высокоуровневого синтеза, который позволял писать «С-образный» алгоритмический код, разбавляя его явными указаниям «f (a, b); @ (posedge clock); g (b.c)» (в VHDL — wait). Реальные инженеры середины 1990-х рассматривали Behavioral Compiler как дорогую игрушку, которой было трудно управлять (раскладывать арифметические операции по разным clock cycles и т.д.)

                    7. Короче говоря, синопсоиды скомбинировали два непопулярных тула и получили что-то невероятно отвращающее! (И это сделал индустриальный лидер!)

                    3.3 Что делать?

                    Несмотря на такую реацию первых пользователей, синопсоидный маркетинг не моргнув глазом объявил «Ну вот и вышел Стандарт! Вся индустрия Выстраивается за Нами!» То, что они объявили себя стандартом в момент, когда они не заработали на SystemC ни цента, их не смущало.

                    Тем не менее, так как у Synopsys-а была на кону репутация, им нужно было придумать, как выкрутится из ситуации. Прежде всего они объявили, что этот тул не для RTL designers, а для system designers. Так как system designers в десять раз меньше, чем RTL designers, это вывело тул из прямого огня критики.

                    Потом они сказали, что симуляция медленная, потому что типа так и нужно — у симуляции низкий RTL уровень и «его нужно повысить». Что такое «уровень выше, чем RTL», не совсем понятно даже сейчас, поэтому под «повышением уровня» они решили в SystemC чего-нибудь добавить. Добавить получилось за счет технологий, придуманных стартапами. Пришел бельгийский Frontier Design и принесли в общую копилку библиотеку классов для fixed-point арифметики, сделанную для DSP-приложений (Digital Signal Processing).

                    Потом пришла компания CoWare (тоже с корнями из Бенилюкса) и внесла в копилку свои идео о «высокоуровневых каналах», которые эта компания пробовала внедрить в индустрию с помощью странного тула, который назывался чего-то типа «от салфетки — к системе». Идея была в том, что люди рисуют какую-то фигню на салфетке, сидя в ресторане (iPad-ов в 1998 году еще не было), и тул магически превращает это в дизайн. На самом деле, называть то, что у них было «тулом» — это преувеличение, CoWare продавала не тул, а сервис — их инженеры шли в компании типа Sony, которым объясняли, как делать интерфейсы для их систем.

                    Синопсису удалось втянуть в консорциум и Джона Сангвинетти с его CynApps-ом.

                    Также оставался мой стартап C Level Design, который умудрился заработать около 3 миллионов на конкурирующей технологии CycleC, когда большая компания Synopsys не заработала ничего. Это попало в прессу — известный техноблоггер John Cooley написал:

                    http://www.deepchip.com/items/dac01-08.html

                    ( DAC 01 Item 8 ) — [ 7/31/01 ]

                    Subject: Forte/CynApps, C-Level, CoWare, SystemC, Frontier, Y Explorations

                    THE NEXT GENERATION: On two levels, Synopsys has been horribly embarrassed
                    in pioneering SystemC as the «wave of the future». The first embarrassment
                    came from the poltical fiasco around getting SystemC out as a supposedly
                    «independent» standard. Too many people kept walking out loudly saying
                    SystemC was a Synopsys scam. And this happened multiple times! Ouch. The
                    second embarrassment for Synopsys here is, after all this grief, C-Level's
                    «System Compiler» is beating the crap out of Synopsys's «SystemC Compiler»!!!
                    Three months ago at SNUG'01, Dan Joyce of Compaq reported the first C-Level
                    tape-out. Below, I have 6 more designers reporting either that they're
                    using C-Level «System Compiler» for current designs or that they've already
                    taped out with C-Level. In contrast, there's only 1 guy around talking
                    about using Synopsys «SystemC Compiler» for a design *plus* a lot of other
                    user letters saying how they tried but couldn't get SystemC to work! Ouch!

                    Forte/CynApps' special CynLibs flavor of C classes seems to be capitulating
                    to the massive marketing arm of Synopsys SystemC. They're talking about
                    'merging' their CynLibs with SystemC.

                    Frontier, Y Explorations, and CoWare seem to be forgotten sideshows here.
                    Out of the 182 customer responses I got for this report, these 3 companies
                    were only mentioned once (each).


                    Там по ссылке еще много интересных мнений.

                    Из-за этого в конце 2001 года, когда в C Level Design закончились деньги, а Бин Ладен разрушил финансовые рынки, Synopsys купил assets C Level Designs, придушив заодно неприятный для них источник шума.

                    Пиар пиаром, но еще через несколько месяцев до менеджемента Synopsys-а дошло, что с SystemC они крупно проиграли. В 2002 году синопсисовская команда SystemC была расформирована, инженеры разошлись в группы Design Compiler, VCS и другие, и знамя SystemC подхватил Cadence.

                    3.4. SystemC после Synopsys

                    После 2002 года с SystemC произошло несколько изменений.

                    1. Появились некие группы людей, которые стали использовать SystemC в системном моделировании, в частности в Европе. Возможно сыграло роль обстоятельство, что конкурирующая технология (SystemVerilog) в начале 2000-х была неразвита. Возможно, пользователи оказались привлечены возможностью использования сложных структур данных (struct, class, mailbox, queue), полная поддержка которых не сразу была добавлена в SystemVerilog для Synopsys VCS, Cadence NC-Verilog и Mentor ModelSim. Сейчас сложные структуры данных адекватно поддерживаются во всех основных симуляторах, включая вероятно и бесплатеный урезанный ModelSim, поставляемый с Altera Quartus II (мне нужно проверить).

                    2. Появилась технология TLM (Transaction Level Modeling). TLM 2.0 — одна из самых странных технологий, которых я видел в жизни. Я так и не понял, кому и зачем она нужна. Её корни произошли вероятно от CoWare и Synopsys и родились от желания «а давайте придумаем новый уровень (level), чтобы объявить SystemC более высоким уровнем». В TLM 1.0 сигналы группируются в «каналы», а в TLM 2.0 вводятся некие транзакции, которые по факту являются просто структурами а-ля C struct или С++/SystemVerilog class, но эдакими «непростыми» структурами. То есть, для того, чтобы добавить в структуру «generic payload» поле, нужно не добавить поле и даже не создать inhereted class, а использовать какой-то тягомотный API. Причем это перенесли из SystemC в SystemVerilog. При виде такого я долго гадал, зачем это может быть нужно и решил, что такие структуры можно наверное автоматически перемещать между симуляторами (SystemC -> SystemVerilog и наоборот). Год назад на конференции в Сан-Хозе ( http://dvcon.org/ ) я узнал, что они это собирались сделать, но так и не сделали.

                    3. Появилась библиотека классов SystemC Verification Standard (SCV), авторы которой по-видимому решили имплементировать в SystemC всякие технологии из SystemVerilog, но потом мне сказали, что SCV больше не поддерживается

                    3.5. А все-таки, для чего нужен SystemC?

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

                    1. Уже упомянутые сложные структуры данных — проблема разрешена

                    2. Возможность «невидимого» интерфейса C и Verilog в виде instantiation модуля на SystemC внутри SystemVerilog. ИМХО для большинства задач, где нужно сделать интерфейс между C и Verilog, гораздо проще использовать интерфейс DPI, в котором данные просто передаются с помощью вызова функции. Это гораздо проще, чем организовать код для модуля SystemC. Более того, насколько я понимаю, внутри интерфейса SystemC — SystemVerilog используется все тот же DPI.

                    3. Возможность вставить в код на C аналог верилоговского "#delay" или VHDL-ного «wait», используя SC_THREAD. Эта сильная feature, и её использую для некоторых применений даже я сам, при всей нелюбви к SystemC. Тем не менее, её использовать необязательно — при правильно организации кода для смешанной симуляции C / Verilog можно обойтись просто вызовами DPI функций.

                    4. Отсутствие доступа к симулятору, адекватно поддерживающему SystemVerilog. Нужно проверить, поддерживает ли SystemVerilog урезанный ModelSim, поставляемый с Altera Quartus II. Если нет, можно сконтактировать российские представительства Synopsys, Cadence и Mentor Graphics или например http://univeda.ru/node/4

                    5. Может быть соображение «Ну мы же работаем с VHDL, зачем впрягать для верификации модулей на VHDL — SystemVerilog?» Почему бы и нет? На конференции DVCon в 2011 году выступал военный контрактор (а VHDL является стандартом для Пентагона) и высказывал ровно то, что так как в VHDL нет всяких штучек для верификации, которые есть в SystemVerilog, то он использует именно такую комбинацию.

                    Отступление: Еще встречается комбинация VHDL + e/Specman. Specman — средство для верификации, которое любимо своими пользователями. К сожалению, судя по динамике киг в книжном магазине «Digital Guru» в Саниивейл, Specman потихоньку сходит со сцены — если книги по SystemVerilog быстро и уверенно расходятся, то пара книг по Specman стоят месяцами. Такое впечатление, что народ читает их в книжном магазине и кладет на полку, отчего одна книга стала засаленной.

                    4. Альтернатива — SystemVerilog и UVM

                    Я не буду особенно долго агитировать за SystemVerilog, посколько про него есть куча текстов а интернете, например на testbench.in. Тем не менее, я скажу, зачем нужна ключевая для SystemVerilog технология functional-coverage based constraint random verification.Эта технология, несмотря на то, что в её названии есть слово «случайная» — приносит в процесс тестирования плановость и предсказуемость. До тестирования verification engineer описывает сценарии, которые должен покрыть тест, используя coverage bins и другие стредства. Потом он определяет правила, по которым устройство (Device Under Test — DUT) бомбардируется псевдослучайными транзакциями (которые включают и направленные тесты — direct tests) в testbench, в которую также встроены средства для самопроверки. В процессе тестирования симулятор проверяет, были ли покрыты все интересные сценарии, описанные на своеобразном под-языке для спецификации coverage bins. Дальше симулятор составляет интерактивный report. При правильном применении, технология позволяет рационально ответить на вопрос «достаточно ли мы тестировали устройство»? Закончили ли мы?

                    Вот несколько книжек по теме SystemVerilog и Verification:

                    Очень хорошая книжка — SystemVerilog for Verification: A Guide to Learning the Testbench Language Features by Chris Spear and Greg Tumbush

                    Нудная, но есть полезные моменты — Writing Testbenches using SystemVerilog by Janick Bergeron

                    Хорошая книжка, но про верификацию там нет, и все это можно выучить их стандарта — SystemVerilog for Design Second Edition: A Guide to Using SystemVerilog for Hardware Design and Modeling by Stuart Sutherland, Simon Davidmann, Peter Flake, P. Moorby



                    5. А если все-таки транслировать тесты из SystemC в VHDL?

                    Если бы по тем или иным причинам мне бы понадобилось делать именно это, я бы не использовал тул общего назначения. За текущим состоянием конкретно этих тулов я не слежу, а тулы этого типа из прошлого обычно генерировали кучу ненужных вещей. Я бы написал скрипт на скриптовом языке, который транслирует именно данный конкретный формат тестов из SystemC в VHDL. Хотя такое трудно расширить или сделать гибким, но для локальной задачи это может подойти.
                      +3
                      Спасибо за ответ, было интересно узнать про историю SystemC подробнее. Ведь я во время описываемых событий ещё учился в школе!

                      Попытаюсь выступить в роли адвоката SystemC и ответить на критику:)

                      1. Про сложность SystemC для среднестатического хардверщика: полностью согласен. С точно такой же критикой C++ выступают программисты на Си. C++ это наверное один из самых сложных языков программирования. Поэтому большинство инженеров использующих SystemC, которых я знаю, имеют C++ный бэкграунд (неторые даже не знают Verilog/VHDL).

                      2. Про время компиляции: какое оно имеет значение если время симуляции на порядок больше? Если говорить про простейшие модули, то на моём десктопе и SystemC и Verilog компилируются меньше секунды, без precompiled headers.

                      3. SystemC симулируется медленнее чем Verilog в компилирующем симуляторе.
                      Простой ответ: это проблема компилятора, а не SystemC. Технически, ничто не мешает сделать компилирующий симулятор для SystemC.
                      Правильный ответ: не нужно писать RTL на SystemC. На SystemC нужно стараться использовать untimed код и TLM интерфейсы.
                      Пример из жизни: занимаюсь сейчас обработкой видео: Синтезируемый SystemC обрабатывает один кадр примерно за 30 секунд, полученный из него RTL на Verilog — около 10 минут.

                      4. Про медленное смешанное исполнение SystemC и HDL. Не знаю, может и так. Но мы верифицируем SystemC без HDL примесей. А Full-chip симуляцию проводим на SystemVerilog, без SystemC.

                      5. TLM 2.0 — странная технология. Может быть. Но сама идея обмена данными через function-call'ы очень правильная. Никто вас не заставляет использовать TLM2.0, можно придумать свой велосипед с похожим функционалом.
                      Как мне кажется история с TLM2.0 ровно такая же как c UVM: они облегчают торговлю IP. В случае TLM-2.0 это IP для виртуальных прототипов. В случае UVM — это IP для верификации.

                      6. Альтернатива SystemC — SystemVerilog и UVM. Это мне кажется вопрос привычки: на SystemC вполне можно написать testbench в UVM стиле. Европейцы уже даже запилили UVM на SystemC. Рандомизировать пакеты тоже можно, и функциональный coverage мерить, и code-coverage тулы для C++ тоже есть.

                      Но на деле часто получается что пока ты пишешь свой суперрандомизированный тест, парни в соседней комнате уже подняли FPGA эмулятор и ОС запускают. А дальше приходится уже трейсы с железа снимать и писать директед тесты, чтобы понять почему оно повисло.

                      Но это конечно зависит от проекта. Не знаю как у вас всё устроено. Наверное в MIPS вы одно ядро можете десяток лет продавать и поддерживать, поэтому можете даже формальную модель написать, чтобы она вам без симуляции всё доказала.
                      0
                      А BlueSpec Вы не видели? Интересно с ним сравнить.
                        +3
                        Видел, но не пробовал. Хотя знаю людей которые его использовали для быстрого прототипирования архитектуры на FPGA. Отзывались положительно.

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

                        Я читал спеку на язык. BSV это описание аппаратуры в стиле функционального программирования c системой типов из Haskell. Как мне кажется, идея в целом правильная. Функциональное программирвоание хорошо ложится на вычислительную среду в виде массива триггеров/вентилей.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое