OpenCores
URL https://opencores.org/ocsvn/funbase_ip_library/funbase_ip_library/trunk

Subversion Repositories funbase_ip_library

[/] [funbase_ip_library/] [trunk/] [TUT/] [ip.hwp.storage/] [sdram2hibi/] [1.0/] [vhd/] [sdram_controller_de2.vhd] - Rev 145

Compare with Previous | Blame | View Log

-------------------------------------------------------------------------------
-- Title      : sdram_controller, modified for DE2 by A.Alhonen.
-- Project    : 
-------------------------------------------------------------------------------
-- File       : sdram_controller.vhd
-- Author     : 
-- Company    : 
-- Created    : 2005-06-08
-- Platform   : 
-- Standard   : VHDL'87
-------------------------------------------------------------------------------
-- Description: Altera DE2 uses A2V64S40TCP SDRAM.
-------------------------------------------------------------------------------
-- Copyright (c) 2005 
-------------------------------------------------------------------------------
-- Revisions  :
-- Date        Version  Author  Description
-- 2005-06-08  1.0      penttin5        Created
--
-- 2005-07-12  1.1      penttin5        Split command_in port to command_in,
--                                        address_in, data_amount_in and
--                                        byte_select_in.
--                                      Changed behavior
--                                        If output fifo is full in read
--                                        operation or input fifo is empty in
--                                        write operation, stop operation and
--                                        go to idle to have a new command.
--                               AA 2012: Why?! I decided not to fix it here
--                                        because apparently there is some
--                                        reason to this.
--                                      Data_amount_in width is now generic
-- 19.04.2007           penttin5        Added generic sim_ena_g to make
--                                        simulation easier. When sim_ena_g is
--                                        1, the long init wait is skipped
-- 12.08.2009           alhonena        Started modifying for DE2: 16-bit data,
--                                      different timing.
-- 16.07.2011           alhonena        Continued modifying for DE2
-------------------------------------------------------------------------------
--
-- NOTE!!! The following assignments must be used in Quartus to
--         ensure correct operation:
--
--         In Assignment Editor: Logic options
--
-- from    to                     Assignment name              Value Enabled
--         sdram_data_out_r     : fast output register         On    Yes
--         write_on_r           : fast output enable register  On    Yes
--         data_out             : fast input register          On    Yes
--
-- Use node finder to find these nodes.
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
 
entity sdram_controller is
  generic (
    clk_freq_mhz_g      : integer := 143;  -- clock frequency in MHz
    mem_addr_width_g    : integer := 22;
    amountw_g           : integer := 22;
    block_read_length_g : integer := 640;
    sim_ena_g           : integer := 0  -- skips the init wait phase
    );
 
  port (
    clk   : in std_logic;
    rst_n : in std_logic;
 
    command_in             : in    std_logic_vector(1 downto 0);
    address_in             : in    std_logic_vector(mem_addr_width_g-1 downto 0);
    data_amount_in         : in    std_logic_vector(amountw_g - 1
                                                    downto 0);
    byte_select_in         : in    std_logic_vector(1 downto 0);  -- ACTIVE LOW!
    input_empty_in         : in    std_logic;
    input_one_d_in         : in    std_logic;
    output_full_in         : in    std_logic;
    data_in                : in    std_logic_vector(15 downto 0);
    write_on_out           : out   std_logic;
    busy_out               : out   std_logic;
    output_we_out          : out   std_logic;  -- this is combinational
    input_re_out           : out   std_logic;
    data_out               : out   std_logic_vector(15 downto 0);
    sdram_data_inout       : inout std_logic_vector(15 downto 0);
    sdram_cke_out          : out   std_logic;
    sdram_cs_n_out         : out   std_logic;
    sdram_we_n_out         : out   std_logic;
    sdram_ras_n_out        : out   std_logic;
    sdram_cas_n_out        : out   std_logic;
    sdram_dqm_out          : out   std_logic_vector(1 downto 0);
    sdram_ba_out           : out   std_logic_vector(1 downto 0);
    sdram_address_out      : out   std_logic_vector(11 downto 0)
    );
 
  attribute useioff : boolean;
  attribute useioff of data_out : signal is true;
  attribute useioff of sdram_data_inout : signal is true;
 
end;
 
