Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Boot Baremetal application from QSPI flash
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
WitFed
Привет всем из раздела "Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Среды разработки - обсуждаем САПРы" (http://electronix.ru/forum/index.php?showforum=5) !
Там народу поменьше, спецы по другим областям обсуждают Квартус, и решил я, недоARMпрограммер за 2 месяца, обратиться в более людное место, вдруг счастья и гуру тут поболе ! wink.gif
Вот ссылки на мои вопли: http://electronix.ru/forum/index.php?showtopic=121120, http://electronix.ru/forum/index.php?showt...t&p=1254953. Тут я перешерстил всё за 3 часа и во "взрослом" ARM -- нет похожей тематики.

Купили мы "в одном флаконе" (rocketboards.org/foswiki/Documentation/AlteraSoCDevelopmentBoard) ПЛИС и 2 проца ARM-Cortex-A9 за хорошие деньги, масса периферии всякой, но пока не удаётся главное -- чтоб стартовало наше целиком самописное приложение из флэши, несмотря на многочисленные намёки в документации, бахвальство народа по форумам и смотря на отсутствие конкретного алгоритма нигде wink.gif sad.gif
Там в комплекте есть SD-карточка, из неё прекрасно и долго стартует Линух, но очень хочется чего-то полностью своего, без непонятных мегатонн кода, и из любой флэши.
...Ну и книжку хорошую на нашем языке по всем ARMам сразу, тут на форуме ж полно материала для научных обобщений! Думаю, участники даже скинутся на такой мегапроект! wink.gif

У Альтеры есть образец программы hwlib.axf, который моргает диодиками, будучи загружен через отладчик ARM DS-5 (Altera Edition) и стартован, всё более-менее работает, кроме кучи мелких пакостных особенностей, а вот зашиваешь приложение во флэшь -- зацикл на векторе прерывания 1.
Система загрузки в этой AE такова: жёсткий BootROM на 64К с адреса 0, он что-то инитит в процессоре, потомв зависимости от замкнутости 3х внешних ног вызывает Preloader из некоторой флэши в 64К ОЗУ FFFF0000 (он инитит более большие RAM и пускает следующую стадию; исходники Прелоадера уже есть на базе стандартного u-boot, компилятся в u-boot-spl.axf -- second program loader). И этот Прелоадер должен грузить 3ю стадию с адреса и девайса, что ему указано в особом h-нике. Хорошо им грузится u-boot, а мой/Альтерный пример hwlib -- никак.
Я поставил перед пуском 3й стадии бесконечный цикл, включаю плату, подключаюсь туда отладчиком, прыгаю на следующую строку после зацикла, там прыг на адрес 02000000 загруженного приложения -- и кирдык команд через 8 стартапных в asm, на вызове SVC 0x123456 (ранее была SWI, как много где пишут, software interrupt, supervisor call). Идёт прыжок на адрес FFFF0008, где вечный зацикл, ибо из 8 векторов задан только первый, резетный, остальные циклят сами в себе. Причём бит расположения прерываний lo/hi в системном регистре указывает на таблицу прерываний по адресу 0! И даже если глянуть на адрес 0, там те же проблемы -- 1е прерывание обслужено вектором, остальные циклят.
Получается, что системный вызов упирается в неназначенный обработчик, который явно должен был быть назначенным в u-boot-spl.axf на 2й стадии, ибо в моём hwlib ещё очень и очень ранняя фаза загрузки. Это я только сегодня допёр, глядя как рыба об лёд на свои проблемы. Ну или в BootROM дожно быть нечто такое, хотя по логике он мало знает о dest приложениях, вряд ли сможет их обслужить. Плюс описания сервиса по SVC я не встретил нигде пока.
В моём приложении по адресу 02000000 тоже есть 8 обработок прерываний, но они также ограничиваются первым резетным, да и не могли ещё вступить в действие никак за 8 команд!
И самый Склиф состоит в том, что при запуске чисто моего hwlib-приложения под отладчиком вся эта многостадийная хрень стартует успешно!
Какие есть идеи, где собаки роются и как шаманят ? Разве сам отладчик может быть обработчиком запросов системного уровня ? Это же противоречит любым разумным предположениям, ибо наш код должен работать и самостоятельно, отладчик лишь приближает к этой ступени!
Пробовал также грузить тот же самый код Прелоадера, настроенного для дальнейшей работы из QSPI-флэши, из отладчика, доводить его до конечного перехода, там те же 8 команд -- и зацикл.
Во флэши у меня hwlib лежит абсолютно правильно -- выгружал память с 02000000 наружу во всех случаях, различий нет что при хорошем, что при плохом вариантах. Так же и с Прелоадерными кодами по адресу FFFF0000.
Мож какой-то другой отладчик посоветуете, чтоб работал по JTAG с нашим ПЛИС-ARM-ом ?
WitFed
Похоже, эта инструкция SVC заведует семихостингом!
Я написал свой обработчик этого дела:
Код
void svc_handler(void) {return; }

, обработка стала проходить чуть дальше, но после первого же вызова происходит проверка возвращаемого значения, несколько загрузок из-за конца образа, в SP попадает нечто некошерное и падёж по 4му вектору.
Поменял свой "обработчик" на:
Код
int svc_handler(void) {return -1; }

, пустился -- вообще всё живое, заходит, выходит, гуляет по main(), но диодики не моргают, как в отладчике wink.gif
Что-то надо ещё мутить, примеры API этого SVC кто-то встречал ?
jcxz
Цитата(WitFed @ Jun 16 2014, 21:07) *
Что-то надо ещё мутить, примеры API этого SVC кто-то встречал ?

Открываете документацию на ядро Cortex. Там SVC подробно разжёвана.
WitFed
Она-то разжёвана, но на другом уровне -- что в какой битик и регистр попадает при вызове, и как возвращаться обратно.
А вот смысл значений в регистре r0 (16) и регистре r1, которые заполняются перед вызовом "SVC 123456" на 8й исполняемой команде моего файла hwlib.axf, и самого этого 123456, мне пока не встретился нигде.
Там же на дороге народ вешает крючочек с попыткой продать нам платную версию компилятора вместо lite, которая ограничивает стек с кучей в сумме до 64К. Вдруг и DS-5 в этом замешана...
Т.е. похоже по дизассемблингу, что в r0 № функции, в r1 -- адрес, сама функция вертает что-то отрицательное при неудаче, а при удаче из того адреса можно будет получить результаты вызова семихостинга, типа адрес стека, начало кучи, назначить их в глобальные места...
И неясно, кто эти API-стандарты придумывает, ARM или Altera, или даже rocketboards, как производитель платы.
У меня в коде штук 10 вызовов этой самой "секретной добавки" "SVC 123456" с кодом EF123456 ! И ещё незнамо сколько всего без исходников, а из безмозглых либ...
Начал думать, как бы вообще подменить всё своими текстами, ибо иметь дело с непознанным в силу неописанности (Doxygen-овые приблуды в отдельном каталоге в расчёт не беру, они к исходникам ничего не прибавляют) очень тяжко который месяц.
Интересную особенность обнаружил сегодня -- если в успешно пускающемся непосредственно из DS-5 приложении написать в окне отладки команду "set semihosting enabled false", то оно начинает падать абсолютно аналогично загружаемому из флэши, т.е. логично -- нет обработчика, так циклим на дефолтном, а есть -- он вызывается. Чудеса исчезают, когда "SVC 123456" нормально отрабатывала с потусторонним миром ! wink.gif
А если попробовать "set semihosting enabled true" с грузимым из флэши, то он не подымается -- варнинг на эту операцию после старта приложения.
Я ж ещё нашёл опцию SEMIHOSTING в bsp-editor, она выключена сейчас. Думаю -- включу, и Прелоадер начнёт поддерживать то самое SVC-прерывание по адресу 08 для моего приложения, сообщит ему, жив ли хост, есть ли отладчик, перекомпилил его, зашил... Ан нет -- просто всё вешается гораздо ранее, после первой мессаги в RS, и из хоста отладчик достучаться не может.

...Слова "SVC" и "semihosting" на сайте Альтеры находятся несильно, у ARM этого добра выше крыши (хотя сайт infocenter.arm.com с фреймами, оставляющими на полезную информацию менее трети экрана, жутко неудобен, назад ходит не туда), похоже, они законодатели моды, в http://infocenter.arm.com/help/topic/com.a..._processors.pdf вроде бы нашлось что-то!..
jcxz
Выкиньте все эти мегабайты чужого кода - зачем в них ковыряться?
Откройте даташит на CPU, найдите лёгкий stand-alone проект под ваш CPU, разберитесь с его asm-стартапом, перепишите под себя.
На SVC часто вешаются системные сервисы.
Например - видел в каких-то исходниках, использовавших чужой стартап в котором CPU сразу переводился в thread-mode, main()
запускался уже в thread-mode, потом, позже при инициализации чего-то (для чего требовался handler-mode), как раз и использовались
вызовы SVC для переключения в handler-mode.

Если вы пишеет всё сами, то и управлять режимами CPU можете сами. И SVC-сервис тоже можете использовать для своих целей (если захотите).
И никакие "секретные добавки" вам будут не страшны wink.gif
Я например SVC использую для вызова ловушек критических ошибок и других надобностей.
WitFed
Там жуткое количество всяких режимов и регистров с битиками, несмотря на заявления о простоте ARM и посему энергоэффективности, моя башка за пару мес так и не вобрала 1/33 wink.gif
Потому и люблю BM, что там инфы нужно хранить на порядки меньше, всё своё, ну только сначала пару мес напрячься, чтобы достругаль Альтерное брёвнышко под себя от лишних сучков wink.gif
Советы хорошие, спасибо, мне ещё несколько мессаг осталось для просьбы попадания в Свои, а там книжек выкачаю ARMированных, начну подыматься над тупыми текущими потугами... Если начальство ещё дозволит wink.gif
...Но мечта идиота начала сбываться -- к вечеру приложение моё размером 5М стартовало из QSPI менее чем за 2 с, прогрузило ПЛИС и поморгало диодиками!
Вдруг кому надо окажется: пока Альтерцы не чешутся с заточкой Прелоадера под загрузку ВМ из любой флэши, нужно повесить свой обработчик прерывания SVC в файле C:\altera\13.1\embedded\examples\hardware\cv_soc_devkit_ghrd\software\spl_bsp\uboot-socfpga\common\spl\spl.c:
Код
int svc_my_handler(int R0, int R1) {
  __asm (" push {r1-r12, lr}"); int *r1 = (int*)R1, i;
  switch (R0) {
        case 0x01: { // 8.17 SYS_OPEN // R0 contains a nonzero handle if the call is successful
          char *cp = (char*)r1[0];  static int fn = 2;  R0 = -1;
          if (r1[2] != 3 || cp[0] != ':' || cp[1] != 't' || cp[2] != 't') break;
          R0 = fn; if (fn == 2) fn--; else if (fn == 1) fn = 3; else fn++; break;
        }
        case 0x05: { // 8.27 SYS_WRITE
          if (r1[0] == 1) { volatile int j;
                char *cp = (char*)r1[1]; for (i = 0; i < r1[2]; i++) {
              _serial_putc(*cp++,1);
              for (j = 0; j < 1000*10; j++);
                }
          }
          R0 = 0; break; // R0 contains 0 if the call is successful
        }
        case 0x09: // 8.16 SYS_ISTTY
          R0 = 1; break; // R0 contains: 1 if the handle identifies an interactive device; 0 if the handle identifies a file
        case 0x0C: // 8.12 SYS_FLEN
          R0 = -1; break; // R0 contains the current length of the file object, if the call is successful
        case 0x13: // 8.11 SYS_ERRNO (0x13)
          /*R0 = 0x13;*/ break; // On exit, R0 contains the value of the C library errno variable.
        case 0x15: { // 8.13 SYS_GET_CMDLINE // On exit:
          R0 = 0; r1[1] = 11; // Register R1 points to a two-word data block:
                // word 1 a pointer to null-terminated string of the command line
                // word 2 the length of the string.
                // Register R0 contains an error code: 0 if the call is successful
          char *c = (char*)(r1[0]);
          *c++ = 'h'; *c++ = 'w'; *c++ = 'l'; *c++ = 'i'; *c++ = 'b'; *c++ = '.'; *c++ = 'a'; *c++ = 'x'; *c++ = 'f'; *c++ = ' '; *c++ = 0;
          break;
        }
        case 0x16: // 8.14 SYS_HEAPINFO // On entry, R1 contains the address of a pointer to a four-word data block.
      if (0) R0 = -1;
      else { r1 = (int*)(r1[0]); R0 = 0;
        r1[0] = 0x023B3BA0; r1[1] = 0/*x023C339C*/; r1[2] = 0/*x023C3BA0*/; r1[3] = 0x023C33A0;
      }
      break;
        default:
          R0 = -1; break;
  }
  __asm (" pop {r1-r12, lr}");
  return R0;
}

И его подключить внизу функции "__weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)":
Код
#ifdef CONFIG_HW_WATCHDOG
        WATCHDOG_RESET();
#endif
        //volatile int i = 0; while (printf("? %i\n", i) != -128 && !i) {i++; i--; }
        printf("!!!\n");
        *(int *)(8 + 8*4 - 4) = (int)(void*)&svc_my_handler;
        disable_interrupts();
        image_entry((u32 *)boot_params_ptr);
}

