2024年3月25日发(作者:)

1)所有综合工具都支持的结构:always,assign,begin,end,case,

wire,tri,aupply0,supply1,reg,integer,default,for,function,

and,nand,or,nor,xor,xnor,buf,not,bufif0,bufif1,notif0,

notif1,if,inout,input,instantitation,module,negedge,

posedge,operators,output,parameter。

(2)所有综合工具都不支持的结构:time,defparam,$finish,fork,

join,initial,delays,UDP,wait。

(3)有些工具支持有些工具不支持的结构:casex,casez,wand,triand,

wor,trior,real,disable,forever,arrays,memories,repeat,

task,while。

建立可综合模型的原则

要保证Verilog HDL赋值语句的可综合性,在建模时应注意以下要点:

(1)不使用initial。

(2)不使用#10。

(3)不使用循环次数不确定的循环语句,如forever、while等。

(4)不使用用户自定义原语(UDP元件)。

(5)尽量使用同步方式设计电路。

(6)除非是关键路径的设计,一般不采用调用门级元件来描述设计的方法,

建议采用行为语句来完成设计。

(7)用always过程块描述组合逻辑,应在敏感信号列表中列出所有的输

入信号。

(8)所有的内部寄存器都应该能够被复位,在使用FPGA实现设计时,应

尽量使用器件的全局复位端作为系统总的复位。

(9)对时序逻辑描述和建模,应尽量使用非阻塞赋值方式。对组合逻辑描

述和建模,既可以用阻塞赋值,也可以用非阻塞赋值。但在同一个过程块中,

最好不要同时用阻塞赋值和非阻塞赋值。

(10)不能在一个以上的always过程块中对同一个变量赋值。而对同一

个赋值对象不能既使用阻塞式赋值,又使用非阻塞式赋值。

(11)如果不打算把变量推导成锁存器,那么必须在if语句或case语句

的所有条件分支中都对变量明确地赋值。

(12)避免混合使用上升沿和下降沿触发的触发器。

(13)同一个变量的赋值不能受多个时钟控制,也不能受两种不同的时钟条

件(或者不同的时钟沿)控制。

(14)避免在case语句的分支项中使用x值或z值。

不可综合verilog语句

1、initial

只能在test bench中使用,不能综合。(我用ISE9.1综合时,有的简

单的initial也可以综合,不知道为什么)

2、events

event在同步test bench时更有用,不能综合。

3、real

不支持real数据类型的综合。

4、time

不支持time数据类型的综合。

5、force 和release

不支持force和release的综合。

6、assign 和deassign

不支持对reg 数据类型的assign或deassign进行综合,支持对wire

数据类型的assign或deassign进行综合。

7、fork join

不可综合,可以使用非块语句达到同样的效果。

8、primitives

支持门级原语的综合,不支持非门级原语的综合。

9、table

不支持UDP 和table的综合。

10、敏感列表里同时带有posedge和negedge

如:always @(posedge clk or negedge clk) d

这个always块不可综合。

11、同一个reg变量被多个always块驱动

12、延时

以#开头的延时不可综合成硬件电路延时,综合工具会忽略所有延时代码,

但不会报错。

如:a=#10 b;

这里的#10是用于仿真时的延时,在综合的时候综合工具会忽略它。也就是

说,在综合的时候上式等同于a=b;

13、与X、Z的比较

可能会有人喜欢在条件表达式中把数据和X(或Z)进行比较,殊不知这是不

可综合的,综合工具同样会忽略。所以要确保信号只有两个状态:0或1。

如:

?

module

synthesis_compare_xz (a,b);

2

output

a;

3

input

b;

4

reg

a;

5

6

always

@ (b)

7

begin

8

if

((b == 1'bz) || (b == 1'bx))

9

begin

10

a = 1;

11

end

12

else

13

begin

14

a = 0;

15

end

16

end

1

17

18

endmodule

常用的RTL语法结构如下

☆ 模块声明: module……endmodule

☆ 端口声明:input,output,inout(inout的用法比较特殊,需要注意)

☆ 信号类型:wire,reg,tri等,integer常用语for语句中(reg,wire时最常用的,一般tri和integer

不用)

☆ 参数定义:parameter

☆ 运算操作符:各种逻辑操作符、移位操作符、算术操作符大多时可综合的(注:===与!==是不

可综合的)

☆ 比较判断:if……else,case(casex,casez)……default endcase

☆ 连续赋值:assign,问号表达式(?:)

☆ always模块:(敏感表可以为电平、沿信号posedge/negedge;通常和@连用)

