If you want to learn vhdl, start with a simpler project.
To learnDDRoperation, study the device data sheet.
Too late
I've started coding a simple controller. I'm not looking
for performance, just for basic functionalities, like read a single 32
bit
value and write a single 32 bit value from/to a specific location.
or is there something around ready to use?
I think so.
Load the web version of quartus or xst and have a look.
I've searched the web without luck, there is nothing specific for the
device
tough there are some example of similiar memories.
Now, I've read something about VHDL, and I've written a code that,
after
initialization should keep reading a 32 bit value. If someone has any
advice
for the code I've written I'd be very grateful, because the more I
look to my
VHDL the more I think that it's horrible!
I'm not asking for errors
in the
DDR protocol (I'll probably go and ask in comp.arch.fpga
, just for
VHDL
suggestions.
Thanks in advance, code follows:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- DDR driver for Micron 521 Mbit 46V32M16
-- read functionality implemented
-- address = 24 bit address of the 32 bit value
-- value = 32 bit read value
entity ddr_driver is
port (
-- DDR pins
sd_a: inout std_logic_vector(12 downto 0);
sd_dq: inout std_logic_vector(15 downto 0);
sd_ba: out std_logic_vector(1 downto 0);
sd_cas: out std_logic;
sd_cke: out std_logic := '0';
sd_cs: out std_logic;
sd_ldm: out std_logic;
sd_ras: out std_logic;
sd_udm: out std_logic;
sd_we: out std_logic;
sd_ck_p: in std_logic;
sd_ck_n: in std_logic;
sd_ldqs: inout std_logic; -- bidirection strobe
sd_udqs: inout std_logic; -- bidirection strobe
-- LSB always 0, burst of 2 (0 1)
address: in std_logic_vector(23 downto 0); -- 24 bit, 16 million
locations (32 bit, burst of 2 16 bit)
value: inout std_logic_vector(31 downto 0);
-- LCD debug
debug_status: out std_logic_vector(7 downto 0) := "00000000" -- to
LCD
);
end ddr_driver;
architecture Behavioral of ddr_driver is
shared variable data_read : boolean := false;
shared variable data_write : boolean := false;
begin
-- first 16 bit, upper byte, udqs strobe rising edge
ldata_out_d1: process(sd_udqs)
begin
if data_read = true then
if sd_udqs'event and sd_udqs = '1' then
debug_status(7) <= '1'; -- LCD debugging
value(31 downto 24) <= sd_dq(15 downto 8);
end if;
end if;
end process ldata_out_d1;
-- first 16 bit, lower byte, ldqs strobe rising edge
udata_out_d1: process(sd_ldqs)
begin
if data_read = true then
if sd_ldqs'event and sd_ldqs = '1' then
debug_status(6) <= '1'; -- LCD debugging
value(23 downto 16) <= sd_dq(7 downto 0);
end if;
end if;
end process udata_out_d1;
-- second 16 bit, upper byte, udqs strobe falling edge
ldata_out_d2: process(sd_udqs)
begin
if data_read = true then
if sd_udqs'event and sd_udqs = '0' then
debug_status(5) <= '1'; -- LCD debugging
value(15 downto 8) <= sd_dq(15 downto 8);
end if;
end if;
end process ldata_out_d2;
-- second 16 bit, lower byte, ldqs strobe falling edge
udata_out_d2: process(sd_ldqs)
begin
if data_read = true then
if sd_ldqs'event and sd_ldqs = '0' then
debug_status(4) <= '1'; -- LCD debugging
value(7 downto 0) <= sd_dq(7 downto 0);
end if;
end if;
end process udata_out_d2;
main: process(sd_ck_n)
variable ms_count: integer range 0 to 1023 := 0;
variable us_count: integer range 0 to 1023 := 0;
variable ns_count: integer range 0 to 1023 := 0;
variable state: integer range 0 to 1023 := 0;
procedure next_state is
begin
state := state + 1;
end next_state;
procedure reset_watch is
begin
ms_count := 0;
us_count := 0;
ns_count := 0;
end reset_watch;
procedure update_watch is
begin
ns_count := ns_count + 10; -- 100 Mhz clock, 10 ns cycle
if ns_count = 1000 then
ns_count := 0;
us_count := us_count + 1;
if us_count = 1000 then
us_count := 0;
ms_count := ms_count + 1;
end if;
end if;
end update_watch;
procedure cmd_nop is
begin
reset_watch;
sd_ras <= '1';
sd_cas <= '1';
sd_we <= '1';
next_state;
end cmd_nop;
procedure cmd_precharge is
begin
reset_watch;
sd_ras <= '0';
sd_cas <= '1';
sd_we <= '0';
next_state;
end cmd_precharge;
procedure cmd_lmr is
begin
reset_watch;
sd_ras <= '0';
sd_cas <= '0';
sd_we <= '0';
next_state;
end cmd_lmr;
procedure cmd_ar is
begin
reset_watch;
sd_ras <= '0';
sd_cas <= '0';
sd_we <= '1';
next_state;
end cmd_ar;
procedure cmd_active is
begin
reset_watch;
sd_ras <= '0';
sd_cas <= '1';
sd_we <= '1';
next_state;
end cmd_active;
procedure cmd_read is
begin
reset_watch;
sd_ras <= '1';
sd_cas <= '0';
sd_we <= '1';
next_state;
end cmd_read;
procedure wait_for( constant t_ms: in integer;
constant t_us: in integer;
constant t_ns: in integer) is
begin
-- issue a NOP while waiting
sd_ras <= '1';
sd_cas <= '1';
sd_we <= '1';
if ms_count >= t_ms and us_count >= t_us and ns_count >= t_ns
then
reset_watch;
next_state;
end if;
end wait_for;
begin
if sd_ck_n'event and sd_ck_n = '1' then
update_watch;
if state = 0 then
wait_for(0, 200, 0); -- 200 us wait
elsif state = 1 then
sd_cke <= '1';
sd_cs <= '0';
cmd_nop;
elsif state = 2 then
sd_a(10) <= '1'; -- PRECHARGE ALL
cmd_precharge;
elsif state = 3 then
wait_for(0, 0, 15 - 10); -- at least 15 ns (tRP) NOP, less 10 ns
elsif state = 4 then
sd_ba <= "01"; -- 00 base mode register, 01 is extended
sd_a(0) <= '0'; -- enable dll
sd_a(1) <= '0'; -- normal drive strength
sd_a(12 downto 2) <= "00000000000";
cmd_lmr;
elsif state = 5 then
wait_for(0, 0, 12 - 10); -- at least 12 ns (tMRD) NOP
elsif state = 6 then
sd_ba <= "00"; -- 00 base mode register, 01 is extended
sd_a(12 downto 7) <= "000010"; -- "000000" normal operation,
"000010" norm op/reset dll
sd_a(6 downto 4) <= "010"; -- CAS latency 2
sd_a(3) <= '0'; -- sequential burst (1 would be interleaved)
sd_a(2 downto 0) <= "001"; -- burst length of 2
cmd_lmr;
elsif state = 7 then
wait_for(0, 0, 12 - 10); -- at least 12 ns (tMRD) NOP
elsif state = 8 then
sd_a(10) <= '1'; -- precharge all
cmd_precharge;
elsif state = 9 then
wait_for(0, 0, 15 - 10); -- at least 15 ns (tRP)
elsif state = 10 then
cmd_ar;
elsif state = 11 then
wait_for(0, 0, 72 - 10); -- tRFC = 72 ns
elsif state = 12 then
cmd_ar;
elsif state = 13 then
wait_for(0, 0, 72 - 10); -- tRFC = 72 ns
elsif state = 14 then
sd_ba <= "00"; -- 00 base mode register, 01 is extended
sd_a(12 downto 7) <= "000000"; -- "000000" normal operation,
"000010" norm op/reset dll
sd_a(6 downto 4) <= "010"; -- CAS latency 2
sd_a(3) <= '0'; -- sequential burst ('1' would be interleaved)
sd_a(2 downto 0) <= "001"; -- burst length of 2
cmd_lmr; -- LMR (base) required by JEDEC to clear the DLL reset
(A8)
elsif state = 15 then
-- at least 12 ns (tMRD) NOP
-- wait at least 200 tCK (200 * 10 ns = 2000 ns = 2 us)
wait_for(0, 2, 12-10);
elsif state = 16 then
sd_ba <= address(23 downto 22);
sd_a(12 downto 0) <= address(21 downto 9);
cmd_active;
elsif state = 17 then
wait_for(0, 0, 15 - 10); -- tRCD = 15 ns
elsif state = 18 then
sd_ba <= address(23 downto 22);
sd_a(9 downto 1) <= address(8 downto 0);
sd_a(0) <= '0'; -- start from even address, burst of 2 (0 - 1)
sd_a(10) <= '1'; -- auto precharge enabled
cmd_read;
elsif state = 19 then
cmd_nop;
elsif state = 20 then
data_read := true;
cmd_nop;
elsif state = 21 then
cmd_nop;
elsif state = 22 then
data_read := false;
cmd_nop;
elsif state = 23 then
-- read done, precharge done automatically
-- all the banks idle, auto refresh now
cmd_ar;
elsif state = 24 then
wait_for(0, 0, 72 - 10); -- tRFC = 72 ns
elsif state = 25 then
state := 16;
end if;
end if;
end process main;
end Behavioral;