------------------------------------------------------------------------- -- Project: SDRAM controller -- This file: SDRAM command controller -- -- 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_const.all; use work.sdram_config.all; use work.sdram_types.all; entity sdram_cmd is Generic ( F_SDRCLK : real := 100.0 ); Port ( rst : in STD_LOGIC; clk : in STD_LOGIC; enable : in STD_LOGIC; tag : in user_tag_t; col_addr : in col_addr_t; mode_word : in mode_word_t; cmd : in sdr_cmd_t; cmd_we : in STD_LOGIC; cmd_ack : out STD_LOGIC; phy_ctrl : out phy_ctrl_t ); end sdram_cmd; architecture behaviour of sdram_cmd is subtype cycle_cnt_t is natural range 0 to 15; subtype burst_cnt_t is natural range 0 to 2**(LMR_BL_CURR)-1; signal cc_preset : cycle_cnt_t; signal cc_load_en : std_logic; signal cycle_finished : std_logic; signal cycle_cnt : cycle_cnt_t; signal burst_load_en : std_logic; signal burst_finished : std_logic; signal burst_cnt : burst_cnt_t; signal st_sdr, st_sdr_next : sdr_state_t; type part_timing_array_t is array (sdr_cmd_t) of cycle_cnt_t; constant TIMING : part_timing_array_t := -- command cycle_cnt ( SD_DESELECT => (0 ), SD_NOP => (0 ), SD_LMR => (TMRD-1 ), SD_ACT => (to_cycles(TRCD, F_SDRCLK)-1), SD_READ => (TCAS-1 ), SD_WRITE => (to_cycles(TWR, F_SDRCLK)-1), SD_PRE => (to_cycles(TRP, F_SDRCLK)-1), SD_BST => (0 ), SD_AR => (to_cycles(TRFC, F_SDRCLK)-1), SD_SR => (to_cycles(TRFC, F_SDRCLK)-1) ); begin ------------------------------------------------------------------------------------------ fsm_sdr_state: process (st_sdr, cmd, cmd_we, cycle_finished, burst_finished, mode_word, enable, col_addr, tag) begin st_sdr_next <= st_sdr; phy_ctrl.part.cmd <= COMMAND(SD_NOP); phy_ctrl.part.cke <= enable; phy_ctrl.u_tag <= tag; cc_load_en <= '0'; cc_preset <= TIMING(cmd); burst_load_en <= '0'; cmd_ack <= '0'; phy_ctrl.re <= '0'; phy_ctrl.drive_en <= '0'; phy_ctrl.we <= '0'; phy_ctrl.utag_we <= '0'; phy_ctrl.part.addr <= mode_word(phy_ctrl.part.addr'left downto phy_ctrl.part.addr'right); phy_ctrl.part.ba <= mode_word(mode_word_t'left downto mode_word_t'left-1); case st_sdr is when PWR_DOWN => phy_ctrl.part.cmd <= COMMAND(SD_DESELECT); if enable = '1' then st_sdr_next <= IDLE; end if; when PRECHARGE => if cycle_finished = '1' then st_sdr_next <= IDLE; end if; when MODE => if cycle_finished = '1' then st_sdr_next <= IDLE; end if; when IDLE => if cmd_we = '1' then cmd_ack <= '1'; cc_load_en <= '1'; cc_preset <= TIMING(cmd); phy_ctrl.part.cmd <= COMMAND(cmd); case cmd is when SD_PRE => st_sdr_next <= PRECHARGE; when SD_LMR => st_sdr_next <= MODE; when SD_ACT => st_sdr_next <= ROW_ACT; when SD_AR => st_sdr_next <= AUTO_REF; when others => null; end case; end if; when ROW_ACT => if cycle_finished = '1' then if cmd_we = '1' then cmd_ack <= '1'; burst_load_en <= '1'; cc_load_en <= '1'; cc_preset <= TIMING(cmd); phy_ctrl.part.cmd <= COMMAND(cmd); case cmd is when SD_PRE => st_sdr_next <= PRECHARGE; when SD_READ => phy_ctrl.utag_we <= '1'; st_sdr_next <= READ; phy_ctrl.part.addr(col_addr_t'range) <= col_addr; when SD_WRITE => phy_ctrl.utag_we <= '1'; phy_ctrl.drive_en <= '1'; st_sdr_next <= WRITE; phy_ctrl.part.addr(col_addr_t'range) <= col_addr; when others => null; end case; end if; end if; when WRITE => phy_ctrl.drive_en <= '1'; phy_ctrl.we <= '1'; phy_ctrl.part.cmd <= COMMAND(SD_NOP); if cmd_we = '1' and cmd = SD_WRITE then cmd_ack <= '1'; phy_ctrl.utag_we <= '1'; phy_ctrl.part.addr(col_addr_t'range) <= col_addr; if burst_finished = '1' then burst_load_en <= '1'; phy_ctrl.part.cmd <= COMMAND(cmd); end if; else phy_ctrl.drive_en <= '0'; cc_load_en <= '1'; cc_preset <= TIMING(SD_WRITE)+1; st_sdr_next <= ROW_ACT; end if; when READ => phy_ctrl.re <= '1'; phy_ctrl.part.cmd <= COMMAND(SD_NOP); if cmd_we = '1' and cmd = SD_READ then cmd_ack <= '1'; phy_ctrl.utag_we <= '1'; phy_ctrl.part.addr(col_addr_t'range) <= col_addr; if burst_finished = '1' then burst_load_en <= '1'; phy_ctrl.part.cmd <= COMMAND(cmd); end if; else cc_load_en <= '1'; cc_preset <= TIMING(SD_READ); st_sdr_next <= ROW_ACT; end if; when WRITE_A => -- not implemented yet cmd_ack <= '1'; st_sdr_next <= IDLE; when READ_A => -- not implemented yet cmd_ack <= '1'; st_sdr_next <= IDLE; when BURST_STOP => -- not implemented yet cmd_ack <= '1'; st_sdr_next <= IDLE; when SELF_REF => -- not implemented yet cmd_ack <= '1'; st_sdr_next <= IDLE; when PRE_PWR_DOWN => -- not implemented yet cmd_ack <= '1'; st_sdr_next <= IDLE; when ACT_PWR_DOWN => -- not implemented yet cmd_ack <= '1'; st_sdr_next <= IDLE; when AUTO_REF => if cycle_finished = '1' then st_sdr_next <= IDLE; end if; when others => st_sdr_next <= IDLE; end case; end process; fsm_sdr_state_next: process (clk) begin if rising_edge(clk) then if rst = '1' then st_sdr <= PWR_DOWN; else st_sdr <= st_sdr_next; end if; end if; end process; ------------------------------------------------------------------------------------------ cycle_counter: process (clk) begin if 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; ------------------------------------------------------------------------------------------ burst_counter: process (clk) begin if rising_edge(clk) then if burst_load_en = '1' then burst_finished <= '0'; burst_cnt <= burst_cnt_t'high; if 2**(LMR_BL_CURR) = DATA_RATE_FACTOR then burst_finished <= '1'; end if; elsif burst_cnt <= DATA_RATE_FACTOR then burst_finished <= '1'; else burst_cnt <= burst_cnt - DATA_RATE_FACTOR; end if; end if; end process; ------------------------------------------------------------------------------------------ end behaviour;