2
\$\begingroup\$

I am working with a DE0-nano board, on which is a Cyclone IV EP4CE22F17C6 FPGA, connected to an ISSI IS42S16160G-7TLI 16Mx16 SDRAM chip. In order to setup the communication between the FPGA, I've started with a simple program which allows me to initialize the SDRAM mode, then fill the whole memory with the first 16M integers.

The SDRAM core controller I've chosen to use is this one : https://github.com/nullobject/sdram-fpga. It's a simple VHDL SDRAM core, that writes/reads with 2 words bursts. I did a few changes to the code : removed the line 229 next_state <= state;, and the line 232 next_cmd <= CMD_NOP; (while putting back this line in the fsm wherever it was required). Here is the modified code :

--   __   __     __  __     __         __
--  /\ "-.\ \   /\ \/\ \   /\ \       /\ \
--  \ \ \-.  \  \ \ \_\ \  \ \ \____  \ \ \____
--   \ \_\\"\_\  \ \_____\  \ \_____\  \ \_____\
--    \/_/ \/_/   \/_____/   \/_____/   \/_____/
--   ______     ______       __     ______     ______     ______
--  /\  __ \   /\  == \     /\ \   /\  ___\   /\  ___\   /\__  _\
--  \ \ \/\ \  \ \  __<    _\_\ \  \ \  __\   \ \ \____  \/_/\ \/
--   \ \_____\  \ \_____\ /\_____\  \ \_____\  \ \_____\    \ \_\
--    \/_____/   \/_____/ \/_____/   \/_____/   \/_____/     \/_/
--
-- https://joshbassett.info
-- https://twitter.com/nullobject
-- https://github.com/nullobject
--
-- Copyright (c) 2020 Josh Bassett
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;

-- This SDRAM controller provides a symmetric 32-bit synchronous read/write
-- interface for a 16Mx16-bit SDRAM chip (e.g. AS4C16M16SA-6TCN, IS42S16400F,
-- etc.).
entity sdram is
  generic (
    -- clock frequency (in MHz)
    --
    -- This value must be provided, as it is used to calculate the number of
    -- clock cycles required for the other timing values.
    CLK_FREQ : real;

    -- 32-bit controller interface
    ADDR_WIDTH : natural := 23;
    DATA_WIDTH : natural := 32;

    -- SDRAM interface
    SDRAM_ADDR_WIDTH : natural := 13;
    SDRAM_DATA_WIDTH : natural := 16;
    SDRAM_COL_WIDTH  : natural := 9;
    SDRAM_ROW_WIDTH  : natural := 13;
    SDRAM_BANK_WIDTH : natural := 2;

    -- The delay in clock cycles, between the start of a read command and the
    -- availability of the output data.
    CAS_LATENCY : natural := 2; -- 2=below 133MHz, 3=above 133MHz

    -- The number of 16-bit words to be bursted during a read/write.
    BURST_LENGTH : natural := 2;

    -- timing values (in nanoseconds)
    --
    -- These values can be adjusted to match the exact timing of your SDRAM
    -- chip (refer to the datasheet).
    T_DESL : real := 100000.0; -- startup delay
    T_MRD  : real :=     14.0; -- mode register cycle time
    T_RC   : real :=     60.0; -- row cycle time
    T_RCD  : real :=     15.0; -- RAS to CAS delay
    T_RP   : real :=     15.0; -- precharge to activate delay
    T_WR   : real :=     15.0; -- write recovery time
    T_REFI : real :=   7800.0  -- average refresh interval
  );
  port (
    -- reset
    reset : in std_logic := '0';

    -- clock
    clk : in std_logic;

    -- address bus
    addr : in unsigned(ADDR_WIDTH-1 downto 0);

    -- input data bus
    data : in std_logic_vector(DATA_WIDTH-1 downto 0);

    -- When the write enable signal is asserted, a write operation will be performed.
    we : in std_logic;

    -- When the request signal is asserted, an operation will be performed.
    req : in std_logic;

    -- The acknowledge signal is asserted by the SDRAM controller when
    -- a request has been accepted.
    ack : out std_logic;

    -- The valid signal is asserted when there is a valid word on the output
    -- data bus.
    valid : out std_logic;

    -- output data bus
    q : out std_logic_vector(DATA_WIDTH-1 downto 0);

    -- SDRAM interface (e.g. AS4C16M16SA-6TCN, IS42S16400F, etc.)
    sdram_a     : out unsigned(SDRAM_ADDR_WIDTH-1 downto 0);
    sdram_ba    : out unsigned(SDRAM_BANK_WIDTH-1 downto 0);
    sdram_dq    : inout std_logic_vector(SDRAM_DATA_WIDTH-1 downto 0);
    sdram_cke   : out std_logic;
    sdram_cs_n  : out std_logic;
    sdram_ras_n : out std_logic;
    sdram_cas_n : out std_logic;
    sdram_we_n  : out std_logic;
    sdram_dqml  : out std_logic;
    sdram_dqmh  : out std_logic
  );
