OS:Win7 x64 EDA软件:Quartus II 13.1 FPGA平台:DE1-SOC |
在quartus II中设计一个逻辑(功能),让DE1-SOC的LEDR0灯亮1s灭1s。
LED灯怎么才会亮?
有电流通过LED灯的时候。
怎么才能使LED内有电流通过?
给LED两端施加电压。
给LED施加多大的电压合适?
在FPGA等数字系统中,电压就只有高/低两个逻辑电平的概念。不管LED连接方式如何,每隔1s翻转加在LED某端的电平,LED一定会以亮1s灭1s的方式工作。
从哪里得到LED的高/低电平?
FPGA上的时钟就是高低电平,而且成周期性变化。
如此,LEDR0以1s间隔亮就被等效为:每过1s翻转一次在LEDR0一端的电平。那么就需要设计一个每个1s就翻转一次LEDR0一端电平的逻辑模块。那么在DE1-SoC的FPGA模块中,LEDR0和时钟是如何分布的呢?这就需要查看DE1-Soc相关的手册。
在~../DE1-SoC_v.3.1.0_SystemCD/Schematic目录下打开FPGA的原理图(我的是DE1-SoC_Rev_D.pdf)。在文档中搜(Ctrl+ F)“LEDR0”,搜到在5CSEMA5F31C6(DE1-SOC主芯片的型号)图中的LEDR0标号,LEDR0连接FPGA的引脚V16。再查看5CSEMA5F31C6上与时钟有关的引脚(共有四个,其频率都为50MHZ),选取CLOCK_50作为LEDR0的高/低电平,CLOCK_50连接FPGA引脚AF14。
如此,要设计的逻辑模块的功能为:将CLOCK_50变为0.5HZ。结合分析和Quartus II的开发流程,整个过程可表述如下:
Figure1. 完成目标所需要做的设计
FPGA的时钟都是50MHZ的,给LEDR0一端的电平需要0.5HZ。可以设计一个逻辑模块my_first_counter,输入为CLOCK_50,输出为my_first_out;每隔CLOCK_50的50000000个周期,就翻转my_first_out上的电平;再将my_first_out连接到LEDR0上,这样就可以实现LEDR0以1秒的间隔亮。
输入/输出(接受CLOCK_50/输出my_first_out)模块调用quartus II库中的模块;采用Verilog编写CLOCK_50变为0.5HZ模块(初学时,多自己编码)。
使用Quartus II建立工程的步骤参见“My_First_Fpga.pdf”文档。将工程命名为my_first_fpga。3.1和3.2是新建Quartus II工程中两个需要被注意的地方。
“top-leveldesign entity name”指定顶层文件的名称。设计者要在顶层文件中完成整个设计(可调用设计在其它文件中的子模块)。顶层文件相当于C语言中包含main()主函数的主文件。
在Family& Device Settings页面选择跟DE1-SoC开发板主芯片一致的5CSEMA5F31C6型号。
工程文件中表述逻辑的方式(Verilog或原理图)都是写给编译器的;而其中包含的逻辑贯穿整个工程。
根据New Project Wizard…建立工程后,在Quartus II 中Project Navigator中还未有任何文件。
考虑到my_first_fpga工程是一个小工程,可以用模块/原理图(.bdf)作为顶层文件:
[1] File>> New >> Design Files >> Block Diagram/Schematic File>> OK。
[2] 切换到刚新建Block Diagram/Schematic File界面,File >> Save as…,将其命名为my_first_fpga.bdf保存在bdf目录(在工程目录下新建)下。
[3] 在Project Navigator框中切换到Files界面,选中my_first_fpga.bdf文件>> 右键>> Set as Top-Level Entity。
这样,my_first_fpga.bdf就成为了此次工程的顶层设计文件,我们将在这个文件中完成所有的设计。
有了顶层设计文件之后,我们就要为顶层设计文件创造资源,供顶层文件调用。
File >>New >> Design Files >> Verilog HDL File,然后在弹出的窗口中输入以下内容:
1. /*module是模块定义的开始;endmodule是模块定义的结束。 2. *@my_first_counter是模块名, 3. *这个模块的功能是当CLOCK_50满50000000个周期时,my_counter_out电平翻转。 4. *@CLOCK_50和my_counter_out是my_first_counter的端口, 5. *其类型在后续内容中声明。*/ 6. modulemy_first_counter(CLOCK_50, my_counter_out); 7. 8. /****端口声明/数据定义部分*****/ 9. //声明CLOCK_50为my_first_counter模块的输入端口, 10. //也可以将CLOCK_50的数据类型定义为wire类型,因为它需要CLOCK_50驱动 11. input CLOCK_50; 12. //wire CLOCK_50; 13. 14. //声明my_counter_out为输出端口 15. //在always中被赋值的变量必须为reg类型 16. output my_counter_out; 17. reg my_counter_out; 18. 19. //integer类型为32位的有符号整数类型 20. integer counter = 1; 21. 22. //parameter用作定义符号常量,CNT_L为常量50000000 23. parameter CNT_L = 50000000, CNT_H = 100000000; 24. /****端口声明/数据定义部分*****/ 25. 26. 27. /****逻辑功能描述部分****/ 28. //只要CLOCK_50有上升沿(posedge)发生就执行begin到end之间的内容 29. always @ (posedge CLOCK_50) 30. begin 31. counter =counter + 1; 32. if (counter <= CNT_L) //<=为关系运算符 33. begin 34. my_counter_out <= CLOCK_50; //<=为赋值运算符 35. end 36. else if (counter > CNT_L && counter<= CNT_H) 37. begin 38. my_counter_out <= !CLOCK_50; 39. if (counter == CNT_H) counter = 1; 40. end 41. end 42. /****逻辑功能描述部分****/ 43. endmodule |
由于之前没用过Verilog,数电基础也差,所以注释得比较详细。这段程序被折腾了好久(查看《Verilog经典教程》等语法书)才完成这段代码。与C语言不同的是,Verilog中每个变量的位数不是表示此变量在内存中占多少位,而是此变量会用多少根导线。
Ctrl + s保存,在工程目录下新建一个Verilog的文件夹。将包含Verilog代码的文件命名为my_first_counter.v;勾选“Add file to current project”;保存在Verilog目录下。
顶层设计文件是.bdf文件,它是以原理图符号来描述数字电路的。所以,我们需要将Verilog代码所描述的原理图符号转换出来。在Project Navigator中切换到Files界面,选中my_first_counter.v>> 右键>> Create Symbol Files for Current File,随之在工程目录下生成my_first_counter.bsf文件。将界面切换到my_first_fpga.bdf界面,双击添加my_first_counter.bsf模块:
Figure2. my_first_counter.bsf
将50MHZ频率变为0.5HZ的my_first_counter完成;my_first_counter中有输入/输出端口,那么连接这些端口的导线该如何描述?(可以看这些模块的源码,借此加深抽象的Verilog和实际硬件符号的对应)我们直接调用quartus II库中已有的导线模块。
Quartus II 自带的库在~../altera/13.1/quartuss/libraries下,我主要是想用altera/13.1/quartus/libraries/pin下的input和output模块。将工程切换到my_first_fpga.bdf界面并双击.bdf的空白处。在Symbol框中选择~../altera/13.1/quartus/libraries/pin下的input、output,放入my_first_fpga.bdf中。
放在顶层设计文件中的input、output、my_first_counter都还只是独立的模块,要将它们连接起来才能组成一个完整的电路通路(参照“My_First_Fpga.pdf”文档中的步骤):
Figure3. 顶层文件的逻辑图
Figure 3顶层设计文件内诞生了一个完整的数字通路。可以进行功能仿真了。(如果不进行功能仿真,到这里就可以编译这个工程了,可以在Processing>> Start下指定具体的编译阶段,也可以用Start Compile完成所有阶段的编译。)
功能仿真是在设计输入之后,还没有综合、布局布线之前的仿真。是不考虑电路的逻辑和门的时间延迟,着重考虑电路在理想环境下的行为和设计构想的一致性。如果在设计上连功能上都不能满足,就没有进行后续开发的必要,应该及时修改设计,保证功能正确。
由于my_first_counter.v中参数设置得太大了(50000000),要50000000个CLOCK_50周期才能看到一个完整的高电平。我们不妨将这个参数设置得小一点,比如INT_L=10,INT_H=20。在my_first_counter.v中修改这两个参数后,保存。重新生成my_fisrt_counter.bsf文件,并替换掉my_first_fpga.bdf中的my_first_counter。
File >> New >> University Program VWF >> 双击Simulation Waveform Editor窗口的左子窗口>> 在Insert Nodeor Bus窗口中点击Node Finder… >> 在Node Finder窗口中点击List 后再点“>>”,然后点击 OK按钮。
在进行功能仿真时,我们需要给CLOCK_50一个值,看my_first_counter是否能够按照设计的功能那样得到正确的输出。选中CLOCK_50>> Overwrite clock(图标)>>将Clock的周期(Period)设置为20ns(50MHZ)。Simulation>> Run functional simulation:
在CLOCK_50的前9个(CLOCK_50开始就算1个)周期LEDR0保持高电平,然后翻转为低电平,保持10个CLOCK_50周期的低电平。这说明,功能是按照程序的描述在进行,可以做后续工作了。
[在功能正确的情况下,需要为工程写时序约束文件,对设计进行布局布线,再对工程进行时序仿真。由于目前对时序认识不到位再加上my_first_fpga工程简单,在此可省略时序仿真这个步骤,到时再专门笔记]。
Quartus II顶层文件中所设计的是一个具有某种功能的逻辑模块,编译器能够识别这个逻辑模块的功能。但要在FPGA上实现此逻辑功能,得将此逻辑块的输入/输出对应到FPGA的实际引脚上。一方面,为逻辑模块指定了真实的硬件源;另一方面,也给编译器提供配置FPGA哪个区域的信息。
引脚分配的前提是要将设计的逻辑经过“分析 & 阐述”阶段(Choose Processing > Start > Start Analysis & Elaboration )。然后进入Assignments >> Pin Planner:
Figure5. 引脚分配
在Location列双击,就会出现选择FPGA引脚的下拉框,输入/输出在FPGA上所对应的引脚已经在分析步骤中获得。
Figure6. LEDR0闪烁
2015.01.25—01.26
二进制系统内只有1和0两个数字,它可以作为只存在两种状态的系统的抽象层。
如在(CMOS数字)系统中,2V ~ 3.3V范围中的电压值被称为高电平;在0V~ 0.8V范围中的电压值被称为低电平。对于这样只具有高、低电平两种状态的系统,就可以用二进制系统中的一个数字来描述这个系统的一个电平状态,可以用1描述高电平,用0描述低电平。如此,二进制系统成为这样数字系统的一个重要的抽象层(所有关于高、低电平的地方都可以用二进制系统来描述)。
反过来,对应二进制中1和0的电平范围称为逻辑电平。
不仅是二进制系统是数字电路中的一个重要抽象,如,波形图工具也是数字系统中的重要抽象;逻辑图符是对二进制运算法则(和实现逻辑图符所描述功能的电路)的抽象,等等。在数字电路中,归根到底,都是在对逻辑电平的描述。书中写的大部分内容,都是抽象层面的东西。
数字系统不能识别二进制!它只能传输高低电平。
在数字领域中,二进制系统只是一个抽象层。很多书都说计算机内只有(只能执行)二进制,其实这是一个在二进制系统抽象层上的说法,它表示计算机内部只有高、低电平。
二进制系统在计算机内从来就没有存在过。我们通过键盘往编辑器内敲入字符时,其实首先是将一串高、低电平传递给计算机(内存),由于每个按键开启的电路不同,每个字符传递给计算机的高、低电平序列也不同。将这些高、低电平传输到计算机中不同的电路中,所起到的作用就不一样。如在编辑器内显示了输入的字符,是计算机将按键的高、低电平传输到了显存电路中,让您误以为字符在您的编辑器内。
保存在硬盘上的文件也不存在二进制系统,硬盘只是对文件中的高、低电平进行一个记录。当从硬盘中读数据时,一定有某种机制去驱动某段内存成为相应的高、低电平状态。
Figure7. 数字集成电路
对计算机来说,数据就是一系列的高低电平。掉电后,数字器件的高、低电平都不存在了(我将它称为逻辑电平丢失),何以有掉电后所存数据还有不丢之说?掉电丢失数据与否的含义应该是“重新给数字器件上电后,其内的每个逻辑电平状态是否跟掉电前一样。”
对存储器来说,只有被地址线选中过的存储单元才是有意义的。将存储器中能表一个逻辑电平的部件成为存储单元。
为什么重新上电后,ROM中的数据能跟上电前一样?
在ROM中,每个ROM存储单元的电路固定,这些电路导通时,逻辑电平固定。
[1]掉电后,ROM中的逻辑电平丢失。
[2]重新上电后,被地址线选中的ROM存储单元,按照固定电路的功能输出高、低电平。
为什么重新上电后,RAM中的数据跟上电前不一样?
SRAM存储单元使用一个锁存器来输出逻辑电平。
[1]掉电后,SRAM中的逻辑电平丢失。
[2]重新上电后,若SRAM存储单元的行选线关闭,则SRAM存储单元处于逻辑电平丢失状态;若SRAM存储单元的行选线被开启,则SRAM存储单元的逻辑电平输出依据其输入端的电平值,这个电平跟掉电前的电平可能不一致,故而SRAM存储单元的输出也可能与掉电前不一致。
DRAM存储单元使用电容的电压(电荷)来表逻辑电平,在电容之上的电荷本身就易泄露(上电期间都必须刷新)。
[1]掉电后,DRAM中的逻辑电平丢失。
[2]重新上电后,若DRAM存储单元的行选线关闭,则DRAM存储单元处于逻辑电平丢失状态;若DRAM存储单元的行选线被开启,则DRAM存储单元的逻辑电平输出依据其输入端的电平值,这个电平跟掉电前的电平可能不一致,故而DRAM存储单元的输出也可能与掉电前不一致。
编程的本质是逻辑电平。可编程器件是指对于不同的逻辑电平输入会有不同的逻辑电平输出。如
Figure8. 可编程电路
若in_1的逻辑电平确定,out的逻辑电平取决于中间的MOSFET是否导通(假设这个MOSFET在高电平时导通,低电平时截止)。如果MOSFET导通,out输出一个逻辑电平(同in_1);如果MOSFET不导通,out输出另一个逻辑电平。如果采用编程的手段来决定in_2的逻辑电平,当在程序中为in_2写高电平时,out输出in_1的电平状态;当给in_2写低电平时,out输出另一个逻辑电平。
不像RAM存储单元,其输出电平是依据其不同的输入电平而得到(改变);而可编程器件的输出电平是依据实电路连通与否而得到(不同的)输出。
可编程器件内部比Figure2要复杂,实现可编程的技术也可能与Figure 2不一样。但,其实现思想都是用逻辑电平去开启/关闭某些电路,让输出的电平与输入电平成某种逻辑(函数)关系。从而得到不同的高/低电平(数字系统中的一切数据不都是高/低电平么)。
在用可编程器件来设计数字系统这个流程中,有很多层的抽象。这些抽象层都是为了能更好的进行数字系统的设计而出现的,最终下载到FPGA中只是一串高低电平(二进制流),这些逻辑电平去配置诸如Figure2中那样的in_2。
2015.01.25
如果设计所包含的功能复杂,性能要求高,就要求我们充分理解所用的FPGA器件的结构特点,合理地使用其内部的功能模块和布线资源。这样,即使我们的设计十分复杂,也能够在FPGA下验证设计。
FPGA一般是基于SRAM工艺的,其基本可编程逻辑单元(FPGA还有其它模块)几乎都是由查找表(LUT,Lookup table,在FPGA内一般为4输入)和寄存器组成的。
Figure9. 4输入的LUT示例
寄存器结构灵活,可以配置为带同步/异步复位或置位、时钟使能的触发器,也可以配置成为锁存器。
在可编程逻辑器件领域,IP核是预先设计好的参数化的算法或功能的电路模块,让其他用户可以直接调用这些模块。
Altera公司以及第三方IP合作伙伴给用户提供了两类功能模块:免费的LPM宏功能模块(Meggafunctions/LPM)和需要授权使用的IP知识产权(MegaCore)。两者从实现的功能上区分,使用方法则基本相同。一般来说,不是我写的模块或者函数都应该伴有手册(^_^)。
像存储器、DSP块、LVDS驱动器、PLL以及SERDES和DDIO电路等必须[不科学吧]使用Altera提供的可参数化LPM宏功能模块和LPM函数才可以使用Altera特定器件的功能。
(4) Quartuss II
Quartus II集成了设计数字系统(电路)的编辑器(HDL文本编辑器,原理图编辑器)和编译器等众多工具。编辑器供开发者描述数字系统;编译器可将开发者在编辑器内描述的数字系统翻译成网表文件,也可根据网表文件进一步生成一串可以配置FPGA的二进制流(这一步主要供开发者验证所设计的数字逻辑)。
网表文件:与、或、非门,RAM,触发器等元件的连接关系的文件,可以根据网表文件内的元件及其连接关系加工出实际的数字系统。
所以,用Quartus II来开发数字系统,就要学会怎么用Quartus II来设计数字系统、怎么得到我们所需的文件(网表或二进制流)。
2015.01.28-01.29
数据就是电平。
专用硬线逻辑是将处理输入的算法f()实施在电路设计上,即输出电平 = f(输入电平)。只要输入一到来,所设计的硬线逻辑就立即处理这些输入,具有实时性。
微处理器只能处理存储在RAM中的电平,并照顺序依次处理,每次处理的电平个数有限(如最多32位)。
微处理器首先要读RAM中的电平,然后再把这些电平传输到实际的硬线逻辑中去(就有数据存储、取指、译码、执行的过程)。而硬线逻辑没有这些过程,电平只在硬件上传输着。在微处理上,算法主要体现在存储电平和如何让CPU处理这些电平上(如何编写程序),即如何占用更少的RAM存储空间,如何让微处理器读更少的电平就能够完成任务;在硬线逻辑上,算法主要用来设计硬线逻辑电路,怎么样的一个硬线逻辑电路的传输速度才是更快的。
在微处理器上处理数据时,要了解微处理器的原理和结构,明白系统是如何工作的。将算法融入程序中,让微处理器在处理程序表达的电平上花更少的时间。
在设计硬线逻辑时,将算法体现在逻辑设计上。结合数字电路基本知识和硬件描述语言设计出能进行快速计算的硬线逻辑专用电路。(下一步就不要再点灯了,可以往阅读或者实现FFT算法、DCT算法部件的道路上发展),不过精的前提是要有基本功,可将《Verilog数字系统设计教程》中的基本器件以及设计练习的Verilog HDL模型都编码一遍。
硬件描述语言(HDL)是一种用形式化方法(formal methods,在逻辑科学中是指分析、研究思维形式结构的方法)来描述数字电路和设计数字逻辑系统的语言。数字逻辑电路设计者可以利用这种语言来描述自己的设计思想,然后利用电子设计自动化(EDA)工具进行仿真,再自动综合到门级电路,再用ASIC或FPGA实现其功能。
Verilog HDL既是一种行为描述的语言也是一种结构描述的语言,即既可以用电路的功能描述也可以用元器件和它们之间的连接来建立所设计电路的Verilog HDL模型。Verilog模型可以是实际电路的不同级别的抽象。共有以下五种:
用Verilog HDL对实际电路建模时,如果能够明确是在对实际电路的哪一个级别进行抽象,入门的干活。
顺序块begin...end:
并行块fork...join:
如果用Verilog模块(module… endmodule)实现一定的功能,首先应该清楚哪些是同时发生的(各逻辑功能同时发生,assign,andetc., always),哪些是顺序发生的(always内的语句时顺序发生的)。
非阻塞(Non_Blocking)赋值方式(如 b <= a; ) 这种方式的赋值并不是马上执行的,也就是说"always"块内的下一条语句执行后,b并不等于a,而是保持原来的值。"always"块结束后,才进行赋值。
阻塞(Blocking)赋值方式(如 b = a; ) 这种赋值方式是马上执行的。也就是说执行下一条语句时,b已等于a。
task和function说明语句分别用来定义任务和函数。利用任务和函数可以把一个很大的程序模块分解成许多较小的任务和函数便于理解和调试。输入、输出和总线信号的值可以传入、传出任务和函数。任务和函数往往还是大的程序模块中在不同地点多次用到的相同的程序段。Verilog中的任务和函数各有独特的讲究;Verilog自带系统函数和任务。
可以将C看成是对汇编的抽象(C经过C编译器可对应到汇编),由于C对汇编抽象是一种语言对语言的抽象,没太大的过渡,就算单纯停留在C这一层学习(不去管汇编和计算机运行,有点算法基础),最差也算一个n流C的学习者。但Verilog是直接对硬件(硬件符号?)的抽象,如果无硬件(数电甚至是模电)基础的人写Verilog程序,恐自己都不清楚程序的实际意思。有这些基础固然好,没有也没关系,就边写边补,也不失风度。唯有将抽象层描述的实际对象搞清楚,才有可能在抽象层走得更加游刃有余。至于是从Verilog学到电路还是从电路学到Verilog,这是一个策略问题,可能会影响每个最终的功力。最好是能对不同模块用相应抽象级别的Verilog HDL模块描述。
功能经过验证的、可综合的、实现后电路结构总门数在5000门以上的Verilog HDL模型称之为“软核”(SoftCore)。
由软核构成的器件称为虚拟器件,在新电路的研制过程中,软核和虚拟器件可以很容易地借助EDA综合工具与其它外部逻辑结合为一体。
在某一种现场可编程门阵列(FPGA)器件上实现的,经验证是正确的总门数在5000门以上电路结构编码文件,称之为“固核”。
在某一种专用半导体集成电路工艺的(ASIC)器件上实现的经验证是正确的总门数在5000门以上的电路结构掩膜,称之为“硬核”。
自顶向下的设计(即TOP_DOWN设计)是从系统级开始,把系统划分为基本单元,然后再把每个基本单元划分为下一层次的基本单元,一直这样做下去,直到可以直接用EDA元件库中的元件来实现为止。
复杂数字逻辑电路和系统的层次化、结构化设计隐含着硬件设计方案的逐次分解。在设计过程中的任意层次,硬件至少有一种抽象的描述形式。硬件的描述特别是行为描述通常称为行为建模(HDL建模)。在集成电路设计的每一层次,硬件可以分为一些模块,该层次的硬件结构由这些模块的互连描述,该层次的硬件的行为由这些模块的行为描述。
在不同的层次做具体模块的设计所用的方法也有所不同,在高层次上往往编写一些行为级的模块通过仿真加以验证,其主要目的是系统性能的总体考虑和各模块的指标分配,并非具体电路的实现。因而综合及其以后的步骤往往不需进行。而当设计的层次比较接近底层时行为描述往往需要用电路逻辑来实现,这时的模块不仅需要通过仿真加以验证,还需进行综合、优化、布线和后仿真。
采用EDA工具进行数字系统开发时,下图可表实践中的基本流程:
Figure10. 数字系统设计的各层次
由于各种ASIC和FPFA器件的工艺各不相同,因而当用不同厂家的不同器件来实现已验证的逻辑网表(EDIF文件)时,就需要不同的基本单元库与布线延迟模型与之对应才能进行准确的优化、映象、和布局布线。基本单元库与布线延迟模型由熟悉本厂工艺的工程师提供,再由EDA厂商的工程师编入相应的处理程序,而逻辑电路设计师只需用一文件说明所用的工艺器件和约束条件,EDA工具就会自动地根据这一文件选择相应的库和模型进行准确的处理从而大大提高设计效率。仿真用的基本部件库是由半导体厂家和EDA工具厂家共同提供的。