Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32F429 + SDRAM + Keil
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
MiklPolikov
Есть STM32F429 + SDRAM IS42S16400J-7TLI 64Мбит , проект в Keil.

Возникли вопросы:
1) Можно ли сделать так, что бы обращение к памяти было не через указатель на её адрес, а просто путем инициализации переменной ?
Т.е. что бы в коде декларировалась переменная char x[100000]; а уже компилятор сам размещал её по адресу внешней памяти ?
2) Нужно сообщить компилятору о наличие этой памяти, а для этого адрес и размер памяти нужно указать вот тут (картинка) в строке RAM1 ? И поставить галочку слева ?
А что означает галочка справа no init ?
3) Нужно ли заботится о том, что бы во внешней памяти размещались только большие массивы, а все часто используемые переменные были во внутренней ?
4) Какие ещё тонкости нужно учитывать ?

Заранее спасибо !
KnightIgor
Цитата(MiklPolikov @ Dec 3 2015, 15:53) *
Есть STM32F429 + SDRAM IS42S16400J-7TLI 64Мбит , проект в Keil.
Возникли вопросы:

Мне кажется, ультимативный подход - это размещение переменных для внешней RAM путем размещения их в определенной секции с последующим указанием в скрипте компоновщика (скаттере), что секция должна попасть во внешнюю RAM.

Четыре шага:
1. В опциях проекта, именно в том диалоге, что Вы привели в картинке, активировать либо IRAM2, либо RAM1, с указанием области адресов и размера.
2. Переменная для той RAM получит атрибут с именем секции:

char BlaBla[1000] __attribute__((section("EXTRAM")));

3. Скаттер, который генерируется по-умолчанию (имя проекта.SCT), дополнить (сразу за RW_IRAM1):

RW_IRAM2 <begin address> <size> {
*(EXTRAM)(+RW +ZI)
} ; если активирована IRAM2


или

RW_RAM1 <begin address> <size> {
*(EXTRAM)(+RW +ZI)
} ; если активирована RAM1


4. Переименовать скаттер файл и указать его явно в опциях проекта в секции компоновщика.
adnega
Цитата(KnightIgor @ Dec 3 2015, 20:11) *
Четыре шага:

При указании массива с атрибутом +ZI будет произведена его инициализация?
В какой момент производится инициализация? До main() в startup?
Внешняя память (интерфейс) настраивается после вызова main() и обращения к ней ранее не допустимы.

Что делать?:
1. Указать, что память не нуждается в инициализации и всю жизнь помнить об этом? Как это сделать?
2. Перенести инициализацию интерфейса внешней памяти вместе с тестированием в startup?
Нужно ли в нем заботится об инициализации областей data и bss или все случится на автомате?
А если это плюсы то, там, вроде, еще и конструкторы нужно инициализировать? Ничего не забыл?
Что делать если память не проходит тест? До инициализации памяти нужно еще много чего проиницализировать (RCC, GPIO).
Причем, такая инициализация использует переменные, которые тоже должны быть проинициализированы.
Или забыть о Си и шпарить без переменных на асме в startup?

Короче, кто как делает?
aaarrr
Цитата(adnega @ Dec 3 2015, 21:08) *
При указании массива с атрибутом +ZI будет произведена его инициализация?

ZI - это не атрибут переменной, а тип секции. Атрибут переменной - __attribute__((zero_init)),
при присвоении этого атрибута переменной, она будет помещена в секцию типа ZI.
Как ясно из названия, будет инициализирован нулями.

Цитата(adnega @ Dec 3 2015, 21:08) *
В какой момент производится инициализация? До main() в startup?

Уже после startup, внутри __main():
startup -> __main -> 'C' main()

Цитата(adnega @ Dec 3 2015, 21:08) *
1. Указать, что память не нуждается в инициализации и всю жизнь помнить об этом? Как это сделать?

В scatter-файле через атрибут секции UNINIT.

