原文链接(相关文章合集): OFDM 802.11a的xilinx FPGA实现
1.前言
在之前已经完成了data域数据的处理,在构建整个802.11a OFDM数据帧的时候,还剩下前导码和signal域的数据帧,这两部分的内容。 PLCP的前导部分由一组重复10次的短训练序列和一组加了一个长型保护间隔与重复2次的有效OFDM符号组成的长训练序列组成。上一节实现了短训练序列,这一节实现长训练序列。
2.原理
从时域的帧结构来看,在短训练序列之后是长训练序列,其长度为8us,其中包括两个有效()FDM符号的长度(每个3.2us)和一个长型保护间隔的长度(1.6us)。长训练序列主要用于精确的频率偏差估计和信道估计。从频域来看,长训练序列符号与正常的OFDM符号一样由53(包括直流处一个取“0”值的空符号)个子载波组成,分别占据从一26~26的子信道。长训练的作用之一是在频率进行信道均衡。为了简化接收端的信道估计运算,传输的数据是BPSK调制的,即
在实际使用过程当中,由与短训练序列和长训练序列是固定的,因此可以先使用matlab,python等工具先将短训练序列和长训练序列生成出来,然后将序列存储在ROM当中,在使用的时候,直接从ROM当中读取出来就可以了。
3.Matlab生成长训练序列
由于Data域数据在IFFT处理之前会扩大8倍,相应的长训练序列在进行IFFT处理之前也需要扩大8倍。同时长训练序列也是需要经过加窗处理的,即多输出一个样值,该值为第一个样值,同时将第一个样值与最后一个样值缩小一倍。
lts = [ 0,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,0,0,0,0,0,0,0,0,0,0,0,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 ];
lts_time = 8 * ifft(lts,64); %长训练序列进行ifft
q = quantizer('fixed','round','saturate',[8,6]);%复数以8位定点数形式进行输出,格式为:1位符号位,一位整数位,6位小数位,负数以补码形式表示。
lts_time_q = num2bin(q,lts_time);%量化,存到FPGA的ROM
lts_rom = [0.5*lts_time(33),lts_time(34:end),lts_time,lts_time,0.5*lts_time(1)]; %长训练序列加窗
长训练序列时域值(复数以8位定点数形式进行输出,格式为:1位符号位,一位整数位,6位小数位,负数以补码形式表示):
'01010000 + 00000000i'
'11111101 + 11000010i'
'00010100 + 11000111i'
'00110010 + 00101010i'
'00001011 + 00001110i'
'00011111 + 11010011i'
'11000101 + 11100100i'
'11101100 + 11001010i'
'00110010 + 11110011i'
'00011011 + 00000010i'
'00000001 + 11000101i'
'10111010 + 11101000i'
'00001101 + 11100010i'
'00011110 + 11111000i'
'11110100 + 01010010i'
'00111101 + 11111110i'
'00100000 + 11100000i'
'00010011 + 00110010i'
'11100011 + 00010100i'
'10111101 + 00100001i'
'00101010 + 00101111i'
'00100100 + 00000111i'
'11100001 + 00101010i'
'11100011 + 11110101i'
'11101110 + 10110011i'
'11000010 + 11111000i'
'10111111 + 11110110i'
'00100110 + 11011010i'
'11111111 + 00011100i'
'11010001 + 00111011i'
'00101111 + 00110110i'
'00000110 + 00110010i'
'10110000 + 00000000i'
'00000110 + 11001110i'
'00101111 + 11001010i'
'11010001 + 11000101i'
'11111111 + 11100100i'
'00100110 + 00100110i'
'10111111 + 00001010i'
'11000010 + 00001000i'
'11101110 + 01001101i'
'11100011 + 00001011i'
'11100001 + 11010110i'
'00100100 + 11111001i'
'00101010 + 11010001i'
'10111101 + 11011111i'
'11100011 + 11101100i'
'00010011 + 11001110i'
'00100000 + 00100000i'
'00111101 + 00000010i'
'11110100 + 10101110i'
'00011110 + 00001000i'
'00001101 + 00011110i'
'10111010 + 00011000i'
'00000001 + 00111011i'
'00011011 + 11111110i'
'00110010 + 00001101i'
'11101100 + 00110110i'
'11000101 + 00011100i'
'00011111 + 00101101i'
'00001011 + 11110010i'
'00110010 + 11010110i'
'00010100 + 00111001i'
'11111101 + 00111110i'
4.硬件实现
LTS Generator的设计原理与实现方式与STS Generator相似。标准规定,LTS 包括两个周期的 Symbol(每个有64个样值)和一个长度为32个采样点的长型CP(LTS64个样值中的后32个)。为此,存储LTS的片内ROM被设定为64字X16位,如上面所示,读取时通过对Read Address的控制先输出后32个地址空间中的样值形成CP,然后再将整个ROM中的样值按顺序重复读取两次。verilog代码如下:
assign En_cnt = LTS_din_rdy & ~cnt_last;
assign addr = cnt - 6'd32;
counter #(.CNT_NUM('d161),
.ADD(1'b1))
u_counter(
.clk (clk ),
.rst_n (rst_n ),
.En_cnt (En_cnt ),
.cnt (cnt ),
.cnt_last (cnt_last )
);
always @(posedge clk or negedge rst_n)
if(!rst_n)begin
LTS_dout_last <= 1'b0;
LTS_dout_Index <= 'd0;
end
else begin
LTS_dout_last <= cnt_last;
LTS_dout_Index <= cnt;
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin //时域样值Im Re
Long_Mem[0] <= {8'b00000000 , 8'b01010000};
Long_Mem[1] <= {8'b11000010 , 8'b11111101};
Long_Mem[2] <= {8'b11000111 , 8'b00010100};
Long_Mem[3] <= {8'b00101010 , 8'b00110010};
Long_Mem[4] <= {8'b00001110 , 8'b00001011};
Long_Mem[5] <= {8'b11010011 , 8'b00011111};
Long_Mem[6] <= {8'b11100100 , 8'b11000101};
Long_Mem[7] <= {8'b11001010 , 8'b11101100};
Long_Mem[8] <= {8'b11110011 , 8'b00110010};
Long_Mem[9] <= {8'b00000010 , 8'b00011011};
Long_Mem[10] <= {8'b11000101 , 8'b00000001};
Long_Mem[11] <= {8'b11101000 , 8'b10111010};
Long_Mem[12] <= {8'b11100010 , 8'b00001101};
Long_Mem[13] <= {8'b11111000 , 8'b00011110};
Long_Mem[14] <= {8'b01010010 , 8'b11110100};
Long_Mem[15] <= {8'b11111110 , 8'b00111101};
Long_Mem[16] <= {8'b11100000 , 8'b00100000};
Long_Mem[17] <= {8'b00110010 , 8'b00010011};
Long_Mem[18] <= {8'b00010100 , 8'b11100011};
Long_Mem[19] <= {8'b00100001 , 8'b10111101};
Long_Mem[20] <= {8'b00101111 , 8'b00101010};
Long_Mem[21] <= {8'b00000111 , 8'b00100100};
Long_Mem[22] <= {8'b00101010 , 8'b11100001};
Long_Mem[23] <= {8'b11110101 , 8'b11100011};
Long_Mem[24] <= {8'b10110011 , 8'b11101110};
Long_Mem[25] <= {8'b11111000 , 8'b11000010};
Long_Mem[26] <= {8'b11110110 , 8'b10111111};
Long_Mem[27] <= {8'b11011010 , 8'b00100110};
Long_Mem[28] <= {8'b00011100 , 8'b11111111};
Long_Mem[29] <= {8'b00111011 , 8'b11010001};
Long_Mem[30] <= {8'b00110110 , 8'b00101111};
Long_Mem[31] <= {8'b00110010 , 8'b00000110};
Long_Mem[32] <= {8'b00000000 , 8'b10110000};
Long_Mem[33] <= {8'b11001110 , 8'b00000110};
Long_Mem[34] <= {8'b11001010 , 8'b00101111};
Long_Mem[35] <= {8'b11000101 , 8'b11010001};
Long_Mem[36] <= {8'b11100100 , 8'b11111111};
Long_Mem[37] <= {8'b00100110 , 8'b00100110};
Long_Mem[38] <= {8'b00001010 , 8'b10111111};
Long_Mem[39] <= {8'b00001000 , 8'b11000010};
Long_Mem[40] <= {8'b01001101 , 8'b11101110};
Long_Mem[41] <= {8'b00001011 , 8'b11100011};
Long_Mem[42] <= {8'b11010110 , 8'b11100001};
Long_Mem[43] <= {8'b11111001 , 8'b00100100};
Long_Mem[44] <= {8'b11010001 , 8'b00101010};
Long_Mem[45] <= {8'b11011111 , 8'b10111101};
Long_Mem[46] <= {8'b11101100 , 8'b11100011};
Long_Mem[47] <= {8'b11001110 , 8'b00010011};
Long_Mem[48] <= {8'b00100000 , 8'b00100000};
Long_Mem[49] <= {8'b00000010 , 8'b00111101};
Long_Mem[50] <= {8'b10101110 , 8'b11110100};
Long_Mem[51] <= {8'b00001000 , 8'b00011110};
Long_Mem[52] <= {8'b00011110 , 8'b00001101};
Long_Mem[53] <= {8'b00011000 , 8'b10111010};
Long_Mem[54] <= {8'b00111011 , 8'b00000001};
Long_Mem[55] <= {8'b11111110 , 8'b00011011};
Long_Mem[56] <= {8'b00001101 , 8'b00110010};
Long_Mem[57] <= {8'b00110110 , 8'b11101100};
Long_Mem[58] <= {8'b00011100 , 8'b11000101};
Long_Mem[59] <= {8'b00101101 , 8'b00011111};
Long_Mem[60] <= {8'b11110010 , 8'b00001011};
Long_Mem[61] <= {8'b11010110 , 8'b00110010};
Long_Mem[62] <= {8'b00111001 , 8'b00010100};
Long_Mem[63] <= {8'b00111110 , 8'b11111101};
LTS_dout <= 'd0;
LTS_dout_vld <= 1'b0;
end
else if(LTS_din_rdy & LTS_dout_last)
LTS_dout_vld <= 1'b0;
else if(cnt == 'd0)begin
LTS_dout <= {$signed(Long_Mem[32][15:8])>>>1,$signed(Long_Mem[32][7:0])>>>1};
LTS_dout_vld <= 1'b1;
end
else if(cnt_last)begin
LTS_dout <= {$signed(Long_Mem[0][15:8])>>>1,$signed(Long_Mem[0][7:0])>>>1};
LTS_dout_vld <= 1'b1;
end
else if(cnt < 32)begin
LTS_dout <= Long_Mem[{1'b1,cnt[4:0]}];
LTS_dout_vld <= 1'b1;
end
else begin
LTS_dout <= Long_Mem[addr[5:0]];
LTS_dout_vld <= 1'b1;
end
end
5.ModelSim仿真
6.和Matlab仿真结果对比
%% LTS
FPGA_LTS_dout = readlines('D:/FPGA/OFDM_802.11a_my/TX/matlab/LTS_data_out.txt','EmptyLineRule','skip')';
display(FPGA_LTS_dout);
FPGA_Im_LTS_dout = extractBefore(FPGA_LTS_dout,9);
FPGA_Re_LTS_dout = extractAfter(FPGA_LTS_dout,8);
q = quantizer('fixed','round','saturate',[8,6]);
FPGA_Re_LTS_dout = bin2num(q,FPGA_Re_LTS_dout);
FPGA_Im_LTS_dout = bin2num(q,FPGA_Im_LTS_dout);
FPGA_Re_LTS_dout = cell2mat(FPGA_Re_LTS_dout);
FPGA_Im_LTS_dout = cell2mat(FPGA_Im_LTS_dout);
FPGA_LTS_dout = FPGA_Re_LTS_dout + 1j*FPGA_Im_LTS_dout;
lts_rom_q = num2bin(q,lts_rom);%量化
lts_rom = bin2num(q,lts_rom_q);%反量化
check_LTS = FPGA_LTS_dout == lts_rom.';
display(check_LTS);
check_LTS =
1×161 logical 数组
列 1 至 40
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 1 1
列 41 至 80
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 1 1
列 81 至 120
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 1 1
列 121 至 160
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 1 1
列 161
1
今天在设计这部分时,发现前面OFDM 802.11a的FPGA实现中的文章在涉及到加窗操作的时候,使用到了移位操作,但是没有考虑到有符号数的问题。而恰好今天发现了这一问题,写了一篇文章详细对verilog中移位操作时,有符号数和无符号数的区别进行讲解,并且设计补码、反码、原码的知识。
感兴趣的可以前往查看https://mp.weixin.qq.com/s/5II3YzVeEXl9BoTe8MM5ZQ
原文链接(相关文章合集):OFDM 802.11a的xilinx FPGA实现