end sdram;

architecture arch of sdram is
  function ilog2(n : natural) return natural is
  begin
    return natural(ceil(log2(real(n))));
  end ilog2;

  subtype command_t is std_logic_vector(3 downto 0);

  -- commands
  constant CMD_DESELECT     : command_t := "1---";
  constant CMD_LOAD_MODE    : command_t := "0000";
  constant CMD_AUTO_REFRESH : command_t := "0001";
  constant CMD_PRECHARGE    : command_t := "0010";
  constant CMD_ACTIVE       : command_t := "0011";
  constant CMD_WRITE        : command_t := "0100";
  constant CMD_READ         : command_t := "0101";
  constant CMD_STOP         : command_t := "0110";
  constant CMD_NOP          : command_t := "0111";

  -- the ordering of the accesses within a burst
  constant BURST_TYPE : std_logic := '0'; -- 0=sequential, 1=interleaved

  -- the write burst mode enables bursting for write operations
  constant WRITE_BURST_MODE : std_logic := '0'; -- 0=burst, 1=single

  -- the value written to the mode register to configure the memory
  constant MODE_REG : unsigned(SDRAM_ADDR_WIDTH-1 downto 0) := (
    "000" &
    WRITE_BURST_MODE &
    "00" &
    to_unsigned(CAS_LATENCY, 3) &
    BURST_TYPE &
    to_unsigned(ilog2(BURST_LENGTH), 3)
  );

  -- calculate the clock period (in nanoseconds)
  constant CLK_PERIOD : real := 1.0/CLK_FREQ*1000.0;

  -- the number of clock cycles to wait before initialising the device
  constant INIT_WAIT : natural := natural(ceil(T_DESL/CLK_PERIOD));

  -- the number of clock cycles to wait while a LOAD MODE command is being
  -- executed
  constant LOAD_MODE_WAIT : natural := natural(ceil(T_MRD/CLK_PERIOD));

  -- the number of clock cycles to wait while an ACTIVE command is being
  -- executed
  constant ACTIVE_WAIT : natural := natural(ceil(T_RCD/CLK_PERIOD));

  -- the number of clock cycles to wait while a REFRESH command is being
  -- executed
  constant REFRESH_WAIT : natural := natural(ceil(T_RC/CLK_PERIOD));

  -- the number of clock cycles to wait while a PRECHARGE command is being
  -- executed
  constant PRECHARGE_WAIT : natural := natural(ceil(T_RP/CLK_PERIOD));

  -- the number of clock cycles to wait while a READ command is being executed
  constant READ_WAIT : natural := CAS_LATENCY+BURST_LENGTH;

  -- the number of clock cycles to wait while a WRITE command is being executed
  constant WRITE_WAIT : natural := BURST_LENGTH+natural(ceil((T_WR+T_RP)/CLK_PERIOD));

  -- the number of clock cycles before the memory controller needs to refresh
  -- the SDRAM
  constant REFRESH_INTERVAL : natural := natural(floor(T_REFI/CLK_PERIOD))-10;

  type state_t is (INIT, MODE, IDLE, ACTIVE, READ, WRITE, REFRESH);

  -- state signals
  signal state, next_state : state_t;

  -- command signals
  signal cmd, next_cmd : command_t := CMD_NOP;

  -- control signals
  signal start          : std_logic;
  signal load_mode_done : std_logic;
  signal active_done    : std_logic;
  signal refresh_done   : std_logic;
  signal first_word     : std_logic;
  signal read_done      : std_logic;
  signal write_done     : std_logic;
  signal should_refresh : std_logic;

  -- counters
  signal wait_counter    : natural range 0 to 1048575;
  signal refresh_counter : natural range 0 to 1023;

  -- registers
  signal addr_reg : unsigned(SDRAM_COL_WIDTH+SDRAM_ROW_WIDTH+SDRAM_BANK_WIDTH-1 downto 0);
  signal data_reg : std_logic_vector(DATA_WIDTH-1 downto 0);
  signal we_reg   : std_logic;
  signal q_reg    : std_logic_vector(DATA_WIDTH-1 downto 0);

  -- aliases to decode the address register
  alias col  : unsigned(SDRAM_COL_WIDTH-1 downto 0) is addr_reg(SDRAM_COL_WIDTH-1 downto 0);
  alias row  : unsigned(SDRAM_ROW_WIDTH-1 downto 0) is addr_reg(SDRAM_COL_WIDTH+SDRAM_ROW_WIDTH-1 downto SDRAM_COL_WIDTH);
  alias bank : unsigned(SDRAM_BANK_WIDTH-1 downto 0) is addr_reg(SDRAM_COL_WIDTH+SDRAM_ROW_WIDTH+SDRAM_BANK_WIDTH-1 downto SDRAM_COL_WIDTH+SDRAM_ROW_WIDTH);
