Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Чтение внутренних регистров STM32
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
hardgame
В стареньком ADS 1.2 можно было в функции С делать вставку inline asm ссылкой сразу на внутренние регистры r0-r15. Столкнулся в Кеил, что предыдущие inline asm не компилится. А вот что нашел в доках
"The inline assembler provides no direct access to the physical registers of an ARM processor. If an ARM register name is used as an operand in an inline assembler instruction it becomes a reference to a variable of the same name, and not the physical ARM register."
Есть ли способ чтения внутренних регистров, возможно средствами cmsis, или нужно создавать asm раздел и увязывать?
Forger
Цитата(hardgame @ May 16 2016, 17:10) *
Есть ли способ чтения внутренних регистров, возможно средствами cmsis, или нужно создавать asm раздел и увязывать?

Я бы переписал существующий код так, чтобы вообще не приходилось лазить во внутренние регистры проца,
ибо рано или поздно эта "самодеятельность" еще проявит себя, причем, самым неожиданным способом smile3046.gif
prottoss
Цитата(Forger @ May 16 2016, 20:31) *
Я бы переписал существующий код так, чтобы вообще не приходилось лазить во внутренние регистры проца, ибо рано или поздно эта "самодеятельность" еще проявит себя, причем, самым пакостным способом, какой только можно себе вообразить smile3046.gif
А если это планировщик задач? rolleyes.gif
Forger
Цитата(prottoss @ May 16 2016, 17:35) *
А если это планировщик задач? rolleyes.gif

Планировщик прекрасно целиком пишется на голом C.
Вот переключение контекста нужно писать на asm (или на C c asm вставками целиком функций), но эти "порты" можно выдрать с любой годной оси (под KEIL или под нужную среду)
и переписать их под себя, коли так уже нужна самодельная ось.
Как-то помню возился и делал сам порт под Cortex-M0+, имея на руках тока порт под M3.
Мартышкин труд, промудохался помню много, потом открыл примеры других осей под M0 и дело пошло быстрее sm.gif


Вот пример (кусок С-файла):

