基于Verilog HDL的模型优化
来源:岁月联盟
时间:2010-08-30
1 引言
每个设计者在进行Verilog建模时都会形成自己的设计风格,同一个电路设计,用Verilog描述可以写出许多逻辑上等价的模型,而大多数设计者考虑的主要是代码书写上的方便和功能上是否正确,对设计的模型是否最优化结构却考虑甚少,这样不仅加重了逻辑综合的负担,影响综合效率,而且很可能会导致设计出的芯片未达到最优的面积和速度。因此,在Verilog建模时,很有必要进行模型优化。2 模型优化概述
影响一个芯片性能的指标主要有两个:面积和速度。模型优化就是通过一定的手段对模型的结构进行调整、组合和精简,从而使设计出的芯片达到更小的面积和更快的速度。 综合所生成的逻辑易受模型描述方式的影响。把语句从一个位置移到另一个位置,或者拆分表达式都会对所生成的逻辑产生重大影响,这可能会造成综合出的逻辑门数有所增减,也可能改变其定时特性。因此,采取一定的手段可以实现对逻辑的优化。但是由于优化终点包含的两个方面面积和速度是互相矛盾的,优化一个方面必定影响另一个方面,而无法实现面积和速度都达到最优,这就需要设计者对两者进行权衡,看设计偏重于哪个方面,而采取不同的优化起点。下面分别从面积和速度两个方面对模型优化的手段进行介绍。3 面积的优化
3.1 提取公共子表达式
如果条件语句的互斥分支中有公共子表达式,可以提取该公共子表达式。如下面的模型可以提取公共子表达式: if(enable) P = A&(B+C); else Q = (B+C)|D; 此模型中条件语句的互斥分支中都了表达式B+C,因此,应将该表达式提取出来放在条件语句之前进行赋值,新模型如下所示: Tmp = B+C; //引入一个临时变量 if(enable) P = A&Tmp; else Q = Tmp |D; 这样,综合工具就会综合出一个加法器,而原来的模型则会综合出两个加法器。引申到一般情况,若在逻辑中找到有公共子表达式,就可以将该公共子表达式赋值给一个临时变量,然后用该临时变量来表示该公共子表达式,这样就可以减少综合出的ALU单元的数量,以实现面积的优化。3.2 代码移位
如果在循环语句内某个表达式的值在每次循环中都不变化,可以将该表达式移至循环之外。如下面的模型可以进行代码移位:P = ……for(i=1;i<=5;i++)begin … Q = P+5; //假设循环中未对P赋新值 …end 赋值语句“Q = P+5;”右端的表达式不随循环变量而变,因此,应将该表达式移至循环之外,新模型如下所示:P = ……Tmp = P+5; //引入一个临时变量for(i=1;i<=5;i++)begin … Q = Tmp; …end 这样,综合工具对“P+5”只会综合出一个加法器,而原来的模型会产生5个加法器,每循环一次就产生一个,造成了代码冗余。优化后的新模型不仅减少了综合出的ALU单元的数量,而且提高了仿真效率。3.3 资源共享
资源共享是指在互斥条件下共享算术逻辑单元(ALU)的过程。如下面的模型: if(num>5) P = A+B; else P = A-C; 如果不采用资源分配,算符“+”和“-”就会被综合成两个单独的ALU。而如果采用了资源分配,仅需一个ALU就可以实现“+”和“-”这两种运算。这是因为这两种算符总是互斥地使用。此外还生成了一个多路选择器,用来从B和C中选择合适的量接到ALU的第二个输入端上。实际上,资源分配就是共享算符的过程。共享算符有以下几种可能的情况: (1)算符相同,运算量相同。如:A+B和A+B,这种情况同“提取公共子表达式”,显然必须共享。 (2)算符相同,有一个运算量不同。如:A+B和A+C,这时需引入一个多路选择器,要进行面积与速度之间的权衡。 (3)算符相同,运算量都不同。如:A+B和C+D,这时需引入两个多路选择器,要进行面积与速度之间的权衡。 (4)算符不同,运算量相同。如:A+B和A-B,可以将“+”和“—”合成一个ALU单元,要共享。 (5)算符不同,有一个运算量不同。如:A+B和A-C,这时需引入一个多路选择器,要进行面积与速度之间的权衡。 (6)算符和运算量都不同。如:A+B和C-D,这时需引入两个多路选择器,要进行面积与速度之间的权衡。 在共享ALU的时候,要在ALU的某个输入端引入多路选择器,这样会增加路径的延迟。因此,设计者应根据实际情况权衡是优化面积重要还是优化速度重要,如果是在“定时至上”的设计中,最好不要采用资源共享。 此外,对于复杂的运算单元,可以采用函数和任务来定义这些共享的数据处理模块,以减少器件资源的消耗,降低成本。3.4 消除触发器
有些设计者为了图编写代码的方便,喜欢将同一条件控制下的赋值语句写在一个时序控制语句中,如下面的模型:always @(posedge CLK)begin case(State) 0: begin preState <= 1; Dout <= 16’h56; end 1: begin preState <= 0; Dout <= 16’h29; endendcaseend 设计者的本意仅是要把preState的值保存在上升沿触发的触发器中,而Dout的值只是受State影响的组合逻辑,原本只需要1个触发器即可,而上述模型综合后的网表会生成17个触发器,浪费了资源,优化后的模型如下所示:always @(posedge CLK) //推导出触发器begin case(State) 0: preState <= 1; 1: preState <= 0;endcaseend always @(State) //组合逻辑begin case(State) 0: Dout <= 16’h56; 1: Dout <= 16’h29; endcaseend3.5 消除锁存器
推导锁存器的规则是: (1)变量在条件语句(if或case语句)中被赋值。 (2)变量未在条件语句的所有分支中都被赋值。 (3)在always语句的多次调用之间需要保存变量值。 同时满足以上3个条件,就会将变量推导成锁存器。有的设计者可能会图省事没有在所有的条件分支中对变量进行赋值,这样就导致原本不需要产生锁存器的变量产生了锁存器,而浪费了资源。 消除锁存器的最好方法是在设计时明确哪些变量需要锁存器,哪些则不需要。对不需要推导出锁存器的变量,在其所有条件分支中都对其赋值,或者是在条件语句之前对其进行初始化赋值。4 速度的优化
4.1 使用括号
在表达式中使用括号,可以控制所综合出的逻辑电路的结构,缩短电路的关键路径,从而实现速度的优化。 例如对语句P = A+B-C+D,综合工具在综合右端表达式时遵循从左至右进行演算,就会构造出如图1所示的电路。 使用括号后的语句为:P = (A+B)-(C-D),综合后的电路如图2所示。

