Я пишу флэш-загрузчик для устройства Cortex M4 и хочу «вернуть» значение для управляющего приложения ПК, используя непосредственное значение инструкции точки останова.
В то время как жесткое кодирование немедленно работает нормально:
__asm("bkpt 0x70");
__asm("bkpt %0" : : "i" (0x70));
как только я хочу "вернуть" что-то зависящее от времени выполнения, например
uint8_t status = Flash_EraseAll();
__asm("bkpt %0" : : "i" (status));
Компиляция не работает с
Error[Ta090]: Immediate operand is not constant
Я пытался использовать макросы препроцессора с различными настройками конкатенации, но безрезультатно.
Кто-нибудь понял, как я могу ввести флаги состояния, зависящие от времени выполнения, в блок __asm()
в IAR как немедленный? Основываясь на том, что я прочитал здесь, это не совсем возможно, но может быть хитрый хакерский способ сделать это.
P.S.: Да, в качестве обходного пути я мог бы использовать оператор switch, в котором я перечисляю и жестко закодирую каждое возможное состояние, но это просто уродливо и долго.
@TimothyBaldwin Это может объяснить пару странных поступков, которые у меня возникают. Прежде чем я изменю свое редактирование, не могли бы вы пояснить, насколько это небезопасно? Что может случиться с устройством/стеком в рабочем корпусе? Я должен добавить, что __("bkpt 0x0"); это последнее утверждение моего кода
Опубликуйте свой ответ как ответ, где люди могут проголосовать за него. Не присваивайте ему особый статус, редактируя его в вопрос. Если это лучше, чем (неполный) существующий ответ, вы можете принять свой собственный ответ.
@PeterCordes Спасибо за подсказку, я изменил тему.
Я бы поместил значение в стек, а затем использовал инструкцию bkpt
с определенным числом, чтобы отладчик мог посмотреть в стеке это состояние.
Что-то вроде этого (псевдокод):
__asm("push %0" : : "i" (status));
__asm("bkpt %0" : : "i" (0x70));
Конечно, вы не должны забывать после этого очищать стек.
Поскольку bkpt
закодирован только с немедленным, вы, очевидно, не можете изменить это во время выполнения, так как вам придется изменить код.
Хм... Это неплохая идея и кажется достаточно простой. Не думал об этом. Я подожду некоторое время, чтобы увидеть, есть ли у кого-нибудь дополнительные мысли по этому поводу. Спасибо!
Обе инструкции должны быть частью одного ассемблерного оператора, иначе компилятор может принять решение поместить некоторые сгенерированные компилятором инструкции между этими операторами. Кроме того, предположительно вам нужно очистить стек после возобновления. (пинг @davidanderle). Кроме того, очевидно, что вам нужно ограничение, отличное от "i"
для status
, потому что "i"
требует константы времени компиляции для замены в качестве немедленной. Вероятно, вы хотите "r"(status)
, и вы можете снова просто жестко закодировать bkpt 0x70
, если только вы не хотите установить число с помощью определения макроса C.
Основываясь на идее @Деволус, я получил следующее:
uint32_t status = Flash_EraseAll();
__asm volatile ("str %0, [sp, #-4]!\n\t" // Push to stack
"bkpt 0x0\n\t" // Halt CPU
"add sp, sp, #4\n\t" // Restore SP
: : "r"(status)); // status as input to __asm()
Инструкции по ассемблеру говорят компилятору поместить переменную состояния в удобный регистр «r» и сохранить содержимое этого регистра по предварительно уменьшенному адресу указателя стека, а затем остановить выполнение ЦП с немедленным 0.
Приложение для вождения будет опрашивать цель, если она будет остановлена (попадание bkpt). Если оно остановлено, прочитав 16-битные данные под текущим ПК (__asm("bkpt 0x00") -> 0xbe00 -> #imm = 0xbe00 & 0x00ff = 0), приложение может убедиться, что выполнение остановилось в нужном месте. место. Затем он будет считывать 32-битные данные по конечному адресу SP, чтобы получить статус выполнения встроенного кода.
Таким образом, вместо статического 8-битного кода непосредственно из bkpt, можно динамически "сообщать" больше информации во внешний мир (32-битный в данном случае).
Как подчеркивается в @ПитерКордес, операторы push и bkpt должны находиться в одной и той же инструкции встроенного ассемблера, иначе компилятор может решить вставить код между операторами. Кроме того, значение SP должно быть восстановлено до значения, предшествующего __asm(), поскольку компилятор берет на себя единоличный контроль над SP.
Вы нигде не упоминаете о восстановлении SP
. С этим ассемблером обработчик точки останова должен добавить 4
к SP перед возобновлением работы, иначе встроенный ассемблер модифицировал SP без уведомления компилятора.
@PeterCordes Это был конец моей программы, поэтому мне не нужно было ничего восстанавливать. Я думаю, если кто-то захочет сделать что-то еще после возобновления выполнения из bkpt, они могут просто добавить «добавить sp, sp, # 4», чтобы отменить то, что сделал str?
да. Это довольно важная деталь, которую следует опустить для будущих читателей, которым она может оказаться полезной.
@PeterCordes Спасибо за помощь, очень признателен!
Ваше решение небезопасно, так как вы изменяете
sp
в первом операторе ASM. Нажатие, точка останова и восстановлениеsp
должны быть в одном операторе asm.