Что такое cpu инициализация памяти

Обновлено: 07.07.2024

Инициализация памяти
Мало кто подозревает о том, что при включении в оперативке далеко не всегда все байты равны 0xFF. Они могут, но не обязаны. Равно как и регистры РОН не всегда равны нулю при запуске. Обычно да, все обнулено, но я несколько раз сталкивался со случаями когда после перезапуска и/или включения-выключения питания, микроконтроллер начинал творить не пойми что. Особнно часто возникает когда питание выключаешь, а потом, спустя некоторое время, пара минут, не больше, включаешь. А всему виной остаточные значения в регистрах.

Итак, возьмите себе за правило после каждого включения, в разделе инициализации, еще даже до инициализации стека, делать зануление памяти и очистку всех регистров. Разумеется делается это все в цикле. Вот примерный вариант кода:

RAM_Flush: LDI ZL,Low(SRAM_START) ; Адрес начала ОЗУ в индекс LDI ZH,High(SRAM_START) CLR R16 ; Очищаем R16 Flush: ST Z+,R16 ; Сохраняем 0 в ячейку памяти CPI ZH,High(RAMEND+1) ; Достигли конца оперативки? BRNE Flush ; Нет? Крутимся дальше! CPI ZL,Low(RAMEND+1) ; А младший байт достиг конца? BRNE Flush CLR ZL ; Очищаем индекс CLR ZH

Поскольку адрес оперативки у нас двубайтный, то мы вначале смотрим, чтобы старший байт совпал с концом, а потом добиваем оставшиеся 255 байт в младшем байте адреса.
Далее убиваем все регистры от первого до последнего. Все, контроллер готов к работе.

LDI ZL, 30 ; Адрес самого старшего регистра CLR ZH ; А тут у нас будет ноль DEC ZL ; Уменьшая адрес ST Z, ZH ; Записываем в регистр 0 BRNE PC-2 ; Пока не перебрали все не успокоились

За процедурку зануления регистров спасибо Testicq

Либо значения сразу же инициализируются нужными величинами. Но, обычно, я от нуля всегда пляшу. Поэтому зануляю все.

Спасибо. Вы потрясающие! Всего за месяц мы собрали нужную сумму в 500000 на хоккейную коробку для детского дома Аистенок. Из которых 125000+ было от вас, читателей EasyElectronics. Были даже переводы на 25000+ и просто поток платежей на 251 рубль. Это невероятно круто. Сейчас идет заключение договора и подготовка к строительству!

А я встрял на три года, как минимум, ежемесячной пахоты над статьями :)))))))))))) Спасибо вам за такой мощный пинок.

58 thoughts on “AVR. Учебный курс. Стартовая инициализация”

Такой метод инициализации катит только если у тебя переменные инициируются нулями. Но такое не всгеда бывает.

Как вариант, хранить во флеше слепок начальных значений переменных и копировать их. Или просто не забывать инициировать их по мере надобности.

А чистить регистры и место для стека считаю пустой тратой тактов =) Регистры ты так или иначе будешь инициировать по ходу дела, а стек у тебя изначально нулевой и при росте перекроет любые левые значения.

Ну так а что мешает потом забить переменные нужынми значениями. Все проще чем слепок памяти держать.

Не забывай, что я пишу на асме у меня часто регистры юзаются как статичные переменные. Т.е. очень часто какой либо регистр юзается только для одной конкретной цели.

Знаю. Я тоже пишу на асме. Но это тебя не освобождает от инициализации переменных =)

Тут фишка такая, когда ты используешь умолчальные инициализации, ты тереяшь в понятности кода.

