# Лекция 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реакции Это время, между запросом на прерывание и переходом к прерывающей программе (см. рис. 2) - **Затраты времени на переключение программ**, Tзагрузки и Tвостановления Это время, которое требуется программе выполнить сервисные инструкции (см. рис. 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, то на соответствующий выход y1 будет подана 1, которая будет говорить о том, что именно первое устройство сделало запрос на прерывание. На выход `INT` также будет передана 1, которая отправится процессору для оповещения о прерывании. На остальные же выходы y2...yn будет передан 0. Т.е. на выходе y1...yn будет получена только одна 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) на видеозапись лекции ## Дополнительные материалы к лекции для саморазвития