Spartan6 FPGA 使用ISE自建DDR3写测试平台

Spartan6 FPGA DDR3自建写测试平台

FPGA


先简单总结一下前面建立FPGA DDR3 IP Core,以及简单简述一下DDR3 IP Core的信号。

1.DDR3 IP core建立的时候,如果网上没有搭建这个IP核的资料,我们要怎么搭建呢? 
 

image_1d0u2eqfflvm98ciqqjaa1j7l9.png-77.1kB


2.打开后得到这个文档,点击Click here,可以获得这个IP Core的搭建设置方法 
 

image_1d0u2ibp26du1o61bt91ckbc3r16.png-62.7kB


3.点击Click here后,得到User Guide手册 

image_1d0u2l7ie11u91h5ka911q2b18vn26.png-81.9kB


DDR3 IP Core各个信号,查看MCB这个文档

image_1d0u2pgfu1ogqmil1c15los5633.png-72.4kB


他的引脚信号分布 
 

image_1d0uj1u671vhms9kq5m1oaphoc4j.png-213.1kB


我们控制的就是右边的IOB信号。左边的信号有几个端口就要看你创建IP核的时候的Port设置了。 
 

image_1d0uja2tu1rsa1r1om3414vk1acm9.png-207.2kB

因为我之前创建IP核的时候是将他设置成了2个64bit的Port。所以这里是有P0和P1信号端口。 
 

image_1d0uj7g4g844i9irmr1nla13fo50.png-96.4kB


接口的分类

image_1d0ulbe1319kkhnp1gmide2mnl4n.png-56.1kB

 

image_1d0ulhrs0h4m1mfuu3g170o2f05h.png-56.8kB


command path 中比较有用的几个信号 
(c3_p0_cmd_clk),//cmd FIFO的用户时钟,上升沿有效 
(c3_p0_cmd_en), //该高电平有效信号是用于写入的写入使能信号 
(c3_p0_cmd_instr),//指令端口 
(c3_p0_cmd_bl),//突发长度,0-63 
(c3_p0_cmd_byte_addr),//字节开始的地址,他的后面几位不能为0 
(c3_p0_cmd_empty),//FIFO的空标志,高有效 
(c3_p0_cmd_full),//FIFO的满标志

由MCB的手册可知,这些信号都是Command path信号 
 

image_1d0ukecmrjvm1d5719hesv4160t16.png-127.8kB

 
那我们如何向DDR3写入command呢?打开目录,找到command path timing,找到他的时序图,根据手册时序图写程序就可以向DDR3写入指令。 
 

image_1d0ukg2ki1lbh1ura115h1td6n222j.png-22.9kB

 
 

image_1d0ukpiphv6us0qqc56ja1e813t.png-186.9kB


command path的读写时序。

写时序 

image_1d0ukv3q015eb31ekj1cr01j814a.png-179kB

注意:这里需要注意时钟,这里有好几个时钟,command path timing(write)时钟是cmd_clk,这个时钟主要是用来检测cmd_en信号的,而我们如果需要将instr指令端口信号写入DDR3,则使用的是Wr_clk

我们看看cmd_clk和wr_clk手册怎么说的吧

cmd_clk 
 

image_1d0ult0at1amp45h1g8e4o91m7q7u.png-167.6kB

 
wr_clk 

image_1d0uluc6i19h81lgimv01t94139b8b.png-151.7kB

write_path_timing 

image_1d0vrdcl11v60po318a1b254039.png-214kB

那cmd和wirte是怎么协调的呢?

image_1d1drj51d2ni6ji12vu1q6ogqjm.png-434.6kB


