library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; library work; use work.sdram_util.ALL; -- a simple dram controller (no pipelineing) -- that looks like a slow static ram entity sdram_ctrl is port ( clock_100 : in std_logic; reset_n : in std_logic; bus_cs_n : in std_logic; bus_rnw : in std_logic; bus_wait_n : out std_logic; bus_addr : in addr_t; bus_data_in : in data_t; bus_data_out : out data_t; sdram_clk : out std_logic; sdram_cke : 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_addr : out std_logic_vector(12 downto 0); sdram_ba : out std_logic_vector(1 downto 0); sdram_dq : inout data_t; sdram_dqm : out dqm_t; debug : out std_logic_vector(7 downto 0) ); end entity; architecture rtl of sdram_ctrl is signal clock : std_logic; constant DEBUG_0 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#3F#,7)); constant DEBUG_1 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#06#,7)); constant DEBUG_2 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#5B#,7)); constant DEBUG_3 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#4F#,7)); constant DEBUG_4 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#66#,7)); constant DEBUG_5 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#6D#,7)); constant DEBUG_6 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#7D#,7)); constant DEBUG_7 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#07#,7)); constant DEBUG_8 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#7F#,7)); constant DEBUG_9 : std_logic_vector(6 downto 0) := std_logic_vector(to_unsigned(16#6F#,7)); -- bits in the MEM_CMD register RAS_N CAS_N WE_N constant MEM_CMD_NOP : std_logic_vector(2 downto 0 ):="111"; constant MEM_CMD_READ : std_logic_vector(2 downto 0 ):="101"; constant MEM_CMD_WRIT : std_logic_vector(2 downto 0 ):="100"; constant MEM_CMD_ACTV : std_logic_vector(2 downto 0 ):="011"; constant MEM_CMD_PRE : std_logic_vector(2 downto 0 ):="010"; constant MEM_CMD_REF : std_logic_vector(2 downto 0 ):="001"; constant MEM_CMD_MRS : std_logic_vector(2 downto 0 ):="000"; constant MEM_CS_N_ALL : cs_n_t := "0"; constant MEM_CS_N_NONE : cs_n_t := "1"; -- refresh logic signal need_refresh : std_logic; signal refresh_counter : uint13_t; -- init logic signal init_done : std_logic; signal i_addr : std_logic_vector(12 downto 0); signal i_cs_n: cs_n_t; signal i_cmd : std_logic_vector(2 downto 0); signal i_count : uint4_t; signal i_next : uint3_t; signal i_refs : uint3_t; -- debuging signal i_debug : std_logic_vector(6 downto 0); signal m_debug : std_logic_vector(7 downto 0); signal b_debug : std_logic_vector(7 downto 0); -- init fsm 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; -- memory interface wires signal mem_cs_n : cs_n_t; signal mem_bank : std_logic_vector(1 downto 0); signal mem_cmd : std_logic_vector(2 downto 0); signal mem_addr : std_logic_vector(12 downto 0); signal mem_data_out : data_t; signal mem_data_in : data_t; signal mem_dqm : dqm_t; signal mem_oe: std_logic; -- main logic signal ack_refresh : std_logic; signal ack_request : std_logic; signal m_count: uint3_t; signal active_cs_n : cs_n_t; signal active_addr : addr_t; signal active_data : data_t; signal active_rnw : std_logic; signal active_dqm : dqm_t; -- main fsm signal m_state : std_logic_vector(9 downto 0); signal m_next : std_logic_vector(9 downto 0); constant M_ST_WAITING_INIT : std_logic_vector(9 downto 0 ):="0000000001"; constant M_ST_IDLE : std_logic_vector(9 downto 0 ):="0000000010"; constant M_ST_RAS : std_logic_vector(9 downto 0 ):="0000000100"; constant M_ST_NOP_COUNT : std_logic_vector(9 downto 0 ):="0000001000"; constant M_ST_READ : std_logic_vector(9 downto 0 ):="0000010000"; constant M_ST_WRITE : std_logic_vector(9 downto 0 ):="0000100000"; constant M_ST_ACTIVE : std_logic_vector(9 downto 0 ):="0001000000"; constant M_ST_WAIT_FOR_PRECHARGE : std_logic_vector(9 downto 0 ):="0010000000"; constant M_ST_PRECHARGE : std_logic_vector(9 downto 0 ):="0100000000"; constant M_ST_REFRESH : std_logic_vector(9 downto 0 ):="1000000000"; -- request logic signal request_pending: std_logic; signal request_cs_n : cs_n_t; signal request_addr : addr_t; signal request_data : data_t; signal request_rnw : std_logic; signal request_dqm : dqm_t; -- read state logic signal r_data_valid: std_logic_vector(3 downto 0); -- bus logic signal post_request: std_logic; -- bus fsm signal b_state : std_logic_vector(4 downto 0); constant B_ST_WAIT_CS_N_LOW : std_logic_vector(4 downto 0):="00001"; constant B_ST_LODGE_REQUEST : std_logic_vector(4 downto 0):="00010"; constant B_ST_WAIT_ACK : std_logic_vector(4 downto 0):="00100"; constant B_ST_WAIT_DATA : std_logic_vector(4 downto 0):="01000"; constant B_ST_WAIT_CS_N_HIGH : std_logic_vector(4 downto 0):="10000"; -- convert boolean to active high logic function b2l_ah(constant val : in boolean) return std_logic is begin if val then return '1'; else return '0'; end if; end function; -- convert active high logic to boolean value function l2b_ah(constant val : in std_logic) return boolean is begin return val='1'; end function; function l2b_al(constant val : in std_logic) return boolean is begin return val='0'; end function; function same_bank_and_row ( constant a1: in addr_t; constant a2: in addr_t ) return boolean is begin return a1(23 downto 9) = a2(23 downto 9); end function; begin clock<=clock_100; refresh_counter_process: process(reset_n,clock) begin if l2b_al(reset_n) 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_refresh) begin if l2b_al(reset_n) then need_refresh <= '0'; elsif rising_edge(clock) then need_refresh <= (b2l_ah(refresh_counter = 0) or need_refresh) and init_done and not ack_refresh; end if; end process; request_pending_process: process(reset_n, clock, post_request, request_pending,ack_request,refresh_counter) begin if l2b_al(reset_n) then request_pending <= '0'; elsif rising_edge(clock) then --request_pending <= post_request or (request_pending and not b2l_ah(refresh_counter = 0 )); request_pending <= post_request or (request_pending and not ack_request); end if; end process; init_done_process: process (reset_n,clock,i_state) begin if l2b_al(reset_n) then init_done <= '0'; elsif rising_edge(clock) then init_done <= init_done or b2l_ah(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 l2b_al(reset_n) then i_state <= I_ST_RESET_WAIT; i_next <= I_ST_RESET_WAIT; i_cs_n <=MEM_CS_N_NONE; i_cmd <= MEM_CMD_NOP; i_addr <= (others => '1'); i_count <= 0; i_debug <= DEBUG_0; elsif rising_edge(clock) then if i_state = I_ST_RESET_WAIT then i_debug <= DEBUG_1; -- after reset wait until the refresh_counter ticks over for RAM to stabalize i_cs_n <=MEM_CS_N_NONE; i_cmd <= MEM_CMD_NOP; i_refs <= 0; if refresh_counter=0 then i_state <= I_ST_PRECHARGE; end if; elsif i_state = I_ST_PRECHARGE then i_debug <= DEBUG_2; -- precharge all banks, wait one clock, then go to refresh i_cs_n <=MEM_CS_N_ALL; i_cmd <=MEM_CMD_PRE; i_state <= I_ST_WAIT_COUNT; i_count <= 1; i_next <= I_ST_REFRESH; elsif i_state =I_ST_REFRESH then i_debug <= DEBUG_3; -- repeat 7 times { refresh, wait 5 counts } i_cs_n <=MEM_CS_N_ALL; i_cmd <=MEM_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 i_debug <= DEBUG_4; -- wait i_count ticks then goto state i_next i_cs_n <=MEM_CS_N_ALL; i_cmd <= MEM_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 i_debug <= DEBUG_5; -- set mode, wait 3 ticks then goto done i_cs_n <=MEM_CS_N_ALL; i_cmd <= MEM_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; i_state <= I_ST_WAIT_COUNT; elsif i_state=I_ST_DONE then i_debug <= DEBUG_6; 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,need_refresh,request_pending,request_addr,request_data,request_cs_n,request_rnw,request_dqm,i_debug) begin if reset_n ='0' then m_state <= M_ST_WAITING_INIT; m_next <= M_ST_WAITING_INIT; mem_cmd <= MEM_CMD_NOP; mem_cs_n <= MEM_CS_N_NONE; mem_bank <= (others => '0'); mem_addr <= (others => '0'); mem_data_out <= (others => '0'); mem_dqm <= (others => '0'); m_count <= 0; ack_refresh <= '0'; ack_request <= '0'; mem_oe <= '0'; m_debug(6 downto 0) <=(others =>'0'); m_debug(7) <='1'; elsif rising_edge(clock) then if m_state =M_ST_WAITING_INIT then m_debug(6 downto 0) <= i_debug; m_debug(7) <='1'; mem_addr <= i_addr; mem_cmd <= i_cmd; mem_cs_n <= i_cs_n; if l2b_ah(init_done) then m_state <= M_ST_IDLE; end if; elsif m_state = M_ST_IDLE then m_debug(6 downto 0) <= DEBUG_1; m_debug(7) <='0'; mem_cmd <= MEM_CMD_NOP; ack_refresh <='0'; if l2b_ah(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 mem_cs_n<=MEM_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 (MEM_CMD_IGN) mem_cs_n<=MEM_CS_N_NONE; -- find out if anyone wants us if l2b_ah(request_pending) 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 m_debug(6 downto 0) <= DEBUG_2; -- activate the relevant ROW, wait 3 clocks, then go and do the read or write ack_request <='0'; mem_cs_n <= active_cs_n; mem_cmd <= MEM_CMD_ACTV; mem_bank(1) <= active_addr(23); mem_bank(0) <= active_addr(9); mem_addr <= active_addr(22 downto 10); mem_data_out <= active_data; mem_dqm <= active_dqm; m_state <= M_ST_NOP_COUNT; m_count <= 2; --t 3 clocks : FIXME if l2b_ah(active_rnw) then m_next <= M_ST_READ; else m_next <= M_ST_WRITE; end if; elsif m_state = M_ST_NOP_COUNT then m_debug(6 downto 0) <= DEBUG_3; -- send nops for m_count, then go to state m_next mem_cmd <=MEM_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 m_debug(6 downto 0) <= DEBUG_4; ack_request <='0'; mem_cmd <=MEM_CMD_READ; mem_addr(12 downto 9) <= (others => '0'); mem_addr(8 downto 0) <= active_addr(8 downto 0); m_state <= M_ST_ACTIVE; elsif m_state = M_ST_WRITE then m_debug(6 downto 0) <= DEBUG_5; ack_request <='0'; mem_cmd<=MEM_CMD_WRIT; mem_addr(12 downto 9) <= (others => '0'); mem_addr(8 downto 0) <= active_addr(8 downto 0); mem_oe <= '1'; mem_data_out <= active_data; m_state <=M_ST_ACTIVE; elsif m_state = M_ST_ACTIVE then m_debug(6 downto 0) <= DEBUG_6; -- turn off drivers (we might have come from a write) mem_oe <= '0'; -- sit doing NOPS on this row until either we get a new request -- or we need a refresh mem_cmd <= MEM_CMD_NOP; if l2b_ah(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 l2b_ah(request_pending) 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 l2b_ah(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 m_debug(6 downto 0) <= DEBUG_7; -- wait count then do a precharge mem_cmd <= MEM_CMD_NOP; if m_count > 1 then m_count <= m_count -1; else m_state<=M_ST_PRECHARGE; end if; elsif m_state = M_ST_PRECHARGE then m_debug(6 downto 0) <= DEBUG_8; -- do a precharge, wait two clocks, then goto m_next mem_addr <= (others => '1'); mem_cmd <= MEM_CMD_PRE; m_count <= 1; m_state <= M_ST_NOP_COUNT; elsif m_state=M_ST_REFRESH then m_debug(6 downto 0) <= DEBUG_9; -- do a refresh, wait six clocks, return to idle mem_cmd <= MEM_CMD_REF; m_count <=5; m_next <=M_ST_IDLE; m_state <= M_ST_NOP_COUNT; -- tell the gubbins we did the refresh ack_refresh <= '1'; else -- shouldn't be here - crash m_debug(7) <='1'; m_debug(6 downto 0) <= DEBUG_9; mem_cmd <=MEM_CMD_NOP; mem_cs_n <= MEM_CS_N_NONE; mem_oe <= '0'; end if; end if; end process; -- drive the memory lines sdram_clk <= clock; sdram_cke <= '1'; sdram_cs_n <= mem_cs_n(0); sdram_ras_n <= mem_cmd(2); sdram_cas_n <= mem_cmd(1); sdram_we_n <= mem_cmd(0); sdram_addr <= mem_addr; sdram_ba <= mem_bank; sdram_dq_tristate: process(mem_oe,mem_data_out) begin if l2b_ah(mem_oe) then sdram_dq <= mem_data_out; else sdram_dq <= (others => 'Z' ); end if; end process; mem_data_in <= sdram_dq; sdram_dqm <= mem_dqm; -- a shift register to track the reads read_process: process(reset_n,clock, mem_cmd) begin if l2b_al(reset_n) then r_data_valid <= (others => '0'); elsif rising_edge(clock) then r_data_valid(2 downto 0)<=r_data_valid(3 downto 1); r_data_valid(3) <= b2l_ah(mem_cmd = MEM_CMD_READ); end if; end process; bus_fsm: process (reset_n,clock,b_state,bus_cs_n,bus_rnw,bus_addr,bus_data_in,ack_request,r_data_valid) begin if l2b_al(reset_n) then bus_data_out <= (others => '0'); request_data <= (others => '0'); request_addr <= (others => '0'); request_rnw <= '0'; request_cs_n <= MEM_CS_N_NONE; request_dqm <= "00"; b_state <= B_ST_WAIT_CS_N_LOW; b_debug(6 downto 0) <= DEBUG_0; b_debug(7) <='0'; bus_wait_n <= '0'; elsif rising_edge(clock) then if b_state = B_ST_WAIT_CS_N_LOW then b_debug(6 downto 0) <= DEBUG_1; bus_wait_n <= '0'; if l2b_al(bus_cs_n) then -- new request ship it to the main state machine post_request <='1'; request_addr <= bus_addr; request_rnw <= bus_rnw; request_data <= bus_data_in; -- send to first chip and all bytes request_cs_n <= "0"; -- (others => '1'); request_dqm <= "00"; b_state <= B_ST_LODGE_REQUEST; end if; elsif b_state =B_ST_LODGE_REQUEST then b_debug(6 downto 0) <= DEBUG_2; post_request <='0'; b_state <= B_ST_WAIT_ACK; elsif b_state =B_ST_WAIT_ACK then b_debug(6 downto 0) <= DEBUG_3; if not l2b_ah(request_pending) then -- the logic has pushed the request to the ram if l2b_al(request_rnw) then -- if it's a write we're all done b_state <= B_ST_WAIT_CS_N_HIGH; else -- if it's a read we have to wait for the data b_state <= B_ST_WAIT_DATA; end if; end if; elsif b_state = B_ST_WAIT_DATA then b_debug(6 downto 0) <= DEBUG_4; if l2b_ah(r_data_valid(0)) then bus_data_out <= mem_data_in; -- bus_data_out <= request_addr(15 downto 0); b_state <= B_ST_WAIT_CS_N_HIGH; end if; elsif b_state = B_ST_WAIT_CS_N_HIGH then b_debug(6 downto 0) <= DEBUG_5; b_debug(7) <='1'; bus_wait_n <= '1'; if not l2b_al(bus_cs_n) then b_state <=B_ST_WAIT_CS_N_LOW; end if; else b_state <=B_ST_WAIT_CS_N_LOW; end if; end if; end process; -- debug <=b_debug; debug <= m_debug; end;