Поключение SDRAM к системе на кристалле Amber

Вообще-то оригинальный проект Amber уже имел встроенный контроллер DDR. Я же его за ненадобностью выбросил, потому, что на нашей плате Марсоход2 стоит не DDR память, а SDRAM. Память SDRAM, конечно, проще, чем DDR. Она не требует отдельного питания и выделения специальных банков выводов с микросхемы ПЛИС. Однако, теперь у нас есть проблема – как подключить SDRAM к системе?

Не будем изобретать велосипед, а возьмем какой-нибудь готовый контроллер с opencores.org. Я выбираю вот этот:

OpenCores sdram controller

Похоже это то, что нужно:

Description

Feature:
• 8/16/32 Configurable SDRAM data width
• Wish Bone compatible
• Application clock and SDRAM clock can be async
• Programmable column address
• Support for industry-standard SDRAM devices and modules
• Supports all standard SDRAM functions
• Fully Synchronous; All signals registered on positive edge of system clock
• One chip-select signals
• Support SDRAM with four bank
• Programmable CAS latency
• Data mask signals for partial write operations
• Bank management architecture, which minimizes latency
• Automatic controlled refresh
• Static synchronous design
• Fully synthesizable

Выкачал исходники с сайта OpenCores (я говорил, что там много полезного можно найти) и положил их в папку нашего проекта Amber hw/vlog/sdram.

Что дальше? Добавил в мой проект системы Amber для Quartus II в файл msystem.qsf ссылки на добавленные файлы:

set_global_assignment -name VERILOG_FILE ../vlog/sdram/sdr_ctrl/trunk/rtl/lib/sync_fifo.v

set_global_assignment -name VERILOG_FILE ../vlog/sdram/sdr_ctrl/trunk/rtl/lib/async_fifo.v
set_global_assignment -name VERILOG_FILE ../vlog/sdram/sdr_ctrl/trunk/rtl/wb2sdrc/wb2sdrc.v
set_global_assignment -name VERILOG_FILE ../vlog/sdram/sdr_ctrl/trunk/rtl/top/sdrc_top.v


Расширил пути поиска для компиляторов Quartus II:

set_global_assignment -name SEARCH_PATH ../vlog/sdram/sdr_ctrl/trunk/rtl/core

Добавил новый макрос для Verilog:

set_global_assignment -name VERILOG_MACRO "SDRAM=1"

Кроме этого, изменил Verilog модуль верхнего уровня msystem.v:

  • Добавил входы и выходы SDRAM.
  • Подключил к шине Wishbone модуль контроллера памяти sdrc_top.
  • Назначил параметры контроллера SDRAM.

Вот такое включение модуля контроллера памяти SDRAM


`ifdef SDRAM
//system with SDRAM
assign s_wb_err[2] = 0;
wire sdr_init_done;
assign phy_init_done = sdr_init_done;
assign sdr_clk = mem_clk;

sdrc_top #(.SDR_DW(16),.SDR_BW(2)) u_sdram(
 .cfg_sdr_width      (2'b01              ), // 16 BIT SDRAM
 .cfg_colbits        (2'b00              ), // 8 Bit Column Address

 /* WISHBONE */
 .wb_rst_i           (sys_rst),
 .wb_clk_i           (sys_clk            ),
 .wb_stb_i           (s_wb_stb[2]        ),
 .wb_ack_o           (s_wb_ack[2]        ),
 .wb_addr_i          (s_wb_adr[2]>>2     ),
 .wb_we_i            (s_wb_we[2]         ),
 .wb_dat_i           (s_wb_dat_w[2]      ),
 .wb_sel_i           (s_wb_sel[2]        ),
 .wb_dat_o           (s_wb_dat_r[2]      ),
 .wb_cyc_i           (s_wb_cyc[2]        ),
 .wb_cti_i           (0),

 /* Interface to SDRAMs */

 .sdram_clk          (sdr_clk            ),
 .sdram_resetn       (~sys_rst           ),
 .sdr_cs_n           ( /*sdr_cs_n*/      ),
 .sdr_cke            ( /*sdr_cke*/       ),
 .sdr_ras_n          (sdr_ras_n          ),
 .sdr_cas_n          (sdr_cas_n          ),
 .sdr_we_n           (sdr_we_n           ),
 .sdr_dqm            (sdr_dqm            ),
 .sdr_ba             (sdr_ba             ),
 .sdr_addr           (sdr_addr           ),
 .sdr_dq             (sdr_dq             ),

 /* Parameters */

 .sdr_init_done      (sdr_init_done      ),
 .cfg_req_depth      (2'h3               ), //how many req. buffer should hold
 .cfg_sdr_en         (sdr_ena            ),
 .cfg_sdr_mode_reg   (12'h023            ),
 .cfg_sdr_tras_d     (4'h4               ), //44-120000ns
 .cfg_sdr_trp_d      (4'h2               ), //min 20ns
 .cfg_sdr_trcd_d     (4'h2               ), //min 20ns
 .cfg_sdr_cas        (3'h3               ), //cas latency in clocks, depends on mode reg
 .cfg_sdr_trcar_d    (4'h7               ), //min 66ns
 .cfg_sdr_twr_d      (4'h1               ), //write recovery time, 1ck+7,5/15ns
 .cfg_sdr_rfsh       (12'h100            ), //16 or 64 ms?
 .cfg_sdr_rfmax      (3'h6               )
 );
`else
 //assume memory always ready if no SDRAM
 assign phy_init_done = 1'd1;
