FPGA数字通信入门手把手教你用Verilog和Quartus搭建正交调制解调仿真环境在数字通信领域正交调制解调技术是实现高效频谱利用的核心方法之一。对于通信工程和电子信息专业的学生而言掌握这项技术的FPGA实现不仅能够加深对理论的理解更能为未来的工程实践打下坚实基础。本文将带领读者从零开始一步步完成一个完整的正交调制解调仿真项目。1. 环境准备与工程创建1.1 软件安装与配置开始前需要确保已安装以下工具Quartus Prime 18.0ModelSim-Intel FPGA Starter Edition 10.5b提示建议将Quartus和ModelSim安装在默认路径避免后续仿真时出现文件路径问题。安装完成后需要进行必要的环境配置在Quartus中设置ModelSim为默认仿真工具配置库映射文件确保IP核能被正确识别检查许可证文件是否包含所有必要组件的授权1.2 创建新工程在Quartus中创建新工程的步骤如下# 创建工程目录结构 mkdir -p qpsk_prj/{src,sim,ip,constraints}启动Quartus选择File→New Project Wizard指定工程名称和存储路径建议使用英文路径选择目标器件型号本文使用Cyclone IV E系列EP4CE6E22C8N添加已有的设计文件初始阶段可跳过完成工程创建2. 正交调制解调原理与架构设计2.1 正交调制原理正交调制的基本数学表达式为s(t) I(t)cos(2πf₀t) - Q(t)sin(2πf₀t)其中I(t)和Q(t)分别代表信号的同相和正交分量。2.2 系统架构设计整个系统包含以下关键模块模块名称功能描述实现方式载波生成模块产生正交的sin/cos载波信号Verilog状态机实现调制模块完成基带信号与载波的乘法运算Verilog组合逻辑解调模块实现相干解调Verilog时序逻辑FIR滤波器模块完成低通滤波Quartus IP核实现时钟管理模块提供系统所需时钟PLL IP核实现3. Verilog代码实现3.1 载波生成模块载波生成是系统的核心模块之一采用状态机实现module carrier( input clk, input clk_400K, input rst_n, output reg signed [1:0] carrier_cos, output reg signed [1:0] carrier_sin ); reg [2:0] cnt_4_cos, cnt_4_sin; always (posedge clk_400K) begin if (!rst_n) begin cnt_4_sin 3d0; cnt_4_cos 3d0; end else begin cnt_4_cos cnt_4_cos 1b1; cnt_4_sin cnt_4_sin 1b1; if(cnt_4_cos 3) cnt_4_cos 3d0; if(cnt_4_sin 3) cnt_4_sin 3d0; end end always (posedge clk) begin if(!rst_n) begin carrier_cos 2d0; carrier_sin 2d0; end else begin case(cnt_4_cos) 3d0: carrier_cos 2b1; 3d1: carrier_cos 2b0; 3d2: carrier_cos -2b1; 3d3: carrier_cos 2b0; endcase case(cnt_4_sin) 3d0: carrier_sin 2b0; 3d1: carrier_sin 2b1; 3d2: carrier_sin 2b0; 3d3: carrier_sin -2b1; endcase end end endmodule3.2 调制模块实现调制模块负责将基带信号与载波相乘module mixed( input clk, input clk_400K, input rst_n, input signed [15:0] data, input data_valid, input signed [1:0] carrier_cos, input signed [1:0] carrier_sin, output reg signed [15:0] signal_1, output reg signed [15:0] signal_2, output reg signed [15:0] signal_output ); wire signed [15:0] signal; assign signal (data - 14d8192); // 数据预处理 always (posedge clk) begin if (!rst_n) begin signal_1 16d0; signal_2 16d0; signal_output 16d0; end else begin signal_1 signal * carrier_cos; signal_2 signal * carrier_sin; signal_output signal * carrier_cos signal * carrier_sin; end end endmodule4. FIR滤波器IP核配置4.1 使用MATLAB设计滤波器系数首先在MATLAB中设计滤波器% FIR低通滤波器设计 Fs 400e3; % 采样频率 Fpass 20e3; % 通带频率 Fstop 25e3; % 阻带频率 Wpass 1; % 通带权重 Wstop 1; % 阻带权重 filter_order 64; % 滤波器阶数 % 使用firpm函数设计滤波器 b firpm(filter_order, [0 Fpass Fstop Fs/2]/(Fs/2), [1 1 0 0], [Wpass Wstop]);将生成的系数导出为.coe文件供Quartus使用。4.2 在Quartus中配置FIR IP核打开IP Catalog搜索FIR选择FIR II CompilerIP核配置参数滤波器类型低通系数来源从文件导入数据位宽16位输出位宽19位生成IP核并添加到工程5. 仿真与验证5.1 Testbench编写完整的测试平台需要包含以下功能timescale 1 ps/ 1 ps module FIR_top_vlg_tst(); // 时钟生成 localparam TCLK_HALF 5_000; initial begin sys_clk 1b0; forever #TCLK_HALF sys_clk ~sys_clk; end // 复位控制 initial begin sys_rst_n 1b0; #30 sys_rst_n 1b1; #1_000_060_000 $finish; end // 数据读取 reg [15:0] stimulus [0:38399]; integer i; initial begin $readmemh(data_cw_38400.txt, stimulus); i 1; data stimulus[0]; forever begin (negedge sys_clk); if(data_valid i38400) begin data stimulus[i]; i i 1; end end end // 结果保存 integer fir1_file, fir2_file; initial fir1_file $fopen(data_out_1.txt); initial fir2_file $fopen(data_out_2.txt); always (posedge sys_clk) begin if (ast_source_valid_1) begin $fwrite(fir1_file,%f\n,$signed(ast_source_data_1)); $fwrite(fir2_file,%f\n,$signed(ast_source_data_2)); end end endmodule5.2 仿真结果分析通过ModelSim仿真后可以将结果与MATLAB理论计算结果进行对比% 数据对比分析 fid_F1 fopen(data_out_1.txt,r); [f_F1, count_F1] fscanf(fid_F1, %f, [38400 1]); fclose(fid_F1); fid_M1 fopen(out1_38400.txt,r); [f_M1, count_M1] fscanf(fid_M1, %f, [38400 1]); fclose(fid_M1); % 归一化处理 d_max 2^20; d_min 2; data_F1 mapminmax(f_F1, d_min, d_max); data_M1 mapminmax(f_M1, d_min, d_max); % 计算误差 error_rate mean(abs(data_F1 - data_M1)./abs(data_M1)); disp([平均相对误差, num2str(error_rate*100), %]);6. 常见问题与调试技巧在实际开发过程中可能会遇到以下典型问题数据位宽不匹配现象仿真结果出现异常幅值解决方法检查各模块接口的位宽定义是否一致时钟域同步问题现象随机出现数据错误解决方法添加跨时钟域同步寄存器滤波器IP核配置错误现象滤波效果不符合预期解决方法重新检查系数文件和IP核参数设置文件路径问题现象仿真时无法读取数据文件解决方法使用绝对路径或确保文件位于工程目录下注意在调试过程中建议逐步验证每个模块的功能先确保载波生成正确再测试调制模块最后集成整个系统。