Xilinx-Verilog-學習筆記(19):正弦波訊號發生器與DDS

2020-11-13 13:00:56

Xilinx-Verilog-學習筆記(19):正弦波訊號發生器與DDS

一、正弦波訊號發生器

1、浮點數的定點化

這裡以2.918為例,實現浮點數向定點數的轉換:

(1)在進行浮點轉定點之前,要先確定整數部分位寬小數部分位寬3位整數位寬,12位元的小數位寬,最高位的符號位1位。
(2)15位寬的數能夠表示的數值範圍為-32768到32767。整數部分3位寬的數最大能表示到8,因此最大精度為8/32767=0.000244140625。
(3)2.918進行定點化的過程為2.918/(8/32768)=11952.128~=11952這個值就是定點後的2.918的值。而最後的0.128就是定點化會帶來量化誤差。

2、用matlab生成正弦波並進行量化處理

量化處理
量化在數位訊號處理領域,是指將訊號的連續取值(或者大量可能的離散取值)近似為有限多個(或較少的)離散值的過程。

正弦波量化思想
本實驗通過256個位寬為8的資料對正弦訊號進行取樣,以對一個週期的正弦訊號進行量化處理。每個資料位寬為8,相當於把正弦訊號的幅度從-1到1進行256份等間隔劃分;取樣點數為256個,相當於對一個正弦週期等間隔地取樣了256個點。

量化後的資料由256個介於-1到1之間的數構成,之後將浮點型的數值進行定點化的轉換,使其為0~256之間的整數。(具體處理方法見程式碼)

MatLab程式碼:

clc;
clear all;
N=2^8;
s_p=0:255;%正弦波一個週期的取樣點數
sin_data=sin(2*pi*s_p/N);%256個介於-1到1之間的數

%列印我們的波形
% plot(sin_data,'r*');
% hold on;
% plot(sin_data);

%定點化
fix_p_sin_data=fix(sin_data*127);%256個介於-128到127之間的數
for i=1:N
    if fix_p_sin_data(i)<0
        fix_p_sin_data(i)=N+fix_p_sin_data(i);%將負數搬到128到256之間(用255加即可)
    else
        fix_p_sin_data(i)=fix_p_sin_data(i);%整數不用管
    end
end

%以下是生成mif檔案,用於RAM的初始化
fid=fopen('sp_ram_256x8.mif','w+');
fprintf(fid,'WIDTH=8;\n');
fprintf(fid,'DEPTH=256;\n');
fprintf(fid,'ADDRESS_RADIX=UNS;\n');
fprintf(fid,'DATA_RADIX=UNS;\n');
fprintf(fid,'CONTENT BEGIN \n');
for i=1:N
	fprintf(fid,'%d:%d; \n',i-1,fix_p_sin_data(i));
end
fprintf(fid,'END; \n');
fclose(fid);

通過sin_data=sin(2 * pi * s_p/N); 即可實現對一個正弦週期的量化。
在這裡插入圖片描述

3、design檔案

module	ex_dds(
	input	wire		sclk,
	input	wire		rst_n,
	output	wire	[7:0]	o_wave
);

reg	[7:0]	addr;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		addr <= 'd0;
	else 
		addr <= addr + 1'b1;

