EMIF (External Memory Interface) 是 Quartus 平台提供的 IP,用于实现高速存储器件接口与控制器。通过 Intel Quartus Prime 软件,可以很方便地实现 EMIF IP 电路。本文记录了使用 EMIF 实现 DDR3 控制器的仿真过程,软件平台为 Quartus Prime Pro 21.3,器件型号为 10CX220YF780E6G.
目录
1 EMIF IP 介绍
2 EMIF DDR3 IP 配置
3 EMIF DDR3 IP 仿真
1 EMIF IP 介绍
Intel EMIF IP 是 Quartus 平台提供的 IP,用于实现高速存储器件接口与存储控制器电路。借助 EMIF 电路,FPGA 可以与外部存储器件进行数据交换。
EMIF IP 实现物理层接口与存储控制器,这两部分的功能说明如下:
- 物理层接口(Physical Layer Interface),用于建立数据通路,以及管理 FPGA 和存储器件的传输时序;
- 存储控制器(Memory Controller),实现内存命令与协议层规范。
EMIF IP 的总体设计流程如下图:
EMIF IP 设计过程中涉及许多参数与配置步骤,IP 配置完成之后,可以生成参考工程,用于功能仿真,以检查参数配置是否有误。
2 EMIF DDR3 IP 配置
新建工程,器件型号选择 10CX220YF780E6G。
在 IP Catalog 中输入 memory,然后双击选择 External Memory Interfaces Intel Cyclone 10 FPGA IP。
Memory Protocol 选 DDR3,时钟频率和 PLL 参考时钟频率根据需要配置,这里时钟频率填 933M,PLL 参考时钟频率为 116.625MHz.
(PS:右下角 Presets 中有预设的选项,双击可以直接应用)
容量为 1Gb 的 DDR3,行地址为 13bit,DQ width 根据实际器件类型填写。
中间时序参数可以先跳过,最后有个仿真选项,选择 Skip Calibration,跳过校准阶段。
IP 配置完成之后,点击 Generate Example Design,生成参考工程。
3 EMIF DDR3 IP 仿真
在前面生成的参考工程中,仿真相关文件在 sim/ed_sim/mentor 和 sim/ed_sim/sim 路径下。修改 sim/ed_sim/sim 路径下的 ed_sim.v 文件,替换掉 ed_sim_tg 模块例化代码,就可以仿真自己编写的控制逻辑。
仿照 ed_sim_tg 模块接口,编写 ed_sim_tg_0 模块,代码如下:
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 isgeneric(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; -- 233MHzlocal_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)
beginif emif_usr_reset_n = '0' thenpstate <= 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) thencase(pstate) iswhen st_emif_init => if local_cal_success = '1' thenpstate <= st_amm_idle;elsepstate <= 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 thencnt_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';elseif buf_amm_write_0 = '1' and amm_ready_0 = '1' thencnt_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' thenbuf_amm_read_0 <= '0';end if;if cnt_amm_readdatavalid_0 = 64 thencnt_amm_readdatavalid_0 <= (others => '0');pstate <= st_amm_idle;elseif amm_readdatavalid_0 = '1' thencnt_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;
打开 Modelsim 读取 msim_setup.tcl 文件,编译 EMIF IP 文件与用户设计文件,并启动仿真。
等待 emif_c10_0_status_local_cal_success 拉高,就可以进行 DDR3 数据读写操作了。