有没有遇到过Xilinx ISEauto place and routee 时间太久的问题

1、XST - &ERROR:Xst:902 - &file&.v, line xx: Unexpected event in always block sensitivity list.&
解决方法:Resolution 1
XST does not currently support logical operators in the sensitivity list. Because these logical operators are not evaluated during synthesis, there is no need to include them in the sensitivity list. Instead, include the operands in the sensitivity list and perform the logical operation in the procedural block.
For example:
Change the following:
always@(A && B)
if (A && B) ...
to the following:
always@(A or B)
if (A && B) ...
Resolution 2
XST also reports the above error if the sensitivity list is mixed between edge-sensitive and level-sensitive signals.
For example:
always @(posedge c or a or b or d) q &= a & b &
However, the above construct is illegal in Verilog. The sensitivity list must be either edge-sensitive or level-sensitive and not a combination of the two.
Resolution 3
XST also produces the above error message if a parameter is listed in the sensitivity list:
For example:
module test (in1, in2, out1);
parameter const = 5;
input [2:0] in1, in2;
output reg [3:0] out1;
always @(in1, in2, const)
out1 = in1 + in2 +
Because parameters never change values (or are static at compile time), you do not need to include them in the sensitivity list and you can safely remove them.
Resolution 4
2、ERROR:Xst:898 - counterlatch32.v line 12: The reset or set test condition for &index& is incompatible with the event declaration in the sensitivity list.
always @(posedge counter_increment or posedge reset)
&&&& begin
& if (!reset)
&& index &= index + 1;
&& index &= 5'b00000;
&always @(posedge enable)
&& out[index] &=
解决方法:问题出在,在always中,RESET是边沿触发的敏感信号,而在下面的描述中,RESET必须是高电平有效也就是说一切敏感事件必须在高电平时发生。可以修改如下:
always @(posedge counter_increment&or posedge reset)
&&&& begin
& if (reset)
&& index &= index + 1;
&& index &= 5'b00000;
&always @(posedge enable)
&& out[index] &=
按以上修改,就能解决问题,主要是因为触发器的问题。
3、ERROR:Xst:880 - &mon.v& line 72: Cannot mix blocking and non blocking assignments on signal &j&.
解决办法:
XST rejects Verilog designs if a given signal is assigned through both blocking and non-blocking assignments. For example:
always @(in1) begin
if (in2) out1 = in1;
else out1 &= in2;
If a variable is assigned in both a blocking and non-blocking assignment, the above error message is reported.
Restrictions also exist when blocking and non-blocking assignments are mixed on bits and slices. The following example will be rejected, even if there is no genuine mixing of blocking and non-blocking assignments:
if (in2) begin
out1[0] = 1'b0;
out1[1] &= in1;
else begin
out1[0] = in2;
out1[1] &= 1'b1;
Errors are checked at the signal level, not at the bit level. If there is more than a single blocking/non-blocking error, only the first one will be reported. In some cases, the line number for the error might be incorrect (as there might be multiple lines in which the signal has been assigned).
4、WARNING:Xst:647 - Input &OUiso_CPLD& is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
& 解决办法:This particular port has been declared in your HDL description, but does not drive or is not driven by any internal logic.
Unused input ports will remain in the design, but they will be completely unconnected. If the port is not intended to be used, this message can be safely ignored. To avoid this message, remove any loadless or sourceless elements from your HDL description.
Output ports will remain in the final netlist and will be driven by a logic 0. To avoid the message and to save the port resource, remove the unused output port from your HDL description.
5、Xst:2677 - Node &my_signal& of sequential type is unconnected in block &block&.
&&& 解决办法:That warning is caused by lot of things.....
One main reason is if your outputs are not connected..ie if you are not reading the module outputs the ise optimisation step removes all signal inside your block and fire a 2677 warning...
check the module outputs
6、ERROR:ProjectMgmt:387 - TOE: ITclInterp::ExecuteCmd gave Tcl result 'error copying &mppr_result.par''
出错描述:
Setting a project to use both SmartGuide and Multi-pass, Place & Route generates the following error when the &Implement Design& process is run.&
&ERROR:Par:368 - SmartGuide and MPPR are incompatible. Please remove the -smartguide switch or the -n switch&
ERROR (dpm_executeCommand): par failed&
ERROR:ProjectMgmt:387 - TOE: ITclInterp::ExecuteCmd gave Tcl result 'error copying &mppr_result.par&: no such file or directory'.&
Tcl_ErrnoId: ENOENT&
Tcl_ErrnoMsg: no such file or directory&
_cmd: ::Xilinx::Dpm::dpm_chTransformExecuteForPar dpm_parRun $piThisInterface&
errorInfo: error copying &mppr_result.par&: no such file or directory&
while executing&
&file copy -force $_MpprReport $_MpprRpt &&
(procedure &dpm_parSortMpprResults& line 12)&
invoked from within&
&dpm_parSortMpprResults $sProjectDir&&
(procedure &::Xilinx::Dpm::dpm_chTransformExecuteForPar& line 79)&
invoked from within&
&::Xilinx::Dpm::dpm_chTransformExecuteForPar dpm_parRun $piThisInterface&
解决办法:
The usage is incorrect. SmartGuide and MPPR do not work together. The error from PAR clearly indicates this problem. However, Project Navigator should have prevented this use model or issued a warning when you set the incompatible properties. Additionally, the Project Navigator Tcl script for PAR should handle the inconsistency better.&
7、XST - &WARNING:Xst:737 - Found n-bit latch for signal &name&&
问题描述以及产生原因:
Keywords: HDL Advisor
Urgency: Standard
General Description:
When a latch inference is discovered during HDL synthesis, XST reports the following HDL Advisor message:
&WARNING:Xst:737 - Found n-bit latch for signal &name&.&
The listing for &n& is the width of the latch.
If latch inference is intended, you can safely ignore this message. However, some inefficient coding styles can lead to accidental latch inference. You should analyze your code to see if this result is intended. The examples below illustrate how you can avoid latch inference.
解决办法:
1、Include all possible cases in the case statement.
always @ (SEL or DIN1 or DIN2)
case (SEL)
2'b00 : DOUT &= DIN1 + DIN2;
2'b01 : DOUT &= DIN1 - DIN2;
2'b10 : DOUT &= DIN1;
process (SEL, DIN1, DIN2)
case SEL is
when &00& =& DOUT &= DIN1 + DIN2;
when &01& =& DOUT &= DIN1 - DIN2;
when &10& =& DOUT &= DIN1;
These two examples create latches because there is no provision for the case when SEL = &11.& To eliminate the latches, add another entry to deal with this possibility.
2'b11 : DOUT &= DIN2;
when &11& =& DOUT &= DIN2;
Using the &DEFAULT& (Verilog) or &WHEN OTHERS& (VHDL) clause always works, but this can create extraneous logic. This is always the safest methodology, but might produce a larger and slower design since any unknown state has logic that is needed to bring it to a known state.
2、Assign to all the same outputs in each case.
always @ (SEL or DIN1 or DIN2)
case (SEL)
2'b00 : DOUT &= DIN1 + DIN2;
2'b01 : DOUT &= DIN1 - DIN2;
2'b10 : DOUT &= DIN1;
DOUT &= DIN2;
TEMP &= DIN1;
process (SEL, DIN1, DIN2)
case SEL is
when &00& =& DOUT &= DIN1 + DIN2;
when &01& =& DOUT &= DIN1 - DIN2;
when &10& =& DOUT &= DIN1;
when &11& =&
DOUT &= DIN2;
TEMP &= DIN1;
These examples infer latches because the &11& case assigns two outputs, while the others assign only one. Looking at this case from TEMP's point of view, only one of four possible cases are specified, so it is incomplete. You can avoid this situation by assigning values to the exact same list of outputs for each case.
3、Make sure any &if / else if& statements have a concluding &else& clause:
process (ge, din) is begin
if (ge = '1') then
dout_a &= '0'; -- This is a concluding &else& statement.
always @(ge
旗下网站:
与非门科技(北京)有限公司 All Rights Reserved.
京ICP证:070212号
北京市公安局备案编号: 京ICP备:号博客访问: 268353
博文数量: 86
博客积分: 2085
博客等级: 大尉
技术积分: 807
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: Verilog
在进行FPGA的设计时,经常会需要在综合、实现的阶段添加约束,以便能够控制综合、实现过程,使设计满足我们需要的运行速度、引脚位置等要求。通常的做法是设计编写约束文件并导入到综合实现工具,在进行FPGA/CPLD的综合、实现过程中指导逻辑的映射和布局布线。下面主要总结一下Xilinx FPGA时序约束设计和分析。
  一、周期约束
    周期约束是Xilinx FPGA 时序约束中最常见的约束方式。它附加在时钟网线上,时序分析工具会根据周期约束来检查时钟域内所有同步元件的时序是否满足需求。周期约束会自动的寄存器时钟端的反相。如果相邻的两个元件的时钟相位是相反的,那么它们之间的延迟将被默认的限制成周期约束的一半。
    在进行周期约束之前,必须对电路的时钟周期明了,这样才不会出现约束过松或者过紧的现象。一般情况下,设计电路所能达到的最高运行频率取决于同步元件本身的Setup Time 和 Hold Time,以及同步元件之间的逻辑和布线延迟。周期约束一般是使用下面的约束方法:
  1、period_item PERIOD=period {HIGH|LOW} [high_or low_item]
    其中,period_item可以是NET或TIMEGRP,分别代表时钟线名称net name或元件分组名称group-name。用NET表示PERIOD约束作用到名为“net name”的时钟网线所驱动的同步元件上,用TIMEGRP表示PERIOD约束作用到TIMEGRP所定义的分组(包括FFS、LATCH和 RAM等同步元件)上。period是目标时钟周期,单位可以是ps、ns、μS和ms等。HIGH|LOW指出时钟周期中的第1个脉冲是高电平还是低电平,high or low time为HIGH LOW指定的脉冲的持续时间,默认单位是ns。如果没有该参数,时钟占空比 是50%。例如, NET SYS_CLK PERIOD=10 ns HIGH 4ns
  2、NET“clock net name”TNM_NET=“timing group name”;
  TIMESPEC“TSidentifier”=PERIOD “TNM reference”period {HIGH | LOW} [high or low item]INPUT_JITTER
    很多时候为了能够定义比较复杂的派生关系的时钟周期,就要使用该方法。其中TIMESPEC在时序约束中作为一个标识符表示本约束为时序规范;TSidentifier包括字母TS和一个标识符identifier共同作为一个TS属性;TNM reference指定了时序约束是附加在哪一个组上,一般情况下加在TNM_NET定义的分组上。HIGH | LOW 指的是时钟的初始相位表明第一个时钟是上升沿还是下降沿;high or low item 表示的是时钟占空比,即就是high或者low的时间,默认为1:1, INPUT_JITTER 表示的是时钟的抖动时间,时钟会在这个时间范围内抖动,默认单元为ps。比如周期约束:
  NET "ex_clk200m_p" TNM_NET = TNM_clk200_p;
  TIMESPEC "TS_clk200_p" = PERIOD "TNM_clk200_p" 5.000 ns HIGH 50 %;
    建立一个TNM_clk200_p的时序分组,包括时钟网络ex_clk200m_p驱动的所有同步元件,这些同步元件都将受到时序规范TS_clk200_p的约束。同步元件到同步元件有5ns的时间要求。占空比为1:1.
  二、偏移约束
    偏移约束包括OFFSET_IN_BEFORE、OFFSET_IN_AFTER、OFFSET_OUT_BEFORE和OFFSET_OUT_AFTER等4种约束。属于基本的时序约束,它规定了外部时钟和数据输入输出引脚之间的时序关系,只能用于与引脚相连接。其基本的语法为:
  OFFSET = {IN | OUT} “offset_time” [units] {BEFORE | AFTER} “clk_name” [TIMEGRP “group_name”];
    其中,[IN | OUT] 说明约束是输入还是输出,”offset_time”为FPGA引脚数据变化与有效时钟之间的时间差,[BEFORE | AFTER]说明该时间差在有效时钟沿的前面还是后面,”clk_name”时钟名,[TIMEGRP “group_name”]定义约束的触发器组,缺省时约束clk_name驱动的所有触发器。
  1、OFFSET_IN_BEFORE、OFFSET_IN_AFTER约束
    OFFSET_IN_BEFORE和OFFSET_IN_AFTER都是输入偏移约束。OFFSET_IN_BEFORE说明了输入数据比有效时钟沿提前多长时间准备好。于是芯片内部与输入引脚相连的,组合逻辑的延迟不能大于这个时间,否则会发生数据采样错误。OFFSET_IN_AFTER是输入数据在有效时间沿之后多久到达芯片的输入引脚。
  OFFSET_IN_BEFORE对芯片内部的输入逻辑的约束,约束如下:
  NET data_in OFFSET = IN Time BEFORE CLK;
  2、OFFSET_OUT_AFTER、OFFSET_OUT_BEFORE约束
    这两个都是输出约束,OFFSET_OUT _AFTER规定了输出数据在有效沿之后多久稳定下来,芯片内部的输出延迟必须小于这个数值。OFFSET_OUT_BEFORE是指下一级芯片的输入数据应该在有效时钟沿之前多久准备好。从下一级的输入端的延迟可以计算出当前设计输出的数据必须在何时稳定下来。其基本的语法规则为:“NET data_out” OFFSET=OUT
  Time AFTER CLK“;
  三、专门约束
  附加约束的一般策略是首先附加整天约束,比如PERIOD、OFFSET等约束,然后对局部的电路进行约束。 专门约束包括以下几个:
  1、FROM_TO约束
  FROM_TO在两个组之间定义的约束,对二者之间的逻辑和布线延迟进行控制,这两个组可以是用户自定义的,也可以预定义的,可以使用TNM_NET、TNM和TIMEGRP定义组。其语法如下:
  TIMESPEC “TSname“ = FROM ”group1“ TO ”group2“
  其中group1和group2分别是路径的起点和终点,value为延迟时间,可以是具体的数值或者表达式。比如:TIMESPEC TS_CLK = PERIOD CLK 30 TIMESPEC T1_T3 = FROM T1 TO T3 60ns;
  2、MAXDELAY约束
  MAXDELAY约束定义了特定的网络线的最大延迟,其语法如:
  NET “net_name“ MAXDELAY = value units ; value 为延迟时间。
  3、MAXSKEW约束
  MAXSKEW是高级时序约束,通过MAXSKEW约束附加在某一网线上,可以约束该网线上的最大SKEW。MAXSKEW语法如下:
  NET “net_name“ MAXSKEW = allowable_
  比如,NET “Singal“ MAXSKEW = 3ns;
  四、分组约束
    在FPGA设计当中,往往包含大量的触发器、寄存器和RAM等元件,为了方便附加约束需要把他们分成不同的组,然后根据需要对某些组分别约束。
  1、TNM约束。
  使用TNM约束可以选出一个构成分组的元件,并且赋予一个名字,以便添加约束。如:{NET | INST | PIN} “object_name“ TNM= “identifier“;
  其中object_name为NET、INST或PIN的名称,identifier为分组名称。
  2、TNM_NEY约束
    TNM_NET约束只加在网线上,其作用与TNM约束加在网线时基本相同,即把该网线所在路径上的所有有效同步元件作为一组命名。不同之处在于TNM是加在引脚上的,而不是该网线所在路径上的同步元件,也就是说TNM约束不能穿过IBUF,用TNM_NET约束就不出现这种情况。
  NET “net_name“ TNM_NET = [predefined_group:] identifier;
  TNM_NET只能用在网线上,否则会出现警告,或者同时该约束被忽略。
  2、TIMEGRP约束
    可以通过TIMEGRP约束使已有的分组约束构成新的分组,已经有的预定义和用TNM/TIMEGRP定义。使用TIMEGRP约束可以使多个分组合并组成一个新分组。
  TIMEGRP “big_group1“ = ”small_group“ ”medium_group“
  将”small_group“ ”medium_group“合并一个新分组big_group1。
  五、简单的避免时序违规和补救的方法
    PERIOD 约束定义的是触发器等同步元件的时钟周期。可使用时序分析器来验证同步元件之间的所有路径是否满足设计的建立和保持时序要求。PERIOD 约束违例将以负的时序裕量显示在在时序报告,并说明到底是建立时间还是保持时间要求出现违例。应找出两个所分析的同步元件间一条较快路径,如果是多周期路径,应该采样多周期约束,或至少是某种方法来确保数据在合适时间内到达并保持足够长的时间,以便时钟脉冲边沿能够正确采样。若布局布线软件无法找到更快的路径,则可从 FPGA Editor 工具中手动进行布线。不过一般不推荐使用手工布局布线。首先应该试试重构电路来满足时序要求。比如用寄存器打一拍,有点逻辑复制的意思,这样可以增加了信号的延迟,但是却可以使电路正确的采样数据。
    若外部信号值在时钟脉冲边沿之前发生变化,则需使用 DCM 或 PLL 延迟时钟脉冲边沿,这样数据才能由新的延迟时钟正确采样。有一种替代方法,就是在输入/ 输出模块中使用 IDELAY 元件,将数据移到时钟有效的位置上。
  一般情况下,提高电路的运行速度可以尽量减少时序违规的问题。具体总结如下:
  1、通过设置Xilinx ISE软件在“Implement Design“点击右键,选择”属性“选择”“Optimization Strategy”栏中选择”speed“以及点击右键选择”Design Goals and Strategies“选择”Timing performance“。
  2、尽量使用Xilinx公司提高的专用资源,FPGA厂商都提高了一些专用的,比如进位链MUX、SRL等。
  3、重新分配关键路径
  (1) 关键路径在同一Module,这样综合时可以获得最佳的时序效果。
  (2) 对关键路径近LOC约束,如果发现关键路径相关的LUT距离太远,可以使用floorplanner手工布线。
  (3) 复制电路,减少关键路径的扇出。当一个信号网络所带的负载越多的时候,他的路径也会相应的增加,所以通过复制电路,可以有效的减少甚至消除时序违规的问题。
  (4) 在关键路径网络上插入buffer减少扇出,提高速度,消除时序违规。
  4、 进行特殊约束,通过设置Multi_Cycle_Path意义不大,因为它不会直接对关键路径产生影响,这样想法显然不对,因为它可以对非关键路径散开的作用,给关键路径节约了空间,间接的达到压缩关键路径延迟的问题,有点“曲线救国“的味道。
  5、 通过修改代码减少逻辑级数和分割组合逻辑。
   注: 一般情况下,使用比较多的都是周期约束,专门约束只是针对部分器件可能需要,一般高端FPGA是不需要这个的
阅读(6106) | 评论(0) | 转发(0) |
下一篇:没有了
相关热门文章
给主人留下些什么吧!~~
请登录后评论。********************************************************************************************************************
*作者:&Ian& &&时间:& 09:04& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & **标题:&菜鸟做设计必看!有关如何做设计的整体思路,以及能否综合的笔记& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &**所谓综合,就是把描述语言转化成能硬件实现的电路,学verilog的时候,没有人给我说要不要考虑能否综合的问题~~~& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & **看了5本书,居然没有一本书讲到能否综合,所以设计出来的程序完全不能用~~~ & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & **而且,书中都是讲语句的具体使用办法,例如always @(),但是什么时候用always,几个always之间、时序电路、逻辑电路、任务与函数什么时候用,却没有一本书能讲清楚。**这个笔记详细写了这些思路的问题,分享给新手看看,学习一种思路~~*点击此处下载&&(原文件名:verilog_经验(适合初学者).doc)& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &*
********************************************************************************************************************
先记下来:1、不使用初始化语句;2、不使用延时语句;3、不使用循环次数不确定的语句,如:forever,while等;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 C=A+B;C的最高位用来存放进位。6、关系运算符:关系运算符:&,&,&=,&=和算术操作符一样,可以进行有符号和无符号运算,取决于数据类型是reg,net还是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、共用表达式:如:C=A+B;& & D=G+(A+B);两者虽然有共用的A+B,但是有些综合工具不能识别.可以将第二句改为:D=G+C;这样只需两个加法器.3、转移代码:如循环语句中没有发生变化的语句移出循环.4、避免latch:两种方法:1、在每一个IF分支中对变量赋值。2、在每一个IF语句中都对变量赋初值。5:模块:综合生成的存储器如ROM或RAM不是一种好方法,只是成堆的寄存器,很费资源。最好用库自带的存储器模块。五、验证:1、敏感表:在always语句中,如果敏感表不含时钟,最好将所有的被读取的信号都放在敏感表中。2、异步复位:建议不要在异步时对变量读取,即异步复位时,对信号赋以常数值。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直接用形如"c=a+b"的表示二进制的加减运算;3综合时可控制性好:VHDL对信号不加区分地定义为"signal",而verilog区分为register类型的和wire类型的;但是也有人支持VHDL,认为verilog和VHDL的关系仿佛C和C++.C)典型的verilog模块讨论以下典型电路的verilog描述:*与非门;*加法器;&&//即全加器* D触发器;*计数器; //**分频的counter**时序机;*RAM;& &//用synopsys的*模块引用;*预编译;*与非门的verilog描述如下://verilog使用和C语言相同的注释方法module nd02(a1,a2,zn);//一个verilog模块总是以module开始,以endmodule& &结束,nd02是模块名,a1,a2,zn是模块的3个输入输出信号input a1,a2;&&//告诉编译器a1,a2对此模块而言是输入,并且数据类型是"bit"&&//告诉编译器zn对此模块而言是输出,数据类型也是"bit"nand&&(zn,a1,a2); //我理解nand是运算符,我们不必深究verilog中的正式术语是什么了吧,总之这种形式表示zn=~(a1 && a2);你一定已经想到类似的运算符还有"not","and","or","nor","xor"了吧;除了"not",括号里的信号数可以任意,例如or (z,f,g,h)表示z=f || g || h,并且延时是3个单位时间,#x表示延时x个单位时间;endmodule*加法器的verilog描述如下:module ad03d1(A,B,CI,S,CO) ;input [2:0] A,B;&&//表示A,B是输入信号,并且是3位矢量,上界是2,下界是0input CI;output [2:0] S;output CO;assign {CO,S}=A+B+CI;//一对"{"和"}"表示链接,即将CO和S合并成4位矢量endmodule*带异步清零端的D触发器的verilog描述如下:module dfctnb (d,cp,cdn,q,qn);input d,cp,output q,reg q,& & //关键字"reg"表示q和qn是"register"类型的信号;verilog中有两种类型的信号:"register"类型和"wire"类型.你可以简单地把register类型的信号想象为某个D触发器的输出,而wire类型的的信号是组合逻辑的输出.二者的最大区别在于:你可以对register类型的信号进行定时赋值(用wait语句在特定时刻的赋值,详见下面always语句),而对于wire类型的信号则不可.always wait (cdn==0) //表示每当cdn=0时,将要对D触发器清零,"always"和"wait"嵌套,"wait"和"@"是verilog的两个关键字,表示一旦有某事发生;则执行下面的语句块,"always"有点象C语言中的"if ... then...","wait"和"@"的区别:请参考本模块.wait表示本语句块的进程停止,直到"cdn=0"的条件出现才继续;我理解在verilog中,每个最外层语句块都是一个***的进程;"@"(请看下个always语句)也表示本语句块的进程停止,直到后面定义"posedge cp"(即出现cp的上升沿)的事件出现才继续;也许wait和@可以合二为一吧,但至少到目前verilog中wait表示"条件",@表示"事件";具体运用中,wait总是用于类似"wait(xxx=1)"之类的场合,@总是用于类似"@(xxx)"或"@(posedge/negedge xxx)"之类的场合整句话的意思是"每当cdn等于0时,则作以下事情"begin& &&&//begin...end结构的用法类似于pascal语言& && & q=0;& && && &qn=1;& && &&&wait (cdn==1);endalways @ (posedge cp)//"@(posedge cp)"中有两个关键字:"@ (x)"表示"每当事件x发生","posedge x"表示"x的上升沿,"negedge x"表示"x的下降沿",整句话的意思是"每当cp的上升沿,则作以下事情"& && &&&if (cdn)&&//如果cdn=1(意味着清零端无效)& && &&&begin& && && && && &&&q=d;& && && && && &&&qn=~q;//"~"表示反相& && &&&endendmodule*计数器的verilog描述如下:module count(in,set,cp,out) ;//此计数器,在cp的上升沿将输入赋给输出,在cp的上升沿使输出加一input [15:0]input set,output [15:0]reg [15:0]always @ (posedge set)out =always @(posedge cp)out = out+1;&&//verilog容许一个信号同时出现在等号两端,只要它是reg类型的endmodule*latch的描述如下:always @(clk or d)& & if (clk) q =*时序机的verilog描述如下:always @(posedge CLK)&&//D是下一个状态,Q是当前状态,e1,e2是输入,a,b是输出Q=D;always @(Q or othercase) begin //当Q变化或输入e1,e2变化时D要相应变化D = Q; //note1a = 0;b = 0;......case(Q)&&q1:begin& &q1& &if(e1)D=d1;& &if(e2)D=d2;& &else D=d3;& &a = 1; //note 2& &end&&q2:begin& &b = 1;& &......& &end&&default:begin& &a = 0;& &b = 0;& &......endend---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,分别有:输入端:输入地址输入数据上升沿有效的写信号/输出端:输出地址输出数据高电平有效的读信号&&inout [7:0]&&input [7:0] ain,&&input rd,&&output [7:0]&&reg [7:0] memory [0:255];& &//请注意这是存储阵列的描述方法,描述了一个共有256个字的存储阵列,每个字是8位&&assign dout = rd ? memory[aout] : 8' //"assign"关键字表示并行赋值语句的开始"?"运算符的作用和在C语言中一样"8'bz"是一个常量,表示一个字节的高阻态,其中8表示长度是8bit,"'"是固定分割符,"b"表示后面的数据是以比特形式给出的,"z"表示高阻;举例:4'ha表示长4bit的数"1010"。类似的还可举出5'b等等&&always @(posedge wr)memory[ain] =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语句中对一个信号赋值.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.在嵌入式存储器中使用BIST14.虚单元和一些修正电路是必需的15.一些简单的测试电路也是需要的,经常在一个芯片中有许多测试模块16.除非低功耗不要用门控时钟,强烈建议不要在design中使用gate clock17.不要依靠脚本来保证设计。但是在脚本中的一些好的约束能够起到更好的性能(例如前向加法器)18.如果时间充裕,通过时钟做一个多锁存器来取代用MUX19.不要用内部tri-state, ASIC需要总线保持器来处理内部tri-state,如IO cell。20.在top level中作pad insertion21.选择pad时要小心(如上拉能力,施密特触发器,5伏耐压等),选择合适的IO cell22.小心由时钟偏差引起的问题23.不要试着产生半周期信号24.如果有很多函数要修正,请一个一个地作,修正一个函数检查一个函数25.在一个计算等式中排列每个信号的位数是一个好习惯,即使综合工具能做26.不要使用HDL提供的除法器27.削减不必要的时钟。它会在设计和布局中引起很多麻烦,大多数FPGA有1-4个专门的时钟通道良好代码编写风格可以满足信、达、雅的要求。在满足功能和性能目标的前提下,增强代码的可读性、可移植性,首要的工作是在项目开发之前为整个设计团队建立一个命名约定和缩略语清单,以文档的形式记录下来,并要求每位设计人员在代码编写过程中都要严格遵守。良好代码编写风格的通则概括如下:&&(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& & e=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)& & begin& & e=d&a&b; /*d在敏感电平列表中,d变化时e立刻变化*/& & d=e |c;& & end2,条件的描述完备性如果if语句和case语句的条件描述不完备,也会造成不必要的锁存器。Example1:if (a==1'b1) q=1'b1;//如果a==1'b0,q=? q将保持原值不变,生成锁存器!Example2:if (a==1'b1) q=1'b1;else& && && &q=1'b0;//q有明确的值。不会生成锁存器!Example3:& &reg[1:0] a,q;& &....& &case (a)& && &2'b00 : q=2'b00;& && &2'b01 : q=2'b11;//如果a==2'b10或a==2'b11,q=? q将保持原值不变,锁存器!& &endcaseExample4:& &reg[1:0] a,q;& &....& &case (a)& && &2'b00 : q=2'b00;& && &2'b01 : q=2'b11;& && &default: q=2'b00;//q有明确的值。不会生成锁存器!& &endcase& &&&Verilog中端口的描述1,端口的位宽最好定义在I/O说明中,不要放在数据类型定义中;Example1:module test(addr,read,write,datain,dataout)input[7:0]&&input[15:0]input& && & read,output[7:0]&&//要这样定义端口的位宽!wire addr,read,write,reg&&Example2:module test(addr,read,write,datain,dataout)input&&datain,addr,read,wire[15:0]wire[7:0]&&wire& && & read,reg[7:0]& && &//不要这样定义端口的位宽!!2,端口的I/O与数据类型的关系:& & 端口的I/O& && && &&&端口的数据类型& && && && && && && &&&module内部& &&&module外部& && &input& && && && &&&wire& && && & wire或reg& && &output& && && &wire或reg& && && &&&wire& && &inout& && && && &wire& && && && &&&wire3,assign语句的左端变量必须是wire;直接用"="给变量赋值时左端变量必须是reg!Example:assign a=b; //a必须被定义为wire!!********begin& &a=b; //a必须被定义为reg!end&&VHDL中STD_LOGIC_VECTOR和INTEGER的区别例如A是INTEGER型,范围从0到255;B是STD_LOGIC_VECTOR,定义为8位。A累加到255时,再加1就一直保持255不变,不会自动反转到0,除非令其为0;而B累加到255时,再加1就会自动反转到0。所以在使用时要特别注意!以触发器为例说明描述的规范性1,无置位/清零的时序逻辑& & always @( posedge CLK)& && & begin& && & Q&=D;& && & end2,有异步置位/清零的时序逻辑&&异步置位/清零是与时钟无关的,当异步置位/清零信号到来时,触发器的输出立即&&被置为1或0,不需要等到时钟沿到来才置位/清零。所以,必须要把置位/清零信号&&列入always块的事件控制表达式。& & always @( posedge CLK or negedge RESET)& && & begin& && & if (!RESET)& && && & Q=0;& && & else& && && & Q&=D;& && & end3,有同步置位/清零的时序逻辑& &同步置位/清零是指只有在时钟的有效跳变时刻置位/清零,才能使触发器的输出分& &别转换为1或0。所以,不要把置位/清零信号列入always块的事件控制表达式。但是& &必须在always块中首先检查置位/清零信号的电平。& & always @( posedge CLK )& & begin& && & if (!RESET)& && && & Q=0;& && & else& && && & Q&=D;& && & end结构规范性在整个芯片设计项目中,行为设计和结构设计的编码是最重要的一个步骤。 它对逻辑综合和布线结果、时序测定、校验能力、测试能力甚至产品支持 都有重要的影响。考虑到仿真器和真实的逻辑电路之间的差异,为了有效的进行仿真测试:&&1,避免使用内部生成的时钟& & 内部生成的时钟称为门生时钟(gated clock)。如果外部输入时钟和门生时钟同时驱动,& & 则不可避免的两者的步调不一致,造成逻辑混乱。而且,门生时钟将会增加测试的难度& & 和时间。&&2,绝对避免使用内部生成的异步置位/清零信号& & 内部生成的置位/清零信号会引起测试问题。使某些输出信号被置位或清零,无法正常& & 测试。3,避免使用锁存器& & 锁存器可能引起测试问题。对于测试向量自动生成(ATPG),& & 为了使扫描进行,锁存器需要置为透明模式(transparent mode),& & 反过来,测试锁存器需要构造特定的向量,这可非同一般。&&4,时序过程要有明确的复位值& & 使触发器带有复位端,在制造测试、ATPG以及模拟初始化时,可以对整个电路进行& & 快速复位。&&5,避免模块内的三态/双向& & 内部三态信号在制造测试和逻辑综合过程中难于处理.近日读 J.Bhasker 的&verilog synthesis practical primer& , 受益匪浅,理清了不少基础电路知识 , 记下一些 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_input data_reg data_//data_inout的映象寄存器reg link_assign data_inout=link_data?data_reg:1&//link_data控制三态门//对于data_reg,可以通过组合逻辑或者时序逻辑根据data_in对其赋值.通过控制link_data的高低电平,从而设置data_inout是输出数据还是处于高阻态,如果处于高阻态,则此时当作输入端口使用.link_data可以通过相关电路来控制.2 编写测试模块时,对于inout类型的端口,需要定义成wire类型变量,而其它输入端口都定义成reg类型,这两者是有区别的.当上面例子中的data_inout用作输入时,需要赋值给data_inout,其余情况可以断开.此时可以用assign语句实现:assign data_inout=link?data_in_t:1&其中的link ,data_in_t是reg类型变量,在测试模块中赋值.另外,可以设置一个输出端口观察data_inout用作输出的情况:Wire data_Assign data_out_t=(!link)?data_inout:1&else,in RTLinout use in top module(PAD)dont use inout(tri) in sub module也就是说,在内部模块最好不要出现inout,如果确实需要,那么用两个port实现,到顶层的时候再用三态实现。理由是:在非顶层模块用双向口的话,该双向口必然有它的上层跟它相连。既然是双向口,则上层至少有一个输入口和一个输出口联到该双向口上,则发生两个内部输出单元连接到一起的情况出现,这样在综合时往往会出错。对双向口,我们可以将其理解为2个分量:一个输入分量,一个输出分量。另外还需要一个控制信号控制输出分量何时输出。此时,我们就可以很容易地对双向端口建模。例子:CODE:module dual_port (....inout_pin,....);inout inout_wire inout_wire input_of_wire output_of_wire out_assign input_of_inout = inout_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_........................assign data_inout=(! link)?datareg:1'endmodule方法一:使用相反控制信号inout口,等于两个模块之间用inout双向口互连。这种方法要注意assign 语句只能放在initial和always块内。module test();wire data_reg data_initial begin..........endassign data_inout=link?data_reg:1'endmodule方法二:使用force和release语句,但这种方法不能准确反映双向端口的信号变化,但这种方法可以反在块内。module test();wire data_reg data_#& && &&&//延时force data_inout=1'& && && &&&//强制作为输入端口...............#release data_& && && & //释放输入端口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_inout[7:0] inner_inout[7:0] outer_assign outer_port=(out_en==1)?inner_port:8'assign inner_port=(out_en==0)?outer_port:8'endmodule用VHDL语言描述双向端口如下:use IEEE.STD_LOGIC_1164.ALL;entity bidirection_io isport ( inner_port : inout std_logic_vector(7 downto 0);out_en : in std_outer_port : inout std_logic_vector(7 downto 0) );end bidirection_architecture behavioral of bidirection_io isbeginouter_port&=inner_port when out_en='1' else (OTHERS=&'Z');inner_port&=outer_port when out_en='0' else (OTHERS=&'Z');仿真时需要验证双向端口能正确输出数据,以及正确读入数据,因此需要驱动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的工作过程为1)out_en=1时,双向端口处于输出状态,testbench给inner_port_tb_reg信号赋值,然后读取outer_port_tb_wire的值,如果两者一致,双向端口工作正常。2)out_en=0时,双向端口处于输如状态,testbench给outer_port_tb_reg信号赋值,然后读取inner_port_tb_wire的值,如果两者一致,双向端口工作正常。用Verilog代码编写的testbench如下,其中使用了自动结果比较,随机化激励产生等技术。`timescale 1ns/10psmodule tb();reg[7:0] inner_port_tb_wire[7:0] inner_port_tb_reg[7:0] outer_port_tb_wire[7:0] outer_port_tb_reg out_en_initialbeginout_en_tb=0;inner_port_tb_reg=0;outer_port_tb_reg=0;i=0;repeat(20)begin#50i=$out_en_tb=i[0]; //randomize out_en_tbinner_port_tb_reg=$ //randomize dataouter_port_tb_reg=$endend//**** drive the ports connecting to bidirction_ioassign inner_port_tb_wire=(out_en_tb==1)?inner_port_tb_reg:8'assign outer_port_tb_wire=(out_en_tb==0)?outer_port_tb_reg:8'//instatiate the bidirction_io modulebidirection_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_wire===inner_port_tb_wire)begin$display("\n **** time=%t ****",$time);$display("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);endelsebegin$display("\n **** time=%t ****",$time);$display("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);endendendmodule今天重新回顾了一下阻塞赋值和非阻塞赋值的概念,感觉又有所收获。&一、特点:& & 阻塞赋值:1、RHS的表达式计算和LHS的赋值更新,这两个动作之间不能插入其他动作,即所谓计算完毕,立即更新。& && && && & 2、所谓阻塞就是指在一个&begin...end&块中的多个阻塞赋值语句内,只有上一句完全执行完毕后,才会执行下一语句,否则阻塞程序的执行。& & 非阻塞赋值:RHS的表达式计算和LHS的赋值更新分两个节拍执行,首先,应该是RHS的表达式计算,得到新值后并不立即赋值,而是放在事件队列中等待,直到& && && && &&&当前仿真时刻的后期才执行(原因下文会提到)。二、Verilog的分层事件队列:& & 在Verilog中,事件队列可以划分为5个不同的区域,不同的事件根据规定放在不同的区域内,按照优先级的高低决定执行的先后顺序,下表就列出了部分Verilog分层事件队列。其中,活跃事件的优先级最高(最先执行),而监控事件的优先级最低,而且在活跃事件中的各事件的执行顺序是随机的(注:为方便起见,在一般的仿真器中,对同一区域的不同事件是按照调度的先后关系执行的)。& &当前仿真时间事件活跃事件& & & &&&阻塞赋值,非阻塞赋值的RHS计算&&非活跃事件显式0延时的阻塞赋值&&非阻塞赋值更新事件由非阻塞语句产生的一个非阻塞赋值更新事件,并被调入当前仿真时刻。监控事件$monitor和$strobe等系统任务将来仿真时间事件& & & &&&& & & &&&被调度到将来仿真时间的事件三、结论:& & 由上表就可以知道,阻塞赋值属于活跃事件,会立刻执行,这就是阻塞赋值&计算完毕,立刻更新&的原因。此外,由于在分层事件队列中,只有将活跃事件中排在前面的事件调出,并执行完毕后,才能够执行下面的事件,这就可以解释阻塞赋值的第二个特点。& & 同样是由上表知,非阻塞赋值的RHS计算属于活跃事件,而非阻塞赋值更新事件排在非活跃事件之后,因此只有仿真队列中所有的活跃事件和非活跃事件都执行完毕后,才轮到非阻塞赋值更新事件,这就是非阻塞赋值必须分两拍完成的原因。以上就是我今天的读书笔记,写得仓促,如有不对,敬请指出 。一. 强调Verilog代码编写风格的必要性。强调Verilog代码编写规范,经常是一个不太受欢迎的话题,但却是非常有必要的。每个代码编写者都有自己的编写习惯,而且都喜欢按照自己的习惯去编写代码。与自己编写风格相近的代码,阅读起来容易接受和理解。相反和自己编写风格差别较大的代码,阅读和接受起来就困难一些。曾有编程大师总结说,一个优秀的程序员,能维护的代码长度大约在1万行数量级。代码的整洁程度,很大程度上影响着代码的维护难度。遵循代码编写规范书写的代码,很容易阅读、理解、维护、修改、跟踪调试、整理文档。相反代码编写风格随意的代码,通常晦涩、凌乱,会给开发者本人的调试、修改工作带来困难,也会给合作者带来很大麻烦。(实际上英文Coding Style有另一层涵义,更偏重的是,某一个电路,用那一种形式的语言描述,才能将电路描述得更准确,综合以后产生的电路更合理。本文更偏重的是,编写Verilog代码时的书写习惯。)二. 强调编写规范的宗旨。缩小篇幅提高整洁度便于跟踪、分析、调试增强可读性,帮助阅读者理解便于整理文档便于交流合作三. 变量及信号命名规范。1. 系统级信号的命名。系统级信号指复位信号,置位信号,时钟信号等需要输送到各个模块的全局信号;系统信号以字符串Sys开头。2. 低电平有效的信号后一律加下划线和字母n。如:SysRst_n;FifoFull_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。5. 模块之间的接口信号的命名。所有变量命名分为两个部分,第一部分表明数据方向,其中数据发出方在前,数据接收方在后,第二部分为数据名称。两部分之间用下划线隔离开。第一部分全部大写,第二部分所有具有明确意义的英文名全部拼写或缩写的第一个字母大写,其余部分小写。举例:CPUMMU_WrReq,下划线左边是第一部分,代表数据方向是从CPU模块发向存储器管理单元模块(MMU)。下划线右边Wr为Write的缩写,Req是Request的缩写。两个缩写的第一个字母都大写,便于理解。整个变量连起来的意思就是CPU发送给MMU的写请求信号。模块上下层次间信号的命名也遵循本规定。若某个信号从一个模块传递到多个模块,其命名应视信号的主要路径而定。6. 模块内部信号:模块内部的信号由几个单词连接而成,缩写要求能基本表明本单词的含义;单词除常用的缩写方法外(如:Clock-&Clk, Write-&Wr, Read-&Rd等),一律取该单词的前几个字母( 如:Frequency-&Freq, Variable-&Var 等);每个缩写单词的第一个字母大写;若遇两个大写字母相邻,中间添加一个下划线(如DivN_Cntr);举例:SdramWrEn_n;FlashAddrLatchEn;四. 编码格式规范。1. 分节书写,各节之间加1到多行空格。如每个always,initial语句都是一节。每节基本上完成一个特定的功能,即用于描述某几个信号的产生。在每节之前有几行注释对该节代码加以描述,至少列出本节中描述的信号的含义。2. 行首不要使用空格来对齐,而是用Tab键,Tab键的宽度设为4个字符宽度。行尾不要有多余的空格。3. 注释。使用//进行的注释行以分号结束;使用/* */进行的注释,/*和*/各占用一行,并且顶头;例:// Edge detector used to synchronize the input signal;4. 空格的使用:不同变量,以及变量与符号、变量与括号之间都应当保留一个空格。Verilog关键字与其它任何字符串之间都应当保留一个空格。如:Always @ (&&)使用大括号和小括号时,前括号的后边和后括号的前边应当留有一个空格。逻辑运算符、算术运算符、比较运算符等运算符的两侧各留一个空格,与变量分隔开来;单操作数运算符例外,直接位于操作数前,不使用空格。使用//进行的注释,在//后应当有一个空格;注释行的末尾不要有多余的空格。例:assign SramAddrBus = { AddrBus[31:24], AddrBus[7:0] };assign DivCntr[3:0] = DivCntr[3:0] + 4&b0001;assign Result = ~O5. 同一个层次的所有语句左端对齐;Initial、always等语句块的begin关键词跟在本行的末尾,相应的end关键词与Initial、always对齐;这样做的好处是避免因begin独占一行而造成行数太多;例:always @ ( posedge SysClk or negedge SysRst ) beginif( !SysRst ) DataOut &= 4'b0000;else if( LdEn ) beginDataOut &= DataIn;Endelse DataOut &= DataOut + 4'b0001;end6. 不同层次之间的语句使用Tab键进行缩进,每加深一层缩进一个Tab;8. 在endmodule,endtask,endcase等标记一个代码块结束的关键词后面要加上一行注释说明这个代码块的名称;9. 在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[255:0];这个例子定义了一个名为mema的存储器,该存储器有256个8位的存储器。该存储器的地址范围是0到255。注意:对存储器进行地址索引的表达式必须是常数表达式。尽管memory型数据和reg型数据的定义格式很相似,但要注意其不同之处。如一个由n个1位寄存器构成的存储器组是不同于一个n位的寄存器的。见下例:reg [n-1:0]& & //一个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)& && &+ (加法运算符,或正值运算符,如 rega+regb,+3)2)& && &-(减法运算符,或负值运算符,如 rega-3,-3)3)& && &&(乘法运算符,如rega*3)4)& && &/ (除法运算符,如5/3)5)& && &% (模运算符,或称为求余运算符,要求%两侧均为整型数据。如7%3的值为1)注意:& & & & 在进行算术运算操作时,如果某一个操作数有不确定的值x,则整个结果也为不定值x。1)& && &~& && && & //取反2)& && &&& && && & //按位与3)& && &|& && && & //按位或4)& && &^& && && & //按位异或5)& && &^~& && && &//按位同或(异或非)在Verilog HDL语言中存在三种逻辑运算符:1)& && &&& 逻辑与2)& && &|| 逻辑或3)& && &! 逻辑非关系运算符共有以下四种:a & b& && & a小于ba & b& && & a大于ba &= b& && &a小于或等于ba &= b& && &a大于或等于b3.3.5.等式运算符3.3.6.移位运算符3.3.7.位拼接运算符(Concatation)3.3.10.关键词在Verilog HDL中,所有的关键词是事先定义好的确认符,用来组织语言结构。关键词是用小写字母定义的,因此在编写原程序时要注意关键词的书写,以避免出错。下面是Verilog HDL中使用的关键词(请参阅附录:Verilog语言参考手册):always, and, assign,begin,buf,bufif0,bufif1,case,casex,casez,cmos,deassign,default,defparam,disable,edge,else,end,endcase,endmodule,endfunction,endprimitive, endspecify, endtable, endtask, event, for, force, forever, fork, function,highz0, highz1, if,initial, inout, input,integer,join,large,macromodule,medium,module,nand,negedge,nmos,nor,not,notif0,notifl, or, output, parameter, pmos, posedge, primitive, pull0, pull1, pullup, pulldown, rcmos, reg, releses, repeat, mmos, rpmos, rtran, rtranif0,rtranif1,scalared,small,specify,specparam,strength,strong0, strong1, supply0, supply1, table, task, time, tran, tranif0, tranif1, tri, tri0, tri1, triand, trior, trireg,vectored,wait,wand,weak0,weak1,while, wire,wor, xnor, xor(1).非阻塞(Non_Blocking)赋值方式( 如 b &= )1)& && &块结束后才完成赋值操作。2)& && &b的值并不是立刻就改变的。3)& && &这是一种比较常用的赋值方法。(特别在编写可综合模块时)(2).阻塞(Blocking)赋值方式( 如 b = )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 综合时会出现时序验证问题。信号穿过时钟的两半个周期时,要在前后分别取样;防止出现半稳定状态。#16:fpga设计中 不要使用门时钟(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,并且想确定该电路的保持时间是否不够长。在技术文献中,这种类型的电路分析通常叫做时序分析。假设该触发器的时序参数为:tsu=0.6ns,th=0.4ns,0.8ns&=tcQ&=1.0ns。给tcq参数规定一个范围是因为延迟参数分布在一定范围内,这样处理是现成集成电路芯片常用的方法。为了计算最小的时钟信号周期Tmin=1/Fmax,我们必须考虑在触发器中从开始到结束的所有路径。在这个简单的电路中,只有一条这样的路径,这条路径开始于数据被时钟信号的正跳变沿加载进入触发器,经过tcQ的延迟后传播到Q的输出端,再传播通过非门,同时必须满足D输入端的建立时间要求。因此:Tmin=tcQ+tNOT+tsu由于我们关注的只是计算出最长的延迟时间,所以应该用tcQ的最大值。为了计算出tNOT,我们将假设通过任何逻辑门的延迟都可以用1+0.1k进行计算,其中k是该门的输入信号的个数。对非门而言,k=1,因此得到如下Tmin和Fmax的值:Tmin=1.0+1.1+0.6=2.7nsFmax=1/2.7ns=370.37MHz当然,有必要检查电路中的保持时间是否违反规定。在这种场合,我们必须核查从时钟信号的正跳变沿到D输入值改变的最短延迟。该延迟由tcQ+tNOT=0.8+1.1=1.9ns给定。因为1.9ns&0.4ns,所以保持时间够长,没有违反规定。再举一个触发器电路时序分析的例子,请考虑图7-85所示的计数器电路。假设所用的触发器的时序参数与图7-84中用过的触发器相同,请计算该电路能正常运行的最高频率。再次假设通过逻辑门的传播延迟可以用1+0.1k来计算。在这个电路中,存在着四个触发器从开始到结束的许多路径。最长的路径从触发器Q0起到触发器Q3结束。在某个电路中最长的路径成为关键路径。关键路径的延迟包括触发器Q0的时钟信号到Q的延迟、通过三个与门的传播延迟和一个异或门的延迟。我们还必须考虑触发器Q3的建立时间。因此,得到Tmin=tcQ+3(tAND)+tXOR+tsu用tcQ的最大值,得到Tmin=1.0+3(1.2)+1.2+0.6=6.4nsFmax=1/6.4ns=156.25MHz该电路的最短路径是从每个触发器通过异或门反馈到该触发器本身的输入端。沿每个这样路径的最小延迟为tcQ+tXOR=0.8+1.2=2.0ns。因为2.0ns&th=0.4ns,因此保持时间足够长,没有违反规定。& && & 在上面的分析中,假设时钟信号同时到达所有四个触发器。我们现在将重复这个分析,假设时钟信号同时到达触发器Q0,Q1,Q2,但到达触发器Q3有一些延迟。始终到达不同的触发器之间的时间差称为时钟偏差(clock skew),记作tskew,时钟偏差可以由许多原因引起。& && & 在图7-85中,电路的关键路径是从触发器Q0起到触发器Q3。然而,Q3的时钟偏差使得这个延迟减少,因为时钟偏差在数据被加载进该触发器前提供了附加的时间。如果考虑增加1.5ns的时钟偏差,则从触发器Q0到触发器Q3的路径延迟由tcQ+3(tAND)+tXOR+tsu-tskew=6.4-1.5ns=4.9ns给定。该电路现在还存在一个不同的关键路径,该路径从触发器Q0起到触发器Q2结束。这条路径的延迟为Tmin=tcQ+2(tAND)+tXOR+tsu=1.0+2(1.2)+1.2+0.6ns=5.2nsFmax=1/5.2ns=192.31MHz& && & 在这种场合,时钟偏差导致电路的最高时钟频率提高。但是,如果时钟偏差是负的,即触发器Q3的时钟到达时间比其他触发器更早一些,则会造成该电路的最高时钟频率Fmax降低。& && & 因为数据加载到触发器Q3被时钟偏差延迟了,所以对所有起始于Q0,Q1,Q2而以Q3为结束点的路径,都会产生使触发器Q3的保持时间需要增加到th+tskew的影响。在该电路中,这种最短的路径是从触发器Q2到Q3的路径,其延迟时间为TcQ+tAND+tXOR=0.8+1.2+1.2=3.2ns。因为3.2ns&th+tskew=1.9ns,所以保持时间足够长,没有违反规定。如果对时钟偏差值tskew&=3.2-th=2.8ns,重复以上保持时间的分析,则会出现保持时间不够的情况。当tskew&=2.8ns时,该电路将不可能在任何频率下可靠地运行。由于时钟偏差的存在会引起电路时序问题,所以好的电路设计方法必须保证时钟信号到达所有触发器的偏差尽可能小。最后是我的总结:确定最小周期是找关键路径即最长路径。确定Th是否违例是找最短路径。最短路径要大于Th。如果有Tskew的情况则要大于Th+Tskew(有skew的寄存器为最短路径的终点的时候)还有就是我对有Tskew的情况的时候为什么防止违例要最短路径&Th+Tskew。因为Q0,Q1和Q2时钟比Q3早,以他们为起点的路径已经开始走了一段时间后Q3的时钟才到才开始打入数据。所以保持时间上要加上这段skewISE 约束文件的基本操作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" A其中,&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&可以是FPGA芯片中任一或多个合法位置。如果为多个定位,需要用逗号&,&隔开,如下所示:LOC = location1,location2,...,目前,还不支持将多个逻辑置于同一位置以及将多个逻辑至于多个位置上。需要说明的是,多位置约束并不是将设计定位到所有的位置上,而是在布局布线过程中,布局器任意挑选其中的一个作为最终的布局位置。范围定位的语法为:INST &instance_name& LOC=location:location [SOFT];常用的LOC定位语句如表4-4所列。表 常用的LOC定位语句使用LOC完成端口定义时,其语法如下:NET "Top_Module_PORT" LOC = "Chip_Port";其中,&Top_Modu

我要回帖

更多关于 place and route 的文章

 

随机推荐