[novice] DDR controller

Guest
Hi all, I'm a novice in VHDL and I'm working on a Spartan 3E eval
board. This board is equipped with a Micron SDRAM (MT46V32M16) and I'd
like to start learning about DDR. The board doc suggests using the
(commercial) MicroBlaze soft processor but I'd prefer to try using a
VHDL code instead, does it make any sense to write a DDR controller
from scratch or is there something around ready to use? Thanks in
advance!

Andrew
 
quark.flavour@gmail.com wrote:
...This board is equipped with a Micron SDRAM (MT46V32M16) and I'd
like to start learning about DDR. The board doc suggests using the
(commercial) MicroBlaze soft processor but I'd prefer to try using a
VHDL code instead, does it make any sense to write a DDR controller
from scratch
If you want to learn vhdl, start with a simpler project.
To learn DDR operation, study the device data sheet.

or is there something around ready to use?

I think so.
Load the web version of quartus or xst and have a look.

-- Mike Treseler
 
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;
 
quark.flavour@gmail.com wrote:

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.
I stand by my previous advice.
Start with a synchronous template and add a little at a time.
One clock.

begin -- process template
if reset = '1' then
init_regs;
elsif rising_edge(clock) then
update_regs;
end if;
update_ports;
end process sync_template;
end architecture synth;


-- Mike Treseler
 
I stand by my previous advice.
Start with a synchronous template and add a little at a time.
-- Mike Treseler
Yes, a DDR controller probably comes under the heading "ambitious". At
the start, it's enough to learn the basic concepts and to struggle
with a new language.

But it's probably not very important how he starts. What's important
is to start. If the task is difficult, he can scale back his
ambitions. It reminds me of mathematician J E Littlewood who
"...never regretted having tackled the Riemann hypothesis, remarking
that if one attempted a problem that was too difficult then one would
always end up proving some interesting related results".

Becoming an expert in a given field isn't a linear process. You get
familiar as necessary with various sub-fields (some of which others
may regard as "advanced", even though you still feel like a beginner).
This takes pain and struggle and late nights, after which you still
feel very ignorant. Then, one day, you wake up and realise that
you're familiar with the landscape, that the parts you don't know yet
don't bother you any more and that you're confident enough not to
worry about looking a fool in a newsgroup. (I hope I make it that far
with HDLs!) How you got there doesn't make a lot of difference and
I'm not sure one road is much longer than another.

Mike
 
I stand by my previous advice.
Start with a synchronous template and add a little at a time.
One clock.

   begin  -- process template
      if reset = '1' then
         init_regs;
      elsif rising_edge(clock) then
         update_regs;
      end if;
      update_ports;
   end process sync_template;
Thanks again for the suggestion and the template! I'll work step by
step.

Regards

Andrew
 
On Jan 5, 11:32 pm, MikeShepherd...@btinternet.com wrote:
I'm not sure one road is much longer than another.

Mike
I never refuse a suggestion :) what I'm doing now is learning while
having fun,
trying to do something practical, as I'm a practitioner.
As for the Riemann hypothesis, well, we could write the following
mathematical proportion:

Littlewood : Riemann hypothesis = Andrew : DDR controller

;^)

Andrew
 

Welcome to EDABoard.com

Sponsor

Back
Top