Метка: arm

ARM Without Magic. Урок 1.2 Линковка

Пришла пора привести в понятный вид скрипт линковки для прошивки под STM32 (или любой другой ARM Cortex-M).
Прошлый раз мы писали как попало и не очень понятно было, где и что лежит. Будем устранять непонятности.
Я все время говорю – символы, секции. Давайте займемся терминологией.
Первое и главное – это символ. Для компоновщика символ это именованный адрес не более того. (правда похоже на переменную в любом языке программирования?). Причем не важно, что скрывается под этим символом – адрес переменной, код функции, все что угодно. Плюс некоторые «атрибуты», такие как принадлежность к секции, размер. Когда в коде написано uint32_t var_a = 12345;, при компиляции создается символ var_a, в атрибуты помещается размер в 4 байта и принадлежность к секции .data (мы уже говорили, что инициализированные переменные попадают в эту секцию). Для функций – примерно тоже самое, только секция будет .text, ну и размер нельзя вот прямо так сразу назвать, но после компиляции он конечно же будет известен.
Читать далее

Проблема со сбросом stm32F7xx в OpenOCD

Симптомы проблемы такие: на версии Open On-Chip Debugger 0.10.0+dev-00924-g16496488 выглядело как ошибка:
in procedure 'program'
in procedure 'program_error' called at file "embedded:startup.tcl", line 474

на Open On-Chip Debugger 0.10.0+dev-00973-g80f1a92b
ошибки нет, но чипу мозг выносит, вместо резета он прыгает в какую-нибудь функцию и там умирает

Вроде как решается правкой stm32f7x.cfg

reset_config srst_only srst_nogate
на
reset_config srst_nogate

STM32 CCM

Есть у некоторых взрослых STM-ок такая дурная штука, как CCM (core coupled memory). Это отдельный регион памяти, не шуточного размера в 64K (для F4xx), который по-умолчанию вообще не используется. Выделена эта память в отдельный регион, потому что имеет подключение только к D-BUS ядра, что накладывает кое-какие ограничения. Главное — это невозможно использовать вместе с DMA и DMA2D и выполнять код (примечание: это для F40x, но вообще неплохо заглянуть в PDF, раздел «Архитектура системы»), а в остальном — память как память.
Есть несколько способов хоть как-то его начать использовать, самый простой способ — это положить туда стек.
Для этого нужно модифицировать всего одну строку скрипта линковки, ту что задает адрес _estack и положить в этот символ адрес конца CCM (0x10010000):

_estack = 0x10010000;    /* end of CCMRAM */

При этом весь стек переедет в CCM и как следствие все локальные переменные, аргументы функции итд.
Второй не менее простой, но довольно ручной способ — это указание секции при описании переменной:

__attribute__((section(".ccmram"))) int ccmvar;

однако, стоит при этом помнить, что без модификации startup-скрипта при инициализации там будет мусор, заполним нулями, добавив где-нибудь после FillZeroBSS

/* Zero fill the CCM segment. */
ldr  r2, =_sccmram
FillZeroCCM:
  movs  r3, #0
  str  r3, [r2], #4
 
LoopFillZeroCCM:
  ldr  r3, = _eccmram
  cmp  r2, r3
  bcc  FillZeroCCM

а заодно уберем в скрипте линковки из описания секции .ccmram запись инициализации во FLASH:

  .ccmram (NOLOAD):
  {
    . = ALIGN(4);
    _sccmram = .;       /* create a global symbol at ccmram start */
    *(.ccmram)
    *(.ccmram*)
 
    . = ALIGN(4);
    _eccmram = .;       /* create a global symbol at ccmram end */
   } >CCMRAM

теперь все переменные с attribute((section(«.ccmram»))) будут стартовать с нулевыми значениями.

Подробнее про переменные, секции и инициализацию можно почитать тут: ARM Without Magic. Урок 1.1 Переменные и инициализация.

STM32: Шифруем прошивку.

Не сильно хорошая, но все же защита от бездумного копирования устройства. А может быть и еще что-то. В общем, идея такая: зашифровать некоторые критические функции, без которых устройство работать не будет, хитрожопые алгоритмы или математика какая-нибудь. Причем, желательно не потерять удобную сборку и отладку, и использовать желательно без всяких указателей. Я использую arm-none-eabi-gcc в качестве тулчейна и CMake как систему сборки. Поэтому все нижесказанное относится именно к этой связки и для других компиляторов-сборщиков придется немного перепилить.
Читать далее

STM32 и malloc во внешней памяти FMC / SDRAM

После того, как FMC/FSMC запустился и работает, внешняя память отражается по соответствующим адресам. Эту память можно использовать по захардкоденным адресам

char * ptr = (char*)0xC0000000;

но каждый раз вычислять адреса не комильфо. Тем более, для этого как раз есть специальная штука — heap и malloc/free.
Нужно только рассказать malloc где у нас эта память и сколько её. Самый простой способ — исправить правила линковки: Суть такова. _sbrk использует память начиная c адреса переданного линковщиком как end, стоит положить его адрес SDRAM, как malloc начинает раздавать адреса из неё.
Читать далее

ARM Semihosting

В ARM-контроллерах есть полезнейшая вещица, Semihosting, позволяет переадресовывать I/O операции к хосту
Почему-то при отладке, в основном используют только printf() для вывода отладочных сообщений, хотя возможности порядком шире: можно переадресовать любую(!) I/O операцию. Например запись файла.
Для быстрого примера, создадим проект в Eclipse, конфигуратор проекта спросит про системные вызовы:
Use system calls — вот тут надо выбрать Semihosting (POSIX system calls via host) — это полное перенаправление всех POSIX вызовов к хосту.
Trace output — не важно, пусть будет Semihosting STDOUT, отладочные сообщения будут падать в STDOUT сервера
Ну и чтобы побыстрее все проверить — content — стандартный шаблон Blinky — мигалка светодиодом.

А дальше все просто — добавляем в main обычный код для вывода в файл:

 FILE* f = fopen("./test.txt", "w");
  if(f){
      fprintf(f, "Hello from ARM...\n");
      fclose(f);
  }

Думаю, и так понятно, что мы открываем файл test.txt для записи в текущем (относительно semihosting сервера) файл, и если открылся — записываем в него строку.
собираем, а вот запускать для наглядности будем из консоли:
у меня под рукой STM32VL-Discovery, поэтому конфиг запуска для неё

$ openocd -f board/stm32vldiscovery.cfg

Далее, запускаем openocd который и будет semihosting server
цепляемся к нему отладчиком

$ arm-none-eabi-gdb project.elf

и выполняем следующие команды в отладчике

target remote :3333  # подключаемся а GDB-серверу OpenOCD
monitor arm semihosting enable # включаем semihosting
monitor init # инициализируем кристал
monitor reset init # сбрасываем кристал
load  # загружаем в кристал файл указанный в параметре запука gdb
continue # запускаем запущенную программу

прошивка blinky содержит в себе примеры отладочных сообщений, так что можно вполне их посмотреть в консоли OpenOCD, а после выполнения добавленного участка кода — можно смотреть на файл test.txt:

$ cat test.txt
Hello from ARM...

Да, самое главное — прошивка с включенным Semihosting не сможет работать в standalone режиме (т.е. без подключенного отладчика), застопорится на первом же обращении к хосту. Поэтому в release версии нужно отключать. Для этого в элипс достаточно удалить define в соответствующей конфигурации