Там строка с комментарием служила временной остановке после ВКЛ, чтобы подключиться отладчиком DS-5 и продолжить дальше через изменение PC.
Основная проблема -- жёсткая завязанность на семихостинг у Альтерного примера, и если он не включён, отладчик его не юзает, прерывание не обрабатывается "чудесным образом", всё лежит. Повесил свой обработчик, написал обработку всех встреченных у себя функций (основываясь на вчерашней ссылке описания семихостинг-API в pdf) ровно так, как это делал отладчик, и всё задышало. Только диоды не моргали -- время обработки оказалось малым, я начал выводить семихостовые сообщения в консоль (вместо отброса) в RS -- всё равно малая задержка, ну и вставил цикл по переменной volatile до 10 000, теперь нормально.
Заодно убрал хитромудрое ограничение по размерам стека и памяти для lite-версии компилятора -- там если 0 читается из дебужного ответа в функции 16, оставляется дефолтное значение. Чуть некрасиво, что конкретные значения начала кучи и начала стека сидят в Прелоадере, но это ещё обдумается, главное -- BAREMETAL ПРОГА САМА ВКЛЮЧАЕТСЯ ИЗ ФЛЭШИ!!! wink.gif
Информация долго добывается почему-то в ARMированных дебрях, а с нею уже гораздо легче!
WitFed
Может кто-нибудь изложить всё необходимое для старта ARM-developing на 10 страницах с точными ссылками на дальнейшее раскрытие вместо тысяч по десяткам и сотням страничек с перекрёстным дубляжом ?
Очень неэффективна система, когда отделы документирования фирм-разработчиков живут своей жизнью вдали от самих разработчиков и получают зряплату за "план по валу" -- количество написанного текста, а совсем не его качество и отзывы клиентуры !
Понятно, что всё это вывалена информация нужная, но очень ненаучно и неудобно изложенная, может и необходимая высоколобым разработчикам круто-ресурсо-жрательных-ОС, но не большинству посетителей web-ресурсов, которым сначала нужен "вид сверху".