Задание начальных значений переменным должно сидеть в подсознании, на чем бы не писал. В этом отношении хороши компиляторы семейства Паскалей. Например, в Дельфи компилятор постоянно предупреждает, если какая-то переменная может перед использованием иметь неопределенное значение, (например, значение задается в куске кода или цикле, который может в некоторых случаях не выполняться. Си в этом отношении не так строг, много чего пропускает, что потом сносит башку при отладке.
Вчера я начал писать программку уже для контроллера бамперов своего робота, и в самом начале, в процедуре инициализации, сразу поставил кусок (В ходовом контроллере тоже):

if PCON.1 = 0 then // Если был сброс по питанию, Очищаем память!
begin
FSR := $20; //Начало ОЗУ
while FSR < $40 do // Первые 32 байта.
begin
INDF := 0; // Очищаем память!
Inc(FSR);
end;
PCON.1 := 1;
end;
Т.Е. по холодному старту очищаю область памяти, используемую пока в программе, далее в программе тем переменным, которые должны быть отличны от нуля, присваиваются конкретные значения. Сначала хотел сделать этот кусок на асме, но потом посмотрел, как его расписал компилятор, и решил оставить на Паскале. Вот этот кусок после компиляции:

;ROBO_SWG_2.ppas,81 :: begin
;ROBO_SWG_2.ppas,82 :: if PCON.1 = 0 then // Если был сброс по питанию, Очищаем память!
$00FA $1303 BCF STATUS, RP1
$00FB $1683 BSF STATUS, RP0
$00FC $080E MOVF PCON, 0
$00FD $00F1 MOVWF STACK_1
$00FE $3000 MOVLW 0
$00FF $18F1 BTFSC STACK_1, 1
$0100 $3001 MOVLW 1
$0101 $00F1 MOVWF STACK_1
$0102 $0871 MOVF STACK_1, 0
$0103 $3A00 XORLW 0
$0104 $1D03 BTFSS STATUS, Z
$0105 $2910 GOTO ROBO_SWG_2_L_9
$0106 $ ROBO_SWG_2_L_8:
;ROBO_SWG_2.ppas,84 :: FSR := $20; //Начало ОЗУ
$0106 $3020 MOVLW 32
$0107 $0084 MOVWF FSR
;ROBO_SWG_2.ppas,85 :: while FSR < $40 do // Первые 32 байта.
$0108 $ ROBO_SWG_2_L_12:
$0108 $3040 MOVLW 64
$0109 $0204 SUBWF FSR, 0
$010A $1803 BTFSC STATUS, C
$010B $290F GOTO ROBO_SWG_2_L_13
;ROBO_SWG_2.ppas,87 :: INDF := 0; // Очищаем память!
$010C $0180 CLRF INDF, 1
;ROBO_SWG_2.ppas,88 :: Inc(FSR);
$010D $0A84 INCF FSR, 1
;ROBO_SWG_2.ppas,89 :: end;
$010E $2908 GOTO ROBO_SWG_2_L_12
$010F $ ROBO_SWG_2_L_13:
;ROBO_SWG_2.ppas,90 :: PCON.1 := 1;
$010F $ ROBO_SWG_2_L_16:
$010F $148E BSF PCON, 1
$0110 $ ROBO_SWG_2_L_17:
;ROBO_SWG_2.ppas,91 :: end;

Всего 23 байта. Можно сделать меньше, но зачем?
При других видах сброса (например, по сторожевому таймеру) память пока не очищаю, (вдруг какие-то данные понадобятся), только задаю значения основным переменным.

вообщем, инициализация тоже имеет в себе тонкости.

А разьве прерывание таймера не выполнится после выхода из прерывания INT0?

Выполнится, но мне надо было прерывание таймера внутри обработчика INT0

Слабо представляю зачем это нужно. Сколько по времени у тебя выполнялся обработчик INT0?

На обработчик INT0 у меня повешен альтернативный режим работы девайса. Так что он перехватывает на себя управление и держит его до тех пор пока не отработает всю логику.


Не во всяком аппарате организован такой алгоритм.
Зачастую чистую память нужно инициализировать ручками, записывая в нее нужный код.
Вообще, само понятие "инициализации" - это создание таких условий, после которых аппарат оживает и его можно настраивать дальше, его штатными средствами.

Пример из компьютеров: если стирается флеш-БИОС, машина умирает.
Чтобы такого не происходило (а особенно это неприятно, если микросхема на пайке, а не на кровати), сейчас делают нестираемую часть прошивки, в которой записана процедура восстановления БИОС с внешнего носителя (например, у Асусов сделано так).


Caша писал:

И еще – что такое флэш-память?Я знаю такие:
EEPROM – 24C04,93C46 и т.п.
EPROM(ПЗУ) – 27C512,27C040 и т.п.
Что из этого флэш?Что такое ROM и RAM?В общем я запутался в трех соснах.

RAM - Random Access Memory, память с произвольной выборкой, ОЗУ (оперативное запоминающее устройство). Можно писать, можно читать.
Но обесточил - и она все забыла.

ROM - Read Only Memory, память только для чтения, ПЗУ (постоянное запоминающее устройство).

ПЗУ бывают масочные (т.е., нужный код зашивается в процессе изготовления кристалла), с прожигаемыми перемычками (программируются один раз), электрически программируемые с ультрафиолетовым стиранием (программируются несколько раз, EPROM), электрически перепрограммируемые (можно перепрограммировать, не вынимая из аппаратуры, EEPROM).

Масочные - например, контроллер клавиатуры компьютера.
Там стоит однокристальный универсальный контроллер, в который зашита прямо при изготовлении микросхемы программа, обеспечивающая функцию клавиатуры.

Память с прожигаемыми перемычками существует в основном в виде программируемых логических матриц (ПЛМ), сложных дешифраторов.

Память с УФ стиранием программируется нулями, а единицы записываются сразу во все ячейки, при освещении УФ лампой.

Что происходит при запуске сервера, порой неизвестно даже опытным системным администраторам и разработчикам. Во второй части материала тестировщик Selectel, Владимир Туров, подробно рассказывает о процессе на примере UEFI.

Первая версия того, что сейчас известно как Unified Extensive Firmware Interface (UEFI), разрабатывалась в 90-е годы прошлого тысячелетия специально под системы на Intel® Itanium® и называлась Intel Boot Initiative, а позже — EFI.

Желание «обновить» процесс загрузки было ожидаемо. PC-BIOS, именуемый ныне Legacy, предлагает работать в 16-битном real mode, адресует всего 1 МБ оперативной памяти, а загрузчик вместе с таблицей разделов должен размещаться в первых 512 байтах накопителя. Более того, PC-BIOS передает управление первому найденному загрузчику без возможности возврата назад. При этом обработку случаев с несколькими операционными системами возлагают на плечи загрузчика.

Ограничение на размер загрузчика диктует использование разметки Master Boot Record (MBR), появившийся в 1983 году. MBR не стандартизирован, однако множество производителей придерживаются «сложившихся традиций». У MBR есть серьезные ограничения: по умолчанию поддерживается только 4 раздела и объем накопителя не более 2.2 ТБ.

В декабре 2000 года была выпущена первая широко распространенная спецификация EFI под версией 1.02. Спустя пять лет Intel передали EFI в UEFI Forum, добавив Unified в название, чтобы подчеркнуть изменения. Спецификация UEFI лежит в открытом доступе и состоит из нескольких документов:

  • ACPI Specification;
  • UEFI Specification;
  • UEFI Shell Specification;
  • UEFI Platform Initialization Specification;
  • UEFI Platform Initialization Distribution Packaging Specification.

Самое интересное начинается в UEFI Platform Initialization Specification, где описываются все фазы загрузки платформы.

UEFI универсален, но в данной статье мы будем опираться на стандарт, поглядывая в сторону процессоров на архитектуре x86_64.

Последовательность фаз загрузки UEFI Источник: UEFI Platform Initialization Specification

После инициации включения платформы блок питания ждет, пока не завершатся переходные процессы, и после устанавливает сигнал на линию Power_Good. И первым начинает работу не центральный процессор, а автономная подсистема Intel® Management Engine (ME) или аналогичная ей AMD Secure Technology (ST). Эта подсистема проводит собственные операции, а затем подготавливает и запускает первое ядро одного процессора, именуемое Bootstrap Processor (BSP).

В соответствии с принятой терминологией ядро/поток процессора здесь и далее будет называться процессором: начальным (bootstrap processor) или прикладным (application processor).

Как и в Legacy, процессор начинает выполнять первую инструкцию в конце адресного пространства по адресу 0xfffffff0. Эта инструкция — прыжок на первую фазу инициализации платформы — SEC.

  • обработка события включения;
  • инициализация достаточного количества памяти для следующей фазы;
  • становление корня доверия системы;
  • передача необходимой информации и управления на следующую фазу.

Процессоры x86_64 запускаются в 16-битном реальном режиме, и в процессе первичной инициализации BSP переводится в 32-битный защищенный режим. Затем происходит обновление микрокода всех доступных процессоров.

Следом происходит обработка события включения. Под этим подразумевается агрегация информации о состояниях оборудования, чтобы на следующей фазе некоторые модули могли сделать выводы о «здоровье» и общем состоянии платформы.

Во время фазы SEC не происходит инициализация оперативной памяти. Вместо этого свободный кэш процессора помечается как несбрасываемый, и он превращается во временную оперативную память. Такой режим называется no-eviction mode (NEM). В выделенной памяти создается стек, что позволит модулям из следующих фаз использовать стековые языки программирования до инициализации основной оперативной памяти.

Далее происходит инициализация всех прикладных процессоров (Application Processor, AP) с отправкой им специальной последовательности межпроцессорных прерываний (Inter-Processor Interrupt, IPI). Последовательность Init IPI — Start-up IPI — пробуждает прикладной процессор и запускает на нем самотестирование — Built-In Self-Test (BIST). Результаты тестирования записываются и передаются далее для анализа.

В конце фазы Security необходимо найти раздел Boot Firmware Volume (BFV), на котором располагается исполняемый код следующей фазы, а также по возможности найти другие, неосновные, разделы с кодом (Firmware Volume, FV).

Чтобы оправдать название фазы Security и стать корнем доверия, во время выполнения этой фазы код, которому мы планируем передать управление, может быть проверен на отсутствие несанкционированных изменений и вредоносных частей программы.

8. Инициализация микропроцессора

Аппаратный сброс

Для МП 80386 при частоте 16 МГц на самотестирование уходит около 2 19 тактов или приблизительно 33 мс.

Сброс переводит процессор в реальный режим и устанавливает ряд регистров в определенное состояние. Содержимое регистра ЕАХ зависит от результатов выполнения процедуры самотестирования при включении питания. В регистре DX размещаются идентификатор изделия и номер модификации. Поле DH содержит число, указывающее на тип процессора: 3 - 80386, 4 - i486, 5 - Pentium, 6 - P6, 15 - Pentium 4. В поле DL заносится номер модификации.

Состояние регистров процессора после инициализации:

РегистрСостояние (hex)
EFLAGSxxx00002
CR060000010
CR2, CR3, CR400000000
EIP0000FFF0
CS (селектор)
База
Передел
F000
FFFF0000
FFFF
SS,DS,ES,FS,GS
База
Передел
0000
00000000
FFFF
EAX00000000
EDX00000mmm
mmm - модель CPU
EBX,ECX,ESI,EDI,EBP,ESP00000000
GDTR,IDTR
База
Передел

0000000
FFFF
LDTR,TR (селектор)
База
Передел
0000
0000000
FFFF
DR0-DR300000000
DR6FFFF0FF0
DR700000400

Подготовка к работе в реальном режиме

Перед тем как запускать программы в реальном режиме, следует инициализировать некоторые структуры. Для того, чтобы можно было выполнять команды с обращением к стеку, надо загрузить регистр сегмента стека SS. Он должен указывать на область оперативной памяти.

  • заполнить все позиции IDT, которые могут использоваться прерываниями или исключениями, указателями работоспособных программ обработки прерываний;
  • занести в IDTR указатель таблицы прерываний;
  • изменить значение предела в IDTR на 0, это останавливает систему при возникновении исключения или немаскируемого прерывания.

Подготовка к работе в защищенном режиме

Переключение процессора в защищенный режим из реального осуществляется загрузкой в системный регистр CR0 слова с установленным битом РЕ (Protect Enable).

Как правило, большая часть действий по инициализации защищенного режима выполняется либо перед переключением в этот режим, либо непосредственно после переключения. Если инициализация осуществляется после переключения, выполняемые затем процедуры не должны использовать не инициализированные еще средства защищенного режима.

  • Регистр задачи должен указывать на область сохранения состояния текущей задачи. После первого переключения информация, занесенная в эту область, теряет свое значение и область можно использовать с другими целями.
  • Должен существовать достоверный TSS новой задачи. Указатель стека в TSS (для уровней привилегий, численно меньших или равных начальному CPL) должен указывать на достоверный сегмент стека.

Регистр сегмента стека (SS) можно загружать и в защищенном режиме, и в реальном режимах. Если SS загрузить в реальном режиме, он и после переключения в защищенный режим продолжает указывать на тот же самый линейный базовый адрес.

Форматы таблицы прерываний отличаются в защищенном и реальном режимах. Поскольку невозможно одновременно переключить режим и изменить формат таблицы прерываний, неизбежно, что в течение некоторого времени IDTR будет определять таблицу прерываний с неправильным форматом. Прерывание или исключение в течение этого времени приведут к непредсказуемым результатам. Чтобы избежать этого, следует запретить прерывания до тех пор, пока не будут размещены все программы обработки прерываний и не будет создана правильная таблица дескрипторов прерываний IDT в защищенном режиме.

  • непосредственно за установкой PG должна следовать команда межсегментного перехода (FAR JMP);
  • исполняемая в настоящий момент страница должна отображаться на те же физические адреса как до, так и после установки PG.

Регистр GDTR должен указывать на достоверную таблицу GDT перед тем, как содержимое какого-либо регистра изменяется в защищенном режиме. Таблицы GDT и LDT следует размещать в оперативной памяти, потому что МП модифицирует бит доступа в дескрипторах. Инициализацию GDT и GDTR можно провести в реальном режиме.

Переключение процессора из защищенного режима в реальный возможно не только через аппаратный сброс, как это было у 80286, но и с помощью сброса бита РЕ в CR0. до этого переключения также необходимо загрузить в сегментные регистры селекторы дескрипторов, описывающие свойства сегментов стандартного реального режима. Однако вместо этого можно создать и так называемый "нереальный" режим, отличающийся от реального возможностью доступа к сегментам большого (до 4 Гбайт) размера. Правда, у процессоров 80286 и 80386 лимит кодового сегмента принудительно ограничивается размером 64 Кбайт, но у более новых процессоров большой размер допустим для всех сегментов. "Нереальный режим" часто используется менеджерами памяти для DOS и игровыми программами, требующими большого объема памяти.

Читайте также: