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

Георгий Курячий Нет. Мы уже многократно этот вопрос обсуждали, и я приводил некоторое количество примеров, из которых следовало, что исключения «по задумке» именно что предполагают возврат — и обработка доступа к памяти, и выравнивание. Вопрос же не в том, что чаще случается.

А зачем его тогда здесь упоминать?

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

Не знаю, зачем эта классификация. Тем более что 2 и 3 по сути дублируют друг друга, а 4 и 5 очевидны из предназначения ловушки. Ну то есть было бы странно, если бы прерывание приема байта как-то портило регистры или ecall приема строки их наоборот не менял.

Вложенные прерывания с разным приоритетом - полезная штука. Правда, количество уровней приоритета обычно невелико. А для исключений - постепенный переход по привилегиям. То есть U-mode сделал ecall, его поймали на S-mode, сделали ecall, его поймали на M-mode и уже там обработали. Одна и та же машина, одни и те же регистры, даже стек может быть одним и тем же.

Чего?! Ловушки обрабатываются только собственно обработчиком. Это даже не задача, это просто функция. Максимум, что тут можно сделать - конечный автомат. Ну или перебрасывать данные в конкретную задачу.

Георгий Курячий Я не смог понять, как эта фраза связана с предыдущей.

Скорее, наоборот: по умолчанию все ловушки и так обрабатываются на M-mode.

Георгий Курячий Это в конце концов, после изобретения M-mode и прочего, обработку ловушек можно туда перенести. Ну, я так и написал.

Зачем здесь это? Изоляция делается через MPU/MMU вообще независимо от ловушек. Ограничение доступа - через уровни привилегий, опять-таки независимо от ловушек.

Георгий Курячий Затем, что здесь идёт речь именно об уровнях привилегий, см одну строку назад

По сути, всего два варианта: управлять ли специальными инструкциями (вообще без разницы, относить ли их к "сопроцессору" или к основному) или через запись в память (MMIO или CSR).

Георгий Курячий Не совсем. Идея в том, есть ли (1) отдельные инструкции управления процессором, и (2) отдельная доступная к изменению изнутри процессора логика. Получается три варианта, один из которых («нет, нет») — вырожденный.

Можно еще добавить, что идут не подряд. То есть это не адреса в непрерывной памяти, а именно разрозненный набор регистров.

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

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

Правильнее сказать даже отображение (mapping). Например, чтобы можно было поменять способ округления, не потеряв флаги. Обычным способом пришлось бы делать crsc + csrs, а так можно просто csrw.

Можно добавить еще столбец как регистры называются в современном стандарте | Rars | Современный стандарт | Адрес в Rars | описание | |


|


|


|


| | cycle | mcycle/scycle/ucycle | 3072 | Количество тактов с момента старта | | time | mtime/stime/utime | 3073 | Абсолютное время (в абстрактных единицах) |

Побочного эффекта производителям железа (разработчикам ядер, ...) следует избегать.

Вот это совершенно точно не так. Более того, ecall как раз и существует только для того, чтобы пробить ограничение прав доступа. А комбинация M-mode + U-mode поддерживается даже в микроконтроллерах (кроме полутора уж совсем примитивных. Даже в Каракатицах, gd32vf103 / ch32v303 оба уровня привилегий в наличии).

Георгий Курячий Вы будете смеяться, но тут я совершенно точно цитирую одно из ваших замечаний. Уберу :)

Он не предотвращает повторных вход. Предотвращает как раз бит xIE. А xPIE говорит, разрешать ли ловушки при выходе.

В xPIE записывается xIE. В xIE записывается ноль, что запрещает повторный вход.

При выходе соответственно в xIE записывается xPIE. В зависимости от настроек это может разрешить прерывания. И никто не запрещает записать в xIE / xPIE что-то руками. Кстати, если уровней привилегий больше 1, добавляются еще PPIE и т.д. - для вложенных исключений.

Здесь же стоит написать, что номера исключений фиксированы стандартом RISC-V, а вот номера прерываний каждый производитель назначает по-своему. Но принцип тот же: в ₓcause записывается номер прерывания.

Лучше разделить: 8. ECALL-U, исключение ecall, вызванное с U-уровня 9. ECALL-S 10. ECALL-H 11. ECALL-M, исключение ecall, вызванное с M-уровня

