Конфигурация FPGA в качестве RISC-V процессора

7 мая 2018

В свете нашумевших атак на CPU (из последних атак — Meltdown и Spectre) наблюдается рост интереса к открытым процессорам. Тот факт, что спецификация и конкретные реализации таких процессоров полностью открыты, существенно упрощает их анализ с точки зрения безопасности. Еще одно преимущество открытых процессоров заключается в том, что любой производитель может просто взять и начать выпускать такие процессоры, и никто его за это не попытается засудить, как в случае с x86/x64 или ARM. Сегодня наиболее хайповыми открытыми процессорами, по всей видимости, являются процессоры RISC-V (читается «риск файф»).

Коротко о RISC-V

RISC-V — это открытая спецификация архитектуры набора команд (ISA, Instruction Set Architecture), основанного на принципах сокращенного набора инструкций (RISC, Reduced Instruction Set Computer). Другими словами, RISC-V представляет собой описание ассемблерных инструкций, способа их кодирования, и семантику работы. Придерживание принципов RISC означает, что RISC-V не вводит лишних инструкций без сильной надобности. Например, в нем нет специальных «строковых» операций, таких, как инструкции movs, stos и cmps в ассемблере x86/x64. За счет этого существенно упрощается архитектура процессора. На самом деле, подобные CISC-инструкции являются пережитком прошлого, когда код на ассемблере писался вручную. Современное железо выполняет их даже медленнее, чем аналогичный цикл, написанный в RISC-стиле.

Инструкции RISC-V имеют переменную длину. При этом длина инструкции определяется достаточно просто, подобно тому, как определяется длина символа в кодировке UTF-8. Набор команд является расширяемым. В любой реализации обязательно присутствует базовый набор инструкций, который может быть 32-х, 64-х или 128-и битным. Базовые наборы команд называются соответственно RV32I, RV64I и RV128I. Помимо базовых команд конкретная реализация может предлагать команды, относящиеся к тем или иным расширениям. Например, расширение M содержит в себе команды целочисленного умножения и деления, расширение A — атомарные операции, а расширения F и D — операции над числами с плавающей точкой одинарной и двойной точности соответственно (то есть, float и double).

Fun fact! 128-и битные инструкции были добавлены в RISC-V на будущее. По расчетам создателей данного ISA, учитывая динамику роста современных суперкомпьютеров, а также возможный приход повсеместного NVM (Non-Volatile Memory, то есть, когда диски о оперативная память станут одним устройством), 64-х битных адресов может перестать хватать уже к 2030-му году.

Если, скажем, процессор реализует базовые 32-х битные инструкции, а также поддерживает целочисленное умножение и деление, говорят, что он является RV32IM процессором. То есть, реализованные расширения дописываются в конце следом за обозначением базового набора инструкций. Обозначения RV32IMAFD и RV64IMAFD сокращают до RV32G и RV64G, от general-purpose. То есть, такие процессоры считаются процессорами общего назначения. Помимо упомянутых расширений также есть, или разрабатываются, другие. Например, есть расширение с компактными инструкциями, кодируемыми 16-ю битами (C), инструкциями для манипулирования битами (B), для работы с транзакционной памятью (T), расширение с векторными инструкциями (V), с Packed-SIMD инструкциями (P), и другие.

Звучит занятно, но можно ли это где-то попробовать? Оказывается, что можно. Существует несколько реализаций RISC-V для FPGA, с одной из которых мы познакомимся далее. Это, пожалуй, наиболее быстрый и дешевый способ попробовать RISC-V. Кроме того, существуют готовые отладочные платы с RISC-V микроконтроллерами и процессорами, например HiFive1 и HiFive Unleashed. На последнем можно запустить полноценный Linux. Из дистрибутивов, официально поддерживающих RISC-V, можно привести в пример Debian.

Проект Icicle

Icicle — это реализация ядра RV32I для FPGA семейства Lattice ICE40. Код написан на SystemVerilog и компилируется при помощи IceStorm, открытого стека разработки под данные FPGA. Ядру требуется около 2500 логических ячеек, поэтому на отладочной плате iCEstick его запустить не получится. Однако его можно запустить на BlackIce II, так как используемый в нем ICE40HX4K имеет достаточное количество (3520) логических ячеек.

Компиляция проекта и конфигурация FPGA осуществляются очень просто:

# установка зависимостей
yaourt -S riscv64-unknown-elf-gcc

