Как сгенерировать исключения в Cortex M3? - PullRequest
0 голосов
/ 09 июля 2020

Я пытаюсь создать исключения, такие как «Ошибка шины», «Ошибка использования» на ARM Cortex-M3. Мой код для включения исключений:

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 
}

Я попытался сгенерировать деление на нулевое исключение и увидел значение переменных как «Бесконечность». Однако система не генерирует никаких исключений при продолжении работы. Также попытался сгенерировать исключение сбоя шины, и произошло то же самое.

Также, когда я закомментировал функцию EnableExceptions, система работает правильно. Что не так с моим кодом? Обрабатывает ли ARM подобные ошибки внутри микропроцессора?

Ответы [ 2 ]

2 голосов
/ 09 июля 2020

Устройства Cortex-M используют исключительно набор инструкций Thumb-2, ARM использует младший бит адреса перехода / перехода / вызова, чтобы определить, является ли целью код Thumb или ARM, поскольку Cortex-M не может запускать ARM кода, вы можете сгенерировать исключение BusFault, создав переход на четный адрес.

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

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

    foo() ;
}

Следующее также будет работать, поскольку вызов завершится ошибкой до того, как будут выполнены какие-либо инструкции, поэтому ему не нужно указывать на какой-либо действительный код.

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

Вы можете создать ошибку использования, принудительно разделив целое число на ноль:

int main()
{
    volatile int x = 0 ;
    volatile int y = 1 / x ;
}
1 голос
/ 09 июля 2020

Из вашего комментария вопрос: Как сгенерировать любое исключение.

Вот один из документации:

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

...

Exceptions 
SVCall.

Который я можно найти, выполнив поиск по SVCall.

Исключения хорошо задокументированы ARM, есть способы вызвать исключения, которые вы указали, без необходимости разрывать шину (требуя симулятора fpga или создания собственного кремния), вы уже знать условия поиска по документу, чтобы найти сбой шины и сбой использования.

Как ARM обрабатывает их (внутренне или нет), задокументировано. Внутренне в этом случае означает блокировку или нет, если вы посмотрите, в противном случае они выполняют обработчик ошибок (если, конечно, нет ошибки, вызывающей обработчик ошибок).

Большинство из них вы можете создать в C, не прибегая к инструкции на языке ассемблера, но вы должны быть осторожны, чтобы он генерировал то, что, по вашему мнению, генерирует:

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

Disassembly of section .text:

00000000 <fun>:
   0:   4770        bx  lr

Вместо этого вам нужно что-то, что фактически генерирует инструкцию, которая может вызвать ошибку:

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

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

Вот почему OP должен предоставить полный минимальный пример, причина, по которой не обнаруживаются ошибки, не в процессоре. Но программное обеспечение и / или тестовый код.

Edit

Полный минимальный пример, все, что вам нужно, кроме набора инструментов GNU (нет. Это на синей таблетке stm32 и STM32F103 ...

fla sh .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

fla sh .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);
}

сборка

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

Все эти параметры командной строки не требуются arm- linux -gnueabi- и другие разновидности инструментальных средств gnu отлично работают от нескольких версий до настоящего момента, поскольку я использую их в качестве компилятора, ассемблер и компоновщик и не связывайтесь с библиотекой или другими вещами, которые варьируются от одного аромата к другому.

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

Итак, здесь тест переходит в руку адрес вместо адреса большого пальца, и это вызывает ошибку использования. (Можно прочитать об инструкции BX, бит psr.t, как и когда он будет изменен, и c также в документации)

Резервное копирование этого это синяя таблетка stm32. На ПК13 есть светодиод, код включает ошибку использования, настраивает ПК13 как выход , мигает один раз, поэтому мы видим, что программа запущена, а затем, если она попадает в обработчик ошибок использования, она мигает вечно. вы видите, что это повторяется бесконечно.

Перед естественным запуском вы проверяете сборку, чтобы убедиться, что она работает:

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}

