DDR2难度比 SDRAM 复杂,自己写控制器不现实,直接用 IP 核比较靠谱。本篇博客记录一下 QuartusII 13.0 软件下的 DDR2 IP 核的官方例程仿真。
注意事项
1、关闭 Windows 的防火墙和电脑里的各种奇葩电脑管家。
2、以“管理员身份”打开 Quartus II 13.0。
3、所建工程要和 Quartus II 13.0 的安装盘符不同。
4、DDR2 IP 创建于工程目录下,否则仿真出错。
一、建工程
建立工程,命名为:DDR2_test,不引入任何 .v 文件。
二、IP调用
1、 打开 IP 创建界面:Tools ---> MegaWizard Plug-in Menager。
2、找到 DDR2 IP 并命名。
3、点击 OK,等待约 10 秒则会弹出 IP 核配置界面。
4、若 IP 核界面显示不完整,可查看文档《小梅哥DDR2简明教程》寻找解决办法。
5、设置 IP 参数,先设置第 1 个界面。
6、一路 next,直到 EDA 选项界面,勾选 Generate Simulation model,生成仿真模型。
7、点击 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 //inputs -------------------------------------------- 16 input wire clock_source , //时钟 17 input wire global_reset_n , //复位 18 //outputs ------------------------------------------- 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 output wire pnf , //数据正确指示(pass not fail) 33 output wire [ 3: 0] pnf_per_byte , //数据正确指示(字节) 34 output wire test_complete , //DDR2测试完成指示 35 output wire [ 7: 0] test_status //DDR2测试状态 36 ); 37 //========================< 连线 >========================================== 38 wire [ 0: 0] cs_n ; 39 wire local_burstbegin_sig ; 40 wire mem_aux_full_rate_clk ; 41 wire mem_aux_half_rate_clk ; 42 wire [ 27: 0] mem_local_addr ; 43 wire [ 3: 0] mem_local_be ; 44 wire [ 9: 0] mem_local_col_addr ; 45 wire mem_local_cs_addr ; 46 wire [ 31: 0] mem_local_rdata ; 47 wire mem_local_rdata_valid ; 48 wire mem_local_read_req ; 49 wire mem_local_ready ; 50 wire [ 2: 0] mem_local_size ; 51 wire [ 31: 0] mem_local_wdata ; 52 wire mem_local_write_req ; 53 wire phy_clk ; 54 wire reset_phy_clk_n ; 55 wire tie_high ; 56 wire tie_low ; 57 //========================================================================== 58 //== code 59 //========================================================================== 60 assign mem_cs_n = cs_n; //多次一举,直接连线mem_cs_n即可 61 assign tie_high = 1'b1; //拉高 62 assign tie_low = 1'b0; //拉低 63 //========================================================================== 64 //== DDR2 IP 65 //========================================================================== 66 DDR2 DDR2_inst 67 ( 68 .aux_full_rate_clk (mem_aux_full_rate_clk ), //全速率时钟 69 .aux_half_rate_clk (mem_aux_half_rate_clk ), //半速率时钟 70 .global_reset_n (global_reset_n ), //全局异步复位 71 //----------------------------------------------- 72 .local_address (mem_local_addr ), //用户_地址总线 73 .local_be (mem_local_be ), //用户_字节使能标志 74 .local_burstbegin (local_burstbegin_sig ), //用户_突发起始 75 .local_init_done ( ), //用户_初始化完成 76 .local_rdata (mem_local_rdata ), //用户_读数据总线 77 .local_rdata_valid (mem_local_rdata_valid ), //用户_读数据有效 78 .local_read_req (mem_local_read_req ), //用户_读数据请求 79 .local_ready (mem_local_ready ), //用户_读写请求被接收指示 80 .local_refresh_ack ( ), //用户_刷新请求 81 .local_size (mem_local_size ), //用户_突发大小 82 .local_wdata (mem_local_wdata ), //用户_写数据总线 83 .local_write_req (mem_local_write_req ), //用户_写数据请求 84 //----------------------------------------------- 85 .mem_addr (mem_addr[15 : 0] ), //DDR2地址总线 86 .mem_ba (mem_ba ), //DDR2组地址总线 87 .mem_cas_n (mem_cas_n ), //DDR2列地址选择 88 .mem_cke (mem_cke ), //DDR2时钟使能 89 .mem_clk (mem_clk ), //DDR2时钟 90 .mem_clk_n (mem_clk_n ), //DDR2时钟反相 91 .mem_cs_n (cs_n ), //DDR2片选 92 .mem_dm (mem_dm[1 : 0] ), //DDR2掩码 93 .mem_dq (mem_dq ), //DDR2数据总线 94 .mem_dqs (mem_dqs[1 : 0] ), //DDR2数据源同步 95 .mem_odt (mem_odt ), //DDR2片内终止控制 96 .mem_ras_n (mem_ras_n ), //DDR2行地址选择 97 .mem_we_n (mem_we_n ), //DDR2写使能 98 //----------------------------------------------- 99 .phy_clk (phy_clk ), //DDR2工作时钟 100 .pll_ref_clk (clock_source ), //IP核中的PLL输入时钟 101 .reset_phy_clk_n (reset_phy_clk_n ), //IP核提供的复位 102 .reset_request_n ( ), //IP核中的PLL锁定 103 .soft_reset_n (tie_high ) //全局异步复位信号(不复位PLL) 104 ); 105 //========================================================================== 106 //== 连接列地址位,因为2:1的数据速率,从示例驱动程序输出中删除1位 107 //========================================================================== 108 assign mem_local_addr[8 : 0] = mem_local_col_addr[9 : 1]; 109 //========================================================================== 110 //== DDR2 控制器 111 //========================================================================== 112 DDR2_example_driver driver 113 ( 114 .clk (phy_clk ), //DDR2工作时钟 115 .local_bank_addr (mem_local_addr[27 : 25]), //用户_地址总线 116 .local_be (mem_local_be ), //用户_字节使能标志 117 .local_burstbegin (local_burstbegin_sig ), //用户_突发起始 118 .local_col_addr (mem_local_col_addr ), //用户_地址总线 119 .local_cs_addr (mem_local_cs_addr ), //未用到 120 .local_rdata (mem_local_rdata ), //用户_读数据总线 121 .local_rdata_valid (mem_local_rdata_valid ), //用户_读数据有效 122 .local_read_req (mem_local_read_req ), //用户_读数据请求 123 .local_ready (mem_local_ready ), //用户_读写请求被接收指示 124 .local_row_addr (mem_local_addr[24 : 9] ), //用户_地址总线 125 .local_size (mem_local_size ), //用户_突发大小 126 .local_wdata (mem_local_wdata ), //用户_写数据总线 127 .local_write_req (mem_local_write_req ), //用户_写数据请求 128 .pnf_per_byte (pnf_per_byte[3 : 0] ), //数据正确指示(字节) 129 .pnf_persist (pnf ), //数据正确指示 130 .reset_n (reset_phy_clk_n ), //IP核提供的复位 131 .test_complete (test_complete ), //DDR2测试完成指示 132 .test_status (test_status ) //DDR2测试的运行状态 133 ); 134 135 136 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 执行仿真。
五、波形展现
经过一小段时间的等待,波形就出来了。
六、补充说明
如果使用的 Quartus II 是13.0 以上的版本,在执行仿真这一步会报错: “仿真需要调用一个叫 cycloneiii_ver 的库,结果找不到这个库,为啥找不到呢?可能这个 IP 核的工程模型是基于 cycloneiii 器件设计的,仿真的时候默认还是调用 cyclone iii 的器件模型,但是从 Quartus II 13.0 以后,Quartus II 软件已经不再支持 cyclone iii 软件,里面也不再提供 cyclone iii 的器件模型,因此仿真时候无法找到这个模型,当然无法开始仿真了。” ——小梅哥 小梅哥没有给出解决办法,但是看到一篇博客里记录了 QuartusII 15.0 仿真 DDR2 官方例程的全过程,链接如下:https://blog.csdn.net/linker00/article/details/98034776参考资料:小梅哥DDR2简明教程.pdf