Цитата(adnega @ Dec 3 2015, 21:08) *
2. Перенести инициализацию интерфейса внешней памяти вместе с тестированием в startup?
Нужно ли в нем заботится об инициализации областей data и bss или все случится на автомате?
А если это плюсы то, там, вроде, еще и конструкторы нужно инициализировать? Ничего не забыл?
Что делать если память не проходит тест? До инициализации памяти нужно еще много чего проиницализировать (RCC, GPIO).
Причем, такая инициализация использует переменные, которые тоже должны быть проинициализированы.
Или забыть о Си и шпарить без переменных на асме в startup?

А смысл тестировать память? Внутреннюю тоже тестируете? Неисправная железка - это неисправная железка, ничего не поделаешь.

Инициализацию можно написать на 'C', не будут доступны для использования только глобальные инициализированные переменные.
MiklPolikov
Спасибо большое за ответы.
Я понял, что лучше ничего не говорить компилятору про внешнюю память, а большие переменные писать в неё через указатели с заданными вручную адресами. sm.gif
Tarbal
Цитата(MiklPolikov @ Dec 3 2015, 23:20) *
Спасибо большое за ответы.
Я понял, что лучше ничего не говорить компилятору про внешнюю память, а большие переменные писать в неё через указатели с заданными вручную адресами. sm.gif


Так конечно проще, но лучше все-таки сделать как вам посоветовали. Так по правилам.
aaarrr
Цитата(MiklPolikov @ Dec 3 2015, 22:20) *
Я понял, что лучше ничего не говорить компилятору про внешнюю память, а большие переменные писать в неё через указатели с заданными вручную адресами. sm.gif

Подход имеет право на жизнь, если вся внешняя память используется, например, для хранения пары массивов, задействованных в одном модуле программы.
Но даже в таком случае грех отбирать у линкера его хлеб.
KnightIgor
Цитата(aaarrr @ Dec 3 2015, 21:14) *
Подход имеет право на жизнь

Более того, оба подхода сосуществуют, если размещать-таки переменные с помощью секции, указать запрет их инициализации (noinit), но потом, ссылаясь на них как на нормально объявленные переменные (об адресах позаботился компоновщик), проинициализировать, как желаемо, да и работать потом с ними проще... Как правильно замечено, зачем отбирать у компоновщика лопату и месить бетон самому, досыпая кучу #define и заботясь вручную о распределении памяти? При таком подходе чуть что забыл, все поехало, наехало, налезло и поломало. Потом пойдут вопросы "у меня спорадически портится кусок памяти, где искать".

Кстати, я смутно припоминаю, потому могу ошибаться, что в библиотеке в части инициализации внутри __main предусмотрены "виртуальные" (слабоопределенные) функции, которые можно переписать с целью предварительной инициализации аппаратуры доступа к внешней памяти. Где-то на странице arm или onarm было. А если это-таки плод моей фантазии, то можно либо дополнить system_"processor".c, где реализована функция SystemInit(), вызываемая из startup_"processor".s, либо модифицировать тот же startup_"processor".s как ниже:
Код
              
LDR     R0, =SystemInit
BLX     R0              
IMPORT ExtRAMInit
LDR     R0, =ExtRAMInit
BLX     R0              
LDR     R0, =__main
BX      R0

написав на С функцию ExtRAMInit().
QuadMan
Подниму тему, никак не могу понять одну вещь.
Использую Keil, ARM Compiler V6.6.

Вот есть у меня внешняя периферия, которая начинается с адреса 0x50000000 к примеру.
Объявляю структуру
Код
volatile __attribute__((section(".ARM.__at_0x50000000"))) SystemRegs  REGS;

Программа загружается во внешнюю память по адресу 0x10000000 и оттуда работает

в scatter файле пишу
Код
LR_IROM1 0x10000000 0x0000C800  {   ; load region size_region
  ER_IROM1 0x10000000 0x0000C800  { ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
    *(.init_array) ; Section .init_array must be placed explicitly,
                   ; otherwise it is shared between two regions, and
                   ; the linker is unable to decide where to place it.  
  }

  RW_IRAM1 0x20000000 UNINIT 0x0008000  { ; RW data
   .ANY (+RW +ZI)
  }

  ER_PERIPHERAL 0x50000000 UNINIT 0x1000
  {
    *(.ARM.__at_0x50000000)
  }
}

