shangdawei 发表于 2012-5-7 11:19:09

SDRAM_Memory_Controller 参考代码

http://hamsterworks.co.nz/mediawiki/index.php/SDRAM_Memory_Controller

This is my design for a memory controller for my Terasic DE0-nano FPGA board, which uses the ISSI IS42S16160B-7 32MB SDRAM chip

Although aimed at 100MHz all of the designs below can be adapted to other clock speeds. The only changes needed are to increase the number of NOPs in the refresh chain to ensure that it takes at least 70ns.

Adapting to a CAS setting of 2 is only a little bit more difficult, as the data is available one cycle earlier. A CAS of 2 can only be used with a clock speed of 100MHz, and will make the biggest difference with the simple FSM where it saves a cycle on every read, or in the most complex where it saves a cycle flipping between reads and writes.

The priority should be first to perform any pending refresh, but priority of performing reads over writes depends on your target application. For example if you are generating a VGA video signal reads should take priority over writes otherwise "tearing" of the picture could occur.

I might not be completely following accepted conventions, but the idea behind the directed graphs in this context are that as you follow the arrows from node to node you will always generate a valid set of commands for the SDRAM. For each clock tick you must follow an arrow, so at 100MHz it takes 10ns to go from node to node, allowing you to partially verify the design on paper.




(Blue circles are data transfers from the SDRAM, red circles are data transfers to the SDRAM)












shangdawei 发表于 2012-5-7 11:21:58


-- FSM for a SDRAM controller
--
-- Version 0.1 - Ready to simulate
--
-- Author: Mike Field (hamster@snap.net.nz)
--
-- Feel free to use it however you would like, but
-- just drop me an email to say thanks.
-------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity sdram_controller is
port (
    CLOCK_50 : in STD_LOGIC;

   -- Signals to/from the SDRAM chip
    DRAM_ADDR: out   STD_LOGIC_VECTOR (12 downto 0);
    DRAM_BA    : out   STD_LOGIC_VECTOR (1 downto 0);
    DRAM_CAS_N : out   STD_LOGIC;
    DRAM_CKE   : out   STD_LOGIC;
    DRAM_CLK   : out   STD_LOGIC;
    DRAM_CS_N: out   STD_LOGIC;
    DRAM_DQ    : inout STD_LOGIC_VECTOR(15 downto 0);
    DRAM_DQM   : out   STD_LOGIC_VECTOR(1 downto 0);
    DRAM_RAS_N : out   STD_LOGIC;
    DRAM_WE_N: out   STD_LOGIC;

   --- Inputs from rest of the system
    address      : in   STD_LOGIC_VECTOR (23 downto 0);
    req_read       : in   STD_LOGIC;
    req_write      : in   STD_LOGIC;
    data_out       : out   STD_LOGIC_VECTOR (31 downto 0);
    data_out_valid : out   STD_LOGIC;
    data_in      : in   STD_LOGIC_VECTOR (31 downto 0)
);
end entity;


architecture rtl of sdram_controller is


type reg is record
    state          : std_logic_vector(8 downto 0);
    address      : std_logic_vector(12 downto 0);
    bank         : std_logic_vector( 1 downto 0);
    init_counter   : std_logic_vector(14 downto 0);
    rf_counter   : std_logic_vector( 9 downto 0);
    rf_pending   : std_logic;
    rd_pending   : std_logic;
    wr_pending   : std_logic;
    act_row      : std_logic_vector(12 downto 0);
    data_out_low   : std_logic_vector(15 downto 0);
    data_out_valid : std_logic;
    dq_masks       : std_logic_vector(1 downto 0);
end record;

component sdram_clk_gen
    port
   (
      inclk0 : inSTD_LOGIC;
      c0   : out STD_LOGIC;
      c1   : out STD_LOGIC
    );
end component;

   -- note to self - this constant should be "(others => '0')" when not simulating!!!
signal r : reg := ((others => '0'), (others => '0'),
    (others => '0'), "000000000001000", (others => '0'),
    '0', '0', '0', (others => '0'), (others => '0'), '0', (others => '0'));
signal n : reg;

   -- Vectors for each SDRAM 'command'
   --- CS_N, RAS_N, CAS_N, WE_N
