summaryrefslogtreecommitdiffstats
path: root/sdram_ctrl.vhd
diff options
context:
space:
mode:
Diffstat (limited to 'sdram_ctrl.vhd')
-rw-r--r--sdram_ctrl.vhd615
1 files changed, 615 insertions, 0 deletions
diff --git a/sdram_ctrl.vhd b/sdram_ctrl.vhd
new file mode 100644
index 0000000..b0b4a1f
--- /dev/null
+++ b/sdram_ctrl.vhd
@@ -0,0 +1,615 @@
+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;
+
+
+
+
+