AXI协议与自定义AXI4-Lite外设
之前在Xilinx的FPGA上做设计,只是知道AXI接口,但是没有详细地了解过这个协议,现在需要用AXI总线做整个系统的设计了,所以还是有必要详细了解一下这个协议。
参考资料
IHI0022H: AMBA AXI and ACE Protocol Specification
IHI0051A: AMBA 4 AXI4-Stream Protocol Specification
上面这两篇都是ARM官方的文档,都可以在ARM的官网找到,这两个文档对AXI4、AXI4-Lite和AXI4-Stream做了详细的介绍。除了这两个文档,再就是Xilinx的几个IP的文档了,有AXI Interconnect、SmartConnect、AXI4-Stream Interconnect,这三个只需要大致看一下就行。
网上也有很多介绍AXI协议的帖子,也都写得非常好,而且也比较详细。本文的内容会比较少,因为都只是我在阅读过程中做的一些笔记,如果真心想了解这些内容,强烈建议要用心阅读上面两篇Specification!
AXI 协议
- 特点
- 地址、控制和数据分开控制
- 支持非对齐的数据传输
- 支持突发传输
- 读写通道分开
- 支持乱序传输
- AXI 结构
- 有AR、R、AW、W、B 五个独立通道,分别对应读地址、读数据、写地址、写数据、写响应。
- 地址通道带有控制信息
- B是响应通道,用来返回写响应
- R通道不仅有读的数据也有响应信息
- 总线互连的时候,公用地址和数据总线;或者公用地址线,多条数据总线;再或者有多条地址和数据总线。
- 全局信号
- 时钟,上升沿采样;复位低电平有效
- 时钟与复位
- 时钟 上升沿有效,接口之间的信号没有组合逻辑通路
- 复位可以是异步复位但一定要同步释放
- 复位的时候 manager端的AW、AR、W通道的VALID信号都要是low;subordinate端的R、B通道的VALID信号都要是low
- VALID只有在复位撤销后的第一个时钟上升沿才能变为有效
- 同一通道的VALID信号不能依赖READY信号
- VALID有效之后只有出现READY才能撤销
- destination可以等待VALID出现再产生READY
- READY出现之后,可以在VALID出现之前撤销
- 通道信号
- 建议AWREADY和ARREADY默认为高电平,这样可以比较快
- 推荐inactive的byte lanes的WDATA RDATA是低电平
- 通道之间的关系
- 写响应要在写数据的LAST信号有效之后产生
- 读数据要在读地址送给subordinate之后送出数据
- 协议里面之规定了这两组关系,也就是说可能会有比如写数据比写地址先到或者后到的情况,这些都是允许的。
- 事务结构
- AXI协议是基于突发(burst)传输的,突发传输由subordinate计算地址,因为manager只会发一个起始地址,后面字节的地址得靠subordinate来计算。
- 不能超过4kB地址边界
- AxBurst定义了突发的类型,突发不能终止
- 有三类突发,INCR,FIXED,WRAP。INCR是地址自增的,FIXED是地址固定的,WRAP是会地址回滚的,它跟INCR类似,但就是传输过程中如果地址到了上边界,它就会让地址再回到下边界。
- AxLEN表示突发的长度,就是一次burst有几次VALID和READY的握手;AxSize表示一次传输的字节个数,就是一次VALID和READY的握手传输多少个字节。
- WRAP的突发长度不能超过16,INCR虽然可以超过,但实际上是转换成多个16长度的突发
- AxSize用3bit表示,最多128字节
- Regular Transaction 常规的事务
- Regular_Transactions_Only 这个属性如果是TRUE,那么就只支持常规事务
- AxLen 1,2,4,8,16
- if AxLen > 1 , AxSize = data bus width
- AxBurst = INCR or WRAP,没有FIXED
- 地址是对齐的
- 数据读写的结构
- 实现混合的大小端数据的读写和非对齐传输(因为有字节选通,所以数据是按照字节为单位传的,没有大小端的问题,可以做到大小端混合的数据传输)
- byte strobe 在VALID信号为低的时候保持低电平或者维持之前的信号不变
- narrow transfers,就是用strobe选通。INCR和WRAP模式,不同传输次数之间的字节位置不同;FIXED模式下,字节位置固定。
- 非对齐传输的两种方式:
- 直接给非对齐的地址,从这个非对齐地址开始到对齐的边界算作一次传输
- 用更低的对齐的地址,然后用strobe选通
- 一次burst对应一次响应,有四种响应OKAY、EXOKAY、SLVERR、DECERR
- AxCACHE
- subordinate设备分为peripheral和memory
- peripheral的信号可以简化,因为它实现的功能少
- modifiable:
- 一个事务可以拆分成多个事务
- 多个事务可以合并成一个事务
- 一个读事务可以读比需要的更多的数据
- 一个写事务可以访问比需要的更多的地址空间,通过strobe选通
- 在另外生成的事务中,AxADDR AxSIZE AxLEN AxBUREST可以被改写
- AxLOCK和AxPROT不能被改写
- 有了ID就可以有多个Outstanding的事务
- RID和ARID对应
- subordinate要对RID排序,这个排序的深度在设计subordinate的时候给定,manager没办法获得这个深度
- interconnect会在每个master的ID前面加上master的ID,所以不用担心不同master的ID重复
- exclusive访问:不支持其他master的访问
AXI4-Lite
- 所有事务的AxLEN都是1
- size都是数据位宽32/64
- 不支持exclusive访问
- non-modifiable
- 虽然支持多个Outstanding的事务,但是subordinate可以合理地利用握手信号来限制这个
AXI4-Stream
- 三种byte类型
- Data byte:数据
- Position byte: placeholder,不包含数据
- Null byte:不包含任何数据和位置信息
- 术语定义:
- Transfer:一次VALID和READY的握手
- Packet:多个Transfer
- Frame:多个Packet
- 四种数据流
- Byte stream:data byte和null byte间隔
- 只有data byte的对齐传输
- 连续的data byte,在起始和结尾处补上position byte构成对齐传输
- Sparse stream:data byte和position byte间隔
- 信号
- TSTRB和TKEEP:TSTRB区分data和position byte,TKEEP无效的是null byte
- 时钟和复位同AXI4
- 推荐tuser的bit数是字节个数的整数倍
- 和AXI4的不同
- AXI4不允许interleaving
- AXI4-Stream没有最大的burst长度
- AXI4-Stream的数据位宽任意
- AXI4-Stream包含TID和TDEST指示源和目的信息
- AXI4-Stream对TUSER的定义更具有操作性
- AXI4-Stream多了TKEEP信号
自定义AXI4-Lite外设
Vivado的Tool标签下有一个选项就是“Create and Package New IPs”,点进去之后可以选择创建一个新的AXI4外设,然后给这个IP起个名字,可以先放到IP库里,然后在Block Design中调用它,再右键“Edit in IP Packager”。
点击“Edit in IP Packager”之后,可以看到这个IP的顶层文件和一个它实例化的AXI4-Lite接口的模块。我们可以在这个模块中加入自己的设计,这里面一开始生成的代码大部分都不需要去改动,而且注释也都写得很清楚,每个always块都有注释。
1 | // Implement axi_awready generation |
第一个always块是对awready和aw_en进行控制,注释也写了它“excepts no outstanding transactions”,也就是它只能逐个事务进行处理,只有在响应完成之后才能接收新的写数据。wready和awready是一样的,都会受到aw_en的控制,只有aw_en为高电平的时候,这两个ready才有可能置一;然后aw_en又只会在响应完成之后置高。
1 | // Implement memory mapped register select and write logic generation |
我给这个AXI4-Lite接口的外设分配了4个寄存器,上面这段代码是在AW和W通道都握手的情况下通过W通道往寄存器写数据,case语句选择寄存器,再根据WSTRB选择对应的字节。
概括一下剩下的代码实现的功能。写响应只实现了响应OKAY,没有其他类型的响应。在ARVALID有效之后给出ARREADY,ARREADY只响应一个周期,同时锁存住读地址。在发现AR通道握手之后给出一个周期的RVALID信号。slv_reg_rden是AR通道握手,这个信号有效比RVALID提前一个周期,用这个信号作为使能,打一拍之后把数据送到RDATA上,正好RDATA和RVALID一起有效。
用户逻辑设计的时候,可能只需要改上面的写寄存器的部分。根据寄存器中的值设计相应的逻辑对上面这部分代码没有影响。用户也可以增加自己的接口和参数定义,修改完之后记得改顶层的调用。