`endif

 

Еще изменил модуль my_clocks_reset.v. В нем теперь синтезируется в PLL две тактовых частоты: 40Мгц для системы и 80Мгц для памяти SDRAM.

В процессе отладки проекта с помощью SignalTap и чтения документации на контроллер SDRAM (она есть в папке hw/vlog/sdram/sdr_ctrl/trunk/doc) выяснилось, что для успешной инициализации памяти необходимо подать на нее тактовую частоту и выждать около 100мкс. Только после этого контроллер может выдавать микросхеме памяти какие-то первые инициализационные команды. Я это не сразу понял. Сигнал cfg_sdr_en  на модуле контроллера как раз для этого. Теперь мой модуль my_clocks_reset сам выжидает како-то время и выдает контроллеру памяти sdrc_top сигнал разрешения работы cfg_sdr_en. Контроллер программирует микросхеаму памяти в некоторый заданный режим и после этого выдает сигнал sdr_init_done. Вот только после этого стартует процессор.

Еще одна проблема, которую я встретил – в настройках контроллера почему-то присутствуют дублирующие друг-друга настройки. Так, на вход cfg_sdr_mode_reg подается константа 12’h023 и вторая тетрада «2» обозначает CAS Latency – задержка готовности данных. Тем не менее есть еще один конфигурирующий сигнал cfg_sdr_cas который обозначает то же самое. Однако странно, чтобы все работало правильно нужно, чтобы cfg_sdr_cas был на единицу больше, чем указанное число в MODE регистре, определяемом сигналами cfg_sdr_mode_reg. Это довольно странно. Довольно много здесь времени потерял, пока понял, как надо сделать. MODE регистр в памяти SDRAM определяет режим работы микросхемы и должен быть проинициализирован в начале работы системы.

В самом процессоре в boot-rom я теперь помещяю не программу hello-world, как раньше, а специальную программу boot-loader. Я не сам ее писал, а взял оригинальную из папки sw/boot-loader. Я создал свою папку boot-loader-8M и скопировал все оригинальные файлы туда. Собственно изменение в этом проекте только одно. Нужно поставить стек программ под вершину нашей памяти в 8Мбайт.

Как и в случае с программой hello-world я компилирую boot-loader и преобразую полученный MEM файл в Altera MIF командами

make
../tools/mem2mif boot-loader.mem >> boot-loader.mif


В проекте Altera Quartus II в визарде для sram_2048_32_byte_en теперь указываю, что bootrom должен при старте инициализирован новым boot-loader.mif

В принципе это в общем и все.

Для дальнейших экспериментов нам понадобится программа терминала для последовательного порта TeraTerm. Я раньше пользовался Putty, но теперь рекомендую эту программу TeraTerm. Чуть позже объясню почему.

Теперь опишу как это работает. После компиляции проекта в Quartus II открываю в TeraTerm последовательный порт COM9 на скорости 921600, 1 стоп бит, без четности. Запускаю программатор Quartus II и загружаю проект в ПЛИС платы Марсоход2.

Boot-loader в плате Марсоход2

В консоли последовательного порта появляется вот такое «меню». Это, в отличии от программы hello-world, уже интерактивное приложение. Набирая команду «w» можно писать в память, а команду «d» читать и отображать блок памяти. На картинке видно, что я записал число 0x55667788 по адресу 0x80004 и потом посмотрел область с адреса 0x80000.

Но и это еще не все! Теперь можно через последовательный порт загрузить программу в память устройства. Для этого и нужен терминал TeraTerm. Он позволяет передавать файлы по протоколу XMODEM w/1k на наше устройство. В boot-loader как раз и реализован этот протокол.

xmodem

Набираем в консоли команду «l» и boot-loader начинает ожидать файл из COM порта. В меню программы TeraTerm выбираем пункты File => Transfer => XMODEM => Send

Далее выбираем файл, который желаете передать и не забудьте поставить флажек w/1k – это видимо разновидность протокола передачи.

Boot-loader в плате Марсоход2

Я выбираю программу, скомпилированную ранее - hello-world.elf - ELF это такой тип исполняемых файлов в Linux. Вот только такая тонкость. Помните, для первого запуска программы hello-world в плате Марсоход2 мне пришлось поменять базовый адрес программы с 0x8000 в 0x0000? Я тогда удивлялся, зачем автор так странно делает такой адрес, ведь там нет boot-rom? Так вот теперь и понятно - адрес 0x8000 как раз нужен, чтобы программу hello-world загрузить с помощью boot-loader в память устройства через последовательный порт протоколом XMODEM! В общем, программу hello-world нужно перекомпилировать установив базовый адрес программы назад в 0x8000 в файле sections.lds.

Выбирая файл не забудьте указать опцию в диалоговом окне терминала TeraTerm - "1K" - это видимо какая-то разновидность протокола передачи.

Все - программа загружена:

Boot-loader в плате Марсоход2

Ее легко запустить на устройстве, набрав команду "j 8000"

Вот так, я подключил память SDRAM к системе. Пока еще не могу с уверенностью сказать, что там все работает превосходно - нужно бы посмотреть на временные диаграммы. Однако уже сейчас видно, что система становится более функциональной.

Весь проект с изменениями есть на нашем GitHub разделе - вот тут можно взять TAG с проектом https://github.com/marsohod4you/Amber-Marsohod2/zipball/sdram-attached

 

 

 


Добавить комментарий