МАРСОХОД

Open Source Hardware Project

FPGA & CPLD блог

Все о плате Марсоход, Марсоход2, программировании ПЛИС, о Verilog HDL и среде проектирования Altera Quartus II

 

Презентация "Введение в программируемую логику Altera"

Компания “ЭФО”, официальный дистрибьютор Altera с 1995 года, опубликовала новую видео-презентацию "Введение в программируемую логику Altera".

Презентация предназначена  для разработчиков, начинающих использовать программируемые логические интегральные схемы (ПЛИС).

В данной презентации рассмотрены основные принципы работы программируемой логики и возможности средств разработки Altera.

 

Как я пытался наблюдать солнечное затмение

Вчера пришел на работу, думал заниматься текущими проектами и тут читаю, что оказывается 20-го марта солнечное затмение. Ну как можно работать в таких условиях, все бросил, решил попробовать посмотреть затмение через свой телескоп.

Да-да, знаю, это затмение уже всем надоело - все во всех соц. сетях про него уже сто раз написали. Ну и я напишу. 

Обновление для простого SDRAM контроллера

По давней академической традиции начало календарного года у большинства студентов совпадает с промежуточной аттестацией, которая является своеобразным итогом нескольких предыдущих месяцев напряжённой учёбы. Мне тоже пришлось на время забыть о Марсоходе и погрузиться в экзаменационную рутину, но, слава роботам, сессия закончилась и я могу вернуться к более приятным вещам.

Прежде всего, я решил подготовить небольшое обновление простого контроллера SDRAM. Основным недостатком существующей версии этого контроллера является отсутствие поддержки автоматической регенерации ячеек памяти – это незаметно, если регулярно обращаться ко всему объёму используемой оперативной памяти, однако, если требуется отложить данные "в долгий ящик", можно столкнуться с проблемой их постепенной порчи.

Производитель микросхемы памяти предусмотрел два способа регенерации, из которых задействовать можно только AUTO REFRESH (таковы особенности схемотехники платы Марсоход2). Micron рекомендует подавать 4096 команд AUTO REFRESH в течении 64 миллисекунд, причём каждая команда регенерирует какую-то одну строку соответственно внутреннему счётчику. Для применения команды необходимо, чтобы все активные строки и банки были закрыты, а после команды следует выждать некоторое время (66 наносекунд для микросхемы, установленной на Марсоход2).

Вот мой обновленный контроллер:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity sdram_controller is
	generic (
				--memory frequency in MHz
				sdram_frequency	: integer := 50
				);
	port (
			--ready to operation
			ready						: out std_logic := '0';
			--clk
			clk						: in std_logic;
			--read
			rd_req					: in std_logic;
			rd_adr					: in std_logic_vector(21 downto 0);
			rd_data					: out std_logic_vector(15 downto 0);
			rd_valid					: out std_logic;
			--write
			wr_req					: in std_logic;
			wr_adr					: in std_logic_vector(21 downto 0);
			wr_data					: in std_logic_vector(15 downto 0);
			--SDRAM interface
			sdram_wren_n			: out std_logic := '1';
			sdram_cas_n				: out std_logic := '1';
			sdram_ras_n				: out std_logic := '1';
			sdram_a					: out std_logic_vector(11 downto 0);
			sdram_ba					: out std_logic_vector(1 downto 0);
			sdram_dqm				: out std_logic_vector(1 downto 0);
			sdram_dq					: inout std_logic_vector(15 downto 0);
			sdram_clk_n				: out std_logic
			);
end entity;

