Это шестая статья цикла о микроконтроллерах. В предыдущей статье "Микроконтроллеры для начинающих. Часть 5. Архитектура. Адресные пространства и память" мы узнали, как процессор связан с памятью и что такое адресные пространства. Сегодня мы рассмотрим как устроена работа с памятью внутри этих адресных пространств. Это обязательная к изучению тема, имеющая непосредственное отношение к практике использования микроконтроллеров.
Сегодня статья будет довольно короткая, так как рассматриваемый вопрос не столь сложен. Но он важен для понимания работы с микроконтроллерами. Даже если с точки зрения программы адресное пространство линейное вы можете столкнуться с терминами блок, строка, страница, параграф, например, при записи в область памяти программ в своей программе. И я это покажу, когда буду рассматривать организацию памяти реальных микроконтроллеров.
Линейная адресация памяти
Большинство воспринимает память, не суть важно, какую, как непрерывное единое пространство адресуемых ячеек. В общем и целом, это не лишенное логики представление. Физическая память может состоять из совокупности отдельных блоков памяти, например, отдельных микросхем или кристаллов. Но с точки зрения выполняющейся процессором программы это зачастую не имеет значения, так как все служебные сигналы (например, CS - выбор микросхемы) формируются или отдельным блоком управления памятью, или аппаратурой самого процессора.
На рисунке выше я показал физическую память состоящую из нескольких отдельных блоков. Каждый блок памяти имеет свои адреса ячеек, которые могут совпадать с адресами в других блоках. Однако, блок управления памятью скрывает от программы все тонкости физической реализации, что позволяет считать всю память единым блоком, единой совокупностью, последовательно пронумерованных ячеек.
В общем случае, часть ячеек может отсутствовать (показаны серым цветом), например по причине не установленной микросхемы памяти. Но тот диапазон адресов, где должны были располагаться отсутствующие ячейки памяти все равно продолжает нумерацию.
Мы можем пройтись по всем ячейкам просто последовательно перебирая их адреса, без дополнительных ухищрений. Это простейший способ организации памяти (с точки зрения процессора). И самый удобный, с точки зрения программиста. Но у него есть и недостатки.
Ограничения линейной адресации
Так адрес ячейки памяти может оказаться слишком длинным. Так адресация памяти из 256 ячеек требует адреса диной 1 байт (8 бит). А для 64К ячеек (65536 ячеек) нужно уже 16 бит, или 2 байта. Как я уже говорил в "Микроконтроллеры для начинающих. Часть 2. Процессор микроконтроллера", каждая машинная инструкция, за некоторым исключением, включает в себя один или несколько операндов. Если операнд представляет собой адрес ячейки памяти, а у нас адрес занимает 2 байта, то машинная инструкция не может занимать менее 3 байт (нужно же и код команды где то разместить).
Размер памяти программ в микроконтроллерах ограничен, поэтому такое расточительство недопустимо. Производители микроконтроллеров решают проблему по разному.
Во первых, сразу можно заметить, что у нас разные адресные пространства программ и данных, так как используется Гарвардская архитектура (о чем я писал в предыдущей статье). Объем памяти данных часто меньше, чем объем памяти программ. А значит, в инструкция работающих с данными мы можем уменьшить размер поля адреса операнда. А в инструкциях работающих с адресами инструкция (инструкции перехода) выделить под адрес больше места.
Во вторых, можно сделать длину машинной инструкции переменной, что позволит иногда экономить место в памяти программ. Или просто задавать не полный адрес ячейки памяти, а лишь его часть. А это приводит нас к страничной организации памяти.
Страничная (банковая) адресация памяти
Если у нас инструкция содержит не полный адрес, а лишь его часть, то как мы сможем задать полный адрес? Очень просто, достаточно разбить память на блоки меньшей длины. И указывать где то номер блока, с которым мы работаем. А в самой машинной инструкции потребуется только указать номер ячейки в блоке, что требует меньше места.
Но это же лишняя работа и дополнительные затраты памяти команд! Не совсем так. Дело в том, что данные, которые обрабатывает программа, занимают не так много места и группируются в памяти в виде отдельных "островков". И это касается не только обработки массивов, но и в целом локальных переменных. А значит, указав один раз номер блока памяти мы можем в дальнейшем работать только с ячейками внутри него. И требуемый объем памяти программ уменьшится.
Блоки, на которые разбивается единое пространство памяти называют страницами. Думаю, многим это понятие знакомо. Как минимум, по процессорам 80х86. Существует и еще одно название - банк. Иногда, блоки памяти программ называют страницами, а блоки памяти данных банками.
Что то не то с этой иллюстрацией? Вы правы! Здесь показан сам принцип деления адреса на две части, но не понятно, в чем же тут выгода. Действительно, ведь длина адреса, если судить по иллюстрации не изменилась. Дело в том, что полный адрес физической ячейки памяти на самом деле формируется так
Теперь у нас логический адрес задаваемый в машинной инструкции может быть коротким, а недостающая часть адреса хранится в специальном регистре номера страницы.
Мы сэкономили немного памяти программ, но получили усложнение в работе с памятью. Теперь, последовательно увеличивая адрес ячейки памяти, мы можем перемещаться только внутри страницы. Так? Почти.
Для памяти данных это, обычно, действительно так. А вот для памяти программ, обычно, последовательный перебор адресов (последовательное выполнение инструкций) позволяет игнорировать границы страниц.
Реализовано это очень просто, счетчик-указатель адреса машинной инструкции (РС) выполнен как единое целое, а при выполнении инструкции перехода в него записывается новое значение адреса, которое формируется из поля инструкции и содержимого регистра номера страницы. Таким образом инкремент регистра РС при последовательном выполнении инструкций выполняется как единого целого, а вот размер поля адреса в командах переходов можно сократить.
Пара слов о реальных микроконтроллерах
Довольно подробно этот вопрос я буду рассматривать позже, отдельно для разных линеек микроконтроллеров. А сейчас упомяну об этом буквальной парой слов, что бы стало понятно, что сегодня мы обсуждали не нечто отвлеченное.
В микроконтроллерах STM8 адресация линейная. Причем, как я уже упоминал в прошлый раз, они имеют логическое единое адресное пространство. Поле адреса в инструкциях этих микроконтроллеров имеет переменную длину, но страничная адресация, в классическом виде, там не используется. А вот как быть с недостающей частью адреса я расскажу когда буду рассматривать эти микроконтроллеры. Но Flash память программ в этих микроконтроллерах организована в виде страниц, состоящих из блоков, состоящих из отдельных байт. И это нужно учитывать при записи в память программ.
В 8-разрядных микроконтроллерах PIC Microchip используется страничная организация памяти. Причем в разных линейках ее организация отличается. Причем блоки памяти данных там называются банками, а блоки памяти программ страницами. Ну а "чтобы жизнь медом не казалась" многие (а иногда и все) банки памяти данных включают в себя и специальные регистры. Мы с этим тоже будем разбираться.
В микроконтроллерах AVR Atmel (Microchip!) используется линейная адресация, но там свои тонкости. А запись с память программ тоже выполняется блоками.
Заключение
Мы почти (но только почти!) заканчиваем разбираться с организацией памяти в микроконтроллерах. В следующей статье я расскажу о методах адресации операндов в машинных инструкциях. После чего будут статьи с более подробным описанием памяти в различных реальных микроконтроллерах. Ну а там и до системы машинных инструкций доберемся. А дальше будет попроще.
До новых встреч!