DDR2(2):Quartus DDR2 IP 官方例程仿真
DDR2,全称 Double Data Rate 2 SDRAM,即第二代双倍数据速率同步动态随机存取存储器。它属于 SDRAM 家族的存储器产品,提供了相较于 DDR SDRAM 更高的运行效能与更低的电压,是 DDR SDRAM 的后继者,虽然 DDR2 和 DDR 都采用了在时钟的.上升沿和下降沿同时进行数据传输的基本方式,但是 DDR2 拥有两倍于 DDR 的预读取系统命令数据的能力(DDR2 4bit预读,DDR 2bit预读)。也就是说,在同样100MHz 的工作频率下,DDR 的实际频率为 200MHz,而 DDR2 则可以达到 400MHz。
DDR2 难度比 SDRAM 复杂,直接用 IP 核比较靠谱。本篇博客记录一下 QuartusII 13.0 软件下的 DDR2 IP 核的官方例程仿真。
注意事项
1、关闭 Windows 的防火墙和电脑里的各种电脑管家。
2、以“管理员身份”打开 Quartus II 13.0。
3、所建工程要和 Quartus II 13.0 的安装盘符不同。
4、DDR2 IP 创建于工程目录下或其子文件,否则仿真出错。
5、如果无论如何都失败,打开任务管理器,将下面这个鬼东西关闭即可。
一、建工程
建立文件夹 DDR2_test,管理员身份打开 Quartus II,创建工程 DDR2_test,不引入任何 .v 文件。
二、IP调用
打开 IP 创建界面:Tools ---> MegaWizard Plug-in Menager。找到 DDR2 IP 并命名。
1、Memory Settings
若 IP 核界面显示不完整,可查看文档《小梅哥DDR2简明教程》寻找解决办法。先设置第 1 个界面:Memory Seting。
(1)General Settings
即页面左上角,这部分主要是配置驱动 DDR2 存储器的通用设置。
- Device family:设备家族,也就是我们用的是哪个系列的 FPGA 芯片,像我们常用的以 EP4CE 开头都是 CycloneIV E 系列。
- Speed grade:FPGA 芯片的速度等级,在芯片的型号名称中可以看到,比如 “EP4CE30F23C8”, 最后一位 8 指的就是速度等级。
- PLL reference clock frequency: PLL 参考时钟频率,由于 DDR2 IP 核内部包含了 PLL 模块,因此我们需要给它提供参考时钟,一般由外部晶振或外部 PLL 输出提供。
- Memory clock frequency:存储器时钟频率,指的就是 DDR2 存储器的工作频率。
- Controller datarate:控制器数据速率,这里有两种选择,一种是Full (全速率),另一种是Half(半速率)。全速率控制器提供的接口时钟频率就是存储器工作时钟频率,半速率控制器提供的接口时钟频率是存储器工作时钟频率的一半。
- Local interface clock frequency:本地接口时钟频率,指的是 DDR2 IP 核给用户提供的接口(也就是控制器接口)时钟频率,由存储器时钟频率和控制器数据速率决定。
- Local interface width:本地接口宽度,指的是 DDR2 IP 核给用户提供的接口(也就是控制器接口)数据宽度,由存储器数据宽度和控制器数据速率决定。如果控制器速率选择的是 Full,那么本地接口宽度是存储器数据宽度的两倍( DDR2内部在时钟上升沿和下降沿都能吞吐数据)。如果控制器速率选择的是 Half,那么本地接口宽度是存储器数据宽度的4倍,这是由于控制器接口时钟频率降低了一半。
(2)Memory Presets
即页面右下角,这部分主要是选择 DDR2 存储器芯片型号和设置相关参数。如果加载列表中没有我们想要的芯片型号,那么可以点击【Load Preset】选项,通过选择对应的 .xml 文件就能把定制的芯片参数加载到列表中。选好芯片型号后,在右边有个按钮【Modify parameters】,点进去可以设置所选芯片的参数集,如下所示:
整个参数设置分 3 组,分别是 Memory Attributes、Memory Initialization Options 和 Memory Timing Parameters,其中灰色部分代表的是和存储器直接相关的参数,修改这些参数会导致生成一个新的芯片型号参数集。
① Memory Attributes
- Memoryvendor:存储器生厂商,有Micron、 Samsung、 Hynix, 以及 JEDEC 等。
- Memory format:存储器格式,有 Discrete Device (分散设备),Unbuffered DIMM (无缓存DIMM) 和 Registered DIMM (寄存器的DIMM )。
- Maximum memory frequency:设置存储器支持的最大频率。
- Column address width:定义存储器列地址宽度。
- Row address width:定义存储器行地址宽度。
- Bank address width:定义存储器 bank 地址宽度。
- Output clock pairs from FPGA:指定从 FPGA 驱动到存储器的差分时钟对。当接口有多个设备时,时钟对越多,每个输出的负载就越小。
- Chip selects per DIMM:定义接口中每个 DIMM 的片选数。
- Total Memory chip selects:设置存储器接口中片选信号数,存储深度决定该参数。可以限制显示本地端二进制编码片选地址的范围。如果范围不能满足设计要求,则可以选择下一个较高的值。但是 ALTMEMPHY 的最高位地址空间不会映射到真正的存储器地址。ALTMEMPHY 使用多个片选信号并用 mem_cs_n 信号校准片选信号。
- Precharge address bit:选择用于预充电的地址宽度。
- Drive DM pins from FPGA:指定是否用 DM 引脚进行写操作。
- Maximum memory frequency for CAS latency (3.0、4.0、 5.0、6.0):每个给定 CAS 延时情况下存储器的最大频率限制,如果输入的值超出了范围则会生成警告信息。
② Memory Initialization Options
- Memory burst length:设置每次传输的读或写数据字的个数。这是IP核内,不是外部的 local_size,full rate下只能是 4,half rate下只能 8 。
- Memory burst ordering:控制向写操作期间存储器和 FPGA 之间数据传输的顺序。
- Enable the DLL in the memory devices:指定是否在存储器设备中使能DLL,最好选择是,否则 ALMEMPHY 不能确保在关闭在 DLL 时能正常工作。
- Memory drive strength setting:控制存储器芯片输出缓存的驱动强度。
- Memory on-die termination (ODT) setting:设置存储器 ODT 的值。
- Memory CAS latency setting:设置从读命令到从存储器中输出第 1 个数据之间的延时时钟周期数。
- Memory Additive CAS latency setting:附加 CAS 延迟设置。
③ Memory Timing Parameters
- Memory initialization time at power- -up (tINIT):最小存储器初始化时间。
- Load mode register command period (tMRD):最小加载模式寄存器命令周期。
- Active to precharge time (tRAS):激活到预充电的最小时间。
- Active to read/write time (tRCD):激活到读-写的最小时间。
- Precharge command peniod (tRP):最小预充电命令周期。
- Refresh command interval (tREFI):刷新命令之间的最大间隔。
- Auto-refresh command interval (tRFC):最小自刷新命令周期。
- Write recovery time (tWR):最小写恢复时间。
- Write to read period (tWTR):最小写到读命令延时。
- DQ output access time from CK/CK# (tAC):从 CK/CK# 信号到 DQ 输出访问时间。
- DQS output access time from CK/CK# (tDQSCK):从 CK/CK# 信号到 DQS 输出访问时间。
- DQS-DQ skew for DQS and associated DQ signals (tDQSQ):DQS 到DQ 的最大时间差。
- First latching edge of DQS to associated clock edge,+or- (tDQSS):正的 DQS 锁存沿到相应时钟沿的时间。
- DQ and DM input hold time relative to DQS (tDH):DQ 和 DM 关于 DQS 的输入保持时间。
- DQ and DM input setup time relative to DQS (tDS):DQ 和 DM 关于 DQS 的建立时间。
- DQS flling edge hold time from CK (tDSH):从 CK 开始 DQS 下降沿保持时间。
- DQS flling edge to CK setup time (tDSS):DQS 下降沿到 CK 建立的时间。
- Address and command input hold time (tlH):地址和控制输入保持时间。
- Address and command input setup time (tIS):地址和控制输入建立时间。
- DQ hold skew factor (tQHS):最大数据保持时间差因子。
- Four active window time (tFAW):每个芯片的 4 个活动窗时间。
- RAS to RAS delay time (tRRD):每个芯片的激活时间。
- Read to precharge time(tRTP):读到预充电的时间。
2、PHY Settings
这个标签我们一般不用改动。可以看出,这里设置的是 ALTEMEMPHY 有关的参数。对于 cyclone IV E 系列芯片,在 PHY Settings 页面下只有两项可以修改:
- Address/Command Clock Settings:设置的是时钟相位,调整地址和命令相位可以改进存储器芯片中地址和命令的建立和保持延时,以补偿不同加载带来的传输延时。可以根据 phy_clk 和 write_clk 时钟信号的上升沿和下降沿选择0°、90°、 180° 和 270° 相位偏移。
- Auto-Calibration Simulation Options:设置的是自动校准仿真选项,在 Full Calibration(全校准) Quick Calibration (快速校准) 和 Skip Calibration (跳过校准)之间进行选择,仅用于仿真,选择 Full Calibration 时仿真时间较长。
3、Board Settings
这个标签我们一般不用改动。可以看出,这里主要是设置一些板级参数来消除对时序的影响。
- Number of Slots/Discrete Devices:需要设置芯片数,可以选择单级或多级配置。
- CKCK# slew rate ( Differential):设置 CK/CK# 信号的差分压摆率。
- Addr/Command slew rate:设置地址/命令信号的压摆率。
- DQS/DQS# slew rate ( Differential ):设置 DQS/DQS# 信号的差分压摆率。
- DQ slew rate:设置 DQ 信号的压摆率。
- tIS、tH、tDS、tDH:这 4 个量是控制信号和数据总线的建立和保持时间,这意味着建立和保持时间是通过设置压摆率来实现的,当然这 4 个量也可以手动输入更改。
- Addr/Commmand eye reduction ( setup ):设置地址和命令信号上由于 ISI 导致的建立过程的眼图缩减。
- Addr/Commmand eye reduction ( hold ):设置地址和命令信号.上由于 ISI 导致的保持过程的眼图缩减。
- DQ eyereduction:设置 DQ 信号上由于 ISI 导致的眼图缩减。
- Delta DQS:设置由于 ISI 导致 DQS 信号到达时间范围的增量。
- Max skew within DQS group:设置 DQS 组中 DQ 管脚之间的最大偏移,此值在所有配置(单级或多级,DIMM或器件)中影响 DDR2 接口的读捕捉和写裕量。
- Max skew between DQS groups:设置不同 DQS 组中 DQS 信号之间的最大偏移,此值在单级或多级配置中都会影响 DDR2 接口的再同步裕量。
- Addr/command to CK skew:设置 CK 信号与地址和命令信号之间的偏移或传输延迟,正值代表长于 CK 信号的地址和命令信号,负值代表短于 CK 信号的地址和命令信号,Quartus II 使用该偏移值来优化地址和命令延迟,从而为 DDR2 接口获得合适的建立和保持裕量。
4、Controller Settings
这个标签我们一般不用改动。可以看出,这里设置的是控制器的参数。我们一般默认选择的是:High Performance Controller II,第二代,性能更好。
- Enable Self-Refresh Controls:使能控制器允许用户控制何时让外部存储器工作在自刷新模式。
- Enable Auto Power Down:使能控制器在观测到特定数量的空闲时钟周期后自动将外部存储器置于 power-down 模式,如果勾选了该项,在其后的 Auto Power Down Cycles 一栏可以设置空闲时钟的数量。
- Enable User Auto-Refresh Controls:使能控制器允许用户发出单一刷新。
- Enable Auto-Precharge Control:在控制器顶层使能自动预充电控制,用户可以在读或写突发期间置位 auto-precharge 控制信号来指定控制器在读或写突发末尾是否关闭(也就是auto-precharge )当前打开页。
- Enable Reordering:使能控制器重排序命令和数据来达到更高效率。
- Starvation limit for each command:指定执行一个等待命令之前能够执行的命令数量,该值的合法范围是1~63。
- Local-to-Memory Address Mapping:允许用户控制Avalon接口的地址位与存储器接口的 chip、row、 bank、 column 位的映射方式,如果用户应用程序发出了大于存储器列大小的突发操作,选择 Chip-Row-Bank-Column 可以利用 look-ahead bank management 特性在突发到达列尾时隐藏改变当前打开页所产生的影响。另外一种情况是,如果用户应用程序有几个主机并且用到的是存储器的不同区域,那么选择 Chip-Bank-Row-Column 可以通过高位地址给每个主机分配相应的物理bank,这样就避免了当控制器必须在同一 bank 打开和关闭行的情况下不同主机访问同一bank可能造成的效率下降。
- Command Queue Look- Ahead Depth:指定命令队列 look-ahead 深度值来控制 look- ahead bank management 逻辑检验的读或写请求数。
- Local Maximum Burst Count:指定突发数来配置控制器从端口能接收的最大 Avalon 突发数,选择 4(100),则外部 local_size 位宽为3。
- Enable Configuratio and Status Register Interface:使能控制器的运行时刻配置和状态检索,这样可以添加一个额外的 Avalon-MM 从端口到存储控制器顶层,从而实现存储器时序参数、存储器地址大小和模式寄存器设置以及控制器功能,如果 Eror Detection and Correction Logic 选项被使能,那么该从端口允许用户控制和检索该逻辑的状态。
- Enable Error Detection and CorrectionLogic:使能错误检测和纠正(ECC )功能用于单比特和双比特错误纠正。Enable Auto Error Correction 开启此选项后,当 ECC 逻辑检测到单比特错误时,允许控制器执行自动纠错,如果关闭此选项,意味着在特定的时间进行纠错,以实现更高的系统效率。
5、EDA
从该页面中可以看出,如果我们想要仿真 DDR2 IP 核,那么就需要用到 altera_mf 、220model 和 sgate 这三个仿真库。Generate simulation model 选项可以选择是否要生成这3个文件,Generate netlist 选项可以选择是否生成网表文件,如果用户想用第三方EDA综合工具来综合设计的文件,Quartus可以为该综合工具提供网表文件,用来评估时序和资源利用率。这里需要注意的是,并不是所有的第三方EDA工具都支持。我们需要仿真,因此勾选。
6、Summary
总结页面,不用管。
点击 Finish,等待 1 分钟,生成 IP 完毕。如果等了 3 分钟还未生成,表明生成 IP 失败,请删除工程,检查注意事项后重新操作。
三、添加例程
1、例程添加
点击 Quartus II 界面的 File,将例程添加进来。
(1)DDR2_example_top.v
(2)DDR2_example_driver.v
2、模块设置
将 DDR2_example_top 右键设为顶层模块。
3、代码解释
例程可以帮我们理解 DDR2 IP 的使用,具体的解释可以查看设计文档《emi_ddr_ug.pdf》》。生成的代码有些乱,注释很少,我稍微整理了一下:
1 //************************************************************************** 2 // *** 名称 : DDR2_example_top.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2020-6-9 6 // *** 工具 : Quartus 13.0 7 // *** 芯片 : Cyclone IV E 8 // *** 型号 : EP4CE30F23C6 9 // *** 描述 : DDR2_IP官方仿真例程的顶层模块 10 //************************************************************************** 11 12 module DDR2_example_top 13 //========================< 端口 >========================================== 14 ( 15 //system -------------------------------------------- 16 input wire clock_source , //时钟 17 input wire global_reset_n , //复位 18 //DDR2 ---------------------------------------------- 19 output wire [ 15: 0] mem_addr , //DDR2地址总线 20 output wire [ 2: 0] mem_ba , //DDR2组地址总线 21 output wire mem_cas_n , //DDR2列地址选择 22 output wire mem_cke , //DDR2时钟使能 23 inout wire mem_clk , //DDR2时钟 24 inout wire mem_clk_n , //DDR2时钟反相 25 output wire mem_cs_n , //DDR2片选 26 output wire [ 1: 0] mem_dm , //DDR2掩码 27 inout wire [ 15: 0] mem_dq , //DDR2数据总线 28 inout wire [ 1: 0] mem_dqs , //DDR2数据源同步 29 output wire mem_odt , //DDR2片内终止控制 30 output wire mem_ras_n , //DDR2行地址选择 31 output wire mem_we_n , //DDR2写使能 32 //DDR2_test ----------------------------------------- 33 output wire pnf , //数据正确指示(pass not fail) 34 output wire [ 3: 0] pnf_per_byte , //数据正确指示(字节) 35 output wire test_complete , //DDR2测试完成指示 36 output wire [ 7: 0] test_status //DDR2测试状态 37 ); 38 //========================< 连线 >========================================== 39 wire cs_n ; 40 wire local_burstbegin_sig ; 41 wire mem_aux_full_rate_clk ; 42 wire mem_aux_half_rate_clk ; 43 wire [ 27: 0] mem_local_addr ; 44 wire [ 3: 0] mem_local_be ; 45 wire [ 9: 0] mem_local_col_addr ; 46 wire mem_local_cs_addr ; 47 wire [ 31: 0] mem_local_rdata ; 48 wire mem_local_rdata_valid ; 49 wire mem_local_read_req ; 50 wire mem_local_ready ; 51 wire [ 2: 0] mem_local_size ; 52 wire [ 31: 0] mem_local_wdata ; 53 wire mem_local_write_req ; 54 wire phy_clk ; 55 wire reset_phy_clk_n ; 56 wire tie_high ; 57 wire tie_low ; 58 //========================================================================== 59 //== code 60 //========================================================================== 61 assign mem_cs_n = cs_n; //多次一举,直接连线mem_cs_n即可 62 assign tie_high = 1'b1; //拉高 63 assign tie_low = 1'b0; //拉低 64 //========================================================================== 65 //== DDR2 IP 66 //========================================================================== 67 DDR2 DDR2_inst 68 ( 69 .aux_full_rate_clk (mem_aux_full_rate_clk ), //全速率时钟 70 .aux_half_rate_clk (mem_aux_half_rate_clk ), //半速率时钟 71 .global_reset_n (global_reset_n ), //全局异步复位 72 //----------------------------------------------- 73 .local_address (mem_local_addr ), //用户_地址总线 74 .local_be (mem_local_be ), //用户_字节使能标志 75 .local_burstbegin (local_burstbegin_sig ), //用户_突发起始 76 .local_init_done ( ), //用户_初始化完成 77 .local_rdata (mem_local_rdata ), //用户_读数据总线 78 .local_rdata_valid (mem_local_rdata_valid ), //用户_读数据有效 79 .local_read_req (mem_local_read_req ), //用户_读数据请求 80 .local_ready (mem_local_ready ), //用户_读写请求被接收指示 81 .local_refresh_ack ( ), //用户_刷新请求 82 .local_size (mem_local_size ), //用户_突发大小 83 .local_wdata (mem_local_wdata ), //用户_写数据总线 84 .local_write_req (mem_local_write_req ), //用户_写数据请求 85 //----------------------------------------------- 86 .mem_addr (mem_addr[15 : 0] ), //DDR2地址总线 87 .mem_ba (mem_ba ), //DDR2组地址总线 88 .mem_cas_n (mem_cas_n ), //DDR2列地址选择 89 .mem_cke (mem_cke ), //DDR2时钟使能 90 .mem_clk (mem_clk ), //DDR2时钟 91 .mem_clk_n (mem_clk_n ), //DDR2时钟反相 92 .mem_cs_n (cs_n ), //DDR2片选 93 .mem_dm (mem_dm[1 : 0] ), //DDR2掩码 94 .mem_dq (mem_dq ), //DDR2数据总线 95 .mem_dqs (mem_dqs[1 : 0] ), //DDR2数据源同步 96 .mem_odt (mem_odt ), //DDR2片内终止控制 97 .mem_ras_n (mem_ras_n ), //DDR2行地址选择 98 .mem_we_n (mem_we_n ), //DDR2写使能 99 //----------------------------------------------- 100 .phy_clk (phy_clk ), //DDR2工作时钟 101 .pll_ref_clk (clock_source ), //IP核中的PLL输入时钟 102 .reset_phy_clk_n (reset_phy_clk_n ), //IP核提供的复位 103 .reset_request_n ( ), //IP核中的PLL锁定 104 .soft_reset_n (tie_high ) //全局异步复位信号(不复位PLL) 105 ); 106 //========================================================================== 107 //== 连接列地址位,因为2:1的数据速率,从示例驱动程序输出中删除1位 108 //========================================================================== 109 assign mem_local_addr[8 : 0] = mem_local_col_addr[9 : 1]; 110 //========================================================================== 111 //== DDR2 控制器 112 //========================================================================== 113 DDR2_example_driver driver 114 ( 115 .clk (phy_clk ), //DDR2工作时钟 116 .local_bank_addr (mem_local_addr[27 : 25]), //用户_地址总线 117 .local_be (mem_local_be ), //用户_字节使能标志 118 .local_burstbegin (local_burstbegin_sig ), //用户_突发起始 119 .local_col_addr (mem_local_col_addr ), //用户_地址总线 120 .local_cs_addr (mem_local_cs_addr ), //未用到 121 .local_rdata (mem_local_rdata ), //用户_读数据总线 122 .local_rdata_valid (mem_local_rdata_valid ), //用户_读数据有效 123 .local_read_req (mem_local_read_req ), //用户_读数据请求 124 .local_ready (mem_local_ready ), //用户_读写请求被接收指示 125 .local_row_addr (mem_local_addr[24 : 9] ), //用户_地址总线 126 .local_size (mem_local_size ), //用户_突发大小 127 .local_wdata (mem_local_wdata ), //用户_写数据总线 128 .local_write_req (mem_local_write_req ), //用户_写数据请求 129 .pnf_per_byte (pnf_per_byte[3 : 0] ), //数据正确指示(字节) 130 .pnf_persist (pnf ), //数据正确指示 131 .reset_n (reset_phy_clk_n ), //IP核提供的复位 132 .test_complete (test_complete ), //DDR2测试完成指示 133 .test_status (test_status ) //DDR2测试的运行状态 134 ); 135 136 137 endmodule
四、仿真搭建
1、Modelsim 软件的路径设置。打开Quartus ii,点击Tools --- Options --- EDA Tool Optinons,将modelsim的安装路径填写进去。我这用的是QuestaSim,和Modelsim是完全没有区别的。为了防止遗漏,我把下面三个位置都填上:
2、Quartus ii 关联 Modelsim,一开始建立工程时就可以设置,如果忘记了或者设置错了也可以再次更改。点击Assignments --- Setings --- Eda Tool Setings,将仿真工具选择好即可。(我的是QuastaSim,就填的QuastaSim)
3、点击Assignments --- Setings --- EDA Tool Setings下的simulation,将 Tool name 选择好,然后点击 Compile testbench,之后点击Test Benches。
4、填写 New Test Bench Settings,添加仿真文件。
(1)DDR2_mem_model.v,DDR2 仿真模型;
(2)DDR2_example_top_tb,DDR2 仿真文件;
5、回到 Quartus II 界面,综合编译后点击 RTL Simulation 执行仿真。
经过一小段时间的等待,波形就出来了。
五、上板看波形
上面只是仿真,要上板则增加几点注意事项。
1、选择型号
上面仿真时是看着小梅哥《小梅哥DDR2简明教程》做的,但是要上板时发现自己的 DDR2 型号和他的不一致,而且是两片 DDR2 一起用。选择自己的 DDR2 型号,点击 Modify parameter 对 DQ 和 Bank 参数进行修改一下,其他选项都一样,如下所示:
2、添加 tcl 电平标准文件
FPGA板卡是 3.3V,而 DDR2 是 1.8v,还有一些别的东西,都可以通过添加 tcl 文件实现。点击菜单栏 Tools --- Tcl Scripts,选择 DDR2_pin_assignments.tcl,Run
3、添加网表约束文件
点击菜单栏 Assignments --- Settings --- TimeQuest Timing Analyzer,照下图添加即可。
4、添加引脚约束文件
找到开发板原理图,将引脚位置填上去,大多开发板都会提供写好的引脚文件,复制到 qsf 文件中即可。
5、添加 SignalTapII ,编译,查看波形
波形出来了,说明我们操作成功!
六、补充说明
参考资料:
1、小梅哥《DDR2简明教程》
2、锆石科技FPGA教程