在异构计算系统中,AXI Memory-Mapped和SRIO就像两个说不同语言的快递员。AXI是ARM公司推出的片上总线协议,负责芯片内部的数据搬运;而SRIO则是面向高速串行互连的行业标准,擅长跨设备通信。要让这两个"快递员"高效协作,关键在于设计一个智能的"翻译官"——这就是我们要讨论的SRIO控制器。
我曾在DSP+FPGA的异构系统中实测过,原生SRIO IP核的AXI4-Stream接口就像个只会说方言的搬运工,虽然能搬货但不知道货该放哪里。Memory-Mapped的妙处在于它给每个数据包都贴上了明确的"门牌号",通过地址映射直接访问DDR空间。这就像给快递员配备了GPS导航,数据包能精准送达指定内存位置。
大端小端问题是第一个要跨过的坑。有次调试时发现DSP发送的图像在FPGA端总是错位,排查半天才发现SRIO默认采用大端模式(高位字节在前),而我们的ARM处理器是小端架构。解决方案是在AXI转换层加入字节序调整逻辑,相当于给数据包做了"镜像翻转":
verilog复制// 大端转小端转换示例
assign axi_wdata[31:24] = srio_data[7:0];
assign axi_wdata[23:16] = srio_data[15:8];
assign axi_wdata[15:8] = srio_data[23:16];
assign axi_wdata[7:0] = srio_data[31:24];
接收控制器就像个专业的包裹分拣中心。当NWRITE数据包通过SRIO链路到达时,首先要做的是"拆包裹"——解析包头中的目的地址和载荷大小。这里有个关键细节:AXI协议要求突发传输不能跨越4KB地址边界。在实际项目中,我们遇到过因忽略这个规则导致的DDR访问异常。
突发拆分算法是我们的解决方案:对于跨边界的传输请求,自动拆分成多个合法突发。例如一个5KB的NWRITE包会被分解为:
c复制// 突发拆分伪代码
void split_transfer(uint64_t addr, size_t size) {
size_t remaining = size;
while(remaining > 0) {
size_t chunk = min(remaining, 4KB - (addr % 4KB));
generate_axi_burst(addr, chunk);
addr += chunk;
remaining -= chunk;
}
}
发送端我们选择了SWRITE作为主力运输车,这是经过实际对比测试后的决定。与NWRITE相比,SWRITE的包头更精简,实测在x4 3.125Gbps链路上能减少约15%的协议开销。但要注意SWRITE不支持目标端确认,适合对可靠性要求不高的场景。
带宽利用率提升技巧:
门铃就像系统间的"微信通知"。我们的设计中有个踩坑经验:初期直接使用电平中断导致多次误触发,后来改为脉冲宽度大于2个时钟周期的边沿触发才稳定。门铃响应还有个细节——prio字段必须+1返回,这是SRIO协议容易忽略的规则。
中断风暴防护措施:
遇到过因DSP持续高速发送导致FPGA端FIFO溢出的情况,我们最终采用三级流控:
在DSP+FPGA的异构系统中,我们设计了两级缓存策略:
实测这种结构比直接跨时钟域传输效率提升40%,时序违规减少90%。
通过以下措施将传输延迟从最初的5us降低到1.2us:
在Xilinx Zynq UltraScale+ MPSoC平台上的测试数据:
| 测试项 | 初始值 | 优化后 | 提升幅度 |
|---|---|---|---|
| 单向吞吐量 | 5.8Gbps | 8.85Gbps | 52.6% |
| 往返延迟 | 5us | 1.2us | 76% |
| 中断响应时间 | 200ns | 80ns | 60% |
调试过程中有个值得分享的发现:当AXI突发长度为16时,DDR控制器效率突然下降。后来用ChipScope抓取信号发现是bank冲突导致,调整为8或32后性能回归正常。这种非线性性能变化在高速系统中很常见,建议大家在关键路径上多设置几个性能采样点。