多选一的抉择
Decoder解决了一拆多的问题,那么Arbiter就需要解决多选一的问题。对于写通道,axi4有aw、w、b三个通道,相应地Arbiter就需要解决:
调度问题:从多路端口中仲裁选出相应的端口进行数据路由,且需要保障端口的公平性。
一致性问题:aw、w、b通道相互独立,需确保aw、w、b通道调度顺序的一致性 。
Axi4WriteOnlyArbiter
端口的调度问题很容易解决,其重点在于一致性问题。aw,w通道是master——>slave方向的传输,而b通道又是slave——>master方向的传输,端口之前的协调才是重点。整个Axi4WriteOnlyArbiter的结构图大体如下图所示:
writeCmd 调度设计
对于Axi4写操作,通常情况下命令通道的指令信息不会晚于数据通道,因此多端口的调度选择主要由CmdArbiter来进行。为保证调度的公平性,Cnd Arbiter采用RR调度形式。同时为保证aw、w通道调度的一致性,端口的调度信息通过Route Buffer传递给w通道。同时为了保障Rsp Arbiter的一致性,将端口调度信息也拼接至writeCmd的id子段。
writeData 调度设计
对于w通道的调度设计,其简单很多,Data Arbiter通过Route Buffer中所示的writeCmd调度信息选择合适的端口进行数据路由。
writeRsp调度设计
由于在writeCmd 中的id字段已经包含了端口的选择信息,那么在WriteRsp中仅需要通过id字段解析出端口信息进行路由即可。这也就意味着对于下游设计而言,其必须将writeCmd中id字段包含的信息通过writeRsp中的id字段给返回回来,否则writeRsp将会错误路由。
参数配置
Axi4WriteOnlyArbiter的参数配置包含:
outputConfig:输出端口Axi4配置。
inputsCount:输入端口个数
routeBufferSize: route Buffer Size 大小,该值大小也决定了Axi4WriteOnlyArbiter可以处理的指令个数。
routeBufferLatency:route Buffer通过fifo实现,这里用于指定fifo参数。
routeBufferS2mPipe:时序优化使用。为trure时routeBuffer输出端口将会插入一级寄存器。
是否发现这里并没有input端口的axi4Config配置?这里输入端口的配置是由outputConfig端口自动推断的:
可以看到,输入端口与输出端口的接口配置唯一差别在于其id位宽的不同,主要原因为输出端口的id字段需包含路由信息,故而输入端口的id字段位宽比输出端口的id字段位宽少log2Up(inputsCount) bit。
代码解析
先来看writeCmd仲裁的代码实现:
该部分设计主要调用了两个库:
StreamArbiterFactory:端口仲裁,通过StreamArbiterFactory的roundRobin属性来指定调度算法为RR调度。
StreamFork2:针对该IP可参照文章《不可不知的StreamFork》。对于一个writeCmd,其真正消耗需向下游writeCmd端口及routeBuffer中均成功写入相关内容后才会被消耗,故而StreamFork2用于处理该场景。
另外可以看到,writeCmd中的id字段位宽拼接了端口选择信息cmdArbiter.io.chosen。
这才是代码该有的样子,简洁而又优雅。
再来看writeData的代码实现:
这里例化了一个routeBuffer。知道第一行代码里包含了多少的信息么?
由于cmdRouteork中包含的内容仍为writeCmd中的负载,而我们真正想要压入buffer中的是端口选择信息,故而首先调用translateWith将负载切换为cmdArbiter.io.chosen。随后通过queueLowLatency例化一个StreamFifoLowLatency并将数据压入fifo,同时将fifo的输出端口连接至routeBuffer。若使能了routeBufferS2mPipe,那么输出端口将会再插入一级pipeline。
最后,就是writeRsp的实现:
writeRsp的实现较为简单,通过返回的writeRsp中的id字段解析出端口信息,通过该信息进行端口的路由即可。
》写在最后
整个代码的实现也不过区区几十行,整个的设计思想并非HLS的思路,依旧按照逻辑设计思维来进行设计。而SpinalHDL能够让我们更多的去聚焦逻辑设计本身而非逻辑设计中那些重复性的毫无技术含量的工作。
原作者:玉骐
更多回帖