大多数人终生追逐的那些希望和努力是毫无价值的。每个人只是因为有个胃,就注定要参与这种追逐。而且由于参与这种追逐,他的胃是可能得到满足的,但是,一个有思想、有感情的人却不能由此得到满足。——爱因斯坦

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

 

【完】

posted @ 2011-12-28 10:38  东去春来  阅读(11323)  评论(0编辑  收藏  举报