FPGA-ZCU106-PL侧读写ddr4(全网唯一)

1、由于一直在PL侧做算法,外设接口接触的比较少,目前只做了sfp的UDP传输,但是由于课题的原因需要将一部分PL计算数据存储,而RAM存储空间比较小,因此本次给大带来了ZCU106的PL侧读写ddr4的教程,本教程是全网ZCU106 DDR4 PL侧读写的唯一一篇教程。下面是4个参考资料:

①:ZCU106开发之PL侧DDR4_lixiaolin126的博客-CSDN博客_zcu106开发板ddr4感谢大家漫长的等待!!我们团队从2017底拿到ZCU106后就一直在进行相关研发,由于手头上的活比较多就把ZCU106开发详解的发布给延迟了。现在我们将ZCU106开发过程中遇到的问题和解决办法跟大家进行分享。大家如若对博客中内容有疑问,请加我们的QQ群836535064进行交流。希望跟大家一起玩好这一个牛逼的芯片。版权声明:欢迎技术交流和帮助,提供IT相关服务,索要源码请联系博主QQ,若该文...https://blog.csdn.net/lixiaolin126/article/details/83958665?spm=1001.2014.3001.5501这个是在PS侧对PL侧通过bram进行的读写,操作难度较高。

②:FPGA学习之路-zynq7000-PS侧读写ddr_发光的沙子的博客-CSDN博客_ps读写ddr1、由于ZCU106板子的sdk调试一直没有成功,因此还是基于ZYNQ7000板子写了一个ps侧读写内存的内容2、实验软硬件:Vivado 2019.1、Xilinx sdk 2019.1、ZYBO板子(xc7z010clg400-1 )3、实验过程:step1:在VIvado中建立工程:miz701_sys,芯片选择xc7z010clg400-1step2:在IP INERGATOR中点击创建,然后搜索ZYNQ双击。 然后点击Diagram的自动运行模块,弹出的窗口点击OK。.https://blog.csdn.net/qq_37912811/article/details/121921754?spm=1001.2014.3001.5502这个是本人的z7的PS侧操作ddr3的教程

③:FPGA----PL侧对ram的读写_发光的沙子的博客-CSDN博客_fpga ram读写1、例化模块的输出端口只能是wire型,而不能是reg型,也就是不能更改输出。2、input\output都需要有位宽表示。3、assign变量也是wire型。4、①实验任务:将32位宽的22个数放入PL侧的ddr,并且读取(本实验可以为数组在模块间的传递提供解决方案【数组在Verilog中是不可以作为端口传递的,但是在SystemVerilog中可以】)②实验软硬件:Vivado 2019.1、ZCU106③实验过程:step1:建立test_pl_ddr项目,过程不再赘述sthttps://blog.csdn.net/qq_37912811/article/details/122204870?spm=1001.2014.3001.5502这个是本人一直被说诟病的ZCU106的ram读写

2、本教程采用的是xilinx的ddr4的IP核的AXI4接口开发的,因此需要先了解AXI4总线协议。

①AXI4接口描述

