参考正点原子视频及例程
模块:顶层、MigIP核、时钟IP核、ddr3读写模块
一、MIG核
1.1mig核介绍
具体了解参考快速上手Xilinx DDR3 IP核(1)----MIG IP核的介绍及配置(Native接口)_migip核_孤独的单刀的博客-CSDN博客
MIG IP 核对外分出了两组接口。左侧是用户接口,就是 用户(FPGA)同 MIG 交互的接口,用户只有充分掌握了这些接口才能操作 MIG。右侧为 DDR 物理芯片 接口,负责产生具体的操作时序,并直接操作芯片管脚。这一侧用户只负责分配正确的管脚,其他不用关心。
1.2具体引脚讲解参考Xilinx FPGA平台DDR3设计保姆式教程(3)MIG IP核使用教程及DDR读写时序_ddr3读写时序图_子墨祭的博客-CSDN博客
DDR写数据信号归类
①前提条件
app_rdy
app_wdf_rdy
app_en
②地址和命令
app_cmd
app_addr
③写数据
app_wdf_wren
app_wdf_data
app_wdf_end
app_wdf_mask :一般不用,直接置0
想要写数据到DDR?必须在①前提条件全部为高时,给出②地址和命令(app_cmd = 3’b000),然后给出③写数据的信号,就成功写入数据到DDR了;
注意:①②时序严格对齐!③相对①②可以提前1拍,或最多延迟2拍,但是最好跟①②对齐,不容易出错。(结合后文时序图更容易理解)
1.3需要注意DDR3实验涉及多个不同时钟。(重要)参考(很详细)Xilinx FPGA平台DDR3设计保姆式教程(2)DDR3各时钟频率及带宽分析_ddr3时钟频率_子墨祭的博客-CSDN博客
DDR时钟400M
图上有个4:1,说明MIG 输出到app接口上的时钟ui_clk = 800M/4=200M ,即到时我们在写RTL逻辑代码时操作MIG核时,用的就是这个200M时钟;
input clock period 对应的时钟就是MIG核的系统时钟,由PLL/MMCM输入;对应到例化代码就是:
.sys_clk_i (sys_clk_i ), //系统时钟输入
我们配置MIG核时选择多少M时钟,那么这里就要输入多少M
二、DDR3读写模块
正点原子官方例程中,状态机描述采用了一段式代码,不建议这样使用。
改为三段式状态机,代码如下
//3段式状态机
always@(posedge ui_clk or negedge rst_n)
if(!rst_n) current_state<=S0_IDLE;
else current_state<=next_state;
always@(*)begin
case(current_state)
S0_IDLE:
if(init_calib_complete)//初始化完成
next_state<=S1_WRITE;
else
next_state<=S0_IDLE;
S1_WRITE:
if(wr_addr_cnt == TEST_LENGTH - 1 )
next_state<=S2_WAIT;
else
next_state<=S1_WRITE;
S2_WAIT:
next_state<=S3_READ;
S3_READ:
if(rd_addr_cnt == TEST_LENGTH - 1 && app_rdy)
next_state<=S0_IDLE;
else
next_state<=S3_READ;
default:
next_state<=S0_IDLE;
endcase
end
always@(posedge ui_clk or negedge rst_n)begin
if((!rst_n)||(error_flag)) begin
app_wdf_data <= 128'd0;
wr_addr_cnt <= 24'd0;
rd_addr_cnt <= 24'd0;
app_addr <= 28'd0;
end
else case(current_state)
S0_IDLE:begin
app_wdf_data <= 256'd0;
wr_addr_cnt <= 24'd0;
rd_addr_cnt <= 24'd0;
app_addr <= 28'd0;
end
S1_WRITE:begin
if(app_rdy && app_wdf_rdy)begin //写条件满足
app_wdf_data <= app_wdf_data + 1; //写数据自加
wr_addr_cnt <= wr_addr_cnt + 1; //写地址自加
app_addr <= app_addr + 8; //DDR3 地址加8
end
else begin //写条件不满足,保持当前值
app_wdf_data <= app_wdf_data;
wr_addr_cnt <= wr_addr_cnt;
app_addr <= app_addr;
end
end
S2_WAIT:begin
rd_addr_cnt <= 24'd0; //读地址复位
app_addr <= 28'd0; //DDR3读从地址0开始
end
S3_READ:begin
if(app_rdy)begin //若MIG已经准备好,则开始读
rd_addr_cnt <= rd_addr_cnt + 1'd1; //用户地址每次加一
app_addr <= app_addr + 8; //DDR3地址加8
end
else begin //若MIG没准备好,则保持原值
rd_addr_cnt <= rd_addr_cnt;
app_addr <= app_addr;
end
end
default:begin
app_wdf_data <= 256'd0;
wr_addr_cnt <= 24'd0;
rd_addr_cnt <= 24'd0;
app_addr <= 28'd0;
end
endcase
end
遇到的问题及学习难点:
IP核和ddr3引脚信号太多,刚接触很折磨。
IP核有自己的例化模板,不用自己写。路径:IP SOURCE-IP核-ip_name.veo
不清楚各信号位宽(这点现在还是一知半解)
按照视频完成的工程生成比特流不成功,报错如下图。在xdc文件中添加
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets sys_clk]
[Place 30-575] Sub-optimal placement for a clock-capable IO pin and MMCM pair. If this sub optimal condition is acceptable for this design, you may use the CLOCK_DEDICATED_ROUTE constraint in the .xdc file to demote this message to a WARNING. However, the use of this override is highly discouraged. These examples can be used directly in the .xdc file to override this clock rule.
< set_property CLOCK_DEDICATED_ROUTE BACKBONE [get_nets clk_wiz_0_u/inst/clk_in1_clk_wiz_0] >
clk_wiz_0_u/inst/clkin1_ibufg (IBUF.O) is locked to IOB_X1Y74
clk_wiz_0_u/inst/mmcm_adv_inst (MMCME2_ADV.CLKIN1) is provisionally placed by clockplacer on MMCME2_ADV_X1Y2
The above error could possibly be related to other connected instances. Following is a list of
all the related clock rules and their respective instances.
Clock Rule: rule_mmcm_bufg
Status: PASS
Rule Description: An MMCM driving a BUFG must be placed on the same half side (top/bottom) of the device
clk_wiz_0_u/inst/mmcm_adv_inst (MMCME2_ADV.CLKFBOUT) is provisionally placed by clockplacer on MMCME2_ADV_X1Y2
and clk_wiz_0_u/inst/clkf_buf (BUFG.I) is provisionally placed by clockplacer on BUFGCTRL_X0Y31
实验仅仅通过LED灯亮灭验证成功与否,不够直观,最好编写testbench观察具体数据波形。