architecture rtl of sdram_controller is


	signal adr						: std_logic_vector(21 downto 0)	 := (others => '0'); 			--selected address
	signal adr_reg					: std_logic_vector(21 downto 0)	 := (others => '0');				--selected address register
	signal state					: std_logic_vector(2 downto 0)	 := "100";							--state machine register
	signal sdram_cmd				: std_logic_vector(2 downto 0)	 := (others => '1');				--command register
	signal wr_data1				: std_logic_vector(15 downto 0)	 := (others => '0');				--write data pipe stage1
	signal wr_data2				: std_logic_vector(15 downto 0)	 := (others => '0'); 			--write data pipe stage2
	signal rd_pipe_valid			: std_logic_vector(3 downto 0)	 := (others => '0');	
	signal rd_now					: std_logic := '0';
	signal rd_selected			: std_logic := '0';
	signal wr_selected			: std_logic := '0';
	signal rd_cycle				: std_logic := '0';
	signal same_row_and_bank	: std_logic := '0';
	signal sdram_dq_oe			: std_logic := '0';					--output enable
	signal init_cnt				: integer := 0;						--initialization counter value
	signal refresh_cnt			: integer := 0;						--refresh counter
	
	--sdram commands
	constant cmd_loadmode		: std_logic_vector(2 downto 0) := "000";
	constant cmd_refresh			: std_logic_vector(2 downto 0) := "001";
	constant cmd_precharge		: std_logic_vector(2 downto 0) := "010";
	constant cmd_active 			: std_logic_vector(2 downto 0) := "011";
	constant cmd_write			: std_logic_vector(2 downto 0) := "100";
	constant cmd_read				: std_logic_vector(2 downto 0) := "101";
	constant cmd_nop				: std_logic_vector(2 downto 0) := "111";
	
	
	--Timing parameters
	constant tRAS			: integer :=	((sdram_frequency * 44)/1000)+1;		--ACTIVE-to-PRECHARGE command
	constant tRC			: integer :=	((sdram_frequency * 66)/1000)+1;		--ACTIVE-to-ACTIVE command period
	constant tRCD			: integer :=	((sdram_frequency * 20)/1000)+1;		--ACTIVE-to-READ or WRITE delay
	constant tRFC			: integer :=	((sdram_frequency * 66)/1000)+1;		--AUTO REFRESH command period
	constant tRP			: integer :=	((sdram_frequency * 20)/1000)+1;		--PRECHARGE command period
	constant tRRD			: integer :=	((sdram_frequency * 15)/1000)+1;		--ACTIVE bank a to ACTIVE bank b command
	constant tWR			: integer :=	((sdram_frequency * 15)/1000)+1;		--WRITE recovery time
	constant tINIT			: integer :=	(sdram_frequency * 10)+1;				--minimal initialization time
	constant tREF			: integer :=	((sdram_frequency * 1560)/1000)+1;	--REFRESH period (for row)
	
