PIC32MX конвейер

PIC
В прошлой статье осталась некоторая недосказанность по поводу инструкции «nop» сразу после вызова функции.
Такое поведение связано с особенностью архитектуры MIPS32 — «branch delay slot». Как всё это работает?


Это шестая статья из цикла: один, два, три, четыре, пять.

Конвейер
Вычислительный конвейер это метод выполнения инструкций процессором при котором, каждая инструкция разбивается на несколько стадий, а каждая стадия выполняется за один такт процессора.

Процессор состоит из логических элементов.
Чем меньше логических элементов должно работать за один такт, тем большая максимальная тактовая частота у архитектуры. Небольшое количество элементов по самому длинному пути прохождения сигнала не позволяет реализовывать весь функционал команды. Команды деления и умножения занимает несколько тактов, потому что прямая однотактовая реализация требует слишком много логических элементов.
Время прохождения сигнала через логический элемент имеет фиксированное количество микро/нано/пикосекунд.
Невозможно бесконечно наращивать сложность стадии/команды, много логических элементов может не успеть пропустить ток между двумя фронтами генератора тактовой частоты и процессор не будет работать.

Как сделать процессор с несколькими стадиями конвейера?
Берём простой однотактовый процессор, работающий на частоте 20 MHz.
После удачного разделения обработки команды на стадии, например на 6 стадий, мы можем получить процессор работающий на 60-100MHz, просто за счёт сокращения самой длинной цепочки обработки команды, но каждая команда будет занимать не один такт, а 6 тактов.
Вместо нашего быстрого процессора на 20 MHz и 20 MIPS (миллионов инструкций в секунду) мы получаем процессор работающий на частоте 80MHz и 13 MIPS.
Куда делись мегагерцы? Многостадийная обработка добавляет хранение промежуточных результатов и логические элементы для обработки самих стадий.

Это худший случай работы конвейера, все инструкции выполняются одна за другой, стадия за стадией.

Какие бывают стадии?
В классическом RISC процессоре выделяют пять стадий:
IF = Instruction Fetch = считывание инструкции в процессор
ID = Instruction Decode = Декодирование инструкции, выделение команды и операндов, чтение регистров
EX = Execute = Фактическое выполнение инструкции
MEM = Memory access = Загрузка/Сохранение регистра в память
WB = Writeback = запись результата в файл регистров

Каждая стадия использует кусочек процессора. Оставив практически идентичное количество логических элементов по сравнению с однотактовым процессором мы можем получить процессор с несколькими стадиями.

Во время исполнения стадии ID для первой команды, блок IF свободен. Свободную часть процессора мы можем заставить работать над второй командой.
Одновременно исполняя по 6 команд мы получаем 80 MIPS, получив результат производительности в 4 раза больше чем в первоначальном однотактовом процессоре.

Добавим в процессор второй набор всех обработчиков стадий, тогда в пике мы можем получить паралельное исполнение 12 команд и 156MIPS.
Продолжать можно было бы до бесконечности, но с каждой стадией расходы на организацию стадий и хранение данных между стадиями растут вместе с количеством исполняющих блоков. Производительность начинает падать уже на 30-60 стадиях конвейера.

Стадия ID и WB содержат обращение к регистрам. Допустим у нас есть код:
add R1,R2,R3
add R1,R1,R4

После выполнения второй команды в однотактовом процессоре, мы будем получать R1 = (R2 + R3) + R4.
В конвейерном процессоре вторая команда начинает читать данные до того как первая команда запишет результат работы в регистр R1.
Получаем R1 = R1 (до выполнения первой команды) + R4
Это неправильный результат.

Есть два способа добиться правильного результата:
1) Подождать, пока первая команда полностью доработает (закончит стадию WB) и только потом переходить к стадии EX. Теряем пять тактов.
2) Подождать, пока появится результат сложения: задержать ID пока не появится результат сложения MEM(первая команда) и использовать этот результат вместо чтения из файла регистров. Теряем один такт.
2) Передать значение R1 со стадии MEM(первая команда) в стадию EX(вторая команда), исполнение на максимальной скорости.

Первый способ называется «stals».
Второй способ — «slips».
Третий способ — «bypass».

Как обстоят дела, конкретно у PIC32MX:
Смотрим мануал

Деление на стадии происходит исходя из желания разработчиков архитектуры и не обязательно представляет собой классический набор стадий.

Конвейер (pipeline) состоит из пяти стадий:
  • Instruction (I ) Stage
  • Execution (E) Stage
  • Memory (M) Stage
  • Align (A) Stage
  • Writeback (W) Stage

Рассмотрим каждую стадию по подробнее.

Instruction (I ): Стадия инструкции
Аналогично классическому риску (IF стадия)
  • Загружаем команду в процессор.
  • Если это MIPS16e, то преобразовываем её в mips32 формат.

Выполнение:
  • Чтение значений регистра из набора регистров
  • Арифметико-логическое устройство (АЛУ/ALU) начинает выполнять арифметику или логическую операцию.
  • Умножение и деление начинается на этой стадии.
  • ALU вычисляет условие ветвления и рассчитывает адрес назначения прыжка

M стадия — загрузка из памяти:
  • АLU заканчивает работу до начала этой стадии
  • Команды загрузки/сохранения из памяти (lw/sw) читают память
  • Умножение и деление останавливают конвейер на этой стадии
А стадия — выравнивание
  • Выравнивание загруженных данных по границе слова.
  • Данные с этой стадии доступны для Е стадий других команд.
  • Получен результат умножения или деления

W стадия — обратная запись
Новые значения регистров записываются обратно в набор регистров (register file)

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