AXI4 接口描述
通道 信号 主/从 信号描述
全局
信号
aclk 主机 AXI4总线时钟
aresetn 主机 AXI4总线复位
写通
道地
址与
控制
信号
通道
M_AXI_WR_awid 主机 写地址ID,用来表示哪个主机写入的数据,一般是一台主机对应一个从机,因此这个可以随便写。
M_AXI_WR_awaddr 主机 写地址,给出一次写突发传输的写地址
M_AXI_WR_awlen 主机 突发长度,给出突发传输的次数
M_AXI_WR_awsize 主机 突发大小,给出每次突发传输的字节数
M_AXI_WR_awburst 主机 突发类型
M_AXI_WR_awlock 主机 总线锁信号,可提供操作的原子性
M_AXI_WR_awcache 主机 内存类型,表明一次传输是怎样通过系统
M_AXI_WR_awprot 主机 保护类型,表明一次传输的特权级及安全
等级
M_AXI_WR_awqos 主机 质量服务QoS
M_AXI_WR_awvalid 主机 有效信号,表明此通道的地址控制信号有
M_AXI_WR_awready 从机 表明“从”可以接收地址和对应的控制信
写通
道数
据通
M_AXI_WR_wdata 主机 写入的数据
M_AXI_WR_wstrb 主机 写数据有效的字节线,用来表明哪8bits 数据是有效的(例如32位的数据[4个8bit数据],他的wstrb一般是4位宽,一般设置为F,表示全数据位有效)
M_AXI_WR_wlast 主机 表明此次传输是最后一个突发传输
M_AXI_WR_wvalid 主机 写有效,表明此次写有效
M_AXI_WR_wready 从机 表明从机可以接收写数据
写通
道响
应通
M_AXI_WR_bid 从机 写响应ID
M_AXI_WR_bresp 从机 写响应,表明写传输的状态
M_AXI_WR_bvalid 从机 写响应有效
M_AXI_WR_bready 主机 表明主机能够接收写响应
读通
道地
址与
控制
信号
通道
M_AXI_RD_arid 主机 读地址ID,与M_AXI_WR_awid类似
M_AXI_RD_araddr 主机 读地址,给出一次写突发传输的读地址
M_AXI_RD_arlen 主机 突发长度,给出突发传输的次数
M_AXI_RD_arsize 主机 突发大小,给出每次突发传输的字节数
M_AXI_RD_arburst 主机 突发类型
M_AXI_RD_arlock 主机 总线锁信号,可提供操作的原子性
M_AXI_RD_arcache 主机 内存类型,表明一次传输是怎样通过系统
M_AXI_RD_arprot 主机 保护类型,表明一次传输的特权级及安全
等级
M_AXI_RD_arqos 主机 质量服务QOS
M_AXI_RD_arvalid 主机 有效信号,表明此通道的地址控制信号有
M_AXI_RD_arready 从机 表明“从”可以接收地址和对应的控制信
读通
道数
据通
M_AXI_RD_rid 从机 读IDtag
M_AXI_RD_rdata 从机 读数据
M_AXI_RD_rresp 从机 读响应,表明读传输的状态
M_AXI_RD_rlast 从机 表明读突发的最后一次传输
M_AXI_RD_rvalid 从机 表明此通道信号有效
M_AXI_RD_rready 主机 表明主机能够接收读数据和响应信息

②写数据:

写空闲:等待触发突发信号。
写通道写地址等待:准备好写地址AWADDR,然后拉高AWVALID。
写通道写地址:从机接受到AWVALID,发出AWREADY。
写数据等待:准备好数据WDATA,拉高WVALID。

写数据循环: 从机接受WVALID, 确认数据WDATA 有效并且接受, 发出WREADY,AXI 是突发传输:循环该操作到接受到WLAST 最后一个数据标志位。
接受写应答:接受到从机发出的BVALID,主机发出BREADY。
写结束:拉低未拉低的信号,进入写空闲。

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第1张图片 AXI4 写数据时序图

 ③读数据:

读空闲:等待触发突发信号。
读通道写地址等待:准备好写地址ARADDR,然后拉高ARVALID。
读通道写地址:从机接受到ARVALID,发出ARREADY。
读数据等待:从机器准备好数据WDATA,从机拉高RVALID。
读数据循环:主机接受RVALID,确认数据RDATA 有效并且接受,发出RREADY 给从机,AXI 是突发传输:循环该操作到接受到RLAST 最后一个数据标志位。
读结束:拉低未拉低的信号,进入读空闲。

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第2张图片 AXI4 读数据时序图

 

④总结:X代表W/R

空闲:等待突发读写信号

写地址等待:主机发出AXADDR,并且AXVALID置1

从机地址握手:从机发出AXREADY

写/读数据等待:主机发出WDATA,并且WVALID置1。/从机发出RDATA,并且RVALID置1。

主/从机数据循环:从机发出WREADY/主机发出RREADY,循环该操作到接受到XLAST 最后一个数据标志位。

接受写应答(写数据才有):接受到从机发出的BVALID,主机发出BREADY。

注:任何时候都是求偶方放出VALID信号,被求偶方放出READY信号

3、实验过程

①试验任务:完成ZCU106板卡PL侧对的ddr4的读写操作

