К1921ВГ015 общее

32-разрядные микроконтроллеры разработки АО "НИИЭТ"

Модераторы: ea, dav, bkolbov, Alis, pip, _sva_

RabidRabbit
Сообщения: 17
Зарегистрирован: 10 июн 2025, 12:11
Предприятие: HomeWork

Re: К1921ВГ015 общее

Сообщение RabidRabbit »

Vcoder писал(а): 21 июн 2025, 22:26 Нашёл описание интересующих регистров в документах "riscv-privileged". Осталось понять, какой из двух относится к нашему МК. :)
Ровно два регистра - mtime и mtimecmp.
Vcoder
Сообщения: 22
Зарегистрирован: 01 май 2025, 14:50
Предприятие: .
Откуда: Уфа

Re: К1921ВГ015 общее

Сообщение Vcoder »

Был несколько озадачен, что если в примере "mtimer" включить TMR32 одновременно с mtimer, то при запуске оно зависает наглухо. Однако если mtimer не запускать (закомментировать вызов mtimer_init() в main() ), то всё работает.
Дело оказалось в следующем.

Когда mtimer не инициализируется, вызов TMR32_IRQHandler() происходит по следующему пути (call stack):

Код: Выделить всё

TMR32_IRQHandler@0x400002f0 (main.c:140)
PLIC_MachHandler@0x4000049a (platform/source/plic.c:165)
trap_handler@0x40000ba2 (platform/source/plic.c:219)
trap_entry@0x40000200 (platform/source/startup_k1921vg015.S:262)
При этом в обработчике trap_handler() происходит безусловный вызов PLIC_MachHandler(), в котором уже проверяется, назначен ли соответствующий прерыванию обработчик, и если да, то вызывается

Если же инициализировать mtimer, то в функции mtimer_init() через вызов riscv_irq_set_handler() в массив riscv_handler_map[] под седьмым номером записывается адрес функции MTIMER_IRQHandler(), а затем вызывается riscv_irq_init(), где переопределяется глобальный обработчик прерываний на irq_entry(). В результате стек вызовов к обработчику прерывания от mtimer имеет вид:

Код: Выделить всё

MTIMER_IRQHandler@0x400003c0 (main.c:147)
irq_entry@0x40000940 (platform/source/riscv-irq.c:84)
Когда происходит прерывание от TMR32 ("машинное" в терминологии RISC-V), то по соответствующему ему индексу 11 в массиве riscv_handler_map[] оказываются нули, и программа попадает в бесконечный цикл в irq_entry().

Получается, в зависимости от того, какой таймер используется, применяются разные подходы к обработке прерываний. В связи с этим у меня вопрос: а как правильно то?
RabidRabbit
Сообщения: 17
Зарегистрирован: 10 июн 2025, 12:11
Предприятие: HomeWork

Re: К1921ВГ015 общее

Сообщение RabidRabbit »

Vcoder писал(а): 22 июн 2025, 11:43 Получается, в зависимости от того, какой таймер используется, применяются разные подходы к обработке прерываний. В связи с этим у меня вопрос: а как правильно то?
Сморите, системный таймер - это часть ядра и у него есть отдельный признак, что прерывание именно от сиситемного таймера. Для всего остального (TMR32, DMA и прочее) - признак ровно один - "внешнее прерывание", и обрабоатывать нужно отдельно, используя PLIC.

А то, что в примере "mtimer" ошибка - так то бывает :)
Вот проверенный (во всяком случае у меня) пример https://gitflic.ru/project/rabidrabbit/ ... nch=master с 16-битным таймером TMR0 (по прерыванию от него мигает светодиод) и системным таймером, по прерыванию от которого который обновляется счётчик миллисекунд, по которому устроена задержка между выводом строк в UART0.
Vcoder
Сообщения: 22
Зарегистрирован: 01 май 2025, 14:50
Предприятие: .
Откуда: Уфа

Re: К1921ВГ015 общее

Сообщение Vcoder »

