Jump to content

    
Sign in to follow this  
MiklPolikov

STM32F429 + SDRAM + Keil

Recommended Posts

Есть STM32F429 + SDRAM IS42S16400J-7TLI 64Мбит , проект в Keil.

 

Возникли вопросы:

1) Можно ли сделать так, что бы обращение к памяти было не через указатель на её адрес, а просто путем инициализации переменной ?

Т.е. что бы в коде декларировалась переменная char x[100000]; а уже компилятор сам размещал её по адресу внешней памяти ?

2) Нужно сообщить компилятору о наличие этой памяти, а для этого адрес и размер памяти нужно указать вот тут (картинка) в строке RAM1 ? И поставить галочку слева ?

А что означает галочка справа no init ?

3) Нужно ли заботится о том, что бы во внешней памяти размещались только большие массивы, а все часто используемые переменные были во внутренней ?

4) Какие ещё тонкости нужно учитывать ?

 

Заранее спасибо !

post-24702-1449154414_thumb.jpg

Share this post


Link to post
Share on other sites
Есть 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. Переименовать скаттер файл и указать его явно в опциях проекта в секции компоновщика.

 

Share this post


Link to post
Share on other sites
Четыре шага:

При указании массива с атрибутом +ZI будет произведена его инициализация?

В какой момент производится инициализация? До main() в startup?

Внешняя память (интерфейс) настраивается после вызова main() и обращения к ней ранее не допустимы.

 

Что делать?:

1. Указать, что память не нуждается в инициализации и всю жизнь помнить об этом? Как это сделать?

2. Перенести инициализацию интерфейса внешней памяти вместе с тестированием в startup?

Нужно ли в нем заботится об инициализации областей data и bss или все случится на автомате?

А если это плюсы то, там, вроде, еще и конструкторы нужно инициализировать? Ничего не забыл?

Что делать если память не проходит тест? До инициализации памяти нужно еще много чего проиницализировать (RCC, GPIO).

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

Или забыть о Си и шпарить без переменных на асме в startup?

 

Короче, кто как делает?

Share this post


Link to post
Share on other sites
При указании массива с атрибутом +ZI будет произведена его инициализация?

ZI - это не атрибут переменной, а тип секции. Атрибут переменной - __attribute__((zero_init)),

при присвоении этого атрибута переменной, она будет помещена в секцию типа ZI.

Как ясно из названия, будет инициализирован нулями.

 

В какой момент производится инициализация? До main() в startup?

Уже после startup, внутри __main():

startup -> __main -> 'C' main()

 

1. Указать, что память не нуждается в инициализации и всю жизнь помнить об этом? Как это сделать?

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

 

2. Перенести инициализацию интерфейса внешней памяти вместе с тестированием в startup?

Нужно ли в нем заботится об инициализации областей data и bss или все случится на автомате?

А если это плюсы то, там, вроде, еще и конструкторы нужно инициализировать? Ничего не забыл?

Что делать если память не проходит тест? До инициализации памяти нужно еще много чего проиницализировать (RCC, GPIO).

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

Или забыть о Си и шпарить без переменных на асме в startup?

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

 

Инициализацию можно написать на 'C', не будут доступны для использования только глобальные инициализированные переменные.

Share this post


Link to post
Share on other sites

Спасибо большое за ответы.

Я понял, что лучше ничего не говорить компилятору про внешнюю память, а большие переменные писать в неё через указатели с заданными вручную адресами. :)

 

Share this post


Link to post
Share on other sites
Спасибо большое за ответы.

Я понял, что лучше ничего не говорить компилятору про внешнюю память, а большие переменные писать в неё через указатели с заданными вручную адресами. :)

 

Так конечно проще, но лучше все-таки сделать как вам посоветовали. Так по правилам.

Share this post


Link to post
Share on other sites
Я понял, что лучше ничего не говорить компилятору про внешнюю память, а большие переменные писать в неё через указатели с заданными вручную адресами. :)

Подход имеет право на жизнь, если вся внешняя память используется, например, для хранения пары массивов, задействованных в одном модуле программы.

Но даже в таком случае грех отбирать у линкера его хлеб.

Share this post


Link to post
Share on other sites
Подход имеет право на жизнь

Более того, оба подхода сосуществуют, если размещать-таки переменные с помощью секции, указать запрет их инициализации (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().

Edited by KnightIgor

Share this post


Link to post
Share on other sites

Подниму тему, никак не могу понять одну вещь.

Использую 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, зачем он вообще туда лезет? И как правильно это дело настроить? Через указатели не очень хочется.

Share this post


Link to post
Share on other sites
Но, ведь я указал, что эта область UNINIT, зачем он вообще туда лезет?

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

 

И как правильно это дело настроить? Через указатели не очень хочется.

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

Share this post


Link to post
Share on other sites
UNINIT, но не ZI, например.

 

 

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

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

 

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

 

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

:(((

 

 

 

Share this post


Link to post
Share on other sites
Но у меня есть еще большой массив данных, который я храню во внешней памяти.

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

:(((

Я не знаю Кейла, но например в IAR, чтобы переменная не обнулялась си-стартап-кодом, недостаточно ей указать атрибут __no_init. Надо ещё её поместить в соответствующую (отдельную от .bss) секцию и указать компоновщику в его файле конфигурации не инициализировать эту секцию:

в *.cpp: static __no_init int info @ ".eraw";

в *.icf: do not initialize {section .eraw};

Возможно, что в Кейле аналогично.

Я много раз использовал в проектах внешнюю память, размещал в ней и переменные (инициализированные и нет) и код и без каких-то проблем. Точно так же как во внутренней ОЗУ. Правда всё под IAR и CCS.

Share this post


Link to post
Share on other sites
Я не знаю Кейла, но например в 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)
  }

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this