|
|
  |
Контроллер памяти DDR2 для Altera Cyclone, размышления на тему о предельной частоте |
|
|
|
Jun 27 2011, 12:38
|
Знающий
   
Группа: Участник
Сообщений: 881
Регистрация: 21-03-10
Из: _// \\_
Пользователь №: 56 107

|
Цитата Тут уже трое берутся написать свой DDR2 контроллер. ну, не так. Двое подумывают, а я то его уже практически написал. Вот времянка памяти из моделсима. Это срез сигналов на выводах памяти. Частота 175 МГц. Тест пока довольно простой, записываются 4 слова подряд, затем они читаются назад. Это продолжается в цикле по всей памяти. На диаграмме показан переход от чтения к записи. Модель памяти взята у микрона, а контроллер синтезирован для EP3C16F484C8. Контроллер занял 575 LCELLs и 352 регистра. Осталось доделать прием данных с шины памяти. Несколько комментариев к времянке. Вопреки сомнениям скептиков на ней хорошо видно, что окошки данных там ОГРОМНЫЕ, туда "боинг можно посадить". При этом, DQS вообще всегда стоит посередине данных, потому что они синхронизированы по общему удвоенному клоку, только DQ переключаются по заднему фронту, а DQS - по переднему. Времянка потянула бы и бОльшие частоты, если бы не ограничения самого циклона на скорость IOE. Сравните это, например, со стандартным ALTMEMPHY, где времянка DQS базируется на фазовом сдвиге PLL, а мультиплексор в IOE вносит фиксированную задержку, которая мешает свободно масштабировать частоту, не ковыряя существенно фазы всех клоков. В данном случае используется всего 3 клока: 1) основной клок на 175 Мгц, 2) удвоенный клок на 350 МГц, сфазированный с основным, 3) внешний клок 175 МГц, сдвинутый относительно основного по фазе так, чтобы соответствовать окну данных. Фактически нужно играть только одним этим клоком, и это абсолютно тривиально.
Сообщение отредактировал Hoodwin - Jun 27 2011, 12:50
Эскизы прикрепленных изображений
|
|
|
|
|
Jun 30 2011, 13:27
|
Знающий
   
Группа: Участник
Сообщений: 881
Регистрация: 21-03-10
Из: _// \\_
Пользователь №: 56 107

|
Интерфейс такой: Код ready : out std_logic; -- controller is ready to accept command ad : in std_logic_vector(CTL_WIDTH_ADDR-1 downto 0); wr : in std_logic; -- write strobe wr_tag : in std_logic_vector(TAG_WIDTH-1 downto 0); di : in std_logic_vector(MEM_WIDTH_DATA*2-1 downto 0); di_ena : in std_logic_vector(MEM_WIDTH_DATA/4-1 downto 0); di_get : out std_logic; -- input data read (e.g. from FIFO) di_tag : out std_logic_vector(TAG_WIDTH-1 downto 0); rd : in std_logic; -- read strobe rd_tag : in std_logic_vector(TAG_WIDTH-1 downto 0); do : out std_logic_vector(MEM_WIDTH_DATA*2-1 downto 0); do_put : out std_logic; -- output data vaild do_tag : out std_logic_vector(TAG_WIDTH-1 downto 0); Пояснения- ready - сигнал готовности интерфейса к приему команды rd или wr. При любый условиях после приема команды ready пропадает как минимум на один такт, что позволяет делать мультиплексор входной команды и адреса, дающий задержку в 1 такт. Такая задержка неизбежно возникнет. если будет потребность в скоростном многопортовом доступе к памяти. - ad - адрес текущей операции. - wr - признак команды записи. Не должен выдаваться одновременно с rd. - rd - признак команды чтения. Не должен выдаваться одновременно с wr. - wr_tag - метка команды записи. См описание меток ниже. - rd_tag - метка команды чтения. См описание меток ниже. - di - данные для записи. Считываются при активном стробе чтения di_get. - di_ena - признаки разрешения на обновление данных. Управляют маской при записи. значение '1' разрешает запись соответствующего байта. - di_get - строб чтения. Возникает через некоторое время после того, как контроллер принял команду записи. Длится два такта для BL=4, и 4 такта для BL=8. - di_tag - метка входных данных. Выдается синхронно с di_get и позволяет установить, от какого источника фактически нужны данные в данный момент. - do - данные, прочитанные из памяти, активны под стробом do_put. - do_put - строб готовности данных чтения. Возникает некоторое время после того, как контроллер принял команду чтения. Длится два такта для BL=4, и 4 такта для BL=8. - do_tag - метка выходных данных. Выдается синхронно с do_put и позволяет установить, кому предназначаются данные в данный момент. Общий принцип работы такой. Контроллер устанавливает сигнал ready и ожидает команды. Команда считается принятой, если в одном такте либо ready='1' и rd='1', либо ready='1' и wr='1'. В этом такте должны вместе с командой должны быть указаны также адрес ad, метка чтения rd_tag (rd='1') или метка записи (wr='1'). Приняв команду контроллер снимает ready и планирует команду на внешнем интерфейсе, параллельно проверяя всякие внутренние события: необходимость делать precharge, активацию новой строки, ожидание на переключение направления шины, регенерацию и т.п. Как только внутреннее состояние контроллера доберется до выдачи непосредственной команды на шину, контроллер формирует сигнал di_get для команды записи и забирает данные пользователя, которые затем попадают на шину. При команде чтения с некоторой задержкой формируется строб do_put. МеткиПролистывал документ от Altera, не видал у них такой фичи. Но себе сделал. потому как удобно. Часто память требует разделения между разными потребителями. Например, у меня видеопамять требует доступа на чтения со стороны подсистемы формирования развертки, полного доступа стороны процессора, а также, в перспективе, доступа на запись со стороны видео-ускорителя. Конвейерный характер доступа к памяти, а также довольно большие задержки конвейера приводят к тому, что команды разных потребителей порождают непредсказуемо задержанные фазы передачи данных и для чередования потребителей в такой ситуации приходится ждать, пока контроллер полностью освободит не только командную шину (rd, wr, ad), но и шины данных (di и do). Проверка этих условий весьма утомительна, так как, вообще говоря, занятость шины данных неизвестна. Можно было бы учитывать, сколько команд выдано. и сколько потом фаз данных состоялось, но это муторно, это не наш метод. Вместо этого контроллер позволяет метить команды с помощью тегов и гарантирует, что система тэговых задержек синхронизирована с шиной самой памяти, так что в итоге разные потребители могут просто свалить в кучу все команды и получат назад пакет данных, в котором выходные данные помечены ровно так же как и входная последовательность команд. например, если читатель выдал команду rd='1', rd_tag=2, то его данные пойдут под стробом do_put and do_tag=2, причем совершенно не важно, с какой задержкой возникнет строб. Это достаточно удобно. Вам просто достаточно сделать автомат, планирующий команды и правильно подключить источники/приемники данных. В особенности это хорошо сочетается с FIFO, поскольку там нет явного адреса, но и с чтением в память тоже сочетается без сложностей. В заключение привожу времянку, как это выглядит. Обратите внимание на движение rd_tag -> do_tag & do_put и на wr_tag -> di_tag & di_get.
Эскизы прикрепленных изображений
|
|
|
|
|
Jul 1 2011, 07:53
|
Местный
  