RabidRabbit писал(а): 22 июн 2025, 12:57Сморите, системный таймер - это часть ядра и у него есть отдельный признак, что прерывание именно от сиситемного таймера. Для всего остального (TMR32, DMA и прочее) - признак ровно один - "внешнее прерывание", и обрабоатывать нужно отдельно, используя PLIC.
Да, я уже понял, что при прерывании всегда осуществляется переход на один определённый адрес, и там уже обработчик должен разбираться с тем, что произошло. Но сейчас я не об этом.

Если в примере "mtimer" закомментировать инициализацию mtimer-а, при возникновении прерывания управление передаётся обработчику в ассемблерном файле. Там происходит сохранение контекста (макрос context_save) и регистра mstatus, а затем вызов обработчика следующего уровня, где уже проверяется, прерывание это от ядра или от периферии. И т. д. После завершения прерывания происходит восстановление контекста и mstatus.

Однако если включить инициализацию mtimer, вместо обработчика в ассемблерном файле подставляется обработчик в riscv-irq.c. Как я понял, сохранение контекста и mstatus там не выполняется. Во всяком случае явно.

Мой вопрос заключается как раз в том, насколько такая замена обработчика допустима. Не случится ли однажды неожиданное поведение программы, если будет вызываться обработчик, в котором контекст и mstatus не сохраняются?
RabidRabbit
Сообщения: 17
Зарегистрирован: 10 июн 2025, 12:11
Предприятие: HomeWork

Re: К1921ВГ015 общее

Сообщение RabidRabbit »

Vcoder писал(а): 22 июн 2025, 13:56 Мой вопрос заключается как раз в том, насколько такая замена обработчика допустима. Не случится ли однажды неожиданное поведение программы, если будет вызываться обработчик, в котором контекст и mstatus не сохраняются?
В примере mtimer явная ошибка, так как там для обработки прерывания подставляется адрес функции void MTIMER_IRQHandler(), которая не объявлена, как обработчик прерывания. Если сделать так "__attribute__((interrupt)) void MTIMER_IRQHandler()" и добавить в файле main.c примера mtimer после строки
riscv_irq_set_handler(RISCV_IRQ_MTI, MTIMER_IRQHandler);
строку
riscv_irq_set_handler(RISCV_IRQ_MEI, PLIC_MachHandler);
теоретически, должно заработать и одновременно. Т.е. этот пример (в том виде, в каком он предоставляется) не предназначен для одновременного использования прерываний от TMR32 и от системного таймера, а видимо получен "копипастой" примера от TMR32.
Т.е. по норме обработчик должен быть ровно один :) Опять же можете помотреть пример по ссылке, которую я закидывал в предыдущем ответе.
RabidRabbit
Сообщения: 17
Зарегистрирован: 10 июн 2025, 12:11
Предприятие: HomeWork

Re: К1921ВГ015 общее

Сообщение RabidRabbit »

RabidRabbit писал(а): 22 июн 2025, 16:39 В примере mtimer явная ошибка, так как там для обработки прерывания подставляется адрес функции void MTIMER_IRQHandler(), которая не объявлена, как обработчик прерывания. Если сделать так "__attribute__((interrupt)) void MTIMER_IRQHandler()"
Поспешил. Не так "__attribute__((interrupt)) void MTIMER_IRQHandler()" а так "__attribute__((interrupt)) void irq_entry (void)" (в файле riscv-irq.c)
Vcoder
Сообщения: 22
Зарегистрирован: 01 май 2025, 14:50
Предприятие: .
Откуда: Уфа

Re: К1921ВГ015 общее

Сообщение Vcoder »

RabidRabbit писал(а): 22 июн 2025, 16:39Опять же можете помотреть пример по ссылке, которую я закидывал в предыдущем ответе.
Посмотрел, походил отладчиком.
У вас тоже прерывание направляется на сишную функцию trap_handler(). Данная функция реализована примерно так, как я себе и представлял: проверяем тип прерывания (таймер, периферия, софтовое) и вызываем соответствующий обработчик. И у вас также не выполняется сохранение контекста и mstatus, как и в примере НИИЭТ "mtimer" при выборе таймера mtimer. Но которое в НИИЭТовском примере выполняется при выборе таймера TMR32.

В общем, мой вопрос о том, насколько необходим этот код, расположенный в "startup_k1921vg015.S" по метке "trap_entry", всё ещё актуален.

