|
Перенос векторов прерываний в nRF51822 |
|
|
|
Mar 19 2018, 14:52
|

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

|
QUOTE (sevstels @ Mar 19 2018, 15:55)  Подскажите если есть идеи, где мог сделать ошибкy. 1) Вы перед запихиванием в канал проверяете наличие в нем свободного места? 2) Какая ассемблерная инструкция вызывает падение, какие значения имеют используемые в ней регистры?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 19 2018, 18:45
|

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

|
QUOTE (sevstels @ Mar 19 2018, 17:52)  2. Не могу точно посмотреть. После выхода из обработчика и ввода в дебагер адреса из sp, оказываюсь на адресе в ram где лежит os_kernel. Никаких асм инструкций .. Недопонял. Вы же писали, что исключение происходит при выполнении кода "while(CurProcPriority != SchedProcPriority);". Посмотрите конкретную инструкцию в окне дизассемблера. И что за шаманство с вводом адреса из sp? Вопрос третий - вылетает при первом же обращении к push()? Не может такого быть, что у вас прерывание возникает раньше, чем запустилась ОС?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 20 2018, 05:14
|

Знающий
   
Группа: Участник
Сообщений: 626
Регистрация: 3-12-07
Пользователь №: 32 910

|
>> что за шаманство с вводом адреса из sp? Нашел данную методику но она не помогает. 1. Первым важным для нас регистром является счетчик команд. Переменная pc содержит значение счетчика команд где содержится адрес инструкции, которая выполнялась, когда произошла Hard Fault ошибка (или другая ошибка). Чтобы найти инструкцию по адресу, содержащемуся в переменной pc, откройте окно Disassembly (IAR) в отладчике и вручную введите адрес в окно Go to для просмотра asm инструкций по этому адресу. 2. Далее вручную установите точку останова в Disassembly отладчике или точку прерывания выполнения или доступа по этому адресу. С установленной точкой останова перезапустите приложение, чтобы увидеть, к какой строке C/C++ кода относится инструкция. 3. Код обработчика HardFault_Handler() #pragma section = "CSTACK" #pragma optimize=none extern "C" void HardFault_Handler(void) { volatile uint32_t r0; volatile uint32_t r1; volatile uint32_t r2; volatile uint32_t r3; volatile uint32_t r12; volatile uint32_t lr; volatile uint32_t pc; volatile uint32_t psr; uint32_t *pStackAddress = (uint32_t*)__section_begin("CSTACK"); r0 = pStackAddress++; r1 = pStackAddress++; r2 = pStackAddress++; r3 = pStackAddress++; r12 = pStackAddress++; lr = pStackAddress++; //Link register pc = pStackAddress++; //Program counter psr = pStackAddress; //Program status register //When the following line is hit, the variables contain the register values volatile int i = 0; while(i==0); //Stop here } По данной методике результат можно посмотреть на картинке. Методика 2. Шагаем в Disassembly из обработчика. Момент останова в обработчике > картинкаМомент выхода из обработчика > картинка>>Вопрос третий - вылетает при первом же обращении к push()? Обычно после первого же, но иногда со второго. >>прерывание возникает раньше, чем запустилась ОС? Исключено. Прерывания запрещены до старта OS. Прерывание UART приходит с компа по моей команде, когда уже программа запущена. Если метод push() закомментировать, то все работает, но нет связи по UART.
Сообщение отредактировал sevstels - Mar 20 2018, 07:04
--------------------
|
|
|
|
|
Mar 20 2018, 06:45
|

Знающий
   
Группа: Участник
Сообщений: 626
Регистрация: 3-12-07
Пользователь №: 32 910

|
All pictures by one zipНа мой взгляд целесообразно ввести в port раздельное заполнение тестовым паттерном. #define scmRTOS_STACK_PATTERN 0xAAAAAAAAUL #define scmRTOS_PROCESS_PATTERN 0xBBBBBBBBUL
Сообщение отредактировал sevstels - Mar 20 2018, 07:56
--------------------
|
|
|
|
|
Mar 20 2018, 14:13
|

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

|
QUOTE (sevstels @ Mar 20 2018, 07:14)  Нашел данную методику но она не помогает. По данной методике результат можно посмотреть на картинке. На всех ваших картинках SP_main имет значения порядка 0x2000043x, то есть стек прерываний расположен где-то в начале ОЗУ и, судя по всему, довольно небольшого размера. Такое ощущение, что этот стек переполняется и затирает объект OS::Kernel, а дальше, при перепланировке, происходит унос черт знает куда из-за загрузки мусора в PC. На картинке test-3 в архиве (по прямой ссылке другая картинка) отладчик показывает адрес OS:TKernel равный 0x0000122B, но этого не может быть - в этих адресах нет ОЗУ. Расскажите более подробно о ремапе векторов. Возможно вы в нем затираете какие-то данные.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 20 2018, 17:46
|

