非常好的Verilog设计经验谈
- 格式:doc
- 大小:29.50 KB
- 文档页数:4
1.数字电路基础知识:布尔代数、门级电路的内部晶体管结构、组合逻辑电路分析与设计、触发器、时序逻辑电路分析与设计2.数字系统的构成:传感器AD 数字处理器DA 执行部件3.程序通在硬件上的执行过程:C语言(经过编译-->该处理器的机器语言(放入存储器-->按时钟的节拍,逐条取出指令、分析指令、执行指令4.DSP处理是个广泛概念,统指在数字系统中做的变换(DFT、滤波、编码解码、加密解密、压缩解压等处理5.数字处理器包括两部分:高速数据通道接口逻辑、高速算法电路逻辑6.当前,IC产业包括IC制造和IC设计两部分,IC设计技术发展速度高于IC 设计7.FPGA设计的前续课程:数值分析、DSP、C语言、算法与数据结构、数字电路、HDL语言计算机微体系结构8.数字处理器处理性能的提高:软件算法的优化、微体系结构的优化9.数字系统的实现方式:编写C程序,然后用编译工具得到通用微处理器的机器指令代码,在通用微处理器上运行(如8051/ARM/PENTUIM专用DSP硬件处理器用FPGA硬件逻辑实现算法,但性能不如ASIC用ASIC实现,经费充足、大批量的情况下使用,因为投片成本高、周期长10.FPGA设计方法:IP核重用、并行设计、层次化模块化设计、top-down 思想FPGA设计分工:前端逻辑设计、后端电路实现、仿真验证11.matlab的应用:matlab中有许多现成的数学函数可以利用,节省了复杂函数的编写时间matlab 可以与C程序接口做算法仿真和验证时能很快生成有用的数据文件和表格DSP builder可以直接将simulink模型转换成HDL代码,跳过了中间的C 语言改写步骤12.常规从算法到硬件电路的开发过程:算法的开发C语言的功能描述并行结构的C语言改写verilog的改写仿真、验证、修正综合、布局布线、投入实用13.C语言改写成verilog代码的困难点:并行C语言的改写,因为C本身是顺序执行,而不是并行执行不使用C语言中的复杂数据结构,如指针目前有将C语言转换成verilog的工具?14.HDLHDL描述方法是从电路图描述方法演化来的,相比来说更容易修改符合IEEE标准的有verilog HDL和VHDLVHDL由美国国防部开发,有1987和1993两个版本verilog由cadence持有,有1995、2001、2005三个版本verilog较VHDL更有前景:具有模拟电路描述能力、不仅可以开发电路还可以验证电路、门级以下描述比VHDL强RTL级和门级的综合已经成熟,主要是注意行为级的综合结果,使用可综合的编程风格SYSTEM VERILOG是VERILOG的一种延伸15.IP核的应用:软核soft core:功能经过验证的、可综合的、实现后门数在5K以上的HDL代码固核firm core:功能经过验证的、可综合的、实现后门数在5K以上的电路结构编码文件,如edif,不可更改硬核hard core: 功能经过验证的、可综合的、实现后门数在5K以上的电路结构版图,已带工艺参数,不可更改16.HDL语言综合后得到EDIF,这是一种标准电路网表EDIF经过具体工艺库匹配、布局布线、延时计算后得到网表EDIF不可更改,作为固核存在17.verilog特点:区分大小写,所有关键字都要求小写不是强类型语言,不同类型数据之间可以赋值和运算//是单行注释可以跨行注释描述风格有系统级描述、行为级描述、RTL级描述、门级描述,其中RTL级和门级别与具体电路结构有关,行为级描述要遵守可综合原则门级描述使用门级模型或者用户自定义模型UDP来代替具体基本元件,在IDE 中针对不同FPGA器件已经有对应的基本元件原语18.verlog语法要点:module endmodule之间由两部分构成:接口描述和逻辑功能描述IO端口种类:input output inout相同位宽的输入输出信号可以一起声明,input[3:0] a,b; 不同位宽的必须分开写内部信号为reg类型,内部信号信号的状态:0 1 x z,3'bx1=3'bxx1 x/z会往左扩展3'b1=3'b001 数字不往左扩展逻辑功能描述中常用assign描述组合逻辑电路,always既可以描述组合逻辑电路又可以描述时序逻辑电路,还可以用元件调用方法描述逻辑功能always之间、assign之间、实例引用之间以及它们之间都是并行执行, always内部是顺序执行常量格式:<+/-><二进制位宽><'><进制><该进制的数值>:默认进制为10进制默认位宽为32位位宽是从二进制宽度角度而言的由位宽决定从低位截取二进制数2'hFF=2'b11,通常由被赋值的reg变量位宽决定parameter常用于定义延迟和变量位宽,可用常量或常量表达式定义变量种类:wire reg memoryIO信号默认为wire类型,除非指定为reg类型wire可以用作任何输入输出端口wire包括input output inoutwire不带寄存功能assign赋值语句中,被赋值的信号都是wire类型assign之所以称为连续赋值,是因为不断检测表达式的变化reg类型可以被赋值后再使用,而不是向wire一样只能输出,类似VHDL 中的buffer端口reg类型变量初始值为x (VHDL中初始值为本类型最小值,通常是0always模块里被赋值的信号都必须定义为reg类型,因为always可以反复执行,而reg表示信号的寄存,可以保留上次执行的值reg类型变量与integer变量不同,即使赋负值,实质上也是按二进制无符号数存储的,integer是有符号数verilog中所有内部信号都是静态变量,因为它们的值都在reg中存储起来了memory型只有一维数组,由reg型变量组成memory初始化只能按地址赋值,不能一次性赋值1*256的memory写法:reg mema[255:0] mema[3]=0;不同位宽的变量之间赋值,处理之前都以被赋值的变量位宽为准扩展或截取A[a:b] 无论a b谁大,a总是实际电路的信号高位,b总是实际电路的信号低位算术运算中如果有X值则结果为Xfor循环中的变量另外定义成integer,因为它不是实际信号,有正负; reg则以无符号数存在== 和!=只比较0、1,遇到z或x时结果都为x (x在if中算做假条件,结果可能是1、0、x===和!==比较更加苛刻,包括x和z的精确比较,结果可能是0、1&&的结果只有1'b1或1'b0两种,A&A的结果位宽则是与A相同的{1,0}为64'h100000000,所以拼接运算中各信号一定要指定位宽移位运算左移将保留4'b1000<<1等于5'b10000,右移则舍弃4'b0011<<1等于4'b0001数字电路里位运算应用普遍,包括按位逻辑运算、移位运算、拼接运算、缩减运算非阻塞式赋值<=与阻塞式赋值=阻塞:在同一个always过程中,后面的赋值语句要等待前一个赋值语句执行完,后面的语句被该赋值语句阻塞非阻塞:在同一个always过程中,非阻塞赋值语句是同时进行的,排在后面的语句不会被该赋值语句阻塞<=:块结束后才能完成赋值块内所有<=语句在always块结束时刻同时赋值<=右边各变量的值是上一次时钟边沿时,这些变量当时的值用于描述可综合的时序电路=:=语句结束之后过程always才可能结束在always过程中,begin end块内按先后顺序立即赋值,在fork join 内同时赋值(可能造成冲突与assign连用描述组合电路begin end中阻塞的含义:begin ...@(A B=C...; end 如果A 事件不发生则永远不能执行下去,被阻塞了由于时钟的延时(往往在ps级,多个always(posedge之间究竟谁先执行是个未知数使用原则:同一个always过程块内建立时序电路用<=纯组合逻辑电路用=,生成的电路结构最简单,执行速度最快同一个always块内不要混用<=和=不要在多个always块内对同一个变量赋值(多源驱动 if else的三种形式,第三种形式适合描述优先编码器if条件中0/x/z当成假,1当成真,非0的数值也当成真case语句的三种:case(四种状态的比较 casez(忽略z casex(忽略x 和z,只看哪些位的信号有用case语句中所有表达式值的位宽必须相等,default中不能将n'bx用'bx代替避免生成锁存器的方法:电平触发时if后加else case中加default ?使用casex会将不必要的状态视为无关项,使得综合出来的电路最简单两种特殊的括号:begin 顺序语句... end fork 并行语句... join,其差别在于块内语句的起止时间、执行顺序、相对延时块被命名后,其内部变量可以被调用,因为变量都是静态的(调用信号:对应电路中的一个信号线被引到另一处initial块只无条件执行一次always块在满足条件时不断执行initial常用来写测试文件,always块常用来写电路描述always既可以描述组合逻辑电路又可以描述时序逻辑电路always如果后面有敏感信号列表则不能用wait语句always既可以描述电平触发又可以描述边沿触发,wait只能描述电平触发assign 常用于描述组合逻辑电路测试文件中一般都是现initial 后always生成语句:生成快的本质是使用循环内的一条语句代替多条重复的verilog语句,简化了用户的编程genvar用于声明生成变量,生成变量只能用在生成快之间仿真时,仿真器会将生成块中的代码展平,在确立后的方针代码中,生成变量是不存在的最好是先想象出来循环生成语句被展平后的电路样子,再写相关的描述语句task和function的区别:task可以定义自己的仿真时间单位,function与主模块共用同一个仿真时间单位函数不能启动任务,任务能够启动函数函数至少要有一个输入变量,任务没有输入变量函数返回一个值,任务不返回值一个模块的设计包括3个部分:电路模块的设计测试模块的设计设计文档的编写设计者通过布局布线工具生成具有布线延迟的电路,再进行后仿真,得到时序分析报告从时序分析报告中可以知道电路的实际延迟t,同步电路内每个时钟周期要大于t,从而可确定该运算逻辑的最高频率综合器之所以能够实现加法器、乘法器是因为库中已经存在可配置的参数化器件模型FPGA内总线宽度容易自定义,以便实现高速数据流,三态数据总线相当于数据流的控制阀门数字系统内数据流的控制:开关(或三态数据总线、数据暂存部件(寄存器、同步状态机控制(整个系统在一个时钟域内流水线操作pipe line:K级流水线就是从组合逻辑的输入到输出恰好有K个寄存器组,上一级的输出是下一级的输入流水线操作获得第一个结果的时间要比不用流水线操作的时间长,但以后结果获得时间都只需要一个时钟周期,提高了数据吞吐量流水线操作的保证:Tclk>K*(组合逻辑延迟+触发器的建立保持时间/触发时间,即时间片段要长于最大路径延迟体现了面积换速度的思想,在综合时考虑的是以面积小为主还是以速度为主本质上是一种同步逻辑同步时序逻辑和异步时序逻辑:同步时序逻辑指所有寄存器组由唯一时钟触发 always@(posedge clk 或always@(negedage clk 异步时序逻辑指触发条件不唯一,任意一个条件都会引起触发 always@(posedge clk or posedage reset 目前的综合器是以同步时序逻辑综合的,因为同步时序逻辑较异步时序逻辑可靠严格的同步要求时钟信号传递速度远远大于各部分的延迟,实际中clk要单独用线,而不要经过反相器等部件 always @(posedge.. begin ...<=... end 表示同步时序逻辑(同时刻赋值不同速率数据接口的处理方法(异步数据的处理方法:帧同步 FIFO 双端口RAM 同步状态机:包括moore和mealy型两种,及其反馈模型(是一种反馈控制系统,当前状态就是其内部状态变量状态机的开发步骤:根据实际问题列出输入输出变量和状态数画出状态图并化简写出状态转移真值表得到逻辑表达式用D触发器或JK触发器构建电路(目前用D触发器多 verilog描述时只需要得到简化的状态图就可以描述状态编码方式:独热码格雷码状态机主体程序有单always描述方式和多always描述方式采用case/casez/casex建立模型最好,因为x是无关态,生成的电路最简单 default: state='bx与实际情况更一致,效果等同于 default: state<=idle 只有同步状态机才能被目前的综合 for 语句会将所有变量的情况展开,占用巨量逻辑资源,替代办法是用计数器和 case 语句说明所有情况有优先级的 if else 结构会消耗更多资源,建议用无优先级的 case 替代模块的复用往往比代码上修改节省的资源多 PLL 的分频、倍频、移相操作会增加设计精度同步时序电路的延时:#x 通常用于仿真测试,实际硬件延时是:长延迟用计数器,小延迟用 D 触发器,此方法用来取代延迟链同步电路中,稳定的数据采用必须满足采样寄存器的建立和保持时间 reg 类型在 always 中不一定综合成时序电路,也可能是组合逻辑电路乒乓操作与作用异步时钟域同步问题延迟包括门延迟和线延迟组合逻辑产生的时钟仅能应用在时钟频率较低、精度要求不高的情况下增减敏感信号得到的结果一样补充部分: verilog HDL 起初是作为写 testbench 而产生的 verilog 有 1995 进入 IEEE 标准,为 IEEE-1364, 于 2001 年进行了扩展,为 IEEE 1364-2001; verilog AMS 可用于模拟电路和数字电路的综合,目前正在不断发展和完善中; verilog 的标识符区分大小写,关键字使用小写;用\\来进行单行注释,用\* *\来进行跨行注释;标识符由字母、数字、下划线构成,并以字母开头;关键字又叫保留字,只有小写的关键字才是保留字;信号的状态有 4 种: 0 1 x z x 和 z 在描述电路时不区分大小写,在仿真时大小写有不同意义;常量表达式中: x z 不区分大小写;进制符号 h o d b 与 H O D B 不区分大小写;十六进制中 a~f 不区分大小写;下划线_用于提高可读性;?在数中可以代替 z; x 和 z 的左端补位;字符和字符串都以 ASICII 码形式存在,也可以当成电路内的信号;字符串必须包含在同一行,不能分成多行书写;如果表达式或者赋值语句中将字符串当成操作数,则字符串中的每个字符都被看成 8 位的 ASCII 值序列;可综合的信号类型:wire reg memory 它们用来描述数字电路不可综合的数据类型:integer real 它们只用仿真,位于testbench 中 wire 是连线的抽象模型,不能保存数据,其值由驱动元的值决定;wire 不能用在 always 或 initial 块中; wire 的默认值为高阻 z; wire 的使用情形:1.作为模块的输出端口 2.用连续赋值语句 assign 赋值; reg 是 1 位寄存器(触发器)的抽象模型,可以保存数据; reg 必须用在 always 或 initial 块中; reg 的默认值为 x; reg 的使用情形:1.阻塞赋值<= 2.非阻塞赋值= memory 只能是一维的;memory 只能对每个单元分别初始化,方法:1.一个一个赋值 2. 通过系统任务$readmem 赋值 reg[3:0] fc;//一个 4 位寄存器 reg fc[3:0] //4 个一位寄存器 parameter的作用:仿真开始以前对其进行赋值,整个仿真过程中保持其值不变;关系运算符将以逻辑 1 或逻辑 0 返回比较的结果; == !=的返回值有 0 1 x 三种情况,=== !==的返回值只有 0 1 两种情况; verilog 由于是描述电路的,用于位的操作较多,有: 位逻辑操作,移位操作,并置操作,归约操作;位逻辑运算的结果中,位数与原操作数一样多;归约符是在原操作数的所有位上进行操作,并产生 1 位结果;并置运算可以发生在 bit 与 bit 之间 bit 与矢量之间矢量与矢量之间用于仿真的系统任务:所有系统任务都必须在 initial 或 always 内;所有系统任务都必须以$开头;常见系统任务:显示任务($diplay 系列和$write 系列监控任务($monitor 系列探测任务($strobe 系列文件打开、输入、关闭任务(&fopen &fclose &fdisplay... 读取文件任务($readmemb $readmemh 仿真结束控制任务($finish $stop 随即信号任务($random 过程块: initial 块和 always 块一个 module 内可以包含多个 initial 或always 模块;所有 initial 或 always 块在 0 时刻开始并行执行,各 initial 或 always 块内部顺序执行; initial 过程块主要是面向 testbench 的,通常不具有可综合性;always 过程块在描述电路时既可以描述组合逻辑电路(电平敏感又可以描述时序逻辑电路(边沿敏感;写 testbench 时 initial 通常用于初始化以及顺序波形的描述,always 通常用于重复波形的描述;任务 task 与函数 function: 为了描述模块中被多次执行的部分以及为了增强代码的易读性 verilog 中的高级程序语句如 for 循环语句只用在写 testbench 中; begin end 和 fork join 是两种特殊的括号 if 语句的第三种形式适合描述优先编码器,case 语句适合描述数据选择器和状态机;case 的条件表达式如果与分支项表达式长度不同,则在比较前将所有表达式都统一为这些表达式的最长长度; casez 忽略 z,casex 忽略 z 和 x; assign 语句只在右端表达式发生变化时才重新计算并重新赋值,其余时间都是连续赋值; assign 语句可以指定 bit、vector 或是任意拼接操作的结果; assign 语句是连续赋值的,用于驱动网线 wire, reg 类型不需要连续赋值, reg 类型一旦被赋值就会一直保存;过程赋值语句有两种:阻塞式=和非阻塞式<=,只能在过程块 initial 和always 中使用; @对事件触发的控制与 wait 语句不能同时使用;。
Verilog 是一种硬件描述语言 (HDL),广泛应用于数字电路设计中。
在Verilog 中,组合电路是一种重要的设计模块,它由一系列逻辑门和时序元件组成,能够实现特定的逻辑功能。
本文将介绍 Verilog 组合电路的设计原理、方法和实践经验,旨在帮助读者深入了解 Verilog 组合电路设计的核心概念和技术要点。
【内容概要】1. Verilog 组合电路设计概述2. Verilog 组合电路的建模与描述3. Verilog 组合电路的仿真与验证4. Verilog 组合电路设计案例分析5. Verilog 组合电路设计的未来发展趋势【正文】1. Verilog 组合电路设计概述Verilog 是一种硬件描述语言,它提供了丰富的语法和功能,能够描述数字电路中的各种逻辑元件和时序元件,包括组合电路、时序电路和FSM 等。
在 Verilog 中,组合电路是一种重要的设计模块,它由逻辑门、数据选择器、多路器、解码器等逻辑元件组成,能够实现特定的逻辑功能。
在数字电路设计中,组合电路通常用于实现各种逻辑运算和逻辑控制功能,如加法器、减法器、乘法器、除法器、逻辑门、数据选择器、编码器和解码器等。
2. Verilog 组合电路的建模与描述在 Verilog 中,组合电路可以通过行为建模或结构建模的方式进行描述。
行为建模是指根据组合电路的逻辑功能和特性,使用逻辑表达式、真值表或状态转换图等形式描述其逻辑行为。
结构建模是指根据组合电路的硬件结构和连接关系,使用逻辑门、数据选择器、多路器、解码器等硬件元件的实例化描述其物理结构。
在进行 Verilog 组合电路建模时,应根据具体的设计目标和需求选择合适的建模方式,并遵循Verilog 语言的语法规范和设计规范,确保描述的准确性和完整性。
3. Verilog 组合电路的仿真与验证Verilog 组合电路的仿真与验证是设计过程中至关重要的一环,它能够帮助设计工程师验证电路功能是否符合设计要求,发现和解决潜在的设计缺陷和错误。
每天多学一点:Verilog编写技巧(一)来源:网路素材好的设计者一般都要对电路要实现的功能有清晰的认识,对数据流很清楚,知道数据如何从一个点移动到另一个点,这就是所谓的“勾划”(walk-through)。
一旦设计蓝图在脑海中变得清晰,此后釆用Verilog编写数据路径和控制逻辑就会变得思路清晰。
脑海中的模拟正如大多数人玩过的象棋游戏,我们都知道提前谋划是何等重要,要在下一次移动棋子之前考虑好此后的几步棋应该怎么走,以确保不会出错,不被对手捕捉到机会。
电路设计过程与下棋非常相似。
当设计状态机、数据路径或者控制逻辑时,我们知道它们的功能。
在进行设计仿真之前,我们需要思考代码在不同输入和边界条件下如何工作。
如果用心去做好这一步工作,并且分析可能出现的问题,验证工作将会变得非常高效。
另外,这一步也给我们建立了自信,使我们确信整个设计非常扎实,可以很好地工作。
否则很可能出现的情况是在验证阶段反复发现问题并进行电路修改,不断进行补救T.作,并且最终也不能确定设计足否还隐含着没有被发现的问题。
哪种风格—数据流或算法描述组合逻辑有两种方式—使用wire(对应数据流描述方式)或者使用reg(对应算法描述方式)。
这两种方式都能实现相同的逻辑功能,综合后得到相同的门电路,具体使用哪一种方式可以根据个人喜好。
数据流—短表达式举例wire [7:0] regl0_nxt;assign regl0_nxt = wren ? data_in : regl0;算法—短表达式举例reg [7:0] regl0_nxt;always @(*) beginreglO_nxt = reglO;if (wren)regl0_nxt = data_in;end当表达式非常简单时,一般更倾向于使用数据流风格来实现,此时代码行数很少。
然而,当表达式很长并且与很多条件有关时,数据流风格阅读起来较为费力。
此时可以使用算法风格,可以采用if-else 语句进行描述,以易于阅读和减少错误发生。
数字设计中的Verilog语法调整技巧数字设计是现代电子领域中的重要技术之一,Verilog作为一种硬件描述语言,在数字设计中扮演着至关重要的角色。
Verilog语法的准确使用和调整是数字设计过程中的关键一环,下面将从几个方面介绍数字设计中的Verilog语法调整技巧。
首先,在编写Verilog代码时,合理的模块化设计是至关重要的。
通过将整个设计划分为多个模块,可以提高代码的复用性和可维护性。
在模块化设计中,可以使用模块实例化的方式来调用其他模块进行功能实现。
此外,在模块内部也可以进一步进行代码块的划分,提高代码的清晰度和可读性。
其次,对于Verilog语法中的信号赋值,需要合理地选择适合的赋值方式。
在Verilog中,有非阻塞赋值和阻塞赋值两种方式。
对于时序逻辑的描述,通常使用非阻塞赋值,以保证设计在时钟信号的作用下按时序执行;而对于组合逻辑的描述,通常使用阻塞赋值。
正确选择赋值方式可以有效避免由于信号赋值顺序导致的逻辑错误。
另外,在Verilog语法中,对于always块的使用也需要注意一些技巧。
always块是描述Verilog代码的执行顺序的重要部分,其中的敏感列表决定了代码块的执行触发条件。
在编写always块时,需要确保敏感列表中覆盖了所有的输入信号,以避免遗漏导致的逻辑错误。
另外,在时序逻辑设计中,需要使用posedge(上升沿触发)或negedge(下降沿触发)来确保时钟信号的正确触发。
此外,当涉及到多位宽信号的处理时,需要注意Verilog语法中的位操作技巧。
Verilog中提供了丰富的位操作符号,如与(&)、或(|)、异或(^)等,可以方便地实现多位宽信号的逻辑运算。
同时,也可以通过拼接符号{ }来对多位宽信号进行连接或分割,非常适合处理复杂的数据结构。
最后,在Verilog代码的调试和优化过程中,合理利用仿真工具是至关重要的。
通过仿真工具可以实时观察信号波形,验证代码的正确性和功能实现情况。
verilog快速掌握之模块例化一、什么是例化1)FPGA逻辑设计中通常是一个大的模块中包含了一个或多个功能子模块,verilog通过模块调用或称为模块实例化的方式来实现这些子模块与高层模块的连接,有利于简化每一个模块的代码,易于维护和修改。
2)如下图,当有了例化的方法之后,当模块中需要两个同样功能的USB模块时,我们只需要编写好一个usb模块的代码,之后通过例化,就可以将俩个usb模块添加到顶层模块之中。
(注意,我们要有硬件思维,例化一个模块,那么就有一个硬件与之对应。
例化多个,就有多个硬件。
)二、例化方法1)首先我们设计好我们需要的功能模块,如下图左边的uart模块;2)之后我们在大模块例化uart模块,如下图右边的例化格式,信号列表中“.”之后的信号是uart模块定义的信号,括号内的信号则是在大模块中申明的信号,这样就将大模块的信号与uart模块信号一一对应起来。
三、参数例化1) 模块例化还有一个好处就是在调用的时候还可以传递/改变参数。
如下图,我们设计了一个uart模块,他的数据位宽只有8位,但是如果我们需要一个16位宽的uart模块,这时候我们可以通过参数例化来减少重新编写模块的工作量。
2)我们定义数据位宽为DATA_W=83)在例化的时候我们可以如下图,在模块名后面加上“#(.DATA_W(16))”,就可以将数据位宽定义为16位。
注意,大家务必掌握参数传递的方法,点拨FPGA课程后面有一部分练习要计时到1秒时间,如果modelsim真的要仿真1秒,跑一个用例就要花费好几分钟或者几个小时,这样的低效率是难以忍受的。
我们仿真时,在测试文件改变1秒的参数值,将它变得很小,这样就能加快仿真迅速。
详细情况,学到相应章节自然会清楚的。
Verilog(FPGA/CPLD)设计小技巧(来自Xilinx)这是一个在设计中常犯的错误列表这些错误常使得你的设计不可靠或速度较慢为了提高你的设计性能和提高速度的可靠性你必须确定你的设计通过所有的这些检查可靠性**为时钟信号选用全局时钟缓冲器BUFG• 不选用全局时钟缓冲器的时钟将会引入偏差 。
**只用一个时钟沿来寄存数据• 使用时钟的两个沿是不可靠的因为时钟的某沿或者两个沿会漂移; 如果时钟有漂移而且你只使用了时钟的一个沿你就降低了时钟边沿漂移的风险。
• 这个问题可以这样来解决就是允许CLKDLL自动纠正时钟的占空比以达百分之五十的占空比否则强烈建议你只使用一个时钟沿**除了用CLKDLL或DCM产生的时钟外不要在内部产生时钟.• 这包括产生门控时钟和分频时钟• 作为替代可以建立时钟使能或使用CLKDLL或DCM来产生不同的时钟信号。
• 对于一个纯同步设计建议你在任何可能的情况下只使用一个时钟**不要在内部产生异步的控制信号 例如复位信号或者置位信号• 内部产生的异步控制信号会产生毛刺• 作为替代可以产生一个同步的复位/置位信号这个信号的译码要比需要作用的时刻提前一个时钟周期**不要使用没有相位关系的多个时钟• 你也许并不总能避免这个条件在这些情况下确定你已使用了适当的同步电路来跨越时钟域**不要使用没有相位关系的多个时钟• 再次你也许并不总能避免这个条件相反许多设计都需要这样在这 些情况下确定你已适当地约束了跨越时钟域的路径**不要使用内部锁存器• 内部锁存器会混淆时序而且常常会引入另外的时钟信号• 内部锁存器在透明门打开时可以被看成是组合逻辑但在门被锁存时 可以被看成是同步元件这将会混淆时序分析• 内部锁存器常常会引入门控时钟门控时钟会产生毛刺使得设计变得不可靠性能**逻辑级的时延不要超过时序预算的百分之五十• 每个路径逻辑级时延可以在逻辑级时序报告或布局后时序报告中找到详细分析了每个路径之后时序分析器将生成每个路径时延的统计量检查一下总共的逻辑级时延超过了你的时序预算的百分之五十吗?**IOB 寄存器• IOB寄存器提供了最快的时钟到输出和输入到时钟的时延• 首先有一些限制对于输入寄存器在从管脚到寄存器间不能有组合逻 辑存在对于输出寄存器在寄存器和管脚之间也不能有组合逻辑存在对于三态输出在IOB 中的所有的寄存器必须使用同一个时钟信号和复位信号而且IOB三态寄存器必须低电平有效才能放到IOB中三态缓冲器低电平有效所以在寄存器和三态缓冲器之间不需要一个反相器• 你必须使软件能够选用IOB寄存器你可以设置全局实现选项为输入 输出或输入输出选择IOB寄存器缺省值为关(off)。
数字设计中的Verilog编程技巧在数字设计中,Verilog是一种常用的硬件描述语言,广泛应用于数字集成电路设计和仿真中。
Verilog编程技巧对于提高设计效率、优化性能和减少错误至关重要。
下面我将介绍几种Verilog编程技巧,帮助您更好地应用Verilog进行数字设计。
首先,合理使用模块化设计。
在Verilog中,模块是设计的基本单元,将复杂系统划分为多个模块可以提高可维护性和复用性。
因此,在设计过程中应该遵循模块化设计原则,将功能模块化、分层和相互独立。
其次,正确使用参数化。
在Verilog中,参数化是指通过参数来描述模块或信号的行为。
合理使用参数可以提高代码的灵活性和可复用性。
通过定义参数,可以轻松地调整模块的行为,使得代码更加通用和易扩展。
此外,熟练掌握Verilog中的时序控制。
时序控制是Verilog设计中的关键部分,用于描述时序逻辑和时序行为。
在设计过程中,需要注意时钟的约束和时序分析,确保设计的正确性和稳定性。
可以使用时钟域划分、时钟同步等技术来解决时序问题。
另外,注意Verilog中的模拟和仿真。
在数字设计中,模拟和仿真是验证设计正确性的重要步骤。
Verilog提供了Simulator工具,可以进行行为级仿真和波形仿真。
通过模拟和仿真可以验证设计是否符合预期,发现和修复设计中的问题。
最后,关注Verilog中的调试和优化。
在设计过程中,经常需要进行调试和优化,以确保设计满足性能和功耗要求。
Verilog提供了一些调试和优化技术,如添加断点、查看波形、进行逻辑分析等。
通过调试和优化可以找到设计中的问题,并对设计进行改进。
总的来说,Verilog编程技巧对于数字设计至关重要。
通过合理的模块化设计、参数化、时序控制、模拟和仿真、调试和优化,可以提高设计效率、优化性能和减少错误。
希望以上介绍的Verilog编程技巧能够帮助您在数字设计中取得更好的结果。
祝您在Verilog编程中取得成功!。
个人总结Verilog编程27条经验1.强烈建议用同步设计;2.在设计时总是记住时序问题;3.在一个设计开始就要考虑到地电平或高电平复位、同步或异步复位、上升沿或下降沿触发等问题,在所有模块中都要遵守它;4.在不同的情况下用if和case,最好少用if的多层嵌套(1层或2层比较合适,当在3层以上时,最好修改写法,因为这样不仅可以reduce area,而且可以获得好的timing);5.在锁存一个信号或总线时要小心,对于整个design,尽量避免使用latch,因为在DFT时很难test;6.确信所有的信号被复位,在DFT时,所有的FlipFlop都是controllable;7.永远不要再写入之前读取任何内部存储器(如SRAM);8.从一个时钟到另一个不同的时钟传输数据时用数据缓冲,他工作像一个双时钟FIFO(是异步的),可以用Async SRAM搭建Async FIFO;9.在VHDL中二维数组可以使用,它是非常有用的。
在VERILOG中他仅仅可以使用在测试模块中,不能被综合;10.遵守register-in register-out规则;11.像synopsys的DC的综合工具是非常稳定的,任何bugs都不会从综合工具中产生12.确保FPGA版本与ASIC的版本尽可能的相似,特别是SRAM类型,若版本一致是最理想的,但是在工作中FPGA版本一般用FPGA自带的SRAM,ASIC版本一般用厂商提供的SRAM;13.在嵌入式存储器中使用BIST;14.虚单元和一些修正电路是必需的;15.一些简单的测试电路也是需要的,经常在一个芯片中有许多测试模块;16.除非低功耗不要用门控时钟,强烈建议不要在design中使用gate clock;17.不要依靠脚本来保证设计。
但是在脚本中的一些好的约束能够起到更好的性能(例如前向加法器);18.如果时间充裕,通过时钟做一个多锁存器来取代用MUX;19.不要用内部tri-state, ASIC需要总线保持器来处理内部tri-state,如IOcell;20.在top level中作pad insertion;21.选择pad时要小心(如上拉能力,施密特触发器,5伏耐压等),选择合适的IO cell;22.小心由时钟偏差引起的问题;23.不要试着产生半周期信号;24.如果有很多函数要修正,请一个一个地作,修正一个函数检查一个函数;25.在一个计算等式中排列每个信号的位数是一个好习惯,即使综合工具能做;26.不要使用HDL提供的除法器;27.削减不必要的时钟。
使用Verilog语言进行FPGA设计的总结1.在使用case或casex结构进行同步状态机构建时,最后一定要有default状态,在这个状态中主要设计的是纠错程序,这个非常重要,通过这个状态可以把状态机因状态位错误而进入无效或错误的状态中拉回来,或跳出这次操作强制进入下一次操作的初始状态。
2.在使用else-else if ……-else结构时,各个条件之间必须完全是明确地互斥,任何两条件在任何时候都必须不能同时满足。
这个很重要,此重要性,虽在仿真阶段没什么大问题,但当综合成门电路以后在现实的硬件上进行运行时,就会暴露出很多缺陷,影响稳定性问题以及兼容性问题。
3.①在进行同步时序逻辑设计时,对由其他非主时钟信号沿触发的事件,采用信号沿跟踪寄存器,来及时判断沿的发生,当然这样会产生误差,最大会有一个主时钟周期;②判断沿发生的另一种方法是,使用主时钟发生两个一个周期相同的时钟信号,第二个时钟要落后于第一个时钟一个周期,当第一个时钟为高而第一个时钟为低时,说明在此之前第一个时钟有一个上升沿,第二种方法依然会有一个主时钟周期的误差。
4.在把测试模块包括在内的顶层模块的编写中,需要使用“include”来把各个子模块包含进来(被包含的文件名要写完全路径),不需要有输入输出端口列表,也不需要在模块中使用input和output进行声明,但在模块中,必须对需要引用的下层模块的输入输出变量进行声明,不管输入还是输出变量都声明成wire类型,如果不声明则均默认成位宽为1位的变量;在不包括测试模块的顶层模块中,要有输入输出列表,把所有子模块的对外输入输出在列表中体现,并且使用input和output进行声明,其中output也必须默认成wire类型的,而子模块间的输入输出变量使用wire进行一下声明就可以了,但必须要有。
5.在同步状态机设计中,要在前一状态为下一状态的关键变量进行赋值,以使在进入下一状态时,获得稳定的条件。
综合:不可综合的运算符:= = = ,!= =,/(除法),%(取余数)。
1、不使用初始化语句。
2、不使用带有延时的描述。
3、不使用循环次数不确定的循环语句,如:forever、while等。
4、尽量采用同步方式设计电路。
5、除非是关键路径的设计,一般不调用门级元件来描述设计的方法,建议采用行为语句来完成设计。
6、用always过程块描述组合逻辑,应在信号敏感列表中列出所有的输入信号。
7、所有的内部寄存器都应该能够被复位,在使用FPGA实现设计时,应尽量使用器件的全局复位端作为系统总的复位。
8、在verilog模块中,任务(task)通常被综合成组合逻辑的形式,每个函数(function)在调用时通常也被综合为一个独立的组合电路模块。
9、用户自定义原语(UDP)是不可综合的,它只能用来建立门级元件的仿真模型。
移位运算符:Verilog HDL提供向右(>>)及向左(<<)两种运算符,运算符高位或地位一旦移出即予丢弃,其空缺的位则予以补零。
连续赋值语句(assign)、case语句、if…else语句都是可以综合的initial 语句内若包含有多个语句时,必须以begin end 作聚合;单一的初值赋值,因此并不需以begin end做聚合。
循环(Loops)并不能单独地在程序中存在,而必须在initial和always块中才能使用。
initial过程块中的语句仅执行一次,而always块中的语句是不断重复执行的。
编写顶层模块的注意事项每个端口除了要声明是输入、输出还是双向外,还要声明其数据类型,是连线型(wire)还是寄存器型(reg),如果没有声明则综合器默认为wire型。
1、输入和双向端口不能声明为寄存器型。
2、在测试模块中不需要定义端口。
编写testbentch所归纳的心得module 模块名称;将input 定义为reg;将output定义为wire;引用欲测试的module 别名initial begin设定reg 初始值endalways处理变化值endmodule在always 、initial 过程块内,被赋值的每一个信号都必须定义成寄存器型。
Verilog学习心得因为Verilog是一种硬件描述语言,所以在写Verilog语言时,首先要有所要写的module在硬件上如何实现的概念,而不是去想编译器如何去解释这个module. 比如在决定是否使用reg定义时,要问问自己物理上是不是真正存在这个register, 如果是,它的clock是什么? D端是什么?Q端是什么?有没有清零和置位?同步还是异步?再比如上面讨论的三态输出问题,首先想到的应该是在register的输出后面加一个三态门,而不是如何才能让编译器知道要“赋值”给一个信号为三态。
同样,Verilog中没有“编译”的概念,而只有综合的概念。
写硬件描述语言的目的是为了综合,所以说要想写的好就要对综合器有很深的了解,这样写出来的代码才有效率。
曾经接触过motorola苏州设计中心的一位资深工程师,他忠告了一句:就是用verilog描述电路的时候,一定要清楚它实现的电路,很多人只顾学习verilog语言,而不熟悉它实现的电路,这是设计不出好的电路来的.一般写verilog code时,对整个硬件的结构应该是很清楚了,最好有详细的电路图画出,时序问题等都应该考虑清楚了。
可以看着图直接写code。
要知道,最初Verilog是为了实现仿真而发明的.不可综合的Verilog 语句也是很重要的.因为在实际设计电路时,除了要实现一个可综合的module外,你还要知道它的外围电路是怎样的,以及我的这个电路与这些外围电路能否协调工作.这些外围电路就可以用不可综合的语句来实现而不必管它是如何实现的.因为它们可能已经实际存在了,我仅是用它来模拟的.所以,在写verilog的时候应该要先明确我是用它来仿真的还是综合的.要是用来综合的话,就必须要严格地使用可综合的语句,而且不同的写法可能产生的电路会有很大差别,这时就要懂一些verilog综合方法的知识.就像前面说的,脑子里要有一个硬件的概念.特别是当综合报错时,就要想一想我这种写法能不能用硬件来实现,verilog毕竟还不是C,很多写法是不可实现的.要是这个module仅是用来仿真的,就要灵活得多了,这时你大可不必太在意硬件实现.只要满足它的语法,实现你要的功能就行了.有网友说关于#10 clk=~clk的问题,虽然这种语句是不可综合的,但是在做simulation和verification是常常用它在testbench中来产生一个clock信号。
第四章、Verilog HDL设计技巧本章通过一些简单的实例演示一下如何在verilog hdl中实现看似不可能实现的技巧,主要包括1.双向端口的使用2.PWM波形的产生3.常见几种分频器的设计4.巧用存储器定义语句实现存储器设计5.基于存储器的DDS设计6.有限状态机本章讲述一些常用的verilog设计方法和技巧,可以加速实际的工程应用,提高效率。
双向端口双向端口在应用过程中常常用到,如在进行和存储器的接口设计时。
由于存储器的数据线是双向信号,故FPGA的端口也必须用双向端口才能够连接。
Verilog HDL中的双向端口关键字为inout,如inout dat,则表示dat为一个双向(既可以输入也可以输出)的端口。
在硬件中为了更好的处理端口,大多数情况下都是使用单向端口,即input或者output。
这样更容易对电路的逻辑进行描述。
双向端口也往往转换成两个单向的端口进行操作。
仔细分析一下,当一个端口作为双向端口时,实际上是分时的输入和输出,也就是说,当内部逻辑需要双向端口作为输入时,这时候是用双向的输入功能,反之,用输出功能。
假设”内部需要”为一个变量,暂且命名为dir,则,双向端口转化为两个单向端口的电路就可以如下图所示。
图中,实线框是整个的逻辑设计电路模块,io为此模块的双向端口。
虚线框为将双向端口转化为两个单向端口的子模块,两个三角形的电路为三态门,上面的三态门低电平使能,下面的三态门高电平使能。
当需要将io端作为输入时候,即dir为高(假设为高电平时使用io 端的输入功能),上面的三态门被关闭,下面的三态门打开,于是信号的流向从io口经过下面的三态门由a端输出,那么这个时候其他逻辑部分就可以使用a端的信号进行运算处理;反之,当内部的其他逻辑需要输出信号至io双向端口时,可以置dir为低电平,上面的三态门被打开,下面的三态门被关闭,这样b端口的信号经由上面的三态门输出至io端。
这样,虚线框中的子模块就完成了双向端口转化为两个独立的单向端口(a和b)的功能。
因为Verilog是一种硬件描述语言,所以在写Verilog语言时,首先要有所要写的module在硬件上如何实现的概念,而不是去想编译器如何去解释这个module. 比如在决定是否使用reg定义时,要问问自己物理上是不是真正存在这个register, 如果是,它的clock是什么? D端是什么?Q端是什么?有没有清零和置位?同步还是异步?再比如上面讨论的三态输出问题,首先想到的应该是在register的输出后面加一个三态门,而不是如何才能让编译器知道要“赋值”给一个信号为三态。
同样,Verilog中没有“编译”的概念,而只有综合的概念。
verilog设计经验浅谈1,敏感变量的描述完备性V erilog中,用always块设计组合逻辑电路时,在赋值表达式右端参与赋值的所有信号都必须在always@(敏感电平列表)中列出,always中if语句的判断表达式必须在敏感电平列表中列出。
如果在赋值表达式右端引用了敏感电平列表中没有列出的信号,在综合时将会为没有列出的信号隐含地产生一个透明锁存器。
这是因为该信号的变化不会立刻引起所赋值的变化,而必须等到敏感电平列表中的某一个信号变化时,它的作用才表现出来,即相当于存在一个透明锁存器,把该信号的变化暂存起来,待敏感电平列表中的某一个信号变化时再起作用,纯组合逻辑电路不可能作到这一点。
综合器会发出警告。
Example1:input a,b,c;reg e,d;always @(a or b or c)begine=d&a&b; /*d没有在敏感电平列表中,d变化时e不会立刻变化,直到a,b,c中某一个变化*/d=e |c;endExample2:input a,b,c;reg e,d;always @(a or b or c or d)begine=d&a&b; /*d在敏感电平列表中,d变化时e立刻变化*/d=e |c;end2, 条件的描述完备性如果if语句和case语句的条件描述不完备,也会造成不必要的锁存器。
Verilog 开发经验总结●以硬件为基础的原则Verilog是硬件描述语言,所谓描述就是是在在描绘已经设计好的电路。
尤其是在刚开始学习HDL时,还没有能直接把语言对应到具体电路的能力,更不能上手直接写Verilog代码。
所以最优方案是先设计好硬件电路,再按照电路编写Verilog。
●模块化思想这个思想不仅仅是Verilog的开发了,就算是C语言甚至现在的超高级语言,也一直在强调封装的概念。
Verilog开发遵循的是自顶向下的模块化设计,思路基本是从最终功能不断细分,直到Verilog可以很直接地描述最基础的硬件单元,例如加法器,移位寄存器等等。
模块划分一定要尽量细,功能单一,且一定要留出使能、复位等接口以便于系统搭建。
说明以下,所谓Verilog可直接描述指的是按照规范描述出来的电路,开发工具能够很清晰地理解所要描述的功能,而不会发生误解等现象。
一个只使用编译器能理解的代码开发的电路,错误率会大大降低。
相反,如果功能划分不够清晰,使得一个模块的功能过于庞杂,不仅描述困难,编译器也可能产生很大的误解。
把握编译器的理解方式是有助于做Verilog的开发的,但这就需要一个长远的积累了,我在最后会给出一些例子。
●时序电路与逻辑电路完全分开Verilog中除了数据流模型和门级模型以外,最常用的是always即行为级模型描述电路。
一个always块可以理解为一个电路,或者实物上的一个芯片。
所以不要在一个always里杂糅时序电路和逻辑电路,那样很可能导致编译器综合出一些奇葩的结果。
时序always模块的敏感变量有且最多两个,一个是时钟边沿,一个是复位边沿,没有再多的敏感变量了。
组合逻辑always中敏感变量列表必须包含该模块所有涉及到的变量,或者直接用(*)代替,个人推荐后者。
●时序always块编写规范在时序always中,统一使用非阻塞赋值<=,因为时序always中在赋值号左侧的变量是真实的触发器,非阻塞赋值在触发时是同时赋值的,这很符合触发器在上升沿到来后同时将D输出到Q的实际情况。
verilogcoding_style建议Verilog coding style建议1.设计必须采用同步设计;同步设计就是保证电路中所有的寄存器都在同一个clock的控制下变化。
因为目前的EDA工具并不能很好的支持异步电路的分析,用同步设计加上良好的编码规范得到的电路仿真结果就等同于实际电路的运行结果,若是异步电路,仿真结果与实际电路的结果可能不相同。
异步电路的核心逻辑是用组合电路来实现的,电路的输出,主要信号等变化并不依赖于任何一个时钟,所有验证、调试都是相当繁琐而有难度的,所有建议采用同步设计。
2. 宏定义利于仿真的角度出发,应该设置一个user delay(UD),即宏定义`define UD #1,对没有时序逻辑寄存器都延时一个单位,默认是1ps,这样就可以模拟FPAG内部的延时;仿真结果更接近实际电路,值得一提的是,它是不被综合的,就是对电路的运行没有一点影响,仅仅只是为了仿真的方便才定义的。
3. 时序与组合电路分开描述这是IC设计最基本的一个coding style,因为这样有利于debug。
一般来说,时序电路是非常难以debug的,所有设计的话,时序电路尽量简单,而要完成的功能通过组合电路去完成,时序电路只设置为一个简单的D触发器。
这样的话,进行debug 的时候,看源码就可以只看组合模块,忽略时序电路(D flip_flop),既方便又节省时间。
组合描述的敏感链表用always @ (*)来描述。
其中*代表组合逻辑模块中影响输出的所有信号,这是verilog2001的新语法规定,大家不熟悉的话可以查一下。
4. 信号的赋值建议一个信号只在一个always 块里面进行赋值,而一个always 块也只对一个信号进行赋值;时序设计采用非阻塞赋值,组合电路采用阻塞赋值,这样做又以下几个好处:首先,可以很好的避免多驱动源的发生,减少这种错误发生的概率;其次,debug的时候若是发现该信号有错误,就可以进行很简单的定位,因为这个信号只是这一个always里面赋值,非常有利于debug;最后,时序采用非阻塞赋值,组合设计采样阻塞赋值,因为这样做最符合信号的实际情况;5. 避免非目的的latch的产生Latch对电路的危害:首先它是电平触发的,不能被同步,容易产生冒险-竞争现象,对电路造成一定的危害;其次,FPGA设计资源中,大部分器件没有latch这个资源,而需要一个门电路和一个触发器(FF)来构成,这样就造成资源浪费。
可维护、可移植的verilog工程设计技巧TIDERIP——新手上路A随着集成电路发展的脚步,28nm技术已经大规模应用在FPGA领域,FPGA开发者面临着日趋庞大的系统设计。
笔者也在这段时间开始接触带有硬核ARM的Cyclone V,Quartus 也华丽丽地升级到了13.1版本。
随着硬件平台及开发环境的更迭,项目版本的更新,深深感觉到代码的可维护性,可移植性异常重要,所以写下这篇笔记,笔者接触FPGA的时间并不长,水平有限,就当做抛砖引玉吧。
一,工程设计层次化,结构化要编写可维护、可移植的工程首先要对设计进行层次化、结构化设计。
不要急于Coding,先整理思路,做好体系结构和模块的划分。
一个设计任务需要按照功能或者类别分成若干个可独立操作的模块,每个模块又可以细分下去,这样可以由若干名工程师同时设计,当然也可以选用商业模块。
通过这样的逐次分解与模块的例化,整个工程能以设计树的方式绘出。
如图1所示:Top 顶层模块模块A 模块B 模块C模块A1 模块A2 模块B1 模块C1 模块C2这样的设计在后期维护以及移植的时候可以根据具体要求更新部分模块,尽量少地改变其他部分的代码和时序。
(由于FPGA结构的原因,即使修改部分模块,也会对整体的布局布线造成改变,需要对时序进行约束,而这又是另外一个课题了)二,模块及例化设计要做到可维护,可移植,对底层模块的要求较高,要求尽量做到功能独立、可重入。
功能独立便于移植裁剪、系统维护的时候可以只对部分模块进行修改。
而模块的重入性越高,维护和移植的时候修改的代码量就越小,只对底层模块修改即可,上层行为级模块可不做修改。
Verilog中以例化的方式调用其他功能模块,与C语言等软件函数调用不同的是,硬件描述语言的例化是实实在在的电路,同一个功能模块的多次例化分别是独立的实体,而不是软件函数简单的复用。
所以,上面的两种设计技巧只是从coding的方面考虑问题,而FPGA 向来要求从硬件的角度去考虑问题,底层模块的修改,意味电路结构,布局布线,时序都发生了变化,可维护性和可移植性在硬件的角度需要更多考虑。
关于verilog的学习经验简单分享 我学verilog语言进行FPGA设计也就半年时间,很多的东西就是在网上学到的,现在想说说自己对使用verilog进行FPGA设计的一些体会,我水平不高,主要是为新手朋友们介绍自己的一点经验少走点弯路。
1、verilog语言 学习verilog最重要的不是语法,因为10%的语法就能完成90%的工作,verilog语言常用语言就是always@(),if~else,case,assign这几个了,不用去专研繁杂的语法,有些问题等你碰到了查查书就好了。
这里推荐夏雨闻老师的《verilog数字系统设计教程》,一本很适合新手的好书。
2、硬件原则 虽然verilog语言很象c语言,但它和c语言还是有本质的区别的,因为verilog进行的是硬件设计,你写出来的东西是实实在在电路,所以要有数字电路的知识是肯定的。
数字电路就是由时序电路(触发器)和组合逻辑电路(各种逻辑门)构成的,用verilog写的程序在FPGA实现就是触发器和逻辑门,所以最重要的就是你对你写的语言生成的电路心中有数,做到这一点你就不会有写出来的程序不能综合的麻烦,电路的冗余逻辑肯定也是最少的。
还要注意一点就是verilog程序是并行的,不是象c那样是顺序执行的,这是因为fpga硬件可配置,可形成不同的任务单元同时工作;而单片机这种基于通用目的,硬件结构也固定了,它处理任务只能一件一件顺序的进行。
3、同步原则 在进行FPGA设计的时候,同步原则应该是最重要的原则之一了,因为异步电路的不可控性,很可能有毛刺产生,而在芯片内部的任何一点毛刺都会一级一级的传递下去,最终影响系统的稳定性。
同步原则用一句话来总结就是不要试图产生自己的时钟,最好一个设计或者一个模块只使用同一个时钟,这样所有的触发器都在同一个时钟沿跳变,当然最稳定了,系统也能跑到很高的速度。
一个小技巧就是多使用触发器的使能端和取沿电路。
4、养成好的代码习惯 很多的细节,比如缩进、命名、参数化、组合逻辑和时序逻辑分离、注释等等,也就是大家说的代码风格。
时序是设计出来的我的boss有在华为及峻龙工作的背景,自然就给我们讲了一些华为及altera 做逻辑的一些东西,而我们的项目规范,也基本上是按华为的那一套去做。
在工作这几个月中,给我感触最深的是华为的那句话:时序是设计出来的,不是仿出来的,更不是湊出来的。
在我们公司,每一个项目都有很严格的评审,只有评审通过了,才能做下一步的工作。
以做逻辑为例,并不是一上来就开始写代码,而是要先写总体设计方案和逻辑详细设计方案,要等这些方案评审通过,认为可行了,才能进行编码,一般来说这部分工作所占的时间要远大于编码的时间。
总体方案主要是涉及模块划分,一级模块和二级模块的接口信号和时序(我们要求把接口信号的时序波形描述出来)以及将来如何测试设计。
在这一级方案中,要保证在今后的设计中时序要收敛到一级模块(最后是在二级模块中)。
什么意思呢?我们在做详细设计的时候,对于一些信号的时序肯定会做一些调整的,但是这种时序的调整最多只能波及到本一级模块,而不能影响到整个设计。
记得以前在学校做设计的时候,由于不懂得设计时序,经常因为有一处信号的时序不满足,结果不得不将其它模块信号的时序也改一下,搞得人很郁闷。
在逻辑详细设计方案这一级的时候,我们已经将各级模块的接口时序都设计出来了,各级模块内部是怎么实现的也基本上确定下来了。
由于做到这一点,在编码的时候自然就很快了,最重要的是这样做后可以让设计会一直处于可控的状态,不会因为某一处的错误引起整个设计从头进行。
==================================================================== 如何提高电路工作频率对于设计者来说,我们当然希望我们设计的电路的工作频率(在这里如无特别说明,工作频率指FPGA片内的工作频率)尽量高。
我们也经常听说用资源换速度,用流水的方式可以提高工作频率,这确实是一个很重要的方法,今天我想进一步去分析该如何提高电路的工作频率。
我们先来分析下是什么影响了电路的工作频率。
我们电路的工作频率主要与寄存器到寄存器之间的信号传播时延及clock skew有关。
在FPGA内部如果时钟走长线的话,clock skew很小,基本上可以忽略, 在这里为了简单起见,我们只考虑信号的传播时延的因素。
信号的传播时延包括寄存器的开关时延、走线时延、经过组合逻辑的时延(这样划分或许不是很准确,不过对分析问题来说应该是没有可以的),要提高电路的工作频率,我们就要在这三个时延中做文章,使其尽可能的小。
我们先来看开关时延,这个时延是由器件物理特性决定的,我们没有办法去改变,所以我们只能通过改变走线方式和减少组合逻辑的方法来提高工作频率。
1.通过改变走线的方式减少时延。
以altera的器件为例,我们在quartus里面的timing closure floorplan 可以看到有很多条条块块,我们可以将条条块块按行和按列分,每一个条块代表1个LAB,每个LAB里有8个或者是10个LE。
它们的走线时延的关系如下:同一个LAB中(最快) < 同列或者同行 < 不同行且不同列。
我们通过给综合器加适当的约束(不可贪心,一般以加5%裕量较为合适,比如电路工作在100Mhz,则加约束加到105Mhz就可以了,贪心效果反而不好,且极大增加综合时间)可以将相关的逻辑在布线时尽量布的靠近一点,从而减少走线的时延。
(注:约束的实现不完全是通过改进布局布线方式去提高工作频率,还有其它的改进措施)2.通过减少组合逻辑的减少时延。
上面我们讲了可以通过加约束来提高工作频率,但是我们在做设计之初可万万不可将提高工作频率的美好愿望寄托在加约束上,我们要通过合理的设计去避免出现大的组合逻辑,从而提高电路的工作频率,这才能增强设计的可移植性,才可以使得我们的设计在移植到另一同等速度级别的芯片时还能使用。
我们知道,目前大部分FPGA都基于4输入LUT的,如果一个输出对应的判断条件大于四输入的话就要由多个LUT级联才能完成,这样就引入一级组合逻辑时延,我们要减少组合逻辑,无非就是要输入条件尽可能的少,,这样就可以级联的LUT更少,从而减少了组合逻辑引起的时延。
我们平时听说的流水就是一种通过切割大的组合逻辑(在其中插入一级或多级D触发器,从而使寄存器与寄存器之间的组合逻辑减少)来提高工作频率的方法。
比如一个32位的计数器,该计数器的进位链很长,必然会降低工作频率,我们可以将其分割成4位和8位的计数,每当4位的计数器计到15后触发一次8位的计数器,这样就实现了计数器的切割,也提高了工作频率。
在状态机中,一般也要将大的计数器移到状态机外,因为计数器这东西一般是经常是大于4输入的,如果再和其它条件一起做为状态的跳变判据的话,必然会增加LUT的级联,从而增大组合逻辑。
以一个6输入的计数器为例,我们原希望当计数器计到111100后状态跳变,现在我们将计数器放到状态机外,当计数器计到111011后产生个enable信号去触发状态跳变,这样就将组合逻辑减少了。
上面说的都是可以通过流水的方式切割组合逻辑的情况,但是有些情况下我们是很难去切割组合逻辑的,在这些情况下我们又该怎么做呢?状态机就是这么一个例子,我们不能通过往状态译码组合逻辑中加入流水。
如果我们的设计中有一个几十个状态的状态机,它的状态译码逻辑将非常之巨大,毫无疑问,这极有可能是设计中的关键路径。
那我们该怎么做呢?还是老思路,减少组合逻辑。
我们可以对状态的输出进行分析,对它们进行重新分类,并根据这个重新定义成一组组小状态机,通过对输入进行选择(case语句)并去触发相应的小状态机,从而实现了将大的状态机切割成小的状态机。
在ATA6的规范中(硬盘的标准),输入的命令大概有20十种,每一个命令又对应很多种状态,如果用一个大的状态机(状态套状态)去做那是不可想象的,我们可以通过case语句去对命令进行译码,并触发相应的状态机,这样做下来这一个模块的频率就可以跑得比较高了。
总结:提高工作频率的本质就是要减少寄存器到寄存器的时延,最有效的方法就是避免出现大的组合逻辑,也就是要尽量去满足四输入的条件,减少LUT级联的数量。
我们可以通过加约束、流水、切割状态的方法提高工作频率。
===================================================================== ==============做逻辑的难点在于系统结构设计和仿真验证刚去公司的时候BOSS就和我讲,做逻辑的难点不在于RTL级代码的设计,而在于系统结构设计和仿真验证方面。
目前国内对可综合的设计强调的比较多,而对系统结构设计和仿真验证方面似乎还没有什么资料,这或许也从一个侧面反映了国内目前的设计水平还比较低下吧。
以前在学校的时候,总是觉得将RTL级代码做好就行了,仿真验证只是形式而已,所以对HDL的行为描述方面的语法不屑一顾,对testbench也一直不愿意去学--因为觉得画波形图方便;对于系统结构设计更是一点都不懂了。
到了公司接触了些东西才发现完全不是这样。
其实在国外,花在仿真验证上的时间和人力大概是花在RTL级代码上的两倍,现在仿真验证才是百万门级芯片设计的关键路径。
仿真验证的难点主要在于怎么建模才能完全和准确地去验证设计的正确性(主要是提高代码覆盖),在这过程中,验证速度也是很重要的。
验证说白了也就是怎么产生足够覆盖率的激励源,然后怎么去检测错误。
我个人认为,在仿真验证中,最基本就是要做到验证的自动化。
这也是为什么我们要写testbench的原因。
在我现在的一个设计中,每次跑仿真都要一个小时左右(这其实算小设计)。
由于画波形图无法做到验证自动化,如果用通过画波形图来仿真的话,一是画波形会画死(特别是对于算法复杂的、输入呈统计分布的设计),二是看波形图要看死,三是检错率几乎为零。
那么怎么做到自动化呢?我个人的水平还很有限,只能简单地谈下BFM (bus function model,总线功能模型)。
以做一个MAC的core为例(背板是PCI总线),那么我们需要一个MAC_BFM 和PCI_BFM及PCI_BM(PCI behavior model)。
MAC_BFM的主要功能是产生以太网帧(激励源),随机的长度和帧头,内容也是随机的,在发送的同时也将其复制一份到PCI_BM中;PCI_BFM的功能则是仿PCI总线的行为,比如被测收到了一个正确帧后会向PCI总线发送一个请求,PCI_BFM则会去响应它,并将数据收进来;PCI_BM的主要功能是将MAC_BFM发送出来的东西与PCI_BFM接收到的东西做比较,由于它具有了MAC_BFM的发送信息和PCI_BFM的接收信息,只要设计合理,它总是可以自动地、完全地去测试被测是否工作正常,从而实现自动检测。
华为在仿真验证方面估计在国内来说是做的比较好的,他们已建立起了比较好的验证平台,大部分与通信有关的BFM都做好了,听我朋友说,现在他们只需要将被测放在测试平台中,并配置好参数,就可以自动地检测被测功能的正确与否。
在功能仿真做完后,由于我们做在是FPGA的设计,在设计时已经基本保证RTL级代码在综合结果和功能仿真结果的一致性,只要综合布局布线后的静态时序报告没有违反时序约束的警告,就可以下到板子上去调试了。
事实上,在华为中兴,他们做FPGA的设计时也是不做时序仿真的,因为做时序仿真很花时间,且效果也不见得比看静态时序分析报告好。
当然了,如果是ASIC的设计话,它们的仿真验证的工作量要大一些,在涉及到多时钟域的设计时,一般还是做后仿的。
不过在做后仿之前,也一般会先用形式验证工具和通过静态时序分序报告去查看有没有违反设计要求的地方,这样做了之后,后仿的工作量可以小很多。
在HDL语言方面,国内语言很多人都在争论VHDL和verilog哪个好,其实我个人认为这并没有多大的意义,外面的大公司基本上都是用verilog在做RTL级的代码,所以还是建议大家尽量学verilog。
在仿真方面,由于VHDL在行为级建模方面弱于verilog,用VHDL做仿真模型的很少,当然也不是说verilog 就好,其实verilog在复杂的行为级建模方面的能力也是有限的,比如目前它还不支持数组。
在一些复杂的算法设计中,需要高级语言做抽象才能描述出行为级模型。
在国外,仿真建模很多都是用System C和E语言,用verilog的都算是很落后的了,国内华为的验证平台好像是用System C写。
在系统结构设计方面,由于我做的设计还不够大,还谈不上什么经验,只是觉得必须要具备一些计算机系统结构的知识才行。
划分的首要依据是功能,之后是选择合适的总线结构、存储结构和处理器架构,通过系统结构划分要使各部分功能模块清晰,易于实现。