architecture rtl of sdram_controller is
 
  -- purpose: counts the needed delays in clock cycles from clock frequency
  --          and nanoseconds
  function calculate_cycles (clk_freq_mhz : integer;
                             time_ns      : integer;
                             round        : string)
    return integer is
    variable result_cycles : integer;
  begin  -- calculate_cycles
    assert round = "down" or round = "up"
      report
      "Error in calling function calculate_cycles: unknown rounding parameter!"
      severity failure;
    result_cycles := (clk_freq_mhz * time_ns);
    if result_cycles rem 1000 /= 0 then
      if round = "up" then
        result_cycles := result_cycles + 1000;
      end if;
    end if;
    return result_cycles/1000;
  end calculate_cycles;
 
  -- purpose: sets correct cas latency according to clock frequency and
  --          checks that clock frequency isn't higher than 143MHz
  function set_cas_latency (
    clk_freq_mhz_g : integer)
    return integer is
    variable cas_result : integer;
  begin  -- set_cas_latency
    if clk_freq_mhz_g <= 50 then
      cas_result := 2;
    elsif clk_freq_mhz_g <= 100 then
      cas_result := 2;
    elsif clk_freq_mhz_g <= 143 then
      cas_result := 3;
    else
      assert false report "Clock frequency too high (>143MHz) for SDRAM"
        severity warning;
    end if;
    return cas_result;
  end set_cas_latency;
 
  -- ACTIVE to ACTIVE in the same bank
  -- tRC = 63ns
  constant t_rc_c      : integer := calculate_cycles(clk_freq_mhz_g, 63, "up");
  -- ACTIVE to READ/WRITE
  -- tRCD = 21ns
  constant t_rcd_c     : integer := calculate_cycles(clk_freq_mhz_g, 21, "up");
  -- ACTIVE to PRECHARGE
  -- tRAS(min) = 42ns
  constant t_ras_min_c : integer := calculate_cycles(clk_freq_mhz_g, 42, "up");
  -- ACTIVE to PRECHARGE
  -- tRAS(max) = 100 000ns
  constant t_ras_max_c :
    integer := calculate_cycles(clk_freq_mhz_g, 100000, "up") - 2;
  -- Auto refresh period
  -- tRFC = 70ns = tRC
  constant t_rfc_c : integer := calculate_cycles(clk_freq_mhz_g, 70, "up");
  -- Precharge period
  -- tRP = 21ns
  constant t_rp_c :
    integer := calculate_cycles(clk_freq_mhz_g, 21, "up");
  -- ACTIVE to ACTIVE, different banks
  -- tRRD = 14ns
  constant t_rrd_c : integer := calculate_cycles(clk_freq_mhz_g, 14, "up");
  -- Write recovery time -- Tätä ei jostain syystä löydy datalehdestä, jätin ennalleen.
  -- tWR = 14ns wo/autoprecharge = 1clk + 7ns w/autoprecharge
  constant t_wr_c  : integer := calculate_cycles(clk_freq_mhz_g, 14, "up");
  -- CAS latency +1(registered output causes 1 extra cycle latency)
  constant t_cas_c : integer := set_cas_latency(clk_freq_mhz_g) + 1;
  -- LOAD MODE REGISTER command period
  -- tMRD = 2 cycles
  constant t_mrd_c : integer := 2;
 
  -- Evenly distributed refresh commands
  -- 1 autorefresh cycle per 15,625us = 15625ns
  -- 1 cycle removed to compensate clock uncertainty
 
  constant refresh_period_c : integer :=
    calculate_cycles(clk_freq_mhz_g, 15625, "down") - 1; --30;
 
  constant refresh_latency_write_c : integer :=
    t_wr_c + t_rp_c + t_rfc_c + 3;
--    t_ras_min_c + t_rp_c;
  constant refresh_latency_read_c : integer :=
    t_cas_c + t_rp_c + t_rfc_c + 2;
--    t_cas_c + t_ras_min_c + t_rp_c + 2;
  constant refresh_latency_idle_c : integer :=
    t_rcd_c + t_rp_c + t_rfc_c + 8;
 
  -- after reset 200us = 200000ns of NOPs
  constant init_wait_cycles_c : integer :=
    calculate_cycles(clk_freq_mhz_g, 200000, "up");
 
  -- commands
  constant command_nop_c   : std_logic_vector(1 downto 0) := "00";
  constant command_read_c  : std_logic_vector(1 downto 0) := "01";
  constant command_write_c : std_logic_vector(1 downto 0) := "10";
 
  -- SDRAM commands
  constant sdram_nop_c          : std_logic_vector(3 downto 0) := "0111";
  constant sdram_active_c       : std_logic_vector(3 downto 0) := "0011";
  constant sdram_read_c         : std_logic_vector(3 downto 0) := "0101";
  constant sdram_write_c        : std_logic_vector(3 downto 0) := "0100";
  constant sdram_precharge_c    : std_logic_vector(3 downto 0) := "0010";
  constant sdram_auto_refresh_c : std_logic_vector(3 downto 0) := "0001";
  constant sdram_load_mode_c    : std_logic_vector(3 downto 0) := "0000";
 
  -- SDRAM default mode = burst length 1
  constant default_mode_c : std_logic_vector(11 downto 0) :=
    "00000" & conv_std_logic_vector(t_cas_c - 1, 3) & "0000";
  -- Write Burst Lengthiksi vaihdettu "single bit".
  -- Vaihdettu takas nollaks (= burst). Se on toi keskimmäinen noista viidestä.
 
  -- SDRAM command register
  signal sdram_command_r : std_logic_vector(3 downto 0);
 
  -- SDRAM address register
  signal sdram_address_r : std_logic_vector(mem_addr_width_g - 1 downto 0);
 
  -- registers for operations longer than one word
  signal data_counter_r : std_logic_vector(data_amount_in'length - 1
                                           downto 0);
  signal succesful_reads_r : std_logic_vector(data_amount_in'length - 1
                                              downto 0);
 
  signal data_amount_r : std_logic_vector(data_amount_in'length - 1
                                          downto 0);
 
  signal read_on_r   : std_logic;
  signal output_we_r : std_logic;
 
  -- state machine signals
  type state_vector is (init_wait,
                        init_precharge,
                        init_auto_refresh1,
                        init_auto_refresh2,
                        init_load_mode_register,
                        idle,
                        idle_auto_refresh,
 
                        start_read,
                        continue_read,
                        read_refresh_precharge,
                        read_refresh,
                        read_refresh_wait,
                        read_finished,
                        read_finished_precharge,
 
                        start_write,
                        wait_write_active_to_active,
                        continue_write,
                        write_refresh_precharge,
                        write_refresh,
                        write_refresh_wait,
                        write_change_row_precharge,
                        write_finished,
                        write_finished_precharge
                        );
 
  signal current_state_r : state_vector;
 
  -- signals for timing
  signal t_cas_r         : integer range 0 to t_cas_c - 1;
  signal t_end_cas_r     : integer range 0 to t_cas_c - 1;
  signal t_rfc_r         : integer range 0 to t_rfc_c - 1;
  signal t_rp_r          : integer range 0 to t_rp_c - 1;
  signal t_from_active_r : integer range 0 to t_ras_max_c - 2;
 
  signal t_wr_r               : integer range 0 to t_wr_c - 1;
  signal t_mrd_r              : integer range 0 to t_mrd_c - 1;
  signal t_from_refresh_r     : integer range 0 to init_wait_cycles_c - 1;
  signal refresh_period_clr_r : std_logic;
  signal row_active_r         : std_logic;
  signal input_re_out_r       : std_logic;
 
  signal sdram_data_out_r : std_logic_vector(15 downto 0);
  signal input_one_d_in_r   : std_logic;
  signal input_empty_in_r   : std_logic;
 
  signal write_on_r : std_logic;
 
  signal data_to_sdram2hibi_r : std_logic_vector(15 downto 0);
 
begin  -- rtl
 
  write_on_out <= write_on_r;
 
  data_out <= data_to_sdram2hibi_r;
 
  output_we_out <= output_we_r and not(output_full_in);
 
  input_re_out <= input_re_out_r;
 
  sdram_data_inout <= sdram_data_out_r when write_on_r = '1'
                      else (others => 'Z');
  -- assign commands to sdram control lines
  sdram_cs_n_out  <= sdram_command_r(3);
  sdram_ras_n_out <= sdram_command_r(2);
  sdram_cas_n_out <= sdram_command_r(1);
  sdram_we_n_out  <= sdram_command_r(0);
 
  -- sdram clock is always enabled
  sdram_cke_out <= '1';
 
  register_inouts : process (clk, rst_n)
  begin  -- process register_inouts
 
    if rst_n = '0' then                 -- asynchronous reset (active low)
      sdram_data_out_r   <= (others => '0');
      input_empty_in_r     <= '0';
      input_one_d_in_r     <= '0';
      data_to_sdram2hibi_r <= (others => '0');
 
    elsif clk'event and clk = '1' then  -- rising clock edge
      sdram_data_out_r   <= data_in;
      input_empty_in_r     <= input_empty_in;
      input_one_d_in_r     <= input_one_d_in;
      data_to_sdram2hibi_r <= sdram_data_inout;
    end if;
  end process register_inouts;
 
  -- purpose: State machine
  state_machine_proc : process (clk, rst_n)
  begin  -- process state_machine_proc
    if rst_n = '0' then                 -- asynchronous reset (active low)
      row_active_r         <= '0';
      sdram_dqm_out        <= (others => '0');
      t_wr_r               <= 0;
      data_counter_r       <= (others => '0');
      data_amount_r        <= (others => '0');
      sdram_ba_out         <= (others => '0');
      sdram_address_out    <= (others => '0');
      t_rp_r               <= 0;
      t_rfc_r              <= 0;
      t_mrd_r              <= 0;
      t_end_cas_r          <= 0;
      refresh_period_clr_r <= '0';
      input_re_out_r       <= '0';
      sdram_command_r      <= sdram_nop_c;
      sdram_address_r      <= (others => '0');
      current_state_r      <= init_wait;
      busy_out             <= '1';
      read_on_r            <= '0';
      write_on_r           <= '0';
    elsif clk'event and clk = '1' then  -- rising clock edge
 
      case current_state_r is
 
-------------------------------------------------------------------------------
-- start initialization sequence
-------------------------------------------------------------------------------
        -- after reset NOP commands for 100us
        when init_wait =>
          refresh_period_clr_r <= '0';
          if t_from_refresh_r = init_wait_cycles_c - 1
            or sim_ena_g = 1 then
            t_rp_r                <= 0;
            sdram_address_out(10) <= '1';
            sdram_command_r       <= sdram_precharge_c;
            current_state_r       <= init_precharge;
          else
            sdram_command_r <= sdram_nop_c;
            current_state_r <= init_wait;
          end if;
 
          -- after 100us of NOPs precharge all banks
        when init_precharge =>
          if t_rp_r = t_rp_c - 1 or t_rp_c <= 1 then
            sdram_command_r <= sdram_auto_refresh_c;
            current_state_r <= init_auto_refresh1;
          else
            t_rp_r          <= t_rp_r + 1;
            sdram_command_r <= sdram_nop_c;
            current_state_r <= init_precharge;
          end if;
 
          -- after precharge two auto refreshes
        when init_auto_refresh1 =>
          if t_rfc_r = t_rfc_c - 1 or t_rfc_c <= 1 then
            refresh_period_clr_r <= '1';
            t_rfc_r              <= 0;
            sdram_command_r      <= sdram_auto_refresh_c;
            current_state_r      <= init_auto_refresh2;
          else
            refresh_period_clr_r <= '0';
            t_rfc_r              <= t_rfc_r + 1;
            sdram_command_r      <= sdram_nop_c;
            current_state_r      <= init_auto_refresh1;
          end if;
 
        when init_auto_refresh2 =>
          refresh_period_clr_r                <= '0';
          if t_rfc_r = t_rfc_c - 1 or t_rfc_c <= 1 then
            t_rfc_r           <= 0;
            sdram_address_out <= default_mode_c;
            sdram_command_r   <= sdram_load_mode_c;
            current_state_r   <= init_load_mode_register;
          else
            t_rfc_r         <= t_rfc_r + 1;
            sdram_command_r <= sdram_nop_c;
            current_state_r <= init_auto_refresh2;
          end if;
 
          -- load default mode
        when init_load_mode_register =>
          sdram_command_r                     <= sdram_nop_c;
          if t_mrd_r = t_mrd_c - 1 or t_mrd_c <= 1 then
            t_mrd_r         <= 0;
            busy_out        <= '0';
            current_state_r <= idle;
          else
            t_mrd_r         <= t_mrd_r + 1;
            current_state_r <= init_load_mode_register;
          end if;
