#方案描述:
#DDS是怎么实现的?(以载波频率1MHz为例):
##DDS原理:
其中fo为输出频率,fc为时钟频率,N为相位累加器的位数,K为频率控制字。其中频率控制字作为相位累加器的输入,相位累加器的高P位输出作为ROM表的输入,输出为相位对应的幅值。从未达到频率对应幅值的效果,产生数字的正弦信号。
##Verilog代码及输出文件:
###顶层文件
/*-----------------------------------------------------
Author : madezhuang Communication University of China
Technology blogs : http://blog.csdn.net/proton_boke
Email address : [email protected]
module function :
Simulate the function of dds to generate a sine wave
Data : 2017-04-01
Version : 1.0
-----------------------------------------------------*/
module Top
(
CLK , // Global clock 50MHz
RST , // Global reset
FWEN, // inputfrequency word update enable
RD , // read data
CLKOUT // Accumulator overflow flag
);
input CLK;
input RST;
input FWEN;
output CLKOUT;
output [9:0] RD;
reg [9:0] RA;
wire [31:0] FQWD; // output frequency word
reg [32-1:0] FWIN;
wire [7 :0] ADD_R; // ROM_dress
//--------------------------------------------
//generate_freq_word
generate_freq_word u_generate_freq_word
(
FQWD,
FWEN
);
//--------------------------------------------
always @(FQWD)
begin
FWIN <= FQWD;
end
//--------------------------------------------
//phase_adder
phase_adder u_phase_adder
(
CLK ,
RST ,
FWIN ,
ADD_R ,
CLKOUT
);
//--------------------------------------------
always @(ADD_R)
begin
RA <= ADD_R;
end
//--------------------------------------------
//Output discrete sine waveforms
DDS_CORE_ROM u_DDS_CORE_ROM
(
CLK ,
RA ,
RD
);
//--------------------------------------------
endmodule
###产生频率控制字
module generate_freq_word(
FQWD , // output frequency word
FWEN ); // input frequency word update enable
parameter VAL_FREQ = 32'h051E_B851;
// 50MHz clock, 1MHz out wave freqword value
output [31:0] FQWD;
input FWEN;
reg [31:0] FQWD;
always @(FWEN)
begin
if(FWEN)
FQWD <= VAL_FREQ;
else
FQWD <= 0;
end
endmodule
###相位累加器
module phase_adder(
CLK , // clock, posedge valid
RST , // reset, high level reset
FWIN , // input frequency word
ADD_R , // output ROM_dress
CLKOUT // output clock
);
input CLK;
input RST;
input [32-1:0] FWIN;
output[8 -1:0] ADD_R;
output CLKOUT;
reg [32 -1:0] fwin_R; // freq word DFF
reg [32 -1:0] acc_R; // phase ACC DFF
reg [8 -1:0] ADD_R; // ROM_dress
reg CLKOUT;
always @ (posedge CLK or posedge RST) begin
if(RST) begin
fwin_R <= 0;
acc_R <= 0;
end
else begin
// update fwin_R DFF
fwin_R <= FWIN;
// update acc_R
acc_R <= fwin_R + acc_R;
// update addr_R, the acc_R high RA_WL is rom address
ADD_R <= acc_R[32-1:24];
end
end
always @(posedge CLK or posedge RST) begin
if(RST)
CLKOUT <= 1'b0;
else if(acc_R < 32'h7FFF_FFFF)
CLKOUT <= 1'b0;
else
CLKOUT <= 1'b1;
end
endmodule
###DDS波表ROM
module DDS_CORE_ROM(
CLK , // clock
RA , // read address
RD ); // read data
input CLK;
input [7 :0] RA;
output [9 :0] RD;
reg [9 :0] RD;
always @ (posedge CLK)
case(RA)
8 'd 0 :RD = #1 10'b 0000000000; // 0 0x0
8 'd 1 :RD = #1 10'b 0000001100; // 12 0xC
8 'd 2 :RD = #1 10'b 0000011001; // 25 0x19
8 'd 3 :RD = #1 10'b 0000100101; // 37 0x25
8 'd 4 :RD = #1 10'b 0000110010; // 50 0x32
8 'd 5 :RD = #1 10'b 0000111110; // 62 0x3E
8 'd 6 :RD = #1 10'b 0001001010; // 74 0x4A
8 'd 7 :RD = #1 10'b 0001010111; // 87 0x57
...............//省略了
8 'd 169 :RD = #1 10'b 1001010001; // -431 0x251
8 'd 170 :RD = #1 10'b 1001001010; // -438 0x24A
8 'd 171 :RD = #1 10'b 1001000100; // -444 0x244
8 'd 172 :RD = #1 10'b 1000111110; // -450 0x23E
8 'd 173 :RD = #1 10'b 1000111000; // -456 0x238
8 'd 174 :RD = #1 10'b 1000110011; // -461 0x233
8 'd 175 :RD = #1 10'b 1000101101; // -467 0x22D
...............//省略了
8 'd 249 :RD = #1 10'b 1110101001; // -87 0x3A9
8 'd 250 :RD = #1 10'b 1110110110; // -74 0x3B6
8 'd 251 :RD = #1 10'b 1111000010; // -62 0x3C2
8 'd 252 :RD = #1 10'b 1111001110; // -50 0x3CE
8 'd 253 :RD = #1 10'b 1111011011; // -37 0x3DB
8 'd 254 :RD = #1 10'b 1111100111; // -25 0x3E7
8 'd 255 :RD = #1 10'b 1111110100; // -12 0x3F4
default : RD = #1 0;
endcase
endmodule
###SignalTap
#将SignalTap中的数据导入至MATLAB分析:
在SignalTap文件sin_wave_out位置单击鼠标右键选择Create signal tapⅡ list file,生成如下图所示的数据
利用文件编辑器Utradit文件编辑器,选择需要的数据,保存为.m文件。之后就可以在matlab中对此文件进行调用。
##通用正弦信号频谱分析的MATLAB代码:
%/////////////////////////////////////////////////////////////////////
%用途:正确的正弦信号频谱分析的matlab代码
%方案,设计生成一个标准的正弦信号,对其作fft变换分析频谱,若与实际情况一致,则证明matlab代码可用
%作者:中国传媒大学 马德壮 PS:几乎未改动的来自杜伟韬杜老师
%//////////////////////////////////////////////////////////////////////
clc;
clear all;
fc = 1e3; %正弦信号频率
fs = 32e3; %采样频率
N = 64; %采样点数,则此时采样点数共包含N/(fs/fc) = 2 个周期正弦信号
idx = [0:N-1];
x1 = sin(2*pi*fc/fs*idx); %t = 1/fs*[0:N-1]
figure;
subplot(2,2,1:2); %将绘图窗口分为两行两列的,其中第一幅图占据第一行的第一、二列
stem(x1); %将以火柴柱状图画出
grid on; %添加网格线
title('Sampled Sine Signal', 'fontsize',14); %改变标题字体的大小至14
%频谱实现左右对称,横轴为频率,纵轴为幅度值
y1 = fftshift ( fft(x1) );
y1_abs = abs(y1);
subplot(2,2,3);
f = linspace(-(fs/2),(fs/2), N);
stem(f,y1_abs,'LineWidth',1,'MarkerSize',6);
xlim([-(fs/2) (fs/2)]);
grid on;
title('DFT Amplitude in Linear scale', 'fontsize',14);
xlabel('Frequency(Hz)');
ylabel('Magnitude');
y1_abs_dB = 20*log10(y1_abs);
min_y1 = min(y1_abs_dB);
subplot(2,2,4);
% f = linspace(-(fs/2),(fs/2), N);
stem(f,y1_abs_dB, 'LineWidth',1,'MarkerSize',6,'BaseValue',min_y1);
xlim([-(fs/2) (fs/2)]);
grid on;
title('DFT Amplitude in dB scale','fontsize',14);
xlabel('Frequency(Hz)');
ylabel('Magnitude(dB)');
% %频谱未搬移,即频谱不对称
% y1 = fft(x1);
% y1_abs = abs(y1);
% subplot(2,2,3);
% stem(y1_abs);
% grid on;
% title('DFT Amplitude in Linear scale', 'fontsize',14);
%
% %纵轴以分贝显示,频谱未搬移,即频谱不对称
% y1_abs_dB = 20*log10(y1_abs);
% min_y1 = min(y1_abs_dB);
% subplot(2,2,4);
% stem(y1_abs_dB, 'LineWidth',1,'MarkerSize',6,'BaseValue',min_y1);
% % stem(y1_abs_dB); 采用这条代码时会发生错误,错误原因暂不去查明
% grid on;
% title('DFT Amplitude in dB scale', 'fontsize',14);
##将上述代码稍加改动的分析来自SignalTap中的数据:
###第一次代码
%//////////////////////////////////////////////////////////////////////
%用途:对DDS产生的正弦信号进行FFT变换,频谱分析
%作者:中国传媒大学 马德壮
%//////////////////////////////////////////////////////////////////////
clc;
clear all;
sin_wave_out; %调用sin_wave_out里面的数据
signal = reshape(sin_wave_data, 1024, 1)/512;
fc = 1e6; %正弦信号频率
fs = 50e6; %采样频率
N = 1024; %采样点数,则此时采样点数共包含N/(fs/fc) = 10.24 个周期正弦信号
figure; subplot(4,4,1:4); %将绘图窗口分为两行两列的,其中第一幅图占据第一行的第一、二、三、四列
plot([0:N-1],signal);
title('Sine Signal', 'fontsize',14); %改变标题字体的大小至14
subplot(4,4,5:8);
stem(signal);
xlim([100,200]);
grid on; %添加网格线
title('Sampled Sine Signal(Partially displayed)', 'fontsize',14); %改变标题字体的大小至14
%频谱实现左右对称,横轴为频率,纵轴为幅度值
y1 = fftshift ( fft(signal) );
y1_abs = abs(y1);
subplot(4,4,9:12);
f = linspace(-(fs/2),(fs/2), N);
plot(f,y1_abs);
grid on;
title('DFT Amplitude in Linear scale', 'fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude');
y1_abs_dB = 20*log10(y1_abs);
subplot(4,4,13:16);
plot(f,y1_abs_dB,'linewidth',1);
grid on;
title('DFT Amplitude in dB scale','fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude(dB)');
###第一次波形
###第一波小分析
%///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
%结果显示:(单一频率正弦波,强度适中)噪声太大,即频谱泄露严重
%结果分析:采样点数不是整数个周期造成频谱泄露
%改进方法:不管是否乐意,我们拿过来一段有限长序列,等价于对序列在时域乘了一个矩形窗,于是等价于在频域对信号的频谱与矩形窗的频谱做卷积。而我们知道,矩形窗的频域即为抽样函数,这就导致正弦信号的尖峰在整个频域扩展开来。因此,可以选择一个合适的窗,以改进频谱泄露。
%///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
###第二次代码(加kaiser窗)
clc;
clear all;
sin_wave_out; %调用sin_wave_out里面的数据
signal = reshape(sin_wave_data, 1024, 1)/512;
kaiser_Beta = 12;
N = 1024; %采样点数,则此时采样点数共包含N/(fs/fc) = 20.48 个周期正弦信号
win = kaiser(N,kaiser_Beta); %凯撒窗
x1 = signal.*win;
fc = 1e6; %正弦信号频率
fs = 50e6; %采样频率
figure; subplot(3,3,1:3);
plot([0:N-1],x1);
grid on; %添加网格线
title('Sine Signal(Kaiser window,Beta 14)', 'fontsize',10); %改变标题字体的大小至14
%频谱实现左右对称,横轴为频率,纵轴为幅度值
y1 = fftshift ( fft(x1) );
y1_abs = abs(y1);
subplot(3,3,4:6);
f = linspace(-(fs/2),(fs/2), N);
plot(f,y1_abs);
xlim([-(fs/2) (fs/2)]);
grid on;
title('DFT Amplitude in Linear scale', 'fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude');
y1_abs_dB = 20*log10(y1_abs);
subplot(3,3,7:9);
f = linspace(-(fs/2),(fs/2), N);
plot(f,y1_abs_dB);
xlim([-(fs/2) (fs/2)]);
grid on;
title('DFT Amplitude in dB scale','fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude(dB)');
###第二次波形
%///////////////////////////////////////////////////////////////////////
%结果显示:(单一频率正弦波,强度适中)噪声仍然很大
%结果分析:采用的是kaiser窗,Beta为14,比较第一次波形虽有改善,但噪声仍未被压制到-40dB以下,刚开始并未找到原因
%查阅相关资料发现:单音信号的幅度和所有频率噪声的RMS幅度之和的比值就是信号噪声比(SNR)。对于一个NbitADC,SNR可由公式:SNR = 6.02*N + 1.76dB得到 。为了改善SNR和更为精确地再现输入信号,对于传统ADC来讲,必须增加位数。那么,简单的过采样和滤波是如何改善SNR的呢?一个1bitADC的SNR为7.78dB(6.02+1.76),每4倍过采样将使SNR增加6dB,SNR每增加6dB等效于分辨率增加1bit
%/////////////////////////////////////////////////////////////////////
###第三次波形(加kaiser窗,ROM12bit量化)
###第三波小分析
%/////////////////////////////////////////////////////////////////////
%结果显示,分析:通过加大bit量化数,噪声压制较第二次波形有一定的改善
%/////////////////////////////////////////////////////////////////////
#Modelsim测试代码:
/*-----------------------------------------------------
Author : madezhuang Communication University of China
Technology blogs : http://blog.csdn.net/proton_boke
Email address : [email protected]
module function :
Simulation of DDS design files wave
Data : 2017-04-13
Version : 1.0
-----------------------------------------------------*/
`timescale 1ns/1ns
module DDS_sin_TB;
//------------------------------
//clock generate module
reg CLK;
reg RST;
reg FWEN;
wire CLKOUT;
wire [9:0] RD;
localparam Period = 20; //50MHz
initial
begin
CLK =0;
forever #(Period/2)
CLK = ~CLK;
end
task task_RST;
begin
RST = 1;
repeat(2)@(negedge CLK);
RST = 0;
end
endtask
task task_FWEN;
begin
FWEN = 0;
repeat(2)@(negedge CLK);
FWEN = 1;
end
endtask
//--------------------------------
//Export the RD waveform data to the RD_data.txt
integer i;
integer w_file;
initial
begin
w_file = $fopen("RD.txt");
for(i=0; i<=511; i=i+1)
begin
#10;
$fdisplay(w_file,"%d",RD);
if(i == 512)
$fclose(w_file);
else
;
end
end
//--------------------------------
//system initialization
task task_sysint;
begin
end
endtask
//--------------------------------
//tsetbench of the RTL
initial
begin
task_sysint;
task_RST;
task_FWEN;
end
//--------------------------------
//the target component instantiation
dds_sin u_dds_sin
(
//global clock
.CLK (CLK) ,
.RST (RST) ,
//user face
.FWEN (FWEN) , // inputfrequency word update enable
.RD (RD) , // read data
.CLKOUT (CLKOUT) // Accumulator overflow flag
);
endmodule
##将Modelsim中的数据导入至MATLAB分析:
此处用代码的方式进行数据导出,所用代码如下
//--------------------------------
//Export the RD waveform data to the RD_data.txt
integer i;
integer w_file;
initial
begin
w_file = $fopen("RD.txt");
for(i=0; i<=511; i=i+1)
begin
#10;
$fdisplay(w_file,"%d",RD);
if(i == 512)
$fclose(w_file);
else
;
end
end
利用文件编辑器Utradit文件编辑器,选择需要的数据,保存为.m文件。之后就可以在matlab中对此文件进行调用。
##利用matlab分析来自Modelsim中的数据:
此处仅仅给出,加窗前后,信号的时域与频域的波形
##:
#使用MATLAB对整个过程进行数学仿真:
%//////////////////////////////////////////////////////
%用途:使用单音正弦信号对载波信号进行双边带幅度调制
%作者:中国传媒大学 马德壮
%//////////////////////////////////////////////////////
%产生频率为1kHz的单音正弦信号(sin),以及1MHz的载波(cos)
clc; clear all;
f1 = 1e3; %频率
A = 1; %幅值
fs = 50e6;
t1 = 0: 1/fs: 49999/fs;
y1 = A * sin(2*pi*f1*t1);
figure(1);
subplot(3,3,1:3); plot(t1,y1);
title('单音正弦信号(1kHz)'); ylabel('幅度'); xlabel('时间');
f2 = 1e6; %频率
A = 1; %幅值
t2 = 0: 1/fs: 49999/fs;
y2 = A * cos(2*pi*f2*t2);
subplot(3,3,4:6); plot(t2,y2);
title('载波信号(1MHz)'); ylabel('幅度'); xlabel('时间');
xlim([0,1/(1e5)]);
%合成双边带DSB信号
DSB_t = y1 .* y2 ; %时域
subplot(3,3,7:9); plot(t2, DSB_t);
title('双边带调制DSB'); ylabel('幅度'); xlabel('时间');
DSB_t_1_1 = y1 .* y2 ; %时域分段显示1_1
figure(2);
subplot(2,5,1:5); plot(t2, DSB_t_1_1);
xlim([0, 0.5/(1e3)] );
DSB_t_1_2 = y1 .* y2 ; %时域分段显示1_2
subplot(2,5,6:10); plot(t2, DSB_t_1_2);
xlim([0.5/(1e3), 1/(1e3)] );
%//////////////////////////////////////////////////////
%用途:对双边带调制信号进行频谱分析
%作者:中国传媒大学 马德壮
%//////////////////////////////////////////////////////
%频谱实现左右对称,横轴为频率,纵轴为幅度值
N = length(DSB_t);
Y = fftshift ( fft(DSB_t) );
Y_abs = abs(Y);
figure(4);
subplot(2,4,1:4);
f = linspace(-(fs/2),(fs/2), N);
plot(f,Y_abs);
grid on;
title('DFT Amplitude in Linear scale', 'fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude');
Y_abs_dB = 20*log10(Y_abs);
subplot(2,4,5:8);
plot(f,Y_abs_dB,'linewidth',1);
grid on;
title('DFT Amplitude in dB scale','fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude(dB)');
####频域显示:
由频谱图中的数据可以看出,调制信号(正弦信号)的频谱被左右搬移了fc即1MHz
##Quartus_Project的顶层文件:
/*------------------------------------------------------------
Author : madezhuang Communication University of China
Technology blogs : http://blog.csdn.net/proton_boke
Email address : [email protected]
module function :
Produce double sideband amplitude modulation signal
Data : 2017-04-19
Version : 1.0
------------------------------------------------------------*/
`timescale 1ns/1ns
module Top
(
CLK , // Global clock 50MHz
RST , // Global reset
FWEN, // inputfrequency word update enable
RD , // read data
RD_1, // read data
RD_2, // read data
CLKOUT_1, // Accumulator overflow flag
CLKOUT_2 // Accumulator overflow flag
);
input CLK;
input RST;
input FWEN;
output CLKOUT_1;
output CLKOUT_2;
output [20:0] RD;
output [9 :0] RD_1;
output [9 :0] RD_2;
integer i;
reg signed [20:0] RD;
wire signed [9 :0] RD_1;
wire signed [9 :0] RD_2;
//--------------------------------------------
//generate a sine wave(1MHz)
Top_1 u_Top_1
(
CLK , // Global clock 50MHz
RST , // Global reset
FWEN, // inputfrequency word update enable
RD_1, // read data
CLKOUT_1 // Accumulator overflow flag
);
//--------------------------------------------
//generate a sine wave(1MHz)
Top_2 u_Top_2
(
CLK , // Global clock 50MHz
RST , // Global reset
FWEN, // inputfrequency word update enable
RD_2, // read data
CLKOUT_2 // Accumulator overflow flag
);
//--------------------------------------------
//Produce double sideband amplitude modulation signal
always @(RD_1 or RD_2)
begin
RD = RD_1 *RD_2;
end
endmodule
#使用Modelsim进行前仿真:
`timescale 1ns/1ns
module AM_DSB_test;
//------------------------------
//clock generate module
reg CLK;
reg RST;
reg FWEN;
wire CLKOUT_1;
wire CLKOUT_2;
wire [20:0] RD;
wire [9 :0] RD_1;
wire [9 :0] RD_2;
localparam Period = 20; //50MHz
initial
begin
CLK =0;
forever #(Period/2)
CLK = ~CLK;
end
task task_RST;
begin
RST = 1;
repeat(2)@(negedge CLK);
RST = 0;
end
endtask
task task_FWEN;
begin
FWEN = 0;
repeat(2)@(negedge CLK);
FWEN = 1;
end
endtask
//--------------------------------
//Export the RD waveform data to the RD_data.txt
integer i;
integer w_file;
initial
begin
w_file = $fopen("RD.txt");
for(i=0; i<=511; i=i+1)
begin
#10;
$fdisplay(w_file,"%d",RD);
if(i == 512)
$fclose(w_file);
else
;
end
end
//--------------------------------
//system initialization
task task_sysint;
begin
end
endtask
//--------------------------------
//tsetbench of the RTL
initial
begin
task_sysint;
task_RST;
task_FWEN;
end
//--------------------------------
//the target component instantiation
Top u_Top
(
//global clock
.CLK (CLK) ,
.RST (RST) ,
//user face
.FWEN (FWEN) , // inputfrequency word update enable
.RD (RD) , // read data
.RD_1 (RD_1) , // read data
.RD_2 (RD_2) , // read data
.CLKOUT_1 (CLKOUT_1) , // Accumulator overflow flag
.CLKOUT_2 (CLKOUT_2) // Accumulator overflow flag
);
endmodule
##使用Modelsim仿真,观察波形,使用Matlab观察频谱:
此处对于数据导出至文本文件,采用方法是分别对RD_1,RD_2数据导出,转化为实际数值后,进行相乘归一操作
%部分代码如下:
clc;
clear all;
RD_data1; %调用RD_data1里面的数据
signal_1 = reshape(RD1_data, 65536, 1);
for i=1:1:65536
if signal_1(i)> 511
signal_1(i) = signal_1(i) -1024;
else
signal_1(i) = signal_1(i);
end
end
signal_1 = signal_1 / 512;
RD_data2; %调用RD_data2里面的数据
signal_2 = reshape(RD2_data, 65536, 1);
for j=1:1:65536
if signal_2(j)> 511
signal_2(j) = signal_2(j) -1024;
else
signal_2(j) = signal_2(j);
end
end
signal_2 = signal_2 / 512;
signal = signal_1.* signal_2;
#Quartus_SignalTap波形显示:
由于存储器的容量限制,此处仅给出二进制补码数显示,读者可以根据图中显示数据进行验证
##使用Signaltap抓取电路输出波形,使用Matlab观察频谱:
此处仅仅给出,加窗前后,信号的时域与频域的波形