一、IP核
IP(Intellectual Property)原指知识产权、著作权等,在IC设计领域通常被理解为实现某种功能的设计。IP模块则是完成某种比较复杂算法或功能(如FIR滤波器、FFT、SDRAM控制器、PCIe接口、CPU核等)并且参数可修改的电路模块,又称为IP核(IP Core)。随着CPLD/FPGA器件的集成度越来越高,设计越来越复杂,使用IP核是EDA设计的发展趋势。根据实现方式的不同,IP核可以分为软核(soft core)、固核(firm core)和硬核(hard core)。
Intel公司以及第三方合作伙伴提供的IP模块可以分为两类:可修改参数的IP核(Library of Parameterized Modules, LPM)和需要授权才能在生产设计中使用的IP核(功能更复杂的模块,也称为MegaCore)。这些模块专门针对不同的器件结构进行了优化,在设计数字系统时,我们可以充分利用这些IP模块,加快设计进度,同时提高器件资源的利用率。
注:该内容引用自Verilog HDL与FGPGA数字系统设计(第二版)
二、DDS信号发生器设计
1.DDS信号发生器
(1)概念
直接数字频率合成(Direct Digital Frequency Synthesis,简称DDS或DDFS)是一种应用数字技术产生信号波形的方法,它是由美国学者J. Tierncy、C.M. Rader和B. Gold在1971年提出的,他们以数字信号处理理论为基础,从相位概念出发提出了一种新的直接合成所需波形的全数字频率合成方法。
DDS系统通常包含以下模块:
相位累加器(Phase Accumulator)
波形查找表(Waveform ROM/LUT)
数模转换器(DAC)
抗混叠滤波器(Anti-Aliasing Filter)
(2)DDS工作流程
2. DDS信号发生器设计
使用Quartus Prime Lite创建工程,顶层文件名为DDS_top,芯片选择EP4CE115F29C7(详细步骤看其余FPGA文章)。
(1)相位累加器的设计
新建Verilog HDL File文件,文件名为addr_cnt.v(如果与VS Code连用代码写好后另存为就好)
//=====相位累加器和数据锁存器=====
module addr_cnt(CPi,K,ROMaddr,Address);input CPi; //系统基准时钟(100MHz)input [12:0] K; //13位频率控制字output reg [9:0] ROMaddr; //10位ROM地址output reg [16:0] Address; //17位相位累加器地址信号always @(posedge CPi)beginAddress = Address + K;ROMaddr = Address[16:7];end
endmodule
在项目中添加addr_cnt.v文件,选择Files,右键点击Files,点击添加
找到刚才保存的文件添加
选择Set as Top-Level Entity将其设为顶层文件,点击编译
右键点击addr_cnt.v文件,选择CreateSymbol Files for Current File命令,生成该模块的符号
在Quartus中打开生成的addr_cnt.bsf文件,生成的该模块的符号如图
(2)波形存储器ROM的设计
A.方波模块
步骤跟上面的一样,文件名为squwave.v,其代码如下
//=====方波产生模块:squwave.v ======
module squwave(CPi,RSTn,Address,Qsquare);input CPi; //系统基准时钟(100MHz)input RSTn; //同步清零input [16:0] Address; //17位地址输入信号output reg [11:0] Qsquare; //输出方波信号,12位宽,送至DACalways @(posedge CPi) if(!RSTn) Qsquare=12'h000; //同步清零else begin if(Address<=17'h0FFFF) Qsquare=12'hFFF; //输出高电平else Qsquare=12'h000; //输出低电平end
endmodule
打开生成的squwave.bsf文件后该模块的符号如图
B.正弦波形存储器模块
Quartus Prime软件接受两种格式的初始化文件MemoryInitialization File(.mif)和Hexadecimal(Intel-Format)File(.hex)。使用时,将初始化文件放在当前工程项目子目录中,在配置LPM_ROM时会对其进行初始化。而建立.mif格式文件有两种方法,一种是直接编辑法,另一种是用C语言等软件生成初始化文件(初始化储存单元较多时更加实用)
打开c语言编译器,建立sinewave.c文件,代码如下
#include <stdio.h>
#include <math.h>
#define PI 3.141592
#define DEPTH 1024 //数据深度,即存储单元的个数
#define WIDTH 12 //存储单元的宽度
int main(void)
{int n,temp;float v;FILE *fp;
/*建立文件名为Sine1024.mif的新文件,允许写入数据,对文件名没有特殊要求,但扩展名必须为.mif*/fp=fopen("Sine1024.mif","w+");if(NULL==fp)printf("Can not creat file!\r\n");else{printf("File created successfully!\n");/*生成文件头,注意不要忘了“;” */fprintf(fp,"DEPTH =%d;\n",DEPTH);fprintf(fp,"WIDTH =%d;\n",WIDTH);fprintf(fp,"ADDRESS_RADIX=HEX;\n");fprintf(fp,"DATA_RADIX=HEX;\n");fprintf(fp,"CONTENT\n");fprintf(fp,"BEGIN\n");/*以十六进制输出地址和数据*/for(n=0;n<DEPTH;n++){/*周期为1024个点的正弦波*/v=sin(2*PI*n/DEPTH);/*将-1~1之间的正弦波的值扩展到0~4095之间*/temp=(int)((v+1)*4095/2); //v+1将数值平移到0~2之间/*以十六进制输出地址和数据*/fprintf(fp,"%x\t:\t%x;\n",n,temp);}fprintf(fp,"END;\n");fclose(fp); //关闭文件}
}
运行此文件后会生成sinewave.exe文件,双击运行就会生成Sine1024.mif文件
接着,验证生成的数据是否正确。用记事本打开生成的mif文件,同时用Quartus Prime软件打开mif文件,若能成功导入数据且数据一致,则说明生成文件正确,将其添加到工程文件中。
在Quartus Prime主界面选择Tool→IP Catalog
在查找框内输入ROM, IP核目录(IP Catalog)栏中会列出相关的IP核,选择ROM:1-PORT并双击
弹出如图所示的保存IP设置界面,输入文件名SineROM.v,并选中Verilog,单击OK按钮
设置ROM的数据位宽为12,存储容量(字数)为1024,单击Next按钮
点击Next,配置如下
点击Browse...选择生成的Sine1024.mif文件,这是指明初始化ROM所使用的数据文件名
然后Next直到最后一页,弹出如图所示的选择输出文件的对话框(最重要的是.v文件,其余文件按需要勾选,.bsf文件也可以选上),选择好后点击Finish
SineROM.v等相关文件就生成好了
该模块的符号如下图所示:
3.锁相环倍频电路设计
定制一个名称为PLL100M_CP的时钟模块,该模块的输入inclk0为50MHz时钟信号,输出c0为100MHz的脉冲信号,占空比为50%,带有相位锁定指示输出端locked。
在右侧查找框内输入ALTPLL(嵌入式锁相环)
双击打开,输入文件名PLL100M_CP.v,并选中Verilog,单击OK按钮
设置输入时钟(inclk0)频率为50MHz
其余的按如下图片设置
最后选择需要生成的文件
4.顶层电路设计
代码如下
//========DDS的顶层模块:DDS_top.v ======
module DDS_top(CLOCK_50, RSTn, WaveSel, K,
WaveValue, LEDG, CLOCK_100);input CLOCK_50; //50MHz时钟input RSTn; //控制方波清零,低电平有效input [1:0] WaveSel; //波形选择:SW[17:16]=10时为方波;SW//[17:16]=01时为正弦波input [12:0] K; //频率控制字SW12..SW0output reg [11:0] WaveValue; //输出波形数据wire [9:0] ROMaddr; //波形存储器地址wire [16:0] Address; //17位相位累加器地址wire [11:0] Qsine, Qsquare; //正弦、方波数据输出output [0:0]LEDG; //锁相环相位锁定指示灯,亮表示锁定output CLOCK_100; //锁相环输出时钟,频率为100MHzwire CPi =CLOCK_100;PLL100M_CP PLL100M_CP_inst ( //实例引用锁相环子模块.inclk0 ( CLOCK_50 ), //50MHz时钟输入.c0 ( CLOCK_100 ), //100MHz时钟输出.locked ( LEDG[0] ) //相位锁定指示
);addr_cnt U0_instance(CPi,K,ROMaddr,Address);//实例引用地址累加器SineROM ROM_inst ( //实例引用正弦LPM_ROM子模块.address (ROMaddr),.clock ( CPi ),.q ( Qsine )
);squwave U1(CPi,RSTn, Address,Qsquare); //实例引用方波子模块always @(posedge CPi)
begincase(WaveSel) //选择输出波形2'b01:WaveValue=Qsine; //输出正弦波2'b10:WaveValue=Qsquare; //输出方波default:WaveValue=Qsine;endcase
end
endmodule
写好后添加到工程中,将此文件设为顶层模块并进行编译。
三、设计实现
使用DE2-115开发板来验证上述设计。用板上的50MHz晶振作为时钟输入,用KEY3控制方波清零,用SW12~SW0设置频率控制字,SW17、SW16用来选择输出波形的种类,用LEDG0作为PLL的相位锁定指示。
有DE2_115_pin_assignments.csv文件可以直接导入不用手动配置引脚,没有的话参考DE2-115文档配置。为了方便导入文件DE2_115_pin_assignments.csv进行引脚分配,将使用该文件中的端口名称代替上述DDS_top.v(代码如下)中的信号名称。为此再编写一个顶层文件DE2_DDS_top.v代码如下:
//=====在开发板上运行的DDS的顶层模块:DE2_115_DDS_top.v ======
module DE2_115_DDS_top(CLOCK_50, KEY, SW, GPIO_0, LEDG);input CLOCK_50; //50MHz时钟input[3:3] KEY; //按键KEY3,控制方波清零input[17:0] SW; //拨动开关output [12:0] GPIO_0; //扩展接口,送出波形数据给DACoutput [0:0]LEDG; //绿色LED指示相位是否锁定wire CLOCK_100; //100MHz时钟assign GPIO_0[12]=CLOCK_100; //送给DAC的时钟wire RSTn = KEY[3]; //控制方波清零,低电平有效wire [1:0] WaveSel = SW[17:16]; //选择输出波形wire [12:0] K = SW[12:0]; //设置频率控制字,最小值必须为1wire [11:0] WaveValue;assign GPIO_0[11:0] = WaveValue; //输出波形数据DDS_top DE2(CLOCK_50, RSTn, WaveSel, K, WaveValue, LEDG, CLOCK_100);
endmodule
对其进行全编译