Zynq中PL读写PS端DDR数据

前情回顾node

(1)ZYNQ中PS端MIO操做
(2)ZYNQ中PS端MIO中断
(3)ZYNQ中PS端UART通讯
(4)ZYNQ中PS端XADC读取
1.   读写DDR底层结构

zynq 7000 SOCHP口是High-Performance Ports的缩写,以下图所示,一共有4HP接口,HP接口是AXI Slave设备,咱们能够经过这4HP接口实现高带宽的数据交互。实现PL读写PS端挂载的DDR须要使用HP接口。微信

      以下图所示,选择HP0 interface。url

使用的时钟是 150Mhz HP 的带宽是 150Mhz * 64bit ,对于视频处理, ADC 数据采集等应用都有足够的带宽。以下图所示,配置完 HP 端口之后, zynq 会多出一个 AXI Slave 端口,名称为 S_AXI_HP0 ,不过这些端口都是 AXI3 标准的,咱们经常使用的是 AXI4 协议,这里添加 1 AXI Interconnect IP ,用于协议转换( AXI3<->AXI4 )。设置 S00_AXI 端口为 AXI4 协议。

2.  PLAXI   MASTER的机制

  AXI4 所采用的是一种 READY VALID 握手通讯机制,即主从模块进行数据通讯前,先根据操做对各所用到的数据、地址通道进行握手。主要操做包括传输发送者 A 等到传输接受者 B READY 信号后, A 将数据与 VALID 信号同时发送给 B ,这是一种典型的握手机制。


 

AXI总线分为五个通道:
spa

读地址通道,包含ARVALID, ARADDR, ARREADY信号;.net

写地址通道,包含AWVALIDAWADDR, AWREADY信号;3d

读数据通道,包含RVALID, RDATA, RREADY, RRESP信号;orm

写数据通道,包含WVALID, WDATAWSTRB, WREADY信号;视频

写应答通道,包含BVALID, BRESP, BREADY信号;blog

系统通道,包含:ACLKARESETN信号;token

 

其中ACLKaxi总线时钟,ARESETNaxi总线复位信号,低电平有效;读写数据与读写地址类信号宽度都为32bitREADYVALID是对应的通道握手信号;WSTRB信号为1bit对应WDATA有效数据字节,WSTRB宽度是32bit/8=4bitBRESPRRESP分别为写回应信号,读回应信号,宽度都为2bit‘h0表明成功,其余为错误。

读操做顺序为主与从进行读地址通道握手并传输地址内容,而后在读数据通道握手并传输所读内容以及读取操做的回应,时钟上升沿有效。如图所示: 


写操做顺序为主与从进行写地址通道握手并传输地址内容,而后在写数据通道握手并传输所读内容,最后再写回应通道握手,并传输写回应数据,时钟上升沿有效。如图所示:


 

附录代码清单:

