Архитектура и система команд RISC-V

Базовая статья RISC-V и RISC-V

Рассмотрим наиболее примитивную модель процессора из эмулятора Ripes

Цикл работы процессора:

  1. (F, Istruction Fetch)

    • Выборка очередной инструкции из памяти: в регистр PC заносится либо результат прибавления 4 к предыдущему значению PC, либо адрес перехода из Branch, а затем содержимое по этому адресу считывается в Istruction Memory

  2. (D, Instruction decode)

    • Декодирование опкода / функций
    • Заполнение блока операндов Registers значениями из соответствующих регистров и непосредственного операнда в Immediate (если присутствует в инструкции)

  3. (E, Execute operation)

    • Работа ALU: операции над содержимым блока операндов (регистрами и непосредственным значением) — сложение, сдвиг и прочие преобразования данных, а также вычисление адресов (перехода и адресации со смещением)

  4. (M, Memory access)

    • Работа с памятью (чтение / запись)
  5. (W, Write back)

    • Обновление блока операндов и самих регистров (самый последний элемент на схеме)

Система команд RISC-V

  • 4 базовых типа команд

    LecturesCMC/ArchitectureAssembler2022/01_AboutRiscV/RISCV_4_Commands.png

    • R — типа «регистр-регистр-регистр» (Register)

    • I — типа «непосредственное значение-регистр-регистр» (Immediate)

    • S — типа «регистр-регистр-непосредственное значение» (Store)

    • U — типа «непосредственное значение-регистр» (Upper)

  • Пояснения к схеме:
    • opcode — код операции (6 битов)

    • rs1 — № регистра-источника (5 битов)

    • rs2 — № регистра-операнда (5 битов)

      • rd — № регистра-приёмника (5 битов)

    • imm[11:…] — непосредственный операнд размером в 12 битов

      • В случае, когда непосредственное значение определяет «приёмник» (смещение адреса для «близкого» перехода или записи результата в память), 12 битов целиком в поле rd не помещаются, и его приходится «распиливать» (инструкция типа S).

      • Непосредственный операнд всегда знаковый, и его знак всегда приходится на 31-й бит. Это значит, что процессору легко отличить отрицательное число от положительного, даже если оно хранится в двух частях машинного слова: у отрицательного числа единичный 31-й бит
    • imm[31:…] — непосредственный операнд размером в 20 битов. Используется в инструкциях типа U для заполнения старших двадцати битов регистра (в операциях «далёкого» перехода и как дополнительная инструкция при записи в регистр полного 32-разрядного непосредственного операнда)

      • 31-й бит снова знаковый!
    • funct — поле функции (6 битов), используется для разных инструкций, у которых код операции одинаковый. Например, все арифметические инструкции типа I имеют одинаковый opcode OP-IMM (чему он равен?), а различаются полем funct. По-видимому, для эффективной реализации R-команд в конвейере удобнее не декодировать опкод, а по-быстрому сравнить его с нулём, и получать значения регистров, параллельно декодируя функцию, чтобы потом её применить.

  • Интерпретация значения imm может отличаться (например, в командах перехода в imm хранится смещение без последнего бита, так как адрес инструкции всегда чётен)

Система расширяема

Стоит ещё раз заметить, что в ISA RISC-V невозможно хранить полный адрес непосредственно в инструкции (как это было в УМ). Полный адрес хранится в регистре (в зависимости от типа инструкции либо в pc, либо в x*), а в непосредственном операнде указывается смещение.

Язык ассемблера

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

Задачи, решаемые ассемблером:

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

Цикл программирования на языке ассемблера — классический для компилируемого языка:

  1. Написание текста программы
  2. Трансляция в машинные коды (запускаемый файл)
  3. Загрузка запускаемого файла в память и запуск

Специфика RARS: программа транслируется сразу в соответствующие места машинной памяти, нет необходимости хранить оттранслированный запускаемый файл.

Обзор ISA

Шпаргалка (NB! в RARS есть встроенная подсказака)

Общий вид инструкции ассемблера RISC-V:

[метка:]  операция [операнд1[, операнд2[, …]]]

В тексте программы на языке ассемблера могут встречаться т. н. директивы — команды самому ассемблеру.

<!> TODO Примеры инструкций (базовый набор?)

Псевдоинструкции

Псевдоинструкции — это заложенные в спецификацию конструкции языка ассемблера, семантика которых не совпадает с машинными командами, в которые они раскрываются

Обратите также внимание на то, что к регистрам регистры можно обращаться как xРЕГИСТР, так и по их мнемонике (tРЕГИСТР, aРЕГИСТР, sРЕГИСТР, zero и т. п.), отражающей ковенции их использования.

FrBrGeorge/MyDict/speech_balloon_question.png Найдите все поля инструкций типа I и U в колонке Code из примера выше.

Полный список инструкций, поддерживаемых в RARS.

Представление о «внешних вызовах» (environment call)

Низкоуровневое программирование (в машинных кодах или на языке ассемблера) требует некоторого количества высокоуровневых операций. Например, простейший ввод десятичных чисел с клавиатуры, если его реализовывать от начала до конца, становится крайне сложным мероприятием: надо управлять внешним устройством (клавиатурой), отслеживать появление на нём введённых символов, выявлять конец ввода этих символов, хранить их где-то, а после окончания ввода — преобразовывать из строкового представления (каждый символ — это один байт, обозначающий цифру) в десятичное число (одна ячейка памяти). И это мы ещё не договорились об обработке ошибок ввода!

Зачастую исполнитель низкоуровневых команд вообще не выполняет такие сложные операции, а передаёт их «на уровень выше»: кто-то или что-то, что запустило программу, пускай само позаботится о вводе и выводе. Например, в учебных машинах вводом и выводом занимается сама программа modelmachine по запросу программиста, а в системе команд вообще ничего про ввод и вывод нет.

В RISC-V это «обращение к запустившему нас окружению» включено в систему команд — это инструкция ecall. С её помощью образом программа может самостоятельно вводить и выводить данные (и выполнять множество других функций), если известно, что эти операции умеет выполнять окружение. Окружением (aka «то, что нас запустило») может при этом являться что угодно — ядро операционной системы, гипервизор, аппаратура, а в случае RARS — программный код на Java.

Список таких функций сильно зависит от природы окружения. Например, в RARS они определены так. От программиста требуется только положить в регистр a7 (x17) номер внешнего вызова согласно таблице, в регистры a0 - a6 (x10 - x16) — ожидаемые этим конкретным внешним вызовом параметры и выполнить инструкцию ecall.

По большей части в домашних заданиях мы будем пользоваться тремя внешними вызовами — ввод, вывод и останов.

   1         li      a7 5        # Внешний вызов №5 — ввести десятичное число
   2         ecall               # Результат — в регистре a0
   3         mv      t0 a0       # Сохраняем результат в t0
   4         ecall               # Регистр a7 не менялся, тот же внешний вызов
   5         add     a0 t0 a0    # Прибавляем ко второму число первое
   6         li      a7 1        # Внешний вызов №1 — вывести десятичное число
   7         ecall
   8         li      a7 10       # Внешний вызов №10 — останов программы
   9         ecall

LecturesCMC/ArchitectureAssemblerProject/07_Arcitecture (последним исправлял пользователь FrBrGeorge 2024-07-07 19:29:28)