可综合的Verilog

可综合的HDL的语法只是它们自己语言的一个子集。为什么要这么做?

  • 减少综合的次数或运行时间,最快得到良好的综合结果
  • 采用比较简单的综合约束就可以得到时序和面积的要求
  • 提高综合结果网表的性能,简化静态时序分析的过程

什么是逻辑综合?

  • 逻辑综合是在标准单元库和特定的设计约束的基础上,把设计的高层次描述转换成优化的门级网表的过程。
  • 标准单元库可以包含简单的单元,如:与非门、或非门、与门和或门等基本逻辑门,也可以包含宏单元,如加法器、多路选择器和触发器等。

综合的一般原则

  1. 综合之前一定要进行仿真,这是因为仿真会暴露逻辑错误,所以建议大家这样做。如果不做仿真,没有发现的逻辑错误会进入综合器,使综合的结果产生同样的逻辑错误。
  2. 每一次布局布线之后都要进行仿真,在器件编程或流片之前要做最后的仿真。
  3. 用 VerilogHDL 描述的异步状态机不能综合的,因此应该避免用综合器来设计;如果一定要设计异步状态机,则可用电路图输入的方法来设计。
  4. 如果要为电平敏感的锁存器建模,使用连续赋值语句是最简单的方法。

语言指导原则

  • 组合逻辑使用阻塞赋值语句描述
  • 时序逻辑使用非阻塞赋值语句描述

可综合、不可综合?

可进行逻辑综合的VerilogHDL结构

  • 端口:input, inout, output
  • 参数:parameter
  • 模块定义:module
  • 信号变量:wire, reg, tri;允许使用向量表示
  • 调用(实例引用):module instance, gate instance
  • 函数和任务:function, task;不考虑时序结构
  • 过程:always, if, else, case, casex, casez;
  • 过程块:begin...end,命名块,disable
  • 数据流:assign;不考虑延时
  • 循环:for, while, forever;while和forever必须包含@(posedge clock)或@(negedge clock)

几乎所有的操作符都可以被综合,除了两兄弟......?

答案

=== !==

不可综合的Verilog关键字

  • time、defparam(不一定支持)、fork、join、initial、wait
  • 各种延时
  • UDP
  • 循环语句: repeat forever while
  • 过程持续赋值: assign deassign force release

小心锁存器

always always...

always的敏感列表一定要写全。不然,总是会产生透明锁存器。这是非常非常糟糕的

1
2
3
4
5
6
7
8
9
10
11
12
13
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信号的变化锁存其中
*/
d = e | c;
end
这是Latch

此外,不能混用沿触发和电平触发。

但是上升沿和下降沿可以混用,不过并不推荐。

写全所有可能情况

如何避免锁存器产生?

注意'bxn'bx的用法。

数字系统设计复习笔记:第二篇

避免在综合时引入锁存器的方法

  • 组合函数的输出必须在每个可能的控制路径中被赋值
  • 每次执行always块时,在生成组合逻辑的always块中赋值的所有信号都必须有明确的值
  • 组合电路的每一个if描述语句都对应一个else语句
  • 每一个case语句都对应一个default语句(在没有优先级的情况下优先使用,设计路径延时要小于if-else)

可综合的Verilog HDL模块实例

组合逻辑电路

指令译码电路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
`define plus    3'd0
`define minus 3'd1
`define band 3'd2
`define bor 3'd3
`define unegate 3'd4
module alu (
out,
opcode,
a,
b
);
output [7:0] out;
input [2:0] opcode;
input [7:0] a,b;
reg
[7:0] out;
always @(opcode or a or b)
//用电平敏感的always块描述组合逻辑
begin
case(opcode)
//算术运算
`plus:
out=a+b;
`minus:
out=a-b;
//位运算
`band:
out=a&b;
`bor:
out=a|b;
//单目运算
`unegate:
out=~a;
default:
out=8'hx;
endcase
end
endmodule

比较器

1
2
3
4
5
6
7
8
9
10
module compare (
equal,
a,
b
);
parameter size = 1;
output equal;
input [size-1:0] a, b;
assign equal = (a == b) ? 1 : 0;
endmodule

3-8译码器

1
2
3
4
5
6
7
8
9
module decoder (
out,
in
);
output [7:0] out;
input [2:0] in;
assign out = 1'b1 << in;
//把最低位的1左移 in(根据从in口输入的值)位,并赋予out
endmodule

8-3编码器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
module encoder1 (
none_on,
out,
in
);
output none_on;
output [2:0] out;
input [7:0] in;
reg [2:0] out;
reg none_on;

always @(in) begin : local_1
/*
此处begin-end块必须有一模块名
因为块中定义了局部变量
*/
integer i;
out = 0;
none_on = 1;
/*
返回输入信号in的8位中为1的最髙位数
*/

for (i = 0; i < 8; i = i + 1) begin
if (in[i]) begin
out = i;
none_on = 0;
end
end

end

endmodule

时序逻辑电路

八位计数器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module counter2( out, cout, data, load, cin, clk);
output [7:0] out;
output cout;
input [7:0] data;
input load, cin, clk;
reg [7:0] out;
reg cout;
reg [7:0] preout;
//创建8位寄存器
always @(posedge clk)
begin
out <= preout;
end
/****计算计数器和进位的下一个状态,注意:为提高性能不希望加载影响进位****/
always @(out or data or load or cin)
begin
{cout, preout} = out + cin;
if(load)
preout = data;
end
endmodule

移位寄存器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module shifter( din, clk, clr,
dout);
input din, clk, clr;
output [7:0] dout;
reg [7:0] dout;
always @(posedge clk)
begin
if(clr)
//清零
dout <= 8'b0;
else
begin
dout <= dout<<1;//左移1位
dout[0] <= din;
//把输入信号放入寄存器的最低位
end
end
endmodule

良好的Verilog可综合代码风格

  • 采用异步复位,控制电路中所有的FF和latch的异步置位或者清零;
  • 只使用时钟的上升沿/或下降沿;
  • Reset, set, Clock采用clean信号(或者外部输入信号);采用gated clock时要保证信号没有glitch;
  • vector range的定义采用descending order;
  • 模块调用采用显式端口连接,并保持与模块定义中端口顺序一致
  • 模块端口定义按照input、output顺序;
  • 芯片内部模块避免使用inout端口
  • reg变量禁止在多个always块中赋值(除了三态总线,最好三态总线也避免使用);
  • FSM至少采用两个进程描述,分别产生组合逻辑和时序逻辑,状态编码使用parameter语句
  • always语句中的敏感量表要完整,但也不要多余;
  • verilog组合逻辑描述不要引起多余的或者不期望的Latch
  • 除了仿真调试用的语句外,要采用可综合的描述。仿真调试用的部分可以加注编译指令synopsys translate_off
  • 组合逻辑采用阻塞赋值(=),时序逻辑采用非阻塞赋值(<=);
  • 一个block中不要混合使用阻塞和非阻塞赋值
  • 复杂表达式要采用括号分割
  • 每行只写一个verilog语句;
  • 每个子模块的输出信号最好是DFF的寄存输出;
  • 尽可能不要对信号赋值x,不要使用casex语句
  • 注释:采用简洁的注释,包括文件头注释,语句模块注释;文件头注释应包括:Filename,Author、VersionHistory,Function Description,parameter等;

好的代码风格可以加快综合速度!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
always@(a or b or in1 or in2)
begin
if ((a - b) > 0)
c = in1;
else
c = in2;
end

// 下面写法更好,因为不需要工具根据常数优化逻辑
always@(a or b or in1 or in2)
begin
if (a > b)
c = in1;
else
c = in2;
end