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

View File

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

View File

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