Вообще-то оригинальный проект Amber уже имел встроенный контроллер DDR. Я же его за ненадобностью выбросил, потому, что на нашей плате Марсоход2 стоит не DDR память, а SDRAM. Память SDRAM, конечно, проще, чем DDR. Она не требует отдельного питания и выделения специальных банков выводов с микросхемы ПЛИС. Однако, теперь у нас есть проблема – как подключить SDRAM к системе?
Не будем изобретать велосипед, а возьмем какой-нибудь готовый контроллер с opencores.org. Я выбираю вот этот:
Похоже это то, что нужно:
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.
В консоли последовательного порта появляется вот такое «меню». Это, в отличии от программы hello-world, уже интерактивное приложение. Набирая команду «w» можно писать в память, а команду «d» читать и отображать блок памяти. На картинке видно, что я записал число 0x55667788 по адресу 0x80004 и потом посмотрел область с адреса 0x80000.
Но и это еще не все! Теперь можно через последовательный порт загрузить программу в память устройства. Для этого и нужен терминал TeraTerm. Он позволяет передавать файлы по протоколу XMODEM w/1k на наше устройство. В boot-loader как раз и реализован этот протокол.
Набираем в консоли команду «l» и boot-loader начинает ожидать файл из COM порта. В меню программы TeraTerm выбираем пункты File => Transfer => XMODEM => Send
Далее выбираем файл, который желаете передать и не забудьте поставить флажек w/1k – это видимо разновидность протокола передачи.
Я выбираю программу, скомпилированную ранее - 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" - это видимо какая-то разновидность протокола передачи.
Все - программа загружена:
Ее легко запустить на устройстве, набрав команду "j 8000"
Вот так, я подключил память SDRAM к системе. Пока еще не могу с уверенностью сказать, что там все работает превосходно - нужно бы посмотреть на временные диаграммы. Однако уже сейчас видно, что система становится более функциональной.
Весь проект с изменениями есть на нашем GitHub разделе - вот тут можно взять TAG с проектом https://github.com/marsohod4you/Amber-Marsohod2/zipball/sdram-attached
Подробнее...