|
Выход из handler-mode в cortex-m3 |
|
|
|
May 12 2014, 16:17
|
Участник

Группа: Участник
Сообщений: 56
Регистрация: 12-01-14
Из: Омск
Пользователь №: 80 002

|
Видимо действительно у меня нет понимания. Я решаю, извините за каламбур, задачу переключения задач в ассемблере. Для этого хотелось использовать прерывание от системного таймера, так как там вполне ясный и понятный режим срабатывания (время прошло - сработало прерывание - задача сменилась). Про PendSV мне удалось найти только информацию о том, что он пригодится при переключении задач. Но то как его вызывать, как он срабатывает и что делает - нет. Каким образом тогда построить стэк, чтобы при записи числа EXC он правильно подцеплялся?
Сообщение отредактировал Omnicake - May 12 2014, 16:20
|
|
|
|
|
May 12 2014, 17:03
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(Omnicake @ May 12 2014, 20:17)  Видимо действительно у меня нет понимания. Я решаю, извините за каламбур, задачу переключения задач в ассемблере. Для этого хотелось использовать прерывание от системного таймера, так как там вполне ясный и понятный режим срабатывания (время прошло - сработало прерывание - задача сменилась). Про PendSV мне удалось найти только информацию о том, что он пригодится при переключении задач. Но то как его вызывать, как он срабатывает и что делает - нет. Каким образом тогда построить стэк, чтобы при записи числа EXC он правильно подцеплялся? Понимаю тягу к ассемблеру Cortex-M, но считаю, что это всего лишь инструмент. Для переключения контекста не обязательно писать на Си или Асм - нужно разобраться в архитектуре. SysTick это всего лишь таймер, одинаковый для всех производителей микроконтроллеров на ядре Cortex-M. Для переключения задач Вам нужно либо в задаче вызавать системную функцию, в которой произойдет переключение. Либо переключить задачу по прерыванию. В том числе от таймера. В том числе от таймера SysTick. Можно переключать задачи в SysTick, но в случае, если SysTick у Вас с самым низким приоритетом - это гарантирует, что Вы точно знаете положение стекового кадра main. Можно использовать два стека, что в какой-то степени решит проблему вложенных прерываний. Но когда Вы сделаете такой переключатель задач по таймеру, вряд ли легко сможете переключать задачи где-либо еще. Задачи в некоторый случаях (где есть ассинхронная пауза) будут "тупить" до момента сработки таймера SysTick. Если в конце SysTick взводить PendSv (ровно как и в функциях ядра), то переключение будет: - гарантированно происходить после каждого срабатывания SysTick; - в функциях ядра; - гарантированно происходить до момента передачи управления в main. Идеально. Что при таком подходе Вам не нравится? На Асме красиво реализуется.
|
|
|
|
|
May 12 2014, 17:56
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
1. PendSv - это просто особое прерывание, приоритет которого можно поставить минимальным, это позволит вам быть уверенным что в него вы попали из задачи а не из другого прерывания. В вашем случае PendSV и Прерывания таймера не различимы, задайте таймеру минимальный приоритет, и будет почти тоже самое. То есть во взрослых системах PendSV вызывается во многих случаях, те только через заданное время после прошлого вызова, у вас же только так. 2. Цитата LDR sp, [r1,#24];Загрузил стэк программы MOVW R2,#0x00d0; в котором по адресу 0x200000d0 MOVT R2,#0x2000; положил число 0xFFFFFFF9 LDR pc, [r2]; Загрузил его в PC чет я не понимаю  ... Видать я как то непонятно пишу... Попробую еще раз. если вы находитесь в hendlre режиме, как только вы в счетчик команд запишите 0xFFFFFFF9, обращаю внимание именно в счетчик команд, ни в стэк, ни в память, а именно в него. То процессор запустить процедуру выхода из hendler режима в thread режим (он не перейдет по указанному адресу, а запустить процедуру). В ходе этой процедуры процессор из стэка восстановит значения счетчика команд куда ему надо идти. При этом процессор будет использовать главный стэк в случае если на конце 9, и стэк программ если на конце D. В стэке к моменту записи магического числа должно лежать правильной последовательности PC, LR, r0-r3, и чего-то там еще, то есть полный набор сохраняемых данных для входа в прерывание. забудьте про регистр LR - он строго говоря вам не нужен, без вложенных прерываний, и без надобности вернуться в старую задачу он в целом нафиг не сдался... вам надо подменить указатель стека на стэк той задачи в какую вы идете, в стеке этой задачи должна лежать правильная структура, и записать магическое число. После этого проц сам из указаного стека выберет значение счетчика команд и пойдет по задаче. В следующем прерывании, процессор сам сохранит счетчик команд на каком моменте он остановился. И когда опять дойдет до вызова этой задачи, счетчик команд будет восстановлен из стэка. То есть в начале работы вам надо сделать задачи со своими локальными стэками положить в них структуру которая кладется когда вызовиться прерывание из этой задача, в место где хранится РС положить начало вашей задачи. далее в прерывании вам надо, сохранять PSP задачи из которой пришли, далее РSP устанавливать на стэк нвоой задачи, и записывать магическое число. Все!
|
|
|
|
|
May 13 2014, 02:10
|
Участник

Группа: Участник
Сообщений: 56
Регистрация: 12-01-14
Из: Омск
Пользователь №: 80 002

|
Цитата вам надо сделать задачи со своими локальными стэками положить в них структуру которая кладется когда вызовиться прерывание из этой задача, в место где хранится РС положить начало вашей задачи. Локальные стэки я задал, в каждой задаче у меня прописано Stack_task1[512]. LDR sp, [r1,#24] как раз и грузить адрес метки Stack_task1. А по поводу Цитата если вы находитесь в hendlre режиме, как только вы в счетчик команд запишите 0xFFFFFFF9, обращаю внимание именно в счетчик команд, ни в стэк, ни в память, а именно в него. Может я чего-то не увидел, но напрямую через команду MOV в PC на Cortex M3 вроде бы писать нельзя, компилятор мне выдает ошибку. Да и в инструкции написано, что грузятся значения в PC только через LDR или POP, а для этого нужно магическое число куда-то положить (я и положил его в стэк задачи).
|
|
|
|
|
May 13 2014, 04:44
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
обращаемся к первоисточнику http://infocenter.arm.com/help/index.jsp?t...9125006491.html1. есть мнение что LDR sp - изменит не PSP а MSP, вернее через sp вы вообще меняете 13 регистр, который по сути есть только отображение указателя стэка. А надо менять именно psp или msp 2. положили ли вы в стэк задач http://infocenter.arm.com/help/index.jsp?t...a/Babefdjc.html структуру как на рисуночке с указателями и прочей байдой, где в РС лежит первая команда задачи? А указатель показывает на последнюю непустую ячейку 3. Exception return occurs when the processor is in Handler mode and executes one of the following instructions attempts to set the PC to an EXC_RETURN value: an LDM or POP instruction that loads the PC an LDR instruction with PC as the destination a BX instruction using any register. EXC_RETURN - это магическое число. Причем 0xFFFFFFF9 - это возврат через MSP, а 0xFFFFFFFD это возврат через PSP Третий пункт вы вроде бы выполнили правильно (хотя я воспользовался бы командой BX, наверное), значит остается вопросы к 1, 2 пунктам....
|
|
|
|
|
May 13 2014, 05:34
|
Участник

Группа: Участник
Сообщений: 56
Регистрация: 12-01-14
Из: Омск
Пользователь №: 80 002

|
1. Ну у меня и отображается активным MSP все это время, и до прерывания и внутри него. Командой LDR я меняю указатель на метку Stack_task1 - массив из 512 чисел, который я взял за стэк задачи. 2. Как корректно класть туда эту структуру? Заранее прописывать ее в массив в самой задаче или загонять через PUSH необходимые регистры?
|
|
|
|
|
May 13 2014, 06:37
|
Участник

Группа: Участник
Сообщений: 56
Регистрация: 12-01-14
Из: Омск
Пользователь №: 80 002

|
Цитата В LR заносится магическое число. Если вы наблюдаете в LR адрес возврата - выкиньте свой симулятор. Или вы попадаете в исключение каким-то нестандартным методом, вроде прямого вызова функции? Тут возможно я недостаточно подробно описал, при входе в прерывание там действительно лежит число EXC_RETURN но затем, из за того что в обработчике у меня стоит вызов другой программы (а именно переключателя задач), он уходит на ту программу, помещая в LR адрес возврата.
|
|
|
|
|
May 13 2014, 08:11
|
Участник

Группа: Участник
Сообщений: 56
Регистрация: 12-01-14
Из: Омск
Пользователь №: 80 002

|
Видимо это Keil uVision и делает: после того как я добавил ассемблерные вставки в main, написанный на C, он создал автоматически файл main.s и там функция Код SysTick_Handler { scheduler(); } Выглядит вот так Код SysTick_Handler PUSH {r4,lr} BL systick POP {r4,pc} Однако из-за того, что из systick я прыгаю на другую программу, POP {r4,pc} пропускается. Значит, судя по всему, нужно делать это внутри шедулера.
|
|
|
|
|
May 13 2014, 08:26
|
Участник

Группа: Участник
Сообщений: 56
Регистрация: 12-01-14
Из: Омск
Пользователь №: 80 002

|
Хорошо. Спасибо.
|
|
|
|
|
May 13 2014, 11:47
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(Omnicake @ May 13 2014, 11:11)  Однако из-за того, что из systick я прыгаю на другую программу, POP {r4,pc} пропускается. Так вы вернитесь из "другой подпрограммы". Или, если не собираетесь возвращаться, переходите на SysTick не по BL, а по B. И в LR останется магическое число.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
May 13 2014, 12:53
|
Участник

Группа: Участник
Сообщений: 56
Регистрация: 12-01-14
Из: Омск
Пользователь №: 80 002

|
Починил, правда, для меня, это шаманством осталось. Я говорил выше, что компилятор создает файл main.s вместо main.c, а так как внутри него я не мог ничего редактировать (он создан компилятором) я решил просто скопировать его и заменить им файл main.c, после этого когда я запустил в шедулере этот код: Код LDR r0, =TaskPointer LDR r0, [r0] LDR r0, [r0] MOV r1, r0 LDR sp, [r1,#24] BX lr При этом стэк для задачи я задал вручную вот так: Код int Stack_task1[512]={0x00000000,0xfffffff9,0x00000007,0xe000e000,0x200021e8,0x200021e8,0x200021 c4,0x08000211,0x08000212,0x21000000}; поместив в него нужные метки и то самое число. С этим стэком я уже запускал до этого (на main.c) и ничего не работало, однако тут удалось: после BX LR он вышел из шедулера на хэндлер, выполнил POP {pc} и прыгнул на задачу. Метод скорее всего "напролом" но от него уже можно плясать. Спасибо всем за советы и помощь, буду работать дальше.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|