一直没系统的整理过DC这块的东西,这里借助一个挺好的文档《综合与Deisgn Compiler》以及我自己的经验和理解来归总一下。
1. 综合是什么综合是使用软件的方法来设计硬件,然后将门级电路实现与优化的工作留给综合工具的一种设计方法。它是根据一个系统逻辑功能与性能的要求,在一个包含众多结构、功能、性能均已知的逻辑元件的单元库的支持下,寻找出一个逻辑网络结构的最佳实现方案。即实现在满足设计电路的功能、速度及面积等限制条件下,将行为级描述转化为指定的技术库中单元电路的连接。
综合分为转换,映射,优化三个阶段,先把HDL描述转换成RTL级网表(此时与工艺库无关),再映射到指定的工艺库形成门级网表,最后根据延时、面积等设计约束进行网表优化。
我们这次讨论的所有综合都是基于Synopsys Design Compiler综合工具。
2. Verilog到网表的映射Verilog编码会影响综合生成的电路的质量,因此代码编写时需要满足一定的规则,例如要保证代码的可综合性,不使用无法综合的语句;尽量多使用同步逻辑,将同步逻辑和异步逻辑分开处理;注意代码编写的抽象层次,尽量使用RTL级的描述等。
对于一个优秀的设计者来说,清楚的知道自己编写的代码会被综合成何种电路,以及如何控制工具去综合出自己想要的电路是非常重要的。
接下来介绍一些典型Verilog语句到具体电路之间的映射关系。
首先是基本的组合逻辑块,Verilog描述的逻辑关系会与实际电路直接映射,例如:
always@(A or B or C or D) beginTemp1 = A ^ B;Temp2 = C ^ D;Z = Temp1 ^ Temp2;end生成的电路是:
对于verilog的if语句来说,会产生受条件控制的电路,例如:
always@(Ctrl or A or B) beginif(Ctrl)Z = A & B;elseZ = A | B;end产生的电路如下(A,B,Z均为2bit信号):
可以看到这里的逻辑选择功能与或门和与非门相关,将方框里或门和与非门组成的OAI21逻辑单元列逻辑表达式:
\[Z=\overline{(C+X)\cdot Y}=\overline{(C+X)}+\overline{Y}=\overline{C}\cdot\overline{X}+\overline{Y}\]这里的\(X\)即\(\overline{A+B}\),\(Y\)即\(\overline{A\cdot B}\),如果\(C=1\),那么\(Z=0\cdot\overline{X}+\overline{Y}=\overline{Y}=A\cdot B\),如果\(C=0\),那么\(Z=1\cdot \overline{X}+\overline{Y}=\overline{X}+\overline{Y}=A+B+A\cdot B=A+B\cdot(1+A)=A+B\),因此:
\[Z =\begin{cases} A\cdot B & C=1\\ A+B & C=0 \end{cases}\]这里利用了输入的性质进行了一些逻辑化简,如果是更一般的情况,我们需要的逻辑表达式是:
\[Z =\begin{cases} \overline{Y} & C=1\\ \overline{X} & C=0 \end{cases}\]因此\(Z=\overline{C}\cdot \overline{X}+C\cdot \overline{Y}\),运用德摩根定律可以得到\(Z=\overline{(C+X)\cdot(\overline{C}+Y)}\),这个电路可以用一个OAI22来实现,只是控制比特\(C\)需要做一个反相。
如果if条件语句没有写全,就会出现latch的问题,例如:
always@(Marks) beginif(Marks < 5)Grade = FAIL;else if((Marks >= 5) & (Marks < 10))Grade = PASS;end产生的电路如下(Mark为4bit信号,Grade为2bit信号):
由于对于Marks大于等于10的输入条件没有判断,因此可以当成此时输出不予更新,即输出被锁存,因此此时会生成latch。
latch对于时序分析有非常负面的影响,会导致综合时产生unconstrained point。因此在代码上一定要规避这种情况,直截了当的方式就是补全条件:
always@(Marks) beginif(Marks < 5)Grade = FAIL;else if((Marks >= 5) & (Marks < 10))Grade = PASS;elseGrade = ExCELLENT;end此时就可以完全规避latch的产生:
这里需要注意的问题是,使用if语句描述的电路是隐含了优先级的,即语句顺序上写在前面的逻辑会被优先判断。而对于case语句来说,如果其是条件互斥的(即不存在同时满足多个条件的情况),那么就不存在优先级的概念。
例如:
always@(Op or A or B) begincase (Op)ADD : Z = A + B;SUB : Z = A - B;MUL : Z = A * B;DIV : Z = A / B;endcaseend对应的电路为:
如果使用casex语句可以实现优先级编码,例如:
always@(Toggle) begincasex(Toggle)3'bxx1 : NextToggle = 3'b010;3'bx1x : NextToggle = 3'b110;3'b1xx : NextToggle = 3'b001;default: NextToggle = 3'b000;endcaseendmodule产生的电路为:
其功能和下面的if语句一致:
if(Toggle[0] == 1'b1)NextToggle = 3'b010;else if(Toggle[1] == 1'b1)NextToggle = 3'b110;else if(Toggle[2] == 1'b1)NextToggle = 3'b001;elseNextToggle = 3'b000;和if的情况类似,如果没有指出case的全部情况,那么会引入latch,例如:
always@(Toggle) begincase(Toggle)2'b01 : NextToggle = 2'b10;2'b10 : NextToggle = 2'b01;endcaseend产生的电路为:
原因和if产生latch的情况类似,如果出现了未被条件分支包含的解决的方案是增加一个default的描述:
always@(Toggle) begincase(Toggle)2'b01 : NextToggle = 2'b10;2'b10 : NextToggle = 2'b01;default : NextToggle = 2'b01;endcaseend对于case语句来说,可以通过综合指令的方式来指定一些特殊情况,例如,如果设计者已经知道除了列出的case之外不会出现其他的条件,而又不想让工具综合出latch,那么可以使用综合指令synopsys full_case来传达,具体来说:
always@(Toggle) begincase(Toggle)// synopsys full_case2'b01 : NextToggle = 2'b10;2'b10 : NextToggle = 2'b01;endcaseend但需要注意的两个问题是:
加入综合指令会使代码的结果依赖于所用的综合工具,从而降低代码的可移植性加入综合指令后产生的电路网表会和当初的Verilog建模有出入,导致验证的复杂另一种情况是,假设我们已经知道了case的条件是互斥的(互斥的情况下,case会平行的检查所有可能的情况,而不是先检查第一个再检查第二个),为了实现并行检查,可以加入synopsys parallel_case指令,这样综合工具会理解case项是互斥的,从而避免产生带优先级的电路,而是平行的译码结构(或者MUX),例如:
always@(Toggle) begincasex(Toggle)// synopsys parallel_case3'bxx1 : NextToggle = 3'b010;3'bx1x : NextToggle = 3'b110;3'b1xx : NextToggle = 3'b001;default: NextToggle = 3'b000;endcaseendmodule对应的if语句为:
if(Toggle[0] == 1'b1)NextToggle = 3'b010;if(Toggle[1] == 1'b1)NextToggle = 3'b110;if(Toggle[2] == 1'b1)NextToggle = 3'b001;if((Toggle[0] != 1'b1) && (Toggle[1] != 1'b1) && (Toggle[2] != 1'b1))NextToggle = 3'b000;值得一提的是,在systemverilog中可以使用unique case来指定互斥的case条件语句(对应并行编码电路),用priority case来指定带有优先级的case条件语句(对应优先级编码电路)
unique case ()... // case itemsendcasepriority case ()... // case itemsendcase进一步通过systemverilog的always_comb语句可以控制电路综合时不产生latch。因此一个ALU可以写成:
always_combunique case (opcode)2’b00: y = a + b;2’b01: y = a - b;2’b10: y = a * b;2’b11: y = a / b;endcase现在的仿真工具和综合工具已经能够支持systemverilog和verilog的混编,因此一些设计需求可以考虑用systemverilog来实现从而解决之前提到的用编译指令带来的影响可移植性和网表与verilog建模不一致的问题。具体内容可以参考以前的博客。
当我们需要重复性的产生电路时,可以使用verilog中的loop语句,最常用的是for-loop语句,具体来说:
integer J;always@(Address) beginfor(J=3; J>=0; J=J-1) beginif(Address == J)Line[J] = 1;elseLine[J] = 0;endend在这个例子会产生下面的电路:
而如果展开编写的话则等效于:
if(Address == 3) Line[3] = 1; else Line[3] = 0;if(Address == 2) Line[2] = 1; else Line[2] = 0;if(Address == 1) Line[1] = 1; else Line[1] = 0;if(Address == 0) Line[0] = 1; else Line[0] = 0;显然对于多次重复的电路来说,用loop来产生是更高效的做法,基本的for循环语句只可以用于赋值操作。如果涉及例化,常见的做法是用genvar以及generate for来进行,例如:
genvar i;generatefor(i=0;i $report_path/timing.rptcheck_design > $report_path/check_design_post_compile.rptcheck_timing > $report_path/check_timing_post_compile.rptsdc
# Definition of Clockset Tclk 20 ;#50MHzset clk_name "clk"set unc_perc 0.05create_clock -name $clk_name -period $Tclk [get_ports clk]set_clock_uncertainty -setup [expr $Tclk * $unc_perc] [get_clocks $clk_name]set_clock_transition 0.4 [all_clocks]# Set Ideal Networkset_dont_touch_network [get_ports clk]set_ideal_network [get_port clk]set_dont_touch_network [get_ports rst_n]# Definition of IOset_input_delay [expr $Tclk*1/5.0] -clock $clk_name [all_inputs]remove_input_delay [get_ports clk]set_output_delay [expr $Tclk*1/5.0] -clock $clk_name [all_outputs]# Other Constraintset_max_transition 0.6 [current_design]set_max_fanout 64[current_design]set_max_capacitance 2[current_design]set_input_transition -max 0.5 [all_inputs]set_load 2 [all_outputs]set_max_leakage_power 0set_max_area 0