Таблица векторов верна правые векторы указывают вправо мест.

0xE000ED28 CFSR RW 0x00000000

HFSR - это старшие биты CFSR

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

И этот бит

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.

Теперь

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.

Значение сброса CCR ОПРЕДЕЛЕНО РЕАЛИЗАЦИЕЙ, поэтому он может быть включен для вас или нет, вероятно, придется посмотреть на Cortex-m3 TRM или просто прочитать его:

> mdw 0xE000ED14          
0xe000ed14: 00000000

, так что его нули на моем .

Так добавьте веселья. c:

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

Измените так. 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

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

 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

загрузить и запустить и th Вызывается обработчик e.

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

и это означает, что это деление на ноль.

Итак, все, что вам нужно было знать / делать, действительно было в документации, в одном документе.

99,999% программирования на «голом железе» - это чтение или проведение экспериментов для проверки того, что было прочитано, почти ни одна из работ - это написание окончательного приложения, это лишь крошечная часть работы.

Перед вами Чтобы перейти к программированию на голом железе, вы должны освоить инструментальную цепочку, иначе ничего не будет работать. Освоение инструментальной цепочки может быть выполнено без какого-либо целевого оборудования с использованием бесплатных инструментов, так что достаточно просто сесть и сделать это.

Насколько вы пытаетесь сделать деление с плавающей запятой на ноль на ядро, которое не имеет аппаратного деления на ноль, вам нужно посмотреть на soft float, например libg cc:

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

, который должен был быть виден при разборке, я не отключаю- рука видит принудительное исключение (преднамеренная неопределенная инструкция, swi / sv c или что-то в этом роде). Это только одна из возможных библиотек, и теперь, когда я думаю об этом, это похоже на руку, а не на большой палец, поэтому пришлось бы go искать это, проще просто посмотреть на разборку.

На основе вашего комментария и если я снова прочитаю другой вопрос, я предполагаю, поскольку он не вызвал исключения, правильным результатом деления на ноль является правильно подписанная бесконечность. но если вы переключитесь на cortex-m4 или m7, вы можете вызвать аппаратное исключение, но ... прочтите документацию, чтобы узнать.

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 

и этот бит означает

The processor has attempted to execute an undefined instruction

Таким образом, volatile не смог дать желаемый результат, используя g cc

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.

, а также

Disassembly of section .text:

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

(и вы можете прокладывать себе путь через других).

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

Создание всех трех энергозависимых

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

вызовет желаемую ошибку.

и без оптимизации

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

также вызовет желаемую ошибку.

Итак, сначала освоите язык (чтение, чтение, чтение), затем освоите инструментальную цепочку, затем (чтение, чтение, чтение), а затем программирование с нуля (чтение, чтение, чтение). Все дело в чтении, а не в кодировании. Как показано выше, даже имея многолетний опыт работы на этом уровне, вы не можете полностью предсказать, что сгенерируют инструменты; вам нужно просто попробовать это, но самое главное, потому что вы разобрались с этим для одного инструмента один раз в один день на одной машине, нет причин для слишком широких предположений. Придется попробовать и изучить, что производит компилятор, повторять процесс до тех пор, пока не получите желаемый эффект. Pu sh приходит в голову, просто напишите несколько строк asm и покончите с этим.

Вы не видели ошибок, потому что вы не генерировали их и / или не улавливали их, или и то, и другое. Список возможных причин большой длины основан на предоставленном коде, но эти примеры, в которых у вас не должно возникнуть проблем с портированием на вашу платформу, также должны демонстрировать работу вашего оборудования, а затем вы можете разобраться, почему ваше программное обеспечение не работает, подключив точки между кодом, который делает, и кодом, который этого не делает. Все, что я сделал, - это следил за документацией и изучил вывод компилятора, как только я включил минимальное количество вещей, был вызван обработчик ошибок. Без них ошибка использования не запускалась.

...