Группа: Свой
Сообщений: 375
Регистрация: 9-10-08
Из: Таганрог, Ростовская обл.
Пользователь №: 40 792

|
Цитата(Hoodwin @ Jun 30 2011, 17:27)  Метки Пролистывал документ от Altera, не видал у них такой фичи. Но себе сделал. потому как удобно. Часто память требует разделения между разными потребителями. Вообще-то Альтера гарантирует, что последовательность команд на входе контроллера будет сохранена при обращении к памяти. Вы эту работу перекладываете на пользователя? Тоесть если на локальном интерфейсе я сперва заказал чтение адреса Х, а потом записал по тому же адресу, то возможна ситуация, когда данные сперва запишутся, а потом прочитаются? В принципе интерфейс понятен и без особых проблем ложится на стандартные локальные интерфейсы. А Метки вообще можно прикрутить к AMBA AXI, что ускорит обмен на интерконнекте.
--------------------
Глупцы игнорируют сложность. Прагматики терпят ее. Некоторые могут избегать ее. Гении ее устраняют.
|
|
|
|
|
Jul 1 2011, 07:55
|
Знающий
   
Группа: Участник
Сообщений: 835
Регистрация: 9-08-08
Из: Санкт-Петербург
Пользователь №: 39 515

|
Может быть, имеет смысл разделить на отдельные модули командный блок и блок передачи данных? Иначе этот контроллер будет Cyclone 3 only. Блок передачи данных нельзя нормально сделать архитектурно независимым, зато он достаточно тривиален по логике и его можно спокойно портировать. А командный блок по логике нетривиален, зато архитектурно независим. Если контроллер не изменяет порядок выполнения команд относительно порядка запросов, то стоит также вынести в отдельный модуль управление тэгами. Полезно предусмотреть команды с автозакрытием банка - так будет работать быстрее, например, для каналов DMA, делающих запросы не очень часто. Что касается подключения к шинам типа Avalone, Wishbone и тд, то мосты для них могут написать те, кому это больше всех надо  , и потом можно добавить к проекту.
|
|
|
|
|
Jul 1 2011, 09:09
|
Знающий
   
Группа: Участник
Сообщений: 881
Регистрация: 21-03-10
Из: _// \\_
Пользователь №: 56 107

