------------------------------------------------------------------------- -- Project: SDRAM controller -- This file: SDRAM main controller and user I/F -- -- Copyright (C) 2007 J. Ahrensfeld -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . -- -- For questions and ideas, please contact the author at jens@jayfield.org -- -------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.numeric_std.ALL; use work.sdram_config.all; use work.sdram_types.all; use work.utils_pkg.all; entity sdram_ctrl is Generic ( f_sysclk : natural := 100E6; BL : natural := 2 ); Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; u_busy : out STD_LOGIC; u_tag_in : in user_tag_t; u_tag_out : out user_tag_t; u_addr : in user_addr_t; u_cmd : in user_cmd_t; u_cmd_we : in STD_LOGIC; col_addr : out col_addr_t; sdr_cmd_busy : in STD_LOGIC; sdr_cmd : out sdr_cmd_t; sdr_cmd_we : out STD_LOGIC; sdr_mode : out mode_word_t; sdr_cke : out STD_LOGIC ); end sdram_ctrl; architecture behaviour of sdram_ctrl is constant LMR_BURST_LEN : natural := NextExpBaseTwo(BL); type ctrl_state_t is (RESET, POWER_WAIT, INIT, INIT_WAIT, USER_READY, USER_WRITE_PRE, USER_WRITE_ACT, USER_WRITE, USER_READ_PRE, USER_READ_ACT, USER_READ, REFRESH_PRE, REFRESH); signal st_ctrl, st_ctrl_next : ctrl_state_t; constant PWR_UP_CLOCK_INTERVAL : natural := PWR_UP_WAIT*(f_sysclk/1E6); signal pwr_up_cnt : natural range 0 to PWR_UP_CLOCK_INTERVAL-1; signal pwr_up_cnt_rst : std_logic; signal pwr_up_finished : std_logic; signal cycle_cnt : natural range 0 to 255; signal cc_preset : natural range 0 to 255; signal cc_load_en : std_logic; signal cycle_finished : std_logic; signal seq_cnt : natural range 0 to 31; signal seq_rst_en : std_logic; signal seq_cnt_en : std_logic; constant REFRESH_CLOCK_INTERVAL : natural := natural(REFRESH_INTERVAL*real(f_sysclk)/1.0E6); signal refresh_cnt : natural range 0 to REFRESH_CLOCK_INTERVAL-1; signal refresh_request : std_logic; signal refresh_cnt_rst : std_logic; signal addr_reg_load_en : std_logic; signal u_tag_reg : user_tag_t; signal bank_addr_reg : bank_addr_t; signal row_addr_reg : row_addr_t; signal col_addr_reg : col_addr_t; signal bank_addr_last : bank_addr_t; signal row_addr_last : row_addr_t; signal col_addr_last : col_addr_t; signal u_bank_addr : bank_addr_t; signal u_row_addr : row_addr_t; signal u_col_addr : col_addr_t; signal bank_active_reg : unsigned(0 to 3); signal bank_is_active : std_logic; signal bank_activate : std_logic; signal bank_clr : std_logic; type init_seq_rom_t is array (0 to 14) of init_seq_t; constant init_seq_rom : init_seq_rom_t := ( ( cmd => SD_NOP, mode_word => (others => '0'), wait_cycle => 0 ), ( cmd => SD_PRE, mode_word => "000010000000000", wait_cycle => 0 ), ( cmd => SD_NOP, mode_word => (others => '0'), wait_cycle => 0 ), ( cmd => SD_LMR, mode_word => to_unsigned(LMR_REG_EXTENDED, 2) & "0000000000010", wait_cycle => 0 ), ( cmd => SD_NOP, mode_word => (others => '0'), wait_cycle => 0 ), ( cmd => SD_LMR, mode_word => to_unsigned(LMR_REG_BASE, 2) & to_unsigned(LMR_OP_RES_DLL, 6) & to_unsigned(LMR_CL2, 3) & '0' & to_unsigned(LMR_BURST_LEN, 3), wait_cycle => 200 ), ( cmd => SD_NOP, mode_word => (others => '0'), wait_cycle => 0 ), ( cmd => SD_PRE, mode_word => "000010000000000", wait_cycle => 0 ), ( cmd => SD_NOP, mode_word => (others => '0'), wait_cycle => 0 ), ( cmd => SD_AR, mode_word => (others => '0'), wait_cycle => 0 ), ( cmd => SD_NOP, mode_word => (others => '0'), wait_cycle => 0 ), ( cmd => SD_AR, mode_word => (others => '0'), wait_cycle => 0 ), ( cmd => SD_NOP, mode_word => (others => '0'), wait_cycle => 0 ), ( cmd => SD_LMR, mode_word => to_unsigned(LMR_REG_BASE, 2) & to_unsigned(LMR_OP_NORMAL, 6) & to_unsigned(LMR_CL2, 3) & '0' & to_unsigned(LMR_BURST_LEN, 3), wait_cycle => 200 ), ( cmd => SD_NOP, mode_word => (others => '0'), wait_cycle => 0 ) ); begin u_row_addr <= u_addr(user_addr_t'left downto user_addr_t'left-row_addr_t'length+1); u_bank_addr <= u_addr(user_addr_t'left-row_addr_t'length downto user_addr_t'left-row_addr_t'length-bank_addr_t'length+1); u_col_addr <= u_addr(user_addr_t'left-row_addr_t'length-bank_addr_t'length downto 0); bank_is_active <= bank_active_reg(to_integer(u_bank_addr)); ------------------------------------------------------------------------------------------ fsm_ctrl_state: process (st_ctrl, u_tag_in, u_tag_reg, u_cmd, u_cmd_we, sdr_cmd_busy, pwr_up_finished, seq_cnt, cycle_finished, u_bank_addr, u_row_addr, bank_addr_reg, row_addr_reg, u_col_addr, col_addr_reg, bank_addr_last, row_addr_last, refresh_request, bank_is_active) begin u_busy <= '1'; pwr_up_cnt_rst <= '0'; cc_preset <= init_seq_rom(seq_cnt).wait_cycle; cc_load_en <= '0'; sdr_cmd <= init_seq_rom(seq_cnt).cmd; sdr_cmd_we <= '0'; sdr_mode <= bank_addr_reg & row_addr_reg; col_addr <= col_addr_reg; sdr_cke <= '1'; seq_cnt_en <= '0'; seq_rst_en <= '0'; addr_reg_load_en <= '0'; refresh_cnt_rst <= '0'; bank_activate <= '0'; bank_clr <= '0'; u_tag_out <= u_tag_reg; st_ctrl_next <= st_ctrl; case st_ctrl is when RESET => sdr_cke <= '0'; pwr_up_cnt_rst <= '1'; st_ctrl_next <= POWER_WAIT; when POWER_WAIT => sdr_cke <= '0'; if pwr_up_finished = '1' then seq_rst_en <= '1'; st_ctrl_next <= INIT; end if; when INIT => sdr_mode <= init_seq_rom(seq_cnt).mode_word; sdr_cmd <= init_seq_rom(seq_cnt).cmd; cc_preset <= init_seq_rom(seq_cnt).wait_cycle; if seq_cnt = init_seq_rom_t'high then bank_clr <= '1'; refresh_cnt_rst <= '1'; st_ctrl_next <= USER_READY; elsif sdr_cmd_busy = '0' then cc_load_en <= '1'; sdr_cmd_we <= '1'; st_ctrl_next <= INIT_WAIT; end if; when INIT_WAIT => if sdr_cmd_busy = '0' and cycle_finished = '1' then seq_cnt_en <= '1'; st_ctrl_next <= INIT; end if; when USER_READY => if refresh_request = '1' then st_ctrl_next <= REFRESH_PRE; else u_busy <= '0'; col_addr <= u_col_addr; u_tag_out <= u_tag_in; sdr_mode <= u_bank_addr & u_row_addr; sdr_mode(BIT_AUTO_PRE) <= ENABLE_AUTO_PRE; if u_cmd_we = '1' then addr_reg_load_en <= '1'; case u_cmd is when UCMD_NOP => sdr_cmd <= SD_NOP; when UCMD_LMR => sdr_cmd <= SD_NOP; when UCMD_WRITE => sdr_cmd <= SD_WRITE; if (u_row_addr /= row_addr_reg) then st_ctrl_next <= USER_WRITE_PRE; elsif bank_is_active = '0' then st_ctrl_next <= USER_WRITE_ACT; elsif sdr_cmd_busy = '1' then st_ctrl_next <= USER_WRITE; else sdr_cmd_we <= '1'; end if; when UCMD_READ => sdr_cmd <= SD_READ; if (u_row_addr /= row_addr_reg) then st_ctrl_next <= USER_READ_PRE; elsif bank_is_active = '0' then st_ctrl_next <= USER_READ_ACT; elsif sdr_cmd_busy = '1' then st_ctrl_next <= USER_READ; else sdr_cmd_we <= '1'; end if; when others => null; end case; end if; end if; when USER_WRITE_PRE => bank_clr <= '1'; sdr_cmd <= SD_PRE; sdr_mode <= bank_addr_last & row_addr_last; sdr_mode(BIT_PRE_ALL) <= ENABLE_PRE_ALL; if sdr_cmd_busy = '0' then sdr_cmd_we <= '1'; st_ctrl_next <= USER_WRITE_ACT; end if; when USER_WRITE_ACT => bank_activate <= '1'; sdr_cmd <= SD_ACT; if sdr_cmd_busy = '0' then sdr_cmd_we <= '1'; st_ctrl_next <= USER_WRITE; end if; when USER_WRITE => sdr_cmd <= SD_WRITE; sdr_mode(BIT_AUTO_PRE) <= ENABLE_AUTO_PRE; if sdr_cmd_busy = '0' then sdr_cmd_we <= '1'; st_ctrl_next <= USER_READY; end if; when USER_READ_PRE => bank_clr <= '1'; sdr_cmd <= SD_PRE; sdr_mode <= bank_addr_last & row_addr_last; sdr_mode(BIT_PRE_ALL) <= ENABLE_PRE_ALL; if sdr_cmd_busy = '0' then sdr_cmd_we <= '1'; st_ctrl_next <= USER_READ_ACT; end if; when USER_READ_ACT => bank_activate <= '1'; sdr_cmd <= SD_ACT; if sdr_cmd_busy = '0' then sdr_cmd_we <= '1'; st_ctrl_next <= USER_READ; end if; when USER_READ => sdr_cmd <= SD_READ; sdr_mode(BIT_AUTO_PRE) <= ENABLE_AUTO_PRE; if sdr_cmd_busy = '0' then sdr_cmd_we <= '1'; st_ctrl_next <= USER_READY; end if; when REFRESH_PRE => bank_clr <= '1'; sdr_cmd <= SD_PRE; -- sdr_mode <= bank_addr_last & row_addr_last; sdr_mode(BIT_PRE_ALL) <= ENABLE_PRE_ALL; if sdr_cmd_busy = '0' then sdr_cmd_we <= '1'; st_ctrl_next <= REFRESH; end if; when REFRESH => sdr_cmd <= SD_AR; if sdr_cmd_busy = '0' then sdr_cmd_we <= '1'; st_ctrl_next <= USER_READY; refresh_cnt_rst <= '1'; end if; when others => st_ctrl_next <= RESET; end case; end process; fsm_ctrl_state_next: process (rst, clk) begin if rst = '1' then st_ctrl <= RESET; elsif rising_edge(clk) then st_ctrl <= st_ctrl_next; end if; end process; ------------------------------------------------------------------------------------------ bank_active_register: process (clk) begin if rising_edge(clk) then if bank_clr = '1' then bank_active_reg <= (others => '0'); -- assumes PRE_ALL !!!!!!!!!!!!! elsif bank_activate = '1' then bank_active_reg(to_integer(bank_addr_reg)) <= '1'; end if; end if; end process; ------------------------------------------------------------------------------------------ user_addr_register: process (rst, clk) begin if rst = '1' then bank_addr_reg <= (others => '0'); row_addr_reg <= (others => '0'); col_addr_reg <= (others => '0'); u_tag_reg <= (others => '0'); elsif rising_edge(clk) then if addr_reg_load_en = '1' then bank_addr_last <= bank_addr_reg; row_addr_last <= row_addr_reg; col_addr_last <= col_addr_reg; bank_addr_reg <= u_bank_addr; row_addr_reg <= u_row_addr; col_addr_reg <= u_col_addr; u_tag_reg <= u_tag_in; end if; end if; end process; ------------------------------------------------------------------------------------------ power_up_counter: process (rst, clk) begin if rst = '1' then pwr_up_cnt <= 0; pwr_up_finished <= '0'; elsif rising_edge(clk) then pwr_up_finished <= '0'; if pwr_up_cnt_rst = '1' then pwr_up_cnt <= PWR_UP_CLOCK_INTERVAL-1; else if pwr_up_cnt /= 0 then pwr_up_cnt <= pwr_up_cnt - 1; else pwr_up_finished <= '1'; end if; end if; end if; end process; ------------------------------------------------------------------------------------------ refresh_counter: process (rst, clk) begin if rst = '1' then refresh_cnt <= 0; refresh_request <= '0'; elsif rising_edge(clk) then refresh_request <= '0'; if refresh_cnt_rst = '1' then refresh_cnt <= REFRESH_CLOCK_INTERVAL-1; else if refresh_cnt /= 0 then refresh_cnt <= refresh_cnt - 1; else refresh_request <= '1'; end if; end if; end if; end process; ------------------------------------------------------------------------------------------ cycle_counter: process (rst, clk) begin if rst = '1' then cycle_cnt <= 0; cycle_finished <= '0'; elsif rising_edge(clk) then cycle_finished <= '0'; if cc_load_en = '1' then cycle_cnt <= cc_preset; elsif cycle_cnt /= 0 then cycle_cnt <= cycle_cnt - 1; else cycle_finished <= '1'; end if; end if; end process; ------------------------------------------------------------------------------------------ seq_counter: process (rst, clk) begin if rst = '1' then seq_cnt <= 0; elsif rising_edge(clk) then if seq_rst_en = '1' then seq_cnt <= 0; elsif seq_cnt_en = '1' then if seq_cnt /= init_seq_rom_t'high then seq_cnt <= seq_cnt + 1; end if; end if; end if; end process; ------------------------------------------------------------------------------------------ end behaviour;