Лисья Нора

Оглавление


§ О процессоре

Этот процессор уникален. Не буду говорить о том, сколько лет я пытался просто подумать о том чтобы его сделать, но... много. В любом случае, процессор RISC-V вот почему мне стал сейчас интересен.
У процессора и другие преимущества есть, но я выделил главное. Да, есть такая проблема что набор инструкции 32-х битные, но кто-то не считает что это проблема, а кто-то считает.
Я долго пытался подобраться к нему и наконец, пришло время разобраться и поставить все, полностью все точки на i.

§ Регистры

Количество регистров у процессора не меньше, чем у AVR, а именно 32. Но они, в отличии от AVR, все строго 32х битные. Занимают регистры 1024 бит памяти в ячейках либо BRAM, либо же в самих LE / ALM в ПЛИС.
Называются они от x0 до x31. Регистр x0 всегда равен 0. Но обычно у регистров RISC-V есть определенные, закрепленные за ними названия и я приведу таблицу. Как утверждают сами разработчики спецификации, делайте с этими регистрами чего хотите, нам всё равно. Но так тоже не хорошо и потому некоторым регистрам всё-таки названия, но дали.
РЕГИСТР | ИМЯ | ЗНАЧЕНИЕ
========+========+===================================
x0 | zero | Всегда 0
x1 | ra | Адрес возврата
x2 | sp | Указатель стека
x3 | gp | Глобальный указатель
x4 | tp | Указатель потока
x5-x7 | t0-t2 | Временные
x8 | s0/fp | Сохраненные данные / Фрейм
x9 | s1 | Сохраненные данные
x10-x11 | a0-a1 | Аргументы / Данные возврата
x12-x17 | a2-a7 | Аргументы
x18-x27 | s2-s11 | Сохраненные данные
x28-x31 | t3-t6 | Временные
Все регистры, кроме 0, распределены условно, но имеют определенное значение.

§ Схема инструкции

В отличии от CISC инструкции, все инструкции в RISC-V крайне просты по своей структуре (ортогональны) и помещаются в базовый формат.
+==============+=========+=========+=========+=============+========+=====+
| 31 25 | 24 20 | 19 15 | 14 12 | 11 7 | 6 0 | ТИП |
+==============+=========+=========+=========+=============+========+=====+
| funct7 | rs2 | rs1 | funct3 | rd | opcode | R |
| imm[11:0] | rs1 | funct3 | rd | opcode | I |
| imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode | S |
| imm[12,10:5] | rs2 | rs1 | funct3 | imm[4:1,11] | opcode | SB |
| imm[31:12] | rd | opcode | U |
| imm[20,10:1,11,19:12] | rd | opcode | UJ |
+==============+=========+=========+=========+=============+========+=====+
Смотря на некоторые значения в imm – вычисления непосредственного значения, приходит удивление со словами "а где это просто?", поскольку данные собираются буквально "по битам" со всех сторон. Да, есть некоторые сложные вещи тут, но только для человеческого восприятия, но вовсе не для машинного зрения. Я, конечно, сам не понимаю, зачем сделали так сложно для UJ-типа, но ладно.
Приведу пояснения к обозначениям.
Несмотря ни на что, инструкции собираются весьма просто. Опкод размером 7 бит (от 00 до 7F), а также funct3 и funct7 для его дополнения потенциально может кодировать огромное количество инструкции.
На таблице можно отметить что существуют несколько типов инструкции. Все они 32-х битные, однако каждый тип обладает собственным форматированием полей.
Краткое описание типов:

§ Перемешанные Immediate

В некоторых типах, таких как SB или UJ биты очень сильно перемешаны. Если, например, понятно что imm[11:0] для I-типа будет брать из диапазона инструкции [31:20] и записываться в диапазон imm[11:0], что в Си-коде достаточно сделать так I >> 20, и для U-типа так I & 0xFFFFF000, то SB-тип (условные переходы) уже сложнее.
# SB-тип Условные переходы
31..25 => imm[12,10:5]
11..7 => imm[4:1,11]
Если представить в виде таблицы маппинга битов, то получится следующее:
31 | 30 29 28 27 26 25 | 11 10 9 8 | 7 | ИНСТРУКЦИЯ
12 | 10 9 8 7 6 5 | 4 3 2 1 | 11 | КОНСТАНТА
Сверху представлены исходные биты, ниже – биты, куда будут они поставлены в итоговое значение константы. Здесь видна интересная особенность – нет 0-го бита, и это объясняется тем что для совершения перехода всё выравнивается на 2 байта минимально. То есть, нет смысла хранить младший бит для этого.
Далее надо все компоненты собрать воедино.
# S-тип Указатели в память
Здесь немного попроще.
31..25 => imm[11:5]
11..7 => imm[4:0]
Достаточно лишь два действия сделать: (I >> 25) << 5 и (I >> 7) & 0x1F, беззнаковое смещение сначала на 25 бит, а потом установка на позицию 5, и далее сместить на 7, ограничить AND 0x1F и... всё, то есть, двигать больше никуда не надо, так как уже этот диапазон на позиции 0 установлен.
# UJ-тип Безусловный переход
Используется в инструкции JAL (Jump & Link) и почти такой же запутанный, как и SB-тип.
31 | 30 29 28 27 26 25 24 23 22 21 | 20 | 19 18 17 16 15 14 13 12 | ИНСТРУКЦИЯ
20 | 10 9 8 7 6 5 4 3 2 1 | 11 | 19 18 17 16 15 14 13 12 | КОНСТАНТА

§ Compressed инструкции

Есть еще интересная особенность процессора: он может работать в двух режимах – как обычном, когда инструкции размером 32 бита, так и в C-режиме, с 16-битными инструкциями.
Для обычных инструкции опкод всегда заканчивается на 11, то есть, первые два младших бита равны единицам. Если инструкция представляет из себя 16-битную "сжатую" инструкцию, то у нее как минимум в двух младших битах не 11, а например, 01 или другие значения. Когда процессор исполняет очередную 16-битную инструкцию, то он прибавляет к PC+2, а не PC+4, как обычно.
Конечно, для считывания из памяти это не очень удобно, потому я пока что буду реализовывать именно стандартный вариант.