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 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;
打开 Modelsim 读取 msim_setup.tcl 文件,编译 EMIF IP 文件与用户设计文件,并启动仿真。
等待 emif_c10_0_status_local_cal_success 拉高,就可以进行 DDR3 数据读写操作了。