diff --git a/ERRATA.md b/ERRATA.md index 14efd3f..afc806b 100644 --- a/ERRATA.md +++ b/ERRATA.md @@ -2,6 +2,122 @@ ![http://95.215.8.74:5000/days_since_last_commit.png](http://95.215.8.74:5000/days_since_last_commit.png) +**25.07.2026**: В _листинг II.10-1_ (стр. 179-181) были внесены изменения: + +1. Адреса программного стека и стека прерываний изменены с `3FFC` и `1FFC` на `1F0` и `F0` соответственно. + Младшая часть адресов изменена с `C` на `0` поскольку верхушка стека должна быть выровнена по границе в 16 байт (таково требование соглашения о вызовах). + Старшая часть адреса была уменьшена с `3F` до `01` и с `1F` до `00`, чтобы оба стека уместились в исходную память данных, чей размер изначально был задан в `memory_pkg` репозитория на значении в 512 байт. +2. Поскольку адреса `1F0` и `F0` умещаются в 12 бит, загрузка этих адресов теперь умещается в одну инструкцию, а не две. Благодаря этому, инструкции `li`, расположенные по адресам `0x00` и `0x18` разворачиваются в одну инструкцию `addi`. Из-за этого изменились адреса всех инструкций в листинге. +3. В листинге была допущена ошибка: верхушка указателя на стек была записана в регистр `x2`, однако в последствии в качестве верхушки стека использовался регистр `x5`. Теперь листинг использует корректный регистр `x2`. +4. В начале обработчика перехвата `trap_handler` между инструкциями с адресами `0x44` и `0x48` была пропущена инструкция поднятия верхушки стека на 16 байт вверх `addi x2, x2, -16`. Аналогично, была пропущена инструкция опускания верхушки стека на 16 байт вниз `addi x2, x2, 16` в конце обработчика `done` между инструкциями с адресами `0x90` и `0x94` соответственно. + +
+ Исправленная версия листинга + +```asm +_start: +# Инициализируем начальные значения регистров +00: li x2, 0x00001F0 # устанавливаем указатель на верхушку стека +04: li x3, 0x00000000 # устанавливаем указатель на глобальные данные + +08: la x5, trap_handler # псевдоинструкция la аналогично li загружает число, + # только в случае la — это число является адресом + # указанного места (адресом обработчика перехвата) + +0C: csrw mtvec, x5 # устанавливаем вектор прерывания + +10: li x5, 0x000000F0 # готовим адрес верхушки стека прерывания + +14: csrw mscratch, x5 # загружаем указатель на верхушку стека прерывания + +18: li x5, -1 # подготавливаем маску, разрешающую все источники + # прерывания + +1C: csrw mie, x5 # загружаем маску в регистр маски + +20: li x5, 1 # начальное значение глобальной переменной +24: sw x5, 0(x3) # загружаем переменную в память + +28: li x6, 0 # начальное значение, чтобы в симуляции не было xxx +2C: li x7, 0 # начальное значение, чтобы в симуляции не было xxx + +# Вызов ecall исключительно из хулиганских соображений, поскольку в данной +# микроархитектурной реализации это приведет к появлению illegal_instr и +# последующей обработке исключения +30: ecall + +# Вызов функции main +main: +34: beq x0, x0, main # бесконечный цикл, аналогичный while (1); + +# ОБРАБОТЧИК ПЕРЕХВАТА +# Без стороннего вмешательства процессор никогда не перейдёт к инструкциям ниже, +# однако в случае перехвата в программный счетчик будет загружен адрес первой +# нижележащей инструкции. + +# Сохраняем используемые регистры на стек +trap_handler: +38: csrrw x2, mscratch, x2 # меняем местами mscratch и x2 +3C: addi x2, x2, -16 # поднимаем верхушку стека на 16 байт вверх + # (указатель на стек всегда должен быть выровнен + # по границе в 16 байт) +40: sw x6, 0(x2) # сохраняем x6 на стек mscratch +44: sw x7, 4(x2) # сохраняем x7 на стек mscratch + +# Проверяем произошло ли прерывание +48: csrr x6, mcause # x6 = mcause +4C: li x7, 0x80000010 # загружаем в x7 код того, что произошло прерывание +50: # данная псевдоинструкция будет разбита на две + # инструкции: lui и addi +54: bne x6, x7, exc_handler # если коды не совпадают, переходим к проверке + # на исключение +# Обработчик прерывания +58: lw x7, 0(x3) # загружаем переменную из памяти +5C: addi x7, x7, 3 # прибавляем к значению 3 +60: sw x7, 0(x3) # возвращаем переменную в память +64: j done # идем возвращать регистры и на выход + +exc_handler: # Проверяем произошло ли исключение +68: li x7, 0x0000002 # загружаем в x7 код того, что произошло исключение +6C: bne x6, x7, done # если это не оно, то выходим + +# Обработчик исключения +70: csrr x6, mepc # Узнаём значение PC (адреса инструкции, + # вызвавшей исключение) +74: lw x7, 0x0(x6) # Загружаем эту инструкцию в регистр x7. + # В текущей микроархитектурной реализации это + # невозможно, т.к. память инструкций отделена от + # памяти данных и не участвует в выполнении + # операций load / store. + # Другой способ узнать об инструкции, приведшей + # к исключению — добавить поддержку статусного + # регистра mtval, в который при исключении + # может быть записана текущая инструкция. + # Теоретически мы могли бы после этого + # сделать что-то, в зависимости от этой инструкции. + # Например, если это операция умножения — вызвать + # подпрограмму умножения. + +78: addi x6, x6, 4 # Увеличиваем значение PC на 4, чтобы после + # возврата не попасть на инструкцию, вызвавшую + # исключение. +7C: csrw mepc, x6 # Записываем обновленное значение PC в регистр mepc +80: j done # идем восстанавливать регистры со стека и на выход + +# Возвращаем регистры на места и выходим +done: +84: lw x6, 0(x2) # возвращаем x6 со стека +88: lw x7, 4(x2) # возвращаем x7 со стека +8C: addi x2, x2, 16 # опускаем верхушку стека обратно на 16 байт вниз +90: csrrw x2, mscratch, x2 # меняем обратно местами x2 и mscratch +94: mret # возвращаем управление программе (pc = mepc) + # что означает возврат в бесконечный цикл +``` + +
+ +
+ **30.12.2025**: В Финальном обзоре ЛР№4 (стр. 111) указан неверный тип константы для инструкций переходов: 23-битная `const`, в то время как на самом деле используется 8-битная `offset`.
diff --git a/Labs/10. Interrupt subsystem/README.md b/Labs/10. Interrupt subsystem/README.md index 21af7da..f292ab9 100644 --- a/Labs/10. Interrupt subsystem/README.md +++ b/Labs/10. Interrupt subsystem/README.md @@ -237,81 +237,74 @@ _Рисунок 5. Структурная схема контроллера пр ```asm _start: # Инициализируем начальные значения регистров -00: li x2, 0x00003FF0 # устанавливаем указатель на верхушку стека -04: # данная псевдоинструкция будет разбита на две - # инструкции: lui и addi +00: li x2, 0x00001F0 # устанавливаем указатель на верхушку стека +04: li x3, 0x00000000 # устанавливаем указатель на глобальные данные -08: li x3, 0x00000000 # устанавливаем указатель на глобальные данные - -0С: la x5, trap_handler # псевдоинструкция la аналогично li загружает число, -10: # только в случае la — это число является адресом +08: la x5, trap_handler # псевдоинструкция la аналогично li загружает число, + # только в случае la — это число является адресом # указанного места (адресом обработчика перехвата) - # данная псевдоинструкция будет разбита на две - # инструкции: lui и addi -14: csrw mtvec, x5 # устанавливаем вектор прерывания +0C: csrw mtvec, x5 # устанавливаем вектор прерывания -18: li x5, 0x00001FFC # готовим адрес верхушки стека прерывания -1С: # данная псевдоинструкция будет разбита на две - # инструкции: lui и addi +10: li x5, 0x000000F0 # готовим адрес верхушки стека прерывания -20: csrw mscratch, x5 # загружаем указатель на верхушку стека прерывания +14: csrw mscratch, x5 # загружаем указатель на верхушку стека прерывания -24: li x5, 0x00010000 # подготавливаем маску прерывания единственного - # входа прерываний +18: li x5, -1 # подготавливаем маску, разрешающую все источники + # прерывания -28: csrw mie, x5 # загружаем маску в регистр маски +1C: csrw mie, x5 # загружаем маску в регистр маски -2С: li x5, 1 # начальное значение глобальной переменной -30: sw x5, 0(x3) # загружаем переменную в память +20: li x5, 1 # начальное значение глобальной переменной +24: sw x5, 0(x3) # загружаем переменную в память -34: li x6, 0 # начальное значение, чтобы в симуляции не было xxx -38: li x7, 0 # начальное значение, чтобы в симуляции не было xxx +28: li x6, 0 # начальное значение, чтобы в симуляции не было xxx +2C: li x7, 0 # начальное значение, чтобы в симуляции не было xxx # Вызов ecall исключительно из хулиганских соображений, поскольку в данной # микроархитектурной реализации это приведет к появлению illegal_instr и # последующей обработке исключения -3С: ecall +30: ecall # Вызов функции main main: -40: beq x0, x0, main # бесконечный цикл, аналогичный while (1); +34: beq x0, x0, main # бесконечный цикл, аналогичный while (1); # ОБРАБОТЧИК ПЕРЕХВАТА -# Без стороннего вмешательства процессор никогда не перейдет к инструкциям ниже, +# Без стороннего вмешательства процессор никогда не перейдёт к инструкциям ниже, # однако в случае перехвата в программный счетчик будет загружен адрес первой # нижележащей инструкции. # Сохраняем используемые регистры на стек trap_handler: -44: csrrw x5, mscratch, x5 # меняем местами mscratch и x5 -48: addi x5, x5, -16 # поднимаем верхушку стека на 16 байт вверх +38: csrrw x2, mscratch, x2 # меняем местами mscratch и x2 +3C: addi x2, x2, -16 # поднимаем верхушку стека на 16 байт вверх # (указатель на стек всегда должен быть выровнен - # границе в 16 байт) -4С: sw x6, 0(x5) # сохраняем x6 на стек mscratch -50: sw x7, 4(x5) # сохраняем x7 на стек mscratch + # по границе в 16 байт) +40: sw x6, 0(x2) # сохраняем x6 на стек mscratch +44: sw x7, 4(x2) # сохраняем x7 на стек mscratch # Проверяем произошло ли прерывание -54: csrr x6, mcause # x6 = mcause -58: li x7, 0x80000010 # загружаем в x7 код того, что произошло прерывание -5С: # данная псевдоинструкция будет разбита на две +48: csrr x6, mcause # x6 = mcause +4C: li x7, 0x80000010 # загружаем в x7 код того, что произошло прерывание +50: # данная псевдоинструкция будет разбита на две # инструкции: lui и addi -60: bne x6, x7, exc_handler # если коды не совпадают, переходим к проверке +54: bne x6, x7, exc_handler # если коды не совпадают, переходим к проверке # на исключение # Обработчик прерывания -64: lw x7, 0(x3) # загружаем переменную из памяти -68: addi x7, x7, 3 # прибавляем к значению 3 -6С: sw x7, 0(x3) # возвращаем переменную в память -70: j done # идем возвращать регистры и на выход +58: lw x7, 0(x3) # загружаем переменную из памяти +5C: addi x7, x7, 3 # прибавляем к значению 3 +60: sw x7, 0(x3) # возвращаем переменную в память +64: j done # идем возвращать регистры и на выход exc_handler: # Проверяем произошло ли исключение -74: li x7, 0x0000002 # загружаем в x7 код того, что произошло исключение -78: bne x6, x7, done # если это не оно, то выходим +68: li x7, 0x0000002 # загружаем в x7 код того, что произошло исключение +6C: bne x6, x7, done # если это не оно, то выходим # Обработчик исключения -7С: csrr x6, mepc # Узнаём значение PC (адреса инструкции, +70: csrr x6, mepc # Узнаём значение PC (адреса инструкции, # вызвавшей исключение) -80: lw x7, 0x0(x6) # Загружаем эту инструкцию в регистр x7. +74: lw x7, 0x0(x6) # Загружаем эту инструкцию в регистр x7. # В текущей микроархитектурной реализации это # невозможно, т.к. память инструкций отделена от # памяти данных и не участвует в выполнении @@ -325,19 +318,19 @@ exc_handler: # Проверяем произошло ли иск # Например, если это операция умножения — вызвать # подпрограмму умножения. -84: addi x6, x6, 4 # Увеличиваем значение PC на 4, чтобы после +78: addi x6, x6, 4 # Увеличиваем значение PC на 4, чтобы после # возврата не попасть на инструкцию, вызвавшую # исключение. -88: csrw mepc, x6 # Записываем обновленное значение PC в регистр mepc -8С: j done # идем восстанавливать регистры со стека и на выход +7C: csrw mepc, x6 # Записываем обновленное значение PC в регистр mepc +80: j done # идем восстанавливать регистры со стека и на выход # Возвращаем регистры на места и выходим done: -90: lw x6, 0(x5) # возвращаем x6 со стека -94: lw x7, 4(x5) # возвращаем x7 со стека -98: addi x5, x5, 16 # опускаем верхушку стека обратно на 16 байт вниз -9С: csrrw x5, mscratch, x5 # меняем обратно местами x5 и mscratch -A0: mret # возвращаем управление программе (pc = mepc) +84: lw x6, 0(x2) # возвращаем x6 со стека +88: lw x7, 4(x2) # возвращаем x7 со стека +8C: addi x2, x2, 16 # опускаем верхушку стека обратно на 16 байт вниз +90: csrrw x2, mscratch, x2 # меняем обратно местами x2 и mscratch +94: mret # возвращаем управление программе (pc = mepc) # что означает возврат в бесконечный цикл ``` diff --git a/Labs/11. Interrupt integration/program.mem b/Labs/11. Interrupt integration/program.mem index 6386966..8c8d276 100644 --- a/Labs/11. Interrupt integration/program.mem +++ b/Labs/11. Interrupt integration/program.mem @@ -1,13 +1,10 @@ -00004137 -FF010113 +1f000113 00000193 -04400293 -00028293 +03800293 30529073 -000022B7 -FFC28293 +0F000293 34029073 -000102B7 +FFF00293 30429073 00100293 0051A023 @@ -15,10 +12,10 @@ FFC28293 00000393 00000073 00000063 -340292F3 -ff028293 -0062A023 -0072A223 +34011173 +FF010113 +00612023 +00712223 34202373 800003B7 01038393 @@ -34,8 +31,8 @@ ff028293 00430313 34131073 0040006F -0002A303 -0042A383 -01028293 -340292F3 +00012303 +00412383 +01010113 +34011173 30200073 \ No newline at end of file