Я, например, никак не могу надыбать исчерпывающее описание inline-ассемблера. Например, в тексте функций:
Код
void enable_interrupts (void) {
        unsigned long temp;
        __asm__ __volatile__("mrs %0, cpsr\n"
                             "bic %0, %0, #0x80\n"
                             "msr cpsr_c, %0"
                             : "=r" (temp)
                             :
                             : "memory");
}

int disable_interrupts (void) {
        unsigned long old,temp;
        __asm__ __volatile__("mrs %0, cpsr\n"
                             "orr %1, %0, #0xc0\n"
                             "msr cpsr_c, %1"
                             : "=r" (old), "=r" (temp)
                             :
                             : "memory");
        return (old & 0x80) == 0;
}

какие-то многострочные фичи с параметрами в виде регистровых переменных и кучей двоеточий совсем не С-ного вида, преобразуемые особым компилятором в нечто рабочее, "memory" вообще неясное...

По направлению этих disable_interrupts/enable_interrupts виден следующий суровый просмотр/недотестинг Альтеры/rocketboards, "7 нянек" в разработке нашего Altera Cyclone SoC. Возможно, те, кто "у меня всё работает", берут какое-то другое железо и качают совсем другие дистрибутивы ?..
Вход IRQ процессора срабатывает по 0, на него заведен "прерывательный" выход PIO-блочка в ПЛИС, который по дефолту 0 и в 1 приходит, когда "учует" нечто. Посему, когда мы пытаемся получить прерывание от user buttons на обычной прошивке FPGA из Альтерной поставки и её недалёких производных, вызываем enable_interrupts() и мгновенно получаем непрерывное прерывание и даже непонятное затирание кода обработчика танцующим мусором при попытках хождения в отладчике. В регистре CP15_ISR сразу явственно виден бит IRQ pending, если прошивка ПЛИС прогрузилась.
Как Линукс стартует на таком "ослике", ума не приложу. То ли это только до первого ософления живёт, то ли поллинг у них мой любимый...

