263 lines
15 KiB
TeX
263 lines
15 KiB
TeX
\documentclass[a4paper,fontsize=14bp]{article}
|
||
|
||
\input{settings/common-preamble}
|
||
\input{settings/fancy-listings-preamble}
|
||
\input{settings/bmstu-preamble}
|
||
\setcounter{secnumdepth}{4}
|
||
\numerationTop
|
||
|
||
\begin{document}
|
||
\thispagestyle{empty}
|
||
\makeBMSTUHeader
|
||
|
||
\makeReportTitle{лабораторной}{№ 3}{Разработка и интеграция пользовательских инструкций в состав ядра Nios II}{Проектирование цифровых устройств на \\ программируемых логических интегральных схемах (ПЛИС)}{}{С.В. Фёдоров}
|
||
|
||
\newpage
|
||
\pagestyle{fancy}
|
||
\section{Цель}
|
||
Освоить методику использования в составе системы на кристалле пользовательских периферийных модулей и пользовательских инструкций. Реализовать и отладить программное обеспечение системы на кристалле.
|
||
|
||
\section{Задачи}
|
||
\begin{itemize}
|
||
\item Изучить методики расширения набора команд процессора пользовательскими инструкциями.
|
||
\item Реализовать программное обеспечение, использующее пользовательские инструкции на примере инструкции вычисления количества единиц в двоичном числе.
|
||
\item Сравнить быстродействие с программной реализацией на базовом наборе команд.
|
||
\item Выполнить индивидуальное задание.
|
||
\end{itemize}
|
||
|
||
\section{Выполнение работы}
|
||
По шагам из методического материала был создан проект в САПР Quartus Prime (доступен по \href{https://git.iovchinnikov.ru/ivan-igorevich/fpga-lab-2/commits/branch/lab3}{ссылке}).
|
||
|
||
Созданная пользовательская инструкция доступна для обращения из программного кода в прикладном проекте.
|
||
|
||
\section{Индивидуальное задание}
|
||
В качестве индивидуального было дано следующее задание: описать пользовательскую инструкцию, принимающую на вход два 32-разрядных значения и возвращающая минимальное, максимальное, верхнее медианное и нижнее медианное значения. Схема получения данных на рис. \hrf{pic:indi-scheme}. Исходные данные представляют собой два 32-разрядных слова, каждое из которых необходимо разделить на четыре 8-разрядных слова, отсортировать все восемь 8-разрядных слов по возрастанию и взять их минимум (1), максимум (4) и медианы (2, 3).
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\fontsize{12}{1}\selectfont
|
||
\includesvg[scale=1.01]{pics/03-fpga-03-lab-indi-scheme.svg}
|
||
\caption{Схема обработки данных}
|
||
\label{pic:indi-scheme}
|
||
\end{figure}
|
||
|
||
Поскольку сложность самой быстрой сортировки $O(N*\lg_2(N))$, принято решение опираться на то, что значений в искомом множестве всегда фиксированное число. Сложность задания состоит в том, чтобы получить медианы не отсортированного множества за время меньшее, чем $O(N*\lg_2(N))$. Отсутствие необходимости разработки для множеств не фиксированной длины позволяет воспользоваться несколькими способами получения медианных значений.
|
||
|
||
\subsection{Описание схемотехнического решения}
|
||
Дан исходный числовой ряд (восемь байт, полученных из двух 32-разрядных переменных), например
|
||
|
||
\[ [31, 44, 216, 0, 132, 68, 18, 100]. \]
|
||
|
||
Изначально был применён алгоритм комбинационного поиска медианы для 8 значений:
|
||
\begin{enumerate}
|
||
\item сравнение соседних значений в ряду парами;
|
||
\item меньшие значения сравнить с меньшими значениями пар, а большие с большими;
|
||
\item взять меньшую из больших полученных пар и б\'{о}льшую из меньших, также сравнить меньшие с меньшими и б\'{о}льшие с б\'{о}льшими;
|
||
\end{enumerate}
|
||
\begin{equation*}
|
||
\begin{gathered}
|
||
[31, 44, 216, 0, 132, 68, 18, 100]
|
||
\to
|
||
\begin{bmatrix}
|
||
31, 44 \\ 0, 216 \\ 68, 132 \\ 18, 100
|
||
\end{bmatrix}
|
||
\\ \to
|
||
\begin{bmatrix}
|
||
0, 31 \\ 18, 68 \\ 44, 216 \\ 100, 132
|
||
\end{bmatrix}
|
||
\to
|
||
\begin{bmatrix}
|
||
44, 100 \\ 31, 68
|
||
\end{bmatrix}
|
||
\\ \to [31, 44, 68, 100] \to 44, 68;
|
||
\end{gathered}
|
||
\end{equation*}
|
||
|
||
Из приведённых вычислений очевидно, что:
|
||
\begin{itemize}
|
||
\item нижняя медиана = 44
|
||
\item верхняя медиана = 68.
|
||
\end{itemize}
|
||
|
||
Однако, дополнительные тесты показали, что алгоритм работает не для всех возможных вариантов начального распределения значений во множестве. Исходные коды модуля \hrf{lst:mediancomb} и вспомогательного \hrf{lst:lessmore} приведены в приложении \hrf{appendix:src}.
|
||
|
||
\begin{lstlisting}[language=Verilog,style=VerilogStyle,caption={\code{minmedmax.sv}},label={lst:mmm}]
|
||
module minmedmax
|
||
(
|
||
input clk, reset,
|
||
input [31:0] in1, in2,
|
||
output reg [31:0] result
|
||
);
|
||
|
||
logic [7:0] temp [0:7];
|
||
integer status [0:7];
|
||
|
||
assign temp = '{in1[31:24], in1[23:16], in1[15:8], in1[7:0], in2[31:24], in2[23:16], in2[15:8], in2[7:0]};
|
||
|
||
integer i, s, mini, maxi;
|
||
always_comb begin
|
||
for (s = 0; s < 6; s = s + 1) begin
|
||
if (s == 0) begin status = '{0,0,0,0,0,0,0,0}; end
|
||
mini = 0; maxi = 0;
|
||
for (i = 0; i < 8; i = i + 1) begin
|
||
if ((temp[i] <= temp[mini] || status[mini] == 1) && status[i] != 1) mini = i;
|
||
if ((temp[i] >= temp[maxi] || status[maxi] == 1) && status[i] != 1) maxi = i;
|
||
end
|
||
status[mini] = 1;
|
||
if (s == 0) begin
|
||
result[31:24] = temp[maxi];
|
||
result[7:0] = temp[mini];
|
||
status[maxi] = 1;
|
||
end
|
||
if (s == 3) begin result[15:8] = temp[mini]; end
|
||
if (s == 4) begin result[23:16] = temp[mini]; end
|
||
if (s == 5) begin status = '{0,0,0,0,0,0,0,0}; end
|
||
end
|
||
end // always_comb
|
||
endmodule
|
||
\end{lstlisting}
|
||
|
||
Конечный вариант модуля вычисления медиан, минимума и максимума представлен в листинге \hrf{lst:mmm}. Модуль осуществляет проход по сформированной шине из 8-разрядных значений, являющихся результатом конкатенации двух входящих 32-разрядных значений. Создаётся вспомогательный массив для отметок о проверке значения. Основной цикл опирается на сведения о том, что значений всегда восемь, поэтому итераций внешнего цикла нужно шесть (на первой будет найден минимум и максимум, на четвёртом и пятом - медианы, на шестом очищена сервисная шина). Основной цикл проходит по всей 64-разрядной временной шине и выставляет флаги минимума и максимума по следующему условию: \textit{проверяемый} элемент минимальный (максимальный), если он меньше (больше) \textit{найденного} на предыдущем шаге минимального (максимального) элемента или \textit{найденный} уже проверен\footnote{условие добавлено для первой итерации цикла в случаях, когда первой элемент является минимальным или максимальным.} и если проверяемый элемент не был проверен ранее.
|
||
|
||
\subsection{Описание программного решения}
|
||
Два входящих слова записываются во временный указатель и интерпретируются, как указатель на восемь 8-разрядных переменных \code{alt_u8}, далее цикл работает с ними как с массивом данных. На каждом шаге цикла ищется минимальный и максимальный элемент. Найденные элементы меняются местами с теми числами, которые находятся на месте действительно минимального и максимального элемента соответственно. Алгоритм являет собой совмещение \textit{сортировки выбором} и \textit{шейкерной сортировки}. Таким образом за четыре итерации получается сортированное множество, в котором необходимые значения берутся по индексу.
|
||
|
||
\begin{equation*}
|
||
\begin{gathered}
|
||
[31, 44, 216, 0, 132, 68, 18, 100] \to \\
|
||
[0, 44, 100, 31, 132, 68, 18, 216] \to \\
|
||
[0, 18, 100, 31, 44, 68, 132, 216] \to \\
|
||
[0, 18, 31, 68, 44, 100, 132, 216] \to \\
|
||
[0, 18, 31, 44, 68, 100, 132, 216]
|
||
\end{gathered}
|
||
\end{equation*}
|
||
|
||
Программная реализация алгоритма копирует исходные значения функцией \code{memset(dst, src, size)} и обрабатывает ситуацию, в которой найденный максимум равен найденному минимуму.
|
||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||
alt_u32 ones_sw (
|
||
alt_u32* data_block_a,
|
||
alt_u32* data_block_b,
|
||
alt_u32 words) {
|
||
alt_u32 result;
|
||
int word;
|
||
for (word = 0; word < words; ++word) {
|
||
alt_u8 tmp[8];
|
||
memset(tmp, data_block_a[word], 4);
|
||
memset((tmp + 4), data_block_b[word], 4);
|
||
|
||
for (int k = 0; k < 4; ++k) {
|
||
int min = tmp[k];
|
||
int max = min;
|
||
for (int i = k; i < 8 - k; ++i) {
|
||
if (min >= tmp[i]) {
|
||
min = tmp[i];
|
||
tmp[i] = tmp[k];
|
||
tmp[k] = min;
|
||
}
|
||
|
||
if ((max <= tmp[i]) && (max != min)) {
|
||
max = tmp[i];
|
||
tmp[i] = tmp[8 - k - 1];
|
||
tmp[8 - k - 1] = max;
|
||
}
|
||
}
|
||
}
|
||
|
||
result += tmp[0];
|
||
result += (tmp[3] << 8);
|
||
result += (tmp[4] << 16);
|
||
result += (tmp[7] << 24);
|
||
}
|
||
return result;
|
||
}
|
||
\end{lstlisting}
|
||
|
||
\section{Результат и выводы}
|
||
После запуска приложения были получены результаты, представленные на рис. %\hrf{:}.
|
||
|
||
%\begin{figure}[H]
|
||
% \centering
|
||
% \includegraphics[width=12cm]{.}
|
||
% \caption{}
|
||
% \label{pic:}
|
||
%\end{figure}
|
||
|
||
|
||
Пользовательская инструкция для процессора Nios II -- это эффективный инструмент ускорения работы программы и выноса некоторых алгоритмов поточной обработки данных в аппаратную часть.
|
||
|
||
\newpage
|
||
\appendix
|
||
\setcounter{secnumdepth}{4}
|
||
\section{Приложения}
|
||
\subsection{Исходные коды проекта}
|
||
\label{appendix:src}
|
||
|
||
\begin{lstlisting}[language=Verilog,style=VerilogStyle,caption={\code{lessmore.sv}},label={lst:lessmore}]
|
||
module lessmore (
|
||
input [7:0] in1,
|
||
input [7:0] in2,
|
||
output logic [7:0] less,
|
||
output logic [7:0] more
|
||
);
|
||
|
||
always_comb begin
|
||
if (in1 < in2) begin
|
||
less = in1;
|
||
more = in2;
|
||
end else begin
|
||
less = in2;
|
||
more = in1;
|
||
end
|
||
end
|
||
endmodule
|
||
\end{lstlisting}
|
||
|
||
\begin{lstlisting}[language=Verilog,style=VerilogStyle,caption={\code{minmedmax.sv}},label={lst:mediancomb}]
|
||
module minmelhmax (
|
||
input clk,
|
||
input reset,
|
||
input [31:0] in1,
|
||
input [31:0] in2,
|
||
output logic [31:0] result
|
||
);
|
||
|
||
logic [7:0] step1less [0:3];
|
||
logic [7:0] step1more [0:3];
|
||
logic [7:0] step2less [0:3];
|
||
logic [7:0] step2more [0:3];
|
||
logic [7:0] median [0:3];
|
||
logic [7:0] temp [0:3];
|
||
|
||
// 1: compare pairs
|
||
lessmore s01 (in1[7:0], in1[15:8], step1less[0], step1more[0]);
|
||
lessmore s02 (in1[23:16], in1[31:24], step1less[1], step1more[1]);
|
||
lessmore s03 (in2[7:0], in2[15:8], step1less[2], step1more[2]);
|
||
lessmore s04 (in2[23:16], in2[31:24], step1less[3], step1more[3]);
|
||
|
||
// 2: 1st step mins to mins, maxes to maxes
|
||
lessmore s11 (step1less[0], step1less[1], step2less[0], step2more[0]);
|
||
lessmore s12 (step1less[2], step1less[3], step2less[1], step2more[1]);
|
||
lessmore s13 (step1more[0], step1more[1], step2less[2], step2more[2]);
|
||
lessmore s14 (step1more[2], step1more[3], step2less[3], step2more[3]);
|
||
|
||
// 3: 2nd step less-maxes, more-mins
|
||
lessmore s21 (step2less[2], step2less[3], median[0], median[1]);
|
||
lessmore s22 (step2more[0], step2more[1], median[2], median[3]);
|
||
|
||
// 4: median of four
|
||
lessmore s31 (median[0], median[1], temp[1], result[1]);
|
||
lessmore s32 (median[2], median[3], result[2], temp[2]);
|
||
|
||
// 5: max and min of input
|
||
lessmore s41 (step2less[0], step2less[1], result[0], temp[0]);
|
||
lessmore s42 (step2more[2], step2more[3], temp[3], result[3]);
|
||
endmodule
|
||
\end{lstlisting}
|
||
|
||
\lstinputlisting[language=C,style=CCodeStyle,caption={\code{sem.c}},label={lst:sem}]{src/sem.c}
|
||
|
||
\end{document}
|
||
|
||
|
||
|