moduleaq_axi_master(

  // Reset, Clock

  input           ARESETN,

  input           ACLK,

 

  // Master 写地址通道

  output [0:0] M_AXI_AWID,

  output [31:0] M_AXI_AWADDR,

  output [7:0] M_AXI_AWLEN,    // Burst Length:0-255

  output [2:0] M_AXI_AWSIZE,   // Burst Size:Fixed 2'b011

  output [1:0] M_AXI_AWBURST,  // Burst Type:Fixed 2'b01(Incremental Burst)

  output       M_AXI_AWLOCK,   // Lock: Fixed2'b00

  output [3:0] M_AXI_AWCACHE,  // Cache: Fiex2'b0011

  output [2:0] M_AXI_AWPROT,   // Protect: Fixed2'b000

  output [3:0] M_AXI_AWQOS,    // QoS: Fixed2'b0000

  output [0:0] M_AXI_AWUSER,   // User: Fixed32'd0

  output       M_AXI_AWVALID,

  input        M_AXI_AWREADY,

 

  // Master 写数据通道

  output [63:0] M_AXI_WDATA,

  output [7:0] M_AXI_WSTRB,

  output       M_AXI_WLAST,

  output [0:0] M_AXI_WUSER,

  output       M_AXI_WVALID,

  input        M_AXI_WREADY,

 

  // Master 写响应通道

  input [0:0]  M_AXI_BID,

  input [1:0]  M_AXI_BRESP,

  input [0:0]  M_AXI_BUSER,

  input        M_AXI_BVALID,

  output       M_AXI_BREADY,

   

  // Master 读地址通道

  output [0:0] M_AXI_ARID,

  output [31:0] M_AXI_ARADDR,

  output [7:0] M_AXI_ARLEN,

  output [2:0] M_AXI_ARSIZE,

  output [1:0] M_AXI_ARBURST,

  output [1:0] M_AXI_ARLOCK,

  output [3:0] M_AXI_ARCACHE,

  output [2:0] M_AXI_ARPROT,

  output [3:0] M_AXI_ARQOS,

  output [0:0] M_AXI_ARUSER,

  output       M_AXI_ARVALID,

  input        M_AXI_ARREADY,

   

  // Master 读数据通道

  input [0:0]  M_AXI_RID,

  input [63:0] M_AXI_RDATA,

  input [1:0]  M_AXI_RRESP,

  input        M_AXI_RLAST,

  input [0:0]  M_AXI_RUSER,

  input        M_AXI_RVALID,

  output       M_AXI_RREADY,

       

  // Local Bus

  input        MASTER_RST,

  

  input        WR_START,

  input [31:0] WR_ADRS,

  input [31:0] WR_LEN,

  output       WR_READY,

  output       WR_FIFO_RE,

  input        WR_FIFO_EMPTY,

  input        WR_FIFO_AEMPTY,

  input [63:0] WR_FIFO_DATA,

  output       WR_DONE,

 

  input        RD_START,

  input [31:0] RD_ADRS,

  input [31:0] RD_LEN,

  output       RD_READY,

  output       RD_FIFO_WE,

  input        RD_FIFO_FULL,

  input        RD_FIFO_AFULL,

  output [63:0] RD_FIFO_DATA,

  output       RD_DONE,

 

  output [31:0] DEBUG

);

 

  localparam S_WR_IDLE  = 3'd0;

  localparam S_WA_WAIT  = 3'd1;

  localparam S_WA_START = 3'd2;

  localparam S_WD_WAIT  = 3'd3;

  localparam S_WD_PROC  = 3'd4;

  localparam S_WR_WAIT  = 3'd5;

  localparam S_WR_DONE  = 3'd6;

 

  reg [2:0]  wr_state;

  reg [31:0] reg_wr_adrs;

  reg [31:0] reg_wr_len;

  reg        reg_awvalid, reg_wvalid, reg_w_last;

  reg [7:0]  reg_w_len;

  reg [7:0]  reg_w_stb;

  reg [1:0]  reg_wr_status;

  reg [3:0]  reg_w_count, reg_r_count;

 

  reg [7:0]  rd_chkdata, wr_chkdata;

  reg [1:0]  resp;

  reg rd_first_data;

  reg rd_fifo_enable;

  reg[31:0] rd_fifo_cnt;

assign WR_DONE =(wr_state == S_WR_DONE);

 

 

 

assignWR_FIFO_RE         = rd_first_data |(reg_wvalid & ~WR_FIFO_EMPTY & M_AXI_WREADY & rd_fifo_enable);

always @(posedgeACLK or negedge ARESETN)

begin

      if(!ARESETN)

             rd_fifo_cnt <= 32'd0;

      else if(WR_FIFO_RE)

             rd_fifo_cnt <= rd_fifo_cnt +32'd1;

      else if(wr_state == S_WR_IDLE)

             rd_fifo_cnt <= 32'd0;

end

 

always @(posedgeACLK or negedge ARESETN)

