OFDM项目开发(10):FPGA OFDM帧生成模块(FrameGen)设计详解

发布时间:2026/6/26 6:59:19

OFDM项目开发(10):FPGA OFDM帧生成模块(FrameGen)设计详解 一、引言在OFDM正交频分复用基带发射机设计中帧生成模块Frame Generation扮演着至关重要的角色——它负责将经过IFFT变换、添加循环前缀CP和ZC序列后的时域数据按照特定的帧格式组装成完整的OFDM基带帧。在实际工程中帧生成模块面临一个核心挑战跨时钟域数据处理。前端模块如IFFT、CP添加、ZC添加通常工作在高速时钟域如i_clk_8dx而后续DAC或射频前端可能工作在较低速的时钟域如i_clk。如何在不同时钟域之间可靠地传输数据并完成帧组装是本文要解决的核心问题。本文介绍的FrameGen模块采用异步FIFO作为跨时钟域数据缓冲的核心手段实现了从高速写入到低速读出的平滑过渡。该模块已在FPGA平台上验证通过可广泛应用于OFDM基带发射机、软件无线电等场景。二、模块功能概述FrameGen模块的主要功能是数据缓冲将前端输入的I/Q两路10位有符号数据i_Izc/i_Qzc暂存到异步FIFO中跨时钟域传输数据在i_clk_8dx时钟域写入在i_clk时钟域读出帧数据输出在适当的时机读出FIFO中的数据输出完整的I/Q帧数据o_Iframe/o_Qframe端口定义端口方向位宽描述i_clkinput1读时钟低速i_clk_8dxinput1写时钟高速8倍频i_rstinput1异步复位高有效i_eninput1写使能i_Izcinput10I路数据输入有符号i_Qzcinput10Q路数据输入有符号o_Iframeoutput10I路帧数据输出有符号o_Qframeoutput10Q路帧数据输出有符号三、核心设计思路与创新点3.1 创新点一异步FIFO实现跨时钟域组帧传统组帧方案往往采用双时钟握手或寄存器链同步存在吞吐量低、逻辑复杂等问题。本设计采用异步FIFO IP核作为跨时钟域数据缓冲的核心具有以下优势高吞吐量FIFO深度为1024可缓存大量数据避免写满/读空自动同步FIFO内部已处理好读写指针的跨时钟域同步格雷码用户无需关心亚稳态问题状态可观测通过rd_data_count和wr_data_count实时监控FIFO状态3.2 创新点二读使能的自适应控制策略本模块最精巧的设计在于读使能信号的产生逻辑always (posedge i_clk_8dx or posedge i_rst)begin if(i_rst) r_rd_en 1d0; else if(w_wr_data_cnt 288 512 - 10) r_rd_en 1d1; else if(w_empty) r_rd_en 1d0; else r_rd_en r_rd_en; end设计解析w_wr_data_cntFIFO中已写入的数据个数当写入数据量达到288 512 - 10 790时启动读使能这里的288是ZC序列长度 CP长度 其他开销512是IFFT点数-10是预留的时序余量这种阈值触发机制确保了FIFO中有足够数据后再开始读出避免读空3.3 创新点三双寄存器缓存消除亚稳态读出的数据通过两级寄存器同步always (posedge i_clk or posedge i_rst)begin if(i_rst) r_rd_en_2 1d0; else r_rd_en_2 r_rd_en; endr_rd_en在i_clk_8dx域产生r_rd_en_2在i_clk域打拍有效消除了跨时钟域控制信号的亚稳态风险。四、模块代码详解4.1 完整代码module FrameGen( input i_clk , input i_clk_8dx , input i_rst , input i_en , input signed [ 9: 0] i_Izc , input signed [ 9: 0] i_Qzc , output signed[ 9: 0] o_Iframe , output signed[ 9: 0] o_Qframe ); // 输入寄存器 reg ri_en ; reg signed [ 9: 0] ri_Izc ; reg signed [ 9: 0] ri_Qzc ; // 输出寄存器 reg signed [ 9: 0] ro_Iframe ; reg signed [ 9: 0] ro_Qframe ; // 读控制寄存器 reg r_rd_en ; reg r_rd_en_2 ; // FIFO信号 wire [ 9: 0] w_rd_data_cnt ; wire [ 9: 0] w_wr_data_cnt ; wire w_full ; wire w_empty ; wire [ 19: 0] w_dout ; wire w_wr_rst_busy ; wire w_rd_rst_busy ; // 输出赋值 assign o_Iframe ro_Iframe ; assign o_Qframe ro_Qframe ; // 输入数据采样写时钟域 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) begin ri_en 1d0 ; ri_Izc 10d0 ; ri_Qzc 10d0 ; end else begin ri_en i_en ; ri_Izc i_Izc ; ri_Qzc i_Qzc ; end end // I路帧数据输出读时钟域 always (posedge i_clk or posedge i_rst) begin if(i_rst) ro_Iframe 10d0 ; else if(r_rd_en_2) ro_Iframe w_dout[19:10] ; // 提取I路高10位 else ro_Iframe 10d0 ; end // Q路帧数据输出读时钟域 always (posedge i_clk or posedge i_rst) begin if(i_rst) ro_Qframe 10d0 ; else if(r_rd_en_2) ro_Qframe w_dout[9:0] ; // 提取Q路低10位 else ro_Qframe 10d0 ; end // 读使能产生写时钟域 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) r_rd_en 1d0 ; else if(w_wr_data_cnt 288 512 - 10) r_rd_en 1d1 ; // 数据量达到阈值启动读出 else if(w_empty) r_rd_en 1d0 ; // FIFO读空停止读出 else r_rd_en r_rd_en ; end // 读使能跨时钟域同步 always (posedge i_clk or posedge i_rst) begin if(i_rst) r_rd_en_2 1d0 ; else r_rd_en_2 r_rd_en ; end // 异步FIFO例化 fifo_generator_1 fifo_generator_1_u0 ( .rst (i_rst ), // 异步复位 .wr_clk (i_clk_8dx ), // 写时钟高速 .rd_clk (i_clk ), // 读时钟低速 .din ({ri_Izc, ri_Qzc}), // 20位输入{I, Q} .wr_en (ri_en ), // 写使能 .rd_en (r_rd_en ), // 读使能 .dout (w_dout ), // 20位输出 .full (w_full ), // 满标志 .empty (w_empty ), // 空标志 .rd_data_count (w_rd_data_cnt ), // 读侧数据计数 .wr_data_count (w_wr_data_cnt ), // 写侧数据计数 .wr_rst_busy (w_wr_rst_busy ), // 写复位忙 .rd_rst_busy (w_rd_rst_busy ) // 读复位忙 ); endmodule4.2 代码结构说明代码段时钟域功能输入寄存器i_clk_8dx采样前端输入数据读使能产生i_clk_8dx根据FIFO写入量控制读操作读使能同步i_clk跨时钟域同步控制信号输出寄存器i_clk输出帧数据五、测试平台Testbench5.1 测试架构完整的测试平台包含以下模块Signal_Gen信号发生器产生PN序列和时隙使能juanji_code卷积编码器QPSK_MapQPSK调制映射pilot_insert导频插入ifft_sysn / ifft_topsIFFT变换add_cp / add_zc添加循环前缀和ZC序列FrameGen被测模块DUT5.2 测试代码timescale 1ns / 1ps module test(); // 时钟和复位 reg i_clk8dx; reg i_clk; reg i_rst; // Signal_Gen 信号 wire [1:0] o_enable; // enable: 00 null, 01 pilot, 10 data wire o_x; // 卷积编码 wire [1:0] o_encode; // QPSK映射 wire [1:0] o_I; wire [1:0] o_Q; // 导频插入 wire signed [1:0] o_Ip; wire signed [1:0] o_Qp; wire signed [1:0] o_ploitI; wire signed [1:0] o_ploitQ; wire signed [1:0] o_I_null; wire signed [1:0] o_Q_null; wire o_enframe; // IFFT控制 wire o_before; wire o_last; wire o_en; wire signed [1:0] o_Iifft; wire signed [1:0] o_Qifft; // IFFT变换 wire o_beforeIFFT; wire o_lastIFFT; wire o_enIFFT; wire signed [9:0] o_IpIFFT; wire signed [9:0] o_QpIFFT; // 添加循环前缀 wire o_encp; wire signed [9:0] o_Icp; wire signed [9:0] o_Qcp; // 添加ZC序列 wire o_enzc; wire signed [9:0] o_Izc; wire signed [9:0] o_Qzc; // 帧生成输出 wire signed [9:0] o_Iframe; wire signed [9:0] o_Qframe; // 模块例化 // 信号发生器 Signal_Gen Signal_Genu( .i_clk (i_clk), .i_rst (i_rst), .o_enable (o_enable), .o_x (o_x) ); // 卷积编码器 juanji_code juanji_codeu( .i_clk (i_clk), .i_rst (i_rst), .i_Signal (o_x), .o_encode (o_encode) ); // QPSK映射 QPSK_Map QPSK_Mapu( .i_clk (i_clk), .i_rst (i_rst), .i_en (o_enable), .i_encode (o_encode), .o_I (o_I), .o_Q (o_Q) ); // 导频插入 pilot_insert pilot_insertu( .i_clk (i_clk), .i_rst (i_rst), .i_en (o_enable), .i_I (o_I), .i_Q (o_Q), .o_Ip (o_Ip), .o_Qp (o_Qp), .o_pilotI (o_ploitI), .o_pilotQ (o_ploitQ), .o_I_null (o_I_null), .o_Q_null (o_Q_null), .o_enframe (o_enframe) ); // IFFT控制 ifft_sysn ifft_sysnu( .i_clk (i_clk), .i_clk_8dx (i_clk8dx), .i_rst (i_rst), .i_en (o_enframe), .i_Ip (o_Ip), .i_Qp (o_Qp), .o_before (o_before), .o_last (o_last), .o_en (o_en), .o_Ip (o_Iifft), .o_Qp (o_Qifft) ); // IFFT变换 ifft_tops ifft_topsu( .i_clock8dx(i_clk8dx), .i_rst (i_rst), .i_before (o_before), .i_last (o_last), .i_en (o_en), .i_Ip (o_Iifft), .i_Qp (o_Qifft), .o_before (o_beforeIFFT), .o_last (o_lastIFFT), .o_en (o_enIFFT), .o_Ip (o_IpIFFT), .o_Qp (o_QpIFFT) ); // 添加循环前缀 add_cp add_cpu( .i_clk_8dx (i_clk8dx), .i_rst (i_rst), .i_en (o_enIFFT), .i_Ip (o_IpIFFT), .i_Qp (o_QpIFFT), .o_en (o_encp), .o_Icp (o_Icp), .o_Qcp (o_Qcp) ); // 添加ZC序列 add_zc add_zcu( .i_clk_8dx (i_clk8dx), .i_rst (i_rst), .i_en (o_encp), .i_Icp (o_Icp), .i_Qcp (o_Qcp), .o_en (o_enzc), .o_Izc (o_Izc), .o_Qzc (o_Qzc) ); // 被测模块FrameGen FrameGen FrameGen_u0( .i_clk (i_clk), .i_clk_8dx (i_clk8dx), .i_rst (i_rst), .i_en (o_enzc), .i_Izc (o_Izc), .i_Qzc (o_Qzc), .o_Iframe (o_Iframe), .o_Qframe (o_Qframe) ); // 时钟和复位激励 initial begin i_clk 1b1; i_clk8dx 1b1; i_rst 1b1; #1000 i_rst 1b0; end always #40 i_clk ~i_clk; // 25MHz 读时钟 always #5 i_clk8dx ~i_clk8dx; // 200MHz 写时钟8倍频 endmodule5.3 时钟频率说明时钟周期频率用途i_clk40ns25MHz读时钟帧数据输出i_clk_8dx5ns200MHz写时钟前端数据处理六、仿真波形分析6.1 关键时序写入阶段前端模块在i_clk_8dx上升沿将I/Q数据写入FIFOwr_data_count逐渐增加启动读出当wr_data_count 790时r_rd_en拉高FIFO开始输出数据数据输出在i_clk上升沿r_rd_en_2有效时o_Iframe和o_Qframe输出有效数据停止读出当FIFO读空w_empty1时r_rd_en拉低停止读出6.2 预期波形i_clk_8dx : _|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_ i_clk : ___|‾|___|‾|___|‾|___|‾|___|‾|___ i_en : ___|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|___ wr_data_cnt: 0 → ... → 790 → 791 → ... r_rd_en : 0 → ... → 1 → 1 → 1 → ... r_rd_en_2 : 0 → ... → 0 → 1 → 1 → ... o_Iframe : 0 → ... → 0 → DATA0 → DATA1 → ...七、设计要点总结7.1 FIFO深度选择本设计使用深度为1024的FIFO由fifo_generator_1IP核生成。选择依据最大帧长度约800个采样点读写时钟频率比约8:1需要预留足够的缓冲空间应对读写速率 mismatch7.2 阈值计算读使能启动阈值为288 512 - 10 790288ZC序列长度含CP512IFFT点数-10时序余量确保FIFO不会读空7.3 注意事项复位同步FIFO的rst为异步复位需确保复位释放后FIFO处于已知状态busy信号wr_rst_busy和rd_rst_busy在复位后需等待其拉低才能正常操作数据位宽I/Q各10位合并为20位存入FIFO读出后拆分八、工程应用建议参数化设计将帧长度、IFFT点数等参数定义为常量或参数便于不同系统复用FIFO IP配置建议使用First-Word Fall-Through模式减少读出延迟仿真验证建议结合Matlab生成参考数据进行定点数比对验证资源优化如果FPGA资源紧张可考虑使用Block RAM实现FIFO九、总结本文详细介绍了OFDM基带发射机中的FrameGen帧生成模块。该模块的核心价值在于跨时钟域数据缓冲利用异步FIFO实现了从高速处理域到低速输出域的平滑过渡智能读控制基于FIFO数据量的阈值触发机制避免了读空/写满风险高可靠性通过双寄存器同步和FIFO内部格雷码指针彻底消除了亚稳态问题该模块已在FPGA平台上成功验证可直接应用于OFDM通信系统、软件无线电等项目中。完整的代码和测试平台已在文中给出读者可根据实际需求进行参数调整和功能扩展。

相关新闻