[Quartus FPGA] EMIF DDR3 IP simulation record

EMIF (External Memory Interface) is an IP provided by the Quartus platform for implementing high-speed memory device interfaces and controllers. Through the Intel Quartus Prime software, the EMIF IP circuit can be easily implemented. This article records the simulation process of using EMIF to realize the DDR3 controller. The software platform is Quartus Prime Pro 21.3, and the device model is 10CX220YF780E6G.

Table of contents

1 Introduction to EMIF IP

2 EMIF DDR3 IP Configuration

3 EMIF DDR3 IP Emulation


1 Introduction to EMIF IP

 

       Intel EMIF IP is an IP provided by the Quartus platform for implementing high-speed memory device interfaces and memory controller circuits. With the help of EMIF circuits, FPGA can exchange data with external memory devices. 

        EMIF IP implements the physical layer interface and storage controller. The functions of these two parts are described as follows:

  • Physical Layer Interface (Physical Layer Interface), used to establish data paths, and manage the transmission timing of FPGA and storage devices;
  • Memory Controller (Memory Controller), implements memory commands and protocol layer specifications.

        The overall design process of EMIF IP is as follows:

        The EMIF IP design process involves many parameters and configuration steps. After the IP configuration is completed, a reference project can be generated for functional simulation to check whether the parameter configuration is correct.

2 EMIF DDR3 IP Configuration

        For a new project, select 10CX220YF780E6G for the device model.

 

        Enter memory in IP Catalog, then double-click to select External Memory Interfaces Intel Cyclone 10 FPGA IP.

        Select DDR3 as Memory Protocol, and configure the clock frequency and PLL reference clock frequency as required. Here, fill in 933M for the clock frequency, and 116.625MHz for the PLL reference clock frequency.

 (PS: There are preset options in Presets in the lower right corner, double-click to apply directly)

        For DDR3 with a capacity of 1Gb, the row address is 13bit, and the DQ width is filled in according to the actual device type.

        The intermediate timing parameters can be skipped first, and finally there is a simulation option, select Skip Calibration, and skip the calibration stage.

        After the IP configuration is complete, click Generate Example Design to generate a reference project.

3 EMIF DDR3 IP Emulation

        In the reference project generated above, the simulation-related files are under the sim/ed_sim/mentor and sim/ed_sim/sim paths. Modify the ed_sim.v file under the sim/ed_sim/sim path, replace the ed_sim_tg module instantiation code, and then you can simulate the control logic written by yourself.

        Following the ed_sim_tg module interface, write the ed_sim_tg_0 module, the code is as follows:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity ed_sim_tg_0 is
   generic(
      AMM_WRITE_PATTERN   : std_logic_vector := X"0102030405060708090A0B0C0D0E0F10";
      AMM_WRITE_INC       : std_logic_vector := X"01010101010101010101010101010101"
   );
   port(
      emif_usr_reset_n    : in std_logic;
      emif_usr_clk        : in std_logic; -- 233MHz
      local_cal_success   : in std_logic;
      amm_ready_0         : in std_logic;
      amm_read_0          : out std_logic;
      amm_write_0         : out std_logic;
      amm_address_0       : out std_logic_vector(28 downto 0);
      amm_readdata_0      : in std_logic_vector(127 downto 0);
      amm_writedata_0     : out std_logic_vector(127 downto 0);
      amm_burstcount_0    : out std_logic_vector(6 downto 0);
      amm_byteenable_0    : out std_logic_vector(15 downto 0);
      amm_readdatavalid_0 : in std_logic
   );
end entity;
architecture behav of ed_sim_tg_0 is
-- internal signal declarations
type state is (
   st_emif_init,
   st_amm_idle,
   st_amm_write,
   st_amm_read
);
signal pstate                  : state := st_emif_init;
signal buf_amm_read_0          : std_logic;
signal buf_amm_write_0         : std_logic;
signal cnt_amm_write_0         : std_logic_vector(7 downto 0);
signal buf_amm_address_0       : std_logic_vector(28 downto 0);
signal buf_amm_writedata_0     : std_logic_vector(127 downto 0);
signal buf_amm_burstcount_0    : std_logic_vector(6 downto 0);
signal buf_amm_byteenable_0    : std_logic_vector(15 downto 0);
signal cnt_amm_readdatavalid_0 : std_logic_vector(7 downto 0);
------------------------------------------------------
begin
------------------------------------------------------
amm_write_0      <= buf_amm_write_0;
amm_read_0       <= buf_amm_read_0;
amm_address_0    <= buf_amm_address_0;
amm_writedata_0  <= buf_amm_writedata_0;
amm_burstcount_0 <= buf_amm_burstcount_0;
amm_byteenable_0 <= buf_amm_byteenable_0;

process(emif_usr_reset_n,emif_usr_clk) 
begin
   if emif_usr_reset_n = '0' then
      pstate <= st_emif_init;
      buf_amm_write_0 <= '0';
      cnt_amm_write_0 <= (others => '0');
      buf_amm_read_0 <= '0';
      buf_amm_address_0 <= (others => '0');
      buf_amm_writedata_0 <= (others => '0');
      buf_amm_burstcount_0 <= (others => '0');
      buf_amm_byteenable_0 <= (others => '0');
      cnt_amm_readdatavalid_0 <= (others => '0');
   elsif rising_edge(emif_usr_clk) then
      case(pstate) is
         when st_emif_init => 
            if local_cal_success = '1' then
               pstate <= st_amm_idle;
            else
               pstate <= st_emif_init;
            end if;

         when st_amm_idle => 
            pstate <= st_amm_write;
            buf_amm_writedata_0 <= AMM_WRITE_PATTERN;
            buf_amm_address_0 <= buf_amm_address_0 + 1;

         when st_amm_write => 
            if cnt_amm_write_0 = 63 then
               cnt_amm_write_0 <= (others => '0');
               buf_amm_write_0 <= '0';
               buf_amm_byteenable_0 <= (others => '0');
               pstate <= st_amm_read;
               buf_amm_read_0 <= '1';
            else
               if buf_amm_write_0 = '1' and amm_ready_0 = '1' then
                  cnt_amm_write_0 <= cnt_amm_write_0 + 1;
                  buf_amm_writedata_0 <= buf_amm_writedata_0 + AMM_WRITE_INC;
               end if;
               buf_amm_write_0 <= '1';
               buf_amm_burstcount_0 <= conv_std_logic_vector(64,7);
               buf_amm_byteenable_0 <= (others => '1');
               pstate <= st_amm_write;
            end if;

         when st_amm_read => 
            if buf_amm_read_0 = '1' and amm_ready_0 = '1' then
               buf_amm_read_0 <= '0';
            end if;

            if cnt_amm_readdatavalid_0 = 64 then
               cnt_amm_readdatavalid_0 <= (others => '0');
               pstate <= st_amm_idle;
            else
               if amm_readdatavalid_0 = '1' then
                  cnt_amm_readdatavalid_0 <= cnt_amm_readdatavalid_0 + 1;
               end if;
               pstate <= st_amm_read;
            end if;

         when others => NULL;

      end case;
   end if;
end process;
end architecture;

        Open Modelsim to read the msim_setup.tcl file, compile the EMIF IP file and user design file, and start the simulation. 

Wait for emif_c10_0_status_local_cal_success to be pulled high, then DDR3 data can be read and written.

Guess you like

Origin blog.csdn.net/sxyang2018/article/details/131583656