|
прыгнуть на другой адрес в ROM |
|
|
|
Apr 5 2008, 08:17
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Приветствую, MCU: at91sam7s256 toolchain: Yagarto (gcc-4.2.1) Есть два простых приложения. Образ первого (размером XXX байт) расположен по адресу 0x100000 (т.е. в самом начале флеша), печатает строку в DBGU и переходит на адрес, по которому расположен другой образ. Второй образ лежит по адресу 0x100000 + XXX . Все что он делает это выводит другую строку в DBGU и на этом успокаивается. Но проблема в том, что переход по этому адресу не происходит! Адреса прописал верно, скрипты линкера подлправил. Не знаю, что я упустил... Прикладываю маленкьй архив с кодом обоих приложений и ld-скриптов. Очень надеюсь, что знающие люди подскажут где я облажался. Спасибо!
|
|
|
|
|
 |
Ответов
(1 - 55)
|
Apr 6 2008, 07:54
|
Частый гость
 
Группа: Свой
Сообщений: 172
Регистрация: 5-08-06
Из: Владивосток
Пользователь №: 19 343

|
Не специалист, увы, но по моим соображениям: - внушает опасение длина кода (orig 0x001004f8, LENGTH = 0x00010000), а ведь остается (0x10000 - 0x4f8). - а объектники линкуются в один файл? или создаете два образа, которые потом прошиваете один за другим? - стоило бы скачать прошивку с цели и посмотреть, есть ли в 0x1004fd искомое, а то может, его просто нет... Наверно, так.
|
|
|
|
|
Apr 6 2008, 08:27
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(Leen @ Apr 6 2008, 10:54)  Не специалист, увы, но по моим соображениям: - внушает опасение длина кода (orig 0x001004f8, LENGTH = 0x00010000), а ведь остается (0x10000 - 0x4f8). гм.. я сейчас не на работе, в понедельник проверю. Цитата - а объектники линкуются в один файл? или создаете два образа, которые потом прошиваете один за другим? Я создаю два разных образа и прошиваю один за другим. Цитата - стоило бы скачать прошивку с цели и посмотреть, есть ли в 0x1004fd искомое, а то может, его просто нет... Наверно, так. образ прошивается правильно, по крайней мере по адресу 0x1004fd он находится, проверял с помощью SAM-BA.
|
|
|
|
|
Apr 6 2008, 10:53
|

Группа: Участник
Сообщений: 13
Регистрация: 20-10-07
Пользователь №: 31 533

