前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Zynq中PL读写PS端DDR数据

Zynq中PL读写PS端DDR数据

作者头像
瓜大三哥
发布2019-11-05 12:36:09
8.3K2
发布2019-11-05 12:36:09
举报
文章被收录于专栏:瓜大三哥

前情回顾

(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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 瓜大三哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
媒体处理
媒体处理(Media Processing Service,MPS)是智能、强大、全面的多媒体数据处理服务,行业支持最全面的音视频编码标准,基于自研编码内核和AI算法,提供音视频转码和增强、媒体智能、质检评测等能力,帮助您提升媒体质量、降低成本,满足各类场景的音视频处理需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档