mirror of
https://github.com/MPSU/APS.git
synced 2026-07-25 16:28:26 +00:00
ЛР10-11. Обновление листинга
This commit is contained in:
116
ERRATA.md
116
ERRATA.md
@@ -2,6 +2,122 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
**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` соответственно.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary> Исправленная версия листинга </summary>
|
||||||
|
|
||||||
|
```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)
|
||||||
|
# что означает возврат в бесконечный цикл
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
**30.12.2025**: В Финальном обзоре ЛР№4 (стр. 111) указан неверный тип константы для инструкций переходов: 23-битная `const`, в то время как на самом деле используется 8-битная `offset`.
|
**30.12.2025**: В Финальном обзоре ЛР№4 (стр. 111) указан неверный тип константы для инструкций переходов: 23-битная `const`, в то время как на самом деле используется 8-битная `offset`.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
@@ -237,81 +237,74 @@ _Рисунок 5. Структурная схема контроллера пр
|
|||||||
```asm
|
```asm
|
||||||
_start:
|
_start:
|
||||||
# Инициализируем начальные значения регистров
|
# Инициализируем начальные значения регистров
|
||||||
00: li x2, 0x00003FF0 # устанавливаем указатель на верхушку стека
|
00: li x2, 0x00001F0 # устанавливаем указатель на верхушку стека
|
||||||
04: # данная псевдоинструкция будет разбита на две
|
04: li x3, 0x00000000 # устанавливаем указатель на глобальные данные
|
||||||
# инструкции: lui и addi
|
|
||||||
|
|
||||||
08: li x3, 0x00000000 # устанавливаем указатель на глобальные данные
|
08: la x5, trap_handler # псевдоинструкция la аналогично li загружает число,
|
||||||
|
# только в случае la — это число является адресом
|
||||||
0С: la x5, trap_handler # псевдоинструкция la аналогично li загружает число,
|
|
||||||
10: # только в случае la — это число является адресом
|
|
||||||
# указанного места (адресом обработчика перехвата)
|
# указанного места (адресом обработчика перехвата)
|
||||||
# данная псевдоинструкция будет разбита на две
|
|
||||||
# инструкции: lui и addi
|
|
||||||
|
|
||||||
14: csrw mtvec, x5 # устанавливаем вектор прерывания
|
0C: csrw mtvec, x5 # устанавливаем вектор прерывания
|
||||||
|
|
||||||
18: li x5, 0x00001FFC # готовим адрес верхушки стека прерывания
|
10: li x5, 0x000000F0 # готовим адрес верхушки стека прерывания
|
||||||
1С: # данная псевдоинструкция будет разбита на две
|
|
||||||
# инструкции: lui и addi
|
|
||||||
|
|
||||||
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 # начальное значение глобальной переменной
|
20: li x5, 1 # начальное значение глобальной переменной
|
||||||
30: sw x5, 0(x3) # загружаем переменную в память
|
24: sw x5, 0(x3) # загружаем переменную в память
|
||||||
|
|
||||||
34: li x6, 0 # начальное значение, чтобы в симуляции не было xxx
|
28: li x6, 0 # начальное значение, чтобы в симуляции не было xxx
|
||||||
38: li x7, 0 # начальное значение, чтобы в симуляции не было xxx
|
2C: li x7, 0 # начальное значение, чтобы в симуляции не было xxx
|
||||||
|
|
||||||
# Вызов ecall исключительно из хулиганских соображений, поскольку в данной
|
# Вызов ecall исключительно из хулиганских соображений, поскольку в данной
|
||||||
# микроархитектурной реализации это приведет к появлению illegal_instr и
|
# микроархитектурной реализации это приведет к появлению illegal_instr и
|
||||||
# последующей обработке исключения
|
# последующей обработке исключения
|
||||||
3С: ecall
|
30: ecall
|
||||||
|
|
||||||
# Вызов функции main
|
# Вызов функции main
|
||||||
main:
|
main:
|
||||||
40: beq x0, x0, main # бесконечный цикл, аналогичный while (1);
|
34: beq x0, x0, main # бесконечный цикл, аналогичный while (1);
|
||||||
|
|
||||||
# ОБРАБОТЧИК ПЕРЕХВАТА
|
# ОБРАБОТЧИК ПЕРЕХВАТА
|
||||||
# Без стороннего вмешательства процессор никогда не перейдет к инструкциям ниже,
|
# Без стороннего вмешательства процессор никогда не перейдёт к инструкциям ниже,
|
||||||
# однако в случае перехвата в программный счетчик будет загружен адрес первой
|
# однако в случае перехвата в программный счетчик будет загружен адрес первой
|
||||||
# нижележащей инструкции.
|
# нижележащей инструкции.
|
||||||
|
|
||||||
# Сохраняем используемые регистры на стек
|
# Сохраняем используемые регистры на стек
|
||||||
trap_handler:
|
trap_handler:
|
||||||
44: csrrw x5, mscratch, x5 # меняем местами mscratch и x5
|
38: csrrw x2, mscratch, x2 # меняем местами mscratch и x2
|
||||||
48: addi x5, x5, -16 # поднимаем верхушку стека на 16 байт вверх
|
3C: addi x2, x2, -16 # поднимаем верхушку стека на 16 байт вверх
|
||||||
# (указатель на стек всегда должен быть выровнен
|
# (указатель на стек всегда должен быть выровнен
|
||||||
# границе в 16 байт)
|
# по границе в 16 байт)
|
||||||
4С: sw x6, 0(x5) # сохраняем x6 на стек mscratch
|
40: sw x6, 0(x2) # сохраняем x6 на стек mscratch
|
||||||
50: sw x7, 4(x5) # сохраняем x7 на стек mscratch
|
44: sw x7, 4(x2) # сохраняем x7 на стек mscratch
|
||||||
|
|
||||||
# Проверяем произошло ли прерывание
|
# Проверяем произошло ли прерывание
|
||||||
54: csrr x6, mcause # x6 = mcause
|
48: csrr x6, mcause # x6 = mcause
|
||||||
58: li x7, 0x80000010 # загружаем в x7 код того, что произошло прерывание
|
4C: li x7, 0x80000010 # загружаем в x7 код того, что произошло прерывание
|
||||||
5С: # данная псевдоинструкция будет разбита на две
|
50: # данная псевдоинструкция будет разбита на две
|
||||||
# инструкции: lui и addi
|
# инструкции: lui и addi
|
||||||
60: bne x6, x7, exc_handler # если коды не совпадают, переходим к проверке
|
54: bne x6, x7, exc_handler # если коды не совпадают, переходим к проверке
|
||||||
# на исключение
|
# на исключение
|
||||||
# Обработчик прерывания
|
# Обработчик прерывания
|
||||||
64: lw x7, 0(x3) # загружаем переменную из памяти
|
58: lw x7, 0(x3) # загружаем переменную из памяти
|
||||||
68: addi x7, x7, 3 # прибавляем к значению 3
|
5C: addi x7, x7, 3 # прибавляем к значению 3
|
||||||
6С: sw x7, 0(x3) # возвращаем переменную в память
|
60: sw x7, 0(x3) # возвращаем переменную в память
|
||||||
70: j done # идем возвращать регистры и на выход
|
64: j done # идем возвращать регистры и на выход
|
||||||
|
|
||||||
exc_handler: # Проверяем произошло ли исключение
|
exc_handler: # Проверяем произошло ли исключение
|
||||||
74: li x7, 0x0000002 # загружаем в x7 код того, что произошло исключение
|
68: li x7, 0x0000002 # загружаем в x7 код того, что произошло исключение
|
||||||
78: bne x6, x7, done # если это не оно, то выходим
|
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
|
7C: csrw mepc, x6 # Записываем обновленное значение PC в регистр mepc
|
||||||
8С: j done # идем восстанавливать регистры со стека и на выход
|
80: j done # идем восстанавливать регистры со стека и на выход
|
||||||
|
|
||||||
# Возвращаем регистры на места и выходим
|
# Возвращаем регистры на места и выходим
|
||||||
done:
|
done:
|
||||||
90: lw x6, 0(x5) # возвращаем x6 со стека
|
84: lw x6, 0(x2) # возвращаем x6 со стека
|
||||||
94: lw x7, 4(x5) # возвращаем x7 со стека
|
88: lw x7, 4(x2) # возвращаем x7 со стека
|
||||||
98: addi x5, x5, 16 # опускаем верхушку стека обратно на 16 байт вниз
|
8C: addi x2, x2, 16 # опускаем верхушку стека обратно на 16 байт вниз
|
||||||
9С: csrrw x5, mscratch, x5 # меняем обратно местами x5 и mscratch
|
90: csrrw x2, mscratch, x2 # меняем обратно местами x2 и mscratch
|
||||||
A0: mret # возвращаем управление программе (pc = mepc)
|
94: mret # возвращаем управление программе (pc = mepc)
|
||||||
# что означает возврат в бесконечный цикл
|
# что означает возврат в бесконечный цикл
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
00004137
|
1f000113
|
||||||
FF010113
|
|
||||||
00000193
|
00000193
|
||||||
04400293
|
03800293
|
||||||
00028293
|
|
||||||
30529073
|
30529073
|
||||||
000022B7
|
0F000293
|
||||||
FFC28293
|
|
||||||
34029073
|
34029073
|
||||||
000102B7
|
FFF00293
|
||||||
30429073
|
30429073
|
||||||
00100293
|
00100293
|
||||||
0051A023
|
0051A023
|
||||||
@@ -15,10 +12,10 @@ FFC28293
|
|||||||
00000393
|
00000393
|
||||||
00000073
|
00000073
|
||||||
00000063
|
00000063
|
||||||
340292F3
|
34011173
|
||||||
ff028293
|
FF010113
|
||||||
0062A023
|
00612023
|
||||||
0072A223
|
00712223
|
||||||
34202373
|
34202373
|
||||||
800003B7
|
800003B7
|
||||||
01038393
|
01038393
|
||||||
@@ -34,8 +31,8 @@ ff028293
|
|||||||
00430313
|
00430313
|
||||||
34131073
|
34131073
|
||||||
0040006F
|
0040006F
|
||||||
0002A303
|
00012303
|
||||||
0042A383
|
00412383
|
||||||
01028293
|
01010113
|
||||||
340292F3
|
34011173
|
||||||
30200073
|
30200073
|
||||||
Reference in New Issue
Block a user