begin

	state_machine:process(clk)
	begin
		if rising_edge(clk) then
			case state is
				when "000" =>	if ((rd_req = '1') or (wr_req = '1')) then	--if read or write request 
										sdram_cmd <= cmd_active;						--then activate sdram
										sdram_ba <= adr(21 downto 20);				--and open this bank
										sdram_a <= adr(19 downto 8);					--and this row
										sdram_dqm <= "11";
										state <= "001";									--go to "read or write" state after all
									else
										sdram_cmd <= cmd_nop;							--if no requests then no operation needed
										sdram_ba <= (others => '0');
										sdram_a <= (others => '0');
										sdram_dqm <= "11";
										state <= "000";
									end if;
									
				when "001" =>	if (rd_selected = '1') then
										sdram_cmd <= cmd_read;							--run read if read needed
									else
										sdram_cmd <= cmd_write;							--...or write
									end if;
									sdram_ba <= adr_reg(21 downto 20);
									sdram_a(9 downto 0) <= "00" & adr_reg(7 downto 0);
									sdram_a(10) <= '0';
									sdram_dqm <= "00";
									--if row address do not change, repeat prev operation
									if ((rd_selected = '1' and rd_req = '1' and same_row_and_bank = '1') or (rd_selected = '0' and wr_req = '1' and same_row_and_bank = '1')) then
										state <= "001";
									else --else open new bank and row
										state <= "010";
									end if;
									
				when "010" =>	sdram_cmd <= cmd_precharge; --closing row
									sdram_ba <= (others => '0');
									sdram_a <= (10 => '1', others => '0');
									sdram_dqm <= "11";
									state <= "011";
									
									
				when "011" =>	sdram_cmd <= cmd_nop;
									sdram_ba <= (others => '0');
									sdram_a <= (others => '0');
									sdram_dqm <= "11";
									if (refresh_cnt = tREF+1) then
										state <= "101";
									else
										state <= "000";
									end if;
				
				when "100" =>	if (init_cnt = tINIT+1) then --initialization
										sdram_cmd <= cmd_precharge;
										sdram_a(10) <= '1';
									elsif (init_cnt = tINIT+tRP+1 or init_cnt = tINIT+tRP+tRFC+1) then
										sdram_cmd <= cmd_refresh;
									elsif (init_cnt = tINIT+tRP+2*tRFC+1) then
										sdram_cmd <= cmd_loadmode;
										sdram_a(9 downto 0) <= "0000100111";
									elsif (init_cnt = tINIT+tRP+2*tRFC+3+1) then
										state <= "000";
									else
										sdram_cmd <= cmd_nop;
									end if;
									
				when "101" =>	sdram_cmd <= cmd_refresh;
								if ((refresh_cnt > 0) and refresh_cnt < tRFC) then
									sdram_cmd <= cmd_nop;
								elsif (refresh_cnt = tRFC) then
									state <= "000";
								end if;								
				
				when others => null;
										
			end case;
		end if;
	end process state_machine;
	
	read_priority:process(rd_req, wr_req) --read requests have priority
	begin
		rd_now <= rd_req;
	end process read_priority;
	
	process(clk)
	begin
		if rising_edge(clk) then
			if (state = "000") then
				rd_selected <= rd_now;
			end if;
		end if;
	end process;
	
	address_select:process(clk, rd_cycle)
	begin
		if rising_edge(clk) then
			adr_reg <= adr;
		end if;
	end process address_select;
	
	output_enable:process(clk)
	begin
		if rising_edge(clk) then
			if (state = "001") then
				sdram_dq_oe <= wr_selected;
			else
				sdram_dq_oe <= '0';
			end if;
		end if;
	end process output_enable;
	
	process(clk)
	begin
		if rising_edge(clk) then
			wr_data1 <= wr_data;
			wr_data2 <= wr_data1;
		end if;
	end process;
	
	read_valid:process(clk)
	begin
		if rising_edge(clk) then
			if (state = "001" and rd_selected = '1') then
				rd_pipe_valid <= rd_pipe_valid(2 downto 0) & '1';
			else
				rd_pipe_valid <= rd_pipe_valid(2 downto 0) & '0';
			end if;
		end if;
	end process read_valid;
	
	read_data:process(clk)
	begin
		if rising_edge(clk) then
			rd_data <= sdram_dq;
		end if;
	end process read_data;
	
	init_counter:process(clk)
	begin
		if rising_edge(clk) then
			if (init_cnt < (tINIT+2*tRFC+tRP+3+1)) then
				init_cnt <= init_cnt+1;
			else
				null;
			end if;
		end if;
	end process init_counter;
	
	refresh_counter:process(clk)
	begin
		if rising_edge(clk) then
			if (refresh_cnt < tREF+1) then
				refresh_cnt <= refresh_cnt+1;
			else
				if (state = "101") then
					refresh_cnt <= 0;
				end if;
			end if;
		end if;
	end process refresh_counter;
	
	ready <= '1' when ((state = "000") or (state = "001")) else '0';
	
	adr <= rd_adr when (rd_cycle = '1') else wr_adr;	--address select
	
	same_row_and_bank <= '1' when (adr(21 downto 8) = adr_reg(21 downto 8)) else '0';
	rd_cycle <= rd_now when (state = "000") else rd_selected;
	wr_selected <= not rd_selected;
	rd_valid <= rd_pipe_valid(3);
	
	--command set
	sdram_ras_n <= sdram_cmd(2);
	sdram_cas_n <= sdram_cmd(1);
	sdram_wren_n <= sdram_cmd(0);
	
	--write
	sdram_dq <= wr_data2 when sdram_dq_oe = '1' else (others => 'Z');
	
	--sdram_clock
	sdram_clk_n <= not clk;

