尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LDPC编解码器设计与仿真全流程解析:从原理到硬件验证

LDPC编解码器设计与仿真全流程解析:从原理到硬件验证 做LDPC编解码器设计和仿真这件事我前前后后折腾了大半年从最初的原理推导到最终的硬件验证踩了不少坑也积累了一些比较靠谱的经验。通信圈的朋友应该都知道LDPC低密度奇偶校验码这几年基本是信道编码领域绕不开的核心方案5G NR、WiFi 6、卫星通信、光传输甚至NAND Flash的纠错链路里都有它的身影。很多人一上来就啃教材里的稀疏矩阵和置信传播公式结果卡在“看得懂原理、写不出代码、跑不出性能”这个尴尬阶段。这篇文章我就把自己从零搭起一套LDPC编解码器仿真环境的完整过程拆开来讲包括编码器怎么设计、译码器怎么选算法、仿真平台怎么搭、遇到问题怎么排查尽量把关键细节和背后的逻辑都交代清楚希望能给正在做相关项目或者准备入门的同学一些实际的参考。1. 项目概述与设计思路拆解1.1 LDPC到底解决了什么问题LDPC本质上是一种线性分组码核心就是让发送端给数据加上“冗余”接收端利用这种冗余关系来发现并纠正传输过程中引入的错误。它最大的特点是校验矩阵极度稀疏也就是说每个校验方程涉及的码字比特非常少这就给迭代译码创造了非常好的结构条件。和Turbo码相比LDPC在相同复杂度下可以获得更接近香农极限的性能而且在译码时天然支持并行处理吞吐量可以做得非常高这也是为什么5G和WiFi 6最终选择了LDPC作为长码块的信道编码方案。在实际项目中你需要根据具体的链路预算和误码率要求确定码率、码长、迭代次数这些关键参数。比如我之前做过一个卫星通信链路的仿真信道条件比较差信噪比Eb/N0在2到4dB之间波动那码率就不能定太高1/2的码率配合8到16次迭代才能保证误码率在10的负6次方以下。而如果做的是光通信里面的硬判决纠错可能更关注吞吐量和时延就会选高码率、低迭代次数的方案。搞清楚LDPC要解决什么问题其实比急着写代码更重要——参数选错了后面所有工作都是在错误的方向上使劲。1.2 设计仿真前必须先定的五个关键参数我见过太多人拿到任务就开始写编译码函数结果做到一半发现信道模型不对、码长不合适整个仿真框架推倒重来。这里我根据自己的经验把起步阶段必须定的参数总结成下面这几点建议先花时间理清楚再动手码长N和信息位长度KN和K直接决定码率RK/N也决定了校验矩阵的规模。我建议内存资源允许的话优先选中等码长比如1024或2048这样既能看到LDPC在长码下的性能优势又不至于仿真跑一次要好几个小时。校验矩阵的构造方式随机构造容易理解但性能不稳定且不利于硬件实现。准循环LDPCQC-LDPC用循环移位矩阵拼接而成性能和硬件友好度都更好是工程项目的首选。信道模型和调制方式大多数入门项目用BPSK调制加高斯白噪声AWGN信道接收端LLR计算非常简单适合用来验证算法正确性。如果后续要做QPSK或更高阶QAMLLR计算复杂度会上升建议先在BPSK上把链路调通。译码算法和迭代次数浮点仿真阶段可以直接用BP算法硬件实现阶段再切换到最小和Min-Sum及其改进算法。迭代次数建议先拿较小值比如8次调通全流程再逐步加大到16、32甚至更多。性能指标和仿真点数误码率BER/误帧率FER曲线是最常用的评估指标。每种信噪比条件下至少要统计100个错误帧以上才能让曲线平滑信噪比越高需要的仿真帧数越多这个时间成本要在项目计划里提前预留。这些参数之间是有耦合关系的并不存在一个万能组合。比如你为了追求性能选了很长的码长但发现仿真时间完全不能接受那就必须牺牲一部分编码增益来换速度。这种取舍只有在实际跑起来之后才有真切感受所以我建议第一版参数组合可以保守一点先保证整个链路能跑通再逐步优化。2. 编码器设计核心从校验矩阵到生成矩阵2.1 校验矩阵的几种构造思路LDPC校验矩阵的好坏直接决定了编译码性能和实现复杂度。我最早入门时用MATLAB随机生成稀疏矩阵然后检查并消除短环girth为4的环必须要避免这种方法做原理验证完全没问题但问题是随机矩阵的结构性太差之后做硬件编码器时几乎没法映射成并行电路。后来项目中我改用了准循环构造方式。QC-LDPC的校验矩阵由若干个大小相同的单位阵循环移位或全零矩阵拼接而成每个子矩阵的移位量由一个基础矩阵Base Matrix定义。例如一个码率为1/2、子矩阵大小为Z×Z的QC-LDPC码其校验矩阵H整体规模是(Mb×Z)行、(Nb×Z)列其中Mb和Nb是基础矩阵的行数和列数。这种结构最大的好处是只要确定了基础矩阵里的移位系数整个校验矩阵就被完全确定了占用的存储资源极少而且编码和译码都可以按子矩阵分块并行处理。5G NR标准中用的就是这种结构相关的基础矩阵和移位系数表在标准文档里写得清清楚楚。如果你不想自己设计基础矩阵完全可以先拿标准里的矩阵来做仿真等整个流程跑通了再根据自己项目的码长、码率需求做修改。这里我特别想提醒一句自己设计基础矩阵时一定要先做短环检查四环会让BP译码的消息独立性假设失效直接导致错误平层抬高、性能明显恶化。2.2 从H矩阵到编码G矩阵法和RU算法详解拿到校验矩阵H之后理论上的编码思路是用高斯消元把H变换成系统形式从而推导出生成矩阵G然后直接把信息比特乘以G得到码字。但这里有一个工程上很要命的问题G矩阵完全不稀疏尺寸是K×N复杂度大约是O(N²)码长一上来编码器实现的资源开销和时延都是灾难。所以在实际项目中我更推荐用Richardson和Urbanke提出的RU算法做编码。这个算法的核心思想是对H矩阵做行列置换把它变换成一个近似下三角的形式然后利用矩阵的稀疏性把编码过程拆分成几个步骤每一步都是对稀疏矩阵的乘法和回代复杂度可以降到接近O(N)。具体实现时你会先对H做预处理第一步通过行列置换把H变成形如[H1 H2]的结构其中H2是一个可逆的方阵第二步对H2做LU分解或者求逆但因为这个过程只需要在预处理阶段做一次所以即使代价高一点也能接受第三步在实际编码时先用信息比特计算校验比特的上半部分再回代求出完整的校验比特。我自己的做法是在MATLAB里面先用高斯消元验证一下H是不是满秩的然后把RU算法写成一个独立的预处理函数输出一个结构体里面保存好预处理后的矩阵和中间变量。这样在实际编码时就是纯粹的矩阵乘法和回代操作逻辑非常清晰。如果你用的是QC-LDPC矩阵还可以利用子矩阵的循环移位特性把乘法进一步简化为循环移位累加这种优化在硬件实现时几乎是必须的。2.3 编码实操步骤和参数计算示例我给出一段可以直接参考的编码流程这个流程我实测下来比较稳定也容易迁移到C或者Verilog确定基础矩阵和扩展因子Z生成完整的校验矩阵H。确保H是满秩的如果不满秩需要删掉线性相关的行或者调整基础矩阵设计。对H做RU预处理得到置换后的矩阵H_ru以及必要的求逆结果。这一步建议保存下来因为后续每次编码都用同一份结果。已知信息比特向量u长度KN-M。若需要系统码结构第一步先构造长度为N的临时向量前K位放u后M位待定。利用H_ru的稀疏结构先根据信息位和前一部分校验位的关系求出校验位的上半部分再代入下半部分方程求出剩余校验位。把信息位和校验位拼接起来就得到完整的码字c。我以一个码长N1024、码率1/2的QC-LDPC码为例拆解一下信息位K512校验位M512。假设扩展因子Z64那么基础矩阵就是8行×16列。编码时预处理阶段对H做RU分解得到中间矩阵实际编码就是对512比特的信息做16个分块的循环移位累加。整个过程如果用MATLAB仿真单次编码时间不到1毫秒如果用C语言实现加上循环移位查表优化可以达到亚微秒级别。这就是为什么QC-LDPC在工程中如此受欢迎——结构规则带来的优势是实打实的。这里还有一个比较容易踩的坑做RU算法时矩阵的行列置换直接决定了后面编码的正确性。我建议你每一步置换都记录下来并且用一段测试向量去验证随机生成一个信息向量先用浮点方式编码再手工检查是否满足H乘以码字的转置等于零向量。这个校验步骤虽然简单但能帮你过滤掉90%的预处理错误。3. 译码器设计核心概率域的BP、对数域的BP与最小和算法3.1 置信传播算法的完整推导LDPC译码的黄金标准是置信传播BP算法。它是在Tanner图上做消息传递变量节点和校验节点之间来回传递“可信度”信息经过多次迭代后每个变量节点的可信度收敛最终做硬判决得出译码结果。在浮点仿真阶段我建议直接用对数似然比LLR形式的BP算法因为LLR把概率的连乘变成对数的求和实现起来简单数值稳定性也更好。整个算法可以分为四步初始化对每个变量节点n根据信道输出计算初始LLR。BPSK调制加AWGN信道下初始LLR等于2乘以接收信号除以噪声方差即L_n 2y_n/σ²。这里我习惯先把接收信号归一化到±1附近σ²按实际信噪比换算。校验节点更新对每个校验节点m利用它连接的所有变量节点传来的消息计算一个新的消息反馈给每个相邻变量节点n。标准公式要用双曲正切函数乘积再取双曲正切反函数。这一步是整个算法里计算量最大的地方。变量节点更新对每个变量节点n把信道初始LLR和所有相邻校验节点发来的消息做求和更新自己的置信度并生成发给相邻校验节点的外信息注意要排除目标校验节点自己的消息避免信息循环放大。硬判决和终止条件更新完所有节点后根据变量节点的总LLR符号做硬判决得到一个候选码字然后校验H乘以候选码字是否为全零。如果满足则宣布译码成功并退出迭代否则继续迭代直到达到最大迭代次数。我把这段算法用MATLAB实现时第一步就写了一个很小的测试用例N32的规则LDPC码随机错误模式下跑了一遍验证纠错能力正常后再扩展到长码。这里提醒一下LLR初始化里的噪声方差非常关键如果仿真时给的σ²和实际加噪功率不匹配性能会明显下降而且这种下降很容易被误认为是算法写错了。3.2 最小和算法及两种经典改进方案BP算法里校验节点更新涉及双曲正切函数这个运算在硬件里实现代价非常高。工程上最常用的替代方案是最小和Min-Sum算法它的核心近似是利用“小输入对应大输出”的特性把校验节点更新简化为一条规则输出消息的符号等于输入消息符号的乘积输出消息的幅值等于输入消息幅值的最小值。这样只需要比较和符号判断硬件实现极其简单。但Min-Sum的代价是译码性能会比BP差一些因为最小值的近似高估了校验节点输出的幅度。我在仿真中测过未经修正的Min-Sum在码率1/2、码长1024的配置下相比BP会有大概0.3到0.5dB的增益损失。为了弥补这个损失工程上普遍采用两种经典修正方案Normalized Min-Sum把校验节点的输出统一乘以一个小于1的归一化因子α通常取0.75到0.8之间。这个α可以用蒙特卡洛仿真在固定信噪比下扫出来也可以参考论文里的经验值。我自己的做法是在0.7到0.85之间按0.05步进扫描选BER最低的那个值。Offset Min-Sum把校验节点的输出幅值减去一个正的偏移量β小于β的直接置零。β的典型取值在0.25到0.5之间。这种做法在低信噪比区间的行为和高信噪比下有微妙差异通常比Normalized的鲁棒性稍好一些。我在同一套仿真平台上分别测了BP、Min-Sum、Normalized Min-Sum和Offset Min-Sum四种算法的BER曲线。在BER10的负5次方这个参考点Normalized Min-Sum相比Min-Sum能挽回大约0.2到0.3dB的性能损失Offset Min-Sum在部分信噪比范围内表现更好。最终我的硬件方案选了Normalized Min-Sum因为乘一个固定因子在硬件里可以用移位加加法近似实现比如取0.75就等于乘以3再右移2位完全避免了乘法器。3.3 定点化设计从浮点到硬件的关键一步如果你的目标是最终用FPGA或ASIC实现译码器那么在浮点仿真跑通之后必须再做一次定点化仿真。定点化的核心是确定LLR和中间消息的量化位宽以及小数点的位置。我踩过最大的坑是LLR位宽给得太窄结果随着迭代进行消息幅值不断增长直接饱和溢出译码性能劣化到比Min-Sum浮点还差。一个相对稳妥的定点化方案是LLR总位宽8到10比特其中符号位1比特整数位3到4比特其余为小数位。校验节点输出消息的幅值通常比变量节点消息稍小可以用7到8比特。变量节点更新时的求和结果需要防止溢出最稳妥的做法是每轮更新后做一次饱和截位。对于迭代次数FPGA实现时通常会设一个上限比如8到16次然后配合早停机制实时判断是否满足校验方程满足就提前退出这样能在不损失性能的情况下显著降低平均功耗。我在做定点仿真时不是简单地把浮点程序改成整数运算而是把量化函数集中封装起来在每个消息更新点插入一个量化操作这样能方便地切换不同位宽做对比测试。建议你先用较大的位宽比如12比特跑一遍确认性能逼近浮点再逐步缩小位宽观察BER曲线开始明显恶化的那一点那就是你硬件位宽的下限。把这个过程记录下来对后续写RTL代码时确定数据通路宽度非常有帮助。4. 仿真平台搭建与完整实操流程4.1 仿真平台整体架构和模块划分一套完整的LDPC编译码仿真平台至少应该包含信源模块、编码模块、调制模块、信道模块、解调/LLR计算模块、译码模块和性能统计模块。我强烈建议一开始就按这种模块化结构来组织代码而不是把所有逻辑写在一个脚本里否则后面换算法、换参数时你会被各种全局变量和临时脚本坑到崩溃。在项目初期我建议用MATLAB搭浮点平台因为矩阵运算和调试工具都很方便。平台的主循环可以这样组织固定一个Eb/N0循环发送若干帧数据每一帧依次经过信源生成、LDPC编码、BPSK调制、AWGN加噪、LLR计算、BP迭代译码然后统计当前帧是否正确。当错误帧数累计到一定数量后记录该信噪比点的BER和FER再切换到下一个信噪比点。信噪比参数的计算也需要单独说清楚。BPSK调制下Eb/N0和噪声方差的关系是σ² 1/(2·R·10^(Eb/N0/10))其中R是码率。如果不除码率你会发现译码性能曲线整体右移了大约1.76dB而且还会错误地认为LDPC的编码增益不明显。这是个非常容易忽略的细节我反复在不同项目里遇到有人栽在这里。4.2 MATLAB浮点仿真实现要点MATLAB里实现LDPC译码器有两条路可以选一是直接调用Communications Toolbox里现成的ldpcDecode函数二是自己按公式写BP或Min-Sum算法的迭代更新。我自己的建议是验证算法性能时可以直接用工具箱但最终做定点化、做硬件对接时一定要有自己的算法代码否则很多底层细节你根本控制不了。自己写译码器时我发现一个能明显加速仿真的小技巧不要逐节点循环更新而是把校验节点更新和变量节点更新矩阵化。具体地说可以预先建立一个M行N列的稀疏消息矩阵每个非零元素对应Tanner图上的一条边然后校验节点更新时按稀疏矩阵的行操作变量节点更新时按列操作。这样充分利用MATLAB的矩阵运算加速我在N2048、迭代次数16的配置下仿真速度比逐节点循环快了好几倍。另外调试阶段可以打开中间变量的可视化观察迭代过程中校验方程是否逐渐趋于满足。具体做法是每轮迭代后计算一下校验方程不满足的个数简称USC数如果USC随迭代单调下降说明算法实现基本正确如果USC上下抖动不收敛那大概率是消息传递的方向或者外信息排除逻辑写错了。我强烈建议把USC曲线作为译码器调试的第一诊断工具。4.3 C语言定点仿真的搭建和交叉验证为了和硬件实现衔接C语言定点仿真平台几乎是必需品。我的做法是把MATLAB平台里验证过的译码算法用标准C重新实现一遍并且把所有浮点运算替换成定点运算的模拟定义一个结构体存储消息的整数位宽和小数位宽用int16_t或int32_t表示消息值在每个更新点调用一个宏完成量化和饱和操作。这里有一个很重要的点C语言定点平台的结果必须和MATLAB平台在相同条件下对拍验证。我的做法是先在MATLAB平台生成若干帧的接收数据和对应的正确译码结果保存成文本文件然后用C语言平台读入同样的接收数据跑译码再对比译码结果和标准答案。只有每一帧都完全一致才能确认C代码逻辑正确。这个过程看起来土但非常有效我几乎每次都能靠它对出几处C语言特有的溢出或截断问题。C语言平台的性能优势在高信噪比条件下体现得非常明显。我实测过N2048、迭代次数16时MATLAB仿真一帧大约要几十毫秒C语言平台只需要不到一毫秒。这意味着在BER要求很低的信噪比点比如10的负7次方用C平台可以在合理时间内积累足够多的错误帧来画出平滑的曲线。如果你准备做大规模参数扫描我建议直接跳过纯MATLAB仿真一开始就把算法在C平台上实现好。4.4 从仿真结果到硬件验证的衔接方法很多项目做到仿真阶段就停住了但实际上仿真平台还有一个很重要的用途为硬件验证生成测试向量和对比参考。当你开始写Verilog译码器时RTL仿真里的激励和期望输出可以直接来自C语言定点平台生成的中间结果。我通常会在C平台里加一个调试模式把每一轮迭代的校验节点消息和变量节点消息都打印出来然后把这些数据作为RTL仿真的比对基准。这样一来当RTL仿真结果和C平台对不上时你可以快速定位是第一个迭代的哪个环节出了差异——是初始化LLR位宽不对还是校验节点更新时饱和策略不一致又或者是变量节点更新的消息传递方向搞反了。节省的调试时间非常可观。另外在实际硬件验证阶段还需要用到在线逻辑分析仪抓取真实的FPGA片上信号和仿真平台的输出做进一步对比这一步虽然超出纯仿真范围但提前在C平台里预留好调试数据输出接口会给你后期省下大量麻烦。5. 常见问题与排查技巧实录5.1 译码不收敛和性能劣化的原因分析我在做LDPC仿真的过程中遇到最多的现象就是译码不收敛BER曲线出现所谓“瀑布区”之后突然拖出一条长长的尾巴无论怎么增加迭代次数性能都上不去。排查这类问题我一般按下面的顺序梳理校验矩阵是否含四环如果H矩阵的四环比例过高BP算法的独立性假设失效受害者效应会让错误消息在局部循环里不断放大。解决办法是重新构造矩阵或至少把四环消除掉再跑一遍性能对比。状态初始化是否合理LLR初始化的噪声方差如果和实际加噪功率不匹配变量节点初始置信度就是偏差的后续迭代会一直在这个偏差上打转。我建议先用理论值代入跑一次再用蒙特卡洛统计实际信道噪声方差跑一次两次结果应该基本一致。迭代上限和早停条件迭代上限太低时部分帧在还没收敛前就被强制截止误码率自然下不去但如果早停条件写错比如校验关系判断反了也有可能出现假收敛输出仍然是错误码字。5.2 错误平层问题及其排查方法错误平层是指BER曲线在高信噪比区域突然变平这是LDPC译码特有的现象。它在很大程度上和最小距离较小的码字或所谓的“陷阱集”Trapping Set有关。Min-Sum算法相比BP更容易在陷阱集上卡住这是它性能损失的一个重要原因。我在项目里遇到错误平层时常用的处理手段有两个一是引入少量的比特翻转扰动比如在每次迭代硬判决时以极小概率翻转最不自信的几个比特能打破陷阱集的锁定二是结合CRC校验在LDPC译码失败时通过外码信息做重译码。这些方法在白皮书里不太会讲但在实际产品设计里经常会用到。5.3 仿真与现实结果不一致的常见因素到了硬件环节经常会出现仿真平台性能很好但实际测试性能明显变差的情况。我总结下来主要有三类原因有限字长效应定点化位宽不足导致LLR饱和、舍入误差累积性能损失超过预期。解决方案就是前面提到的位宽扫描选定性能拐点之前的安全位宽。时钟和时序带来的亚稳态问题这在高速译码器中尤其明显消息传递路径上的组合逻辑延时超标导致某几个节点的消息更新被延迟一拍相当于引入了额外噪声。实现细节与仿真模型的偏差例如饱和策略、截位方向、迭代过程中消息存储的更新顺序这些细节在C平台仿真和RTL实现之间很容易出现不一致。我在做RTL时特意用“逐拍对比”的方式排查很快就能定位是哪一拍的差异。5.4 常用问题速查表问题现象可能原因排查与解决方法译码完全不收敛BER接近0.5校验矩阵不满秩或LLR符号相反检查H矩阵秩先对初始化LLR做符号自检性能曲线在低信噪比区正常高信噪比区有平层陷阱集或多环结构影响检查矩阵围长尝试改进最小和算法或加CRC辅助定点仿真性能比浮点差很多位宽不足、溢出或量化策略不当做位宽扫描在关键数据路径上增加饱和保护高信噪比下错误帧集中在一两帧特殊短码字或接收数据异常打印该帧的LLR轨迹查看是否落在陷阱集硬件RTL和C平台结果不一致消息更新顺序、截位方式、存储位宽不同逐轮比对中间消息矩阵定位第一个差异点5.5 我的一些经验总结最后再聊聊我个人在反复做LDPC编解码器设计仿真过程中沉淀下来的体会。一个项目的价值往往不在最终那张漂亮的BER曲线而在于整个调试过程中建立起来的对算法和实现边界的理解。很多人在仿真阶段总是追求高迭代次数、长码长、最好逼近香农极限但真正做产品级设计时你会发现在复杂度、存储、时延和性能之间几乎每个维度都需要反复权衡。比如我在一个功耗敏感的项目里最终选的是迭代次数8次的Normalized Min-Sum译码器虽然比BP算法损失了大概0.2dB增益但换来的硬件资源节省和功耗下降完全值得。仿真平台的意义就是让你在投入硬件之前先把这些权衡都量化出来而不是靠拍脑袋做决定。如果这篇文章能帮你在自己的项目里少走几个弯路那就值得了。后续如果你打算继续深入可以接着研究非均匀量化方案、分层调度译码或者把LDPC和高阶调制做联合设计这些都是很有意思的方向。
返回列表