constant cmd_nop   : std_logic_vector(3 downto 0) := "0111";
constant cmd_read: std_logic_vector(3 downto 0) := "0101";   -- Must be sure A10 is low.
constant cmd_write : std_logic_vector(3 downto 0) := "0100";
constant cmd_act   : std_logic_vector(3 downto 0) := "0011";
constant cmd_pre   : std_logic_vector(3 downto 0) := "0010";-- Must set A10 to '1'.
constant cmd_ref   : std_logic_vector(3 downto 0) := "0001";
constant cmd_mrs   : std_logic_vector(3 downto 0) := "0000"; -- Mode register set
   -- State assignments
constant s_init_nop : std_logic_vector(8 downto 0) := "00000" & cmd_nop;
constant s_init_pre : std_logic_vector(8 downto 0) := "00000" & cmd_pre;
constant s_init_ref : std_logic_vector(8 downto 0) := "00000" & cmd_ref;
constant s_init_mrs : std_logic_vector(8 downto 0) := "00000" & cmd_mrs;

constant s_idle : std_logic_vector(8 downto 0) := "00001" & cmd_nop;

constant s_rf0 : std_logic_vector(8 downto 0) := "00010" & cmd_ref;
constant s_rf1 : std_logic_vector(8 downto 0) := "00011" & cmd_nop;
constant s_rf2 : std_logic_vector(8 downto 0) := "00100" & cmd_nop;
constant s_rf3 : std_logic_vector(8 downto 0) := "00101" & cmd_nop;
constant s_rf4 : std_logic_vector(8 downto 0) := "00110" & cmd_nop;
constant s_rf5 : std_logic_vector(8 downto 0) := "00111" & cmd_nop;

constant s_ra0 : std_logic_vector(8 downto 0) := "01000" & cmd_act;
constant s_ra1 : std_logic_vector(8 downto 0) := "01001" & cmd_nop;
constant s_ra2 : std_logic_vector(8 downto 0) := "01010" & cmd_nop;

constant s_dr0 : std_logic_vector(8 downto 0) := "01011" & cmd_pre;
constant s_dr1 : std_logic_vector(8 downto 0) := "01100" & cmd_nop;

constant s_wr0 : std_logic_vector(8 downto 0) := "01101" & cmd_write;
constant s_wr1 : std_logic_vector(8 downto 0) := "01110" & cmd_nop;
constant s_wr2 : std_logic_vector(8 downto 0) := "01111" & cmd_nop;
constant s_wr3 : std_logic_vector(8 downto 0) := "10000" & cmd_nop;

constant s_rd0 : std_logic_vector(8 downto 0) := "10001" & cmd_read;
constant s_rd1 : std_logic_vector(8 downto 0) := "10010" & cmd_nop;
constant s_rd2 : std_logic_vector(8 downto 0) := "10011" & cmd_nop;
constant s_rd3 : std_logic_vector(8 downto 0) := "10100" & cmd_nop;
constant s_rd4 : std_logic_vector(8 downto 0) := "10101" & cmd_read;
constant s_rd5 : std_logic_vector(8 downto 0) := "10110" & cmd_nop;
constant s_rd6 : std_logic_vector(8 downto 0) := "10111" & cmd_nop;
constant s_rd7 : std_logic_vector(8 downto 0) := "11000" & cmd_nop;
constant s_rd8 : std_logic_vector(8 downto 0) := "11001" & cmd_nop;
constant s_rd9 : std_logic_vector(8 downto 0) := "11011" & cmd_nop;

constant s_drdr0 : std_logic_vector(8 downto 0) := "11101" & cmd_pre;
constant s_drdr1 : std_logic_vector(8 downto 0) := "11110" & cmd_nop;
constant s_drdr2 : std_logic_vector(8 downto 0) := "11111" & cmd_nop;

signal addr_row: std_logic_vector(12 downto 0);
signal addr_bank : std_logic_vector(1 downto 0);
signal addr_col: std_logic_vector(9 downto 0);

signal captured : std_logic_vector(15 downto 0);

signal clock_100             : std_logic;
signal clock_100_delayed_3ns : std_logic;
begin
   -- Addressing is in 32 bit words - twice that of the DRAM width,
   -- so each burst of four access two system words.
addr_row<= address(23 downto 11);
addr_bank <= address(10 downto 9);
addr_col<= address(8 downto1) & "00";

