matlab与quartus的联合数据交换(NCO与文件数据的混频处理)

背景

由于modelsm只能观察时域波形,无法显示数据的频谱特性,并且对数据进行分析、处理不够方便,特别是在FPGA中设计数字滤波器时,无法直接观察滤波器的频域响应。另外书写激励文件的时候,很难产生用户所需要的具有任意信噪比的输入信号。特别声明,不要转载本博主的所有blog,侵权必究,否则博主将关闭自己的所有blog。

再次认识关于DDS的来源

实际案例

官方资料阅读(NCO IP core)

参数原理

通常你可以使用IP工具接口来实施NCO的架构,包括基于ROM,CORDIC算法、和乘法器。另外IP工具会在你设置参数的时候提供可视化的时域和频域图像。设计者通常可以用NCO在通信领域,产生正交的IQ生成器。
在这里插入图片描述
在这里插入图片描述
输出的两个信号,用补码的形式给出。
正如上图所示,上图是官方给出的NCO的架构,其实可以看到,是不用于我们所认知的DDS的,所以有很多人通常用DDS来认知NCO,这个是不对的,对于IP核,我们只有从官方手册中学习,才能正确认识到官方想表达的意思。
另外,我们需要注意的是实线部分是必须的,而虚线部分是可选的输入参数。NCO的IP核函数允许你生成一种NCO架构,你可以创建你自定义的NCO,实施的效果你可以在NCO的图形界面中看到。
在这里插入图片描述
正如上图公式所示,你可以看到这个波形生成是由上述公式生成的,其中我们要注意的正是这些参数。
在这里插入图片描述
在这里插入图片描述
从生成的黑盒子模块,我们可以看到,输入接口phi_inc_i正是公式中的fO,也就是NCO架构中左边的第一个必须输入的参数。
在这里插入图片描述
而例化中的 [15:0] freq_mod_i ,这个指的正是上图中的Frequency Modulation中,这个是个可选项。
所以输出波形的频率正由[15:0] phi_inc_i;和[15:0] freq_mod_i一起累加构成频率。
在这里插入图片描述
在这里插入图片描述
上图中的两个公式,虽然官方介绍的是f0(phi_inc)是相位增量,而fFM是频率,其实根据公式,我们可以看出,没啥区别,都是作为频率,既然官方这么叫,那也行吧。另外还有一个精度是角度精度,这个没啥用,和相位精度保持一致即可,这里官方解释的是:角度精度是将坐标转换为笛卡尔的极化坐标转换前的角度精度,那么我们就把它与相位精度保持一致即可。
在这里插入图片描述
在这里插入图片描述
正如上图所示,我们还可以看到相位调制器参数,这个也是个可选项,它影响我们的初始相位,所以在黑盒子,我们可以看到
input [15:0] phase_mod_i;
正是代表的这个参数。
此外还有一个参数就是抖动,啥叫相位抖动,也就是说会有某个相位这个参数的值是不确定的,会有左右漂移,从而导致幅度的不确定性。
在这里插入图片描述
在这里插入图片描述
在IP核的这里面可以设置,也就是说我们可以设置相位噪声。
在这里插入图片描述
在这里插入图片描述
从上面两张图中,我仅仅改变了抖动大小,可以看到抖动越大,信噪比越低,这一点特别是在输出频率比较高的情况下尤其明显。注意,参数只是样图,请不要按照上面的参数设置。

至此,我已经对主要的输入参数进行了彻底的讲解了。

通常的步骤

首先配置参数
在这里插入图片描述
然后建立仿真,勾选仿真模型,并设置仿真用的语言,verilog。注意,如果不勾选仿真,那么在后续的仿真中,仿真是无法运行的。
在这里插入图片描述

最后是生成IP
在这里插入图片描述在这里插入图片描述
如上图,会生成如上图所示的这些文件。从上面,我们可以知道.v文件需要Quartus II综合,它会添加到你的Quartus II工程中。另外qiq文件,也是需要添加入工程中的。另外_bb.v文件,是IP的黑盒子,当使用第三方仿真工具的时候可以使用这个文件,来例化。cos_c.hex和cos_f.hex文件sin_c.hex和sin_f.hex文件,这四个文件是存储初始化数据文件,以十六进制。

  • setting parameters
    在这里插入图片描述
    首先,对于算法的具体实现细节,我就不展开讲了,因为很多论文都有写过(抄过)。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

好的,至此理论部分,我们已经讲解完毕,至于具体算法实现的细节,比如CORDIC算法的原理,这些我就不讲解了,很多论文已经反反复复的抄过。

工程实例

MATLAB生成波形txt文件

