# Лекция 14. Подсистема прерывания

## Содержание

- [Лекция 14. Подсистема прерывания](#Лекция-14-подсистема-прерывания)
  - [Содержание](#Содержание)
  - [Определение](#Определение)
    - [Сканирование-входов](#Сканирование-входов)
  - [Передача управления при прерывании](#Передача-управления-при-прерывании)
  - [Классификация событий прерывания](#Классификация-событий-прерывания)
  - [Основные характеристики прерываний](#Основные-характеристики-прерываний)
  - [Допустимые моменты прерывания программы](#Допустимые-моменты-прерывания-программы)
    - [Маски́рование прерывания](#Маскирование-прерывания)
  - [Способы выявления прерывания](#Способы-выявления-прерывания)
  - [Схемы реализации контроллера прерываний](#Схемы-реализации-контроллера-прерываний)
    - [Контроллер прерывания](#Контроллер-прерывания)
  - [Control and Status Register RISC-V](#control-and-status-register-risc-v)
  - [Подсистема прерываний для RISC-V](#Подсистема-прерываний-для-risc-v)
  - [Программа, реализующая прерывания на ассемблере](#Программа-реализующая-прерывания-на-ассемблере)
  - [Основные материалы лекции](#Основные-материалы-лекции)
  - [Дополнительные материалы к лекции для саморазвития](#Дополнительные-материалы-к-лекции-для-саморазвития)

## Определение

**Прерывание** — событие, на которое реагирует процессор.

_Пример_: Перемещение мыши. Если бы не было прерываний, то процессор был бы вынужден постоянно обращаться к мыши с целью узнать: изменилось ли её положение или нет, стоит ли перерисовать курсор. А это очень ресурсоёмко. Такой подход называется [сканирование входов](#Сканирование-входов).

### Сканирование-входов

**Сканирование входов** — последовательный программный опрос входных сигналов. Этот подход используется в суперкомпьютерах, т.к. у них неразвитая периферия. В таком случае подсистема прерываний не нужна.

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

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

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_01.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_01.jpg)

*Рис. 1. Схема передачи управления при прерывании.*

## Классификация событий прерывания

- **Прерывания** — события, сигнал о которых приходит извне. Это события происходит _асинхронно_  фронту тактового импульса. Пример: перемещение мыши.
  - Маски́руемые — прерывания, которые процессор может игнорировать. _Например_: нажатие клавиши на клавиатуре. [Аппаратная реализация]( #маски́рование-прерывания) будет продемонстрирована чуть позже.
  - Немаски́руемые — прерывания, которые процессор не может игнорировать. _Например_: сигнал от датчика температуры о перегреве процессора.  
- **Исключения** — события, происходящие вследствие выполнения какой-то инструкции. Эти события происходят _синхронно_  фронту тактового импульса.
  - Ошибки. _Например_: обращение в несуществующую область памяти.
  - Ловушки. _Например_: деление на 0.
  - Отказы. _Например_: ситуации, которые никак нельзя устранить. Зачастую приводят к выключению/перезапуску процессора.

## Основные характеристики прерываний

- **Время реакции прерывания**, T<sub>реакции</sub>

  Это время, между запросом на прерывание и переходом к прерывающей программе (см. рис. 2)

- **Затраты времени на переключение программ**, T<sub>загрузки</sub> и T<sub>востановления</sub>

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

- **Эффективность прерывания**

  Это время эффективной работы программы. Т.е. отношение "полезных" (не сервисных) инструкций программы прерывания ко всем инструкциям прерывания.

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_02.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_02.jpg)

*Рис. 2. Характеристики прерывания.*

- **Глубина прерывания**

    Это кол-во прерываний, которые могут произойти во время обработки прерывания (см. рис. 3). Например: если процессор, находясь внутри прерывания, не способен прерваться на другое, то говорят что глубина прерывания n=1. Также программы могут неограниченно прерывать друг друга, в таком случаем, есть ограничение по памяти.

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_03.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_03.jpg)

*Рис. 3. Глубина прерываний.*

## Допустимые моменты прерывания программы

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

- **Метод помеченного оператора**

    Внутри какой-нибудь инструкции добавляется бит, отвечающий за прерывание. Допустим, если этот бит равен 1 и есть сигнал на прерывание, то процессор запустит обработчик прерывания.

- **Покомандный метод**

    Этот метод относится к [многотактным архитектурам](09.%20Multicycle%20processor.md). Процессор начинает реагировать на сигнал прерывания, только после завершения инструкции.

- **Метод быстрого реагирования**

    Этот метод позволяет реагировать на прерывание на любом такте.

### Маскирование прерывания

  **Маска прерывания** — это регистр, отвечающий за разрешение на прерывание (см. рис. 4). Эта маска побитово перемножается с запросами на прерывание других устройств/модулей. Если в результате перемножения получилась хотя бы одна 1, то формируется сигнал `interrupt`, который подаётся на блок управления процессора.

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_04.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_04.jpg)

*Рис. 4. Аппаратная реализация маски́рования прерывания.*

## Способы выявления прерывания

- **Обзорная система прерывания**

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

- **Векторная система прерывания**

    В векторной системе есть участок памяти — _таблица векторов прерывания_. В таблице лежат **вектора прерывания** — адрес начала подпрограммы обработчика прерывания. После обращения к таблице будет запущена нужная подпрограмма. Работает такая система быстрее, но реализовать её сложнее.

## Схемы реализации контроллера прерываний

[**Контроллер прерывания**](#Контроллер-прерывания) — устройство, которое отвечает за передачу сигнала прерывания процессору и формирование кода причины прерывания.

- **Цепочечная схема**

    От процессора идет сигнал подтверждения `ПДТ`, который приходит только тогда, когда процессор готов обработать какое-то прерывание (см. рис. 5.). Этот сигнал приходит на вход первого устройства, которое, в случае необходимости, выдаёт процессору сигнал на прерывание и вектор прерывания. Если же в прерывании от первого устройства нет необходимости, то сигнал подтверждения передаётся на следующее устройство и т.д. Цепочечная схема проста в реализации, но очень медленная. К тому же в этой схеме некоторые устройства имеют приоритет над другими в очерёдности обработки запроса на прерывание. Также существует вероятность, что до последних устройств никогда не дойдёт сигнал подтверждения, и процессор не обработает их запрос на прерывание.

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_05.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_05.jpg)

*Рис. 5. Цепочечная схема.*

- **Схема с циклическим опросом**

    Каждый раз, когда в счётчик `СТ` приходит сигнал `C` (тактирование), он увеличивает своё значение (см. рис. 6). Затем это значение подаётся на вход дешифратору `DC`, который только на одном выходе выдаёт 1. И каждый такт меняется номер выхода от 0 до N. Если во время обхода всех выходов появится сигнал на прерывание, то этот сигнал отправится на RS триггер, который затем отправит `interrupt` сигнал процессору. А на вход `C` счётчика `CT` будет отправлен 0. В таком случае счётчик перестанет суммировать, и он будет иметь значение кода номера прерывания.

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_06.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_06.jpg)

*Рис. 6. Циклическая схема.*

- **Дейзи-цепочка**

    Рассмотрим дейзи-цепочку (см. рисунок 7). Эта схема будет работать только тогда, когда вход `Приоритет` равен 1. Если на первый запрос пришла 1, то на соответствующий выход y<sub>1</sub> будет подана 1, которая будет говорить о том, что именно первое устройство сделало запрос на прерывание. На выход `INT` также будет передана 1, которая отправится процессору для оповещения о прерывании. На остальные же выходы y<sub>2</sub>...y<sub>n</sub> будет передан 0. Т.е. на выходе y<sub>1</sub>...y<sub>n</sub> будет получена только одна 1 от запроса с наивысшим приоритетом. Дейзи-цепочка часто используется в определении наиболее приоритетного запроса.  

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_07.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_07.jpg)

*Рис. 7. Дейзи-цепочка.*

### Контроллер прерывания

Теперь рассмотрим, как выглядит контроллер прерывания. (см. рис. 8) По каналу `ПДТ` поступает сигнал от процессора, разрешающий прерывание. Этот сигнал поступает на регистры запроса прерывания. Затем с помощью [маски прерывания](#маски́рование-прерывания) определяются, каким устройствам разрешён запрос на прерывание. Сигналы на прерывания попадают в `схему определения наиболее приоритетного запроса` (_например_: дейзи-цепочка), где определяется самый приоритетный запрос.  С помощью `формирователя номера запроса` формируется некоторое число, отвечающее за степень приоритета запроса. В `схеме сравнения приоритетов` сравнивается это число с приоритетом текущего прерывания (приоритет текущего прерывания записан в регистры `порога прерывания`). Если число, хранимое в регистрах "_порога прерывания_" меньше, то  в `порог прерывания` записывается новое число, а на процессор по каналу `ЗП` пойдёт сигнал о новом прерывании.

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_08.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_08.jpg)

*Рис. 8. Схема контроллера прерывания.*

## Control and Status Register RISC-V

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

| Номер       | Привилегия | Имя       | Описание                                                   |
|:------------|:-----------|:----------|:-----------------------------------------------------------|
| 0x304       | MRW        | mie       | Регистр маски                                              |
| 0x305       | MRW        | mtvec     | Регистр вектора прерывания                                 |
| 0x340       | MRW        | mscratch  | Регистр указателя на стек прерывания                       |
| 0x341       | MRW        | mepc      | Регистр адреса инструкции, на котором случилось прерывание |
| 0x342       | MRW        | mcause    | Регистр причины (кода) прерывания                          |

 Инструкции для работы с CSR:

| Opcode    | func3 | Тип  | Инструкция          |  Описание                 | Операция                |
|:----------|:------|:-----|:--------------------|:--------------------------|:------------------------|
| 1110011   | 000   |  I   | mret                | Возврат и прерывание      | PC=mepc                 |
| 1110011   | 001   |  I   | csrrw rd, csr, rs1  | Чтение/запись CSR         | rd = csr, csr=rs1       |
| 1110011   | 010   |  I   | csrrs rd, csr, rs1  | Чтение/установка бит CSR  | rd = csr, csr=csr\|rs1  |
| 1110011   | 011   |  I   | csrrc rd, csr, rs1  | Чтение/очистка бит CSR    | rd=csr, csr=csr&~rs1    |

## Подсистема прерываний для RISC-V

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_09.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_09.jpg)

*Рис. 9. Микроархитектура RISC-V с CSR.*

Теперь рассмотрим отдельно блок CSR:

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_10.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_10.jpg)

*Рис. 10. Схема CSR.*

Рассмотрим схему CSR (см. рис. 10). На ней красным прямоугольником (номер 1) отмечены регистры CSR. К нему подключены мультиплексор и демультиплексор для считывания и записи данных с регистров (номер 2 и 3). С помощью входа `A`, определяется к какому регистру будет применена запись/чтение. Теперь рассмотрим нижнюю часть схемы (см. рис. 11):

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_11.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_11.jpg)

*Рис. 11. Нижняя часть схемы CSR.*

Она нужна для оперативного обновления значений регистров `mepc` и `mcause`. Вход `OP[2]`  — это сигнал о возникновении прерывания. Если сигнал на прерывание равен 1, то значения `mepc` и `mcause` изменятся. Осталось рассмотреть верхнюю часть схемы (см. рис. 12):

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_12.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_12.jpg)

*Рис. 12. Верхняя часть схемы CSR.*

Она нужна для реализации инструкций для работы с CSR. В зависимости от нужной инструкции у выхода `WD` будет своё значение. На нулевой вход мультиплексора поступают нули, если не требуется запись в регистры.
Отдельно стоит отметить, что значение регистра причины прерывания `mcause` берётся из контроллера прерывания. А значение регистра маски прерывании `mie`, которое мы устанавливаем в блоке CSR, отправляется в контроллер прерывания. Давайте рассмотрим устройство контроллера прерывания. У него будет [схема с циклическим опросом](#Схемы-реализации-контроллера-прерываний) (см. рис. 13):

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_13.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_13.jpg)

*Рис. 13. Контроллер прерывания с циклическим опросом.*

На вход в контроллер прерывания подаются регистры маски прерывания `mie[31:0]`, запросы на прерывание `int_req[31:0]`. Если бит маски и бит запроса будут равны 1 и если в этот момент времени счётчик (красный прямоугольник на рисунке 13) будет соответствовать данному запросу на прерывание, то счётчик будет заблокирован, и будет сформирован сигнал `INT`, который сообщит о прерывании. Значение регистра `mcause` будет соответствовать значению остановленного счётчика.

Такое состояние будет, пока не придёт сигнал с `INT_RST`. Также после получения сигнала `INT_RST` периферийному устройству, чей сигнал обрабатывался, будет передана 1 через регистр `int_fin`, сигнализирующий о том, что обработка прерывания окончена.

В целом схема подключения выглядит так (см. рис. 14):

![../.pic/Lectures/14.%20Interrupt%20subsystem/fig_14.jpg](../.pic/Lectures/14.%20Interrupt%20subsystem/fig_14.jpg)

*Рис. 14. Схема подключения подсистемы прерывания.*

## Программа, реализующая прерывания на ассемблере

Программа обрабатывает два прерывания, имеющие коды 5 и 19. При пятом прерывании некоторое число из памяти делится на 2. При девятнадцатом прерывании к некоторому числу из памяти прибавляется 3.

```assembly
# Инициализируем начальные значения регистров
    li    sp, 0xFFFFFFFC # устанавливаем указатель на верхушку стека
    li    gp, 0x10000000 # устанавливаем указатель на глобальные данные
 
    li    t0, 0x00080020 # подготавливаем маску прерывания для 5 и 19 входов
    csrw  mie, t0  # загружаем маску в регистр маски
    la    t0, interrupt  # аналогично li загружает число, в данном случае - адрес
    csrw  mtvec, t0  # устанавливаем вектор прерывания
    li    t0, 0xEFFFFFFC # готовим адрес верхушки стека прерывания
    csrw  mscratch, t0  # загружаем в указатель на верхушку стека прерывания
 
    li    t0, 1   # начальное значение глобальной переменной
    lw    t0, 0(gp)  # загружаем переменную в память
 
while:    # бесконечный цикл, аналогичный while (1);
    beq x0, x0, while  # ничего не делаем!

interrupt:
    csrrw t0, mscratch, t0  # меняем местами mscratch и t0
    sw    t1, 0(t0)   # сохраняем t1 на стек mscratch
    sw    t2, 4(t0)   # сохраняем t2 на стек mscratch

  # Переключаем адрес возврата на следующую инструкцию
    csrr  t2, mepc   # t2 = pc в момент прерывания
    addi  t2, t2, 4   # увеличиваем pc на 4
    csrw  mepc, t2   # mepc = mepc + 4

# Проверяем регистр причины и на 5-ое прерывание
    csrr  t1, mcause   # t1 = mcause
    li    t2, 5   # t2 = 5 (код одного из прерываний)
    bne   t1, t2, nineteen  # если это не 5 прерывание, то проверяем 19
  # Обработчик 5-го прерывания
    lw    t2, 0(gp)  # загружаем переменную из памяти
    addi  t2, t2, 3  # прибавляем к значению 3
    sw    t2, 0(gp)  # возвращаем переменную в память
    j     done    # идем возвращать регистры и на выход
 
nineteen: # Проверяем на 19-ое прерывание
    li    t2, 19   # t2 = 19 (код  другого прерывания)
    bne   t1, t2, done   # если не 19-ое, то выходим
  # Обработчик 19-го прерывания
    lw    t2, 0(gp)  # загружаем переменную из памяти
    srli  t2, t2, 1  # делим число пополам сдвигом вправо
    sw    t2, 0(gp)  # возвращаем переменную в память
    j     done   # идем возвращать регистры и на выход
 
# Возвращаем регистры на места и выходим
done:
    lw    t1, 0(t0)   # возвращаем t1 со стека
    lw    t2, 4(t0)   # возвращаем t2 со стека
    csrrw t0, mscratch, t0  # меняем обратно местами t0 и mscratch
    mret    # возвращаем управление программе (pc = mepc)

```

## Основные материалы лекции

1. [Ссылка](https://youtu.be/E21EtIasiU8?si=f_UEatkkQUow9iX5) на видеозапись лекции

## Дополнительные материалы к лекции для саморазвития
