Использование языков программирования с или с++ для HDL описания – будущий стандарт или неудачный эксперимент?

Компоненты и технологии, № 9'2002

Использование языков программирования С или С++ для И01-описания -

будущий стандарт или неудачный эксперимент?

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

В статье рассматривается существующая реализация такого языка — БувГетС. Приведены примеры описания генератора ПСП на С, 5у$ГетС, УегПод ИРЬ.

Сергей Емец

syemets@yandex.ru

Введение

При разработке сложных электронных систем, как, например, 8оС — система на кристалле возникает необходимость разработки программной и аппаратной части. При этом, как правило, в начале разрабатывается модель системы, которая описывает поведение системы на верхнем структурном уровне. Эта модель служит для проверки заложенных в систему алгоритмов и совместно с моделями физических уровней (радиолинии, проводной связи, объекта управления и т. п.) используется для моделирования всей системы. В настоящее время из-за своей популярности и универсальности для программного моделирования широко используется язык С++. После завершения моделирования система разбивается на аппаратную и программную составляющие и разработка ведется по двум направлениям. Для описания аппаратной части проекта используются языки описания «железа» (ИБЬ): УегДо§ или УИБЬ (также могут использоваться низкоуровневые языки ИБЬ и схематический ввод). В результате такого подхода возникает проблема связи между исходной поведенческой моделью верхнего уровня и более детальным описанием «железной» части проекта, теряется возможность моделировать весь проект целиком, и последующие изменения в аппаратной части либо повторяются в высокоуровневой модели (требуется удвоенное время на внесение изменений как в ИБЬ, так и в С++ модель), либо высокоуровневая модель устаревает и не используется. После разработки аппаратной части требуется полностью переписывать программную часть, так как система должна работать в реальном времени и пользоваться специфическими возможностями ИТ08 (многозадачность, прерывания, детерминированное время отклика), также возможно, что реальная аппаратная часть сильно отличается от исходной модели. Возникает необходимость в универсальном языке программирования, пригодном как для описания повенденческих моделей системы, так и для описания «железа» и программ, работающих на этом «железе». Таким образом, «программа максимум» заключается в реализации программного

средства, объединяющего все этапы разработки — от модели верхнего уровня до готового продукта.

В качестве начального шага («программа минимум») можно рассматривать разработку языка, позволяющего описывать «железо» используя синтаксис, С/С++. Такой шаг позволит упростить разработку аппаратуры за счет того, что разработчику не придется изучать специальный язык HDL. А при наличии поведенческой модели на С++ возможно использование готового кода с минимальными изменениями.

В статье основное внимание направлено на исследование возможности использования С/С++ в качестве языка HDL, но также рассматривается возможность построения единой системы разработки от модели до продукта с использованием единого универсального языка. Для иллюстрации классического подхода к разработке аппаратуры приведен пример синтезируемого описания на Verilog совместно с «тестовым стендом» (testbench).

Обзор средств, существующих в настоящее время

Развитие использования С/С++ для описания архитектуры в настоящее время идет по двум направлениям:

1)разработка библиотек на C++ — SystemC (www.systemc.org) и Cynlib (www.ForteDS.com),

которые должны использоваться совместно со стандартным компилятором C++;

2) разработка специального языка, основанного на синтаксисе ANSI C и имеющего специальные конструкции для моделирования «железа» — Handel C (www.celoxica.com) и SpecC (www.specc.org). Концептуально библиотеки гораздо лучше удовлетворяют требованиям, сформулированным во введении. Такой подход дает реальный шанс реализовать «программу максимум» — программное средство для ведения проекта от модели до изделия, также это достаточно удачный первый шаг — альтернатива языкам HDL. К минусам таких библиотек я отношу достаточно сложный синтаксис языка C++.

Специальные языки с синтаксисом, похожим на C, не обеспечивают универсальности и не подхо-

Компоненты и технологии, № 9'2002

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

Библиотеки SystemC распространяются свободно и, следовательно, для симуляции потребуется только компилятор C++ (поддерживается свободный GCC, а для Windows — либо GCC+cygwin, либо Microsoft VC++). Крупные фирмы Cadence, Synopsys поддерживают SystemC. Synopsys предлагает в настоящее время средство RTL-синтеза описаний SystemC, поддерживающее синтезируемое подмножество языка, и обещает реализовать поведенческий синтез («behavioral synthesis»). Существуют свободные средства, транслирующие описание на VHDL в описание на SystemC.

Cynlib также является свободной библиотекой, во многом подобной SystemC, но синтаксис описания аппаратуры более сложный. Для упрощения предлагается препроцессор Cyn+ + , который транслирует конструкции, похожие на Verilog HDL, в Cynlib. На основе GDB предлагается отладчик, работающий с конструкциями Cynlib. Для синтеза предлагается коммерческий продукт Cynthesizer, который транслирует исходник C++ (Cynlib) в описание Verilog/VHDL. Также есть средство, выполняющее преобразование из Verilog/VHDL в C++ c использованием Cynlib. В последние версии Cynlib включена поддержка SystemC.

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

То есть принципиального выигрыша по сравнению с Verilog/VHDL в данном случае нет. Плюсом данного подхода, по сравнению с использованием библиотек C++, является простота синтаксиса и легкость изучения языка. Целесообразность замены существующих языков описания аппаратуры новыми кажется сомнительной — функциональность языка такая же, как и в Verilog/VHDL, а отличия от C значительны. При моделировании системы, часть которой описана на C, я не вижу выигрыша от использования такого языка и связки Verilog + PLI.

О^едует заметить, что применяются и другие подходы для объединения разработки программной и аппаратной части проекта одним универсальным языком. Например, JHDL на основе Java. Но разработка этих проектов находится в зачаточной стадии и использование любого другого объектно-ориентированного языка, кроме C++, не имеет в настоящее время выигрыша перед SystemC или Cynlib.

Таким образом, наиболее заслуживающим внимания направлением является SystemC. Значительным плюсом свободно распространяемой библиотеки можно считать то, что для моделирования системы не нужны коммерческие продукты. То есть дорогостоящие симуляторы, требующиеся для моделирования описаний на Verilog/VHDL, не требуются для симуляции модели SystemC.

Краткое описание возможностей SystemC

