Лабораторная работа №3. Изучение архитектуры RISC-V на примере ассемблерной программы
3.1 Цель и задачи
Целью работы является получить представление о наборе команд RISC-V (базовый instruction set architecture (ISA), основные типы инструкций)
Для достижения поставленной цели требуется решить следующие задачи:
- Ознакомиться с основными компонентами компьютера.
- Понять взаимосвязь между SW и HW процессора (ISA - мост между SW и HW).
- Понимать взаимосвязь между процессором и языком ассемблера.
- Знать основные этапы компиляции программы и место ассемблера.
- Изучить классический RISC - конвейер.
- Понимать принцип кодирования инструкции на языке ассемблера в машинный код.
Презентация к блоку "Ассемблер RISC-V"
3.2. Основные теоретические сведения
Место RISC-V в современных архитектурах согласно системе команд:
- CISC: одна инструкция = много последовательных операций.
- RISC: одна инструкция = операция, ограниченная по времени выполнения.
- VLIW: одна инструкция = много параллельных операция (или одна большая).
Принципы RISC:
- отсутствие вычислительно сложных инструкций,
- фиксированная длина инструкции,
- большое количество регистров общего назначения,
- ограничения на работу непосредственно с оперативной памятью как с медленным устройством.
Особенности инструкций:
- отсутствие регистра флагов в RISC-V.
- формат из 3-х операндов.
- дополнительные функции отдельных инструкций (add вместо mov).
- базовый набор и стандартные расширения.
- наличие псевдоинструкций.
В архитектуре RISC-V имеется обязательное для реализации небольшое подмножество команд (набор инструкций I - Integer) и несколько стандартных опциональных расширений. В базовый набор входят инструкции условной и безусловной передачи управления/ветвления, минимальный набор арифметических/битовых операций на регистрах, операций с памятью (load/store), а также небольшое число служебных инструкций.
Операции ветвления не используют каких-либо общих флагов, как результатов ранее выполненных операций сравнения, а непосредственно сравнивают свои регистровые операнды. Базис операций сравнения минимален, а для поддержки комплементарных операций операнды просто меняются местами.
3.2.1 Базовый набор регистров и альтернативные имена в стандарте ABI (application binary interface)
Базовое подмножество команд использует следующий набор регистров (Таблица 3.1): специальный регистр x0 (zero), 31 целочисленный регистр общего назначения (x1 - x31), регистр счётчика команд (PC, используется только косвенно), а также множество CSR (Control and Status Registers, может быть адресовано до 4096 CSR).
Номер | Название | Описание |
---|---|---|
x0 | zero | Константа нуля (zero register) |
x1 | ra | Адрес возврата (return address) |
x2 | sp | Указатель стека (stack pointer) |
x3 | gp | Глобальный указатель (global pointer) |
x4 | tp | Указатель потока (thread pointer) |
x5-x7 | t0-t2 | Временные переменные (temporary registers) |
x8 | s0/fp | Сохраняемая переменная /Указатель фрейма стека (saved register / frame pointer) |
x9 | s1 | Сохраняемая переменная (saved register) |
x10-x11 | a0-a1 | Аргументы функций/Возвращаемые значения (function arguments / return values) |
x12-x17 | a2-a7 | Аргументы функций (function arguments) |
x18-x27 | s2-s11 | Сохраняемые переменные (saved registers) |
x28-x31 | t3-t6 | Временные переменные (temporary registers) |
Для лучшей читаемости кода инструкции ассемблера обычно используют специальные имена, например s1, но они также могут использовать номер регистра (например, x9 для регистра номер 9). В нулевом регистре x0 всегда хранится константа 0; попытка записать в него другое значение игнорируется. Регистры от s0 до s11 (регистры 8-9 и 18-27) и от t0 до t6 (регистры 5-7 и 28-31) используются для хранения переменных; ra и регистры от a0 до a7 служат для вызовов функций. Регистры 2-4 носят имена sp, gp и tp. Нет операций над частями регистров, нет каких-либо выделенных "регистровых пар".
Регистр gp (global pointer) представляет собой специальную константу, указывающую в блок данных, где хранятся константы и глобальные переменные. Значение данного регистра инициализируется в начале работы программы (это делает линковщик, когда компонует программу) смещением 0x800 от начала данной секции, что позволяет получать доступ к содержимому секции используя 12-битное смещение со знаком, что значительно упрощает доступ к константам и глобальным переменным. Изменять значение регистра gp самостоятельно не рекомендуется.
Архитектура RISC-V использует только little-endian модель (Рис. 3.1) - первый байт операнда в памяти соответствует наименее значащим битам значений регистрового операнда. RISC-V применяет память с побайтовой адресацией. Это значит, что каждый байт памяти имеет уникальный адрес. Поскольку 32-битное слово состоит из четырех 8-битных байтов, то адрес каждого слова (word address) кратен 4. Старший байт (most significant byte, MSB) находится слева, а младший байт (least significant byte, LSB) - справа.
Инструкции базового набора имеют длину 32 бита. Формат 32-битной машинной команды (признаки - младшие биты всегда quot;11quot; и 2-4 биты ? quot;111quot;).
Обозначения на Рис. 3.2:
- rs1 (5бит) - номер регистра в котором находится первый операнд
- rs2 (5бит) - номер регистра в котором находится второй операнд
- rd (5бит) - номер регистра в который будет записан результат
- opcode (7бит) +funct7+funct3 (10бит) определяют операцию
- imm - непосредственный операнд (константа) в дополнительном коде
Таким образом, в архитектуре RISC-V используются четыре формата инструкций: типа R, типа I, типа S/B и типа U/J.
3.3. Задание к лабораторной работе
Напишите программу на ассемблере, которая пересылает константу 1 в регистр х1. К полученному результату прибавьте 1 и сохраните результат в регистре х1.
- Скомпилируйте программу.
- Представьте программу в машинном коде.
- Выполните вручную декодирование программы в соответствии с типами используемых инструкций.
- Повторите перечисленные выше действия, используя альтернативные имена регистров в стандарте ABI.
3.3.1. Описание последовательности выполнения работы
Скомпилируйте исходную программу и убедитесь в ее работоспособности в RISC-V ОС, либо через qemu-riscv64.
С помощью утилиты objdump дизассемблируйте бинарный файл программы.
Используя информацию о том, как кодируются инструкции, выполните декодирование в виде таблицы.
3.3.2. Пример выполнения задания на защиту
-----code----- .text .global _st art _start: addi x1, x0, 1 addi x1, x1, 1 addi a7, x0, 93 # set a7 = 93, Linux call exit ecall Disassembly of section .text: 00000000000100b0 <_start>: 100b0: 00100093 addi x1, x0, 1 100b4: 00108093 addi x1, x1, 1 100b8: 05d00893 addi a7, x0, 93 100bc: 00000073 ecall -----end code-----
Инструкция addi относится к I-типу, opcode=0x13, funct3=0x0
addi x1, x0, 1 | 0000 0000 0001 | 0000 0 | 000 | 0000 1 | 001 0011 |
addi x1, x1, 1 | 0000 0000 0001 | 0000 1 | 000 | 0000 1 | 001 0011 |
3.4. Вопросы для контроля
- Что входит в базовый набор инструкций?
- За что отвечают регистры a2-a7?
- Поясните, как кодируются инструкции типа J.