|
делал нечто похожее, но на lpc2378. Проблема была в том, что startup от первого приложения переводил процессор в user mode, соответственно startup от второго приложения уже не мог переключиться в supervisor mode (нужно для инициализации стеков в каждом их режимов). Все заработало после того, как я убрал перевод первого приложения в user mode из startup.
|
|
|
|
|
Apr 6 2008, 23:39
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(r301 @ Apr 6 2008, 13:53)  делал нечто похожее, но на lpc2378. Проблема была в том, что startup от первого приложения переводил процессор в user mode, соответственно startup от второго приложения уже не мог переключиться в supervisor mode (нужно для инициализации стеков в каждом их режимов). Все заработало после того, как я убрал перевод первого приложения в user mode из startup. Приветствую. Проверил весь стартап, нигде переключения в user режим не обнаружил. Даже не знаю, что еще проверить...
|
|
|
|
|
Apr 7 2008, 06:25
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(Leen @ Apr 7 2008, 08:21)  А Вы можете сдампить прошивку контроллера и выложить ее? Хекс, без отладочных файлов... В симулятор загнать, посмотреть, чего он там творит... По-иному не знаю, как. Здравствуйте. А что за симулятор? У меня gnu-arm тулчейн, в комлекте ничего похожего на симулятор нет.
|
|
|
|
|
Apr 7 2008, 09:54
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(romez777 @ Apr 6 2008, 11:27)  Я создаю два разных образа и прошиваю один за другим. Возьмите каждый из этих образов (.elf) и при помощи arm-elf-objcopy получите дизассемблерный листинг каждого из них. После этого пройдите "в уме" каждый листинг по командам и убедитесь, что 1) программы располагаются в тех адресах, куда вы их хотели разместить 2) они пытаются делать то, что вы от них хотели. Еще посмотрите, не затирает ли самба одну прошивку другой.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Apr 7 2008, 11:24
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 5 2008, 11:17)  Приветствую,
MCU: at91sam7s256 toolchain: Yagarto (gcc-4.2.1)
Есть два простых приложения. Образ первого (размером XXX байт) расположен по адресу 0x100000 (т.е. в самом начале флеша), печатает строку в DBGU и переходит на адрес, по которому расположен другой образ.
Второй образ лежит по адресу 0x100000 + XXX . Все что он делает это выводит другую строку в DBGU и на этом успокаивается.
Но проблема в том, что переход по этому адресу не происходит! Адреса прописал верно, скрипты линкера подлправил. Не знаю, что я упустил...
Прикладываю маленкьй архив с кодом обоих приложений и ld-скриптов. Очень надеюсь, что знающие люди подскажут где я облажался.
Спасибо! А что, собственно, располагается по адресу 0x1004f8? А Вы что ожидаете по этому адресу? main()? Так оно не должно там быть. А startup код у Вас есть? Где он? В обоих программах? И дайте вывод Код arm-elf-objdump -SD app1.elf arm-elf-objdump -SD app2.elf PS: insight просимулирует этот код, но без периферии. Да из eclipse на gdb при target sim. Не знаю, входит ли insight в Yagarto. Я сам собирал.
Сообщение отредактировал amw - Apr 7 2008, 11:29
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 8 2008, 03:34
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(amw @ Apr 7 2008, 14:24)  А что, собственно, располагается по адресу 0x1004f8? А Вы что ожидаете по этому адресу? main()? Так оно не должно там быть. А startup код у Вас есть? Где он? В обоих программах? Startup конечно же есть. Цитата И дайте вывод Код arm-elf-objdump -SD app1.elf arm-elf-objdump -SD app2.elf Приаттачил вывод objdump, C-startup и линк-скрипты (код немного модифицировался, размер бинарника и адрес второго образа тоже).
Прикрепленные файлы
boot.zip ( 152.29 килобайт )
Кол-во скачиваний: 99
|
|
|
|
|
Apr 9 2008, 09:21
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(amw @ Apr 7 2008, 14:24)  И дайте вывод Код arm-elf-objdump -SD app1.elf arm-elf-objdump -SD app2.elf Поизучал полученный дамп, но вроде никакого криминала не обнаружилось. Вот так выглядят инструкции перехода на другой образ: Код ... ((funct)0x100580)(); 1002f2: 4b06 ldr r3, [pc, #24] (10030c <.text+0x30c>) 1002f4: f000 f80c bl 100310 <.text+0x310> ... Может быть стартап второго приложения нужно переписать (на данный момент он идентичен с первым), ведь первое приложение все уже инициализирует, во втором наверное достаточно только настроить стек и bss? Есть еще какие-то идеи? Спасибо!
|
|
|
|
|
Apr 9 2008, 10:35
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(romez777 @ Apr 9 2008, 12:21)  Может быть стартап второго приложения нужно переписать (на данный момент он идентичен с первым), ведь первое приложение все уже инициализирует, во втором наверное достаточно только настроить стек и bss? А вот этого делать ни в коем случае не нужно. Мало ли что понанастраивало первое приложение. Второму виднее, что ему нужно и пусть оно настроит все, в чем нуждается. Иначе у вас будет слишком большая зависимость второго приложения от первого. Цитата(romez777 @ Apr 9 2008, 12:21)  Поизучал полученный дамп, но вроде никакого криминала не обнаружилось. Нифига себе - никакого  Код 00100000 <_startup>: //*- If an exception occurs before remap, this would result in an infinite loop. //*- To ensure if a exeption occurs before start application to infinite loop. //*------------------------------------------------------------------------------*/
B InitReset /* 0x00 Reset handler */ 100000: ea000010 b 100048 <InitReset> Но этот код должен исполняться не с 100000, а с нуля, хотя расположен должен быть действительно по адресу 100000. Kоманда B, будучи перемещенная в адрес 0, прыгнет совсем не туда, куда вы хотите, ибо использует относительную адресацию. Вы попадете на адрес 0x48, и надо ли описывать, что произойдет после ремапа? В начале этого стартапа крупными буквами написано: Цитата Generic CStartup for KEIL and GCC No Use REMAP Он предназначен для линковки с адреса 0. Второе приложение у вас находится в том же секторе, что и первое. Интересно, как ваш загрузчик будет его стирать? Вместе с собой? Надо сразу распределять память под приложение правильно. Прикладываю свой вариант стартапа, скриптов линкера и "рыбу" загрузчика. Проверка целостности приложения происходит в __low_level_init(). Если приложение можно запускать, оно тут же и запускается (приложение получает практически всю периферию нетронутой - только запущен PLL и настроено направление портов). Если нельзя - после возврата из __low_level_init() заканчиввается инициализация загрузчика и запуск main() собственно загрузчика. Приложение использует этот же стартап-код из crt.s.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Apr 10 2008, 05:47
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Здравствуйте, большое спасибо за ценные замечания. У меня все же путаница в голове о ремаппинге и пр. Вот мое видение - поправьте, если что-то не так. После ресета АРМ переходит на адрес 0х0 и выполняет найденную там инструкцию; обычно по этому адресу располагается таблица векторов. Обычно девайсы грузятся с ROM'a. Согласно даташиту, ПЗУ в SAM7 после сброса чипа (и до ремапа) доступно по адресу 0x0; также в любое время к флешу можно обратиться по адресу 0x00100000 - т.е. флэш "двуликий"  RAM, в свою очередь, располагается по адресу 0x00200000. После ремапа - с адреса 0x0. Как я понял, основная польза от ремапа в том, чтобы код выполнялся из SRAM, что значительно быстрее, и для модификации таблицы векторов. А также при работе с флешом (запись, чтение) - функции, оперирующие с флешем, должны выполняться из RAM. Цитата(Сергей Борщ @ Apr 9 2008, 13:35)  Но этот код должен исполняться не с 100000, а с нуля, хотя расположен должен быть действительно по адресу 100000. Понятно, ведь ARM стартует с 0x0. Но тогда почему все атмеловские примеры для САМ7 привязываются к 0x100000 (это и по objdump видно, и по линк-скриптам).... Цитата Kоманда B, будучи перемещенная в адрес 0, прыгнет совсем не туда, куда вы хотите, ибо использует относительную адресацию. Вы попадете на адрес 0x48, и надо ли описывать, что произойдет после ремапа? В начале этого стартапа крупными буквами написано: Он предназначен для линковки с адреса 0. Вот здесь опять конфуз в голове... Ведь после ремаппинга RAM также доступна с 0x0. Или здесь под адресами линковки подразумевается только internal flash? Цитата Второе приложение у вас находится в том же секторе, что и первое. Интересно, как ваш загрузчик будет его стирать? Вместе с собой? Надо сразу распределять память под приложение правильно.
Прикладываю свой вариант стартапа, скриптов линкера и "рыбу" загрузчика. Проверка целостности приложения происходит в __low_level_init(). Если приложение можно запускать, оно тут же и запускается(приложение получает практически всю периферию нетронутой - только запущен PLL и настроено направление портов). Если нельзя - после возврата из __low_level_init() заканчиввается инициализация загрузчика и запуск main() собственно загрузчика. Приложение использует этот же стартап-код из crt.s. Я посмотрел стартап, не заметил больших отличий от того, что я использую (из атмеловского примера). Только в main.c вы делаете ремап (устанавливаете битик в MC_RCR). И есть разница в линкерном скрипте... Вот это: Код SECTIONS { .vectors : { *(.vectors) KEEP(*(.vectors)) } > REMAPED AT > ROM говорит линкеру грузить секцию .vectors в область REMAPED, определенную ранее. правильно я понимаю?
|
|
|
|
|
Apr 10 2008, 06:36
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(romez777 @ Apr 10 2008, 08:47)  Обычно девайсы грузятся с ROM'a. Согласно даташиту, ПЗУ в SAM7 после сброса чипа (и до ремапа) доступно по адресу 0x0; также в любое время к флешу можно обратиться по адресу 0x00100000 - т.е. флэш "двуликий"  RAM, в свою очередь, располагается по адресу 0x00200000. После ремапа - с адреса 0x0. Как я понял, основная польза от ремапа в том, чтобы код выполнялся из SRAM, что значительно быстрее, и для модификации таблицы векторов. А также при работе с флешом (запись, чтение) - функции, оперирующие с флешем, должны выполняться из RAM. абсолютно правильно. Кроме записи-чтения флеша. Чтение может выполняться откуда угодно, а при записи в ОЗУ должна находиться лишь мааленькая функция: Код void flash_t::command_RAM(uint32_t command) { AT91C_BASE_MC->MC_FCR = command; while(!(AT91C_BASE_MC->MC_FSR & AT91C_MC_FRDY)) ; } А вызываться она может из флеша: Код void flash_t::command(uint32_t command, uint32_t mode) { disable_interrupts(); AT91C_BASE_MC->MC_FMR = mode; command_RAM(command); enable_interrupts(); }
void flash_t::program(uint32_t const *page_addr) { command( ((AT91C_MC_KEY / 0xFFU) * 0x5A) | (((uint32_t)page_addr >> 7) << 8) | AT91C_MC_FCMD_START_PROG, (0 * AT91C_MC_FRDY) | (0 * AT91C_MC_LOCKE) | ( 0 * AT91C_MC_PROGE) | (1 * AT91C_MC_NEBP) | (FLASH_WAITSTATES) | (AT91C_MC_FMCN / 0xFFU * MCK_CYCLES) ); } Цитата(romez777 @ Apr 10 2008, 08:47)  Понятно, ведь ARM стартует с 0x0. Но тогда почему все атмеловские примеры для САМ7 привязываются к 0x100000 (это и по objdump видно, и по линк-скриптам).... Видимо у них первой командой идет не B, а LDR PC,[PC, #24], которая грузит в PC абсолютный адрес, расположенный тут же в сегменте векторов. Т.е. для первой команды все равно где располагаться, а вторая передаст управление на абсолютный адрес во флеше, т.е дальше программа работает в адресах 0x10.... Почему они линкуют вектора на абсолютные адреса флешь - загадка. Может быть, для упрощения скрипта. А может просто исходят из того, что поскольку и в адресах флеша и в нулевых адресах лежат копии, то как бы все равно, откуда реально исполняется код. Ну побегает он по нулевым, потом на первой же команде абсолютного перехода перескочит в адреса флеш. Но если, находясь в нулевых адресах, он сделает ремап - наступит карамба, поскольку следующую команду он неожиданно получит из ОЗУ. Цитата(romez777 @ Apr 10 2008, 08:47)  Вот здесь опять конфуз в голове... Ведь после ремаппинга RAM также доступна с 0x0. Да. А в начало RAM перед ремапом скопирован сегмент векторов приложения. Код в это время исполняется уже с адресов 0x10...., и передав выполнение на адрес 0 мы попадаем на начальную команду приложения. А там стоит LDR PC, [PC, #24], а по адресу 0x20 лежит абсолютный адрес начала стартапа приложения. Он грузится в PC и управление снова попадает на адреса флеш, куда-то после 0x101000. При этом приложение имеет свой сегмент векторов, который совершенно независим от векторов загрузчика. Ближе к обеду еще раз внимательно изучу ваш код, возможно вы пытались построить передачу управления на другом принципе. Цитата(romez777 @ Apr 10 2008, 08:47)  Вот это: говорит линкеру грузить секцию .vectors в область REMAPED, определенную ранее. правильно я понимаю? Не совсем. Оно говорит, что во время исполнения эта секция будет находиться в области REMAPED. Поэтому все ссылки из нее должны быть установлены так, как будто она находится в области REMAPED. Но физически эти байты надо сложить в область ROM. Аналогично для секции .data - все ссылки будут на область RAM, а физически начальные значения будут сложены в область ROM. Как будет происходить перемещение кода (ремап или копирование, как для секции .data), линкер не знает.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Apr 10 2008, 14:46
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Приветствую. Все немного проясняется, из мозаики складывается картинка  Но все же не до конца представляю: для моей задачи (бутлоадер), нужен ли стартап с remap или без оного? Отсюда соответственно и линкер срипт. И еще. Как Вы уже упомянули, мой первоначальный startup-код "должен исполняться не с 100000, а с нуля, хотя расположен должен быть действительно по адресу 100000". Почему тогда в отдельности (т.е. _без_ перехода на второй образ) этот простой код работает? И вот еще по адресам: как я понимаю, до ремапа все адреса, о которых мы говорили, физические (например, физически флеш находится по адресу 0x100000, но отмаплена на 0х00 при старте), а после ремапа - они логические? Цитата Не совсем. Оно говорит, что во время исполнения эта секция будет находиться в области REMAPED. Поэтому все ссылки из нее должны быть установлены так, как будто она находится в области REMAPED. Но физически эти байты надо сложить в область ROM. Аналогично для секции .data - все ссылки будут на область RAM, а физически начальные значения будут сложены в область ROM. Как будет происходить перемещение кода (ремап или копирование, как для секции .data), линкер не знает. Ваш стартап использует remap; вопрос - область REMAPED, описанная в линкер скрипте, определяет именно SRAM область после ремапа - правильно я понимаю? Спасибо !
|
|
|
|
|
Apr 11 2008, 07:56
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 10 2008, 17:46)  Все немного проясняется, из мозаики складывается картинка  Но все же не до конца представляю: для моей задачи (бутлоадер), нужен ли стартап с remap или без оного? Отсюда соответственно и линкер срипт. Зависит от того, как Вы напишете бутлоадер. Например часть кода из стартап линкуется для исполнения во флеш. А все остальное слинковано для выполнения в ОЗУ. Если нужны прерывания - то ремап думаю нужен (если обработчики в ОЗУ то лучше и вектора туда положить). Цитата И еще. Как Вы уже упомянули, мой первоначальный startup-код "должен исполняться не с 100000, а с нуля, хотя расположен должен быть действительно по адресу 100000". Почему тогда в отдельности (т.е. _без_ перехода на второй образ) этот простой код работает? Первые команды (бутлоадер) пользуясь относительной адрессацией выполняют переход по абсолютному адресу. Во второй части программы (собственно программа) то-же самое, но смещения не верны из-за того, что код расположен не с нулевого адреса. И ОЗУ и флеш доступны одноврнменно и по адресу 0 и по их фактическому = абсолютному адресу и смщения как от нуля так и от, скажем 100000 для флеш одинаковы как от 0 так и от 100000 для бутлоадера, а вот для основной программы - нет. Цитата И вот еще по адресам: как я понимаю, до ремапа все адреса, о которых мы говорили, физические (например, физически флеш находится по адресу 0x100000, но отмаплена на 0х00 при старте), а после ремапа - они логические? Нет. В этом процессоре все адреса физические. Просто флеш (или ОЗУ) доступна одновременно по двум адресам.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 12 2008, 01:28
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(Сергей Борщ @ Apr 9 2008, 13:35)  Прикладываю свой вариант стартапа, скриптов линкера и "рыбу" загрузчика. Проверка целостности приложения происходит в __low_level_init(). Если приложение можно запускать, оно тут же и запускается (приложение получает практически всю периферию нетронутой - только запущен PLL и настроено направление портов). Если нельзя - после возврата из __low_level_init() заканчиввается инициализация загрузчика и запуск main() собственно загрузчика. Приложение использует этот же стартап-код из crt.s. Сергей, в вашем варианте стартапа вы не инициализируете стеки для каждого из режимов (interrupt mode, supervisor mode). Почему? И еще. В этом примере http://www.atmel.com/dyn/resources/prod_do...started_1.0.zip в линкер-скрипте они вообще не описывают секции памяти (директива MEMORY), секция .vectors (содержит таблицу векторов) помещается в область .data; сам стартап делает remap Код at91sam7s_getting_started_flash.elf: file format elf32-littlearm
Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000cac 00100000 00100000 000000a0 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 00000080 00200000 00100cac 00000d4c 2**2 CONTENTS, ALLOC, LOAD, DATA 2 .bss 0000000c 00200080 00200080 00000dcc 2**2 ALLOC 3 .debug_line 000001ef 00000000 00000000 00000dcc 2**0 CONTENTS, READONLY, DEBUGGING 4 .debug_info 0000149a 00000000 00000000 00000fbb 2**0 CONTENTS, READONLY, DEBUGGING 5 .debug_abbrev 00000286 00000000 00000000 00002455 2**0 CONTENTS, READONLY, DEBUGGING 6 .debug_aranges 00000068 00000000 00000000 000026e0 2**3 CONTENTS, READONLY, DEBUGGING 7 .debug_frame 00000260 00000000 00000000 00002748 2**2 CONTENTS, READONLY, DEBUGGING 8 .debug_loc 000002f4 00000000 00000000 000029a8 2**0 CONTENTS, READONLY, DEBUGGING 9 .debug_pubnames 00000144 00000000 00000000 00002c9c 2**0 CONTENTS, READONLY, DEBUGGING 10 .debug_str 00000046 00000000 00000000 00002de0 2**0 CONTENTS, READONLY, DEBUGGING 11 .comment 00000024 00000000 00000000 00002e26 2**0 CONTENTS, READONLY Видно, что область .data линкуется в 0x100000 адреса. Это потому, что remapping происходит? Эх... все больше загадок
|
|
|
|
|
Apr 12 2008, 04:11
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Здравствуйте, Цитата(amw @ Apr 11 2008, 10:56)  Зависит от того, как Вы напишете бутлоадер. Например часть кода из стартап линкуется для исполнения во флеш. А все остальное слинковано для выполнения в ОЗУ. Если нужны прерывания - то ремап думаю нужен (если обработчики в ОЗУ то лучше и вектора туда положить). Я делаю бутлоадер для конкретного применения, т.е. он не универсальный, а будет обновлять в железке конкретное ПО, которое активно использует прерывания (fiq и irq), работает с TWI, spi и пр. То есть remap таки нужен? Цитата Нет. В этом процессоре все адреса физические. Просто флеш (или ОЗУ) доступна одновременно по двум адресам. Еще такой фундаментальный вопрос... Чего я до конца не понимаю - код ведь всегда будет выполняться из RAM, или это определяется линковкой? То есть ежели линкуем с FLASH адресами (и нет ремаппинга), то код будет выполняться из флеш. В потивном случае - если слинковали с SRAM и включен ремаппинг, код будет выполняться из RAM. Правильно ли я понимаю? Это справедливо для всех ARM-ов или только sam7? Спасибо.
|
|
|
|
|
Apr 12 2008, 07:30
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 12 2008, 07:11)  Здравствуйте, Я делаю бутлоадер для конкретного применения, т.е. он не универсальный, а будет обновлять в железке конкретное ПО, которое активно использует прерывания (fiq и irq), работает с TWI, spi и пр. То есть remap таки нужен? Если Вам в основной программе нужны вектора, то располагайте вектора в ОЗУ (линкуйте вектора для ОЗУ), копируйте вектора в ОЗУ и делайте потом REMAP. Цитата Еще такой фундаментальный вопрос... Чего я до конца не понимаю - код ведь всегда будет выполняться из RAM, или это определяется линковкой? То есть ежели линкуем с FLASH адресами (и нет ремаппинга), то код будет выполняться из флеш. В потивном случае - если слинковали с SRAM и включен ремаппинг, код будет выполняться из RAM.
Правильно ли я понимаю? Это справедливо для всех ARM-ов или только sam7? Ой ни ни!!!! Не правильно! Линковка определяет абсолютные адреса по кторым будут располягаться функции и данные фактически. В процессоре имеется: 1. Вектора, и они всегда по нулевому адресу. 2. Флеш, она хранит и программу и данные. Находится по адресу X. Данные в ней не модифицируются и при отключении питания не теряются. 3. ОЗУ, она хранит и программу и данные. Гаходится по адресу Y. Данные в нет теряются при выключении питания. Но быстрее флеш. Если Вам нужно иметь быстрый код - линкуйте его в ОЗУ. Но! При выключении питания этот код исчезает. Так как он попадет в ОЗУ при следующем включении? А очень просто! Он хранится во флеш и при каждом включении питания программа, запускаемая из флеш копирует нужный кусок в ОЗУ. Как это обеспечить? При линковке, все команды перехода используют абсолютный адрес ОЗУ для вычисления адреса конкретной команды перехода. Линкер складывает в HEX файл все подряд. Сначала то, что слинковано для запуска из флеш по адресам во флеш, где этот код должен находится во время выполнения программы. За ним (за этим кодом) фо флеш лежит копия того, что должно во время выполнения быть в ОЗУ. А стартап код выполняет копирование этой копии в ОЗУ по нужным адресам. Вектора находятся по адресу ноль. И как не линкуй, а вектора должны быть по адресу ноль и только ноль. Так что же делать? Да просто сделать так, что бы флеш или ОЗУ было видно по адресу ноль. Как это сделать? Ну в процесоре ставят "хитрый" адресный дешифратор, который в зависимости от бита REMAP будет показывать по нулевому адресу либо флеш либо ОЗУ. Тут главное обеспечить из вектора команду перехода так, что бы он "послал" процессор по правильному абсолютному адресу! Не зависимо от REMAP и флеш и ОЗУ остаются доступны по своим адесам. То есть по адресу X всегда видна флеш а адресу Y всегда видно ОЗУ. А адресу ноль видно "то же самое" что и во флеш/ОЗУ. Что именно видно по адресу ноль - зависит текущего значения REMAP. Но прогамма слинкованная для ОЗУ, расположенному по адресу Y не может выполнятся по адресам, видимым начиная с нуля! Тогда в вектора ставятся адреса перехода, указывающие адреса ОЗУ. В разных АРМ по разному. В SAM7 все ОЗУ или вся флеш мапится на нолевой адрес, в LPC - только первые 64 байта (то есть сами вектора и место для указания абсолютных адресов перехода). Представте, что Вы сидите в комнате. Слева у Вас окно, и Вы в нем видите море. Справа у Вас окно, и Вы в нем видите горы. А прямо перед Вами супер пупер фул ХД телевизор. К телевизору подключены две камеры. Однв показывает то, что видно в левом окне, а вторая - то что в правом. Море - это флеш. Горы - это ОЗУ. Телевизор - это то что видно по адресу ноль. Щелкая ДУ телевизора Вы можете видеть либо море, либо горы. То есть либо флеш, либо ОЗУ переключая бит REMAP. НО!!! В телевизоре Вы можете только видеть. Телевизор не становится ни морем ни горами. Так вот Вы - процессор АРМ  . Вы можете посмотреть в телевизор и узнать, в какое окно Вам прыгать  то есть по какому адресу должен быть переход.
Сообщение отредактировал amw - Apr 12 2008, 07:46
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 14 2008, 07:28
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Большое спасибо за подробные разъяснения! Цитата(amw @ Apr 12 2008, 10:30)  Если Вам в основной программе нужны вектора, то располагайте вектора в ОЗУ (линкуйте вектора для ОЗУ), копируйте вектора в ОЗУ и делайте потом REMAP. Таким образом, в _основной_ программе мне нужны вектора (обработчики fiq и irq): 1) в основной программе (загружаемой бутлоадером) копируем таблицу векторов в RAM, далее устанавливаем бит remap, тем самым отмапливаем ОЗУ по адресам 0x200000 И 0x0 2) бутлоадер - ремап не обязателен, ведь нам нужно только загрузить приложение куда следует, а оно уже само разберется. Как я понял, ремаппинг может понадобиться только для работы с флэшем, чтобы избежать compiler-specific фич (например, __ramfunc в IAR'e). Такая концепция верная? Цитата ... Ну в процесоре ставят "хитрый" адресный дешифратор, который в зависимости от бита REMAP будет показывать по нулевому адресу либо флеш либо ОЗУ. Тут главное обеспечить из вектора команду перехода так, что бы он "послал" процессор по правильному абсолютному адресу! Здесь речь идет о командах типа B, BL, BX или что-то другое? Есть какие-то специализированные инструкции послать процессор по абсолютному адресу?
|
|
|
|
|
Apr 14 2008, 13:22
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 14 2008, 10:28)  Большое спасибо за подробные разъяснения! Таким образом, в _основной_ программе мне нужны вектора (обработчики fiq и irq): 1) в основной программе (загружаемой бутлоадером) копируем таблицу векторов в RAM, далее устанавливаем бит remap, тем самым отмапливаем ОЗУ по адресам 0x200000 И 0x0 2) бутлоадер - ремап не обязателен, ведь нам нужно только загрузить приложение куда следует, а оно уже само разберется. Как я понял, ремаппинг может понадобиться только для работы с флэшем, чтобы избежать compiler-specific фич (например, __ramfunc в IAR'e). Примерно так. Учтите, что в ОЗУ копировать нужно вектора ПРИЛОЖЕНИЯ ане бутлоадера. При этом векора приложения сами должны быть слинкованы по адресу ноль, а остальное приложение по его родному адресу. Цитата Такая концепция верная? Здесь речь идет о командах типа B, BL, BX или что-то другое? Есть какие-то специализированные инструкции послать процессор по абсолютному адресу? Команды B и BL используют относительную адресацию. В них указывается смещение (т.е. разница) до нужного адреса от текущего значения PC. Это смещение должно после копирования векторов в ОЗУ соответствовать расположению программы (обработчика). Команда BX использует абсолютную адресацию. То есть для команды BX абсолютный адрес перехода находится в регистре. Как он туда попал - вот это вопрос. А вот дальше не уверен что все правильно распишу, поправте кто-нибудь, если я лажанулся. Команда LDR PC, AAA использует значение AAA как смещение от текущего PC до адреса, где лежит абсолютный адрес перехода. Брррр. Пример Код EXCEPTION_VECTORS: ldr pc, RESET_ADDR ldr pc, UNDEF_ADDR ldr pc, SWI_ADDR ldr pc, PREFETCH_ABORT_ADDR ldr pc, DATA_ABORT_ADDR .word 0 ldr pc, IRQ_ADDR ldr pc, FIQ_ADDR RESET_ADDR: .word _start UNDEF_ADDR: .word UNDEF_handler SWI_ADDR: .word SWI_handler PREFETCH_ABORT_ADDR: .word PREFETCH_ABORT_handler DATA_ABORT_ADDR: .word DATA_ABORT_handler IRQ_ADDR: .word IRQ_handler FIQ_ADDR: .word FIQ_handler UNDEF_handler: Код исключения SWI_handler: Код исключения PREFETCH_ABORT_handler: Код исключения DATA_ABORT_handler: Код исключения IRQ_handler: Код исключения FIQ_handler: Код исключения .................. _start: .................. В данном примере команда ldr pc, IRQ_ADDR Загрузит в PC число, лежащее в памяти по адресу IRQ_ADDR. По адресу IRQ_ADDR лежит число = адресу собственно обработчика прерывания IRQ_handler.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 15 2008, 13:31
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(amw @ Apr 14 2008, 16:22)  Примерно так. Учтите, что в ОЗУ копировать нужно вектора ПРИЛОЖЕНИЯ ане бутлоадера. При этом векора приложения сами должны быть слинкованы по адресу ноль, а остальное приложение по его родному адресу. Приветствую. Есть вопрос по линкер-скрипту приложения. Взял скрипт из одного из атмеловсеих примеров под gcc и немного переделал. Вот что получилось: Код OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_startup)
/* Memory layout */ MEMORY { ROM (rx) : ORIGIN = 0x00101000, LENGTH = 0x00001000 /* 4K is enough for bootloader */ RAM (rw) : ORIGIN = 0x00200000, LENGTH = 0x00004000 /* any SAM7 has at least 16K RAM */ STACK (rw) : ORIGIN = 0x00204000,LENGTH = 0x00000000 REMAPPED (rwx) : ORIGIN = 0x00000000, LENGTH = LENGTH(RAM) }
/* Section Definitions */ SECTIONS { /* place vectors at 0x0 */ .vectors : { _vectors_start = .; *(.vectors) KEEP(*(.vectors)) _vectors_end = .; } >REMAPPED AT >ROM
/* first section is .text which is used for code */ /*. = 0x0000000;*/ .text : { *Cstartup.o (.text) } >ROM =0 .text : { *(.text) /* code */ *(.rodata) /* read-only data (constants) */ *(.rodata*)
*(.glue_7t) *(.glue_7)
} >ROM =0
. = ALIGN(4);
_etext = .; PROVIDE (etext = .);
/* .data section which is used for initialized data */ .data : { _data = .; *(.ramfunc) /* copy ramfunc code to RAM in same cycle with data init values */ *(.data*) } >RAM AT >ROM /* place init values immediatly after .text section */ . = ALIGN(4);
_edata = .; PROVIDE (edata = .);
/* .bss section which is used for uninitialized data */ .bss : { __bss_start = .; __bss_start__ = .; *(.bss) *(COMMON) } >RAM . = ALIGN(4); __bss_end__ = .; __bss_end__ = .; _end = .; /*PROVIDE (end = .);*/
.int_data : { *(.internal_ram_top) }> STACK
/* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } /* DWARF debug sections. Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) }
} После компиляции смотрю map-файл (он генерируется гнушным линкером и содержит карту памяти) и вижу, что секция vectors занимает 0 байт  Что неправильно в этом скрипте? Спасибо.
|
|
|
|
|
Apr 16 2008, 00:19
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Здравствуйте. Цитата(Сергей Борщ @ Apr 15 2008, 16:50)  А в файле с исходным текстом указано, что вектора надо класть в секцию vectors? В оригинальном стартапе вектора не были оформлены в отдельную секцию директивой .section, поэтому я сделал таким образом: Код ... .section .vectors .global _startup .func _startup
_startup: reset: B InitReset /* 0x00 Reset handler */ undefvec: B undefvec /* 0x04 Undefined Instruction */ swivec: B swivec /* 0x08 Software Interrupt */ pabtvec: B pabtvec /* 0x0C Prefetch Abort */ dabtvec: B dabtvec /* 0x10 Data Abort */ rsvdvec: B rsvdvec /* 0x14 reserved */ irqvec: B IRQ_Handler_Entry /* 0x18 IRQ */ fiqvec: B fiqvec /* 0x1c FIQ */
FIQ_Handler_Entry: /* здесь обработчик FIQ */
InitReset: .extern AT91F_LowLevelInit /*- minumum C initialization */ /*- call AT91F_LowLevelInit( void) */ ... Как я догадываюсь (в документации на GNU assembler не нашел явной информации), сеция определенная с помощью .section простирается до следующей директивы .section. Но вот откуда начинается следующая за .vectors секция, т.е. .text?  С вектора InitReset? А вот так копируются вектора в память с последующим ремапом: Код /* Copy exception vectors in RAM */ LDR r0, =_vectors_start LDR r1, =0x00200000 LDR r2, =_vectors_start copy: LDR r3, [r0], #4 STR r3, [r1], #4 CMP r0, r2 BNE copy
/* Remap SRAM to have new exception vectors at address 0 */ LDR r0, =0xFFFFFF00 LDR r1, =(0x1 << 0) STR r1, [r0, #0]
ldr lr,=exit ldr r0,=main bx r0 Цитата Сделайте дизассемблерный файл и посмотрите, куда они попали на самом деле. Код main.elf: file format elf32-littlearm
Disassembly of section .text:
00101000 <_startup>: //*- If an exception occurs before remap, this would result in an infinite loop. //*- To ensure if a exeption occurs before start application to infinite loop. //*------------------------------------------------------------------------------*/
B InitReset /* 0x00 Reset handler */ 101000: ea000011 b 4c <InitReset>
00101004 <undefvec>: ... Должно же быть "b 0x0 ..."? Гм...
|
|
|
|
|
Apr 17 2008, 07:51
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Немного покрутил, но все равно не работает  Сейчас карта памяти вроде выглядит правильно: Код Memory Configuration
Name Origin Length Attributes ROM 0x00101000 0x00001000 xr RAM 0x00200000 0x00004000 rw STACK 0x00204000 0x00000000 rw REMAPPED 0x00000000 0x00004000 xrw *default* 0x00000000 0xffffffff ... .vectors 0x00000000 0x20 load address 0x00101000 0x00000000 _vectors_start = . *(.vectors) .vectors 0x00000000 0x20 Cstartup.o 0x00000000 _startup *(.vectors) 0x00000020 _vectors_end = .
.text 0x00101020 0x488 *(.text) ... То есть вектора расположились правильно и размер у них правильный - 32байта. А вот objdump: Код main.elf: file format elf32-littlearm
Disassembly of section .vectors:
00000000 <_startup>: //*- If an exception occurs before remap, this would result in an infinite loop. //*- To ensure if a exeption occurs before start application to infinite loop. //*------------------------------------------------------------------------------*/
B InitReset /* 0x00 Reset handler */ 0: ea040411 b 10104c <InitReset> ... Может быть это особенность компилятора (или скорее ассемблера)?
|
|
|
|
|
Apr 17 2008, 08:07
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Да есть такая проблема. Сделайте так Код Тут убрать секцию с векторами .text : { /* ARM Exception vectors */ . = _vectors; KEEP (*(.vectors*)) . = _evectors; KEEP (*Cstartup.o (.text) ) /* Startup code */ *(.text*) /* remaining code */ *(.glue_7t) *(.glue_7) } >Что тут у Вас должно быть Дальше продолжение вашего скрипта. Дело в том, что 1. Ваш скрипт описывает действия с секцией текст несколько раз 2. Секции располагаются в памяти в алфавитном порядке если не указанно иное. Поскольку vectors идет после text то тут возникают вопросы.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 18 2008, 02:17
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Приветствую. Цитата(amw @ Apr 17 2008, 11:07)  Да есть такая проблема. Сделайте так Код Тут убрать секцию с векторами .text : { /* ARM Exception vectors */ . = _vectors; KEEP (*(.vectors*)) . = _evectors; KEEP (*Cstartup.o (.text) ) /* Startup code */ *(.text*) /* remaining code */ *(.glue_7t) *(.glue_7) } >Что тут у Вас должно быть Дальше продолжение вашего скрипта. Наверное вы имели в виду: Код SECTIONS { .text : { /* ARM exception vectors */ _vectors_start = .; KEEP(*(.vectors)) _vectors_end = .; KEEP(*Cstartup.o (.text)) /* Startup code */ *(.text) /* code */ *(.rodata) /* read-only data (constants) */ *(.rodata*)
*(.glue_7t) *(.glue_7) } >ROM =0 } Но это все равно не помогает. Map-файл выглядит так: Код Memory Configuration
Name Origin Length Attributes ROM 0x00101000 0x00001000 xr RAM 0x00200000 0x00004000 rw STACK 0x00204000 0x00000000 rw REMAPPED 0x00000000 0x00004000 xrw *default* 0x00000000 0xffffffff ...
.text 0x00101000 0x4f0 0x00101000 _vectors_start = . *(.vectors) .vectors 0x00101000 0x20 Cstartup.o 0x00101000 _startup 0x00101020 _vectors_end = . *Cstartup.o(.text) .text 0x00101020 0x14c Cstartup.o ... Вектора он таки поместил в начало и размер 0x20, то есть 32 байта. Но objdump тем не менее видит эти вектора по неверному адресу: Код main.elf: file format elf32-littlearm
Disassembly of section .text:
00101000 <_startup>: //*- If an exception occurs before remap, this would result in an infinite loop. //*- To ensure if a exeption occurs before start application to infinite loop. //*------------------------------------------------------------------------------*/
B InitReset /* 0x00 Reset handler */ 101000: ea000011 b 10104c <InitReset> ... Цитата Дело в том, что 1. Ваш скрипт описывает действия с секцией текст несколько раз В линкер-скрипте секция .text только один раз.
|
|
|
|
|
Apr 18 2008, 08:28
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 18 2008, 05:17)  Приветствую. Наверное вы имели в виду: Давайте согласуем действия. Возмем за основу приложенный проект. Линкер скрипт взят из поста #24. Ваши коментарии? Результаты компиляции? Код $ arm-elf-as -v GNU assembler version 2.18 (arm-elf) using BFD version (GNU Binutils) 2.18 $ arm-elf-ld -v GNU ld (GNU Binutils) 2.18 $ arm-elf-gcc -v Using built-in specs. Target: arm-elf Configured with: /home/amw/devel/ARM/arm7tdmi/tools/src/gcc-4.2.2/configure --target=arm-elf --prefix=/usr/local/arm/arm-elf --enable-interwork --enable-multilib --with-newlib --with-headers=/usr/local/arm/arm-elf/arm-elf/include --with-gnu-ld --with-gnu-as --disable-nls --disable-shared --enable-languages=c Thread model: single gcc version 4.2.2 Цитата В линкер-скрипте секция .text только один раз. Пост #24. ?????
Прикрепленные файлы
jump.zip ( 6.68 килобайт )
Кол-во скачиваний: 78
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 19 2008, 04:02
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Приветствую. Спасибо за ваше терпение в ответах на мои глупые вопросы  Цитата(amw @ Apr 18 2008, 11:28)  Давайте согласуем действия. Возмем за основу приложенный проект. Линкер скрипт взят из поста #24. Ваши коментарии? Результаты компиляции? Хорошо. Итак, берем линкер скрипт из поста N24 (скрипт из приложенного вами проекта отличается совмем немного, но об этом позже), приложенный проект и успешно компилирую. НО - binary образ, полученный в результате arm-elf-objcopy -O binary tst.elf tst.bin получается огромным (1,052,808байт!) и соответственно не прошивается. При этом бинарник из архива jump.zip крохотный, благополучно прошивается и похоже на то, что работает. Мой тулчейн взят с www.yagarto.de, собран под Win32: Код D:\Documents and Settings\sam>arm-elf-gcc --version arm-elf-gcc (GCC) 4.2.1 Copyright (C) 2007 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
D:\Documents and Settings\sam>arm-elf-ld --version GNU ld version 2.17 Copyright 2005 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License. This program has absolutely no warranty.
D:\Documents and Settings\sam>arm-elf-as --version GNU assembler 2.17 Copyright 2005 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License. This program has absolutely no warranty. This assembler was configured for a target of `arm-elf'. Вопросы: 1) почему в вашем стартапе вектора определены через "ldr pc, ..." и далее просто word-ами размещены точки входа в обработчики? Чем это лучше/правильнее/оптимальнее, почему не сразу, например: Код B InitReset undefvec: B undefvec swivec: B swivec ... 2) в стартапе вы не делаете ремап и не инициализируете стеки (это наверное связано с тем, что приложение простое и дано как пример?) 3) непонятет вот этот кусок: Код _startup: ldr r0, =0x12345678 mov r1, #0x14 str r0, [r1] bl main b _startup Обычно я видел, что в r0 кладется адрес ф-ции main, а потом bx r0 4) в линкер файле вы определяете размер секции векторов в 64 байта. Это потому, что там сначала идут ldr pc ... (7 штук и ноль), а потом 7 слов ? 5) в линкер скрипте секция .vectors вынесена отдельно и .text описывается дважды. Ранее вы советовали vectors вынести внутрь .text. Какой же способ правильный?
|
|
|
|
|
Apr 19 2008, 10:05
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 19 2008, 07:02)  Приветствую. Спасибо за ваше терпение в ответах на мои глупые вопросы  Хорошо. Итак, берем линкер скрипт из поста N24 (скрипт из приложенного вами проекта отличается совмем немного, но об этом позже), приложенный проект и успешно компилирую. НО - binary образ, полученный в результате arm-elf-objcopy -O binary tst.elf tst.bin получается огромным (1,052,808байт!) и соответственно не прошивается. При этом бинарник из архива jump.zip крохотный, благополучно прошивается и похоже на то, что работает. Мой тулчейн взят с www.yagarto.de, собран под Win32: Мой тулчейн собран мной и работаю я на Debian/Lenny amd64. Давайте отлчия в скрипте линкера. Скорее всего проблема с LOAD ADDRESS (то что в секции после } > !!!!!!!). Цитата Код D:\Documents and Settings\sam>arm-elf-gcc --version arm-elf-gcc (GCC) 4.2.1 Copyright (C) 2007 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. D:\Documents and Settings\sam>arm-elf-ld --version GNU ld version 2.17 Copyright 2005 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License. This program has absolutely no warranty.
D:\Documents and Settings\sam>arm-elf-as --version GNU assembler 2.17 Copyright 2005 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License. This program has absolutely no warranty. This assembler was configured for a target of `arm-elf'. Почти одинаково. Думаю это не имеет значения. Цитата Вопросы: 1) почему в вашем стартапе вектора определены через "ldr pc, ..." и далее просто word-ами размещены точки входа в обработчики? Чем это лучше/правильнее/оптимальнее, почему не сразу, например: Код B InitReset undefvec: B undefvec swivec: B swivec ... Потому что команда B использует относительную адресацию. Если Вы переместите этот кусок кода в другое место (а Вы его копируете в нулевой адрес), то разница между текущим PC и адресом перехода изменится, а команда B ничего про это не знает. Объявленные адреса обработчиков через .word содержат АБСОЛЮТНЫЕ адреса обработчиков. Команда LDR то-же использует относительную адресацию, но Вы же копируете и собственно LDR .... и абсолютные адреса (всего 64 байта). Таким образом в команде LDR после копирования будет правильное смещение до слова в памяти, по которому лежит абсолютный адрес обработчика. Цитата 2) в стартапе вы не делаете ремап и не инициализируете стеки (это наверное связано с тем, что приложение простое и дано как пример?) 3) непонятет вот этот кусок: Код _startup: ldr r0, =0x12345678 mov r1, #0x14 str r0, [r1] bl main b _startup Обычно я видел, что в r0 кладется адрес ф-ции main, а потом bx r0 Ну этот кусок кода просто для того, что бы что-то было определенное, а не просто заглушка в виде xxx: B xxx Этот кусок кода можна, например, пройти отладчиком и смотреть на адреса и тп. Цитата 4) в линкер файле вы определяете размер секции векторов в 64 байта. Это потому, что там сначала идут ldr pc ... (7 штук и ноль), а потом 7 слов ? Всего векторов 8 (один резервный) плюс 8 абсолютных адресов обработчиков. Итого 16 слов по 4 байта. Всего 64 байта. В LPC, например ремапу подлежит только 64 первых байта флеш или ОЗУ. Ну и для ровного счета. Это называется "common practics". Цитата 5) в линкер скрипте секция .vectors вынесена отдельно и .text описывается дважды. Ранее вы советовали vectors вынести внутрь .text. Какой же способ правильный? Правильно и так и эдак. Все зависит от нужной в каждом конкретном случае линковки. Внесение .vectors внутрь .text облегчает написание скрипта, но менее гибко.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 21 2008, 02:08
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(amw @ Apr 19 2008, 13:05)  Мой тулчейн собран мной и работаю я на Debian/Lenny amd64. Давайте отлчия в скрипте линкера. Скорее всего проблема с LOAD ADDRESS (то что в секции после } > !!!!!!!). Отличий нет, я пытаюсь скомпилировать ваш код из архива jump.zip из прежнего поста, используя ваш линкер-скрипт, на своей системе. Изменений не вносил, просто набираю make. Все компилируется и линкуется, но бинарный образ исключительно огромный (!), 1052808байт.
|
|
|
|
|
Apr 21 2008, 08:07
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 21 2008, 05:08)  Отличий нет, я пытаюсь скомпилировать ваш код из архива jump.zip из прежнего поста, используя ваш линкер-скрипт, на своей системе. Изменений не вносил, просто набираю make. Все компилируется и линкуется, но бинарный образ исключительно огромный (!), 1052808байт. Ну что-ж, тогда Вам придется переписать скрипт по приложенному примеру. Ключевое место - определение символов, заканчивающихся на _lma__ Возможно я ошибся и binutils-2.17 ведут себя неправильно. Если есть возможность проверте jump.zip с binutils-2.18.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 22 2008, 06:06
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата Потому что команда B использует относительную адресацию. Если Вы переместите этот кусок кода в другое место (а Вы его копируете в нулевой адрес), то разница между текущим PC и адресом перехода изменится, а команда B ничего про это не знает. Наверное это напрямую связано с тем, что мой код не работает должным образом. То есть код бутлоадера стартует, делает переход на второе приложение - и вот тут-то косяки, приложение не отвечает. Учитывая все это, я переделал стартап приложения, линкер скрипт сделан с вашей помошью. Я прикрепил архив с стартапом/линк-скриптом бутлоадера, стартапом/линк-скриптом приложения и картой памяти. Посмотрите plz что еще не так, возможно с инициализацией периферии или стеками... не знаю, что еще проверить  Судя по всему, сейчас все линкуется правильно, грузится по нужным адресам. Вот фрагмент objdump'a приложения: Код main.elf: file format elf32-littlearm
Disassembly of section .vectors:
00000000 <_vectors_start>: //*------------------------------------------------------------------------------*/ .section .vectors .code 32 EXCEPTION_VECTORS: ldr pc, resetvec 0: e59ff018 ldr pc, [pc, #24]; 20 <resetvec> ldr pc, undefvec 4: e59ff018 ldr pc, [pc, #24]; 24 <undefvec> ldr pc, swivec 8: e59ff018 ldr pc, [pc, #24]; 28 <swivec> ldr pc, pabtvec c: e59ff018 ldr pc, [pc, #24]; 2c <pabtvec> ldr pc, dabtvec 10: e59ff018 ldr pc, [pc, #24]; 30 <dabtvec> 14: 00000000 andeq r0, r0, r0 .word 0 ldr pc, irqvec 18: e59ff014 ldr pc, [pc, #20]; 34 <irqvec> ldr pc, fiqvec 1c: e59ff014 ldr pc, [pc, #20]; 38 <fiqvec>
00000020 <resetvec>: ... Цитата Ну что-ж, тогда Вам придется переписать скрипт по приложенному примеру. Ключевое место - определение символов, заканчивающихся на _lma__ Возможно я ошибся и binutils-2.17 ведут себя неправильно. Если есть возможность проверте jump.zip с binutils-2.18. Обновил тулчейн до binutils-2.18, ваш пример собирается, скрипт не понадобилось править. Спасибо, что надоумили обновиться
Прикрепленные файлы
boot.zip ( 9.27 килобайт )
Кол-во скачиваний: 40
|
|
|
|
|
Apr 22 2008, 08:49
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 22 2008, 09:06)  Файл app.S Код /* Copy exception vectors in RAM */ LDR r0, =_vectors_start LDR r1, =0x00200000 LDR r2, =_vectors_start copy: LDR r3, [r0], #4 STR r3, [r1], #4 CMP r0, r2 BNE copy Попробуйте объяснить русским языком что делает этот код  . С точным указанием адресов  . Вот как это понимаю я. 1. r0 = _vectors_start 2. r1 = 0x00200000 3. r2 = _vectors_start 4. r3 = тому, что лежит по адресу _vectors_start, r0 = r0 + 4 5. По адресу 0x00200000 положить значение из r3, r1 = r1 + 4 6. Сравнить r0 и r2. Результат сравнения: r0 = _vectors_start + 4, r2 = _vectors_start 7. Если r0 не равен r2 повторить все начиная с пункта 4. Вывод: Это будет продолжаться до тех пор пока не будет скопировано 4 гигабайта памяти  . r2 нужно загружать не _vectors_start, а _vectors_end А лучше сделать так как это у Вас для копирования секции .data. То есть сначала проверить, а потом уже копировать и прибавлять 4. Рекомендую так-же: 1. Привести в порядок инициализацию стеков. Там у Вас полный бардак. Хотя в простейшем примере это работает, но это не критерий, правда  . 2. Используйте один стиль коментариев. А то мешанина из символов коментариев #, ;, //, /* приводит к неожиданным результатам, по крайней мере в синтаксической раскраске редактором, но часто может сыграть злую шутку и с Вами при компиляции. И придерживайтесь стиля коментариев, рекомендованного для ассемблера данного процессора. Для ARM это символ @. Например где заканчивается коментарий примера ниже по Вашему мнению? А по мнению компилятора? А как расценивать символ # в начале строки? Как коментарий или как директиву препроцессора? Код #//* Comment ldr pc, initreset /* Jump to initialization */ И еще. Чесно говоря MAP файл сам по себе малоинформативен. objdump -SD часто дает гораздо больше полезного (а совместно с MAP файлом вообже супер  ) чем сам по себе MAP файл. И не забывайте, что BIN или HEX тоже можно читать с помощью человека  .
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 23 2008, 00:27
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата Код /* Copy exception vectors in RAM */ LDR r0, =_vectors_start LDR r1, =0x00200000 LDR r2, =_vectors_start copy: LDR r3, [r0], #4 STR r3, [r1], #4 CMP r0, r2 BNE copy Попробуйте объяснить русским языком что делает этот код  . С точным указанием адресов  . Признаю, лоханулся  Цитата А лучше сделать так как это у Вас для копирования секции .data. То есть сначала проверить, а потом уже копировать и прибавлять 4. Пока оставил вышеотквоченный вариант. Хотя бы добиться чтобы работало, но пока изменений не заметил. Цитата Рекомендую так-же: 1. Привести в порядок инициализацию стеков. Там у Вас полный бардак. Хотя в простейшем примере это работает, но это не критерий, правда  . А в чем именно бардак? Я взял инициализацию стеков из одного из атмеловских примеров (оттуда и мешанина стилей комментариев, наверное индусики портировали тот код  ). Таким же образом инициализация делается в аппноте http://www.atmel.com/dyn/resources/prod_do...ation%201.0.zip(правда там для SAM7X/XC) Или вы намекаете на то, что правильнее инициализировать стеки только для тех режимов, в которых собираюсь работать, т.е. например IRQ mode и system mode, а остальные не трогать? Цитата И еще. Чесно говоря MAP файл сам по себе малоинформативен. objdump -SD часто дает гораздо больше полезного (а совместно с MAP файлом вообже супер  ) чем сам по себе MAP файл. И не забывайте, что BIN или HEX тоже можно читать с помощью человека  . А вот еще такой интересный момент, связанный с отладкой. Загружаю свой бутлоадер в gdb, ставлю break на строку, где передача управления приложению: Код int main(void) { ... ((funct)0x00101000)(); /* здесь ставим break point */ } Вплоть до этого брейкпоинта иду по шагам, смотрю регистры, все ОК. А как теперь прыгнуть на код по адресу 0x00101000? Просто next не работает: Код Breakpoint 1, main () at main.c:102 102 ((funct)0x00101000)(); Current language: auto; currently c (gdb) n Cannot access memory at address 0x8008126 0x00100300 in ?? () (gdb) Интересно проверить, как стартап приложения себя действительно ведет.
|
|
|
|
|
Apr 23 2008, 08:16
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 23 2008, 03:27)  Признаю, лоханулся  Пока оставил вышеотквоченный вариант. Хотя бы добиться чтобы работало, но пока изменений не заметил. Ну Вы хоть исправили значение для конца цикла? Цитата А в чем именно бардак? Я взял инициализацию стеков из одного из атмеловских примеров (оттуда и мешанина стилей комментариев, наверное индусики портировали тот код  ). Таким же образом инициализация делается в аппноте http://www.atmel.com/dyn/resources/prod_do...ation%201.0.zip(правда там для SAM7X/XC) Или вы намекаете на то, что правильнее инициализировать стеки только для тех режимов, в которых собираюсь работать, т.е. например IRQ mode и system mode, а остальные не трогать? Ну вот пример из boot.S с моими коментариями Код mov r0,r13 - Понятно. r13пока содержит RAM_TOP /*- Set up Fast Interrupt Mode and set FIQ Mode Stack*/ msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT /*- Init the FIQ register*/ ldr r8, =AT91C_BASE_AIC - А в чем смысл? Оно действительно используется?
/*- Set up Interrupt Mode and set IRQ Mode Stack*/ msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT mov r13, r0 /* Init stack IRQ */ Стек для FIQ и IRQ один и тот -же? А Вы уверены, что разрулите это? sub r0, r0, #IRQ_Stack_Size /*- Set up Supervisor Mode and set Supervisor Mode Stack*/ msr CPSR_c, #ARM_MODE_SVC mov r13, r0 /* Init stack Sup */ Цитата А вот еще такой интересный момент, связанный с отладкой. Загружаю свой бутлоадер в gdb, ставлю break на строку, где передача управления приложению: Код int main(void) { ... ((funct)0x00101000)(); /* здесь ставим break point */ } Вплоть до этого брейкпоинта иду по шагам, смотрю регистры, все ОК. А как теперь прыгнуть на код по адресу 0x00101000? Просто next не работает: Код Breakpoint 1, main () at main.c:102 102 ((funct)0x00101000)(); Current language: auto; currently c (gdb) n Cannot access memory at address 0x8008126 0x00100300 in ?? () (gdb) Интересно проверить, как стартап приложения себя действительно ведет. Чтобы отлаживать то, что Вы хотите, в elf файле bootloader должно быть и приложение. А его там нет. Что в таком случае будет с gdb?
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 25 2008, 05:38
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата Ну Вы хоть исправили значение для конца цикла? Да, конечно. Код стартапа для бутлоадера я взял из атмеловского примера. К сожалению, не до конца понимаю логику работы, АРМ ассемблер только изучаю.. Цитата Ну вот пример из boot.S с моими коментариями mov r0,r13 - Понятно. r13пока содержит RAM_TOP /*- Set up Fast Interrupt Mode and set FIQ Mode Stack*/ msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT /*- Init the FIQ register*/ ldr r8, =AT91C_BASE_AIC - А в чем смысл? Оно действительно используется? В самом бутлоадере нет, но приложение AIC использует. Вот как я слегка переписал инициализацию стеков: Код .EQU IRQ_STACK_SIZE, (3*8*4) .EQU FIQ_STACK_SIZE, (3*4) .EQU ARM_MODE_FIQ, 0x11 .EQU ARM_MODE_IRQ, 0x12 .EQU ARM_MODE_SVC, 0x13
.EQU I_BIT, 0x80 .EQU F_BIT, 0x40 ...
@------------------------------------- @- Setup the stack for each mode @------------------------------------- mov r0,r13
@- Set up Fast Interrupt Mode and set FIQ Mode Stack msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT mov sp, r0 sub r0, r0, #FIQ_STACK_SIZE
@- Set up Interrupt Mode and set IRQ Mode Stack msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT mov sp, r0 sub r0, r0, #IRQ_STACK_SIZE
@- Set up Supervisor Mode and set Supervisor Mode Stack msr CPSR_c, #ARM_MODE_SVC mov r13, r0 Я правильно понимаю, что bootloader нужно оставить в supervisor режиме, а в стартапе приложения перейти в user mode? Цитата Чтобы отлаживать то, что Вы хотите, в elf файле bootloader должно быть и приложение. А его там нет. Что в таком случае будет с gdb? Я полагал, что перед переходом на другой участок памяти, где располагется второй код, в дебагер можно принудительно загрузить файл с символами (file, потом load), тем самым объяснив отладчику про новые секции кода, данных и пр. Но такой трюк не прошел.
|
|
|
|
|
Apr 25 2008, 10:07
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 25 2008, 08:38)  В самом бутлоадере нет, но приложение AIC использует. Вот как я слегка переписал инициализацию стеков: Код .EQU IRQ_STACK_SIZE, (3*8*4) .EQU FIQ_STACK_SIZE, (3*4) .EQU ARM_MODE_FIQ, 0x11 .EQU ARM_MODE_IRQ, 0x12 .EQU ARM_MODE_SVC, 0x13
.EQU I_BIT, 0x80 .EQU F_BIT, 0x40 ...
@------------------------------------- @- Setup the stack for each mode @------------------------------------- mov r0,r13
@- Set up Fast Interrupt Mode and set FIQ Mode Stack msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT mov sp, r0 sub r0, r0, #FIQ_STACK_SIZE
@- Set up Interrupt Mode and set IRQ Mode Stack msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT mov sp, r0 sub r0, r0, #IRQ_STACK_SIZE
@- Set up Supervisor Mode and set Supervisor Mode Stack msr CPSR_c, #ARM_MODE_SVC mov r13, r0 Я правильно понимаю, что bootloader нужно оставить в supervisor режиме, а в стартапе приложения перейти в user mode? Да, правильно понимаете. Приложение должно иметь возможность перенастроить стеки под себя. Для этого приложению потребуется привелегированный режим работы. Цитата Я полагал, что перед переходом на другой участок памяти, где располагется второй код, в дебагер можно принудительно загрузить файл с символами (file, потом load), тем самым объяснив отладчику про новые секции кода, данных и пр. Но такой трюк не прошел. Не могу ничего сказать по поводу заливки еще одного файла, не пробовал. Не уверен, что при заливке второго файла останется доступен первый. Возможно второй раз load, замещает первый. Единственно, что риходит в голову, это слинковать для симулятора boot и app в один файл. Хотя тут будут проблемы с дублями сиволов main(), _start, и пр. Цитата(Сергей Борщ @ Apr 25 2008, 12:28)  А ходить по дизассемблированному коду по одной ассемблерной команде за раз он разве не умеет? Да вроде может, но вопрос втом есть ли оно там, этот код, по которому нужно ходить.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 26 2008, 01:30
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата Да, правильно понимаете. Приложение должно иметь возможность перенастроить стеки под себя. Для этого приложению потребуется привелегированный режим работы. Но ведь user mode это непривилигерованный режим. Или вы имеете в виду, что от бутлоадера после передачи управления второму приложению достанется supervisor режим. После выполнения всего необходимого, второе приложение уходит в юзер-моуд? Цитата(Сергей Борщ @ Apr 25 2008, 12:28)  А ходить по дизассемблированному коду по одной ассемблерной команде за раз он разве не умеет? gdb может это делать, но когда я пытался посмотреть дизассемблерный код по адресу, по которому расположен второй образ, то получал отказ. NotaBene. Я стараюсь польностью понять, как правильно сделать бутлоадер. Я думаю, что сам загрузчик должен быть компактный, без инициализации лишнего, и главная его задача - получить образ (в моем случае через usb, я возьму за основу пример от атмела с USB CDC), записать во флеш и передать управление дальше. Соответственно и стартап загрузчика будет отличаться. Вот как я его представляю (попправьте plz где ошибаюсь!): 1) определить точку входа [это сделал] 2) установить вектора [это сделал] 3) проинициализировать флеш [во всех примерах, что я встречал, выставляется flash wait state] 4) настроить критические устройства [все что я видел - это отключение watchdog'a, настраивается осциллятор...] 5) инициализация стека [это тоже есть, вроде разобрался что к чему] 6) включить прерывания [это нужно сделать после настрйки стеков. Обязательно настроить стек supervisor режима] 7) сменить режим процессора [как я понял, это делается вместе с пункотом 6; выставляется нужный режим в program status регистре] 8) jump на main() Кроме этого в примерах видел, что ставятся заглушки для AIC обработчиков. Это как быcommon practics?
|
|
|
|
|
Apr 28 2008, 11:33
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ Apr 26 2008, 04:30)  Но ведь user mode это непривилигерованный режим. Или вы имеете в виду, что от бутлоадера после передачи управления второму приложению достанется supervisor режим. После выполнения всего необходимого, второе приложение уходит в юзер-моуд? gdb может это делать, но когда я пытался посмотреть дизассемблерный код по адресу, по которому расположен второй образ, то получал отказ. Лучше всего, в bootloader использовать минимум. И передачу управления на приложение делать в ARM Supervisor режим. Суть в том, что приложение вообще не должно знать о наличии bootloader, и потому действовать как будто оно там одно. Если Вы хотите использовать прерывания в bootloader - инициализируйте два стека - SVC и IRQ. А приложение потом переинициализирует все по полной программе. Цитата NotaBene. Я стараюсь польностью понять, как правильно сделать бутлоадер. Я думаю, что сам загрузчик должен быть компактный, без инициализации лишнего, и главная его задача - получить образ (в моем случае через usb, я возьму за основу пример от атмела с USB CDC), записать во флеш и передать управление дальше. Соответственно и стартап загрузчика будет отличаться. Вот как я его представляю (попправьте plz где ошибаюсь!): Ну "правильно" - здесь достаточно растяжимое понятие. Главное - что бы он выполнял нужные Вам функции. Цитата 1) определить точку входа [это сделал] 2) установить вектора [это сделал] 3) проинициализировать флеш [во всех примерах, что я встречал, выставляется flash wait state] 4) настроить критические устройства [все что я видел - это отключение watchdog'a, настраивается осциллятор...] 5) инициализация стека [это тоже есть, вроде разобрался что к чему] 6) включить прерывания [это нужно сделать после настрйки стеков. Обязательно настроить стек supervisor режима] 7) сменить режим процессора [как я понял, это делается вместе с пункотом 6; выставляется нужный режим в program status регистре] 8) jump на main() 1) - не понял, точка входа куда? 7) - ну режимы процессора меняются по мере настройки стеков. Потом не забыть вернуться в SVC. Цитата Кроме этого в примерах видел, что ставятся заглушки для AIC обработчиков. Это как быcommon practics? В общем да. Хотя не обязательно нужно устанавливать заглушки. Здесь есть подводный камень. Он появится если Вы захотите из приложения "вызвать" функции bootloader. Это и стеки и прерывания, используемые приложением.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
May 6 2008, 14:58
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(amw @ Apr 28 2008, 14:33)  Лучше всего, в bootloader использовать минимум. И передачу управления на приложение делать в ARM Supervisor режим. Суть в том, что приложение вообще не должно знать о наличии bootloader, и потому действовать как будто оно там одно. Приветствую. Приаттачил мой последний вариант, который не работает - здесь приложение и бутлоадер, их линкер-скрипты и стартапы. Я не стал включать в архив многочисленные инклюды и пр. (типа атмеловского AT91SAM7S64.h), думаю вам будет достаточно просто взглянуть и найти ошибки  Стартап приложения содержит все дополнения и модификации по вашим советам. В принципе, можно сказать, что он максимально приближен к "скелету" стартапа, который вы публиковали в этой ветке. Здесь есть одно НО - всякий раз, когда я пытался добавить что-то свое (например, IRQ_handler) в ваш стартап, то линкер ругался "not enough room for headers".
Прикрепленные файлы
boot.zip ( 10.93 килобайт )
Кол-во скачиваний: 37
|
|
|
|
|
May 6 2008, 18:01
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ May 6 2008, 17:58)  Приветствую. Приаттачил мой последний вариант, который не работает - здесь приложение и бутлоадер, их линкер-скрипты и стартапы. Я не стал включать в архив многочисленные инклюды и пр. (типа атмеловского AT91SAM7S64.h), думаю вам будет достаточно просто взглянуть и найти ошибки  И напрасно. С телепатией у меня что-то пока туго. Я ж их теперь должен искать и придумывать чем заменить функции из Board.h, dbgu.h и все остальное. Я, знаете ли, в уме компилировать не умею. Цитата Стартап приложения содержит все дополнения и модификации по вашим советам. В принципе, можно сказать, что он максимально приближен к "скелету" стартапа, который вы публиковали в этой ветке.
Здесь есть одно НО - всякий раз, когда я пытался добавить что-то свое (например, IRQ_handler) в ваш стартап, то линкер ругался "not enough room for headers". Буду отвечать поэтапно, по мере накопления мыслей. Так что будут добавления позже. И так скрипты линкера. boot: Код CODE (rx) : ORIGIN = 0x00100000, LENGTH = 0x00040000 app: Код ROM (rx) : ORIGIN = 0x00101000, LENGTH = 0x00001000 /* 4K is enough for bootloader */ Перекрываются MEMORY REGION. Может это не заметно сейчас, но потом вылезет боком. boot: Код SECTIONS { /* first section is .text which is used for code */ . = 0x0000000; .text : { *Cstartup.o (.text) }>CODE =0 А почему ноль а не 0x00100000? app, AT91SAM7S64-ROM.ld Код .vectors : { _vectors_start = .; *(.vectors) KEEP(*(.vectors)) _vectors_end = .; } >REMAPPED AT >ROM Этой секции нет в bin/hex. После дизассеблирования получаем Код app.elf: file format elf32-littlearm
Disassembly of section .vectors:
00000000 <_vectors_start>: 0: e59ff018 ldr pc, [pc, #24]; 20 <resetvec> 4: e59ff018 ldr pc, [pc, #24]; 24 <undefvec> 8: e59ff018 ldr pc, [pc, #24]; 28 <swivec> c: e59ff018 ldr pc, [pc, #24]; 2c <pabtvec> 10: e59ff018 ldr pc, [pc, #24]; 30 <dabtvec> 14: 00000000 .word 0x00000000 18: e59ff014 ldr pc, [pc, #20]; 34 <irqvec> 1c: e59ff014 ldr pc, [pc, #20]; 38 <fiqvec> А в hex файле (начало файла) Код :020000040010EA :10103C00FEFFFFEAFDFFFFEAFCFFFFEAFEFFFFEA0F :10104C0004E04EE200402DE900E04FE100402DE9C4 Переместить вектора в секцию .text и поставить С НАЧАЛА СТАРТАП ФАЙЛА. PS: Проверить в железе сейчас не могу, нет под рукой SAM7. Так что внесите изменения, потестите, сообщите симптомы. Смотрите вложение и дайте недостающие функции. ---------------- Пока все. Действуйте.
Сообщение отредактировал amw - May 6 2008, 18:19
Прикрепленные файлы
boot.zip ( 55.17 килобайт )
Кол-во скачиваний: 36
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
May 7 2008, 14:49
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Приветствую. Выкладываю полный архив со всеми инклюдами. Цитата boot: Код CODE (rx) : ORIGIN = 0x00100000, LENGTH = 0x00040000 app: Код ROM (rx) : ORIGIN = 0x00101000, LENGTH = 0x00001000 /* 4K is enough for bootloader */ Перекрываются MEMORY REGION. Может это не заметно сейчас, но потом вылезет боком. Возможно это и было корнем проблем. Как же я упустил это из виду... Цитата Код SECTIONS { /* first section is .text which is used for code */ . = 0x0000000; .text : { *Cstartup.o (.text) }>CODE =0 А почему ноль а не 0x00100000? Но ведь стартует с 0х0? Хотя не уверен, откуда взялась эта строчка. Цитата app, AT91SAM7S64-ROM.ld Код .vectors : { _vectors_start = .; *(.vectors) KEEP(*(.vectors)) _vectors_end = .; } >REMAPPED AT >ROM Этой секции нет в bin/hex. После дизассеблирования получаем Ранее вы советовали выносить vectors в отдельную секцию, а не мешать с .text. IMHO это вполне логично, особенно если потом планируется тусовать вектора по памяти. Цитата Код app.elf: file format elf32-littlearm
Disassembly of section .vectors:
00000000 <_vectors_start>: 0: e59ff018 ldr pc, [pc, #24]; 20 <resetvec> 4: e59ff018 ldr pc, [pc, #24]; 24 <undefvec> 8: e59ff018 ldr pc, [pc, #24]; 28 <swivec> c: e59ff018 ldr pc, [pc, #24]; 2c <pabtvec> 10: e59ff018 ldr pc, [pc, #24]; 30 <dabtvec> 14: 00000000 .word 0x00000000 18: e59ff014 ldr pc, [pc, #20]; 34 <irqvec> 1c: e59ff014 ldr pc, [pc, #20]; 38 <fiqvec> А в hex файле (начало файла) Код :020000040010EA :10103C00FEFFFFEAFDFFFFEAFCFFFFEAFEFFFFEA0F :10104C0004E04EE200402DE900E04FE100402DE9C4 Переместить вектора в секцию .text и поставить С НАЧАЛА СТАРТАП ФАЙЛА. А почему вы решили, что в bin этой секции нет, ведь там явно написано "дизассемблируем секцию .vectors" и далее армовские инструкции. А hex я пока читать не умею  Я протестирую приложенный архив, о результатах сообщу. Спасибо.
Прикрепленные файлы
loader.zip ( 141.26 килобайт )
Кол-во скачиваний: 41
|
|
|
|
|
May 7 2008, 15:10
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ May 7 2008, 17:49)  Приветствую.
Выкладываю полный архив со всеми инклюдами. Возможно это и было корнем проблем. Как же я упустил это из виду... Вряд ли. Бинарники пока меньше 1К. Цитата Но ведь стартует с 0х0? Хотя не уверен, откуда взялась эта строчка. Нужно смотреть на результат. Цитата Ранее вы советовали выносить vectors в отдельную секцию, а не мешать с .text. IMHO это вполне логично, особенно если потом планируется тусовать вектора по памяти. А почему вы решили, что в bin этой секции нет, ведь там явно написано "дизассемблируем секцию .vectors" и далее армовские инструкции. А hex я пока читать не умею  В дизассемблере даны КОДЫ КОМАНД. В бинарике и HEX файле этих кодов НЕТ. Цитата Я протестирую приложенный архив, о результатах сообщу. Спасибо.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
May 8 2008, 06:10
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата В дизассемблере даны КОДЫ КОМАНД. В бинарике и HEX файле этих кодов НЕТ. Да, сейчас вижу, разбираюсь. Все проверил - видимо, дело в ключах компилятора/линкера, используемых вами и мною, и это сильно влияет на генерируемый бинарник. Сейчас все пересобрал у себя и специально проверил : и в бинарнике и в hex-файле коды присутствуют, но в hex они в перевернутом виде (endianess?). Я только подправил секцию MEMORY в скриптах, чтобы секции не перекрывались. Вопрос по makefile'ам. Никак нельзя сделать так: Код /* Size of bootloader region */ BOOTSIZE = 0x00040000;
/* Memory Definitions */ MEMORY { CODE (rx) : ORIGIN = 0x00100000, LENGTH = BOOTSIZE ... } Линковщик ругается "nonconstant expression for length". Поискал в документации среди встроенных ф-ций линкера, но ничего подходящего не нашел.
|
|
|
|
|
May 8 2008, 09:12
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ May 8 2008, 09:10)  Да, сейчас вижу, разбираюсь. Все проверил - видимо, дело в ключах компилятора/линкера, используемых вами и мною, и это сильно влияет на генерируемый бинарник. Сейчас все пересобрал у себя и специально проверил : и в бинарнике и в hex-файле коды присутствуют, но в hex они в перевернутом виде (endianess?).
Я только подправил секцию MEMORY в скриптах, чтобы секции не перекрывались. Ну и каков результат? Цитата Вопрос по makefile'ам. Никак нельзя сделать так: Код /* Size of bootloader region */ BOOTSIZE = 0x00040000;
/* Memory Definitions */ MEMORY { CODE (rx) : ORIGIN = 0x00100000, LENGTH = BOOTSIZE ... } Линковщик ругается "nonconstant expression for length". Поискал в документации среди встроенных ф-ций линкера, но ничего подходящего не нашел. Во первых это линкер скрипт а не Makefile. Во вторих - никак. Здесь могут использоваться толко константы. Выдержки из info ld Цитата A linker script may contain at most one use of the `MEMORY' command. However, you can define as many blocks of memory within it as you wish. The syntax is: MEMORY { NAME [(ATTR)] : ORIGIN = ORIGIN, LENGTH = LEN ... } The ORIGIN is an numerical expression for the start address of the memory region. The expression must evaluate to a constant and it cannot involve any symbols. The keyword `ORIGIN' may be abbreviated to `org' or `o' (but not, for example, `ORG'). The LEN is an expression for the size in bytes of the memory region. As with the ORIGIN expression, the expression must be numerical only and must evaluate to a constant. The keyword `LENGTH' may be abbreviated to `len' or `l'. In the following example, we specify that there are two memory regions available for allocation: one starting at `0' for 256 kilobytes, and the other starting at `0x40000000' for four megabytes. The linker will place into the `rom' memory region every section which is not explicitly mapped into a memory region, and is either read-only or executable. The linker will place other sections which are not explicitly mapped into a memory region into the `ram' memory region. MEMORY { rom (rx) : ORIGIN = 0, LENGTH = 256K ram (!rx) : org = 0x40000000, l = 4M }
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
May 8 2008, 11:08
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата Все проверил - видимо, дело в ключах компилятора/линкера, используемых вами и мною, и это сильно влияет на генерируемый бинарник. Сейчас все пересобрал у себя и специально проверил : и в бинарнике и в hex-файле коды присутствуют, но в hex они в перевернутом виде (endianess?).
Я только подправил секцию MEMORY в скриптах, чтобы секции не перекрывались. А вот что насчет перевернутых адресов, так и должно быть? По идее тулчейн уже все знает о целевой платформе и генерирует код в соответствующем endianess. Цитата(amw @ May 8 2008, 12:12)  Ну и каков результат? На данный момент это никак не повлияло на процесс, но несомненно это отразится в будущем. Вы правильно заметили, что потом можно огрести немало проблем с пересечением секций.
|
|
|
|
|
May 8 2008, 11:20
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ May 8 2008, 14:08)  А вот что насчет перевернутых адресов, так и должно быть? По идее тулчейн уже все знает о целевой платформе и генерирует код в соответствующем endianess. Ну так у SAM7S little endian. Все правильно. По младшему адресу - младший байт. Если есть светодиоды на плате, то для визуализации процесса можно понатыкать в Cstartup.S поочередное зажигание. И посмотреть где оно стопорится. Давайте последний проект, посмотрю. Цитата На данный момент это никак не повлияло на процесс, но несомненно это отразится в будущем. Вы правильно заметили, что потом можно огрести немало проблем с пересечением секций.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
May 9 2008, 05:37
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(amw @ May 8 2008, 14:20)  Ну так у SAM7S little endian. Все правильно. По младшему адресу - младший байт. Я так и думал, но как объяснить слудующее. Фрагмент дизассемблера: Код Disassembly of section .text:
0010403c <Undef_Handler>: 10403c: eafffffe b 10403c <Undef_Handler> А вот hex: Код :10403C00FEFFFFEA...... Дизассембляция сделана командой: arm-elf-objdump -h -S -C - то есть дамп, как я понимаю, уже должен быть с перевернутыми байтами? Цитата Если есть светодиоды на плате, то для визуализации процесса можно понатыкать в Cstartup.S поочередное зажигание. И посмотреть где оно стопорится. Давайте последний проект, посмотрю. Отличная идея! Сейчас приложу архив.
Прикрепленные файлы
boot.zip ( 106.05 килобайт )
Кол-во скачиваний: 40
|
|
|
|
|
May 11 2008, 10:35
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ May 9 2008, 08:37)  Я так и думал, но как объяснить слудующее. Фрагмент дизассемблера: Код Disassembly of section .text:
0010403c <Undef_Handler>: 10403c: eafffffe b 10403c <Undef_Handler> А вот hex: Код :10403C00FEFFFFEA...... Дизассембляция сделана командой: arm-elf-objdump -h -S -C - то есть дамп, как я понимаю, уже должен быть с перевернутыми байтами? Тут как раз байты не перевернуты, а стоят в правильном порядке. Маленький экскурс в HEX файлы. : - Признак начала строки HEX. Все строки должны начинаться с него. 10 - Размер строки в байтах. Считаются только полезные байты. 403C - Адрес с которого начинает размещаться строка. 00 - Код записи. 00 - значит строка с данными. FE - Первый байт данных. Размещается по указанному адресу. FF - Второй байт данных. Размещается по адресу (указанный + 1) .... Всего байтов данных столько, сколько указанно после двоеточия. <Последний байт в строке> - контрольная сумма. Цитата Отличная идея! Сейчас приложу архив. Сейчас гляну. Посмотрел. 1. У Вас приложение (в бинарик и HEX) начинается вот с этого кода Код Undef_Handler: B Undef_Handler 10403c: eafffffe b 10403c <Undef_Handler> А векторов там нет. 2. Разберитесь сначала с ARM режимом, а то сразу и ARM и THUMB Вас запутывает. Например, Ваш boot переходит в THUMB, а потом запускает приложение командой BL, а приложение начинается с ARM режима, и соответмтвенно ничего не работает. 3. Цитата arm-elf-objdump -h -S -C - то есть дамп, как я понимаю, уже должен быть с перевернутыми байтами? А причем здесь endian? -h - Показать заголовок -S - Показать исходный код Цитата -C --demangle[=style] Decode (demangle) low-level symbol names into user-level names. Besides removing any initial underscore prepended by the system, this makes function names readable. Different compilers have different mangling styles. The optional demangling style argument can be used to choose an appropriate demangling style for your compiler. Указать тип разименования символов, например c, cxx .... Причем тут endian? Радуйтесь, что у Вас litle-endian  .
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
May 13 2008, 05:28
|
Местный
  
