ЛР14,16. Дополнение startup-файла

- добавлен код восстановления CS-регистра mcause
- в более явном виде описано почему в низкоуровневом обработчике
на стек сохраняется не весь регистровый файл.
This commit is contained in:
Andrei Solodovnikov
2025-05-13 17:11:06 +03:00
parent 6f74ab5c21
commit eca95fe11f
3 changed files with 131 additions and 124 deletions

View File

@@ -361,21 +361,19 @@ _endless_loop:
# В основе кода лежит обработчик из репозитория urv-core:
# https://github.com/twlostow/urv-core/blob/master/sw/common/irq.S
# Из реализации убраны сохранения нереализованных CS-регистров. Кроме того,
# судя по документу приведенному ниже, обычное ABI подразумевает такое же
# сохранение контекста, что и при программном вызове (EABI подразумевает ещё
# меньшее сохранение контекста), поэтому нет нужды сохранять весь регистровый
# файл.
# Документ:
# https://github.com/riscv-non-isa/riscv-eabi-spec/blob/master/EABI.adoc
# в реализации сохраняются только необерегаемые регистры регистрового файла.
# Это сделано по причине того, что при вызове высокоуровневого обработчика
# прерываний, тот будет обязан сохранить оберегаемые регистры в соответствии
# с соглашением о вызовах.
_int_handler:
# Данная операция меняет местами регистры sp и mscratch.
# В итоге указатель на стек прерываний оказывается в регистре sp, а вершина
# программного стека оказывается в регистре mscratch.
csrrw sp, mscratch, sp
csrrw sp, mscratch,sp
# Далее мы поднимаемся по стеку прерываний и сохраняем все регистры.
addi sp, sp, -80 # Указатель на стек должен быть выровнен до 16 байт, поэтому
# поднимаемся вверх не на 76, а на 80.
addi sp, sp, -80 # Указатель на стек должен быть выровнен до 16 байт, поэтому
# поднимаемся вверх не на 76, а на 80.
sw ra, 4(sp)
# Мы хотим убедиться, что очередное прерывание не наложит стек прерываний на
# программный стек, поэтому записываем в освободившийся регистр низ
@@ -387,32 +385,32 @@ _int_handler:
la ra, _stack_ptr
blt sp, ra, _endless_loop
sw t0,12(sp) # Мы перепрыгнули через смещение 8, поскольку там должен
# лежать регистр sp, который ранее сохранили в mscratch.
# Мы запишем его на стек чуть позже.
sw t1,16(sp)
sw t2,20(sp)
sw a0,24(sp)
sw a1,28(sp)
sw a2,32(sp)
sw a3,36(sp)
sw a4,40(sp)
sw a5,44(sp)
sw a6,48(sp)
sw a7,52(sp)
sw t3,56(sp)
sw t4,60(sp)
sw t5,64(sp)
sw t6,68(sp)
sw t0, 12(sp) # Мы перепрыгнули через смещение 8, поскольку там должен
# лежать регистр sp, который ранее сохранили в mscratch.
# Мы запишем его на стек чуть позже.
sw t1, 16(sp)
sw t2, 20(sp)
sw a0, 24(sp)
sw a1, 28(sp)
sw a2, 32(sp)
sw a3, 36(sp)
sw a4, 40(sp)
sw a5, 44(sp)
sw a6, 48(sp)
sw a7, 52(sp)
sw t3, 56(sp)
sw t4, 60(sp)
sw t5, 64(sp)
sw t6, 68(sp)
# Кроме того, мы сохраняем состояние регистров прерываний на случай, если
# произойдет ещё одно прерывание.
csrr t0,mscratch
csrr t1,mepc
csrr a0,mcause
sw t0,8(sp)
sw t1,72(sp)
sw a0,76(sp)
csrr t0, mscratch
csrr t1, mepc
csrr a0, mcause
sw t0, 8(sp)
sw t1, 72(sp)
sw a0, 76(sp)
# Вызов высокоуровневого обработчика прерываний.
# Для того чтобы программа скомпоновалась, где-то должна быть описана
@@ -426,33 +424,36 @@ _int_handler:
# сохраним его в регистр a0, и будем восстанавливаться из него.
mv a0,sp
lw t1,72(a0)
addi sp,sp,80
csrw mscratch,sp
csrw mepc,t1
lw ra,4(a0)
lw sp,8(a0)
lw t0,12(a0)
lw t1,16(a0)
lw t2,20(a0)
lw a1,28(a0) # Мы пропустили a0, потому что сейчас он используется в
# качестве указателя на верхушку стека и не может быть
# восстановлен.
lw a2,32(a0)
lw a3,36(a0)
lw a4,40(a0)
lw a5,44(a0)
lw a6,48(a0)
lw a7,52(a0)
lw t3,56(a0)
lw t4,60(a0)
lw t5,64(a0)
lw t6,68(a0)
lw a0,40(a0)
lw t1, 72(a0)
lw t2, 76(a0)
addi sp, sp, 80
csrw mscratch, sp
csrw mepc, t1
csrw mcause, t2
lw ra, 4(a0)
lw sp, 8(a0)
lw t0, 12(a0)
lw t1, 16(a0)
lw t2, 20(a0)
lw a1, 28(a0) # Мы пропустили a0, потому что сейчас он используется в
# качестве указателя на верхушку стека и не может быть
# восстановлен.
lw a2, 32(a0)
lw a3, 36(a0)
lw a4, 40(a0)
lw a5, 44(a0)
lw a6, 48(a0)
lw a7, 52(a0)
lw t3, 56(a0)
lw t4, 60(a0)
lw t5, 64(a0)
lw t6, 68(a0)
lw a0, 40(a0)
# Выход из обработчика прерывания
mret
```
_Листинг 2. Пример содержимого файла первичных команд с поясняющими комментариями._

View File

@@ -58,12 +58,10 @@ _endless_loop:
# В основе кода лежит обработчик из репозитория urv-core:
# https://github.com/twlostow/urv-core/blob/master/sw/common/irq.S
# Из реализации убраны сохранения нереализованных CS-регистров. Кроме того,
# судя по документу приведенному ниже, обычное ABI подразумевает такое же
# сохранение контекста, что и при программном вызове (EABI подразумевает еще
# меньшее сохранение контекста), поэтому нет нужды сохранять весь регистровый
# файл.
# Документ:
# https://github.com/riscv-non-isa/riscv-eabi-spec/blob/master/EABI.adoc
# в реализации сохраняются только необерегаемые регистры регистрового файла.
# Это сделано по причине того, что при вызове высокоуровневого обработчика
# прерываний, тот будет обязан сохранить оберегаемые регистры в соответствии
# с соглашением о вызовах.
_int_handler:
# Данная операция меняет местами регистры sp и mscratch.
# В итоге указатель на стек прерываний оказывается в регистре sp, а вершина
@@ -103,7 +101,7 @@ _int_handler:
sw t6, 68(sp)
# Кроме того, мы сохраняем состояние регистров прерываний на случай, если
# произойдет еще одно прерывание.
# произойдет ещё одно прерывание.
csrr t0, mscratch
csrr t1, mepc
csrr a0, mcause
@@ -119,14 +117,16 @@ _int_handler:
# Восстановление контекста. В первую очередь мы хотим восстановить CS-регистры,
# на случай, если происходило вложенное прерывание. Для этого, мы должны
# вернуть исходное значение указателя стека прерываний. Однако его нынешнее
# значение нам еще необходимо для восстановления контекста, поэтому мы
# значение нам ещё необходимо для восстановления контекста, поэтому мы
# сохраним его в регистр a0, и будем восстанавливаться из него.
mv a0,sp
lw t1, 72(a0)
lw t2, 76(a0)
addi sp, sp, 80
csrw mscratch, sp
csrw mepc, t1
csrw mcause, t2
lw ra, 4(a0)
lw sp, 8(a0)
lw t0, 12(a0)

View File

@@ -41,7 +41,11 @@ _main_call:
# нулевой элемент которого является именем исполняемого файла,
# Но для простоты реализации оба аргумента всего лишь обнулены.
# Это сделано для детерминированного поведения программы в случае,
# если будет пытаться использовать эти аргументы.
# если программист будет пытаться использовать эти аргументы.
# Вызов main.
# Для того чтобы программа скомпоновалась, где-то должна быть описана
# функция именно с таким именем.
call main
# Зацикливание после выхода из функции main
_endless_loop:
@@ -54,22 +58,20 @@ _endless_loop:
# В основе кода лежит обработчик из репозитория urv-core:
# https://github.com/twlostow/urv-core/blob/master/sw/common/irq.S
# Из реализации убраны сохранения нереализованных CS-регистров. Кроме того,
# судя по документу приведенному ниже, обычное ABI подразумевает такое же
# сохранение контекста, что и при программном вызове (EABI подразумевает еще
# меньшее сохранение контекста), поэтому нет нужды сохранять весь регистровый
# файл.
# Документ:
# https://github.com/riscv-non-isa/riscv-eabi-spec/blob/master/EABI.adoc
# в реализации сохраняются только необерегаемые регистры регистрового файла.
# Это сделано по причине того, что при вызове высокоуровневого обработчика
# прерываний, тот будет обязан сохранить оберегаемые регистры в соответствии
# с соглашением о вызовах.
_int_handler:
# Данная операция меняет местами регистры sp и mscratch.
# В итоге указатель на стек прерываний оказывается в регистре sp, а вершина
# программного стека оказывается в регистре mscratch.
csrrw sp,mscratch,sp
csrrw sp, mscratch,sp
# Далее мы поднимаемся по стеку прерываний и сохраняем все регистры.
addi sp,sp,-80 # Указатель на стек должен быть выровнен до 16 байт, поэтому
# поднимаемся вверх не на 76, а на 80.
sw ra,4(sp)
addi sp, sp, -80 # Указатель на стек должен быть выровнен до 16 байт, поэтому
# поднимаемся вверх не на 76, а на 80.
sw ra, 4(sp)
# Мы хотим убедиться, что очередное прерывание не наложит стек прерываний на
# программный стек, поэтому записываем в освободившийся регистр низ
# программного стека, и проверяем что приподнятый указатель на верхушку
@@ -80,66 +82,70 @@ _int_handler:
la ra, _stack_ptr
blt sp, ra, _endless_loop
sw t0,12(sp) # Мы перепрыгнули через смещение 8, поскольку там должен
# лежать регистр sp, который ранее сохранили в mscratch.
# Мы запишем его на стек чуть позже.
sw t1,16(sp)
sw t2,20(sp)
sw a0,24(sp)
sw a1,28(sp)
sw a2,32(sp)
sw a3,36(sp)
sw a4,40(sp)
sw a5,44(sp)
sw a6,48(sp)
sw a7,52(sp)
sw t3,56(sp)
sw t4,60(sp)
sw t5,64(sp)
sw t6,68(sp)
sw t0, 12(sp) # Мы перепрыгнули через смещение 8, поскольку там должен
# лежать регистр sp, который ранее сохранили в mscratch.
# Мы запишем его на стек чуть позже.
sw t1, 16(sp)
sw t2, 20(sp)
sw a0, 24(sp)
sw a1, 28(sp)
sw a2, 32(sp)
sw a3, 36(sp)
sw a4, 40(sp)
sw a5, 44(sp)
sw a6, 48(sp)
sw a7, 52(sp)
sw t3, 56(sp)
sw t4, 60(sp)
sw t5, 64(sp)
sw t6, 68(sp)
# Кроме того, мы сохраняем состояние регистров прерываний на случай, если
# произойдет еще одно прерывание.
csrr t0,mscratch
csrr t1,mepc
csrr a0,mcause
sw t0,8(sp)
sw t1,72(sp)
sw a0,76(sp)
# произойдет ещё одно прерывание.
csrr t0, mscratch
csrr t1, mepc
csrr a0, mcause
sw t0, 8(sp)
sw t1, 72(sp)
sw a0, 76(sp)
# Вызов высокоуровневого обработчика прерываний
# call int_handler
# Вызов высокоуровневого обработчика прерываний.
# Для того чтобы программа скомпоновалась, где-то должна быть описана
# функция именно с таким именем.
call int_handler
# Восстановление контекста. В первую очередь мы хотим восстановить CS-регистры,
# на случай, если происходило вложенное прерывание. Для этого, мы должны
# вернуть исходное значение указателя стека прерываний. Однако его нынешнее
# значение нам еще необходимо для восстановления контекста, поэтому мы
# значение нам ещё необходимо для восстановления контекста, поэтому мы
# сохраним его в регистр a0, и будем восстанавливаться из него.
mv a0,sp
lw t1,72(a0)
addi sp,sp,80
csrw mscratch,sp
csrw mepc,t1
lw ra,4(a0)
lw sp,8(a0)
lw t0,12(a0)
lw t1,16(a0)
lw t2,20(a0)
lw a1,28(a0) # Мы пропустили a0, потому что сейчас он используется в
# качестве указателя на верхушку стека и не может быть
# восстановлен.
lw a2,32(a0)
lw a3,36(a0)
lw a4,40(a0)
lw a5,44(a0)
lw a6,48(a0)
lw a7,52(a0)
lw t3,56(a0)
lw t4,60(a0)
lw t5,64(a0)
lw t6,68(a0)
lw a0,40(a0)
lw t1, 72(a0)
lw t2, 76(a0)
addi sp, sp, 80
csrw mscratch, sp
csrw mepc, t1
csrw mcause, t2
lw ra, 4(a0)
lw sp, 8(a0)
lw t0, 12(a0)
lw t1, 16(a0)
lw t2, 20(a0)
lw a1, 28(a0) # Мы пропустили a0, потому что сейчас он используется в
# качестве указателя на верхушку стека и не может быть
# восстановлен.
lw a2, 32(a0)
lw a3, 36(a0)
lw a4, 40(a0)
lw a5, 44(a0)
lw a6, 48(a0)
lw a7, 52(a0)
lw t3, 56(a0)
lw t4, 60(a0)
lw t5, 64(a0)
lw t6, 68(a0)
lw a0, 40(a0)
# Выход из обработчика прерывания
mret