MiklPolikov 0 3 декабря, 2015 Опубликовано 3 декабря, 2015 · Жалоба Есть STM32F429 + SDRAM IS42S16400J-7TLI 64Мбит , проект в Keil. Возникли вопросы: 1) Можно ли сделать так, что бы обращение к памяти было не через указатель на её адрес, а просто путем инициализации переменной ? Т.е. что бы в коде декларировалась переменная char x[100000]; а уже компилятор сам размещал её по адресу внешней памяти ? 2) Нужно сообщить компилятору о наличие этой памяти, а для этого адрес и размер памяти нужно указать вот тут (картинка) в строке RAM1 ? И поставить галочку слева ? А что означает галочка справа no init ? 3) Нужно ли заботится о том, что бы во внешней памяти размещались только большие массивы, а все часто используемые переменные были во внутренней ? 4) Какие ещё тонкости нужно учитывать ? Заранее спасибо ! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 3 декабря, 2015 Опубликовано 3 декабря, 2015 · Жалоба Есть 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 11 3 декабря, 2015 Опубликовано 3 декабря, 2015 · Жалоба Четыре шага: При указании массива с атрибутом +ZI будет произведена его инициализация? В какой момент производится инициализация? До main() в startup? Внешняя память (интерфейс) настраивается после вызова main() и обращения к ней ранее не допустимы. Что делать?: 1. Указать, что память не нуждается в инициализации и всю жизнь помнить об этом? Как это сделать? 2. Перенести инициализацию интерфейса внешней памяти вместе с тестированием в startup? Нужно ли в нем заботится об инициализации областей data и bss или все случится на автомате? А если это плюсы то, там, вроде, еще и конструкторы нужно инициализировать? Ничего не забыл? Что делать если память не проходит тест? До инициализации памяти нужно еще много чего проиницализировать (RCC, GPIO). Причем, такая инициализация использует переменные, которые тоже должны быть проинициализированы. Или забыть о Си и шпарить без переменных на асме в startup? Короче, кто как делает? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 69 3 декабря, 2015 Опубликовано 3 декабря, 2015 · Жалоба При указании массива с атрибутом +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', не будут доступны для использования только глобальные инициализированные переменные. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MiklPolikov 0 3 декабря, 2015 Опубликовано 3 декабря, 2015 · Жалоба Спасибо большое за ответы. Я понял, что лучше ничего не говорить компилятору про внешнюю память, а большие переменные писать в неё через указатели с заданными вручную адресами. :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Tarbal 4 3 декабря, 2015 Опубликовано 3 декабря, 2015 · Жалоба Спасибо большое за ответы. Я понял, что лучше ничего не говорить компилятору про внешнюю память, а большие переменные писать в неё через указатели с заданными вручную адресами. :) Так конечно проще, но лучше все-таки сделать как вам посоветовали. Так по правилам. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 69 3 декабря, 2015 Опубликовано 3 декабря, 2015 · Жалоба Я понял, что лучше ничего не говорить компилятору про внешнюю память, а большие переменные писать в неё через указатели с заданными вручную адресами. :) Подход имеет право на жизнь, если вся внешняя память используется, например, для хранения пары массивов, задействованных в одном модуле программы. Но даже в таком случае грех отбирать у линкера его хлеб. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 4 декабря, 2015 Опубликовано 4 декабря, 2015 (изменено) · Жалоба Подход имеет право на жизнь Более того, оба подхода сосуществуют, если размещать-таки переменные с помощью секции, указать запрет их инициализации (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(). Изменено 4 декабря, 2015 пользователем KnightIgor Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
QuadMan 0 10 мая, 2017 Опубликовано 10 мая, 2017 · Жалоба Подниму тему, никак не могу понять одну вещь. Использую 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 69 10 мая, 2017 Опубликовано 10 мая, 2017 · Жалоба Но, ведь я указал, что эта область UNINIT, зачем он вообще туда лезет? UNINIT, но не ZI, например. И как правильно это дело настроить? Через указатели не очень хочется. Если речь идет о периферии, то правильно будет как раз через указатели. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
QuadMan 0 11 мая, 2017 Опубликовано 11 мая, 2017 · Жалоба UNINIT, но не ZI, например. Если речь идет о периферии, то правильно будет как раз через указатели. Ok, периферию я переделал на указатели, не сложно. Но у меня есть еще большой массив данных, который я храню во внешней памяти. Атрибут zero_init в компиляторе версии 6.6 не распознается, хотя это, похоже, именно то, что нужно. :((( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 12 мая, 2017 Опубликовано 12 мая, 2017 · Жалоба Но у меня есть еще большой массив данных, который я храню во внешней памяти. Атрибут zero_init в компиляторе версии 6.6 не распознается, хотя это, похоже, именно то, что нужно. :((( Я не знаю Кейла, но например в IAR, чтобы переменная не обнулялась си-стартап-кодом, недостаточно ей указать атрибут __no_init. Надо ещё её поместить в соответствующую (отдельную от .bss) секцию и указать компоновщику в его файле конфигурации не инициализировать эту секцию: в *.cpp: static __no_init int info @ ".eraw"; в *.icf: do not initialize {section .eraw}; Возможно, что в Кейле аналогично. Я много раз использовал в проектах внешнюю память, размещал в ней и переменные (инициализированные и нет) и код и без каких-то проблем. Точно так же как во внутренней ОЗУ. Правда всё под IAR и CCS. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
yanvasilij 0 14 мая, 2017 Опубликовано 14 мая, 2017 · Жалоба Я не знаю Кейла, но например в 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) } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться