theCore-embedded / theCore

theCore: C++ embedded framework
https://forgge.github.io/theCore
Mozilla Public License 2.0
81 stars 23 forks source link

Add default handlers for some ARM Cortex-M* exceptions #257

Open forGGe opened 7 years ago

forGGe commented 7 years ago

Bunch of ARM exceptions are exist that not handled within theCore:

Those exceptions if triggered and not handled by user, should produce stack trace, registry info and some status info, if possible.

Default handler verbosity must be configurable and dependable on build mode, i.e. in debug build it should print more info. Handlers override must be possible in compile time.

forGGe commented 6 years ago

Right now, theCore handles all faults in common way - it hangs:

https://github.com/forGGe/theCore/blob/047b0a560b1b4942359ee537c0c67694a12a5175/arch/arm_cm/startup_arm_cm.S#L241-L259

To be able to handle it externally, two steps must be taken:

  1. remove corresponding line from default handlers listing. Say, you want to implement UsageFault handler, you need to remove def_irq_handler UsageFault_Handler line

  2. implement C function with following signature:

    extern "C" void UsageFault_Handler(void)
    {
        /* Print registers, and other useful stuff */
    }

    Note that the ecl::bypass_puts() function must be used to print data, instead of printf(). Bypass console is specifically crafted for such purpose and can be invoked from fault/IRQ handlers.

Some additional guides:

M3one commented 6 years ago

Trying to implement the UsageFault_Handler on external interrupt example. I've modified the _startup_armcm.S file removing def_irq_handler UsageFault_Handler and its definition in vectors: I've also modified the main.cpp file of the mentioned example adding the exception's handler as follows: extern "C" void UsageFault_Handler(void) { ecl::bypass_puts("This is an Usange Fault"); } and added a division by 0 to force the fault: `int a; int b=2; int c=b;

int main() {

ecl::cout « "Hello, Embedded World!" « ecl::endl;
//Forced Fault
a=b/(c-b);`

but the program works fine. Any idea? Maybe theCore has defined this exception with a priority so low that it's never active?

forGGe commented 6 years ago

Hi @M3one !

Thanks for your feedback.

Either it is optimized out by the compiler, or handler is not called indeed.

Could you please modify it a bit:


int main()
{
    ecl::cout << "Hello, Embedded World!" << ecl::endl;
   //Forced Fault
   a=b/(c-b);`
   ecl::cout << "After Fault! << ecl::endl;
}

Meantime, I will check on my side. Stay tuned.

forGGe commented 6 years ago

Indeed, by default trap is not enabled on division by zero. Try to add there:

    *((volatile int*)0) = 42;

You should get the MemManage fault (I hope, currently I have only M0 on my table, that results in HardFault when trying to write into 0x0 address).

Let me know if that helps.

M3one commented 6 years ago

main.cpp file:


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "target.hpp"

#include <ecl/iostream.hpp>
#include <ecl/thread/utils.hpp>
#include <ecl/thread/semaphore.hpp>

#include <platform/exti_manager.hpp>

using ecl::exti_manager;
using ecl::cout;
using ecl::endl;
using ecl::semaphore;
using ecl::usr_btn; // Defined in stm32discovery.json

extern "C"
void board_init()
{
    gpio_init_generated();
}

extern "C" void UsageFault_Handler(void)
{
    ecl::bypass_puts("This is an Usange Fault");
}

void user_button_handler(void *ctx)
{
    semaphore *s = reinterpret_cast<semaphore*>(ctx);
    s->signal();
}

int main()
{

    ecl::cout << "Hello, Embedded World!" << ecl::endl;
    //Forced Fault
    *((volatile int*)0) = 42;

    ecl::bypass_puts("Working?"); //to be sure I'm not messing with files

    semaphore s;
    exti_manager::handler h;

    h.set_ctx(&s);
    h.set_cb(user_button_handler);

    exti_manager::subscribe<usr_btn>(h, exti_manager::trigger::rising);

    while (1) {
        s.wait();
        ecl::cout << "Button pressed!" << ecl::endl;
        exti_manager::unmask(h);
    }
}

Output: Welcome to theCore the_core v0.3.0.300 047b0a5-dirty Hello, Embedded World! Working? Not working :(

M3one commented 6 years ago

F4 discovery board MCU has Usage Faults no active (or masked, not sure) by default. That's why it didn't work. If a hard fault is provoke, then it does call the HardFault_Handler():

extern "C" void     HardFault_Handler()
{
    ecl::bypass_puts("This is an Hard Fault");
    for(;;);
}

void user_button_handler(void *ctx)
{
    semaphore *s = reinterpret_cast<semaphore*>(ctx);
    s->signal();
}

int main()
{

    ecl::cout << "Hello, Embedded World!" << ecl::endl;
    //Forced Fault
    typedef void (*fptr)(void);
    ((fptr)0xdeadbeaf)();

    semaphore s;
    exti_manager::handler h;
M3one commented 6 years ago

To enable hardware reporting of div0 errors we need to configure the CCR. The CCR is part of the Cortex-M’s System Control Block (SCB) and controls entry trapping of divide by zero and unaligned accesses among other things.

Source Here

forGGe commented 6 years ago

F4 discovery board MCU has Usage Faults no active (or masked, not sure) by default. That's why it didn't work. If a hard fault is provoke, then it does call the HardFault_Handler():

To enable hardware reporting of div0 errors we need to configure the CCR. The CCR is part of the Cortex-M’s System Control Block (SCB) and controls entry trapping of divide by zero and unaligned accesses among other things. Source Here

That's answer to many things. Thanks!

Could you please enable all possible faults by adding more code in platform.cpp file? In such way, we can be sure that no bugs (like division by 0, which is shame to ignore!) slips trough our fingers.

M3one commented 6 years ago

Done. It should be working. Now both divide-by-cero and unaligned memory access fault exceptions are enable. Don't know why but these faults are scaled to hard fault. I've also added more code to the main.cpp of the external interrupt example to handle all exceptions. Not yet finished but you can check the faults if you want.

forGGe commented 6 years ago

Per discussion is Telegram chat:

The fault handling will be moved in arch.cpp for the CM* architecture module. No MSP or stack unwinding will be made in first iteration of this task.

Thanks!