, а не килобайт) памяти. В том же году для распространения языка Basic for Altair Биллом Гейтсом и Полом Алленом была создана фирма, получившая первоначальное название Micro-Soft. Одна из самых серьезных проблем, которую пришлось решать — нехватка памяти, потому что созданный ими интерпретатор Basic требовал аж 4 кбайтов!
Проблема объемов памяти и ее дороговизна преследовала разработчиков до самого последнего времени, еще в конце 90-х стоимость памяти для ПК можно было смело прикидывать на уровне $ 1/Мбайт, что при требовавшихся уже тогда для комфортной работы объемах ОЗУ порядка 128–256 Мбайт могло составлять значительную часть стоимости устройства. Сейчас гигабайтом памяти в настольном ПК и даже ноутбуке никого не удивишь, к тому же все современные ОС «умеют» автоматически дополнять недостающий объем ОЗУ за счет дискового пространства. Это привело, в частности, к кардинальным изменениям в самом подходе к программированию: если еще при программировании под DOS о компактности программ и экономии памяти в процессе работы нужно было специально заботиться, то теперь это практически не требуется.
Но в программировании для микроконтроллеров это все еще не так. Хотя гейтсовский интерпретатор Basic «влезет» в большинство современных однокристальных МК, но экономная программа легче отлаживается (а значит, содержит меньше ошибок) и быстрее выполняется. Три-четыре такта, потерянных на вызове процедуры, могут стать причиной какой-нибудь трудновылавливаемой ошибки времени выполнения, например, если за это время произойдет вызов прерывания. Поэтому память в МК стоит экономить, даже если вы располагаете заведомо достаточным ее объемом.
Согласно упоминавшимся принципам фон Неймана, которые до сих пор являются основополагающими для разработчиков компьютерных систем, память должна быть организована иерархически, от памяти малого объема, но с высоким быстродействием, до медленной памяти большой емкости. Именно так и проектируют современные компьютеры, которые содержат очень быструю кэш-память на одном кристалле с процессором (а иногда два или три уровня такой памяти), потом идет быстродействующее ОЗУ (оперативное запоминающее устройство, та самая память, которая указывается в характеристиках ПК), а затем более медленные устройства типа жестких дисков или CD- и DVD-ROM.
Далее мы рассмотрим основные разновидности памяти, используемые как в составе микроконтроллеров, так и во внешних узлах. И начнем с того, что попробуем сами сконструировать устройство долговременной памяти — ПЗУ (постоянное запоминающее устройство). Как мы увидим, любая память в принципе есть не что иное, как преобразователь кодов.
Всем известное благодаря распространению оптических дисков сокращение ROM — Read Only Memory — и есть не что иное, как «западное» название ПЗУ. На самом деле это название («память только для чтения») не очень точно характеризует суть дела, отечественный термин «постоянное запоминающее устройство» более корректен, самое же правильное называть такую память «энергонезависимой». ПЗУ отличается от других типов памяти не тем, что его можно только читать, а записывать нельзя, а тем, что информация в нем не пропадает при выключении питания. Сама по себе невозможность записи теперь нехарактерна даже для компакт-дисков, однако название ROM сохранилось.
Тем не менее первыми разновидностями ПЗУ, изобретенными еще в 1956 году, были именно нестираемые кристаллы, которые носят наименование OTP ROM (One-Time Programmable ROM, «однократно программируемое ПЗУ»). До недавнего времени на них делали память программ МК для удешевления серийных устройств. Вы отлаживаете программу на перезаписываемой памяти, а в серию пускаете приборы с «прожигаемой» OTP ROM. И лишь в последние годы «прожигаемая» память стала постепенно вытесняться более удобной flash-памятью, когда последняя подешевела настолько, что смысл в использовании одноразовых кристаллов пропал. Они продолжают выпускаться лишь за счет инерции производства: в 2007 году доля однократно программируемых и масочных (т. е. программируемых прямо на производстве) микроконтроллеров составит, по прогнозам, не более 1/4 всех выпускаемых чипов.
Мы сконструируем подобие «прожигаемого» ПЗУ с помощью диодов. Простейший вариант такого ПЗУ представлен на рис. 11.4. В данном случае он представляет собой не что иное, как преобразователь из десятичного кода в семисегментный.
Рис. 11.4.Простейшее ПЗУ — преобразователь кода
Если на входе поставить дешифратор типа 561ИД1, то мы получим аналог микросхемы 561ИД5. Представьте себе, что первоначально на всех пересечениях между строками и столбцами диоды присутствовали — это вариант незаполненной памяти, в которой записаны все единицы. Затем мы взяли и каким-то образом (например, подачей высокого напряжения) разрушили те диоды, которые нам не нужны, в результате чего получили нужную конфигурацию. Эта схема не содержит активных элементов и потому возможности ее ограничены, например, выходы устройства, подающего активный высокий уровень по входным линиям, должны «тащить» всю нагрузку по зажиганию сегментов. Обычная микросхема ПЗУ построена на транзисторных ячейках и поэтому без всяких хитростей принимает и выдает обычные логические уровни. К тому же она включает в себя и дешифрирующую логику, поэтому на вход подается двоичный, а не десятичный код.
Постойте, а при чем тут ПЗУ вообще? Дело в том, что любое ПЗУ можно представить, как универсальный преобразователь кодов, если рассматривать входной код, как адрес ячейки, а код, получающийся на выходе — как содержимое этой ячейки. Причем удобство состоит в том, что изначально в ПЗУ не записано ничего (одни нули или единицы), и мы можем реализовать на нем любую логическую функцию, все зависит только от емкости. В том числе, такую простую, как преобразователь кодов. Или такую сложную, как операционная система Windows. Последнее мы каждый раз и делаем, когда устанавливаем Windows на компьютер, причем в качестве ПЗУ выступает жесткий диск. Из этого примера отчетливо видно, что каким бы сложным ни был алгоритм, он все равно в конечном итоге сводится к совокупности однозначных логических уравнений, которые можно реализовать как через ПЗУ, так и с помощью устройства памяти любого другого типа.
Общее устройство однобитной ячейки памяти (любого типа) показано на рис. 11.5.
Рис. 11.5.Общее устройство ЗУ с однобитным выходом
Из нее видно, что память всегда имеет матричную структуру. В данном случае матрица имеет 8 х 8 = 64 ячейки. На рис. 11.5 показано, как производится вывод и загрузка информации в память с помощью мультиплексоров/демультиплексоров (вроде 561КП2, см. главу 9). Код, поступающий на мультиплексор слева (х3 - х5) подключает к строке с номером, соответствующим этому коду, активирующий уровень напряжения (это может быть логическая единица или ноль, неважно). Код на верхнем мультиплексоре (х0 - х2) выбирает аналогичным образом столбец, в результате к выходу этого мультиплексора подключается ячейка, стоящая на пересечении выбранных строки и столбца.
Легко заметить, что сама по себе организация матрицы при таком однобитном доступе для внешнего мира не имеет значения. Если она будет выглядеть, как 4x16 или 32x2 или даже 64x1 — в любом случае код доступа (он называется адресным кодом) будет 6-разрядным, а выход один-единственный. Поэтому всем таким ЗУ приписывается организация Nx1 бит, где N — общее число битов. Для того чтобы получить байтную организацию, надо просто взять 8 таких микросхем и добавить к адресной линии еще три разряда, которыми через отдельный мультиплексор можно управлять выборкой одной из этих микросхем (для этого каждая такая микросхема имеет специальный вывод, называемый «выбор кристалла» — chip select, или CS). В данном примере мы получим в сумме 9 адресных разрядов, что соответствует емкости памяти (64 х 8 = 512 бит или 29). Один из битов можно использовать при этом для контроля четности, так что у нас получается хорошая модель типового модуля емкостью 256 байт, вроде тех, что были в упомянутом ILLIAC–IV. Большинство выпускаемых интегральных ЗУ также сложены из таких отдельных однобитных модулей (только в наше время уже значительно большей емкости) и имеют 8 или 16 параллельных выходов, но бывают кристаллы и с последовательным (побитным) доступом.
В качестве примера можно привести, скажем, ПЗУ с организацией 64Кх16 типа АТ27С1024 фирмы Atmel. Это однократно программируемое КМОП ПЗУ с напряжением питания 5 В и емкостью 1024 Мбита, что составляет 128 кбайт или 64 К двухбайтных слов (как мы увидим, такая организация очень удобна в качестве внешней памяти программ в контроллерах той же Atmel). Следует отметить, что в области микросхем памяти сложилась счастливая ситуация, когда все они, независимо от производителя и даже технологии, совпадают по выводам, разводка которых зависит только от организации матрицы (даже, как правило, не от объема!) и, соответственно, от применяемого корпуса (в данном случае — DIP-40). Для разных типов (RAM, ROM, EEPROM и т. д.) различается разводка выводов, управляющих процессом программирования, но можно спокойно заменять одну микросхему на другую (с той же организацией и, соответственно, в таком же корпусе) без переделки платы. Разводка выводов АТ27С1024 показана на рис. 11.6.
Рис. 11.6. Разводка выводов АТ27С1024
Традиционное название энергозависимых типов памяти, как и в случае ROM, следует признать довольно неудачным. RAM значит Random Access Memory, т. е. «память с произвольным доступом», по-русски это звучит как ЗУПВ — «запоминающее устройство с произвольной выборкой». Главным же признаком класса является не «произвольная выборка», а то, что при выключении питания память стирается. EEPROM (о которой далее), к примеру, тоже позволяет произвольную выборку и при записи, и при чтении. Но так сложилось исторически, и не нам нарушать традиции.