☆ begin……end(通俗的说,它就是C语言里的 “{ }”)

☆ 任务定义:task……endtask

☆ 循环语句:for(用的也比较少,但是在一些特定的设计中使用它会起到事半功倍的效果)

☆ 赋值符号:= 和 <= (阻塞和非阻塞赋值,在具体设计中时很有讲究的)

可综合的语法时verilog可用语法里很小的一个子集,用最精简的语句描述最复杂的硬件,这也正是

硬件描述语言的本质。对于做RTL级设计来说,掌握好上面这些基本语法是很重要。

相信大家在看了这么多了verilog语法书籍以后一定有点烦了,那么现在我告诉大家一个好消息,对

于一个RTL级的设计来说,掌握了上面的语法就已经足够了,无论多么牛逼的工程师,在他的代码

里无非也就是上面一些语法而已。当然了,对于一个能够进行很好的仿真验证的代码,一般还需要在

RTL级的设计代码中添加一些延时之类的语句,比如大家一定知道#10的作用,就是延时10个单位

时间,这个语句虽然在仿真的时候是实实在在的延时,但是这个语句在综合后是会被忽略的,也就是

说在我们综合并且布局布线最后烧进FPGA里,这个#10的延时是不会在硬件上实现的。所以说,上

面给出的这些语法才是可以最后在硬件上实现的,其它的语法大多会在综合后被忽略。这么一来大家

就要问了,为什么语法书里又要给出这么多的语法呢?呵呵,它们大都是为仿真验证是写testbench

准备的,先点到为止,下集继续!

对于模型(module)的建立,要保证可综合性应该注意:

(1)不使用initial。(被忽略)

(2)不使用#10。(被忽略)

(3)不使用循环次数不确定的循环语句,如forever、while等。

(4)不使用用户自定义原语(UDP元件)。

(5)尽量使用同步方式设计电路。

(6)除非是关键路径的设计,一般不采用调用门级元件来描述设计的方法,建议采用行为语句来完

成设计。

(7)用always过程块描述组合逻辑,应在敏感信号列表中列出所有的输入信号。

(8)所有的内部寄存器都应该能够被复位,在使用FPGA实现设计时,应尽量使用器件的全局复位

端作为系统总的复位。

(9)对时序逻辑描述和建模,应尽量使用非阻塞赋值方式。对组合逻辑描述和建模,既可以用阻塞

赋值,也可以用非阻塞赋值。但在同一个过程块中,最好不要同时用阻塞赋值和非阻塞赋值。

(10)不能在一个以上的always过程块中对同一个变量赋值。而对同一个赋值对象不能既使用阻塞

式赋值,又使用非阻塞式赋值。

(11)如果不打算把变量推导成锁存器,那么必须在if语句或case语句的所有条件分支中都对变量

明确地赋值。

(12)避免混合使用上升沿和下降沿触发的触发器。

(13)同一个变量的赋值不能受多个时钟控制,也不能受两种不同的时钟条件(或者不同的时钟沿)

控制。

(14)避免在case语句的分支项中使用x值或z值。

但是在仿真验证程序中,一般VerilogHDL语法都是容许的。

Verilog语法需要注意的一些要点

1. Testbench中时钟和数据比特流的简单设计方法

always clock = ~clock;

always @(posedge clock)

begin

data_in_a = {$radom} % 2; // 产生-1到1之间随机的比特流

data_in_b = 16’b1101_1011_1110_0011; // 产生固定的比特流

$display(“a= %dn”, a); // 与C语言类似

end

2. Verilog语法中的并行与顺序模块

(1) 连续赋值语句、always模块之间、实例模块之间都是并行语句

(2) always模块内部是分情况而定,对于if…else…而言,总是有优先级的顺序的,对于case而

言,无优先级,是完全顺序执行的,此外,还要对阻塞语句和非阻塞语句具体分析。

3. Verilog中四种最常见的变量

(1) wire,即线网形变量,它不能存储值,必须受到驱动器或者连续赋值语句的驱动,如果没有驱

动,那么它将会是高阻态

(2) reg是数据存储单元的抽象,通过赋值语句可以改变寄存器存储的值,其作用与改变触发器存

储的值相当。寄存器变量的初时值为不确定态。在always内部用到的变量必须是reg型的。

(3) prarmeter相当与VHDL中的constant

4. 注意区分集中容易混淆的运算符

(1) 位运算符,按位操作,~,|,&,^,其输出与输出一样位宽

(2) 逻辑运算符,输出0或者1,&&,||,!

(3) 缩减运算符,按位递归运算,&,|,!,其输出仅仅是1或者0