%采用matlab进行电路仿真,用于验证整个FPGA电路的工作过程及输出结果是否满足要求
%同时产生FPGA程序中需要使用到的正弦波采样数据,50M采样率产生625KHZ的随机相位的正弦信号。也就是说一个周期可以采样80个点,已经能够非常好的显示出正弦波形了。
%采用matlab进行电路仿真,用于验证整个FPGA电路的工作过程及输出结果是否满足要求
%同时产生FPGA程序中需要使用到的正弦波采样数据
clc;
clear;
fi=625000;     %输入信号的频率
fc=625000;     %本振信号的频率

Fs=50000000;    %采样频率
L=1024;       %数据长度
N=10;         %量化位数

% 产生输入信号
t=0:1/Fs:(1/Fs)*(L-1);     %产生采样频率的时间序列
theta=rand()*2*pi;       %产生一个随机相位角度
si=sin(2*pi*fi*t+theta);    %生成具随机起始相位的正弦波输入信号

si=round(si*(2^(N-1)-1));     %10bit量化

%产生本振信号
sc=sin(2*pi*fc*t);      %生成本振信号
sc=round(sc*(2^(N-1)-1));       %10bit量化

%仿真混频输出并画图
so=si.*sc;     %混频器输出
sof=so-mean(so);    %混频器滤出直流分量后输出
fso=abs(fft(so,L));     %求FFT变换的幅度值
%归一化处理
sc=sc/max(abs(sc));       %本振信号的归一化处理
si=si/max(abs(si));       %输入信号的归一化处理
so=so/max(abs(so));       %输出信号的归一化处理
sof=sof/max(abs(sof));    %混频器输出信号滤出直流分量后归一化处理
fso=fso/max(fso);    %FFT的幅度值归一化处理
%转换成相对于原点对称的信号
fso=[fso(L/2+1:L),fso(1:L/2)];     %画图
m=[-L/2:1:(L/2-1)]*Fs/L*(10^(-6));    %生成频率坐标轴,单位为MHz
t=t*(10^6) ;                          %生成时间坐标轴,单位为us
subplot(221);plot(t(1:L),si(1:L));
title('10bit量化后的输入信号si','fontsize',8);
subplot(222);plot(t(1:L),so(1:L));
title('20bit量化后的混频输出信号so','fontsize',8);
subplot(223);plot(t(1:L),sof(1:L));
title('20bit滤出直流分量后的混频输出sof','fontsize',8);
subplot(224);plot(m,fso);
title('混频输出信号的幅频响应','fontsize',8);


%将生成的输入正弦信号的数据,写入外部文本文件(sinin.txt)中
f_s=si/max(abs(si));      %归一化处理
Q_s=round(f_s*(2^(N-1)-1));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%新建文本文件前,必须建好文件存放的目录文件夹,否则出现提示信息:
%??? Error using ==> fprintf
%Invalid file identifier
fid=fopen('D:\quartus_Project\Liruifeng_tem\DO_Pro\CP2\CP_2_4_matlab_alt_mixNCO\sin.txt','w');
for k=1:length(Q_s)
    B_s=dec2bin(Q_s(k)+(Q_s(k)<0)*2^N,N);  %Q_s小于0就为1,大于0就为0
    %k;
    for j=1:N
       if B_s(j)=='1'
           tb=1;
       else
           tb=0;
       end
       fprintf(fid,'%d',tb);  
    end
    fprintf(fid,'\r\n');
end
fprintf(fid,';'); 
fclose(fid);

在这里插入图片描述
关于以上部分代码的解释:
B_s=dec2bin(Q_s(k)+(Q_s(k)<0)*2^N,N); %Q_s小于0就为1,大于0就为0
由于dec2bin只能够将整数转换成二进制,所以必须要判断一下是否有负数,如果有负数,(Q_s(k)<0)的返回值就是1,如果是正数,返回值就是0,也就是说,如果是负数,那么就将原来的值加2^N ,就是其负数的补码的二进制。这一点,我之前已经写过。https://blog.csdn.net/ciscomonkey/article/details/87104636
在这里插入图片描述

顶层文件设计

   module mix_top (
	rst,clk,din,
	s_oc,dout) ;	

input		rst;         //复位信号,高电平有效
input		clk;         //数据采样时钟/FPGA系统时钟,频率为5MHz
input	   [9:0]	 din;  //输入的625KHz单频信号
output 	[9:0]	 s_oc; //本地OC输出的625KHz单频信号
output	[19:0] dout; //输出混频滤波后的的1.25MHz单频信号


