Перейти к содержанию
    

Перенос векторов прерываний в nRF51822

Подскажите если есть идеи, где мог сделать ошибкy.

 

Недавно подключить к готовому проекту Cortex M0 bootloader. Перенес вектора прерываний приложения из начала флеш в середину, сделал ремап векторов. scmRTOS запускается успешно но стал вылетать в HardFault_Handler после вызова channel.push() в прерывании UART.

 

Вылет происходит когда накладываюя 2 прерывания.

От системного таймера и вызова метода push().

 

Поиск выхода в точку падения по методу Сергей Борщ, в дизассемблере по шагам на выход из обработчика стабильно приводит к позиции scmRTOS: while(CurProcPriority != SchedProcPriority);

 

Размер стека я менял - не помогает.

Если медленно шагать по щагам в дизасме, найти точку вылета не получается. Все работает.

 

Я знаю, что каналы в прерываниях использовать не рекомендовано. Но программа отлично и долго работала в этом варианте. И только сдвиг векторов из начала flash порушил работоспособность.

 

Неделя борьбы с багом ни к чему не привела, может у кого есть свежая идея. Спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подскажите если есть идеи, где мог сделать ошибкy.

1) Вы перед запихиванием в канал проверяете наличие в нем свободного места?

2) Какая ассемблерная инструкция вызывает падение, какие значения имеют используемые в ней регистры?

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1. Свободное место проверяется, канал никогда не переполняется.

 

2. Не могу точно посмотреть. После выхода из обработчика и ввода в дебагер адреса из sp, оказываюсь на адресе в ram где лежит os_kernel. Никаких асм инструкций ..

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2. Не могу точно посмотреть. После выхода из обработчика и ввода в дебагер адреса из sp, оказываюсь на адресе в ram где лежит os_kernel. Никаких асм инструкций ..
Недопонял. Вы же писали, что исключение происходит при выполнении кода "while(CurProcPriority != SchedProcPriority);". Посмотрите конкретную инструкцию в окне дизассемблера. И что за шаманство с вводом адреса из sp?

 

Вопрос третий - вылетает при первом же обращении к push()? Не может такого быть, что у вас прерывание возникает раньше, чем запустилась ОС?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

>> что за шаманство с вводом адреса из 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

All pictures by one zip

 

На мой взгляд целесообразно ввести в port раздельное заполнение тестовым паттерном.

#define scmRTOS_STACK_PATTERN 0xAAAAAAAAUL

#define scmRTOS_PROCESS_PATTERN 0xBBBBBBBBUL

Изменено пользователем sevstels

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нашел данную методику но она не помогает.

По данной методике результат можно посмотреть на картинке.

На всех ваших картинках SP_main имет значения порядка 0x2000043x, то есть стек прерываний расположен где-то в начале ОЗУ и, судя по всему, довольно небольшого размера. Такое ощущение, что этот стек переполняется и затирает объект OS::Kernel, а дальше, при перепланировке, происходит унос черт знает куда из-за загрузки мусора в PC.

 

На картинке test-3 в архиве (по прямой ссылке другая картинка) отладчик показывает адрес OS:TKernel равный 0x0000122B, но этого не может быть - в этих адресах нет ОЗУ.

 

 

Расскажите более подробно о ремапе векторов. Возможно вы в нем затираете какие-то данные.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Недавно подключить к готовому проекту Cortex M0 bootloader. Перенес вектора прерываний приложения из начала флеш в середину, сделал ремап векторов. scmRTOS запускается успешно но стал вылетать в HardFault_Handler после вызова channel.push() в прерывании UART.

Немного не вяжется: у вас Cortex M0 или Cortex M0+ ?

У Cortex M0 нет регистра VTOR, соответственно нельзя перенести таблицу векторов в середину флеш.

Можно только в начало ОЗУ.

Может дело в этом?

Тогда при первом прерывании программа и будет улетать неизвестно куда.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Немного не вяжется: у вас Cortex M0 или Cortex M0+ ?

У Cortex M0 нет регистра VTOR, соответственно нельзя перенести таблицу векторов в середину флеш.

У M0+ вроде тоже нет. Но у них есть процедура отражения ОЗУ на начало памяти (remap), таким образом можно скопировать вектора в начало ОЗУ откуда угодно.

Можно только в начало ОЗУ.
Собственно, да.

 

Меня очень смущает расположение основного стека близко к началу ОЗУ. Обычно его располагают в самом конце, чтобы он мог расти максимально далеко.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Немного не вяжется: у вас 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 вызывает выдачу моря ошибок линковки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Помогите исправить и проверить идею Практически на 90% работает :)

Не слишком углублялся в вашу идею, уж больно она нестандартная :laughing:

А чем вам не угодил стандартный способ ремапа векторов прерываний с копированием в начало ОЗУ?

При этом создаются два независимых проекта, можно отлаживаться как отдельно, так и вместе (в ИАРе).

Ссылки на примеры на STM32, но принципы то одинаковы:

Bootloader on STM32F0

Interrupt vector table relocation on cortex M0

 

з.ы. зря вы закинули свой вопрос в эту тему, судя по всему, ваши проблемы никак не касаются scmRTOS...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

>> чем не угодил стандартный способ ремапа

Не нашел функцию SYSCFG_MemoryRemapConfig.

Похоже вы правы - глянул, System configuration controller (SYSCFG) это периферия STM32F0, она не входит в ядро Cortex-M0. Так что в вашем nRF51822 его может и не быть. А что производитель рекомендует для ремапа? Тот метод, что вы применили?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Производитель еще тот крендель. Они ничего не рекомендуют кроме своих сборок. Обойти их безумный индуский код стороной - было лучшее решение в данном случае.

 

Вероятно они поленились и в их 'авторско горяче-финской' модификации ядра никак не предусмотрен перенос векторов. Теперь каждый спасется как может...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...