AXI-Lite

学习一下Vivado的AXI-Lite Slave端代码。通过Vivado创建新的IP并且选择添加AXI Wrapper就会看到Vivado提供的AXI-Lite代码,下面逐项分析这段示例代码。

AW通道

AWREADY生成逻辑如下

always@(posedge S_AXI_ACLK)begin
  if(S_AXI_ACLK == 1'b0)begin
    axi_awready <= 1'b0;
    aw_en <= 1'b1;
  end
  else begin
    if(!axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)begin
      axi_awready <= 1'b1;
      aw_en <= 1'b0;
    end
    else if(S_AXI_BREADY && axi_bvalid)begin
      axi_awready <= 1'b0;
      aw_en <= 1'b1;
    end
    else begin
      axi_awready <= 1'b0;
    end
end

结合写地址和写数据通道的握手依赖关系图进行分析

AWREADY是不依赖于其他信号的,这段代码的注释中声明This design expects no outstanding transactions,这意味着slave需要写地址和写数据通道都有效的时候才认为是master发起了一次有效的传输,并且正常情况下AWREADY是一个脉冲信号。当B通道的握手也完成的时候,AWREADY变为无效。总结一下就是AWREADY会在写传输开始时,也就是AW和W通道的数据都有效时,被使能;在写传输完成,也就是R通道握手完成时,变为无效。同样的道理也可以解释下面AWADDR的代码


always@(posedge S_AXI_CLK)begin
  if(S_AXI_ARESETN == 1'b0)
    axi_awaddr <= 0;
  else begin
    if( !axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
      axi_awaddr <= S_AXI_AWADDR;
  end
end

W通道

W通道行为类似于AW通道,WREADY生成逻辑如下

always@(posedge S_AXI_CLK)begin
  if(S_AXI_ARESETN == 1'b0)
    axi_wready <= 1'b0;
  else begin
    if(!axi_wready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
      axi_wready <= 1'b1;
    else
      axi_wready <= 1'b0;
  end
end

下面是W通道数据写的逻辑,按照注释,只有AW通道和W通道的握手完成时才会执行写入操作,核心的控制信号逻辑如下

assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;

具体写寄存器的逻辑比较简单这里不贴代码了,逻辑就是当slv_reg_wren为1时根据地址和WSTRB写入对应寄存器的字节。

B通道

B通道和W通道逻辑类似,当写入完成时slave使能BVALID并驱动BRESP,因为只有简单的寄存器读写所以BRESP都是OKAY,相应的代码如下

always@(posedge S_AXI_ACLK)begin
  if( S_AXI_ARESETN == 1'b0)begin
    axi_bvalid <= 1'b0;
    axi_bresp <= 2'b0;
  end
  else begin
    if(axi_awready && S_AXI_AWVALID && axi_wready && AXI_WREADY && !axi_bvalid)begin
      axi_bvalid <= 1'b1;
      axi_bresp <= 2'b0;
    end
    else begin
      if(S_AXI_BREADY && axi_bvalid)
        axi_bvalid <= 1'b0;
    end
  end
end

到这里写相关的逻辑就都有了,相应的波形图如下

AR通道

读传输只有AR和R两个通道,握手逻辑是一样的,逻辑比较简单,相关握手信号的依赖关系如下

AR通道代码如下

always@(posedge S_AXI_ACLK)begin
  if(S_AXI_ARESETN == 1'b0)begin
    axi_arready <= 1'b0;
    axi_araddr <= 0;
  end
  else begin
    if(!axi_arready && S_AXI_ARVALID)begin
      axi_arready <= 1'b1;
      axi_araddr <= S_AXI_ARADDR;
    end
    else
      axi_arready <= 1'b0;
  end
end

R通道

从依赖图可以得知RVALID依赖于ARREADY和ARVALID,相关的驱动逻辑如下

always@(posedge S_AXI_ACLK)begin
  if(S_AXI_ARESETN == 1'b0)begin
    axi_rvalid <= 1'b0;
    axi_rresp <= 0;
  end
  else begin
    if(axi_arready && S_AXI_ARVALID && !axi_rvalid)begin
      axi_rvalid <= 1'b1;
      axi_rresp <= 2'b0;
    end
    else if(axi_rvalid && S_AXI_RREADY)
      axi_rvalid <= 1'b0;
  end
end

RRDATA必须不能晚于RVALID更新,示例代码中是在AR通道握手完成后更新,核心逻辑如下

assign slv_reg_rden = axi_arready && S_AXI_ARVALID && !axi_rvalid;

这里我有点没搞清楚为什么还要求!axi_rvalid,我猜测是为了让rvalid有效期间rrdata保持不动,因为rrdata这里也有一级寄存器,如果axi_araddr发生变化的话rrdata继续采样就会出错。写传输的波形图如下

AXI-Lite的代码到这里就结束了,总的来说还是比较简单的,不过有一些东西还是值得总结学习一下的

  • 协议中要求的握手以来关系肯定要实现,一个是RVALID要在ARVALID和ARREADY都使能后才允许被使能,一个是BVALID需要在AWVALID,AWREADY,WVALID,WREADY都使能后才能被使能。除此之外还有一些隐含的,例如VALID的使能不取决于对应的READY
  • 想清楚一个信号什么时候该动,什么时候不该动,要做成电平还是脉冲。以aw_en为例,这个信号之所以存在,是因为需要一个信号来控制slav当前是否能接受写传输,复位之后自然是可以接受写传输,当master发起一个写传输后,在当前传输完成之前都不能接受新的写传输,所以在AW发起一次传输后,直到当前传输完成,slave都不能再接受新的传输。如何界定当前写传输是否完成?显然是R通道握手完成