Проектирование процессоров RISC-V в среде Logisim и на языке Verilog в среде Quartus

Архитектура процессоров

Разбить на страницы
Показывать лекцию целиком

Процессор или центральный процессор - основное устройство любой электронно-вычислительной машины (ЭВМ) в широком понимании данного термина. Он осуществляет управление всеми элементами данной машины и выполняет основной набор вычислительных функций для конкретного решения. Упрощенный вариант процессора приведен на рисунке 7.1.

Блок управления осуществляет выборку инструкции из памяти ЭВМ, дает команды внутри процессора и управляет работой внешних устройств в том числе и памятью. Выбранная инструкция, если содержит данные в памяти, должна через дешифратор инструкций загрузить данные в соответствующие регистры процессора (условные регистр А и В). Дешифратор инструкций, если подразумевается какая-либо операция над данными, активирует соответствующий узел операционного блока. Результат инструкции может быть возвращен в память либо в один из регистров. Далее осуществляется выборка следующей инструкции или переход к другому участку памяти, в случае вызова перехода в результате исполнения инструкции.

(рис 7.1)

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

Первый вид классификации процессоров - система команд процессора (ISA - Instruction Set Architecture). ISA - набор инструкций, которые процессор может выполнять. Инструкции включает в себя как основные команды, так и формат данных, управление памятью и регистры. ISA является ключевым понятием в архитектуре микропроцессорных систем, так как определяет, каким образом программное обеспечение взаимодействует с аппаратным.