Группа: Свой
Сообщений: 292
Регистрация: 9-11-04
Пользователь №: 1 077

|
Цитата(amw @ May 11 2008, 13:35)  1. У Вас приложение (в бинарик и HEX) начинается вот с этого кода Код Undef_Handler: B Undef_Handler 10403c: eafffffe b 10403c <Undef_Handler> А векторов там нет. 2. Разберитесь сначала с ARM режимом, а то сразу и ARM и THUMB Вас запутывает. Например, Ваш boot переходит в THUMB, а потом запускает приложение командой BL, а приложение начинается с ARM режима, и соответмтвенно ничего не работает. Вот смотрите... После сброса чип бутлоадеру в ARM-режиме. В стартапе бутлоадера после различных инициализаций и пр. происходит переход на main()-функцию, причем переход по инструкции BX, которая, суда по документации, переводит процессор в THUMB-режим. Выходит, что приложение получает thumb-процессор? Но как это может повлиять на расположение секций в памяти и в образе? Означает ли это, что все приложение, запускаемое бутом, нужно пересобирать c ключем -mthumb?
|
|
|
|
|
May 13 2008, 07:16
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(romez777 @ May 13 2008, 08:28)  Вот смотрите... После сброса чип бутлоадеру в ARM-режиме. В стартапе бутлоадера после различных инициализаций и пр. происходит переход на main()-функцию, причем переход по инструкции BX, которая, суда по документации, переводит процессор в THUMB-режим.
Выходит, что приложение получает thumb-процессор?
Но как это может повлиять на расположение секций в памяти и в образе? Означает ли это, что все приложение, запускаемое бутом, нужно пересобирать c ключем -mthumb? Если bootloader переводит процессор в thumb то и приложение должно быть ЦЕЛИКОМ thumb. Поскольку процессор по ресету стартует в ARM SUPERVISOR и все исключения - а соответственно и обработчики прерываний, которые у Вас в Cstartup.S то-же работают в ARM - то запуск приложения ДОЛЖЕН производится в ARM режиме. ВО ВСЯКОМ СЛУЧАЕ ПРИЛОЖЕНИЕ ПО ВЕКТОРУ ИСКЛЮЧЕНИЯ RESET (у Вас ӕто 0x00001000) ДОЛЖЕН СТАРТОВАТЬ В ARM SUPERVISOR РЕЖИМЕ. Конечно Вы можете делать все что угодно, но тогда подразумевается, что Вы точно знаете что делаете. То есть, по крайней мере Вы должны из bootloader перейтив ARM SVC. Прежде чем смешивать ARM и THUMB и в bootloader и в app разберитесь сначала с одним режимом. И ӕтот режим для Вас должен быть ARM. А THUMB введете потом. И вообще, у Вас что действительно есть необходимость в thumb прямо сейчас? В конечном итоге, рекомендую оставить main() в bootloader полностью ARM и запускать приложение bootloaderом именно из ARM и именно в ARM приложения, а функции перепрошивки флеш можно и THUMB сделать.
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|