10 Verilog语法_一般设计规范

软件版本:无

操作系统:WIN10 64bit

硬件平台:适用所有系列FPGA

登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!

1概述

本小节讲解Verilog语法的一般设计规范,需要掌握时序或组合电路设计中需要注意的几点,掌握设计避免出现锁存器。

2设计规范

上一节课我们讲解了竞争与冒险,其实我们在编写代码中,多注意设计规范,能够减少大多数的竞争与冒险的问题。

在verilog编程时需要注意以下几点:

(1)时序电路建模时,用非阻塞赋值(<=)。

(2)锁存器电路建模时,用非阻塞赋值(<=)。

(3)用always和组合逻辑建模时,用阻塞赋值(=)。

(4)在同一个always块中建立时序和组合逻辑模型时,用非阻塞赋值(<=)。

(5)在同一个always块中不要既用阻塞赋值又用非阻塞赋值(<=)。

(6)不要在多个always块中为同一个变量赋值。

阻塞赋值"="对应组合逻辑电路赋值,并且会阻塞后面的赋值操作,有先后顺序。非阻塞赋值"<="对应时序逻辑电路赋值,所有非阻塞赋值操作在同一时刻进行赋值。

2.1 时序逻辑电路中,对寄存器用非阻塞赋值。

时序电路中使用非阻塞赋值可以有效消除竞争与冒险。

比如下面代码描述,由于无法确定 a 与 b 阻塞赋值的操作顺序,也就是实际延迟时间未知,就有可能带来竞争冒险。

always @(posedge clk)

begin

    a = b ; //阻塞赋值,关键词" = "

    b = a ; //阻塞赋值,关键词" = "

end

如果使用非阻塞赋值,两个赋值操作是同时进行的,因此就不会带来竞争冒险。例:

always @(posedge clk) begin

    a <= b ; //非阻塞赋值,关键词" <= "

    b <= a ; //非阻塞赋值,关键词" <= "

end

2.2 组合逻辑电路中,对寄存器用阻塞赋值。

设计实现 C = A&B, F=C&D 的组合逻辑功能,也就是F= A&B&D,用非阻塞赋值语句设计如下。

always @(*)

begin

    C <= A & B ; //非阻塞赋值,关键词" <= "

    F <= C & D ; //非阻塞赋值,关键词" <= "

end

两条赋值语句同时赋值,F <= C & D 中使用的是信号 C 的旧值,因此导致此时的逻辑是错误的,F 的逻辑值不等于 A&B&D。

而且,此时要求信号 C 具有存储功能,但不是时钟驱动,所以 C 可能会被综合成锁存器(latch),导致竞争冒险。

对代码进行如下修改,F = C & D 的操作一定是在 C = A & B 之后,此时 F 的逻辑值等于 A&B&D,符合设计。

always @(*)

begin

    C = A & B ; //阻塞赋值,关键词" = "

    F = C & D ; //阻塞赋值,关键词" = "

end

2.3 同一个 always 块中建立时序和组合逻辑模型时,用非阻塞赋值。

在时序电路中常常可能会涉及组合逻辑,但是如果赋值操作使用非阻塞赋值,会可能导致出现问题。

例:

always @(posedge clk or negedge rst_n)

begin

    if (!rst_n) begin

        q <= 1'b0; //非阻塞赋值,关键词" <= "

    end

    else begin

        q <= a || b;   //非阻塞赋值,关键词" <= "

     end

end

2.4 同一个 always 块中不要既使用阻塞赋值又使用非阻塞赋值。

always 语句涉及的组合逻辑中,既有阻塞赋值又有非阻塞赋值时,可能会导致意外的结果。例:

always @(*) begin

    C = A & B ; //阻塞赋值,关键词" = "

    F <= C & D ; //非阻塞赋值,关键词" <= " ,不能同时使用

end

此时信号 C 阻塞赋值完毕以后,信号 F 才会被非阻塞赋值,结果可能正确。

如果 F 信号有其他的负载,F 的最新值并不能马上传递出去,数据有效时间还是在下一个触发时刻。此时要求 F 具有存储功能,可能会被综合成 latch,导致竞争与冒险。例:

always @(*)

