利用HLS实现LDPC译码

这是自己第一次写文章,可能存在一定的错误,还望大家海涵。并且由于时间精力问题,没有补充完整。完整的代码已经上传到github中,链接:https://github.com/awdsn/LDPC_MS_decode。

一、译码算法

截取的硕士论文中对MS算法的描述
算法打算采用MS译码算法,更好的话可以改进算法如NMS算法,由于在数据位宽降低的情况下NMS算法译码效果反而不如MS算法译码效果,故自己没有选用。

二、C++代码

1.初始

下面代码将测试模块与译码模块放在了一起;并且按照译码算法中的步骤分别设置了以下子函数:校验节点更新,变量节点更新,校验节点和值计算,校验。


///初始化
void initialize()//计算qji_0
{
	int a, b;
	bool c_in[M];
	///读取文本到数组
	ifstream infile; //定义读取文件流,相对于程序来说是in
	infile.open("接收到信号y-test.txt");//打开文件//接收到信号y//编码后向量x
	for (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=1
			else
				q[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; //定义读取文件流,相对于程序来说是in
	infile.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;//行数------j
				b0 = b;//列数-------i
				for (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;//行数------j
				b0 = b;//列数-------i
				for (j = 0; j < N; j++) {
					if (H[j][b0]) {//j_temp=C(i)\j
						j_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;
		else
			c[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;//满足校验矩阵,结束迭代
	else
		fanhui = 0;
	return fanhui;
	s_sum = 0;
}

int main() {

	int diedai = 0;//迭代次数,即第几次迭代
	int result, b, i;
	float error_num = 0;
	float error_rate ;
	//读入x
	ifstream infile; //定义读取文件流,相对于程序来说是in
	infile.open("编码后向量x.txt");
	for (b = 0;b < M; b++) {
		infile >> x[b];
		//cout << x[M] << endl;
	}
	infile.close();//读取完成之后关闭文件

	///*************************************译码******************************************//
	initialize();///初始化,计算q0_ij,即pi
	check_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;
			 else
				 sign1[i] = -1;
		 }		
	 }

		LOOP_check_note_update:
//#pragma HLS dependence variable=r inter false
			for (i = 0; i < M; i++) {
#pragma HLS pipeline rewind
				r_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);
					else
						r[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 rewind
			if (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 rewind
			if (qi[i] > 0)
				c[i] = 0;
			else
				c[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 rewind
				q[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 rewind
			temp = 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;//满足校验矩阵,结束迭代
	else
		return 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=1
				r_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 pipeline
			c[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中选择的芯片要相同。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值