Что такое cpu инициализация памяти
Ключевые слова: пользовательское пространство для космического дивизии, узел / зона / до, memblock, PGD / PUD / PMD / PTE, Nownmem / Highmem, ZONE_DMA / ZONE_NOMMAL / ZONE_HIGHMEM, Watermark, Migrate_Types.
Инициализация физической памяти основана на инициализации ядра Linux, в то время как управление памятью также является основой многих других функций. В сочетании с различными модулями в ядре вместе.
Перед выполнением инициализации вы знаете, что структура управления памятью Linux помогает управлять возможном изображением для управления памятью.
Во-первых, вам нужно знать, как разделяется все пространство пользователя и ядра (3: 1, 2: 2), а затем инициализируйте из иерархии узла-> зоны-> страницы> до тех пор, пока память не достигнет доступного состояния.
Отношения между узлами, зонами и страницами, «ULVMM». Рисунок 2.1 вводит, хотя слой ZONE_MEM_MAP был заменен, но все же отражает отношения уровня дерева между ними.
Pg_data_t соответствует узлу, node_zones содержит другую зону; зона определяет per_cpu_pageet, страницу связывания и процессоры.
1. Пользовательское пространство и космическое отделение ядра
В 32-разрядном Linux виртуальное адресное пространство имеет в общей сложности 4 ГБ. Все виртуальное адресное пространство делится на пространство пользователя + ядро пространства, есть три типа:
В ARCH / ARM / OLLECT / ASM / MEMORY.h Видно, что Page_Offset - это водораздел в пространстве пользователя и пространстве ядра. Это также отправная точка пространства ядра.
2. Получить размер физической памяти
Все управление инициализацией и управлением памяти являются физическая память на основе физической памяти, поэтому они сначала получают физический адрес и размер физической памяти.
Получите физические свойства памяти через DTS, затем разрешите и добавьте его в подсистему Memblock.
Согласно DTS выше, в start_kernel -> setup_arch -> setup_machine_fdt -> ranseup_init_d_scan_nodes -> of_scan_flat_dt (обходные узлы) -> ранний_инит_dt_scan_memory (инициализировать один узел памяти).
Результатом заключается в том, что размер базового размера 0x60000000 0x40000000 от DTS.
Затем согласно анализу базы / размера, вызовите ранний_Ирч -> memblock_add -> memblock_add_range, чтобы быть разрешенным.Физическая памятьДобавить в подсистему Memblock.
memblock_addИспользуется для добавления области вmemblock.memoryСредний; используйте много мест на этапе инициализации (например, HA ARM_MEMBLOCK_INIT)memblock_reserveДобавить область вmemblock.reserved。
memblock_removeИспользуется, чтобы поставить регион изmemblock.memoryУдалять,memblock_freeОжидание региона отmemblock.reservedУдалять.
Адрес в этомФизический адрес(. Вся информация в МемблокеГлобальная переменнаяв.
вФаза запуска ядраИ есть также спрос на управление памятью, но в это времяПартнерская система не завершает инициализациюОтказ Использовать в раннем конце ядраМеханизм bootmemme.Как начальный этап ядраПамятный дистрибьютор。
Позже Memblock использовался в качестве дистрибьютора начальной фазовой памяти ядра для распределения и выпуска памяти.
CONFIG_NO_BOOTMEMИспользуется для решения того, использовать буксирную копию, VExpress, поэтому используйте Memblock в качестве дистрибьютора памяти на начальной фазе.
Потому что оба bootmemy и memblockAPI совместимТаким образом, у пользователя нет смысла. использоватьmemblockВремя скомпилироватьmm/nobootmem.cПозвоните в интерфейс распределителя в MEMBLOCK.C.
3. Картирование физической памяти
Поскольку Config_arm_LPAE не открывается, таблица Page Linux использует два слоя отображения. Следовательно, PGD-> PUD-> PMD-> PTE пропускается, а возвращаемое значение pmd_off_k на самом деле pgd_offset_k.
Prepare_Page_Table используется для опорожнения записи страницы, на самом деле, элемент страницы страницы трех сегментов, 0 ~ Modules_Vaddr, Modules_Vaddr ~ Page_offset, 0xef800000 ~ vmalloc_start.
True Создание таблицы страницы заключается в том, что в MAP_LOWMEM создаются два интервала отображения, один 0x60000000 ~ 0x60800000 (0xC0000000 ~ 0xC0800000) и интервал 2 0x60800000 ~ 0x8f800000 (0xc0800000 ~ 0xef800000).
Интервал 1: с разрешениями выполнения прочитанного и записи, в основном используются для хранения сегмента данных кода ядра, но и содержимого SWAPPER_PG_DIR.
Интервал 2: Прочитал и пишу, не допускается, является нормальной раздел памяти.
Видно, что два интервала виртуальные к физическому отображению адреса являются линейными отображениями, но есть специальные две страницы, которые присутствуют в конце.
Существует также часть карты памяти в DeviceMaps_init, отображение векторов:
Mt_high_vectors: виртуальный адрес -0xffffff0000 ~ 0xfff1000, соответствующий физический адрес составляет 0x8f7fe000 ~ 0x8f7ff000.
Mt_low_vectors: виртуальный адрес -0xffff2000 ~ 0xffff2000, соответствующий физический адрес 0x8f7ff000 ~ 0x8f800000.
void __init sanity_check_meminfo(void)
? ? ? ? ? Как эти страницы гарантируют не использовать другого? ? ? ? ?
4. Инициализация зоны
Управление памятью распределяет узел памяти для управления, определить тип зоны в Enum ZONE_TYPE.
Vexpress определяет два нормальных и высокосимальных и инициализация зоны, проводится в bootmem_init. Найдите цифровую память начните номер кадра MIN_LOW_PFN, завершите номер кадра MAX_LOW_PFN номер кадра MAX_PFN, а также нормальная область.
Отверстие между каждой зоной и зоной и зоной рассчитывается в Zone_Sizes_init, затем вызовите Free_area_init_node для создания узла трафика.
Вышеуказанная операция функции выглядит следующим образом:
Таким образом, Zone_normal соответствует физическому адресу 0x60000000 - 0x8f800000, Zone_Highmem соответствует физическому адресу 0x8F800000 - 0xa0000000.
Каждая зона будет рассчитать значение водного положения, когда система инициализирована: warm_min, wark_low, warm_high. Эти параметры будут использоваться, когда KSWAPD восстановлена память страницы.
Важным параметром расчетного уровня воды MIN_FREE_KBYTES сделан в init_per_zone_wmark_min:
Рассчитанное положение воды выполняется __setup_per_zone_wmarks:
Распечатайте каждую информацию о каждой зоне следующим образом:
5. Инициализация физической памяти
Распределение партнерских систем Управление простоями страницами основано на двух свойствах: размер страницы, страницы PAGE заказа; и типы миграции страницы.
Не во всяком аппарате организован такой алгоритм.
Зачастую чистую память нужно инициализировать ручками, записывая в нее нужный код.
Вообще, само понятие "инициализации" - это создание таких условий, после которых аппарат оживает и его можно настраивать дальше, его штатными средствами.
Пример из компьютеров: если стирается флеш-БИОС, машина умирает.
Чтобы такого не происходило (а особенно это неприятно, если микросхема на пайке, а не на кровати), сейчас делают нестираемую часть прошивки, в которой записана процедура восстановления БИОС с внешнего носителя (например, у Асусов сделано так).
Caша писал: |
И еще – что такое флэш-память?Я знаю такие: EEPROM – 24C04,93C46 и т.п. EPROM(ПЗУ) – 27C512,27C040 и т.п. Что из этого флэш?Что такое ROM и RAM?В общем я запутался в трех соснах. |
RAM - Random Access Memory, память с произвольной выборкой, ОЗУ (оперативное запоминающее устройство). Можно писать, можно читать.
Но обесточил - и она все забыла.
ROM - Read Only Memory, память только для чтения, ПЗУ (постоянное запоминающее устройство).
ПЗУ бывают масочные (т.е., нужный код зашивается в процессе изготовления кристалла), с прожигаемыми перемычками (программируются один раз), электрически программируемые с ультрафиолетовым стиранием (программируются несколько раз, EPROM), электрически перепрограммируемые (можно перепрограммировать, не вынимая из аппаратуры, EEPROM).
Масочные - например, контроллер клавиатуры компьютера.
Там стоит однокристальный универсальный контроллер, в который зашита прямо при изготовлении микросхемы программа, обеспечивающая функцию клавиатуры.
Память с прожигаемыми перемычками существует в основном в виде программируемых логических матриц (ПЛМ), сложных дешифраторов.
Память с УФ стиранием программируется нулями, а единицы записываются сразу во все ячейки, при освещении УФ лампой.
Вы включили компьютер, появляется текстовый экран загрузки на котором быстро мелькают цифры и буквы. Обычно, компьютер работает нормально, и Вы не обращаете внимание на них. Но это важная часть работы компьютера в процессе которой работают микропрограммы встроенные в BIOS . Но вот случилось что-то непонятное и все остановилось, на экран выводится код ошибки, а иногда вообще ничего не выводится - мигает курсор и все застыло в непонятном сне.
Как это работает
После включения компьютера в его оперативной памяти нет операционной системы. А без операционной системы, аппаратное обеспечение компьютера не может выполнять сложные действия, такие как, например, загрузку программы в память. Таким образом возникает парадокс, который кажется неразрешимым: для того, чтобы загрузить операционную систему в память, мы уже должны иметь операционную систему в памяти.
Решением данного парадокса является использование нескольких микропрограмм размещаемых в одной или нескольких микросхемах, BIOS (Basic Input/Output System). Процесс загрузки начинается с автоматического выполнения процессором команд, расположенных в постоянной (или перезаписываемой) памяти ( EEPROM или Flash ROM ), начиная с заданного адреса. Эти микропрограммы не обладает всей функциональностью операционной системы, но её (функциональности) достаточно для того, чтобы чтобы выполнить последовательную загрузку других программ, которые выполняются друг за другом до тех пор, пока последняя из них не загрузит операционную систему.
Последовательность основных блоков функций BIOS в PC -совместимых компьютерах:
1. - POST - самотестирование при включении питания процессора, памяти, набора микросхем системной логики, видеоадаптера, контроллера дисков, клавиатуры, мыши и других контроллеров и устройств;
2. - Setup BIOS ( программа установки параметров BIOS) - конфигурирование параметров системы. Она может быть запущена во время выполнения процедуры POST при нажатии определенной комбинации клавиш. Если она не была вызвана пользователем, загружаются параметры установленные и сохраненные в постоянной памяти в о время последнего конфигурирования Setup BIOS .
3. - Загрузчик операционной системы - подпрограмма, выполняющая поиск действующего основного загрузочного сектора на дисковом устройстве.
4. - BIOS - набор драйверов, предназначенных для взаимодействия операционной системы и аппаратного обеспечения при загрузке системы.
В процессе загрузки BIOS осуществляется, кроме перечисленного, подключение, отключение, установка режима работы контроллеров устройств системной платы в соответствии с настройками записанными в постоянную память.
Зачем это нужно?
- проверки исправности и поэтому готовности к работе аппаратного обеспечения системной платы;
- проверки готовности работы внешнего аппаратного обеспечения, в том числе его параметров и исправности, а так же соответствие его необходимому минимуму, который позволит управлять компьютером до и после загрузки;
- проверки возможности загрузки операционной системы.
В процессе его выполнения проверяется наличие загрузочных устройств которые должны быть проинициализировано до загрузки операционной системы.
К ним относятся:
- устройства ввода (клавиатура, мышь),
- базовое устройство вывода (дисплей),
- устройство, с которого будет произведена загрузка ОС — дисковод, жесткий диск, CD-ROM, флэш-диск, SCSI-устройство, сетевая карта (при загрузке по сети)
В случае жесткого диска, начальный загрузчик называется главной загрузочной записью (MBR) и часто не зависит от операционной системы. Обычно он ищет активный раздел жесткого диска, загружает загрузочный сектор данного раздела и передает ему управление. Этот загрузочный сектор, как правило, зависит от операционной системы. Он должен загрузить в память ядро операционной системы и передать ему управление.
Если активного раздела не существует, или загрузочный сектор активного раздела некорректен, MBR может загрузить резервный начальный загрузчик и передать управление ему. Резервный начальный загрузчик должен выбрать раздел (зачастую с помощью пользователя), загрузить его загрузочный сектор и передать ему управление.
Последовательность загрузки стандартного IBM-совместимого персонального компьютера
После включения персонального компьютера его процессор еще не начинает работу.
Первое устройство, которое запускается после нажатия кнопки включения компьютера, — блок питания. Если все питающие напряжения окажутся в наличии и будут соответствовать норме, на системную плату будет подан специальный сигнал Power Good, свидетельствующий об успешном тестировании блока питания и разрешающий запуск компонентов системной платы.
После этого чипсет формирует сигнал сброса центрального процессора, по которому очищаются регистры процессора, и он запускается.
Первая выполняемая команда расположена по адресу FFFF0h и принадлежит пространству адресов BIOS. Данная команда просто передает управление программе инициализации BIOS и выполняет следующую команду (микропрограмму BIOS ).
Программа инициализации BIOS с помощью программы POST проверяет, что все необходимые для работы BIOS и последующей загрузки основной операционной системы, устройства компьютера работают корректно и инициализирует их.
Таким образом, его работа — последовательно читать и выполнять команды из памяти.
Системная память сконфигурирована так, что первая команда, которую считает процессор после сброса, будет находиться в микросхеме BIOS.
Последовательно выбирая команды из BIOS, процессор начнет выполнять процедуру самотестирования, или POST.
Процедура самотестирования POST состоит из нескольких этапов.
- Первоначальная инициализация основных системных компонентов;
- Детектирование оперативной памяти, копирование кода BIOS в оперативную память и проверка контрольных сумм BIOS;
- Первоначальная настройка чипсета;
- Поиск и инициализация видеоадаптера. Современные видеоадаптеры имеют собственную BIOS, которую системная BIOS пытается обнаружить в специально отведенном сегменте адресов. В ходе инициализации видеоадаптера на экране появляется первое изображение, сформированное с помощью BIOS видео адаптера ;
- Проверка контрольной суммы CMOS и состояния батарейки. Если контрольная сумма CMOS ошибочна, будут загружены значения по умолчанию ;
- Тестирование процессора и оперативной памяти. Результаты тестирования обычно выводятся на экран ;
- Подключение клавиатуры, тестирование портов ввода/вывода и других устройств.
- Инициализация дисковых накопителей. Сведения об обнаруженных устройствах обычно выводятся на экран ;
- Распределение ресурсов между устройствами и вывод таблицы с обнаруженными устройствами и назначенными для них ресурсами;
- Поиск и инициализация устройств, имеющих собственную BIOS;
- Вызов программного прерывания BIOS INT 19h, который ищет загрузочный сектор на устройствах, указанных в списке загрузки.
В зависимости от конкретной версии BIOS порядок процедуры POST может немного раз отличаться, но приведенные выше основные этапы выполняются при загрузке любого компьютера.
Что такое POST-коды?
После включения питания компьютера, если исправны блок питания и основные узлы материнской платы (генератор тактовых частот, компоненты, отвечающие за работу системной шины и шины памяти), процессор начинает выполнение кода BIOS.
Если быть совсем точным, во многих современных чипсетах перед передачей команд процессору системным контроллером предварительно конфигурируется «умная» системная шина. Но это не меняет сути дела.
Основная задача BIOS на данном этапе — проверка исправности и инициализация основных аппаратных компонентов компьютера. Вначале конфигурируются внутренние регистры чипсета и процессора, проверяется целостность кода BIOS. Затем происходит определение типа и размера оперативной памяти, поиск и инициализация видеокарты (интегрированной в чипсет или внешней). Следом конфигурируются порты ввода-вывода, контроллер дисковода, IDE/SATA-контроллер и подключенные к нему накопители. И, наконец, осуществляется поиск и инициализация интегрированных на материнскую плату дополнительных контроллеров и установленных карт расширения. Всего получается около ста промежуточных шагов, после чего управление передается загрузчику BOOTStrap, отвечающему за старт операционной системы.
Каждый из шагов POST-тестов имеет свой уникальный номер, называемый POST-кодом. Перед началом выполнения очередной процедуры ее POST-код записывается в специальный порт, именуемый Manufacturing Test Port. При успешной инициализации устройства в Manufacturing Test Port записывается POST-код следующей процедуры и так далее, до полного прохождения всех тестов. Если сконфигурировать устройство не удалось, дальнейшее выполнение POST-тестов прекращается, а в Manufacturing Test Port остается POST-код процедуры, вызывавшей сбой. Прочитав его можно однозначно идентифицировать проблемное устройство.
Имейте в виду, после перезагрузки компьютера средствами операционной системы («мягкая» или «горячая» перезагрузка) или при выходе из энергосберегающего режима обычно выполняются не все шаги по тестированию и конфигурированию аппаратных компонентов, а только необходимый минимум — так быстрее. При поиске неисправности необходимо всегда выполнять «жесткую» («холодную») перезагрузку — клавишей RESET или отключением питания компьютера. Только так гарантируется, что все этапы инициализации будут выполнены в полном объеме.
Award BIOS 6.0: вариант полной загрузки
Данную таблицу можно использовать не только как список POST-кодов, но и как последовательность действий, которые выполняются при включении компьютера. Она содержит POST-коды, которые отображаются при полной процедуре POST.
Невыполнение или сбой выполнения любого шага в последовательности тестов приводит к остановке тестирования и выдаче POST - кода соответствующего данному шагу сбоя.
POST - коды других производителей можно найти на сайтах производителя Вашей системной платы или производителя DIOS или в Internet .
В мастерских или у занимающихся ремонтом специалистов контроль выполнения микропрограмм BIOS осуществляется с помощью специальной карты расширения. Она вставляется в свободный слот (большинство современных моделей рассчитано на шину PCI) и по мере загрузки отображает на своем индикаторе код выполняемой в текущий момент процедуры.
Примером может быть Post карта PCI BM9222 .
Однако POST-карта это не широко распространенное средство. Скорее, это инструмент профессионального ремонтника компьютеров. Осознавая данный факт, производители материнских плат стали оснащать модели, рассчитанные на энтузиастов экспериментирующих с настройками и разгоном компьютера, встроенными индикаторами POST-кодов.
Примером может быть системная плата ECS H67H2-M , или модели X58 Extreme3, P55 Deluxe3 и 890GX Extreme3 .
Встречается и более дешевое решение — во время начальной инициализации компонентов POST-коды могут отображаться на экране наряду с другой служебной информацией. Правда у этого решения есть существенный недостаток: если проблема связана с видеокартой, вы, скорее всего, ничего не увидите.
Но для этого необходимо, чтобы в корпусе ПК имел системный динамик и он был подключен к системной платы.
Звуковые сигналы особенно ценны на начальном этапе, когда видеокарта еще не проинициализирована и, как следствие, не в состоянии отобразить что-либо на экране. Уникальная комбинация длинных и коротких сигналов укажет на проблемный компонент.
Процедура Setup
Для этого необходимо нажать определенную клавишу или сочетание клавиш.
Обычно на экранной заставке при тестировании отображается надпись типа «Press DEL to enter Setup» — это означает, что для входа в BIOS Setup необходимо нажать клавишу DEL. Узнать, за которой клавишей закреплен вход в BIOS, можно из инструкции к материнской плате. Если инструкции нет, а экранная заставка не отображает подсказки, можно опробовать наиболее известные варианты комбинаций:
Delete
Esc
Ctrl + Shift + S или Ctrl + Alt + S
Ctrl + Alt + Esc
Ctrl + Alt + Delete
Безопасная работа с BIOS Setup
Работа с BIOS Setup связана с определенным риском, поскольку при неправильном или неосторожном изменении параметра система может функционировать нестабильно либо не функционировать вообще. Есть несколько простых советов, которые позволяют свести возможный риск к минимуму:
- Экспериментировать с настройками BIOS Setup лучше всего на новом не заполненном информацией компьютере;
- Старайтесь вообще не экспериментировать с BIOS на компьютерах, обрабатывающих или хранящих важную или объемную информацию. Перед настройкой системы с помощью BIOS позаботьтесь о резервном копировании важных данных. Главное в таких компьютерах — стабильность. Подвисший разогнанный компьютер через несколько часов обработки видео — это потеря времени, электроэнергии и результата работы. Неразогнанный справится с данной задачей гораздо эффективнее и сохранит ваши нервы;
- Прежде, чем изменить важные параметры, всегда фиксируйте выставленное и измененное значение. Это позволит вам в случае нестабильной работы системы вернуть ее в рабочее состояние;
- Не изменяйте значения параметров, которые вам неизвестны. Уточните их значение либо в инструкции к материнской плате, либо в сети Internet на ресурсе разработчика платы;
- Не редактируйте за раз несколько важных не связанных между собой параметров. При нестабильной работе системы гораздо сложнее определить, какой параметр вызвал нестабильную работу;
- Не разгоняйте компьютер без соответствующей изучения работы разгоняемой системы и подготовки;
- Не используйте раздел Hard Disk Utility, который предназначен для низкоуровневого форматирования устаревших моделей жестких дисков и встречается в старых версиях BIOS, т.к. может вывести из строя современный жесткий диск;
- Если после выставления параметров и выхода из BIOS компьютер перестает запускаться вообще, вернуть систему в рабочее состояние можно несколькими способами:
- Если после перезагрузки компьютера возможно войти в BIOS Setup, нужно установить прежние значения отредактированных параметров. Некоторые версии BIOS сами осуществляют откат изменений за последнюю сессию.
- Если сделанные изменения неизвестны, то лучше воспользоваться параметрами по умолчанию, использовав команду Load Fail-Safe Defaults. После этого придется настраивать систему на оптимальную работу.
- Если компьютер вовсе не запускается из-за неправильных настроек BIOS, то в таком случае необходимо обнулить содержимое CMOS. При этом все значения включая дату/время будут изменены. Для этого сбросить неправильные установки, для этого просто переместить перемычку Flash Recovery ( IBM ) или джампер Clearing CMOS в положение "очистка CMOS ". В последнем случае нужно просто замкнуть перемычкой на несколько минут контакты соответствующего джампера.
- В случае неудачных результатов настройки Setup BIOS , необходимо после сброса неудачной конфигурации с помощью джампера в процедуре Setup BIOS продублировать возвращение загрузку значений BIOS по умолчанию с помощью команды "Load Fail-Safe Defaults ". Ваша система вернется в исходное состояние.
В Award BIOS 6.0 это строка меню Setup BIOS - " Load Optimized Defaults " или "Load Fail-Safe Defaults " в этом случае загружается исходная настройки Setup BIOS .
Собственный BIOS имеется и на других платах устанавливаемых в компьютер, например:
- видеоадаптерах;
- SCSI адаптеры;
- сетевые адаптеры и других.
Сборка А. Сорокин
2. Модернизация и ремонт ПК, 15-е издание, Пер. с англ, М, изд. дом "Вильямс" 2005
Прошлый мой топик был полностью теоретическим, этот же будет практическим. Практика будет довольно хардкорной (я сам занялся этим вопросом только через год работы с ARMами) — инициализация процессора и памяти. Иными словами: что нужно сделать с процессором, чтобы попасть в функцию main() . Первая часть статьи посвящена инструментам сборки и отладки. Вторая — обработке векторов исключений, третья — инициализации стеков и памяти.
Но сначала хочу сделать одно уточнение. Многие почему-то считают, что ARM — это обязательно монстр со внешней памятью, кучей обвязки, работающий на частоте не менее 600Mhz, и т.д. Это правда лишь отчасти (если говорить об ARM9 и более поздних семействах). Тот чип, с которым я обычно работаю (AT91SAM7X512), не намного сложнее знакомых многим AVR. Ему для работы нужны только кварц и питание (можно и без кварца, но тогда будет совсем грустно). Всё. Но возможностей у него, конечно, больше, много больше, чем у AVR. Но об этом позже. Сегодняшняя статья никак не будет привязана к конкретному железу.
Компиляторы, линковщики, дебаггеры
Обработчики исключений
Ладно, будем считать, что компилятор поставили и настроили. Давайте теперь что-нибудь запустим. Начнем с самого начала: что происходит, когда процессор сбрасывается (например, после того, как включили питание и напряжение устаканилось). Тут всё просто: процессор начинает исполнять программу с адреса 0x0. Казалось бы — можно разместить с этого адреса код инициализации и работать себе. Но не всё так просто. Дело в том, что в начальных адресах хранятся вектора обработчиков исключений.
Например, если возникнет прерывание, то обработку его процессор начнет с адреса 0x18, а исключение «неизвестная инструкция» будет обрабатываться с адреса 0x04. В общем, первые 28 байт отведены для таблицы обработчиков исключительных ситуаций (reset — это тоже исключительная ситуация).
На рисунке это видно более наглядно. С рисунка же видно, что на каждый обработчик отведено 4 байта, или одна команда процессора. (В режиме ARM. Все обработчики вызываются в этом режиме инструкций.)
Соответсвенно, первым, что мы должны сделать, — это написать обработчики исключений и правильно их разместить. Этим и займемся:
ldr pc, ResetHandlerAddr
ldr pc, UndefHandlerAddr
ldr pc, SWIHandlerAddr
ldr pc, PrefetchAbtHandlerAddr
ldr pc, DataAbtHandlerAddr
nop
ldr pc, IRQHandlerAddr
ldr pc, FIQHandlerAddr
Что делает этот код? Это команды загрузки в регистр pc адресов настоящих обработчиков. Такой себе безусловный переход. Дальше по коду идут переменные, хранящие эти самые адреса:ResetHandlerAddr: .word ResetHandler
UndefHandlerAddr: .word UndefHandler
SWIHandlerAddr: .word SWIHandler
PrefetchAbtHandlerAddr: .word PrefetchAbtHandler
DataAbtHandlerAddr: .word DataAbtHandler
IRQHandlerAddr: .word IRQHandler
FIQHandlerAddr: .word FIQHandler
Тут можно было применить несколько фокусов, ускоряющих обработку прерываний. Например, как видно, обработчик FIQ находится самым последним в списке, так что обработку этого прерывания можно было начать прямо на месте.
Также можно было использовать регистры AIC (advanced interrupt controller) для прямого перехода на обработчик возникшего прерывания. Но пока не будем усложнять себе жизнь. Пока нам важна только обработка Reset'а.
Так что давайте напишем сами обработчики максимально простыми. Они будут вешать процессор (бесконечно выполняя команду безусловного перехода на самих себя). Всё равно мы не знаем пока, как обрабатывать исключения, поэтому повисший процессор — вполне допустимо.
UndefHandler: B UndefHandler
SWIHandler: B SWIHandler
PrefetchAbtHandler: B PrefetchAbtHandler
DataAbtHandler: B DataAbtHandler
IRQHandler: B IRQHandler
FIQHandler: B FIQHandler
B — это команда безусловного перехода (Branch)
Следующее, что нам нужно сделать, — это настроить указатели стека sp для каждого из режимов работы. Таким образом, если возникнут исключения, — у обработчика уже будет свой стек. Только вначале опишем размеры всех стеков.
.EQU IRQ_STACK_SIZE, 0x100
.EQU FIQ_STACK_SIZE, 0x100
.EQU ABT_STACK_SIZE, 0x100
.EQU UND_STACK_SIZE, 0x100
.EQU SVC_STACK_SIZE, 0x100
Чтобы не мучаться долго, выделим по 256 байт на стек для каждого режима. На самом деле для большинства из этих режимов — это много. Хотя всё зависит от обработчиков. Как видно, тут описаны размеры для 5 из 6 режимов. Остальная память будет использоваться совместно кучей и стеком шестого (user mode) режима.
Теперь опишем константы для облегчения перехода в разные режимы. За текущий режим отвечает регистр CPSR. Он же выполняет и роль статусного регистра.
.EQU ARM_MODE_FIQ, 0x11
.EQU ARM_MODE_IRQ, 0x12
.EQU ARM_MODE_SVC, 0x13
.EQU ARM_MODE_ABT, 0x17
.EQU ARM_MODE_UND, 0x1B
.EQU ARM_MODE_USR, 0x10.EQU I_BIT, 0x80
.EQU F_BIT, 0x40
Константы I_BIT и F_BIT — это биты, которые запрещают простые и быстрые прерывания, соответственно. Теперь у нас всё готово для инициализации стеков. Делается это просто: загружаем в регистр r0 указатель на вершину стека, потом переходим в нужный режим, записываем в sp значение r0 , затем уменьшаем r0 на размер стека и повторяем.
.RAM_TOP:
.word __TOP_STACK
ResetHandler:
ldr sp, .RAM_TOPИнициализация памяти
Теперь мы находимся в непривилегилированном режиме со включенными прерываниями и настроенным стеком. Кстати, выйти из этого режима просто так нельзя. Только вызвав исключение. Но об этом в следующей статье.
До перехода в функцию main() осталось совсем чуть-чуть. Надо только перенести кое-какие данные в RAM и обнулить память, которая находится в сегменте .BSS. Это та память, где хранятся глобальные переменные. Дело в том, что стандарт языка C обещает, что глобальные переменные будут обнулены в начале работы, а ARM нам этого не гарантирует. Поэтому обнулим сегмент вручную:
Константы __bss_end__ & __bss_start__ любезно предоставлены нам линковщиком.
Кстати, тут вы можете наблюдать использование условных инструкций (с суффиксом O). Они будут исполняться, пока R1!=R2.
Также надо перенести из ROM в RAM предварительно инициализированные переменные (те, которые int x=42 ).
Если будем писать на C++, то нужно ещё вызвать конструкторы глобальных объектов:Ну в общем ивсё. Вызываем main() :
Поздравляю, теперь мы в сишной функции void main(void) . Можно заняться инициализацией периферии. Дело в том, что до этого мы инициализировали только програмную среду. Поэтому процессор сейчас работает на самой низкой частоте из всех возможных, вся периферия отключена. Тут не разгуляешься :)
Но инициализация периферии — это штука, которая зависит от конкретной железяки, а цель этой статьи — рассказать, как запускать абстрактный ARM.
И ещё несколько нюансов: этот код напрямую скомпилировать и запустить не получится, потому что здесь не описаны секции, где он располагается. Также я не приводил скрипты линковщика (эти скрипты описывают размещение секций кода и данных в памяти и в образе прошивки).
Но в интернете полно готовых примеров для запуска той или иной железки. Со скриптами, мейкфайлами и всем-всем-всем. Ищите на сайтах производителей :)Следующая статья, судя по всему, будет опять посвящена теории, на этот раз — описанию режимов процессора и исключительных ситуаций.
У любой операционной системы есть определенный механизм запуска. Принцип работы этого механизма у каждой системы свой. Обычно говорят, что система загружается (англ. boot), это сокращение от «bootstrap», которое отсылает к выражению «pull oneself over a fence by one’s bootstraps» (перебраться через ограду, потянув себя за ремешки на обуви), что примерно описывает, как система самостоятельно переходит из состояния, в котором память заполнена пустотой (прим. переводчика: если совсем точно, то мусором) к стабильному выполнению программ. Традиционно в память загружается небольшая часть программы, она может храниться в ПЗУ. В прошлом она могла вводиться при помощи переключателей на передней панели компьютера. Этот начальный загрузчик считывал более сложную программу загрузки, которая уже загружала операционную систему. Сегодня настольный компьютер загружается следующим образом: код в BIOS ищет устройства (жесткие диски, CD-ROM, USB-флешки), с которых можно загрузиться, после чего загружается операционная система.
ОС для встраиваемых систем также может инициализироваться подобным образом. И в самом деле, встраиваемые операционные системы, разработанные на основе настольных операционных систем, так и загружаются. Но в большинстве «классических» ОСРВ используется гораздо более простой (а, следовательно, более быстрый) способ.
Операционная система – часть программного обеспечения. Если это программное обеспечение уже находится в памяти (например, в том или ином виде ПЗУ), то требуется всего лишь сделать так, чтобы последовательность команд ЦП после сброса заканчивалась выполнением кода инициализации ОС. Именно так работают большинство ОСРВ, в том числе и Nucleus SE (примечание переводчика: и к нашей ОСРВ МАКС это тоже относится).Большинство средств разработки встраиваемого программного обеспечения включают в себя необходимый код запуска для обработки сброса ЦП и передачи управления во входную точку (Entry Point) в функции main(). Распространяемый код Nucleus SE не имеет дела с этим процессом, так как он должен быть максимально портируемым. Вместо этого, он содержит функцию main(), которая берет контроль над ЦП и инициализирует и запускает ОС. Эта функция будет подробно рассмотрена ниже.
Функция main()
Ниже приведен полный код функции main() Nucleus SE:
Последовательность операций довольно проста:- Сначала вызывается функция NUSE_Init(). Она инициализирует все структуры данных Nucleus SE и будет подробно описана ниже.
- Затем пользователь может вставить любой код инициализации приложения, который будет выполнен до запуска планировщика задач. Подробнее о том, чего можно достичь при помощи этого кода, см. далее в этой статье.
- Наконец, запускается планировщик Nucleus SE (NUSE_Scheduler()). Это также будет подробно рассмотрено далее в этой статье.
Инициализация пулов разделов
Ниже приведен полный код функции NUSE_Init_Partition_Pool():
«Использованному» счетчику пула разделов (NUSE_Partition_Pool__Partition_Used[pool]) присваивается нулевое значение.Если активирована блокировка задач, счетчику заблокированных задач пулов разделов (NUSE_Partition_Pool_Blocking_Count[pool]) присваивается нулевое значение.
Инициализация почтовых ящиков
Ниже приведен полный код NUSE_Init_Mailbox():
Хранилищу данных почтовых ящиков (NUSE_Mailbox_Data[mailbox]) присваивается нулевое значение, и состояние (NUSE_Mailbox_Status[mailbox]) становится «неиспользуемым» (т.е. нулевым).Если активирована блокировка задач, счетчику заблокированных задач почтовых ящиков (NUSE_Mailbox_Blocking_Count[mailbox]) присваивается нулевое значение.
Инициализация каналов
Ниже приведен полный код функции NUSE_Init_Pipe():
Указателям на начало и конец канала (на самом деле, это индексы – NUSE_Pipe_Head[pipe] и NUSE_Pipe_Tail[pipe]) присваивается значение, указывающее на начало области данных канала (т.е. они принимают нулевое значение). Счетчику элементов в канале (NUSE_Pipe_Items[pipe]) также присваивается нулевое значение.Если активирована блокировка задач, счетчику заблокированных задач канала (NUSE_Pipe_Blocking_Count[pipe]) присваивается нулевое значение.
Запуск планировщика
- Присваивание глобальной переменной NUSE_Task_State значения NUSE_TASK_CONTEXT.
- Выбор индекса первой запускаемой задачи. Если поддержка начальной задачи активирована, выполняется поиск первой готовой задачи, в противном случае — используется нулевое значение.
- Вызывается планировщик – NUSE_Scheduler().
В следующей статье будет рассматриваться диагностика и проверка ошибок.
Инициализация групп событий
Ниже приведен полный код функции NUSE_Init_Event_Group():
Флаги группы событий сбрасываются, т.е. NUSE_Event_Group_Data[group] присваивается нулевое значение.Если активирована блокировка задач, счетчику заблокированных задач группы флагов событий (NUSE_Event_Group_Blocking_Count[group]) присваивается нулевое значение.
Инициализация очередей
Ниже приведен полный код функции NUSE_Init_Queue():
Указателям на начало и конец очереди (на самом деле, это индексы NUSE_Queue_Head[queue] и NUSE_Queue_Tail[queue]) присваиваются значения, указывающие на начало области данных очередей (т.е. они принимают нулевое значение). Счетчику элементов в очереди (NUSE_Queue_Items[queue]) также присваивается нулевое значение.Если активирована блокировка задач, счетчику заблокированных задач очередей (NUSE_Queue_Blocking_Count[queue]) присваивается нулевое значение.
Инициализация семафоров
Ниже приведен полный код функции NUSE_Init_Semaphore():
Счетчик семафоров (NUSE_Semaphore_Counter[semaphore]) инициализируется значением, заданным пользователем (NUSE_Semaphore_Initial_Value[semaphore]).Если активирована блокировка задач, счетчику заблокированных задач семафора (NUSE_Semaphore_Blocking_Count[semaphore]) присваивается нулевое значение.
Инициализация памяти
Все переменные ROM инициализируются статически, что логично. Переменные RAM не инициализируются статически (так как это работает только с определенными наборами инструментов, которые настроены на автоматическое копирование из ПЗУ в ОЗУ); явный код инициализации включен в приложение и будет подробно описан ниже.
Nucleus SE не хранит «константных» данных в ОЗУ, которая обычно в дефиците у небольших систем. Вместо использования сложных структур данных для описания объектов ядра используются наборы таблиц (массивов), которые без проблем размещаются в ПЗУ или ОЗУ, в зависимости от необходимости.
Инициализация памяти
Все переменные ROM инициализируются статически, что логично. Переменные RAM не инициализируются статически (так как это работает только с определенными наборами инструментов, которые настроены на автоматическое копирование из ПЗУ в ОЗУ); явный код инициализации включен в приложение и будет подробно описан ниже.
Nucleus SE не хранит «константных» данных в ОЗУ, которая обычно в дефиците у небольших систем. Вместо использования сложных структур данных для описания объектов ядра используются наборы таблиц (массивов), которые без проблем размещаются в ПЗУ или ОЗУ, в зависимости от необходимости.
Инициализация задач
Если планировщик Run to Completion не был сконфигурирован, инициализируется контекстный блок для задачи NUSE_Task_Context[task][]. Большинству элементов не присваиваются значения, так как они представляют общие машинные регистры, которые должны иметь промежуточное значение при запуске задачи. В примере (Freescale ColdFire) реализации Nucleus SE (но и у других процессоров механизм будет аналогичным) последние три записи заданы явным образом:
Если активирована приостановка задачи (т.е. служебный вызов API NUSE_Task_Sleep()), счетчику таймаута задачи (NUSE_Task_Timeout_Counter[task]) присваивается нулевое значение.
Если активировано состояние ожидания задачи (task suspend), статус задачи (NUSE_Task_Status[task]) инициализируется. Это начальное значение задается пользователем (в NUSE_Task_Initial_State[task]), если активирована поддержка начального состояния задачи. В противном случае состоянию присваивается NUSE_READY.
Если активирован счетчик планировок, счетчику задачи (NUSE_Task_Schedule_Count[task]) присваивается нулевое значение.
Инициализация таймеров
Ниже приведен полный код NUSE_Init_Timer();
Состояние таймера (NUSE_Timer_Status[timer]) устанавливается в значение «неиспользуемое», т.е. FALSE.Значение обратного отсчета (NUSE_Timer_Value[timer]) инициализируется значением, заданным пользователем (NUSE_Timer_Initial_Time[timer]).
Счетчику завершений (NUSE_Timer_Expirations_Counter[timer]) присваивается нулевое значение.
Функция NUSE_Init()
Эта функция инициализирует все переменные ядра и структуры данных Nucleus SE.
- NUSE_Task_Active – индекс активной задачи, инициализируется нулевым значением; позднее это может поменять планировщик.
- NUSE_Task_State – инициализируется значением NUSE_STARTUP_CONTEXT, которое ограничивает функционал API для любого последующего кода инициализации приложения.
- Если активирована поддержка системного времени, NUSE_Tick_Clock присваивается нулевое значение.
- Если активирован планировщик Time Slice, NUSE_Time_Slice_Ticks присваивается сконфигурированное значение NUSE_TIME_SLICE_TICKS.
- NUSE_Init_Task() вызывается для инициализации структур данных каждой задачи. Этот вызов пропускается, только если используется планировщик Run to Completion, а сигналы, приостановка задач и счетчик планировок не сконфигурированы (так как эта комбинация функций приведет отсутствию структур данных задач в ОЗУ, следовательно, инициализация не будет осуществлена).
- NUSE_Init_Partition_Pool() вызывается для инициализации каждого объекта пула разделов. Эти вызовы пропускаются, если нет сконфигурированных пулов разделов.
- NUSE_Init_Mailbox() вызывается для инициализации каждого объекта почтовых ящиков. Эти вызовы пропускаются, если нет сконфигурированных почтовых ящиков.
- NUSE_Init_Queue() вызывается для инициализации каждого объекта очереди. Эти вызовы пропускаются, если нет сконфигурированных очередей.
- NUSE_Init_Pipe() вызывается для инициализации каждого объекта канала. Эти вызовы пропускаются, если нет сконфигурированных каналов.
- NUSE_Init_Semaphore() вызывается для инициализации каждого объекта семафоров. Эти вызовы пропускаются, если нет сконфигурированных семафоров.
- NUSE_Init_Event_Group() вызывается для инициализации каждого объекта групп событий. Эти вызовы пропускаются, если нет сконфигурированных групп событий.
- NUSE_Init_Timer() вызывается для инициализации каждого объекта таймеров. Эти вызовы пропускаются, если нет сконфигурированных таймеров.
Инициализация кода приложения
После того как структуры данных Nucleus SE были инициализированы, появляется возможность выполнить код, отвечающий за инициализацию приложения до начала выполнения задачи. Это возможность может пригодиться для следующих задач:
У вызовов API есть ограничение: все действия, которые обычно приводят к активации планировщика, запрещены (например, приостановка/блокировка задач). Глобальной переменной NUSE_Task_State было присвоено значение NUSE_STARTUP_CONTEXT, чтобы отметить это ограничение.
Читайте также: