
1. 板卡定位与整体架构拆解工控机箱里那条空了很久的 PCIe x1 插槽是这张卡存在的全部理由。需求本身不复杂主机侧要把一批高速采样数据搬进来卡上要跑实时闭环运算算完的结果还得在毫秒级内回送给主机。用纯软件在 PC 上做这套东西抖动太大用纯单片机做带宽又不够。所以就有了这么一套组合PCIE8311业内更常见的写法是 PEX8311PLX 被 Avago 收编后丝印常标 PEX8311-AA66BC F负责 PCIe 到本地总线的翻译FPGA 在本地总线上做缓冲和时序适配TMS320F28335 接过数据做浮点运算硬件部分全部用 Protel 99 SE 出原理图和 PCB。这套架构的价值在于各司其职。8311 是硬件协议桥你不写一行 PCIe 协议代码主机装好驱动就能像访问内存一样读写卡上的寄存器FPGA 是万能胶水哪里时序对不上、哪里需要 FIFO 缓冲、哪里要打拍跨时钟域都塞给它F28335 是 C28x 内核加浮点单元150MHz 主频跑 PID、锁相、坐标变换这类运算比 FPGA 写定点快得多开发周期也短。三颗料加起来成本可控资料齐全适合做小批量、定制化的数据采集与控制卡。适合看这篇的人分三类一类是刚接触 PCIe 板卡设计、想知道桥片方案怎么落地的硬件工程师一类是要在 Protel 99 SE 这种老工具上完成高速板设计的从业者还有一类是手上已经有类似板子、调试卡在枚举或本地总线读写不通的同行。下面我会把架构取舍、原理图细节、PCB 叠层与等长、以及打样回来之后的排查顺序一条条拆开讲参数和计算过程都给出来能直接抄的部分我会写清楚。1.1 三颗芯片到底谁干什么活先把分工说明白不然后面原理图会画得很乱。PCIE8311 站在最前面它的上游是 PCIe x1 链路2.5GT/s8b/10b 编码单向理论带宽 250MB/s下游是一组本地总线引脚包括地址、数据、读写选通、片选、等待握手。主机发过来的 Memory Read/Write TLP会被它翻译成本地总线上的一次读写周期。它还内置 DMA 引擎可以自己把本地侧的数据成块推到主机的内存里不需要 CPU 介入搬运。FPGA 站在中间本地总线这一侧它是从设备要响应 8311 发起的读写DSP 那一侧它又变成主设备或者更准确说是被 DSP 访问的从设备。这种两头从、中间当缓冲的角色是 FPGA 最舒服的位置。它的核心工作只有三件把 8311 写进来的数据按帧存进 Block RAM 组成的 FIFO把需要回传的数据准备好让 8311 读走以及给 DSP 提供一个简单到不能再简单的寄存器窗口。TMS320F28335 站在最后一环它通过自己的 XINTF 外部并行接口去读写 FPGA 里的寄存器组和 FIFO。28335 有 6 个 DMA 通道、16 路 12 位 ADC、多个 ePWM 模块如果你做的是电机驱动或电源控制它的外设基本够用不用再挂外围。它和 FPGA 之间的数据量通常不大——控制环一般几 kHz 到几十 kHz每个周期几百字节顶天了XINTF 完全扛得住。把这三者的分工写成一句话8311 管协议FPGA 管缓冲和适配28335 管运算。任何一颗的位置放错整块板子的调试难度都会翻倍。1.2 数据流与带宽预算怎么算带宽这件事必须先算清楚否则后面选 FIFO 深度、定本地总线时序都是瞎猜。我一般按三个层次算账。第一层是 PCIe 链路。x1 Gen12.5GT/s 乘上 8/10b 编码效率物理层能给到 250MB/s 单向。扣掉 TLP 头、DLLP、ACK/NAK 这些协议开销实测 DMA 搬移稳定在 180MB/s 到 200MB/s 之间这个数字后面的设计要拿它当上限。第二层是 8311 的本地总线。32 位数据宽度本地时钟最高 66MHz峰值 264MB/s。看似够用但实际一次读写的有效周期不是每个时钟都能传数据地址建立、等待状态、总线翻转都要吃掉时间。工程上按 60% 到 70% 的效率估有效带宽大约 160MB/s 到 180MB/s。这意味着本地总线不会成为卡脖子的那一环除非你的 FPGA 逻辑写得特别拖沓。第三层是 DSP 的 XINTF。这是真正的瓶颈。28335 的 XINTF 数据线 16 位XTIMCLK 由 SYSCLKOUT 分频典型配置下是 75MHz。一个读写周期走前导 激活 尾随三段我通常配成 121也就是 4 个 XTIMCLK换算成时间是 53.3ns。一次传 2 字节理论速率只有 37.5MB/s实际带上握手和地址切换能跑到 25MB/s 就算不错。数字摆出来结论就清楚了大流量的原始采样数据绝对不能绕道 DSP。合理的数据通路是采集前端 - FPGA FIFO - 8311 本地总线 - PCIe DMA - 主机内存DSP 只从 FPGA 的另一个小 FIFO 里取它需要的控制量算完把结果写回另一个寄存器窗口再由 FPGA 打包好等着 8311 来读最后走 DMA 返回主机。这样 DSP 的 25MB/s 只管控制回路够用得很。我做这块板子的时候专门在 FPGA 里划了三块独立的 Block RAM一块 8KB 的上行大 FIFO一块 2KB 的下行 FIFO一块 512 字节的控制/状态寄存器组。三块空间互不重叠8311 和 DSP 各自访问自己的区域靠简单的读写指针同步避免了两边抢同一块内存的麻烦。1.3 为什么不用 FPGA 自带的 PCIe 硬核肯定有人要问现在很多 FPGA 自带 PCIe 硬核为什么还要多挂一颗 8311。这个问题我当初也纠结过很久最后选桥片的理由有三条。第一条是器件门槛。带 PCIe 硬核的 FPGA比如 Artix-7、Cyclone V 的某些型号价格和封装都比普通 FPGA 高一截很多还是 BGA 加高速收发器四层板基本别想六层起步走线规则也复杂。而 8311 方案里FPGA 只需要处理本地总线的并行信号普通 Cyclone IV 或者 Spartan-6 的 TQFP 封装就够了四层板能搞定打样成本差好几倍。第二条是协议栈的坑。PCIe 硬核虽然帮你做了物理层和链路层但从事务层到驱动这一整套配置空间、BAR、MSI 中断、DMA 描述符还是要自己写。8311 的好处是它把这些都做成了硬件寄存器主机的操作系统认它就是一张标准卡驱动开发量小得多。对于小批量项目省下来的开发时间比省下来的物料钱值钱。第三条是复用性。8311 的本地总线是标准并行接口你今天接 FPGA明天想换一颗别的处理器本地总线逻辑基本不用大改。硬核方案一旦换了 FPGA 型号整个 IP 配置和约束都得重来。当然桥片方案也有代价。8311 是 Gen1 器件只能跑 2.5GT/s如果你想做 5GT/s 甚至更高的带宽这条路走不通。另外它需要一颗串行 EEPROM 来存配置多一个物料多一个失效点。所以选型的时候要拿需求说话需求在 200MB/s 以内、要控制成本、要快出样就用桥片需求超过 500MB/s直接上带硬核的 FPGA别犹豫。2. 关键器件与总线接口细节解析架构定下来之后接下来就是一颗颗把接口抠清楚。这一节讲的东西直接决定原理图上每个引脚怎么接值得花时间。很多人原理图画得漂漂亮亮板子回来死活枚举不了问题往往就出在这里某根线上拉错了、某个电容放错边了。2.1 PEX8311 的本地总线模式选型8311 的本地总线支持三种工作模式分别叫 C mode、J mode 和 M mode选错了整个 FPGA 侧的逻辑都要推倒重来所以我把它放在最前面说。C mode 是地址和数据分离的模式地址线走 LA数据线走 LD另外有 LBE 字节使能、LWR 写选通、LRD 读选通、LCS 片选、LRDY 握手。它最像一片普通的异步 SRAMFPGA 侧只需要实现一个同步状态机去采样这些信号就能完成读写。这是我最推荐的模式逻辑简单、时序清晰、调试时用逻辑分析仪抓波形一眼就能看懂。J mode 把地址和数据复用到同一组线上一个周期里先送几个地址节拍再送数据节拍引脚数量省很多。代价是 FPGA 侧要精确跟随它的复用节拍时序窗口很紧调试起来比较费劲。只有在 PCB 布线空间实在紧张、或者要兼容老的 PCI 桥设计时才考虑。M mode 提供了另一套控制信号组合偏向特定处理器总线的对接。它在这类项目里用得最少除非你手里有明确要求。我这张板子选的是 C mode。原因很直接FPGA 的 IO 数量对 TQFP144 或者 208 封装的器件来说完全够本地总线 32 位地址加 32 位数据再加控制线加起来不到 80 根没必要为了省线去折腾复用模式。而且 C mode 下本地总线的有效带宽实测更稳定因为每个节拍都在传数据没有地址节拍浪费。本地时钟这块要注意LCLK 可以由 8311 输出给 FPGA也可以从外部送进 8311具体由配置决定。我一般让 8311 输出FPGA 用它做本地总线侧的采样时钟这样两边的时钟同源不需要额外做跨时钟域。频率上我设成 50MHz比 66MHz 保守一点留出足够的建立保持余量第一次打样先求稳跑通了再往上提。2.2 8311 的配置 EEPROM 与枚举前状态8311 上电之后在主机枚举它之前它得先知道自己是谁。厂商 ID、设备 ID、类代码、BAR 空间大小这些信息要么从一颗串行 EEPROM 里加载要么用芯片内部的默认值。这两条路差别很大。没有 EEPROM 的时候8311 会用默认的厂商 ID 和设备 ID。主机枚举时看到的是一张无名卡你需要自己在驱动里根据这两个默认 ID 去匹配或者用工具把配置写进寄存器。做原型验证可以这么干但正式产品不行因为默认值有可能和其他设备冲突用户装驱动的时候也会看到一堆看不懂的设备名。挂上一颗 93C46 或者兼容的串行 EEPROM 之后上电时 8311 通过 EECS、EECLK、EEDI、EEDO 四根线把配置读进来主机枚举时就能看到一张有正经厂商 ID、设备 ID、子系统 ID 的卡。这里有个细节EEPROM 里的配置字有固定的格式和校验写错一位可能导致 8311 加载失败然后退回默认值表现出来就是我明明烧了 EEPROM怎么设备 ID 还是默认的。排查的时候先用示波器看 EECLK 有没有波形再看 EEDO 上有没有数据回来能快速定位是硬件还是数据问题。原理图上四根 EEPROM 线要按手册加必要的上拉或下拉具体哪几根需要、阻值多少一定要对着 8311 数据手册里那一章的参考电路来别凭经验套。我自己就吃过一次亏把 EEDO 的方向搞反了结果 EEPROM 永远读不出来板子白等了两周。另外 PRSNT1# 和 PRSNT2# 这两根存在检测脚不能悬空x1 卡上要把它们连起来主机才知道槽里插了卡。这个错误很低级但真的常见尤其是自己画封装的时候漏掉这两个 pin。2.3 FPGA 与 8311 之间的握手与 FIFO 划分FPGA 侧对着本地总线要实现一个从设备核心状态机在等 LCS 有效、LRD 或 LWR 有效然后根据地址把数据落到对应的存储空间或者从存储空间取出来。听起来简单坑主要在握手信号 LRDY 的处理上。8311 发起一次本地总线访问之后它会等 FPGA 拉高 LRDY 表示数据有效。如果你的逻辑响应太慢超过了 8311 内部的等待上限这次访问就会超时并且可能挂起。所以 FPGA 一侧在读 FIFO 的时候要做好数据还没准备好就插入等待的逻辑比较稳妥的做法是内部用小容量同步 FIFO 加一个预取机制让读操作尽量在一个或两个本地时钟内就给出数据别让 8311 干等。地址空间划分我是这么做的低 512 字节是控制寄存器区读写都直接映射到寄存器再往上一段是上行 FIFO 的读端口8311 主机侧读这个地址就把 FPGA 攒好的数据取走再往上一段是下行 FIFO 的写端口主机写数据进来就进了 FPGA 的缓冲区等着转发给 DSP 或者直接驱动片外接口。三个区域的基地址在 FPGA 里做成参数改起来方便。FIFO 深度要结合带宽算。假设主机侧驱动每 1ms 来收一次数据上行速率按本地总线能给的 100MB/s 算1ms 内能攒 100KB那 8KB 的 FIFO 根本不够会溢出。实际做的时候要么把 FIFO 做大要么用 8311 的 DMA 让它持续搬不要让主机轮询。我一般优先用 DMAFIFO 只做速率不匹配时的缓冲深度取 4KB 到 16KB看具体采样率。2.4 F28335 的 XINTF 时序配置DSP 这一侧最容易出问题的地方就是 XINTF 的时序。28335 的 XINTF 分成 Zone 0、Zone 6、Zone 7 三个区每个区有独立的地址范围和时序寄存器。我一般把 FPGA 映射到 Zone 6因为它的地址空间够大配置起来也灵活。时序寄存器有几个关键字段XWRLEAD、XWRACTIVE、XWRTRAIL 管写周期XRDLEAD、XRDACTIVE、XRDTRAIL 管读周期还有一个 X2TIMING 位决定这些参数是按 XTIMCLK 的整数倍算还是按 SYSCLKOUT 的倍数算。XTIMCLK 本身由 XINTCNF2 寄存器里的 XTIMCLK 位决定可以配成 SYSCLKOUT 或者 SYSCLKOUT/2。150MHz 主频下配成 SYSCLKOUT/2 就是 75MHz一个周期 13.3ns。下面是 Zone 6 一份我常用的配置可以直接参考// Zone 6 配置16 位数据总线XTIMCLK SYSCLKOUT/2 75MHz EALLOW; XTiming6.bit.XWRLEAD 1; // 写前导 1 个 XTIMCLK XTiming6.bit.XWRACTIVE 2; // 写激活 2 个 XTIMCLK XTiming6.bit.XWRTRAIL 1; // 写尾随 1 个 XTIMCLK XTiming6.bit.XRDLEAD 1; // 读前导 1 个 XTiming6.bit.XRDACTIVE 3; // 读激活 3 个 XTiming6.bit.XRDTRAIL 1; // 读尾随 1 个 XTiming6.bit.X2TIMING 1; // 时序按 XTIMCLK 的整数倍 XBANK.bit.BANK6 1; // 使能 Zone 6 EDIS;按这个配置算写周期是 121 等于 4 个 XTIMCLK53.3ns读周期是 131 等于 5 个66.7ns。这个速度对 FPGA 侧的组合逻辑来说很宽松不用担心建立时间不够。这里有个经验第一次调板子的时候先把 LEAD 和 ACTIVE 都往大了配比如读激活给到 8 个周期等读写通了、数据也对了再一组一组往下压压到出错就回退一档。这种先慢后快的调法比一上来就冲极限省时间得多。2.5 时钟树与复位网络时钟这块要单独拎出来说因为它是最容易埋雷的地方。整块板子上的时钟源有三类主机通过金手指送过来的 100MHz 差分参考时钟PCIe 专用板上的晶振或者时钟发生器给 FPGA 和 DSP 用的时钟以及 8311 输出的本地总线时钟。100MHz 参考时钟从金手指的 A13/A14 进来是 HCSL 电平的差分信号。接到 8311 的参考时钟输入之前要按手册加交流耦合电容和端接电阻具体值一定要查手册不要照搬别人的设计因为不同批次的芯片对端接要求略有差异。走线必须按差分对处理阻抗按 85Ω 差分控制两根线严格等长。FPGA 的系统时钟我用一颗 50MHz 的有源晶振直接进 FPGA 的全局时钟引脚。28335 这边用一颗 30MHz 的无源晶振接在 X1/X2 上内部 PLL 倍频到 150MHz。为什么要给 DSP 单配一个晶振而不是从 FPGA 分频给它因为 DSP 的 PLL 对输入时钟的抖动和频偏有要求从 FPGA 的普通 IO 分频出来的时钟抖动大容易让 PLL 锁不稳或者失锁。多花一颗晶振的钱省下的是几天的调试时间。复位这块主机侧送过来的 PERST# 信号必须接进 8311 的复位脚这是 PCIe 规范要求的。板上还要有一条全局复位网络把 PERST# 和板上的上电复位芯片输出做或运算一起送到 8311、FPGA 和 DSP 的复位脚。为什么要或因为主机可能在板子已经上电之后才发起复位这时候不能只靠上电复位芯片得让主机的复位信号也能生效。复位信号的走线要注意两点。一是别串太长尤其是到 FPGA 和 DSP 的复位脚尽量走短走直。二是如果板上有多处需要复位用缓冲器扇出不要让一个复位源直接驱动七八个负载那样边沿会变缓容易在阈值附近停留导致复位不干净。我见过因为复位边沿太慢、芯片反复进复位又出来的案例现象是板子时好时坏换了半天器件才发现是复位走线的问题。3. Protel99SE 原理图设计实操Protel 99 SE 是个老工具界面停留在上世纪但它的原理图和 PCB 引擎对付这种多层高速板完全够用很多老工程师用顺手了根本不想换。这一节讲的是怎么在这套工具里把工程组织好、把引脚映射做对、把网表导干净。3.1 DDB 工程组织与库文件管理Protel 99 SE 用的是 .ddb 数据库文件一个 DDB 里面可以放原理图、PCB、库文件、网表好几类文档像一个小型的文件系统。这个组织方式有好处也有坑。好处是所有东西在一个文件里拷贝给别人不会漏文件。坑是 DDB 有大小限制而且文件一多就容易变慢甚至损坏。我的做法是一个 DDB 只装一个项目原理图按功能分区拆成多张图纸PCB 和原理图放在同一个 DDB 里库文件单独建一个 DDB 存着用到的器件从库里调进来之后就地化处理别用引用外部库的方式那样换个电脑就找不到器件了。多图纸设计的时候Protel 99 SE 靠图纸符号和端口来做层级连接。顶层图纸只放几个方块和连线每个方块代表一张子图。这种做法在器件多的时候特别有用因为一张 A3 图纸塞不下几百个网络标号分成几块之后每张图都能看得清。库文件管理上我建议给这个项目建四个专用库一个原理图符号库放 8311、FPGA、DSP 这些大器件一个 PCB 封装库放对应的 Footprint一个放阻容器件一个放接插件和金手指。听起来麻烦但等你画第二版、第三版的时候就知道好处了——直接改库所有调用处自动更新。有个细节要提醒Protel 99 SE 的库不区分符号和封装的绑定关系你得在原理图器件的属性里手动填 Footprint。填错了或者漏填导网表的时候不会报错但更新到 PCB 时那颗器件就会消失或者变成一个小方块。所以每建一个器件立刻把 Footprint 属性填上别攒着。3.2 引脚映射与网络标号规范8311 和 FPGA 这类器件的引脚动辄几百个光是把引脚号理清楚就是个大工程。我的做法是先在数据手册里把所有引脚按功能分组画符号的时候也按功能分组排布这样原理图上连线的时候一眼就能看出哪些是总线、哪些是控制、哪些是电源。符号的引脚号必须和实际封装一一对应这一条没得商量。有人图省事符号上写 LS1 到 LS32实际封装是 1 到 32结果连出来的网表全错。我的习惯是符号引脚号的字体调小一点但一定要显示出来画完连完线之后随机抽查十个引脚号对着手册核一遍。这个动作花五分钟能避免一次打样报废。网络标号要有命名规范不然过一个月自己都看不懂。我一般这么定电源类用3V3、1V8、1V9、12V前面带正号一眼区分地统一用GND模拟地和数字地如果要分割用AGND和DGND在单点连接处用磁珠或零欧电阻桥接差分信号用PET0_P、PET0_N、PER0_P、PER0_N后缀区分正负本地总线用LB_ADDR[31..0]、LB_DATA[31..0]、LB_LWR、LB_LRDDSP 接口用XINTF_D[15..0]、XINTF_A[18..0]、XZCS6总线命名用方括号而不是圆括号Protel 99 SE 对总线入口的识别是认方括号的圆括号会被当成普通字符网络就连不上。还有一点跨图纸连接靠端口图纸内部的连接靠网络标号。端口名字和网络标号可以重名但含义不同画的时候要心里有数。我一般在端口名字前加一个前缀比如P_LB_ADDR0避免和图纸内部的标号混淆。3.3 电源域划分与去耦电容排布这块板子的电源不算复杂但要理清楚。金手指送进来的有 12V 和 3.3V还有可选的 3.3Vaux。板内需要的电压有8311 要 1.0V 或 1.2V 的核心电压加 3.3V IOFPGA 按型号可能是 1.2V 核心加 3.3V IO 或者 2.5V IODSP 要 1.9V 核心150MHz 时典型值加 3.3V IO。算一下电流需求。8311 核心电流大概几百毫安FPGA 看逻辑规模普通 Cyclone 几百毫安28335 核心电流在 150MHz 下大约 200 多毫安。加起来 1.9V 这一路要准备 1A 以上余量。3.3V 这一路要算上所有 IO 和外围准备 2A 比较稳。金手指给出的 3.3V 电流是有限的一定要查 PCIe CEM 规范里对 x1 插槽的供电定义别想当然地以为能随便抽几安培。如果算下来不够就得从 12V 通过板上的 DC-DC 转出 3.3V而不要把负载全压在金手指的 3.3V 上。去耦电容的排布是有讲究的不是每个电源脚旁边塞一个 0.1uF 就完事。正确的做法是分三层大容量的电解或钽电容放在电源入口负责低频储能一般 100uF 到 470uF中容量的陶瓷电容 10uF 放在每个电源域附近负责中频小容量的 0.1uF 和 0.01uF 放在每个芯片电源脚旁边负责高频而且要走最短的路径到地。原理图上画的时候我习惯把去耦电容单独画成一组每组的电源和地用一个短网络连到主电源然后在 PCB 阶段再把这组电容拆到各个芯片旁边。这样原理图看起来很干净PCB 上也不会漏。有个坑要避免去耦电容的地回路。0.1uF 的电容要放在芯片电源脚旁边而且它的接地端要就近打过孔到地平面走线总长度控制在几毫米以内。如果电容放在离芯片两厘米的地方那这颗电容对高频基本没用等于白放。3.4 网表导出前的自检清单原理图画完到导网表之间有一个自检动作必不可少。我整理了一份清单每次出图前都过一遍。检查项具体动作常见后果电源脚是否全部连接用 View 里的电源网络高亮逐个核对漏接导致芯片不工作未连接引脚是否有标记悬空脚打 no-connect 叉ERC 报警或误接Footprint 是否填全导出 BOM 逐个核对封装名更新 PCB 时器件丢失差分对命名是否规范检查正负后缀布线时无法识别总线入口是否正确检查方括号和总线分支网络连不上器件的 Value 是否填电阻电容的阻值和容值BOM 无法采购特殊引脚上拉下拉复位、使能、模式选择脚上电状态不确定导网表的时候Protel 99 SE 会输出一个错误报告里面主要是找不到器件封装和网络只有一个节点这两类。前者是封装名写错或者库没加载后者是某个网络只连了一个引脚说明有断线或者标号打错。这两类错误必须清零再往 PCB 走带着错误更新 PCB后面会很难查。我再分享一个小技巧导网表之前把原理图全选复制一份存成_backup后缀的图纸。Protel 99 SE 有个毛病重复的网络标号或者重复的器件标号有时候不报错改起来还要一张一张图找。留一份备份出问题直接回滚比修复快。4. PCB 叠层设计与 PCIe 高速布线原理图阶段犯的错还能改PCB 一旦投板错了就是钱和时间。这一节是整块板子最考验功力的地方尤其是 PCIe 那对差分线和金手指区域的处理。4.1 四层还是六层叠层方案对比先说结论如果只是做 PCIe x1 Gen1四层板完全能做但有条件。条件是你的走线层能保证差分线全程有完整的参考平面而且差分线不跨越平面分割。如果做不到就老老实实上六层。四层板的叠层我一般是这么排的第一层 Top放 PCIe 差分对、本地总线、晶振和关键信号第二层 GND完整地平面不做任何分割第三层 PWR电源平面可以分割成 3.3V、1.9V、1.2V 几个区域第四层 Bottom放电源走线、慢速控制和外围器件这个排法的关键点是第二层必须是完整地平面第一层的所有高速信号都参考它。第四层信号参考的是第三层的分割电源平面所以第四层只能走慢速信号不能走任何对阻抗敏感的东西。六层板的排法第一层 Top高速信号第二层 GND完整地第三层 Signal本地总线和 DSP 接口第四层 PWR电源平面第五层 GND完整地第六层 Bottom低速信号和电源走线六层的好处是第三层的信号参考第二层地、第六层参考第五层地所有信号层都有干净的地参考阻抗控制容易做到 ±10% 以内。代价是成本大概翻倍。我这边第一版用的是四层因为只是验证功能。如果确认要量产我会改六层因为四层板在 66MHz 本地总线上跑长线的时候串扰和反射的余量确实比较紧张实测眼图比六层差不少。4.2 差分阻抗与等长怎么算PCIe 的差分阻抗要求是 85Ω注意不是常见的 100Ω。这个数字写在 PCIe 电气规范里一定要按 85Ω 控按 100Ω 做出来的线插上去可能也能用但眼图余量会变小高温或者长线情况下容易出错。先说单端阻抗。FR4 材料介电常数取 4.3如果用四层板Top 到第二层地平面之间的介质厚度假设是 0.2mm约 8mil那 50Ω 单端微带线的线宽大概在 0.28mm 左右。这个数要用板厂的阻抗计算器或者专门的工具算我这里给的是数量级实际投板前必须让板厂按他们的工艺参数算一遍因为不同板厂的半固化片厚度和介电常数都不一样。差分阻抗 85Ω 的时候在 0.2mm 介质、50Ω 单端的前提下两根线间距大概等于线宽比如线宽 0.2mm、间距 0.2mm算出来在 80Ω 到 90Ω 之间。具体数值必须让板厂确认我给的方法是把叠层参数和线宽间距报给板厂让他们回一个阻抗确认单这个流程走一遍就心里有数了。等长这块分两个层次。差分对内部的两根线P 和 N必须严格等长Gen1 的要求是偏差控制在 5mil 以内做到 2mil 到 3mil 更好。为什么要这么严因为 P 和 N 之间的时间偏差会直接吃掉眼图的水平张开度。算一笔账FR4 微带线的传播延迟大约是 150ps/inch5mil 就是 0.75ps 的偏差相对于 Gen1 的 400ps 单位间隔来说占比很小。如果偏差到 50mil就是 7.5ps虽然看着还能接受但再叠加上其他抖动源就危险了。不同差分对之间比如 TX 对和 RX 对不需要等长它们是独立的链路各走各的。REFCLK 差分对内部同样要等长要求和对内偏差一样。走线的时候还有几个硬性要求差分线全程保持等间距不要为了绕障碍把一根线拉开拉开的地方阻抗会突变差分线要参考完整地平面绝对不能跨越平面缝隙差分线尽量少打过孔一个过孔引入的寄生电容会让阻抗局部下降非打不可的时候两根线要对称打差分线两侧和邻近信号保持 3 倍线宽以上的间距减少串扰差分线走线拐弯用 45 度或者圆弧别用直角如果一定要加 AC 耦合电容100nF 是标准值封装用 0402 或者 0201。电容要放在发送端而且两个电容的摆放要对称焊盘之间的走线长度差要补回来。加电容的地方阻抗会有一个小的不连续所以电容要尽量靠近发送源把不连续留在信号能量还比较强的地方反而不是好事实际上更靠近接收端的容差更大具体看手册建议。4.3 金手指区域的处理要点金手指是这块板子和主机唯一的机械和电气接口处理不好整块板子就是块废板。先讲尺寸。PCIe x1 卡的板厚是 1.6mm公差 ±0.15mm。金手指的倒角有明确要求前端的 45 度倒角是为了插拔时顺畅长度和角度都要按规范来。金手指的镀金厚度也有要求一般 0.5um 到 1um找板厂做的时候说清楚是PCIe 金手指卡他们就知道该怎么处理。x1 槽的引脚分布我列一下关键几根方便对着画封装面引脚信号说明AA13REFCLK100MHz 参考时钟正AA14REFCLK-100MHz 参考时钟负AA16PERp0接收差分正AA17PERn0接收差分负BB14PETp0发送差分正BB15PETn0发送差分负A/B多处12V / 3.3V / GND电源和地这里要特别注意方向。从卡的角度看PET 是卡发给主机的PER 是卡从主机接收的。所以 8311 或者 FPGA 的发送差分对要接到 B14/B15接收差分对接到 A16/A17。接反了链路训练不起来而且从现象上看不出来是谁的问题只能上示波器量。金手指区域的铺铜要小心。金手指焊盘正下方和周围不要铺铜也不要有过孔否则插拔的时候铜皮可能被蹭掉导致短路。金手指焊盘和金手指区域之间要保持足够的间距具体数值按规范来。ESD 保护器件要不要加Gen1 速率下可以考虑加超低电容的 ESD 阵列电容要小于 0.5pF否则会破坏阻抗。但我见过不少设计为了省事直接不加靠金手指本身和机箱的地来扛。如果产品要用在恶劣环境加上比较稳妥如果是内部设备不加也能过。4.4 本地总线与 DSP 侧的布线策略本地总线的速率比 PCIe 低50MHz 到 66MHz 的并行总线但它的线数量多32 位数据加 32 位地址再加控制线八九十根线挤在一起串扰和地回流的问题比 PCIe 还麻烦。我的处理原则是分组。把数据线、地址线、控制线分成三组每组内部的线尽量走同一层、走等长组与组之间保持足够的间距最好中间夹一根地线做隔离。数据线的等长要求不严偏差控制在 500mil 以内一般没问题因为 50MHz 下单位间隔是 20nsFR4 里 500mil 只有 75ps占比很小。地址线和数据线如果走同一层尽量让它们正交也就是一层横着走、一层竖着走减少层间耦合。四层板里第一层和第四层正好可以这么用第一层走横向的地址线第四层走纵向的数据线中间隔着地和电源平面串扰很小。控制线像是 LWR、LRD、LCS 这些是时序的关键路径要和数据线一起走保证它们的到达时间差在可控范围内。我一般把控制线和数据线放在同一层走线长度差控制在 300mil 以内。DSP 侧的 XINTF 接口速率更低可以放宽要求但有个例外是 XREADY 信号。XREADY 是 FPGA 用来告诉 DSP数据好了的握手信号如果这条线走得太长延迟超过了 XINTF 的采样窗口DSP 就会读到错误的数据。所以 XREADY 要尽量短而且要走在数据线旁边保持类似的走线环境。时钟线是另一类要单独照顾的信号。LCLK 从 8311 送到 FPGA这条线要包地长度要控制不要和任何高速数据线平行长距离走。有条件的话用第三层或者第六层的专用区域走时钟上下都有地平面参考。4.5 电源平面与地回流地回流这件事很多人只在高速设计里听过其实在并行总线上同样重要。信号从驱动器出发经过走线到达接收端电流还得从接收端的地回到驱动器的地这个回流路径如果被切断信号就会绕远路产生额外的电感和辐射。保证回流路径的办法很简单让每个信号层都有一个相邻的完整地平面。四层板里第一层参考第二层地第四层参考第三层电源这样第四层的回流路径就要靠电源平面上的去耦电容来提供路径变长效果打折。所以我的做法是四层板里尽量把关键信号都放第一层第四层只走慢速信号让地回流的问题最小化。电源平面分割要小心。3.3V、1.9V、1.2V 这几个电压的平面不要犬牙交错地分割尽量用整块的区域分割缝不要穿过高速信号的投影区。如果一颗芯片需要多个电压比如 FPGA 的核心和 IO 电压那它的电源脚要分别连到对应的平面上不要在一个小区域里混着走。去耦电容在 PCB 上的位置前面原理图那节提过要就近打孔到地。PCB 上具体操作是电容的一个焊盘用短走线连到芯片电源脚另一个焊盘直接打孔到地平面。过孔尽量靠近焊盘走线长度控制在 2mm 以内。如果空间允许用两个过孔接地降低接地电感。再说一个容易被忽略的点大电流路径。3.3V 从金手指进来经过板上的电源转换再分配到各个芯片这条路径上的铜箔宽度要按电流算。1A 的电流1oz 铜厚温升 10 度的情况下需要大约 20mil 到 25mil 的线宽。如果空间允许用铜皮铺面而不是细线效果更好。电源入口处的大电容和电感周围要留足空间别让两个大体积器件挤在一起影响散热。5. 打样回来后的调试与问题排查板子回来插上电第一件事不是马上跑程序是先看电流。这一节讲的是从上电到跑通数据链路的完整排查顺序以及每一步可能踩到的坑。5.1 链路训练失败的排查顺序PCIe 设备插上之后主机的链路层会和设备做链路训练也就是 LTSSM 状态机从 Detect 一路走到 L0。如果训练不起来设备在系统里就看不到。这一步失败的原因挺多的我按排查成本从低到高排个序。第一步查供电。用万用表量金手指上的 12V 和 3.3V 是不是正常量 8311 的各个电源脚电压对不对尤其是核心电压。很多枚举不了其实是芯片根本没上电或者核心电压没起来。第二步查参考时钟。用示波器最好是差分探头量 A13/A14 上有没有 100MHz 的差分时钟幅度对不对。如果时钟没有主机可能没给也可能是卡上的端接有问题把它压掉了。第三步查 PERST#。主机的 PERST# 拉低之后应该保持至少 100ms 再拉高8311 靠这个信号复位。量一下它的时序对不对有没有一直保持低电平。第四步查链路信号。用高带宽示波器看 B14/B15 上有没有发送数据A16/A17 上有没有接收到训练序列。这一步会比较难因为信号是 2.5GT/s 的差分普通探头看不到细节需要差分探头加足够的带宽。如果没有接收信号可能是发送方向接反了或者 8311 的发送使能没起来。第五步查 EEPROM。如果 8311 上电读 EEPROM 失败它会退回默认配置某些默认配置下链路参数可能不对。量 EECLK 有没有时钟EEDO 有没有数据回来。排查的时候有个顺序技巧先用电表再用示波器最后才上协议分析仪。因为电表最快最简单能排除掉大部分低级错误。我见过有人上来就接协议分析仪折腾了一下午才发现是 3.3V 没供上。5.2 枚举异常与 BAR 空间定位链路训练通了主机的操作系统里能看到设备了但设备管理器里可能显示黄叹号或者驱动加载了但访问不了。这时候要看的是配置空间和 BAR。枚举的过程中主机 BIOS 或者操作系统会给设备的 BAR 分配地址空间。8311 的 BAR0 一般是内存映射空间用来访问本地总线的存储区BAR1 可能是另一段内存或者 IO 空间。如果 BAR 的地址分配失败或者 BAR 的大小和 EEPROM 里配置的不一致就会出现设备存在但无法访问的情况。排查方法是进系统之后用工具读 8311 的配置空间看 Vendor ID、Device ID 是不是你 EEPROM 里写的看 BAR0 的地址是不是被分配了一个有效的非零值。如果 BAR 全是 0 或者全是 F说明配置没生效。有一种情况是 BAR 分配冲突。如果 EEPROM 里把 BAR 的大小写成了 64MB而主机的可用窗口不够或者和别的设备冲突主机就分配不了。这时候要么改 EEPROM 里 BAR 的大小要么调整系统的资源分配。做板子的时候 BAR 不要贪大本地总线能用到的空间有多少就配多少我一般配 1MB 到 16MB 之间。还有一种更隐蔽的情况BAR 分配成功了但读写它的地址会导致系统卡死。这通常是因为本地总线的从设备没有正确响应8311 的访问超时机制没生效。这种情况下要在 FPGA 里把本地总线的响应逻辑做得更健壮保证任何地址的访问都能在有限周期内返回数据或者错误标志不要让它无限等待。5.3 本地总线读写不通怎么查配置空间那一关过了接下来是本地总线。现象是主机能读到 8311 的寄存器但读写本地总线的映射地址时读回来的全是 0 或者全是 F。第一步先确认地址映射对不对。8311 的本地总线地址和 PCIe 地址之间有一个转换关系通常是把 PCIe 地址空间的一段映射到本地总线的基地址上。要看 8311 里的地址转换寄存器配置对不对这部分配置可能在 EEPROM 里也可能需要初始化的时候写。第二步用逻辑分析仪抓本地总线的波形。抓 LCS、LWR、LRD、地址线、数据线看主机发起访问的时候这些信号有没有动。如果 LCS 一直不动说明 8311 根本没发起本地总线周期问题在 8311 或者地址映射。如果 LCS 动了但数据不对问题在 FPGA 的从设备逻辑。第三步检查握手信号 LRDY。8311 发起访问之后会等 FPGA 拉高 LRDY 才完成这次访问。如果 LRDY 一直不拉高访问就会超时。用逻辑分析仪同时抓 LRDY看它有没有在预期的时间内拉起来。第四步检查数据方向。本地总线的数据线是双向的FPGA 侧要正确控制三态门的使能。读周期的时候 FPGA 要驱动数据线写周期的时候要释放。这个使能逻辑写错的话会出现读回来的数据全是 0总线没被驱动被下拉了或者全是 F总线被上拉或者两个方向同时驱动导致数据混乱。我在调这块的时候遇到过一个很典型的问题FPGA 侧的三态门使能用了组合逻辑结果在地址切换的瞬间出现了短暂的短路驱动导致 8311 收到错误数据重试几次之后进入了错误状态。后来把三态使能改成时钟同步的用本地时钟打一拍再输出问题就消失了。这个经验值得记一下本地总线上的所有输出使能都做同步处理别用纯组合逻辑。5.4 带宽实测与瓶颈定位功能通了之后下一步是测带宽。我用的是 8311 的 DMA 模式让主机这边一次性请求一大块数据测实际吞吐。实测结果分类看如果实测带宽在 150MB/s 到 200MB/s 之间说明整个链路是健康的PCIe 链路和本地总线都没问题如果在 80MB/s 到 150MB/s 之间通常是本地总线的效率问题检查 FPGA 是否插入了过多的等待周期或者本地时钟频率设低了如果低于 80MB/s那就要怀疑是不是 DMA 配置不对或者每次搬移的长度太短导致开销占比过高提升带宽有几个方向。一是提高本地总线的时钟频率从 50MHz 提到 66MHz理论上能提升 32%。二是减少 FPGA 从设备的等待周期读操作尽量在一个时钟内返回数据。三是加大 DMA 每次搬移的长度减少描述符处理的开销一般单次搬移 4KB 以上比较合理。还有一个坑是 CPU 缓存。主机侧驱动如果直接读写映射到设备的地址而这些地址被 CPU 缓存了读回来的可能是缓存里的旧数据而不是设备的新数据。解决办法是在驱动里把这块地址映射成非缓存的或者用 DMA 一致性内存。这个问题在 x86 上表现得不明显因为 x86 通常不会缓存设备内存但在某些架构上就必须处理。带宽测出来之后还要看波动的稳定性。如果带宽忽高忽低可能是 DMA 和其他总线主设备抢带宽也可能是 FPGA 的 FIFO 深度不够导致频繁停顿。抓一段长时间的带宽曲线看稳定的曲线比峰值更重要。6. 常见问题速查与踩坑心得调试过程中遇到的问题大部分是可以归类的我把这段时间踩过的坑整理成一张速查表再加上几条我自己觉得最值钱的经验。6.1 问题速查表现象可能原因排查动作系统里看不到设备供电没上、参考时钟缺失、PERST# 异常电表查电压示波器查时钟和复位设备有黄叹号BAR 空间分配失败或配置错误读配置空间核对 EEPROM 里的 BAR 大小读设备地址系统卡死本地总线从设备无响应访问超时检查 LRDY 握手完善 FPGA 超时逻辑读回数据全 0三态门未驱动、地址映射错误逻辑分析仪抓数据线方向和 LCS读回数据全 F总线上拉、片选未选中检查片选译码和上拉电阻数据偶尔出错时序余量不足、三态使能竞争放宽 XINTF 时序三态使能同步化带宽只有几十兆DMA 描述符开销大、搬移长度短加大单次搬移长度检查本地时钟板子时好时坏复位边沿太慢、电源纹波大示波器看复位边沿和电源纹波EEPROM 配置不生效配置字格式错误、EEDO 方向接反抓 EECLK 和 EEDO核对配置格式DSP 读 FPGA 数据错位XINTF 时序或地址线接错抓 XRD 和数据线核对地址映射6.2 几条我自己踩出来的经验第一条第一次打样永远做保守配置。本地总线时钟设低一点XINTF 时序放宽一点去耦电容多加一点。板子回来跑通了再一组一组往极限压。我第一版就是想一次跑到位本地时钟直接上 66MHzXINTF 时序压到最小结果板子回来读数据十次错三次花了整整一周才定位到是时序余量的问题。如果一开始跑 50MHz功能验证完了再往上提这一周能省下来。第二条FPGA 内部一定要留调试资源。这块板子我用的是 Altera 的 FPGA综合的时候留了一个 SignalTap 的采样深度把本地总线的关键信号和内部状态机都挂上去。板子回来第一次上电不用示波器就能看到内部逻辑对不对效率高很多。如果用 Xilinx就留 ILA。这些片上逻辑分析仪不占多少资源但在调试阶段是救命的。第三条DSP 和 FPGA 之间的接口地址线一定要做好译码保护。我设计的时候给 FPGA 留了 4KB 的地址空间但实际只用了前 512 字节。结果 DSP 程序里有个指针越界访问到了没实现的那段地址FPGA 没做处理DSP 就一直等 XREADY程序直接卡死。后来在 FPGA 里加了个默认返回任何未实现的地址访问都立刻返回一个无效标志程序不会卡死。这个保护看起来多余实际上救过我好几次。第四条电源和地的测试点一定要留够。板子上我留了 3.3V、1.9V、1.2V 和 GND 的测试点每一个都做成一个可以夹钩子的焊盘。调试的时候用万用表或者示波器直接夹上去不用去找引脚。这个成本几乎为零但省下的时间很可观。第五条原理图和 PCB 的版本管理要靠自己。Protel 99 SE 没有 Git 那种版本控制我用的是最土的办法每次改动存一版文件名带上日期和版本号比如mainboard_v1_2_20240615.ddb。同时在一个文本文件里记下每次改了什么。这个习惯养成之后回退和对比就方便了。看起来笨但比用复杂的工具省事。第六条金手指的板厚和倒角投板的时候在工程说明里写清楚。我第一次投的时候没写板厂按常规做的 1.6mm但倒角角度不对插进机箱的时候有点费劲。第二次专门标注了PCIe 金手指卡倒角按规范拿到手就顺了。这种和板厂沟通的细节写清楚比事后返工便宜太多。最后再说一个方向性的建议。这套 PCIe8311 加 FPGA 加 DSP 的架构虽然器件都不算新但它的灵活性在于每一层都可以替换。明天你想把 8311 换成更高速的桥片或者把 DSP 换成别的处理器只要接口定义清楚改动量并不大。所以画板的时候把本地总线和 DSP 接口这两组信号的引脚分配做得规整一点电源和地留出余量下一版升级会轻松很多。