②实验软硬件:ZCU106、Vivado 2019.1

③实验过程:

step1:建立项目:ddr4_test

step2:设置IP核

ddr4 IP:

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第3张图片

 FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第4张图片

 FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第5张图片

 FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第6张图片

 FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第7张图片

 fifo设置:FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第8张图片

 wr_fifo:

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第9张图片

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第10张图片 FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第11张图片

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第12张图片

 rd_fifo:除了下面两个设置都一样

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第13张图片

 FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第14张图片

 ③项目结构:

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第15张图片

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第16张图片

 ④代码部分:只给出用户生成数据、顶层、xdc文件,剩余文件请联系作者。

`timescale 1ns / 1ps
//
// Company: 东北电力大学
// Engineer: Yang Zheng
//
// Create Date: 2022/07/21 12:46:13
// Design Name:
// Module Name: ddr_data
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//


module ddr_data(input sys_clk,        //系统时钟
                input sys_rst_n,      //系统复位,低电平有效
                output reg wr_en,          //写使能
                output reg [127:0] wr_data, //写数据
                output reg rd_en,          //读使能
                input [127:0] rd_data, //读数据
                input rd_fifo_valid,//读出数据时为高电平,由于ddr是512位的,因此我们需要计数4次,即每次读回4条数据
                output read_enable);  //写完成,开始读
    //********************************************************************//
    //****************** Parameter and Internal Signal *******************//
    //********************************************************************//
    
    //parameter define
    parameter COUTER_MAX = 30'd200;//0.2s
    
    //reg define
    
    reg [29:0] count; //写入计数器
    reg wr_flag; //写标志
    //记录数据
    reg [127:0] rd_data_r0 = 128'b0;
    reg [127:0] rd_data_r1 = 128'b0;
    reg [127:0] rd_data_r2 = 128'b0;
    reg [127:0] rd_data_r3 = 128'b0;
    //********************************************************************//
    //***************************** Main Code ****************************//
    //********************************************************************//
    
    assign read_enable = wr_flag;

    reg [2:0] rd_data_count = 3'b0;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (sys_rst_n == 1'b0) begin
            rd_data_count <= 1'b0;
            rd_data_r0 <= 128'b0;
            rd_data_r1 <= 128'b0;
            rd_data_r2 <= 128'b0;
            rd_data_r3 <= 128'b0;
        end
        else if (rd_fifo_valid && rd_data_count == 'd0) begin
            rd_data_r0 <= rd_data;
            rd_data_count <= rd_data_count + 1'b1;
        end
        else if (rd_fifo_valid && rd_data_count == 'd1) begin
            rd_data_r1 <= rd_data;
            rd_data_count <= rd_data_count + 1'b1;
        end
        else if (rd_fifo_valid && rd_data_count == 'd2) begin
            rd_data_r2 <= rd_data;
            rd_data_count <= rd_data_count + 1'b1;
        end
        else if (rd_fifo_valid && rd_data_count == 'd3) begin
            rd_data_r3 <= rd_data;
            rd_data_count <= 1'b0;
        end
    end
    //0.2s 计数器
    always@(posedge sys_clk or negedge sys_rst_n) begin
        if (sys_rst_n == 1'b0) begin
            count <= 30'd0;
        end
        else begin
            if (~rd_en) begin
                count <= count+1;
                if (count == COUTER_MAX) begin
                    count <= 30'd0;
                end
            end
        end
    end
    
    //生成1 到200 个数据并且写入ddr 中
    //这里的 wr_data是输入到ddr的数据,wr_en是使能端口
    //这里的 wr_flag是可以ddr存有数据,因此可读标志
    always@(posedge sys_clk or negedge sys_rst_n) begin
        if (sys_rst_n == 1'b0) begin
            wr_data <= 127'd0;
            wr_en   <= 1'd0;
            wr_flag <= 1'b0;
        end
        else if (wr_flag == 1'b0) begin//写过程
            wr_data <= {32'h00000001 + count,32'h00000002 + count,
                        32'h00000003 + count,32'h00000004 + count};
            wr_en   <= 1'd1;
            if (count == 'd200) begin
                //wr_data <= 127'd0;
                wr_flag <= 1'b1;
            end
        end
        else begin
            wr_data <= wr_data;
            wr_en   <= 1'd0;
            wr_flag <= wr_flag;//写完成
        end
        
    end
    //根据写完成拉高读使能数据
    //这里的rd_en可以控制是否读取ddr,因此每次读取给出一个周期的高电平即可
    //一直读取
    always@(posedge sys_clk or negedge sys_rst_n) begin
        if (sys_rst_n == 1'b0) begin
            rd_en <= 1'd0;
        end
        //0.2s 读取一次数据
        else if (count == COUTER_MAX - 1) begin//(wr_flag == 1'b1)&&(count == COUTER_MAX)
            rd_en <= 1'd1;
        end
    
    end
    //每次读取4个
    /*reg [1:0] COUNT_FLAG = 2'b0;
    always@(posedge sys_clk or negedge sys_rst_n) begin
        if (sys_rst_n == 1'b0) begin
            rd_en <= 1'd0;
        end
        //0.2s 读取一次数据
        else if (wr_flag && COUNT_FLAG == 2'd0) begin//(wr_flag == 1'b1)&&(count == COUTER_MAX)
            rd_en <= 1'd1;
            COUNT_FLAG <= 2'd1;
        end
        else begin
            rd_en <= 1'd0;
        end

        if (COUNT_FLAG) begin
            COUNT_FLAG <= 2'd2;
        end
    end*/
endmodule

 

`timescale 1ns / 1ps
//
// Company: 东北电力大学
// Engineer: Yang Zheng
//
// Create Date: 2022/07/21 12:28:54
// Design Name:
// Module Name: ddr
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//


