Zynq中PL读写PS端DDR数据

2019-11-05 12:36:09 浏览数 (2)

前情回顾

(1)ZYNQ中PS端MIO操作

(2)ZYNQ中PS端MIO中断

(3)ZYNQ中PS端UART通信

(4)ZYNQ中PS端XADC读取

1. 读写DDR底层结构 zynq 7000 SOC的HP口是High-Performance Ports的缩写,如下图所示,一共有4个HP接口,HP接口是AXI Slave设备,我们可以通过这4个HP接口实现高带宽的数据交互。实现PL读写PS端挂载的DDR需要使用HP接口。

如下图所示,选择HP0 interface。

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

2. PL端AXI MASTER的机制

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

AXI总线分为五个通道:

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

写地址通道,包含AWVALID,AWADDR, AWREADY信号;

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

写数据通道,包含WVALID, WDATA,WSTRB, WREADY信号;

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

系统通道,包含:ACLK,ARESETN信号;

其中ACLK为axi总线时钟,ARESETN是axi总线复位信号,低电平有效;读写数据与读写地址类信号宽度都为32bit;READY与VALID是对应的通道握手信号;WSTRB信号为1的bit对应WDATA有效数据字节,WSTRB宽度是32bit/8=4bit;BRESP与RRESP分别为写回应信号,读回应信号,宽度都为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

0 人点赞