Автобиография
Реферат
Библиотека
Ссылки
Отчет о поиске
Индивидуальное задание

Немного о SystemC

Источник: http://community.livejournal.com/ru_hdl_fpga/1164.html

Дело в том, что я искал по теме SystemC хорошие русские материалы, но ничего толкового не нашел. Поэтому я решил написать что-то свое, которое конечно может и не получит статус "хорошего материала", но будет полезно для первоначального ознакомления с SystemC.

Что такое SystemC?

SystemC - это набор классов, которые свободно распространяются и позиционируются как средство моделирования цифровых схем с помощью любого компилятора C (в первую очередь GCC и MSVC, но есть кстати портирование на C++Builder).

Что хорошо - можно действительно моделировать сложные схемы, описанные как классы, производные от класса SC_MODULE из библиотеки SystemC, просто компилируя их в exe-шники - без всякого использования ModelSim или Riviera/ActiveHDL. Кстати, при моделировании возможно использование всех средства языка C/C++ (функции, объекты и т.д.).

Что плохо: во-первых, описание схемы на языке Си - является несколько странным и экзотическим. А во-вторых - практически нет средств синтеза с SystemC. Точнее средства-то есть, но они либо недоступны для скачивания, либо их разработка свернута (SystemC в Cocentric). Тут важно не путать синтез с SystemC и синтез со специального подмножества Ansi C/C++ (Handel C). Последнее представляет собой в самом деле аппаратизацию алгоритмов, а не синтез в обычном понимании этого слова. Что касается синтеза схем - то тут трудно найти что более привычное чем VHDL/Verilog.

Но тем не менее SystemC может быть полезен разработчикам в качестве мощного средства моделирования схем. Я, кстати, долгое время не обращал внимание на его возможности. Так было до тех пор, пока мне не пришлось использовать USBHostSlave IP с http://www.opencores.org/, в котором тестбенч выполнен именно на SystemC, и сделано это как нельзя кстати.

В чем суть?

Большая доля дизайнов на FPGA работает в микропроцессорных системах. Т.е. тут либо FPGA подключается к микропроцессору, либо вообще микропроцессор целиком реализуется на FPGA и тогда ресуры FPGA могу быть также использованы для реализации периферии. А никто не будет спорить, что естественным языком программирования для embedded микропроцессорных систем является язык C (или C++). Поэтому ставится задача моделирования (верификации) проекта, в котором микропроцессорное ядро выполняет софт, написанный на C/C++ и при этом софт дергает хард, который написан на языке VHDL/Verilog и представляет собой периферию процессора в широком понимании этого слова.

Картинка заменяет все эти слова:

Photobucket - Video and Image Hosting

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

Как можно поступить иначе? Рассмотрим простую картинку, на которой нарисована функциональная структура программно-аппаратной системы:

Photobucket - Video and Image Hosting

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

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

Hard - аппаратное обеспечение, которое подключено к микропроцессору либо через некоторую шину, либо через его линии ввода-вывода (порты). В данном случае, Hard - это FPGA, функции которой описаны на каком-то языке (VHDL/Verilog).

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

Поэтому рационально моделировать и держать в системе моделирования только HAL и Hard, а Soft - выполнять подобно обычной программе на компьютере. Т.е. иными словами моделирование (т.е. запуск системы моделирования в которой крутятся HAL и Hard) будет производиться только тогда, когда Soft захочет обратиться к Hard, вызвав соответствующие функции HAL. Поэтому подобного рода организацию процесса моделирования называют TLM (transaction-level model). И для сложных микропроцессорных систем, такое моделирование позволяет получить 50 и более кратный выигрыш в скорости!

Что мы будем делать?

Ниже будет очень простая и бесполезная вещь - микроконтроллер (ну допустим, какой-нибудь ATMega) генерирует случайные числа (шириной в 8 бит), и записывает в FIFO-буфер, который выполнен на FPGA (например, на Altera ACEX1K), а уже из FIFO-буфера эти числа будут читаться другим внешним устройством с постоянным периодом в 0,1 мс. Картинка, которая поясняет структуру дизайна:

Photobucket - Video and Image Hosting

И мы, естественно, желаем верифицировать софт и хард, в едином "design flow".