Изначально библиотека разрабатывалась для реализации полного цикла разработки, начиная от модели системы и физических объектов и заканчивая моделью RTOS и аппаратной части. Так же, как и для других высокоуровневых языков HDL, для синтеза может быть использован только ограниченный набор конструкций языка. Для того чтобы ознакомиться с этим подмножеством, лучше всего воспользоваться описанием синтеза с SystemC от Synopsys (http://www.synopsys.com/prod-ucts/cocentric_systemC/cocentric_systemC.html). Особенно полезен документ, описывающий синтезируемое подмножество языка Describing Synthesizable RTL in SystemC.

Независимо от языка, на котором сделано RTL-описание, результатом синтеза является список соединений между триггерами, защелками и логическими элементами. Поэтому синтезируемые подмножества Verilog, VHDL и SystemC совпадают по смыслу и отличаются только синтаксисом описания. Возможно, что разработка поведенческого синтеза расширит возможности SystemC для описания аппаратуры, но в настоящее время следует ориентироваться на привычные и широко используемые RTL-описания.

Для понимания синтаксиса SystemC следует разбираться в C++, так как любая конструкция SystemC является синтаксически верной конструкцией C++, особое внимание следует обратить на классы и шаблоны (template). Из-за популярности C++ существует большое количество книг, описывающих этот язык, из которых можно рекомендовать Б. &рауструп «Язык программирования C++». Но в то же время SystemC можно рассматривать как самостоятельный язык, со своим собственным синтаксисом.

Рассмотрим конструкции SystemC, применяемые для описания иерархической структуры модулей.

#include «systemc.h»

SC_MODULE

{

//Module port declarations //Signal variable declarations //Data variable declarations //Member function declarations //Method process declarations //Module constructor SC_CTOR

{

//Register processes //Declare sensitivity list

В дополнение к стандартным объектам класса С++ добавлены описания портов, сигналов и процессов. Эти объекты воспринимаются средством синтеза и являются основными элементами ИТЬ-описания.

Порты описываются следующим образом:

//Module port declarations sc_in<port_data_type> port_name; sc_out<port_data_type> port_name; sc_inout<port_data_type> port_name; sc_in<port_data_type> port_name;

Типом порта port_data_type может являться любой допустимый для синтеза C++ или SystemC тип данных. Это может быть как встроенный тип языка C++, так и определенный в SystemC. Для битовых переменных может использоваться bool, sc_bit, sc_logic. Для численных или векторных типов могут использоваться signed и unsigned char, int, long или специализированные типы SystemC sc_lv<n>, sc_bv<n>, sc_int<n>, sc_uint<n>, sc_bigint<n>, sc_biguint<n>, где n — число, показывающее разрядность. Для n>64 следует использовать sc_bigint/sc_biguint, а sc_logic и sc_lv применяются в случаях, когда требуется моделирование сигнала с X, Z. Также могут применяться определяемые пользователем структуры struct, содержащие поля из вышеперечисленных типов данных (допустимых для синтеза). Перечисления enum поддерживаются синтезом так же, как и компилятором C++ и используются для задания именованных констант.

Для взаимодействия процессов внутри модуля следует использовать сигналы. Это требуется для моделирования конкурирующих процессов и соединения модулей. Сигналы описываются следующим образом:

//Internal signal variable declarations sc_signal<signal_type> signal_name; sc_signal<signal_type> signall, signal2;

На типы сигналов действуют те же ограничения, что и на типы портов.

Внутри модуля могут использоваться переменные, не являющиеся сигналами или портами. Описание переменных не отличается от описания в C++. Типы переменных должны принадлежать тому же подмножеству, что и для портов и сигналов. Разница между сигналами и переменными состоит в том, что значения сигналам присваиваются в конце временного шага исполнения модели (дельта-цикла), а присвоения переменным происходят мгновенно.

Аналогом Verilog блока alwais (или process в VHDL) с условием запуска по уровню или фронту сигнала (сигналов), который применяется для описания триггеров, защелок и комбинаторной (параллельной) логики, является процесс. В SystemC существует несколько видов процессов, но для RTL-описаний применяется только SC_METHOD. Процесс должен быть описан как функция-член, а потом зарегистрирован с помощью макроса SC_METHOD.

// process declaration void my_method_proc();

// module constructor SC_CTOR

{

// register process

SC_METHOD(my_method_proc);

// Define the sensitivity list

Список чувствительности может включать в себя события по уровню и по фронту. При этом правила синтезируемости такие же, как и в Verilog/VHDL, поэтому я их приводить не буду.

SC_METHOD(my_method_proc);

// declare level-sensitive sensitivity list sensitive << a << c << d; // Stream declaration sensitive(b); //Function declaration sensitive(e); //Function declaration

Компоненты и технологии, № 9'2002

Предлагается два метода описания списка чувствительности в виде потока или в виде функций. Для описания чувствительности по фронту используются sensitive_pos или sensitive_neg вместо sensitive.

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

В качестве неблокирующего присвоения (Verilog <=) сигналам следует пользоваться функциями read(), write(), но также переопределен оператор присваивания.

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

// Create data members — pointers to instantiated // modules fir_fsm *fir_fsm1; fir_fsm *fir_fsm2;

SC_CTOR

{

// Create new instance of fir_fsm module fir_fsm1 = new fir_fsm(«FirFSM1»); fir_fsm2 = new fir_fsm(«FirFSM2»);

// Binding by name fir_fsm1->clock(CLK); fir_fsm1->reset(RESET); fir_f sm1->state_out(state_out1);

// Binding by position alternative fir_fsm2 (CLK, RESET, state_out2);

В общем, этого достаточно для описания аппаратной части. В качестве примера приведу описания некоторых стандартных элементов на SystemC:

Пример1

#include «systemc.h» SC_MODULE

{

sc_in<bool> in_data, reset; sc_out<bool> out_q; sc_in<bool> clock; // clock port '/ Method for D-flip-flop void dff ();

// Constructor SC_CTOR

SC_METHOD (dff); sensitive_pos << clock;

void dffsr::dff

if (reset.read()) out_q.write(0); else out_q.write(in_data.read());

Пример 2

#include «systemc.h» SC_MODULE

{

sc_in<unsigned char> in_i; sc_out<unsigned char> out; // Method process void decoder_fcn

{

case 0: out.write(0x01); break case 1: out.write(0x02); break case 2: out.write(0x04); break case 3: out.write(0x10); break case 4: out.write(0x20); break case 5: out.write(0x40); break default: out.write(0x01);

}

Пример l описывает триггер с синхронным сбросом, а пример 2 — комбинаторную логику.

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

Для моделирования цифровых схем (или любых других систем) отсутствуют ограничения, накладываемые на описание RTL-кода. То есть возможно использовать всю мощь языка C++ и в дополнение к этому в SystemC существуют классы C++, описывающие электрические сигналы (может принимать значения 0, 1, Z, X) и имеющие правила разрешения конфликтов. Также система моделирования поддерживает запись изменения сигналов в различных форматах, например, vcd. Механизм моделирования привязан ко времени исполнения и использует понятие дельтацикла так же, как и симулятор Verilog/VHDL.

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

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

Для моделирования RTOS имеются потоки вычислений (THREAD), семафоры, очереди и модель невытесняющей многозадачности.

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

Пример использования SystemC для моделирования устройства генерации GPS L5 кода

Проведем сравнение этапа разработки моделирования аппаратной реализации генератора псевдошумового кода от модели C до RTL-кода с использованием Verilog и SystemC. Исходная модель на C++ описывает процедуру генерации псевдошумового сигнала, который планируется для использования в навигационных GPS-спутниках следующего поколения. Для статьи модель была упрощена: отсутствует модуляция несущей, модель шума, сигналы соседних полос и т. п.

const int DEBUG=4; //verbose level 0-4 const int N=7; //select satelit

#include <iostream>

#include <iomanip>

namespace L5_ICD { struct XBcode {

int adv,

unsigned long state;

};

struct L5codes {

XBcode xi;

XBcode xq;

};

const int Number_of_codes=37;

const unsigned long XA_POLY=0x201B;

const unsigned long XB_POLY=Ox36E3;

const unsigned long POLY_XA=0x3601;

const unsigned long POLY_XB=Ox31DB;

const unsigned long POLY_MASK=0x2000;

const L5codes c_list[Nu

{{266, 0x0AE4} , {1701, 0x12CC}},

{{365, 0x1835} , {323, 0x08F6}},

{{804, 0x0808} , {5292, 0x1E23}},

{{1138, 0x1626} , {2020, 0x076A}}

{{1509, 0x1DD7} , {5429, 0x07B2}}

{{1559, 0x0CFA} , {7136, 0x0AA9}}

{{1756, 0x149F} {1041, 0x1F81}

{{2084, 0x17A4} , {5947, 0x0D68}}

{{2170, 0x1F2B} , {4315, 0x1743}

{{2303, 0x0FDE} {148, 0x0486}},

{{2527, 0x013A} , {535, 0x0205}},

{{2687, 0x1CF9} , {1939, 0x0AC5}}

{{2930, 0x039C} , {5206, 0x09A5}}

{{3471, 0x0827} , {5910, 0x143F}

{{3940, 0x0D5A} , {3595, 0x178F}

{{4132, 0x03C9} , {5135, 0x1A5F}}

{{4332, 0x098F} , {6082, 0x1CC8}}

{{4924, 0x1E1E} , {6990, 0x16E4}

{{5343, 0x191F} , {3546, 0x065B}}

{{5443, 0x0D6D} , {1523, 0x1871}

{{5641, 0x0408} , {4548, 0x0D90}}

{{5816, 0x1DEF} , {4484, 0x058E}}

{{5898, 0x10FE} , {1893, 0x117D}}

{{5918, 0x18B4} , {3961, 0x0DF3}}

{{5955, 0x1A6D} , {7106, 0x089B}}

{{6243, 0x1596} , {5299, 0x0ABC}}

{{6345, 0x0ADE} , {4660, 0x10FA}}

{{6477, 0x0F56} , {276, 0x1F42}},

{{6518, 0x0BE1} , {4389, 0x0A24}}

{{6875, 0x10B7} , {3783, 0x1079}

{{7168, 0x029E} , {1591, 0x0BE5}}

{{7187, 0x00B9} , {6518, 0x0BE1}

{{7329, 0x1A81} , {749, 0x1644}},

{{7577, 0x1BF9} {1138, 0x1626}

{{7720, 0x1EDC} {1661, 0x0CB3}}

{{7777, 0x12C8} , {3210, 0x07AF}}

{{8057, 0x0690} , {4332, 0x098F}

using namespace L5_ICD;

struct L5data { double xi; double long xq;

// return XOR

int get_bit

int j=0; if

{

do

{

unsigned long t=w-1; j=i-j; w&=t;

}while(w);

return j;

}

//rotate right shift reg with tabs unsigned long stateXA; int nextXA

{

int bit=get_bit((XA_POLYAPOLY_MASK)&stateXA); int ret=stateXA&1;

stateXA&=~POLY_MASK; stateXAI=(bit?POLY_MASK:0); stateXA>>=1; if (stateXA==0x1ffd) stateXA=0x1fff; return ret;

Компоненты и технологии, № 9'2002

//rotate right shift reg with tabs unsigned long stateXBI; int nextXBI

{

int bit=get_bit((XB_POLYAPOLY_MASK)&stateXBI); int ret=stateXBI&1;

stateXBI&=~POLY_MASK; stateXBI|=(bit?POLY_MASK:0); stateXBI>>=1; return ret;

}

//rotate right shift reg with tabs unsigned long stateXBQ; int nextXBQ

{

int bit=get_bit((XB_POLYAPOLY_MASK)&stateXBQ); int ret=stateXBQ&1;

stateXBQ&=~POLY_MASK; stateXBQ|=(bit?POLY_MASK:0); stateXBQ>>=1; return ret;

L5data L5code

{

static int bit_number=0; static L5data out;

int xa=nextXA();

out.xi=(nextXBI()Axa);

out.xq=(nextXBQ()Axa);

if

{

bit_number=0;

stateXA=0x1fff;

stateXBI=c_list[N].xi.state;

stateXBQ=c_list[N].xq.state;

}

if

{

cerr<<»XA 0x»<<hex<<setw(4)<<setfill('0')<<stateXA<<» «; cerr<<»XBI 0»><<hex<<setw(4)<<setfi]ll('0')<<stateXBI<<» « cerr<<»XBQ 0x»<<hex<<setw(4)<<setfin('0')<<stateXBQ<<» «;

}

if (DEBUG>1) cerr<<»XI code: «<<dec<<out.xi<<» XQ code: «<<out.xq<<»\n»;

}

main

{

//init code stateXA=0x1fff; stateXBI=c_list[N].xi.state; stateXBQ=c_list[N].xq.state;

//generate PRN sequence for(long i=0;i<10240; i++, L5code());

}

Модель описывает генерацию двух псев-дошумовых последовательностей, предназначенных для квадратурного модулятора. Последовательности образуются смешиванием двух М-последовательностей, генерируемых 13-разрядными регистрами с разными порождающими полиномами. Одна из последовательностей (ХА) укорочена на 1 такт, а вторая запускается со сдвигом — для квадратурного (XQ) и синфазного (XI) каналов сдвиги разные. Смешиваются последовательности посредством «исключающего или», а длина повторения — 10230 чипов. Число 10230 определяется кодом в полосе L1 и выбрано так, чтобы время передачи на частоте 10,23 МГц совпало со временем повторения последовательности L1 (код Голда длины 1023 на частоте 1,023 МГц) и составило 1 мс. Иерархия верхнего уровня приведена на рис. 1. Предлагается 37 последовательностей, определяемых

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

При использовании в качестве языка описания аппаратуры Verilog HDL синтезируемая модель может быть описана следующим образом:

module L5code(/*AUTOARG*/

// Outputs XI, XQ,

// Inputs C, RST, N

input C,RST; output XI,XQ; input [6:0] N;

parameter XA_poly=13'h01B; parameter XB_poly=13'h16E3; parameter SEQ_len=14'd10230;

reg [12:0] stateXA,stateXBI,stateXBQ; reg [13:0] cntr; reg rel_ff;

assign XI=stateXA[0]AstateXBI[0]; assign XQ=stateXA[0]AstateXBQ[0]; wire reload=RST|rel_ff;

always @(posedge C) begin : counter if (reload) begin : load rel_ff<=0; cntr<=SEQ_len; end else

begin : decrement cntr<=cntr-1; if (~lcntr) rel_ff<=1; end

end // block: counter

always @(posedge C) begin : XA_gen if (reload I ~l(stateXAA13'h1ffe)) stateXA=13'h1fff; else

stateXA={A(stateXA&XA_poly),stateXA[12:1]};

end

always @

stateXBI=initXBI(N);

else

stateXBI=nextXB(stateXBI);

end

always @

stateXBQ=initXBQ(N);

else

stateXBQ=nextXB(stateXBQ);

end

function [12:0] nextXB; input [12:0] state; begin

nextXB={A(state&XB_poly),state[12:1]};

end

endfunction // nextXB

function [12:0] initXBI; input [6:0] sati;

begin

case

7'd1 : initXBI=13 ' b0101011100100

7'd2 : initXBI=13 ' b1100000110101

7'd3 : initXBI=13 ' b0100000001000

7'd4 : initXBI=13 ' b1011000100110

7'd5 : initXBI=13 ' b1110111010111

7'd6 : initXBI=13 ' b0110011111010

7'd7 : initXBI=13 ' b1010010011111

7'd8 : initXBI=13 ' b1011110100100

7'd9 : initXBI=13 ' b1111100101011

7'd10 : initXBI=13 ' b0111111011110

7'd11 : initXBI=13 ' b0000100111010

7'd12 : initXBI=13 ' b1110011111001

7'd13 : initXBI=13 ' b0001110011100

7'd14 : initXBI=13 ' b0100000100111

7'd15 7'd16 7'd17 7'd18 7'd19 7'd20 7'd21 7'd22 7'd23 7'd24 7'd25 7'd26 7'd27 7'd28 7'd29 7'd30 7'd31 7'd32 7'd33 7'd34 7'd35 7'd36 7'd37 default endcase end endfunction

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

initXBI=13

// case

// initXBI

b0110101011010 b0001111001001 b0100110001111 b1111000011110 b1100100011111 b0110101101101 b0010000001000 b1110111101111 b1000011111110 b1100010110100 b1101001101101 b1010110010110 b0101011011110 b0111101010110 b0101111100001 b1000010110111 b0001010011110 b0000010111001 b1101010000001 b1101111111001 b1111011011100 b1001011001000 b0011010010000 h1fff;

function [12:0] initXBQ; input [6:0] satq;

begin case

7'd1 : initXBQ=13 ' b1001011001100

7'd2 : initXBQ=13 ' b0100011110110

7'd3 : initXBQ=13 ' b1111000100011

7'd4 : initXBQ=13 ' b0011101101010

7'd5 : initXBQ=13 ' b0011110110010

7'd6 : initXBQ=13 ' b0101010101001

7'd7 : initXBQ=13 ' b1111110000001

7'd8 : initXBQ=13 ' b0110101101000

7'd9 : initXBQ=13 ' b1011101000011

7'd10 : initXBQ=13 ' b0010010000110

7'd11 : initXBQ=13 ' b0001000000101

7'd12 : initXBQ=13 ' b0101011000101

7'd13 : initXBQ=13 ' b0100110100101

7'd14 : initXBQ=13 ' b1010000111111

7'd15 : initXBQ=13 ' b1011110001111

7'd16 : initXBQ=13 ' b1101001011111

7'd17 : initXBQ=13 ' b1110011001000

7'd18 : initXBQ=13 ' b1011011100100

7'd19 : initXBQ=13 ' b0011001011011

7'd20 : initXBQ=13 ' b1100001110001

7'd21 : initXBQ=13 ' b0110110010000

7'd22 : initXBQ=13 ' b0010110001110

7'd23 : initXBQ=13 ' b1000101111101

7'd24 : initXBQ=13 ' b0110111110011

7'd25 : initXBQ=13 ' b0100010011011

7'd26 : initXBQ=13 ' b0101010111100

7'd27 : initXBQ=13 ' b1000011111010

7'd28 : initXBQ=13 ' b1111101000010

7'd29 : initXBQ=13 ' b0101000100100

7'd30 : initXBQ=13 ' b1000001111001

7'd31 : initXBQ=13 ' b0101111100101

7'd32 : initXBQ=13 ' b0101111100001

7'd33 : initXBQ=13 ' b1011001000100

7'd34 : initXBQ=13 ' b1011000100110

7'd35 : initXBQ=13 ' b0110010110011

7'd36 : initXBQ=13 ' b0011110101111

7'd37 : initXBQ=13 ' b0100110001111

default initXBQ=13 ' h1fff;

endcase // case

end

endfunction // initXBQ

endmodule // L5code

Сразу появляются отличия:

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

• непринципиальные, например, в модели С номер спутника начинается с 0, а в модели Уеп^ с 1.

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

return out

Компоненты и технологии, № 9'2OO2

||8057, 0x06901 , |4332, 0x098FH

SC_MODULE

Для проверки работы Verilog-модели требуется разработать «испытательный стенд» (testbench). Это специальная программа на Verilog, которая может использовать несинтезируемые конструкции языка и средства ввода-вывода. Для тестирования модели генератора можно воспользоваться таким testbench:

'timescale 1ns/10ps 'define N 7'd8 //'define DEBUG

module L5code_testbench();

integer i; reg rst,clk_en; reg [6:0] n;

wire xi,xq,clk;

L5code DUT(.XI(xi), .XQ(xq), .C(clk), .RST(rst), .N(n)); nand #20 clk_gen(clk,clk_en,cIk);

begin

clk_en=0;

rst=1;

n='N;

i=0;

#50;

clk_en=1;

#100;

'ifdef DEBUG

$monitor(«# %d XA %x XBI %x XBQ %x»,i,

DUT.stateXA, DUT.stateXBI, DUT.stateXBQ);

'else

$monitor(«# %d XI %x XQ %ox»,i,xi,xq);

'endif

rst=0;

for

@(posedge clk);

$finish; end // initial begin

endmodule // L5code_testbench

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

Для описания данной модели на SystemC лучше всего пользоваться встроенными типами C++ int и bool, для того чтобы воспользоваться уже готовым кодом с минимальными изменениями. Конечно, описание на SystemC отличается от исходного описания, но если бы

описание модели было сделано на SystemC изначально, то объем работы можно было уменьшить вдвое без потери функциональности. Напоминаю, что изменения потребовались, чтобы создать синтезируемую (RTL) модель. Для этого потребовалось ввести тактовый сигнал и немного изменить структуру.

Как принято среди разработчиков C++, разобьем описание на заголовок (h-файл) и файл с определениями (cpp).

#include <systemc.h> struct XBcode | int adv; int state; і; struct L5codes | XBcode xi; XBcode xq; і; const int Number_of_codes=37;

const nt SEQ_LEN=10230;

const nt XA_POLY=0x001B;

const nt XB_POLY=0x16E3;

const nt POLY_MASK=0x2000;

const Acodes c_list[Number_of_codes]=|

{{266, 0x0AE4l , I1701, 0x12CCH,

{{365, 0x1835і , |323, 0x08F6H,

{{804, 0x0808і , |5292, 0x1E23H,

{{1138, 0x1626! , |2020, 0x076AH,

{{1509, 0x1DD7! I5429, 0x07B2H,

{{1559, 0x0CFAl , |7136, 0x0AA9H,

{{1756, 0x149Fl I1041, 0x1F81 і і,

{{2084, 0x17A4l |5947, 0x0D68H,

{{2170, 0x1F2B} |4315, 0x1743Н,

{{2303, 0x0FDEl 1148, 0x0486Н,

{{2527, 0x013Al , |535, 0x0205Н,

{{2687, 0x1CF9l , |1939, 0x0AC5H,

{{2930, 0x039C і , |5206, 0x09A5H,

{{3471, 0x0827l , |5910, 0x143FH,

{{3940, 0x0D5Al , |3595, 0x178FH,

{{4132, 0x03 C9l , |5135, 0x1A5FH,

{{4332, 0x098Fl , |6082, 0x1CC8H,

{{4924, 0x1 E^ , |6990, 0x16E4H,

{{5343, 0x191Fl , |3546, 0x065BH,

{{5443, 0x0D6Dl , |1523, 0x1871»,

{{5641, 0x0408l , |4548, 0x0D90H,

{{5816, 0x1DEFl , |4484, 0x058EH,

{{5898, 0x10FE} , |1893, 0x117D1 і,

{{5918, 0x18B4l , |3961, 0x0DF3H,

{{5955, 0x1A6Dl , |7106, 0x089BH,

{{6243, 0x15961 , |5299, 0x0ABCH,

{{6345, 0x0ADEl , |4660, 0x10FAH,

{{6477, 0x0F561 , |276, 0x1F42H,

{{6518, 0x0BE1l , |4389, 0x0A24H,

{{6875, 0x10B7l , |3783, 0x1079Н,

{{7168, 0x029El I1591, 0x0BE5H,

{{7187, 0x00B9l , |6518, 0x0BE1H,

{{7329, 0x^8^ , |749, 0x1644Н,

{{7577, 0x1BF9l 11138, 0x1626Н,

{{7720, 0x1EDCl I1661, 0x0CB3H,

{{7777, 0x12C8l , |3210, 0x07AFH,

{

sc_in<bool> C; sc_in<bool> RST; sc_in<int> N; sc_out<bool> XI; sc_out<bool> XQ;

sc_signal<bool> rel_ff, reload, bitXA, bitXBI, bitXBQ;

unsigned stateXA,stateXBI,stateXBQ; int cntr;

void output_logic(); void reload_logic(); void L5_gen();

SC_CTOR

{

SC_METHOD(output_logic);

sensitive<<bitXA<<bitXBI<<bitXBQ;

SC_METHOD(reload_logic); sensitive << RST << rel_ff;

SC_METHOD(L5_gen);

sensitive_pos(C);

};

private:

// from C source bool nextXA(); bool nextXBI(); bool nextXBQ(); int get_bit(unsigned);

Можно видеть, что структуры данных и основные функции перенесены из исходного C-файла. Требования на аппаратную часть (интерфейс) описаны в классе L5codegen.

#include «L5code_systemc.h» void L5codegen::output_logic

{

XI=bitXAAbitXBI;

XQ=bitXAAbitXBQ;

}

void L5codegen::reload_logic

{

О

reload.write(RST I rel_ff);

}

void L5codegen::L5_gen

{

if

{

rel_ff=0;

cntr=SEQ_LEN;

stateXA=0x1fff;

stateXBI=c_list[N].xi.state;

stateXBQ=c_list[N].xq.state;

}

else

if(cntr--==0) rel_ff=1; bitXA=nextXA(); bitXBI=nextXBI(); bitXBQ=nextXBQ();

}

}

//main logic from original C source // return parity

int L5codegen::get_bit

int j=0; if

do

unsigned t=w-1; j=1-j; w&=t;

}while(w);

}

return j;

}

//rotate right shift reg with tabs bool L5codegen::nextXA

{

int bit=get_bit((XA_POLYAPOLY_MASK)&stateXA); int ret=stateXA&1;

initial

Компоненты и технологии, № 9'2002

stateXA&=~POLY_MASK;

stateXAI=(bit?P0LY_MASK:0);

stateXA>>=1;

if (stateXA==0x1ffd) stateXA=0x1fff; return ret;

//rotate right shift reg with tabs bool L5codegen::nextXBI

{

int bit=get_bit((XB_POLYAPOLY_MASK)&stateXBI); int ret=stateXBI&1;

stateXBI&=~POLY_MASK; stateXBII=(bit?POLY_MASK:0); stateXBI>>=1; return ret?true:false;

//rotate right shift reg with tabs bool L5codegen::nextXBQ

{

int bit=get_bit((XB_POLYAPOLY_MASK)&stateXBQ); int ret=stateXBQ&1;

stateXBQ&=~POLY_MASK; stateXBQI=(bit?POLY_MASK:0); stateXBQ>>=1; return ret?true:false;

}

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

Для того чтобы проверить работоспособность модуля, нужно написать соответствующий испытательный стенд (testbench). Для краткости определения и описания (интерфейс) испытательного стенда помещены в один файл.

#include «L5code_systemc.h»

SC_MODULE(test) { sc_out<bool> reset; sc_in<bool> CLK; sc_in<bool> xi,xq; sc_out<int> n;

unsigned cycle;

SC_CTOR

{

SC_METHOD(stimul); dont_initialize(); sensitive_pos(CLK); cycle = 0;

SC_METHOD(display);

dont_initialize();

sensitive_pos(CLK);

};

void stimul(); void display();

void test::stimul

{

n.write(7);

cycle++;

// sending some reset values if

{

reset.write(true); if (cycle>1000) sc_stop();

}

else

{

reset.write(false);

void test::display

{

bool tmp1 = xi.read(); bool tmp2 = xq.read();

cout << «Display : « << tmp1 << « « << tmp2 << « at time « << sc_simulation_time() << endl;

}

Рис. 2. Окно отладки примера в среде MSVC++

int sc_main

{

sc_clock mclk(«MCLK»,20,0.5); sc_signal<bool> reset; sc_signal<bool> xi,xq; sc_signal<int> n;

L5codegen *L5ins; test *tb;

L5ins=new L5codegen(«DUT»); L5ins->C(mclk); L5ins->RST(reset); L5ins->N(n);

L5ins->XI(xi);

L5ins->XQ(xq);

tb=new test(«TB»);

tb->CLK(mclk);

tb->reset(reset);

tb->n(n);

tb->xi(xi);

tb->xq(xq);

sc_start(10000);

//sc_start(-1);

return 0;

В этом файле описан дополнительный модуль test, который формирует управляющие сигналы для тестируемого модуля и печатает результаты. Функция sc_main является аналогом стандартной функции main в C/C++ коде: управление передается на эту функцию после завершения инициализации. sc_clock mclk(«MCLK»,20,0.5); — описание тактового сигнала. Функция sc_start() — функция запуска симулятора, если аргумент — 1, то повторение продолжается до тех пор, пока не будет вызвана функция sc_stop(), иначе аргумент определяет время исполнения.

Для компиляции и исполнения этого примера требуется собрать библиотеку systemc.lib и подключить ее к проекту. В Unix-системах следует воспользоваться configure-make, для пользователей Windows в пакете есть поддиректория msvc60, в которой находятся проекты для сборки библиотеки и примеров.

Выводы

SystemC выглядит удачной альтернативой языкам HDL и может использоваться в качестве системного языка разработки от модели

до готового изделия. В библиотеке реализовано много классов для моделирования различных этапов разработки SoC, в особенности для систем связи и обработки сигналов. Минусом системы, на мой взгляд, является сложный синтаксис языка C++, но использование стандартного языка позволяет использовать существующие компиляторы, прежде всего свободно распространяемый gcc www.gcc.org. Если же ограничить использование SystemC описанием RTL, то существенного выигрыша перед HDL-языками у Verilog/VHDL — нет. К плюсам следует отнести то, что для моделирования не требуется симулятор, а к минусам — то, что средства синтеза для SystemC распространены меньше, чем для специализированных HDL-языков. Но производители начинают поддерживать RTL SystemC — в настоящее время продаются компилятор CoCentric от Synopsys или продукция Forte Design Systems.

В примерах SystemC, поставляемых совместно с библиотекой или свободно доступных в сети, можно найти описания процессора, фильтров, схем шифрования, МР3-декодера, различных интерфейсов и т. д. Но большая часть этих примеров не является RTL-кодом и не может быть использована для синтеза.

Совместно со свободными средствами просмотра vcd (value change dump) файлов SystemC может использоваться как полноценное средство моделирования цифровых схем. При этом свободное (бесплатное) использование этих программных продуктов является легальным, и результаты могут использоваться для получения прибыли. Свободный vcd-viewer можно загрузить с http://www.cs.man.ac.uk/ amulet/tools/gtkwave/, а версию для Windows — http://daggit.pagecreator.com/ ver/wave/gtk-wave9x.html.

Можно предположить, что в ближайшее время SystemC не вытеснит Verilog/VHDL, но займет место среди HDL-языков. Большим плюсом SystemC является возможность интеграции описания аппаратуры, по которому может изготовляться изделие (SoC), и программного обеспечения, пригодного для использования в готовом изделии в одной модели.