自学内容网 自学内容网

DDR3保姆级使用教程:ZYNQ 7010

内容:使用DDR3 IP核,向DDR3写入数据,然后再读出数据,通过串口打印。

设备:ZYNQ 7010 xc7z010clg-400-1。软件VIVADO 2018.3

(1)工程模块:一个写FIFO,一个读FIFO。一个ZYNQ IP核(DDR3在ZYNQ IP核内勾选)。一个AXI4通信接口转换模块。

(2)工程的运行逻辑简介:

AXI4的接口是64位,也就是,两个FIFO(读FIFO、写FIFO)与ZYNQ 的接口位宽是64位。我们进入和出来DDR3的数据都是64位的。

整个工程分为两个流程:

  1. 突发写DDR3(设置一次突发写的长度是16个64位的数据)
    顶层文件内,持续向写FIFO写入数据,写FIFO的读计数会累积增加。每次写入FIFO的数据位宽是16位,当我们写入64个16位的数据,读计数值(rd_cnt)就会等于16(代表16个64位的数据排列在FIFO里)。
    当rd_cnt = 16 就代表满足一次突发写DDR3的设置长度“16”,那就会自动执行一次DDR3的突发写,数据就进入到DDR3里了(每次向DDR3里写1个64位数,重复了16次)。

  2. 突发读DDR3(设置一次突发读的长度是16个64位的数据)
    我们要把之前写到DDR3里的数据读出来放到读FIFO里。DDR3的读操作受限于读FIFO的写计数值(wr_cnt)。wr_cnt在读FIFO被写入数据后会自动增加,增加1就代表DDR3给FIFO写了1个64位的数据。在读FIFO复位后,wr_cnt会清零,如果wr_cnt小于等于我们设定的16,DDR就自动往读FIFO搬运数值,一直到wr_cnt等于16了,DDR3就不会往FIFO里搬运数据。(当wr_cnt等于16就等于DDR3给了读FIFO16个64位的数据,代表一次突发读完成)

关于两个过程的一些问题:

在这里插入图片描述
红色箭头代表写流程,绿色箭头代表读流程
其中有很多的问题,比如时钟,信号线等等。看看我们怎么代码实现对DDR3的数据写入和读取吧。

**代码块一:**给写FIFO写入数据,这些数据会自动存入到DDR3中
/***********************方式2*  直接使能计数****************************/
reg [15:0] dat2;
wire wrfifo_data_en = dat2 ==1024;   //代表输入1024个16位数  rd = 256 *64
  always@(posedge loc_clk100m or posedge cnt_en_reg2)
  begin
    if(cnt_en_reg2)
      dat2 <= 16'd0;
    else if(!wrfifo_data_en && !cnt_en_reg2)
      dat2 <= dat2 + 1'b1;
    else
      dat2 <= dat2;
  end

  assign wrfifo_clr  = reset;
  assign wrfifo_wren =!wrfifo_data_en & !cnt_en_reg2; 
  assign wrfifo_din  = dat2;

assign wrfifo_wren 代表允许写FIFO写入数据的使能信号
assign wrfifo_din 代表给写FIFO写入的数据,在使能信号为1的情况下有效

**代码块二:**读FIFO的读出数据。DDR3会自动把数据给读FIFO,我们要把读FIFO的数据读出
/***********************方式1*   标志位cnt读计数****************************/
wire rdfifo_empty;
wire rdfifo_rd_rst_busy;
wire rd_en;
wire rdfifo_en_reg2;
reg [19:0] rdfifo_en_reg;
reg rd_clr;
assign rdfifo_en_reg2 = rdfifo_en_reg[19];
// //延迟20个时钟周期
  always@(posedge loc_clk100m or negedge wrfifo_data_en)
  begin
    if(!wrfifo_data_en)
      rdfifo_en_reg <= {20{1'b1}};
    else
      rdfifo_en_reg <= rdfifo_en_reg << 1;
  end
  
assign rd_en = rdfifo_empty | rdfifo_rd_rst_busy;
reg [15:0] rd_dat;
reg rd_send_en;
wire rdfifo_data_en = rd_dat ==1024;   //代表输入1024个16位数  
    reg [31:0] rd_cnt;
    always @ (posedge loc_clk100m or posedge rdfifo_en_reg2)
        if(rdfifo_en_reg2)begin
            rd_dat <= 0;
            rd_cnt <= 0;
            rd_send_en <=  0;
        end
        else  if(!rdfifo_data_en  &&  !rd_en)begin
            if(rd_cnt == 32'd9_999_999)   begin//   d199_999
                rd_dat <= rd_dat +1'b1;
                rd_send_en <= 1;
                rd_cnt <= 0;
        end
            else begin
                rd_cnt <= rd_cnt + 1'b1;
                rd_dat <= rd_dat;
                rd_send_en <=  0;
            end
        end
        else begin
            rd_cnt <= 0 ;
            rd_send_en <=  0;
            rd_dat <= rd_dat;
        end

reg rd_send_en_reg;
    always @ (posedge loc_clk100m or posedge rdfifo_en_reg2)
        if(rdfifo_en_reg2)
            rd_send_en_reg <=  0;
        else  if(rd_send_en)
                rd_send_en_reg <= 1;
        else 
            rd_send_en_reg <=  0;

  assign rdfifo_rden =rd_send_en;
  assign rdfifo_clr  = reset   | rd_clr;

这里的rd_send_en 是读FIFO的读使能信号,每隔“d9_999_999”也就是9_999_999 + 1 = 10_000_000 * 10ns = 100 ms读一次"读FIFO"的数据,因为读使能和读数据有一个周期的延迟,因此rd_send_en_reg 出现了,他被用来给uart模块块用来发送读FIFO的数据,来验证我们整个流程是否正确。

**代码块三:**串口打印
  uart tx  /
uart_byte_tx uart_byte_tx(
.clk(loc_clk100m),
.reset(reset),

.data_byte(rdfifo_dout),
.send_en(rd_send_en_reg),
.baud_set(3'd0),

.uart_tx(uart_tx),
.tx_done( ),
.uart_state()
);

rdfifo_dout就是读FIFO的数据口了,因为rdfifo_dout是16位的,我们这样使用会直接输出低八位的数据。

在代码块一,我们看到
/***********************方式2*  直接使能计数****************************/
reg [15:0] dat2;
wire wrfifo_data_en = dat2 ==1024;   //代表输入1024个16位数  rd = 256 *64
  always@(posedge loc_clk100m or posedge cnt_en_reg2)
  begin
    if(cnt_en_reg2)
      dat2 <= 16'd0;
    else if(!wrfifo_data_en && !cnt_en_reg2)
      dat2 <= dat2 + 1'b1;
    else
      dat2 <= dat2;
  end

  assign wrfifo_clr  = reset;
  assign wrfifo_wren =!wrfifo_data_en & !cnt_en_reg2; 
  assign wrfifo_din  = dat2;

assign wrfifo_din = dat2;
dat2作为一直给FIFO,也就是一直给DDR3的数据是从0~1024开始变化的。所以串口打印的数据只有八位8h00到8hFF,一共256个数据。那么根据1024个数据挨个打印出来,在低八位上显示的效果就是4次循环打印输出00到FF
在这里插入图片描述

到这里整个流程就结束了,由于这个DDR3的工程涉及到PS与PL侧的资源,导致会有很多出BUG的地方。因此,整个工程代码难度比较高,我还是建议使用一个基础版的工程文件来学习整个DDR3的运行流程,接下来我会上传我自己编写的基础版的代码供大家学习参考


原文地址:https://blog.csdn.net/Ievn_Hare/article/details/144162052

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!