如何把文件放到文件列表顶层?

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。

大侠好,欢迎来到FPGA技术江湖。本次带来FPGA系统性学习系列,今天开始正式更新,之前更新过类似的郝旭帅FPGA零基础学习系列,由于时间久远,之前的系列所用开发操作软件、硬件设备及所涉及知识维度都有待更新及完善。

本系列将带来FPGA的系统性学习,从最基本的数字电路基础开始,最详细操作步骤,最直白的言语描述,手把手的“傻瓜式”讲解,让电子、信息、通信类专业学生、初入职场小白及打算进阶提升的职业开发者都可以有系统性学习的机会。

系统性的掌握技术开发以及相关要求,对个人就业以及职业发展都有着潜在的帮助,希望对大家有所帮助。后续会陆续更新 Xilinx 的 Vivado、ISE 及相关操作软件的开发的相关内容,学习FPGA设计方法及设计思想的同时,实操结合各类操作软件,会让你在技术学习道路上无比的顺畅,告别技术学习小BUG卡破脑壳,告别目前忽悠性的培训诱导,真正的去学习去实战应用,这种快乐试试你就会懂的。话不多说,上货。

作者:郝旭帅 校对:陆辉

9. 生成配置文件并下载

本篇将设计一个简单的二输入与门,来讲解整个设计流程。至于设计语言就不在单独列出一个章节去做特殊说明,语法、操作、实验将同时讲解,这样更具有带入性,便于读者阅读和学习。

在设计之前我们需要在两个方面进行准备:硬件方面和软件方面。

开发FPGA设计,最终的产品是要落在使用FPGA芯片完成某种功能。所以我们首先需要一个带有Intel FPGA芯片的开发板。

本文中设计将采用CYCLONE系列FPGA进行讲解,如果读者有其他系列(必须是Intel FPGA,否则无法在Quartus上开发),也可以进行学习,不同系列的开发流程基本相同。

我们需要综合工具-quartus 软件和仿真工具-modelsim软件。正确安装这两个软件是开发Intel FPGA的必要条件。

按照的方式安装,Modelsim软件的图标将不会出现在桌面上。并不是没有安装上,而是此时Quartus软件认为Modelsim软件只是它的一个小程序包而已,所以就没有体现单独的软件图标。

当准备好软件后,笔者向大家推荐一个工程管理的方式,也是本文中做工程管理的方式,这样会比较清楚。

做设计的话,会编写很多设计文件、仿真文件以及综合器会给我输出很多的过程文件等等,那么这些文件最好都能有自己的一个“归宿”,不要乱放。

路径和命名中不允许出现非法字符(合法字符包括:数字、字母、下划线。特别说明:空格是非法的),笔者建议以字母开头。

我们是做FPGA开发设计的,首先我们将建立一个文件夹,专门用来放FPGA开发设计。例如:E:/fpga_design。

在后续的开发设计中,我们会做各种各样的设计。每个设计都有自己的名字,在上述文件内,用实验的名字命名一个文件夹。名字的话一定要带有某种含义,不建议随便给个字母序列当做名字。例如:第一个要做的二输入与门的设计,命名为and_gate2_1。

在建立完某项设计的文件后,依次在其里面新建四个文件夹,分别为:rtl、qprj、msim、doc。

图 2 :工程管理文件脉络示意图

rtl文件夹用于存放设计的源文件。

doc文件夹用于存放设计的一些文档性的资料。

qprj文件夹用于存放quaruts 工程以及quartus生成的一些过程性文件。

msim文件夹用于存放仿真文件。

在 FPGA 设计时,主要是这四个文件的使用。某些时候我们也会新加一些文件,例如:FPGA板卡需要我们设计自己时,就会多一个文件夹PCB,用于存放PCB相关的工程或者源文件等。

做好设计前准备后,就可以开始建立quartus 工程了。

在做设计时,都是以工程为主体的设计。在没有工程的情况下,利用quartus软件打开设计源文件等,也是不支持编译和综合的。

双击quartus 软件的图标(图4-1),打开quartus软件。在有的电脑上,软件启动的速度不是很快(在确保自己已经双击打开的前提下,可以等待1分钟),不要多次去双击图标,容易造成PC卡死或者启动了很多个quartus软件。

笔者这里不对每个界面进行单独介绍,后续用到那个功能或者界面时,会单独介绍说明。

图4 :打开新工程创建向导

打开新工程向导,首先出现一个工程向导介绍说明。

图5 :工程向导的介绍界面

在工程向导中,我们会指定工程名称和位置,顶层实体的名称,工程文件和库文件,目标器件,EDA工具。

在复杂设计时,会将电路分成各个小模块去做设计,最终还需要一个大模块将这些小模块包括进来,对外呈现都是大模块的接口。此时,这个大模块就是顶层实体(TOP level entity)。如果设计中只有一个模块,那么这个模块就是顶层实体。

图6 :指定工程位置、工程名称和顶层实体的界面

将工程的位置指定到之前我们准备好的文件夹(qprj)中。点击编辑框后面的三个小点,进行文件搜索指定。

工程的名称就是采用之前我们做的设计文件夹的名字,这个名字可以是任意的,笔者建议和文件夹保持一致,因为当初建立文件夹时,就是选择用工程的名字。直接输入工程名称and_gate2_1即可。

顶层实体的名字会自动出现,与工程的名字保持一致。我们也可以重新指定一个新名字,笔者建议与工程名字保持一致。

图7 :选择建立的工程的类型

选择空白工程(默认空白工程),点击Next。

建立工程时,我们可以直接向工程中添加已有的文件。一般我们选择什么都不添加,后续设计中,如果有提前做好的文件,也是选择什么都不添加。建立完工程后,依然可以向工程添加文件。

FPGA设计最终是要落实到芯片内部,在这里要选择对应的芯片(自己手里开发板的FPGA芯片)。芯片的型号在FPAG的芯片上有描述,如果芯片上看不清楚,或者芯片在被其他东西挡住,可以查看开发板的资料,一般都有介绍。笔者手中开发板的FPGA的型号为EP4CE10F17C8N。后续所有的选择将按照笔者手中的型号进行设计和选择,读者不相同的,请自行改动。如果暂时还没有开发板的读者,可以跟着笔者选择继续下面的步骤(没有开发板的话,后面有一些步骤是做不了的)。

选择时,首先选择对应的系列。

图10 :选择芯片所对应的系列

选择对应的系列后,可以看到下面的器件列表(此列表的窗口是可以拉大的,可以直接扩大整个界面)中,就出现很多的器件。呈现的器件都是按照一定的规律进行排列的,可以很快的找到自己的芯片,然后单击选择芯片(先不考虑为什么没有EP4CE10F17C8N,而选择EP4CE10F17C8)。

如果每次新建工程都是这样去寻找芯片的话,是有一定的累人。好在这个软件给我们提供了筛选面板,我们可以把筛选条件输入进去。

设置好筛选条件后,器件列表中就只有四个器件了,很容易就可以找到我们这款芯片。

筛选条件中有封装类型、管脚数目和速度等级。那么我们怎么知道自己手中的芯片的这些信息呢?

答案都隐藏在芯片的名字中。笔者手中的是CYCLONE IV系列的片子,下面就介绍一下这个系列的命名方式。

如果是其他系列,请自行参考所对应的芯片手册。

通过观察图4-13,EP4CE10F17C8N中末位N,只是表示无铅封装,和具体内部机构没有任何关系。故而选择不带N的,也是可以的。

注意无论是哪种方法,最后找到自己想要的芯片后,一定要点击它,选中它。

