Я использую компилятор 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 с определенным компоновщиком значением, которое в обоих случаях является избыточным. Если бы они столкнулись с таким странным поведением, они бы этого никогда не заметили.
g_pfnVectors
не должен указывать на08000960
, а не на08000961
? - person Austin Phillips   schedule 20.03.2013