Георгий Курячий Если честно, прочёл это как предложение, заменить 8, 9, 10, 11 на 8, 9, 10, 11 — и не понял, зачем.

Первым пунктом можно добавить использование общего стека, как простейший случай. Уже потом, что есть специальный регистр ₓscratch, в который можно временно сохранить sp чтобы потом инициализировать его уже ядерным стеком. Или вовсе хранить в ₓscratch полностью готовый ядерный стек, просто переключая его с юзерским при необходимости.

Георгий Курячий Всё-таки хранение «стека обработчика в sp» — это не часть дисциплины.

И то и другое - не специфика Рарс.

Георгий Курячий Конечно. Речь идёт о дисциплине, а не о специфике.

Регистры, естественно, общие. Это ведь часть ядра. Аппаратного переключения банков регистров в RISC-V нет. Разве что какой производитель решит от себя добавить, но я такого не видел.

Адресное пространство тоже никто не заставляет разделять, это ж не MIPS. Собственно, чтобы адресное пространство разделить, надо приложить известные усилия - настроить MMU/MPU.

Его стоит переместить повыше, сразу после описания uret. И начать лучше с простейшего варианта на юзерском стеке.

И от магических чиселок избавляться! Я вот наизусть не помню что там за 65-й CSR. Кстати, мне казалось, что их имена уже прошиты в ассемблер, даже дефайнить руками не требуется. Но даже если требуется, список не такой длинный, можно хоть копипастить в голову каждого файла.

В реальности это что-то вроде

` uint32_t mcause = csr_read(mcause); if( mcause & (1<<31) ){

}else{

} `

Опять-таки, на Си просто нагляднее. Что здесь важно: что обаботчики прерываний сгруппированы в массив, из которого вызываются просто по индексу. И что исключения это именно исключения. Какие можем обработать, обрабатываем, все остальные - ошибки, не позволяющие продолжить работу.

Хм. Думаю, стоит взять пример обработчика от 1921вг015 (там тоже не поддерживается векторная обработка), привести ее плюс примерный перевод на ассемблер (ну так, для соответствия тематике курса).

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

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

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

Зачем? По сути это jump( mtvec + mcause)

Георгий Курячий Да вот нет, это то=то вроде jump(@(mtvec + mcause)), в смысле переход по адресу B, который содержится по адресу A — двойная косвенная адресация как она есть.

Делают и так, и эдак, и вообще непойми как. В том же ch32v303 реализованы ВСЕ ТРИ варианта: можно использовать один общий вектор, можно таблицу адресов и можно таблицу джампов.

Это не так:

Георгий Курячий По стандарту это так «When MODE=Vectored, all synchronous exceptions into machine mode cause the pc to be set to the address in the BASE field, whereas interrupts cause the pc to be set to the address in the BASE field plus four times the interrupt cause number.»

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

Даже в ch32v003, а это САМЫЙ слабый RISC-V контроллер, который я вообще видел, 38 векторов, из которых первые 12 отданы под исключения и тому подобное. В v303 (Каракатица) векторов уже 103. В gd32vf103 (более старая Каракатица) 115.

К чему такие сложности?

Георгий Курячий К проверке Д/З.

` Unhandled_exception: j Unhandled_exception `

Это используется в реальных проектах. И оно же используется как заглушка для необъявленных прерываний.

Георгий Курячий И при проверке Д/З приедет тупой TL.

О прямом, непрямом и побочном эффекте. Если мы пишем в fsrm число 1, то прямой эффект - появление числа 1 в регистре (его можно считать), непрямой эффект - изменение поведения FPU. Получается, scratch - регистр, у которого есть только прямой эффект. А в gd32 есть нестандартный регистр mscratchcsrw. Если привилегии менялись, он срабатывает как mscratch, если нет - как nop. Пожалуй, это тоже только прямой эффект, хотя и нестандартный.

О таблице прерываний. Таблица адресов позволяет адресовать все 4 ГБ памяти, причем позиционно-независимо. Иногда это важно. Таблица переходов позволяет адресовать всего 2 МБ, но позиционно-независимо. Иногда бывает нужно и это. Единый обработчик позволяет более гибко всем этим управлять.

LecturesCMC/ArchitectureAssembler2026/07_Exceptions/COKPOWEHEU (последним исправлял пользователь FrBrGeorge 2026-04-05 20:50:28)