5. 阻塞语句(blocking)与非阻塞赋值语句(non-blocking)

(1) 非连续赋值语句(non-blocking)(b <= a)

在always块结束后才完成赋值操作,并且赋值后b不是立即就改变,在时序逻辑或者既有时序逻辑

也有组合逻辑中一定要用这种赋值方式

(2) 阻塞语句(blocking)(b = a)

赋值之后,b就立即改变,也就是在赋值语句完成以后,always才结束,在综合时,如果不注意,将

产生意想不到的结果

一个非常典型的例子:

always @(clock)

begin

b = a;

c = b;

end

always @(clock)

begin

c = b;

b = a;

end

always @(clock)

begin

b <= a;

c <= b;

end

6. 使用if或者是case进行综合时,一定要覆盖所有可能的情况,防止锁存器的综合

7. 写testbench一些常见的保留字

$display(“print_word_value = %d”, print_word_value); // 与C语言类似

$display(“%h, %o”, 12’b1101_0100_0111); // 结果是 0xD47和o6507

$monitoron; // 监控开始

$mointor($time, “rxd = %b”, “txd = %b”, rxd, txd); // 打印当前时刻的监控值

$monitoroff;

$finish; // 仿真结束

$readmemb(“数据文件名”, 存储器名); // 把数据读入到当前指定的存储器内

rand_data = {random} % 60; // 产生-59到59之间的随机数

8. 比较下面代码,分别综合出什么,可能出现什么问题

module dff_en(I_en, I_data, I_clock, O_data);

input I_clock;

input I_en;

input I_data;

output O_data;

reg R_data;

assign O_data = R_data;

always @(I_clock)

begin

if (I_en == 1) R_data <= I_data;

end

endmodule

有以下告警(用synplify综合)

(1) Incomplete sensitivity list - assuming completeness

(2) Referenced variable I_en is not in sensitivity list

(3) Referenced variable I_data is not in sensitivity list

(4) Latch generated from always block for signal

如果改always @(I_clock)为always @(posedge I_clock)就可以避免,并产生一个带使能的DFF

9. 下面逻辑有没有问题?

module dff_en(I_reset1, I_reset2, I_data, I_clock, O_data);

input I_clock;

input I_reset1;

input I_reset2;

input I_data;

output O_data;

reg R_data;

assign O_data = R_data;

always @(negedge I_reset1 or posedge I_clock) // 敏感变量I_reset2在时钟

begin

if (I_reset1 == 0) R_data <= 0;

else if (I_reset2 == 0) R_data <= 1;

else R_data <= I_data;

end

endmodule

综合时不会有告警,产生一个带复位和置位的DFF,最好改always @(negedge I_reset1 or posedge

I_clock)为always @(negedge I_reset1 or negedge I_reset2 or posedge I_clock)

10.逻辑综合结果是什么?

module mux2s1(I_sel, I_a, I_b, O_c, O_d)

input I_sel, I_a, I_b;

output O_c, O_d;

reg R_c;

reg R_d;

assign O_c = R_c;

assign O_d = R_d;

always @(I_sel, I_a, I_b)

begin

case (I_sel)

1'b0: R_c = I_a; // 综合出latch

1'b1: R_d = I_b; // latch

endcase

end

endmodule

该逻辑将产生锁存器。

11.下面代码综合结果是什么?

module dff_sys(I_clock, I_data, O_data);

input I_clock, I_data;

output O_data;

reg R_b, R_c, R_d;

always @(posedge I_clock)

begin

R_c = R_b;

R_b = I_data;

R_d <= R_c;

end

endmodule

module dff_sys(I_clock, I_data, O_data);

input I_clock, I_data;

output O_data;

reg R_b, R_c, R_d;

always @(posedge I_clock)

begin

R_c = R_b;

R_d <= R_c;

R_b = I_data;

end

endmodule

两段逻辑都没有任何区别,综合电路如下,这也说明了non-block语句的特点

下面逻辑呢?

module dff_sys(I_clock, I_data, O_data);

input I_clock, I_data;

output O_data;

reg R_b, R_c, R_d;

assign O_data = R_d;

always @(posedge I_clock)

begin

R_b = I_data;

R_c = R_b;

R_d <= R_c;

end

endmodule

module dff_sys(I_clock, I_data, O_data);

input I_clock, I_data;

output O_data;

reg R_b, R_c, R_d;

assign O_data = R_d;

always @(posedge I_clock)

begin

R_b <= I_data;

R_c <= R_b;

R_d <= R_c;

end

endmodule