文章目录
前言
最近需要使用到AXI从机,所以就学习了一下,一开始学习时看到那么多信号非常头疼,其实一般的使用并不会把所有信号全用上,所以只需要把重要的信号学习一遍,等到需要时再来学习其他的。
一、参考资料
AXI4的官方文档(free):
https://download.csdn.net/download/qq_45843356/78108133
虽然读完了文档,但是对于如何写代码仍然一头雾水,
于是在网上搜了一通AXI从机设计的代码,没有发现特别有用的,后来通过VIVADO生成ip核时生成的代码,获得了一份参考资料。
https://download.csdn.net/download/qq_45843356/78106496
链接放这了,免费下载
二、与例程相符的时序图
突发写时序图:
突发读时序图:
三、例程讲解
1. axi_awready
// Implement axi_awready generation
// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awready <= 1'b0;
axi_awv_awr_flag <= 1'b0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
begin
// slave is ready to accept an address and
// associated control signals
axi_awready <= 1'b1;
axi_awv_awr_flag <= 1'b1;
// used for generation of bresp() and bvalid
end
else if (S_AXI_WLAST && axi_wready)
// preparing to accept next address after current write burst tx completion
begin
axi_awv_awr_flag <= 1'b0;
end
else
begin
axi_awready <= 1'b0;
end
end
end
axi_awv_awr_flag,axi_arv_arr_flag这两个信号是标志前一个突发读或突发写已经开始但未接收完成的标志,目的是等待接收完一个突发后,再启动axi_awready,准备接收读或写的地址。可参照突发写时序图理解
这种关系是AXI技术文档上确定的,如下图,
注:单箭头表示箭头指向信号可以在箭头尾信号有效之前或之后有效
双箭头表示箭头指向信号必须再箭头尾有效之后才能有效
2.axi_awaddr
// Implement axi_awaddr latching
// This process is used to latch the address when both
// S_AXI_AWVALID and S_AXI_WVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awaddr <= 0;
axi_awlen_cntr <= 0;
axi_awburst <= 0;
axi_awlen <= 0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag)
begin
// address latching
axi_awaddr <= S_AXI_AWADDR[C_S_AXI_ADDR_WIDTH - 1:0];
axi_awburst <= S_AXI_AWBURST;
axi_awlen <= S_AXI_AWLEN;
// start address of transfer
axi_awlen_cntr <= 0;
end
else if((axi_awlen_cntr <= axi_awlen) && axi_wready && S_AXI_WVALID)
begin
axi_awlen_cntr <= axi_awlen_cntr + 1;
case (axi_awburst)
2'b00: // fixed burst
// The write address for all the beats in the transaction are fixed
begin
axi_awaddr <= axi_awaddr;
//for awsize = 4 bytes (010)
end
2'b01: //incremental burst
// The write address for all the beats in the transaction are increments by awsize
begin
axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//awaddr aligned to 4 byte boundary
axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
//for awsize = 4 bytes (010)
end
2'b10: //Wrapping burst
// The write address wraps when the address reaches wrap boundary
if (aw_wrap_en)
begin
axi_awaddr <= (axi_awaddr - aw_wrap_size);
end
else
begin
axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
end
default: //reserved (incremental burst for example)
begin
axi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//for awsize = 4 bytes (010)
end
endcase
end
end
end
此模块是为了锁存axi_awaddr。if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag)此处是为了保证在axi_awready拉高的同一个时钟沿,锁存axi_awaddr,S_AXI_AWALID有效后下一个时钟axi_awaddr就会有效,例程在检测到S_AXI_AWALID有效后,同时使axi_awready有效和锁存axi_awaddr。可参照突发写时序图理解
在锁存axi_awaddr后,从机会根据axi_awburst,调整axi_awaddr。
3.axi_wready
// Implement axi_wready generation
// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_wready <= 1'b0;
end
else
begin
if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)
begin
// slave can accept the write data
axi_wready <= 1'b1;
end
//else if (~axi_awv_awr_flag)
else if (S_AXI_WLAST && axi_wready)
begin
axi_wready <= 1'b0;
end
end
end
在S_AXI_WAWLID有效后,使axi_wready有效,在最后一个数据传输完成,S_AXI_WLAST有效后,使axi_wready无效。
4.axi_bvalid,axi_bresp,axi_buser
// Implement write response logic generation
// The write response and response valid signals are asserted by the slave
// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
// This marks the acceptance of address and indicates the status of
// write transaction.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_bvalid <= 0;
axi_bresp <= 2'b0;
axi_buser <= 0;
end
else
begin
if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST )
begin
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0;
// 'OKAY' response
end
else
begin
if (S_AXI_BREADY && axi_bvalid)
//check if bready is asserted while bvalid is high)
//(there is a possibility that bready is always asserted high)
begin
axi_bvalid <= 1'b0;
end
end
end
end
写回复信号,在使用时参考AXI文档中握手信号依赖性的规定
5.axi_arready
// Implement axi_arready generation
// axi_arready is asserted for one S_AXI_ACLK clock cycle when
// S_AXI_ARVALID is asserted. axi_awready is
// de-asserted when reset (active low) is asserted.
// The read address is also latched when S_AXI_ARVALID is
// asserted. axi_araddr is reset to zero on reset assertion.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_arready <= 1'b0;
axi_arv_arr_flag <= 1'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
begin
axi_arready <= 1'b1;
axi_arv_arr_flag <= 1'b1;
end
else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)
// preparing to accept next address after current read completion
begin
axi_arv_arr_flag <= 1'b0;
end
else
begin
axi_arready <= 1'b0;
end
end
end
axi_arv_arr_flag的作用同axi_awv_awr_flag的作用类似,可参照AXI读时序图理解
6.axi_araddr
// Implement axi_araddr latching
//This process is used to latch the address when both
//S_AXI_ARVALID and S_AXI_RVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_araddr <= 0;
axi_arlen_cntr <= 0;
axi_arburst <= 0;
axi_arlen <= 0;
axi_rlast <= 1'b0;
axi_ruser <= 0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)
begin
// address latching
axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0];
axi_arburst <= S_AXI_ARBURST;
axi_arlen <= S_AXI_ARLEN;
// start address of transfer
axi_arlen_cntr <= 0;
axi_rlast <= 1'b0;
end
else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)
begin
axi_arlen_cntr <= axi_arlen_cntr + 1;
axi_rlast <= 1'b0;
case (axi_arburst)
2'b00: // fixed burst
// The read address for all the beats in the transaction are fixed
begin
axi_araddr <= axi_araddr;
//for arsize = 4 bytes (010)
end
2'b01: //incremental burst
// The read address for all the beats in the transaction are increments by awsize
begin
axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//araddr aligned to 4 byte boundary
axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
//for awsize = 4 bytes (010)
end
2'b10: //Wrapping burst
// The read address wraps when the address reaches wrap boundary
if (ar_wrap_en)
begin
axi_araddr <= (axi_araddr - ar_wrap_size);
end
else
begin
axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//araddr aligned to 4 byte boundary
axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
end
default: //reserved (incremental burst for example)
begin
axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
//for arsize = 4 bytes (010)
end
endcase
end
else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )
begin
axi_rlast <= 1'b1;
end
else if (S_AXI_RREADY)
begin
axi_rlast <= 1'b0;
end
end
end
axi_awaddr锁存,根据axi_arburst(突发类型)调整axi_awaddr。
并且完成发送计数axi_arlen_cntr,达到预定长度axi_arlen时,使axi_rlast有效一个时钟周期。
7.axi_rvalid
// Implement axi_arvalid generation
// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_ARVALID and axi_arready are asserted. The slave registers
// data are available on the axi_rdata bus at this instance. The
// assertion of axi_rvalid marks the validity of read data on the
// bus and axi_rresp indicates the status of read transaction.axi_rvalid
// is deasserted on reset (active low). axi_rresp and axi_rdata are
// cleared to zero on reset (active low).
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rvalid <= 0;
axi_rresp <= 0;
end
else
begin
if (axi_arv_arr_flag && ~axi_rvalid)
begin
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0;
// 'OKAY' response
end
else if (axi_rvalid && S_AXI_RREADY)
begin
axi_rvalid <= 1'b0;
end
end
end
握手信号axi_rvalid,参考AXI读时序图理解。
四、总结
主要工作是根据例程重画了一遍贴合例程的突发读时序图和突发写时序图,最初开始看不懂程序主要是因为涉及的信号比较多,顺着程序把读过程和写过程理一遍,把时序图画出来,对于整个例程的把握加深了许多。
参考:https://blog.csdn.net/wuzhikaidetb/article/details/121594798