//实例化NCO IP核
wire reset_n,out_valid,clken;
wire [15:0] phi_inc_i;		//本地信号的相位
wire [15:0] phase_mod_i;
wire [15:0] freq_mod_i;
wire [9:0]  oc_sin;			//本地信号的输出,默认无符号

assign reset_n = rst;       //NCO的复位信号低电平有效

assign phi_inc_i = 16'd0; //设置相位为0
assign phase_mod_i=16'd0; //设置相位为0
assign freq_mod_i=16'd819;		//大约为624KHZ,当然,有小数的话也只能舍弃。值本来应该是819.2,其实我们可以从IP核里面的提示看出,叫我们相位增量设置为819,这里,我把频率增量设置为819,相位增量设置为0,也一样。

assign clken = 1'b1;         //设置时钟允许信号始终有效

assign s_oc = oc_sin;        //将本振输出信号送至模块输出


nco nco_inst(
	.phi_inc_i(phi_inc_i),
	.freq_mod_i(freq_mod_i),
	.phase_mod_i(phase_mod_i),
	.clk(clk),
	.reset_n(reset_n),
	.clken(clken),
	.fsin_o(oc_sin),
	.out_valid(out_valid)
	);
//乘法运算实现混频输出
reg signed [19:0] mult=0;    //有符号的混频运算结果
wire signed [9:0] s_din;				//有符号的输入数字信号
wire signed [9:0] s_oc_sin;		//有符号的本地输出625KHz

assign s_din = din;       //将乘数转换成有符号数运算
assign s_oc_sin = oc_sin; //将乘数转换成有符号数运算

always @(posedge clk or negedge rst)
	if (!rst)
		mult <= 20'd0;
	else
		mult <= s_din * s_oc_sin;			//转换为有符号后,进行乘法运算

//求均值
reg signed [19:0] m1,m2,m3,m4,m5,m6,m7;
always @(posedge clk or negedge rst)
	if (!rst)
		begin
			m1 <= 20'd0;
			m2 <= 20'd0;
			m3 <= 20'd0;
			m4 <= 20'd0;
			m5 <= 20'd0;
			m6 <= 20'd0;
			m7 <= 20'd0;
		end
	else
		begin
			m1 <= mult;
			m2 <= m1;
			m3 <= m2;
			m4 <= m3;
			m5 <= m4;
			m6 <= m5;				m7 <= m6;
		end
wire signed [22:0] madd;
wire signed [19:0] mean,mt;
assign madd = mult+m1+m2+m3+m4+m5+m6+m7;   //代表将8个有符号数据加起来,其实这里我要补充一点了,原书上写的是给输入时钟是5M,输出625KHZ,所以刚好是8倍关系,那么也就是一个周期8个点。但是,这里我们的时钟是50MHz,我们本来应该是去平均值80个点,寄存80个,才能求出平均值。这里我就不管那么多啦,大家明白即可。毕竟要写80个点平均值,有点懒得写。
assign mean = madd[22:3];						//代表将8加起来的数据左移3位,相当于除以8

//滤出直流分量(均值)
assign mt = mult -mean;							//滤出去直流分量
assign dout = mt;									
endmodule

这里为什么要减去直流分量呢,当然是因为混频后626KHZ-625KHZ=0KHZ,那么就会产生直流分量,所以必须要减去直流分量。此外,还要注意的是,你试图计算一下频率增量,如果要在16位,50M时钟的情况下产生NCO,那么根本没法算出一个625KHZ的正弦,算出来,频率增量,我们应该取819.4,这里我就取819吧,这样产生的就是一个624点几KHZ的正弦。也差不多吧,本来也有噪声的,也会导致难免差一点点。这些东西差一点点,也影响不大。

仿真文件:

// Copyright (C) 1991-2013 Altera Corporation
// Your use of Altera Corporation's design tools, logic functions 
// and other software and tools, and its AMPP partner logic 
// functions, and any output files from any of the foregoing 
// (including device programming or simulation files), and any 
// associated documentation or information are expressly subject 
// to the terms and conditions of the Altera Program License 
// Subscription Agreement, Altera MegaCore Function License 
// Agreement, or other applicable license agreement, including, 
// without limitation, that your use is for the sole purpose of 
// programming logic devices manufactured by Altera and sold by 
// Altera or its authorized distributors.  Please refer to the 
// applicable agreement for further details.

// *****************************************************************************
// This file contains a Verilog test bench template that is freely editable to  
// suit user's needs .Comments are provided in each section to help the user    
// fill out necessary details.                                                  
// *****************************************************************************
// Generated on "05/30/2019 16:24:44"
                                                                                
// Verilog Test Bench template for design : mix_top
// 
// Simulation tool : ModelSim (Verilog)
// 

