[novice] DDR controller

Discussion in 'VHDL' started by quark.flavour@gmail.com, Dec 30, 2007.

  1. 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
    , Dec 30, 2007
    #1
    1. Advertising

  2. 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
    Mike Treseler, Dec 30, 2007
    #2
    1. Advertising

  3. Guest

    Re: DDR controller

    > 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;
    , Jan 5, 2008
    #3
  4. Re: DDR controller

    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
    Mike Treseler, Jan 5, 2008
    #4
  5. Guest

    Re: DDR controller

    > 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
    , Jan 5, 2008
    #5
  6. Guest

    Re: DDR controller

    >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
    , Jan 5, 2008
    #6
  7. Guest

    Re: DDR controller

    On Jan 5, 11:32 pm, 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
    , Jan 7, 2008
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Abdul K Shaik

    DDR/SDR-SDRAM Bank Switching Doubt

    Abdul K Shaik, Aug 9, 2003, in forum: VHDL
    Replies:
    1
    Views:
    2,239
    David Jones
    Aug 9, 2003
  2. ALuPin

    DDR SDRAM

    ALuPin, Aug 24, 2004, in forum: VHDL
    Replies:
    6
    Views:
    937
    H. Peter Anvin
    Sep 3, 2004
  3. Raghavendra

    Refresh rate in DDR-SDRAM

    Raghavendra, Jan 6, 2005, in forum: VHDL
    Replies:
    0
    Views:
    1,562
    Raghavendra
    Jan 6, 2005
  4. bxbxb3

    DDR SDRAM Controller

    bxbxb3, Jan 24, 2005, in forum: VHDL
    Replies:
    11
    Views:
    6,861
  5. Michael Earls
    Replies:
    3
    Views:
    3,270
    MBUnit
    Mar 24, 2009
Loading...

Share This Page