Рис. 1. Micron MT48LC4M16A2-75
Большинство современных микросхем программируемой логики имеет некоторый объём внутренней памяти, удовлетворяющий минимальные потребности разработчика. Например, в ПЛИС Cyclone III, на которой базируется плата Марсоход2, есть около 51 Кбайт собственной памяти. Этого достаточно, например, для реализации простого текстового VGA-режима, однако для полноценного кадрового буфера, который бы хранил информацию о цветовых величинах каждого отдельного пикселя, памяти ПЛИС явно мало. Тут на помощь разработчику приходят внешние запоминающие устройства различного типа.
На плате Марсоход2 установлена микросхема динамического ОЗУ (SDRAM) ёмкостью 8 Мбайт. К сожалению, по сравнению со статической памятью ПЛИС управлять этой микросхемой сложнее: требуется специальный контроллер, для самой SDRAM характерны задержки, которые необходимо учитывать разработчику, а если долго не обращаться к записанным в ОЗУ данным, они начинают деградировать и могут быть безвозвратно утеряны. Но так ли всё сложно на самом деле? В этой статье я попытаюсь рассказать о том, что представляет из себя динамическое ОЗУ и как им следует управлять на примере платы Марсоход2 и установленной на ней микросхемы Micron MT48LC4M16A2-75 (Рис. 1.).
Важным отличием памяти SDRAM от SRAM является другая система адресации. Если статическую память можно представить в виде очень длинного одномерного массива слов, то в динамическом ОЗУ используется матричная организация, то есть, слово выбирается по строке и столбцу. Для повышения быстродействия микросхема SDRAM обычно разделяется на несколько таких матриц, называемых "банками". Такой подход обусловлен тем, что некоторые операции могут занимать продолжительное время, поэтому контроллер может работать с одним банком, ожидая другого.
Таким образом, адрес слова складывается из количества банков, а также количества строк и столбцов в одном банке. У микросхемы MT48LC4M16A2-75 есть четыре банка (2 бита адресной шины), представляющих собой массив из 4096 на 256 слов (соответственно ещё 12 и 8 бит адресной шины), состоящих из 16 бит каждое. Что характерно, адрес передаётся в микросхему за несколько шагов: сначала отправляется адрес банка и строки, затем контроллер должен выждать некоторое время до того, как нужная строка будет активирована, и только тогда контроллер получает доступ к чтению и записи слов из этой самой строки. Перед активацией новой строки контроллер должен закрыть предыдущую. Поскольку на открытие и закрытие строки тратится время, для оптимизации быстродействия с активной строкой следует выполнить как можно больше операций (пакетная запись или пакетное чтение). Некоторые контроллеры умеют открывать несколько банков, что, как упоминалось выше, позволяет сократить время ожидания на промежуточные действия.
Если мы посмотрим на интерфейс нашей микросхемы SDRAM, мы обнаружим 12-разрядную шину, по которой передаются адреса строк и столбцов, а также двухразрядную шину указателя банка. Кроме того, особого внимания заслуживают сигналы wren, cas и ras (с активным нулём), по которым в микросхему передаются команды. О системе команд динамического ОЗУ следует рассказать отдельно.
Основными командами являются:
- NOP – "no operation", указывает микросхеме, что в настоящий момент никаких действий не требуется;
- ACTIVE – выбор банка и активация строки по установленному адресу;
- READ – начало чтения из активной строки, установленный адрес указывает на столбец; бит A10 даёт команду автоматического закрытия строки после завершения цикла чтения;
- WRITE – начало записи в активную строку, установленный адрес указывает на столбец; бит A10 даёт команду автоматического закрытия строки после завершения цикла записи;
- PRECHARGE – закрытие активной строки;
- REFRESH – обновление данных в ячейках памяти для предотвращения их деградации и необратимого повреждения;
- LOAD MODE REGISTER – эта команда позволяет загрузить в микросхему различные настройки (главным образом, задержки и длину пакета) посредством адресной шины.
Важно, что команда NOP разрешена всегда, в то время как LOAD MODE REGISTER требует, чтобы все банки были неактивны, а после её выполнения микросхема на какое-то время будет недоступна для выполнения других операций. Также работает и команда REFRESH, а операции READ, WRITE и PRECHARGE могут выполняться только тогда, когда активен хотя бы один из банков. С более подробной информацией о системе команд SDRAM можно ознакомиться, например, в соответствующей статье википедии или в руководстве Micron к используемой микросхеме (
Говоря о динамическом ОЗУ, нельзя не упомянуть о свойственных этой памяти задержках. Ключевой характеристикой зачастую называется CAS-задержка: время в тактах синхрочастоты от момента передачи контроллером микросхеме памяти адреса столбца до появления на информационной шине микросхемы соответствующих данных. Важно, что желаемая строка уже должна быть активна, иначе время задержки увеличивается. RAS-задержка характеризует время от обращения к памяти до момента появления данных на шине. Ещё одним показательным параметром является RC-задержка – время цикла, или характеристика того, как быстро может быть произведено два последовательных доступа к данным.
В изучение архитектуры SDRAM можно погрузиться значительно глубже, но изложенного выше уже достаточно для попытки создания простейшего контроллера (Рис. 2), что и является целью моего проекта. Контроллер должен обладать двумя главными качествами, а именно: простотой в использовании и поддержкой пакетной записи и пакетного чтения. Для начала попробуем разобраться с его интерфейсной частью:
Контроллер SDRAM. Рис.2.
У контроллера обязательно должны быть входы адресных шин rd_adr и wr_adr шириной 22 разряда, шины записываемых данных wr_data (16 разрядов), запросов на чтение rd_req и запись wr_req, а также, разумеется, вход для сигнала синхрочастоты. Выходы – шина rd_data на 16 разрядов и сигнал rd_valid, высокий уровень которого показывает готовность новых данных. Плюс ряд выводов непосредственно на микросхему памяти динамического ОЗУ. Кстати говоря, шина данных sdram_dq двунаправлена, поэтому на стороне ПЛИС её следует переводить в высокоимпедансное состояние, когда не производится операций записи. Это позволяет беспрепятственно считывать данные с шины.
Работа контроллера будет представлена в виде автомата. Графически его наиболее простая форма выглядит так:
Граф работы SDRAM контроллера. Рис.3.
Но, если предполагается работа памяти на высокой частоте (например, не менее 100 МГц), для борьбы с задержками, возможно, придётся добавить в автомат новые состояния. Кроме того, если пользовательская логика не может гарантировать регулярный доступ ко всем данным ОЗУ, нужно предусмотреть периодический запуск команды регенерации. Таким образом, более полный граф будет таким:
Расширенный граф работы SDRAM контроллера. Рис.4.
Дабы не утомить читателя, я не стану приводить описание контроллера на VHDL.
Исходные данные этого проекта можно загрузить по этой ссылке:
Между тем, чтобы убедиться в том, что логика контроллера устроена верно, его следует загнать в симулятор, и проследить за поведением различных сигналов. В качестве симулятора выступит приложение с удивительно "дружелюбным" и "интуитивно понятным" интерфейсом ModelSim, бесплатная версия которого входит в стандартный комплект поставки Quartus II.
Наверное, контроллер следовало бы рассматривать в совокупности с моделью памяти, используемой на плате Марсоход2 (соответствующее описание на языке Verilog предоставляет компания Micron – разработчик микросхемы). Но симулятор с этим оказался категорически не согласен, мотивируя свою точку зрения массой ограничений, свойственных бесплатной версии ModelSim. Я полагаю, доказательством работы контроллера будет установка правильных выходных сигналов на выводах, предназначенных для подключения к интерфейсу SDRAM, в зависимости от входных данных (запросов на чтение и запись, значений адресов, например). Если контроллер будет выдавать правильные сигналы, у нас не останется оснований полагать, что он не сможет управлять любой микросхемой динамического ОЗУ, соответствующей отраслевым стандартам.
На временных диаграммах ниже представлены все сигналы, существующие в контроллере – не только входные и выходные, но и ряд внутренних. Хотя иллюстрация особой наглядностью не отличается, она даёт возможность понять, что происходит внутри рассматриваемого объекта на каждом этапе его работы. Я проводил функциональную симуляцию, которая представляет собой не более чем тестирование логики.
Первая ситуация – запрос на запись wr_req активен, на входах wr_data и wr_adr установлены какие-то величины, запрос на чтение rd_req отсутствует. Мы видим, что команда на запись и данные на выходе sdram_dq появляются спустя два такта после подачи первого тестового воздействия. При этом видно, что автомат внутри контроллера меняет своё состояние, открывая банк и строку по запрошенному адресу. Меняя на входе адрес и данные, мы получаем соответствующие изменения на выходе, причём на изменение адреса банка или строки контроллер реагирует должным образом.
Запись данных (кликнуть, чтобы увеличить изображение). Рис.5.
Теперь попробуем аналогичные воздействия, но с запросом на чтение. Кстати говоря, операциям чтения контроллер отдаёт приоритет, поэтому при активном запросе на запись и чтение исполняться будет именно последний. Также на диаграмме можно разглядеть, что на переключение банка и строки контроллер тратит два такта, а успеет ли микросхема отреагировать на запрос вовремя зависит уже от задержек конкретного решения.
Чтение данных (кликнуть, чтобы увеличить изображение). Рис.6.
Статья получилась несколько сумбурной и, вероятно, не слишком наглядной. Реальные испытания контроллера – в составе VGA-модуля с кадровым буфером – показали его работоспособность, но об этом проекте я расскажу в одной из следующих статей. Как обычно, проект можно обсудить в комментариях – с вашей помощью статьи могут стать интереснее и нагляднее.
P.S.: Читайте продолжение истории здесь: Симуляция контроллера SDRAM в ModelSim.
P.P.S.В статье, посвящённой симуляции моей реализации контроллера динамической оперативной памяти совместно с моделью микросхемы SDRAM, предоставленной компанией Micron, Николай обнаружил ряд нежелательных и потенциально критических ошибок. Все они вызваны тем, что в контроллере не предусмотрена процедура начального запуска микросхемы, которая подразумевает подачу определённой последовательности команд и загрузку настроек в регистр режима (Mode Register). Хотя работа с микросхемой возможна и без инициализации – настройки берутся из некоего состояния "по умолчанию" – нарушать рекомендации производителя всё же не стоит, поэтому я немного модернизировал контроллер, научив его инициализировать микросхему ОЗУ.
Таким образом, в автомате, на котором базируется работа контроллера, появилось ещё одно состояние. В нём контроллер находится в момент включения, и только после прохождения процедуры инициализации переходит в состояние idle (см. иллюстрации выше). Кроме того, появился настраиваемый параметр sdram_frequency, исчисляемый в МГц, на основе которого рассчитывается размерность счётчика, запускающего команды инициализации, и выход ready, высокое состояние которого указывает на то, что начальные процедуры выполнены. Пока на выходе ready сохраняется низкое логическое состояние, контроллер не будет воспринимать информацию с входов (за исключением тактового сигнала).
С процедурой инициализации можно ознакомиться в руководстве к микросхеме памяти. После исправлений ошибки, обнаруженные Николаем, больше не возникают. Обновлённую версию контроллера можно получить по этой ссылке.
Добавлю ещё примерный алгоритм работы с контроллером:
- все операции возможны только после инициализации микросхемы (обнаруживается по высокому состоянию выхода ready);
- для записи данных нужно:
- установить запрос на запись (wr_req = '1'), убедившись, что неактивен запрос на чтение (rd_req = '0');
- одновременно подать адрес первого записываемого слова на вход wr_adr и само слово на wr_data;
- информация с входов wr_adr и wr_data при активном wr_req считывается каждый такт синхронизирующего сигнала;
- слово будет записано в память с задержкой на 2 такта после его "принятия" контроллером;
- при пакетной записи (в пределах одной строки) поток входной информации может быть непрерывным (каждый такт – новое слово и новый адрес);
- при изменении адреса банка или строки контроллер должен закрыть активную строку и открыть новую, поэтому в этом случае перед подачей новых данных следует выждать 3 такта;
- для чтения данных нужно:
- установить запрос на чтение (rd_req = '1'), активность запроса на запись в этом случае не играет роли;
- одновременно подать адрес rd_adr читаемого из памяти слова;
- адрес rd_adr считывается контроллером каждый такт синхронизирующего сигнала;
- готовность считанных из памяти данных на выходе rd_data обнаруживается по активности выхода rd_valid, задержка чтения составляет 4 такта;
- при пакетном чтении поток входной информации может быть непрерывным;
- при изменении адреса банка или строки контроллер должен закрыть активную строку и открыть новую, поэтому в этом случае перед подачей новых данных следует выждать 5 тактов.
P.P.P.S.: еще статьи на сайте https://marsohod.org связанные и использованием памяти SDRAM:
1) Фоторамка. Часть3. Фреймбуффер.
2) Тест SDRAM или Фреймбуффер2.
3) Поключение SDRAM к системе на кристалле Amber
Подробнее...