Verilog UDP(User Defined Primitives)
User Defined Primitives
这是一篇很浅显易懂的介绍Verilog UDP的文章,翻译过来留存,原文可参考这里。
l 介绍
Verilog有内建原语如门,传输管,开关等,这些都是相当小的原语,如果我们需要更为复杂的原语,verilog提供了UDP,也就是用户定义原语(User Defined Primitives). 使用UDP可以建模组合电路和时序电路。
l 语法
UDP以保留字primitive开始,以endprimitive结束,并紧接着原语的Ports/terminals。这与module的定义类似。UDP应该定义在module和endmoudle外面。
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。
l 端口
- 一个UDP只可以包含一个输出和最多10个输入。
- 输出端口应该是第一个端口,然后才是一个或多个输入端口。
- 所有的UDP都是标量,也就是,向量端口不允许。
- UDP不能是双向端口。
- 时序UDP的输出端需要额外声明为reg类型。
- 组合UDP的输出端声明为reg类型是非法的。
l 功能体
原语的功能(包括组合和时序)在一个table表里描述,以保留字endtable结束。如以下代码所示。对于时序UDP,我们可以使用initial 给输出赋一个初始值。
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
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
仿真输出
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关键字开始,紧接着的必须是一个赋值语句。
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。
相同的输入却指定不同的输出是非法的。
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
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
仿真输出
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比起来,在输入和输出区域之间多了一个额外的区域,代表当前状态区域,且当前状态与当前输出值相同,这个区域也用冒号和输入输出分隔开。
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)
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
【完】