很显然,未使用括号时关键路径的深度为3,而使用括号后的关键路径深度为2,优化了速度。
4.2 提取关键路径
在电路设计中,有些信号的路径比较长,或者信号本身就来得比较晚,从而造成电路的建立时间不够。这种引起电路建立时间不够的信号路径称为关键路径。这种关键信号路径大多要提取出来特别对待,以尽量减少它的延时。4.2.1 提取重复变量 如语句P = (a&b&c)|(b&d&e)中的信号b的路径就是关键路径,可以提取出来单独处理,提取关键路径前后的电路模型如图3所示。由图中可以看出,信号b的路径由2级变成了1级,增加了其建立时间,缩短了延迟,而且还减少了一个与门,既提高了速度又减少了面积。4.2.2 提取先行关键路径如下面的模型所示: always @(a or b or c or d or e or current_out) begin next_out = current_out; if(!a)begin if(b & !(d & !e)) next_out = !c; else next_out = c; end else if(d & !e) next_out = c; end其中,输入信号e在always语句块中是个时间非常紧的关键信号,需要进行特殊处理。处理后的模型如下所示: always @(a or b or c or d or e or current_out) begin next_out = current_out; if(e)begin if(!a) begin if(b) next_out = !c; else next_out = c; end end else begin if(!a) begin if(b&!d) next_out = !c; else next_out = c; end
else if(d) next_out = c;
end end 上述模型描述了关键信号e的分步提取方法,改写后的描述都与原always块逻辑等效。