Просто Che
    
Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881

|
Цитата(sevstels @ Mar 19 2018, 15:55)  Недавно подключить к готовому проекту Cortex M0 bootloader. Перенес вектора прерываний приложения из начала флеш в середину, сделал ремап векторов. scmRTOS запускается успешно но стал вылетать в HardFault_Handler после вызова channel.push() в прерывании UART. Немного не вяжется: у вас Cortex M0 или Cortex M0+ ? У Cortex M0 нет регистра VTOR, соответственно нельзя перенести таблицу векторов в середину флеш. Можно только в начало ОЗУ. Может дело в этом? Тогда при первом прерывании программа и будет улетать неизвестно куда.
|
|
|
|
|
Mar 20 2018, 18:47
|

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

|
QUOTE (Baser @ Mar 20 2018, 19:46)  Немного не вяжется: у вас Cortex M0 или Cortex M0+ ? У Cortex M0 нет регистра VTOR, соответственно нельзя перенести таблицу векторов в середину флеш. У M0+ вроде тоже нет. Но у них есть процедура отражения ОЗУ на начало памяти (remap), таким образом можно скопировать вектора в начало ОЗУ откуда угодно. QUOTE (Baser @ Mar 20 2018, 19:46)  Можно только в начало ОЗУ. Собственно, да. Меня очень смущает расположение основного стека близко к началу ОЗУ. Обычно его располагают в самом конце, чтобы он мог расти максимально далеко.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Mar 21 2018, 11:21
|

Знающий
   
Группа: Участник
Сообщений: 626
Регистрация: 3-12-07
Пользователь №: 32 910