begin

      if(!ARESETN)

             rd_fifo_enable <= 1'b0;

      else if(wr_state == S_WR_IDLE &&WR_START)

             rd_fifo_enable <= 1'b1;

      else if(WR_FIFO_RE && (rd_fifo_cnt== RD_LEN[31:3] - 32'd1) )

             rd_fifo_enable <= 1'b0;          

end

  // Write State

  always @(posedge ACLK or negedge ARESETN)begin

    if(!ARESETN) begin

      wr_state            <= S_WR_IDLE;

      reg_wr_adrs[31:0]   <= 32'd0;

      reg_wr_len[31:0]    <= 32'd0;

      reg_awvalid         <= 1'b0;

      reg_wvalid          <= 1'b0;

      reg_w_last          <= 1'b0;

      reg_w_len[7:0]      <= 8'd0;

      reg_w_stb[7:0]      <= 8'd0;

      reg_wr_status[1:0]  <= 2'd0;

      reg_w_count[3:0]    <= 4'd0;

      reg_r_count[3:0]  <= 4'd0;

      wr_chkdata          <= 8'd0;

      rd_chkdata <= 8'd0;

      resp <= 2'd0;

       rd_first_data <= 1'b0;

  end else begin

    if(MASTER_RST) begin

      wr_state <= S_WR_IDLE;

    end else begin

      case(wr_state)

        S_WR_IDLE: begin

          if(WR_START) begin              //外部开始写地址

            wr_state          <= S_WA_WAIT;

            reg_wr_adrs[31:0] <=WR_ADRS[31:0];//写地址

            reg_wr_len[31:0]  <= WR_LEN[31:0] -32'd1;//写长度

                    rd_first_data <= 1'b1;

          end

          reg_awvalid         <= 1'b0;

          reg_wvalid          <= 1'b0;

          reg_w_last          <= 1'b0;

          reg_w_len[7:0]      <= 8'd0;

          reg_w_stb[7:0]      <= 8'd0;

          reg_wr_status[1:0]  <= 2'd0;

        end

             //写地址等待

        S_WA_WAIT: begin

                    //外部FIFO不空或者长度为0则开始写地址

          if(!WR_FIFO_AEMPTY |(reg_wr_len[31:11] == 21'd0)) begin

            wr_state          <= S_WA_START;

          end

              rd_first_data <= 1'b0;

        end

             //写地址开始

        S_WA_START: begin

          wr_state            <= S_WD_WAIT;//写数据等待

          reg_awvalid         <= 1'b1;

              //写长度减一

          reg_wr_len[31:11]    <= reg_wr_len[31:11] - 21'd1;

          if(reg_wr_len[31:11] != 21'd0) begin

            reg_w_len[7:0]  <= 8'hFF;//每次写256个数据

            reg_w_last      <= 1'b0;

            reg_w_stb[7:0]  <= 8'hFF;

          end else begin//最后不足256个的数据写入

            reg_w_len[7:0]  <= reg_wr_len[10:3];

            reg_w_last      <= 1'b1;

            reg_w_stb[7:0]  <= 8'hFF;

          end

        end

        S_WD_WAIT: begin

             //等待写总线READY,进入写数据状态

          if(M_AXI_AWREADY) begin

            wr_state        <= S_WD_PROC;

            reg_awvalid     <= 1'b0;

                    //开始写数据

            reg_wvalid      <= 1'b1;

          end

        end

             //写数据

        S_WD_PROC: begin

          if(M_AXI_WREADY & ~WR_FIFO_EMPTY)begin

              //一次突发写完成

            if(reg_w_len[7:0] == 8'd0) begin

              wr_state        <= S_WR_WAIT;

              reg_wvalid      <= 1'b0;

              reg_w_stb[7:0]  <= 8'h00;

            end else begin

              reg_w_len[7:0]  <= reg_w_len[7:0] -8'd1;

            end

          end

        end

             //写等待

        S_WR_WAIT: begin

                    //写响应完成

          if(M_AXI_BVALID) begin

            reg_wr_status[1:0]  <= reg_wr_status[1:0] | M_AXI_BRESP[1:0];

            if(reg_w_last) begin//写完成

              wr_state          <= S_WR_DONE;

            end else begin//写未完成

              wr_state          <= S_WA_WAIT;

                      //地址每次递增

              reg_wr_adrs[31:0] <=reg_wr_adrs[31:0] + 32'd2048;

            end

          end

        end

        S_WR_DONE: begin

            wr_state <= S_WR_IDLE;

          end

       

        default: begin

          wr_state <= S_WR_IDLE;

        end

      endcase

      end

    end

  end

  

  assign M_AXI_AWID         = 1'b0;

  assign M_AXI_AWADDR[31:0] =reg_wr_adrs[31:0];

  assign M_AXI_AWLEN[7:0]   = reg_w_len[7:0];

  assign M_AXI_AWSIZE[2:0]  = 2'b011;

  assign M_AXI_AWBURST[1:0] = 2'b01;

  assign M_AXI_AWLOCK       = 1'b0;

  assign M_AXI_AWCACHE[3:0] = 4'b0011;

  assign M_AXI_AWPROT[2:0]  = 3'b000;

  assign M_AXI_AWQOS[3:0]   = 4'b0000;

  assign M_AXI_AWUSER[0]    = 1'b1;

  assign M_AXI_AWVALID      = reg_awvalid;

 

  assign M_AXI_WDATA[63:0]  = WR_FIFO_DATA[63:0];

  assign M_AXI_WSTRB[7:0]   = (reg_wvalid & ~WR_FIFO_EMPTY)?8'hFF:8'h00;

  assign M_AXI_WLAST        = (reg_w_len[7:0] == 8'd0)?1'b1:1'b0;

  assign M_AXI_WUSER        = 1;

  assign M_AXI_WVALID       = reg_wvalid & ~WR_FIFO_EMPTY;

  assign M_AXI_BREADY       = M_AXI_BVALID;

  assign WR_READY           = (wr_state == S_WR_IDLE)?1'b1:1'b0;

 

  localparam S_RD_IDLE  = 3'd0;

  localparam S_RA_WAIT  = 3'd1;

  localparam S_RA_START = 3'd2;

  localparam S_RD_WAIT  = 3'd3;

  localparam S_RD_PROC  = 3'd4;

  localparam S_RD_DONE  = 3'd5;

 

  reg [2:0]  rd_state;

  reg[31:0]  reg_rd_adrs;

  reg [31:0] reg_rd_len;

  reg        reg_arvalid, reg_r_last;

  reg [7:0]  reg_r_len;

 assign RD_DONE = (rd_state == S_RD_DONE) ;

  // Read State

  always @(posedge ACLK or negedge ARESETN)begin

    if(!ARESETN) begin

      rd_state          <= S_RD_IDLE;

      reg_rd_adrs[31:0] <= 32'd0;

      reg_rd_len[31:0]  <= 32'd0;

      reg_arvalid       <= 1'b0;

      reg_r_len[7:0]    <= 8'd0;

    end else begin

      case(rd_state)

        S_RD_IDLE: begin

             //读开始

          if(RD_START) begin

            rd_state          <= S_RA_WAIT;

            reg_rd_adrs[31:0] <=RD_ADRS[31:0];

            reg_rd_len[31:0]  <= RD_LEN[31:0] -32'd1;

          end

          reg_arvalid     <= 1'b0;

          reg_r_len[7:0]  <= 8'd0;

        end

             //读通道等待

        S_RA_WAIT: begin

          if(~RD_FIFO_AFULL) begin

            rd_state          <= S_RA_START;

          end

        end

             //读地址开始

        S_RA_START: begin

          rd_state          <= S_RD_WAIT;

          reg_arvalid       <= 1'b1;

          reg_rd_len[31:11] <=reg_rd_len[31:11] -21'd1;

          if(reg_rd_len[31:11] != 21'd0) begin

            reg_r_last      <= 1'b0;

            reg_r_len[7:0]  <= 8'd255;

          end else begin

            reg_r_last      <= 1'b1;

            reg_r_len[7:0]  <= reg_rd_len[10:3];

          end

        end

             //读数据等待

        S_RD_WAIT: begin

          if(M_AXI_ARREADY) begin

            rd_state        <= S_RD_PROC;

            reg_arvalid     <= 1'b0;

          end

        end

             //读数据开始

        S_RD_PROC: begin

          if(M_AXI_RVALID) begin

            if(M_AXI_RLAST) begin

              if(reg_r_last) begin

                rd_state          <= S_RD_DONE;

              end else begin

                rd_state          <= S_RA_WAIT;

                reg_rd_adrs[31:0] <=reg_rd_adrs[31:0] + 32'd2048;

              end

            end else begin

              reg_r_len[7:0] <=reg_r_len[7:0] -8'd1;

            end

          end

        end

             S_RD_DONE:begin

                    rd_state          <= S_RD_IDLE;

             end

                   

       endcase

    end

  end

  

  // Master Read Address

  assign M_AXI_ARID         = 1'b0;

  assign M_AXI_ARADDR[31:0] =reg_rd_adrs[31:0];

  assign M_AXI_ARLEN[7:0]   = reg_r_len[7:0];

  assign M_AXI_ARSIZE[2:0]  = 3'b011;

  assign M_AXI_ARBURST[1:0] = 2'b01;

  assign M_AXI_ARLOCK       = 1'b0;

  assign M_AXI_ARCACHE[3:0] = 4'b0011;

  assign M_AXI_ARPROT[2:0]  = 3'b000;

  assign M_AXI_ARQOS[3:0]   = 4'b0000;

  assign M_AXI_ARUSER[0]    = 1'b1;

  assignM_AXI_ARVALID      = reg_arvalid;

 

  assign M_AXI_RREADY       = M_AXI_RVALID & ~RD_FIFO_FULL;

 

  assign RD_READY           = (rd_state == S_RD_IDLE)?1'b1:1'b0;

  assign RD_FIFO_WE         = M_AXI_RVALID;

  assign RD_FIFO_DATA[63:0] = M_AXI_RDATA[63:0];

 

  assign DEBUG[31:0] = {reg_wr_len[31:8],

                        1'd0, wr_state[2:0],1'd0, rd_state[2:0]};

  

endmodule


本文分享自微信公众号 - 瓜大三哥(xiguazai_tortoise)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索