目录
完整工程链接(含verilog和Matlab代码):OFDM 802.11a的xilinx FPGA实现
1.前言
上一节完成了MUC模块,实现了对MAC层进行数据交互,以及控制物理层各个模块的工作。现在还剩下最后一个DAC模块,作用是将时域的输出按照前导码、signal帧、data域的顺序进行排列,将数据速率从125M的突发形式,转换为20M的连续形式,然后送往硬件DAC输出模拟信号。
PPDU帧结构
PPDU帧结构
2.实现思路
在MCU模块当中,signal域和data域的数据在处理之前已经进行了排序,所以IFFT的输出顺序就是先signal域数据的时域,然后data域的时域。需要设计DAC模块,让前导码(长短训练序列)先输出,然后再输出IFFT输出的数据。
前面设计的训练序列模块,在输出结束后会产生一个last信号,我们可以将这个信号作为选通的条件,为低时,选择训练序列输出;为高时,选择ifft结果输出,来实现时域组帧。代码如下所示:
//dac_train_din_last为训练序列输出完毕标志,为低时,选择训练序列输出;为高时,选择ifft结果输出,来实现时域组帧
assign s_axis_tvalid = dac_train_din_last ? dac_ifft_din_vld : dac_train_din_vld ;
assign s_axis_tlast = dac_train_din_last ? dac_ifft_din_last : dac_train_din_last ;
assign s_axis_tuser = dac_train_din_last ? dac_ifft_din_Index : dac_train_din_Index ;
assign dac_ifft_dout_rdy = dac_train_din_last ? s_axis_tready : 1'b0;
assign dac_train_dout_rdy = dac_train_din_last ? 1'b0 : s_axis_tready;
另外,前导码设计时多输出了一个数据(长训练序列第一个数据,右移1位),这个数据需要与signal域(IFFT模块输出时,已经进行了右移1位)输出的第一个数据进行相加,实现加窗操作。代码如下:
//训练序列最后一个输出与signal域第一个输出相加(由于前面都进行了右移一位,此处只需相加),做加窗处理。
assign s_axis_tdata = dac_train_din_last ? dac_train_din_last_edge_pluse ?
{dac_ifft_din[15:8] + dac_train_din[15:8],
dac_ifft_din[7:0] + dac_train_din[7:0]}:
dac_ifft_din : dac_train_din ;
DAC模块的实现方式其实就是选通,先让训练序列输出,再让IFFT输出,最后将数据速率从125M的突发形式,转换为20M的连续形式,然后送往硬件DAC输出模拟信号。
DAC模块
3.verilog代码
`timescale 1ns / 1ps
module dac(
input clk ,
input clk_dac ,
input rst_n ,
input [15:0] dac_ifft_din ,
input dac_ifft_din_vld ,
input dac_ifft_din_last ,
input [7:0] dac_ifft_din_Index ,
output dac_ifft_dout_rdy ,
input [15:0] dac_train_din ,
input dac_train_din_vld ,
input dac_train_din_last ,
input [8:0] dac_train_din_Index ,
output dac_train_dout_rdy ,
input dac_din_rdy ,
output [15:0] dac_dout ,
output dac_dout_vld ,
output dac_dout_last ,
output [8:0] dac_dout_Index
);
//fifo
wire s_axis_tvalid ;
wire s_axis_tready ;
wire [15:0] s_axis_tdata ;
wire s_axis_tlast ;
wire [8:0] s_axis_tuser ;
wire m_axis_tvalid ;
wire m_axis_tready ;
wire [15:0] m_axis_tdata ;
wire m_axis_tlast ;
wire [8:0] m_axis_tuser ;
wire dac_train_din_last_edge_pluse;
//检测训练序列输出完毕标志的上升沿
edge_detection #(.POSEDGE(1'b1))
u_edge_detection (
.clk (clk ),
.edge_din (dac_train_din_last ),
.edge_pluse (dac_train_din_last_edge_pluse )
);
//训练序列最后一个输出与signal域第一个输出相加(由于前面都进行了右移一位,此处只需相加),做加窗处理。
assign s_axis_tdata = dac_train_din_last ? dac_train_din_last_edge_pluse ?
{dac_ifft_din[15:8] + dac_train_din[15:8],
dac_ifft_din[7:0] + dac_train_din[7:0]}:
dac_ifft_din : dac_train_din ;
//dac_train_din_last为训练序列输出完毕标志,为低时,选择训练序列输出;为高时,选择ifft结果输出,来实现时域组帧
assign s_axis_tvalid = dac_train_din_last ? dac_ifft_din_vld : dac_train_din_vld ;
assign s_axis_tlast = dac_train_din_last ? dac_ifft_din_last : dac_train_din_last ;
assign s_axis_tuser = dac_train_din_last ? dac_ifft_din_Index : dac_train_din_Index ;
assign dac_ifft_dout_rdy = dac_train_din_last ? s_axis_tready : 1'b0;
assign dac_train_dout_rdy = dac_train_din_last ? 1'b0 : s_axis_tready;
fifo_generator_0 u_fifo (
.m_aclk (clk_dac ), // input wire m_aclk
.s_aclk (clk ), // input wire s_aclk
.s_aresetn (rst_n ), // input wire s_aresetn
.s_axis_tvalid (s_axis_tvalid ), // input wire s_axis_tvalid
.s_axis_tready (s_axis_tready ), // output wire s_axis_tready
.s_axis_tdata (s_axis_tdata ), // input wire [15 : 0] s_axis_tdata
.s_axis_tlast (s_axis_tlast ), // input wire s_axis_tlast
.s_axis_tuser (s_axis_tuser ), // input wire [7 : 0] s_axis_tuser
.m_axis_tvalid (m_axis_tvalid ), // output wire m_axis_tvalid
.m_axis_tready (m_axis_tready ), // input wire m_axis_tready
.m_axis_tdata (m_axis_tdata ), // output wire [15 : 0] m_axis_tdata
.m_axis_tlast (m_axis_tlast ), // output wire m_axis_tlast
.m_axis_tuser (m_axis_tuser ) // output wire [7 : 0] m_axis_tuser
);
assign m_axis_tready = dac_din_rdy ;
assign dac_dout = m_axis_tdata ;
assign dac_dout_vld = m_axis_tvalid ;
assign dac_dout_last = m_axis_tlast ;
assign dac_dout_Index = m_axis_tuser ;
endmodule
4.MoselSim仿真
MoselSim仿真
整个设计顶层硬件连接如下图所示:
顶层硬件连接
目前,这个项目还有些许瑕疵,还需要进行测试,等完全调试通过之后,并且通过Matlab仿真进行闭环测试,确定逻辑没有问题,再分享出来,供有需要者学习。