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
【完】