# компиляция проекта
git clone https://github.com/grahamedgecombe/icicle.git
cd icicle
BOARD=blackice-ii make

# конфигурация FPGA
stty -F /dev/ttyACM0 raw
cat top.bin > /dev/ttyACM0

Соединяем второй USB-порт отладочной платы с компьютером и говорим:

screen /dev/ttyUSB0

Должны увидеть, что программа постоянно выводит:

Hello world!

Давайте отредактируем progmem.c таким образом:

#include <stdint.h>

#define LEDS        *((volatile uint32_t *) 0x00010000)

static inline uint32_t rdcycle(void) {
    uint32_t cycle;
    asm volatile ("rdcycle %0" : "=r"(cycle));
    return cycle;
}

int main() {
    uint8_t leds = 0xAA;
    LEDS = (uint32_t)leds;
 
    for (;;) {
        leds = ~leds;
        LEDS = (uint32_t)leds;

        uint32_t start = rdcycle();
        while ((rdcycle() - start) <= FREQ);
    }
}

… и переконфигурим FPGA. Если все было сделано правильно, теперь плата будет мигать светодиодами. Гореть будет либо LED1 и LED3, либо LED2 и LED4, переключение горящих светодиодов будет происходить раз в секунду.

А можно ли посмотреть на получившийся ассемблерный код? Почему бы и нет:

riscv64-unknown-elf-objdump -d progmem

У меня код процедуры main получился таким:

// a5 := 0x10 << 12, это адрес LEDS
130:  000107b7            lui a5,0x10
// a4 := 0xAA
134:  0aa00713            li  a4,170
// a3 := 0x16e3 << 12
138:  016e36b7            lui a3,0x16e3
// записать 0xAA (регистр a4) в LEDS
13c:  00e7a023            sw  a4,0(a5)
// a2 := 0xAA
140:  0aa00613            li  a2,170
// a1 := 0x10 << 12, снова адрес LEDS
144:  000105b7            lui a1,0x10
// a3 := (0x16e3 << 12) + 1536 = 24000000
148:  60068693            addi  a3,a3,1536

// начало цикла for

14c:  fff64613            not a2,a2
150:  0ff67613            andi  a2,a2,255
154:  00c5a023            sw  a2,0(a1)
158:  c0002773            rdcycle a4

// цикл ожидания

15c:  c00027f3            rdcycle a5
160:  40e787b3            sub a5,a5,a4
164:  fef6fce3            bleu  a5,a3,15c
168:  fe5ff06f            j 14c

В теории, можно было бы посмотреть на исполнение этого кода в симуляторе Spike. Но, увы, на момент написания этих строк симулятор был сыроват, и не особо преуспевал с симуляцией этого конкретного примера. Хочется надеяться, что со временем это поправят.

Больше информации об ассемблере RISC-V вы найдете в документе The RISC-V Instruction Set Manual, Volume I: User-Level ISA [PDF], а также в шпаргалке Free & Open RISC-V Reference Card [PDF].

Заключение

Итак, сегодня мы узнали, что 2500 LUT достаточно для запуска хоть и простого, но все-таки процессора. Также мы познакомились с RISC-V и выяснили, что при сильном желании уже сегодня можно использовать микроконтроллеры на базе данного ISA вместо, скажем, STM32, и даже поднять десктоп на базе RISC-V. Правда, насколько это удобно и оправданно в современных реалиях — это другой вопрос.

Мы вряд ли увидим массовое использование RISC-V на десктопах и серверах, по крайней мере, в ближайшем будущем. Но стоит помнить, что процессоры нужны не только на десктопах и серверах. Так, Nvidia объявила о намерении использовать RISC-V в графических картах GeForce вместо используемого в настоящее время процессора Falcon. Кроме того, Western Digital собирается использовать процессоры RISC-V в своих будущих продуктах. Среди компаний, поддерживающих RISC-V Foundation, можно привести в пример AMD, IBM, Huawei, Google, Oracle, Lattice Semiconductor, NXP, и другие. Даже Intel, и тот инвестирует в RISC-V!

Лично у меня RISC-V вызывает исключительно одобрение и мне хочется надеяться, что в будущем мы увидим много продуктов на его базе!

Метки: , , .

Подпишись через RSS, Google+, Facebook, ВКонтакте или Twitter!

Понравился пост? Поделись с другими: