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

server/2024/10/18 20:19:40/

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/server/24472.html

相关文章

Flutter应用下拉菜单设计DropdownButtonFormField控件介绍

文章目录 DropdownButtonFormField介绍使用方法重点代码说明属性解释 注意事项 DropdownButtonFormField介绍 Flutter 中的 DropdownButtonFormField 是一个用于在表单中选择下拉菜单的控件。它是 DropdownButton 和 TextFormField 的组合&#xff0c;允许用户从一组选项中选择…

使用Matplotlib创建漂亮的数据可视化图表

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 使用 Matplotlib 创建漂亮的数据可视化图表 在数据科学和机器学习领域&#xff0c;数据可视…

2024-04-29 区块链-项目-记录

摘要: 2024-04-29 区块链-项目-记录 区块链项目记录: (1) C 比特币(BTC) github&#xff1a; https://github.com/bitcoin/bitcoin 莱特币(LTC) github&#xff1a; https://github.com/litecoin-project/litecore-litecoin 瑞波币(XRP) github&#xff1a; https://gi…

目标检测(二阶段)领域,常见词汇

1、Backbone&#xff08;主干网络&#xff09; 定义: Backbone是目标检测模型的基础部分&#xff0c;通常是一个预训练的卷积神经网络&#xff08;如ResNet、VGG、MobileNet等&#xff09;&#xff0c;负责从输入图像中提取多层特征图。这些特征图包含了不同尺度和抽象级别的信…

开源AI智能名片商城小程序:深度解读IMC(IP、MarTech、Content)视角

在数字化浪潮中&#xff0c;私域流量的运营已成为企业不可或缺的增长引擎。而开源AI智能名片商城小程序&#xff0c;则是以一种全新的视角——IMC&#xff08;IP、MarTech、Content&#xff09;&#xff0c;为企业打开私域流量运营的新篇章。今天&#xff0c;我们就来一起深入解…

[C++][算法基础]最长公共子序列(动态规划)

给定两个长度分别为 &#x1d441; 和 &#x1d440; 的字符串 &#x1d434; 和 &#x1d435;&#xff0c;求既是 &#x1d434; 的子序列又是 &#x1d435; 的子序列的字符串长度最长是多少。 输入格式 第一行包含两个整数 &#x1d441; 和 &#x1d440;。 第二行包含…

用Scrapy编写第一个入门项目(基础四件套:spider,pipeline,setting,items)

简介&#xff1a;scrapy是一个用于爬取网页并提取数据的应用框架&#xff0c;也可用于提取API数据 写在前面&#xff1a;只想看scrapy的童鞋子请跳过5-7直接step8&#xff09; step5&#xff0c;6是xpath和css入门&#xff0c;用于提取数据&#xff1b; step7是文件储存方式&…

html如何实现按钮跳转,以及访问随机跳转

html如何实现按钮跳转&#xff0c;以及访问随机跳转。 <!DOCTYPE html> <html> <head><title>访问者跳转模拟</title><script type"text/javascript">function redirectToPort() {// 基于时间或随机数生成端口号var basePort …