频谱搬移的过程如下,输入信号Data_in,与NCO产生的单音信号进行复乘得到Data_out;
将Data_in与Data_out的频谱放到一起如下,可以看到频谱搬移的过程。
频谱搬移在计算上就是复乘:(i+q*j)*(cos+sin*j)=(cos*i-sin*q)+(sin*i+cos*q)*j,用xilinx片子实现的话我们还是选择DSP48,其结构如下:
其中用到了dsp的级联,可以参考之前dsp48e1详细讲解的文章:FPGA的底层资源之DSP48E1和Xilinx DSP48E1仿真。这里不再详细描述。
代码实现如下:
首先的端口声明:
// ============================================================
// File Name: cm_mix
// VERSION : V1.0
// DATA : 2022/11/6
// Author : FPGA干货分享
// ============================================================
// 功能:数字混频器 (i+q*j)*(cos+sin*j) = (cos*i - sin*q) + (sin*i + cos*q)*j
// delay
// ============================================================
`timescale 1ns/1ps
module cm_mix
parameter C_DATA_WITH = 16 ) //
(
input wire I_sys_clk , // 输入时钟
input wire I_rst_in , // 输入复位 高有效
input wire [6:0] I_phase , // 初始相位
input wire [7:0] I_freq , // 频率,步进,1代表1M
input wire [C_DATA_WITH-1:0] I_data_in_i , // 输入数据 实部I
input wire [C_DATA_WITH-1:0] I_data_in_q , // 输入除数 虚部Q
output reg [C_DATA_WITH-1:0] O_data_out_i , // 输出数据 实部I
output reg [C_DATA_WITH-1:0] O_data_out_q );// 输出除数 虚部Q
// ============================================================
// 内部参数
// ============================================================
// ============================================================
// 变量
// ============================================================
wire [10:0] S_sin_out ;
wire [10:0] S_cos_out ;
reg [C_DATA_WITH-1:0] S_data_in_q ;
reg [10:0] S_sin_out_d ;
reg [10:0] S_cos_out_d ;
wire [47:0] S_pcout_cos_i ;
wire [47:0] S_dsp_out_i ;
wire [47:0] S_pcout_sin_i ;
wire [47:0] S_dsp_out_q ;
然后调用上一篇文章中的NCO模块FPGA数字信号处理之verilog实现NCO(代码及仿真):
cm_nco_100 cm_nco_100 (
.I_sys_clk (I_sys_clk ) , /// 工作时钟 100M
.I_rst_n (!I_rst_in ) , /// 复位信号,用来清相位
.I_phase (I_phase ) , /// 初始相位
.I_freq (I_freq ) , /// 频率,步进,1代表1M
.O_sin_out (S_sin_out ) , /// 输出正弦值
.O_cos_out (S_cos_out ) /// 输出余弦值
);
接着打拍并调用乘法器:
always @(posedge I_sys_clk )
if(I_rst_in)
begin
S_data_in_q <= 'd0 ;
S_sin_out_d <= 'd0 ;
S_cos_out_d <= 'd0 ;
end
else
begin
S_data_in_q <= I_data_in_q ;
S_sin_out_d <= S_sin_out ;
S_cos_out_d <= S_cos_out ;
end
// ============================================================
// (cos*i - sin*q)
// ============================================================
//cos*i
cm_dsp48e1 #(
.C_DATA_WITH_A (C_DATA_WITH ),
.C_DATA_WITH_B (11 ),
.C_DATA_WITH_C (48 ),
.C_DATA_WITH_D (25 )
)
U0_cm_dsp48e1(
.I_CLK (I_sys_clk ) , // clk
.I_RST (I_rst_in ) , // RST
.I_A (I_data_in_i ) , // [29:0]
.I_B (S_cos_out ) , // [17:0]
.I_C (48'd0 ) , // [47:0]
.I_D (25'd0 ) , // [24:0]
.I_PCIN (48'd0 ) , // [47:0] 只能直连PCOUT
.I_ALUMODE (4'd0 ) , // [3:0]
.I_INMODE (5'b00101 ) , // [4:0]
.I_OPMODE (7'b0000101 ) , // [6:0]
.O_P ( ) , // [47:0]
.O_PCOUT (S_pcout_cos_i ) // [47:0] 只能直连PCIN
);
//Pcin - sin*q
cm_dsp48e1 #(
.C_DATA_WITH_A (C_DATA_WITH ),
.C_DATA_WITH_B (11 ),
.C_DATA_WITH_C (48 ),
.C_DATA_WITH_D (25 )
)
U1_cm_dsp48e1(
.I_CLK (I_sys_clk ) , // clk
.I_RST (I_rst_in ) , // RST
.I_A (S_data_in_q ) , // [29:0]
.I_B (S_sin_out_d ) , // [17:0]
.I_C (48'd0 ) , // [47:0]
.I_D (25'd0 ) , // [24:0]
.I_PCIN (S_pcout_cos_i ) , // [47:0] 只能直连PCOUT
.I_ALUMODE (4'b0011 ) , // [3:0]
.I_INMODE (5'b00101 ) , // [4:0]
.I_OPMODE (7'b0010101 ) , // [6:0]
.O_P (S_dsp_out_i ) , // [47:0]
.O_PCOUT ( ) // [47:0] 只能直连PCIN
);
// ============================================================
// (sin*i + cos*q)
// ============================================================
//sin*i
cm_dsp48e1 #(
.C_DATA_WITH_A (C_DATA_WITH ),
.C_DATA_WITH_B (11 ),
.C_DATA_WITH_C (48 ),
.C_DATA_WITH_D (25 )
)
U2_cm_dsp48e1(
.I_CLK (I_sys_clk ) , // clk
.I_RST (I_rst_in ) , // RST
.I_A (I_data_in_i ) , // [29:0]
.I_B (S_sin_out ) , // [17:0]
.I_C (48'd0 ) , // [47:0]
.I_D (25'd0 ) , // [24:0]
.I_PCIN (48'd0 ) , // [47:0] 只能直连PCOUT
.I_ALUMODE (4'd0 ) , // [3:0]
.I_INMODE (5'b00101 ) , // [4:0]
.I_OPMODE (7'b0000101 ) , // [6:0]
.O_P ( ) , // [47:0]
.O_PCOUT (S_pcout_sin_i ) // [47:0] 只能直连PCIN
);
//Pcin + cos*q
cm_dsp48e1 #(
.C_DATA_WITH_A (C_DATA_WITH ),
.C_DATA_WITH_B (11 ),
.C_DATA_WITH_C (48 ),
.C_DATA_WITH_D (25 )
)
U3_cm_dsp48e1(
.I_CLK (I_sys_clk ) , // clk
.I_RST (I_rst_in ) , // RST
.I_A (S_data_in_q ) , // [29:0]
.I_B (S_cos_out_d ) , // [17:0]
.I_C (48'd0 ) , // [47:0]
.I_D (25'd0 ) , // [24:0]
.I_PCIN (S_pcout_sin_i ) , // [47:0] 只能直连PCOUT
.I_ALUMODE (4'b0000 ) , // [3:0]
.I_INMODE (5'b00101 ) , // [4:0]
.I_OPMODE (7'b0010101 ) , // [6:0]
.O_P (S_dsp_out_q ) , // [47:0]
.O_PCOUT ( ) // [47:0] 只能直连PCIN
);
最后四舍五入后输出:
/// 四合五入输出
always @(posedge I_sys_clk )
if(I_rst_in)
begin
O_data_out_i <= 'd0;
O_data_out_q <= 'd0;
end
else
begin
O_data_out_i <= S_dsp_out_i[10+:C_DATA_WITH] + S_dsp_out_i[9];
O_data_out_q <= S_dsp_out_q[10+:C_DATA_WITH] + S_dsp_out_q[9];
end
endmodule
对代码的详细讲解参考B站视频:
【FPGA数字信号处理之verilog实现数字混频器】 https://www.bilibili.com/video/BV1hg411B7Rb/?share_source=copy_web&vd_source=9736f43bc2eebc284f4fbbe5805247a7
审核编辑 :李倩
全部0条评论
快来发表一下你的评论吧 !