Ещё глобально интересен вопрос, почему в disable_interrupts() ставятся 2 бита из управляющего регистра (маска 0xc0), а в enable_interrupts() стирается один (0x80)? Как можно разрешить прерывание FIQ, если перед этим были запрещены оба ? Или в исходниках u-boot, откуда эти "тела", просто не было необходимости в этом, прерывания только усугубляют разработки ?

P.S. По прерываниям какие-то непонятки, на самом деле -- бит CP15_ISR.I ведёт себя недетерминированно.
То на входе в main() уже торчит, то после printf() в ините DMA вскочит, было и минут 10, что доходил 0м до моего теста прерываний!
В доке на архитектуру DDI0406C_b* он описан как RO и отражающий непосредственно ноги процессора вместе с CP15_ISR.F.
Ногам верить надо, они живые, инверсия вдруг и есть на входе, но на кого наехать теперь, неясно wink.gif
По-любому, запрос стоит, причина его неясна, обработчик вызывается, как только разрешаю прерывания, не видит нажатых button-ов, выходит, сразу заходит обратно, и кирдык навечно wink.gif

P.P.S. Много упоминаний в тексте о "PL1 mode", бывает ещё 0 послабше и 2 покруче, а в каком регистре прячут этот privilege level ?
jcxz
Цитата(WitFed @ Jun 20 2014, 12:31) *
Может кто-нибудь изложить всё необходимое для старта ARM-developing на 10 страницах с точными ссылками на дальнейшее раскрытие вместо тысяч по десяткам и сотням страничек с перекрёстным Я,