Quartus软件是一个综合工具,他可以关联一些其他的工具协助设计FPGA。在这里我们在simulation一栏,工具选择modelsim-altera,格式选择verilog HDL。其他保持默认。

图15 :工程向导配置总结

这个总结显示出在新工程向导中,我们所做的所有的设置。大家可以检查一下,如果发现那一项和自己的要求不一致,就需要点击back,修改后,在回到此步骤。

点击Finsh,完成工程创建。

图16 :工程建立完成后,工程向导界面

工程建立完成后,工程向导界面显示出选择的器件和指定的顶层实体。

图17 :建立完工程后的qprj文件夹

db文件夹为基础数据文件夹。

Quarus 的版本很多,如果用一个版本建立的工程,用另外一个版本打开可能会出一些bug,所以建议采用使用已建好工程的版本打开。可以使用记事本的方式打开.qsf。

图18 :qsf文件的一部分

通过查看.qsf文件,可以了解到工程的最初用什么版本建立,最后用什么版本打开(打开时,建议采用最后的版本打开)。

打开工程的方式,不建议采用双击.qpf文件。有时间一个PC上面,会有多个quaruts软件的情况,如果直接双击,就会采用某一个版本打开,这不一定是我们想要的。

建议打开工程的方式,首先查看应该选择的版本,启动对应的版本quartus软件,点击File,选择Open project(不要点击Open),找到工程,启动。

图19 :打开工程的途径

当建立完工程后,就可以输入设计。输入设计的方法有三种:原理图输入、HDL代码输入、原理图和HDL代码混入输入。

原理图输入就是以现有模块级联的方式实现功能。

图21 :新建文件列表

对于新建的bdf文件立刻进行另存为。

另存为到qprj文件夹。Bdf文件是只有quartus软件才能认可,不具有移植性,把它放到工程文件夹中。

图23 :保存路径和名称

点击保存。特别注意保存路径和保存名称。不要盲目直接点击保存,一定要再三确认。

在BDF文件的空白位置,双击,打开添加原件的窗口。

图24 :打开添加原件的窗口

图25 :添加二输入与门

鼠标会拖动一个二输入与门的符号,此时点击鼠标左键,一个二输入的与门就放置在文件中了。

图26 :放置的二输入与门

当添加完二输入与门后,也就意味着芯片内部的设计完成。现在需要将内部设计与外部相连接。芯片与外界相连接靠的是IO。

双击空白处,选择input(输入),点击ok。添加输入IO。

二输入与门,要有两个输入。同样的方法,再次添加一个。同时也要有一个输出,选择output(输出)(在input下面),添加一个输出。

图28 :添加两个输入和一个输出

将管脚和二输入与门进行相连接。

把鼠标移至需要连接线的地方时,鼠标会自动变为十字状(不是十字状箭头),并且右下带有一个“7”形的导管。然后按住鼠标左键进行导线的引出。当连接到另外一个接口时,

就会出现一个小方框,证明已经连接上,此时释放鼠标左键即可。

图29 :连接完成图样

此时默认两个输入的管脚的名字为pin_name1和pin_name2。在设计时,二输入与门我们将其输入命名为一个为A,一个为B,输出为Y。

图31 :修改管脚名称

将其他两个管脚也做对应修改。

图32 :修改完管脚名称的图样

此时点击保存(ctrl + s),使用原理图的输入方式就完成了。

用计算机语言设计一个数字电路系统,其实质就是用一种语言描述一个硬件模型,因此这样的语言又称为硬件描述语言(Hardware Description Language),或使用缩写HDL。虽然现在HDL已经有多种语言版本,而且还在发展中。但是在本书讨论的HDL仅包括现在最常使用的Verilog HDL和VHDL两种语言系统。

目前在国内做FPGA设计的公司中,使用Verilog HDL占据大多数,故而本书以Verilog HDL为主,在后续的章节中,专门设置一章来讲解VHDL。

新建完成后,立刻另存为,保存到rtl中。hdl文件的移植性比较高,无论在哪个平台都是通用的。

保存时,注意名字和保存路径。Verilog文件的后缀为.v。

建立完,verilog HDL文件后,就需要输入二输入与门所对应的verilog代码了。

Verilog 语法和C很相似,学习起来比较容易。下面我们按照做电路的方式讲解verilog语言。

做电路的话,首先需要拿出一块打的面包板,剪出合适大小的一块。相当于圈了一个地方,做设计只能在这块区域内。

对于verilog语言来说,需要用module和endmodule圈出一个区域,设计代码只能在这块区域中。Verilog语言区分大小写,我们一律采用小写。module和endmodule是verilog的关键字,在综合器中会变蓝。如果endmodule没有变蓝,请多打一个回车或者空格。

当剪出合适大小的面包板后,需要其上面写一个名字。后面应用也好,说起来也好,好歹有个名字。

在verilog中命名的话,需要遵从一定的规则。由字母、数字、下划线构成;建议字母开头;不能够与verilog的关键字相同;命名是要有一定的意义。

对于module name来说,一般还有一个要求,与文件名称保持一致。那么此时我们要做二输入与门,文件名称是and_gate2_1。要求module name也写成and_gate2_1。

当对面包板命名后,需要给它添加输入和输出的端口(合理的布局接口)。

二输入与门有两个输入,一个为A,另外一个为B;一个输出为Y。在verilog中,布置接口的方式有两种。

图37 :布置接口的第一种方式

图38 :布置接口的第二种方式

Verilog布置接口的第一种方式为1995标准,第二种方式为2003标准。目前大多数平台都可以支持这两种方式。笔者建议用2003标准。

端口列表中,描述端口时,用逗号隔开,最后一个端口后面不加逗号。在端口列表的括号后面有一个分号。

对于描述端口来说,有最基本的四项:方向、类型、位宽、名称。

input表示输入,output表示输出,inout表示输入输出。

类型中比较常用的有两种:一种是wire,另外一种reg。wire类型时,wire可以省略不写。另外input必须是wire类型。笔者建议wire不省略,都写上。

在做电路时,位宽表示有几根线。有时候为了方便,会将同一种类的线进行同时命名,此时就需要用到位宽。例如:5位的地址线。可以单独命名5次,但是比较麻烦。位宽用中括号括起来,例:[3:0],[3:1],[2:5]。如果位宽为1的话,省略不写。笔者建议位宽的右侧为0,左侧为位宽减一。

名称就是为这个输入命名了一个名字。命名时要遵从verilog命名规则。

在做完端口后,需要在面包板上做出符合功能的设计,然后用连接线将设计和输入输出管脚相连接。

二输入与门的设计是需要在中间放一个组合逻辑电路二输入与门。

Verilog中,描述组合逻辑的第一种方式是利用assign语句进行描述。

图39 :assign语句描述二输入与门

assign语句要求被赋值变量(Y)为wire类型,中间采用阻塞赋值(=)的方式,最后面是赋值表达式,在verilog中,算术与用&来表示(后续介绍算术运算和逻辑运算的区别)。

至此,二输入与门的HDL输入就完成了。

  • 原理图和HDL代码混入输入

在复杂设计时,我们可以用HDL代码生成底层模块,用原理图的方式,将底层模块进行连接。此方式在后续的章节中介绍。

当设计输入完成后,需要对设计进行综合分析,同时也检查一下其中是否存在错误。可以单击综合分析按钮,可以双击综合分析选项,也可以利用快捷键(Ctrl + K)。

图40 :综合分析按钮

图41 :综合分析选项

进行综合分析时,有时会提出一个提示:

图42 :某文件被改变,是否要保存

出现上述提示,就证明我们在设计时,修改了某些文件后,没有点击保存。此时点击Yes即可。但是这是一个非常不好的习惯,建议大家修改完任何设计都要及时保存。

