zoukankan      html  css  js  c++  java
  • (转)浅谈逻辑设计如何入门

    原文地址:http://edacn.net/bbs/viewthread.php?tid=118442&extra=&highlight=&page=1
    其中第四部分作者写的不易理解,且有疑问(已标红),感觉第二个时钟周期后,reg1应该为1

    浅谈逻辑设计如何入门

    首先强调一点,在逻辑设计verilog/VHDL是最不重要的知识。如果想通过verilog/VHDL的学习来掌握逻辑设计那就大错特错了。逻辑设计需要掌握的基础知识:1)CMOS电路的原理,是逻辑设计的基础,就像数学是一切自然科学的基础一样。CMOS电路的基础决定了你对逻辑设计中一些问题(比如压稳态)的理解程度。2)数字ASIC设计方法,可以找一本介绍数字IC设计的书来读一读(注意不是verilog/VHDL介绍书),个人推荐《数字IC系统设计》和《SOC设计方法与实现》这两本,看完这两本书之后,应该掌握以下知识:同步设计,跨时钟信号的传递,ASIC设计的基本流程。3)最后才是verilog/VHDL语言,个人认为《Verilog HDL数字设计与综合》一本书足以。
    2)如何把学到的知识付注于实践是本文讨论的重点。数字电路基本上分为控制型和运算型,所谓运算型就是输入的数据(通常来自memory)经过某些运算再输出(通常到memory),典型的运算型电路就是滤波器,特点就是复杂密集的运算和相对简单的控制,实际上这也是硬件的优点,而且实际项目中大部分模块都属于这一类。
    运算型电路的设计可以分成数据通路设计和控制逻辑设计两部分。一般先设计数据通路,所谓数据通路就是从输入到输出的路径,一般包括逻辑运算,多路器,寄存器甚至memory。数据通路大体可以决定电路的性能(速度,面积等),逻辑设计所有技巧差不多都是为了优化数据通路的。数据通路完成之后,设计者开始画数据流的时序图。如果说数据通路决定设计方案的好坏,那么控制逻辑决定了设计的对错。所谓控制信号就是interface上的控制信号,数据通路内寄存器的使能信号,多路器选择信号和memroy控制信号。控制信号的时序图是根据数据流的时序图
    产生的,根据控制信号的时序图就很容易得通过状态机甚至计数器来产生控制信号。需要指出的是,并不是所有控制信号都要由状态机或者计数器直接产生,也可以由其它控制信号的单位延迟或者逻辑运算产生。总之,控制逻辑的设计比较随意,对面积的影响比较小。
    控制型电路顾名思义,控制比较复杂,数据通路分支很多但是运算逻辑比较简单。常见的模块有:UART,I2C甚至CPU。有一种极端情况,就是只有控制逻辑而没有数据通路,比如:饮料机,交通灯,时钟,电梯控制器。个人认为这些模块用硬件来设计既没有实际意义也没有教学意义。虽然初学者觉得很有挑战性,但是状态机的设计没有什么技术深度。而且这些模块往往会对初学者形成误导,即用软件设计的思路来设计硬件,比如画流程图。当然对于某些控制性模块流程图是必须的,但是对于初学者来说最重要的事情是把RTL程序区分开,所以运算型的电路更适合初学者学习。不过,运算型电路对于自学者来说有一个很大的障碍就是往往计算过程比较复杂,需要事先用c model来描述并生成测试向量和比对结果,对c语言有一定要求。不是通过观察波形而是通过自动比对来验证设计的正确性是一种专业的做法。另外一个问题是运算型电路都涉及一定的背景知识,比如滤波器就要求设计者有一定的数字信号处理基础,估计这也是大量的介绍Verilog/VHDL设计的参考书很少列举运算型设计的原因。我这里推荐《数字信号处理的fpga实现》这本书,虽然大部分内容都很晦涩难懂(至少对于我来讲),但是哪怕只看1/10对自己水平的提高也有很大帮助。
    3)初学者把RTL等同于程序,对RTL综合之后的结果没有感性认识,或者面对一个项目感觉无从下手,都来源于一个问题:把时序控制进程复杂化。什么计数器,移位寄存器,状态机等等等等,表象就是RTL很难很强大!本质上,rtl表达能力十分有限,比c语言简单多了。所谓RTL就是寄存器传输级描述,基本组成就是逻辑门和寄存器。大家对组合逻辑建模应该很有心得,不过就是输出是输入的某种表达式。使用assign描述,输出信号用wire声明,使用always描述,输出信号用reg声明,并且所有输入信号都在敏感信号列表中。时序建模其实也很简单:边沿触发的寄存器!
    异步复位的:always @(posedge clk or negedge rst_n)
                         if (~rst_n)
                            dout <= 0;
                         else
                            dout <= din;
    或者没有复位信号的:always @(posedge clk) dout <= din。在同步电路中,使用clock的就这两者足以!需要指出的是尽管有些库提供同步复位和使能端的寄存器,实质上它仍然是第二种寄存器加上一些组合逻辑得来的:
    always @(posedge clk)
           if (rst)
              dout <= 0;
           else if (en)
              dout <= din;
    等同于:
    assing din1 = rst? 0 : en? din : dout;
    always @(posedge clk) dout <= din1;即一个三选一的MUX和一个没有复位端的寄存器。
    又比如一个计数器通常描述:
    always @(posedge clk or negedge rst_n)
           if (~rst_n)
              count <= 0;
           else if (count_en)
              count <= count + 1;
    实质上是:
    assign cnt = count + 1;
    assign din = count_en? cnt : count;
    always @(posedge clk or negedge rst_n)
           if (~rst_n)
              count <= 0;
           else
              count <= din;
    即一个加1逻辑,一个二选一MUX和一组寄存器。移位寄存器也可以使用这种方式实现,我这里建议初学者尽量用后者这种方式来描述你的设计,尽管代码量增加了不少,但是实际电路的和前者是一样的,而且后者的描述和电路联系的更紧密。
    4)最后再提一下同步设计,据我所知很多初学者都没有同步的概念,表现就是时钟乱飞,甚至以能使用latch为骄傲。最典型的同步设计就是只有一个clock,所有触发器在同一个时钟沿去所存数据。举一个例子:
    always @(posedge clk) reg0 <= din;
    always @(posedge clk) reg1 <= reg0;
    假设reg0和reg1的初始值分别是0和1,din在clk第一个上升沿到来前为1,第二个上升沿到来前变为0并一直保持。那么在第一个上升沿之后,reg0加载din的值1,而reg1加载reg0的初始值0,只有在第二个上升沿到来之后reg1才又变成0。大部分人对我上面的描述没有异议,但是有一些人会马上产生另一个疑问:为什么在第一个上升沿到来时,reg1加载的时reg0的初始值而不是当前值。这些人为了保证reg1采到的是reg0的初始值,会这样该reg1:always @(negedge clk) reg1 <= reg0。之所以他们有这种担心是因为不知道最终的门级描述并不只有两个寄存器。事实上在reg0的输出到reg1的输入以及reg0和reg1的时钟端之间会有若干对反相器来提供延迟,具体数值由EDA工具得出。所以在设计者和eda工具之间是有一个默认前提的,即:reg0的数值到达reg1输入端的时间为当前时钟沿到达reg1之后一点时间到下一个时钟沿到达reg1之前一点时间之间。可以这么理解,在同步设计中时序关系是默认的,设计者不需要care,从而设计得到很大的简化。
  • 相关阅读:
    C++中无数据成员的类的对象占用内存大小
    malloc在函数内分配内存问题
    字符数组拷贝与strcpy函数
    Python,anaconda及pycharm安装过程笔记
    DFS-深度优先搜索与BFS-广度优先搜索
    90 k数和 II
    88 Lowest Common Ancestor of a Binary Tree
    AdaBoost笔记之代码
    79 最长公共子串
    78 最长公共前缀
  • 原文地址:https://www.cnblogs.com/god_like_donkey/p/1561864.html
Copyright © 2011-2022 走看看