library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity sdram_ctrl is port ( clock_100 : in std_logic; reset_n : in std_logic; b_cs_n : in std_logic; b_rd_n : in std_logic; b_wr_n : in std_logic; b_wait_n : out std_logic; b_addr : in std_logic_vector(15 downto 0); b_data : inout std_logic_vector(7 downto 0); sdram_clk : out std_logic; sdram_cs_n : out std_logic; sdram_cas_n : out std_logic; sdram_ras_n : out std_logic; sdram_we_n : out std_logic; sdram_cke : out std_logic; sdram_addr : out std_logic_vector(12 downto 0); sdram_ba : out std_logic_vector(1 downto 0); sdram_dq : inout std_logic_vector(15 downto 0); sdram_dqm : out std_logic_vector(1 downto 0) ); end entity; architecture rtl of sdram_ctrl is signal clock : std_logic; signal ack_need_refresh : std_logic; signal need_refresh : std_logic; subtype uint13_t is integer range 0 to 8191; signal refresh_counter : uint13_t; signal init_done : std_logic; signal i_addr : std_logic_vector(12 downto 0); signal i_cmd : std_logic_vector(3 downto 0); subtype uint4_t is integer range 0 to 15; signal i_count : uint4_t; subtype uint3_t is integer range 0 to 7; signal i_next : uint3_t; signal i_refs : uint3_t; signal i_state :uint3_t; constant I_ST_RESET_WAIT : uint3_t:=0; constant I_ST_PRECHARGE : uint3_t :=1; constant I_ST_REFRESH : uint3_t :=2; constant I_ST_WAIT_COUNT : uint3_t :=3; constant I_ST_SET_MODE : uint3_t :=4; constant I_ST_DONE : uint3_t :=5; -- RAS_N CAS_N WE_N constant CMD_NOP : std_logic_vector(2 downto 0 ):="111"; constant CMD_READ : std_logic_vector(2 downto 0 ):="101"; constant CMD_WRIT : std_logic_vector(2 downto 0 ):="100"; constant CMD_ACTV : std_logic_vector(2 downto 0 ):="011"; constant CMD_PRE : std_logic_vector(2 downto 0 ):="010"; constant CMD_REF : std_logic_vector(2 downto 0 ):="001"; constant CMD_MRS : std_logic_vector(2 downto 0 ):="000"; signal m_state : std_logic_vector(8 downto 0); constant M_ST_IDLE : std_logic_vector(8 downto 0 ):="000000001"; constant M_ST_RAS : std_logic_vector(8 downto 0 ):="000000010"; constant M_ST_NOP_COUNT : std_logic_vector(8 downto 0 ):="000000100"; constant M_ST_READ : std_logic_vector(8 downto 0 ):="000001000"; constant M_ST_WRITE : std_logic_vector(8 downto 0 ):="000010000"; constant M_ST_WAIT_FOR_PRECHARGE : std_logic_vector(8 downto 0 ):="000100000"; constant M_ST_PRECHARGE : std_logic_vector(8 downto 0 ):="001000000"; constant M_ST_REFRESH : std_logic_vector(8 downto 0 ):="010000000"; constant M_ST_FETCH_NEXT : std_logic_vector(8 downto 0 ):="100000000"; function active_high(constant val : in boolean) return std_logic is begin if val then return '1'; else return '0'; end if; end function; function ternary( constant val : in boolean, constant if_true : in std_logic, constant if_false : in std_logic ) return std_logic is begin if val then return if_true; else return if_false; end if; end function; begin clock<=clock_100; refresh_counter_process: process(reset_n,clock) begin if reset_n = '0' then refresh_counter <= 8000; elsif rising_edge(clock) then if refresh_counter = 0 then refresh_counter <= 624; else refresh_counter <= refresh_counter - 1; end if; end if; end process; need_refresh_process: process(reset_n,clock,refresh_counter,need_refresh,init_done,ack_need_refresh) begin if reset_n = '0' then need_refresh <= '0'; elsif rising_edge(clock) then need_refresh <= (active_high(refresh_counter = 0) or need_refresh) and init_done and not ack_need_refresh; end if; end process; init_done_process: process (reset_n,clock,i_state) begin if reset_n = '0' then init_done <= '0'; elsif rising_edge(clock) then init_done <= init_done or active_high(i_state = I_ST_DONE ); end if; end process; init_fsm: process (reset_n,clock,i_state,i_count,i_next,refresh_counter) begin if reset_n = '0' then i_state <= I_ST_RESET_WAIT; i_next <= I_ST_RESET_WAIT; i_cs_n <=CS_N_NONE; i_cmd <= CMD_NOP; i_addr <= (others => '1'); i_count <= 0; elsif rising_edge(clock) then if i_state = I_ST_RESET_WAIT then -- after reset wait until the refresh_counter ticks over for RAM to stabalize i_cs_n <=CS_N_NONE; i_cmd <= CMD_NOP; i_refs <= 0; if refresh_counter=0 then i_state <= I_ST_PRECHARGE; end if; elsif i_state = I_ST_PRECHARGE then -- precharge all banks, wait one clock, then go to refresh i_cs_n <=CS_N_ALL; i_cmd <=CMD_PRE; i_state <= I_ST_WAIT_COUNT; i_count <= 1; i_next <= I_ST_REFRESH; elsif i_state =I_ST_REFRESH then -- repeat 7 times { refresh, wait 5 counts } i_cs_n <=CS_N_ALL; i_cmd <=CMD_REF; i_refs <= i_refs + 1; i_state <= I_ST_WAIT_COUNT; i_count <= 5; if i_refs = 7 then i_next <= I_ST_SET_MODE; else i_next <= I_ST_REFRESH; end if; elsif i_state = I_ST_WAIT_COUNT then -- wait i_count ticks then goto state i_next i_cs_n <=CS_N_ALL; i_cmd <= CMD_NOP; if (i_count > 1) then i_count <= i_count -1; else i_state <= i_next; end if; elsif i_state=I_ST_SET_MODE then -- set mode, wait 3 ticks then goto done i_cs_n <=CS_N_ALL; i_cmd <= CMD_MRS; -- reserverd 000 -- opcode burst read/write 0 -- reserved 00 -- cas latency 3 011 -- sequential burst 0 -- burst length 1 000 i_addr <= "0000000110000"; i_count <= 3; i_next <= I_ST_DONE; elsif i_state=I_ST_DONE then i_state <= I_ST_DONE; else i_state <= I_ST_RESET_WAIT; end if; end if; end process; main_fsm: process (reset_n,clock,m_state,refresh_counter) begin end process; if reset_n ='0' then m_state <= M_ST_WAITING_INIT; m_next <= M_ST_WAITING_INIT; m_cmd <= CMD_NOP; m_cs_n <= '1'; m_bank <= "00"; m_addr <= "0000000000000"; m_data <= "0000000000000000"; m_dqm <= "00"; m_count <= 0; ack_need_refresh <= 0; f_pop <= 0; oe <= 0; bus_busy<=1; elsif rising_edge(clock) then if m_state =M_ST_WAITING_INIT then m_addr <= i_addr; m_cmd <= i_cmd; m_cs_n <= i_cs_n; if active_high(init_done) then m_state <= M_ST_IDLE; end if; elsif m_state = M_ST_IDLE then m_cmd <= CMD_NOP; ack_need_refresh <='0'; if active_high(need_refresh) then -- If we got here needing a refresh -- we may have come straight from a -- read so leave CS on so we get -- that data m_cs_n<=CS_N_ALL; -- precharge everyone (putting back -- the open row if we did come from a read -- or write) then refresh everyone. m_state <= M_ST_PRECHARGE; m_next <= M_ST_REFRESH; else -- idle to all chips (CMD_IGN) m_cs_n<=CS_N_NONE; -- find out if anyone wants us if active_high(request_ready) then active_cs_n <= request_cs_n; active_rnw <= request_rnw; active_addr <= request_addr; active_data <= request_data; active_dqm <= request_dqm; m_state <= M_ST_RAS; ack_request <='1'; end if; end if; elsif m_state = M_ST_RAS then -- activate the relevant ROW, wait 3 clocks, then go and do the read or write ack_request <='0'; m_cs_n <= active_cs_n; m_cmd <= CMD_ACTV; m_bank <= active_bank; m_addr <= active_addr(22 downto 10); m_data <= active_data; m_dqm <= active_dqm; m_state <= M_ST_NOP_COUNT; m_count <= 2; --t 3 clocks : FIXME if (active_high(active_rnw)) then m_next <= M_ST_READ; else m_next <= M_ST_WRITE; end if; elsif m_state = M_ST_NOP_COUNT then -- send nops for m_count, then go to state m_next m_cmd <=CMD_NOP; if m_count > 1 then m_count <= m_count - 1; else m_state <= m_next; end if; elsif m_state = M_ST_READ then ack_request <='0'; m_cmd <=CMD_READ; m_addr(13 downto 10) <= (others => '0'); m_addr(9 downto 0) <= active_addr[8:0]; m_next <= M_ST_ACTIVE elsif m_state = M_ST_WRITE then ack_request <='0'; m_cmd<=CMD_WRIT; m_addr(13 downto 10) <= (others => '0'); m_addr(9 downto 0) <= active_addr[8:0]; oe <= '1'; m_data <= active_data; m_state <=M_ST_ACTIVE elsif m_state = M_ST_ACTIVE then -- turn off drivers (we might have come from a write) oe <= '0'; -- sit doing NOPS on this row until either we get a new request -- or we need a refresh m_cmd <= M_CMD_NOP; if active_high(need_refresh) then -- the refresh will first precharge everything, so no need to precharge -- just this row. m_state <= M_ST_NOP_COUNT; m_next <= M_ST_IDLE; m_count <= 1; elsif active_high(request_ready) then if (active_cs_n = request_cs_n) and (active_rnw == request_rnw) and same_bank_and_row(active_addr,request_addr) then -- this request matches our currently active row -- we can process it active_addr <= request_addr; active_data <= request_data; active_dqm <= request_dqm; ack_request <='1'; if active_high(request_rnw) then m_state <= M_ST_READ; else m_state <= M_ST_WRITE; end if; else -- new request doesn't match, precharge and go to idle m_state <= M_ST_WAIT_FOR_PRECHARGE; m_next <= M_ST_IDLE; end if; end if; elsif m_state = M_ST_WAIT_FOR_PRECHARGE then -- wait count then do a precharge m_cmd <= CMD_NOP; if m_count > 1 then m_count <= m_count -1; else m_state=M_ST_PRECHARGE; end; elsif m_state = M_ST_PRECHARGE then -- do a precharge, wait two clocks, then goto m_next m_addr <= (others => '1'); m_cmd <= CMD_PRE; m_count = 1; m_state <= M_ST_NOP_COUNT; elsif m_state=M_ST_REFRESH then -- do a refresh, wait six clocks, return to idle m_cmd <= CMD_REF; m_count <=5; m_next <=M_ST_IDLE; m_state <= M_ST_NOP_COUNT; -- tell the gubbins we did the refresh ack_need_refresh <= '1'; else -- shouldn't be here - crash m_cmd <=CMD_NOP; m_cs_n <= M_CS_NONE oe <= '0'; end if; end process; end;