图43 :Task的综合分析前面的进度条和后面的已用时间

经过一段时间后,会出现错误。

出现错误后,可以观察quartus 的下部massage窗口。

报错信息为:错误(12049):无法将实体“and_gate2_1”的重复声明编译到库“work”中。报错的原因是我们在本工程中声明了多个and_gate2_1,在电路中是不允许出现多个电路模块是同样的名字。

在设计时,为了演示原理图输入和HDL代码输入,工程中存在原理图输入的and_gate2_1和HDL代码输入的and_gate2_1。

在工程向导界面,选择Hierarchy,选择Files。Hierarchy为结构,显示工程的电路模块的结构;Files为文件,显示工程中存放的所有文件。可以利用此方式查看,工程中是否存在多个and_gate2_1。

在Files界面中,可以发现工程中确实存在两个and_gate2_1。

随后在确定界面点击Yes。

将其移除工程后,就会只剩下一个原理图输入的and_gate2_1。然后进行综合分析,等待结果完成。

图50 :综合分析成功

如果中间有错误的话,请参考输入设计中的原理图输入,查看自己的步骤是否正确。

图51 :从工程中添加或移除文件

选择编辑框后面三个点。

图52 :寻找添加文件

图53 :添加选中文件

注意添加文件的路径,and_gate2_1.v是存放在rtl文件夹中。

然后点击apply,OK。就添加成功。

图54 :确认添加文件

点击综合分析,确认综合分析成功。如果综合分析失败,请参考输入设计中的HDL输入,查找错误的地方。

无论是哪一种输入方式,综合分析成功。双击RTL视图选项,打开RTL视图,查看quartus综合出的电路模型。

图55 :RTL视图的选项

在RTL视图中,综合出来的电路图,只是电路模型而已。在FPGA中是没有与门的,有的只是LUT等效的二输入与门电路。

图56 :RTL视图的二输入与门

综合分析成功后,会产生一个报告。

图57 :综合分析报告

在报告中,可以看出综合状态、软件信息、工程版本信息、顶层实体、器件系列、目标器件、时序模型、逻辑单元数量、寄存器数量、管脚数量、虚拟管脚数量、存储器大小、嵌入式乘法器的使用个数、锁相环使用个数。

只是设计了一个二输入的与门,所以使用一个逻辑单元,3个管脚,其他都没有涉及到。

在综合分析完成后,对于简单的设计,通过查看RTL视图中综合出来的电路模型,就能够知道所做设计是否正确。但是对于复杂的设计,电路模型比较复杂,无法直接判断是否设计正确。

如果直接将不知道正确与否的设计加载到板卡中,很多时候无法通过结果直接看出来是否设计比较严谨。所以要求设计者在软件环境下对所做设计进行一定的测试。

仿真是利用模型复现实际系统中发生的本质过程,并通过对系统模型的实验来研究存在的或设计中的系统。

当所研究的系统造价昂贵、实验的危害性大或需要很长的时间才能了解系统参数变化所引起的后果时,仿真是一种特别有效的研究手段。

仿真其实就是模拟实际情况。对于电路来说,就是给予合适的输入,观测输出是否和设计时所预想的相同。

图58 :仿真的示意图

电路的输入、中间过程和输出,都是数字信号,用波形来表示比较直观。

在真正的电路中,是存在电路延迟的。在仿真时,如果加载的综合出来的电路模型,那么此时验证的内容主要是测试模型的逻辑功能是否正确,不考虑延时信息。这种仿真被称为功能仿真、RTL仿真、前仿真、前仿。

仿真的途径有两个,一个是quaruts 自带的仿真环境,一个是modelsim的仿真环境。

图60 :VWF初始化界面

图61 :插入节点或者总线

点击“list”。在“filter”中,默认选择的是“Pins:all”,当点击“List”后,“Nodes Found”的界面中会出现所有的端口。

图63 :查找需要激励和观测的信号

图64 :选择观测和激励信号

图65 :确定选择的信号

图66 :选择信号完成

A端口和B端口是二输入与门的两个输入,只要给A、B两个端口加载上合适的信号,观测Y端口的输出是否正确即可。

A端口和B端口一共只有四种情况“00”、“01”、“10”和“11”,只要将四种情况全部加载一遍即可。

利用鼠标左键,选中A端口信号一段或者B端口信号一段,利用上方的置1或者置0按钮,将输入信号做成下图所示:

图67 :设置输入信号

设置好输入信号后,点击保存,将其保存到qprj文件中。虽然其为仿真文件,但是此文件依然只是quartus软件能识别,可移植性太差。

图68 :选择运行功能仿真

图69 :运行功能仿真的过程

在运行中,如果有报错,请认真回顾上述步骤。

图70 :功能仿真结果图

通过分析上述结果图,容易得出结论:AB“00” – > Y“0”, AB“01” – > Y“0”, AB“10” – > Y“0”, AB“11” – > Y“1”。通过波形的图的分析,二输入与门的功能仿真是没有任何问题的。

利用quartus 自带的仿真器,可以支持原理图输入和HDL代码输入;可移植性不强;对于一些复杂的输入信号,利用这种驱动方式较为复杂;在企业设计研发中,很少有人会用这个工具。

Mentor公司的ModelSim是业界最优秀的HDL语言仿真软件,它能提供友好的仿真环境,是业界唯一的单内核支持VHDL和Verilog混合仿真的仿真器。

利用quaruts 自带的仿真器仿真时,是利用绘制波形的方式进行输入信号的驱动。但是这种方法移植性不好,无法在modelsim中充当激励。

在开发中用的比较多的方式是利用HDL的方式进行充当激励,modelsim软件会自动抓取HDL代码中的信号进行绘制波形,用于设计者的观测。

Modelsim的软件无法为原理图的输入方式进行仿真,所以要将HDL代码输入的方式添加到工程,将原理图输入的方式移除工程。在企业工程开发时,不建议使用原理图的方式输入,移植性太低。所以在后续的章节中,将全部采用代码的输入方式进行设计。

新建一个verilog文件,命名为and_gate2_1_tb,保存到msim中。这个verilog文件是当做测试文件的,命名时,建议名字设置成为被测试模块的名字,然后后面加上“_tb”。tb为testbench的简写。

