一、引言在OFDM通信系统中前导Preamble是实现接收端同步时间同步、频率同步的关键。Zadoff-ChuZC序列因其良好的自相关和互相关特性被广泛应用于LTE、5G等系统的同步序列设计。本模块add_zc负责在OFDM基带发射链路中在数据帧的最前面插入一个由ZC序列构成的前导符号包含循环前缀以辅助接收端进行帧检测和同步。本文介绍一个基于移位寄存器延迟链和ROM只读存储器的ZC插入模块该模块在FPGA中实现具有资源消耗低、时序清晰、易集成等特点。模块设计灵活前导长度可配置且I/Q分量通过MATLAB离线生成后存入ROM。二、模块功能与设计思路2.1 模块端口module add_zc( input i_clk_8dx , // 工作时钟 input i_rst , // 高电平复位 input i_en , // 输入数据有效来自CP插入模块 input signed [ 9: 0] i_Icp , // 输入实部带CP的OFDM符号 input signed [ 9: 0] i_Qcp , // 输入虚部 output o_en , // 输出数据有效 output signed[ 9: 0] o_Izc , // 输出实部前导数据 output signed[ 9: 0] o_Qzc // 输出虚部 );2.2 总体架构模块的核心任务是在OFDM帧的最前端插入一个前导符号其结构为[ZC序列 CP]长度等于N_fft N_cp本设计为 256 32 288。插入完成后后续数据来自add_cp模块的连续OFDM符号直接透传。模块由以下部分组成ZC序列存储两个ROMblk_mem_gen_i和blk_mem_gen_q分别存储ZC前导的实部和虚部深度为288宽度为10bit。数据延迟链使用三个移位寄存器链分别存储使能信号、实部、虚部延迟长度为288个时钟周期以匹配前导的持续时间。输出选择逻辑在前导输出期间选择ROM输出之后选择延迟链输出的数据实现无缝衔接。i路 ROM IP核设置Q路 ROM IP核设置2.3 设计思路详解ZC序列生成使用MATLAB生成一个ZC序列经IFFT变换为时域信号再添加循环前缀CP最后量化并缩放为10bit有符号整数生成.coe文件初始化ROM。前导插入机制当i_en有效时表示一个完整的OFDM帧开始输入模块启动r_w_start标志同时开始输出前导序列。从ROM中依次读取ZC前导的288个采样点并输出o_en拉高。同时将输入的原始数据即原本应在帧首的数据延迟288个周期待前导发送完毕后再从延迟链中输出从而实现前导替换。延迟链实现使用寄存器数组r_ram_men_dly0/1/2构建深度为291稍大于288的移位寄存器确保数据延迟足够且时序对齐。控制信号通过计数器r_addr_2控制ROM读地址并在读取完毕后停止地址保持为287。同时通过r_en_1/2/3和r_ram_men_dly0[2883]等信号产生输出使能和数据选择信号。三、关键代码解析3.1 ZC序列ROM存储blk_mem_gen_i blk_mem_gen_i_u0 ( .clka (i_clk_8dx ), .rsta (i_rst ), .addra (r_addr_2 ), .douta (w_IZC ), .rsta_busy( ) );两个ROM分别存储实部和虚部地址由r_addr_2控制输出为10bit数据。3.2 延迟链实现reg signed r_ram_men_dly0[291:1]; // 使能信号延迟 reg signed [ 9: 0] r_ram_men_dly1[291:1]; // 实部延迟 reg signed [ 9: 0] r_ram_men_dly2[291:1]; // 虚部延迟 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) begin for(i1;i291;ii1) begin r_ram_men_dly0[i] d0; r_ram_men_dly1[i] d0; r_ram_men_dly2[i] d0; end end else begin r_ram_men_dly0[1] ri_en; for(i2;i291;ii1) begin r_ram_men_dly0[i] r_ram_men_dly0[i-1]; end // 同样处理实部和虚部 end end通过移位寄存器将输入数据延迟291个周期最终从第2883级取出以确保与ROM读取时序对齐。3.3 输出选择assign w_en_2 r_ram_men_dly0[2883]; // 延迟后的使能信号 assign w_I2 r_ram_men_dly1[2883]; // 延迟后的实部 assign w_Q2 r_ram_men_dly2[2883]; // 延迟后的虚部 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) ro_Izc d0; else if(w_en_2 1b0) // 当延迟数据未到达时输出ROM前导 ro_Izc w_IZC; else ro_Izc w_I2; // 否则输出延迟的数据 end当w_en_2为0时表示仍在输出前导此时选择ROM数据当w_en_2为1时延迟数据有效切换为原始数据。3.4 输出使能控制always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) ro_en d0; else ro_en r_en_3 | w_en_2; // 前导输出或数据输出时均有效 endr_en_3是ri_en延迟3个周期的信号用于提前启动ROM读取确保数据对齐。四、MATLAB生成ZC系数在实现前需使用MATLAB生成ZC序列的I/Q分量并量化后存入ROM。脚本ZC.m执行以下步骤% 参数设置N_fft256;% IFFT长度N_cp32;% CP长度u7;% ZC根序列索引% 生成ZC序列n0:N_fft-1;zc_seqexp(-1j*pi*u*n.*(n1)/N_fft);% IFFT得到时域前导zc_ifftifft(zc_seq)*sqrt(N_fft);% 添加CPzc_cp[zc_ifft(end-N_cp1:end),zc_ifft];% 量化为10bit有符号整数缩放因子30防止溢出Izcround(30*real(zc_cp));Qzcround(30*imag(zc_cp));% 写入.coe文件% ... 见完整代码生成的.coe文件可直接用于Xilinx Block Memory Generator初始化ROM。五、完整模块代码5.1add_zc.v已给出此处整理完整module add_zc( input i_clk_8dx , input i_rst , input i_en , input signed [ 9: 0] i_Icp , input signed [ 9: 0] i_Qcp , output o_en , output signed[ 9: 0] o_Izc , output signed[ 9: 0] o_Qzc ); reg ri_en ; reg signed [ 9: 0] ri_Icp ; reg signed [ 9: 0] ri_Qcp ; reg ro_en ; reg signed [ 9: 0] ro_Izc ; reg signed [ 9: 0] ro_Qzc ; reg r_w_start ; reg r_en_1 ; reg r_en_2 ; reg r_en_3 ; reg [ 8: 0] r_addr_2 ; reg [ 8: 0] r_1_addr_2 ; reg [ 8: 0] r_2_addr_2 ; reg signed r_ram_men_dly0[291:1] ; reg signed [ 9: 0] r_ram_men_dly1[291:1] ; reg signed [ 9: 0] r_ram_men_dly2[291:1] ; wire [ 9: 0] w_IZC ; wire [ 9: 0] w_QZC ; wire w_en_2 ; wire [ 9: 0] w_I2 ; wire [ 9: 0] w_Q2 ; // 输入寄存器 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) begin ri_en d0 ; ri_Icp d0 ; ri_Qcp d0 ; end else begin ri_en i_en ; ri_Icp i_Icp ; ri_Qcp i_Qcp ; end end assign o_en ro_en ; assign o_Izc ro_Izc ; assign o_Qzc ro_Qzc ; // 开始标志 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) r_w_start d0; else if(ri_en) r_w_start d1; else r_w_start r_w_start; end // 使能延迟链 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) begin r_en_1 d0 ; r_en_2 d0 ; r_en_3 d0 ; end else begin r_en_1 ri_en ; r_en_2 r_en_1 ; r_en_3 r_en_2 ; end end // ROM读地址计数器0~287 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) r_addr_2 d0; else if(r_en_1 r_addr_2 d287) r_addr_2 d287; // 保持最后地址停止读取 else if(r_en_1) r_addr_2 r_addr_2 d1; else r_addr_2 d0; end // 地址延迟用于时序对齐 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) begin r_1_addr_2 d0 ; r_2_addr_2 d0 ; end else begin r_1_addr_2 r_addr_2 ; r_2_addr_2 r_1_addr_2 ; end end integer i; // 延迟链使能 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) begin for(i1;i291;ii1) r_ram_men_dly0[i] d0; end else begin r_ram_men_dly0[1] ri_en; for(i2;i291;ii1) r_ram_men_dly0[i] r_ram_men_dly0[i-1]; end end // 延迟链实部 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) begin for(i1;i291;ii1) r_ram_men_dly1[i] d0; end else begin r_ram_men_dly1[1] ri_Icp; for(i2;i291;ii1) r_ram_men_dly1[i] r_ram_men_dly1[i-1]; end end // 延迟链虚部 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) begin for(i1;i291;ii1) r_ram_men_dly2[i] d0; end else begin r_ram_men_dly2[1] ri_Qcp; for(i2;i291;ii1) r_ram_men_dly2[i] r_ram_men_dly2[i-1]; end end // 输出使能 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) ro_en d0; else ro_en r_en_3 | w_en_2; end // 输出实部选择 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) ro_Izc d0; else if(w_en_2 1b0) ro_Izc w_IZC; else ro_Izc w_I2; end // 输出虚部选择 always (posedge i_clk_8dx or posedge i_rst) begin if(i_rst) ro_Qzc d0; else if(w_en_2 1b0) ro_Qzc w_QZC; else ro_Qzc w_Q2; end // ROM实例实部 blk_mem_gen_i blk_mem_gen_i_u0 ( .clka (i_clk_8dx ), .rsta (i_rst ), .addra (r_addr_2 ), .douta (w_IZC ), .rsta_busy( ) ); // ROM实例虚部 blk_mem_gen_q blk_mem_gen_q_u0 ( .clka (i_clk_8dx ), .rsta (i_rst ), .addra (r_addr_2 ), .douta (w_QZC ), .rsta_busy( ) ); // 延迟数据选择信号 assign w_en_2 r_ram_men_dly0[2883]; assign w_I2 r_ram_men_dly1[2883]; assign w_Q2 r_ram_men_dly2[2883]; endmodule5.2 测试平台tb_add_zc.v测试平台已给出这里不再重复与之前tb_add_zc.v内容一致。它将整个发射链路连接包含add_zc模块并生成时钟复位。5.3 MATLAB脚本ZC.m已提供用于生成ROM初始化文件。六、仿真结果与波形分析仿真时观察以下信号输入数据i_Icp/i_Qcp为连续的OFDM符号来自CP插入。前导输出当i_en有效后o_en立即拉高o_Izc/o_Qzc开始输出ROM中存储的ZC前导持续288个时钟周期。数据透传前导发送完毕后o_Izc/o_Qzc切换为延迟后的原始数据且o_en保持高实现无缝过渡。时序上通过延迟链确保数据对齐无毛刺或断裂。七、创新点与功能点总结创新点创新点说明移位寄存器延迟替代FIFO使用寄存器链实现数据延迟避免了BRAM/FIFO资源开销适合小深度延迟。双ROM分离存储I/Q将前导的实部和虚部分别存入两个ROM简化接口便于独立控制和生成。自动切换机制利用延迟链输出的使能信号w_en_2作为选择标志实现前导与数据的无缝切换无需复杂状态机。参数可配置通过修改N_fft、N_cp和延迟深度可灵活适配不同OFDM参数如LTE、WiFi。MATLAB协同设计使用MATLAB生成ZC序列并量化形成完整的软硬件协同设计流程。功能点功能描述前导插入在每个OFDM帧的起始位置插入ZC序列含CP用于接收端同步。数据透明传输前导插入后后续数据保持原样无失真或延迟误差。输出使能指示o_en在整个前导数据期间保持高保证下级模块连续工作。低资源占用仅需两个小容量ROM和若干寄存器适合资源受限的FPGA。易于集成标准时钟与使能接口可直接级联在CP插入模块之后。八、使用注意事项ROM初始化确保在Vivado中生成blk_mem_gen_i和blk_mem_gen_q时将MATLAB生成的.coe文件加载为初始化数据且深度、位宽与设计一致。延迟深度调整若前导长度N_fftN_cp发生变化需同步修改延迟链深度291和2883中的偏移值保证数据延迟恰好等于前导长度。缩放因子MATLAB中缩放系数本例为30需根据实际DAC/射频需求调整并确保信号不溢出。地址计数ROM地址最大为N_fftN_cp-1287代码中已固化修改参数时需同步。九、总结本文设计了一个用于OFDM发射机的ZC前导插入模块通过ROM存储前导采样点、移位寄存器延迟数据并实现自动切换结构简洁、功能清晰。该模块已在实际项目中经过验证能够有效辅助接收端完成时间和频率同步。全套代码Verilog、测试平台、MATLAB生成脚本均已提供读者可在此基础上快速构建自己的OFDM基带发射链路。完整工程代码可联系作者提供欢迎交流讨论