How to Generate Exceptions on Cortex M3?

-1

I am trying to generate exceptions like Bus Fault, Usage Fault on ARM Cortex-M3. My code for enable exceptions:

void EnableExceptions(void)
{
    UINT32 uReg = SCB->SHCSR;

    uReg |= 0x00070000;

    SCB->SHCSR = uReg;

    //Set Configurable Fault Status Register
    SCB->CFSR = 0x0367E7C3;
    //Set to 1 DIV_0_TRP register
    SCB->CCR |= 0x00000010;

    //Set priorities of fault handlers

    NVIC_SetPriority(MemoryManagement_IRQn, 0x01);

    NVIC_SetPriority(BusFault_IRQn, 0x01);

    NVIC_SetPriority(UsageFault_IRQn, 0x01);
}
    
void UsageFault_Handler(void){
    //handle
    //I've set a breakpoint but system does not hit 
}

void BusFault_Handler(void){
    //handle
    //I've set a breakpoint but system does not hit 
}

I tried to generate division by zero exception and saw the variables value as "Infinity". However system does not generate any exception on keeps running. Also tried to generate Bus Fault exception and same thing happens.

Also when I comment out to EnableExceptions function system works correctly. What is wrong with my code? Does ARM handle these kind of errors inside of the microprocessor?

c
exception
embedded
cortex-m3
asked on Stack Overflow Jul 9, 2020 by aaerciyas

3 Answers

2

Cortex-M devices use the Thumb-2 instruction set exclusively, ARM uses the least significant bit of the the branch/jump/call address to determine whether the target is Thumb or ARM code, since the Cortex-M cannot run ARM code, you can generate an BusFault exception by creating a jump to an even address.

int dummy(){ volatile x = 0 ; return x ; }

int main()
{
    typedef void (*fn_t)();
    fn_t foo = (fn_t)(((char*)dummy) - 1) ;

    foo() ;
}

The following will also work, since the call will fail before any instructions are executed, so it does not need to point to any valid code.

int main()
{
    typedef void (*fn_t)();
    fn_t foo = (fn_t)(0x8004000) ;    
    foo() ;
}

You can generate a usage fault by forcing an integer divide by zero:

int main()
{
    volatile int x = 0 ;
    volatile int y = 1 / x ;
}
answered on Stack Overflow Jul 9, 2020 by Clifford • edited Jul 9, 2020 by Clifford
1

From your comment question: How to generate any exception.

Here is one from the documentation:

Encoding T1 All versions of the Thumb instruction set.
SVC<c> #<imm8>

...

Exceptions 
SVCall.

Which I can find by searching for SVCall.

Exceptions are well documented by ARM, there are ways to cause the exceptions you listed without having to break the bus (requiring a sim an fpga or creating your own silicon), you already know the search terms for the document to find busfault and usagefault.

How ARM handles these (internally or not) is documented. Internally in this case means a lockup or not if you look, otherwise they execute the fault handler (unless of course there is a fault fetching the fault handler).

Most you can create in C without resorting to assembly language instructions, but you have to be careful that it is generating what you think it is generating:

void fun ( void )
{
  int x = 3;
  int y = 0;
  int z = x / y;
}

Disassembly of section .text:

00000000 <fun>:
   0:   4770        bx  lr

Instead you want something that actually generates the instruction that can cause the fault:

int fun0 ( int x, int y )
{
    return(x/y);
}
void fun1 ( void )
{
    fun0(3,0);
}

00000000 <fun0>:
   0:   fb90 f0f1   sdiv    r0, r0, r1
   4:   4770        bx  lr
   6:   bf00        nop

00000008 <fun1>:
   8:   4770        bx  lr

but as shown you have to be careful about where and how you call it. In this case the call was done in the same file so the optimizer had visibility to see that this is now dead code and optimized it out, so a test like this would fail to generate a fault for multiple reasons.

This is why the OP needs to provide a complete minimal example the reason why faults are not being seen is not the processor. But the software and/or test code.

Edit

A complete minimal example, everything you need but a gnu toolchain (no . This is on a stm32 blue pill an STM32F103...

flash.s

.cpu cortex-m3
.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset /* 1 Reset */
.word hang  /* 2 NMI */
.word hang  /* 3 HardFault */
.word hang  /* 4 MemManage */
.word hang  /* 5 BusFault */
.word usagefault  /* 6 UsageFault */
.word hang  /* 7 Reserved */
.word hang  /* 8 Reserved */
.word hang  /* 9 Reserved */
.word hang  /*10 Reserved */
.word hang  /*11 SVCall */
.word hang  /*12 DebugMonitor */
.word hang  /*13 Reserved */
.word hang  /*14 PendSV */
.word hang  /*15 SysTick */
.word hang  /* External interrupt 1 */
.word hang  /* External interrupt 2 */

.thumb_func
reset:
    bl notmain
    b hang
.thumb_func
hang:   b .

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.thumb_func
.globl dummy
dummy:
    bx lr

.thumb_func
.globl dosvc
dosvc:
    svc 1

.thumb_func
.globl hop
hop:
    bx r0

flash.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

fun.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
void hop ( unsigned int );

#define GPIOCBASE   0x40011000
#define RCCBASE     0x40021000
#define SHCSR       0xE000ED24

void usagefault ( void )
{
    unsigned int ra;

    while(1)
    {
        PUT32(GPIOCBASE+0x10,1<<(13+0));
        for(ra=0;ra<100000;ra++) dummy(ra);
        PUT32(GPIOCBASE+0x10,1<<(13+16));
        for(ra=0;ra<100000;ra++) dummy(ra);
    }
}

int notmain ( void )
{
    unsigned int ra;

    ra=GET32(SHCSR);
    ra|=1<<18; //usagefault
    PUT32(SHCSR,ra);

    ra=GET32(RCCBASE+0x18);
    ra|=1<<4; //enable port c
    PUT32(RCCBASE+0x18,ra);
    ra=GET32(GPIOCBASE+0x04);
    ra&=(~(3<<20));   //PC13
    ra|=  (1<<20) ;   //PC13
    ra&=(~(3<<22));   //PC13
    ra|=  (0<<22) ;   //PC13
    PUT32(GPIOCBASE+0x04,ra);

    PUT32(GPIOCBASE+0x10,1<<(13+0));
    for(ra=0;ra<200000;ra++) dummy(ra);
    PUT32(GPIOCBASE+0x10,1<<(13+16));
    for(ra=0;ra<200000;ra++) dummy(ra);

    ra=GET32(0x08000004);
    ra&=(~1);
    hop(ra);

    return(0);
}

build

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m3 -mthumb -c so.c -o so.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o so.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy -O binary so.elf so.bin

All of those command line options are not required arm-linux-gnueabi- and other flavors of gnu toolchains work just fine from several versions back to the present as I use them as a compiler, assembler and linker and don't mess with library or other stuff that varies from one flavor to another.

UsageFault The UsageFault fault handles non-memory related faults
caused by instruction execution. 

A number of different situations  cause usage faults, including: 

 • Undefined Instruction. 
 • Invalid state on instruction execution. 
 • Error on exception return. 
 • Attempting to access a disabled or unavailable coprocessor. 

 The following can cause usage faults when the processor is configured to 
 report them: 
  • A word or halfword memory accesses to an unaligned address. 
  • Division by zero. 

Software can disable this fault. If it does, a UsageFault escalates to HardFault. UsageFault has a configurable priority.

...

Instruction execution with EPSR.T set to 0 causes the invalid state UsageFault

So the test here branches to an arm address vs a thumb address and this causes a usagefault. (Can read up about the BX instruction the psr.t bit how and when it gets changed, etc in the documentation as well)

Backing up this is the stm32 blue pill. There is an led on PC13, the code enables usagefault, configures PC13 as an output, blinks it once so we see the program started then if it hits the usagefault handler then it blinks forever.

ra&=(~1);

If you comment this out then it keeps branching to reset which does everything again one slow blink and you see that repeat forever.

Before running naturally you check the build to see that it has a chance of working:

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000
 8000004:   08000049
 8000008:   0800004f
 800000c:   0800004f
 8000010:   0800004f
 8000014:   0800004f
 8000018:   08000061
 800001c:   0800004f
 8000020:   0800004f
 8000024:   0800004f
 8000028:   0800004f
 800002c:   0800004f
 8000030:   0800004f
 8000034:   0800004f
 8000038:   0800004f
 800003c:   0800004f
 8000040:   0800004f
 8000044:   0800004f

08000048 <reset>:
 8000048:   f000 f82a   bl  80000a0 <notmain>
 800004c:   e7ff        b.n 800004e <hang>

0800004e <hang>:
 800004e:   e7fe        b.n 800004e <hang>

...

08000060 <usagefault>:
 8000060:   b570        push    {r4, r5, r6, lr}

The vector table is correct the right vectors point to the right places.

0xE000ED28 CFSR RW 0x00000000

The HFSR is upper bits of the CFSR

> halt
target halted due to debug-request, current mode: Handler UsageFault
xPSR: 0x81000006 pc: 0x0800008a msp: 0x20000fc0
> mdw 0xE000ED28
0xe000ed28: 00020000

And that bit is

INVSTATE, bit[1] 
0 EPSR.T bit and EPSR.IT bits are valid for instruction execution.
1 Instruction executed with invalid EPSR.T or EPSR.IT field.

Now

Using the CCR, see Configuration and Control Register, CCR on page B3-604, software can enable or disable:
• Divide by zero faults, alignment faults and some features of processor operation.
• BusFaults at priority -1 and higher.

The reset value of the CCR is IMPLEMENTATION DEFINED so it might just be enabled for you or not, likely have to look at the Cortex-m3 TRM or just read it:

> mdw 0xE000ED14          
0xe000ed14: 00000000

so its zeros on mine.

So add fun.c:

unsigned int fun ( unsigned int x, unsigned int y)
{
    return(x/y);
}

Change so.c:

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
unsigned int fun ( unsigned int, unsigned int);

#define GPIOCBASE   0x40011000
#define RCCBASE     0x40021000
#define SHCSR       0xE000ED24

#define CCR         0xE000ED14

void usagefault ( void )
{
    unsigned int ra;

    while(1)
    {
        PUT32(GPIOCBASE+0x10,1<<(13+0));
        for(ra=0;ra<100000;ra++) dummy(ra);
        PUT32(GPIOCBASE+0x10,1<<(13+16));
        for(ra=0;ra<100000;ra++) dummy(ra);
    }
}

int notmain ( void )
{
    unsigned int ra;

    ra=GET32(SHCSR);
    ra|=1<<18; //usagefault
    PUT32(SHCSR,ra);

    ra=GET32(CCR);
    ra|=1<<4; //div by zero
    PUT32(CCR,ra);

    ra=GET32(RCCBASE+0x18);
    ra|=1<<4; //enable port c
    PUT32(RCCBASE+0x18,ra);
    ra=GET32(GPIOCBASE+0x04);
    ra&=(~(3<<20));   //PC13
    ra|=  (1<<20) ;   //PC13
    ra&=(~(3<<22));   //PC13
    ra|=  (0<<22) ;   //PC13
    PUT32(GPIOCBASE+0x04,ra);

    PUT32(GPIOCBASE+0x10,1<<(13+0));
    for(ra=0;ra<200000;ra++) dummy(ra);
    PUT32(GPIOCBASE+0x10,1<<(13+16));
    for(ra=0;ra<200000;ra++) dummy(ra);

    fun(3,0);

    return(0);
}

build

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m3 -mthumb -c so.c -o so.o
arm-none-eabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m3 -mthumb -c fun.c -o fun.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o so.o fun.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy -O binary so.elf so.bin

confirm there is actually a divide instruction that we are going to hit

 800011e:   2100        movs    r1, #0
 8000120:   2003        movs    r0, #3
 8000122:   f000 f80f   bl  8000144 <fun>
...
08000144 <fun>:
 8000144:   fbb0 f0f1   udiv    r0, r0, r1
 8000148:   4770        bx  lr

load and run and the handler is called.

target halted due to debug-request, current mode: Handler UsageFault
xPSR: 0x81000006 pc: 0x08000086 msp: 0x20000fc0
> mdw 0xE000ED28                
0xe000ed28: 02000000 

and that indicates it was a divide by zero.

So everything you needed to know/do really was in the documentation, one document.

99.999% of bare-metal programming is reading or doing experiments to validate what was read, almost none of the work is writing the final application, it is but a tiny part of the job.

Before you can get to bare-metal programming you have to have mastered the toolchain otherwise nothing will work. Mastering the toolchain can be done without any target hardware, using free tools so that is just a matter of sitting down and doing it.

As far as you're trying to do a floating point divide by zero on a core that doesn't have hardware divide by zero you need to look at the soft float, for example libgcc:

ARM_FUNC_START divsf3
ARM_FUNC_ALIAS aeabi_fdiv divsf3
    CFI_START_FUNCTION

    @ Mask out exponents, trap any zero/denormal/INF/NAN.
    mov ip, #0xff
    ands    r2, ip, r0, lsr #23
    do_it   ne, tt
    COND(and,s,ne)  r3, ip, r1, lsr #23
    teqne   r2, ip
    teqne   r3, ip
    beq LSYM(Ldv_s)
LSYM(Ldv_x):

...


    @ Division by 0x1p*: let''s shortcut a lot of code.
LSYM(Ldv_1):
    and ip, ip, #0x80000000
    orr r0, ip, r0, lsr #9
    adds    r2, r2, #127
    do_it   gt, tt
    COND(rsb,s,gt)  r3, r2, #255

and so on

which should have been visible in the disassembly, I don't off-hand see a forced exception (intentional undefined instruction, swi/svc or anything like that). This is only one possible library and now that I think about it this looks like arm not thumb, so would have to go looking for that, easier to just look at the disassembly.

Based on your comment and if I read the other question again I assume because it didn't raise an exception the correct result of a divide by zero is a properly signed infinity. but if you switch to a cortex-m4 or m7 then you might be able to trigger a hardware exception, but....read the documentation to find out.

Edit 2

void fun ( void )
{
    int a = 3;
    int b = 0;
    volatile int c = a/b;
}

fun.c:6:18: warning: unused variable ‘c’ [-Wunused-variable]
    6 |     volatile int c = a/b;
      |                  ^

08000140 <fun>:
 8000140:   deff        udf #255    ; 0xff
 8000142:   bf00        nop

> halt                          
target halted due to debug-request, current mode: Handler UsageFault
xPSR: 0x01000006 pc: 0x08000076 msp: 0x20000fc0
> mdw 0xE000ED28                
0xe000ed28: 00010000 

and that bit means

The processor has attempted to execute an undefined instruction

So volatile failed to produce the desired result using gcc

arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 10.1.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


arm-linux-gnueabi-gcc --version
arm-linux-gnueabi-gcc (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

produces

Disassembly of section .text:

00000000 <fun>:
   0:   deff        udf #255    ; 0xff
   2:   bf00        nop

as well (and you can godbolt your way through others).

Yes a fault was produced and it was the usage fault but this would have been yet another Stack Overflow question as to why I didn't get a divide by zero. Blindly using volatile to force a divide doesn't work.

Making all three volatile

void fun ( void )
{
    volatile int a = 3;
    volatile int b = 0;
    volatile int c = a/b;
}

Disassembly of section .text:

00000000 <fun>:
   0:   2203        movs    r2, #3
   2:   2300        movs    r3, #0
   4:   b084        sub sp, #16
   6:   9201        str r2, [sp, #4]
   8:   9302        str r3, [sp, #8]
   a:   9b01        ldr r3, [sp, #4]
   c:   9a02        ldr r2, [sp, #8]
   e:   fb93 f3f2   sdiv    r3, r3, r2
  12:   9303        str r3, [sp, #12]
  14:   b004        add sp, #16
  16:   4770        bx  lr

will generate the desired fault.

and with no optimizations

00000000 <fun>:
   0:   b480        push    {r7}
   2:   b085        sub sp, #20
   4:   af00        add r7, sp, #0
   6:   2303        movs    r3, #3
   8:   60fb        str r3, [r7, #12]
   a:   2300        movs    r3, #0
   c:   60bb        str r3, [r7, #8]
   e:   68fa        ldr r2, [r7, #12]
  10:   68bb        ldr r3, [r7, #8]
  12:   fb92 f3f3   sdiv    r3, r2, r3
  16:   607b        str r3, [r7, #4]
  18:   bf00        nop
  1a:   3714        adds    r7, #20
  1c:   46bd        mov sp, r7
  1e:   bc80        pop {r7}
  20:   4770        bx  lr

will also generate the desired fault

So master the language first (read read read), then master the toolchain second (read read read) then bare-metal programming (read read read). It is all about reading, not about coding. As shown above even with decades of experience at this level, you can't completely predict what the tools will generate; you have to just try it, but most important because you figured it out for one tool one time one day on one machine no reason to get too broad in your assumptions. Have to try it and examine what the compiler produces, repeat the process until you get the desired effect. Push comes to shove just write a few lines of asm and be done with it.

You weren't seeing faults because you weren't generating any and/or weren't trapping them or both. The list of possible reasons why is long based on the code provided, but these examples, that you should have no problem porting to your platform, should demonstrate your hardware works too, and then you can sort out why your software didn't by connecting the dots between code that does and code that doesn't. All I did was follow the documentation, and examine the output of the compiler, once I had the minimum number of things enabled, the fault handler was called. Without those enabled the usage fault was not triggered.

answered on Stack Overflow Jul 9, 2020 by old_timer • edited Aug 22, 2020 by halfer
1

BusFault, HardFault, MemmanageFault, UsageFault, SVC Call , NMI those are internal exception for arm cortex-M microprocessors. it depends really from which processor you are using, but let's suppose you are having cortex-m3:

  • By default all fault are mapped to hardfault handler unless you enable them explicitly to get mapped to their own handler
  • Set bits: USGFAULTENA, BUSFAULTENA, MEMFAULTENA in system handler control and state register => those fault each one will be mapped to its proper handler

To generate a fault you can try :

  • Access not mapped memory area => this generate Busfault
  • Execute unrecognized instruction => UsageFault
  • Explictly trigger one of those fault by setting one of those bits:
    USGFAULTACT, BUSFAULTACT, MEMFAULTACT in systen handler control and status register => this will generate an exception for sure

Please for more details refer to : https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/system-control-block/system-handler-control-and-state-register?lang=en

answered on Stack Overflow Feb 17, 2021 by dhokar.w

User contributions licensed under CC BY-SA 3.0