Конвейер и операции ветвления
Наш процессор имеет 1.5 DMIPS/MHz, значит способен выполнять до 120 миллионов команд в секунду.
Согласно тестам из первых статей мы можем выполнять следующий цикл со скоростью 14 миллионов циклов = 28M команд в секунду.
.delay:
        addiu   $2,$2,-1
        bne     $2,$0,.delay


Почему конвейер не помогает выполняться на максимальной скорости в этом цикле?
Ветвление (bne) зависит от значения регистра $2, который мы модифицируем в команде ALU (addiu).

Как видно вычисление и сохранение результата проходит на разных стадиях. Согласно руководству («2.3 PIC32MX CPU DETAILS»), PIC32MX использует bypass оптимизацию и получает результат сложения на стадии выполнения команды ветвления. Таким образом, возможно выполнение двух команд за шесть тактов.

«2.15.3.2 Branch Delays and the Branch Delay Slot»
При ветвлении есть момент, когда инструкция ветвления уже загружена в конвейер, но ещё не исполнена.
В этот же момент мы можем загружать следующую инструкцию (PC+4). Эта загрузка будет бесполезна, если ветвление сработает. Такая следующая инструкция называется branch delay slot.

В MIPS используется оптимизация: branch delay slot исполняется вне зависимости от условия перехода.
Для обеспечения корректности работы, branch delay slot не может содержать другие операции ветвления.

Посмотрим в «живую» как же происходит работа конвейера:
Онлайн MIPS симулятор с поддержкой конвейера
Загружаем и компилируем код:
.text
main:
         li $t0, 10
         lui $t1, 65535
         ori $t1, $t1, 65535
loop1:
         bne $t0,$0, loop1
         add   $t0, $t0,$t1 # delay slot

loop:  j loop
         nop # delay slot


Ход выполнения программы:
  1. Когда конвейер пустой выполнение инструкции занимает пять тактов
  2. Десять команд выполняются уже за 15 тактов.
  3. После выполнения команды ветвления выполняется «branch delay slot» (но до записи в регистры)
  4. После выполнения «branch delay slot»
  5. Внутри цикла растёт число остановок конвейера «Stall»
  6. Только две стадии конвейера используются

Выводы
Чем больше стадий конвейера, тем больше разброс во времени выполнения «правильного» и «неправильного» кода
MIPS не так прост как кажется, branch delay slot есть только у MIPS, SuperH и SPARC (из ныне живущих процессоров)

Что почитать/посмотреть:
Для тех, у кого нет проблем с восприятием английского языка, есть хороший цикл видео:
www.youtube.com/watch?v=ZQot4fJJPzo
www.youtube.com/watch?v=s0RUg3ribfw
И для бранчей
Для инструкций, которые вычисляются за несколько тактов
  • +2
  • 18 апреля 2014, 11:11
  • ihanick

Комментарии (11)

RSS свернуть / развернуть
Материал интересный, но изложение сумбурное и малопонятное.
0
  • avatar
  • Vga
  • 18 апреля 2014, 17:46
У Rajeev 80 минут насыщенного фактами доклада, MIPS там взят только для примера, принципы общие.
Если интересно, я могу сделать из этого статьи.

Тема простая, но очень объёмная (нужны картинки, схемы), даже в двух словах как в текущем посте получилось 4 экрана текста.

Насколько полезна тема?
Для atmega — безполезна (разве что для осознания приметивности конструкции)
для AVR32 — конвеер на 7 инструкций, полезно понимать почему исполнение может быть в 6-7 раз медленнее.
для MSP430 и STM34F4 не так полезно, всего 3 стадии в конвеере
MPC555 — четыре стадии, но зато сам процессор довольно сложный.
0
MPC555 — четыре стадии, но зато сам процессор довольно сложный.
А это что за зверь?

Что такое конвеер я в курсе, но изложение у тебя все равно сумбурное. Можно было только про особенности конкретно ядра PIC32MX, но не столь сумбурно.
Видео пока не смотрел.
0
А это что за зверь?
Из более менее свежего — Frescale MPC5510, MPC5675K.
PowerPC_e200

Это платформа мотороллы на powerpc, много в автомобильную промышленность продавали.
В последних версиях, даже branch prediction сделали.
0
А мне наоборот показалось, что довольно внятно.
ЗЫ: В заголовке написано «PIX32MX», опечатка, да? PIС должно быть?
0
А мне наоборот показалось, что довольно внятно.
Это уже вторая редакция этой статьи.

Заголовок поправил, спасибо.
0
А delay slot есть только у условных прыжков или у всех? И у CALL/RET он тоже есть, или вызовы в MIPS тоже выполняются прыжком, как в ARM?
0
  • avatar
  • Vga
  • 27 апреля 2014, 19:54
Delay Slot есть у всех прыжков:
4.1.3.2 Branch Delays and the Branch Delay Slot

Конвейер узнаёт куда прыгнем на этапе декодирования, одновременно с этим происходит fetch инструкции delay slot.

В качестве call инструкции используется инструкция jal. Она сохраняет PC+8 в последнем 31ом регистре ($ra).

ret происходит просто как j $ra и тоже имеет delay slot.
0
То есть, по сути, любую команду прыжка, включая jal, нужно размещать за одну команду до реального прыжка, с тем лишь ограничением, чтобы не шло два прыжка подряд?
0
именно так, либо там будет будет какая-то полезная команда, либо nop. Если залезть на online симулятор, ссылка на который внутри поста, то будет видно, что конвейер останавливается благодаря delay slot. Остановки случаются только из-за зависимых инструкций.
0
упс: читать как конвейер НЕ останавливается благодаря delay slot
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.