Выделяют три категории в данной классификации:

  • CISC (Complex Instruction Set Computer) сложный набор инструкций.

    Такие процессоры могут иметь инструкции произвольной длины. Процессоры этой архитектуры способны выполнять несколько операций за несколько тактов. В архитектуре CISC обеспечивается большое количество методов адресации. В тоже время требуется условно небольшое количество регистров необходимых для реализации программы.

    К данной категории процессоров относятся x86 совместимые процессоры.

  • RISC (Reduced Instruction Set Computer) упрощенный набор инструкций.

    Процессоры RISC имеют одинаковый формат команд. Все команды имеют одинаковую длину. Инструкции исполняются за один такт за исключением сложных арифметических инструкций. RISC процессоры имеют небольшое количество команд по сравнению с CISC. Для повышения быстродействия имеют большое количество неименованных регистров, объединяемых в регистровый файл.

    Для RISC процессоров признаками являются:

  • расширенный набор регистров;
  • симметричность использования определенных регистров;
  • упрощенный дешифратор команд за счет простоты организации инструкций;
  • простые механизмы адресации данных.
  • К данной категории процессоров относятся процессоры ARM, различные микроархитектуры микроконтроллеров, например, AVR, PIC, а также решения RISC-V.

  • VLIW (Very Long Instruction Word) - очень длинная машинная команда - архитектура процессоров, характеризующаяся возможностью объединения нескольких простых команд в так называемую связку. Входящие в нее команды должны быть независимы друг от друга и выполняться параллельно. Таким образом, из нескольких независимых машинных команд транслятор формирует одно очень длинное командное слово. Данная архитектура подразумевает, что длинная команда распараллеливается на несколько ядер, исполняющих каждое свою часть. При применении процессоров данной архитектуры большая часть работы выполняется компилятором языка программирования.

    К данной категории процессоров относятся процессоры Эльбрус и некоторые решения IA64.

  • Вторым видом классификации архитектур процессоров является способ доступа к памяти. В данной классификации выделяют две категории:

    1.Архитектура Фон Неймана:

  • данные и инструкции хранятся в одной памяти;
  • инструкции исполняются в строгом порядке, переходы должны быть регламентированы;
  • обмен данными между памятью и периферийными устройствами осуществляется под управлением процессора и дополнительными арбитрами.
  • Данная архитектура имеет следующие преимущества:

  • простота организации - использование одного интерфейса для доступа к памяти;
  • универсальность работы с памятью.
  • Недостатки:

  • переполнение буфера - ввод данных в ходе исполнения программы может повредить данные или код, размещенные за пределами отведенного сегмента памяти;
  • низкая производительность. Данный недостаток вызван тем, что необходимо поочередно выбирать инструкции, а затем выбирать данные из памяти.
  • На рисунке 7.2 приведена упрощенная схема организации данной архитектуры.

    (рис 7.2)

    Архитектура фон Неймана применяется x86 совместимыми процессорами и их последующими поколениями.

    Гарвардская архитектура имеет следующие свойства:

  • данные и инструкции хранятся раздельно в разных модулях памяти;
  • высокое быстродействие за счет параллельного доступа к инструкциям и данным.
  • Свойства данной архитектуры исключают недостатки архитектуры фон Неймана. Недостатком данной архитектуры является увеличенное количество линий связи с памятью и схемотехническая сложность интерфейса модулей памяти.

    Процессоры с RISC системой команд строят по гарвардской архитектуре, для обеспечения максимальной производительности. На рисунке 7.3 приведена упрощенная схема организации гарвардской архитектуры.

    (рис 7.3)

    Другими специфическими особенностями классификации, скорее микроархитектур, можно отнести следующие:

  • Наличие регистра флагов. Например, в RISC-V отсутствует данный регистр. Применение триггеров различных признаков состояния процессора, таких как переполнение АЛУ, признак (флаг) 0. Данные триггеры объединяются в регистр флагов и применяются при построении схемы управления ветвлениями в процессорах.
  • Наличие определенного регистра аккумулятора для работы с АЛУ (x86, микроконтроллеры PIC имеют данный регистр).
  • По системам команд процессоры классифицируются на:

  • безадресные (стековые);
  • одноадресные;
  • двухадресные, например, x86 и ряд RISC процессоров и микроконтроллеров;
  • трехадресные - архитектура RISC-V.
  • Еще одним видом классификации процессоров является степень параллелизма обработки данных:

    1. SISD (Single Instruction, Single Data) - это архитектура, в которой один процессор выполняет одну инструкцию над одним набором данных в один момент времени. Это традиционная модель вычислений, используемая в большинстве обычных процессоров.

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

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

    Преимущества: простота реализации и программирования.

    Недостатки: низкая эффективность в сравнении с параллельными архитектурами при выполнении сложных задач.

    2. SIMD (Single Instruction, Multiple Data) - это архитектура, в которой одна и та же инструкция применяется одновременно ко многим данным. Это позволяет значительно ускорить выполнение операций над большими массивами данных.

    В один момент времени одна инструкция применяется ко всем элементам векторов или массивов. Например, сложение двух векторов чисел.

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

    Преимущества: высокая производительность при обработке больших объемов однотипных данных, эффективное использование ресурсов.

    Недостатки: ограниченная гибкость, поскольку не все задачи можно разбить на операции с одинаковыми данными.

    3. MIMD (Multiple Instruction, Multiple Data) - это архитектура, в которой несколько процессоров могут выполнять разные инструкции над разными наборами данных одновременно. Это наиболее универсальный тип архитектуры.

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

    Применение: используется в многоядерных процессорах, кластерах, распределенных системах и для высокопроизводительных вычислений.

    Преимущества: высокая степень гибкости и универсальности, возможность работы с разнообразными задачами.

    Недостатки: более сложная реализация и необходимость в эффективной синхронизации и управлении потоками.

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

    Процессоры x86

    Применяемые в большинстве решений вычислительных машин, процессоры реализованы на так называемой Intel совместимой архитектуре. Данные процессорные решения имеют разную микроархитектуру и совместимы между собой на уровне общих инструкций языка ассемблера. В то же время могут иметь собственные специфические инструкции, описываемые в документации разработчика. На рисунке 7.4 приведена общедоступная структурная схема процессора Intel Pentium [3], совместимость с которым у других производителей была наиболее близкой. На структурной схеме можно выделить следующие блоки:

  • Bus Interface Unit (блок интерфейса шины) обеспечивает взаимодействие с внешней шиной и кэшом L2.
  • Instruction Fetch Unit (блок извлечения инструкций) отвечает за извлечение инструкций из памяти.
  • Instruction Decoder (дешифратор инструкций) декодирует извлеченные инструкции. Данный блок состоит из двух простых и одного сложного дешифраторов, отвечающих за преобразование инструкции в микрокоманды управления.
  • Branch Target Buffer (буфер адреса перехода) ускоряет выполнение ветвящихся инструкций, обеспечивая быстрый доступ к целевым адресам.
  • Microinstruction Sequencer (планировщик последовательности микрокоманд) обрабатывает сложные инструкции, которые не могут выполнить составные дешифраторы Instruction Decoder, формируя при этом необходимое количество микрокоманд. При работе с данным блоком активируется Reservation Station.
  • Register Alias Table (таблица алиасов регистров) управляет именами регистров, чтобы избежать конфликтов при одновременном обращении к двух соседних команд к одному регистру. В данном блоке именные ссылки на регистры преобразуются в адреса 40 физических регистров.
  • Reservation Station (станция ожидания) хранит инструкции, ожидающие выполнения и освобождения для них ресурсов.
  • Memory Reorder Buffer (буфер упорядочения памяти) обеспечивает упорядоченное выполнение инструкций в отношении памяти. Пересылает данные в память тогда, когда микроинструкция осуществлявшая запись в память удалена.
  • Data Cache Unit (блок данных кэша) хранит временные данные для быстрого доступа.
  • Memory Interface Unit (блок интерфейса памяти) управляет взаимодействием с основной памятью.
  • Address Generation Unit (блок генерации адресов) генерирует адреса для доступа к памяти.
  • Integer Execution Unit (управляющий блок целых чисел) выполняет арифметические и логические операции над целыми числами.
  • Floating-point Execution Unit (управляющий блок с плавающей запятой) отвечает за выполнение операций над числами с плавающей запятой.
  • Reorder Buffer and Retirement Register File (буфер восстановления последовательности) управляет завершением выполнения инструкций, гарантируя, что они будут выполнены в правильном порядке. Хранит часть микрокоманд, ожидающих исполнения.
  • (рис 7.4)

    С точки зрения программиста в Intel архитектуре выделяются общие поименованные регистры, которые применяются при разработке программ на языке ассемблера. Данные регистры называют регистрами общего назначения (РОН). К некоторым из них можно обращаться секциями: байтом, словом двойным словом (для 32-разрядной x86 архитектуры). На рисунке 7.5 приведены поименованные 32-х разрядные регистры Intel x86 и форматы взаимодействия с частями данных регистров. При работе с 64-х разрядной архитектурой процессора префикс заменяется на букву R. Регистры общего назначения являются основой для разработки программ и позволяют решать основные задачи по обработке данных, таких, как хранение операндов для арифметических и логических операций, операндов вычисления адресов ячеек памяти, указателей на переменные в памяти.

    Несмотря на общий характер данных регистров они всё же используются для определенных целей:

  • EAX - регистр аккумулятор. Применяется при работе с арифметическими и логическими операциями, хранит результат операции. Наличие регистра аккумулятора относит процессор к одному из видов процессоров - процессоров с аккумуляторами
  • EBX - базовый регистр. Применяется для хранения базового адреса данных в памяти, адресуемой регистром DS;
  • ECX - регистр счетчик. Применяется для организации циклов и хранения элементов строковых операций;
  • EDX - регистр данных. Хранит промежуточные данные и применяется при работе с арифметико-логическим устройством и операциями ввода вывода;
  • ESP - регистр указатель стека. Указывает вершину стека, адресуемого регистром SS.
  • EBP - регистр указатель. Применяется для указания базового стекового адреса
  • ESI - регистр указатель источника индекса. Указывает на данные, находящиеся в сегменте оперативной памяти, адресуемом регистром DS.
  • EDI - регистр указатель индекса получателя данных. Данный регистр указывает на сегмент данных, адресуемый регистром ES.
  • В 32-х разрядных процессорах архитектуры x86 для управления памятью применяются следующие сегментные регистры:

  • CS - регистр сегмента кода. Содержит адрес сегмента, содержащий коды инструкций программы, загружаемой в процессор;
  • DS - регистр сегмента данных. Содержит обрабатываемые программой данные;
  • SS - регистр сегмента стека. Содержит адрес стека.
  • (рис 7.5)

    Стек (стековая память) - структура данных в оперативной памяти, применяемая для временного хранения содержимого регистров процессора, например, для обработки подпрограммы прерывания. Данные упорядочиваются в стеке последовательно. Существуют следующие виды стеков:

  • LIFO - Last input, First output. Данные, поступившие в данный сегмент памяти последними, будут извлечены и удалены первыми;
  • FIFO - First input, First output. Данные, поступившие в данный сегмент памяти первыми, будут извлечены и удалены первыми.
  • Регистр указатель стека ESP фиксирует в себе адрес последних записанных данных.

    Отдельно стоит отметить наличие регистра флагов, определяющего состояние процессора, применяемого для управления переходами.

    Как видно из описания, x86 процессоры представляют собой сложную организацию с закрытой микроархитектурой. Механизмы управления и параллелизма представляют собой множество цепей управления, соответственно система управления является громоздкой и требует инженерного творчества для обеспечения высокой производительности.

    RISC процессоры

    Рассмотрим микроархитектуру RISC на примере восьмиразрядного микроконтроллера Atmega328P (устанавливается на все Arduino UNO отладочные платы). На рисунке 7.6 приведена упрощенная схема данного микроконтроллера со следующими основными блоками:

  • Flas Program memory - перепрограммируемая память инструкций;
  • Instruction Register (IR) - регистр текущей инструкции;
  • Instruction Decoder - дешифратор команд;
  • Program Counter (PC) - счетчик адреса инструкций. Указывает на адрес памяти инструкций;
  • Status and Control - регистр состояний;
  • 32x8 General Purpose Registers - регистры общего назначения, объединенные в регистровый файл;
  • Data SRAM - оперативная статическая память данных;
  • EEPROM - постоянная память данных;
  • I/O Lines - линии соединения процессорного ядра с периферийными модулями.
  • Расположенные справа модули представляют специфические для каждого микроконтроллера встроенные периферийные устройства, обеспечивающие сбор, прием/передачу данных и сигналов для исполнительных механизмов, т.е порты ввода-вывода данных (I/O Module).

    Как видно из схемы память инструкций и данных разделены, соответственно архитектура организации памяти - гарвардская.

    (рис 7.6)

    RISC процессоры ARM

    ARM процессоры - это коммерческие лицензируемые ядра процессоров, построенные с использованием RISC команд, но с технологическими особенностями организации этого ядра. Компания ARM, разработчик различных инженерных решений, продает лицензии и документацию на ядро процессора конечным производителям микропроцессоров. Разработчики, используя свои периферийные модули и другие вычислительные блоки, добавляя их к ядру ARM, получают готовое решение. На рисунке 7.7 представлена упрощенная структурная схема процессора ARM9, реализованная на описаниях из разных информационных источников [4,5]. На структурной схеме можно выделить следующие блоки:

  • Регистровый файл содержит в себе регистры общего назначения - стандартное решение для RISC архитектур, с особенностями применения в зависимости от режима работы процессора;
  • АЛУ - арифметико-логическое устройство;
  • Устройство быстрого сдвига находится вне АЛУ, позволяет сдвигать входные данные на заданное число бит до операции в АЛУ;
  • Блок умножителя, работающий по алгоритму Бута
  • Кэш память инструкций и кэш память данных являются памятью быстрого доступа для ускорения выполнения операций. Быстродействие достигается за счет предварительной загрузки из основной памяти, тем самым снижая время обращения к ней.
  • Блок выборки и инструкций и дешифратор инструкций - часть системы управления процессора. Они выбирают команды из памяти и осуществляют коммутацию блоков процессора;
  • Блок мультиплексоров участвует в распараллеливании доступа к операционным блокам процессора;
  • Интерфейс данных - часть общей системы управления, участвующая в распараллеливании процессов, а также включающая в себя регистр флагов для управления переходами;
  • Устройство управления памятью (MMU) - блок трансляции "виртуальны адресов " программ в физические адреса памяти.
  • (рис 7.7)

    Более подробная информация об организации работы процессоров, существующих архитектурах, методах работы с памятью приводится в литературе [1,2] и дополнительной литературе [2,3].

    Для понимания различий в архитектуре рассмотренных процессоров реализуем решение задачи по вычислению чисел Фибоначчи на языках ассемблера (ISA). Предварительно приведем код для вычисления чисел Фибоначчи на языке Си (листинге 7.1).

    include  <stdio.h >
    int main() {
        int n= 10; // количество чисел Фибоначчи
        unsigned long long fib[10]; // массив для хранения чисел Фибоначчи
        // инициализация первых двух чисел Фибоначчи
        fib[0] = 0;
        fib[1] = 1;
        // вычисление чисел Фибоначчи
        for (int i = 2; i  < n; i++) {
            fib[i] = fib[i - 1] + fib[i - 2];
        }
        // вывод чисел Фибоначчи
        printf("Первые %d чисел Фибоначчи:\n", n);
        for (int i = 0; i  < n; i++) {
            printf("%llu ", fib[i]);
        }
        printf("\n");
        return 0;
    }
    

    Для описанного выше примера приведем код на языке ассемблера процессора x86, реализованного в компиляторе NASM (листинге 7.2)

    section .data
        fmt db "Fibonacci(%d) = %d", 10, 0  ; Формат строки для вывода
        n db 5; Измените здесь для получения n-го числа Фибоначчи
    section .bss
        result resd 1; Результат вычисления
    section .text
        extern printf; Импорт функции printf
        global main; Точка входа в программу
    main:
        ; Загружаем значение n
        movzx ecx, byte [n]; Загружаем n в ecx
        mov eax, 0; fib(0)
        mov ebx, 1; fib(1)
        cmp ecx, 0; Если n = 0
        je .done; Переход к завершению
        cmp ecx, 1; Если n = 1
        je .next; Переход к следующему
        ; Итеративная часть
    .loop:
        ;Вычисляем n-е число Фибоначчи
        mov edx, eax                        ; Сохраняем предыдущую Fibonacci (fib(n-2))
        add eax, ebx; fib(n) = fib(n-1) + fib(n-2)
        mov ebx, edx; Обновляем fib(n-1) на fib(n-2)
        dec ecx; Уменьшаем счетчик
        jnz .loop; Повторяем до n = 0
    .next:
        ; Задаем результат для вывода
        mov [result], eax ; Сохраняем результат
    .done:
        ; Вывод результата
        push eax; Параметр: значение Fibonacci
        push dword [n]; Параметр: n
        push fmt; Строка для printf
        call printf; Вызов функции printf
        add esp, 12; Очистка стека
        ; Завершение программы
        mov eax, 1; sys_exit
        xor ebx, ebx; Устанавливаем код возврата 0
        int 0x80; Вызов ядра
    

    Приведем решение данной задачи на языке ассемблера для микроконтроллера Atmega328P. Код приведен в листинге 7.3.

    .section .text
    .global fib
    fib:
       ; Сохранение регистров
       push r16
       push r17
       mov r16, r24
       ; Инициализация значений
       ldi r18, 0; Заносим первое значение
       ldi r19, 1; Заносим второе значение
       ; Проверяем (0 или 1)
       cp r16, r18
       breq .fib_done
       cp r16, r19
       breq .fib_done
       ; Цикл вычислений
       ldi r20, 2
       ldi r21, 0
    .fib_loop:
       cp r20, r16
       breq .fib_done
       ; Вычисления чисел Фибоначчи
       add r21, r18
       mov r18, r19
       mov r19, r21
       ; Увеличение счетчика
       inc r20
       ; Проверка окончания цикла .fib_loop
       brne .fib_loop
       ; Возврат по окончании вычислений
       call .fib_done
    .fib_done:
       mov r24, r19; Сохранение итога
       ; Завершение программы
       pop r17
       pop r16
       ret
    

    Далее приводится код на ассемблере ARM процессора. Код разработан для микрокомпьютера Raspberry Pi 2/3 (листинг 7.4).

    .section .data
        fib_num: .asciz "Fibonacci: %d\n"  // Форматированная строка для вывода
    .section .bss
        num1: .skip 4  // Первый элемент массива
        num2: .skip 4  // Второй элемент массива
        result: .skip 4  // Результат
    .section .text
    .global _start
    .extern printf  // Объявление функции printf
    _start:
        ldr r0, =0          // num1 = 0
        str r0, num1
        ldr r0, =1          // num2 = 1
        str r0, num2
        mov r1, #0          // Счетчик
        mov r2, #10         // Количество чисел Фибоначчи для вычисления
    fib_loop:
        ldr r0, num1
        ldr r3, num2
        add r0, r0, r3     // r0 = num1 + num2
        str r0, result
        ldr r0, =fib_num   // Указатель на строку
        ldr r1, result     // Результат для передачи в printf
        bl printf
        ldr r0, num2       // Обновление num1
        str r0, num1
        str r0, num2
        add r1, r1, #1
        cmp r1, r2
        blt fib_loop       // Если r1  < r2, продолжаем цикл
        mov r0, #0         // Код возврата 0
        bx lr              // Завершение программы
    

    В приложении А приводится краткая система команд данных процессоров.

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

    Архитектура RISC-V представляет собой расширяемую открытую свободную систему команд, а также свободно распространяемую микроархитектуру ядра.

    Свобода распространения и внесение изменений привлекли пристальное внимание со стороны как энтузиастов в академической среде, так и крупных разработчиков, производителей электроники. На сегодняшний день сформировался пул организаций, поддерживающих и развивающих данную архитектуру. В Российской Федерации также существует команда разработчиков и производителей, объединённая в "Альянс RISC-V ".

    На рисунке 7.8 представлена структурная схема микроархитектуры ядра процессора RISC-V для стандартных целочисленных операций.

    На рисунке структурной схемы выделены следующие блоки:

  • PC (program counter) - счетчик адреса памяти инструкции (Instruction memory, IMEM), указывающий на выбираемую из памяти инструкцию. В ряде источников данный счетчик обычно называется указателем команд (IP) или регистром адреса команд (IAR).

    После выполнения инструкции значение PC обновляется так, чтобы указывать на следующую инструкцию в последовательности. Вне зависимости от того какая инструкция выполняется, процессор последовательно считывает инструкции, увеличивая счетчик на размер инструкций (4 байта для стандартных 32-х битных инструкций). Данная операция выполняется блоком сумматора текущего адреса и константы 4.

    Если в программе есть ветвления, такие как условные операции (например, beq - branch if equal), значение PC будет изменяться не на постоянной основе, а в зависимости от результата операции. На рисунке данный блок обозначен как Branch.

  • Instruction memory (IMEM) - память инструкций, хранящая в себе машинный код исполняемой программы. Разрядность шины данных - 32 бита.
  • Data memory (DMEM) - память данных. В архитектуре RISC-V играет ключевую роль хранения и управления данными, которые процессор обрабатывает во время выполнения программ. Data Memory используется для хранения переменных, массивов, структур и других видов данных. Процессор может выполнять операции чтения (load) и записи (store) данных. Эти операции позволяют загружать данные из памяти в регистры процессора и сохранять результат вычислений обратно в память.
  • Registers - регистровый файл. RISC-V содержит 32 регистра общего назначения. Все операции с ними производятся одинаково, за исключением нулевого (zero) регистра. Попытки записи в данный регистр игнорируются, а чтение всегда возвращает 0. Данная опция полезна, когда не требуется сохранять результат некоторых операций или есть необходимость иметь 0 в качестве операнда.
  • Несмотря на то, что регистры общего назначения могут использоваться без каких-либо ограничений, некоторые из них, согласно разработанному application binary interface RISC-V, имеют второе имя или алиас, по которому на них могут ссылаться в программном коде.

    В таблице 7.1 приводится соответствие номера регистра, его алиас (псевдоним) и назначение.

    Номер регистра Псевдоним Назначение
    x0 Zero Константа нуля
    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 Временные переменные

    Сохраняемые переменные (регистры) должны содержать неизменное значение до начала и по окончании вызываемой функции. Временные регистры могут изменять свои значения.

  • ALU (arithmetic logic unit) - арифметико-логическое устройство. АЛУ выполняет арифметические, логические операции, операции сравнения и сдвига. В АЛУ базового набора инструкций RV32I выполняется всего 10 операций (для выполнения данных инструкций в предыдущем разделе был спроектирован блок АЛУ в рамках практической работы).
  • Control (control unit) - блок управления. В данном случае в него включен и дешифратор инструкций (в разных источниках может отображаться отдельно сразу за IMEM). Блок управления декодирует инструкцию, поступающую из памяти инструкции, и коммутирует соответствующие блоки процессора посредством формирования управляющих сигналов записи/чтения (управления мультиплексорами). Помимо этого, блок анализирует результаты операций и управляет цепями ветвлений. Также этот блок координирует работу выполнения инструкции и является составной частью конвейера.
  • Control unit является цифровым автоматом и может быть реализован с применением микропрограммного автомата либо на комбинационной логике ( "жесткой логике ").

    Необходимо обратить внимание, что в процессоре RISC-V манипуляции с данными производятся только из регистрового файла. Для манипуляции с данными их предварительно необходимо загрузить из памяти в регистры регистрового файла, выполнить операцию, а затем если это предусмотрено кодом инструкции разместить обратно в памяти.

    В данной архитектуре обращение к памяти возможно только с помощью двух специальных команд: load (lw) и store (sw). В английской транскрипции данный метод организации работы с памятью называют Load/Store architecture.

    Команда load (загрузка) обеспечивает считывание данных из основной памяти и занесение их в регистр процессора (в команде обычно указывается адрес ячейки памяти и номер регистра)

    Пересылка информации в противоположном направлении производится командой store (сохранение).

    (рис 7.8)

    Набор инструкций RISC-V

    Подход создания базового набора инструкций с выбором необходимых расширений в RISC-V позволяет беспрепятственно развивать архитектуру без затруднений для обратной совместимости. Также обеспечивается возможность производить на единой архитектуре чипы с одинаковой эффективностью, предназначенные для различных сегментов, например, встраиваемых системы где делается упор на энергоэффективность и цену, либо пользовательские решения, где важна производительность.

    Как и в остальных архитектурах, операции в RISC-V кодируются в особый формат, однако (за исключением особого расширения сжатых инструкций), инструкции имеют всегда одинаковую длину кодировки (32 бит) вне зависимости от разрядности процессора. Такой подход существенно упрощает декодирование инструкций, работу с памятью и повторное использование частей расширения непосредственно в реализациях других процессоров.

    Ниже перечислены базовые инструкции работы процессора RISC-V RVI32, необходимые для собственной реализации ядра процессора:

    1. Арифметические инструкции

    ADD: сложение.

    Пример: ADD x3, x1, x2 - сложение значений в регистрах x1 и x2, результат записывается в x3.

    SUB: вычитание.

    Пример: SUB x3, x1, x2 - вычитание значения x2 из x1, результат записывается в x3.

    2. Логические инструкции

    AND: логическое И.

    Пример: AND x3, x1, x2 - побитовое И значений в регистрах x1 и x2, результат записывается в x3.

    OR: логическое ИЛИ.

    Пример: OR x3, x1, x2 - побитовое ИЛИ значений в регистрах x1 и x2, результат записывается в x3.

    XOR: исключающее ИЛИ.

    Пример: XOR x3, x1, x2 - побитовое исключающее ИЛИ, результат записывается в x3.

    3. Инструкции для работы с памятью

    LW: загрузка слова из памяти.

    Пример: LW x3, 0(x1) - загрузка 32-битного слова из адреса, основанного на значении в x1, в регистр x3.

    SW: запись слова в память.

    Пример: SW x3, 0(x1) - запись 32-битного слова из x3 по адресу, основанному на значении в x1.

    4. Управление потоком выполнения

    JAL: переход с сохранением адреса возврата.

    Пример: JAL x1, label - переход к метке label, адрес возврата сохраняется в x1.

    LUI: загрузка верхней части значения.

    Пример: LUI x1, 0x12345 - загрузка значения 0x12345000 в регистр x1.

    BEQ: условный переход (если равно)

    Пример: BEQ x1, x2, label - переход к метке label, если значения в x1 и x2 равны.

    JALR: переход по адресу в регистре с сохранением адреса возврата.

    Пример: JALR x1, 0(x5) # Переход к адресу, указанному в x5. Возврат по выполнении по адресу, сохраненному в x1.

    AUIPC: добавление адреса текущей инструкции.

    Пример: AUIPC x1, 0x10 - добавляет к адресу текущей инструкции значение 0x10, результат помещается в x1.

    BNE: условный переход (если не равно).

    Пример: BNE x1, x2, label - переход к метке label, если значения в x1 и x2 не равны.

    5. Инструкции для работы с непосредственными данными

    ADDI: сложение с немедленным значением.

    Пример: ADDI x3, x1, 10 - сложение значения в x1 с 10, результат записывается в x3.

    SLTI: сравнение меньше с немедленным значением.

    Пример: SLTI x3, x1, 10 - x3 будет равно 1, если x1 < 10, иначе 0.

    6. Инструкции для операций с битами

    SLL: сдвиг влево.

    Пример: SLL x3, x1, 2 - сдвиг значения в x1 на 2 бита влево, результат записывается в x3.

    SRL: логический сдвиг вправо.

    Пример: SRL x3, x1, 2 - логический сдвиг значения в x1 на 2 бита вправо.

    Все инструкции в RISC-V разделены на 6 типов:

  • R - операции АЛУ типа регистр - регистр;
  • I - операции АЛУ с непосредственным значением в команде;
  • S - операции загрузки/сохранения;
  • B - условная передача управления;
  • U - операции с расширенным непосредственным значением;
  • J - безусловная передача управления.
  • Форматы типов инструкций представлены на рисунке 7.9 и определены в "The RISC-V Instruction Set Manual " [6]. В приложении Б приведен перечень инструкции для базовой версии RV32I

    (рис 7.9)

    Рассмотрим пример кодировки инструкции add - инструкции суммирования содержимого двух регистров и записи результата в третий регистр. На рисунке 7.10 представлен пример кодировки соответствующей инструкции.

    (рис 7.10)

    Первая строчка отображает то, как будет записана типовая инструкция в данном расширении. Opcode - номер операции, который описан в документации на каждую инструкцию константным значением. В случае с инструкцией add это будет 0110011, rd - номер регистра назначения результата. Funct3 - указатель дешифратору операций, какую операцию реализовывать, на случай если opcode инструкций одинаковы, rs1 - номер первого операнда. rs2 - номер второго операнда. Оставшаяся часть заполняется нулями. При детальном рассмотрении типовой кодировки и кодировки конкретной инструкции можно заметить, что у нас отсутствует imm(произвольная константа), которая занимает 12 бит. Это связано с тем, что логика инструкции сделана так, что никаких констант не требуется. Освободившееся место использовано для второго операнда, положение которого не стандартизировано в типовой кодировке. Если бы у нас была константа, но не было нужды в номере регистра второго операнда, то операнд находился именно в поле с 31 по20-ый бит. Таким образом, при сложении r1 и r2 с записью результата в r3, кодировка будет иметь следующий вид: 0000000_00010_00001_000_00011_0110011.

    При изучении и в сравнении с другими системами ISA можно заметить, что процессоры RISC-V на уровне микроархитектуры в силу упрощения и повышения быстродействия не реализуют множество инструкций, характерных для других решений. Поэтому в компиляторы ассемблера заложены псевдо-инструкции, которые может применять программист, а они в свою очередь будут транслированы в инструкции, реализуемые процессором. В таблицу 7.2 сведены псевдо-инструкции, транслируемые в инструкции процессоров RISC-V.

    Псевдо-инструкция Инструкция RISC-V Описание
    nop addi zero,zero,0 нет операции
    mv rd, rs1 addi rd, rs, 0 Копирование между регистрами rs в rd
    not rd, rs1 xori rd, rs, -1 Запись в rd логической инверсии RS (все биты проходят операцию XOR с 1)
    neg rd, rs1 sub rd, x0, rs Запись в rd числа в обратном коде rs
    seqz rd, rs1 sltiu rd, rs, 1 Устанавливает результат в 1 если rs <1
    snez rd, rs1 sltu rd, x0, rs Устанавливает результат в 1 если rs не равен 0
    sltz rd, rs1 slt rd, rs, x0 Устанавливает результат в 1 если rs < 0
    sgtz rd, rs1 slt rd, x0, rs Устанавливает результат в 1 если rs >0
    beqz rs1, offset beq rs, x0, offset Переход если равно 0
    bnez rs1, offset bne rs, x0, offset Переход если не равно 0
    blez rs1, offset bge x0, rs, offset Переход если меньше или равно 0
    bgez rs1, offset bge rs, x0, offset Переход если больше или равно 0
    bltz rs1, offset blt rs, x0, offset Переход если меньше 0
    bgtz rs1, offset blt x0, rs, offset Переход если больше 0
    bgt rs, rt, offset blt rt, rs, offset Переход если больше
    ble rs, rt, offset bge rt, rs, offset Переход если меньше или равно
    bgtu rs, rt, offset bltu rt, rs, offset Условный переход сравнить два беззнаковых числа. Если rs >rt переход по адресу offset
    j offset jal x0, offset безусловный переход
    jr offset jal x1, offset Безусловный переход по адресу, адрес возврата в x1
    ret jalr x0, x1, 0 Возврат из функции

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

    # Начальные значения fib(0) = 0, fib(1) = 1
        add x1, x0, x0          # в x1 заносим 0 
        add x2, x0, x1          # в x2 заносим 1
        # Переменная n для хранения входящего значения
        add x3, x10, x0          # x3 = n
        # Проверка n (0 и 1)
        add x4, x0, x0          # x4 = 0                    ; fib(0)
        beq x3, x4, return_zero  # Если n == 0, переход к return_zero
        add x4, x0, x0          # x4 = 0                    ; переменная для счетчика
        addi x4, x4, 1          # x4 = 1                    ; стартовый индекс (fib(1))
        next_fib:
        add x5, x1, x2          # x5 = fib(n-1) + fib(n-2)
        add x1, x2, x0          # fib(n-1) = fib(n)
        add x2, x5, x0          # fib(n) = новый fib(n)
        addi x4, x4, 1          # увеличиваем счетчик
        bne x4, x3, next_fib    # если счетчик не равен n, переходим к next_fib
    finish:
        add x10, x2, x0          # возвращаем результат в a0
    return_zero:
        add x10, x0, x0          # возвращаем 0
    

    Организация и работа с памятью в RISC-V. Стековая память

    Стековая память в архитектуре RISC-V организуется аналогично другим архитектурам с учетом особенностей RISC-V. Как отмечалось выше основное назначение стека - временное хранение переменных при вызове функции, а также хранение адресов возврата из функций (подпрограмм).

    Стек растет в выделенной для него области памяти от старших адресов к младшим. При добавлении новых данных в стек адрес стека уменьшается.

    Для работы со стеком в файле регистров выделен регистр x2, имеющий алиас sp - указывает на вершину стека.

    В дополнение к sp может применятся регистр fp (frame pointer), который помогает отслеживать основание текущего фрейма функции, что облегчает доступ к локальным переменным и параметрам.

    Операции со стеком:

    При входе в функцию устанавливается фрейм указателя fp и выделяется место для локальных переменных (пример приведен в листинге 7.6, а работа показана на рисунке 7.11).

         addi sp, sp, -16; уменьшить указатель стека для выделения 16 байт
         sw ra, 12(sp); сохранить адрес возврата
         sw fp, 8(sp); сохранить предыдущий фрейм указателя
         addi fp, sp, 16; установить новый фрейм указателя
    
    (рис 7.11)

    При завершении работы функции указатели восстанавливаются, и стек возвращается в прежнее состояние (листинг 7.7).

        addi sp, sp, 16; восстановить указатель стека
         lw ra, 12(sp); восстановить адрес возврата
         lw fp, 8(sp); восстановить предыдущий фрейм указателя
         ret; возврат из функции
    

    Кэш память

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

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

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

    Кэш-память включает несколько уровней, каждый из которых имеет свои характеристики:

  • Уровень L1

    Разделение на кэш инструкций и кэш данных:

  • I-cache (кэш инструкций): Хранит инструкции, которые процессор будет выполнять. Обычно реализуется с невысокой латентностью доступа.
  • D-cache (кэш данных): Хранит данные, необходимые для выполнения программ.
  • Размер: Обычно L1-кэш имеет небольшой размер, диапазон обычно от 16 KB до 128 KB. Этот уровень кэша имеет очень высокую скорость доступа.

  • Уровень L2

    Единый или разделённый L2-кэш может быть либо единым для инструкций и данных, либо разделённым на I-cache и D-cache.

    Объем больший по сравнению с L1-кэшем и может варьироваться от 256 KB до нескольких МБ. Он медленнее чем L1, но все ещё намного быстрее, чем доступ к основной памяти.

  • На рисунке 6.12 приведена пример схемы кэш-памяти.

    (рис 7.12)

    Ассоциативность кэша определяет, сколько мест (строк) может использоваться для хранения определённого блока данных.

    Прямой кэш: каждый блок памяти может быть помещён только в одно определённое место в кэше. Это простейшая и наиболее быстрая организация.

    Ассоциативный кэш: позволяет хранить блоки данных в нескольких местах. Например, 4-х ассоциативный кэш может разместить данные в четырёх различных местах, что снижает вероятность конфликтов при хранении.

    Полностью ассоциативный кэш: данные могут храниться в любом месте кэша, что повышает гибкость, но требует сложных алгоритмов поиска.

    Устройство кэш-строк:

  • тег: уникальный идентификатор, относящийся к блоку данных в основной памяти. Используется для определения принадлежности данных к конкретному блоку.
  • данные: хранимые данные.
  • контрольная информация: например, биты валидности (valid bits) и биты "изменения" (dirty bits), которые показывают, были ли данные изменены.
  • Существуют следующие алгоритмы кэширования

  • Замещение: когда кэш заполнен и необходимо загрузить новый блок, используется алгоритм замещения:

  • LRU (Least Recently Used): Удаляет наименее использованные блоки.
  • FIFO (First In First Out): Удаляет самые старые блоки.
  • Random: Случайным образом выбирает блок для удаления.
  • Запись: при записи данных в кэш могут использоваться различные стратегии:

  • Write-Through - данные сразу записываются как в кэш, так и в основную память. Это гарантирует целостность данных, но может быть медленнее.
  • - Write-Back - данные записываются в кэш, а в основную память только при необходимости (например, при замещении). Это может повысить производительность, но потребует дополнительной логики для поддержания согласованности данных.
  • Кэш-память основывается на двух основных принципах локальности:

  • Локальность по времени. Если данные были использованы в последнее время, они с большой вероятностью будут использованы снова.
  • Локальность по пространству. Если данные были использованы, то данные, находящиеся рядом в с ними в памяти также имеют высокую вероятность быть использованными.
  • При разработке собственных решений на архитектуре RISC-V в зависимости от проекта применяется создание блока кэш-памяти. Относительно ядра процессора кэш будет внешним устройством и внедрение потребует создание блока контроллера кэш - памяти. На рисунке 7.13 приведен пример одной из разработок для RISC-V на FPGA с блоком кэш-памяти [7]. Для приведенного блока разработчиком определены следующие характеристики:

  • объем кэш памяти: 512 байт;
  • размер блока кэш-памяти: 16 байт;
  • адрес памяти: 10 бит;
  • смещение: 2 бита;
  • индекс данных: 5 бит;
  • тэг: 3 бита.
  • (рис 7.13)

    Тактирование процессора

    Тактирование процессора определяет порядок его функционирования, обеспечивая синхронность процессов, выполнения операций и управления работой всех блоков.

    Цикл тактирования время между двумя последовательными пиками тактового сигнала. Это время, за которое процессор может выполнить одно или несколько действий.

    Критически длинный путь - время, отводимое на исполнение самой долгой инструкции.

    В большинстве процессоров, включая RISC-V, выполнение инструкций делится на несколько этапов, каждый из которых обычно занимает один тактовый цикл. Эти этапы, как было отмечено в начале раздела, могут включать:

  • Выборка инструкции (Fetch). Процессор считывает следующую инструкцию из памяти. Используется счетчик инструкций (PC), указывающий на адрес инструкции.
  • Декодирование (Decode). Инструкция декодируется для определения, какие действия следует выполнить и какие регистры или ячейки памяти необходимы. Активируются связи для взаимодействия операндов.
  • Исполнение (Execute). Выполняются операции, указанные в инструкции, часто с использованием арифметико-логического устройства (ALU).Например, в случае арифметических операций производится математическое вычисление.
  • Запись результата (Write Back). Результат выполнения операции записывается обратно в регистры или память.
  • Выделяют однотактные, многоактные и конвейерные процессоры.

    Однотактный (single-cycle) - процессор выполняет всю команду за один такт. Принцип работы легко объяснить, а устройство управления довольно простое. Из-за того, что все действия выполняются за один такт, эта микроархитектура не требует дополнительных регистров, требуемых для работы процессора, но недоступных для использования программистом). Однако, длительность такта ограничена самой медленной командой, использующей самый длинный критический путь.

    Основным преимуществом однотактной микроархитектуры является простота понимания ее работы. К минусам можно отнести: относительно высокие аппаратные затраты из-за использования дополнительных сумматоров и раздельной основной памяти (гарвардская архитектура), низкая тактовая частота из-за длинного критического пути, так как разные инструкции проходят разный путь, скорость работы ограничена скоростью самой медленной инструкции.

    Многотактный процессор (multi-cycle) - выполняет команду за несколько более коротких тактов. Простым командам нужно меньше тактов, чем сложным. Вдобавок, многотактная микроархитектура уменьшает количество необходимой аппаратуры путем повторного использования таких "дорогих " блоков, как сумматоры и блоки памяти.

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

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

    Приведенный на рисунке 7.8 процессор, который проектируется в данном курсе является однотактным. В дополнительной литературе [1] приводятся примеры многотактных процессоров и организации конвейера.

    Приложение А

    1. Сокращенная система команд Intel x86 (IA32)

    Арифметические операции:

    ADD - сложение.

    Пример: add eax, ebx; сложить EAX и EBX результат в EAX

    SUB - вычитание.

    MUL - умножение (беззнаковое).

    IMUL - умножение (знаковое).

    DIV- деление (беззнаковое).

    IDIV- деление (знаковое).

    INC - инкремент, увеличение на 1.

    DEX - декремент уменьшение на 1.

    Пример: dec ecx; Уменьшение счетчика ecx на 1

    Логические операции:

    AND - логическое "И".

    OR - логическое "ИЛИ".

    XOR - исключающее "ИЛИ".

    Пример: xor ebx, ebx; Очистить ebx

    NOT- логическое отрицание.

    Сравнения и переходы:

    CMP - сравнение с установкой флага 0

    Пример: cmp ecx, 0; сравнение содержимого регистра ecx с 0 в результате совпадения флаг 0 - ZF регистра состояний установится в 0;

    JNE (JNZ)-переход, если не равно.

    Пример: dec ecx; jnz .loop; Если ecx не достиг 0 вернуться на начало

    JE - переход, если равно.

    Пример: cmp ecx, 0 je .done; Если в ходе предыдущей операции ecx=0, то перейти по адресу метки done

    JL - переход, если меньше.

    JG - переход, если больше.

    Передача управления:

    CALL - вызов процедуры.

    Пример: call printf; вызвать подпрограмму по адресу метки

    RET - возврат из процедуры.

    JMP - безусловный переход.

    Загрузки и сохранения:

    MOV - перемещение данных.

    Пример: mov ebx, edx; записать содержимое регистра edx в ebx

    MOVZX - считать содержимое источника как байт или слово с заполнением 0 до размера операнда.

    Пример: movzx ecx, byte [n]; загрузить байт с заполнением нулями до размера ecx

    PUSH - помещение значения в стек.

    Пример: push eax; поместить в стек eax

    POP - извлечение значения из стека.

    Пример: pop eax; восстановить из стека eax

    LOAD - загрузка из памяти (в основном через MOV).

    Управление прерываниями:

    INT - вызов прерывания.

    Пример int 0x80; вызов прерывания операционной системы Linux

    IRET - возврат из прерывания.

    Системные команды (для работы с процессором и памятью):

    HLT - остановка процессора.

    NOP -нет операции (пустая команда).

    2. Система команд Atmega328

    Арифметические команды:

    ADD - сложение без переноса

    Пример: ADD r16, r17; сложение регистров r16 и r17 с занесением результата в r16

    ADC - сложение с учетом переноса.

    Пример: ADC r16, r17; сложение регистров r16 и r17 с занесением результата в r16 и формирование знака переноса

    SUB - вычитание.

    SBC - вычитание с учетом переноса.

    MUL - умножение.

    DIV - деление.

    INC - инкремент, увеличение регистра на 1.

    Пример: inc r20; Инкремент содержимого r20

    DEC - декремент, уменьшение регистра на 1.

    Логические команды:

    AND - логическое И.

    Пример: and r2, r3 ; Поразрядное and r2 и r3, результат поместить в r2

    OR - логическое ИЛИ.

    XOR - логическое исключающее ИЛИ.

    Команды сдвига и поворота:

    LSL - логический сдвиг влево.

    LSR - логический сдвиг вправо.

    ROR - циклический сдвиг вправо.

    ROL - циклический сдвиг влево.

    Команды сравнения:

    CPI - сравнение с немедленным значением.

    CP - сравнение двух регистров.

    Пример: cp r20, r16; Сравнить регистры r20 и r16

    Управление переходами:

    JMP - безусловный переход.

    CALL - вызов подпрограммы.

    RET - возврат из подпрограммы.

    BRNE, BEQ, BGE, BLT и другие - условные переходы.

    Пример: brne <метка перехода > перейти по метке если прядущее значение равно 0

    cpi r27, 5; Сравнить r27 с 5

    brne loop ; Перейти если r27 < > 5

    Команды работы с вводом-выводом:

    MOV - запись между регистрами

    Пример: mov r18, r19; Копировать r19 в r18

    IN - чтение данных из порта ввода.

    Пример: in r25, $16; Считать порт B

    OUT - запись данных в порт вывода.

    Команды работы с памятью:

    LD - загрузка данных из SRAM или EEPROM.

    LDI - загрузка непосредственного значения в регистр

    Пример: LDI R16, 10; Загрузить в регистр R16 значение 10

    ST - сохранение данных в SRAM.

    Команды работы с прерываниями:

    SEI - разрешение прерываний.

    CLI - отключение прерываний.

    RET - возврат из обслуживания прерывания.

    Подробное описание инструкций на русском языке [8].

    3. Общая система команд для процессоров ARM

    Команды ввода вывода

    LOAD (LD) - загрузка данных из памяти в регистр.

    Примеры:

    LDR r0, [r1]; Загружает значение из памяти по адресу R1 в регистр R0

    ldr r0, =0 ; Заносит в регистр r0 значение по ссылке

    STORE (STR) - сохранение данных из регистра в память.

    Пример: STR r0, [r2] ; Сохраняет значение r0 в память по адресу хранящемуся в r2

    Арифметические и логические команды

    ADD - сложение двух регистров.

    Пример: ADD R2, R0, R1; Сложить r0 и r1 результат записать r2

    SUB - вычитание.

    AND, ORR, EOR - логические операции.

    Пример: AND R4, R2, R3; Выполнить побитно операцию И над содержимым регистров r2 и r3 результат записать в r4

    MUL - умножение.

    SDIV, UDIV - деление знаковых и беззнаковых чисел.

    Примеры: MUL R4, R0, R1; Умножить содержимое регистров r0 и r1, результат поместить в r4; SDIV R5, R4, R2; Разделить содержимое регистра r4 на r2, результат в r5 (знаковое деление)

    Команды управления переходами

    B - безусловный переход.

    BL - переход с вызовом подпрограммы.

    BX - переход на адрес, расположенный в регистре.

    Примеры: B label; Переход к метке label. BL function; Вызов функции. BX R3; Переход на адрес, находящийся в r3

    Команды управления памятью

    PUSH - сохранить регистры в стек.

    POP - извлечь регистры из стека.

    Примеры: PUSH {R0, R1}; Сохраняет R0 и R1 в стек. POP {R2, R3}; Извлекает значения из стека в R2 и R3

    Подробное описание инструкций на русском языке [9].

    Приложение Б

    (рис 7.14)

    Литература

  • Орлов С.А. Цилькер Б.А. Организация ЭВМ и систем: Учебник для ВУЗов. 3-е изд. СПб.2014.-688с.:ил.
  • Ерохин В.В. Микропроцессоры. Теория и практика проектирования, Солон-Пресс, 2023. - 256с.:ил.
  • Структурная схема процессора Intel Pentium
  • Статья An Introduction to ARM Architecture with Each Module's Working Principle
  • Статья A microarchitectural analysis of soft error propagation in a production-level embedded microprocessor
  • Инструкция консорциума RISC-V The RISC-V Instruction Set Manual
  • Репозиторий с процессором RISC-V и модулем кэш-памяти
  • Справочные данные по системе команд Atmega 328
  • Справочные данные по системе команд ARM
  • Дополнительная литература

  • С. Харрис, Д. Харрис. Цифровая схемотехника и архитектура компьютера RISC-V/ пер. с англ. В.С. Яценкова, А.Ю. Романова; под. ред. А.Ю. Романова.-М.: ДМК Пресс, 2021. - 810 с.: ил.
  • С. Харрис, Д. Харрис. Цифровая схемотехника и архитектура компьютера. Дополнение по архитектуре АРМ. ДМК Пресс,2019. - 356с.:ил.
  • Смит Б. Ассемблер для Raspberry Pi. Практическое руководство. - СПб.: БХВ-Петербург, 2022. - 320с.:ил.
  • Страницы:

    Процессор или центральный процессор - основное устройство любой электронно-вычислительной машины (ЭВМ) в широком понимании данного термина. Он осуществляет управление всеми элементами данной машины и выполняет основной набор вычислительных функций для конкретного решения. Упрощенный вариант процессора приведен на рисунке 7.1.

    Блок управления осуществляет выборку инструкции из памяти ЭВМ, дает команды внутри процессора и управляет работой внешних устройств в том числе и памятью. Выбранная инструкция, если содержит данные в памяти, должна через дешифратор инструкций загрузить данные в соответствующие регистры процессора (условные регистр А и В). Дешифратор инструкций, если подразумевается какая-либо операция над данными, активирует соответствующий узел операционного блока. Результат инструкции может быть возвращен в память либо в один из регистров. Далее осуществляется выборка следующей инструкции или переход к другому участку памяти, в случае вызова перехода в результате исполнения инструкции.

    (рис 7.1)

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

    Первый вид классификации процессоров - система команд процессора (ISA - Instruction Set Architecture). ISA - набор инструкций, которые процессор может выполнять. Инструкции включает в себя как основные команды, так и формат данных, управление памятью и регистры. ISA является ключевым понятием в архитектуре микропроцессорных систем, так как определяет, каким образом программное обеспечение взаимодействует с аппаратным.

    Выделяют три категории в данной классификации:

  • CISC (Complex Instruction Set Computer) сложный набор инструкций.

    Такие процессоры могут иметь инструкции произвольной длины. Процессоры этой архитектуры способны выполнять несколько операций за несколько тактов. В архитектуре CISC обеспечивается большое количество методов адресации. В тоже время требуется условно небольшое количество регистров необходимых для реализации программы.

    К данной категории процессоров относятся x86 совместимые процессоры.

  • RISC (Reduced Instruction Set Computer) упрощенный набор инструкций.

    Процессоры RISC имеют одинаковый формат команд. Все команды имеют одинаковую длину. Инструкции исполняются за один такт за исключением сложных арифметических инструкций. RISC процессоры имеют небольшое количество команд по сравнению с CISC. Для повышения быстродействия имеют большое количество неименованных регистров, объединяемых в регистровый файл.

    Для RISC процессоров признаками являются:

  • расширенный набор регистров;
  • симметричность использования определенных регистров;
  • упрощенный дешифратор команд за счет простоты организации инструкций;
  • простые механизмы адресации данных.
  • К данной категории процессоров относятся процессоры ARM, различные микроархитектуры микроконтроллеров, например, AVR, PIC, а также решения RISC-V.

  • VLIW (Very Long Instruction Word) - очень длинная машинная команда - архитектура процессоров, характеризующаяся возможностью объединения нескольких простых команд в так называемую связку. Входящие в нее команды должны быть независимы друг от друга и выполняться параллельно. Таким образом, из нескольких независимых машинных команд транслятор формирует одно очень длинное командное слово. Данная архитектура подразумевает, что длинная команда распараллеливается на несколько ядер, исполняющих каждое свою часть. При применении процессоров данной архитектуры большая часть работы выполняется компилятором языка программирования.

    К данной категории процессоров относятся процессоры Эльбрус и некоторые решения IA64.

  • Вторым видом классификации архитектур процессоров является способ доступа к памяти. В данной классификации выделяют две категории:

    1.Архитектура Фон Неймана:

  • данные и инструкции хранятся в одной памяти;
  • инструкции исполняются в строгом порядке, переходы должны быть регламентированы;
  • обмен данными между памятью и периферийными устройствами осуществляется под управлением процессора и дополнительными арбитрами.
  • Данная архитектура имеет следующие преимущества:

  • простота организации - использование одного интерфейса для доступа к памяти;
  • универсальность работы с памятью.
  • Недостатки:

  • переполнение буфера - ввод данных в ходе исполнения программы может повредить данные или код, размещенные за пределами отведенного сегмента памяти;
  • низкая производительность. Данный недостаток вызван тем, что необходимо поочередно выбирать инструкции, а затем выбирать данные из памяти.
  • На рисунке 7.2 приведена упрощенная схема организации данной архитектуры.

    (рис 7.2)

    Архитектура фон Неймана применяется x86 совместимыми процессорами и их последующими поколениями.

    Гарвардская архитектура имеет следующие свойства:

  • данные и инструкции хранятся раздельно в разных модулях памяти;
  • высокое быстродействие за счет параллельного доступа к инструкциям и данным.
  • Свойства данной архитектуры исключают недостатки архитектуры фон Неймана. Недостатком данной архитектуры является увеличенное количество линий связи с памятью и схемотехническая сложность интерфейса модулей памяти.

    Процессоры с RISC системой команд строят по гарвардской архитектуре, для обеспечения максимальной производительности. На рисунке 7.3 приведена упрощенная схема организации гарвардской архитектуры.

    (рис 7.3)

    Другими специфическими особенностями классификации, скорее микроархитектур, можно отнести следующие:

  • Наличие регистра флагов. Например, в RISC-V отсутствует данный регистр. Применение триггеров различных признаков состояния процессора, таких как переполнение АЛУ, признак (флаг) 0. Данные триггеры объединяются в регистр флагов и применяются при построении схемы управления ветвлениями в процессорах.
  • Наличие определенного регистра аккумулятора для работы с АЛУ (x86, микроконтроллеры PIC имеют данный регистр).
  • По системам команд процессоры классифицируются на:

  • безадресные (стековые);
  • одноадресные;
  • двухадресные, например, x86 и ряд RISC процессоров и микроконтроллеров;
  • трехадресные - архитектура RISC-V.
  • Еще одним видом классификации процессоров является степень параллелизма обработки данных:

    1. SISD (Single Instruction, Single Data) - это архитектура, в которой один процессор выполняет одну инструкцию над одним набором данных в один момент времени. Это традиционная модель вычислений, используемая в большинстве обычных процессоров.

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

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

    Преимущества: простота реализации и программирования.

    Недостатки: низкая эффективность в сравнении с параллельными архитектурами при выполнении сложных задач.

    2. SIMD (Single Instruction, Multiple Data) - это архитектура, в которой одна и та же инструкция применяется одновременно ко многим данным. Это позволяет значительно ускорить выполнение операций над большими массивами данных.

    В один момент времени одна инструкция применяется ко всем элементам векторов или массивов. Например, сложение двух векторов чисел.

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

    Преимущества: высокая производительность при обработке больших объемов однотипных данных, эффективное использование ресурсов.

    Недостатки: ограниченная гибкость, поскольку не все задачи можно разбить на операции с одинаковыми данными.

    3. MIMD (Multiple Instruction, Multiple Data) - это архитектура, в которой несколько процессоров могут выполнять разные инструкции над разными наборами данных одновременно. Это наиболее универсальный тип архитектуры.

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

    Применение: используется в многоядерных процессорах, кластерах, распределенных системах и для высокопроизводительных вычислений.

    Преимущества: высокая степень гибкости и универсальности, возможность работы с разнообразными задачами.

    Недостатки: более сложная реализация и необходимость в эффективной синхронизации и управлении потоками.

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

    Процессоры x86

    Применяемые в большинстве решений вычислительных машин, процессоры реализованы на так называемой Intel совместимой архитектуре. Данные процессорные решения имеют разную микроархитектуру и совместимы между собой на уровне общих инструкций языка ассемблера. В то же время могут иметь собственные специфические инструкции, описываемые в документации разработчика. На рисунке 7.4 приведена общедоступная структурная схема процессора Intel Pentium [3], совместимость с которым у других производителей была наиболее близкой. На структурной схеме можно выделить следующие блоки:

  • Bus Interface Unit (блок интерфейса шины) обеспечивает взаимодействие с внешней шиной и кэшом L2.
  • Instruction Fetch Unit (блок извлечения инструкций) отвечает за извлечение инструкций из памяти.
  • Instruction Decoder (дешифратор инструкций) декодирует извлеченные инструкции. Данный блок состоит из двух простых и одного сложного дешифраторов, отвечающих за преобразование инструкции в микрокоманды управления.
  • Branch Target Buffer (буфер адреса перехода) ускоряет выполнение ветвящихся инструкций, обеспечивая быстрый доступ к целевым адресам.
  • Microinstruction Sequencer (планировщик последовательности микрокоманд) обрабатывает сложные инструкции, которые не могут выполнить составные дешифраторы Instruction Decoder, формируя при этом необходимое количество микрокоманд. При работе с данным блоком активируется Reservation Station.
  • Register Alias Table (таблица алиасов регистров) управляет именами регистров, чтобы избежать конфликтов при одновременном обращении к двух соседних команд к одному регистру. В данном блоке именные ссылки на регистры преобразуются в адреса 40 физических регистров.
  • Reservation Station (станция ожидания) хранит инструкции, ожидающие выполнения и освобождения для них ресурсов.
  • Memory Reorder Buffer (буфер упорядочения памяти) обеспечивает упорядоченное выполнение инструкций в отношении памяти. Пересылает данные в память тогда, когда микроинструкция осуществлявшая запись в память удалена.
  • Data Cache Unit (блок данных кэша) хранит временные данные для быстрого доступа.
  • Memory Interface Unit (блок интерфейса памяти) управляет взаимодействием с основной памятью.
  • Address Generation Unit (блок генерации адресов) генерирует адреса для доступа к памяти.
  • Integer Execution Unit (управляющий блок целых чисел) выполняет арифметические и логические операции над целыми числами.
  • Floating-point Execution Unit (управляющий блок с плавающей запятой) отвечает за выполнение операций над числами с плавающей запятой.
  • Reorder Buffer and Retirement Register File (буфер восстановления последовательности) управляет завершением выполнения инструкций, гарантируя, что они будут выполнены в правильном порядке. Хранит часть микрокоманд, ожидающих исполнения.
  • (рис 7.4)

    С точки зрения программиста в Intel архитектуре выделяются общие поименованные регистры, которые применяются при разработке программ на языке ассемблера. Данные регистры называют регистрами общего назначения (РОН). К некоторым из них можно обращаться секциями: байтом, словом двойным словом (для 32-разрядной x86 архитектуры). На рисунке 7.5 приведены поименованные 32-х разрядные регистры Intel x86 и форматы взаимодействия с частями данных регистров. При работе с 64-х разрядной архитектурой процессора префикс заменяется на букву R. Регистры общего назначения являются основой для разработки программ и позволяют решать основные задачи по обработке данных, таких, как хранение операндов для арифметических и логических операций, операндов вычисления адресов ячеек памяти, указателей на переменные в памяти.

    Несмотря на общий характер данных регистров они всё же используются для определенных целей:

  • EAX - регистр аккумулятор. Применяется при работе с арифметическими и логическими операциями, хранит результат операции. Наличие регистра аккумулятора относит процессор к одному из видов процессоров - процессоров с аккумуляторами
  • EBX - базовый регистр. Применяется для хранения базового адреса данных в памяти, адресуемой регистром DS;
  • ECX - регистр счетчик. Применяется для организации циклов и хранения элементов строковых операций;
  • EDX - регистр данных. Хранит промежуточные данные и применяется при работе с арифметико-логическим устройством и операциями ввода вывода;
  • ESP - регистр указатель стека. Указывает вершину стека, адресуемого регистром SS.
  • EBP - регистр указатель. Применяется для указания базового стекового адреса
  • ESI - регистр указатель источника индекса. Указывает на данные, находящиеся в сегменте оперативной памяти, адресуемом регистром DS.
  • EDI - регистр указатель индекса получателя данных. Данный регистр указывает на сегмент данных, адресуемый регистром ES.
  • В 32-х разрядных процессорах архитектуры x86 для управления памятью применяются следующие сегментные регистры:

  • CS - регистр сегмента кода. Содержит адрес сегмента, содержащий коды инструкций программы, загружаемой в процессор;
  • DS - регистр сегмента данных. Содержит обрабатываемые программой данные;
  • SS - регистр сегмента стека. Содержит адрес стека.
  • (рис 7.5)

    Стек (стековая память) - структура данных в оперативной памяти, применяемая для временного хранения содержимого регистров процессора, например, для обработки подпрограммы прерывания. Данные упорядочиваются в стеке последовательно. Существуют следующие виды стеков:

  • LIFO - Last input, First output. Данные, поступившие в данный сегмент памяти последними, будут извлечены и удалены первыми;
  • FIFO - First input, First output. Данные, поступившие в данный сегмент памяти первыми, будут извлечены и удалены первыми.
  • Регистр указатель стека ESP фиксирует в себе адрес последних записанных данных.

    Отдельно стоит отметить наличие регистра флагов, определяющего состояние процессора, применяемого для управления переходами.

    Как видно из описания, x86 процессоры представляют собой сложную организацию с закрытой микроархитектурой. Механизмы управления и параллелизма представляют собой множество цепей управления, соответственно система управления является громоздкой и требует инженерного творчества для обеспечения высокой производительности.

    RISC процессоры

    Рассмотрим микроархитектуру RISC на примере восьмиразрядного микроконтроллера Atmega328P (устанавливается на все Arduino UNO отладочные платы). На рисунке 7.6 приведена упрощенная схема данного микроконтроллера со следующими основными блоками:

  • Flas Program memory - перепрограммируемая память инструкций;
  • Instruction Register (IR) - регистр текущей инструкции;
  • Instruction Decoder - дешифратор команд;
  • Program Counter (PC) - счетчик адреса инструкций. Указывает на адрес памяти инструкций;
  • Status and Control - регистр состояний;
  • 32x8 General Purpose Registers - регистры общего назначения, объединенные в регистровый файл;
  • Data SRAM - оперативная статическая память данных;
  • EEPROM - постоянная память данных;
  • I/O Lines - линии соединения процессорного ядра с периферийными модулями.
  • Расположенные справа модули представляют специфические для каждого микроконтроллера встроенные периферийные устройства, обеспечивающие сбор, прием/передачу данных и сигналов для исполнительных механизмов, т.е порты ввода-вывода данных (I/O Module).

    Как видно из схемы память инструкций и данных разделены, соответственно архитектура организации памяти - гарвардская.

    (рис 7.6)

    RISC процессоры ARM

    ARM процессоры - это коммерческие лицензируемые ядра процессоров, построенные с использованием RISC команд, но с технологическими особенностями организации этого ядра. Компания ARM, разработчик различных инженерных решений, продает лицензии и документацию на ядро процессора конечным производителям микропроцессоров. Разработчики, используя свои периферийные модули и другие вычислительные блоки, добавляя их к ядру ARM, получают готовое решение. На рисунке 7.7 представлена упрощенная структурная схема процессора ARM9, реализованная на описаниях из разных информационных источников [4,5]. На структурной схеме можно выделить следующие блоки:

  • Регистровый файл содержит в себе регистры общего назначения - стандартное решение для RISC архитектур, с особенностями применения в зависимости от режима работы процессора;
  • АЛУ - арифметико-логическое устройство;
  • Устройство быстрого сдвига находится вне АЛУ, позволяет сдвигать входные данные на заданное число бит до операции в АЛУ;
  • Блок умножителя, работающий по алгоритму Бута
  • Кэш память инструкций и кэш память данных являются памятью быстрого доступа для ускорения выполнения операций. Быстродействие достигается за счет предварительной загрузки из основной памяти, тем самым снижая время обращения к ней.
  • Блок выборки и инструкций и дешифратор инструкций - часть системы управления процессора. Они выбирают команды из памяти и осуществляют коммутацию блоков процессора;
  • Блок мультиплексоров участвует в распараллеливании доступа к операционным блокам процессора;
  • Интерфейс данных - часть общей системы управления, участвующая в распараллеливании процессов, а также включающая в себя регистр флагов для управления переходами;
  • Устройство управления памятью (MMU) - блок трансляции "виртуальны адресов " программ в физические адреса памяти.
  • (рис 7.7)

    Более подробная информация об организации работы процессоров, существующих архитектурах, методах работы с памятью приводится в литературе [1,2] и дополнительной литературе [2,3].

    Для понимания различий в архитектуре рассмотренных процессоров реализуем решение задачи по вычислению чисел Фибоначчи на языках ассемблера (ISA). Предварительно приведем код для вычисления чисел Фибоначчи на языке Си (листинге 7.1).

    include  <stdio.h >
    int main() {
        int n= 10; // количество чисел Фибоначчи
        unsigned long long fib[10]; // массив для хранения чисел Фибоначчи
        // инициализация первых двух чисел Фибоначчи
        fib[0] = 0;
        fib[1] = 1;
        // вычисление чисел Фибоначчи
        for (int i = 2; i  < n; i++) {
            fib[i] = fib[i - 1] + fib[i - 2];
        }
        // вывод чисел Фибоначчи
        printf("Первые %d чисел Фибоначчи:\n", n);
        for (int i = 0; i  < n; i++) {
            printf("%llu ", fib[i]);
        }
        printf("\n");
        return 0;
    }
    

    Для описанного выше примера приведем код на языке ассемблера процессора x86, реализованного в компиляторе NASM (листинге 7.2)

    section .data
        fmt db "Fibonacci(%d) = %d", 10, 0  ; Формат строки для вывода
        n db 5; Измените здесь для получения n-го числа Фибоначчи
    section .bss
        result resd 1; Результат вычисления
    section .text
        extern printf; Импорт функции printf
        global main; Точка входа в программу
    main:
        ; Загружаем значение n
        movzx ecx, byte [n]; Загружаем n в ecx
        mov eax, 0; fib(0)
        mov ebx, 1; fib(1)
        cmp ecx, 0; Если n = 0
        je .done; Переход к завершению
        cmp ecx, 1; Если n = 1
        je .next; Переход к следующему
        ; Итеративная часть
    .loop:
        ;Вычисляем n-е число Фибоначчи
        mov edx, eax                        ; Сохраняем предыдущую Fibonacci (fib(n-2))
        add eax, ebx; fib(n) = fib(n-1) + fib(n-2)
        mov ebx, edx; Обновляем fib(n-1) на fib(n-2)
        dec ecx; Уменьшаем счетчик
        jnz .loop; Повторяем до n = 0
    .next:
        ; Задаем результат для вывода
        mov [result], eax ; Сохраняем результат
    .done:
        ; Вывод результата
        push eax; Параметр: значение Fibonacci
        push dword [n]; Параметр: n
        push fmt; Строка для printf
        call printf; Вызов функции printf
        add esp, 12; Очистка стека
        ; Завершение программы
        mov eax, 1; sys_exit
        xor ebx, ebx; Устанавливаем код возврата 0
        int 0x80; Вызов ядра
    

    Приведем решение данной задачи на языке ассемблера для микроконтроллера Atmega328P. Код приведен в листинге 7.3.

    .section .text
    .global fib
    fib:
       ; Сохранение регистров
       push r16
       push r17
       mov r16, r24
       ; Инициализация значений
       ldi r18, 0; Заносим первое значение
       ldi r19, 1; Заносим второе значение
       ; Проверяем (0 или 1)
       cp r16, r18
       breq .fib_done
       cp r16, r19
       breq .fib_done
       ; Цикл вычислений
       ldi r20, 2
       ldi r21, 0
    .fib_loop:
       cp r20, r16
       breq .fib_done
       ; Вычисления чисел Фибоначчи
       add r21, r18
       mov r18, r19
       mov r19, r21
       ; Увеличение счетчика
       inc r20
       ; Проверка окончания цикла .fib_loop
       brne .fib_loop
       ; Возврат по окончании вычислений
       call .fib_done
    .fib_done:
       mov r24, r19; Сохранение итога
       ; Завершение программы
       pop r17
       pop r16
       ret
    

    Далее приводится код на ассемблере ARM процессора. Код разработан для микрокомпьютера Raspberry Pi 2/3 (листинг 7.4).

    .section .data
        fib_num: .asciz "Fibonacci: %d\n"  // Форматированная строка для вывода
    .section .bss
        num1: .skip 4  // Первый элемент массива
        num2: .skip 4  // Второй элемент массива
        result: .skip 4  // Результат
    .section .text
    .global _start
    .extern printf  // Объявление функции printf
    _start:
        ldr r0, =0          // num1 = 0
        str r0, num1
        ldr r0, =1          // num2 = 1
        str r0, num2
        mov r1, #0          // Счетчик
        mov r2, #10         // Количество чисел Фибоначчи для вычисления
    fib_loop:
        ldr r0, num1
        ldr r3, num2
        add r0, r0, r3     // r0 = num1 + num2
        str r0, result
        ldr r0, =fib_num   // Указатель на строку
        ldr r1, result     // Результат для передачи в printf
        bl printf
        ldr r0, num2       // Обновление num1
        str r0, num1
        str r0, num2
        add r1, r1, #1
        cmp r1, r2
        blt fib_loop       // Если r1  < r2, продолжаем цикл
        mov r0, #0         // Код возврата 0
        bx lr              // Завершение программы
    

    В приложении А приводится краткая система команд данных процессоров.

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

    Архитектура RISC-V представляет собой расширяемую открытую свободную систему команд, а также свободно распространяемую микроархитектуру ядра.

    Свобода распространения и внесение изменений привлекли пристальное внимание со стороны как энтузиастов в академической среде, так и крупных разработчиков, производителей электроники. На сегодняшний день сформировался пул организаций, поддерживающих и развивающих данную архитектуру. В Российской Федерации также существует команда разработчиков и производителей, объединённая в "Альянс RISC-V ".

    На рисунке 7.8 представлена структурная схема микроархитектуры ядра процессора RISC-V для стандартных целочисленных операций.

    На рисунке структурной схемы выделены следующие блоки:

  • PC (program counter) - счетчик адреса памяти инструкции (Instruction memory, IMEM), указывающий на выбираемую из памяти инструкцию. В ряде источников данный счетчик обычно называется указателем команд (IP) или регистром адреса команд (IAR).

    После выполнения инструкции значение PC обновляется так, чтобы указывать на следующую инструкцию в последовательности. Вне зависимости от того какая инструкция выполняется, процессор последовательно считывает инструкции, увеличивая счетчик на размер инструкций (4 байта для стандартных 32-х битных инструкций). Данная операция выполняется блоком сумматора текущего адреса и константы 4.

    Если в программе есть ветвления, такие как условные операции (например, beq - branch if equal), значение PC будет изменяться не на постоянной основе, а в зависимости от результата операции. На рисунке данный блок обозначен как Branch.

  • Instruction memory (IMEM) - память инструкций, хранящая в себе машинный код исполняемой программы. Разрядность шины данных - 32 бита.
  • Data memory (DMEM) - память данных. В архитектуре RISC-V играет ключевую роль хранения и управления данными, которые процессор обрабатывает во время выполнения программ. Data Memory используется для хранения переменных, массивов, структур и других видов данных. Процессор может выполнять операции чтения (load) и записи (store) данных. Эти операции позволяют загружать данные из памяти в регистры процессора и сохранять результат вычислений обратно в память.
  • Registers - регистровый файл. RISC-V содержит 32 регистра общего назначения. Все операции с ними производятся одинаково, за исключением нулевого (zero) регистра. Попытки записи в данный регистр игнорируются, а чтение всегда возвращает 0. Данная опция полезна, когда не требуется сохранять результат некоторых операций или есть необходимость иметь 0 в качестве операнда.
  • Несмотря на то, что регистры общего назначения могут использоваться без каких-либо ограничений, некоторые из них, согласно разработанному application binary interface RISC-V, имеют второе имя или алиас, по которому на них могут ссылаться в программном коде.

    В таблице 7.1 приводится соответствие номера регистра, его алиас (псевдоним) и назначение.

    Номер регистра Псевдоним Назначение
    x0 Zero Константа нуля
    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 Временные переменные

    Сохраняемые переменные (регистры) должны содержать неизменное значение до начала и по окончании вызываемой функции. Временные регистры могут изменять свои значения.

  • ALU (arithmetic logic unit) - арифметико-логическое устройство. АЛУ выполняет арифметические, логические операции, операции сравнения и сдвига. В АЛУ базового набора инструкций RV32I выполняется всего 10 операций (для выполнения данных инструкций в предыдущем разделе был спроектирован блок АЛУ в рамках практической работы).
  • Control (control unit) - блок управления. В данном случае в него включен и дешифратор инструкций (в разных источниках может отображаться отдельно сразу за IMEM). Блок управления декодирует инструкцию, поступающую из памяти инструкции, и коммутирует соответствующие блоки процессора посредством формирования управляющих сигналов записи/чтения (управления мультиплексорами). Помимо этого, блок анализирует результаты операций и управляет цепями ветвлений. Также этот блок координирует работу выполнения инструкции и является составной частью конвейера.
  • Control unit является цифровым автоматом и может быть реализован с применением микропрограммного автомата либо на комбинационной логике ( "жесткой логике ").

    Необходимо обратить внимание, что в процессоре RISC-V манипуляции с данными производятся только из регистрового файла. Для манипуляции с данными их предварительно необходимо загрузить из памяти в регистры регистрового файла, выполнить операцию, а затем если это предусмотрено кодом инструкции разместить обратно в памяти.

    В данной архитектуре обращение к памяти возможно только с помощью двух специальных команд: load (lw) и store (sw). В английской транскрипции данный метод организации работы с памятью называют Load/Store architecture.

    Команда load (загрузка) обеспечивает считывание данных из основной памяти и занесение их в регистр процессора (в команде обычно указывается адрес ячейки памяти и номер регистра)

    Пересылка информации в противоположном направлении производится командой store (сохранение).

    (рис 7.8)

    Набор инструкций RISC-V

    Подход создания базового набора инструкций с выбором необходимых расширений в RISC-V позволяет беспрепятственно развивать архитектуру без затруднений для обратной совместимости. Также обеспечивается возможность производить на единой архитектуре чипы с одинаковой эффективностью, предназначенные для различных сегментов, например, встраиваемых системы где делается упор на энергоэффективность и цену, либо пользовательские решения, где важна производительность.

    Как и в остальных архитектурах, операции в RISC-V кодируются в особый формат, однако (за исключением особого расширения сжатых инструкций), инструкции имеют всегда одинаковую длину кодировки (32 бит) вне зависимости от разрядности процессора. Такой подход существенно упрощает декодирование инструкций, работу с памятью и повторное использование частей расширения непосредственно в реализациях других процессоров.

    Ниже перечислены базовые инструкции работы процессора RISC-V RVI32, необходимые для собственной реализации ядра процессора:

    1. Арифметические инструкции

    ADD: сложение.

    Пример: ADD x3, x1, x2 - сложение значений в регистрах x1 и x2, результат записывается в x3.

    SUB: вычитание.

    Пример: SUB x3, x1, x2 - вычитание значения x2 из x1, результат записывается в x3.

    2. Логические инструкции

    AND: логическое И.

    Пример: AND x3, x1, x2 - побитовое И значений в регистрах x1 и x2, результат записывается в x3.

    OR: логическое ИЛИ.

    Пример: OR x3, x1, x2 - побитовое ИЛИ значений в регистрах x1 и x2, результат записывается в x3.

    XOR: исключающее ИЛИ.

    Пример: XOR x3, x1, x2 - побитовое исключающее ИЛИ, результат записывается в x3.

    3. Инструкции для работы с памятью

    LW: загрузка слова из памяти.

    Пример: LW x3, 0(x1) - загрузка 32-битного слова из адреса, основанного на значении в x1, в регистр x3.

    SW: запись слова в память.

    Пример: SW x3, 0(x1) - запись 32-битного слова из x3 по адресу, основанному на значении в x1.

    4. Управление потоком выполнения

    JAL: переход с сохранением адреса возврата.

    Пример: JAL x1, label - переход к метке label, адрес возврата сохраняется в x1.

    LUI: загрузка верхней части значения.

    Пример: LUI x1, 0x12345 - загрузка значения 0x12345000 в регистр x1.

    BEQ: условный переход (если равно)

    Пример: BEQ x1, x2, label - переход к метке label, если значения в x1 и x2 равны.

    JALR: переход по адресу в регистре с сохранением адреса возврата.

    Пример: JALR x1, 0(x5) # Переход к адресу, указанному в x5. Возврат по выполнении по адресу, сохраненному в x1.

    AUIPC: добавление адреса текущей инструкции.

    Пример: AUIPC x1, 0x10 - добавляет к адресу текущей инструкции значение 0x10, результат помещается в x1.

    BNE: условный переход (если не равно).

    Пример: BNE x1, x2, label - переход к метке label, если значения в x1 и x2 не равны.

    5. Инструкции для работы с непосредственными данными

    ADDI: сложение с немедленным значением.

    Пример: ADDI x3, x1, 10 - сложение значения в x1 с 10, результат записывается в x3.

    SLTI: сравнение меньше с немедленным значением.

    Пример: SLTI x3, x1, 10 - x3 будет равно 1, если x1 < 10, иначе 0.

    6. Инструкции для операций с битами

    SLL: сдвиг влево.

    Пример: SLL x3, x1, 2 - сдвиг значения в x1 на 2 бита влево, результат записывается в x3.

    SRL: логический сдвиг вправо.

    Пример: SRL x3, x1, 2 - логический сдвиг значения в x1 на 2 бита вправо.

    Все инструкции в RISC-V разделены на 6 типов:

  • R - операции АЛУ типа регистр - регистр;
  • I - операции АЛУ с непосредственным значением в команде;
  • S - операции загрузки/сохранения;
  • B - условная передача управления;
  • U - операции с расширенным непосредственным значением;
  • J - безусловная передача управления.
  • Форматы типов инструкций представлены на рисунке 7.9 и определены в "The RISC-V Instruction Set Manual " [6]. В приложении Б приведен перечень инструкции для базовой версии RV32I

    (рис 7.9)

    Рассмотрим пример кодировки инструкции add - инструкции суммирования содержимого двух регистров и записи результата в третий регистр. На рисунке 7.10 представлен пример кодировки соответствующей инструкции.

    (рис 7.10)

    Первая строчка отображает то, как будет записана типовая инструкция в данном расширении. Opcode - номер операции, который описан в документации на каждую инструкцию константным значением. В случае с инструкцией add это будет 0110011, rd - номер регистра назначения результата. Funct3 - указатель дешифратору операций, какую операцию реализовывать, на случай если opcode инструкций одинаковы, rs1 - номер первого операнда. rs2 - номер второго операнда. Оставшаяся часть заполняется нулями. При детальном рассмотрении типовой кодировки и кодировки конкретной инструкции можно заметить, что у нас отсутствует imm(произвольная константа), которая занимает 12 бит. Это связано с тем, что логика инструкции сделана так, что никаких констант не требуется. Освободившееся место использовано для второго операнда, положение которого не стандартизировано в типовой кодировке. Если бы у нас была константа, но не было нужды в номере регистра второго операнда, то операнд находился именно в поле с 31 по20-ый бит. Таким образом, при сложении r1 и r2 с записью результата в r3, кодировка будет иметь следующий вид: 0000000_00010_00001_000_00011_0110011.

    При изучении и в сравнении с другими системами ISA можно заметить, что процессоры RISC-V на уровне микроархитектуры в силу упрощения и повышения быстродействия не реализуют множество инструкций, характерных для других решений. Поэтому в компиляторы ассемблера заложены псевдо-инструкции, которые может применять программист, а они в свою очередь будут транслированы в инструкции, реализуемые процессором. В таблицу 7.2 сведены псевдо-инструкции, транслируемые в инструкции процессоров RISC-V.

    Псевдо-инструкция Инструкция RISC-V Описание
    nop addi zero,zero,0 нет операции
    mv rd, rs1 addi rd, rs, 0 Копирование между регистрами rs в rd
    not rd, rs1 xori rd, rs, -1 Запись в rd логической инверсии RS (все биты проходят операцию XOR с 1)
    neg rd, rs1 sub rd, x0, rs Запись в rd числа в обратном коде rs
    seqz rd, rs1 sltiu rd, rs, 1 Устанавливает результат в 1 если rs <1
    snez rd, rs1 sltu rd, x0, rs Устанавливает результат в 1 если rs не равен 0
    sltz rd, rs1 slt rd, rs, x0 Устанавливает результат в 1 если rs < 0
    sgtz rd, rs1 slt rd, x0, rs Устанавливает результат в 1 если rs >0
    beqz rs1, offset beq rs, x0, offset Переход если равно 0
    bnez rs1, offset bne rs, x0, offset Переход если не равно 0
    blez rs1, offset bge x0, rs, offset Переход если меньше или равно 0
    bgez rs1, offset bge rs, x0, offset Переход если больше или равно 0
    bltz rs1, offset blt rs, x0, offset Переход если меньше 0
    bgtz rs1, offset blt x0, rs, offset Переход если больше 0
    bgt rs, rt, offset blt rt, rs, offset Переход если больше
    ble rs, rt, offset bge rt, rs, offset Переход если меньше или равно
    bgtu rs, rt, offset bltu rt, rs, offset Условный переход сравнить два беззнаковых числа. Если rs >rt переход по адресу offset
    j offset jal x0, offset безусловный переход
    jr offset jal x1, offset Безусловный переход по адресу, адрес возврата в x1
    ret jalr x0, x1, 0 Возврат из функции

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

    # Начальные значения fib(0) = 0, fib(1) = 1
        add x1, x0, x0          # в x1 заносим 0 
        add x2, x0, x1          # в x2 заносим 1
        # Переменная n для хранения входящего значения
        add x3, x10, x0          # x3 = n
        # Проверка n (0 и 1)
        add x4, x0, x0          # x4 = 0                    ; fib(0)
        beq x3, x4, return_zero  # Если n == 0, переход к return_zero
        add x4, x0, x0          # x4 = 0                    ; переменная для счетчика
        addi x4, x4, 1          # x4 = 1                    ; стартовый индекс (fib(1))
        next_fib:
        add x5, x1, x2          # x5 = fib(n-1) + fib(n-2)
        add x1, x2, x0          # fib(n-1) = fib(n)
        add x2, x5, x0          # fib(n) = новый fib(n)
        addi x4, x4, 1          # увеличиваем счетчик
        bne x4, x3, next_fib    # если счетчик не равен n, переходим к next_fib
    finish:
        add x10, x2, x0          # возвращаем результат в a0
    return_zero:
        add x10, x0, x0          # возвращаем 0
    

    Организация и работа с памятью в RISC-V. Стековая память

    Стековая память в архитектуре RISC-V организуется аналогично другим архитектурам с учетом особенностей RISC-V. Как отмечалось выше основное назначение стека - временное хранение переменных при вызове функции, а также хранение адресов возврата из функций (подпрограмм).

    Стек растет в выделенной для него области памяти от старших адресов к младшим. При добавлении новых данных в стек адрес стека уменьшается.

    Для работы со стеком в файле регистров выделен регистр x2, имеющий алиас sp - указывает на вершину стека.

    В дополнение к sp может применятся регистр fp (frame pointer), который помогает отслеживать основание текущего фрейма функции, что облегчает доступ к локальным переменным и параметрам.

    Операции со стеком:

    При входе в функцию устанавливается фрейм указателя fp и выделяется место для локальных переменных (пример приведен в листинге 7.6, а работа показана на рисунке 7.11).

         addi sp, sp, -16; уменьшить указатель стека для выделения 16 байт
         sw ra, 12(sp); сохранить адрес возврата
         sw fp, 8(sp); сохранить предыдущий фрейм указателя
         addi fp, sp, 16; установить новый фрейм указателя
    
    (рис 7.11)

    При завершении работы функции указатели восстанавливаются, и стек возвращается в прежнее состояние (листинг 7.7).

        addi sp, sp, 16; восстановить указатель стека
         lw ra, 12(sp); восстановить адрес возврата
         lw fp, 8(sp); восстановить предыдущий фрейм указателя
         ret; возврат из функции
    

    Кэш память

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

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

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

    Кэш-память включает несколько уровней, каждый из которых имеет свои характеристики:

  • Уровень L1

    Разделение на кэш инструкций и кэш данных:

  • I-cache (кэш инструкций): Хранит инструкции, которые процессор будет выполнять. Обычно реализуется с невысокой латентностью доступа.
  • D-cache (кэш данных): Хранит данные, необходимые для выполнения программ.
  • Размер: Обычно L1-кэш имеет небольшой размер, диапазон обычно от 16 KB до 128 KB. Этот уровень кэша имеет очень высокую скорость доступа.

  • Уровень L2

    Единый или разделённый L2-кэш может быть либо единым для инструкций и данных, либо разделённым на I-cache и D-cache.

    Объем больший по сравнению с L1-кэшем и может варьироваться от 256 KB до нескольких МБ. Он медленнее чем L1, но все ещё намного быстрее, чем доступ к основной памяти.

  • На рисунке 6.12 приведена пример схемы кэш-памяти.

    (рис 7.12)

    Ассоциативность кэша определяет, сколько мест (строк) может использоваться для хранения определённого блока данных.

    Прямой кэш: каждый блок памяти может быть помещён только в одно определённое место в кэше. Это простейшая и наиболее быстрая организация.

    Ассоциативный кэш: позволяет хранить блоки данных в нескольких местах. Например, 4-х ассоциативный кэш может разместить данные в четырёх различных местах, что снижает вероятность конфликтов при хранении.

    Полностью ассоциативный кэш: данные могут храниться в любом месте кэша, что повышает гибкость, но требует сложных алгоритмов поиска.

    Устройство кэш-строк:

  • тег: уникальный идентификатор, относящийся к блоку данных в основной памяти. Используется для определения принадлежности данных к конкретному блоку.
  • данные: хранимые данные.
  • контрольная информация: например, биты валидности (valid bits) и биты "изменения" (dirty bits), которые показывают, были ли данные изменены.
  • Существуют следующие алгоритмы кэширования

  • Замещение: когда кэш заполнен и необходимо загрузить новый блок, используется алгоритм замещения:

  • LRU (Least Recently Used): Удаляет наименее использованные блоки.
  • FIFO (First In First Out): Удаляет самые старые блоки.
  • Random: Случайным образом выбирает блок для удаления.
  • Запись: при записи данных в кэш могут использоваться различные стратегии:

  • Write-Through - данные сразу записываются как в кэш, так и в основную память. Это гарантирует целостность данных, но может быть медленнее.
  • - Write-Back - данные записываются в кэш, а в основную память только при необходимости (например, при замещении). Это может повысить производительность, но потребует дополнительной логики для поддержания согласованности данных.
  • Кэш-память основывается на двух основных принципах локальности:

  • Локальность по времени. Если данные были использованы в последнее время, они с большой вероятностью будут использованы снова.
  • Локальность по пространству. Если данные были использованы, то данные, находящиеся рядом в с ними в памяти также имеют высокую вероятность быть использованными.
  • При разработке собственных решений на архитектуре RISC-V в зависимости от проекта применяется создание блока кэш-памяти. Относительно ядра процессора кэш будет внешним устройством и внедрение потребует создание блока контроллера кэш - памяти. На рисунке 7.13 приведен пример одной из разработок для RISC-V на FPGA с блоком кэш-памяти [7]. Для приведенного блока разработчиком определены следующие характеристики:

  • объем кэш памяти: 512 байт;
  • размер блока кэш-памяти: 16 байт;
  • адрес памяти: 10 бит;
  • смещение: 2 бита;
  • индекс данных: 5 бит;
  • тэг: 3 бита.
  • (рис 7.13)

    Тактирование процессора

    Тактирование процессора определяет порядок его функционирования, обеспечивая синхронность процессов, выполнения операций и управления работой всех блоков.

    Цикл тактирования время между двумя последовательными пиками тактового сигнала. Это время, за которое процессор может выполнить одно или несколько действий.

    Критически длинный путь - время, отводимое на исполнение самой долгой инструкции.

    В большинстве процессоров, включая RISC-V, выполнение инструкций делится на несколько этапов, каждый из которых обычно занимает один тактовый цикл. Эти этапы, как было отмечено в начале раздела, могут включать:

  • Выборка инструкции (Fetch). Процессор считывает следующую инструкцию из памяти. Используется счетчик инструкций (PC), указывающий на адрес инструкции.
  • Декодирование (Decode). Инструкция декодируется для определения, какие действия следует выполнить и какие регистры или ячейки памяти необходимы. Активируются связи для взаимодействия операндов.
  • Исполнение (Execute). Выполняются операции, указанные в инструкции, часто с использованием арифметико-логического устройства (ALU).Например, в случае арифметических операций производится математическое вычисление.
  • Запись результата (Write Back). Результат выполнения операции записывается обратно в регистры или память.
  • Выделяют однотактные, многоактные и конвейерные процессоры.

    Однотактный (single-cycle) - процессор выполняет всю команду за один такт. Принцип работы легко объяснить, а устройство управления довольно простое. Из-за того, что все действия выполняются за один такт, эта микроархитектура не требует дополнительных регистров, требуемых для работы процессора, но недоступных для использования программистом). Однако, длительность такта ограничена самой медленной командой, использующей самый длинный критический путь.

    Основным преимуществом однотактной микроархитектуры является простота понимания ее работы. К минусам можно отнести: относительно высокие аппаратные затраты из-за использования дополнительных сумматоров и раздельной основной памяти (гарвардская архитектура), низкая тактовая частота из-за длинного критического пути, так как разные инструкции проходят разный путь, скорость работы ограничена скоростью самой медленной инструкции.

    Многотактный процессор (multi-cycle) - выполняет команду за несколько более коротких тактов. Простым командам нужно меньше тактов, чем сложным. Вдобавок, многотактная микроархитектура уменьшает количество необходимой аппаратуры путем повторного использования таких "дорогих " блоков, как сумматоры и блоки памяти.

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

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

    Приведенный на рисунке 7.8 процессор, который проектируется в данном курсе является однотактным. В дополнительной литературе [1] приводятся примеры многотактных процессоров и организации конвейера.

    Приложение А

    1. Сокращенная система команд Intel x86 (IA32)

    Арифметические операции:

    ADD - сложение.

    Пример: add eax, ebx; сложить EAX и EBX результат в EAX

    SUB - вычитание.

    MUL - умножение (беззнаковое).

    IMUL - умножение (знаковое).

    DIV- деление (беззнаковое).

    IDIV- деление (знаковое).

    INC - инкремент, увеличение на 1.

    DEX - декремент уменьшение на 1.

    Пример: dec ecx; Уменьшение счетчика ecx на 1

    Логические операции:

    AND - логическое "И".

    OR - логическое "ИЛИ".

    XOR - исключающее "ИЛИ".

    Пример: xor ebx, ebx; Очистить ebx

    NOT- логическое отрицание.

    Сравнения и переходы:

    CMP - сравнение с установкой флага 0

    Пример: cmp ecx, 0; сравнение содержимого регистра ecx с 0 в результате совпадения флаг 0 - ZF регистра состояний установится в 0;

    JNE (JNZ)-переход, если не равно.

    Пример: dec ecx; jnz .loop; Если ecx не достиг 0 вернуться на начало

    JE - переход, если равно.

    Пример: cmp ecx, 0 je .done; Если в ходе предыдущей операции ecx=0, то перейти по адресу метки done

    JL - переход, если меньше.

    JG - переход, если больше.

    Передача управления:

    CALL - вызов процедуры.

    Пример: call printf; вызвать подпрограмму по адресу метки

    RET - возврат из процедуры.

    JMP - безусловный переход.

    Загрузки и сохранения:

    MOV - перемещение данных.

    Пример: mov ebx, edx; записать содержимое регистра edx в ebx

    MOVZX - считать содержимое источника как байт или слово с заполнением 0 до размера операнда.

    Пример: movzx ecx, byte [n]; загрузить байт с заполнением нулями до размера ecx

    PUSH - помещение значения в стек.

    Пример: push eax; поместить в стек eax

    POP - извлечение значения из стека.

    Пример: pop eax; восстановить из стека eax

    LOAD - загрузка из памяти (в основном через MOV).

    Управление прерываниями:

    INT - вызов прерывания.

    Пример int 0x80; вызов прерывания операционной системы Linux

    IRET - возврат из прерывания.

    Системные команды (для работы с процессором и памятью):

    HLT - остановка процессора.

    NOP -нет операции (пустая команда).

    2. Система команд Atmega328

    Арифметические команды:

    ADD - сложение без переноса

    Пример: ADD r16, r17; сложение регистров r16 и r17 с занесением результата в r16

    ADC - сложение с учетом переноса.

    Пример: ADC r16, r17; сложение регистров r16 и r17 с занесением результата в r16 и формирование знака переноса

    SUB - вычитание.

    SBC - вычитание с учетом переноса.

    MUL - умножение.

    DIV - деление.

    INC - инкремент, увеличение регистра на 1.

    Пример: inc r20; Инкремент содержимого r20

    DEC - декремент, уменьшение регистра на 1.

    Логические команды:

    AND - логическое И.

    Пример: and r2, r3 ; Поразрядное and r2 и r3, результат поместить в r2

    OR - логическое ИЛИ.

    XOR - логическое исключающее ИЛИ.

    Команды сдвига и поворота:

    LSL - логический сдвиг влево.

    LSR - логический сдвиг вправо.

    ROR - циклический сдвиг вправо.

    ROL - циклический сдвиг влево.

    Команды сравнения:

    CPI - сравнение с немедленным значением.

    CP - сравнение двух регистров.

    Пример: cp r20, r16; Сравнить регистры r20 и r16

    Управление переходами:

    JMP - безусловный переход.

    CALL - вызов подпрограммы.

    RET - возврат из подпрограммы.

    BRNE, BEQ, BGE, BLT и другие - условные переходы.

    Пример: brne <метка перехода > перейти по метке если прядущее значение равно 0

    cpi r27, 5; Сравнить r27 с 5

    brne loop ; Перейти если r27 < > 5

    Команды работы с вводом-выводом:

    MOV - запись между регистрами

    Пример: mov r18, r19; Копировать r19 в r18

    IN - чтение данных из порта ввода.

    Пример: in r25, $16; Считать порт B

    OUT - запись данных в порт вывода.

    Команды работы с памятью:

    LD - загрузка данных из SRAM или EEPROM.

    LDI - загрузка непосредственного значения в регистр

    Пример: LDI R16, 10; Загрузить в регистр R16 значение 10

    ST - сохранение данных в SRAM.

    Команды работы с прерываниями:

    SEI - разрешение прерываний.

    CLI - отключение прерываний.

    RET - возврат из обслуживания прерывания.

    Подробное описание инструкций на русском языке [8].

    3. Общая система команд для процессоров ARM

    Команды ввода вывода

    LOAD (LD) - загрузка данных из памяти в регистр.

    Примеры:

    LDR r0, [r1]; Загружает значение из памяти по адресу R1 в регистр R0

    ldr r0, =0 ; Заносит в регистр r0 значение по ссылке

    STORE (STR) - сохранение данных из регистра в память.

    Пример: STR r0, [r2] ; Сохраняет значение r0 в память по адресу хранящемуся в r2

    Арифметические и логические команды

    ADD - сложение двух регистров.

    Пример: ADD R2, R0, R1; Сложить r0 и r1 результат записать r2

    SUB - вычитание.

    AND, ORR, EOR - логические операции.

    Пример: AND R4, R2, R3; Выполнить побитно операцию И над содержимым регистров r2 и r3 результат записать в r4

    MUL - умножение.

    SDIV, UDIV - деление знаковых и беззнаковых чисел.

    Примеры: MUL R4, R0, R1; Умножить содержимое регистров r0 и r1, результат поместить в r4; SDIV R5, R4, R2; Разделить содержимое регистра r4 на r2, результат в r5 (знаковое деление)

    Команды управления переходами

    B - безусловный переход.

    BL - переход с вызовом подпрограммы.

    BX - переход на адрес, расположенный в регистре.

    Примеры: B label; Переход к метке label. BL function; Вызов функции. BX R3; Переход на адрес, находящийся в r3

    Команды управления памятью

    PUSH - сохранить регистры в стек.

    POP - извлечь регистры из стека.

    Примеры: PUSH {R0, R1}; Сохраняет R0 и R1 в стек. POP {R2, R3}; Извлекает значения из стека в R2 и R3

    Подробное описание инструкций на русском языке [9].

    Приложение Б

    (рис 7.14)

    Литература

  • Орлов С.А. Цилькер Б.А. Организация ЭВМ и систем: Учебник для ВУЗов. 3-е изд. СПб.2014.-688с.:ил.
  • Ерохин В.В. Микропроцессоры. Теория и практика проектирования, Солон-Пресс, 2023. - 256с.:ил.
  • Структурная схема процессора Intel Pentium
  • Статья An Introduction to ARM Architecture with Each Module's Working Principle
  • Статья A microarchitectural analysis of soft error propagation in a production-level embedded microprocessor
  • Инструкция консорциума RISC-V The RISC-V Instruction Set Manual
  • Репозиторий с процессором RISC-V и модулем кэш-памяти
  • Справочные данные по системе команд Atmega 328
  • Справочные данные по системе команд ARM
  • Дополнительная литература

  • С. Харрис, Д. Харрис. Цифровая схемотехника и архитектура компьютера RISC-V/ пер. с англ. В.С. Яценкова, А.Ю. Романова; под. ред. А.Ю. Романова.-М.: ДМК Пресс, 2021. - 810 с.: ил.
  • С. Харрис, Д. Харрис. Цифровая схемотехника и архитектура компьютера. Дополнение по архитектуре АРМ. ДМК Пресс,2019. - 356с.:ил.
  • Смит Б. Ассемблер для Raspberry Pi. Практическое руководство. - СПб.: БХВ-Петербург, 2022. - 320с.:ил.
  • Вернуться к учебному плану