利用HLS实现LDPC译码
绪
这是自己第一次写文章,可能存在一定的错误,还望大家海涵。并且由于时间精力问题,没有补充完整。完整的代码已经上传到github中,链接:https://github.com/awdsn/LDPC_MS_decode。
一、译码算法
算法打算采用MS译码算法,更好的话可以改进算法如NMS算法,由于在数据位宽降低的情况下NMS算法译码效果反而不如MS算法译码效果,故自己没有选用。
二、C++代码
1.初始
下面代码将测试模块与译码模块放在了一起;并且按照译码算法中的步骤分别设置了以下子函数:校验节点更新,变量节点更新,校验节点和值计算,校验。
///初始化
void initialize()//计算qji_0
{int a, b;bool c_in[M];///读取文本到数组ifstream infile; //定义读取文件流,相对于程序来说是ininfile.open("接收到信号y-test.txt");//打开文件//接收到信号y//编码后向量xfor (b = 0; b < M; b++) {infile >> c_in[b];for (a = 0; a < N; a++) {if (c_in[b])q[0][a][b] = 1;//ci=1elseq[0][a][b] = -1;//ci=0}}infile.close();//读取完成之后关闭文件for (b = 0; b < M; b++) {//直接将读取的文件做输出校验c[b] = c_in[b];}
}
void check_matrix_read()//将校验矩阵读入数组
{int a, b;ifstream infile; //定义读取文件流,相对于程序来说是ininfile.open("校验矩阵H.txt");//打开文件for (a = 0; a < N; a++) {for (b = 0; b < M; b++) {infile >> H[a][b];//a行b列}}infile.close();//读取完成之后关闭文件
}
void check_note_update(int diedai)//校验节点更新;计算出rji的值
{int i, a, b, a0, b0, i_temp;double temp0 = 1;double min_temp = 40;//将最小值缓存for (b = 0; b < M; b++) {for (a = 0; a < N; a++) {if (H[a][b]) {a0 = a;//行数------jb0 = b;//列数-------ifor (i = 0; i < M; i++) {if (H[a0][i]) {i_temp = i;if (i_temp != b0) {//i_temp=R(j)\i; 变量节点集合min_temp = min(min_temp, fabs(q[diedai - 1][a0][i_temp]));temp0 = temp0 * sign(q[diedai - 1][a0][i_temp]);///}} }r[diedai][a0][b] = temp0 * min_temp;///?????????????temp0 = 1;//计算完依次的rji之后将temp_0值恢复,准备计算下一次}}}
}
void variable_note_update(int diedai) {//变量节点更新int j, a, b, a0, b0, j_temp;double temp1 = 0;//rki和值缓存for (a = 0; a < N; a++) {for (b = 0; b < M; b++) {if (H[a][b]) {a0 = a;//行数------jb0 = b;//列数-------ifor (j = 0; j < N; j++) {if (H[j][b0]) {//j_temp=C(i)\jj_temp = j;if (j_temp != a0) {temp1 = temp1 + r[diedai][j_temp][b0];}}}q[diedai ][a][b] = q[0][0][b] + temp1;temp1 = 0;//恢复原样}}}
}
void variable_note_panjue(int diedai) {//变量节点后验概率以及判决int i, j;double temp2 = 0;for (i = 0; i < M; i++) {for (j = 0; j < N; j++) {if (H[j][i]) {temp2 = temp2 + r[diedai][j][i];}}qi[diedai][i] = q[0][0][i] + temp2;temp2 = 0;if (qi[diedai][i] > 0)c[i] = 1.;else if (qi[diedai][i] < 0)c[i] = 0;elsec[i] = 0;//}
}
int check_formula(int diedai) {//根据校验矩阵计算是否译码完成int i, j;int temp = 0;int s_sum = 0;//s_sum负责记录满足条件,即s[i]=0的个数for (j = 0; j < N; j++) {//N个校验方程for (i = 0; i < M; i++) {temp = temp + c[i] * H[j][i];}s[j] = temp;if (s[j]%2 == 0) {s_sum = s_sum + 1;}temp = 0;}cout << "第" << diedai << "次: 满足条件的s数目为" << s_sum << endl;//负责调试,确实功能是否存在错误/// 验证int fanhui;//暂存值,负责确定返回值是什么if (s_sum == N)fanhui = 1;//满足校验矩阵,结束迭代elsefanhui = 0;return fanhui;s_sum = 0;
}int main() {int diedai = 0;//迭代次数,即第几次迭代int result, b, i;float error_num = 0;float error_rate ;//读入xifstream infile; //定义读取文件流,相对于程序来说是ininfile.open("编码后向量x.txt");for (b = 0;b < M; b++) {infile >> x[b];//cout << x[M] << endl;}infile.close();//读取完成之后关闭文件///*************************************译码******************************************//initialize();///初始化,计算q0_ij,即picheck_matrix_read();//将校验矩阵读入数组while (diedai <= diedai_max) {result = check_formula(diedai);if (result) {//cout << "最终迭代次数为: " << diedai << endl;//确定第几次迭代时结束运行diedai = diedai_max + 1;}else {diedai = diedai + 1;}check_note_update(diedai);variable_note_update(diedai);variable_note_panjue(diedai);}//****************************************测试模块:验证输出******************************************//cout << setw(5) << "M" << setw(10) << "x[M]" << setw(10) << "c[N]" << endl;for (i = 0; i < M; i++) {//cout << setw(5) << i << setw(10) << x[i] << setw(10) << c[i] << endl;if (x[i] != c[i]) {error_num = error_num + 1;}}error_rate = error_num / M;//cout.setf(ios::fixed);cout << "最终的误比特率为" << error_num << endl;return 0;
}
2.改进后
将上述代码进行初步改进,如改变读取数据的方式;优化存储中间变量的数组;为了减少整个译码模块执行所需的时间,将一些可以并行执行的for循环放在一个函数中,而不是按照执行的功能放在不同的函数中;此外,对顶层函数中调用各子函数的顺序进行一定的改变,进一步减少译码时间。最终各部分的代码如下:
void check_note_update(int j ,bool H[N][M], data_zj q[M],data_zj r[N][M],data_zj r_copy[M])//校验节点更新;计算出rji的值
{int i, b;int i1_temp = 0;ap_int<2> sign1_ji=1;ap_int<2> sign1[M] ; ///变量节点矩阵中数值的符号data_zj min1_temp = fabs(q[0]);//将最小值缓存data_zj min2_temp = fabs(q[0]);//将次小值缓存for (i = 0; i < M; i++) {i1_temp = 0;*/if (H[j][i]) {if (min2_temp >= fabs(q[i])) {min2_temp = fabs(q[i]);if (min2_temp <= min1_temp) {data_zj temp = min2_temp;min2_temp = min1_temp;min1_temp = temp;i1_temp = i;}}}}for (i = 0; i < M; i++) {//计算符号if (H[j][i]) {sign1_ji = sign1_ji * sign(q[i]); }}for (i = 0; i < M; i++) {//计算符号if (H[j][i]) {if (sign(q[i]) == sign1_ji)sign1[i] = 1;elsesign1[i] = -1;} }LOOP_check_note_update:
//#pragma HLS dependence variable=r inter falsefor (i = 0; i < M; i++) {
#pragma HLS pipeline rewindr_copy[i]=r[j+1][i];if (H[j][i]) {if (i != i1_temp)//i_temp=R(j)\i; 变量节点集合r[j][i] = sign1[i] * (min1_temp);elser[j][i] = sign1[i] * (min2_temp);}}
}void variable_note_pos(data_zj P[M],bool H[N][M],data_zj r[N][M],ap_fixed <10,8,AP_TRN,AP_SAT> qi[M]) {
//变量节点后验概率int i,j;ap_fixed <10,8,AP_TRN,AP_SAT> temp2 = 0;LOOP_qi_1:for (i = 0; i < M; i++) {LOOP_qi_2:for (j = 0; j < N; j++){
#pragma HLS UNROLL factor=2
#pragma HLS pipeline rewindif (H[j][i]) { //j=C(i);temp2 = temp2 + r[j][i];}if(j==N-1){qi[i] = P[i] + temp2;temp2 = 0;}}}
}
void panjue(bool c[M],ap_fixed <10,8,AP_TRN,AP_SAT> qi[M]){int i;
//****************************************判决*****************************************LOOP_panjue:for (i = 0; i < M; i++) {
#pragma HLS pipeline rewindif (qi[i] > 0)c[i] = 0;elsec[i] = 1;//}
}
void variable_note_update(int j,bool H[N][M],data_zj r_copy[M],data_zj q[M], ap_fixed<10,8, AP_TRN, AP_SAT> qi[M]) {int i,b;j表示计算第几行//*****************************************变量节点更新*****************************************LOOP_v_update_1:for (i = 0; i < M; i++) {
#pragma HLS UNROLL factor=2
#pragma HLS pipeline rewindq[i]=qi[i]-r_copy[i];}
}ap_int<1> check_formula(int diedai,bool c[M],bool H[N][M]) {
//根据校验矩阵计算是否译码完成int i, j;ap_uint<11> s[N];//矩阵相乘后的结果ap_int<11> temp = 0;ap_int<11> s_sum = 0;//s_sum负责记录满足条件,即s[i]=0的个数LOOP_formula1:for (j = 0; j < N; j++) {//N个校验方程LOOP_formula2:for (i = 0; i < M; i++) {
#pragma HLS pipeline rewindtemp = temp + c[i] * H[j][i];if(i==M-1){if (temp%2 == 0) s_sum = s_sum + 1;temp = 0;}}}if (s_sum == N)return 1;//满足校验矩阵,结束迭代elsereturn 0;s_sum = 0;}
void update(bool H[N][M],data_zj r[N][M],ap_fixed<10, 8, AP_TRN, AP_SAT> qi[M]){
LOOP_N_hang:for(int j=0;j<N;j++){if(j==0)LOOP_r_copy:for (int i = 0; i < M; i++)
#pragma HLS pipeline II=1r_copy[i] = r[0][i];variable_note_update(j,H,r_copy,q,qi);check_note_update(j,H,q,r,r_copy);}
}
void decode(bool c[M]) {ap_uint<4> diedai_max=5;ap_uint<4> diedai;//迭代次数,即第几次迭代ap_uint<1> result=0;bool H[N][M] = {#include"H.h"};data_zj P[M] = {#include"y.h"};///*************************************译码******************************************int i,j;LOOP_initialize:for (i = 0; i < M; i++) {
#pragma HLS pipelinec[i]=BPSK(P[i]);}LOOP_diedai:for(diedai=0;diedai<diedai_max;diedai=diedai+1){result = check_formula(diedai,c,H);if (result) {break;}else{update(H,r,qi);variable_note_pos(P, H, r, qi);panjue(c, qi);}}
}
3.C++代码在HLS中的综合结果
将上述代码放到HLS中综合,相关的约束大部分已写在代码,其余约束如下:
set_directive_resource -core RAM_2P_LUTRAM "check_note_update" r
set_directive_resource -core RAM_2P_LUTRAM "variable_note_pos" r
set_directive_resource -core RAM_2P_LUTRAM "decode" r
set_directive_resource -core RAM_2P_LUTRAM "check_note_update" q
set_directive_resource -core RAM_2P_LUTRAM "check_note_update" r_copy
set_directive_resource -core RAM_2P_LUTRAM "variable_note_update" q
set_directive_resource -core RAM_2P_LUTRAM "variable_note_update" r_copy
set_directive_resource -core RAM_2P_LUTRAM "variable_note_update" qi
set_directive_resource -core RAM_2P_LUTRAM "decode" q
set_directive_resource -core RAM_2P_LUTRAM "decode" r_copy
set_directive_resource -core RAM_2P_LUTRAM "decode" qi
set_directive_resource -core RAM_2P_LUTRAM "variable_note_pos" qi
set_directive_resource -core RAM_2P_LUTRAM "panjue" qi
set_directive_resource -core ROM_2P_LUTRAM "decode" P
set_directive_dependence -variable r -class array -type intra -direction WAR -dependent false "check_note_update/LOOP_check_note_update1"
综合后的结果如下图:


进行联合仿真,验证综合后的代码功能是否正确,波形如下:

三、Verilog代码
HLS综合后生成的IP放到vivado中,与其他模块一起仿真,负责将译码后的输出通过串口发送出去。整体需要调用时钟生成IP与串口发送模块。
之后,将电路烧写到开发板中,并连回PC。(下图中的数据是不断重复的)

四、MATLAB对译码算法仿真
具体的MATLAB译码算法代码,主体使用了github别人开源的代码,自己对其进行了一定的修改,不在写出。
设置仿真帧数为10^6,仿真得到FER与迭代次数、加入噪声信噪比之间的关系如下图:


五、整个过程中出现的部分问题和解决方法
1.HLS中生成IP失败
官网中已有相关的解决方法:https://blog.csdn.net/qq_41873311/article/details/122596244
其中需要在Xilinx文件夹目录下,执行脚本;但自己电脑上安装路径没有该文件夹,后发现官方默认的Xilinx文件夹对应自己电脑中vivado安装的文件夹:E:\learn\software\vivado\rote。运行对应的脚本后成功出现下图:
2.HLS优化时将2个数组合并成1个数组失败
一个数组被resource指定成ROM,而合并后的数组被指定为RAM,发生冲突,导致优化失败
3.Vivado中没有自己需要的开发板和芯片型号
首先需要对Vivado更新,安装第二个版本,成功使vivado支持所有k7系列的芯片,从而使vivado中找到了板子的芯片xc7k325t-ffg900;(此时没有板子的信息)
之后,按照官方教程在digilent的GitHub库中将boards的new文件夹的信息全部拷下来到安装路径下面相应的文件夹。
4Vivado中无法调用HLS生成的IP
HLS中选择的芯片和vivado中选择的芯片要相同。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
