Предложения по дополнению и улучшению лабораторной 15 (#52)

* Предложения по дополнению и улучшению лабораторной 15

* Apply suggestions from code review

Co-authored-by: Andrei Solodovnikov <VoultBoy@yandex.ru>

---------

Co-authored-by: Mikhail Popov <mikhail.popov@bsc.es>
Co-authored-by: Andrei Solodovnikov <VoultBoy@yandex.ru>
This commit is contained in:
Mikhail Popov
2024-01-29 16:27:45 +02:00
committed by GitHub
parent 65c307d526
commit b8e1fddcf5

View File

@@ -9,42 +9,42 @@
## Цель
На текущий момент мы создали процессорную систему, которая способна взаимодействовать с внешним миром посредством периферийных устройств ввода-вывода и программатора. Однако сложно понять, какое место данная система занимает в ряду уже существующих вычислительных систем.
Дать количественную оценку характеризующую производительность реализованной вычислительной системы. На текущий момент мы создали процессорную систему, которая способна взаимодействовать с внешним миром посредством периферийных устройств ввода-вывода и программатора, по сути являющуюся компьютером. Однако встает вопрос, какое место данная система занимает в ряду уже существующих вычислительных систем.
<!-- Ближе ли она по производительности к [разностной машине Бэббиджа](https://ru.wikipedia.org/wiki/Разностная_машинаарльза_Бэббиджа) или же к [Frontier](https://ru.wikipedia.org/wiki/Frontier_(суперкомпьютер)) (первому в мире [экзафлопсному компьютеру](https://ru.wikipedia.org/wiki/Exascale_computing)). Получится ли на ней запустить Doom или Skyrim? Всего этого мы не узнаем наверняка до тех пор, пока не произведем оценку производительности. -->
Для оценки производительности мы должны будем модифицировать процессорную систему, собрать и запустить специализированное ПО, отвечающее за измерение производительности (мы будем использовать программу Coremark).
Для оценки производительности необходимо модифицировать сущесутвующую процессорную систему, а после собрать и запустить специализированное ПО, отвечающее за измерение производительности (будет использована программа Coremark).
## Теория
[Coremark](https://www.eembc.org/coremark/faq.php) (далее кормарк) — это набор синтетических тестов для измерения производительности процессорной системы. В данный набор входят такие тесты, как работа со связными списками, матричные вычисления, обработка конечных автоматов и подсчет контрольной суммы. Результат выражается в одном числе, которое можно использовать для сравнения с результатами других процессорных систем.
[Coremark](https://www.eembc.org/coremark/faq.php) (далее кормарк) — это набор синтетических тестов (специальных программ) для измерения производительности процессорной системы. В данный набор входят такие тесты, как работа со связными списками, матричные вычисления, обработка конечных автоматов и подсчет контрольной суммы. Результат выражается в одном числе, которое можно использовать для сравнения с результатами других процессорных систем.
Для подсчета производительности, кормарк опирается на функцию, возвращающую текущее время, поэтому для оценки производительности нам потребуется вспомогательное периферийное устройство: таймер.
Для вывода результатов тестирования, необходимо описать способ, которым кормарк сможет выводить очередной символ сообщения (для этого мы будем использовать контроллер UART из предыдущих лабораторных работ).
Для вывода результатов тестирования, необходимо описать способ, которым кормарк сможет выводить очередной символ сообщения для этого мы будем использовать контроллер UART из предыдущих лабораторных работ.
Кроме того, скомпилированная без оптимизаций программа будет занимать чуть более 32KiB, поэтому нам потребуется изменить размер памяти инструкций
Кроме того, скомпилированная без оптимизаций программа будет занимать чуть более 32KiB, поэтому нам потребуется изменить размер памяти инструкций.
Таким образом, для того чтобы запустить данную программу, нам необходимо выполнить как аппаратные изменения процессорной системы (добавить таймер и (если отсутствует) контроллер UART), так и программные изменения самого кормарка (для этого в нем предусмотрены специальные платформозависимые файлы, в которых объявлены функции, реализацию которых нам необходимо выполнить).
## Задание
1. Реализовать модуль-контроллер "таймер".
2. Подключить этот модуль к системной шине.
2.1 В случае, если до этого в ЛР12 вашим устройством вывода было не UART TX, вам необходимо подключить к системной шине и готовый модуль [uart_tx_sb_ctrl](../Made-up%20modules/lab_12.uart_tx_sb_ctrl.sv).
3. Добавить реализацию платформозависимых функций программы coremark.
4. Скомпилировать программу.
1. Реализовать модуль-контроллер "таймер"
2. Подключить этот модуль к системной шине
2.1. В случае, если до этого в ЛР12 вашим устройством вывода было не UART TX, вам необходимо подключить к системной шине готовый модуль [uart_tx_sb_ctrl](../Made-up%20modules/lab_12.uart_tx_sb_ctrl.sv)
3. Добавить реализацию платформозависимых функций программы coremark
4. Скомпилировать программу
5. Изменить размер памяти инструкций
6. Запустить моделирование
7. Сравнить результаты измерения производительности с результатами существующих процессорных системам.
7. Сравнить результаты измерения производительности с результатами существующих процессорных системам
### Таймер
Разберемся с тем, как будет работать наш таймер. По сути, это просто системный счетчик (не путайте с программным счетчиком), непрерывно считающий такты с момента последнего сброса. Для измерения времени мы будем засекать значение счетчика на момент начала отсчета и значение счетчика в конце отсчета. Зная тактовую частоту и разность между значениями счетчика мы с легкостью сможем вычислить прошедшее время. При этом нужно обеспечить счетчик такой разрядностью, чтобы он точно не смог переполниться.
Разберемся с тем, как будет работать наш таймер. По сути, это просто системный счетчик (не путайте с программным счетчиком), непрерывно считающий такты с момента последнего сброса. Системным он называется потому, что работает на системной тактовой частоте. Для измерения времени мы будем засекать значение счетчика на момент начала отсчета и значение счетчика в конце отсчета. Зная тактовую частоту и разность между значениями счетчика мы с легкостью сможем вычислить прошедшее время. При этом нужно обеспечить счетчик такой разрядностью, чтобы он точно не смог переполниться.
Поскольку мы уже назвали данный модуль "таймером", чтобы тот не был слишком простым, давайте добавим ему функциональности: пускай это будет устройство, способное генерировать прерывание через заданное число тактов. Таким образом, процессорная система сможет засекать время без постоянного опроса счетчика.
Было бы удобно, чтобы мы могли управлять тем, каким образом данный модуль будет генерировать такое прерывание: однократно, заданное число раз или же бесконечно, пока тот не остановят.
Было бы удобно, чтобы мы могли управлять тем, каким образом данный модуль будет генерировать такое прерывание: однократно, заданное число раз, или же бесконечно, пока тот не остановят.
Таким образом, мы сформировали следующее адресное пространство данного контроллера:
@@ -104,9 +104,9 @@ logic [63:0] system_counter_at_start;
### Настройка Coremark
В первую очередь, необходимо скачать исходный код данной программы, размещенный по адресу: [https://github.com/eembc/coremark](https://github.com/eembc/coremark). На случай возможных несовместимых изменений в будущем, все дальнейшие ссылки будут даваться слепок репозитория, который был на момент коммита `d5fad6b`.
В первую очередь, необходимо скачать исходный код данной программы, размещенный по адресу: [https://github.com/eembc/coremark](https://github.com/eembc/coremark). На случай возможных несовместимых изменений в будущем, все дальнейшие ссылки будут даваться на слепок репозитория, который был на момент коммита `d5fad6b`.
Нам необходимо добавить поддержку нашей процессорной системы. Для этого необходимо
После этого, чтобы добавить поддержку нашей процессорной системы потребуется:
1. Реализовать функцию, измеряющую время
2. Реализовать функцию, выводящую очередной символ сообщения с результатами
@@ -115,7 +115,7 @@ logic [63:0] system_counter_at_start;
Все файлы, содержимое которых мы будем менять расположены в папке [barebones](https://github.com/eembc/coremark/tree/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones).
#### Реализация функции, измеряющей время
#### 1. Реализация функции, измеряющей время
Не мы первые придумали измерять время путем отсчета системных тактов, поэтому вся логика по измерению времени уже реализована в coremark. От нас требуется только реализовать функцию, которая возвращает текущее значение системного счетчика.
@@ -140,15 +140,15 @@ barebones_clock()
На этом наша задача по измерению времени завершена. Остальные правки будут не сложнее этих.
#### Реализация вывода очередного символа сообщения
#### 2. Реализация вывода очередного символа сообщения
Для вывода очередного символа во встраиваемых системах используется (какое совпадение!) функция [`uart_send_char`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/ee_printf.c#L663), расположенная в файле [`ee_printf.c`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/ee_printf.c).
В реализации данной функции вам уже предлагают алгоритм, по которому та должна работать, необходимо:
В реализации данной функции вам уже предлагают алгоритм, по которому та должна работать. Необходимо:
1. дождаться готовности UART к отправке
2. передать отправляемый символ
3. дождаться готовности UART к отправке (завершения передачи)
1. дождаться готовности UART к отправке;
2. дередать отправляемый символ;
3. дождаться готовности UART к отправке (завершения передачи).
Давайте так и реализуем эту функцию:
@@ -163,11 +163,11 @@ uart_send_char(char c)
```
`0x06000000` — базовый адрес контроллера UART TX из ЛР12 (и адрес передаваемых этим контроллером данных).
`0x08` — адрес регистра `busy` в адресном пространстве этого контроллера.
`0x08` смещение до адреса регистра `busy` в адресном пространстве этого контроллера.
#### Реализация функции первичной настройки
#### 3. Реализация функции первичной настройки
Это функция [`portable_init`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/core_portme.c#L130), расположенная в уже известном ранее файле [`core_portme`.c]. Данная функция выполняет необходимые нам настройки перед началом теста. Для нас главное — настроить нужным образом контроллер UART.
Это функция [`portable_init`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/core_portme.c#L130), расположена в уже известном ранее файле [`core_portme`.c]. Данная функция выполняет необходимые нам настройки перед началом теста. Для нас главное — настроить нужным образом контроллер UART.
Допустим, мы хотим чтобы данные передавались на скорости `115200`, c одним стоповым битом и контролем бита четности. В этом случае, мы должны добавить в начало функции следующий код:
```C
@@ -182,9 +182,9 @@ portable_init(core_portable *p, int *argc, char *argv[])
}
```
#### Дополнительные настройки
#### 4. Дополнительные настройки
Для тонких настроек используется заголовочный файл [`core_portme.h`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/core_portme.h). Нам необходимо:
Для тонких настроек используется заголовочный файл [`core_portme.h`](https://github.com/eembc/coremark/blob/d5fad6bd094899101a4e5fd53af7298160ced6ab/barebones/core_portme.h), куда также требуется внести несколько изменений. Нам необходимо:
1. Объявить в начале файла макрос `ITERATIONS`, влияющий на количество прогонов теста. Нам достаточно выставить значение 1.
2. Обновить значение макроса `COMPILER_FLAGS`, заменив его значение `FLAGS_STR` на`"-march=rv32i_zicsr -mabi=ilp32"`, именно с этими аргументами мы будем собирать программу. Это опциональная настройка, которая позволит вывести флаги компиляции в итоговом сообщении.
@@ -202,7 +202,7 @@ portable_init(core_portable *p, int *argc, char *argv[])
make
```
В случае, если на вашем рабочем компьютере не установлена эта утилита, вы скомпилировать программу вручную, выполнив следующую серию команд:
В случае, если на вашем рабочем компьютере не установлена утилита `make`, то вы можете скомпилировать программу вручную, выполнив следующую последовательность команд:
```bash
cp barebones/*.c barebones/*.h ./
@@ -232,13 +232,13 @@ sed -i '1d' coremark_data.mem
### Изменение размера памяти инструкций
Как видите, размер секции инструкций превышает 32KiB на 1556 байт. Поэтому на время оценки моделирования, нам придется увеличить размер памяти инструкций до 64KiB, изменив число слов памяти инструкций до 16384. При этом необходимо изменить диапазон бит адреса, используемых для чтения инструкции из памяти с `[11:2]` на `[15:2]`.
Как видите, размер секции инструкций превышает 32KiB на 1556 байт (32768—34324). Поэтому на время оценки моделирования, нам придется увеличить размер памяти инструкций до 64KiB, изменив число **слов** памяти инструкций до 16384. При этом необходимо изменить диапазон бит адреса, используемых для чтения инструкции из памяти с `[11:2]` на `[15:2]`.
Обратите внимание, что увеличение размера памяти в 16 раз приведет к значительному увеличению времени синтеза устройства, поэтому данное изменение мы производим исключительно на время поведенческого моделирования.
### Запуск моделирования
Программирование 32KiB по UART займет продолжительное время, поэтому вам предлагается проинициализировать память инструкций и данных "по старинке" через системные функции `$readmemh`.
Программирование 32KiB по UART займет ощутимое время, поэтому вам предлагается проинициализировать память инструкций и данных "по старинке" через системные функции `$readmemh`.
Если все было сделано без ошибок, то примерно на `276ms` времени моделирования вам начнется выводиться сообщение вида:
@@ -265,29 +265,29 @@ Correct operation validated. See README.md for run and reporting rules.
## Порядок выполнения задания
1. [Опишите](#таймер) таймер в виде модуля `timer_sb_ctrl`.
2. Проверьте описанный модуль с помощью модуля [tb_timer](tb_timer.sv).
2. Проверьте описанный модуль с помощью тестового окружения [tb_timer](tb_timer.sv).
3. Подключите `timer_sb_ctrl` к системной шине. Сигнал прерывания этого модуля подключать не нужно.
2.1 В случае, если до этого в ЛР12 вашим устройством вывода было не UART TX, вам необходимо подключить к системной шине и готовый модуль [uart_tx_sb_ctrl](../Made-up%20modules/lab_12.uart_tx_sb_ctrl.sv).
4. Получите исходники программы Coremark. Для этого можно либо склонировать репозиторий, либо скачать его в виде архива со страницы: [https://github.com/eembc/coremark](https://github.com/eembc/coremark).
5. Добавьте реализацию платформозависимых функций программы coremark. Для этого в папке `barebones` необходимо:
1. в файле `core_portme.c`:
1. [реализовать](#реализация-функции-измеряющей-время) функцию `barebones_clock`, возвращающую текущее значение системного счетчика;
1. [реализовать](#1-реализация-функции-измеряющей-время) функцию `barebones_clock`, возвращающую текущее значение системного счетчика;
2. объявить макрос `CLOCKS_PER_SEC`, характеризующий тактовую частоту процессора;
3. [реализовать](#реализация-функции-первичной-настройки) функцию `portable_init`, выполняющую первичную инициализацию периферийных устройств до начала теста;
2. в файле `ee_printf.c` [реализовать](#реализация-вывода-очередного-символа-сообщения) функцию `uart_send_char`, отвечающую за отправку очередного символа сообщения о результате.
3. [реализовать](#3-реализация-функции-первичной-настройки) функцию `portable_init`, выполняющую первичную инициализацию периферийных устройств до начала теста;
2. в файле `ee_printf.c` [реализовать](#2-реализация-вывода-очередного-символа-сообщения) функцию `uart_send_char`, отвечающую за отправку очередного символа сообщения о результате.
6. Добавьте с заменой в корень программы файлы [Makefile](Makefile), [linker_script.ld](linker_script.ld) и [startup.S](../13.%20Programming/startup.S).
7. Скомпилируйте программу вызовом `make`.
1. Если кросскомпилятор расположен не в директории `C:/riscv_cc`, перед вызовом `make` вам необходимо соответствующим образом отредактировать первую строчку в `Makefile`.
2. В случае отсутствия на компьютере утилиты `make`, вы можете самостоятельно скомпилировать программу вызовом команд, представленных в разделе ["Компиляция"](#компиляция).
8. Временно измените размер памяти инструкций до 64KiB.
1. Для этого необходимо изменить размер памяти инструкций с 1024 слов до 16384 слов.
2. Кроме того, необходимо изменить используемые индексы адреса с `[11:2]` на `[15:2]`.
2. Кроме того, необходимо изменить используемые индексы адреса в памяти с `[11:2]` на `[15:2]`.
9. Проинициализируйте память инструкций и память данных файлами "coremark_instr.mem" и "coremark_data.mem", полученными в ходе компиляции программы.
10. Выполните моделирование системы с помощью модуля [tb_coremark](tb_coremark).
1. Результаты теста будут выведены приблизительно на `335ms` времени моделирования.
<details>
<summary>10. Прочти меня после успешного завершения моделирования </summary>
<summary>11. Прочти меня после успешного завершения моделирования </summary>
Итак, вы получили сообщение вида:
@@ -314,7 +314,7 @@ Correct operation validated. See README.md for run and reporting rules.
Нас интересует строка:
```text
Iterations/Sec : <скрыто то получения результатов моделирования>
Iterations/Sec : <скрыто до получения результатов моделирования>
```
Это и есть так называемый "кормарк" — метрика данной программы. Результат нашего процессора: 3.88 кормарка.
@@ -335,9 +335,9 @@ Iterations/Sec : <скрыто то получения результатов
А знаете, с чем был сопоставим по производительности компьютер TRS-80? С бортовым компьютером [Apollo Guidance Computer](https://en.wikipedia.org/wiki/Apollo_Guidance_Computer), который проводил вычисления и контролировал движение, навигацию, и управлял командным и лунным модулями в ходе полётов по программе Аполлон.
Иными словами, мы разработали процессор, который приблизительно в 7-14 раз производительнее компьютера, управлявшего полетом космического корабля, который доставившем человека на луну!
Иными словами, мы разработали процессор, который приблизительно в 7-14 раз производительнее компьютера, управлявшего полетом космического корабля, который доставил человека на Луну!
Можно ли как-то улучшить наш результат? Безусловно. Мы можем улучшить его примерно на 5% изменив буквально одну строчку. Дело в том, что для простоты реализации, мы генерировали сигнал `stall` для каждой операции обращения в память. Однако приостанавливать работу процессора было необходимо только для операций чтения из памяти. Если не генерировать сигнал `stall` для операций типа `store`, мы уменьшим время, необходимое на исполнение бенчмарка.
Можно ли как-то улучшить наш результат? Безусловно. Мы можем улучшить его примерно на 5% изменив буквально одну строчку. Дело в том, что для простоты реализации, мы генерировали сигнал `stall` для каждой операции обращения в память. Однако приостанавливать работу процессора было необходимо только для операций чтения из памяти. Если не генерировать сигнал `stall` для операций типа `store`, мы уменьшим время, необходимое на исполнение бенчмарка. Попробуйте сделать это сами.
Добавление умножителей, конвейеризация и множество других потенциальных улучшений увеличат производительность в разы.