begin
  -- state machine
  fsm : process (state, wait_counter, req, we_reg, load_mode_done, active_done, refresh_done, read_done, write_done, should_refresh)
  begin

    case state is
      -- execute the initialisation sequence
      when INIT =>
        if wait_counter = 0 then
          next_cmd <= CMD_DESELECT;
        elsif wait_counter = INIT_WAIT-1 then
          next_cmd <= CMD_PRECHARGE;
        elsif wait_counter = INIT_WAIT+PRECHARGE_WAIT-1 then
          next_cmd <= CMD_AUTO_REFRESH;
        elsif wait_counter = INIT_WAIT+PRECHARGE_WAIT+REFRESH_WAIT-1 then
          next_cmd <= CMD_AUTO_REFRESH;
        elsif wait_counter = INIT_WAIT+PRECHARGE_WAIT+REFRESH_WAIT+REFRESH_WAIT-1 then
          next_state <= MODE;
          next_cmd   <= CMD_LOAD_MODE;
        end if;

      -- load the mode register
      when MODE =>
        if load_mode_done = '1' then
          next_state <= IDLE;
          next_cmd <= CMD_NOP;
        end if;

      -- wait for a read/write request
      when IDLE =>
        if should_refresh = '1' then
          next_state <= REFRESH;
          next_cmd   <= CMD_AUTO_REFRESH;
        elsif req = '1' then
          next_state <= ACTIVE;
          next_cmd   <= CMD_ACTIVE;
        end if;

      -- activate the row
      when ACTIVE =>
        if active_done = '1' then
          if we_reg = '1' then
            next_state <= WRITE;
            next_cmd   <= CMD_WRITE;
          else
            next_state <= READ;
            next_cmd   <= CMD_READ;
          end if;
        end if;

      -- execute a read command
      when READ =>
        if read_done = '1' then
          if should_refresh = '1' then
            next_state <= REFRESH;
            next_cmd   <= CMD_AUTO_REFRESH;
          elsif req = '1' then
            next_state <= ACTIVE;
            next_cmd   <= CMD_ACTIVE;
          else
            next_state <= IDLE;
            next_cmd <= CMD_NOP;
          end if;
        end if;

      -- execute a write command
      when WRITE =>
        if write_done = '1' then
          if should_refresh = '1' then
            next_state <= REFRESH;
            next_cmd   <= CMD_AUTO_REFRESH;
          elsif req = '1' then
            next_state <= ACTIVE;
            next_cmd   <= CMD_ACTIVE;
          else
            next_state <= IDLE;
            next_cmd <= CMD_NOP;
          end if;
        end if;

      -- execute an auto refresh
      when REFRESH =>
        if refresh_done = '1' then
          if req = '1' then
            next_state <= ACTIVE;
            next_cmd   <= CMD_ACTIVE;
          else
            next_state <= IDLE;
            next_cmd <= CMD_NOP;
          end if;
        end if;
    end case;
  end process;

  -- latch the next state
  latch_next_state : process (clk, reset)
  begin
    if reset = '1' then
      state <= INIT;
      cmd   <= CMD_NOP;
    elsif rising_edge(clk) then
      state <= next_state;
      cmd   <= next_cmd;
    end if;
  end process;

  -- the wait counter is used to hold the current state for a number of clock
  -- cycles
  update_wait_counter : process (clk, reset)
  begin
    if reset = '1' then
      wait_counter <= 0;
    elsif rising_edge(clk) then
      if state /= next_state then -- state changing
        wait_counter <= 0;
      else
        wait_counter <= wait_counter + 1;
      end if;
    end if;
  end process;

  -- the refresh counter is used to periodically trigger a refresh operation
  update_refresh_counter : process (clk, reset)
  begin
    if reset = '1' then
      refresh_counter <= 0;
    elsif rising_edge(clk) then
      if state = REFRESH and wait_counter = 0 then
        refresh_counter <= 0;
      else
        refresh_counter <= refresh_counter + 1;
      end if;
    end if;
  end process;

  -- latch the rquest
  latch_request : process (clk)
  begin
    if rising_edge(clk) then
      if start = '1' then
        -- we need to multiply the address by two, because we are converting
        -- from a 32-bit controller address to a 16-bit SDRAM address
        addr_reg <= shift_left(resize(addr, addr_reg'length), 1);
        data_reg <= data;
        we_reg   <= we;
      end if;
    end if;
  end process;

  -- latch the output data as it's bursted from the SDRAM
  latch_sdram_data : process (clk)
  begin
    if rising_edge(clk) then

        if state = READ then
            if first_word = '1' then
                q_reg(31 downto 16) <= sdram_dq;
                valid <= '0';
            elsif read_done = '1' then
                q_reg(15 downto 0) <= sdram_dq;
                valid <= '1';
            end if;
        else
            valid <= '0';
        end if;
    end if;
  end process;

  -- set wait signals
  load_mode_done <= '1' when wait_counter = LOAD_MODE_WAIT-1 else '0';
  active_done    <= '1' when wait_counter = ACTIVE_WAIT-1    else '0';
  refresh_done   <= '1' when wait_counter = REFRESH_WAIT-1   else '0';
  first_word     <= '1' when wait_counter = CAS_LATENCY      else '0';
  read_done      <= '1' when wait_counter = READ_WAIT-1      else '0';
  write_done     <= '1' when wait_counter = WRITE_WAIT-1     else '0';

  -- the SDRAM should be refreshed when the refresh interval has elapsed
  should_refresh <= '1' when refresh_counter >= REFRESH_INTERVAL-1 else '0';

  -- a new request is only allowed at the end of the IDLE, READ, WRITE, and
  -- REFRESH states
  start <= '1' when (state = IDLE) or
                    (state = READ and read_done = '1') or
                    (state = WRITE and write_done = '1') or
                    (state = REFRESH and refresh_done = '1') else '0';

  -- assert the acknowledge signal at the beginning of the ACTIVE state
  ack <= '1' when state = ACTIVE and wait_counter = 0 else '0';

  -- set output data
  q <= q_reg;

  -- deassert the clock enable at the beginning of the INIT state
  sdram_cke <= '0' when state = INIT and wait_counter = 0 else '1';

  -- set SDRAM control signals
  (sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n) <= cmd;

  -- set SDRAM bank
  with state select
    sdram_ba <=
      bank            when ACTIVE,
      bank            when READ,
      bank            when WRITE,
      (others => '0') when others;

  -- set SDRAM address
  with state select
    sdram_a <=
      "0010000000000" when INIT,
      MODE_REG        when MODE,
      row             when ACTIVE,
      "0010" & col    when READ,   -- auto precharge
      "0010" & col    when WRITE,  -- auto precharge
      (others => '0') when others;

  -- decode the next 16-bit word from the write buffer
  --sdram_dq <= data_reg((BURST_LENGTH-wait_counter)*SDRAM_DATA_WIDTH-1 downto (BURST_LENGTH-wait_counter-1)*SDRAM_DATA_WIDTH) when state = WRITE else (others => 'Z');

  B_data_mux: block
    type T_mux_sdram_dq is array (0 to BURST_LENGTH-1) of std_logic_vector(SDRAM_DATA_WIDTH-1 downto 0);
    signal S_mux_sdram_dq: T_mux_sdram_dq;
    signal addr_wait_counter: unsigned(ilog2(BURST_LENGTH)-1 downto 0);
  begin
    G_mux_sdram_dq: for i in 0 to BURST_LENGTH-1 generate
      S_mux_sdram_dq(i) <= data_reg((BURST_LENGTH-i)*SDRAM_DATA_WIDTH-1 downto (BURST_LENGTH-i-1)*SDRAM_DATA_WIDTH);
    end generate;
    addr_wait_counter <= to_unsigned(wait_counter, addr_wait_counter'length);
    sdram_dq <= S_mux_sdram_dq(to_integer(addr_wait_counter)) when state = WRITE else (others => 'Z');
  end block;

  -- set SDRAM data mask
  sdram_dqmh <= '0';
  sdram_dqml <= '0';
end architecture arch;

Along with this core, the main file is a pretty simple program with a few debug features. On startup, it does nothing, and after pressing a button, it starts writing the RAM with all integers from 0 to 8388607 (2^22-1). Once everything is written, I can press a second button which reads the next address, and shows 8 bits of the data result on the LEDs, such that I can see if there are errors or not. Here is the code :

library ieee;
use ieee.numeric_std.all;
use ieee.std_logic_1164.all;

use work.CLK100M;
use work.sdram;

entity SynthFPGA is
port (
    clk50 : in std_logic;
    dram_addr : out unsigned(12 downto 0);
    dram_dq : inout std_logic_vector(15 downto 0);
    dram_ba : out unsigned(1 downto 0);
    dram_dqm : out std_logic_vector(1 downto 0);
    dram_ras_n : out std_logic;
    dram_cas_n : out std_logic;
    dram_cke : out std_logic;
    dram_clk : out std_logic;
    dram_we_n : out std_logic;
    dram_cs_n : out std_logic;
    LEDS_n : out std_logic_vector(7 downto 0);
    button1 : in std_logic;
    button2 : in std_logic;
    dummy_out : out std_logic_vector(31 downto 0)
);
end entity SynthFPGA;

architecture rtl of SynthFPGA is

signal clkmain : std_logic;
signal pll_locked : std_logic;
signal clk48k_audio : std_logic;
signal clk48k_audio_locked : std_logic;
signal initialized : std_logic := '0';
signal reset : std_logic := '0';

signal ram_ctrl_addr    : unsigned(22 downto 0);
signal ram_ctrl_data    : std_logic_vector(31 downto 0);
signal ram_ctrl_we      : std_logic;
signal ram_ctrl_req     : std_logic;
signal ram_ctrl_ack     : std_logic;
signal ram_ctrl_valid   : std_logic;
signal ram_ctrl_q       : std_logic_vector(31 downto 0);

signal wr_ram_ctrl_addr    : unsigned(22 downto 0);
signal wr_ram_ctrl_data    : std_logic_vector(31 downto 0);
signal wr_ram_ctrl_we      : std_logic;
signal wr_ram_ctrl_req     : std_logic;

signal rd_ram_ctrl_addr    : unsigned(22 downto 0);
signal rd_ram_ctrl_data    : std_logic_vector(31 downto 0);
signal rd_ram_ctrl_we      : std_logic;
signal rd_ram_ctrl_req     : std_logic;

signal last_read_data : std_logic_vector(31 downto 0) := (others => '0');

signal rdcnt : natural range 0 to 8_388_607 := 0;

type main_state_type is (IDLE, WRITING_CMD, WRITING_ACK,  READING, READING_BUTTON_RST);
signal main_state : main_state_type := IDLE;

type reading_state_type is (IDLE, START, ACKNOWLEDGED, TERMINATE);
signal reading_state : reading_state_type := IDLE;

signal wrcnt : natural range 0 to 8_388_607 := 0;

begin

dram_clk <= clkmain;

CLK_100M_I : CLK100M
port map(
    inclk0      =>  clk50,
    c0          =>  clkmain,
    c1          =>  clk48k_audio,
    locked      =>  pll_locked
);

main : process(clkmain, reset)
begin
if reset = '1' then
--
elsif rising_edge(clkmain) and pll_locked = '1' then
    case main_state is
        when IDLE =>
            if button1 = '0' then
                main_state <= WRITING_CMD;
                LEDS_N <= "01010101";
            end if;
        when WRITING_CMD =>
            wr_ram_ctrl_addr <= to_unsigned(wrcnt, 23);
            wr_ram_ctrl_data <= std_logic_vector(to_unsigned(wrcnt,32));
            wr_ram_ctrl_we <= '1';
            wr_ram_ctrl_req <= '1';
            main_state <= WRITING_ACK;
            LEDS_N <= "10101010";
        when WRITING_ACK =>
            if ram_ctrl_ack = '1' then
                if wrcnt < 8_388_607 then
                    wrcnt <= wrcnt + 1;
                    wr_ram_ctrl_addr <= to_unsigned(wrcnt, 23);
                    wr_ram_ctrl_data <=  std_logic_vector(to_unsigned(wrcnt,32));
                    wr_ram_ctrl_we <= '1';
                    wr_ram_ctrl_req <= '1';
                    LEDS_n <= std_logic_vector(to_unsigned(wrcnt, 23))(22 downto 15);
                    main_state <= WRITING_ACK;
                else
                    wrcnt <= 0;
                    wr_ram_ctrl_we <= '0';
                    wr_ram_ctrl_req <= '0';
                    main_state <= READING;
                end if;
            end if;

        when READING =>
            if button1 = '1' then
                main_state <= READING_BUTTON_RST;
            end if;
        
        when READING_BUTTON_RST =>
        LEDs_n <= last_read_data(9 downto 2);
            if button1 = '0' then
                main_state <= IDLE;
            end if;

    end case;
end if;
end process;

process(main_state)
begin
    if main_state = WRITING_CMD or main_state = WRITING_ACK then
        ram_ctrl_addr  <= wr_ram_ctrl_addr;
        ram_ctrl_data  <= wr_ram_ctrl_data;
        ram_ctrl_we    <= wr_ram_ctrl_we  ;
        ram_ctrl_req   <= wr_ram_ctrl_req ;
    elsif main_state = READING or main_state = READING_BUTTON_RST then
        ram_ctrl_addr    <= rd_ram_ctrl_addr;
        ram_ctrl_data  <= (others => 'Z');
        ram_ctrl_we      <= rd_ram_ctrl_we  ;
        ram_ctrl_req     <= rd_ram_ctrl_req ;
    end if;
end process;

reading_addr : process(clkmain)

begin
    if rising_edge(clkmain) and pll_locked = '1' and (main_state = READING or main_state = READING_BUTTON_RST) then
        case reading_state is
            when IDLE =>
                if button2 = '0' then
                    reading_state <= START;
                    rdcnt <= rdcnt + 1;
                end if;
            when START =>
                if ram_ctrl_ack = '1' then
                    rd_ram_ctrl_req <= '0';
                    reading_state <= ACKNOWLEDGED;
                else
                    rd_ram_ctrl_addr <= to_unsigned(rdcnt, 23);
                    rd_ram_ctrl_we <= '0';
                    rd_ram_ctrl_req <= '1';
                end if;
            when ACKNOWLEDGED =>
                if ram_ctrl_valid = '1' then
                    last_read_data <= ram_ctrl_q;
                    reading_state <= TERMINATE;
                end if;
            when TERMINATE =>
                if button2 = '1' then
                    reading_state <= IDLE;
                end if;
        end case;
    end if;
end process reading_addr;

dummy_out <= last_read_data;

sdram_controller_instance : entity sdram
generic map(
    CAS_LATENCY =>        2,
    CLK_FREQ    =>    133.0,
    T_DESL      => 200000.0,
    T_MRD       =>     14.0,
    T_RC        =>     60.0,
    T_RCD       =>     15.0,
    T_RP        =>     15.0,
    T_WR        =>     15.0,
    T_REFI      =>  20000.0
)
port map(
    reset       =>  not pll_locked,
    clk         =>  clkmain,
    addr        =>  ram_ctrl_addr,
    data        =>  ram_ctrl_data,
    we          =>  ram_ctrl_we,
    req         =>  ram_ctrl_req,
    ack         =>  ram_ctrl_ack,
    valid       =>  ram_ctrl_valid,
    q           =>  ram_ctrl_q,

    sdram_a     =>  dram_addr,
    sdram_ba    =>  dram_ba,
    sdram_dq    =>  dram_dq,
    sdram_cke   =>  dram_cke,
    sdram_cs_n  =>  dram_cs_n,
    sdram_ras_n =>  dram_ras_n,
    sdram_cas_n =>  dram_cas_n,
    sdram_we_n  =>  dram_we_n,
    sdram_dqml  =>  dram_dqm(0),
    sdram_dqmh  =>  dram_dqm(1)
);

end architecture rtl;

The PLL is a simple Altera PLL IP, which is fed by the 50MHz on-board clock of the DE0-nano board, generating a 133.035714MHz (149/56 of the 50) clock. However, this program does not seem to work correctly, the data I'm reading from the RAM is corrupted.

I'm honestly not sure what is going wrong, and the debugging is complicated. I don't have any oscilloscope that goes up above 30MHz signals, and for some reason, the Signal Tap Analyzer from Quartus Prime makes the FPGA behave strangely (or at least, differently than when it's not connected).

I'm pretty sure that the issue might come from some parameters that I did not setup correctly, but I can't find which ones. After double checking my assignments, and the SDRAM chip datasheet, I'm not able to find my mistake, and I'm completely unable to locate the issue in my system. Where can the issues come from in my logic ?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

As noted by the other answer you do need to shift the clock. But what I would do first is try and run the SDRAM at a (much) slower clock. At 50Mhz you most likely can get away with not shifting the clock. So I would first try to make it work at 50Mhz to validate the state machine is correct. Then if that works move to a higher clock speed and introduce the shift.

\$\endgroup\$
0
\$\begingroup\$

You may need to phase shift the clock for the SDRAM. Here is a tutorial.

https://ftp.intel.com/Public/Pub/fpgaup/pub/Teaching_Materials/current/Tutorials/VHDL/DE0-Nano/Using_the_SDRAM.pdf

See Note:

"The clock skew depends on physical characteristics of the DE0-Nano board. For proper operation of the SDRAM chip, it is necessary that its clock signal, DRAM_CLK, leads the Nios II system clock, CLOCK_50, by 3 nanoseconds."

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.