首页 基于FPGA 的DDS的硬件和软件设计

基于FPGA 的DDS的硬件和软件设计

举报
开通vip

基于FPGA 的DDS的硬件和软件设计基于FPGA 的DDS的硬件和软件设计 Pulse Interval Modulation – Dual Header (PIM-DH)引言 在通信系统的科研实验中,常常需要用到多种不同频率和相位的信号,如正弦波、三角波、方波和锯齿波等,因此多波形信号发生器的应用十分广泛。传统的波形发生器多采用模拟分立元件实现,产生的波形种类要受到电路硬件的限制,体积大,灵活性和稳定性也相对较差。近年来,以数字技术为基础的波形发生器得到了飞速的发展,性能指标都达到了一个新的水平。现场可编程门阵列器件具有容量大、运算速度快、现场...

基于FPGA 的DDS的硬件和软件设计
基于FPGA 的DDS的硬件和软件设计 Pulse Interval Modulation – Dual Header (PIM-DH)引言 在通信系统的科研实验中,常常需要用到多种不同频率和相位的信号,如正弦波、三角波、方波和锯齿波等,因此多波形信号发生器的应用十分广泛。传统的波形发生器多采用模拟分立元件实现,产生的波形种类要受到电路硬件的限制,体积大,灵活性和稳定性也相对较差。近年来,以数字技术为基础的波形发生器得到了飞速的发展,性能指标都达到了一个新的水平。现场可编程门阵列器件具有容量大、运算速度快、现场可编程等优点,使得许多复杂的电路有了新的实现途径,越来越被广泛地应用到实际系统中。本文参考直接数字频率合成原理,利用Max-PIUSII软件和Matlab数学工具,基于FPGA配合相应外围器件实现多波形信号发生器,电路结构简单、易于扩展,具有极大的灵活性和方便性。实现的多波形发生器可产生正弦波、三角波、锯齿波和方波等信号,输出信号频率在0 Hz一100KHz范围内可调。 1 设计任务 设计一基于FPGA的多波形发生器。设计目标为,可以实现稳定的正弦波、三角波、方波的输出,输出频率范围:1Hz~100KHz,且频率可调(步进1Hz)。 2 设计思路及原理 DDS的工作原理是以数控振荡器的方式产生频率、相位可控制的正弦波。电路一般包括基准时钟、频率累加器、相位累加器、幅度/相位转换电路、D/A转换器和低通滤波器(LPF)。频率累加器对输入信号进行累加运算,产生频率控制数据X(frequency data或相位步进量)。相位累加器由N位全加器和N位累加寄存器级联而成,对代表频率的2进制码进行累加运算,是典型的反馈电路,产生累加结果Y。幅度/相位转换电路实质上是一个波形寄存器,以供查表使用。读出的数据送入D/A转换器和低通滤波器。市场上目前专用的DDS芯片种类比较多,虽然他们的输出频率高、输出波形好,但是输出波形比较单一,一般只有正弦(余弦)波和方波;另外专用DDS芯片的输入已经固定,当需要一个变化灵活的频率或相位时,可能无法通过这些输入信息达到要求;这些都限制了专 用DDS芯片的使用范围。而用FPGA实现的DDS电路则不一样,由于波形信息存储在RoM 中,可以通过改变RoM中的存储内容达到实现不同波形的要求。同样波形是和频率、相位有关系的,因此也可以通过修改ROM 中的波形数据来实现比较灵活的频率和相位要求。另外专用DDS芯片的功耗大,价格高,相比而言用FPGA 实现的电路在这些方面同样具有很大的优势。而且由于FPGA自身的特点,能够很容易在FPGA内部生成多个基本模块,从而开发出功能强大的芯片。另外FLEX1OK器件还提供了可供选择的时钟锁定和时钟自举电路,这两种电路中均含有用来提高设 计速度和减少资源占用的锁相环,可以支持高速设计。通过MAX+PLUS I或者QUARTUS软件可以调用FPGA 的内部模块或者VHDL语言设计电路,使电路的设计、修改变得简单方便 2.1 DDS工作原理及原理框图 直接数字频率合成(DDS)技术是60年代末出现的第三代频率合成技术,以Nyquist时域采样定理为基础,在时域中进行频率合成。DDS具有相对带宽很宽,频率转换时间极短(可小于20ns),频率分辨率可以做到很高(典型值为0.001Hz)等优点;另外,全数字化结构便于集成,输出相位连续,频率、相位、幅度都可以实现程控,通过更换波形数据可以轻易实现任意波形功能。总的来说,新一代的直接数字频率合成器采用全数字的方式实现频率合成,与传统的频率合成技术相比,具有以下特点:(1)频率转换快。直接数字频率合成是一个开环系统,无任何反馈环节,其频率转换时间主要由频率控制字状态改变所需的时间及各电路的延时时间所决定,转换时间很短。(2)频率分辨率高、频点数多。DDS输出频率的分辨率和频点数随机位累加器的位数的增长而呈指数增长。分辨率高达μHz。(3)相位连续。DDS在改变频率时只需改变频率控制字(即累加器累加步长),而不需改变原有的累加值,故改变频率时相位是连续的。(4)相位噪声小。DDS的相位噪声主要取决于参考源的相位噪声。(5)控制容易、稳定可靠。高集成度、高速和高可靠是FPGA/CPLD最明显的特点,其时钟延迟可达纳秒级,结合其并行工作方式,在超高速应用领域和实时测控方面有非常广阔的应用前景。在高可靠应用领域,如果设计得当,将不会存在类似于MCU的复位不可靠和PC可能跑飞等问题。CPLD/FPGA的高可靠性还表现在,几乎可将整个系统下载于同一芯片中,实现所谓片上系统,从而大大缩小了体积,易于管理和屏蔽。所以,采用FPGA来实现DDS有明显的好处。 2.2 具体工作过程 图2-1 DDS结构 上图所示是一个基于的DDS电路的工作原理框图 DDS的工作原理是以数控振荡器的方式产生频率、相位可控制的正弦波。电路一般包括基准时钟、频率累加器、相位累加器、幅度/相位转换电路、D/A转换器和低通滤波器(LPF)。频率累加器对输入信号进行累加运算,产生频率控制数据X(frequency data或相位步进量)。相位累加器由N位全加器和N位累加寄存器级联而成,对代表频率的2进制码进行累加运算,是典型的反馈电路,产生累加结果Y。幅度/相位转换电路实质上是一个波形寄存器,以供查表使用。读出的数据送入D/A转换器和低通滤波器。 每来一个时钟脉冲Fclk,N位加法器将频率控制数据X与累加寄存器输出的累加相位数据相加,把相加后的结果Y送至累加寄存器的输入端。累加寄存器一方面将在上一时钟周期作用后所产生的新的相位数据反馈到加法器的输入端,以使加法器在下一时钟的作用下继续与频率控制数据X相加;另一方面将这个值作为取样地址值送入幅度/相位转换电路,幅度/相位转换电路根据这个地址输出相应的波形数据。最后经D/A转换器和低通滤波器将波形数据转换成所需要的模拟波形。相位累加器在基准时钟的作用下,进行线性相位累加,当相位累加器加满量时就会产生一次溢出,这样就完成了一个周期,这个周期也就是DDS信号的频率周期。 DDS输出信号的频率由下式给定: F,(x/y)*F outclk (2-1) f,16Mhz假定基准时钟为,累加器为24位,则再假定X=1000000Mhz,则 clk f,(1000000/16777216)*16000000,1Mhz out 可见,通过设定相位累加器位数、频率控制字X和基准时钟的值,就可以产生任意频率的输出。DDS的频率分辨率定义为: Fout=Fclk/Y (2-2) 由于基准时钟一般固定,因此相位累加器的位数就决定了频率分辨率。如上面的例子,相位累加器为24位,那么频率分辨率就可以认为是24位。位数越多,分频率越高。 3 系统电路的设计及原理 3.1 系统原理 用 FP GA 实现多波形信号发生器基于直接数字频率合成技术。图1以正弦输出为例,概括了直接数字频率合成器的结构和原理。图1中频率控制字和相位控制字分别控制DDS输出波形的频率和相位。DDS系统的核心是N bit相位累加器。相位累加器以频率控制字为步长进行地址抽取,以参考时钟周期为采样率,产生待合成信号的数字化相位取样值。随后通过查表变换,数字相位取样值被转化为信号波形的数字幅度序列,再由数/模变换器(DAC)将代表波形幅度的数字序列转化为模拟电压,最后经由低通滤波器将DAC输出的阶梯状波形平滑为所需的连续波形。 若频率控制字设为K,相位累加器为N位,参考时钟频率fc,则输出频率为 (3-1) 最小频率分辨率为 (3-2) DDS的输出频率下限对应于频率控制字K=0的情形,即 fout=0 根据奈奎斯特抽样定律,DDS的输出上限频率为时钟频率的1/2,即 (3-3) 但由于实际输出低通滤波器的非理想特性,工程上可实现的频率上限为 (3-4) 即若参考频率为fc, DDS的输出频率范围是0~2/5fc。由于 DDS的模块化结构,其输出波形由波形查找表中的数据来决定,因此,只需改变查找表中的数据,即可以方便地利用DDS产生出正弦波之外的其他波形,如余弦波、方波、三角波、锯齿波甚至调频、调相波以及带限的噪声信号。 系统总框图如(图3-1)所示。 图3-1 系统整体框图 3.2 各模块具体实现原理分析和说明 3.2.1 外围电路 FPGA的外围电路如(图3-2)所示 图3-2 FPGA的外围电路 单片机是用来控制整个电路的,他负责输入FPGA的频率、相位信息,对键盘和显示模块的控制。键盘用来输入需要的频率和相位信息,经单片机再输入FPGA。显示模块用来显示输入的频率、相位信息以及输出频率等状态。DAC用来将FPGA输出的数字信息转换成模拟信息。LPF滤除不必要的频率,使波形更加平滑。 3.2.2 频率控制模块 这一模块是由FPGA实现的,FPGA完成相位累加器的功能,而频率控制字X是由外部拨码开关给出的。 相位累加器(见图3-3)是实现DDS的核心,它由一个N位字长的二进制加法器和一个固定时钟脉冲取样的N位相位寄存器组成。相位寄存器的输出与加法器的一个输入端在 fc 频率控制字X 输出序列N N位加法N位相位寄存器 图3-3 相位累加器结构 器 内部相连,加法器的另一个输入端是外部输入的频率控制字X。这样,在每个时钟到达时,相位寄存器采样上个时钟周期内相位寄存器的值与频率控制字X之和,并作为相位累加器在这一时钟周期的输出。频率控制字X决定了相应的相位增量,相位累加器则不断地对该相位增量进行线性累加,当相位累加器积满两时就会产生一次溢出,从而完成一个周期性的动作,这个动作周期即是DDS合成信号的一 个频率周期。于是,输出信号波形的频率表示式为 N (3-5) ,,f,f,K/2oc 由该式可知,输出信号频率主要取决于频率控制字X,当X增大时,可以不断fo的增高,由抽样定理,最高输出频率不得大于/2,而根据实验所得,实际工fc 作频率小于/3时较为合适。 fc 由于受到FPGA芯片资源限制,所能达到的频率不是很高,范围也不是很广。根据设计目标,决定取=51200Hz,选取4.096MHz有源晶振,经过80分频,得fc 到基准频率为51200Hz。N为9位。这样,频率分辨率为 N9 f,f/2,51200/2,100Hzminc 可以达到的最高最佳频率为51200/3?17KHz。 另外Altera还在它们公司的VHDL设计平台MAX +PLU S上提供了LPM_ADD_SUB核和LPM_DFF核,利用MAX+PLUS综合可以自动编译包含LPM_ADD_SUB和LPM_DFF元件的文件,本设计利 用LPM_ADD_SUB核以及LPM_DFF核在FPGA上实现相位累加器。 3.2.3 ROM查找表设计 FPGA 的结构是由基于半定制门陈列的设计思想而得到的。从本质上讲, FPGA是一种比半定制还方便的ASIC 设计技术。FPGA的结构主要分为三部分: 可编程逻辑块、可编程IˆO 模块、可编程内部连线。可编程逻辑块的构造主要有两种类型: 即基于查找表 的结构类型和基于乘积项的结构类型。查找表型FPGA的可编程逻辑单元是由功能为查找表的SRAM构成逻辑函数发生器, 实现与其它功能块的可编程连接。在此,选用FLEX10K系列FPGA作为目标芯片,FLEX10K系列是Altera 公司于1995年推出的产品,它的集成度达到了10 万门,而且在FPGA 中首次集成了嵌入式存储器块的芯片。它是查找表型FPGA , 由输入-输出控制模块( IOC )、嵌入式阵列块(EAB )、逻辑阵列块(LAB ) 和快速通道( FASTTRACK) 构成。其中EAB是一种输入输出端带有寄存器的非常灵活的RAM ,它既可以用来作为存储器使用,也可以用来实现逻辑功能。每一个EAB有容量为2048位的RAM , 可配置为存储器或逻辑函数,FLEX10K系列中不同型号的芯片其中包含的EAB的数目不同, FLEX10K10有3个EAB。另外Altera还在它们公司的VHDL设计平台MAX +PLU S上提供了LPM ROM 核,利用MAX+PLUS综合可以自动编译包含LPM ROM元件的文件, 在FLEX10K10的EAB上产生ROM功能。本设计利用LPM ROM核预置正弦查找表在FPGA上。LPM 2ROM 核简介:输入端口: address[ ], ROM 的地址信号;输出端口: q[ ], ROM 的输出数据。具体参数:LPM_WIDTH: 输出数据的宽度(1, 64 任选);LPM_WIDTHAD: 输 入地址的宽度(1, 12任选);LPM_FILE: 存储器初始化文件名(. m if or . hex)。本设计中各参数设置如下: LPM_WIDTH: =>8; LPM_WIDTHAD=>9; LPM_OUTDATA=>”UNREGISTERED”; LPM_ADDRESS_CONTROL=>"UNREGISTERED"; 其中LPM_FILE的“.mif”中存放正弦的值,可由文本形式生成,也可由高级语言如C语言生成。ROM查找表在整个设计中是一个比较重要的部分。为了保证波形的平滑,设计时可将一个周期分为512个点。但是,点数太多时,用文本方式输入可能有很多困难。因此,应当用C语言描述正弦方程式,最后再将其转化为所需的mif文件。下面是用TC来在512.mif文件里产生512个点的正弦波数据的源程序: #include #include"math.h" int main() { int s; int i; FILE *fp; fp=fopen("512.mif","r+"); fprintf(fp," -- MAX+plus II - generated Memory Initialization File\n "); fprintf(fp," -- By 00022809\n\n\n\n\n"); fprintf(fp,"WIDTH=8;\n\n"); fprintf(fp,"DEPTH=512;\n\n"); fprintf(fp,"ADDRESS_RADIX=HEX;\n\n"); fprintf(fp,"DATA_RADIX=HEX;\n\n"); fprintf(fp,"CONTENT BEGIN\n"); for(i=0;i<512;i++) {s=abs(sin(atan(1.0)*8/512*i)*255); fprintf(fp,"%x\t:\t%x;\n",i,s); } fprintf(fp,"END;\n"); fclose(fp); } 这样就可以得到一个周期512个点的波形数据。 ROM 存储器是设计人员在许多设计电路中不可缺少的关键部件,特别是在一些特殊的运算场合,设计人员通常利用ROM 改造出各种各样的查找表,以简化电路,提高电路的处理速度和稳定性。在FPGA 器件中实现存储器功能,需要占用芯片的存储单元,而这种资源是十分有限的,如何在FPGA 器件的存储单元和逻辑单元的使用效率上取得最佳折衷是设计人员必须细心考虑的问题。一个好的设计应该是速度、可靠性和资源利用率三者的最佳结合。 对于锯齿波、三角波、方波等相位/幅度对应关系比较简单的信号,则可以通过直接计算得到幅度数据,无需占用有限的ROM资源。这一部分相位/幅度转换采用Verilog HDL语言完成。 3.2.4 数模转换模块 数模转换。本来欲打算采用转换速度为85ns,带宽为10MHz的8位单调高速乘法器AD DAC08或者是DAC0800。但由于都购买不到,而改为使用转换速度为1us带宽为1MHz的DAC0832,这样最高频率就不是很高。DAC0832为电流输出型D/A转换器,需要外接运算放大器进行电流电压变换才能得到模拟电压输出。输出方式为单极性输出方式的时候,输出级接一低电压温漂运放op07作为电压电流转换器,在运算放大器的输出端就可以得到单极性模拟电压: VOUT (3-6) V,,IROUTOUTfb 3.2.5 滤波输出电路模块 D/A输出后,通过滤波电路,使信号平滑。滤波器在通带内的平坦程度对我们而言,比其衰减更为重要,而且,巴特沃斯滤波器的元件值也较合乎实际情况,不像绝大多数其他类型滤波器对元件值要求那么苛刻。在截止频率附近,频率响应钝化可能使这些滤波器在要求锐截止的地方不合要求。 二阶巴特沃斯有源低通滤波器设计(图3-4): 正弦波的输出频率小于10KHz,为保证10KHz频带内输出幅度平坦,又要尽可能抑制谐波和高频噪声,综合考虑取 R1 = 1 KΩ,R2 = 1 KΩ,C1 = 100 pF,C = 100pF 图3-4 有源低通滤波器设计 3.3 设计所用关键元器件的介绍 3.3.1 FLEX10K系列FPGA (1) 概述。 FLEX10K是工业界第一个嵌入式的可编程逻辑器件,采用可重构的CMOS SRAM工艺,把连续的快速通道互连与独特的嵌入式阵列结构相结合,同时也结合了众多可编程器件的优点来完成普通门阵列的宏功能。由于其具有高密度、低成本、低功率等特点,所以脱颖而出成为当今Altera PLD中应用最好的器件系列。其集成度已达到25万门。它能让设计人员轻松地开发出集存储器、数字信号处理器及特殊逻辑包括32位多总线系统等强大功能于一身的芯片。到目前为止,已经推出了FLEX10K、FLEX10KA、FLEX10KV、FLEX10KE等分支系列。FLEX10K结构类似于嵌入式门阵列,是门阵列市场中成长最快的器件。像 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 门阵列一样,嵌入式门阵列采用一般的门海(sea of gates)结构实现普通逻辑,因此,在实现大的特殊逻辑时会有潜在死区。与标准门阵列相比,嵌入式门阵列通过在硅片中嵌入逻辑块的 方法 快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载 来减少死区,提高速度。然而,典型的嵌入式宏功能模块通常是不能改变的,这就限制了设计人员的选择。相反,FLEX10K器件是可编程的,在调试时它允许设计人员全面控制嵌入式宏功能模块和一般的逻辑,可以方便地反复修改设计。 嵌入式阵列用来实 每个FLEX10K器件包含一个嵌入式阵列和一个逻辑阵列。 现各种存储器及复杂的逻辑功能,如数字信号处理、微控制器、数据传输等。逻辑阵列用来实现普通逻辑功能,如计数器、加法器、状态机、多路选择器等。嵌入式阵列和逻辑阵列结合而成的嵌入式门阵列的高性能和高密度特性,使得设计人员可在单个器件中实现一个完整的系统。 FLEX10K器件的配置通常是在系统上电时,通过存储于一个串行PROM中的配置数据或者由系统控制器提供的配置数据来完成。Altera提供EPC1、EPC2、EPC16和EPC1441等配置用的PROM器件,配置数据也能从系统RAM和BitBlaster串行下载电缆或ByteBlasterMV并行下载电缆获得。对于配置过的器件,可以通过重新复位器件、加载新数据的方法实现在线可配置(ICR,In-Circuit Reconfigurability)。由于重新配置要求少于320ms,因此可在系统工作时实时改变配置。FLEX10K器件由MAX+PLUS II开发软件支持。 (2)FLEX10K器件系列的特点。 ?FLEX10K是工业界第一种嵌入式的PLD器件系列,具有实现宏功能的嵌入式阵列(例如实现高效存储器和特殊的逻辑功能)和实现普通功能的逻辑阵列,可提供可编程单芯片系统(SOPC)集成。 ? 高密度:1万~25万个可用门。高达40960位内部RAM(每个EAB有2048位),所有这些都可在不减少逻辑能力的情况下使用。 ? 系统级特点:支持多电压接口;在FLEX10KA器件中允许输入的引脚电压为5.0V,在FLEX10KB器件中允许输入的引脚电压为3.3V和5.0V;低功耗(维持状态小于0.5mA);遵守PCI总线规定;内带JTAG边界扫描测试电路;边界扫描测试电路;器件采用先进的工艺制造并可在2.5V、3.3V、5.0V电源电压下工作。所有器件都100%经过功能测试。 ? 灵活的内部连接:快速、可预测连线延时的快速通道(Fast Track)连续式布线结构; 实现快速加法器、计数器和比较器的专用进位链;实现高速、多输入逻辑函数的专用级联链;实现内部三态总线的三态模拟;多达六个全局时钟信号和四个全局清除信号。 强大的I/O引脚功能:每个引脚都有一个独立的三态输出使能控制及漏极? 开路配置选项(Open-drain option);可编程输出电压的摆率控制,可有减少开关噪声。FLEX10KA、FLEX10KE、FLEX10KS器件支持热拔插。 ? 多种封装方式:用户可任意选择84~600引脚的各种封装。封装形式有PLCC、TQFP、PQFP、RQFP、PGA、BGA等,同一种封装中的各种FLEX10K系列器件的引脚相兼容。 FLEX10K还包括了一个优化界面,允许微处理器以串行方式或并行方式、同步方式或异步方式对FLEX10K器件进行配置。 FLEX10K的这些特点使得FLEX10K器件成为替代传统门阵列专用的理想选择,其使用非常广泛;又由于FLEX10K系列的芯片都带有JTAG口(Joint Test Action Group—联合测试行动组),能够使用JTAG PROGRAM指令进行配置;为了尽量简化电路,所以本设计采用FLEX10K系列产品中的FLEX10K器件,该型号芯片采用PLCC封装,有84个管脚,其中I/O引脚个数为59。 FLEX10K的主要引脚和引脚名称的对应关系如(表3-1)所示。 表3-1 FLEX10K10的主要引脚和引脚名称 引脚名称 输出引脚 引脚名称 输出引脚 MSEL0(2) 31 MSEL1(2) 32 nSTATUS(2) 55 nCONFIG(2) 34 DCLK(2) 13 CONF-DONE(2) 76 INIT-DONE(3) 69 nCE(2) 14 nCEO(2) 75 nWS(4) 80 nRS(4) 81 nCS(4) 78 CS(4) 79 RDYnBSY(4) 70 CLKUSR(4) 73 DATA7(4) 5 DATA6(4) 6 DATA5(4) 7 DATA4(4) 8 DATA3(4) 9 DATA2(4) 10 DATA1(4) 11 DATA0(2),(5) 12 TDI(2) 15 TDO(2) 74 TCK(2) 77 TMS(2) 57 TRST(2) 56 Dedicated Input 2,42,44,84 Dedicated Clock 1,43 Pins DEVB-CLRn(3) 3 DEV-OE(3) 83 VCCINT 4,20,33,40,GNDINT 26,41,46,68, 45,63 82 Total User I/O Pins 59 注: ? 没有列出所有的用户I/O引脚。 ? 该引脚为专用引脚,不能用作用户I/O引脚。 ? 该引脚如果没有用于器件配置功能,则它可用作用户I/O引脚。 ? 该引脚在器件配置后可用作用户I/O引脚。 ? 该引脚在用户模式中是三态。 ? 用户I/O引脚数包括专用输入引脚、专用时钟引脚和所有I/O引脚。 (3)FPGA 结构与原理 ? 查找表(Look-Up-Table)的原理与结构 采用这种结构的PLD芯片我们也可以称之为FPGA:如altera的ACEX,APEX系列,xilinx的Spartan,Virtex系列等。 查找表(Look-Up-Table)简称为LUT,LUT本质上就是一个RAM。 目前FPGA中多使用4输入的LUT,所以每一个LUT可以看成一个有4位地址线的16x1的RAM。 当用户通过原理图或HDL语言描述了一个逻辑电路以后,PLD/FPGA开发软件会自动计算逻辑电路的所有可能的结果,并把结果事先写入RAM,这样,每输入一个信号进行逻辑运算就等于输入一个地址进行查表,找出地址对应的内容,然后输出即可。 (表3-2)是一个4输入与门的例子, 表3-2 实际逻辑电路 LUT的实现方式 a,b,c,d 输入 逻辑输出 地址 RAM中存储的内容 0000 0 0000 0 0001 0 0001 0 .... 0 ... 0 1111 1 1111 1 ? 基于查找表(LUT)的FPGA的结构 我们看一看xilinx Spartan-II的内部结构,如下图: 图3-5 xilinx Spartan-II的内部结构 Spartan-II主要包括CLBs,I/O块,RAM块和可编程连线(未表示出)。在spartan-II中,一个CLB包括2个Slices,每个slices包括两个LUT,两个触发器和相关逻辑。 Slices可以看成是SpartanII实现逻辑的最基本结构 (xilinx其他系列,如SpartanXL,Virtex的结构与此稍有不同,具体请参阅数据 手册 华为质量管理手册 下载焊接手册下载团建手册下载团建手册下载ld手册下载 ) altera的FLEX/ACEX等芯片的结构如下图: 图3-6 altera FLEX/ACEX 芯片的内部结构 图3-7 逻辑单元(LE)内部结构 FLEX/ACEX的结构主要包括LAB,I/O块,RAM块(未表示出)和可编程行/列连线。在FLEX/ACEX中,一个LAB包括8个逻辑单元(LE),每个LE包括一个LUT,一个触发器和相关的相关逻辑。LE是FLEX/ACEX芯片实现逻辑的最基本结构(altera其他系列,如APEX的结构与此基本相同,具体请参阅数据手册) ? 查找表结构的FPGA逻辑实现原理 我们还是以这个电路的为例: 图3-8 A,B,C,D由FPGA芯片的管脚输入后进入可编程连线,然后作为地址线连到到LUT,LUT中已经事先写入了所有可能的逻辑结果,通过地址查找到相应的数据然后输出,这样组合逻辑就实现了。 该电路中D触发器是直接利用LUT后面D触发器来实现。时钟信号CLK由I/O脚输入后进入芯片内部的时钟专用通道,直接连接到触发器的时钟端。触发器的输出与I/O脚相连,把结果输出到芯片管脚。这样PLD就完成了图3所示电路的功能。(以上这些步骤都是由软件自动完成的,不需要人为干预)这个电路是一个很简单的例子,只需要一个LUT加上一个触发器就可以完成。对于一个LUT无法完成的的电路,就需要通过进位逻辑将多个单元相连,这样FPGA就可以实现复杂的逻辑。由于LUT主要适合SRAM工艺生产,所以目前大部分FPGA都是基于SRAM工艺的,而SRAM工艺的芯片在掉电后信息就会丢失,一定需要外加一片专用配置芯片,在上电的时候,由这个专用配置芯片把数据加载到FPGA中,然后FPGA就可以正常工作,由于配置时间很短,不会影响系统正常工作。 也有少数FPGA采用反熔丝或Flash工艺,对这种FPGA,就不需要外加专用的配置芯片。 ? 其他类型的FPGA和PLD 随着技术的发展,在2004年以后,一些厂家推出了一些新的PLD和FPGA,这些产品模糊了PLD和FPGA的区别。例如Altera最新的MAXII系列PLD,这是一种基于FPGA(LUT)结构,集成配置芯片的PLD,在本质上它就是一种在内部集成了配置芯片的FPGA,但由于配置时间极短,上电就可以工作,所以对用户来说,感觉不到配置过程,可以传统的PLD一样使用,加上容量和传统PLD类似,所以altera把它归作PLD。 还有像Lattice的XP系列FPGA,也是使用了同样的原理,将外部配置芯片集成到内部,在使用方法上和PLD类似,但是因为容量大,性能和传统FPGA相同,也是LUT架构,所以Lattice仍把它归为FPGA。 ? 选择PLD还是FPGA, 根据上一篇PLD的结构和原理可以知道,PLD分解组合逻辑的功能很强,一个宏单元就可以分解十几个甚至20,30多个组合逻辑输入。而FPGA的一个LUT只能处理4输入的组合逻辑,因此,PLD适合用于设计译码等复杂组合逻辑。 但FPGA的制造工艺确定了FPGA芯片中包含的LUT和触发器的数量非常多,往往都是几千上万,PLD一般只能做到512个逻辑单元,而且如果用芯片价格除以逻辑单元数量,FPGA的平均逻辑单元成本大大低于PLD。 所以如果设计中使用到大量触发器,例如设计一个复杂的时序逻辑,那么使用FPGA就是一个很好选择。 同时PLD拥有上电即可工作的特性,而大部分FPGA需要一个加载过程,所以,如果系统要可编程逻辑器件上电就要工作,那么就应该选择PLD 3.3.2 AT89S51芯片介绍 (1)AT89S51简介 AT89S51是一个低功耗,高性能CMOS 8位单片机,片内含4k Bytes ISP(In-system programmable)的可反复擦写1000次的Flash只读程序存储器,器件采用ATMEL公司的高密度、非易失性存储技术制造,兼容标准MCS-51指令系统 位中央处理器和ISP Flash存储单元,功及80C51引脚结构,芯片内集成了通用8 能强大的微型计算机的AT89S51可为许多嵌入式控制应用系统提供高性价比的解决 方案 气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载 。 AT89S51具有如下特点:40个引脚,4k Bytes Flash片内程序存储器,128 bytes的随机存取数据存储器(RAM),32个外部双向输入/输出(I/O)口,5个中断优先级2层中断嵌套中断,2个16位可编程定时计数器,2个全双工串行通信口,看门狗(WDT)电路,片内时钟振荡器。 此外,AT89S51设计和配置了振荡频率可为0Hz并可通过软件设置省电模式。空闲模式下,CPU暂停工作,而RAM定时计数器,串行口,外中断系统可继续工作,掉电模式冻结振荡器而保存RAM的数据,停止芯片其它功能直至外中断激活或硬件复位。同时该芯片还具有PDIP、TQFP和PLCC等三种封装形式,以适应不同产品的需求。 图3-9 AT89S51引脚图 主要功能特性: 兼容MCS-51指令系统 32个双向I/O口 2个16位可编程定时/计数器 全双工UART串行中断口线 2个外部中断源 中断唤醒省电模式 看门狗(WDT)电路 灵活的ISP字节和分页编程 4k可反复擦写(>1000次)ISP Flash ROM 4.5-5.5V工作电压 时钟频率0-33MHz 128x8bit内部RAM 低功耗空闲和省电模式 3级加密位 软件设置空闲和省电功能 双数据寄存器指针 (2)管脚说明: VCC:供电电压。 GND:接地。 P0口:P0口为一个8位漏级开路双向I/O口,每脚可吸收8TTL门电流。当P1口的管脚第一次写1时,被定义为高阻输入。P0能够用于外部程序数据存储器,它可以被定义为数据/地址的第八位。在FIASH编程时,P0 口作为原码输入口,当FIASH进行校验时,P0输出原码,此时P0外部必须被拉高。 P1口:P1口是一个内部提供上拉电阻的8位双向I/O口,P1口缓冲器能接收输出4TTL门电流。P1口管脚写入1后,被内部上拉为高,可用作输入,P1口被外部下拉为低电平时,将输出电流,这是由于内部上拉的缘故。在FLASH编程和校验时,P1口作为第八位地址接收。 P2口:P2口为一个内部上拉电阻的8位双向I/O口,P2口缓冲器可接收,输出4个TTL门电流,当P2口被写“1”时,其管脚被内部上拉电阻拉高,且作为输入。并因此作为输入时,P2口的管脚被外部拉低,将输出电流。这是由于内部上拉的缘故。P2口当用于外部程序存储器或16位地址外部数据存储器进行存取时,P2口输出地址的高八位。在给出地址“1”时,它利用内部上拉优势,当对外部八位地址数据存储器进行读写时,P2口输出其特殊功能寄存器的内容。P2口在FLASH编程和校验时接收高八位地址信号和控制信号。 P3口:P3口管脚是8个带内部上拉电阻的双向I/O口,可接收输出4个TTL门电流。当P3口写入“1”后,它们被内部上拉为高电平,并用作输入。作为输入,由于外部下拉为低电平,P3口将输出电流(ILL)这是由于上拉的缘故。P3口也可作为AT89C51的一些特殊功能口,如下表所示: 口管脚 备选功能 P3.0 RXD(串行输入口) P3.1 TXD(串行输出口) P3.2 /INT0(外部中断0) P3.3 /INT1(外部中断1) P3.4 T0(记时器0外部输入) P3.5 T1(记时器1外部输入) P3.6 /WR(外部数据存储器写选通) P3.7 /RD(外部数据存储器读选通) P3口同时为闪烁编程和编程校验接收一些控制信号。 RST:复位输入。当振荡器复位器件时,要保持RST脚两个机器周期的高电平时间。 ALE/PROG:当访问外部存储器时,地址锁存允许的输出电平用于锁存地址的地位字节。在FLASH编程期间,此引脚用于输入编程脉冲。在平时,ALE端以不变的频率周期输出正脉冲信号,此频率为振荡器频率的1/6。因此它可用作对外部输出的脉冲或用于定时目的。然而要注意的是:每当用作外部数据存储器时,将跳过一个ALE脉冲。如想禁止ALE的输出可在SFR8EH地址上置0。此时, ALE只有在执行MOVX,MOVC指令是ALE才起作用。另外,该引脚被略微拉高。如果微处理器在外部执行状态ALE禁止,置位无效。 /PSEN:外部程序存储器的选通信号。在由外部程序存储器取指期间,每个机器周期两次/PSEN有效。但在访问外部数据存储器时,这两次有效的/PSEN信号将不出现。 /EA/VPP:当/EA保持低电平时,则在此期间外部程序存储器(0000H-FFFFH),不管是否有内部程序存储器。注意加密方式1时,/EA将内部锁定为RESET;当/EA端保持高电平时,此间内部程序存储器。在FLASH编程期间,此引脚也用于施加12V编程电源(VPP)。 XTAL1:反向振荡放大器的输入及内部时钟工作电路的输入。 XTAL2:来自反向振荡器的输出。 (3)振荡器特性 XTAL1和XTAL2分别为反向放大器的输入和输出。该反向放大器可以配置为片内振荡器。石晶振荡和陶瓷振荡均可采用。如采用外部时钟源驱动器件,XTAL2应不接。有余输入至内部时钟信号要通过一个二分频触发器,因此对外部时钟信号的脉宽无任何要求,但必须保证脉冲的高低电平要求的宽度。 (4)芯片擦除 整个PEROM阵列和三个锁定位的电擦除可通过正确的控制信号组合,并保持ALE管脚处于低电平10ms 来完成。在芯片擦操作中,代码阵列全被写“1”且在任何非空存储字节被重复编程以前,该操作必须被执行。 此外,AT89C51设有稳态逻辑,可以在低到零频率的条件下静态逻辑,支持两种软件可选的掉电模式。在闲置模式下,CPU停止工作。但RAM,定时器,计数器,串口和中断系统仍在工作。在掉电模式下,保存RAM的内容并且冻结振荡器,禁止所用其他芯片功能,直到下一个硬件复位为止。 3.3.3 DAC0832芯片介绍 (1)芯片简介。 DAC0832是NSC公司(美国国家半导体公司)生产的8位DAC芯片,可直接与多种CPU总线连接而不必增加任何附加逻辑。DAC0830、DAC0831与它兼容,可以完全相互代换。 DAC0832由两级数据缓冲器和D/A转换器组成,第一级数据缓冲器称为输入寄存器,第二级称为DAC寄存器,如(图3-10)所示。 图3-10 DAC0832内部结构 DAC0832各引脚的定义如下: ?D7~D0——8位数据输入端。 ?ILE——允许输入锁存(输入),高电平有效。 ?——片选(输入),低电平有效。 CS ?——写信号1(输入),低电平有效。ILE、、为输入寄存器的选通CSWRWR11信号,当ILE、、全部有效,LE1为高电平时,选通输入寄存器,数据CSWR1 总线D7~D0上的输入数据进入输入寄存器;当ILE、CS、不同时有效,LE1WR1为低电平时,输入寄存器中原有数据被锁存。 ?——传送控制信号(输入),低电平有效。 XFER ?——写信号2(输入),输入低电平有效。、为DAC寄存器的选XFERWRWR22通信号,当、同时有效时,LE2为高电平,选通DAC寄存器,输入寄XFERWR2 存器中锁存的数据进入DAC寄存器;、不同时有效时,LE2为低电平,XFERWR2DAC寄存器中原有数据被锁存。 ?I——模拟电流输出端1,它是逻辑电平为1的各位输出电流之和。 OUT1 ?I——模拟电流输出端2,它是逻辑电平为0的各位输出电流之和。OUT2 II+=常数。 OUT1OUT1 ?——基准电压输入,+10V ~ -10V,此电压越稳定模拟输出精度越高。 VREF RI?——反馈电阻引出端,DAC0832内部此端与端之间已集成一反馈内阻fbOUT1RR,其值为15KΩ,所以可以直接接到外部运算放大器的输出端。 fbfb V?——电源电压,+5V ~ +15V。 CC ?AGND——模拟地,芯片模拟信号接地点。 ?DGND——数字地,芯片数字信号接地点。 必须注意的是:在使用DAC芯片和ADC芯片的电路中,必须正确处理地线和连 接问题。电路中有两种芯片:一种是模拟电路芯片,如DAC、ADC、运算放大器等; 另一种是数字电路芯片,如CPU、译码器、寄存器等。这两种芯片应由两个独立 的电源分别供电。模拟地线和数字地线应该分开、模拟地和数字地应分别连接到 系统的模拟地线和数字地线。在整个系统中仅有一个共地点,避免造成回路,防止数字信号通过数字地线干扰微弱的模拟信号。 (2)DAC0832的工作方式。 DAC0832有两级锁存器,因此有3种工作方式:双缓冲工作方式、单缓冲工作方式和直通工作方式。 双缓冲工作方式,就是把DAC0832的输入寄存器和DAC寄存器都接成受控方式。CPU对DAC芯片的写操作分两步进行:第一步把数据写进输入寄存器,第二步把输入寄存器的内容写入DAC寄存器。双缓冲工作方式的优点是DAC0832的数据接收和启动转换异步进 行。于是,可在D/A转换的同时进行下一数据的接收,可实现多个转换器的同时输出。 单缓冲工作方式,就是使两个寄存器中一个处于直通状态,而另一个处于受控状态。这种方式可以减少一条输出指令,在不要求多个DAC同时进行D/A转换时普遍采用此种方式。 当、、和引脚全部接数字地,ILE引脚为高电平时,芯片CSXFERWRWR12 位数字量一旦达到D7~D0输入端,便立即进行D/A转换。就处于直通工作状态。8 此种方式下,DAC0832不能直接和CPU的数据总线相连,故很少采用。由于本设计要求数据一到立即转换,因此,采用了此种工作方式。 (3)DAC0832的输出方式。 DAC0832为电流输出型D/A转换器,需要外接运算放大器进行电流电压变换才能得到模拟电压输出。输出方式有两种:单极性输出和双极性输出。 单极性电压输出方式输出的电压极性是单一的,而双极性输出方式输出的电压极性是可变的,即有正有负。单极性输出时,在运算放大器的输出端就可以得 V到单极性模拟电压: OUT V,,IROUTOUTfb (3-7) V若参考电压为+5V,则当数字量从00H~FFH变化时,对应的模拟电压输出范围OUT是0V ~ -5V。 V双极性电压输出方式需要两级运算放大器级联输出电压,输出电压与OUT VV及第一级运算放大器输出的关系是: OUT1REF VVV = —(2+) (3-8) OUTOUT1REF V这时,当数字量从00H ~ FFH变化时,对应的模拟电压输出范围是 —5V ~ +5V,OUT 显然,其分辨率较单极性输出降低一倍。 根据设计目标需要,本设计采用单极性电压输出方式。 3.3.4其他的主要芯片 两片DAC0832的电流-电压转换都采用了低温漂电压的运算放大器op07, OP07是一种高精度单片运算放大器,具有很低的输入失调电压和漂移。OP07的优良特性使它特别适合作前级放大器,放大微弱信号。使用OP07一般不用考虑调零和频率问题就能满足要求。 主要特点: ? 低输入失调电压:75uV(最大) ? 低失调电压温漂:1.3uV/?(最大) ? 低失调电压时漂:1.5uV/月(最大) ? 低噪声:0.6uV P-P(最大) ? 宽输入电压范围:?14V ? 宽电源电压范围:3V,18V 4 FPGA部分VHDL仿真及说明 FPGA部分电路是整个电路设计系统中最主要的部分,所以其能否正常工作对整个系统的工作起关键作用。FPGA部分仿真主要是观察波形数据的值的输出是否正确和值输出的频率是否和预置的频率控制字相对应,在MAX+plus II的仿真文件.scf里对FPGA部分的电路进行了验证。由于外部有源晶振的频率为4.096MHz,故将.scf的option里的Grid size设置为244.1ns,即周期差不多是4.096MHz。运行仿真,得到的结果跟理论的结果几乎一样,因此,可以确定整个FPGA部分电路可以正常运行。仿真部分波形如(图4-1)所示。 图4-1 DDS部分电路的仿真结果 从图8的波形仿真结果中很难直观地看出DDS输出正弦波的情况。为了便于调试设计电路,我们可以利用计算机高级语言将MAX + plus II波形仿真结果转换为波形曲线,这就需要借助于MAX + plus II的 表格 关于规范使用各类表格的通知入职表格免费下载关于主播时间做一个表格详细英语字母大小写表格下载简历表格模板下载 文件(.tbl文件)。 MAX + plus II的设计软件中的.tbl文件是纯文本文件,它包含了.scf文件或.wdf文件中的所有信息。.tbl文件的生成很简单,在MAX + plus II波形仿真结束后,打开.scf文件,然后从“File”菜单中选择“Create Table File”选项,就可产生.tbl文件。.tbl文件的基本结构可分为四大部分,其中第三部分和第四部分是最关键的,可从中获取仿真波形数据,并利用计算机高级语言处理这 些数据,将其转换为直观的波形曲线。需要注意的是,每做一次MAX + plus II波形仿真,都要重新生成一次.tbl文件,以更新.tbl文件内的数据。经过 MaxplusII的综合与仿真,结果表明,能够实现多波形发生器的各个波形功能及不同精度要求。波形仿真后,在软件中观察.scf文件可以得到输出波形的数字幅度序列数据。为了直观的观察到输出波形,仍然借助Matlab工具,将仿真结果转换为波形曲线。在MaxplusIl中将仿真结果生成表格文件(.tb l文件)。在Matlab工具中,通过编写的一段m文件程序,读取表格文件中的相应数据信息,绘出电路的仿真波形曲线。以下给出具体的m文件(假设tbl文件的位置在 d:\dmax2work\下): clear all ; fid =fopen('d:\max2work\example.tbl','r'); data= fscanf(fid,‘%s'); fclose (fid); b= find( data= =‘=’); number := length(b); j= 0 ; for i =1: number if data(b(i)-1)= =’0' j= j+ 1; out_hex( j,l )= data( b( i) +1 ); out_hex( j,2)= data( b( i) +2 ); out_hex( j,3)= data( b( i) +3 ); end end out_dec= hex2dec(out_hex); plot(out _hec); (图4-2)是用这一程序绘出的电路仿真波形曲线(仍以输出正弦波为例)。 图4-2 仿真波形 5 工程设计 EDA是电子设计自动化(Electronic Design Automation)的缩写,是90年代初,从CAD(计算机辅助设计)、CAM(计算机辅助制造)、CAT(计算机辅助测试)和CAE(计算机辅助工程)的概念发展而来的。本设计主要使用了两个EDA工具,MAX + plus II和Protel 99SE,以下就分别对这两个工具在本设计中的应用进行简单的说明。 5.1 MAX + plus II的使用以及VHDL程序的编写 FPGA部分电路的VHDL程序主要是依靠MAX + plus II来处理。 VHDL的英文全名是Very-High-Speed Integrated Circuit Hardware Description Language,诞生与1982年。1987年底,VHDL被IEEE(The Institute of Electrical and Electronics Engineers)和美国国防部确认为标准硬件描述语言。自IEEE公布了VHDL的标准版本(IEEE-1076)以后,各EDA公司相继推出了自己的VHDL设计环境,或宣布自己的设计工具可以和VHDL接口。此后VHDL在电子设计领域得到了广泛的接受,并逐步取代了原有的非标准硬件描述语言。1993年,IEEE对VHDL进行了修订,从更高的抽象层次和系统描述能力上扩展VHDL的内容,公布了新版本的VHDL,即IEEE标准的1076-1993版本。现在,VHDL和Verilog语言将承担起几乎全部的数字系统设计任务。 VHDL主要用于描述数字系统的结构、行为、功能和接口。除了含有许多具有硬件特征的语句外,VHDL的语言形式和描述风格与句法十分类似于一般的计算机高级语言。VHDL的程序结构特点是将一项工程设计,或称设计实体(可以是一个元件、一个电路模体的内部功能和算法完成部分。在对一个设计实体定义了外部界面后,一旦其内部开发完成后,其它的设计就可以直接调用这个实体。这种将 设计实体部分分成内外部分的概念是VHDL系统设计的基本点。应用VHDL进行工程设计的优点是多方面的,具体如下: 5.1.1 VHDL进行工程设计的优点 (1)与其它的硬件描述语言相比,VHDL具有更强的行为描述能力,从而决定了它成为系统设计领域最佳的硬件描述语言。强大的行为描述能力是避开具体的器件结构,从逻辑行为上描述和设计大规模电子系统的重要保证。就目前流行的EDA工具和VHDL综合器而言,将基于抽象的行为描述风格的VHDL程序综合成为具体的FPGA和CPLD等目标器件的网表文件已不成问题,只是在综合与优化效率上略有差异。 (2)VHDL丰富的仿真语句和库函数,使得在任何大系统的设计早期(即尚未完成),就能查验设计系统的功能可行性,随时可对设计进行仿真模拟。即在远离门级的高层次上进行模拟,使设计者对整个工程设计的结构和功能的可行性作出决策。 (3)VHDL语句的行为描述能力和程序结构决定了它具有支持大规模设计的分解和已有设计的再利用功能。符合市场需求的大规模系统高效、高速的完成必须由多人甚至多个开发组共同并行工作才能实现。VHDL中设计实体的概念、程序包的概念、设计库的概念为设计的分解和并行工作提供了有力的支持。 (4)对于用VHDL完成的一个确定的设计,可以利用EDA工具进行逻辑综合和优化,并自动地把VHDL描述设计转变成门级网表(根据不同的实现芯片)。这种方式突破了门级设计的瓶颈,极大地减少了电路设计的时间和可能发生的错误,降低了开发成本。应用EDA工具的逻辑优化功能,可以自动地把一个综合后的设计变成一个更小、更高速的电路系统。反过来,设计者还可以容易地从综合和优化后的电路获得设计信息,反回去更新修改VHDL设计描述,使之更为完善。 (5)VHDL对设计的描述具有相对独立性,设计者可以不懂硬件的结构,也不必管最终设计实现的目标器件是什么,而进行独立的设计。正因为VHDL的硬件描述与具体的工艺技术和硬件结构无关,VHDL设计程序的硬件实现目标器件有广阔的选择范围,其中包括各系列的CPLD、FPGA及各种门阵列器件。 (6)由于VHDL具有类属描述语句和子程序调用等功能,对于已完成的设计,在不改变源程序的条件下,只需改变类属参量或函数,就能轻易地改变设计的规模和结构。 MAX+plus II界面友好,使用便捷,被誉为业界最易用易学的EDA软件。MAX+plus II支持原理图、AHDL、VHDL和Verilog语言文本文件、以及波形与EDIF等格式的文件作为设计输出,并支持这些文件的任意混合设计。MAX+plus II具有门级仿真器,可以进行功能仿真和时序仿真,能够产生精确的仿真结果。在适配之后, MAX+plus II生成供时序仿真用的EDIF、VHDL和Verilog三种不同格式的网表文件。 5.1.2 MAX+plus II的过程 MAX+plus II支持主流的第三方EDA工具,如Synopsys、Cadence、Synplicity、 Exemplar和Model Technology等。 Mentor、Viewlogic、 MAX+plus II支持除APEX20K系列之外的所有Altera FPGA/CPLD大规模逻辑器件。 本设计使用Altera公司的EDA软件MAX+plus II的过程大概如下所述。 本设计分二个模块,即freqdiv80.vhd和TOP.GDF。其中freqdiv80.vhd是用VHDL编写的,TOP.GDF则是原理图,是本例以原理图方式表示的顶层设计文件。图中将是用freqdiv80.vhd作为元件进行调用。freqdiv160.vhd是一个80分频器;顶层文件TOP.GDF则将freqdiv80.vhd、MAX+plus II自带的LPM_ADD_SUB、LPM_DFF以及LPM_ROM模块组装起来构成一完整的设计实体。 首先为工程建立一目录,然后进入MAX+plus II集成环境。 (1)创建源程序freqdiv80.vhd 选择菜单“File”?“New„” ,出现一个对话框,在框中选中“Text Editor file”,按“OK”按钮,即选中了文本编辑方式。在出现的“Untitled-Text Editor”文本编辑窗口中输入程序。 输入完毕后,选择菜单“File?Save”,即出现如图12所示的对话框。首先在“Directories”目录中选择存放本文件的目录,然后在“File Name”框中输入文件名freqdiv80.vhd,然后按“OK”按钮,即把输入的文件放在“Directories”目录中了。 文件的后缀名将决定使用的语言形式,在MAX+plus II中,后缀为.VHD表示VHDL文件;后缀为.TDF表示AHDL文件;后缀为.V表示Verilog文件。 文件存盘后,为了能在图形编辑器中调用freqdiv160,需要为freqdiv160创建一个元件图形符号。选择菜单“File”?“Creat Default Symbol”,MAX+plus II出现一个对话框,询问是否将当前工程设为freqdiv80,可按下“确定”按钮。这时MAX+plus II调出编译器对freqdiv80.vhd进行编译,编译后生成freqdiv80的图形符号。如果源程序有错,要对源程序进行修改,重复上面的步骤,知道此元件符号创建成功。成功后出现元件符号创建成功的对话框。退出编译器,再退出编译器,回到主窗口。 (2)创建源文件TOP.GDF TOP.GDF是本项示例的最顶层的图形设计文件,调用了前面创建的功能文件,将Freqdiv80.vhd、LPM_ADD_SUB、LPM_DFF以及LPM_ROM几个模块组装起 来,成为一个完整的设计。选择菜单File?New,在对话框中选择“Graphic Editor File”,按“OK”按钮,即出现图形编辑器窗口Graphic Editor。现按照以下给出的方法在“Graphic Editor” 中绘出原理图。 ?往图中添加元件 将freqdiv80.vhd、LPM_ADD_SUB、LPM_DFF以及LPM_ROM调入原理图编辑器。排好它们的位置。并设置好LPM_ADD_SUB、LPM_DFF以及LPM_ROM这几个模块的参数。输入输出接口符号名为“INPUT”和“OUTPUT”。 ?在符号之间进行连线 先放好输入/输出元件符号,再将鼠标箭头移到符号的输入/输出引脚上,鼠标箭头形状会变成“+”字型,然后可以按着鼠标左键并拖动鼠标,绘出一条线,送开鼠标键完成一次操作。细线表示单根线,粗线表示总线,它的根数可从元件符号的标示上看得 出。 ?设置输入/输出引脚名 在INPUT或OUTPUT符号的引脚上双击鼠标左键,可以在端口上输入新的引脚名。TOP.GDF中有两个输入引脚“CLK”和“FREQ[7..0]”及8位总线输出引脚“Q[7..0]”,分别输入端口符号。FREQ[7..0]在VHDL中是一个数组,表示由信号FREQ7~FREQ0组成的总线信号。实际上这里有8个输入引脚。Q[7..0]也是同样的道理。最后选择File?Save菜单,将此顶层原理图文件取名为TOP.GDF,并写入“File Name”中,存入同一目录中。 ?编译TOP.GDF 在编译TOP.GDF之前,需要设置此文件为顶层文件(最上层文件),或称工程文件:Project。选择菜单File?Project?Set Project to Current File,当前的工程即被设为TOP。然后选择用于编程的目标芯片和确定引脚。全部设定结束后,按“OK”键。最后开始编译和综合。选择“MAX+plus II”?“Compiler”菜单,可运行编译器。最后在Compiler窗口中按下“Start”按钮,启动编译过程,直到编译结束。如果源程序有错误,用鼠标双击红色的错误信息即可返回图形或文本编辑器进行修改,然后再次编译,直到通过。 ?仿真顶层设计TOP MAX+plus II支持功能仿真和时序仿真两种仿真形式。功能仿真用于大型设计编译适配之前的仿真,而时序仿真则是在编译适配生成时序信息文件之后进行的仿真。仿真首先要建立波形文件。将所有的信号全部选中到波形编辑器中。设置好各信号后,按下“Simulator”子窗口中的“Start”按钮,即刻进行仿真运算。 波形观察窗左排按钮是用于设置输入信号的,十分方便。使用时先用鼠标在 输入波形上拖一需要改变的黑色区域,然后点左排按钮,其中“0”、“1”、“X”、“Z”、“INV”、 “G”分别表示低电平、高电平、任意、高阻态、反相和总线数据设置。 ? 将设计文件TOP编程下载到芯片中去 选择“MAX+plus II” ?“Programmer”菜单,可调出编程器(Programmer)窗口。在将设计文件编程配置进硬件芯片前,需连接好硬件测试系统。本设计使用FLEX10K系列的10K10器件,一切连接就绪后,方可按下编程器窗口中的“Configure”按钮,若一切无误,即可将所设计的内容下载到10K10芯片中。下载成功后将在一弹出的小窗中显示“Configure Complete”。 如果在安装MAX+plus II软件之后第一次调用编程器子窗口,则MAX+plus II将弹出对话框选择编程器型号,以便调用正确的编程器驱动程序。如果用FLEX或ISP型MAX系列器件,通常选择“ByteBlaster”编程器。“ByteBlaster”实际上是指连接在并行打印口使用的下载电缆。编程器型号的选择方法是启动“Programmer”,选菜单“Options” ?“Hardware Setup”,在Hardware Type下拉栏中选ByteBlaster,按“OK”即可。 5.2 Protel 99SE的使用 在进行电路板及PCB设计时,采用的是Protel 99SE软件。这是一个目前被普遍使用的软件,在介绍具体的电路板及PCB设计前,有必要对这个软件进行一定的简介。 电路及PCB设计是EDA技术中的一个重要内容,而EDA技术是现代电子工程领域的一门新技术,它提供了基于计算机和信息技术的电路系统设计方法。Protel是其中比较杰出的一个软件,在国内流行最早、应用面最宽。其最新版的Protel 99SE较以前的Protel功能更为强大,它是桌面环境下以设计管理和协作技术(PDM)为核心的一个优秀的印刷电路板设计系统。它在先前的Protel的基础上新增加了3项技术,它们分别是SmarTDoc、SmartTeam和SmartTool,这些功能增加了人与工具之间的交互功能。Protel 99SE软件包主要包含以下几个模块:原理图设计软件Protel Advanced Schimatic 99SE、电路板设计软件Protel Advanced PCB 99SE、用于PCB自动布线的Protel Adcanced Route 99SE、可编程逻辑器件设计的Protel Advanced PLD99SE、用于电路仿真的Advanced SIM99和信号完整性分析的Advanced Integrity99,可谓是功能齐全。 上面简单介绍了Protel 99SE的情况,下面就简述设计方法过程。 首先构思电路模块。在着手绘制具体的电路之前,将本设计的整个系统进行了模块化设计。将设计的整个系统分为二个模块。它们分别是D/A、幅度控制以及滤波输出这个主模块和FPGA程序加载模块。分别对这二个模块绘制电路图, 最后通过长脚插针,将FPGA程序加载模块插在主模块上。 接下来就是设计电路原理图。在这部分是利用Protel 99SE的原理图设计系统绘制我所需要的电路原理图。在这一过程中,要充分利用各种原理图绘制工具和编辑功能。在完成原理图设计后,利用Tools中的ERC命令,检查绘制原理图时是否出现一些简单 的基本错误,如连线错误、标号错误等。 接下来产生SPICE netlist。这是电路原理图设计(Sch)与印制电路板设计(PCB)之间的一座桥梁,是电路板自动布线的核心。 设计印制板的过程是整个设计中的核心部分,如果印制板没有设计好,将会影响整个后期的调试,甚至会影响电路板的效果。下面就简单介绍设计印制电路板时可能会出现的一些问题以及解决方法。 在将报表网络调入时,常常会出现一种情况:在原理图中相连的两个元件被调入后并没有连在一起。解决的方法是在PCB图中进行手动连接。 在PCB的库文件中,常常没有适合实际中的一些电器件。比如四脚开关、拨码开关等;还有些场合下,库中已经存在的元件,其引脚的排列顺序不符合图纸的要求,必须修改。解决这两种情况的办法都是利用Protel的编辑功能和修改功能。 在布板时,有手动布板和自动布板两种方法。这两种方法各有各的的优点,在时间允许的情况下推荐采用手动布板。这里有几个原因,一是自动布板不利于进行电路检查;二是自动布板比手动布板复杂,它会出现一些重复和不合实际情况的布线。比如出现直角会对高频电路产生影响,而折线对高频电路的影响将会小的多;另外地线应该较其它线条粗,可以防止干扰;信号线应该短一些,这样也可以防止信号之间的串扰,避免主要信号中串入太多干扰信号。 最后在附录里附上电路的PCB图。 6 制作工艺 PCB设计的一般原则要使电子电路获得最佳性能,元器件的布且及导线的布设是很重要的。本设计由于频率不高,因此对电路板的工艺以及布线的要求不高,但是需要注意的是,信号最好不要拉得太长,以免引入太多干扰信号,影响波形的效果。为了设计质量好、造价低的PCB。应遵循以下一般原则: (1)布局 首先,要考虑PCB尺寸大小。PCB尺寸过大时,印制线条长,阻抗增加,抗噪声能力下降,成本也增加;过小,则散热不好,且邻近线条易受干扰。在确定PCB尺寸后(再确定特殊元件的位置。最后,根据电路的功能单元,对电路的全部元器件进行布局。 在确定特殊元件的位置时要遵守以下原则: ?尽可能缩短高频元器件之间的连线,设法减少它们的分布参数和相互间的电磁干扰。易受干扰的元器件不能相互挨得太近,输入和输出元件应尽量远离。 ?某些元器件或导线之间可能有较高的电位差,应加大它们之间的距离,以免放电引出意外短路。带高电压的元器件应尽量布置在调试时手不易触及的地方。 ?重量超过15g的元器件、应当用支架加以固定,然后焊接。那些又大又重、发热量多的元器件,不宜装在印制板上,而应装在整机的机箱底板上,且应考虑散热问题。 热敏元件应远离发热元件。 ?对于电位器、可调电感线圈、可变电容器、微动开关等可调元件的布局应考虑整机的结构要求。若是机内调节,应放在印制板上方便于调节的地方;若是机外调节,其位置要与调节旋钮在机箱面板上的位置相适应。 ?应留出印制扳定位孔及固定支架所占用的位置。 根据电路的功能单元(对电路的全部元器件进行布局时,要符合以下原则: ?按照电路的流程安排各个功能电路单元的位置,使布局便于信号流通,并使信号尽可能保持一致的方向。 ?以每个功能电路的核心元件为中心,围绕它来进行布局。元器件应均匀、 整齐、紧凑地排列在PCB上(尽量减少和缩短各元器件之间的引线和连接。 ?在高频下工作的电路,要考虑元器件之间的分布参数。一般电路应尽可能使元器件平行排列。这样,不但美观(而且装焊容易(易于批量生产。 ?位于电路板边缘的元器件,离电路板边缘一般不小于2mm。电路板的最佳形状为矩形。长宽比为3:2成4:3。电路板面尺寸大于200x150mm时(应考虑电路板所受的机械强度。 (2)布线 布线的原则如下: ?输入输出端用的导线应尽量避免相邻平行。最好加线间地线,以免发生反馈藕合。 ?印制摄导线的最小宽度主要由导线与绝缘基扳间的粘附强度和流过它们的电流值决定。当铜箔厚度为 0.05mm、宽度为 1~15mm 时(通过 2A的电流,温度不会高于3?,因此(导线宽度为1.5mm可满足要求。对于集成电路,尤其是数字电路,通常选0.02~0.3mm导线宽度。当然,只要允许,还是尽可能用宽线(尤其是电源线和地线。导线的最小间距主要由最坏情况下的线间绝缘电阻和击穿电压决定。对于集成电路,尤其是数字电路,只要工艺允许,可使间距小至 5~8mm。 ?印制导线拐弯处一般取圆弧形,而直角或夹角在高频电路中会影响电气性能。此外,尽量避免使用大面积铜箔,否则(长时间受热时,易发生铜箔膨胀和脱落现象。必须用大面积铜箔时,最好用栅格状(这样有利于排除铜箔与基板间粘合剂受热产生的挥发性气体。 (3)焊盘 焊盘中心孔要比器件引线直径稍大一些。焊盘太大易形成虚焊。焊盘外径D一般不小于(d+1.2)mm,其中d为引线孔径。对高密度的数字电路,焊盘最小直径可取(d+1.0)mm。 此外,还应注意以下两点: ?在印制板中有接触器、继电器、按钮等元件时(操作它们时均会产生较大火花放电,必须采用RC电路来吸收放电电流。一般 R 取 1~2K,C取2.2~47uF。 ?CMOS的输入阻抗很高,且易受感应,因此在使用时对不用端要接地或接正电源。 7 调试测试以及对结果的分析 根据方案设计的要求,测试过程共分三大部分:硬件调试、软件调试和软硬件联调。 电路按模块调试,各模块逐个调试通过后再联调。FPGA部分的VHDL程序先在GW48 EDA实验开发系统上测试通过,再与硬件系统联调。 7.1 测试仪器 SS-7802A双踪示波器 SS1792可跟踪直流稳压电源 5V电源 UT2004数字万用表 7.2 系统指标测试 测试数据如下表: 表7-1 输出波形频率测试 预置频率/Hz 实际输出频率/Hz 1 0.98 10 9.76 100 97.55 1000 975.54 10000 9755.44 20000 19510.89 30000 29266.32 由表可以看出,在频率稳定度方面,输出频率稳定度和晶振稳定度在同一个 数量级。 7.3 测试结果分析 (1)本设计的输出在较高频率时由于运算放大器、D/A的带宽限制,输出幅度有所下降,频率稳定度不够好。 (2)本设计对波形一个周期存放512个数据点,有效防止了奈奎斯特混叠,故输出波形失真较小。 8 总结 本设计研究了有关利用FPGA实现DDS的方法,主要内容如下: (1)通过对有关DDS现有技术的大量文献的调研,打下了设计DDS的理论基础,分析了影响DDS性能的主要因素。 (2)根据理论研究,确定了DDS的设计目标,以Altera MAX+plus II上的宏函数LPM_ADD_SUB和LPM_DFF进行了累加器的VHDL设计,得到了预期的结果。 (3)以Altera MAX+plus II上的宏函数LPM_ROM为存储单元设计出了用于DDS的相位-幅度变换器。 (4)该系统利用MAX+plus II软件在Altera FLEX10K10芯片上进行了逻辑综合、仿真和硬件实现,证实了设计的正确性和可行性。 本设计的不足之处及以后的研究方向: 本设计中没有深入地分析了DDS的主要误差来源和输出频谱之间的关系。而且,没有能够利用高级语言来模拟DDS波形的输出。最大的缺陷是由于准备不充分和元件的缺 少,不能够将系统的频率做得很高,频率分辨率比较低,有关DDS的理论研究和工程技术可以说已经成熟了,但是正确地去理解现有的理论和技术而且导入到实践上也是个大的课题。 致 谢 本次课程设计之所以能够顺利完成,首先要感谢四年来精心授课的各位老师,没有他们的辛勤施教,就不会有我现在的成功。与此同时,我要特别感谢我的指导老师姜天松老师和邓德迎老师,是他的细心指导使我顺利地完成了课程设计,并取得了良好的成绩。其次,我要感谢各种参考资料的编写者,正是他们的智慧结晶使我对电子世界有了更多更深的了解。我还要感谢一起做课程设计的同学们,他们的关心给了我很大的帮助。最后,我想再次对所有给予我帮助的老师和同学表示衷心的感谢~ 参考文献 [1] J.Tierey,C.Rader,and B.Gold.A Digital Frequency Synthesizer.IEEE Trans.Audio and Electroacoust.VoLAU-19, 1971,48:57. [2] J.Vankka,M.Waltari et al.Direct Digital Synthesizer with on-Chip D/A-Converter.IEEE J.of SCC,VoL33,1998,218:227. [3] 潘松,王国栋.VHDL实用教程[M].成都:电子科技大学出版社,2001:1~330. [4] 褚振勇,翁木云.FPGA设计及应用[M].西安:西安电子科技大学出版社,2003:288~293. [5] 全国大学生电子设计竞赛组委会. 第五届全国大学生电子设计竞赛获奖作品选编[M]. 北京理工大学出版社,2003:2~48. [6] 潘松.VHDL技术实用教程[M].北京:科学出版社,2002年. [7] 边计年.VHDL设计电子电路[M].北京:清华大学出版社,2000年. [8] 林名权.VHDL数字控制系统设计范例[M].北京:电子工业出版社,2003年. [9] 徐彬,谭征,袁蕾等.基于DDS技术的任意波形发生器[J].电子世界,2001,1:58~60. 附 录 附录1:电路图 附录2:PCB图 附录3 原理图 附录4:仿真波形 其中CLK是时钟 OUT[7..0]是输出的波形的数据 SEL[1..0]是波形选择 DATA[7..0]是频率输入 方波 三角波 正弦波 附录5 单片机源程序: (DDS) #include #include"lcd.h" uchar code table1[]={"FREQ:"}; uchar code table2[]={"WAVE;"}; uchar code table3[]={"Hz"}; uchar code table4[]={"fang bo"}; uchar code table5[]={"zhengxianbo"}; uchar code table6[]={"jc"}; uchar code table7[]={"san jiao bo"}; uchar table10[8]; uchar state=1; uint n=0; uchar count=1; uint th0; uint tl0; uint frequency; uint amplitude; bit FLAG=0; /*/////////////////////////////////////////////////////////////////// //// 函数名: ACSII转换函数 功能: 返回值: 无 注意: 无 ///////////////////////////////////////////////////////////////////// /*/ asc_change(int f) { table10[6]=0x00; table10[5]=0x20; table10[4]=f%10+0x30; //table10[4]=f/10%10+0x30; table10[3]=f%100/10+0x30; table10[2]=f%1000/100+0x30; table10[1]=f%10000/1000+0x30; table10[0]=f/10000+0x30; } /*/////////////////////////////////////////////////////////////////// //// 函数名: 数字选择函数 功能: 返回值: 无 注意: count对应的是频率设置还是幅度设置 ///////////////////////////////////////////////////////////////////// /*/ select(uchar x) {while((P1&0xf0)!=0xf0); if(n<50000) {n=n*10+x; asc_change( n); frequency=n; lcd_dis(table10,0x86);} } /*/////////////////////////////////////////////////////////////////// //// 函数名: 波形选择显示 功能: 返回值: 无 注意: state是指当前对应的是什么波形 ///////////////////////////////////////////////////////////////////// /*/ wave(uchar t) {while((P1&0xf0)!=0xf0); if(t==1) lcd_dis(table5,0xc5); //正弦波 else if(t==2) lcd_dis(table7,0xc5); //三角波 else if(t==3) lcd_dis(table4,0xc5); //方波 state=t;//状态为t } /*/////////////////////////////////////////////////////////////////// //// 函数名: 确定函数 功能: 返回值: 无 注意: 无 ///////////////////////////////////////////////////////////////////// /*/ enter() { while((P1&0xf0)!=0xf0); P0=frequency%256; P2=frequency/256; } /*/////////////////////////////////////////////////////////////////// //// 函数名: 频率设置函数 功能: 返回值: 无 注意: ///////////////////////////////////////////////////////////////////// /*/ set() {P0=0; P2=0; n=0; asc_change(n); lcd_dis(table10,0x86); } /*/////////////////////////////////////////////////////////////////// //// 函数名: 键盘扫描函数 功能: 返回值: 无 注意: 返回具体键位地址 ///////////////////////////////////////////////////////////////////// /*/ uchar key(void) //键盘扫描 {uchar sccode,recode; //P1=0xf0; if((P1&0xf0)!=0xf0) {delay(10); if((P1&0xf0)!=0xf0) {delay(10); if((P1&0xf0)==0xf0); sccode=0xfe; while((sccode&0x10)!=0) {P1=sccode; if((P1&0xf0)!=0xf0) {recode=(P1&0xf0)|0x0f; P1=0xf0; return((~sccode)+(~recode)); } else sccode=(sccode<<1)|0x01; } } } P1=0xf0; return(0); } /*/////////////////////////////////////////////////////////////////// //// 函数名: 键位对应函数 功能: 返回值: 无 注意: 无 ///////////////////////////////////////////////////////////////////// /*/ sweep() {uchar i; i=key(); { switch(i) { case 0x11:TR0=0;select(1);break; case 0x21:TR0=0;select(2);break; case 0x41:TR0=0;select(3);break; case 0x81:TR0=0;wave(1); break; //方波 case 0x12:TR0=0;select(4);break; case 0x22:TR0=0;select(5);break; case 0x42:TR0=0;select(6);break; case 0x82:TR0=0;wave(2); break; //正弦波 case 0x14:TR0=0;select(7);break; case 0x24:TR0=0;select(8);break; case 0x44:TR0=0;select(9);break; case 0x84:TR0=0;wave(3); break; case 0x18:TR0=0;enter(); break; //确定 case 0x28:TR0=0;select(0);break; case 0x48:TR0=0;set(); break; //case 0x88:TR0=0;wave(3); break; } }} main() { P0=0x00; P2=0X00; lcdinit(); delay(3); lcd_dis(table1,0x80); lcd_dis(table2,0xc0); lcd_dis(table3,0x08d); wave(1); //默认是正弦波 while(1) { if(state==1)//正弦波 { if(P1!=0xf0) sweep(); P3_6=1; P3_7=1; } if(state==2)//三角 { if(P1!=0xf0) sweep(); P3_6=0; P3_7=1; } if(state==3)//方波 { if(P1!=0xf0) sweep(); P3_6=1; P3_7=0; } }} (LCD) #include"lcd.h" /*/////////////////////////////////////////////////////////////////// //// 函数名: 液晶数据发送delay 功能: 返回值: 注意: 无 ///////////////////////////////////////////////////////////////////// /*/ delay(uchar n) {uchar f,m; for(m=n;m>0;m--) //for(n=15;n>0;n++) for(f=248;f>0;f--) {} } /*/////////////////////////////////////////////////////////////////// //// 函数名: 模拟串行数据发送函数 功能: 将要发送的数据转成串行数据 返回值: 注意: 这里使用164芯片转换 ///////////////////////////////////////////////////////////////////// /*/ /*------------------------------------------------------------------- ------------------------------*/ corr(uchar dat) //164使用共阴代码 {uchar ADS,n; ADS=dat; for(n=8;n>0;n--) { TD=0; RDat=ADS&0x80; TD=1; ADS=ADS<<1; } } /*/////////////////////////////////////////////////////////////////// //// 函数名: 液晶显示器的命令发送函数 功能: 将要显示的字符在液晶上的地址发到液晶显示器还有显示的初此化 数据 返回值: 注意: ///////////////////////////////////////////////////////////////////// /*/ lcd_com(unsigned char a) { E=1; RS=0; WR1=0; corr(a); /*f_buzy(); */ delay(1); E=0; } /*/////////////////////////////////////////////////////////////////// //// 函数名:液晶显示器的数据发送函数 功能: 将要显示的字符的ASCII码发到液晶模块 返回值:无 注意: 无 ///////////////////////////////////////////////////////////////////// /*/ lcd_dat(unsigned char b) { E=1; RS=1; WR1=0; corr(b); /*f_buzy(); */ delay(1); E=0; } /*/////////////////////////////////////////////////////////////////// //// 函数名: 液晶显示数据缓冲函数 功能: 将要显示的数据的首地址和要显示的地址缓冲 返回值: 无 注意: 对于字符型显示器它第一行的首地址是0x80第二行是0xc0 ///////////////////////////////////////////////////////////////////// /*/ lcd_dis(uchar k[],char r) {char n; lcd_com(r); //delay(1); for(n=0;k[n]!=0x00;n++) {lcd_dat(k[n]); } } /*/////////////////////////////////////////////////////////////////// //// 函数名: 液晶显示初此化函数 功能: 初此化液晶数据 返回值: 无 注意: ///////////////////////////////////////////////////////////////////// /*/ lcdinit() { lcd_com(0x0f); //显示开关控制,开显示,开光标,闪烁? lcd_com(0x38); //8位数据总线,两行显示,5X7点阵 lcd_com(0x01); //清屏 delay(1); lcd_com(0x06); //AC自动加1,显示不动 } (LCD) #include /*********************************************************** 宏定义参数 **********************************************************/ typedef unsigned char uchar; typedef unsigned int uint; /*********************************************************** 管脚定义 ***********************************************************/ sbit RS=P3^4; sbit WR1=P3^3; sbit E=P3^2; sbit RDat=P3^0; sbit TD=P3^1; /*********************************************************** 函数列表 **********************************************************/ delay(uchar b); corr(uchar dat); lcd_com(uchar a); lcd_dat(uchar b); extern lcd_dis(uchar *string,char r); extern lcdinit() ; 附录6 FPGA各模块原理图及源程序 6.1 累加器原理图及源程序: 累加器 INCLUDE "lpm_add_sub"; INCLUDE "addcore"; INCLUDE "look_add"; INCLUDE "bypassff"; INCLUDE "altshift"; PARAMETERS ( LPM_WIDTH, LPM_REPRESENTATION = "SIGNED", LPM_DIRECTION = "DEFAULT", -- controlled by add_sub port ONE_INPUT_IS_CONSTANT = "NO", LPM_PIPELINE = 0, MAXIMIZE_SPEED = 5, OPTIMIZE_FOR_SPEED = 5, CARRY_CHAIN = "IGNORE", CARRY_CHAIN_LENGTH = 32, DEVICE_FAMILY, STYLE ); INCLUDE "aglobal.inc"; % device family definitions % DEFINE MIN(a, b) = a < b ? a : b; CONSTANT LATENCY = LPM_PIPELINE; DEFINE CONSTANT_CIN() = (((LPM_DIRECTION == "SUB" # LPM_DIRECTION == "ADD") & !USED(cin)) # (LPM_DIRECTION == "DEFAULT" & !USED(cin) & !USED(add_sub))) ? 1 : 0; -- Determine the effective speed (vs. size) optimization factor: If The local -- param is used, take it as the effective value, otherwise use the global value CONSTANT SPEED_MAX_FACTOR = USED(MAXIMIZE_SPEED) ? MAXIMIZE_SPEED : OPTIMIZE_FOR_SPEED; CONSTANT INT_LATENCY = MIN(LPM_WIDTH, (LATENCY+1)); CONSTANT EXT_LATENCY = (LATENCY >= LPM_WIDTH) ? (LATENCY - LPM_WIDTH + 1) : 0; CONSTANT RWIDTH = LPM_WIDTH MOD INT_LATENCY; -- width of the right side CONSTANT LWIDTH = INT_LATENCY - RWIDTH; -- width of the left side -- SUBWIDTH0 = width of the right side adders -- SUBWIDTH1 = width of the left side adders CONSTANT SUB_WIDTH1 = FLOOR(LPM_WIDTH DIV INT_LATENCY); CONSTANT SUB_WIDTH0 = SUB_WIDTH1 + 1; -- Number of 8-bit adder blocks in carry-look-ahead cases. CONSTANT BLOCKS = CEIL(LPM_WIDTH DIV 8); SUBDESIGN lpm_add_sub ( dataa[LPM_WIDTH-1..0] : INPUT = GND; datab[LPM_WIDTH-1..0] : INPUT = GND; cin : INPUT = GND; add_sub : INPUT = VCC; clock : INPUT = GND; aclr : INPUT = GND; clken : INPUT = VCC; result[LPM_WIDTH-1..0] : OUTPUT; cout : OUTPUT; overflow : OUTPUT; ) VARIABLE IF (FAMILY_FLEX() == 1) GENERATE IF (CARRY_CHAIN != "IGNORE") GENERATE IF USED(add_sub) & ONE_INPUT_IS_CONSTANT == "NO" GENERATE datab_node[LPM_WIDTH-1..0] : LCELL; ELSE GENERATE datab_node[LPM_WIDTH-1..0] : NODE; END GENERATE; ELSE GENERATE IF USED(add_sub) & ONE_INPUT_IS_CONSTANT == "NO" GENERATE datab_node[LPM_WIDTH-1..0] : SOFT; ELSE GENERATE datab_node[LPM_WIDTH-1..0] : NODE; END GENERATE; END GENERATE; ELSE GENERATE IF USED(add_sub) & ONE_INPUT_IS_CONSTANT == "NO" GENERATE datab_node[LPM_WIDTH-1..0] : SOFT; ELSE GENERATE datab_node[LPM_WIDTH-1..0] : SOFT; END GENERATE; END GENERATE; cin_node : NODE; IF INT_LATENCY > 1 GENERATE IF LPM_REPRESENTATION == "UNSIGNED" & LPM_DIRECTION != "SUB" GENERATE add_sub_ff[INT_LATENCY-2..0] : bypassff WITH (WIDTH = 1); END GENERATE; ---------------------------------------------- -- cases where pipeline structure is needed -- ---------------------------------------------- IF !(FAMILY_FLEX() == 1) GENERATE ---------------------------------- -- non-FLEX cases -- ---------------------------------- -- if a nonhomogenous adder, generate the longer (right side) adders IF RWIDTH > 0 GENERATE adder0[RWIDTH-1..0] : lpm_add_sub WITH (LPM_WIDTH = SUB_WIDTH0, LPM_DIRECTION = "ADD", LPM_PIPELINE = 0); datab0_ff[INT_LATENCY-1..0][RWIDTH-1..0] : bypassff WITH (WIDTH = SUB_WIDTH0); END GENERATE; -- generate the shorter (left side) adders adder1[LWIDTH-1..0] : lpm_add_sub WITH (LPM_WIDTH = SUB_WIDTH1, LPM_DIRECTION = "ADD", LPM_PIPELINE = 0); datab1_ff[INT_LATENCY-1..0][LWIDTH-1..0] : bypassff WITH (WIDTH = SUB_WIDTH1); -- inter-stage carry holders carry_ff[INT_LATENCY-1..0] : bypassff WITH (WIDTH = 1); -- dataa pipeline registers dataa_ff[INT_LATENCY-2..0] : bypassff WITH (WIDTH = LPM_WIDTH); ELSE GENERATE ---------------------------------------------- -- FLEX cases -- ---------------------------------------------- -- if a nonhomogenous adder, generate the longer (right side) adders IF RWIDTH > 0 GENERATE adder0[RWIDTH-1..0] : lpm_add_sub WITH (LPM_WIDTH = SUB_WIDTH0 + 1, LPM_DIRECTION = "ADD", LPM_PIPELINE = 0); IF RWIDTH > 1 GENERATE adder0_0[RWIDTH-1..1] : lpm_add_sub WITH (LPM_WIDTH = SUB_WIDTH0 + 1, LPM_DIRECTION = "ADD", LPM_PIPELINE = 0); END GENERATE; datab0_ff[INT_LATENCY-1..0][RWIDTH-1..0] : bypassff WITH (WIDTH = SUB_WIDTH0+1); adder1_0[LWIDTH-1..0] : lpm_add_sub WITH (LPM_WIDTH = SUB_WIDTH1 + 1, LPM_DIRECTION = "ADD", LPM_PIPELINE = 0); adder1[LWIDTH-1..0] : lpm_add_sub WITH (LPM_WIDTH = SUB_WIDTH1 + 1, LPM_DIRECTION = "ADD", LPM_PIPELINE = 0); ELSE GENERATE adder1[LWIDTH-1..0] : lpm_add_sub WITH (LPM_WIDTH = SUB_WIDTH1 + 1, LPM_DIRECTION = "ADD", LPM_PIPELINE = 0); IF LWIDTH > 1 GENERATE adder1_0[LWIDTH-1..1] : lpm_add_sub WITH (LPM_WIDTH = SUB_WIDTH1 + 1, LPM_DIRECTION = "ADD", LPM_PIPELINE = 0); END GENERATE; END GENERATE; datab1_ff[INT_LATENCY-1..0][LWIDTH-1..0] : bypassff WITH (WIDTH = SUB_WIDTH1+1); IF LPM_REPRESENTATION == "SIGNED" GENERATE sign_ff[INT_LATENCY-2..0] : bypassff WITH (WIDTH = 2); END GENERATE; END GENERATE; ELSE GENERATE ---------------------------------- -- non-pipelined adder cases -- ---------------------------------- -- Will use a look-ahead type adder for FLEX/NORMAL with SPEED_MAX_FACTOR > 5 or -- MAX/FAST cases. Will use a ripple type adder for all other cases. IF ((FAMILY_FLEX() == 1) & (CARRY_CHAIN != "IGNORE" # (CARRY_CHAIN == "IGNORE" & SPEED_MAX_FACTOR <= 5))) # (!(FAMILY_FLEX() == 1) & (STYLE == "NORMAL" & SPEED_MAX_FACTOR <= 5)) GENERATE adder : addcore WITH (WIDTH = LPM_WIDTH, CONSTANT_CIN = CONSTANT_CIN()); ELSE GENERATE adder[BLOCKS-1..0] : addcore WITH (WIDTH = 8, CONSTANT_CIN = CONSTANT_CIN()); look_aheader : look_add WITH (WIDTH = BLOCKS); END GENERATE; END GENERATE; result_node [LPM_WIDTH-1..0] : NODE; cout_node : NODE; result_ext_latency_ffs : altshift WITH (WIDTH = LPM_WIDTH, DEPTH = EXT_LATENCY); carry_ext_latency_ffs : altshift WITH (WIDTH = 1, DEPTH = EXT_LATENCY); oflow_ext_latency_ffs : altshift WITH (WIDTH = 1, DEPTH = EXT_LATENCY); BEGIN ASSERT REPORT "LPM_WIDTH = %" LPM_WIDTH SEVERITY DEBUG; ASSERT REPORT "LATENCY = %" LATENCY SEVERITY DEBUG; ASSERT REPORT "LWIDTH = %" LWIDTH SEVERITY DEBUG; ASSERT REPORT "RWIDTH = %" RWIDTH SEVERITY DEBUG; ASSERT REPORT "INT_LATENCY = %" INT_LATENCY SEVERITY DEBUG; ASSERT REPORT "EXT_LATENCY = %" EXT_LATENCY SEVERITY DEBUG; ASSERT REPORT "SUB_WIDTH1 = %" SUB_WIDTH1 SEVERITY DEBUG; ASSERT (LPM_REPRESENTATION == "SIGNED" # LPM_REPRESENTATION == "UNSIGNED") REPORT "Illegal value for LPM_REPRESENTATION parameter (""%"") -- value must be ""SIGNED"" or ""UNSIGNED""" LPM_REPRESENTATION SEVERITY ERROR HELP_ID LPM_ADD_SUB_REPRESENTATION; ASSERT (LPM_WIDTH > 0) REPORT "LPM_WIDTH parameter value must be greater than 0" SEVERITY ERROR HELP_ID LPM_ADD_SUB_WIDTH; ASSERT (USED(clock) ? LATENCY > 0 : LATENCY == 0) REPORT "Value of LPM_PIPELINE parameter must be greater than 0 if clock input is used and vice versa" SEVERITY ERROR HELP_ID LPM_ADD_SUB_CLOCK_WITHOUT_LATENCY; ASSERT (LATENCY <= LPM_WIDTH) REPORT "Value of LPM_PIPELINE parameter (%) should be lower -- use % for best performance/utilization" LATENCY, INT_LATENCY SEVERITY INFO HELP_ID LPM_ADD_SUB_CLOCK_LATENCY_VALUE; ASSERT (LPM_WIDTH > 0) REPORT "Value of LPM_WIDTH parameter must be greater than 0" SEVERITY ERROR HELP_ID LPM_ADD_SUB_WIDTH2; ASSERT (LPM_REPRESENTATION == "UNSIGNED" # LPM_REPRESENTATION == "SIGNED") REPORT "Illegal value for LPM_REPRESENTATION parameter (%) -- value must be UNSIGNED (the default) or SIGNED" LPM_REPRESENTATION SEVERITY ERROR HELP_ID LPM_ADD_SUB_REPRESENTATION2; ASSERT (ONE_INPUT_IS_CONSTANT == "YES" # ONE_INPUT_IS_CONSTANT == "NO") REPORT "Illegal value for ONE_INPUT_IS_CONSTANT parameter (%) -- value must be YES or NO (the default)" ONE_INPUT_IS_CONSTANT SEVERITY ERROR HELP_ID LPM_ADD_SUB_ICONSTANT; ASSERT (LPM_DIRECTION == "DEFAULT" # LPM_DIRECTION == "ADD" # LPM_DIRECTION == "SUB") REPORT "Illegal value for LPM_DIRECTION parameter (%) -- value must be ADD, SUB, or DEFAULT" LPM_DIRECTION SEVERITY ERROR HELP_ID LPM_ADD_SUB_DIRECTION; ASSERT (LPM_DIRECTION == "DEFAULT" # USED(add_sub) == 0) REPORT "Value of LPM_DIRECTION parameter (%) is not consistent with the use of the add_sub port" LPM_DIRECTION SEVERITY ERROR HELP_ID LPM_ADD_SUB_DIRECTION_ADD_SUB; -- The next assertion is not implemented because MAX+PLUS II implementation -- differs from the LPM standard. Both overflow and cout are allowed -- in MAX+PLUS II. -- ASSERT (USED(overflow) == 0 # USED(cout) == 0) -- REPORT "Can't use overflow port if cout port is used" -- SEVERITY ERROR -- HELP_ID LPM_ADD_SUB_OVERCOUT; ASSERT (FAMILY_IS_KNOWN() == 1) REPORT "Megafunction lpm_add_sub does not recognize the current device family (%) -- ensure that you are using the newest version of the megafunction" DEVICE_FAMILY SEVERITY WARNING HELP_ID LPM_ADD_SUB_FAMILY_UNKNOWN; ----------------------------------------------------------------- ----- IF LPM_DIRECTION == "SUB" GENERATE datab_node[] = !datab[]; ELSE GENERATE IF USED(add_sub) GENERATE datab_node[] = datab[] $ !add_sub; ELSE GENERATE datab_node[] = datab[]; END GENERATE; END GENERATE; IF USED(cin) GENERATE cin_node = cin; ELSE GENERATE IF LPM_DIRECTION == "SUB" GENERATE cin_node = VCC; ELSE GENERATE cin_node = !add_sub; END GENERATE; END GENERATE; IF INT_LATENCY > 1 GENERATE IF LPM_REPRESENTATION == "UNSIGNED" & LPM_DIRECTION != "SUB" GENERATE add_sub_ff[0].d[0] = add_sub; IF INT_LATENCY > 2 GENERATE add_sub_ff[INT_LATENCY-2..1].d[0] = add_sub_ff[INT_LATENCY-3..0].q[0]; END GENERATE; add_sub_ff[].(clk, clrn) = (clock, !aclr); IF USED(clken) GENERATE add_sub_ff[].ena = clken; END GENERATE; END GENERATE; IF !(FAMILY_FLEX() == 1) GENERATE ---------------------------------------------- -- non-FLEX cases -- ---------------------------------------------- -- clock connections dataa_ff[].(clk, clrn) = (clock, !aclr); IF USED(clken) GENERATE dataa_ff[].ena = clken; END GENERATE; IF RWIDTH > 0 GENERATE datab0_ff[INT_LATENCY-2..0][].(clk, clrn) = (clock, !aclr); IF USED(clken) GENERATE datab0_ff[INT_LATENCY-2..0][].ena = clken; END GENERATE; END GENERATE; datab1_ff[INT_LATENCY-2..0][].(clk, clrn) = (clock, !aclr); carry_ff[INT_LATENCY-2..0].(clk, clrn) = (clock, !aclr); IF USED(clken) GENERATE datab1_ff[INT_LATENCY-2..0][].ena = clken; carry_ff[INT_LATENCY-2..0].ena = clken; END GENERATE; -- dataa connections as we have intermediate subadders. dataa_ff[0].d[] = dataa[]; IF INT_LATENCY > 2 GENERATE dataa_ff[INT_LATENCY-2..1].d[] = dataa_ff[INT_LATENCY-3..0].q[]; END GENERATE; -- datab input connections IF RWIDTH > 0 GENERATE IF RWIDTH > 1 GENERATE FOR I IN 1 TO RWIDTH-1 GENERATE datab0_ff[0][I].d[] = datab_node[(I+1)*SUB_WIDTH0-1..I*SUB_WIDTH0]; END GENERATE; END GENERATE; FOR I IN 0 TO LWIDTH-1 GENERATE datab1_ff[0][I].d[] = datab_node[(I+1)*SUB_WIDTH1+RWIDTH*SUB_WIDTH0-1.. I*SUB_WIDTH1+RWIDTH*SUB_WIDTH0]; END GENERATE; ELSE GENERATE IF LWIDTH > 1 GENERATE FOR I IN 1 TO LWIDTH-1 GENERATE datab1_ff[0][I].d[] = datab_node[(I+1)*SUB_WIDTH1-1..I*SUB_WIDTH1]; END GENERATE; END GENERATE; END GENERATE; -- some adder connections IF RWIDTH > 0 GENERATE -- The nonhomogeneous adder case. Note that with RWIDTH > 0, -- INT_LATENCY must have been > 1. -- longer (right hand side) adder(s) connection(s) -- the upper right-most adder is connected to the input nodes adder0[0].dataa[] = dataa[SUB_WIDTH0-1..0]; adder0[0].datab[] = datab_node[SUB_WIDTH0-1..0]; adder0[0].cin = cin_node; carry_ff[0].d[] = adder0[0].cout; -- if more than one right-side adder, make the input and carry connections IF RWIDTH > 1 GENERATE FOR I IN 1 TO RWIDTH-1 GENERATE adder0[I].dataa[] = dataa_ff[I-1].q[(I+1)*SUB_WIDTH0-1..I*SUB_WIDTH0]; adder0[I].datab[] = datab0_ff[I-1][I].q[]; adder0[I].cin = carry_ff[I-1].q[]; carry_ff[I].d[] = adder0[I].cout; END GENERATE; END GENERATE; -- first left-hand-side adder connections adder1[0].dataa[] = dataa_ff[RWIDTH-1].q[SUB_WIDTH1+RWIDTH*SUB_WIDTH0-1..RWIDTH*SUB_WIDTH 0]; adder1[0].datab[] = datab1_ff[RWIDTH-1][0].q[]; adder1[0].cin = carry_ff[RWIDTH-1].q[]; carry_ff[RWIDTH].d[] = adder1[0].cout; ELSE GENERATE -- case with homogeneous adders adder1[0].dataa[] = dataa[SUB_WIDTH1-1..0]; adder1[0].datab[] = datab_node[SUB_WIDTH1-1..0]; adder1[0].cin = cin_node; carry_ff[0].d[] = adder1[0].cout; END GENERATE; -- more connections if more than 1 left-hand-side adders exist IF LWIDTH > 1 GENERATE FOR I IN 1 TO LWIDTH-1 GENERATE adder1[I].dataa[] = dataa_ff[I+RWIDTH-1].q[(I+1)*SUB_WIDTH1+RWIDTH*SUB_WIDTH0-1.. I*SUB_WIDTH1+RWIDTH*SUB_WIDTH0]; adder1[I].datab[] = datab1_ff[I+RWIDTH-1][I].q[]; END GENERATE; adder1[LWIDTH-1..1].cin = carry_ff[INT_LATENCY-2..RWIDTH].q[]; carry_ff[INT_LATENCY-1..(RWIDTH+1)].d[] = adder1[LWIDTH-1..1].cout; END GENERATE; IF USED(cout) # USED(overflow) GENERATE cout_node = carry_ff[INT_LATENCY-1].q[]; ELSE GENERATE cout_node = GND; END GENERATE; ELSE GENERATE ---------------------------------------------- -- FLEX cases -- ---------------------------------------------- -- clock connections IF RWIDTH > 0 GENERATE datab0_ff[INT_LATENCY-2..0][].(clk, clrn) = (clock, !aclr); IF USED(clken) GENERATE datab0_ff[INT_LATENCY-2..0][].ena = clken; END GENERATE; END GENERATE; datab1_ff[INT_LATENCY-2..0][].(clk, clrn) = (clock, !aclr); IF USED(clken) GENERATE datab1_ff[INT_LATENCY-2..0][].ena = clken; END GENERATE; IF LPM_REPRESENTATION == "SIGNED" GENERATE sign_ff[INT_LATENCY-2..0].(clk, clrn) = (clock, !aclr); IF USED(clken) GENERATE sign_ff[INT_LATENCY-2..0].ena = clken; END GENERATE; END GENERATE; -- datab input connections IF RWIDTH > 0 GENERATE IF RWIDTH > 1 GENERATE FOR I IN 1 TO RWIDTH-1 GENERATE adder0_0[I].dataa[SUB_WIDTH0-1..0] = dataa[(I+1)*SUB_WIDTH0-1..I*SUB_WIDTH0]; adder0_0[I].datab[SUB_WIDTH0-1..0] = datab_node[(I+1)*SUB_WIDTH0-1..I*SUB_WIDTH0]; datab0_ff[0][I].d[] = adder0_0[I].result[]; END GENERATE; END GENERATE; FOR I IN 0 TO LWIDTH-1 GENERATE adder1_0[I].dataa[SUB_WIDTH1-1..0] = dataa[(I+1)*SUB_WIDTH1+RWIDTH*SUB_WIDTH0-1.. I*SUB_WIDTH1+RWIDTH*SUB_WIDTH0]; adder1_0[I].datab[SUB_WIDTH1-1..0] = datab_node[(I+1)*SUB_WIDTH1+RWIDTH*SUB_WIDTH0-1.. I*SUB_WIDTH1+RWIDTH*SUB_WIDTH0]; datab1_ff[0][I].d[] = adder1_0[I].result[]; END GENERATE; ELSE GENERATE IF LWIDTH > 1 GENERATE FOR I IN 1 TO LWIDTH-1 GENERATE adder1_0[I].dataa[SUB_WIDTH1-1..0] = dataa[(I+1)*SUB_WIDTH1-1..I*SUB_WIDTH1]; adder1_0[I].datab[SUB_WIDTH1-1..0] = datab_node[(I+1)*SUB_WIDTH1-1..I*SUB_WIDTH1]; datab1_ff[0][I].d[] = adder1_0[I].result[]; END GENERATE; END GENERATE; END GENERATE; -- some adder connections IF RWIDTH > 0 GENERATE -- The nonhomogeneous adder case. Note that with RWIDTH > 0, -- INT_LATENCY must have been > 1. -- longer (right hand side) adder(s) connection(s) -- the upper right-most adder is connected to the input nodes adder0[0].dataa[SUB_WIDTH0-1..0] = dataa[SUB_WIDTH0-1..0]; adder0[0].datab[SUB_WIDTH0-1..0] = datab_node[SUB_WIDTH0-1..0]; adder0[0].cin = cin_node; -- if more than one right-side adder, make the input and carry connections IF RWIDTH > 1 GENERATE FOR I IN 1 TO RWIDTH-1 GENERATE adder0[I].dataa[0] = datab0_ff[I-1][I-1].q[SUB_WIDTH0]; adder0[I].datab[] = datab0_ff[I-1][I].q[]; END GENERATE; END GENERATE; -- first left-hand-side adder connections adder1[0].dataa[0] = datab0_ff[RWIDTH-1][RWIDTH-1].q[SUB_WIDTH0]; adder1[0].datab[] = datab1_ff[RWIDTH-1][0].q[]; ELSE GENERATE -- case with homogeneous adders adder1[0].dataa[SUB_WIDTH1-1..0] = dataa[SUB_WIDTH1-1..0]; adder1[0].datab[SUB_WIDTH1-1..0] = datab_node[SUB_WIDTH1-1..0]; adder1[0].cin = cin_node; END GENERATE; -- more connections if more than 1 left-hand-side adders exist IF LWIDTH > 1 GENERATE FOR I IN 1 TO LWIDTH-1 GENERATE adder1[I].dataa[0] = datab1_ff[I+RWIDTH-1][I-1].q[SUB_WIDTH1]; adder1[I].datab[] = datab1_ff[I+RWIDTH-1][I].q[]; END GENERATE; END GENERATE; IF LPM_REPRESENTATION == "SIGNED" GENERATE sign_ff[0].d[] = (dataa[LPM_WIDTH-1], datab_node[LPM_WIDTH-1]); IF INT_LATENCY > 2 GENERATE FOR I IN 1 TO INT_LATENCY-2 GENERATE sign_ff[I].d[] = sign_ff[I-1].q[]; END GENERATE; END GENERATE; END GENERATE; IF USED(cout) # USED(overflow) GENERATE cout_node = datab1_ff[INT_LATENCY-1][LWIDTH-1].q[SUB_WIDTH1]; ELSE GENERATE cout_node = GND; END GENERATE; END GENERATE; -------------------------- -- datab_ff connections -- -------------------------- IF RWIDTH > 0 GENERATE -- first quadrant connections FOR I IN 0 TO RWIDTH-1 GENERATE datab0_ff[I][I].d[] = adder0[I].result[]; END GENERATE; IF RWIDTH > 1 GENERATE IF RWIDTH > 2 GENERATE FOR I IN 1 TO RWIDTH-2 GENERATE datab0_ff[I][RWIDTH-1..(I+1)].d[] = datab0_ff[I-1][RWIDTH-1..(I+1)].q[]; END GENERATE; END GENERATE; FOR I IN 1 TO RWIDTH-1 GENERATE datab0_ff[I][I-1..0].d[] = datab0_ff[I-1][I-1..0].q[]; END GENERATE; END GENERATE; -- fourth quadrant connections FOR I IN RWIDTH TO INT_LATENCY-1 GENERATE datab0_ff[I][RWIDTH-1..0].d[] = datab0_ff[I-1][RWIDTH-1..0].q[]; END GENERATE; -- second quadrant connections IF RWIDTH > 1 GENERATE FOR I IN 1 TO RWIDTH-1 GENERATE datab1_ff[I][LWIDTH-1..0].d[] = datab1_ff[I-1][LWIDTH-1..0].q[]; END GENERATE; END GENERATE; -- datab1_ff interface between second and third quadrants IF LWIDTH >1 GENERATE datab1_ff[RWIDTH][LWIDTH-1..1].d[] = datab1_ff[RWIDTH-1][LWIDTH-1..1].q[]; END GENERATE; END GENERATE; -- third quadrant connections FOR I IN 0 TO LWIDTH-1 GENERATE datab1_ff[I+RWIDTH][I].d[] = adder1[I].result[]; END GENERATE; IF LWIDTH > 1 GENERATE FOR I IN 1 TO LWIDTH-1 GENERATE datab1_ff[I+RWIDTH][I-1..0].d[] = datab1_ff[I+RWIDTH-1][I-1..0].q[]; END GENERATE; IF LWIDTH > 2 GENERATE FOR I IN 1 TO LWIDTH-2 GENERATE datab1_ff[I+RWIDTH][LWIDTH-1..I+1].d[] = datab1_ff[I+RWIDTH-1][LWIDTH-1..I+1].q[]; END GENERATE; END GENERATE; END GENERATE; -- connections of last row to output nodes -- right section IF RWIDTH > 0 GENERATE FOR J IN 0 TO RWIDTH-1 GENERATE result_node[(J+1)*SUB_WIDTH0-1..J*SUB_WIDTH0] = datab0_ff[INT_LATENCY-1][J].q[SUB_WIDTH0-1..0]; END GENERATE; END GENERATE; -- left section FOR J IN 0 TO LWIDTH-1 GENERATE result_node[(J+1)*SUB_WIDTH1+RWIDTH*SUB_WIDTH0-1..J*SUB_WIDTH1+RW IDTH*SUB_WIDTH0] = datab1_ff[INT_LATENCY-1][J].q[SUB_WIDTH1-1..0]; END GENERATE; -- overflow detection IF LPM_REPRESENTATION == "SIGNED" GENERATE IF !(FAMILY_FLEX() == 1) GENERATE oflow_ext_latency_ffs.data[] = (datab1_ff[INT_LATENCY-2][LWIDTH-1].q[SUB_WIDTH1-1] !$ dataa_ff[INT_LATENCY-2].q[LPM_WIDTH-1]) & (dataa_ff[INT_LATENCY-2].q[LPM_WIDTH-1] $ adder1[LWIDTH-1].result[SUB_WIDTH1-1]); ELSE GENERATE oflow_ext_latency_ffs.data[] = !(sign_ff[INT_LATENCY-2].q[0] $ sign_ff[INT_LATENCY-2].q[1]) & (sign_ff[INT_LATENCY-2].q[0] $ adder1[LWIDTH-1].result[SUB_WIDTH1-1]); END GENERATE; ELSE GENERATE IF LPM_DIRECTION == "SUB" GENERATE oflow_ext_latency_ffs.data[] = !cout_node; ELSE GENERATE oflow_ext_latency_ffs.data[] = !add_sub_ff[INT_LATENCY-2].q[0] $ cout_node; END GENERATE; END GENERATE; ELSE GENERATE ---------------------------------- -- non-pipelined adder cases -- ---------------------------------- IF ((FAMILY_FLEX() == 1) & (CARRY_CHAIN != "IGNORE" # (CARRY_CHAIN == "IGNORE" & SPEED_MAX_FACTOR <= 5))) # (!(FAMILY_FLEX() == 1) & (STYLE == "NORMAL" & SPEED_MAX_FACTOR <= 5)) GENERATE ------------------------------------------------- -- connections for a non-look-ahead type adder -- ------------------------------------------------- adder.dataa[] = dataa[]; adder.datab[] = datab_node[]; adder.cin = cin_node; result_node[] = adder.result[]; IF (USED(cout) # USED (overflow)) GENERATE cout_node = adder.cout; ELSE GENERATE cout_node = GND; END GENERATE; ELSE GENERATE --------------------------------------------- -- connections for a look-ahead type adder -- --------------------------------------------- IF BLOCKS > 1 GENERATE FOR I IN 0 TO BLOCKS-2 GENERATE adder[I].dataa[] = dataa[(I+1)*8-1..I*8]; adder[I].datab[] = datab_node[(I+1)*8-1..I*8]; adder[I].cin = look_aheader.cout[I]; result_node[(I+1)*8-1..I*8] = adder[I].result[]; END GENERATE; END GENERATE; adder[BLOCKS-1].dataa[LPM_WIDTH-(BLOCKS-1)*8-1..0] = dataa[LPM_WIDTH-1..(BLOCKS-1)*8]; adder[BLOCKS-1].datab[LPM_WIDTH-(BLOCKS-1)*8-1..0] = datab_node[LPM_WIDTH-1..(BLOCKS-1)*8]; adder[BLOCKS-1].cin = look_aheader.cout[BLOCKS-1]; result_node[LPM_WIDTH-1..(BLOCKS-1)*8] = adder[BLOCKS-1].result[LPM_WIDTH-(BLOCKS-1)*8-1..0]; look_aheader.cin = cin_node; look_aheader.bg_in[] = adder[].bg_out; look_aheader.bp_in[] = adder[].bp_out; IF USED(cout) # USED(overflow) GENERATE IF LPM_WIDTH MOD 8 == 0 GENERATE cout_node = adder[BLOCKS-1].cout; ELSE GENERATE cout_node = adder[BLOCKS-1].result[LPM_WIDTH MOD 8]; END GENERATE; ELSE GENERATE cout_node = GND; END GENERATE; END GENERATE; -- overflow detection IF (LPM_REPRESENTATION == "SIGNED") GENERATE -- SIGNED overflow: TRUE when MSB(a) & MSB(b) are same but MSB(result) is different. IF (LPM_DIRECTION == "SUB") GENERATE -- Special case (can't tell from add_sub pin) oflow_ext_latency_ffs.data[] = !(dataa[LPM_WIDTH-1] !$ datab[LPM_WIDTH-1]) & (dataa[LPM_WIDTH-1] $ result_node[LPM_WIDTH-1]); ELSE GENERATE oflow_ext_latency_ffs.data[]= !(dataa[LPM_WIDTH-1] $ (datab[LPM_WIDTH-1] $ !add_sub)) & (dataa[LPM_WIDTH-1] $ result_node[LPM_WIDTH-1]); END GENERATE; ELSE GENERATE -- UNSIGNED overflow calc: TRUE when carry out during add or underflow during sub IF (LPM_DIRECTION == "SUB") GENERATE -- Special case (can't tell from add_sub pin) oflow_ext_latency_ffs.data[]= !cout_node; ELSE GENERATE oflow_ext_latency_ffs.data[]= !add_sub $ cout_node; END GENERATE; END GENERATE; END GENERATE; -- external latency connections result_ext_latency_ffs.data[] = result_node[]; result[] = result_ext_latency_ffs.result[]; carry_ext_latency_ffs.data[] = cout_node; cout = carry_ext_latency_ffs.result[]; overflow = oflow_ext_latency_ffs.result[]; IF EXT_LATENCY > 0 GENERATE result_ext_latency_ffs.(clock, aclr) = (clock, aclr); carry_ext_latency_ffs.(clock, aclr) = (clock, aclr); oflow_ext_latency_ffs.(clock, aclr) = (clock, aclr); IF USED(clken) GENERATE result_ext_latency_ffs.clken = clken; carry_ext_latency_ffs.clken = clken; oflow_ext_latency_ffs.clken = clken; END GENERATE; END GENERATE; END; 6.2 缓冲器原理图及源程序: 缓冲器 INCLUDE "lpm_constant.inc"; INCLUDE "dffeea.inc"; PARAMETERS ( LPM_WIDTH, LPM_AVALUE = 0, LPM_SVALUE = 0, DEVICE_FAMILY ); INCLUDE "aglobal.inc"; DEFINE ASYNC_COUNT() = (USED(aconst) ? 1 : 0) + (USED(aclr) ? 1 : 0) + (USED(aset) ? 1 : 0); SUBDESIGN lpm_dff ( data[LPM_WIDTH-1..0] : INPUT = GND; clock : INPUT; enable : INPUT = VCC; shiftin : INPUT = VCC; shiften : INPUT = GND; aclr, aset, aconst : INPUT = GND; sclr, sset, sconst : INPUT = GND; q[LPM_WIDTH-1..0] : OUTPUT; shiftout : OUTPUT; ) VARIABLE IF (FAMILY_HAS_PRESET() == 0 & ASYNC_COUNT() > 1) GENERATE dffs[LPM_WIDTH-1..0] : dffeea; ELSE GENERATE dffs[LPM_WIDTH-1..0] : DFFE; END GENERATE; IF (USED(LPM_AVALUE)) GENERATE ac : lpm_constant with (LPM_WIDTH=LPM_WIDTH, LPM_CVALUE=LPM_AVALUE); END GENERATE; IF (USED(LPM_SVALUE)) GENERATE sc : lpm_constant with (LPM_WIDTH=LPM_WIDTH, LPM_CVALUE=LPM_SVALUE); END GENERATE; % Nodes that define DFFE inputs when shifting % shnode[LPM_WIDTH-1..0] : node; BEGIN ASSERT (LPM_WIDTH > 0) REPORT "Value of LPM_WIDTH parameter must be greater than 0" SEVERITY ERROR HELP_ID LPM_DFF_WIDTH; ASSERT (USED(aconst) == 0 # USED(LPM_AVALUE)) REPORT "Value of LPM_AVALUE parameter must be assigned if aconst port is used" SEVERITY ERROR HELP_ID LPM_DFF_ACONST; ASSERT (USED(aconst) == 0 # LPM_AVALUE < 2^LPM_WIDTH) REPORT "Value of LPM_AVALUE parameter must be less than 2^LPM_WIDTH if aconst port is used -- using % instead" LPM_AVALUE mod 2^LPM_WIDTH SEVERITY WARNING HELP_ID LPM_DFF_ACONST_BIG; ASSERT (USED(aconst) # USED(aset) # USED(LPM_AVALUE) == 0) REPORT "Ignored LPM_AVALUE parameter because neither the aconst or aset port is used" SEVERITY WARNING HELP_ID LPM_DFF_AVALUE; ASSERT (USED(aconst) == 0 # (USED(aclr) == 0 & USED(aset) == 0)) REPORT "Can't use aconst port if the aclr or aset port is used" SEVERITY ERROR HELP_ID LPM_DFF_ACONFLICT; ASSERT (USED(sconst) == 0 # USED(LPM_SVALUE)) REPORT "Value of LPM_SVALUE parameter must be assigned if sconst port is used" SEVERITY ERROR HELP_ID LPM_DFF_SCONST; ASSERT (USED(sconst) == 0 # LPM_SVALUE < 2^LPM_WIDTH) REPORT "Value of LPM_SVALUE parameter must be less than 2^LPM_WIDTH if sconst port is used -- using % instead" LPM_SVALUE mod 2^LPM_WIDTH SEVERITY WARNING HELP_ID LPM_DFF_SCONST_BIG; ASSERT (USED(sconst) # USED(sset) # USED(LPM_SVALUE) == 0) REPORT "Ignored LPM_SVALUE parameter because neither the sconst or sset port is used" SEVERITY WARNING HELP_ID LPM_DFF_SVALUE; ASSERT (USED(sconst) == 0 # (USED(sclr) == 0 & USED(sset) == 0)) REPORT "Can't use sconst port if the sclr or sset port is used" SEVERITY ERROR HELP_ID LPM_DFF_SCONFLICT; % common ports % dffs[].ena = enable; dffs[].clk = clock; % Asynchronous control logic % IF (USED(aconst)) GENERATE dffs[].clrn = !aconst # ac.result[]; dffs[].prn = !aconst # !ac.result[]; ELSE GENERATE IF (USED(LPM_AVALUE)) GENERATE dffs[].clrn = !aclr & (!aset # ac.result[]); dffs[].prn = aclr # !aset # !ac.result[]; ELSE GENERATE IF (USED(aclr)) GENERATE dffs[].clrn = !aclr; END GENERATE; IF (USED(aset)) GENERATE dffs[].prn = aclr # !aset; END GENERATE; END GENERATE; END GENERATE; % shift-loaded nodes % IF (LPM_WIDTH > 1) GENERATE shnode[] = (dffs[LPM_WIDTH-2..0].q, shiftin); ELSE GENERATE shnode[] = shiftin; END GENERATE; % Synchronous input logic % IF (USED(sconst)) GENERATE dffs[].d = sconst & sc.result[] # !sconst & ( shiften & shnode[] # !shiften & data[]); ELSE GENERATE IF (USED(LPM_SVALUE)) GENERATE dffs[].d = !sclr & ( sset & sc.result[] # !sset & ( shiften & shnode[] # !shiften & data[])); ELSE GENERATE dffs[].d = !sclr & (sset # ( shiften & shnode[] # !shiften & data[])); END GENERATE; END GENERATE; % Connect outputs % q[] = dffs[].q; shiftout = dffs[LPM_WIDTH-1].q; END; 6.3 分频器原理图及源程序: 分频器 library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity fp is PORT(CLK: IN STD_LOGIC; CLK2: OUT STD_LOGIC); end fp; architecture fen of fp is signal temp:std_logic:='0'; begin clk2<=temp; process(clk) begin if clk'event and clk='1' then temp<=not temp; end if; end process; end fen; 6.4 存储器原理图及源程序: 存储器 INCLUDE "altrom.inc"; PARAMETERS ( LPM_WIDTH, LPM_WIDTHAD, LPM_NUMWORDS = 2^LPM_WIDTHAD, LPM_ADDRESS_CONTROL = "REGISTERED", LPM_OUTDATA = "REGISTERED", LPM_FILE, DEVICE_FAMILY ); INCLUDE "aglobal.inc"; CONSTANT NUMWORDS = (LPM_NUMWORDS == 0) ? 2^LPM_WIDTHAD : LPM_NUMWORDS; SUBDESIGN lpm_rom ( address[LPM_WIDTHAD-1..0] : INPUT = GND; inclock : INPUT = VCC; outclock : INPUT = VCC; memenab : INPUT = VCC; q[LPM_WIDTH-1..0] : OUTPUT; ) VARIABLE IF (LPM_ADDRESS_CONTROL == "REGISTERED") GENERATE srom : altrom WITH (WIDTH=LPM_WIDTH, AD_WIDTH=LPM_WIDTHAD, NUMWORDS=NUMWORDS, FILE=LPM_FILE, REGISTERINPUTMODE="ADDRESS_CONTROL"); ELSE GENERATE srom : altrom WITH (WIDTH=LPM_WIDTH, AD_WIDTH=LPM_WIDTHAD, NUMWORDS=NUMWORDS, FILE=LPM_FILE, REGISTERINPUTMODE="DEFAULT"); END GENERATE; otri[LPM_WIDTH-1..0] : TRI; BEGIN ASSERT (LPM_WIDTH > 0) REPORT "Value of LPM_WIDTH parameter value must be greater than 0" SEVERITY ERROR HELP_ID LPM_ROM_WIDTH; ASSERT (LPM_WIDTHAD > 0) REPORT "Value of LPM_WIDTHAD parameter value must be greater than 0" SEVERITY ERROR HELP_ID LPM_ROM_WIDTHAD; ASSERT (NUMWORDS > 2^(LPM_WIDTHAD-1) & NUMWORDS <= 2^LPM_WIDTHAD) REPORT "Value of LPM_NUMWORDS parameter requires % address lines, but LPM_WIDTHAD value, which defines the number of address lines, is %" CEIL(LOG2(NUMWORDS)), LPM_WIDTHAD SEVERITY ERROR HELP_ID LPM_ROM_NUMWORDS; ASSERT (LPM_ADDRESS_CONTROL == "REGISTERED" # LPM_ADDRESS_CONTROL == "UNREGISTERED") REPORT "Illegal value for LPM_ADDRESS_CONTROL parameter (%) -- value must be REGISTERED (the default) or UNREGISTERED" LPM_ADDRESS_CONTROL SEVERITY ERROR HELP_ID LPM_ROM_ADDRESS_CONTROL; ASSERT (LPM_OUTDATA == "REGISTERED" # LPM_OUTDATA == "UNREGISTERED") REPORT "Illegal value for LPM_OUTDATA parameter (%) -- value must be REGISTERED or UNREGISTERED (the default)" LPM_OUTDATA SEVERITY ERROR HELP_ID LPM_ROM_OUTDATA; ASSERT ( LPM_ADDRESS_CONTROL == "REGISTERED" & USED(inclock) # LPM_ADDRESS_CONTROL == "UNREGISTERED" & USED(inclock) == 0) REPORT "Value of LPM_ADDRESS_CONTROL parameter (%) is not consistent with the use of the inclock port" LPM_ADDRESS_CONTROL SEVERITY ERROR HELP_ID LPM_ROM_ADDRESS_CONTROL_CLOCK; ASSERT ( LPM_OUTDATA == "REGISTERED" & USED(outclock) # LPM_OUTDATA == "UNREGISTERED" & USED(outclock) == 0) REPORT "Value of LPM_OUTDATA parameter (%) is not consistent with the use of the outclock port" LPM_OUTDATA SEVERITY ERROR HELP_ID LPM_ROM_OUTDATA_CLOCK; ASSERT (FAMILY_IS_KNOWN() == 1) REPORT "Megafunction lpm_rom does not recognize the current device family (%) -- ensure that you are using the newest version of the megafunction" DEVICE_FAMILY SEVERITY WARNING HELP_ID LPM_ROM_FAMILY_UNKNOWN; srom.address[] = address[]; IF (USED(inclock)) GENERATE srom.clocki = inclock; END GENERATE; IF (USED(outclock)) GENERATE srom.clocko = outclock; END GENERATE; otri[] = srom.q[]; otri[].oe = memenab; q[] = otri[]; END; 6.5 数据选择器原理图及源程序: 数据选择器 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY MUX44 IS PORT( D3,D2,D1,D0 :IN STD_LOGIC_VECTOR(7 DOWNTO 0); A :IN STD_LOGIC_VECTOR(1 DOWNTO 0); Y :OUT STD_LOGIC_VECTOR(7 DOWNTO 0) ); END MUX44; ARCHITECTURE A OF MUX44 IS BEGIN Y<=D0 WHEN A="00" ELSE D1 WHEN A="01" ELSE D2 WHEN A="10" ELSE D3 WHEN A="11" ELSE "0000"; END A;
本文档为【基于FPGA 的DDS的硬件和软件设计】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_321635
暂无简介~
格式:doc
大小:470KB
软件:Word
页数:0
分类:生活休闲
上传时间:2017-12-12
浏览量:28