zoukankan      html  css  js  c++  java
  • Verilog UDP(User Defined Primitives)

    User Defined Primitives

    这是一篇很浅显易懂的介绍Verilog UDP的文章,翻译过来留存,原文可参考这里

     

    介绍

    Verilog有内建原语如门,传输管,开关等,这些都是相当小的原语,如果我们需要更为复杂的原语,verilog提供了UDP,也就是用户定义原语(User Defined Primitives). 使用UDP可以建模组合电路和时序电路。

    语法

    UDP以保留字primitive开始,以endprimitive结束,并紧接着原语的Ports/terminals。这与module的定义类似。UDP应该定义在module和endmoudle外面。

    View Code
     1  //This code shows how input/output ports
    2 // and primitve is declared
    3 primitive udp_syntax (
    4 a, // Port a
    5 b, // Port b
    6 c, // Port c
    7 d // Port d
    8 );
    9 output a;
    10 input b,c,d;
    11
    12 // UDP function code here
    13 endprimitive

    在上面的语法中,udp_syntax是原语的名字,包含端口a,b,c,d。

    端口

    • 一个UDP只可以包含一个输出和最多10个输入。
    • 输出端口应该是第一个端口,然后才是一个或多个输入端口。
    • 所有的UDP都是标量,也就是,向量端口不允许。
    • UDP不能是双向端口。
    • 时序UDP的输出端需要额外声明为reg类型。
    • 组合UDP的输出端声明为reg类型是非法的。

    功能体

    原语的功能(包括组合和时序)在一个table表里描述,以保留字endtable结束。如以下代码所示。对于时序UDP,我们可以使用initial 给输出赋一个初始值。

    View Code
     1 // This code shows how UDP body looks like
    2 primitive udp_body (
    3 a, // Port a
    4 b, // Port b
    5 c // Port c
    6 );
    7 output a;
    8 input b,c;
    9 // UDP function code here
    10 // A = B | C;
    11 table
    12 // B C : A
    13 ? 1 : 1;
    14 1 ? : 1;
    15 0 0 : 0;
    16 endtable
    17
    18 endprimitive

    注意:UDP不能在输入table中使用“Z”

    TestBench

    View Code
     1 `include "udp_body.v"
    2 module udp_body_tb();
    3 reg b,c;
    4 wire a;
    5 udp_body udp (a,b,c);
    6 initial begin
    7 $monitor(" B = %b C = %b A = %b",b,c,a);
    8 b = 0;
    9 c = 0;
    10 #1 b = 1;
    11 #1 b = 0;
    12 #1 c = 1;
    13 #1 b = 1'bx;
    14 #1 c = 0;
    15 #1 b = 1;
    16 #1 c = 1'bx;
    17 #1 b = 0;
    18 #1 $finish;
    19 end
    20
    21 endmodule

    仿真输出

    View Code
      B = 0 C = 0  A = 0
    B = 1 C = 0 A = 1
    B = 0 C = 0 A = 0
    B = 0 C = 1 A = 1
    B = x C = 1 A = 1
    B = x C = 0 A = x
    B = 1 C = 0 A = 1
    B = 1 C = x A = 1
    B = 0 C = x A = x

    Table

    Table 描述UDP的功能,语法很简单,表的每一行是一个条件,当一个输入改变,匹配输入条件得到输出。

    Initial

    初始赋值用于时序UDP的初始化。这个语句以initial关键字开始,紧接着的必须是一个赋值语句。

    View Code
     1 primitive udp_initial (a,b,c);
    2 output a;
    3 input b,c;
    4 reg a;
    5
    6 // a has value of 1 at start of sim
    7 initial a = 1'b1;
    8
    9 table
    10 // udp_initial behaviour
    11 endtable
    12
    13 endprimitive

    符号

    UDP使用特别的符号描述功能,如rising edge, don't care等等。如下表所示:

    Symbol

    Interpretation

    Explanation

    ?

    0 or 1 or X

    ? means the variable can be 0 or 1 or x

    b

    0 or 1

    Same as ?, but x is not included

    f

    (10)

    Falling edge on an input

    r

    (01)

    Rising edge on an input

    p

    (01) or (0x) or (x1) or (1z) or (z1)

    Rising edge including x and z

    n

    (10) or (1x) or (x0) or (0z) or (z0)

    Falling edge including x and z

    *

    (??)

    All transitions

    -

    no change

    No Change


    组合UDP

    组合UDP中,输出是当前输入的函数。无论什么时候输入改变,UDP匹配表中的一行,输出状态被设置到那一行所指定的值。这与条件语句类似,table的每一行是一个条件。组合UDP的每一个输入或输出都以冒号分隔,表的每一行以分号结束。

    注意:table的每一行输入的顺序必须和UDP定义中Header部分的端口列表输入端口顺序一致,但与端口声明的顺序无关。

      每一行定义输出是输入状态的特殊组合。

      如果所有的输入都指定X,那么输入必须指定为X。

      所有没有显性指定的组合都默认导致输出为X。

      相同的输入却指定不同的输出是非法的。

    View Code
     1 // This code shows how UDP body looks like
    2 primitive udp_body (
    3 a, // Port a
    4 b, // Port b
    5 c // Port c
    6 );
    7 output a;
    8 input b,c;
    9
    10 // UDP function code here
    11 // A = B | C;
    12 table
    13 // B C : A
    14 ? 1 : 1;
    15 1 ? : 1;
    16 0 0 : 0;
    17 endtable
    18
    19 endprimitive

    TestBench

    View Code
     1  `include "udp_body.v"
    2 module udp_body_tb();
    3
    4 reg b,c;
    5 wire a;
    6
    7 udp_body udp (a,b,c);
    8
    9 initial begin
    10 $monitor(" B = %b C = %b A = %b",b,c,a);
    11 b = 0;
    12 c = 0;
    13 #1 b = 1;
    14 #1 b = 0;
    15 #1 c = 1;
    16 #1 b = 1'bx;
    17 #1 c = 0;
    18 #1 b = 1;
    19 #1 c = 1'bx;
    20 #1 b = 0;
    21 #1 $finish;
    22 end
    23
    24 endmodule
    
    

    仿真输出

    View Code
      B = 0 C = 0  A = 0
    B = 1 C = 0 A = 1
    B = 0 C = 0 A = 0
    B = 0 C = 1 A = 1
    B = x C = 1 A = 1
    B = x C = 0 A = x
    B = 1 C = 0 A = 1
    B = 1 C = x A = 1
    B = 0 C = x A = x


    时序UDP - 电平敏感

    电平敏感时序行为的描述与组合行为相同(除了输出需要声明为reg类型,且在table中有一个额外的区域,用于代表当前UDP的状态)。

    输出声明为reg表示UDP有一个内部状态,UDP的输出总是与内部状态相同。时序UDP与组合UDP比起来,在输入和输出区域之间多了一个额外的区域,代表当前状态区域,且当前状态与当前输出值相同,这个区域也用冒号和输入输出分隔开。

    View Code
     1   primitive udp_latch(q, clk, d) ;
    2 output q;
    3 input clk, d;
    4
    5 reg q;
    6
    7 table
    8 //clk d q q+
    9 0 1 : ? : 1 ;
    10 0 0 : ? : 0 ;
    11 1 ? : ? : - ;
    12 endtable
    13
    14 endprimitive

    以上示例是一个低电平敏感的锁存器,q即使当前输出也是当前状态,q+即为下一个输出,表现和组合电路类似。

    时序UDP - 边沿敏感

    电平敏感行为中,输入的值和当前状态决定了输出值,但边沿敏感行为其输入的转换触发的输出改变是不同的。由于在组合和电平敏感中,问号(?)代表0, 1, 和x.破折号 (-) 指定输出值不改变。任何未指定的转换默认输出值为X。因而,在前一个例子中,clock从0到x且data等于0,当前状态等于1,导致输出q变为x。

    不影响输出的所有转换必须显性的指定。否则,他们将导致输出值变为x。如果UDP对任何输入的边沿敏感,对所有输入的所有边沿都必须指定需要的输出状态。

    示例UDP(with initial)

    View Code
     1   primitive udp_sequential_initial(q, clk, d);
    2 output q;
    3 input clk, d;
    4
    5 reg q;
    6
    7 initial begin
    8 q = 0;
    9 end
    10
    11 table
    12 // obtain output on rising edge of clk
    13 // clk d q q+
    14 (01) 0 : ? : 0 ;
    15 (01) 1 : ? : 1 ;
    16 (0?) 1 : 1 : 1 ;
    17 (0?) 0 : 0 : 0 ;
    18 // ignore negative edge of clk
    19 (?0) ? : ? : - ;
    20 // ignore d changes on steady clk
    21 ? (??) : ? : - ;
    22 endtable
    23
    24 endprimitive

    【完】

  • 相关阅读:
    疯狂
    绝对基金的最爱,今年推荐
    蛛丝马迹中愤怒的老总
    值得作一年投资的股票
    狂牛终于被制服了,一起来享受盛宴吧(公布一些数据)
    敬而远之
    发现一庄两股
    一下表格大家好好研究吧
    怎样申购新股以及申购技巧
    股市比女人还善变
  • 原文地址:https://www.cnblogs.com/yuyan/p/2304392.html
Copyright © 2011-2022 走看看