module ddr(input sys_clk_p,           //系统时钟
           input sys_clk_n,           //系统时钟
           input sys_rst,           //系统复位
           output [16:0] c0_ddr4_adr,
           output [1:0] c0_ddr4_ba,
           output [0:0] c0_ddr4_cke,
           output [0:0] c0_ddr4_cs_n,
           inout [7:0] c0_ddr4_dm_dbi_n,
           inout [63:0] c0_ddr4_dq,
           inout [7:0] c0_ddr4_dqs_c,
           inout [7:0] c0_ddr4_dqs_t,
           output [0:0] c0_ddr4_odt,
           output [0:0] c0_ddr4_bg,
           output c0_ddr4_reset_n,
           output c0_ddr4_act_n,
           output [0:0] c0_ddr4_ck_c,
           output [0:0] c0_ddr4_ck_t,

           output c0_init_calib_complete,
           output use_clk100m
           );
reg   pingpang                             = 0 ;
reg   [30:0]  wr_b_addr                    = 0 ;
reg   [30:0]  wr_e_addr                    = 200*16 ;
wire   data_wren;
wire   [127:0]  data_wr;
reg   wr_rst                               = 0 ;
reg   [30:0]  rd_b_addr                    = 0 ;
reg   [30:0]  rd_e_addr                    = 200*16 ;
reg   user_rd_clk                          = 0 ;
wire   data_rden;
reg   rd_rst                               = 0 ;
wire   read_enable;

// axi_ddr_top Outputs
wire  [127:0]  data_rd                     ;
wire  ui_clk                               ;
wire  ui_rst                               ;
wire rd_fifo_empty                         ;