Теперь после запуска отладчика, программа валится в MemoryMngFault, судя по всему при отработке функции _decompress системной библиотеки.
Если убрать секцию ER_PERIPHERAL из scatter файла, и в определении переменной REGS, все компилится и запускается нормально, но REGS, естественно, располагается во внутренней памяти.

Отсюда я делаю вывод, что при загрузке компилятор пытается проинициализировать переменную REGS во внешней памяти, хотя доступ к ней еще не настроен, поэтому и валится в HardFault.

Но, ведь я указал, что эта область UNINIT, зачем он вообще туда лезет? И как правильно это дело настроить? Через указатели не очень хочется.
aaarrr
Цитата(QuadMan @ May 10 2017, 16:42) *
Но, ведь я указал, что эта область UNINIT, зачем он вообще туда лезет?

UNINIT, но не ZI, например.

Цитата(QuadMan @ May 10 2017, 16:42) *
И как правильно это дело настроить? Через указатели не очень хочется.

Если речь идет о периферии, то правильно будет как раз через указатели.
QuadMan
Цитата(aaarrr @ May 10 2017, 23:23) *
UNINIT, но не ZI, например.


Если речь идет о периферии, то правильно будет как раз через указатели.

Ok, периферию я переделал на указатели, не сложно.

Но у меня есть еще большой массив данных, который я храню во внешней памяти.

Атрибут zero_init в компиляторе версии 6.6 не распознается, хотя это, похоже, именно то, что нужно.
sad.gif((


jcxz
Цитата(QuadMan @ May 11 2017, 14:15) *
Но у меня есть еще большой массив данных, который я храню во внешней памяти.
Атрибут zero_init в компиляторе версии 6.6 не распознается, хотя это, похоже, именно то, что нужно.
sad.gif((

Я не знаю Кейла, но например в IAR, чтобы переменная не обнулялась си-стартап-кодом, недостаточно ей указать атрибут __no_init. Надо ещё её поместить в соответствующую (отдельную от .bss) секцию и указать компоновщику в его файле конфигурации не инициализировать эту секцию:
в *.cpp: static __no_init int info @ ".eraw";
в *.icf: do not initialize {section .eraw};
Возможно, что в Кейле аналогично.
Я много раз использовал в проектах внешнюю память, размещал в ней и переменные (инициализированные и нет) и код и без каких-то проблем. Точно так же как во внутренней ОЗУ. Правда всё под IAR и CCS.
yanvasiij
Цитата(jcxz @ May 12 2017, 11:34) *
Я не знаю Кейла, но например в IAR, чтобы переменная не обнулялась си-стартап-кодом, недостаточно ей указать атрибут __no_init. Надо ещё её поместить в соответствующую (отдельную от .bss) секцию и указать компоновщику в его файле конфигурации не инициализировать эту секцию:


В keil тоже самое, но там файл линковщика называется scatter-файлом подробнее о нем можно почитать например ТУТ, по синтаксису очень похож и на IAR и на gcc. Чтобы начать его редактировать нужно перейти в указанном ТС окне во вкладку Linker, там убрать галочку Use memory layot from target dialog, после этого станет активной строчка Scatter file, слева от этой строчки нажимаете на кнопку edit и открывается Scatter-файл на редактирование. Там вы можете объявлять секции внутри внешней ОЗУ и размещать в ней, что хотите, даже bss целых файлов. Например, есть у вас внешняя SDRAM по адресу 0x80000000 размером скажем 0x00010000 байт, и вы хотите разместить в ней всю оперативную память из файла blalbacode.cpp, тогда в scatter нужно добавить:

Код
  RW_SDRAM1 0x80000000 0x00010000  {; RW data
   blablacode.o (+RW +ZI)
  }
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.