-------------------------------------------------------------------------------
-- end initialization and start normal operation
-------------------------------------------------------------------------------
 
        when idle =>
 
          data_counter_r <= (others => '0');
          -- precharge is always done before returning to idle state
          -- so we don't have to worry about t_ras_min_c or t_rp_c
          -- (2*t_ras_min),t_rc_c and t_rp_c subracted from refresh_period to prevent
          -- situations where refresh is done right next to active command.
          -- (which is waste of time)
          -- This way when we start executing reads or writes we can do
          -- at least one operation before refreshing.
          if t_from_refresh_r
            >= refresh_period_c - refresh_latency_idle_c then
--             >= refresh_period_c - t_ras_min_c - t_rp_c - 20  -- - 2
--            - (2 * t_ras_min_c) - t_rc_c - t_rp_c - 1 then
            sdram_command_r      <= sdram_auto_refresh_c;
            refresh_period_clr_r <= '1';
            current_state_r      <= idle_auto_refresh;
            busy_out             <= '0';
          else
 
            -- If there is a valid command in the command_in port
            -- we start to process it immediately and tell it to
            -- others by setting busy_out to '1'
            case command_in is
 
              -- if time from active to active is met, activate
              -- row and proceed to reading. Otherwise go to
              -- wait_read_active_to_active state. And wait
              -- for t_rc_c or t_rrd
              when command_read_c =>
 
                sdram_dqm_out <= (others => '0');
                if data_amount_in >
                  conv_std_logic_vector(block_read_length_g,
                                        data_amount_r'length) then
                  data_amount_r <=
                    conv_std_logic_vector(block_read_length_g,
                                          data_amount_r'length);
                else
                  data_amount_r <= data_amount_in;
                end if;
                sdram_address_r   <= address_in;
                sdram_ba_out      <= address_in(21 downto 20);
                sdram_address_out <= address_in(19 downto 8);
 
                -- read operation to bank that was activated in previous operation
                if address_in(21 downto 20)
                   = sdram_address_r(21 downto 20) then
 
                  if (t_from_active_r >= t_rc_c - 1 or t_rc_c <= 1)
                    and sdram_command_r /= sdram_active_c
                    and output_full_in = '0' then
 
                    busy_out        <= '1';
                    read_on_r <= '1';
                    sdram_command_r <= sdram_active_c;
                    current_state_r <= start_read;
                  else
                    busy_out        <= '0';
                    sdram_command_r <= sdram_nop_c;
                    current_state_r <= idle;
                  end if;
 
                  -- read operation to different bank than previously
                else
 
                  if (t_from_active_r >= t_rrd_c - 1 or t_rrd_c <= 1)
                    and sdram_command_r /= sdram_active_c
                    and output_full_in = '0' then
                    busy_out        <= '1';
                    read_on_r <= '1';
                    sdram_command_r <= sdram_active_c;
                    current_state_r <= start_read;
                  else
                    busy_out        <= '0';
                    sdram_command_r <= sdram_nop_c;
                    current_state_r <= idle;
                  end if;
 
                end if;
 
              when command_write_c =>
 
                sdram_dqm_out     <= byte_select_in;
                data_amount_r     <= data_amount_in;
                sdram_address_r   <= address_in;
                sdram_ba_out      <= address_in(21 downto 20);
                sdram_address_out <= address_in(19 downto 8);
 
                -- write operation to bank that was activated in
                -- the previous operation
                if address_in(21 downto 20)
                   = sdram_address_r(21 downto 20) then
 
                  if (t_from_active_r >= t_rc_c - 1 or t_rc_c <= 1)
                    and sdram_command_r /= sdram_active_c
                    and input_empty_in = '0' then
 
                    busy_out        <= '1';
                    sdram_command_r <= sdram_active_c;
                    current_state_r <= start_write;
                  else
                    busy_out        <= '0';
                    sdram_command_r <= sdram_nop_c;
                    current_state_r <= idle;
                  end if;
 
                  -- write operation to different bank than previously
                else
 
                  if (t_from_active_r >= t_rrd_c - 1 or t_rrd_c <= 1)
                    and sdram_command_r /= sdram_active_c
                    and input_empty_in = '0' then
                    busy_out        <= '1';
                    sdram_command_r <= sdram_active_c;
                    current_state_r <= start_write;
                  else
                    busy_out        <= '0';
                    sdram_command_r <= sdram_nop_c;
                    current_state_r <= idle;
                  end if;
 
                end if;
 
                -- NOP or unknown command, don't do anything
              when others =>
                busy_out        <= '0';
                sdram_command_r <= sdram_nop_c;
                current_state_r <= idle;
            end case;
          end if;
 
 
        -- wait until time minimum time from active to active is met
        -- and precharge command period is met
        -- go to start write
        when wait_write_active_to_active =>
          sdram_ba_out      <= sdram_address_r(21 downto 20);
          sdram_address_out <= sdram_address_r(19 downto 8);
 
          if t_rp_r >= t_rp_c - 1 or t_rp_c <= 1 then
            t_rp_r <= t_rp_r;
          else
            t_rp_r <= t_rp_r + 1;
          end if;
 
          -- operation to bank that was activated in previous operation
          if address_in(21 downto 20)
             = sdram_address_r(21 downto 20) then
 
            if (t_from_active_r >= t_rc_c - 2 or t_rc_c <= 2)
              and sdram_command_r /= sdram_active_c
              and t_rp_r >= t_rp_c - 1 then
 
              sdram_command_r <= sdram_active_c;
              current_state_r <= start_write;
            else
              sdram_command_r <= sdram_nop_c;
              current_state_r <= wait_write_active_to_active;
            end if;
 
            -- operation to different bank than previously
          else
 
            if (t_from_active_r >= t_rrd_c - 2 or t_rrd_c <= 2)
              and sdram_command_r /= sdram_active_c
              and t_rp_r >= t_rp_c - 1 then
 
              sdram_command_r <= sdram_active_c;
              current_state_r <= start_write;
            else
              sdram_command_r <= sdram_nop_c;
              current_state_r <= wait_write_active_to_active;
            end if;
 
          end if;
 
          -- Wait for data to be written.
          -- Since we don't know when input data is available
          -- we must check refreshing and t_ras_max(maximum
          -- time that row can be open).
          -- When input data arrives, read data(input_re_out = '1')
          -- and go to continue write state.
        when start_write =>
 
          sdram_command_r <= sdram_nop_c;
 
          -- Worst case in refreshing is that we go to
          -- write_change_row_precharge state then
          -- the next possibility to make refresh is after:
          --   t_ras_min_c in write_change_row_precharge +
          --   t_rc_c in wait_write_active_to_active +
          --   t_ras_min_c in write_refresh_precharge +
          --   t_rp_c in write_refresh
          --     = 2*t_ras_min_c + t_rc_c + t_rp
          if t_from_refresh_r
             >= refresh_period_c - refresh_latency_write_c - 1
            or (t_from_active_r >= t_ras_max_c - t_wr_c - 3
                and sdram_command_r /= sdram_active_c) then
 
            input_re_out_r  <= '0';
            current_state_r <= write_refresh_precharge;
 
          else
            -- is t_rcd_c (minimum time from active to read/write) met?
            if t_rcd_c <= 2
                          or (t_from_active_r >= t_rcd_c - 3
                              and sdram_command_r /= sdram_active_c) then
 
              sdram_ba_out      <= sdram_address_r(21 downto 20);
              sdram_address_out <= "0000" & sdram_address_r(7 downto 0);
 
              -- We must change row if address is first column in row and
              -- this is not the first write of this operation.
              if input_empty_in = '0' and not(input_one_d_in = '1' and
                                              input_re_out_r = '1') then
 
                if sdram_address_r(7 downto 0) = 0 and data_counter_r /= 0
                  and row_active_r = '0' then
                  input_re_out_r  <= '0';
                  row_active_r    <= '1';
                  current_state_r <= write_change_row_precharge;
                else
                  input_re_out_r  <= '1';
                  current_state_r <= continue_write;
                end if;
              else
                input_re_out_r  <= '0';
                current_state_r <= write_finished;
              end if;
            else
              input_re_out_r  <= '0';
              current_state_r <= start_write;
            end if;
          end if;
 
          -- Write until operation is completed. Change row,
          -- precharge and refresh if necessary
        when continue_write =>
 
          sdram_ba_out      <= sdram_address_r(21 downto 20);
          sdram_address_out <= "0000" & sdram_address_r(7 downto 0);
 
          -- write finished or terminated
          if data_counter_r = data_amount_r
            or (sdram_address_r
                 = conv_std_logic_vector(0, sdram_address_r'length)
                and data_counter_r /= 0) then
            row_active_r <= '0';
 
            input_re_out_r  <= '0';
            data_counter_r  <= (others => '0');
            sdram_command_r <= sdram_nop_c;
            current_state_r <= write_finished;
            write_on_r      <= '0';
 
            -- refresh. refresh_latency_write takes care of possible
            -- delays before refresh can be done (precharge command period
            -- and changing row.
          elsif t_from_refresh_r >= refresh_period_c - refresh_latency_write_c
            or (t_from_active_r >= t_ras_max_c - t_wr_c - 2
                and sdram_command_r /= sdram_active_c) then
            input_re_out_r  <= '0';
            sdram_command_r <= sdram_nop_c;
            current_state_r <= write_refresh_precharge;
            write_on_r      <= '0';
 
 
            -- change row
          elsif sdram_address_r(7 downto 0) = 0 and data_counter_r /= 0
            and row_active_r = '0' then
            input_re_out_r  <= '0';
            row_active_r    <= '1';
            sdram_command_r <= sdram_nop_c;
            current_state_r <= write_change_row_precharge;
            write_on_r      <= '0';
 
            -- normal write
          else
            row_active_r <= '0';
 
            if (input_empty_in_r = '0' and not(input_one_d_in_r = '1'
                                               and input_re_out_r = '1'))
              or data_counter_r = 0 then
 
              data_counter_r  <= data_counter_r + 1;
              sdram_address_r <= sdram_address_r + 1;
 
              -- don't load new data if write finishes or row changes
              -- or refresh or t_ras_max_c is met.
              -- In t_from_refresh_r >= refresh_period - x the x
              -- must be one larger than in the previous if(refresh)
              -- otherwise the data_counter_r goes wrong
 
              if sdram_address_r(7 downto 0) = "11111111"
                or t_from_refresh_r
                 >= refresh_period_c - refresh_latency_write_c - 1
                or (t_from_active_r >= t_ras_max_c - t_wr_c - 2 - 1
                    and sdram_command_r /= sdram_active_c)
                or data_counter_r = data_amount_r - 1
                or (input_empty_in = '1' or (input_one_d_in = '1'
                                             and input_re_out_r = '1')) then
                input_re_out_r  <= '0';
                current_state_r <= write_finished;
 
              else
                input_re_out_r  <= '1';
                current_state_r <= continue_write;
              end if;
              write_on_r      <= '1';
              sdram_command_r <= sdram_write_c;
              t_wr_r          <= 0;
 
              -- no input data available, terminate write
            else
              write_on_r      <= '0';
              data_counter_r  <= data_counter_r;
              sdram_address_r <= sdram_address_r;
              input_re_out_r  <= '0';
              sdram_command_r <= sdram_nop_c;
              current_state_r <= write_finished;
            end if;
          end if;
 
          -- Refresh during write operation.
          -- First we must wait for t_wr(write recovery time) and
          -- t_ras_min(minimum time from active to precharge)
          -- Then we must precharge all banks before refresh.
        when write_refresh_precharge =>
          if t_wr_c <= 2 or (t_wr_r >= t_wr_c - 2) then
            if (t_from_active_r >= t_ras_min_c - 2
                and sdram_command_r /= sdram_active_c) then
              t_rp_r                <= 0;
              sdram_address_out(10) <= '1';
              sdram_command_r       <= sdram_precharge_c;
              current_state_r       <= write_refresh;
            else
              t_wr_r          <= t_wr_r;
              sdram_command_r <= sdram_nop_c;
              current_state_r <= write_refresh_precharge;
            end if;
 
          else
            t_wr_r          <= t_wr_r + 1;
            sdram_command_r <= sdram_nop_c;
            current_state_r <= write_refresh_precharge;
          end if;
 
          -- Wait for t_rp(precharge command period)
          -- after that refresh
        when write_refresh =>
          if t_rp_r = t_rp_c - 1 or t_rp_c <= 1 then
            refresh_period_clr_r <= '1';
            t_rp_r               <= 0;
            sdram_command_r      <= sdram_auto_refresh_c;
            current_state_r      <= write_refresh_wait;
          else
            sdram_command_r <= sdram_nop_c;
            t_rp_r          <= t_rp_r + 1;
            current_state_r <= write_refresh;
          end if;
 
          -- wait for refresh command period and
          -- proceed write operation
        when write_refresh_wait =>
          refresh_period_clr_r                <= '0';
          if t_rfc_r = t_rfc_c - 1 or t_rfc_c <= 1 then
            t_rfc_r         <= 0;
            sdram_command_r <= sdram_nop_c;
            if data_counter_r = data_amount_r then
              row_active_r    <= '0';
              busy_out        <= '0';
              current_state_r <= idle;
            else
              busy_out        <= '1';
              current_state_r <= wait_write_active_to_active;
            end if;
          else
            t_rfc_r         <= t_rfc_r + 1;
            sdram_command_r <= sdram_nop_c;
            current_state_r <= write_refresh_wait;
          end if;
 
          -- Change row in write operation.
          -- Wait for write recovery time and precharge
          -- all banks. Then go to wait_write_active_to_active
          -- state that handles activating the new row/bank
          -- and proceeds writing.
        when write_change_row_precharge =>
          if t_wr_c <= 2 or (t_wr_r = t_wr_c - 2) then
            if (t_from_active_r >= t_ras_min_c - 2 or t_ras_min_c <= 2)
              and sdram_command_r /= sdram_active_c then
              t_rp_r                <= 0;
              sdram_address_out(10) <= '1';
              sdram_command_r       <= sdram_precharge_c;
 
              if data_counter_r = data_amount_r then
                busy_out        <= '1';
                row_active_r    <= '0';
                current_state_r <= write_finished_precharge;
              else
                current_state_r <= wait_write_active_to_active;
              end if;
            else
              t_wr_r          <= t_wr_r;
              sdram_command_r <= sdram_nop_c;
              current_state_r <= write_change_row_precharge;
            end if;
          else
            t_wr_r          <= t_wr_r + 1;
            sdram_command_r <= sdram_nop_c;
            current_state_r <= write_change_row_precharge;
          end if;
 
          -- write finished, so wait for wait_recovery time
          -- and minimum time from active to precharge and
          -- precharge all banks
        when write_finished =>
          write_on_r   <= '0';
          row_active_r <= '0';
 
          if write_on_r = '0' then
 
            if (t_wr_c <= 2 or (t_wr_r >= t_wr_c - 2)) then
              if (t_from_active_r >= t_ras_min_c - 2 or t_ras_min_c <= 2)
                and sdram_command_r /= sdram_active_c then
                sdram_address_out(10) <= '1';
                t_rp_r                <= 0;
                busy_out              <= '1';
                sdram_command_r       <= sdram_precharge_c;
                current_state_r       <= write_finished_precharge;
              else
                t_wr_r          <= t_wr_r;
                sdram_command_r <= sdram_nop_c;
                current_state_r <= write_finished;
              end if;
            else
              t_wr_r          <= t_wr_r + 1;
              sdram_command_r <= sdram_nop_c;
              current_state_r <= write_finished;
            end if;
          else
            sdram_command_r <= sdram_nop_c;
            current_state_r <= write_finished;
          end if;
 
          -- wait for precharge command period and
          -- go to idle state.
        when write_finished_precharge =>
          sdram_command_r <= sdram_nop_c;
          if t_rp_c       <= 2 or (t_rp_r = t_rp_c - 1 - 1) then
            t_rp_r          <= t_rp_c - 1;
            busy_out        <= '0';
            current_state_r <= idle;
          else
            t_rp_r          <= t_rp_r + 1;
            current_state_r <= write_finished_precharge;
          end if;
 
          -- refresh during idle state
        when idle_auto_refresh =>
          refresh_period_clr_r                <= '0';
          sdram_command_r                     <= sdram_nop_c;
          if t_rfc_r = t_rfc_c - 1 or t_rfc_c <= 1 then
            t_rfc_r         <= 0;
            busy_out        <= '0';
            current_state_r <= idle;
          else
            t_rfc_r         <= t_rfc_r + 1;
            current_state_r <= idle_auto_refresh;
          end if;
 
          -- Start read state
          -- Check t_rcd(minimum time between active and read/write command).
          -- If timings are met the go to continue read state where
          -- the actual read happens.
        when start_read =>
          sdram_command_r <= sdram_nop_c;
 
          if t_rcd_c <= 2
                        or (t_from_active_r >= t_rcd_c - 3
                            and sdram_command_r /= sdram_active_c) then
            current_state_r <= continue_read;
          else
            current_state_r <= start_read;
          end if;
 
          -- continue read state:
          -- Check refreshing and t_ras_max.
          -- If output fifo is full or
          -- row changes, terminate read
        when continue_read =>
 
          -- refresh
          if t_from_refresh_r >= refresh_period_c - refresh_latency_read_c
            or (t_from_active_r >= t_ras_max_c - 2
                and sdram_command_r /= sdram_active_c) then
 
            if output_full_in = '1' then
              data_counter_r <= succesful_reads_r;
            else
              data_counter_r  <= data_counter_r;
              sdram_address_r <= sdram_address_r;
            end if;
 
            sdram_command_r <= sdram_nop_c;
            current_state_r <= read_refresh_precharge;
 
          else
 
            if output_full_in = '0' then
 
              -- read finished
              if data_counter_r = data_amount_r then
                sdram_address_r <= sdram_address_r;
                data_counter_r  <= data_counter_r;
                sdram_command_r <= sdram_nop_c;
                current_state_r <= read_finished;
 
                -- address overflow, read the last address until operation
                -- is finished
              elsif sdram_address_r
                 = conv_std_logic_vector(0, sdram_address_r'length)
                and data_counter_r /= 0 then
 
                sdram_address_r   <= (others => '1');
                sdram_ba_out      <= sdram_address_r(21 downto 20);
                sdram_address_out <= "0000" & sdram_address_r(7 downto 0);
 
                data_counter_r    <= data_counter_r + 1;
                sdram_command_r   <= sdram_read_c;
                current_state_r   <= continue_read;
 
                -- row changes so terminate read
              elsif sdram_address_r(7 downto 0) = "00000000"
                and data_counter_r /= 0 then
 
                sdram_command_r <= sdram_nop_c;
                current_state_r <= read_finished;
 
                -- normal read
              else
                data_counter_r    <= data_counter_r + 1;
                sdram_address_r   <= sdram_address_r + 1;
                sdram_ba_out      <= sdram_address_r(21 downto 20);
                sdram_address_out <= "0000" & sdram_address_r(7 downto 0);
                sdram_command_r   <= sdram_read_c;
                current_state_r   <= continue_read;
              end if;
 
              -- output full, terminate read
            else
              sdram_command_r <= sdram_nop_c;
              data_counter_r  <= succesful_reads_r;
 
              current_state_r <= read_finished;
 
            end if;
          end if;
 
          -- wait for timings to be met and precharge
        when read_refresh_precharge =>
 
          if t_end_cas_r = t_cas_c - 1 or output_full_in = '1' then
 
            data_counter_r <= succesful_reads_r;
 
            if (t_from_active_r >= t_ras_min_c - 2 or t_ras_min_c <= 2)
              and sdram_command_r /= sdram_active_c then
              t_end_cas_r           <= 0;
              t_rp_r                <= 0;
              data_counter_r        <= (others => '0');
              read_on_r             <= '0';
              sdram_address_out(10) <= '1';
              sdram_command_r       <= sdram_precharge_c;
              current_state_r       <= read_refresh;
 
            else
              sdram_command_r <= sdram_nop_c;
              t_end_cas_r     <= t_end_cas_r;
              current_state_r <= read_refresh_precharge;
            end if;
 
          else
            t_end_cas_r     <= t_end_cas_r + 1;
            sdram_command_r <= sdram_nop_c;
            current_state_r <= read_refresh_precharge;
          end if;
 
          -- wait for precharge command period and refresh
        when read_refresh =>
          if t_rp_r = t_rp_c - 1 or t_rp_c <= 1 then
            refresh_period_clr_r <= '1';
            sdram_command_r      <= sdram_auto_refresh_c;
            current_state_r      <= read_refresh_wait;
          else
            sdram_command_r <= sdram_nop_c;
            t_rp_r          <= t_rp_r + 1;
            current_state_r <= read_refresh;
          end if;
 
          -- wait for refresh command period and proceed reading.
        when read_refresh_wait =>
          refresh_period_clr_r                <= '0';
          if t_rfc_r = t_rfc_c - 1 or t_rfc_c <= 1 then
            t_rfc_r         <= 0;
            sdram_command_r <= sdram_nop_c;
            busy_out        <= '0';
            read_on_r       <= '0';
            current_state_r <= idle;
          else
            t_rfc_r         <= t_rfc_r + 1;
            sdram_command_r <= sdram_nop_c;
            current_state_r <= read_refresh_wait;
          end if;
 
          -- All read commands are send to sdram.
          -- Wait for cas latency and check that all read data fits
          -- to output fifo. If fifo gets full then terminate read.
          -- Precharge all banks.
        when read_finished =>
 
          if t_end_cas_r = t_cas_c - 1 or output_full_in = '1' then
 
            data_counter_r <= succesful_reads_r;
 
            if (t_from_active_r >= t_ras_min_c - 2 or t_ras_min_c <= 2)
              and sdram_command_r /= sdram_active_c then
              data_counter_r        <= (others => '0');
              t_end_cas_r           <= 0;
              t_rp_r                <= 0;
              sdram_address_out(10) <= '1';
              busy_out              <= '1';
              read_on_r             <= '0';
              sdram_command_r       <= sdram_precharge_c;
              current_state_r       <= read_finished_precharge;
            else
              sdram_command_r <= sdram_nop_c;
              t_end_cas_r     <= t_end_cas_r;
              current_state_r <= read_finished;
            end if;
          else
            sdram_command_r <= sdram_nop_c;
            t_end_cas_r     <= t_end_cas_r + 1;
            current_state_r <= read_finished;
          end if;
 
          -- Read operation is finished, all read data is
          -- put to output fifo and precharge is done.
          -- Now wait for precharge command period and
          -- go to idle state.
        when read_finished_precharge =>
          sdram_command_r <= sdram_nop_c;
          if t_rp_c       <= 2 or (t_rp_r = t_rp_c - 1 - 1) then
            t_rp_r          <= t_rp_c - 1;
            busy_out        <= '0';
            read_on_r       <= '0';
            current_state_r <= idle;
          else
            t_rp_r          <= t_rp_r + 1;
            current_state_r <= read_finished_precharge;
          end if;
 
        when others =>
          sdram_command_r <= sdram_nop_c;
          current_state_r <= idle;
      end case;
    end if;
 
  end process state_machine_proc;
 
  -- This process writes read data from sdram data bus to
  -- output fifo.
  write_read_data_to_output : process (clk, rst_n)
 
  begin  -- process write_read_data_to_output
    if rst_n = '0' then                 -- asynchronous reset (active low)
      t_cas_r           <= 0;
      succesful_reads_r <= (others => '0');
      output_we_r       <= '0';
 
    elsif clk'event and clk = '1' then  -- rising clock edge
 
      -- first check that current operation is read
      if read_on_r = '1' then
 
        -- if output fifo gets full. Clear cas latency counter
        -- and don't write read data to output
        if output_full_in = '1' then
 
          t_cas_r <= 0;
          output_we_r <= '0';
 
        else
 
          -- if read succeeds(output is not full) then proceed
          -- writing data to output buffer until the operation
          -- is complete
          if data_counter_r > succesful_reads_r then
 
            -- if cas latency is met(data from sdram is on the data bus),
            -- then write data to output fifo and update succesful read
            -- count and succesful read and store their previous values.
            -- address.
            if t_cas_r = t_cas_c - 1 then
              t_cas_r           <= t_cas_r;
              succesful_reads_r <= succesful_reads_r + 1;
              output_we_r       <= '1';
 
              -- wait for data from sdram(cas latency)
            else
              t_cas_r           <= t_cas_r + 1;
              succesful_reads_r <= succesful_reads_r;
              output_we_r       <= '0';
            end if;
 
            -- read is finished, clear cas and don't write to output fifo
          else
            t_cas_r     <= 0;
            output_we_r <= '0';
          end if;
        end if;
 
        -- if current operation is not read then reset this process
      else
        t_cas_r           <= 0;
        succesful_reads_r <= (others => '0');
        output_we_r       <= '0';
      end if;
    end if;
 
  end process write_read_data_to_output;
 
  -- purpose: keeps count of time from active command
  --          handles t_rc, t_rcd, t_rrd, t_ras_min, t_ras_max
  t_from_active_counter : process (clk, rst_n)
 
  begin  -- process t_from_active_counter
 
    if rst_n = '0' then                 -- asynchronous reset (active low)
      t_from_active_r <= 0;
    elsif clk'event and clk = '1' then  -- rising clock edge
 
      if sdram_command_r = sdram_active_c then
        t_from_active_r <= 0;
      else
        if t_from_active_r = t_ras_max_c - 2 then
          t_from_active_r <= t_from_active_r;
        else
          t_from_active_r <= t_from_active_r + 1;
        end if;
      end if;
 
    end if;
 
  end process t_from_active_counter;
 
  -- purpose: keeps count of refresh periods
  refresh_period_counter : process (clk, rst_n)
 
  begin  -- process refresh_period_counter
 
    if rst_n = '0' then                 -- asynchronous reset (active low)
      t_from_refresh_r <= 0;
 
    elsif clk'event and clk = '1' then  -- rising clock edge
 
      if refresh_period_clr_r = '1' then
        t_from_refresh_r <= 0;
      else
 
        if t_from_refresh_r = init_wait_cycles_c - 1 then
          t_from_refresh_r <= t_from_refresh_r;
        else
          t_from_refresh_r <= t_from_refresh_r + 1;
        end if;
      end if;
 
    end if;
 
  end process refresh_period_counter;
 
end rtl;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.