У нас есть софт на C и описание харда на Verilog, и мы желаем после совместной симуляции хард+софт сразу имплементировать это дело на микроконтроллере и FPGA.

Использовать будем ModelSim - только нормальный ModelSim (не OEM-edition, поставляемый например с ISE ("XilinX edition") или с Quartus-ом ("Altera edition") - поскольку они НЕ позволяют симулировать смешанные проекты).

Разбор исходников

Рассмотрим исходники проекта для ModelSim-а, ссылка на который дана в конце статьи.

Для начала хард.
Буфер описан в файле Acex_FIFO.v. Там ничего интересного: инсталляция на Verilog-е компонента dcfifo из библиотеки мегафункций от Quartus-а. Поскольку Quartus может быть не у всех есть - исходники функциональных моделей мегафункции также содержатся в архиве (да простит меня Altera).

Опишем внешние сигналы модуля:
data - 8 бит - данные для записи
wrreq - разрешение записи
rdclk - тактовый сигнал чтения
wrclk - тактовый сигнал записи
aclr - асинхронный сброс очереди
q - 8 бит - данные для чтения
rdempty - признак, что очередь пустая (выход синхронный, обновляется по rdclk)
wrfull - признак, что очередь полная (выход синхронный, обновляется по wrclk)

Внешнее устройство моделируется в ExternalDevice.v - там еще более тривиальный код, который не использует флаг rdempty (мы полагаем что внешнее устройство достаточно интеллектуально, чтобы подождать некоторое время после сброса) и просто читает выход q очереди, стробируя rdclk.

Теперь рассмотрим софт.

Код Main.cpp - собственно наша "firmware" :)
Тут ничего интересного:
GenerateRandomNum() - генерирует очередное случайное число (можно было использовать rand из stl - но так быстрее работает - хотя и не сильно)
main() - все инициализируется вызовом Init() и в долговечном цикле генерируются случайные числа и записываются в очередь
Мы видим, что функция Init() и WriteToFIFO() не объявлены - зато есть заголовок HAL.h, где все это содержится. А также внизу строки:

#ifdef SIMULATION
SC_MODULE_EXPORT(SystemMainTest);
#endif

Это - макрос, экспортирующий SystemC топ-модуль (его имя тут SystemMainTest) для связывания и симуляции. Любой модуль на SystemC должен заканчиваться такой директивой экспорта (здесь и далее я говорю про ModelSim). Также стоит обратить внимание на объявление SIMULATION - так мы переключаемся между реальным кодом HAL и его моделью.

А таеперь заглянем в HAL.h
Первое что мы видим - все обрамлено директивой условной компиляции по объявлению SIMULATION.
Грубо говоря, код делится на две части:

#ifdef SIMULATION
// тут код SystemC модуля, симулирующего HAL
#else
// тут код для реального процессора AVR
#endif

С последней частью, где код реального процессора - все ясно. Там функции:
Init() - настраивает порты
Delay() - задержка
WriteToFIFO(...) - запись в FIFO

Обратимся к SystemC - модели, подставляемой если SIMULATION объявлено. Сначала идут вложения заголовчный файлов:

#include < systemc.h >
#include "Acex_FIFO.h"
#include "ExternalDevice.h"

systemc.h - тут объявлены все классы SystemC, Acex_FIFO.h и ExternalDevice.h - это заголовки к нашим Verilog-модулям, они генерируются автоматически (об этом чуть ниже).
Далее идет объявление вида:

SC_MODULE(SystemMainTest)
{
}

Все что нужно знать - это объявление потомка класса, которое после макроподстановки выглядит так:

class SystemMainTest : public sc_module
{
}

Ну соответсвенно - далее идут поля этого класса. Сначала наши модули:

Acex_FIFO* Acex_FIFO_INST;
ExternalDevice* ExternalDevice_INST;

Как видно, SystemC оперирует с модулями как с объектами. Далее идут объявления сигналов (сигнал в SystemC - более близок к аналогичному понятию в VHDL, чем проводу (wire) в Verilog):

sc_signal < sc_int < 8 > > data;
sc_signal < bool > wrreq;
...

Все это реализовано в виде шаблонов, и мы видим, что wrreq - это булевская переменная (1 бит), data - 8 бит. Теперь пропустим методы и обратимся к концу объявления где содержится конструктор:

