ZYNQ AXI协议学习(AXI_HP)
在ZYNQ AXI文章中介绍了 HP接口用于访问DDR和OCM
vivado版本用的2019,创建一个带HP接口的IP核(先分析官方的Demo,后续给出自己工程代码)
IP配置
在这个IP里面,接口就比AXI_Lite多很多了,首先还是附上整理的接口
分析端口信号
-
status
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进行在线调试分析