读写分离的设计
在Axi4总线中,读和写通道是完全相互独立,互不干扰。故而无论是在设计Decoder还是Arbiter时,均可以采用读写分离的方式。如前文所述,SpinalHDL在基于Axi4总线基础上有设计了Axi4WriteOnly、Axi4ReadOnly总线。并提供了相应的转换方法:
那么前文中的Deocder和Arbiter均可以分为
Axi4WriteOnlyDecoder
Axi4ReadOnlyDecoder
Axi4WriteOnlyArbiter
Axi4ReadOnlyArbiter
这次先来看看SpinalHDL中Axi4WriteOnlyDecoder的设计思路。
Axi4WriteOnlyDecoder
Axi4WriteOnlyDecoder属于一入多出的模块,而Axi4的写操作需要aw、w、b三个通道交互配合才能够完成依次传输。而其中aw、w均为master——>slave传输,而b则为slave——>通道传输。那么在设计时需要考虑的因素有:
每个写通道writeIssuingCapability的支持。
通道切换契机的选择,确保每个通道(aw、w,b)在切换通道之前均能保障之前的数据传输完成。
地址译码没有命中任何slave端口的处理。
基于上述考虑,及对于每个slave端口,其时序并不能保障先后顺序(后接受数据的slave端口可能先返回b通道数据)。为此,Axi4WriteOnlyDecoder设计时采用了如下原则:
1、对于每个通道,均支持writeIssuingCapability,但如果当前指令地址译码命中的slave端口和上次不一样,那么则需等待之前的数据完成传输才进行本次数据传输。
2、设计单独的Axi4WriteOnlyErrorSlave用于处理地址未命中任何slave端口的情况。
模块参数配置
Axi4WriteOnlyDecoder在例化时的参数包括:
axiConfig: Axi4Config,Axi4总线参数配置。
decodings: Seq[SizeMapping],Slave端口的地址段划分。地址段不能有重叠。
pendingMax:Int,在接收新指令时允许链路中待完成的指令个数。值得注意的是该参数需为2^n-1
lowLantency:Boolean,该参数设置为false,其实现存在bug,后续也将会删除该参数。
代码解读
Axi4WriteOnlyDecoder对于aw、w通道,采用了两个计数器来计数当前链路中待完成的任务数:
从上可以看到,一次写命令的完成是要等待writeRsp.fire时才完成。
地址的译码动作由下面的代码实现:
decodedCmdSels用于表示当前译码状态,每个bit对应一个slave通道,当 decodedCmdSels为0时则意味着所有的slave通道均未命中,则decodedCmdError。而pendingSels、pendingError则用于记录上次的通道选择标志。
针对cmdAllowedStart信号:
对于aw通道,命令只有在pendingCmdCounter为0或者pendingCmdCounter为不超过pendingMax且pendingSels与decodedCmdSels一致情况下才允许发射传输。当pendingCmdCounter为0时,意味着链路中不存在待完成的任务,那么当前任务cmd可即使被路由传输,而若不为0,则只有在当前任务的译码结果与上次保持一致时才能够传输,也就意味着当不一致时必须等待上个端口所有任务均完成才能够进行这次路由传输。
cmdAllowedStart本质上用于表示一次writeCmd被处理,由于pendingSels、pendingError均依赖于cmdAllowedStart进行锁存,之所以cmdAllowedStart被这么来赋值,是为了考虑当一开始writeCmd.ready没有拉高时不影响writeData通道(路由通道的选择依赖于aw通道,但不依赖于aw.ready的状态,一旦路由确定,writeData即可进行数据传输,而无需考虑aw.ready).
针对地址译码错误,其处理形式为:
基于上面的工作,剩下的就是端口的互联了(可能图片不清晰,可以去源代码中去看):
这里有一点值得注意的是:writeCmd通道路由的选择依赖于信号decodedCmdSels,而writeData,writeRsp通道路由的选择则依赖于pendingSels。若将lowLantency配置为true,将会存在路由信号错误路由的情况。而当前这种设计模式在做通道切换时首包报文的writeData通路将会插入一拍的buble。
原作者:玉骐