------------------------------------------------------ -- 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: IN STD_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 r : reg := ((others => '0'), (others => '0'), (others => '0'), (others => '0'), (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 downto 1) & "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'; n.data_out_valid <= '0'; end if; if req_write = '1' then n.wr_pending <= '1'; n.data_out_valid <= '0'; 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;