`timescale 1 ns/ 1 ns
module mix_top_vlg_tst();
reg clk;
reg [9:0] din;
reg rst;
// wires                                               
wire [19:0]  dout;
wire [9:0]  s_oc;




parameter clk_period=20;		//20ns
parameter data_num=800;    //仿真数据长度
parameter time_sim=data_num*clk_period;    //仿真时间


// assign statements (if any)                          
mix_top i1 (
// port map - connection between master ports and signals/registers   
	.clk(clk),		//时钟
	.din(din),    //从文件读取输入的625KHz单频信号
	.dout(dout),		//	输出混频滤波后的的1.25MHz单频信号	
	.rst(rst),			//复位
	.s_oc(s_oc)		//本地NCO产生输出的625KHz单频信号
);
initial                                                
begin
clk=0;
rst=0;
din=10'd10;//设置从文本中读取输入的625KHz单频信号的初值
#50 rst=1;

//设置仿真时间
#time_sim
$stop;
                                                 
end     
//产生时钟信号                                            
always  #(clk_period/2) clk=~clk;
//从外部TXT文件中读入数据作为测试激励
reg [9:0] stimulus[1:data_num];    //用于存储从文本中读取的数据,全部存放于数组stimulus中
integer address=0; 
 initial
begin
$readmemb("sin.txt",stimulus);//文件必须放到simulation\modelsim的文件夹中
	repeat(data_num)			
		begin
		address=address+1;
		din=stimulus[address];
		#clk_period;
		end
end 

//将混频滤波后的的1.25MHz单频信号dout写入外部TXT文件中(out.txt)

integer file_out;
initial
begin
file_out=$fopen("out.txt");
	//文件必须放到simulation\modelsim的文件夹中
	if(!file_out)
	begin
		$display("could not open file!");
		$finish;
	end
end

wire clk_write;
wire signed[19:0] dout_s;		//将混频后的数据,转换为有符号数
assign dout_s=dout;
assign clk_write=clk&(rst);    //产生写入的时钟信号,复位状态时候不写入数据

always @ (posedge clk_write)
   $fdisplay(file_out,"%d",dout_s);	//将混频后输出的有符号的数据,写入file_out代表的out.txt文件中

//将NCO产生的数据写入NCO.txt文件中
integer file_nco;
initial
begin
   //文件必须放到simulation\modelsim的文件夹中                                                
	file_nco = $fopen("nco.txt");
	if(!file_nco)
		begin
			$display("could not open file!");
			$finish;
		end
end
wire signed [9:0] nco_s;
assign nco_s = s_oc;//将NCO产生的数据转变为有符号数据
always @(posedge clk_write)
	$fdisplay(file_nco,"%d",nco_s);
endmodule

IP配置

调用NCO的IP核

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后点击geinirate即可
在这里插入图片描述
最后生成成功。
最后,方可得出NCO成功生成并添加其IP。
在这里插入图片描述
仿真的过程主要包括了行为仿真和时序仿真,无论何种仿真均需要设计测试激励文件,采用HDL代码文件书写激励,且需要在激励文件中将部分仿真结果数据写入外部的文本中,以方便matlab读取数据做进一步的仿真。
testbench中导入文本数据文件

文件命令语法(官方提示)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

modelsim仿真

在这里插入图片描述
如上图所示,可以看到混频后输出的波形差不多是1.24MHz的样子,大差不差,存在误差也正常,毕竟我们的NCO本来就是产生的近似625KHZ的信号。

下面,我们来仔细看看从modelsim中,我们还能获取哪些知识。
在这里插入图片描述
首先,我们可以看到din,导入的数据是与文件中的数据完全一致的,
此外,我们可以看到,NCO产生的数据,刚开始需要一定的时钟后,然后才能产生稳定的波形。
在这里插入图片描述

此外,我们可以看到,写入数据文本与波形中的数据完全一致
在这里插入图片描述
我们看到noc_s的波形数据与文本上面的一致。
mult <= s_din * s_oc_sin; //转换为有符号后,进行乘法运算
下面,我们再来看一下转换为有符号后,进行乘法运算,乘法运算所消耗的时钟。
在这里插入图片描述
可以看到一个时钟就可以完成乘法运算。
另外采用8级流水线,求平均值,然后减去平均值
在这里插入图片描述
可以看到移位操作在本时钟周期以内就可以完成,所以,这就是说移位操作符是非常的强大。
另外,我们再来看看减法操作,减法操作也是在本时钟周期以内就可以完成。

在这里插入图片描述

工程福利连接

联系QQ:1183699227 并附带加友目的。

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值