http://blog.ednchina.com/codeman/200831/message.aspx
(1)浅淡逻辑设计的学习
学习逻辑设计首先要有项目挂靠,如果你觉得未来一段时间你都不可能有的话,接下来的内容你就没有必要再看了,花的时间再多也只能学到皮毛--很多细节的问题光写代码是发现不到的。而且要真正入门,最好要多做几个项目(这三年大大小小的项目我做有七八个),总线型的和数字信号处理型的最好都要接触一些,因为这两个方向的逻辑设计差异比较大:前者主要是控制型的,会涉及到状态机等控制逻辑;后者主要是计算型的,难点主要在对符号、浮点数转定点数、位宽等方面的处理上。
第二要有好的师父。这里说的好的师父并不是指画原理图画了几十年的老师傅,而是指曾在专业IC公司做过一段时间的人,好的专业IC公司可以接触国内外最新的设计思想,在他们的帮助下,起点就可以比其他人高不少,更重要的是你可以学习逻辑设计思想性的东西!如果你的师傅经常跟你说画原理图的好处,你还是重新找过师父算了--用原理图设计是一种很落后的方式,即使他们可能会说可以系统级设计(专业的IC设计公司系统级设计绝对是由方案保证的,而不会靠原理图这鬼东西)更为清淅。
第三要看一些好的资料。RTL级的书中《Verilog 硬件描述语言》、EDA先锋写的那几本书都还可以,还有不得不提的是cliff的一些paper(www.sunburst-design.com上有);验证方面入门可以看下《Writting Testbenches》, 提高可以看下snug(Synopsys的用户论坛,里面的文章基本上反映了业界的领先水平)的paper;系统级的可以看看《片上系统-可重用性设计方法学》。
第四要自己多总结,多动脑筋。逻辑设计的东西其实本质上的东西并不多:把RTL级的常用的D触发器、计数器、移位寄存器、状态机、多路选择器等基本的电路标准化、固定化;先做方案再写代码;设计时序;知道约束原理及怎么加约束;划分模块时知道怎么做到时序收敛;做验证的时候熟悉相应语言的行为级描述(这个肯定比RTL级好学多了)然后就是理解testbench的结构化设计。把这些东西的本质都搞清楚了做个合格的逻辑工程师应该是绰绰有余了,呵呵。
在接下来的部分我主要就第四点随便说点自己的经验,说的不好还请大家批评指正。
入门前
刚才开始接触逻辑设计很多人会觉得很简单:因为verilog的语法不多,半天就可以把书看完了。但是很快许多人就发现这个想法是错误的,他们经常埋怨综合器怎么和自己的想法差别这么大:它竟然连用for循环写的一个计数器都不认识!
相信上一段的经历大部分人都曾有,原因是做逻辑设计的思维和做软件的很不相同,我们需要从电路的角度去考虑问题。
在这个过程中首先要明白的是软件设计和逻辑设计的不同,并理解什么是硬件意识。
软件代码的执行是一个顺序的过程,编绎以后的机器码放在存储器里,等着CPU一条一条的取指并执行;因此软件设计中经常会带有顺序处理的思维。而逻辑设计则不同,我们设计的是数字电路,它是由很多很多的与非门及D触发器构成的,上电之后所有与非门和D触发器都同时工作,不会因为A触发器的代码描述在 B触发器之前A触发器就是先工作,事实上,RTL级代码的代码先后顺序在综合成网表文件后这种顺序就消失了,取代的是基本逻辑电路之间的互联关系描述;因此逻辑设计需要的是一种并发的思维,我们也需要用并发的思维去考虑电路的设计。
当然,我们设计的电路功能一般都有先后顺序的关系,如果这种顺序不能通过代码的先后顺序来实现,那么要怎么完成这一功能呢?在逻辑设计中,我们所说的先后顺序都是基于时间轴来实现:它的承载体就是时序逻辑,也就是那些触发器。
硬件意识的东西网上谈论的已经很多,这里就不再多说了。
其次就是要熟悉基本电路的设计。
基本的电路不是很多,也就是D触发器、计数器、移位寄存器、状态机、多路选择器、译码器等几种,所有复杂的电路都可由这些基本的电路构成。高手水平高的体现并不是他能写出一些很奇特的电路,相反,水平高是体现在他们总能将复杂的电路用这些很朴素的基本电路去描述。甚至,你会发现他们的代码基本上是由 if...else、case这些语句构成的,朴素的让你觉得奇怪。
我认为,初学者在入门的时候,对于基本电路的设计应该固定化、标准化,每种电路该用什么样的代码描述,应该要固定、统一,尽量少一些花哨的东西。说来这里我举个例子。
以前有几个朋友因为仿真有问题请我帮忙找问题。他们的代码写的很乱,出现了很多种稀奇古怪的电路,一看头都大了,只好建议他们按照标准的电路重新写下代码。结果过了半天,他们就和我说问题不见了。
所以,高手们喜欢用简单的代码是有道理的,电路的标准化和规范化可以减少许多稀奇古怪的问题,问题少了他们也就能在别人加班的时候回家多睡回觉,呵呵。总之,简单的、朴素的就是最好的。
最后是代码的规范化。
代码规范主要是代码书写、命名等规范。比如不能用TAB键空格、低电平有效信号命名时加_n(如rst_n等)、每行只能写一行代码等。这些东西网上也很多,这里只是强烈建议大家要严格遵守,像华为等公司如果代码不规范的话肯定是要打回去重写的。
入门
结合一两个小项目把上面所说的事情都做好后,差不多就可以进入入门的阶段了(要求稍微严格了一点点,呵呵)。
入门阶段要学的有:设计时序;理解约束的原理及如何加约束。
先谈谈设计时序。
设计时序是进行逻辑设计的基本要求:时序是设计出来的,不是仿出来的,更不是凑出来的。
很多人在做逻辑设计时喜欢一上来就狂写代码,写到一半后发现信号间的时序出问题了,只好推倒重来;好不容易反复了几次之后,通过仿真软件看了下,差不多要对了,于是再凑一下时序,竟然对了!但这个做法除了设计周期长外,代码的质量也难以保证,往往存在很多冗余的逻辑,甚至有一些隐藏着较深的bug。
为什么会出现上面的问题呢?因为我们设计的是数字逻辑,而信号之间的逻辑关系往往是比较复杂的,在内部信号很多的情况下,仅凭拍下脑袋就写代码肯定是不能理清楚它们之前的复杂的关系,所以出错在所难免。
正确的做法是我们要先对整个设计有一些规划--时时刻刻都要有设计时序的思想。设计时序最重要的是做好方案,这里说的方案绝不是只是摆几个框图在那里。我们在做设计的时候需要做总体设计方案、逻辑详细设计方案。这两种方案包括了很多东西,逻辑总体方案主要是一级模块的划分及接口时序的定义,而逻辑详细方案就是代码的文字及图形描述!
对于入门者来说,接触的比较多的是逻辑详细设计方案。在这一级别的方案中,我们是要求的是至少要做到模块内部所有关键信号的时序都要先设计好,这里讲的设计时序主要就是画波形图,在一个操作周期内每个信号在每一个时钟周期该是什么样子就画成什么样子。附图(时序图)是我曾设计的一个模块的主要信号时序:aes_cnt信号控制着w_fifo_rden、aes_ready等信号,是该模块的关键信号,通过将它们之间的时序关系通过时序图反应出来,写代码时就可以做到胸有成竹,减少出现逻辑混乱的情况。
听起来似乎很简单,但是执行起来却不容易,因为画波形图是一件很烦锁的事(有一次一个模块因为操作比较多我画了8张时序图)。但是请相信我,如果不这样做,因为时序关系没有处理好引起设计多次迭代所花的时间远多于画波形图的时间。
时序设计好之后,模块内部各个信号之间的关系就理得差不多了,之后就是将它翻译成代码了,这个过程以体力劳动为主,我就不多说了。
补充一下,画波形图推荐用TimingDesigner这个软件,如果有更好的,请告诉我,我也不喜欢TimingDesigner。
另一个就是约束。
这里的约束是针对综合软件和布局布线软件而言的。
为什么会有约束这个东西出现呢?主要原因是EDA软件比较笨,难以明白我们的心思,如果我们不把更详细的信息告诉它的话它就干不好活,比如需要将输出寄存器放的与输出管脚近一点,如果不加约束,EDA软件可能布通之后就不管了,导致Tco狂大,一点也不善解人意。所以我们需要约束这个东西,告诉 EDA软件要怎么干活,工程验收的标准又是什么。
在加约束之前,我们首先要定义一些术语好告诉EDA软件我们想干什么,这些术语便是Fmax、Tsu、Tco等等这些东西。这些东西的含义这里就不多说了,网上的讨论已经很多了。
有了术语,还要有一种通信方式与EDA软件通信,脚本语言充当了这一角色。不过现在像quartus这类软件做的比较智能化了,提供了图形化界面,但是这背后支撑的还是些脚本语言,大家可以用UltraEdit打加*.qsf文件去看看我们加的约束用脚本语言是怎么写的。
在加了约束之后,EDA工具就可以更好地按照我们的意愿去干活了,比较我们加了Fmax的约束,它就会尽可能地将关键路径放的靠近一些,以提高电路工作频率。当然,这是有代价的,寻找路径是需要时间的,要求越苛刻,时间花的越多,因此加约束的原则的适用就行。如果约束加的过高,就相当于让EDA工具去做一件不可能完成的事,找更短的路径的时候说不定找着找着就掉下悬崖了,效果反而更差。
虽然有约束这个好东西,不过提醒一下,在项目之前千万对它抱有太多的幻想,把希望寄托在别人的身上并不是每一次都很可靠的,出了问题还是要麻烦自己,加约束只能做一些锦上添花的事情。所以,我们在做方案的时候就需要对关键路径进行预估,要通过设计而不是约束解决这些问题。
最后是一个时序图的示例:
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
(2)浅淡逻辑设计的学习_读后感
几个月前从网上看到了王x人写的《浅淡逻辑设计的学习》,这个人应该是个专家吧。当时看完了4页的文字倒没什么收获,几个月后再把它翻出来看--完全不一样的感觉。摘录几点建议,谈谈自己的想法。
@@学习逻辑设计首先要有项目挂靠,如果你觉得未来一段时间你都不可能有的话,接下来的内容你就没有必要再看了,花的时间再多也只能学到皮毛--很多细节的问题光写代码是发现不到的。而且要真正入门,最好要多做几个项目(这三年大大小小的项目我做有七八个),总线型的和数字信号处理型的最好都要接触一些,因为这两个方向的逻辑设计差异比较大:前者主要是控制型的,会涉及到状态机等控制逻辑;后者主要是计算型的,难点主要在对My Comments:呵,虽然到现在为止没坐过什么项目,但是XX同志给我看的都是他们以前做的产品的代码,应该很有代表性吧。
===================================================================================================
@@第二要有好的师父。这里说的好的师父并不是指画原理图画了几十年的老师傅,而是指曾在专业IC公司做过一段时间的人,好的专业IC公司可以接触国内外最新的设计思想,在他们的帮助下,起点就可以比其他人高不少,更重要的是你可以学习逻辑设计思想性的东西!如果你的师傅经常跟你说画原理图的好处,你还是重新找过师父算了--用原理图设计是一种很落后的方式,即使他们可能会说可以系统级设计(专业的IC设计公司系统级设计绝对是由方案保证的,而不会靠原理图这鬼东西)更为清淅。
My Comments:XX同志在XX和XX待过的,他曾经对我说他是XX的创始人之一,我一点都不怀疑,即使现在他每天几乎都在10:00pm后才会离开公司,我很邪恶地想,以后要以XX精神指导我公司的研发进程,哈哈。当然这只是表象,更重要的是他给我的学习和分析问题的方法,--代码从文档开始,实际上是要对代码的功能要有透彻的理解,然后代码的书写就水到渠成了。我得承认一开始我是有点急于求成的,以致原理性的东西没搞清楚就上代码,结果是前功尽弃--方向都错了,走得再远又有啥意义呢?其次是,我的疑惑都能够从他那得到很好的解决答案,他的确是我认为的“牛人”,so good。
===================================================================================================
@@第三要看一些好的资料。RTL级的书中《Verilog 硬件描述语言》、EDA先锋写的那几本书都还可以,还有不得不提的是cliff的一些paper(www.sunburst-design.com上有);验证方面入门可以看下《Writting Testbenches》, 提高可以看下snug(Synopsys的用户论坛,里面的文章基本上反映了业界的领先水平)的paper;系统级的可以看看《片上系统-可重用性设计方法学》。
My Comments:好的资料。在学校的时候我就有这个习惯了:找牛X的人或牛X的出版社写的书。从我的观点来看,只有自己本身对某个问题有透彻的理解才能写出像样的东西来,所以我对资料的要求还是有点苛刻的,通常我都会采用“书海”战术--网上捣腾到一堆书,然后从这些资料中找到自己最中意的,列入自己的 “经典库”,ok了,以后的参考资料几乎全从这里可以找到。
====================================================================================================
@@第四要自己多总结,多动脑筋。逻辑设计的东西其实本质上的东西并不多:把RTL级的常用的D触发器、计数器、移位寄存器、状态机、多路选择器等基本的电路标准化、固定化;先做方案再写代码;设计时序;知道约束原理及怎么加约束;划分模块时知道怎么做到时序收敛;做验证的时候熟悉相应语言的行为级描述(这个肯定比RTL级好学多了)然后就是理解testbench的结构化设计。把这些东西的本质都搞清楚了做个合格的逻辑工程师应该是绰绰有余了,呵呵。
My Comments:很有道理,我想到了一句话,学而不思则罔,思而不学则殆。你看到的代码永远是人家的作品,也许刚刚看了觉得都懂了,但是把这些代码抛开,让你自己在描述时会发现其实是有很多细节点要注意的。平心而论,我没接触过XXX的东西,所以让我“创新性”地用代码描述它几乎不可能,况且我对基本的逻辑电路还不够了解,最明显的例子就是当初我对RAM的描述都是很模糊的,接触了相关的代码之后我才有了感性的认识,再联系看到过的文档,很快就理解透彻了。所以XX给了我代码,其实单单一段代码表示的功能对我来说是没问题的,下一步要做的是写了这些代码之后脑中要有认识--写的这段代码综合起来是什么电路,解决这个问题的方法--看一段代码,参考这些代码到综合工具里去查看底层,5k行代码下来,代码描述的是D触发器、计数器、移位寄存器、状态机还是多路选择器等等,基本就有数了。
=====================================================================================================
@@设计时序是进行逻辑设计的基本要求:时序是设计出来的,不是仿出来的,更不是凑出来的。很多人在做逻辑设计时喜欢一上来就狂写代码,写到一半后发现信号间的时序出问题了,只好推倒重来;好不容易反复了几次之后,通过仿真软件看了下,差不多要对了,于是再凑一下时序,竟然对了!但这个做法除了设计周期长外,代码的质量也难以保证,往往存在很多冗余的逻辑,甚至有一些隐藏着较深的bug。为什么会出现上面的问题呢?因为我们设计的是数字逻辑,而信号之间的逻辑关系往往是比较复杂的,在内部信号很多的情况下,仅凭拍下脑袋就写代码肯定是不能理清楚它们之前的复杂的关系,所以出错在所难免。正确的做法是我们要先对整个设计有一些规划--时时刻刻都要有设计时序的思想。设计时序最重要的是做好方案,这里说的方案绝不是只是摆几个框图在那里。我们在做设计的时候需要做总体设计方案、逻辑详细设计方案。这两种方案包括了很多东西,逻辑总体方案主要是一级模块的划分及接口时序的定义,而逻辑详细方案就是代码的文字及图形描述!对于入门者来说,接触的比较多的是逻辑详细设计方案。在这一级别的方案中,我们是要求的是至少要做到模块内部所有关键信号的时序都要先设计好,这里讲的设计时序主要就是画波形图,在一个操作周期内每个信号在每一个时钟周期该是什么样子就画成什么样子。
My Comments:这一点可以看作是对第4点的具体论证吧。接触的对时序最早的认识是信号经过一级触发器后会延时一拍。第一次对这个东西不以为然,不就是一个触发器么,数据过去不就行了么,后来在XX的提醒下才渐渐意识到,这个其实是逻辑设计里考虑时序最基本的一点,因为设计中会用到大量的触发器,在经过每一级触发器后都会有延时,那么内部信号的时序关系都要考虑作相应的延时,比如说为了某个目的,写地址要延时一拍,那么相应的读地址也要延时,读地址延时了该地址读的数据也会延时一拍,处理该数据的相关信号都要有必要的延时。上面说得很对,时序是设计出来的,该做5次延时的地方你做了四次,这次凑对了下次还是会错的,因为根基就已经错了。
对这个100路复用的代码,光时序图我就画了好几张纸。100路复用,也就是说一百个时钟周期后才会处理同一路数据的信号, 所以首先概念上就要理清,不是一个计数器在每一拍时钟下计数,下一拍实际上是下一路通道所表示的信号。
还有一个状态机的时序。Merely型状态机,当前输出状态跟输入和当前状态有关。对这个“输出状态”和“当前状态”我又捣鼓了很久。关键点在于,当前输出状态要放到RAM中去,然后通过适当地读写地址的设计,把它用作下一个时刻的当前状态,再结合当前输入给出当前输出,依次循环下去,是不是有点绕人?
=================================================================================================
最后补充自己的一点:Practice makes perfect,perfect makes perfecter。很多东西第一次接触相当崩溃的,比如说用低频率时钟恢复高频率数据、复用等等。设计中有很多巧妙的方法,巧妙的方法往往需要对细节点尤为关注。