我们可以看到在command path timing 时序和write path timing都可以看到cmd和write都有调用到FIFO。cmd_FIFO存储的是命令指令,write data fifo里面存储是要写入到DDR3中的数据。那我们要往DDR3写入数据,那么DDR3先读入cmd_instr的数据。先知道是读操作还是写操作。得知是写操作后,会去访问Write Data FIFO里面的数据,将Write FIFO里面的数据写入DDR3 FIFO。读操作,就是讲Read FIFO里面的数据读出来。因此,在写命令的时候,我们需要确保DDR3的Wr_data_FIFO里面是有数据的。所以我们要将上面的步骤反过来,先向Write data fifo写入想要DDR3芯片内部数据,之后再向cmd fifo中写入相应的写指令。这样的话,我们的时序图可以这么写 
 

image_1d1dsfcof1iq71ipn1mfr1ihff4o13.png-3.9kB

 
cmd_en也就是去检测wr_en的下降沿。


代码的实现

image_1d1f8f3b0l7a1sbk1ld1mg9o53m.png-70kB


将顶层文件DDR3的command 引脚和 write引脚引出来。 
因为我们是仿真时序,所以我们新建一个wr_trig来控制wr_en。 
 

image_1d1f8hvjb8mcbj8qi21sec2013.png-7.9kB

 
仿真时序图

新建一个ddr3_drive文件。 
 

image_1d1f8kg4r12931u821c4ma7l1huq1g.png-66.5kB

 
上图是ddr3_drive的引脚信息。因为我们是仿真,所以我们将instr直接固定成3'b000写模式,将Bl,mask也固定。

ddr3_drive代码:

    module ddr3_drive(
//system signal
input                   s_clk                   ,       
input                   s_rst_n                 ,       
//DDR3 User Interface
output wire             p0_cmd_en               ,       
output wire [2:0]       p0_cmd_instr            ,       
output wire [5:0]       p0_cmd_bl               ,       
output wire [29:0]      p0_cmd_byte_addr        ,       
output reg              p0_wr_en                ,       
output wire [7:0]       p0_wr_mask              ,       
output reg [63:0]       p0_wr_data              ,       
//Debug
input                   wr_trig 
);


//========================================================================\
// =========== Define Parameter and Internal signals =========== 
//========================================================================/
reg                             p0_wr_en_r1                     ;      //用于捕获wr_en的下降沿 


