-
Notifications
You must be signed in to change notification settings - Fork 1
/
lab-model-builder.tex
180 lines (127 loc) · 17.3 KB
/
lab-model-builder.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
\chapter{Создание программных моделей}\label{chap:lab-model-builder}
\section{Цель занятия}
В данной лабораторной работе продолжается изучение принципов работы симулятора Simics. В ней будут определены основные понятия, используемые при работе с симуляцией и при написании моделей устройств. В качестве основного языка программирования моделей будет использоваться Си.
Кроме использования готовых моделей устройств из поставляемых в составе Simics, имеется возможность создавать свои собственные модели различных устройств. Для этого симулятор предоставляет среду сборки, API для взаимодействия с симулятором и специальный язык DML для быстрой разработки периферийных устройств\footnote{В данной работе DML не будет использоваться; однако Simics API доступен из всех поддерживаемых языков, включая Python, Си и C++.}. Подробное описание принципов написания моделей периферийных устройств дано в~\cite{model-builder}; особенности интеграции моделей центральных процессоров описываются в~\cite{processor-integration}.
\section{Модули Simics}
Базовая единица загрузки нового кода Simics --- \textit{модуль}, разделяемая библиотека (в Linux это файлы с расширением .so или .pymod), использующая API Simics. Каждый класс объектов должен содержаться максимум в одном модуле. При этом один модуль может содержать в себе более одного класса моделей.
Для просмотра текущих загруженных в симуляцию модулей используйте команду \texttt{list-modules -l}. Например, в <<пустой>> симуляции этот список выглядит так (флаг \texttt{-v} позволяет увидеть больше информации о том, откуда загружены модули):
\begin{lstlisting}
simics> list-modules -v -l
Name Status ABI Build ID Thread-safe
Path
-----------------------------------------------------------
breakpoint-manager Loaded 4052 4145 Yes
/opt/simics/simics-4.6/simics-4.6.103/linux64/lib/breakpoint-manager.so
software-tracker Loaded 4052 4145 Yes
/opt/simics/simics-4.6/simics-4.6.103/linux64/lib/software-tracker.pymod
software-tracker-iface Loaded 4052 4145 Yes
/opt/simics/simics-4.6/simics-4.6.103/linux64/lib/software-tracker-iface.so
\end{lstlisting}
Поиск стандартных модулей, доступных для загрузки, проводится при старте Simics в директориях инсталляции базового и других пакетов. Пользовательские пакеты ищутся в текущем workspace в подпапке \texttt{linux64/lib}. Они помещаются туда в результате успешной компиляции из исходных кодов.
Для загрузки некоторого модуля из командной строки вручную используется команда \texttt{load-module <name>}. После этого становятся доступны все классы и команды, определённые в нём, т.е. можно создавать новые объекты для моделей устройств новых классов.
\section[Исходный код и сборка]{Исходный код и сборка моделей устройств}
Код всех модулей дожен быть размещён в текущем workspace, в поддиректории \texttt{modules}. По общим соглашениям имя директории должно совпадать с именем модуля.
Система сбоки модулей Simics довольно сложна и требует строгого соблюдения процедуры. Она основана на GNU Make~\cite{gmake} и использует компилятор GCC на всех поддерживаемых хозяйских платформах.
\subsection{Генерация шаблонного устройства}
Для начала необходимо сгенерировать <<скелет>> нового устройства --- минимально необходимый набор файлов. Это позволит начать работать с уже компилирующимся примером.
Из workspace выполните команду:
\begin{lstlisting}
$ ./bin/workspace-setup --copy-device sample-device-c
\end{lstlisting}
Теперь в директории \texttt{modules} появился новый элемент \texttt{sample-device-c} с исходными файлами:
\begin{lstlisting}
$ ls -R modules/
modules/:
sample-device-c
modules/sample-device-c:
Makefile commands.py sample-device.c test
modules/sample-device-c/test:
SUITEINFO s-sample-c.py
\end{lstlisting}
\subsection{Сборка с помощью Make}
Для запуска сборки достаточно использовать программу \texttt{make} с именем цели, совпадающей с именем модуля:
\begin{lstlisting}
make sample-device-c
=== Environment Check ===
'/home/simics/workspace' is up-to date
gcc version 4.6.2
=== Building module "sample-device-c" ===
module_id.c
DEP module_id.d
DEP sample-device.d
CC sample-device.o
CC module_id.o
CCLD sample-device-c.so
mod_sample_device_c_commands.pyc
\end{lstlisting}
Далее мы будем работать с файлами внутри директории \texttt{sample-device-c}.
\subsection{Структура Makefile}
В целом синтаксис языка Makefile позволяет писать достаточно сложные и запутанные правила для сборки приложений. В Simics используется собственная система соглашений для упрощения определения модулей. В простейшем случае достаточно перечислить все требуемые файлы с исходными кодами в переменной \texttt{SRC_FILES}. Кроме того, имя нового класса должно указываться в переменной \texttt{MODULE_CLASSES} в \texttt{Makefile}.
\section{Регистрация нового класса}
Перейдём теперь к деталям того, как использовать Simics API для создания нового класса устройств.
При загрузке любого модуля, написанного на Си, Simics исполняет из него единственную функцию с именем \texttt{init_local()}. В ней должна быть выполнена регистрация нового класса объектов с указанием всех его свойств.
Для этого используется функция \texttt{SIM_register_class()}. В примере \texttt{sample-device-c}:
\begin{lstlisting}
conf_class_t *class = SIM_register_class("sample-device-c", &funcs);
\end{lstlisting}
Первый аргумент этой функции --- имя класса, второй --- структура, задающая функцию-конструктор новых копий объекта, а также строки с описанием.
В результате успешного завершения возвращается ссылка на \texttt{class}. Только что созданный класс пока что почти пуст --- он не экспортирует наружу никаких свойств моделируемых объектов. Перейдём к добавлению двух важнейших аспектов любого полноценного класса --- регистрации атрибутов и интерфейсов.
\section{Атрибуты}
Атрибуты позволяют видеть и изменять состояние объекта из командной строки Simics. Кроме того, они необходимы для того, чтобы механизм точек сохранения мог корректно записывать состояние на диск и впоследствии загружать его.
Структура объектов \texttt{sample-device-c} задана следующей структурой:
\begin{lstlisting}
typedef struct {
/* Simics configuration object */
conf_object_t obj;
/* device specific data */
unsigned value;
} sample_device_t;
\end{lstlisting}
Здесь \texttt{conf_object_t obj} --- <<базовый класс>> иерархии объектов Simics, он должен присутствовать в любом объекте. Собственно устройство имеет только один регистр, названный \texttt{value}. Для того, чтобы иметь возможность читать и писать его значение, с этим полем ассоциируется атрибут, также названный value:
\begin{lstlisting}
SIM_register_typed_attribute(
class, "value",
get_value_attribute, NULL, set_value_attribute, NULL,
Sim_Attr_Optional, "i", NULL,
"The <i>value</i> field.");
\end{lstlisting}
Здесь \texttt{class} --- ранее созданный класс устройства, \texttt{get_value_attribute()} и \texttt{set_value_attribute()} --- функции для чтения и записи\footnote{Т.н. getter и setter.} значения, \texttt{"i"} --- тип атрибута (в данном случае это целое число), строка \texttt{"The <i>value</i> field."} --- справка о назначении атрибута. Необязательные аргументы функции \texttt{SIM_register_typed_attribute()} равны \texttt{NULL}.
Особое внимание следует обратить на то, как устроены getter и setter. Значения атрибутов, которые могут иметь довольно сложные типы, упакованы в специальный класс \texttt{attr_value_t}. Для получения значений используется семейство функций \texttt{SIM_attr_*}.
\section{Интерфейсы}
Назначение методов, группируемых в интерфейсы, состоит в изменении состояния устройств, а также чтения значений. В отличе от атрибутов, способных делать то же самое, интерфейсы служат для представления архитектурных возможностей устройств. Атрибуты не имеют выражения в реальной аппаратуре, тогда как интерфейсы напрямую отображаются на шины, сигналы, протоколы и т.п.
Каждый интерфейс имеет уникальное имя и фиксированный набор методов, объединённых общей целью. Модель, желающая предоставлять некоторый интерфейс для других устройств, обязана реализовать один или несколько его методов и затем объявить его доступным. Устройство, имеющее ссылку на объект, может получить по нему заявленные интерфейсы и вызывать включённые в него методы. Таким образом, в Simics интерфейсы предоставляют объектно-ориентированную парадигму для взаимодействия отдельных моделей.
Например, один из атрибутов процессора, настраиваемый на этапе инициализации модели, --- это \texttt{physical_memory}, его тип \texttt{o}, т.е. <<объект>>. Допустим, что \texttt{cpu->physical_memory = mem}. По указателю на объект \texttt{mem} процессор может извлечь из него реализацию интерфейса \texttt{memory-space}, который содержит методы \texttt{read}, \texttt{write}, \texttt{access} и др. для работы с пространствами памяти.
В случае \texttt{sample-device-t} регистрируются два интерфейса --- \texttt{sample_interface} и \texttt{io_memory}.
Пример регистрации первого из них:
\begin{lstlisting}
static const sample_interface_t sample_iface = {
.simple_method = simple_method
};
SIM_register_interface(class, SAMPLE_INTERFACE, &sample_iface);
\end{lstlisting}
Здесь \texttt{sample_iface} --- структура из указателей на функции, которая передаётся функции \texttt{SIM_register_interface()}. В данном случае в ней содержится только один указатель --- \texttt{simple_method}. \texttt{class} --- тот же самый класс, что был получен при регистрации класса, \texttt{SAMPLE_INTERFACE} --- строка\footnote{По соглашениям строки с названиями интерфейсов хранятся в \#define'ах, названных из заглавных букв имени, т.е. \texttt{SAMPLE_INTERFACE} эквивалентно \texttt{"sample_interface"}.} с именем интерфейса.
\section{Ход работы}
\begin{itemize*}
\item Соберите модуль \texttt{sample-device-c}.
\item Измените код внутри модуля так, чтобы поменять имя класса на \texttt{mydevice}, а имя модуля --- на \texttt{mymodule}.
\item Добавьте атрибут \texttt{counter}, содержащий значение целого типа.
\item Измените логику работы \texttt{simple_method()} так, чтобы она изменяла значение \texttt{counter}, например, монотонно увеличивала его.
\item Загрузите Simics, затем подгрузите модуль \texttt{mymodule}.
\item С помощью команды \texttt{help mydevice} проверьте, что новый класс действительно предоставляет все интерфейсы и атрибуты.
\item Создайте объект класса \texttt{mydevice} c именем \texttt{dev0}\footnote{О создании конфигураций рассказывается в следующей главе}.
\item Вызовите метод simple_method из устройства с помощью команды
\begin{lstlisting}
simics> @conf.dev0.iface.sample_iface.simple_method(3)
\end{lstlisting}
\item Проверьте, что значение атрибута \texttt{counter} изменилось.
\end{itemize*}
\section{Контрольные вопросы}
\begin{enumerate*}
\item Чем отличается вывод команды \texttt{list-modules} с флагом \texttt{-l} и без него?
\item Некоторые найденные модули по той или иной причине могут быть отвергнуты при загрузке. Увидеть их список можно с помощью команды \texttt{list-failed-modules}. Загрузите модель Viper и определите причины, по которым список, выдаваемый командой \texttt{list-failed-modules}, не пуст.
\item По каким причинам не следует использовать манипуляцию атрибутами одного устройства из другого?
\item Объясните, для чего служит атрибут \texttt{add_log}, регистрируемый в конце \texttt{init_local()}.
\end{enumerate*}
\iftoggle{webpaper}{
\printbibliography[title={Список литературы к занятию}]
}{}