查公司注册信息怎么查,昆明网站seo报价,wordpress首页显示vip标志,能打开各种网站的搜索引擎对Verilog 初学者比较有用的整理(转自它处) 作者: Ian11122840 时间: 2010-9-27 09:04 标题: 菜鸟做设计必看#xff01;有关如何做设计的整体思路#xff0c;以及能否综合的笔记 所谓综合#xff0c;就是把描述语言转化成能硬件实现的电路#xff0c;学verilog的时候有关如何做设计的整体思路以及能否综合的笔记 所谓综合就是把描述语言转化成能硬件实现的电路学verilog的时候没有人给我说要不要考虑能否综合的问题~ 看了5本书居然没有一本书讲到能否综合所以设计出来的程序完全不能用~ 而且书中都是讲语句的具体使用办法例如always ()但是什么时候用always几个always之间、时序电路、逻辑电路、任务与函数什么时候用却没有一本书能讲清楚。 *这个笔记详细写了这些思路的问题分享给新手看看学习一种思路~~ 点击此处下载 ourdev_585849OJ54KV.doc(文件大小:720K) (原文件名:verilog_经验(适合初学者).doc) 先记下来 1、不使用初始化语句 2、不使用延时语句 3、不使用循环次数不确定的语句如foreverwhile等 4、尽量采用同步方式设计电路 5、尽量采用行为语句完成设计 6、always过程块描述组合逻辑应在敏感信号表中列出所有的输入信号 7、所有的内部寄存器都应该可以被复位 8、用户自定义原件UDP元件是不能被综合的。 一基本 Verilog中的变量有线网类型和寄存器类型。线网型变量综合成wire而寄存器可能综合成WIRE锁存器和触发器还有可能被优化掉。 二verilog语句结构到门级的映射 1、连续性赋值assign 连续性赋值语句逻辑结构上就是将等式右边的驱动左边的结点。因此连续性赋值的目标结点总是综合成由组合逻辑驱动的结点。Assign语句中的延时综合时都将忽视。 2、过程性赋值 过程性赋值只出现在always语句中。 阻塞赋值和非阻塞赋值就该赋值本身是没有区别的只是对后面的语句有不同的影响。 建议设计组合逻辑电路时用阻塞赋值设计时序电路时用非阻塞赋值。 过程性赋值的赋值对象有可能综合成wire, latch,和flip-flop取决于具体状况。如时钟控制下的非阻塞赋值综合成flip-flop。 过程性赋值语句中的任何延时在综合时都将忽略。 建议同一个变量单一地使用阻塞或者非阻塞赋值。 3、逻辑操作符 逻辑操作符对应于硬件中已有的逻辑门一些操作符不能被综合、!。 4、算术操作符 Verilog中将reg视为无符号数而integer视为有符号数。因此进行有符号操作时使用integer,使用无符号操作时使用reg。 5、进位 通常会将进行运算操作的结果比原操作数扩展一位用来存放进位或者借位。如 Wire [3:0] A,B; Wire [4:0] C; Assign CAB; C的最高位用来存放进位。 6、关系运算符 关系运算符,,, 和算术操作符一样可以进行有符号和无符号运算取决于数据类型是regnet还是integer。 7、相等运算符 注意和是不可综合的。 可以进行有符号或无符号操作取决于数据类型 8、移位运算符 左移右移右边操作数可以是常数或者是变量二者综合出来的结果不同。 9、部分选择 部分选择索引必须是常量。 10、BIT选择 BIT选择中的索引可以用变量这样将综合成多路复用器。 11、敏感表Always过程中所有被读取的数据即等号右边的变量都要应放在敏感表中不然综合时不能正确地映射到所用的门。 12、IF 如果变量没有在IF语句的每个分支中进行赋值将会产生latch。如果IF语句中产生了latch则IF的条件中最好不要用到算术操作。Case语句类似。Case的条款可以是变量。 如果一个变量在同一个IF条件分支中先赎值然后读取则不会产生latch。如果先读取后赎值则会产生latch。 13、循环 只有for-loop语句是可以综合的。 14、设计时序电路时建议变量在always语句中赋值而在该always语句外使用使综合时能准确地匹配。建议不要使用局部变量。 15、不能在多个always块中对同一个变量赎值 16、函数 函数代表一个组合逻辑所有内部定义的变量都是临时的这些变量综合后为wire。 17、任务 任务可能是组合逻辑或者时序逻辑取决于何种情况下调用任务。 18、Z Z会综合成一个三态门必须在条件语句中赋值 19、参数化设计 优点参数可重载不需要多次定义模块 四模块优化 1、资源共享 当进程涉及到共用ALU时要考虑资源分配问题。可以共享的操作符主要有关系操作符、加减乘除操作符。通常乘和加不共用ALU乘除通常在其内部共用。 2、共用表达式 如CAB; DG(AB); 两者虽然有共用的但是有些综合工具不能识别可以将第二句改为DGC这样只需两个加法器 3、转移代码 如循环语句中没有发生变化的语句移出循环 4、避免latch 两种方法1、在每一个IF分支中对变量赋值。2、在每一个IF语句中都对变量赋初值。 5模块 综合生成的存储器如ROM或RAM不是一种好方法只是成堆的寄存器很费资源。最好用库自带的存储器模块。 五、验证 、敏感表 在always语句中如果敏感表不含时钟最好将所有的被读取的信号都放在敏感表中。 、异步复位 建议不要在异步时对变量读取即异步复位时对信号赋以常数值。
Averilog的流行,有两方面的原因; B verilog与VHDL相比的优点 C典型的verilog模块 D verilog语法要点
A) verilog的流行,有两方面的原因: 1它是cadence的模拟器verilog-XL的基础,cadence的广泛流行使得verilog在90年代深入人心; 2它在硅谷获得广泛使用; B) verilog与VHDL相比的优点二者的关系仿佛C与FORTRAN,具体而言: 1 verilog的代码效率更高: 比较明显的对比: VHDL在描述一个实体时采用entity/architecture模式, verilog在描述一个实体时只需用一个”module/edumodule”语句块. 此外verilog的高效性还在很多地方体现出来; 2 verilog支持二进制的加减运算: VHDL在进行二进制的加减运算时使用conv_***函数或者进行其他的定义,总之必须通知编译器;verilog直接用形如”cab”的表示二进制的加减运算; 3综合时可控制性好: VHDL对信号不加区分地定义为”signal”, 而verilog区分为register类型的和wire类型的; 但是也有人支持VHDL,认为verilog和VHDL的关系仿佛C和C.C)典型的verilog模块 讨论以下典型电路的verilog描述: *与非门; *加法器; //即全加器 * D触发器; 计数器; //*分频的counter * latch; *时序机; *RAM; //用synopsys的 *模块引用; *预编译; *与非门的verilog描述如下: //verilog使用和C语言相同的注释方法 module nd02(a1,a2,zn);//一个verilog模块总是以module开始,以endmodule 结束,nd02是模块名,a1,a2,zn是模块的3个输入输出信号 input a1,a2; //告诉编译器a1,a2对此模块而言是输入,并且数据类型是”bit” output zn; //告诉编译器zn对此模块而言是输出,数据类型也是”bit” nand (zn,a1,a2); //我理解nand是运算符,我们不必深究verilog中的正式术语是什 么了吧总之这种形式表示zn~(a1 a2);你一定已经想到类似的运算符还有”not”,”and”,”or”,”nor”,”xor”了吧;除了”not”,括号里的信号数可以任意,例如or (z,f,g,h)表示zf || g || h,并且延时是3个单位时间#x表示延时x个单位时间; endmodule
*加法器的verilog描述如下: module ad03d1(A,B,CI,S,CO) ; input [2:0] A,B; //表示A,B是输入信号,并且是3位矢量,上界是2,下界是0 input CI; output [2:0] S; output CO; assign {CO,S}ABCI;//一对”{“和”}”表示链接,即将CO和S合并成4位矢量 endmodule
*带异步清零端的D触发器的verilog描述如下: module dfctnb (d,cp,cdn,q,qn); input d,cp,cdn; output q,qn; reg q,qn; //关键字”reg”表示q和qn是”register”类型的信号;verilog中有两种类型的信号:”register”类型和”wire”类型.你可以简单地把register类型的信号想象为某个D触发器的输出,而wire类型的的信号是组合逻辑的输出.二者的最大区别在于:你可以对register类型的信号进行定时赋值(用wait语句在特定时刻的赋值,详见下面always语句),而对于wire类型的信号则不可. always wait (cdn0) //表示每当cdn0时,将要对D触发器清零,”always”和”wait”嵌套”wait”和””是verilog的两个关键字,表示一旦有某事发生则执行下面的语句块,”always”有点象C语言中的”if … then…””wait”和””的区别:请参考本模块.wait表示本语句块的进程停止,直到”cdn0”的条件出现才继续我理解在verilog中,每个最外层语句块都是一个*的进程;””(请看下个always语句)也表示本语句块的进程停止,直到后面定义”posedge cp”(即出现cp的上升沿)的事件出现才继续;也许wait和可以合二为一吧,但至少到目前verilog中wait表示”条件”,表示”事件”;具体运用中,wait总是用于类似”wait(xxx1)”之类的场合,总是用于类似”(xxx)”或”(posedge/negedge xxx)”之类的场合整句话的意思是”每当cdn等于0时,则作以下事情” begin //begin…end结构的用法类似于pascal语言 q0; qn1; wait (cdn1); end always (posedge cp)//”(posedge cp)”中有两个关键字:” (x)”表示”每当事件x发 生”,”posedge x”表示”x的上升沿,”negedge x”表示”x的下降沿”整句话的意思是”每当cp的上升沿,则作以下事情” if (cdn) //如果cdn1(意味着清零端无效) begin qd; qn~q;//”~”表示反相 end endmodule *计数器的verilog描述如下: module count(in,set,cp,out) ;//此计数器,在cp的上升沿将输入赋给输出,在cp的上升沿使输出加一 input [15:0] in; input set,cp; output [15:0] out; reg [15:0] out; always (posedge set) out in; always (posedge cp) out out1; //verilog容许一个信号同时出现在等号两端,只要它是reg类型的 endmodule
*latch的描述如下: always (clk or d) if (clk) q d;
*时序机的verilog描述如下: always (posedge CLK) //D是下一个状态,Q是当前状态,e1,e2是输入,a,b是输出 QD; always (Q or othercase) begin //当Q变化或输入e1,e2变化时D要相应变化 D Q; //note1 a 0; b 0; …… case(Q) q1:begin q1 action; if(e1)Dd1; if(e2)Dd2; else Dd3; a 1; //note 2 end q2:begin b 1; …… end default:begin a 0; b 0; …… end end —annotations— note 1: This is a custom expression,after reset,D should be equal to Q; note 2: In this state machine,a is only equal to 1 at state q1,in other state,a is equal to 0; * RAM的verilog描述如下: module ram(din,ain,dout,aout,rd,wr);//这是一个双口RAM,分别有:输入端:输入地址ain;输入数据din;上升沿有效的写信号wr;/输出端:输出地址aout;输出数据dout;高电平有效的读信号rd; inout [7:0] din; input [7:0] ain,aout; input rd,wr; output [7:0] dout; reg [7:0] memory [0:255]; //请注意这是存储阵列的描述方法,描述了一个共有2 56个字的存储阵列,每个字是8位 assign dout rd ? memory[aout] : 8’bz; //”assign”关键字表示并行赋值语句的 开始”?”运算符的作用和在C语言中一样”8’bz”是一个常量,表示一个字节的高阻态,其中8表示长度是8bit,”’”是固定分割符,”b”表示后面的数据是以比特形式给出的,”z”表示高阻;举例:4’ha表示长4bit的数”1010”。类似的还可举出5’b10111,6’o33等等 always (posedge wr) memory[ain] din; endmodule *模块引用 假设在前面(可以是别的模块)定义了module ram(din,ain,dout,aout,rd,wr),则引用此 模块时只需写 ram myram(din_in_map,ain_in_map,dout_in_map,aout_in_map,rd_in_map,wr_in_map) ; //其中”ram”是所引用的module名,”myram”是你起的instance名,”din_in_map”等等是图中的节点名,和器件(module)中的”din…”进行”虚实结合”; *预编译 类似C语言,只需写 include pathname:filename,反上撇号“是verilog的预编译符,类似C中的”#”. D) verilog语法要点 *基本原则 设计时应该把你的系统划分为计数器,触发器,时序机,组合逻辑等等可综合的单元,对此不同的IC公司和EDA开发商可能根据自己的见解和经验提出不同的要求,并且对verilog程序的细节进行自己的规定,但有一点是对的:即写硬件描述语言不象写C语言那样符合语法就行.单单符合verilog语法的程序可能被拒绝综合,甚至被拒绝模拟; *最外层可以写什么? 这里所说的最外层是指module语句后的第一层,在这一层可以写这些可执行语句: assign和nand等定义组合逻辑的语句, always语句, 模块引用语句, 一些以”$”开头的系统定义语句. 特别注意不可以写if语句.if语句只能放在always内部. 不推荐写wait语句因为不能综合. *不可以在多个always语句中对一个信号赋值.
强烈建议用同步设计 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自带的SRAMASIC版本一般用厂商提供的SRAM。 13.在嵌入式存储器中使用BIST 14.虚单元和一些修正电路是必需的 15.一些简单的测试电路也是需要的经常在一个芯片中有许多测试模块 16.除非低功耗不要用门控时钟强烈建议不要在design中使用gate clock 17.不要依靠脚本来保证设计。但是在脚本中的一些好的约束能够起到更好的性能例如前向加法器 18.如果时间充裕通过时钟做一个多锁存器来取代用MUX 19.不要用内部tri-state, ASIC需要总线保持器来处理内部tri-state如IO cell。 20.在top level中作pad insertion 21.选择pad时要小心如上拉能力施密特触发器5伏耐压等选择合适的IO cell 22.小心由时钟偏差引起的问题 23.不要试着产生半周期信号 24.如果有很多函数要修正请一个一个地作修正一个函数检查一个函数 25.在一个计算等式中排列每个信号的位数是一个好习惯即使综合工具能做 26.不要使用HDL提供的除法器 27.削减不必要的时钟。它会在设计和布局中引起很多麻烦大多数FPGA有14个专门的时钟通道
良好代码编写风格可以满足信、达、雅的要求。在满足功能和性能目标的前提下增强代码的可读性、可移植性首要的工作是在项目开发之前为整个设计团队建立一个命名约定和缩略语清单以文档的形式记录下来并要求每位设计人员在代码编写过程中都要严格遵守。良好代码编写风格的通则概括如下 1 对所有的信号名、变量名和端口名都用小写这样做是为了和业界的习惯保持一致对常量名和用户定义的类型用大写 2 使用有意义的信号名、端口名、函数名和参数名 3 信号名长度不要太长 4 对于时钟信号使用clk 作为信号名如果设计中存在多个时钟使用clk 作为时钟信号的前缀 5 对来自同一驱动源的信号在不同的子模块中采用相同的名字这要求在芯片总体设计时就定义好顶层子模块间连线的名字端口和连接端口的信号尽可能采用相同的名字 6 对于低电平有效的信号应该以一个下划线跟一个小写字母b 或n 表示。注意在同一个设计中要使用同一个小写字母表示低电平有效 7 对于复位信号使用rst 作为信号名如果复位信号是低电平有效建议使用rst_n 8 当描述多比特总线时使用一致的定义顺序对于verilog 建议采用bus_signal[x:0]的表示 9 尽量遵循业界已经习惯的一些约定。如*_r 表示寄存器输出*_a 表示异步信号*_pn 表示多周期路径第n 个周期使用的信号*_nxt 表示锁存前的信号*_z 表示三态信号等 10在源文件、批处理文件的开始应该包含一个文件头、文件头一般包含的内容如下例所示文件名作者模块的实现功能概述和关键特性描述文件创建和修改的记录包括修改时间修改的内容等 11使用适当的注释来解释所有的always 进程、函数、端口定义、信号含义、变量含义或信号组、变量组的意义等。注释应该放在它所注释的代码附近要求简明扼要只要足够说明设计意图即可避免过于复杂 12每一行语句独立成行。尽管VHDL 和Verilog 都允许一行可以写多个语句当时每个语句独立成行可以增加可读性和可维护性。同时保持每行小于或等于72 个字符这样做都是为了提高代码得可读性 13建议采用缩进提高续行和嵌套语句得可读性。缩进一般采用两个空格如西安交通大学SOC 设计中心2 如果空格太多则在深层嵌套时限制行长。同时缩进避免使用TAB 键这样可以避免不同机器TAB 键得设置不同限制代码得可移植能力 14在RTL 源码的设计中任何元素包括端口、信号、变量、函数、任务、模块等的命名都不能取Verilog 和VHDL 语言的关键字 15在进行模块的端口申明时每行只申明一个端口并建议采用以下顺序 输入信号的clk、rst、enables other control signals、data and address signals。然后再申明输出信号的clk、rst、enalbes other control signals、data signals 16在例化模块时使用名字相关的显式映射而不要采用位置相关的映射这样可以提高代码的可读性和方便debug 连线错误 17如果同一段代码需要重复多次尽可能使用函数如果有可能可以将函数通用化以使得它可以复用。注意内部函数的定义一般要添加注释这样可以提高代码的可读性 18尽可能使用循环语句和寄存器组来提高源代码的可读性这样可以有效地减少代码行数 19对一些重要的always 语句块定义一个有意义的标号这样有助于调试。注意标号名不要与信号名、变量名重复 20代码编写时的数据类型只使用IEEE 定义的标准类型在VHDL 语言中设计者可以定义新的类型和子类型但是所有这些都必须基于IEEE 的标准 21在设计中不要直接使用数字作为例外可以使用0 和1。建议采用参数定义代替直接的数字。同时在定义常量时如果一个常量依赖于另一个常量建议在定义该常量时用表达式表示出这种关系 22不要在源代码中使用嵌入式的dc_shell 综合命令。这是因为其他的综合工具并不认得这些隐含命令从而导致错误的或较差的综合结果。即使使用Design Compiler当综合策略改变时嵌入式的综合命令也不如放到批处理综合文件中易于维护。这个规则有一个例外的综合命令即编译开关的打开和关闭可以嵌入到代码中 23在设计中避免实例化具体的门级电路。门级电路可读性差且难于理解和维护如果使用特定工艺的门电路设计将变得不可移植。如果必须实例化门电路我们建议采用独立于工艺库的门电路如SYNOPSYS 公司提供的GTECH 库包含了高质量的常用的门级电路 24避免冗长的逻辑和子表达式 25避免采用内部三态电路建议用多路选择电路代替内部三态电路。
规则 #1: 建立时序逻辑模型时采用非阻塞赋值语句。 规则 #2: 建立latch模型时采用非阻塞赋值语句。 规则 #3: 在always块中建立组合逻辑模型时采用阻塞赋值语句。 规则 #4: 在一个always块中同时有组合和时序逻辑时时采用非阻塞赋值语句。 规则 #5: 不要在一个always块中同时采用阻塞和非阻塞赋值语句。 规则 #6: 同一个变量不要在多个always块中赋值。 规则 #7: 调用$strobe系统函数显示用非阻塞赋值语句赋的值。 规则 #8: 不要使用#0延时赋值。 组合逻辑 1敏感变量的描述完备性 Verilog中用always块设计组合逻辑电路时在赋值表达式右端参与赋值的所有信号都必须在always (敏感电平列表)中列出,always中if语句的判断表达式必须在敏感电平列表中列出。如果在赋值表达式右端引用了敏感电平列表中没有列出的信号在综合时将会为没有列出的信号隐含地产生一个透明锁存器。这是因为该信号的变化不会立刻引起所赋值的变化而必须等到敏感电平列表中的某一个信号变化时它的作用才表现出来即相当于存在一个透明锁存器把该信号的变化暂存起来待敏感电平列表中的某一个 信号变化时再起作用纯组合逻辑电路不可能作到这一点。综合器会发出警告。 Example1: input a,b,c; reg e,d; always (a or b or c) begin edab; /d没有在敏感电平列表中,d变化时e不会立刻变化,直到a,b,c中某一个变化/ de |c; end Example2: input a,b,c; reg e,d; always (a or b or c or d) begin edab; /d在敏感电平列表中,d变化时e立刻变化/ de |c; end 2,条件的描述完备性 如果if语句和case语句的条件描述不完备也会造成不必要的锁存器。 Example1: if (a1’b1) q1’b1;//如果a1’b0,q? q将保持原值不变生成锁存器 Example2: if (a1’b1) q1’b1; else q1’b0;//q有明确的值。不会生成锁存器 Example3: reg[1:0] a,q; …. case (a) 2’b00 : q2’b00; 2’b01 : q2’b11;//如果a2’b10或a2’b11,q? q将保持原值不变锁存器 endcase Example4: reg[1:0] a,q; …. case (a) 2’b00 : q2’b00; 2’b01 : q2’b11; default: q2’b00;//q有明确的值。不会生成锁存器 endcase Verilog中端口的描述 1端口的位宽最好定义在I/O说明中,不要放在数据类型定义中 Example1: module test(addr,read,write,datain,dataout) input[7:0] datain; input[15:0] addr; input read,write; output[7:0] dataout; //要这样定义端口的位宽 wire addr,read,write,datain; reg dataout; Example2: module test(addr,read,write,datain,dataout) input datain,addr,read,write; output dataout; wire[15:0] addr; wire[7:0] datain; wire read,write; reg[7:0] dataout; //不要这样定义端口的位宽 2端口的I/O与数据类型的关系 端口的I/O 端口的数据类型 module内部 module外部 input wire wire或reg output wire或reg wire inout wire wire 3assign语句的左端变量必须是wire直接用””给变量赋值时左端变量必须是reg Example: assign ab; //a必须被定义为wire begin ab; //a必须被定义为reg end VHDL中STD_LOGIC_VECTOR和INTEGER的区别 例如A是INTEGER型范围从0到255B是STD_LOGIC_VECTOR定义为8位。A累加到255时再加1就一直保持255不变不会自动反转到0除非令其为0而B累加到255时再加1就会自动反转到0。所以在使用时要特别注意 以触发器为例说明描述的规范性 1无置位/清零的时序逻辑 always ( posedge CLK) begin QD; end 2有异步置位/清零的时序逻辑 异步置位/清零是与时钟无关的当异步置位/清零信号到来时触发器的输出立即 被置为1或0不需要等到时钟沿到来才置位/清零。所以必须要把置位/清零信号 列入always块的事件控制表达式。 always ( posedge CLK or negedge RESET) begin if (!RESET) Q0; else QD; end 3有同步置位/清零的时序逻辑 同步置位/清零是指只有在时钟的有效跳变时刻置位/清零才能使触发器的输出分 别转换为1或0。所以不要把置位/清零信号列入always块的事件控制表达式。但是 必须在always块中首先检查置位/清零信号的电平。 always ( posedge CLK ) begin if (!RESET) Q0; else QD; end 结构规范性 在整个芯片设计项目中行为设计和结构设计的编码是最重要的一个步骤。 它对逻辑综合和布线结果、时序测定、校验能力、测试能力甚至产品支持 都有重要的影响。考虑到仿真器和真实的逻辑电路之间的差异为了有效的 进行仿真测试 1避免使用内部生成的时钟 内部生成的时钟称为门生时钟gated clock。如果外部输入时钟和门生时钟同时驱动 则不可避免的两者的步调不一致造成逻辑混乱。而且门生时钟将会增加测试的难度 和时间。 2绝对避免使用内部生成的异步置位/清零信号 内部生成的置位/清零信号会引起测试问题。使某些输出信号被置位或清零无法正常 测试。 3避免使用锁存器 锁存器可能引起测试问题。对于测试向量自动生成ATPG 为了使扫描进行锁存器需要置为透明模式transparent mode 反过来测试锁存器需要构造特定的向量这可非同一般。 4时序过程要有明确的复位值 使触发器带有复位端在制造测试、ATPG以及模拟初始化时可以对整个电路进行 快速复位。 5避免模块内的三态/双向 内部三态信号在制造测试和逻辑综合过程中难于处理.
近日读 J.Bhasker 的 , 受益匪浅,理清了不少基础电路知识 , 记下一些 tips : 1. 过程赋值(always 中触发赋值)的变量,可能会被综合成连线 或触发器 或锁存器. 2.综合成锁存器的规则: a. 变量在条件语句(if 或case)中,被赋值. b. 变量未在条件语句的所有分支中被赋值. c. 在always语句多次调用之间需要保持变量值 . 以上三个条件必须同时满足. 3.综合成触发器的规则: 变量在时钟沿的控制下被赋值。 例外情况变量的赋值和引用都仅出现在一条always语句中则该变量被视为中 间变量而不是触发器。 4. 对于无时钟事情的always语句即组合逻辑建模其时间表应包括该alwa语 句引用的所有变量否则会出现RTL与Netlist的不一致 芯片外部引脚很多都使用inout类型的为的是节省管腿。一般信号线用做总线等双向数据传输的时候就要用到INOUT类型了。就是一个端口同时做输入和输出。 inout在具体实现上一般用三态门来实现。三态门的第三个状态就是高阻’Z’。 当inout端口不输出时将三态门置高阻。这样信号就不会因为两端同时输出而出错了,更详细的内容可以搜索一下三态门tri-state的资料. 1 使用inout类型数据,可以用如下写法: inout data_inout; input data_in; reg data_reg;//data_inout的映象寄存器 reg link_data; assign data_inoutlink_data?data_reg:1’bz;//link_data控制三态门 //对于data_reg,可以通过组合逻辑或者时序逻辑根据data_in对其赋值.通过控制link_data的高低电平,从而设置data_inout是输出数据还是处于高阻态,如果处于高阻态,则此时当作输入端口使用.link_data可以通过相关电路来控制. 2 编写测试模块时,对于inout类型的端口,需要定义成wire类型变量,而其它输入端口都定义成reg类型,这两者是有区别的. 当上面例子中的data_inout用作输入时,需要赋值给data_inout,其余情况可以断开.此时可以用assign语句实现:assign data_inoutlink?data_in_t:1’bz;其中的link ,data_in_t是reg类型变量,在测试模块中赋值. 另外,可以设置一个输出端口观察data_inout用作输出的情况: Wire data_out; Assign data_out_t(!link)?data_inout:1’bz; elsein RTL inout use in top module(PAD) dont use inout(tri) in sub module 也就是说在内部模块最好不要出现inout如果确实需要那么用两个port实现到顶层的时候再用三态实现。理由是在非顶层模块用双向口的话该双向口必然有它的上层跟它相连。既然是双向口则上层至少有一个输入口和一个输出口联到该双向口上则发生两个内部输出单元连接到一起的情况出现这样在综合时往往会出错。 对双向口我们可以将其理解为2个分量一个输入分量一个输出分量。另外还需要一个控制信号控制输出分量何时输出。此时我们就可以很容易地对双向端口建模。 例子 CODE: module dual_port ( …. inout_pin, …. ); inout inout_pin; wire inout_pin; wire input_of_inout; wire output_of_inout; wire out_en; assign input_of_inout inout_pin; assign inout_pin out_en ? output_of_inout : 高阻; endmodule 可见此时input_of_inout和output_of_inout就可以当作普通信号使用了。 在仿真的时候需要注意双向口的处理。如果是直接与另外一个模块的双向口连接那么只要保证一个模块在输出的时候另外一个模块没有输出处于高阻态就可以了。 如果是在ModelSim中作为单独的模块仿真那么在模块输出的时候不能使用force命令将其设为高阻态而是使用release命令将总线释放掉 很多初学者在写testbench进行仿真和验证的时候被inout双向口难住了。仿真器老是提示错误不能进行。下面是我个人对inout端口写testbench仿真的一些总结并举例进行说明。在这里先要说明一下inout口在testbench中要定义为wire型变量。 先假设有一源代码为 module xx(data_inout , ……..); inout data_inout; …………………… assign data_inout(! link)?datareg:1’bz; endmodule 方法一使用相反控制信号inout口等于两个模块之间用inout双向口互连。这种方法要注意assign 语句只能放在initial和always块内。 module test(); wire data_inout; reg data_reg; reg link; initial begin ………. end assign data_inoutlink?data_reg:1’bz; endmodule 方法二使用force和release语句但这种方法不能准确反映双向端口的信号变化但这种方法可以反在块内。 module test(); wire data_inout; reg data_reg; reg link;
xx; //延时
force data_inout1’bx; //强制作为输入端口 ……………
xx;
release data_inout; //释放输入端口 endmodule 很多读者反映仿真双向端口的时候遇到困难这里介绍一下双向端口的仿真方法。一个典型的双向端口如图1所示。
其中inner_port与芯片内部其他逻辑相连outer_port为芯片外部管脚out_en用于控制双向端口的方向out_en为1时端口为输出方向out_en为0时端口为输入方向。
用Verilog语言描述如下 module bidirection_io(inner_port,out_en,outer_port); input out_en; inout[7:0] inner_port; inout[7:0] outer_port; assign outer_port(out_en1)?inner_port:8’hzz; assign inner_port(out_en0)?outer_port:8’hzz; endmodule
用VHDL语言描述双向端口如下 library ieee; use IEEE.STD_LOGIC_1164.ALL; entity bidirection_io is port ( inner_port : inout std_logic_vector(7 downto 0); out_en : in std_logic; outer_port : inout std_logic_vector(7 downto 0) ); end bidirection_io; architecture behavioral of bidirection_io is begin outer_portinner_port when out_en’1’ else (OTHERS’Z’); inner_portouter_port when out_en’0’ else (OTHERS’Z’); end behavioral;
仿真时需要验证双向端口能正确输出数据以及正确读入数据因此需要驱动out_en端口当out_en端口为1时testbench驱动inner_port端口然后检查outer_port端口输出的数据是否正确当out_en端口为0时testbench驱动outer_port端口然后检查inner_port端口读入的数据是否正确。由于inner_port和outer_port端口都是双向端口在VHDL和Verilog语言中都用inout定义因此驱动方法与单向端口有所不同。 验证该双向端口的testbench结构如图2所示。
这是一个self-checking testbench可以自动检查仿真结果是否正确并在Modelsim控制台上打印出提示信息。图中Monitor完成信号采样、结果自动比较的功能。 testbench的工作过程为 1out_en1时双向端口处于输出状态testbench给inner_port_tb_reg信号赋值然后读取outer_port_tb_wire的值如果两者一致双向端口工作正常。 2out_en0时双向端口处于输如状态testbench给outer_port_tb_reg信号赋值然后读取inner_port_tb_wire的值如果两者一致双向端口工作正常。
用Verilog代码编写的testbench如下其中使用了自动结果比较随机化激励产生等技术。
timescale 1ns/10ps module tb(); reg[7:0] inner_port_tb_reg; wire[7:0] inner_port_tb_wire; reg[7:0] outer_port_tb_reg; wire[7:0] outer_port_tb_wire; reg out_en_tb; integer i;
initial begin out_en_tb0; inner_port_tb_reg0; outer_port_tb_reg0; i0; repeat(20) begin
50
irandom;outentbi[0];//randomizeoutentbinnerporttbregrandom;
out_en_tb=i[0]; //randomize out_en_tb
inner_port_tb_reg=random; //randomize data outer_port_tb_reg$random; end end
//** drive the ports connecting to bidirction_io assign inner_port_tb_wire(out_en_tb1)?inner_port_tb_reg:8’hzz; assign outer_port_tb_wire(out_en_tb0)?outer_port_tb_reg:8’hzz;
//instatiate the bidirction_io module bidirection_io bidirection_io_inst(.inner_port(inner_port_tb_wire), .out_en(out_en_tb), .outer_port(outer_port_tb_wire));
// monitor * always(out_en_tb,inner_port_tb_wire,outer_port_tb_wire) begin
1;
if(outer_port_tb_wireinner_port_tb_wire) begin display(\n∗∗∗∗timedisplay("\n **** time=%t ****",time); display(“OK!outendisplay(“OK! out_en=%d”,out_en_tb);
display(“OK! outer_port_tb_wire%d,inner_port_tb_wire%d”, outer_port_tb_wire,inner_port_tb_wire); end else begin display(\n∗∗∗∗timedisplay("\n **** time=%t ****",time); display(“ERROR!outendisplay(“ERROR! out_en=%d”,out_en_tb);
display(“ERROR! outer_port_tb_wire ! inner_port_tb_wire” ); $display(“ERROR! outer_port_tb_wire%d, inner_port_tb_wire%d”, outer_port_tb_wire,inner_port_tb_wire); end end endmodule
今天重新回顾了一下阻塞赋值和非阻塞赋值的概念感觉又有所收获。 一、特点 阻塞赋值1、RHS的表达式计算和LHS的赋值更新这两个动作之间不能插入其他动作即所谓计算完毕立即更新。 2、所谓阻塞就是指在一个“begin…end”块中的多个阻塞赋值语句内只有上一句完全执行完毕后才会执行下一语句否则阻塞程序的执行。 非阻塞赋值RHS的表达式计算和LHS的赋值更新分两个节拍执行首先应该是RHS的表达式计算得到新值后并不立即赋值而是放在事件队列中等待直到 当前仿真时刻的后期才执行原因下文会提到。 二、Verilog的分层事件队列 在Verilog中事件队列可以划分为5个不同的区域不同的事件根据规定放在不同的区域内按照优先级的高低决定执行的先后顺序下表就列出了部分Verilog分层事件队列。其中活跃事件的优先级最高最先执行而监控事件的优先级最低而且在活跃事件中的各事件的执行顺序是随机的注为方便起见在一般的仿真器中对同一区域的不同事件是按照调度的先后关系执行的。
当前仿真 时间事件
活跃事件 阻塞赋值非阻塞赋值的RHS计算……
非活跃事件
显式0延时的阻塞赋值……
非阻塞赋值更新事件
由非阻塞语句产生的一个非阻塞赋值更新事件并被调入当前仿真时刻。
监控事件
monitor和monitor和strobe等系统任务
将来仿真 时间事件 被调度到将来仿真时间的事件
三、结论 由上表就可以知道阻塞赋值属于活跃事件会立刻执行这就是阻塞赋值“计算完毕立刻更新”的原因。此外由于在分层事件队列中只有将活跃事件中排在前面的事件调出并执行完毕后才能够执行下面的事件这就可以解释阻塞赋值的第二个特点。 同样是由上表知非阻塞赋值的RHS计算属于活跃事件而非阻塞赋值更新事件排在非活跃事件之后因此只有仿真队列中所有的活跃事件和非活跃事件都执行完毕后才轮到非阻塞赋值更新事件这就是非阻塞赋值必须分两拍完成的原因。
以上就是我今天的读书笔记写得仓促如有不对敬请指出 。
一. 强调Verilog代码编写风格的必要性。 强调Verilog代码编写规范经常是一个不太受欢迎的话题但却是非常有必要的。 每个代码编写者都有自己的编写习惯而且都喜欢按照自己的习惯去编写代码。与自己编写风格相近的代码阅读起来容易接受和理解。相反和自己编写风格差别较大的代码阅读和接受起来就困难一些。 曾有编程大师总结说一个优秀的程序员能维护的代码长度大约在1万行数量级。代码的整洁程度很大程度上影响着代码的维护难度。 遵循代码编写规范书写的代码很容易阅读、理解、维护、修改、跟踪调试、整理文档。相反代码编写风格随意的代码通常晦涩、凌乱会给开发者本人的调试、修改工作带来困难也会给合作者带来很大麻烦。 实际上英文Coding Style有另一层涵义更偏重的是某一个电路用那一种形式的语言描述才能将电路描述得更准确综合以后产生的电路更合理。本文更偏重的是编写Verilog代码时的书写习惯。
二. 强调编写规范的宗旨。 缩小篇幅 提高整洁度 便于跟踪、分析、调试 增强可读性帮助阅读者理解 便于整理文档 便于交流合作 三. 变量及信号命名规范。 1. 系统级信号的命名。 系统级信号指复位信号置位信号时钟信号等需要输送到各个模块的全局信号系统信号以字符串Sys开头。 2. 低电平有效的信号后一律加下划线和字母n。如SysRst_nFifoFull_n 3. 经过锁存器锁存后的信号后加下划线和字母r与锁存前的信号区别。如CpuRamRd信号经锁存后应命名为CpuRamRd_r。 低电平有效的信号经过锁存器锁存后其命名应在_n后加r。如CpuRamRd_n信号经锁存后应命名为CpuRamRd_nr 多级锁存的信号可多加r以标明。如CpuRamRd信号经两级触发器锁存后应命名为CpuRamRd_rr。 4. 模块的命名。 在系统设计阶段应该为每个模块进行命名。命名的方法是将模块英文名称的各个单词首字母组合起来形成3到5个字符的缩写。若模块的英文名只有一个单词可取该单词的前3个字母。各模块的命名以3个字母为宜。例如 Arithmatic Logical Unit模块命名为ALU。 Data Memory Interface模块命名为DMI。 Decoder模块命名为DEC。
模块之间的接口信号的命名。 所有变量命名分为两个部分第一部分表明数据方向其中数据发出方在前数据接收方在后第二部分为数据名称。 两部分之间用下划线隔离开。 第一部分全部大写第二部分所有具有明确意义的英文名全部拼写或缩写的第一个字母大写其余部分小写。 举例CPUMMU_WrReq下划线左边是第一部分代表数据方向是从CPU模块发向存储器管理单元模块MMU。下划线右边Wr为Write的缩写Req是Request的缩写。两个缩写的第一个字母都大写便于理解。整个变量连起来的意思就是CPU发送给MMU的写请求信号。 模块上下层次间信号的命名也遵循本规定。 若某个信号从一个模块传递到多个模块其命名应视信号的主要路径而定。模块内部信号 模块内部的信号由几个单词连接而成缩写要求能基本表明本单词的含义 单词除常用的缩写方法外如Clock-Clk, Write-Wr, Read-Rd等一律取该单词的前几个字母 如Frequency-Freq, Variable-Var 等 每个缩写单词的第一个字母大写 若遇两个大写字母相邻中间添加一个下划线如DivN_Cntr 举例SdramWrEn_nFlashAddrLatchEn; 四. 编码格式规范。分节书写各节之间加1到多行空格。如每个always,initial语句都是一节。每节基本上完成一个特定的功能即用于描述某几个信号的产生。在每节之前有几行注释对该节代码加以描述至少列出本节中描述的信号的含义。行首不要使用空格来对齐而是用Tab键Tab键的宽度设为4个字符宽度。行尾不要有多余的空格。注释。 使用//进行的注释行以分号结束 使用/* /进行的注释/和*/各占用一行并且顶头 例 // Edge detector used to synchronize the input signal空格的使用 不同变量以及变量与符号、变量与括号之间都应当保留一个空格。 Verilog关键字与其它任何字符串之间都应当保留一个空格。如 Always (……) 使用大括号和小括号时前括号的后边和后括号的前边应当留有一个空格。 逻辑运算符、算术运算符、比较运算符等运算符的两侧各留一个空格与变量分隔开来单操作数运算符例外直接位于操作数前不使用空格。 使用//进行的注释在//后应当有一个空格注释行的末尾不要有多余的空格。 例 assign SramAddrBus { AddrBus[31:24], AddrBus[7:0] }; assign DivCntr[3:0] DivCntr[3:0] 4’b0001; assign Result ~Operand;同一个层次的所有语句左端对齐Initial、always等语句块的begin关键词跟在本行的末尾相应的end关键词与Initial、always对齐这样做的好处是避免因begin独占一行而造成行数太多 例 always ( posedge SysClk or negedge SysRst ) begin if( !SysRst ) DataOut 4’b0000; else if( LdEn ) begin DataOut DataIn; End else DataOut DataOut 4’b0001; end不同层次之间的语句使用Tab键进行缩进每加深一层缩进一个Tab在endmoduleendtaskendcase等标记一个代码块结束的关键词后面要加上一行注释说明这个代码块的名称在task名称前加tsk以示标记。在function的名称前加func以示标记。例如 task tskResetSystem …… endtask //of tskResetSystem 五小结 以上列出的代码编写规范无法覆盖代码编写的方方面面还有很多细节问题需要在实际编写过程中加以考虑。并且有些规定也不是绝对的需要灵活处理。并不是律条但是在一个项目组内部、一个项目的进程中应该有一套类似的代码编写规范来作为约束。 总的方向是努力写整洁、可读性好的代码
二reg型 在“always”块内被赋值的每一个信号都必须定义成reg型。 reg型数据的缺省初始值是不定值。 reg型只表示被定义的信号将用在“always”块内理解这一点很重要。并不是说reg型信号一定是寄存器或触发器的输出。虽然reg型信号常常是寄存器或触发器的输出但并不一定总是这样。
三memory型 memory型数据是通过扩展reg型数据的地址范围来生成的。其格式如下
reg [n-1:0] 存储器名[m-1:0] 或 reg [n-1:0] 存储器名[m:1]
在这里reg[n-1:0]定义了存储器中每一个存储单元的大小即该存储单元是一个n位的寄存器。存储器名后的[m-1:0]或[m:1]则定义了该存储器中有多少个这样的寄存器。 reg [7:0] mema[2550];
这个例子定义了一个名为mema的存储器该存储器有256个8位的存储器。该存储器的地址范围是0到255。注意对存储器进行地址索引的表达式必须是常数表达式。 尽管memory型数据和reg型数据的定义格式很相似但要注意其不同之处。如一个由n个1位寄存器构成的存储器组是不同于一个n位的寄存器的。见下例
reg [n-1:0] rega; //一个n位的寄存器 reg mema [n-1:0]; //一个由n个1位寄存器构成的存储器组
一个n位的寄存器可以在一条赋值语句里进行赋值而一个完整的存储器则不行。见下例
rega 0; //合法赋值语句 mema 0; //非法赋值语句
如果想对memory中的存储单元进行读写操作必须指定该单元在存储器中的地址。下面的写法是正确的。 mema[3]0; //给memory中的第3个存储单元赋值为0。 3.3.1.基本的算术运算符
在Verilog HDL语言中算术运算符又称为二进制运算符共有下面几种 1) (加法运算符,或正值运算符,如 regaregb3) 2) (减法运算符或负值运算符如 rega3,3) 3) ×(乘法运算符如rega*3) 4) / (除法运算符如5/3) 5) % (模运算符或称为求余运算符要求两侧均为整型数据。如73的值为1)
注意 在进行算术运算操作时如果某一个操作数有不确定的值x则整个结果也为不定值x。 1) ~ //取反 2) //按位与 3) | //按位或 4) ^ //按位异或 5) ^~ //按位同或(异或非) 在Verilog HDL语言中存在三种逻辑运算符: 1) 逻辑与 2) || 逻辑或 3) ! 逻辑非
关系运算符共有以下四种
a b a小于b a b a大于b a b a小于或等于b a b a大于或等于b
3.3.5.等式运算符 3.3.6.移位运算符 3.3.7.位拼接运算符(Concatation) 3.3.10.关键词 在Verilog HDL中所有的关键词是事先定义好的确认符,用来组织语言结构。关键词是用小写字母定义的,因此在编写原程序时要注意关键词的书写,以避免出错。下面是Verilog HDL中使用的关键词(请参阅附录Verilog语言参考手册)
always and assignbeginbufbufif0bufif1casecasexcasezcmosdeassigndefaultdefparamdisableedgeelseendendcaseendmoduleendfunctionendprimitive, endspecify, endtable endtask event for force forever fork functionhighz0 highz1, ifinitial, inout, inputintegerjoin,largemacromodulemediummodulenandnegedgenmosnornotnotif0notifl, or, output, parameter, pmos, posedge, primitive, pull0, pull1, pullup, pulldown, rcmos, reg, releses, repeat, mmos, rpmos, rtran, rtranif0,rtranif1,scalared,smallspecifyspecparamstrengthstrong0, strong1, supply0, supply1, table, task, time, tran, tranif0, tranif1, tri, tri0, tri1, triand, trior triregvectoredwaitwandweak0weak1while wirewor, xnor xor
(1).非阻塞(Non_Blocking)赋值方式( 如 b a; ) 1) 块结束后才完成赋值操作。 2) b的值并不是立刻就改变的。 3) 这是一种比较常用的赋值方法。特别在编写可综合模块时
(2).阻塞(Blocking)赋值方式( 如 b a; ) 1) 赋值语句执行完后,块才结束。 2) b的值在赋值语句执行完后立刻就改变的。 3) 可能会产生意想不到的结果。
一顺序块 顺序块有以下特点 1) 块内的语句是按顺序执行的即只有上面一条语句执行完后下面的语句才能执行。 2) 每条语句的延迟时间是相对于前一条语句的仿真时间而言的。 3) 直到最后一条语句执行完程序流程控制才跳出该语句块。 顺序块的格式如下 begin 语句1; 语句2; …… 语句n; end 其中 块名即该块的名字一个标识名。其作用后面再详细介绍。 块内声明语句可以是参数声明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句。
二 并行块 并行块有以下四个特点 1) 块内语句是同时执行的即程序流程控制一进入到该并行块块内语句则开始同时并行地执行。 2) 块内每条语句的延迟时间是相对于程序流程控制进入到块内时的仿真时间的。 3) 延迟时间是用来给赋值语句提供执行时序的。 4) 当按时间时序排序在最后的语句执行完后或一个disable语句执行时程序流程控制跳出该程序块。
并行块的格式如下 fork 语句1; 语句2; ……. 语句n; join 其中 • 块名即标识该块的一个名字相当于一个标识符。 • 块内说明语句可以是参数说明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句、time型变量声明语句、事件(event)说明语句。
在fork_join块内各条语句不必按顺序给出因此在并行块里各条语句在前还是在后是无关紧要的。见下例
三 块名 在VerilgHDL语言中可以给每个块取一个名字只需将名字加在关键词begin或fork后面即可。这样做的原因有以下几点。 1) 这样可以在块内定义局部变量即只在块内使用的变量。 2) 这样可以允许块被其它语句调用如被disable语句。 3) 在Verilog语言里所有的变量都是静态的即所有的变量都只有一个唯一的存储地址因此进入或跳出块并不影响存储在变量内的值。 基于以上原因块名就提供了一个在任何仿真时刻确认变量值的方法。
casez语句用来处理不考虑高阻值z的比较过程casex语句则将高阻值z和不定值都视为不必关心的情况。
如果用到if语句最好写上else项。如果用case语句最好写上default项。遵循上面两条原则就可以避免发生这种错误使设计者更加明确设计目标同时也增强了Verilog程序的可读性。 3.6.循环语句
在Verilog HDL中存在着四种类型的循环语句用来控制执行语句的执行次数。
1) forever 连续的执行语句。 2) repeat 连续执行一条语句 n 次。 3) while 执行一条语句直到某个条件不满足。如果一开始条件即不满足(为假) 则语句一次也不能被执行。 4) for通过以下三个步骤来决定语句的循环执行。 a) 先给控制循环次数的变量赋初值。 b) 判定控制循环的表达式的值如为假则跳出循环语句如为真则执行指定的语句后转到第三步。 c)
1:当为时序逻辑建模使用“非阻塞赋值”。 2:当为锁存器latch建模使用“非阻塞赋值”。 3:当用always块为组合逻辑建模使用“阻塞赋值” 4:当在同一个always块里面既为组合逻辑又为时序逻辑建模使用“非阻塞赋值”。 5:不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。 6:不要在两个或两个以上always块里面对同一个变量进行赋值。 7:使用$strobe以显示已被“非阻塞赋值”的值。 8:不要使用0延迟的赋值。
9:在VERILOG语法中 if…else if … else 语句是有优先级的一般说来第一个IF的优先级最高最后一个ELSE的优先级最低。如果描述一个编码器在XILINX的XST综合参数就有一个关于优先级编码器硬件原语句的选项Priority Encoder Extraction. 而CASE语句是”平行”的结构所有的CASE的条件和执行都没有“优先级”。而建立优先级结构会消耗大量的组合逻辑所以如果能够使用CASE语句的地方尽量使用CASE替换IF…ELSE结构。
10:XILINX的底层可编程硬件资源叫SLICE由2个FF和2个LUT组成。 FF触发器 LUT查找表
ALTERA的底层可编程硬件资源叫LE, 由1个FF和1个LUT组成。
11:慎用锁存器(latch)同步时序设计要尽量避免使用锁存器综合出非目的性latch的主要原因在于不完全的条件判断句。另外一种情况是设计中有组合逻辑的反馈环路(combinatorial feedback loops)。
12:状态机的一般设计原则Biary, gray-code 编码使用最少的触发器较多的组合逻辑。而one-hot编码反之。所以CPLD多使用GRAY-CODE, 而FPGA多使用ONE-HOT编码。另一方面小型设计使用GRAY-CODE和BINARY编码更有效而大型状态机使用ONE-HOT更有效。
13:业界主流CPLD产品是lattice的LC4000系列和ALTERA的MAX3000系列。
14:复位使初始状态可预测防止出现禁用状态。FPGA 和CPLD 的复位信号采用异步低电平有效信号连接到其全局复位输入端使用专用路径通道,复位信号必须连接到FPGA 和CPLD 的全局复位管脚。。
15:不要用时钟或复位信号作数据或使能信号也不能用数据信号作为时钟或复位信号否则HDL 综合时会出现时序验证问题。信号穿过时钟的两半个周期时要在前后分别取样防止出现半稳定状态。
16fpga设计中 不要使用门时钟don’t use gated clock。时钟信号必须连接到全局时钟管脚上。
17不要使用内部三态信号否则增加功耗。
18只使用同步设计不要使用延时单元。
19避免使用负延触发的双稳态多谐振荡器flip flop。
20:不要使用信号和变量的默认值或初始值用复位脉冲初始化
信号和变量。
21:不要在代码中使用buffer 类型的端口读取输出数据要使用out 类型再增加另外变量或信号以获取输出值。
这是因为buffer 类型的端口不能连接到其他类型的端口上因此buffer 类型就会在整个设计的端口中传播下去。
22:对变量要先读后写如果先写后读就会产生长的组合逻辑和锁存器或寄存器。这是因为变量值是立即获取的。
23:在组合逻辑进程中其敏感向量标中要包含所有要读取得信号这是为了防止出现不必要的锁存器。
近期在stephen Brown的一本书数字逻辑基础与verilog设计一书中看到关于触发器电路的时序分析。以前一直没有搞明白这个问题现在觉得豁然开朗。怕忘记了特地摘抄与此与edacn网友分享。 触发器电路的时序分析 图7-84给出了一个使用D触发器的简单电路。我们想要计算该电路能正常工作的最大的时钟频率Fmax并且想确定该电路的保持时间是否不够长。在技术文献中这种类型的电路分析通常叫做时序分析。假设该触发器的时序参数为tsu0.6nsth0.4ns0.8nstcQ1.0ns。给tcq参数规定一个范围是因为延迟参数分布在一定范围内这样处理是现成集成电路芯片常用的方法。为了计算最小的时钟信号周期Tmin1/Fmax我们必须考虑在触发器中从开始到结束的所有路径。在这个简单的电路中只有一条这样的路径这条路径开始于数据被时钟信号的正跳变沿加载进入触发器经过tcQ的延迟后传播到Q的输出端再传播通过非门同时必须满足D输入端的建立时间要求。因此 TmintcQtNOTtsu 由于我们关注的只是计算出最长的延迟时间所以应该用tcQ的最大值。为了计算出tNOT我们将假设通过任何逻辑门的延迟都可以用10.1k进行计算其中k是该门的输入信号的个数。对非门而言k1因此得到如下Tmin和Fmax的值Tmin1.01.10.62.7ns Fmax1/2.7ns370.37MHz 当然有必要检查电路中的保持时间是否违反规定。在这种场合我们必须核查从时钟信号的正跳变沿到D输入值改变的最短延迟。该延迟由tcQtNOT0.81.11.9ns给定。因为1.9ns0.4ns所以保持时间够长没有违反规定。再举一个触发器电路时序分析的例子请考虑图7-85所示的计数器电路。假设所用的触发器的时序参数与图7-84中用过的触发器相同请计算该电路能正常运行的最高频率。再次假设通过逻辑门的传播延迟可以用10.1k来计算。 在这个电路中存在着四个触发器从开始到结束的许多路径。最长的路径从触发器Q0起到触发器Q3结束。在某个电路中最长的路径成为关键路径。关键路径的延迟包括触发器Q0的时钟信号到Q的延迟、通过三个与门的传播延迟和一个异或门的延迟。我们还必须考虑触发器Q3的建立时间。因此得到 TmintcQ3(tAND)tXORtsu 用tcQ的最大值得到 Tmin1.03(1.2)1.20.66.4ns Fmax1/6.4ns156.25MHz 该电路的最短路径是从每个触发器通过异或门反馈到该触发器本身的输入端。沿每个这样路径的最小延迟为tcQtXOR0.81.22.0ns。因为2.0nsth0.4ns因此保持时间足够长没有违反规定。 在上面的分析中假设时钟信号同时到达所有四个触发器。我们现在将重复这个分析假设时钟信号同时到达触发器Q0,Q1,Q2但到达触发器Q3有一些延迟。始终到达不同的触发器之间的时间差称为时钟偏差(clock skew)记作tskew时钟偏差可以由许多原因引起。 在图7-85中电路的关键路径是从触发器Q0起到触发器Q3。然而Q3的时钟偏差使得这个延迟减少因为时钟偏差在数据被加载进该触发器前提供了附加的时间。如果考虑增加1.5ns的时钟偏差则从触发器Q0到触发器Q3的路径延迟由tcQ3(tAND)tXORtsu-tskew6.4-1.5ns4.9ns给定。该电路现在还存在一个不同的关键路径该路径从触发器Q0起到触发器Q2结束。这条路径的延迟为 TmintcQ2(tAND)tXORtsu1.02(1.2)1.20.6ns5.2ns Fmax1/5.2ns192.31MHz 在这种场合时钟偏差导致电路的最高时钟频率提高。但是如果时钟偏差是负的即触发器Q3的时钟到达时间比其他触发器更早一些则会造成该电路的最高时钟频率Fmax降低。 因为数据加载到触发器Q3被时钟偏差延迟了所以对所有起始于Q0Q1Q2而以Q3为结束点的路径都会产生使触发器Q3的保持时间需要增加到thtskew的影响。在该电路中这种最短的路径是从触发器Q2到Q3的路径其延迟时间为TcQtANDtXOR0.81.21.23.2ns。因为3.2nsthtskew1.9ns所以保持时间足够长没有违反规定。 如果对时钟偏差值tskew3.2-th2.8ns重复以上保持时间的分析则会出现保持时间不够的情况。当tskew2.8ns时该电路将不可能在任何频率下可靠地运行。由于时钟偏差的存在会引起电路时序问题所以好的电路设计方法必须保证时钟信号到达所有触发器的偏差尽可能小。 最后是我的总结确定最小周期是找关键路径即最长路径。确定Th是否违例是找最短路径。最短路径要大于Th。如果有Tskew的情况则要大于ThTskew有skew的寄存器为最短路径的终点的时候 还有就是我对有Tskew的情况的时候为什么防止违例要最短路径ThTskew。因为Q0Q1和Q2时钟比Q3早以他们为起点的路径已经开始走了一段时间后Q3的时钟才到才开始打入数据。所以保持时间上要加上这段skew
ISE 约束文件的基本操作 1约束文件的概念
FPGA设计中的约束文件有3类用户设计文件.UCF文件、网表约束文件.NCF文件以及物理约束文件.PCF文件可以完成时序约束、管脚约束以及区域约束。3类约束文件的关系为用户在设计输入阶段编写UCF文件然后UCF文件和设计综合后生成NCF文件最后再经过实现后生成PCF 文件。本节主要介绍UCF文件的使用方法。
UCF文件是ASC 2码文件描述了逻辑设计的约束可以用文本编辑器和Xilinx约束文件编辑器进行编辑。NCF约束文件的语法和UCF文件相同二者的区别在于 UCF文件由用户输入NCF文件由综合工具自动生成当二者发生冲突时以UCF文件为准这是因为UCF的优先级最高。PCF文件可以分为两个部分一部分是映射产生的物理约束另一部分是用户输入的约束同样用户约束输入的优先级最高。一般情况下用户约束都应在UCF文件中完成不建议直接修改 NCF文件和PCF文件。
2创建约束文件
约束文件的后缀是.ucf所以一般也被称为UCF文件。创建约束文件有两种方法一种是通过新建方式另一种则是利用过程管理器来完成。
第一种方法新建一个源文件在代码类型中选取“Implementation Constrains File”在“File Name”中输入“one2two_ucf”。单击“Next”按键进入模块选择对话框选择模块“one2two”然后单击“Next”进入下一页再单击“Finish”按键完成约束文件的创建。
第二种方法在工程管理区中将“Source for”设置为“Synthesis/Implementation”。“Constrains Editor”是一个专用的约束文件编辑器双击过程管理区中“User Constrains”下的“Create Timing Constrains”就可以打开“Constrains Editor”其界面如图所示
图 启动Constrains Editor引脚约束编辑
在“Ports”选项卡中可以看到所有的端口都已经罗列出来了如果要修改端口和FPGA管脚的对应关系只需要在每个端口的“Location”列中填入管脚的编号即可。例如在UCF文件中描述管脚分配的语法为 NET “端口名称” LOC 引脚编号;需要注意的是UCF文件是大小敏感的端口名称必须和源代码中的名字一致且端口名字不能和关键字一样。但是关键字NET是不区分大小写的。
3编辑约束文件
在工程管理区中将“Source for”设置为“Synthesis/Implementation”然后双击过程管理区中“User Constrains”下的“Edit Constraints (Text)”就可以打开约束文件编辑器如下图所示就会新建当前工程的约束文件。
图 用户约束管理窗口
UCF文件的语法说明 1语法 UCF文件的语法为 {NET|INST|PIN} “signal_name” Attribute; 其中“signal_name”是指所约束对象的名字包含了对象所在层次的描述“Attribute”为约束的具体描述语句必须以分号“”结束。可以用“#”或“/* */”添加注释。需要注意的是UCF文件是大小写敏感的信号名必须和设计中保持大小写一致但约束的关键字可以是大写、小写甚至大小写混合。例如 NET “CLK” LOC P30; “CLK”就是所约束信号名LOC P30是约束具体的含义将CLK信号分配到FPGA的P30管脚上。
对于所有的约束文件使用与约束关键字或设计环境保留字相同的信号名会产生错误信息除非将其用” “括起来因此在输入约束文件时最好用” “将所有的信号名括起来。
2通配符 在UCF文件中通配符指的是“”和“?”。“”可以代表任何字符串以及空“?”则代表一个字符。在编辑约束文件时使用通配符可以快速选择一组信号当然这些信号都要包含部分共有的字符串。例如 NET “*CLK?” FAST; 将包含“CLK”字符并以一个字符结尾的所有信号并提高了其速率。 在位置约束中可以在行号和列号中使用通配符。例如 INST “/CLK_logic/*” LOC CLB_r*c7; 把CLK_logic层次中所有的实例放在第7列的CLB中。
3定义设计层次 在UCF文件中通过通配符*可以指定信号的设计层次。其语法规则为 * 遍历所有层次 Level1/* 遍历level1及以下层次中的模块 Level1/*/ 遍历level1种的模块但不遍历更低层的模块
例4-5 根据图4-75所示的结构使用通配符遍历表4-3所要求的各个模块。
图 层次模块示意图 表 要求遍历的符号列表
管脚和区域约束语法
LOC约束是FPGA设计中最基本的布局约束和综合约束能够定义基本设计单元在FPGA芯片中的位置可实现绝对定位、范围定位以及区域定位。此外 LOC还能将一组基本单元约束在特定区域之中。LOC语句既可以书写在约束文件中也可以直接添加到设计文件中。换句话说ISE中的FPGA底层工具编辑器FPGA Editor、布局规划器Floorplanner和引脚和区域约束编辑器的主要功能都可以通过LOC语句完成。 • LOC语句语法 INST “instance_name ” LOC location;
其中“location”可以是FPGA芯片中任一或多个合法位置。如果为多个定位需要用逗号“,”隔开如下所示 LOC location1,location2,…,locationx; 目前还不支持将多个逻辑置于同一位置以及将多个逻辑至于多个位置上。需要说明的是多位置约束并不是将设计定位到所有的位置上而是在布局布线过程中布局器任意挑选其中的一个作为最终的布局位置。
范围定位的语法为 INST “instance_name” LOClocation:location [SOFT];
常用的LOC定位语句如表4-4所列。 表 常用的LOC定位语句
使用LOC完成端口定义时其语法如下 NET “Top_Module_PORT” LOC “Chip_Port”;
其中“Top_Module_PORT”为用户设计中顶层模块的信号端口“Chip_Port”为FPGA芯片的管脚名。
LOC语句中是存在优先级的当同时指定LOC端口和其端口连线时对其连线约束的优先级是最高的。例如在图4-76中LOC11的优先级高于LOC38。
图 LOC优先级示意图 2LOC属性说明
LOC语句通过加载不同的属性可以约束管脚位置、CLB、Slice、TBUF、块RAM、硬核乘法器、全局时钟、数字锁相环DLL以及DCM模块等资源基本涵盖了FPGA芯片中所有类型的资源。由此可见LOC语句功能十分强大表4-5列出了LOC的常用属性。 表 LOC语句常用属性列表
Verilog HDL代码描述对状态机综合的研究 2007-11-25 16:59 1 引言 Verilog HDL作为当今国际主流的HDL语言,在芯片的前端设计中有着广泛的应用。它的语法丰富,成功地应用于设计的各个阶段建模、仿真、验证和综合等。可综合是指综合工具能将Verilog HDL代码转换成标准的门级结构网表,因此代码的描述必须符合一定的规则。大部分数字系统都可以分为控制单元和数据单元两个部分,控制单元的主体是一个状态机,它接收外部信号以及数据单元产生的状态信息,产生控制信号,因而状态机性能的好坏对系统性能有很大的影响。 有许多可综合状态机的Verilog代码描述风格,不同代码描述风格经综合后得到电路的物理实现在速度和面积上有很大差别。优秀的代码描述应当易于修改、易于编写和理解,有助于仿真和调试,并能生成高效的综合结果。 2 有限状态机 有限状态机(Finite State Machine,FSM)在数字系统设计中应用十分广泛。根据状态机的输出是否与输入有关,可将状态机分为两大类摩尔(Moore)型状态机和米莉(Mealy)型状态机。Moore型状态机的输出仅与现态有关;Mealy型状态机的输出不仅与现态有关,而且和输入也有关。图1是有限状态机的一般结构图,它主要包括三个部分,其中组合逻辑部分包括状态译码器和输出译码器,状态译码器确定状态机的下一个状态,输出译码器确定状态机的输出,状态寄存器属于时序逻辑部分,用来存储状态机的内部状态。
图1 状态机的结构框图 2.1 好的状态机标准 好的状态机的标准很多,最重要的几个方面如下 第一,状态机要安全,是指FSM不会进入死循环,特别是不会进入非预知的状态,而且由于某些扰动进入非设计状态,也能很快的恢复到正常的状态循环中来。这里面有两层含义。其一要求该FSM的综合实现结果无_毛刺等异常扰动,其二要求FSM要完备,即使受到异常扰动进入非设计状态,也能很快恢复到正常状态。 第二,状态机的设计要满足设计的面积和速度的要求。 第三,状态机的设计要清晰易懂、易维护。 需要说明的是,以上各项标准,不是割裂的,它们有着直接紧密的内在联系。在芯片设计中,对综合结果评判的两个基本标准为面积和速度。“面积”是指设计所占用的逻辑资源数量;“速度”指设计在芯片上稳定运行所能够达到的最高频率。两者是对立统一的矛盾体,要求一个设计同时具备设计面积最小,运行频率最高,这是不现实的。科学的设计目标应该是在满足设计时序要求(包含对设计最高频率的要求)的前提下,占用最小的芯片面积,或者在所规定的面积下,使设计的时序余量更大,频率更高。另外,如果要求FSM安全,则很多时候需要使用“full case”的编码方式,即将状态转移变量的所有向量组合情况都在FSM 中有相应的处理,这经常势必意味着要多花更多的设计资源,有时也会影响FSM的频率所以,上述的标准要综合考虑,根据设计的要求进行权衡。
2.2 状态机描述方法 状态机描述时关键是要描述清楚几个状态机的要素,即如何进行状态转移,每个状态的输出是什么,状态转移的条件等。具体描述时方法各种各样,最常见的有三种描述方式 第一,整个状态机写到一个always模块里面,在该模块中既描述状态转移,又描述状态的输入和输出; 第二,用两个always模块来描述状态机,其中一个always模块采用同步时序描述状态转移;另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律以及输出; 第三,在两个always模块描述方法基础上,使用三个always模块,一个always模块采用同步时序描述状态转移,一个采用组合逻辑判断状态转移条件,描述状态转移规律,另一个always模块描述状态的输出(可以用组合电路输出,也可以时序电路输出)。 一般而言,推荐的FSM 描述方法是后两种。这是因为:FSM和其他设计一样,最好使用同步时序方式设计,以提高设计的稳定性,消除毛刺。状态机实现后,一般来说,状态转移部分是同步时序电路而状态的转移条件的判断是组合逻辑。 第二种描述方法同第一种描述方法相比,将同步时序和组合逻辑分别放到不同的always模块中实现,这样做的好处不仅仅是便于阅读、理解、维护,更重要的是利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。在第二种方式的描述中,描述当前状态的输出用组合逻辑实现,组合逻辑很容易产生毛刺,而且不利于约束,不利于综合器和布局布线器实现高性能的设计。第三种描述方式与第二种相比,关键在于根据状态转移规律,在上一状态根据输入条件判断出当前状态的输出,从而在不插入额外时钟节拍的前提下,实现了寄存器输出。
2.3 状态机的编码 二进制编码(Binary)、格雷码(Gray-code)编码使用最少的触发器,较多的组合逻辑,而独热码(One-hot)编码反之。独热码编码的最大优势在于状态比较时仅仅需要比较一个位,从而一定程度上简化了比较逻辑,减少了毛刺产生的概率。由于CPLD更多地提供组合逻辑资源,而FPGA更多地提供触发器资源,所以CPLD多使用二进制编码或格雷码,而FPGA多使用独热码编码。另一方面,对于小型设计使用二进制和格雷码编码更有效,而大型状态机使用独热码更高效。 3 实例说明 下面通过实例来说明Verilog HDL代码描述对状态机综合结果的影响。 设计一个序列检测器,用于检测串行的二进制序列,每当连续输入三个或三个以上的1时,序列检测器的输出为1,其它情况下输出为0。 假设初始的状态为s0,输入一个1的状态记为s1,连续输入二个1后的状态记为s2,输入三个或以上1的状态记为s3,不论现态是何种状态一旦输入0的话,就返回到初始状态。根据题意,可画出状态图如图2所示。
图2 状态图 根据状态图以及前面状态机的介绍,可以采用一个always模块来描述,状态编码采用二进制编码,程序如下 module fsm(clk,ina,out); input clk,ina; output out; reg out; parameter s0 3’bOO,s1 3’b01,s2 3’b10,s33’b11; reg[0:1]state; always (posedge clk) begin states0; out 0; case(state) s0:begin state(ina)?s1:s0;out0; end s1:begin state(ina)?s2:s0;out0; end s2:begin state(ina)?s3:s0;out0; end s3:begin state(ina)?s3:s0;out1; end endcase end endmodule 采用Synplify Pro工具在Altera EPF10K10系列器件上进行综合,其综合的结果如图3所示。
如果采用两个always来描述,程序的模块声明、端口定义和信号类型部分不变,只是改动逻辑功能描述部分,改动部分的程序如下alwys (posedge dk) state fsm next_state; always (state_fsm or ina) begin states0;out 0; case(state_fsm ) s0:begin next_state(ina)?s1:s0;out0; end s1:begin next state(ina)?s2:s0;out0 end s2:begin next_state(ina)?s3:s0;out0; end s3:begin next_state(ina)?s3:s0;out1; end endcase end endmodule 在相同的器件上其综合的结果如图4所示,比较图3与图4的综合结果,可以看出。两种综合结果都是采用了两个触发器来存储状态。其不同的地方是输出部分,采用一个always模块的输出结果是寄存器输出。采用两个always模块描述的是组合逻辑直接输出,这是因为代码中的输出赋值也放在了时钟的上升沿(always (posedge clk))。其综合的结果是寄存器,因此它比直接组合逻辑输出延迟一个时钟周期。
图4 如果采用一位hot编码,仅改动参数设置的两行程序。采用一个always模块描述,改动部分的程序如下 parameter s0 3’b0001,s1 3’b0010,s2 3’b0100,s33’b1000; reg[0:3] state;
图5 综合的结果如图5所示。将图5与图3相比,可以看出 图5中状态寄存器采用了4个触发器来存储状态,而图3采用了两个触发器来存储状态,这是由于它们的状态编码的不同而得到的不同的综合结果,采用二进制编码综合得到的触发器要比采用独热码综合得到的触发器少。它们的共同之处都是采用了寄存器来输出的。 3 结束语 有多种可综合状态机的Verilog HDL代码描述风格。其综合的结果是不同的。其中广泛采用的是两个或三个always模块描述。组合逻辑输出型状态机不适合应用在高速复杂系统设计中,在高速系统中应当采用寄存器输出型状态机。寄存器类型信号不会产生毛刺,并且输出不含组合逻辑。会减少组合逻辑门延时。容易满足高速系统设计要求。总之,状态机的设计是数字系统设计中的关键部分,设计时做到心中有电路。充分考虑其综合的结果,才能编写出高质量的综合代码。进而提高设计水平。
模块划分非常重要除了关系到是否最大程度上发挥项目成员的协同设计能力而且直接决定着设计的综合、实现时间。下面是一些模块划分的原则。 a.对每个同步设计的子模块的输出使用寄存器registering。也即用寄存器分割同步时序模块的原则。) ( F3 f D” j 使用寄存器的好处有综合工具在编译综合时会将所分割的子模块中的组合电路和同步时序电路整体考虑。而且这种模块结构符合时序约束的习惯便于使用时序约束熟悉进行约束。) q9 t/ |# a \7 p0 C b.将相关的逻辑或者可以复用的逻辑划分在同一模块内。 这样做的好处有一方面将相关的逻辑和可以复用的逻辑划分在同一模块可以最大程度的复用资源减少设计消耗的面积。同时也更利于综合工具优化一个具体功能操作在时序上的关键路径。其原因是综合工具只能同时考虑一部分逻辑而所同时优化的逻辑单元就是模块所以将相关功能划分在同一模块更有利于综合器的优化。; l/ w” k5 r9 G4 X4 x c.将不同优化目标的逻辑分开。 好的设计在规划阶段设计者就已经思考了设计的大概规模和关键路径并对设计的优化目标有一个整体上的把握。对于时序紧张的部分应该独立划分为一个模块其优化目标为“speed”这种划分方法便于设计者进行时序约束也便于综合和实现工具进行优化。比如时序优化的利器Amplify使用模块进行区域优化更方便一些。另一类矛盾集中在面积的设计也应该划分成独立的模块这类模块的优化目标是“Area”同样将他们规划到一起更有利于区域布局与约束。这种根据优化目标进行优化的方法的最大好处是对于某个模块综合器仅仅需要考虑一种优化目标和策略从而比较容易达到较好的优化效果。相反的如果同时考虑两种优化目标会使综合器陷入互相制约的困境。 d.将松约束的逻辑归到同一模块。 有些逻辑的时序非常宽松不需要较高的时序约束可以将这类逻辑归入同一模块如多周期路径“multi-cycle”等。将这些模块归类并指定松约束则可以让综合器尽量的节省面积资源。 e.将RAM/ROM/FIFO等逻辑独立划分成模块。 这样做的好处是便于综合器将这类资源类推为器件的硬件原语同时仿真时消耗的内存也会少些便于提高仿真速度。大多数仿真器对大面积的RAM都有独特的内存管理方式0 o4 B! p5 Q- D) O) Y7 M/ ] f.合适的模块规模。 规模大利于“Resource Sharing”。但是对综合器同时处理的逻辑量太大不利于多模块和增量编译模式。
关于约束时序分析的问题汇总 很多人发贴来信询问关于约束、时序分析的问题比如如何设置setuphold时间如何使用全局时钟和第二全局时钟长线资源如何进行分组约束如何约束某部分组合逻辑如何通过约束保证异步时钟域之间的数据交换可靠如何使用I/O逻辑单元内部的寄存器资源如何进行物理区域约束完成物理综合和物理实现等等。。。 为了解决大家的疑难我们将逐一讨论这些问题。
今天先讨论一下约束的作用 有些人不知道何时该添加约束何时不需要添加有些人认为低速设计不需要时序约束关于这些问题希望下面关于约束作用的论述能够有所帮助 附加约束的基本作用有3 (1)提高设计的工作频率 对很多数字电路设计来说提高工作频率非常重要因为高工作频率意味着高处理能力。通过附加约束可以控制逻辑的综合、映射、布局和布线以减小逻辑和布线延时从而提高工作频率。 (2)获得正确的时序分析报告 几乎所有的FPGA设计平台都包含静态时序分析工具利用这类工具可以获得映射或布局布线后的时序分析报告从而对设计的性能做出评估。静态时序分析工具以约束作为判断时序是否满足设计要求的标准因此要求设计者正确输入约束以便静态时序分析工具输出正确的时序分析报告。 (3)指定FPGA/CPLD引脚位置与电气标准 FPGA/CPLD的可编程特性使电路板设计加工和FPGA/CPLD设计可以同时进行而不必等FPGA/CPLD引脚位置完全确定从而节省了系统开发时间。这样电路板加工完成后设计者要根据电路板的走线对FPGA/CPLD加上引脚位置约束使FPGA/CPLD与电路板正确连接。另外通过约束还可以指定IO引脚所支持的接口标准和其他电气特性。为了满足日新月异的通信发展Xilinx新型FPGA/CPLD可以通过IO引脚约束设置支持诸如AGP、BLVDS、CTT、GTL、GTLP、HSTL、LDT、LVCMOS、LVDCI、LVDS、LVPECL、LVDSEXT、LVTTL、PCI、PCIX、SSTL、ULVDS等丰富的IO接口标准。
另外通过区域约束还能在FPGA上规划各个模块的实现区域通过物理布局布线约束完成模块化设计等。
贴2时序约束的概念和基本策略 时序约束主要包括周期约束FFS到FFS即触发器到触发器和偏移约束IPAD到FFS、FFS到OPAD以及静态路径约束IPAD到OPAD等3种。通过附加约束条件可以使综合布线工具调整映射和布局布线过程使设计达到时序要求。例如用OFFSET_IN_BEFORE约束可以告诉综合布线工具输入信号在时钟之前什么时候准备好综合布线工具就可以根据这个约束调整与IPAD相连的Logic Circuitry的综合实现过程使结果满足FFS的建立时间要求。 附加时序约束的一般策略是先附加全局约束然后对快速和慢速例外路径附加专门约束。附加全局约束时首先定义设计的所有时钟对各时钟域内的同步元件进行分组对分组附加周期约束然后对FPGA/CPLD输入输出PAD附加偏移约束、对全组合逻辑的PAD TO PAD路径附加约束。附加专门约束时首先约束分组之间的路径然后约束快、慢速例外路径和多周期路径以及其他特殊路径。
贴3周期PERIOD的含义 周期的含义是时序中最简单也是最重要的含义其它很多时序概念会因为软件商不同略有差异而周期的概念确是最通用的周期的概念是FPGA/ASIC时序定义的基础概念。后面要讲到的其它时序约束都是建立在周期约束的基础上的很多其它时序公式可以用周期公式推导。 周期约束是一个基本时序和综合约束它附加在时钟网线上时序分析工具根据PERIOD约束检查时钟域内所有同步元件的时序是否满足要求。PERIOD约束会自动处理寄存器时钟端的反相问题如果相邻同步元件时钟相位相反那么它们之间的延迟将被默认限制为PERIOD约束值的一半。 如下图所示时钟的最小周期为 TCLK TCKO TLOGIC TNET TSETUP TCLK_SKEW TCLK_SKEW TCD2 TCD1 其中TCKO为时钟输出时间TLOGIC为同步元件之间的组合逻辑延迟TNET为网线延迟TSETUP为同步元件的建立时间TCLK_SKEW为时钟信号延迟的差别。
这个帖子打算先澄清一些时序约束的基本概念然后将在综合工具Synplify Pro为例设计平台ISE5.x 和Quartus 2.2为例的具体约束方法和技巧然后将如何利用时序分析工具分析关键路径。如果没有意外应该30多个帖子吧。 仿真时序本来是Deve的老本行随时需要Deve加入一起把这个帖子办好。欢迎大家畅谈观点本站的版主冲锋啊嘻嘻。
贴4数据和时钟之间的约束OFFSET和SETUP、HOLD时间。 为了确保芯片数据采样可靠和下级芯片之间正确的交换数据需要约束外部时钟和数据输入输出引脚之间的时序关系或者内部时钟和外部输入/输出数据之间的关系这仅仅是从采用了不同的参照系罢了。约束的内容为告诉综合器、布线器输入数据到达的时刻或者输出数据稳定的时刻从而保证与下一级电路的时序关系。 这种时序约束在Xilinx中用Setup to ClockedgeClockedge to hold等表示。在Altera里常用tsu (Input Setup Times)、th (Input Hold Times)、tco (Clock to Out Delays)来表示。很多其它时序工具直接用setup和hold表示。其实他们所要描述的是同一个问题仅仅是时间节点的定义上略有不同。下面依次介绍。
贴5关于输入到达时间这一贴估计问题比较多看起来也比较累但是没有办法这些都是时序的基本概念啊。搞不清楚永远痛苦长痛不如短痛了呵呵。
Xilinx的”输入到达时间的计算”时序描述如图所示
定义的含义是输入数据在有效时钟沿之后的TARRIVAL时刻到达。则 TARRIVALTCKOTOUTPUTTLOGIC 公式1 根据”贴3“介绍的周期Period公式我们可以得到 TckoToutputTlogicTinputTsetup-Tclk_skewTclk; 公式2 将公式1代入公式2 TarrivalTinputTsetup-Tclk_skewTclk, 而Tclk_skew满足时序关系后为负所以 TARRIVAL TINPUTTSETUP