CODE
__asm void SVC_Handler (void)
{
movs R0, #4
mov R1, LR
tst R0, R1
beq msp_stacked

psp_stacked
mrs R0,PSP // Read PSP
b done

msp_stacked
mrs R0,MSP // Read MSP

done
ldr R1,[R0,#24] // Read Saved PC from Stack
subs R1,R1,#2 // Point to SVC Instruction
ldrb R1,[R1] // Load SVC index
cmp R1,#0
beq svc_0 // SVC0
cmp R1,#1
beq svc_1 // SVC1
cmp R1,#2
beq svc_2 // SVC2

svc_0
ldr R0, =__cpp(&tn_start_first_task)
bx R0

svc_1
ldr R0, =__cpp(&tn_tick_int_processing)
bx R0

svc_2
ldr R0, =__cpp(&tn_timer)
bx R0

ALIGN
}
scifi
Цитата(Forger @ May 16 2016, 17:31) *
Я бы переписал существующий код так, чтобы вообще не приходилось лазить во внутренние регистры проца,
ибо рано или поздно эта "самодеятельность" еще проявит себя, причем, самым неожиданным способом smile3046.gif

+много.
Вообще не понимаю, каким может быть код, якобы написанный на Си, и лезущий в регистры. Наверное, минное поле. Одно неосторожное движение - и вы отец laughing.gif
Alechek
Единственное, для чего мне это понадобилось, это скинуть дамп регистров при возникновении Fault.
Пришлось написать вставку на ASM для копирования регистров в память (структуру). Остальная работа на C.
prottoss
Цитата(Forger @ May 16 2016, 20:44) *
Вот переключение контекста нужно писать на asm...
Я именно об этом и подразумевал под планировщиком.


Цитата(Alechek @ May 16 2016, 22:09) *
...это скинуть дамп регистров при возникновении Fault...
Ну вот - человеку как раз нужен контекст.
adnega
Цитата(Alechek @ May 16 2016, 19:09) *
Единственное, для чего мне это понадобилось, это скинуть дамп регистров при возникновении Fault.
Пришлось написать вставку на ASM для копирования регистров в память (структуру). Остальная работа на C.

Зачем копировать? Там уже все в стеке (правда, нужно определить правильный стек).
CODE
.global HardFaultException
.extern print_fault
HardFaultException:
mov r0, sp
push {lr}
bl print_fault
pop {pc}

void print_fault(sFAULT *fault)
{
con_str("FAULT:\n\r");
con_str("r0 = "); con_dword(fault->r0); con_str(", ");
con_str("r1 = "); con_dword(fault->r1); con_str(", ");
con_str("r2 = "); con_dword(fault->r2); con_str(", ");
con_str("r3 = "); con_dword(fault->r3); con_str("\n\r");
con_str("r12 = "); con_dword(fault->r12); con_str(", ");
con_str("lr = "); con_dword(fault->lr); con_str(", ");
con_str("pc = "); con_dword(fault->pc); con_str(", ");
con_str("xpsr = "); con_dword(fault->xpsr); con_str("\n\r");
con_start();
}

typedef struct sFAULT
{
DWORD r0; //!< Регистр R0
DWORD r1; //!< Регистр R1
DWORD r2; //!< Регистр R2
DWORD r3; //!< Регистр R3
DWORD r12; //!< Регистр R12
DWORD lr; //!< Регистр LR
DWORD pc; //!< Регистр PC
DWORD xpsr; //!< Регистр XPSR
} sFAULT;

prottoss
ИМХО - лучше всего сделать отдельный ассемблерный файл. Если кейловский ассемблер поддерживает директивы условной компиляции - вообще прекрасно. Можно сделать этот файл поддерживаемым несколькими вариантами ARM-платформы.
hardgame
Всем Спасибо за комментарии. Регистры были необходимы для обработки Fault исключения.
aaarrr
Цитата(adnega @ May 16 2016, 19:20) *
Зачем копировать? Там уже все в стеке

А R4-R11 где?
adnega
Цитата(aaarrr @ May 16 2016, 22:42) *
А R4-R11 где?

Реально, по значениям LR, PC, R0-R4 в подавляющем большинстве случаев можно найти причину фэйла.
В борьбе за полноту информации можно упомянуть еще с десяток важных регистров.
Меня больше интересует кто как обрабатывает это исключение?
aaarrr
Цитата(adnega @ May 16 2016, 22:59) *
Меня больше интересует кто как обрабатывает это исключение?

У меня алгоритм такой:
Исключение -> запись контекста исключения в RAM -> перезапуск -> сохранение контекста на внешнем носителе или передача на отладочный сервер -> возобновление работы
adnega
Цитата(aaarrr @ May 16 2016, 23:27) *
У меня алгоритм такой:
Исключение -> запись контекста исключения в RAM -> перезапуск -> сохранение контекста на внешнем носителе или передача на отладочный сервер -> возобновление работы

А на практике сильно помогает найти проблему? Какая самая частая проблема?
В моем случае только явная ошибка в коде (выход за границу массива, порча указателя, неинициализированные объекты, глючная библиотека и т.п.).
На Cortex-M0 добавляется невыровненный доступ.
aaarrr
Цитата(adnega @ May 16 2016, 23:38) *
А на практике сильно помогает найти проблему? Какая самая частая проблема?

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

На удаленном оборудовании пару раз выручало. Всех подробностей не вспомню, но потребовалось
помимо регистров изучать и содержимое стека.
jcxz
Цитата(Alechek @ May 16 2016, 22:09) *
Единственное, для чего мне это понадобилось, это скинуть дамп регистров при возникновении Fault.
Пришлось написать вставку на ASM для копирования регистров в память (структуру). Остальная работа на C.

И где гарантия, что компилятор при очередной перекомпиляции, не разместит какие-то переменные в регистрах которые Вы хотели сохранить до вашей вставки?
Такое надо делать целиком в asm-функциях. Напишите ISR fault-ов полностью на asm, там всё элементарно.

Цитата(adnega @ May 17 2016, 01:59) *
Реально, по значениям LR, PC, R0-R4 в подавляющем большинстве случаев можно найти причину фэйла.

Не знаю не знаю... У меня сохраняются все регистры + дамп стека. И то часто не хватает.

Цитата(aaarrr @ May 17 2016, 02:27) *
У меня алгоритм такой:
Исключение -> запись контекста исключения в RAM -> перезапуск -> сохранение контекста на внешнем носителе или передача на отладочный сервер -> возобновление работы

У меня все fault-ы примерно так же обрабатываются, только кроме дампа регистров сохраняю ещё и дамп текущего стека.
Отличия:
Для release-сборки: всё так же, кроме передачи на отладочный сервер.
Для debug-сборки: вместо перезапуска - переинициализация железа в минимальный дефолт-конфиг trap-режима и зацикливание в цикле периодического вывода инфы о событии в лог (UART). Чтобы не пропускать такие ошибки, а исправлять сразу.
Этот-же механизм используется для обработки программных критических ошибок (через SVC).

Цитата(aaarrr @ May 17 2016, 03:24) *
На удаленном оборудовании пару раз выручало. Всех подробностей не вспомню, но потребовалось

Помогает очень часто в обнаружении редко проявляющихся ошибок. Когда тестируем сразу несколько десятков (а то и сотни) устройств в течение длительного времени (дни/недели непрерывной работы).
С JTAG-ом тут не посидишь, а вот устройства защёлкнувшиеся в trap-цикле сразу видны. А потом - по регистрам-стеку ищем причину.
И если такие ошибки проявились уже на объектах заказчика - тут другого выбора нет, очень помогает.

Без дампа стека вообще мало когда эта инфа помогает. Типичная ситуация: срабатывание исключения MPU защиты памяти при попытке обращения к недопустимой памяти изнутри memcpy().
От регистров почти никакого толку - в каком месте кода произошёл сбой? Зато по стеку можно легко проследить цепочку вызовов. Уже сколько так багов нашли.
Alechek
Цитата(jcxz @ May 17 2016, 07:00) *
И где гарантия, что компилятор при очередной перекомпиляции, не разместит какие-то переменные в регистрах которые Вы хотели сохранить до вашей вставки?
Такое надо делать целиком в asm-функциях. Напишите ISR fault-ов полностью на asm, там всё элементарно.

С легкой руки (для IAR)
CODE
__stackless void FaultHandler(void * p_base, unsigned long EXC_RETURN)
{
SaveContext(p_base, EXC_RETURN);
EXPT.VERSION = APP_Get_FwVersion();
gMarker = EXCEPTION_MARKER;
#ifdef NDEBUG
SYS_Reset();
#else
while (1);
#endif
}

__stackless __irq void HardFaultHandler(void)
{
if (SCB->HFSR & SCB_HFSR_DEBUGEVT_Pos)
return;
FaultHandler((void*)( (__get_LR() & BIT(2)) ? __get_PSP() : __get_MSP()),
__get_LR());
}

__stackless __irq void MemoryFaultHandler(void)
{
FaultHandler((void*)( (__get_LR() & BIT(2)) ? __get_PSP() : __get_MSP()),
__get_LR());
}

__stackless __irq void BusFaultHandler(void)
{
FaultHandler((void*)( (__get_LR() & BIT(2)) ? __get_PSP() : __get_MSP()),
__get_LR());

}

__stackless __irq void UsageFaultHandler(void)
{
FaultHandler((void*)( (__get_LR() & BIT(2)) ? __get_PSP() : __get_MSP()),
__get_LR());

// while(1);
}


void SaveContext(void * p_base, unsigned long EXC_RETURN)
{
EXPT.Registers = *(struct exception_saved_context * )p_base;
EXPT.SP = (DWORD)p_base + sizeof(struct exception_saved_context);

EXPT.Status.dwRaw = SCB->CFSR;
EXPT.Exception = (int_source_t)(SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk);
EXPT.EXC_RETURN = ( EXPT.Status.BFSR.BFARVALID) ? SCB->BFAR : EXC_RETURN;
SCB->CFSR = 0;

EXPT.HeapFree = OS_MEMORY_AVAILABLE();
EXPT.ShedulerState = OS_GET_SHEDULER_STATE();
EXPT.Context.StackFree = OS_GET_TASK_STACK_WATERMARK(OS_GET_CURRENT_TASK_HANDLE());
strncpy(EXPT.Context.Name, (char*)OS_GET_CURRENT_TASK_NAME() , sizeof(EXPT.Context.Name));
EXPT.Context.Name[sizeof(EXPT.Context.Name)-1] = '\0';


Для CORTEX ядра все получается и без ASM функций.
А вот для ARM делал вставку на ASM с обработчиками исключений и функцией сохранения регистров.

В большинстве случаев эта информация помогает на столе.
В основном в исключения валимся при переполнении стека. Поэтому место возникновения исключения, при наличии RTOS, мало что скажет. Главное состояние планировщика, текущая задача(последняя в случае переключения контекста) и свободное место в стеке задачи.
jcxz
Цитата(Alechek @ May 17 2016, 11:22) *
__stackless void FaultHandler(void * p_base, unsigned long EXC_RETURN)
{
SaveContext(p_base, EXC_RETURN);
...
Для CORTEX ядра все получается и без ASM функций.

Ещё раз: Где гарантия, что компилятор не вставит перед Вашей SaveContext() например создание стекового фрейма с занесением указателя на него в любой из R4-R11?
Типа:
PUSH {R7, LR}
ADD R7, SP, #N
...

или ещё чего, чего ему вздумается.
scifi
Цитата(jcxz @ May 17 2016, 11:48) *
Ещё раз: Где гарантия, что компилятор не вставит перед Вашей SaveContext() например создание стекового фрейма с занесением указателя на него в любой из R4-R11?

Как где? __stackless же. У gcc есть аналогичная штука - naked.
jcxz
Цитата(scifi @ May 17 2016, 15:01) *
Как где? __stackless же. У gcc есть аналогичная штука - naked.

А это обязательное или только рекомендуемое как inline? А функции, которые из FaultHandler() вызываются тоже все __stackless?
И стек - это для примера. Что угодно, например - в какой-либо регистр может заранее положить указатель на секцию данных, либо какие-то другие данные.
Использование регистров внутри си-функции - по усмотрению компилятора, любые домыслы по их содержимому - потенциальные грабли.
Alechek
jcxz, если делать что-то бездумно - то возможны любые грабли.
А если подумать - компилятор пытается лишь сделать то, что от него просят. И не больше.
Головой думать надо, прежде что-то делать.
adnega
Цитата(Alechek @ May 17 2016, 14:00) *
И не больше.

Ну, ну. Я как-то на Си осмелился пописать под AVR. На меге8 килобайты флеш закончились настолько быстро, что захотелось узнать куда.
Листинг показал, что компилятор сохраняет в стек все регистры при вызове функций, даже неиспользуемые.
Отдельным приключением было сделать программный UART на Си - по тактам реализация очень неустойчива - на том краю планеты бабочка
махнет рукой (добавим, например, переменную в проект), а у нас пару лишних тактов в цикле задержки.
Что касается регистров, тактов и байтов - компилятор очень непредсказуемая штука.
Копейкин
Цитата(adnega @ May 17 2016, 14:10) *
Листинг показал, что компилятор сохраняет в стек все регистры при вызове функций, даже неиспользуемые.

Компилятор IAR так поступает при выключенной оптимизации (или невысоком уровне).
Alechek
adnega, речь немного не про такой случай.
Использование стека еще как компиляторозависимо. От того, как он будет оптимизировать код, и какую оптимизацию ему разрешено использовать.
Уверен, и Вашему случаю нашлось бы объяснение в мануалах.

Здесь о том, что __stackless говорит компилятору, что стека НЕТ! То есть только регистры. И ничего он в них выделять не будет, пока не попросишь.

Да, и определитесь, что у вас закончилось, стек или флеш.... не вяжется как-то.
adnega
Цитата(Копейкин @ May 17 2016, 14:34) *
Компилятор IAR так поступает при выключенной оптимизации (или невысоком уровне).

Тут советовали головой думать, но моя не может придумать - "зачем"?
Зачем неиспользуемые регистры складывать в стек??

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

Цитата(Alechek @ May 17 2016, 14:40) *
Да, и определитесь, что у вас закончилось, стек или флеш.... не вяжется как-то.

Прошу без эмоций определить, что быстрее заканчивается при таком подходе
Код
11e:    1f 92           push    r1
120:    0f 92           push    r0
122:    0f b6           in    r0, 0x3f; 63
124:    0f 92           push    r0
126:    11 24           eor    r1, r1
128:    2f 93           push    r18
12a:    3f 93           push    r19
12c:    4f 93           push    r20
12e:    5f 93           push    r21
130:    6f 93           push    r22
132:    7f 93           push    r23
134:    8f 93           push    r24
136:    9f 93           push    r25
138:    af 93           push    r26
13a:    bf 93           push    r27
13c:    ef 93           push    r30
13e:    ff 93           push    r31

Это еще годный код, т.к. не все регистры убираются в стек, а только используемые.
Я описывал ситуацию, когда банальная установка переменной оборачивалась push-ами и pop-ами для всех регистров.
В моем случае резко закончилась флеш, хотя и к глубине стека требования резко возрастают.
Перед использованием С этот же проект был реализован на asm. При 10% asm-функционала на С закончился флеш.
Alechek
Цитата(adnega @ May 17 2016, 16:41) *
Зачем неиспользуемые регистры складывать в стек??

Затем что оптимизации НЕТ. Делаем так, чтобы 110% все работало и отладка была удобной.
Определитесь для начала, какой результат хотите: быстрый, мало памяти занимал, или для отладки.
Программа не работала? Так какие претензии к компилятору?


Цитата(adnega @ May 17 2016, 16:51) *
Прошу без эмоций определить, что быстрее заканчивается при таком подходе

Так да. Закончится и то, и другое.

На AVR почти не писал. А в ARM,как тут уже приводили, - одна инструкция хооть для всех регистров.
jcxz
Цитата(Alechek @ May 17 2016, 17:00) *
jcxz, если делать что-то бездумно - то возможны любые грабли.
А если подумать - компилятор пытается лишь сделать то, что от него просят. И не больше.
Головой думать надо, прежде что-то делать.

Ну-ну. И что же он пытается сделать? Расскажите-ка нам какие именно регистры задействуют все возможные компиляторы во всех возможных режимах оптимизации.
К Вашему сведению: компилятор имеет право использовать внутри функции любые регистры как ему заблагорассудится если это не противоречит соглашениям вызова.
Если думать именно головой, то как раз не надо делать никаких гаданий как именно будет построен код внутри си-функции.
Alechek
jcxz, сравнивать неопределеность, исходящую от компилятора с определенностью, полученой от ручного кодирования машинных кодов (ASM) бессмысленно. ASM в выигрыше однозначно.
Но, вероятность получить "пасхальное яйцо" от компилятора примерно равна вероятности получить неработоспособный код со вполне работоспособных исходников.
Выбор всегда есть:
- хочешь сложное кодирование но 110% результат - пиши в машинных кодах,
- хочешь более легкое программирование и 100% результат - попытайся думать так, как думает компилятор.

И когда я говорю про компилятор, я имею ввиду конкретный, а не абстрактную сущность всех компиляторов.
К слову, ASM файлы тоже в большинстве случаев не переносимы.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.