Тема прерываний GD32VF103 не раскрыта. У него есть режим с общим
входом в прерывание и с переходом сразу по таблице векторов
прерываний. 1. Для случая общего входа в прерывания обработчик как-то так выглядит (это макет кода)
void __attribute__((used, aligned(4), naked)) CommonIRQHandler()
{
riscv::StackMove(-19*4);
Nuclei::push_mcause(0);
Nuclei::push_mepc(1);
Nuclei::push_msubm(2);
// Тут сохранить регистры
Nuclei::CallIRQ(); //csrrw ra, jalsnxti, ra
__disable_irq();
// Тут восстановить регистры
riscv::CSR<Nuclei::CSR_REGS::msubm>::write(riscv::StackRead(8));
riscv::CSR<Nuclei::CSR_REGS::mepc>::write(riscv::StackRead(4));
riscv::CSR<Nuclei::CSR_REGS::mcause>::write(riscv::StackRead(0));
riscv::StackMove(19*4);
riscv::mret();
}
При таком подходе обработчик прерываний можно naked делать. Способ самый простой, но не самый быстрый - полное сохранение всех регистров избыточно.
2. При векторизированной обработке каждый обработчик можно как-то так сделать
template <typename F>
void TVecoredIRQFunc(F f)
{
riscv::StackMove(-12);
Nuclei::push_mcause(0);
Nuclei::push_mepc(1);
Nuclei::push_msubm(2);
__enable_irq();
f();
__disable_irq();
riscv::CSR::write(riscv::StackRead(8));
riscv::CSR::write(riscv::StackRead(4));
riscv::CSR::write(riscv::StackRead(0));
riscv::StackMove(12);
}
С виду всё красиво, но реально f() будет сохранять свои регистры до riscv::StackMove(-12) что удлиняет время нахождения в состоянии запрета прерываний. Плюс лишняя StackMove в этом же состоянии. Тут напрашивается чтобы пролог и эпилог считал компилятор, тогда это будет работать быстрее.