Это Вы процессором ошиблись. Если Вам на 10 страничек надо, то Вам, извините, прямой путь на AVR. sm.gif
Уровень Cortex-M3 (к примеру) это уже по-минимуму - 2-3 pdf в сумме ~1000страниц.
А уровень вашего CPU - ещё больше. С ним дела не имел, но имел например с L137, примерно подобным по уровню.
А для L137 я изучал уже порядка ~30 pdf по несколько десятков-сотен страниц каждый.
И это была только необходимая мне периферия, а не вся.

Цитата(WitFed @ Jun 20 2014, 12:31) *
например, никак не могу надыбать исчерпывающее описание inline-ассемблера. Например, в тексте функций:
...

Совет: Не пользуйтесь inline-ассемблером, пользуйтесь нормальным.

Изучать CPU имхо нужно примерно в таком порядке:
1.Ядро (или ядра): архитектура, режимы, прерывания/исключения. Можно выборочно, а позже возвращаться и дополнять.
Это даже если не собираетесь писать на асм. Если собираетесь, то ещё +систему команд, регистры, адресации и т.п.
2.Карта памяти.
3.Система тактирования (источники, частоты разных блоков, PLL и т.п.), питания.
4.Пины. GPIO, мультиплексор пинов.
5.Система прерываний/исключений (может входить в ядро, а может система прерываний ядра дополняться внешним контроллером прерываний).
6.Если ядер несколько - система синхронизации/взаимодействия ядер.
7.Ну и далее - вся необходимая периферия поблочно.
Параллельно всему этому нужно изучать документацию на компилятор, ассемблер и линкер.

В таком порядке изучение последующих шагов будет основываться на знаниях полученных от предыдущих шагов.
WitFed
Спасибо, продолжу ковыряния в 333 соснах, но особо много периферии нам не надо, просто начальство получило намёк, что кортексы с крутыми расширениями умеют в рилтайме сжимать большое видео, а аналогичные IP-ядра стоят как самолёт, ну и хочет переложить числомолотилку на ARM, а ПЛИС рядышком займётся вводом-выводом, чем-то ещё отработанным...
Делов там будет немного, особенно если исходники молотилки хорошей найти ! Вдруг такие нахаляву бывают wink.gif
Пока 15 с у меня на кадр, коду open много -- а толку минимум, еле-еле научился стартовать из Альтерной флэши sad.gif
Просто масса наработок авторов проца для круто-тормоз-ОС точно окажется за бортом, кроме Neon, даже недоловленные пока прерывания -- хотелось бы найти корневые мануалы типа "вид сверху", а они вживую тысячами страниц для 100 яйцеголовых человек из всего мира, любящих ковыряться в сатанинских мелочах и усложнять жизнь простым человекам wink.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.