Spartan6 FPGA 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

猜你喜欢

转载自blog.csdn.net/qq_38376586/article/details/86534646
今日推荐