文章目录

  • 背景
  • 再次认识关于DDS的来源
  • 实际案例
    • 官方资料阅读(NCO IP core)
      • 参数原理
      • 通常的步骤
    • 工程实例
      • MATLAB生成波形txt文件
      • IP配置
      • 文件命令语法(官方提示)
      • modelsim仿真
      • 工程福利连接

背景

由于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:Nif B_s(j)=='1'tb=1;elsetb=0;endfprintf(fid,'%d',tb);  endfprintf(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;     //有符号的本地输出625KHzassign s_din = din;       //将乘数转换成有符号数运算
assign s_oc_sin = oc_sin; //将乘数转换成有符号数运算always @(posedge clk or negedge rst)if (!rst)mult <= 20'd0;elsemult <= s_din * s_oc_sin;         //转换为有符号后,进行乘法运算//求均值
reg signed [19:0] m1,m2,m3,m4,m5,m6,m7;
always @(posedge clk or negedge rst)if (!rst)beginm1 <= 20'd0;m2 <= 20'd0;m3 <= 20'd0;m4 <= 20'd0;m5 <= 20'd0;m6 <= 20'd0;m7 <= 20'd0;endelsebeginm1 <= 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)            beginaddress=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
endwire 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 并附带加友目的。

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

  1. 前置交换机数据交换_我们的数据科学交换所

    前置交换机数据交换 The DNC Data Science team builds and manages dozens of models that support a broad range o ...

  2. 用forall的save exceptions机制高效处理数据交换中的异常数据

    在一个数据交换场景中,对方提供一个远程数据库,我方根据时间戳提取增量数据,经转换处理后存到我方数据库的表中. 对方数据有以下特征: 1.增量数据数量较大: 2.数据不规范,部分数据无法直接写入我方数据 ...

  3. python读数据-python读取各种文件数据方法解析

    python读取.txt(.log)文件 ..xml 文件 .excel文件数据,并将数据类型转换为需要的类型,添加到list中详解 1.读取文本文件数据(.txt结尾的文件)或日志文件(.log结尾 ...

  4. oracle数据泵导出csv文件,数据泵expdp导出遇到ORA-01555和ORA-22924问题的分析和处理...

    使用数据泵导出数据库数据时,发现如下错误提示: ORA-31693: Table data object "CAMS_CORE"."BP_EXCEPTION_LOG&qu ...

  5. Python 使用matplotlib数据可视化显示CSV文件数据

    (一)获取数据 1.下载sitka_weather_07-2014.csv数据:https://ehmatthes.github.io/pcc/ (二)分析CSV文件头 csv模块包含在python标 ...

  6. flask 接口上传文件_Flask干货:Flask数据交换——上传文件

    图 | 源网络文 | 5号程序员01 事情是这样的. 有一天五号程序员打算网购一盒巧克力送给自己的女朋友 想必女朋友收到礼物是这样的: 结果商家邮寄来的巧克力中夹着一张纸条: 害!你说气不气人,现在都 ...

  7. 数据预处理:读取文件数据,并存为python数组

    文件的简单读取 # 定义一个将文件中的数据转化为数组的类 import numpy as np class DataUtil: # ================================== ...

  8. python使用to_csv(mode=‘a‘)追加数据/(mode=‘w‘)覆盖文件数据

    覆盖数据直接to_csv,追加数据需添加mode='a' Data=['2021-09-01','2021-09-10','vivo','99999'] ColumnsName=['统计开始时间',' ...

  9. Dmc雷赛板卡仿写(六):数据在程序中的保存与读取 ,类变量读写,json文件数据读入,ini文件数据读入

    1.类变量读入(之前类的学习中写过) //在.h中实例化了这些类using AxisName = QString;using AxisHash = QMap<AxisName, DmcAxis* ...

最新文章

  1. 红外协议之NEC协议
  2. Gradle of Android Example
  3. java多线程runnable_Java 多线程 之 Runnable
  4. 笔记-高项案例题-2016年上-计算题
  5. vue学习笔记(二)- 数据绑定、列表渲染、条件判断
  6. 建模就用Rose的时代宣告结束
  7. .NET的一点历史故事:擦肩而过的机遇
  8. 华为荣耀30pro鸿蒙内测版,荣耀手机用户放心了 消息称荣耀30 Pro正在内测华为鸿蒙OS...
  9. pyinstaller--将py文件转化成exe
  10. 华为P40pro 手机云台_2020年目前拍照最好的手机推荐!华为P40 Pro!DXO全球榜首
  11. Flex 给PopUpButton 设置皮肤
  12. python入门——条件控制+循环语句
  13. vue + wangeditor封装富文本组件
  14. Linux内核驱动之主次编号
  15. 【ZOJ 1964】【尺取】Bound Found【暑期 No.3】
  16. 解决夜神模拟器设置了代理后无法上网
  17. 卸载控制面板(Control Panel)存在的重复程序(CrowdStrike)
  18. OSS对象存储是什么?
  19. 【Cpp】C和C++混合编程
  20. 【其它】怎样开启Win7快速启动栏

热门文章

  1. 内核知识第九讲,32位下的分页管理,36位下的分页管理.以及64位下的分页管理
  2. Win7(64位)下安装Anaconda+Tensorflow(cpu)
  3. .Net 获取日期所属于一年中的第几周
  4. javac、jar使用实录
  5. 也谈表达式分析和计算
  6. C++ const 学习
  7. VS2012调试ReactOS源码环境搭建4 - 生成ReactOS镜像和VS解决方案成功
  8. Java虚拟机字节码指令概述
  9. 利用yum下载rpm包并批量安装
  10. jdk官网历史版本下载Oracle账号密码