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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索