|
Цитата(warrior-2001 @ Jul 1 2011, 11:53)  Вообще-то Альтера гарантирует, что последовательность команд на входе контроллера будет сохранена при обращении к памяти. Вы эту работу перекладываете на пользователя? Нет, не так. Последовательность команд и у меня сохраняется, но только это та последовательность, которая имеется на входе контроллера. Просто если эти команды формируют разные потребители, скажем, процессор, и контроллер DMA, то им трудно отслеживать, например, кто именно в данный момент должен давать данные на шину памяти, там же конвейер. Тэги идут по конвейеру так же как и команда, поэтому они позволяют быстро разобраться, кто должен давать данные или получать их. Если же вопрос был о том, что будет если процессор и контроллер DMA копошатся в одних и тех же адресах, то это действительно проблема пользователя, как там выстроить общий порядок команд к памяти. Но это всеобщая проблема. Имея довольно большой опыт с DMA в DSP от TI, pci bus mastering и т.п. могу сказать, что нигде такое разделение не делается на уровне контроллера доступа к шине. Обычно софт следит за разделением адресных пространств для разных подсистем. Цитата Тоесть если на локальном интерфейсе я сперва заказал чтение адреса Х, а потом записал по тому же адресу, то возможна ситуация, когда данные сперва запишутся, а потом прочитаются? Нет, такого не будет никогда. Timmy Можно подробнее, что за блоки? И какие связи между ними, а то я что-то пока у себя не заметил. Если имеется ввиду выделить какой-то интерфейс для IO шины данных, то я думал об этом, но пока не получилось придумать такой его интерфейс, чтобы он легко реализовывался, скажем и на fast output register + double i/o clock rate, и на DDR I/O cell за счет ее внутренних свойств. Там сразу все разъезжается по времени, меняются принципы настройки. Кроме того, я не думаю, что у Cyclone какие-то выдающиеся особенности в соотношении между предельной частотой внутри кристалла и на I/O. Даже, я бы сказал, совсем не выдающиеся. Предельная частота I/O ниже частоты внутри кристалла. Пока что все примерно сбалансировано, и частота контроллера примерно вдвое ниже частоты I/O после разводки. На более продвинутых семействах, скажем, на Stratix, где уже LUT имеет 6 входов, а не 4, и где более сложная комбинаторика будет иметь тенденцию к ускорению, частота контроллера станет больше, чем половина частоты I/O, но производительность упрется в предельную частоту I/O cell. Но мы и сейчас в нее упираемся в Cyclone 3. Если где-то это окажется не так, и контроллер будет тормознее, чем половина частоты I/O, вот тогда и будем думать, как его оптимизировать. Управление тэгами нельзя вынести в отдельный блок, просто потому что тэги - это тот же самый конвейер команд, только напичканный не командами rd, wr, а пользовательскими битами. нет никакого смысла делать это отдельно. Можно не использовать тэги, если у используется ровно один писатель и ровно один читатель памяти, но тогда они просто вымрут при компиляции. О командах с автозакрытием банка (а именно write/read with auto precharge) я думал, но при реализации они как-то не востребовались. Они усложняют логику команд, экономят в общей сложности 1% тактов по сравнению с отдельным precharge, но зато рубят частоту контроллера из за усложнения логики блокировки последующей команды, оно надо? Кроме того, что плохого в том, что банк после DMA останется активным? Вдруг в него кто-то еще раз обратится потом, и попадет в тот же ряд? Возможно, это имеет смысл с точки зрения низкого энергопотребления, но хочу заметить, что раз в 3.9-7.8 мкс и так возникает событие регенерации памяти, которое требует закрыть вообще все банки. Если после этого никто в них не лезет, то они и останутся закрытыми с низким потреблением.
|
|
|
|
|
Jul 4 2011, 10:50
|
Местный
  
Группа: Свой
Сообщений: 371
Регистрация: 24-07-05
Из: Москва
Пользователь №: 7 056

|
Докладываю о своих результатах. Сделана инициализация и запись с Auto Precharge. Тактовая частота 265 МГц (не DDR) в EP3SE50F780C3. Осталось: 1. Доделать чтение. 2. Написать констрейны. 3. Проверить на железе. 4. Оптимизировать под различные виды записи и чтения (потоковая и побайтная).
Эскизы прикрепленных изображений
|
|
|
|
|
Jul 4 2011, 12:31
|
Знающий
   
Группа: Участник
Сообщений: 881
Регистрация: 21-03-10
Из: _// \\_
Пользователь №: 56 107

|
Судя по картинке, это пока чистая модель, откуда частота 265 МГц? Или это проверка на собираемость просто? Еще не увидел на картинке собственно модели памяти DDR2. Там c реальной моделью много интересного еще может повылезать. 1,2. Чтение, кстати, интересный дало эффект. Оказалось, что аннотация реальных задержек в кристалле приводит к сдвигу приема данных с шины на 1 такт. В итоге, поведение модели до сборки и после сборки отличается. В пятницу проверял на железе, уперся в этот факт. Сижу вот теперь, приделываю обманку для этого. 3. А перед тем как на железе проверять, хорошо бы выдать vho + sdf и посмотреть, что за времянка получается. 4. Побайтная запись не интересна на самом деле, нужно потоковую обеспечивать. Во всяком случае, я бы выбрал потоковую... P.S. А еще есть рефреш, который тоже не худо бы сделать
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|