begin

    C <= A & B ; //非阻塞赋值,关键词" <= "

    F = C & D ; //阻塞赋值,关键词" = "

end

信号 C 被非阻塞赋值,下一个触发时刻才会有效。而 F = C & D 虽然是阻塞赋值,但是信号 C 不是阻塞赋值,所以 F 逻辑中使用的还是 C 的旧值。

在时序电路里既有阻塞赋值,又有非阻塞赋值会怎样呢?例:

always @(posedge clk or negedge rst_n)

begin

    if (!rst_n)

begin

        q <= 1'b0; //非阻塞赋值,关键词" <= "

    end

    else

begin

        q = a & b;   //阻塞赋值,关键词" = "

    end

end

假如复位信号与时钟同步,那么由于复位导致的信号 q 为 0,是在下一个时钟周期才有效。如果是信号 a 或 b 导致的 q 为 0,则在当期时钟周期内有效。假如 q 还有其他负载,就会导致 q 的时序特别混乱,显然不符合设计需求。

2.5 不允许在多个 always 块中为同一个变量赋值

Verilog语法中不允许在多个 always 块中为同一个变量赋值。如果这样做了,该信号拥有多驱动源,这种行为是禁止的。当然,也不允许 assign 语句为同一个变量进行多次连线赋值。从信号角度来讲,多驱动时,同一个信号变量在很短的时间内进行多次不同的赋值结果,就有可能产生竞争冒险。从语法来讲,很多编译器检测到多驱动时,也会报错的。

2.6 设计避免产生latch

Latch是锁存器,是一种对脉冲电平敏感的存储单元电路,它们可以在特定输入脉冲电平作用下改变状态。锁存,就是把信号暂存以维持某种电平状态。锁存器的最主要作用是缓存,其次完成高速的控制器与慢速的外设的不同步问题,再其次是解决驱动的问题,最后是解决一个 I/O 口既能输出也能输入的问题。锁存器是利用电平控制数据的输入,它包括不带使能控制的锁存器和带使能控制的锁存器。

如图所示:

触发器(flip-flop),是边沿敏感的存储单元,数据存储的动作(状态转换)由某一信号的上升沿或者下降沿进行同步的。

如图所示:

寄存器(register),在FPGA中用来暂时存放中间运算的数据和结果的变量。一个变量声明为寄存器时,它既可以被综合成触发器,也可能被综合成 Latch。但是大多数情况下我们希望它被综合成触发器,但是有时候由于代码书写问题,它会被综合成不期望的 Latch。

锁存器与触发器最大区别在于,锁存器是电平触发,而触发器是边沿触发。锁存器在不锁存数据时,输出随输入变化,但一旦数据锁存时,输入对输出不产生任何影响。

Latch 的主要危害有:

(1)输入状态可能多次变化,容易产生毛刺,增加了下一级电路的不确定性。

(2)在大部分 FPGA 的资源中,可能需要比触发器更多的资源去实现 Latch 结构。

(3)锁存器的出现使得静态时序分析变得更加复杂。

Latch 多用于门控时钟(clock gating)的控制,一般设计时,应当避免 Latch 的产生。

Latch 的产生主要有下面几种情况:

1、在组合逻辑中,不完整的if-else结构,会产生latch。

避免此类latch的方法主要有两种,一种是补全if-else结构,或者对信号赋初值。

但是在时序逻辑中,不完整的if-else结构,不会产生latch,这是因为,寄存器具有存储功能,且其值在时钟的边沿下才会改变。

2、 在组合逻辑中,当 case 选项列表不全且没有加 default 关键字,或有多个赋值语句不完整时,也会产生 Latch。

消除此种 latch 的方法也是 2 种,将 case 选项列表补充完整,或对信号赋初值。

补充完整 case 选项列表时,可以罗列所有的选项结果,也可以用 default 关键字来代替其他选项结果。

总之,为避免 latch 的产生,在组合逻辑中,需要注意以下几点:

1)if-else 或 case 语句,结构一定要完整;

2)不要将赋值信号放在条件判断中;

3)敏感信号列表建议多用 always@(*)。

posted @ 2024-01-07 12:33  米联客(milianke)  阅读(65)  评论(0编辑  收藏  举报