
1. 开始前的准备与设计思路1.1 LDPC IP核在通信链路中的位置LDPC低密度奇偶校验码是目前通信系统里绕不开的纠错编码方案。无论是5G NR、DVB-S2、Wi-Fi 802.11n还是各种卫星和有线通信都能看到它的身影。它的核心优势在于逼近香农极限的纠错能力同时校验矩阵的稀疏特性让硬件实现的复杂度可控所以特别适合做成FPGA IP核。很多人在做基带信号处理链路时前级是CRC、加扰中间是信道编码后面接调制、OFDM等等。LDPC IP核就卡在信道编码这一段承担发射端的编码和接收端的译码工作。我自己第一次在Vivado里集成LDPC编码器时原以为和FFT IP核一样拉出来配个长度点一下生成就行。结果打开配置界面发现要填的东西比想象中多什么Base Graph、Z factor、填充位、码率每个名词背后都是一套通信协议规则。尤其是当手里的数据块正好是6144bit时问题就来了这个数字能不能直接填需不需要填充用哪个基矩阵这篇文章就是从我实际调试一个项目出发把6144bit数据在Vivado LDPC IP核里的完整配置路径讲清楚给正在做类似工作的朋友一个可以直接复用的参考。1.2 6144bit数据块的来源与格式6144bit这个数字在通信协议里很有代表性。很多标准在传输块分段时会把大的传输块切成若干小段而6144bit是一个常见的最大编码块长度边界。比如在LTE和5G NR的相关文档里超过这个长度的数据会被拆成多个编码块每个编码块单独做LDPC编码。所以如果你手头有一个6144bit的包大概率是协议栈已经切好的编码块等着交给物理层处理。在FPGA工程里6144bit数据通常不是一次并行给它全塞进去的而是通过AXI4-Stream接口按拍发送。比如数据位宽是64bit时一个6144bit的输入块需要96拍6144 ÷ 64 96。如果数据位宽是128bit则需要48拍。这个位宽由IP核的接口配置决定和你前级模块的输出位宽要匹配。另外还有一个关键点6144bit是有效信息位但LDPC编码器实际吃进去的比特数往往不等于6144因为协议规定编码块的信息列数必须等于某个基矩阵列数乘以扩展因子Z而6144不一定能被Z整除。这就引出了填充位的概念。1.3 先想清楚直接填参数还是按标准来不少新手打开LDPC IP核后会本能地在信息长度栏里填6144然后点生成。如果IP支持自定义码字参数可能不会报错但生成的编码结果未必符合你对接的协议。更常见的情况是界面直接告诉你“Information bits out of range”或者“Invalid Z factor”让人一头雾水。我的建议是在打开Vivado之前先把标准协议里LDPC编码的那张基矩阵表找出来。你需要回答三个问题用哪个基矩阵比如5G NR的BG1还是BG2扩展因子Z取多少实际编码器输入的信息位长度是不是6144还是加了填充位之后的值把这三个问题在纸上或者Python脚本里算清楚再去IP核界面里填参数基本不会出岔子。毕竟IP核只是工具它需要的是符合协议定义的一组参数而不是一句“我想要6144bit编码”。2. 核心参数拆解从6144bit反推LDPC配置2.1 关键参数码长、信息位、校验位和码率LDPC编码器本质上做的是矩阵运算输入一个信息向量乘以生成矩阵得到码字。码字长度N、信息位长度K、校验位长度M三者满足N K M。码率R K / NR越小冗余越大纠错能力越强但有效吞吐率也越低。如果直接把6144当作信息位K那么选不同码率时码长和校验位会随之变化。举例来说码率1/2时N 12288M 6144码率2/3时N 9216M 3072码率1/3时N 18432M 12288。这些计算看起来很简单但IP核配置界面里的“Code Rate”选项有时候并不是让你直接填一个分数而是通过选择基矩阵、Z值和打孔方案来间接确定。尤其是在5G NR LDPC里码率有专门的定义方式基矩阵中信息列数固定BG1是22列校验列数可变再通过打孔rate matching得到目标码率。所以你不能简单地把N6144/R填进去而是要理解IP核内部生成的是完整的LDPC码字还是已经做过速率匹配后的码字。2.2 如何根据6144bit确定基矩阵和扩展因子5G NR的LDPC基矩阵分为BG1和BG2。BG1支持的信息块最大值是8448信息列数Kb 22BG2支持的信息块最大值是3840信息列数Kb 10。我们的数据是6144bit大于3840所以BG2直接排除只能选BG1。BG1的每个信息列都对应Z个比特因此编码器输入的信息位长度K Kb × Z 22 × Z。为了让K至少能装下6144bit有效数据需要满足22 × Z ≥ 6144也就是Z ≥ 279.27。Z又不能随便取5G NR协议里规定了一组离散的Z值从2、4、8一直到384按特定规则排列。在大于等于280的合法Z值里最小的一个通常是288。所以这里选Z 288此时K 22 × 288 6336 bit。这意味着编码器实际输入是6336bit其中有6336 - 6144 192bit是填充位通常填零。Vivado IP核一般支持自动填充功能你可以在配置界面里设置“Fill Bits Enable”IP会在前级数据流结束后自动插入192bit零值或者由外部通过tkeep/tlast信号标志填充位置。选Z288的好处是填充开销最小只有3.03%192 ÷ 6336而且协议标准里正好有这个Z值后续如果要做软判决或与解调器对接处理起来也方便。2.3 填充比特与信息位的关系填充位是个容易被忽略但后果严重的问题。标准LDPC解码器在译码时会把已知的填充位对应的信道软信息置为最大似然值相当于告诉解码器“这些位我确定是0”。如果编码时填充位没填对或者解码端不知道哪些位置是填充位译码性能会急剧下降甚至完全解错。在Vivado的LDPC IP核里关于填充位一般有两种处理路径。第一种是IP内部自动填充你在配置界面里设置输入信息位长度为6144Z288IP会在内部把信息位补齐到6336bit。这种方式最简单但需要确认IP版本支持自动填充并且在接口侧正确拉高有效信号。第二种是外部手动填充你自己在数据通路里把6144bit有效数据扩展成6336bit在192bit填充位上补零然后直接告诉IP我要发6336bit。这种方式更灵活适合数据前级已经做了速率匹配和字节对齐的场景。我个人的经验是能用IP自动填充就尽量用自动自己在RTL里插一堆零很烦而且容易在tlast边界的时序上踩坑。如果必须手动填充一定要把填充长度和位置通过参数传到解码端两边统一。不然的话编码端和解码端各算各的Z值和填充长度结果一定不对。2.4 数据位宽与AXI接口时序的配合搞定了信息位长度接下来要解决的是“这些比特怎么送进IP核”。Vivado LDPC IP核通常使用AXI4-Stream接口。你需要在配置里设置tdata位宽常见的有32、64、128bit。位宽选得越大所需拍数越少但内部处理资源和时序压力也越大。以Z288、K6336bit为例如果tdata位宽取64bit则编码器输入需要99拍6336 ÷ 64 99。当然这是理想情况还要考虑填充位是否占用完整拍。如果6144bit有效数据加192bit填充前96拍发6144bit有效数据第97拍的高64bit里只有前12bit是填充位其实填不满64bit所以在AXI4-Stream接口上需要用tkeep信号指明哪些字节有效。很多IP核为了简化处理会要求你把填充位也带上并且输入数据总长度正好是位宽的整数倍。6336恰好能被64整除所以用99拍就能发完这也是选Z288的一个小优势。如果选了Z320K70407040 ÷ 64 110拍虽然也是整数但填充开销大了不少896bit白白浪费资源。输出端的码字长度同样受Z影响。BG1的基矩阵是46行68列其中信息列22列校验列46列。如果做全码字输出码长N 68 × Z 68 × 288 19584 bit。但实际很多标准会通过打孔去掉部分校验位比如5G NR里固定打掉前2列校验比特这样有效码字长度会小于19584。所以在IP核配置界面里你需要明确选择输出的是“Full Codeword”还是“Rate Matched”这个决定了输出接口需要多少拍以及后续拼包模块的位宽设计。3. Vivado中IP核配置实操3.1 在IP Catalog中添加LDPC Encoder IP打开Vivado工程在左侧Flow Navigator里点击“IP Catalog”然后在搜索框输入“LDPC”。会看到类似“LDPC Encoder”和“LDPC Decoder”的条目有的是标准3GPP 5G LDPC有的是通用LDPC。不同版本界面略有差异但总体流程一致。我这里以Vivado 2022.1里的LDPC Encoder IP为例你的版本如果是2020以后的基本都能找到对应项。双击IP核后会弹出配置窗口包含Basic、Interface、Implementation等页面。建议一开始不要瞎点先切到“Documentation”页签看一下当前IP支持的标准列表和输入输出范围。有的IP只支持DVB-S2不支持5G NR如果你拿6144bit硬填配置再正确也白搭。选择支持3GPP 5G NR的版本然后再开始配置。3.2 逐项设置参数以6144bit 1/2码率为例下面以一个典型的案例来说明数据块6144bit目标码率1/2基矩阵BG1Z288。在Basic配置页面里需要设置以下几项我整理成了一张表参数项推荐值说明Component Nameldpc_encoder_6144自定义IP例化名不要和工程其他模块重名Operation ModeEncoder编码器模式译码另选DecoderStandard3GPP 5G NR按协议标准驱动参数Base GraphBG1信息长度6144必须用BG1Z Factor288可以手动填也可以选Auto让IP自动算Information Bits6144有效信息位长度不含填充位Enable Fill Bits勾选让IP内部自动补齐到6336bitCode Rate1/2结合打孔配置选目标码率Output TypeRate Matched输出经过速率匹配的码字长度对应目标码率Data Width64AXI4-Stream接口位宽可按吞吐需求调整设置完之后界面一般会显示出等效信息位长度和码字长度。以Z288、BG1为例填充后信息位是6336bit目标码率1/2时经过打孔输出的码字长度大约为12672bit具体数值看IP的rate matching实现。这里请特别注意不要手动去算要以IP界面显示的“Codeword Length”为准。“Allow Unsupported Z”或“Custom Z”这类选项最好别开。除非你对5G NR协议烂熟于心知道自己填的Z值确实可用否则用标准Z表里的值最稳妥。IP在生成时会对基矩阵和Z做校验不合法的组合会直接报错。3.3 连接时钟与复位生成并例化参数都填好后点击OK然后右键生成的IP选择“Open IP Example Design”Vivado会为你生成一个完整的参考工程包含IP例化代码、仿真testbench和约束文件。这个例程非常有用建议不要略过。在顶层模块里需要连接的信号一般包括aclk、aresetn、s_axis_input_tvalid、s_axis_input_tdata、s_axis_input_tready、m_axis_output_tvalid、m_axis_output_tdata、m_axis_output_tlast等。我们假设位宽64bit那么s_axis_input_tdata是64位m_axis_output_tdata也是64位。这里有一个容易踩的坑复位信号aresetn是低有效有些初学者不确定结果把复位信号一直拉低IP始终处于复位状态tready不拉高数据根本送不进去。还有一种情况是时钟频率太高IP内部延迟导致tready周期性拉低此时前级模块必须支持反压也就是要等tready为高再拉高tvalid否则数据会丢失。AXI4-Stream握手协议里tvalid和tready必须同时为高才算一拍有效数据传输。生成比特流之前建议先跑一遍功能仿真。Vivado的IP例程里通常已经把激励写好了你只需要改一下输入数据让它输出6144bit的测试序列然后观察输出码字长度和tlast的位置是否符合预期。3.4 用testbench验证输出码字编写testbench时我一般会分成三块输入激励生成、DUT例化、输出监测。输入激励方面既可以用循环语句生成伪随机数也可以从文件读入一段固定数据。关键是信息数据长度要严格等于6144bit并且按照64bit一拍发送一共96拍。第97拍开始IP内部自动补充填充位这部分不需要你在前端给数据。输出监测方面建议抓取m_axis_output_tvalid和m_axis_output_tdata。因为你选了Rate Matched输出码字长度和目标码率有关不要天真地以为输出数据长度等于输入长度。以6144bit信息位、1/2码率为例如果输出码字是12672bit那么在64bit位宽下需要198拍。仿真结束后可以把输出数据存到文本文件用MATLAB或Python读取再调用对应通信标准库里的LDPC解码器尝试译码看能不能还原出原始6144bit。这样做一遍才能确认IP核配置和接口时序都正确。如果嫌写完整仿真麻烦至少也要用IP核自带的example design跑通一次然后把ILA核挂在m_axis_output_tvalid和s_axis_input_tready上观察握手信号是否正常。很多设计在静态配置上看着没问题但一上板跑起来就死在时序上通过ILA查波形要比反复仿真快得多。4. 常见问题排查与调优经验4.1 配置界面报错的典型原因配置LDPC IP核时最常见的报错之一是“Invalid Z factor”。原因很简单你选的Z不在协议标准Z表里。解决方法是打开5G NR的标准文档从Z表里选一个大于等于K/Kb的最小合法值。以6144bit和BG1为例直接填279肯定不行因为Z必须是2的幂乘以某个奇数的形式279不是标准Z。老老实实填288界面就不报了。另一个常见报错是“Information bits out of range”。这是因为你选了BG2但BG2最大信息位长度只有3840你填6144自然超出范围。遇到这种情况切回BG1即可。如果你是自定义标准而非3GPP 5G NR则要注意IP核允许的最大信息块长度不同IP版本上限不同。最后一种典型问题是“Code rate not supported”。有些IP核只支持固定的那几种码率比如1/2、2/3、3/4、5/6。你如果想要一个比如0.42这种非标准码率就需要选择Custom/Rate Matching模式。如果在标准模式下找不到你要的码率建议先回去确认协议要求是否支持这个码率如果协议里速率匹配后确实有这个值那么IP里的“Code Rate”要选“Custom”并填入实际输出校验比特数。4.2 时序违例和资源优化LDPC编码器在FPGA里虽然是成熟IP但配置不当也会吃很多资源。BG1本身的基矩阵是46×68扩展因子Z288后校验矩阵规模并不小。如果你用的是中低端FPGA最好在配置页面里把“Optimization Target”设为Area并且关闭不必要的调试接口。实测下来这能减少不少LUT和FF占用。时序方面如果编码器输入频率是200MHz但内部矩阵运算组合逻辑较长很容易出现setup violation。这时优先降低接口数据位宽因为位宽越高内部并行度越大布线压力反而增加。相反如果需要提升吞吐率可以考虑提高时钟频率但降低位宽或者使用IP的“Max Channels”多通道模式并行编码多个块。不要一味地追求128bit位宽很多时候64bit配合合理频率已经能满足需求。另外复位后IP内部通常需要几个时钟周期的初始化时间。如果你的设计在FPGA配置完成后立即拉高tvalid准备发送数据就可能在IP尚未就绪时造成首包错误。建议在状态机里至少等待tready稳定拉高后再开始发送或者在IP配置里打开“Stable Output”之类的选项。可以通过ILA抓取复位释放后的前几十拍波形来确认。4.3 实测中发现的几个坑先说填充位校验。我之前在一个项目里为了让IP输出码字长度正好等于信道帧长手动在外部把6144bit扩展成6336bit但忘了在IP配置里关掉自动填充。结果IP又自动插了一遍零造成输入数据全部错位。后来才发现自动填充和手动填充是互斥的启用自动填充的前提是输入数据长度必须是实际信息位长度6144而不是填充后的6336。其次是tkeep信号。如果输入数据位宽是64bit而最后一拍有效数据不足64bit时必须正确配置tkeep。6144bit恰好能被64整除所以没有这个问题。但加了填充位后如果你选择外部填充最后一拍可能只有12bit有效此时tkeep要写成0xFFF假设以字节为单位否则IP会以为后面还有垃圾数据。建议还是让IP自动处理填充这样tkeep会由IP内部根据有效长度自动生成外部省心很多。最后是端序问题。AXI4-Stream tdata的bit顺序在不同IP核里可能不一样。有的IP把第一个bit放在tdata[0]有的放在tdata[63]。如果你的数据来自前级DMA或者协议栈一定要在仿真阶段就确认编码输出和输入的顺序一致。否则你可能会发现明明配置都对但输出码字译码后是乱序的。这个坑不好查因为波形上看编码输出长度和时序都正常只有拿到软件译码才算发现。我个人现在的做法是在testbench里先用一个只有很少1的数据模式比如64bit里只有bit0和bit31是1跑完编码后把输出码字打印出来对照协议里的生成矩阵手算一遍很快就能定位端序问题。虽然土了点但比盲调强得多。4.3 实测中发现的几个坑续还有关于Z值自动选择的问题。有些IP核版本里Z Factor填了Auto之后它不会告诉你到底选了哪一个Z只在生成报告中用一行小字打印。如果你需要将Z值同步给后级的速率匹配模块或解调器建议不要依赖Auto直接手动填288。毕竟你的数据量6144和BG1已经决定了Z288是最优解为什么不直接写死呢自动选择留给那些需要快速迭代多种码长的工程但可读性和可控性都会差一些。时钟域方面LDPC编码器输入和输出一般都在同一个时钟域。如果你的设计中有多个时钟域比如前级是100MHz编码器是200MHz不能用纯AXI4-Stream直连需要插入异步FIFO。很多同事以为IP核内部会自动做跨时钟域处理其实没有跨时钟必须自己搞定。我建议在配置IP之前先画一个数据流框图标清楚每级接口的时钟和位宽再开始例化。尤其是6144bit这种长包如果没有FIFO隔离前级反压处理不好一包数据可能会被截断。资源占用方面BG1搭配Z288的LDPC编码器在Xilinx Artix-7 35T级别的小芯片上会显得比较吃紧但通常还能放下。如果是译码器资源占用会成倍增加这时候就要考虑是否改用Z值更小的BG2或者把数据块拆小再或者同时编码多个小块但共享译码资源。总之6144bit用BG1是刚需但后续的资源规划一定要提前估算别等布局布线满了才发现。5. 收尾一点个人习惯最后分享一个我自己的小习惯每次配置完LDPC IP核后我会把当前界面的所有参数截一张图放到项目文档里并且把所有关键参数信息位长度、填充后长度、Z值、实际码字长度、接口位宽、时钟频率写成一个表。这样不仅方便自己回溯也方便团队其他成员在review时快速理解你的链路设计。配合脚本计算部分比如自动填I、算Z用Python写十几行代码就能搞定比手算可靠很多。如果你也正在做LDPC编码链路希望这篇文章能帮你少走弯路。记住四个字先算法后IP。协议把参数定下来了Vivado里的配置就是填空题。