Кстати RabidRabbit, откуда взяли исходники, которые в каталоге "libbaremetal"? В НИИЭТовских примерах я их не встречал.
RabidRabbit
Сообщения: 17
Зарегистрирован: 10 июн 2025, 12:11
Предприятие: HomeWork

Re: К1921ВГ015 общее

Сообщение RabidRabbit »

Vcoder писал(а): 22 июн 2025, 19:00 Посмотрел, походил отладчиком.
У вас тоже прерывание направляется на сишную функцию trap_handler(). Данная функция реализована примерно так, как я себе и представлял: проверяем тип прерывания (таймер, периферия, софтовое) и вызываем соответствующий обработчик. И у вас также не выполняется сохранение контекста и mstatus, как и в примере НИИЭТ "mtimer" при выборе таймера mtimer. Но которое в НИИЭТовском примере выполняется при выборе таймера TMR32.
Задайте себе вопрос, зачем нужно сохранять mstatus? :) Я вижу только пару причин - чтобы разрешить вложенные прерывания или чтобы сохранять "состояние" процесса при переключении процессов в многозадачной ОС. Ни то, ни другое в этом примере не используется, значит и сохранять mstatus никакого смысла нет, т.к. в обработчике прерывания я не собираюсь менять состояние этого регистра.
Далее - что Вы понимаете под "сохранением контекста"? В данном случае при входе в обработчик прерывания нужно сохранить на стеке содержимое тех регистров, которые будет использовать код самого обработчика прерывания, чтобы перез выходом загрузить эти значения обратно и вернуться в точку, откуда продолжится прерванное выполнение так сказать "без следов", не нарушив дальнейшее исполнение прерванного кода. И такое сохранение регистров обеспечивает атрибут interrupt, указанный для функции trap_handler(), кроме этого, этот атрибут обеспечивает выход из функции trap_handler() командой mret, вместо команды ret.
Vcoder писал(а): 22 июн 2025, 19:00 В общем, мой вопрос о том, насколько необходим этот код, расположенный в "startup_k1921vg015.S" по метке "trap_entry", всё ещё актуален.
На мой взгляд так - если Вы будете использовать обработчик прерываний из startup_k1921vg015.S - то и тот код нужно оставить.
Vcoder писал(а): 22 июн 2025, 19:00 Кстати RabidRabbit, откуда взяли исходники, которые в каталоге "libbaremetal"? В НИИЭТовских примерах я их не встречал.
"Потырено" понемногу отовсюду, включая musl libc, picojpeg, исходники ардуиновских библиотек и прочее в github. Что-то подсмотрено в НИИЭТовских исходниках, ибо они хоть как-то дополняют довольно скупую на подробности документацию.
Vcoder
Сообщения: 22
Зарегистрирован: 01 май 2025, 14:50
Предприятие: .
Откуда: Уфа

Re: К1921ВГ015 общее

Сообщение Vcoder »

RabidRabbit писал(а): 22 июн 2025, 19:52Задайте себе вопрос, зачем нужно сохранять mstatus? :)
Это было первое, что я сделал. :) Но моих познаний в тонкостях работы с RISC-V недостаточно для ответа.
RabidRabbit писал(а): 22 июн 2025, 19:52Далее - что Вы понимаете под "сохранением контекста"?
Ровно то, что делает макрос "context_save" в файле memasm.h (строка 104). Опять же, моих знаний в ассемблере недостаточно, чтобы точно понять происходящее, но в целом похоже что да - сохранение регистров.
RabidRabbit
Сообщения: 17
Зарегистрирован: 10 июн 2025, 12:11
Предприятие: HomeWork

Re: К1921ВГ015 общее

Сообщение RabidRabbit »

Vcoder писал(а): 23 июн 2025, 08:25 Опять же, моих знаний в ассемблере недостаточно, чтобы точно понять происходящее, но в целом похоже что да - сохранение регистров.
Это дело наживное :) Проще всего собирать с флагами -O0 -g и потом смотреть листинг через например
riscv64-unknown-elf-objdump -dS builded.elf | less
При -O0 компилятор буквально следует правилам языка, и по командам достаточно легко разобраться.
Ответить

Вернуться в «32-разрядные микроконтроллеры»