Как работает микропроцессор?
Для того чтобы понять, как работает микропроцессор, зададим себе вопрос – а как он должен работать? Есть теория (в основном созданная постфактум – после того, как первые ЭВМ были уже построены и функционировали), которая указывает, как именно строить алгоритмы, и что процессор в соответствии с ними должен делать.
Мы, естественно, углубляться в это не будем, просто констатируем, что любой алгоритм есть последовательность неких действий, записанных в виде набора последовательно выполняемых команд (инструкций, операторов). При этом среди таких команд могут встречаться команды перехода, которые в некоторых случаях нарушают исходную последовательность выполнения операторов строго друг за другом. Среди прочих должны быть также команды ввода и вывода данных (программа должна как‑то общаться с внешним миром?), а также команды выполнения арифметических и логических операций.
Команды должны где‑то храниться, поэтому неотъемлемой частью всей системы должно быть устройство памяти программ. Где‑то надо складывать и данные, как исходные, так и результаты работы программы, поэтому должно быть устройство памяти данных. Так как команды и данные, в конечном счете, все равно есть числа, то память может быть общая, только надо уметь отличать, где именно у нас команды, а где – данные. Это есть один из принципов фон Неймана, хотя и в микроконтроллерах, о которых мы будем говорить в дальнейшем, традиционно используют не фон‑неймановскую, а так называемую гарвардскую архитектуру, когда памяти данных и программ разделены (это разделение, впрочем, может в определенных пределах нарушаться). Процессор, построенный по фон Нейману, более универсален – например, он позволяет без особых проблем наращивать память, строить ее иерархически и более эффективно ее перераспределять прямо по ходу работы. Так, в системе Windows всегда предполагается, что компьютер имеет практически неограниченный объем памяти (измеряемый в терабайтах), а если ее реально не хватает, к делу подключается своп‑файл (так называемый файл подкачки ) на жестком диске. В то же время микроконтроллерам подобная гибкость не особенно требуется – на их основе, как правило, строятся узлы, выполняющие узкую задачу и работающие по конкретной программе, так что нужную конфигурацию системы ничего не стоит предусмотреть заранее.
* * *
МП и МК
Кстати, а почему мы все время говорим то микропроцессоры (МП), то микроконтроллеры (МК)? Микроконтроллер отличается от микропроцессора тем, что он предназначен для управления другими устройствами, и поэтому имеет встроенную развитую систему ввода/вывода, но, как правило, относительно более слабое АЛУ. Микроконтроллерам очень хорошо подходит английский термин «computer‑on‑chip», однокристальный компьютер. В самом деле, для построения простейшего вычислительного устройства, которое могло бы выполнять что‑то полезное, обычный микропроцессор, от i4004 до Pentium и Core, приходится дополнять памятью, ПЗУ с записанной BIOS, устройствами ввода/вывода, контроллером прерываний, тактовым генератором с таймерами и т. п. – всем тем, что сейчас стало объединяться в так называемые чипсеты . «Голый» МП способен только на одно – правильно включиться, ему даже программу загрузки неоткуда взять.
В то же время для МК микропроцессор – это только ядро, даже не самая большая часть кристалла. Для построения законченной системы на типовом МК не требуется вообще ничего, кроме источника питания и периферийных исполняющих устройств, которые позволяли бы человеку определить, что система работает. Обычный МК может без дополнительных компонентов общаться с другими МК, внешней памятью, специальными микросхемами (вроде часов реального времени или флэш‑памяти), управлять небольшими (а иногда – и большими) матричными панелями, к нему можно напрямую подключать датчики физических величин (в том числе – чисто аналоговые, АЦП тоже часто входят в МК), кнопки, клавиатуры, светодиоды и индикаторы, короче – в микроконтроллерах сделано все, чтобы приходилось как можно меньше паять и задумываться над подбором элементов. За это приходится расплачиваться пониженным быстродействием (которое, впрочем, не так‑то уж и важно в типовых задачах для МК) и некоторым ограничением в отдельных функциях – по сравнению с универсальными, но в сотни раз более дорогими и громоздкими системами на «настоящих» МП. Вы можете мне не поверить, но процессоры для персональных компьютеров (ПК), о которых мы столько слышим, занимают в общем количестве выпускаемых процессоров лишь 5–6 % – остальные составляют микроконтроллеры различного назначения.
* * *
В соответствии с изложенным, основной цикл работы процессора должен быть таким: выборка очередной команды (из памяти), если необходимо – выборка исходных данных для нее, выполнение команды, размещение результатов в памяти (опять же если это необходимо). Вся работа в этом цикле должна происходить автоматически по командам некоторого устройства управления, содержащего тактовый генератор – системные часы, по которым все синхронизируется. Кроме того, где‑то это все должно происходить: складирование данных, кода команды, выполнение действий и т. п., так что процессор должен содержать некий набор рабочих регистров (по сути – небольшую по объему сверхбыструю память), определенным образом связанных как между собой, так и с устройством управления и АЛУ, которое неизбежно должно присутствовать.
Решающую роль в работе процессора играет счетчик команд . Он автоматически устанавливается на нуль в начале работы, что соответствует первой команде, и автоматически же инкрементируется (т. е. увеличивается на единицу) с каждой выполненной командой. Если по ходу дела порядок следования команд нарушается – например, встречается команда перехода (ветвления), то в счетчик загружается соответствующий адрес команды – ее номер от начала программы. Если это не просто ветвление, а выполнение подпрограммы, которое предполагает в дальнейшем возврат к основной последовательности команд (к следующей команде после вызова подпрограммы), то перед переходом к выполнению подпрограммы текущее значение счетчика команд сохраняется в специально отведенной для этой цели области памяти – стеке. По команде окончания подпрограммы сохраненный адрес извлекается из стека , и выполнение основной программы продолжается. К счастью, нам самим не придется иметь дело со счетчиком команд, потому что все указания на этот счет содержатся в командах, и процессор все делает автоматически.
Блок‑схема простейшего МК, содержащего процессорное ядро и минимум компонентов для «общения» с внешней средой, показана на рис. 18.2.
Рис. 18.2 . Блок‑схема простейшего микроконтроллера
Здесь мы включили в состав системы память программ, которая у ПК‑процессоров находится отдельно на жестком диске (если не считать относительно небольшого объема ПЗУ, содержащего так называемую BIOS; т. е. базовые процедуры для запуска и обмена с внешней средой) – сами знаете, какой объем программ бывает в персональных компьютерах. В большинстве современных микроконтроллеров постоянное запоминающее устройство (ПЗУ) для программ входит в состав чипа и обычно составляет от 1–2 до 8‑32 Кбайт, хотя есть модели и с 256 килобайтами встроенной памяти. Для подавляющего большинства применений вполне достаточно 2–8 Кбайт – при условии, что вы создаете программы прямо в командах контроллера, на языке ассемблера. Применение языка высокого уровня (обычно одного из вариантов языка С ), что становится все более популярным из‑за удобства работы с ним, как мы увидим, существенно повышает требования к объему памяти программ.
Встроенное оперативное запоминающее устройство (ОЗУ) для хранения данных в том или ином объеме также имеется во всех современных микроконтроллерах, типичный размер такого ОЗУ: от 128–256 байтов до 1–4 Кбайт. В большинстве универсальных контроллеров есть и некоторое количество встроенной энергонезависимой памяти для хранения констант – обычно столько же, сколько и ОЗУ данных. Но к памяти мы еще вернемся в этой главе, а пока продолжим про процессоры.
* * *
Подробности
В первых моделях микропроцессоров (включая и интеловские процессоры для ПК – от 8086 до 80386) процессор выполнял команды строго последовательно: загрузить команду, определить, что ей нужны операнды, загрузить эти операнды (по адресу регистров, которые их должны содержать, – адреса эти, как правило, хранятся сразу после собственно кода команды или определены заранее), потом проделать нужные действия, складировать результаты… До нашего времени дошла архитектура суперпопулярных еще недавно микроконтроллеров 8051, выпускающихся и по сей день различными фирмами (Atmel, Philips ), которые выполняли одну команду аж за 12 тактов (в некоторых современных аналогах, впрочем, это число меньше). Для ускорения работы стали делить такты на части (например, срабатывать по переднему и заднему фронтам), но действительный прорыв произошел с внедрением конвейера. Со времен Генри Форда известно, что производительность конвейера зависит только от времени выполнения самой длинной операции, – если поделить команды на этапы и выполнять их одновременно разными аппаратными узлами, то можно добиться существенного ускорения (хотя и не во всех случаях). В рассматриваемых далее микроконтроллерах Atmel AVR конвейер двухступенчатый: когда очередная команда загружается и декодируется, предыдущая уже выполняется и пишет результаты. В AVR это позволило выполнять большинство команд за один такт (кроме команд ветвления программы).
* * *
Главное устройство в МП, которое связывает все узлы в единую систему, – внутренняя шина данных . По ней все устройства обмениваются сигналами. Например, если МП требуется обратиться к внешней дополнительной памяти, то при исполнении соответствующей команды на шину данных выставляется нужный адрес, от устройства управления поступает через нее же запрос на обращение к нужным портам ввода/вывода. Если порты готовы, адрес поступает на выходы портов (т. е. на соответствующие выводы контроллера), затем по готовности принимающий порт выставляет на шину принятые из внешней памяти данные, которые загружаются в нужный регистр, после чего шина данных свободна. Для того чтобы все устройства не мешали друг другу, все это строго синхронизировано, при этом каждое устройство имеет, во‑первых, собственный адрес, во‑вторых, может находиться в трех состояниях: работать на ввод, на вывод или находиться в третьем состоянии, не мешая другим работать.
Под разрядностью МП обычно понимают разрядность чисел, с которыми работает АЛУ, соответственно, такую же разрядность имеют и рабочие регистры. Например, все ПК‑процессоры от i386 до последних инкарнаций Pentium были 32‑разрядными, большинство последних моделей от Intel и AMD относятся уже к 64‑разрядным. Большинство микроконтроллеров общего назначения – 8‑разрядные, но все большую популярность приобретают 32‑разрядные, обладающие принципиально большими возможностями при практически той же цене. Интересно, что развитие промежуточной ветви 16‑разрядных контроллеров практически остановилось ввиду нецелесообразности.
* * *
Заметки на полях
Обычно тактовая частота универсальных МК невелика (хотя инженеру 1980‑х, когда ПК работали на частотах не выше 6 МГц, она показалась бы огромной) – порядка 8‑16 МГц, иногда до 20 МГц или несколько более. И это всех устраивает – дело в том, что обычные МК и не предназначены для разработки быстродействующих схем. Если требуется быстродействие, то используется другой класс интегральных схем – ПЛИС, «программируемые логические интегральные схемы» (английское название самой популярной сейчас их разновидности – FPGA, field‑programmable gate array ). Простейшая ПЛИС представляет собой набор никак не связанных между собой логических элементов (наиболее сложные из них могут включать в себя и некоторые законченные узлы, вроде триггеров и генераторов), которые в процессе программирования такого чипа соединяются в нужную схему. Комбинационная логика работает гораздо быстрее тактируемых контроллеров, и для построения сложных логических схем в настоящее время применяют только ПЛИС, от использования дискретных элементов («рассыпухи») в массовых масштабах уже давно отказались. Еще одно преимущество ПЛИС – статическое потребление энергии для некоторых серий составляет единицы микроватт, в отличие от МК, которые во включенном состоянии потребляют достаточно много (если не находятся в режиме энергосбережения). В совокупности с более универсальными и значительно более простыми в обращении, но менее быстрыми и экономичными микроконтроллерами, ПЛИС составляют основу большинства массовых электронных изделий, которые вы видите на прилавках. В этой книге мы, конечно, рассматривать ПЛИС не будем – в любительской практике, в основном из‑за дороговизны соответствующего инструментария и высокого порога его освоения, они не используются, а для конструирования одиночных экземпляров приборов даже для профессиональных применений их использовать нецелесообразно.
* * *
Если подробности внутреннего функционирования МП нас волнуют не очень (центральный узел – АЛУ – мы уже «изобретали» в главе 15 , и этого достаточно, чтобы понимать, что именно происходит внутри процессорного ядра), то обмен с внешней средой нас как раз интересует во всех деталях. Для этого служат порты ввода/вывода (I/O‑port, от Input/Output). В этом термине имеется некоторая неопределенность, т. к. те, кто программировал для ПК на ассемблере, помнят, что в ПК портами ввода/вывода (ПВВ) назывались регистры для управления всеми устройствами, кроме непосредственно процессорного ядра. В микроконтроллерах то же самое называют регистрами ввода/вывода (РВВ) – это регистры для доступа ко встроенным компонентам контроллера, внешним по отношению к вычислительному ядру. А это все узлы, которыми непосредственно управляет пользователь: от таймеров и последовательных портов до регистра флагов и управления прерываниями. Кроме ОЗУ, доступ к которому обеспечивается специальными командами, все остальное в контроллере управляется через РВВ, и путать с портами ввода/вывода их не следует.
ПВВ в МК служат для обмена с «окружающей средой» (управляются они, естественно, тоже внутренними регистрами ввода/вывода). На схеме рис. 18.2 показано 3 ПВВ: А, В и С , в реальных МК их может быть и больше, и меньше. Еще важнее число выводов этих портов, которое чаще всего совпадает с разрядностью процессора (но не всегда, как это было у 8086, который имел внутреннюю 16‑разрядную структуру, а внешне выглядел 8‑разрядным). Если мы заставим 8‑разрядные порты «общаться», например, с внешней памятью, то на двух из них можно выставить 16‑разрядный адрес, а на оставшемся – принимать данные. А как быть, если портов два или вообще один? (К примеру, в микроконтроллере ATtiny2313 портов формально два, но один усеченный, так что общее число линий составляет 15). Для того чтобы даже в такой ситуации это было возможно, все внешние порты в МП всегда двунаправленные. Скажем, если портов два, то можно сначала выставить адрес, а затем переключить порты на вход и принимать данные. Естественно, для этого порты должны позволять работу на общую шину – т. е. либо иметь третье состояние, либо выход с общим коллектором для объединения в «монтажное ИЛИ».
Варианты для обоих случаев организации выходной линии порта показаны на рис. 18.3, где приведены упрощенные схемы выходных линий микроконтроллеров семейства 8048 – когда‑то широко использовавшегося предшественника популярного МК 8051 (например, 8048 был выбран в качестве контроллера клавиатуры в IBM PC). В современных МК построение портов несколько сложнее (в частности, вместо резистора там полевой транзистор), но для уяснения принципов работы это несущественно.
Рис. 18.3. Упрощенные схемы портов ввода/вывода МК 80481 : а – портов 1 и 2, б – порта 0
По первому варианту (рис. 18.3, а ) в МК 8048 построены порты 1 и 2 . Когда в порт производится запись, то логический уровень поступает с прямого выхода защелки на статическом D‑триггере на вход схемы «И», а с инверсного – на затвор транзистора VT2. Если этот уровень равен логическому нулю, то транзистор VT1 заперт, а VT2 открыт, на выходе также логический ноль. Если уровень равен логической единице, то на время действия импульса «Запись» транзистор VT1 открывается, а транзистор VT2 запирается (они одинаковой полярности). Если на выходе присутствует емкость (а она всегда имеется в виде распределенной емкости проводников и емкости входов других компонентов), то через открытый VT1 протекает достаточно большой ток заряда этой емкости, позволяющий сформировать хороший фронт перехода из 0 в 1. Как только импульс «Запись» заканчивается, оба транзистора отключаются, и логическая единица на выходе поддерживается резистором R1. Выходное сопротивление открытого транзистора VT1 примерно 5 кОм, а резистора – 50 кОм. Любое другое устройство, подключенное к этой шине, при работе на выход может лишь либо поддержать логическую единицу, включив свой подобный резистор параллельно R1, либо занять линию своим логическим нулем – это, как видите, и есть схема «монтажное ИЛИ». При работе на вход состояние линии просто считывается во время действия импульса «Запись» со входного буфера (элемент В на рис. 18.3, а ).
Второй вариант (рис. 18.3, б ), по которому устроен порт 0, есть обычный выходной каскад КМОП с третьим состоянием, т. е. такой порт может работать на выход, только полностью занимая линию, остальные подключенные к линии устройства при этом должны смиренно внимать монополисту, воспринимая сигналы. Это обычно не создает особых трудностей и схемотехнически даже предпочтительно ввиду симметрии выходных сигналов и высокого сопротивления для входных. Единственная сложность возникает при сопряжении такого порта с линией, работающей по первому варианту, т. к. при логической единице на выходе могут возникнуть электрические конфликты, если кто‑то попытается выдать в линию логический ноль (ток от источника пойдет через два распахнутых транзистора).
Для обеспечения работы трехстабильного порта по схеме «монтажное ИЛИ» применяют хитрый прием: всю линию «подтягивают» к напряжению питания с помощью внешнего резистора (во многих МК существует встроенный отключаемый резистор, установленный аналогично R1 в схеме рис. 18.3, а ), и нормальное состояние всех участвующих трехстабильных портов – работа на вход в третьем состоянии. В этом режиме на линии всегда будет логическая единица. На выход же линию переключают только, когда надо выдать логический ноль. В этом случае, даже при одновременной активности нескольких портов, конфликтов не возникнет.
Лечение амнезии
В 1965 году в Иллинойсском университете был запущен один из самых передовых компьютеров по тому времени – ILLIAC–IV. Он стал первым компьютером, в котором использовалась быстрая память на микросхемах, – каждый чип (производства Fairchild Semiconductor ) имел емкость 256 битов, а всего было набрано 1 Мбайт. Стоимость этой памяти составила ощутимую часть от всей стоимости устройства, обошедшегося заказчику – NASA – в $31 млн. Через 10 лет один из первых персональных компьютеров Altair 8800 (1975 год), продававшийся в виде набора «сделай сам», при стоимости порядка $500 имел всего 256 байтов (именно байтов, а не килобайт) памяти. В том же году для распространения языка Basic for Altair Биллом Гейтсом и Полом Алленом была создана фирма, получившая первоначальное название Micro‑Soft . Одна из самых серьезных проблем, которую им пришлось решать, – нехватка памяти, потому что созданный ими интерпретатор Basic требовал аж 4 Кбайт!
Проблема объемов памяти и ее дороговизна преследовала разработчиков достаточно долго – еще в конце 1990‑х стоимость памяти для ПК можно было смело прикидывать из расчета 1 доллар/Мбайт, что при требовавшихся уже тогда для комфортной работы объемах ОЗУ порядка 128–256 Мбайт могло составлять значительную часть стоимости устройства. Сейчас 4 гигабайта памяти в настольном ПК или ноутбуке уже стали фактическим стандартом. Это привело, в частности, к кардинальным изменениям в самом подходе к программированию – если еще при программировании под DOS о компактности программ и экономии памяти в процессе работы нужно было специально заботиться, то теперь это практически не требуется.
Но в программировании для микроконтроллеров это все еще не так. Хотя гейтсовский интерпретатор Basic влезет в большинство современных однокристальных МК, но экономная программа легче отлаживается (а, значит, содержит меньше ошибок) и быстрее выполняется. Три‑четыре потерянных на вызове процедуры такта могут стать причиной какой‑нибудь трудновылавливаемой ошибки времени выполнения – например, если за это время произойдет вызов прерывания. Поэтому память в МК стоит экономить, даже если вы располагаете заведомо достаточным ее объемом. Этот призыв, конечно, пропадает впустую, когда мы сталкиваемся с программированием на языках высокого уровня, где от нас зависит гораздо меньше, чем от разработчиков компиляторов.
Далее мы рассмотрим основные разновидности памяти, используемые как в составе микроконтроллеров, так и во внешних узлах. И начнем с того, что попробуем сами сконструировать устройство долговременной памяти – ПЗУ (постоянное запоминающее устройство). Как мы увидим, любая память в принципе есть не что иное, как преобразователь кодов.
Изобретаем простейшую ROM
Всем известно сокращение ROM – Read‑Only Memory – английское название постоянного запоминающего устройства, ПЗУ. На самом деле это название (память только для чтения ) не очень точно характеризует суть дела, отечественное название есть более корректный термин, самое же правильное называть такую память энергонезависимой . Ведь ПЗУ отличается от других типов памяти не тем, что его можно только читать, а записывать нельзя (практически все современные устройства ROM имеют возможность записи), а тем, что информация в нем не пропадает при выключении питания.
Тем не менее, первыми разновидностями ПЗУ, изобретенными еще в 1956 году, были именно нестираемые кристаллы, которые носят наименование ОТР ROM – O ne‑T ime P rogrammable ROM , однократно программируемое ПЗУ. До недавнего времени на них делали память программ МК для удешевления серийных устройств – вы отлаживаете программу на перезаписываемой памяти, а в серию пускаете приборы с «прожигаемой» ОТР ROM. И лишь в последние годы «прожигаемая» память стала постепенно вытесняться более удобной flash‑памятью, поскольку последняя подешевела настолько, что смысл в использовании одноразовых кристаллов пропал.
Мы сконструируем подобие «прожигаемого» ПЗУ с помощью диодов. Простейший вариант такого ПЗУ показан на рис. 18.4. В данном случае он представляет собой не что иное, как преобразователь из десятичного кода в семисегментный. Если на входе поставить дешифратор типа 561ИД1, переводящий двоичный код в десятичный, то мы получим аналог микросхемы 561 ИД5.
Рис. 18.4. ПростейшееПЗУ – преобразователь кода
Чтобы понять, как это работает, представьте себе, что первоначально на всех пересечениях между строками и столбцами диоды присутствовали – это аналог незаполненной памяти, в которой записаны все единицы. Затем мы взяли и каким‑то образом (например, подачей высокого напряжения) разрушили те диоды, которые нам не нужны, в результате чего получили нужную конфигурацию.
Эта схема не содержит активных элементов, и потому возможности ее ограниченны, – например, выходы устройства, подающего активный высокий уровень по входным линиям, должны «тащить» всю нагрузку по зажиганию сегментов. Обычная микросхема ПЗУ построена на транзисторных ячейках и поэтому без всяких хитростей принимает и выдает обычные логические уровни. К тому же она включает в себя и дешифрирующую логику, поэтому на вход подается двоичный, а не десятичный код.
Постойте, а причем тут ПЗУ вообще? Дело в том, что входной код здесь можно рассматривать, как адрес ячейки, в выходной – как ее содержимое. И любое ПЗУ можно представить, как универсальный преобразователь кодов. Причем удобство состоит в том, что изначально в ПЗУ не записано ничего (одни нули или единицы), и мы можем реализовать на нем любую логическую функцию – все зависит только от его емкости. В том числе, такую простую, как преобразователь кодов десятичный‑семисегментный, или же такую сложную, как операционная система Windows.
Последнее мы каждый раз и делаем, когда устанавливаем Windows на компьютер, причем в качестве ПЗУ выступает жесткий диск. Из этого примера отчетливо видно, что каким бы сложным ни был алгоритм, он все равно в конечном итоге сводится к совокупности однозначных логических уравнений, которые можно реализовать как через ПЗУ с записанной программой, так и с помощью цифрового устройства любого другого типа.
Дата добавления: 2016-05-11; просмотров: 1951;