Electronic – Cortex M7 goes to HardFault immediately after reset

armassembly

I am using the ATSAME70Q21 processor with a Cortex-M7 CPU.

My development environment is Atmel Studio 7. I am using the SAME70 XPlained demo board.

I am creating some code in assembly language.

As a test I setup a vector table at the start of flash (0x00400000) which contain the initial stack pointer value and the location of the Reset_Handler in the first 8 bytes.

When I hit reset on the debugger the Program counter gets set to 0x0040001C (which is correct). The instruction at location 0x0040001C is 0xC046 which is a NOP. The next several instructions in memory are NOPs also.

If I single step the debugger (execute the NOP) the program counter immediately jumps to the HardFault_Handler.

I don't see how executing a NOP can generate a HardFault.

What are likely reasons for a HardFault to occur immediately after reset.

I don't think its a hardware issue since I am using an off the shelf demo board and the problem doesn't occur if I use one of the example C projects in Atmel studio.

Best Answer

Note that assembly language is defined by the tool not the target. For gnu assembler, to date for thumb, particularly with these cortex-ms where you need/want to get the vector table right you started with this problem:

.thumb
.globl _start
_start:
    .word 0x20001000
    .word reset
    .word loop
    .word loop
reset:
    b reset
loop:
    b loop

assemble/dissassemble and you can see the problem

Disassembly of section .text:

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000010    andeq   r0, r0, r0, lsl r0
   8:   00000014    andeq   r0, r0, r4, lsl r0
   c:   00000014    andeq   r0, r0, r4, lsl r0

00000010 <reset>:
  10:   eafffffe    b   10 <reset>

00000014 <loop>:
  14:   eafffffe    b   14 <loop>

(once you know that the vector addresses need to be the address orred with 1)

.thumb
.globl _start
_start:
    .word 0x20001000
    .word reset
    .word loop
    .word loop
.thumb_func
reset:
    b reset
loop:
    b loop

the minimum but clean solution is to add .thumb_func to indicate that the next label you find is a thumb function not a generic address.

(you have to link not just assemble/disassemble)

Disassembly of section .text:

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000011    andeq   r0, r0, r1, lsl r0
   8:   00000012    andeq   r0, r0, r2, lsl r0
   c:   00000012    andeq   r0, r0, r2, lsl r0

00000010 <reset>:
  10:   e7fe        b.n 10 <reset>

00000012 <loop>:
  12:   e7fe        b.n 12 <loop>

and that fixed one vector.

.thumb
.globl _start
_start:
    .word 0x20001000
    .word reset
    .word loop
    .word loop
.thumb_func
reset:
    b reset
.thumb_func
loop:
    b loop

and that fixed the other.

Disassembly of section .text:

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000011    andeq   r0, r0, r1, lsl r0
   8:   00000013    andeq   r0, r0, r3, lsl r0
   c:   00000013    andeq   r0, r0, r3, lsl r0

00000010 <reset>:
  10:   e7fe        b.n 10 <reset>

00000012 <loop>:
  12:   e7fe        b.n 12 <loop>

add some C

.thumb
.globl _start
_start:
    .word 0x20001000
    .word reset
    .word loop
    .word fun
.thumb_func
reset:
    b reset
.thumb_func
loop:
    b loop

void fun ( void )
{
}

Disassembly of section .text:

00000000 <_start>:
   0:   20001000    andcs   r1, r0, r0
   4:   00000011    andeq   r0, r0, r1, lsl r0
   8:   00000013    andeq   r0, r0, r3, lsl r0
   c:   00000015    andeq   r0, r0, r5, lsl r0

00000010 <reset>:
  10:   e7fe        b.n 10 <reset>

00000012 <loop>:
  12:   e7fe        b.n 12 <loop>

00000014 <fun>:
  14:   4770        bx  lr
  16:   46c0        nop         ; (mov r8, r8)

Then look at the output of the C compiler

    .cpu arm7tdmi
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 1
    .eabi_attribute 30, 2
    .eabi_attribute 34, 0
    .eabi_attribute 18, 4
    .file   "fun.c"
    .text
    .align  1
    .p2align 2,,3
    .global fun
    .arch armv4t
    .syntax unified
    .code   16
    .thumb_func
    .fpu softvfp
    .type   fun, %function
fun:
    @ Function supports interworking.
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    @ sp needed
    bx  lr
    .size   fun, .-fun
    .ident  "GCC: (GNU) 8.2.0"

(doesnt matter that I defaulted to the arm7, but there is .thumb_func)

some assembly languages will have directives like proc or function or other similar things that you use to declare a label as a function rather than just a generic address to something, so when trying other assemblers for arm you may run into that.

if this uses the gnu assembler then .thumb_func should fix it. I would in your mind think if this as an orring with one not an adding with one in case the address is correct you might end up breaking it with the just add one to everything notion.

Same goes for function pointer stuff in C you have to be careful and can run into this.

void loop ( void );
void hop ( unsigned int );
void fun ( void )
{
    unsigned int x;
    x = (unsigned int) loop;
    hop(x);
}
void more_fun ( void )
{
    unsigned int x;
    unsigned short z[4];
    z[0]=0x46c0;
    z[1]=0x4770;
    z[2]=0x46c0;
    z[3]=0x46c0;
    x = (unsigned int) z;
    hop(x);
}

00000012 <loop>:
  12:   e7fe        b.n 12 <loop>

00000014 <hop>:
  14:   4700        bx  r0

00000018 <fun>:
  18:   b510        push    {r4, lr}
  1a:   4803        ldr r0, [pc, #12]   ; (28 <fun+0x10>)
  1c:   f7ff fffa   bl  14 <hop>
  20:   bc10        pop {r4}
  22:   bc01        pop {r0}
  24:   4700        bx  r0
  26:   46c0        nop         ; (mov r8, r8)
  28:   00000013    andeq   r0, r0, r3, lsl r0

0000002c <more_fun>:
  2c:   b500        push    {lr}
  2e:   4b05        ldr r3, [pc, #20]   ; (44 <more_fun+0x18>)
  30:   b083        sub sp, #12
  32:   9300        str r3, [sp, #0]
  34:   4b04        ldr r3, [pc, #16]   ; (48 <more_fun+0x1c>)
  36:   4668        mov r0, sp
  38:   9301        str r3, [sp, #4]
  3a:   f7ff ffeb   bl  14 <hop>
  3e:   b003        add sp, #12
  40:   bc01        pop {r0}
  42:   4700        bx  r0
  44:   477046c0    
  48:   46c046c0    

not that you are going to run code like that but you can see the problem.

and a fix

void more_fun ( void )
{
    unsigned int x;
    unsigned short z[4];
    z[0]=0x46c0;
    z[1]=0x4770;
    z[2]=0x46c0;
    z[3]=0x46c0;
    x = (unsigned int) z;
    hop(x|1);
}

0000002c <more_fun>:
  2c:   b500        push    {lr}
  2e:   4b06        ldr r3, [pc, #24]   ; (48 <more_fun+0x1c>)
  30:   b083        sub sp, #12
  32:   9300        str r3, [sp, #0]
  34:   4b05        ldr r3, [pc, #20]   ; (4c <more_fun+0x20>)
  36:   4668        mov r0, sp
  38:   9301        str r3, [sp, #4]
  3a:   2301        movs    r3, #1
  3c:   4318        orrs    r0, r3
  3e:   f7ff ffe9   bl  14 <hop>
  42:   b003        add sp, #12
  44:   bc01        pop {r0}
  46:   4700        bx  r0
  48:   477046c0 
  4c:   46c046c0 

what you may very well do is write a bootloader and to get the hop into the loaded code right you need to solve it there are use a function pointer things you can try and maybe not write some asm, but I recommend the asm and orr with one, more likely to get it to work.