SC_CTOR(SystemMainTest)
{
}

Опять-же это - макроподстановка, которая разворачивается в "нормальное" объявление конструктора. Видно, что идет создание модулей:

Acex_FIFO_INST = new Acex_FIFO("Acex_FIFO_INST", "Acex_FIFO");
ExternalDevice_INST = new ExternalDevice("ExternalDevice_INST", "ExternalDevice");

Вот так подключаются модули проводами:

Acex_FIFO_INST->data(data);
Acex_FIFO_INST->wrreq(wrreq);
...

Т.е. подключение по виду: модуль->порт_модуля(провод).
Далее очень важно - с помощью макроподстановки SC_THREAD запускаются на непрерывное выполнение методы класса:

SC_THREAD(Main);
SC_THREAD(ClkGenerator);

В нашем случае запускается Main() (он же main() в Main.cpp) и метод генерации такта ClkGenerator().

Теперь обратимся к методам.
Сначала - Init(). Мы обнаруживаем, что запись значения сигналов осуществляется вызовом метода write сигнала, т.е. сигнал.write(значение). Замечу, что чтение состояния сигнала производится аналогично с помощью метода read : сигнал.read().
Далее мы встречаем вызов Delay() - функции задержки.

Здесь надо сделать отступление. Дело в том, что моделирование в SystemC в общем случае не является "time accurate", т.е. выполнение любого C/C++ кода предполагается мгновенным, если не использованы никакие средства по моделированию задержек. Еще раз подчеркну, вот такой например код будет вообще проигнорирован:

for (i = 0; i < Count; i++) ;

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



Рассмотрим далее код функции Delay(). Здесь мы видим, что функция ожидает Count событий по сигналу Clk (событие - это фронт или спад) с помощью оператора wait:

wait(Clk.default_event())

Сигнал Clk генерируется отдельным процессом ClkGenerator(), вызываемым из конструктора. В этом процессе в долговечном цикле выполняется такой код:

Clk.write(1);
wait(1000, SC_NS);
Clk.write(0);
wait(1000, SC_NS);

Оператор wait тут используется как средство останова (паузы) на заданный промежуток времени (1000 нс). Почему нельзя такое сделать в Delay? Потому что есть тонкость - остановка с помощью wait-а на заданное время возможна только в процессах - т.е. в методах запущенных с помощью SC_THREAD(метод).

После такого объяснения код метода WriteToFIFO() становится совершенно понятным. Единственное, что можно заметить - это цикл while, в котором проверяется и ожидается состояние когда очередь не полная и туда можно что-то записать.

Если посмотреть на скрипт симуляции Run_SystemC.do, то можно заметить, что с SystemC связаны следующие строки:

scgenmod -bool Acex_FIFO > Acex_FIFO.h
scgenmod -bool ExternalDevice > ExternalDevice.h
sccom -g Main.cpp
sccom -link

scgenmod - генерирует заголовочный файл для модулей на Verilog/VHDL (параемтр -bool - определяет что тип сигналов совместим с типом bool С/C++ - т.е. сигналам можно присваивать 0 и 1), sccom - компиляция SystemC модуля (-g - добавление отладочной информации), sccom -link - линковка.

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

Photobucket - Video and Image Hosting

Заключение

Надеюсь, что такое вводное описание языка SystemC несколько осветило данную тему для тех, кто не обращал внимание на это средство моделирования схем. Если вас заинтересовало применение SystemC, то первое куда надо идти - это сюда: http://www.systemc.org/ А также я очень рекомендую прочитать в документации к используемой вами системе моделирования о поддержке SystemC - разные программы моделирования предъявляют разные требования к описаниями на SystemC, и конечно имеют различные команды для компиляции/линковки модулей. Также - смотрите примеры на SystemC к соответствующим программам моделирования. Все это сэкономит много времени!

Вот тут можно скачать все исходные коды и проект к ICCAVR, основанный на SystemC модуле (все что я сделал - это закоментировал SIMULATION и переименовал Main.Cpp в Main.C): http://meetfile.com/files/28825/SystemC_example.rar.html



Магистр ДонНТУ Смешков Александр Сергеевич