zoukankan      html  css  js  c++  java
  • Verilog杂谈

    1. Testbech总是用reg去驱动DUT的input端口,因为需要在仿真期间设置和保持输入端的值(例如在initial中设置初值,在always中设置激励值);

    2. 避免对局部reg在定义时赋值,尽管这在计算机语言中很常见。例如:

    always @(...) begin : XXX
      reg c = 0;
      c = ...;
    end

       上面的代码中,定义时赋值会使得c仅在初次进入always时被赋值为0,其后的值因reg特性而得以保留,从而综合工具(至少quartus)会综合出组合环路。改成如下形式即可:

       reg c;

       c = 0;

       c = ...

       modelsim甚至拒绝编译局部reg定义时赋值的代码。

    3. 可以在always语句中给reg赋默认值

    always @(...) begin
      c = 1;            // c is a reg
      if(condition)
        c = 0;
    end

        这样即使没有else也不会综合出环路。(VHDL的process语句也可采用这种写法)

    4. in/out/reg等等可直接写在端口列表中,例如:

    module(input clk, input[3:0] preset, output reg[3:0] cnt);

    5. always begin/end中,“=”是阻塞式赋值,"<="是并发式赋值。所以:

    always @(posedge clk) begin
      d1 = d0;
      d2 = d1;     // 阻塞赋值,两条语句依次执行完毕后(d2=d1)等待下一个clk,对应综合结果一级锁存
    end
    
    always @(posedge clk) begin
      d1 <= d0;
      d2 <= d1;    // 并发赋值,两条语句(在同一个时钟边沿)并发执行,对应综合结果二级并行锁存
    end

        注意:设计程序时应避免在同一always块内同时出现阻塞赋值和非阻塞赋值。

        综合工具(至少quartus)拒绝综合always fork/join。

    6. in/inout*不*能是reg;in端口从模块外部取值因此不可能保持某个值;inout具有in能力因此同前述(从综合角度看inout必须具有tri能力以便在必要时关闭out用作in)。

    7. 惯性延迟和传播延迟

    7.1 惯性延迟

    惯性延迟使得小于延迟时间的信号变化被忽略。

    例如:

    assign #5 b = a;

    或者:

    always @(a) #5 b = a;

    以上语句执行如下:

    1> a发生变化;

    2> 5个时间单位后,将a的当前值赋给b。

    于是,若a上产生的是小于5个时间单位的脉冲,那么当赋值语句执行时,a的值已恢复,b上将不会产生脉冲。

    测试代码和仿真结果如下:

    `timescale 1ns/100ps
    module sim; reg a; reg b; initial begin a = 0; b = 0; #1 a = 1; #1 a = 0; #1 a = 1; #1 a = 0; end always @(a) begin if($time > 0) begin $display($time, " a=%b, b=%b", a, b); #5 b = a; $display($time, " a=%b, b=%b", a, b); end end endmodule

    代码跳过$time=0时刻在a上x->0的变化。

    $display语句打印如下消息:

    # 1 a=1, b=0
    # 6 a=0, b=0

    仿真波形:

    7.2 传播延迟

    传播延迟传递信号的任何变化。测试程序如下:

    (注意赋值语句为非阻塞赋值,注意延时值在语句中的位置)

    `timescale 1ns/100ps
    
    module sim;
      
      reg a;
      reg b;
      
      initial begin
        a = 0;
        b = 0;
        #1 a = 1;
        #1 a = 0;
        #1 a = 1;
        #1 a = 0;
      end
      
      always @(a) begin
        if($time > 0) begin
          $display($time, " +++ a=%b, b=%b", a, b);
          b <=#5 a;
          $display($time, " --- a=%b, b=%b", a, b);
        end
      end
      
    endmodule

    上面always块执行时,a发生变化后立即取a的值,在5个时间单位后赋值给b。并且,因为采用了非阻塞式赋值,always语句块在取得a的值后就退出,以便能够捕捉到a上的下一次变化(此时延时时间还没到,b还没有得到新值)。

    仿真结果如下:

    # 1 +++ a=1, b=0
    # 1 --- a=1, b=0
    # 2 +++ a=0, b=0
    # 2 --- a=0, b=0
    # 3 +++ a=1, b=0
    # 3 --- a=1, b=0
    # 4 +++ a=0, b=0
    # 4 --- a=0, b=0

    从仿真结果可以看出,非阻塞赋值语句的执行和生效是分开的。当使用上述赋值形式时,语句的执行是立即的,而生效时间由延时时间设定。

    7.3 使用语句内延迟和阻塞式赋值

    always @(a) b =#5 a;

    执行如下:

    取得a的值;延时5时间单位;赋值给b。此写法捕获a的第一次变化,但忽略其后a上5个时间单位内的任何变化。

    例如对于以下激励,b在时间单位6从0变为1,且不再回到0。

      initial begin
        a = 0;
        b = 0;
        #1 a = 1;
        #1 a = 0;
        #1 a = 1;
        #1 a = 0;
      end

    7.4 使用语句间延迟和非阻塞赋值

    always @(a) #5 b <= a;

    由于只有一条赋值语句,其结果与惯性延迟等价。

    7.4 在fork/join内使用延时语句

    [略]

    8 event

    event是广播且无记忆的,因此,当event发生时,只有且所有正在其上等待的进程会接收到这个ev。

    9 纯延时

    always begin
      #5;
    end

     10 语句块和disable

    总是使用独立的begin/end来标识语句块(?):

    always begin
        begin : my_block
            for(int i = 0; i < 16; i++) begin // i++是system verilog语法
                if(i == 8) begin
                    disable my_block;
                end
            end
        end
    end

    这样看起来比较清晰且不会引发误解。如果将my_block标记在for循环的begin后面,disable的将是该begin和对应end之间的语句块,也就是if语句,而for循环本身不会被禁用。
    disable对语句块的禁用是“临时”的,下次满足进入语句块条件时仍会执行。上例中禁用for循环后,再次进入always块仍然会执行for循环。

  • 相关阅读:
    Skim设置豆沙绿背景色的方法
    被咬掉一口的苹果标识的快捷键
    删除 Mac OS X 中“打开方式”里重复或无用的程序列表
    Android开发学习笔记1
    新学到的Eclipse快捷键 2个
    Android开发学习笔记2
    Mac下Eclipse的自动补全设置
    Nsight Eclipse关于CUDA程序语法高亮颜色的调整
    Tecpolt for mac
    转载:Nsight颜色设置
  • 原文地址:https://www.cnblogs.com/byeyear/p/5143718.html
Copyright © 2011-2022 走看看