Миро Самек

В тази част ще започнем да разглеждаме кода, споменат в първата част и достъпен на Embedded.com, който съдържа C и C++ версиите на примера "Blinky" на платката за разработка AT91SAM7S-EK с четири мигащи светодиода от Atmel.

[Примери за кода в превода са дадени от изходните текстове, изтеглени от сайта "Quantum Leaps, LLC" и в някои случаи, за да се подобри четливостта, те могат да бъдат преформатирани и/или съкратени.

Текстът в квадратни скоби, подчертан по същия начин като този фрагмент, съдържа бележки, добавени от преводача.

Вариантът "C" се намира в поддиректорията "c_blinky", а вариантът C++ се намира в "cpp_blinky". Приложението "Blinky" е просто, но внимателно проектирано и използва всички подходи и функции, описани в тази статия. Проектът използва инструменти "CodeSourcery G++ GNU" за ARM процесор [3].

Тази част ще опише универсалния стартов код и инициализация на ниско ниво за гола система на ARM процесор. Препоръчително е да прочетете „Справочно ръководство за IAR Compiler“ [7], а именно разделите „Стартиране и прекратяване на системата“ и „Персонализиране на инициализацията на системата“.

2.1 Стартов код

Реализацията на асемблер на инициализация за голи метални системи на ARM се намира във файла "startup.s", който е един и същ за C и C++ проекти. Кодът е проектиран да бъде общ и трябва да работи на всеки ARM контролер без модификация.

Всички инициализации на процесори и платки за отстраняване на грешки преди стартирането на функцията " main() " трябва да бъдат обработени от рутината " low_level_init () ", която обикновено може да бъде написана на C/C++, нонеобходимо и в асемблер.

Списък 2.1 е началният код, написан на GNU асемблиране. Основните точки на процеса на инициализация са обяснени по-долу.

(1) Директивата ".text" казва на асемблера да добави кода в края на кодовия сегмент "text".

(2) Директивата ".code 32" избира 32-битовия набор от инструкции ARM (стойност 16 избира 16-битовия набор от инструкции THUMB). По този начин ядрото стартира в състояние ARM.

(3) Директивата " .global " прави етикета " _vectors " видим за линкера.

(4) Директивата " .func " създава информация за отстраняване на грешки за функцията " _vectors " (тялото на функцията трябва да завършва с директивата " . endfunc ").

(6) Първоначалната векторна таблица се състои само от набор от безкрайни цикли (прехвърлящи управление на себе си) и се използва, докато не бъде заменена от векторна таблица, вградена в RAM. Ако възникне изключение по време на процеса на подмяна, платката за отстраняване на грешки най-вероятно ще влезе в състояние, от което процесорът не може да се възстанови сам, така че работещите устройства трябва да имат верига (като външен таймер за наблюдение с независим източник на часовник), за да информират потребителя за това събитие.

(8) След текстовия низ [произволна дължина], включен в кода, е необходимо да се подравни към границата на машинната дума. [По-правилно е да поставите процедурата за подравняване преди изпълнимия код, защото той е този, който се подравнява.]

(9) Преминете към този етикет след нулиране.

(Функцията "low_level_init()" може да бъде написана на C/C++ при следните ограничения. Тя трябва да работи в състояние на ARM ядрото и не трябва да засяга инициализирането на секцията " .data " или " .bss ". Също така, ако е необходимореорганизация на паметта, тогава тази операция трябва да се извърши вътре във функцията "low_level_init()", тъй като след връщането на контрола кодът престава да бъде позиционно независим.)

(15) Етикетът "_cstartup" маркира началото на инициализацията на "C".

(18) Секцията ".bss" се използва за неинициализирани променливи, които според стандарта "C" трябва да бъдат зададени на нула (вижте третата част на статията).

(19) Разделът " .stack " се използва за съхраняване на стекове. Разделът е попълнен със специална стойност, за да се улесни виждането на използването на стека в програмата за отстраняване на грешки.

(20) Всички указатели на стека на комутирани регистрови банки се инициализират.

(21) Указателят на стека в потребителски/системен режим се инициализира последен. Всички последващи кодове за инициализация се изпълняват в системен режим.

(22) Функцията на библиотеката " __libc_init_array() " извиква всички статични конструктори на C++ (вижте третата част на статията). Посочената функция се извиква от инструкцията "BX", която позволява ядрото да бъде превключено в състояние THUMB. В C тази функция не прави нищо.

(23) Функцията " main() " се извиква от инструкцията " BX ", която позволява на ядрото да бъде превключено в състояние THUMB.

(24) В голите проекти функцията " main() " никога не се връща, защото няма операционна система. Ако „ main() “ се върне, ще бъде хвърлено изключение „ Софтуерно прекъсване “, в което потребителят може да посочи как да се справи с такава ситуация.

2.2 Инициализация на ниско ниво

Инициализацията на ниско ниво, извършвана от функцията " low_level_init() " винаги е силно зависима от конкретния модел процесор и спецификата на процедурата за реорганизация на паметта. По-рано беше казано, че функцията "low_level_init()" може да бъде написана на "C" илиC++, но трябва да бъде компилиран за набора от инструкции на ARM, не може да инициализира секции " .data " и " .bss " или да извиква статични конструктори на C++.