sp_ram_256x8	sp_ram_256x8_inst (
	.address ( addr ),
	.clock ( sclk ),
	.data ( 8'd0 ),
	.wren ( 1'b0 ),
	.q ( o_wave )
	);
endmodule 

其中例化的深度256,位寬為8的RAM是直接呼叫IP核產生的。

4、testbench檔案

`timescale 1ns/1ns
module tb_ex_dds;
reg	sclk,rst_n;
wire	[7:0]	o_wave;
initial begin
	sclk =0;
	rst_n=0;
	#100 
	rst_n =1;
end

always #10 sclk =~sclk;//50Mhz

ex_dds ex_dds_inst(
	.sclk		(sclk),
	.rst_n		(rst_n),
	.o_wave		(o_wave)
);

endmodule

5、模擬波形

在這裡插入圖片描述

將RAM輸出的數值用模擬波形表示出來,可以看出是正弦訊號。對於地址是鋸齒波,因為是從0到255然後再到0。

二、DDS直接頻率合成器

1、基本原理

在這裡插入圖片描述

  • fc為每秒鐘累加多少次,2^n為累加器能夠累加到的最大範圍,所以fc/2 ^n為每秒鐘累加器會溢位多少次,即為週期(解析度)。提高相位累加器的位寬n可以調高頻率解析度。
  • 儲存正弦的RAM深度或者叫正弦波一個週期量化的點數,量化的點數越多生成的正弦波相位噪聲低。
  • M是控制字,通過頻率控制字來產生不同的頻率。

這裡相位累加暫存器的位寬為32,目標實現1Mhz的訊號:

(1)控制字設定:1e6=M * 50e6/2^32,則M=1e6 * 2^32/50e6~=85899345.
(2)相位累加器為32位元,而最終輸出為8位元,為了取整數倍,因此取相位累加器的高8位元。

在這裡插入圖片描述
可以得到1Mhz的正弦週期。因此,通過修改M的值可以調節正弦訊號的頻率。

於是我們可以設計過一段時間將M的值增大一定值,即可實現頻率的連續變換:

2、design檔案

module	ex_dds(
	input	wire		sclk,
	input	wire		rst_n,
	output	wire	[7:0]	o_wave
);

parameter	FRQ_W=32'd85899346;		//相當於M
parameter	FRQ_ADD=32'd85899346/2;	//相當於遞增量
reg		[31:0]	phase_sum;
wire	[7:0]	addr;
reg		[31:0]	frq_word;
reg		[6:0]	div_cnt;
reg				div_flag;

//記100個時鐘週期,M的值遞增一次
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		div_cnt <= 1'b0;
	else if(div_cnt == 7'd99)
		div_cnt <= 1'b0;
	else 
		div_cnt <= div_cnt + 1'b1;

always @(posedge sclk or negedge rst_n)		
	if(rst_n == 1'b0)
		div_flag <= 1'b0;
	else if(div_cnt == 7'd99)
		div_flag <= 1'b1;
	else
		div_flag <= 1'b0;
		
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		frq_word <= FRQ_W;
	else if(div_flag == 1'b1)
		frq_word <= frq_word + FRQ_ADD;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		phase_sum <= 'd0;
	else 
		phase_sum <= phase_sum + frq_word;
	
assign addr = phase_sum[31:24];

/* always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		addr <= 'd0;
	else 
		addr <= addr + 1'b1; */

sp_ram_256x8	sp_ram_256x8_inst (
	.address ( addr ),
	.clock ( sclk ),
	.data ( 8'd0 ),
	.wren ( 1'b0 ),
	.q ( o_wave )
	);
endmodule 

此時通過計數器,實現100個時鐘週期變頻一次。
如果用於高速電路的話,不要直接用分頻器作為標誌,可以通過定義一個div_flag作為標誌。

3、模擬波形

在這裡插入圖片描述
從波形可以看出,隨著M的逐漸增大,時脈頻率也越來越高。

三、混頻器

混頻器的主要任務是將兩個不同頻率的訊號進行疊加。

1、MatLab程式碼

(1)首先定義好取樣率,兩個時鐘的頻率,從圖中可以看出,紅色的時脈頻率是藍色的5倍。
在這裡插入圖片描述
(2)然後是混頻,混頻後的到的訊號包含兩個頻率分量,一個是f1+f2,一個是f1-f2,即一個6Mhz,一個4Mhz。從時域訊號中我們難以看出結果是否正確,因此需要對其進行FFT變換,從頻域分析。
在這裡插入圖片描述
前1024個點表示正頻域,後1024個點表示複頻域,因此只需要看前1024個即可。

頻率解析度與FFT計算的點數N、取樣頻率fs都有關。頻率解析度=fs/N
50M/2048=0.0244Mhz,這是FFT中能夠分辨出的最低的頻率,用0.0244*1024=25Mhz,正好是50Mhz取樣頻率能夠識別出來的頻率。

兩個峰的點分別是165和247,0.0244 *165=4Mhz,0.0244 * 247=6Mhz,正好是混頻後的兩個頻率分量。

clc;
clear all;
fs=50e6;   %50Mhz取樣率
f1=1e6;    %1Mhz頻率
f2=5e6;    %5Mhz頻率
n=0:2047;
s_1=sin(2*pi*n*f1/fs);
s_2=sin(2*pi*n*f2/fs);

%s_1和s_2混頻,就是相乘
s_12=s_1.*s_2; %混頻後包含兩個頻率分量,一個是f1+f2一個是f1-f2
%頻域分析
fft_out=fft(s_12,2048);
fft_abs=abs(fft_out);

2、design檔案

本實驗設計1Khz的正弦訊號和10Khz的正弦訊號進行混頻。
通過計算:
(1)1Khz:M=1000 * 2^32/50e6=85899
(2)10Khz:M=10000 * 2^32/50e6=858993

module	ex_dds(
	input	wire		sclk,//50Mhz
	input	wire		rst_n,
	output	wire	[15:0]	o_wave
);
parameter	FRQ_W_1k=32'd85899;
parameter	FRQ_W_10k=32'd858993;

reg	[31:0]	phase_sum_1k,phase_sum_10k;
wire	[7:0]	addr_1k,addr_10k;
wire	[7:0]	o_wave_1k;
wire	[7:0]	o_wave_10k;
//
reg	[22:0]	sum;
reg	[14:0]	sum_cnt;
reg		sum_flag;
reg	[22:0]	sum_r;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		sum_cnt <= 'd0;
	else if(sum_cnt == 'd29999)
		sum_cnt <='d0;
	else 
		sum_cnt <= sum_cnt + 1'b1;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		sum_flag<= 1'b0;
	else if(sum_cnt == 'd29999)
		sum_flag <= 1'b1;
	else 
		sum_flag <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		sum <= 'd0;
	else if(sum_flag  == 1'b1)
		sum <= {{15{o_wave_1k[7]}},o_wave_1k};
	else
		sum <= sum + {{15{o_wave_1k[7]}},o_wave_1k};
		

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		sum_r <= 'd0;
	else if(sum_flag == 1'b1)
		sum_r <= sum;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		phase_sum_1k<='d0;
	else 
		phase_sum_1k <= phase_sum_1k + FRQ_W_1k ;
		
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		phase_sum_10k<='d0;
	else 
		phase_sum_10k <= phase_sum_10k + FRQ_W_10k ;

assign	addr_1k=phase_sum_1k[31:24];

assign	addr_10k=phase_sum_10k[31:24];

//混頻器
mult_8x8_l0	mult_8x8_l0_inst (
	.dataa ( o_wave_1k ),
	.datab ( o_wave_10k ),
	.result ( o_wave )
	);


sp_ram_256x8	sp_ram_256x8_inst (
	.address ( addr_1k ),
	.clock ( sclk ),
	.data ( 8'd0 ),
	.wren ( 1'b0 ),
	.q ( o_wave_1k )
	);

sp_ram_256x8	sp_ram_256x8_inst_1 (
	.address ( addr_10k ),
	.clock ( sclk ),
	.data ( 8'd0 ),
	.wren ( 1'b0 ),
	.q ( o_wave_10k )
	);
endmodule 

該模組中通過例化兩個RAM來生成兩個正弦波形,通過例化一個乘法器來實現兩個正弦訊號的混頻。

3、testbench檔案

`timescale 1ns/1ns
module tb_ex_dds;
reg	sclk,rst_n;
wire	[15:0]	o_wave;
initial begin
	sclk =0;
	rst_n=0;
	#100 
	rst_n =1;
end

always #10 sclk =~sclk;//50Mhz

ex_dds ex_dds_inst(
	.sclk		(sclk),
	.rst_n		(rst_n),
	.o_wave		(o_wave)
);

endmodule

4、模擬波形

在這裡插入圖片描述
從波形中可以看出,下面兩個不同頻率的正弦訊號進行混頻後,生成了含兩個頻率分量的訊號o_wave。