`timescale是verilog中定义时间标度的关键字。1ns/1ps中的1ns表示时间的单位,在veirlog中不支持直接写出单位,例:5 ns,ns等时间单位是不被识别的。当定义了1ns为时间单位后,表示时间时,可以直接写出,例:表示10ns时,可以直接写10即可。1ns/1ps中的1ps表示时间的精度,由于精度的存在我们可以写小数。例:表示5.5ns时,可以直接写5.5。但是也正是由于精度的存在,小数不能无限制向下描述。例:表示5.523ns时,可以成5.523,如果表示5.258963ns,那目前的精度是到不了这么精确的。精度是1ps,因此小数的位数最多能有三位。在设计中,很少用到比ps还要精确的单位,所以一般的时间标度都是1ns/1ps。

在tb文件中,是没有端口的。在测试时,输入的信号都由内部产生,输出信号只要引出到内部即可,仿真器会自动捕获。所以tb的模块是没有端口的。

在测试文件中,需要将被测试元件例化进来。例化的方式如下:

在例化时,首先是模块名称(and_gate2_1),后面是例化名称,这个名字可以任意名字,笔者建议例化名称要和模块名称有一定的关系,笔者采用模块名称后加上_inst,表示例化的意思。后面的括号是端口列表,每一个端口的前面加上一个“.”,后面加上一个“()”,此时表示这个端口可以连接线了,连接线放到“()”里面就是连接上了。

对于连接线来说,命名也是任意的,笔者建议连接线的名字和要连接的端口的名字要有一定的关系,笔者采用端口名字前面加tb_,表示tb中的连接线。

所有的连接线都需要提前定义。在定义时,都可以采用“wire”类型(后续会有更改)。

当例化完成,连接线定义和连接完成后,就需要开始测试了。而测试就是给模块的输入赋值,观测输出是否正确。

在测试时,我们需要顺序性的给出激励,verilog提供了一种比较简单的方式“initial”语句。在这个语句中,我们只需要顺序性的给出激励就可以了。“#”表示延时,延时在verilog中是不可综合的,但是在仿真中,是存在的。

“1’b0”中的1表示位宽为1,’b表示后面的数码为2进制,0表示数码为0。

在赋值时,建议采用位宽+进制+数码的方式进行赋值。

Verilog中,begin end表示中间的语句是一个整体,verilog和C类似,每一个函数或者语句下只能包含一个语句块。C语言中采用大括号,verilog中采用begin end。

上述的initial语句块中,描述了tb_A和tb_B被先赋值00,延迟20ns,被赋值为01,延迟20ns,被赋值为10,延迟20ns,被赋值为11。

Verilog语法规定,在initial语句中被赋值的变量,应该定义为reg类型。

图73 :定义变量(连接线)

在写完testbench后,可以综合分析一下。保证没有任何的语法错误。

在仿真之前,需要指定仿真文件。

添加成功后,点击ok。

在设置完成后,进行综合和分析。

稍微等一段时间,modelsim软件会自动启动。如果没有启动的话,并且报错的话,请查看modelsim的关联位置。

图87 :配置关联路径

在modelsim-Altera中,看看路径是否正确(这是笔者的安装路径,请自行查看自己的安装路径),注意最后那个“\”,很多的系统中,没有它也是不对的。

配置完成后,点击ok。

重新点击RTL 仿真。

在wave界面中,点击一下鼠标左键,就会出现一个黄色的光标。

图89 :放大缩小的图标

在全局缩放前面一个为缩小,后面一个为放大。两头的两个图标,暂时不考虑。

全局缩放后,所有的波形都显示到wave窗口中,经过分析,设计正确。

此时就可以关闭modelsim软件。

输入设计后,经过综合和分析以及RTL仿真后,证明设计的逻辑功能是没有任何错误的。但是设计是在芯片内部进行实现的,如果想要发挥功能,势必要与外部的逻辑电路进行相连接。

在上述例子中,设计了二输入与门。我们可以将两个输入绑定到任意的两个管脚上,将输出绑定到任意一个管脚上。经过下载后,可以在输入的管脚上加载电平,测量输出管脚的电平,验证设计是否正确。

在FPGA学习开发板上,大部分都会有一些按键和LED,这些按键就可以为输入提供高低电平,LED就可以检测输出的电平值。

所以最好的验证方法是,将输入的管脚分配到连接有按键的管脚上,将输出分配到带有LED的管脚上。

自己制作或者购买的开发板,都会有原理图。

图91 :四位按键的电路原理图

经过分析,key1的网络是直接连接到FPGA芯片上的;按键释放时,key1网络为高电平,按下时,key1网络为低电平。其他按键原理相同。

经过分析,LED1的网络是直接连接到FPGA芯片上的;当FPGA将LED1网络置成高电平时,LED是熄灭的;当FPGA将LED1网络置成低电平时,LED是点亮的。其他的LED原理类似。

不同的电路原理,后续会得出不同的结果。请认真分析原理。

通过查看各个网络与FPGA的芯片连接关系,就可以得出按键、LED电路与FPGA的线连接的IO。

图93 :外部网络与FPGA连接示意图

经过查看,两个按键分别选择M15和M16。LED选择G15。

图94 :打开管脚规划器

在对应端口的Location标签下的空白窗位置双击。

图95 :准备输入管脚号

图96 :输入对应的管脚号

输入完成后,点击关闭即可。

点击综合和分析,等待综合分析完成。

综合分析只是将外部的输入转换成为对应的电路模型或者对应FPGA的电路模型,我们可以对电路模型进行RTL仿真,来排除逻辑功能的错误。

在FPGA片内实现的话,就需要对模型进行“实地”布置,利用FPGA片内的资源来实现模型,并且要固定好位置和连接线。这部分操作称为适配,也被称为布局布线。

图97 :布局布线选项

双击Fitter即可进行布局布线。

布局布线后形成的报告有可能和之前综合分析形成的报告中的资源利用有所出入,最终我们以布局布线后的资源为准。

在真正的电路中,是存在电路延迟的。在仿真时,如果加载的综合出来的电路模型,那么此时验证的内容主要是测试模型的逻辑功能是否正确,不考虑延时信息。这种仿真被称为功能仿真、RTL仿真、前仿真、前仿。

在仿真时,也可以加载布局布线后的电路模型,那么此时验证的内容主要是测试模型的延时是否能够达到我们的要求。这种仿真被称为时序仿真、后仿真、后仿、门级仿真。

图4-89 :启动后仿真

直接默认选择,点击RUN。

图99 :选择时序模型

综合器给出了外部不同条件下的时序模型,这里先不叙述他们各自的作用,后续时序分析中,会专门提到各个模型的作用。

图100 :二输入与门后仿真波形图

在输入信号都变为高后,输出信号没有立刻变化为高,而是延迟了一段时间后,才变为高电平。

在二输入与门中,电路的延迟对于我们来说是可以接受的,没有任何的要求。在后续的一些复杂的电路中,就要求电路的延迟不能太大,否则就会影响电路的功能。

时序仿真在企业设计中用的不太多,对于时序的很多问题,采用静态时序分析来查看。所以在后续的设计中,时序仿真将不在重点叙述。

9 生成配置文件并下载

在布局布线后,需要将对应的结果下载到FPGA片内。对于模型来说是无法下载的,只有通过编译,形成某种文件才可以下载。

图101 :产生配置文件选项

利用下载电缆连接FPGA开发板和PC。Intel FPGA的下载器为usb blaster ,当连接到PC后,需要安装驱动。

将FPGA开发板通电。

打开设备管理器。在通用串行总线控制器的下面,观看有没有Altera USB blaster。如果有的话,证明已经有了驱动,不需要再次安装。如果没有Altera USB blaster,在其他设备中,观测有没有“其他设备”。如果没有“其他设备”的话,就需要认真的检查开发板的各个连接线的连接情况。

选中USB-Blaster,右击,选择更新驱动程序。

图104 :如何搜索驱动程序软件

选择“浏览计算机以查找驱动程序软件”。

图105 :浏览计算机上的驱动程序文件

点击“浏览”,按照图4-96中所示路径(quartus 的安装路径,读者请寻找自己的路径,后面的路径是相同的)选择文件夹,然后点击确定。

点击下一步,开始安装驱动。安装过程中,PC会询问是否安装,点击安装即可。

安装完成后,可以将下载线从PC上重新插拔一下。在通用串行总线控制器中就有Altera USB Blaster的驱动。

选择USB Blaster后,下载界面中显示出,USB Blaster。此时直接Start即可开始下载。右上角会有下载进度条。

如果下载界面没有可下载的文件,可以点击add files,在qprj中的output files文件夹中,有一个后缀为.sof文件。选择后,下载即可。

下载完成后,此界面就可以关闭。询问是否保存时,选择否即可。

当配置完成后,我们就可以进行验证。按下按键,分析LED的灯的状态。我们做的是二输入与门,它的真值表如下:

图109 :二输入与门真值表

通过记录按键和LED的状态,也会得到一组真值表。

图110 :二输入与门的按键和LED功能真值表

通过分析按键和LED的工作原理,可以化简真值表。

图111 :二输入与门的按键和LED化简真值表

通过分析两个真值表,确认功能正确。

不同的按键和LED原理,可以对应去分析。

大多数FPGA的内部实现是用SRAM等效出来的电路,SRAM是一种掉电丢失的器件。所以FPGA下载成功后可以正常运行,但是掉电后,FPGA会丢失之前配置的所有信息。

这种情况非常不利于产品的研发,设备断电时常有的事情,而断电后再上电,还是希望FPGA能够正常工作的。

正是由于FPGA掉电丢失所有信息,所以在FPGA的周边会配置一块掉电不丢失的存储器(flash),可以将配置信息存储到存储器中,FPGA每次上电后读取存储器的内容即可。

向flash中存储信息,需要将上述.sof文件转换为.jic文件。

图112 :转换配置文件

图115 :选择配置文件

图116 :生成文件成功

点击OK后,关闭界面即可。

重新打开下载界面。选择下载文件,点击delete。

图117:移除默认下载文件

下载此文件速度比较慢,请耐心等待。

下载后,FPGA不能够正常工作,需要断电后上电,FPGA就可以正常工作了。

以后每次断电再上电,都可以正常工作。

本文的1到9小节就是正常的开发流程。在此过程之外,还有可能会加入一些其他的流程,例:功耗分析、时序约束等等。

例如使用了如下命令会列出所有的文件:

使用什么命令,才能只显示顶层的目录列表。如下,我只需要如下的信息:

正则就是一个规则,用来处理字符串的规则
编写一个规则,验证某个字符串是否符合这个规则,正则匹配使用的是test方法

编写一个规则,在一个字符串中把符合规则的内容都获取到;正则的exec、字符串的split、replace、match等方法都支持正则

正则的创建方式以及区别

接下来八位 出生年+月+日 接下来的两位:没什么用 倒数第二位数字 奇数为男,偶数为女

捕获的内容是一个数组,数组中的第一项是当前正则捕获的内容,如果有小括号(),则第二项捕获的是小括号里的内容。

  • index:捕获内容在字符串中开始的索引位置。
  • input:捕获的原始字符串。如果没有匹配的返回null;

1、(懒惰性)捕获的特点:执行一次捕获一次,每次执行exec只捕获第一个匹配的内容。

  • 每个正则都自带lastIndex属性:正则每一次捕获在字符串中开始查找的位置,默认值为0;
    我们手动修改lastIndex的值也不会影响exec每次开始查找的索引。

如何解决只捕获第一个匹配(懒惰性)?

在正则末尾加一个修饰符"g"
修改lastIndex的值为上一次捕获内容之后的第一个字符的索引;加了g之后能保证下次捕获第二个匹配的内容;
通过加g 每次捕获结束后,lastIndex的值都会改变成最新的值,下次捕获从最新的位置开始找。如果没有找到符合的,则返回null;
如何解决exec执行一次捕获一次?

>用字符串方法方法实现多次捕获 >1、如果正则中加了修饰符g,那么执行一次就可以把所有匹配的结果捕获到 >2、如果不加g,那么只捕获第一次匹配的结果。返回一个数组,数组中是捕获的每一项; >在分组捕获中, >如果正则不加修饰符g, > - 那么match和exec一样,可以捕获到小分组的内容。 >如果正则加了修饰符g > - match只能捕获到大正则匹配的内容,而对于小分组匹配的内容是无法捕获到的; `注意`:由于test和exec的原理相同,如果加了g,都是修改lastIndex值来匹配或者捕获,所以使用test和exec都会修改lastIndex的值,所以使用exec捕获之前最好不要使用test; 用test也可以捕获符合的字符串 RegExp.$1//只能捕获第1个小分组里面的内容 RegExp.$2//只能捕获第2个小分组里面的内容 RegExp.$3//只能捕获第2个小分组里面的内容 RegExp.$1//只能捕获第1个小分组里面的内容 RegExp.$2//只能捕获第2个小分组里面的内容 ###所有的支持正则的方法都可以实现正则的捕获(一般都是字符串的方法) >字符串中常用的支持正则的方法: >- split:如果给的正则中包含小分组,那么会把小分组中的内容也捕获到; >?:只匹配不捕获, 只能放在小分组的开头。如果加在分组中,那么只会匹配该分组中的内容,而不会捕获 >计算是第几个分组的时候,我们从左向右找'('即可 ##replace(第二个参数支持直接在字符串中使用$1-9) >在不使用正则的情况下,每次执行只能替换第一个匹配的字符串,而且每次执行都是从字符串索引为0的位置开始查找 >`第一个参数为正则时`,正则捕获几次,就替换几次,换成函数也同理,正则捕获几次,函数就执行几次,函数中返回的是什么,就相当于把正则捕获的内容替换成什么。 >`第二个参数为函数时`,正则捕获几次,函数就执行几次,函数执行的时候还会默认的传入三个参数: >index(捕获内容在字符串中的开始索引) //如果正则中有分组的话 …………直到小分组的内容显示完 return 的是什么就会把正则每次捕获的内容替换成什么 需求:把每个单词的首字母替换为大写,zhu-feng-pei-xun当成一个单词 ###时间格式化字符串 //1、执行一次捕获操作,得到需要的六个结果 >我们把对象数据类型实现`把描述同一件事务的属性或者特征归纳汇总在一起,以此避免全局变量冲突问题`的方式和思想叫做:单例设计模式; >把描述同一件事务的属性或者方法存放在某一个命名空间下,多个命名空间中的属性和方法是互不干扰的

使用单例模式实现模块化开发

模块化开发:把一个复杂页面按照具体功能划分成几大块,然后由不同的人分别去开发,这种模块划分的思想就是模块化开发功能。

//=>项目主管(开发人员):公共模块
//=>陆永勇:搜索模块
//=>唐元帅:天气模块
//=>陈金广:频道模块
 //=>在当前的命名空间下调取其它命名空间的方法:指定好对应的命名空间名字即可,使用 [NameSpace].[property] 就可以操作了
 //=>调取本模块中的一些方法,可以直接使用THIS处理即可:此方法中的THIS一般都是当前模块的命名空间

基于JS高阶编程技巧惰性思想,来实现的单例模式,并且可以把一些常用的设计模式(如:命令模式、发布订阅模式、promise设计模式等)融合进来,最后清晰的规划我们的业务逻辑代码,方便后期二次开发和维护,这种设计思想综合体就是高级单例模式,也是最常用的。

类:对象的具体细分(按照属性或特性细分的一些类别)
实例:某一个类中的具体事物

Number:每个数字或者NaN是它的一个实例
Object:对象类,每个对象数据类型都是它的实例

Function:函数类,每个函数都是它的一个实例

元素对象或者元素集合的类

2.实例创建方式(构造函数方式)

1、对于引用数据类型来说,两种创建方式是大致相同的,只不过,两种方法创建的语法不同。

两种创建方式在核心意义上没有差别,都是创建Array这个类的一个实例,但是在语法上是有区别的
1、字面量创建方式传递进来什么,都是给数组每一项加入的内容
new Array(10):创建一个长度为10的数组,数组中的每一项都是空
new Array('10'):如果只传递一个实参,并且实参不是数字,相当于把当前值作为数组的第一项存储进来
new Array(10,20,30):如果传递多个实参,不是设置长度,而是把传递的内容当做数组中的每一项存储起来

2、对于基本数据类型来说,字面量方式创建出来的结果和实例方式创建出来的结果是有一定区别的,从严格意义上来讲,只有实例创建出来的结果才是标准的对象,数据类型值也是标准的基本数据类型,也是标准的内置类的实例;对于字面量方式创建出来的结果是基本数据类型的值,不是严格的实例,但是由于JS的松散特点,导致了可以使用 内置类.prototype上提供的方法;

使用构造函数方式,主要是为了创建类和实例的,也就是基于面向对象思想来实现一些需求

在JS中,当我们使用new xxx()执行函数的时候,此时的函数就不是普通的函数了,而是变为一个类,返回的结果叫做当前类的实例,我们这种new xxx执行的方式称之为构造函数设计模式

构造函数执行时new都干了些什么?

Fn(),执行的时候,是先把函数执行了,也就是后面的Fn()先执行,形成一个私有作用域,形参赋值变量提升,在变量提升完了之后,new操作符才起了作用,这个时候,浏览器开始创建一个新的对象,让Fn中的this指向这个新创建的对象,然后让这个对象的_proto_指向Fn.prototype,然后JS代码才开始继续往下执行,开始往新创建的对象当中添加每个实例私有的属性和方法。JS代码执行完成后,会默认返回当前创建的这个对象。

普通函数执行与构造函数执行的区别

构造函数执行的时候,也是先形成一个私有作用域,形参赋值,变量提升,在代码从上而下执行之前,构造函数有特殊的操作:浏览器会在当前的作用域中默认创建一个对象数据类型的值,并且会让当前函数中的this指向创建的这个对象。然后JS代码再执行,代码执行完成后,即使函数中没有写return,在构造函数模式中:浏览器会默认的把创建的对象返回到函数外面

  • 构造函数执行期间,既具备函数执行的一面,也同时具备自己独有的操作:在构造函数执行期间,浏览器会默认创建一个对象,这个对象就是当前这个构造函数(类)实例,函数执行完成后,浏览器会默认的把这个实例返回。所以new Fn()执行,Fn是一个类,返回的结果就是Fn这个类的一个实例

构造函数执行后面的‘()’问题

构造函数执行如果不需要传递参数,函数后面的()可省略,如new Fn()可写为new Fn

  • 如果要在new Fn之后直接调用实例的方法,则必须要加小括号,即必须写成new Fn().方法名

构造函数模式的返回值问题

构造函数模式中默认返回值是当前的实例,如果有return,返回分2种情况:
1、return 后面是一个基本数据类型的值,当前实例是不变的,例如return 100;我们的返回值还是当前类的实例;
2、return 后面是一个引用数据类型的值,当前实例会被返回的值给替换掉例如return {name:"珠峰"}我们的返回值就不再是当前类的实例了,而是对象 {name:"珠峰"};

检测某一个实例是否属于这个类, 判断A实例是否属于B类

检测attr是否是object的属性,不管是私有属性还是公有属性只要存在,用in来检测都是true

attr是否是A实例的私有属性

  • 所有的类(内置类和自定义类都是)

基于构造函数模式的原型链模式解决了方法或者属性公有的问题,把实例之间公有的属性和方法写在当前类的prototype属性上;
1、每一个函数数据类型都有一个天生自带的属性:prototype(原型),并且这个属性的属性值是一个对象数据类型的值(<font 2、在浏览器给prototype开辟的这个堆内存中,浏览器天生给它加了一个constructor属性(构造函数),属性值是当前函数(类)本身;
3、每一个对象数据类型(普通对象、数组、正则、实例、protoype..)也天生自带一个属性:_proto_,属性值指向当前实例所属类的原型(prototype);
(IE中屏蔽了对象的__proto__属性,但是确实有,只是不让我们使用而已)
4、Object是JS中所有对象数据类型的基类(最顶层的类);

原型链模式中的this

原型模式中的this分两种情况:
2、 原型链中提供的私有(公有)方法中的this问题:
总结看执行的时候"."前面是谁this就是谁。具体操作步骤如下

  • 1、需要先确定this的指向(this)
  • 2、把this替换成对应的的代码
  • 3、按照原型链查找的机制,一步步的查找结果

如何往原型中批量添加属性和方法

让某个构造函数的原型指向我们自己开辟的堆内存,但是自己开辟的堆内存当中是没有constructor属性的,所以要往自己开辟的堆内存中添加constructor属性,属性值为当前构造函数本身;
缺点:重构原型后,会导致之前添加的属性和方法都没有了,只能使用重构之后添加的属性和方法;

  • 不要忘了重构之后要添加constructor属性指向当前构造函数;
  • 内置类的原型不能重构,浏览器不允许我们这么做;
  • 重构原型之前生成的实例的__proto__属性值依然指向重构之前的原型,而不是重构之后的原型,只有在重构原型之后生成的实例的__proto__属性值才指向新的原型;

它是一种编程思想,让我们基于类和实例的概念来编程开发和学习。

把实现一个功能的代码,封装在一个函数当中,以后再想实现这个功能,只需要执行这个方法即可,不需要重复的编写代码,减少了页面中的代码冗余度,提高了代码的重复利用率,实现了低耦合高内聚的思想

一个类的多种形态:重载、重写;
方法名相同,参数不同,叫做方法的重载
JS中没有严格意义上的重载,如果方法名相同,那么就会保留最后一个方法,和传递的参数没有关系;
JS中的重载:根据传递的实参不同,来实现不同的功能。可以把这种方法叫做重载。
JS中的重写:子类重写父类的方法

子类继承父类的一些属性和方法

/继承父类之后,需要手动添加constructor属性

<font color=red>特点</font>:它是把父类中私有+公有的属性和方法的都继承到了子类的原型上(子类公有的)
<font color=red>核心</font>:原型继承是让子类和父类之间增加了原型链的连接关系,以后子类的实例想要父类中的方法,需要利用原型链查找机制一级一级向上查找来使用

  • 1、子类的原型指向了父类的一个实例,如果实例的某个属性的值是引用数据类型,那么我生成子类的一个实例f,我通过f来修改子类公有的这个引用数据类型的值,那么子类的其他实例也会受影响;
  • 2、不能向父类的构造函数传递参数。如果父类给其实例定义的私有属性的值跟传入的参数有关系,那么子类继承过来之后,所有子类实例的公有属性的值都是一样的,
//一般都把call继承放在子类函数体中的第一行,这样做的好处就是子类私有的可以替换掉继承过来的结果;

<font color=red>特点</font>:把父类私有的属性和方法克隆一份一模一样的作为子类私有的属性(父类公有的无法继承)

  • 子类公有继承了父类公有的
  • 子类私有继承了父类私有的
  • 而且子类还可以扩展自己的实例公有的属性和方法(扩展到创建的空对象上),而不影响到父类

Object.create():创建一个空对象,并把传入的参数当作空对象的原型;
<font color=red>原理</font>:把父类当作函数在子类中执行,修改函数中的this为子类的实例。用Object.create()创建空对象,并把传入的参数当作空对象的原型,把子类的原型指向空对象的原型。

//创建一个空对象,让传入的参数当作空对象的原型,然后让B的原型指向这个空对象; //为了保证构造函数的完整性,我们要给子类的原型手动设置constructor属性值 //以下写法兼容所有浏览器;

ES6中的继承相当于寄生组合式继承

注意:super()只能用在子类的构造函数之中,用在其他地方会报错 super(name,age);//super中的参数相当于把父类当作普通函数执行时传递给普通函数的参数 //下面为子类的私有属性 //下面为子类公有的方法 关于super的两种情况: 注意,使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。 只能在子类的构造函数中第一行写super(),此时就相当于Father.call(this);否则会报错(比如在子类的公有方法中执行super); 2、作为普通对象调用时 在子类的静态方法中(把子类当作对象添加的方法),指向父类。

a,b,他们同为类A的实例。因为A在闭包里,所以现在我们是不能直接访问A的,那如果我想给类A增加新方法怎么办?
这个时候就可以通过a.constructor.prototype 来给其所属类添加公有的属性和方法了

css中的盒子模型有两种,分别是 ie 盒子模型和标准 w3c 盒子模型

CSS3盒子模型与IE中的盒子模型

width和height不仅仅是内容的宽度,而是代表整个盒子的宽度(已经包含了padding和border),以后修改的padding或者border,只会影响盒子中内容的高度,盒子的大小不会改变

在网页的顶部加上 <font color="red">!DOCTYPE</font> 声明。假如不加 doctype 声明,那么各个浏览器会根据自己的行为去理解网页,即 ie 浏览器会采用 ie 盒子模型去解释你的盒子,而火狐会采用标准 w3c 盒子模型解释你的盒子,所以网页在不同的浏览器中就显示的不一样了。反之,假如加上了 doctype 声明,那么所有浏览器都会采用标准 w3c 盒子模型去解释你的盒子,网页就能在各个浏览器中显示一致了。

通过JS盒子模型属性获取到的结果都是不带单位的,而且只能是正 数(会默认的进行四舍五入),而且只有scrollTop和scrollLeft可读写,其他都是只读属性;

(只读属性,每次访问都会重新计算,最好用变量保存)
当前盒子可视区域的宽度和高度
可视区域:内容宽高+左右填充padding
和内容是否溢出,以及是否设置了overflow:hidden没有关系

  • 获取当前浏览器可视窗口(一屏幕)的宽度

offsetParent:当前元素的父级参照物(在同一个平面中,最外层的元素是里面所有元素的父级参照,和HTML层级结构没有必然的联系。一个页面中所有的父级参照物都是body。标准流中为body)

**offsetLeft:当前元素距离其父级参照物的内边框的左偏移量 **
offsetTop:当前元素距离其父级参照物的内边框的上偏移量
在IE8当中,是从当前元素的外边框距离其父级参照物的外边框的偏移量
当前元素的外边框距离父级元素参照物内边框的偏移量,父级参照物默认为body

scrollLeft/scrollTop:横向或纵向滚动条卷去的宽度/高度(只有这两个可读写)
存在最大最小值:最小为0,最大为卷去的高度/宽度 - 一屏幕的高度clientHeight

  • 获取当前元素所有写在行内样式上的值(只有写在行内样式上的才能获取到)
  • getComputedStyle(ele,当前元素的伪类一般为null),获取的是一个对象数据类型的值,如果需要获取某一项需要用点或者[]来访问获取结果的某个属性
  • ele.currentStyle (currentStyle是元素的一个属性,而不是一个方法)获取结果也是对象数据类型的,获取某一项也要通过点或者[]来访问;

这两个属性获取的结果都是字符串;

如何让一个盒子水平垂直居中

使用定位,需要知道盒子的具体宽高(兼容IE低版本)
使用定位:不需要知道盒子的宽和高(不兼容IE低版本)
如何让一个宽度不固定的块级元素居中?
首先让盒子先绝对定位,然后设置盒子的left和top值即可
top=(当前浏览器窗口的高度-内容的高度)/2+'px'

设定一个定时器,到达指定时间之后执行对应的方法(执行一次就结束了);

设置一个定时器,每间隔多少时间执行一次fn,直到定时器清除为止(执行很多次)

设置定时器时都会有一个返回值,代表当前是在浏览器中设置的第几个定时器(返回的是定时器的序号,不管是setTimeout还是setInterval,)只要遇到这两个其中一个,就算一个新的定时器,定时器的序号就会加1,即使清除了设置的定时器,也不会重新计算序号。
setTimeout是隔一段时间之后执行一次函数
setInterval是每隔一段时间之后都执行一次函数;

用clearTimeout和clearInterval都可以清除两个中任何一个定时器,传入的参数为定时器的序号;

为什么我们要手动清除定时器?

定时器也是一个函数,函数执行完成后,返回了一个基本数据类型值(定时器的序列号)。没有产生堆内存的话就会在空闲时间被销毁掉,但是为什么setTimeout不会自动销毁?
因为当创建一个定时器的时候,浏览器会同时开启一个监听者,当setTimeout执行完成后,监听者始终知道这个定时器的地址,所以不能销毁,我们要手动去清除定时器。我们手动清除的时候其实就是在切断定时器和监听者之间的联系;这样定时器就会被销毁掉;

定时器中的this问题

不管在哪执行,定时器函数中的this是window;
 
 //也可以用变量保存的方式来改变this

JS是单线程的,当前的任务没有完成,下面的任务是不进行处理的(同步的)

当前的事情没有完成,继续处理当前的事情,只有当前的事件完成了,才会去做下一件事情。(JS中大部分都是同步编程的)如for循环

规划一件事情,但不是当前马上去执行这件事,需要一定时间之后再执行,不会等到时间到了,任务执行完了,才继续完成下面的任务。而是把它放到等待任务队列中,同时开始计算等待时间,继续执行主任务队列中的任务,只有主任务队列中的任务都完成了,再到等待任务队列当中,看哪个先到时间了,就先执行哪个。如果主任务队列中的任务没完成,不会去执行等待任务队列当中的任务;

在JS中的异步编程只有四种:
1、定时器都是异步编程的
2、所有的事件绑定都是异步编程、
3、Ajax读取数据的时候,我们一般都设置为异步编程
4、回调函数也是异步编程

定时器是有最小等待时间的,即使设置为0也不会马上执行,等待时间短的先执行;

同步异步编程的核心原理

JS中有两个任务队列(存放任务列表的空间就是任务队列)
1、主任务队列:同步执行任务;(从上到下执行)
2、等待任务队列:存放异步的任务;
color=red>原理</font>:规划一件事情,但不是当前马上去执行这件事,需要一定时间之后再执行,不会等到时间到了,任务执行完了,才继续完成下面的任务。而是把它放到等待任务队列中,开始计时,继续执行下面的操作,只有主任务队列中的任务都完成了,再到等待任务队列当中,看哪个先到时间了,就先执行哪个,如果都到时间了,那么就看哪个等待的时间短,就先执行哪一个。如果主任务队列中的任务没完成,不会去执行等待任务队列当中的任务;

性能好,实现起来简单,CSS能解决的动画绝对不用其他方式。
不兼容大部分IE或者其他低版本浏览器(移动端的动画一般都是基于CSS3来完成的)

在JS中实现动画常用的有:
1、使用定时器驱动动画,
而所谓的canvas动画基本上也是基于这两种方案完成的(canvas本身是绘图)

非常早的动画处理方案,想要实现动画,需要把这部分制作成Flash影片,然后用Adobe Flash Player插件来播放;现在一些简单的DOM动画都告别了flash的时代,影片的播放也可以基于H5中的audio或者video完成;

JS中基于定时器的动画运动的两种形式

1、限定步长,不限制运动时间;
2、限定运动的总时间,不限制每一步走的长度;公式:

  • time为定时器走一次的时间,zTime为运动总的时间
  • 元素当前所在位置=(time/zTime*总路程+起始位置)

1、此方法只能测试运算时间在1毫秒以上的,

//需要测试消耗时间的代码

2、此方法可以测试运算时间在1毫秒以下的

//需要测试消耗时间的代码

把一个函数当作实参值传递给另外一个函数,在另外一个函数中执行这个函数,这种处理机制就是回调函数机制;
什么时候用到回调函数?
凡是在某一个函数的某一个阶段需要完成某一件事情(而这件事情是不确定的),都可以利用回调函数机制,把需要处理的事情当作值传递进来

//也可以使用以下默认方式(不过不严谨:可能传递的不是函数)

1、回调函数可以被执行多次;
2、还可以给callBack()传递参数值;
3、还可以把回调函数中的this进行修改;
4、我们还可以接收回调函数执行返回的值;

回调函数中的this问题

我们一般执行回调函数的时候都是直接执行回调函数,没有指定执行主体,所以默认情况下都是window;

定时器中的this问题

只要不指明执行主体,定时器中的this就是window.(严格模式下也是window);

关于Each循环方法的封装

类似于JQ中的EACH方法,我们需要支持对数组,类书组,纯对象的遍历任务
需要支持对原有数组的修改(回调函数中的返回值,可以修改原来数组中的某一项值)
在遍历中,通过回调函数返回值,来结束当前正在遍历的操作(回调函数中返回false,我们应该结束对数组的遍历)

(详细内容请参考阮一峰大神的的ES6入门:)

我们之所以可以使用babel命令,是因为在全局环境下会生成一些xxx.cmd的文件,而这里的xxx就是可以在doc窗口中执行的命令
执行babel命令后,可以完成一些编译或者其他任务,原因是执行babel命令后,会自动加载处理任务的文件;
配置.babelrc文件,安装在一些语言解析包
我们需要把.babelrc文件配置在当前项目的根目录下
注意:在电脑上不能直接创建没有文件名的文件,但是可以在webS中new->file来创建,或者使用命令创建
1、babelrc这个后缀名在某些ws中是不识别的,我们需要设置关联
2、在这个文件中编写内容:

"presets":[],//存的是我们编译代码时候需要依赖的语言解析包 "plugins":[]//存的是我们编译代码时候需要依赖的插件信息 ],//存的是我们编译代码时候需要依赖的语言解析包 "plugins":[]//存的是我们编译代码时候需要依赖的插件信息

三、使用命令编译JS代码
基本上所有支持命令操作的模块都有一个命令
babel -d 把某一个文件夹中所有的JS文件中的ES6代码进行编译
babel -w 监听文件中代码的改变,当代码改变后,会自动进行编译

let 变量名=变量值
使用let创建变量和使用var创建变量的区别

  • 1、用var声明的变量会变量提升,用let声明的变量不会进行变量提升
  • 2、用let定义变量不允许在同一个作用域中重复声明一个变量(只要当前作用域中有这个变量,不管是用var还是用let声明的,再用let声明的话会报错:不能重复声明一个变量),但是可以重复定义(赋值)
i=20;重复赋值不会报错
  • 3、暂时性死区:在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
//下面也会报错出现TDZ //作为比较如果一个变量根本没有被声明,使用typeof反而不会报错。
  • 4、ES6语法创建的变量(let)存在块级作用域
  • 函数执行形成的私有作用域 除了有ES5中的两个作用域,ES6中新增加块级作用域(我们可以把块级作用域理解为之前学习的私有作用域,存在私有作用域和作用域链的一些机制)ES6中把大部分用{}包起来的都称之为块级作用域;

const声明的常量只要声明就必须赋值,而且变量的值是一定的,不能被修改;

  • 注意:并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

const声明的变量也存在暂时性死区,即只能在声明的位置之后使用;

JS中创建变量的方式汇总

    ES5中创建变量或者函数:存在变量提升,重复声明等特征;
    ES6中创建的变量或者常量:都不存在变量提升,也不可以重复声明,而且还存在块级作用域;
    class:创建一个类

按照原有值的结构,把原有值中的某一部分内容快速获取到(快速赋值给一个变量)

解构赋值本身是ES6的语法规范,使用什么关键字来声明这些变量是无所谓的,如果不用关键字来声明,那么就相当于给window添加的自定义属性;(严格模式下必须使用关键字来声明,因为严格模式下不允许出现不用关键字声明的变量;),如果解构不到值,那么变量的值就是undefined;

多维数组的解构赋值,可以让我们快速的获取到需要的结果

//数组中不需要解构的值可用逗号(,)空开,一个逗号代表空开一项

在解构赋值中,支持扩展运算符即<font color=red>...</font>,只要用了扩展运算符,就相当于新生成了一个数组或者对象,如果解构不到值的话,新生成的数组或者对象为空,而不是undefined,但是扩展运算符必须放在末尾

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
如果变量名与属性名不一致,必须写成下面这样。 真正被赋值的是后者,而不是前者。 如果要将一个已经声明的变量用于解构赋值,必须非常小心。 因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。 放在圆括号当中就可以避免 JavaScript

解构赋值中支持指定默认值

1、快速交换两个变量的值

2、可以接收函数返回的多个值

1、扩展运算符(注意,在解构赋值中,叫做扩展运算符,只能放在末尾)
只要用了扩展运算符,就相当于新生成了一个数组或者对象,如果解构不到值的话,新生成的数组或者对象为空,而不是undefined,但是扩展运算符必须放在末尾
 ...arg就相当于剩余运算符,可以把传递的所有参数都获取到,而且获取到的是一个数组
 
 ...arguments:这里的...就相当于展开运算符,把arguments展开,把里面的每一项分别传递给fn1当作参数,然后让fn1执行;
两种写法:1、表达式 2、函数体

1、箭头函数中不支持arguments,但是用 剩余运算...arg 代替了arguments,arg是一个数组,可以直接使用数组方法

//此方法的属性名为fn,属性值为一个函数,和下面的sum写法是一样的; /=>不支持arguments,我们使用ES6中的剩余运算符...来获取传递的进来的所有参数值(优势:使用剩余运算符接收到的结果本身就是一个数组,不需要再转换了) //=>也可以把FN简写成以下方式

2、箭头函数中的this问题,可以默认为箭头函数中没有this,在箭头函数中出现的this都是宿主环境中(即上级作用域中)的this,与箭头函数点之前的执行主体没有任何关系;

ES6中创建类和实例用class,创建出来的类不存在变量提升;
ES5中创建类和实例,以及如何禁止用户把类当做普通函数执行:new.target

//下面为子类的私有属性 super当作对象调用时,super就相当于父类的原型 //下面为子类公有的方法

返回布尔值,字符串中是否包含val所代表的字符串;

val是否在字符串的起始位置。

val是否在字符串的尾部。

三个方法都返回一个布尔值

这三个方法都支持第二个参数,表示开始搜索的位置。

上面代码表示,使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它 针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。

repeat():返回一个新字符串,表示将原字符串重复n次。

模版字符串,也是字符串,可以直接使用字符串中的方法;
模版字符串的空格和换行,都是被保留的,如果想要消除空格可以使用trim方法;

模版字符串中可以嵌入变量,需要将变量写在${}中,大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。

模板字符串之中还能调用函数。

forEach:不支持返回值,只是普通的循环遍历
for in:key输出的值为字符串类型,包括把数组当成对象添加的属性也可以遍历出来
for of:只返回具有数字索引的属性。这一点跟for...in循环也不一样。(不能遍历对象)

//for in是把arr当成对象遍历,i是属性名,包括arr的私有属性 //for of是把arr当成数组遍历,i是数组中的每一项 如果只想拿到索引,可用keys()方法 如果两个都想拿到,可用entries()方法

ES6中的模块导入和导出

//=>A模块(在A模块中导入B模块)
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...

  • 10月8日,今天是国庆后上班的第一天,我决定爬起来写晨间日记。这个动机是源自于昨天翻看印象笔记,读到了16年的工作...

  • 我一直觉得怎么样,才能最后让原本善良的她,变得面目狰狞,直到懂得她的际遇,我才渐渐体会,并且很庆幸她并没有跟我上床...

我要回帖

更多关于 怎样把文件夹里的文件放到桌面上 的文章

 

随机推荐