赞
踩
在ZYNQ AXI文章中介绍了 HP接口用于访问DDR和OCM
vivado版本用的2019,创建一个带HP接口的IP核(先分析官方的Demo,后续给出自己工程代码)
IP配置
在这个IP里面,接口就比AXI_Lite多很多了,首先还是附上整理的接口
INIT_AXI_TXN 这个代码的开始信号
TXN_DONE 数据对比无误(整个Demo是读写数据进行对比)
ERROR 出错信号
Write Addr
M_AXI_AWID = 0 主机写地址ID
M_AXI_AWBURST = 2'b01 确定每次突发的地址如何计算
M_AXI_AWLOCK = 0 传输的原子性
M_AXI_AWCACHE = 4'b0010 内存类型
M_AXI_AWPROT = 0 保护类型
M_AXI_AWQOS = 0
M_AXI_AWUSER = 1 写地址通道中的可选用户定义信号
M_AXI_AWADDR 写地址端口,基地址由上面配置可得0x10000000
M_AXI_AWLEN 突发的数据长度,由配置可得16(最大256)
M_AXI_AWSIZE 突发的数据大小,由配置可得2
M_AXI_AWVALID和M_AXI_AWREADY 一对握手信号
Write Data
M_AXI_WSTRB = 4'b1111 写选通
M_AXI_WUSER = 0 写入数据通道中的可选用户定义信号
M_AXI_WDATA 写数据端口,位宽32
M_AXI_WLAST 写突发最后一次的信号
M_AXI_WVALID和M_AXI_WREADY 一对握手信号
Write resp
M_AXI_BID 在代码里无逻辑存在
M_AXI_BRESP 此信号位宽2,第1位用于表示是否出错,高电平为错误状态
M_AXI_BUSER 在代码里无逻辑存在
M_AXI_BVALID和M_AXI_BREADY 一对握手信号
Read Addr
M_AXI_ARID = 0 主机读地址ID
M_AXI_ARBURST = 2'b01 确定每次突发的地址如何计算
M_AXI_ARLOCK = 0 传输的原子性
M_AXI_ARCACHE = 4'b0010 内存类型
M_AXI_ARPROT = 0 保护类型
M_AXI_ARQOS = 0
M_AXI_ARUSER = 1 写地址通道中的可选用户定义信号
M_AXI_ARADDR 读地址端口,基地址由上面配置可得0x10000000
M_AXI_ARLEN 突发的数据长度,由配置可得16(最大256)
M_AXI_ARSIZE 突发的数据大小,由配置可得2
M_AXI_ARVALID和M_AXI_ARREADY 一对握手信号
Read Data
M_AXI_RID 在代码里无逻辑存在
M_AXI_RDATA 读数据端口,位宽32
M_AXI_RRESP 此信号位宽2,第1位用于表示是否出错,高电平为错误状态
M_AXI_RLAST 读突发最后一次的信号
M_AXI_RUSER 在代码里无逻辑存在
M_AXI_RVALID和M_AXI_RREADY 一对握手信号
在线Debug波形图
写地址
由前面分析端口可以得出,在写地址通道里面,我们只需要主要
M__AXI_AWADDR M_AXI_AWVALID M_AXI_AW_READY (其余得端口都是在初始化就固定了)
assign M_AXI_AWADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr;
//代码243行 写地址等于基地址加上偏移地址 基地址C_M_TARGET_SLAVE_BASE_ADDR = 0x10000000(自己修改)
//axi_awaddr 偏移地址 大小等于突发长度*4。突发长度在初始化可以看到是16的长度,地址增长就是0x40.图中可以看到0x10000000后就是0x10000040
//M_AXI_AWVALID和M_AXI_AW_READY信号握手成功后,传输地址,之后准备新的地址,等待下一轮突发
写数据
图中看出写数据的第一个和写地址是同一个时钟,只有在M_AXI_WVALID和M_AXI_WREADY 同时为高电平时传输数据,当传输突发的最后一个数据时,M_AXI_WLAST拉高一个周期,表示突发一次结束
写回应
写回应通道中,其实只需要分析M_AXI_BRESP端口,握手信号如图所示。BRESP的第1位用于表示是否突发写数据出错。只有在写回应通道收到正确信号后,才能开始下一次的突发写操作
分析代码:
always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 ) begin axi_awvalid <= 1'b0; end // If previously not valid , start next transaction else if (~axi_awvalid && start_single_burst_write) begin axi_awvalid <= 1'b1; end /* Once asserted, VALIDs cannot be deasserted, so axi_awvalid must wait until transaction is accepted */ else if (M_AXI_AWREADY && axi_awvalid) begin axi_awvalid <= 1'b0; end else axi_awvalid <= axi_awvalid; end //此逻辑是写地址有效信号,(写地址准备好信号是PS控制)start_single_burst_write 该信号是由状态机控制的 INIT_WRITE: if (writes_done) begin mst_exec_state <= INIT_READ;// end else begin mst_exec_state <= INIT_WRITE; if (~axi_awvalid && ~start_single_burst_write && ~burst_write_active) begin start_single_burst_write <= 1'b1; end else begin start_single_burst_write <= 1'b0; //Negate to generate a pulse end end //此逻辑是截取的状态机写操作 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) burst_write_active <= 1'b0; //The burst_write_active is asserted when a write burst transaction is initiated else if (start_single_burst_write) burst_write_active <= 1'b1; else if (M_AXI_BVALID && axi_bready) burst_write_active <=0; end //此逻辑是写回应逻辑 //分析这3段逻辑: axi_awvalid start_single_burst_write burst_write_active 其实就这3个信号的跳变,默认3个信号都是0,当状态机进入写状态时, start_single_burst_write=1,axi_awvalid = 0 burst_write_active = 0 下一个时钟start_single_burst_write = 0,axi_awvalid = 1 burst_write_active = 1 下一个时钟start_single_burst_write = 0,axi_awvalid = 0 burst_write_active = 1 只有在写回应通道握手信号都有效时:3个信号同时为0,若此时状态机还在写状态,那么开启下一轮的写地址 always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) begin axi_awaddr <= 'b0; end else if (M_AXI_AWREADY && axi_awvalid) begin axi_awaddr <= axi_awaddr + burst_size_bytes; end else axi_awaddr <= axi_awaddr; end //在axi_awvalid = 1 的一个有效周期内 改变地址 assign write_resp_error = axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]; //判断写是否出错
写数据就不一一分析了,写数据就是按照突发长度一直送数据就是了
在线Debug波形图
读地址
读地址通道和写地址通道逻辑是一样的,都是基地址加上偏移地址,每次加上突发长度*4
读数据
由图中可以看出,当M_AXI_RVALID 拉高的同时,地址数据已经准备好了。当M_AXI_RREADY拉高时,就可以接收突发长度的数据(注:这里也是16的长度,我的理解是这个长度是自己定义不需要和写操作一样,不超过256都行)。
分析代码:
因为读写地址逻辑是一样的,直接按照写地址逻辑分析读地址就可以了。 分析读数据: always @(posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 ) begin axi_rready <= 1'b0; end // accept/acknowledge rdata/rresp with axi_rready by the master // when M_AXI_RVALID is asserted by slave else if (M_AXI_RVALID) begin if (M_AXI_RLAST && axi_rready) begin axi_rready <= 1'b0; end else begin axi_rready <= 1'b1; end end // retain the previous value end //读数据通道的握手信号逻辑, 当M_AXI_RVALID = 1 下一个周期 M_AXI_RVALID = 1 axi_rready = 1.开始传输数据 直到M_AXI_RLAST = 1时结束当前突发传输,查看调试图,可以看到3个信号是同时变成低电平的 assign read_resp_error = axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]; //用于判断是否出错
AXI_RVALID = 1
下一个周期 M_AXI_RVALID = 1 axi_rready = 1.开始传输数据
直到M_AXI_RLAST = 1时结束当前突发传输,查看调试图,可以看到3个信号是同时变成低电平的
assign read_resp_error = axi_rready & M_AXI_RVALID & M_AXI_RRESP[1];
//用于判断是否出错
读数据的其他代码其实是用于进行数据对比,在此不分析
至此HP接口读写DDR已分析完毕,后续我会写一个自己的Demo进行在线调试分析
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。