Я сказал в вопросе:
// 1. correct way A:
extern uint32_t __flash_start__;
printf("__flash_start__ addr = 0x%lX\n", (uint32_t)&__flash_start__);
// OR 2. correct way B (my preferred approach):
extern uint32_t __flash_start__[]; // not a true array; [] is required to access linker script variables (addresses) as though they were normal variables
printf("__flash_start__ addr = 0x%lX\n", (uint32_t)__flash_start__);
// OR 3. COMPLETELY WRONG WAY TO DO IT!
// - IS THIS UNDEFINED BEHAVIOR?
extern uint32_t __flash_start__;
printf("__flash_start__ addr = 0x%lX\n", __flash_start__);
(см. Обсуждение под вопросом о том, как я пришел к этому).
Особо глядя на № 3 выше :
Ну, на самом деле, если ваша цель - прочитать адрес из __flash_start__
, который в данном случае равен 0x8000000
, тогда да, это совершенно неправильно. Но это НЕ неопределенное поведение! Вместо этого он фактически читает содержимое (значение) этого адреса (0x8000000
) как тип uint32_t
. Другими словами, он просто читает первые 4 байта раздела FLASH и интерпретирует их как uint32_t
. содержимое (uint32_t
значение по этому адресу) в данном случае просто равно 0x20080000
.
Чтобы дополнительно доказать эту точку зрения, следующие абсолютно идентичные:
// Read the actual *contents* of the __flash_start__ address as a 4-byte value!
// The 2 techniques should be the same.
extern uint32_t __flash_start__;
uint32_t u32_1 = __flash_start__;
uint32_t u32_2 = *((uint32_t *)&__flash_start__);
printf("u32_1 = 0x%lX\n", u32_1);
printf("u32_2 = 0x%lX\n", u32_2);
Вывод:
u32_1 = 0x20080000
u32_2 = 0x20080000
Обратите внимание, что они дают одинаковый результат. Каждый из них выдает действительное значение uint32_t
-типа, которое хранится по адресу 0x8000000
.
Оказывается, однако, что техника u32_1
, показанная выше, является более прямым и прямым способом чтения значения all, и опять же, это , а не неопределенное поведение. Скорее, он правильно читает значение (содержимое) этого адреса.
Кажется, я говорю по кругу. Во всяком случае, сумасшедший, но теперь я понял. Я был убежден, прежде чем должен был использовать только технику u32_2
, показанную выше, но оказалось, что они оба просто хороши, и опять же, техника u32_1
явно более прямолинейна (там я снова говорю кругами ). :)
Приветствие.
Копаем глубже: Откуда взято значение 0x20080000
, хранящееся в самом начале моей флэш-памяти?
Еще один маленький кусочек. Я на самом деле запустил этот тестовый код на STM32F777 MCU, который имеет 512 КБ ОЗУ. Поскольку RAM начинается с адреса 0x20000000, это означает, что 0x20000000 + 512K = 0x20080000. Так же бывает и содержимое ОЗУ с нулевым адресом, потому что Руководство по программированию PM0253 Ред. 4 , стр. 42, «Рисунок 10. Таблица векторов» показывает, что первые 4 байта таблицы векторов содержат «Начальное значение SP [Указатель стека]». Смотрите здесь:
Я знаю, что Vector Table находится прямо в начале памяти программ, которая находится во Flash, так что это означает, что 0x20080000 - это мое начальное значение указателя стека. Это имеет смысл, потому что Reset_Handler
- это начало программы (и, кстати, ее вектор просто является вторым 4-байтовым значением в начале таблицы векторов), и первое, что он делает, как показано в моем файле запуска сборки " startup_stm32f777xx.s ", указатель стека (sp) установлен в _estack
:
Reset_Handler:
ldr sp, =_estack /* set stack pointer */
Кроме того, _estack
определяется в моем скрипте компоновщика следующим образом:
/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of RAM */
Итак, вот оно! Первое 4-байтовое значение в моей Таблице векторов, в самом начале Flash, задано как начальное значение указателя стека, которое определено как _estack
прямо в моем файле сценария компоновщика, а _estack
- это адрес в конец моей оперативной памяти, которая составляет 0x20000000 + 512K = 0x20080000. Итак, все это имеет смысл! Я только что доказал, что прочитал правильное значение!