一 项目所得
1.1 心得体会
- 任何事物都要从最简单、最基本的构架入手,理解复杂的。——接触学习新东西时的指导思路。
- 做理论要做做想想,边停边走;做工程则要不断推进,因为不做不出来。工程就是靠时间堆出来的,不在于谁比谁聪明多少,而在于谁比谁用功了多少。
- 做工程思路:不要挑刺钻牛角尖,能用就行。要实用主义而不是理想主义,忌追求完美(比如正确解调需要信噪比至少10dB,则前端的同步就无须追求在5dB下性能最佳)!该细则细,该粗则粗。不要死抠理论,太专注细节往往看不到整体(又如同步性能设计再好,解调不出正确数据也是白搭,还不如多分配些时间精力给解调模块)。
- 不是做大项目就能学到东西的。恰恰相反,因为难以吃透大项目,所以只能按照别人的现有思路走,导致思维固化。而做小东西更具灵活性,可以充分发挥自己的想象力,寻求多种解决途径。外面做了几年大项目的人拿到新项目时,仍会感到无从下手;而老师接到新项目后,能立刻在脑海里构建出各个功能模块(“庖丁解牛”),原因即在此。
- 不要着急,一步一步来,别指望一口吃成胖子。很多时候工程做到一定程度了才能发现问题。你要做的就是通过项目学到能力,没必要尽善尽美,说实话项目本身对你今后的工作帮助不大。
- 自己的核心设计往往只占工程的很小一部分,大部分工作在于工程的集成。
- 把项目当成学习的好机会,而不是负担,才能有激情。
- 保留自己的想法,接受并尝试老师的想法而不是固守自己的方案——记住你是在“学习”,不是在“打工”!多向老师学习系统观和做事方法。老师的工程经验很丰富,整体化的系统思维值得认真学习借鉴。不要怕和老师讨论。伸手向老师要,老师才会给你。
- 请教老师前先说说自己的尝试方案,因为老师有经验一看便知如何入手;请教师兄们时则保留自己的方案,因为容易限制师兄们的思路。
- 不讨论、不提问、不向老师同学请教学习,即使做出东西又有何用?你只是发挥了自己的水平而已,能力并未得到提高!按自己的思维而不是借鉴别人的好想法做工程,只是交差,能力得不到提高!
- 工作后技术可以是二流的,但做人应该是一流的。
- 工程有忙有闲。忙的时候能忙得上,闲的时候能闲下来。一味地忙或闲都不好。
- 不要屡教不改,总犯同样的问题!
- 不要太专注所做东西本身,而更要体会做事的过程。
1.2 设计实践
- 数电思路 + 参考相关芯片设计
- 不要想着先做出来再说——应该先讨论确定方案。随时向老师汇报,排除系统原理结构上的设计失误!策略性问题及时与老师讨论,公式推导、器件操作等可自己验证的则自行搞定。
- 必须跳出自己的思维约束,把握整体构架,确定整体思路后再着手实现。最好先画出波形图,至少要有关键信号的时序关系。着手写程序时可以先写出大体框架,然后完成和完善子模块。
- 设计时问一问自己:是否从工程的思维入手?体会老师设计的控制模块的优点(先定整体时序,便于调试)。
- 对照Matlab理论波形与示波器实际波形。不要满足于软件仿真“正确”,要放到实践中测试!类似地,电源等电器件不要直接拿来用,要实测一下电压值。
- 设计要具有一定的通用性和灵活性,如便于扩充或调试。
模块通用性:基本思想是做通用可扩展、特定功能的小“模块”,而不要在后续阶段为加入功能而添加代码。
模块就是你给我输入,我给你想要的输出,仅此而已,尽可能不要让我进行控制。
模块化,减少耦合度,便于移植和调试。尽量模块化,可避免大的改动。
- 在阅读别人的程序时,遇到不确定有无的地方,不要冒然删减,应深入思考其存在的理由,深入画图分析(也利于别人理解和答辩申明)。
- 在不了解系统全局的情况下,先摸索后看手册(前提是避免危险的操作)。在摸索尝试中理解,切莫一开始就拿起手册从头看。先尝试各种可能来解决问题,不要轻易下结论!做完后再回过来理解,不要“轻信”资料。比如调时AD开发板时,与其研究透彻每个寄存器怎么设置后再着手调,不如边调边试。同样地,对于别人的东西既要吸收,又要质疑。不要因为那是别人做好的就认为OK了,实际上可能有很严重的设计缺陷。
- 利用别人的力量做出来才是水平。当你看到别人的东西并很快领悟为自己的东西才是水平。先模仿后发展。多向别人请教想法!尽量不要问具体问题,问思路才最重要。
- “反驳激将法”获取别人的想法,集思广益。多征求想法,记在纸上。即使现在想法不成熟,以后也可能有用。外行也能提出新想法新思路。因为我们的设计都是面向实际生活的,外行可能更清楚实际需要怎么做——当局者迷,旁观者清。比如我在用4点采样做同步,而数据却是8点采样,还煞有介事地试图找到精确的同步位置。假如说给搞图像处理的人听,人家的第一反应就是“这不可能”,因为同步“分辨率”不够。后来才在老师的启发下改用两个同步模块解决了问题。
- 思路灵活。做时定式,想时无定式。不要一根筋走到底,要考虑变通的方法。比如同步相关模块之前用定制的元件altshift_tags,tap
distance>=3,不得不进行时钟三分频。原先的想法是用时钟来换取LE的减少(假设定制元件比自己编写的节省资源——其实未必,考虑“通用性”),后来发现时钟远比LE重要。所以自己编写了移存器模块,可以实现清零复位,tap
distance等效为1,免去了时钟三分频。
【注】自己编写的三分频模块在Modelsim里会仿真出什么结果呢?比如clk_sn由75M时钟clk_sf三分频产生,那么仿真出来的clk_sn为42ns的时钟,而不是40ns(25M)——因为你所加的75M时钟仿真激励不可能很精确(13.333...ns)!
1.3 论文
- 每节开门见山,不要啰嗦。每章前面有摘要,后面有小结,承上启下,既有条理又凑够字数。
- 仿真要多,尽量多参数,反复仿多个,得出结论!仿真分析等尽量用1、2、3....条理化。
- 图表要多,摘要绪论及结束语一定要写好,评审老师哪有工夫看理论。
- 论文侧重理论。理论上下功夫的论文容易通过(“新名词”多)。许多评委都是理论出身,容易接受理论文章,故起评分高。偏工程类的论文除工程实现还必须要做理论部分,否则答辩时评委要问你除了“实现”还有什么创新点呢?你的工作量(工程)人家不一定认可啊。
- 将数据导出来在Matlab中观察(如画图)往往能发现新问题甚至新思路。
-
把FPGA仿真结果数据导入Matlab中,画出Matlab理论曲线与FPGA仿真曲线比较(大数据量)
-
提出一种想法后务必画出理论比较曲线(有比较才有说服力),过多的解释说明倒不如图表直观。
- 设计验证机制
- 不要写得太细,给评委提问的余地(大的方面让评委觉得你水平挺高,思路清晰,有创新性;从细处一想又有不清楚处)——否则评委可能会问预料之外的问题!
1.4 其他
勤写报告:1. 保持进度; 2. 用于提问讨论; 3.论文素材。
三种报告:
1) 给老师的报告:综合、轮廓、整体思路——图表化
2) 给自己的报告:设计算法、心得经验、调试细节
3) 给接收人的报告:系统、思路、如何实现及该做哪些工作,便于迅速上手。
以后发挥自己的能动性,每天向老师汇报进度——不是做了多少(太“虚”),而是具体完成了什么!
计划、讨论、汇报,讲给老师听。
有问题一定写文档问老师,不要想着口头说——这其实减少了仔细思考的过程。
二 测试调试
2.1 调试信念
不合理的现象背后,一定有合理的原因。只有抱定这一信念,才能解决调试中遇到的各种问题。与此相反的是:遇到不合理的现象,不是去寻求合理的解释,而是猜想有一些不可控、不可证明的原因。比如猜想综合工具有bug,电路的信号完整性有问题,芯片过热等。这样一想,问题往往不了了之,调试也就半途而废了。
遇到问题首先思考什么情况下会产生此类问题,寻求可能原因,而不是怀疑软/硬件工具有问题。
2.2 调试原则
- “遇到拦路虎一定要把它解决掉,否则日后碰到它你会更发怵”。不要回避问题,躲得过初一躲不过十五,迟早是要面对的——与其推倒重来,不如在初期就多加讨论解决!
- 从最简单的开始调试,先确定哪些模块正确,哪些有问题,而不是整个工程拿来跑,期望某次会“碰巧”正确(寻找问题所在,而不是回避问题)。
- 按规程走,不要寄托于修改“好”的工程以求毕其功于一役。
- 看起来最简单最不会出错的地方才最容易出错,反而复杂程序却很少出致命问题!
- 最基本的“常识”——不工作时各个寄存器(尤其是使能信号和计数器)都要复位!
2.3 调试技巧
- “隔离”(回路之间加入不同的延迟单元,测试时序是否满足)、“直通”(模块直连,如调制数据直接送往解调模块——bypass同步)。
- 设计时就应该写好测试比较模块。一是软件比较,如Quartus
II仿真结果与Matlab理论结果比较,或Modelsim结果(testbench)输出至Matlab中与理论结果画图比较。二是硬件比较,如可把待调制数据写入FIFO,当解调数据输出时同时读该FIFO并进行比较,如果对应位不同则输出ERROR高电平(此法也可用来判断两个模块是否实现完全相同的功能)。后者在上板子后可用逻辑分析仪捕捉ERROR信号(触发),注意“Trigger”|“Trigger
position”项选择“Post trigger position”,这样可以捕捉到出错前的信号。 - 高效率:多建工程备份(面积换速度),这样可以立刻调试正确版本。
- 将已经基本调试成功的工程备份至另外的目录,并且每个工程属性改为“只读”,防止无意中误操作修改了正确版本。——保留备份!
- 当前正确工程备份后,在此基础上逐步添加新模块。
- 及时给程序加注释,便于理解;及时写调试文档,重点记录错误点,避免重犯(还有一种情况就是后面想到某个曾被部分否定的方法,却不知道哪里需要注意)。
- 将中间结果尤其是关键信号的波形截图保存在word中,可以方便对照!
- 逐个模块调试,Modelsim大数据量仿真(尤其注意两段数据之间的使能信号及计数器的状态),示波器观察波形(周期,相位...尤其注意不要出现长时间的直流或跳变)。找到出错的地方,看其与哪些变量直接相关,然后由出错位置出发,观察这些变量甚至它们的相关变量,逆向一步步寻找出错原因。
- 用已知在别人机器上正确的工程去检验自己的软件环境,而不是从头新建工程trial and error!
- 先从最简单的功能,最简单的数据(如方波)做起。先测试最基本的信号(如时钟)。输入特殊值观察规律。
- 若某个reg信号在仿真时波形不正常,可试着将其设为输出output(改变布线)重新仿真。
- 合理地使用拼接符可以提高程序的可读性和可维护性。使用拼接可以把多个信号的某些位拼接起来进行运算操作。它使用简单,方便阅读,感觉不错。特别对应物理实现时就是将相关的位线对应连接起来即可,物理实现比较简单。
2.4 易错处
- 仿真输入数据有误或不合理。
- 仿真时序有问题,包括时钟周期、时钟相位对齐、时钟与使能信号及其指示的输入/输出之间的位置关系等等。
- 变量位宽不够,导致溢出。类似的还有FIFO存储空间设置过小。
- v文件中参数值修改后,对应的bdf文件中符号参数值却未改变。
- 例化模块时经常位置、端口关联混用,导致出错。
- ……
2.5 特别注意
- 仿真时特别注意时钟,有没有加时钟或给时钟脉冲,或者时钟周期是否是预期的(这点上的粗心往往导致仿真“不正确”)。
- Modelsim仿真时,所有的寄存器必须有清零/复位处理(如Clr),否则默认值为不定态(Quartus中默认值为0)。
- 将上级模块的输出波形复制作为下级模块的输入时,要特别注意数据是否正确复制,以及与时钟的相对位置是否可靠,大数据量的复制往往可能产生波形“失真”。
- 编译时把Message框拉开,观察warning,往往会发现程序中的一些小疏漏,及时停止编译,可节省大量时间。
- if判断语句必须注意判断表达式变量的起始值(复位)!所有使能/指示信号必须保证起始端符合要求!
- 先保证时序,后保证数据!时序错了,数据对了结果也是错的;数据错了但时序正确,调整时序即可。时序仿真结果不正确,除了怀疑不满足tsu时序要求外,还应注意输入数据是否正确无误!如果仿真结果不合乎要求,应对波形数据进行分析,往往会找到问题所在,不要盲目地修改程序,要知道编译比仿真更费时。分析数据时要首尾兼顾,还应注意数据对齐关系----往往这个影响内部计数器的工作,从而导致结果出错。除了比较数据外,还要确保数据长度正确无误。
- 接口规范:if...else配套,begin...end对齐,不要追求“新颖”的风格,要便于和别人程序对接。模块编译成功后生成直观的原理图符号.bdf文件,便于修改管理和交流。
三 其他
3.1 FPGA基本设计流程
1) 设计目标分析
主要确定芯片应当完成哪些基本功能,以及分析未来可能要增加的功能。注意选择器件时要留有足够的设计余量,以保证今后升级时单板不会因此而修改。
2) 功能模块划分
让设计层次分明条理清晰。另外在确定功能模块划分过程中,能使设计者在总体上考虑芯片的各个问题,发现一些比较深层次的问题。
3) 确定关键电路时序和模块间接口时序(总体方案)
事实上,在功能模块划分过程中就必须考虑时序方面的问题。有时,在确定设计目标时就得考虑关键电路时序。
在此前的设计过程中,我们是将项目设计进行功能分解之后,立刻着手子模块具体电路设计,最后再进行系统联调,出现问题了再去更改设计。这是在凑电路,而不是在设计电路。
设计电路,尤其是数字电路,最关键的一环就是:设计各模块间的接口时序,确定关键电路的时序。在做总体方案时,应深入到模块间的时序划分,关键电路的时序确定,并以此作为指导来进行设计。
“时序是事先设计出来的,而不是事后测出来的,更不是凑出来的。”
4) 具体电路设计(并写出详细设计文档)
在进行模块设计时,应先画出每个模块的原理结构,而后画出其工作原理时序图,在工作原理时序图的指导下进行具体电路设计。即“先时序后电路”的设计方法。在条件允许范围内,尽量用集成度高的电路语言直接表述。
设计流程:明确模块的原理功能-->画出时序图-->(映射为逻辑电路)-->用硬件语言描述时序图(或电路映射图)——先时序后电路!
5) 调试验证(仿真测试方案)
设计时就应该做好测试验证机制。我们目前所采用的验证机制为“隔离”(回路之间加入不同的延迟单元,测试时序是否满足)、“直通”(模块直连,如调制数据直接送往解调模块——bypass同步)。示意图如下:
其中延迟单元Delay Unit结构如下:
3.2 同步电路的设计规则(仅供参考)
- 尽可能在整个设计中只使用一个主时钟,同时只使用同一个时钟沿。主时钟走全局时钟网络。
- 在FPGA设计中,推荐所有输入输出信号均应通过寄存器寄存,寄存器接口当作异步接口考虑。
- 当全部电路不能用同步电路思想设计时(即需要多个时钟来实现),则可以将全部电路分成若干局部同步电路。尽量以同一个时钟为一个模块,局部同步电路之间接口当作异步接口考虑。
- 当必须采用多个时钟设计时,每个时钟信号的时钟偏差要严格控制。
- 电路的实际最高工作频率不应大于理论最高工作频率,留有设计余量保证芯片可靠工作。
- 电路中所有寄存器、状态机在单板上电复位时,应处在一个已知的状态。
由于时钟建立-保持时间的限制,FPGA设计中应尽量避免采用多时钟网络,或尽量减少时钟的个数。尽量避免采用多个时钟,多使用触发器的使能端来解决。
为了避免异步的时钟域产生错误的采样电平,一般使用RAM、FIFO存储的方法完成异步时钟域的数据转换,在输入端口使用上一级时钟写数据,在输出端口使用本级时钟来读数据,能够非常方便地完成异步时钟域之间的数据交换。
处理速度与面积问题的原则:向关键路径部分要时间,向非关键路径部分要面积。为了获得更高的速度,应当尽量减少关键路径上的逻辑级数;为了获得更小的面积,应当尽量共享已有的逻辑电路。
以寄存器为边界划分工作模块。在设计较大规模的电路时,分模块设计是必不可少的,在各模块通过之后再进行系统的联调。但由于在单模块调试和联调时布线资源的占用紧张程度不同,使得每个模块的输出无法保持与单独布线时相同,在联调时造成困难。如果每一个模块的输出端口都采用寄存器输出,那么即使在整体布局布线后,各模块的输出依然可以保证原来的时序,这使得联调的工作效率大大提高。加入这些寄存器也使得电路的可测性有所提高。
3.3 Quartus功能仿真流程
1) 将HDL文件或bdf文件设为顶层
2) Processing | Start | Start Analysis &
Synthesis及Start Fitter生成post-fitting节点
3) 建立vwf波形文件加入节点和激励
4) Simulation Tool | Functional | Generate Functional
Simulation Netlist | Start
修改v文件后再次仿真时,只需要Generate Functional Simulation
Netlist即可。而生成网表后,除重新编译源文件外,每次修改.vwf波形后都不必重新生成网表。
功能仿真时不计入器件延时,但经过寄存器后必产生一个周期的“延时”,这不同于器件延时,因为它本质上与时钟节拍相关,并非器件或布线固有延时。
Quartus仿真时最好让CLK有效沿采到输入数据的中间(稳定),这样无论Functional还是Timing仿真,都不会出现采样问题。如果输入数据在CLK有效沿变化,则Functional仿真会向后采,而Timing仿真会向前采。中间变量均向前采。似乎Quartus功能仿真时默认下降沿锁存数据,上升沿驱动输出,而modelsim均取上升沿。
End Time尽量设大一些,免得输出延时太大导致波形显示不全,而重新设置初始化文件既不方便又容易出错。当然End
Time大了,仿真时间会长一点,但总好过因End Time不够而被迫重新编辑波形。End Time比较大时,时间轴Time
bar的移动很不方便,可以在波形需要读取坐标处右键“Insert Time bar”。如果此前已有其他Time bar,则在新Time
bar(虚线)上右键“Make Master Time
bar”设为主坐标(实现),便可通过键盘“←”、“→”键调整坐标位置。记录下目标点的大致坐标,观察时直接拖动水平滚动条更快。直接在信号波形上双击,即可选中整个波形。选择一段波形,再鼠标滚轮放大,则以选中波段为中心显示放大。Tools|Options|Processing里可以设置仿真输出格式为vwf、cvwf或vcd。
v文件设为顶层后File-->Create/Update...即可生成symbol,但建议先做...Elaboration过滤语法错误。bdf文件更新及转HDL文件时无需设为顶层。改变源文件端口次序可使生成的符号易于连线。bdf生成的HDL文件智能化不够,底层模块修改参数后注意要在HDL中一定要改过来!
在图表文件里双击symbol可以打开其对应的v文件或bdf文件或Select对话框。如果同时还有对应的vwf文件加入工程,也会将其列入对话框中。
在Quartus里打开已锁定管脚的图表文件,有时会发现未显示管脚,这时需将该图表文件设为顶层文件(注意从Project
Navigator中剔除与其对应或重名的.v文件),再Start Analysis and Elaboration即可在图表文件中显示已分配过的管脚。
用MegaWizard定制元件,其实只需生成.v和.bsf两种格式的文件。除非定制ROM(需指定内存初始化文件),否则类似的宏单元没必要重复定制,其实只需例化名不同即可。比如经常要定制I、Q两路的FIFO,其实只需定制一个,在例化名里标识出I、Q即可。在Project
Navigator-->Hierarchy里宏单元名上右键“MegaWizard Plug-in
Manager”或直接双击,可打开宏单元编辑器修改定制元件。如果Hierarchy 不能展开,设置顶层并Start Analysis and
Elaboration即可。
Quartus
II停止响应时,直接关掉重启。如果上次编译后未做修改,则重新打开工程后不必再编译一遍。但不要因为编译后重新打开工程不必再次编译,而不必要地重启工程或重启Quartus。因为重启后再也无法Undo此前所做的修改了,而这可能带来非常大的麻烦。
3.4
Quartus中调用Modelsim做仿真
1) 功能仿真
将图表文件转换为HDL文件,然后将.v文件加入工程并设为顶层(同时剔除.bdf文件),在Settings里设置testbench路径,Start
Analysis and Elaboration无误后Run EDA RTL Simulation,否则会出现Module “xxx” is not
defined的错误。
2) 时序仿真
步骤基本同上,只不过最后要选Run EDA Gate Level Simulation。
仿真ROM时,要将.hex文件放置在simulation/modelsim目录下。
Modelsim中显示模拟波形:信号名上右键“Properties”-->“Format”-->“Analog”选项卡,Height设为50(默认17),Offset设为0,Scale设为0.1。
如果你的程序复杂,时钟频率高,测试向量多都会导致较长的Modelsim仿真时间,这是难以避免的。当然有一些小技巧可以在一定程度上缩短仿真时间(不考虑是否改善计算机硬件配置)比如设计的工作时钟频率为80MHz,即周期为12.5ns,原则上同步时序电路进行功能仿真的时间步进单位就是6.25ns,timescale为0.01ns因为同步时序逻辑一般都只使用时钟的上升沿
所以不需要严格要求时钟其他指标的具体实现 这样就可以把时钟信号设置为非1:1的占空比 即高电平6.5ns 低电平6ns 此时的timescale变为0.1ns
仿真速度可以加快一些 另外如果通过让testbench输出仿真结果文件而不是直接察看波形窗口的话 仿真速度可有较明显提高。
timescale在仿真的时候需要注意:
1) 如果不设置,modelsim默认1ps/1ps,vcs默认1s/1s
2) 可通过printtimescale打印出来指定模块的timescale,如下
1 Initial begin
2 $printtimescale(modulenume);
3 end
3) timescale 与`resetall成对使用
不要Load“projectname_run_msim_rtl_verilog.do”,否则会重新编译库。
3.5 SignalTap II
SignalTap II在加入Pre_synthesis
Node之前,只需知道有哪些Node即可,并不需要做Fitter和Assembler。只需Start | Start Analysis and
Elaboration即可,等加完Node后再做Start Compilation,这样可减少一次编译的时间。
post-fitting用于选择增量编译时,而pre-synthesis用于非增量编译时。
逮数据时只把Start触发沿设为rising
edge。先Run逻辑分析仪,然后瞬间按下清零键,就可以逮到Start变高时的数据。
使用Signaltap
II嵌入式逻辑分析仪时,注意采样深度不要设置过大,因为stp文件是要加入工程的,采样RAM占用了器件很大一部分memory bits。