Cortex m3 выполнение первой инструкции

Я использую компилятор Sourcery CodeBench Lite 2012.03-56 и набор gdb с texane gdb server.

Сегодня я хотел попробовать демо-пример FreeRTOS для дешевой платы STM32VLDISCOVERY, я скопировал все необходимые исходные файлы, скомпилировал без ошибок, но пример не сработал. Я запустил отладчик и заметил, что пример не работает, когда он пытается разыменовать указатель на регистры GPIO. Переменная глобального массива, содержащая указатели на регистры GPIO:

GPIO_TypeDef* GPIO_PORT[LEDn] = {LED3_GPIO_PORT, LED4_GPIO_PORT};

не был должным образом инициализирован и был заполнен случайными значениями. Я проверил, что препроцессор определяет LED3_GPIO_PORT и LED3_GPIO_PORT, и они действительны.

После некоторого исследования, где может быть проблема, я просмотрел файл запуска, предоставленный для trueSTUDIO, найденный в CMSIS lib. Исходный файл startup_stm32f10x_md_vl.S:

    .section    .text.Reset_Handler
    .weak   Reset_Handler
    .type   Reset_Handler, %function
Reset_Handler:

/* Copy the data segment initializers from flash to SRAM */
  movs  r1, #0
  b LoopCopyDataInit

CopyDataInit:
    ldr r3, =_sidata
    ldr r3, [r3, r1]
    str r3, [r0, r1]
    adds    r1, r1, #4

LoopCopyDataInit:
    ldr r0, =_sdata
    ldr r3, =_edata
    adds    r2, r0, r1
    cmp r2, r3
    bcc CopyDataInit
    ldr r2, =_sbss
    b   LoopFillZerobss
...

Во время отладки я заметил, что регистр r1 никогда не инициализируется нулем первой инструкцией movs r1, # 0. Регистр r1 используется в качестве счетчика в цикле, поэтому, когда выполнение достигает цикла LoopCopyDataInit, он никогда не входит в цикл, поскольку регистр r1 загружен некоторыми данными мусора из предыдущего выполнения. В результате этого код запуска никогда не инициализирует раздел .data.

Когда я поместил две инструкции nop перед инструкцией movs r1, # 0, регистр r1 был инициализирован равным 0, и пример начал работать:

Измененная часть файла startup_stm32f10x_md_vl.S:

/* Copy the data segment initializers from flash to SRAM */
  nop
  nop
  movs  r1, #0
  b LoopCopyDataInit

Это разборка соответствующих частей окончательного кода:

Disassembly of section .isr_vector:

08000000 <g_pfnVectors>:
 8000000:       20002000        andcs   r2, r0, r0
 8000004:       08000961        stmdaeq r0, {r0, r5, r6, r8, fp}
 ...

Disassembly of section .text:

 ...
8000960 <Reset_Handler>:
 8000960:   2100            movs    r1, #0
 8000962:   f000 b804       b.w     800096e <LoopCopyDataInit>

08000966 <CopyDataInit>:
 8000966:   4b0d            ldr     r3, [pc, #52]   ; (800099c <LoopFillZerobss+0x16>)
 8000968:   585b            ldr     r3, [r3, r1]
 800096a:   5043            str     r3, [r0, r1]
 800096c:   3104            adds    r1, #4 

Как видите, таблица векторов ISR правильно указывает на адрес Reset_Handler. Итак, что происходит? Почему первая инструкция movs r1, # 0 никогда не выполнялась в исходном коде запуска?

РЕДАКТИРОВАТЬ:

Исходный код работает, когда я выключаю плату и снова включаю ее. Я могу перезагружать MCU несколько раз, и он работает. Когда я запускаю gdb-server, код не работает даже после сброса. Мне нужно снова выключить и снова включить его, чтобы он заработал. Я предполагаю, что это какая-то странность отладчика.

ПРИМЕЧАНИЕ.

Я посмотрел, какой код запуска используют другие люди с этим MCU, и они либо отключают прерывания, либо загружают регистр SP с определенным компоновщиком значением, которое в обоих случаях является избыточным. Если бы они столкнулись с таким странным поведением, они бы этого никогда не заметили.


person BlueSky    schedule 19.03.2013    source источник
comment
Разве вектор сброса в g_pfnVectors не должен указывать на 08000960, а не на 08000961?   -  person Austin Phillips    schedule 20.03.2013
comment
Из Подробное руководство по ARM Cortex-M3, Джозеф Йиу, 3.7. Последовательность сброса: вектор сброса (начальный адрес выполнения программы; LSB должен быть установлен в 1, чтобы указать состояние пальца). Это говорит о том, что 8000961 на самом деле является адресом 08000960.   -  person BlueSky    schedule 20.03.2013
comment
В этом есть смысл. Не вижу ничего явно неправильного, возможно, это странное взаимодействие с вашим отладчиком.   -  person Austin Phillips    schedule 20.03.2013


Ответы (2)


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

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

GPIO_TypeDef* const GPIO_PORT[LEDn] = {LED3_GPIO_PORT, LED4_GPIO_PORT};
person Igor Skochinsky    schedule 20.03.2013
comment
Да, я там оставил NOP. В любом случае, это всего лишь пример проекта, который я взял из пакета freeRTOS. Я не являюсь автором кода, но согласен с вами, что const там нужен. Спасибо. - person BlueSky; 20.03.2013

Сразу ничего не выскакивает, что там могло быть не так. Во-первых, как вы отлаживаете этот код? Вы подключаете отладчик, а затем через JTAG выдаете сброс процессору? Я бы попытался поместить туда b Reset_Handler сразу после вашей метки Reset_Handler: в качестве вашей первой инструкции, прошить его, включить плату, затем подключить JTAG, чтобы вы могли минимизировать любые возможные странности от отладчика. Затем установите свой компьютер на эту mov инструкцию и посмотрите, работает ли она. Запускает ли этот код загрузчик или загрузочное ПЗУ? Возможно, что-то странное происходит с инструкциями или кешем данных.

person ThePosey    schedule 20.03.2013
comment
Я использую сервер texane gdb (usb to swd) для загрузки и отладки изображения. Когда я выполняю «b ResetHandler», компьютер уже указывает на «8000962», поэтому первая инструкция сразу же пропускается. - person BlueSky; 20.03.2013