ЛР10. Переработка mcause

Приведение генерации причины перехвата в соответствие с машин модом.
Это необходимо для проверки проца с помощью spike.
This commit is contained in:
Andrei Solodovnikov
2023-10-16 22:42:06 +03:00
parent 91c4a73d60
commit 505b5c5818
5 changed files with 22 additions and 12 deletions

View File

@@ -103,6 +103,16 @@ _Таблица 2. Список регистров, подлежащих реа
Так как обработчик прерывания будет использовать те же регистры, что и прерванная программа, то перед использованием регистрового файла, данные из него необходимо сохранить, разместив их на стеке. Стек для прерывания находится не там же, где программный стек, а адрес начала этого стека хранится в регистре `mscratch` и по сути является указателем на верхушку стека. Регистр `mepc` сохраняет адрес инструкции во время которой произошел перехват. Это очень важно понимать, при реализации обработчика исключения — если в нем не перезаписать этот регистр, по возврату из обработчика процессор снова окажется на инструкции, которая вызвала исключение.
То как кодируется причина перехвата в регистре `mcause` описано в [спецификации](https://github.com/riscv/riscv-isa-manual/releases/download/riscv-isa-release-1239329-2023-05-23/riscv-privileged.pdf) привилегированной архитектуры (раздел 3.1.15, стр. 38):
![../../.pic/Labs/lab_10_irq/tab_03.png](../../.pic/Labs/lab_10_irq/tab_03.png)
_Таблица 3. Кодирование причины прерывания в регистре `mcause`_
Нас интересуют части, выделенные красным. В первую очередь то как кодируется старший бит регистра `mcause`. Он зависит от типа причины перехвата (`1` в случае прерывания, `0` в случае исключения). Оставшиеся 31 бит регистра отводятся под коды различных причин. Поскольку мы создаем учебный процессор, который не будет использован в реальной жизни, он не будет поддерживать большую часть прерываний/исключений (таких как невыровненный доступ к памяти, таймеры и т.п.). В рамках данного курса мы должны поддерживать исключение по нелегальной инструкции (код 0x02) и должны уметь поддерживать прерывания периферийных устройств (под которые зарезервированы коды начиная с 16-го). Для кодирования причины прерывания нам потребуется 16 разрядов в регистре `mcause`. Поскольку мы можем использовать коды начиная со значения 16, мы не будем использовать 4 младших бита, вместо этого взяв разместив код причины прерывания в `mcause[19:4]`.
Таким образом: в случае если произошло исключение (в связи с нелегальной инструкцией), значение `mcause` должно быть `0x00000002`. Если произошло прерывание, значение `mcause` должно быть `{1'b1, 11'd0, irq_cause, 4'd0}`.
Когда процессор включается, программа первым делом должна инициализировать все требуемые CS-регистры, в частности:
- задать маску прерывания `mie`,
@@ -121,7 +131,7 @@ _Таблица 2. Список регистров, подлежащих реа
|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. Список инструкций для работы с регистрами контроля и статуса_
_Таблица 4. Список инструкций для работы с регистрами контроля и статуса_
Для удобства программирования на языке ассемблера RISC-V существуют псевдоинструкции для работы с CS-регистрами.
@@ -130,7 +140,7 @@ _Таблица 2. Список инструкций для работы с ре
| csrr rd, csr | csrrs rd, csr, x0 | Чтение CSR | rd = csr |
| csrw csr, rs1 | csrrw x0, csr, rs1 | Запись CSR | csr = rs1 |
_Таблица 3. Псевдоинструкции для работы с регистрами контроля и статуса_
_Таблица 5. Псевдоинструкции для работы с регистрами контроля и статуса_
Операция логического ИЛИ нулевого регистра с содержимым CS-регистра не меняет его содержимого, поэтому при использовании инструкции `csrr` происходит только операция чтения. Подобным образом реализована псевдоинструкция `csrw`.
@@ -150,7 +160,7 @@ _Таблица 3. Псевдоинструкции для работы с ре
Контроллер прерываний это блок процессора, обеспечивающий взаимодействие с устройствами, запрашивающими прерывания, формирование кода причины прерывания для процессора, маскирование прерываний, а также, в других реализациях, может реагировать на прерывания в соответствии с приоритетом и тому подобное.
Каждое периферийное устройство, которое может сгенерировать прерывание, подключается к контроллеру прерывания по одной из 32 пар проводов: запрос на прерывание (`int_req_i`) и прерывание обслужено (`int_ret_o`). Например, подключили клавиатуру к 7-ой паре. Когда на клавиатуру нажимают, код этой клавиши попадает в буферный регистр с дополнительным управляющим битом, выставленным в единицу, который подключен к входу запроса на прерывание. Если прерывание не замаскировано, то есть в данном примере 7-ой бит регистра `mie` выставлен в 1, то контроллер прерывания сгенерирует соответствующий код причины (например, 7 или 7*4, если реализуется векторная система прерывания [по желанию студента]). Кроме этого, контроллер прерывания выдаст сигнал **INT** прямо в устройство управления процессора, чтобы оно узнало, что произошло прерывание и разрешило обновить содержимое регистра причины `mcause`, сохранило адрес прерванной инструкции в `mepc` и загрузило в `PC` вектор прерывания `mtvec`.
Каждое периферийное устройство, которое может сгенерировать прерывание, подключается к контроллеру прерывания по одной из 16 пар проводов: запрос на прерывание (`int_req_i`) и прерывание обслужено (`int_ret_o`). Например, подключили клавиатуру к 7-ой паре. Когда на клавиатуру нажимают, код этой клавиши попадает в буферный регистр с дополнительным управляющим битом, выставленным в единицу, который подключен к входу запроса на прерывание. Если прерывание не замаскировано, то есть в данном примере 7-ой бит регистра `mie` выставлен в 1, то контроллер прерывания сгенерирует соответствующий код причины (например, 7 или 7*4, если реализуется векторная система прерывания [по желанию студента]). Кроме этого, контроллер прерывания выдаст сигнал **INT** прямо в устройство управления процессора, чтобы оно узнало, что произошло прерывание и разрешило обновить содержимое регистра причины `mcause`, сохранило адрес прерванной инструкции в `mepc` и загрузило в `PC` вектор прерывания `mtvec`.
Когда будет выполняться инструкция `mret`, устройство управления подаст сигнал контроллеру прерывания, чтобы тот, в свою очередь, направил его в виде сигнала «прерывание обслужено» для соответствующего устройства. После этого периферийное устройство обязано снять сигнал запроса прерывания хотя бы на один такт. В нашем примере сигнал «прерывание обслужено» может быть подключен непосредственно к сбросу буферного регистра клавиатуры.
@@ -192,7 +202,7 @@ _Рисунок 4. Структурная схема daisy-цепочки_
Дейзи-цепочка состоит из двух массивов элементов И. Первый массив (верхний ряд элементов) формирует многоразрядный сигнал (назовем его для определенности `enable`), который перемножается с запросами с помощью массива элементов из нижнего ряда. Обратите внимание на то, что результат очередного элемента нижнего массива влияет на результат следующего за ним элемента верхнего массива. Как только на одном из элементов нижнего массива появится значение `1`, оно сразу же распространится в виде инверсии по всем оставшимся элементам верхнего массива.
Обратите внимание, что результат верхнего ряда массивов (который ранее была назван `enable`) не является маской разрешения прерываний `mie_i`. Сперва исходные запросы на прерывания логически перемножаются с маской `mie_i`, и только после этого, результат логического умножения снова логически перемножается с сигналом `enable` (и это логическое перемножение формирует нижний ряд элементов на схеме). Его результат может содержать только одну единицу, она будет соответствовать одному из запросов на прерывание. Поэтому этот результат можно использовать в качестве сигнала `mcause_o` для идентификации причины прерывания (соответствует сигналам y<sub>1</sub>,y<sub>2</sub>,...,y<sub>n</sub> на схеме). Свертка по ИЛИ этого сигнала даст итоговый запрос на прерывание.
Обратите внимание, что результат верхнего ряда массивов (который ранее была назван `enable`) не является маской разрешения прерываний `mie_i`. Сперва исходные запросы на прерывания логически перемножаются с маской `mie_i`, и только после этого, результат логического умножения снова логически перемножается с сигналом `enable` (и это логическое перемножение формирует нижний ряд элементов на схеме). Его результат может содержать только одну единицу, она будет соответствовать одному из запросов на прерывание. Поэтому этот результат можно использовать в качестве сигнала `irq_cause_o` для идентификации причины прерывания (соответствует сигналам y<sub>1</sub>,y<sub>2</sub>,...,y<sub>n</sub> на схеме). Свертка по ИЛИ этого сигнала даст итоговый запрос на прерывание.
Для описания верхнего ряда на языке SystemVerilog будет удобно воспользоваться конструкцией `generate for`, о которой рассказывалось в [ЛР 1 "Сумматор"](../01.%20Adder#Задание).
@@ -200,13 +210,13 @@ _Рисунок 4. Структурная схема daisy-цепочки_
Помимо портов `clk_i` и `rst_i`, модуль будет иметь 4 входа и три выхода:
- `irq_req_i`32-разрядный вход запроса прерывания (т.е. процессор будет поддерживать 32 источника прерывания).
- `irq_req_i`16-разрядный вход запроса прерывания (т.е. процессор будет поддерживать 16 источников прерывания).
- `mie_i` — маска прерывания, логически перемножающаяся с запросом на прерывание. С помощью маски можно игнорировать отдельные прерывания (0 — прерывание игнорируется, 1 — прерывание не игнорируется).
- `stall_i` — сигнал о выполнении операции с памятью. Пока он равен единице, программный счетчик не изменится, а нам будет нужно отследить момент, когда будет меняться программный счетчик.
- `mret_i` — сигнал о возврате управления основному потоку инструкций (выход из обработчика прерываний)
- `irq_o` — сигнал о начале обработки прерываний. Когда этот сигнал равен единице, в программный счетчик будет загружаться адрес из CS-регистра `mtvec`. Поэтому в случае обработки прерывания, единица должна подняться на этом выходе ровно на 1 такт (иначе в программный счетчик будет непрерывно записываться значение `mtvec`).
- `mcause_o` — причина прерывания. В нашем случае, на данном выходе только один бит будет равен единице в момент обработки прерывания (бит, соответствующий индексу принятого прерывания)
- `irq_ret_o` — сигнал о завершении обработки запроса на прерывания. Будет соответствовать `mcause_o` в момент появления сигнала `mret_i`.
- `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)
@@ -326,12 +336,12 @@ module interrupt_controller(
input logic clk_i,
input logic rst_i,
input logic stall_i,
input logic [31:0] irq_req_i,
input logic [31:0] mie_i,
input logic [15:0] irq_req_i,
input logic [15:0] mie_i,
input logic mret_i,
output logic [31:0] irq_ret_o,
output logic [31:0] mcause_o,
output logic [15:0] irq_ret_o,
output logic [15:0] irq_cause_o,
output logic irq_o
);
@@ -349,6 +359,6 @@ endmodule
4. Реализуйте модуль `irq_controller`. Для этого:
1. В `Design Sources` проекта с предыдущих лаб, создайте `SystemSystemVerilog`-файл `irq_controller.sv`.
2. Опишите в нем модуль `irq_controller` с таким же именем и портами, как указано в [задании](#задание).
1. Обратите внимание. что верхний ряд 32 элементов логических И проще всего будет реализовать с помощью непрерывного присваивания в блоке `generate for`. Нижний ряд логических И реализовывается в одном выражении побитового И между двумя 32-разрядными сигналами.
1. Обратите внимание. что верхний ряд 16 элементов логических И проще всего будет реализовать с помощью непрерывного присваивания в блоке `generate for`. Нижний ряд логических И реализовывается в одном выражении побитового И между двумя 16-разрядными сигналами.
2. Свертка по ИЛИ выполняется посредством **унарного оператора** `|`, ставящегося перед многоразрядным сигналом.
5. Работа по интеграции и проверке модулей в ядро процессора будет происходить в рамках следующей лабораторной работы.