OFDM802.11a的FPGA实现(七)一级交织:分组交织器(含verilog和matlab代码)

ops/2024/11/9 10:03:30/

1.前言

  在前面的文章中讲解了卷积编码和删余,实现了1/2、2/3、3/4编码速率的输出。数据域在编码之后,下一个部分就是交织。今天对交织进行具体实现。

  交织是为了在时域或频域或者同时在时域、频域上分布传输的信息比特,使信道的突发错误在时间上得以扩散,从而使得译码器可以将它们当作随机错误处理。交织器在几个分组长度或几个约束长度的范围内对码元进行混洗,这个范围是由突发持续时间决定的。通信系统的交织模式取决于信道特性。如果系统在一个纯粹的AWGN环境下运行,即准平稳信道,那么在一个数据包的持续时间上基本没有什么变化,就不需要交织。因为这时,通过重新分配的方法是无法改变误码分布的。

  交织必然在系统中引人延时,这是因为接收到的比特顺序与信息源发送时的顺序是不相同的。通信系统通常规定了系统所能容忍的最大延时,因此也限制了所能使用的交织器的交织深度。

2.原理

2.1分组交织器

  分组交织器是针对一组bit进行的,该分组当中bit的数量称为交织深度,交织深度越大,离散度越大,抗突发差错的能力也就越大,相应的引起的交织编码处理时间也越长。
我们可以用一个矩阵来描述分组交织器,将数据按照行进行写入数据,按照列的方式进行读出,或者按照相反的方式也是可以的。

4x5分组交织器的写和读的结构

  4x5的分组交织器,由此可见交织深度为20,以行的方式进行写入,以列的方式进行读出。

01234
56789
1011121314
1516171819

  反交织的作用与交织的作用正好相反,将上图当中的矩阵进行转秩,然后同样按照以行的方式进行写入,以列的方式进行读出,即可将原始的数据恢复。

051015
161116
271217
381318
491419

2.2 802.11a当中的交织

  802.11a规定了包括调制方案、编码效率以及数据速率之间的关系,如下面三个表所示:
表1

表2

表3
  802.11a当中交织的深度为一个OFDM符号,因此这是一个分组交织器,交织深度与所采用的调制方式有关:BPSK,QPSK,16QAM,64QAM的交织深度分别为48,96,192,288个bit。每种调制当时的交织深度是通过数据子载波的数量与每个符号当中bit的个数相乘得到的。确定了调制方案、编码效率以及数据速率之后,就可以计算出每个OFDM符号在调制前的码长,公式如下:
N C B P S = 48 × M ÷ R N_{CBPS} = 48 \times M \div R NCBPS=48×M÷R

  其中,$ N_{CBPS} 表示每个 O F D M 符号在调制前的码长, 表示每个OFDM符号在调制前的码长, 表示每个OFDM符号在调制前的码长,M 表示调制阶数( 1 − B P S K , 2 − Q P S K , 3 − 8 Q A M , 4 − 16 Q A M , 5 − 32 Q A M , 6 − 64 Q A M ) , 表示调制阶数(1-BPSK,2-QPSK,3-8QAM,4-16QAM,5-32QAM,6-64QAM), 表示调制阶数(1BPSK2QPSK,38QAM,416QAM532QAM,664QAM,R$表示编码效率。

  802.11a规定数据域的交织分为两个步骤,今天主要讲第一次交织,其目的是保证相邻的比特在经过ofdm调制之后数据会落在不相邻的子载波上。
以QAM64调制,速率为54Mb/s为例,能够确定 s = 3 , N C B P S = 288 s=3,N_{CBPS = 288} s=3NCBPS=288有了这些参数之后,我们就可以将这个交织的变换关系给计算出来,我们只需要根据这个关系进行将数据写入到一个RAM对应的地址当中,再依次将数据从RAM中读取出来即可以完成交织的工作。802.11a中最大的交织深度即为288,搞定了这个之后其他的也是一样的操作,其关系网如下所示:

以QAM64调制,速率为54Mb/s为例的交织关系

3.verilog代码

  在设计上,为保证流水线设计结构,能够对数据进行连续处理,需要将RAM的存储空间设置为最大交织深度288的一倍。相邻的OFDM符元就能实现乒乓缓存。0-287地址空间为乒乓缓存A区数据空间,288-575为乒乓缓存B区数据空间,实现A区写入数据,B区读取数据。 当输入数据有效时,对输入数据按一级交织方式进行错位写入RAM,并驱动写入计数器以交织深度为长度进行计数,计满一个交织深度后,拉高当前缓存区标志信号,并驱动读取计数器以当前缓存区长度计数。模块接口如下所示:

**《基于XILINX FPGA的OFDM通信系统基带设计》**书中对于这个模块是这样设计的:

  他又用到了跨时钟域,其实很没有必要,还是采用之前的valid-ready握手机制就可以解决这个问题。还有这里虽说是用到了RAM,但是个人认为并没有必要去调用FPGA的Block RAM资源,因为一块Block RAM一般都是18k或者36k,用在这里很浪费。具体可以去查看芯片资料。这里两个缓存区加起来不过也才576位,直接用两个288位的寄存器即可。

  接下来需要解决写地址的问题:定义第一次交织前的编码比特序列角标顺序号用 k 表示,交织之后的数据序列角标顺序号用 i 表示,第二次交织后数据序列角标顺序号用 j 来表示,我们可以得到如下公式:

  由于FPGA中直接用除法和取余非常浪费资源,此处处被除数是固定的,我们只需要采用移位的方式就可以实现除法和取余。代码如下:

//---------valid-ready握手---------//
assign	wr_en	 = intv1_dout_rdy & intv1_din_vld ;//与上游握手成功,开始接收数据
assign	rd_en	 = intv1_din_rdy & intv1_dout_vld ;//与下游握手成功,开始输出
assign	intv1_dout_rdy = (~bufferA_full | ~bufferB_full) ? 1'b1 : 1'b0;
assign	intv1_dout_vld = ( bufferA_full |  bufferB_full) ? 1'b1 : 1'b0;
//------------buffer读写控制----------------//	
always@(posedge clk or negedge rst_n ) beginif(!rst_n)buffer_flag <= 1'b0;//0为A区,1为B区else if(w_cnt_last & wr_en)buffer_flag <= ~buffer_flag;//0为A区,1为B区
end
//-----------buffer写满和读空控制------------//	
always @(posedge clk or negedge rst_n)beginif(!rst_n)bufferA_full <= 1'b0;else if(w_cnt_last & wr_en & ~buffer_flag)bufferA_full <= 1'b1;else if(r_cnt_last & rd_en & bufferA_full)bufferA_full <= 1'b0;
endalways @(posedge clk or negedge rst_n)beginif(!rst_n)bufferB_full <= 1'b0;else if(w_cnt_last & wr_en & buffer_flag)bufferB_full <= 1'b1;else if(r_cnt_last & rd_en & bufferB_full)bufferB_full <= 1'b0;
end
//-----------根据符号长度选择写计数值------------//	
always@( * ) begin  case ( intv1_Con )N_48 	:  	cnt_Max = 48 -1	; N_96 	:  	cnt_Max = 96 -1	;  N_192	:  	cnt_Max = 192 -1;  N_288	:  	cnt_Max = 288 -1;  default	:	cnt_Max = 48 -1	;endcase 
end
//------buffer写入操作,以行写入,但是地址换算为列输出时的地址--//
counter_in #(.CNT_NUM('d288),.ADD(1'b1))
u_counter_w(
.clk		(clk				),	
.rst_n		(rst_n				),
.En_cnt		(wr_en				), 
.cnt_din	(cnt_Max			),     
.cnt		(w_cnt				),	
.cnt_last	(w_cnt_last			)
);always@(*) beginif(!rst_n) w_addr = 0; else begincase(intv1_Con)N_48	: w_addr = w_cnt[3:0] + (w_cnt[3:0]<<1) + w_cnt[8:4] ; //N = 48//w_cnt *3	  N_96	: w_addr = (w_cnt[3:0]<<1)  + (w_cnt[3:0]<<2) + w_cnt[8:4] ;//N = 96 //w_cnt *6N_192	: w_addr = (w_cnt[3:0]<<3)  + (w_cnt[3:0]<<2) + w_cnt[8:4]; //N = 192//w_cnt *12N_288	: w_addr = (w_cnt[3:0]<<4)  + (w_cnt[3:0]<<1) + w_cnt[8:4]; //N = 288//w_cnt *18default	: w_addr = w_cnt[3:0] + (w_cnt[3:0]<<1) + w_cnt[8:4] ; //N = 48//w_cnt *3endcaseend	
endalways@(posedge clk or negedge rst_n ) beginif(!rst_n)beginbufferA <= 1'b0;bufferB <= 1'b0;	endelse if(wr_en)beginif(buffer_flag)bufferB[w_addr] <= intv1_din;elsebufferA[w_addr] <= intv1_din;end	
end//------buffer读出操作,按照顺序地址读出---------//
counter_in #(.CNT_NUM('d288),.ADD(1'b1))
u_counter_r(
.clk		(clk				),	
.rst_n		(rst_n				),
.En_cnt		(rd_en				), 
.cnt_din	(cnt_Max			),     
.cnt		(r_cnt				),	
.cnt_last	(r_cnt_last			)
);assign	intv1_dout = (rd_en & buffer_flag) ? bufferA[r_cnt] : bufferB[r_cnt];//输出Map_Type,作为后面调制映射的输入,确定调制方式
always@(posedge clk or negedge rst_n ) beginif(!rst_n) begin Map_Type <= 0;        end    else if(intv1_dout_vld) begin Map_Type <= intv1_Con ; end   
end 

4.Matlab仿真

  Matlab代码如下:

matlab">%% 一级交织
%一级交织器产生:16*list 列写行读
conv_out_Len = length(conv_out);    %编码后的长度
symbol_Len = conv_out_Len / k;      %单个符号码长度
list = conv_out_Len / k / 16;       %一级交织的列数
row = length(conv_out)/list;        %将数据转为list列的矩阵 
ram = zeros(row,list);              %将输入数据存储在list列,row行的矩阵中
for n = 1:k                         %将ram矩阵拆为n个16*list的矩阵,每次写入list列for m = 1:symbol_Len            %以列写入row_index = mod(m-1,16)+1;      %写入到矩阵中行的位置list_index = ceil(m/16);           %写入到矩阵中列的位置ram((n-1)*16+row_index,list_index) = conv_out((n-1)*symbol_Len+m); %将数据写入ram矩阵end
end
int_lea_1_out = zeros(1,conv_out_Len);
for n = 1:kfor row_index = 1:16for list_index = 1:list    %按行读出ram中的数据,每次读list列int_lea_1_out((n-1)*symbol_Len+(row_index-1)*list+list_index) = ...ram(((n-1)*16+row_index),list_index);endend
end

5.ModelSim仿真

  按照如下连接仿真:

  仿真截图如下,可以看到读写可以同时进行,实现了乒乓操作。


为了验证模块的正确性,仿真使用4个OFDM符号输入,调制方式位BPSK(其他调制方式数据太多,不方便展示),3/4编码效率。计算得知,每个符号码长为48,即16行3列,列写入的数据如下:

110
100
100
100
110
001
100
110
010
100
000
011
001
101
101
000
001
101
011
011
010
000
010
010
010
001
100
111
010
110
011
011
001
001
011
011
100
011
100
001
100
100
011
010
111
000
100
101
110
001
000
111
111
100
101
101
110
001
100
101
100
010
011
110

  将matlab仿真的数据和ModelSim中的数据进行对比。

matlab">clc;
%% 串并转换
test_data = load('D:/FPGA/OFDM_802.11a_my/TX/matlab/test_data.txt')';
display(test_data);
FPGA_S2P2S = load('D:/FPGA/OFDM_802.11a_my/TX/matlab/u2_data_out.txt')';
display(FPGA_S2P2S);
check_S2P = test_data == FPGA_S2P2S; 
display(check_S2P);
%% 扰码
FPGA_scram_dout = load('D:/FPGA/OFDM_802.11a_my/TX/matlab/scram_data_out.txt')';
display(scram_out0);
display(FPGA_scram_dout);
check_scram = FPGA_scram_dout == scram_out0;
display(check_scram);
%% 卷积编码
FPGA_conv_dout = load('D:/FPGA/OFDM_802.11a_my/TX/matlab/conv_data_out.txt')';
display(conv_out0);
display(FPGA_conv_dout);
check_conv = FPGA_conv_dout == conv_out0;
display(check_conv);
%% 删余
FPGA_punt_dout = load('D:/FPGA/OFDM_802.11a_my/TX/matlab/punt_data_out.txt')';
display(conv_out);
display(FPGA_punt_dout);
check_punt = FPGA_punt_dout == conv_out;
display(check_punt);
%% 交织
FPGA_intv1_dout = load('D:/FPGA/OFDM_802.11a_my/TX/matlab/intv1_data_out.txt')';
display(int_lea_1_out);
display(FPGA_intv1_dout);
check_intv1 = FPGA_intv1_dout == int_lea_1_out;
display(check_intv1);

  matlab交织输出如下:

int_lea_1_out =

列 1 至 25

 1     1     0     1     0     0     1     0     0     1     0     0     1     1     0     0     0     1     1     0     0     1     1     0     0

列 26 至 50

 1     0     1     0     0     0     0     0     0     1     1     0     0     1     1     0     1     1     0     1     0     0     0     0     0

列 51 至 75

 1     1     0     1     0     1     1     0     1     1     0     1     0     0     0     0     0     1     0     0     1     0     0     1     0

列 76 至 100

 0     0     1     1     0     0     1     1     1     0     1     0     1     1     0     0     1     1     0     1     1     0     0     1     0

列 101 至 125

 0     1     0     1     1     0     1     1     1     0     0     0     1     1     1     0     0     0     0     1     1     0     0     1     0

列 126 至 150

 0     0     1     1     0     1     0     1     1     1     0     0     0     1     0     0     1     0     1     1     1     0     0     0     1

列 151 至 175

 0     0     0     1     1     1     1     1     1     1     0     0     1     0     1     1     0     1     1     1     0     0     0     1     1

列 176 至 192

 0     0     1     0     1     1     0     0     0     1     0     0     1     1     1     1     0

  FPGA交织输出如下:
FPGA_intv1_dout =

列 1 至 25

 1     1     0     1     0     0     1     0     0     1     0     0     1     1     0     0     0     1     1     0     0     1     1     0     0

列 26 至 50

 1     0     1     0     0     0     0     0     0     1     1     0     0     1     1     0     1     1     0     1     0     0     0     0     0

列 51 至 75

 1     1     0     1     0     1     1     0     1     1     0     1     0     0     0     0     0     1     0     0     1     0     0     1     0

列 76 至 100

 0     0     1     1     0     0     1     1     1     0     1     0     1     1     0     0     1     1     0     1     1     0     0     1     0

列 101 至 125

 0     1     0     1     1     0     1     1     1     0     0     0     1     1     1     0     0     0     0     1     1     0     0     1     0

列 126 至 150

 0     0     1     1     0     1     0     1     1     1     0     0     0     1     0     0     1     0     1     1     1     0     0     0     1

列 151 至 175

 0     0     0     1     1     1     1     1     1     1     0     0     1     0     1     1     0     1     1     1     0     0     0     1     1

列 176 至 192

 0     0     1     0     1     1     0     0     0     1     0     0     1     1     1     1     0

  对比结果如下:

check_intv1 =

1×192 logical 数组

列 1 至 38

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

列 39 至 76

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

列 77 至 114

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

列 115 至 152

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

列 153 至 190

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

列 191 至 192

1 1

  结果全为1,说明完全正确。作者也对其他调制方式和编码方案进行了测试,都是正确的,这里就不再赘述。

4总结

  需要回顾前面知识请点击链接:OFDM 802.11a的xilinx FPGA实现


http://www.ppmy.cn/ops/24726.html

相关文章

FPGA搭积木之边沿检测电路

目录 1前言2.原理3.代码4仿真 1前言 今天分享一个FPGA设计中很常用的边沿检测电路&#xff0c;并参数化封装成自己的IP核。该电路的作用是输入一个信号&#xff0c;在其上升沿或者下降沿时&#xff08;可选&#xff09;输出一个时钟周期脉冲。时序图如下&#xff1a; 2.原理 利…

如何在ubuntu 24.04上安装配置x11vnc以便远程访问

在ubuntu 24.04上安装x11vnc的方法&#xff08;经过验证04/25/2024&#xff09; sudo apt update sudo apt install x11vnc x11vnc -storepasswd 下面的命令确认authentication文件的位置&#xff08;示例中的uid 1000&#xff09; systemctl status display-manager.service…

【软件工程】第一章概述与软件生命周期

目录 软件开发方法学&#xff1a;结构化方法面向对象方法两种开发方法的区别从结构到面向对象面向对象技术的优势 什么是软件&#xff1f;为什么出现软件工程学科&#xff1f;软件危机 软件工程的概念软件生命&#xff08;存&#xff09;周期------------重点⭐每个时期每个阶段…

yarn的安装与使用

Yarn的安装与使用主要涉及到以下几个步骤&#xff1a; 安装Yarn&#xff1a; 首先&#xff0c;确保您的系统中已安装Node.js和npm。Yarn可以在Windows、MacOS和Linux上运行。使用npm安装Yarn。运行命令npm install -g yarn来安装和升级Yarn。安装完成后&#xff0c;通过运行y…

【数据采集实操】网页抓取实例之淘宝商品信息抓取||电商API接口数据采集

之前我们已经说过网页抓取的相关内容 上次我们是以亚马逊某网页的产品为例 抓取价格、品牌、型号、样式 主流电商API接口数据采集返回商品价格 SKU 数据 该网页上价格、品牌、型号、样式等 都只有一个 如果网页上的目标内容 根据不同规格有多个 又该怎么提取呢&#xf…

LLMs之MiniCPM:MiniCPM(揭示端侧大语言模型的无限潜力)的简介、安装和使用方法、案例应用之详细攻略

LLMs之MiniCPM&#xff1a;MiniCPM(揭示端侧大语言模型的无限潜力)的简介、安装和使用方法、案例应用之详细攻略 目录 MiniCPM的简介 0、更新日志 1、公开的模型 2、局限性 3、文本模型评测 越级比较: 同级比较&#xff1a; Chat模型比较&#xff1a; DPO后模型比较&am…

Jmeter插件技术:性能测试中服务端资源监控

性能测试过程中我们需要不断的监测服务端资源的使用情况&#xff0c;例如CPU、内存、I/O等。 Jmeter的插件技术可以很好的实时监控到服务器资源的运行情况&#xff0c;并以图形化的方式展示出来&#xff0c;非常方便我们性能测试分析。 操作步骤&#xff1a; 1、安装插件管理…

Swift中的WebView

WebView是Swift中用于显示网页内容的组件&#xff0c;可以将网页嵌入到iOS应用中。WebView可以加载和显示网页、处理用户的交互操作&#xff0c;并提供了一些控制网页内容的方法。 在Swift中使用WebView&#xff0c;首先需要导入WebKit框架&#xff1a; import WebKit然后&am…