sdram_clk_pll : sdram_clk_gen

   -- Generate the 100MHz clock and the same phase shifted by 3ns
port map
   (
    inclk0 => CLOCK_50,
    c0   => clock_100,
    c1   => clock_100_delayed_3ns
);

DRAM_CLK       <= clock_100_delayed_3ns;
DRAM_CKE       <= '1';
DRAM_CS_N      <= r.state(3);
DRAM_RAS_N   <= r.state(2);
DRAM_CAS_N   <= r.state(1);
DRAM_WE_N      <= r.state(0);
DRAM_ADDR      <= r.address;
DRAM_BA      <= r.bank;
DATA_OUT       <= captured & r.data_out_low;
DRAM_DQM       <= r.dq_masks;
data_out_valid <= r.data_out_valid;

process (r, address, req_read, req_write, addr_row, addr_bank, addr_col, data_in, captured)
begin
      -- copy the existing values
    n <= r;
    if req_read = '1' then
      n.rd_pending <= '1';
    end if;

    if req_write = '1' then
      n.wr_pending <= '1';
    end if;

    n.dq_masks <= "11";

      -- first off, do we need to perform a refresh cycle ASAP?
    if r.rf_counter = 770 then -- 781 = 64,000,000ns / 8192 / 10ns
      n.rf_counter <= (others => '0');
      n.rf_pending <= '1';
    else
         -- only start looking for refreshes outside of the initialisation state.
      if not(r.state(8 downto 4) = s_init_nop(8 downto 4)) then
      n.rf_counter <= r.rf_counter + 1;
      end if;
    end if;

      -- Set the data bus into HIZ, high and low bytes masked
    DRAM_DQ <= (others => 'Z');

    n.init_counter <= r.init_counter-1;

      -- Process the FSM
    case r.state(8 downto 4) is
      when s_init_nop(8 downto 4) =>
      n.state          <= s_init_nop;
      n.address      <= (others => '0');
      n.bank         <= (others => '0');
      n.rf_counter   <= (others => '0');
      n.data_out_valid <= '1';

            -- T-130, precharge all banks.
      if r.init_counter = "000000010000010" then
          n.state       <= s_init_pre;
          n.address(10) <= '1';
      end if;

            -- T-127, T-111, T-95, T-79, T-63, T-47, T-31, T-15, the 8 refreshes

      if r.init_counter(14 downto 7) = 0 and r.init_counter(3 downto 0) = 15 then
          n.state <= s_init_ref;
      end if;

            -- T-3, the load mode register
      if r.init_counter = 3 then
          n.state <= s_init_mrs;
                           -- Mode register is as follows:
                           -- resvd   wr_b   OpMd   CAS=3   Seq   bust=4
          n.address <= "000" & "0" & "00" & "011" & "0" & "010";
                           -- resvd
          n.bank <= "00";
      end if;


            -- T-1 The switch to the FSM (first command will be a NOP
      if r.init_counter = 1 then
          n.state <= s_idle;
      end if;

         ------------------------------
         -- The Idle section
         ------------------------------
      when s_idle(8 downto 4) =>
      n.state <= s_idle;

            -- do we have to activate a row?
      if r.rd_pending = '1' or r.wr_pending = '1' then
          n.state   <= s_ra0;
          n.address <= addr_row;
          n.act_row <= addr_row;
      end if;

            -- refreshes take priority over everything
      if r.rf_pending = '1' then
          n.state      <= s_rf0;
          n.rf_pending <= '0';
      end if;
         ------------------------------
         -- Row activation
         -- s_ra2 is also the "idle with active row" state and provides
         -- a resting point between operations on the same row
         ------------------------------
      when s_ra0(8 downto 4) =>
      n.state <= s_ra1;
      when s_ra1(8 downto 4) =>
      n.state <= s_ra2;
      when s_ra2(8 downto 4) =>
            -- we can stay in this state until we have something to do
      n.state <= s_ra2;

            -- If there is a read pending, deactivate the row
      if r.rd_pending = '1' or r.wr_pending = '1' then
          n.state       <= s_dr0;
          n.address(10) <= '1';
      end if;

            -- unless we have a read to perform on the same row? do that instead
      if r.rd_pending = '1' and r.act_row = addr_row then
          n.state      <= s_rd0;
          n.address    <= "000" & addr_col;
          n.bank       <= addr_bank;
          n.dq_masks   <= "00";
          n.rd_pending <= '0';
      end if;

            -- unless we have a write on the same row? writes take priroty over reads
      if r.wr_pending = '1' and r.act_row = addr_row then
          n.state      <= s_wr0;
          n.address    <= "000" & addr_col;
          n.bank       <= addr_bank;
          n.dq_masks   <= "00";
          n.wr_pending <= '0';
      end if;

            -- But refreshes take piority over everything!
      if r.rf_pending = '1' then
          n.state       <= s_dr0;
          n.address(10) <= '1';
      end if;

         ------------------------------------------------------
         -- Deactivate the current row and return to idle state
         ------------------------------------------------------
      when s_dr0(8 downto 4) =>
      n.state <= s_dr1;
      when s_dr1(8 downto 4) =>
      n.state <= s_idle;

         ------------------------------
         -- The Refresh section
         ------------------------------
      when s_rf0(8 downto 4) =>
      n.state <= s_rf1;
      when s_rf1(8 downto 4) =>
      n.state <= s_rf2;
      when s_rf2(8 downto 4) =>
      n.state <= s_rf3;
      when s_rf3(8 downto 4) =>
      n.state <= s_rf4;
      when s_rf4(8 downto 4) =>
      n.state <= s_rf5;
      when s_rf5(8 downto 4) =>
      n.state <= s_idle;
         ------------------------------
         -- The Write section
         ------------------------------
      when s_wr0(8 downto 4) =>
      n.state    <= s_wr1;
      n.address<= "000" & addr_col;
      n.bank   <= addr_bank;
      DRAM_DQ    <= data_in(15 downto 0);
      n.dq_masks <= "00";
      when s_wr1(8 downto 4) =>
      n.state    <= s_wr2;
      DRAM_DQ    <= data_in(31 downto 16);
      n.dq_masks <= "00";
      when s_wr2(8 downto 4) =>
      DRAM_DQ    <= data_in(15 downto 0);
      n.state    <= s_wr3;
      n.dq_masks <= "00";
      when s_wr3(8 downto 4) =>
            -- Default to the idle+row active state
      n.state    <= s_ra2;
      DRAM_DQ    <= data_in(31 downto 16);
      n.dq_masks <= "11";

            -- If there is a read or write then deactivate the row
      if r.rd_pending = '1' or r.wr_pending = '1' then
          n.state       <= s_dr0;
          n.address(10) <= '1';
      end if;

            -- But if there is a read pending in the same row, do that
      if r.rd_pending = '1' and r.act_row = addr_row then
          n.state      <= s_rd0;
          n.address    <= "000" & addr_col;
          n.bank       <= addr_bank;
          n.dq_masks   <= "00";
          n.rd_pending <= '0';
      end if;

            -- unless there is a write pending in the same row, do that
      if r.wr_pending = '1' and r.act_row = addr_row then
          n.state      <= s_wr0;
          n.address    <= "000" & addr_col;
          n.bank       <= addr_bank;
          n.dq_masks   <= "00";
          n.wr_pending <= '0';
      end if;

            -- But always try and refresh if one is pending!
      if r.rf_pending = '1' then
          n.state       <= s_dr0;
          n.address(10) <= '1';
      end if;

         ------------------------------
         -- The Read section
         ------------------------------
      when s_rd0(8 downto 4) =>
      n.state    <= s_rd1;
      n.dq_masks <= "00";
      when s_rd1(8 downto 4) =>
      n.state    <= s_rd2;
      n.dq_masks <= "00";
      when s_rd2(8 downto 4) =>
      n.state    <= s_rd3;
      n.dq_masks <= "00";
      when s_rd3(8 downto 4) =>
            -- default is to end the read with the row open
      n.state <= s_rd7;

            -- otherwise if there is a read or write prepare to deactivate the row.
            -- (This is overridden if the read/write is to the same page)
      if r.rd_pending = '1' or r.wr_pending = '1' then
          n.state       <= s_drdr0;
          n.address(10) <= '1';
      end if;

            -- override if the write is from the same row
      if r.wr_pending = '1' and r.act_row = addr_row then
          n.state <= s_rd7;
      end if;

            -- override if the read is from the same row
      if r.rd_pending = '1' and r.act_row = addr_row then
          n.state    <= s_rd4;
          n.address<= "000" & addr_col;
          n.bank   <= addr_bank;
          n.dq_masks <= "00";
      end if;

               -- If a refresh is pending then always deactivate the row
      if r.rf_pending = '1' then
          n.state       <= s_drdr0;
          n.address(10) <= '1';
      end if;
      n.data_out_low   <= captured;
      n.data_out_valid <= '1';
      when s_rd4(8 downto 4) =>
      n.state    <= s_rd5;
      n.dq_masks <= "00";
      when s_rd5(8 downto 4) =>
      n.state          <= s_rd6;
      n.data_out_low   <= captured;
      n.data_out_valid <= '1';
      n.dq_masks       <= "00";
      when s_rd6(8 downto 4) =>
      n.state    <= s_rd3;
      n.dq_masks <= "00";
      when s_rd7(8 downto 4) =>
      n.state          <= s_rd8;
      n.data_out_low   <= captured;
      n.data_out_valid <= '1';
      when s_rd8(8 downto 4) =>
      n.state <= s_rd9;
      when s_rd9(8 downto 4) =>
            -- by default go to the idle-with-row-active state
      n.state          <= s_ra2;
      n.data_out_low   <= captured;
      n.data_out_valid <= '1';

            -- otherwise if there is a read or write prepare to deactivate the row.
            -- (This is overridden if the read/write is to the same row)
      if r.rd_pending = '1' or r.wr_pending = '1' then
          n.state       <= s_dr0;
          n.address(10) <= '1';
      end if;

            -- this is to catch if a read has turned up since the choices at state s_dr3
      if r.rd_pending = '1' and r.act_row = addr_row then
          n.state      <= s_rd0;
          n.address    <= "000" & addr_col;
          n.bank       <= addr_bank;
          n.dq_masks   <= "00";
          n.rd_pending <= '0';
      end if;

            -- this is to catch if a read has turned up since the choices at state s_dr3
      if r.wr_pending = '1' and r.act_row = addr_row then
          n.state      <= s_wr0;
          n.address    <= "000" & addr_col;
          n.bank       <= addr_bank;
          n.dq_masks   <= "00";
          n.wr_pending <= '0';
      end if;

      if r.rf_pending = '1' then
          n.state       <= s_dr0;
          n.address(10) <= '1';
      end if;

         ------------------------------
         -- The Deactivate row during read section
         ------------------------------
      when s_drdr0(8 downto 4) =>
      n.state <= s_drdr1;
      when s_drdr1(8 downto 4) =>
      n.state          <= s_drdr2;
      n.data_out_low   <= captured;
      n.data_out_valid <= '1';
      when s_drdr2(8 downto 4) =>
      n.state <= s_idle;

      if r.rf_pending = '1' then
          n.state <= s_rf0;
      end if;

      if r.rd_pending = '1' or r.wr_pending = '1' then
          n.state   <= s_ra0;
          n.address <= addr_row;
          n.act_row <= addr_row;
          n.bank    <= addr_bank;
      end if;

      when others =>
      n.state <= s_init_nop;
    end case;
end process;

   --- The clock driven logic
process (clock_100, n)
begin
    if clock_100'event and clock_100 = '1' then
      r <= n;
    end if;
end process;

process (clock_100_delayed_3ns, dram_dq)
begin
    if clock_100_delayed_3ns'event and clock_100_delayed_3ns = '1' then
      captured <= dram_dq;
    end if;
end process;

end rtl;

dreamer@sun 发表于 2012-5-7 11:27:10

谢楼主分享

xiaoluo8805 发表于 2012-5-30 15:05:52

{:smile:}....学习了。。。谢谢

kneken 发表于 2012-5-30 19:47:04

mark!!!!!!!!!!!!

君临天下 发表于 2012-6-1 22:48:59

mark,{:lol:}

xiongxie007 发表于 2013-5-8 10:01:39

正要学,就来了。

寒晨蓝雪 发表于 2013-8-8 15:59:35

正好在学习,MARK一下                  

zlpvch 发表于 2013-8-9 13:40:17

mark            

lu976046395 发表于 2014-5-9 10:20:29

正在学习中
页: [1]
查看完整版本: SDRAM_Memory_Controller 参考代码