//=============================================================================
//**************    Main Code   **************
//=============================================================================
always  @(posedge s_clk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
            p0_wr_en <= 'd0;
        else if(p0_wr_data >= 'd15)
            p0_wr_en <= 'd0;
        else if(wr_trig == 1'b1)
            p0_wr_en <= 1'b1;
end

always  @(posedge s_clk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
            p0_wr_data <= 'd0;
        else if(p0_wr_en == 1'b1)
            p0_wr_data <= p0_wr_data + 1'b1;
end


always  @(posedge s_clk) begin
       p0_wr_en_r1 <= p0_wr_en;
end

assign p0_cmd_en = ~p0_wr_en & p0_wr_en_r1;
assign p0_cmd_instr = 3'b000;//write
assign p0_cmd_bl = 'd15;
assign p0_cmd_byte_addr = 'd0;
assign p0_wr_mask = 8'b0;

    endmodule 

顶层文件将ddr3_drive文件例化进去。 
 

image_1d1f8qjbr1vet1o0a134gifg1khp1t.png-22.7kB

 
注意:这里给ddr3_drive的是ddr3例化文件给出的user clock和user reset。reset是高电平有效的,即高电平复位。所以给ddr3_drive传入的是~c3_rst0。需要去一次反。 

image_1d1f8tjlbm2keju1bjh1ql48732d.png-40kB

让ddr3_drive的信号与ddr3的例化文件信号一致。 
 

image_1d1f928kqtt11h233h1jhd1q9o2q.png-49.6kB

 
将cmd_clk和wr_clk都用c3_clk0传入。

在top文件中再声明一下即可。 

image_1d1f93qnaomuvge69nig1paf37.png-16.4kB


接下来就是写仿真的脚本文件了

主要就是去产生一个wr_trig信号 
代码如下

initial begin
    wr_trig = 0;
    @(posedge c3_calib_done)
    #10_0000;//10ns
    wr_trig = 1;
    #25600;
    wr_trig = 0;
end

因为ddr3的操作要在calib_done完成后(calib_done是指MCB初始化完成的信号)才能读写ddr3.所以top文件也需要将c3_calib_done引出,在tb文件中例化进来。 

image_1d1f99ako1h2f1u8h9ts1ds1qdm3k.png-46.3kB


接着因为我们需要查看ddr3_drive_inst,u_mig_39_2的信号,为了提高效率,我们通过tcl脚本添加。 
 

image_1d1f9dbrg68f4rc1pqctjd1s3n41.png-39kB

 
在工程文件中有fdo和udo两个文件。这就相当于我们平时自己写的do文件。fdo是ISE产生的,一般不改变,udo是ISE给用户添加的do文件。udo也就是user do的意思。 
 

image_1d1f9ior93lc16h0h1n1108h514e.png-32.9kB

 
将分组和添加信号的脚本卸载udo文件中。 
 

image_1d1f9mna67msrqj1lkk4561his4r.png-30.7kB

 
点击开始仿真。


波形的分析

image_1d1f9r04f1t8715q41ml086b1lav5o.png-114.7kB


我们要先找到wr_trig信号。因为真正的数据端口是ddr3_dq引脚,我们主要查看的是wr_trig拉高后的ddr3_dq引脚信号。看波形我们可以知道wr_trig引脚在34855200ps后拉高。我们就去看这个时间后的打印信息。 
 

image_1d1fa183f1ph9be63j683cuit78.png-117.1kB


注意:因为我们FPGA DDR3 IP是64bit port,而我们DDR3实际是16bit的port。所以我们这里是并不是0000,0001,0002这样下去。 
 

image_1d1fa5b051onk13tdi66144q164u7l.png-92.3kB

 
而是这样四个一组的。 
因为我们测试是0-15,所以最终到0x0f,看打印信息,可知仿真正确。 

image_1d1fa7ub015u1th1dar5t5u6a9k.png-78.8kB

欢迎关注微信公众号:文鸿开源工作室 

微信公众号图像.bmp-212.2kB

制作日志: /****************************2016-07-10 更新*********************************/ 经过不知道多少个工作日空余时间和周末业余时间,终于大致设计板子浮出水面,等待后续检查。 板子采用4层PCB,层叠情况:Top -> GND -> Power -> Bottom板子芯片情况: (1) FPGA: Xilinx Spartan6系列的XC6SLX16-FTG256 (2) DDR3: Micron的MT41J128M16,2Gbit存储容量 (2) 电源:采用2片Onsemi的NCP1529分别为FPGA Core 1.2V和DDR3 1.5V提供电源 /****************************2016-07-18 更新*********************************/ PCB打样回来了,5mil/5mil的线宽线距,10mil的过孔,花了我好多大洋!!!赶紧贴板子去了!! 贴完再上照。 /****************************2016-07-19 更新*********************************/ 搞了一个上午,终于搞定第一个板子,FPGA的1.2V VDDCore电压,1.5V的DDR3供电电压, VREF的0.75V电压都OK。往FPGA内部下载点灯程序OK,往SPI FLASH固化程序也OK。 下一步,DDR3 的MCB实现。 /****************************2016-07-23 更新*********************************/ 经测试,第一版的DDR3可以正常稳定运行在400MHz,全地址空间读数据无任何问题。 现在开始准备第二版,打算生成100个pcb,并且会将阻焊颜色由绿色改为黑色。 具体设计细节和第一版的区别如下: (1) IO引脚数量由原来的80个增加到86个IO; (2) 所有引出的差分线尽量保持等长; (3) 电容部分进行了改进,每个DC/DC输出都增加了铝电解电容,增加可靠性,铝电解电容都放在背面。 下面是第二版的图片: /****************************2016-07-26 完成*********************************/ 2Gbit全地址空间测试完成,用的是Xilinx提供的MCB IP硬核,附件是原理图和说明书,欢迎下载。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值