|
Цитата(Baser @ Mar 21 2018, 02:46)  Немного не вяжется: у вас Cortex M0 или Cortex M0+ ? У Cortex M0 нет регистра VTOR, соответственно нельзя перенести таблицу векторов в середину флеш. Можно только в начало ОЗУ. Проект базируется на nRF51822, а это Cortex M0, VTOR нет. Но на мой взгляд, таблицу перенести можно. Для этого в стандартный (нулевой) адрес FLASH следует записать таблицу общую для bootloader и для application. Bootloader: *iar_startup.s file В этой таблице заменить все вектора на адрес функции обработчика прерывания. Код Address from 0x0000 SECTION .main_intvec __vector_table_main DCD sfe(CSTACK) DCD Reset_Handler DCD irq_forwarder DCD irq_forwarder DCD 0; Reserved DCD 0; Reserved DCD 0; Reserved DCD 0; Reserved ....... Далее собственная таблица векторов bootloader. Она может располагаться где угодно в пространстве FLASH, задается в настройках линкера. Any FLASH address Код SECTION .boot_intvec __vector_table DCD sfe(CSTACK) DCD Reset_Handler DCD NMI_Handler DCD HardFault_Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD PendSV_Handler DCD SysTick_Handler Далее за основной таблицей прерываний располагаем функцию форварда адресов: Linker file: place at address mem: 0xc8 {readonly section .irq_forwarder}; keep { section .irq_forwarder }; Code file: Переменная irq_table_offset может указывать смещение как во FLASH так и в RAM. Код irq_table_offset @ 0x20000000 = (unsigned long) __section_begin(".boot_intvec"); typedef void (*irq_handler_t)(void); __root void irq_forwarder(void) { //Read the number of the currently executing interrupt handler from IPSR. //See the Cortex-M0 user guide for details. uint32_t irq_interrupt_number = __get_IPSR(); //Find the address offset in the vector table //for the currently running interrupt, irq_interrupt_number * 4 uint32_t vector_table_offset = irq_interrupt_number << 2; //Get the address of the vector in the offset vector table uint32_t offset_vector_address = vector_table_offset + irq_table_offset; ///uint32_t offset_vector_address = vector_table_offset + (uint32_t)&irq_ram_table[0]; //Read the address of the IRQ handler to branch to from the offset vector irq_handler_t irq_handler=(irq_handler_t)(*(uint32_t*)offset_vector_address); //Branch to the irq_handler irq_handler(); } При запуске bootloader инициализация: irq_table_offset = (unsigned long) __section_begin(".boot_intvec"); При запуске application from bootloader инициализация: Код typedef void (*application_main_t)(void); apl_base_addr = find_application(); launch_application(apl_base_addr); void launch_application(unsigned long base_addr) { unsigned long *pData; unsigned long appl_cstack_pointer; application_main_t appl_reset_handler; //Get application CSTACK value pData = (unsigned long*)base_addr; appl_cstack_pointer = *pData++; //+4 bytes //Get Reset_Handler vector value from application intvec table appl_reset_handler = *(application_main_t*)(pData); //Set mode flag NRF_POWER->GPREGRET = DEV_APPLICATION_MODE; //Set application CStack pointer __set_SP(appl_cstack_pointer); //Jump to application Reset_Handler appl_reset_handler(); } Траблы происходят из за использования форвардера Я не смог его обьявить как __task, компилятор воспротивился, чтоб запретить сохранять на стеке адрес возврата. Видимо проблема в этой точке кода. Помогите исправить и проверить идею Практически на 90% работает  >> Меня очень смущает расположение основного стека близко к началу ОЗУ. Обычно его располагают в самом конце. Пока не могу преодолеть. Усилия перенести стек в конец RAM, яростно отвергаются линкером. Код place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; place in ROM_region { readonly }; place in RAM_region { readwrite, block CSTACK, block HEAP }; //place at end of RAM_region {readwrite}; Строка place at end of RAM_region вызывает выдачу моря ошибок линковки.
--------------------
|
|
|
|
|
Mar 21 2018, 12:20
|

Просто Che
    
Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881

|
Цитата(sevstels @ Mar 21 2018, 13:21)  Помогите исправить и проверить идею Практически на 90% работает  Не слишком углублялся в вашу идею, уж больно она нестандартная А чем вам не угодил стандартный способ ремапа векторов прерываний с копированием в начало ОЗУ? При этом создаются два независимых проекта, можно отлаживаться как отдельно, так и вместе (в ИАРе). Ссылки на примеры на STM32, но принципы то одинаковы: Bootloader on STM32F0Interrupt vector table relocation on cortex M0з.ы. зря вы закинули свой вопрос в эту тему, судя по всему, ваши проблемы никак не касаются scmRTOS...
|
|
|
|
|
Mar 21 2018, 15:13
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(sevstels @ Mar 21 2018, 20:00)  Проверял. Смещение текущего прерывания четко определяет без ошибок. И тем не менее. Попробуйте сделать пять-шесть отдельных обработчиков (думаю, для тестового приложения хватит), каждый из которых проверяет переменную "работает_приложение", и прыгает на соответствующий обработчик в приложении. Возможно по шагам оно всё хорошо работает, а без отладки, да ещё при наложении прерываний - плохо. Кстати, использовать очереди в прерываниях очень даже можно. Единственное условие - проверять, что в очереди есть место.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Mar 22 2018, 09:24
|

Просто Che
    
Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881

|
Цитата(sevstels @ Mar 21 2018, 16:35)  Производитель еще тот крендель. Они ничего не рекомендуют кроме своих сборок. Обойти их безумный индуский код стороной - было лучшее решение в данном случае.
Вероятно они поленились и в их 'авторско горяче-финской' модификации ядра никак не предусмотрен перенос векторов. Теперь каждый спасется как может... Глянул форум Нордика, действительно, есть только такое единственное предложение по ремапу векторов. Видимо и в их библиотеках применяется подобный метод, который они называют MBR. Так что метод должен работать, видимо дело в нюансах, что-то упустили. Цитата(sevstels @ Mar 21 2018, 17:00)  Проверял. Смещение текущего прерывания четко определяет без ошибок. Более того, bootloader полностью рабочий через форвардер, пишет, проверяет, запускает. Прерывания нормально используются, ничего не крэшится.
Трудности начинаются в приложении. Оно под scmRTOS... Навскидку, если очевидные вещи вы уже проверили, еще могут быть такие узкие места: 1. Еще раз проверить функции ремапа на реентерабельность, нет ли скрытых камней при обработке вложенных прерываний. 2. Разграничение физических областей кода бутлодера и приложения. Нет ли какого-нибудь ограничения чтения одной области из другой области через какие-нибудь биты защиты кода или через MPU (Memory Protection Unit). 3. На всех ли векторах прерываний в обеих таблицах есть заглушки при неиспользуемых векторах. А то может возникнуть прерывание, на которое вы не рассчитывали, а там пустота или другой код.
|
|
|
|
|
Mar 23 2018, 11:01
|

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

|
Чтобы за время вашего отпуска мысли не забылись, изложу их кратенько тут: 1) в launch_application я не заметил установки нового значения в irq_table_offset. 2) irq_forwarder писали индусы. Накой этот закат солнца вручную? "Машина должна работать, а человек - думать". С расчетом всех этих адресов прекрасно справляется компилятор : CODE typedef void (*irq_handler_t)(void);
// pointer to interrupt vectors table irq_handler_t * irq_table @ 0x20000000 = (irq_handler_t *) __section_begin(".boot_intvec");
void irq_forwarder(void) { //Branch to IPSR-th handler the irq_table irq_table[__get_IPSR()](); } 3) __root не нужен для irq_forwarder - на него есть ссылка из таблицы векторов. А если ссылки нет - то функция не нужна, ее можно выкинуть и __root тут никак не поможет.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|