diff --git a/.pic/Labs/lab_10_irq/fig_02.drawio.png b/.pic/Labs/lab_10_irq/fig_02.drawio.png index 64cf4bff..e03c71a5 100644 Binary files a/.pic/Labs/lab_10_irq/fig_02.drawio.png and b/.pic/Labs/lab_10_irq/fig_02.drawio.png differ diff --git a/.pic/Labs/lab_10_irq/fig_04.drawio.png b/.pic/Labs/lab_10_irq/fig_04.drawio.png new file mode 100644 index 00000000..cbdc2019 Binary files /dev/null and b/.pic/Labs/lab_10_irq/fig_04.drawio.png differ diff --git a/.pic/Labs/lab_10_irq/fig_04.png b/.pic/Labs/lab_10_irq/fig_04.png deleted file mode 100644 index 442c67b7..00000000 Binary files a/.pic/Labs/lab_10_irq/fig_04.png and /dev/null differ diff --git a/.pic/Labs/lab_10_irq/fig_05.drawio.png b/.pic/Labs/lab_10_irq/fig_05.drawio.png deleted file mode 100644 index 1be2d34b..00000000 Binary files a/.pic/Labs/lab_10_irq/fig_05.drawio.png and /dev/null differ diff --git a/.pic/Labs/lab_11_irq_integration/fig_01.drawio.png b/.pic/Labs/lab_11_irq_integration/fig_01.drawio.png index 36aa7d61..2c25bcc2 100644 Binary files a/.pic/Labs/lab_11_irq_integration/fig_01.drawio.png and b/.pic/Labs/lab_11_irq_integration/fig_01.drawio.png differ diff --git a/Labs/10. Interrupt subsystem/README.md b/Labs/10. Interrupt subsystem/README.md index 75b045f8..4bfaf05a 100644 --- a/Labs/10. Interrupt subsystem/README.md +++ b/Labs/10. Interrupt subsystem/README.md @@ -82,7 +82,30 @@ _Рисунок 1. Распределение привилегий по уров _Таблица 1. Регистры контроля и состояния машинного (наивысшего) уровня привилегий_ -Для работы с CS-регистрами используются специальные инструкции **SYSTEM** (1110011) I-типа, хранящие в 12-битном поле **imm** адрес регистра, к которому будет осуществлен доступ и адреса в регистровом файле откуда будет считан или куда будет записан один из CS-регистров . Вы уже добавляли поддержку этих инструкций во время выполнения лабораторной работы №5 "Основной дешифратор". +Для работы с CS-регистрами используются специальные инструкции **SYSTEM** (1110011) I-типа, хранящие в 12-битном поле **imm** адрес регистра, к которому будет осуществлен доступ и адреса в регистровом файле откуда будет считан или куда будет записан один из CS-регистров . Вы уже добавляли поддержку этих инструкций во время выполнения [лабораторной работы №5](../05.%20Main%20decoder/) "Основной дешифратор". + +| 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 | +|1110011 | 101 | I | csrrwi rd, csr, rs1 | Чтение и Запись CSR | rd = csr, csr = imm | +|1110011 | 110 | I | csrrsi rd, csr, rs1 | Чтение и Установка бит CSR| rd = csr, csr = csr \| imm | +|1110011 | 111 | I | csrrci rd, csr, rs1 | Чтение и Очистка бит CSR | rd = csr, csr = csr & ~imm | + +_Таблица 2. Список инструкций для работы с регистрами контроля и статуса_ + +Для удобства программирования на языке ассемблера RISC-V существуют псевдоинструкции для работы с CS-регистрами. + +| Псевдоинструкция | Инструкция RISC-V | Описание | Операция | +|------------------|--------------------|-------------|-----------| +| csrr rd, csr | csrrs rd, csr, x0 | Чтение CSR | rd = csr | +| csrw csr, rs1 | csrrw x0, csr, rs1 | Запись CSR | csr = rs1 | + +_Таблица 3. Псевдоинструкции для работы с регистрами контроля и статуса_ + +Операция логического ИЛИ нулевого регистра с содержимым CS-регистра не меняет его содержимого, поэтому при использовании инструкции `csrr` происходит только операция чтения. Подобным образом реализована псевдоинструкция `csrw`. Для реализации простейшей системы прерывания на процессоре с архитектурой RISC-V достаточно реализовать 5 CS-регистров работающих в машинном, самом привилегированном режиме. @@ -95,7 +118,7 @@ _Таблица 1. Регистры контроля и состояния ма |0x341 | MRW | mepc | Регистр, хранящий адрес перехваченной инструкции. | |0x342 | MRW | mcause | Причина перехвата | -_Таблица 2. Список регистров, подлежащих реализации в рамках лабораторной работы_ +_Таблица 4. Список регистров, подлежащих реализации в рамках лабораторной работы_ По адресу `0x304` должен располагаться регистр, позволяющий маскировать прерывания. Например, если на 5-ом входе системы прерывания генерируется прерывание, то процессор отреагирует на него только в том случае, если 5-ый бит регистра `mie` будет равен 1. @@ -107,11 +130,13 @@ _Таблица 2. Список регистров, подлежащих реа ![../../.pic/Labs/lab_10_irq/tab_03.png](../../.pic/Labs/lab_10_irq/tab_03.png) -_Таблица 3. Кодирование причины прерывания в регистре `mcause`_ +_Таблица 5. Кодирование причины прерывания в регистре `mcause`_ + +Нас интересуют части, выделенные красным. В первую очередь то как кодируется старший бит регистра `mcause`. Он зависит от типа причины перехвата (`1` в случае прерывания, `0` в случае исключения). Оставшиеся 31 бит регистра отводятся под коды различных причин. Поскольку мы создаем учебный процессор, который не будет использован в реальной жизни, он не будет поддерживать большую часть прерываний/исключений (таких как невыровненный доступ к памяти, таймеры и т.п.). В рамках данного курса мы должны поддерживать исключение по нелегальной инструкции (код 0x02) и должны уметь поддерживать прерывания периферийных устройств (под которые зарезервированы коды начиная с 16-го). Процессор будет поддерживать только один источник прерывания, поэтому для кодирования причины прерывания нам потребуется только первый код из диапазона _"Designated for platform use"_. -Нас интересуют части, выделенные красным. В первую очередь то как кодируется старший бит регистра `mcause`. Он зависит от типа причины перехвата (`1` в случае прерывания, `0` в случае исключения). Оставшиеся 31 бит регистра отводятся под коды различных причин. Поскольку мы создаем учебный процессор, который не будет использован в реальной жизни, он не будет поддерживать большую часть прерываний/исключений (таких как невыровненный доступ к памяти, таймеры и т.п.). В рамках данного курса мы должны поддерживать исключение по нелегальной инструкции (код 0x02) и должны уметь поддерживать прерывания периферийных устройств (под которые зарезервированы коды начиная с 16-го). Для кодирования причины прерывания нам потребуется 16 разрядов в регистре `mcause`. Поскольку мы можем использовать коды начиная со значения 16, мы не будем использовать 4 младших бита, вместо этого разместив код причины прерывания в `mcause[19:4]`. +Таким образом: в случае если произошло исключение (в связи с нелегальной инструкцией), значение `mcause` должно быть `0x00000002`. Если произошло прерывание, значение `mcause` должно быть `0x10000010`. -Таким образом: в случае если произошло исключение (в связи с нелегальной инструкцией), значение `mcause` должно быть `0x00000002`. Если произошло прерывание, значение `mcause` должно быть `{1'b1, 11'd0, irq_cause, 4'd0}`. +При желании, процессор можно будет улучшить, добавив поддержку большего числа периферийных устройств. В этом случае потребуется только расширить контроллер прерываний. Когда процессор включается, программа первым делом должна инициализировать все требуемые CS-регистры, в частности: @@ -119,30 +144,7 @@ _Таблица 3. Кодирование причины прерывания в - задать адрес вектора прерывания `mtvec`, - задать адрес вершины стека прерываний `mscratch`. -Остальные операции считывают значение одного из CS-регистров в регистровый файл, при этом, инструкция **csrrw** еще записывает значение из регистрового файла в CSR. Инструкция `csrrs` выполняет логическое ИЛИ между содержимым CS-регистров и регистрового файла, тем самым устанавливая в CS-регистре единицу в тех же битах, что и у считываемого регистра. Операция `csrrc` приводит к очищению битов, значения которых в считываемом из регистрового файла регистре были равны 1. - -| 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 | -|1110011 | 101 | I | csrrwi rd, csr, rs1 | Чтение/Запись CSR | rd = csr, csr = imm | -|1110011 | 110 | I | csrrsi rd, csr, rs1 | Чтение/Установка бит CSR | rd = csr, csr = csr \| imm | -|1110011 | 111 | I | csrrci rd, csr, rs1 | Чтение/ Очистка бит CSR | rd = csr, csr = csr & ~imm | - -_Таблица 4. Список инструкций для работы с регистрами контроля и статуса_ - -Для удобства программирования на языке ассемблера RISC-V существуют псевдоинструкции для работы с CS-регистрами. - -| Псевдоинструкция | Инструкция RISC-V | Описание | Операция | -|------------------|--------------------|-------------|-----------| -| csrr rd, csr | csrrs rd, csr, x0 | Чтение CSR | rd = csr | -| csrw csr, rs1 | csrrw x0, csr, rs1 | Запись CSR | csr = rs1 | - -_Таблица 5. Псевдоинструкции для работы с регистрами контроля и статуса_ - -Операция логического ИЛИ нулевого регистра с содержимым CS-регистра не меняет его содержимого, поэтому при использовании инструкции `csrr` происходит только операция чтения. Подобным образом реализована псевдоинструкция `csrw`. +После чего уже можно переходить к исполнению основного потока инструкций. ### Реализация прерываний в архитектуре RISC-V @@ -160,7 +162,7 @@ _Таблица 5. Псевдоинструкции для работы с ре Контроллер прерываний – это блок процессора, обеспечивающий взаимодействие с устройствами, запрашивающими прерывания, формирование кода причины прерывания для процессора, маскирование прерываний, а также, в других реализациях, может реагировать на прерывания в соответствии с приоритетом и тому подобное. -Каждое периферийное устройство, которое может сгенерировать прерывание, подключается к контроллеру прерывания по одной из 16 пар проводов: запрос на прерывание (`int_req_i`) и прерывание обслужено (`int_ret_o`). Например, подключили клавиатуру к 7-ой паре. Когда на клавиатуру нажимают, код этой клавиши попадает в буферный регистр с дополнительным управляющим битом, выставленным в единицу, который подключен к входу запроса на прерывание. Если прерывание не замаскировано, то есть в данном примере 7-ой бит регистра `mie` выставлен в 1, то контроллер прерывания сгенерирует соответствующий код причины (например, 7 или 7*4, если реализуется векторная система прерывания [по желанию студента]). Кроме этого, контроллер прерывания выдаст сигнал **INT** прямо в устройство управления процессора, чтобы оно узнало, что произошло прерывание и разрешило обновить содержимое регистра причины `mcause`, сохранило адрес прерванной инструкции в `mepc` и загрузило в `PC` вектор прерывания `mtvec`. +Периферийное устройство, которое может генерировать прерывание, подключается к контроллеру прерывания паре проводов: запрос на прерывание (`int_req_i`) и прерывание обслужено (`int_ret_o`). Предположим, к контроллеру прерываний подключили клавиатуру. Когда на ней нажимают клавишу, код этой клавиши попадает в буферный регистр с дополнительным управляющим битом, выставленным в единицу, который подключен к входу запроса на прерывание. Если прерывание не замаскировано (в нашем процессоре это означает, что нулевой бит регистра `mie` выставлен в 1), то контроллер прерывания сгенерирует код причины прерывания (в нашем случае — это константа `0x10000010`). Кроме этого, контроллер прерывания подаст сигнал `irq_o`, чтобы устройство управления процессора узнало, что произошло прерывание и разрешило обновить содержимое регистра причины `mcause`, сохранило адрес прерванной инструкции в `mepc` и загрузило в `PC` вектор прерывания `mtvec`. Когда будет выполняться инструкция `mret`, устройство управления подаст сигнал контроллеру прерывания, чтобы тот, в свою очередь, направил его в виде сигнала «прерывание обслужено» для соответствующего устройства. После этого периферийное устройство обязано снять сигнал запроса прерывания хотя бы на один такт. В нашем примере сигнал «прерывание обслужено» может быть подключен непосредственно к сбросу буферного регистра клавиатуры. @@ -194,45 +196,52 @@ _Рисунок 3. Структурная схема контроллера CS- ### Контроллер прерываний -Рассмотрим один из возможных способов реализации простейшего контроллера прерываний по схеме `daisy chain` (гирлянда). Пример такой схемы вы можете увидеть на _рис. 4_. +Рассмотрим один из возможных способов реализации простейшего контроллера прерываний, представленного на _рис. 4_. -![../../.pic/Labs/lab_10_irq/fig_04.png](../../.pic/Labs/lab_10_irq/fig_04.png) +![../../.pic/Labs/lab_10_irq/fig_04.drawio.png](../../.pic/Labs/lab_10_irq/fig_04.drawio.png) -_Рисунок 4. Структурная схема daisy-цепочки_ +_Рисунок 4. Структурная схема контроллера прерываний_ -Дейзи-цепочка состоит из двух массивов элементов И. Первый массив (верхний ряд элементов) формирует многоразрядный сигнал (назовем его для определенности `enable`), который перемножается с запросами с помощью массива элементов из нижнего ряда. Обратите внимание на то, что результат очередного элемента нижнего массива влияет на результат следующего за ним элемента верхнего массива. Как только на одном из элементов нижнего массива появится значение `1`, оно сразу же распространится в виде инверсии по всем оставшимся элементам верхнего массива. +Несмотря на простоту схемы, данный контроллер состоит из: -Обратите внимание, что результат верхнего ряда массивов (который ранее была назван `enable`) не является маской разрешения прерываний `mie_i`. Сперва исходные запросы на прерывания логически перемножаются с маской `mie_i`, и только после этого, результат логического умножения снова логически перемножается с сигналом `enable` (и это логическое перемножение формирует нижний ряд элементов на схеме). Его результат может содержать только одну единицу, она будет соответствовать одному из запросов на прерывание. Поэтому этот результат можно использовать в качестве сигнала `irq_cause_o` для идентификации причины прерывания (соответствует сигналам y1,y2,...,yn на схеме). Свертка по ИЛИ этого сигнала даст итоговый запрос на прерывание. +- логики обработки вложенных прерываний, частью которой являются регистры отслеживания обработки прерывания и исключения (`irq_h` и `exc_h` соответственно), +- логики установки и сброса этих регистров (которая вместе с этими регистрами заключена в штрихованные прямоугольники), +- логики приоритета исключений над прерываниями, +- а так же логики маскирования запросов на прерывание. -Для описания верхнего ряда на языке SystemVerilog будет удобно воспользоваться конструкцией `generate for`, о которой рассказывалось в [ЛР 1 "Сумматор"](../01.%20Adder#Задание). +Разберем каждую из этих частей. -Рассмотрим реализацию нашего контроллера прерываний: +Регистры отслеживания обработки прерывания и исключения нужны для того, чтобы мы могли понимать, что в данный момент процессор уже выполняет обработку прерывания / исключения. В такие моменты (если любой из регистров `exc_h`/`irq_h` содержит значение `1`) все последующие запросы на прерывание игнорируются. За это отвечают вентили И и ИЛИ-НЕ в правом верхнем углу схемы. -Помимо портов `clk_i` и `rst_i`, модуль будет иметь 4 входа и три выхода: +>Однако возможна ситуация возникновения исключения во время обработки прерывания — в этом случае, оба регистра будут хранить значение `1`. В момент возврата из обработчика, придет сигнал `mret_i`, который в первую очередь сбросит регистр `exc_h` и только если тот равен нулю, сбросит регистр `irq_h`. +> +>Исключение во время обработки исключения не поддерживается данной микроархитектурой и приведет к неопределенному поведению. Поэтому код обработчика исключений должен быть написан с особым вниманием. -- `irq_req_i` — 16-разрядный вход запроса прерывания (т.е. процессор будет поддерживать 16 источников прерывания). -- `mie_i` — маска прерывания, логически перемножающаяся с запросом на прерывание. С помощью маски можно игнорировать отдельные прерывания (0 — прерывание игнорируется, 1 — прерывание не игнорируется). -- `stall_i` — сигнал о выполнении операции с памятью. Пока он равен единице, программный счетчик не изменится, а нам будет нужно отследить момент, когда будет меняться программный счетчик. -- `mret_i` — сигнал о возврате управления основному потоку инструкций (выход из обработчика прерываний) -- `irq_o` — сигнал о начале обработки прерываний. Когда этот сигнал равен единице, в программный счетчик будет загружаться адрес из CS-регистра `mtvec`. Поэтому в случае обработки прерывания, единица должна подняться на этом выходе ровно на 1 такт (иначе в программный счетчик будет непрерывно записываться значение `mtvec`). -- `irq_cause_o` — причина прерывания. В нашем случае, на данном выходе только один бит будет равен единице в момент обработки прерывания (бит, принятому прерыванию). Данный сигнал будет использован для записи в регистр CS-регистр `mcause`. -- `irq_ret_o` — сигнал о завершении обработки запроса на прерывания. Будет соответствовать `irq_cause_o` в момент появления сигнала `mret_i`. +Логика установки и сброса работает следующим образом: -![../../.pic/Labs/lab_10_irq/fig_05.drawio.png](../../.pic/Labs/lab_10_irq/fig_05.drawio.png) +- если сигнал, обозначенный в прямоугольнике как `reset` равен единице, в регистр будет записано значение `0`; +- если сигнал, обозначенный в прямоугольнике как `set` равен единице, в регистр будет записано значение `1`; +- в остальных случах, регистр сохраняет свое значение. -_Рисунок 5. Структурная схема контроллера прерываний_ +Обратите внимание, что логика установки и сброса регистров дает приоритет сбросу, хотя сигнал сброса никогда не придет одновременно с сигналом установки (поскольку инструкция `mret` не генерирует исключение, сигнал `mret_i` никогда не придет одновременно с сигналом `exception_i`, а логика приоритета исключений над прерываниями не даст сигналу `mret` распространиться до регистра `irq_h` одновременно с формированием сигнала `irq_o`). -Особое внимание стоит уделить регистру `busy`. Инверсия этого регистра подается на вход daisy-цепочки, и если эта инверсия будет равна нулю, то вся цепь будет отключена. +Логика приоритета исключений над прерываниями заключается в том, что сигнал `exception_i` является частью логики обработки вложенных прерываний. Пройдя через два логических ИЛИ и последующий инвертор, этот сигнал обнулит запрос на прерывание на логическом И в правом верхнем углу. -С помощью этого регистра можно управлять работой контроллера прерываний, пока он равен единице, новый сигнал о прерывании формироваться не будет. Таким образом мы будем игнорировать все последующие прерывания, пока не будет обслужено предыдущее. Более того, в случае если прерывание пришло посреди исполнения инструкции работы с памятью (поднят сигнал `stall_i`), необходимо дождаться завершения исполнения этой инструкции. +Логика маскирования запросов на прерывания заключается в простейшем И между запросом на прерывания (`irq_req_i`) и сигналом разрешения прерывания (`mie_i`). -## Пример обработки прерывания +## Пример обработки перехвата -Ниже представлен пример программы и обработчика прерывания. Программа начинается с инициализации начальных значений регистров управления, указателя на верхушку стека и глобальную область данных, после чего уходит в бесконечный цикл ничего не делая, до тех пор, пока не произойдет прерывание. +Ниже представлен пример программы и обработчика перехватов. Программа начинается с инициализации начальных значений регистров управления, указателя на верхушку стека и глобальную область данных, после чего уходит в бесконечный цикл ничего не делая, до тех пор, пока не произойдет перехват. -Для данного примера представим, что к контроллеру прерываний подключено две условные кнопки: одна к 5-ому входу прерывания, вторая – к 19-ому. Обработчик прерывания сначала сохраняет значения используемых регистров на стек → проверяет регистр причины, чтобы запустить необходимую подпрограмму для конкретного прерывания → выполняет полезную задачу, связанную с этим прерывание → восстанавливает значения регистрового файла → возвращает управление прерванной программе. Если бы система прерывания была векторная, то рутина со считыванием кода причины отсутствовала. +Алгоритм работы обработчика перехвата (`trap handler`-) выглядит следующим образом: -Для примера пусть прерывание 5 будет прибавлять число 3 к некоторой глобальной переменной, а прерывание 19 будет делить это же число пополам. +1. сохраняется содержимое регистрового файла на стек; +2. проверяется регистр причины чтобы запустить необходимую подпрограмму; +3. происходит вызов необходимой подпрограммы; +4. после возврата происходит восстановление содержимого регистрового файла; +5. затем происходит возврат управления прерванной программе. + +Если бы система прерывания была векторной, то рутина со считыванием кода причины отсутствовала. ```asm _start: @@ -240,10 +249,16 @@ _start: li sp, 0xFFFFFFFC # устанавливаем указатель на верхушку стека li gp, 0x10000000 # устанавливаем указатель на глобальные данные -li t0, 0x00080020 # подготавливаем маску прерывания для 5 и 19 входов +li t0, 0x00000001 # подготавливаем маску прерывания единственного + # (нулевого) входа csrw mie, t0 # загружаем маску в регистр маски -la t0, interrupt # аналогично li загружает число, в данном случае - адрес + +la t0, interrupt # псевдоинструкция la аналогично li загружает число, + # только в случае la — это число является адресом + # указанного места (адреса обработчика перехвата) + csrw mtvec, t0 # устанавливаем вектор прерывания + li t0, 0xEFFFFFFC # готовим адрес верхушки стека прерывания csrw mscratch, t0 # загружаем в указатель на верхушку стека прерывания @@ -265,36 +280,47 @@ main: # Сохраняем используемые регистры на стек interrupt: - csrrw t0, mscratch, t0 # меняем местами mscratch и t0 - sw t1, 0(t0) # сохраняем t1 на стек mscratch - sw t2, 4(t0) # сохраняем t2 на стек mscratch - - # Проверяем регистр причины и на 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 # идем возвращать регистры и на выход + csrrw t0, mscratch, t0 # меняем местами mscratch и t0 + sw t1, 0(t0) # сохраняем t1 на стек mscratch + sw t2, 4(t0) # сохраняем t2 на стек mscratch + + # Проверяем произошло ли прерывание + csrr t1, mcause # t1 = mcause + li t2, 0x10000010 # загружаем в t2 код того, что произошло прерывание + bne t1, t2, exc_handler # если коды не совпадают, переходим к проверке + # на исключение + # Обработчик прерывания + lw t2, 0(gp) # загружаем переменную из памяти + addi t2, t2, 3 # прибавляем к значению 3 + sw t2, 0(gp) # возвращаем переменную в память + j done # идем возвращать регистры и на выход + +exc_handler: # Проверяем произошло ли исключение + li t2, 0x0000002 # загружаем в t2 код того, что произошло исключение + bne t1, t2, done # если это не оно, то выходим + + # Обработчик исключения + csrr mepc, t1 # Узнаем значение PC (адреса инструкции, + # вызвавшей исключение) + lw t2 0x0(t1) # Загружаем эту инструкцию в регистр t2 + # Теоретически мы могли бы после этого + # сделать что-то, в зависимости от этой инструкции. + # Например если это операция умножения — вызвать + # подпрограмму умножения. + + addi t1, t1, 4 # Увеличиваем значение PC на 4, чтобы после + # возврата не попасть на инструкцию, вызвавшую + # исключение. + сsrw mepc, t1 # Записываем обновленное значение PC в регистр mepc + j done # идем восстанавливать регистры со стека и на выход # Возвращаем регистры на места и выходим done: - lw t1, 0(t0) # возвращаем t1 со стека - lw t2, 4(t0) # возвращаем t2 со стека - csrrw t0, mscratch, t0 # меняем обратно местами t0 и mscratch - mret # возвращаем управление программе (pc = mepc) - # что означает возврат в бесконечный цикл + lw t1, 0(t0) # возвращаем t1 со стека + lw t2, 4(t0) # возвращаем t2 со стека + csrrw t0, mscratch, t0 # меняем обратно местами t0 и mscratch + mret # возвращаем управление программе (pc = mepc) + # что означает возврат в бесконечный цикл ``` @@ -335,13 +361,13 @@ endmodule module interrupt_controller( input logic clk_i, input logic rst_i, - input logic stall_i, - input logic [15:0] irq_req_i, - input logic [15:0] mie_i, + input logic exception_i, + input logic irq_req_i, + input logic mie_i, input logic mret_i, - output logic [15:0] irq_ret_o, - output logic [15:0] irq_cause_o, + output logic irq_ret_o, + output logic [31:0] irq_cause_o, output logic irq_o ); @@ -355,10 +381,17 @@ endmodule 1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemSystemVerilog`-файл `csr_controller.sv`. 2. Опишите в нем модуль `csr_controller` с таким же именем и портами, как указано в [задании](#задание). 3. Обратите внимание на наличие импорта пакета `csr_pkg`, данный пакет содержит адреса используемых регистров контроля и статуса, которыми будет удобно пользоваться при реализации модуля. -3. Внимательно ознакомьтесь с описанием функционального поведения сигналов `irq_controller`, а так же его структурной схемой. В случае возникновения вопросов, проконсультируйтесь с преподавателем. -4. Реализуйте модуль `irq_controller`. Для этого: +3. После описания модуля, его необходимо проверить с помощью тестового окружения. + 1. Тестовое окружение находится [здесь](tb_csr.sv). + 2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md). + 3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_csr`). + 4. Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста! +4. Внимательно ознакомьтесь с описанием функционального поведения сигналов `irq_controller`, а так же его структурной схемой. В случае возникновения вопросов, проконсультируйтесь с преподавателем. +5. Реализуйте модуль `irq_controller`. Для этого: 1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemSystemVerilog`-файл `irq_controller.sv`. 2. Опишите в нем модуль `irq_controller` с таким же именем и портами, как указано в [задании](#задание). - 1. Обратите внимание. что верхний ряд 16 элементов логических И проще всего будет реализовать с помощью непрерывного присваивания в блоке `generate for`. Нижний ряд логических И реализовывается в одном выражении побитового И между двумя 16-разрядными сигналами. - 2. Свертка по ИЛИ выполняется посредством **унарного оператора** `|`, ставящегося перед многоразрядным сигналом. -5. Работа по интеграции и проверке модулей в ядро процессора будет происходить в рамках следующей лабораторной работы. +6. После описания модуля, его необходимо проверить с помощью тестового окружения. + 1. Тестовое окружение находится [здесь](tb_irq.sv). + 2. Для запуска симуляции воспользуйтесь [`этой инструкцией`](../../Vivado%20Basics/Run%20Simulation.md). + 3. Перед запуском симуляции убедитесь, что в качестве top-level модуля выбран корректный (`tb_irq`). + 4. Во время симуляции, вы должны прожать "Run All" и убедиться, что в логе есть сообщение о завершении теста!