ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于复旦微FPGA与NOC互连的软件无线电平台设计与实现

基于复旦微FPGA与NOC互连的软件无线电平台设计与实现 1. 项目缘起与整体设计思路1.1 为什么选复旦微电子FPGA做软件无线电平台软件无线电这个概念说了很多年核心思想其实很朴素把传统上靠模拟器件完成的混频、滤波、解调这些活儿尽量搬到数字域来做让一套硬件能通过改软件适配多种通信制式。这件事对硬件平台的要求集中在三点——足够的逻辑资源做并行信号处理、足够灵活的接口连接射频前端、足够低的功耗支撑长时间运行。复旦微电子的FPGA产品线在这几个维度上有自己的特点。以FMQL系列为代表的PSoC器件把可编程逻辑和处理器子系统集成在一颗芯片里PS端跑Linux做协议栈和上层调度PL端做硬实时的基带处理两者通过片内总线紧耦合。这种架构对软件无线电来说很合适因为SDR的典型数据流就是“射频采样→数字下变频→基带解调→协议解析→应用处理”前面几步要求确定性延迟和高吞吐后面几步要求灵活性和可扩展性正好对应PL和PS的分工。我选这个平台还有一个实际原因国产化替代的需求越来越明确很多项目在方案论证阶段就会问“能不能用国产FPGA做”。复旦微的器件在工具链成熟度和IP生态上虽然和国外大厂还有差距但对于软件无线电这种以自定义逻辑为主的应用来说差距并没有想象中那么大。而且它的PSoC架构在板级设计上省掉了一颗独立的处理器对体积和功耗都有好处。1.2 NOC互连为什么成为瓶颈软件无线电平台里数据从ADC进来之后要经过多级处理DDC数字下变频、抽取滤波、匹配滤波、同步、解调、信道解码。每一级可能跑在不同的时钟域数据位宽和速率也不一样。传统做法是用AXI总线或者FIFO把各级串起来但到了多通道场景——比如同时处理4路收发——总线仲裁的冲突就会变成瓶颈。NOCNetwork on Chip的思路是把片上通信也当成一个网络来设计每个处理单元挂在一个路由节点上数据以包的形式在网络里传输。好处是并行度高、可扩展性好增加通道数的时候不需要重新设计总线仲裁逻辑。缺点也明显包交换会引入额外延迟路由逻辑本身要消耗逻辑资源而且设计复杂度比总线高不少。在这个项目里我用NOC主要是解决两个问题一是多通道ADC数据的分发二是PL端多个处理模块之间的数据交换。下面会详细展开具体的设计取舍。1.3 整体架构一句话概括整个平台可以概括为射频前端→ADC→PL端NOC互连的基带处理链→PS端协议栈→上位机显示。PL端用NOC把DDC、滤波、解调等模块连成一张网PS端跑Linux做控制和上层协议两者通过AXI-Stream和寄存器接口交互。下面分模块拆解。2. 核心细节解析与实操要点2.1 复旦微PSoC的PS-PL接口设计复旦微的PSoC器件里PS和PL之间的接口主要有三类AXI-GP通用寄存器访问、AXI-HP高性能数据通道、AXI-ACP加速器一致性端口。软件无线电的数据流主要走AXI-HP因为它的带宽最高适合ADC采样数据从PL往PS搬。控制寄存器走AXI-GP就够了带宽要求不高但要求确定性。这里有个坑要注意AXI-HP的位宽和时钟频率决定了理论带宽上限。比如64位位宽、150MHz时钟理论带宽是64×150M/81200MB/s。但实际能跑到的可能只有70%左右因为AXI协议本身有握手开销。如果你的ADC采样率是100MSPS、14位那原始数据率就是100M×14/8175MB/s单通道没问题四通道就是700MB/s已经接近实际上限了。这时候要么提高AXI-HP时钟要么在PL端先做抽取降低数据率再往PS搬。我的做法是在PL端做两级抽取第一级CIC抽取4倍第二级FIR抽取2倍总共8倍抽取。这样100MSPS的ADC数据到了PS端就变成12.5MSPS四通道合计50MSPS位宽16位的话数据率是100MB/sAXI-HP轻松应对。2.2 NOC互连的拓扑选择与参数配置NOC的拓扑结构常见的有Mesh、Ring、Star几种。Mesh适合节点多、流量分布均匀的场景但每个路由节点要连四个方向逻辑资源消耗大。Ring结构简单但跳数多了延迟会累积。Star结构适合中心节点带宽要求高的场景。我这个项目里PL端的处理模块大概有8个4个DDC通道、1个滤波模块、1个同步模块、1个解调模块、1个控制模块。流量分布是DDC到滤波是四路汇聚到一路滤波到同步是一对一同步到解调是一对一。这种流量模式下我选了简化的Mesh拓扑但只做了2×2的网格每个路由节点连两个DDC通道和一个下游模块。路由节点的参数配置有几个关键点一是FIFO深度太浅了容易丢包太深了浪费BRAM。我实测下来每个输入端口配512深度的FIFO比较稳妥能吸收突发流量。二是仲裁策略我用的是轮询仲裁因为四路DDC的数据率是一样的轮询能保证公平性。三是包格式我定义了一个简单的包头8位源地址8位目的地址4位包类型4位保留后面跟数据。包头开销12.5%可以接受。注意NOC的时钟域 crossing 一定要处理好。如果路由节点和下游模块不在同一个时钟域必须在路由节点输出端加异步FIFO。我一开始忘了加结果数据偶尔出错查了两天才定位到是亚稳态问题。2.3 软件无线电基带处理链的FPGA实现基带处理链的核心模块包括DDC、CIC抽取、FIR滤波、同步和解调。DDC的本质是数字混频把中频信号搬到基带。实现方式是用NCO产生正弦和余弦序列和ADC数据相乘。NCO的相位累加器位宽决定了频率分辨率我用的是32位累加器频率分辨率是采样率/2^32对于100MSPS采样率来说分辨率是0.023Hz完全够用。CIC抽取滤波器的好处是不需要乘法器只用加减和积分适合高速数据流。但CIC的幅频响应有 sinc 函数的滚降通带边缘衰减比较大。我的做法是CIC抽取4倍之后再用一个FIR补偿滤波器把通带平坦度拉回来。FIR的系数用MATLAB的fdatool生成量化成16位定点数。同步模块包括载波同步和定时同步。载波同步我用的是Costas环定时同步用的是Gardner算法。这两个算法在FPGA里实现的时候环路滤波器的参数需要仔细调。比例增益和积分增益的比值决定了环路的锁定速度和稳态误差。我一开始参数设得太激进环路震荡了后来把积分增益降到比例增益的1/100左右才稳定下来。解调模块根据调制方式不同实现不一样。我目前实现了BPSK和QPSK两种BPSK就是取实部的符号QPSK是分别取实部和虚部的符号。解调之后的软信息送给PS端做信道解码。2.4 时钟与复位设计软件无线电平台对时钟的要求比较高因为ADC采样时钟的抖动会直接影响信噪比。我用的是外部晶振经过时钟芯片产生多路时钟ADC采样时钟、PL主时钟、PS主时钟、DDR时钟。这几路时钟的同源很重要否则会有频率偏移。复位设计有个经典问题异步复位同步释放。如果复位信号是异步的释放的时候可能违反触发器的建立保持时间导致亚稳态。我的做法是用两级触发器做同步释放复位断言是异步的释放是同步的。这个细节在低速设计里可能无所谓但在高速数据路径里必须处理。实操心得复旦微的FPGA工具链里时序约束的写法和其他家略有不同。set_false_path和set_clock_groups的语法要查手册确认不能直接照搬Xilinx的约束文件。我踩过这个坑约束写错了工具不报错但时序分析结果不对。3. 实操过程与核心环节实现3.1 开发环境搭建与工程创建复旦微的FPGA开发工具是Procise界面和Quartus有点类似。安装过程没什么特别的但要注意版本匹配Procise的版本要和器件型号对应不然可能找不到器件。我用的版本是Procise 2023.1支持FMQL45T900这个型号。工程创建的时候有几个选项要注意一是器件型号要选对FMQL45T900和FMQL45T900A的逻辑资源不一样。二是速度等级速度等级越高时序越容易收敛但价格也越贵。三是综合策略面积优先还是速度优先。软件无线电这种数据路径我一般选速度优先。工程建好之后先把PS端的配置做好。复旦微的PSoC需要配置PS端的DDR控制器、时钟树、外设引脚。这些在Procise的PS配置界面里完成生成一个配置文件给FSBL用。PL端的逻辑单独综合实现最后和PS端的配置一起生成BOOT.bin。3.2 NOC互连的RTL实现NOC的路由节点我用Verilog写的核心是一个交叉开关加仲裁器。每个输入端口有一个FIFOFIFO非空的时候向仲裁器发请求仲裁器轮询选择一路输出。输出端口也有FIFO用来做时钟域 crossing。路由表的配置我放在一个寄存器文件里PS端可以通过AXI-GP改写。这样不同的应用场景可以配不同的路由策略不用重新综合。路由表的格式是每个目的地址对应一个输出端口号查表的时候用目的地址做索引。包格式我定义得比较简单// 包头格式 // bit[31:24] 源地址 // bit[23:16] 目的地址 // bit[15:12] 包类型 // bit[11:0] 数据长度 // 后面跟数据数据长度我用12位最大4096个数据。对于基带处理来说一包4096个采样点足够了。包类型我定义了四种ADC数据、控制命令、状态回读、调试数据。3.3 DDC与抽取滤波的定点化实现DDC的混频乘法我用的是18×18的乘法器ADC数据是14位NCO输出是16位乘积是30位。截取高16位作为输出。这里有个定点数的细节ADC数据是整数NCO输出是[-1,1)的定点小数乘积之后需要右移相应的位数。CIC抽取滤波器的实现要注意积分器位宽。CIC的增益是抽取因子乘以差分延迟的阶数。比如抽取因子是4差分延迟是1阶数是3那增益就是(4×1)^364。积分器的位宽要在输入位宽的基础上增加log2(增益)位不然会溢出。我的输入是16位增益64是6位所以积分器用22位。FIR补偿滤波器的系数我用MATLAB生成量化成16位。滤波器的阶数是32阶用转置结构实现这样关键路径比较短。乘累加用DSP48实现复旦微的DSP48和Xilinx的类似都是预加器乘法器累加器。3.4 PS端Linux系统与驱动开发PS端跑的是Linux 5.10内核复旦微提供了BSP包。设备树里要配置PL端的AXI外设地址映射这样驱动才能访问。我用的驱动框架是UIO因为PL端的寄存器访问比较简单不需要复杂的驱动逻辑。UIO驱动的设备树节点大概长这样noc_ctrl: noc_ctrla0000000 { compatible generic-uio; reg 0x0 0xa0000000 0x0 0x10000; interrupts 0 29 4; };驱动加载之后/dev/uio0就是对应的设备节点mmap之后就能读写寄存器了。中断处理在用户空间做用read()阻塞等待中断。上位机通信我用的是千兆以太网PS端的MAC控制器直接支持。协议用的是UDP因为实时性要求高TCP的重传机制反而会引入不确定延迟。数据包格式自定义每包1024个采样点加上时间戳和通道号。3.5 系统联调与性能测试联调的时候我按信号链的顺序逐级验证。先用信号发生器给一个单音信号看ADC采到的数据对不对。然后在PL端加一个ILA集成逻辑分析仪抓DDC输出的波形确认混频和抽取正确。再抓同步模块的环路锁定指示确认Costas环能锁上。性能测试主要看几个指标一是EVM误差向量幅度反映解调质量。我测下来QPSK的EVM在2%左右满足一般通信要求。二是吞吐量PL端到PS端的数据率实测能到800MB/s四通道12.5MSPS×16位×4800Mbps余量充足。三是延迟从ADC输入到PS端收到数据实测延迟在50微秒左右主要来自CIC和FIR的群延迟。注意事项ILA抓波形的时候要注意采样深度和触发条件。我一开始设的采样深度太浅抓不到完整的包后来改成4096深度才够用。触发条件用包头的源地址做触发这样能抓到特定通道的数据。4. 常见问题与排查技巧实录4.1 NOC丢包与数据错位NOC调试初期遇到最多的问题就是丢包和数据错位。丢包的原因通常是FIFO溢出数据错位的原因通常是包头解析错误。排查的时候我一般先看路由节点的FIFO满标志如果满了说明下游处理不过来要么降低数据率要么加深FIFO。数据错位更隐蔽一些。有一次我抓到的数据包长度对不上查了半天发现是包头里的长度字段和实际数据长度不一致。原因是我的包生成模块在计算长度的时候把包头本身也算进去了但解析的时候没算。这种问题用ILA抓一次包就能定位关键是触发条件要设对。4.2 时序不收敛的常见原因复旦微FPGA的时序收敛和Xilinx比确实要难一些主要是工具链的优化策略不同。我遇到过的时序问题主要有三类一是跨时钟域路径没约束好工具按同步路径分析结果不满足。二是DSP48的级联路径太长需要插入寄存器。三是BRAM的输出到DSP的输入路径太长需要打拍。跨时钟域的问题用set_clock_groups -asynchronous解决但要注意只对真正的异步时钟用同源时钟不能用。DSP48级联的问题可以在综合选项里开retiming让工具自动插入寄存器。BRAM到DSP的路径可以手动加一级流水线寄存器。4.3 PS端Linux启动失败排查PS端Linux启动失败的原因比较多常见的有DDR配置不对、设备树写错、BOOT.bin生成有问题。排查的时候先看串口有没有输出如果完全没有输出大概率是DDR配置或者FSBL的问题。如果有输出但卡在某一步看内核打印的错误信息。我遇到过一次启动卡在“Starting kernel...”之后查了半天发现是设备树里的PL端地址映射和实际不符。PL端的AXI外设地址是在Procise里配的设备树里要一致。这个细节容易忽略因为PL端逻辑本身能跑但Linux访问不到。4.4 常见问题速查表现象可能原因排查方法解决方法NOC丢包FIFO溢出看FIFO满标志加深FIFO或降低数据率数据错位包头解析错误ILA抓包对比检查长度字段计算时序不收敛跨时钟域未约束看时序报告set_clock_groupsDSP路径太长级联过多看关键路径开retiming或手动打拍Linux启动失败DDR配置错误看串口输出检查FSBL配置设备树地址不对PL地址映射不符对比Procise配置修改设备树reg属性Costas环不锁环路参数不当看环路滤波器输出降低积分增益ADC数据异常时钟抖动大测时钟相噪换低抖动时钟源4.5 几个独家避坑技巧第一个技巧是关于NOC路由表的。路由表我一开始是写死的后来改成PS端可配置。改的时候要注意路由表更新的时候要暂停NOC不然正在传输的包会走错路由。我的做法是加一个soft_reset寄存器PS端更新路由表之前先置位更新完再清零。第二个技巧是关于CIC滤波器的。CIC的积分器在高速时钟下容易成为关键路径因为积分器是反馈结构加法器的输出又反馈到输入。我的做法是把积分器拆成两级流水线虽然多了一个时钟周期延迟但时序好收敛很多。第三个技巧是关于PS-PL数据交互的。AXI-Stream的tready和tvalid握手如果处理不好容易死锁。我的做法是在PL端加一个超时计数器如果tready持续为低超过一定周期就主动丢弃当前包并置错误标志。这样至少不会把整个系统卡死。第四个技巧是关于定点数溢出的。软件无线电里的信号幅度变化范围很大定点数运算很容易溢出。我的做法是在每个乘法器后面加一个饱和截断逻辑溢出的时候输出最大值而不是回绕。回绕会导致信号完全失真饱和截断只是削顶对解调影响小得多。5. 工具链与IP选型的一些体会5.1 复旦微Procise工具的使用感受Procise的界面布局和Quartus比较像用过Quartus的人上手不难。综合和布局布线的速度还可以FMQL45T900这个规模的器件一次全流程大概20分钟。时序分析报告比较详细关键路径能追到具体的逻辑级。和Vivado比Procise的IP生态确实弱一些。很多常用的IP核比如FFT、FIR编译器Procise里没有现成的需要自己写或者从别处移植。不过软件无线电的基带处理模块相对固定写一次就能复用问题不大。调试工具方面Procise的ILA和Vivado的ILA功能差不多抓波形、设触发条件都支持。但ILA的采样深度受BRAM资源限制深度设大了会占很多BRAM。我的做法是只在关键节点放ILA而且调试完之后及时删掉不然影响时序。5.2 第三方IP的移植注意事项我从Xilinx平台移植过一些IP到复旦微的平台主要工作是改原语和约束。Xilinx的DSP48原语叫DSP48E1复旦微的叫DSP48端口名字不一样但功能类似。BRAM的原语也不一样Xilinx是RAMB36E1复旦微是BRAM36。改的时候要仔细对照手册不能想当然。时钟相关的原语差异最大。Xilinx的MMCM和PLL原语在复旦微里对应的是PLL和DLL配置参数和锁定指示的用法都不一样。我建议时钟相关的逻辑尽量用工具生成的IP不要手写原语这样移植的时候改起来方便。5.3 软硬件协同设计的流程软硬件协同设计的关键是接口定义要早。我在项目开始的时候就定好了PS和PL之间的寄存器映射和数据包格式后面PL端和PS端可以并行开发。PL端用ILA验证数据流PS端用模拟数据验证协议栈最后联调的时候一次通过。寄存器映射我定义了一个头文件PL端和PS端共用。PL端用Verilog的definePS端用C的#define两边保持一致。数据包格式也是类似的做法定义一个结构体两边都按这个结构体来打包和解包。实操心得联调的时候一定要先做回环测试。我在PL端加了一个回环模式ADC数据直接环回到DAC不经过任何处理。这样能验证整个数据通路是否正常排除处理逻辑的问题。回环测试通过之后再逐级打开处理模块定位问题就快很多。6. 后续扩展方向这个平台目前实现了基本的软件无线电收发功能后续可以扩展的方向不少。一是增加调制解调方式目前只有BPSK和QPSK可以加16QAM、64QAM。二是增加信道编码目前是硬判决输出可以加Viterbi译码或者LDPC译码。三是提高NOC的吞吐量目前是2×2网格可以扩展到4×4支持更多通道。NOC的扩展还有一个方向是加入QoS机制。目前的轮询仲裁对所有包一视同仁但控制命令的优先级应该比数据高。可以在包头里加优先级字段仲裁器根据优先级调度。这个改动不大但能明显改善控制响应速度。PS端的扩展主要是协议栈。目前只做了简单的UDP收发可以加TCP/IP协议栈、加Web服务器做远程配置、加数据库做数据记录。这些在Linux下都有现成的库开发量不大。功耗优化也是一个方向。复旦微的PSoC支持动态功耗管理可以根据负载调整PL端的时钟频率。软件无线电的负载变化很大空闲的时候降频能省不少电。这个功能我还没仔细调后续有时间会试试。我个人在这个项目里最大的体会是国产FPGA做软件无线电技术上完全可行但工具链和IP生态的差距需要用自己的工作量来补。NOC互连的设计是难点也是亮点把通信网络的思路用到片上互连虽然增加了设计复杂度但换来了更好的可扩展性和并行度。如果你也在做类似的项目建议先把数据流和接口定义清楚PL端和PS端并行开发联调的时候从回环测试开始逐级验证这样能少走很多弯路。
返回列表