axi_ddr_top #(
    .DDR_WR_LEN ( 1 ),
    .DDR_RD_LEN ( 1 ))
 u_axi_ddr_top (
    .sys_rst                 ( sys_rst                         ),//低电平复位
    .c0_sys_clk_p            ( sys_clk_p                       ),
    .c0_sys_clk_n            ( sys_clk_n                       ),
    .pingpang                ( pingpang                        ),
    .wr_b_addr               ( wr_b_addr               [30:0]  ),
    .wr_e_addr               ( wr_e_addr               [30:0]  ),
    .user_wr_clk             ( use_clk100m                     ),
    .data_wren               ( data_wren                       ),
    .data_wr                 ( data_wr                 [127:0] ),
    .wr_rst                  ( wr_rst                          ),
    .rd_b_addr               ( rd_b_addr               [30:0]  ),
    .rd_e_addr               ( rd_e_addr               [30:0]  ),
    .user_rd_clk             ( use_clk100m                     ),
    .data_rden               ( data_rden                       ),
    .rd_rst                  ( rd_rst                          ),
    .read_enable             ( read_enable                     ),
    .rd_fifo_valid           (rd_fifo_valid                    ),

    .c0_ddr4_adr             ( c0_ddr4_adr             [16:0]  ),
    .c0_ddr4_ba              ( c0_ddr4_ba              [1:0]   ),
    .c0_ddr4_cke             ( c0_ddr4_cke             [0:0]   ),
    .c0_ddr4_cs_n            ( c0_ddr4_cs_n            [0:0]   ),
    .c0_ddr4_odt             ( c0_ddr4_odt             [0:0]   ),
    .c0_ddr4_bg              ( c0_ddr4_bg              [0:0]   ),
    .c0_ddr4_reset_n         ( c0_ddr4_reset_n                 ),
    .c0_ddr4_act_n           ( c0_ddr4_act_n                   ),
    .c0_ddr4_ck_c            ( c0_ddr4_ck_c            [0:0]   ),
    .c0_ddr4_ck_t            ( c0_ddr4_ck_t            [0:0]   ),
    .data_rd                 ( data_rd                 [127:0] ),
    .ui_clk                  ( ui_clk                          ),
    .ui_rst                  ( ui_rst                          ),

    .use_clk1                ( use_clk100m                     ),
    .c0_init_calib_complete  ( c0_init_calib_complete          ),

    .c0_ddr4_dm_dbi_n        ( c0_ddr4_dm_dbi_n        [7:0]   ),
    .c0_ddr4_dq              ( c0_ddr4_dq              [63:0]  ),
    .c0_ddr4_dqs_c           ( c0_ddr4_dqs_c           [7:0]   ),
    .c0_ddr4_dqs_t           ( c0_ddr4_dqs_t           [7:0]   )
);


ddr_data u_ddr_data (
    .sys_clk                 ( use_clk100m                      ),
    .sys_rst_n               ( c0_init_calib_complete           ),
    .rd_data                 ( data_rd                  [127:0] ),
    .rd_fifo_valid           (rd_fifo_valid                     ),

    .wr_en                   ( data_wren                        ),
    .wr_data                 ( data_wr                  [127:0] ),
    .rd_en                   ( data_rden                        ),
    .read_enable             ( read_enable                      )
);
endmodule
set_property IOSTANDARD LVCMOS12 [get_ports sys_rst]
set_property PACKAGE_PIN AK12 [get_ports sys_rst]

set_property PACKAGE_PIN AL11 [get_ports c0_init_calib_complete]
set_property PACKAGE_PIN AK13 [get_ports use_clk100m]
set_property IOSTANDARD LVCMOS12 [get_ports c0_init_calib_complete]
set_property IOSTANDARD LVCMOS12 [get_ports use_clk100m]
set_property C_CLK_INPUT_FREQ_HZ 300000000 [get_debug_cores dbg_hub]
set_property C_ENABLE_CLK_DIVIDER false [get_debug_cores dbg_hub]
set_property C_USER_SCAN_CHAIN 1 [get_debug_cores dbg_hub]
connect_debug_port dbg_hub/clk [get_nets clk]

⑤仿真:ddr4仿真一般必须上板调试,而xilinx太牛了,直接用sverilog写了一个ddr4的模型应该。因此本人直接采用了xilinx自带的ddr4仿真文件进行的仿真,方便快捷。

FPGA-ZCU106-PL侧读写ddr4(全网唯一)_第17张图片

 

 

 可以看到用户侧的rd_data_rx已经收到了之前存入ddr4中数据。

4、DDR4 MIG的BUG

 我们可以看到00地址记录01020304,40地址记录05060708,80地址记录09abc ,而读出数据是40地址读取的00,80地址才开始读取05060708。也就是读取时,00地址正常读取,40地址读到0数据,80地址以后对应的才是写入时的40地址的数据,这个原因我试了一天我也不清楚为啥,我反正觉得我代码没问题,哈哈哈。

你可能感兴趣的:(Verilog,fpga开发)