end rtl;

 

Изменения в контроллере нельзя назвать масштабными. Прежде всего, появился счётчик, который отмеряет периоды между командами AUTO REFRESH. Надо заметить, что для перехода автомата в состояние "регенерации", на котором базируется работа контроллера, должно выполниться два условия: счётчик должен накопить определённое значение, и должна быть применена операция закрытия активной строки/банка. Автомат будет находиться в состоянии "регенерации" столько, сколько необходимо для соблюдения рекомендуемых производителем временных интервалов.

Алгоритм работы с контроллером тоже немного изменился – теперь следует больше внимания уделять выходу ready. На нём формируется высокое логическое состояние, когда контроллер готов воспринимать сигналы со входов (запросы на чтение и запись, данные, адреса), и низкое, когда производятся операции инициализации, закрытия строк и регенерации. На мой взгляд, это должно сделать взаимодействие с контроллером более удобным.

Наконец, я добавил настраиваемый параметр sdram_frequency – на его основе высчитываются основные задержки микросхемы в тактах синхронизирующего сигнала. Этот параметр следует указывать в МГц, причём я бы не рекомендовал выходить за пределы 100 МГц – на такой частоте стабильная работа контроллера и памяти уже не гарантируется.

Честно говоря, у меня пока нет возможности верифицировать работу контроллера на 100%, поэтому я надеюсь на обратную связь – о найденных ошибках и прочих "неопрятностях" можно писать прямо в комментариях к этой статье или мне на электронную Этот адрес электронной почты защищен от спам-ботов. У вас должен быть включен JavaScript для просмотра..

PS: эта статья - продолжение моей другой работы  Простейший SDRAM-контроллер на VHDL

 

Несколько советов по Altera Quartus II

Если Вы начинаете разбираться со средой проектирования Altera Quartus II, начинаете делать свой новый проект для ПЛИС, то, возможно, эти советы покажутся вам полезными.

Обновление сайта

Сегодня наш сайт изменился.

Это скорее вынужденная мера - происходит смена версии движка сайта, так как предыдущая версия более не поддерживается создателями. Этот апдейт готовился примерно пол года. Мы вынуждены пойти на эти изменения - слишком старая версия ядра сайта угрожает безопасности самого сайта и данным (в том числе пользовательским), которые размещены на нем.

Сайт многократно подвергался атакам хакеров, так же несколько раз мы обнаруживали на страницах сайта внедренный сторонний код - видимо сайт имел известные хакерам уязвимости. Бороться с этими атаками не так и просто.

С некоторой тревогой мы идем на это обновление, так как до конца не понимаем, все ли функции сайта сохранены в целости после обновления. Надеюсь мы не все поломали, но если что-то и перестало работать - будем чинить.

Нам нужно понять: могут ли пользователи выполнять вход на форум, видят ли они свои профили и аватары в порядке, могут ли создавать темы и комментировать статьи, регистрироваться в конце-концов. Конечно, мы проделали тесты, какие могли проделать, но, кто знает, может что и упустили из виду.

Обо всех неполадках просьба писать на Этот адрес электронной почты защищен от спам-ботов. У вас должен быть включен JavaScript для просмотра.

Еще одна забота - мы надеемся, что нам удалось сохранить все входящие ссылки на наш сайт. Когда сторонние ресурсы ссылаются на нас - это плюс к нашему ТИЦ (по яндексу, Тематический Индекс Цитирования). Если ссылочная масса не дай бог упадет, вслед за ней может упасть и посещаемость.. Ну надеюсь этого не будет..

PS: Еще одно нововведение: возможно вскоре весь сайт переедет на протокол https. В общем-то https уже работает, параллельно с http. И даже сертификат мы уже поставили, а то тут google chrome обещает вскоре вообще все http сайты объявить "опасными".


GitHub YouTube Twitter
Вы здесь: Начало